diff --git a/src/components/Map/usePostsEngine.js b/src/components/Map/usePostsEngine.js index c1c742e..6fcd682 100644 --- a/src/components/Map/usePostsEngine.js +++ b/src/components/Map/usePostsEngine.js @@ -464,12 +464,34 @@ export function usePostsEngine({ const map = mapRef.current; if (!map) return; initialFetchKickRef.current = true; - map.__swForceFetchAt = Date.now(); - setTimeout(() => { + + // Poll until map has valid center, then trigger fetch + let attempts = 0; + const maxAttempts = 20; + const pollInterval = setInterval(() => { + attempts++; try { - map.fire("moveend"); - } catch {} - }, 120); + const center = map.getCenter(); + if (center && center.lat != null && center.lng != null) { + clearInterval(pollInterval); + map.__swForceFetchAt = Date.now(); + console.log('[usePostsEngine] Initial fetch kick - map center ready:', center.lat, center.lng); + map.fire("moveend"); + } else if (attempts >= maxAttempts) { + clearInterval(pollInterval); + // Force it anyway after max attempts + map.__swForceFetchAt = Date.now(); + console.log('[usePostsEngine] Initial fetch kick - forcing after max attempts'); + map.fire("moveend"); + } + } catch (e) { + if (attempts >= maxAttempts) { + clearInterval(pollInterval); + } + } + }, 100); + + return () => clearInterval(pollInterval); }, [mapReady, mapRef]); // CRITICAL: Dedicated effect for contentFilter changes (media filter buttons) @@ -1234,6 +1256,9 @@ export function usePostsEngine({ } try { + // Clear initial load flag now that we're actually fetching + initialLoadRef.current = false; + setLoadingPosts(true); setLoadError(""); @@ -1362,6 +1387,24 @@ export function usePostsEngine({ } setLoadingPosts(false); + + // Auto-retry if we got very few posts (network timing issue) + const MIN_EXPECTED_POSTS = 5; + const fetchedCount = Array.isArray(newPosts) ? newPosts.length : 0; + if (fetchedCount < MIN_EXPECTED_POSTS && initialLoadRef.current) { + const retryKey = `retry:${Math.round(lat * 100)}|${Math.round(lng * 100)}`; + const now = Date.now(); + if (retryRef.current.key !== retryKey || now - retryRef.current.ts > 10000) { + retryRef.current = { key: retryKey, count: 0, ts: now }; + } + if (retryRef.current.count < 2) { + retryRef.current.count += 1; + retryRef.current.ts = now; + console.log(`[usePostsEngine] Low post count (${fetchedCount}), auto-retry #${retryRef.current.count}`); + if (delayedFetchRef.current) clearTimeout(delayedFetchRef.current); + delayedFetchRef.current = setTimeout(load, 800); + } + } } catch (err) { setLoadingPosts(false); setLoadError("fetch failed"); @@ -1391,8 +1434,8 @@ export function usePostsEngine({ const forceFetchNow = mapObj?.__swForceFetchAt && Date.now() - mapObj.__swForceFetchAt < 5000; if (initialLoadRef.current || forceFetchNow) { - initialLoadRef.current = false; - // Note: flag is cleared inside load() at line ~1095, not here + // Don't clear initialLoadRef here - let load() clear it after successful fetch + // This way if load() returns early (no view center), we'll retry on next effect run load(); // Immediate fetch for initial load or filter change } else { moveDebounceRef.current = setTimeout(load, 300);