From 4682b4bf3f86be5136e810ca549a0a4d2a4b0f4a Mon Sep 17 00:00:00 2001 From: chenhang Date: Mon, 13 Jul 2026 15:20:07 +0800 Subject: [PATCH] refactor(chat): simplify paid message lock state --- .../chat-insufficient-credits-banner.tsx | 2 +- src/core/net/chat-websocket.ts | 8 --- .../__tests__/unlock_private_response.test.ts | 55 +++++++-------- .../dto/chat/response/chat_send_response.ts | 1 - .../chat_media_cache_helpers.test.ts | 8 --- .../repositories/chat_local_message_store.ts | 5 -- src/data/schemas/chat/chat_payloads.ts | 10 --- src/data/schemas/chat/chat_send_response.ts | 2 - .../__tests__/chat-machine.helpers.test.ts | 70 +++++++------------ .../chat-machine.transitions.test.ts | 28 +------- .../chat/__tests__/chat-promotion.test.ts | 3 +- src/stores/chat/chat-machine.ts | 2 +- src/stores/chat/chat-message-mappers.ts | 35 +++------- src/stores/chat/chat-send-flow.ts | 13 ++-- src/stores/chat/chat-send-state.ts | 19 +---- src/stores/chat/chat-state.ts | 2 - src/stores/chat/chat-unlock-helpers.ts | 13 +++- 17 files changed, 86 insertions(+), 190 deletions(-) diff --git a/src/app/chat/components/chat-insufficient-credits-banner.tsx b/src/app/chat/components/chat-insufficient-credits-banner.tsx index 000fde17..0da54223 100644 --- a/src/app/chat/components/chat-insufficient-credits-banner.tsx +++ b/src/app/chat/components/chat-insufficient-credits-banner.tsx @@ -3,7 +3,7 @@ * ChatInsufficientCreditsBanner 积分不足横幅 * * 何时显示: - * - 后端返回 cannotSendReason="insufficient_credits" + * - 后端返回 canSendMessage=false * * 视觉规格(与设计稿对齐): * - 粉→品红渐变背景(与 splash 渐变一致) diff --git a/src/core/net/chat-websocket.ts b/src/core/net/chat-websocket.ts index 83244837..a3c35c6a 100644 --- a/src/core/net/chat-websocket.ts +++ b/src/core/net/chat-websocket.ts @@ -24,11 +24,7 @@ export interface SentencePayload { export interface PaywallStatusPayload { locked: boolean; - showContent: boolean; - showUpgrade: boolean; reason: string | null; - hint: string | null; - detail: Record | null; } export class ChatWebSocket { @@ -178,11 +174,7 @@ export class ChatWebSocket { const lockDetail = payload.data?.lockDetail; this.onPaywallStatus?.({ locked: lockDetail?.locked ?? false, - showContent: lockDetail?.showContent ?? true, - showUpgrade: lockDetail?.showUpgrade ?? false, reason: lockDetail?.reason ?? null, - hint: lockDetail?.hint ?? null, - detail: lockDetail?.detail ?? null, }); break; } diff --git a/src/data/dto/chat/__tests__/unlock_private_response.test.ts b/src/data/dto/chat/__tests__/unlock_private_response.test.ts index 1e30b9eb..bfd53544 100644 --- a/src/data/dto/chat/__tests__/unlock_private_response.test.ts +++ b/src/data/dto/chat/__tests__/unlock_private_response.test.ts @@ -15,11 +15,7 @@ describe("UnlockPrivateResponse", () => { shortfallCredits: 0, lockDetail: { locked: false, - showContent: true, - showUpgrade: false, reason: null, - hint: null, - detail: null, }, }); @@ -49,8 +45,7 @@ describe("UnlockPrivateResponse", () => { }, lockDetail: { locked: false, - showContent: true, - showUpgrade: false, + reason: null, }, }); @@ -73,13 +68,7 @@ describe("UnlockPrivateResponse", () => { shortfallCredits: 5, lockDetail: { locked: true, - showContent: false, - showUpgrade: true, - reason: "insufficient_balance", - hint: "Insufficient credits.", - detail: { - messageId: "msg_001", - }, + reason: "voice_message", }, }); @@ -89,7 +78,10 @@ describe("UnlockPrivateResponse", () => { expect(response.creditsCharged).toBe(0); expect(response.requiredCredits).toBe(10); expect(response.shortfallCredits).toBe(5); - expect(response.lockDetail.showUpgrade).toBe(true); + expect(response.lockDetail).toEqual({ + locked: true, + reason: "voice_message", + }); }); it("defaults nullable content to an empty string", () => { @@ -100,11 +92,7 @@ describe("UnlockPrivateResponse", () => { reason: "insufficient_balance", lockDetail: { locked: true, - showContent: false, - showUpgrade: true, - reason: "insufficient_balance", - hint: null, - detail: null, + reason: "private_message", }, }); @@ -123,11 +111,7 @@ describe("UnlockPrivateResponse", () => { shortfallCredits: null, lockDetail: { locked: true, - showContent: false, - showUpgrade: true, - reason: "insufficient_balance", - hint: null, - detail: null, + reason: "image_paywall", }, }); @@ -147,11 +131,26 @@ describe("UnlockPrivateResponse", () => { expect(response.lockDetail).toEqual({ locked: false, - showContent: true, - showUpgrade: false, reason: null, - hint: null, - detail: null, + }); + }); + + it("strips legacy lock detail fields while preserving lock state and type", () => { + const response = UnlockPrivateResponse.fromJson({ + unlocked: false, + lockDetail: { + locked: true, + reason: "private_message", + showContent: false, + showUpgrade: true, + hint: "Legacy hint", + detail: { messageId: "legacy-message" }, + }, + }); + + expect(response.lockDetail).toEqual({ + locked: true, + reason: "private_message", }); }); }); diff --git a/src/data/dto/chat/response/chat_send_response.ts b/src/data/dto/chat/response/chat_send_response.ts index c5c0c101..2f560cfe 100644 --- a/src/data/dto/chat/response/chat_send_response.ts +++ b/src/data/dto/chat/response/chat_send_response.ts @@ -17,7 +17,6 @@ export class ChatSendResponse { declare readonly image: ChatImageData; declare readonly lockDetail: ChatLockDetailData; declare readonly canSendMessage: boolean; - // declare readonly cannotSendReason: string | null; declare readonly creditBalance: number; declare readonly creditsCharged: number; declare readonly requiredCredits: number; diff --git a/src/data/repositories/__tests__/chat_media_cache_helpers.test.ts b/src/data/repositories/__tests__/chat_media_cache_helpers.test.ts index 43983e9f..73ed6424 100644 --- a/src/data/repositories/__tests__/chat_media_cache_helpers.test.ts +++ b/src/data/repositories/__tests__/chat_media_cache_helpers.test.ts @@ -23,11 +23,7 @@ function makeMessage( image: { type: null, url: null }, lockDetail: { locked: false, - showContent: true, - showUpgrade: false, reason: null, - hint: null, - detail: null, }, ...overrides, }); @@ -45,11 +41,7 @@ function makeResponse( image: { type: null, url: null }, lockDetail: { locked: false, - showContent: true, - showUpgrade: false, reason: null, - hint: null, - detail: null, }, ...overrides, }); diff --git a/src/data/repositories/chat_local_message_store.ts b/src/data/repositories/chat_local_message_store.ts index 777b0845..3eee8659 100644 --- a/src/data/repositories/chat_local_message_store.ts +++ b/src/data/repositories/chat_local_message_store.ts @@ -28,13 +28,8 @@ export class ChatLocalMessageStore { audioUrl: normalizeUnlockedAudioUrl(patch.audioUrl, message.audioUrl), image: patch.image ?? message.image, lockDetail: patch.lockDetail ?? { - ...message.lockDetail, locked: false, - showContent: true, - showUpgrade: false, reason: null, - hint: null, - detail: null, }, }); }); diff --git a/src/data/schemas/chat/chat_payloads.ts b/src/data/schemas/chat/chat_payloads.ts index 29714241..7fef6773 100644 --- a/src/data/schemas/chat/chat_payloads.ts +++ b/src/data/schemas/chat/chat_payloads.ts @@ -5,21 +5,15 @@ import { z } from "zod"; import { booleanOrFalse, - booleanOrTrue, schemaOr, stringOrNull, - unknownRecordOrNull, } from "../nullable-defaults"; const CHAT_IMAGE_DEFAULTS = { type: null, url: null } as const; const CHAT_LOCK_DETAIL_DEFAULTS = { locked: false, - showContent: true, - showUpgrade: false, reason: null, - hint: null, - detail: null, } as const; export const ChatImageSchema = schemaOr( @@ -33,11 +27,7 @@ export const ChatImageSchema = schemaOr( export const ChatLockDetailSchema = schemaOr( z.object({ locked: booleanOrFalse, - showContent: booleanOrTrue, - showUpgrade: booleanOrFalse, reason: stringOrNull, - hint: stringOrNull, - detail: unknownRecordOrNull, }), CHAT_LOCK_DETAIL_DEFAULTS, ); diff --git a/src/data/schemas/chat/chat_send_response.ts b/src/data/schemas/chat/chat_send_response.ts index 41012113..ffb1dc2f 100644 --- a/src/data/schemas/chat/chat_send_response.ts +++ b/src/data/schemas/chat/chat_send_response.ts @@ -9,7 +9,6 @@ import { numberOrLazy, numberOrZero, stringOrEmpty, - stringOrNull, } from "../nullable-defaults"; import { ChatImageSchema, ChatLockDetailSchema } from "./chat_payloads"; @@ -23,7 +22,6 @@ export const ChatSendResponseSchema = z.object({ image: ChatImageSchema, lockDetail: ChatLockDetailSchema, canSendMessage: booleanOrTrue, - cannotSendReason: stringOrNull, creditBalance: numberOrZero, creditsCharged: numberOrZero, requiredCredits: numberOrZero, diff --git a/src/stores/chat/__tests__/chat-machine.helpers.test.ts b/src/stores/chat/__tests__/chat-machine.helpers.test.ts index 8c1aec20..60b572b9 100644 --- a/src/stores/chat/__tests__/chat-machine.helpers.test.ts +++ b/src/stores/chat/__tests__/chat-machine.helpers.test.ts @@ -21,11 +21,7 @@ function makeResponse( image: { type: null, url: null }, lockDetail: { locked: false, - showContent: true, - showUpgrade: false, reason: null, - hint: null, - detail: null, }, ...overrides, }); @@ -40,7 +36,6 @@ function makeChatState(overrides: Partial = {}): ChatState { upgradePromptVisible: false, upgradeReason: null, canSendMessage: true, - cannotSendReason: null, creditBalance: 0, creditsCharged: 0, requiredCredits: 0, @@ -73,11 +68,7 @@ describe("sendResponseToUiMessage", () => { audioUrl: "", lockDetail: { locked: true, - showContent: false, - showUpgrade: true, reason: "voice_message", - hint: "这语气好撩呀,让人有点心跳加速呢…", - detail: { messageId: "msg-1" }, }, }), ); @@ -87,9 +78,7 @@ describe("sendResponseToUiMessage", () => { expect(message.locked).toBe(true); expect(message.lockReason).toBe("voice_message"); expect(message.lockedPrivate).toBeUndefined(); - expect(message.privateMessageHint).toBe( - "这语气好撩呀,让人有点心跳加速呢…", - ); + expect(message.privateMessageHint).toBeUndefined(); }); it("maps locked private text messages as private messages", () => { @@ -97,11 +86,7 @@ describe("sendResponseToUiMessage", () => { makeResponse({ lockDetail: { locked: true, - showContent: false, - showUpgrade: true, reason: "private_message", - hint: "Elio has a private message for you.", - detail: { messageId: "msg-1" }, }, }), ); @@ -111,9 +96,7 @@ describe("sendResponseToUiMessage", () => { expect(message.lockReason).toBe("private_message"); expect(message.isPrivate).toBe(true); expect(message.lockedPrivate).toBe(true); - expect(message.privateMessageHint).toBe( - "Elio has a private message for you.", - ); + expect(message.privateMessageHint).toBeUndefined(); }); it("keeps paywalled image messages visible but locked", () => { @@ -125,11 +108,7 @@ describe("sendResponseToUiMessage", () => { }, lockDetail: { locked: true, - showContent: false, - showUpgrade: true, reason: "image", - hint: "Activate VIP to view the full photo.", - detail: { imageId: "image-1" }, }, }), ); @@ -141,6 +120,25 @@ describe("sendResponseToUiMessage", () => { expect(message.lockReason).toBe("image"); expect(message.lockedPrivate).toBeUndefined(); }); + + it("does not mark a message as paywalled when its lock reason is unlocked", () => { + const message = sendResponseToUiMessage( + makeResponse({ + image: { + type: "jpg", + url: "https://example.com/unlocked-photo.jpg", + }, + lockDetail: { + locked: false, + reason: "image_paywall", + }, + }), + ); + + expect(message.locked).toBe(false); + expect(message.lockReason).toBe("image_paywall"); + expect(message.imagePaywalled).toBeUndefined(); + }); }); describe("applyHttpSendOutput", () => { @@ -160,22 +158,13 @@ describe("applyHttpSendOutput", () => { reply: "", messageId: "", canSendMessage: false, - cannotSendReason: "insufficient_credits", creditBalance: 0, creditsCharged: 0, requiredCredits: 2, shortfallCredits: 2, lockDetail: { - locked: true, - showContent: false, - showUpgrade: true, - reason: "insufficient_credits", - hint: "Insufficient credits.", - detail: { - requiredCredits: 2, - currentCredits: 0, - shortfallCredits: 2, - }, + locked: false, + reason: null, }, }), reply: null, @@ -186,7 +175,6 @@ describe("applyHttpSendOutput", () => { expect(nextState.upgradePromptVisible).toBe(true); expect(nextState.upgradeReason).toBe("insufficient_credits"); expect(nextState.canSendMessage).toBe(false); - expect(nextState.cannotSendReason).toBe("insufficient_credits"); expect(nextState.requiredCredits).toBe(2); expect(nextState.shortfallCredits).toBe(2); }); @@ -205,7 +193,6 @@ describe("applyHttpSendOutput", () => { makeResponse({ reply: "This one went through, but you need credits next.", canSendMessage: false, - cannotSendReason: "insufficient_credits", creditBalance: 0, requiredCredits: 2, shortfallCredits: 2, @@ -216,7 +203,6 @@ describe("applyHttpSendOutput", () => { response: makeResponse({ reply: "This one went through, but you need credits next.", canSendMessage: false, - cannotSendReason: "insufficient_credits", creditBalance: 0, requiredCredits: 2, shortfallCredits: 2, @@ -248,11 +234,7 @@ describe("localMessagesToUi", () => { image: { type: null, url: null }, lockDetail: { locked: true, - showContent: false, - showUpgrade: true, reason: "voice_message", - hint: "A locked voice message is waiting.", - detail: { messageId: "voice-1" }, }, }, ]); @@ -262,7 +244,7 @@ describe("localMessagesToUi", () => { expect(message.locked).toBe(true); expect(message.lockReason).toBe("voice_message"); expect(message.lockedPrivate).toBeUndefined(); - expect(message.privateMessageHint).toBe("A locked voice message is waiting."); + expect(message.privateMessageHint).toBeUndefined(); }); it("hides text content for image history messages", () => { @@ -280,11 +262,7 @@ describe("localMessagesToUi", () => { }, lockDetail: { locked: false, - showContent: true, - showUpgrade: false, reason: null, - hint: null, - detail: null, }, }, ]); diff --git a/src/stores/chat/__tests__/chat-machine.transitions.test.ts b/src/stores/chat/__tests__/chat-machine.transitions.test.ts index 21dfee3c..4a005878 100644 --- a/src/stores/chat/__tests__/chat-machine.transitions.test.ts +++ b/src/stores/chat/__tests__/chat-machine.transitions.test.ts @@ -48,11 +48,7 @@ function makeChatSendResponse(): ChatSendResponse { image: { type: null, url: null }, lockDetail: { locked: false, - showContent: true, - showUpgrade: false, reason: null, - hint: null, - detail: null, }, }); } @@ -71,11 +67,7 @@ function makeUnlockPrivateResponse( shortfallCredits: 0, lockDetail: { locked: false, - showContent: true, - showUpgrade: false, reason: null, - hint: null, - detail: null, }, ...overrides, }); @@ -870,11 +862,7 @@ describe("chatMachine transitions", () => { shortfallCredits: 7, lockDetail: { locked: true, - showContent: false, - showUpgrade: true, - reason: "insufficient_balance", - hint: "Insufficient credits.", - detail: { messageId: "msg-voice-locked" }, + reason: "voice_message", }, }), }, @@ -938,19 +926,10 @@ describe("chatMachine transitions", () => { timestamp: Date.now(), image: { type: null, url: null }, lockDetail: { - locked: true, - showContent: false, - showUpgrade: true, - reason: "insufficient_credits", - hint: "Insufficient credits.", - detail: { - requiredCredits: 2, - currentCredits: 0, - shortfallCredits: 2, - }, + locked: false, + reason: null, }, canSendMessage: false, - cannotSendReason: "insufficient_credits", creditBalance: 0, requiredCredits: 2, shortfallCredits: 2, @@ -970,7 +949,6 @@ describe("chatMachine transitions", () => { expect(actor.getSnapshot().context.upgradePromptVisible).toBe(false); expect(actor.getSnapshot().context.upgradeReason).toBeNull(); expect(actor.getSnapshot().context.canSendMessage).toBe(true); - expect(actor.getSnapshot().context.cannotSendReason).toBeNull(); actor.stop(); }); diff --git a/src/stores/chat/__tests__/chat-promotion.test.ts b/src/stores/chat/__tests__/chat-promotion.test.ts index c80e76d4..3d640ad1 100644 --- a/src/stores/chat/__tests__/chat-promotion.test.ts +++ b/src/stores/chat/__tests__/chat-promotion.test.ts @@ -59,8 +59,7 @@ describe("chat promotion", () => { }, lockDetail: { locked: false, - showContent: true, - showUpgrade: false, + reason: null, }, }), }); diff --git a/src/stores/chat/chat-machine.ts b/src/stores/chat/chat-machine.ts index 6bf04126..de3b6990 100644 --- a/src/stores/chat/chat-machine.ts +++ b/src/stores/chat/chat-machine.ts @@ -23,7 +23,7 @@ * * 发送能力: * - 前端不再处理本地消息额度;游客 / 注册用户均以后端响应为准。 - * - 后端返回 `cannotSendReason="insufficient_credits"` 时展示充值引导。 + * - 后端返回 `canSendMessage=false` 时展示积分不足充值引导。 * */ diff --git a/src/stores/chat/chat-message-mappers.ts b/src/stores/chat/chat-message-mappers.ts index b515fe33..4aa5c353 100644 --- a/src/stores/chat/chat-message-mappers.ts +++ b/src/stores/chat/chat-message-mappers.ts @@ -1,4 +1,5 @@ import { type ChatSendResponse, type UiMessage } from "@/data/dto/chat"; +import type { ChatLockDetailData } from "@/data/schemas/chat"; import { todayString } from "@/utils"; /** @@ -14,14 +15,7 @@ export function localMessagesToUi( createdAt: string; audioUrl?: string | null; image?: { type: string | null; url: string | null }; - lockDetail?: { - locked: boolean; - showContent: boolean; - showUpgrade: boolean; - reason: string | null; - hint: string | null; - detail: Record | null; - }; + lockDetail?: ChatLockDetailData; }>, ): UiMessage[] { return records.map((m) => ({ @@ -72,30 +66,19 @@ function getAiMessageDisplayContent(input: { } function deriveUiLockFields( - lockDetail: - | { - locked: boolean; - showContent: boolean; - showUpgrade: boolean; - reason: string | null; - hint: string | null; - } - | undefined, + lockDetail: ChatLockDetailData | undefined, imageUrl?: string | null, ): Partial { const lockReason = lockDetail?.reason ?? null; - const isPrivateMessage = lockReason === "private_message"; - const isVoiceMessage = lockReason === "voice_message"; + const isLocked = lockDetail?.locked === true; + const isPrivateMessage = isLocked && lockReason === "private_message"; + const isLockedImage = + isLocked && (lockReason === "image_paywall" || lockReason === "image"); return { ...(imageUrl ? { imageUrl } : {}), - ...(imageUrl && lockDetail?.showUpgrade ? { imagePaywalled: true } : {}), + ...(imageUrl && isLockedImage ? { imagePaywalled: true } : {}), ...(lockDetail ? { locked: lockDetail.locked, lockReason } : {}), ...(isPrivateMessage ? { isPrivate: true } : {}), - ...(isPrivateMessage && lockDetail?.locked - ? { lockedPrivate: true } - : {}), - ...((isPrivateMessage || isVoiceMessage) && lockDetail?.hint - ? { privateMessageHint: lockDetail.hint } - : {}), + ...(isPrivateMessage ? { lockedPrivate: true } : {}), }; } diff --git a/src/stores/chat/chat-send-flow.ts b/src/stores/chat/chat-send-flow.ts index 18dcef56..46dd3325 100644 --- a/src/stores/chat/chat-send-flow.ts +++ b/src/stores/chat/chat-send-flow.ts @@ -119,7 +119,6 @@ export const clearUpgradePromptAction = chatAssign(() => ({ upgradePromptVisible: false, upgradeReason: null, canSendMessage: true, - cannotSendReason: null, requiredCredits: 0, shortfallCredits: 0, })); @@ -173,7 +172,6 @@ async function sendMessageViaHttp(content: string): Promise<{ void chatRepo.prefetchMediaForSendResponse(result.data); const isInsufficientCredits = result.data.canSendMessage === false && - result.data.cannotSendReason === "insufficient_credits" && !hasRenderableSendResponse(result.data); return { response: result.data, @@ -183,12 +181,17 @@ async function sendMessageViaHttp(content: string): Promise<{ } function hasRenderableSendResponse(response: ChatSendResponse): boolean { + const isLockedPaidMessage = + response.lockDetail.locked && + (response.lockDetail.reason === "private_message" || + response.lockDetail.reason === "voice_message" || + response.lockDetail.reason === "image_paywall" || + response.lockDetail.reason === "image"); + return ( response.reply.trim().length > 0 || response.audioUrl.trim().length > 0 || Boolean(response.image.url) || - response.lockDetail.reason === "private_message" || - response.lockDetail.reason === "voice_message" || - response.lockDetail.reason === "image_paywall" + isLockedPaidMessage ); } diff --git a/src/stores/chat/chat-send-state.ts b/src/stores/chat/chat-send-state.ts index 3b1d49b5..149469c2 100644 --- a/src/stores/chat/chat-send-state.ts +++ b/src/stores/chat/chat-send-state.ts @@ -34,9 +34,7 @@ export function applyHttpSendOutput( ): Partial { const { response, reply } = output; - const isInsufficientCredits = - response.canSendMessage === false && - response.cannotSendReason === "insufficient_credits"; + const isInsufficientCredits = response.canSendMessage === false; const upgradeReason: ChatUpgradeReason | null = isInsufficientCredits ? "insufficient_credits" : null; @@ -65,19 +63,6 @@ export function applyHttpSendOutput( }; } - if ( - response.lockDetail.showUpgrade && - response.lockDetail.reason === "private_message" - ) { - return { - messages: [...context.messages, reply], - ...finishPendingReply(context), - upgradePromptVisible: false, - upgradeReason: null, - ...sendCapabilityState, - }; - } - return { messages: [...context.messages, reply], ...finishPendingReply(context), @@ -92,7 +77,6 @@ function getSendCapabilityState( ): Pick< ChatState, | "canSendMessage" - | "cannotSendReason" | "creditBalance" | "creditsCharged" | "requiredCredits" @@ -100,7 +84,6 @@ function getSendCapabilityState( > { return { canSendMessage: response.canSendMessage, - cannotSendReason: response.cannotSendReason, creditBalance: response.creditBalance, creditsCharged: response.creditsCharged, requiredCredits: response.requiredCredits, diff --git a/src/stores/chat/chat-state.ts b/src/stores/chat/chat-state.ts index 0b8a7eb1..db51ec58 100644 --- a/src/stores/chat/chat-state.ts +++ b/src/stores/chat/chat-state.ts @@ -27,7 +27,6 @@ export interface ChatState { upgradePromptVisible: boolean; upgradeReason: ChatUpgradeReason | null; canSendMessage: boolean; - cannotSendReason: string | null; creditBalance: number; creditsCharged: number; requiredCredits: number; @@ -63,7 +62,6 @@ export const initialState: ChatState = { upgradePromptVisible: false, upgradeReason: null, canSendMessage: true, - cannotSendReason: null, creditBalance: 0, creditsCharged: 0, requiredCredits: 0, diff --git a/src/stores/chat/chat-unlock-helpers.ts b/src/stores/chat/chat-unlock-helpers.ts index e0459d92..9760ed61 100644 --- a/src/stores/chat/chat-unlock-helpers.ts +++ b/src/stores/chat/chat-unlock-helpers.ts @@ -51,8 +51,7 @@ export function applySingleUnlockOutput( locked: output.response.lockDetail.locked, lockReason: output.response.lockDetail.reason, imagePaywalled: resolvedImageUrl - ? output.response.lockDetail.locked && - output.response.lockDetail.showUpgrade + ? isLockedImage(output.response.lockDetail) : undefined, lockedPrivate: false, privateMessageHint: null, @@ -60,6 +59,16 @@ export function applySingleUnlockOutput( }); } +function isLockedImage(lockDetail: { + locked: boolean; + reason: string | null; +}): boolean { + return ( + lockDetail.locked && + (lockDetail.reason === "image_paywall" || lockDetail.reason === "image") + ); +} + function getUnlockedAudioUrl( message: UiMessage, response: UnlockPrivateResponse,