Evita di calcolare le statistiche di tutta la rosa dove serve solo l'anagrafica
LinkProfilo (header di ogni pagina) e il Calendario (compleanni del mese) usavano useIo/useRosa, che calcola MVP, pagelle, cacche, palloni e infortuni per l'intera squadra tramite 6 hook e un useMemo pesante - anche se a entrambi serviva solo id/nome/nascita/iniziali. Il Calendario in particolare pagava questo costo ad ogni apertura solo per i compleanni del mese. - crapp-data.ts: esporta inizialiDa, già usata internamente per il fallback avatar. - ui-bits.tsx: LinkProfilo usa useGiocatoreBase (sola anagrafica) invece di useIo. - rosa.ts: nuovo hook useAnagraficaRosa (id, nome, nascita) senza gli altri 5 hook statistici ne' il useMemo su tutta la rosa. - eventi.ts: compleanniEventi accetta solo i tre campi che usa, non l'intero Giocatore. - calendario.tsx: usa useAnagraficaRosa al posto di useRosa. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,8 +2,9 @@ import { useId, useState, type ComponentPropsWithoutRef, type ReactNode } from "
|
|||||||
import { Link } from "@tanstack/react-router";
|
import { Link } from "@tanstack/react-router";
|
||||||
import { ChevronDown } from "lucide-react";
|
import { ChevronDown } from "lucide-react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { statoMeta, type Stato } from "@/lib/crapp-data";
|
import { inizialiDa, statoMeta, type Stato } from "@/lib/crapp-data";
|
||||||
import { useIo } from "@/lib/rosa";
|
import { nomeCompleto } from "@/lib/giocatori-squadra";
|
||||||
|
import { useGiocatoreBase } from "@/lib/user-store";
|
||||||
import { Avatar } from "@/components/crapp/Avatar";
|
import { Avatar } from "@/components/crapp/Avatar";
|
||||||
import { Reveal } from "@/components/motion/Reveal";
|
import { Reveal } from "@/components/motion/Reveal";
|
||||||
import { Numero } from "@/components/motion/Numero";
|
import { Numero } from "@/components/motion/Numero";
|
||||||
@@ -51,9 +52,14 @@ export function Card({
|
|||||||
* Accesso al profilo in alto a destra: la BottomNav ha quattro voci e questa è
|
* Accesso al profilo in alto a destra: la BottomNav ha quattro voci e questa è
|
||||||
* l'unica porta verso `/profilo`. Sulla pagina del profilo si passa `azione` a
|
* l'unica porta verso `/profilo`. Sulla pagina del profilo si passa `azione` a
|
||||||
* `PageHeader` per rimetterci il logo — sarebbe un link a sé stessa.
|
* `PageHeader` per rimetterci il logo — sarebbe un link a sé stessa.
|
||||||
|
*
|
||||||
|
* Usa `useGiocatoreBase` (sola anagrafica) e non `useIo`: qui serve solo id e
|
||||||
|
* iniziali, mentre `useIo` calcola l'intera rosa con statistiche (MVP, pagelle,
|
||||||
|
* cacche, palloni, infortuni). Essendo in un componente montato su quasi ogni
|
||||||
|
* pagina, quei moduli finirebbero nel bundle condiviso di tutte le rotte.
|
||||||
*/
|
*/
|
||||||
export function LinkProfilo() {
|
export function LinkProfilo() {
|
||||||
const g = useIo();
|
const g = useGiocatoreBase();
|
||||||
if (!g) return <TeamLogo className="h-12 w-12" />;
|
if (!g) return <TeamLogo className="h-12 w-12" />;
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
@@ -61,7 +67,7 @@ export function LinkProfilo() {
|
|||||||
aria-label="Il tuo profilo"
|
aria-label="Il tuo profilo"
|
||||||
className="premi shrink-0 rounded-2xl ring-2 ring-primary-foreground/30"
|
className="premi shrink-0 rounded-2xl ring-2 ring-primary-foreground/30"
|
||||||
>
|
>
|
||||||
<Avatar id={g.id} fallback={g.iniziali} className="h-12 w-12 text-lg" />
|
<Avatar id={g.id} fallback={inizialiDa(nomeCompleto(g))} className="h-12 w-12 text-lg" />
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,8 @@ const rosaCSI: Rosa[] = [
|
|||||||
{ nome: "Giada Valbonesi", nascita: "1994-05-20", ruolo: "Opposto", numero: 10 },
|
{ nome: "Giada Valbonesi", nascita: "1994-05-20", ruolo: "Opposto", numero: 10 },
|
||||||
];
|
];
|
||||||
|
|
||||||
function inizialiDa(nome: string) {
|
/** Iniziali da un nome completo (max 2 lettere), es. per il fallback di `Avatar`. */
|
||||||
|
export function inizialiDa(nome: string) {
|
||||||
return nome
|
return nome
|
||||||
.split(" ")
|
.split(" ")
|
||||||
.map((p) => p[0] ?? "")
|
.map((p) => p[0] ?? "")
|
||||||
|
|||||||
+12
-2
@@ -162,8 +162,18 @@ export function useEliminaEvento() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Compleanni della rosa, come eventi di calendario dell'anno indicato. */
|
/**
|
||||||
export function compleanniEventi(rosa: Giocatore[], anno = new Date().getFullYear()): Evento[] {
|
* Compleanni della rosa, come eventi di calendario dell'anno indicato.
|
||||||
|
*
|
||||||
|
* Prende solo i tre campi che usa (non l'intero `Giocatore`): il Calendario li
|
||||||
|
* legge da un'anagrafica leggera per non tirarsi dietro tutte le statistiche
|
||||||
|
* (MVP, pagelle, cacche, palloni, infortuni) di `useRosa` solo per le date di
|
||||||
|
* nascita.
|
||||||
|
*/
|
||||||
|
export function compleanniEventi(
|
||||||
|
rosa: Array<Pick<Giocatore, "id" | "nome" | "nascita">>,
|
||||||
|
anno = new Date().getFullYear(),
|
||||||
|
): Evento[] {
|
||||||
return rosa
|
return rosa
|
||||||
.filter((g) => g.nascita)
|
.filter((g) => g.nascita)
|
||||||
.map((g) => {
|
.map((g) => {
|
||||||
|
|||||||
@@ -24,6 +24,23 @@ function iniziali(nome: string, cognome: string): string {
|
|||||||
return `${nome[0] ?? ""}${cognome[0] ?? ""}`.toUpperCase();
|
return `${nome[0] ?? ""}${cognome[0] ?? ""}`.toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Solo anagrafica (id, nome, data di nascita) dei giocatori attivi — es. per i
|
||||||
|
* compleanni nel Calendario. A differenza di `useRosa` non legge MVP, pagelle,
|
||||||
|
* cacche, palloni né infortuni: evita di montare quei cinque hook e il relativo
|
||||||
|
* `useMemo` solo per una data di nascita.
|
||||||
|
*/
|
||||||
|
export function useAnagraficaRosa(): Array<Pick<Giocatore, "id" | "nome" | "nascita">> {
|
||||||
|
const { righe: squadra } = useGiocatoriSquadra();
|
||||||
|
return useMemo(
|
||||||
|
() =>
|
||||||
|
squadra
|
||||||
|
.filter((g) => g.attivo)
|
||||||
|
.map((g) => ({ id: g.id, nome: nomeCompleto(g), nascita: nascitaPerId[g.id] ?? "" })),
|
||||||
|
[squadra],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rosa completa con tutte le statistiche personali (presenze, MVP, media voto,
|
* Rosa completa con tutte le statistiche personali (presenze, MVP, media voto,
|
||||||
* palloni, infortuni, ritardi, cacche). Legge l'anagrafica da `giocatori_squadra`
|
* palloni, infortuni, ritardi, cacche). Legge l'anagrafica da `giocatori_squadra`
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { useMotoRidotto } from "@/lib/motion";
|
|||||||
import { EventoCard, linkPerEvento } from "@/components/crapp/EventoCard";
|
import { EventoCard, linkPerEvento } from "@/components/crapp/EventoCard";
|
||||||
import { Card, PageHeader, Section } from "@/components/crapp/ui-bits";
|
import { Card, PageHeader, Section } from "@/components/crapp/ui-bits";
|
||||||
import { compleanniEventi, useEventi, type Evento } from "@/lib/eventi";
|
import { compleanniEventi, useEventi, type Evento } from "@/lib/eventi";
|
||||||
import { useRosa } from "@/lib/rosa";
|
import { useAnagraficaRosa } from "@/lib/rosa";
|
||||||
import { useIsAdmin } from "@/lib/ruoli";
|
import { useIsAdmin } from "@/lib/ruoli";
|
||||||
import {
|
import {
|
||||||
Drawer,
|
Drawer,
|
||||||
@@ -115,7 +115,7 @@ function Calendario() {
|
|||||||
const [drawerAperto, setDrawerAperto] = useState(false);
|
const [drawerAperto, setDrawerAperto] = useState(false);
|
||||||
const admin = useIsAdmin();
|
const admin = useIsAdmin();
|
||||||
const { eventi } = useEventi();
|
const { eventi } = useEventi();
|
||||||
const rosa = useRosa();
|
const rosa = useAnagraficaRosa();
|
||||||
// `useMotoRidotto` copre anche i device deboli (RAM bassa), non solo
|
// `useMotoRidotto` copre anche i device deboli (RAM bassa), non solo
|
||||||
// `prefers-reduced-motion`: disattiva anche lo swipe orizzontale tra mesi.
|
// `prefers-reduced-motion`: disattiva anche lo swipe orizzontale tra mesi.
|
||||||
const ridotto = useMotoRidotto();
|
const ridotto = useMotoRidotto();
|
||||||
|
|||||||
@@ -105,13 +105,49 @@ non c'è contenuto "nascosto" che viene ricalcolato inutilmente — non modifica
|
|||||||
precedente (c089d9e) e da allora orfana. Rimossa insieme ai suoi fallback; il commento che la
|
precedente (c089d9e) e da allora orfana. Rimossa insieme ai suoi fallback; il commento che la
|
||||||
citava come confronto per `.vetro` è stato aggiornato.
|
citava come confronto per `.vetro` è stato aggiornato.
|
||||||
|
|
||||||
|
## 9. [x] `LinkProfilo` (header di ogni pagina) tirava dentro tutte le statistiche della rosa
|
||||||
|
— APPLICATA
|
||||||
|
|
||||||
|
`ui-bits.tsx` (`LinkProfilo`, usato in `PageHeader` su quasi ogni rotta) usava `useIo`, che
|
||||||
|
internamente chiama `useRosa()`: quest'ultimo calcola l'**intera rosa arricchita** (voti MVP,
|
||||||
|
medie pagelle, statistiche cacche, turni palloni, infortuni/ritardi — 6 hook + un `useMemo`
|
||||||
|
pesante su tutta la squadra) solo per leggere `id` e iniziali di un giocatore.
|
||||||
|
|
||||||
|
Fix: `LinkProfilo` ora usa `useGiocatoreBase` (`lib/user-store.ts`, sola anagrafica da
|
||||||
|
`giocatori_squadra`) e calcola le iniziali con `inizialiDa` (ora esportata da `crapp-data.ts`),
|
||||||
|
senza toccare gli altri moduli statistici.
|
||||||
|
|
||||||
|
Verificato con build reale: le funzioni pesanti (`mediePagelle`, `statisticheCacche`,
|
||||||
|
`conteggioTurni`, `InfortuniERitardi`) sono sparite dal chunk `ui-bits`. **Attenzione**: la
|
||||||
|
dimensione totale del chunk non è cambiata (452KB) perché il grosso del peso è l'SDK Supabase
|
||||||
|
stesso, già necessario a monte in `__root.tsx` per il gate di login — non rimovibile da qui.
|
||||||
|
Il guadagno reale di questo fix è sul lavoro a runtime evitato (6 hook + memo in meno ad ogni
|
||||||
|
render dell'header), non sulla dimensione del bundle.
|
||||||
|
|
||||||
|
## 10. [x] Aprire Calendario montava la stessa rosa "pesante" solo per i compleanni — APPLICATA
|
||||||
|
|
||||||
|
`calendario.tsx` chiamava `useRosa()` (le stesse 6 statistiche del punto 9: MVP, pagelle,
|
||||||
|
cacche, palloni, infortuni/ritardi) ma usava il risultato **solo** per `compleanniEventi`, che
|
||||||
|
legge esclusivamente `id`, `nome` e `nascita`. Cliccare Calendario forzava quindi il calcolo (e,
|
||||||
|
la prima volta nella sessione, il fetch) di dati completamente estranei al calendario — probabile
|
||||||
|
causa diretta del lag "particolarmente su Calendario" segnalato.
|
||||||
|
|
||||||
|
Fix: nuovo hook `useAnagraficaRosa` in `rosa.ts` — solo `useGiocatoriSquadra` + lookup statico
|
||||||
|
`nascitaPerId`, senza gli altri 5 hook né il `useMemo` pesante. `compleanniEventi` (`eventi.ts`)
|
||||||
|
accetta ora `Pick<Giocatore, "id" | "nome" | "nascita">[]` invece dell'intero `Giocatore[]`,
|
||||||
|
riflettendo che è tutto ciò che usa.
|
||||||
|
|
||||||
## Note
|
## Note
|
||||||
|
|
||||||
- `useMotoRidotto` (`lib/motion.ts`) è ora l'heuristic condiviso di "device debole" usato in
|
- `useMotoRidotto` (`lib/motion.ts`) è ora l'heuristic condiviso di "device debole" usato in
|
||||||
`BottomNav.tsx`, `BarraSottosezioni.tsx` e `calendario.tsx` (punti 2, 3, 6). Se si riprende il
|
`BottomNav.tsx`, `BarraSottosezioni.tsx` e `calendario.tsx` (punti 2, 3, 6). Se si riprende il
|
||||||
punto 4 (coriandoli), conviene usare lo stesso hook invece di un check separato.
|
punto 4 (coriandoli), conviene usare lo stesso hook invece di un check separato.
|
||||||
- Applicati: 1, 2, 3, 6, 7. Restano da discutere/prioritizzare: 4 (coriandoli su device medi),
|
- Applicati: 1, 2, 3, 6, 7, 8, 9, 10. Restano da discutere/prioritizzare: 4 (coriandoli su
|
||||||
5 (virtualizzazione liste lunghe).
|
device medi), 5 (virtualizzazione liste lunghe).
|
||||||
|
- Pattern ricorrente (punti 9, 10): `useRosa()` è comodo ma calcola *tutte* le statistiche della
|
||||||
|
squadra; usarlo solo per identità/anagrafica (id, nome, nascita, iniziali) costa 5-6 hook e un
|
||||||
|
`useMemo` su tutta la rosa inutilmente. Se in futuro emerge un altro caso simile, riusare
|
||||||
|
`useAnagraficaRosa` (o `useGiocatoreBase` per il singolo giocatore) invece di `useRosa`/`useIo`.
|
||||||
- Se il lag persistesse ancora dopo questi fix, il prossimo passo è profilare un device Android
|
- Se il lag persistesse ancora dopo questi fix, il prossimo passo è profilare un device Android
|
||||||
reale (Chrome DevTools remoto o `chrome://inspect`) invece di continuare a ipotizzare: a
|
reale (Chrome DevTools remoto o `chrome://inspect`) invece di continuare a ipotizzare: a
|
||||||
questo punto le cause "ovvie" lette dal codice sono coperte, e senza un trace reale si rischia
|
questo punto le cause "ovvie" lette dal codice sono coperte, e senza un trace reale si rischia
|
||||||
|
|||||||
Reference in New Issue
Block a user