|
|
@@ -82,6 +82,8 @@ extension Onboarding {
|
|
|
switch currentStep {
|
|
|
case .welcome:
|
|
|
WelcomeStepView()
|
|
|
+ case .unitSelection:
|
|
|
+ UnitSelectionStepView(state: state)
|
|
|
case .glucoseTarget:
|
|
|
GlucoseTargetStepView(state: state)
|
|
|
case .basalProfile:
|
|
|
@@ -90,6 +92,8 @@ extension Onboarding {
|
|
|
CarbRatioStepView(state: state)
|
|
|
case .insulinSensitivity:
|
|
|
InsulinSensitivityStepView(state: state)
|
|
|
+ case .deliveryLimits:
|
|
|
+ DeliveryLimitsStepView(state: state)
|
|
|
case .completed:
|
|
|
CompletedStepView()
|
|
|
}
|
|
|
@@ -195,238 +199,238 @@ struct OnboardingProgressBar: View {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/// A simple animated placeholder for each step
|
|
|
-struct AnimationPlaceholder: View {
|
|
|
- let step: OnboardingStep
|
|
|
- @State private var animationValue: Double = 0
|
|
|
-
|
|
|
- init(for step: OnboardingStep) {
|
|
|
- self.step = step
|
|
|
- }
|
|
|
-
|
|
|
- var body: some View {
|
|
|
- VStack {
|
|
|
- Group {
|
|
|
- switch step {
|
|
|
- case .welcome:
|
|
|
- welcomeAnimation
|
|
|
- case .glucoseTarget:
|
|
|
- glucoseTargetAnimation
|
|
|
- case .basalProfile:
|
|
|
- basalProfileAnimation
|
|
|
- case .carbRatio:
|
|
|
- carbRatioAnimation
|
|
|
- case .insulinSensitivity:
|
|
|
- insulinSensitivityAnimation
|
|
|
- case .completed:
|
|
|
- completedAnimation
|
|
|
- }
|
|
|
- }
|
|
|
- .frame(height: 180)
|
|
|
- }
|
|
|
- .onAppear {
|
|
|
- withAnimation(Animation.easeInOut(duration: 2).repeatForever(autoreverses: true)) {
|
|
|
- animationValue = 1.0
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Custom animated views for each step
|
|
|
- var welcomeAnimation: some View {
|
|
|
- ZStack {
|
|
|
- ForEach(0 ..< 5) { index in
|
|
|
- Image(systemName: "heart.fill")
|
|
|
- .font(.system(size: 40))
|
|
|
- .foregroundColor(step.accentColor.opacity(0.8 - Double(index) * 0.15))
|
|
|
- .offset(x: CGFloat.random(in: -100 ... 100), y: CGFloat.random(in: -60 ... 60))
|
|
|
- .scaleEffect(1.0 + animationValue * 0.3)
|
|
|
- .rotationEffect(.degrees(animationValue * Double.random(in: -30 ... 30)))
|
|
|
- }
|
|
|
-
|
|
|
- Image(systemName: "syringe.fill")
|
|
|
- .font(.system(size: 80))
|
|
|
- .foregroundColor(step.accentColor)
|
|
|
- .scaleEffect(1.0 + animationValue * 0.2)
|
|
|
- .shadow(color: step.accentColor.opacity(0.5), radius: 10 * animationValue, x: 0, y: 0)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- var glucoseTargetAnimation: some View {
|
|
|
- ZStack {
|
|
|
- // Target rings
|
|
|
- ForEach(0 ..< 3) { index in
|
|
|
- Circle()
|
|
|
- .stroke(step.accentColor.opacity(Double(3 - index) * 0.3), lineWidth: 8)
|
|
|
- .frame(width: 120 + CGFloat(index * 40))
|
|
|
- .scaleEffect(1.0 + animationValue * 0.05)
|
|
|
- }
|
|
|
-
|
|
|
- // Arrow
|
|
|
- Image(systemName: "arrow.down.to.line")
|
|
|
- .font(.system(size: 50))
|
|
|
- .foregroundColor(step.accentColor)
|
|
|
- .offset(y: -10 + animationValue * 20)
|
|
|
- .rotationEffect(.degrees(animationValue * 360))
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- var basalProfileAnimation: some View {
|
|
|
- ZStack {
|
|
|
- // Line graph representation
|
|
|
- Path { path in
|
|
|
- let width: CGFloat = 300
|
|
|
- let height: CGFloat = 100
|
|
|
-
|
|
|
- path.move(to: CGPoint(x: 0, y: height * 0.5))
|
|
|
-
|
|
|
- for i in 0 ..< 8 {
|
|
|
- let x = width * CGFloat(i) / 7
|
|
|
- let y = height * (0.5 + (sin(Double(i) * .pi / 3) * 0.4))
|
|
|
-
|
|
|
- if i == 0 {
|
|
|
- path.move(to: CGPoint(x: x, y: y))
|
|
|
- } else {
|
|
|
- path.addLine(to: CGPoint(x: x, y: y))
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- .trim(from: 0, to: animationValue)
|
|
|
- .stroke(step.accentColor, style: StrokeStyle(lineWidth: 5, lineCap: .round, lineJoin: .round))
|
|
|
- .frame(width: 300, height: 100)
|
|
|
-
|
|
|
- // Clock symbols to represent time
|
|
|
- HStack(spacing: 50) {
|
|
|
- Image(systemName: "clock")
|
|
|
- .font(.system(size: 20))
|
|
|
- .foregroundColor(step.accentColor)
|
|
|
- .opacity(animationValue)
|
|
|
-
|
|
|
- Image(systemName: "clock.fill")
|
|
|
- .font(.system(size: 20))
|
|
|
- .foregroundColor(step.accentColor)
|
|
|
- .opacity(animationValue)
|
|
|
-
|
|
|
- Image(systemName: "clock")
|
|
|
- .font(.system(size: 20))
|
|
|
- .foregroundColor(step.accentColor)
|
|
|
- .opacity(animationValue)
|
|
|
- }
|
|
|
- .offset(y: 70)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- var carbRatioAnimation: some View {
|
|
|
- ZStack {
|
|
|
- // Plate
|
|
|
- Circle()
|
|
|
- .fill(Color.gray.opacity(0.1))
|
|
|
- .frame(width: 150)
|
|
|
-
|
|
|
- // Food items
|
|
|
- ForEach(0 ..< 5) { index in
|
|
|
- Image(systemName: [
|
|
|
- "carrot.fill",
|
|
|
- "fork.knife",
|
|
|
- "takeoutbag.and.cup.and.straw.fill",
|
|
|
- "wallet.pass.fill",
|
|
|
- "cup.and.saucer.fill"
|
|
|
- ][index % 5])
|
|
|
- .font(.system(size: 25))
|
|
|
- .foregroundColor(step.accentColor)
|
|
|
- .offset(
|
|
|
- x: cos(Double(index) * .pi * 2 / 5) * 50 * animationValue,
|
|
|
- y: sin(Double(index) * .pi * 2 / 5) * 50 * animationValue
|
|
|
- )
|
|
|
- .rotationEffect(.degrees(animationValue * 360))
|
|
|
- }
|
|
|
-
|
|
|
- // Insulin
|
|
|
- Image(systemName: "drop.fill")
|
|
|
- .font(.system(size: 40))
|
|
|
- .foregroundColor(.blue)
|
|
|
- .scaleEffect(0.8 + animationValue * 0.3)
|
|
|
- .shadow(color: .blue.opacity(0.5), radius: 5, x: 0, y: 0)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- var insulinSensitivityAnimation: some View {
|
|
|
- ZStack {
|
|
|
- // Glucose meter
|
|
|
- RoundedRectangle(cornerRadius: 20)
|
|
|
- .fill(Color.gray.opacity(0.1))
|
|
|
- .frame(width: 120, height: 200)
|
|
|
-
|
|
|
- // Display screen
|
|
|
- RoundedRectangle(cornerRadius: 10)
|
|
|
- .fill(Color.black.opacity(0.1))
|
|
|
- .frame(width: 100, height: 60)
|
|
|
- .offset(y: -60)
|
|
|
-
|
|
|
- // Value on screen
|
|
|
- Text("120")
|
|
|
- .font(.system(size: 24, weight: .bold, design: .monospaced))
|
|
|
- .foregroundColor(step.accentColor)
|
|
|
- .offset(y: -60)
|
|
|
- .opacity(animationValue)
|
|
|
-
|
|
|
- // Insulin drop
|
|
|
- Image(systemName: "drop.fill")
|
|
|
- .font(.system(size: 30))
|
|
|
- .foregroundColor(.blue)
|
|
|
- .offset(y: 20)
|
|
|
- .opacity(1)
|
|
|
-
|
|
|
- // Arrow showing decrease
|
|
|
- Image(systemName: "arrow.down")
|
|
|
- .font(.system(size: 30))
|
|
|
- .foregroundColor(step.accentColor)
|
|
|
- .offset(y: 60)
|
|
|
- .opacity(animationValue)
|
|
|
- .scaleEffect(1.0 + animationValue * 0.5)
|
|
|
-
|
|
|
- // Lower value
|
|
|
- Text("80")
|
|
|
- .font(.system(size: 24, weight: .bold, design: .monospaced))
|
|
|
- .foregroundColor(step.accentColor)
|
|
|
- .offset(y: 100)
|
|
|
- .opacity(animationValue)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- var completedAnimation: some View {
|
|
|
- ZStack {
|
|
|
- // Success checkmark
|
|
|
- Circle()
|
|
|
- .fill(step.accentColor.opacity(0.2))
|
|
|
- .frame(width: 150)
|
|
|
- .scaleEffect(animationValue)
|
|
|
-
|
|
|
- Circle()
|
|
|
- .stroke(step.accentColor, lineWidth: 5)
|
|
|
- .frame(width: 150)
|
|
|
- .scaleEffect(animationValue)
|
|
|
-
|
|
|
- Image(systemName: "checkmark")
|
|
|
- .font(.system(size: 80, weight: .bold))
|
|
|
- .foregroundColor(step.accentColor)
|
|
|
- .offset(y: animationValue * 5)
|
|
|
- .scaleEffect(animationValue)
|
|
|
-
|
|
|
- // Celebrate particles
|
|
|
- ForEach(0 ..< 8) { index in
|
|
|
- Image(systemName: "star.fill")
|
|
|
- .font(.system(size: 20))
|
|
|
- .foregroundColor(step.accentColor)
|
|
|
- .offset(
|
|
|
- x: cos(Double(index) * .pi / 4) * 100 * animationValue,
|
|
|
- y: sin(Double(index) * .pi / 4) * 100 * animationValue
|
|
|
- )
|
|
|
- .opacity(animationValue)
|
|
|
- .scaleEffect(animationValue)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
+///// A simple animated placeholder for each step
|
|
|
+// struct AnimationPlaceholder: View {
|
|
|
+// let step: OnboardingStep
|
|
|
+// @State private var animationValue: Double = 0
|
|
|
+//
|
|
|
+// init(for step: OnboardingStep) {
|
|
|
+// self.step = step
|
|
|
+// }
|
|
|
+//
|
|
|
+// var body: some View {
|
|
|
+// VStack {
|
|
|
+// Group {
|
|
|
+// switch step {
|
|
|
+// case .welcome:
|
|
|
+// welcomeAnimation
|
|
|
+// case .glucoseTarget:
|
|
|
+// glucoseTargetAnimation
|
|
|
+// case .basalProfile:
|
|
|
+// basalProfileAnimation
|
|
|
+// case .carbRatio:
|
|
|
+// carbRatioAnimation
|
|
|
+// case .insulinSensitivity:
|
|
|
+// insulinSensitivityAnimation
|
|
|
+// case .completed:
|
|
|
+// completedAnimation
|
|
|
+// }
|
|
|
+// }
|
|
|
+// .frame(height: 180)
|
|
|
+// }
|
|
|
+// .onAppear {
|
|
|
+// withAnimation(Animation.easeInOut(duration: 2).repeatForever(autoreverses: true)) {
|
|
|
+// animationValue = 1.0
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// // Custom animated views for each step
|
|
|
+// var welcomeAnimation: some View {
|
|
|
+// ZStack {
|
|
|
+// ForEach(0 ..< 5) { index in
|
|
|
+// Image(systemName: "heart.fill")
|
|
|
+// .font(.system(size: 40))
|
|
|
+// .foregroundColor(step.accentColor.opacity(0.8 - Double(index) * 0.15))
|
|
|
+// .offset(x: CGFloat.random(in: -100 ... 100), y: CGFloat.random(in: -60 ... 60))
|
|
|
+// .scaleEffect(1.0 + animationValue * 0.3)
|
|
|
+// .rotationEffect(.degrees(animationValue * Double.random(in: -30 ... 30)))
|
|
|
+// }
|
|
|
+//
|
|
|
+// Image(systemName: "syringe.fill")
|
|
|
+// .font(.system(size: 80))
|
|
|
+// .foregroundColor(step.accentColor)
|
|
|
+// .scaleEffect(1.0 + animationValue * 0.2)
|
|
|
+// .shadow(color: step.accentColor.opacity(0.5), radius: 10 * animationValue, x: 0, y: 0)
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// var glucoseTargetAnimation: some View {
|
|
|
+// ZStack {
|
|
|
+// // Target rings
|
|
|
+// ForEach(0 ..< 3) { index in
|
|
|
+// Circle()
|
|
|
+// .stroke(step.accentColor.opacity(Double(3 - index) * 0.3), lineWidth: 8)
|
|
|
+// .frame(width: 120 + CGFloat(index * 40))
|
|
|
+// .scaleEffect(1.0 + animationValue * 0.05)
|
|
|
+// }
|
|
|
+//
|
|
|
+// // Arrow
|
|
|
+// Image(systemName: "arrow.down.to.line")
|
|
|
+// .font(.system(size: 50))
|
|
|
+// .foregroundColor(step.accentColor)
|
|
|
+// .offset(y: -10 + animationValue * 20)
|
|
|
+// .rotationEffect(.degrees(animationValue * 360))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// var basalProfileAnimation: some View {
|
|
|
+// ZStack {
|
|
|
+// // Line graph representation
|
|
|
+// Path { path in
|
|
|
+// let width: CGFloat = 300
|
|
|
+// let height: CGFloat = 100
|
|
|
+//
|
|
|
+// path.move(to: CGPoint(x: 0, y: height * 0.5))
|
|
|
+//
|
|
|
+// for i in 0 ..< 8 {
|
|
|
+// let x = width * CGFloat(i) / 7
|
|
|
+// let y = height * (0.5 + (sin(Double(i) * .pi / 3) * 0.4))
|
|
|
+//
|
|
|
+// if i == 0 {
|
|
|
+// path.move(to: CGPoint(x: x, y: y))
|
|
|
+// } else {
|
|
|
+// path.addLine(to: CGPoint(x: x, y: y))
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// .trim(from: 0, to: animationValue)
|
|
|
+// .stroke(step.accentColor, style: StrokeStyle(lineWidth: 5, lineCap: .round, lineJoin: .round))
|
|
|
+// .frame(width: 300, height: 100)
|
|
|
+//
|
|
|
+// // Clock symbols to represent time
|
|
|
+// HStack(spacing: 50) {
|
|
|
+// Image(systemName: "clock")
|
|
|
+// .font(.system(size: 20))
|
|
|
+// .foregroundColor(step.accentColor)
|
|
|
+// .opacity(animationValue)
|
|
|
+//
|
|
|
+// Image(systemName: "clock.fill")
|
|
|
+// .font(.system(size: 20))
|
|
|
+// .foregroundColor(step.accentColor)
|
|
|
+// .opacity(animationValue)
|
|
|
+//
|
|
|
+// Image(systemName: "clock")
|
|
|
+// .font(.system(size: 20))
|
|
|
+// .foregroundColor(step.accentColor)
|
|
|
+// .opacity(animationValue)
|
|
|
+// }
|
|
|
+// .offset(y: 70)
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// var carbRatioAnimation: some View {
|
|
|
+// ZStack {
|
|
|
+// // Plate
|
|
|
+// Circle()
|
|
|
+// .fill(Color.gray.opacity(0.1))
|
|
|
+// .frame(width: 150)
|
|
|
+//
|
|
|
+// // Food items
|
|
|
+// ForEach(0 ..< 5) { index in
|
|
|
+// Image(systemName: [
|
|
|
+// "carrot.fill",
|
|
|
+// "fork.knife",
|
|
|
+// "takeoutbag.and.cup.and.straw.fill",
|
|
|
+// "wallet.pass.fill",
|
|
|
+// "cup.and.saucer.fill"
|
|
|
+// ][index % 5])
|
|
|
+// .font(.system(size: 25))
|
|
|
+// .foregroundColor(step.accentColor)
|
|
|
+// .offset(
|
|
|
+// x: cos(Double(index) * .pi * 2 / 5) * 50 * animationValue,
|
|
|
+// y: sin(Double(index) * .pi * 2 / 5) * 50 * animationValue
|
|
|
+// )
|
|
|
+// .rotationEffect(.degrees(animationValue * 360))
|
|
|
+// }
|
|
|
+//
|
|
|
+// // Insulin
|
|
|
+// Image(systemName: "drop.fill")
|
|
|
+// .font(.system(size: 40))
|
|
|
+// .foregroundColor(.blue)
|
|
|
+// .scaleEffect(0.8 + animationValue * 0.3)
|
|
|
+// .shadow(color: .blue.opacity(0.5), radius: 5, x: 0, y: 0)
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// var insulinSensitivityAnimation: some View {
|
|
|
+// ZStack {
|
|
|
+// // Glucose meter
|
|
|
+// RoundedRectangle(cornerRadius: 20)
|
|
|
+// .fill(Color.gray.opacity(0.1))
|
|
|
+// .frame(width: 120, height: 200)
|
|
|
+//
|
|
|
+// // Display screen
|
|
|
+// RoundedRectangle(cornerRadius: 10)
|
|
|
+// .fill(Color.black.opacity(0.1))
|
|
|
+// .frame(width: 100, height: 60)
|
|
|
+// .offset(y: -60)
|
|
|
+//
|
|
|
+// // Value on screen
|
|
|
+// Text("120")
|
|
|
+// .font(.system(size: 24, weight: .bold, design: .monospaced))
|
|
|
+// .foregroundColor(step.accentColor)
|
|
|
+// .offset(y: -60)
|
|
|
+// .opacity(animationValue)
|
|
|
+//
|
|
|
+// // Insulin drop
|
|
|
+// Image(systemName: "drop.fill")
|
|
|
+// .font(.system(size: 30))
|
|
|
+// .foregroundColor(.blue)
|
|
|
+// .offset(y: 20)
|
|
|
+// .opacity(1)
|
|
|
+//
|
|
|
+// // Arrow showing decrease
|
|
|
+// Image(systemName: "arrow.down")
|
|
|
+// .font(.system(size: 30))
|
|
|
+// .foregroundColor(step.accentColor)
|
|
|
+// .offset(y: 60)
|
|
|
+// .opacity(animationValue)
|
|
|
+// .scaleEffect(1.0 + animationValue * 0.5)
|
|
|
+//
|
|
|
+// // Lower value
|
|
|
+// Text("80")
|
|
|
+// .font(.system(size: 24, weight: .bold, design: .monospaced))
|
|
|
+// .foregroundColor(step.accentColor)
|
|
|
+// .offset(y: 100)
|
|
|
+// .opacity(animationValue)
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// var completedAnimation: some View {
|
|
|
+// ZStack {
|
|
|
+// // Success checkmark
|
|
|
+// Circle()
|
|
|
+// .fill(step.accentColor.opacity(0.2))
|
|
|
+// .frame(width: 150)
|
|
|
+// .scaleEffect(animationValue)
|
|
|
+//
|
|
|
+// Circle()
|
|
|
+// .stroke(step.accentColor, lineWidth: 5)
|
|
|
+// .frame(width: 150)
|
|
|
+// .scaleEffect(animationValue)
|
|
|
+//
|
|
|
+// Image(systemName: "checkmark")
|
|
|
+// .font(.system(size: 80, weight: .bold))
|
|
|
+// .foregroundColor(step.accentColor)
|
|
|
+// .offset(y: animationValue * 5)
|
|
|
+// .scaleEffect(animationValue)
|
|
|
+//
|
|
|
+// // Celebrate particles
|
|
|
+// ForEach(0 ..< 8) { index in
|
|
|
+// Image(systemName: "star.fill")
|
|
|
+// .font(.system(size: 20))
|
|
|
+// .foregroundColor(step.accentColor)
|
|
|
+// .offset(
|
|
|
+// x: cos(Double(index) * .pi / 4) * 100 * animationValue,
|
|
|
+// y: sin(Double(index) * .pi / 4) * 100 * animationValue
|
|
|
+// )
|
|
|
+// .opacity(animationValue)
|
|
|
+// .scaleEffect(animationValue)
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
|
|
|
struct Onboarding_Preview: PreviewProvider {
|
|
|
static var previews: some View {
|