import { memo, useCallback, useEffect, useState, type ReactNode } from "react"; import { createPortal } from "react-dom"; import { ChevronLeftIcon, ChevronRightIcon, XIcon } from "lucide-react"; import { Button } from "../ui/button"; import type { ExpandedImagePreview } from "./ExpandedImagePreview"; import { prepareVideoFirstFrame } from "../../lib/videoFirstFrame"; import { resolveExternalWebLinkHost } from "./externalLinkContextMenu"; import { OpenMediaLink } from "../media/OpenMediaLink"; import { MediaActions, type MediaActionSource } from "../media/MediaActions"; import { isContextMenuOpen } from "../../contextMenuFallback"; interface ExpandedImageDialogProps { preview: ExpandedImagePreview; onClose: () => void; } function ExpandedMediaFailure({ children }: { children: ReactNode }) { return (
{children}
); } export const ExpandedImageDialog = memo(function ExpandedImageDialog({ preview, onClose, }: ExpandedImageDialogProps) { const [imageOffset, setImageOffset] = useState(0); const [failedVideoSrc, setFailedVideoSrc] = useState(null); const [failedImageSrc, setFailedImageSrc] = useState(null); const index = (preview.index + imageOffset + preview.images.length) % preview.images.length; const item = preview.images[index]; const source: MediaActionSource = item?.actionsSource ?? { kind: item?.type === "video" ? "video" : "image", name: item?.name ?? "Media", src: item?.src ?? null, }; const openFile = source.onOpenFile; const actionsSource: MediaActionSource = openFile ? { ...source, onOpenFile: () => { openFile(); onClose(); }, } : source; const navigateImage = useCallback((direction: -1 | 1) => { setImageOffset((current) => current + direction); }, []); useEffect(() => { const onKeyDown = (event: globalThis.KeyboardEvent) => { if (event.defaultPrevented || isContextMenuOpen()) { return; } if (event.key === "Escape") { event.preventDefault(); event.stopPropagation(); onClose(); return; } if (preview.images.length <= 1) return; if (event.key === "ArrowLeft") { event.preventDefault(); event.stopPropagation(); navigateImage(-1); return; } if (event.key !== "ArrowRight") return; event.preventDefault(); event.stopPropagation(); navigateImage(1); }; window.addEventListener("keydown", onKeyDown); return () => window.removeEventListener("keydown", onKeyDown); }, [navigateImage, onClose, preview.images.length]); if (!item) return null; const mediaLabel = item.type === "video" ? "video" : "image"; const openOriginalLink = item.originalUrl && resolveExternalWebLinkHost(item.originalUrl) !== null ? ( ) : null; return createPortal(
)}
{item.type === "video" && failedVideoSrc === item.src ? (

This video could not be loaded or played.

) : item.type === "video" ? (
{preview.images.length > 1 && ( )}
, document.body, ); });