refactor(chat): split machine by business flow
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
import { type DoneActorEvent } from "xstate";
|
||||
|
||||
import {
|
||||
applyPromotionUnlockOutput,
|
||||
applySingleUnlockOutput,
|
||||
countLockedHistoryMessages,
|
||||
type UnlockMessageOutput,
|
||||
} from "../chat-machine.helpers";
|
||||
import type { UnlockHistoryOutput } from "../chat-unlock-flow";
|
||||
import { sendMachineSetup } from "./send-flow";
|
||||
import { createChatActorActionSetup } from "./setup";
|
||||
|
||||
const markPaymentUnlockPendingAction = sendMachineSetup.assign(() => ({
|
||||
paymentUnlockPending: true,
|
||||
unlockHistoryError: null,
|
||||
}));
|
||||
|
||||
const showUnlockHistoryPromptAction = sendMachineSetup.assign(
|
||||
({ context }) => ({
|
||||
paymentUnlockPending: false,
|
||||
unlockHistoryPromptVisible: true,
|
||||
lockedHistoryCount: countLockedHistoryMessages(context.messages),
|
||||
unlockHistoryError: null,
|
||||
}),
|
||||
);
|
||||
|
||||
const dismissUnlockHistoryPromptAction = sendMachineSetup.assign(() => ({
|
||||
paymentUnlockPending: false,
|
||||
unlockHistoryPromptVisible: false,
|
||||
unlockHistoryError: null,
|
||||
}));
|
||||
|
||||
const markUnlockHistoryStartedAction = sendMachineSetup.assign(() => ({
|
||||
paymentUnlockPending: false,
|
||||
unlockHistoryPromptVisible: false,
|
||||
isUnlockingHistory: true,
|
||||
unlockHistoryError: null,
|
||||
}));
|
||||
|
||||
const markUnlockHistoryFailedAction = sendMachineSetup.assign(() => ({
|
||||
isUnlockingHistory: false,
|
||||
unlockHistoryPromptVisible: true,
|
||||
unlockHistoryError: "Failed to unlock messages. Please try again.",
|
||||
}));
|
||||
|
||||
const markUnlockMessageStartedAction = sendMachineSetup.assign(
|
||||
({ event }) => {
|
||||
if (event.type !== "ChatUnlockMessageRequested") return {};
|
||||
return {
|
||||
isUnlockingMessage: true,
|
||||
unlockingMessageId: event.messageId,
|
||||
unlockingMessageKind: event.kind,
|
||||
unlockingRemoteMessageId:
|
||||
event.remoteMessageId ?? (event.lockType ? null : event.messageId),
|
||||
unlockingLockType: event.lockType ?? null,
|
||||
unlockingClientLockId: event.clientLockId ?? null,
|
||||
unlockMessageError: null,
|
||||
unlockPaywallRequest: null,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
const applyUnlockMessageSucceededAction = sendMachineSetup.assign(
|
||||
({ context, event }) => {
|
||||
const { output } = event as unknown as DoneActorEvent<UnlockMessageOutput>;
|
||||
return {
|
||||
messages: applySingleUnlockOutput(context.messages, output),
|
||||
promotion: applyPromotionUnlockOutput(context.promotion, output),
|
||||
isUnlockingMessage: false,
|
||||
unlockingMessageId: null,
|
||||
unlockingMessageKind: null,
|
||||
unlockingRemoteMessageId: null,
|
||||
unlockingLockType: null,
|
||||
unlockingClientLockId: null,
|
||||
unlockMessageError: null,
|
||||
unlockPaywallRequest: null,
|
||||
creditBalance: output.response.creditBalance,
|
||||
creditsCharged: output.response.creditsCharged,
|
||||
requiredCredits: 0,
|
||||
shortfallCredits: 0,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
const requestUnlockPaymentFromOutputAction = sendMachineSetup.assign(
|
||||
({ context, event }) => {
|
||||
const { output } = event as unknown as DoneActorEvent<UnlockMessageOutput>;
|
||||
return {
|
||||
promotion: applyPromotionUnlockOutput(context.promotion, output),
|
||||
isUnlockingMessage: false,
|
||||
unlockingMessageId: null,
|
||||
unlockingMessageKind: null,
|
||||
unlockingRemoteMessageId: null,
|
||||
unlockingLockType: null,
|
||||
unlockingClientLockId: null,
|
||||
unlockMessageError: output.response.reason,
|
||||
unlockPaywallRequest: {
|
||||
displayMessageId:
|
||||
output.response.messageId || output.displayMessageId,
|
||||
...(output.response.messageId || output.request.messageId
|
||||
? {
|
||||
messageId:
|
||||
output.response.messageId || output.request.messageId,
|
||||
}
|
||||
: {}),
|
||||
kind: context.unlockingMessageKind ?? "private",
|
||||
...(output.request.lockType
|
||||
? { lockType: output.request.lockType }
|
||||
: {}),
|
||||
...(output.request.clientLockId
|
||||
? { clientLockId: output.request.clientLockId }
|
||||
: {}),
|
||||
...(context.promotion?.message.id === output.displayMessageId
|
||||
? { promotion: context.promotion.session }
|
||||
: {}),
|
||||
reason: output.response.reason,
|
||||
creditBalance: output.response.creditBalance,
|
||||
requiredCredits: output.response.requiredCredits,
|
||||
shortfallCredits: output.response.shortfallCredits,
|
||||
},
|
||||
creditBalance: output.response.creditBalance,
|
||||
requiredCredits: output.response.requiredCredits,
|
||||
shortfallCredits: output.response.shortfallCredits,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
const requestUnlockPaymentFromErrorAction = sendMachineSetup.assign(
|
||||
({ context }) => ({
|
||||
isUnlockingMessage: false,
|
||||
unlockMessageError: "unlock_failed",
|
||||
unlockPaywallRequest:
|
||||
context.unlockingMessageId && context.unlockingMessageKind
|
||||
? {
|
||||
displayMessageId: context.unlockingMessageId,
|
||||
...(context.unlockingRemoteMessageId
|
||||
? { messageId: context.unlockingRemoteMessageId }
|
||||
: {}),
|
||||
kind: context.unlockingMessageKind,
|
||||
...(context.unlockingLockType
|
||||
? { lockType: context.unlockingLockType }
|
||||
: {}),
|
||||
...(context.unlockingClientLockId
|
||||
? { clientLockId: context.unlockingClientLockId }
|
||||
: {}),
|
||||
...(context.promotion?.message.id === context.unlockingMessageId
|
||||
? { promotion: context.promotion.session }
|
||||
: {}),
|
||||
reason: "unlock_failed",
|
||||
creditBalance: context.creditBalance,
|
||||
requiredCredits: context.requiredCredits,
|
||||
shortfallCredits: context.shortfallCredits,
|
||||
}
|
||||
: null,
|
||||
unlockingMessageId: null,
|
||||
unlockingMessageKind: null,
|
||||
unlockingRemoteMessageId: null,
|
||||
unlockingLockType: null,
|
||||
unlockingClientLockId: null,
|
||||
}),
|
||||
);
|
||||
|
||||
const clearUnlockPaywallRequestAction = sendMachineSetup.assign(() => ({
|
||||
unlockPaywallRequest: null,
|
||||
}));
|
||||
|
||||
export const unlockMachineSetup = sendMachineSetup.extend({
|
||||
actions: {
|
||||
markPaymentUnlockPending: markPaymentUnlockPendingAction,
|
||||
showUnlockHistoryPrompt: showUnlockHistoryPromptAction,
|
||||
dismissUnlockHistoryPrompt: dismissUnlockHistoryPromptAction,
|
||||
markUnlockHistoryStarted: markUnlockHistoryStartedAction,
|
||||
markUnlockHistoryFailed: markUnlockHistoryFailedAction,
|
||||
markUnlockMessageStarted: markUnlockMessageStartedAction,
|
||||
applyUnlockMessageSucceeded: applyUnlockMessageSucceededAction,
|
||||
requestUnlockPaymentFromOutput: requestUnlockPaymentFromOutputAction,
|
||||
requestUnlockPaymentFromError: requestUnlockPaymentFromErrorAction,
|
||||
clearUnlockPaywallRequest: clearUnlockPaywallRequestAction,
|
||||
},
|
||||
});
|
||||
|
||||
const unlockHistoryDoneActionSetup =
|
||||
createChatActorActionSetup<DoneActorEvent<UnlockHistoryOutput>>();
|
||||
|
||||
const applyUnlockHistoryOutputAction =
|
||||
unlockHistoryDoneActionSetup.assign(({ event }) => {
|
||||
const lockedHistoryCount = countLockedHistoryMessages(
|
||||
event.output.messages,
|
||||
);
|
||||
const insufficientBalance =
|
||||
!event.output.unlocked &&
|
||||
event.output.reason === "insufficient_balance";
|
||||
return {
|
||||
messages: event.output.messages,
|
||||
historyLoaded: true,
|
||||
paymentUnlockPending: false,
|
||||
unlockHistoryPromptVisible: insufficientBalance,
|
||||
lockedHistoryCount,
|
||||
isUnlockingHistory: false,
|
||||
unlockHistoryError: insufficientBalance
|
||||
? `Insufficient credits. You still need ${event.output.shortfallCredits} credits.`
|
||||
: null,
|
||||
};
|
||||
});
|
||||
|
||||
export const unlockingHistoryState = unlockMachineSetup.createStateConfig({
|
||||
invoke: {
|
||||
id: "unlockHistory",
|
||||
src: "unlockHistory",
|
||||
onDone: {
|
||||
target: "ready",
|
||||
actions: applyUnlockHistoryOutputAction,
|
||||
},
|
||||
onError: {
|
||||
target: "ready",
|
||||
actions: "markUnlockHistoryFailed",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const unlockingMessageState = unlockMachineSetup.createStateConfig({
|
||||
invoke: {
|
||||
id: "unlockMessage",
|
||||
src: "unlockMessage",
|
||||
input: ({ context }) => ({
|
||||
displayMessageId: context.unlockingMessageId ?? "",
|
||||
...(context.unlockingRemoteMessageId
|
||||
? { messageId: context.unlockingRemoteMessageId }
|
||||
: {}),
|
||||
...(context.unlockingLockType
|
||||
? { lockType: context.unlockingLockType }
|
||||
: {}),
|
||||
...(context.unlockingClientLockId
|
||||
? { clientLockId: context.unlockingClientLockId }
|
||||
: {}),
|
||||
}),
|
||||
onDone: [
|
||||
{
|
||||
guard: ({ event }) => event.output.response.unlocked,
|
||||
target: "ready",
|
||||
actions: "applyUnlockMessageSucceeded",
|
||||
},
|
||||
{
|
||||
target: "ready",
|
||||
actions: "requestUnlockPaymentFromOutput",
|
||||
},
|
||||
],
|
||||
onError: {
|
||||
target: "ready",
|
||||
actions: "requestUnlockPaymentFromError",
|
||||
},
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user