feat: map view with GPS extraction fix
Adds a new Map sidebar entry that plots photos by their EXIF GPS coordinates on a clustered Leaflet map. While wiring this up, the metadata extractor was reading unprefixed GPS keys that never exist in `exiftool -G -j` output AND assumed coordinates were already floats — every photo silently lost its GPS. The new extract_gps helper handles Composite/EXIF group prefixes and parses DMS strings, and lat/lon are stored as first-class indexed columns so the map can query them without parsing exif_json on every request. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { X, Info } from 'lucide-react'
|
||||
import { usePhotoStore } from '../../store/photoStore'
|
||||
import { usePhotosQuery } from '../../hooks/usePhotosQuery'
|
||||
import { photos as photosApi } from '../../services/api'
|
||||
import type { Photo } from '../../types/photo'
|
||||
import { PreviewImage } from './PreviewImage'
|
||||
import { PreviewFilmstrip } from './PreviewFilmstrip'
|
||||
@@ -43,7 +45,24 @@ export function PreviewView() {
|
||||
? photos.findIndex((p) => p.id === activePhotoId)
|
||||
: 0
|
||||
const safeIndex = currentIndex < 0 ? 0 : currentIndex
|
||||
const currentPhoto: Photo | undefined = photos[safeIndex]
|
||||
|
||||
// Fallback fetch: when the preview is opened for a photo that isn't in
|
||||
// the timeline query result (e.g. clicked from the Map view, where the
|
||||
// active section's filter excludes it), look it up by id directly.
|
||||
// PhotoInfoPanel runs the same query under the same key, so they share
|
||||
// one cache entry — no extra request.
|
||||
const photoInListById = activePhotoId
|
||||
? photos.find((p) => p.id === activePhotoId)
|
||||
: undefined
|
||||
const { data: standalonePhoto } = useQuery<Photo>({
|
||||
queryKey: ['photo', activePhotoId],
|
||||
queryFn: () => photosApi.get(activePhotoId as string),
|
||||
enabled: !!activePhotoId && !photoInListById,
|
||||
staleTime: 60_000,
|
||||
})
|
||||
|
||||
const currentPhoto: Photo | undefined =
|
||||
photoInListById ?? photos[safeIndex] ?? standalonePhoto
|
||||
|
||||
// Keep the latest photos array + active id in a ref so the keyboard
|
||||
// handlers ALWAYS read the freshest state. Without this, react-hotkeys-
|
||||
|
||||
Reference in New Issue
Block a user