From 05747933cf2f420f0f3e446626c7dd701c39d92b Mon Sep 17 00:00:00 2001 From: mudabbir-ahmad Date: Thu, 9 Apr 2026 22:32:45 +0100 Subject: [PATCH] global claim fix attempt #1 --- src/components/gameplay/ClaimTimerView.js | 54 ++++++++++++----- .../screens/global/GlobalCacheViewScreen.js | 12 ++-- .../screens/global/GlobalEventsScreen.js | 25 +++++--- .../screens/global/GlobalMapScreen.js | 59 +++++++++++++------ 4 files changed, 105 insertions(+), 45 deletions(-) diff --git a/src/components/gameplay/ClaimTimerView.js b/src/components/gameplay/ClaimTimerView.js index 45b8c0f..f390a46 100644 --- a/src/components/gameplay/ClaimTimerView.js +++ b/src/components/gameplay/ClaimTimerView.js @@ -2,36 +2,62 @@ import React, {useEffect, useRef, useState} from 'react'; import {StyleSheet, Text, View} from 'react-native'; import {CACHE_CLAIM_TIMER} from '../../utils/geoMath'; -const ClaimTimerView = ({ cache, onClaimSuccess, isClaiming, showClaimedPopup }) => { - const [timeLeft, setTimeLeft] = useState(CACHE_CLAIM_TIMER); - // Use a ref for the callback so it does not appear in the dependency array +const ClaimTimerView = ({ + cache, + onClaimSuccess, + isClaiming, + showClaimedPopup, + claimDurationSeconds = CACHE_CLAIM_TIMER, + claimedPopupMessage = 'Cache Claimed!', +}) => { + const [timeLeft, setTimeLeft] = useState(claimDurationSeconds); const onClaimRef = useRef(onClaimSuccess); + const hasClaimedRef = useRef(false); onClaimRef.current = onClaimSuccess; + useEffect(() => { + setTimeLeft(claimDurationSeconds); + hasClaimedRef.current = false; + }, [cache?.id, claimDurationSeconds]); useEffect(() => { let timer; - if (isClaiming && cache && timeLeft > 0) { - timer = setTimeout(() => setTimeLeft((prev) => prev - 1), 1000); - } else if (isClaiming && timeLeft === 0 && cache) { - onClaimRef.current(cache.id); - } else if (!isClaiming) { - setTimeLeft(CACHE_CLAIM_TIMER); + + if (!isClaiming || !cache) { + if (!isClaiming) { + setTimeLeft(claimDurationSeconds); + hasClaimedRef.current = false; + } + return () => clearTimeout(timer); } + + if (claimDurationSeconds <= 0) { + if (!hasClaimedRef.current) { + hasClaimedRef.current = true; + onClaimRef.current(cache.id); + } + return () => clearTimeout(timer); + } + + if (timeLeft > 0) { + timer = setTimeout(() => setTimeLeft((prev) => prev - 1), 1000); + } else if (!hasClaimedRef.current) { + hasClaimedRef.current = true; + onClaimRef.current(cache.id); + } + return () => clearTimeout(timer); - }, [isClaiming, timeLeft, cache]); + }, [claimDurationSeconds, cache, isClaiming, timeLeft]); - - // Show "Cache Claimed!" popup after a successful claim if (showClaimedPopup) { return ( - Cache Claimed! + {claimedPopupMessage} ); } - if (!isClaiming || !cache) { + if (!isClaiming || !cache || claimDurationSeconds <= 0) { return null; } diff --git a/src/components/screens/global/GlobalCacheViewScreen.js b/src/components/screens/global/GlobalCacheViewScreen.js index bd8d58a..9834a4c 100644 --- a/src/components/screens/global/GlobalCacheViewScreen.js +++ b/src/components/screens/global/GlobalCacheViewScreen.js @@ -1,10 +1,10 @@ -import { useEffect } from "react"; -import { StyleSheet, Text, View } from "react-native"; +import {useEffect} from "react"; +import {StyleSheet, Text, View} from "react-native"; import Screen from "../../layout/Screen"; -import { Button, ButtonTray } from "../../UI/Button"; +import {Button, ButtonTray} from "../../UI/Button"; import Card from "../../UI/Card"; -import { getSession } from "../../../hooks/SessionStore"; -import { GAME_MODE } from "../../../utils/gameConstants"; +import {getSession} from "../../../hooks/SessionStore"; +import {GAME_MODE} from "../../../utils/gameConstants"; const GlobalCacheViewScreen = ({ navigation, route }) => { // Initialisations --------------------- @@ -23,6 +23,8 @@ const GlobalCacheViewScreen = ({ navigation, route }) => { const handleBackToMap = () => { navigation.navigate("GlobalMapScreen", { eventId: cache.CacheEventID ?? session.currentGlobalEventId, + event: route.params?.event ?? null, + cache, selectedCacheId: cache.CacheID, }); }; diff --git a/src/components/screens/global/GlobalEventsScreen.js b/src/components/screens/global/GlobalEventsScreen.js index 6ae1e3d..3206505 100644 --- a/src/components/screens/global/GlobalEventsScreen.js +++ b/src/components/screens/global/GlobalEventsScreen.js @@ -1,11 +1,11 @@ -import { useCallback, useEffect, useRef, useState } from "react"; -import { ActivityIndicator, StyleSheet, Text, View } from "react-native"; +import {useCallback, useEffect, useRef, useState} from "react"; +import {ActivityIndicator, StyleSheet, Text, View} from "react-native"; import Screen from "../../layout/Screen"; -import { Button, ButtonTray } from "../../UI/Button"; +import {Button, ButtonTray} from "../../UI/Button"; import EventList from "../../../entity/event/EventList"; import useGlobalHook from "../../../hooks/useGlobalHook"; -import { getSession, setGlobalSession } from "../../../hooks/SessionStore"; -import { GAME_MODE } from "../../../utils/gameConstants"; +import {getSession, setGlobalSession} from "../../../hooks/SessionStore"; +import {GAME_MODE} from "../../../utils/gameConstants"; const GlobalEventsScreen = ({ navigation }) => { // Initialisations --------------------- @@ -93,7 +93,10 @@ const GlobalEventsScreen = ({ navigation }) => { const existingPlayerId = pickPlayerId(existing); if (existingPlayerId !== null) { setGlobalSession(event.EventID, existingPlayerId); - navigation.navigate("GlobalMapScreen", { eventId: event.EventID }); + navigation.navigate("GlobalMapScreen", { + eventId: event.EventID, + event, + }); setJoining(null); return; } @@ -107,7 +110,10 @@ const GlobalEventsScreen = ({ navigation }) => { const joinedPlayerId = pickPlayerId(player); if (joinedPlayerId !== null) { setGlobalSession(event.EventID, joinedPlayerId); - navigation.navigate("GlobalMapScreen", { eventId: event.EventID }); + navigation.navigate("GlobalMapScreen", { + eventId: event.EventID, + event, + }); setJoining(null); return; } @@ -123,7 +129,10 @@ const GlobalEventsScreen = ({ navigation }) => { const recoveredPlayerId = pickPlayerId(recovered); if (recoveredPlayerId !== null) { setGlobalSession(event.EventID, recoveredPlayerId); - navigation.navigate("GlobalMapScreen", { eventId: event.EventID }); + navigation.navigate("GlobalMapScreen", { + eventId: event.EventID, + event, + }); } else { setError("Failed to join event. Please try again."); } diff --git a/src/components/screens/global/GlobalMapScreen.js b/src/components/screens/global/GlobalMapScreen.js index 5f6e4d1..b796c3c 100644 --- a/src/components/screens/global/GlobalMapScreen.js +++ b/src/components/screens/global/GlobalMapScreen.js @@ -1,16 +1,16 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { ActivityIndicator, StyleSheet, Text, View } from "react-native"; -import MapView, { Circle, Marker, Polygon } from "react-native-maps"; +import {useCallback, useEffect, useMemo, useRef, useState} from "react"; +import {ActivityIndicator, StyleSheet, Text, 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 { Button, ButtonTray } from "../../UI/Button"; +import {Button, ButtonTray} from "../../UI/Button"; import ClaimTimerView from "../../gameplay/ClaimTimerView"; import CacheList from "../../../entity/cache/CacheList"; import useGlobalHook from "../../../hooks/useGlobalHook"; import usePlayerGame from "../../../hooks/usePlayerGame"; -import { getSession } from "../../../hooks/SessionStore"; -import { getFovCone } from "../../../utils/geoMath"; -import { GAME_MODE } from "../../../utils/gameConstants"; +import {getSession} from "../../../hooks/SessionStore"; +import {getFovCone} from "../../../utils/geoMath"; +import {GAME_MODE} from "../../../utils/gameConstants"; const DEFAULT_REGION = { latitude: 51.5074, @@ -25,7 +25,9 @@ const GlobalMapScreen = ({ navigation, route }) => { // Initialisations --------------------- const session = getSession(); - const eventId = route?.params?.eventId ?? session.currentGlobalEventId; + const routeEvent = route?.params?.event ?? null; + const eventId = + route?.params?.eventId ?? routeEvent?.EventID ?? session.currentGlobalEventId; const { getCachesByEvent, getFindsByPlayer, logFind } = useGlobalHook(); const globalApiRef = useRef({ getCachesByEvent, @@ -47,8 +49,11 @@ const GlobalMapScreen = ({ navigation, route }) => { const [isLoading, setIsLoading] = useState(true); const [activeTab, setActiveTab] = useState("map"); const [error, setError] = useState(""); - const [selectedCacheId, setSelectedCacheId] = useState(null); + const [selectedCacheId, setSelectedCacheId] = useState( + route?.params?.selectedCacheId ?? route?.params?.cache?.CacheID ?? null, + ); const [claimedPopupVisible, setClaimedPopupVisible] = useState(false); + const [claimedPopupMessage, setClaimedPopupMessage] = useState("Cache Claimed!"); const claimableCaches = useMemo( () => @@ -114,6 +119,10 @@ const GlobalMapScreen = ({ navigation, route }) => { return; } + const claimedCache = (caches || []).find( + (cache) => String(cache.CacheID) === String(cacheId), + ); + const result = await globalApiRef.current.logFind({ FindPlayerID: session.currentGlobalPlayerId, FindCacheID: cacheId, @@ -123,12 +132,19 @@ const GlobalMapScreen = ({ navigation, route }) => { setIsClaiming(false); if (result) { + const points = Number(claimedCache?.CachePoints ?? 0); + const cacheName = claimedCache?.CacheName || "Cache"; + setFoundCacheIds((prev) => + prev.includes(cacheId) ? prev : [...prev, cacheId], + ); + setClaimedPopupMessage( + `${cacheName} claimed! You received ${points} points.`, + ); setClaimedPopupVisible(true); setTimeout(() => setClaimedPopupVisible(false), 3000); - await loadData({ forceRefresh: true }); } }, - [foundCacheIds, loadData, session.currentGlobalPlayerId, setIsClaiming], + [caches, foundCacheIds, session.currentGlobalPlayerId, setIsClaiming], ); const handleCacheSelect = (cache) => { @@ -143,6 +159,8 @@ const GlobalMapScreen = ({ navigation, route }) => { if (!selected) return; navigation.navigate("GlobalCacheViewScreen", { cache: selected, + event: routeEvent, + eventId, isFound: foundCacheIds.includes(selected.CacheID), }); }; @@ -197,14 +215,15 @@ const GlobalMapScreen = ({ navigation, route }) => { }, [route?.params?.selectedCacheId]); useEffect(() => { - if ( - selectedCacheId && - claimableCaches.some((cache) => cache.id === selectedCacheId) - ) { - return; + if (route?.params?.cache?.CacheID) { + setSelectedCacheId(route.params.cache.CacheID); } - setSelectedCacheId(claimableCaches[0]?.id || null); - }, [claimableCaches, selectedCacheId]); + }, [route?.params?.cache?.CacheID]); + + useEffect(() => { + if (selectedCacheId) return; + setSelectedCacheId(route?.params?.cache?.CacheID ?? claimableCaches[0]?.id ?? null); + }, [claimableCaches, route?.params?.cache?.CacheID, selectedCacheId]); useEffect(() => { let locationSub; @@ -318,6 +337,8 @@ const GlobalMapScreen = ({ navigation, route }) => { onCalloutPress={() => navigation.navigate("GlobalCacheViewScreen", { cache, + event: routeEvent, + eventId, isFound: found, }) } @@ -356,6 +377,8 @@ const GlobalMapScreen = ({ navigation, route }) => { isClaiming={isClaiming} onClaimSuccess={handleClaim} showClaimedPopup={claimedPopupVisible} + claimDurationSeconds={0} + claimedPopupMessage={claimedPopupMessage} />