Files
Spanish/Conjuga/SharedModels/Sources/SharedModels/WidgetSnapshot.swift
T
Trey t f59d81fc5a Widget word-of-day picks from master verb list filtered by user level
Previously the widget was picking from course VocabCards, which could
land on any course week and was showing unrelated phrases instead of the
verbs the user is actually studying.

Now the widget uses a new VerbStore.fetchVerbOfDay helper that:
- Expands the user's selectedLevel via VerbLevelGroup.dataLevels
- Runs a FetchDescriptor<Verb> filtered by those levels, sorted by rank
- Uses fetchCount + fetchOffset for a deterministic daily pick

The main app mirrors UserProgress.selectedLevel into the shared app
group UserDefaults (key "selectedVerbLevel") on every WidgetDataService
update, so the widget process can read it without touching the cloud
store.

WordOfDay.weekNumber was replaced with a more flexible subtitle: String
so widgets can display "Level: Basic" instead of course week numbers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 14:04:45 -05:00

65 lines
1.8 KiB
Swift

import Foundation
public struct WordOfDay: Codable, Equatable, Sendable {
public var spanish: String
public var english: String
public var subtitle: String
public init(spanish: String, english: String, subtitle: String) {
self.spanish = spanish
self.english = english
self.subtitle = subtitle
}
}
public struct WidgetData: Codable, Equatable, Sendable {
public var todayCount: Int
public var dailyGoal: Int
public var currentStreak: Int
public var dueCardCount: Int
public var wordOfTheDay: WordOfDay?
public var latestTestScore: Int?
public var latestTestWeek: Int?
public var currentWeek: Int
public var lastUpdated: Date
public init(
todayCount: Int,
dailyGoal: Int,
currentStreak: Int,
dueCardCount: Int,
wordOfTheDay: WordOfDay?,
latestTestScore: Int?,
latestTestWeek: Int?,
currentWeek: Int,
lastUpdated: Date
) {
self.todayCount = todayCount
self.dailyGoal = dailyGoal
self.currentStreak = currentStreak
self.dueCardCount = dueCardCount
self.wordOfTheDay = wordOfTheDay
self.latestTestScore = latestTestScore
self.latestTestWeek = latestTestWeek
self.currentWeek = currentWeek
self.lastUpdated = lastUpdated
}
public static let placeholder = WidgetData(
todayCount: 12,
dailyGoal: 50,
currentStreak: 3,
dueCardCount: 8,
wordOfTheDay: WordOfDay(spanish: "hablar", english: "to speak", subtitle: "Level: Basic"),
latestTestScore: 85,
latestTestWeek: 2,
currentWeek: 2,
lastUpdated: Date()
)
public var progressPercent: Double {
guard dailyGoal > 0 else { return 0 }
return min(Double(todayCount) / Double(dailyGoal), 1.0)
}
}