feat(chat): unlock history after payment

This commit is contained in:
2026-06-29 10:53:52 +08:00
parent 58cd2a7545
commit b7779878cf
15 changed files with 591 additions and 18 deletions
+12 -1
View File
@@ -5,7 +5,7 @@ import Image from "next/image";
import { useRouter } from "next/navigation";
import { useAuthState } from "@/stores/auth/auth-context";
import { useChatState } from "@/stores/chat/chat-context";
import { useChatDispatch, useChatState } from "@/stores/chat/chat-context";
import { MobileShell } from "@/app/_components/core";
@@ -16,6 +16,7 @@ import {
ChatInputBar,
ChatQuotaExhaustedBanner,
ExternalBrowserDialog,
HistoryUnlockDialog,
PwaInstallOverlay,
} from "./components";
import {
@@ -31,6 +32,7 @@ import styles from "./components/chat-screen.module.css";
export function ChatScreen() {
const state = useChatState();
const chatDispatch = useChatDispatch();
const authState = useAuthState();
const router = useRouter();
const [showExternalBrowserDialog, setShowExternalBrowserDialog] =
@@ -159,6 +161,15 @@ export function ChatScreen() {
onClose={() => setShowExternalBrowserDialog(false)}
onConfirm={() => void handleOpenExternalBrowser()}
/>
<HistoryUnlockDialog
open={state.unlockHistoryPromptVisible}
lockedCount={state.lockedHistoryCount}
isLoading={state.isUnlockingHistory}
errorMessage={state.unlockHistoryError}
onClose={() => chatDispatch({ type: "ChatUnlockHistoryDismissed" })}
onConfirm={() => chatDispatch({ type: "ChatUnlockHistoryConfirmed" })}
/>
</div>
</MobileShell>
);
@@ -0,0 +1,80 @@
.overlay {
position: fixed;
inset: 0;
z-index: 75;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.5);
}
.dialog {
width: calc(100% - 40px);
max-width: var(--pwa-install-dialog-max-width, 360px);
padding: 24px 16px 16px;
text-align: center;
background: var(--color-page-background, #ffffff);
border-radius: 40px;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
}
.title {
margin: 0 0 10px;
font-size: var(--font-size-22, 22px);
font-weight: 700;
line-height: 1.2;
color: var(--color-text-foreground, #171717);
}
.content {
margin: 0 24px var(--spacing-lg, 16px);
font-size: var(--font-size-lg, 16px);
line-height: 1.5;
text-align: left;
color: #393939;
}
.error {
margin: 0 24px var(--spacing-lg, 16px);
font-size: var(--font-size-md, 14px);
line-height: 1.4;
text-align: left;
color: #c0364c;
}
.actions {
display: flex;
gap: var(--spacing-md, 12px);
width: 100%;
}
.button {
flex: 1 1 auto;
height: var(--pwa-button-height, 44px);
border: 0;
border-radius: var(--radius-bottom-sheet, 28px);
font-size: var(--font-size-lg, 16px);
font-weight: 600;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.button:disabled {
cursor: not-allowed;
opacity: 0.72;
}
.secondary {
color: var(--color-page-background, #ffffff);
background: var(--color-text-secondary, #9e9e9e);
}
.primary {
color: var(--color-page-background, #ffffff);
background: linear-gradient(
var(--color-button-gradient-start, #ff67e0),
var(--color-button-gradient-end, #ff52a2)
);
}
@@ -0,0 +1,63 @@
"use client";
import styles from "./history-unlock-dialog.module.css";
export interface HistoryUnlockDialogProps {
open: boolean;
lockedCount: number;
isLoading?: boolean;
errorMessage?: string | null;
onClose: () => void;
onConfirm: () => void;
}
export function HistoryUnlockDialog({
open,
lockedCount,
isLoading = false,
errorMessage,
onClose,
onConfirm,
}: HistoryUnlockDialogProps) {
if (!open) return null;
return (
<div
className={styles.overlay}
role="dialog"
aria-modal="true"
aria-labelledby="history-unlock-title"
>
<div className={styles.dialog}>
<h2 id="history-unlock-title" className={styles.title}>
Unlock previous messages?
</h2>
<p className={styles.content}>
We found {lockedCount} locked messages in your chat history. You can
unlock them now to continue the conversation with full context.
</p>
{errorMessage ? (
<p className={styles.error}>{errorMessage}</p>
) : null}
<div className={styles.actions}>
<button
type="button"
className={`${styles.button} ${styles.secondary}`}
onClick={onClose}
disabled={isLoading}
>
Later
</button>
<button
type="button"
className={`${styles.button} ${styles.primary}`}
onClick={onConfirm}
disabled={isLoading}
>
{isLoading ? "Unlocking..." : "Unlock now"}
</button>
</div>
</div>
</div>
);
}
+1
View File
@@ -14,6 +14,7 @@ export * from "./chat-send-button";
export * from "./date-header";
export * from "./external-browser-dialog";
export * from "./fullscreen-image-viewer";
export * from "./history-unlock-dialog";
export * from "./image-bubble";
export * from "./language-dialog";
export * from "./lottie-message-bubble";