diff --git a/src/stores/chat/__tests__/chat-machine.helpers.test.ts b/src/stores/chat/__tests__/chat-machine.helpers.test.ts index 60b572b9..4df4158e 100644 --- a/src/stores/chat/__tests__/chat-machine.helpers.test.ts +++ b/src/stores/chat/__tests__/chat-machine.helpers.test.ts @@ -40,9 +40,6 @@ function makeChatState(overrides: Partial = {}): ChatState { creditsCharged: 0, requiredCredits: 0, shortfallCredits: 0, - isLoadingMore: false, - hasMore: true, - historyOffset: 0, historyLoaded: true, paymentUnlockPending: false, unlockHistoryPromptVisible: false, diff --git a/src/stores/chat/__tests__/chat-machine.transitions.test.ts b/src/stores/chat/__tests__/chat-machine.transitions.test.ts index 4a005878..f980beeb 100644 --- a/src/stores/chat/__tests__/chat-machine.transitions.test.ts +++ b/src/stores/chat/__tests__/chat-machine.transitions.test.ts @@ -13,12 +13,6 @@ import type { UnlockMessageRequest, } from "@/stores/chat/chat-machine.helpers"; -interface LoadMoreHistoryOutput { - messages: UiMessage[]; - hasMore: boolean; - newOffset: number; -} - interface SendMessageHttpOutput { response: ChatSendResponse; reply: UiMessage | null; @@ -29,8 +23,6 @@ interface UnlockHistoryOutput { reason: string; shortfallCredits: number; messages: UiMessage[]; - hasMore: boolean; - newOffset: number; } interface TestUnlockMessageOutput { @@ -83,13 +75,6 @@ function createTestChatMachine( return chatMachine.provide({ actors: { loadHistory: createLoadHistoryCallback(options.historyMessages ?? []), - loadMoreHistory: fromPromise( - async () => ({ - messages: [], - hasMore: false, - newOffset: 0, - }), - ), sendMessageHttp: fromPromise< SendMessageHttpOutput, { content: string } @@ -116,8 +101,6 @@ function createTestChatMachine( reason: "ok", shortfallCredits: 0, messages: [], - hasMore: false, - newOffset: 0, ...options.unlockHistoryOutput, })), unlockMessage: fromPromise< @@ -147,8 +130,6 @@ function createLoadHistoryCallback( type: "ChatLocalHistoryLoaded", output: { messages, - hasMore: false, - newOffset: messages.length, localCount: messages.length, }, }); @@ -156,8 +137,6 @@ function createLoadHistoryCallback( type: "ChatNetworkHistoryLoaded", output: { messages: networkMessages, - hasMore: false, - newOffset: networkMessages.length, localOverwritten: true, localCount: messages.length, networkCount: networkMessages.length, @@ -283,8 +262,6 @@ describe("chatMachine transitions", () => { type: "ChatLocalHistoryLoaded", output: { messages: [localMessage], - hasMore: false, - newOffset: 1, localCount: 1, }, }); @@ -293,8 +270,6 @@ describe("chatMachine transitions", () => { type: "ChatNetworkHistoryLoaded", output: { messages: [networkMessage], - hasMore: false, - newOffset: 1, localOverwritten: true, localCount: 1, networkCount: 1, @@ -399,13 +374,6 @@ describe("chatMachine transitions", () => { const machine = chatMachine.provide({ actors: { loadHistory: createLoadHistoryCallback([]), - loadMoreHistory: fromPromise( - async () => ({ - messages: [], - hasMore: false, - newOffset: 0, - }), - ), sendMessageHttp: fromPromise< SendMessageHttpOutput, { content: string } @@ -435,8 +403,6 @@ describe("chatMachine transitions", () => { reason: "ok", shortfallCredits: 0, messages: [], - hasMore: false, - newOffset: 0, })), }, }); @@ -526,8 +492,6 @@ describe("chatMachine transitions", () => { lockReason: null, }, ], - hasMore: false, - newOffset: 1, }, }), ).start(); @@ -585,8 +549,6 @@ describe("chatMachine transitions", () => { lockReason: null, }, ], - hasMore: false, - newOffset: 1, }, }), ).start(); diff --git a/src/stores/chat/chat-context.tsx b/src/stores/chat/chat-context.tsx index a2b43883..6e2cd78d 100644 --- a/src/stores/chat/chat-context.tsx +++ b/src/stores/chat/chat-context.tsx @@ -26,9 +26,6 @@ interface ChatState { isReplyingAI: boolean; upgradePromptVisible: boolean; upgradeReason: MachineContext["upgradeReason"]; - isLoadingMore: boolean; - hasMore: boolean; - historyOffset: number; /** history 初始加载完成标志(local → network → save 跑完) —— UI 可用此显示 loading */ historyLoaded: boolean; unlockHistoryPromptVisible: boolean; @@ -63,9 +60,6 @@ export function ChatProvider({ children }: ChatProviderProps) { isReplyingAI: state.context.isReplyingAI, upgradePromptVisible: state.context.upgradePromptVisible, upgradeReason: state.context.upgradeReason, - isLoadingMore: state.context.isLoadingMore, - hasMore: state.context.hasMore, - historyOffset: state.context.historyOffset, historyLoaded: state.context.historyLoaded, unlockHistoryPromptVisible: state.context.unlockHistoryPromptVisible, lockedHistoryCount: state.context.lockedHistoryCount, diff --git a/src/stores/chat/chat-events.ts b/src/stores/chat/chat-events.ts index e9db2510..bf50d502 100644 --- a/src/stores/chat/chat-events.ts +++ b/src/stores/chat/chat-events.ts @@ -36,7 +36,6 @@ export type ChatEvent = // 业务事件 | { type: "ChatSendMessage"; content: string } | { type: "ChatSendImage"; imageBase64: string } - | { type: "ChatLoadMoreHistory" } | { type: "ChatPromotionInjected"; promotion: PendingChatPromotion; diff --git a/src/stores/chat/chat-history-flow.ts b/src/stores/chat/chat-history-flow.ts index ca333fa9..453bfd2e 100644 --- a/src/stores/chat/chat-history-flow.ts +++ b/src/stores/chat/chat-history-flow.ts @@ -1,24 +1,16 @@ -import { fromCallback, fromPromise } from "xstate"; +import { fromCallback } from "xstate"; -import { getChatRepository } from "@/data/repositories/chat_repository"; -import { resolveChatCacheOwnerKey } from "@/data/repositories/chat_cache_identity"; -import { Logger, Result } from "@/utils"; +import { Logger } from "@/utils"; import { readLocalHistorySnapshot, resolveHistoryCacheIdentity, syncNetworkHistory, } from "./chat-history-sync"; -import { - PAGE_SIZE, - localMessagesToUi, -} from "./chat-machine.helpers"; import type { ChatEvent } from "./chat-events"; const log = new Logger("StoresChatChatHistoryFlow"); -type UiMessage = import("@/data/dto/chat").UiMessage; - export const loadHistoryActor = fromCallback(({ sendBack }) => { let cancelled = false; @@ -50,30 +42,3 @@ export const loadHistoryActor = fromCallback(({ sendBack }) => { cancelled = true; }; }); - -export const loadMoreHistoryActor = fromPromise< - { messages: UiMessage[]; hasMore: boolean; newOffset: number }, - { offset: number } ->(async ({ input }) => { - const chatRepo = getChatRepository(); - const cacheIdentityResult = await resolveChatCacheOwnerKey(); - const result = await chatRepo.getHistory(PAGE_SIZE, input.offset); - if (Result.isErr(result)) { - log.error("[chat-machine] loadMoreHistoryActor failed", { - error: result.error, - }); - throw result.error; - } - const page = localMessagesToUi(result.data.messages); - if (Result.isOk(cacheIdentityResult)) { - void chatRepo.prefetchMediaForMessages( - result.data.messages, - cacheIdentityResult.data, - ); - } - return { - messages: page, - hasMore: page.length >= PAGE_SIZE, - newOffset: input.offset + page.length, - }; -}); diff --git a/src/stores/chat/chat-history-sync.ts b/src/stores/chat/chat-history-sync.ts index bc774cb3..85870c03 100644 --- a/src/stores/chat/chat-history-sync.ts +++ b/src/stores/chat/chat-history-sync.ts @@ -3,15 +3,16 @@ import { resolveChatCacheOwnerKey } from "@/data/repositories/chat_cache_identit import { getChatRepository } from "@/data/repositories/chat_repository"; import { Logger, Result, todayString } from "@/utils"; -import { localMessagesToUi, PAGE_SIZE } from "./chat-machine.helpers"; +import { + CHAT_HISTORY_LIMIT, + localMessagesToUi, +} 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; @@ -21,8 +22,6 @@ export type ReadAndSyncHistoryOutput = { export type LocalHistorySnapshotOutput = { /** Local cached messages. Empty local history includes a UI greeting. */ messages: UiMessage[]; - hasMore: boolean; - newOffset: number; localCount: number; }; @@ -69,8 +68,6 @@ export async function readLocalHistorySnapshot( return { messages: snapshotMessages, - hasMore: localMessages.length >= PAGE_SIZE, - newOffset: snapshotMessages.length, localCount: localMessages.length, }; } @@ -82,7 +79,7 @@ export async function syncNetworkHistory( const chatRepo = getChatRepository(); const greetingMessage = createGreetingMessage(); - const networkResult = await chatRepo.getHistory(PAGE_SIZE, 0); + const networkResult = await chatRepo.getHistory(CHAT_HISTORY_LIMIT, 0); if (Result.isErr(networkResult)) { log.error("[chat-machine] loadHistory NETWORK FAILED", { error: networkResult.error, @@ -120,8 +117,6 @@ export async function syncNetworkHistory( return { messages: finalMessages, - hasMore: finalMessages.length >= PAGE_SIZE, - newOffset: finalMessages.length, localOverwritten, localCount, networkCount: networkUi.length, @@ -145,8 +140,6 @@ export async function readAndSyncHistory(): Promise { return { messages: localSnapshot.messages, - hasMore: localSnapshot.hasMore, - newOffset: localSnapshot.newOffset, localOverwritten: false, localCount: localSnapshot.localCount, networkCount: 0, diff --git a/src/stores/chat/chat-machine.actors.ts b/src/stores/chat/chat-machine.actors.ts index fb95a382..d2413ff0 100644 --- a/src/stores/chat/chat-machine.actors.ts +++ b/src/stores/chat/chat-machine.actors.ts @@ -6,10 +6,7 @@ * - chat-send-flow * - chat-unlock-flow */ -export { - loadHistoryActor, - loadMoreHistoryActor, -} from "./chat-history-flow"; +export { loadHistoryActor } from "./chat-history-flow"; export { httpMessageQueueActor, sendMessageHttpActor, diff --git a/src/stores/chat/chat-machine.helpers.ts b/src/stores/chat/chat-machine.helpers.ts index d934aa2f..c57b6e04 100644 --- a/src/stores/chat/chat-machine.helpers.ts +++ b/src/stores/chat/chat-machine.helpers.ts @@ -2,8 +2,8 @@ import type { UiMessage } from "@/data/dto/chat"; import type { ChatState } from "./chat-state"; -// History pagination size used by the history actors. -export const PAGE_SIZE = 50; +// The initial history request remains bounded even though pagination is disabled. +export const CHAT_HISTORY_LIMIT = 50; export * from "./chat-message-mappers"; export * from "./chat-promotion"; @@ -13,17 +13,10 @@ export * from "./chat-unlock-helpers"; export function applyHistoryLoadedOutput( output: { messages: UiMessage[]; - hasMore: boolean; - newOffset: number; }, -): Pick< - ChatState, - "messages" | "hasMore" | "historyOffset" | "historyLoaded" -> { +): Pick { return { messages: output.messages, - hasMore: output.hasMore, - historyOffset: output.newOffset, historyLoaded: true, }; } @@ -32,18 +25,13 @@ export function applyNetworkHistoryLoadedOutput( context: ChatState, output: { messages: UiMessage[]; - hasMore: boolean; - newOffset: number; + localCount: number; }, -): Pick< - ChatState, - "messages" | "hasMore" | "historyOffset" | "historyLoaded" -> { - const optimisticTail = context.messages.slice(context.historyOffset); +): Pick { + const localSnapshotSize = output.localCount === 0 ? 1 : output.localCount; + const optimisticTail = context.messages.slice(localSnapshotSize); return { messages: [...output.messages, ...optimisticTail], - hasMore: output.hasMore, - historyOffset: output.newOffset, historyLoaded: true, }; } diff --git a/src/stores/chat/chat-machine.ts b/src/stores/chat/chat-machine.ts index de3b6990..d4047bbe 100644 --- a/src/stores/chat/chat-machine.ts +++ b/src/stores/chat/chat-machine.ts @@ -39,10 +39,7 @@ import { shouldPromptUnlockHistory, createChatPromotionState, } from "./chat-machine.helpers"; -import { - loadHistoryActor, - loadMoreHistoryActor, -} from "./chat-history-flow"; +import { loadHistoryActor } from "./chat-history-flow"; import { appendGuestUserImageAction, appendUserImageAction, @@ -87,7 +84,6 @@ export const chatMachine = setup({ }, actors: { loadHistory: loadHistoryActor, - loadMoreHistory: loadMoreHistoryActor, sendMessageHttp: sendMessageHttpActor, httpMessageQueue: httpMessageQueueActor, unlockHistory: unlockHistoryActor, @@ -154,7 +150,6 @@ export const chatMachine = setup({ if (event.type !== "ChatNetworkHistoryLoaded") return {}; return { ...applyNetworkHistoryLoadedOutput(context, event.output), - isLoadingMore: false, paymentUnlockPending: false, }; }), @@ -167,7 +162,6 @@ export const chatMachine = setup({ ); return { ...nextHistory, - isLoadingMore: false, paymentUnlockPending: false, unlockHistoryPromptVisible: true, lockedHistoryCount: countLockedHistoryMessages(nextHistory.messages), @@ -175,10 +169,7 @@ export const chatMachine = setup({ }; }), - markHistoryLoadFailed: assign({ - isLoadingMore: false, - historyLoaded: true, - }), + markHistoryLoadFailed: assign({ historyLoaded: true }), }, }).createMachine({ id: "chat", @@ -404,9 +395,6 @@ export const chatMachine = setup({ ChatSendImage: { actions: "appendUserImage", }, - ChatLoadMoreHistory: { - target: "loadingMore", - }, ChatUnlockMessageRequested: { guard: ({ event }) => event.messageId.trim().length > 0, target: "unlockingMessage", @@ -432,25 +420,6 @@ export const chatMachine = setup({ }, }, }, - loadingMore: { - invoke: { - src: "loadMoreHistory", - input: ({ context }) => ({ offset: context.historyOffset }), - onDone: { - target: "ready", - actions: assign(({ context, event }) => ({ - messages: [...event.output.messages, ...context.messages], - isLoadingMore: false, - hasMore: event.output.hasMore, - historyOffset: event.output.newOffset, - })), - }, - onError: { - target: "ready", - actions: assign({ isLoadingMore: false }), - }, - }, - }, unlockingHistory: { invoke: { id: "unlockHistory", @@ -466,8 +435,6 @@ export const chatMachine = setup({ event.output.reason === "insufficient_balance"; return { messages: event.output.messages, - hasMore: event.output.hasMore, - historyOffset: event.output.newOffset, historyLoaded: true, paymentUnlockPending: false, unlockHistoryPromptVisible: insufficientBalance, diff --git a/src/stores/chat/chat-state.ts b/src/stores/chat/chat-state.ts index db51ec58..cd34cfee 100644 --- a/src/stores/chat/chat-state.ts +++ b/src/stores/chat/chat-state.ts @@ -31,13 +31,7 @@ export interface ChatState { creditsCharged: number; requiredCredits: number; shortfallCredits: number; - isLoadingMore: boolean; - hasMore: boolean; - historyOffset: number; - /** history 首屏可展示标志(本地快照加载完成即可进入 ready) - * - 网络历史加载完成后会再次刷新消息列表 - * - 不被 `loadMoreHistoryActor` 设置(翻页是另一码事) - */ + /** history 首屏可展示标志(本地快照加载完成即可进入 ready)。 */ historyLoaded: boolean; paymentUnlockPending: boolean; unlockHistoryPromptVisible: boolean; @@ -66,9 +60,6 @@ export const initialState: ChatState = { creditsCharged: 0, requiredCredits: 0, shortfallCredits: 0, - isLoadingMore: false, - hasMore: true, - historyOffset: 0, historyLoaded: false, paymentUnlockPending: false, unlockHistoryPromptVisible: false, diff --git a/src/stores/chat/chat-unlock-flow.ts b/src/stores/chat/chat-unlock-flow.ts index 2be6fddb..0f19e5e3 100644 --- a/src/stores/chat/chat-unlock-flow.ts +++ b/src/stores/chat/chat-unlock-flow.ts @@ -27,8 +27,6 @@ export const unlockHistoryActor = fromPromise<{ reason: string; shortfallCredits: number; messages: UiMessage[]; - hasMore: boolean; - newOffset: number; }>(async () => { const chatRepo = getChatRepository(); const unlockResult = await chatRepo.unlockHistory(); @@ -45,8 +43,6 @@ export const unlockHistoryActor = fromPromise<{ reason: unlockResult.data.reason, shortfallCredits: unlockResult.data.shortfallCredits, messages: history.messages, - hasMore: history.hasMore, - newOffset: history.newOffset, }; });