mirror of
https://git.rezel.net/LudoTech/traque.git
synced 2026-04-11 00:30:19 +02:00
Fix photos + API hooks + cleaning
This commit is contained in:
@@ -8,22 +8,22 @@ import { CustomButton } from '../components/button';
|
||||
import { CustomImage } from '../components/image';
|
||||
import { CustomTextInput } from '../components/input';
|
||||
// Contexts
|
||||
import { useSocket } from "../context/socketContext";
|
||||
import { useTeamConnexion } from "../context/teamConnexionContext";
|
||||
import { useTeamContext } from "../context/teamContext";
|
||||
// Hooks
|
||||
import { usePickImage } from '../hook/usePickImage';
|
||||
import { useImageApi } from '../hook/useImageApi';
|
||||
// Util
|
||||
import { Colors } from '../util/colors';
|
||||
|
||||
const Index = () => {
|
||||
const router = useRouter();
|
||||
const {SERVER_URL} = useSocket();
|
||||
const {login, loggedIn, loading} = useTeamConnexion();
|
||||
const {login, loggedIn} = useTeamConnexion();
|
||||
const {getLocationAuthorization, stopLocationTracking} = useTeamContext();
|
||||
const {image, pickImage, sendImage} = usePickImage();
|
||||
const [teamID, setTeamID] = useState("");
|
||||
const {image, pickImage} = usePickImage();
|
||||
const [teamId, setTeamId] = useState("");
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const { uploadTeamImage } = useImageApi();
|
||||
|
||||
// Disbaling location tracking
|
||||
useEffect(() => {
|
||||
@@ -32,32 +32,37 @@ const Index = () => {
|
||||
|
||||
// Routeur
|
||||
useEffect(() => {
|
||||
if (!loading && loggedIn) {
|
||||
if (loggedIn) {
|
||||
uploadTeamImage(image?.uri);
|
||||
router.replace("/interface");
|
||||
}
|
||||
}, [router, loggedIn, loading]);
|
||||
}, [router, loggedIn, uploadTeamImage, image]);
|
||||
|
||||
function handleSubmit() {
|
||||
if (!isSubmitting && !loading) {
|
||||
setIsSubmitting(true);
|
||||
if (getLocationAuthorization()) {
|
||||
login(parseInt(teamID))
|
||||
.then((response) => {
|
||||
if (response.isLoggedIn) {
|
||||
sendImage(`${SERVER_URL}/upload?team=${teamID}`);
|
||||
} else {
|
||||
Alert.alert("Échec", "L'ID d'équipe est inconnu.");
|
||||
}
|
||||
setIsSubmitting(false);
|
||||
})
|
||||
.catch(() => {
|
||||
Alert.alert("Échec", "La connection au serveur a échoué.");
|
||||
setIsSubmitting(false);
|
||||
});
|
||||
}
|
||||
setTeamID("");
|
||||
const handleSubmit = async () => {
|
||||
if (isSubmitting || !getLocationAuthorization()) return;
|
||||
|
||||
setIsSubmitting(true);
|
||||
|
||||
const regex = /^\d{6}$/;
|
||||
if (!regex.test(teamId)) {
|
||||
setTimeout(() => Alert.alert("Erreur", "Veuillez entrer un ID d'équipe valide."), 100);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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 (
|
||||
<ScrollView contentContainerStyle={styles.container}>
|
||||
@@ -67,7 +72,7 @@ const Index = () => {
|
||||
<Text style={styles.logoText}>LA TRAQUE</Text>
|
||||
</View>
|
||||
<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 style={styles.subContainer}>
|
||||
<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}/>
|
||||
</View>
|
||||
<View style={styles.subContainer}>
|
||||
<CustomButton label={(isSubmitting || loading) ? "..." : "Valider"} onPress={handleSubmit}/>
|
||||
<CustomButton label={isSubmitting ? "..." : "Valider"} onPress={handleSubmit}/>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
||||
@@ -20,9 +20,10 @@ import { Colors } from '../util/colors';
|
||||
|
||||
const Interface = () => {
|
||||
const router = useRouter();
|
||||
const {messages, nextZoneDate, isShrinking, startLocationTracking, stopLocationTracking, gameState} = useTeamContext();
|
||||
const {loggedIn, logout, loading} = useTeamConnexion();
|
||||
const {name, ready, captured, locationSendDeadline, sendCurrentPosition, outOfZone, outOfZoneDeadline, hasHandicap, enemyHasHandicap} = useGame();
|
||||
const {teamInfos, messages, nextZoneDate, isShrinking, startLocationTracking, stopLocationTracking, gameState} = useTeamContext();
|
||||
const {name, ready, captured, locationSendDeadline, outOfZone, outOfZoneDeadline, hasHandicap, enemyHasHandicap} = teamInfos;
|
||||
const {loggedIn, logout} = useTeamConnexion();
|
||||
const {sendCurrentPosition} = useGame();
|
||||
const [timeLeftSendLocation] = useTimeDifference(locationSendDeadline, 1000);
|
||||
const [timeLeftNextZone] = useTimeDifference(nextZoneDate, 1000);
|
||||
const [timeLeftOutOfZone] = useTimeDifference(outOfZoneDeadline, 1000);
|
||||
@@ -48,12 +49,10 @@ const Interface = () => {
|
||||
|
||||
// Router
|
||||
useEffect(() => {
|
||||
if (!loading) {
|
||||
if (!loggedIn) {
|
||||
router.replace("/");
|
||||
}
|
||||
if (!loggedIn) {
|
||||
router.replace("/");
|
||||
}
|
||||
}, [router, loggedIn, loading]);
|
||||
}, [router, loggedIn]);
|
||||
|
||||
// Activating geolocation tracking
|
||||
useEffect(() => {
|
||||
|
||||
@@ -9,7 +9,6 @@ import { CustomTextInput } from './input';
|
||||
import { Stat } from './stat';
|
||||
// Contexts
|
||||
import { useTeamContext } from '../context/teamContext';
|
||||
import { useSocket } from '../context/socketContext';
|
||||
// Hooks
|
||||
import { useTimeDifference } from '../hook/useTimeDifference';
|
||||
import { useGame } from '../hook/useGame';
|
||||
@@ -17,17 +16,18 @@ import { useGame } from '../hook/useGame';
|
||||
import { GameState } from '../util/gameState';
|
||||
import { Colors } from '../util/colors';
|
||||
import { secondsToHHMMSS } from '../util/functions';
|
||||
import { useImageApi } from '../hook/useImageApi';
|
||||
|
||||
export const Drawer = ({ height }) => {
|
||||
const [collapsibleState, setCollapsibleState] = useState(true);
|
||||
const [enemyCaptureCode, setEnemyCaptureCode] = useState("");
|
||||
const {SERVER_URL} = useSocket();
|
||||
const {gameState, startDate} = useTeamContext();
|
||||
const {capture, enemyName, captureCode, name, teamId, distance, finishDate, nCaptures, nSentLocation, hasHandicap} = useGame();
|
||||
const {teamInfos, gameState, startDate} = useTeamContext();
|
||||
const {enemyName, captureCode, name, distance, finishDate, nCaptures, nSentLocation, hasHandicap} = teamInfos;
|
||||
const {capture} = useGame();
|
||||
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 captureStatusColor = {0: "#777", 1: "#FFA500", 2: "#FF6B6B", 3: "#81C784"};
|
||||
const { enemyImage } = useImageApi();
|
||||
|
||||
const avgSpeed = useMemo(() => {
|
||||
const hours = (finishDate ? (finishDate - startDate) : timeSinceStart*1000) / 1000 / 3600;
|
||||
@@ -48,11 +48,6 @@ export const Drawer = ({ height }) => {
|
||||
}
|
||||
}, [captureStatus]);
|
||||
|
||||
// Refresh the image
|
||||
useEffect(() => {
|
||||
setEnemyImageURI(`${SERVER_URL}/photo/enemy?team=${teamId}&t=${new Date().getTime()}`);
|
||||
}, [SERVER_URL, enemyName, teamId]);
|
||||
|
||||
const handleCapture = () => {
|
||||
if (captureStatus != 1) {
|
||||
setCaptureStatus(1);
|
||||
@@ -86,8 +81,8 @@ export const Drawer = ({ height }) => {
|
||||
}
|
||||
{ gameState == GameState.PLAYING && !hasHandicap && <Fragment>
|
||||
<View style={styles.imageContainer}>
|
||||
{<Text style={{fontSize: 15, margin: 5}}>{"Cible (" + (enemyName ?? "Indisponible") + ")"}</Text>}
|
||||
{<CustomImage source={{ uri : enemyImageURI }} canZoom/>}
|
||||
<Text style={{fontSize: 15, margin: 5}}>{"Cible (" + (enemyName ?? "Indisponible") + ")"}</Text>
|
||||
<CustomImage source={enemyImage} canZoom/>
|
||||
</View>
|
||||
<View style={styles.actionsContainer}>
|
||||
<View style={styles.actionsLeftContainer}>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// React
|
||||
import { Fragment } from 'react';
|
||||
import { Polygon } from 'react-native-maps';
|
||||
import { circleToPolygon } from '../util/functions';
|
||||
|
||||
@@ -7,15 +7,13 @@ import LinearGradient from 'react-native-linear-gradient';
|
||||
import { DashedCircle, InvertedCircle, InvertedPolygon } from './layer';
|
||||
// Contexts
|
||||
import { useTeamContext } from '../context/teamContext';
|
||||
// Hooks
|
||||
import { useGame } from '../hook/useGame';
|
||||
// Util
|
||||
import { GameState } from '../util/gameState';
|
||||
import { ZoneTypes, InitialRegions } from '../util/constants';
|
||||
|
||||
export const CustomMap = () => {
|
||||
const {zoneType, zoneExtremities, location, gameState} = useTeamContext();
|
||||
const {enemyLocation, startingArea, lastSentLocation, hasHandicap} = useGame();
|
||||
const {teamInfos, zoneType, zoneExtremities, location, gameState} = useTeamContext();
|
||||
const {enemyLocation, startingArea, lastSentLocation, hasHandicap} = teamInfos;
|
||||
const [centerMap, setCenterMap] = useState(true);
|
||||
const mapRef = useRef(null);
|
||||
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
// React
|
||||
import { createContext, useContext, useMemo } from "react";
|
||||
// IO
|
||||
import { io } from "socket.io-client";
|
||||
// Util
|
||||
import { SOCKET_URL } from "../util/constants";
|
||||
|
||||
const IP = "172.16.1.180";
|
||||
const SOCKET_URL = `ws://${IP}/player`;
|
||||
const SERVER_URL = `http://${IP}/back`;
|
||||
const SocketContext = createContext();
|
||||
|
||||
export const teamSocket = io(SOCKET_URL, {
|
||||
path: "/back/socket.io",
|
||||
});
|
||||
|
||||
export const SocketContext = createContext();
|
||||
const teamSocket = io(SOCKET_URL, {path: "/back/socket.io"});
|
||||
|
||||
export const SocketProvider = ({ children }) => {
|
||||
const value = useMemo(() => ({ teamSocket, SERVER_URL }), []);
|
||||
const value = useMemo(() => ({ teamSocket }), []);
|
||||
|
||||
return (
|
||||
<SocketContext.Provider value={value}>{children}</SocketContext.Provider>
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
// React
|
||||
import { createContext, useContext, useMemo } from "react";
|
||||
import { useSocket } from "./socketContext";
|
||||
// Hook
|
||||
import { useSocketAuth } from "../hook/useSocketAuth";
|
||||
|
||||
const teamConnexionContext = createContext();
|
||||
const TeamConnexionContext = createContext();
|
||||
|
||||
export const TeamConnexionProvider = ({ children }) => {
|
||||
const { teamSocket } = useSocket();
|
||||
const { login, password: teamId, loggedIn, loading, logout } = useSocketAuth(teamSocket, "team_password");
|
||||
const { login, password: teamId, loggedIn, logout } = useSocketAuth();
|
||||
|
||||
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 (
|
||||
<teamConnexionContext.Provider value={value}>
|
||||
<TeamConnexionContext.Provider value={value}>
|
||||
{children}
|
||||
</teamConnexionContext.Provider>
|
||||
</TeamConnexionContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useTeamConnexion = () => {
|
||||
return useContext(teamConnexionContext);
|
||||
return useContext(TeamConnexionContext);
|
||||
};
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
// React
|
||||
import { createContext, useContext, useMemo, useState } from "react";
|
||||
// Context
|
||||
import { useSocket } from "./socketContext";
|
||||
import { useTeamConnexion } from "./teamConnexionContext";
|
||||
import { GameState } from "../util/gameState";
|
||||
// Hook
|
||||
import { useSendDeviceInfo } from "../hook/useSendDeviceInfo";
|
||||
import { useLocation } from "../hook/useLocation";
|
||||
import { useSocketListener } from "../hook/useSocketListener";
|
||||
// Util
|
||||
import { GameState } from "../util/gameState";
|
||||
|
||||
const teamContext = createContext();
|
||||
const TeamContext = createContext();
|
||||
|
||||
export const TeamProvider = ({children}) => {
|
||||
const {teamSocket} = useSocket();
|
||||
@@ -51,18 +55,17 @@ export const TeamProvider = ({children}) => {
|
||||
|
||||
useSocketListener(teamSocket, "logout", logout);
|
||||
|
||||
|
||||
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]);
|
||||
|
||||
return (
|
||||
<teamContext.Provider value={value}>
|
||||
<TeamContext.Provider value={value}>
|
||||
{children}
|
||||
</teamContext.Provider>
|
||||
</TeamContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useTeamContext = () => {
|
||||
return useContext(teamContext);
|
||||
return useContext(TeamContext);
|
||||
};
|
||||
|
||||
@@ -1,33 +1,28 @@
|
||||
import { useSocket } from "../context/socketContext";
|
||||
import { useTeamConnexion } from "../context/teamConnexionContext";
|
||||
import { useTeamContext } from "../context/teamContext";
|
||||
// React
|
||||
import { useCallback } from "react";
|
||||
// Hook
|
||||
import { useSocketCommands } from "./useSocketCommands";
|
||||
|
||||
export const useGame = () => {
|
||||
const { teamSocket } = useSocket();
|
||||
const { teamId } = useTeamConnexion();
|
||||
const { teamInfos } = useTeamContext();
|
||||
const { emitSendPosition, emitCapture } = useSocketCommands();
|
||||
|
||||
function sendCurrentPosition() {
|
||||
console.log("Reveal position.");
|
||||
teamSocket.emit("send_position");
|
||||
}
|
||||
const sendCurrentPosition = useCallback(() => {
|
||||
emitSendPosition();
|
||||
}, [emitSendPosition]);
|
||||
|
||||
function capture(captureCode) {
|
||||
console.log("Try to capture :", captureCode);
|
||||
|
||||
const capture = useCallback((captureCode) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
console.warn("Server did not respond to capture emit.");
|
||||
reject();
|
||||
console.warn("Server timeout: capture", captureCode);
|
||||
reject(new Error("Timeout"));
|
||||
}, 3000);
|
||||
|
||||
teamSocket.emit("capture", captureCode, (response) => {
|
||||
emitCapture(captureCode, (response) => {
|
||||
clearTimeout(timeout);
|
||||
console.log(response.message);
|
||||
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 { useEffect, useState } from "react";
|
||||
|
||||
export const useLocalStorage = (key, initialValue) => {
|
||||
const [storedValue, setStoredValue] = useState(initialValue);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchData() {
|
||||
let isMounted = true;
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const item = await AsyncStorage.getItem(key);
|
||||
setStoredValue(item ? JSON.parse(item) : initialValue);
|
||||
if (isMounted && item !== null) {
|
||||
setStoredValue(JSON.parse(item));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
console.error(`Error loading key "${key}":`, error);
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [initialValue, key]);
|
||||
return () => { isMounted = false; };
|
||||
}, [key]);
|
||||
|
||||
const setValue = async value => {
|
||||
const setValue = useCallback(async (value) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
||||
setStoredValue(valueToStore);
|
||||
await AsyncStorage.setItem(key, JSON.stringify(valueToStore));
|
||||
setLoading(false);
|
||||
setStoredValue((prevValue) => {
|
||||
const valueToStore = value instanceof Function ? value(prevValue) : value;
|
||||
|
||||
AsyncStorage.setItem(key, JSON.stringify(valueToStore)).catch(err =>
|
||||
console.error(`Error saving key "${key}":`, err)
|
||||
);
|
||||
|
||||
return valueToStore;
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
}, [key]);
|
||||
|
||||
return [storedValue, setValue, loading];
|
||||
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";
|
||||
// Expo
|
||||
import { defineTask, isTaskRegisteredAsync } from "expo-task-manager";
|
||||
import * as Location from 'expo-location';
|
||||
import { useSocket } from "../context/socketContext";
|
||||
// Hook
|
||||
import { useSocketCommands } from "./useSocketCommands";
|
||||
|
||||
export const useLocation = (timeInterval, distanceInterval) => {
|
||||
const { emitUpdatePosition } = useSocketCommands();
|
||||
const [location, setLocation] = useState(null); // [latitude, longitude]
|
||||
const { teamSocket } = useSocket();
|
||||
const LOCATION_TASK_NAME = "background-location-task";
|
||||
const locationUpdateParameters = {
|
||||
const locationUpdateParameters = useMemo(() => ({
|
||||
accuracy: Location.Accuracy.High,
|
||||
distanceInterval: distanceInterval, // Update every 10 meters
|
||||
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.",
|
||||
notificationColor: "#FF0000", // (Android) Notification icon color
|
||||
},
|
||||
};
|
||||
}), [distanceInterval, timeInterval]);
|
||||
|
||||
defineTask(LOCATION_TASK_NAME, async ({ data, error }) => {
|
||||
if (error) {
|
||||
@@ -37,19 +40,14 @@ export const useLocation = (timeInterval, distanceInterval) => {
|
||||
} catch (e) {
|
||||
console.warn("setLocation failed (probably in background):", e);
|
||||
}
|
||||
console.log("Sending position :", new_location);
|
||||
teamSocket.emit("update_position", new_location);
|
||||
emitUpdatePosition(new_location);
|
||||
} else {
|
||||
console.log("No location measured.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
getLocationAuthorization();
|
||||
}, []);
|
||||
|
||||
async function getLocationAuthorization() {
|
||||
const getLocationAuthorization = useCallback(async () => {
|
||||
const { status : statusForeground } = await Location.requestForegroundPermissionsAsync();
|
||||
const { status : statusBackground } = await Location.requestBackgroundPermissionsAsync();
|
||||
if (statusForeground !== "granted" || statusBackground !== "granted") {
|
||||
@@ -58,23 +56,27 @@ export const useLocation = (timeInterval, distanceInterval) => {
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
async function startLocationTracking() {
|
||||
const startLocationTracking = useCallback(async () => {
|
||||
if (await getLocationAuthorization()) {
|
||||
if (!(await isTaskRegisteredAsync(LOCATION_TASK_NAME))) {
|
||||
await Location.startLocationUpdatesAsync(LOCATION_TASK_NAME, locationUpdateParameters);
|
||||
console.log("Location tracking started.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [getLocationAuthorization, locationUpdateParameters]);
|
||||
|
||||
async function stopLocationTracking() {
|
||||
const stopLocationTracking = useCallback(async () => {
|
||||
if (await isTaskRegisteredAsync(LOCATION_TASK_NAME)) {
|
||||
await Location.stopLocationUpdatesAsync(LOCATION_TASK_NAME);
|
||||
console.log("Location tracking stopped.");
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getLocationAuthorization();
|
||||
}, [getLocationAuthorization]);
|
||||
|
||||
return [location, getLocationAuthorization, startLocationTracking, stopLocationTracking];
|
||||
};
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { useState, } from 'react';
|
||||
// React
|
||||
import { useState, useCallback } from 'react';
|
||||
import { Alert } from 'react-native';
|
||||
// Expo
|
||||
import { launchImageLibraryAsync, requestMediaLibraryPermissionsAsync } from 'expo-image-picker';
|
||||
|
||||
export const usePickImage = () => {
|
||||
const [image, setImage] = useState(null);
|
||||
|
||||
const pickImage = async () => {
|
||||
const pickImage = useCallback(async () => {
|
||||
try {
|
||||
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.");
|
||||
return;
|
||||
}
|
||||
|
||||
let result = await launchImageLibraryAsync({
|
||||
mediaTypes: ['images'],
|
||||
allowsMultipleSelection: false,
|
||||
@@ -28,31 +29,11 @@ export const usePickImage = () => {
|
||||
else {
|
||||
console.log('Image picker cancelled.');
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
} catch (error) {
|
||||
console.error('Error picking image;', error);
|
||||
Alert.alert('Erreur', "Une erreur est survenue lors de la sélection d'une image.");
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
function sendImage(location) {
|
||||
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};
|
||||
return {image, pickImage};
|
||||
};
|
||||
|
||||
@@ -1,35 +1,47 @@
|
||||
import { useEffect } from 'react';
|
||||
// React
|
||||
import { useEffect, useRef } from 'react';
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
import { useSocket } from "../context/socketContext";
|
||||
// Context
|
||||
import { useTeamConnexion } from "../context/teamConnexionContext";
|
||||
// Hook
|
||||
import { useSocketCommands } from "./useSocketCommands";
|
||||
|
||||
export const useSendDeviceInfo = () => {
|
||||
const batteryUpdateTimeout = 5*60*1000;
|
||||
const { teamSocket } = useSocket();
|
||||
const {loggedIn} = useTeamConnexion();
|
||||
const { emitBattery, emitDeviceInfo } = useSocketCommands();
|
||||
const { loggedIn } = useTeamConnexion();
|
||||
const isMounted = useRef(true);
|
||||
|
||||
useEffect(() => {
|
||||
isMounted.current = true;
|
||||
|
||||
if (!loggedIn) return;
|
||||
|
||||
const sendInfo = async () => {
|
||||
const brand = DeviceInfo.getBrand();
|
||||
const model = DeviceInfo.getModel();
|
||||
const name = await DeviceInfo.getDeviceName();
|
||||
teamSocket.emit('device_info', {model: brand + " " + model, name: name});
|
||||
const [brand, model, name] = await Promise.all([
|
||||
DeviceInfo.getBrand(),
|
||||
DeviceInfo.getModel(),
|
||||
DeviceInfo.getDeviceName()
|
||||
]);
|
||||
if (!isMounted) return;
|
||||
emitDeviceInfo({model: brand + " " + model, name: name});
|
||||
};
|
||||
|
||||
const sendBattery = async () => {
|
||||
const level = await DeviceInfo.getBatteryLevel();
|
||||
teamSocket.emit('battery_update', Math.round(level * 100));
|
||||
if (!isMounted) return;
|
||||
emitBattery(Math.round(level * 100));
|
||||
};
|
||||
|
||||
sendInfo();
|
||||
sendBattery();
|
||||
|
||||
const batteryCheckInterval = setInterval(() => sendBattery(), batteryUpdateTimeout);
|
||||
const batteryCheckInterval = setInterval(() => sendBattery(), 5*60*1000); // 5 minutes
|
||||
|
||||
return () => clearInterval(batteryCheckInterval);
|
||||
}, [batteryUpdateTimeout, loggedIn, teamSocket]);
|
||||
return () => {
|
||||
isMounted.current = false;
|
||||
clearInterval(batteryCheckInterval);
|
||||
};
|
||||
}, [emitBattery, emitDeviceInfo, loggedIn]);
|
||||
|
||||
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 { useSocketCommands } from "./useSocketCommands";
|
||||
|
||||
const LOGIN_MESSAGE = "login";
|
||||
const LOGOUT_MESSAGE = "logout";
|
||||
|
||||
export const useSocketAuth = (socket, passwordName) => {
|
||||
export const useSocketAuth = () => {
|
||||
const { emitLogin, emitLogout } = useSocketCommands();
|
||||
const [loggedIn, setLoggedIn] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [waitingForResponse, setWaitingForResponse] = useState(false);
|
||||
const [hasTriedSavedPassword, setHasTriedSavedPassword] = useState(false);
|
||||
const [savedPassword, setSavedPassword, savedPasswordLoading] = useLocalStorage(passwordName, null);
|
||||
const [savedPassword, setSavedPassword] = useLocalStorage("team_password", null);
|
||||
const isMounted = useRef(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && !hasTriedSavedPassword) {
|
||||
console.log("Try to log in with saved password :", savedPassword);
|
||||
setWaitingForResponse(true);
|
||||
isMounted.current = true;
|
||||
return () => {
|
||||
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) => {
|
||||
const timeout = setTimeout(() => {
|
||||
if (!isMounted.current) return;
|
||||
console.warn("Server did not respond to login emit.");
|
||||
setWaitingForResponse(false);
|
||||
reject();
|
||||
}, 3000);
|
||||
}, 2000);
|
||||
|
||||
socket.emit(LOGIN_MESSAGE, password, (response) => {
|
||||
emitLogin(password, (response) => {
|
||||
clearTimeout(timeout);
|
||||
console.log(response.message);
|
||||
setLoggedIn(response.isLoggedIn);
|
||||
setWaitingForResponse(false);
|
||||
resolve(response);
|
||||
|
||||
if (!isMounted.current) return;
|
||||
|
||||
if (response.isLoggedIn) {
|
||||
console.log("Log in accepted.");
|
||||
setLoggedIn(true);
|
||||
setSavedPassword(password);
|
||||
resolve(response);
|
||||
} else {
|
||||
console.log("Log in refused.");
|
||||
setLoggedIn(false);
|
||||
reject();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function logout() {
|
||||
console.log("Logout");
|
||||
setSavedPassword(null);
|
||||
setLoggedIn(false);
|
||||
socket.emit(LOGOUT_MESSAGE);
|
||||
}
|
||||
}, [emitLogin, setSavedPassword]);
|
||||
|
||||
useEffect(() => {
|
||||
if(!waitingForResponse && !savedPasswordLoading) {
|
||||
setLoading(false);
|
||||
} else {
|
||||
setLoading(true);
|
||||
if (!loggedIn && savedPassword) {
|
||||
login(savedPassword);
|
||||
}
|
||||
}, [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";
|
||||
|
||||
export const useSocketListener = (socket, event, callback) => {
|
||||
useEffect(() => {
|
||||
socket.on(event,callback);
|
||||
socket.on(event, callback);
|
||||
return () => {
|
||||
socket.off(event, callback);
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// React
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
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 = {
|
||||
paris : {
|
||||
latitude: 48.864,
|
||||
@@ -10,4 +13,4 @@ export const InitialRegions = {
|
||||
export const ZoneTypes = {
|
||||
circle: "circle",
|
||||
polygon: "polygon"
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user