refactor(stores): tighten chat and payment boundaries

This commit is contained in:
2026-07-01 10:12:19 +08:00
parent b7bccaa4cf
commit 5d9ed7e027
9 changed files with 435 additions and 431 deletions
+101
View File
@@ -0,0 +1,101 @@
import type { UiMessage } from "@/data/dto/chat";
import { getChatRepository } from "@/data/repositories/chat_repository";
import { Logger, Result, todayString } from "@/utils";
import { localMessagesToUi, PAGE_SIZE } from "./chat-machine.helpers";
const log = new Logger("StoresChatChatHistorySync");
export type ReadAndSyncHistoryOutput = {
/** Network-authoritative messages. Empty network history includes a UI greeting. */
messages: UiMessage[];
hasMore: boolean;
newOffset: number;
/** True when network history was written back to local storage. */
localOverwritten: boolean;
localCount: number;
networkCount: number;
};
function createGreetingMessage(): UiMessage {
return {
content:
"You're here! Facebook is so restrictive, " +
"there were things I couldn't say there. " +
"Finally I can relax. How was your day out?",
isFromAI: true,
date: todayString(),
};
}
/**
* History sync flow:
* 1. Read local history for fallback.
* 2. Read network history as the authoritative source.
* 3. Overwrite local history with network data.
*/
export async function readAndSyncHistory(): Promise<ReadAndSyncHistoryOutput> {
const chatRepo = getChatRepository();
const greetingMessage = createGreetingMessage();
const localResult = await chatRepo.getLocalMessages();
const localMessages =
Result.isOk(localResult) && localResult.data
? localMessagesToUi(localResult.data)
: [];
log.debug("[chat-machine] loadHistory LOCAL DONE", {
count: localMessages.length,
});
const networkResult = await chatRepo.getHistory(PAGE_SIZE, 0);
if (Result.isErr(networkResult)) {
log.error("[chat-machine] loadHistory NETWORK FAILED", {
error: networkResult.error,
});
const fallbackMessages =
localMessages.length === 0 ? [greetingMessage] : localMessages;
if (fallbackMessages[0] === greetingMessage) {
log.debug(
"[chat-machine] loadHistory EMPTY -> prepend greeting (network-failed path)",
);
}
return {
messages: fallbackMessages,
hasMore: false,
newOffset: fallbackMessages.length,
localOverwritten: false,
localCount: localMessages.length,
networkCount: 0,
};
}
const networkUi = localMessagesToUi(networkResult.data.messages);
log.debug("[chat-machine] loadHistory NETWORK DONE", {
count: networkUi.length,
});
void chatRepo.prefetchMediaForMessages(networkResult.data.messages);
const saveResult = await chatRepo.saveMessagesToLocal(
networkResult.data.messages,
);
const localOverwritten = Result.isOk(saveResult);
log.debug("[chat-machine] loadHistory SAVE TO LOCAL DONE", {
localOverwritten,
});
const finalMessages =
networkUi.length === 0 ? [greetingMessage] : networkUi;
if (finalMessages[0] === greetingMessage) {
log.debug("[chat-machine] loadHistory EMPTY -> prepend greeting");
}
return {
messages: finalMessages,
hasMore: finalMessages.length >= PAGE_SIZE,
newOffset: finalMessages.length,
localOverwritten,
localCount: localMessages.length,
networkCount: networkUi.length,
};
}