feat(chat): add private message unlock flow

This commit is contained in:
2026-06-23 13:21:57 +08:00
parent c9d75b6fe6
commit ebd44c4980
20 changed files with 455 additions and 29 deletions
+26 -14
View File
@@ -8,7 +8,7 @@ import type { LoginStatus } from "@/data/dto/auth";
import { AppStorage } from "@/data/storage/app/app_storage";
import { AuthStorage } from "@/data/storage/auth/auth_storage";
import { UserStorage } from "@/data/storage/user/user_storage";
import { useChatState } from "@/stores/chat/chat-context";
import { useChatDispatch, useChatState } from "@/stores/chat/chat-context";
import { AppEnvUtil, BrowserDetector, todayString, UrlLauncherUtil } from "@/utils";
import { ROUTE_BUILDERS, ROUTES } from "@/router/routes";
@@ -35,6 +35,7 @@ function deriveIsGuest(loginStatus: LoginStatus): boolean {
export function ChatScreen() {
const state = useChatState();
const chatDispatch = useChatDispatch();
const authState = useAuthState();
const [showExternalBrowserDialog, setShowExternalBrowserDialog] =
useState(false);
@@ -48,8 +49,16 @@ export function ChatScreen() {
state.paywallReason === "daily_limit";
const showPhotoPaywallBanner =
state.paywallTriggered && state.paywallReason === "photo_paywall";
const showExhaustedBanner =
showDailyLimitBanner || showPhotoPaywallBanner;
const showPrivatePaywallBanner =
state.paywallTriggered && state.paywallReason === "private_paywall";
const showInlineUpgradeBanner =
showPhotoPaywallBanner || showPrivatePaywallBanner;
const inlineUpgradeTitle = showPrivatePaywallBanner
? "Unlock VIP to view private messages"
: "Unlock VIP to view Elio's photos";
const inlineUpgradeCta = showPrivatePaywallBanner
? "Activate VIP to view private messages"
: "Activate VIP to view photos";
const externalBrowserPromptShownRef = useRef(false);
@@ -122,6 +131,10 @@ export function ChatScreen() {
UrlLauncherUtil.openUrlWithExternalBrowser(ROUTES.splash);
}
function handleUnlockPrivateMessage(messageId: string): void {
chatDispatch({ type: "ChatUnlockPrivateMessage", messageId });
}
return (
<MobileShell>
<div className={styles.shell}>
@@ -144,20 +157,19 @@ export function ChatScreen() {
messages={state.messages}
isReplyingAI={state.isReplyingAI}
isGuest={isGuest}
unlockingPrivateMessageId={state.unlockingPrivateMessageId}
onUnlockPrivateMessage={handleUnlockPrivateMessage}
/>
{showExhaustedBanner ? (
{showInlineUpgradeBanner ? (
<ChatQuotaExhaustedBanner
title={inlineUpgradeTitle}
ctaLabel={inlineUpgradeCta}
/>
) : null}
{showDailyLimitBanner ? (
<ChatQuotaExhaustedBanner
title={
showPhotoPaywallBanner
? "Unlock VIP to view Elio's photos"
: undefined
}
ctaLabel={
showPhotoPaywallBanner
? "Activate VIP to view photos"
: undefined
}
/>
) : (
<ChatInputBar disabled={state.isReplyingAI} />