Browse Source

Merge branch 'core-data-sync-trio' into core-data-sync-autotune-removed

Deniz Cengiz 1 năm trước cách đây
mục cha
commit
83532f8a32
50 tập tin đã thay đổi với 110 bổ sung581 xóa
  1. 0 12
      FreeAPS.xcodeproj/project.pbxproj
  2. 0 1
      FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json
  3. 0 97
      FreeAPS/Sources/APS/APSManager.swift
  4. 0 52
      FreeAPS/Sources/APS/FetchAnnouncementsManager.swift
  5. 0 2
      FreeAPS/Sources/APS/OpenAPS/Constants.swift
  6. 0 80
      FreeAPS/Sources/APS/Storage/AnnouncementsStorage.swift
  7. 1 1
      FreeAPS/Sources/APS/Storage/OverrideStorage.swift
  8. 0 1
      FreeAPS/Sources/Application/FreeAPSApp.swift
  9. 0 1
      FreeAPS/Sources/Assemblies/APSAssembly.swift
  10. 0 1
      FreeAPS/Sources/Assemblies/StorageAssembly.swift
  11. 0 6
      FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings
  12. 0 6
      FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings
  13. 0 6
      FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings
  14. 0 6
      FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings
  15. 0 6
      FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings
  16. 0 6
      FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings
  17. 0 6
      FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings
  18. 0 6
      FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings
  19. 0 6
      FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings
  20. 0 6
      FreeAPS/Sources/Localizations/Main/hu.lproj/Localizable.strings
  21. 0 6
      FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings
  22. 0 6
      FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings
  23. 0 6
      FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings
  24. 0 6
      FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings
  25. 0 6
      FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings
  26. 0 6
      FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings
  27. 0 6
      FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings
  28. 0 6
      FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings
  29. 0 6
      FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings
  30. 0 6
      FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings
  31. 0 6
      FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings
  32. 0 6
      FreeAPS/Sources/Localizations/Main/vi.lproj/Localizable.strings
  33. 0 6
      FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings
  34. 0 57
      FreeAPS/Sources/Models/Announcement.swift
  35. 0 5
      FreeAPS/Sources/Models/FreeAPSSettings.swift
  36. 0 1
      FreeAPS/Sources/Modules/Home/HomeDataFlow.swift
  37. 0 7
      FreeAPS/Sources/Modules/Home/HomeProvider.swift
  38. 0 1
      FreeAPS/Sources/Modules/Home/HomeStateModel.swift
  39. 45 14
      FreeAPS/Sources/Modules/Home/View/Chart/ChartElements/BasalChart.swift
  40. 1 1
      FreeAPS/Sources/Modules/Home/View/Chart/ChartElements/OverrideView.swift
  41. 5 2
      FreeAPS/Sources/Modules/Home/View/HomeRootView.swift
  42. 0 2
      FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigStateModel.swift
  43. 1 1
      FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConfigRootView.swift
  44. 2 35
      FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutFetchView.swift
  45. 0 4
      FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift
  46. 24 0
      FreeAPS/Sources/Modules/Treatments/TreatmentsStateModel.swift
  47. 31 13
      FreeAPS/Sources/Modules/Treatments/View/TreatmentsRootView.swift
  48. 0 35
      FreeAPS/Sources/Services/Network/Nightscout/NightscoutAPI.swift
  49. 0 16
      FreeAPS/Sources/Services/Network/Nightscout/NightscoutManager.swift
  50. 0 1
      LiveActivity/Views/WidgetItems/LiveActivityIOBLabelView.swift

+ 0 - 12
FreeAPS.xcodeproj/project.pbxproj

@@ -120,8 +120,6 @@
 		38569349270B5DFB0002C50D /* AppGroupSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38569346270B5DFB0002C50D /* AppGroupSource.swift */; };
 		38569353270B5E350002C50D /* CGMRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38569352270B5E350002C50D /* CGMRootView.swift */; };
 		385CEA8225F23DFD002D6D5B /* NightscoutStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 385CEA8125F23DFD002D6D5B /* NightscoutStatus.swift */; };
-		385CEAC125F2EA52002D6D5B /* Announcement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 385CEAC025F2EA52002D6D5B /* Announcement.swift */; };
-		385CEAC425F2F154002D6D5B /* AnnouncementsStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 385CEAC325F2F154002D6D5B /* AnnouncementsStorage.swift */; };
 		3862CC2E2743F9F700BF832C /* CalendarManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3862CC2D2743F9F700BF832C /* CalendarManager.swift */; };
 		3870FF4725EC187A0088248F /* BloodGlucose.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3870FF4225EC13F40088248F /* BloodGlucose.swift */; };
 		3871F39C25ED892B0013ECB5 /* TempTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3871F39B25ED892B0013ECB5 /* TempTarget.swift */; };
@@ -146,7 +144,6 @@
 		38A0363B25ECF07E00FCBB52 /* GlucoseStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A0363A25ECF07E00FCBB52 /* GlucoseStorage.swift */; };
 		38A0364225ED069400FCBB52 /* TempBasal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A0364125ED069400FCBB52 /* TempBasal.swift */; };
 		38A13D3225E28B4B00EAA382 /* PumpHistoryEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A13D3125E28B4B00EAA382 /* PumpHistoryEvent.swift */; };
-		38A43598262E0E4900E80935 /* FetchAnnouncementsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A43597262E0E4900E80935 /* FetchAnnouncementsManager.swift */; };
 		38A504A425DD9C4000C5B9E8 /* UserDefaultsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A5049125DD9C4000C5B9E8 /* UserDefaultsExtensions.swift */; };
 		38A9260525F012D8009E3739 /* CarbRatios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38A9260425F012D8009E3739 /* CarbRatios.swift */; };
 		38AAF85525FFF846004AF583 /* CurrentGlucoseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AAF85425FFF846004AF583 /* CurrentGlucoseView.swift */; };
@@ -813,8 +810,6 @@
 		38569346270B5DFB0002C50D /* AppGroupSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppGroupSource.swift; sourceTree = "<group>"; };
 		38569352270B5E350002C50D /* CGMRootView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGMRootView.swift; sourceTree = "<group>"; };
 		385CEA8125F23DFD002D6D5B /* NightscoutStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutStatus.swift; sourceTree = "<group>"; };
-		385CEAC025F2EA52002D6D5B /* Announcement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Announcement.swift; sourceTree = "<group>"; };
-		385CEAC325F2F154002D6D5B /* AnnouncementsStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnouncementsStorage.swift; sourceTree = "<group>"; };
 		3862CC2D2743F9F700BF832C /* CalendarManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarManager.swift; sourceTree = "<group>"; };
 		3870FF4225EC13F40088248F /* BloodGlucose.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BloodGlucose.swift; sourceTree = "<group>"; };
 		3871F39B25ED892B0013ECB5 /* TempTarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TempTarget.swift; sourceTree = "<group>"; };
@@ -841,7 +836,6 @@
 		38A0363A25ECF07E00FCBB52 /* GlucoseStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseStorage.swift; sourceTree = "<group>"; };
 		38A0364125ED069400FCBB52 /* TempBasal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TempBasal.swift; sourceTree = "<group>"; };
 		38A13D3125E28B4B00EAA382 /* PumpHistoryEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PumpHistoryEvent.swift; sourceTree = "<group>"; };
-		38A43597262E0E4900E80935 /* FetchAnnouncementsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchAnnouncementsManager.swift; sourceTree = "<group>"; };
 		38A5049125DD9C4000C5B9E8 /* UserDefaultsExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsExtensions.swift; sourceTree = "<group>"; };
 		38A9260425F012D8009E3739 /* CarbRatios.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbRatios.swift; sourceTree = "<group>"; };
 		38AAF85425FFF846004AF583 /* CurrentGlucoseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentGlucoseView.swift; sourceTree = "<group>"; };
@@ -1813,7 +1807,6 @@
 				CE95BF562BA5F5FE00DC3DE3 /* PluginManager.swift */,
 				3811DF0F25CAAAE200A708ED /* APSManager.swift */,
 				38BF021E25E7F0DE00579895 /* DeviceDataManager.swift */,
-				38A43597262E0E4900E80935 /* FetchAnnouncementsManager.swift */,
 				38DAB289260D349500F74C1A /* FetchGlucoseManager.swift */,
 				38192E06261BA9960094D973 /* FetchTreatmentsManager.swift */,
 				3856933F270B57A00002C50D /* CGM */,
@@ -1991,7 +1984,6 @@
 				DD07CA132CE80B73002D45A9 /* TimeInRangeChartStyle.swift */,
 				DD940BA92CA7585D000830A5 /* GlucoseColorScheme.swift */,
 				DD6D67E32C9C253500660C9B /* ColorSchemeOption.swift */,
-				385CEAC025F2EA52002D6D5B /* Announcement.swift */,
 				388E5A5F25B6F2310019842D /* Autosens.swift */,
 				388358C725EEF6D200E024B2 /* BasalProfileEntry.swift */,
 				38D0B3B525EBE24900CB6E88 /* Battery.swift */,
@@ -2084,7 +2076,6 @@
 			isa = PBXGroup;
 			children = (
 				CE82E02428E867BA00473A9C /* AlertStorage.swift */,
-				385CEAC325F2F154002D6D5B /* AnnouncementsStorage.swift */,
 				38AEE75625F0F18E0013F05B /* CarbsStorage.swift */,
 				DDB37CC42D05048F00D99BF4 /* ContactImageStorage.swift */,
 				5864E8582C42CFAE00294306 /* DeterminationStorage.swift */,
@@ -3402,7 +3393,6 @@
 				5825A1BE2C97335C0046467E /* EditTempTargetForm.swift in Sources */,
 				19D466A329AA2B80004D5F33 /* MealSettingsDataFlow.swift in Sources */,
 				3811DEB225C9D88300A708ED /* KeychainItemAccessibility.swift in Sources */,
-				385CEAC425F2F154002D6D5B /* AnnouncementsStorage.swift in Sources */,
 				38AEE73D25F0200C0013F05B /* FreeAPSSettings.swift in Sources */,
 				38FCF3FD25E997A80078B0D1 /* PumpHistoryStorage.swift in Sources */,
 				58645BA72CA2D390008AFCE7 /* ChartAxisSetup.swift in Sources */,
@@ -3657,7 +3647,6 @@
 				BD2B464E0745FBE7B79913F4 /* NightscoutConfigProvider.swift in Sources */,
 				9825E5E923F0B8FA80C8C7C7 /* NightscoutConfigStateModel.swift in Sources */,
 				58645B9D2CA2D275008AFCE7 /* DeterminationSetup.swift in Sources */,
-				38A43598262E0E4900E80935 /* FetchAnnouncementsManager.swift in Sources */,
 				DD1745442C55C60E00211FAC /* AutosensSettingsDataFlow.swift in Sources */,
 				BDCAF2382C639F35002DC907 /* SettingItems.swift in Sources */,
 				58D08B342C8DF9A700AA37D3 /* CobIobChart.swift in Sources */,
@@ -3685,7 +3674,6 @@
 				DD1745242C55526000211FAC /* SMBSettingsStateModel.swift in Sources */,
 				F90692D1274B99B60037068D /* HealthKitProvider.swift in Sources */,
 				19F95FF729F10FEE00314DDC /* StatStateModel.swift in Sources */,
-				385CEAC125F2EA52002D6D5B /* Announcement.swift in Sources */,
 				8B759CFCF47B392BB365C251 /* BasalProfileEditorDataFlow.swift in Sources */,
 				195D80B42AF6973A00D25097 /* DynamicSettingsRootView.swift in Sources */,
 				389442CB25F65F7100FA1F27 /* NightscoutTreatment.swift in Sources */,

+ 0 - 1
FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json

@@ -1,7 +1,6 @@
 {
   "units" : "mg/dL",
   "closedLoop" : false,
-  "allowAnnouncements" : false,
   "isUploadEnabled" : false,
   "isDownloadEnabled" : false,
   "useLocalGlucoseSource" : false,

+ 0 - 97
FreeAPS/Sources/APS/APSManager.swift

@@ -26,7 +26,6 @@ protocol APSManager {
     func roundBolus(amount: Decimal) -> Decimal
     var lastError: CurrentValueSubject<Error?, Never> { get }
     func cancelBolus() async
-    func enactAnnouncement(_ announcement: Announcement)
 }
 
 enum APSError: LocalizedError {
@@ -62,7 +61,6 @@ final class BaseAPSManager: APSManager, Injectable {
     @Injected() private var alertHistoryStorage: AlertHistoryStorage!
     @Injected() private var tempTargetsStorage: TempTargetsStorage!
     @Injected() private var carbsStorage: CarbsStorage!
-    @Injected() private var announcementsStorage: AnnouncementsStorage!
     @Injected() private var determinationStorage: DeterminationStorage!
     @Injected() private var deviceDataManager: DeviceDataManager!
     @Injected() private var nightscout: NightscoutManager!
@@ -520,101 +518,6 @@ final class BaseAPSManager: APSManager, Injectable {
         }
     }
 
-    func enactAnnouncement(_ announcement: Announcement) {
-        guard let action = announcement.action else {
-            warning(.apsManager, "Invalid Announcement action")
-            return
-        }
-
-        guard let pump = pumpManager else {
-            warning(.apsManager, "Pump is not set")
-            return
-        }
-
-        debug(.apsManager, "Start enact announcement: \(action)")
-
-        switch action {
-        case let .bolus(amount):
-            if let error = verifyStatus() {
-                processError(error)
-                return
-            }
-            let roundedAmount = pump.roundToSupportedBolusVolume(units: Double(amount))
-            pump.enactBolus(units: roundedAmount, activationType: .manualRecommendationAccepted) { error in
-                if let error = error {
-                    // warning(.apsManager, "Announcement Bolus failed with error: \(error.localizedDescription)")
-                    switch error {
-                    case .uncertainDelivery:
-                        // Do not generate notification on uncertain delivery error
-                        break
-                    default:
-                        // Do not generate notifications for automatic boluses that fail.
-                        warning(.apsManager, "Announcement Bolus failed with error: \(error.localizedDescription)")
-                    }
-
-                } else {
-                    debug(.apsManager, "Announcement Bolus succeeded")
-                    self.announcementsStorage.storeAnnouncements([announcement], enacted: true)
-                    self.bolusProgress.send(0)
-                }
-            }
-        case let .pump(pumpAction):
-            switch pumpAction {
-            case .suspend:
-                if let error = verifyStatus() {
-                    processError(error)
-                    return
-                }
-                pump.suspendDelivery { error in
-                    if let error = error {
-                        debug(.apsManager, "Pump not suspended by Announcement: \(error.localizedDescription)")
-                    } else {
-                        debug(.apsManager, "Pump suspended by Announcement")
-                        self.announcementsStorage.storeAnnouncements([announcement], enacted: true)
-                    }
-                }
-            case .resume:
-                guard pump.status.pumpStatus.suspended else {
-                    return
-                }
-                pump.resumeDelivery { error in
-                    if let error = error {
-                        warning(.apsManager, "Pump not resumed by Announcement: \(error.localizedDescription)")
-                    } else {
-                        debug(.apsManager, "Pump resumed by Announcement")
-                        self.announcementsStorage.storeAnnouncements([announcement], enacted: true)
-                    }
-                }
-            }
-        case let .looping(closedLoop):
-            settings.closedLoop = closedLoop
-            debug(.apsManager, "Closed loop \(closedLoop) by Announcement")
-            announcementsStorage.storeAnnouncements([announcement], enacted: true)
-        case let .tempbasal(rate, duration):
-            if let error = verifyStatus() {
-                processError(error)
-                return
-            }
-            // unable to do temp basal during manual temp basal 😁
-            if isManualTempBasal {
-                processError(APSError.manualBasalTemp(message: "Loop not possible during the manual basal temp"))
-                return
-            }
-            guard !settings.closedLoop else {
-                return
-            }
-            let roundedRate = pump.roundToSupportedBasalRate(unitsPerHour: Double(rate))
-            pump.enactTempBasal(unitsPerHour: roundedRate, for: TimeInterval(duration) * 60) { error in
-                if let error = error {
-                    warning(.apsManager, "Announcement TempBasal failed with error: \(error.localizedDescription)")
-                } else {
-                    debug(.apsManager, "Announcement TempBasal succeeded")
-                    self.announcementsStorage.storeAnnouncements([announcement], enacted: true)
-                }
-            }
-        }
-    }
-
     private func fetchCurrentTempBasal(date: Date) async -> TempBasal {
         let results = await CoreDataStack.shared.fetchEntitiesAsync(
             ofType: PumpEventStored.self,

+ 0 - 52
FreeAPS/Sources/APS/FetchAnnouncementsManager.swift

@@ -1,52 +0,0 @@
-import Combine
-import Foundation
-import SwiftDate
-import Swinject
-
-protocol FetchAnnouncementsManager {}
-
-final class BaseFetchAnnouncementsManager: FetchAnnouncementsManager, Injectable {
-    private let processQueue = DispatchQueue(label: "BaseFetchAnnouncementsManager.processQueue")
-    @Injected() var announcementsStorage: AnnouncementsStorage!
-    @Injected() var nightscoutManager: NightscoutManager!
-    @Injected() var apsManager: APSManager!
-    @Injected() var settingsManager: SettingsManager!
-
-    private var lifetime = Lifetime()
-    private let timer = DispatchTimer(timeInterval: 5.minutes.timeInterval)
-
-    init(resolver: Resolver) {
-        injectServices(resolver)
-        subscribe()
-    }
-
-    private func subscribe() {
-        timer.publisher
-            .receive(on: processQueue)
-            .flatMap { _ -> AnyPublisher<[Announcement], Never> in
-                guard self.settingsManager.settings.allowAnnouncements else {
-                    return Just([]).eraseToAnyPublisher()
-                }
-                debug(.nightscout, "FetchAnnouncementsManager heartbeat")
-                debug(.nightscout, "Start fetching announcements")
-                return self.nightscoutManager.fetchAnnouncements()
-            }
-            .sink { announcements in
-                guard let last = announcements.filter({ $0.createdAt > self.announcementsStorage.syncDate() })
-                    .sorted(by: { $0.createdAt < $1.createdAt })
-                    .last
-                else { return }
-
-                self.announcementsStorage.storeAnnouncements([last], enacted: false)
-                if self.settingsManager.settings.allowAnnouncements, let recent = self.announcementsStorage.recent(),
-                   recent.action != nil
-                {
-                    debug(.nightscout, "New announcements found")
-                    self.apsManager.enactAnnouncement(recent)
-                }
-            }
-            .store(in: &lifetime)
-        timer.fire()
-        timer.resume()
-    }
-}

+ 0 - 2
FreeAPS/Sources/APS/OpenAPS/Constants.swift

@@ -92,8 +92,6 @@ extension OpenAPS {
 
     enum FreeAPS {
         static let settings = "freeaps/freeaps_settings.json"
-        static let announcements = "freeaps/announcements.json"
-        static let announcementsEnacted = "freeaps/announcements_enacted.json"
         static let tempTargetsPresets = "freeaps/temptargets_presets.json"
         static let calibrations = "freeaps/calibrations.json"
     }

+ 0 - 80
FreeAPS/Sources/APS/Storage/AnnouncementsStorage.swift

@@ -1,80 +0,0 @@
-import Foundation
-import SwiftDate
-import Swinject
-
-protocol AnnouncementsStorage {
-    func storeAnnouncements(_ announcements: [Announcement], enacted: Bool)
-    func syncDate() -> Date
-    func recent() -> Announcement?
-    func validate() -> [Announcement]
-}
-
-final class BaseAnnouncementsStorage: AnnouncementsStorage, Injectable {
-    enum Config {
-        static let recentInterval = 10.minutes.timeInterval
-    }
-
-    private let processQueue = DispatchQueue(label: "BaseAnnouncementsStorage.processQueue")
-    @Injected() private var storage: FileStorage!
-
-    init(resolver: Resolver) {
-        injectServices(resolver)
-    }
-
-    func storeAnnouncements(_ announcements: [Announcement], enacted: Bool) {
-        processQueue.sync {
-            let file = enacted ? OpenAPS.FreeAPS.announcementsEnacted : OpenAPS.FreeAPS.announcements
-            self.storage.transaction { storage in
-                storage.append(announcements, to: file, uniqBy: \.createdAt)
-                let uniqEvents = storage.retrieve(file, as: [Announcement].self)?
-                    .filter { $0.createdAt.addingTimeInterval(1.days.timeInterval) > Date() }
-                    .sorted { $0.createdAt > $1.createdAt } ?? []
-                storage.save(Array(uniqEvents), as: file)
-            }
-        }
-    }
-
-    func syncDate() -> Date {
-        guard let events = storage.retrieve(OpenAPS.FreeAPS.announcementsEnacted, as: [Announcement].self),
-              let recentEnacted = events.filter({ $0.enteredBy == Announcement.remote }).first
-        else {
-            return Date().addingTimeInterval(-Config.recentInterval)
-        }
-        return recentEnacted.createdAt.addingTimeInterval(Config.recentInterval)
-    }
-
-    func recent() -> Announcement? {
-        guard let events = storage.retrieve(OpenAPS.FreeAPS.announcements, as: [Announcement].self)
-        else {
-            return nil
-        }
-        guard let recent = events
-            .filter({
-                $0.enteredBy == Announcement.remote && $0.createdAt.addingTimeInterval(Config.recentInterval) > Date()
-            })
-            .first
-        else {
-            return nil
-        }
-        guard let enactedEvents = storage.retrieve(OpenAPS.FreeAPS.announcementsEnacted, as: [Announcement].self)
-        else {
-            return recent
-        }
-
-        guard enactedEvents.first(where: { $0.createdAt == recent.createdAt }) == nil
-        else {
-            return nil
-        }
-        return recent
-    }
-
-    func validate() -> [Announcement] {
-        guard let enactedEvents = storage.retrieve(OpenAPS.FreeAPS.announcementsEnacted, as: [Announcement].self)?.reversed()
-        else {
-            return []
-        }
-        let validate = enactedEvents
-            .filter({ $0.enteredBy == Announcement.remote })
-        return validate
-    }
-}

+ 1 - 1
FreeAPS/Sources/APS/Storage/OverrideStorage.swift

@@ -218,7 +218,7 @@ final class BaseOverrideStorage: @preconcurrency OverrideStorage, Injectable {
             guard let fetchedOverrides = results as? [OverrideStored] else { return [] }
 
             return fetchedOverrides.map { override in
-                let duration = override.indefinite ? 1440 : override.duration ?? 0 // 1440 min = 1 day
+                let duration = override.indefinite ? 43200 : override.duration ?? 0 // 43200 min = 30 days
                 return NightscoutExercise(
                     duration: Int(truncating: duration),
                     eventType: OverrideStored.EventType.nsExercise,

+ 0 - 1
FreeAPS/Sources/Application/FreeAPSApp.swift

@@ -46,7 +46,6 @@ import Swinject
         _ = resolver.resolve(APSManager.self)!
         _ = resolver.resolve(FetchGlucoseManager.self)!
         _ = resolver.resolve(FetchTreatmentsManager.self)!
-        _ = resolver.resolve(FetchAnnouncementsManager.self)!
         _ = resolver.resolve(CalendarManager.self)!
         _ = resolver.resolve(UserNotificationsManager.self)!
         _ = resolver.resolve(WatchManager.self)!

+ 0 - 1
FreeAPS/Sources/Assemblies/APSAssembly.swift

@@ -7,7 +7,6 @@ final class APSAssembly: Assembly {
         container.register(APSManager.self) { r in BaseAPSManager(resolver: r) }
         container.register(FetchGlucoseManager.self) { r in BaseFetchGlucoseManager(resolver: r) }
         container.register(FetchTreatmentsManager.self) { r in BaseFetchTreatmentsManager(resolver: r) }
-        container.register(FetchAnnouncementsManager.self) { r in BaseFetchAnnouncementsManager(resolver: r) }
         container.register(BluetoothStateManager.self) { r in BaseBluetoothStateManager(resolver: r) }
         container.register(PluginManager.self) { r in BasePluginManager(resolver: r) }
         container.register(CalibrationService.self) { r in BaseCalibrationService(resolver: r) }

+ 0 - 1
FreeAPS/Sources/Assemblies/StorageAssembly.swift

@@ -14,7 +14,6 @@ final class StorageAssembly: Assembly {
         container.register(TempTargetsStorage.self) { r in BaseTempTargetsStorage(resolver: r) }
         container.register(CarbsStorage.self) { r in BaseCarbsStorage(resolver: r) }
         container.register(ContactImageStorage.self) { r in BaseContactImageStorage(resolver: r) }
-        container.register(AnnouncementsStorage.self) { r in BaseAnnouncementsStorage(resolver: r) }
         container.register(SettingsManager.self) { r in BaseSettingsManager(resolver: r) }
         container.register(Keychain.self) { _ in BaseKeychain() }
         container.register(AlertHistoryStorage.self) { r in BaseAlertHistoryStorage(resolver: r) }

+ 0 - 6
FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings

@@ -1235,12 +1235,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Enacted";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Announcements";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Enacted announcements";
-
 /* Debug option view Autotune */
 "Autotune" = "Autotune";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings

@@ -1086,12 +1086,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Enacted";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Announcements";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Enacted announcements";
-
 /* Debug option view Autotune */
 "Autotune" = "Autotune";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings

@@ -1196,12 +1196,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Udført";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Announcements";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Enacted announcements";
-
 /* Debug option view Autotune */
 "Autotune" = "Autotune";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings

@@ -1241,12 +1241,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "letzte Berechnung";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Notifikationen";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Erlassene Ankündigungen";
-
 /* Debug option view Autotune */
 "Autotune" = "Autotune";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings

@@ -1238,12 +1238,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Enacted";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Announcements";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Enacted announcements";
-
 /* Debug option view Autotune */
 "Autotune" = "Autotune";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings

@@ -1234,12 +1234,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Enacted";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Announcements";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Enacted announcements";
-
 /* Debug option view Autotune */
 "Autotune" = "Autotune";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings

@@ -1241,12 +1241,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Enacted";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Announcements";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Enacted announcements";
-
 /* Debug option view Autotune */
 "Autotune" = "Autotune";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings

@@ -1235,12 +1235,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Activé";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Annonces";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Annonces émises";
-
 /* Debug option view Autotune */
 "Autotune" = "Autotune";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings

@@ -1235,12 +1235,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Enacted";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Announcements";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Enacted announcements";
-
 /* Debug option view Autotune */
 "Autotune" = "Autotune";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/hu.lproj/Localizable.strings

@@ -1193,12 +1193,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Enacted";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Announcements";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Enacted announcements";
-
 /* Debug option view Autotune */
 "Autotune" = "Autotune";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings

@@ -1235,12 +1235,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Debug Attivato";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Annunci";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Annunci attivati";
-
 /* Debug option view Autotune */
 "Autotune" = "Autotune";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings

@@ -1235,12 +1235,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Utført";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Kunngjøringer";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Utførte kunngjøringer";
-
 /* Debug option view Autotune */
 "Autotune" = "Autotune";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings

@@ -1235,12 +1235,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Vastgesteld";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Meldingen";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Ingevoerde meldingen";
-
 /* Debug option view Autotune */
 "Autotune" = "Autotune";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings

@@ -1237,12 +1237,6 @@ Połączono z Nightscout!";
 /* Debug option view Enacted */
 "Enacted" = "Enacted";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Announcements";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Enacted announcements";
-
 /* Debug option view Autotune */
 "Autotune" = "Autotune";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings

@@ -1235,12 +1235,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Enacted";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Announcements";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Enacted announcements";
-
 /* Debug option view Autotune */
 "Autotune" = "Autotune";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings

@@ -1235,12 +1235,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Enacted";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Announcements";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Enacted announcements";
-
 /* Debug option view Autotune */
 "Autotune" = "Autotune";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings

@@ -1238,12 +1238,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Принято";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Оповещения";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Принятые оповещения";
-
 /* Debug option view Autotune */
 "Autotune" = "Автотюн";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings

@@ -1193,12 +1193,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Prijaté";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Oznámenia";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Prijaté oznámenia";
-
 /* Debug option view Autotune */
 "Autotune" = "Autotune";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings

@@ -1235,12 +1235,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Utfört";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Meddelanden (via NS)";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Överförda meddelanden";
-
 /* Debug option view Autotune */
 "Autotune" = "Autotune";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings

@@ -1239,12 +1239,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Enacted";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Duyurular";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Enacted announcements";
-
 /* Debug option view Autotune */
 "Autotune" = "Otoayar";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings

@@ -1235,12 +1235,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Введено в дію";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Оголошення";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Введені в дію оголошення";
-
 /* Debug option view Autotune */
 "Autotune" = "Автотюн";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/vi.lproj/Localizable.strings

@@ -1193,12 +1193,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Đã kích hoạt";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Các thông báo";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Thông báo đã kích hoạt";
-
 /* Debug option view Autotune */
 "Autotune" = "Autotune";
 

+ 0 - 6
FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings

@@ -1235,12 +1235,6 @@ Enact a temp Basal or a temp target */
 /* Debug option view Enacted */
 "Enacted" = "Enacted";
 
-/* Debug option view Announcements (from NS) */
-"Announcements" = "Announcements";
-
-/* Debug option view Enacted announcements announcements (from NS) */
-"Enacted announcements" = "Enacted announcements";
-
 /* Debug option view Autotune */
 "Autotune" = "自动调谐";
 

+ 0 - 57
FreeAPS/Sources/Models/Announcement.swift

@@ -1,57 +0,0 @@
-import Foundation
-
-struct Announcement: JSON, Equatable, Hashable {
-    let createdAt: Date
-    let enteredBy: String
-    let notes: String
-
-    static let remote = "remote"
-
-    var action: AnnouncementAction? {
-        let components = notes.replacingOccurrences(of: " ", with: "").split(separator: ":")
-        guard components.count == 2 else {
-            return nil
-        }
-        let command = String(components[0]).lowercased()
-        let arguments = String(components[1]).lowercased()
-        switch command {
-        case "bolus":
-            guard let amount = Decimal(from: arguments) else { return nil }
-            return .bolus(amount)
-        case "pump":
-            guard let action = PumpAction(rawValue: arguments) else { return nil }
-            return .pump(action)
-        case "looping":
-            guard let looping = Bool(from: arguments) else { return nil }
-            return .looping(looping)
-        case "tempbasal":
-            let basalComponents = arguments.split(separator: ",")
-            guard basalComponents.count == 2 else { return nil }
-            let rateArg = String(basalComponents[0])
-            let durationArg = String(basalComponents[1])
-            guard let rate = Decimal(from: rateArg), let duration = Decimal(from: durationArg) else { return nil }
-            return .tempbasal(rate: rate, duration: duration)
-        default: return nil
-        }
-    }
-}
-
-extension Announcement {
-    private enum CodingKeys: String, CodingKey {
-        case createdAt = "created_at"
-        case enteredBy
-        case notes
-    }
-}
-
-enum AnnouncementAction {
-    case bolus(Decimal)
-    case pump(PumpAction)
-    case looping(Bool)
-    case tempbasal(rate: Decimal, duration: Decimal)
-}
-
-enum PumpAction: String {
-    case suspend
-    case resume
-}

+ 0 - 5
FreeAPS/Sources/Models/FreeAPSSettings.swift

@@ -18,7 +18,6 @@ enum BolusShortcutLimit: String, JSON, CaseIterable, Identifiable {
 struct FreeAPSSettings: JSON, Equatable {
     var units: GlucoseUnits = .mgdL
     var closedLoop: Bool = false
-    var allowAnnouncements: Bool = false
     var isUploadEnabled: Bool = false
     var isDownloadEnabled: Bool = false
     var useLocalGlucoseSource: Bool = false
@@ -92,10 +91,6 @@ extension FreeAPSSettings: Decodable {
             settings.closedLoop = closedLoop
         }
 
-        if let allowAnnouncements = try? container.decode(Bool.self, forKey: .allowAnnouncements) {
-            settings.allowAnnouncements = allowAnnouncements
-        }
-
         if let isUploadEnabled = try? container.decode(Bool.self, forKey: .isUploadEnabled) {
             settings.isUploadEnabled = isUploadEnabled
         }

+ 0 - 1
FreeAPS/Sources/Modules/Home/HomeDataFlow.swift

@@ -12,6 +12,5 @@ protocol HomeProvider: Provider {
     func tempTargets(hours: Int) -> [TempTarget]
     func pumpReservoir() -> Decimal?
     func tempTarget() -> TempTarget?
-    func announcement(_ hours: Int) -> [Announcement]
     func getBGTarget() async -> BGTargets
 }

+ 0 - 7
FreeAPS/Sources/Modules/Home/HomeProvider.swift

@@ -7,7 +7,6 @@ extension Home {
         @Injected() var apsManager: APSManager!
         @Injected() var glucoseStorage: GlucoseStorage!
         @Injected() var tempTargetsStorage: TempTargetsStorage!
-        @Injected() var announcementStorage: AnnouncementsStorage!
 
         func pumpTimeZone() -> TimeZone? {
             apsManager.pumpManager?.status.timeZone
@@ -27,12 +26,6 @@ extension Home {
             tempTargetsStorage.current()
         }
 
-        func announcement(_ hours: Int) -> [Announcement] {
-            announcementStorage.validate().filter {
-                $0.createdAt.addingTimeInterval(hours.hours.timeInterval) > Date()
-            }
-        }
-
         func pumpSettings() -> PumpSettings {
             storage.retrieve(OpenAPS.Settings.settings, as: PumpSettings.self)
                 ?? PumpSettings(from: OpenAPS.defaults(for: OpenAPS.Settings.settings))

+ 0 - 1
FreeAPS/Sources/Modules/Home/HomeStateModel.swift

@@ -20,7 +20,6 @@ extension Home {
         private let timer = DispatchTimer(timeInterval: 5)
         private(set) var filteredHours = 24
         var manualGlucose: [BloodGlucose] = []
-        var announcement: [Announcement] = []
         var uploadStats = false
         var recentGlucose: BloodGlucose?
         var maxBasal: Decimal = 2

+ 45 - 14
FreeAPS/Sources/Modules/Home/View/Chart/ChartElements/BasalChart.swift

@@ -153,7 +153,7 @@ extension MainChartView {
         return tempBasals.compactMap { temp -> (start: Date, end: Date, rate: Double)? in
             let duration = temp.tempBasal?.duration ?? 0
             let timestamp = temp.timestamp ?? Date()
-            let end = min(timestamp + duration.minutes, now)
+            let end = timestamp + duration.minutes
             let isInsulinSuspended = state.suspensions.contains { $0.timestamp ?? now >= timestamp && $0.timestamp ?? now <= end }
 
             let rate = Double(truncating: temp.tempBasal?.rate ?? Decimal.zero as NSDecimalNumber) * (isInsulinSuspended ? 0 : 1)
@@ -202,25 +202,56 @@ extension MainChartView {
         Task {
             let dayAgoTime = Date().addingTimeInterval(-1.days.timeInterval).timeIntervalSince1970
 
-            // Get Regular Basal
-            let basalPoints = await findRegularBasalPoints(
+            async let getRegularBasalPoints = findRegularBasalPoints(
                 timeBegin: dayAgoTime,
                 timeEnd: endMarker.timeIntervalSince1970
             )
 
-            var totalBasal = basalPoints
-            totalBasal.sort {
-                $0.startDate.timeIntervalSince1970 < $1.startDate.timeIntervalSince1970
-            }
+            var regularPoints = await getRegularBasalPoints
+            regularPoints.sort { $0.startDate < $1.startDate }
 
             var basals: [BasalProfile] = []
-            totalBasal.indices.forEach { index in
-                basals.append(BasalProfile(
-                    amount: totalBasal[index].amount,
-                    isOverwritten: totalBasal[index].isOverwritten,
-                    startDate: totalBasal[index].startDate,
-                    endDate: totalBasal.count > index + 1 ? totalBasal[index + 1].startDate : endMarker
-                ))
+
+            // No basal data? Then there's nothing to draw
+            if regularPoints.isEmpty {
+                // basals stays empty; do nothing
+            }
+            // Exactly one data point?
+            else if regularPoints.count == 1 {
+                let single = regularPoints[0]
+                // Make one BasalProfile that stretches entire marker area
+                basals.append(
+                    BasalProfile(
+                        amount: single.amount,
+                        isOverwritten: single.isOverwritten,
+                        startDate: startMarker,
+                        endDate: endMarker
+                    )
+                )
+            }
+            // Multiple data points: chain them so each point ends where the next begins
+            else {
+                for i in 0 ..< (regularPoints.count - 1) {
+                    basals.append(
+                        BasalProfile(
+                            amount: regularPoints[i].amount,
+                            isOverwritten: regularPoints[i].isOverwritten,
+                            startDate: regularPoints[i].startDate,
+                            endDate: regularPoints[i + 1].startDate
+                        )
+                    )
+                }
+                // The last item goes from its start to endMarker
+                if let lastItem = regularPoints.last {
+                    basals.append(
+                        BasalProfile(
+                            amount: lastItem.amount,
+                            isOverwritten: lastItem.isOverwritten,
+                            startDate: lastItem.startDate,
+                            endDate: endMarker
+                        )
+                    )
+                }
             }
 
             await MainActor.run {

+ 1 - 1
FreeAPS/Sources/Modules/Home/View/Chart/ChartElements/OverrideView.swift

@@ -24,7 +24,7 @@ struct OverrideView: ChartContent {
                 context: viewContext
             ) ?? 0
             let end: Date = duration != 0 ? start.addingTimeInterval(duration) : start
-                .addingTimeInterval(6 * 60 * 60) // handle infinite overrides
+                .addingTimeInterval(60 * 60 * 24 * 30) // handle infinite overrides -> 60s x 60m x 24h x 30d = 30 days duration
 
             let target = getOverrideTarget(override: override)
 

+ 5 - 2
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -732,7 +732,10 @@ extension Home {
 
                 }.padding(.horizontal, 10).padding(.bottom, UIDevice.adjustPadding(min: nil, max: 10))
                     .overlay(alignment: .bottom) {
-                        bolusProgressBar(progress).padding(.horizontal, 18).offset(y: 48)
+                        // Use a geo-based offset here to position progress bar independent of device size
+                        let offset = geo.size.height * 0.0725
+                        bolusProgressBar(progress).padding(.horizontal, 18)
+                            .offset(y: offset)
                     }.clipShape(RoundedRectangle(cornerRadius: 15))
             }
         }
@@ -952,7 +955,7 @@ extension Home {
                             .font(.system(size: 40))
                             .foregroundStyle(Color.tabBar)
                             .padding(.bottom, 1)
-                            .padding(.horizontal, 20)
+                            .padding(.horizontal, 22.5)
                     }
                 )
             }.ignoresSafeArea(.keyboard, edges: .bottom).blur(radius: state.waitForSuggestion ? 8 : 0)

+ 0 - 2
FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigStateModel.swift

@@ -32,7 +32,6 @@ extension NightscoutConfig {
         @Published var dia: Decimal = 6
         @Published var maxBasal: Decimal = 2
         @Published var maxBolus: Decimal = 10
-        @Published var allowAnnouncements: Bool = false
         @Published var isConnectedToNS: Bool = false
 
         @Published var isImportResultReviewPresented: Bool = false
@@ -57,7 +56,6 @@ extension NightscoutConfig {
             maxBolus = settingsManager.pumpSettings.maxBolus
             changeUploadGlucose = (cgmManager.cgmGlucoseSourceType != CGMType.plugin)
 
-            subscribeSetting(\.allowAnnouncements, on: $allowAnnouncements) { allowAnnouncements = $0 }
             subscribeSetting(\.isUploadEnabled, on: $isUploadEnabled) { isUploadEnabled = $0 }
             subscribeSetting(\.isDownloadEnabled, on: $isDownloadEnabled) { isDownloadEnabled = $0 }
             subscribeSetting(\.useLocalGlucoseSource, on: $useLocalSource) { useLocalSource = $0 }

+ 1 - 1
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConfigRootView.swift

@@ -41,7 +41,7 @@ extension NightscoutConfig {
                                 }
                             })
                             NavigationLink("Upload", destination: NightscoutUploadView(state: state))
-                            NavigationLink("Fetch & Remote Control", destination: NightscoutFetchView(state: state))
+                            NavigationLink("Fetch", destination: NightscoutFetchView(state: state))
                         }
                     ).listRowBackground(Color.chart)
 

+ 2 - 35
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutFetchView.swift

@@ -37,41 +37,8 @@ struct NightscoutFetchView: View {
                         "The Fetch Treatments toggle enables fetching of carbs and temp targets entered in Careportal or by another uploading device than Trio from Nightscout."
                     )
                 },
-                headerText: "Remote & Fetch Capabilities"
+                headerText: "Fetch NS Care Portal Data"
             )
-
-            if state.isDownloadEnabled {
-                SettingInputSection(
-                    decimalValue: $decimalPlaceholder,
-                    booleanValue: $state.allowAnnouncements,
-                    shouldDisplayHint: $shouldDisplayHint,
-                    selectedVerboseHint: Binding(
-                        get: { selectedVerboseHint },
-                        set: {
-                            selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Allow Remote Control of Trio"
-                        }
-                    ),
-                    units: state.units,
-                    type: .boolean,
-                    label: "Allow Remote Control of Trio",
-                    miniHint: "Enables selected remote control capabilities via Nightscout.",
-                    verboseHint: VStack(alignment: .leading, spacing: 10) {
-                        Text("Default: OFF").bold()
-                        Text("When enabled you allow the following remote functions through announcements from Nightscout:")
-                        VStack(alignment: .leading) {
-                            Text("• Suspend/Resume Pump")
-                            Text("• Opening/Closing Loop")
-                            Text("• Set Temp Basal")
-                            Text("• Enact Bolus")
-                        }
-                    }
-                )
-            } else {
-                Section {
-                    Text("'Allow Fetching from Nightscout' must be enabled to allow for Trio Remote Control.")
-                }.listRowBackground(Color.tabBar)
-            }
         }
         .listSectionSpacing(sectionSpacing)
         .sheet(isPresented: $shouldDisplayHint) {
@@ -83,7 +50,7 @@ struct NightscoutFetchView: View {
                 sheetTitle: "Help"
             )
         }
-        .navigationTitle("Fetch & Remote")
+        .navigationTitle("Fetch")
         .navigationBarTitleDisplayMode(.automatic)
         .scrollContentBackground(.hidden)
         .background(appState.trioBackgroundColor(for: colorScheme))

+ 0 - 4
FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift

@@ -266,10 +266,6 @@ extension Settings {
 //                                .navigationLink(to: .configEditor(file: OpenAPS.Settings.profile), from: self)
 //                            //                            Text("Carbs")
 //                            //                                .navigationLink(to: .configEditor(file: OpenAPS.Monitor.carbHistory), from: self)
-//                            //                            Text("Announcements")
-//                            //                                .navigationLink(to: .configEditor(file: OpenAPS.FreeAPS.announcements), from: self)
-//                            //                            Text("Enacted announcements")
-//                            //                                .navigationLink(to: .configEditor(file: OpenAPS.FreeAPS.announcementsEnacted), from: self)
 //                        }
 //
 //                        Group {

+ 24 - 0
FreeAPS/Sources/Modules/Treatments/TreatmentsStateModel.swift

@@ -126,6 +126,9 @@ extension Treatments {
 
         typealias PumpEvent = PumpEventStored.EventType
 
+        var isBolusInProgress: Bool = false
+        private var bolusProgressCancellable: AnyCancellable?
+
         func unsubscribe() {
             subscriptions.forEach { $0.cancel() }
             subscriptions.removeAll()
@@ -145,6 +148,7 @@ extension Treatments {
             registerHandlers()
             registerSubscribers()
             setupBolusStateConcurrently()
+            subscribeToBolusProgress()
         }
 
         deinit {
@@ -155,6 +159,8 @@ extension Treatments {
             // Cancel Combine subscriptions
             unsubscribe()
 
+            bolusProgressCancellable?.cancel()
+
             debug(.bolusState, "Bolus.StateModel deinitialized")
         }
 
@@ -191,6 +197,24 @@ extension Treatments {
             }
         }
 
+        /// Observes changes to the `bolusProgress` published by the `apsManager` to update the `isBolusInProgress` property in real time.
+        ///
+        /// - Important:
+        ///   - `apsManager.bolusProgress` is a `CurrentValueSubject<Decimal?, Never>`.
+        ///   - When a bolus starts, this subject emits `0` (or a fraction like `0.1, 0.5, etc.`).
+        ///   - When the bolus finishes, the subject is typically set to `nil`.
+        ///   - This treats ANY non-nil value as “bolus in progress.”
+        ///
+        private func subscribeToBolusProgress() {
+            bolusProgressCancellable = apsManager.bolusProgress
+                .receive(on: DispatchQueue.main)
+                .sink { [weak self] progressValue in
+                    guard let self = self else { return }
+                    // If progressValue is non-nil, a bolus is in progress.
+                    self.isBolusInProgress = (progressValue != nil)
+                }
+        }
+
         // MARK: - Basal
 
         private enum SettingType {

+ 31 - 13
FreeAPS/Sources/Modules/Treatments/View/TreatmentsRootView.swift

@@ -365,28 +365,38 @@ extension Treatments {
         }
 
         var treatmentButton: some View {
-            Button {
+            var treatmentButtonBackground = Color(.systemBlue)
+            if limitExceeded {
+                treatmentButtonBackground = Color(.systemRed)
+            } else if disableTaskButton {
+                treatmentButtonBackground = Color(.systemGray)
+            }
+
+            return Button {
                 state.invokeTreatmentsTask()
             } label: {
-                taskButtonLabel
-                    .font(.headline)
-                    .foregroundStyle(Color.white)
-                    .frame(maxWidth: .infinity, alignment: .center)
-                    .frame(height: 35)
+                HStack {
+                    if state.isBolusInProgress && state
+                        .amount > 0 && !state.externalInsulin && (state.carbs == 0 || state.fat == 0 || state.protein == 0)
+                    {
+                        ProgressView()
+                    }
+                    taskButtonLabel
+                }
+                .font(.headline)
+                .foregroundStyle(Color.white)
+                .frame(maxWidth: .infinity, alignment: .center)
+                .frame(height: 35)
             }
             .disabled(disableTaskButton)
-            .listRowBackground(
-                limitExceeded ? Color(.systemRed) :
-                    disableTaskButton ? Color(.systemGray) :
-                    Color(.systemBlue)
-            )
+            .listRowBackground(treatmentButtonBackground)
             .shadow(radius: 3)
             .clipShape(RoundedRectangle(cornerRadius: 8))
         }
 
         private var taskButtonLabel: some View {
             if pumpBolusLimitExceeded {
-                return Text("Max Bolus of \(state.maxBolus.description) U Exceeded")
+                return Text("Max Bolus of \(state.maxBolus.description) U E== 0xceeded")
             } else if externalBolusLimitExceeded {
                 return Text("Max External Bolus of \(state.maxExternal.description) U Exceeded")
             } else if carbLimitExceeded {
@@ -402,6 +412,10 @@ extension Treatments {
             let hasFatOrProtein = state.fat > 0 || state.protein > 0
             let bolusString = state.externalInsulin ? "External Insulin" : "Enact Bolus"
 
+            if state.isBolusInProgress && hasInsulin && !state.externalInsulin && (!hasCarbs || !hasFatOrProtein) {
+                return Text("Bolus In Progress...")
+            }
+
             switch (hasInsulin, hasCarbs, hasFatOrProtein) {
             case (true, true, true):
                 return Text("Log Meal and \(bolusString)")
@@ -447,7 +461,11 @@ extension Treatments {
         }
 
         private var disableTaskButton: Bool {
-            state.addButtonPressed || limitExceeded
+            (
+                state.isBolusInProgress && state
+                    .amount > 0 && !state.externalInsulin && (state.carbs == 0 || state.fat == 0 || state.protein == 0)
+            ) || state
+                .addButtonPressed || limitExceeded
         }
     }
 

+ 0 - 35
FreeAPS/Sources/Services/Network/Nightscout/NightscoutAPI.swift

@@ -286,41 +286,6 @@ extension NightscoutAPI {
         }
     }
 
-    func fetchAnnouncement(sinceDate: Date? = nil) -> AnyPublisher<[Announcement], Swift.Error> {
-        var components = URLComponents()
-        components.scheme = url.scheme
-        components.host = url.host
-        components.port = url.port
-        components.path = Config.treatmentsPath
-        components.queryItems = [
-            URLQueryItem(name: "find[eventType]", value: "Announcement"),
-            URLQueryItem(
-                name: "find[enteredBy]",
-                value: Announcement.remote.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
-            )
-        ]
-        if let date = sinceDate {
-            let dateItem = URLQueryItem(
-                name: "find[created_at][$gte]",
-                value: Formatter.iso8601withFractionalSeconds.string(from: date)
-            )
-            components.queryItems?.append(dateItem)
-        }
-
-        var request = URLRequest(url: components.url!)
-        request.allowsConstrainedNetworkAccess = false
-        request.timeoutInterval = Config.timeout
-
-        if let secret = secret {
-            request.addValue(secret.sha1(), forHTTPHeaderField: "api-secret")
-        }
-
-        return service.run(request)
-            .retry(Config.retryCount)
-            .decode(type: [Announcement].self, decoder: JSONCoding.decoder)
-            .eraseToAnyPublisher()
-    }
-
     func uploadTreatments(_ treatments: [NightscoutTreatment]) async throws {
         var components = URLComponents()
         components.scheme = url.scheme

+ 0 - 16
FreeAPS/Sources/Services/Network/Nightscout/NightscoutManager.swift

@@ -9,7 +9,6 @@ protocol NightscoutManager: GlucoseSource {
     func fetchGlucose(since date: Date) async -> [BloodGlucose]
     func fetchCarbs() async -> [CarbsEntry]
     func fetchTempTargets() async -> [TempTarget]
-    func fetchAnnouncements() -> AnyPublisher<[Announcement], Never>
     func deleteCarbs(withID id: String) async
     func deleteInsulin(withID id: String) async
     func deleteManualGlucose(withID id: String) async
@@ -35,7 +34,6 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
     @Injected() private var carbsStorage: CarbsStorage!
     @Injected() private var pumpHistoryStorage: PumpHistoryStorage!
     @Injected() private var storage: FileStorage!
-    @Injected() private var announcementsStorage: AnnouncementsStorage!
     @Injected() private var settingsManager: SettingsManager!
     @Injected() private var broadcaster: Broadcaster!
     @Injected() private var reachabilityManager: ReachabilityManager!
@@ -319,23 +317,9 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
         }
     }
 
-    func fetchAnnouncements() -> AnyPublisher<[Announcement], Never> {
-        guard let nightscout = nightscoutAPI, isNetworkReachable, isDownloadEnabled else {
-            return Just([]).eraseToAnyPublisher()
-        }
-
-        let since = announcementsStorage.syncDate()
-        return nightscout.fetchAnnouncement(sinceDate: since)
-            .replaceError(with: [])
-            .eraseToAnyPublisher()
-    }
-
     func deleteCarbs(withID id: String) async {
         guard let nightscout = nightscoutAPI, isUploadEnabled else { return }
 
-        // TODO: - healthkit rewrite, deletion of FPUs
-//        healthkitManager.deleteCarbs(syncID: arg1, fpuID: arg2)
-
         do {
             try await nightscout.deleteCarbs(withId: id)
             debug(.nightscout, "Carbs deleted")

+ 0 - 1
LiveActivity/Views/WidgetItems/LiveActivityIOBLabelView.swift

@@ -16,7 +16,6 @@ struct LiveActivityIOBLabelView: View {
         let formatter = NumberFormatter()
         formatter.numberStyle = .decimal
         formatter.maximumFractionDigits = 1
-        formatter.decimalSeparator = "."
         return formatter
     }