Commit Graph

80 Commits

Author SHA1 Message Date
Jeff Emmett c0ef77b6b8 Broaden WebAuthn error handling to show register form
Catch SecurityError and AbortError in addition to NotAllowedError
when login fails — all indicate the user needs to register rather
than showing a cryptic error message.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 01:46:26 +00:00
Jeff Emmett cdd04fe185 Fix EncryptID auth: add .well-known/webauthn + fix pingerName scope
1. WebAuthn Related Origins: rmaps.online was missing the
   .well-known/webauthn endpoint, so browsers couldn't use passkeys
   registered under RP ID "rspace.online" on rmaps.online. Added
   Next.js route handler returning { origins: ["https://rspace.online"] }.

2. Server: moved pingerName declaration before the WS message block
   (was inside the push block, causing ReferenceError on every ping).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 01:32:13 +00:00
Jeff Emmett f54f537483 Fix pingerName crash + auto-redirect returning users to last room
1. Server: pingerName was used before declaration in the WS message
   block (defined inside the push block). Moved declaration above both
   blocks. This caused all /push/request-location calls to crash with
   ReferenceError.

2. Landing page: always auto-redirect returning users (have saved user
   + last room) instead of only in standalone mode. Shows a loading
   spinner during redirect so the landing page doesn't flash.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 00:44:19 +00:00
Jeff Emmett 53dd95fcac PWA auto-opens last room + visible ping notifications
1. Installed PWA (standalone mode) auto-redirects to last visited room
   on launch instead of showing the landing page. Uses localStorage
   rmaps_last_room + display-mode: standalone media query.

2. Manual pings now show visible feedback in three ways:
   - In-app toast: green banner "Alice pinged you for your location!"
   - Browser notification: fires Notification API when permission granted
   - Vibration: unchanged [200, 100, 200, 100, 400] pattern

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 00:38:47 +00:00
Jeff Emmett a1132371f4 Thread caller name through ping notifications
Ping notifications now show who pinged: "<Name> pinged you for your
location!" instead of generic "Someone is looking for you". Caller name
flows through WS messages, silent push payloads, SW postMessage, URL
params (?pinger=), and visible push notifications.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 00:35:54 +00:00
Jeff Emmett c3f884d2c4 Ensure offline users vibrate and auto-respond to manual pings
Three fixes for offline ping gaps:

1. SW silent push fallback: when a manual ping arrives as a silent push
   but no app window is open, show a visible notification with vibration
   instead of failing silently.

2. SW notificationclick: when opening a fresh window (app was closed),
   append ?ping=manual to the URL so the app can detect it was pinged.

3. page.tsx: on mount, detect ?ping=manual param, clean it from the URL,
   and auto-fire GPS once the WebSocket connection is established.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 00:28:18 +00:00
Jeff Emmett 8fd4ed22f9 Add floating QR code button on map for easy room joining
Tap the QR icon (bottom-right on mobile, top-right on desktop) to show
an inline QR code overlay. Scan-to-join — tapping the QR dismisses it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 00:20:30 +00:00
Jeff Emmett 0bea3ba73b feat: persist offline users and push subscriptions to disk
Rooms and push subscriptions now survive server restarts via JSON files
on a Docker volume. Stale participant cleanup skips users who have
active push subscriptions — they remain in the room and can still be
pinged for location and notifications when offline.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 00:10:53 +00:00
Jeff Emmett a54ae04140 feat: manual ping vibrates device and force-shares GPS location
Manual "Ping All" now sends `manual: true` flag through WebSocket and
push channels. Receiving clients vibrate and respond with a one-shot
getCurrentPosition() regardless of sharing toggle. Auto-periodic 60s
pings stay silent and only respond if sharing is enabled.

Also fixes: SW cache invalidation (v2→v3), navigation requests now
network-first, sync server lastSeen uses ISO strings, Dockerfile
includes verify-token.js.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 00:06:05 +00:00
Jeff Emmett f9faea1851 fix: connect frontend to sync server for multiplayer
NEXT_PUBLIC_SYNC_URL was never set, so useRoom always fell into
local-only mode — users couldn't see other joiners. Added build arg
to bake wss://sync.rmaps.online into the client bundle.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 23:34:04 +00:00
Jeff Emmett 7aca62e9fa feat: add QR code to share modal for easy room sharing
Replace the "QR Coming Soon" placeholder with a real QR code
(qrcode.react) encoding the room URL. Scan to join.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 09:56:47 +00:00
Jeff Emmett 4faab77278 fix: auto-center map on user's location instead of Hamburg
Trigger GeolocateControl on map load so the map flies to the user's
position. Default viewport changed from Hamburg CCH zoom-15 to a
world view, so denied geolocation still looks reasonable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 09:47:13 +00:00
Jeff Emmett 186aeb72f9 feat: standardize ecosystem footer with all 16 r-suite apps
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 09:43:05 +00:00
Jeff Emmett 0f2bee21a9 fix: use parent context in Docker build for encryptid-sdk dependency
The file: link to ../encryptid-sdk requires the build context to be
the parent directory so Docker can access the SDK as a sibling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 16:08:48 -07:00
Jeff Emmett c8aab758eb chore: backlog TASK-14 SpaceRole bridge (Done)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:35:15 -07:00
Jeff Emmett 4b2e345652 chore: update lockfile after encryptid-sdk install
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 15:33:49 -07:00
Jeff Emmett 97e9922da2 feat: add SpaceRole bridge for cross-module membership sync
Anonymous-first role resolution (PARTICIPANT default for open rooms).
Queries EncryptID server for space-linked rooms with 5-minute cache.
Capability checks for add_markers, share_location, configure_map.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 14:32:26 -07:00
Jeff Emmett b26547bd83 docs: add MODULE_SPEC.md with permission model and capabilities
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 12:30:14 -07:00
Jeff Emmett 81fb2db865 chore: add rInbox to r* Ecosystem footer
Add rinbox.online link to the ecosystem footer navigation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 18:31:21 -07:00
Jeff Emmett 30f32e6da7 fix: deduplicate participants, visible callout push for offline users, fix notification timing
- Server-side participant dedup on join: remove ghost entries with same name but different ID
- Reduce stale participant threshold from 1hr to 15min to match client-side cleanup
- Refactor push subscriptions from Set to Map keyed by endpoint (prevents duplicate pushes)
- Store participantId with push subscriptions for identity-aware routing
- Exclude joining user from their own "Friend Joined" push notification
- Callout (ping) sends visible push to offline users ("X is looking for you!") instead of silent push
- Return last known locations in callout API response for immediate display
- Service worker: 10s cooldown on location request pushes to prevent burst on app reopen
- Service worker: suppress join/leave notifications when app window is focused
- Pass callerName from ParticipantList so offline callout shows who's looking

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 14:01:56 -07:00
Jeff Emmett 8e96d4eec0 feat: rewrite demo page with live rSpace data via useDemoSync
Replace static SVG map with real-time WebSocket connection to the
shared demo community. Alpine route map with interactive markers,
all changes sync across the r* ecosystem in real-time.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 09:39:13 -07:00
Jeff Emmett a4caa71621 feat: migrate auth to EncryptID SDK client
Replace duplicated WebAuthn ceremony code with SDK EncryptIDClient.
Add @encryptid/sdk dependency and cookie persistence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 09:35:58 -07:00
Jeff Emmett 96e46af9dc feat: overhaul landing page and add demo with SVG map
Overhaul landing page from login screen to proper hero + features.
Add interactive demo page showing CCC Camp 2026 event with SVG camp
map, animated friend markers, status system, and friend list panel.
Add ecosystem footer to both pages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 08:58:54 -07:00
Jeff Emmett 9a8ea19f89 Mark PWA offline support task as done
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 20:40:47 -07:00
Jeff Emmett 6814f156a0 Fix TypeScript build: enable downlevelIteration for Uint8Array spread
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 19:27:39 -07:00
Jeff Emmett e7738b5f9f Add emoji favicon (🗺️) for browser tab
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 19:13:38 -07:00
Jeff Emmett f174709086 feat: add EncryptID auth to sync server and gate room creation
Verify JWT tokens on WebSocket connections via query param. Check room
visibility before allowing access. Block writes from read-only connections.
Add room config endpoint. Require auth for creating new rooms.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 11:54:20 -07:00
Jeff Emmett d7fc2cc8db fix: ArrayBuffer type casts for WebAuthn in TS 5.x strict mode
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 07:43:06 -07:00
Jeff Emmett 88aebcd997 feat: add optional EncryptID passkey authentication
Add optional passkey identity (anonymous access remains default):
- Add Zustand auth store with EncryptID login/register/logout
- Add AuthButton component to home page
- Auto-fill name from EncryptID when authenticated
- Use DID as persistent participant ID in rooms when signed in
- Update useRoom hook to accept optional encryptIdDid

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 07:35:08 -07:00
Jeff Emmett 3f37d92aa0 feat: Simplify Google Maps import - accept ZIP files directly
- Users can now upload the ZIP file directly from Google Takeout
- No need to extract the ZIP first
- Simplified 3-step instructions with direct link to Takeout
- Added JSZip dependency for ZIP processing
- Auto-detects saved places JSON in various ZIP structures
- Shows loading spinner while processing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 17:57:52 +01:00
Jeff Emmett fb38a07e37 fix: Ensure header displays on mobile
- Add flex-shrink-0 to prevent header from being compressed
- Increase header z-index to z-30 to stay above map elements
- Add relative positioning to header

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 17:49:57 +01:00
Jeff Emmett aacc6eb03f feat: Add floating "See Friends" button when list is closed
Shows a prominent button at bottom-left (mobile) or top-left (desktop)
to reopen the friends list after closing it.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 17:38:47 +01:00
Jeff Emmett 4302f2d4f8 feat: Add Google Maps import feature
- Add googleMapsParser.ts utility for parsing Google Takeout GeoJSON
- Add ImportModal component with drag-and-drop file upload
- Three-step wizard: Upload → Preview → Success
- Preview list with checkboxes and select/deselect all
- Add "Import Places" button to ParticipantList footer
- Imported places become waypoints with type "poi"

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 16:33:53 +01:00
Jeff Emmett 9d8314096b feat: Add comprehensive PWA offline support
- Enhanced service worker with multi-strategy caching:
  - App shell precaching for instant loading
  - Map tiles cache-first with background refresh (max 500 tiles)
  - API requests network-first with cache fallback
  - Static assets stale-while-revalidate
- IndexedDB room state persistence for offline access
- Room state sync in useRoom hook:
  - Saves state to service worker on changes
  - Loads cached state on initial load for offline fallback
- Message handlers for SAVE_ROOM_STATE, GET_ROOM_STATE, CLEAR_CACHES

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 17:04:07 +01:00
Jeff Emmett cf8bd63298 docs: Update backlog tasks with session progress
- task-3 (Push notifications): Marked Done, added implementation notes
- task-4 (PWA offline): Marked In Progress, documented partial impl
- task-6 (c3nav indoor): Added note about removing indoor button
- task-12 (Stale participants): Added greyed-out locations feature

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 15:30:13 +01:00
Jeff Emmett c34a84672a feat: Bell icon for pings and individual user ping support
- Changed ping button icon from circular arrows to bell with brackets
- Added individual ping button on each participant row
- Server now supports targeting specific participant by ID
- Deduplication still applies (only pings one session per name)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 13:32:25 +01:00
Jeff Emmett ff45193ba2 feat: Grey out stale locations, remove indoor map, dedupe pings
1. Stale locations (>5 min old) are now greyed out on the map with
   reduced opacity and hover tooltip showing time since last seen
2. Removed indoor map button and related UI elements
3. Location request pings now deduplicate by participant name to
   avoid pinging the same user multiple times

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 13:06:52 +01:00
Jeff Emmett 007a7e877f fix: Add fallback to low-accuracy geolocation when GPS times out
- After 2 consecutive timeouts with high accuracy mode, automatically
  switch to low accuracy mode (WiFi/cell-based location)
- Increased maxAge from 5s to 30s to accept older cached positions
- Reduced timeout from 30s to 15s for faster fallback
- Low accuracy mode uses 60s maxAge and 60s timeout for reliability

This helps indoor users who can't get GPS signal.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 10:23:06 +01:00
Jeff Emmett 2284f94ee3 feat: Auto-load saved user and remember last visited room
- JoinForm now pre-populates name/emoji from localStorage and shows
  a "Quick Join as [name]" button for returning users
- Home page shows "Rejoin /[room]" button when user has visited before
- Room slug saved to localStorage on every visit for quick rejoin

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 21:19:03 +01:00
Jeff Emmett 47dea7c9c3 fix: Deduplicate participants by name across map and list
Added useMemo-based deduplication that keeps the most recently seen
participant for each name, preventing duplicate markers on the map
and duplicate entries in the friends list.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 20:21:52 +01:00
Jeff Emmett 796fd2c727 feat: Direct room URL navigation + fix c3nav iframe blocking
- Add inline JoinForm when navigating directly to room URL
- No longer redirects to home page for new users
- Replace c3nav iframe with fallback UI (c3nav blocks iframe embedding)
- Add 'Open c3nav' button to open indoor map in new tab
- Still shows friend markers with indoor locations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 15:48:30 +01:00
Jeff Emmett 908767be15 feat: Show navigation panel when clicking user from list
When clicking a user from the participant list:
1. Map zooms to their location
2. Navigation panel appears with "Navigate here" option
3. Clicking "Navigate here" calculates route to that user

The selectedParticipant state is now lifted to the page level and
passed to DualMapView so both components stay in sync.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 03:03:16 +01:00
Jeff Emmett 40cfea26a2 feat: Zoom to location instead of opening Google Maps
Replace all Google Maps navigation links with in-app zoom functionality.
When clicking on a participant or waypoint to navigate, the map now
smoothly flies to their location at zoom level 17 instead of opening
an external Google Maps link.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 02:53:16 +01:00
Jeff Emmett 1dddf16c6a fix: Navigation now uses actual current location
Pass current location from useLocationSharing hook to navigateTo
instead of using stale data from the Zustand store. This fixes the
"Enable location sharing to get directions" error when already sharing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 02:46:07 +01:00
Jeff Emmett b8adffc4ad fix: Request notification permission on room join
Request push notification permission automatically when joining a room,
similar to how location permission is requested.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 02:37:39 +01:00
Jeff Emmett 4f110a7604 feat: Add push notifications for background friend pings
- Auto-subscribe to push notifications when location sharing starts
- Use C3NavEmbed (iframe) for faster indoor map loading
- Set event to 39c3 for current congress
- Increase c3nav API cache to 1 hour with stale-while-revalidate
- Add strong vibration pattern and sound for notifications

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 02:33:08 +01:00
Jeff Emmett 7410246a01 feat: Fix navigate button and add PWA install prompt
- Navigate button now opens Google Maps directions to friend's location
- Added PWA install banner for non-installed users
- iOS users get manual instructions for Add to Home Screen
- Banner can be dismissed and stays dismissed for session

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 01:54:25 +01:00
Jeff Emmett d6a20f9500 feat: Add WebSocket-based location refresh for online friends
- Server sends request_location via WebSocket to connected clients
- Falls back to silent push for offline/background clients
- Client responds with current GPS location when requested
- Refresh button now works for online friends (no push subscription needed)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 01:51:09 +01:00
Jeff Emmett aa501b1778 fix: Add Background Sync API type declarations
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 01:43:47 +01:00
Jeff Emmett 9c618cffb7 fix: Cast applicationServerKey to ArrayBuffer for strict TypeScript
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 01:41:36 +01:00