v0.3.0 — Phase β-2 Study-Loop

Voller Lern-Flow mit Web-Parität: fällige Karten via /reviews/due
laden, flip + rate (4 Buttons + Haptic), Grades via Offline-Queue
ans Server-FSRS schicken.

- Card/Review/DueReview DTOs mit snake_case + camelCase-deckId-
  Sonderfall im embedded card-Subobjekt
- CardType-Enum (alle 7 Typen), Rating-Enum mit deutschen Labels
- Cloze-Helper 1:1-Port aus cards-domain (extractClusterIds,
  subIndexCount, clusterId, renderPrompt/Answer, hint)
- CardsAPI.dueReviews(deckId:) + gradeReview(cardId,subIndex,rating,reviewedAt)
- PendingGrade SwiftData-Model + GradeQueue (FIFO-Drain, originaler
  Timestamp bleibt, bei Netzfehler in Queue, Retry beim nächsten Drain)
- StudySession @Observable State-Machine
- CardRenderer für basic, basic-reverse, cloze; Placeholder für
  image-occlusion/audio-front/typing/multiple-choice (β-3/β-4)
- RatingBar mit UIImpactFeedbackGenerator (medium/heavy)
- StudySessionView per NavigationLink aus DeckListView
- 9 neue Tests (Cloze: 8, Review-Decoding: 3), insgesamt 17 grün

Server-authoritative FSRS bleibt — kein ts-fsrs-Port.
Endurance-Test auf realem Gerät steht aus (siehe PLAN.md).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-05-13 00:16:11 +02:00
parent f664a00b64
commit 3b861af3fb
15 changed files with 1013 additions and 23 deletions

64
PLAN.md
View file

@ -1,9 +1,13 @@
# Plan — cards-native (SwiftUI Universal)
**Stand: 2026-05-13 — Phasen β-0 + β-1 abgeschlossen.** Repo lebt
auf Forgejo, Login funktioniert, Deck-Liste mit Card-/Due-Counts +
Offline-SwiftData-Cache + Pull-to-Refresh + Inbox-Banner für
Marketplace-Forks. 6 Unit-Tests + 1 UI-Test grün.
**Stand: 2026-05-13 — Phasen β-0 + β-1 + β-2 abgeschlossen.**
Repo auf Forgejo, Login funktioniert, Deck-Liste mit Cache +
Pull-to-Refresh, voller Study-Loop mit Flip/Rating/Haptic +
Offline-Queue für Grades (PendingGrade SwiftData). Cloze client-
rendered (1:1-Port aus cards-domain). 17 Unit-Tests + 1 UI-Test grün.
Pflicht-Check für β-2: Endurance-Test auf realem Gerät (200+ Karten
mit Flugmodus zwischendurch) steht aus — Aufgabe für Till.
> **SOT:** `../mana/docs/playbooks/CARDS_NATIVE_GREENFIELD.md`.
> Dieses File ist die App-lokale Status-Spur, das Greenfield-Doc
@ -24,6 +28,26 @@ Marketplace-Forks. 6 Unit-Tests + 1 UI-Test grün.
- `LoginView` (Email/PW gegen mana-auth)
- 3 Unit-Tests (AppConfig)
✅ **β-2 — Study-Loop (2026-05-13, Tag `v0.3.0`)**
- `Card`, `Review`, `DueReview` Codable-DTOs, `CardType`-Enum (alle 7 Typen)
- `Rating`-Enum: `again | hard | good | easy` mit deutschen Labels
- `Cloze`-Helpers (extractClusterIds, subIndexCount, clusterId,
renderPrompt, renderAnswer, hint) — 1:1-Port aus
`cards/packages/cards-domain/src/cloze.ts`
- `CardsAPI.dueReviews(deckId:)`, `CardsAPI.gradeReview(...)` mit
ISO8601-Encoder
- `PendingGrade` SwiftData-Model + `GradeQueue` für Offline-Submit
(FIFO-Drain, originaler reviewedAt-Timestamp bleibt erhalten)
- `StudySession` als @Observable State-Machine
(loading/studying/finished/failed)
- `CardRenderer`: basic, basic-reverse (sub-index-abhängig), cloze
client-rendered. image-occlusion/audio-front/typing/multiple-choice
zeigen Placeholder (β-3/β-4)
- `RatingBar` mit Haptic-Feedback (medium für again/hard/good,
heavy für easy, soft beim Flip)
- `StudySessionView` vollbild aus DeckListView per NavigationLink
- 9 zusätzliche Tests (Cloze 8x, Review/DueReview-Decoding 3x)
✅ **β-1 — Decks lesen (2026-05-13, Tag `v0.2.0`)**
- `Deck`-Codable-DTO mit snake_case-CodingKeys, plus
`DeckCategory`, `DeckVisibility`, `FsrsSettings`
@ -43,29 +67,33 @@ Marketplace-Forks. 6 Unit-Tests + 1 UI-Test grün.
|---|---|---|
| β-0 | ✅ 2026-05-12 | Setup, Login, API-Probe |
| β-1 | ✅ 2026-05-13 | Decks lesen, SwiftData-Cache, Pull-to-Refresh |
| β-2 | — | Study-Loop, Offline-Grade-Queue, Endurance-Test |
| β-2 | ✅ 2026-05-13 | Study-Loop, Offline-Grade-Queue (Endurance-Test offen) |
| β-3 | — | Card-/Deck-Editor (basic, cloze, typing, multiple-choice) |
| β-4 | — | Media, image-occlusion (PencilKit), audio-front |
| β-5 | — | Marketplace, Universal-Links |
| β-6 | — | Native-Polish (Widgets, Notifications, Share-Extension) |
| β-7 | — | App-Store-Submission |
## Nächste Schritte für β-2
## Nächste Schritte für β-3 (Editor)
Aus Greenfield-Plan-Sektion "Phase β-2 — Study-Loop":
Aus Greenfield-Plan-Sektion "Phase β-3 — Card-/Deck-Editor":
1. `Card`-DTO + `Review`-DTO aus `cards/apps/api/src/lib/dto.ts`
2. `CardsAPI.dueCards(deckId:)` → fetcht `/reviews/due` + zugehörige
`/cards/:id`-Details für die Karten-Inhalte
3. `StudySessionView` mit `CardRenderer`-switch (basic + basic-reverse
+ cloze; cloze-Rendering kommt vom Server via `renderClozePrompt`)
4. Flip-Animation, Rating-Bar (`again | hard | good | easy`)
5. `POST /api/v1/reviews/:cardId/:subIndex/grade` mit Haptic-Feedback
6. `PendingGrade` SwiftData-Model als Offline-Queue, Drain bei Reconnect
7. Endurance-Test auf realem Gerät (200+ Karten, Flugmodus zwischendurch)
1. `DeckCreateView`: Form für Name, Description, Color (Picker),
Category-Picker, Visibility, FSRS-Settings (Sheet)
2. `CardEditorView` per Type (basic, cloze, typing, multiple-choice):
Two-Text-Fields oder Cloze-Syntax-Highlighting
3. POST/PATCH/DELETE `/api/v1/cards` und `/api/v1/decks`
4. Anki-Import als Datei-Picker → `/api/v1/decks/import`
**Erfolgskriterium:** 50 Karten am Stück im Simulator durchgraden,
Web zeigt nach Refresh die gleichen Reviews-States.
**Erfolgskriterium:** Karte in Native erstellt, in Web sichtbar;
Karte in Web erstellt, in Native sichtbar (Pull-to-Refresh).
## Pflicht-Tests für β-2 (vor β-3-Start)
- [ ] Endurance-Test auf realem Gerät: 200+ Karten lernen, Flugmodus
zwischendurch — alle Grades landen am Server nach Reconnect.
- [ ] Cross-Check mit Web: Karte gegrade in Native → Web zeigt
identischen Review-State nach Reload.
## Cross-Refs