Refactor GameSettingsScreen: enhance data fetching with useRef for API calls, improve loading state management, and streamline subgroup and waitlist handling.

This commit is contained in:
bobbert committed 2026-04-09 13:47:05 +01:00
1 parent 4a0468d155
commit 188c581f37
1 file changed
+68 -36
+68 -36
View File
@@ -1,4 +1,4 @@
import React, {useCallback, useState} from 'react'; import React, {useCallback, useEffect, useRef, useState} from 'react';
import {ActivityIndicator, Alert, ScrollView, StyleSheet, Switch, Text, TextInput, View} from 'react-native'; import {ActivityIndicator, Alert, ScrollView, StyleSheet, Switch, Text, TextInput, View} from 'react-native';
import {useFocusEffect} from '@react-navigation/native'; import {useFocusEffect} from '@react-navigation/native';
import Screen from '../layout/Screen'; import Screen from '../layout/Screen';
@@ -12,6 +12,21 @@ const GameSettingsScreen = ({navigation}) => {
const session = getSession(); const session = getSession();
const {getLobby, updateGroup, getSubgroups, createSubgroup, deleteSubgroup, resetGame, getAdminWaitlist, approveAdmin, rejectAdmin, getUser, disbandTeams} = useGameHook(); const {getLobby, updateGroup, getSubgroups, createSubgroup, deleteSubgroup, resetGame, getAdminWaitlist, approveAdmin, rejectAdmin, getUser, disbandTeams} = useGameHook();
const gameApiRef = useRef({
getLobby,
getSubgroups,
getAdminWaitlist,
getUser,
});
gameApiRef.current = {
getLobby,
getSubgroups,
getAdminWaitlist,
getUser,
};
const isMountedRef = useRef(true);
const isRefreshingRef = useRef(false);
const hasHydratedRef = useRef(false);
// State ---------------------- // State ----------------------
@@ -34,56 +49,73 @@ const GameSettingsScreen = ({navigation}) => {
// Handlers ------------------- // Handlers -------------------
const loadWaitlist = useCallback(async (gid) => { useEffect(() => () => {
if (!gid) return; isMountedRef.current = false;
const rows = await getAdminWaitlist(gid); }, []);
setWaitlist(rows || []);
const names = {};
for (const entry of (rows || [])) {
const u = await getUser(entry.Uid);
if (u) names[entry.Uid] = u.username;
}
setWaitlistNames(names);
}, [getAdminWaitlist, getUser]);
const loadSubgroups = useCallback(async (gid) => { const refreshSettings = useCallback(async ({showLoader = false} = {}) => {
if (!gid) return;
const sgs = await getSubgroups(gid);
setSubgroupList(sgs || []);
// Set the default member join code from first non-admin subgroup
const memberSg = (sgs || []).find((sg) => !sg.IsAdminGroup);
if (memberSg) setMemberJoinCode(memberSg.JoinCode || '');
}, [getSubgroups]);
const loadSettings = useCallback(async () => {
const activeSession = getSession(); const activeSession = getSession();
if (!activeSession.currentGid) { if (!activeSession.currentGid) {
setLoading(false); if (isMountedRef.current) setLoading(false);
return; return;
} }
const group = await getLobby(activeSession.currentGid);
// Guard against re-entrant focus refresh calls.
if (isRefreshingRef.current) return;
isRefreshingRef.current = true;
if (showLoader && isMountedRef.current) setLoading(true);
try {
const [group, sgs, waitlistRows] = await Promise.all([
gameApiRef.current.getLobby(activeSession.currentGid),
gameApiRef.current.getSubgroups(activeSession.currentGid),
gameApiRef.current.getAdminWaitlist(activeSession.currentGid),
]);
if (!isMountedRef.current) return;
if (group) { if (group) {
setGroupName(group.GroupName || ''); setGroupName(group.GroupName || '');
setBusinessName(group.BusinessOrSchoolName || ''); setBusinessName(group.BusinessOrSchoolName || '');
setIsBusinessGroup(Boolean(group.BusinessOrSchoolName || group.OrgJoinCode)); // Organisation mode is based on organisation profile data, not generic org join code presence.
setIsBusinessGroup(Boolean(group.BusinessOrSchoolName));
const serverTeams = Boolean(group.TeamsEnabled); const serverTeams = Boolean(group.TeamsEnabled);
setTeamsEnabled(serverTeams); setTeamsEnabled(serverTeams);
setServerTeamsEnabled(serverTeams); setServerTeamsEnabled(serverTeams);
setSessionTeamsEnabled(serverTeams); setSessionTeamsEnabled(serverTeams);
setCacheTriggerMeters(String(group.CacheTriggerMeters || 20)); setCacheTriggerMeters(String(group.CacheTriggerMeters || 20));
setAdminJoinCode(group.AdminJoinCode || ''); setAdminJoinCode(group.AdminJoinCode || '');
setOrgJoinCode(group.OrgJoinCode || ''); setOrgJoinCode(group.OrgJoinCode || '');
} }
await loadSubgroups(activeSession.currentGid);
await loadWaitlist(activeSession.currentGid); setSubgroupList(sgs || []);
const memberSg = (sgs || []).find((sg) => !sg.IsAdminGroup);
setMemberJoinCode(memberSg?.JoinCode || '');
setWaitlist(waitlistRows || []);
const names = {};
for (const entry of (waitlistRows || [])) {
const user = await gameApiRef.current.getUser(entry.Uid);
if (user) names[entry.Uid] = user.username;
}
if (!isMountedRef.current) return;
setWaitlistNames(names);
hasHydratedRef.current = true;
} finally {
if (isMountedRef.current && (showLoader || !hasHydratedRef.current)) {
setLoading(false); setLoading(false);
}, [getLobby, loadSubgroups, loadWaitlist]); }
isRefreshingRef.current = false;
}
}, []);
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
setLoading(true); refreshSettings({showLoader: !hasHydratedRef.current});
loadSettings(); }, [refreshSettings]),
}, [loadSettings]),
); );
// --- Individual save --- // --- Individual save ---
@@ -147,12 +179,12 @@ const GameSettingsScreen = ({navigation}) => {
const handleApproveAdmin = async (entry) => { const handleApproveAdmin = async (entry) => {
await approveAdmin(entry.id); await approveAdmin(entry.id);
await loadWaitlist(); await refreshSettings();
}; };
const handleRejectAdmin = async (entry) => { const handleRejectAdmin = async (entry) => {
await rejectAdmin(entry.id); await rejectAdmin(entry.id);
await loadWaitlist(); await refreshSettings();
}; };
// Business — create a new department (subgroup) // Business — create a new department (subgroup)
@@ -167,7 +199,7 @@ const GameSettingsScreen = ({navigation}) => {
CacheTriggerMeters: 20, CacheTriggerMeters: 20,
}); });
setNewDeptName(''); setNewDeptName('');
await loadSubgroups(); await refreshSettings();
}; };
// Business — delete a department // Business — delete a department
@@ -179,7 +211,7 @@ const GameSettingsScreen = ({navigation}) => {
style: 'destructive', style: 'destructive',
onPress: async () => { onPress: async () => {
await deleteSubgroup(sg.SGid); await deleteSubgroup(sg.SGid);
await loadSubgroups(); await refreshSettings();
}, },
}, },
]); ]);
@@ -393,7 +425,7 @@ const GameSettingsScreen = ({navigation}) => {
</View> </View>
<View style={styles.codeSection}> <View style={styles.codeSection}>
<Text style={styles.codeLabel}>Player Join Code</Text> <Text style={styles.codeLabel}>Game Join Code</Text>
<Text style={styles.codeValue}>{memberJoinCode || '—'}</Text> <Text style={styles.codeValue}>{memberJoinCode || '—'}</Text>
</View> </View>