Refactor map functionality: streamline cache visibility logic by removing proximity fallback for iOS, ensuring player must have heading to view caches, and simplify heading checks in player game hook

This commit is contained in:
bobbert committed 2026-04-09 08:52:23 +01:00
1 parent 63c082bb8f
commit 4a5cc716db
3 files changed
+19 -46

No files matched your search

+4 -12
View File
@@ -11,8 +11,9 @@ const PlayerMapView = ({userLocation, visibleCaches, heading, claimDistance}) =>
if (!userLocation) return null;
const hasHeading = heading !== null && heading !== undefined;
const coneCoords = hasHeading ? getFovCone(userLocation, heading) : null;
const coneCoords = (heading !== null && heading !== undefined)
? getFovCone(userLocation, heading)
: null;
return (
<MapView
@@ -46,7 +47,7 @@ const PlayerMapView = ({userLocation, visibleCaches, heading, claimDistance}) =>
/>
</React.Fragment>
))}
{/* FOV cone when heading is available */}
{/* FOV heading cone */}
{coneCoords && (
<Polygon
coordinates={coneCoords}
@@ -55,15 +56,6 @@ const PlayerMapView = ({userLocation, visibleCaches, heading, claimDistance}) =>
strokeWidth={1}
/>
)}
{/* Proximity circle when heading is NOT available (iOS fallback) */}
{!hasHeading && claimDistance > 0 && (
<Circle
center={userLocation}
radius={claimDistance}
fillColor="rgba(66,133,244,0.12)"
strokeColor="rgba(66,133,244,0.40)"
/>
)}
</MapView>
);
};
+5 -18
View File
@@ -3,7 +3,7 @@ import {Platform, StyleSheet, View} from 'react-native';
import MapView, {Circle, Marker, Polygon} from 'react-native-maps';
import * as Location from 'expo-location';
import Screen from '../layout/Screen';
import {getFovCone, isInClaimCone, isWithinRadius} from '../../utils/geoMath';
import {getFovCone, isInClaimCone} from '../../utils/geoMath';
const DEFAULT_REGION = {latitude: 51.5074, longitude: -0.1278, latitudeDelta: 0.01, longitudeDelta: 0.01};
@@ -71,14 +71,10 @@ const ExpandedMapScreen = ({route}) => {
? getFovCone(userLocation, heading)
: null;
// For players — when heading is available use FOV cone, otherwise fall back
// to proximity-only so the expanded map works on iPhones without compass data
const visiblePlayerCaches = (!isAdmin && userLocation)
? caches.filter((c) =>
hasHeading
? isInClaimCone(heading, userLocation, c.coordinates, claimDistance)
: isWithinRadius(userLocation, c.coordinates, claimDistance),
)
// For players — heading is required so the player must point their device
// towards a cache for it to appear (same logic as the main MapScreen)
const visiblePlayerCaches = (!isAdmin && userLocation && hasHeading)
? caches.filter((c) => isInClaimCone(heading, userLocation, c.coordinates, claimDistance))
: [];
return (
@@ -140,15 +136,6 @@ const ExpandedMapScreen = ({route}) => {
strokeWidth={1}
/>
)}
{/* Proximity circle when heading is unavailable (iOS fallback) */}
{!isAdmin && userLocation && !hasHeading && claimDistance > 0 && (
<Circle
center={userLocation}
radius={claimDistance}
fillColor="rgba(66,133,244,0.12)"
strokeColor="rgba(66,133,244,0.40)"
/>
)}
</MapView>
</View>
</Screen>
+10 -16
View File
@@ -1,5 +1,5 @@
import {useEffect, useRef, useState} from 'react';
import {isInClaimCone, isWithinRadius} from '../utils/geoMath';
import {isInClaimCone} from '../utils/geoMath';
const usePlayerGame = (playerLocation, playerHeading, activeCaches, claimDistance) => {
// State ----------------------
@@ -11,8 +11,9 @@ const usePlayerGame = (playerLocation, playerHeading, activeCaches, claimDistanc
// Handlers -------------------
useEffect(() => {
// No location at all — clear everything
if (!playerLocation) {
// Both location AND heading are required — the player must physically
// point their device towards a cache to trigger the claim countdown
if (!playerLocation || playerHeading === null || playerHeading === undefined) {
if (prevIdsRef.current !== '') {
prevIdsRef.current = '';
setVisibleCaches([]);
@@ -21,24 +22,17 @@ const usePlayerGame = (playerLocation, playerHeading, activeCaches, claimDistanc
return;
}
const hasHeading = playerHeading !== null && playerHeading !== undefined;
// When heading is available use the full FOV cone check.
// When heading is NOT available (common on iOS / Apple Maps where the
// compass may be delayed or unavailable) fall back to proximity-only so
// iPhone users can still discover and claim caches within range.
const nearby = (activeCaches || []).filter((cache) =>
hasHeading
? isInClaimCone(playerHeading, playerLocation, cache.coordinates, claimDistance)
: isWithinRadius(playerLocation, cache.coordinates, claimDistance),
// Find all caches that fall inside the claim cone
const inCone = (activeCaches || []).filter((cache) =>
isInClaimCone(playerHeading, playerLocation, cache.coordinates, claimDistance),
);
// Only update state when the set of visible caches actually changes
const newIds = nearby.map((c) => c.id).join(',');
const newIds = inCone.map((c) => c.id).join(',');
if (newIds !== prevIdsRef.current) {
prevIdsRef.current = newIds;
setVisibleCaches(nearby);
setIsClaiming(nearby.length > 0);
setVisibleCaches(inCone);
setIsClaiming(inCone.length > 0);
}
}, [playerLocation, playerHeading, activeCaches, claimDistance]);