Add pending admin state handling and update navigation logic for admin approval

This commit is contained in:
bobbert committed 2026-04-09 15:08:36 +01:00
1 parent 41b0b723ca
commit f69ead8e45
7 files changed
+148 -11

No files matched your search

+14 -2
View File
@@ -15,7 +15,14 @@ const BottomNavbar = ({navigation, routeName, routeParams}) => {
const adminTabs = [
{label: 'Settings', route: 'GameSettingsScreen', onPress: () => navigation.navigate('GameSettingsScreen')},
{label: 'Map', route: 'MapScreen', onPress: () => navigation.navigate('MapScreen')},
{
label: 'Map',
route: 'MapScreen',
onPress: () => navigation.navigate('MapScreen', {
selectedDepartmentSGid: null,
selectedDepartmentName: null,
}),
},
{
label: 'Players',
route: 'PlayersScreen',
@@ -39,6 +46,11 @@ const BottomNavbar = ({navigation, routeName, routeParams}) => {
];
const tabs = session.isAcceptedAdmin ? adminTabs : playerTabs;
const pendingTabs = [
{label: 'Pending', route: 'TeamScreen', onPress: () => navigation.navigate('TeamScreen')},
...(inGame ? [{label: 'Leave', route: '__leave__', onPress: () => handleLeaveGame()}] : []),
];
const visibleTabs = (session.isPendingAdmin && !session.isAcceptedAdmin) ? pendingTabs : tabs;
// State ----------------------
// Handlers -------------------
@@ -67,7 +79,7 @@ const BottomNavbar = ({navigation, routeName, routeParams}) => {
return (
<View style={[styles.container, {paddingBottom: insets.bottom}]}>
{tabs.map((tab) => {
{visibleTabs.map((tab) => {
const isActive = routeName === tab.route;
const isLeave = tab.route === '__leave__';
return (
@@ -184,6 +184,14 @@ const LeaderboardScreen = ({navigation, route}) => {
);
}
if (session.isPendingAdmin && !session.isAcceptedAdmin) {
return (
<Screen style={styles.center}>
<Text style={styles.body}>Waiting for admin approval. You will get access once approved.</Text>
</Screen>
);
}
if (isBusiness && isAdmin && !selectedDepartment) {
return (
<Screen style={styles.container}>
+42 -4
View File
@@ -10,7 +10,7 @@ import ClaimTimerView from '../gameplay/ClaimTimerView';
import PlayerMapView from '../gameplay/PlayerMapView';
import useGameHook from '../../hooks/useGameHook';
import usePlayerGame from '../../hooks/usePlayerGame';
import {getSession, setSelectedCache, setSessionGroup, setSessionTeam, setSessionUser} from '../../hooks/SessionStore';
import {getSession, setPendingAdmin, setSelectedCache, setSessionGroup, setSessionTeam, setSessionUser} from '../../hooks/SessionStore';
import {getFovCone} from '../../utils/geoMath';
const DEFAULT_REGION = {latitude: 51.5074, longitude: -0.1278, latitudeDelta: 0.05, longitudeDelta: 0.05};
@@ -19,7 +19,7 @@ const DEFAULT_REGION = {latitude: 51.5074, longitude: -0.1278, latitudeDelta: 0.
const MapScreen = ({navigation, route}) => {
const session = getSession();
const isBusiness = Boolean(session.isBusiness);
const {getCaches, claimCache, upsertCache, deleteCache, joinPrivateGame, createPrivateGame, getLobby, getUser, getSubgroups, getGroupByOrgCode} = useGameHook();
const {getCaches, claimCache, upsertCache, deleteCache, joinPrivateGame, joinTeamByCode, createPrivateGame, getLobby, getUser, getSubgroups, getGroupByOrgCode} = useGameHook();
// Stable ref to prevent infinite re-render loops
const getCachesRef = useRef(getCaches);
@@ -104,8 +104,9 @@ const MapScreen = ({navigation, route}) => {
return map;
}, [departments]);
const routeDepartmentSGid = route?.params?.selectedDepartmentSGid;
const isDepartmentScopedMap = routeDepartmentSGid !== null && routeDepartmentSGid !== undefined;
const effectiveSGid = isBusiness
? (isAdmin ? (selectedCacheSubgroupId ?? routeDepartmentSGid ?? defaultMemberSGid ?? null) : (session.currentSGid ?? null))
? (isAdmin ? (isDepartmentScopedMap ? routeDepartmentSGid : null) : (session.currentSGid ?? null))
: null;
useEffect(() => {
@@ -199,6 +200,7 @@ const MapScreen = ({navigation, route}) => {
const result = await joinPrivateGame({JoinCode: gameCode.trim().toUpperCase(), Uid: session.currentUid});
if (!result) return;
setSessionGroup(result.Gid, result.SGid);
setPendingAdmin(false);
const freshUser = await getUser(session.currentUid);
if (freshUser) {
@@ -219,6 +221,7 @@ const MapScreen = ({navigation, route}) => {
MaxMemberSubgroups: 1,
});
if (!result) return;
setPendingAdmin(false);
const freshUser = await getUser(session.currentUid);
if (freshUser) {
setSessionUser(freshUser);
@@ -239,6 +242,7 @@ const MapScreen = ({navigation, route}) => {
isBusiness: true,
});
if (!result) return;
setPendingAdmin(false);
const freshUser = await getUser(session.currentUid);
if (freshUser) {
setSessionUser(freshUser);
@@ -265,8 +269,33 @@ const MapScreen = ({navigation, route}) => {
const handleJoinDepartment = async () => {
setJoinError('');
if (!deptCode.trim()) return;
const enteredCode = deptCode.trim().toUpperCase();
const adminCode = String(matchedGroup?.AdminJoinCode || '').trim().toUpperCase();
// If the code matches the org admin code, submit an admin waitlist request
if (adminCode && enteredCode === adminCode) {
const waitlistResult = await joinTeamByCode({
JoinCode: enteredCode,
Uid: session.currentUid,
ExpectedGid: matchedGroup.Gid,
});
if (!waitlistResult || !waitlistResult.adminWaitlist) {
setJoinError('Could not submit admin request. Please try again.');
return;
}
setSessionGroup(matchedGroup.Gid, null);
setSessionTeam(null);
setPendingAdmin(true);
setInGame(true);
setIsAdmin(false);
setJoinStep(null);
setDeptCode('');
navigation.navigate('TeamScreen');
return;
}
const result = await joinPrivateGame({
JoinCode: deptCode.trim().toUpperCase(),
JoinCode: enteredCode,
Uid: session.currentUid,
ExpectedGid: matchedGroup.Gid,
});
@@ -275,6 +304,7 @@ const MapScreen = ({navigation, route}) => {
return;
}
setSessionGroup(result.Gid, result.SGid);
setPendingAdmin(false);
const freshUser = await getUser(session.currentUid);
if (freshUser) {
setSessionUser(freshUser);
@@ -684,6 +714,14 @@ const MapScreen = ({navigation, route}) => {
);
}
if (session.isPendingAdmin && !session.isAcceptedAdmin) {
return (
<Screen style={styles.center}>
<Text style={styles.loadingText}>Waiting for admin approval. You will get access once approved.</Text>
</Screen>
);
}
// Player — loading location
if (playerLoading) {
return (
+8
View File
@@ -178,6 +178,14 @@ const PlayersScreen = ({navigation, route}) => {
);
}
if (session.isPendingAdmin && !session.isAcceptedAdmin) {
return (
<Screen style={styles.center}>
<Text style={styles.body}>Waiting for admin approval. You will get access once approved.</Text>
</Screen>
);
}
if (isBusiness && isAdmin && !selectedDepartment) {
return (
<Screen style={styles.container}>
+59 -2
View File
@@ -4,9 +4,9 @@ import Screen from '../layout/Screen';
import Card from '../UI/Card';
import {Button, ButtonTray} from '../UI/Button';
import useGameHook from '../../hooks/useGameHook';
import {getSession, setSessionTeam} from '../../hooks/SessionStore';
import {getSession, setPendingAdmin, setSessionTeam, setSessionUser} from '../../hooks/SessionStore';
const TeamScreen = () => {
const TeamScreen = ({navigation}) => {
const session = getSession();
const isAdmin = session.isAcceptedAdmin;
const isBusiness = Boolean(session.isBusiness);
@@ -48,6 +48,24 @@ const TeamScreen = () => {
if (subgroup) teamsAreEnabled = Boolean(subgroup.TeamsEnabled);
}
if (session.isPendingAdmin && !session.isAcceptedAdmin) {
const freshUser = await getUser(session.currentUid);
if (freshUser?.IsAcceptedAdmin) {
setPendingAdmin(false);
setSessionUser(freshUser);
setSessionTeam(freshUser.TGid ?? null);
navigation.reset({index: 0, routes: [{name: 'Admin'}]});
return;
}
const waitlist = await getAdminWaitlist(session.currentGid, session.currentUid);
if (waitlist.length > 0) {
setOnAdminWaitlist(true);
setLoading(false);
return;
}
setPendingAdmin(false);
}
if (!teamsAreEnabled) {
setTeamsDisabled(true);
setLoading(false);
@@ -69,6 +87,44 @@ const TeamScreen = () => {
load();
}, [activeTid]);
useEffect(() => {
if (!onAdminWaitlist && !session.isPendingAdmin) return;
let stopped = false;
const refreshWaitlistStatus = async () => {
const currentSession = getSession();
if (!currentSession.currentUid || !currentSession.currentGid) return;
const freshUser = await getUser(currentSession.currentUid);
if (stopped) return;
if (freshUser?.IsAcceptedAdmin) {
setPendingAdmin(false);
setSessionUser(freshUser);
setSessionTeam(freshUser.TGid ?? null);
navigation.reset({index: 0, routes: [{name: 'Admin'}]});
return;
}
const waitlist = await getAdminWaitlist(currentSession.currentGid, currentSession.currentUid);
if (stopped) return;
const isStillWaiting = waitlist.length > 0;
setOnAdminWaitlist(isStillWaiting);
if (!isStillWaiting) {
setPendingAdmin(false);
}
};
refreshWaitlistStatus();
const intervalId = setInterval(refreshWaitlistStatus, 5000);
return () => {
stopped = true;
clearInterval(intervalId);
};
}, [onAdminWaitlist, session.isPendingAdmin]);
const handleJoinTeam = async () => {
if (!teamCode.trim()) return;
// Force uppercase just in case a lowercase code is pasted
@@ -82,6 +138,7 @@ const TeamScreen = () => {
// If the server indicates this code matched the admin join code
if (result.adminWaitlist) {
setPendingAdmin(true);
setOnAdminWaitlist(true);
return;
}
+14 -3
View File
@@ -11,6 +11,7 @@ const session = {
currentTid: null,
isBusiness: null,
isAcceptedAdmin: false,
isPendingAdmin: false,
teamsEnabled: false,
selectedCacheId: null,
};
@@ -24,13 +25,16 @@ const setSessionUser = (user) => {
session.currentTid = user?.TGid ?? null;
session.isBusiness = user?.isBusiness ?? null;
session.isAcceptedAdmin = Boolean(user?.IsAcceptedAdmin);
session.isPendingAdmin = Boolean(
user?.isBusiness && user?.Gid && !user?.SGid && !user?.IsAcceptedAdmin,
);
persistSession();
};
const setSessionGroup = (gid, sgid = null) => {
session.currentGid = gid ?? null;
if (sgid !== null && sgid !== undefined) {
session.currentSGid = sgid;
if (sgid !== undefined) {
session.currentSGid = sgid ?? null;
}
persistSession();
};
@@ -45,6 +49,11 @@ const setSessionTeamsEnabled = (val) => {
persistSession();
};
const setPendingAdmin = (val) => {
session.isPendingAdmin = Boolean(val);
persistSession();
};
const setSelectedCache = (cacheId) => {
session.selectedCacheId = cacheId ?? null;
persistSession();
@@ -59,6 +68,7 @@ const clearSession = () => {
session.currentTid = null;
session.isBusiness = null;
session.isAcceptedAdmin = false;
session.isPendingAdmin = false;
AsyncStorage.removeItem(STORAGE_KEY).catch(() => {});
};
@@ -68,6 +78,7 @@ const clearGameSession = () => {
session.currentSGid = null;
session.currentTid = null;
session.isAcceptedAdmin = false;
session.isPendingAdmin = false;
session.teamsEnabled = false;
session.selectedCacheId = null;
persistSession();
@@ -92,4 +103,4 @@ const loadSession = async () => {
return { ...session };
};
export { setSessionUser, setSessionGroup, setSessionTeam, setSessionTeamsEnabled, setSelectedCache, getSession, clearSession, clearGameSession, loadSession };
export { setSessionUser, setSessionGroup, setSessionTeam, setSessionTeamsEnabled, setPendingAdmin, setSelectedCache, getSession, clearSession, clearGameSession, loadSession };
+3
View File
@@ -140,6 +140,9 @@ const useGameHook = () => {
};
if (payload.JoinCode !== undefined) data.JoinCode = payload.JoinCode;
if (payload.Tid !== undefined) data.Tid = payload.Tid;
if (payload.ExpectedGid !== undefined && payload.ExpectedGid !== null) {
data.ExpectedGid = payload.ExpectedGid;
}
if (payload.ExpectedSGid !== undefined && payload.ExpectedSGid !== null) {
data.ExpectedSGid = payload.ExpectedSGid;
}