diff --git a/App.js b/App.js
index 0ae59de..6ba295b 100644
--- a/App.js
+++ b/App.js
@@ -17,65 +17,56 @@ import ManageSubgroupsScreen from "./src/components/screens/ManageSubgroupsScree
const Stack = createNativeStackNavigator();
const AuthStack = () => (
-
-
-
-
+
+
+
+
);
const GameStack = () => (
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
);
const AdminStack = () => (
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
);
export const App = () => {
- return (
-
-
-
-
-
-
-
- );
+ return (
+
+
+
+
+
+
+
+ );
};
export default App;
-
diff --git a/package.json b/package.json
index ca70bae..f6df575 100644
--- a/package.json
+++ b/package.json
@@ -3,10 +3,7 @@
"main": "node_modules/expo/AppEntry.js",
"version": "1.0.0",
"scripts": {
- "api": "node server/server.js",
- "api:standalone": "npm --prefix server start",
"start": "expo start",
- "start:api": "npm run api",
"android": "expo run:android",
"ios": "expo run:ios",
"web": "expo start --web",
diff --git a/src/components/API/API.js b/src/components/API/API.js
new file mode 100644
index 0000000..074de50
--- /dev/null
+++ b/src/components/API/API.js
@@ -0,0 +1,32 @@
+const API = {};
+
+API.get = (endpoint) => callFetch(endpoint, 'GET');
+API.post = (endpoint, data) => callFetch(endpoint, 'POST', data);
+API.put = (endpoint, data) => callFetch(endpoint, 'PUT', data);
+API.delete = (endpoint) => callFetch(endpoint, 'DELETE');
+
+export default API;
+
+const callFetch = async (endpoint, method, dataObj = null) => {
+ // Build request object
+ let requestObj = {method: method}; // GET, POST, PUT or DELETE
+ if (dataObj)
+ requestObj = {
+ ...requestObj,
+ headers: {'Content-type': 'application/json'},
+ body: JSON.stringify(dataObj),
+ };
+
+ // Call the fetch and process the return
+ try {
+ let result = null;
+ const response = await fetch(endpoint, requestObj);
+ if (response.status !== 204) result = await response.json();
+ return response.status >= 200 && response.status < 300
+ ? {isSuccess: true, result}
+ : {isSuccess: false, message: `${result.message}`};
+ } catch (error) {
+ return {isSuccess: false, message: error.message};
+ }
+};
+
diff --git a/src/components/API/useLoad.js b/src/components/API/useLoad.js
new file mode 100644
index 0000000..5f3feef
--- /dev/null
+++ b/src/components/API/useLoad.js
@@ -0,0 +1,30 @@
+import {useEffect, useState} from "react";
+import API from "./API";
+
+
+const useLoad = (loadEndpoint) => {
+
+ // State ----------------------
+
+ const [records, setRecords] = useState([]);
+ const [isLoading, setIsLoading] = useState(true);
+
+ // Loader ----------------------
+ const loadRecords = async (endpoint) => {
+ setIsLoading(true);
+ const response = await API.get(endpoint);
+ if (response.isSuccess) setRecords(response.result);
+ setIsLoading(false);
+ };
+
+ useEffect(() => {
+ loadRecords(loadEndpoint);
+ }, []);
+
+ // return ----------------------
+
+ return [records, setRecords, isLoading, loadRecords];
+};
+
+export default useLoad;
+
diff --git a/src/components/UI/Button.js b/src/components/UI/Button.js
index 7c3bf4e..3c39ec2 100644
--- a/src/components/UI/Button.js
+++ b/src/components/UI/Button.js
@@ -1,6 +1,11 @@
import {Pressable, StyleSheet, Text, View} from "react-native";
export const Button = ({ label, icon, onClick, styleLabel, styleButton }) => {
+ // Initialisation -------------
+ // State ----------------------
+ // Handlers -------------------
+ // View -----------------------
+
return (
{icon ? icon : null}
@@ -10,6 +15,11 @@ export const Button = ({ label, icon, onClick, styleLabel, styleButton }) => {
};
export const ButtonTray = ({ children }) => {
+ // Initialisation -------------
+ // State ----------------------
+ // Handlers -------------------
+ // View -----------------------
+
return {children};
};
diff --git a/src/components/gameplay/AdminCacheEditorView.js b/src/components/gameplay/AdminCacheEditorView.js
index 9635b6b..1880224 100644
--- a/src/components/gameplay/AdminCacheEditorView.js
+++ b/src/components/gameplay/AdminCacheEditorView.js
@@ -2,6 +2,11 @@ import React from 'react';
import MapView, {Circle, Marker} from 'react-native-maps';
const AdminCacheEditorView = ({ userLocation, caches, onAddCache, onMoveCache }) => {
+// Initialisation -------------
+// State ----------------------
+// Handlers -------------------
+// View -----------------------
+
if (!userLocation) {
return null;
}
diff --git a/src/components/gameplay/ClaimTimerView.js b/src/components/gameplay/ClaimTimerView.js
index 6c723bc..0b46031 100644
--- a/src/components/gameplay/ClaimTimerView.js
+++ b/src/components/gameplay/ClaimTimerView.js
@@ -2,8 +2,13 @@ import React, {useEffect, useState} from 'react';
import {StyleSheet, Text, View} from 'react-native';
const ClaimTimerView = ({ cache, onClaimSuccess, isClaiming }) => {
+// Initialisation -------------
+// State ----------------------
+
const [timeLeft, setTimeLeft] = useState(5);
+// Handlers -------------------
+
useEffect(() => {
let timer;
if (isClaiming && timeLeft > 0) {
@@ -16,6 +21,8 @@ const ClaimTimerView = ({ cache, onClaimSuccess, isClaiming }) => {
return () => clearTimeout(timer);
}, [isClaiming, timeLeft, cache, onClaimSuccess]);
+// View -----------------------
+
if (!isClaiming) {
return null;
}
@@ -45,4 +52,3 @@ const styles = StyleSheet.create({
});
export default ClaimTimerView;
-
diff --git a/src/components/gameplay/PlayerMapView.js b/src/components/gameplay/PlayerMapView.js
index 707901c..de9df1e 100644
--- a/src/components/gameplay/PlayerMapView.js
+++ b/src/components/gameplay/PlayerMapView.js
@@ -3,9 +3,12 @@ import MapView, {Circle} from 'react-native-maps';
import ClaimTimerView from './ClaimTimerView';
const PlayerMapView = ({ userLocation, visibleCache, isClaiming, onClaimSuccess }) => {
- if (!userLocation) {
- return null;
- }
+// Initialisation -------------
+// State ----------------------
+// Handlers -------------------
+// View -----------------------
+
+ if (!userLocation) return null;
return (
<>
diff --git a/src/components/layout/BottomNavbar.js b/src/components/layout/BottomNavbar.js
index 8ad987d..54473df 100644
--- a/src/components/layout/BottomNavbar.js
+++ b/src/components/layout/BottomNavbar.js
@@ -1,74 +1,79 @@
import {Pressable, StyleSheet, Text, View} from 'react-native';
const BottomNavbar = ({ navigation, routeName }) => {
- const tabs = [
- {
- label: 'Map',
- route: 'MapScreen',
- onPress: () => navigation.navigate('Game', { screen: 'MapScreen' }),
- },
- {
- label: 'Leaderboard',
- route: 'LeaderboardScreen',
- onPress: () => navigation.navigate('Game', { screen: 'LeaderboardScreen' }),
- },
- {
- label: 'Current Team',
- route: 'TeamScreen',
- onPress: () => navigation.navigate('Game', { screen: 'TeamScreen' }),
- },
- ];
+// Initialisation ------------
- return (
-
- {tabs.map((tab) => {
- const isActive = routeName === tab.route;
- return (
-
- {tab.label}
-
- );
- })}
-
- );
+ const tabs = [
+ {
+ label: 'Map',
+ route: 'MapScreen',
+ onPress: () => navigation.navigate('Game', { screen: 'MapScreen' }),
+ },
+ {
+ label: 'Leaderboard',
+ route: 'LeaderboardScreen',
+ onPress: () => navigation.navigate('Game', { screen: 'LeaderboardScreen' }),
+ },
+ {
+ label: 'Current Team',
+ route: 'TeamScreen',
+ onPress: () => navigation.navigate('Game', { screen: 'TeamScreen' }),
+ },
+ ];
+
+// State ----------------------
+// Handlers -------------------
+// View -----------------------
+
+ return (
+
+ {tabs.map((tab) => {
+ const isActive = routeName === tab.route;
+ return (
+
+ {tab.label}
+
+ );
+ })}
+
+ );
};
const styles = StyleSheet.create({
- container: {
- minHeight: 64,
- borderTopWidth: 1,
- borderTopColor: '#e5e7eb',
- backgroundColor: '#ffffff',
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'space-around',
- paddingHorizontal: 8,
- },
- tab: {
- flex: 1,
- alignItems: 'center',
- justifyContent: 'center',
- paddingVertical: 12,
- borderRadius: 8,
- marginHorizontal: 4,
- },
- tabActive: {
- backgroundColor: '#eff6ff',
- },
- tabText: {
- color: '#374151',
- fontSize: 12,
- fontWeight: '600',
- textAlign: 'center',
- },
- tabTextActive: {
- color: '#2563eb',
- },
+ container: {
+ minHeight: 64,
+ borderTopWidth: 1,
+ borderTopColor: '#e5e7eb',
+ backgroundColor: '#ffffff',
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'space-around',
+ paddingHorizontal: 8,
+ },
+ tab: {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ paddingVertical: 12,
+ borderRadius: 8,
+ marginHorizontal: 4,
+ },
+ tabActive: {
+ backgroundColor: '#eff6ff',
+ },
+ tabText: {
+ color: '#374151',
+ fontSize: 12,
+ fontWeight: '600',
+ textAlign: 'center',
+ },
+ tabTextActive: {
+ color: '#2563eb',
+ },
});
export default BottomNavbar;
-
diff --git a/src/components/layout/Screen.js b/src/components/layout/Screen.js
index 3c8a5f7..3382560 100644
--- a/src/components/layout/Screen.js
+++ b/src/components/layout/Screen.js
@@ -5,18 +5,18 @@ import TopNavbar from "./TopNavbar";
import BottomNavbar from "./BottomNavbar";
const Screen = ({ children, style }) => {
+ // Initialisation -------------
+
const navigation = useNavigation();
const route = useRoute();
const titleMap = {
- LoginScreen: "Login",
- RegisterScreen: "Register",
GameTypeScreen: "Game Type",
JoinPrivateGameScreen: "Join Private Game",
ManagePrivateGameScreen: "Manage Private Game",
CreateGameScreen: "Create Game",
ManageGameScreen: "Manage Game",
- GameLobbyScreen: "Leaderboard",
+ GameLobbyScreen: "Game Lobby",
LeaderboardScreen: "Leaderboard",
TeamScreen: "Current Team",
MapScreen: "Map",
@@ -24,15 +24,21 @@ const Screen = ({ children, style }) => {
ManageSubgroupsScreen: "Manage Subgroups",
};
+ // State ----------------------
+
const routeName = route.name;
- const showBack = routeName !== "LoginScreen" && navigation.canGoBack();
+ const isAuthScreen = routeName === "LoginScreen" || routeName === "RegisterScreen";
+ const showBack = !isAuthScreen && navigation.canGoBack();
const pageTitle = titleMap[routeName] || "Treasure Hunt";
+ // Handlers -------------------
+ // View -----------------------
+
return (
-
+ {!isAuthScreen && }
{children}
-
+ {!isAuthScreen && }
);
diff --git a/src/components/layout/TopNavbar.js b/src/components/layout/TopNavbar.js
index 088517a..fffeb5e 100644
--- a/src/components/layout/TopNavbar.js
+++ b/src/components/layout/TopNavbar.js
@@ -1,90 +1,94 @@
import {Pressable, StyleSheet, Text, View} from 'react-native';
import {clearSession} from '../../hooks/SessionStore';
-const TopNavbar = ({ title, showBack, navigation }) => {
- const routeName = navigation.getState()?.routes?.[navigation.getState().index]?.name;
- const showLogout = routeName !== 'LoginScreen' && routeName !== 'RegisterScreen';
+const TopNavbar = ({title, showBack, navigation}) => {
+// Initialisation ------------
- const handleBack = () => {
- if (navigation.canGoBack()) {
- navigation.goBack();
- }
- };
+ const routeName = navigation.getState()?.routes?.[navigation.getState().index]?.name;
+ const showLogout = routeName !== 'LoginScreen' && routeName !== 'RegisterScreen';
- const handleLogout = () => {
- clearSession();
- navigation.reset({
- index: 0,
- routes: [{ name: 'Auth', params: { screen: 'LoginScreen' } }],
- });
- };
+// State ----------------------
+// Handlers -------------------
- return (
-
-
- {showBack ? (
-
- {'<'}
-
- ) : null}
-
- {title}
-
- {showLogout ? (
-
- Logout
-
- ) : null}
-
-
- );
+ const handleBack = () => {
+ if (navigation.canGoBack()) navigation.goBack();
+ };
+
+ const handleLogout = () => {
+ clearSession();
+ navigation.reset({
+ index: 0,
+ routes: [{name: 'Auth', params: {screen: 'LoginScreen'}}],
+ });
+ };
+
+// View -----------------------
+
+ return (
+
+
+ {showBack ? (
+
+ {'<'}
+
+ ) : null}
+
+ {title}
+
+ {showLogout ? (
+
+ Logout
+
+ ) : null}
+
+
+ );
};
const styles = StyleSheet.create({
- container: {
- minHeight: 56,
- borderBottomWidth: 1,
- borderBottomColor: '#e5e7eb',
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'space-between',
- paddingHorizontal: 12,
- backgroundColor: '#ffffff',
- },
- leftSlot: {
- width: 72,
- alignItems: 'flex-start',
- },
- rightSlot: {
- width: 72,
- alignItems: 'flex-end',
- },
- iconButton: {
- paddingVertical: 8,
- paddingHorizontal: 10,
- },
- iconText: {
- fontSize: 20,
- color: '#111827',
- fontWeight: '700',
- },
- title: {
- flex: 1,
- textAlign: 'center',
- fontSize: 18,
- fontWeight: '700',
- color: '#111827',
- },
- logoutButton: {
- paddingVertical: 6,
- paddingHorizontal: 8,
- },
- logoutText: {
- color: '#2563eb',
- fontSize: 13,
- fontWeight: '600',
- },
+ container: {
+ minHeight: 56,
+ borderBottomWidth: 1,
+ borderBottomColor: '#e5e7eb',
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ paddingHorizontal: 12,
+ backgroundColor: '#ffffff',
+ },
+ leftSlot: {
+ width: 72,
+ alignItems: 'flex-start',
+ },
+ rightSlot: {
+ width: 72,
+ alignItems: 'flex-end',
+ },
+ iconButton: {
+ paddingVertical: 8,
+ paddingHorizontal: 10,
+ },
+ iconText: {
+ fontSize: 20,
+ color: '#111827',
+ fontWeight: '700',
+ },
+ title: {
+ flex: 1,
+ textAlign: 'center',
+ fontSize: 18,
+ fontWeight: '700',
+ color: '#111827',
+ },
+ logoutButton: {
+ paddingVertical: 6,
+ paddingHorizontal: 8,
+ },
+ logoutText: {
+ color: '#2563eb',
+ fontSize: 13,
+ fontWeight: '600',
+ },
});
export default TopNavbar;
-
diff --git a/src/components/screens/AdminHubScreen.js b/src/components/screens/AdminHubScreen.js
index 1e68d6f..58822b6 100644
--- a/src/components/screens/AdminHubScreen.js
+++ b/src/components/screens/AdminHubScreen.js
@@ -2,17 +2,24 @@ import React from 'react';
import {StyleSheet, Text} from 'react-native';
import Screen from '../layout/Screen';
-const AdminHubScreen = () => (
-
- Admin Hub
- Admin hub setup goes here.
-
-);
+const AdminHubScreen = () => {
+// Initialisation -------------
+// State ----------------------
+// Handlers -------------------
+// View -----------------------
+
+ return (
+
+ Admin Hub
+ Admin hub setup goes here.
+
+ );
+};
const styles = StyleSheet.create({
- container: { justifyContent: 'center', alignItems: 'center' },
- title: { fontSize: 24, fontWeight: 'bold' },
- body: { color: '#4b5563', fontSize: 15, marginTop: 10 },
+ container: {justifyContent: 'center', alignItems: 'center'},
+ title: {fontSize: 24, fontWeight: 'bold'},
+ body: {color: '#4b5563', fontSize: 15, marginTop: 10},
});
export default AdminHubScreen;
diff --git a/src/components/screens/CreateGameScreen.js b/src/components/screens/CreateGameScreen.js
index d00cdae..d5f35a7 100644
--- a/src/components/screens/CreateGameScreen.js
+++ b/src/components/screens/CreateGameScreen.js
@@ -2,17 +2,24 @@ import React from 'react';
import {StyleSheet, Text} from 'react-native';
import Screen from '../layout/Screen';
-const CreateGameScreen = () => (
-
- Create Game
- Create game setup goes here.
-
-);
+const CreateGameScreen = () => {
+// Initialisation -------------
+// State ----------------------
+// Handlers -------------------
+// View -----------------------
+
+ return (
+
+ Create Game
+ Create game setup goes here.
+
+ );
+};
const styles = StyleSheet.create({
- container: { justifyContent: 'center', alignItems: 'center' },
- title: { fontSize: 24, fontWeight: 'bold' },
- body: { color: '#4b5563', fontSize: 15, marginTop: 10 },
+ container: {justifyContent: 'center', alignItems: 'center'},
+ title: {fontSize: 24, fontWeight: 'bold'},
+ body: {color: '#4b5563', fontSize: 15, marginTop: 10},
});
export default CreateGameScreen;
diff --git a/src/components/screens/GameLobbyScreen.js b/src/components/screens/GameLobbyScreen.js
index 6312cc6..1d726c7 100644
--- a/src/components/screens/GameLobbyScreen.js
+++ b/src/components/screens/GameLobbyScreen.js
@@ -2,17 +2,24 @@ import React from 'react';
import {StyleSheet, Text} from 'react-native';
import Screen from '../layout/Screen';
-const GameLobbyScreen = () => (
-
- Game Lobby
- Game lobby setup goes here.
-
-);
+const GameLobbyScreen = () => {
+// Initialisation -------------
+// State ----------------------
+// Handlers -------------------
+// View -----------------------
+
+ return (
+
+ Game Lobby
+ Game lobby setup goes here.
+
+ );
+};
const styles = StyleSheet.create({
- container: { justifyContent: 'center', alignItems: 'center' },
- title: { fontSize: 24, fontWeight: 'bold' },
- body: { color: '#4b5563', fontSize: 15, marginTop: 10 },
+ container: {justifyContent: 'center', alignItems: 'center'},
+ title: {fontSize: 24, fontWeight: 'bold'},
+ body: {color: '#4b5563', fontSize: 15, marginTop: 10},
});
export default GameLobbyScreen;
diff --git a/src/components/screens/GameTypeScreen.js b/src/components/screens/GameTypeScreen.js
index 8a3ef5d..f16a462 100644
--- a/src/components/screens/GameTypeScreen.js
+++ b/src/components/screens/GameTypeScreen.js
@@ -3,66 +3,71 @@ import {Pressable, StyleSheet, Text, View} from 'react-native';
import useGameHook from '../../hooks/useGameHook';
import Screen from '../layout/Screen';
-const GameTypeScreen = ({ navigation }) => {
- const { getGameTypes } = useGameHook();
- const [gameTypes, setGameTypes] = useState([]);
+const GameTypeScreen = ({navigation}) => {
+// Initialisation ------------
- useEffect(() => {
- let isMounted = true;
+ const {getGameTypes} = useGameHook();
- const load = async () => {
- const types = await getGameTypes();
- if (isMounted) {
- setGameTypes(types || []);
- }
- };
+// State ----------------------
- load();
+ const [gameTypes, setGameTypes] = useState([]);
- return () => {
- isMounted = false;
- };
- }, [getGameTypes]);
+// Handlers -------------------
- return (
-
- Select Game Type
-
- {gameTypes && gameTypes.map((type) => (
- navigation.navigate('GameLobbyScreen')}>
- {type}
-
- ))}
-
-
- );
+ useEffect(() => {
+ let isMounted = true;
+ const load = async () => {
+ const types = await getGameTypes();
+ if (isMounted) setGameTypes(types || []);
+ };
+ load();
+ return () => { isMounted = false; };
+ }, []);
+
+// View -----------------------
+
+ return (
+
+ Select Game Type
+
+ {gameTypes && gameTypes.map((type) => (
+ navigation.navigate('GameLobbyScreen')}
+ >
+ {type}
+
+ ))}
+
+
+ );
};
const styles = StyleSheet.create({
- container: {
- justifyContent: 'center',
- },
- content: {
- gap: 12,
- flex: 1,
- },
- title: {
- fontSize: 24,
- fontWeight: 'bold',
- textAlign: 'center',
- marginBottom: 20,
- },
- button: {
- backgroundColor: '#2563eb',
- borderRadius: 8,
- paddingVertical: 12,
- alignItems: 'center',
- },
- buttonText: {
- color: '#ffffff',
- fontWeight: '600',
- },
+ container: {
+ justifyContent: 'center',
+ },
+ content: {
+ gap: 12,
+ flex: 1,
+ },
+ title: {
+ fontSize: 24,
+ fontWeight: 'bold',
+ textAlign: 'center',
+ marginBottom: 20,
+ },
+ button: {
+ backgroundColor: '#2563eb',
+ borderRadius: 8,
+ paddingVertical: 12,
+ alignItems: 'center',
+ },
+ buttonText: {
+ color: '#ffffff',
+ fontWeight: '600',
+ },
});
export default GameTypeScreen;
-
diff --git a/src/components/screens/JoinPrivateGameScreen.js b/src/components/screens/JoinPrivateGameScreen.js
index 4579833..e2c90e1 100644
--- a/src/components/screens/JoinPrivateGameScreen.js
+++ b/src/components/screens/JoinPrivateGameScreen.js
@@ -3,6 +3,11 @@ import {StyleSheet, Text} from 'react-native';
import Screen from '../layout/Screen';
const JoinPrivateGameScreen = () => {
+ // Initialisation -------------
+ // State ----------------------
+ // Handlers -------------------
+ // View -----------------------
+
return (
Join Private Game
diff --git a/src/components/screens/LeaderboardScreen.js b/src/components/screens/LeaderboardScreen.js
index 79ff6a4..4ed2214 100644
--- a/src/components/screens/LeaderboardScreen.js
+++ b/src/components/screens/LeaderboardScreen.js
@@ -2,18 +2,24 @@ import React from 'react';
import {StyleSheet, Text} from 'react-native';
import Screen from '../layout/Screen';
-const LeaderboardScreen = () => (
-
- Leaderboard
- Leaderboard view goes here.
-
-);
+const LeaderboardScreen = () => {
+// Initialisation -------------
+// State ----------------------
+// Handlers -------------------
+// View -----------------------
+
+ return (
+
+ Leaderboard
+ Leaderboard view goes here.
+
+ );
+};
const styles = StyleSheet.create({
- container: { justifyContent: 'center', alignItems: 'center' },
- title: { fontSize: 24, fontWeight: 'bold' },
- body: { color: '#4b5563', fontSize: 15, marginTop: 10 },
+ container: {justifyContent: 'center', alignItems: 'center'},
+ title: {fontSize: 24, fontWeight: 'bold'},
+ body: {color: '#4b5563', fontSize: 15, marginTop: 10},
});
export default LeaderboardScreen;
-
diff --git a/src/components/screens/LoginScreen.js b/src/components/screens/LoginScreen.js
index 06172b3..a764f8d 100644
--- a/src/components/screens/LoginScreen.js
+++ b/src/components/screens/LoginScreen.js
@@ -5,9 +5,16 @@ import Screen from '../layout/Screen';
import {Button, ButtonTray} from '../UI/Button';
const LoginScreen = ({ navigation }) => {
+// Initialisation ------------->
+
const { email, password, error, isLoading, setEmail, setPassword, login } = useAuthHook();
+
+// State ----------------------
+
const [submitting, setSubmitting] = useState(false);
+// Handlers -------------------
+
const handleLogin = async () => {
setSubmitting(true);
const user = await login();
@@ -22,6 +29,8 @@ const LoginScreen = ({ navigation }) => {
navigation.reset({ index: 0, routes: [{ name: 'Game' }] });
};
+// View -----------------------
+
return (
@@ -107,4 +116,3 @@ const styles = StyleSheet.create({
});
export default LoginScreen;
-
diff --git a/src/components/screens/ManageGameScreen.js b/src/components/screens/ManageGameScreen.js
index 3bb235b..01201c1 100644
--- a/src/components/screens/ManageGameScreen.js
+++ b/src/components/screens/ManageGameScreen.js
@@ -7,163 +7,138 @@ import useGameHook from '../../hooks/useGameHook';
import {getSession} from '../../hooks/SessionStore';
const ManageGameScreen = () => {
- const session = getSession();
- const { getCaches, upsertCache } = useGameHook();
- const [userLocation, setUserLocation] = useState(null);
- const [loading, setLoading] = useState(true);
- const [error, setError] = useState('');
- const [cacheRecords, setCacheRecords] = useState([]);
+// Initialisation ------------
- const loadCaches = useCallback(async () => {
- if (!session.currentGid) {
- setCacheRecords([]);
- return;
- }
- const rows = await getCaches(session.currentGid, null);
- setCacheRecords(rows || []);
- }, [getCaches, session.currentGid]);
+ const session = getSession();
+ const {getCaches, upsertCache} = useGameHook();
- useEffect(() => {
- loadCaches();
- }, [loadCaches]);
+// State ----------------------
- useEffect(() => {
- let locationSub;
+ const [userLocation, setUserLocation] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState('');
+ const [cacheRecords, setCacheRecords] = useState([]);
- const start = async () => {
- const { status } = await Location.requestForegroundPermissionsAsync();
- if (status !== 'granted') {
- setError('Location permission denied');
- setLoading(false);
- return;
- }
+// Handlers -------------------
- const current = await Location.getCurrentPositionAsync({ accuracy: Location.Accuracy.Balanced });
- setUserLocation({ latitude: current.coords.latitude, longitude: current.coords.longitude });
+ const loadCaches = useCallback(async () => {
+ if (!session.currentGid) {
+ setCacheRecords([]);
+ return;
+ }
+ const rows = await getCaches(session.currentGid, null);
+ setCacheRecords(rows || []);
+ }, [session.currentGid]);
- locationSub = await Location.watchPositionAsync(
- {
- accuracy: Location.Accuracy.Balanced,
- distanceInterval: 1,
- timeInterval: 1000,
- },
- (next) => {
- setUserLocation({ latitude: next.coords.latitude, longitude: next.coords.longitude });
- },
- );
+ useEffect(() => { loadCaches(); }, [loadCaches]);
- setLoading(false);
+ useEffect(() => {
+ let locationSub;
+ const start = async () => {
+ const {status} = await Location.requestForegroundPermissionsAsync();
+ if (status !== 'granted') {
+ setError('Location permission denied');
+ setLoading(false);
+ return;
+ }
+ const current = await Location.getCurrentPositionAsync({accuracy: Location.Accuracy.Balanced});
+ setUserLocation({latitude: current.coords.latitude, longitude: current.coords.longitude});
+
+ locationSub = await Location.watchPositionAsync(
+ {accuracy: Location.Accuracy.Balanced, distanceInterval: 1, timeInterval: 1000},
+ (next) => {
+ setUserLocation({latitude: next.coords.latitude, longitude: next.coords.longitude});
+ },
+ );
+ setLoading(false);
+ };
+ start();
+ return () => { if (locationSub) locationSub.remove(); };
+ }, []);
+
+ const handleAddCache = async (coordinate) => {
+ if (!session.currentGid) return;
+ await upsertCache({
+ gid: session.currentGid,
+ latitude: coordinate.latitude,
+ longitude: coordinate.longitude,
+ radius: 20,
+ clue: `Cache ${cacheRecords.length + 1}`,
+ subgroupId: 1,
+ });
+ await loadCaches();
};
- start();
-
- return () => {
- if (locationSub) {
- locationSub.remove();
- }
+ const handleMoveCache = async (cacheId, coordinate) => {
+ if (!session.currentGid) return;
+ const current = cacheRecords.find((cache) => cache.id === cacheId);
+ if (!current) return;
+ await upsertCache({
+ gid: session.currentGid,
+ cacheId,
+ latitude: coordinate.latitude,
+ longitude: coordinate.longitude,
+ radius: current.radius,
+ clue: current.clue,
+ subgroupId: current.subgroupId,
+ });
+ await loadCaches();
};
- }, []);
- const handleAddCache = async (coordinate) => {
+// View -----------------------
+
if (!session.currentGid) {
- return;
+ return (
+
+ Create or join a game first.
+
+ );
}
- await upsertCache({
- gid: session.currentGid,
- latitude: coordinate.latitude,
- longitude: coordinate.longitude,
- radius: 20,
- clue: `Cache ${cacheRecords.length + 1}`,
- subgroupId: 1,
- });
- await loadCaches();
- };
- const handleMoveCache = async (cacheId, coordinate) => {
- if (!session.currentGid) {
- return;
+ if (!session.isAcceptedAdmin) {
+ return (
+
+ Only admins can edit cache locations.
+
+ );
}
- const current = cacheRecords.find((cache) => cache.id === cacheId);
- if (!current) {
- return;
+
+ if (loading) {
+ return (
+
+
+
+ );
+ }
+
+ if (error) {
+ return (
+
+ {error}
+
+ );
}
- await upsertCache({
- gid: session.currentGid,
- cacheId,
- latitude: coordinate.latitude,
- longitude: coordinate.longitude,
- radius: current.radius,
- clue: current.clue,
- subgroupId: current.subgroupId,
- });
- await loadCaches();
- };
- if (!session.currentGid) {
return (
-
- Create or join a game first.
-
+
+
+
+
+
);
- }
-
- if (!session.isAcceptedAdmin) {
- return (
-
- Only admins can edit cache locations.
-
- );
- }
-
- if (loading) {
- return (
-
-
-
- );
- }
-
- if (error) {
- return (
-
- {error}
-
- );
- }
-
- return (
-
-
-
-
-
- );
};
const styles = StyleSheet.create({
- container: {
- padding: 0,
- },
- mapWrap: {
- flex: 1,
- },
- center: {
- justifyContent: 'center',
- alignItems: 'center',
- },
- body: {
- color: '#4b5563',
- fontSize: 15,
- },
- error: {
- color: '#dc2626',
- fontSize: 15,
- },
+ container: {padding: 0},
+ mapWrap: {flex: 1},
+ center: {justifyContent: 'center', alignItems: 'center'},
+ body: {color: '#4b5563', fontSize: 15},
+ error: {color: '#dc2626', fontSize: 15},
});
export default ManageGameScreen;
diff --git a/src/components/screens/ManagePrivateGameScreen.js b/src/components/screens/ManagePrivateGameScreen.js
index 3de6a59..f8b1f24 100644
--- a/src/components/screens/ManagePrivateGameScreen.js
+++ b/src/components/screens/ManagePrivateGameScreen.js
@@ -3,18 +3,23 @@ import {StyleSheet, Text} from 'react-native';
import Screen from '../layout/Screen';
const ManagePrivateGameScreen = () => {
- return (
-
- Manage Private Game
- Manage private game setup goes here.
-
- );
+// Initialisation -------------
+// State ----------------------
+// Handlers -------------------
+// View -----------------------
+
+ return (
+
+ Manage Private Game
+ Manage private game setup goes here.
+
+ );
};
const styles = StyleSheet.create({
- container: { justifyContent: 'center', alignItems: 'center' },
- title: { fontSize: 24, fontWeight: 'bold' },
- body: { color: '#4b5563', fontSize: 15, marginTop: 10 },
+ container: {justifyContent: 'center', alignItems: 'center'},
+ title: {fontSize: 24, fontWeight: 'bold'},
+ body: {color: '#4b5563', fontSize: 15, marginTop: 10},
});
export default ManagePrivateGameScreen;
diff --git a/src/components/screens/ManageSubgroupsScreen.js b/src/components/screens/ManageSubgroupsScreen.js
index 3cdde37..1df2523 100644
--- a/src/components/screens/ManageSubgroupsScreen.js
+++ b/src/components/screens/ManageSubgroupsScreen.js
@@ -2,17 +2,24 @@ import React from 'react';
import {StyleSheet, Text} from 'react-native';
import Screen from '../layout/Screen';
-const ManageSubgroupsScreen = () => (
-
- Manage Subgroups
- Manage subgroups setup goes here.
-
-);
+const ManageSubgroupsScreen = () => {
+// Initialisation -------------
+// State ----------------------
+// Handlers -------------------
+// View -----------------------
+
+ return (
+
+ Manage Subgroups
+ Manage subgroups setup goes here.
+
+ );
+};
const styles = StyleSheet.create({
- container: { justifyContent: 'center', alignItems: 'center' },
- title: { fontSize: 24, fontWeight: 'bold' },
- body: { color: '#4b5563', fontSize: 15, marginTop: 10 },
+ container: {justifyContent: 'center', alignItems: 'center'},
+ title: {fontSize: 24, fontWeight: 'bold'},
+ body: {color: '#4b5563', fontSize: 15, marginTop: 10},
});
export default ManageSubgroupsScreen;
diff --git a/src/components/screens/MapScreen.js b/src/components/screens/MapScreen.js
index 8e1704b..97aaeb2 100644
--- a/src/components/screens/MapScreen.js
+++ b/src/components/screens/MapScreen.js
@@ -9,175 +9,153 @@ import usePlayerGame from '../../hooks/usePlayerGame';
import {getSession} from '../../hooks/SessionStore';
import Cache from '../../models/Cache';
-const toHeading = ({ x, y }) => {
- const angle = Math.atan2(y, x) * (180 / Math.PI);
- return angle < 0 ? angle + 360 : angle;
+const toHeading = ({x, y}) => {
+ const angle = Math.atan2(y, x) * (180 / Math.PI);
+ return angle < 0 ? angle + 360 : angle;
};
const MapScreen = () => {
- const session = getSession();
- const { getCaches, claimCache } = useGameHook();
- const [userLocation, setUserLocation] = useState(null);
- const [heading, setHeading] = useState(null);
- const [loading, setLoading] = useState(true);
- const [cacheRecords, setCacheRecords] = useState([]);
- const [error, setError] = useState('');
+// Initialisation ------------
- const activeCaches = useMemo(() => (
- cacheRecords.map(
- (cache) => new Cache(
- cache.id,
- cache.coordinates.latitude,
- cache.coordinates.longitude,
- cache.radius,
- cache.clue,
- cache.groupId,
- cache.subgroupId,
- ),
- )
- ), [cacheRecords]);
+ const session = getSession();
+ const {getCaches, claimCache} = useGameHook();
- const subgroupFilter = session.currentSGid;
- const { visibleCache, isClaiming, setIsClaiming } = usePlayerGame(userLocation, heading, activeCaches);
+// State ----------------------
- const loadCaches = useCallback(async () => {
- if (!session.currentGid) {
- setCacheRecords([]);
- return;
- }
- const rows = await getCaches(session.currentGid, subgroupFilter);
- setCacheRecords(rows || []);
- }, [getCaches, session.currentGid, subgroupFilter]);
+ const [userLocation, setUserLocation] = useState(null);
+ const [heading, setHeading] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [cacheRecords, setCacheRecords] = useState([]);
+ const [error, setError] = useState('');
- useEffect(() => {
- loadCaches();
- }, [loadCaches]);
+ const activeCaches = useMemo(() => (
+ cacheRecords.map((cache) => new Cache(
+ cache.id,
+ cache.coordinates.latitude,
+ cache.coordinates.longitude,
+ cache.radius,
+ cache.clue,
+ cache.groupId,
+ cache.subgroupId,
+ ))
+ ), [cacheRecords]);
- useEffect(() => {
- let locationSub;
- let headingSub;
+ const subgroupFilter = session.currentSGid;
+ const {visibleCache, isClaiming, setIsClaiming} = usePlayerGame(userLocation, heading, activeCaches);
- const start = async () => {
- const { status } = await Location.requestForegroundPermissionsAsync();
- if (status !== 'granted') {
- setError('Location permission denied');
- setLoading(false);
- return;
- }
+// Handlers -------------------
- const current = await Location.getCurrentPositionAsync({ accuracy: Location.Accuracy.Balanced });
- setUserLocation({ latitude: current.coords.latitude, longitude: current.coords.longitude });
+ const loadCaches = useCallback(async () => {
+ if (!session.currentGid) {
+ setCacheRecords([]);
+ return;
+ }
+ const rows = await getCaches(session.currentGid, subgroupFilter);
+ setCacheRecords(rows || []);
+ }, [session.currentGid, subgroupFilter]);
- locationSub = await Location.watchPositionAsync(
- {
- accuracy: Location.Accuracy.Balanced,
- distanceInterval: 1,
- timeInterval: 1000,
- },
- (next) => {
- setUserLocation({ latitude: next.coords.latitude, longitude: next.coords.longitude });
- },
- );
+ useEffect(() => { loadCaches(); }, [loadCaches]);
- Magnetometer.setUpdateInterval(500);
- headingSub = Magnetometer.addListener((data) => {
- setHeading(toHeading(data));
- });
+ useEffect(() => {
+ let locationSub;
+ let headingSub;
- setLoading(false);
+ const start = async () => {
+ const {status} = await Location.requestForegroundPermissionsAsync();
+ if (status !== 'granted') {
+ setError('Location permission denied');
+ setLoading(false);
+ return;
+ }
+ const current = await Location.getCurrentPositionAsync({accuracy: Location.Accuracy.Balanced});
+ setUserLocation({latitude: current.coords.latitude, longitude: current.coords.longitude});
+
+ locationSub = await Location.watchPositionAsync(
+ {accuracy: Location.Accuracy.Balanced, distanceInterval: 1, timeInterval: 1000},
+ (next) => {
+ setUserLocation({latitude: next.coords.latitude, longitude: next.coords.longitude});
+ },
+ );
+
+ Magnetometer.setUpdateInterval(500);
+ headingSub = Magnetometer.addListener((data) => { setHeading(toHeading(data)); });
+ setLoading(false);
+ };
+
+ start();
+
+ return () => {
+ if (locationSub) locationSub.remove();
+ if (headingSub) headingSub.remove();
+ };
+ }, []);
+
+ const handleClaim = async (cacheId) => {
+ if (!session.currentGid) return;
+ await claimCache({
+ gid: session.currentGid,
+ cacheId,
+ uid: session.currentUid,
+ tid: session.currentTid,
+ });
+ setIsClaiming(false);
+ await loadCaches();
};
- start();
+// View -----------------------
- return () => {
- if (locationSub) {
- locationSub.remove();
- }
- if (headingSub) {
- headingSub.remove();
- }
- };
- }, []);
-
- const handleClaim = async (cacheId) => {
if (!session.currentGid) {
- return;
+ return (
+
+ Join or create a game to use map features.
+
+ );
+ }
+
+ if (session.isAcceptedAdmin) {
+ return (
+
+ Admins manage caches from Manage Game.
+
+ );
+ }
+
+ if (loading) {
+ return (
+
+
+
+ );
+ }
+
+ if (error) {
+ return (
+
+ {error}
+
+ );
}
- await claimCache({
- gid: session.currentGid,
- cacheId,
- uid: session.currentUid,
- tid: session.currentTid,
- });
- setIsClaiming(false);
- await loadCaches();
- };
- if (!session.currentGid) {
return (
-
- Join or create a game to use map features.
-
+
+
+
+
+
);
- }
-
- if (session.isAcceptedAdmin) {
- return (
-
- Admins manage caches from Manage Game.
-
- );
- }
-
- if (loading) {
- return (
-
-
-
- );
- }
-
- if (error) {
- return (
-
- {error}
-
- );
- }
-
- return (
-
-
-
-
-
- );
};
const styles = StyleSheet.create({
- container: {
- padding: 0,
- },
- mapWrap: {
- flex: 1,
- },
- center: {
- justifyContent: 'center',
- alignItems: 'center',
- },
- body: {
- color: '#4b5563',
- fontSize: 15,
- },
- error: {
- color: '#dc2626',
- fontSize: 15,
- },
+ container: {padding: 0},
+ mapWrap: {flex: 1},
+ center: {justifyContent: 'center', alignItems: 'center'},
+ body: {color: '#4b5563', fontSize: 15},
+ error: {color: '#dc2626', fontSize: 15},
});
export default MapScreen;
diff --git a/src/components/screens/RegisterScreen.js b/src/components/screens/RegisterScreen.js
index a575459..7883d85 100644
--- a/src/components/screens/RegisterScreen.js
+++ b/src/components/screens/RegisterScreen.js
@@ -4,175 +4,177 @@ import useAuthHook from '../../hooks/useAuthHook';
import Screen from '../layout/Screen';
import {Button, ButtonTray} from '../UI/Button';
-const RegisterScreen = ({ navigation }) => {
- const {
- username,
- email,
- password,
- confirmPassword,
- accountType,
- error,
- isLoading,
- setUsername,
- setEmail,
- setPassword,
- setConfirmPassword,
- setAccountType,
- register,
- } = useAuthHook();
- const [submitting, setSubmitting] = useState(false);
+const RegisterScreen = ({navigation}) => {
+// Initialisation ------------
- const handleRegister = async () => {
- setSubmitting(true);
- const user = await register();
- setSubmitting(false);
- if (!user) {
- return;
- }
- if (user.IsAcceptedAdmin) {
- navigation.reset({ index: 0, routes: [{ name: 'Admin' }] });
- return;
- }
- navigation.reset({ index: 0, routes: [{ name: 'Game' }] });
- };
+ const {
+ username, email, password, confirmPassword, accountType,
+ error, isLoading,
+ setUsername, setEmail, setPassword, setConfirmPassword, setAccountType,
+ register,
+ } = useAuthHook();
- return (
-
-
- Create Account
+// State ----------------------
-
-
-
-
+ const [submitting, setSubmitting] = useState(false);
-
- setAccountType('Individual')}
- disabled={isLoading}
- >
- Individual
-
- setAccountType('Business/School')}
- disabled={isLoading}
- >
- Business
-
-
+// Handlers -------------------
- {error ? {error} : null}
+ const handleRegister = async () => {
+ setSubmitting(true);
+ const user = await register();
+ setSubmitting(false);
+ if (!user) return;
+ if (user.IsAcceptedAdmin) {
+ navigation.reset({index: 0, routes: [{name: 'Admin'}]});
+ return;
+ }
+ navigation.reset({index: 0, routes: [{name: 'Game'}]});
+ };
-
-
-
+// View -----------------------
- navigation.navigate('Auth', { screen: 'LoginScreen' })}>
- Back to Login
-
-
-
- );
+ return (
+
+
+ Create Account
+
+
+
+
+
+
+
+ setAccountType('Individual')}
+ disabled={isLoading}
+ >
+
+ Individual
+
+
+ setAccountType('Business/School')}
+ disabled={isLoading}
+ >
+
+ Business
+
+
+
+
+ {error ? {error} : null}
+
+
+
+
+
+ navigation.navigate('Auth', {screen: 'LoginScreen'})}>
+ Back to Login
+
+
+
+ );
};
const styles = StyleSheet.create({
- container: {
- justifyContent: 'center',
- },
- content: {
- gap: 12,
- },
- title: {
- fontSize: 24,
- fontWeight: 'bold',
- textAlign: 'center',
- marginBottom: 10,
- },
- input: {
- borderWidth: 1,
- borderColor: '#d1d5db',
- borderRadius: 8,
- paddingHorizontal: 12,
- paddingVertical: 10,
- fontSize: 16,
- },
- typeRow: {
- flexDirection: 'row',
- gap: 10,
- },
- typeButton: {
- flex: 1,
- borderWidth: 1,
- borderColor: '#cbd5e1',
- borderRadius: 8,
- alignItems: 'center',
- paddingVertical: 10,
- },
- typeButtonActive: {
- backgroundColor: '#2563eb',
- borderColor: '#2563eb',
- },
- typeText: {
- color: '#1f2937',
- fontWeight: '600',
- },
- typeTextActive: {
- color: '#ffffff',
- },
- errorText: {
- color: '#dc2626',
- textAlign: 'center',
- marginTop: 5,
- },
- submitButton: {
- backgroundColor: '#2563eb',
- borderColor: '#2563eb',
- },
- submitText: {
- color: '#ffffff',
- fontWeight: '600',
- },
- linkText: {
- textAlign: 'center',
- color: '#2563eb',
- fontWeight: '600',
- paddingVertical: 10,
- },
+ container: {
+ justifyContent: 'center',
+ },
+ content: {
+ gap: 12,
+ },
+ title: {
+ fontSize: 24,
+ fontWeight: 'bold',
+ textAlign: 'center',
+ marginBottom: 10,
+ },
+ input: {
+ borderWidth: 1,
+ borderColor: '#d1d5db',
+ borderRadius: 8,
+ paddingHorizontal: 12,
+ paddingVertical: 10,
+ fontSize: 16,
+ },
+ typeRow: {
+ flexDirection: 'row',
+ gap: 10,
+ },
+ typeButton: {
+ flex: 1,
+ borderWidth: 1,
+ borderColor: '#cbd5e1',
+ borderRadius: 8,
+ alignItems: 'center',
+ paddingVertical: 10,
+ },
+ typeButtonActive: {
+ backgroundColor: '#2563eb',
+ borderColor: '#2563eb',
+ },
+ typeText: {
+ color: '#1f2937',
+ fontWeight: '600',
+ },
+ typeTextActive: {
+ color: '#ffffff',
+ },
+ errorText: {
+ color: '#dc2626',
+ textAlign: 'center',
+ marginTop: 5,
+ },
+ submitButton: {
+ backgroundColor: '#2563eb',
+ borderColor: '#2563eb',
+ },
+ submitText: {
+ color: '#ffffff',
+ fontWeight: '600',
+ },
+ linkText: {
+ textAlign: 'center',
+ color: '#2563eb',
+ fontWeight: '600',
+ paddingVertical: 10,
+ },
});
export default RegisterScreen;
diff --git a/src/components/screens/TeamScreen.js b/src/components/screens/TeamScreen.js
index d77b6d5..2703947 100644
--- a/src/components/screens/TeamScreen.js
+++ b/src/components/screens/TeamScreen.js
@@ -2,17 +2,24 @@ import React from 'react';
import {StyleSheet, Text} from 'react-native';
import Screen from '../layout/Screen';
-const TeamScreen = () => (
-
- Team Management
- Team management setup goes here.
-
-);
+const TeamScreen = () => {
+// Initialisation -------------
+// State ----------------------
+// Handlers -------------------
+// View -----------------------
+
+ return (
+
+ Team Management
+ Team management setup goes here.
+
+ );
+};
const styles = StyleSheet.create({
- container: { justifyContent: 'center', alignItems: 'center' },
- title: { fontSize: 24, fontWeight: 'bold' },
- body: { color: '#4b5563', fontSize: 15, marginTop: 10 },
+ container: {justifyContent: 'center', alignItems: 'center'},
+ title: {fontSize: 24, fontWeight: 'bold'},
+ body: {color: '#4b5563', fontSize: 15, marginTop: 10},
});
export default TeamScreen;
diff --git a/src/hooks/SessionStore.js b/src/hooks/SessionStore.js
index ee1f591..ab5ec12 100644
--- a/src/hooks/SessionStore.js
+++ b/src/hooks/SessionStore.js
@@ -1,3 +1,5 @@
+// --- Session Store ---
+
const session = {
currentUid: null,
currentGid: null,
@@ -7,6 +9,8 @@ const session = {
isAcceptedAdmin: false,
};
+// --- Handlers ---
+
const setSessionUser = (user) => {
session.currentUid = user?.Uid ?? null;
session.currentGid = user?.Gid ?? null;
diff --git a/src/hooks/dbLink.js b/src/hooks/dbLink.js
index 755ea82..5ad9594 100644
--- a/src/hooks/dbLink.js
+++ b/src/hooks/dbLink.js
@@ -1,22 +1,3 @@
-import Constants from 'expo-constants';
+const API_BASE_URL = 'http://localhost:3000';
-const hostFromExpo = Constants.expoConfig?.hostUri?.split(':')[0] || null;
-const apiBaseUrl = 'http://localhost:3000'; // Shirish, i need to deploy the database first so... wait until that... sorry buddy...
-// if you want you can run your own db locally i can send you the files for it just ask :(((
-
-const postJson = async (path, payload) => {
- const response = await fetch(`${apiBaseUrl}${path}`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(payload),
- });
- const result = await response.json();
- if (!response.ok) {
- throw new Error(result.message || 'Request failed');
- }
- return result;
-};
-
-export const postDb = async (action, payload = {}) => {
- return postJson('/db', { action, payload });
-};
+export default API_BASE_URL;
diff --git a/src/hooks/useAuthHook.js b/src/hooks/useAuthHook.js
index 3e2e010..ec7aff4 100644
--- a/src/hooks/useAuthHook.js
+++ b/src/hooks/useAuthHook.js
@@ -1,8 +1,16 @@
import {useState} from 'react';
-import {postDb} from './dbLink';
+import API from '../components/API/API';
+import API_BASE_URL from './dbLink';
import {clearSession, setSessionUser} from './SessionStore';
const useAuthHook = () => {
+ // Initialisation ------------
+
+ const loginEndpoint = `${API_BASE_URL}/auth/login`;
+ const registerEndpoint = `${API_BASE_URL}/auth/register`;
+
+ // State ----------------------
+
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [username, setUsername] = useState('');
@@ -11,40 +19,41 @@ const useAuthHook = () => {
const [error, setError] = useState('');
const [isLoading, setIsLoading] = useState(false);
+ // Handlers -------------------
+
const login = async () => {
setError('');
setIsLoading(true);
- try {
- const user = await postDb('login', { email: email.trim(), password });
- setSessionUser(user);
- setIsLoading(false);
- return user;
- } catch (e) {
- setError(e.message);
- setIsLoading(false);
- return null;
+ const response = await API.post(loginEndpoint, {
+ email: email.trim(),
+ password,
+ });
+ setIsLoading(false);
+ if (response.isSuccess) {
+ setSessionUser(response.result);
+ return response.result;
}
+ setError(response.message);
+ return null;
};
const register = async () => {
setError('');
setIsLoading(true);
- try {
- const user = await postDb('register', {
- username: username.trim(),
- email: email.trim().toLowerCase(),
- password,
- confirmPassword,
- accountType,
- });
- setSessionUser(user);
- setIsLoading(false);
- return user;
- } catch (e) {
- setError(e.message);
- setIsLoading(false);
- return null;
+ const response = await API.post(registerEndpoint, {
+ username: username.trim(),
+ email: email.trim().toLowerCase(),
+ password,
+ confirmPassword,
+ accountType,
+ });
+ setIsLoading(false);
+ if (response.isSuccess) {
+ setSessionUser(response.result);
+ return response.result;
}
+ setError(response.message);
+ return null;
};
const logout = () => {
@@ -57,6 +66,8 @@ const useAuthHook = () => {
setError('');
};
+ // Return ---------------------
+
return {
email,
password,
diff --git a/src/hooks/useGameHook.js b/src/hooks/useGameHook.js
index 082f874..7a36e8c 100644
--- a/src/hooks/useGameHook.js
+++ b/src/hooks/useGameHook.js
@@ -1,33 +1,102 @@
-import {postDb} from './dbLink';
+import API from '../components/API/API';
+import API_BASE_URL from './dbLink';
const useGameHook = () => {
- const request = async (action, payload = {}) => postDb(action, payload);
+ // Initialisation ------------
- const getGameTypes = async () => request('getGameTypes');
+ const gameTypesEndpoint = `${API_BASE_URL}/game-types`;
+ const groupsEndpoint = `${API_BASE_URL}/groups`;
+ const subgroupMembershipsEndpoint = `${API_BASE_URL}/subgroup-memberships`;
+ const teamsEndpoint = `${API_BASE_URL}/teams`;
+ const teamMembersEndpoint = `${API_BASE_URL}/team-members`;
+ const gameDataEndpoint = `${API_BASE_URL}/game-data`;
+ const adminWaitlistEndpoint = `${API_BASE_URL}/admin-waitlist`;
- const getCreatedPrivateGame = async (userId) => request('getCreatedPrivateGameByUser', { userId });
+ // Handlers -------------------
- const createPrivateGame = async (payload) => request('createPrivateGame', payload);
+ const getGameTypes = async () => {
+ const response = await API.get(gameTypesEndpoint);
+ return response.isSuccess ? response.result : [];
+ };
- const joinPrivateGame = async (payload) => request('joinPrivateGame', payload);
+ const getCreatedPrivateGame = async (userId) => {
+ const response = await API.get(`${groupsEndpoint}?CreatedByUid=${userId}`);
+ return response.isSuccess ? response.result : [];
+ };
- const joinAsAdmin = async (payload) => request('joinAsAdmin', payload);
+ const createPrivateGame = async (payload) => {
+ const response = await API.post(groupsEndpoint, payload);
+ return response.isSuccess ? response.result : null;
+ };
- const getLobby = async (gid) => request('getLobby', { gid });
+ const joinPrivateGame = async (payload) => {
+ const response = await API.post(subgroupMembershipsEndpoint, payload);
+ return response.isSuccess ? response.result : null;
+ };
- const getTeam = async (tid) => request('getTeam', { tid });
+ const joinAsAdmin = async (payload) => {
+ const response = await API.post(adminWaitlistEndpoint, payload);
+ return response.isSuccess ? response.result : null;
+ };
- const createTeam = async (payload) => request('createTeam', payload);
+ const getLobby = async (gid) => {
+ const response = await API.get(`${groupsEndpoint}/${gid}`);
+ return response.isSuccess ? response.result : null;
+ };
- const joinTeamByCode = async (payload) => request('joinTeamByCode', payload);
+ const getTeam = async (tid) => {
+ const response = await API.get(`${teamsEndpoint}/${tid}`);
+ return response.isSuccess ? response.result : null;
+ };
- const getMapPoints = async (gid, sgid = null) => request('getMapPoints', { gid, sgid });
+ const createTeam = async (payload) => {
+ const response = await API.post(teamsEndpoint, payload);
+ return response.isSuccess ? response.result : null;
+ };
- const getCaches = async (gid, sgid = null) => request('getCaches', { gid, sgid });
+ const joinTeamByCode = async (payload) => {
+ const response = await API.post(teamMembersEndpoint, payload);
+ return response.isSuccess ? response.result : null;
+ };
- const upsertCache = async (payload) => request('upsertCache', payload);
+ const getMapPoints = async (gid, sgid = null) => {
+ let url = `${gameDataEndpoint}/${gid}/caches`;
+ if (sgid !== null) url += `?SGid=${sgid}`;
+ const response = await API.get(url);
+ return response.isSuccess ? response.result : [];
+ };
- const claimCache = async (payload) => request('claimCache', payload);
+ const getCaches = async (gid, sgid = null) => {
+ let url = `${gameDataEndpoint}/${gid}/caches`;
+ if (sgid !== null) url += `?SGid=${sgid}`;
+ const response = await API.get(url);
+ return response.isSuccess ? response.result : [];
+ };
+
+ const upsertCache = async (payload) => {
+ if (payload.cacheId) {
+ const response = await API.put(
+ `${gameDataEndpoint}/${payload.gid}/caches/${payload.cacheId}`,
+ payload,
+ );
+ return response.isSuccess ? response.result : null;
+ }
+ const response = await API.post(
+ `${gameDataEndpoint}/${payload.gid}/caches`,
+ payload,
+ );
+ return response.isSuccess ? response.result : null;
+ };
+
+ const claimCache = async (payload) => {
+ const response = await API.put(
+ `${gameDataEndpoint}/${payload.gid}/caches/${payload.cacheId}`,
+ {ClaimedByUid: payload.uid, ClaimedByTid: payload.tid},
+ );
+ return response.isSuccess ? response.result : null;
+ };
+
+ // Return ---------------------
return {
getGameTypes,
diff --git a/src/hooks/usePlayerGame.js b/src/hooks/usePlayerGame.js
index a179780..a2140cc 100644
--- a/src/hooks/usePlayerGame.js
+++ b/src/hooks/usePlayerGame.js
@@ -2,36 +2,37 @@ import {useEffect, useState} from 'react';
import {isLookingAtCache, isWithinRadius} from '../utils/geoMath';
const usePlayerGame = (playerLocation, playerHeading, activeCaches) => {
- const [visibleCache, setVisibleCache] = useState(null);
- const [isClaiming, setIsClaiming] = useState(false);
+// State ----------------------
- useEffect(() => {
- if (!playerLocation || playerHeading === null || playerHeading === undefined) {
- setVisibleCache(null);
- setIsClaiming(false);
- return;
- }
+ const [visibleCache, setVisibleCache] = useState(null);
+ const [isClaiming, setIsClaiming] = useState(false);
- const nearbyCache = (activeCaches || []).find((cache) =>
- isWithinRadius(playerLocation, cache.coordinates, cache.radius));
+// Handlers -------------------
- if (!nearbyCache) {
- setVisibleCache(null);
- setIsClaiming(false);
- return;
- }
+ useEffect(() => {
+ if (!playerLocation || playerHeading === null || playerHeading === undefined) {
+ setVisibleCache(null);
+ setIsClaiming(false);
+ return;
+ }
- const isTargeted = isLookingAtCache(
- playerHeading,
- playerLocation,
- nearbyCache.coordinates,
- );
+ const nearbyCache = (activeCaches || []).find((cache) =>
+ isWithinRadius(playerLocation, cache.coordinates, cache.radius));
- setVisibleCache(nearbyCache);
- setIsClaiming(isTargeted);
- }, [playerLocation, playerHeading, activeCaches]);
+ if (!nearbyCache) {
+ setVisibleCache(null);
+ setIsClaiming(false);
+ return;
+ }
- return { visibleCache, isClaiming, setIsClaiming };
+ const isTargeted = isLookingAtCache(playerHeading, playerLocation, nearbyCache.coordinates);
+ setVisibleCache(nearbyCache);
+ setIsClaiming(isTargeted);
+ }, [playerLocation, playerHeading, activeCaches]);
+
+// Return ---------------------
+
+ return {visibleCache, isClaiming, setIsClaiming};
};
export default usePlayerGame;