diff --git a/client/components/Map.tsx b/client/components/Map.tsx
index 1af026b..0446c10 100644
--- a/client/components/Map.tsx
+++ b/client/components/Map.tsx
@@ -1,27 +1,52 @@
-import { StyleSheet } from 'react-native'
-import MapLibreGL, { Camera, FillLayer, LineLayer, MapView, PointAnnotation, RasterLayer, RasterSource, ShapeSource, UserLocation, UserTrackingMode } from '@maplibre/maplibre-react-native'
import { FontAwesome5, MaterialIcons } from '@expo/vector-icons'
+import MapLibreGL, { Camera, FillLayer, LineLayer, MapView, PointAnnotation, RasterLayer, RasterSource, ShapeSource, UserLocation, UserTrackingMode } from '@maplibre/maplibre-react-native'
+import { useQuery } from '@tanstack/react-query'
import { circle } from '@turf/circle'
-import { useLastOwnLocation, useLastPlayerLocations } from '@/hooks/useLocation'
import React, { useMemo, useState } from 'react'
-import { PlayerLocation } from '@/utils/features/location/locationSlice'
+import { StyleSheet } from 'react-native'
+import { Button, Dialog, FAB, Portal, Text } from 'react-native-paper'
+import { useAuth } from '@/hooks/useAuth'
import { useGame } from '@/hooks/useGame'
-import { FAB } from 'react-native-paper'
+import { useLastOwnLocation, useLastPlayerLocations } from '@/hooks/useLocation'
+import { isAuthValid } from '@/utils/features/auth/authSlice'
+import { Player } from '@/utils/features/game/gameSlice'
+import { PlayerLocation } from '@/utils/features/location/locationSlice'
export default function Map() {
const [followUser, setFollowUser] = useState(true)
+
return (
<>
-
- }
- onPress={() => setFollowUser(followUser => !followUser)} />
+
+
>
)
}
-function MapComponent({ followUser, setFollowUser }: { followUser?: boolean, setFollowUser: React.Dispatch> }) {
+type FollowUserProps = {
+ followUser: boolean,
+ setFollowUser: React.Dispatch>,
+}
+
+function MapWrapper({ followUser, setFollowUser }: FollowUserProps) {
+ const [displayedPlayerId, setDisplayedPlayerId] = useState(null)
+ return (
+ <>
+
+
+ setDisplayedPlayerId(null)} />
+
+ >
+ )
+}
+
+type MapComponentProps = {
+ followUser?: boolean,
+ setFollowUser: React.Dispatch>,
+ setDisplayedPlayerId: React.Dispatch>
+}
+
+function MapComponent({ followUser, setFollowUser, setDisplayedPlayerId }: MapComponentProps) {
MapLibreGL.setAccessToken(null)
const userLocation = useLastOwnLocation()
return (
@@ -44,22 +69,22 @@ function MapComponent({ followUser, setFollowUser }: { followUser?: boolean, set
-
+
)
}
-function PlayerLocationsMarkers() {
+function PlayerLocationsMarkers({ setDisplayedPlayerId }: { setDisplayedPlayerId: React.Dispatch> }) {
const game = useGame()
const lastPlayerLocations = useLastPlayerLocations()
return lastPlayerLocations ? lastPlayerLocations
.filter(() => game.currentRunner === true || !game.gameStarted)
.filter(playerLoc => playerLoc.playerId !== game.playerId)
- .map(playerLoc => ) : <>>
+ .map(playerLoc => ) : <>>
}
-function PlayerLocationMarker({ playerLocation }: { playerLocation: PlayerLocation }) {
+function PlayerLocationMarker({ playerLocation, setDisplayedPlayerId }: { playerLocation: PlayerLocation, setDisplayedPlayerId: React.Dispatch> }) {
const accuracyCircle = useMemo(() => circle([playerLocation.longitude, playerLocation.latitude], playerLocation.accuracy, {steps: 64, units: 'meters'}), [playerLocation])
return <>
+ coordinate={[playerLocation.longitude, playerLocation.latitude]}
+ onSelected={() => { setDisplayedPlayerId(playerLocation.playerId) }}>
>
@@ -88,3 +114,56 @@ const styles = StyleSheet.create({
alignSelf: 'stretch',
}
})
+
+function FollowUserButton({ followUser, setFollowUser }: FollowUserProps) {
+ return (
+ }
+ onPress={() => setFollowUser(followUser => !followUser)} />
+ )
+}
+
+function PlayerLocationDialog({ displayedPlayerId, onDismiss }: { displayedPlayerId: number | null, onDismiss: () => void }) {
+ const auth = useAuth()
+ const lastPlayerLocations = useLastPlayerLocations()
+ const playersQuery = useQuery({
+ queryKey: ['get-players', auth.token],
+ queryFn: () => fetch(`${process.env.EXPO_PUBLIC_TRAINTRAPE_MOI_SERVER}/players/`, {
+ headers: { "Authorization": `Bearer ${auth.token}` }}
+ ).then(resp => resp.json()),
+ enabled: isAuthValid(auth),
+ initialData: { data: [], meta: { currentPage: 0, lastPage: 0, nextPage: 0, prevPage: 0, total: 0, totalPerPage: 0 } },
+ })
+ const displayedPlayerLoc = useMemo(() => {
+ return lastPlayerLocations.find(loc => loc.playerId === displayedPlayerId)
+ }, [displayedPlayerId, lastPlayerLocations])
+ const displayedPlayerName = useMemo(() => {
+ if (!playersQuery.isSuccess || !displayedPlayerId)
+ return "Chargement…"
+ const player: Player | undefined = playersQuery.data.data.find((player: Player) => player.id === displayedPlayerId)
+ if (!player)
+ return "Chargement…"
+ return player.name
+ }, [displayedPlayerId, playersQuery])
+
+ return (
+
+ )
+}
diff --git a/client/utils/features/game/gameSlice.ts b/client/utils/features/game/gameSlice.ts
index a91b2ce..9ef7205 100644
--- a/client/utils/features/game/gameSlice.ts
+++ b/client/utils/features/game/gameSlice.ts
@@ -20,6 +20,13 @@ export interface PenaltyPayload {
penaltyEnd: number | null
}
+export interface Player {
+ id: number
+ name: string
+ money: number
+ activeChallengeId: number | null
+}
+
export interface GameState {
playerId: number | null
runId: number | null