From 9b38dff66027ce0ff47e83a81e42ac29013f82bf Mon Sep 17 00:00:00 2001 From: Alexey Sidorov Date: Thu, 20 Jul 2023 00:14:03 +0200 Subject: [PATCH 1/3] Withdrawal --- .../Models/WithdrawProvider.swift | 15 ++ .../Striga/Mock/MockStrigaLocalProvider.swift | 8 + .../Striga/Models/StrigaWithdrawalInfo.swift | 13 ++ .../DataProviders/StrigaLocalProvider.swift | 13 ++ ...StrigaBankTransferUserDataRepository.swift | 20 ++ .../Resolver+registerAllServices.swift | 89 ++++++--- .../Claim/StrigaClaimTransaction.swift | 39 ++++ .../StrigaRegistrationFirstStepView.swift | 2 +- ...StrigaRegistrationFirstStepViewModel.swift | 2 +- .../StrigaRegistrationSecondStepView.swift | 2 +- ...trigaRegistrationSecondStepViewModel.swift | 2 +- .../StrigaRegistrationTextField.swift | 20 +- .../Subviews/StringRegistrationCell.swift | 8 +- .../Withdraw/WithdrawCoordinator.swift | 60 ++++++ .../BankTransfer/Withdraw/WithdrawView.swift | 128 +++++++++++++ .../Withdraw/WithdrawViewModel.swift | 172 ++++++++++++++++++ .../Scenes/Main/NewHome/HomeCoordinator.swift | 27 +++ 17 files changed, 576 insertions(+), 44 deletions(-) create mode 100644 Packages/KeyAppKit/Sources/BankTransfer/Models/WithdrawProvider.swift create mode 100644 Packages/KeyAppKit/Sources/BankTransfer/Striga/Models/StrigaWithdrawalInfo.swift create mode 100644 p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawCoordinator.swift create mode 100644 p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawView.swift create mode 100644 p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawViewModel.swift diff --git a/Packages/KeyAppKit/Sources/BankTransfer/Models/WithdrawProvider.swift b/Packages/KeyAppKit/Sources/BankTransfer/Models/WithdrawProvider.swift new file mode 100644 index 0000000000..b16889138a --- /dev/null +++ b/Packages/KeyAppKit/Sources/BankTransfer/Models/WithdrawProvider.swift @@ -0,0 +1,15 @@ +import Foundation + +//Can't come up with a good name +public protocol WithdrawalInfoType { + var IBAN: String? { get } + var BIC: String? { get } + var receiver: String { get } +} + +public protocol WithdrawProvider { + associatedtype WithdrawalInfo: WithdrawalInfoType + + func withdrawalInfo() async throws -> WithdrawalInfo + func save(IBAN: String, BIC: String, receiver: String) async throws +} diff --git a/Packages/KeyAppKit/Sources/BankTransfer/Striga/Mock/MockStrigaLocalProvider.swift b/Packages/KeyAppKit/Sources/BankTransfer/Striga/Mock/MockStrigaLocalProvider.swift index d95cd2c3e3..c2135b613d 100644 --- a/Packages/KeyAppKit/Sources/BankTransfer/Striga/Mock/MockStrigaLocalProvider.swift +++ b/Packages/KeyAppKit/Sources/BankTransfer/Striga/Mock/MockStrigaLocalProvider.swift @@ -88,6 +88,14 @@ public actor MockStrigaLocalProvider: StrigaLocalProvider { fatalError() } + public func getCachedWithdrawalInfo() async -> StrigaWithdrawalInfo? { + fatalError() + } + + public func save(withdrawalInfo: StrigaWithdrawalInfo) async throws { + fatalError() + } + public func clear() async { self.cachedRegistrationData = nil self.useCase = .unregisteredUser diff --git a/Packages/KeyAppKit/Sources/BankTransfer/Striga/Models/StrigaWithdrawalInfo.swift b/Packages/KeyAppKit/Sources/BankTransfer/Striga/Models/StrigaWithdrawalInfo.swift new file mode 100644 index 0000000000..b9cb7ebdc0 --- /dev/null +++ b/Packages/KeyAppKit/Sources/BankTransfer/Striga/Models/StrigaWithdrawalInfo.swift @@ -0,0 +1,13 @@ +import Foundation + +public struct StrigaWithdrawalInfo: WithdrawalInfoType, Codable { + public var IBAN: String? + public var BIC: String? + public var receiver: String + + public init(IBAN: String? = nil, BIC: String? = nil, receiver: String) { + self.IBAN = IBAN + self.BIC = BIC + self.receiver = receiver + } +} diff --git a/Packages/KeyAppKit/Sources/BankTransfer/Striga/Repository/DataProviders/StrigaLocalProvider.swift b/Packages/KeyAppKit/Sources/BankTransfer/Striga/Repository/DataProviders/StrigaLocalProvider.swift index 46e9f57371..0c61c3a926 100644 --- a/Packages/KeyAppKit/Sources/BankTransfer/Striga/Repository/DataProviders/StrigaLocalProvider.swift +++ b/Packages/KeyAppKit/Sources/BankTransfer/Striga/Repository/DataProviders/StrigaLocalProvider.swift @@ -10,6 +10,9 @@ public protocol StrigaLocalProvider { func getWhitelistedUserDestinations() async throws -> [StrigaWhitelistAddressResponse] func save(whitelisted: [StrigaWhitelistAddressResponse]) async throws + func getCachedWithdrawalInfo() async -> StrigaWithdrawalInfo? + func save(withdrawalInfo: StrigaWithdrawalInfo) async throws + func clear() async } @@ -39,6 +42,14 @@ public actor StrigaLocalProviderImpl { extension StrigaLocalProviderImpl: StrigaLocalProvider { + public func getCachedWithdrawalInfo() async -> StrigaWithdrawalInfo? { + return get(from: cacheFileFor(.withdrawalInfo)) + } + + public func save(withdrawalInfo: StrigaWithdrawalInfo) async throws { + try await save(model: withdrawalInfo, in: cacheFileFor(.withdrawalInfo)) + } + public func getCachedRegistrationData() -> StrigaUserDetailsResponse? { return get(from: cacheFileFor(.registration)) } @@ -88,6 +99,8 @@ extension StrigaLocalProviderImpl: StrigaLocalProvider { case registration case account case whitelisted + case withdrawalInfo + } private func cacheFileFor(_ name: CacheFileName) -> URL { diff --git a/Packages/KeyAppKit/Sources/BankTransfer/Striga/Repository/StrigaBankTransferUserDataRepository.swift b/Packages/KeyAppKit/Sources/BankTransfer/Striga/Repository/StrigaBankTransferUserDataRepository.swift index 9853387eb2..4850825869 100644 --- a/Packages/KeyAppKit/Sources/BankTransfer/Striga/Repository/StrigaBankTransferUserDataRepository.swift +++ b/Packages/KeyAppKit/Sources/BankTransfer/Striga/Repository/StrigaBankTransferUserDataRepository.swift @@ -412,3 +412,23 @@ private extension UserWallet { self.accounts = UserAccounts(eur: eur, usdc: usdc) } } + +extension StrigaBankTransferUserDataRepository: WithdrawProvider { + public typealias WithdrawalInfo = StrigaWithdrawalInfo + + public func withdrawalInfo() async throws -> WithdrawalInfo { + await localProvider.getCachedWithdrawalInfo() ?? + /// GetAccountStatement here + WithdrawalInfo(IBAN: "IBAN", BIC: "BIC", receiver: "Receiver") + } + + public func save(IBAN: String, BIC: String, receiver: String) async throws { + try await localProvider.save( + withdrawalInfo: .init( + IBAN: IBAN, + BIC: BIC, + receiver: receiver + ) + ) + } +} diff --git a/p2p_wallet/Injection/Resolver+registerAllServices.swift b/p2p_wallet/Injection/Resolver+registerAllServices.swift index 72eac91f63..8e0ac91ef2 100644 --- a/p2p_wallet/Injection/Resolver+registerAllServices.swift +++ b/p2p_wallet/Injection/Resolver+registerAllServices.swift @@ -424,6 +424,68 @@ extension Resolver: ResolverRegistering { } .scope(.session) + register { + StrigaBankTransferUserDataRepository( + localProvider: GlobalAppState.shared.strigaMockingEnabled ? + MockStrigaLocalProvider( + useCase: .unregisteredUser, + hasCachedInput: true + ) : + StrigaLocalProviderImpl(), + remoteProvider: GlobalAppState.shared.strigaMockingEnabled ? + MockStrigaRemoteProvider( + useCase: .unregisteredUser, + mockUserId: "user-id", + mockKYCToken: "kyc-token" + ) : + StrigaRemoteProviderImpl( + baseURL: GlobalAppState.shared.strigaEndpoint, + solanaKeyPair: Resolver.resolve(UserWalletManager.self).wallet?.account + ), + metadataProvider: GlobalAppState.shared.strigaMockingEnabled ? + MockStrigaMetadataProvider( + useCase: .unregisteredUser, + mockUserId: "user-id" + ) : + Resolver.resolve(StrigaMetadataProvider.self), + commonInfoProvider: CommonInfoLocalProviderImpl(), + solanaKeyPair: Resolver.resolve(UserWalletManager.self).wallet?.account + ) + } + .implements(BankTransferUserDataRepository.self) + .scope(.session) + + register { + StrigaBankTransferUserDataRepository( + localProvider: GlobalAppState.shared.strigaMockingEnabled ? + MockStrigaLocalProvider( + useCase: .unregisteredUser, + hasCachedInput: true + ) : + StrigaLocalProviderImpl(), + remoteProvider: GlobalAppState.shared.strigaMockingEnabled ? + MockStrigaRemoteProvider( + useCase: .unregisteredUser, + mockUserId: "user-id", + mockKYCToken: "kyc-token" + ) : + StrigaRemoteProviderImpl( + baseURL: GlobalAppState.shared.strigaEndpoint, + solanaKeyPair: Resolver.resolve(UserWalletManager.self).wallet?.account + ), + metadataProvider: GlobalAppState.shared.strigaMockingEnabled ? + MockStrigaMetadataProvider( + useCase: .unregisteredUser, + mockUserId: "user-id" + ) : + Resolver.resolve(StrigaMetadataProvider.self), + commonInfoProvider: CommonInfoLocalProviderImpl(), + solanaKeyPair: Resolver.resolve(UserWalletManager.self).wallet?.account + ) + } + .implements((any WithdrawProvider).self) + .scope(.session) + register { RelayServiceImpl( contextManager: resolve(), orcaSwap: resolve(), @@ -611,32 +673,7 @@ extension Resolver: ResolverRegistering { register { BankTransferServiceImpl( - repository: StrigaBankTransferUserDataRepository( - localProvider: GlobalAppState.shared.strigaMockingEnabled ? - MockStrigaLocalProvider( - useCase: .unregisteredUser, - hasCachedInput: true - ) : - StrigaLocalProviderImpl(), - remoteProvider: GlobalAppState.shared.strigaMockingEnabled ? - MockStrigaRemoteProvider( - useCase: .unregisteredUser, - mockUserId: "user-id", - mockKYCToken: "kyc-token" - ) : - StrigaRemoteProviderImpl( - baseURL: GlobalAppState.shared.strigaEndpoint, - solanaKeyPair: Resolver.resolve(UserWalletManager.self).wallet?.account - ), - metadataProvider: GlobalAppState.shared.strigaMockingEnabled ? - MockStrigaMetadataProvider( - useCase: .unregisteredUser, - mockUserId: "user-id" - ) : - Resolver.resolve(StrigaMetadataProvider.self), - commonInfoProvider: CommonInfoLocalProviderImpl(), - solanaKeyPair: Resolver.resolve(UserWalletManager.self).wallet?.account - ) + repository: resolve() ) } .implements((any BankTransferService).self) diff --git a/p2p_wallet/Scenes/Main/BankTransfer/Claim/StrigaClaimTransaction.swift b/p2p_wallet/Scenes/Main/BankTransfer/Claim/StrigaClaimTransaction.swift index 5b9f5c7b3b..05a780e934 100644 --- a/p2p_wallet/Scenes/Main/BankTransfer/Claim/StrigaClaimTransaction.swift +++ b/p2p_wallet/Scenes/Main/BankTransfer/Claim/StrigaClaimTransaction.swift @@ -50,3 +50,42 @@ struct StrigaClaimTransaction: StrigaClaimTransactionType { return .fakeTransactionSignature(id: UUID().uuidString) } } + +protocol StrigaWithdrawTransactionType { + var IBAN: String { get } + var BIC: String { get } + var feeAmount: FeeAmount { get } + var amount: Double { get } +} + +/// Default implemetation of `StrigaClaimTransactionType` +struct StrigaWithdrawTransaction: StrigaWithdrawTransactionType { + + // MARK: - Properties + var IBAN: String + + var BIC: String + + var amount: Double + + let token: Token = .usdc + let feeAmount: FeeAmount + let fromAddress: String + let receivingAddress: String + + var mainDescription: String { + "" + } + + // MARK: - Methods + + func createRequest() async throws -> String { + // get transaction from proxy api + + // sign transaction + + // TODO: - send to blockchain + try? await Task.sleep(seconds: 1) + return .fakeTransactionSignature(id: UUID().uuidString) + } +} diff --git a/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/FirstStep/StrigaRegistrationFirstStepView.swift b/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/FirstStep/StrigaRegistrationFirstStepView.swift index 3043ec236a..2a540601ee 100644 --- a/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/FirstStep/StrigaRegistrationFirstStepView.swift +++ b/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/FirstStep/StrigaRegistrationFirstStepView.swift @@ -3,7 +3,7 @@ import KeyAppUI import CountriesAPI fileprivate typealias TextField = StrigaRegistrationTextField -fileprivate typealias Cell = StrigaRegistrationCell +fileprivate typealias Cell = StrigaFormCell fileprivate typealias DetailedButton = StrigaRegistrationDetailedButton struct StrigaRegistrationFirstStepView: View { diff --git a/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/FirstStep/StrigaRegistrationFirstStepViewModel.swift b/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/FirstStep/StrigaRegistrationFirstStepViewModel.swift index b718c2d5c2..e715a98bf9 100644 --- a/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/FirstStep/StrigaRegistrationFirstStepViewModel.swift +++ b/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/FirstStep/StrigaRegistrationFirstStepViewModel.swift @@ -40,7 +40,7 @@ final class StrigaRegistrationFirstStepViewModel: BaseViewModel, ObservableObjec let choosePhoneCountryCode = PassthroughSubject() let back = PassthroughSubject() - var fieldsStatuses = [StrigaRegistrationField: StrigaRegistrationTextFieldStatus]() + var fieldsStatuses = [StrigaRegistrationField: StrigaFormTextFieldStatus]() @Published var selectedCountryOfBirth: Country? @Published private var dateOfBirthModel: StrigaUserDetailsResponse.DateOfBirth? diff --git a/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/SecondStep/StrigaRegistrationSecondStepView.swift b/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/SecondStep/StrigaRegistrationSecondStepView.swift index ac12da6e11..df033d87ad 100644 --- a/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/SecondStep/StrigaRegistrationSecondStepView.swift +++ b/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/SecondStep/StrigaRegistrationSecondStepView.swift @@ -2,7 +2,7 @@ import SwiftUI import KeyAppUI fileprivate typealias TextField = StrigaRegistrationTextField -fileprivate typealias Cell = StrigaRegistrationCell +fileprivate typealias Cell = StrigaFormCell fileprivate typealias DetailedButton = StrigaRegistrationDetailedButton struct StrigaRegistrationSecondStepView: View { diff --git a/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/SecondStep/StrigaRegistrationSecondStepViewModel.swift b/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/SecondStep/StrigaRegistrationSecondStepViewModel.swift index 6f880cc97a..1b4973d47e 100644 --- a/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/SecondStep/StrigaRegistrationSecondStepViewModel.swift +++ b/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/SecondStep/StrigaRegistrationSecondStepViewModel.swift @@ -41,7 +41,7 @@ final class StrigaRegistrationSecondStepViewModel: BaseViewModel, ObservableObje let chooseCountry = PassthroughSubject() let openHardError = PassthroughSubject() - var fieldsStatuses = [StrigaRegistrationField: StrigaRegistrationTextFieldStatus]() + var fieldsStatuses = [StrigaRegistrationField: StrigaFormTextFieldStatus]() @Published var selectedCountry: Country? @Published var selectedIndustry: Industry? diff --git a/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/Subviews/StrigaRegistrationTextField.swift b/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/Subviews/StrigaRegistrationTextField.swift index 9c5036d3d9..4eb81099db 100644 --- a/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/Subviews/StrigaRegistrationTextField.swift +++ b/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/Subviews/StrigaRegistrationTextField.swift @@ -1,25 +1,25 @@ import SwiftUI import KeyAppUI -struct StrigaRegistrationTextField: View { +struct StrigaRegistrationTextField: View { - let field: StrigaRegistrationField + let field: TextFieldType let placeholder: String let isEnabled: Bool let onSubmit: () -> Void let submitLabel: SubmitLabel @Binding var text: String - @Binding var focus: StrigaRegistrationField? + @Binding var focus: TextFieldType? - @FocusState private var isFocused: StrigaRegistrationField? + @FocusState private var isFocused: TextFieldType? init( - field: StrigaRegistrationField, + field: TextFieldType, placeholder: String, text: Binding, isEnabled: Bool = true, - focus: Binding, + focus: Binding, onSubmit: @escaping () -> Void, submitLabel: SubmitLabel ) { @@ -58,8 +58,8 @@ struct StrigaRegistrationTextField: View { struct StrigaRegistrationTextField_Previews: PreviewProvider { static var previews: some View { VStack { - StrigaRegistrationTextField( - field: .email, + StrigaRegistrationTextField( + field: StrigaRegistrationField.email, placeholder: "Enter email", text: .constant(""), focus: .constant(nil), @@ -67,8 +67,8 @@ struct StrigaRegistrationTextField_Previews: PreviewProvider { submitLabel: .next ) - StrigaRegistrationTextField( - field: .phoneNumber, + StrigaRegistrationTextField( + field: StrigaRegistrationField.phoneNumber, placeholder: "Enter phone", text: .constant(""), focus: .constant(nil), diff --git a/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/Subviews/StringRegistrationCell.swift b/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/Subviews/StringRegistrationCell.swift index adbd50a1e1..3dc20e52e1 100644 --- a/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/Subviews/StringRegistrationCell.swift +++ b/p2p_wallet/Scenes/Main/BankTransfer/StrigaRegistration/Subviews/StringRegistrationCell.swift @@ -1,19 +1,19 @@ import SwiftUI import KeyAppUI -enum StrigaRegistrationTextFieldStatus: Equatable { +enum StrigaFormTextFieldStatus: Equatable { case valid case invalid(error: String) } -struct StrigaRegistrationCell: View { +struct StrigaFormCell: View { let title: String - let status: StrigaRegistrationTextFieldStatus + let status: StrigaFormTextFieldStatus @ViewBuilder private var content: () -> Content init( title: String, - status: StrigaRegistrationTextFieldStatus? = .valid, + status: StrigaFormTextFieldStatus? = .valid, @ViewBuilder content: @escaping () -> Content ) { self.title = title diff --git a/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawCoordinator.swift b/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawCoordinator.swift new file mode 100644 index 0000000000..0a7d6f1c2d --- /dev/null +++ b/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawCoordinator.swift @@ -0,0 +1,60 @@ +import Combine +import BankTransfer +import Foundation +import Resolver +import SwiftUI + +final class WithdrawCoordinator: Coordinator { + + let navigationController: UINavigationController + let strategy: Strategy + init( + navigationController: UINavigationController, + strategy: Strategy = .gathering + ) { + self.navigationController = navigationController + self.strategy = strategy + super.init() + } + + override func start() -> AnyPublisher { + let viewModel = WithdrawViewModel( + provider: Resolver.resolve(), + withdrawalInfo: StrigaWithdrawalInfo( + IBAN: nil, + BIC: nil, + receiver: "Alexey Sidorov" + ) + ) + let view = WithdrawView( + viewModel: viewModel + ) + let viewController = UIHostingController(rootView: view) + viewController.hidesBottomBarWhenPushed = true + navigationController.pushViewController(viewController, animated: true) + return Publishers.Merge( + viewController.deallocatedPublisher() + .map { WithdrawCoordinator.Result.canceled }, + viewModel.actionCompletedPublisher + .map { WithdrawCoordinator.Result.verified } + .handleEvents(receiveOutput: { _ in + self.navigationController.popViewController(animated: true) + }) + ) + .prefix(1).eraseToAnyPublisher() + } +} + +extension WithdrawCoordinator { + enum Result { + case verified + case canceled + } + + enum Strategy { + /// Used to collect IBAN + case gathering + /// Used to confirm withdrawal + case confirmation + } +} diff --git a/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawView.swift b/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawView.swift new file mode 100644 index 0000000000..2c938d49a1 --- /dev/null +++ b/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawView.swift @@ -0,0 +1,128 @@ +import SwiftUI +import Resolver +import BankTransfer +import Combine +import KeyAppUI + +struct WithdrawView: View { + @ObservedObject var viewModel: WithdrawViewModel + @State private var focus: WithdrawViewField? + + var body: some View { + ColoredBackground { + VStack { + ScrollView { + form + .animation(.spring(blendDuration: 0.01), value: viewModel.fieldsStatuses) + } + + Spacer() + + NewTextButton( + title: viewModel.actionTitle.uppercaseFirst, + style: .primaryWhite, + expandable: true, + isEnabled: true, + isLoading: viewModel.isLoading, + trailing: viewModel.isDataValid ? .arrowForward : nil, + action: { + resignFirstResponder() + Task { + await viewModel.action() + } + } + ) + .padding(.bottom, 20) + } + .padding(.horizontal, 16) + } + .toolbar { + ToolbarItem(placement: .principal) { + Text(L10n.withdraw) + .fontWeight(.semibold) + } + } + .onDisappear { + resignFirstResponder() + } + } + + var form: some View { + VStack(spacing: 12) { + StrigaFormCell( + title: L10n.iban, + status: viewModel.fieldsStatuses[.IBAN] + ) { + StrigaRegistrationTextField( + field: .IBAN, + placeholder: "", + text: $viewModel.IBAN, + focus: $focus, + onSubmit: { focus = .BIC }, + submitLabel: .next + ) + } + + StrigaFormCell( + title: L10n.bic, + status: viewModel.fieldsStatuses[.BIC] + ) { + StrigaRegistrationTextField( + field: .BIC, + placeholder: "", + text: $viewModel.BIC, + focus: $focus, + onSubmit: { focus = nil }, + submitLabel: .done + ) + } + + VStack(spacing: 4) { + StrigaFormCell( + title: "Receiver", + status: .valid) { + StrigaRegistrationTextField( + field: .receiver, + placeholder: "", + text: $viewModel.receiver, + isEnabled: false, + focus: $focus, + onSubmit: { focus = nil }, + submitLabel: .next + ) + } + Text("Your bank account name must match the name registered to your Key App account") + .apply(style: .label1) + .foregroundColor(Color(Asset.Colors.night.color)) + } + } + } +} + +//struct WithdrawView_Previews: PreviewProvider { +// static var previews: some View { +// WithdrawView(viewModel: WithdrawViewModel(provider: Resolver.resolve(), withdrawalInfo: nil)) +// } +//} + + +enum WithdrawViewField: Int, Identifiable { + var id: Int { rawValue } + + case IBAN + case BIC + case receiver +} + +extension WithdrawView { + // When screen disappear via some action, you should called this method twice for some reason: on action and on disappear function + // Seems like a UI bug of iOS https://stackoverflow.com/a/74124962 + private func resignFirstResponder() { + UIApplication.shared.sendAction( + #selector(UIResponder.resignFirstResponder), + to: nil, + from: nil, + for: nil + ) + } +} diff --git a/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawViewModel.swift b/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawViewModel.swift new file mode 100644 index 0000000000..c594724a41 --- /dev/null +++ b/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawViewModel.swift @@ -0,0 +1,172 @@ +import BankTransfer +import Combine +import Foundation +import Resolver + +class WithdrawViewModel: BaseViewModel, ObservableObject { + typealias FieldStatus = StrigaFormTextFieldStatus + typealias Provider = any WithdrawProvider + + @Injected var notificationService: NotificationService + + @Published var IBAN: String = "IBAN TextField" + @Published var BIC: String = "BIC TextField" + @Published var receiver: String = "Receiver TextField" + @Published var actionTitle: String = "Withdraw" + @Published var isDataValid = false + @Published var fieldsStatuses = [WithdrawViewField: FieldStatus]() + @Published var isLoading = false + @Published private var actionHasBeenTapped = false + + private let actionCompletedSubject = PassthroughSubject() + public var actionCompletedPublisher: AnyPublisher { + actionCompletedSubject.eraseToAnyPublisher() + } + + var provider: Provider + init( + provider: Provider, + withdrawalInfo: WithdrawalInfoType + ) { + self.provider = provider + + super.init() + + self.IBAN = withdrawalInfo.IBAN ?? "" + self.BIC = withdrawalInfo.BIC ?? "" + self.receiver = withdrawalInfo.receiver + + Publishers.CombineLatest3($IBAN, $BIC, $actionHasBeenTapped) + .drop(while: { _, _, actionHasBeenTapped in + !actionHasBeenTapped + }) + .map { iban, bic, _ in + [ + WithdrawViewField.IBAN: self.checkIBAN(iban), + WithdrawViewField.BIC: self.checkBIC(bic) + ] + } + .assignWeak(to: \.fieldsStatuses, on: self) + .store(in: &subscriptions) + + $IBAN + .debounce(for: 0.1, scheduler: DispatchQueue.main) + .removeDuplicates() + .map { self.formatIBAN($0) } + .assignWeak(to: \.IBAN, on: self) + .store(in: &subscriptions) + + } + + func action() async { + actionHasBeenTapped = true + guard !isLoading, checkIBAN(IBAN) == .valid, checkBIC(BIC) == .valid else { + return + } + + isLoading = true + defer { + isLoading = false + } + // Save to local + do { + try await provider.save( + IBAN: IBAN.filterIBAN(), + BIC: BIC, + receiver: receiver + ) + } catch { + notificationService.showDefaultErrorNotification() + } + actionCompletedSubject.send() + } + + func formatIBAN(_ iban: String) -> String { + // Remove any spaces or special characters from the input string + let cleanedIBAN = iban.components(separatedBy: CharacterSet.alphanumerics.inverted).joined() + + // Check if the IBAN is empty or not valid (less than 4 characters) + guard cleanedIBAN.count >= 4 else { + return cleanedIBAN + } + + // Create a formatted IBAN by grouping characters in blocks of four + var formattedIBAN = "" + var index = cleanedIBAN.startIndex + + while index < cleanedIBAN.endIndex { + let nextIndex = cleanedIBAN.index(index, offsetBy: 4, limitedBy: cleanedIBAN.endIndex) ?? cleanedIBAN.endIndex + let block = cleanedIBAN[index.. FieldStatus { + let filteredIBAN = iban.filterIBAN() + if filteredIBAN.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + return .invalid(error: WithdrawViewFieldError.empty.rawValue) + } + return filteredIBAN.passesMod97Check() ? .valid : .invalid(error: WithdrawViewFieldError.invalidIBAN.rawValue) + } + + private func checkBIC(_ bic: String) -> FieldStatus { + let bic = bic.trimmingCharacters(in: .whitespacesAndNewlines) + if bic.isEmpty { + return .invalid(error: WithdrawViewFieldError.empty.rawValue) + } + return bic.passesBICCheck() ? .valid : .invalid(error: WithdrawViewFieldError.invalidBIC.rawValue) + } +} + +// Validation +private extension String { + private func mod97() -> Int { + let symbols: [Character] = Array(self) + let swapped = symbols.dropFirst(4) + symbols.prefix(4) + + let mod: Int = swapped.reduce(0) { (previousMod, char) in + let value = Int(String(char), radix: 36)! // "0" => 0, "A" => 10, "Z" => 35 + let factor = value < 10 ? 10 : 100 + return (factor * previousMod + value) % 97 + } + return mod + } + + func passesMod97Check() -> Bool { + guard count >= 4 else { + return false + } + + let uppercase = uppercased() + + guard uppercase.range(of: "^[0-9A-Z]*$", options: .regularExpression) != nil else { + return false + } + return (uppercase.mod97() == 1) + } + + func passesBICCheck() -> Bool { + let bicRegex = "^([A-Za-z]{4}[A-Za-z]{2})([A-Za-z0-9]{2})([A-Za-z0-9]{3})?$" + let bicTest = NSPredicate(format: "SELF MATCHES %@", bicRegex) + return bicTest.evaluate(with: self) + } + + func filterIBAN() -> String { + // Use a character set containing allowed characters (alphanumeric and spaces) + let allowedCharacterSet = CharacterSet(charactersIn: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") + // Remove any characters not in the allowed set + return self.components(separatedBy: allowedCharacterSet.inverted).joined() + } +} + +enum WithdrawViewFieldError: String { + case empty = "Could not be empty" + case invalidIBAN = "Invalid IBAN" + case invalidBIC = "Invalid BIC" +} diff --git a/p2p_wallet/Scenes/Main/NewHome/HomeCoordinator.swift b/p2p_wallet/Scenes/Main/NewHome/HomeCoordinator.swift index 18a9fe5a55..494156f7dd 100644 --- a/p2p_wallet/Scenes/Main/NewHome/HomeCoordinator.swift +++ b/p2p_wallet/Scenes/Main/NewHome/HomeCoordinator.swift @@ -98,6 +98,33 @@ final class HomeCoordinator: Coordinator { .sink(receiveValue: {}) .store(in: &subscriptions) + WithdrawCoordinator( + navigationController: self.navigationController + ) + .start() + .flatMap({ result in + switch result { + case .verified: + return BankTransferClaimCoordinator( + navigationController: self.navigationController, + transaction: StrigaClaimTransaction( + challengeId: "1", + token: .usdc, + amount: 120, + feeAmount: FeeAmount( + transaction: 0, + accountBalances: 0), + fromAddress: "123", + receivingAddress: "234" + ) + ).start().map { _ in Void() }.eraseToAnyPublisher() + case .canceled: + return Just(()).eraseToAnyPublisher() + } + }) + .sink { _ in } + .store(in: &self.subscriptions) + // return publisher return resultSubject.prefix(1).eraseToAnyPublisher() } From 4eb2fe2684648ca78b4ada3004a5c18756e52bb9 Mon Sep 17 00:00:00 2001 From: Alexey Sidorov Date: Thu, 20 Jul 2023 11:42:29 +0200 Subject: [PATCH 2/3] WIP navigation --- .../Models/WithdrawProvider.swift | 4 +- ...StrigaBankTransferUserDataRepository.swift | 6 +- .../Claim/BankTransferClaimCoordinator.swift | 17 +++--- .../Withdraw/WithdrawCoordinator.swift | 12 ++-- .../Withdraw/WithdrawViewModel.swift | 8 +-- .../Scenes/Main/NewHome/HomeCoordinator.swift | 58 ++++++++++--------- 6 files changed, 58 insertions(+), 47 deletions(-) diff --git a/Packages/KeyAppKit/Sources/BankTransfer/Models/WithdrawProvider.swift b/Packages/KeyAppKit/Sources/BankTransfer/Models/WithdrawProvider.swift index b16889138a..3b7a405b78 100644 --- a/Packages/KeyAppKit/Sources/BankTransfer/Models/WithdrawProvider.swift +++ b/Packages/KeyAppKit/Sources/BankTransfer/Models/WithdrawProvider.swift @@ -1,7 +1,7 @@ import Foundation //Can't come up with a good name -public protocol WithdrawalInfoType { +public protocol WithdrawalInfoType: Equatable { var IBAN: String? { get } var BIC: String? { get } var receiver: String { get } @@ -10,6 +10,6 @@ public protocol WithdrawalInfoType { public protocol WithdrawProvider { associatedtype WithdrawalInfo: WithdrawalInfoType - func withdrawalInfo() async throws -> WithdrawalInfo + func withdrawalInfo() async throws -> WithdrawalInfo? func save(IBAN: String, BIC: String, receiver: String) async throws } diff --git a/Packages/KeyAppKit/Sources/BankTransfer/Striga/Repository/StrigaBankTransferUserDataRepository.swift b/Packages/KeyAppKit/Sources/BankTransfer/Striga/Repository/StrigaBankTransferUserDataRepository.swift index 4850825869..04413e4622 100644 --- a/Packages/KeyAppKit/Sources/BankTransfer/Striga/Repository/StrigaBankTransferUserDataRepository.swift +++ b/Packages/KeyAppKit/Sources/BankTransfer/Striga/Repository/StrigaBankTransferUserDataRepository.swift @@ -416,10 +416,10 @@ private extension UserWallet { extension StrigaBankTransferUserDataRepository: WithdrawProvider { public typealias WithdrawalInfo = StrigaWithdrawalInfo - public func withdrawalInfo() async throws -> WithdrawalInfo { - await localProvider.getCachedWithdrawalInfo() ?? + public func withdrawalInfo() async throws -> WithdrawalInfo? { + await localProvider.getCachedWithdrawalInfo()// ?? /// GetAccountStatement here - WithdrawalInfo(IBAN: "IBAN", BIC: "BIC", receiver: "Receiver") +// WithdrawalInfo(IBAN: "IBAN", BIC: "BIC", receiver: "Receiver") } public func save(IBAN: String, BIC: String, receiver: String) async throws { diff --git a/p2p_wallet/Scenes/Main/BankTransfer/Claim/BankTransferClaimCoordinator.swift b/p2p_wallet/Scenes/Main/BankTransfer/Claim/BankTransferClaimCoordinator.swift index 60321cf5e7..09098911ea 100644 --- a/p2p_wallet/Scenes/Main/BankTransfer/Claim/BankTransferClaimCoordinator.swift +++ b/p2p_wallet/Scenes/Main/BankTransfer/Claim/BankTransferClaimCoordinator.swift @@ -42,6 +42,8 @@ final class BankTransferClaimCoordinator: Coordinator AnyPublisher { // Start OTP Coordinator bankTransferService.value.state + .prefix(1) + .receive(on: RunLoop.main) .flatMap { [unowned self] state in guard let phone = state.value.mobileNumber else { return Just(StrigaOTPCoordinatorResult.canceled) @@ -56,13 +58,14 @@ final class BankTransferClaimCoordinator: Coordinator { let navigationController: UINavigationController let strategy: Strategy + let withdrawalInfo: any WithdrawalInfoType + init( navigationController: UINavigationController, - strategy: Strategy = .gathering + strategy: Strategy = .gathering, + withdrawalInfo: any WithdrawalInfoType ) { self.navigationController = navigationController self.strategy = strategy + self.withdrawalInfo = withdrawalInfo super.init() } @@ -21,9 +25,9 @@ final class WithdrawCoordinator: Coordinator { let viewModel = WithdrawViewModel( provider: Resolver.resolve(), withdrawalInfo: StrigaWithdrawalInfo( - IBAN: nil, - BIC: nil, - receiver: "Alexey Sidorov" + IBAN: withdrawalInfo.BIC, + BIC: withdrawalInfo.IBAN, + receiver: withdrawalInfo.receiver ) ) let view = WithdrawView( diff --git a/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawViewModel.swift b/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawViewModel.swift index c594724a41..a9f28b21a5 100644 --- a/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawViewModel.swift +++ b/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawViewModel.swift @@ -9,9 +9,9 @@ class WithdrawViewModel: BaseViewModel, ObservableObject { @Injected var notificationService: NotificationService - @Published var IBAN: String = "IBAN TextField" - @Published var BIC: String = "BIC TextField" - @Published var receiver: String = "Receiver TextField" + @Published var IBAN: String = "" + @Published var BIC: String = "" + @Published var receiver: String = "" @Published var actionTitle: String = "Withdraw" @Published var isDataValid = false @Published var fieldsStatuses = [WithdrawViewField: FieldStatus]() @@ -26,7 +26,7 @@ class WithdrawViewModel: BaseViewModel, ObservableObject { var provider: Provider init( provider: Provider, - withdrawalInfo: WithdrawalInfoType + withdrawalInfo: any WithdrawalInfoType ) { self.provider = provider diff --git a/p2p_wallet/Scenes/Main/NewHome/HomeCoordinator.swift b/p2p_wallet/Scenes/Main/NewHome/HomeCoordinator.swift index 494156f7dd..8b63c4219b 100644 --- a/p2p_wallet/Scenes/Main/NewHome/HomeCoordinator.swift +++ b/p2p_wallet/Scenes/Main/NewHome/HomeCoordinator.swift @@ -26,6 +26,7 @@ enum HomeNavigation: Equatable { case topUpCoin(Token) case topUp // Top up via bank transfer, bank card or crypto receive case bankTransfer // Only bank transfer + case withdraw(StrigaWithdrawalInfo) // Error case error(show: Bool) } @@ -98,33 +99,6 @@ final class HomeCoordinator: Coordinator { .sink(receiveValue: {}) .store(in: &subscriptions) - WithdrawCoordinator( - navigationController: self.navigationController - ) - .start() - .flatMap({ result in - switch result { - case .verified: - return BankTransferClaimCoordinator( - navigationController: self.navigationController, - transaction: StrigaClaimTransaction( - challengeId: "1", - token: .usdc, - amount: 120, - feeAmount: FeeAmount( - transaction: 0, - accountBalances: 0), - fromAddress: "123", - receivingAddress: "234" - ) - ).start().map { _ in Void() }.eraseToAnyPublisher() - case .canceled: - return Just(()).eraseToAnyPublisher() - } - }) - .sink { _ in } - .store(in: &self.subscriptions) - // return publisher return resultSubject.prefix(1).eraseToAnyPublisher() } @@ -327,6 +301,36 @@ final class HomeCoordinator: Coordinator { } return Just(()) .eraseToAnyPublisher() + case .withdraw(let withdrawalInfo): + return coordinate(to: + WithdrawCoordinator( + navigationController: self.navigationController, + withdrawalInfo: withdrawalInfo + ) + ) + .flatMap({ result in + switch result { + case .verified: + return self.coordinate(to: + BankTransferClaimCoordinator( + navigationController: self.navigationController, + transaction: StrigaClaimTransaction( + challengeId: "1", + token: .usdc, + amount: 120, + feeAmount: FeeAmount( + transaction: 0, + accountBalances: 0), + fromAddress: "4iP2r5437gMF5iavTyBApSaMyYUQbtvQ1yhHm6VpnijH", + receivingAddress: "4iP2r5437gMF5iavTyBApSaMyYUQbtvQ1yhHm6VpnijH" + ) + )).map { _ in Void() } + case .canceled: + fatalError() +// return Just(()).eraseToAnyPublisher() + } + }) + .eraseToAnyPublisher() } } From ebde184ad9d4c10434c832c0b8746b7f9264b2ab Mon Sep 17 00:00:00 2001 From: Alexey Sidorov Date: Thu, 20 Jul 2023 12:22:00 +0200 Subject: [PATCH 3/3] WIP --- .../Models/WithdrawProvider.swift | 15 ---- .../BankTransferUserDataRepository.swift | 5 ++ .../Service/BankTransferService.swift | 5 ++ .../Service/BankTransferServiceImpl.swift | 9 ++ .../Striga/Models/StrigaWithdrawalInfo.swift | 2 +- ...StrigaBankTransferUserDataRepository.swift | 16 ++-- .../Resolver+registerAllServices.swift | 89 ++++++------------- .../Withdraw/WithdrawCoordinator.swift | 5 +- .../Withdraw/WithdrawViewModel.swift | 23 ++--- .../Scenes/Main/NewHome/HomeCoordinator.swift | 32 ------- 10 files changed, 67 insertions(+), 134 deletions(-) delete mode 100644 Packages/KeyAppKit/Sources/BankTransfer/Models/WithdrawProvider.swift diff --git a/Packages/KeyAppKit/Sources/BankTransfer/Models/WithdrawProvider.swift b/Packages/KeyAppKit/Sources/BankTransfer/Models/WithdrawProvider.swift deleted file mode 100644 index 3b7a405b78..0000000000 --- a/Packages/KeyAppKit/Sources/BankTransfer/Models/WithdrawProvider.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Foundation - -//Can't come up with a good name -public protocol WithdrawalInfoType: Equatable { - var IBAN: String? { get } - var BIC: String? { get } - var receiver: String { get } -} - -public protocol WithdrawProvider { - associatedtype WithdrawalInfo: WithdrawalInfoType - - func withdrawalInfo() async throws -> WithdrawalInfo? - func save(IBAN: String, BIC: String, receiver: String) async throws -} diff --git a/Packages/KeyAppKit/Sources/BankTransfer/Repository/BankTransferUserDataRepository.swift b/Packages/KeyAppKit/Sources/BankTransfer/Repository/BankTransferUserDataRepository.swift index 767ab9b4d4..e0c2345d33 100644 --- a/Packages/KeyAppKit/Sources/BankTransfer/Repository/BankTransferUserDataRepository.swift +++ b/Packages/KeyAppKit/Sources/BankTransfer/Repository/BankTransferUserDataRepository.swift @@ -1,4 +1,6 @@ public protocol BankTransferUserDataRepository { + associatedtype WithdrawalInfo + func getUserId() async -> String? func getKYCStatus() async throws -> StrigaKYC @@ -17,4 +19,7 @@ public protocol BankTransferUserDataRepository { func clearCache() async func getWallet(userId: String) async throws -> UserWallet? + + func withdrawalInfo() async throws -> WithdrawalInfo? + func save(_ info: WithdrawalInfo) async throws } diff --git a/Packages/KeyAppKit/Sources/BankTransfer/Service/BankTransferService.swift b/Packages/KeyAppKit/Sources/BankTransfer/Service/BankTransferService.swift index 507fc6fd0a..bc00e1a324 100644 --- a/Packages/KeyAppKit/Sources/BankTransfer/Service/BankTransferService.swift +++ b/Packages/KeyAppKit/Sources/BankTransfer/Service/BankTransferService.swift @@ -3,6 +3,7 @@ import KeyAppKitCore public protocol BankTransferService where Provider: BankTransferUserDataRepository { associatedtype Provider + typealias WithdrawalInfo = Provider.WithdrawalInfo var state: AnyPublisher, Never> { get } @@ -22,6 +23,10 @@ public protocol BankTransferService where Provider: BankTransferUserDa func resendSMS() async throws func getKYCToken() async throws -> String + + // WithdrowalProvider + func withdrawalInfo() async throws -> WithdrawalInfo? + func saveWithdrawalInfo(info: WithdrawalInfo) async throws } public class AnyBankTransferService { diff --git a/Packages/KeyAppKit/Sources/BankTransfer/Service/BankTransferServiceImpl.swift b/Packages/KeyAppKit/Sources/BankTransfer/Service/BankTransferServiceImpl.swift index f057e194be..792c916dda 100644 --- a/Packages/KeyAppKit/Sources/BankTransfer/Service/BankTransferServiceImpl.swift +++ b/Packages/KeyAppKit/Sources/BankTransfer/Service/BankTransferServiceImpl.swift @@ -168,4 +168,13 @@ extension BankTransferServiceImpl { ) ) } + + public func withdrawalInfo() async throws -> Provider.WithdrawalInfo? { + try await repository.withdrawalInfo() + + } + + public func saveWithdrawalInfo(info: Provider.WithdrawalInfo) async throws { + try await repository.save(info) + } } diff --git a/Packages/KeyAppKit/Sources/BankTransfer/Striga/Models/StrigaWithdrawalInfo.swift b/Packages/KeyAppKit/Sources/BankTransfer/Striga/Models/StrigaWithdrawalInfo.swift index b9cb7ebdc0..a9e30550bc 100644 --- a/Packages/KeyAppKit/Sources/BankTransfer/Striga/Models/StrigaWithdrawalInfo.swift +++ b/Packages/KeyAppKit/Sources/BankTransfer/Striga/Models/StrigaWithdrawalInfo.swift @@ -1,6 +1,6 @@ import Foundation -public struct StrigaWithdrawalInfo: WithdrawalInfoType, Codable { +public struct StrigaWithdrawalInfo: Codable { public var IBAN: String? public var BIC: String? public var receiver: String diff --git a/Packages/KeyAppKit/Sources/BankTransfer/Striga/Repository/StrigaBankTransferUserDataRepository.swift b/Packages/KeyAppKit/Sources/BankTransfer/Striga/Repository/StrigaBankTransferUserDataRepository.swift index 04413e4622..13c121bf4b 100644 --- a/Packages/KeyAppKit/Sources/BankTransfer/Striga/Repository/StrigaBankTransferUserDataRepository.swift +++ b/Packages/KeyAppKit/Sources/BankTransfer/Striga/Repository/StrigaBankTransferUserDataRepository.swift @@ -7,7 +7,7 @@ import KeyAppKitCore import KeyAppKitLogger public final class StrigaBankTransferUserDataRepository: BankTransferUserDataRepository { - + public typealias WithdrawalInfo = StrigaWithdrawalInfo // MARK: - Properties private let localProvider: StrigaLocalProvider @@ -413,21 +413,19 @@ private extension UserWallet { } } -extension StrigaBankTransferUserDataRepository: WithdrawProvider { - public typealias WithdrawalInfo = StrigaWithdrawalInfo - +extension StrigaBankTransferUserDataRepository { public func withdrawalInfo() async throws -> WithdrawalInfo? { await localProvider.getCachedWithdrawalInfo()// ?? /// GetAccountStatement here // WithdrawalInfo(IBAN: "IBAN", BIC: "BIC", receiver: "Receiver") } - - public func save(IBAN: String, BIC: String, receiver: String) async throws { + + public func save(_ info: StrigaWithdrawalInfo) async throws { try await localProvider.save( withdrawalInfo: .init( - IBAN: IBAN, - BIC: BIC, - receiver: receiver + IBAN: info.IBAN, + BIC: info.BIC, + receiver: info.receiver ) ) } diff --git a/p2p_wallet/Injection/Resolver+registerAllServices.swift b/p2p_wallet/Injection/Resolver+registerAllServices.swift index 8e0ac91ef2..72eac91f63 100644 --- a/p2p_wallet/Injection/Resolver+registerAllServices.swift +++ b/p2p_wallet/Injection/Resolver+registerAllServices.swift @@ -424,68 +424,6 @@ extension Resolver: ResolverRegistering { } .scope(.session) - register { - StrigaBankTransferUserDataRepository( - localProvider: GlobalAppState.shared.strigaMockingEnabled ? - MockStrigaLocalProvider( - useCase: .unregisteredUser, - hasCachedInput: true - ) : - StrigaLocalProviderImpl(), - remoteProvider: GlobalAppState.shared.strigaMockingEnabled ? - MockStrigaRemoteProvider( - useCase: .unregisteredUser, - mockUserId: "user-id", - mockKYCToken: "kyc-token" - ) : - StrigaRemoteProviderImpl( - baseURL: GlobalAppState.shared.strigaEndpoint, - solanaKeyPair: Resolver.resolve(UserWalletManager.self).wallet?.account - ), - metadataProvider: GlobalAppState.shared.strigaMockingEnabled ? - MockStrigaMetadataProvider( - useCase: .unregisteredUser, - mockUserId: "user-id" - ) : - Resolver.resolve(StrigaMetadataProvider.self), - commonInfoProvider: CommonInfoLocalProviderImpl(), - solanaKeyPair: Resolver.resolve(UserWalletManager.self).wallet?.account - ) - } - .implements(BankTransferUserDataRepository.self) - .scope(.session) - - register { - StrigaBankTransferUserDataRepository( - localProvider: GlobalAppState.shared.strigaMockingEnabled ? - MockStrigaLocalProvider( - useCase: .unregisteredUser, - hasCachedInput: true - ) : - StrigaLocalProviderImpl(), - remoteProvider: GlobalAppState.shared.strigaMockingEnabled ? - MockStrigaRemoteProvider( - useCase: .unregisteredUser, - mockUserId: "user-id", - mockKYCToken: "kyc-token" - ) : - StrigaRemoteProviderImpl( - baseURL: GlobalAppState.shared.strigaEndpoint, - solanaKeyPair: Resolver.resolve(UserWalletManager.self).wallet?.account - ), - metadataProvider: GlobalAppState.shared.strigaMockingEnabled ? - MockStrigaMetadataProvider( - useCase: .unregisteredUser, - mockUserId: "user-id" - ) : - Resolver.resolve(StrigaMetadataProvider.self), - commonInfoProvider: CommonInfoLocalProviderImpl(), - solanaKeyPair: Resolver.resolve(UserWalletManager.self).wallet?.account - ) - } - .implements((any WithdrawProvider).self) - .scope(.session) - register { RelayServiceImpl( contextManager: resolve(), orcaSwap: resolve(), @@ -673,7 +611,32 @@ extension Resolver: ResolverRegistering { register { BankTransferServiceImpl( - repository: resolve() + repository: StrigaBankTransferUserDataRepository( + localProvider: GlobalAppState.shared.strigaMockingEnabled ? + MockStrigaLocalProvider( + useCase: .unregisteredUser, + hasCachedInput: true + ) : + StrigaLocalProviderImpl(), + remoteProvider: GlobalAppState.shared.strigaMockingEnabled ? + MockStrigaRemoteProvider( + useCase: .unregisteredUser, + mockUserId: "user-id", + mockKYCToken: "kyc-token" + ) : + StrigaRemoteProviderImpl( + baseURL: GlobalAppState.shared.strigaEndpoint, + solanaKeyPair: Resolver.resolve(UserWalletManager.self).wallet?.account + ), + metadataProvider: GlobalAppState.shared.strigaMockingEnabled ? + MockStrigaMetadataProvider( + useCase: .unregisteredUser, + mockUserId: "user-id" + ) : + Resolver.resolve(StrigaMetadataProvider.self), + commonInfoProvider: CommonInfoLocalProviderImpl(), + solanaKeyPair: Resolver.resolve(UserWalletManager.self).wallet?.account + ) ) } .implements((any BankTransferService).self) diff --git a/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawCoordinator.swift b/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawCoordinator.swift index a4fdd506cc..449447a9f8 100644 --- a/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawCoordinator.swift +++ b/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawCoordinator.swift @@ -8,12 +8,12 @@ final class WithdrawCoordinator: Coordinator { let navigationController: UINavigationController let strategy: Strategy - let withdrawalInfo: any WithdrawalInfoType + let withdrawalInfo: StrigaWithdrawalInfo init( navigationController: UINavigationController, strategy: Strategy = .gathering, - withdrawalInfo: any WithdrawalInfoType + withdrawalInfo: StrigaWithdrawalInfo ) { self.navigationController = navigationController self.strategy = strategy @@ -23,7 +23,6 @@ final class WithdrawCoordinator: Coordinator { override func start() -> AnyPublisher { let viewModel = WithdrawViewModel( - provider: Resolver.resolve(), withdrawalInfo: StrigaWithdrawalInfo( IBAN: withdrawalInfo.BIC, BIC: withdrawalInfo.IBAN, diff --git a/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawViewModel.swift b/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawViewModel.swift index a9f28b21a5..06fe3b07f8 100644 --- a/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawViewModel.swift +++ b/p2p_wallet/Scenes/Main/BankTransfer/Withdraw/WithdrawViewModel.swift @@ -5,10 +5,14 @@ import Resolver class WithdrawViewModel: BaseViewModel, ObservableObject { typealias FieldStatus = StrigaFormTextFieldStatus - typealias Provider = any WithdrawProvider + // MARK: - + + @Injected var bankTransferService: AnyBankTransferService @Injected var notificationService: NotificationService + // MARK: - + @Published var IBAN: String = "" @Published var BIC: String = "" @Published var receiver: String = "" @@ -23,13 +27,9 @@ class WithdrawViewModel: BaseViewModel, ObservableObject { actionCompletedSubject.eraseToAnyPublisher() } - var provider: Provider init( - provider: Provider, - withdrawalInfo: any WithdrawalInfoType + withdrawalInfo: StrigaWithdrawalInfo ) { - self.provider = provider - super.init() self.IBAN = withdrawalInfo.IBAN ?? "" @@ -55,7 +55,6 @@ class WithdrawViewModel: BaseViewModel, ObservableObject { .map { self.formatIBAN($0) } .assignWeak(to: \.IBAN, on: self) .store(in: &subscriptions) - } func action() async { @@ -70,10 +69,12 @@ class WithdrawViewModel: BaseViewModel, ObservableObject { } // Save to local do { - try await provider.save( - IBAN: IBAN.filterIBAN(), - BIC: BIC, - receiver: receiver + try await bankTransferService.value.saveWithdrawalInfo(info: + .init( + IBAN: IBAN.filterIBAN(), + BIC: BIC, + receiver: receiver + ) ) } catch { notificationService.showDefaultErrorNotification() diff --git a/p2p_wallet/Scenes/Main/NewHome/HomeCoordinator.swift b/p2p_wallet/Scenes/Main/NewHome/HomeCoordinator.swift index 8b63c4219b..816c2c4af1 100644 --- a/p2p_wallet/Scenes/Main/NewHome/HomeCoordinator.swift +++ b/p2p_wallet/Scenes/Main/NewHome/HomeCoordinator.swift @@ -26,7 +26,6 @@ enum HomeNavigation: Equatable { case topUpCoin(Token) case topUp // Top up via bank transfer, bank card or crypto receive case bankTransfer // Only bank transfer - case withdraw(StrigaWithdrawalInfo) // Error case error(show: Bool) } @@ -98,7 +97,6 @@ final class HomeCoordinator: Coordinator { } .sink(receiveValue: {}) .store(in: &subscriptions) - // return publisher return resultSubject.prefix(1).eraseToAnyPublisher() } @@ -301,36 +299,6 @@ final class HomeCoordinator: Coordinator { } return Just(()) .eraseToAnyPublisher() - case .withdraw(let withdrawalInfo): - return coordinate(to: - WithdrawCoordinator( - navigationController: self.navigationController, - withdrawalInfo: withdrawalInfo - ) - ) - .flatMap({ result in - switch result { - case .verified: - return self.coordinate(to: - BankTransferClaimCoordinator( - navigationController: self.navigationController, - transaction: StrigaClaimTransaction( - challengeId: "1", - token: .usdc, - amount: 120, - feeAmount: FeeAmount( - transaction: 0, - accountBalances: 0), - fromAddress: "4iP2r5437gMF5iavTyBApSaMyYUQbtvQ1yhHm6VpnijH", - receivingAddress: "4iP2r5437gMF5iavTyBApSaMyYUQbtvQ1yhHm6VpnijH" - ) - )).map { _ in Void() } - case .canceled: - fatalError() -// return Just(()).eraseToAnyPublisher() - } - }) - .eraseToAnyPublisher() } }