mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-08-29 03:55:38 -04:00
Optimize performance: batch DB ops, N+1 fixes, Suspense streaming, and Jotai best practices
- Add composite indexes on userEpisodeWatches and userMovieWatches for hot queries - Batch episode tracking: wrap season/batch watches in single transaction (~8 queries vs 8*N) - Fix N+1 patterns in credits, recommendations, and filmography with batch prefetch+insert - Stream TV season hydration via Suspense instead of blocking page render - Optimize webhook logs (per-connection LIMIT 10) and system health queries - Merge genre filter waterfalls into single Promise.all fetch - Migrate deprecated Jotai loadable() to unwrap() - Replace isolated createStore()+Provider with useHydrateAtoms on root store - Remove unnecessary atomWithStorage SSR guards (handled by Jotai internally) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+22
-25
@@ -1,5 +1,5 @@
|
||||
import { atom } from "jotai";
|
||||
import { loadable } from "jotai/utils";
|
||||
import { unwrap } from "jotai/utils";
|
||||
import {
|
||||
fetchEpisodeProgress,
|
||||
fetchUserStatuses,
|
||||
@@ -35,30 +35,27 @@ const genreResultsAsyncAtom = atom(async (get) => {
|
||||
return (data.results ?? []) as TitleRowItem[];
|
||||
});
|
||||
|
||||
export const genreResultsLoadable = loadable(genreResultsAsyncAtom);
|
||||
export const genreResultsAtom = unwrap(genreResultsAsyncAtom);
|
||||
|
||||
const genreUserStatusesAsyncAtom = atom(async (get) => {
|
||||
const genre = get(selectedGenreAtom);
|
||||
if (genre === null) return null;
|
||||
const genreResults = await get(genreResultsAsyncAtom);
|
||||
if (!genreResults || genreResults.length === 0) return {};
|
||||
return fetchUserStatuses(
|
||||
genreResults.map((r) => ({ tmdbId: r.tmdbId, type: r.type })),
|
||||
);
|
||||
});
|
||||
interface GenreEnrichments {
|
||||
statuses: Record<string, TitleStatus>;
|
||||
progress: Record<string, { watched: number; total: number }>;
|
||||
}
|
||||
|
||||
export const genreUserStatusesLoadable = loadable(genreUserStatusesAsyncAtom);
|
||||
|
||||
const genreEpisodeProgressAsyncAtom = atom(async (get) => {
|
||||
const genre = get(selectedGenreAtom);
|
||||
if (genre === null) return null;
|
||||
const genreResults = await get(genreResultsAsyncAtom);
|
||||
if (!genreResults || genreResults.length === 0) return {};
|
||||
return fetchEpisodeProgress(
|
||||
genreResults.map((r) => ({ tmdbId: r.tmdbId, type: r.type })),
|
||||
);
|
||||
});
|
||||
|
||||
export const genreEpisodeProgressLoadable = loadable(
|
||||
genreEpisodeProgressAsyncAtom,
|
||||
const genreEnrichmentsAsyncAtom = atom(
|
||||
async (get): Promise<GenreEnrichments | null> => {
|
||||
const genre = get(selectedGenreAtom);
|
||||
if (genre === null) return null;
|
||||
const genreResults = await get(genreResultsAsyncAtom);
|
||||
if (!genreResults || genreResults.length === 0)
|
||||
return { statuses: {}, progress: {} };
|
||||
const items = genreResults.map((r) => ({ tmdbId: r.tmdbId, type: r.type }));
|
||||
const [statuses, progress] = await Promise.all([
|
||||
fetchUserStatuses(items),
|
||||
fetchEpisodeProgress(items),
|
||||
]);
|
||||
return { statuses, progress };
|
||||
},
|
||||
);
|
||||
|
||||
export const genreEnrichmentsAtom = unwrap(genreEnrichmentsAsyncAtom);
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
import { atom } from "jotai";
|
||||
import { loadable } from "jotai/utils";
|
||||
import { unwrap } from "jotai/utils";
|
||||
import type { HistoryBucket, TimePeriod } from "@/lib/services/discovery";
|
||||
|
||||
export const moviePeriodAtom = atom<TimePeriod>("this_month");
|
||||
@@ -25,5 +25,5 @@ const episodeStatsAsyncAtom = atom(async (get) => {
|
||||
return fetchStats("episodes", period);
|
||||
});
|
||||
|
||||
export const movieStatsLoadable = loadable(movieStatsAsyncAtom);
|
||||
export const episodeStatsLoadable = loadable(episodeStatsAsyncAtom);
|
||||
export const movieStatsAtom = unwrap(movieStatsAsyncAtom);
|
||||
export const episodeStatsAtom = unwrap(episodeStatsAsyncAtom);
|
||||
|
||||
Reference in New Issue
Block a user