Smart review: name the source deck on every card, break the counts down
"It contains mistakes I either did more than once or from decks that I've removed - there's no way there's 34 to repeat with 2 decks." The counts were honest (dueItems/mistakeItems only resolve live deck+activity pairs; a seeded repro with 37 SRS rows and 30 live shows "30 due"), but nothing in the UI said where an item came from: a deck created twice from the same notes shows every question twice, and a deck that survived a delete or a sync looks like a ghost. Both read as a broken list. - SessionFeed gains showDeck (review + mistakes): a deck chip above every card, so two identical questions are visibly two decks. - The Smart review card breaks its numbers down under the buttons: "Due by deck: Sky deck - 17 - Sky deck (2) - 17", capped at three decks plus "+n more" (groupByDeck in lib/learning). - New i18n keys (en + cs, plural forms for "+n more"). review-test pins it: a twin-deck seed renders the breakdown and both deck chips in the feed. Version 0.6.8, SW v90.
This commit is contained in:
parent
2a956a1cd4
commit
1e8606a95b
9 changed files with 129 additions and 8 deletions
|
|
@ -16,7 +16,7 @@ import { createFolder, folderStore, renameFolder } from "@/lib/folders";
|
|||
import { deleteHomework, homeworkStore, sortHomework } from "@/lib/homework";
|
||||
import { learningStore } from "@/lib/history";
|
||||
import { useI18n } from "@/lib/i18n";
|
||||
import { dueItems, mistakeItems, todayAnswerCount } from "@/lib/learning";
|
||||
import { dueItems, groupByDeck, mistakeItems, todayAnswerCount, type ReviewItem } from "@/lib/learning";
|
||||
import { isReported, reportStore } from "@/lib/question-reports";
|
||||
import { settingsStore } from "@/lib/settings";
|
||||
import { ALL_FOLDERS, folderSelection, useSelectedFolder } from "@/lib/nav";
|
||||
|
|
@ -120,20 +120,22 @@ export function LibraryScreen({ initialTab = "decks" }: { initialTab?: "decks" |
|
|||
deleting a deck or reporting a question must drop its due schedules and
|
||||
its mistakes right away, matching ReviewClient. */
|
||||
const deckIds = useMemo(() => new Set(decks.map((deck) => deck.id)), [decks]);
|
||||
const due = useMemo(
|
||||
const dueItemsList = useMemo(
|
||||
() => {
|
||||
void reports;
|
||||
return dueItems(srs).filter((item) => deckIds.has(item.deck.id) && !isReported(item.deck.id, item.activity.id)).length;
|
||||
return dueItems(srs).filter((item) => deckIds.has(item.deck.id) && !isReported(item.deck.id, item.activity.id));
|
||||
},
|
||||
[srs, deckIds, reports],
|
||||
);
|
||||
const mistakes = useMemo(
|
||||
const mistakeItemsList = useMemo(
|
||||
() => {
|
||||
void reports;
|
||||
return mistakeItems(answers).filter((item) => deckIds.has(item.deck.id) && !isReported(item.deck.id, item.activity.id)).length;
|
||||
return mistakeItems(answers).filter((item) => deckIds.has(item.deck.id) && !isReported(item.deck.id, item.activity.id));
|
||||
},
|
||||
[answers, deckIds, reports],
|
||||
);
|
||||
const due = dueItemsList.length;
|
||||
const mistakes = mistakeItemsList.length;
|
||||
const todayCount = useMemo(() => todayAnswerCount(answers), [answers]);
|
||||
const importRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
|
|
@ -370,6 +372,19 @@ export function LibraryScreen({ initialTab = "decks" }: { initialTab?: "decks" |
|
|||
<LinkLoadingScreen messageKey="review.preparing" />
|
||||
</Link>
|
||||
</div>
|
||||
{/* Where the numbers come from. "34 due" over a two-deck library read
|
||||
as duplicated or ghost items; naming the decks (and how many each
|
||||
contributes) makes the count verifiable at a glance. */}
|
||||
{due > 0 ? (
|
||||
<p className="text-[11.5px] leading-snug text-[var(--text-muted)]">
|
||||
{t("review.dueByDeck", { list: deckBreakdown(dueItemsList, t) })}
|
||||
</p>
|
||||
) : null}
|
||||
{mistakes > 0 ? (
|
||||
<p className="text-[11.5px] leading-snug text-[var(--text-muted)]">
|
||||
{t("review.mistakesByDeck", { list: deckBreakdown(mistakeItemsList, t) })}
|
||||
</p>
|
||||
) : null}
|
||||
</section>
|
||||
|
||||
{/* gentle backup nudge — dismissible, never blocks ---------------- */}
|
||||
|
|
@ -664,3 +679,15 @@ export function LibraryScreen({ initialTab = "decks" }: { initialTab?: "decks" |
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** "Sky deck · 17 · Biology · 14 (+2 more)" — capped so the line stays short. */
|
||||
function deckBreakdown(
|
||||
items: ReviewItem[],
|
||||
t: (key: string, vars?: Record<string, string | number>) => string,
|
||||
): string {
|
||||
const groups = groupByDeck(items);
|
||||
const shown = groups.slice(0, 3).map((group) => `${group.label} · ${group.count}`);
|
||||
const rest = groups.length - shown.length;
|
||||
if (rest > 0) shown.push(t("review.moreDecks", { n: rest }));
|
||||
return shown.join(" · ");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue