Auto deploy: 2025-12-09 03:38:30
This commit is contained in:
parent
71368d4a5e
commit
c755c0c9c0
|
|
@ -100,7 +100,7 @@ const CATEGORY_MAP = {
|
||||||
export default function MapView() {
|
export default function MapView() {
|
||||||
const containerRef = useRef(null);
|
const containerRef = useRef(null);
|
||||||
const mapRef = useRef(null);
|
const mapRef = useRef(null);
|
||||||
const markersRef = useRef([]);
|
const markersRef = useRef([]); // [{ id, marker, el, compactHTML, expandedHTML }]
|
||||||
const expandedElRef = useRef(null);
|
const expandedElRef = useRef(null);
|
||||||
|
|
||||||
const [status, setStatus] = useState("Loading posts...");
|
const [status, setStatus] = useState("Loading posts...");
|
||||||
|
|
@ -110,7 +110,6 @@ export default function MapView() {
|
||||||
const [subFilter, setSubFilter] = useState(null); // sous-catégorie du bas
|
const [subFilter, setSubFilter] = useState(null); // sous-catégorie du bas
|
||||||
|
|
||||||
const [userPosition, setUserPosition] = useState(null); // [lng, lat]
|
const [userPosition, setUserPosition] = useState(null); // [lng, lat]
|
||||||
const [reloadToken, setReloadToken] = useState(0);
|
|
||||||
|
|
||||||
const [viewParams, setViewParams] = useState({
|
const [viewParams, setViewParams] = useState({
|
||||||
center: null,
|
center: null,
|
||||||
|
|
@ -307,6 +306,102 @@ export default function MapView() {
|
||||||
}
|
}
|
||||||
}, [mainFilter, bottomCategories, subFilter]);
|
}, [mainFilter, bottomCategories, subFilter]);
|
||||||
|
|
||||||
|
/* ------------ FACTEUR EN COMMUN: CREATION D'UN MARKER POUR UN POST -------------- */
|
||||||
|
const createMarkerForPost = (post) => {
|
||||||
|
const map = mapRef.current;
|
||||||
|
if (!map) return;
|
||||||
|
|
||||||
|
const coords = getCoords(post);
|
||||||
|
if (!coords) return;
|
||||||
|
|
||||||
|
// évite les doublons (même id déjà affiché)
|
||||||
|
if (
|
||||||
|
typeof post.id === "number" &&
|
||||||
|
markersRef.current.some((m) => m.id === post.id)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [lngP, latP] = coords;
|
||||||
|
if (
|
||||||
|
typeof lngP !== "number" ||
|
||||||
|
typeof latP !== "number" ||
|
||||||
|
Number.isNaN(lngP) ||
|
||||||
|
Number.isNaN(latP)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { title, body, short } = extract(post);
|
||||||
|
|
||||||
|
const compactHTML = `
|
||||||
|
<div class="marker-inner">
|
||||||
|
<div class="marker-dot"></div>
|
||||||
|
<span class="marker-text">${short}</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const expandedHTML = `
|
||||||
|
<div class="marker-expanded-inner">
|
||||||
|
<div class="marker-expanded-header">
|
||||||
|
<div class="marker-dot"></div>
|
||||||
|
<span class="marker-expanded-title">${title}</span>
|
||||||
|
</div>
|
||||||
|
<div class="marker-expanded-body">
|
||||||
|
${body}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const el = document.createElement("div");
|
||||||
|
el.className = "post-marker post-marker-compact";
|
||||||
|
el.innerHTML = compactHTML;
|
||||||
|
|
||||||
|
const marker = new maplibregl.Marker({
|
||||||
|
element: el,
|
||||||
|
anchor: "bottom",
|
||||||
|
})
|
||||||
|
.setLngLat(coords)
|
||||||
|
.addTo(map);
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
id: post.id ?? null,
|
||||||
|
marker,
|
||||||
|
el,
|
||||||
|
compactHTML,
|
||||||
|
expandedHTML,
|
||||||
|
};
|
||||||
|
markersRef.current.push(meta);
|
||||||
|
|
||||||
|
const toggle = () => {
|
||||||
|
if (expandedElRef.current && expandedElRef.current !== el) {
|
||||||
|
const prev = markersRef.current.find(
|
||||||
|
(m) => m.el === expandedElRef.current
|
||||||
|
);
|
||||||
|
if (prev) {
|
||||||
|
prev.el.className = "post-marker post-marker-compact";
|
||||||
|
prev.el.innerHTML = prev.compactHTML;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expandedElRef.current === el) {
|
||||||
|
el.className = "post-marker post-marker-compact";
|
||||||
|
el.innerHTML = compactHTML;
|
||||||
|
expandedElRef.current = null;
|
||||||
|
} else {
|
||||||
|
el.className = "post-marker post-marker-expanded";
|
||||||
|
el.innerHTML = expandedHTML;
|
||||||
|
expandedElRef.current = el;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
el.addEventListener("click", toggle);
|
||||||
|
el.addEventListener("touchend", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
toggle();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/* ------------ LOAD POSTS POUR LA ZONE COURANTE + FILTRES -------------- */
|
/* ------------ LOAD POSTS POUR LA ZONE COURANTE + FILTRES -------------- */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const map = mapRef.current;
|
const map = mapRef.current;
|
||||||
|
|
@ -337,86 +432,13 @@ export default function MapView() {
|
||||||
|
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
|
|
||||||
|
// ici on recharge la vue => on enlève tout puis on remet
|
||||||
markersRef.current.forEach((m) => m.marker.remove());
|
markersRef.current.forEach((m) => m.marker.remove());
|
||||||
markersRef.current = [];
|
markersRef.current = [];
|
||||||
expandedElRef.current = null;
|
expandedElRef.current = null;
|
||||||
|
|
||||||
posts.forEach((post) => {
|
posts.forEach((post) => {
|
||||||
const coords = getCoords(post);
|
createMarkerForPost(post);
|
||||||
if (!coords) return;
|
|
||||||
|
|
||||||
const [lngP, latP] = coords;
|
|
||||||
if (
|
|
||||||
typeof lngP !== "number" ||
|
|
||||||
typeof latP !== "number" ||
|
|
||||||
Number.isNaN(lngP) ||
|
|
||||||
Number.isNaN(latP)
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { title, body, short } = extract(post);
|
|
||||||
|
|
||||||
const compactHTML = `
|
|
||||||
<div class="marker-inner">
|
|
||||||
<div class="marker-dot"></div>
|
|
||||||
<span class="marker-text">${short}</span>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
const expandedHTML = `
|
|
||||||
<div class="marker-expanded-inner">
|
|
||||||
<div class="marker-expanded-header">
|
|
||||||
<div class="marker-dot"></div>
|
|
||||||
<span class="marker-expanded-title">${title}</span>
|
|
||||||
</div>
|
|
||||||
<div class="marker-expanded-body">
|
|
||||||
${body}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
const el = document.createElement("div");
|
|
||||||
el.className = "post-marker post-marker-compact";
|
|
||||||
el.innerHTML = compactHTML;
|
|
||||||
|
|
||||||
const marker = new maplibregl.Marker({
|
|
||||||
element: el,
|
|
||||||
anchor: "bottom",
|
|
||||||
})
|
|
||||||
.setLngLat(coords)
|
|
||||||
.addTo(map);
|
|
||||||
|
|
||||||
const meta = { marker, el, compactHTML, expandedHTML };
|
|
||||||
markersRef.current.push(meta);
|
|
||||||
|
|
||||||
const toggle = () => {
|
|
||||||
if (expandedElRef.current && expandedElRef.current !== el) {
|
|
||||||
const prev = markersRef.current.find(
|
|
||||||
(m) => m.el === expandedElRef.current
|
|
||||||
);
|
|
||||||
if (prev) {
|
|
||||||
prev.el.className = "post-marker post-marker-compact";
|
|
||||||
prev.el.innerHTML = prev.compactHTML;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expandedElRef.current === el) {
|
|
||||||
el.className = "post-marker post-marker-compact";
|
|
||||||
el.innerHTML = compactHTML;
|
|
||||||
expandedElRef.current = null;
|
|
||||||
} else {
|
|
||||||
el.className = "post-marker post-marker-expanded";
|
|
||||||
el.innerHTML = expandedHTML;
|
|
||||||
expandedElRef.current = el;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
el.addEventListener("click", toggle);
|
|
||||||
el.addEventListener("touchend", (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
toggle();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
setStatus(posts.length ? "" : "No posts found.");
|
setStatus(posts.length ? "" : "No posts found.");
|
||||||
|
|
@ -431,9 +453,9 @@ export default function MapView() {
|
||||||
return () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
};
|
};
|
||||||
}, [viewParams, mainFilter, timeFilter, subFilter, reloadToken]);
|
}, [viewParams, mainFilter, timeFilter, subFilter]);
|
||||||
|
|
||||||
/* ------------ WEBSOCKET : RELOAD SUR new_post -------------- */
|
/* ------------ WEBSOCKET : AJOUTER JUSTE LE NOUVEAU POST -------------- */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const proto = window.location.protocol === "https:" ? "wss" : "ws";
|
const proto = window.location.protocol === "https:" ? "wss" : "ws";
|
||||||
const host =
|
const host =
|
||||||
|
|
@ -454,10 +476,35 @@ export default function MapView() {
|
||||||
ws.onmessage = (ev) => {
|
ws.onmessage = (ev) => {
|
||||||
try {
|
try {
|
||||||
const msg = JSON.parse(ev.data);
|
const msg = JSON.parse(ev.data);
|
||||||
if (msg.type === "new_post") {
|
if (msg.type === "new_post" && msg.post) {
|
||||||
setReloadToken((n) => n + 1);
|
const p = msg.post;
|
||||||
|
|
||||||
|
// Optionnel: respecter les filtres actuels
|
||||||
|
const catCode = categoryCode(mainFilter);
|
||||||
|
if (catCode && p.category && p.category.toUpperCase() !== catCode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position: ne l'afficher que s'il est dans le rayon courant
|
||||||
|
if (viewParams.center && viewParams.radiusKm) {
|
||||||
|
const coords = getCoords(p);
|
||||||
|
if (coords) {
|
||||||
|
const [lngP, latP] = coords;
|
||||||
|
const [lngC, latC] = viewParams.center;
|
||||||
|
const dKm = haversineKm(latC, lngC, latP, lngP);
|
||||||
|
if (dKm > viewParams.radiusKm * 1.2) {
|
||||||
|
// trop loin de la vue actuelle → on ignore
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// On ajoute juste ce post, sans effacer les autres
|
||||||
|
createMarkerForPost(p);
|
||||||
}
|
}
|
||||||
} catch (_) {}
|
} catch (_) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onerror = (e) => {
|
ws.onerror = (e) => {
|
||||||
|
|
@ -469,7 +516,7 @@ export default function MapView() {
|
||||||
ws.close();
|
ws.close();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, []);
|
}, [mainFilter, viewParams]);
|
||||||
|
|
||||||
/* ------------ ACTIONS UI -------------- */
|
/* ------------ ACTIONS UI -------------- */
|
||||||
|
|
||||||
|
|
@ -479,7 +526,7 @@ export default function MapView() {
|
||||||
map.flyTo({
|
map.flyTo({
|
||||||
center: userPosition,
|
center: userPosition,
|
||||||
zoom: 9,
|
zoom: 9,
|
||||||
speed: 1.1,
|
speed: 1.2,
|
||||||
curve: 1.5,
|
curve: 1.5,
|
||||||
essential: true,
|
essential: true,
|
||||||
});
|
});
|
||||||
|
|
@ -550,7 +597,7 @@ export default function MapView() {
|
||||||
await createPost(payload);
|
await createPost(payload);
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
setIsCreating(false);
|
setIsCreating(false);
|
||||||
setReloadToken((n) => n + 1);
|
// On ne refetch pas tout ici, on laisse le WS rajouter le nouveau
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("createPost error:", e);
|
console.error("createPost error:", e);
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue