diff --git a/Kukai Mobile/Controls/EnterAddressComponent/EnterAddressComponent.swift b/Kukai Mobile/Controls/EnterAddressComponent/EnterAddressComponent.swift index da908ed4..63b49010 100644 --- a/Kukai Mobile/Controls/EnterAddressComponent/EnterAddressComponent.swift +++ b/Kukai Mobile/Controls/EnterAddressComponent/EnterAddressComponent.swift @@ -42,6 +42,13 @@ public class EnterAddressComponent: UIView { private var currentSelectedType: AddressType = .tezosAddress public weak var delegate: EnterAddressComponentDelegate? = nil + public var allowEmpty: Bool = false { + didSet { + if (self.textField.text == nil || self.textField.text == "") && allowEmpty { + self.sendButton.isEnabled = true + } + } + } required init?(coder aDecoder: NSCoder) { @@ -71,7 +78,7 @@ public class EnterAddressComponent: UIView { textField.validatorTextFieldDelegate = self self.hideError(animate: false) - self.sendButton.isEnabled = false + self.sendButton.isEnabled = allowEmpty } public func updateAvilableAddressTypes(_ types: [AddressType]) { @@ -275,7 +282,7 @@ extension EnterAddressComponent: ValidatorTextFieldDelegate { self.sendButton.isEnabled = true } else if text == "" { - self.sendButton.isEnabled = false + self.sendButton.isEnabled = allowEmpty } else { self.sendButton.isEnabled = false diff --git a/Kukai Mobile/Extensions/UIViewController+extensions.swift b/Kukai Mobile/Extensions/UIViewController+extensions.swift index a73dacb2..5db27bba 100644 --- a/Kukai Mobile/Extensions/UIViewController+extensions.swift +++ b/Kukai Mobile/Extensions/UIViewController+extensions.swift @@ -42,7 +42,7 @@ extension UIViewController { activityViewStatusLabel.font = UIFont.custom(ofType: .medium, andSize: 16) activityViewStatusLabel.textColor = UIColor.white activityViewStatusLabel.textAlignment = .center - activityViewStatusLabel.frame = CGRect(x: view.center.x, y: view.center.y, width: view.frame.width - 64, height: 50) + activityViewStatusLabel.frame = CGRect(x: view.center.x, y: view.center.y, width: view.frame.width - 64, height: 200) UIViewController.activityViewActivityIndicator.color = UIColor.white diff --git a/Kukai Mobile/Modules/Account/TokenDetailsViewModel.swift b/Kukai Mobile/Modules/Account/TokenDetailsViewModel.swift index 12c418ff..95cc5dfe 100644 --- a/Kukai Mobile/Modules/Account/TokenDetailsViewModel.swift +++ b/Kukai Mobile/Modules/Account/TokenDetailsViewModel.swift @@ -7,6 +7,7 @@ import UIKit import KukaiCoreSwift +import Combine import OSLog struct AllChartData: Hashable { @@ -122,6 +123,7 @@ public class TokenDetailsViewModel: ViewModel, TokenDetailsChartCellDelegate { private let chartDateFormatter = DateFormatter(withFormat: "MMM dd HH:mm a") private var initialChartLoad = true private var onlineXTZFetchGroup = DispatchGroup() + private var bag = [AnyCancellable]() // Set by VC weak var delegate: (TokenDetailsViewModelDelegate & TokenDetailsBakerDelegate & TokenDetailsStakeBalanceDelegate)? = nil @@ -155,6 +157,22 @@ public class TokenDetailsViewModel: ViewModel, TokenDetailsChartCellDelegate { var finaliseableAmount: TokenAmount = .zero() + override init() { + super.init() + + DependencyManager.shared.$addressRefreshed + .dropFirst() + .sink { [weak self] address in + let selectedAddress = DependencyManager.shared.selectedWalletAddress ?? "" + if self?.dataSource != nil && selectedAddress == address { + self?.refresh(animate: true) + } + }.store(in: &bag) + } + + deinit { + bag.forEach({ $0.cancel() }) + } diff --git a/Kukai Mobile/Modules/Login/LoginViewController.swift b/Kukai Mobile/Modules/Login/LoginViewController.swift index f50ed1a6..75bef38c 100644 --- a/Kukai Mobile/Modules/Login/LoginViewController.swift +++ b/Kukai Mobile/Modules/Login/LoginViewController.swift @@ -63,7 +63,7 @@ class LoginViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - /* // TODO: uncomment + // Hide biometric button if its not enabled, or we are in the middle of the edit passcode flow if isEditPasscodeMode() || CurrentDevice.biometricTypeAuthorized() == .none || StorageService.isBiometricEnabled() == false { useBiometricsButton.isHidden = true @@ -83,9 +83,6 @@ class LoginViewController: UIViewController { // Edit passcode popup self.hiddenTextfield.becomeFirstResponder() } - */ - - self.next() } override func viewDidDisappear(_ animated: Bool) { diff --git a/Kukai Mobile/Modules/Send/SendAbstractConfirmViewController.swift b/Kukai Mobile/Modules/Send/SendAbstractConfirmViewController.swift index f00ae6a6..84919a1a 100644 --- a/Kukai Mobile/Modules/Send/SendAbstractConfirmViewController.swift +++ b/Kukai Mobile/Modules/Send/SendAbstractConfirmViewController.swift @@ -114,6 +114,7 @@ class SendAbstractConfirmViewController: UIViewController { if collapseOnly == false { if isDuringStakeOnboardingFlow { topMostNavigationController?.dismiss(animated: true) + } else { (self?.presentingViewController as? UINavigationController)?.popToHome() } diff --git a/Kukai Mobile/Modules/Stake/ChooseBakerConfirmViewController.swift b/Kukai Mobile/Modules/Stake/ChooseBakerConfirmViewController.swift index 392a6a6a..d11a502b 100644 --- a/Kukai Mobile/Modules/Stake/ChooseBakerConfirmViewController.swift +++ b/Kukai Mobile/Modules/Stake/ChooseBakerConfirmViewController.swift @@ -181,10 +181,7 @@ class ChooseBakerConfirmViewController: SendAbstractConfirmViewController, Slide func didCompleteSlide() { self.blockInteraction(exceptFor: [closeButton]) - - // TODO: swap back - //self.performAuth() - self.handleApproval(opHash: "", slideButton: self.slideButton) + self.performAuth() } override func authSuccessful() { diff --git a/Kukai Mobile/Modules/Stake/ChooseBakerViewController.swift b/Kukai Mobile/Modules/Stake/ChooseBakerViewController.swift index 18eb48c1..a667d15a 100644 --- a/Kukai Mobile/Modules/Stake/ChooseBakerViewController.swift +++ b/Kukai Mobile/Modules/Stake/ChooseBakerViewController.swift @@ -13,7 +13,7 @@ class ChooseBakerViewController: UIViewController { @IBOutlet weak var tableView: UITableView! - private let viewModel = ChooseBakerViewModel() + private var viewModel = ChooseBakerViewModel() private var cancellable: AnyCancellable? private let footerView = UIView(frame: CGRect(x: 0, y: 0, width: 1, height: 2)) private let blankView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 0.1)) @@ -49,7 +49,10 @@ class ChooseBakerViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - viewModel.refresh(animate: false) + + if viewModel.bakers.count == 0 { + viewModel.refresh(animate: false) + } } public func enteredCustomBaker(address: String) { @@ -58,9 +61,10 @@ class ChooseBakerViewController: UIViewController { if address == "" { let currentDelegate = DependencyManager.shared.balanceService.account.delegate let name = currentDelegate?.alias ?? currentDelegate?.address.truncateTezosAddress() ?? "" - let baker = TzKTBaker(address: "", name: name) + let baker = TzKTBaker(address: currentDelegate?.address ?? "", name: name) TransactionService.shared.delegateData.chosenBaker = baker + TransactionService.shared.stakeData.chosenBaker = baker TransactionService.shared.delegateData.isAdd = false } else { @@ -68,11 +72,13 @@ class ChooseBakerViewController: UIViewController { if let foundBaker = viewModel.bakerFor(address: address) { TransactionService.shared.delegateData.chosenBaker = foundBaker + TransactionService.shared.stakeData.chosenBaker = foundBaker TransactionService.shared.delegateData.isAdd = true } else { let baker = TzKTBaker(address: address, name: address.truncateTezosAddress()) TransactionService.shared.delegateData.chosenBaker = baker + TransactionService.shared.stakeData.chosenBaker = baker TransactionService.shared.delegateData.isAdd = true } } @@ -97,7 +103,13 @@ class ChooseBakerViewController: UIViewController { return } - let operations = OperationFactory.delegateOperation(to: toAddress, from: selectedWallet.address) + var operations: [KukaiCoreSwift.Operation] = [] + if TransactionService.shared.delegateData.isAdd == true { + operations = OperationFactory.delegateOperation(to: toAddress, from: selectedWallet.address) + } else { + operations = OperationFactory.undelegateOperation(address: selectedWallet.address) + } + DependencyManager.shared.tezosNodeClient.estimate(operations: operations, walletAddress: selectedWallet.address, base58EncodedPublicKey: selectedWallet.publicKeyBase58encoded()) { [weak self] estimationResult in self?.hideLoadingView() @@ -132,6 +144,7 @@ extension ChooseBakerViewController: UITableViewDelegate { if let baker = viewModel.bakerFor(indexPath: indexPath) { TransactionService.shared.delegateData.chosenBaker = baker + TransactionService.shared.stakeData.chosenBaker = baker TransactionService.shared.delegateData.isAdd = true self.performSegue(withIdentifier: "details", sender: nil) diff --git a/Kukai Mobile/Modules/Stake/ChooseBakerViewModel.swift b/Kukai Mobile/Modules/Stake/ChooseBakerViewModel.swift index 42a82d50..24e5d8a1 100644 --- a/Kukai Mobile/Modules/Stake/ChooseBakerViewModel.swift +++ b/Kukai Mobile/Modules/Stake/ChooseBakerViewModel.swift @@ -32,15 +32,6 @@ class ChooseBakerViewModel: ViewModel, UITableViewDiffableDataSourceHandler { override init() { super.init() - - DependencyManager.shared.$addressRefreshed - .dropFirst() - .sink { [weak self] address in - let selectedAddress = DependencyManager.shared.selectedWalletAddress ?? "" - if self?.dataSource != nil && selectedAddress == address { - self?.refresh(animate: true) - } - }.store(in: &bag) } deinit { diff --git a/Kukai Mobile/Modules/Stake/EnterCustomBakerViewController.swift b/Kukai Mobile/Modules/Stake/EnterCustomBakerViewController.swift index 4b4122bf..ada6d567 100644 --- a/Kukai Mobile/Modules/Stake/EnterCustomBakerViewController.swift +++ b/Kukai Mobile/Modules/Stake/EnterCustomBakerViewController.swift @@ -18,6 +18,7 @@ class EnterCustomBakerViewController: UIViewController, EnterAddressComponentDel enterAddressComponent.headerLabel.text = "Baker:" enterAddressComponent.updateAvilableAddressTypes([.tezosAddress, .tezosDomain]) enterAddressComponent.delegate = self + enterAddressComponent.allowEmpty = true // undelegate by entering blank into field } func validatedInput(entered: String, validAddress: Bool, ofType: AddressType) { diff --git a/Kukai Mobile/Modules/Stake/Stake.storyboard b/Kukai Mobile/Modules/Stake/Stake.storyboard index 00971a25..fdc72c24 100644 --- a/Kukai Mobile/Modules/Stake/Stake.storyboard +++ b/Kukai Mobile/Modules/Stake/Stake.storyboard @@ -253,7 +253,7 @@ - + @@ -293,7 +293,7 @@ Bakers validate and participate in Tezos governance. By delegating to a Baker, y - + @@ -434,6 +434,9 @@ Bakers validate and participate in Tezos governance. By delegating to a Baker, y + + + @@ -451,6 +454,7 @@ Bakers validate and participate in Tezos governance. By delegating to a Baker, y + @@ -471,7 +475,7 @@ Bakers validate and participate in Tezos governance. By delegating to a Baker, y - + @@ -704,7 +708,7 @@ Bakers validate and participate in Tezos governance. By delegating to a Baker, y - + @@ -745,7 +749,7 @@ Bakers validate and participate in Tezos governance. By delegating to a Baker, y - + @@ -1090,6 +1094,7 @@ Bakers validate and participate in Tezos governance. By delegating to a Baker, y + @@ -1114,7 +1119,7 @@ Bakers validate and participate in Tezos governance. By delegating to a Baker, y - + @@ -1703,7 +1708,7 @@ Bakers validate and participate in Tezos governance. By delegating to a Baker, y - + @@ -1711,7 +1716,7 @@ Bakers validate and participate in Tezos governance. By delegating to a Baker, y - + @@ -1719,7 +1724,7 @@ Bakers validate and participate in Tezos governance. By delegating to a Baker, y - + @@ -2345,7 +2350,7 @@ Bakers validate and participate in Tezos governance. By delegating to a Baker, y - + @@ -2353,7 +2358,7 @@ Bakers validate and participate in Tezos governance. By delegating to a Baker, y - + @@ -2361,7 +2366,7 @@ Bakers validate and participate in Tezos governance. By delegating to a Baker, y - + @@ -3050,11 +3055,219 @@ Bakers also vote on behalf of users to make changes to the network, during the G + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Kukai Mobile/Modules/Stake/StakeOnboardingContainerViewController.swift b/Kukai Mobile/Modules/Stake/StakeOnboardingContainerViewController.swift index b20ee93f..cbd8a458 100644 --- a/Kukai Mobile/Modules/Stake/StakeOnboardingContainerViewController.swift +++ b/Kukai Mobile/Modules/Stake/StakeOnboardingContainerViewController.swift @@ -6,6 +6,8 @@ // import UIKit +import KukaiCoreSwift +import Combine class StakeOnboardingContainerViewController: UIViewController { @@ -23,55 +25,41 @@ class StakeOnboardingContainerViewController: UIViewController { @IBOutlet weak var navigationContainerView: UIView! private var childNavigationController: UINavigationController? = nil private var currentChildViewController: UIViewController? = nil + private var bag = [AnyCancellable]() + private var currentStep: String = "" override func viewDidLoad() { super.viewDidLoad() GradientView.add(toView: self.view, withType: .fullScreenBackground) actionButton.customButtonType = .primary - self.pageIndicator1.setInprogress(pageNumber: 1) - //NotificationCenter.default.addObserver(self, selector: #selector(bakerConfirmation), name: ChooseBakerViewController.notificationNameBakerChosen, object: nil) - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - /* - DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in - self?.pageIndicator1.setComplete() - self?.setProgressSegmentComplete(self?.progressSegment1) - self?.pageIndicator2.setInprogress(pageNumber: 2) - } - - DispatchQueue.main.asyncAfter(deadline: .now() + 6) { [weak self] in - self?.pageIndicator2.setComplete() - self?.setProgressSegmentComplete(self?.progressSegment2) - self?.pageIndicator3.setInprogress(pageNumber: 3) - } - - DispatchQueue.main.asyncAfter(deadline: .now() + 9) { [weak self] in - self?.pageIndicator3.setComplete() - self?.setProgressSegmentComplete(self?.progressSegment3) - self?.pageIndicator4.setInprogress(pageNumber: 4) - } - - DispatchQueue.main.asyncAfter(deadline: .now() + 12) { [weak self] in - self?.pageIndicator4.setComplete() - self?.setProgressSegmentComplete(self?.progressSegment4) - self?.pageIndicator5.setInprogress(pageNumber: 5) - } - - DispatchQueue.main.asyncAfter(deadline: .now() + 15) { [weak self] in - self?.pageIndicator5.setComplete() - } - */ + DependencyManager.shared.activityService.$addressesWithPendingOperation + .dropFirst() + .sink { [weak self] addresses in + guard let address = DependencyManager.shared.selectedWalletAddress else { + return + } + + DispatchQueue.main.async { [weak self] in + if addresses.contains([address]) { + self?.showLoadingView() + self?.updateLoadingViewStatusLabel(message: "Waiting for transaction to complete \n\nThis should only take a few seconds") + + } else { + self?.hideLoadingView() + self?.handleOperationComplete() + } + } + }.store(in: &bag) } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) - - //NotificationCenter.default.removeObserver(self, name: ChooseBakerViewController.notificationNameBakerChosen, object: nil) + } + + @IBAction func closeTapped(_ sender: Any) { + self.navigationController?.popToDetails() } func setProgressSegmentComplete(_ view: UIProgressView?) { @@ -83,7 +71,6 @@ class StakeOnboardingContainerViewController: UIViewController { override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "embed", let dest = segue.destination as? UINavigationController { childNavigationController = dest - } } @@ -94,24 +81,65 @@ class StakeOnboardingContainerViewController: UIViewController { } currentChildViewController = currentChildVc + currentStep = currentChildVc.title ?? "" + switch currentChildVc.title { case "step1": currentChildVc.performSegue(withIdentifier: "next", sender: nil) - self.pageIndicator1.setComplete() - self.setProgressSegmentComplete(self.progressSegment1) - self.pageIndicator2.setInprogress(pageNumber: 2) + self.pageIndicator1.setInprogress(pageNumber: 1) case "step2": if handlePageControllerNext(vc: currentChildVc) == true { actionButton.setTitle("Choose Baker", for: .normal) - self.pageIndicator2.setComplete() - self.setProgressSegmentComplete(self.progressSegment2) - self.pageIndicator3.setInprogress(pageNumber: 3) + self.pageIndicator1.setComplete() + self.setProgressSegmentComplete(self.progressSegment1) + self.pageIndicator2.setInprogress(pageNumber: 2) } case "step3": self.performSegue(withIdentifier: "chooseBaker", sender: nil) + case "step4": + currentChildVc.performSegue(withIdentifier: "next", sender: nil) + + case "step5": + if handlePageControllerNext(vc: currentChildVc) == true { + actionButton.setTitle("Stake", for: .normal) + self.pageIndicator3.setComplete() + self.setProgressSegmentComplete(self.progressSegment3) + self.pageIndicator4.setInprogress(pageNumber: 4) + } + + case "step6": + TransactionService.shared.currentTransactionType = .stake + TransactionService.shared.stakeData.chosenToken = Token.xtz(withAmount: DependencyManager.shared.balanceService.account.xtzBalance) + // chosenBaker will be set inside the the delegation flow + self.performSegue(withIdentifier: "stake", sender: nil) + + case "step7": + self.navigationController?.popToDetails() + + default: + self.windowError(withTitle: "error".localized(), description: "Unknown error") + } + } + + private func handleOperationComplete() { + switch currentStep { + case "step3": + self.pageIndicator2.setComplete() + self.setProgressSegmentComplete(self.progressSegment2) + self.pageIndicator3.setInprogress(pageNumber: 3) + self.currentChildViewController?.performSegue(withIdentifier: "next", sender: nil) + self.actionButton.setTitle("Next", for: .normal) + + case "step6": + self.pageIndicator4.setComplete() + self.setProgressSegmentComplete(self.progressSegment4) + self.pageIndicator5.setInprogress(pageNumber: 5) + self.currentChildViewController?.performSegue(withIdentifier: "next", sender: nil) + self.actionButton.setTitle("Done", for: .normal) + default: self.windowError(withTitle: "error".localized(), description: "Unknown error") } @@ -131,13 +159,4 @@ class StakeOnboardingContainerViewController: UIViewController { return false } } - - /* - @objc private func bakerConfirmation() { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in - self?.hideLoadingView() - self?.performSegue(withIdentifier: "confirmBaker", sender: nil) - } - } - */ }