// // OverrideSelectionHistory.swift // LoopUI // // Created by Anna Quinlan on 8/1/20. // Copyright ยฉ 2020 LoopKit Authors. All rights reserved. // import SwiftUI import LoopKit import HealthKit public class OverrideHistorystate: ObservableObject { var overrides: [TemporaryScheduleOverride] var glucoseUnit: HKUnit var didEditOverride: ((TemporaryScheduleOverride) -> Void)? var didDeleteOverride: ((TemporaryScheduleOverride) -> Void)? public init( overrides: [TemporaryScheduleOverride], glucoseUnit: HKUnit ) { self.overrides = overrides self.glucoseUnit = glucoseUnit } } public struct OverrideSelectionHistory: View { @ObservedObject var model: OverrideHistorystate private var quantityFormatter: QuantityFormatter private var glucoseNumberFormatter: NumberFormatter private var durationFormatter: DateComponentsFormatter public init(model: OverrideHistorystate) { self.model = model self.quantityFormatter = { let quantityFormatter = QuantityFormatter() quantityFormatter.setPreferredNumberFormatter(for: model.glucoseUnit) return quantityFormatter }() self.glucoseNumberFormatter = quantityFormatter.numberFormatter self.durationFormatter = { let formatter = DateComponentsFormatter() formatter.allowedUnits = [.hour, .minute] formatter.unitsStyle = .short return formatter }() } // Style conditionally based on iOS so we get a grouped list style public var body: some View { #if swift(>=5.2) if #available(iOS 14.0, *) { bodyContents .listStyle(InsetGroupedListStyle()) } else { bodyContents .listStyle(GroupedListStyle()) .environment(\.horizontalSizeClass, .regular) } #else bodyContents .listStyle(GroupedListStyle()) .environment(\.horizontalSizeClass, .regular) #endif } private var bodyContents: some View { List { ForEach(model.overrides, id: \.self) { override in Group { // Don't show overrides in history that were never active if override.actualEnd != .deleted { Section { NavigationLink(destination: self.detailView(for: override)) { self.createCell(for: override) .padding(EdgeInsets(top: 4, leading: 0, bottom: 4, trailing: 0)) } } } } } } .navigationBarTitle(Text(LocalizedString("Override History", comment: "Title for override history view")), displayMode: .large) } private func makeTargetRangeText(from targetRange: ClosedRange) -> String { guard let minTarget = glucoseNumberFormatter.string(from: targetRange.lowerBound.doubleValue(for: model.glucoseUnit)), let maxTarget = glucoseNumberFormatter.string(from: targetRange.upperBound.doubleValue(for: model.glucoseUnit)) else { return "" } return String(format: LocalizedString("%1$@ โ€“ %2$@ %3$@", comment: "The format for a glucose target range. (1: min target)(2: max target)(3: glucose unit)"), minTarget, maxTarget, quantityFormatter.string(from: model.glucoseUnit)) } private func createCell(for override: TemporaryScheduleOverride) -> OverrideViewCell { let startTime = DateFormatter.localizedString(from: override.startDate, dateStyle: .none, timeStyle: .short) var targetRange: String = "" if let range = override.settings.targetRange { targetRange = makeTargetRangeText(from: range) } var duration: String { // Don't use the durationFormatter if the interval is infinite if !override.duration.isFinite && override.scheduledEndDate == override.actualEndDate { return "โˆž" } return durationFormatter.string(from: override.startDate, to: override.actualEndDate)! } let insulinNeeds = override.settings.insulinNeedsScaleFactor switch override.context { case .legacyWorkout: return OverrideViewCell( symbol: Text("๐Ÿƒโ€โ™‚๏ธ"), name: Text("Workout", comment: "Title for workout override history cell"), targetRange: Text(targetRange), duration: Text(duration), subtitle: Text(startTime), insulinNeedsScaleFactor: insulinNeeds) case .preMeal: return OverrideViewCell( symbol: Text("๐Ÿฝ"), name: Text("Pre-Meal", comment: "Title for pre-meal override history cell"), targetRange: Text(targetRange), duration: Text(duration), subtitle: Text(startTime), insulinNeedsScaleFactor: insulinNeeds) case .preset(let preset): return OverrideViewCell( symbol: Text(preset.symbol), name: Text(preset.name), targetRange: Text(targetRange), duration: Text(duration), subtitle: Text(startTime), insulinNeedsScaleFactor: insulinNeeds) case .custom: return OverrideViewCell( symbol: Text("ยทยทยท"), name: Text("Custom", comment: "Title for custom override history cell"), targetRange: Text(targetRange), duration: Text(duration), subtitle: Text(startTime), insulinNeedsScaleFactor: insulinNeeds) } } private func title(for override: TemporaryScheduleOverride) -> String { switch override.context { case .legacyWorkout: return LocalizedString("๐Ÿƒโ€โ™‚๏ธ Workout", comment: "Workout override preset title") case .preMeal: return LocalizedString("๐Ÿฝ Pre-Meal", comment: "Premeal override preset title") case .preset(let preset): let symbol = preset.symbol let name = preset.name let format = LocalizedString("%1$@ %2$@", comment: "The format for an override symbol and name (1: symbol)(2: name)") return String(format: format, symbol, name) case .custom: return LocalizedString("Custom Override", comment: "Custom override preset title") } } private func detailView(for override: TemporaryScheduleOverride) -> some View { let editorTitle = title(for: override) return HistoricalOverrideDetailView( override: override, glucoseUnit: model.glucoseUnit, delegate: nil ).navigationBarTitle(editorTitle) } }