Переглянути джерело

Add Fat/Protein order change banner and flag

Introduce a persisted flag (hasSeenFatProteinOrderChange) to track whether the user has acknowledged that the order of Fat and Protein inputs changed. Show a dismissible banner in TreatmentsRootView when the flag is unset, and mark the flag when dismissed. Set the flag during onboarding completion so new users won't see the banner. Add the new flag to FileProtectionFixer cleanup list and add a localization key for the banner text. Can be removed after 1 year (March 2027).
Mike Plante 3 місяців тому
батько
коміт
dc1e669c92

+ 3 - 0
Trio/Sources/Helpers/PropertyPersistentFlags.swift

@@ -23,4 +23,7 @@ final class PropertyPersistentFlags {
     @PersistedProperty(key: "diagnosticsSharing") var diagnosticsSharingEnabled: Bool?
 
     @PersistedProperty(key: "lastCleanupDate") var lastCleanupDate: Date?
+
+    // TODO: This flag can be deleted in March 2027. Check the commit for other places to cleanup.
+    @PersistedProperty(key: "hasSeenFatProteinOrderChange") var hasSeenFatProteinOrderChange: Bool?
 }

+ 2 - 1
Trio/Sources/Helpers/PropertyWrappers/PersistedProperty.swift

@@ -122,7 +122,8 @@ enum FileProtectionFixer {
         let flagFiles = [
             "onboardingCompleted.plist",
             "diagnosticsSharing.plist",
-            "lastCleanupDate.plist"
+            "lastCleanupDate.plist",
+            "hasSeenFatProteinOrderChange.plist"
         ]
 
         let fileManager = FileManager.default

+ 3 - 0
Trio/Sources/Localizations/Main/Localizable.xcstrings

@@ -230208,6 +230208,9 @@
         }
       }
     },
+    "The order of Fat and Protein inputs has changed." : {
+
+    },
     "The oref algorithm determines insulin dosing based on a number of scenarios that it estimates with different types of forecasts." : {
       "localizations" : {
         "bg" : {

+ 22 - 0
Trio/Sources/Modules/Treatments/View/TreatmentsRootView.swift

@@ -24,6 +24,7 @@ extension Treatments {
         @State private var calculatorDetent = PresentationDetent.large
         @State private var pushed: Bool = false
         @State private var debounce: DispatchWorkItem?
+        @State private var showFatProteinOrderBanner = false
 
         private enum Config {
             static let dividerHeight: CGFloat = 2
@@ -198,6 +199,23 @@ extension Treatments {
 
                             if state.useFPUconversion {
                                 proteinAndFat()
+
+                                if showFatProteinOrderBanner {
+                                    HStack {
+                                        Image(systemName: "arrow.left.arrow.right")
+                                        Text("The order of Fat and Protein inputs has changed.").font(.callout)
+                                        Spacer()
+                                        Button {
+                                            PropertyPersistentFlags.shared.hasSeenFatProteinOrderChange = true
+                                            withAnimation { showFatProteinOrderBanner = false }
+                                        } label: {
+                                            Image(systemName: "xmark.circle.fill")
+                                        }
+                                        .buttonStyle(.plain)
+                                    }
+                                    .listRowBackground(Color.orange.opacity(0.75))
+                                    .transition(.opacity)
+                                }
                             }
 
                             // Time
@@ -391,6 +409,10 @@ extension Treatments {
                     Task { @MainActor in
                         state.insulinCalculated = await state.calculateInsulin()
                     }
+
+                    if PropertyPersistentFlags.shared.hasSeenFatProteinOrderChange != true {
+                        showFatProteinOrderBanner = true
+                    }
                 }
             }
             .onDisappear {

+ 1 - 0
Trio/Sources/Services/OnboardingManager/OnboardingManager.swift

@@ -24,6 +24,7 @@ import Swinject
     /// Marks onboarding as completed and updates the shouldShowOnboarding flag.
     func completeOnboarding() {
         PropertyPersistentFlags.shared.onboardingCompleted = true
+        PropertyPersistentFlags.shared.hasSeenFatProteinOrderChange = true
         shouldShowOnboarding = false
     }