import type { ChatLockType, UnlockPrivateResponse, } from "@/data/schemas/chat"; import type { UiMessage } from "@/stores/chat/ui-message"; export interface UnlockMessageRequest { displayMessageId: string; messageId?: string; lockType?: ChatLockType; clientLockId?: string; } export interface UnlockMessageOutput { displayMessageId: string; request: UnlockMessageRequest; response: UnlockPrivateResponse; } export function countLockedHistoryMessages(messages: readonly UiMessage[]): number { return messages.filter(isUnlockableLockedMessage).length; } export function applySingleUnlockOutput( messages: readonly UiMessage[], output: UnlockMessageOutput, ): UiMessage[] { return messages.map((message) => { if (!shouldApplySingleUnlock(message, output.displayMessageId)) { return message; } const remoteId = output.response.messageId || message.remoteId; if (!output.response.unlocked) { return { ...message, ...(remoteId ? { remoteId } : {}), }; } const resolvedImageUrl = output.response.image.url ?? message.imageUrl; const resolvedContent = message.lockReason === "private_message" ? output.response.content || message.content : message.content; return { ...message, ...(remoteId ? { remoteId } : {}), content: resolvedContent, audioUrl: getUnlockedAudioUrl(message, output.response), imageUrl: resolvedImageUrl, locked: false, lockReason: null, imagePaywalled: resolvedImageUrl ? false : undefined, lockedPrivate: false, privateMessageHint: null, }; }); } function getUnlockedAudioUrl( message: UiMessage, response: UnlockPrivateResponse, ): string | undefined { if (message.lockReason !== "voice_message") return message.audioUrl; if (response.audioUrl.length === 0) return message.audioUrl; return response.audioUrl; } function shouldApplySingleUnlock( message: UiMessage, displayMessageId: string, ): boolean { if (message.displayId !== displayMessageId) return false; if (!message.isFromAI) return false; if (message.locked !== true) return false; return ( message.imagePaywalled === true || message.lockReason === "image_paywall" || message.lockReason === "image" || message.lockReason === "private_message" || message.lockReason === "voice_message" ); } export function shouldPromptUnlockHistory(messages: readonly UiMessage[]): boolean { return countLockedHistoryMessages(messages) > 1; } function isUnlockableLockedMessage(message: UiMessage): boolean { if (!message.isFromAI || message.locked !== true) return false; if (message.imagePaywalled === true) return true; return ( message.lockReason === "image_paywall" || message.lockReason === "image" || message.lockReason === "private_message" || message.lockReason === "voice_message" ); }