aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/client/atoms/study.ts
blob: 324f92ecf78e5e7062be21180b56212bac203c46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import { atomFamily } from "jotai-family";
import { atomWithSuspenseQuery } from "jotai-tanstack-query";
import { apiClient } from "../api/client";
import { shuffle } from "../utils/shuffle";

export interface StudyCard {
	id: string;
	deckId: string;
	noteId: string;
	isReversed: boolean;
	front: string;
	back: string;
	state: number;
	due: string;
	stability: number;
	difficulty: number;
	reps: number;
	lapses: number;
	noteType: {
		frontTemplate: string;
		backTemplate: string;
	};
	fieldValuesMap: Record<string, string>;
}

export interface StudyDeck {
	id: string;
	name: string;
}

export interface StudyData {
	deck: StudyDeck;
	cards: StudyCard[];
}

// =====================
// Study Session - Suspense-compatible
// =====================

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);

			return {
				deck: deckData.deck,
				cards: shuffle(cardsData.cards),
			};
		},
	})),
);