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

IOS-8569 create wallet #4294

Merged
merged 15 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from 6 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
6 changes: 4 additions & 2 deletions Tangem/Common/TangemSdk/Tasks/AppScanTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@
import Foundation
import TangemSdk
import BlockchainSdk
import TangemVisa

enum DefaultWalletData: Codable {
case file(WalletData)
case legacy(WalletData)
case twin(WalletData, TwinData)
case visa(accessToken: String, refreshToken: String)
case visa(activationInput: VisaCardActivationInput, tokens: VisaAuthorizationTokens?)
case none

var twinData: TwinData? {
Expand Down Expand Up @@ -72,7 +73,8 @@ final class AppScanTask: CardSessionRunnable {
return
}

if FirmwareVersion.visaRange.contains(card.firmwareVersion.doubleValue) {
let visaUtils = VisaUtilities(isTestnet: false)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Думаю можно в VisaUtils положить что-то типа isVisa(cardId: ..)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

сделал

if FirmwareVersion.visaRange.contains(card.firmwareVersion.doubleValue) && visaUtils.batchId.contains(card.batchId) {
readVisaCard(session, completion)
return
}
Expand Down
18 changes: 16 additions & 2 deletions Tangem/Common/TangemSdk/Tasks/Handlers/VisaCardScanHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ class VisaCardScanHandler {

func handleVisaCardScan(session: CardSession, completion: @escaping CompletionResult<DefaultWalletData>) {
log("Attempting to handle Visa card scan")
let mandatoryCurve = VisaUtilities().mandatoryCurve
guard let card = session.environment.card else {
completion(.failure(.missingPreflightRead))
return
}

guard card.wallets.contains(where: { $0.curve == mandatoryCurve }) else {
completion(.success(.visa(
activationInput: VisaCardActivationInput(cardId: card.cardId, cardPublicKey: card.cardPublicKey),
tokens: nil
)))
return
}

Task(priority: .userInitiated) { [weak self] in
self?.log("Running task for Visa card scan")
await self?.handleVisaCardScanAsync(session: session, completion: completion)
Expand Down Expand Up @@ -56,8 +70,8 @@ class VisaCardScanHandler {
log("Access token response: \(accessTokenResponse)")

let visaWalletData = DefaultWalletData.visa(
accessToken: accessTokenResponse.accessToken,
refreshToken: accessTokenResponse.refreshToken
activationInput: VisaCardActivationInput(cardId: card.cardId, cardPublicKey: card.cardPublicKey),
tokens: accessTokenResponse
)
completion(.success(visaWalletData))
} catch let error as TangemSdkError {
Expand Down
17 changes: 17 additions & 0 deletions Tangem/Modules/Onboarding/OnboardingCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,26 @@ class OnboardingCoordinator: CoordinatorObject {
onDismissalAttempt = model.backButtonAction
viewState = .wallet(model)
case .visa:
let visaCardInput: VisaCardActivationInput
Copy link
Collaborator

Choose a reason for hiding this comment

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

Предлагаю тут подчистить, чтобы просто разворот опционала остался например. Либо передать внутрь VisaOnboardingViewModel и там раскрыть, засоряется координатор

Copy link
Contributor Author

Choose a reason for hiding this comment

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

вынес в отдельный билдер тот же код, пока без изменений. Пока оставил с фаталами, не хватает выброса ошибки из функции start, правда единственное место где оно нужно, так что вряд ли имеет смысл внедрять куда-то ещё. В любом случае обновляться код будет ещё при продолжении активации, вернусь ещё к нему

switch input.cardInput {
case .cardId:
fatalError("Invalid card input for Visa onboarding")
case .cardInfo(let cardInfo):
switch cardInfo.walletData {
case .visa(let activationInput, _):
visaCardInput = activationInput
default:
fatalError("Invalid card input for Visa onboarding")
}
case .userWalletModel(let userWalletModel):
// TODO: IOS-8588
fatalError("Invalid onboarding input for Visa")
}
let model = VisaOnboardingViewModel(
input: input,
visaActivationManager: VisaActivationManagerFactory().make(
cardInput: visaCardInput,
tangemSdk: TangemSdkDefaultFactory().makeTangemSdk(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

В идеале бы сделать по принципу cardInitializer и других мест по взаимодействию с картой (Common/TangemSdk). TangemSDK по приле не гулет, гуляет сущность, которая работает с картой посредством TangemSDK

Copy link
Contributor Author

Choose a reason for hiding this comment

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

пока не могу понять как это сделать так, чтобы чисто было, т.к. часть требований ещё уточняется + много кастомных вызовов TangemSdk, часть из них получается положить в TangemVisa, часть - нет. Думаю это лучше смотреть уже в конце, когда все будет готово и работать и потом зная все требования и все особенности - отрефачить до такого состояния.

urlSessionConfiguration: .default,
logger: AppLog.shared
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Foundation
import SwiftUI
import Combine
import TangemFoundation
import TangemVisa

protocol VisaOnboardingAccessCodeSetupDelegate: AnyObject {
/// We need to show alert in parent view, otherwise it won't be shown
Expand All @@ -30,10 +31,16 @@ class VisaOnboardingAccessCodeSetupViewModel: ObservableObject {
private var selectedAccessCode: String = ""
private var accessCodeInputSubscription: AnyCancellable?

private var accessCodeValidator: VisaAccessCodeValidator
private weak var delegate: VisaOnboardingAccessCodeSetupDelegate?

init(delegate: VisaOnboardingAccessCodeSetupDelegate) {
init(
accessCodeValidator: VisaAccessCodeValidator,
delegate: VisaOnboardingAccessCodeSetupDelegate
) {
self.accessCodeValidator = accessCodeValidator
self.delegate = delegate

bind()
}

Expand Down Expand Up @@ -92,20 +99,20 @@ class VisaOnboardingAccessCodeSetupViewModel: ObservableObject {
} catch let validationError as ValidationError {
errorMessage = validationError.description
isButtonDisabled = true
return
} catch let visaValidationError as VisaAccessCodeValidationError {
let appError = ValidationError(visaValidationError: visaValidationError)
errorMessage = appError.description
isButtonDisabled = true
} catch {
errorMessage = error.localizedDescription
isButtonDisabled = true
return
}
}

private func isAccessCodeValid(accessCode: String) throws {
switch viewState {
case .accessCode:
if accessCode.count < 4 {
throw ValidationError.accessCodeTooShort
}
try accessCodeValidator.validateAccessCode(accessCode: accessCode)
case .repeatAccessCode:
if accessCode != selectedAccessCode {
throw ValidationError.codesDoNotMatch
Expand Down Expand Up @@ -142,9 +149,11 @@ private extension VisaOnboardingAccessCodeSetupViewModel {
do {
try await viewModel.delegate?.useSelectedCode(accessCode: viewModel.selectedAccessCode)
} catch {
viewModel.log("Failed to use selected access code. Reason: \(error)")
/// We need to show alert in parent view, otherwise it won't be shown
await viewModel.delegate?.showAlert(error.alertBinder)
if !error.isCancellationError {
viewModel.log("Failed to use selected access code. Reason: \(error)")
/// We need to show alert in parent view, otherwise it won't be shown
await viewModel.delegate?.showAlert(error.alertBinder)
}
}

await runOnMain {
Expand All @@ -166,6 +175,13 @@ extension VisaOnboardingAccessCodeSetupViewModel {
case .codesDoNotMatch: return Localization.onboardingAccessCodesDoesntMatch
}
}

init(visaValidationError: VisaAccessCodeValidationError) {
switch visaValidationError {
case .accessCodeIsTooShort:
self = .accessCodeTooShort
}
}
}
}

Expand Down
38 changes: 12 additions & 26 deletions Tangem/Modules/Onboarding/Visa/VisaOnboardingViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class VisaOnboardingViewModel: ObservableObject {
startActivationDelegate: weakify(self, forFunction: VisaOnboardingViewModel.goToNextStep)
)

lazy var accessCodeSetupViewModel = VisaOnboardingAccessCodeSetupViewModel(delegate: self)
lazy var accessCodeSetupViewModel = VisaOnboardingAccessCodeSetupViewModel(accessCodeValidator: visaActivationManager, delegate: self)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Предлагаю вынести наружу в место сборки ViewModel и конфигурировать там, в итоге получается аккуратнее

Copy link
Contributor Author

Choose a reason for hiding this comment

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

не совсем понял зачем. Сейчас все вью модели создаются как и в других онбордингах лениво и по необходимости. Если обновить - их надо инициализировать при переходах между шагами, сложность такого подхода, что в коде надо будет при переключении шагов инициализировать, как будто бы запутанней будет. А если инициализировать все вложенные модели и на старте онбординга их все внедрять - получается много лишней работы, причем некоторые модели вообще не будут использоваться, но будут тратить время на инициализацию и память.

Может не совсем понял твою идею.


var navigationBarTitle: String {
currentStep.navigationTitle
Expand Down Expand Up @@ -159,28 +159,6 @@ private extension VisaOnboardingViewModel {
userWalletRepository.updateSelection()
coordinator?.closeOnboarding()
}

// This function will be updated in IOS-8509. Requirements for activation flow was reworked,
// so for now this function is for testing purposes
func startActivation() {
guard activationManagerTask == nil else {
print("Activation task already exists, skipping")
return
}

guard
case .cardInfo(let cardInfo) = input.cardInput,
case .visa(let accessToken, let refreshToken) = cardInfo.walletData
else {
print("Wrong onboarding input")
return
}

let authorizationTokens = VisaAuthorizationTokens(accessToken: accessToken, refreshToken: refreshToken)
activationManagerTask = runTask(in: self, isDetached: false) { viewModel in
try await viewModel.visaActivationManager.startActivation(authorizationTokens)
}.eraseToAnyCancellable()
}
}

extension VisaOnboardingViewModel: UserWalletStorageAgreementRoutable {
Expand Down Expand Up @@ -230,8 +208,8 @@ extension VisaOnboardingViewModel: VisaOnboardingAccessCodeSetupDelegate {
}

func useSelectedCode(accessCode: String) async throws {
try await Task.sleep(seconds: 5)
throw "Will be implemented later IOS-8509"
try visaActivationManager.saveAccessCode(accessCode: accessCode)
try await visaActivationManager.startActivation()
}
}

Expand All @@ -250,10 +228,18 @@ extension VisaOnboardingViewModel {
onboardingStepsBuilderFactory: cardMockConfig,
pushNotificationsInteractor: PushNotificationsInteractorMock()
)
guard let cardInput = inputFactory.makeOnboardingInput() else {
fatalError("Failed to generate card input for visa onboarding")
}

return .init(
input: inputFactory.makeOnboardingInput()!,
input: cardInput,
visaActivationManager: VisaActivationManagerFactory().make(
cardInput: .init(
cardId: cardMock.card.cardId,
cardPublicKey: cardMock.card.cardPublicKey
),
tangemSdk: TangemSdkDefaultFactory().makeTangemSdk(),
urlSessionConfiguration: .default,
logger: AppLog.shared
),
Expand Down
Loading