feat(chat): unlock history after payment
This commit is contained in:
+142
-16
@@ -42,14 +42,19 @@ import { ChatState, initialState } from "./chat-state";
|
||||
import type { ChatEvent } from "./chat-events";
|
||||
import {
|
||||
applyHttpSendOutput,
|
||||
applyHistoryLoadedOutput,
|
||||
beginPendingReply,
|
||||
countLockedHistoryMessages,
|
||||
finishPendingReply,
|
||||
shouldAutoUnlockHistory,
|
||||
shouldPromptUnlockHistory,
|
||||
} from "./chat-machine.helpers";
|
||||
import {
|
||||
loadHistoryActor,
|
||||
sendMessageHttpActor,
|
||||
loadMoreHistoryActor,
|
||||
httpMessageQueueActor,
|
||||
unlockHistoryActor,
|
||||
} from "./chat-machine.actors";
|
||||
|
||||
const log = new Logger("StoresChatChatMachine");
|
||||
@@ -72,6 +77,7 @@ export const chatMachine = setup({
|
||||
sendMessageHttp: sendMessageHttpActor,
|
||||
loadMoreHistory: loadMoreHistoryActor,
|
||||
httpMessageQueue: httpMessageQueueActor,
|
||||
unlockHistory: unlockHistoryActor,
|
||||
},
|
||||
actions: {
|
||||
enqueueMessage: sendTo("messageQueue", ({ event }) => event),
|
||||
@@ -211,6 +217,37 @@ export const chatMachine = setup({
|
||||
if (event.type !== "ChatQueuedHttpDone") return {};
|
||||
return applyHttpSendOutput(context, event.output);
|
||||
}),
|
||||
|
||||
markPaymentUnlockPending: assign(() => ({
|
||||
paymentUnlockPending: true,
|
||||
unlockHistoryError: null,
|
||||
})),
|
||||
|
||||
showUnlockHistoryPrompt: assign(({ context }) => ({
|
||||
paymentUnlockPending: false,
|
||||
unlockHistoryPromptVisible: true,
|
||||
lockedHistoryCount: countLockedHistoryMessages(context.messages),
|
||||
unlockHistoryError: null,
|
||||
})),
|
||||
|
||||
dismissUnlockHistoryPrompt: assign(() => ({
|
||||
paymentUnlockPending: false,
|
||||
unlockHistoryPromptVisible: false,
|
||||
unlockHistoryError: null,
|
||||
})),
|
||||
|
||||
markUnlockHistoryStarted: assign(() => ({
|
||||
paymentUnlockPending: false,
|
||||
unlockHistoryPromptVisible: false,
|
||||
isUnlockingHistory: true,
|
||||
unlockHistoryError: null,
|
||||
})),
|
||||
|
||||
markUnlockHistoryFailed: assign(() => ({
|
||||
isUnlockingHistory: false,
|
||||
unlockHistoryPromptVisible: true,
|
||||
unlockHistoryError: "Failed to unlock messages. Please try again.",
|
||||
})),
|
||||
},
|
||||
}).createMachine({
|
||||
id: "chat",
|
||||
@@ -264,12 +301,9 @@ export const chatMachine = setup({
|
||||
id: "loadHistory",
|
||||
src: "loadHistory",
|
||||
onDone: {
|
||||
actions: assign(({ event }) => ({
|
||||
messages: event.output.messages,
|
||||
hasMore: event.output.hasMore,
|
||||
historyOffset: event.output.newOffset,
|
||||
historyLoaded: true,
|
||||
})),
|
||||
actions: assign(({ event }) =>
|
||||
applyHistoryLoadedOutput(event.output),
|
||||
),
|
||||
},
|
||||
onError: {
|
||||
// 失败也标 loaded,不卡 init(让 UI 还是能进 ready,屏幕可能空)
|
||||
@@ -337,22 +371,80 @@ export const chatMachine = setup({
|
||||
ChatQueuedSendError: {
|
||||
actions: "appendQueuedSendErrorMessage",
|
||||
},
|
||||
ChatPaymentSucceeded: [
|
||||
{
|
||||
guard: ({ context }) =>
|
||||
context.historyLoaded && shouldAutoUnlockHistory(context.messages),
|
||||
target: ".unlockingHistory",
|
||||
actions: "markUnlockHistoryStarted",
|
||||
},
|
||||
{
|
||||
guard: ({ context }) =>
|
||||
context.historyLoaded &&
|
||||
shouldPromptUnlockHistory(context.messages),
|
||||
actions: "showUnlockHistoryPrompt",
|
||||
},
|
||||
{
|
||||
guard: ({ context }) => !context.historyLoaded,
|
||||
actions: "markPaymentUnlockPending",
|
||||
},
|
||||
{
|
||||
guard: ({ context }) => context.historyLoaded,
|
||||
actions: "dismissUnlockHistoryPrompt",
|
||||
},
|
||||
],
|
||||
ChatUnlockHistoryConfirmed: {
|
||||
target: ".unlockingHistory",
|
||||
actions: "markUnlockHistoryStarted",
|
||||
},
|
||||
ChatUnlockHistoryDismissed: {
|
||||
actions: "dismissUnlockHistoryPrompt",
|
||||
},
|
||||
},
|
||||
initial: "initializing",
|
||||
states: {
|
||||
initializing: {
|
||||
invoke: {
|
||||
src: "loadHistory",
|
||||
onDone: {
|
||||
target: "ready",
|
||||
actions: assign(({ event }) => ({
|
||||
messages: event.output.messages,
|
||||
isLoadingMore: false,
|
||||
hasMore: event.output.hasMore,
|
||||
historyOffset: event.output.newOffset,
|
||||
historyLoaded: true,
|
||||
})),
|
||||
},
|
||||
onDone: [
|
||||
{
|
||||
guard: ({ context, event }) =>
|
||||
context.paymentUnlockPending &&
|
||||
shouldAutoUnlockHistory(event.output.messages),
|
||||
target: "unlockingHistory",
|
||||
actions: [
|
||||
assign(({ event }) => ({
|
||||
...applyHistoryLoadedOutput(event.output),
|
||||
isLoadingMore: false,
|
||||
})),
|
||||
"markUnlockHistoryStarted",
|
||||
],
|
||||
},
|
||||
{
|
||||
guard: ({ context, event }) =>
|
||||
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,
|
||||
})),
|
||||
},
|
||||
{
|
||||
target: "ready",
|
||||
actions: assign(({ event }) => ({
|
||||
...applyHistoryLoadedOutput(event.output),
|
||||
isLoadingMore: false,
|
||||
paymentUnlockPending: false,
|
||||
})),
|
||||
},
|
||||
],
|
||||
onError: {
|
||||
target: "ready",
|
||||
actions: assign({
|
||||
@@ -413,6 +505,40 @@ export const chatMachine = setup({
|
||||
},
|
||||
},
|
||||
},
|
||||
unlockingHistory: {
|
||||
invoke: {
|
||||
id: "unlockHistory",
|
||||
src: "unlockHistory",
|
||||
onDone: {
|
||||
target: "ready",
|
||||
actions: assign(({ event }) => {
|
||||
const lockedHistoryCount = countLockedHistoryMessages(
|
||||
event.output.messages,
|
||||
);
|
||||
const insufficientBalance =
|
||||
!event.output.unlocked &&
|
||||
event.output.reason === "insufficient_balance";
|
||||
return {
|
||||
messages: event.output.messages,
|
||||
hasMore: event.output.hasMore,
|
||||
historyOffset: event.output.newOffset,
|
||||
historyLoaded: true,
|
||||
paymentUnlockPending: false,
|
||||
unlockHistoryPromptVisible: insufficientBalance,
|
||||
lockedHistoryCount,
|
||||
isUnlockingHistory: false,
|
||||
unlockHistoryError: insufficientBalance
|
||||
? `Insufficient credits. You still need ${event.output.shortfallCredits} credits.`
|
||||
: null,
|
||||
};
|
||||
}),
|
||||
},
|
||||
onError: {
|
||||
target: "ready",
|
||||
actions: "markUnlockHistoryFailed",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user