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;
}
}