import React, {useCallback, useEffect, useState} from 'react'; import {ActivityIndicator, Alert, Pressable, ScrollView, StyleSheet, Text, View} from 'react-native'; import Screen from '../layout/Screen'; import Card from '../UI/Card'; import {Button} from '../UI/Button'; import useGameHook from '../../hooks/useGameHook'; import {getSession} from '../../hooks/SessionStore'; const PlayersScreen = ({navigation, route}) => { // Initialisation ------------ const session = getSession(); const {getTeams, getTeamMembers, getGroupMembers, removeMember, resetPlayerProgress, getUser, getAdminWaitlist, approveAdmin, rejectAdmin, getSubgroups} = useGameHook(); const isBusiness = Boolean(session.isBusiness); const isAdmin = Boolean(session.isAcceptedAdmin); const selectedDepartment = route?.params?.department || null; // State ---------------------- const [loading, setLoading] = useState(true); const [players, setPlayers] = useState([]); const [departmentList, setDepartmentList] = useState([]); // Handlers ------------------- const loadData = useCallback(async () => { if (!session.currentGid) { setLoading(false); return; } const effectiveSGid = isBusiness ? ((isAdmin && selectedDepartment?.SGid) ? selectedDepartment.SGid : (isAdmin ? null : session.currentSGid)) : null; if (isBusiness && isAdmin && !selectedDepartment) { const sgs = await getSubgroups(session.currentGid); setDepartmentList((sgs || []).filter((sg) => !sg.IsAdminGroup)); setPlayers([]); setLoading(false); return; } // Load subgroup names for department display (business accounts) let subgroupNameMap = {}; if (isBusiness) { const sgs = await getSubgroups(session.currentGid); for (const sg of (sgs || [])) { if (!sg.IsAdminGroup) subgroupNameMap[sg.SGid] = sg.SubGroupName; } } // Get ALL members (including admins) const allMembers = await getGroupMembers(session.currentGid, effectiveSGid ?? null); // Get admin waitlist entries const waitlist = effectiveSGid ? [] : await getAdminWaitlist(session.currentGid); // Deduplicate allMembers by Uid — a user may have two rows if they were a // regular member before being approved as an admin. Prefer the admin row. const seenUids = new Set(); const uniqueMembers = (allMembers || []) .sort((a, b) => (b.IsAcceptedAdmin ? 1 : 0) - (a.IsAcceptedAdmin ? 1 : 0)) .filter((m) => { if (seenUids.has(m.Uid)) return false; seenUids.add(m.Uid); return true; }); // Build team lookup: { Uid -> { teamCode, teamName, isLeader } } const allTeams = await getTeams(session.currentGid, effectiveSGid ?? null); const teamInfoByUid = {}; for (const team of (allTeams || [])) { const tms = await getTeamMembers(team.Tid); for (const tm of (tms || [])) { teamInfoByUid[tm.Uid] = { teamCode: team.JoinCode, teamName: team.TeamName, isLeader: Boolean(tm.IsLeader), }; } } // Build player list — include admins with tags const playerList = []; for (const m of uniqueMembers) { // Skip the current admin viewing the list if (m.Uid === session.currentUid) continue; const user = await getUser(m.Uid); if (user) { // A group member may also be on the admin waitlist (applied after joining as a player). // Check the waitlist first so they are treated as pending, not a regular player. const waitlistEntry = (waitlist || []).find((w) => w.Uid === m.Uid); let adminTag = null; let waitlistId = null; if (waitlistEntry) { adminTag = '[Admin Awaiting Response]'; waitlistId = waitlistEntry.id; } else if (m.IsAcceptedAdmin) { adminTag = '[ADMIN]'; } playerList.push({ ...user, membershipId: m.id, team: teamInfoByUid[m.Uid] || null, adminTag, waitlistId, departmentName: subgroupNameMap[m.SGid] || null, }); } } // Include waitlisted users who are not yet group members at all for (const w of (waitlist || [])) { if (playerList.some((p) => p.Uid === w.Uid)) continue; if (w.Uid === session.currentUid) continue; const user = await getUser(w.Uid); if (user) { playerList.push({ ...user, membershipId: null, team: null, adminTag: '[Admin Awaiting Response]', waitlistId: w.id, departmentName: null, }); } } setPlayers(playerList); setLoading(false); }, [session.currentGid, session.currentUid, session.currentSGid, isBusiness, isAdmin, selectedDepartment, selectedDepartment?.SGid]); useEffect(() => { loadData(); }, [loadData]); const handleRemovePlayer = async (membershipId) => { await removeMember(membershipId); await loadData(); }; const handleResetPlayerProgress = (player) => { Alert.alert( 'Reset Progress', `Reset all cache claims for ${player.username}?`, [ {text: 'Cancel', style: 'cancel'}, { text: 'Reset', style: 'destructive', onPress: async () => { await resetPlayerProgress(session.currentGid, player.Uid); Alert.alert('Done', `Progress for ${player.username} has been reset.`); }, }, ], ); }; // Approve a waitlisted admin and refresh the list const handleApproveAdmin = async (player) => { await approveAdmin(player.waitlistId); await loadData(); }; // Deny (remove from waitlist) a pending admin and refresh the list const handleDenyAdmin = async (player) => { await rejectAdmin(player.waitlistId); await loadData(); }; // View ----------------------- if (loading) { return ( ); } if (!session.currentGid) { return ( Create or join a game first. ); } if (isBusiness && isAdmin && !selectedDepartment) { return ( Departments {departmentList.map((department) => ( navigation.navigate('PlayersScreen', {department})} > {department.SubGroupName} Open ))} {departmentList.length === 0 && ( No departments yet. )} ); } return ( Players {players.map((player) => ( // Waitlisted admins get a light red tint so they stand out from regular players {player.username} {player.adminTag && ( {' '}{player.adminTag} )} {/* Show department name in brackets for non-admin players (org accounts) */} {!player.adminTag && player.departmentName && ( {' '}({player.departmentName}) )} {player.team?.isLeader && ( (Team Leader) )} {player.team && ( {player.team.teamCode} )} {/* Waitlisted admins get Approve / Deny instead of the normal player actions */} {player.waitlistId ? ( <>