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 = [ const adminTabs = [
{label: 'Settings', route: 'GameSettingsScreen', onPress: () => navigation.navigate('GameSettingsScreen')}, {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', label: 'Players',
route: 'PlayersScreen', route: 'PlayersScreen',
@@ -39,6 +46,11 @@ const BottomNavbar = ({navigation, routeName, routeParams}) => {
]; ];
const tabs = session.isAcceptedAdmin ? adminTabs : playerTabs; 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 ---------------------- // State ----------------------
// Handlers ------------------- // Handlers -------------------
@@ -67,7 +79,7 @@ const BottomNavbar = ({navigation, routeName, routeParams}) => {
return ( return (
<View style={[styles.container, {paddingBottom: insets.bottom}]}> <View style={[styles.container, {paddingBottom: insets.bottom}]}>
{tabs.map((tab) => { {visibleTabs.map((tab) => {
const isActive = routeName === tab.route; const isActive = routeName === tab.route;
const isLeave = tab.route === '__leave__'; const isLeave = tab.route === '__leave__';
return ( 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) { if (isBusiness && isAdmin && !selectedDepartment) {
return ( return (
<Screen style={styles.container}> <Screen style={styles.container}>
+42 -4
View File
@@ -10,7 +10,7 @@ import ClaimTimerView from '../gameplay/ClaimTimerView';
import PlayerMapView from '../gameplay/PlayerMapView'; import PlayerMapView from '../gameplay/PlayerMapView';
import useGameHook from '../../hooks/useGameHook'; import useGameHook from '../../hooks/useGameHook';
import usePlayerGame from '../../hooks/usePlayerGame'; 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'; import {getFovCone} from '../../utils/geoMath';
const DEFAULT_REGION = {latitude: 51.5074, longitude: -0.1278, latitudeDelta: 0.05, longitudeDelta: 0.05}; 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 MapScreen = ({navigation, route}) => {
const session = getSession(); const session = getSession();
const isBusiness = Boolean(session.isBusiness); 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 // Stable ref to prevent infinite re-render loops
const getCachesRef = useRef(getCaches); const getCachesRef = useRef(getCaches);
@@ -104,8 +104,9 @@ const MapScreen = ({navigation, route}) => {
return map; return map;
}, [departments]); }, [departments]);
const routeDepartmentSGid = route?.params?.selectedDepartmentSGid; const routeDepartmentSGid = route?.params?.selectedDepartmentSGid;
const isDepartmentScopedMap = routeDepartmentSGid !== null && routeDepartmentSGid !== undefined;
const effectiveSGid = isBusiness const effectiveSGid = isBusiness
? (isAdmin ? (selectedCacheSubgroupId ?? routeDepartmentSGid ?? defaultMemberSGid ?? null) : (session.currentSGid ?? null)) ? (isAdmin ? (isDepartmentScopedMap ? routeDepartmentSGid : null) : (session.currentSGid ?? null))
: null; : null;
useEffect(() => { useEffect(() => {
@@ -199,6 +200,7 @@ const MapScreen = ({navigation, route}) => {
const result = await joinPrivateGame({JoinCode: gameCode.trim().toUpperCase(), Uid: session.currentUid}); const result = await joinPrivateGame({JoinCode: gameCode.trim().toUpperCase(), Uid: session.currentUid});
if (!result) return; if (!result) return;
setSessionGroup(result.Gid, result.SGid); setSessionGroup(result.Gid, result.SGid);
setPendingAdmin(false);
const freshUser = await getUser(session.currentUid); const freshUser = await getUser(session.currentUid);
if (freshUser) { if (freshUser) {
@@ -219,6 +221,7 @@ const MapScreen = ({navigation, route}) => {
MaxMemberSubgroups: 1, MaxMemberSubgroups: 1,
}); });
if (!result) return; if (!result) return;
setPendingAdmin(false);
const freshUser = await getUser(session.currentUid); const freshUser = await getUser(session.currentUid);
if (freshUser) { if (freshUser) {
setSessionUser(freshUser); setSessionUser(freshUser);
@@ -239,6 +242,7 @@ const MapScreen = ({navigation, route}) => {
isBusiness: true, isBusiness: true,
}); });
if (!result) return; if (!result) return;
setPendingAdmin(false);
const freshUser = await getUser(session.currentUid); const freshUser = await getUser(session.currentUid);
if (freshUser) { if (freshUser) {
setSessionUser(freshUser); setSessionUser(freshUser);
@@ -265,8 +269,33 @@ const MapScreen = ({navigation, route}) => {
const handleJoinDepartment = async () => { const handleJoinDepartment = async () => {
setJoinError(''); setJoinError('');
if (!deptCode.trim()) return; 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({ const result = await joinPrivateGame({
JoinCode: deptCode.trim().toUpperCase(), JoinCode: enteredCode,
Uid: session.currentUid, Uid: session.currentUid,
ExpectedGid: matchedGroup.Gid, ExpectedGid: matchedGroup.Gid,
}); });
@@ -275,6 +304,7 @@ const MapScreen = ({navigation, route}) => {
return; return;
} }
setSessionGroup(result.Gid, result.SGid); setSessionGroup(result.Gid, result.SGid);
setPendingAdmin(false);
const freshUser = await getUser(session.currentUid); const freshUser = await getUser(session.currentUid);
if (freshUser) { if (freshUser) {
setSessionUser(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 // Player — loading location
if (playerLoading) { if (playerLoading) {
return ( 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) { if (isBusiness && isAdmin && !selectedDepartment) {
return ( return (
<Screen style={styles.container}> <Screen style={styles.container}>
+59 -2
View File
@@ -4,9 +4,9 @@ import Screen from '../layout/Screen';
import Card from '../UI/Card'; import Card from '../UI/Card';
import {Button, ButtonTray} from '../UI/Button'; import {Button, ButtonTray} from '../UI/Button';
import useGameHook from '../../hooks/useGameHook'; 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 session = getSession();
const isAdmin = session.isAcceptedAdmin; const isAdmin = session.isAcceptedAdmin;
const isBusiness = Boolean(session.isBusiness); const isBusiness = Boolean(session.isBusiness);
@@ -48,6 +48,24 @@ const TeamScreen = () => {
if (subgroup) teamsAreEnabled = Boolean(subgroup.TeamsEnabled); 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) { if (!teamsAreEnabled) {
setTeamsDisabled(true); setTeamsDisabled(true);
setLoading(false); setLoading(false);
@@ -69,6 +87,44 @@ const TeamScreen = () => {
load(); load();
}, [activeTid]); }, [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 () => { const handleJoinTeam = async () => {
if (!teamCode.trim()) return; if (!teamCode.trim()) return;
// Force uppercase just in case a lowercase code is pasted // 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 the server indicates this code matched the admin join code
if (result.adminWaitlist) { if (result.adminWaitlist) {
setPendingAdmin(true);
setOnAdminWaitlist(true); setOnAdminWaitlist(true);
return; return;
} }
+14 -3
View File
@@ -11,6 +11,7 @@ const session = {
currentTid: null, currentTid: null,
isBusiness: null, isBusiness: null,
isAcceptedAdmin: false, isAcceptedAdmin: false,
isPendingAdmin: false,
teamsEnabled: false, teamsEnabled: false,
selectedCacheId: null, selectedCacheId: null,
}; };
@@ -24,13 +25,16 @@ const setSessionUser = (user) => {
session.currentTid = user?.TGid ?? null; session.currentTid = user?.TGid ?? null;
session.isBusiness = user?.isBusiness ?? null; session.isBusiness = user?.isBusiness ?? null;
session.isAcceptedAdmin = Boolean(user?.IsAcceptedAdmin); session.isAcceptedAdmin = Boolean(user?.IsAcceptedAdmin);
session.isPendingAdmin = Boolean(
user?.isBusiness && user?.Gid && !user?.SGid && !user?.IsAcceptedAdmin,
);
persistSession(); persistSession();
}; };
const setSessionGroup = (gid, sgid = null) => { const setSessionGroup = (gid, sgid = null) => {
session.currentGid = gid ?? null; session.currentGid = gid ?? null;
if (sgid !== null && sgid !== undefined) { if (sgid !== undefined) {
session.currentSGid = sgid; session.currentSGid = sgid ?? null;
} }
persistSession(); persistSession();
}; };
@@ -45,6 +49,11 @@ const setSessionTeamsEnabled = (val) => {
persistSession(); persistSession();
}; };
const setPendingAdmin = (val) => {
session.isPendingAdmin = Boolean(val);
persistSession();
};
const setSelectedCache = (cacheId) => { const setSelectedCache = (cacheId) => {
session.selectedCacheId = cacheId ?? null; session.selectedCacheId = cacheId ?? null;
persistSession(); persistSession();
@@ -59,6 +68,7 @@ const clearSession = () => {
session.currentTid = null; session.currentTid = null;
session.isBusiness = null; session.isBusiness = null;
session.isAcceptedAdmin = false; session.isAcceptedAdmin = false;
session.isPendingAdmin = false;
AsyncStorage.removeItem(STORAGE_KEY).catch(() => {}); AsyncStorage.removeItem(STORAGE_KEY).catch(() => {});
}; };
@@ -68,6 +78,7 @@ const clearGameSession = () => {
session.currentSGid = null; session.currentSGid = null;
session.currentTid = null; session.currentTid = null;
session.isAcceptedAdmin = false; session.isAcceptedAdmin = false;
session.isPendingAdmin = false;
session.teamsEnabled = false; session.teamsEnabled = false;
session.selectedCacheId = null; session.selectedCacheId = null;
persistSession(); persistSession();
@@ -92,4 +103,4 @@ const loadSession = async () => {
return { ...session }; 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.JoinCode !== undefined) data.JoinCode = payload.JoinCode;
if (payload.Tid !== undefined) data.Tid = payload.Tid; 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) { if (payload.ExpectedSGid !== undefined && payload.ExpectedSGid !== null) {
data.ExpectedSGid = payload.ExpectedSGid; data.ExpectedSGid = payload.ExpectedSGid;
} }