feat: Persist location sharing across browser sessions

- Remember location sharing preference per room
- Auto-start sharing when returning to a room
- Restore last known location immediately on page load (if < 1 hour old)
- Location updates are saved to localStorage for faster reload

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2025-12-28 23:47:44 +01:00
parent 912104f740
commit 6294e1de41
1 changed files with 66 additions and 7 deletions

View File

@ -31,8 +31,9 @@ export default function RoomPage() {
const [showMeetingPoint, setShowMeetingPoint] = useState(false); const [showMeetingPoint, setShowMeetingPoint] = useState(false);
const [currentUser, setCurrentUser] = useState<{ name: string; emoji: string } | null>(null); const [currentUser, setCurrentUser] = useState<{ name: string; emoji: string } | null>(null);
const [selectedParticipant, setSelectedParticipant] = useState<Participant | null>(null); const [selectedParticipant, setSelectedParticipant] = useState<Participant | null>(null);
const [shouldAutoStartSharing, setShouldAutoStartSharing] = useState(false);
// Load user from localStorage // Load user and sharing preference from localStorage
useEffect(() => { useEffect(() => {
const stored = localStorage.getItem('rmaps_user'); const stored = localStorage.getItem('rmaps_user');
if (stored) { if (stored) {
@ -40,8 +41,15 @@ export default function RoomPage() {
} else { } else {
// Redirect to home if no user info // Redirect to home if no user info
router.push('/'); router.push('/');
return;
} }
}, [router]);
// Check if user had location sharing enabled for this room
const sharingPref = localStorage.getItem(`rmaps_sharing_${slug}`);
if (sharingPref === 'true') {
setShouldAutoStartSharing(true);
}
}, [router, slug]);
// Room hook (only initialize when we have user info) // Room hook (only initialize when we have user info)
const { const {
@ -77,13 +85,24 @@ export default function RoomPage() {
updateLocationRef.current = updateLocation; updateLocationRef.current = updateLocation;
}, [updateLocation]); }, [updateLocation]);
// Stable callback that always uses latest refs // Stable callback that always uses latest refs - also persists location
const handleLocationUpdate = useCallback((location: ParticipantLocation) => { const handleLocationUpdate = useCallback((location: ParticipantLocation) => {
console.log('Location update received:', location.latitude, location.longitude, 'connected:', isConnectedRef.current); console.log('Location update received:', location.latitude, location.longitude, 'connected:', isConnectedRef.current);
if (isConnectedRef.current) { if (isConnectedRef.current) {
updateLocationRef.current(location); updateLocationRef.current(location);
// Persist last known location for faster reload
try {
localStorage.setItem(`rmaps_last_location_${slug}`, JSON.stringify({
latitude: location.latitude,
longitude: location.longitude,
accuracy: location.accuracy,
timestamp: location.timestamp.toISOString(),
}));
} catch {
// Ignore storage errors
}
} }
}, []); }, [slug]);
// Location sharing hook // Location sharing hook
const { const {
@ -97,18 +116,58 @@ export default function RoomPage() {
highAccuracy: true, highAccuracy: true,
}); });
// Location sharing is opt-in - user must click to start // Restore last known location immediately when connected
// Handler for toggling location sharing const hasRestoredLocationRef = useRef(false);
useEffect(() => {
if (isConnected && !hasRestoredLocationRef.current) {
hasRestoredLocationRef.current = true;
try {
const savedLocation = localStorage.getItem(`rmaps_last_location_${slug}`);
if (savedLocation) {
const loc = JSON.parse(savedLocation);
// Only restore if less than 1 hour old
const age = Date.now() - new Date(loc.timestamp).getTime();
if (age < 60 * 60 * 1000) {
console.log('Restoring last known location:', loc.latitude, loc.longitude);
updateLocation({
latitude: loc.latitude,
longitude: loc.longitude,
accuracy: loc.accuracy || 100,
timestamp: new Date(loc.timestamp),
source: 'gps',
});
}
}
} catch {
// Ignore restore errors
}
}
}, [isConnected, slug, updateLocation]);
// Auto-start location sharing if user had it enabled before
useEffect(() => {
if (shouldAutoStartSharing && isConnected && !isSharing) {
console.log('Auto-starting location sharing from saved preference');
startSharing();
setShouldAutoStartSharing(false); // Only auto-start once
}
}, [shouldAutoStartSharing, isConnected, isSharing, startSharing]);
// Handler for toggling location sharing - persists preference
const handleToggleSharing = useCallback(() => { const handleToggleSharing = useCallback(() => {
if (isSharing) { if (isSharing) {
console.log('Stopping location sharing and clearing location'); console.log('Stopping location sharing and clearing location');
stopSharing(); stopSharing();
clearLocation(); clearLocation();
// Save preference
localStorage.setItem(`rmaps_sharing_${slug}`, 'false');
} else { } else {
console.log('Starting location sharing'); console.log('Starting location sharing');
startSharing(); startSharing();
// Save preference
localStorage.setItem(`rmaps_sharing_${slug}`, 'true');
} }
}, [isSharing, startSharing, stopSharing, clearLocation]); }, [isSharing, startSharing, stopSharing, clearLocation, slug]);
// Track if we've centered on user's location yet // Track if we've centered on user's location yet
const hasCenteredRef = useRef(false); const hasCenteredRef = useRef(false);