Files
flashcards/packages/frontend/src/stores/sessionStore.ts

67 lines
2.0 KiB
TypeScript

import { create } from 'zustand';
import type { SessionRow, QueueItem } from '@flashcard/shared';
import { sessionsApi } from '../api/sessions.js';
interface SessionState {
session: SessionRow | null;
current: QueueItem | null;
done: boolean;
showAnswer: boolean;
shownAt: number | null;
start: (input: { lessonId: number; maxCards: number | null; shuffle: boolean; direction: 'forward' | 'backward' | 'both' }) => Promise<void>;
reveal: () => void;
answer: (result: 'correct' | 'incorrect') => Promise<void>;
end: () => Promise<void>;
abandon: () => Promise<void>;
reset: () => void;
}
export const useSession = create<SessionState>((set, get) => ({
session: null, current: null, done: false, showAnswer: false, shownAt: null,
start: async (input) => {
const { session } = await sessionsApi.start(input);
const nx = await sessionsApi.next(session.id);
set({
session,
done: nx.done,
current: nx.done ? null : nx.item,
showAnswer: false,
shownAt: Date.now(),
});
},
reveal: () => set({ showAnswer: true }),
answer: async (result) => {
const s = get();
if (!s.session || !s.current) return;
const ttm = s.shownAt ? Date.now() - s.shownAt : null;
await sessionsApi.attempt(s.session.id, {
cardId: s.current.cardId, direction: s.current.direction, result, timeToAnswerMs: ttm,
});
const nx = await sessionsApi.next(s.session.id);
if (nx.done) {
set({ done: true, current: null, showAnswer: false });
} else {
set({ current: nx.item, showAnswer: false, shownAt: Date.now() });
}
},
end: async () => {
const s = get();
if (!s.session) return;
const finished = await sessionsApi.end(s.session.id);
set({ session: finished });
},
abandon: async () => {
const s = get();
if (!s.session) return;
await sessionsApi.abandon(s.session.id);
set({ session: null, current: null, done: false, showAnswer: false });
},
reset: () => set({ session: null, current: null, done: false, showAnswer: false, shownAt: null }),
}));