import { A, useParams } from "@solidjs/router"; import { createMemo, createSignal, For, onSettled, refresh, Show } from "solid-js"; import type { Grade } from "ts-fsrs"; import { Markdown } from "../components/Markdown"; import { studyQueue } from "../db/cards"; import { getDb } from "../db/client"; import { getDeck } from "../db/decks"; import { recordReview } from "../db/reviews"; import { isoNow } from "../lib/time"; import { btnGhost, btnPrimary, card } from "../lib/ui"; import { GRADE_LABELS, GRADES, previewIntervals, rateCard } from "../srs/scheduler"; const NEW_CARDS_PER_SESSION = 20; async function fetchQueue(deckId: number) { const db = await getDb(); return studyQueue(db, deckId, isoNow(), NEW_CARDS_PER_SESSION); } export default function StudyPage() { const params = useParams(); const deckId = () => Number(params["id"]); const deck = createMemo(() => fetchDeck(deckId())); const queue = createMemo(() => fetchQueue(deckId())); const [index, setIndex] = createSignal(0); const [revealed, setRevealed] = createSignal(false); const [reviewedCount, setReviewedCount] = createSignal(0); const [busy, setBusy] = createSignal(false); const current = () => queue()[index()]; const intervals = createMemo(() => { const item = current(); return item && previewIntervals(item, Temporal.Now.instant()); }); async function rate(grade: Grade) { const item = current(); if (!item || busy()) return; setBusy(true); try { const { fsrs, log } = rateCard(item, grade, Temporal.Now.instant()); const db = await getDb(); await recordReview(db, item.id, fsrs, log); setReviewedCount((count) => count + 1); setRevealed(false); setIndex((i) => i + 1); } finally { setBusy(false); } } function checkForMore() { setIndex(0); setRevealed(false); refresh(queue); } function onKeyDown(event: KeyboardEvent) { if (!current()) return; if (event.key === " ") { event.preventDefault(); setRevealed(true); return; } if (!revealed()) return; const grade = GRADES[Number(event.key) - 1]; if (grade) void rate(grade); } onSettled(() => { window.addEventListener("keydown", onKeyDown); return () => window.removeEventListener("keydown", onKeyDown); }); return (
โ† {deck()?.name ?? "Deck"} {reviewedCount()} reviewed ยท {Math.max(queue().length - index(), 0)} left
} > {(item) => (
setRevealed(true)}> Show answer (space) } >
{(grade, gradeIndex) => ( )}
)}
); } async function fetchDeck(id: number) { const db = await getDb(); return getDeck(db, id); } function DoneScreen(props: { reviewedCount: number; onCheckForMore: () => void }) { return (

All caught up!

You reviewed {props.reviewedCount} {props.reviewedCount === 1 ? "card" : "cards"} this session. Cards in learning may become due again in a few minutes.

Back to decks
); } function gradeButtonClass(grade: Grade): string { const base = "cursor-pointer rounded-lg px-2 py-2 text-sm font-medium text-white transition-colors disabled:opacity-50"; const colors: Record = { 1: "bg-red-600 hover:bg-red-500", 2: "bg-amber-600 hover:bg-amber-500", 3: "bg-teal-600 hover:bg-teal-500", 4: "bg-sky-600 hover:bg-sky-500", }; return `${base} ${colors[grade]}`; }