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>
This commit is contained in:
Jeff Emmett 2026-02-19 00:28:18 +00:00
parent 8fd4ed22f9
commit c3f884d2c4
2 changed files with 46 additions and 2 deletions

View File

@ -346,8 +346,24 @@ self.addEventListener('push', (event) => {
const isManual = !!(data.data?.manual);
event.waitUntil(
requestLocationFromClient(isManual).then((sent) => {
requestLocationFromClient(isManual).then(async (sent) => {
console.log('[SW] Push location request sent:', sent, 'manual:', isManual);
// If no app window is open and this is a manual ping, show a visible notification
// so the user can tap to open the app and auto-respond with location
if (!sent && isManual) {
await self.registration.showNotification('📍 Someone is looking for you!', {
body: 'Tap to share your location',
icon: '/icon-192.png',
badge: '/icon-192.png',
tag: `callout-${data.data?.roomSlug || 'unknown'}`,
data: { type: 'callout', roomSlug: data.data?.roomSlug, manual: true },
vibrate: [200, 100, 200, 100, 400],
requireInteraction: true,
actions: [{ action: 'view', title: 'Open Map' }],
renotify: true,
silent: false,
});
}
})
);
return;
@ -418,7 +434,9 @@ self.addEventListener('notificationclick', (event) => {
}
}
if (clients.openWindow) {
return clients.openWindow(targetUrl);
// If manual ping, append ?ping=manual so the app auto-responds with GPS on load
const openUrl = data.manual ? `${targetUrl}?ping=manual` : targetUrl;
return clients.openWindow(openUrl);
}
})
);

View File

@ -47,6 +47,23 @@ export default function RoomPage() {
const [shouldAutoStartSharing, setShouldAutoStartSharing] = useState(false);
const [zoomToLocation, setZoomToLocation] = useState<{ latitude: number; longitude: number } | null>(null);
const [needsJoin, setNeedsJoin] = useState(false);
const [pendingManualPing, setPendingManualPing] = useState(false);
// Check if opened from a manual ping notification (e.g. ?ping=manual)
useEffect(() => {
if (typeof window !== 'undefined') {
const params = new URLSearchParams(window.location.search);
if (params.get('ping') === 'manual') {
// Clean the param from URL
params.delete('ping');
const newUrl = params.toString()
? `${window.location.pathname}?${params.toString()}`
: window.location.pathname;
window.history.replaceState({}, '', newUrl);
setPendingManualPing(true);
}
}
}, []);
// Load user and sharing preference from localStorage
useEffect(() => {
@ -207,6 +224,15 @@ export default function RoomPage() {
}
}, [isConnected, isPushSubscribed, subscribePush, syncUrl]);
// Execute pending manual ping once connected (from notification tap while app was closed)
useEffect(() => {
if (pendingManualPing && isConnected) {
console.log('Executing pending manual ping from notification tap');
setPendingManualPing(false);
handleManualPing();
}
}, [pendingManualPing, isConnected, handleManualPing]);
// Restore last known location immediately when connected
const hasRestoredLocationRef = useRef(false);
useEffect(() => {