Add system (curated) vs. user-uploaded flashcards

System decks/notes are owned by a reserved sentinel user id, reusing
all existing per-user store/index plumbing unchanged. Regular users
see them under a separate "Official Decks" section, can study them
with fully independent per-user FSRS progress (lazily materialized on
first visit), but can never edit or archive them - enforced both in
the client (no Edit/Archive controls) and server-side (ownership guard
on every mutating endpoint). Includes a small idempotent seed command
(`dotnet run -- seed-flashcards`) for testing, since there's no in-app
authoring UI yet.
This commit is contained in:
hz
2026-07-22 15:52:19 -05:00
parent 3a01e9156c
commit 7f76ca0fdc
13 changed files with 444 additions and 31 deletions
+11
View File
@@ -72,6 +72,7 @@ var compact_layout := false
var deck_panel: PanelContainer
var deck_name_label: Label
var deck_system_badge: Label
var deck_ring: Control
var deck_mastered_label: Label
var change_deck_button: Button
@@ -371,6 +372,13 @@ func _build_deck_panel() -> void:
deck_name_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
content.add_child(deck_name_label)
deck_system_badge = Label.new()
deck_system_badge.text = "Official Deck"
deck_system_badge.add_theme_font_size_override("font_size", 12)
deck_system_badge.add_theme_color_override("font_color", Brand.PURPLE)
deck_system_badge.visible = false
content.add_child(deck_system_badge)
var ring_center := CenterContainer.new()
content.add_child(ring_center)
deck_ring = CircularProgress.new()
@@ -597,6 +605,7 @@ func _refresh_deck_panel() -> void:
var name := "All decks"
var total := 0
var mastered := 0
var is_system := false
if deck_id.is_empty():
for summary in decks:
total += int(summary.get("total", 0))
@@ -608,8 +617,10 @@ func _refresh_deck_panel() -> void:
name = str(deck.get("name", "Deck"))
total = int(summary.get("total", 0))
mastered = int(summary.get("mastered", 0))
is_system = bool(deck.get("isSystem", false))
break
deck_name_label.text = name
deck_system_badge.visible = is_system
var fraction := float(mastered) / float(total) if total > 0 else 0.0
deck_ring.configure(fraction, "%d%%" % roundi(fraction * 100.0), "Mastery", Brand.ROYAL_BLUE, dark)
deck_mastered_label.text = "%d / %d cards mastered" % [mastered, total]