Onboarding Screen
This commit is contained in:
parent
883ce52011
commit
cc32e881ba
|
@ -72,6 +72,10 @@
|
||||||
0DB8AA81271DC7520035BC9D /* DesignGuide.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB8AA80271DC7520035BC9D /* DesignGuide.swift */; };
|
0DB8AA81271DC7520035BC9D /* DesignGuide.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB8AA80271DC7520035BC9D /* DesignGuide.swift */; };
|
||||||
0DF2DC51272344E400FA31E2 /* EmptyChip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DF2DC50272344E400FA31E2 /* EmptyChip.swift */; };
|
0DF2DC51272344E400FA31E2 /* EmptyChip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DF2DC50272344E400FA31E2 /* EmptyChip.swift */; };
|
||||||
0DF2DC5427235E3E00FA31E2 /* View+InnerShadow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DF2DC5327235E3E00FA31E2 /* View+InnerShadow.swift */; };
|
0DF2DC5427235E3E00FA31E2 /* View+InnerShadow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DF2DC5327235E3E00FA31E2 /* View+InnerShadow.swift */; };
|
||||||
|
2E58E73B274679F000B2B84B /* OnboardingHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E58E73A274679F000B2B84B /* OnboardingHeaderView.swift */; };
|
||||||
|
2EA11F5B27467EF800709571 /* OnboardingFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EA11F5A27467EF800709571 /* OnboardingFooterView.swift */; };
|
||||||
|
2EA11F5D27467F7700709571 /* OnboardingContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EA11F5C27467F7700709571 /* OnboardingContentView.swift */; };
|
||||||
|
2EB660E02747EAB900A06A07 /* OnboardingScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E5C03802738C570008BFFD3 /* OnboardingScreen.swift */; };
|
||||||
660558E9270C7A54009D6954 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 660558E8270C7A54009D6954 /* Colors.xcassets */; };
|
660558E9270C7A54009D6954 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 660558E8270C7A54009D6954 /* Colors.xcassets */; };
|
||||||
660558F7270C862F009D6954 /* Fonts+Generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660558F5270C862F009D6954 /* Fonts+Generated.swift */; };
|
660558F7270C862F009D6954 /* Fonts+Generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660558F5270C862F009D6954 /* Fonts+Generated.swift */; };
|
||||||
660558F8270C862F009D6954 /* XCAssets+Generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660558F6270C862F009D6954 /* XCAssets+Generated.swift */; };
|
660558F8270C862F009D6954 /* XCAssets+Generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660558F6270C862F009D6954 /* XCAssets+Generated.swift */; };
|
||||||
|
@ -190,6 +194,10 @@
|
||||||
0DB8AA80271DC7520035BC9D /* DesignGuide.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DesignGuide.swift; sourceTree = "<group>"; };
|
0DB8AA80271DC7520035BC9D /* DesignGuide.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DesignGuide.swift; sourceTree = "<group>"; };
|
||||||
0DF2DC50272344E400FA31E2 /* EmptyChip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyChip.swift; sourceTree = "<group>"; };
|
0DF2DC50272344E400FA31E2 /* EmptyChip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyChip.swift; sourceTree = "<group>"; };
|
||||||
0DF2DC5327235E3E00FA31E2 /* View+InnerShadow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+InnerShadow.swift"; sourceTree = "<group>"; };
|
0DF2DC5327235E3E00FA31E2 /* View+InnerShadow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+InnerShadow.swift"; sourceTree = "<group>"; };
|
||||||
|
2E58E73A274679F000B2B84B /* OnboardingHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingHeaderView.swift; sourceTree = "<group>"; };
|
||||||
|
2E5C03802738C570008BFFD3 /* OnboardingScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingScreen.swift; sourceTree = "<group>"; };
|
||||||
|
2EA11F5A27467EF800709571 /* OnboardingFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingFooterView.swift; sourceTree = "<group>"; };
|
||||||
|
2EA11F5C27467F7700709571 /* OnboardingContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingContentView.swift; sourceTree = "<group>"; };
|
||||||
660558E8270C7A54009D6954 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = "<group>"; };
|
660558E8270C7A54009D6954 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = "<group>"; };
|
||||||
660558F5270C862F009D6954 /* Fonts+Generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Fonts+Generated.swift"; sourceTree = "<group>"; };
|
660558F5270C862F009D6954 /* Fonts+Generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Fonts+Generated.swift"; sourceTree = "<group>"; };
|
||||||
660558F6270C862F009D6954 /* XCAssets+Generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XCAssets+Generated.swift"; sourceTree = "<group>"; };
|
660558F6270C862F009D6954 /* XCAssets+Generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XCAssets+Generated.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -201,6 +209,7 @@
|
||||||
6654C7402715A47300901167 /* Onboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Onboarding.swift; sourceTree = "<group>"; };
|
6654C7402715A47300901167 /* Onboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Onboarding.swift; sourceTree = "<group>"; };
|
||||||
6654C7432715A4AC00901167 /* OnboardingStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingStoreTests.swift; sourceTree = "<group>"; };
|
6654C7432715A4AC00901167 /* OnboardingStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingStoreTests.swift; sourceTree = "<group>"; };
|
||||||
665C963E272C26E600BC04FB /* CircularFrameBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularFrameBackground.swift; sourceTree = "<group>"; };
|
665C963E272C26E600BC04FB /* CircularFrameBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularFrameBackground.swift; sourceTree = "<group>"; };
|
||||||
|
66779071273AAC26003A1540 /* OnboardingScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingScreen.swift; sourceTree = "<group>"; };
|
||||||
669FDAE8272C23B3007B9422 /* CircularFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularFrame.swift; sourceTree = "<group>"; };
|
669FDAE8272C23B3007B9422 /* CircularFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularFrame.swift; sourceTree = "<group>"; };
|
||||||
669FDAEA272C23C2007B9422 /* CircularFrameBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularFrameBadge.swift; sourceTree = "<group>"; };
|
669FDAEA272C23C2007B9422 /* CircularFrameBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularFrameBadge.swift; sourceTree = "<group>"; };
|
||||||
66A0807A271993C500118B79 /* OnboardingProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingProgressIndicator.swift; sourceTree = "<group>"; };
|
66A0807A271993C500118B79 /* OnboardingProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingProgressIndicator.swift; sourceTree = "<group>"; };
|
||||||
|
@ -274,6 +283,7 @@
|
||||||
0D1922EB26BDD9A500052649 /* Screens */ = {
|
0D1922EB26BDD9A500052649 /* Screens */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
2E5C037F2738C55F008BFFD3 /* Onboarding */,
|
||||||
0D864A0B26E1580700A61879 /* Loading */,
|
0D864A0B26E1580700A61879 /* Loading */,
|
||||||
0D864A0626E154D100A61879 /* Error */,
|
0D864A0626E154D100A61879 /* Error */,
|
||||||
0D32282F26C5874B00262533 /* Balance */,
|
0D32282F26C5874B00262533 /* Balance */,
|
||||||
|
@ -370,6 +380,7 @@
|
||||||
0D4E7A1926B364180058B01E /* secantTests */,
|
0D4E7A1926B364180058B01E /* secantTests */,
|
||||||
0D4E7A2426B364180058B01E /* secantUITests */,
|
0D4E7A2426B364180058B01E /* secantUITests */,
|
||||||
0D4E7A0626B364170058B01E /* Products */,
|
0D4E7A0626B364170058B01E /* Products */,
|
||||||
|
2EB660DF2747EA6000A06A07 /* Recovered References */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
@ -581,6 +592,25 @@
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
2E5C037F2738C55F008BFFD3 /* Onboarding */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2E5C03802738C570008BFFD3 /* OnboardingScreen.swift */,
|
||||||
|
2E58E73A274679F000B2B84B /* OnboardingHeaderView.swift */,
|
||||||
|
2EA11F5C27467F7700709571 /* OnboardingContentView.swift */,
|
||||||
|
2EA11F5A27467EF800709571 /* OnboardingFooterView.swift */,
|
||||||
|
);
|
||||||
|
path = Onboarding;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
2EB660DF2747EA6000A06A07 /* Recovered References */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
66779071273AAC26003A1540 /* OnboardingScreen.swift */,
|
||||||
|
);
|
||||||
|
name = "Recovered References";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
660558F4270C85F7009D6954 /* Generated */ = {
|
660558F4270C85F7009D6954 /* Generated */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -887,7 +917,7 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "swiftlint_version=0.44.0\n\nif which swiftlint >/dev/null; then\n if [ $(swiftlint version) = $swiftlint_version ]; then\n swiftlint\n else\n echo \"warning: Compatible SwiftLint version not installed, download version $swiftlint_version from https://github.com/realm/SwiftLint. Currently installed version is $(swiftlint version)\"\n fi \nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
|
shellScript = "swiftlint_version=0.45.0\n\nif which swiftlint >/dev/null; then\n if [ $(swiftlint version) = $swiftlint_version ]; then\n swiftlint\n else\n echo \"warning: Compatible SwiftLint version not installed, download version $swiftlint_version from https://github.com/realm/SwiftLint. Currently installed version is $(swiftlint version)\"\n fi \nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
|
||||||
};
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
|
@ -896,6 +926,7 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
2EB660E02747EAB900A06A07 /* OnboardingScreen.swift in Sources */,
|
||||||
660558F8270C862F009D6954 /* XCAssets+Generated.swift in Sources */,
|
660558F8270C862F009D6954 /* XCAssets+Generated.swift in Sources */,
|
||||||
F96B41E9273B501F0021B49A /* TransactionHistoryView.swift in Sources */,
|
F96B41E9273B501F0021B49A /* TransactionHistoryView.swift in Sources */,
|
||||||
0D32281F26C5867D00262533 /* ScanQrScreenViewModel.swift in Sources */,
|
0D32281F26C5867D00262533 /* ScanQrScreenViewModel.swift in Sources */,
|
||||||
|
@ -929,6 +960,7 @@
|
||||||
0D32282326C586A800262533 /* HistoryScreen.swift in Sources */,
|
0D32282326C586A800262533 /* HistoryScreen.swift in Sources */,
|
||||||
0D864A0A26E154FD00A61879 /* InitFailedScreenViewModel.swift in Sources */,
|
0D864A0A26E154FD00A61879 /* InitFailedScreenViewModel.swift in Sources */,
|
||||||
0DA13CA526C1963000E3B610 /* Balance.swift in Sources */,
|
0DA13CA526C1963000E3B610 /* Balance.swift in Sources */,
|
||||||
|
2EA11F5B27467EF800709571 /* OnboardingFooterView.swift in Sources */,
|
||||||
66D50668271D9B6100E51F0D /* NavigationButtonStyle.swift in Sources */,
|
66D50668271D9B6100E51F0D /* NavigationButtonStyle.swift in Sources */,
|
||||||
0D1922F826BDEB3500052649 /* MockServices.swift in Sources */,
|
0D1922F826BDEB3500052649 /* MockServices.swift in Sources */,
|
||||||
0D4E7A0B26B364170058B01E /* ContentView.swift in Sources */,
|
0D4E7A0B26B364170058B01E /* ContentView.swift in Sources */,
|
||||||
|
@ -949,8 +981,10 @@
|
||||||
0D32281A26C5864B00262533 /* ProfileScreenViewModel.swift in Sources */,
|
0D32281A26C5864B00262533 /* ProfileScreenViewModel.swift in Sources */,
|
||||||
0D185819272723FF0046B928 /* BlueChip.swift in Sources */,
|
0D185819272723FF0046B928 /* BlueChip.swift in Sources */,
|
||||||
0D864A0F26E1583000A61879 /* LoadingScreenViewModel.swift in Sources */,
|
0D864A0F26E1583000A61879 /* LoadingScreenViewModel.swift in Sources */,
|
||||||
|
2EA11F5D27467F7700709571 /* OnboardingContentView.swift in Sources */,
|
||||||
0DA13CA126C1955600E3B610 /* HomeScreen.swift in Sources */,
|
0DA13CA126C1955600E3B610 /* HomeScreen.swift in Sources */,
|
||||||
0DA13C9026C15D1D00E3B610 /* WelcomeScreenViewModel.swift in Sources */,
|
0DA13C9026C15D1D00E3B610 /* WelcomeScreenViewModel.swift in Sources */,
|
||||||
|
2E58E73B274679F000B2B84B /* OnboardingHeaderView.swift in Sources */,
|
||||||
66A0807B271993C500118B79 /* OnboardingProgressIndicator.swift in Sources */,
|
66A0807B271993C500118B79 /* OnboardingProgressIndicator.swift in Sources */,
|
||||||
663FAB9E271D875700E495F8 /* CreateButton.swift in Sources */,
|
663FAB9E271D875700E495F8 /* CreateButton.swift in Sources */,
|
||||||
0D7DF08C271DCC0E00530046 /* ScreenBackground.swift in Sources */,
|
0D7DF08C271DCC0E00530046 /* ScreenBackground.swift in Sources */,
|
||||||
|
|
|
@ -26,7 +26,10 @@ struct HomeView: View {
|
||||||
HStack {
|
HStack {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Text("Route: \(String(dumping: viewStore.route))")
|
Text("Route: \(String(dumping: viewStore.route))")
|
||||||
Text("SelectedTransaction: \(String(dumping: viewStore.transactionHistoryState.route.map(/TransactionHistoryState.Route.showTransaction)))")
|
Text(
|
||||||
|
// swiftlint:disable:next line_length
|
||||||
|
"SelectedTransaction: \(String(dumping: viewStore.transactionHistoryState.route.map(/TransactionHistoryState.Route.showTransaction)))"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
|
|
@ -9,20 +9,22 @@ import Foundation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import ComposableArchitecture
|
import ComposableArchitecture
|
||||||
|
|
||||||
struct OnboardingStep: Equatable, Identifiable {
|
struct OnboardingState: Equatable {
|
||||||
|
struct Step: Equatable, Identifiable {
|
||||||
let id: UUID
|
let id: UUID
|
||||||
let title: String
|
let title: String
|
||||||
let description: String
|
let description: String
|
||||||
}
|
let background: Image
|
||||||
|
let badge: Badge
|
||||||
|
}
|
||||||
|
|
||||||
struct OnboardingState: Equatable {
|
var steps: IdentifiedArrayOf<Step> = Self.onboardingSteps
|
||||||
var steps: IdentifiedArrayOf<OnboardingStep> = Self.onboardingSteps
|
|
||||||
var index = 0
|
var index = 0
|
||||||
var skippedAtindex: Int?
|
var skippedAtindex: Int?
|
||||||
|
|
||||||
var currentStep: OnboardingStep { steps[index] }
|
var currentStep: Step { steps[index] }
|
||||||
var skipButtonDisabled: Bool { steps.count == index + 1 }
|
var isFinalStep: Bool { steps.count == index + 1 }
|
||||||
var backButtonDisabled: Bool { index == 0 }
|
var isInitialStep: Bool { index == 0 }
|
||||||
var progress: Int { ((index + 1) * 100) / (steps.count) }
|
var progress: Int { ((index + 1) * 100) / (steps.count) }
|
||||||
var offset: CGFloat {
|
var offset: CGFloat {
|
||||||
let maxOffset = CGFloat(-60)
|
let maxOffset = CGFloat(-60)
|
||||||
|
@ -39,7 +41,10 @@ enum OnboardingAction: Equatable {
|
||||||
case createNewWallet
|
case createNewWallet
|
||||||
}
|
}
|
||||||
|
|
||||||
let onboardingReducer = Reducer<OnboardingState, OnboardingAction, Void> { state, action, _ in
|
typealias OnboardingReducer = Reducer<OnboardingState, OnboardingAction, Void>
|
||||||
|
|
||||||
|
extension OnboardingReducer {
|
||||||
|
static let `default` = Reducer<OnboardingState, OnboardingAction, Void> { state, action, _ in
|
||||||
switch action {
|
switch action {
|
||||||
case .back:
|
case .back:
|
||||||
guard state.index > 0 else { return .none }
|
guard state.index > 0 else { return .none }
|
||||||
|
@ -57,6 +62,7 @@ let onboardingReducer = Reducer<OnboardingState, OnboardingAction, Void> { state
|
||||||
return .none
|
return .none
|
||||||
|
|
||||||
case .skip:
|
case .skip:
|
||||||
|
guard state.skippedAtindex == nil else { return .none }
|
||||||
state.skippedAtindex = state.index
|
state.skippedAtindex = state.index
|
||||||
state.index = state.steps.count - 1
|
state.index = state.steps.count - 1
|
||||||
return .none
|
return .none
|
||||||
|
@ -64,4 +70,5 @@ let onboardingReducer = Reducer<OnboardingState, OnboardingAction, Void> { state
|
||||||
case .createNewWallet:
|
case .createNewWallet:
|
||||||
return .none
|
return .none
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,13 +16,13 @@ struct OnboardingView: View {
|
||||||
VStack(spacing: 50) {
|
VStack(spacing: 50) {
|
||||||
HStack(spacing: 50) {
|
HStack(spacing: 50) {
|
||||||
Button("Back") { viewStore.send(.back) }
|
Button("Back") { viewStore.send(.back) }
|
||||||
.disabled(viewStore.backButtonDisabled)
|
.disabled(viewStore.isInitialStep)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
Button("Next") { viewStore.send(.next) }
|
Button("Next") { viewStore.send(.next) }
|
||||||
|
|
||||||
Button("Skip") { viewStore.send(.skip) }
|
Button("Skip") { viewStore.send(.skip) }
|
||||||
.disabled(viewStore.skipButtonDisabled)
|
.disabled(viewStore.isFinalStep)
|
||||||
}
|
}
|
||||||
.frame(height: 100)
|
.frame(height: 100)
|
||||||
.padding(.horizontal, 50)
|
.padding(.horizontal, 50)
|
||||||
|
@ -57,25 +57,33 @@ struct OnboardingView: View {
|
||||||
extension OnboardingState {
|
extension OnboardingState {
|
||||||
static let onboardingSteps = IdentifiedArray(
|
static let onboardingSteps = IdentifiedArray(
|
||||||
uniqueElements: [
|
uniqueElements: [
|
||||||
OnboardingStep(
|
Step(
|
||||||
id: UUID(),
|
id: UUID(),
|
||||||
title: "Shielded by Default",
|
title: "Shielded by Default",
|
||||||
description: "Tired of worrying about which wallet you used last? US TOO! Now you don't have to, as all funds will automatically be moved to your shielded wallet (and migrated for you)."
|
description: "Tired of worrying about which wallet you used last? US TOO! Now you don't have to, as all funds will automatically be moved to your shielded wallet (and migrated for you).",
|
||||||
|
background: Asset.Assets.Backgrounds.callout1.image,
|
||||||
|
badge: .shield
|
||||||
),
|
),
|
||||||
OnboardingStep(
|
Step(
|
||||||
id: UUID(),
|
id: UUID(),
|
||||||
title: "Unified Addresses",
|
title: "Unified Addresses",
|
||||||
description: "Tired of worrying about which wallet you used last? US TOO! Now you don't have to, as all funds will automatically be moved to your shielded wallet (and migrated for you)."
|
description: "Tired of worrying about which wallet you used last? US TOO! Now you don't have to, as all funds will automatically be moved to your shielded wallet (and migrated for you).",
|
||||||
|
background: Asset.Assets.Backgrounds.callout2.image,
|
||||||
|
badge: .person
|
||||||
),
|
),
|
||||||
OnboardingStep(
|
Step(
|
||||||
id: UUID(),
|
id: UUID(),
|
||||||
title: "And so much more...",
|
title: "And so much more...",
|
||||||
description: "Faster reverse syncing (yes it's a thing). Liberated Payments, Social Payments, Address Books, in-line ZEC requests, wrapped Bitcoin, fractionalize NFTs, you providing liquidity for anything you want, getting that Defi, and going to Mexico."
|
description: "Faster reverse syncing (yes it's a thing). Liberated Payments, Social Payments, Address Books, in-line ZEC requests, wrapped Bitcoin, fractionalize NFTs, you providing liquidity for anything you want, getting that Defi, and going to Mexico.",
|
||||||
|
background: Asset.Assets.Backgrounds.callout3.image,
|
||||||
|
badge: .list
|
||||||
),
|
),
|
||||||
OnboardingStep(
|
Step(
|
||||||
id: UUID(),
|
id: UUID(),
|
||||||
title: "Ready for the Future",
|
title: "Ready for the Future",
|
||||||
description: "Lets get you set up!"
|
description: "Lets get you set up!",
|
||||||
|
background: Asset.Assets.Backgrounds.callout4.image,
|
||||||
|
badge: .shield
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -87,7 +95,7 @@ struct Onboarding_Previews: PreviewProvider {
|
||||||
OnboardingView(
|
OnboardingView(
|
||||||
store: Store(
|
store: Store(
|
||||||
initialState: OnboardingState(),
|
initialState: OnboardingState(),
|
||||||
reducer: onboardingReducer,
|
reducer: .default,
|
||||||
environment: ()
|
environment: ()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
//
|
||||||
|
// OnboardingContentView.swift
|
||||||
|
// secant-testnet
|
||||||
|
//
|
||||||
|
// Created by Adam Stener on 11/18/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import ComposableArchitecture
|
||||||
|
|
||||||
|
struct OnboardingContentView: View {
|
||||||
|
let store: Store<OnboardingState, OnboardingAction>
|
||||||
|
let width: Double
|
||||||
|
let height: Double
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
WithViewStore(self.store) { viewStore in
|
||||||
|
ZStack {
|
||||||
|
if viewStore.isFinalStep {
|
||||||
|
VStack {
|
||||||
|
Asset.Assets.Backgrounds.callout4.image
|
||||||
|
.resizable()
|
||||||
|
.frame(
|
||||||
|
width: width,
|
||||||
|
height: height * 0.6
|
||||||
|
)
|
||||||
|
.aspectRatio(contentMode: .fill)
|
||||||
|
.edgesIgnoringSafeArea(.all)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.transition(.opacity)
|
||||||
|
} else {
|
||||||
|
CircularFrame()
|
||||||
|
.backgroundImages(
|
||||||
|
store.actionless.scope(
|
||||||
|
state: { state in
|
||||||
|
CircularFrameBackgroundImages.ViewState(
|
||||||
|
index: state.index,
|
||||||
|
images: state.steps.map { $0.background }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.frame(width: width * 0.85, height: width * 0.85)
|
||||||
|
.badgeIcons(
|
||||||
|
store.actionless.scope(
|
||||||
|
state: { state in
|
||||||
|
BadgesOverlay.ViewState(
|
||||||
|
index: state.index,
|
||||||
|
badges: state.steps.map { $0.badge }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.offset(y: viewStore.offset - height / 7)
|
||||||
|
.transition(.scale(scale: 2).combined(with: .opacity))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ZStack {
|
||||||
|
ForEach(0..<viewStore.steps.count) { stepIndex in
|
||||||
|
VStack(spacing: viewStore.isFinalStep ? 50 : 15) {
|
||||||
|
HStack {
|
||||||
|
Text(viewStore.steps[stepIndex].title)
|
||||||
|
.font(.custom(FontFamily.Roboto.bold.name, size: 30))
|
||||||
|
.fontWeight(.regular)
|
||||||
|
if !viewStore.isFinalStep {
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(viewStore.steps[stepIndex].description)
|
||||||
|
.font(.custom(FontFamily.Roboto.regular.name, size: 15))
|
||||||
|
.lineSpacing(5)
|
||||||
|
}
|
||||||
|
.opacity(stepIndex == viewStore.index ? 1: 0)
|
||||||
|
.padding(.horizontal, 35)
|
||||||
|
.frame(width: width, height: height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.offset(y: viewStore.isFinalStep ? width / 2.3 : viewStore.offset + width / 2.3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OnboardingContentView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
let store = Store(
|
||||||
|
initialState: OnboardingState(index: 2),
|
||||||
|
reducer: OnboardingReducer.default,
|
||||||
|
environment: ()
|
||||||
|
)
|
||||||
|
GeometryReader { proxy in
|
||||||
|
ZStack {
|
||||||
|
OnboardingHeaderView(
|
||||||
|
store: store.scope(
|
||||||
|
state: { state in
|
||||||
|
OnboardingHeaderView.ViewState(
|
||||||
|
isInitialStep: state.isInitialStep,
|
||||||
|
isFinalStep: state.isFinalStep
|
||||||
|
)
|
||||||
|
},
|
||||||
|
action: { action in
|
||||||
|
switch action {
|
||||||
|
case .back: return .back
|
||||||
|
case .skip: return .skip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.zIndex(1)
|
||||||
|
|
||||||
|
OnboardingContentView(
|
||||||
|
store: store,
|
||||||
|
width: proxy.size.width,
|
||||||
|
height: proxy.size.height
|
||||||
|
)
|
||||||
|
.preferredColorScheme(.light)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
//
|
||||||
|
// OnboardingFooterView.swift
|
||||||
|
// secant-testnet
|
||||||
|
//
|
||||||
|
// Created by Adam Stener on 11/18/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import ComposableArchitecture
|
||||||
|
|
||||||
|
struct OnboardingFooterView: View {
|
||||||
|
let store: Store<OnboardingState, OnboardingAction>
|
||||||
|
let animationDuration: CGFloat = 0.8
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
GeometryReader { proxy in
|
||||||
|
WithViewStore(self.store) { viewStore in
|
||||||
|
VStack(spacing: 5) {
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
if viewStore.isFinalStep {
|
||||||
|
Button("Create New Wallet") {
|
||||||
|
withAnimation(.easeInOut(duration: animationDuration)) {
|
||||||
|
viewStore.send(.createNewWallet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.primaryButtonStyle
|
||||||
|
.frame(height: proxy.size.height / 12)
|
||||||
|
.padding(.horizontal, 15)
|
||||||
|
.transition(.opacity)
|
||||||
|
} else {
|
||||||
|
Button("Next") {
|
||||||
|
withAnimation(.easeInOut(duration: animationDuration)) {
|
||||||
|
viewStore.send(.next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.primaryButtonStyle
|
||||||
|
.frame(height: proxy.size.height / 12)
|
||||||
|
.padding(.horizontal, 15)
|
||||||
|
.transition(.opacity)
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressView(
|
||||||
|
"\(viewStore.index + 1)/\(viewStore.steps.count)",
|
||||||
|
value: Double(viewStore.index + 1),
|
||||||
|
total: Double(viewStore.steps.count)
|
||||||
|
)
|
||||||
|
.onboardingProgressStyle
|
||||||
|
.padding(.horizontal, 30)
|
||||||
|
.padding([.vertical], 20)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OnboardingFooterView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
let store = Store<OnboardingState, OnboardingAction>(
|
||||||
|
initialState: OnboardingState(index: 0),
|
||||||
|
reducer: OnboardingReducer.default,
|
||||||
|
environment: ()
|
||||||
|
)
|
||||||
|
|
||||||
|
Group {
|
||||||
|
OnboardingFooterView(store: store)
|
||||||
|
.preferredColorScheme(.dark)
|
||||||
|
.previewDevice("iPhone 13 Pro Max")
|
||||||
|
|
||||||
|
OnboardingFooterView(store: store)
|
||||||
|
.preferredColorScheme(.dark)
|
||||||
|
.previewDevice("iPhone 13 mini")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
//
|
||||||
|
// OnboardingNavigationButtons.swift
|
||||||
|
// secant-testnet
|
||||||
|
//
|
||||||
|
// Created by Adam Stener on 11/18/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import ComposableArchitecture
|
||||||
|
|
||||||
|
struct OnboardingHeaderView: View {
|
||||||
|
struct ViewState: Equatable {
|
||||||
|
let isInitialStep: Bool
|
||||||
|
let isFinalStep: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ViewAction {
|
||||||
|
case back
|
||||||
|
case skip
|
||||||
|
}
|
||||||
|
|
||||||
|
let store: Store<ViewState, ViewAction>
|
||||||
|
let animationDuration: CGFloat = 0.8
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
WithViewStore(self.store) { viewStore in
|
||||||
|
VStack {
|
||||||
|
HStack {
|
||||||
|
if !viewStore.isInitialStep {
|
||||||
|
Button("Back") {
|
||||||
|
withAnimation(.easeInOut(duration: animationDuration)) {
|
||||||
|
viewStore.send(.back)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationButtonStyle
|
||||||
|
.disabled(viewStore.isInitialStep)
|
||||||
|
.frame(width: 75)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
if !viewStore.isFinalStep {
|
||||||
|
Button("Skip") {
|
||||||
|
withAnimation(.easeInOut(duration: animationDuration)) {
|
||||||
|
viewStore.send(.skip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationButtonStyle
|
||||||
|
.disabled(viewStore.isFinalStep)
|
||||||
|
.frame(width: 75)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 30)
|
||||||
|
.frame(height: 40)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.top, 20)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OnboardingHeaderView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
let store = Store<OnboardingState, OnboardingAction>(
|
||||||
|
initialState: OnboardingState(index: 0),
|
||||||
|
reducer: OnboardingReducer.default,
|
||||||
|
environment: ()
|
||||||
|
)
|
||||||
|
|
||||||
|
OnboardingHeaderView(
|
||||||
|
store: store.scope(
|
||||||
|
state: { state in
|
||||||
|
OnboardingHeaderView.ViewState(
|
||||||
|
isInitialStep: state.isInitialStep,
|
||||||
|
isFinalStep: state.isFinalStep
|
||||||
|
)
|
||||||
|
},
|
||||||
|
action: { action in
|
||||||
|
switch action {
|
||||||
|
case .back: return .back
|
||||||
|
case .skip: return .skip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.preferredColorScheme(.light)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
//
|
||||||
|
// OnboardingScreen.swift
|
||||||
|
// secant-testnet
|
||||||
|
//
|
||||||
|
// Created by Adam Stener on 11/7/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import ComposableArchitecture
|
||||||
|
|
||||||
|
struct OnboardingScreen: View {
|
||||||
|
let store: Store<OnboardingState, OnboardingAction>
|
||||||
|
let animationDuration: CGFloat = 0.8
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
GeometryReader { proxy in
|
||||||
|
ZStack {
|
||||||
|
ScreenBackground()
|
||||||
|
.edgesIgnoringSafeArea(.all)
|
||||||
|
|
||||||
|
OnboardingHeaderView(
|
||||||
|
store: store.scope(
|
||||||
|
state: { state in
|
||||||
|
OnboardingHeaderView.ViewState(
|
||||||
|
isInitialStep: state.isInitialStep,
|
||||||
|
isFinalStep: state.isFinalStep
|
||||||
|
)
|
||||||
|
},
|
||||||
|
action: { action in
|
||||||
|
switch action {
|
||||||
|
case .back: return .back
|
||||||
|
case .skip: return .skip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.zIndex(1)
|
||||||
|
|
||||||
|
OnboardingContentView(
|
||||||
|
store: store,
|
||||||
|
width: proxy.size.width,
|
||||||
|
height: proxy.size.height
|
||||||
|
)
|
||||||
|
|
||||||
|
OnboardingFooterView(store: store)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OnboardingScreen_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
OnboardingScreen(
|
||||||
|
store: Store(
|
||||||
|
initialState: OnboardingState(),
|
||||||
|
reducer: OnboardingReducer.default,
|
||||||
|
environment: ()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.preferredColorScheme(.light)
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ import SwiftUI
|
||||||
struct CircularFrame: View {
|
struct CircularFrame: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
GeometryReader { proxy in
|
GeometryReader { proxy in
|
||||||
let lineWidth = proxy.size.width * 0.07
|
let lineWidth = proxy.size.width * 0.05
|
||||||
|
|
||||||
Circle()
|
Circle()
|
||||||
.stroke(lineWidth: lineWidth)
|
.stroke(lineWidth: lineWidth)
|
||||||
|
|
|
@ -6,9 +6,37 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import ComposableArchitecture
|
||||||
|
|
||||||
struct CircularFrameBackgroundImage: ViewModifier {
|
struct CircularFrameBackgroundImages: Animatable, ViewModifier {
|
||||||
|
struct ViewState: Equatable {
|
||||||
|
let index: Int
|
||||||
|
let images: [Image]
|
||||||
|
}
|
||||||
|
|
||||||
|
let store: Store<ViewState, Never>
|
||||||
|
|
||||||
|
func body(content: Content) -> some View {
|
||||||
|
WithViewStore(self.store) { viewStore in
|
||||||
|
ZStack {
|
||||||
|
ForEach(0..<viewStore.images.count - 1) { imageIndex in
|
||||||
|
viewStore.images[imageIndex]
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(1.3, contentMode: .fill)
|
||||||
|
.opacity(imageIndex == viewStore.index ? 1 : 0)
|
||||||
|
.offset(x: imageIndex <= viewStore.index ? 0 : 25)
|
||||||
|
.mask(Circle())
|
||||||
|
}
|
||||||
|
|
||||||
|
content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CircularFrameBackgroundImage: Animatable, ViewModifier {
|
||||||
let image: Image
|
let image: Image
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
func body(content: Content) -> some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
image
|
image
|
||||||
|
@ -25,6 +53,10 @@ extension CircularFrame {
|
||||||
func backgroundImage(_ image: Image) -> some View {
|
func backgroundImage(_ image: Image) -> some View {
|
||||||
modifier(CircularFrameBackgroundImage(image: image))
|
modifier(CircularFrameBackgroundImage(image: image))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func backgroundImages(_ store: Store<CircularFrameBackgroundImages.ViewState, Never>) -> some View {
|
||||||
|
modifier(CircularFrameBackgroundImages(store: store))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CircularFrameBackground_Previews: PreviewProvider {
|
struct CircularFrameBackground_Previews: PreviewProvider {
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import ComposableArchitecture
|
||||||
|
|
||||||
struct BadgeIcon: ViewModifier {
|
enum Badge: Equatable {
|
||||||
enum Badge: Equatable {
|
|
||||||
case shield
|
case shield
|
||||||
case list
|
case list
|
||||||
case person
|
case person
|
||||||
|
@ -20,9 +20,56 @@ struct BadgeIcon: ViewModifier {
|
||||||
case .person: return Asset.Assets.Icons.profile.image
|
case .person: return Asset.Assets.Icons.profile.image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BadgesOverlay: Animatable, ViewModifier {
|
||||||
|
struct ViewState: Equatable {
|
||||||
|
let index: Int
|
||||||
|
let badges: [Badge]
|
||||||
}
|
}
|
||||||
|
|
||||||
let badge: Badge
|
let store: Store<ViewState, Never>
|
||||||
|
|
||||||
|
func body(content: Content) -> some View {
|
||||||
|
WithViewStore(self.store) { viewStore in
|
||||||
|
content
|
||||||
|
.overlay(
|
||||||
|
GeometryReader { proxy in
|
||||||
|
VStack {
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
ZStack {
|
||||||
|
ForEach(0..<viewStore.badges.count) { badgeIndex in
|
||||||
|
viewStore.badges[viewStore.index].image
|
||||||
|
.resizable()
|
||||||
|
.renderingMode(.none)
|
||||||
|
.frame(
|
||||||
|
width: proxy.size.width * 0.35,
|
||||||
|
height: proxy.size.height * 0.35,
|
||||||
|
alignment: .center
|
||||||
|
)
|
||||||
|
.offset(
|
||||||
|
x: 4.0,
|
||||||
|
y: proxy.size.height * 0.15
|
||||||
|
)
|
||||||
|
.opacity(badgeIndex == viewStore.index ? 1 : 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BadgeOverlay: Animatable, ViewModifier {
|
||||||
|
var badge: Badge
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
func body(content: Content) -> some View {
|
||||||
content
|
content
|
||||||
|
@ -37,14 +84,16 @@ struct BadgeIcon: ViewModifier {
|
||||||
badge.image
|
badge.image
|
||||||
.resizable()
|
.resizable()
|
||||||
.frame(
|
.frame(
|
||||||
width: proxy.size.width * 0.5,
|
width: proxy.size.width * 0.35,
|
||||||
height: proxy.size.height * 0.5,
|
height: proxy.size.height * 0.35,
|
||||||
alignment: .center
|
alignment: .center
|
||||||
)
|
)
|
||||||
.offset(
|
.offset(
|
||||||
x: 0.0,
|
x: 4.0,
|
||||||
y: proxy.size.height * 0.21
|
y: proxy.size.height * 0.15
|
||||||
)
|
)
|
||||||
|
.transition(.scale(scale: 2))
|
||||||
|
.transition(.opacity)
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,8 +103,12 @@ struct BadgeIcon: ViewModifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension View {
|
extension View {
|
||||||
func badgeIcon(_ badge: BadgeIcon.Badge) -> some View {
|
func badgeIcon(_ badge: Badge) -> some View {
|
||||||
modifier(BadgeIcon(badge: badge))
|
modifier(BadgeOverlay(badge: badge))
|
||||||
|
}
|
||||||
|
|
||||||
|
func badgeIcons(_ store: Store<BadgesOverlay.ViewState, Never>) -> some View {
|
||||||
|
modifier(BadgesOverlay(store: store))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ struct WithStateBinding<T: Equatable, Content: View>: View {
|
||||||
struct WithStateBinding_Previews: PreviewProvider {
|
struct WithStateBinding_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
|
// swiftlint:disable:next large_tuple
|
||||||
StateContainer(initialState: (false, false, false)) { (binding: Binding<(Bool, Bool, Bool)>) in
|
StateContainer(initialState: (false, false, false)) { (binding: Binding<(Bool, Bool, Bool)>) in
|
||||||
List {
|
List {
|
||||||
NavigationLink(
|
NavigationLink(
|
||||||
|
@ -46,7 +47,7 @@ struct WithStateBinding_Previews: PreviewProvider {
|
||||||
),
|
),
|
||||||
content: {
|
content: {
|
||||||
NavigationLink(
|
NavigationLink(
|
||||||
isActive:$0,
|
isActive: $0,
|
||||||
destination: { Text("Wrapped Custom Binding") },
|
destination: { Text("Wrapped Custom Binding") },
|
||||||
label: { Text("Wrapped Custom Binding") }
|
label: { Text("Wrapped Custom Binding") }
|
||||||
)
|
)
|
||||||
|
|
|
@ -13,15 +13,15 @@ class OnboardingStoreTests: XCTestCase {
|
||||||
func testIncrementingOnboarding() {
|
func testIncrementingOnboarding() {
|
||||||
let store = TestStore(
|
let store = TestStore(
|
||||||
initialState: OnboardingState(),
|
initialState: OnboardingState(),
|
||||||
reducer: onboardingReducer,
|
reducer: OnboardingReducer.default,
|
||||||
environment: ()
|
environment: ()
|
||||||
)
|
)
|
||||||
|
|
||||||
store.send(.next) {
|
store.send(.next) {
|
||||||
$0.index += 1
|
$0.index += 1
|
||||||
|
|
||||||
XCTAssertFalse($0.skipButtonDisabled)
|
XCTAssertFalse($0.isFinalStep)
|
||||||
XCTAssertFalse($0.backButtonDisabled)
|
XCTAssertFalse($0.isInitialStep)
|
||||||
XCTAssertEqual($0.currentStep, $0.steps[1])
|
XCTAssertEqual($0.currentStep, $0.steps[1])
|
||||||
XCTAssertEqual($0.offset, -20.0)
|
XCTAssertEqual($0.offset, -20.0)
|
||||||
XCTAssertEqual($0.progress, 50)
|
XCTAssertEqual($0.progress, 50)
|
||||||
|
@ -30,8 +30,8 @@ class OnboardingStoreTests: XCTestCase {
|
||||||
store.send(.next) {
|
store.send(.next) {
|
||||||
$0.index += 1
|
$0.index += 1
|
||||||
|
|
||||||
XCTAssertFalse($0.skipButtonDisabled)
|
XCTAssertFalse($0.isFinalStep)
|
||||||
XCTAssertFalse($0.backButtonDisabled)
|
XCTAssertFalse($0.isInitialStep)
|
||||||
XCTAssertEqual($0.currentStep, $0.steps[2])
|
XCTAssertEqual($0.currentStep, $0.steps[2])
|
||||||
XCTAssertEqual($0.offset, -40.0)
|
XCTAssertEqual($0.offset, -40.0)
|
||||||
XCTAssertEqual($0.progress, 75)
|
XCTAssertEqual($0.progress, 75)
|
||||||
|
@ -40,8 +40,8 @@ class OnboardingStoreTests: XCTestCase {
|
||||||
store.send(.next) {
|
store.send(.next) {
|
||||||
$0.index += 1
|
$0.index += 1
|
||||||
|
|
||||||
XCTAssertTrue($0.skipButtonDisabled)
|
XCTAssertTrue($0.isFinalStep)
|
||||||
XCTAssertFalse($0.backButtonDisabled)
|
XCTAssertFalse($0.isInitialStep)
|
||||||
XCTAssertEqual($0.currentStep, $0.steps[3])
|
XCTAssertEqual($0.currentStep, $0.steps[3])
|
||||||
XCTAssertEqual($0.offset, -60.0)
|
XCTAssertEqual($0.offset, -60.0)
|
||||||
XCTAssertEqual($0.progress, 100)
|
XCTAssertEqual($0.progress, 100)
|
||||||
|
@ -51,21 +51,21 @@ class OnboardingStoreTests: XCTestCase {
|
||||||
func testIncrementingPastTotalStepsDoesNothing() {
|
func testIncrementingPastTotalStepsDoesNothing() {
|
||||||
let store = TestStore(
|
let store = TestStore(
|
||||||
initialState: OnboardingState(index: 3),
|
initialState: OnboardingState(index: 3),
|
||||||
reducer: onboardingReducer,
|
reducer: OnboardingReducer.default,
|
||||||
environment: ()
|
environment: ()
|
||||||
)
|
)
|
||||||
|
|
||||||
store.send(.next) {
|
store.send(.next) {
|
||||||
XCTAssertTrue($0.skipButtonDisabled)
|
XCTAssertTrue($0.isFinalStep)
|
||||||
XCTAssertFalse($0.backButtonDisabled)
|
XCTAssertFalse($0.isInitialStep)
|
||||||
XCTAssertEqual($0.currentStep, $0.steps[3])
|
XCTAssertEqual($0.currentStep, $0.steps[3])
|
||||||
XCTAssertEqual($0.offset, -60.0)
|
XCTAssertEqual($0.offset, -60.0)
|
||||||
XCTAssertEqual($0.progress, 100)
|
XCTAssertEqual($0.progress, 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
store.send(.next) {
|
store.send(.next) {
|
||||||
XCTAssertTrue($0.skipButtonDisabled)
|
XCTAssertTrue($0.isFinalStep)
|
||||||
XCTAssertFalse($0.backButtonDisabled)
|
XCTAssertFalse($0.isInitialStep)
|
||||||
XCTAssertEqual($0.currentStep, $0.steps[3])
|
XCTAssertEqual($0.currentStep, $0.steps[3])
|
||||||
XCTAssertEqual($0.offset, -60.0)
|
XCTAssertEqual($0.offset, -60.0)
|
||||||
XCTAssertEqual($0.progress, 100)
|
XCTAssertEqual($0.progress, 100)
|
||||||
|
@ -75,15 +75,15 @@ class OnboardingStoreTests: XCTestCase {
|
||||||
func testDecrementingOnboarding() {
|
func testDecrementingOnboarding() {
|
||||||
let store = TestStore(
|
let store = TestStore(
|
||||||
initialState: OnboardingState(index: 2),
|
initialState: OnboardingState(index: 2),
|
||||||
reducer: onboardingReducer,
|
reducer: OnboardingReducer.default,
|
||||||
environment: ()
|
environment: ()
|
||||||
)
|
)
|
||||||
|
|
||||||
store.send(.back) {
|
store.send(.back) {
|
||||||
$0.index -= 1
|
$0.index -= 1
|
||||||
|
|
||||||
XCTAssertFalse($0.skipButtonDisabled)
|
XCTAssertFalse($0.isFinalStep)
|
||||||
XCTAssertFalse($0.backButtonDisabled)
|
XCTAssertFalse($0.isInitialStep)
|
||||||
XCTAssertEqual($0.currentStep, $0.steps[1])
|
XCTAssertEqual($0.currentStep, $0.steps[1])
|
||||||
XCTAssertEqual($0.offset, -20.0)
|
XCTAssertEqual($0.offset, -20.0)
|
||||||
XCTAssertEqual($0.progress, 50)
|
XCTAssertEqual($0.progress, 50)
|
||||||
|
@ -92,8 +92,8 @@ class OnboardingStoreTests: XCTestCase {
|
||||||
store.send(.back) {
|
store.send(.back) {
|
||||||
$0.index -= 1
|
$0.index -= 1
|
||||||
|
|
||||||
XCTAssertFalse($0.skipButtonDisabled)
|
XCTAssertFalse($0.isFinalStep)
|
||||||
XCTAssertTrue($0.backButtonDisabled)
|
XCTAssertTrue($0.isInitialStep)
|
||||||
XCTAssertEqual($0.currentStep, $0.steps[0])
|
XCTAssertEqual($0.currentStep, $0.steps[0])
|
||||||
XCTAssertEqual($0.offset, 0.0)
|
XCTAssertEqual($0.offset, 0.0)
|
||||||
XCTAssertEqual($0.progress, 25)
|
XCTAssertEqual($0.progress, 25)
|
||||||
|
@ -103,21 +103,21 @@ class OnboardingStoreTests: XCTestCase {
|
||||||
func testDecrementingPastFirstStepDoesNothing() {
|
func testDecrementingPastFirstStepDoesNothing() {
|
||||||
let store = TestStore(
|
let store = TestStore(
|
||||||
initialState: OnboardingState(),
|
initialState: OnboardingState(),
|
||||||
reducer: onboardingReducer,
|
reducer: OnboardingReducer.default,
|
||||||
environment: ()
|
environment: ()
|
||||||
)
|
)
|
||||||
|
|
||||||
store.send(.back) {
|
store.send(.back) {
|
||||||
XCTAssertFalse($0.skipButtonDisabled)
|
XCTAssertFalse($0.isFinalStep)
|
||||||
XCTAssertTrue($0.backButtonDisabled)
|
XCTAssertTrue($0.isInitialStep)
|
||||||
XCTAssertEqual($0.currentStep, $0.steps[0])
|
XCTAssertEqual($0.currentStep, $0.steps[0])
|
||||||
XCTAssertEqual($0.offset, 0.0)
|
XCTAssertEqual($0.offset, 0.0)
|
||||||
XCTAssertEqual($0.progress, 25)
|
XCTAssertEqual($0.progress, 25)
|
||||||
}
|
}
|
||||||
|
|
||||||
store.send(.back) {
|
store.send(.back) {
|
||||||
XCTAssertFalse($0.skipButtonDisabled)
|
XCTAssertFalse($0.isFinalStep)
|
||||||
XCTAssertTrue($0.backButtonDisabled)
|
XCTAssertTrue($0.isInitialStep)
|
||||||
XCTAssertEqual($0.currentStep, $0.steps[0])
|
XCTAssertEqual($0.currentStep, $0.steps[0])
|
||||||
XCTAssertEqual($0.offset, 0.0)
|
XCTAssertEqual($0.offset, 0.0)
|
||||||
XCTAssertEqual($0.progress, 25)
|
XCTAssertEqual($0.progress, 25)
|
||||||
|
@ -129,7 +129,7 @@ class OnboardingStoreTests: XCTestCase {
|
||||||
|
|
||||||
let store = TestStore(
|
let store = TestStore(
|
||||||
initialState: OnboardingState(index: initialIndex),
|
initialState: OnboardingState(index: initialIndex),
|
||||||
reducer: onboardingReducer,
|
reducer: OnboardingReducer.default,
|
||||||
environment: ()
|
environment: ()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -137,8 +137,8 @@ class OnboardingStoreTests: XCTestCase {
|
||||||
$0.index = $0.steps.count - 1
|
$0.index = $0.steps.count - 1
|
||||||
$0.skippedAtindex = initialIndex
|
$0.skippedAtindex = initialIndex
|
||||||
|
|
||||||
XCTAssertTrue($0.skipButtonDisabled)
|
XCTAssertTrue($0.isFinalStep)
|
||||||
XCTAssertFalse($0.backButtonDisabled)
|
XCTAssertFalse($0.isInitialStep)
|
||||||
XCTAssertEqual($0.currentStep, $0.steps[3])
|
XCTAssertEqual($0.currentStep, $0.steps[3])
|
||||||
XCTAssertEqual($0.offset, -60.0)
|
XCTAssertEqual($0.offset, -60.0)
|
||||||
XCTAssertEqual($0.progress, 100)
|
XCTAssertEqual($0.progress, 100)
|
||||||
|
@ -148,8 +148,8 @@ class OnboardingStoreTests: XCTestCase {
|
||||||
$0.skippedAtindex = nil
|
$0.skippedAtindex = nil
|
||||||
$0.index = initialIndex
|
$0.index = initialIndex
|
||||||
|
|
||||||
XCTAssertFalse($0.skipButtonDisabled)
|
XCTAssertFalse($0.isFinalStep)
|
||||||
XCTAssertFalse($0.backButtonDisabled)
|
XCTAssertFalse($0.isInitialStep)
|
||||||
XCTAssertEqual($0.currentStep, $0.steps[1])
|
XCTAssertEqual($0.currentStep, $0.steps[1])
|
||||||
XCTAssertEqual($0.offset, -20.0)
|
XCTAssertEqual($0.offset, -20.0)
|
||||||
XCTAssertEqual($0.progress, 50)
|
XCTAssertEqual($0.progress, 50)
|
||||||
|
|
Loading…
Reference in New Issue