Highlight main tenses with Essential badges and focus mode #7
@@ -29,6 +29,18 @@ enum TenseID: String, CaseIterable, Codable, Sendable, Hashable {
|
|||||||
.ind_futuro,
|
.ind_futuro,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/// The 6 most essential tenses every learner should master.
|
||||||
|
static let coreTenses: Set<TenseID> = [
|
||||||
|
.ind_presente,
|
||||||
|
.ind_preterito,
|
||||||
|
.ind_imperfecto,
|
||||||
|
.ind_futuro,
|
||||||
|
.subj_presente,
|
||||||
|
.imp_afirmativo,
|
||||||
|
]
|
||||||
|
|
||||||
|
static let coreTenseIDs = coreTenses.map(\.rawValue)
|
||||||
|
|
||||||
static let defaultPracticeIDs = defaultPractice.map(\.rawValue)
|
static let defaultPracticeIDs = defaultPractice.map(\.rawValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,6 +51,10 @@ struct TenseInfo: Identifiable, Hashable, Sendable {
|
|||||||
let mood: String
|
let mood: String
|
||||||
let order: Int
|
let order: Int
|
||||||
|
|
||||||
|
var isCore: Bool {
|
||||||
|
TenseID(rawValue: id).map { TenseID.coreTenses.contains($0) } ?? false
|
||||||
|
}
|
||||||
|
|
||||||
static let all: [TenseInfo] = [
|
static let all: [TenseInfo] = [
|
||||||
TenseInfo(id: TenseID.ind_presente.rawValue, spanish: "Indicativo Presente", english: "Present", mood: "Indicative", order: 0),
|
TenseInfo(id: TenseID.ind_presente.rawValue, spanish: "Indicativo Presente", english: "Present", mood: "Indicative", order: 0),
|
||||||
TenseInfo(id: TenseID.ind_preterito.rawValue, spanish: "Indicativo Pretérito", english: "Preterite", mood: "Indicative", order: 1),
|
TenseInfo(id: TenseID.ind_preterito.rawValue, spanish: "Indicativo Pretérito", english: "Preterite", mood: "Indicative", order: 1),
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ struct PracticeSessionService {
|
|||||||
if let form = pickIrregularForm(filter: filter) {
|
if let form = pickIrregularForm(filter: filter) {
|
||||||
return loadCard(from: form)
|
return loadCard(from: form)
|
||||||
}
|
}
|
||||||
|
case .commonTenses:
|
||||||
|
if let form = pickCommonTenseForm() {
|
||||||
|
return loadCard(from: form)
|
||||||
|
}
|
||||||
case .none:
|
case .none:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -231,6 +235,20 @@ struct PracticeSessionService {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func pickCommonTenseForm() -> VerbForm? {
|
||||||
|
let settings = settings()
|
||||||
|
let coreTenseIDs = TenseID.coreTenseIDs
|
||||||
|
let verbs = referenceStore.fetchVerbs(selectedLevel: settings.selectedLevel)
|
||||||
|
guard let verb = verbs.randomElement() else { return nil }
|
||||||
|
|
||||||
|
let forms = referenceStore.fetchVerbForms(verbId: verb.id).filter { form in
|
||||||
|
coreTenseIDs.contains(form.tenseId) &&
|
||||||
|
(settings.showVosotros || form.personIndex != 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
return forms.randomElement()
|
||||||
|
}
|
||||||
|
|
||||||
private func pickRandomForm() -> VerbForm? {
|
private func pickRandomForm() -> VerbForm? {
|
||||||
let settings = settings()
|
let settings = settings()
|
||||||
let verbs = referenceStore.fetchVerbs(selectedLevel: settings.selectedLevel)
|
let verbs = referenceStore.fetchVerbs(selectedLevel: settings.selectedLevel)
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ enum FocusMode: Sendable {
|
|||||||
case none
|
case none
|
||||||
case weakVerbs
|
case weakVerbs
|
||||||
case irregularity(IrregularityFilter)
|
case irregularity(IrregularityFilter)
|
||||||
|
case commonTenses
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
|
|||||||
@@ -100,12 +100,25 @@ private struct TenseRowView: View {
|
|||||||
let tense: TenseInfo
|
let tense: TenseInfo
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 2) {
|
HStack {
|
||||||
Text(tense.english)
|
VStack(alignment: .leading, spacing: 2) {
|
||||||
.font(.headline)
|
Text(tense.english)
|
||||||
Text(tense.spanish)
|
.font(.headline)
|
||||||
.font(.subheadline)
|
Text(tense.spanish)
|
||||||
.foregroundStyle(.secondary)
|
.font(.subheadline)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
if tense.isCore {
|
||||||
|
Text("Essential")
|
||||||
|
.font(.caption2.weight(.semibold))
|
||||||
|
.foregroundStyle(.orange)
|
||||||
|
.padding(.horizontal, 8)
|
||||||
|
.padding(.vertical, 3)
|
||||||
|
.background(.orange.opacity(0.12), in: Capsule())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,6 +135,44 @@ struct PracticeView: View {
|
|||||||
.font(.headline)
|
.font(.headline)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
|
||||||
|
// Common tenses focus
|
||||||
|
Button {
|
||||||
|
viewModel.practiceMode = .flashcard
|
||||||
|
viewModel.focusMode = .commonTenses
|
||||||
|
viewModel.sessionCorrect = 0
|
||||||
|
viewModel.sessionTotal = 0
|
||||||
|
viewModel.loadNextCard(
|
||||||
|
localContext: modelContext,
|
||||||
|
cloudContext: cloudModelContext
|
||||||
|
)
|
||||||
|
withAnimation { isPracticing = true }
|
||||||
|
} label: {
|
||||||
|
HStack(spacing: 14) {
|
||||||
|
Image(systemName: "star.fill")
|
||||||
|
.font(.title3)
|
||||||
|
.foregroundStyle(.orange)
|
||||||
|
.frame(width: 32)
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 2) {
|
||||||
|
Text("Common Tenses")
|
||||||
|
.font(.subheadline.weight(.semibold))
|
||||||
|
Text("Practice the 6 most essential tenses")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Image(systemName: "chevron.right")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.tertiary)
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
.padding(.vertical, 12)
|
||||||
|
}
|
||||||
|
.tint(.primary)
|
||||||
|
.glassEffect(in: RoundedRectangle(cornerRadius: 14))
|
||||||
|
|
||||||
// Weak verbs focus
|
// Weak verbs focus
|
||||||
Button {
|
Button {
|
||||||
viewModel.practiceMode = .flashcard
|
viewModel.practiceMode = .flashcard
|
||||||
|
|||||||
Reference in New Issue
Block a user