Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No phone Auth flow #801

Merged
merged 4 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,6 @@ package.addModules([
"MainFeature",
"OnboardingFeature",
"OverlayWindowClient",
"CreateAccountFeature",
"NetworkSwitchingClient",
"GatewaysClient",
"SplashFeature",
],
tests: .yes()
Expand Down
45 changes: 11 additions & 34 deletions Sources/Features/AppFeature/App+Reducer.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import AppPreferencesClient
import CreateAccountFeature
import EngineKit
import FeaturePrelude
import GatewaysClient
import MainFeature
import NetworkSwitchingClient
import OnboardingClient
import OnboardingFeature
import SecureStorageClient
import SplashFeature

// MARK: - App
Expand All @@ -21,8 +16,6 @@ public struct App: Sendable, FeatureReducer {

public var root: Root

public var isOnMainnet = true

@PresentationState
public var alert: Alerts.State?

Expand All @@ -43,7 +36,6 @@ public struct App: Sendable, FeatureReducer {
case incompatibleProfileDeleted
case toMain(isAccountRecoveryNeeded: Bool)
case toOnboarding
case currentGatewayChanged(to: Radix.Gateway)
}

public enum ChildAction: Sendable, Equatable {
Expand Down Expand Up @@ -71,8 +63,6 @@ public struct App: Sendable, FeatureReducer {
}

@Dependency(\.continuousClock) var clock
@Dependency(\.networkSwitchingClient) var networkSwitchingClient
@Dependency(\.gatewaysClient) var gatewaysClient
@Dependency(\.errorQueue) var errorQueue
@Dependency(\.appPreferencesClient) var appPreferencesClient

Expand Down Expand Up @@ -103,28 +93,19 @@ public struct App: Sendable, FeatureReducer {
case .task:
let retBuildInfo = buildInformation()
loggerGlobal.info("EngineToolkit commit hash: \(retBuildInfo.version)")
return .merge(
.run { send in
for try await gateways in await gatewaysClient.gatewaysValues() {
guard !Task.isCancelled else { return }
loggerGlobal.notice("Changed network to: \(gateways.current)")
await send(.internal(.currentGatewayChanged(to: gateways.current)))
}
},
.run { send in
for try await error in errorQueue.errors() {
guard !Task.isCancelled else { return }
// Maybe instead we should listen here for the Profile.State change,
// and when it switches to `.ephemeral` we navigate to onboarding.
// For now, we react to the specific error, since the Profile.State is meant to be private.
if error is Profile.ProfileIsUsedOnAnotherDeviceError {
await send(.internal(.toOnboarding))
// A slight delay to allow any modal that may be shown to be dismissed.
try? await clock.sleep(for: .seconds(0.5))
}
return .run { send in
for try await error in errorQueue.errors() {
guard !Task.isCancelled else { return }
// Maybe instead we should listen here for the Profile.State change,
// and when it switches to `.ephemeral` we navigate to onboarding.
// For now, we react to the specific error, since the Profile.State is meant to be private.
if error is Profile.ProfileIsUsedOnAnotherDeviceError {
await send(.internal(.toOnboarding))
// A slight delay to allow any modal that may be shown to be dismissed.
try? await clock.sleep(for: .seconds(0.5))
}
}
)
}

case .alert(.presented(.incompatibleProfileErrorAlert(.deleteWalletDataButtonTapped))):
return .run { send in
Expand All @@ -150,10 +131,6 @@ public struct App: Sendable, FeatureReducer {

case .toOnboarding:
return goToOnboarding(state: &state)

case let .currentGatewayChanged(currentGateway):
state.isOnMainnet = currentGateway.network == .mainnet
return .none
}
}

Expand Down
12 changes: 0 additions & 12 deletions Sources/Features/AppFeature/App+View.swift
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import AssetTransferFeature
import CreateAccountFeature
import FeaturePrelude
import MainFeature
import OnboardingFeature
import SplashFeature

extension App.State {
public var showIsUsingTestnetBanner: Bool {
!isOnMainnet
}
}

// MARK: - App.View
extension App {
@MainActor
Expand All @@ -22,7 +15,6 @@ extension App {
}

public var body: some SwiftUI.View {
let bannerStore = store.scope(state: \.showIsUsingTestnetBanner, action: actionless)
SwitchStore(store.scope(state: \.root, action: Action.child)) { state in
switch state {
case .main:
Expand Down Expand Up @@ -53,10 +45,6 @@ extension App {
state: /App.Alerts.State.incompatibleProfileErrorAlert,
action: App.Alerts.Action.incompatibleProfileErrorAlert
)
.task { @MainActor in
await store.send(.view(.task)).finish()
}
.showDeveloperDisclaimerBanner(bannerStore)
.presentsLoadingViewOverlay()
}
}
Expand Down
34 changes: 33 additions & 1 deletion Sources/Features/MainFeature/Main+Reducer.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import AppPreferencesClient
import FeaturePrelude
import GatewaysClient
import HomeFeature
import SettingsFeature

Expand All @@ -8,6 +9,8 @@ public struct Main: Sendable, FeatureReducer {
// MARK: - Components
public var home: Home.State

public var isOnMainnet = true

// MARK: - Destinations
@PresentationState
public var destination: Destinations.State?
Expand All @@ -17,6 +20,10 @@ public struct Main: Sendable, FeatureReducer {
}
}

public enum ViewAction: Sendable, Equatable {
case task
}

public enum ChildAction: Sendable, Equatable {
case home(Home.Action)
case destination(PresentationAction<Destinations.Action>)
Expand All @@ -26,6 +33,10 @@ public struct Main: Sendable, FeatureReducer {
case removedWallet
}

public enum InternalAction: Sendable, Equatable {
case currentGatewayChanged(to: Radix.Gateway)
}

public struct Destinations: Sendable, Reducer {
public enum State: Sendable, Hashable {
case settings(Settings.State)
Expand All @@ -42,8 +53,8 @@ public struct Main: Sendable, FeatureReducer {
}
}

@Dependency(\.keychainClient) var keychainClient
@Dependency(\.appPreferencesClient) var appPreferencesClient
@Dependency(\.gatewaysClient) var gatewaysClient

public init() {}

Expand All @@ -57,6 +68,19 @@ public struct Main: Sendable, FeatureReducer {
}
}

public func reduce(into state: inout State, viewAction: ViewAction) -> Effect<Action> {
switch viewAction {
case .task:
return .run { send in
for try await gateways in await gatewaysClient.gatewaysValues() {
guard !Task.isCancelled else { return }
loggerGlobal.notice("Changed network to: \(gateways.current)")
await send(.internal(.currentGatewayChanged(to: gateways.current)))
}
}
}
}

public func reduce(into state: inout State, childAction: ChildAction) -> Effect<Action> {
switch childAction {
case .home(.delegate(.displaySettings)):
Expand All @@ -75,4 +99,12 @@ public struct Main: Sendable, FeatureReducer {
return .none
}
}

public func reduce(into state: inout State, internalAction: InternalAction) -> Effect<Action> {
switch internalAction {
case let .currentGatewayChanged(currentGateway):
state.isOnMainnet = currentGateway.network == .mainnet
return .none
}
}
}
11 changes: 11 additions & 0 deletions Sources/Features/MainFeature/Main+View.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ import FeaturePrelude
import HomeFeature
import SettingsFeature

extension Main.State {
public var showIsUsingTestnetBanner: Bool {
!isOnMainnet
}
}

// MARK: - Main.View
extension Main {
@MainActor
Expand All @@ -14,6 +20,7 @@ extension Main {
}

public var body: some SwiftUI.View {
let bannerStore = store.scope(state: \.showIsUsingTestnetBanner, action: actionless)
NavigationStack {
Home.View(
store: store.scope(
Expand All @@ -33,6 +40,10 @@ extension Main {
)
#endif
}
.task { @MainActor in
await store.send(.view(.task)).finish()
}
.showDeveloperDisclaimerBanner(bannerStore)
.presentsDappInteractions()
}
}
Expand Down
8 changes: 4 additions & 4 deletions Sources/Features/SplashFeature/Splash.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ public struct Splash: Sendable, FeatureReducer {

case .didTapToUnlock:
state.biometricsCheckFailed = false
return .run { send in
await send(.internal(.loadProfileOutcome(loadProfile())))
}
return verifyPasscode()
Copy link
Contributor Author

@GhenadieVP GhenadieVP Sep 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verify the passcode again when user taps unlock. This was the main source of the bug, as loading the profile will succeed and then the user will be redirected to home screen,


case let .passcodeCheckFailedAlert(.presented(action)):
switch action {
Expand All @@ -91,6 +89,7 @@ public struct Splash: Sendable, FeatureReducer {
let config = try? result.value

guard config?.isPasscodeSetUp == true else {
state.biometricsCheckFailed = true
state.passcodeCheckFailedAlert = .init(
title: { .init(L10n.Splash.PasscodeCheckFailedAlert.title) },
actions: {
Expand Down Expand Up @@ -121,8 +120,9 @@ public struct Splash: Sendable, FeatureReducer {
}
return delegateCompleted(loadProfileOutcome: outcome, accountRecoveryNeeded: false)

case .accountRecoveryNeeded(_, .failure):
case let .accountRecoveryNeeded(_, .failure(error)):
state.biometricsCheckFailed = true
errorQueue.schedule(error)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Show any error, in some cases the kechain will throw the error that there is no keychain configured.

return .none

case let .accountRecoveryNeeded(outcome, .success(recoveryNeeded)):
Expand Down
11 changes: 0 additions & 11 deletions Tests/Features/AppFeatureTests/AppFeatureTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ final class AppFeatureTests: TestCase {
await store.send(.child(.main(.delegate(.removedWallet)))) {
$0.root = .onboardingCoordinator(.init())
}
XCTAssertFalse(store.state.showIsUsingTestnetBanner)
}

func test_splash__GIVEN__an_existing_profile__WHEN__existing_profile_loaded__THEN__we_navigate_to_main() async throws {
Expand Down Expand Up @@ -65,14 +64,11 @@ final class AppFeatureTests: TestCase {
) {
$0.errorQueue = .liveValue
$0.continuousClock = clock
$0.gatewaysClient.gatewaysValues = { AsyncLazySequence([.init(current: .default)]).eraseToAnyAsyncSequence() }
}

let viewTask = await store.send(.view(.task))

// then
await store.receive(.internal(.currentGatewayChanged(to: .default)))
XCTAssertFalse(store.state.showIsUsingTestnetBanner)
await store.send(.child(.splash(.delegate(.completed(.newUser, accountRecoveryNeeded: false))))) {
$0.root = .onboardingCoordinator(.init())
}
Expand All @@ -90,7 +86,6 @@ final class AppFeatureTests: TestCase {
) {
$0.errorQueue = .liveValue
$0.continuousClock = clock
$0.gatewaysClient.gatewaysValues = { AsyncLazySequence([.init(current: .default)]).eraseToAnyAsyncSequence() }
}

let viewTask = await store.send(.view(.task))
Expand All @@ -104,8 +99,6 @@ final class AppFeatureTests: TestCase {
let outcome = LoadProfileOutcome.usersExistingProfileCouldNotBeLoaded(failure: failure)

// then
await store.receive(.internal(.currentGatewayChanged(to: .default)))
XCTAssertFalse(store.state.showIsUsingTestnetBanner)
await store.send(.child(.splash(.delegate(.completed(outcome, accountRecoveryNeeded: false))))) {
$0.root = .onboardingCoordinator(.init())
}
Expand All @@ -125,7 +118,6 @@ final class AppFeatureTests: TestCase {
) {
$0.errorQueue = .liveValue
$0.continuousClock = clock
$0.gatewaysClient.gatewaysValues = { AsyncLazySequence([.init(current: .default)]).eraseToAnyAsyncSequence() }
}
store.exhaustivity = .off
let viewTask = await store.send(.view(.task))
Expand Down Expand Up @@ -166,7 +158,6 @@ final class AppFeatureTests: TestCase {
) {
$0.errorQueue = .liveValue
$0.continuousClock = clock
$0.gatewaysClient.gatewaysValues = { AsyncLazySequence([.init(current: .default)]).eraseToAnyAsyncSequence() }
$0.appPreferencesClient.deleteProfileAndFactorSources = { _ in
profileDeletedExpectation.fulfill()
}
Expand All @@ -180,8 +171,6 @@ final class AppFeatureTests: TestCase {

let outcome = LoadProfileOutcome.usersExistingProfileCouldNotBeLoaded(failure: .profileVersionOutdated(json: Data([0xDE, 0xAD]), version: badVersion))

await store.receive(.internal(.currentGatewayChanged(to: .default)))
XCTAssertFalse(store.state.showIsUsingTestnetBanner)
await store.send(.child(.splash(.delegate(.completed(outcome, accountRecoveryNeeded: false))))) {
$0.alert = .incompatibleProfileErrorAlert(
.init(
Expand Down
18 changes: 18 additions & 0 deletions Tests/Features/MainFeatureTests/MainFeatureTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,22 @@ final class MainFeatureTests: TestCase {
$0.destination = nil
}
}

func test_displayTestBanner() async {
// given
let store = TestStore(initialState: Main.State(home: .previewValue)) {
Main()
.dependency(\.userDefaultsClient, .noop)
.dependency(\.gatewaysClient.gatewaysValues) { AsyncLazySequence([.init(current: .stokenet)]).eraseToAnyAsyncSequence() }
}

XCTAssertFalse(store.state.showIsUsingTestnetBanner)

await store.send(.view(.task))

await store.receive(.internal(.currentGatewayChanged(to: .stokenet))) {
$0.isOnMainnet = false
}
XCTAssertTrue(store.state.showIsUsingTestnetBanner)
}
}
1 change: 1 addition & 0 deletions Tests/Features/SplashFeatureTests/SplashFeatureTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ final class SplashFeatureTests: TestCase {

await clock.advance(by: .seconds(0.4))
await store.receive(.internal(.passcodeConfigResult(.success(authBiometricsConfig)))) {
$0.biometricsCheckFailed = true
$0.passcodeCheckFailedAlert = .init(
title: { .init(L10n.Splash.PasscodeCheckFailedAlert.title) },
actions: {
Expand Down
Loading