mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 21:46:49 +00:00
870bd6131f
* feat(journey): serve the GPX tracks behind a journey A journey has no trip of its own, but every entry records the trip it came from, and a trip's routed geometries live on its places. So the tracks are derivable without any new storage: GET /journeys/:id/tracks walks that link and returns the geometries, dropping elevation nothing on the map reads and skipping anything that is not a usable line. * feat(journey): draw those tracks on the journey map Both renderers, Leaflet and GL, plus the mobile map-timeline that shares them. Solid and in the track's own colour with a white casing, so it reads as a recorded route rather than as the dashed line that merely connects entries in time order, and stays legible on satellite tiles. * feat(journey): load the tracks alongside the journey Fetched separately from the journey itself: most journeys have no tracks, and a failed lookup should cost the map its lines rather than the whole page. * docs(journey): document the GPX tracks on the map * fix(journey): label tracks with the house map tooltip bindTooltip's default box ignores the appearance tokens, so it sat there in Leaflet grey next to markers already using .map-tooltip. Same class now, and an unnamed track gets no tooltip at all instead of an empty one.
76 lines
2.8 KiB
TypeScript
76 lines
2.8 KiB
TypeScript
import { Suspense, useImperativeHandle, useRef, type Ref } from 'react'
|
|
import { useSettingsStore } from '../../store/settingsStore'
|
|
import JourneyMap, { type JourneyMapHandle } from './JourneyMap'
|
|
import ErrorBoundary from '../shared/ErrorBoundary'
|
|
import type { JourneyMapGLHandle } from './JourneyMapGL'
|
|
|
|
import { JourneyMapGLMapbox, JourneyMapGLMaplibre } from '../Map/glLazy'
|
|
import type { JourneyTrack } from '@trek/shared'
|
|
|
|
// Unified handle — both providers expose the same three methods.
|
|
export type JourneyMapAutoHandle = JourneyMapHandle
|
|
|
|
interface MapEntry {
|
|
id: string
|
|
lat: number
|
|
lng: number
|
|
title?: string | null
|
|
location_name?: string | null
|
|
mood?: string | null
|
|
entry_date: string
|
|
dayColor?: string
|
|
dayLabel?: number
|
|
}
|
|
|
|
interface Props {
|
|
ref?: Ref<JourneyMapAutoHandle>
|
|
checkins: unknown[]
|
|
entries: MapEntry[]
|
|
trail?: { lat: number; lng: number }[]
|
|
tracks?: JourneyTrack[]
|
|
height?: number
|
|
dark?: boolean
|
|
activeMarkerId?: string | null
|
|
onMarkerClick?: (id: string, type?: string) => void
|
|
fullScreen?: boolean
|
|
paddingBottom?: number
|
|
}
|
|
|
|
function JourneyMapAuto({ ref, ...props }: Props) {
|
|
const provider = useSettingsStore(s => s.settings.map_provider)
|
|
const token = useSettingsStore(s => s.settings.mapbox_access_token)
|
|
const leafletRef = useRef<JourneyMapHandle>(null)
|
|
const glRef = useRef<JourneyMapGLHandle>(null)
|
|
|
|
// Fall back to Leaflet when the user selected Mapbox GL but hasn't
|
|
// supplied a token yet. MapLibre/OpenFreeMap is tokenless.
|
|
const useGL = provider === 'maplibre-gl' || (provider === 'mapbox-gl' && !!token)
|
|
const glProvider = provider === 'maplibre-gl' ? 'maplibre-gl' : 'mapbox-gl'
|
|
|
|
useImperativeHandle(ref, () => ({
|
|
highlightMarker: (id) => (useGL ? glRef.current : leafletRef.current)?.highlightMarker(id),
|
|
focusMarker: (id) => (useGL ? glRef.current : leafletRef.current)?.focusMarker(id),
|
|
invalidateSize: () => (useGL ? glRef.current : leafletRef.current)?.invalidateSize(),
|
|
}), [useGL])
|
|
|
|
// One chunk per engine — see MapViewAuto.
|
|
const JourneyMapGL = glProvider === 'maplibre-gl' ? JourneyMapGLMaplibre : JourneyMapGLMapbox
|
|
if (useGL) {
|
|
return (
|
|
// See MapViewAuto: the boundary has to sit outside the Suspense to catch a
|
|
// chunk that never arrives.
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
<ErrorBoundary boundaryId="journey-map:gl" resetKeys={[glProvider]} fallback={<JourneyMap ref={leafletRef} {...(props as any)} />}>
|
|
<Suspense fallback={null}>
|
|
{/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}
|
|
<JourneyMapGL ref={glRef} {...(props as any)} glProvider={glProvider} />
|
|
</Suspense>
|
|
</ErrorBoundary>
|
|
)
|
|
}
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
return <JourneyMap ref={leafletRef} {...(props as any)} />
|
|
}
|
|
|
|
export default JourneyMapAuto
|