浏览代码

Clean up and last TOTP checking

codebymini 11 月之前
父节点
当前提交
07ae3f7b3c

+ 6 - 2
LoopFollow.xcodeproj/project.pbxproj

@@ -10,9 +10,10 @@
 		3F1335F351590E573D8E6962 /* Pods_LoopFollow.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7D55B42A22051DAD69E89D0 /* Pods_LoopFollow.framework */; };
 		654132E72E19EA7E00BDBE08 /* SimpleQRCodeScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 654132E62E19EA7E00BDBE08 /* SimpleQRCodeScannerView.swift */; };
 		654132EA2E19F24800BDBE08 /* TOTPGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 654132E92E19F24800BDBE08 /* TOTPGenerator.swift */; };
-		6541341C2E1DC28000BDBE08 /* DateExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6541341B2E1DC28000BDBE08 /* DateExtensions.swift */; };
 		654134182E1DC09700BDBE08 /* OverridePresetsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 654134172E1DC09700BDBE08 /* OverridePresetsView.swift */; };
 		6541341A2E1DC27900BDBE08 /* OverridePresetData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 654134192E1DC27900BDBE08 /* OverridePresetData.swift */; };
+		6541341C2E1DC28000BDBE08 /* DateExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6541341B2E1DC28000BDBE08 /* DateExtensions.swift */; };
+		6584B1012E4A263900135D4D /* TOTPService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6584B1002E4A263900135D4D /* TOTPService.swift */; };
 		DD0247592DB2E89600FCADF6 /* AlarmCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0247582DB2E89600FCADF6 /* AlarmCondition.swift */; };
 		DD0247712DB4337700FCADF6 /* BuildExpireCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD02475B2DB2E8FB00FCADF6 /* BuildExpireCondition.swift */; };
 		DD0650A92DCA8A10004D3B41 /* AlarmBGSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0650A82DCA8A10004D3B41 /* AlarmBGSection.swift */; };
@@ -393,9 +394,10 @@
 		059B0FA59AABFE72FE13DDDA /* Pods-LoopFollow.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LoopFollow.release.xcconfig"; path = "Target Support Files/Pods-LoopFollow/Pods-LoopFollow.release.xcconfig"; sourceTree = "<group>"; };
 		654132E62E19EA7E00BDBE08 /* SimpleQRCodeScannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleQRCodeScannerView.swift; sourceTree = "<group>"; };
 		654132E92E19F24800BDBE08 /* TOTPGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TOTPGenerator.swift; sourceTree = "<group>"; };
-		6541341B2E1DC28000BDBE08 /* DateExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtensions.swift; sourceTree = "<group>"; };
 		654134172E1DC09700BDBE08 /* OverridePresetsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverridePresetsView.swift; sourceTree = "<group>"; };
 		654134192E1DC27900BDBE08 /* OverridePresetData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverridePresetData.swift; sourceTree = "<group>"; };
+		6541341B2E1DC28000BDBE08 /* DateExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtensions.swift; sourceTree = "<group>"; };
+		6584B1002E4A263900135D4D /* TOTPService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TOTPService.swift; sourceTree = "<group>"; };
 		A7D55B42A22051DAD69E89D0 /* Pods_LoopFollow.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LoopFollow.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		DD0247582DB2E89600FCADF6 /* AlarmCondition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlarmCondition.swift; sourceTree = "<group>"; };
 		DD02475B2DB2E8FB00FCADF6 /* BuildExpireCondition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildExpireCondition.swift; sourceTree = "<group>"; };
@@ -1191,6 +1193,7 @@
 		DDEF503E2D479B8A00884336 /* LoopAPNS */ = {
 			isa = PBXGroup;
 			children = (
+				6584B1002E4A263900135D4D /* TOTPService.swift */,
 				DDEF503F2D479B8A00884336 /* LoopAPNSService.swift */,
 				DDEF50412D479BAA00884336 /* LoopAPNSCarbsView.swift */,
 				DDEF50422D479BBA00884336 /* LoopAPNSBolusView.swift */,
@@ -1936,6 +1939,7 @@
 				DD5334272C61668800062F9D /* InfoDisplaySettingsViewModel.swift in Sources */,
 				DD0247592DB2E89600FCADF6 /* AlarmCondition.swift in Sources */,
 				DD0650F32DCE9B3D004D3B41 /* MissedReadingEditor.swift in Sources */,
+				6584B1012E4A263900135D4D /* TOTPService.swift in Sources */,
 				DD48780E2C7B74A40048F05C /* TrioRemoteControlViewModel.swift in Sources */,
 				DDEF503A2D31615000999A5D /* LogManager.swift in Sources */,
 				DD4878172C7B75350048F05C /* BolusView.swift in Sources */,

+ 5 - 34
LoopFollow/Remote/LoopAPNS/LoopAPNSBolusView.swift

@@ -26,24 +26,7 @@ struct LoopAPNSBolusView: View {
 
     // Computed property to check if TOTP should be blocked
     private var isTOTPBlocked: Bool {
-        guard Storage.shared.loopAPNSTOTPUsed.value else { return false }
-
-        // If we have a timestamp, check if 30 seconds have passed
-        if let lastUsed = Storage.shared.loopAPNSTOTPLastUsed.value {
-            let timeSinceLastUsed = Date().timeIntervalSince1970 - lastUsed
-            if timeSinceLastUsed >= 30 {
-                // 30 seconds have passed, unblock
-                Storage.shared.loopAPNSTOTPUsed.value = false
-                Storage.shared.loopAPNSTOTPLastUsed.value = nil
-                return false
-            }
-        } else {
-            // No timestamp but flag is set - this shouldn't happen, but let's clean it up
-            Storage.shared.loopAPNSTOTPUsed.value = false
-            return false
-        }
-
-        return true
+        TOTPService.shared.isTOTPBlocked(qrCodeURL: Storage.shared.loopAPNSQrCodeURL.value)
     }
 
     enum AlertType {
@@ -215,24 +198,13 @@ struct LoopAPNSBolusView: View {
                    newOtpTimeRemaining > currentOtpTimeRemaining
                 {
                     // New TOTP code generated, reset the usage flag
-                    Storage.shared.loopAPNSTOTPUsed.value = false
-                    Storage.shared.loopAPNSTOTPLastUsed.value = nil
+                    TOTPService.shared.resetTOTPUsage()
                 }
 
                 // Also check if we're at the very beginning of a new period (when time remaining is close to 30)
                 if newOtpTimeRemaining >= 29 {
                     // We're at the start of a new TOTP period, reset the usage flag
-                    Storage.shared.loopAPNSTOTPUsed.value = false
-                    Storage.shared.loopAPNSTOTPLastUsed.value = nil
-                }
-
-                // Additional safety check: if we have a timestamp and 30+ seconds have passed, unblock
-                if let lastUsed = Storage.shared.loopAPNSTOTPLastUsed.value {
-                    let timeSinceLastUsed = now - lastUsed
-                    if timeSinceLastUsed >= 30 {
-                        Storage.shared.loopAPNSTOTPUsed.value = false
-                        Storage.shared.loopAPNSTOTPLastUsed.value = nil
-                    }
+                    TOTPService.shared.resetTOTPUsage()
                 }
 
                 otpTimeRemaining = newOtpTimeRemaining
@@ -406,9 +378,8 @@ struct LoopAPNSBolusView: View {
                 DispatchQueue.main.async {
                     isLoading = false
                     if success {
-                        // Mark TOTP code as used with timestamp
-                        Storage.shared.loopAPNSTOTPUsed.value = true
-                        Storage.shared.loopAPNSTOTPLastUsed.value = Date().timeIntervalSince1970
+                        // Mark TOTP code as used
+                        TOTPService.shared.markTOTPAsUsed(qrCodeURL: Storage.shared.loopAPNSQrCodeURL.value)
                         alertMessage = "Insulin sent successfully!"
                         alertType = .success
                         LogManager.shared.log(

+ 5 - 34
LoopFollow/Remote/LoopAPNS/LoopAPNSCarbsView.swift

@@ -25,24 +25,7 @@ struct LoopAPNSCarbsView: View {
 
     // Computed property to check if TOTP should be blocked
     private var isTOTPBlocked: Bool {
-        guard Storage.shared.loopAPNSTOTPUsed.value else { return false }
-
-        // If we have a timestamp, check if 30 seconds have passed
-        if let lastUsed = Storage.shared.loopAPNSTOTPLastUsed.value {
-            let timeSinceLastUsed = Date().timeIntervalSince1970 - lastUsed
-            if timeSinceLastUsed >= 30 {
-                // 30 seconds have passed, unblock
-                Storage.shared.loopAPNSTOTPUsed.value = false
-                Storage.shared.loopAPNSTOTPLastUsed.value = nil
-                return false
-            }
-        } else {
-            // No timestamp but flag is set - this shouldn't happen, but let's clean it up
-            Storage.shared.loopAPNSTOTPUsed.value = false
-            return false
-        }
-
-        return true
+        TOTPService.shared.isTOTPBlocked(qrCodeURL: Storage.shared.loopAPNSQrCodeURL.value)
     }
 
     enum AlertType {
@@ -278,24 +261,13 @@ struct LoopAPNSCarbsView: View {
                    newOtpTimeRemaining > currentOtpTimeRemaining
                 {
                     // New TOTP code generated, reset the usage flag
-                    Storage.shared.loopAPNSTOTPUsed.value = false
-                    Storage.shared.loopAPNSTOTPLastUsed.value = nil
+                    TOTPService.shared.resetTOTPUsage()
                 }
 
                 // Also check if we're at the very beginning of a new period (when time remaining is close to 30)
                 if newOtpTimeRemaining >= 29 {
                     // We're at the start of a new TOTP period, reset the usage flag
-                    Storage.shared.loopAPNSTOTPUsed.value = false
-                    Storage.shared.loopAPNSTOTPLastUsed.value = nil
-                }
-
-                // Additional safety check: if we have a timestamp and 30+ seconds have passed, unblock
-                if let lastUsed = Storage.shared.loopAPNSTOTPLastUsed.value {
-                    let timeSinceLastUsed = now - lastUsed
-                    if timeSinceLastUsed >= 30 {
-                        Storage.shared.loopAPNSTOTPUsed.value = false
-                        Storage.shared.loopAPNSTOTPLastUsed.value = nil
-                    }
+                    TOTPService.shared.resetTOTPUsage()
                 }
 
                 otpTimeRemaining = newOtpTimeRemaining
@@ -419,9 +391,8 @@ struct LoopAPNSCarbsView: View {
                 DispatchQueue.main.async {
                     isLoading = false
                     if success {
-                        // Mark TOTP code as used with timestamp
-                        Storage.shared.loopAPNSTOTPUsed.value = true
-                        Storage.shared.loopAPNSTOTPLastUsed.value = Date().timeIntervalSince1970
+                        // Mark TOTP code as used
+                        TOTPService.shared.markTOTPAsUsed(qrCodeURL: Storage.shared.loopAPNSQrCodeURL.value)
                         let timeFormatter = DateFormatter()
                         timeFormatter.timeStyle = .short
                         alertMessage = "Carbs sent successfully for \(timeFormatter.string(from: adjustedConsumedDate))!"

+ 37 - 0
LoopFollow/Remote/LoopAPNS/TOTPService.swift

@@ -0,0 +1,37 @@
+// LoopFollow
+// TOTPService.swift
+// Created by codebymini.
+
+import Foundation
+
+/// Service class for managing TOTP code usage and blocking logic
+class TOTPService {
+    static let shared = TOTPService()
+
+    private init() {}
+
+    /// Checks if the current TOTP code is blocked (already used)
+    /// - Parameter qrCodeURL: The QR code URL to extract the current TOTP from
+    /// - Returns: True if the TOTP is blocked, false otherwise
+    func isTOTPBlocked(qrCodeURL: String) -> Bool {
+        guard let currentTOTP = TOTPGenerator.extractOTPFromURL(qrCodeURL) else {
+            return false
+        }
+
+        // Check if the current TOTP code equals the last sent TOTP code
+        return currentTOTP == Observable.shared.lastSentTOTP.value
+    }
+
+    /// Marks the current TOTP code as used
+    /// - Parameter qrCodeURL: The QR code URL to extract the current TOTP from
+    func markTOTPAsUsed(qrCodeURL: String) {
+        if let currentTOTP = TOTPGenerator.extractOTPFromURL(qrCodeURL) {
+            Observable.shared.lastSentTOTP.set(currentTOTP)
+        }
+    }
+
+    /// Resets the TOTP usage tracking (called when a new TOTP period starts)
+    func resetTOTPUsage() {
+        Observable.shared.lastSentTOTP.set(nil)
+    }
+}

+ 4 - 0
LoopFollow/Storage/Observable.swift

@@ -36,5 +36,9 @@ class Observable {
 
     var settingsPath = ObservableValue<NavigationPath>(default: NavigationPath())
 
+    // MARK: - Loop APNS TOTP Tracking
+
+    var lastSentTOTP = ObservableValue<String?>(default: nil)
+
     private init() {}
 }

+ 0 - 5
LoopFollow/Storage/Storage.swift

@@ -168,11 +168,6 @@ class Storage {
 
     var loopAPNSQrCodeURL = StorageValue<String>(key: "loopAPNSQrCodeURL", defaultValue: "")
 
-    // MARK: - Loop APNS TOTP Usage Tracking
-
-    var loopAPNSTOTPUsed = StorageValue<Bool>(key: "loopAPNSTOTPUsed", defaultValue: false)
-    var loopAPNSTOTPLastUsed = StorageValue<TimeInterval?>(key: "loopAPNSTOTPLastUsed", defaultValue: nil)
-
     static let shared = Storage()
     private init() {}
 }