fix(chat): render cached history before network sync

This commit is contained in:
2026-07-02 11:51:20 +08:00
parent cd25fa35f2
commit 5821a4d062
7 changed files with 339 additions and 114 deletions
+55 -25
View File
@@ -17,7 +17,17 @@ export type ReadAndSyncHistoryOutput = {
networkCount: number;
};
function createGreetingMessage(): UiMessage {
export type LocalHistorySnapshotOutput = {
/** Local cached messages. Empty local history includes a UI greeting. */
messages: UiMessage[];
hasMore: boolean;
newOffset: number;
localCount: number;
};
export type NetworkHistorySyncOutput = ReadAndSyncHistoryOutput;
export function createGreetingMessage(): UiMessage {
return {
content:
"You're here! Facebook is so restrictive, " +
@@ -28,13 +38,7 @@ function createGreetingMessage(): UiMessage {
};
}
/**
* 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> {
export async function readLocalHistorySnapshot(): Promise<LocalHistorySnapshotOutput> {
const chatRepo = getChatRepository();
const greetingMessage = createGreetingMessage();
@@ -47,27 +51,32 @@ export async function readAndSyncHistory(): Promise<ReadAndSyncHistoryOutput> {
count: localMessages.length,
});
const snapshotMessages =
localMessages.length === 0 ? [greetingMessage] : localMessages;
if (snapshotMessages[0] === greetingMessage) {
log.debug("[chat-machine] loadHistory EMPTY -> prepend greeting (local)");
}
return {
messages: snapshotMessages,
hasMore: localMessages.length >= PAGE_SIZE,
newOffset: snapshotMessages.length,
localCount: localMessages.length,
};
}
export async function syncNetworkHistory(
localCount: number,
): Promise<NetworkHistorySyncOutput | null> {
const chatRepo = getChatRepository();
const greetingMessage = createGreetingMessage();
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,
};
return null;
}
const networkUi = localMessagesToUi(networkResult.data.messages);
@@ -95,7 +104,28 @@ export async function readAndSyncHistory(): Promise<ReadAndSyncHistoryOutput> {
hasMore: finalMessages.length >= PAGE_SIZE,
newOffset: finalMessages.length,
localOverwritten,
localCount: localMessages.length,
localCount,
networkCount: networkUi.length,
};
}
/**
* 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 localSnapshot = await readLocalHistorySnapshot();
const networkSnapshot = await syncNetworkHistory(localSnapshot.localCount);
if (networkSnapshot) return networkSnapshot;
return {
messages: localSnapshot.messages,
hasMore: localSnapshot.hasMore,
newOffset: localSnapshot.newOffset,
localOverwritten: false,
localCount: localSnapshot.localCount,
networkCount: 0,
};
}