From b6179fbdd2835f365d34e6c89d6e66859f0afca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Fri, 3 Apr 2026 12:47:40 +0100 Subject: [PATCH] Initialize no-go areas client-side via URL params MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In dev mode, the sessions API and the Yjs WebSocket server are separate processes (Vite plugin vs React Router action), so server-side Yjs doc initialization doesn't reach the client. Now no-go areas flow the same way as waypoints: API response → URL params → client-side Yjs initialization. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../journal/app/routes/api.routes.$id.edit-in-planner.ts | 6 +++++- apps/planner/app/components/SessionView.tsx | 5 +++-- apps/planner/app/lib/use-yjs.ts | 8 ++++++++ apps/planner/app/routes/api.sessions.ts | 9 ++++----- apps/planner/app/routes/session.$id.tsx | 6 ++++++ 5 files changed, 26 insertions(+), 8 deletions(-) diff --git a/apps/journal/app/routes/api.routes.$id.edit-in-planner.ts b/apps/journal/app/routes/api.routes.$id.edit-in-planner.ts index 5bd38cf..61c809e 100644 --- a/apps/journal/app/routes/api.routes.$id.edit-in-planner.ts +++ b/apps/journal/app/routes/api.routes.$id.edit-in-planner.ts @@ -36,13 +36,17 @@ export async function action({ params, request }: Route.ActionArgs) { const session = (await sessionResp.json()) as { url: string; initialWaypoints?: Array<{ lat: number; lon: number; name?: string }>; + initialNoGoAreas?: Array<{ points: Array<{ lat: number; lon: number }> }>; }; - // Encode waypoints in URL params (small — just coordinates, not full GPX) + // Encode planning data in URL params const urlParams = new URLSearchParams({ returnUrl }); if (session.initialWaypoints?.length) { urlParams.set("waypoints", JSON.stringify(session.initialWaypoints)); } + if (session.initialNoGoAreas?.length) { + urlParams.set("noGoAreas", JSON.stringify(session.initialNoGoAreas)); + } return data({ url: `${plannerUrl}${session.url}?${urlParams}`, diff --git a/apps/planner/app/components/SessionView.tsx b/apps/planner/app/components/SessionView.tsx index 0c2ea56..16a0a19 100644 --- a/apps/planner/app/components/SessionView.tsx +++ b/apps/planner/app/components/SessionView.tsx @@ -179,12 +179,13 @@ interface SessionViewProps { callbackToken?: string; returnUrl?: string; initialWaypoints?: Array<{ lat: number; lon: number; name?: string }>; + initialNoGoAreas?: Array<{ points: Array<{ lat: number; lon: number }> }>; } -export function SessionView({ sessionId, callbackUrl, callbackToken, returnUrl, initialWaypoints }: SessionViewProps) { +export function SessionView({ sessionId, callbackUrl, callbackToken, returnUrl, initialWaypoints, initialNoGoAreas }: SessionViewProps) { const { t } = useTranslation("planner"); useEffect(() => { Sentry.setTag("session_id", sessionId); }, [sessionId]); - const yjs = useYjs(sessionId, initialWaypoints); + const yjs = useYjs(sessionId, initialWaypoints, initialNoGoAreas); const { computing, routeError, routeStats, requestRoute } = useRouting(yjs); const [highlightPosition, setHighlightPosition] = useState<[number, number] | null>(null); const { toasts, addToast } = useToasts(); diff --git a/apps/planner/app/lib/use-yjs.ts b/apps/planner/app/lib/use-yjs.ts index 862b72a..bec224d 100644 --- a/apps/planner/app/lib/use-yjs.ts +++ b/apps/planner/app/lib/use-yjs.ts @@ -42,6 +42,7 @@ export interface YjsState { export function useYjs( sessionId: string, initialWaypoints?: Array<{ lat: number; lon: number; name?: string }>, + initialNoGoAreas?: Array<{ points: Array<{ lat: number; lon: number }> }>, ): YjsState | null { const [state, setState] = useState(null); const providerRef = useRef(null); @@ -106,6 +107,13 @@ export function useYjs( if (wp.name) yMap.set("name", wp.name); waypoints.push([yMap]); } + if (initialNoGoAreas?.length && noGoAreas.length === 0) { + for (const area of initialNoGoAreas) { + const yMap = new Y.Map(); + yMap.set("points", area.points); + noGoAreas.push([yMap]); + } + } }); } }); diff --git a/apps/planner/app/routes/api.sessions.ts b/apps/planner/app/routes/api.sessions.ts index f458bfc..fd7df07 100644 --- a/apps/planner/app/routes/api.sessions.ts +++ b/apps/planner/app/routes/api.sessions.ts @@ -1,6 +1,6 @@ import { data } from "react-router"; import type { Route } from "./+types/api.sessions"; -import { createSession, listSessions, initializeSessionWithNoGoAreas } from "~/lib/sessions"; +import { createSession, listSessions } from "~/lib/sessions"; import { parseGpxAsync, extractWaypoints } from "@trails-cool/gpx"; import { withDb } from "@trails-cool/db"; @@ -20,21 +20,20 @@ export async function action({ request }: Route.ActionArgs) { const session = await createSession({ callbackUrl, callbackToken }); let initialWaypoints: Array<{ lat: number; lon: number; name?: string }> | undefined; + let initialNoGoAreas: Array<{ points: Array<{ lat: number; lon: number }> }> | undefined; if (gpx) { try { const gpxData = await parseGpxAsync(gpx); const wps = extractWaypoints(gpxData); if (wps.length > 0) initialWaypoints = wps; - if (gpxData.noGoAreas.length > 0) { - initializeSessionWithNoGoAreas(session.id, gpxData.noGoAreas); - } + if (gpxData.noGoAreas.length > 0) initialNoGoAreas = gpxData.noGoAreas; } catch { // Continue with empty session if GPX is invalid } } return data( - { sessionId: session.id, url: `/session/${session.id}`, initialWaypoints }, + { sessionId: session.id, url: `/session/${session.id}`, initialWaypoints, initialNoGoAreas }, { status: 201 }, ); }); diff --git a/apps/planner/app/routes/session.$id.tsx b/apps/planner/app/routes/session.$id.tsx index 2c630d7..33b8311 100644 --- a/apps/planner/app/routes/session.$id.tsx +++ b/apps/planner/app/routes/session.$id.tsx @@ -37,6 +37,11 @@ export default function SessionPage({ loaderData }: Route.ComponentProps) { if (waypointsParam) { try { initialWaypoints = JSON.parse(waypointsParam); } catch { /* ignore */ } } + const noGoParam = searchParams.get("noGoAreas"); + let initialNoGoAreas: Array<{ points: Array<{ lat: number; lon: number }> }> | undefined; + if (noGoParam) { + try { initialNoGoAreas = JSON.parse(noGoParam); } catch { /* ignore */ } + } return (
@@ -61,6 +66,7 @@ export default function SessionPage({ loaderData }: Route.ComponentProps) { callbackToken={loaderData.callbackToken ?? undefined} returnUrl={returnUrl} initialWaypoints={initialWaypoints} + initialNoGoAreas={initialNoGoAreas} /> )}