Files
Spanish/Conjuga/Conjuga/Views/Settings/SettingsView.swift
T
Trey T cee962c0e0 Vocab Flashcards — make session size configurable in Settings
VocabVerbPool.sessionCardLimit was hardcoded at 20. Settings now has a
"Vocab Flashcards → Cards per session" picker (10 / 15 / 20 / 25 / 30 /
50 / All) backed by the vocabSessionCardLimit @AppStorage key.

VocabVerbPool.sessionCardLimit became a computed property reading that
key (0/unset → default 20; 999 → "All"). Applies to both Quiz and Learn
modes since they share the same session pool.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 13:06:07 -05:00

173 lines
6.8 KiB
Swift

import SwiftUI
import SharedModels
import SwiftData
struct SettingsView: View {
@Environment(\.cloudModelContextProvider) private var cloudModelContextProvider
@State private var progress: UserProgress?
@State private var dailyGoal: Double = 50
@State private var showVosotros: Bool = true
@State private var autoFillStem: Bool = false
/// Cards per vocab-practice session. 999 = "All" (no cap).
@AppStorage("vocabSessionCardLimit") private var vocabSessionCardLimit: Int = 20
private let vocabSessionSizes: [Int] = [10, 15, 20, 25, 30, 50, 999]
private let levels = VerbLevel.allCases
private let irregularCategories: [IrregularSpan.SpanCategory] = [
.spelling, .stemChange, .uniqueIrregular
]
private var cloudModelContext: ModelContext { cloudModelContextProvider() }
var body: some View {
NavigationStack {
Form {
Section("Practice") {
VStack(alignment: .leading) {
Text("Daily Goal: \(Int(dailyGoal)) cards")
Slider(value: $dailyGoal, in: 10...200, step: 10)
}
.onChange(of: dailyGoal) { _, newValue in
progress?.dailyGoal = Int(newValue)
saveProgress()
}
Toggle("Include vosotros", isOn: $showVosotros)
.onChange(of: showVosotros) { _, newValue in
progress?.showVosotros = newValue
saveProgress()
}
Toggle("Auto-fill verb stem (Full Table)", isOn: $autoFillStem)
.onChange(of: autoFillStem) { _, newValue in
progress?.autoFillStem = newValue
saveProgress()
}
}
Section {
Picker("Cards per session", selection: $vocabSessionCardLimit) {
ForEach(vocabSessionSizes, id: \.self) { size in
Text(size == 999 ? "All" : "\(size)").tag(size)
}
}
} header: {
Text("Vocab Flashcards")
} footer: {
Text("How many verbs a Vocab Flashcards session draws. Overdue verbs are pulled first, then new ones.")
}
Section {
ForEach(levels, id: \.self) { level in
Toggle(level.displayName, isOn: Binding(
get: {
progress?.selectedVerbLevels.contains(level) ?? false
},
set: { enabled in
guard let progress else { return }
progress.setLevelEnabled(level, enabled: enabled)
saveProgress()
}
))
}
} header: {
Text("Levels")
} footer: {
Text("Practice pulls only from verbs whose level is enabled. Turn on multiple to mix.")
}
Section {
ForEach(TenseInfo.all) { tense in
Toggle(tense.english, isOn: Binding(
get: {
progress?.enabledTenseIDs.contains(tense.id) ?? false
},
set: { enabled in
guard let progress else { return }
progress.setTenseEnabled(tense.id, enabled: enabled)
saveProgress()
}
))
}
} header: {
Text("Tenses")
}
Section {
ForEach(irregularCategories, id: \.self) { category in
Toggle(category.rawValue, isOn: Binding(
get: {
progress?.enabledIrregularCategories.contains(category) ?? false
},
set: { enabled in
guard let progress else { return }
progress.setIrregularCategoryEnabled(category, enabled: enabled)
saveProgress()
}
))
}
} header: {
Text("Irregular Types")
} footer: {
Text("Leave all off to include regular and irregular verbs. Enable any to restrict practice to those irregularity types.")
}
Section {
Toggle("Reflexive verbs only", isOn: Binding(
get: { progress?.showReflexiveVerbsOnly ?? false },
set: { enabled in
progress?.showReflexiveVerbsOnly = enabled
saveProgress()
}
))
} header: {
Text("Reflexive")
} footer: {
Text("When on, practice pulls only from the curated list of common reflexive verbs.")
}
Section("Stats") {
if let progress {
LabeledContent("Total Reviewed", value: "\(progress.totalReviewed)")
LabeledContent("Current Streak", value: "\(progress.currentStreak) days")
LabeledContent("Longest Streak", value: "\(progress.longestStreak) days")
}
}
Section("Reference") {
NavigationLink("How Features Work") {
FeatureReferenceView()
}
NavigationLink("Downloaded Videos") {
DownloadedVideosView()
}
}
Section("About") {
LabeledContent("Version", value: "1.0.0")
}
}
.navigationTitle("Settings")
.onAppear(perform: loadProgress)
}
}
private func loadProgress() {
let resolved = ReviewStore.fetchOrCreateUserProgress(context: cloudModelContext)
progress = resolved
dailyGoal = Double(resolved.dailyGoal)
showVosotros = resolved.showVosotros
autoFillStem = resolved.autoFillStem
}
private func saveProgress() {
try? cloudModelContext.save()
}
}
#Preview {
SettingsView()
.modelContainer(for: UserProgress.self, inMemory: true)
}