| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- //
- // MockCGMManagerSettingsView.swift
- // MockKitUI
- //
- // Created by Nathaniel Hamming on 2023-05-18.
- // Copyright © 2023 LoopKit Authors. All rights reserved.
- //
- import SwiftUI
- import LoopKit
- import LoopKitUI
- import MockKit
- struct MockCGMManagerSettingsView: View {
- fileprivate enum PresentedAlert {
- case resumeInsulinDeliveryError(Error)
- case suspendInsulinDeliveryError(Error)
- }
-
- @Environment(\.dismissAction) private var dismiss
- @Environment(\.guidanceColors) private var guidanceColors
- @Environment(\.glucoseTintColor) private var glucoseTintColor
- @ObservedObject var viewModel: MockCGMManagerSettingsViewModel
-
- @State private var showSuspendOptions = false
- @State private var presentedAlert: PresentedAlert?
- private var displayGlucosePreference: DisplayGlucosePreference
- private let appName: String
- private let allowDebugFeatures : Bool
-
- private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
-
- init(cgmManager: MockCGMManager, displayGlucosePreference: DisplayGlucosePreference, appName: String, allowDebugFeatures: Bool) {
- viewModel = MockCGMManagerSettingsViewModel(cgmManager: cgmManager, displayGlucosePreference: displayGlucosePreference)
- self.displayGlucosePreference = displayGlucosePreference
- self.appName = appName
- self.allowDebugFeatures = allowDebugFeatures
- }
-
- var body: some View {
- List {
- statusSection
-
- sensorSection
-
- lastReadingSection
-
- supportSection
- }
- .insetGroupedListStyle()
- .navigationBarItems(trailing: doneButton)
- .navigationBarTitle(Text("CGM Simulator"), displayMode: .large)
- .alert(item: $presentedAlert, content: alert(for:))
- }
-
- @ViewBuilder
- private var statusSection: some View {
- statusCardSubSection
-
- notificationSubSection
-
- if (allowDebugFeatures) {
- settingsSubSection
- }
- }
-
- private var statusCardSubSection: some View {
- Section {
- VStack(spacing: 8) {
- sensorProgressView
- .openMockCGMSettingsOnLongPress(enabled: true, cgmManager: viewModel.cgmManager, displayGlucosePreference: displayGlucosePreference)
- Divider()
- lastReadingInfo
- }
- }
- }
-
- private var sensorProgressView: some View {
- HStack(alignment: .center, spacing: 16) {
- pumpImage
- expirationArea
- .offset(y: -3)
- }
- }
-
- private var pumpImage: some View {
- ZStack {
- RoundedRectangle(cornerRadius: 5)
- .fill(Color(frameworkColor: "LightGrey")!)
- .frame(width: 77, height: 76)
- Image(frameworkImage: "CGM Simulator")
- .resizable()
- .aspectRatio(contentMode: ContentMode.fit)
- .frame(maxHeight: 70)
- .frame(width: 70)
- }
- }
-
- private var expirationArea: some View {
- VStack(alignment: .leading) {
- expirationText
- .offset(y: 4)
- expirationTime
- .offset(y: 10)
- progressBar
- }
- }
-
- private var expirationText: some View {
- Text("Sensor expires in ")
- .font(.subheadline)
- .foregroundColor(.secondary)
- }
-
- private var expirationTime: some View {
- HStack(alignment: .lastTextBaseline) {
- Text("5")
- .font(.system(size: 24, weight: .heavy, design: .default))
- Text("days")
- .font(.system(size: 15, weight: .regular, design: .default))
- .foregroundColor(.secondary)
- .offset(x: -3)
- }
- }
-
- private var progressBar: some View {
- ProgressView(progress: viewModel.sensorExpirationPercentComplete)
- .accentColor(glucoseTintColor)
- }
-
- var lastReadingInfo: some View {
- HStack(alignment: .lastTextBaseline) {
- lastGlucoseReading
- .frame(idealWidth: 100)
- Spacer()
- lastReadingTime
- .onReceive(timer) { _ in
- // Update every second
- viewModel.updateLastReadingTime()
- }
- }
- }
-
- @ViewBuilder
- private var lastGlucoseReading: some View {
- VStack(alignment: .leading, spacing: 5) {
- Text("Last Reading")
- .foregroundColor(.secondary)
-
- HStack(alignment: .center, spacing: 16) {
- viewModel.lastGlucoseTrend?.filledImage
- .scaleEffect(1.7, anchor: .leading)
- .foregroundColor(glucoseTintColor)
- HStack(alignment: .firstTextBaseline, spacing: 4) {
- Text(viewModel.lastGlucoseValueFormatted)
- .font(.title)
- .fontWeight(.heavy)
- Text(viewModel.glucoseUnitString)
- .foregroundColor(.secondary)
- }
- }
- }
- }
-
- @ViewBuilder
- private var lastReadingTime: some View {
- HStack(alignment: .center, spacing: 16) {
- Image(systemName: "arrow.triangle.2.circlepath.circle.fill")
- .scaleEffect(1.7, anchor: .leading)
- .foregroundColor(glucoseTintColor)
- HStack(alignment: .firstTextBaseline, spacing: 4) {
- Text("\(viewModel.lastReadingMinutesFromNow)")
- .font(.title)
- .fontWeight(.heavy)
- Text("min")
- .foregroundColor(.secondary)
- }
- }
- .frame(height: 40.0)
- }
-
- private var notificationSubSection: some View {
- Section {
- NavigationLink(destination: DemoPlaceHolderView(appName: appName)) {
- Text("Notification Settings")
- }
- }
- }
-
- private var settingsSubSection: some View {
- Section {
- NavigationLink(destination: MockCGMManagerControlsView(cgmManager: viewModel.cgmManager, displayGlucosePreference: displayGlucosePreference)) {
- Text("Simulator Settings")
- }
- }
- }
-
- @ViewBuilder
- private var sensorSection: some View {
- deviceDetailsSubSection
- stopSensorSubSection
- }
-
- private var deviceDetailsSubSection: some View {
- Section(header: SectionHeader(label: "Sensor")) {
- LabeledValueView(label: "Insertion Time", value: viewModel.sensorInsertionDateTimeString)
-
- LabeledValueView(label: "Sensor Expires", value: viewModel.sensorExpirationDateTimeString)
- }
- }
-
- private var stopSensorSubSection: some View {
- Section {
- NavigationLink(destination: DemoPlaceHolderView(appName: appName)) {
- Text("Stop Sensor")
- .foregroundColor(guidanceColors.critical)
- }
- }
- }
- private var lastReadingSection: some View {
- Section(header: SectionHeader(label: "Last Reading")) {
- LabeledValueView(label: "Glucose", value: viewModel.lastGlucoseValueWithUnitFormatted)
- LabeledValueView(label: "Time", value: viewModel.lastGlucoseDateFormatted)
- LabeledValueView(label: "Trend", value: viewModel.lastGlucoseTrendFormatted)
- }
- }
-
- private var supportSection: some View {
- Section(header: SectionHeader(label: "Support")) {
- NavigationLink(destination: DemoPlaceHolderView(appName: appName)) {
- Text("Get help with your CGM")
- }
- }
- }
-
- private var doneButton: some View {
- Button(LocalizedString("Done", comment: "Settings done button label"), action: dismiss)
- }
-
- private func alert(for presentedAlert: PresentedAlert) -> SwiftUI.Alert {
- switch presentedAlert {
- case .suspendInsulinDeliveryError(let error):
- return Alert(
- title: Text("Failed to Suspend Insulin Delivery"),
- message: Text(error.localizedDescription)
- )
- case .resumeInsulinDeliveryError(let error):
- return Alert(
- title: Text("Failed to Resume Insulin Delivery"),
- message: Text(error.localizedDescription)
- )
- }
- }
- }
- extension MockCGMManagerSettingsView.PresentedAlert: Identifiable {
- var id: Int {
- switch self {
- case .resumeInsulinDeliveryError:
- return 0
- case .suspendInsulinDeliveryError:
- return 1
- }
- }
- }
- struct MockCGMManagerSettingsView_Previews: PreviewProvider {
- static var previews: some View {
- MockCGMManagerSettingsView(cgmManager: MockCGMManager(), displayGlucosePreference: DisplayGlucosePreference(displayGlucoseUnit: .milligramsPerDeciliter), appName: "Loop", allowDebugFeatures: false)
- }
- }
|