Countdown not working yet

This commit is contained in:
2024-04-11 23:42:14 +02:00
parent df31e34a54
commit 7625f62c85
7 changed files with 138 additions and 51 deletions

View File

@@ -19,7 +19,7 @@
6CEF7F7D2BC457E600E205F6 /* DataController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEF7F7C2BC457E600E205F6 /* DataController.swift */; }; 6CEF7F7D2BC457E600E205F6 /* DataController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEF7F7C2BC457E600E205F6 /* DataController.swift */; };
6CEF7F812BC4694900E205F6 /* WordAXCD.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 6CEF7F7F2BC4694900E205F6 /* WordAXCD.xcdatamodeld */; }; 6CEF7F812BC4694900E205F6 /* WordAXCD.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 6CEF7F7F2BC4694900E205F6 /* WordAXCD.xcdatamodeld */; };
6CEF7F842BC46B5900E205F6 /* Flashcard+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEF7F822BC46B5900E205F6 /* Flashcard+CoreDataClass.swift */; }; 6CEF7F842BC46B5900E205F6 /* Flashcard+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEF7F822BC46B5900E205F6 /* Flashcard+CoreDataClass.swift */; };
6CEF7F9E2BC6B4F100E205F6 /* Flashcard+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEF7F9C2BC6B4F100E205F6 /* Flashcard+CoreDataProperties.swift */; }; 6CEF7FA32BC88F6000E205F6 /* Flashcard+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEF7FA12BC88F6000E205F6 /* Flashcard+CoreDataProperties.swift */; };
6CF439522B83541D004C3543 /* WordAXApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF439512B83541D004C3543 /* WordAXApp.swift */; }; 6CF439522B83541D004C3543 /* WordAXApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF439512B83541D004C3543 /* WordAXApp.swift */; };
6CF439542B83541D004C3543 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF439532B83541D004C3543 /* MainView.swift */; }; 6CF439542B83541D004C3543 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF439532B83541D004C3543 /* MainView.swift */; };
6CF439562B83541E004C3543 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6CF439552B83541E004C3543 /* Assets.xcassets */; }; 6CF439562B83541E004C3543 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6CF439552B83541E004C3543 /* Assets.xcassets */; };
@@ -40,7 +40,8 @@
6CEF7F802BC4694900E205F6 /* WordAX.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = WordAX.xcdatamodel; sourceTree = "<group>"; }; 6CEF7F802BC4694900E205F6 /* WordAX.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = WordAX.xcdatamodel; sourceTree = "<group>"; };
6CEF7F822BC46B5900E205F6 /* Flashcard+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Flashcard+CoreDataClass.swift"; sourceTree = "<group>"; }; 6CEF7F822BC46B5900E205F6 /* Flashcard+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Flashcard+CoreDataClass.swift"; sourceTree = "<group>"; };
6CEF7F962BC6B45F00E205F6 /* WordAX0.0.1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = WordAX0.0.1.xcdatamodel; sourceTree = "<group>"; }; 6CEF7F962BC6B45F00E205F6 /* WordAX0.0.1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = WordAX0.0.1.xcdatamodel; sourceTree = "<group>"; };
6CEF7F9C2BC6B4F100E205F6 /* Flashcard+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Flashcard+CoreDataProperties.swift"; path = "../../Flashcard+CoreDataProperties.swift"; sourceTree = "<group>"; }; 6CEF7F9F2BC88F3900E205F6 /* WordAX0.0.2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = WordAX0.0.2.xcdatamodel; sourceTree = "<group>"; };
6CEF7FA12BC88F6000E205F6 /* Flashcard+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Flashcard+CoreDataProperties.swift"; sourceTree = "<group>"; };
6CF4394E2B83541D004C3543 /* WordAX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WordAX.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6CF4394E2B83541D004C3543 /* WordAX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WordAX.app; sourceTree = BUILT_PRODUCTS_DIR; };
6CF439512B83541D004C3543 /* WordAXApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordAXApp.swift; sourceTree = "<group>"; }; 6CF439512B83541D004C3543 /* WordAXApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordAXApp.swift; sourceTree = "<group>"; };
6CF439532B83541D004C3543 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; }; 6CF439532B83541D004C3543 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
@@ -80,7 +81,7 @@
6CEF7F7C2BC457E600E205F6 /* DataController.swift */, 6CEF7F7C2BC457E600E205F6 /* DataController.swift */,
6CEF7F7F2BC4694900E205F6 /* WordAXCD.xcdatamodeld */, 6CEF7F7F2BC4694900E205F6 /* WordAXCD.xcdatamodeld */,
6CEF7F822BC46B5900E205F6 /* Flashcard+CoreDataClass.swift */, 6CEF7F822BC46B5900E205F6 /* Flashcard+CoreDataClass.swift */,
6CEF7F9C2BC6B4F100E205F6 /* Flashcard+CoreDataProperties.swift */, 6CEF7FA12BC88F6000E205F6 /* Flashcard+CoreDataProperties.swift */,
); );
path = Model; path = Model;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -197,7 +198,7 @@
6CF439542B83541D004C3543 /* MainView.swift in Sources */, 6CF439542B83541D004C3543 /* MainView.swift in Sources */,
6C8185082B8B523E0033CF46 /* NextRepetitionButtonView.swift in Sources */, 6C8185082B8B523E0033CF46 /* NextRepetitionButtonView.swift in Sources */,
6C8185062B8A537F0033CF46 /* FlashCardView.swift in Sources */, 6C8185062B8A537F0033CF46 /* FlashCardView.swift in Sources */,
6CEF7F9E2BC6B4F100E205F6 /* Flashcard+CoreDataProperties.swift in Sources */, 6CEF7FA32BC88F6000E205F6 /* Flashcard+CoreDataProperties.swift in Sources */,
6C81850A2B8BA5740033CF46 /* FlashCardListView.swift in Sources */, 6C81850A2B8BA5740033CF46 /* FlashCardListView.swift in Sources */,
6C8185002B88C9660033CF46 /* WordAXModelView.swift in Sources */, 6C8185002B88C9660033CF46 /* WordAXModelView.swift in Sources */,
6C8185042B88CA210033CF46 /* AnkiView.swift in Sources */, 6C8185042B88CA210033CF46 /* AnkiView.swift in Sources */,
@@ -418,6 +419,7 @@
6CEF7F7F2BC4694900E205F6 /* WordAXCD.xcdatamodeld */ = { 6CEF7F7F2BC4694900E205F6 /* WordAXCD.xcdatamodeld */ = {
isa = XCVersionGroup; isa = XCVersionGroup;
children = ( children = (
6CEF7F9F2BC88F3900E205F6 /* WordAX0.0.2.xcdatamodel */,
6CEF7F962BC6B45F00E205F6 /* WordAX0.0.1.xcdatamodel */, 6CEF7F962BC6B45F00E205F6 /* WordAX0.0.1.xcdatamodel */,
6CEF7F802BC4694900E205F6 /* WordAX.xcdatamodel */, 6CEF7F802BC4694900E205F6 /* WordAX.xcdatamodel */,
); );

View File

@@ -29,8 +29,8 @@ class DataController: ObservableObject {
"This is a very short description", "This is a very short description",
"This is a medium length description that should be long enough to cover all cases" "This is a medium length description that should be long enough to cover all cases"
].randomElement()! ].randomElement()!
flashcard.nextSpacedRepetitionMilestone = SpacedRepetitionMilestoneEnum.allCases.randomElement()!.rawValue flashcard.nextSpacedRepetitionMilestone = SpacedRepetitionMilestoneEnum.OneYear.rawValue
flashcard.lastSeenOn = [nil, Date(), Date().addingTimeInterval([-86400, -24000, -100000].randomElement()!)].randomElement()! flashcard.lastSeenOn = [Date().addingTimeInterval([86400, 24000, 100000].randomElement()!)].randomElement()!
flashcard.shownCount = [0, 1, 2, 3, 4, 5].randomElement()! flashcard.shownCount = [0, 1, 2, 3, 4, 5].randomElement()!
flashcard.dateAdded = [Date(), Date().addingTimeInterval(-86400), Date().addingTimeInterval(-172800)].randomElement()! flashcard.dateAdded = [Date(), Date().addingTimeInterval(-86400), Date().addingTimeInterval(-172800)].randomElement()!
} }
@@ -70,43 +70,6 @@ class DataController: ObservableObject {
return [] return []
} }
} }
// public func getFlashCardsToDisplay() -> Flashcard? {
// let flashcards = self.getAllFlashcards()
//
// if flashcards.count > 0 {
// let notShownFlashCards = flashcards.filter({!$0.shown})
// // if today is the date they're supposed to be shown
//
// let displayToday = flashcards.filter({
// $0.lastSeenOn != nil &&
// $0.lastSeenOn!.addSpacedRepetitionMilestone(
// milestone: SpacedRepetitionMilestoneEnum.getMilestoneFromInt(
// value: $0.nextSpacedRepetitionMilestone))
// .isBeforeTodayOrToday()
// })
// if displayToday.count > 0 {
// return displayToday.first!
// }
//
//// let shownWords = words.filter({ $0.shown })
//// if shownWords.count == 0 {
// if notShownFlashCards.count == 0 {
// return nil
// }
// return notShownFlashCards.sorted(by: {$0.id < $1.id}).first
//// }
// // if today is the day to show a new word
//// let settings = model.settings
//// if shownWords.count == 0 ||
//// settings.lastShownNew == nil ||
//// settings.lastShownNew!.addFrequency(frequency: settings.frequency).isAfterToday() {
//// return words.first!
//// }
// }
// // otherwise show nothing
// return nil
// }
} }
@@ -133,3 +96,32 @@ extension Int64 {
return result return result
} }
} }
extension Int {
func convertDurationSecondsToCountdown() -> String {
var result = ""
// Separate into days, hours, minutes and seconds and take the largest one
let days: Int = self / 86400
let hours: Int = self / 60 / 60 % 60
let minutes: Int = self / 60 % 60
let seconds: Int = self % 60
if days > 0 {
result += "\(days)d"
}
if hours > 0 {
result += " \(hours)h"
}
if minutes > 0 {
result += " \(minutes)min"
}
if seconds > 0 {
result += " \(seconds)s"
}
// else {
// result = "\(self)"
// }
return result
}
}

View File

@@ -39,14 +39,28 @@ public class Flashcard: NSManagedObject {
} }
return SpacedRepetitionMilestoneEnum.OneYear return SpacedRepetitionMilestoneEnum.OneYear
} }
static func getMilestoneFromInt(value: Int64) -> SpacedRepetitionMilestoneEnum { static func getMilestoneFromInt(value: Int64) -> SpacedRepetitionMilestoneEnum {
return SpacedRepetitionMilestoneEnum.allCasesSorted.first(where: {$0.rawValue == value}) ?? SpacedRepetitionMilestoneEnum.Now return SpacedRepetitionMilestoneEnum.allCasesSorted.first(where: {$0.rawValue == value}) ?? SpacedRepetitionMilestoneEnum.Now
} }
} }
public override func didChangeValue(forKey key: String) {
super.didChangeValue(forKey: key)
if key == "lastSeenOn" || key == "nextSpacedRepetitionMilestone" {
// updateCalculatedNextRepetition()
calculatedNextRepetition = lastSeenOn ?? Date() + TimeInterval(nextSpacedRepetitionMilestone)
}
}
// func updateCalculatedNextRepetition() {
// if let lastSeen = lastSeenOn {
// calculatedNextRepetition = Calendar.current.date(byAdding: .day, value: Int(nextSpacedRepetitionMilestone), to: lastSeen)
// }
// }
func getSpacedRepetitionMilestone() -> SpacedRepetitionMilestoneEnum { func getSpacedRepetitionMilestone() -> SpacedRepetitionMilestoneEnum {
return SpacedRepetitionMilestoneEnum.getMilestoneFromInt(value: self.nextSpacedRepetitionMilestone) return SpacedRepetitionMilestoneEnum.getMilestoneFromInt(value: self.nextSpacedRepetitionMilestone)
} }

View File

@@ -2,7 +2,7 @@
// Flashcard+CoreDataProperties.swift // Flashcard+CoreDataProperties.swift
// WordAX // WordAX
// //
// Created by Oliver Hnát on 10.04.2024. // Created by Oliver Hnát on 11.04.2024.
// //
// //
@@ -22,7 +22,9 @@ extension Flashcard {
@NSManaged public var lastSeenOn: Date? @NSManaged public var lastSeenOn: Date?
@NSManaged public var name: String? @NSManaged public var name: String?
@NSManaged public var nextSpacedRepetitionMilestone: Int64 @NSManaged public var nextSpacedRepetitionMilestone: Int64
@NSManaged public var shown: Bool
@NSManaged public var shownCount: Int64 @NSManaged public var shownCount: Int64
@NSManaged public var calculatedNextRepetition: Date?
} }

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22757" systemVersion="23D60" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithSwiftData="YES" userDefinedModelVersionIdentifier="">
<entity name="Flashcard" representedClassName="Flashcard" syncable="YES">
<attribute name="calculatedNextRepetition" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="dateAdded" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="desc" optional="YES" attributeType="String"/>
<attribute name="id" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
<attribute name="lastSeenOn" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="name" optional="YES" attributeType="String"/>
<attribute name="nextSpacedRepetitionMilestone" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="shown" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="shownCount" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
</entity>
</model>

View File

@@ -21,6 +21,19 @@ struct AnkiView: View {
]), ]),
NSPredicate(format: "lastSeenOn == nil") NSPredicate(format: "lastSeenOn == nil")
])) var flashcards: FetchedResults<Flashcard> ])) var flashcards: FetchedResults<Flashcard>
@FetchRequest(
sortDescriptors: [
NSSortDescriptor(key: "calculatedNextRepetition", ascending: false)
],
predicate:
NSCompoundPredicate(type: .and, subpredicates: [
NSPredicate(format: "%K != nil", "lastSeenOn"),
NSPredicate(
format: "lastSeenOn + nextSpacedRepetitionMilestone > %@", Date() as CVarArg)
]
)) var soonestFlashcard: FetchedResults<Flashcard>
// get the most recent flashcard
@State var showDescription = false @State var showDescription = false
@@ -73,12 +86,62 @@ struct AnkiView: View {
} }
} }
} else { } else {
Text("There are currently no words to display") // TODO: Add countdown to the next available word
.padding() VStack{
.background(.yellow) if !soonestFlashcard.isEmpty {
.clipShape(.buttonBorder) Text("Time: \(timeRemaining.convertDurationSecondsToCountdown())")
.foregroundStyle(.white)
.padding(.horizontal, 20)
.padding(.vertical, 5)
.background(.black.opacity(0.75))
.clipShape(.capsule)
}
// if !soonestFlashcard.isEmpty {
// Text("HERE")
// Text("\(soonestFlashcard.first!.lastSeenOn!.addingTimeInterval(TimeInterval(soonestFlashcard.first!.nextSpacedRepetitionMilestone)).timeIntervalSince(Date()))")
// Text("\(Int64(timeRemaining))")
// Text("\(soonestFlashcard.first!.lastSeenOn!)")
// Text("\(soonestFlashcard.first!.getSpacedRepetitionMilestone().rawValue)")
// Text("\(soonestFlashcard.first!.lastSeenOn!.addSpacedRepetitionMilestone(milestone: soonestFlashcard.first!.getSpacedRepetitionMilestone()))")
// }
// if soonestFlashcard.first! {
// Text("HERE")
// }
Text("There are currently no words to display")
.padding()
.background(.yellow)
.clipShape(.buttonBorder)
}
.onAppear {
if !soonestFlashcard.isEmpty {
var lastSeen = soonestFlashcard.first!.lastSeenOn!
var showIn = soonestFlashcard.first!.getSpacedRepetitionMilestone()
var show = lastSeen.addSpacedRepetitionMilestone(milestone: showIn)
timeRemaining = Int(show.timeIntervalSinceNow)
}
}
.onReceive(timer) { time in
if !soonestFlashcard.isEmpty {
var lastSeen = soonestFlashcard.first!.lastSeenOn!
var showIn = soonestFlashcard.first!.getSpacedRepetitionMilestone()
var show = lastSeen.addSpacedRepetitionMilestone(milestone: showIn)
timeRemaining = Int(show.timeIntervalSinceNow)
}
// timeRemaining = Int(soonestFlashcard.first!.lastSeenOn!.addSpacedRepetitionMilestone(milestone: soonestFlashcard.first!.getSpacedRepetitionMilestone()).timeIntervalSince(Date()))
// timeRemaining = Int(soonestFlashcard.first!.lastSeenOn!.addingTimeInterval(TimeInterval(soonestFlashcard.first!.nextSpacedRepetitionMilestone)).timeIntervalSince(Date()))
// if timeRemaining > 0 {
// timeRemaining -= 1
// }
// if !soonestFlashcard.isEmpty {
// timeRemaining = Int(Date().timeIntervalSince(soonestFlashcard.first!.lastSeenOn!))
// }
}
} }
} }
@State private var timeRemaining = 10
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
} }
#Preview { #Preview {

View File

@@ -68,7 +68,7 @@ extension Date {
if milestone == nil { if milestone == nil {
return self return self
} }
return self.addingTimeInterval(TimeInterval(milestone!.rawValue * 24 * 60 * 60)) return self.addingTimeInterval(TimeInterval(milestone!.rawValue))
} }
func isAfterToday() -> Bool { func isAfterToday() -> Bool {