aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/client/atoms/study.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/atoms/study.ts')
-rw-r--r--src/client/atoms/study.ts60
1 files changed, 27 insertions, 33 deletions
diff --git a/src/client/atoms/study.ts b/src/client/atoms/study.ts
index 17519de..115bbcd 100644
--- a/src/client/atoms/study.ts
+++ b/src/client/atoms/study.ts
@@ -1,19 +1,12 @@
import { atomFamily } from "jotai-family";
import { atomWithSuspenseQuery } from "jotai-tanstack-query";
import { getStartOfStudyDayBoundary } from "../../shared/date";
-import { apiClient } from "../api/client";
-import type { CardStateType } from "../db";
-import { cacheStudyCards, type ServerStudyCard } from "../sync";
+import { localDeckRepository } from "../db/repositories";
+import { buildStudyCards, type StudyCardView } from "../db/study-builder";
import { createSeededRandom, shuffle } from "../utils/random";
+import { ensureBootstrap } from "./sync";
-export interface StudyCard extends ServerStudyCard {
- state: CardStateType;
- noteType: {
- frontTemplate: string;
- backTemplate: string;
- };
- fieldValuesMap: Record<string, string>;
-}
+export type StudyCard = StudyCardView;
export interface StudyDeck {
id: string;
@@ -25,35 +18,36 @@ export interface StudyData {
cards: StudyCard[];
}
+async function loadStudyData(deckId: string): Promise<StudyData | null> {
+ const deck = await localDeckRepository.findById(deckId);
+ if (!deck || deck.deletedAt !== null) return null;
+ const cards = await buildStudyCards(deckId);
+ const seed = getStartOfStudyDayBoundary().getTime();
+ return {
+ deck: { id: deck.id, name: deck.name },
+ cards: shuffle(cards, createSeededRandom(seed)),
+ };
+}
+
// =====================
-// Study Session - Suspense-compatible
+// Study Session - Suspense-compatible, IndexedDB-first
// =====================
export const studyDataAtomFamily = atomFamily((deckId: string) =>
atomWithSuspenseQuery(() => ({
queryKey: ["decks", deckId, "study"],
queryFn: async (): Promise<StudyData> => {
- // Fetch deck and due cards in parallel
- const [deckRes, cardsRes] = await Promise.all([
- apiClient.rpc.api.decks[":id"].$get({ param: { id: deckId } }),
- apiClient.rpc.api.decks[":deckId"].study.$get({ param: { deckId } }),
- ]);
-
- const deckData = await apiClient.handleResponse<{ deck: StudyDeck }>(
- deckRes,
- );
- const cardsData = await apiClient.handleResponse<{
- cards: StudyCard[];
- }>(cardsRes);
-
- // Cache cards in IndexedDB so reviews can be submitted offline.
- await cacheStudyCards(cardsData.cards);
-
- const seed = getStartOfStudyDayBoundary().getTime();
- return {
- deck: deckData.deck,
- cards: shuffle(cardsData.cards, createSeededRandom(seed)),
- };
+ let data = await loadStudyData(deckId);
+ if (data) {
+ ensureBootstrap();
+ return data;
+ }
+ await ensureBootstrap();
+ data = await loadStudyData(deckId);
+ if (!data) {
+ throw new Error(`Deck not found: ${deckId}`);
+ }
+ return data;
},
})),
);