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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
- 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>
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>
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>
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>
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>
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>
- 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>
- 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>
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>
- 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>
- 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>
- 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>
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>
- 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>
- 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>
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>
- 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>
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>
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>
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>
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>
- 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>
- 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>
- 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>