Optimise Following page: 4 aggregated queries, no correlated subqueries

- Rewrite list_channels to run exactly 4 SQL queries regardless of channel
  count: channel rows, aggregated video stats (GROUP BY), new-video counts,
  and latest video (derived-table JOIN replaces per-row correlated subquery)
- Remove dead _CHANNEL_STATS_SELECT (orphaned after the rewrite)
- Fix upload_frequency_days: use pre-computed date_span_days from vstats
  instead of a broken per-channel db.execute() call
- Restrict new_counts query to id_csv so it uses idx_videos_channel_indexed
- markChannelsSeen: optimistic setQueryData instead of invalidateQueries,
  eliminating a full channel-list re-fetch on every Following page visit
- DownloadIndicator idle poll: 10s → 30s (no need to hit DB when idle)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Mattias Tall
2026-05-26 16:18:33 +02:00
parent 1405acfaed
commit c00d5c7595
3 changed files with 120 additions and 37 deletions

View File

@@ -80,7 +80,7 @@ function DownloadIndicator() {
const active = (query.state.data ?? []).some(
(d) => d.status === "pending" || d.status === "downloading"
);
return active ? 1500 : 10_000;
return active ? 1500 : 30_000;
},
});

View File

@@ -612,7 +612,10 @@ export default function Following() {
useEffect(() => {
if (channels.length > 0) {
markChannelsSeen().then(() => {
qc.invalidateQueries({ queryKey: ["channels"] });
// Zero out new_count optimistically — avoids a full re-fetch just to clear badges
qc.setQueryData(["channels"], (old) =>
old ? old.map((c) => ({ ...c, new_count: 0 })) : old
);
});
}
}, []); // eslint-disable-line react-hooks/exhaustive-deps