Files
cozsweet-frontend-nextjs/src/app/chat/components/fullscreen-image-viewer.tsx
T

99 lines
2.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
/**
* FullscreenImageViewer 全屏图片查看器
*
* 原始 Dart: lib/ui/chat/widgets/fullscreen_image_viewer.dart76 行)
*
* 行为:
* - 全屏黑色背景 + 图片居中(contain)
* - 点击空白 / 下滑手势 → 关闭
* - 双指缩放(CSS `touch-action: pinch-zoom`
*/
import { ChevronLeft } from "lucide-react";
import Image from "next/image";
import { useEffect } from "react";
import styles from "./fullscreen-image-viewer.module.css";
export interface FullscreenImageViewerProps {
imageUrl: string;
imagePaywalled?: boolean;
onUnlockImagePaywall?: () => void;
onClose: () => void;
}
export function FullscreenImageViewer({
imageUrl,
imagePaywalled = false,
onUnlockImagePaywall,
onClose,
}: FullscreenImageViewerProps) {
useEffect(() => {
const handleKey = (e: KeyboardEvent) => {
if (e.key === "Escape") onClose();
};
document.addEventListener("keydown", handleKey);
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}
onClick={onClose}
role="dialog"
aria-label="Fullscreen image"
>
<Image
src={src}
alt=""
width={800}
height={800}
className={styles.viewerImage}
onError={(e) => {
(e.currentTarget as HTMLImageElement).style.display = "none";
(e.currentTarget.parentElement as HTMLElement).innerHTML =
'<div class="error">🖼</div>';
}}
/>
</div>
);
}