refactor(chat): split machine by business flow
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
import { fromCallback, fromPromise } from "xstate";
|
||||
|
||||
import {
|
||||
ChatSendResponse,
|
||||
UnlockPrivateResponse,
|
||||
type UiMessage,
|
||||
} from "@/data/dto/chat";
|
||||
import { chatMachine } from "@/stores/chat/chat-machine";
|
||||
import type { ChatEvent } from "@/stores/chat/chat-events";
|
||||
import type {
|
||||
UnlockMessageOutput as MachineUnlockMessageOutput,
|
||||
UnlockMessageRequest,
|
||||
} from "@/stores/chat/chat-machine.helpers";
|
||||
|
||||
export interface SendMessageHttpOutput {
|
||||
response: ChatSendResponse;
|
||||
reply: UiMessage | null;
|
||||
}
|
||||
|
||||
export interface UnlockHistoryOutput {
|
||||
unlocked: boolean;
|
||||
reason: string;
|
||||
shortfallCredits: number;
|
||||
messages: UiMessage[];
|
||||
}
|
||||
|
||||
export interface TestUnlockMessageOutput {
|
||||
messageId: string;
|
||||
response: UnlockPrivateResponse;
|
||||
}
|
||||
|
||||
export function makeChatSendResponse(): ChatSendResponse {
|
||||
return ChatSendResponse.from({
|
||||
reply: "",
|
||||
audioUrl: "",
|
||||
messageId: "msg-1",
|
||||
isGuest: false,
|
||||
timestamp: Date.now(),
|
||||
image: { type: null, url: null },
|
||||
lockDetail: {
|
||||
locked: false,
|
||||
reason: null,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function makeUnlockPrivateResponse(
|
||||
overrides: Partial<Parameters<typeof UnlockPrivateResponse.from>[0]> = {},
|
||||
): UnlockPrivateResponse {
|
||||
return UnlockPrivateResponse.from({
|
||||
unlocked: true,
|
||||
content: "unlocked content",
|
||||
audioUrl: "",
|
||||
reason: "ok",
|
||||
creditBalance: 90,
|
||||
creditsCharged: 10,
|
||||
requiredCredits: 10,
|
||||
shortfallCredits: 0,
|
||||
...overrides,
|
||||
});
|
||||
}
|
||||
|
||||
export function createTestChatMachine(
|
||||
options: {
|
||||
historyMessages?: UiMessage[];
|
||||
sendMessageHttpError?: Error;
|
||||
unlockHistoryOutput?: UnlockHistoryOutput;
|
||||
unlockHistoryError?: Error;
|
||||
unlockMessageOutput?: TestUnlockMessageOutput;
|
||||
} = {},
|
||||
) {
|
||||
return chatMachine.provide({
|
||||
actors: {
|
||||
loadHistory: createLoadHistoryCallback(options.historyMessages ?? []),
|
||||
sendMessageHttp: fromPromise<
|
||||
SendMessageHttpOutput,
|
||||
{ content: string }
|
||||
>(async () => {
|
||||
if (options.sendMessageHttpError) {
|
||||
throw options.sendMessageHttpError;
|
||||
}
|
||||
return {
|
||||
response: makeChatSendResponse(),
|
||||
reply: null,
|
||||
};
|
||||
}),
|
||||
httpMessageQueue: fromCallback<ChatEvent>(({ receive, sendBack }) => {
|
||||
receive((event) => {
|
||||
if (event.type !== "ChatSendMessage") return;
|
||||
sendBack({ type: "ChatQueuedSendStarted" });
|
||||
sendBack({
|
||||
type: "ChatQueuedHttpDone",
|
||||
output: {
|
||||
response: makeChatSendResponse(),
|
||||
reply: null,
|
||||
},
|
||||
});
|
||||
});
|
||||
return () => undefined;
|
||||
}),
|
||||
unlockHistory: fromPromise<UnlockHistoryOutput>(async () => {
|
||||
if (options.unlockHistoryError) {
|
||||
throw options.unlockHistoryError;
|
||||
}
|
||||
return {
|
||||
unlocked: true,
|
||||
reason: "ok",
|
||||
shortfallCredits: 0,
|
||||
messages: [],
|
||||
...options.unlockHistoryOutput,
|
||||
};
|
||||
}),
|
||||
unlockMessage: fromPromise<
|
||||
MachineUnlockMessageOutput,
|
||||
UnlockMessageRequest
|
||||
>(async ({ input }) => {
|
||||
const output = options.unlockMessageOutput ?? {
|
||||
messageId: input.messageId ?? input.displayMessageId,
|
||||
response: makeUnlockPrivateResponse(),
|
||||
};
|
||||
return {
|
||||
displayMessageId: input.displayMessageId,
|
||||
request: input,
|
||||
response: output.response,
|
||||
};
|
||||
}),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function createLoadHistoryCallback(
|
||||
messages: UiMessage[],
|
||||
networkMessages: UiMessage[] = messages,
|
||||
) {
|
||||
return fromCallback<ChatEvent>(({ sendBack }) => {
|
||||
sendBack({
|
||||
type: "ChatLocalHistoryLoaded",
|
||||
output: {
|
||||
messages,
|
||||
localCount: messages.length,
|
||||
},
|
||||
});
|
||||
sendBack({
|
||||
type: "ChatNetworkHistoryLoaded",
|
||||
output: {
|
||||
messages: networkMessages,
|
||||
localOverwritten: true,
|
||||
localCount: messages.length,
|
||||
networkCount: networkMessages.length,
|
||||
},
|
||||
});
|
||||
return () => undefined;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user