Skip to content
2 changes: 1 addition & 1 deletion Common/Models/PumpManagerUI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import LoopKitUI

typealias PumpManagerHUDViewRawValue = [String: Any]

func PumpManagerHUDViewFromRawValue(_ rawValue: PumpManagerHUDViewRawValue, pluginManager: PluginManager) -> LevelHUDView? {
func PumpManagerHUDViewFromRawValue(_ rawValue: PumpManagerHUDViewRawValue, pluginManager: PluginManager) -> BaseHUDView? {
guard
let identifier = rawValue["managerIdentifier"] as? String,
let rawState = rawValue["hudProviderView"] as? HUDProvider.HUDViewRawState,
Expand Down
2 changes: 1 addition & 1 deletion Loop Status Extension/StatusViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ class StatusViewController: UIViewController, NCWidgetProviding {
}

// Pump Status
let pumpManagerHUDView: LevelHUDView
let pumpManagerHUDView: BaseHUDView
if let hudViewContext = context.pumpManagerHUDViewContext,
let contextHUDView = PumpManagerHUDViewFromRawValue(hudViewContext.pumpManagerHUDViewRawValue, pluginManager: self.pluginManager)
{
Expand Down
94 changes: 81 additions & 13 deletions Loop/Managers/Alerts/AlertManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,19 +106,23 @@ extension AlertManager: AlertManagerResponder {
}

func presentAcknowledgementFailedAlert(error: Error) {
let message: String
if let localizedError = error as? LocalizedError {
message = [localizedError.localizedDescription, localizedError.recoverySuggestion].compactMap({$0}).joined(separator: "\n\n")
} else {
message = String(format: NSLocalizedString("%1$@ is unable to clear the alert from your device", comment: "Message for alert shown when alert acknowledgement fails for a device, and the device does not provide a LocalizedError. (1: app name)"), Bundle.main.bundleDisplayName)
DispatchQueue.main.async {
let message: String
if let localizedError = error as? LocalizedError {
message = [localizedError.localizedDescription, localizedError.recoverySuggestion].compactMap({$0}).joined(separator: "\n\n")
} else {
message = String(format: NSLocalizedString("%1$@ is unable to clear the alert from your device", comment: "Message for alert shown when alert acknowledgement fails for a device, and the device does not provide a LocalizedError. (1: app name)"), Bundle.main.bundleDisplayName)
}
self.log.info("Alert acknowledgement failed: %{public}@", message)

let alert = UIAlertController(
title: NSLocalizedString("Unable To Clear Alert", comment: "Title for alert shown when alert acknowledgement fails"),
message: message,
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Default action for alert when alert acknowledgment fails"), style: .default))

self.alertPresenter.present(alert, animated: true)
}
let alert = UIAlertController(
title: NSLocalizedString("Unable To Clear Alert", comment: "Title for alert shown when alert acknowledgement fails"),
message: message,
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Default action for alert when alert acknowledgment fails"), style: .default))

self.alertPresenter.present(alert, animated: true)
}
}

Expand Down Expand Up @@ -186,7 +190,7 @@ extension AlertManager {
}

private func playbackAlertsFromAlertStore() {
alertStore.lookupAllUnacknowledged {
alertStore.lookupAllUnacknowledgedUnretracted {
switch $0 {
case .failure(let error):
self.log.error("Could not fetch unacknowledged alerts: %@", error.localizedDescription)
Expand Down Expand Up @@ -251,6 +255,70 @@ extension AlertManager {
}
}

// MARK: PersistedAlertStore
extension AlertManager: PersistedAlertStore {
public func doesIssuedAlertExist(identifier: Alert.Identifier, completion: @escaping (Result<Bool, Error>) -> Void) {
alertStore.lookupAllMatching(identifier: identifier) { result in
switch result {
case .success(let storedAlerts):
completion(.success(!storedAlerts.isEmpty))
case .failure(let error):
completion(.failure(error))
}
}
}

public func lookupAllUnretracted(managerIdentifier: String, completion: @escaping (Result<[PersistedAlert], Error>) -> Void) {
alertStore.lookupAllUnretracted(managerIdentifier: managerIdentifier) {
switch $0 {
case .success(let alerts):
do {
let result = try alerts.map {
PersistedAlert(
alert: try Alert(from: $0, adjustedForStorageTime: false),
issuedDate: $0.issuedDate,
retractedDate: $0.retractedDate,
acknowledgedDate: $0.acknowledgedDate
)
}
completion(.success(result))
} catch {
completion(.failure(error))
}
case .failure(let error):
completion(.failure(error))
}
}
}

public func lookupAllUnacknowledgedUnretracted(managerIdentifier: String, completion: @escaping (Result<[PersistedAlert], Error>) -> Void) {
alertStore.lookupAllUnacknowledgedUnretracted(managerIdentifier: managerIdentifier) {
switch $0 {
case .success(let alerts):
do {
let result = try alerts.map {
PersistedAlert(
alert: try Alert(from: $0, adjustedForStorageTime: false),
issuedDate: $0.issuedDate,
retractedDate: $0.retractedDate,
acknowledgedDate: $0.acknowledgedDate
)
}
completion(.success(result))
} catch {
completion(.failure(error))
}
case .failure(let error):
completion(.failure(error))
}
}
}

public func recordRetractedAlert(_ alert: Alert, at date: Date) {
alertStore.recordRetractedAlert(alert, at: date)
}
}

// MARK: Extensions

fileprivate extension SyncAlertObject {
Expand Down
73 changes: 66 additions & 7 deletions Loop/Managers/Alerts/AlertStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public class AlertStore {
}

public func recordIssued(alert: Alert, at date: Date = Date(), completion: ((Result<Void, Error>) -> Void)? = nil) {
self.managedObjectContext.perform {
self.managedObjectContext.performAndWait {
_ = StoredAlert(from: alert, context: self.managedObjectContext, issuedDate: date)
do {
try self.managedObjectContext.save()
Expand All @@ -97,6 +97,23 @@ public class AlertStore {
}
}
}

public func recordRetractedAlert(_ alert: Alert, at date: Date, completion: ((Result<Void, Error>) -> Void)? = nil) {
self.managedObjectContext.performAndWait {
let storedAlert = StoredAlert(from: alert, context: self.managedObjectContext, issuedDate: date)
storedAlert.retractedDate = date
do {
try self.managedObjectContext.save()
self.log.default("Recorded retracted alert: %{public}@", alert.identifier.value)
self.purgeExpired()
self.delegate?.alertStoreHasUpdatedAlertData(self)
completion?(.success)
} catch {
self.log.error("Could not store retracted alert: %{public}@, %{public}@", alert.identifier.value, String(describing: error))
completion?(.failure(error))
}
}
}

public func recordAcknowledgement(of identifier: Alert.Identifier, at date: Date = Date(),
completion: ((Result<Void, Error>) -> Void)? = nil) {
Expand Down Expand Up @@ -125,15 +142,57 @@ public class AlertStore {
},
completion: completion)
}
public func lookupAllUnacknowledged(completion: @escaping (Result<[StoredAlert], Error>) -> Void) {

public func lookupAllMatching(identifier: Alert.Identifier, completion: @escaping (Result<[StoredAlert], Error>) -> Void) {
managedObjectContext.perform {
do {
let fetchRequest: NSFetchRequest<StoredAlert> = StoredAlert.fetchRequest()
fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [
let predicates = [
NSPredicate(format: "managerIdentifier = %@", identifier.managerIdentifier),
NSPredicate(format: "alertIdentifier = %@", identifier.alertIdentifier),
]
fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicates)
fetchRequest.sortDescriptors = [ NSSortDescriptor(key: "modificationCounter", ascending: true) ]
let result = try self.managedObjectContext.fetch(fetchRequest)
completion(.success(result))
} catch {
completion(.failure(error))
}
}
}

public func lookupAllUnretracted(managerIdentifier: String? = nil, completion: @escaping (Result<[StoredAlert], Error>) -> Void) {
managedObjectContext.perform {
do {
let fetchRequest: NSFetchRequest<StoredAlert> = StoredAlert.fetchRequest()
var predicates = [
NSPredicate(format: "retractedDate == nil"),
]
if let managerIdentifier = managerIdentifier {
predicates.insert(NSPredicate(format: "managerIdentifier = %@", managerIdentifier), at: 0)
}
fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicates)
fetchRequest.sortDescriptors = [ NSSortDescriptor(key: "modificationCounter", ascending: true) ]
let result = try self.managedObjectContext.fetch(fetchRequest)
completion(.success(result))
} catch {
completion(.failure(error))
}
}
}

public func lookupAllUnacknowledgedUnretracted(managerIdentifier: String? = nil, completion: @escaping (Result<[StoredAlert], Error>) -> Void) {
managedObjectContext.perform {
do {
let fetchRequest: NSFetchRequest<StoredAlert> = StoredAlert.fetchRequest()
var predicates = [
NSPredicate(format: "acknowledgedDate == nil"),
NSPredicate(format: "retractedDate == nil"),
])
]
if let managerIdentifier = managerIdentifier {
predicates.insert(NSPredicate(format: "managerIdentifier = %@", managerIdentifier), at: 0)
}
fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicates)
fetchRequest.sortDescriptors = [ NSSortDescriptor(key: "modificationCounter", ascending: true) ]
let result = try self.managedObjectContext.fetch(fetchRequest)
completion(.success(result))
Expand Down Expand Up @@ -172,7 +231,7 @@ extension AlertStore {
addingPredicate predicate: NSPredicate,
with updateBlock: @escaping ManagedObjectUpdateBlock,
completion: ((Result<Void, Error>) -> Void)?) {
managedObjectContext.perform {
managedObjectContext.performAndWait {
self.lookupAll(identifier: identifier, predicate: predicate) {
switch $0 {
case .success(let objects):
Expand All @@ -194,7 +253,7 @@ extension AlertStore {
addingPredicate predicate: NSPredicate,
with updateBlock: @escaping ManagedObjectUpdateBlock,
completion: ((Result<Void, Error>) -> Void)?) {
managedObjectContext.perform {
managedObjectContext.performAndWait {
self.lookupLatest(identifier: identifier, predicate: predicate) {
switch $0 {
case .success(let object):
Expand Down
22 changes: 22 additions & 0 deletions Loop/Managers/DeviceDataManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,28 @@ extension DeviceDataManager: AlertIssuer {
}
}

// MARK: - PersistedAlertStore
extension DeviceDataManager: PersistedAlertStore {
func doesIssuedAlertExist(identifier: Alert.Identifier, completion: @escaping (Swift.Result<Bool, Error>) -> Void) {
precondition(alertManager != nil)
alertManager.doesIssuedAlertExist(identifier: identifier, completion: completion)
}
func lookupAllUnretracted(managerIdentifier: String, completion: @escaping (Swift.Result<[PersistedAlert], Error>) -> Void) {
precondition(alertManager != nil)
alertManager.lookupAllUnretracted(managerIdentifier: managerIdentifier, completion: completion)
}

func lookupAllUnacknowledgedUnretracted(managerIdentifier: String, completion: @escaping (Swift.Result<[PersistedAlert], Error>) -> Void) {
precondition(alertManager != nil)
alertManager.lookupAllUnacknowledgedUnretracted(managerIdentifier: managerIdentifier, completion: completion)
}

func recordRetractedAlert(_ alert: Alert, at date: Date) {
precondition(alertManager != nil)
alertManager.recordRetractedAlert(alert, at: date)
}
}

// MARK: - CGMManagerDelegate
extension DeviceDataManager: CGMManagerDelegate {
func cgmManagerWantsDeletion(_ manager: CGMManager) {
Expand Down
2 changes: 1 addition & 1 deletion Loop/Managers/LoopDataManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ final class LoopDataManager: LoopSettingsAlerterDelegate {
// Cancel any active temp basal when going into closed loop off mode
// The dispatch is necessary in case this is coming from a didSet already on the settings struct.
self.automaticDosingStatus.$isClosedLoop
.removeDuplicates()
.dropFirst()
.removeDuplicates()
.receive(on: DispatchQueue.main)
.sink { if !$0 {
self.mutateSettings { settings in
Expand Down
2 changes: 1 addition & 1 deletion Loop/View Controllers/StatusTableViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1511,7 +1511,7 @@ final class StatusTableViewController: LoopChartsTableViewController {
}
}

private func addPumpManagerViewToHUD(_ view: LevelHUDView) {
private func addPumpManagerViewToHUD(_ view: BaseHUDView) {
if let hudView = hudView {
view.stateColors = .pumpStatus
hudView.addPumpManagerProvidedHUDView(view)
Expand Down
Loading