Enhance cache management and UI: implement multi-team cache claiming, add admin waitlist functionality, and improve leaderboard display for better user experience

This commit is contained in:
bobbert committed 2026-04-08 20:42:11 +01:00
1 parent e34cbba9b5
commit 9a9db74369
16 files changed
+636 -129

No files matched your search

+8 -1
View File
@@ -12,6 +12,7 @@ const session = {
isBusiness: null,
isAcceptedAdmin: false,
teamsEnabled: false,
selectedCacheId: null,
};
// --- Handlers ---
@@ -44,6 +45,11 @@ const setSessionTeamsEnabled = (val) => {
persistSession();
};
const setSelectedCache = (cacheId) => {
session.selectedCacheId = cacheId ?? null;
persistSession();
};
const getSession = () => ({ ...session });
const clearSession = () => {
@@ -63,6 +69,7 @@ const clearGameSession = () => {
session.currentTid = null;
session.isAcceptedAdmin = false;
session.teamsEnabled = false;
session.selectedCacheId = null;
persistSession();
};
@@ -85,4 +92,4 @@ const loadSession = async () => {
return { ...session };
};
export { setSessionUser, setSessionGroup, setSessionTeam, setSessionTeamsEnabled, getSession, clearSession, clearGameSession, loadSession };
export { setSessionUser, setSessionGroup, setSessionTeam, setSessionTeamsEnabled, setSelectedCache, getSession, clearSession, clearGameSession, loadSession };
+40 -3
View File
@@ -10,6 +10,7 @@ const useGameHook = () => {
const teamsEndpoint = `${API_BASE_URL}/teams`;
const teamMembersEndpoint = `${API_BASE_URL}/team-members`;
const gameDataEndpoint = `${API_BASE_URL}/game-data`;
const adminWaitlistEndpoint = `${API_BASE_URL}/admin-waitlist`;
// Handlers -------------------
@@ -116,10 +117,11 @@ const useGameHook = () => {
return response.isSuccess ? response.result : null;
};
// Claim a cache via the multi-team claim endpoint
const claimCache = async (payload) => {
const response = await API.put(
`${gameDataEndpoint}/${payload.gid}/caches/${payload.cacheId}`,
{ClaimedByUid: payload.uid, ClaimedByTid: payload.tid},
const response = await API.post(
`${gameDataEndpoint}/${payload.gid}/caches/${payload.cacheId}/claim`,
{Uid: payload.uid, Tid: payload.tid},
);
return response.isSuccess ? response.result : null;
};
@@ -129,6 +131,36 @@ const useGameHook = () => {
return response.isSuccess;
};
// Game reset — clear all claims for a game
const resetGame = async (gid) => {
const response = await API.post(`${gameDataEndpoint}/${gid}/reset`, {});
return response.isSuccess;
};
// Player progress reset — clear all claims by a specific user
const resetPlayerProgress = async (gid, uid) => {
const response = await API.post(`${gameDataEndpoint}/${gid}/reset-player/${uid}`, {});
return response.isSuccess;
};
// Admin waitlist
const getAdminWaitlist = async (gid, uid = null) => {
let url = `${adminWaitlistEndpoint}?Gid=${gid}`;
if (uid !== null) url += `&Uid=${uid}`;
const response = await API.get(url);
return response.isSuccess ? response.result : [];
};
const approveAdmin = async (waitlistId) => {
const response = await API.post(`${adminWaitlistEndpoint}/${waitlistId}/approve`, {});
return response.isSuccess ? response.result : null;
};
const rejectAdmin = async (waitlistId) => {
const response = await API.delete(`${adminWaitlistEndpoint}/${waitlistId}`);
return response.isSuccess;
};
// Return ---------------------
return {
@@ -151,6 +183,11 @@ const useGameHook = () => {
upsertCache,
claimCache,
deleteCache,
resetGame,
resetPlayerProgress,
getAdminWaitlist,
approveAdmin,
rejectAdmin,
};
};
+18 -10
View File
@@ -1,34 +1,42 @@
import {useEffect, useState} from 'react';
import {useEffect, useRef, useState} from 'react';
import {isInClaimCone} from '../utils/geoMath';
const usePlayerGame = (playerLocation, playerHeading, activeCaches, claimDistance) => {
// State ----------------------
const [visibleCache, setVisibleCache] = useState(null);
const [visibleCaches, setVisibleCaches] = useState([]);
const [isClaiming, setIsClaiming] = useState(false);
const prevIdsRef = useRef('');
// Handlers -------------------
useEffect(() => {
if (!playerLocation || playerHeading === null || playerHeading === undefined) {
setVisibleCache(null);
setIsClaiming(false);
if (prevIdsRef.current !== '') {
prevIdsRef.current = '';
setVisibleCaches([]);
setIsClaiming(false);
}
return;
}
// Find the first cache that falls inside the invisible claim cone
// (same FOV angle as the visible cone, but radius = admin-set claimDistance)
const cacheInCone = (activeCaches || []).find((cache) =>
// Find all caches that fall inside the claim cone
const inCone = (activeCaches || []).filter((cache) =>
isInClaimCone(playerHeading, playerLocation, cache.coordinates, claimDistance)
);
setVisibleCache(cacheInCone || null);
setIsClaiming(Boolean(cacheInCone));
// Only update state when the set of visible caches actually changes
const newIds = inCone.map((c) => c.id).join(',');
if (newIds !== prevIdsRef.current) {
prevIdsRef.current = newIds;
setVisibleCaches(inCone);
setIsClaiming(inCone.length > 0);
}
}, [playerLocation, playerHeading, activeCaches, claimDistance]);
// Return ---------------------
return {visibleCache, isClaiming, setIsClaiming};
return {visibleCaches, isClaiming, setIsClaiming};
};
export default usePlayerGame;