Fix: Hide PWA install button when installed, improve clustering, add debug logs
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 <noreply@anthropic.com>
This commit is contained in:
parent
76adb74833
commit
c659c0267d
|
|
@ -4,7 +4,7 @@ import { registerUser } from "../../api/client";
|
||||||
import AuthToast from "../Auth/AuthToast";
|
import AuthToast from "../Auth/AuthToast";
|
||||||
|
|
||||||
// Import pour le bouton PWA
|
// 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 THEME_LABELS = { dark: "Dark", blue: "Blue", light: "Light" };
|
||||||
const LAST_SIGNUP_KEY = "sociowire:lastSignup";
|
const LAST_SIGNUP_KEY = "sociowire:lastSignup";
|
||||||
|
|
@ -83,6 +83,7 @@ export default function TopBar({ theme = "dark", onChangeTheme }) {
|
||||||
const [signupError, setSignupError] = useState("");
|
const [signupError, setSignupError] = useState("");
|
||||||
const [signupInfo, setSignupInfo] = useState("");
|
const [signupInfo, setSignupInfo] = useState("");
|
||||||
const [signupLoading, setSignupLoading] = useState(false);
|
const [signupLoading, setSignupLoading] = useState(false);
|
||||||
|
const [showInstallBtn, setShowInstallBtn] = useState(false);
|
||||||
|
|
||||||
const openModal = (nextMode) => {
|
const openModal = (nextMode) => {
|
||||||
setMode(nextMode);
|
setMode(nextMode);
|
||||||
|
|
@ -100,6 +101,34 @@ export default function TopBar({ theme = "dark", onChangeTheme }) {
|
||||||
if (authenticated) setShowAuth(false);
|
if (authenticated) setShowAuth(false);
|
||||||
}, [authenticated]);
|
}, [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(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
const { get, qs, hs, hash } = parseMergedParams();
|
const { get, qs, hs, hash } = parseMergedParams();
|
||||||
|
|
@ -265,7 +294,8 @@ export default function TopBar({ theme = "dark", onChangeTheme }) {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* BOUTON INSTALL PWA ici en haut */}
|
{/* BOUTON INSTALL PWA ici en haut - seulement si pas déjà installé */}
|
||||||
|
{showInstallBtn && (
|
||||||
<button
|
<button
|
||||||
onClick={() => promptInstall()}
|
onClick={() => promptInstall()}
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -285,6 +315,7 @@ export default function TopBar({ theme = "dark", onChangeTheme }) {
|
||||||
>
|
>
|
||||||
<i className="fa-solid fa-download"></i> Install
|
<i className="fa-solid fa-download"></i> Install
|
||||||
</button>
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ export function calculatePostPriority(post, viewCenter, allPosts) {
|
||||||
let score = 0;
|
let score = 0;
|
||||||
|
|
||||||
// 1. Score intrinsèque du post (si disponible depuis reco-service)
|
// 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%
|
score += recoScore * 0.4; // Weight: 40%
|
||||||
|
|
||||||
// 2. Catégorie (certaines plus importantes)
|
// 2. Catégorie (certaines plus importantes)
|
||||||
|
|
|
||||||
|
|
@ -87,9 +87,15 @@ export function usePostsEngine({
|
||||||
const viewCenter = center ? { lng: center.lng, lat: center.lat } : null;
|
const viewCenter = center ? { lng: center.lng, lat: center.lat } : null;
|
||||||
|
|
||||||
const { fullCardPosts, simpleMarkerPosts } = prioritizePosts(visible, viewCenter, {
|
const { fullCardPosts, simpleMarkerPosts } = prioritizePosts(visible, viewCenter, {
|
||||||
maxFullCards: 15, // Max 15 cartes complètes
|
maxFullCards: 12, // Max 12 cartes complètes (réduit de 15)
|
||||||
clusterRadius: 0.0015, // ~150m clustering radius
|
clusterRadius: 0.002, // ~200m clustering radius (augmenté pour plus de groupement)
|
||||||
minScoreForCard: 35, // Score minimum pour carte complète
|
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
|
// Combine both types for "wanted" tracking
|
||||||
|
|
|
||||||
|
|
@ -98,12 +98,15 @@ export default function SmartSearchBar({
|
||||||
|
|
||||||
const t = setTimeout(async () => {
|
const t = setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
|
console.log('[SmartSearchBar] Searching for:', query);
|
||||||
const res = await recoSearch(query, { signal: ctrl.signal, limit: 12 });
|
const res = await recoSearch(query, { signal: ctrl.signal, limit: 12 });
|
||||||
|
console.log('[SmartSearchBar] Got results:', res?.length || 0, res);
|
||||||
setItems(res);
|
setItems(res);
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
setActive(res.length ? 0 : -1);
|
setActive(res.length ? 0 : -1);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (ctrl.signal.aborted) return;
|
if (ctrl.signal.aborted) return;
|
||||||
|
console.error('[SmartSearchBar] Search error:', e);
|
||||||
setErr((e && e.message) ? e.message : "Search error");
|
setErr((e && e.message) ? e.message : "Search error");
|
||||||
setItems([]);
|
setItems([]);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
||||||
|
|
@ -76,10 +76,18 @@ async function tryFetchAnyPath(q, { signal } = {}) {
|
||||||
for (const p of paths) {
|
for (const p of paths) {
|
||||||
try {
|
try {
|
||||||
const url = `${p}?q=${encodeURIComponent(query)}`;
|
const url = `${p}?q=${encodeURIComponent(query)}`;
|
||||||
|
console.log('[recoSearch] Trying:', url);
|
||||||
const res = await fetch(url, { credentials: "include", signal });
|
const res = await fetch(url, { credentials: "include", signal });
|
||||||
|
|
||||||
if (res.status === 404) continue;
|
console.log('[recoSearch] Response status:', res.status);
|
||||||
if (!res.ok) return []; // évite spam erreurs
|
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 data = await res.json().catch(() => null);
|
||||||
|
|
||||||
const arr = Array.isArray(data)
|
const arr = Array.isArray(data)
|
||||||
|
|
@ -90,11 +98,16 @@ async function tryFetchAnyPath(q, { signal } = {}) {
|
||||||
? data.results
|
? data.results
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
|
console.log('[recoSearch] Success! Returning', arr.length, 'results from', p);
|
||||||
return arr.map(normalizeOne).filter(Boolean);
|
return arr.map(normalizeOne).filter(Boolean);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Abort => stop net
|
// 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
|
// autres erreurs => on teste prochain path
|
||||||
|
console.log('[recoSearch] Error on', p, ':', e.message, '- trying next');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue