Initialize no-go areas client-side via URL params

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) <noreply@anthropic.com>
This commit is contained in:
Ullrich Schäfer 2026-04-03 12:47:40 +01:00
parent fdfacd2997
commit b6179fbdd2
No known key found for this signature in database
GPG key ID: A32FF691A0F752D9
5 changed files with 26 additions and 8 deletions

View file

@ -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}`,

View file

@ -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();

View file

@ -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<YjsState | null>(null);
const providerRef = useRef<WebsocketProvider | null>(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]);
}
}
});
}
});

View file

@ -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 },
);
});

View file

@ -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 (
<div className="flex h-full flex-col">
@ -61,6 +66,7 @@ export default function SessionPage({ loaderData }: Route.ComponentProps) {
callbackToken={loaderData.callbackToken ?? undefined}
returnUrl={returnUrl}
initialWaypoints={initialWaypoints}
initialNoGoAreas={initialNoGoAreas}
/>
</Suspense>
)}