mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
feat(roadtrip): show every change to a road trip live to everyone on it
Two people shaping one drive were each editing their own copy: a via dragged on one screen reached the other only on the next reload, and the route it changed looked settled until then. Every write in the domain now announces itself over the websocket the rest of the trip already uses — the five REST routes and the matching MCP tools alike, so an assistant moving a stop is as visible as a person doing it. Two new trip-scoped events carry it: roadtripVia:changed with the day's whole via list, and roadtripTrack:changed. The whole list rather than a delta, because a via is identified by its position in a chain and a delta protocol would need an ordering guarantee to be worth anything. The originating socket is excluded through the X-Socket-Id header the other domains already forward, so nobody watches their own change arrive twice.
This commit is contained in:
@@ -77,7 +77,9 @@ export class RoadtripMcp {
|
||||
if (!this.db.canAccessTrip(tripId, ctx.userId)) return noAccess();
|
||||
if (!this.guards.hasTripPermission('day_edit', tripId, ctx.userId)) return permissionDenied();
|
||||
if (!this.roadtrip.dayExists(dayId, tripId)) return noAccess();
|
||||
return ok({ via: this.roadtrip.create(dayId, { after_order_index, lat, lng }) });
|
||||
const via = this.roadtrip.create(dayId, { after_order_index, lat, lng });
|
||||
this.announce(tripId, dayId);
|
||||
return ok({ via });
|
||||
}
|
||||
|
||||
@Tool({
|
||||
@@ -121,7 +123,13 @@ export class RoadtripMcp {
|
||||
// a place id from another trip, or a place that is not a track at all, become this
|
||||
// day's label.
|
||||
if (track && !this.roadtrip.trackExists(track.place_id, tripId)) return noAccess();
|
||||
return ok({ vias: this.roadtrip.createMany(dayId, { vias, replace_legs, track }) });
|
||||
const made = this.roadtrip.createMany(dayId, { vias, replace_legs, track });
|
||||
this.announce(tripId, dayId);
|
||||
this.roadtrip.broadcast(String(tripId), 'roadtripTrack:changed', {
|
||||
dayId,
|
||||
track: this.roadtrip.tracksForTrip(String(tripId)).find(t => String(t.day_id) === String(dayId)) ?? null,
|
||||
}, undefined);
|
||||
return ok({ vias: made });
|
||||
}
|
||||
|
||||
@Tool({
|
||||
@@ -149,7 +157,9 @@ export class RoadtripMcp {
|
||||
if (!this.db.canAccessTrip(tripId, ctx.userId)) return noAccess();
|
||||
if (!this.guards.hasTripPermission('day_edit', tripId, ctx.userId)) return permissionDenied();
|
||||
if (!this.roadtrip.dayExists(dayId, tripId)) return noAccess();
|
||||
return ok({ vias: this.roadtrip.reanchor(dayId, { vias, remove }) });
|
||||
const next = this.roadtrip.reanchor(dayId, { vias, remove });
|
||||
this.announce(tripId, dayId);
|
||||
return ok({ vias: next });
|
||||
}
|
||||
|
||||
@Tool({
|
||||
@@ -170,6 +180,22 @@ export class RoadtripMcp {
|
||||
if (!this.guards.hasTripPermission('day_edit', tripId, ctx.userId)) return permissionDenied();
|
||||
if (!this.roadtrip.dayExists(dayId, tripId)) return noAccess();
|
||||
if (!this.roadtrip.remove(viaId, dayId)) return noAccess();
|
||||
this.announce(tripId, dayId);
|
||||
return ok({ success: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Says what this day's drive is routed through now.
|
||||
*
|
||||
* The same announcement the REST routes make, because it is the same change: a via laid
|
||||
* by an assistant moves the line on everybody's map exactly as one dragged by hand.
|
||||
* No originating socket to exclude here — a tool call has no socket of its own, so the
|
||||
* client that asked for it hears about it like everyone else.
|
||||
*/
|
||||
private announce(tripId: number, dayId: number): void {
|
||||
this.roadtrip.broadcast(String(tripId), 'roadtripVia:changed', {
|
||||
dayId,
|
||||
vias: this.roadtrip.listForDay(String(dayId)),
|
||||
}, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user