GeneralAlarmsStepView.swift 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. // LoopFollow
  2. // GeneralAlarmsStepView.swift
  3. import SwiftUI
  4. /// A short set of the alarm-wide settings that matter most up front: when the
  5. /// day and night periods begin (used by day/night alarm options) and how alarm
  6. /// sound is handled. The full set lives in the Menu.
  7. struct GeneralAlarmsStepView: View {
  8. @ObservedObject private var cfgStore = Storage.shared.alarmConfiguration
  9. private var dayBinding: Binding<Date> {
  10. timeBinding(\.dayStart)
  11. }
  12. private var nightBinding: Binding<Date> {
  13. timeBinding(\.nightStart)
  14. }
  15. var body: some View {
  16. Form {
  17. Section {
  18. EmptyView()
  19. } header: {
  20. OnboardingStepHeader(
  21. systemImage: "slider.horizontal.3",
  22. title: "Alarm basics",
  23. subtitle: "Set when your day and night begin, and how alarm sound behaves. You can fine-tune everything later in the Menu."
  24. )
  25. .textCase(nil)
  26. .padding(.bottom, 8)
  27. }
  28. .listRowInsets(EdgeInsets())
  29. .listRowBackground(Color.clear)
  30. Section {
  31. DatePicker("Day starts", selection: dayBinding, displayedComponents: [.hourAndMinute])
  32. .datePickerStyle(.compact)
  33. DatePicker("Night starts", selection: nightBinding, displayedComponents: [.hourAndMinute])
  34. .datePickerStyle(.compact)
  35. } header: {
  36. Text("Day / Night")
  37. } footer: {
  38. Text("Alarms can behave differently during the day and at night.")
  39. }
  40. Section {
  41. Toggle("Override system volume", isOn: $cfgStore.value.overrideSystemOutputVolume)
  42. if cfgStore.value.overrideSystemOutputVolume {
  43. HStack {
  44. Image(systemName: "speaker.fill")
  45. .foregroundColor(.secondary)
  46. Slider(value: $cfgStore.value.forcedOutputVolume, in: 0 ... 1)
  47. Image(systemName: "speaker.wave.3.fill")
  48. .foregroundColor(.secondary)
  49. }
  50. }
  51. Toggle("Play sound during calls", isOn: $cfgStore.value.audioDuringCalls)
  52. } header: {
  53. Text("Sound")
  54. } footer: {
  55. Text("Overriding the system volume lets alarms be heard even when your phone is silenced or in a Focus mode.")
  56. }
  57. }
  58. }
  59. private func timeBinding(_ keyPath: WritableKeyPath<AlarmConfiguration, TimeOfDay>) -> Binding<Date> {
  60. Binding(
  61. get: {
  62. var components = Calendar.current.dateComponents([.year, .month, .day], from: Date())
  63. components.hour = cfgStore.value[keyPath: keyPath].hour
  64. components.minute = cfgStore.value[keyPath: keyPath].minute
  65. return Calendar.current.date(from: components) ?? Date()
  66. },
  67. set: { newDate in
  68. let hm = Calendar.current.dateComponents([.hour, .minute], from: newDate)
  69. cfgStore.value[keyPath: keyPath] = TimeOfDay(hour: hm.hour ?? 0, minute: hm.minute ?? 0)
  70. }
  71. )
  72. }
  73. }