diff --git a/src/api/client.js b/src/api/client.js index 88858d7..26c1271 100644 --- a/src/api/client.js +++ b/src/api/client.js @@ -384,6 +384,9 @@ export async function fetchSmartFeed(params = {}) { if (typeof params.radius_km === "number") qs.set("radius_km", String(params.radius_km)); if (typeof params.limit === "number") qs.set("limit", String(params.limit)); if (typeof params.zoom === "number") qs.set("zoom", String(params.zoom)); + if (params.category) qs.set("category", String(params.category)); + if (params.sub_category) qs.set("sub_category", String(params.sub_category)); + if (params.time_filter) qs.set("time_filter", String(params.time_filter)); // NEW: Screen bounds for on-screen prioritization if (params.bounds) { diff --git a/src/components/Map/mapFilter.js b/src/components/Map/mapFilter.js index 511bf4a..af7c689 100644 --- a/src/components/Map/mapFilter.js +++ b/src/components/Map/mapFilter.js @@ -59,12 +59,12 @@ export function matchesTimeFilter(createdAt, timeFilter) { if (timeFilter === "PAST") return true; const d = parseCreatedAt(createdAt); - if (!d) return true; + if (!d) return false; const now = Date.now(); const diffMs = now - d.getTime(); if (!Number.isFinite(diffMs)) return true; - if (diffMs < 0) return true; + if (diffMs < 0) return false; const diffHours = diffMs / 3600000; const diffDays = diffMs / 86400000; @@ -81,6 +81,19 @@ export function matchesTimeFilter(createdAt, timeFilter) { } } +export function effectivePostTime(post) { + const published = + post?.published_at || + post?.publishedAt || + post?.PublishedAt || + ""; + const d = parseCreatedAt(published); + if (!d) return ""; + const now = Date.now(); + if (d.getTime() > now + 5 * 60 * 1000) return ""; + return published; +} + export function isInViewRadius(post, center, radiusKm) { if (!center || !radiusKm) return true; const [lngC, latC] = center; diff --git a/src/components/Map/mapGeo.js b/src/components/Map/mapGeo.js index 894d461..facb21a 100644 --- a/src/components/Map/mapGeo.js +++ b/src/components/Map/mapGeo.js @@ -31,7 +31,7 @@ export function getViewFromMap(map) { radiusKm = 750; } - radiusKm = Math.min(Math.max(radiusKm * 1.2, 25), 1000); + radiusKm = Math.min(Math.max(radiusKm * 1.2, 25), 2500); return { center: [center.lng, center.lat], diff --git a/src/components/Map/markerManager.js b/src/components/Map/markerManager.js index c0a8ccb..d02f813 100644 --- a/src/components/Map/markerManager.js +++ b/src/components/Map/markerManager.js @@ -623,13 +623,25 @@ function formatRelativeTime(createdAt) { } function getPostTime(post) { - return ( + const published = post?.published_at || post?.publishedAt || post?.PublishedAt || + ""; + if (published) { + const d = new Date(String(published)); + if (!Number.isNaN(d.getTime())) { + const now = Date.now(); + if (d.getTime() <= now + 5 * 60 * 1000) { + return published; + } + } + } + return ( post?.created_at || post?.CreatedAt || - post?.createdAt + post?.createdAt || + published ); } diff --git a/src/components/Map/usePostsEngine.js b/src/components/Map/usePostsEngine.js index 918fba1..474f02a 100644 --- a/src/components/Map/usePostsEngine.js +++ b/src/components/Map/usePostsEngine.js @@ -1,6 +1,6 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { fetchPosts, fetchSmartFeed, fetchUnifiedSearch } from "../../api/client"; -import { categoryCode, matchesSubFilter, matchesTimeFilter } from "./mapFilter"; +import { categoryCode, matchesSubFilter, matchesTimeFilter, effectivePostTime } from "./mapFilter"; import { getCoords, getViewFromMap, haversineKm } from "./mapGeo"; import { createMarkerForPost, createSimpleMarkerForPost } from "./markerManager"; import { getTemplateKeyForPost } from "./templateSpecs"; @@ -277,7 +277,7 @@ function buildClusterAreaFeatures(posts) { return features; } -const MAX_POOL_POSTS = 75; +const MAX_POOL_POSTS = 160; const MAX_VISIBLE_POSTS = 45; function prunePosts(posts, center, maxCount) { @@ -326,6 +326,7 @@ export function usePostsEngine({ const initialFetchKickRef = useRef(false); const autoWidenRef = useRef(false); const searchResultsRef = useRef(false); + const replacePoolRef = useRef(false); const retryRef = useRef({ key: "", count: 0, ts: 0 }); const [mapReady, setMapReady] = useState(false); const clusterModeRef = useRef(false); @@ -370,6 +371,14 @@ export function usePostsEngine({ }, 120); }, [mapReady, mapRef]); + useEffect(() => { + const map = mapRef.current; + replacePoolRef.current = true; + if (map) { + map.__swForceFetchAt = Date.now(); + } + }, [mainFilter, subFilter, timeFilter, mapRef]); + const priorityOpts = { maxFullCards: 15, clusterRadius: 0.002, @@ -667,18 +676,13 @@ export function usePostsEngine({ const searchActive = (searchQuery || "").trim().length > 0; let fullCardPosts = []; let simpleMarkerPosts = []; - if (searchActive) { - fullCardPosts = visible || []; - simpleMarkerPosts = []; - } else { - const prioritized = prioritizePosts(visible, viewCenter, { - maxFullCards: 15, // 10-15 cartes complètes selon le zoom - clusterRadius: 0.002, // ~200m clustering radius (augmenté pour plus de groupement) - minScoreForCard: 50, // Score minimum pour carte complète (augmenté de 35 à 50) - }); - fullCardPosts = prioritized.fullCardPosts; - simpleMarkerPosts = prioritized.simpleMarkerPosts; - } + const prioritized = prioritizePosts(visible, viewCenter, { + maxFullCards: 15, // 10-15 cartes complètes selon le zoom + clusterRadius: 0.002, // ~200m clustering radius (augmenté pour plus de groupement) + minScoreForCard: 50, // Score minimum pour carte complète (augmenté de 35 à 50) + }); + fullCardPosts = prioritized.fullCardPosts; + simpleMarkerPosts = prioritized.simpleMarkerPosts; console.log('[usePostsEngine] Prioritized:', { total: visible.length, @@ -792,7 +796,7 @@ export function usePostsEngine({ if (pc !== catCode) return false; } if (!matchesSubFilter(p, subFilter)) return false; - if (!matchesTimeFilter(p.created_at || p.CreatedAt || p.createdAt, tf)) return false; + if (!matchesTimeFilter(effectivePostTime(p), tf)) return false; if (!searchResultsRef.current && !matchesSearch(p, searchQuery)) return false; return true; }); @@ -960,19 +964,33 @@ export function usePostsEngine({ // Use smart feed for personalized, trending posts const zoom = typeof mapObj?.getZoom === "function" ? mapObj.getZoom() : undefined; + let limit = 50; + if (typeof zoom === "number") { + if (zoom <= 6) limit = 90; + else if (zoom <= 8) limit = 70; + else if (zoom <= 10) limit = 60; + } + if (radiusKm > 800) { + limit = Math.max(limit, 80); + } + if (limit > 120) limit = 120; + let newPosts = await fetchSmartFeed({ lat, lon: lng, radius_km: radiusKm, - limit: 50, + limit, zoom, bounds, username: username || undefined, + time_filter: timeFilter || undefined, + category: catCode || undefined, + sub_category: subCatParam || undefined, }); if (cancelled) return; - const existing = allPostsRef.current || []; + const existing = replacePoolRef.current ? [] : allPostsRef.current || []; const byId = new Map(); for (const p of existing) { const normalized = normalizePostId(p); @@ -994,6 +1012,7 @@ export function usePostsEngine({ } allPostsRef.current = prunePosts(Array.from(byId.values()), [lng, lat], MAX_POOL_POSTS); + replacePoolRef.current = false; lastFetchRef.current = { center: [...resolvedView.center], radiusKm, filterKey, timeFilter, bounds }; // Smooth update: sync markers/clusters + wall without clearing everything @@ -1145,7 +1164,7 @@ export function usePostsEngine({ if (pc !== catCode) return; } if (!matchesSubFilter(p, subFilter)) return; - if (!matchesTimeFilter(p.created_at || p.CreatedAt || p.createdAt, timeFilter)) return; + if (!matchesTimeFilter(effectivePostTime(p), timeFilter)) return; if (!matchesSearch(p, searchQuery)) return; if (clusterModeRef.current) {