Add comments for code structure clarity; enhance readability across multiple components

This commit is contained in:
bobbert committed 2026-04-05 18:07:45 +01:00
1 parent d67522a71d
commit 9b9f13eb6a
29 files changed
+1020 -846

No files matched your search

+16 -9
View File
@@ -2,17 +2,24 @@ import React from 'react';
import {StyleSheet, Text} from 'react-native';
import Screen from '../layout/Screen';
const AdminHubScreen = () => (
<Screen style={styles.container}>
<Text style={styles.title}>Admin Hub</Text>
<Text style={styles.body}>Admin hub setup goes here.</Text>
</Screen>
);
const AdminHubScreen = () => {
// Initialisation -------------
// State ----------------------
// Handlers -------------------
// View -----------------------
return (
<Screen style={styles.container}>
<Text style={styles.title}>Admin Hub</Text>
<Text style={styles.body}>Admin hub setup goes here.</Text>
</Screen>
);
};
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;
+16 -9
View File
@@ -2,17 +2,24 @@ import React from 'react';
import {StyleSheet, Text} from 'react-native';
import Screen from '../layout/Screen';
const CreateGameScreen = () => (
<Screen style={styles.container}>
<Text style={styles.title}>Create Game</Text>
<Text style={styles.body}>Create game setup goes here.</Text>
</Screen>
);
const CreateGameScreen = () => {
// Initialisation -------------
// State ----------------------
// Handlers -------------------
// View -----------------------
return (
<Screen style={styles.container}>
<Text style={styles.title}>Create Game</Text>
<Text style={styles.body}>Create game setup goes here.</Text>
</Screen>
);
};
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;
+16 -9
View File
@@ -2,17 +2,24 @@ import React from 'react';
import {StyleSheet, Text} from 'react-native';
import Screen from '../layout/Screen';
const GameLobbyScreen = () => (
<Screen style={styles.container}>
<Text style={styles.title}>Game Lobby</Text>
<Text style={styles.body}>Game lobby setup goes here.</Text>
</Screen>
);
const GameLobbyScreen = () => {
// Initialisation -------------
// State ----------------------
// Handlers -------------------
// View -----------------------
return (
<Screen style={styles.container}>
<Text style={styles.title}>Game Lobby</Text>
<Text style={styles.body}>Game lobby setup goes here.</Text>
</Screen>
);
};
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;
+57 -52
View File
@@ -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 (
<Screen style={styles.container}>
<Text style={styles.title}>Select Game Type</Text>
<View style={styles.content}>
{gameTypes && gameTypes.map((type) => (
<Pressable key={type} style={styles.button} onPress={() => navigation.navigate('GameLobbyScreen')}>
<Text style={styles.buttonText}>{type}</Text>
</Pressable>
))}
</View>
</Screen>
);
useEffect(() => {
let isMounted = true;
const load = async () => {
const types = await getGameTypes();
if (isMounted) setGameTypes(types || []);
};
load();
return () => { isMounted = false; };
}, []);
// View -----------------------
return (
<Screen style={styles.container}>
<Text style={styles.title}>Select Game Type</Text>
<View style={styles.content}>
{gameTypes && gameTypes.map((type) => (
<Pressable
key={type}
style={styles.button}
onPress={() => navigation.navigate('GameLobbyScreen')}
>
<Text style={styles.buttonText}>{type}</Text>
</Pressable>
))}
</View>
</Screen>
);
};
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;
@@ -3,6 +3,11 @@ import {StyleSheet, Text} from 'react-native';
import Screen from '../layout/Screen';
const JoinPrivateGameScreen = () => {
// Initialisation -------------
// State ----------------------
// Handlers -------------------
// View -----------------------
return (
<Screen style={styles.container}>
<Text style={styles.title}>Join Private Game</Text>
+16 -10
View File
@@ -2,18 +2,24 @@ import React from 'react';
import {StyleSheet, Text} from 'react-native';
import Screen from '../layout/Screen';
const LeaderboardScreen = () => (
<Screen style={styles.container}>
<Text style={styles.title}>Leaderboard</Text>
<Text style={styles.body}>Leaderboard view goes here.</Text>
</Screen>
);
const LeaderboardScreen = () => {
// Initialisation -------------
// State ----------------------
// Handlers -------------------
// View -----------------------
return (
<Screen style={styles.container}>
<Text style={styles.title}>Leaderboard</Text>
<Text style={styles.body}>Leaderboard view goes here.</Text>
</Screen>
);
};
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;
+9 -1
View File
@@ -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 (
<Screen style={styles.container}>
<View style={styles.content}>
@@ -107,4 +116,3 @@ const styles = StyleSheet.create({
});
export default LoginScreen;
+110 -135
View File
@@ -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 (
<Screen style={styles.center}>
<Text style={styles.body}>Create or join a game first.</Text>
</Screen>
);
}
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 (
<Screen style={styles.center}>
<Text style={styles.body}>Only admins can edit cache locations.</Text>
</Screen>
);
}
const current = cacheRecords.find((cache) => cache.id === cacheId);
if (!current) {
return;
if (loading) {
return (
<Screen style={styles.center}>
<ActivityIndicator size="large"/>
</Screen>
);
}
if (error) {
return (
<Screen style={styles.center}>
<Text style={styles.error}>{error}</Text>
</Screen>
);
}
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 (
<Screen style={styles.center}>
<Text style={styles.body}>Create or join a game first.</Text>
</Screen>
<Screen style={styles.container}>
<View style={styles.mapWrap}>
<AdminCacheEditorView
userLocation={userLocation}
caches={cacheRecords}
onAddCache={handleAddCache}
onMoveCache={handleMoveCache}
/>
</View>
</Screen>
);
}
if (!session.isAcceptedAdmin) {
return (
<Screen style={styles.center}>
<Text style={styles.body}>Only admins can edit cache locations.</Text>
</Screen>
);
}
if (loading) {
return (
<Screen style={styles.center}>
<ActivityIndicator size="large" />
</Screen>
);
}
if (error) {
return (
<Screen style={styles.center}>
<Text style={styles.error}>{error}</Text>
</Screen>
);
}
return (
<Screen style={styles.container}>
<View style={styles.mapWrap}>
<AdminCacheEditorView
userLocation={userLocation}
caches={cacheRecords}
onAddCache={handleAddCache}
onMoveCache={handleMoveCache}
/>
</View>
</Screen>
);
};
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;
@@ -3,18 +3,23 @@ import {StyleSheet, Text} from 'react-native';
import Screen from '../layout/Screen';
const ManagePrivateGameScreen = () => {
return (
<Screen style={styles.container}>
<Text style={styles.title}>Manage Private Game</Text>
<Text style={styles.body}>Manage private game setup goes here.</Text>
</Screen>
);
// Initialisation -------------
// State ----------------------
// Handlers -------------------
// View -----------------------
return (
<Screen style={styles.container}>
<Text style={styles.title}>Manage Private Game</Text>
<Text style={styles.body}>Manage private game setup goes here.</Text>
</Screen>
);
};
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;
@@ -2,17 +2,24 @@ import React from 'react';
import {StyleSheet, Text} from 'react-native';
import Screen from '../layout/Screen';
const ManageSubgroupsScreen = () => (
<Screen style={styles.container}>
<Text style={styles.title}>Manage Subgroups</Text>
<Text style={styles.body}>Manage subgroups setup goes here.</Text>
</Screen>
);
const ManageSubgroupsScreen = () => {
// Initialisation -------------
// State ----------------------
// Handlers -------------------
// View -----------------------
return (
<Screen style={styles.container}>
<Text style={styles.title}>Manage Subgroups</Text>
<Text style={styles.body}>Manage subgroups setup goes here.</Text>
</Screen>
);
};
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;
+123 -145
View File
@@ -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 (
<Screen style={styles.center}>
<Text style={styles.body}>Join or create a game to use map features.</Text>
</Screen>
);
}
if (session.isAcceptedAdmin) {
return (
<Screen style={styles.center}>
<Text style={styles.body}>Admins manage caches from Manage Game.</Text>
</Screen>
);
}
if (loading) {
return (
<Screen style={styles.center}>
<ActivityIndicator size="large"/>
</Screen>
);
}
if (error) {
return (
<Screen style={styles.center}>
<Text style={styles.error}>{error}</Text>
</Screen>
);
}
await claimCache({
gid: session.currentGid,
cacheId,
uid: session.currentUid,
tid: session.currentTid,
});
setIsClaiming(false);
await loadCaches();
};
if (!session.currentGid) {
return (
<Screen style={styles.center}>
<Text style={styles.body}>Join or create a game to use map features.</Text>
</Screen>
<Screen style={styles.container}>
<View style={styles.mapWrap}>
<PlayerMapView
userLocation={userLocation}
visibleCache={visibleCache}
isClaiming={isClaiming}
onClaimSuccess={handleClaim}
/>
</View>
</Screen>
);
}
if (session.isAcceptedAdmin) {
return (
<Screen style={styles.center}>
<Text style={styles.body}>Admins manage caches from Manage Game.</Text>
</Screen>
);
}
if (loading) {
return (
<Screen style={styles.center}>
<ActivityIndicator size="large" />
</Screen>
);
}
if (error) {
return (
<Screen style={styles.center}>
<Text style={styles.error}>{error}</Text>
</Screen>
);
}
return (
<Screen style={styles.container}>
<View style={styles.mapWrap}>
<PlayerMapView
userLocation={userLocation}
visibleCache={visibleCache}
isClaiming={isClaiming}
onClaimSuccess={handleClaim}
/>
</View>
</Screen>
);
};
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;
+161 -159
View File
@@ -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 (
<Screen style={styles.container}>
<View style={styles.content}>
<Text style={styles.title}>Create Account</Text>
// State ----------------------
<TextInput
style={styles.input}
placeholder="Username"
value={username}
onChangeText={setUsername}
editable={!isLoading}
/>
<TextInput
style={styles.input}
placeholder="Email"
autoCapitalize="none"
keyboardType="email-address"
value={email}
onChangeText={setEmail}
editable={!isLoading}
/>
<TextInput
style={styles.input}
placeholder="Password"
secureTextEntry
value={password}
onChangeText={setPassword}
editable={!isLoading}
/>
<TextInput
style={styles.input}
placeholder="Confirm Password"
secureTextEntry
value={confirmPassword}
onChangeText={setConfirmPassword}
editable={!isLoading}
/>
const [submitting, setSubmitting] = useState(false);
<View style={styles.typeRow}>
<Pressable
style={[styles.typeButton, accountType === 'Individual' && styles.typeButtonActive]}
onPress={() => setAccountType('Individual')}
disabled={isLoading}
>
<Text style={[styles.typeText, accountType === 'Individual' && styles.typeTextActive]}>Individual</Text>
</Pressable>
<Pressable
style={[styles.typeButton, accountType === 'Business/School' && styles.typeButtonActive]}
onPress={() => setAccountType('Business/School')}
disabled={isLoading}
>
<Text style={[styles.typeText, accountType === 'Business/School' && styles.typeTextActive]}>Business</Text>
</Pressable>
</View>
// Handlers -------------------
{error ? <Text style={styles.errorText}>{error}</Text> : 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'}]});
};
<ButtonTray>
<Button
label={submitting ? 'Creating...' : 'Create Account'}
onClick={handleRegister}
styleButton={styles.submitButton}
styleLabel={styles.submitText}
/>
</ButtonTray>
// View -----------------------
<Pressable onPress={() => navigation.navigate('Auth', { screen: 'LoginScreen' })}>
<Text style={styles.linkText}>Back to Login</Text>
</Pressable>
</View>
</Screen>
);
return (
<Screen style={styles.container}>
<View style={styles.content}>
<Text style={styles.title}>Create Account</Text>
<TextInput
style={styles.input}
placeholder="Username"
value={username}
onChangeText={setUsername}
editable={!isLoading}
/>
<TextInput
style={styles.input}
placeholder="Email"
autoCapitalize="none"
keyboardType="email-address"
value={email}
onChangeText={setEmail}
editable={!isLoading}
/>
<TextInput
style={styles.input}
placeholder="Password"
secureTextEntry
value={password}
onChangeText={setPassword}
editable={!isLoading}
/>
<TextInput
style={styles.input}
placeholder="Confirm Password"
secureTextEntry
value={confirmPassword}
onChangeText={setConfirmPassword}
editable={!isLoading}
/>
<View style={styles.typeRow}>
<Pressable
style={[styles.typeButton, accountType === 'Individual' && styles.typeButtonActive]}
onPress={() => setAccountType('Individual')}
disabled={isLoading}
>
<Text style={[styles.typeText, accountType === 'Individual' && styles.typeTextActive]}>
Individual
</Text>
</Pressable>
<Pressable
style={[styles.typeButton, accountType === 'Business/School' && styles.typeButtonActive]}
onPress={() => setAccountType('Business/School')}
disabled={isLoading}
>
<Text style={[styles.typeText, accountType === 'Business/School' && styles.typeTextActive]}>
Business
</Text>
</Pressable>
</View>
{error ? <Text style={styles.errorText}>{error}</Text> : null}
<ButtonTray>
<Button
label={submitting ? 'Creating...' : 'Create Account'}
onClick={handleRegister}
styleButton={styles.submitButton}
styleLabel={styles.submitText}
/>
</ButtonTray>
<Pressable onPress={() => navigation.navigate('Auth', {screen: 'LoginScreen'})}>
<Text style={styles.linkText}>Back to Login</Text>
</Pressable>
</View>
</Screen>
);
};
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;
+16 -9
View File
@@ -2,17 +2,24 @@ import React from 'react';
import {StyleSheet, Text} from 'react-native';
import Screen from '../layout/Screen';
const TeamScreen = () => (
<Screen style={styles.container}>
<Text style={styles.title}>Team Management</Text>
<Text style={styles.body}>Team management setup goes here.</Text>
</Screen>
);
const TeamScreen = () => {
// Initialisation -------------
// State ----------------------
// Handlers -------------------
// View -----------------------
return (
<Screen style={styles.container}>
<Text style={styles.title}>Team Management</Text>
<Text style={styles.body}>Team management setup goes here.</Text>
</Screen>
);
};
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;