WelcomeStepView.swift 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. // LoopFollow
  2. // WelcomeStepView.swift
  3. import SwiftUI
  4. struct WelcomeStepView: View {
  5. @ObservedObject var viewModel: OnboardingViewModel
  6. @Environment(\.accessibilityReduceMotion) private var reduceMotion
  7. @State private var animate = false
  8. var body: some View {
  9. VStack(spacing: 0) {
  10. Spacer()
  11. VStack(spacing: 20) {
  12. AnimatedLoopFollowLogo(size: 140)
  13. .frame(height: 160)
  14. .opacity(animate || reduceMotion ? 1 : 0)
  15. Text("Welcome to LoopFollow")
  16. .font(.largeTitle.weight(.bold))
  17. .multilineTextAlignment(.center)
  18. Text(viewModel.isAlreadyConfigured
  19. ? "You're already set up. You can skip this guide, or walk through it to review your settings."
  20. : "Let's get you connected to your data and set up a few recommended alarms. It only takes a few minutes.")
  21. .font(.body)
  22. .foregroundColor(.secondary)
  23. .multilineTextAlignment(.center)
  24. .padding(.horizontal, 32)
  25. }
  26. Spacer()
  27. VStack(spacing: 12) {
  28. if viewModel.isAlreadyConfigured {
  29. Button { viewModel.skip() } label: {
  30. primaryLabel("Skip")
  31. }
  32. .buttonStyle(.borderedProminent)
  33. Button { advance() } label: {
  34. Text("Review setup anyway")
  35. .font(.body.weight(.medium))
  36. }
  37. } else {
  38. Button { advance() } label: {
  39. primaryLabel("Get Started")
  40. }
  41. .buttonStyle(.borderedProminent)
  42. Button { viewModel.skip() } label: {
  43. Text("Skip")
  44. .font(.body.weight(.medium))
  45. }
  46. }
  47. }
  48. .padding(.horizontal, 24)
  49. .padding(.bottom, 32)
  50. }
  51. .frame(maxWidth: .infinity, maxHeight: .infinity)
  52. .onAppear {
  53. guard !reduceMotion else { return }
  54. withAnimation(.spring(response: 0.6, dampingFraction: 0.7).delay(0.1)) {
  55. animate = true
  56. }
  57. }
  58. }
  59. private func primaryLabel(_ text: String) -> some View {
  60. Text(text)
  61. .font(.body.weight(.semibold))
  62. .frame(maxWidth: .infinity)
  63. .padding(.vertical, 4)
  64. }
  65. private func advance() {
  66. if reduceMotion {
  67. viewModel.advance()
  68. } else {
  69. withAnimation(.easeInOut(duration: 0.3)) { viewModel.advance() }
  70. }
  71. }
  72. }