CompletionStepView.swift 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. // LoopFollow
  2. // CompletionStepView.swift
  3. import SwiftUI
  4. struct CompletionStepView: View {
  5. @ObservedObject var viewModel: OnboardingViewModel
  6. @Environment(\.accessibilityReduceMotion) private var reduceMotion
  7. @State private var animate = false
  8. /// A short tease of optional features worth discovering later — not part of
  9. /// setup, just a nudge toward a few things that are easy to miss.
  10. private struct Gem: Identifiable {
  11. let id = UUID()
  12. let icon: String
  13. let title: String
  14. let detail: String
  15. }
  16. private let gems: [Gem] = [
  17. Gem(icon: "person.crop.square",
  18. title: "Contact image",
  19. detail: "Show your glucose on your Apple Watch face through a contact."),
  20. Gem(icon: "bell.badge",
  21. title: "Custom alarms",
  22. detail: "Build your own alarms beyond the recommended set."),
  23. Gem(icon: "dot.radiowaves.left.and.right",
  24. title: "Remote commands",
  25. detail: "Send carbs, boluses, and temp targets to Trio or Loop."),
  26. ]
  27. var body: some View {
  28. VStack(spacing: 0) {
  29. Spacer(minLength: 24)
  30. VStack(spacing: 16) {
  31. Image(systemName: "checkmark.circle.fill")
  32. .font(.system(size: 84, weight: .semibold))
  33. .foregroundStyle(.green.gradient)
  34. .scaleEffect(animate || reduceMotion ? 1 : 0.6)
  35. .opacity(animate || reduceMotion ? 1 : 0)
  36. Text("You're all set")
  37. .font(.largeTitle.weight(.bold))
  38. .multilineTextAlignment(.center)
  39. Text("You're ready to go. Adjust everything later from the Menu — and when you have a moment, these are worth a look:")
  40. .font(.body)
  41. .foregroundColor(.secondary)
  42. .multilineTextAlignment(.center)
  43. .padding(.horizontal, 32)
  44. }
  45. gemsCard
  46. .padding(.top, 28)
  47. .padding(.horizontal, 24)
  48. Spacer(minLength: 24)
  49. Button { viewModel.finish() } label: {
  50. Text("Finish")
  51. .font(.body.weight(.semibold))
  52. .frame(maxWidth: .infinity)
  53. .padding(.vertical, 4)
  54. }
  55. .buttonStyle(.borderedProminent)
  56. .padding(.horizontal, 24)
  57. .padding(.bottom, 32)
  58. }
  59. .frame(maxWidth: .infinity, maxHeight: .infinity)
  60. .onAppear {
  61. guard !reduceMotion else { return }
  62. withAnimation(.spring(response: 0.5, dampingFraction: 0.6).delay(0.1)) {
  63. animate = true
  64. }
  65. }
  66. }
  67. private var gemsCard: some View {
  68. VStack(alignment: .leading, spacing: 14) {
  69. ForEach(gems) { gem in
  70. HStack(alignment: .top, spacing: 12) {
  71. Image(systemName: gem.icon)
  72. .font(.body)
  73. .foregroundStyle(Color.accentColor)
  74. .frame(width: 26)
  75. VStack(alignment: .leading, spacing: 2) {
  76. Text(gem.title)
  77. .font(.subheadline.weight(.semibold))
  78. Text(gem.detail)
  79. .font(.caption)
  80. .foregroundColor(.secondary)
  81. .multilineTextAlignment(.leading)
  82. }
  83. Spacer(minLength: 0)
  84. }
  85. }
  86. }
  87. .frame(maxWidth: .infinity, alignment: .leading)
  88. .padding()
  89. .background(
  90. RoundedRectangle(cornerRadius: 14, style: .continuous)
  91. .fill(Color(.secondarySystemGroupedBackground))
  92. )
  93. }
  94. }