Explorar el Código

Surface CGM-owned alarms in their own section with a linked app name

Reading-driven alarms (high / low / urgent low / forecasted low) are
silenced at evaluation time when the active CGM owns its own glucose
alerts. Previously they sat in the Enabled section looking active,
which made the "Use CGM App Alerts" toggle's effect invisible.

Move suppressed alarms into a dedicated "Handled by CGM App" section
at the bottom with reduced opacity. The footer names the specific
companion app (Dexcom G6 / One, Dexcom G7 / One+, xDrip4iOS, ...) and
renders the name as a deep link when a URL scheme is known. Reword
the toggle footer to spell out which CGMs trigger this behavior.
trioneer hace 2 semanas
padre
commit
4d38135452

+ 34 - 44
Trio/Sources/Localizations/Main/Localizable.xcstrings

@@ -4822,9 +4822,6 @@
         }
       }
     },
-    "– –" : {
-
-    },
     ", %lld%%" : {
       "localizations" : {
         "bg" : {
@@ -40924,6 +40921,9 @@
         }
       }
     },
+    "Already set for Day & Night. Change the alarm time window to add a second alarm for the same type." : {
+
+    },
     "Already using Trio and updating from an older version?" : {
       "localizations" : {
         "bg" : {
@@ -52362,6 +52362,9 @@
         }
       }
     },
+    "Available" : {
+
+    },
     "Available Layouts:" : {
       "localizations" : {
         "bg" : {
@@ -66424,6 +66427,9 @@
         }
       }
     },
+    "Carbs Required" : {
+
+    },
     "Carbs Required Badge" : {
       "localizations" : {
         "bg" : {
@@ -66674,6 +66680,7 @@
     },
     "Carbs required: %d g" : {
       "comment" : "Carbs required",
+      "extractionState" : "stale",
       "localizations" : {
         "bg" : {
           "stringUnit" : {
@@ -81817,7 +81824,7 @@
       }
     },
     "d" : {
-      "comment" : "Abbreviation for Days\nabbreviation for days",
+      "comment" : "abbreviation for days",
       "localizations" : {
         "bg" : {
           "stringUnit" : {
@@ -101286,9 +101293,6 @@
         }
       }
     },
-    "Drives the outer-ring + tag on the home screen's glucose bobble." : {
-
-    },
     "Due to how the curve is calculated when using the Sigmoid Formula, increasing this setting has a different impact on the steepness of the curve than in the standard logarithmic Dynamic ISF calculation. Use caution when adjusting this setting." : {
       "localizations" : {
         "bg" : {
@@ -122377,14 +122381,6 @@
         }
       }
     },
-    "Fire test alert (critical)" : {
-      "comment" : "A button that fires a critical test alert.",
-      "isCommentAutoGenerated" : true
-    },
-    "Fire test alert (immediate)" : {
-      "comment" : "A button that fires a test alert.",
-      "isCommentAutoGenerated" : true
-    },
     "Fires when glucose drops to or below a low threshold." : {
 
     },
@@ -122403,6 +122399,12 @@
     "Fires when glucose rises to or above a high threshold." : {
 
     },
+    "Fires when oref recommends eating carbs to avoid a low." : {
+
+    },
+    "Fires when the algorithm suggests to eat at least this many grams of carbs to avoid a low." : {
+
+    },
     "Fires when the forecast at +20 minutes (blended across all available prediction curves) is at or below this value." : {
       "comment" : "Text displayed in a section header for a glucose alert that fires when the forecast at +20 minutes (blended across all available prediction curves) is at or below the specified threshold.",
       "isCommentAutoGenerated" : true
@@ -124165,9 +124167,6 @@
         }
       }
     },
-    "Forecast +%1$d min %2$@." : {
-
-    },
     "Forecast Display Type" : {
       "localizations" : {
         "bg" : {
@@ -124416,9 +124415,6 @@
         }
       }
     },
-    "Forecasted Low Glucose" : {
-
-    },
     "Forecasts" : {
       "localizations" : {
         "bg" : {
@@ -125814,7 +125810,6 @@
       }
     },
     "FreeStyle Libre" : {
-      "extractionState" : "stale",
       "localizations" : {
         "bg" : {
           "stringUnit" : {
@@ -125938,12 +125933,6 @@
         }
       }
     },
-    "FreeStyle Libre 1 / 2 / 2+" : {
-
-    },
-    "FreeStyle Libre 3 / 3+" : {
-
-    },
     "Frequency" : {
 
     },
@@ -126322,7 +126311,7 @@
       }
     },
     "g" : {
-      "comment" : "Gram abbreviation\nThe short unit display string for grams\nUnits for carbs\ngram of carbs",
+      "comment" : "Abbreviation for grams\nGram abbreviation\nThe short unit display string for grams\nUnits for carbs\ngram of carbs",
       "localizations" : {
         "bg" : {
           "stringUnit" : {
@@ -133009,7 +132998,7 @@
       }
     },
     "h" : {
-      "comment" : "Abbreviation for Hours\nabbreviation for hours\nh",
+      "comment" : "abbreviation for hours\nh",
       "localizations" : {
         "bg" : {
           "stringUnit" : {
@@ -133258,6 +133247,9 @@
         }
       }
     },
+    "Handled by CGM App" : {
+
+    },
     "Hardware" : {
       "extractionState" : "manual",
       "localizations" : {
@@ -144375,10 +144367,6 @@
         }
       }
     },
-    "Inactive" : {
-      "comment" : "A section title for inactive glucose alerts.",
-      "isCommentAutoGenerated" : true
-    },
     "Include IOB & COB in the calendar event data." : {
       "localizations" : {
         "bg" : {
@@ -161016,6 +161004,9 @@
         }
       }
     },
+    "Low Glucose Soon" : {
+
+    },
     "Low Limit" : {
       "localizations" : {
         "bg" : {
@@ -211519,10 +211510,6 @@
         }
       }
     },
-    "Retract test alerts" : {
-      "comment" : "A button that retracts test alerts.",
-      "isCommentAutoGenerated" : true
-    },
     "Retry" : {
       "localizations" : {
         "bg" : {
@@ -214940,9 +214927,6 @@
         }
       }
     },
-    "Scenario" : {
-
-    },
     "Schedule " : {
       "extractionState" : "manual",
       "localizations" : {
@@ -220133,9 +220117,6 @@
         }
       }
     },
-    "Sensor Lifecycle Scenario" : {
-
-    },
     "Sensor might have temporarily stopped, fallen off or is too cold or too warm" : {
       "comment" : "ensor might have temporarily stopped, fallen off or is too cold or too warm",
       "extractionState" : "manual",
@@ -250441,6 +250422,9 @@
         }
       }
     },
+    "These alarms are silenced because your CGM app handles them. Turn off \"Use CGM App Alerts\" below to have Trio alert you instead." : {
+
+    },
     "These thresholds drive the chart band colors, statistics in-range buckets, Live Activity colors, and Contact Image colors." : {
 
     },
@@ -273302,6 +273286,9 @@
         }
       }
     },
+    "Unavailable" : {
+
+    },
     "unit" : {
       "localizations" : {
         "bg" : {
@@ -290939,6 +290926,9 @@
     "Your glucose data, therapy settings, credentials, and logs always stay on your device. Pick what you'd like to share — you can change this any time in Settings → App Diagnostics." : {
 
     },
+    "Your glucose may go below %2$@ in %1$d min." : {
+
+    },
     "Your glucose target is the blood glucose level you aim to maintain. Trio will use this to calculate insulin doses and provide recommendations." : {
       "localizations" : {
         "bg" : {

+ 16 - 0
Trio/Sources/Modules/GlucoseAlerts/GlucoseAlertsStateModel.swift

@@ -3,13 +3,29 @@ import SwiftUI
 
 extension GlucoseAlerts {
     final class StateModel: BaseStateModel<Provider> {
+        @Injected() var fetchGlucoseManager: FetchGlucoseManager!
+
         @Published var units: GlucoseUnits = .mgdL
         @Published var glucoseBadge = false
+        @Published var cgmAppInfo: CGMManagerAlertOwnership.OwningApp?
+
+        var cgmProvidesOwnAlerts: Bool { cgmAppInfo != nil }
 
         override func subscribe() {
             units = settingsManager.settings.units
+            refreshCGMOwnership()
             subscribeSetting(\.glucoseBadge, on: $glucoseBadge) { glucoseBadge = $0 }
         }
+
+        /// Re-read whether the active CGM owns its own glucose alerts. The
+        /// active manager can change while the user is in CGM settings, so
+        /// the alarms view calls this on appear.
+        func refreshCGMOwnership() {
+            cgmAppInfo = CGMManagerAlertOwnership.owningApp(
+                manager: fetchGlucoseManager?.cgmManager,
+                sourceType: fetchGlucoseManager?.cgmGlucoseSourceType ?? .none
+            )
+        }
     }
 }
 

+ 68 - 3
Trio/Sources/Modules/GlucoseAlerts/View/GlucoseAlertsRootView.swift

@@ -50,6 +50,16 @@ extension GlucoseAlerts {
                         }
                     }.listRowBackground(Color.chart)
                 }
+                if !cgmHandledAlerts.isEmpty {
+                    Section(
+                        header: Text("Handled by CGM App"),
+                        footer: cgmHandledFooter
+                    ) {
+                        ForEach(cgmHandledAlerts) { alarm in
+                            row(for: alarm).opacity(0.6)
+                        }
+                    }.listRowBackground(Color.chart)
+                }
 
                 // FIXME: make this into a nice setting with mini and verbose hint
                 Section {
@@ -58,7 +68,7 @@ extension GlucoseAlerts {
                 }.listRowBackground(Color.chart)
 
                 Section(footer: Text(
-                    "On by default for all CGM or apps that handle glucose alerts (all Dexcom CGMs, xDrip4iOS). Turn off if you've disabled those and want Trio to alert you instead."
+                    "On by default when using a CGM in Trio which requires another app (e.g. Dexcom G6 / One, Dexcom G7 / One+, or xDrip4iOS). Turn off if you've disabled those and want Trio to alert you instead."
                 )) {
                     Toggle(isOn: Binding(
                         get: { !store.configuration.forceTrioAlertsWhenCGMProvidesOwn },
@@ -128,7 +138,10 @@ extension GlucoseAlerts {
                     sheetTitle: String(localized: "Help", comment: "Help sheet title")
                 )
             }
-            .onAppear(perform: configureView)
+            .onAppear {
+                configureView()
+                state.refreshCGMOwnership()
+            }
         }
 
         private func handleSheetDismiss() {
@@ -148,9 +161,61 @@ extension GlucoseAlerts {
 
         // MARK: - Sorted lists
 
+        /// Mirror of `GlucoseAlertCoordinator.shouldRespect(alarm:)`'s
+        /// CGM-ownership branch: when "Use CGM App Alerts" is ON and the
+        /// active CGM provides its own glucose alerts, the coordinator
+        /// silences reading-driven types. The view surfaces this by moving
+        /// those alarms into a dedicated section.
+        private var isCGMSuppressionActive: Bool {
+            !store.configuration.forceTrioAlertsWhenCGMProvidesOwn && state.cgmProvidesOwnAlerts
+        }
+
+        private var cgmHandledAlerts: [GlucoseAlert] {
+            guard isCGMSuppressionActive else { return [] }
+            return store.alerts
+                .filter { $0.isEnabled && $0.type.isReadingDriven }
+                .sorted { lhs, rhs in
+                    lhs.type.priority < rhs.type.priority
+                }
+        }
+
+        /// Footer for the "Handled by CGM App" section. Names the specific
+        /// companion app, and renders its name as a deep link when a URL
+        /// scheme is known for that app (see CGMManagerAlertOwnership).
+        @ViewBuilder private var cgmHandledFooter: some View {
+            if let info = state.cgmAppInfo {
+                Text(handledFooterMarkdown(for: info))
+            } else {
+                Text(
+                    "These alarms are silenced because the CGM app handles them. To have Trio notify you instead, turn off \"Use CGM App Alerts\" below."
+                )
+            }
+        }
+
+        private func handledFooterMarkdown(for info: CGMManagerAlertOwnership.OwningApp) -> AttributedString {
+            let body = String(
+                format: String(
+                    localized:
+                    "These alarms are silenced because the %@ app handles CGM alerts. To have Trio notify you instead, turn off \"Use CGM App Alerts\" below."
+                ),
+                "{{NAME}}"
+            )
+            var result = AttributedString(body)
+            if let range = result.range(of: "{{NAME}}") {
+                var name = AttributedString(info.name)
+                if let url = info.deepLink {
+                    name.link = url
+                }
+                result.replaceSubrange(range, with: name)
+            }
+            return result
+        }
+
         private var enabledAlerts: [GlucoseAlert] {
-            store.alerts
+            let handled = Set(cgmHandledAlerts.map(\.id))
+            return store.alerts
                 .filter(\.isEnabled)
+                .filter { !handled.contains($0.id) }
                 .sorted { lhs, rhs in
                     lhs.type.priority < rhs.type.priority
                 }