|
|
|
@ -1,7 +1,5 @@ |
|
|
|
|
// Copyright SIX DAY LLC. All rights reserved. |
|
|
|
|
|
|
|
|
|
import UIKit |
|
|
|
|
import Eureka |
|
|
|
|
import BonMot |
|
|
|
|
import TrustKeystore |
|
|
|
|
import QRCodeReaderViewController |
|
|
|
@ -10,57 +8,160 @@ protocol ImportWalletViewControllerDelegate: class { |
|
|
|
|
func didImportAccount(account: Wallet, in viewController: ImportWalletViewController) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class ImportWalletViewController: FormViewController { |
|
|
|
|
class ImportWalletViewController: UIViewController { |
|
|
|
|
struct ValidationError: LocalizedError { |
|
|
|
|
var msg: String |
|
|
|
|
var errorDescription: String? { |
|
|
|
|
return msg |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let keystore: Keystore |
|
|
|
|
private let viewModel = ImportWalletViewModel() |
|
|
|
|
|
|
|
|
|
struct Values { |
|
|
|
|
static let segment = "segment" |
|
|
|
|
static let keystore = "keystore" |
|
|
|
|
static let privateKey = "privateKey" |
|
|
|
|
static let password = "password" |
|
|
|
|
static let watch = "watch" |
|
|
|
|
static let mnemonic = "mnemonic" |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var segmentRow: SegmentedRow<String>? { |
|
|
|
|
return form.rowBy(tag: Values.segment) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var keystoreRow: TextAreaRow? { |
|
|
|
|
return form.rowBy(tag: Values.keystore) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var mnemonicRow: TextAreaRow? { |
|
|
|
|
return form.rowBy(tag: Values.mnemonic) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var privateKeyRow: TextAreaRow? { |
|
|
|
|
return form.rowBy(tag: Values.privateKey) |
|
|
|
|
} |
|
|
|
|
//We don't actually use the rounded corner here, but it's a useful "content" view here |
|
|
|
|
let roundedBackground = RoundedBackground() |
|
|
|
|
let scrollView = UIScrollView() |
|
|
|
|
let footerBar = UIView() |
|
|
|
|
let tabBar = ImportWalletTabBar() |
|
|
|
|
let keystoreJSONTextView = TextView() |
|
|
|
|
let passwordTextField = TextField() |
|
|
|
|
let privateKeyTextView = TextView() |
|
|
|
|
let watchAddressTextField = AddressTextField() |
|
|
|
|
|
|
|
|
|
var passwordRow: TextFloatLabelRow? { |
|
|
|
|
return form.rowBy(tag: Values.password) |
|
|
|
|
} |
|
|
|
|
var keystoreJSONControlsStackView: UIStackView! |
|
|
|
|
var privateKeyControlsStackView: UIStackView! |
|
|
|
|
var watchControlsStackView: UIStackView! |
|
|
|
|
|
|
|
|
|
var watchRow: TextFloatLabelRow? { |
|
|
|
|
return form.rowBy(tag: Values.watch) |
|
|
|
|
} |
|
|
|
|
let importButton = UIButton(type: .system) |
|
|
|
|
|
|
|
|
|
weak var delegate: ImportWalletViewControllerDelegate? |
|
|
|
|
|
|
|
|
|
init( |
|
|
|
|
keystore: Keystore |
|
|
|
|
) { |
|
|
|
|
init(keystore: Keystore) { |
|
|
|
|
self.keystore = keystore |
|
|
|
|
super.init(nibName: nil, bundle: nil) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
override func viewDidLoad() { |
|
|
|
|
super.viewDidLoad() |
|
|
|
|
super.init(nibName: nil, bundle: nil) |
|
|
|
|
|
|
|
|
|
title = viewModel.title |
|
|
|
|
|
|
|
|
|
roundedBackground.translatesAutoresizingMaskIntoConstraints = false |
|
|
|
|
view.addSubview(roundedBackground) |
|
|
|
|
|
|
|
|
|
scrollView.translatesAutoresizingMaskIntoConstraints = false |
|
|
|
|
roundedBackground.addSubview(scrollView) |
|
|
|
|
|
|
|
|
|
tabBar.delegate = self |
|
|
|
|
tabBar.translatesAutoresizingMaskIntoConstraints = false |
|
|
|
|
view.addSubview(tabBar) |
|
|
|
|
|
|
|
|
|
keystoreJSONTextView.label.translatesAutoresizingMaskIntoConstraints = false |
|
|
|
|
keystoreJSONTextView.delegate = self |
|
|
|
|
keystoreJSONTextView.translatesAutoresizingMaskIntoConstraints = false |
|
|
|
|
keystoreJSONTextView.textView.returnKeyType = .next |
|
|
|
|
|
|
|
|
|
passwordTextField.label.translatesAutoresizingMaskIntoConstraints = false |
|
|
|
|
passwordTextField.delegate = self |
|
|
|
|
passwordTextField.translatesAutoresizingMaskIntoConstraints = false |
|
|
|
|
passwordTextField.textField.returnKeyType = .done |
|
|
|
|
passwordTextField.textField.isSecureTextEntry = true |
|
|
|
|
|
|
|
|
|
privateKeyTextView.label.translatesAutoresizingMaskIntoConstraints = false |
|
|
|
|
privateKeyTextView.delegate = self |
|
|
|
|
privateKeyTextView.translatesAutoresizingMaskIntoConstraints = false |
|
|
|
|
privateKeyTextView.textView.returnKeyType = .done |
|
|
|
|
|
|
|
|
|
watchAddressTextField.translatesAutoresizingMaskIntoConstraints = false |
|
|
|
|
watchAddressTextField.delegate = self |
|
|
|
|
watchAddressTextField.textField.returnKeyType = .done |
|
|
|
|
|
|
|
|
|
keystoreJSONControlsStackView = [ |
|
|
|
|
keystoreJSONTextView.label, |
|
|
|
|
.spacer(height: 4), |
|
|
|
|
keystoreJSONTextView, |
|
|
|
|
.spacer(height: 10), |
|
|
|
|
passwordTextField.label, |
|
|
|
|
.spacer(height: 4), |
|
|
|
|
passwordTextField, |
|
|
|
|
].asStackView(axis: .vertical) |
|
|
|
|
keystoreJSONControlsStackView.translatesAutoresizingMaskIntoConstraints = false |
|
|
|
|
|
|
|
|
|
privateKeyControlsStackView = [ |
|
|
|
|
privateKeyTextView.label, |
|
|
|
|
.spacer(height: 4), |
|
|
|
|
privateKeyTextView, |
|
|
|
|
].asStackView(axis: .vertical) |
|
|
|
|
privateKeyControlsStackView.translatesAutoresizingMaskIntoConstraints = false |
|
|
|
|
|
|
|
|
|
watchControlsStackView = [ |
|
|
|
|
watchAddressTextField.label, |
|
|
|
|
.spacer(height: 4), |
|
|
|
|
watchAddressTextField, |
|
|
|
|
].asStackView(axis: .vertical) |
|
|
|
|
watchControlsStackView.translatesAutoresizingMaskIntoConstraints = false |
|
|
|
|
|
|
|
|
|
let stackView = [ |
|
|
|
|
tabBar, |
|
|
|
|
.spacer(height: 10), |
|
|
|
|
keystoreJSONControlsStackView, |
|
|
|
|
privateKeyControlsStackView, |
|
|
|
|
watchControlsStackView, |
|
|
|
|
].asStackView(axis: .vertical, alignment: .center) |
|
|
|
|
stackView.translatesAutoresizingMaskIntoConstraints = false |
|
|
|
|
scrollView.addSubview(stackView) |
|
|
|
|
|
|
|
|
|
importButton.setTitle(R.string.localizable.importWalletImportButtonTitle(), for: .normal) |
|
|
|
|
importButton.addTarget(self, action: #selector(importWallet), for: .touchUpInside) |
|
|
|
|
|
|
|
|
|
let buttonsStackView = [importButton].asStackView(distribution: .fillEqually, contentHuggingPriority: .required) |
|
|
|
|
buttonsStackView.translatesAutoresizingMaskIntoConstraints = false |
|
|
|
|
|
|
|
|
|
footerBar.translatesAutoresizingMaskIntoConstraints = false |
|
|
|
|
footerBar.backgroundColor = Colors.appHighlightGreen |
|
|
|
|
roundedBackground.addSubview(footerBar) |
|
|
|
|
|
|
|
|
|
let buttonsHeight = CGFloat(60) |
|
|
|
|
footerBar.addSubview(buttonsStackView) |
|
|
|
|
|
|
|
|
|
let xMargin = CGFloat(7) |
|
|
|
|
let heightThatFitsPrivateKeyNicely = CGFloat(100) |
|
|
|
|
NSLayoutConstraint.activate([ |
|
|
|
|
keystoreJSONTextView.heightAnchor.constraint(equalToConstant: heightThatFitsPrivateKeyNicely), |
|
|
|
|
privateKeyTextView.heightAnchor.constraint(equalToConstant: heightThatFitsPrivateKeyNicely), |
|
|
|
|
|
|
|
|
|
tabBar.leadingAnchor.constraint(equalTo: stackView.leadingAnchor), |
|
|
|
|
tabBar.trailingAnchor.constraint(equalTo: stackView.trailingAnchor), |
|
|
|
|
|
|
|
|
|
keystoreJSONControlsStackView.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: xMargin), |
|
|
|
|
keystoreJSONControlsStackView.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: -xMargin), |
|
|
|
|
privateKeyControlsStackView.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: xMargin), |
|
|
|
|
privateKeyControlsStackView.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: -xMargin), |
|
|
|
|
watchControlsStackView.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: xMargin), |
|
|
|
|
watchControlsStackView.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: -xMargin), |
|
|
|
|
|
|
|
|
|
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor), |
|
|
|
|
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor), |
|
|
|
|
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor), |
|
|
|
|
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor), |
|
|
|
|
|
|
|
|
|
buttonsStackView.leadingAnchor.constraint(equalTo: footerBar.leadingAnchor), |
|
|
|
|
buttonsStackView.trailingAnchor.constraint(equalTo: footerBar.trailingAnchor), |
|
|
|
|
buttonsStackView.topAnchor.constraint(equalTo: footerBar.topAnchor), |
|
|
|
|
buttonsStackView.heightAnchor.constraint(equalToConstant: buttonsHeight), |
|
|
|
|
|
|
|
|
|
footerBar.leadingAnchor.constraint(equalTo: view.leadingAnchor), |
|
|
|
|
footerBar.trailingAnchor.constraint(equalTo: view.trailingAnchor), |
|
|
|
|
footerBar.heightAnchor.constraint(equalToConstant: buttonsHeight), |
|
|
|
|
footerBar.bottomAnchor.constraint(equalTo: view.bottomAnchor), |
|
|
|
|
|
|
|
|
|
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor), |
|
|
|
|
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor), |
|
|
|
|
scrollView.topAnchor.constraint(equalTo: view.topAnchor), |
|
|
|
|
scrollView.bottomAnchor.constraint(equalTo: footerBar.topAnchor), |
|
|
|
|
] + roundedBackground.createConstraintsWithContainer(view: view)) |
|
|
|
|
|
|
|
|
|
configure() |
|
|
|
|
showKeystoreControlsOnly() |
|
|
|
|
|
|
|
|
|
navigationItem.rightBarButtonItems = [ |
|
|
|
|
UIBarButtonItem(image: R.image.import_options(), style: .done, target: self, action: #selector(importOptions)), |
|
|
|
|
UIBarButtonItem(image: R.image.qr_code_icon(), style: .done, target: self, action: #selector(openReader)), |
|
|
|
@ -71,126 +172,97 @@ class ImportWalletViewController: FormViewController { |
|
|
|
|
self.demo() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let recipientRightView = FieldAppereance.addressFieldRightView( |
|
|
|
|
pasteAction: { [unowned self] in self.pasteAddressAction() }, |
|
|
|
|
qrAction: { [unowned self] in self.openReader() } |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
form |
|
|
|
|
+++ Section { |
|
|
|
|
var header = HeaderFooterView<InfoHeaderView>(.class) |
|
|
|
|
header.height = { 90 } |
|
|
|
|
header.onSetupView = { (view, section) -> Void in |
|
|
|
|
view.label.attributedText = R.string.localizable.walletImportSubtitle().styled( |
|
|
|
|
with: |
|
|
|
|
.color(UIColor(hex: "6e6e72")), |
|
|
|
|
.font(Fonts.regular(size: 16)!), |
|
|
|
|
.lineHeightMultiple(1.25) |
|
|
|
|
) |
|
|
|
|
view.logoImageView.image = R.image.create_wallet_import() |
|
|
|
|
} |
|
|
|
|
$0.header = header |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
<<< SegmentedRow<String>(Values.segment) { |
|
|
|
|
$0.options = [ |
|
|
|
|
//ImportSelectionType.mnemonic.title, |
|
|
|
|
ImportSelectionType.keystore.title, |
|
|
|
|
ImportSelectionType.privateKey.title, |
|
|
|
|
ImportSelectionType.watch.title, |
|
|
|
|
] |
|
|
|
|
$0.value = ImportSelectionType.keystore.title |
|
|
|
|
} |
|
|
|
|
func configure() { |
|
|
|
|
view.backgroundColor = viewModel.backgroundColor |
|
|
|
|
|
|
|
|
|
<<< AppFormAppearance.textArea(tag: Values.mnemonic) { |
|
|
|
|
$0.placeholder = R.string.localizable.mnemonic() |
|
|
|
|
$0.textAreaHeight = .fixed(cellHeight: 140) |
|
|
|
|
$0.add(rule: RuleRequired()) |
|
|
|
|
keystoreJSONTextView.configureOnce() |
|
|
|
|
keystoreJSONTextView.label.textAlignment = .center |
|
|
|
|
keystoreJSONTextView.label.text = viewModel.keystoreJSONLabel |
|
|
|
|
|
|
|
|
|
$0.hidden = Eureka.Condition.function([Values.segment], { _ in |
|
|
|
|
return self.segmentRow?.value != ImportSelectionType.mnemonic.title |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
passwordTextField.configureOnce() |
|
|
|
|
passwordTextField.label.textAlignment = .center |
|
|
|
|
passwordTextField.label.text = viewModel.passwordLabel |
|
|
|
|
|
|
|
|
|
<<< AppFormAppearance.textArea(tag: Values.keystore) { |
|
|
|
|
$0.placeholder = R.string.localizable.keystoreJSON() |
|
|
|
|
$0.textAreaHeight = .fixed(cellHeight: 140) |
|
|
|
|
$0.add(rule: RuleRequired()) |
|
|
|
|
privateKeyTextView.configureOnce() |
|
|
|
|
privateKeyTextView.label.textAlignment = .center |
|
|
|
|
privateKeyTextView.label.text = viewModel.privateKeyLabel |
|
|
|
|
|
|
|
|
|
$0.hidden = Eureka.Condition.function([Values.segment], { _ in |
|
|
|
|
return self.segmentRow?.value != ImportSelectionType.keystore.title |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
watchAddressTextField.configureOnce() |
|
|
|
|
watchAddressTextField.label.textAlignment = .center |
|
|
|
|
watchAddressTextField.label.text = viewModel.watchAddressLabel |
|
|
|
|
|
|
|
|
|
<<< AppFormAppearance.textArea(tag: Values.privateKey) { |
|
|
|
|
$0.placeholder = R.string.localizable.privateKey() |
|
|
|
|
$0.textAreaHeight = .fixed(cellHeight: 140) |
|
|
|
|
$0.add(rule: RuleRequired()) |
|
|
|
|
$0.add(rule: PrivateKeyRule()) |
|
|
|
|
$0.hidden = Eureka.Condition.function([Values.segment], { _ in |
|
|
|
|
return self.segmentRow?.value != ImportSelectionType.privateKey.title |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
importButton.setTitleColor(viewModel.buttonTitleColor, for: .normal) |
|
|
|
|
importButton.backgroundColor = viewModel.buttonBackgroundColor |
|
|
|
|
importButton.titleLabel?.font = viewModel.buttonFont |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
<<< AppFormAppearance.textFieldFloat(tag: Values.watch) { |
|
|
|
|
$0.add(rule: RuleRequired()) |
|
|
|
|
$0.add(rule: EthereumAddressRule()) |
|
|
|
|
$0.hidden = Eureka.Condition.function([Values.segment], { _ in |
|
|
|
|
return self.segmentRow?.value != ImportSelectionType.watch.title |
|
|
|
|
}) |
|
|
|
|
}.cellUpdate { cell, _ in |
|
|
|
|
cell.textField.placeholder = R.string.localizable.ethereumAddress() |
|
|
|
|
cell.textField.rightView = recipientRightView |
|
|
|
|
cell.textField.rightViewMode = .always |
|
|
|
|
} |
|
|
|
|
func didImport(account: Wallet) { |
|
|
|
|
delegate?.didImportAccount(account: account, in: self) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
<<< AppFormAppearance.textFieldFloat(tag: Values.password) { |
|
|
|
|
$0.validationOptions = .validatesOnDemand |
|
|
|
|
$0.hidden = Eureka.Condition.function([Values.segment], { _ in |
|
|
|
|
return self.segmentRow?.value != ImportSelectionType.keystore.title |
|
|
|
|
}) |
|
|
|
|
}.cellUpdate { cell, _ in |
|
|
|
|
cell.textField.isSecureTextEntry = true |
|
|
|
|
cell.textField.textAlignment = .left |
|
|
|
|
cell.textField.placeholder = R.string.localizable.password() |
|
|
|
|
} |
|
|
|
|
///Returns true only if valid |
|
|
|
|
private func validate() -> Bool { |
|
|
|
|
switch tabBar.tab { |
|
|
|
|
case .keystore: |
|
|
|
|
return validateKeystore() |
|
|
|
|
case .privateKey: |
|
|
|
|
return validatePrivateKey() |
|
|
|
|
case .watch: |
|
|
|
|
return validateWatch() |
|
|
|
|
default: |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
+++ Section("") |
|
|
|
|
///Returns true only if valid |
|
|
|
|
private func validateKeystore() -> Bool { |
|
|
|
|
if keystoreJSONTextView.value.isEmpty { |
|
|
|
|
displayError(title: viewModel.keystoreJSONLabel, error: ValidationError(msg: R.string.localizable.warningFieldRequired())) |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
if passwordTextField.value.isEmpty { |
|
|
|
|
displayError(title: viewModel.passwordLabel, error: ValidationError(msg: R.string.localizable.warningFieldRequired())) |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
<<< ButtonRow(R.string.localizable.importWalletImportButtonTitle()) { |
|
|
|
|
$0.title = $0.tag |
|
|
|
|
}.onCellSelection { [unowned self] _, _ in |
|
|
|
|
self.importWallet() |
|
|
|
|
} |
|
|
|
|
///Returns true only if valid |
|
|
|
|
private func validatePrivateKey() -> Bool { |
|
|
|
|
if let validationError = PrivateKeyRule().isValid(value: passwordTextField.value) { |
|
|
|
|
displayError(error: ValidationError(msg: validationError.msg)) |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func didImport(account: Wallet) { |
|
|
|
|
delegate?.didImportAccount(account: account, in: self) |
|
|
|
|
///Returns true only if valid |
|
|
|
|
private func validateWatch() -> Bool { |
|
|
|
|
if let validationError = EthereumAddressRule().isValid(value: watchAddressTextField.value) { |
|
|
|
|
displayError(error: ValidationError(msg: validationError.msg)) |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func importWallet() { |
|
|
|
|
let validatedError = keystoreRow?.section?.form?.validate() |
|
|
|
|
guard let errors = validatedError, errors.isEmpty else { return } |
|
|
|
|
@objc func importWallet() { |
|
|
|
|
guard validate() else { return } |
|
|
|
|
|
|
|
|
|
let keystoreInput = keystoreRow?.value?.trimmed ?? "" |
|
|
|
|
let privateKeyInput = privateKeyRow?.value?.trimmed ?? "" |
|
|
|
|
let password = passwordRow?.value ?? "" |
|
|
|
|
let watchInput = watchRow?.value?.trimmed ?? "" |
|
|
|
|
let mnemonicInput = mnemonicRow?.value?.trimmed ?? "" |
|
|
|
|
let words = mnemonicInput.components(separatedBy: " ").map { $0.trimmed } |
|
|
|
|
let keystoreInput = keystoreJSONTextView.value.trimmed |
|
|
|
|
let privateKeyInput = privateKeyTextView.value.trimmed |
|
|
|
|
let password = passwordTextField.value.trimmed |
|
|
|
|
let watchInput = watchAddressTextField.value.trimmed |
|
|
|
|
|
|
|
|
|
displayLoading(text: R.string.localizable.importWalletImportingIndicatorLabelTitle(), animated: false) |
|
|
|
|
|
|
|
|
|
let type = ImportSelectionType(title: segmentRow?.value) |
|
|
|
|
let importType: ImportType = { |
|
|
|
|
switch type { |
|
|
|
|
switch tabBar.tab { |
|
|
|
|
case .keystore: |
|
|
|
|
return .keystore(string: keystoreInput, password: password) |
|
|
|
|
case .privateKey: |
|
|
|
|
return .privateKey(privateKey: privateKeyInput) |
|
|
|
|
case .mnemonic: |
|
|
|
|
return .mnemonic(words: words, password: password) |
|
|
|
|
case .watch: |
|
|
|
|
let address = Address(string: watchInput)! // Address validated by form view. |
|
|
|
|
return .watch(address: address) |
|
|
|
@ -246,40 +318,60 @@ class ImportWalletViewController: FormViewController { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func setValueForCurrentField(string: String) { |
|
|
|
|
let type = ImportSelectionType(title: segmentRow?.value) |
|
|
|
|
switch type { |
|
|
|
|
switch tabBar.tab { |
|
|
|
|
case .keystore: |
|
|
|
|
keystoreRow?.value = string |
|
|
|
|
keystoreRow?.reload() |
|
|
|
|
keystoreJSONTextView.value = string |
|
|
|
|
case .privateKey: |
|
|
|
|
privateKeyRow?.value = string |
|
|
|
|
privateKeyRow?.reload() |
|
|
|
|
privateKeyTextView.value = string |
|
|
|
|
case .watch: |
|
|
|
|
watchRow?.value = string |
|
|
|
|
watchRow?.reload() |
|
|
|
|
case .mnemonic: |
|
|
|
|
mnemonicRow?.value = string |
|
|
|
|
mnemonicRow?.reload() |
|
|
|
|
watchAddressTextField.value = string |
|
|
|
|
default: |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@objc func pasteAddressAction() { |
|
|
|
|
let value = UIPasteboard.general.string?.trimmed |
|
|
|
|
watchRow?.value = value |
|
|
|
|
watchRow?.reload() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
required init?(coder aDecoder: NSCoder) { |
|
|
|
|
fatalError("init(coder:) has not been implemented") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private func showKeystoreControlsOnly() { |
|
|
|
|
keystoreJSONControlsStackView.isHidden = false |
|
|
|
|
privateKeyControlsStackView.isHidden = true |
|
|
|
|
watchControlsStackView.isHidden = true |
|
|
|
|
} |
|
|
|
|
private func showPrivateKeyControlsOnly() { |
|
|
|
|
keystoreJSONControlsStackView.isHidden = true |
|
|
|
|
privateKeyControlsStackView.isHidden = false |
|
|
|
|
watchControlsStackView.isHidden = true |
|
|
|
|
} |
|
|
|
|
private func showWatchControlsOnly() { |
|
|
|
|
keystoreJSONControlsStackView.isHidden = true |
|
|
|
|
privateKeyControlsStackView.isHidden = true |
|
|
|
|
watchControlsStackView.isHidden = false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private func moveFocusToTextEntryField(after textInput: UIView) { |
|
|
|
|
switch textInput { |
|
|
|
|
case keystoreJSONTextView.textView: |
|
|
|
|
passwordTextField.textField.becomeFirstResponder() |
|
|
|
|
case passwordTextField.textField: |
|
|
|
|
view.endEditing(true) |
|
|
|
|
case privateKeyTextView.textView: |
|
|
|
|
view.endEditing(true) |
|
|
|
|
case watchAddressTextField.textField: |
|
|
|
|
view.endEditing(true) |
|
|
|
|
default: |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
extension ImportWalletViewController: UIDocumentPickerDelegate { |
|
|
|
|
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) { |
|
|
|
|
if controller.documentPickerMode == UIDocumentPickerMode.import { |
|
|
|
|
let text = try? String(contentsOfFile: url.path) |
|
|
|
|
keystoreRow?.value = text |
|
|
|
|
keystoreRow?.reload() |
|
|
|
|
guard controller.documentPickerMode == UIDocumentPickerMode.import else { return } |
|
|
|
|
let text = try? String(contentsOfFile: url.path) |
|
|
|
|
if let text = text { |
|
|
|
|
keystoreJSONTextView.value = text |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -295,3 +387,71 @@ extension ImportWalletViewController: QRCodeReaderDelegate { |
|
|
|
|
reader.dismiss(animated: true) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
extension ImportWalletViewController: TextFieldDelegate { |
|
|
|
|
func shouldReturn(in textField: TextField) -> Bool { |
|
|
|
|
moveFocusToTextEntryField(after: textField.textField) |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func doneButtonTapped(for textField: TextField) { |
|
|
|
|
view.endEditing(true) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func nextButtonTapped(for textField: TextField) { |
|
|
|
|
moveFocusToTextEntryField(after: textField.textField) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
extension ImportWalletViewController: TextViewDelegate { |
|
|
|
|
func shouldReturn(in textView: TextView) -> Bool { |
|
|
|
|
moveFocusToTextEntryField(after: textView.textView) |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func doneButtonTapped(for textView: TextView) { |
|
|
|
|
view.endEditing(true) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func nextButtonTapped(for textView: TextView) { |
|
|
|
|
moveFocusToTextEntryField(after: textView.textView) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
extension ImportWalletViewController: AddressTextFieldDelegate { |
|
|
|
|
func displayError(error: Error, for textField: AddressTextField) { |
|
|
|
|
displayError(error: error) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func openQRCodeReader(for textField: AddressTextField) { |
|
|
|
|
openReader() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func didPaste(in textField: AddressTextField) { |
|
|
|
|
view.endEditing(true) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func shouldReturn(in textField: AddressTextField) -> Bool { |
|
|
|
|
moveFocusToTextEntryField(after: textField.textField) |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func shouldChange(in range: NSRange, to string: String, in textField: AddressTextField) -> Bool { |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
extension ImportWalletViewController: ImportWalletTabBarDelegate { |
|
|
|
|
func didPressImportWalletTab(tab: ImportWalletTab, in tabBar: ImportWalletTabBar) { |
|
|
|
|
switch tab { |
|
|
|
|
case .keystore: |
|
|
|
|
showKeystoreControlsOnly() |
|
|
|
|
case .privateKey: |
|
|
|
|
showPrivateKeyControlsOnly() |
|
|
|
|
case .watch: |
|
|
|
|
showWatchControlsOnly() |
|
|
|
|
default: |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|