fix(chat): keep sent messages in view

This commit is contained in:
2026-07-15 19:14:36 +08:00
parent 47876484fc
commit cf239a51de
8 changed files with 122 additions and 1 deletions
@@ -31,6 +31,7 @@ function makeChatState(overrides: Partial<ChatState> = {}): ChatState {
return {
messages: [],
promotion: null,
outgoingMessageRevision: 0,
isReplyingAI: true,
pendingReplyCount: 1,
upgradePromptVisible: false,
@@ -29,6 +29,10 @@ describe("chat send flow", () => {
{ content: "hello", isFromAI: false },
{ content: "still there?", isFromAI: false },
]);
expect(actor.getSnapshot().context.outgoingMessageRevision).toBe(2);
actor.send({ type: "ChatSendMessage", content: " " });
expect(actor.getSnapshot().context.outgoingMessageRevision).toBe(2);
await waitFor(
actor,
@@ -60,10 +64,32 @@ describe("chat send flow", () => {
expect(actor.getSnapshot().context.messages).toMatchObject([
{ content: "[Image]", isFromAI: false },
]);
expect(actor.getSnapshot().context.outgoingMessageRevision).toBe(1);
actor.stop();
});
it("increments the outgoing revision for a user image", async () => {
const actor = createActor(createTestChatMachine()).start();
actor.send({ type: "ChatUserLogin", token: "token" });
await waitFor(actor, (snapshot) =>
snapshot.matches({ userSession: "ready" }),
);
actor.send({
type: "ChatSendImage",
imageBase64: "data:image/png;base64,user-image",
});
expect(actor.getSnapshot().context.outgoingMessageRevision).toBe(1);
expect(actor.getSnapshot().context.messages.at(-1)).toMatchObject({
content: "[Image]",
isFromAI: false,
});
actor.stop();
});
it("restores ready state when the direct HTTP actor fails", async () => {
const actor = createActor(
createTestChatMachine({
+2
View File
@@ -18,6 +18,7 @@ interface ChatState {
messages: MachineContext["messages"];
historyMessages: MachineContext["messages"];
promotion: MachineContext["promotion"];
outgoingMessageRevision: number;
isReplyingAI: boolean;
upgradePromptVisible: boolean;
upgradeReason: MachineContext["upgradeReason"];
@@ -74,6 +75,7 @@ function selectChatState(state: ChatSnapshot): SelectedChatState {
return {
historyMessages: state.context.messages,
promotion: state.context.promotion,
outgoingMessageRevision: state.context.outgoingMessageRevision,
isReplyingAI: state.context.isReplyingAI,
upgradePromptVisible: state.context.upgradePromptVisible,
upgradeReason: state.context.upgradeReason,
+2
View File
@@ -22,6 +22,7 @@ export interface ChatUnlockPaywallRequest {
export interface ChatState {
messages: UiMessage[];
promotion: ChatPromotionState | null;
outgoingMessageRevision: number;
isReplyingAI: boolean;
pendingReplyCount: number;
upgradePromptVisible: boolean;
@@ -55,6 +56,7 @@ export interface ChatState {
export const initialState: ChatState = {
messages: [],
promotion: null,
outgoingMessageRevision: 0,
isReplyingAI: false,
pendingReplyCount: 0,
upgradePromptVisible: false,
+4
View File
@@ -41,6 +41,7 @@ const appendGuestUserMessageAction = historyMachineSetup.assign(
date: today,
},
],
outgoingMessageRevision: context.outgoingMessageRevision + 1,
upgradePromptVisible: false,
upgradeReason: null,
};
@@ -67,6 +68,7 @@ const appendUserMessageAction = historyMachineSetup.assign(
date: today,
},
],
outgoingMessageRevision: context.outgoingMessageRevision + 1,
upgradePromptVisible: false,
upgradeReason: null,
};
@@ -125,6 +127,7 @@ const appendGuestUserImageAction = historyMachineSetup.assign(
imageUrl: event.imageBase64,
},
],
outgoingMessageRevision: context.outgoingMessageRevision + 1,
...beginPendingReply(context),
upgradePromptVisible: false,
upgradeReason: null,
@@ -151,6 +154,7 @@ const appendUserImageAction = historyMachineSetup.assign(
imageUrl: event.imageBase64,
},
],
outgoingMessageRevision: context.outgoingMessageRevision + 1,
...beginPendingReply(context),
upgradePromptVisible: false,
upgradeReason: null,