Files
cozsweet-frontend-nextjs/src/stores/chat/machine/actors/unlock.ts
T

112 lines
3.3 KiB
TypeScript

import { fromPromise } from "xstate";
import { loadChatRepository } from "@/data/repositories/chat_repository_loader";
import { resolveChatConversationKey } from "@/data/repositories/chat_cache_identity";
import { Logger } from "@/utils/logger";
import { Result } from "@/utils/result";
import { isAbortError } from "@/utils/abort";
import { readAndSyncHistory } from "../../chat-history-sync";
import {
type UnlockMessageRequest,
type UnlockMessageOutput,
} from "../../helper/unlock";
const log = new Logger("StoresChatChatUnlockFlow");
type UiMessage = import("@/stores/chat/ui-message").UiMessage;
export interface UnlockHistoryOutput {
unlocked: boolean;
reason: string;
shortfallCredits: number;
messages: UiMessage[];
}
export const unlockHistoryActor = fromPromise<
UnlockHistoryOutput,
{ characterId: string }
>(async ({ input, signal }) => {
const chatRepo = await loadChatRepository();
const unlockResult = await chatRepo.unlockHistory(input.characterId, {
signal,
});
if (Result.isErr(unlockResult)) {
if (isAbortError(unlockResult.error)) throw unlockResult.error;
log.error("[chat-machine] unlockHistoryActor failed", {
error: unlockResult.error,
});
throw unlockResult.error;
}
signal.throwIfAborted();
const history = await readAndSyncHistory(input.characterId, signal);
return {
unlocked: unlockResult.data.unlocked,
reason: unlockResult.data.reason,
shortfallCredits: unlockResult.data.shortfallCredits,
messages: history.messages,
};
});
export const unlockMessageActor = fromPromise<
UnlockMessageOutput,
UnlockMessageRequest & { characterId: string }
>(async ({ input, signal }) => {
const chatRepo = await loadChatRepository();
const cacheIdentityResult = await resolveChatConversationKey(
input.characterId,
);
const unlockResult = await chatRepo.unlockPrivateMessage(
{
characterId: input.characterId,
...(input.messageId ? { messageId: input.messageId } : {}),
...(input.lockType ? { lockType: input.lockType } : {}),
...(input.clientLockId ? { clientLockId: input.clientLockId } : {}),
},
{ signal },
);
if (Result.isErr(unlockResult)) {
if (isAbortError(unlockResult.error)) throw unlockResult.error;
log.error("[chat-machine] unlockMessageActor failed", {
messageId: input.messageId,
clientLockId: input.clientLockId,
error: unlockResult.error,
});
throw unlockResult.error;
}
signal.throwIfAborted();
if (
unlockResult.data.unlocked &&
input.messageId &&
!input.clientLockId &&
Result.isOk(cacheIdentityResult)
) {
const markResult = await chatRepo.markPrivateMessageUnlockedInLocal(
input.messageId,
{
content: unlockResult.data.content,
audioUrl: unlockResult.data.audioUrl,
...(unlockResult.data.image.url
? { image: unlockResult.data.image }
: {}),
lockDetail: { locked: false, reason: null },
},
cacheIdentityResult.data,
);
if (Result.isErr(markResult)) {
log.warn("[chat-machine] mark unlocked local message failed", {
messageId: input.messageId,
error: markResult.error,
});
}
}
return {
displayMessageId: input.displayMessageId,
request: input,
response: unlockResult.data,
};
});