Add countdown till when the next flashcard is going to show up
This commit is contained in:
		| @@ -33,6 +33,7 @@ class DataController: ObservableObject { | ||||
|             flashcard.lastSeenOn  = [Date().addingTimeInterval([86400, 24000, 100000].randomElement()!)].randomElement()! | ||||
|             flashcard.shownCount = [0, 1, 2, 3, 4, 5].randomElement()! | ||||
|             flashcard.dateAdded = [Date(), Date().addingTimeInterval(-86400), Date().addingTimeInterval(-172800)].randomElement()! | ||||
| //            flashcard.calculatedNextRepetition = flashcard.lastSeenOn ?? Date() + TimeInterval(flashcard.nextSpacedRepetitionMilestone) | ||||
|         } | ||||
|         do { | ||||
|             try viewContext.save() | ||||
|   | ||||
| @@ -11,6 +11,25 @@ import CoreData | ||||
|  | ||||
| @objc(Flashcard) | ||||
| public class Flashcard: NSManagedObject { | ||||
|      | ||||
|     @objc dynamic var calculatedNextRepetition: Date { | ||||
|         if lastSeenOn != nil { | ||||
|             return lastSeenOn!.addSpacedRepetitionMilestone(milestone: self.getSpacedRepetitionMilestone()) | ||||
|         } else { | ||||
|             return Date() | ||||
|         } | ||||
|     } | ||||
|     override public class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> { | ||||
| //    public override func value(forKey key: String) -> Any? { | ||||
|         let keyPaths = super.keyPathsForValuesAffectingValue(forKey: key) | ||||
|         if key == "calculatedNextRepetition" { | ||||
|             return keyPaths.union(Set(["lastSeenOn", "nextSpacedRepetitionMilestone"])) | ||||
|         } else { | ||||
|             return keyPaths | ||||
|         } | ||||
|     } | ||||
|      | ||||
|  | ||||
|     enum SpacedRepetitionMilestoneEnum: Int64, CaseIterable { | ||||
|         case Now = 0 // starting value | ||||
|         case OneMinute = 60 // 60 * 1 | ||||
| @@ -31,28 +50,28 @@ public class Flashcard: NSManagedObject { | ||||
|         static func getNext(milestone: SpacedRepetitionMilestoneEnum?) -> SpacedRepetitionMilestoneEnum { | ||||
|             let sorted = SpacedRepetitionMilestoneEnum.allCasesSorted | ||||
|             if milestone == nil { | ||||
|                 return SpacedRepetitionMilestoneEnum.TenMinutes | ||||
|                 return .TenMinutes | ||||
|             } | ||||
|             let milestoneIndex = sorted.firstIndex(where: {$0.rawValue == milestone!.rawValue})! | ||||
|             if milestoneIndex < SpacedRepetitionMilestoneEnum.allCasesSorted.count { | ||||
|                 return sorted[milestoneIndex + 1] | ||||
|             } | ||||
|             return SpacedRepetitionMilestoneEnum.OneYear | ||||
|             return .OneYear | ||||
|         } | ||||
|          | ||||
|         static func getMilestoneFromInt(value: Int64) -> SpacedRepetitionMilestoneEnum { | ||||
|             return SpacedRepetitionMilestoneEnum.allCasesSorted.first(where: {$0.rawValue == value}) ?? SpacedRepetitionMilestoneEnum.Now | ||||
|             return .allCasesSorted.first(where: {$0.rawValue == value}) ?? .Now | ||||
|         } | ||||
|          | ||||
|     } | ||||
|      | ||||
|     public override func didChangeValue(forKey key: String) { | ||||
|         super.didChangeValue(forKey: key) | ||||
|         if key == "lastSeenOn" || key == "nextSpacedRepetitionMilestone" { | ||||
|             //            updateCalculatedNextRepetition() | ||||
|             calculatedNextRepetition = lastSeenOn ?? Date() + TimeInterval(nextSpacedRepetitionMilestone) | ||||
|         } | ||||
|     } | ||||
| //    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 { | ||||
|   | ||||
| @@ -24,8 +24,6 @@ extension Flashcard { | ||||
|     @NSManaged public var nextSpacedRepetitionMilestone: Int64 | ||||
|     @NSManaged public var shown: Bool | ||||
|     @NSManaged public var shownCount: Int64 | ||||
|     @NSManaged public var calculatedNextRepetition: Date? | ||||
|  | ||||
| } | ||||
|  | ||||
| extension Flashcard : Identifiable { | ||||
|   | ||||
| @@ -0,0 +1,13 @@ | ||||
| <?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="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> | ||||
| @@ -21,19 +21,18 @@ struct AnkiView: View { | ||||
|         ]), | ||||
|         NSPredicate(format: "lastSeenOn == nil") | ||||
|     ])) 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> | ||||
|      | ||||
|     @State private var timeRemaining = 10 | ||||
|     let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() | ||||
|     @State var sortedFlashcards: [Flashcard] = [] | ||||
|      | ||||
|     @Environment(\.colorScheme) var colorScheme | ||||
|  | ||||
|      | ||||
|     // get the most recent flashcard | ||||
|     @FetchRequest(sortDescriptors: [], | ||||
|           predicate: NSPredicate(format: "%K != nil", "lastSeenOn")) var soonestFlashcard: FetchedResults<Flashcard> | ||||
|  | ||||
|      | ||||
|     @State var showDescription = false | ||||
|      | ||||
| @@ -86,62 +85,32 @@ struct AnkiView: View { | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             // TODO: Add countdown to the next available word | ||||
|             VStack{ | ||||
|                 if !soonestFlashcard.isEmpty { | ||||
|                     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") | ||||
|             if !soonestFlashcard.isEmpty { | ||||
|                 Text("Next flashcard in: \(timeRemaining.convertDurationSecondsToCountdown())") | ||||
|                     .foregroundStyle(.black) | ||||
|                     .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!)) | ||||
| //                } | ||||
|                     .padding(.vertical, 50) | ||||
|                     .padding(.horizontal) | ||||
|                     .background(.gray.opacity(0.3)) | ||||
|                     .clipShape(.buttonBorder) | ||||
|                     .onAppear { | ||||
|                         if !soonestFlashcard.isEmpty { | ||||
|                             sortedFlashcards = soonestFlashcard.sorted(by: { | ||||
|                                 ($0.lastSeenOn!.addSpacedRepetitionMilestone(milestone:$0.getSpacedRepetitionMilestone()).timeIntervalSinceNow) < ($1.lastSeenOn!.addSpacedRepetitionMilestone(milestone: $1.getSpacedRepetitionMilestone()).timeIntervalSinceNow) | ||||
|                             }) | ||||
|                             timeRemaining = Int(sortedFlashcards.first!.lastSeenOn!.addSpacedRepetitionMilestone(milestone:sortedFlashcards.first!.getSpacedRepetitionMilestone()).timeIntervalSinceNow) | ||||
|                         } | ||||
|                     } | ||||
|                     .onReceive(timer) { time in | ||||
|                         if timeRemaining > 0 { | ||||
|                             timeRemaining -= 1 | ||||
|                         } | ||||
|                     } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     @State private var timeRemaining = 10 | ||||
|     let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() | ||||
| } | ||||
|  | ||||
| #Preview { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user