diff --git a/client/app/(tabs)/index.tsx b/client/app/(tabs)/index.tsx index b0637d6..5d5bb65 100644 --- a/client/app/(tabs)/index.tsx +++ b/client/app/(tabs)/index.tsx @@ -6,6 +6,7 @@ import { FAB, Surface, Text } from 'react-native-paper' import { useGame } from '@/hooks/useGame' import { FontAwesome6 } from '@expo/vector-icons' import FreeChaseBanner from '@/components/FreeChaseBanner' +import { View } from 'react-native' export default function MapScreen() { const [backgroundStatus, requestBackgroundPermission] = useBackgroundPermissions() @@ -16,20 +17,22 @@ export default function MapScreen() { return ( - {backgroundStatus?.granted ? : La géolocalisation est requise pour utiliser la carte.} - 0} - icon={(props) => } - color='black' - label={`${game.money}`} /> - 0} - size='small' - color='black' - icon={game.currentRunner ? 'run-fast' : () => } - label={game.currentRunner ? "Coureuse" : "Poursuiveuse"} /> + + {backgroundStatus?.granted ? : La géolocalisation est requise pour utiliser la carte.} + 0} + icon={(props) => } + color='black' + label={`${game.money}`} /> + 0} + size='small' + color='black' + icon={game.currentRunner ? 'run-fast' : () => } + label={game.currentRunner ? "Coureuse" : "Poursuiveuse"} /> + ) @@ -39,6 +42,11 @@ const styles = StyleSheet.create({ page: { flex: 1, }, + container: { + flexGrow: 1, + alignItems: 'center', + justifyContent: 'center', + }, map: { flex: 1, alignSelf: 'stretch', diff --git a/client/components/GeolocationProvider.tsx b/client/components/GeolocationProvider.tsx index e62dfb4..5b072fd 100644 --- a/client/components/GeolocationProvider.tsx +++ b/client/components/GeolocationProvider.tsx @@ -1,9 +1,10 @@ import { ReactNode, useEffect } from 'react' import { useAuth } from '@/hooks/useAuth' -import { useQueuedLocations, useUnqueueLocation } from '@/hooks/useLocation' +import { useQueuedLocations, useSetLastPlayerLocations, useUnqueueLocation } from '@/hooks/useLocation' import { useGeolocationMutation } from '@/hooks/mutations/useGeolocationMutation' import { useStartGeolocationServiceEffect } from '@/utils/geolocation' import { Platform } from 'react-native' +import { useQuery } from '@tanstack/react-query' export default function GeolocationProvider({ children }: { children: ReactNode }) { useStartGeolocationServiceEffect() @@ -11,6 +12,7 @@ export default function GeolocationProvider({ children }: { children: ReactNode const auth = useAuth() const geolocationsQueue = useQueuedLocations() const unqueueLocation = useUnqueueLocation() + const setLastPlayerLocations = useSetLastPlayerLocations() const geolocationMutation = useGeolocationMutation({ auth, onPostSuccess: (data) => { @@ -27,6 +29,22 @@ export default function GeolocationProvider({ children }: { children: ReactNode geolocationMutation.mutate(locToSend) }, [auth, geolocationsQueue]) + const lastLocationsQuery = useQuery({ + queryKey: ['get-last-locations', auth.token], + queryFn: () => fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/geolocations/last-locations/`, { + method: "GET", + headers: { + "Authorization": `Bearer ${auth.token}`, + "Content-Type": "application/json", + }} + ).then(resp => resp.json()), + refetchInterval: 5000, + }) + useEffect(() => { + if (lastLocationsQuery.isSuccess && lastLocationsQuery.data) + setLastPlayerLocations(lastLocationsQuery.data) + }, [lastLocationsQuery.status, lastLocationsQuery.dataUpdatedAt]) + return <> {children} diff --git a/client/components/Map.tsx b/client/components/Map.tsx index 7278efb..2ab2a17 100644 --- a/client/components/Map.tsx +++ b/client/components/Map.tsx @@ -2,12 +2,14 @@ import { StyleSheet } from 'react-native' import MapLibreGL, { Camera, FillLayer, LineLayer, MapView, PointAnnotation, RasterLayer, RasterSource, ShapeSource, UserLocation } from '@maplibre/maplibre-react-native' import { FontAwesome5 } from '@expo/vector-icons' import { circle } from '@turf/circle' -import { useLastOwnLocation } from '@/hooks/useLocation' +import { useLastOwnLocation, useLastPlayerLocations } from '@/hooks/useLocation' +import { useMemo } from 'react' +import { PlayerLocation } from '@/utils/features/location/locationSlice' +import { useGame } from '@/hooks/useGame' export default function Map() { const userLocation = useLastOwnLocation() MapLibreGL.setAccessToken(null) - const accuracyCircle = circle([userLocation?.coords.longitude ?? 0, userLocation?.coords.latitude ?? 0], userLocation?.coords.accuracy ?? 0, {steps: 64, units: 'meters'}) return ( } + - {/* FIXME Il faudra avoir uniquement les positions des autres personnes, puisque sa propre position peut être obtenue nativement */} - - - - - - - {/* */} + + ) } + +function PlayerLocationsMarkers() { + const game = useGame() + const lastPlayerLocations = useLastPlayerLocations() + return lastPlayerLocations + .filter(playerLoc => playerLoc.playerId !== game.playerId) + .map(playerLoc => ) +} + +function PlayerLocationMarker({ playerLocation }: { playerLocation: PlayerLocation }) { + const accuracyCircle = useMemo(() => circle([playerLocation.longitude, playerLocation.latitude], playerLocation.accuracy, {steps: 64, units: 'meters'}), [playerLocation]) + return <> + + + + + + + +} + const styles = StyleSheet.create({ map: { flex: 1, diff --git a/client/components/Map.web.tsx b/client/components/Map.web.tsx index 32fa375..edf538b 100644 --- a/client/components/Map.web.tsx +++ b/client/components/Map.web.tsx @@ -1,10 +1,10 @@ -import { useAuth } from "@/hooks/useAuth" -import { useLastOwnLocation } from "@/hooks/useLocation" -import { useQuery } from "@tanstack/react-query" +import { useGame } from "@/hooks/useGame" +import { useLastOwnLocation, useLastPlayerLocations } from "@/hooks/useLocation" +import { PlayerLocation } from "@/utils/features/location/locationSlice" import { circle } from "@turf/circle" import { type Map as MaplibreGLMap } from "maplibre-gl" import { RLayer, RMap, RMarker, RNavigationControl, RSource, useMap } from "maplibre-react-components" -import { useEffect, useMemo, useState } from "react" +import { useMemo, useState } from "react" export default function Map() { return ( @@ -18,7 +18,7 @@ export default function Map() { - + ) } @@ -34,43 +34,44 @@ function UserLocation() { const accuracyCircle = circle([userLocation?.coords.longitude ?? 0, userLocation?.coords.latitude ?? 0], userLocation?.coords.accuracy ?? 0, {steps: 64, units: 'meters'}) const marker = userLocation ? : <> return <> - - - + + + {marker} } -function DownloadedLocation() { - const auth = useAuth() - const query = useQuery({ - queryKey: ['get-last-locations'], - queryFn: () => fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/geolocations/last-locations/`, { - method: "GET", - headers: { - "Authorization": `Bearer ${auth.token}`, - "Content-Type": "application/json", - }} - ).then(resp => resp.json()), - }) - useEffect(() => { - const interval = setInterval(() => query.refetch(), 5000) - return () => clearInterval(interval) - }, []) - console.log(query.data) - const userLocation = query.isSuccess ? query.data[0] : { longitude: 0, latitude: 0, accuracy: 0 } - const [firstUserPositionFetched, setFirstUserPositionFetched] = useState(false) +function PlayerLocationsMarkers() { + const game = useGame() + const lastPlayerLocations = useLastPlayerLocations() + return lastPlayerLocations + // .filter(playerLoc => playerLoc.playerId !== game.playerId) + .map(playerLoc => ) +} + +function PlayerLocationMarker({ playerLocation }: { playerLocation: PlayerLocation }) { const map: MaplibreGLMap = useMap() - if (userLocation != null && !firstUserPositionFetched) { - setFirstUserPositionFetched(true) - map.flyTo({center: [userLocation.longitude, userLocation.latitude], zoom: 15}) - } - const accuracyCircle = useMemo(() => circle([userLocation?.longitude ?? 0, userLocation?.latitude ?? 0], userLocation?.accuracy ?? 0, {steps: 64, units: 'meters'}), [userLocation]) - const marker = userLocation ? : <> + const accuracyCircle = useMemo(() => circle( + [playerLocation.longitude, playerLocation.latitude], + playerLocation.accuracy, + {steps: 64, units: 'meters'}), [playerLocation]) return <> - - - - {marker} + + + + } diff --git a/client/hooks/useLocation.ts b/client/hooks/useLocation.ts index eb4efb7..2f8f2fe 100644 --- a/client/hooks/useLocation.ts +++ b/client/hooks/useLocation.ts @@ -1,9 +1,10 @@ import { LocationObject } from "expo-location" import { useAppDispatch, useAppSelector } from "./useStore" -import { setLastLocation, unqueueLocation } from "@/utils/features/location/locationSlice" +import { PlayerLocation, setLastLocation, setLastPlayerLocations, unqueueLocation } from "@/utils/features/location/locationSlice" export const useLastOwnLocation = () => useAppSelector((state) => state.location.lastOwnLocation) export const useQueuedLocations = () => useAppSelector((state) => state.location.queuedLocations) +export const useLastPlayerLocations = () => useAppSelector((state) => state.location.lastPlayerLocations) export const useSetLastLocation = () => { const dispatch = useAppDispatch() @@ -13,3 +14,7 @@ export const useUnqueueLocation = () => { const dispatch = useAppDispatch() return (location: LocationObject) => dispatch(unqueueLocation(location)) } +export const useSetLastPlayerLocations = () => { + const dispatch = useAppDispatch() + return (playerLocations: PlayerLocation[]) => dispatch(setLastPlayerLocations(playerLocations)) +} diff --git a/client/utils/features/location/locationSlice.ts b/client/utils/features/location/locationSlice.ts index c57ec66..65810c7 100644 --- a/client/utils/features/location/locationSlice.ts +++ b/client/utils/features/location/locationSlice.ts @@ -2,16 +2,30 @@ import { Constants } from '@/constants/Constants' import { createSlice, PayloadAction } from '@reduxjs/toolkit' import { LocationObject } from 'expo-location' +export type PlayerLocation = { + id: number + playerId: number + longitude: number + latitude: number + speed: number + accuracy: number + altitude: number + altitudeAccuracy: number + timestamp: string +} + interface LocationState { lastOwnLocation: LocationObject | null lastSentLocation: LocationObject | null queuedLocations: LocationObject[] + lastPlayerLocations: PlayerLocation[] } const initialState: LocationState = { lastOwnLocation: null, lastSentLocation: null, - queuedLocations: [] + queuedLocations: [], + lastPlayerLocations: [] } export const locationSlice = createSlice({ @@ -33,9 +47,12 @@ export const locationSlice = createSlice({ || loc.coords.latitude !== sentLoc.coords.latitude || loc.coords.longitude !== sentLoc.coords.latitude) }, + setLastPlayerLocations: (state, action: PayloadAction) => { + state.lastPlayerLocations = action.payload + } }, }) -export const { setLastLocation, unqueueLocation } = locationSlice.actions +export const { setLastLocation, unqueueLocation, setLastPlayerLocations } = locationSlice.actions export default locationSlice.reducer