ScheduledExpirationReminderEditView.swift 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. //
  2. // ScheduledExpirationReminderEditView.swift
  3. // OmniKit
  4. //
  5. // Created by Pete Schwamb on 2/17/21.
  6. // Copyright © 2021 LoopKit Authors. All rights reserved.
  7. //
  8. import SwiftUI
  9. import LoopKitUI
  10. extension Date: Identifiable {
  11. public var id: Self { self }
  12. }
  13. struct ScheduledExpirationReminderEditView: View {
  14. @Environment(\.horizontalSizeClass) var horizontalSizeClass
  15. var allowedDates: [Date]
  16. var dateFormatter: DateFormatter
  17. var onSave: ((_ selectedDate: Date?, _ completion: @escaping (_ error: Error?) -> Void) -> Void)?
  18. var onFinish: (() -> Void)?
  19. private var initialValue: Date?
  20. @State private var alertIsPresented: Bool = false
  21. @State private var error: Error?
  22. @State private var saving: Bool = false
  23. @State private var selectedDate: Date?
  24. init(scheduledExpirationReminderDate: Date?, allowedDates: [Date], dateFormatter: DateFormatter, onSave: ((_ selectedDate: Date?, _ completion: @escaping (_ error: Error?) -> Void) -> Void)? = nil, onFinish: (() -> Void)? = nil)
  25. {
  26. self.allowedDates = allowedDates
  27. self.dateFormatter = dateFormatter
  28. self.onSave = onSave
  29. self.onFinish = onFinish
  30. self.initialValue = scheduledExpirationReminderDate
  31. self._selectedDate = State(initialValue: scheduledExpirationReminderDate)
  32. }
  33. var body: some View {
  34. contentWithCancel
  35. }
  36. var content: some View {
  37. VStack {
  38. RoundedCardScrollView(title: LocalizedString("Scheduled Reminder", comment: "Title for scheduled expiration reminder edit page")) {
  39. if self.horizontalSizeClass == .compact {
  40. // Keep picker outside of card in compact view, because it forces full device width.
  41. VStack(spacing: 0) {
  42. RoundedCard {
  43. Text("Scheduled Reminder", comment: "Card title for scheduled reminder")
  44. Divider()
  45. valueRow
  46. }
  47. picker
  48. .background(Color(.secondarySystemGroupedBackground))
  49. }
  50. } else {
  51. RoundedCard {
  52. Text("Scheduled Reminder", comment: "Card title for scheduled reminder")
  53. Divider()
  54. valueRow
  55. picker
  56. }
  57. }
  58. }
  59. Spacer()
  60. Button(action: saveTapped) {
  61. Text(saveButtonText)
  62. .actionButtonStyle()
  63. .padding()
  64. }
  65. .disabled(saving || !valueChanged)
  66. }
  67. .navigationBarTitle("", displayMode: .inline)
  68. .alert(isPresented: $alertIsPresented, content: { alert(error: error) })
  69. }
  70. var valueRow: some View {
  71. RoundedCardValueRow(
  72. label: LocalizedString("Time", comment: "Label for scheduled expiration reminder row"),
  73. value: scheduledReminderDateString(selectedDate),
  74. highlightValue: true
  75. )
  76. }
  77. var picker: some View {
  78. Picker(selection: $selectedDate, label: Text("Numbers")) {
  79. ForEach(self.allowedDates) { date in
  80. Text(scheduledReminderDateString(date)).tag(date as Date?)
  81. }
  82. Text(scheduledReminderDateString(nil)).tag(nil as Date?)
  83. }
  84. .pickerStyle(WheelPickerStyle())
  85. }
  86. var saveButtonText: String {
  87. if saving {
  88. return LocalizedString("Saving...", comment: "button title for saving scheduled reminder while saving")
  89. } else {
  90. return LocalizedString("Save", comment: "button title for saving scheduled reminder")
  91. }
  92. }
  93. private func saveTapped() {
  94. saving = true
  95. self.onSave?(selectedDate) { (error) in
  96. saving = false
  97. if let error = error {
  98. self.error = error
  99. self.alertIsPresented = true
  100. } else {
  101. self.onFinish?()
  102. }
  103. }
  104. }
  105. private func scheduledReminderDateString(_ scheduledDate: Date?) -> String {
  106. if let scheduledDate = scheduledDate {
  107. return dateFormatter.string(from: scheduledDate)
  108. } else {
  109. return LocalizedString("No Reminder", comment: "Value text for no expiration reminder")
  110. }
  111. }
  112. private var valueChanged: Bool {
  113. return selectedDate != initialValue
  114. }
  115. private var contentWithCancel: some View {
  116. if saving {
  117. return AnyView(content
  118. .navigationBarBackButtonHidden(true)
  119. )
  120. } else if valueChanged {
  121. return AnyView(content
  122. .navigationBarBackButtonHidden(true)
  123. .navigationBarItems(leading: cancelButton)
  124. )
  125. } else {
  126. return AnyView(content)
  127. }
  128. }
  129. private var cancelButton: some View {
  130. Button(action: { self.onFinish?() } ) { Text(LocalizedString("Cancel", comment: "Button title for cancelling scheduled reminder date edit")) }
  131. }
  132. private func alert(error: Error?) -> SwiftUI.Alert {
  133. return SwiftUI.Alert(
  134. title: Text(LocalizedString("Failed to Update Expiration Reminder", comment: "Alert title for error when updating expiration reminder")),
  135. message: Text(error?.localizedDescription ?? "No Error")
  136. )
  137. }
  138. }
  139. struct ScheduledExpirationReminderEditView_Previews: PreviewProvider {
  140. static var previews: some View {
  141. ScheduledExpirationReminderEditView(
  142. scheduledExpirationReminderDate: Date(),
  143. allowedDates: [Date()],
  144. dateFormatter: DateFormatter(),
  145. onSave: { (_, _) in },
  146. onFinish: { }
  147. )
  148. }
  149. }