mirror of
https://git.rezel.net/LudoTech/traque.git
synced 2026-04-10 16:30:18 +02:00
Fix photos + API hooks + cleaning
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -137,3 +137,6 @@ yarn.lock
|
|||||||
keys/
|
keys/
|
||||||
temp.*
|
temp.*
|
||||||
temp/
|
temp/
|
||||||
|
.env
|
||||||
|
.env.development
|
||||||
|
.env.production
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ Problèmes
|
|||||||
|
|
||||||
[ ] Une équipe perdait sans arrêt la connection avec le serveur
|
[ ] Une équipe perdait sans arrêt la connection avec le serveur
|
||||||
[ ] La position en arrière plan, téléphone éteint par exemple, n'avait pas l'air de fonctionner (une équipe n'avait pas de notif)
|
[ ] La position en arrière plan, téléphone éteint par exemple, n'avait pas l'air de fonctionner (une équipe n'avait pas de notif)
|
||||||
[ ] La photo d'une des équipes ne parvenait pas jusqu'au serveur. Tout semblait normal pour l'équipe.
|
[x] La photo d'une des équipes ne parvenait pas jusqu'au serveur. Tout semblait normal pour l'équipe. (Potentiellement un problème
|
||||||
|
de conversion : 046512 -> 46512)
|
||||||
[x] Le focus sur une équipe dans la page principale des admins est à revoir. Par exemple, le zoom seul devrait désactiver le focus
|
[x] Le focus sur une équipe dans la page principale des admins est à revoir. Par exemple, le zoom seul devrait désactiver le focus
|
||||||
automatique.
|
automatique.
|
||||||
[x] Il est pas évident de voir qu'elles équipes sont encore en jeu sur la page principale admin. Notamment, on voudrait que les
|
[x] Il est pas évident de voir qu'elles équipes sont encore en jeu sur la page principale admin. Notamment, on voudrait que les
|
||||||
|
|||||||
@@ -7,12 +7,30 @@ lang: en-GB
|
|||||||
This tutorial will help you to set up your development environment, use a dev build and create an APK.
|
This tutorial will help you to set up your development environment, use a dev build and create an APK.
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
* [Environment](#environment) : Dependencies, packages, app key and device
|
* [Environment](#environment) : Dependencies, packages, app key, device and .env
|
||||||
* [Dev build](#dev-build) : Create, install and use
|
* [Dev build](#dev-build) : Create, install and use
|
||||||
* [APK](#apk) : Create and install
|
* [APK](#apk) : Create and install
|
||||||
|
|
||||||
## Environment
|
## Environment
|
||||||
|
|
||||||
|
### .env files
|
||||||
|
|
||||||
|
Some infos like API keys or IP addresses cannot be pushed on the public repository, therefore you have to create the .env files that will store those values. Go in the `traque-app` folder, create those two files and replace the FILL_HERE with the correct values (you can ask someone already working on the project) :
|
||||||
|
|
||||||
|
* `.env.development` :
|
||||||
|
|
||||||
|
```.env
|
||||||
|
EXPO_PUBLIC_SERVER_URL=FILL_HERE
|
||||||
|
EXPO_PUBLIC_SOCKET_URL=FILL_HERE
|
||||||
|
```
|
||||||
|
|
||||||
|
* `.env.production` :
|
||||||
|
|
||||||
|
```.env
|
||||||
|
EXPO_PUBLIC_SERVER_URL=FILL_HERE
|
||||||
|
EXPO_PUBLIC_SOCKET_URL=FILL_HERE
|
||||||
|
```
|
||||||
|
|
||||||
### Installing dependencies and preparing the device
|
### Installing dependencies and preparing the device
|
||||||
|
|
||||||
You will need to install Android Studio, some SDKs and prepare your device if you want to use a physical one. Follow this [tutorial](https://reactnative.dev/docs/set-up-your-environment?os=linux&platform=android).
|
You will need to install Android Studio, some SDKs and prepare your device if you want to use a physical one. Follow this [tutorial](https://reactnative.dev/docs/set-up-your-environment?os=linux&platform=android).
|
||||||
|
|||||||
@@ -8,22 +8,22 @@ import { CustomButton } from '../components/button';
|
|||||||
import { CustomImage } from '../components/image';
|
import { CustomImage } from '../components/image';
|
||||||
import { CustomTextInput } from '../components/input';
|
import { CustomTextInput } from '../components/input';
|
||||||
// Contexts
|
// Contexts
|
||||||
import { useSocket } from "../context/socketContext";
|
|
||||||
import { useTeamConnexion } from "../context/teamConnexionContext";
|
import { useTeamConnexion } from "../context/teamConnexionContext";
|
||||||
import { useTeamContext } from "../context/teamContext";
|
import { useTeamContext } from "../context/teamContext";
|
||||||
// Hooks
|
// Hooks
|
||||||
import { usePickImage } from '../hook/usePickImage';
|
import { usePickImage } from '../hook/usePickImage';
|
||||||
|
import { useImageApi } from '../hook/useImageApi';
|
||||||
// Util
|
// Util
|
||||||
import { Colors } from '../util/colors';
|
import { Colors } from '../util/colors';
|
||||||
|
|
||||||
const Index = () => {
|
const Index = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const {SERVER_URL} = useSocket();
|
const {login, loggedIn} = useTeamConnexion();
|
||||||
const {login, loggedIn, loading} = useTeamConnexion();
|
|
||||||
const {getLocationAuthorization, stopLocationTracking} = useTeamContext();
|
const {getLocationAuthorization, stopLocationTracking} = useTeamContext();
|
||||||
const {image, pickImage, sendImage} = usePickImage();
|
const {image, pickImage} = usePickImage();
|
||||||
const [teamID, setTeamID] = useState("");
|
const [teamId, setTeamId] = useState("");
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
const { uploadTeamImage } = useImageApi();
|
||||||
|
|
||||||
// Disbaling location tracking
|
// Disbaling location tracking
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -32,32 +32,37 @@ const Index = () => {
|
|||||||
|
|
||||||
// Routeur
|
// Routeur
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && loggedIn) {
|
if (loggedIn) {
|
||||||
|
uploadTeamImage(image?.uri);
|
||||||
router.replace("/interface");
|
router.replace("/interface");
|
||||||
}
|
}
|
||||||
}, [router, loggedIn, loading]);
|
}, [router, loggedIn, uploadTeamImage, image]);
|
||||||
|
|
||||||
function handleSubmit() {
|
const handleSubmit = async () => {
|
||||||
if (!isSubmitting && !loading) {
|
if (isSubmitting || !getLocationAuthorization()) return;
|
||||||
setIsSubmitting(true);
|
|
||||||
if (getLocationAuthorization()) {
|
setIsSubmitting(true);
|
||||||
login(parseInt(teamID))
|
|
||||||
.then((response) => {
|
const regex = /^\d{6}$/;
|
||||||
if (response.isLoggedIn) {
|
if (!regex.test(teamId)) {
|
||||||
sendImage(`${SERVER_URL}/upload?team=${teamID}`);
|
setTimeout(() => Alert.alert("Erreur", "Veuillez entrer un ID d'équipe valide."), 100);
|
||||||
} else {
|
return;
|
||||||
Alert.alert("Échec", "L'ID d'équipe est inconnu.");
|
|
||||||
}
|
|
||||||
setIsSubmitting(false);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
Alert.alert("Échec", "La connection au serveur a échoué.");
|
|
||||||
setIsSubmitting(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setTeamID("");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
try {
|
||||||
|
const response = await login(teamId);
|
||||||
|
|
||||||
|
if (response.isLoggedIn) {
|
||||||
|
setTeamId("");
|
||||||
|
} else {
|
||||||
|
setTimeout(() => Alert.alert("Échec", "L'ID d'équipe est inconnu."), 100);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setTimeout(() => Alert.alert("Échec", "La connexion au serveur a échoué."), 100);
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView contentContainerStyle={styles.container}>
|
<ScrollView contentContainerStyle={styles.container}>
|
||||||
@@ -67,7 +72,7 @@ const Index = () => {
|
|||||||
<Text style={styles.logoText}>LA TRAQUE</Text>
|
<Text style={styles.logoText}>LA TRAQUE</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.subContainer}>
|
<View style={styles.subContainer}>
|
||||||
<CustomTextInput value={teamID} inputMode="numeric" placeholder="ID de l'équipe" style={styles.input} onChangeText={setTeamID}/>
|
<CustomTextInput value={teamId} inputMode="numeric" placeholder="ID de l'équipe" style={styles.input} onChangeText={setTeamId}/>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.subContainer}>
|
<View style={styles.subContainer}>
|
||||||
<Text style={{fontSize: 15}}>Appuyer pour changer la photo d'équipe</Text>
|
<Text style={{fontSize: 15}}>Appuyer pour changer la photo d'équipe</Text>
|
||||||
@@ -75,7 +80,7 @@ const Index = () => {
|
|||||||
<CustomImage source={image ? {uri: image.uri} : require('../assets/images/missing_image.jpg')} onPress={pickImage}/>
|
<CustomImage source={image ? {uri: image.uri} : require('../assets/images/missing_image.jpg')} onPress={pickImage}/>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.subContainer}>
|
<View style={styles.subContainer}>
|
||||||
<CustomButton label={(isSubmitting || loading) ? "..." : "Valider"} onPress={handleSubmit}/>
|
<CustomButton label={isSubmitting ? "..." : "Valider"} onPress={handleSubmit}/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
@@ -20,9 +20,10 @@ import { Colors } from '../util/colors';
|
|||||||
|
|
||||||
const Interface = () => {
|
const Interface = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const {messages, nextZoneDate, isShrinking, startLocationTracking, stopLocationTracking, gameState} = useTeamContext();
|
const {teamInfos, messages, nextZoneDate, isShrinking, startLocationTracking, stopLocationTracking, gameState} = useTeamContext();
|
||||||
const {loggedIn, logout, loading} = useTeamConnexion();
|
const {name, ready, captured, locationSendDeadline, outOfZone, outOfZoneDeadline, hasHandicap, enemyHasHandicap} = teamInfos;
|
||||||
const {name, ready, captured, locationSendDeadline, sendCurrentPosition, outOfZone, outOfZoneDeadline, hasHandicap, enemyHasHandicap} = useGame();
|
const {loggedIn, logout} = useTeamConnexion();
|
||||||
|
const {sendCurrentPosition} = useGame();
|
||||||
const [timeLeftSendLocation] = useTimeDifference(locationSendDeadline, 1000);
|
const [timeLeftSendLocation] = useTimeDifference(locationSendDeadline, 1000);
|
||||||
const [timeLeftNextZone] = useTimeDifference(nextZoneDate, 1000);
|
const [timeLeftNextZone] = useTimeDifference(nextZoneDate, 1000);
|
||||||
const [timeLeftOutOfZone] = useTimeDifference(outOfZoneDeadline, 1000);
|
const [timeLeftOutOfZone] = useTimeDifference(outOfZoneDeadline, 1000);
|
||||||
@@ -48,12 +49,10 @@ const Interface = () => {
|
|||||||
|
|
||||||
// Router
|
// Router
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading) {
|
if (!loggedIn) {
|
||||||
if (!loggedIn) {
|
router.replace("/");
|
||||||
router.replace("/");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [router, loggedIn, loading]);
|
}, [router, loggedIn]);
|
||||||
|
|
||||||
// Activating geolocation tracking
|
// Activating geolocation tracking
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { CustomTextInput } from './input';
|
|||||||
import { Stat } from './stat';
|
import { Stat } from './stat';
|
||||||
// Contexts
|
// Contexts
|
||||||
import { useTeamContext } from '../context/teamContext';
|
import { useTeamContext } from '../context/teamContext';
|
||||||
import { useSocket } from '../context/socketContext';
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useTimeDifference } from '../hook/useTimeDifference';
|
import { useTimeDifference } from '../hook/useTimeDifference';
|
||||||
import { useGame } from '../hook/useGame';
|
import { useGame } from '../hook/useGame';
|
||||||
@@ -17,17 +16,18 @@ import { useGame } from '../hook/useGame';
|
|||||||
import { GameState } from '../util/gameState';
|
import { GameState } from '../util/gameState';
|
||||||
import { Colors } from '../util/colors';
|
import { Colors } from '../util/colors';
|
||||||
import { secondsToHHMMSS } from '../util/functions';
|
import { secondsToHHMMSS } from '../util/functions';
|
||||||
|
import { useImageApi } from '../hook/useImageApi';
|
||||||
|
|
||||||
export const Drawer = ({ height }) => {
|
export const Drawer = ({ height }) => {
|
||||||
const [collapsibleState, setCollapsibleState] = useState(true);
|
const [collapsibleState, setCollapsibleState] = useState(true);
|
||||||
const [enemyCaptureCode, setEnemyCaptureCode] = useState("");
|
const [enemyCaptureCode, setEnemyCaptureCode] = useState("");
|
||||||
const {SERVER_URL} = useSocket();
|
const {teamInfos, gameState, startDate} = useTeamContext();
|
||||||
const {gameState, startDate} = useTeamContext();
|
const {enemyName, captureCode, name, distance, finishDate, nCaptures, nSentLocation, hasHandicap} = teamInfos;
|
||||||
const {capture, enemyName, captureCode, name, teamId, distance, finishDate, nCaptures, nSentLocation, hasHandicap} = useGame();
|
const {capture} = useGame();
|
||||||
const [timeSinceStart] = useTimeDifference(startDate, 1000);
|
const [timeSinceStart] = useTimeDifference(startDate, 1000);
|
||||||
const [enemyImageURI, setEnemyImageURI] = useState("../assets/images/missing_image.jpg");
|
|
||||||
const [captureStatus, setCaptureStatus] = useState(0); // 0 : no capture | 1 : waiting for response from server | 2 : capture failed | 3 : capture succesful
|
const [captureStatus, setCaptureStatus] = useState(0); // 0 : no capture | 1 : waiting for response from server | 2 : capture failed | 3 : capture succesful
|
||||||
const captureStatusColor = {0: "#777", 1: "#FFA500", 2: "#FF6B6B", 3: "#81C784"};
|
const captureStatusColor = {0: "#777", 1: "#FFA500", 2: "#FF6B6B", 3: "#81C784"};
|
||||||
|
const { enemyImage } = useImageApi();
|
||||||
|
|
||||||
const avgSpeed = useMemo(() => {
|
const avgSpeed = useMemo(() => {
|
||||||
const hours = (finishDate ? (finishDate - startDate) : timeSinceStart*1000) / 1000 / 3600;
|
const hours = (finishDate ? (finishDate - startDate) : timeSinceStart*1000) / 1000 / 3600;
|
||||||
@@ -48,11 +48,6 @@ export const Drawer = ({ height }) => {
|
|||||||
}
|
}
|
||||||
}, [captureStatus]);
|
}, [captureStatus]);
|
||||||
|
|
||||||
// Refresh the image
|
|
||||||
useEffect(() => {
|
|
||||||
setEnemyImageURI(`${SERVER_URL}/photo/enemy?team=${teamId}&t=${new Date().getTime()}`);
|
|
||||||
}, [SERVER_URL, enemyName, teamId]);
|
|
||||||
|
|
||||||
const handleCapture = () => {
|
const handleCapture = () => {
|
||||||
if (captureStatus != 1) {
|
if (captureStatus != 1) {
|
||||||
setCaptureStatus(1);
|
setCaptureStatus(1);
|
||||||
@@ -86,8 +81,8 @@ export const Drawer = ({ height }) => {
|
|||||||
}
|
}
|
||||||
{ gameState == GameState.PLAYING && !hasHandicap && <Fragment>
|
{ gameState == GameState.PLAYING && !hasHandicap && <Fragment>
|
||||||
<View style={styles.imageContainer}>
|
<View style={styles.imageContainer}>
|
||||||
{<Text style={{fontSize: 15, margin: 5}}>{"Cible (" + (enemyName ?? "Indisponible") + ")"}</Text>}
|
<Text style={{fontSize: 15, margin: 5}}>{"Cible (" + (enemyName ?? "Indisponible") + ")"}</Text>
|
||||||
{<CustomImage source={{ uri : enemyImageURI }} canZoom/>}
|
<CustomImage source={enemyImage} canZoom/>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.actionsContainer}>
|
<View style={styles.actionsContainer}>
|
||||||
<View style={styles.actionsLeftContainer}>
|
<View style={styles.actionsLeftContainer}>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// React
|
||||||
import { Fragment } from 'react';
|
import { Fragment } from 'react';
|
||||||
import { Polygon } from 'react-native-maps';
|
import { Polygon } from 'react-native-maps';
|
||||||
import { circleToPolygon } from '../util/functions';
|
import { circleToPolygon } from '../util/functions';
|
||||||
|
|||||||
@@ -7,15 +7,13 @@ import LinearGradient from 'react-native-linear-gradient';
|
|||||||
import { DashedCircle, InvertedCircle, InvertedPolygon } from './layer';
|
import { DashedCircle, InvertedCircle, InvertedPolygon } from './layer';
|
||||||
// Contexts
|
// Contexts
|
||||||
import { useTeamContext } from '../context/teamContext';
|
import { useTeamContext } from '../context/teamContext';
|
||||||
// Hooks
|
|
||||||
import { useGame } from '../hook/useGame';
|
|
||||||
// Util
|
// Util
|
||||||
import { GameState } from '../util/gameState';
|
import { GameState } from '../util/gameState';
|
||||||
import { ZoneTypes, InitialRegions } from '../util/constants';
|
import { ZoneTypes, InitialRegions } from '../util/constants';
|
||||||
|
|
||||||
export const CustomMap = () => {
|
export const CustomMap = () => {
|
||||||
const {zoneType, zoneExtremities, location, gameState} = useTeamContext();
|
const {teamInfos, zoneType, zoneExtremities, location, gameState} = useTeamContext();
|
||||||
const {enemyLocation, startingArea, lastSentLocation, hasHandicap} = useGame();
|
const {enemyLocation, startingArea, lastSentLocation, hasHandicap} = teamInfos;
|
||||||
const [centerMap, setCenterMap] = useState(true);
|
const [centerMap, setCenterMap] = useState(true);
|
||||||
const mapRef = useRef(null);
|
const mapRef = useRef(null);
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
|
// React
|
||||||
import { createContext, useContext, useMemo } from "react";
|
import { createContext, useContext, useMemo } from "react";
|
||||||
|
// IO
|
||||||
import { io } from "socket.io-client";
|
import { io } from "socket.io-client";
|
||||||
|
// Util
|
||||||
|
import { SOCKET_URL } from "../util/constants";
|
||||||
|
|
||||||
const IP = "172.16.1.180";
|
const SocketContext = createContext();
|
||||||
const SOCKET_URL = `ws://${IP}/player`;
|
|
||||||
const SERVER_URL = `http://${IP}/back`;
|
|
||||||
|
|
||||||
export const teamSocket = io(SOCKET_URL, {
|
const teamSocket = io(SOCKET_URL, {path: "/back/socket.io"});
|
||||||
path: "/back/socket.io",
|
|
||||||
});
|
|
||||||
|
|
||||||
export const SocketContext = createContext();
|
|
||||||
|
|
||||||
export const SocketProvider = ({ children }) => {
|
export const SocketProvider = ({ children }) => {
|
||||||
const value = useMemo(() => ({ teamSocket, SERVER_URL }), []);
|
const value = useMemo(() => ({ teamSocket }), []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SocketContext.Provider value={value}>{children}</SocketContext.Provider>
|
<SocketContext.Provider value={value}>{children}</SocketContext.Provider>
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
|
// React
|
||||||
import { createContext, useContext, useMemo } from "react";
|
import { createContext, useContext, useMemo } from "react";
|
||||||
import { useSocket } from "./socketContext";
|
// Hook
|
||||||
import { useSocketAuth } from "../hook/useSocketAuth";
|
import { useSocketAuth } from "../hook/useSocketAuth";
|
||||||
|
|
||||||
const teamConnexionContext = createContext();
|
const TeamConnexionContext = createContext();
|
||||||
|
|
||||||
export const TeamConnexionProvider = ({ children }) => {
|
export const TeamConnexionProvider = ({ children }) => {
|
||||||
const { teamSocket } = useSocket();
|
const { login, password: teamId, loggedIn, logout } = useSocketAuth();
|
||||||
const { login, password: teamId, loggedIn, loading, logout } = useSocketAuth(teamSocket, "team_password");
|
|
||||||
|
|
||||||
const value = useMemo(() => ({ teamId, login, logout, loggedIn, loading}), [teamId, login, logout, loggedIn, loading]);
|
const value = useMemo(() => ({ teamId, login, logout, loggedIn}), [teamId, login, logout, loggedIn]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<teamConnexionContext.Provider value={value}>
|
<TeamConnexionContext.Provider value={value}>
|
||||||
{children}
|
{children}
|
||||||
</teamConnexionContext.Provider>
|
</TeamConnexionContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useTeamConnexion = () => {
|
export const useTeamConnexion = () => {
|
||||||
return useContext(teamConnexionContext);
|
return useContext(TeamConnexionContext);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
|
// React
|
||||||
import { createContext, useContext, useMemo, useState } from "react";
|
import { createContext, useContext, useMemo, useState } from "react";
|
||||||
|
// Context
|
||||||
import { useSocket } from "./socketContext";
|
import { useSocket } from "./socketContext";
|
||||||
import { useTeamConnexion } from "./teamConnexionContext";
|
import { useTeamConnexion } from "./teamConnexionContext";
|
||||||
import { GameState } from "../util/gameState";
|
// Hook
|
||||||
import { useSendDeviceInfo } from "../hook/useSendDeviceInfo";
|
import { useSendDeviceInfo } from "../hook/useSendDeviceInfo";
|
||||||
import { useLocation } from "../hook/useLocation";
|
import { useLocation } from "../hook/useLocation";
|
||||||
import { useSocketListener } from "../hook/useSocketListener";
|
import { useSocketListener } from "../hook/useSocketListener";
|
||||||
|
// Util
|
||||||
|
import { GameState } from "../util/gameState";
|
||||||
|
|
||||||
const teamContext = createContext();
|
const TeamContext = createContext();
|
||||||
|
|
||||||
export const TeamProvider = ({children}) => {
|
export const TeamProvider = ({children}) => {
|
||||||
const {teamSocket} = useSocket();
|
const {teamSocket} = useSocket();
|
||||||
@@ -51,18 +55,17 @@ export const TeamProvider = ({children}) => {
|
|||||||
|
|
||||||
useSocketListener(teamSocket, "logout", logout);
|
useSocketListener(teamSocket, "logout", logout);
|
||||||
|
|
||||||
|
|
||||||
const value = useMemo(() => (
|
const value = useMemo(() => (
|
||||||
{teamInfos, gameState, startDate, zoneType, zoneExtremities, nextZoneDate, messages, location, getLocationAuthorization, startLocationTracking, stopLocationTracking}
|
{teamInfos, gameState, startDate, zoneType, zoneExtremities, nextZoneDate, messages, location, getLocationAuthorization, startLocationTracking, stopLocationTracking}
|
||||||
), [teamInfos, gameState, startDate, zoneType, zoneExtremities, nextZoneDate, messages, location, getLocationAuthorization, startLocationTracking, stopLocationTracking]);
|
), [teamInfos, gameState, startDate, zoneType, zoneExtremities, nextZoneDate, messages, location, getLocationAuthorization, startLocationTracking, stopLocationTracking]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<teamContext.Provider value={value}>
|
<TeamContext.Provider value={value}>
|
||||||
{children}
|
{children}
|
||||||
</teamContext.Provider>
|
</TeamContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useTeamContext = () => {
|
export const useTeamContext = () => {
|
||||||
return useContext(teamContext);
|
return useContext(TeamContext);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,33 +1,28 @@
|
|||||||
import { useSocket } from "../context/socketContext";
|
// React
|
||||||
import { useTeamConnexion } from "../context/teamConnexionContext";
|
import { useCallback } from "react";
|
||||||
import { useTeamContext } from "../context/teamContext";
|
// Hook
|
||||||
|
import { useSocketCommands } from "./useSocketCommands";
|
||||||
|
|
||||||
export const useGame = () => {
|
export const useGame = () => {
|
||||||
const { teamSocket } = useSocket();
|
const { emitSendPosition, emitCapture } = useSocketCommands();
|
||||||
const { teamId } = useTeamConnexion();
|
|
||||||
const { teamInfos } = useTeamContext();
|
|
||||||
|
|
||||||
function sendCurrentPosition() {
|
const sendCurrentPosition = useCallback(() => {
|
||||||
console.log("Reveal position.");
|
emitSendPosition();
|
||||||
teamSocket.emit("send_position");
|
}, [emitSendPosition]);
|
||||||
}
|
|
||||||
|
|
||||||
function capture(captureCode) {
|
|
||||||
console.log("Try to capture :", captureCode);
|
|
||||||
|
|
||||||
|
const capture = useCallback((captureCode) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
console.warn("Server did not respond to capture emit.");
|
console.warn("Server timeout: capture", captureCode);
|
||||||
reject();
|
reject(new Error("Timeout"));
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
teamSocket.emit("capture", captureCode, (response) => {
|
emitCapture(captureCode, (response) => {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
console.log(response.message);
|
|
||||||
resolve(response);
|
resolve(response);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}, [emitCapture]);
|
||||||
|
|
||||||
return {...teamInfos, sendCurrentPosition, capture, teamId};
|
return { sendCurrentPosition, capture };
|
||||||
};
|
};
|
||||||
|
|||||||
45
mobile/traque-app/hook/useImageApi.jsx
Normal file
45
mobile/traque-app/hook/useImageApi.jsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// Rect
|
||||||
|
import { useCallback, useMemo } from "react";
|
||||||
|
// Contexts
|
||||||
|
import { useTeamConnexion } from "../context/teamConnexionContext";
|
||||||
|
import { useTeamContext } from '../context/teamContext';
|
||||||
|
// Util
|
||||||
|
import { SERVER_URL } from "../util/constants";
|
||||||
|
|
||||||
|
export const useImageApi = () => {
|
||||||
|
const { teamId } = useTeamConnexion();
|
||||||
|
const { teamInfos } = useTeamContext();
|
||||||
|
const { enemyName } = teamInfos;
|
||||||
|
|
||||||
|
const uploadTeamImage = useCallback(async (imageUri) => {
|
||||||
|
if (!imageUri || !teamId) return;
|
||||||
|
|
||||||
|
const data = new FormData();
|
||||||
|
data.append('file', {
|
||||||
|
uri: imageUri,
|
||||||
|
name: 'photo.jpg',
|
||||||
|
type: 'image/jpeg',
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${SERVER_URL}/upload?team=${teamId}`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: data,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error("Échec de l'upload");
|
||||||
|
return await response.blob();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erreur uploadImage :", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}, [teamId]);
|
||||||
|
|
||||||
|
const enemyImage = useMemo(() => {
|
||||||
|
if (!teamId || !enemyName) return require('../assets/images/missing_image.jpg');
|
||||||
|
|
||||||
|
return {uri: `${SERVER_URL}/photo/enemy?team=${teamId}`};
|
||||||
|
}, [teamId, enemyName]);
|
||||||
|
|
||||||
|
return { enemyImage, uploadTeamImage };
|
||||||
|
};
|
||||||
@@ -1,34 +1,43 @@
|
|||||||
|
// React
|
||||||
|
import { useEffect, useState, useCallback } from "react";
|
||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
|
|
||||||
export const useLocalStorage = (key, initialValue) => {
|
export const useLocalStorage = (key, initialValue) => {
|
||||||
const [storedValue, setStoredValue] = useState(initialValue);
|
const [storedValue, setStoredValue] = useState(initialValue);
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchData() {
|
let isMounted = true;
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
try {
|
try {
|
||||||
const item = await AsyncStorage.getItem(key);
|
const item = await AsyncStorage.getItem(key);
|
||||||
setStoredValue(item ? JSON.parse(item) : initialValue);
|
if (isMounted && item !== null) {
|
||||||
|
setStoredValue(JSON.parse(item));
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.error(`Error loading key "${key}":`, error);
|
||||||
}
|
}
|
||||||
setLoading(false);
|
};
|
||||||
}
|
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [initialValue, key]);
|
return () => { isMounted = false; };
|
||||||
|
}, [key]);
|
||||||
|
|
||||||
const setValue = async value => {
|
const setValue = useCallback(async (value) => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setStoredValue((prevValue) => {
|
||||||
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
const valueToStore = value instanceof Function ? value(prevValue) : value;
|
||||||
setStoredValue(valueToStore);
|
|
||||||
await AsyncStorage.setItem(key, JSON.stringify(valueToStore));
|
|
||||||
setLoading(false);
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return [storedValue, setValue, loading];
|
AsyncStorage.setItem(key, JSON.stringify(valueToStore)).catch(err =>
|
||||||
|
console.error(`Error saving key "${key}":`, err)
|
||||||
|
);
|
||||||
|
|
||||||
|
return valueToStore;
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}, [key]);
|
||||||
|
|
||||||
|
return [storedValue, setValue];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
import { useEffect, useState } from "react";
|
// React
|
||||||
|
import { useEffect, useState, useCallback, useMemo } from "react";
|
||||||
import { Alert } from "react-native";
|
import { Alert } from "react-native";
|
||||||
|
// Expo
|
||||||
import { defineTask, isTaskRegisteredAsync } from "expo-task-manager";
|
import { defineTask, isTaskRegisteredAsync } from "expo-task-manager";
|
||||||
import * as Location from 'expo-location';
|
import * as Location from 'expo-location';
|
||||||
import { useSocket } from "../context/socketContext";
|
// Hook
|
||||||
|
import { useSocketCommands } from "./useSocketCommands";
|
||||||
|
|
||||||
export const useLocation = (timeInterval, distanceInterval) => {
|
export const useLocation = (timeInterval, distanceInterval) => {
|
||||||
|
const { emitUpdatePosition } = useSocketCommands();
|
||||||
const [location, setLocation] = useState(null); // [latitude, longitude]
|
const [location, setLocation] = useState(null); // [latitude, longitude]
|
||||||
const { teamSocket } = useSocket();
|
|
||||||
const LOCATION_TASK_NAME = "background-location-task";
|
const LOCATION_TASK_NAME = "background-location-task";
|
||||||
const locationUpdateParameters = {
|
const locationUpdateParameters = useMemo(() => ({
|
||||||
accuracy: Location.Accuracy.High,
|
accuracy: Location.Accuracy.High,
|
||||||
distanceInterval: distanceInterval, // Update every 10 meters
|
distanceInterval: distanceInterval, // Update every 10 meters
|
||||||
timeInterval: timeInterval, // Minimum interval in ms
|
timeInterval: timeInterval, // Minimum interval in ms
|
||||||
@@ -19,7 +22,7 @@ export const useLocation = (timeInterval, distanceInterval) => {
|
|||||||
notificationBody: "L'application utilise votre position en arrière plan.",
|
notificationBody: "L'application utilise votre position en arrière plan.",
|
||||||
notificationColor: "#FF0000", // (Android) Notification icon color
|
notificationColor: "#FF0000", // (Android) Notification icon color
|
||||||
},
|
},
|
||||||
};
|
}), [distanceInterval, timeInterval]);
|
||||||
|
|
||||||
defineTask(LOCATION_TASK_NAME, async ({ data, error }) => {
|
defineTask(LOCATION_TASK_NAME, async ({ data, error }) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -37,19 +40,14 @@ export const useLocation = (timeInterval, distanceInterval) => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("setLocation failed (probably in background):", e);
|
console.warn("setLocation failed (probably in background):", e);
|
||||||
}
|
}
|
||||||
console.log("Sending position :", new_location);
|
emitUpdatePosition(new_location);
|
||||||
teamSocket.emit("update_position", new_location);
|
|
||||||
} else {
|
} else {
|
||||||
console.log("No location measured.");
|
console.log("No location measured.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
const getLocationAuthorization = useCallback(async () => {
|
||||||
getLocationAuthorization();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
async function getLocationAuthorization() {
|
|
||||||
const { status : statusForeground } = await Location.requestForegroundPermissionsAsync();
|
const { status : statusForeground } = await Location.requestForegroundPermissionsAsync();
|
||||||
const { status : statusBackground } = await Location.requestBackgroundPermissionsAsync();
|
const { status : statusBackground } = await Location.requestBackgroundPermissionsAsync();
|
||||||
if (statusForeground !== "granted" || statusBackground !== "granted") {
|
if (statusForeground !== "granted" || statusBackground !== "granted") {
|
||||||
@@ -58,23 +56,27 @@ export const useLocation = (timeInterval, distanceInterval) => {
|
|||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}, []);
|
||||||
|
|
||||||
async function startLocationTracking() {
|
const startLocationTracking = useCallback(async () => {
|
||||||
if (await getLocationAuthorization()) {
|
if (await getLocationAuthorization()) {
|
||||||
if (!(await isTaskRegisteredAsync(LOCATION_TASK_NAME))) {
|
if (!(await isTaskRegisteredAsync(LOCATION_TASK_NAME))) {
|
||||||
await Location.startLocationUpdatesAsync(LOCATION_TASK_NAME, locationUpdateParameters);
|
await Location.startLocationUpdatesAsync(LOCATION_TASK_NAME, locationUpdateParameters);
|
||||||
console.log("Location tracking started.");
|
console.log("Location tracking started.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}, [getLocationAuthorization, locationUpdateParameters]);
|
||||||
|
|
||||||
async function stopLocationTracking() {
|
const stopLocationTracking = useCallback(async () => {
|
||||||
if (await isTaskRegisteredAsync(LOCATION_TASK_NAME)) {
|
if (await isTaskRegisteredAsync(LOCATION_TASK_NAME)) {
|
||||||
await Location.stopLocationUpdatesAsync(LOCATION_TASK_NAME);
|
await Location.stopLocationUpdatesAsync(LOCATION_TASK_NAME);
|
||||||
console.log("Location tracking stopped.");
|
console.log("Location tracking stopped.");
|
||||||
}
|
}
|
||||||
}
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getLocationAuthorization();
|
||||||
|
}, [getLocationAuthorization]);
|
||||||
|
|
||||||
return [location, getLocationAuthorization, startLocationTracking, stopLocationTracking];
|
return [location, getLocationAuthorization, startLocationTracking, stopLocationTracking];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { useState, } from 'react';
|
// React
|
||||||
|
import { useState, useCallback } from 'react';
|
||||||
import { Alert } from 'react-native';
|
import { Alert } from 'react-native';
|
||||||
|
// Expo
|
||||||
import { launchImageLibraryAsync, requestMediaLibraryPermissionsAsync } from 'expo-image-picker';
|
import { launchImageLibraryAsync, requestMediaLibraryPermissionsAsync } from 'expo-image-picker';
|
||||||
|
|
||||||
export const usePickImage = () => {
|
export const usePickImage = () => {
|
||||||
const [image, setImage] = useState(null);
|
const [image, setImage] = useState(null);
|
||||||
|
|
||||||
const pickImage = async () => {
|
const pickImage = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const permissionResult = await requestMediaLibraryPermissionsAsync();
|
const permissionResult = await requestMediaLibraryPermissionsAsync();
|
||||||
|
|
||||||
@@ -13,7 +15,6 @@ export const usePickImage = () => {
|
|||||||
Alert.alert("Permission refusée", "Activez l'accès au stockage ou à la gallerie dans les paramètres.");
|
Alert.alert("Permission refusée", "Activez l'accès au stockage ou à la gallerie dans les paramètres.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = await launchImageLibraryAsync({
|
let result = await launchImageLibraryAsync({
|
||||||
mediaTypes: ['images'],
|
mediaTypes: ['images'],
|
||||||
allowsMultipleSelection: false,
|
allowsMultipleSelection: false,
|
||||||
@@ -28,31 +29,11 @@ export const usePickImage = () => {
|
|||||||
else {
|
else {
|
||||||
console.log('Image picker cancelled.');
|
console.log('Image picker cancelled.');
|
||||||
}
|
}
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
console.error('Error picking image;', error);
|
console.error('Error picking image;', error);
|
||||||
Alert.alert('Erreur', "Une erreur est survenue lors de la sélection d'une image.");
|
Alert.alert('Erreur', "Une erreur est survenue lors de la sélection d'une image.");
|
||||||
}
|
}
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
function sendImage(location) {
|
return {image, pickImage};
|
||||||
if (image) {
|
|
||||||
let data = new FormData();
|
|
||||||
data.append('file', {
|
|
||||||
uri: image.uri,
|
|
||||||
name: 'photo.jpg',
|
|
||||||
type: 'image/jpeg',
|
|
||||||
});
|
|
||||||
|
|
||||||
fetch(location , {
|
|
||||||
method: 'POST',
|
|
||||||
body: data,
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'multipart/form-data',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {image, pickImage, sendImage};
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,35 +1,47 @@
|
|||||||
import { useEffect } from 'react';
|
// React
|
||||||
|
import { useEffect, useRef } from 'react';
|
||||||
import DeviceInfo from 'react-native-device-info';
|
import DeviceInfo from 'react-native-device-info';
|
||||||
import { useSocket } from "../context/socketContext";
|
// Context
|
||||||
import { useTeamConnexion } from "../context/teamConnexionContext";
|
import { useTeamConnexion } from "../context/teamConnexionContext";
|
||||||
|
// Hook
|
||||||
|
import { useSocketCommands } from "./useSocketCommands";
|
||||||
|
|
||||||
export const useSendDeviceInfo = () => {
|
export const useSendDeviceInfo = () => {
|
||||||
const batteryUpdateTimeout = 5*60*1000;
|
const { emitBattery, emitDeviceInfo } = useSocketCommands();
|
||||||
const { teamSocket } = useSocket();
|
const { loggedIn } = useTeamConnexion();
|
||||||
const {loggedIn} = useTeamConnexion();
|
const isMounted = useRef(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
isMounted.current = true;
|
||||||
|
|
||||||
if (!loggedIn) return;
|
if (!loggedIn) return;
|
||||||
|
|
||||||
const sendInfo = async () => {
|
const sendInfo = async () => {
|
||||||
const brand = DeviceInfo.getBrand();
|
const [brand, model, name] = await Promise.all([
|
||||||
const model = DeviceInfo.getModel();
|
DeviceInfo.getBrand(),
|
||||||
const name = await DeviceInfo.getDeviceName();
|
DeviceInfo.getModel(),
|
||||||
teamSocket.emit('device_info', {model: brand + " " + model, name: name});
|
DeviceInfo.getDeviceName()
|
||||||
|
]);
|
||||||
|
if (!isMounted) return;
|
||||||
|
emitDeviceInfo({model: brand + " " + model, name: name});
|
||||||
};
|
};
|
||||||
|
|
||||||
const sendBattery = async () => {
|
const sendBattery = async () => {
|
||||||
const level = await DeviceInfo.getBatteryLevel();
|
const level = await DeviceInfo.getBatteryLevel();
|
||||||
teamSocket.emit('battery_update', Math.round(level * 100));
|
if (!isMounted) return;
|
||||||
|
emitBattery(Math.round(level * 100));
|
||||||
};
|
};
|
||||||
|
|
||||||
sendInfo();
|
sendInfo();
|
||||||
sendBattery();
|
sendBattery();
|
||||||
|
|
||||||
const batteryCheckInterval = setInterval(() => sendBattery(), batteryUpdateTimeout);
|
const batteryCheckInterval = setInterval(() => sendBattery(), 5*60*1000); // 5 minutes
|
||||||
|
|
||||||
return () => clearInterval(batteryCheckInterval);
|
return () => {
|
||||||
}, [batteryUpdateTimeout, loggedIn, teamSocket]);
|
isMounted.current = false;
|
||||||
|
clearInterval(batteryCheckInterval);
|
||||||
|
};
|
||||||
|
}, [emitBattery, emitDeviceInfo, loggedIn]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,72 +1,60 @@
|
|||||||
import { useEffect, useState } from 'react';
|
// React
|
||||||
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||||
|
// Hook
|
||||||
import { useLocalStorage } from './useLocalStorage';
|
import { useLocalStorage } from './useLocalStorage';
|
||||||
|
import { useSocketCommands } from "./useSocketCommands";
|
||||||
|
|
||||||
const LOGIN_MESSAGE = "login";
|
export const useSocketAuth = () => {
|
||||||
const LOGOUT_MESSAGE = "logout";
|
const { emitLogin, emitLogout } = useSocketCommands();
|
||||||
|
|
||||||
export const useSocketAuth = (socket, passwordName) => {
|
|
||||||
const [loggedIn, setLoggedIn] = useState(false);
|
const [loggedIn, setLoggedIn] = useState(false);
|
||||||
const [loading, setLoading] = useState(true);
|
const [savedPassword, setSavedPassword] = useLocalStorage("team_password", null);
|
||||||
const [waitingForResponse, setWaitingForResponse] = useState(false);
|
const isMounted = useRef(true);
|
||||||
const [hasTriedSavedPassword, setHasTriedSavedPassword] = useState(false);
|
|
||||||
const [savedPassword, setSavedPassword, savedPasswordLoading] = useLocalStorage(passwordName, null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !hasTriedSavedPassword) {
|
isMounted.current = true;
|
||||||
console.log("Try to log in with saved password :", savedPassword);
|
return () => {
|
||||||
setWaitingForResponse(true);
|
isMounted.current = false;
|
||||||
|
};
|
||||||
const timeout = setTimeout(() => {
|
}, []);
|
||||||
console.warn("Server did not respond to login emit.");
|
|
||||||
setWaitingForResponse(false);
|
|
||||||
}, 3000);
|
|
||||||
|
|
||||||
socket.emit(LOGIN_MESSAGE, savedPassword, (response) => {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
console.log(response.message);
|
|
||||||
setLoggedIn(response.isLoggedIn);
|
|
||||||
setWaitingForResponse(false);
|
|
||||||
});
|
|
||||||
setHasTriedSavedPassword(true);
|
|
||||||
}
|
|
||||||
}, [hasTriedSavedPassword, loading, savedPassword, socket]);
|
|
||||||
|
|
||||||
function login(password) {
|
|
||||||
console.log("Try to log in with :", password);
|
|
||||||
setSavedPassword(password);
|
|
||||||
setWaitingForResponse(true);
|
|
||||||
|
|
||||||
|
const login = useCallback((password) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
|
if (!isMounted.current) return;
|
||||||
console.warn("Server did not respond to login emit.");
|
console.warn("Server did not respond to login emit.");
|
||||||
setWaitingForResponse(false);
|
|
||||||
reject();
|
reject();
|
||||||
}, 3000);
|
}, 2000);
|
||||||
|
|
||||||
socket.emit(LOGIN_MESSAGE, password, (response) => {
|
emitLogin(password, (response) => {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
console.log(response.message);
|
|
||||||
setLoggedIn(response.isLoggedIn);
|
if (!isMounted.current) return;
|
||||||
setWaitingForResponse(false);
|
|
||||||
resolve(response);
|
if (response.isLoggedIn) {
|
||||||
|
console.log("Log in accepted.");
|
||||||
|
setLoggedIn(true);
|
||||||
|
setSavedPassword(password);
|
||||||
|
resolve(response);
|
||||||
|
} else {
|
||||||
|
console.log("Log in refused.");
|
||||||
|
setLoggedIn(false);
|
||||||
|
reject();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}, [emitLogin, setSavedPassword]);
|
||||||
|
|
||||||
function logout() {
|
|
||||||
console.log("Logout");
|
|
||||||
setSavedPassword(null);
|
|
||||||
setLoggedIn(false);
|
|
||||||
socket.emit(LOGOUT_MESSAGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(!waitingForResponse && !savedPasswordLoading) {
|
if (!loggedIn && savedPassword) {
|
||||||
setLoading(false);
|
login(savedPassword);
|
||||||
} else {
|
|
||||||
setLoading(true);
|
|
||||||
}
|
}
|
||||||
}, [waitingForResponse, savedPasswordLoading]);
|
}, [loggedIn, login, savedPassword]);
|
||||||
|
|
||||||
return {login, logout, password: savedPassword, loggedIn, loading};
|
const logout = useCallback(() => {
|
||||||
|
setLoggedIn(false);
|
||||||
|
setSavedPassword(null);
|
||||||
|
emitLogout();
|
||||||
|
}, [emitLogout, setSavedPassword]);
|
||||||
|
|
||||||
|
return {login, logout, password: savedPassword, loggedIn};
|
||||||
};
|
};
|
||||||
|
|||||||
52
mobile/traque-app/hook/useSocketCommands.jsx
Normal file
52
mobile/traque-app/hook/useSocketCommands.jsx
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// React
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
// Context
|
||||||
|
import { useSocket } from "../context/socketContext";
|
||||||
|
|
||||||
|
export const useSocketCommands = () => {
|
||||||
|
const { teamSocket } = useSocket();
|
||||||
|
|
||||||
|
const emitLogin = useCallback((password, callback) => {
|
||||||
|
if (!teamSocket?.connected) return;
|
||||||
|
console.log("Try to log in with :", password);
|
||||||
|
teamSocket.emit("login", password, callback);
|
||||||
|
}, [teamSocket]);
|
||||||
|
|
||||||
|
const emitLogout = useCallback(() => {
|
||||||
|
if (!teamSocket?.connected) return;
|
||||||
|
console.log("Logout.");
|
||||||
|
teamSocket.emit("logout");
|
||||||
|
}, [teamSocket]);
|
||||||
|
|
||||||
|
const emitSendPosition = useCallback(() => {
|
||||||
|
if (!teamSocket?.connected) return;
|
||||||
|
console.log("Reveal position.");
|
||||||
|
teamSocket.emit("send_position");
|
||||||
|
}, [teamSocket]);
|
||||||
|
|
||||||
|
const emitUpdatePosition = useCallback((location) => {
|
||||||
|
if (!teamSocket?.connected) return;
|
||||||
|
console.log("Update position :", location);
|
||||||
|
teamSocket.emit("update_position", location);
|
||||||
|
}, [teamSocket]);
|
||||||
|
|
||||||
|
const emitCapture = useCallback((captureCode, callback) => {
|
||||||
|
if (!teamSocket?.connected) return;
|
||||||
|
console.log("Try to capture :", captureCode);
|
||||||
|
teamSocket.emit("capture", captureCode, callback);
|
||||||
|
}, [teamSocket]);
|
||||||
|
|
||||||
|
const emitBattery = useCallback((level) => {
|
||||||
|
if (!teamSocket?.connected) return;
|
||||||
|
console.log("Send battery level.");
|
||||||
|
teamSocket.emit('battery_update', level);
|
||||||
|
}, [teamSocket]);
|
||||||
|
|
||||||
|
const emitDeviceInfo = useCallback((infos) => {
|
||||||
|
if (!teamSocket?.connected) return;
|
||||||
|
console.log("Send device infos.");
|
||||||
|
teamSocket.emit('device_info', infos);
|
||||||
|
}, [teamSocket]);
|
||||||
|
|
||||||
|
return { emitLogin, emitLogout, emitSendPosition, emitUpdatePosition, emitCapture, emitBattery, emitDeviceInfo };
|
||||||
|
};
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
|
// React
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
export const useSocketListener = (socket, event, callback) => {
|
export const useSocketListener = (socket, event, callback) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
socket.on(event,callback);
|
socket.on(event, callback);
|
||||||
return () => {
|
return () => {
|
||||||
socket.off(event, callback);
|
socket.off(event, callback);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// React
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
export const useTimeDifference = (refTime, timeout) => {
|
export const useTimeDifference = (refTime, timeout) => {
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
export const SERVER_URL = process.env.EXPO_PUBLIC_SERVER_URL;
|
||||||
|
export const SOCKET_URL = process.env.EXPO_PUBLIC_SOCKET_URL;
|
||||||
|
|
||||||
export const InitialRegions = {
|
export const InitialRegions = {
|
||||||
paris : {
|
paris : {
|
||||||
latitude: 48.864,
|
latitude: 48.864,
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export default {
|
|||||||
getNewTeamId() {
|
getNewTeamId() {
|
||||||
let id = randint(1_000_000);
|
let id = randint(1_000_000);
|
||||||
while (this.teams.find(t => t.id === id)) id = randint(1_000_000);
|
while (this.teams.find(t => t.id === id)) id = randint(1_000_000);
|
||||||
return id;
|
return id.toString().padStart(6, '0');
|
||||||
},
|
},
|
||||||
|
|
||||||
checkEndGame() {
|
checkEndGame() {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const upload = multer({
|
|||||||
fileFilter: function (req, file, callback) {
|
fileFilter: function (req, file, callback) {
|
||||||
if (ALLOWED_MIME.indexOf(file.mimetype) == -1) {
|
if (ALLOWED_MIME.indexOf(file.mimetype) == -1) {
|
||||||
callback(null, false);
|
callback(null, false);
|
||||||
} else if (!game.getTeam(Number(req.query.team))) {
|
} else if (!game.getTeam(req.query.team)) {
|
||||||
callback(null, false);
|
callback(null, false);
|
||||||
} else {
|
} else {
|
||||||
callback(null, true);
|
callback(null, true);
|
||||||
@@ -58,9 +58,9 @@ export function initPhotoUpload() {
|
|||||||
})
|
})
|
||||||
//App handler for serving the photo of a team given its secret ID
|
//App handler for serving the photo of a team given its secret ID
|
||||||
app.get("/photo/my", (req, res) => {
|
app.get("/photo/my", (req, res) => {
|
||||||
let team = game.getTeam(Number(req.query.team));
|
let team = game.getTeam(req.query.team);
|
||||||
if (team) {
|
if (team) {
|
||||||
const imagePath = path.join(process.cwd(), UPLOAD_DIR, team.id.toString());
|
const imagePath = path.join(process.cwd(), UPLOAD_DIR, team.id);
|
||||||
res.set("Content-Type", "image/png")
|
res.set("Content-Type", "image/png")
|
||||||
res.set("Access-Control-Allow-Origin", "*");
|
res.set("Access-Control-Allow-Origin", "*");
|
||||||
res.sendFile(fs.existsSync(imagePath) ? imagePath : path.join(process.cwd(), "images", "missing_image.jpg"));
|
res.sendFile(fs.existsSync(imagePath) ? imagePath : path.join(process.cwd(), "images", "missing_image.jpg"));
|
||||||
@@ -70,9 +70,9 @@ export function initPhotoUpload() {
|
|||||||
})
|
})
|
||||||
//App handler for serving the photo of the team chased by the team given by its secret ID
|
//App handler for serving the photo of the team chased by the team given by its secret ID
|
||||||
app.get("/photo/enemy", (req, res) => {
|
app.get("/photo/enemy", (req, res) => {
|
||||||
let team = game.getTeam(Number(req.query.team));
|
let team = game.getTeam(req.query.team);
|
||||||
if (team) {
|
if (team) {
|
||||||
const imagePath = path.join(process.cwd(), UPLOAD_DIR, team.chasing.toString());
|
const imagePath = path.join(process.cwd(), UPLOAD_DIR, team.chasing);
|
||||||
res.set("Content-Type", "image/png")
|
res.set("Content-Type", "image/png")
|
||||||
res.set("Access-Control-Allow-Origin", "*");
|
res.set("Access-Control-Allow-Origin", "*");
|
||||||
res.sendFile(fs.existsSync(imagePath) ? imagePath : path.join(process.cwd(), "images", "missing_image.jpg"));
|
res.sendFile(fs.existsSync(imagePath) ? imagePath : path.join(process.cwd(), "images", "missing_image.jpg"));
|
||||||
|
|||||||
Reference in New Issue
Block a user