feat(library): close the mobile preview on the frame the reader swiped to, selected in the thumb grid

This commit is contained in:
2026-10-13 12:54:45 +07:00
parent 9a40449f05
commit 9e2e6a6727
3 changed files with 268 additions and 3 deletions
@@ -332,6 +332,12 @@
contain-intrinsic-size: 110px 110px;
}
/* The frame the reader stopped at in the preview, lit up in the grid. */
.mob-lib-thumb-cell.selected {
outline: 2px solid #f59e0b;
outline-offset: -2px;
}
.mob-lib-thumb-cell img {
width: 100%;
height: 100%;
+26 -3
View File
@@ -115,6 +115,9 @@ export function MobileLibrary({
const [tab, setTab] = useState<MobileTab>('photos');
const [selectedAlbum, setSelectedAlbum] = useState<string | null>(null);
const [selectedPhoto, setSelectedPhoto] = useState<LibraryPhoto | null>(null);
// The frame the reader stopped at: it outlives the preview, so closing the
// picture lights that frame up in the thumb grid.
const [activeId, setActiveId] = useState<string | null>(null);
const [shot, setShot] = useState<{ line: string; lat: number | null; lng: number | null; place: string | null } | null>(null);
// Read photo EXIF specifications and GPS (similar to Desktop UI)
@@ -573,6 +576,19 @@ export function MobileLibrary({
const next = i < 0 ? undefined : searchResults[i + d];
if (next) setSelectedPhoto(next);
};
// Every frame the preview shows — opened or swiped to — is the one the reader
// stopped at.
useEffect(() => {
if (selectedPhoto) setActiveId(selectedPhoto.id);
}, [selectedPhoto]);
// Closing the frame in the one-at-a-time list hands the reader to the thumb
// grid, which opens on the frame they stopped at.
const closePhoto = () => {
setSelectedPhoto(null);
if (viewMode === 'single') setViewMode('grid');
};
// Where the finger went down: only the distance it travelled matters, and only
// once it has lifted.
const swipeFrom = useRef<{ x: number; y: number } | null>(null);
@@ -631,6 +647,13 @@ export function MobileLibrary({
sortOrder,
]);
// Landing on that frame: the grid brings it under the eye rather than leaving
// the reader to hunt for it.
useEffect(() => {
if (viewMode !== 'grid' || !activeId) return;
scrollContainerRef.current?.querySelector('.mob-lib-thumb-cell.selected')?.scrollIntoView({ block: 'center' });
}, [viewMode, activeId]);
// Available years from photos & server timeline buckets
const availableYears = useMemo(() => {
const years = new Set<number>();
@@ -1367,7 +1390,7 @@ export function MobileLibrary({
return (
<div
key={photo.id}
className="mob-lib-thumb-cell"
className={`mob-lib-thumb-cell ${activeId === photo.id ? 'selected' : ''}`}
onClick={() => setSelectedPhoto(photo)}
>
<img
@@ -1767,7 +1790,7 @@ export function MobileLibrary({
{searchResults.map((photo) => (
<div
key={photo.id}
className="mob-lib-thumb-cell"
className={`mob-lib-thumb-cell ${activeId === photo.id ? 'selected' : ''}`}
onClick={() => setSelectedPhoto(photo)}
>
<img src={remoteUrl('thumbnail', photo)} alt={photo.name} loading="lazy" />
@@ -2217,7 +2240,7 @@ export function MobileLibrary({
<button
type="button"
className="mob-lib-status-btn"
onClick={() => setSelectedPhoto(null)}
onClick={closePhoto}
style={{ fontSize: '18px', padding: '6px' }}
aria-label="Close"
>