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:
parent
f664a00b64
commit
3b861af3fb
15 changed files with 1013 additions and 23 deletions
51
Sources/Core/Domain/Card.swift
Normal file
51
Sources/Core/Domain/Card.swift
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import Foundation
|
||||
|
||||
/// Card-DTO. Wire-Format aus `cards/apps/api/src/lib/dto.ts:toCardDto`
|
||||
/// und `cards/packages/cards-domain/src/schemas/card.ts`.
|
||||
struct Card: Codable, Identifiable, Hashable, Sendable {
|
||||
let id: String
|
||||
let deckId: String
|
||||
let userId: String
|
||||
let type: CardType
|
||||
let fields: [String: String]
|
||||
let mediaRefs: [String]
|
||||
let contentHash: String?
|
||||
let createdAt: Date
|
||||
let updatedAt: Date
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case deckId = "deck_id"
|
||||
case userId = "user_id"
|
||||
case type
|
||||
case fields
|
||||
case mediaRefs = "media_refs"
|
||||
case contentHash = "content_hash"
|
||||
case createdAt = "created_at"
|
||||
case updatedAt = "updated_at"
|
||||
}
|
||||
}
|
||||
|
||||
/// Card-Type-Enum. Vollständig aus `CardTypeSchema`. In β-2 rendern
|
||||
/// wir nur `basic`, `basic-reverse`, `cloze`. Die anderen Types
|
||||
/// kommen in β-3 und β-4 dazu, sind aber jetzt schon decodierbar.
|
||||
enum CardType: String, Codable, Sendable, CaseIterable {
|
||||
case basic
|
||||
case basicReverse = "basic-reverse"
|
||||
case cloze
|
||||
case imageOcclusion = "image-occlusion"
|
||||
case audioFront = "audio-front"
|
||||
case typing
|
||||
case multipleChoice = "multiple-choice"
|
||||
}
|
||||
|
||||
/// Vereinfachtes Card-Sub-Objekt aus `/reviews/due?deck_id=X`-Response.
|
||||
/// Server liefert nur 4 Felder (id, deckId, type, fields) als Drizzle-
|
||||
/// Joined-Subset — Achtung: `deckId` hier in **camelCase**, nicht
|
||||
/// snake_case wie sonst.
|
||||
struct ReviewCard: Codable, Hashable, Sendable {
|
||||
let id: String
|
||||
let deckId: String
|
||||
let type: CardType
|
||||
let fields: [String: String]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue