Просмотр исходного кода

Add overview step; adjust when progressbar is visible; refactoring

Deniz Cengiz 1 год назад
Родитель
Сommit
7f8bcefcb3

+ 4 - 0
Trio.xcodeproj/project.pbxproj

@@ -599,6 +599,7 @@
 		DDAA29852D2D1D9E006546A1 /* AdjustmentsRootView+TempTargets.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAA29842D2D1D98006546A1 /* AdjustmentsRootView+TempTargets.swift */; };
 		DDB37CC52D05048F00D99BF4 /* ContactImageStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB37CC42D05048F00D99BF4 /* ContactImageStorage.swift */; };
 		DDB37CC72D05127500D99BF4 /* FontExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB37CC62D05127500D99BF4 /* FontExtensions.swift */; };
+		DDBD53FC2DAA903100F940A6 /* OverviewStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDBD53FB2DAA903100F940A6 /* OverviewStepView.swift */; };
 		DDC38E102D9B377800ADCB46 /* OnboardingView+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC38E0F2D9B376900ADCB46 /* OnboardingView+Util.swift */; };
 		DDCAE8332D78D4A800B1BB51 /* TherapySettingsUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCAE8322D78D49C00B1BB51 /* TherapySettingsUtil.swift */; };
 		DDCE790F2D6F97FC000A4D7A /* SubmodulesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCE790E2D6F97F7000A4D7A /* SubmodulesView.swift */; };
@@ -1391,6 +1392,7 @@
 		DDB37CC32D05044D00D99BF4 /* ContactTrickEntryStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ContactTrickEntryStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
 		DDB37CC42D05048F00D99BF4 /* ContactImageStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactImageStorage.swift; sourceTree = "<group>"; };
 		DDB37CC62D05127500D99BF4 /* FontExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontExtensions.swift; sourceTree = "<group>"; };
+		DDBD53FB2DAA903100F940A6 /* OverviewStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverviewStepView.swift; sourceTree = "<group>"; };
 		DDC38E0F2D9B376900ADCB46 /* OnboardingView+Util.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OnboardingView+Util.swift"; sourceTree = "<group>"; };
 		DDCAE8322D78D49C00B1BB51 /* TherapySettingsUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TherapySettingsUtil.swift; sourceTree = "<group>"; };
 		DDCE790E2D6F97F7000A4D7A /* SubmodulesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubmodulesView.swift; sourceTree = "<group>"; };
@@ -2769,6 +2771,7 @@
 		BD47FDD52D8B64AE0043966B /* OnboardingSteps */ = {
 			isa = PBXGroup;
 			children = (
+				DDBD53FB2DAA903100F940A6 /* OverviewStepView.swift */,
 				BD10516C2DA986DC007C6D89 /* LogoAnimation.swift */,
 				DDF691362DA30332008BF16C /* StartupGuideStepView.swift */,
 				DDF6905B2DA0AFC5008BF16C /* WelcomeStepView.swift */,
@@ -3993,6 +3996,7 @@
 				DDF847E82C5DABA30049BB3B /* WatchConfigAppleWatchView.swift in Sources */,
 				3811DF1025CAAAE200A708ED /* APSManager.swift in Sources */,
 				3870FF4725EC187A0088248F /* BloodGlucose.swift in Sources */,
+				DDBD53FC2DAA903100F940A6 /* OverviewStepView.swift in Sources */,
 				38A0364225ED069400FCBB52 /* TempBasal.swift in Sources */,
 				DD3F1F872D9DDB1200DCE7B3 /* AnimationPlaceholder.swift in Sources */,
 				3811DE1725C9D40400A708ED /* Screen.swift in Sources */,

+ 9 - 0
Trio/Sources/Localizations/Main/Localizable.xcstrings

@@ -103867,6 +103867,9 @@
         }
       }
     },
+    "Here is an overview of what to expect:" : {
+
+    },
     "Hi there!" : {
 
     },
@@ -148949,6 +148952,9 @@
         }
       }
     },
+    "Overview" : {
+
+    },
     "𝑷 = protein(g) × 40%" : {
       "localizations" : {
         "bg" : {
@@ -206324,6 +206330,9 @@
         }
       }
     },
+    "Trio's Onboarding consists of several steps. It takes about 5-10 minutes to complete. We'll guide you through each step." : {
+
+    },
     "Trio's Simple Lock Screen Widget displays current glucose reading, trend arrow, delta and the timestamp of the current reading." : {
       "localizations" : {
         "bg" : {

+ 26 - 27
Trio/Sources/Modules/Onboarding/View/OnboardingRootView.swift

@@ -51,24 +51,25 @@ extension Onboarding {
                     .ignoresSafeArea()
 
                     VStack(spacing: 0) {
-                        // Progress bar
-                        OnboardingProgressBar(
-                            currentStep: currentStep,
-                            currentSubstep: {
-                                switch currentStep {
-                                case .deliveryLimits: return currentDeliverySubstep.rawValue
-                                case .nightscout: return currentNightscoutSubstep.rawValue
-                                default: return nil
-                                }
-                            }(),
-                            stepsWithSubsteps: [
-                                .nightscout: NightscoutSubstep.allCases.count,
-                                .deliveryLimits: DeliveryLimitSubstep.allCases.count
-                            ],
-                            nightscoutSetupOption: state.nightscoutSetupOption
-                        )
-
-                        .padding(.top)
+                        if (nonInfoOnboardingSteps + [OnboardingStep.overview, OnboardingStep.completed]).contains(currentStep) {
+                            // Progress bar
+                            OnboardingProgressBar(
+                                currentStep: currentStep,
+                                currentSubstep: {
+                                    switch currentStep {
+                                    case .deliveryLimits: return currentDeliverySubstep.rawValue
+                                    case .nightscout: return currentNightscoutSubstep.rawValue
+                                    default: return nil
+                                    }
+                                }(),
+                                stepsWithSubsteps: [
+                                    .nightscout: NightscoutSubstep.allCases.count,
+                                    .deliveryLimits: DeliveryLimitSubstep.allCases.count
+                                ],
+                                nightscoutSetupOption: state.nightscoutSetupOption
+                            )
+                            .padding(.top)
+                        }
 
                         // Step content
                         ScrollViewReader { scrollProxy in
@@ -133,6 +134,8 @@ extension Onboarding {
                                             WelcomeStepView()
                                         case .startupGuide:
                                             StartupGuideStepView()
+                                        case .overview:
+                                            OverviewStepView()
                                         case .diagnostics:
                                             DiagnosticsStepView(state: state)
                                         case .nightscout:
@@ -345,12 +348,8 @@ struct OnboardingProgressBar: View {
         .padding(.horizontal)
     }
 
-    private var visibleSteps: [OnboardingStep] {
-        OnboardingStep.allCases.filter { $0 != .welcome && $0 != .completed }
-    }
-
     private var renderedSteps: [(id: String, step: OnboardingStep, substeps: Int?)] {
-        visibleSteps.map {
+        nonInfoOnboardingSteps.map {
             (id: "\($0.rawValue)", step: $0, substeps: stepsWithSubsteps[$0])
         }
     }
@@ -359,8 +358,8 @@ struct OnboardingProgressBar: View {
         // If currentStep is .completed, fill everything
         if currentStep == .completed { return 1.0 }
 
-        if let currentIndex = visibleSteps.firstIndex(of: currentStep),
-           let stepIndex = visibleSteps.firstIndex(of: step),
+        if let currentIndex = nonInfoOnboardingSteps.firstIndex(of: currentStep),
+           let stepIndex = nonInfoOnboardingSteps.firstIndex(of: step),
            stepIndex < currentIndex
         {
             return 1.0
@@ -377,8 +376,8 @@ struct OnboardingProgressBar: View {
         // Handle special case: Nightscout was skipped
         if step == .nightscout,
            nightscoutSetupOption == .skipNightscoutSetup,
-           let currentIndex = visibleSteps.firstIndex(of: currentStep),
-           let nightscoutIndex = visibleSteps.firstIndex(of: .nightscout),
+           let currentIndex = nonInfoOnboardingSteps.firstIndex(of: currentStep),
+           let nightscoutIndex = nonInfoOnboardingSteps.firstIndex(of: .nightscout),
            currentIndex > nightscoutIndex
         {
             return 1.0

+ 2 - 37
Trio/Sources/Modules/Onboarding/View/OnboardingSteps/CompletedStepView.swift

@@ -21,10 +21,10 @@ struct CompletedStepView: View {
 
             VStack(alignment: .leading, spacing: 12) {
                 ForEach(
-                    OnboardingStep.allCases.filter { $0 != .welcome && $0 != .startupGuide && $0 != .completed },
+                    nonInfoOnboardingSteps,
                     id: \.self
                 ) { step in
-                    SettingItemView(step: step, icon: step.iconName, title: step.title)
+                    SettingItemView(step: step, icon: step.iconName, title: step.title, type: .complete)
                 }
             }
             .padding()
@@ -40,38 +40,3 @@ struct CompletedStepView: View {
         .frame(maxWidth: .infinity)
     }
 }
-
-/// A reusable view for displaying setting items in the completed step.
-struct SettingItemView: View {
-    let step: OnboardingStep
-    let icon: String
-    let title: String
-
-    var body: some View {
-        HStack(spacing: 15) {
-            if step == .nightscout {
-                Image(icon)
-                    .resizable()
-                    .scaledToFit()
-                    .frame(width: 40, height: 24)
-                    .colorMultiply(Color.green)
-            } else {
-                Image(systemName: icon)
-                    .font(.system(size: 24))
-                    .foregroundColor(.green)
-                    .frame(width: 40)
-            }
-
-            VStack(alignment: .leading, spacing: 2) {
-                Text(title)
-                    .font(.headline)
-            }
-
-            Spacer()
-
-            Image(systemName: "checkmark")
-                .foregroundColor(.green)
-        }
-        .padding(.vertical, 8)
-    }
-}

+ 29 - 0
Trio/Sources/Modules/Onboarding/View/OnboardingSteps/OverviewStepView.swift

@@ -0,0 +1,29 @@
+//
+//  OverviewStepView.swift
+//  Trio
+//
+//  Created by Cengiz Deniz on 06.04.25.
+//
+import SwiftUI
+
+struct OverviewStepView: View {
+    var body: some View {
+        VStack(alignment: .leading, spacing: 20) {
+            Text("Here is an overview of what to expect:")
+                .font(.headline)
+                .padding(.horizontal)
+
+            VStack(alignment: .center, spacing: 12) {
+                ForEach(
+                    nonInfoOnboardingSteps,
+                    id: \.self
+                ) { step in
+                    SettingItemView(step: step, icon: step.iconName, title: step.title, type: .overview)
+                }
+            }
+            .padding()
+            .background(Color.chart.opacity(0.65))
+            .cornerRadius(10)
+        }
+    }
+}

+ 2 - 0
Trio/Sources/Modules/Onboarding/View/OnboardingSteps/WelcomeStepView.swift

@@ -6,6 +6,8 @@ struct WelcomeStepView: View {
         VStack(alignment: .center, spacing: 20) {
             PulsingLogoAnimation()
 
+            Spacer(minLength: 10)
+
             Text("Hi there!")
                 .font(.title2)
                 .fontWeight(.bold)

+ 80 - 0
Trio/Sources/Modules/Onboarding/View/OnboardingView+Util.swift

@@ -10,6 +10,7 @@ enum OnboardingNavigationDirection {
 enum OnboardingStep: Int, CaseIterable, Identifiable, Equatable {
     case welcome
     case startupGuide
+    case overview
     case diagnostics
     case nightscout
     case unitSelection
@@ -38,6 +39,8 @@ enum OnboardingStep: Int, CaseIterable, Identifiable, Equatable {
             return String(localized: "Welcome to Trio")
         case .startupGuide:
             return String(localized: "Startup Guide")
+        case .overview:
+            return String(localized: "Overview")
         case .diagnostics:
             return String(localized: "Diagnostics")
         case .nightscout:
@@ -70,6 +73,10 @@ enum OnboardingStep: Int, CaseIterable, Identifiable, Equatable {
             return String(
                 localized: "Trio comes with a helpful Startup Guide. We recommend opening it now and following along as you go — side by side."
             )
+        case .overview:
+            return String(
+                localized: "Trio's Onboarding consists of several steps. It takes about 5-10 minutes to complete. We'll guide you through each step."
+            )
         case .diagnostics:
             return String(
                 localized: "By default, Trio collects crash reports and other anonymized data related to errors, exceptions, and overall app performance."
@@ -116,6 +123,8 @@ enum OnboardingStep: Int, CaseIterable, Identifiable, Equatable {
             return "hand.wave.fill"
         case .startupGuide:
             return "list.bullet.clipboard.fill"
+        case .overview:
+            return "checklist.unchecked"
         case .diagnostics:
             return "waveform.badge.magnifyingglass"
         case .nightscout:
@@ -160,6 +169,7 @@ enum OnboardingStep: Int, CaseIterable, Identifiable, Equatable {
              .deliveryLimits,
              .diagnostics,
              .nightscout,
+             .overview,
              .startupGuide,
              .unitSelection,
              .welcome:
@@ -176,6 +186,10 @@ enum OnboardingStep: Int, CaseIterable, Identifiable, Equatable {
     }
 }
 
+var nonInfoOnboardingSteps: [OnboardingStep] { OnboardingStep.allCases
+    .filter { $0 != .welcome && $0 != .startupGuide && $0 != .overview && $0 != .completed }
+}
+
 enum DeliveryLimitSubstep: Int, CaseIterable, Identifiable {
     case maxIOB
     case maxBolus
@@ -382,3 +396,69 @@ struct BulletPoint: View {
         }
     }
 }
+
+enum OnboardingSettingItemType: Equatable, CaseIterable, Identifiable {
+    case overview
+    case complete
+
+    var id: UUID {
+        UUID()
+    }
+}
+
+/// A reusable view for displaying setting items in the completed step.
+struct SettingItemView: View {
+    let step: OnboardingStep
+    let icon: String
+    let title: String
+    let type: OnboardingSettingItemType
+
+    private var accentColor: Color {
+        switch type {
+        case .overview:
+            Color.blue
+        case .complete:
+            Color.green
+        }
+    }
+
+    var body: some View {
+        HStack(spacing: 10) {
+            if step == .nightscout {
+                Image(icon)
+                    .resizable()
+                    .scaledToFit()
+                    .frame(width: 40, height: 24)
+                    .colorMultiply(accentColor)
+            } else {
+                Image(systemName: icon)
+                    .font(.system(size: 24))
+                    .foregroundStyle(accentColor)
+                    .frame(width: 40)
+            }
+
+            VStack(alignment: .leading, spacing: 2) {
+                Text(title)
+                    .font(.headline)
+            }
+
+            Spacer()
+
+            switch type {
+            case .overview:
+                let index = nonInfoOnboardingSteps.firstIndex(of: step) ?? 0
+                let stepNumber = index + 1
+                Text(stepNumber.description)
+                    .bold()
+                    .frame(width: 32, height: 32, alignment: .center)
+                    .background(accentColor)
+                    .foregroundStyle(.white)
+                    .clipShape(Capsule())
+            case .complete:
+                Image(systemName: "checkmark")
+                    .foregroundStyle(accentColor)
+            }
+        }
+        .padding(.vertical, 8)
+    }
+}