fix(anki): make quality of life improvements
This commit is contained in:
@@ -22,6 +22,8 @@ struct FlashcardHomeView: View {
|
||||
@State private var showingSession = false
|
||||
@State private var showingStats = false
|
||||
@State private var session: FlashcardSession?
|
||||
@State private var currentTime = Date()
|
||||
@State private var timer: Timer?
|
||||
|
||||
private var statistics: FlashcardStatistics {
|
||||
let tags = showAllTags ? nil : Array(selectedTagObjects)
|
||||
@@ -40,6 +42,11 @@ struct FlashcardHomeView: View {
|
||||
return SpacedRepetitionEngine.getDueCards(in: viewContext, tags: tags)
|
||||
}
|
||||
|
||||
private var nextCardDate: Date? {
|
||||
let tags = showAllTags ? nil : Array(selectedTagObjects)
|
||||
return SpacedRepetitionEngine.getNextCardDate(in: viewContext, tags: tags)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
ScrollView {
|
||||
@@ -183,16 +190,30 @@ struct FlashcardHomeView: View {
|
||||
.font(.title3)
|
||||
.fontWeight(.semibold)
|
||||
|
||||
Text("No cards due for review right now")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
if statistics.totalCards == 0 {
|
||||
if let nextDate = nextCardDate, nextDate > currentTime {
|
||||
Text("Next card available in")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
Text(timeUntilNextCard(nextDate: nextDate))
|
||||
.font(.title2)
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(.blue)
|
||||
.monospacedDigit()
|
||||
} else if statistics.totalCards == 0 {
|
||||
Text("No cards due for review right now")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
Text("💡 Tip: Cards need both German word AND English translation to appear in flashcards")
|
||||
.font(.caption2)
|
||||
.foregroundColor(.orange)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.top, 4)
|
||||
} else {
|
||||
Text("No cards due for review right now")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
@@ -218,6 +239,12 @@ struct FlashcardHomeView: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle("Flashcards")
|
||||
.onAppear {
|
||||
startTimer()
|
||||
}
|
||||
.onDisappear {
|
||||
stopTimer()
|
||||
}
|
||||
.sheet(isPresented: $showingSession) {
|
||||
if let session = session {
|
||||
FlashcardSessionView(session: session)
|
||||
@@ -244,6 +271,35 @@ struct FlashcardHomeView: View {
|
||||
session = FlashcardSession(cards: cards, context: viewContext)
|
||||
showingSession = true
|
||||
}
|
||||
|
||||
private func startTimer() {
|
||||
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
|
||||
currentTime = Date()
|
||||
}
|
||||
}
|
||||
|
||||
private func stopTimer() {
|
||||
timer?.invalidate()
|
||||
timer = nil
|
||||
}
|
||||
|
||||
private func timeUntilNextCard(nextDate: Date) -> String {
|
||||
let timeInterval = nextDate.timeIntervalSince(currentTime)
|
||||
|
||||
guard timeInterval > 0 else {
|
||||
return "Available now!"
|
||||
}
|
||||
|
||||
let hours = Int(timeInterval) / 3600
|
||||
let minutes = (Int(timeInterval) % 3600) / 60
|
||||
let seconds = Int(timeInterval) % 60
|
||||
|
||||
if hours > 0 {
|
||||
return String(format: "%02d:%02d:%02d", hours, minutes, seconds)
|
||||
} else {
|
||||
return String(format: "%02d:%02d", minutes, seconds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Stat Card
|
||||
|
||||
@@ -163,6 +163,34 @@ class SpacedRepetitionEngine {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the date when the next card will become available
|
||||
static func getNextCardDate(
|
||||
in context: NSManagedObjectContext,
|
||||
tags: [Tag]? = nil
|
||||
) -> Date? {
|
||||
let fetchRequest: NSFetchRequest<VocabularyEntry> = VocabularyEntry.fetchRequest()
|
||||
|
||||
// Filter by tags if provided
|
||||
if let tags = tags, !tags.isEmpty {
|
||||
fetchRequest.predicate = NSPredicate(format: "ANY tags IN %@", tags)
|
||||
}
|
||||
|
||||
do {
|
||||
let allEntries = try context.fetch(fetchRequest)
|
||||
|
||||
// Get all flashcard-ready entries that are NOT yet due
|
||||
let futureCards = allEntries.filter { entry in
|
||||
entry.isFlashcardReady && !entry.isDueForReview && entry.nextReviewDate != nil
|
||||
}
|
||||
|
||||
// Find the earliest next review date
|
||||
return futureCards.compactMap { $0.nextReviewDate }.min()
|
||||
} catch {
|
||||
print("Error fetching next card date: \(error)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Get overall statistics for flashcards
|
||||
static func getStatistics(
|
||||
in context: NSManagedObjectContext,
|
||||
|
||||
Reference in New Issue
Block a user