ContactTrickDetailView.swift 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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. }
  44. Toggle("High Contrast Mode", isOn: $contactTrickEntry.hasHighContrast)
  45. }.listRowBackground(Color.chart)
  46. Section(header: Text("Display Values")) {
  47. if contactTrickEntry.layout == .single {
  48. Picker("Primary", selection: $contactTrickEntry.primary) {
  49. ForEach(ContactTrickValue.allCases, id: \.id) { value in
  50. Text(value.displayName).tag(value)
  51. }
  52. }
  53. }
  54. Picker("Top Value", selection: $contactTrickEntry.top) {
  55. ForEach(ContactTrickValue.allCases, id: \.id) { value in
  56. Text(value.displayName).tag(value)
  57. }
  58. }
  59. Picker("Bottom Value", selection: $contactTrickEntry.bottom) {
  60. ForEach(ContactTrickValue.allCases, id: \.id) { value in
  61. Text(value.displayName).tag(value)
  62. }
  63. }
  64. }.listRowBackground(Color.chart)
  65. // Ring Settings Section
  66. Section(header: Text("Ring Settings")) {
  67. Picker("Ring Type", selection: $contactTrickEntry.ring) {
  68. ForEach(ContactTrickLargeRing.allCases, id: \.self) { ring in
  69. Text(ring.displayName).tag(ring)
  70. }
  71. }
  72. if contactTrickEntry.ring != .none {
  73. Picker("Ring Width", selection: $contactTrickEntry.ringWidth) {
  74. ForEach(ContactTrickEntry.RingWidth.allCases, id: \.self) { width in
  75. Text(width.displayName).tag(width)
  76. }
  77. }
  78. Picker("Ring Gap", selection: $contactTrickEntry.ringGap) {
  79. ForEach(ContactTrickEntry.RingGap.allCases, id: \.self) { gap in
  80. Text(gap.displayName).tag(gap)
  81. }
  82. }
  83. }
  84. }.listRowBackground(Color.chart)
  85. // Font Settings Section
  86. Section(header: Text("Font Settings")) {
  87. fontSizePicker
  88. secondaryFontSizePicker
  89. fontWeightPicker
  90. fontWidthPicker
  91. }.listRowBackground(Color.chart)
  92. }
  93. }
  94. .navigationTitle("Edit Contact Items")
  95. .navigationBarTitleDisplayMode(.inline)
  96. .safeAreaInset(edge: .bottom, spacing: 0) { stickySaveButton }
  97. .listSectionSpacing(10)
  98. .padding(.top, 30)
  99. .ignoresSafeArea(edges: .top)
  100. .scrollContentBackground(.hidden)
  101. .background(appState.trioBackgroundColor(for: colorScheme))
  102. .toolbar {
  103. ToolbarItem(placement: .topBarTrailing) {
  104. Button(
  105. action: {
  106. state.isHelpSheetPresented.toggle()
  107. },
  108. label: {
  109. Image(systemName: "questionmark.circle")
  110. }
  111. )
  112. }
  113. }
  114. .sheet(isPresented: $state.isHelpSheetPresented) {
  115. NavigationStack {
  116. List {
  117. Text("Lorem Ipsum Dolor Sit Amet")
  118. }
  119. .padding(.trailing, 10)
  120. .navigationBarTitle("Help", displayMode: .inline)
  121. Button { state.isHelpSheetPresented.toggle() }
  122. label: { Text("Got it!").frame(maxWidth: .infinity, alignment: .center) }
  123. .buttonStyle(.bordered)
  124. .padding(.top)
  125. }
  126. .padding()
  127. .presentationDetents(
  128. [.fraction(0.9), .large],
  129. selection: $state.helpSheetDetent
  130. )
  131. }
  132. }
  133. private func saveChanges() {
  134. Task {
  135. await state.updateContact(with: contactTrickEntry)
  136. dismiss()
  137. }
  138. }
  139. var stickySaveButton: some View {
  140. var isUnchanged: Bool { initialContactTrickEntry == contactTrickEntry }
  141. return ZStack {
  142. Rectangle()
  143. .frame(width: UIScreen.main.bounds.width, height: 65)
  144. .foregroundStyle(colorScheme == .dark ? Color.bgDarkerDarkBlue : Color.white)
  145. .background(.thinMaterial)
  146. .opacity(0.8)
  147. .clipShape(Rectangle())
  148. Button(action: {
  149. saveChanges()
  150. }, label: {
  151. Text("Save").padding(10)
  152. })
  153. .frame(width: UIScreen.main.bounds.width * 0.9, alignment: .center)
  154. .background(isUnchanged ? Color(.systemGray4) : Color(.systemBlue))
  155. .disabled(isUnchanged)
  156. .tint(.white)
  157. .clipShape(RoundedRectangle(cornerRadius: 8))
  158. .padding(5)
  159. }
  160. }
  161. private var fontSizePicker: some View {
  162. Picker("Font Size", selection: $contactTrickEntry.fontSize) {
  163. ForEach(ContactTrickEntry.FontSize.allCases, id: \.self) { size in
  164. Text(size.displayName).tag(size)
  165. }
  166. }
  167. }
  168. private var secondaryFontSizePicker: some View {
  169. Picker("Secondary Font Size", selection: $contactTrickEntry.secondaryFontSize) {
  170. ForEach(ContactTrickEntry.FontSize.allCases, id: \.self) { size in
  171. Text(size.displayName).tag(size)
  172. }
  173. }
  174. }
  175. private var fontWeightPicker: some View {
  176. Picker("Font Weight", selection: $contactTrickEntry.fontWeight) {
  177. ForEach(
  178. [Font.Weight.light, Font.Weight.regular, Font.Weight.medium, Font.Weight.bold, Font.Weight.black],
  179. id: \.self
  180. ) { weight in
  181. Text("\(weight.displayName)".capitalized).tag(weight)
  182. }
  183. }
  184. }
  185. private var fontWidthPicker: some View {
  186. Picker("Font Width", selection: $contactTrickEntry.fontWidth) {
  187. ForEach(
  188. [Font.Width.standard, Font.Width.condensed, Font.Width.expanded],
  189. id: \.self
  190. ) { width in
  191. Text("\(width.displayName)".capitalized).tag(width)
  192. }
  193. }
  194. }
  195. }