ContactTrickDetailView.swift 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. import SwiftUI
  2. struct ContactTrickDetailView: View {
  3. @Environment(\.dismiss) var dismiss
  4. @Environment(\.colorScheme) var colorScheme
  5. @Environment(AppState.self) var appState
  6. @ObservedObject var state: ContactTrick.StateModel
  7. @State private var contactTrickEntry: ContactTrickEntry
  8. @State private var initialContactTrickEntry: ContactTrickEntry
  9. init(entry: ContactTrickEntry, state: ContactTrick.StateModel) {
  10. self.state = state
  11. _contactTrickEntry = State(initialValue: entry)
  12. _initialContactTrickEntry = State(initialValue: entry)
  13. }
  14. var body: some View {
  15. VStack {
  16. HStack {
  17. // TODO: - make this beautiful @Dan
  18. Spacer()
  19. ZStack {
  20. Circle()
  21. .fill(contactTrickEntry.hasHighContrast ? .black : .white)
  22. .foregroundColor(.white)
  23. .frame(width: 100, height: 100)
  24. Image(uiImage: ContactPicture.getImage(contact: contactTrickEntry, state: state.state))
  25. .resizable()
  26. .frame(width: 100, height: 100)
  27. .clipShape(Circle())
  28. Circle()
  29. .stroke(lineWidth: 2)
  30. .foregroundColor(.white)
  31. .frame(width: 100, height: 100)
  32. }
  33. Spacer()
  34. }
  35. .padding(.top, 80)
  36. .padding(.bottom)
  37. Form {
  38. Section(header: Text("Style")) {
  39. Picker("Layout", selection: $contactTrickEntry.layout) {
  40. ForEach(ContactTrickLayout.allCases, id: \.id) { layout in
  41. Text(layout.displayName).tag(layout)
  42. }
  43. }.onChange(of: contactTrickEntry.layout, { oldLayout, newLayout in
  44. if oldLayout != newLayout, newLayout == .split {
  45. contactTrickEntry.top = .glucose
  46. } else {
  47. contactTrickEntry.top = .none
  48. }
  49. })
  50. Toggle("High Contrast Mode", isOn: $contactTrickEntry.hasHighContrast)
  51. }.listRowBackground(Color.chart)
  52. Section(header: Text("Display Values")) {
  53. Picker("Top Value", selection: $contactTrickEntry.top) {
  54. ForEach(ContactTrickValue.allCases, id: \.id) { value in
  55. Text(value.displayName).tag(value)
  56. }
  57. }
  58. if contactTrickEntry.layout == .default {
  59. Picker("Primary", selection: $contactTrickEntry.primary) {
  60. ForEach(ContactTrickValue.allCases, id: \.id) { value in
  61. Text(value.displayName).tag(value)
  62. }
  63. }
  64. }
  65. Picker("Bottom Value", selection: $contactTrickEntry.bottom) {
  66. ForEach(ContactTrickValue.allCases, id: \.id) { value in
  67. Text(value.displayName).tag(value)
  68. }
  69. }
  70. }.listRowBackground(Color.chart)
  71. // Ring Settings Section
  72. Section(header: Text("Ring Settings")) {
  73. Picker("Ring Type", selection: $contactTrickEntry.ring) {
  74. ForEach(ContactTrickLargeRing.allCases, id: \.self) { ring in
  75. Text(ring.displayName).tag(ring)
  76. }
  77. }
  78. if contactTrickEntry.ring != .none {
  79. Picker("Ring Width", selection: $contactTrickEntry.ringWidth) {
  80. ForEach(ContactTrickEntry.RingWidth.allCases, id: \.self) { width in
  81. Text(width.displayName).tag(width)
  82. }
  83. }
  84. Picker("Ring Gap", selection: $contactTrickEntry.ringGap) {
  85. ForEach(ContactTrickEntry.RingGap.allCases, id: \.self) { gap in
  86. Text(gap.displayName).tag(gap)
  87. }
  88. }
  89. }
  90. }.listRowBackground(Color.chart)
  91. // Font Settings Section
  92. Section(header: Text("Font Settings")) {
  93. fontSizePicker
  94. if contactTrickEntry.layout == .split {
  95. secondaryFontSizePicker
  96. }
  97. fontWeightPicker
  98. fontWidthPicker
  99. }.listRowBackground(Color.chart)
  100. }
  101. }
  102. .navigationTitle("Edit Contact Items")
  103. .navigationBarTitleDisplayMode(.inline)
  104. .safeAreaInset(edge: .bottom, spacing: 0) { stickySaveButton }
  105. .listSectionSpacing(10)
  106. .padding(.top, 30)
  107. .ignoresSafeArea(edges: .top)
  108. .scrollContentBackground(.hidden)
  109. .background(appState.trioBackgroundColor(for: colorScheme))
  110. .toolbar {
  111. ToolbarItem(placement: .topBarTrailing) {
  112. Button(
  113. action: {
  114. state.isHelpSheetPresented.toggle()
  115. },
  116. label: {
  117. Image(systemName: "questionmark.circle")
  118. }
  119. )
  120. }
  121. }
  122. .sheet(isPresented: $state.isHelpSheetPresented) {
  123. NavigationStack {
  124. List {
  125. Text("Lorem Ipsum Dolor Sit Amet")
  126. }
  127. .padding(.trailing, 10)
  128. .navigationBarTitle("Help", displayMode: .inline)
  129. Button { state.isHelpSheetPresented.toggle() }
  130. label: { Text("Got it!").frame(maxWidth: .infinity, alignment: .center) }
  131. .buttonStyle(.bordered)
  132. .padding(.top)
  133. }
  134. .padding()
  135. .presentationDetents(
  136. [.fraction(0.9), .large],
  137. selection: $state.helpSheetDetent
  138. )
  139. }
  140. }
  141. private func saveChanges() {
  142. Task {
  143. await state.updateContact(with: contactTrickEntry)
  144. dismiss()
  145. }
  146. }
  147. var stickySaveButton: some View {
  148. var isUnchanged: Bool { initialContactTrickEntry == contactTrickEntry }
  149. return ZStack {
  150. Rectangle()
  151. .frame(width: UIScreen.main.bounds.width, height: 65)
  152. .foregroundStyle(colorScheme == .dark ? Color.bgDarkerDarkBlue : Color.white)
  153. .background(.thinMaterial)
  154. .opacity(0.8)
  155. .clipShape(Rectangle())
  156. Button(action: {
  157. saveChanges()
  158. }, label: {
  159. Text("Save").padding(10)
  160. })
  161. .frame(width: UIScreen.main.bounds.width * 0.9, alignment: .center)
  162. .background(isUnchanged ? Color(.systemGray4) : Color(.systemBlue))
  163. .disabled(isUnchanged)
  164. .tint(.white)
  165. .clipShape(RoundedRectangle(cornerRadius: 8))
  166. .padding(5)
  167. }
  168. }
  169. private var fontSizePicker: some View {
  170. Picker("Font Size", selection: $contactTrickEntry.fontSize) {
  171. ForEach(ContactTrickEntry.FontSize.allCases, id: \.self) { size in
  172. Text(size.displayName).tag(size)
  173. }
  174. }
  175. }
  176. private var secondaryFontSizePicker: some View {
  177. Picker("Secondary Font Size", selection: $contactTrickEntry.secondaryFontSize) {
  178. ForEach(ContactTrickEntry.FontSize.allCases, id: \.self) { size in
  179. Text(size.displayName).tag(size)
  180. }
  181. }
  182. }
  183. private var fontWeightPicker: some View {
  184. Picker("Font Weight", selection: $contactTrickEntry.fontWeight) {
  185. ForEach(
  186. [Font.Weight.light, Font.Weight.regular, Font.Weight.medium, Font.Weight.bold, Font.Weight.black],
  187. id: \.self
  188. ) { weight in
  189. Text("\(weight.displayName)".capitalized).tag(weight)
  190. }
  191. }
  192. }
  193. private var fontWidthPicker: some View {
  194. Picker("Font Width", selection: $contactTrickEntry.fontWidth) {
  195. ForEach(
  196. [Font.Width.standard, Font.Width.expanded],
  197. id: \.self
  198. ) { width in
  199. Text("\(width.displayName)".capitalized).tag(width)
  200. }
  201. }
  202. }
  203. }