diff --git a/Kukai Mobile.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Kukai Mobile.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 64b3ca4e..4bef8cd4 100644 --- a/Kukai Mobile.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Kukai Mobile.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -69,7 +69,7 @@ "location" : "https://github.com/kukai-wallet/kukai-core-swift", "state" : { "branch" : "develop", - "revision" : "55c19abeb503e4e69c7a811d85e77d8199e74b9d" + "revision" : "6a7d1f937117de5d88b9a936e74c64084a56be2b" } }, { diff --git a/Kukai Mobile/Controls/EnterAddressComponent/AddressTypeViewController.swift b/Kukai Mobile/Controls/EnterAddressComponent/AddressTypeViewController.swift index d5f6d1ca..b3563825 100644 --- a/Kukai Mobile/Controls/EnterAddressComponent/AddressTypeViewController.swift +++ b/Kukai Mobile/Controls/EnterAddressComponent/AddressTypeViewController.swift @@ -7,6 +7,7 @@ import UIKit import KukaiCoreSwift +import CloudKit public enum AddressType: String, CaseIterable { case tezosAddress = "Tezos Address" @@ -14,6 +15,7 @@ public enum AddressType: String, CaseIterable { case gmail = "Google" case reddit = "Reddit" case twitter = "Twitter" + case email = "Email" } public protocol AddressTypeDelegate: AnyObject { @@ -30,7 +32,7 @@ class AddressTypeViewController: UIViewController, UITableViewDelegate, UITableV public var selectedIndex: IndexPath = IndexPath(row: 0, section: 0) public var headerText: String = "Recipient Address" - public var supportedAddressTypes: [AddressType] = [.tezosAddress, .tezosDomain, .gmail] + public var supportedAddressTypes: [AddressType] = [.tezosAddress, .tezosDomain, .gmail, .reddit, .twitter, .email] override func viewDidLoad() { super.viewDidLoad() @@ -72,6 +74,9 @@ class AddressTypeViewController: UIViewController, UITableViewDelegate, UITableV case .twitter: tempMetadata = WalletMetadata(address: "", hdWalletGroupName: nil, walletNickname: nil, socialUsername: nil, socialType: .twitter, type: .social, children: [], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: "", backedUp: false) + + case .email: + tempMetadata = WalletMetadata(address: "", hdWalletGroupName: nil, walletNickname: nil, socialUsername: nil, socialType: .email, type: .social, children: [], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: "", backedUp: false) } return TransactionService.walletMedia(forWalletMetadata: tempMetadata, ofSize: .size_22).image diff --git a/Kukai Mobile/Controls/EnterAddressComponent/EnterAddressComponent.swift b/Kukai Mobile/Controls/EnterAddressComponent/EnterAddressComponent.swift index 1688ac29..46097997 100644 --- a/Kukai Mobile/Controls/EnterAddressComponent/EnterAddressComponent.swift +++ b/Kukai Mobile/Controls/EnterAddressComponent/EnterAddressComponent.swift @@ -37,6 +37,7 @@ public class EnterAddressComponent: UIView { private let addressTypeVC = UIStoryboard(name: "SendAddressType", bundle: nil).instantiateInitialViewController() as? AddressTypeViewController private var addressTypeHeader = "Recipient Address" private let nibName = "EnterAddressComponent" + private let cloudKitService = CloudKitService() private var currentSelectedType: AddressType = .tezosAddress @@ -127,7 +128,7 @@ public class EnterAddressComponent: UIView { completion(Result.success(string)) case .tezosDomain: - DependencyManager.shared.tezosDomainsClient.getAddressFor(domain: string, completion: { result in + DependencyManager.shared.tezosDomainsClient.getAddressFor(domain: string.lowercased(), completion: { result in switch result { case .success(let response): if let add = response.data?.domain.address { @@ -150,10 +151,31 @@ public class EnterAddressComponent: UIView { case .twitter: handleTorus(verifier: .twitter, string: string, completion: completion) + + case .email: + handleTorus(verifier: .email, string: string, completion: completion) } } private func handleTorus(verifier: TorusAuthProvider, string: String, completion: @escaping ((Result) -> Void)) { + + // Check to see if we need to fetch torus verfier config + if DependencyManager.shared.torusVerifiers.keys.count == 0 { + cloudKitService.fetchConfigItems { [weak self] error in + if let e = error { + completion(Result.failure(KukaiError.unknown(withString: String.localized(String.localized("error-no-cloudkit-config"), withArguments: e.localizedDescription)))) + + } else { + DependencyManager.shared.torusVerifiers = self?.cloudKitService.extractTorusConfig() ?? [:] + self?.performTorusLookup(verifier: verifier, string: string, completion: completion) + } + } + } else { + performTorusLookup(verifier: verifier, string: string, completion: completion) + } + } + + private func performTorusLookup(verifier: TorusAuthProvider, string: String, completion: @escaping ((Result) -> Void)) { guard DependencyManager.shared.torusVerifiers[verifier] != nil else { let error = KukaiError.unknown(withString: "No \(verifier.rawValue) verifier details found") completion(Result.failure(error)) @@ -167,8 +189,6 @@ public class EnterAddressComponent: UIView { - - // MARK: - UI functions public func showError(message: String) { @@ -248,6 +268,9 @@ extension EnterAddressComponent: ValidatorTextFieldDelegate { case .twitter: return "Invalid Twitter username" + + case .email: + return "Invalid email address" } } @@ -302,6 +325,12 @@ extension EnterAddressComponent: AddressTypeDelegate { addressTypeButton.setTitle("Twitter", for: .normal) textField.placeholder = "@ Enter Twitter Handle" textField.validator = NoWhiteSpaceStringValidator() + + case .email: + sendToIcon.image = AddressTypeViewController.imageFor(addressType: type) + addressTypeButton.setTitle("Email", for: .normal) + textField.placeholder = "Enter email address" + textField.validator = NoWhiteSpaceStringValidator() } if !textField.revalidateTextfield() { diff --git a/Kukai Mobile/Controls/ScanViewController.swift b/Kukai Mobile/Controls/ScanViewController.swift index 6fbf5fcf..4770469e 100644 --- a/Kukai Mobile/Controls/ScanViewController.swift +++ b/Kukai Mobile/Controls/ScanViewController.swift @@ -344,7 +344,6 @@ class ScanViewController: UIViewController, AVCaptureMetadataOutputObjectsDelega self.textfield.text = "" } else { - captureSession.stopRunning() found(code: stringToCheck) } } diff --git a/Kukai Mobile/Localization/en.lproj/Localizable.strings b/Kukai Mobile/Localization/en.lproj/Localizable.strings index af7d7b97..53afb9cf 100644 --- a/Kukai Mobile/Localization/en.lproj/Localizable.strings +++ b/Kukai Mobile/Localization/en.lproj/Localizable.strings @@ -23,6 +23,7 @@ "error-wc2-errorcode"="Unable to respond to Wallet Connect: %@ - %@"; "error-no-cloudkit-config"="Unable to fetch config settings: %@"; "error-cant-cache"="Unable to cache"; +"error-wallet-already-exists"="This wallet address already exists. It can't be added again"; "error-cant-create-wallet"="Unable to create wallet"; "error-missing-verifier"="Unsupported, due to missing verifier"; "error-new-wallet-details"="Unable to create wallet from details supplied. Please check and try again"; diff --git a/Kukai Mobile/Modules/Collectibles/CollectiblesDetailsViewModel.swift b/Kukai Mobile/Modules/Collectibles/CollectiblesDetailsViewModel.swift index 025fab38..10e34a16 100644 --- a/Kukai Mobile/Modules/Collectibles/CollectiblesDetailsViewModel.swift +++ b/Kukai Mobile/Modules/Collectibles/CollectiblesDetailsViewModel.swift @@ -345,7 +345,7 @@ class CollectiblesDetailsViewModel: ViewModel, UICollectionViewDiffableDataSourc if mediaType == nil && !isCached { // Check to see if we have a cached thumbnail. If so load that (using its dimensions for the correct layout), then load real image later - let cacheURL = MediaProxyService.url(fromUri: nft?.thumbnailURI, ofFormat: MediaProxyService.Format.small.rawFormat()) + let cacheURL = MediaProxyService.url(fromUri: nft?.thumbnailURI, ofFormat: MediaProxyService.Format.medium.rawFormat()) MediaProxyService.sizeForImageIfCached(url: cacheURL) { size in let finalSize = (size ?? CGSize(width: 300, height: 300)) let mediaContent = MediaContent(isImage: true, isThumbnail: true, mediaURL: cacheURL, mediaURL2: nil, width: finalSize.width, height: finalSize.height) @@ -393,7 +393,7 @@ class CollectiblesDetailsViewModel: ViewModel, UICollectionViewDiffableDataSourc // Fallback else { - let mediaContent = MediaContent(isImage: true, isThumbnail: true, mediaURL: MediaProxyService.url(fromUri: nft?.thumbnailURI, ofFormat: MediaProxyService.Format.small.rawFormat()), mediaURL2: nil, width: 300, height: 300) + let mediaContent = MediaContent(isImage: true, isThumbnail: true, mediaURL: MediaProxyService.url(fromUri: nft?.thumbnailURI, ofFormat: MediaProxyService.Format.medium.rawFormat()), mediaURL2: nil, width: 300, height: 300) completion((mediaContent: mediaContent, needsToDownloadFullImage: false, needsMediaTypeVerification: true)) } } @@ -431,7 +431,7 @@ class CollectiblesDetailsViewModel: ViewModel, UICollectionViewDiffableDataSourc MediaProxyService.sizeForImageIfCached(url: cacheURL) { size in let finalSize = (size ?? CGSize(width: 300, height: 300)) if mediaType == .imageOnly { - let url = loadingThumbnailFirst ? MediaProxyService.url(fromUri: nft?.thumbnailURI ?? nft?.artifactURI, ofFormat: MediaProxyService.Format.small.rawFormat()) : MediaProxyService.url(fromUri: nft?.displayURI ?? nft?.artifactURI, ofFormat: MediaProxyService.Format.large.rawFormat()) + let url = loadingThumbnailFirst ? MediaProxyService.url(fromUri: nft?.thumbnailURI ?? nft?.artifactURI, ofFormat: MediaProxyService.Format.medium.rawFormat()) : MediaProxyService.url(fromUri: nft?.displayURI ?? nft?.artifactURI, ofFormat: MediaProxyService.Format.large.rawFormat()) let mediaContent = MediaContent(isImage: true, isThumbnail: loadingThumbnailFirst, mediaURL: url, mediaURL2: nil, width: finalSize.width, height: finalSize.height) completion((mediaContent: mediaContent, needsToDownloadFullImage: loadingThumbnailFirst, needsMediaTypeVerification: false)) @@ -452,7 +452,7 @@ class CollectiblesDetailsViewModel: ViewModel, UICollectionViewDiffableDataSourc return } - currentSnapshot.insertItems([newMediaContent], beforeItem: quantityContent) + currentSnapshot.insertItems([newMediaContent], beforeItem: oldMediaContent) currentSnapshot.deleteItems([oldMediaContent]) DispatchQueue.main.async { [weak self] in diff --git a/Kukai Mobile/Modules/Home/AccountsViewModel.swift b/Kukai Mobile/Modules/Home/AccountsViewModel.swift index 7c4e7f69..fcdb2702 100644 --- a/Kukai Mobile/Modules/Home/AccountsViewModel.swift +++ b/Kukai Mobile/Modules/Home/AccountsViewModel.swift @@ -224,15 +224,14 @@ class AccountsViewModel: ViewModel, UITableViewDiffableDataSourceHandler { return } - WalletManagementService.cacheNew(wallet: newChild, forChildOfIndex: hdWalletIndex, markSelected: false) { [weak self] success in - guard success else { + WalletManagementService.cacheNew(wallet: newChild, forChildOfIndex: hdWalletIndex, markSelected: false) { [weak self] errorString in + if let eString = errorString { + vc.hideLoadingView() + vc.windowError(withTitle: "error".localized(), description: eString) + } else { + self?.refresh(animate: true) vc.hideLoadingView() - vc.windowError(withTitle: "error".localized(), description: "error-cant-cache".localized()) - return } - - self?.refresh(animate: true) - vc.hideLoadingView() } }) } diff --git a/Kukai Mobile/Modules/Home/HomeTabBarController.swift b/Kukai Mobile/Modules/Home/HomeTabBarController.swift index 3ee6160a..2bf2c3a7 100644 --- a/Kukai Mobile/Modules/Home/HomeTabBarController.swift +++ b/Kukai Mobile/Modules/Home/HomeTabBarController.swift @@ -464,7 +464,9 @@ extension HomeTabBarController: WalletConnectServiceDelegate { DispatchQueue.main.asyncAfter(wallDeadline: .now() + 3) { [weak self] in WalletConnectService.shared.isConnected { [weak self] connected in if connected { - self?.connectionStatusChanged(status: .connected) + DispatchQueue.main.async { + self?.connectionStatusChanged(status: .connected) + } } } } diff --git a/Kukai Mobile/Modules/Onboarding/CreateWalletViewController.swift b/Kukai Mobile/Modules/Onboarding/CreateWalletViewController.swift index d948ae17..e2444f7b 100644 --- a/Kukai Mobile/Modules/Onboarding/CreateWalletViewController.swift +++ b/Kukai Mobile/Modules/Onboarding/CreateWalletViewController.swift @@ -26,11 +26,21 @@ class CreateWalletViewController: UIViewController { if let wallet = HDWallet(withMnemonicLength: .twentyFour, passphrase: "") { let walletCache = WalletCacheService() - if walletCache.cache(wallet: wallet, childOfIndex: nil, backedUp: false) { + do { + try walletCache.cache(wallet: wallet, childOfIndex: nil, backedUp: false) DependencyManager.shared.walletList = walletCache.readMetadataFromDiskAndDecrypt() DependencyManager.shared.selectedWalletMetadata = DependencyManager.shared.walletList.metadata(forAddress: wallet.address) self.navigate() - } else { + + } catch let error as WalletCacheError { + + if error == WalletCacheError.walletAlreadyExists { + self.windowError(withTitle: "error".localized(), description: "error-wallet-already-exists".localized()) + } else { + self.windowError(withTitle: "error".localized(), description: "error-cant-cache".localized()) + } + + } catch { self.windowError(withTitle: "error".localized(), description: "error-cant-cache".localized()) } } else { diff --git a/Kukai Mobile/Modules/Onboarding/CreateWithSocialViewController.swift b/Kukai Mobile/Modules/Onboarding/CreateWithSocialViewController.swift index cc58c74b..22cf4ffc 100644 --- a/Kukai Mobile/Modules/Onboarding/CreateWithSocialViewController.swift +++ b/Kukai Mobile/Modules/Onboarding/CreateWithSocialViewController.swift @@ -138,13 +138,12 @@ class CreateWithSocialViewController: UIViewController { case .success(let wallet): self.updateLoadingModalStatusLabel(message: "Wallet created, checking for tezos domain registrations") - WalletManagementService.cacheNew(wallet: wallet, forChildOfIndex: nil, markSelected: true) { [weak self] success in - if success { - self?.navigate() - - } else { + WalletManagementService.cacheNew(wallet: wallet, forChildOfIndex: nil, markSelected: true) { [weak self] errorString in + if let eString = errorString { self?.hideLoadingView() - self?.windowError(withTitle: "error".localized(), description: "error-cant-cache".localized()) + self?.windowError(withTitle: "error".localized(), description: eString) + } else { + self?.navigate() } } diff --git a/Kukai Mobile/Modules/Onboarding/ImportWalletViewController.swift b/Kukai Mobile/Modules/Onboarding/ImportWalletViewController.swift index 201cf2a4..5e4c443f 100644 --- a/Kukai Mobile/Modules/Onboarding/ImportWalletViewController.swift +++ b/Kukai Mobile/Modules/Onboarding/ImportWalletViewController.swift @@ -174,35 +174,37 @@ class ImportWalletViewController: UIViewController { if wallet is HDWallet, let hd = wallet as? HDWallet { Task { - if await WalletManagementService.cacheWalletAndScanForAccounts(wallet: hd, progress: { [weak self] found in + let errorString = await WalletManagementService.cacheWalletAndScanForAccounts(wallet: hd, progress: { [weak self] found in if found == 1 { self?.accountScanningVc?.showAllText() } self?.accountScanningVc?.updateFound(found) - }) { + }) + + if let eString = errorString { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in + self?.hideLoadingView() + self?.windowError(withTitle: "error".localized(), description: eString) + } + } else { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in self?.removeScanningVc() self?.navigate() } - - } else { - self.removeScanningVc() - self.windowError(withTitle: "error".localized(), description: "error-cant-cache".localized()) } } } else { accountScanningVc?.hideAllText() - WalletManagementService.cacheNew(wallet: wallet, forChildOfIndex: nil, markSelected: true) { [weak self] success in - if success { + WalletManagementService.cacheNew(wallet: wallet, forChildOfIndex: nil, markSelected: true) { [weak self] errorString in + if let eString = errorString { self?.removeScanningVc() - self?.navigate() - + self?.windowError(withTitle: "error".localized(), description: eString) } else { self?.removeScanningVc() - self?.windowError(withTitle: "error".localized(), description: "error-cant-cache".localized()) + self?.navigate() } } } diff --git a/Kukai Mobile/Modules/Send/SendAbstractConfirmViewController.swift b/Kukai Mobile/Modules/Send/SendAbstractConfirmViewController.swift index 80bf0260..c11b51c3 100644 --- a/Kukai Mobile/Modules/Send/SendAbstractConfirmViewController.swift +++ b/Kukai Mobile/Modules/Send/SendAbstractConfirmViewController.swift @@ -35,29 +35,33 @@ class SendAbstractConfirmViewController: UIViewController { } } - func dismissAndReturn() { + func dismissAndReturn(collapseOnly: Bool) { if !isWalletConnectOp { TransactionService.shared.resetAllState() } self.dismiss(animated: true) - (self.presentingViewController as? UINavigationController)?.popToHome() + + if collapseOnly == false { + (self.presentingViewController as? UINavigationController)?.popToHome() + } } // MARK: - WC2 functions - func handleRejection(andDismiss: Bool = true) { + func handleRejection(andDismiss: Bool = true, collapseOnly: Bool = false) { if !isWalletConnectOp { - if andDismiss { self.dismissAndReturn() } + if andDismiss { self.dismissAndReturn(collapseOnly: collapseOnly) } return } WalletConnectService.rejectCurrentRequest(completion: { [weak self] success, error in self?.hideLoadingModal(completion: { [weak self] in if success { - if andDismiss { self?.dismissAndReturn() } + self?.didSend = true + if andDismiss { self?.dismissAndReturn(collapseOnly: collapseOnly) } } else { var message = "" @@ -69,7 +73,7 @@ class SendAbstractConfirmViewController: UIViewController { Logger.app.error("WC Rejction error: \(error)") self?.windowError(withTitle: "error".localized(), description: message) - self?.dismissAndReturn() + self?.dismissAndReturn(collapseOnly: collapseOnly) } }) }) @@ -77,14 +81,14 @@ class SendAbstractConfirmViewController: UIViewController { func handleApproval(opHash: String) { if !isWalletConnectOp { - self.dismissAndReturn() + self.dismissAndReturn(collapseOnly: false) return } WalletConnectService.approveCurrentRequest(signature: nil, opHash: opHash, completion: { [weak self] success, error in self?.hideLoadingModal(completion: { [weak self] in if success { - self?.dismissAndReturn() + self?.dismissAndReturn(collapseOnly: false) } else { var message = "error-wc2-unrecoverable".localized() @@ -99,7 +103,7 @@ class SendAbstractConfirmViewController: UIViewController { Logger.app.error("WC Approve error: \(error)") self?.windowError(withTitle: "error".localized(), description: message) - self?.dismissAndReturn() + self?.dismissAndReturn(collapseOnly: true) } }) }) diff --git a/Kukai Mobile/Modules/Send/SendCollectibleConfirmViewController.swift b/Kukai Mobile/Modules/Send/SendCollectibleConfirmViewController.swift index e0bd028a..8c6eabab 100644 --- a/Kukai Mobile/Modules/Send/SendCollectibleConfirmViewController.swift +++ b/Kukai Mobile/Modules/Send/SendCollectibleConfirmViewController.swift @@ -184,7 +184,7 @@ class SendCollectibleConfirmViewController: SendAbstractConfirmViewController, S } @IBAction func closeTapped(_ sender: Any) { - handleRejection() + handleRejection(collapseOnly: true) } private func selectedOperationsAndFees() -> [KukaiCoreSwift.Operation] { diff --git a/Kukai Mobile/Modules/Send/SendContractConfirmViewController.swift b/Kukai Mobile/Modules/Send/SendContractConfirmViewController.swift index f40fdb38..2c458f24 100644 --- a/Kukai Mobile/Modules/Send/SendContractConfirmViewController.swift +++ b/Kukai Mobile/Modules/Send/SendContractConfirmViewController.swift @@ -251,7 +251,7 @@ class SendContractConfirmViewController: SendAbstractConfirmViewController, Slid } @IBAction func closeTapped(_ sender: Any) { - handleRejection() + handleRejection(collapseOnly: true) } func addPendingTransaction(opHash: String) { diff --git a/Kukai Mobile/Modules/Send/SendGenericConfirmViewController.swift b/Kukai Mobile/Modules/Send/SendGenericConfirmViewController.swift index a95f40a7..5de6639d 100644 --- a/Kukai Mobile/Modules/Send/SendGenericConfirmViewController.swift +++ b/Kukai Mobile/Modules/Send/SendGenericConfirmViewController.swift @@ -221,7 +221,7 @@ class SendGenericConfirmViewController: SendAbstractConfirmViewController, Slide } @IBAction func closeTapped(_ sender: Any) { - handleRejection() + handleRejection(collapseOnly: true) } @IBAction func copyTapped(_ sender: UIButton) { diff --git a/Kukai Mobile/Modules/Send/SendTokenConfirmViewController.swift b/Kukai Mobile/Modules/Send/SendTokenConfirmViewController.swift index 56e81e7d..2f515d5a 100644 --- a/Kukai Mobile/Modules/Send/SendTokenConfirmViewController.swift +++ b/Kukai Mobile/Modules/Send/SendTokenConfirmViewController.swift @@ -277,7 +277,7 @@ class SendTokenConfirmViewController: SendAbstractConfirmViewController, SlideBu } @IBAction func closeTapped(_ sender: Any) { - handleRejection() + handleRejection(collapseOnly: true) } func addPendingTransaction(opHash: String) { diff --git a/Kukai Mobile/Services/TransactionService.swift b/Kukai Mobile/Services/TransactionService.swift index a16cb49b..51f7a278 100644 --- a/Kukai Mobile/Services/TransactionService.swift +++ b/Kukai Mobile/Services/TransactionService.swift @@ -336,22 +336,26 @@ public class TransactionService { let imageSize = TransactionService.sizeForWalletIcon(walletIconSize: size) let currentNetwork = DependencyManager.shared.currentNetworkType - // Early exit if tezos domain - if metadata.hasDomain(onNetwork: currentNetwork) { - let image = UIImage(named: "Social_TZDomain_Color")?.resizedImage(size: imageSize) ?? UIImage() - return (image: image, title: metadata.primaryDomain(onNetwork: currentNetwork)?.domain.name ?? "", subtitle: metadata.address.truncateTezosAddress()) - } - - // Second Early exit if non-social wallet without domain if metadata.type != .social { let image = UIImage(named: "Social_TZ_1color")?.resizedImage(size: imageSize)?.withTintColor(.colorNamed("BGB4")) ?? UIImage() var title = "" var subtitle: String? = "" if let nickname = metadata.walletNickname { + + // If non social, check for nicknames first title = nickname subtitle = metadata.address.truncateTezosAddress() + + } else if metadata.hasDomain(onNetwork: currentNetwork) { + + // If no nicknames, check for tezos domains + let image = UIImage(named: "Social_TZDomain_Color")?.resizedImage(size: imageSize) ?? UIImage() + return (image: image, title: metadata.primaryDomain(onNetwork: currentNetwork)?.domain.name ?? "", subtitle: metadata.address.truncateTezosAddress()) + } else { + + // Use address title = metadata.address.truncateTezosAddress() subtitle = nil } diff --git a/Kukai Mobile/Services/WalletConnectService.swift b/Kukai Mobile/Services/WalletConnectService.swift index dab0fa0c..155b479e 100644 --- a/Kukai Mobile/Services/WalletConnectService.swift +++ b/Kukai Mobile/Services/WalletConnectService.swift @@ -69,6 +69,8 @@ public class WalletConnectService { icons: ["https://wallet.kukai.app/assets/img/header-logo.svg"], redirect: AppMetadata.Redirect(native: "kukai://", universal: nil)) + private var pairingTimer: Timer? = nil + @Published public var requestDidComplete: Bool = false @Published public var pairsAndSessionsUpdated: Bool = false @@ -155,6 +157,11 @@ public class WalletConnectService { .sink { [weak self] incomingProposalObj in Logger.app.info("WC sessionProposalPublisher") self?.sessionActionRequiredPublisherSubject.send((action: incomingProposalObj.proposal, context: incomingProposalObj.context)) + + self?.delegate?.processingIncomingDone() + self?.pairingTimer?.invalidate() + self?.pairingTimer = nil + }.store(in: &bag) Sign.instance.sessionRequestPublisher @@ -266,6 +273,12 @@ public class WalletConnectService { return Future() { [weak self] promise in Logger.app.info("WC pairing to \(uri.absoluteString)") + self?.delegate?.processingIncomingOperations() + self?.pairingTimer = Timer.scheduledTimer(withTimeInterval: 10, repeats: false, block: { [weak self] timer in + self?.delegateErrorOnMain(message: "No response from application. Please refresh the webpage, and try to connect again", error: nil) + self?.delegate?.processingIncomingDone() + }) + Task { [weak self] in do { try await Pair.instance.pair(uri: uri) @@ -274,6 +287,9 @@ public class WalletConnectService { } catch { Logger.app.error("WC Pairing connect error: \(error)") self?.delegateErrorOnMain(message: "Unable to connect to Pair with dApp, due to: \(error)", error: error) + self?.delegate?.processingIncomingDone() + self?.pairingTimer?.invalidate() + self?.pairingTimer = nil promise(.success(false)) } diff --git a/Kukai Mobile/Services/WalletManagementService.swift b/Kukai Mobile/Services/WalletManagementService.swift index b2a8873e..d42643d5 100644 --- a/Kukai Mobile/Services/WalletManagementService.swift +++ b/Kukai Mobile/Services/WalletManagementService.swift @@ -12,13 +12,13 @@ import Combine class WalletManagementService { /// Cache a new wallet, run tezos domains checks, and update records in DependencyManager correctly - public static func cacheNew(wallet: Wallet, forChildOfIndex: Int?, markSelected: Bool, completion: @escaping ((Bool) -> Void)) { + public static func cacheNew(wallet: Wallet, forChildOfIndex: Int?, markSelected: Bool, completion: @escaping ((String?) -> Void)) { let walletCache = WalletCacheService() - // Only wallets not backed up are brand new wallets created via the HD wallet option - if walletCache.cache(wallet: wallet, childOfIndex: forChildOfIndex, backedUp: true) { - DependencyManager.shared.walletList = walletCache.readMetadataFromDiskAndDecrypt() + do { + try walletCache.cache(wallet: wallet, childOfIndex: forChildOfIndex, backedUp: true) + DependencyManager.shared.walletList = walletCache.readMetadataFromDiskAndDecrypt() if wallet.type == .social, let tWallet = wallet as? TorusWallet { var lookupType: LookupType = .address @@ -28,7 +28,7 @@ class WalletManagementService { case .reddit: lookupType = .reddit - + case .twitter: lookupType = .twitter @@ -67,15 +67,24 @@ class WalletManagementService { } LookupService.shared.cacheRecords() - completion(true) + completion(nil) + return }) - } else { - return completion(false) + } catch let error as WalletCacheError { + + if error == WalletCacheError.walletAlreadyExists { + completion("error-wallet-already-exists".localized()) + } else { + completion("error-cant-cache".localized()) + } + + } catch { + completion("error-cant-cache".localized()) } } - public static func cacheNew(wallet: Wallet, forChildOfIndex: Int?, markSelected: Bool) async -> Bool { + public static func cacheNew(wallet: Wallet, forChildOfIndex: Int?, markSelected: Bool) async -> String? { return await withCheckedContinuation({ continuation in WalletManagementService.cacheNew(wallet: wallet, forChildOfIndex: forChildOfIndex, markSelected: markSelected) { result in continuation.resume(returning: result) @@ -103,31 +112,32 @@ class WalletManagementService { }) } - public static func cacheWalletAndScanForAccounts(wallet: HDWallet, progress: ((Int) -> Void)? = nil) async -> Bool { - guard await WalletManagementService.cacheNew(wallet: wallet, forChildOfIndex: nil, markSelected: true), - let hdIndex = DependencyManager.shared.walletList.hdWallets.firstIndex(where: { $0.address == wallet.address }) else { - return false - } - - var childIndex = 1 - var isUsedAccount = true - - while isUsedAccount { - guard let child = wallet.createChild(accountIndex: childIndex) else { - isUsedAccount = false - continue - } + public static func cacheWalletAndScanForAccounts(wallet: HDWallet, progress: ((Int) -> Void)? = nil) async -> String? { + if let errorString = await WalletManagementService.cacheNew(wallet: wallet, forChildOfIndex: nil, markSelected: true) { + return errorString - if await WalletManagementService.isUsedAccount(address: child.address) { - progress?(childIndex) - let _ = await WalletManagementService.cacheNew(wallet: child, forChildOfIndex: hdIndex, markSelected: false) - } else { - isUsedAccount = false + } else { + let hdIndex = DependencyManager.shared.walletList.hdWallets.firstIndex(where: { $0.address == wallet.address }) + var childIndex = 1 + var isUsedAccount = true + + while isUsedAccount { + guard let child = wallet.createChild(accountIndex: childIndex) else { + isUsedAccount = false + continue + } + + if await WalletManagementService.isUsedAccount(address: child.address) { + progress?(childIndex) + let _ = await WalletManagementService.cacheNew(wallet: child, forChildOfIndex: hdIndex, markSelected: false) + } else { + isUsedAccount = false + } + + childIndex += 1 } - childIndex += 1 + return nil } - - return true } }