diff --git a/src/routes/(game)/game/play/[sessionCode]/+page.svelte b/src/routes/(game)/game/play/[sessionCode]/+page.svelte index 706e143..5f56402 100644 --- a/src/routes/(game)/game/play/[sessionCode]/+page.svelte +++ b/src/routes/(game)/game/play/[sessionCode]/+page.svelte @@ -33,11 +33,28 @@ let userLat = $state(null); let userLon = $state(null); let heading = $state(null); - let watchId: number | null = null; + let watchId = $state(null); let locationError = $state(null); let locationPermission = $state<'prompt' | 'granted' | 'denied' | 'checking'>('prompt'); let distance = $state(null); let arrowRotation = $state(0); + type DebugLogEntry = { + ts: string; + event: string; + details?: Record; + }; + let debugLogs = $state([]); + let lastOrientationEvent = $state(null); + + function addDebug(event: string, details?: Record) { + const entry: DebugLogEntry = { + ts: new Date().toISOString(), + event, + details + }; + debugLogs = [entry, ...debugLogs].slice(0, 80); + console.log('[location-debug]', event, details ?? {}); + } // Calculate distance between two coordinates in meters (Haversine formula) function calculateDistance(lat1: number, lon1: number, lat2: number, lon2: number): number { @@ -95,17 +112,31 @@ // If we have device heading, rotate relative to it; otherwise just use absolute bearing arrowRotation = heading !== null ? bearing - heading : bearing; + addDebug('computed_distance_bearing', { + distance, + bearing, + heading, + arrowRotation + }); } else { distance = null; arrowRotation = 0; + addDebug('reset_distance_rotation', { + stepType: currentStep?.type ?? null, + isCurrentActiveStep, + hasUserCoords: userLat !== null && userLon !== null, + hasStepCoords: currentStep?.latitude != null && currentStep?.longitude != null + }); } }); // Check and request location permission async function checkLocationPermission() { + addDebug('check_location_permission_start'); if (!('geolocation' in navigator)) { locationError = 'Geolocation is not supported by your device'; locationPermission = 'denied'; + addDebug('check_location_permission_no_geolocation'); return false; } @@ -114,25 +145,37 @@ try { const result = await navigator.permissions.query({ name: 'geolocation' }); locationPermission = result.state as 'granted' | 'denied' | 'prompt'; + addDebug('check_location_permission_result', { + permission: result.state + }); // Listen for permission changes result.addEventListener('change', () => { locationPermission = result.state as 'granted' | 'denied' | 'prompt'; + addDebug('check_location_permission_changed', { + permission: result.state + }); }); return result.state === 'granted'; } catch (e) { // Some browsers don't support permissions API for geolocation console.log('Permissions API not fully supported', e); + addDebug('check_location_permission_api_error', { + error: e instanceof Error ? e.message : String(e) + }); } } + addDebug('check_location_permission_fallback_prompt'); return false; } // Request device orientation permission (iOS 13+) async function requestOrientationPermission() { + addDebug('request_orientation_permission_start'); if (typeof DeviceOrientationEvent === 'undefined') { + addDebug('request_orientation_permission_no_api'); return false; } @@ -144,19 +187,26 @@ if (typeof DeviceOrientationEventTyped.requestPermission === 'function') { try { const permission = await DeviceOrientationEventTyped.requestPermission(); + addDebug('request_orientation_permission_result', { permission }); return permission === 'granted'; } catch (error) { console.error('Error requesting orientation permission:', error); + addDebug('request_orientation_permission_error', { + error: error instanceof Error ? error.message : String(error) + }); return false; } } else { // Permission not required on this device + addDebug('request_orientation_permission_not_required'); return true; } } function startLocationTracking() { + addDebug('start_location_tracking_called'); if (!('geolocation' in navigator)) { + addDebug('start_location_tracking_no_geolocation'); return; } @@ -169,6 +219,13 @@ userLon = position.coords.longitude; locationError = null; locationPermission = 'granted'; + addDebug('watch_position_success', { + lat: userLat, + lon: userLon, + accuracy: position.coords.accuracy, + speed: position.coords.speed, + headFromGps: position.coords.heading + }); // Try to get device heading if available from GPS if (position.coords.heading !== null && position.coords.heading >= 0) { @@ -177,6 +234,10 @@ }, (error) => { console.error('Geolocation error:', error); + addDebug('watch_position_error', { + code: error.code, + message: error.message + }); if (error.code === error.PERMISSION_DENIED) { locationError = 'Location access was denied. Please enable location in your browser settings.'; @@ -198,13 +259,19 @@ timeout: 10000 } ); + + addDebug('watch_position_started', { + watchId + }); } async function startOrientationTracking() { + addDebug('start_orientation_tracking_called'); // Request permission first const hasPermission = await requestOrientationPermission(); if (!hasPermission) { console.log('Orientation permission denied or not available'); + addDebug('start_orientation_tracking_permission_denied'); return; } @@ -212,31 +279,51 @@ if (typeof DeviceOrientationEvent !== 'undefined') { const hasAbsoluteOrientation = 'ondeviceorientationabsolute' in window; const hasOrientation = 'ondeviceorientation' in window; + addDebug('start_orientation_tracking_capabilities', { + hasAbsoluteOrientation, + hasOrientation + }); if (hasAbsoluteOrientation) { window.addEventListener('deviceorientationabsolute', handleOrientation as EventListener); + lastOrientationEvent = 'deviceorientationabsolute'; + addDebug('start_orientation_tracking_listener_added', { + event: 'deviceorientationabsolute' + }); } else if (hasOrientation) { window.addEventListener('deviceorientation', handleOrientation as EventListener); + lastOrientationEvent = 'deviceorientation'; + addDebug('start_orientation_tracking_listener_added', { + event: 'deviceorientation' + }); } } } function startAllTracking() { + addDebug('start_all_tracking_called'); startLocationTracking(); void startOrientationTracking(); } function stopLocationTracking() { + addDebug('stop_tracking_called', { watchId }); if (watchId !== null) { navigator.geolocation.clearWatch(watchId); watchId = null; + addDebug('watch_position_stopped'); } window.removeEventListener('deviceorientationabsolute', handleOrientation as EventListener); window.removeEventListener('deviceorientation', handleOrientation as EventListener); + addDebug('orientation_listeners_removed'); } // Initialize location tracking onMount(() => { + addDebug('component_mounted', { + stepType: currentStep?.type ?? null, + isCurrentActiveStep + }); if (currentStep?.type === 'location' && isCurrentActiveStep) { checkLocationPermission().then(() => { startAllTracking(); @@ -252,23 +339,50 @@ $effect(() => { if (currentStep?.type === 'location' && isCurrentActiveStep) { if (watchId === null) { + addDebug('reactive_start_tracking', { + stepId: currentStep?.id ?? null + }); startAllTracking(); } } else if (watchId !== null) { + addDebug('reactive_stop_tracking', { + stepType: currentStep?.type ?? null, + isCurrentActiveStep + }); stopLocationTracking(); } }); function handleOrientation(event: Event) { const e = event as DeviceOrientationEvent; + lastOrientationEvent = event.type; if (e.absolute && e.alpha !== null) { heading = 360 - e.alpha; // Convert to compass heading + addDebug('orientation_update_absolute', { + eventType: event.type, + alpha: e.alpha, + heading + }); } else if (e.alpha !== null) { // Fallback for iOS with webkitCompassHeading const webkit = e as DeviceOrientationEvent & { webkitCompassHeading?: number }; if (webkit.webkitCompassHeading !== undefined) { heading = webkit.webkitCompassHeading; + addDebug('orientation_update_webkit', { + eventType: event.type, + webkitCompassHeading: webkit.webkitCompassHeading, + heading + }); + } else { + addDebug('orientation_update_no_heading', { + eventType: event.type, + alpha: e.alpha + }); } + } else { + addDebug('orientation_event_without_alpha', { + eventType: event.type + }); } } @@ -536,6 +650,42 @@ {/if} {/if} + +
+ Client Debug Info +
+
step.id: {currentStep.id}
+
step.type: {currentStep.type}
+
isCurrentActiveStep: {String(isCurrentActiveStep)}
+
locationPermission: {locationPermission}
+
locationError: {locationError ?? 'null'}
+
watchId: {watchId ?? 'null'}
+
userLat: {userLat ?? 'null'}
+
userLon: {userLon ?? 'null'}
+
stepLat: {currentStep.latitude ?? 'null'}
+
stepLon: {currentStep.longitude ?? 'null'}
+
proximityRadius: {currentStep.proximityRadius ?? 'null'}
+
distance: {distance ?? 'null'}
+
heading: {heading ?? 'null'}
+
arrowRotation: {arrowRotation}
+
lastOrientationEvent: {lastOrientationEvent ?? 'null'}
+
Recent logs ({debugLogs.length})
+
+ {#if debugLogs.length === 0} +
No logs yet
+ {:else} + {#each debugLogs as log (log.ts + log.event)} +
+
[{log.ts}] {log.event}
+ {#if log.details} +
{JSON.stringify(log.details, null, 2)}
+ {/if} +
+ {/each} + {/if} +
+
+
{#if currentStep.hint}