This commit is contained in:
Sebastien Riviere
2026-02-13 00:22:29 +01:00
parent eb72a1e5da
commit 5f16500634
11 changed files with 79 additions and 37 deletions

View File

@@ -11,9 +11,9 @@ Problèmes
[ ] 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 photo d'une des équipes ne parvenait pas jusqu'au serveur. Tout semblait normal pour l'équipe.
[ ] 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.
[ ] 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
voyants des équipes encore en jeu soit mis en avant.
[x] La visibilité des éléments de la map en calque satellite est mauvaise. Il faut revoir les couleurs.
[ ] Les zones polygonales s'affichent mal sur la version prod de l'app mobile. Les anciennes n'ont pas l'air de disparaitre.

View File

@@ -16,9 +16,9 @@ qu'un seul téléphone connecté.
[ ] La photo d'une des équipes ne parvenait pas jusqu'au serveur. Tout semblait normal pour l'équipe.
[ ] Il y a apparement eu un problème de synchronisation de l'affichage entre admin concernant les zones de départs. Il s'est résolu
de lui même. Peut être un problème passager de connection.
[ ] 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.
[ ] 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
voyants des équipes encore en jeu soit mis en avant.
[x] La visibilité des éléments de la map en calque satellite est mauvaise. Il faut revoir les couleurs.
[ ] Les zones polygonales s'affichent mal sur la version prod de l'app mobile. Les anciennes n'ont pas l'air de disparaitre.

View File

@@ -29,7 +29,7 @@
"android"
],
"android": {
"package": "com.anonymous.traqueapp",
"package": "net.rezel.traque",
"permissions": [
"ACCESS_FINE_LOCATION",
"ACCESS_COARSE_LOCATION",
@@ -48,7 +48,7 @@
}
},
"ios": {
"bundleIdentifier": "com.anonymous.traqueapp",
"bundleIdentifier": "net.rezel.traque",
"infoPlist": {
"UIBackgroundModes": [
"location"

View File

@@ -131,7 +131,7 @@ export default function Display() {
if (!Number.isInteger(time)) return "Inconnue";
if (time < 0) time = 0;
const hours = Math.floor(time / 3600);
const minutes = Math.floor(time / 60);
const minutes = Math.floor(time / 60 % 60);
const seconds = Math.floor(time % 60);
return String(hours).padStart(2,"0") + ":" + String(minutes).padStart(2,"0") + ":" + String(seconds).padStart(2,"0");
}

View File

@@ -1,9 +1,8 @@
import { createContext, useContext, useMemo } from "react";
import { io } from "socket.io-client";
import { URLS } from "../util/urls"
const SOCKET_URL = `wss://${URLS.HOST}/player`;
const SERVER_URL = `https://${URLS.HOST}/back`;
const SOCKET_URL = `ws://172.16.1.180/player`;
const SERVER_URL = `http://172.16.1.180/back`;
export const teamSocket = io(SOCKET_URL, {
path: "/back/socket.io",

View File

@@ -1,3 +0,0 @@
export const URLS = {
HOST: 'traque.rezel.net'
}

View File

@@ -10,7 +10,6 @@ export default function LiveMap({ selectedTeamId, onSelected, isFocusing, setIsF
const [timeLeftNextZone, setTimeLeftNextZone] = useState(null);
const [isFullScreen, setIsFullScreen] = useState(false);
useEffect(() => {
if (nextZoneDate) {
const updateTime = () => {
@@ -38,13 +37,13 @@ export default function LiveMap({ selectedTeamId, onSelected, isFocusing, setIsF
switch (zoneType) {
case ZoneTypes.CIRCLE:
return (<>
<CircleZone circle={zoneExtremities.begin} color="red" />
<CircleZone circle={zoneExtremities.end} color="green" />
<CircleZone circle={zoneExtremities.begin} color={mapStyle.currentZoneColor} />
<CircleZone circle={zoneExtremities.end} color={mapStyle.nextZoneColor} />
</>);
case ZoneTypes.POLYGON:
return (<>
<PolygonZone polygon={zoneExtremities.begin?.polygon} color="red" />
<PolygonZone polygon={zoneExtremities.end?.polygon} color="green" />
<PolygonZone polygon={zoneExtremities.begin?.polygon} color={mapStyle.currentZoneColor} />
<PolygonZone polygon={zoneExtremities.end?.polygon} color={mapStyle.nextZoneColor} />
</>);
default:
return null;
@@ -55,14 +54,14 @@ export default function LiveMap({ selectedTeamId, onSelected, isFocusing, setIsF
<div className={`${isFullScreen ? "fixed inset-0 z-[9999]" : "relative h-full w-full"}`}>
<CustomMapContainer mapStyle={mapStyle}>
{isFocusing && <MapPan center={getTeam(selectedTeamId)?.currentLocation} zoom={mapZooms.high} animate />}
<MapEventListener onDragStart={() => setIsFocusing(false)}/>
<MapEventListener onDragStart={() => setIsFocusing(false)} onWheel={() => setIsFocusing(false)} />
<Zones/>
{teams.map((team) => team && <Fragment key={team.id}>
<CircleZone circle={team.startingArea} color="blue" display={gameState == GameState.PLACEMENT && showZones}>
<CircleZone circle={team.startingArea} color={mapStyle.placementZoneColor} display={gameState == GameState.PLACEMENT && showZones}>
<Tag text={team.name} display={showNames} />
</CircleZone>
<Arrow pos1={team.currentLocation} pos2={getTeam(team.chasing)?.currentLocation} display={showArrows}/>
<Position position={team.currentLocation} color={"blue"} onClick={() => onSelected(team.id)} display={!team.captured}>
<Arrow pos1={team.currentLocation} pos2={getTeam(team.chasing)?.currentLocation} color={mapStyle.arrowColor} display={showArrows}/>
<Position position={team.currentLocation} color={mapStyle.playerColor} onClick={() => onSelected(team.id)} display={!team.captured}>
<Tag text={team.name} display={showNames} />
</Position>
</Fragment>)}

View File

@@ -1,6 +1,7 @@
import { List } from '@/components/list';
import useAdmin from '@/hook/useAdmin';
import { getStatus } from '@/util/functions';
import { useMemo } from 'react';
function TeamViewerItem({ team }) {
const { gameState } = useAdmin();
@@ -8,7 +9,7 @@ function TeamViewerItem({ team }) {
const NO_VALUE = "XX";
return (
<div className={'w-full flex flex-row gap-3 p-2 bg-white justify-between'}>
<div className={`w-full flex flex-row gap-3 p-2 ${team.captured ? 'bg-gray-200' : 'bg-white'} justify-between`}>
<div className='flex flex-row items-center gap-3'>
<div className='flex flex-row gap-1'>
<img src={`/icons/user/${team.sockets.length > 0 ? "green" : "red"}.png`} className="w-4 h-4" />
@@ -27,8 +28,16 @@ function TeamViewerItem({ team }) {
export default function TeamViewer({selectedTeamId, onSelected}) {
const { teams } = useAdmin();
// Uncaptured teams first
const sortedTeams = useMemo(() => {
return [...teams].sort((a,b) => {
if (a.captured === b.captured) return 0;
return a.captured ? 1 : -1;
});
}, [teams]);
return (
<List array={teams} selectedId={selectedTeamId} onSelected={onSelected} >
<List array={sortedTeams} selectedId={selectedTeamId} onSelected={onSelected} >
{(team) => (
<TeamViewerItem team={team}/>
)}

View File

@@ -3,13 +3,18 @@ import { Marker, Tooltip, CircleMarker, Circle, Polygon, useMap } from "react-le
import "leaflet/dist/leaflet.css";
import 'leaflet-polylinedecorator';
export function Node({position, nodeSize = 5, color = 'black', display = true}) {
export function Node({position, color = 'black', display = true}) {
const nodeSize = 5;
const fillOpacity = 1;
return (
display && position && <CircleMarker center={position} radius={nodeSize} pathOptions={{ color: color, fillColor: color, fillOpacity: 1 }} />
display && position && <CircleMarker center={position} radius={nodeSize} pathOptions={{ color: color, fillColor: color, fillOpacity: fillOpacity }} />
);
}
export function Label({position, label, color, size = 24, display = true}) {
export function Label({position, label = "", color = "black", display = true}) {
const size = 24;
const labelIcon = L.divIcon({
html: `<div style="
display: flex;
@@ -18,7 +23,7 @@ export function Label({position, label, color, size = 24, display = true}) {
color: ${color};
font-weight: bold;
font-size: ${size}px;
">${label || ""}</div>`,
">${label}</div>`,
className: 'custom-label-icon',
iconSize: [size, size],
iconAnchor: [size / 2, size / 2]
@@ -29,13 +34,19 @@ export function Label({position, label, color, size = 24, display = true}) {
);
}
export function Tag({text, display = true}) {
export function Tag({text = "", display = true}) {
const offset = [0.5, -15];
const opacity = 1;
return (
display && <Tooltip permanent direction="top" offset={[0.5, -15]} className="custom-tooltip">{text || ""}</Tooltip>
display && <Tooltip permanent direction="top" offset={offset} opacity={opacity} className="custom-tooltip">{text}</Tooltip>
);
}
export function CircleZone({circle, color, opacity = '0.1', border = 3, display = true, children}) {
export function CircleZone({circle, color = "black", display = true, children}) {
const opacity = '0.1';
const border = 3;
return (
display && circle &&
<Circle center={circle.center} radius={circle.radius} pathOptions={{ color: color, fillColor: color, fillOpacity: opacity, weight: border }}>
@@ -44,7 +55,10 @@ export function CircleZone({circle, color, opacity = '0.1', border = 3, display
);
}
export function PolygonZone({polygon, color, opacity = '0.1', border = 3, display = true, children}) {
export function PolygonZone({polygon, color = "black", display = true, children}) {
const opacity = '0.1';
const border = 3;
return (
display && polygon && polygon.length >= 3 &&
<Polygon positions={polygon} pathOptions={{ color: color, fillColor: color, fillOpacity: opacity, weight: border }}>
@@ -53,7 +67,7 @@ export function PolygonZone({polygon, color, opacity = '0.1', border = 3, displa
);
}
export function Position({position, color, onClick, display = true, children}) {
export function Position({position, color = "blue", onClick = () => {}, display = true, children}) {
const positionIcon = new L.Icon({
iconUrl: `/icons/marker/${color}.png`,
@@ -71,7 +85,10 @@ export function Position({position, color, onClick, display = true, children}) {
);
}
export function Arrow({ pos1, pos2, color = 'black', weight = 5, arrowSize = 20, insetPixels = 25, display = true }) {
export function Arrow({ pos1, pos2, color = 'black', display = true }) {
const weight = 5;
const arrowSize = 20;
const insetPixels = 25;
const map = useMap();
const [insetPositions, setInsetPositions] = useState(null);

View File

@@ -15,7 +15,7 @@ export function MapPan({center, zoom, animate=false}) {
return null;
}
export function MapEventListener({ onLeftClick, onRightClick, onMouseMove, onDragStart }) {
export function MapEventListener({ onLeftClick, onRightClick, onMouseMove, onDragStart, onWheel }) {
const map = useMap();
// TODO use useMapEvents instead of this + detect when zoom
@@ -93,6 +93,17 @@ export function MapEventListener({ onLeftClick, onRightClick, onMouseMove, onDra
map.off('dragstart', onDragStart);
}
}, [onDragStart]);
useEffect(() => {
if (!onWheel) return;
const container = map.getContainer();
container.addEventListener('wheel', onWheel);
return () => {
container.removeEventListener('wheel', onWheel);
}
}, [onWheel]);
// Prevent right click context menu
useEffect(() => {

View File

@@ -12,11 +12,21 @@ export const mapZooms = {
export const mapStyles = {
default: {
url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
arrowColor: "black",
currentZoneColor: "red",
nextZoneColor: "green",
placementZoneColor: "blue",
playerColor: "blue"
},
satellite: {
url: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
attribution: 'Tiles &copy; Esri'
attribution: 'Tiles &copy; Esri',
arrowColor: "white",
currentZoneColor: "red",
nextZoneColor: "#0F0",
placementZoneColor: "#0FF",
playerColor: "blue"
},
}