'use client'; import * as React from 'react'; import { Loader2, Trash2 } from 'lucide-react'; import { useAuth } from '@/components/id/auth-provider'; import { fetchDocumentPhotoUrl } from '@/lib/api'; import { resolveBrowserMediaUrl } from '@/lib/resilient-media-fetch'; import { cn } from '@/lib/utils'; import { DocumentFullscreenViewer, DocumentPhotoThumbnail } from './document-photo-viewer'; interface DocumentPhotoGalleryProps { userId: string; token: string | null; storageKeys: string[]; readOnly?: boolean; onRemove?: (storageKey: string) => void; layout?: 'grid' | 'strip'; className?: string; } export function DocumentPhotoGallery({ userId, token, storageKeys, readOnly, onRemove, layout = 'grid', className }: DocumentPhotoGalleryProps) { const { isPinLocked } = useAuth(); const [urls, setUrls] = React.useState>({}); const [loadingKeys, setLoadingKeys] = React.useState>(new Set()); const [viewerOpen, setViewerOpen] = React.useState(false); const [viewerIndex, setViewerIndex] = React.useState(0); React.useEffect(() => { if (!token || storageKeys.length === 0 || isPinLocked) { setUrls({}); return; } let cancelled = false; setLoadingKeys(new Set(storageKeys)); void Promise.all( storageKeys.map(async (storageKey) => { try { const response = await fetchDocumentPhotoUrl(userId, storageKey, token); return { storageKey, accessUrl: resolveBrowserMediaUrl(response.accessUrl) }; } catch { return { storageKey, accessUrl: '' }; } }) ).then((results) => { if (cancelled) return; const next: Record = {}; for (const item of results) { if (item.accessUrl) next[item.storageKey] = item.accessUrl; } setUrls(next); setLoadingKeys(new Set()); }); return () => { cancelled = true; }; }, [isPinLocked, storageKeys.join('|'), token, userId]); const resolvedImages = storageKeys .map((storageKey) => ({ storageKey, url: urls[storageKey] })) .filter((item) => Boolean(item.url)); function openViewer(storageKey: string) { const resolvedIndex = resolvedImages.findIndex((item) => item.storageKey === storageKey); if (resolvedIndex >= 0) { setViewerIndex(resolvedIndex); setViewerOpen(true); } } if (storageKeys.length === 0) return null; const isStrip = layout === 'strip'; return ( <>
event.stopPropagation()} > {storageKeys.map((storageKey) => (
openViewer(storageKey)} /> {!readOnly && onRemove ? ( ) : null} {loadingKeys.has(storageKey) ? (
) : null}
))}
); } interface DocumentPhotosInlineProps { userId: string; token: string | null; storageKeys: string[]; className?: string; } /** Горизонтальная полоска фото документа для списков (страница «Документы», «Данные»). */ export function DocumentPhotosInline({ userId, token, storageKeys, className }: DocumentPhotosInlineProps) { if (!storageKeys.length) return null; return ( ); }