CarbsView.swift 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. import SwiftUI
  2. struct CarbsView: View {
  3. @EnvironmentObject var state: WatchStateModel
  4. // Selected nutrient
  5. enum Selection: String {
  6. case carbs
  7. case protein
  8. case fat
  9. }
  10. @State var selection: Selection = .carbs
  11. @State var carbAmount = 0.0
  12. @State var fatAmount = 0.0
  13. @State var proteinAmount = 0.0
  14. @State var colorOfselection: Color = .darkGray
  15. // @State var displayPresets: Bool = false
  16. var numberFormatter: NumberFormatter {
  17. let formatter = NumberFormatter()
  18. formatter.numberStyle = .decimal
  19. formatter.minimum = 0
  20. formatter.maximum = (state.maxCOB ?? 120) as NSNumber
  21. formatter.maximumFractionDigits = 0
  22. formatter.allowsFloats = false
  23. return formatter
  24. }
  25. var body: some View {
  26. VStack {
  27. // nutrient
  28. carbs
  29. if state.displayFatAndProteinOnWatch {
  30. Spacer()
  31. fat
  32. Spacer()
  33. protein
  34. }
  35. buttonStack
  36. }
  37. .onAppear { carbAmount = Double(state.carbsRequired ?? 0) }
  38. }
  39. var nutrient: some View {
  40. HStack {
  41. switch selection {
  42. case .protein:
  43. Text("Protein")
  44. case .fat:
  45. Text("Fat")
  46. default:
  47. Text("Carbs")
  48. }
  49. }.font(.footnote).frame(maxWidth: .infinity, alignment: .center)
  50. }
  51. var carbs: some View {
  52. HStack {
  53. if selection == .carbs {
  54. Button {
  55. WKInterfaceDevice.current().play(.click)
  56. let newValue = carbAmount - 5
  57. carbAmount = max(newValue, 0)
  58. }
  59. label: {
  60. HStack {
  61. Image(systemName: "minus")
  62. Text("") // Ugly fix to increase active tapping (button) area.
  63. }
  64. }
  65. .buttonStyle(.borderless).padding(.leading, 5)
  66. .tint(selection == .carbs ? .blue : .none)
  67. }
  68. Spacer()
  69. Text("🥨")
  70. Spacer()
  71. Text(numberFormatter.string(from: carbAmount as NSNumber)! + " g")
  72. .font(selection == .carbs ? .title : .title3)
  73. .focusable(selection == .carbs)
  74. .digitalCrownRotation(
  75. $carbAmount,
  76. from: 0,
  77. through: Double(state.maxCOB ?? 120),
  78. by: 1,
  79. sensitivity: .medium,
  80. isContinuous: false,
  81. isHapticFeedbackEnabled: true
  82. )
  83. Spacer()
  84. if selection == .carbs {
  85. Button {
  86. WKInterfaceDevice.current().play(.click)
  87. let newValue = carbAmount + 5
  88. carbAmount = min(newValue, Double(state.maxCOB ?? 120))
  89. } label: { Image(systemName: "plus") }
  90. .buttonStyle(.borderless).padding(.trailing, 5)
  91. .tint(selection == .carbs ? .blue : .none)
  92. }
  93. }
  94. .minimumScaleFactor(0.7)
  95. .onTapGesture {
  96. select(entry: .carbs)
  97. }
  98. .background(selection == .carbs && state.displayFatAndProteinOnWatch ? colorOfselection : .black)
  99. .padding(.top)
  100. }
  101. var protein: some View {
  102. HStack {
  103. if selection == .protein {
  104. Button {
  105. WKInterfaceDevice.current().play(.click)
  106. let newValue = proteinAmount - 5
  107. proteinAmount = max(newValue, 0)
  108. } label: {
  109. HStack {
  110. Image(systemName: "minus")
  111. Text("") // Ugly fix to increase active tapping (button) area.
  112. }
  113. }
  114. .buttonStyle(.borderless).padding(.leading, 5)
  115. .tint(selection == .protein ? .blue : .none)
  116. }
  117. Spacer()
  118. Text("🍗")
  119. Spacer()
  120. Text(numberFormatter.string(from: proteinAmount as NSNumber)! + " g")
  121. .font(selection == .protein ? .title : .title3)
  122. .foregroundStyle(.red)
  123. .focusable(selection == .protein)
  124. .digitalCrownRotation(
  125. $proteinAmount,
  126. from: 0,
  127. through: Double(240),
  128. by: 1,
  129. sensitivity: .medium,
  130. isContinuous: false,
  131. isHapticFeedbackEnabled: true
  132. )
  133. Spacer()
  134. if selection == .protein {
  135. Button {
  136. WKInterfaceDevice.current().play(.click)
  137. let newValue = proteinAmount + 5
  138. proteinAmount = min(newValue, Double(240))
  139. } label: { Image(systemName: "plus") }.buttonStyle(.borderless).padding(.trailing, 5)
  140. .tint(selection == .protein ? .blue : .none)
  141. }
  142. }
  143. .minimumScaleFactor(0.7)
  144. .onTapGesture {
  145. select(entry: .protein)
  146. }
  147. .background(selection == .protein ? colorOfselection : .black)
  148. }
  149. var fat: some View {
  150. HStack {
  151. if selection == .fat {
  152. Button {
  153. WKInterfaceDevice.current().play(.click)
  154. let newValue = fatAmount - 5
  155. fatAmount = max(newValue, 0)
  156. } label: {
  157. HStack {
  158. Image(systemName: "minus")
  159. Text("") // Ugly fix to increase active tapping (button) area.
  160. }
  161. }
  162. .buttonStyle(.borderless).padding(.leading, 5)
  163. .tint(selection == .fat ? .blue : .none)
  164. }
  165. Spacer()
  166. Text("🧀")
  167. Spacer()
  168. Text(numberFormatter.string(from: fatAmount as NSNumber)! + " g")
  169. .font(selection == .fat ? .title : .title3)
  170. .foregroundColor(.loopYellow)
  171. .focusable(selection == .fat)
  172. .digitalCrownRotation(
  173. $fatAmount,
  174. from: 0,
  175. through: Double(240),
  176. by: 1,
  177. sensitivity: .medium,
  178. isContinuous: false,
  179. isHapticFeedbackEnabled: true
  180. )
  181. Spacer()
  182. if selection == .fat {
  183. Button {
  184. WKInterfaceDevice.current().play(.click)
  185. let newValue = fatAmount + 5
  186. fatAmount = min(newValue, Double(240))
  187. } label: { Image(systemName: "plus") }
  188. .buttonStyle(.borderless).padding(.trailing, 5)
  189. .tint(selection == .fat ? .blue : .none)
  190. }
  191. }
  192. .minimumScaleFactor(0.7)
  193. .onTapGesture {
  194. select(entry: .fat)
  195. }
  196. .background(selection == .fat ? colorOfselection : .black)
  197. }
  198. var buttonStack: some View {
  199. HStack(spacing: 25) {
  200. /* To do: display the actual meal presets
  201. Button {
  202. displayPresets.toggle()
  203. }
  204. label: { Image(systemName: "menucard.fill") }
  205. .buttonStyle(.borderless)
  206. */
  207. Button {
  208. WKInterfaceDevice.current().play(.click)
  209. // Get amount from displayed string
  210. let amountCarbs = Int(numberFormatter.string(from: carbAmount as NSNumber)!) ?? Int(carbAmount.rounded())
  211. let amountFat = Int(numberFormatter.string(from: fatAmount as NSNumber)!) ?? Int(fatAmount.rounded())
  212. let amountProtein = Int(numberFormatter.string(from: proteinAmount as NSNumber)!) ??
  213. Int(proteinAmount.rounded())
  214. let note = "Via Watch" // Hard-coded note for entries from watch
  215. state.addMeal(amountCarbs, fat: amountFat, protein: amountProtein, note: note)
  216. }
  217. label: { Text("Save") }
  218. .buttonStyle(.borderless)
  219. .font(.callout)
  220. .foregroundColor(carbAmount > 0 || fatAmount > 0 || proteinAmount > 0 ? .blue : .secondary)
  221. .disabled(carbAmount <= 0 && fatAmount <= 0 && proteinAmount <= 0)
  222. }
  223. .frame(maxHeight: .infinity, alignment: .bottom)
  224. .padding(.top)
  225. }
  226. private func select(entry: Selection) {
  227. selection = entry
  228. }
  229. }
  230. struct CarbsView_Previews: PreviewProvider {
  231. static var previews: some View {
  232. let state = WatchStateModel()
  233. state.carbsRequired = 120
  234. return Group {
  235. CarbsView()
  236. CarbsView().previewDevice("Apple Watch Series 5 - 40mm")
  237. CarbsView().previewDevice("Apple Watch Series 3 - 38mm")
  238. }
  239. .environmentObject(state)
  240. }
  241. }