feat(app): improve payment and paywall UX

This commit is contained in:
2026-06-24 10:02:22 +08:00
parent 6c25a24440
commit bfbf7ee91a
23 changed files with 344 additions and 129 deletions
@@ -9,6 +9,7 @@
* - 点击空白 / 下滑手势 → 关闭
* - 双指缩放(CSS `touch-action: pinch-zoom`
*/
import { ChevronLeft } from "lucide-react";
import Image from "next/image";
import { useEffect } from "react";
@@ -16,10 +17,17 @@ import styles from "./fullscreen-image-viewer.module.css";
export interface FullscreenImageViewerProps {
imageUrl: string;
imagePaywalled?: boolean;
onUnlockImagePaywall?: () => void;
onClose: () => void;
}
export function FullscreenImageViewer({ imageUrl, onClose }: FullscreenImageViewerProps) {
export function FullscreenImageViewer({
imageUrl,
imagePaywalled = false,
onUnlockImagePaywall,
onClose,
}: FullscreenImageViewerProps) {
useEffect(() => {
const handleKey = (e: KeyboardEvent) => {
if (e.key === "Escape") onClose();
@@ -28,6 +36,44 @@ export function FullscreenImageViewer({ imageUrl, onClose }: FullscreenImageView
return () => document.removeEventListener("keydown", handleKey);
}, [onClose]);
const src = imageUrl.startsWith("data:") || imageUrl.startsWith("http")
? imageUrl
: `data:image/png;base64,${imageUrl}`;
if (imagePaywalled) {
return (
<div
className={`${styles.viewer} ${styles.paywallViewer}`}
role="dialog"
aria-label="Locked fullscreen image"
>
<Image
src={src}
alt=""
fill
sizes="100vw"
className={styles.paywallImage}
/>
<div className={styles.paywallScrim} aria-hidden="true" />
<button
type="button"
className={styles.backButton}
onClick={onClose}
aria-label="Back"
>
<ChevronLeft size={34} strokeWidth={2.5} aria-hidden="true" />
</button>
<button
type="button"
className={styles.unlockButton}
onClick={onUnlockImagePaywall}
>
Unlock high-definition large image
</button>
</div>
);
}
return (
<div
className={styles.viewer}
@@ -36,7 +82,7 @@ export function FullscreenImageViewer({ imageUrl, onClose }: FullscreenImageView
aria-label="Fullscreen image"
>
<Image
src={imageUrl.startsWith("data:") || imageUrl.startsWith("http") ? imageUrl : `data:image/png;base64,${imageUrl}`}
src={src}
alt=""
width={800}
height={800}