mirror of
https://github.com/mudabbir-ahmad/MAD-Treasure-Hunt.git
synced 2026-10-07 19:00:20 +00:00
Update API base URL for local development; implement fetch timeout handling for improved request management
This commit is contained in:
1 parent
da7d5ea48d
commit
a326c784a5
4 files changed
+362
-38
No files matched your search
@@ -40,15 +40,15 @@ const GameStack = () => (
|
|||||||
contentStyle: { backgroundColor: '#ffffff' },
|
contentStyle: { backgroundColor: '#ffffff' },
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<GameStackNav.Screen name="MapScreen" component={MapScreen}/>
|
||||||
|
<GameStackNav.Screen name="TeamScreen" component={TeamScreen}/>
|
||||||
|
<GameStackNav.Screen name="LeaderboardScreen" component={LeaderboardScreen}/>
|
||||||
<GameStackNav.Screen name="GameTypeScreen" component={GameTypeScreen}/>
|
<GameStackNav.Screen name="GameTypeScreen" component={GameTypeScreen}/>
|
||||||
<GameStackNav.Screen name="JoinPrivateGameScreen" component={JoinPrivateGameScreen}/>
|
<GameStackNav.Screen name="JoinPrivateGameScreen" component={JoinPrivateGameScreen}/>
|
||||||
<GameStackNav.Screen name="ManagePrivateGameScreen" component={ManagePrivateGameScreen}/>
|
<GameStackNav.Screen name="ManagePrivateGameScreen" component={ManagePrivateGameScreen}/>
|
||||||
<GameStackNav.Screen name="CreateGameScreen" component={CreateGameScreen}/>
|
<GameStackNav.Screen name="CreateGameScreen" component={CreateGameScreen}/>
|
||||||
<GameStackNav.Screen name="ManageGameScreen" component={ManageGameScreen}/>
|
<GameStackNav.Screen name="ManageGameScreen" component={ManageGameScreen}/>
|
||||||
<GameStackNav.Screen name="GameLobbyScreen" component={GameLobbyScreen}/>
|
<GameStackNav.Screen name="GameLobbyScreen" component={GameLobbyScreen}/>
|
||||||
<GameStackNav.Screen name="LeaderboardScreen" component={LeaderboardScreen}/>
|
|
||||||
<GameStackNav.Screen name="TeamScreen" component={TeamScreen}/>
|
|
||||||
<GameStackNav.Screen name="MapScreen" component={MapScreen}/>
|
|
||||||
</GameStackNav.Navigator>
|
</GameStackNav.Navigator>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,49 +1,70 @@
|
|||||||
import React, {useCallback, useEffect, useState} from 'react';
|
import React, {useCallback, useEffect, useState} from 'react';
|
||||||
import {ActivityIndicator, StyleSheet, Text, View} from 'react-native';
|
import {ActivityIndicator, ScrollView, StyleSheet, Text, TextInput, View} from 'react-native';
|
||||||
import * as Location from 'expo-location';
|
import * as Location from 'expo-location';
|
||||||
import {Magnetometer} from 'expo-sensors';
|
import {Magnetometer} from 'expo-sensors';
|
||||||
import Screen from '../layout/Screen';
|
import Screen from '../layout/Screen';
|
||||||
|
import {Button, ButtonTray} from '../UI/Button';
|
||||||
import PlayerMapView from '../gameplay/PlayerMapView';
|
import PlayerMapView from '../gameplay/PlayerMapView';
|
||||||
import useGameHook from '../../hooks/useGameHook';
|
import useGameHook from '../../hooks/useGameHook';
|
||||||
import usePlayerGame from '../../hooks/usePlayerGame';
|
import usePlayerGame from '../../hooks/usePlayerGame';
|
||||||
import {getSession} from '../../hooks/SessionStore';
|
import {getSession, setSessionGroup, setSessionUser} from '../../hooks/SessionStore';
|
||||||
|
|
||||||
const toHeading = ({x, y}) => {
|
const toHeading = ({x, y}) => {
|
||||||
const angle = Math.atan2(y, x) * (180 / Math.PI);
|
const angle = Math.atan2(y, x) * (180 / Math.PI);
|
||||||
return angle < 0 ? angle + 360 : angle;
|
return angle < 0 ? angle + 360 : angle;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MapScreen = () => {
|
const MapScreen = ({navigation}) => {
|
||||||
// Initialisation ------------
|
// Initialisation ------------
|
||||||
|
|
||||||
const session = getSession();
|
const session = getSession();
|
||||||
const {getCaches, claimCache} = useGameHook();
|
const {getCaches, claimCache, joinPrivateGame, createPrivateGame, getLobby, getUser} = useGameHook();
|
||||||
|
|
||||||
// State ----------------------
|
// State ----------------------
|
||||||
|
|
||||||
|
const [inGame, setInGame] = useState(Boolean(session.currentGid));
|
||||||
|
const [isAdmin, setIsAdmin] = useState(session.isAcceptedAdmin);
|
||||||
const [userLocation, setUserLocation] = useState(null);
|
const [userLocation, setUserLocation] = useState(null);
|
||||||
const [heading, setHeading] = useState(null);
|
const [heading, setHeading] = useState(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [cacheRecords, setCacheRecords] = useState([]);
|
const [cacheRecords, setCacheRecords] = useState([]);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
|
const [gameCode, setGameCode] = useState('');
|
||||||
|
const [groupInfo, setGroupInfo] = useState(null);
|
||||||
|
|
||||||
const subgroupFilter = session.currentSGid;
|
const isPlayer = inGame && !isAdmin;
|
||||||
const {visibleCache, isClaiming, setIsClaiming} = usePlayerGame(userLocation, heading, cacheRecords);
|
const {visibleCache, isClaiming, setIsClaiming} = usePlayerGame(
|
||||||
|
isPlayer ? userLocation : null,
|
||||||
|
isPlayer ? heading : null,
|
||||||
|
isPlayer ? cacheRecords : [],
|
||||||
|
);
|
||||||
|
|
||||||
// Handlers -------------------
|
// Handlers -------------------
|
||||||
|
|
||||||
|
// Load group info for TeamsEnabled check
|
||||||
|
useEffect(() => {
|
||||||
|
if (!session.currentGid) return;
|
||||||
|
getLobby(session.currentGid).then(setGroupInfo);
|
||||||
|
}, [inGame]);
|
||||||
|
|
||||||
|
// Load caches for players only
|
||||||
const loadCaches = useCallback(async () => {
|
const loadCaches = useCallback(async () => {
|
||||||
if (!session.currentGid) {
|
if (!session.currentGid || isAdmin) {
|
||||||
setCacheRecords([]);
|
setCacheRecords([]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const rows = await getCaches(session.currentGid, subgroupFilter);
|
const rows = await getCaches(session.currentGid, session.currentSGid);
|
||||||
setCacheRecords(rows || []);
|
setCacheRecords(rows || []);
|
||||||
}, [session.currentGid, subgroupFilter]);
|
}, [inGame, isAdmin]);
|
||||||
|
|
||||||
useEffect(() => { loadCaches(); }, [loadCaches]);
|
useEffect(() => { loadCaches(); }, [loadCaches]);
|
||||||
|
|
||||||
|
// Location + compass tracking for players only
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!isPlayer) {
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
let locationSub;
|
let locationSub;
|
||||||
let headingSub;
|
let headingSub;
|
||||||
|
|
||||||
@@ -59,23 +80,46 @@ const MapScreen = () => {
|
|||||||
|
|
||||||
locationSub = await Location.watchPositionAsync(
|
locationSub = await Location.watchPositionAsync(
|
||||||
{accuracy: Location.Accuracy.Balanced, distanceInterval: 1, timeInterval: 1000},
|
{accuracy: Location.Accuracy.Balanced, distanceInterval: 1, timeInterval: 1000},
|
||||||
(next) => {
|
(next) => setUserLocation({latitude: next.coords.latitude, longitude: next.coords.longitude}),
|
||||||
setUserLocation({latitude: next.coords.latitude, longitude: next.coords.longitude});
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Magnetometer.setUpdateInterval(500);
|
Magnetometer.setUpdateInterval(500);
|
||||||
headingSub = Magnetometer.addListener((data) => { setHeading(toHeading(data)); });
|
headingSub = Magnetometer.addListener((data) => setHeading(toHeading(data)));
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
start();
|
start();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (locationSub) locationSub.remove();
|
if (locationSub) locationSub.remove();
|
||||||
if (headingSub) headingSub.remove();
|
if (headingSub) headingSub.remove();
|
||||||
};
|
};
|
||||||
}, []);
|
}, [isPlayer]);
|
||||||
|
|
||||||
|
const handleJoinGame = async () => {
|
||||||
|
if (!gameCode.trim()) return;
|
||||||
|
const result = await joinPrivateGame({JoinCode: gameCode.trim(), Uid: session.currentUid});
|
||||||
|
if (!result) return;
|
||||||
|
setSessionGroup(result.Gid, result.SGid);
|
||||||
|
setInGame(true);
|
||||||
|
setIsAdmin(false);
|
||||||
|
navigation.navigate('TeamScreen');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCreateGame = async () => {
|
||||||
|
const result = await createPrivateGame({
|
||||||
|
GroupName: 'My Game',
|
||||||
|
CreatedByUid: session.currentUid,
|
||||||
|
TeamsEnabled: false,
|
||||||
|
MaxMemberSubgroups: 1,
|
||||||
|
});
|
||||||
|
if (!result) return;
|
||||||
|
const freshUser = await getUser(session.currentUid);
|
||||||
|
if (freshUser) {
|
||||||
|
setSessionUser(freshUser);
|
||||||
|
setInGame(true);
|
||||||
|
setIsAdmin(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleClaim = async (cacheId) => {
|
const handleClaim = async (cacheId) => {
|
||||||
if (!session.currentGid) return;
|
if (!session.currentGid) return;
|
||||||
@@ -91,15 +135,39 @@ const MapScreen = () => {
|
|||||||
|
|
||||||
// View -----------------------
|
// View -----------------------
|
||||||
|
|
||||||
if (!session.currentGid) {
|
// Not in a game
|
||||||
|
if (!inGame) {
|
||||||
return (
|
return (
|
||||||
<Screen style={styles.center}>
|
<Screen style={styles.center}>
|
||||||
<Text style={styles.body}>Join or create a game to use map features.</Text>
|
<View style={styles.row}>
|
||||||
|
<TextInput
|
||||||
|
style={styles.codeInput}
|
||||||
|
placeholder="Enter Game Code"
|
||||||
|
value={gameCode}
|
||||||
|
onChangeText={setGameCode}
|
||||||
|
autoCapitalize="characters"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
label="Join"
|
||||||
|
onClick={handleJoinGame}
|
||||||
|
styleButton={styles.joinButton}
|
||||||
|
styleLabel={styles.joinLabel}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<ButtonTray>
|
||||||
|
<Button
|
||||||
|
label="Create a Game"
|
||||||
|
onClick={handleCreateGame}
|
||||||
|
styleButton={styles.createButton}
|
||||||
|
styleLabel={styles.createLabel}
|
||||||
|
/>
|
||||||
|
</ButtonTray>
|
||||||
</Screen>
|
</Screen>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session.isAcceptedAdmin) {
|
// Admin view
|
||||||
|
if (isAdmin) {
|
||||||
return (
|
return (
|
||||||
<Screen style={styles.center}>
|
<Screen style={styles.center}>
|
||||||
<Text style={styles.body}>Admins manage caches from Manage Game.</Text>
|
<Text style={styles.body}>Admins manage caches from Manage Game.</Text>
|
||||||
@@ -107,6 +175,7 @@ const MapScreen = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Loading
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<Screen style={styles.center}>
|
<Screen style={styles.center}>
|
||||||
@@ -115,6 +184,7 @@ const MapScreen = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<Screen style={styles.center}>
|
<Screen style={styles.center}>
|
||||||
@@ -123,8 +193,14 @@ const MapScreen = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Player in game
|
||||||
|
const teamsWarning = groupInfo?.TeamsEnabled && !session.currentTid;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Screen style={styles.container}>
|
<Screen style={styles.containerMap}>
|
||||||
|
{teamsWarning && (
|
||||||
|
<Text style={styles.warning}>You are not in a team. Teams are required for this game!</Text>
|
||||||
|
)}
|
||||||
<View style={styles.mapWrap}>
|
<View style={styles.mapWrap}>
|
||||||
<PlayerMapView
|
<PlayerMapView
|
||||||
userLocation={userLocation}
|
userLocation={userLocation}
|
||||||
@@ -133,16 +209,73 @@ const MapScreen = () => {
|
|||||||
onClaimSuccess={handleClaim}
|
onClaimSuccess={handleClaim}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
<View style={styles.cacheSection}>
|
||||||
|
<ScrollView>
|
||||||
|
{cacheRecords.map((cache) => (
|
||||||
|
<View key={cache.id} style={styles.cacheCard}>
|
||||||
|
<Text style={styles.cacheClue}>{cache.clue}</Text>
|
||||||
|
<Text style={[styles.cacheStatus, cache.ClaimedByUid && styles.cacheStatusClaimed]}>
|
||||||
|
{cache.ClaimedByUid ? 'Claimed' : 'Available'}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
{cacheRecords.length === 0 && (
|
||||||
|
<Text style={styles.emptyText}>No caches available yet.</Text>
|
||||||
|
)}
|
||||||
|
</ScrollView>
|
||||||
|
</View>
|
||||||
</Screen>
|
</Screen>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {padding: 0},
|
|
||||||
mapWrap: {flex: 1},
|
|
||||||
center: {justifyContent: 'center', alignItems: 'center'},
|
center: {justifyContent: 'center', alignItems: 'center'},
|
||||||
|
containerMap: {padding: 0},
|
||||||
|
mapWrap: {flex: 2},
|
||||||
body: {color: '#4b5563', fontSize: 15},
|
body: {color: '#4b5563', fontSize: 15},
|
||||||
error: {color: '#dc2626', fontSize: 15},
|
error: {color: '#dc2626', fontSize: 15},
|
||||||
|
row: {flexDirection: 'row', gap: 10, marginBottom: 15, width: '100%', paddingHorizontal: 20},
|
||||||
|
codeInput: {
|
||||||
|
flex: 1,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#d1d5db',
|
||||||
|
borderRadius: 8,
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
paddingVertical: 10,
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
joinButton: {backgroundColor: '#2563eb', borderColor: '#2563eb', flex: 0, paddingHorizontal: 20},
|
||||||
|
joinLabel: {color: '#ffffff', fontWeight: '600'},
|
||||||
|
createButton: {backgroundColor: '#16a34a', borderColor: '#16a34a'},
|
||||||
|
createLabel: {color: '#ffffff', fontWeight: '600'},
|
||||||
|
warning: {
|
||||||
|
color: '#dc2626',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
textAlign: 'center',
|
||||||
|
paddingVertical: 8,
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
cacheSection: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#f3f4f6',
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
paddingTop: 10,
|
||||||
|
},
|
||||||
|
cacheCard: {
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
borderRadius: 8,
|
||||||
|
padding: 12,
|
||||||
|
marginBottom: 8,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#e5e7eb',
|
||||||
|
},
|
||||||
|
cacheClue: {fontSize: 15, fontWeight: '600', color: '#1f2937'},
|
||||||
|
cacheStatus: {fontSize: 13, color: '#16a34a', fontWeight: '600'},
|
||||||
|
cacheStatusClaimed: {color: '#9ca3af'},
|
||||||
|
emptyText: {color: '#9ca3af', textAlign: 'center', marginTop: 20, fontSize: 14},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default MapScreen;
|
export default MapScreen;
|
||||||
@@ -1,25 +1,185 @@
|
|||||||
import React from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import {StyleSheet, Text} from 'react-native';
|
import {ActivityIndicator, ScrollView, StyleSheet, Text, TextInput, View} from 'react-native';
|
||||||
import Screen from '../layout/Screen';
|
import Screen from '../layout/Screen';
|
||||||
|
import {Button, ButtonTray} from '../UI/Button';
|
||||||
|
import useGameHook from '../../hooks/useGameHook';
|
||||||
|
import {getSession, setSessionTeam} from '../../hooks/SessionStore';
|
||||||
|
|
||||||
const TeamScreen = () => {
|
const TeamScreen = () => {
|
||||||
// Initialisation -------------
|
// Initialisation ------------
|
||||||
|
|
||||||
|
const session = getSession();
|
||||||
|
const {getTeam, createTeam, joinTeamByCode, getTeamMembers, leaveTeam, getUser, updateUser} = useGameHook();
|
||||||
|
|
||||||
// State ----------------------
|
// State ----------------------
|
||||||
|
|
||||||
|
const [activeTid, setActiveTid] = useState(session.currentTid);
|
||||||
|
const [teamCode, setTeamCode] = useState('');
|
||||||
|
const [team, setTeam] = useState(null);
|
||||||
|
const [members, setMembers] = useState([]);
|
||||||
|
const [memberNames, setMemberNames] = useState({});
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
// Handlers -------------------
|
// Handlers -------------------
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const load = async () => {
|
||||||
|
if (activeTid) {
|
||||||
|
const t = await getTeam(activeTid);
|
||||||
|
setTeam(t);
|
||||||
|
const ms = await getTeamMembers(activeTid);
|
||||||
|
setMembers(ms || []);
|
||||||
|
const names = {};
|
||||||
|
for (const m of (ms || [])) {
|
||||||
|
const u = await getUser(m.Uid);
|
||||||
|
if (u) names[m.Uid] = u.username;
|
||||||
|
}
|
||||||
|
setMemberNames(names);
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
load();
|
||||||
|
}, [activeTid]);
|
||||||
|
|
||||||
|
const handleJoinTeam = async () => {
|
||||||
|
if (!teamCode.trim()) return;
|
||||||
|
const result = await joinTeamByCode({JoinCode: teamCode.trim(), Uid: session.currentUid});
|
||||||
|
if (!result) return;
|
||||||
|
setSessionTeam(result.Tid);
|
||||||
|
setActiveTid(result.Tid);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCreateTeam = async () => {
|
||||||
|
if (!session.currentGid) return;
|
||||||
|
const result = await createTeam({Gid: session.currentGid, TeamName: 'My Team'});
|
||||||
|
if (!result) return;
|
||||||
|
const membership = await joinTeamByCode({Tid: result.Tid, Uid: session.currentUid});
|
||||||
|
if (membership) {
|
||||||
|
setSessionTeam(result.Tid);
|
||||||
|
setActiveTid(result.Tid);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLeaveTeam = async () => {
|
||||||
|
const myMembership = members.find((m) => m.Uid === session.currentUid);
|
||||||
|
if (!myMembership) return;
|
||||||
|
await leaveTeam(myMembership.id);
|
||||||
|
await updateUser(session.currentUid, {TGid: null});
|
||||||
|
setSessionTeam(null);
|
||||||
|
setActiveTid(null);
|
||||||
|
setTeam(null);
|
||||||
|
setMembers([]);
|
||||||
|
setMemberNames({});
|
||||||
|
};
|
||||||
|
|
||||||
// View -----------------------
|
// View -----------------------
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<Screen style={styles.center}>
|
||||||
|
<ActivityIndicator size="large"/>
|
||||||
|
</Screen>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not in a team
|
||||||
|
if (!activeTid || !team) {
|
||||||
|
return (
|
||||||
|
<Screen style={styles.center}>
|
||||||
|
<View style={styles.row}>
|
||||||
|
<TextInput
|
||||||
|
style={styles.codeInput}
|
||||||
|
placeholder="Enter Team Code"
|
||||||
|
value={teamCode}
|
||||||
|
onChangeText={setTeamCode}
|
||||||
|
autoCapitalize="characters"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
label="Join"
|
||||||
|
onClick={handleJoinTeam}
|
||||||
|
styleButton={styles.joinButton}
|
||||||
|
styleLabel={styles.joinLabel}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<ButtonTray>
|
||||||
|
<Button
|
||||||
|
label="Create Team"
|
||||||
|
onClick={handleCreateTeam}
|
||||||
|
styleButton={styles.createButton}
|
||||||
|
styleLabel={styles.createLabel}
|
||||||
|
/>
|
||||||
|
</ButtonTray>
|
||||||
|
</Screen>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// In a team
|
||||||
return (
|
return (
|
||||||
<Screen style={styles.container}>
|
<Screen style={styles.container}>
|
||||||
<Text style={styles.title}>Team Management</Text>
|
<View style={styles.teamHeader}>
|
||||||
<Text style={styles.body}>Team management setup goes here.</Text>
|
<Text style={styles.teamCodeLabel}>Team Code:</Text>
|
||||||
|
<Text style={styles.teamCodeValue}>{team.JoinCode}</Text>
|
||||||
|
</View>
|
||||||
|
<ScrollView style={styles.memberList}>
|
||||||
|
{members.map((m) => (
|
||||||
|
<View key={m.id} style={styles.memberItem}>
|
||||||
|
<Text style={styles.memberName}>{memberNames[m.Uid] || `Player ${m.Uid}`}</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
{members.length === 0 && (
|
||||||
|
<Text style={styles.emptyText}>No members yet.</Text>
|
||||||
|
)}
|
||||||
|
</ScrollView>
|
||||||
|
<View style={styles.leaveWrap}>
|
||||||
|
<Button
|
||||||
|
label="Leave Team"
|
||||||
|
onClick={handleLeaveTeam}
|
||||||
|
styleButton={styles.leaveButton}
|
||||||
|
styleLabel={styles.leaveLabel}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
</Screen>
|
</Screen>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {justifyContent: 'center', alignItems: 'center'},
|
center: {justifyContent: 'center', alignItems: 'center'},
|
||||||
title: {fontSize: 24, fontWeight: 'bold'},
|
container: {padding: 0},
|
||||||
body: {color: '#4b5563', fontSize: 15, marginTop: 10},
|
row: {flexDirection: 'row', gap: 10, marginBottom: 15, width: '100%', paddingHorizontal: 20},
|
||||||
|
codeInput: {
|
||||||
|
flex: 1,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#d1d5db',
|
||||||
|
borderRadius: 8,
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
paddingVertical: 10,
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
joinButton: {backgroundColor: '#2563eb', borderColor: '#2563eb', flex: 0, paddingHorizontal: 20},
|
||||||
|
joinLabel: {color: '#ffffff', fontWeight: '600'},
|
||||||
|
createButton: {backgroundColor: '#16a34a', borderColor: '#16a34a'},
|
||||||
|
createLabel: {color: '#ffffff', fontWeight: '600'},
|
||||||
|
teamHeader: {
|
||||||
|
backgroundColor: '#374151',
|
||||||
|
paddingVertical: 14,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 8,
|
||||||
|
},
|
||||||
|
teamCodeLabel: {color: '#d1d5db', fontSize: 15, fontWeight: '600'},
|
||||||
|
teamCodeValue: {color: '#ffffff', fontSize: 17, fontWeight: '700', letterSpacing: 2},
|
||||||
|
memberList: {flex: 1, paddingHorizontal: 15, paddingTop: 10},
|
||||||
|
memberItem: {
|
||||||
|
paddingVertical: 14,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: '#e5e7eb',
|
||||||
|
},
|
||||||
|
memberName: {fontSize: 16, color: '#1f2937'},
|
||||||
|
emptyText: {color: '#9ca3af', textAlign: 'center', marginTop: 30, fontSize: 14},
|
||||||
|
leaveWrap: {padding: 15},
|
||||||
|
leaveButton: {backgroundColor: '#dc2626', borderColor: '#dc2626'},
|
||||||
|
leaveLabel: {color: '#ffffff', fontWeight: '600'},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default TeamScreen;
|
export default TeamScreen;
|
||||||
@@ -3,6 +3,7 @@ import API, {API_BASE_URL} from '../components/API/API';
|
|||||||
const useGameHook = () => {
|
const useGameHook = () => {
|
||||||
// Initialisation ------------
|
// Initialisation ------------
|
||||||
|
|
||||||
|
const usersEndpoint = `${API_BASE_URL}/users`;
|
||||||
const gameTypesEndpoint = `${API_BASE_URL}/game-types`;
|
const gameTypesEndpoint = `${API_BASE_URL}/game-types`;
|
||||||
const groupsEndpoint = `${API_BASE_URL}/groups`;
|
const groupsEndpoint = `${API_BASE_URL}/groups`;
|
||||||
const subgroupMembershipsEndpoint = `${API_BASE_URL}/subgroup-memberships`;
|
const subgroupMembershipsEndpoint = `${API_BASE_URL}/subgroup-memberships`;
|
||||||
@@ -13,11 +14,24 @@ const useGameHook = () => {
|
|||||||
|
|
||||||
// Handlers -------------------
|
// Handlers -------------------
|
||||||
|
|
||||||
|
// Users
|
||||||
|
const getUser = async (uid) => {
|
||||||
|
const response = await API.get(`${usersEndpoint}/${uid}`);
|
||||||
|
return response.isSuccess ? response.result : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateUser = async (uid, data) => {
|
||||||
|
const response = await API.put(`${usersEndpoint}/${uid}`, data);
|
||||||
|
return response.isSuccess ? response.result : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Game types
|
||||||
const getGameTypes = async () => {
|
const getGameTypes = async () => {
|
||||||
const response = await API.get(gameTypesEndpoint);
|
const response = await API.get(gameTypesEndpoint);
|
||||||
return response.isSuccess ? response.result : [];
|
return response.isSuccess ? response.result : [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Groups
|
||||||
const getCreatedPrivateGame = async (userId) => {
|
const getCreatedPrivateGame = async (userId) => {
|
||||||
const response = await API.get(`${groupsEndpoint}?CreatedByUid=${userId}`);
|
const response = await API.get(`${groupsEndpoint}?CreatedByUid=${userId}`);
|
||||||
return response.isSuccess ? response.result : [];
|
return response.isSuccess ? response.result : [];
|
||||||
@@ -28,6 +42,12 @@ const useGameHook = () => {
|
|||||||
return response.isSuccess ? response.result : null;
|
return response.isSuccess ? response.result : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getLobby = async (gid) => {
|
||||||
|
const response = await API.get(`${groupsEndpoint}/${gid}`);
|
||||||
|
return response.isSuccess ? response.result : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Subgroup memberships
|
||||||
const joinPrivateGame = async (payload) => {
|
const joinPrivateGame = async (payload) => {
|
||||||
const response = await API.post(subgroupMembershipsEndpoint, payload);
|
const response = await API.post(subgroupMembershipsEndpoint, payload);
|
||||||
return response.isSuccess ? response.result : null;
|
return response.isSuccess ? response.result : null;
|
||||||
@@ -38,11 +58,7 @@ const useGameHook = () => {
|
|||||||
return response.isSuccess ? response.result : null;
|
return response.isSuccess ? response.result : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getLobby = async (gid) => {
|
// Teams
|
||||||
const response = await API.get(`${groupsEndpoint}/${gid}`);
|
|
||||||
return response.isSuccess ? response.result : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getTeam = async (tid) => {
|
const getTeam = async (tid) => {
|
||||||
const response = await API.get(`${teamsEndpoint}/${tid}`);
|
const response = await API.get(`${teamsEndpoint}/${tid}`);
|
||||||
return response.isSuccess ? response.result : null;
|
return response.isSuccess ? response.result : null;
|
||||||
@@ -58,7 +74,18 @@ const useGameHook = () => {
|
|||||||
return response.isSuccess ? response.result : null;
|
return response.isSuccess ? response.result : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Team members
|
||||||
|
const getTeamMembers = async (tid) => {
|
||||||
|
const response = await API.get(`${teamMembersEndpoint}?Tid=${tid}`);
|
||||||
|
return response.isSuccess ? response.result : [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const leaveTeam = async (membershipId) => {
|
||||||
|
const response = await API.delete(`${teamMembersEndpoint}/${membershipId}`);
|
||||||
|
return response.isSuccess;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Caches
|
||||||
const getCaches = async (gid, sgid = null) => {
|
const getCaches = async (gid, sgid = null) => {
|
||||||
let url = `${gameDataEndpoint}/${gid}/caches`;
|
let url = `${gameDataEndpoint}/${gid}/caches`;
|
||||||
if (sgid !== null) url += `?SGid=${sgid}`;
|
if (sgid !== null) url += `?SGid=${sgid}`;
|
||||||
@@ -92,15 +119,19 @@ const useGameHook = () => {
|
|||||||
// Return ---------------------
|
// Return ---------------------
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
getUser,
|
||||||
|
updateUser,
|
||||||
getGameTypes,
|
getGameTypes,
|
||||||
getCreatedPrivateGame,
|
getCreatedPrivateGame,
|
||||||
createPrivateGame,
|
createPrivateGame,
|
||||||
|
getLobby,
|
||||||
joinPrivateGame,
|
joinPrivateGame,
|
||||||
joinAsAdmin,
|
joinAsAdmin,
|
||||||
getLobby,
|
|
||||||
getTeam,
|
getTeam,
|
||||||
createTeam,
|
createTeam,
|
||||||
joinTeamByCode,
|
joinTeamByCode,
|
||||||
|
getTeamMembers,
|
||||||
|
leaveTeam,
|
||||||
getCaches,
|
getCaches,
|
||||||
upsertCache,
|
upsertCache,
|
||||||
claimCache,
|
claimCache,
|
||||||
|
|||||||
Reference in new issue
Block a user