diff --git a/public/pwaInstall.js b/public/pwaInstall.js
index d427d8c..9acba9b 100644
--- a/public/pwaInstall.js
+++ b/public/pwaInstall.js
@@ -1,28 +1,25 @@
// src/pwaInstall.js
let deferredPrompt = null;
-// Écoute l'événement natif PWA (se déclenche quand Chrome pense que l'app est installable)
+// Stocke l'événement natif quand Chrome le déclenche
window.addEventListener('beforeinstallprompt', (e) => {
- // Empêche le prompt auto (on veut le contrôler avec le bouton)
+ // Empêche le prompt auto (on veut le contrôler)
e.preventDefault();
deferredPrompt = e;
- console.log('PWA install prompt ready - waiting for user click');
+ console.log('PWA: Install prompt ready - ready to trigger on button click');
});
-// Fonction appelée quand on clique sur le bouton "Install"
+// Fonction appelée par le bouton "Install"
export function promptInstall() {
if (deferredPrompt) {
- deferredPrompt.prompt(); // Affiche le prompt natif "Add to home screen"
+ console.log('PWA: Triggering native install prompt');
+ deferredPrompt.prompt();
deferredPrompt.userChoice.then((choiceResult) => {
- if (choiceResult.outcome === 'accepted') {
- console.log('User accepted PWA install');
- } else {
- console.log('User dismissed PWA install');
- }
+ console.log('PWA Install outcome:', choiceResult.outcome);
deferredPrompt = null;
});
} else {
- console.log('No install prompt available yet - try again later');
- alert('PWA installation not ready yet. Try again in a few seconds.');
+ console.log('PWA: No prompt ready yet - wait for beforeinstallprompt event');
+ alert('PWA not ready yet. Try scrolling or interacting more with the site.');
}
}
\ No newline at end of file
diff --git a/src/auth/AuthContext.jsx.swbak.20251219-170456 b/src/auth/AuthContext.jsx.swbak.20251219-170456
deleted file mode 100644
index 179037f..0000000
--- a/src/auth/AuthContext.jsx.swbak.20251219-170456
+++ /dev/null
@@ -1,268 +0,0 @@
-import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from "react";
-import { clearAuth, guessUsernameFromToken, jwtExpSeconds, loadAuth, saveAuth } from "./authStorage";
-
-const AuthCtx = createContext(null);
-
-function apiBase() {
- return (import.meta?.env?.VITE_API_BASE_URL || "").replace(/\/+$/, "");
-}
-
-async function apiFetch(path, opts = {}) {
- const url = apiBase() + path;
- const res = await fetch(url, {
- credentials: "include",
- ...opts,
- headers: {
- "Content-Type": "application/json",
- ...(opts.headers || {}),
- },
- });
- return res;
-}
-
-// tolerant whoami: accepts {authenticated:true} OR {username:""} OR {user:""} OR just ok+token
-async function whoami(accessToken) {
- const res = await apiFetch("/api/whoami", {
- method: "GET",
- headers: accessToken ? { Authorization: `Bearer ${accessToken}` } : {},
- });
-
- const text = await res.text().catch(() => "");
- let json = null;
- try {
- json = text ? JSON.parse(text) : null;
- } catch {
- json = null;
- }
-
- const uname =
- (json && (json.username || json.user || json.preferred_username)) ||
- guessUsernameFromToken(accessToken) ||
- "";
-
- const authed =
- !!res.ok &&
- (json?.authenticated === true || json?.ok === true || !!json?.username || !!json?.user || !!uname);
-
- return { ok: !!res.ok, status: res.status, json, text, authenticated: authed, username: uname };
-}
-
-async function refreshTokens(refreshToken) {
- const res = await apiFetch("/api/refresh", {
- method: "POST",
- body: JSON.stringify({ refresh_token: refreshToken }),
- });
-
- const text = await res.text().catch(() => "");
- let json = null;
- try {
- json = text ? JSON.parse(text) : null;
- } catch {
- json = null;
- }
-
- return { ok: res.ok, status: res.status, json, text };
-}
-
-function nowSec() {
- return Math.floor(Date.now() / 1000);
-}
-
-export function AuthProvider({ children }) {
- const [status, setStatus] = useState("loading"); // loading | anon | auth
- const [user, setUser] = useState(null); // { username }
- const [tokens, setTokens] = useState(null); // { access_token, refresh_token, ... }
- const [error, setError] = useState("");
- const refreshTimer = useRef(null);
-
- function setAnon(msg = "") {
- setStatus("anon");
- setUser(null);
- setTokens(null);
- setError(msg || "");
- clearAuth();
- }
-
- function setAuthFromTokens(t, usernameGuess = "") {
- setTokens(t);
- saveAuth({ ...t, obtained_at: Date.now() });
- const u = usernameGuess || guessUsernameFromToken(t?.access_token) || "";
- setUser(u ? { username: u } : null);
- setStatus("auth");
- setError("");
- }
-
- async function ensureFreshSession(existing) {
- if (!existing?.access_token) return { ok: false, reason: "no_access_token" };
-
- const exp = jwtExpSeconds(existing.access_token);
- const skew = 60;
- const isExpiredOrSoon = !exp || exp <= nowSec() + skew;
-
- if (isExpiredOrSoon) {
- if (!existing.refresh_token) return { ok: false, reason: "expired_no_refresh" };
-
- const rr = await refreshTokens(existing.refresh_token);
- if (!rr.ok || !rr.json?.access_token) {
- return { ok: false, reason: `refresh_failed_${rr.status}`, detail: rr.text };
- }
-
- const merged = { ...existing, ...rr.json };
- if (!merged.refresh_token) merged.refresh_token = existing.refresh_token;
-
- setAuthFromTokens(merged);
- existing = merged;
- } else {
- setAuthFromTokens(existing);
- }
-
- const w = await whoami(existing.access_token);
- if (w.ok && w.authenticated) {
- setUser(w.username ? { username: w.username } : null);
- setStatus("auth");
- setError("");
- return { ok: true };
- }
-
- return { ok: false, reason: `whoami_${w.status}`, detail: w.text || "" };
- }
-
- function scheduleAutoRefresh() {
- if (refreshTimer.current) {
- clearInterval(refreshTimer.current);
- refreshTimer.current = null;
- }
-
- refreshTimer.current = setInterval(async () => {
- const cur = loadAuth();
- if (!cur?.access_token) return;
-
- const exp = jwtExpSeconds(cur.access_token);
- if (!exp) return;
-
- if (exp <= nowSec() + 90) {
- if (!cur.refresh_token) {
- setAnon("Session expirée (pas de refresh_token).");
- return;
- }
- const rr = await refreshTokens(cur.refresh_token);
- if (!rr.ok || !rr.json?.access_token) {
- setAnon("Session expirée. Please login again.");
- return;
- }
- const merged = { ...cur, ...rr.json };
- if (!merged.refresh_token) merged.refresh_token = cur.refresh_token;
- setAuthFromTokens(merged);
- }
- }, 30000);
- }
-
- async function restore() {
- setStatus("loading");
- setError("");
- const saved = loadAuth();
- if (!saved?.access_token) {
- setAnon("");
- return;
- }
-
- const r = await ensureFreshSession(saved);
- if (!r.ok) {
- setAnon("Login desynced. Please login again.");
- return;
- }
-
- scheduleAutoRefresh();
- }
-
- async function login(username, password) {
- setError("");
- setStatus("loading");
-
- const res = await apiFetch("/api/login", {
- method: "POST",
- body: JSON.stringify({ username, password }),
- });
-
- const text = await res.text().catch(() => "");
- let j = null;
- try {
- j = text ? JSON.parse(text) : null;
- } catch {
- j = null;
- }
-
- if (!res.ok || !j?.access_token) {
- setAnon(j?.error_description || j?.error || text || "Login failed");
- return { ok: false, status: res.status, json: j, text };
- }
-
- const t = { ...j, obtained_at: Date.now() };
- setAuthFromTokens(t);
- scheduleAutoRefresh();
-
- const w = await whoami(t.access_token);
- if (!w.ok || !w.authenticated) {
- setAnon("Login desynced. Please login again.");
- return { ok: false, status: w.status, json: w.json, text: w.text };
- }
-
- setUser(w.username ? { username: w.username } : null);
- setStatus("auth");
- return { ok: true };
- }
-
- function logout() {
- if (refreshTimer.current) clearInterval(refreshTimer.current);
- refreshTimer.current = null;
- setAnon("");
- }
-
- useEffect(() => {
- restore();
- const onStorage = (e) => {
- if (e.key === "sociowire:auth") restore();
- };
- window.addEventListener("storage", onStorage);
- return () => window.removeEventListener("storage", onStorage);
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
-
- // Backward-compatible fields expected by TopBar/MapView
- const value = useMemo(() => {
- const loading = status === "loading";
- const authenticated = status === "auth";
- const username = user?.username || "";
- const token = tokens?.access_token || "";
-
- return {
- // internal
- status,
- user,
- tokens,
- error,
-
- // expected by UI
- loading,
- authenticated,
- username,
- token,
-
- lastError: error || "",
- lastInfo: "",
- needsEmailVerification: false,
- resendVerificationEmail: async () => {},
-
- login,
- logout,
- restore,
- setError,
- };
- }, [status, user, tokens, error]);
-
- return