fixed issues with merge + Global logic

This commit is contained in:
bobbert committed 2026-04-09 18:18:18 +01:00
1 parent b9b11463cc
commit 20a2edbbda
18 files changed
+459 -419

No files matched your search

+11 -25
View File
@@ -1,8 +1,8 @@
import { useEffect, useState } from "react";
import { ActivityIndicator, View } from "react-native";
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { SafeAreaProvider } from "react-native-safe-area-context";
import {useEffect, useState} from "react";
import {ActivityIndicator, View} from "react-native";
import {NavigationContainer} from "@react-navigation/native";
import {createNativeStackNavigator} from "@react-navigation/native-stack";
import {SafeAreaProvider} from "react-native-safe-area-context";
import LoginScreen from "./src/components/screens/LoginScreen";
import RegisterScreen from "./src/components/screens/RegisterScreen";
import TeamScreen from "./src/components/screens/TeamScreen";
@@ -12,12 +12,11 @@ import GameSettingsScreen from "./src/components/screens/GameSettingsScreen";
import DepartmentSettingsScreen from "./src/components/screens/DepartmentSettingsScreen";
import PlayersScreen from "./src/components/screens/PlayersScreen";
import ExpandedMapScreen from "./src/components/screens/ExpandedMapScreen";
import GameSelectionScreen from "./src/components/screens/GameSelectionScreen";
import GlobalEventsScreen from "./src/components/screens/global/GlobalEventsScreen";
import GlobalMapScreen from "./src/components/screens/global/GlobalMapScreen";
import GlobalCacheViewScreen from "./src/components/screens/global/GlobalCacheViewScreen";
import GlobalLeaderboardScreen from "./src/components/screens/global/GlobalLeaderboardScreen";
import { loadSession } from "./src/hooks/SessionStore";
import {loadSession} from "./src/hooks/SessionStore";
const RootStack = createNativeStackNavigator();
const AuthStackNav = createNativeStackNavigator();
@@ -26,7 +25,7 @@ const AdminStackNav = createNativeStackNavigator();
const screenOptions = {
headerShown: false,
contentStyle: { backgroundColor: "#ffffff" },
contentStyle: { backgroundColor: "#1e1e2e" },
};
const AuthStack = () => (
@@ -40,10 +39,6 @@ const AuthStack = () => (
const GameStack = () => (
<GameStackNav.Navigator screenOptions={screenOptions}>
<GameStackNav.Screen
name="GameSelectionScreen"
component={GameSelectionScreen}
/>
<GameStackNav.Screen name="MapScreen" component={MapScreen} />
<GameStackNav.Screen name="TeamScreen" component={TeamScreen} />
<GameStackNav.Screen
@@ -73,14 +68,14 @@ const GameStack = () => (
const AdminStack = () => (
<AdminStackNav.Navigator screenOptions={screenOptions}>
<AdminStackNav.Screen
name="GameSelectionScreen"
component={GameSelectionScreen}
/>
<AdminStackNav.Screen
name="GameSettingsScreen"
component={GameSettingsScreen}
/>
<AdminStackNav.Screen
name="DepartmentSettingsScreen"
component={DepartmentSettingsScreen}
/>
<AdminStackNav.Screen name="MapScreen" component={MapScreen} />
<AdminStackNav.Screen
name="LeaderboardScreen"
@@ -106,15 +101,6 @@ const AdminStack = () => (
component={GlobalLeaderboardScreen}
/>
</AdminStackNav.Navigator>
<AdminStackNav.Navigator screenOptions={screenOptions}>
<AdminStackNav.Screen name="GameSettingsScreen" component={GameSettingsScreen}/>
<AdminStackNav.Screen name="DepartmentSettingsScreen" component={DepartmentSettingsScreen}/>
<AdminStackNav.Screen name="MapScreen" component={MapScreen}/>
<AdminStackNav.Screen name="LeaderboardScreen" component={LeaderboardScreen}/>
<AdminStackNav.Screen name="PlayersScreen" component={PlayersScreen}/>
<AdminStackNav.Screen name="ExpandedMapScreen" component={ExpandedMapScreen}/>
<AdminStackNav.Screen name="TeamScreen" component={TeamScreen}/>
</AdminStackNav.Navigator>
);
export const App = () => {
Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

+1
View File
@@ -36,6 +36,7 @@ const styles = StyleSheet.create({
flex: 1,
flexDirection: "row",
gap: 5,
marginHorizontal: 4,
},
label: {
fontSize: 16,
+75
View File
@@ -0,0 +1,75 @@
import {StyleSheet, TextInput, View} from 'react-native';
import {Button} from './Button';
const CodeJoinRow = ({
value,
onChange,
placeholder,
buttonLabel = 'Join',
onSubmit,
disabled = false,
style,
}) => {
// Initialisation -------------
// State ----------------------
// Handlers -------------------
// View -----------------------
return (
<View style={[styles.inputRow, style]}>
<TextInput
style={[styles.codeInput, disabled && styles.inputDisabled]}
placeholder={placeholder}
placeholderTextColor="#9ca3af"
value={value}
onChangeText={(text) => onChange(text.toUpperCase())}
autoCapitalize="characters"
editable={!disabled}
/>
<Button
label={buttonLabel}
onClick={onSubmit}
styleButton={styles.joinButton}
styleLabel={styles.joinLabel}
disabled={disabled}
/>
</View>
);
};
const styles = StyleSheet.create({
inputRow: {
flexDirection: 'row',
gap: 10,
marginBottom: 15,
width: '100%',
paddingHorizontal: 20,
},
codeInput: {
flex: 1,
borderWidth: 1,
borderColor: '#45475a',
borderRadius: 8,
paddingHorizontal: 12,
paddingVertical: 10,
fontSize: 16,
color: '#cdd6f4',
backgroundColor: '#313244',
},
inputDisabled: {
opacity: 0.5,
},
joinButton: {
backgroundColor: '#bd93f9',
borderColor: '#bd93f9',
flex: 0,
paddingHorizontal: 20,
},
joinLabel: {
color: '#1e1e2e',
fontWeight: '600',
},
});
export default CodeJoinRow;
+120 -233
View File
@@ -1,191 +1,130 @@
import { Alert, Pressable, StyleSheet, Text, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { clearGameSession, getSession } from "../../hooks/SessionStore";
import API, { API_BASE_URL } from "../API/API";
import {Alert, Image, Pressable, StyleSheet, Text, View} from 'react-native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {clearGameSession, clearGlobalSession, getSession, setSessionMode} from '../../hooks/SessionStore';
import API, {API_BASE_URL} from '../API/API';
import {GAME_MODE} from '../../utils/gameConstants';
const iconMap = {
Settings: require('../../../assets/icons/settings-icon.png'),
Events: require('../../../assets/icons/events-icon.png'),
Map: require('../../../assets/icons/Map-icon.png'),
Leaderboard: require('../../../assets/icons/Leaderboard-icon.png'),
Team: require('../../../assets/icons/Teams-icon.png'),
Players: require('../../../assets/icons/Teams-icon.png'),
};
const BottomNavbar = ({ navigation, routeName }) => {
// Initialisation ------------
const BottomNavbar = ({navigation, routeName, routeParams}) => {
// Initialisation ------------
// Initialisation ------------
const insets = useSafeAreaInsets();
const session = getSession();
const inGame = Boolean(session.currentGid);
const isGlobalMode = !session.isBusiness && session.currentGameMode === GAME_MODE.GLOBAL;
const adminTabs = [
{
label: "Settings",
route: "GameSettingsScreen",
onPress: () => navigation.navigate("GameSettingsScreen"),
},
{
label: "Map",
route: "MapScreen",
onPress: () => navigation.navigate("MapScreen"),
},
{
label: "Players",
route: "PlayersScreen",
onPress: () => navigation.navigate("PlayersScreen"),
},
{
label: "Leaderboard",
route: "LeaderboardScreen",
onPress: () => navigation.navigate("LeaderboardScreen"),
},
{
label: "Games",
route: "GameSelectionScreen",
onPress: () => navigation.navigate("GameSelectionScreen"),
},
{
label: "Global",
route: "GlobalEventsScreen",
onPress: () => navigation.navigate("GlobalEventsScreen"),
},
];
const playerTabs = [
{
label: "Team",
route: "TeamScreen",
onPress: () => navigation.navigate("TeamScreen"),
},
{
label: "Map",
route: "MapScreen",
onPress: () => navigation.navigate("MapScreen"),
},
{
label: "Leaderboard",
route: "LeaderboardScreen",
onPress: () => navigation.navigate("LeaderboardScreen"),
},
{
label: "Games",
route: "GameSelectionScreen",
onPress: () => navigation.navigate("GameSelectionScreen"),
},
{
label: "Global",
route: "GlobalEventsScreen",
onPress: () => navigation.navigate("GlobalEventsScreen"),
},
{ label: "Leave", route: "__leave__", onPress: () => handleLeaveGame() },
];
const tabs = session.isAcceptedAdmin ? adminTabs : playerTabs;
const selectedDepartment = routeName === 'DepartmentSettingsScreen' && routeParams?.SGid
? {SGid: routeParams.SGid, SubGroupName: routeParams.SubGroupName || routeParams.selectedDepartmentName || 'Department'}
: null;
const adminTabs = [
{label: 'Settings', route: 'GameSettingsScreen', onPress: () => navigation.navigate('GameSettingsScreen')},
{
label: 'Map',
route: 'MapScreen',
onPress: () => navigation.navigate('MapScreen', {
selectedDepartmentSGid: null,
selectedDepartmentName: null,
}),
},
{
label: 'Players',
route: 'PlayersScreen',
onPress: () => navigation.navigate('PlayersScreen', selectedDepartment ? {department: selectedDepartment} : undefined),
},
{
label: 'Leaderboard',
route: 'LeaderboardScreen',
onPress: () => navigation.navigate('LeaderboardScreen', selectedDepartment ? {department: selectedDepartment} : undefined),
},
];
// Only show the Leave tab when the player is actually in a game
const inGame = Boolean(session.currentGid);
const playerTabs = [
{label: 'Team', route: 'TeamScreen', onPress: () => navigation.navigate('TeamScreen')},
{label: 'Map', route: 'MapScreen', onPress: () => navigation.navigate('MapScreen')},
{label: 'Leaderboard', route: 'LeaderboardScreen', onPress: () => navigation.navigate('LeaderboardScreen')},
...(inGame ? [{label: 'Leave', route: '__leave__', onPress: () => handleLeaveGame()}] : []),
];
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;
const selectedDepartment = routeName === 'DepartmentSettingsScreen' && routeParams?.SGid
? {
SGid: routeParams.SGid,
SubGroupName: routeParams.SubGroupName || routeParams.selectedDepartmentName || 'Department',
}
: null;
// State ----------------------
// Handlers -------------------
const handleLeaveGame = () => {
Alert.alert("Leave Game", "Are you sure you want to leave this game?", [
{ text: "Cancel", style: "cancel" },
Alert.alert('Leave Game', 'Are you sure you want to leave this game?', [
{text: 'Cancel', style: 'cancel'},
{
text: "Leave",
style: "destructive",
text: 'Leave',
style: 'destructive',
onPress: async () => {
const s = getSession();
const endpoint = `${API_BASE_URL}/subgroup-memberships?Uid=${s.currentUid}&Gid=${s.currentGid}`;
const res = await API.get(endpoint);
if (res.isSuccess && res.result.length > 0) {
await API.delete(
`${API_BASE_URL}/subgroup-memberships/${res.result[0].id}`,
);
const currentSession = getSession();
const endpoint = `${API_BASE_URL}/subgroup-members?Uid=${currentSession.currentUid}&Gid=${currentSession.currentGid}`;
const response = await API.get(endpoint);
if (response.isSuccess && response.result.length > 0) {
await API.delete(`${API_BASE_URL}/subgroup-members/${response.result[0].id}`);
}
clearGameSession();
navigation.reset({ index: 0, routes: [{ name: "Game" }] });
const rootNav = navigation.getParent() || navigation;
rootNav.reset({index: 0, routes: [{name: 'Game'}]});
},
},
]);
};
const handleLeaveGame = () => {
Alert.alert('Leave Game', 'Are you sure you want to leave this game?', [
{text: 'Cancel', style: 'cancel'},
{
text: 'Leave',
style: 'destructive',
onPress: async () => {
const s = getSession();
const endpoint = `${API_BASE_URL}/subgroup-members?Uid=${s.currentUid}&Gid=${s.currentGid}`;
const res = await API.get(endpoint);
if (res.isSuccess && res.result.length > 0) {
await API.delete(`${API_BASE_URL}/subgroup-members/${res.result[0].id}`);
}
clearGameSession();
navigation.reset({index: 0, routes: [{name: 'Game'}]});
},
},
]);
};
const handleExitGlobal = () => {
clearGlobalSession();
setSessionMode(session.currentGid ? GAME_MODE.REGULAR : null);
navigation.navigate('MapScreen');
};
const adminTabs = [
{label: 'Settings', route: 'GameSettingsScreen', onPress: () => navigation.navigate('GameSettingsScreen')},
{
label: 'Map',
route: 'MapScreen',
onPress: () => navigation.navigate('MapScreen', {
selectedDepartmentSGid: null,
selectedDepartmentName: null,
}),
},
{
label: 'Players',
route: 'PlayersScreen',
onPress: () => navigation.navigate('PlayersScreen', selectedDepartment ? {department: selectedDepartment} : undefined),
},
{
label: 'Leaderboard',
route: 'LeaderboardScreen',
onPress: () => navigation.navigate('LeaderboardScreen', selectedDepartment ? {department: selectedDepartment} : undefined),
},
];
const playerTabs = [
{label: 'Team', route: 'TeamScreen', onPress: () => navigation.navigate('TeamScreen')},
{label: 'Map', route: 'MapScreen', onPress: () => navigation.navigate('MapScreen')},
{label: 'Leaderboard', route: 'LeaderboardScreen', onPress: () => navigation.navigate('LeaderboardScreen')},
...(inGame ? [{label: 'Leave', route: '__leave__', onPress: handleLeaveGame}] : []),
];
const pendingTabs = [
{label: 'Pending', route: 'TeamScreen', onPress: () => navigation.navigate('TeamScreen')},
...(inGame ? [{label: 'Leave', route: '__leave__', onPress: handleLeaveGame}] : []),
];
const globalTabs = [
{label: 'Events', route: 'GlobalEventsScreen', onPress: () => navigation.navigate('GlobalEventsScreen')},
{label: 'Map', route: 'GlobalMapScreen', onPress: () => navigation.navigate('GlobalMapScreen')},
{label: 'Leaderboard', route: 'GlobalLeaderboardScreen', onPress: () => navigation.navigate('GlobalLeaderboardScreen')},
{label: 'Exit', route: '__exit_global__', onPress: handleExitGlobal},
];
let visibleTabs = session.isAcceptedAdmin ? adminTabs : playerTabs;
if (session.isPendingAdmin && !session.isAcceptedAdmin) {
visibleTabs = pendingTabs;
}
if (isGlobalMode) {
visibleTabs = globalTabs;
}
// View -----------------------
return (
<View style={[styles.container, { paddingBottom: insets.bottom }]}>
{tabs.map((tab) => {
<View style={[styles.container, {paddingBottom: insets.bottom}]}>
{visibleTabs.map((tab) => {
const isActive = routeName === tab.route;
const isLeave = tab.route === "__leave__";
const isLeave = tab.route === '__leave__';
const isExitGlobal = tab.route === '__exit_global__';
const iconSource = iconMap[tab.label];
return (
<Pressable
key={tab.label}
onPress={tab.onPress}
style={[
styles.tab,
isActive && styles.tabActive,
isLeave && styles.tabLeave,
]}
style={[styles.tab, isActive && styles.tabActive, (isLeave || isExitGlobal) && styles.tabLeave]}
>
<Text
style={[
styles.tabText,
isActive && styles.tabTextActive,
isLeave && styles.tabTextLeave,
]}
>
{iconSource ? <Image source={iconSource} style={styles.icon} resizeMode="contain" /> : null}
<Text style={[styles.tabText, isActive && styles.tabTextActive, (isLeave || isExitGlobal) && styles.tabTextLeave]}>
{tab.label}
</Text>
</Pressable>
@@ -199,97 +138,45 @@ const styles = StyleSheet.create({
container: {
minHeight: 64,
borderTopWidth: 1,
borderTopColor: "#555555",
backgroundColor: "#4a4a4a",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-around",
borderTopColor: '#45475a',
backgroundColor: '#313244',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
paddingHorizontal: 8,
zIndex: 10,
},
tab: {
flex: 1,
alignItems: "center",
justifyContent: "center",
paddingVertical: 12,
borderRadius: 8,
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 8,
marginHorizontal: 4,
gap: 4,
},
tabActive: {
backgroundColor: "#5c5c5c",
backgroundColor: '#45475a',
},
tabLeave: {
backgroundColor: "rgba(220,38,38,0.15)",
backgroundColor: 'rgba(217, 40, 0, 0.15)',
},
icon: {
width: 16,
height: 16,
opacity: 0.95,
},
tabText: {
color: "#d1d5db",
color: '#bac2de',
fontSize: 12,
fontWeight: "600",
textAlign: "center",
fontWeight: '600',
textAlign: 'center',
},
tabTextActive: {
color: "#ffffff",
color: '#cdd6f4',
},
tabTextLeave: {
color: "#fca5a5",
color: '#ffffff',
},
return (
<View style={[styles.container, {paddingBottom: insets.bottom}]}>
{visibleTabs.map((tab) => {
const isActive = routeName === tab.route;
const isLeave = tab.route === '__leave__';
return (
<Pressable
key={tab.label}
onPress={tab.onPress}
style={[styles.tab, isActive && styles.tabActive, isLeave && styles.tabLeave]}
>
<Text style={[styles.tabText, isActive && styles.tabTextActive, isLeave && styles.tabTextLeave]}>
{tab.label}
</Text>
</Pressable>
);
})}
</View>
);
};
const styles = StyleSheet.create({
container: {
minHeight: 64,
borderTopWidth: 1,
borderTopColor: '#45475a',
backgroundColor: '#313244',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
paddingHorizontal: 8,
zIndex: 10,
},
tab: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 12,
marginHorizontal: 4,
},
tabActive: {
backgroundColor: '#45475a',
},
tabLeave: {
backgroundColor: 'rgba(243, 139, 168, 0.15)',
},
tabText: {
color: '#bac2de',
fontSize: 12,
fontWeight: '600',
textAlign: 'center',
},
tabTextActive: {
color: '#cdd6f4',
},
tabTextLeave: {
color: '#D92800',
},
});
export default BottomNavbar;
+26 -24
View File
@@ -1,11 +1,11 @@
import { StatusBar } from "expo-status-bar";
import { StyleSheet, View } from "react-native";
import { useNavigation, useRoute } from "@react-navigation/native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import TopNavbar from "./TopNavbar";
import BottomNavbar from "./BottomNavbar";
import {StatusBar} from 'expo-status-bar';
import {StyleSheet, View} from 'react-native';
import {useNavigation, useRoute} from '@react-navigation/native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import TopNavbar from './TopNavbar';
import BottomNavbar from './BottomNavbar';
const Screen = ({ children, style, showBack = false }) => {
const Screen = ({children, style, showBack = false}) => {
// Initialisation -------------
const navigation = useNavigation();
@@ -13,26 +13,25 @@ const Screen = ({ children, style, showBack = false }) => {
const insets = useSafeAreaInsets();
const titleMap = {
LeaderboardScreen: "Leaderboard",
TeamScreen: "Current Team",
MapScreen: "Map",
GameSettingsScreen: "Game Settings",
GameSelectionScreen: "Games",
DepartmentSettingsScreen: "Department Settings",
PlayersScreen: "Players",
ExpandedMapScreen: "Map View",
GlobalEventsScreen: "Global Events",
GlobalMapScreen: "Global Map",
GlobalCacheViewScreen: "Cache Details",
GlobalLeaderboardScreen: "Global Leaderboard",
LeaderboardScreen: 'Leaderboard',
TeamScreen: 'Current Team',
MapScreen: 'Map',
GameSettingsScreen: 'Game Settings',
GameSelectionScreen: 'Games',
DepartmentSettingsScreen: 'Department Settings',
PlayersScreen: 'Players',
ExpandedMapScreen: 'Map View',
GlobalEventsScreen: 'Global Events',
GlobalMapScreen: 'Global Map',
GlobalCacheViewScreen: 'Cache Details',
GlobalLeaderboardScreen: 'Global Leaderboard',
};
// State ----------------------
const routeName = route.name;
const isAuthScreen =
routeName === "LoginScreen" || routeName === "RegisterScreen";
const pageTitle = titleMap[routeName] || "Treasure Hunt";
const isAuthScreen = routeName === 'LoginScreen' || routeName === 'RegisterScreen';
const pageTitle = titleMap[routeName] || 'Treasure Hunt';
// Handlers -------------------
// View -----------------------
@@ -56,9 +55,12 @@ const Screen = ({ children, style, showBack = false }) => {
)}
<View style={[styles.content, style]}>{children}</View>
{!isAuthScreen && (
<BottomNavbar navigation={navigation} routeName={routeName} />
<BottomNavbar
navigation={navigation}
routeName={routeName}
routeParams={route.params}
/>
)}
{!isAuthScreen && <BottomNavbar navigation={navigation} routeName={routeName} routeParams={route.params} />}
<StatusBar style="light" />
</View>
);
+2 -1
View File
@@ -16,7 +16,8 @@ const TopNavbar = ({title, showBack, navigation}) => {
const handleLogout = () => {
clearSession();
navigation.reset({
const rootNav = navigation.getParent() || navigation;
rootNav.reset({
index: 0,
routes: [{name: 'Auth', params: {screen: 'LoginScreen'}}],
});
+4 -25
View File
@@ -1,8 +1,8 @@
import { ScrollView, StyleSheet, Text, View } from "react-native";
import {ScrollView, StyleSheet, Text, View} from "react-native";
import Screen from "../layout/Screen";
import Card from "../UI/Card";
import { Button, ButtonTray } from "../UI/Button";
import { getSession } from "../../hooks/SessionStore";
import {Button, ButtonTray} from "../UI/Button";
import {getSession} from "../../hooks/SessionStore";
const GameSelectionScreen = ({ navigation }) => {
// Initialisations ---------------------
@@ -34,10 +34,6 @@ const GameSelectionScreen = ({ navigation }) => {
navigation.navigate("TeamScreen");
};
const handleExplore = () => {
navigation.navigate("GlobalEventsScreen");
};
// View --------------------------------
return (
@@ -77,16 +73,8 @@ const GameSelectionScreen = ({ navigation }) => {
<View style={styles.sectionBlock}>
<Text style={styles.blockTitle}>Global Mode</Text>
<Text style={styles.supportText}>
Explore all visible caches and live activity from the map.
Individual users can enter GLOBAL in the game join field to switch to Global mode.
</Text>
<ButtonTray>
<Button
label="Explore"
onClick={handleExplore}
styleButton={styles.neutralButton}
styleLabel={styles.neutralButtonLabel}
/>
</ButtonTray>
</View>
</ScrollView>
</Screen>
@@ -170,15 +158,6 @@ const styles = StyleSheet.create({
fontWeight: "600",
fontSize: 14,
},
neutralButton: {
backgroundColor: "#374151",
borderColor: "#374151",
maxWidth: 180,
},
neutralButtonLabel: {
color: "#ffffff",
fontWeight: "600",
},
});
export default GameSelectionScreen;
+4 -3
View File
@@ -15,11 +15,12 @@ const LoginScreen = ({ navigation }) => {
if (!user) {
return;
}
const rootNav = navigation.getParent() || navigation;
if (user.IsAcceptedAdmin) {
navigation.reset({ index: 0, routes: [{ name: 'Admin' }] });
rootNav.reset({ index: 0, routes: [{ name: 'Admin' }] });
return;
}
navigation.reset({ index: 0, routes: [{ name: 'Game' }] });
rootNav.reset({ index: 0, routes: [{ name: 'Game' }] });
};
return (
@@ -58,7 +59,7 @@ const LoginScreen = ({ navigation }) => {
/>
</ButtonTray>
<Pressable onPress={() => navigation.navigate('Auth', { screen: 'RegisterScreen' })}>
<Pressable onPress={() => navigation.navigate('RegisterScreen')}>
<Text style={styles.linkText}>Create new account</Text>
</Pressable>
</View>
+113 -69
View File
@@ -5,14 +5,25 @@ import * as Location from 'expo-location';
import {useFocusEffect} from '@react-navigation/native';
import Screen from '../layout/Screen';
import {Button, ButtonTray} from '../UI/Button';
import CodeJoinRow from '../UI/CodeJoinRow';
import PendingApprovalView from '../UI/PendingApprovalView';
import CacheCardItem from '../gameplay/CacheCardItem';
import ClaimTimerView from '../gameplay/ClaimTimerView';
import PlayerMapView from '../gameplay/PlayerMapView';
import Card from '../UI/Card';
import useGameHook from '../../hooks/useGameHook';
import usePlayerGame from '../../hooks/usePlayerGame';
import {getSession, setPendingAdmin, setSelectedCache, setSessionGroup, setSessionTeam, setSessionUser} from '../../hooks/SessionStore';
import {
getSession,
setPendingAdmin,
setSelectedCache,
setSessionGroup,
setSessionMode,
setSessionTeam,
setSessionUser
} from '../../hooks/SessionStore';
import {getFovCone} from '../../utils/geoMath';
import {GAME_MODE} from '../../utils/gameConstants';
const DEFAULT_REGION = {latitude: 51.5074, longitude: -0.1278, latitudeDelta: 0.05, longitudeDelta: 0.05};
@@ -20,11 +31,17 @@ 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, joinTeamByCode, createPrivateGame, getLobby, getUser, getSubgroups, getGroupByOrgCode} = useGameHook();
const {getCaches, claimCache, upsertCache, deleteCache, joinPrivateGame, joinTeamByCode, createPrivateGame, getGroups, getLobby, getUser, getSubgroups, getGroupByOrgCode} = useGameHook();
// Stable ref to prevent infinite re-render loops
const getCachesRef = useRef(getCaches);
getCachesRef.current = getCaches;
const getLobbyRef = useRef(getLobby);
getLobbyRef.current = getLobby;
const getSubgroupsRef = useRef(getSubgroups);
getSubgroupsRef.current = getSubgroups;
const getGroupsRef = useRef(getGroups);
getGroupsRef.current = getGroups;
const lastAppliedRouteDepartmentRef = useRef(null);
const [inGame, setInGame] = useState(Boolean(session.currentGid));
@@ -39,6 +56,8 @@ const MapScreen = ({navigation, route}) => {
const [selectedCacheId, setSelectedCacheIdState] = useState(session.selectedCacheId);
const [claimedPopupVisible, setClaimedPopupVisible] = useState(false);
const [subgroups, setSubgroups] = useState([]);
const [availableGames, setAvailableGames] = useState([]);
const [gamesLoading, setGamesLoading] = useState(false);
// Business join flow state
const [joinStep, setJoinStep] = useState(null);
@@ -87,9 +106,26 @@ const MapScreen = ({navigation, route}) => {
useEffect(() => {
if (!session.currentGid) return;
getLobby(session.currentGid).then(setGroupInfo);
getSubgroups(session.currentGid).then(setSubgroups);
}, [inGame]);
getLobbyRef.current(session.currentGid).then(setGroupInfo);
getSubgroupsRef.current(session.currentGid).then(setSubgroups);
}, [inGame, session.currentGid]);
useEffect(() => {
const loadAvailableGames = async () => {
if (inGame || session.isBusiness) {
setAvailableGames([]);
return;
}
setGamesLoading(true);
const groups = await getGroupsRef.current();
const individualGames = (groups || []).filter((group) => !group.BusinessOrSchoolName);
setAvailableGames(individualGames);
setGamesLoading(false);
};
loadAvailableGames();
}, [inGame, session.isBusiness]);
// Default member subgroup SGid for cache creation (first non-admin subgroup)
const departments = useMemo(
@@ -153,7 +189,7 @@ const MapScreen = ({navigation, route}) => {
const firstId = activeCachesForPlayer[0].id;
setSelectedCacheIdState(firstId);
setSelectedCache(firstId);
}, [activeCachesForPlayer, isPlayer]);
}, [activeCachesForPlayer, isPlayer, selectedCacheId]);
// Location tracking
useEffect(() => {
@@ -198,9 +234,12 @@ const MapScreen = ({navigation, route}) => {
const handleJoinGame = async () => {
if (!gameCode.trim()) return;
const result = await joinPrivateGame({JoinCode: gameCode.trim().toUpperCase(), Uid: session.currentUid});
const code = gameCode.trim().toUpperCase();
const result = await joinPrivateGame({JoinCode: code, Uid: session.currentUid});
if (!result) return;
setSessionGroup(result.Gid, result.SGid);
setSessionMode(GAME_MODE.REGULAR);
setPendingAdmin(false);
const freshUser = await getUser(session.currentUid);
@@ -214,6 +253,11 @@ const MapScreen = ({navigation, route}) => {
navigation.navigate('TeamScreen');
};
const handleEnterGlobal = () => {
setSessionMode(GAME_MODE.GLOBAL);
navigation.navigate('GlobalEventsScreen');
};
const handleCreateGame = async () => {
const result = await createPrivateGame({
GroupName: 'My Game',
@@ -222,6 +266,7 @@ const MapScreen = ({navigation, route}) => {
MaxMemberSubgroups: 1,
});
if (!result) return;
setSessionMode(GAME_MODE.REGULAR);
setPendingAdmin(false);
const freshUser = await getUser(session.currentUid);
if (freshUser) {
@@ -243,6 +288,7 @@ const MapScreen = ({navigation, route}) => {
isBusiness: true,
});
if (!result) return;
setSessionMode(GAME_MODE.REGULAR);
setPendingAdmin(false);
const freshUser = await getUser(session.currentUid);
if (freshUser) {
@@ -305,6 +351,7 @@ const MapScreen = ({navigation, route}) => {
return;
}
setSessionGroup(result.Gid, result.SGid);
setSessionMode(GAME_MODE.REGULAR);
setPendingAdmin(false);
const freshUser = await getUser(session.currentUid);
if (freshUser) {
@@ -332,7 +379,7 @@ const MapScreen = ({navigation, route}) => {
setTimeout(() => setClaimedPopupVisible(false), 3000);
}
await loadCaches();
}, [session.currentGid, session.currentUid, session.currentTid, effectiveSGid, loadCaches]);
}, [session.currentGid, session.currentUid, session.currentTid, effectiveSGid, claimCache, setIsClaiming, loadCaches]);
const handleCreateCachePress = () => {
const fallback = userLocation || {latitude: mapRegion.latitude, longitude: mapRegion.longitude};
@@ -421,22 +468,12 @@ const MapScreen = ({navigation, route}) => {
<Screen style={styles.center}>
<Text style={styles.bizTitle}>Join {matchedGroup.BusinessOrSchoolName || 'Company'}</Text>
<Text style={styles.bizSubtitle}>Enter your department code to join</Text>
<View style={styles.inputRow}>
<TextInput
style={styles.codeInput}
placeholder="Department Code"
placeholderTextColor="#9ca3af"
value={deptCode}
onChangeText={(text) => setDeptCode(text.toUpperCase())}
autoCapitalize="characters"
/>
<Button
label="Join"
onClick={handleJoinDepartment}
styleButton={styles.joinButton}
styleLabel={styles.joinLabel}
/>
</View>
<CodeJoinRow
value={deptCode}
onChange={setDeptCode}
placeholder="Department Code"
onSubmit={handleJoinDepartment}
/>
{joinError ? <Text style={styles.joinError}>{joinError}</Text> : null}
<View style={styles.fullRow}>
<ButtonTray>
@@ -457,22 +494,13 @@ const MapScreen = ({navigation, route}) => {
<Screen style={styles.center}>
<Text style={styles.bizTitle}>Welcome</Text>
<Text style={styles.bizSubtitle}>Set up your organisation or join an existing one</Text>
<View style={styles.inputRow}>
<TextInput
style={styles.codeInput}
placeholder="Company Code"
placeholderTextColor="#9ca3af"
value={orgCode}
onChangeText={(text) => setOrgCode(text.toUpperCase())}
autoCapitalize="characters"
/>
<Button
label="Join Company"
onClick={handleVerifyOrgCode}
styleButton={styles.joinButton}
styleLabel={styles.joinLabel}
/>
</View>
<CodeJoinRow
value={orgCode}
onChange={setOrgCode}
placeholder="Company Code"
buttonLabel="Join Company"
onSubmit={handleVerifyOrgCode}
/>
{joinError ? <Text style={styles.joinError}>{joinError}</Text> : null}
<View style={styles.fullRow}>
<ButtonTray>
@@ -492,23 +520,23 @@ const MapScreen = ({navigation, route}) => {
if (!inGame) {
return (
<Screen style={styles.center}>
<View style={styles.inputRow}>
<TextInput
style={styles.codeInput}
placeholder="Enter Game Code"
placeholderTextColor="#9ca3af"
value={gameCode}
onChangeText={(text) => setGameCode(text.toUpperCase())}
autoCapitalize="characters"
/>
<Button
label="Join"
onClick={handleJoinGame}
styleButton={styles.joinButton}
styleLabel={styles.joinLabel}
/>
<View style={[styles.fullRow, styles.globalButtonRow]}>
<ButtonTray>
<Button
label="Join Global Game"
onClick={handleEnterGlobal}
styleButton={styles.globalJoinButton}
styleLabel={styles.globalJoinLabel}
/>
</ButtonTray>
</View>
<View style={styles.fullRow}>
<CodeJoinRow
value={gameCode}
onChange={setGameCode}
placeholder="Enter Game Code"
onSubmit={handleJoinGame}
/>
<View style={[styles.fullRow, styles.createGameRow]}>
<ButtonTray>
<Button
label="Create a Game"
@@ -518,6 +546,23 @@ const MapScreen = ({navigation, route}) => {
/>
</ButtonTray>
</View>
<View style={[styles.fullRow, styles.availableGamesRow]}>
<Text style={styles.availableGamesTitle}>Available Individual Games</Text>
{gamesLoading ? (
<Text style={styles.availableGamesBody}>Loading games...</Text>
) : availableGames.length === 0 ? (
<Text style={styles.availableGamesBody}>No individual games available right now.</Text>
) : (
<ScrollView style={styles.availableGamesList} contentContainerStyle={styles.availableGamesContent}>
{availableGames.map((group) => (
<Card key={group.Gid} style={styles.availableGameCard}>
<Text style={styles.availableGameName}>{group.GroupName || `Game ${group.Gid}`}</Text>
<Text style={styles.availableGameMeta}>Game ID: {group.Gid}</Text>
</Card>
))}
</ScrollView>
)}
</View>
</Screen>
);
}
@@ -794,20 +839,19 @@ const styles = StyleSheet.create({
mapContainer: {height: 200},
error: {color: '#D92800', fontSize: 15},
loadingText: {color: '#6c7086', fontSize: 14, marginTop: 10},
inputRow: {flexDirection: 'row', gap: 10, marginBottom: 15, width: '100%', paddingHorizontal: 20},
fullRow: {width: '100%', paddingHorizontal: 20},
codeInput: {
flex: 1,
borderWidth: 1,
borderColor: '#45475a',
borderRadius: 8,
paddingHorizontal: 12,
paddingVertical: 10,
fontSize: 16,
color: '#cdd6f4',
backgroundColor: '#313244',
}, joinButton: {backgroundColor: '#bd93f9', borderColor: '#bd93f9', flex: 0, paddingHorizontal: 20},
joinLabel: {color: '#1e1e2e', fontWeight: '600'},
globalButtonRow: {marginBottom: 12},
createGameRow: {marginBottom: 12},
availableGamesRow: {paddingTop: 4},
globalJoinButton: {backgroundColor: '#bd93f9', borderColor: '#bd93f9'},
globalJoinLabel: {color: '#1e1e2e', fontWeight: '600'},
availableGamesTitle: {color: '#cdd6f4', fontSize: 16, fontWeight: '700', marginBottom: 8},
availableGamesBody: {color: '#bac2de', fontSize: 14, marginBottom: 6},
availableGamesList: {maxHeight: 220},
availableGamesContent: {paddingBottom: 10},
availableGameCard: {marginBottom: 8},
availableGameName: {color: '#cdd6f4', fontSize: 15, fontWeight: '700', marginBottom: 3},
availableGameMeta: {color: '#bac2de', fontSize: 12},
createButton: {backgroundColor: '#a6e3a1', borderColor: '#a6e3a1'},
createLabel: {color: '#1e1e2e', fontWeight: '600'},
bizTitle: {fontSize: 22, fontWeight: '700', color: '#cdd6f4', marginBottom: 6, textAlign: 'center'},
+4 -3
View File
@@ -19,11 +19,12 @@ const RegisterScreen = ({navigation}) => {
const user = await register();
setSubmitting(false);
if (!user) return;
const rootNav = navigation.getParent() || navigation;
if (user.IsAcceptedAdmin) {
navigation.reset({index: 0, routes: [{name: 'Admin'}]});
rootNav.reset({index: 0, routes: [{name: 'Admin'}]});
return;
}
navigation.reset({index: 0, routes: [{name: 'Game'}]});
rootNav.reset({index: 0, routes: [{name: 'Game'}]});
};
return (
@@ -100,7 +101,7 @@ const RegisterScreen = ({navigation}) => {
/>
</ButtonTray>
<Pressable onPress={() => navigation.navigate('Auth', {screen: 'LoginScreen'})}>
<Pressable onPress={() => navigation.navigate('LoginScreen')}>
<Text style={styles.linkText}>Back to Login</Text>
</Pressable>
</View>
@@ -1,10 +1,11 @@
import { useEffect, useState } from "react";
import { ActivityIndicator, Alert, StyleSheet, Text, View } from "react-native";
import {useEffect, useState} from "react";
import {ActivityIndicator, Alert, StyleSheet, Text, View} from "react-native";
import Screen from "../../layout/Screen";
import { Button, ButtonTray } from "../../UI/Button";
import {Button, ButtonTray} from "../../UI/Button";
import Card from "../../UI/Card";
import useGlobalHook from "../../../hooks/useGlobalHook";
import { getSession } from "../../../hooks/SessionStore";
import {getSession} from "../../../hooks/SessionStore";
import {GAME_MODE} from "../../../utils/gameConstants";
const GlobalCacheViewScreen = ({ navigation, route }) => {
// Initialisations ---------------------
@@ -60,6 +61,12 @@ const GlobalCacheViewScreen = ({ navigation, route }) => {
);
};
useEffect(() => {
if (session.isBusiness || session.currentGameMode !== GAME_MODE.GLOBAL) {
navigation.replace("MapScreen");
}
}, [navigation, session.currentGameMode, session.isBusiness]);
// View --------------------------------
return (
@@ -1,16 +1,27 @@
import { useCallback, useEffect, useState } from "react";
import { ActivityIndicator, StyleSheet, Text, View } from "react-native";
import {useCallback, useEffect, useRef, useState} from "react";
import {ActivityIndicator, StyleSheet, Text, View} from "react-native";
import Screen from "../../layout/Screen";
import { Button, ButtonTray } from "../../UI/Button";
import {Button, ButtonTray} from "../../UI/Button";
import EventList from "../../../entity/event/EventList";
import useGlobalHook from "../../../hooks/useGlobalHook";
import { getSession, setGlobalSession } from "../../../hooks/SessionStore";
import {getSession, setGlobalSession} from "../../../hooks/SessionStore";
import {GAME_MODE} from "../../../utils/gameConstants";
const GlobalEventsScreen = ({ navigation }) => {
// Initialisations ---------------------
const { getPublicEvents, getPlayersByEvent, joinEvent } = useGlobalHook();
const session = getSession();
const globalApiRef = useRef({
getPublicEvents,
getPlayersByEvent,
joinEvent,
});
globalApiRef.current = {
getPublicEvents,
getPlayersByEvent,
joinEvent,
};
// State -------------------------------
@@ -24,21 +35,25 @@ const GlobalEventsScreen = ({ navigation }) => {
const loadEvents = useCallback(async () => {
setIsLoading(true);
setError("");
const data = await getPublicEvents();
const data = await globalApiRef.current.getPublicEvents();
setEvents(data);
setIsLoading(false);
}, []);
useEffect(() => {
if (session.isBusiness || session.currentGameMode !== GAME_MODE.GLOBAL) {
navigation.replace("MapScreen");
return;
}
loadEvents();
}, [loadEvents]);
}, [loadEvents, navigation, session.currentGameMode, session.isBusiness]);
const handleSelect = async (event) => {
setError("");
setJoining(event.EventID);
// Check if already a player
const players = await getPlayersByEvent(event.EventID);
const players = await globalApiRef.current.getPlayersByEvent(event.EventID);
const existing = (players || []).find(
(p) => p.PlayerUserID === session.currentUid,
);
@@ -48,7 +63,7 @@ const GlobalEventsScreen = ({ navigation }) => {
navigation.navigate("GlobalMapScreen", { eventId: event.EventID });
} else {
// Join the event
const player = await joinEvent({
const player = await globalApiRef.current.joinEvent({
PlayerUserID: session.currentUid,
PlayerEventID: event.EventID,
});
@@ -1,22 +1,17 @@
import { useCallback, useEffect, useState } from "react";
import {
ActivityIndicator,
ScrollView,
StyleSheet,
Text,
View,
} from "react-native";
import {useCallback, useEffect, useState} from "react";
import {ActivityIndicator, ScrollView, StyleSheet, Text, View,} from "react-native";
import Screen from "../../layout/Screen";
import { Button, ButtonTray } from "../../UI/Button";
import {Button, ButtonTray} from "../../UI/Button";
import Card from "../../UI/Card";
import FindList from "../../../entity/find/FindList";
import useGlobalHook from "../../../hooks/useGlobalHook";
import { getSession } from "../../../hooks/SessionStore";
import {getSession} from "../../../hooks/SessionStore";
import {GAME_MODE} from "../../../utils/gameConstants";
const GlobalLeaderboardScreen = ({ navigation, route }) => {
// Initialisations ---------------------
const { eventId } = route.params;
const eventId = route?.params?.eventId ?? getSession().currentGlobalEventId;
const { getFindsByEvent, getCachesByEvent, getPlayersByEvent } =
useGlobalHook();
const session = getSession();
@@ -79,11 +74,19 @@ const GlobalLeaderboardScreen = ({ navigation, route }) => {
const ranked = Object.values(pointsMap).sort((a, b) => b.points - a.points);
setRankings(ranked);
setIsLoading(false);
}, [eventId]);
}, [eventId, getFindsByEvent, getCachesByEvent, getPlayersByEvent]);
useEffect(() => {
if (session.isBusiness || session.currentGameMode !== GAME_MODE.GLOBAL) {
navigation.replace("MapScreen");
return;
}
if (!eventId) {
navigation.replace("GlobalEventsScreen");
return;
}
loadData();
}, [loadData]);
}, [eventId, loadData, navigation, session.currentGameMode, session.isBusiness]);
// View --------------------------------
@@ -1,12 +1,13 @@
import { useCallback, useEffect, useState } from "react";
import { ActivityIndicator, StyleSheet, Text, View } from "react-native";
import MapView, { Marker } from "react-native-maps";
import {useCallback, useEffect, useState} from "react";
import {ActivityIndicator, StyleSheet, Text, View} from "react-native";
import MapView, {Marker} from "react-native-maps";
import * as Location from "expo-location";
import Screen from "../../layout/Screen";
import { Button, ButtonTray } from "../../UI/Button";
import {Button, ButtonTray} from "../../UI/Button";
import CacheList from "../../../entity/cache/CacheList";
import useGlobalHook from "../../../hooks/useGlobalHook";
import { getSession } from "../../../hooks/SessionStore";
import {getSession} from "../../../hooks/SessionStore";
import {GAME_MODE} from "../../../utils/gameConstants";
const DEFAULT_REGION = {
latitude: 51.5074,
@@ -18,7 +19,7 @@ const DEFAULT_REGION = {
const GlobalMapScreen = ({ navigation, route }) => {
// Initialisations ---------------------
const { eventId } = route.params;
const eventId = route?.params?.eventId ?? getSession().currentGlobalEventId;
const { getCachesByEvent, getFindsByPlayer } = useGlobalHook();
const session = getSession();
@@ -47,11 +48,19 @@ const GlobalMapScreen = ({ navigation, route }) => {
setCaches(cacheData);
setFoundCacheIds((findsData || []).map((f) => f.FindCacheID));
setIsLoading(false);
}, [eventId]);
}, [eventId, getCachesByEvent, getFindsByPlayer, session.currentGlobalPlayerId]);
useEffect(() => {
if (session.isBusiness || session.currentGameMode !== GAME_MODE.GLOBAL) {
navigation.replace("MapScreen");
return;
}
if (!eventId) {
navigation.replace("GlobalEventsScreen");
return;
}
loadData();
}, [loadData]);
}, [eventId, loadData, navigation, session.currentGameMode, session.isBusiness]);
useEffect(() => {
let sub;
+19 -3
View File
@@ -1,6 +1,5 @@
// --- Session Store (with AsyncStorage persistence) ---
import AsyncStorage from "@react-native-async-storage/async-storage";
import {GAME_MODE} from '../utils/gameConstants';
const STORAGE_KEY = "@mad_session";
@@ -13,6 +12,7 @@ const session = {
isAcceptedAdmin: false,
isPendingAdmin: false,
teamsEnabled: false,
currentGameMode: null,
// Global gameplay
currentGlobalEventId: null,
currentGlobalPlayerId: null,
@@ -31,11 +31,16 @@ const setSessionUser = (user) => {
session.isPendingAdmin = Boolean(
user?.isBusiness && user?.Gid && !user?.SGid && !user?.IsAcceptedAdmin,
);
// New login/register starts in regular flow; global flow requires entering GLOBAL code.
session.currentGlobalEventId = null;
session.currentGlobalPlayerId = null;
session.currentGameMode = user?.Gid ? GAME_MODE.REGULAR : null;
persistSession();
};
const setSessionGroup = (gid, sgid = null) => {
session.currentGid = gid ?? null;
session.currentGameMode = gid ? GAME_MODE.REGULAR : session.currentGameMode;
if (sgid !== undefined) {
session.currentSGid = sgid ?? null;
}
@@ -67,12 +72,19 @@ const getSession = () => ({ ...session });
const setGlobalSession = (eventId, playerId) => {
session.currentGlobalEventId = eventId ?? null;
session.currentGlobalPlayerId = playerId ?? null;
session.currentGameMode = eventId ? GAME_MODE.GLOBAL : session.currentGameMode;
persistSession();
};
const setSessionMode = (mode) => {
session.currentGameMode = mode ?? null;
persistSession();
};
const clearGlobalSession = () => {
session.currentGlobalEventId = null;
session.currentGlobalPlayerId = null;
if (!session.currentGid) session.currentGameMode = null;
persistSession();
};
@@ -83,6 +95,7 @@ const clearSession = () => {
session.currentTid = null;
session.isBusiness = null;
session.isAcceptedAdmin = false;
session.currentGameMode = null;
session.currentGlobalEventId = null;
session.currentGlobalPlayerId = null;
session.isPendingAdmin = false;
@@ -98,6 +111,7 @@ const clearGameSession = () => {
session.isPendingAdmin = false;
session.teamsEnabled = false;
session.selectedCacheId = null;
if (!session.currentGlobalEventId) session.currentGameMode = null;
persistSession();
};
@@ -125,11 +139,13 @@ export {
setSessionGroup,
setSessionTeam,
setSessionTeamsEnabled,
setPendingAdmin,
setSelectedCache,
getSession,
clearSession,
clearGameSession,
loadSession,
setGlobalSession,
clearGlobalSession,
setSessionMode,
};
export { setSessionUser, setSessionGroup, setSessionTeam, setSessionTeamsEnabled, setPendingAdmin, setSelectedCache, getSession, clearSession, clearGameSession, loadSession };
+6
View File
@@ -31,6 +31,11 @@ const useGameHook = () => {
return response.isSuccess ? response.result : null;
};
const getGroups = async () => {
const response = await API.get(groupsEndpoint);
return response.isSuccess ? response.result : [];
};
const getLobby = async (gid) => {
const response = await API.get(`${groupsEndpoint}/${gid}`);
return response.isSuccess ? response.result : null;
@@ -250,6 +255,7 @@ const useGameHook = () => {
getUser,
updateUser,
createPrivateGame,
getGroups,
getLobby,
updateGroup,
getGroupByOrgCode,
+7
View File
@@ -0,0 +1,7 @@
export const GLOBAL_GAME_CODE = 'GLOBAL';
export const GAME_MODE = {
REGULAR: 'regular',
GLOBAL: 'global',
};