fix(mail): reactive page (refresh stats + status on scan/connect) + center donut+legend

Two small fixes blocking real "feierabend":

1. Stats-Counter veraltet nach Scan/Connect/Disconnect:
   - mail.tsx hatte zwei separate Data-Sources: useMailStatus (accounts +
     errors + heartbeat) und useMailStats (blockedByDay + blockedByConnection)
   - onScanSuccess + onIntervalChanged + OAuth-onSuccess + disconnect-handler
     refreshten nur useMailStatus → der Account-Collapsible-Counter (kommt
     aus useMailStats.blockedByConnection) blieb veraltet
   - Beobachtet: GMX-Scan-Button meldet "90 blockiert" als Feedback, aber
     Card-Header zeigt weiter 60
   - Fix: refreshAll() = refresh() + refreshStats() parallel. Alle reactive
     callsites (4 Stellen) auf refreshAll umgestellt
   - useMailStats hatte refresh schon exportiert (Z. 153), nur nicht
     verdrahtet

2. Donut + Legend horizontal zentriert:
   - vorher: alignItems center (vertikal), Legend flex:1 → linksbündig mit
     Legend bis Card-Rand gestreckt
   - jetzt: justifyContent center + Legend ohne flex:1 → Block in der Mitte
     mit Whitespace links/rechts

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
chahinebrini 2026-05-14 00:16:53 +02:00
parent 30ed4b90b4
commit 4580a197dd
2 changed files with 17 additions and 8 deletions

View File

@ -255,7 +255,16 @@ export default function MailScreen() {
useMailStatus(plan); useMailStatus(plan);
const { disconnect, disconnecting } = useMailDisconnect(); const { disconnect, disconnecting } = useMailDisconnect();
const hasAccounts = accounts.length > 0; const hasAccounts = accounts.length > 0;
const { blockedByDay, blockedByConnection } = useMailStats(hasAccounts); const { blockedByDay, blockedByConnection, refresh: refreshStats } = useMailStats(hasAccounts);
// Globaler Refresh-Handler für alle reactive-Trigger (Scan-Success,
// Interval-Change, Connect/Disconnect): refresht beide Datenquellen
// parallel — useMailStatus (accounts, connect-errors, heartbeat) UND
// useMailStats (blockedByDay, blockedByConnection für Charts + Counter).
const refreshAll = () => {
refresh();
refreshStats();
};
const [sheetVisible, setSheetVisible] = useState(false); const [sheetVisible, setSheetVisible] = useState(false);
const [successVisible, setSuccessVisible] = useState(false); const [successVisible, setSuccessVisible] = useState(false);
@ -290,11 +299,11 @@ export default function MailScreen() {
await disconnect(id); await disconnect(id);
setDisconnectingId(null); setDisconnectingId(null);
if (expandedAccount === id) setExpandedAccount(null); if (expandedAccount === id) setExpandedAccount(null);
refresh(); refreshAll();
} }
function handleConnectSuccess() { function handleConnectSuccess() {
refresh(); refreshAll();
if (pendingOAuthConnectionId) { if (pendingOAuthConnectionId) {
setOauthTitleSheetConnectionId(pendingOAuthConnectionId); setOauthTitleSheetConnectionId(pendingOAuthConnectionId);
setPendingOAuthConnectionId(null); setPendingOAuthConnectionId(null);
@ -509,11 +518,11 @@ export default function MailScreen() {
expanded={expandedAccount === account.id} expanded={expandedAccount === account.id}
onToggle={() => toggleAccount(account.id)} onToggle={() => toggleAccount(account.id)}
onDisconnect={handleDisconnect} onDisconnect={handleDisconnect}
onIntervalChanged={refresh} onIntervalChanged={refreshAll}
onEditSuccess={handleConnectSuccess} onEditSuccess={handleConnectSuccess}
disconnecting={disconnectingId === account.id && disconnecting} disconnecting={disconnectingId === account.id && disconnecting}
blockedLast30d={connStat?.count} blockedLast30d={connStat?.count}
onScanSuccess={refresh} onScanSuccess={refreshAll}
/> />
); );
})} })}
@ -533,7 +542,7 @@ export default function MailScreen() {
connectionId={oauthTitleSheetConnectionId} connectionId={oauthTitleSheetConnectionId}
currentTitle={null} currentTitle={null}
onClose={() => { setOauthTitleSheetConnectionId(null); setSuccessVisible(true); }} onClose={() => { setOauthTitleSheetConnectionId(null); setSuccessVisible(true); }}
onSuccess={() => { setOauthTitleSheetConnectionId(null); setSuccessVisible(true); refresh(); }} onSuccess={() => { setOauthTitleSheetConnectionId(null); setSuccessVisible(true); refreshAll(); }}
/> />
)} )}

View File

@ -98,14 +98,14 @@ export function MailDistributionChart({ data, hero, totalBlocked, isLegend }: Pr
paddingBottom: 12, paddingBottom: 12,
}} }}
> >
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 16 }}> <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 16 }}>
<HalfDonut <HalfDonut
segments={segments} segments={segments}
centerValue={centerValue} centerValue={centerValue}
centerLabel={centerLabel} centerLabel={centerLabel}
width={DONUT_WIDTH} width={DONUT_WIDTH}
/> />
<View style={{ flex: 1, gap: 5 }}> <View style={{ gap: 5 }}>
{slices.map((slice) => ( {slices.map((slice) => (
<LegendRow key={slice.label} slice={slice} colors={colors} /> <LegendRow key={slice.label} slice={slice} colors={colors} />
))} ))}