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 React, { useMemo, useState } from 'react' 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 { 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 ( <> ) } 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 ( {userLocation && { if (followUser && !event.nativeEvent.payload.followUserLocation) setFollowUser(false) }} />} ) } 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 => ) : <> } 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 <> { setDisplayedPlayerId(playerLocation.playerId) }}> } const styles = StyleSheet.create({ map: { flex: 1, 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 ( {displayedPlayerName} Dernière position : {new Date(displayedPlayerLoc?.timestamp ?? 0).toLocaleString()} Précision : {displayedPlayerLoc?.accuracy.toPrecision(3)} m ) }