Corregge il rimbalzo di /eventi in home e la lista invisibile con molti eventi.
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 <noreply@anthropic.com>
This commit is contained in:
1 parent
a1d2599c47
commit
9e2f83a85e
4 files changed
+34
-9
No files matched your search
@@ -37,7 +37,7 @@ export function Reveal({
|
|||||||
{...(style ? { style } : {})}
|
{...(style ? { style } : {})}
|
||||||
initial={ridotto ? { opacity: 0 } : { opacity: 0, y: 10 }}
|
initial={ridotto ? { opacity: 0 } : { opacity: 0, y: 10 }}
|
||||||
whileInView={ridotto ? { opacity: 1 } : { opacity: 1, y: 0 }}
|
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 }}
|
transition={ridotto ? { duration: 0.2, delay: ritardo } : { ...molla.ui, delay: ritardo }}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
+10
-5
@@ -18,6 +18,7 @@ import { CelebrazioneBadge } from "../components/crapp/CelebrazioneBadge";
|
|||||||
import { Toaster } from "../components/ui/sonner";
|
import { Toaster } from "../components/ui/sonner";
|
||||||
import { TeamLogo } from "../components/crapp/ui-bits";
|
import { TeamLogo } from "../components/crapp/ui-bits";
|
||||||
import { useGiocatoreBase } from "../lib/user-store";
|
import { useGiocatoreBase } from "../lib/user-store";
|
||||||
|
import { useGiocatoriSquadra } from "../lib/giocatori-squadra";
|
||||||
import { useSessione } from "../lib/auth";
|
import { useSessione } from "../lib/auth";
|
||||||
import { mantieniWorkerPushAggiornato } from "../lib/push-client";
|
import { mantieniWorkerPushAggiornato } from "../lib/push-client";
|
||||||
|
|
||||||
@@ -172,6 +173,7 @@ function AppShell() {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const giocatore = useGiocatoreBase();
|
const giocatore = useGiocatoreBase();
|
||||||
|
const { isPending: squadraInCorso } = useGiocatoriSquadra();
|
||||||
const { pronta, utenteId } = useSessione();
|
const { pronta, utenteId } = useSessione();
|
||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
const isBenvenuto = location.pathname === "/benvenuto";
|
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
|
// 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
|
// (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(() => {
|
useEffect(() => {
|
||||||
setMounted(true);
|
setMounted(true);
|
||||||
if (pronta && (!giocatore || !utenteId) && !isBenvenuto) {
|
if (pronta && !squadraInCorso && (!giocatore || !utenteId) && !isBenvenuto) {
|
||||||
navigate({ to: "/benvenuto" });
|
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 (
|
return (
|
||||||
<div className="grid min-h-dvh place-items-center bg-background">
|
<div className="grid min-h-dvh place-items-center bg-background">
|
||||||
<TeamLogo className="h-16 w-16 animate-pulse" />
|
<TeamLogo className="h-16 w-16 animate-pulse" />
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ import {
|
|||||||
import { impostaGiocatore, resetGiocatore, useGiocatoreCorrente } from "@/lib/user-store";
|
import { impostaGiocatore, resetGiocatore, useGiocatoreCorrente } from "@/lib/user-store";
|
||||||
|
|
||||||
export const Route = createFileRoute("/benvenuto")({
|
export const Route = createFileRoute("/benvenuto")({
|
||||||
|
validateSearch: (search: Record<string, unknown>): { 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: () => ({
|
head: () => ({
|
||||||
meta: [
|
meta: [
|
||||||
{ title: "Benvenuto — CrAPP" },
|
{ title: "Benvenuto — CrAPP" },
|
||||||
@@ -32,6 +37,7 @@ export const Route = createFileRoute("/benvenuto")({
|
|||||||
|
|
||||||
function Benvenuto() {
|
function Benvenuto() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { next } = Route.useSearch();
|
||||||
const giocatore = useGiocatoreCorrente();
|
const giocatore = useGiocatoreCorrente();
|
||||||
const { pronta, utenteId, emailUtente } = useSessione();
|
const { pronta, utenteId, emailUtente } = useSessione();
|
||||||
const { righe, daDatabase } = useGiocatoriSquadra();
|
const { righe, daDatabase } = useGiocatoriSquadra();
|
||||||
@@ -45,8 +51,8 @@ function Benvenuto() {
|
|||||||
const puoEntrare = !!giocatore && !!utenteId;
|
const puoEntrare = !!giocatore && !!utenteId;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (puoEntrare) navigate({ to: "/" });
|
if (puoEntrare) navigate({ to: next ?? "/" });
|
||||||
}, [puoEntrare, navigate]);
|
}, [puoEntrare, navigate, next]);
|
||||||
|
|
||||||
// Chi sei lo dice lo slot collegato all'account, non quello che c'è in localStorage:
|
// 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.
|
// senza slot la scelta salvata dalla vecchia selezione libera va buttata.
|
||||||
|
|||||||
+15
-1
@@ -50,7 +50,7 @@ const tipi: Array<{ id: CategoriaEvento; label: string }> = [
|
|||||||
function GestioneEventi() {
|
function GestioneEventi() {
|
||||||
const io = useGiocatoreCorrente();
|
const io = useGiocatoreCorrente();
|
||||||
const admin = useIsAdmin();
|
const admin = useIsAdmin();
|
||||||
const { eventi, isPending } = useEventi();
|
const { eventi, isPending, isError, error, refetch } = useEventi();
|
||||||
const { righe: squadra } = useGiocatoriSquadra();
|
const { righe: squadra } = useGiocatoriSquadra();
|
||||||
const salva = useSalvaEvento();
|
const salva = useSalvaEvento();
|
||||||
const elimina = useEliminaEvento();
|
const elimina = useEliminaEvento();
|
||||||
@@ -275,6 +275,20 @@ function GestioneEventi() {
|
|||||||
<p aria-busy="true" className="text-center text-xs text-muted-foreground">
|
<p aria-busy="true" className="text-center text-xs text-muted-foreground">
|
||||||
Carico gli eventi…
|
Carico gli eventi…
|
||||||
</p>
|
</p>
|
||||||
|
) : isError ? (
|
||||||
|
<div className="space-y-2 rounded-3xl bg-card p-4 text-center text-sm shadow-card">
|
||||||
|
<p className="text-destructive">
|
||||||
|
Non sono riuscito a caricare gli eventi
|
||||||
|
{error instanceof Error ? `: ${error.message}` : ""}.
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => refetch()}
|
||||||
|
className="rounded-2xl bg-secondary px-4 py-2 text-xs font-bold uppercase"
|
||||||
|
>
|
||||||
|
Riprova
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{eventi.map((e) => (
|
{eventi.map((e) => (
|
||||||
|
|||||||
Reference in new issue
Block a user