Add countdown till when the next flashcard is going to show up
This commit is contained in:
		| @@ -20,6 +20,7 @@ | |||||||
| 		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 */; }; | ||||||
| 		6CEF7FA32BC88F6000E205F6 /* Flashcard+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEF7FA12BC88F6000E205F6 /* Flashcard+CoreDataProperties.swift */; }; | 		6CEF7FA32BC88F6000E205F6 /* Flashcard+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEF7FA12BC88F6000E205F6 /* Flashcard+CoreDataProperties.swift */; }; | ||||||
|  | 		6CEF7FA62BC96F2B00E205F6 /* ButtonHStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEF7FA52BC96F2B00E205F6 /* ButtonHStackView.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 */; }; | ||||||
| @@ -42,6 +43,8 @@ | |||||||
| 		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>"; }; | ||||||
| 		6CEF7F9F2BC88F3900E205F6 /* WordAX0.0.2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = WordAX0.0.2.xcdatamodel; 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>"; }; | 		6CEF7FA12BC88F6000E205F6 /* Flashcard+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Flashcard+CoreDataProperties.swift"; sourceTree = "<group>"; }; | ||||||
|  | 		6CEF7FA42BC95F0300E205F6 /* WordAX0.0.3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = WordAX0.0.3.xcdatamodel; sourceTree = "<group>"; }; | ||||||
|  | 		6CEF7FA52BC96F2B00E205F6 /* ButtonHStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonHStackView.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>"; }; | ||||||
| @@ -70,6 +73,7 @@ | |||||||
| 				6C81850B2B8BA6BC0033CF46 /* FlashCardListRowView.swift */, | 				6C81850B2B8BA6BC0033CF46 /* FlashCardListRowView.swift */, | ||||||
| 				6C8185012B88C9FB0033CF46 /* SettingsView.swift */, | 				6C8185012B88C9FB0033CF46 /* SettingsView.swift */, | ||||||
| 				6C8185032B88CA210033CF46 /* AnkiView.swift */, | 				6C8185032B88CA210033CF46 /* AnkiView.swift */, | ||||||
|  | 				6CEF7FA52BC96F2B00E205F6 /* ButtonHStackView.swift */, | ||||||
| 				6C8185052B8A537F0033CF46 /* FlashCardView.swift */, | 				6C8185052B8A537F0033CF46 /* FlashCardView.swift */, | ||||||
| 			); | 			); | ||||||
| 			path = Views; | 			path = Views; | ||||||
| @@ -200,6 +204,7 @@ | |||||||
| 				6C8185062B8A537F0033CF46 /* FlashCardView.swift in Sources */, | 				6C8185062B8A537F0033CF46 /* FlashCardView.swift in Sources */, | ||||||
| 				6CEF7FA32BC88F6000E205F6 /* Flashcard+CoreDataProperties.swift in Sources */, | 				6CEF7FA32BC88F6000E205F6 /* Flashcard+CoreDataProperties.swift in Sources */, | ||||||
| 				6C81850A2B8BA5740033CF46 /* FlashCardListView.swift in Sources */, | 				6C81850A2B8BA5740033CF46 /* FlashCardListView.swift in Sources */, | ||||||
|  | 				6CEF7FA62BC96F2B00E205F6 /* ButtonHStackView.swift in Sources */, | ||||||
| 				6C8185002B88C9660033CF46 /* WordAXModelView.swift in Sources */, | 				6C8185002B88C9660033CF46 /* WordAXModelView.swift in Sources */, | ||||||
| 				6C8185042B88CA210033CF46 /* AnkiView.swift in Sources */, | 				6C8185042B88CA210033CF46 /* AnkiView.swift in Sources */, | ||||||
| 				6CEF7F7D2BC457E600E205F6 /* DataController.swift in Sources */, | 				6CEF7F7D2BC457E600E205F6 /* DataController.swift in Sources */, | ||||||
| @@ -419,6 +424,7 @@ | |||||||
| 		6CEF7F7F2BC4694900E205F6 /* WordAXCD.xcdatamodeld */ = { | 		6CEF7F7F2BC4694900E205F6 /* WordAXCD.xcdatamodeld */ = { | ||||||
| 			isa = XCVersionGroup; | 			isa = XCVersionGroup; | ||||||
| 			children = ( | 			children = ( | ||||||
|  | 				6CEF7FA42BC95F0300E205F6 /* WordAX0.0.3.xcdatamodel */, | ||||||
| 				6CEF7F9F2BC88F3900E205F6 /* WordAX0.0.2.xcdatamodel */, | 				6CEF7F9F2BC88F3900E205F6 /* WordAX0.0.2.xcdatamodel */, | ||||||
| 				6CEF7F962BC6B45F00E205F6 /* WordAX0.0.1.xcdatamodel */, | 				6CEF7F962BC6B45F00E205F6 /* WordAX0.0.1.xcdatamodel */, | ||||||
| 				6CEF7F802BC4694900E205F6 /* WordAX.xcdatamodel */, | 				6CEF7F802BC4694900E205F6 /* WordAX.xcdatamodel */, | ||||||
|   | |||||||
| @@ -33,6 +33,7 @@ class DataController: ObservableObject { | |||||||
|             flashcard.lastSeenOn  = [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()! | ||||||
|  | //            flashcard.calculatedNextRepetition = flashcard.lastSeenOn ?? Date() + TimeInterval(flashcard.nextSpacedRepetitionMilestone) | ||||||
|         } |         } | ||||||
|         do { |         do { | ||||||
|             try viewContext.save() |             try viewContext.save() | ||||||
|   | |||||||
| @@ -11,6 +11,25 @@ import CoreData | |||||||
|  |  | ||||||
| @objc(Flashcard) | @objc(Flashcard) | ||||||
| public class Flashcard: NSManagedObject { | 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 { |     enum SpacedRepetitionMilestoneEnum: Int64, CaseIterable { | ||||||
|         case Now = 0 // starting value |         case Now = 0 // starting value | ||||||
|         case OneMinute = 60 // 60 * 1 |         case OneMinute = 60 // 60 * 1 | ||||||
| @@ -31,28 +50,28 @@ public class Flashcard: NSManagedObject { | |||||||
|         static func getNext(milestone: SpacedRepetitionMilestoneEnum?) -> SpacedRepetitionMilestoneEnum { |         static func getNext(milestone: SpacedRepetitionMilestoneEnum?) -> SpacedRepetitionMilestoneEnum { | ||||||
|             let sorted = SpacedRepetitionMilestoneEnum.allCasesSorted |             let sorted = SpacedRepetitionMilestoneEnum.allCasesSorted | ||||||
|             if milestone == nil { |             if milestone == nil { | ||||||
|                 return SpacedRepetitionMilestoneEnum.TenMinutes |                 return .TenMinutes | ||||||
|             } |             } | ||||||
|             let milestoneIndex = sorted.firstIndex(where: {$0.rawValue == milestone!.rawValue})! |             let milestoneIndex = sorted.firstIndex(where: {$0.rawValue == milestone!.rawValue})! | ||||||
|             if milestoneIndex < SpacedRepetitionMilestoneEnum.allCasesSorted.count { |             if milestoneIndex < SpacedRepetitionMilestoneEnum.allCasesSorted.count { | ||||||
|                 return sorted[milestoneIndex + 1] |                 return sorted[milestoneIndex + 1] | ||||||
|             } |             } | ||||||
|             return SpacedRepetitionMilestoneEnum.OneYear |             return .OneYear | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         static func getMilestoneFromInt(value: Int64) -> SpacedRepetitionMilestoneEnum { |         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) { | //    public override func didChangeValue(forKey key: String) { | ||||||
|         super.didChangeValue(forKey: key) | //        super.didChangeValue(forKey: key) | ||||||
|         if key == "lastSeenOn" || key == "nextSpacedRepetitionMilestone" { | //        if key == "lastSeenOn" || key == "nextSpacedRepetitionMilestone" { | ||||||
|             //            updateCalculatedNextRepetition() | //            //            updateCalculatedNextRepetition() | ||||||
|             calculatedNextRepetition = lastSeenOn ?? Date() + TimeInterval(nextSpacedRepetitionMilestone) | //            calculatedNextRepetition = lastSeenOn ?? Date() + TimeInterval(nextSpacedRepetitionMilestone) | ||||||
|         } | //        } | ||||||
|     } | //    } | ||||||
|      |      | ||||||
|     //    func updateCalculatedNextRepetition() { |     //    func updateCalculatedNextRepetition() { | ||||||
|     //        if let lastSeen = lastSeenOn { |     //        if let lastSeen = lastSeenOn { | ||||||
|   | |||||||
| @@ -24,8 +24,6 @@ extension Flashcard { | |||||||
|     @NSManaged public var nextSpacedRepetitionMilestone: Int64 |     @NSManaged public var nextSpacedRepetitionMilestone: Int64 | ||||||
|     @NSManaged public var shown: Bool |     @NSManaged public var shown: Bool | ||||||
|     @NSManaged public var shownCount: Int64 |     @NSManaged public var shownCount: Int64 | ||||||
|     @NSManaged public var calculatedNextRepetition: Date? |  | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| extension Flashcard : Identifiable { | 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") |         NSPredicate(format: "lastSeenOn == nil") | ||||||
|     ])) var flashcards: FetchedResults<Flashcard> |     ])) var flashcards: FetchedResults<Flashcard> | ||||||
|     @FetchRequest( |      | ||||||
|         sortDescriptors: [ |     @State private var timeRemaining = 10 | ||||||
|             NSSortDescriptor(key: "calculatedNextRepetition", ascending: false) |     let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() | ||||||
|         ], |     @State var sortedFlashcards: [Flashcard] = [] | ||||||
|           predicate: |      | ||||||
|             NSCompoundPredicate(type: .and, subpredicates: [ |     @Environment(\.colorScheme) var colorScheme | ||||||
|                 NSPredicate(format: "%K != nil", "lastSeenOn"), |  | ||||||
|             NSPredicate( |  | ||||||
|                 format: "lastSeenOn + nextSpacedRepetitionMilestone > %@", Date() as CVarArg) |  | ||||||
|                 ] |  | ||||||
|     )) var soonestFlashcard: FetchedResults<Flashcard> |  | ||||||
|      |      | ||||||
|     // get the most recent flashcard |     // get the most recent flashcard | ||||||
|  |     @FetchRequest(sortDescriptors: [], | ||||||
|  |           predicate: NSPredicate(format: "%K != nil", "lastSeenOn")) var soonestFlashcard: FetchedResults<Flashcard> | ||||||
|  |  | ||||||
|      |      | ||||||
|     @State var showDescription = false |     @State var showDescription = false | ||||||
|      |      | ||||||
| @@ -86,62 +85,32 @@ struct AnkiView: View { | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             // TODO: Add countdown to the next available word |  | ||||||
|             VStack{ |  | ||||||
|             if !soonestFlashcard.isEmpty { |             if !soonestFlashcard.isEmpty { | ||||||
|                     Text("Time: \(timeRemaining.convertDurationSecondsToCountdown())") |                 Text("Next flashcard in: \(timeRemaining.convertDurationSecondsToCountdown())") | ||||||
|                         .foregroundStyle(.white) |                     .foregroundStyle(.black) | ||||||
|                         .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() |                     .padding() | ||||||
|                     .background(.yellow) |                     .background(.yellow) | ||||||
|                     .clipShape(.buttonBorder) |                     .clipShape(.buttonBorder) | ||||||
|             } |                     .padding(.vertical, 50) | ||||||
|  |                     .padding(.horizontal) | ||||||
|  |                     .background(.gray.opacity(0.3)) | ||||||
|  |                     .clipShape(.buttonBorder) | ||||||
|                     .onAppear { |                     .onAppear { | ||||||
|                         if !soonestFlashcard.isEmpty { |                         if !soonestFlashcard.isEmpty { | ||||||
|                     var lastSeen = soonestFlashcard.first!.lastSeenOn! |                             sortedFlashcards = soonestFlashcard.sorted(by: { | ||||||
|                     var showIn = soonestFlashcard.first!.getSpacedRepetitionMilestone() |                                 ($0.lastSeenOn!.addSpacedRepetitionMilestone(milestone:$0.getSpacedRepetitionMilestone()).timeIntervalSinceNow) < ($1.lastSeenOn!.addSpacedRepetitionMilestone(milestone: $1.getSpacedRepetitionMilestone()).timeIntervalSinceNow) | ||||||
|                     var show = lastSeen.addSpacedRepetitionMilestone(milestone: showIn) |                             }) | ||||||
|                     timeRemaining = Int(show.timeIntervalSinceNow) |                             timeRemaining = Int(sortedFlashcards.first!.lastSeenOn!.addSpacedRepetitionMilestone(milestone:sortedFlashcards.first!.getSpacedRepetitionMilestone()).timeIntervalSinceNow) | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                     .onReceive(timer) { time in |                     .onReceive(timer) { time in | ||||||
|                 if !soonestFlashcard.isEmpty { |                         if timeRemaining > 0 { | ||||||
|                     var lastSeen = soonestFlashcard.first!.lastSeenOn! |                             timeRemaining -= 1 | ||||||
|                     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 { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user