trails/openspec/changes/route-sharing/tasks.md
Ullrich Schäfer 0a330e4466
Break up route-features into focused specs, add new changes
Archive the monolithic route-features spec and replace with 9 focused
OpenSpec changes: multi-day-routes, waypoint-notes (with POI snapping),
undo-redo, local-dev-stack, route-sharing, route-discovery,
activity-photos, osm-overlays, plus the existing changelog and
komoot-import.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 09:53:41 +02:00

3.7 KiB

1. Schema

  • 1.1 Add visibility_enum PostgreSQL enum (private, public) to packages/db/src/schema/journal.ts using journalSchema.enum()
  • 1.2 Add visibility column to journal.routes table, type visibility_enum, default private
  • 1.3 Add visibility column to journal.activities table, type visibility_enum, default private
  • 1.4 Create journal.route_shares table with columns: id (text PK), routeId (FK to routes, cascade delete), userId (FK to users, cascade delete), permission (enum: view/edit), createdAt. Add unique constraint on (routeId, userId)
  • 1.5 Add forkedFromId nullable text column to journal.routes, FK to routes.id with onDelete: "set null"
  • 1.6 Add contributors jsonb text array column to journal.route_versions
  • 1.7 Run pnpm db:push and verify schema locally

2. Permissions Logic

  • 2.1 Create apps/journal/app/lib/permissions.server.ts with canView(routeId, userId) and canEdit(routeId, userId) — owner always has full access, public routes viewable by anyone, shared routes respect permission level
  • 2.2 Update route detail loader (routes.$id.tsx) to check canView — return 404 for unauthorized. Pass canEdit result to the component for conditional UI (edit button, share button)
  • 2.3 Update route list loader (routes._index.tsx) to include routes shared with the current user alongside owned routes

3. Sharing UI

  • 3.1 Add visibility toggle to route detail page (owner only): form with intent=toggleVisibility, shows current state and toggle button
  • 3.2 Create user search API route (api.users.search.ts): accepts q query param, returns matching users by username/display name (max 10, excludes requesting user), debounce-friendly
  • 3.3 Create share dialog component and integrate on route detail page (owner only): user search input, permission dropdown (view/edit), current shares list with remove button. Uses form actions for add/remove share

4. Forking

  • 4.1 Create fork API route (api.routes.$id.fork.ts): verify user is logged in, source route is public, copy route + latest GPX + metadata to user's collection with forkedFromId set, create v1 version, redirect to new route
  • 4.2 Add "Fork" button on route detail page: visible when route is public, viewer is logged in, and viewer is not the owner
  • 4.3 Show "Forked from [route name]" link on route detail page when forkedFromId is set and original route is viewable

5. Contributor Tracking

  • 5.1 Update Planner callback handler (api.routes.$id.callback.ts): extract contributor identity from JWT, pass to version creation
  • 5.2 Update updateRoute in routes.server.ts to accept and store contributors array on new route version rows
  • 5.3 Display contributors on route detail page: per-version contributor names in version history, combined unique contributors in route header

6. Privacy & i18n

  • 6.1 Update privacy manifest page (privacy.tsx): document route visibility settings, sharing behavior, forking, and contributor tracking
  • 6.2 Add i18n keys (en + de) for: visibility labels (private/public), share dialog strings, fork button/label, contributor display, "forked from" text

7. Testing

  • 7.1 Unit tests for permissions.server.ts: owner access, public access, shared view/edit access, no access, null user on public route
  • 7.2 Unit tests for fork logic: successful fork of public route, reject fork of private route, forkedFromId set correctly, fork starts as private
  • 7.3 E2E test: create route, toggle visibility to public, verify accessible when logged out
  • 7.4 E2E test: share route with another user at view level, verify they can see it but not edit; share at edit level, verify edit access