diff --git a/App.js b/App.js
index 222b6a0..1ba19b5 100644
--- a/App.js
+++ b/App.js
@@ -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";
@@ -11,7 +11,8 @@ import LeaderboardScreen from "./src/components/screens/LeaderboardScreen";
import GameSettingsScreen from "./src/components/screens/GameSettingsScreen";
import PlayersScreen from "./src/components/screens/PlayersScreen";
import ExpandedMapScreen from "./src/components/screens/ExpandedMapScreen";
-import {loadSession} from "./src/hooks/SessionStore";
+import GameSelectionScreen from "./src/components/screens/GameSelectionScreen";
+import { loadSession } from "./src/hooks/SessionStore";
const RootStack = createNativeStackNavigator();
const AuthStackNav = createNativeStackNavigator();
@@ -19,71 +20,106 @@ const GameStackNav = createNativeStackNavigator();
const AdminStackNav = createNativeStackNavigator();
const screenOptions = {
- headerShown: false,
- contentStyle: {backgroundColor: '#ffffff'},
+ headerShown: false,
+ contentStyle: { backgroundColor: "#ffffff" },
};
const AuthStack = () => (
-
-
-
-
+
+
+
+
);
const GameStack = () => (
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
);
const AdminStack = () => (
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
);
export const App = () => {
- // Load persisted session from AsyncStorage before choosing initial route
- const [loading, setLoading] = useState(true);
- const [initialRoute, setInitialRoute] = useState('Auth');
+ // Load persisted session from AsyncStorage before choosing initial route
+ const [loading, setLoading] = useState(true);
+ const [initialRoute, setInitialRoute] = useState("Auth");
- useEffect(() => {
- loadSession().then((session) => {
- if (session.currentUid) {
- setInitialRoute(session.isAcceptedAdmin ? 'Admin' : 'Game');
- }
- setLoading(false);
- });
- }, []);
-
- if (loading) {
- return (
-
-
-
- );
- }
+ useEffect(() => {
+ loadSession().then((session) => {
+ if (session.currentUid) {
+ setInitialRoute(session.isAcceptedAdmin ? "Admin" : "Game");
+ }
+ setLoading(false);
+ });
+ }, []);
+ if (loading) {
return (
-
-
-
-
-
-
-
-
-
+
+
+
);
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
};
export default App;
diff --git a/src/components/layout/BottomNavbar.js b/src/components/layout/BottomNavbar.js
index 528699a..0e68805 100644
--- a/src/components/layout/BottomNavbar.js
+++ b/src/components/layout/BottomNavbar.js
@@ -1,113 +1,164 @@
-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, 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";
-const BottomNavbar = ({navigation, routeName}) => {
-// Initialisation ------------
+const BottomNavbar = ({ navigation, routeName }) => {
+ // Initialisation ------------
- const insets = useSafeAreaInsets();
- const session = getSession();
+ const insets = useSafeAreaInsets();
+ const session = getSession();
- 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')},
- ];
+ 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"),
+ },
+ ];
- 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: 'Leave', route: '__leave__', onPress: () => handleLeaveGame()},
- ];
+ 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"),
+ },
- const tabs = session.isAcceptedAdmin ? adminTabs : playerTabs;
+ { label: "Leave", route: "__leave__", onPress: () => handleLeaveGame() },
+ ];
-// State ----------------------
-// Handlers -------------------
+ const tabs = session.isAcceptedAdmin ? adminTabs : playerTabs;
- 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-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}`);
- }
- clearGameSession();
- navigation.reset({index: 0, routes: [{name: 'Game'}]});
- },
- },
- ]);
- };
+ // State ----------------------
+ // Handlers -------------------
-// View -----------------------
+ 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-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}`,
+ );
+ }
+ clearGameSession();
+ navigation.reset({ index: 0, routes: [{ name: "Game" }] });
+ },
+ },
+ ]);
+ };
- return (
-
- {tabs.map((tab) => {
- const isActive = routeName === tab.route;
- const isLeave = tab.route === '__leave__';
- return (
-
-
- {tab.label}
-
-
- );
- })}
-
- );
+ // View -----------------------
+
+ return (
+
+ {tabs.map((tab) => {
+ const isActive = routeName === tab.route;
+ const isLeave = tab.route === "__leave__";
+ return (
+
+
+ {tab.label}
+
+
+ );
+ })}
+
+ );
};
const styles = StyleSheet.create({
- container: {
- minHeight: 64,
- borderTopWidth: 1,
- borderTopColor: '#555555',
- backgroundColor: '#4a4a4a',
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'space-around',
- paddingHorizontal: 8,
- },
- tab: {
- flex: 1,
- alignItems: 'center',
- justifyContent: 'center',
- paddingVertical: 12,
- borderRadius: 8,
- marginHorizontal: 4,
- },
- tabActive: {
- backgroundColor: '#5c5c5c',
- },
- tabLeave: {
- backgroundColor: 'rgba(220,38,38,0.15)',
- },
- tabText: {
- color: '#d1d5db',
- fontSize: 12,
- fontWeight: '600',
- textAlign: 'center',
- },
- tabTextActive: {
- color: '#ffffff',
- },
- tabTextLeave: {
- color: '#fca5a5',
- },
+ container: {
+ minHeight: 64,
+ borderTopWidth: 1,
+ borderTopColor: "#555555",
+ backgroundColor: "#4a4a4a",
+ flexDirection: "row",
+ alignItems: "center",
+ justifyContent: "space-around",
+ paddingHorizontal: 8,
+ },
+ tab: {
+ flex: 1,
+ alignItems: "center",
+ justifyContent: "center",
+ paddingVertical: 12,
+ borderRadius: 8,
+ marginHorizontal: 4,
+ },
+ tabActive: {
+ backgroundColor: "#5c5c5c",
+ },
+ tabLeave: {
+ backgroundColor: "rgba(220,38,38,0.15)",
+ },
+ tabText: {
+ color: "#d1d5db",
+ fontSize: 12,
+ fontWeight: "600",
+ textAlign: "center",
+ },
+ tabTextActive: {
+ color: "#ffffff",
+ },
+ tabTextLeave: {
+ color: "#fca5a5",
+ },
});
export default BottomNavbar;
diff --git a/src/components/layout/Screen.js b/src/components/layout/Screen.js
index 95957bd..25ffb0a 100644
--- a/src/components/layout/Screen.js
+++ b/src/components/layout/Screen.js
@@ -1,7 +1,7 @@
-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 { 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";
@@ -17,6 +17,7 @@ const Screen = ({ children, style, showBack = false }) => {
TeamScreen: "Current Team",
MapScreen: "Map",
GameSettingsScreen: "Game Settings",
+ GameSelectionScreen: "Games",
PlayersScreen: "Players",
ExpandedMapScreen: "Map View",
};
@@ -24,17 +25,34 @@ const Screen = ({ children, style, showBack = false }) => {
// State ----------------------
const routeName = route.name;
- const isAuthScreen = routeName === "LoginScreen" || routeName === "RegisterScreen";
+ const isAuthScreen =
+ routeName === "LoginScreen" || routeName === "RegisterScreen";
const pageTitle = titleMap[routeName] || "Treasure Hunt";
// Handlers -------------------
// View -----------------------
return (
-
- {!isAuthScreen && }
+
+ {!isAuthScreen && (
+
+ )}
{children}
- {!isAuthScreen && }
+ {!isAuthScreen && (
+
+ )}
);
@@ -52,4 +70,3 @@ const styles = StyleSheet.create({
});
export default Screen;
-
diff --git a/src/components/screens/GameSelectionScreen.js b/src/components/screens/GameSelectionScreen.js
new file mode 100644
index 0000000..5377e5c
--- /dev/null
+++ b/src/components/screens/GameSelectionScreen.js
@@ -0,0 +1,184 @@
+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";
+
+const GameSelectionScreen = ({ navigation }) => {
+ // Initialisations ---------------------
+
+ const session = getSession();
+ const isAdmin = Boolean(session.isAcceptedAdmin);
+
+ const privateGames = [
+ {
+ id: "active-game",
+ name: "Private Game",
+ players: "4 players",
+ endsIn: "Ends in 2h 15m",
+ },
+ ];
+
+ // State -------------------------------
+ // Handlers ----------------------------
+
+ const handleCreateGame = () => {
+ if (isAdmin) {
+ navigation.navigate("GameSettingsScreen");
+ return;
+ }
+ navigation.navigate("TeamScreen");
+ };
+
+ const handleOpenGame = () => {
+ navigation.navigate("TeamScreen");
+ };
+
+ const handleExplore = () => {
+ navigation.navigate("MapScreen");
+ };
+
+ // View --------------------------------
+
+ return (
+
+
+
+ Your Games
+
+
+
+
+
+
+ Private Games
+ {privateGames.map((game) => (
+
+ {game.name}
+ {game.players}
+ {game.endsIn}
+
+
+
+
+ ))}
+
+
+
+ Global Mode
+
+ Explore all visible caches and live activity from the map.
+
+
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ screenContent: {
+ backgroundColor: "#f3f4f6",
+ paddingHorizontal: 0,
+ paddingVertical: 0,
+ },
+ scrollContent: {
+ padding: 15,
+ paddingBottom: 28,
+ },
+ sectionTop: {
+ marginBottom: 16,
+ gap: 10,
+ },
+ sectionTitle: {
+ fontSize: 22,
+ fontWeight: "700",
+ color: "#111827",
+ },
+ sectionBlock: {
+ marginTop: 8,
+ marginBottom: 10,
+ },
+ blockTitle: {
+ fontSize: 15,
+ fontWeight: "700",
+ color: "#374151",
+ marginBottom: 8,
+ },
+ gameCard: {
+ marginBottom: 10,
+ paddingHorizontal: 16,
+ paddingVertical: 14,
+ borderColor: "#d1d5db",
+ borderRadius: 10,
+ },
+ gameName: {
+ fontSize: 17,
+ fontWeight: "700",
+ color: "#111827",
+ marginBottom: 4,
+ },
+ gameMeta: {
+ fontSize: 14,
+ color: "#4b5563",
+ marginBottom: 2,
+ },
+ cardActions: {
+ marginTop: 12,
+ maxWidth: 160,
+ },
+ supportText: {
+ fontSize: 14,
+ color: "#4b5563",
+ marginBottom: 10,
+ lineHeight: 20,
+ },
+ primaryButton: {
+ backgroundColor: "#16a34a",
+ borderColor: "#16a34a",
+ },
+ primaryButtonLabel: {
+ color: "#ffffff",
+ fontWeight: "600",
+ },
+ secondaryButton: {
+ backgroundColor: "#2563eb",
+ borderColor: "#2563eb",
+ flex: 0,
+ minHeight: 38,
+ paddingHorizontal: 18,
+ },
+ secondaryButtonLabel: {
+ color: "#ffffff",
+ fontWeight: "600",
+ fontSize: 14,
+ },
+ neutralButton: {
+ backgroundColor: "#374151",
+ borderColor: "#374151",
+ maxWidth: 180,
+ },
+ neutralButtonLabel: {
+ color: "#ffffff",
+ fontWeight: "600",
+ },
+});
+
+export default GameSelectionScreen;
diff --git a/src/template.js b/src/template.js
new file mode 100644
index 0000000..f707362
--- /dev/null
+++ b/src/template.js
@@ -0,0 +1,13 @@
+/*import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
+const Component = () => {
+// Initialisations ---------------------
+// State -------------------------------
+// Handlers ----------------------------
+// View --------------------------------
+return (
+);
+};
+const styles = StyleSheet.create({
+});
+export default Component;
+*/