Skip to content

Commit 675547d

Browse files
committed
Merge remote-tracking branch 'origin/dev' into feature/end-alarm-early-warning
# Conflicts: # LoopFollow/Controllers/Nightscout/Treatments/Overrides.swift
2 parents 9525919 + fccbdc1 commit 675547d

20 files changed

Lines changed: 293 additions & 34 deletions

‎Config.xcconfig‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66
unique_id = ${DEVELOPMENT_TEAM}
77

88
//Version (DEFAULT)
9-
LOOP_FOLLOW_MARKETING_VERSION = 6.2.33
9+
LOOP_FOLLOW_MARKETING_VERSION = 6.2.36

‎LoopFollow/Charts/BGChartModel.swift‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -537,11 +537,14 @@ final class BGChartModel: ObservableObject {
537537
}
538538
tempTargets = vc.tempTargetGraphData.map {
539539
let target = $0.correctionRange.first.map { String($0) } ?? ""
540+
// Temp targets render at the BG level they target (±5 mg/dL);
541+
// only overrides live in the top strip.
542+
let yCenter = Double($0.correctionRange.first ?? 0)
540543
return BandRect(
541544
start: Date(timeIntervalSince1970: $0.date),
542545
end: Date(timeIntervalSince1970: $0.endDate),
543-
yBottom: yBottom,
544-
yTop: yTop,
546+
yBottom: yCenter - 5,
547+
yTop: yCenter + 5,
545548
label: "Temp Target",
546549
pillText: "Temp Target\n\(Localizer.toDisplayUnits(target))\n\(pillTimeString(for: Date(timeIntervalSince1970: $0.date)))"
547550
)

‎LoopFollow/Charts/BGChartView.swift‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1280,7 +1280,7 @@ private struct BGChartCanvas: View, Equatable {
12801280
y: .value("bg", pt.value),
12811281
series: .value("series", "zt")
12821282
)
1283-
.foregroundStyle(Color(.systemGray))
1283+
.foregroundStyle(Color("ZT"))
12841284
.lineStyle(StrokeStyle(lineWidth: 2))
12851285
}
12861286

@@ -1290,7 +1290,7 @@ private struct BGChartCanvas: View, Equatable {
12901290
y: .value("bg", pt.value),
12911291
series: .value("series", "iob")
12921292
)
1293-
.foregroundStyle(Color(.systemBlue))
1293+
.foregroundStyle(Color("Insulin"))
12941294
.lineStyle(StrokeStyle(lineWidth: 2))
12951295
}
12961296

@@ -1310,7 +1310,7 @@ private struct BGChartCanvas: View, Equatable {
13101310
y: .value("bg", pt.value),
13111311
series: .value("series", "uam")
13121312
)
1313-
.foregroundStyle(Color(.systemPink))
1313+
.foregroundStyle(Color("UAM"))
13141314
.lineStyle(StrokeStyle(lineWidth: 2))
13151315
}
13161316
}

‎LoopFollow/Controllers/NightScout.swift‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,15 @@ extension MainViewController {
7272

7373
func clearOldOverride() {
7474
overrideGraphData.removeAll()
75+
Observable.shared.override.value = nil
76+
Observable.shared.overrideEndAt.value = nil
7577
updateOverrideGraph()
7678
}
7779

7880
func clearOldTempTarget() {
7981
tempTargetGraphData.removeAll()
82+
Observable.shared.tempTarget.value = nil
83+
Observable.shared.tempTargetEndAt.value = nil
8084
updateTempTargetGraph()
8185
}
8286

‎LoopFollow/Controllers/Nightscout/Treatments/Overrides.swift‎

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ extension MainViewController {
77
func processNSOverrides(entries: [[String: AnyObject]]) {
88
overrideGraphData.removeAll()
99
var activeOverrideNote: String?
10+
var activeOverrideEndAt: TimeInterval?
1011

1112
let sorted = entries.sorted { lhs, rhs in
1213
guard
@@ -35,26 +36,29 @@ extension MainViewController {
3536

3637
let start = max(startDate.timeIntervalSince1970, graphHorizon)
3738

38-
let isIndefinite = (e["durationType"] as? String) == "indefinite" // Only for Loop overrides
39-
var end: TimeInterval
40-
if isIndefinite {
41-
end = maxEndDate
42-
} else {
43-
end = start + (e["duration"] as? Double ?? 5) * 60
44-
}
39+
let nextStart: TimeInterval? = {
40+
guard i + 1 < sorted.count,
41+
let nextDateStr = (sorted[i + 1]["timestamp"] as? String) ?? (sorted[i + 1]["created_at"] as? String)
42+
else { return nil }
43+
return NightscoutUtils.parseDate(nextDateStr)?.timeIntervalSince1970
44+
}()
45+
46+
let durationSeconds = (e["duration"] as? Double ?? 5) * 60
47+
// Loop marks indefinite overrides explicitly; Trio represents them
48+
// as a ~30-day duration. Treat a week or longer as indefinite.
49+
let isIndefinite = (e["durationType"] as? String) == "indefinite"
50+
|| durationSeconds >= 7 * 24 * 3600
4551

46-
// The scheduled end is kept unclamped so the end alarm's early
47-
// warning can see ends beyond the graph horizon; nil when the
48-
// override runs indefinitely.
49-
var scheduledEnd: TimeInterval? = isIndefinite ? nil : end
52+
var end: TimeInterval = isIndefinite ? maxEndDate : start + durationSeconds
5053

51-
if i + 1 < sorted.count,
52-
let nextDateStr = (sorted[i + 1]["timestamp"] as? String) ?? (sorted[i + 1]["created_at"] as? String),
53-
let nextStart = NightscoutUtils.parseDate(nextDateStr)?
54-
.timeIntervalSince1970
55-
{
54+
// True end for countdown display and the end alarm's early
55+
// warning: based on the raw start and never clamped to the graph
56+
// edge; nil while indefinite.
57+
var trueEnd: TimeInterval? = isIndefinite ? nil : startDate.timeIntervalSince1970 + durationSeconds
58+
59+
if let nextStart = nextStart {
5660
end = min(end, nextStart - 60) // avoid overlapping overrides
57-
scheduledEnd = scheduledEnd.map { min($0, nextStart - 60) }
61+
trueEnd = trueEnd.map { min($0, nextStart - 60) }
5862
}
5963

6064
end = min(end, maxEndDate)
@@ -82,16 +86,18 @@ extension MainViewController {
8286
?? (e["reason"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines)
8387
?? "",
8488
sgv: -20,
85-
scheduledEndDate: scheduledEnd
89+
scheduledEndDate: trueEnd
8690
)
8791
overrideGraphData.append(dot)
8892

8993
if now >= start, now < end {
9094
activeOverrideNote = e["notes"] as? String ?? e["reason"] as? String
95+
activeOverrideEndAt = trueEnd
9196
}
9297
}
9398

9499
Observable.shared.override.value = activeOverrideNote
100+
Observable.shared.overrideEndAt.value = activeOverrideEndAt
95101
if Storage.shared.device.value != "Loop" {
96102
if let note = activeOverrideNote {
97103
infoManager.updateInfoData(type: .override, value: note)
@@ -102,5 +108,9 @@ extension MainViewController {
102108
if Storage.shared.graphOtherTreatments.value {
103109
updateOverrideGraph()
104110
}
111+
112+
#if !targetEnvironment(macCatalyst)
113+
LiveActivityManager.shared.refreshFromCurrentState(reason: "overrideChanged")
114+
#endif
105115
}
106116
}

‎LoopFollow/Controllers/Nightscout/Treatments/TemporaryTarget.swift‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ extension MainViewController {
99
func processNSTemporaryTarget(entries: [[String: AnyObject]]) {
1010
tempTargetGraphData.removeAll()
1111
var activeTempTarget: Int?
12+
var activeTempTargetEndAt: TimeInterval?
1213

1314
for (index, currentEntry) in entries.reversed().enumerated() {
1415
guard let dateStr = currentEntry["timestamp"] as? String ?? currentEntry["created_at"] as? String else { continue }
@@ -27,6 +28,7 @@ extension MainViewController {
2728
if let activeIndex = tempTargetGraphData.lastIndex(where: { $0.endDate > dateTimeStamp }) {
2829
tempTargetGraphData[activeIndex].endDate = dateTimeStamp
2930
activeTempTarget = nil
31+
activeTempTargetEndAt = nil
3032
}
3133
continue
3234
}
@@ -58,6 +60,9 @@ extension MainViewController {
5860
let currentTime = Date().timeIntervalSince1970
5961
if currentTime < endDate {
6062
activeTempTarget = Int(targetValue!)
63+
// Trio represents indefinite temp targets as a ~30-day duration;
64+
// treat a week or longer as indefinite (no end to display).
65+
activeTempTargetEndAt = duration >= 7 * 24 * 3600 ? nil : endDate
6166
}
6267
}
6368

@@ -72,5 +77,10 @@ extension MainViewController {
7277
} else {
7378
Observable.shared.tempTarget.value = nil
7479
}
80+
Observable.shared.tempTargetEndAt.value = activeTempTargetEndAt
81+
82+
#if !targetEnvironment(macCatalyst)
83+
LiveActivityManager.shared.refreshFromCurrentState(reason: "tempTargetChanged")
84+
#endif
7585
}
7686
}

‎LoopFollow/LiveActivity/APNSClient.swift‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,9 @@ class APNSClient {
246246
if let cob = snapshot.cob { snapshotDict["cob"] = cob }
247247
if let projected = snapshot.projected { snapshotDict["projected"] = projected }
248248
if let override = snapshot.override { snapshotDict["override"] = override }
249+
if let overrideEndAt = snapshot.overrideEndAt { snapshotDict["overrideEndAt"] = overrideEndAt }
250+
if let tempTargetMgdl = snapshot.tempTargetMgdl { snapshotDict["tempTargetMgdl"] = tempTargetMgdl }
251+
if let tempTargetEndAt = snapshot.tempTargetEndAt { snapshotDict["tempTargetEndAt"] = tempTargetEndAt }
249252
if let recBolus = snapshot.recBolus { snapshotDict["recBolus"] = recBolus }
250253
if let battery = snapshot.battery { snapshotDict["battery"] = battery }
251254
if let pumpBattery = snapshot.pumpBattery { snapshotDict["pumpBattery"] = pumpBattery }

‎LoopFollow/LiveActivity/GlucoseSnapshotBuilder.swift‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ protocol CurrentGlucoseStateProviding {
3333
/// Active override name (nil if no active override).
3434
var override: String? { get }
3535

36+
/// End of the active override as Unix epoch seconds (nil = no override or indefinite).
37+
var overrideEndAt: TimeInterval? { get }
38+
39+
/// Active temporary target in mg/dL (nil if no active temp target).
40+
var tempTargetMgdl: Double? { get }
41+
42+
/// End of the active temporary target as Unix epoch seconds.
43+
var tempTargetEndAt: TimeInterval? { get }
44+
3645
/// Recommended bolus in units.
3746
var recBolus: Double? { get }
3847

@@ -141,6 +150,9 @@ enum GlucoseSnapshotBuilder {
141150
cob: provider.cob,
142151
projected: provider.projectedMgdl,
143152
override: provider.override,
153+
overrideEndAt: provider.overrideEndAt,
154+
tempTargetMgdl: provider.tempTargetMgdl,
155+
tempTargetEndAt: provider.tempTargetEndAt,
144156
recBolus: provider.recBolus,
145157
battery: provider.battery,
146158
pumpBattery: provider.pumpBattery,

‎LoopFollow/LiveActivity/StorageCurrentGlucoseStateProvider.swift‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// StorageCurrentGlucoseStateProvider.swift
33

44
import Foundation
5+
import HealthKit
56

67
/// Reads the latest glucose state from LoopFollow's Storage and Observable layers.
78
/// This is the only file in the pipeline that is allowed to touch Storage.shared
@@ -47,6 +48,18 @@ struct StorageCurrentGlucoseStateProvider: CurrentGlucoseStateProviding {
4748
Observable.shared.override.value
4849
}
4950

51+
var overrideEndAt: TimeInterval? {
52+
Observable.shared.overrideEndAt.value
53+
}
54+
55+
var tempTargetMgdl: Double? {
56+
Observable.shared.tempTarget.value?.doubleValue(for: .milligramsPerDeciliter)
57+
}
58+
59+
var tempTargetEndAt: TimeInterval? {
60+
Observable.shared.tempTargetEndAt.value
61+
}
62+
5063
var recBolus: Double? {
5164
Observable.shared.deviceRecBolus.value
5265
}

‎LoopFollow/LiveActivitySettingsView.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
get: { slots[index] },
6060
set: { selectSlot($0, at: index) }
6161
)) {
62-
ForEach(LiveActivitySlotOption.allCases, id: \.self) { option in
62+
ForEach(LiveActivitySlotOption.gridCases, id: \.self) { option in
6363
Text(option.displayName).tag(option)
6464
}
6565
}

0 commit comments

Comments
 (0)