Review breakdown: show each deck's own activity total
Follow-up to the smart-review breakdown: "I only have 2 decks" turned out to
be three deck cards (one clipped at the window edge) and 15 + 13 + 6 = 34
due. The line now carries each deck's activity total next to its due count -
"Due by deck: Sky deck - 15 of 15 - Humanismus - 13 of 15 - ... - 6 of 13" -
so the arithmetic is checkable inline without opening a deck.
groupByDeck returns {label, count, total}; new i18n key review.dueOfTotal
(en + cs). review-test pins the format. Version 0.6.9, SW v91.
This commit is contained in:
parent
1e8606a95b
commit
71c56ad06f
7 changed files with 24 additions and 8 deletions
|
|
@ -1492,6 +1492,14 @@ Things I chose while building, with the reasoning:
|
||||||
into view as you type. No strings, so no i18n; the additive change sits on top of the merged
|
into view as you type. No strings, so no i18n; the additive change sits on top of the merged
|
||||||
scene-heat rework unchanged.
|
scene-heat rework unchanged.
|
||||||
|
|
||||||
|
100. **The review breakdown carries each deck's own total.** The follow-up to 99: "bro its not
|
||||||
|
possible, I only have 2 decks" — the screenshot showed three deck cards (one clipped at the
|
||||||
|
window edge) and a breakdown of 15 + 13 + 6 = 34, so the count was right and the *number of
|
||||||
|
decks* was the misread. To make that checkable inline, each entry now carries the deck's
|
||||||
|
activity total next to its due count — "Due by deck: Renesanční… · 15 of 15 · Humanismus… ·
|
||||||
|
13 of 15 · Renesance… · 6 of 13" — so the arithmetic (34 due out of 43 activities) is
|
||||||
|
verifiable without opening a single deck. review-test pins the format.
|
||||||
|
|
||||||
99. **The smart review names its decks.** "It contains mistakes I either did more than once or
|
99. **The smart review names its decks.** "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 count
|
from decks that I've removed — there's no way there's 34 to repeat with 2 decks." The count
|
||||||
itself was honest: `dueItems`/`mistakeItems` only resolve live (deck, activity) pairs, so a
|
itself was honest: `dueItems`/`mistakeItems` only resolve live (deck, activity) pairs, so a
|
||||||
|
|
|
||||||
|
|
@ -680,13 +680,17 @@ export function LibraryScreen({ initialTab = "decks" }: { initialTab?: "decks" |
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** "Sky deck · 17 · Biology · 14 (+2 more)" — capped so the line stays short. */
|
/** "Sky deck · 17 of 17 · Biology · 14 of 15 (+2 more)" — the deck's own
|
||||||
|
* activity total rides along, so a count that mixes decks can be checked
|
||||||
|
* against the decks instead of looking impossible. Capped at three decks. */
|
||||||
function deckBreakdown(
|
function deckBreakdown(
|
||||||
items: ReviewItem[],
|
items: ReviewItem[],
|
||||||
t: (key: string, vars?: Record<string, string | number>) => string,
|
t: (key: string, vars?: Record<string, string | number>) => string,
|
||||||
): string {
|
): string {
|
||||||
const groups = groupByDeck(items);
|
const groups = groupByDeck(items);
|
||||||
const shown = groups.slice(0, 3).map((group) => `${group.label} · ${group.count}`);
|
const shown = groups
|
||||||
|
.slice(0, 3)
|
||||||
|
.map((group) => `${group.label} · ${t("review.dueOfTotal", { due: group.count, total: group.total })}`);
|
||||||
const rest = groups.length - shown.length;
|
const rest = groups.length - shown.length;
|
||||||
if (rest > 0) shown.push(t("review.moreDecks", { n: rest }));
|
if (rest > 0) shown.push(t("review.moreDecks", { n: rest }));
|
||||||
return shown.join(" · ");
|
return shown.join(" · ");
|
||||||
|
|
|
||||||
|
|
@ -846,6 +846,7 @@ const en: Record<string, string> = {
|
||||||
"review.due.few": "{n} due",
|
"review.due.few": "{n} due",
|
||||||
"review.due.many": "{n} due",
|
"review.due.many": "{n} due",
|
||||||
"review.dueByDeck": "Due by deck: {list}",
|
"review.dueByDeck": "Due by deck: {list}",
|
||||||
|
"review.dueOfTotal": "{due} of {total}",
|
||||||
"review.mistakesByDeck": "Mistakes by deck: {list}",
|
"review.mistakesByDeck": "Mistakes by deck: {list}",
|
||||||
"review.moreDecks.one": "+{n} more deck",
|
"review.moreDecks.one": "+{n} more deck",
|
||||||
"review.moreDecks.few": "+{n} more decks",
|
"review.moreDecks.few": "+{n} more decks",
|
||||||
|
|
@ -1852,6 +1853,7 @@ const cs: Record<string, string> = {
|
||||||
"review.due.few": "{n} k opakování",
|
"review.due.few": "{n} k opakování",
|
||||||
"review.due.many": "{n} k opakování",
|
"review.due.many": "{n} k opakování",
|
||||||
"review.dueByDeck": "K opakování podle sady: {list}",
|
"review.dueByDeck": "K opakování podle sady: {list}",
|
||||||
|
"review.dueOfTotal": "{due} z {total}",
|
||||||
"review.mistakesByDeck": "Chyby podle sady: {list}",
|
"review.mistakesByDeck": "Chyby podle sady: {list}",
|
||||||
"review.moreDecks.one": "+{n} další sada",
|
"review.moreDecks.one": "+{n} další sada",
|
||||||
"review.moreDecks.few": "+{n} další sady",
|
"review.moreDecks.few": "+{n} další sady",
|
||||||
|
|
|
||||||
|
|
@ -77,11 +77,13 @@ export function mistakeCount(answers: AnswerRecord[], deckId?: string): number {
|
||||||
* ("34 due" with two decks on screen) is self-explanatory instead of looking
|
* ("34 due" with two decks on screen) is self-explanatory instead of looking
|
||||||
* like the same mistake twice or an item from a deck that was deleted.
|
* like the same mistake twice or an item from a deck that was deleted.
|
||||||
*/
|
*/
|
||||||
export function groupByDeck(items: ReviewItem[]): { label: string; count: number }[] {
|
/** `total` is the deck's activity count, so "34 due" can be checked against
|
||||||
const groups = new Map<string, { label: string; count: number }>();
|
* the decks themselves ("15 of 15 · 13 of 15 · 6 of 13"). */
|
||||||
|
export function groupByDeck(items: ReviewItem[]): { label: string; count: number; total: number }[] {
|
||||||
|
const groups = new Map<string, { label: string; count: number; total: number }>();
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
const key = item.deck.id;
|
const key = item.deck.id;
|
||||||
const group = groups.get(key) ?? { label: item.deck.title, count: 0 };
|
const group = groups.get(key) ?? { label: item.deck.title, count: 0, total: item.deck.activities.length };
|
||||||
group.count += 1;
|
group.count += 1;
|
||||||
groups.set(key, group);
|
groups.set(key, group);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "freezmo",
|
"name": "freezmo",
|
||||||
"version": "0.6.8",
|
"version": "0.6.9",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
*
|
*
|
||||||
* Bumping CACHE_VERSION is how a new deploy invalidates old assets.
|
* Bumping CACHE_VERSION is how a new deploy invalidates old assets.
|
||||||
*/
|
*/
|
||||||
const CACHE_VERSION = "freezmo-v90";
|
const CACHE_VERSION = "freezmo-v91";
|
||||||
const SHELL_CACHE = `${CACHE_VERSION}-shell`;
|
const SHELL_CACHE = `${CACHE_VERSION}-shell`;
|
||||||
const ASSET_CACHE = `${CACHE_VERSION}-assets`;
|
const ASSET_CACHE = `${CACHE_VERSION}-assets`;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,7 @@ async function makeReviewPage(locale = "en") {
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
"review: the card breaks the count down by deck",
|
"review: the card breaks the count down by deck",
|
||||||
breakdown === "Due by deck: Sky deck · 2 · Twin deck · 2",
|
breakdown === "Due by deck: Sky deck · 2 of 2 · Twin deck · 2 of 2",
|
||||||
breakdown,
|
breakdown,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue