Fix waypoint loading in Edit in Planner flow
Three issues fixed: 1. GPX parser used browser DOMParser which doesn't exist in Node/Vite SSR. Added async parseGpxAsync() using linkedom for server-side parsing. 2. Server-side session initialization stored waypoints in a Yjs doc instance separate from the Vite WebSocket plugin's doc store. Moved waypoint initialization to client-side: API returns parsed waypoints, client adds them to Yjs after sync. 3. GPX was encoded in URL params causing HTTP 431. Now the Journal creates a Planner session via API (POST body), and only passes compact waypoint coordinates in URL params. Verified: Journal route → Edit in Planner → 4 waypoints loaded, route computed (79.3km), elevation profile, Save to Journal ready. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2e2dbe05c1
commit
14b179bb0c
9 changed files with 201 additions and 13 deletions
|
|
@ -21,10 +21,11 @@ interface SessionViewProps {
|
|||
callbackUrl?: string;
|
||||
callbackToken?: string;
|
||||
returnUrl?: string;
|
||||
initialWaypoints?: Array<{ lat: number; lon: number; name?: string }>;
|
||||
}
|
||||
|
||||
export function SessionView({ sessionId, callbackUrl, callbackToken, returnUrl }: SessionViewProps) {
|
||||
const yjs = useYjs(sessionId);
|
||||
export function SessionView({ sessionId, callbackUrl, callbackToken, returnUrl, initialWaypoints }: SessionViewProps) {
|
||||
const yjs = useYjs(sessionId, initialWaypoints);
|
||||
const { isHost, computing, routeStats, requestRoute } = useRouting(yjs);
|
||||
const [highlightPosition, setHighlightPosition] = useState<[number, number] | null>(null);
|
||||
|
||||
|
|
|
|||
|
|
@ -36,10 +36,14 @@ export interface YjsState {
|
|||
connected: boolean;
|
||||
}
|
||||
|
||||
export function useYjs(sessionId: string): YjsState | null {
|
||||
export function useYjs(
|
||||
sessionId: string,
|
||||
initialWaypoints?: Array<{ lat: number; lon: number; name?: string }>,
|
||||
): YjsState | null {
|
||||
const [state, setState] = useState<YjsState | null>(null);
|
||||
const providerRef = useRef<WebsocketProvider | null>(null);
|
||||
const docRef = useRef<Y.Doc | null>(null);
|
||||
const initializedWaypoints = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
const doc = new Y.Doc();
|
||||
|
|
@ -52,10 +56,28 @@ export function useYjs(sessionId: string): YjsState | null {
|
|||
const waypoints = doc.getArray<Y.Map<unknown>>("waypoints");
|
||||
const routeData = doc.getMap("routeData");
|
||||
|
||||
// Use persistent identity
|
||||
const { color, name } = getOrCreateUserIdentity();
|
||||
provider.awareness.setLocalStateField("user", { color, name });
|
||||
|
||||
// Initialize waypoints once after first sync
|
||||
if (initialWaypoints?.length && !initializedWaypoints.current) {
|
||||
(provider as unknown as { on(event: string, cb: () => void): void }).on("synced", () => {
|
||||
// Only add if the doc is empty (avoid duplicating on reconnect)
|
||||
if (waypoints.length === 0 && !initializedWaypoints.current) {
|
||||
initializedWaypoints.current = true;
|
||||
doc.transact(() => {
|
||||
for (const wp of initialWaypoints) {
|
||||
const yMap = new Y.Map();
|
||||
yMap.set("lat", wp.lat);
|
||||
yMap.set("lon", wp.lon);
|
||||
if (wp.name) yMap.set("name", wp.name);
|
||||
waypoints.push([yMap]);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const updateState = (connected: boolean) => {
|
||||
setState({
|
||||
doc,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { data } from "react-router";
|
||||
import type { Route } from "./+types/api.sessions";
|
||||
import { createSession, initializeSessionWithWaypoints, listSessions } from "~/lib/sessions";
|
||||
import { parseGpx } from "@trails-cool/gpx";
|
||||
import { createSession, listSessions } from "~/lib/sessions";
|
||||
import { parseGpxAsync } from "@trails-cool/gpx";
|
||||
|
||||
export async function action({ request }: Route.ActionArgs) {
|
||||
if (request.method !== "POST") {
|
||||
|
|
@ -17,10 +17,11 @@ export async function action({ request }: Route.ActionArgs) {
|
|||
|
||||
const session = await createSession({ callbackUrl, callbackToken });
|
||||
|
||||
let initialWaypoints: Array<{ lat: number; lon: number; name?: string }> | undefined;
|
||||
if (gpx) {
|
||||
try {
|
||||
const gpxData = parseGpx(gpx);
|
||||
initializeSessionWithWaypoints(session.id, gpxData.waypoints);
|
||||
const gpxData = await parseGpxAsync(gpx);
|
||||
initialWaypoints = gpxData.waypoints;
|
||||
} catch (_e) {
|
||||
// Continue with empty session if GPX is invalid
|
||||
}
|
||||
|
|
@ -30,6 +31,7 @@ export async function action({ request }: Route.ActionArgs) {
|
|||
{
|
||||
sessionId: session.id,
|
||||
url: `/session/${session.id}`,
|
||||
initialWaypoints,
|
||||
},
|
||||
{ status: 201 },
|
||||
);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,11 @@ export default function SessionPage({ loaderData }: Route.ComponentProps) {
|
|||
const { id } = useParams();
|
||||
const [searchParams] = useSearchParams();
|
||||
const returnUrl = searchParams.get("returnUrl") ?? undefined;
|
||||
const waypointsParam = searchParams.get("waypoints");
|
||||
let initialWaypoints: Array<{ lat: number; lon: number; name?: string }> | undefined;
|
||||
if (waypointsParam) {
|
||||
try { initialWaypoints = JSON.parse(waypointsParam); } catch { /* ignore */ }
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
|
|
@ -52,6 +57,7 @@ export default function SessionPage({ loaderData }: Route.ComponentProps) {
|
|||
callbackUrl={loaderData.callbackUrl ?? undefined}
|
||||
callbackToken={loaderData.callbackToken ?? undefined}
|
||||
returnUrl={returnUrl}
|
||||
initialWaypoints={initialWaypoints}
|
||||
/>
|
||||
</Suspense>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue