trails/apps/planner/app/lib/use-days.ts
Ullrich Schäfer 3b9672e0ff
planner: give the Yjs document a typed schema seam
The routeData Y.Map's ~15 string keys (geojson, coordinates,
segmentBoundaries, road metadata, profile, colorMode, baseLayer,
overlays, poiCategories) were read and written raw at ~30 call sites,
each with its own JSON parsing and casts; parseJsonArray existed twice
and waypoint extraction four times. GPX assembly was duplicated between
SaveToJournalButton and ExportButton, so the saved plan and the
exported file could silently diverge.

- new lib/route-data.ts owns the routeData (+ noGoAreas) schema:
  typed read/write, JSON encoding internal, ColorMode moves here
  (re-exported from ColoredRoute for existing importers)
- new lib/gpx-export.ts owns GPX assembly: buildRouteGpx /
  buildPlanGpx / buildDayGpxFiles / hasDayBreaks; multi-day splitting
  becomes a pure, tested function
- waypoint-ymap.ts gains extractWaypoints / extractWaypointData; the
  four hand-rolled copies (use-routing, use-waypoint-manager,
  WaypointSidebar, use-days) now share it, and WaypointSidebar's
  moveWaypoint reuses the round-trip helpers instead of re-listing
  every waypoint field
- all hooks/components consume the seam; no raw routeData key strings
  remain outside route-data.ts

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 01:35:04 +02:00

53 lines
1.4 KiB
TypeScript

import { useEffect, useState } from "react";
import { computeDays, type DayStage } from "@trails-cool/gpx";
import type { TrackPoint } from "@trails-cool/gpx";
import type { YjsState } from "./use-yjs.ts";
import { extractWaypoints } from "./waypoint-ymap.ts";
import { getCoordinates } from "./route-data.ts";
/**
* Reactive hook that computes day stages from Yjs waypoints and route data.
* Returns an empty array for single-day routes (no overnight waypoints).
*/
export function useDays(yjs: YjsState | null): DayStage[] {
const [days, setDays] = useState<DayStage[]>([]);
useEffect(() => {
if (!yjs) return;
const recompute = () => {
const waypoints = extractWaypoints(yjs.waypoints);
// Check if any waypoint has isDayBreak
const hasBreaks = waypoints.some((w) => w.isDayBreak);
if (!hasBreaks) {
setDays([]);
return;
}
const coords = getCoordinates(yjs.routeData);
if (!coords) {
setDays([]);
return;
}
const trackPoints: TrackPoint[] = coords.map((c) => ({
lat: c[1],
lon: c[0],
ele: c[2],
}));
setDays(computeDays(waypoints, [trackPoints]));
};
yjs.waypoints.observeDeep(recompute);
yjs.routeData.observe(recompute);
recompute();
return () => {
yjs.waypoints.unobserveDeep(recompute);
yjs.routeData.unobserve(recompute);
};
}, [yjs]);
return days;
}