From c659c0267d92381f188a56d4dff671f661861e80 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 23 Dec 2025 15:23:44 -0500 Subject: [PATCH] Fix: Hide PWA install button when installed, improve clustering, add debug logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes three major issues: 1. PWA Install Button (TopBar.jsx): - Import isRunningAsPWA() and canInstall() from pwaInstall.js - Add state to track if button should be shown - Listen to beforeinstallprompt and appinstalled events - Hide button when app is already installed or running as PWA 2. Search Suggestions Debug (SmartSearchBar.jsx, recoSearch.js): - Add console.log statements to trace search flow - Track: search query, results count, errors - Helps diagnose why suggestions don't appear - recoSearch: Log each endpoint attempt and response 3. Clustering Improvements (usePostsEngine.js, postPriority.js): - Reduce maxFullCards: 15 → 12 (fewer full cards) - Increase clusterRadius: 0.0015 → 0.002 (~200m for better grouping) - Increase minScoreForCard: 35 → 50 (stricter threshold) - Reduce default recoScore: 50 → 40 (lower baseline) - Add console.log for prioritization results - Result: More posts shown as simple markers, less clutter Debug logs will be visible in browser console to diagnose issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- src/components/Layout/TopBar.jsx | 73 +++++++++++++++++------- src/components/Map/postPriority.js | 2 +- src/components/Map/usePostsEngine.js | 12 +++- src/components/Search/SmartSearchBar.jsx | 3 + src/services/recoSearch.js | 19 +++++- 5 files changed, 81 insertions(+), 28 deletions(-) diff --git a/src/components/Layout/TopBar.jsx b/src/components/Layout/TopBar.jsx index 93151d7..f7387c5 100644 --- a/src/components/Layout/TopBar.jsx +++ b/src/components/Layout/TopBar.jsx @@ -4,7 +4,7 @@ import { registerUser } from "../../api/client"; import AuthToast from "../Auth/AuthToast"; // Import pour le bouton PWA -import { promptInstall } from '../../pwaInstall'; +import { promptInstall, isRunningAsPWA, canInstall } from '../../pwaInstall'; const THEME_LABELS = { dark: "Dark", blue: "Blue", light: "Light" }; const LAST_SIGNUP_KEY = "sociowire:lastSignup"; @@ -83,6 +83,7 @@ export default function TopBar({ theme = "dark", onChangeTheme }) { const [signupError, setSignupError] = useState(""); const [signupInfo, setSignupInfo] = useState(""); const [signupLoading, setSignupLoading] = useState(false); + const [showInstallBtn, setShowInstallBtn] = useState(false); const openModal = (nextMode) => { setMode(nextMode); @@ -100,6 +101,34 @@ export default function TopBar({ theme = "dark", onChangeTheme }) { if (authenticated) setShowAuth(false); }, [authenticated]); + // Check if install button should be shown + useEffect(() => { + const checkInstall = () => { + // Hide button if already running as PWA + if (isRunningAsPWA()) { + setShowInstallBtn(false); + return; + } + // Show button if installation is available + setShowInstallBtn(canInstall()); + }; + + checkInstall(); + + // Listen for beforeinstallprompt to update button visibility + const handlePrompt = () => { + setTimeout(checkInstall, 100); + }; + + window.addEventListener('beforeinstallprompt', handlePrompt); + window.addEventListener('appinstalled', () => setShowInstallBtn(false)); + + return () => { + window.removeEventListener('beforeinstallprompt', handlePrompt); + window.removeEventListener('appinstalled', () => setShowInstallBtn(false)); + }; + }, []); + useEffect(() => { try { const { get, qs, hs, hash } = parseMergedParams(); @@ -265,26 +294,28 @@ export default function TopBar({ theme = "dark", onChangeTheme }) { ))} - {/* BOUTON INSTALL PWA ici en haut */} - + {/* BOUTON INSTALL PWA ici en haut - seulement si pas déjà installé */} + {showInstallBtn && ( + + )} diff --git a/src/components/Map/postPriority.js b/src/components/Map/postPriority.js index 4bce58e..2fc8e18 100644 --- a/src/components/Map/postPriority.js +++ b/src/components/Map/postPriority.js @@ -14,7 +14,7 @@ export function calculatePostPriority(post, viewCenter, allPosts) { let score = 0; // 1. Score intrinsèque du post (si disponible depuis reco-service) - const recoScore = typeof post.score === 'number' ? post.score : 50; + const recoScore = typeof post.score === 'number' ? post.score : 40; // Réduit de 50 à 40 pour base plus basse score += recoScore * 0.4; // Weight: 40% // 2. Catégorie (certaines plus importantes) diff --git a/src/components/Map/usePostsEngine.js b/src/components/Map/usePostsEngine.js index 63522e3..47e34f1 100644 --- a/src/components/Map/usePostsEngine.js +++ b/src/components/Map/usePostsEngine.js @@ -87,9 +87,15 @@ export function usePostsEngine({ const viewCenter = center ? { lng: center.lng, lat: center.lat } : null; const { fullCardPosts, simpleMarkerPosts } = prioritizePosts(visible, viewCenter, { - maxFullCards: 15, // Max 15 cartes complètes - clusterRadius: 0.0015, // ~150m clustering radius - minScoreForCard: 35, // Score minimum pour carte complète + maxFullCards: 12, // Max 12 cartes complètes (réduit de 15) + clusterRadius: 0.002, // ~200m clustering radius (augmenté pour plus de groupement) + minScoreForCard: 50, // Score minimum pour carte complète (augmenté de 35 à 50) + }); + + console.log('[usePostsEngine] Prioritized:', { + total: visible.length, + fullCards: fullCardPosts.length, + simpleMarkers: simpleMarkerPosts.length }); // Combine both types for "wanted" tracking diff --git a/src/components/Search/SmartSearchBar.jsx b/src/components/Search/SmartSearchBar.jsx index e7340c7..aee369e 100644 --- a/src/components/Search/SmartSearchBar.jsx +++ b/src/components/Search/SmartSearchBar.jsx @@ -98,12 +98,15 @@ export default function SmartSearchBar({ const t = setTimeout(async () => { try { + console.log('[SmartSearchBar] Searching for:', query); const res = await recoSearch(query, { signal: ctrl.signal, limit: 12 }); + console.log('[SmartSearchBar] Got results:', res?.length || 0, res); setItems(res); setOpen(true); setActive(res.length ? 0 : -1); } catch (e) { if (ctrl.signal.aborted) return; + console.error('[SmartSearchBar] Search error:', e); setErr((e && e.message) ? e.message : "Search error"); setItems([]); } finally { diff --git a/src/services/recoSearch.js b/src/services/recoSearch.js index dd22b9b..a478813 100644 --- a/src/services/recoSearch.js +++ b/src/services/recoSearch.js @@ -76,10 +76,18 @@ async function tryFetchAnyPath(q, { signal } = {}) { for (const p of paths) { try { const url = `${p}?q=${encodeURIComponent(query)}`; + console.log('[recoSearch] Trying:', url); const res = await fetch(url, { credentials: "include", signal }); - if (res.status === 404) continue; - if (!res.ok) return []; // évite spam erreurs + console.log('[recoSearch] Response status:', res.status); + if (res.status === 404) { + console.log('[recoSearch] 404, trying next path'); + continue; + } + if (!res.ok) { + console.log('[recoSearch] Not OK, returning empty'); + return []; // évite spam erreurs + } const data = await res.json().catch(() => null); const arr = Array.isArray(data) @@ -90,11 +98,16 @@ async function tryFetchAnyPath(q, { signal } = {}) { ? data.results : []; + console.log('[recoSearch] Success! Returning', arr.length, 'results from', p); return arr.map(normalizeOne).filter(Boolean); } catch (e) { // Abort => stop net - if (e && (e.name === "AbortError" || e.code === 20)) return []; + if (e && (e.name === "AbortError" || e.code === 20)) { + console.log('[recoSearch] Aborted'); + return []; + } // autres erreurs => on teste prochain path + console.log('[recoSearch] Error on', p, ':', e.message, '- trying next'); continue; } }