-
Notifications
You must be signed in to change notification settings - Fork 251
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
627 additions
and
117 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
282 changes: 170 additions & 112 deletions
282
UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
...Wallet/UnstoppableWallet/Modules/RestoreAccount/RestoreCoinzix/RestoreCoinzixModule.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import UIKit | ||
|
||
struct RestoreCoinzixModule { | ||
|
||
static func viewController(returnViewController: UIViewController?) -> UIViewController? { | ||
guard let hCaptchaKey = App.shared.appConfigProvider.coinzixHCaptchaKey else { | ||
return nil | ||
} | ||
|
||
let service = RestoreCoinzixService( | ||
networkManager: App.shared.networkManager, | ||
accountFactory: App.shared.accountFactory, | ||
accountManager: App.shared.accountManager | ||
) | ||
let viewModel = RestoreCoinzixViewModel(service: service) | ||
|
||
return RestoreCoinzixViewController(hCaptchaKey: hCaptchaKey, viewModel: viewModel, returnViewController: returnViewController) | ||
} | ||
|
||
} |
74 changes: 74 additions & 0 deletions
74
...allet/UnstoppableWallet/Modules/RestoreAccount/RestoreCoinzix/RestoreCoinzixService.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import Combine | ||
import ObjectMapper | ||
import HsToolKit | ||
import HsExtensions | ||
|
||
class RestoreCoinzixService { | ||
private let networkManager: NetworkManager | ||
private let accountFactory: AccountFactory | ||
private let accountManager: AccountManager | ||
private var tasks = Set<AnyTask>() | ||
|
||
var username: String = "" { | ||
didSet { | ||
syncState() | ||
} | ||
} | ||
|
||
var password: String = "" { | ||
didSet { | ||
syncState() | ||
} | ||
} | ||
|
||
@PostPublished private(set) var state: State = .notReady | ||
|
||
init(networkManager: NetworkManager, accountFactory: AccountFactory, accountManager: AccountManager) { | ||
self.networkManager = networkManager | ||
self.accountFactory = accountFactory | ||
self.accountManager = accountManager | ||
} | ||
|
||
private func syncState() { | ||
state = username.trimmingCharacters(in: .whitespaces).isEmpty || password.trimmingCharacters(in: .whitespaces).isEmpty ? .notReady : .idle(error: nil) | ||
} | ||
|
||
private func createAccount(secretKey: String, token: String) { | ||
let type: AccountType = .cex(type: .coinzix(authToken: token, secret: secretKey)) | ||
let name = accountFactory.nextAccountName(cex: .coinzix) | ||
let account = accountFactory.account(type: type, origin: .restored, backedUp: true, name: name) | ||
|
||
accountManager.save(account: account) | ||
|
||
state = .loggedIn | ||
} | ||
|
||
} | ||
|
||
extension RestoreCoinzixService { | ||
|
||
func login(captchaToken: String) { | ||
state = .loggingIn | ||
|
||
Task { [weak self, username, password, networkManager] in | ||
do { | ||
let (secretKey, token) = try await CoinzixCexProvider.login(username: username, password: password, captchaToken: captchaToken, networkManager: networkManager) | ||
self?.createAccount(secretKey: secretKey, token: token) | ||
} catch { | ||
self?.state = .idle(error: error) | ||
} | ||
}.store(in: &tasks) | ||
} | ||
|
||
} | ||
|
||
extension RestoreCoinzixService { | ||
|
||
enum State { | ||
case notReady | ||
case idle(error: Error?) | ||
case loggingIn | ||
case loggedIn | ||
} | ||
|
||
} |
217 changes: 217 additions & 0 deletions
217
...nstoppableWallet/Modules/RestoreAccount/RestoreCoinzix/RestoreCoinzixViewController.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
import Combine | ||
import UIKit | ||
import SnapKit | ||
import ThemeKit | ||
import ComponentKit | ||
import SectionsTableView | ||
import HUD | ||
import HCaptcha | ||
|
||
class RestoreCoinzixViewController: KeyboardAwareViewController { | ||
private let wrapperViewHeight: CGFloat = .heightButton + .margin16 + .heightButton | ||
private let hcaptcha: HCaptcha | ||
private let viewModel: RestoreCoinzixViewModel | ||
private var webView: UIView? | ||
private var cancellables = Set<AnyCancellable>() | ||
|
||
private weak var returnViewController: UIViewController? | ||
|
||
private let tableView = SectionsTableView(style: .grouped) | ||
|
||
private let usernameCell = TextFieldCell() | ||
private let passwordCell = PasswordInputCell() | ||
|
||
private let buttonsHolder = BottomGradientHolder() | ||
private let loginButton = PrimaryButton() | ||
private let logginInButton = PrimaryButton() | ||
|
||
private var isLoaded = false | ||
|
||
init?(hCaptchaKey: String, viewModel: RestoreCoinzixViewModel, returnViewController: UIViewController?) { | ||
guard let hcaptcha = try? HCaptcha(apiKey: hCaptchaKey, baseURL: URL(string: "https://api.coinzix.com")) else { | ||
return nil | ||
} | ||
|
||
self.hcaptcha = hcaptcha | ||
self.viewModel = viewModel | ||
self.returnViewController = returnViewController | ||
|
||
super.init(scrollViews: [tableView], accessoryView: buttonsHolder) | ||
} | ||
|
||
required init?(coder aDecoder: NSCoder) { | ||
fatalError("init(coder:) has not been implemented") | ||
} | ||
|
||
override func viewDidLoad() { | ||
super.viewDidLoad() | ||
|
||
title = Cex.coinzix.title | ||
|
||
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "button.cancel".localized, style: .plain, target: self, action: #selector(onTapCancel)) | ||
|
||
view.addSubview(tableView) | ||
tableView.snp.makeConstraints { make in | ||
make.edges.equalToSuperview() | ||
} | ||
|
||
tableView.registerCell(forClass: DescriptionCell.self) | ||
tableView.sectionDataSource = self | ||
tableView.separatorStyle = .none | ||
tableView.backgroundColor = .clear | ||
|
||
usernameCell.inputPlaceholder = "restore.coinzix.sample_username".localized | ||
usernameCell.autocapitalizationType = .none | ||
usernameCell.onChangeText = { [weak self] in self?.viewModel.onChange(username: $0 ?? "") } | ||
|
||
passwordCell.set(textSecure: true) | ||
passwordCell.onTextSecurityChange = { [weak self] in self?.passwordCell.set(textSecure: $0) } | ||
passwordCell.inputPlaceholder = "restore.coinzix.sample_password".localized | ||
passwordCell.onChangeText = { [weak self] in self?.viewModel.onChange(password: $0 ?? "") } | ||
|
||
|
||
view.addSubview(buttonsHolder) | ||
buttonsHolder.snp.makeConstraints { make in | ||
make.height.equalTo(wrapperViewHeight).priority(.high) | ||
make.leading.trailing.bottom.equalToSuperview() | ||
} | ||
|
||
let stackView = UIStackView() | ||
buttonsHolder.addSubview(stackView) | ||
stackView.snp.makeConstraints { make in | ||
make.edges.equalToSuperview().inset(CGFloat.margin24) | ||
} | ||
|
||
stackView.axis = .vertical | ||
stackView.spacing = .margin16 | ||
|
||
stackView.addArrangedSubview(loginButton) | ||
loginButton.set(style: .yellow) | ||
loginButton.setTitle("restore.coinzix.login".localized, for: .normal) | ||
loginButton.addTarget(self, action: #selector(onTapLogin), for: .touchUpInside) | ||
|
||
stackView.addArrangedSubview(logginInButton) | ||
logginInButton.set(style: .yellow, accessoryType: .spinner) | ||
logginInButton.isEnabled = false | ||
logginInButton.setTitle("restore.coinzix.login".localized, for: .normal) | ||
|
||
let signUpButton = PrimaryButton() | ||
stackView.addArrangedSubview(signUpButton) | ||
signUpButton.set(style: .transparent) | ||
signUpButton.setTitle("restore.coinzix.sign_up".localized, for: .normal) | ||
signUpButton.addTarget(self, action: #selector(onTapSignUp), for: .touchUpInside) | ||
|
||
viewModel.$loginEnabled | ||
.receive(on: DispatchQueue.main) | ||
.sink { [weak self] enabled in self?.loginButton.isEnabled = enabled } | ||
.store(in: &cancellables) | ||
|
||
viewModel.$loginVisible | ||
.receive(on: DispatchQueue.main) | ||
.sink { [weak self] visible in self?.loginButton.isHidden = !visible } | ||
.store(in: &cancellables) | ||
|
||
viewModel.$logginInVisible | ||
.receive(on: DispatchQueue.main) | ||
.sink { [weak self] visible in self?.logginInButton.isHidden = !visible } | ||
.store(in: &cancellables) | ||
|
||
viewModel.errorPublisher | ||
.receive(on: DispatchQueue.main) | ||
.sink { text in HudHelper.instance.showErrorBanner(title: text) } | ||
.store(in: &cancellables) | ||
|
||
viewModel.successPublisher | ||
.receive(on: DispatchQueue.main) | ||
.sink { [weak self] in | ||
HudHelper.instance.show(banner: .imported) | ||
(self?.returnViewController ?? self)?.dismiss(animated: true) | ||
} | ||
.store(in: &cancellables) | ||
|
||
additionalContentInsets = UIEdgeInsets(top: 0, left: 0, bottom: -.margin16, right: 0) | ||
additionalInsetsOnlyForClosedKeyboard = false | ||
ignoreSafeAreaForAccessoryView = false | ||
|
||
tableView.buildSections() | ||
isLoaded = true | ||
|
||
hcaptcha.configureWebView { [weak self] webview in | ||
webview.frame = self?.view.bounds ?? CGRect.zero | ||
self?.webView = webview | ||
} | ||
} | ||
|
||
@objc private func onTapCancel() { | ||
dismiss(animated: true) | ||
} | ||
|
||
@objc private func onTapLogin() { | ||
view.endEditing(true) | ||
|
||
hcaptcha.validate(on: self.view) { [weak self] result in | ||
do { | ||
self?.viewModel.login(captchaToken: try result.dematerialize()) | ||
} catch { | ||
print("ERROR: \(error)") | ||
} | ||
|
||
self?.webView?.removeFromSuperview() | ||
} | ||
} | ||
|
||
@objc private func onTapSignUp() { | ||
UrlManager.open(url: "https://coinzix.com/sign-up") | ||
} | ||
|
||
} | ||
|
||
extension RestoreCoinzixViewController: SectionsDataSource { | ||
|
||
func buildSections() -> [SectionProtocol] { | ||
[ | ||
Section( | ||
id: "description", | ||
headerState: .margin(height: .margin12), | ||
footerState: .margin(height: .margin32), | ||
rows: [ | ||
Row<DescriptionCell>( | ||
id: "description-cell", | ||
dynamicHeight: { containerWidth in | ||
DescriptionCell.height(containerWidth: containerWidth, text: "restore.coinzix.description".localized, font: .subhead2, ignoreBottomMargin: true) | ||
}, | ||
bind: { cell, _ in | ||
cell.label.text = "restore.coinzix.description".localized | ||
cell.label.font = .subhead2 | ||
cell.label.textColor = .themeGray | ||
} | ||
) | ||
] | ||
), | ||
Section( | ||
id: "username", | ||
headerState: .margin(height: .margin12), | ||
rows: [ | ||
StaticRow( | ||
cell: usernameCell, | ||
id: "username", | ||
height: .heightSingleLineCell | ||
) | ||
] | ||
), | ||
Section( | ||
id: "password", | ||
headerState: .margin(height: .margin16), | ||
footerState: .margin(height: .margin32), | ||
rows: [ | ||
StaticRow( | ||
cell: passwordCell, | ||
id: "password", | ||
height: .heightSingleLineCell | ||
) | ||
] | ||
) | ||
] | ||
} | ||
|
||
} |
Oops, something went wrong.