fix(chat): render cached history before network sync
This commit is contained in:
+118
-51
@@ -34,6 +34,7 @@ import type { ChatEvent } from "./chat-events";
|
||||
import {
|
||||
applyHttpSendOutput,
|
||||
applyHistoryLoadedOutput,
|
||||
applyNetworkHistoryLoadedOutput,
|
||||
countLockedHistoryMessages,
|
||||
shouldAutoUnlockHistory,
|
||||
shouldPromptUnlockHistory,
|
||||
@@ -78,6 +79,46 @@ export const chatMachine = setup({
|
||||
clearChatSession: assign(() => ({
|
||||
...initialState,
|
||||
})),
|
||||
|
||||
applyLocalHistoryLoaded: assign(({ event }) => {
|
||||
if (event.type !== "ChatLocalHistoryLoaded") return {};
|
||||
return applyHistoryLoadedOutput(event.output);
|
||||
}),
|
||||
|
||||
applyNetworkHistoryLoaded: assign(({ context, event }) => {
|
||||
if (event.type !== "ChatNetworkHistoryLoaded") return {};
|
||||
return applyNetworkHistoryLoadedOutput(context, event.output);
|
||||
}),
|
||||
|
||||
applyNetworkHistoryLoadedAndClearPayment: assign(({ context, event }) => {
|
||||
if (event.type !== "ChatNetworkHistoryLoaded") return {};
|
||||
return {
|
||||
...applyNetworkHistoryLoadedOutput(context, event.output),
|
||||
isLoadingMore: false,
|
||||
paymentUnlockPending: false,
|
||||
};
|
||||
}),
|
||||
|
||||
showUnlockHistoryPromptFromNetwork: assign(({ context, event }) => {
|
||||
if (event.type !== "ChatNetworkHistoryLoaded") return {};
|
||||
const nextHistory = applyNetworkHistoryLoadedOutput(
|
||||
context,
|
||||
event.output,
|
||||
);
|
||||
return {
|
||||
...nextHistory,
|
||||
isLoadingMore: false,
|
||||
paymentUnlockPending: false,
|
||||
unlockHistoryPromptVisible: true,
|
||||
lockedHistoryCount: countLockedHistoryMessages(nextHistory.messages),
|
||||
unlockHistoryError: null,
|
||||
};
|
||||
}),
|
||||
|
||||
markHistoryLoadFailed: assign({
|
||||
isLoadingMore: false,
|
||||
historyLoaded: true,
|
||||
}),
|
||||
},
|
||||
}).createMachine({
|
||||
id: "chat",
|
||||
@@ -98,15 +139,27 @@ export const chatMachine = setup({
|
||||
},
|
||||
|
||||
guestSession: {
|
||||
invoke: {
|
||||
id: "messageQueue",
|
||||
src: "httpMessageQueue",
|
||||
},
|
||||
invoke: [
|
||||
{
|
||||
id: "messageQueue",
|
||||
src: "httpMessageQueue",
|
||||
},
|
||||
{
|
||||
id: "loadHistory",
|
||||
src: "loadHistory",
|
||||
},
|
||||
],
|
||||
on: {
|
||||
ChatUserLogin: {
|
||||
target: "#chat.userSession",
|
||||
actions: "startUserSession",
|
||||
},
|
||||
ChatNetworkHistoryLoaded: {
|
||||
actions: "applyNetworkHistoryLoaded",
|
||||
},
|
||||
ChatHistoryLoadFailed: {
|
||||
actions: "markHistoryLoadFailed",
|
||||
},
|
||||
ChatQueuedSendStarted: {
|
||||
actions: "markQueuedSendStarted",
|
||||
},
|
||||
@@ -120,25 +173,18 @@ export const chatMachine = setup({
|
||||
initial: "initializing",
|
||||
states: {
|
||||
initializing: {
|
||||
always: [
|
||||
{
|
||||
on: {
|
||||
ChatLocalHistoryLoaded: {
|
||||
target: "ready",
|
||||
guard: ({ context }) => context.historyLoaded,
|
||||
actions: "applyLocalHistoryLoaded",
|
||||
},
|
||||
],
|
||||
invoke: {
|
||||
id: "loadHistory",
|
||||
src: "loadHistory",
|
||||
onDone: {
|
||||
actions: assign(({ event }) =>
|
||||
applyHistoryLoadedOutput(event.output),
|
||||
),
|
||||
ChatNetworkHistoryLoaded: {
|
||||
target: "ready",
|
||||
actions: "applyNetworkHistoryLoaded",
|
||||
},
|
||||
onError: {
|
||||
// 失败也标 loaded,不卡 init(让 UI 还是能进 ready,屏幕可能空)
|
||||
actions: assign({
|
||||
historyLoaded: true,
|
||||
}),
|
||||
ChatHistoryLoadFailed: {
|
||||
target: "ready",
|
||||
actions: "markHistoryLoadFailed",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -178,15 +224,50 @@ export const chatMachine = setup({
|
||||
},
|
||||
|
||||
userSession: {
|
||||
invoke: {
|
||||
id: "messageQueue",
|
||||
src: "httpMessageQueue",
|
||||
},
|
||||
invoke: [
|
||||
{
|
||||
id: "messageQueue",
|
||||
src: "httpMessageQueue",
|
||||
},
|
||||
{
|
||||
id: "loadHistory",
|
||||
src: "loadHistory",
|
||||
},
|
||||
],
|
||||
on: {
|
||||
ChatLogout: {
|
||||
target: "#chat.idle",
|
||||
actions: "clearChatSession",
|
||||
},
|
||||
ChatNetworkHistoryLoaded: [
|
||||
{
|
||||
guard: ({ context, event }) =>
|
||||
event.type === "ChatNetworkHistoryLoaded" &&
|
||||
context.paymentUnlockPending &&
|
||||
shouldAutoUnlockHistory(event.output.messages),
|
||||
target: ".unlockingHistory",
|
||||
actions: [
|
||||
"applyNetworkHistoryLoadedAndClearPayment",
|
||||
"markUnlockHistoryStarted",
|
||||
],
|
||||
},
|
||||
{
|
||||
guard: ({ context, event }) =>
|
||||
event.type === "ChatNetworkHistoryLoaded" &&
|
||||
context.paymentUnlockPending &&
|
||||
shouldPromptUnlockHistory(event.output.messages),
|
||||
target: ".ready",
|
||||
actions: "showUnlockHistoryPromptFromNetwork",
|
||||
},
|
||||
{
|
||||
guard: ({ context }) =>
|
||||
!context.isUnlockingHistory && !context.isUnlockingMessage,
|
||||
actions: "applyNetworkHistoryLoaded",
|
||||
},
|
||||
],
|
||||
ChatHistoryLoadFailed: {
|
||||
actions: "markHistoryLoadFailed",
|
||||
},
|
||||
ChatQueuedSendStarted: {
|
||||
actions: "markQueuedSendStarted",
|
||||
},
|
||||
@@ -232,53 +313,39 @@ export const chatMachine = setup({
|
||||
initial: "initializing",
|
||||
states: {
|
||||
initializing: {
|
||||
invoke: {
|
||||
src: "loadHistory",
|
||||
onDone: [
|
||||
on: {
|
||||
ChatLocalHistoryLoaded: {
|
||||
target: "ready",
|
||||
actions: "applyLocalHistoryLoaded",
|
||||
},
|
||||
ChatNetworkHistoryLoaded: [
|
||||
{
|
||||
guard: ({ context, event }) =>
|
||||
event.type === "ChatNetworkHistoryLoaded" &&
|
||||
context.paymentUnlockPending &&
|
||||
shouldAutoUnlockHistory(event.output.messages),
|
||||
target: "unlockingHistory",
|
||||
actions: [
|
||||
assign(({ event }) => ({
|
||||
...applyHistoryLoadedOutput(event.output),
|
||||
isLoadingMore: false,
|
||||
})),
|
||||
"applyNetworkHistoryLoadedAndClearPayment",
|
||||
"markUnlockHistoryStarted",
|
||||
],
|
||||
},
|
||||
{
|
||||
guard: ({ context, event }) =>
|
||||
event.type === "ChatNetworkHistoryLoaded" &&
|
||||
context.paymentUnlockPending &&
|
||||
shouldPromptUnlockHistory(event.output.messages),
|
||||
target: "ready",
|
||||
actions: assign(({ event }) => ({
|
||||
...applyHistoryLoadedOutput(event.output),
|
||||
isLoadingMore: false,
|
||||
paymentUnlockPending: false,
|
||||
unlockHistoryPromptVisible: true,
|
||||
lockedHistoryCount: countLockedHistoryMessages(
|
||||
event.output.messages,
|
||||
),
|
||||
unlockHistoryError: null,
|
||||
})),
|
||||
actions: "showUnlockHistoryPromptFromNetwork",
|
||||
},
|
||||
{
|
||||
target: "ready",
|
||||
actions: assign(({ event }) => ({
|
||||
...applyHistoryLoadedOutput(event.output),
|
||||
isLoadingMore: false,
|
||||
paymentUnlockPending: false,
|
||||
})),
|
||||
actions: "applyNetworkHistoryLoadedAndClearPayment",
|
||||
},
|
||||
],
|
||||
onError: {
|
||||
ChatHistoryLoadFailed: {
|
||||
target: "ready",
|
||||
actions: assign({
|
||||
isLoadingMore: false,
|
||||
historyLoaded: true,
|
||||
}),
|
||||
actions: "markHistoryLoadFailed",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user