From 9e2f83a85e1ce1e6a14e5015e61f49adf6329633 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Mon, 7 Sep 2026 08:56:33 +0200 Subject: [PATCH] Corregge il rimbalzo di /eventi in home e la lista invisibile con molti eventi. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Il redirect verso /benvenuto scattava se la rosa non era ancora arrivata dalla rete, anche con sessione e profilo validi: caricare /eventi a freddo (refresh, link diretto, riapertura della PWA) ti riportava sempre in Home. Ora si aspetta il caricamento della rosa e si torna alla pagina di destinazione originale. In Reveal, la soglia del 15% dell'altezza dell'elemento per attivare l'animazione era irraggiungibile per una lista lunga (75 eventi = oltre 5700px): la sezione restava invisibile per sempre, a qualunque scroll. La soglia ora è "some" (basta un pixel visibile). Aggiunta anche la gestione dell'errore sul caricamento eventi, che prima falliva in silenzio mostrando una pagina vuota senza alcun messaggio. Co-Authored-By: Claude Sonnet 5 --- src/components/motion/Reveal.tsx | 2 +- src/routes/__root.tsx | 15 ++++++++++----- src/routes/benvenuto.tsx | 10 ++++++++-- src/routes/eventi.tsx | 16 +++++++++++++++- 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/components/motion/Reveal.tsx b/src/components/motion/Reveal.tsx index ca40e83..8845161 100644 --- a/src/components/motion/Reveal.tsx +++ b/src/components/motion/Reveal.tsx @@ -37,7 +37,7 @@ export function Reveal({ {...(style ? { style } : {})} initial={ridotto ? { opacity: 0 } : { opacity: 0, y: 10 }} whileInView={ridotto ? { opacity: 1 } : { opacity: 1, y: 0 }} - viewport={{ once: true, amount: 0.15, margin: "0px 0px -10% 0px" }} + viewport={{ once: true, amount: "some", margin: "0px 0px -10% 0px" }} transition={ridotto ? { duration: 0.2, delay: ritardo } : { ...molla.ui, delay: ritardo }} > {children} diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index 6c37d79..733da9e 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -18,6 +18,7 @@ import { CelebrazioneBadge } from "../components/crapp/CelebrazioneBadge"; import { Toaster } from "../components/ui/sonner"; import { TeamLogo } from "../components/crapp/ui-bits"; import { useGiocatoreBase } from "../lib/user-store"; +import { useGiocatoriSquadra } from "../lib/giocatori-squadra"; import { useSessione } from "../lib/auth"; import { mantieniWorkerPushAggiornato } from "../lib/push-client"; @@ -172,6 +173,7 @@ function AppShell() { const navigate = useNavigate(); const location = useLocation(); const giocatore = useGiocatoreBase(); + const { isPending: squadraInCorso } = useGiocatoriSquadra(); const { pronta, utenteId } = useSessione(); const [mounted, setMounted] = useState(false); const isBenvenuto = location.pathname === "/benvenuto"; @@ -180,15 +182,18 @@ function AppShell() { // Senza sessione Google non si entra: l'identità la dà il login, non la scelta del nome // (DD-011). Si aspetta `pronta`, altrimenti il primo render sloggato rimbalzerebbe fuori - // chi ha già la sessione in localStorage. + // chi ha già la sessione in localStorage. Si aspetta anche `squadraInCorso`: finché la + // rosa non è arrivata, `giocatore` risulta nullo anche per chi è già collegato (la rosa + // di riserva usa id diversi da quelli veri), e rimbalzerebbe su /benvenuto chi stava solo + // ricaricando una pagina profonda come /eventi. useEffect(() => { setMounted(true); - if (pronta && (!giocatore || !utenteId) && !isBenvenuto) { - navigate({ to: "/benvenuto" }); + if (pronta && !squadraInCorso && (!giocatore || !utenteId) && !isBenvenuto) { + navigate({ to: "/benvenuto", search: { next: location.pathname } }); } - }, [giocatore, utenteId, pronta, isBenvenuto, navigate]); + }, [giocatore, utenteId, pronta, squadraInCorso, isBenvenuto, navigate, location.pathname]); - if (!mounted || !pronta) { + if (!mounted || !pronta || squadraInCorso) { return (
diff --git a/src/routes/benvenuto.tsx b/src/routes/benvenuto.tsx index 24f3760..1b42ac2 100644 --- a/src/routes/benvenuto.tsx +++ b/src/routes/benvenuto.tsx @@ -13,6 +13,11 @@ import { import { impostaGiocatore, resetGiocatore, useGiocatoreCorrente } from "@/lib/user-store"; export const Route = createFileRoute("/benvenuto")({ + validateSearch: (search: Record): { next?: string } => { + const next = search["next"]; + // Solo un percorso interno: mai un URL assoluto, per non aprire un redirect esterno. + return typeof next === "string" && next.startsWith("/") ? { next } : {}; + }, head: () => ({ meta: [ { title: "Benvenuto — CrAPP" }, @@ -32,6 +37,7 @@ export const Route = createFileRoute("/benvenuto")({ function Benvenuto() { const navigate = useNavigate(); + const { next } = Route.useSearch(); const giocatore = useGiocatoreCorrente(); const { pronta, utenteId, emailUtente } = useSessione(); const { righe, daDatabase } = useGiocatoriSquadra(); @@ -45,8 +51,8 @@ function Benvenuto() { const puoEntrare = !!giocatore && !!utenteId; useEffect(() => { - if (puoEntrare) navigate({ to: "/" }); - }, [puoEntrare, navigate]); + if (puoEntrare) navigate({ to: next ?? "/" }); + }, [puoEntrare, navigate, next]); // Chi sei lo dice lo slot collegato all'account, non quello che c'è in localStorage: // senza slot la scelta salvata dalla vecchia selezione libera va buttata. diff --git a/src/routes/eventi.tsx b/src/routes/eventi.tsx index 1f6278f..13052a5 100644 --- a/src/routes/eventi.tsx +++ b/src/routes/eventi.tsx @@ -50,7 +50,7 @@ const tipi: Array<{ id: CategoriaEvento; label: string }> = [ function GestioneEventi() { const io = useGiocatoreCorrente(); const admin = useIsAdmin(); - const { eventi, isPending } = useEventi(); + const { eventi, isPending, isError, error, refetch } = useEventi(); const { righe: squadra } = useGiocatoriSquadra(); const salva = useSalvaEvento(); const elimina = useEliminaEvento(); @@ -275,6 +275,20 @@ function GestioneEventi() {

Carico gli eventi…

+ ) : isError ? ( +
+

+ Non sono riuscito a caricare gli eventi + {error instanceof Error ? `: ${error.message}` : ""}. +

+ +
) : (
{eventi.map((e) => (