Jonas Björkert il y a 1 an
Parent
commit
9738af95e0

+ 8 - 0
LoopFollow.xcodeproj/project.pbxproj

@@ -95,6 +95,8 @@
 		DD7E19862ACDA59700DBD158 /* BGCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7E19852ACDA59700DBD158 /* BGCheck.swift */; };
 		DD7E19882ACDA5DA00DBD158 /* Notes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7E19872ACDA5DA00DBD158 /* Notes.swift */; };
 		DD7E198A2ACDA62600DBD158 /* SensorStart.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7E19892ACDA62600DBD158 /* SensorStart.swift */; };
+		DD7F4B9D2DD1EAE500D449E9 /* AlarmAudioSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7F4B9C2DD1EAE500D449E9 /* AlarmAudioSection.swift */; };
+		DD7F4B9F2DD1F92700D449E9 /* AlarmActiveSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7F4B9E2DD1F92700D449E9 /* AlarmActiveSection.swift */; };
 		DD7FFAFD2A72953000C3A304 /* EKEventStore+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7FFAFC2A72953000C3A304 /* EKEventStore+Extensions.swift */; };
 		DD85E9952D739CFE001C8BB7 /* OmnipodDashHeartbeatBluetoothTransmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD85E9942D739CED001C8BB7 /* OmnipodDashHeartbeatBluetoothTransmitter.swift */; };
 		DD91E4DD2BDEC3F8002D9E97 /* GlucoseConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD91E4DC2BDEC3F8002D9E97 /* GlucoseConversion.swift */; };
@@ -414,6 +416,8 @@
 		DD7E19852ACDA59700DBD158 /* BGCheck.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGCheck.swift; sourceTree = "<group>"; };
 		DD7E19872ACDA5DA00DBD158 /* Notes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notes.swift; sourceTree = "<group>"; };
 		DD7E19892ACDA62600DBD158 /* SensorStart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SensorStart.swift; sourceTree = "<group>"; };
+		DD7F4B9C2DD1EAE500D449E9 /* AlarmAudioSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlarmAudioSection.swift; sourceTree = "<group>"; };
+		DD7F4B9E2DD1F92700D449E9 /* AlarmActiveSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlarmActiveSection.swift; sourceTree = "<group>"; };
 		DD7FFAFC2A72953000C3A304 /* EKEventStore+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EKEventStore+Extensions.swift"; sourceTree = "<group>"; };
 		DD85E9942D739CED001C8BB7 /* OmnipodDashHeartbeatBluetoothTransmitter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OmnipodDashHeartbeatBluetoothTransmitter.swift; sourceTree = "<group>"; };
 		DD91E4DC2BDEC3F8002D9E97 /* GlucoseConversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseConversion.swift; sourceTree = "<group>"; };
@@ -915,6 +919,8 @@
 		DDC7E53B2DBD8A1600EB1127 /* Components */ = {
 			isa = PBXGroup;
 			children = (
+				DD7F4B9E2DD1F92700D449E9 /* AlarmActiveSection.swift */,
+				DD7F4B9C2DD1EAE500D449E9 /* AlarmAudioSection.swift */,
 				DD0650F62DCFDA26004D3B41 /* InfoBanner.swift */,
 				DD0650F42DCF303F004D3B41 /* AlarmStepperSection.swift */,
 				DD0650A82DCA8A10004D3B41 /* AlarmBGSection.swift */,
@@ -1630,6 +1636,7 @@
 				DD5334292C6166A500062F9D /* InfoDisplaySettingsView.swift in Sources */,
 				DD48781E2C7DAF2F0048F05C /* PushNotificationManager.swift in Sources */,
 				DD2C2E562D3C3917006413A5 /* DexcomSettingsView.swift in Sources */,
+				DD7F4B9F2DD1F92700D449E9 /* AlarmActiveSection.swift in Sources */,
 				DD4AFB672DB68C5500BB593F /* UUID+Identifiable.swift in Sources */,
 				DD9ED0CA2D355257000D2A63 /* LogView.swift in Sources */,
 				DD9ACA082D32F68B00415D8A /* BGTask.swift in Sources */,
@@ -1645,6 +1652,7 @@
 				DDCF979E24C2382A002C9752 /* AppStateController.swift in Sources */,
 				DDEF503F2D32754F00999A5D /* ProfileTask.swift in Sources */,
 				DD5334212C60EBEE00062F9D /* InsulinCartridgeChange.swift in Sources */,
+				DD7F4B9D2DD1EAE500D449E9 /* AlarmAudioSection.swift in Sources */,
 				FC97881E2485969B00A7906C /* NightScoutViewController.swift in Sources */,
 				DD608A0A2C23593900F91132 /* SMB.swift in Sources */,
 				DDCF979824C1489C002C9752 /* GraphSettingsViewController.swift in Sources */,

+ 18 - 6
LoopFollow/Alarm/Alarm.swift

@@ -10,14 +10,26 @@ import Foundation
 import HealthKit
 import UserNotifications
 
-enum PlaySoundOption: String, CaseIterable, Codable {
-  case always, day, night, never
+protocol DayNightDisplayable {
+    var displayName: String { get }
 }
-enum RepeatSoundOption: String, CaseIterable, Codable {
-  case always, day, night, never
+
+extension DayNightDisplayable where Self: RawRepresentable, Self.RawValue == String {
+    var displayName: String {
+        rawValue == "always" ? "Day & Night" : rawValue.capitalized
+    }
+}
+
+enum PlaySoundOption: String, CaseIterable, Codable, DayNightDisplayable {
+    case always, day, night, never
 }
-enum ActiveOption: String, CaseIterable, Codable {
-  case always, day, night
+
+enum RepeatSoundOption: String, CaseIterable, Codable, DayNightDisplayable {
+    case always, day, night, never
+}
+
+enum ActiveOption: String, CaseIterable, Codable, DayNightDisplayable {
+    case always, day, night
 }
 
 struct Alarm: Identifiable, Codable, Equatable {

+ 20 - 0
LoopFollow/Alarm/AlarmEditing/Components/AlarmActiveSection.swift

@@ -0,0 +1,20 @@
+//
+//  AlarmActiveSection.swift
+//  LoopFollow
+//
+//  Created by Jonas Björkert on 2025-05-12.
+//  Copyright © 2025 Jon Fawcett. All rights reserved.
+//
+
+import SwiftUI
+
+struct AlarmActiveSection: View {
+    @Binding var alarm: Alarm
+
+    var body: some View {
+        Section(header: Text("Active During")) {
+            AlarmEnumMenuPicker(title: "Active",
+                           selection: $alarm.activeOption)
+        }
+    }
+}

+ 94 - 0
LoopFollow/Alarm/AlarmEditing/Components/AlarmAudioSection.swift

@@ -0,0 +1,94 @@
+//
+//  AlarmAudioSection.swift
+//  LoopFollow
+//
+//  Created by Jonas Björkert on 2025-05-12.
+//  Copyright © 2025 Jon Fawcett. All rights reserved.
+//
+
+
+import SwiftUI
+
+struct AlarmAudioSection: View {
+    @Binding var alarm: Alarm
+    @State private var showingTonePicker = false
+
+    var body: some View {
+        Section(header: Text("Alert Sound")) {
+            Button {
+                showingTonePicker = true
+            } label: {
+                HStack {
+                    Text("Tone")
+                    Spacer()
+                    Text(alarm.soundFile.displayName)
+                        .foregroundColor(.secondary)
+                    Image(systemName: "chevron.right")
+                        .foregroundColor(.secondary)
+                }
+            }
+            .buttonStyle(.plain)
+            .sheet(isPresented: $showingTonePicker) {
+                TonePickerSheet(selected: $alarm.soundFile)
+            }
+
+            AlarmEnumMenuPicker(title: "Play", selection: $alarm.playSoundOption)
+            AlarmEnumMenuPicker(title: "Repeat", selection: $alarm.repeatSoundOption)
+        }
+    }
+}
+
+struct AlarmEnumMenuPicker<E: CaseIterable & Hashable & DayNightDisplayable>: View {
+    let title: String
+    @Binding var selection: E
+
+    var body: some View {
+        HStack {
+            Text(title)
+            Spacer()
+            Picker("", selection: $selection) {
+                ForEach(Array(E.allCases), id: \.self) { option in
+                    Text(option.displayName).tag(option)
+                }
+            }
+        }
+    }
+}
+
+private struct TonePickerSheet: View {
+    @Binding var selected: SoundFile
+    @Environment(\.dismiss) private var dismiss
+
+    var body: some View {
+        NavigationView {
+            List {
+                ForEach(SoundFile.allCases) { tone in
+                    Button {
+                        selected = tone
+                        AlarmSound.setSoundFile(str: tone.rawValue)
+                        AlarmSound.stop()
+                        AlarmSound.playTest()
+                    } label: {
+                        HStack {
+                            Text(tone.displayName)
+                            if tone == selected {
+                                Spacer()
+                                Image(systemName: "checkmark")
+                                    .foregroundColor(.accentColor)
+                            }
+                        }
+                    }
+                }
+            }
+            .navigationTitle("Choose Tone")
+            .toolbar {
+                ToolbarItem(placement: .confirmationAction) {
+                    Button("Done") {
+                        AlarmSound.stop()
+                        dismiss()
+                    }
+                }
+            }
+        }
+    }
+}

+ 14 - 0
LoopFollow/Alarm/AlarmEditing/Components/AlarmBGSection.swift

@@ -16,6 +16,20 @@ struct AlarmBGSection: View {
     let range:   ClosedRange<Double>
     @Binding var value: Double
 
+    init(
+        header: String? = nil,
+        footer: String? = nil,
+        title: String,
+        range: ClosedRange<Double>,
+        value: Binding<Double>
+    ) {
+        self.header = header
+        self.footer = footer
+        self.title = title
+        self.range = range
+        self._value = value
+    }
+
     private var unit: HKUnit { UserDefaultsRepository.getPreferredUnit() }
 
     private var displayValue: String {

+ 0 - 100
LoopFollow/Alarm/AlarmEditing/Components/AlarmEditorFields.swift

@@ -46,106 +46,6 @@ struct AlarmSnoozeSection: View {
     }
 }
 
-import SwiftUI
-
-struct AlarmAudioSection: View {
-    @Binding var alarm: Alarm
-    @State private var showingTonePicker = false
-
-    var body: some View {
-        Section(header: Text("Alert Sound")) {
-            // ——— Tone Row ———
-            Button {
-                showingTonePicker = true
-            } label: {
-                HStack {
-                    Text("Tone")
-                    Spacer()
-                    Text(alarm.soundFile.displayName)
-                        .foregroundColor(.secondary)
-                    Image(systemName: "chevron.right")
-                        .foregroundColor(.secondary)
-                }
-            }
-            .sheet(isPresented: $showingTonePicker) {
-                NavigationView {
-                    List {
-                        ForEach(SoundFile.allCases) { tone in
-                            Button {
-                                alarm.soundFile = tone
-                                // play test tone
-                                AlarmSound.setSoundFile(str: tone.rawValue)
-                                AlarmSound.stop()
-                                AlarmSound.playTest()
-                            } label: {
-                                HStack {
-                                    Text(tone.displayName)
-                                    if alarm.soundFile == tone {
-                                        Spacer()
-                                        Image(systemName: "checkmark")
-                                            .foregroundColor(.accentColor)
-                                    }
-                                }
-                            }
-                        }
-                    }
-                    .navigationTitle("Choose Tone")
-                    .toolbar {
-                        ToolbarItem(placement: .confirmationAction) {
-                            Button("Done") {
-                                AlarmSound.stop()
-                                showingTonePicker = false
-                            }
-                        }
-                    }
-                }
-            }
-
-            // ——— Play / Repeat Toggles ———
-            HStack() {
-                Text("Play")
-                    .font(.subheadline)
-                    .foregroundColor(.secondary)
-                Spacer()
-                Picker("", selection: $alarm.playSoundOption) {
-                    ForEach(PlaySoundOption.allCases, id: \.self) { opt in
-                        Text(opt.rawValue.capitalized).tag(opt)
-                    }
-                }
-                .pickerStyle(.menu)
-            }
-
-            VStack(alignment: .leading, spacing: 8) {
-                Text("Repeat")
-                    .font(.subheadline)
-                    .foregroundColor(.secondary)
-
-                Picker("", selection: $alarm.repeatSoundOption) {
-                    ForEach(RepeatSoundOption.allCases, id: \.self) { opt in
-                        Text(opt.rawValue.capitalized).tag(opt)
-                    }
-                }
-                .pickerStyle(.menu)
-            }
-        }
-    }
-}
-
-struct AlarmActiveSection: View {
-    @Binding var alarm: Alarm
-
-    var body: some View {
-        Section(header: Text("Active During")) {
-            Picker("Active", selection: $alarm.activeOption) {
-                Text("Always").tag(ActiveOption.always)
-                Text("Day").tag(ActiveOption.day)
-                Text("Night").tag(ActiveOption.night)
-            }
-            .pickerStyle(.segmented)
-        }
-    }
-}
-
 struct AlarmSnoozedUntilSection: View {
     @Binding var alarm: Alarm
 

+ 0 - 1
LoopFollow/Alarm/AlarmEditing/Editors/LowBgAlarmEditor.swift

@@ -22,7 +22,6 @@ struct LowBgAlarmEditor: View {
 
             AlarmBGSection(
                 header: "Threshold",
-                footer: "bla bla",
                 title: "BG",
                 range: 40...150,
                 value: Binding(