diff --git a/client/src/components/Memories/MemoriesPanel.tsx b/client/src/components/Memories/MemoriesPanel.tsx index 9193aa5..3d11886 100644 --- a/client/src/components/Memories/MemoriesPanel.tsx +++ b/client/src/components/Memories/MemoriesPanel.tsx @@ -1,5 +1,5 @@ import { useState, useEffect, useCallback } from 'react' -import { Camera, Plus, Share2, EyeOff, Eye, X, Check, Search, ArrowUpDown, MapPin, Filter, Link2, RefreshCw, Unlink, FolderOpen } from 'lucide-react' +import { Camera, Plus, Share2, EyeOff, Eye, X, Check, Search, ArrowUpDown, MapPin, Filter, Link2, RefreshCw, Unlink, FolderOpen, Info } from 'lucide-react' import apiClient from '../../api/client' import { useAuthStore } from '../../store/authStore' import { useTranslation } from '../../i18n' @@ -128,6 +128,14 @@ export default function MemoriesPanel({ tripId, startDate, endDate }: MemoriesPa const [lightboxInfo, setLightboxInfo] = useState(null) const [lightboxInfoLoading, setLightboxInfoLoading] = useState(false) const [lightboxOriginalSrc, setLightboxOriginalSrc] = useState('') + const [showMobileInfo, setShowMobileInfo] = useState(false) + const [isMobile, setIsMobile] = useState(() => window.innerWidth < 768) + + useEffect(() => { + const handleResize = () => setIsMobile(window.innerWidth < 768) + window.addEventListener('resize', handleResize) + return () => window.removeEventListener('resize', handleResize) + }, []) // ── Init ────────────────────────────────────────────────────────────────── @@ -750,117 +758,153 @@ export default function MemoriesPanel({ tripId, startDate, endDate }: MemoriesPa )} {/* Lightbox */} - {lightboxId && lightboxUserId && ( -
{ if (lightboxOriginalSrc) URL.revokeObjectURL(lightboxOriginalSrc); setLightboxOriginalSrc(''); setLightboxId(null); setLightboxUserId(null) }} - style={{ - position: 'absolute', inset: 0, zIndex: 100, - background: 'rgba(0,0,0,0.92)', display: 'flex', alignItems: 'center', justifyContent: 'center', - }}> - -
e.stopPropagation()} style={{ display: 'flex', gap: 16, alignItems: 'flex-start', justifyContent: 'center', padding: 20, width: '100%', height: '100%' }}> - + {lightboxId && lightboxUserId && (() => { + const closeLightbox = () => { + if (lightboxOriginalSrc) URL.revokeObjectURL(lightboxOriginalSrc) + setLightboxOriginalSrc('') + setLightboxId(null) + setLightboxUserId(null) + setShowMobileInfo(false) + } - {/* Info panel — liquid glass */} - {lightboxInfo && ( -
- {/* Date */} - {lightboxInfo.takenAt && ( + const exifContent = lightboxInfo ? ( + <> + {lightboxInfo.takenAt && ( +
+
Date
+
{new Date(lightboxInfo.takenAt).toLocaleDateString(undefined, { day: 'numeric', month: 'long', year: 'numeric' })}
+
{new Date(lightboxInfo.takenAt).toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' })}
+
+ )} + {(lightboxInfo.city || lightboxInfo.country) && ( +
+
+ Location +
+
+ {[lightboxInfo.city, lightboxInfo.state, lightboxInfo.country].filter(Boolean).join(', ')} +
+
+ )} + {lightboxInfo.camera && ( +
+
Camera
+
{lightboxInfo.camera}
+ {lightboxInfo.lens &&
{lightboxInfo.lens}
} +
+ )} + {(lightboxInfo.focalLength || lightboxInfo.aperture || lightboxInfo.iso) && ( +
+ {lightboxInfo.focalLength && (
-
Date
-
{new Date(lightboxInfo.takenAt).toLocaleDateString(undefined, { day: 'numeric', month: 'long', year: 'numeric' })}
-
{new Date(lightboxInfo.takenAt).toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' })}
+
Focal
+
{lightboxInfo.focalLength}
)} - - {/* Location */} - {(lightboxInfo.city || lightboxInfo.country) && ( + {lightboxInfo.aperture && (
-
- Location -
-
- {[lightboxInfo.city, lightboxInfo.state, lightboxInfo.country].filter(Boolean).join(', ')} -
+
Aperture
+
{lightboxInfo.aperture}
)} - - {/* Camera */} - {lightboxInfo.camera && ( + {lightboxInfo.shutter && (
-
Camera
-
{lightboxInfo.camera}
- {lightboxInfo.lens &&
{lightboxInfo.lens}
} +
Shutter
+
{lightboxInfo.shutter}
)} - - {/* Settings */} - {(lightboxInfo.focalLength || lightboxInfo.aperture || lightboxInfo.iso) && ( -
- {lightboxInfo.focalLength && ( -
-
Focal
-
{lightboxInfo.focalLength}
-
- )} - {lightboxInfo.aperture && ( -
-
Aperture
-
{lightboxInfo.aperture}
-
- )} - {lightboxInfo.shutter && ( -
-
Shutter
-
{lightboxInfo.shutter}
-
- )} - {lightboxInfo.iso && ( -
-
ISO
-
{lightboxInfo.iso}
-
- )} -
- )} - - {/* Resolution & File */} - {(lightboxInfo.width || lightboxInfo.fileName) && ( -
- {lightboxInfo.width && lightboxInfo.height && ( -
{lightboxInfo.width} × {lightboxInfo.height}
- )} - {lightboxInfo.fileSize && ( -
{(lightboxInfo.fileSize / 1024 / 1024).toFixed(1)} MB
- )} + {lightboxInfo.iso && ( +
+
ISO
+
{lightboxInfo.iso}
)}
)} + {(lightboxInfo.width || lightboxInfo.fileName) && ( +
+ {lightboxInfo.width && lightboxInfo.height && ( +
{lightboxInfo.width} × {lightboxInfo.height}
+ )} + {lightboxInfo.fileSize && ( +
{(lightboxInfo.fileSize / 1024 / 1024).toFixed(1)} MB
+ )} +
+ )} + + ) : null - {lightboxInfoLoading && ( -
-
+ return ( +
+ {/* Close button */} + + + {/* Mobile info toggle button */} + {isMobile && (lightboxInfo || lightboxInfoLoading) && ( + + )} + +
e.stopPropagation()} style={{ display: 'flex', gap: 16, alignItems: 'flex-start', justifyContent: 'center', padding: 20, width: '100%', height: '100%' }}> + + + {/* Desktop info panel — liquid glass */} + {!isMobile && lightboxInfo && ( +
+ {exifContent} +
+ )} + + {!isMobile && lightboxInfoLoading && ( +
+
+
+ )} +
+ + {/* Mobile bottom sheet */} + {isMobile && showMobileInfo && lightboxInfo && ( +
e.stopPropagation()} style={{ + position: 'absolute', bottom: 0, left: 0, right: 0, zIndex: 5, + maxHeight: '60vh', overflowY: 'auto', + borderRadius: '16px 16px 0 0', padding: 18, + background: 'rgba(0,0,0,0.85)', backdropFilter: 'blur(20px)', WebkitBackdropFilter: 'blur(20px)', + border: '1px solid rgba(255,255,255,0.12)', borderBottom: 'none', + color: 'white', display: 'flex', flexDirection: 'column', gap: 14, + }}> + {exifContent}
)}
-
- )} + ) + })()}
) }