diff --git a/Tokenary macOS/Agent.swift b/Tokenary macOS/Agent.swift index 36655b69..dbe3e22d 100644 --- a/Tokenary macOS/Agent.swift +++ b/Tokenary macOS/Agent.swift @@ -54,7 +54,7 @@ class Agent: NSObject { self?.hasPassword = true self?.showInitialScreen(externalRequest: externalRequest) } - let windowController = Window.showNew() + let windowController = Window.showNew(closeOthers: true) windowController.contentViewController = welcomeViewController return } @@ -76,22 +76,22 @@ class Agent: NSObject { if case let .safari(request) = request { processSafariRequest(request) } else { - let windowController = Window.showNew() let accountsList = instantiate(AccountsListViewController.self) if case let .wcSession(session) = request { accountsList.onSelectedWallet = onSelectedWallet(session: session) } + let windowController = Window.showNew(closeOthers: accountsList.onSelectedWallet == nil) windowController.contentViewController = accountsList } } - func showApprove(transaction: Transaction, chain: EthereumChain, peerMeta: PeerMeta?, completion: @escaping (Transaction?) -> Void) { - let windowController = Window.showNew() - let approveViewController = ApproveTransactionViewController.with(transaction: transaction, chain: chain, peerMeta: peerMeta) { [weak self] transaction in + func showApprove(windowController: NSWindowController, transaction: Transaction, chain: EthereumChain, peerMeta: PeerMeta?, completion: @escaping (Transaction?) -> Void) { + let window = windowController.window + let approveViewController = ApproveTransactionViewController.with(transaction: transaction, chain: chain, peerMeta: peerMeta) { [weak self, weak window] transaction in if transaction != nil { - self?.askAuthentication(on: windowController.window, onStart: false, reason: .sendTransaction) { success in + self?.askAuthentication(on: window, onStart: false, reason: .sendTransaction) { success in completion(success ? transaction : nil) } } else { @@ -101,13 +101,13 @@ class Agent: NSObject { windowController.contentViewController = approveViewController } - func showApprove(subject: ApprovalSubject, meta: String, peerMeta: PeerMeta?, completion: @escaping (Bool) -> Void) { - let windowController = Window.showNew() - let approveViewController = ApproveViewController.with(subject: subject, meta: meta, peerMeta: peerMeta) { [weak self] result in + func showApprove(windowController: NSWindowController, subject: ApprovalSubject, meta: String, peerMeta: PeerMeta?, completion: @escaping (Bool) -> Void) { + let window = windowController.window + let approveViewController = ApproveViewController.with(subject: subject, meta: meta, peerMeta: peerMeta) { [weak self, weak window] result in if result { - self?.askAuthentication(on: windowController.window, onStart: false, reason: subject.asAuthenticationReason) { success in + self?.askAuthentication(on: window, getBackTo: window?.contentViewController, onStart: false, reason: subject.asAuthenticationReason) { success in completion(success) - (windowController.contentViewController as? ApproveViewController)?.enableWaiting() + (window?.contentViewController as? ApproveViewController)?.enableWaiting() } } else { completion(result) @@ -116,11 +116,6 @@ class Agent: NSObject { windowController.contentViewController = approveViewController } - func showErrorMessage(_ message: String) { - let windowController = Window.showNew() - windowController.contentViewController = ErrorViewController.withMessage(message) - } - func getWalletSelectionCompletionIfShouldSelect() -> ((EthereumChain?, TokenaryWallet?, Account?) -> Void)? { let session = getSessionFromPasteboard() return onSelectedWallet(session: session) @@ -221,7 +216,7 @@ class Agent: NSObject { guard let session = session else { return nil } return { [weak self] chain, wallet, account in guard let chain = chain, let wallet = wallet, account?.coin == .ethereum else { - Window.closeAllAndActivateBrowser(force: nil) + Window.closeAllAndActivateBrowser(specific: nil) return } self?.connectWallet(session: session, chainId: chain.id, wallet: wallet) @@ -257,12 +252,12 @@ class Agent: NSObject { let canDoLocalAuthentication = context.canEvaluatePolicy(policy, error: &error) func showPasswordScreen() { - let window = on ?? Window.showNew().window + let window = on ?? Window.showNew(closeOthers: onStart).window let passwordViewController = PasswordViewController.with(mode: .enter, reason: reason) { [weak window] success in if let getBackTo = getBackTo { window?.contentViewController = getBackTo } else { - Window.closeAll() + Window.closeWindowAndActivateNext(idToClose: window?.windowNumber, specificBrowser: nil) } completion(success) } @@ -287,36 +282,50 @@ class Agent: NSObject { } private func connectWallet(session: WCSession, chainId: Int, wallet: TokenaryWallet) { - let windowController = Window.showNew() + let windowController = Window.showNew(closeOthers: true) let window = windowController.window windowController.contentViewController = WaitingViewController.withReason(Strings.connecting) walletConnect.connect(session: session, chainId: chainId, walletId: wallet.id) { [weak window] _ in if window?.isVisible == true { - Window.closeAllAndActivateBrowser(force: nil) + Window.closeAllAndActivateBrowser(specific: nil) } } } - + private func processSafariRequest(_ safariRequest: SafariRequest) { + var windowNumber: Int? let action = DappRequestProcessor.processSafariRequest(safariRequest) { - Window.closeAllAndActivateBrowser(force: .safari) + Window.closeWindowAndActivateNext(idToClose: windowNumber, specificBrowser: .safari) } switch action { case .none: break - case .selectAccount(let action), .switchAccount(let action): - let windowController = Window.showNew() + case .selectAccount(let accountAction), .switchAccount(let accountAction): + let closeOtherWindows: Bool + if case .selectAccount = action { + closeOtherWindows = false + } else { + closeOtherWindows = true + } + + let windowController = Window.showNew(closeOthers: closeOtherWindows) + windowNumber = windowController.window?.windowNumber let accountsList = instantiate(AccountsListViewController.self) - accountsList.onSelectedWallet = action.completion + accountsList.onSelectedWallet = accountAction.completion windowController.contentViewController = accountsList case .approveMessage(let action): - showApprove(subject: action.subject, meta: action.meta, peerMeta: action.peerMeta, completion: action.completion) + let windowController = Window.showNew(closeOthers: false) + windowNumber = windowController.window?.windowNumber + showApprove(windowController: windowController, subject: action.subject, meta: action.meta, peerMeta: action.peerMeta, completion: action.completion) case .approveTransaction(let action): - showApprove(transaction: action.transaction, chain: action.chain, peerMeta: action.peerMeta, completion: action.completion) + let windowController = Window.showNew(closeOthers: false) + windowNumber = windowController.window?.windowNumber + showApprove(windowController: windowController, transaction: action.transaction, chain: action.chain, peerMeta: action.peerMeta, completion: action.completion) case .justShowApp: - let windowController = Window.showNew() + let windowController = Window.showNew(closeOthers: true) + windowNumber = windowController.window?.windowNumber let accountsList = instantiate(AccountsListViewController.self) windowController.contentViewController = accountsList } diff --git a/Tokenary macOS/Base.lproj/Main.storyboard b/Tokenary macOS/Base.lproj/Main.storyboard index 14f5861b..267813ef 100644 --- a/Tokenary macOS/Base.lproj/Main.storyboard +++ b/Tokenary macOS/Base.lproj/Main.storyboard @@ -679,7 +679,7 @@ - + @@ -1218,75 +1218,6 @@ DQ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1671,7 +1602,7 @@ DQ - + diff --git a/Tokenary macOS/Screens/ErrorViewController.swift b/Tokenary macOS/Screens/ErrorViewController.swift deleted file mode 100644 index 0dd7a33b..00000000 --- a/Tokenary macOS/Screens/ErrorViewController.swift +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright © 2021 Tokenary. All rights reserved. - -import Cocoa - -class ErrorViewController: NSViewController { - - static func withMessage(_ message: String) -> ErrorViewController { - let new = instantiate(ErrorViewController.self) - new.message = message - return new - } - - private var message = "" - - @IBOutlet weak var titleLabel: NSTextField! - @IBOutlet weak var messageLabel: NSTextField! - - override func viewDidLoad() { - super.viewDidLoad() - messageLabel.stringValue = message - } - - @IBAction func actionButtonTapped(_ sender: Any) { - Window.closeAllAndActivateBrowser(force: nil) - } - -} diff --git a/Tokenary macOS/Screens/PasswordViewController.swift b/Tokenary macOS/Screens/PasswordViewController.swift index 07a70938..aa83075b 100644 --- a/Tokenary macOS/Screens/PasswordViewController.swift +++ b/Tokenary macOS/Screens/PasswordViewController.swift @@ -21,6 +21,7 @@ class PasswordViewController: NSViewController { private var reason: AuthenticationReason? private var passwordToRepeat: String? private var completion: ((Bool) -> Void)? + private var didCallCompletion = false @IBOutlet weak var reasonLabel: NSTextField! @IBOutlet weak var cancelButton: NSButton! @@ -43,6 +44,11 @@ class PasswordViewController: NSViewController { } } + override func viewDidAppear() { + super.viewDidAppear() + view.window?.delegate = self + } + func switchToMode(_ mode: Mode) { self.mode = mode switch mode { @@ -67,11 +73,11 @@ class PasswordViewController: NSViewController { let repeated = passwordTextField.stringValue if repeated == passwordToRepeat { keychain.save(password: repeated) - completion?(true) + callCompletion(result: true) } case .enter: if keychain.password == passwordTextField.stringValue { - completion?(true) + callCompletion(result: true) } } } @@ -83,7 +89,14 @@ class PasswordViewController: NSViewController { case .repeatAfterCreate: switchToMode(.create) case .enter: - completion?(false) + callCompletion(result: false) + } + } + + private func callCompletion(result: Bool) { + if !didCallCompletion { + didCallCompletion = true + completion?(result) } } @@ -96,3 +109,11 @@ extension PasswordViewController: NSTextFieldDelegate { } } + +extension PasswordViewController: NSWindowDelegate { + + func windowWillClose(_ notification: Notification) { + callCompletion(result: false) + } + +} diff --git a/Tokenary macOS/Screens/WaitingViewController.swift b/Tokenary macOS/Screens/WaitingViewController.swift index 1508638b..b675d6b4 100644 --- a/Tokenary macOS/Screens/WaitingViewController.swift +++ b/Tokenary macOS/Screens/WaitingViewController.swift @@ -22,7 +22,7 @@ class WaitingViewController: NSViewController { } @IBAction func actionButtonTapped(_ sender: Any) { - Window.closeAll() + Window.closeWindowAndActivateNext(idToClose: view.window?.windowNumber, specificBrowser: nil) } } diff --git a/Tokenary macOS/WalletConnect.swift b/Tokenary macOS/WalletConnect.swift index d389703c..1ff34800 100644 --- a/Tokenary macOS/WalletConnect.swift +++ b/Tokenary macOS/WalletConnect.swift @@ -133,12 +133,14 @@ class WalletConnect { let peer = PeerMeta(wcPeerMeta: getPeerOfInteractor(interactor)) let transaction = Transaction(from: wct.from, to: to, nonce: wct.nonce, gasPrice: wct.gasPrice, gas: wct.gas, value: wct.value, data: wct.data) - agent.showApprove(transaction: transaction, chain: chain, peerMeta: peer) { [weak self, weak interactor] transaction in + let windowController = Window.showNew(closeOthers: false) + let windowNumber = windowController.window?.windowNumber + agent.showApprove(windowController: windowController, transaction: transaction, chain: chain, peerMeta: peer) { [weak self, weak interactor] transaction in if let transaction = transaction { self?.sendTransaction(transaction, walletId: walletId, chainId: chainId, requestId: id, interactor: interactor) - Window.closeAllAndActivateBrowser(force: nil) + Window.closeWindowAndActivateNext(idToClose: windowNumber, specificBrowser: nil) } else { - Window.closeAllAndActivateBrowser(force: nil) + Window.closeWindowAndActivateNext(idToClose: windowNumber, specificBrowser: nil) self?.rejectRequest(id: id, interactor: interactor, message: Strings.canceled) } } @@ -162,12 +164,14 @@ class WalletConnect { } let peer = PeerMeta(wcPeerMeta: getPeerOfInteractor(interactor)) - agent.showApprove(subject: approvalSubject, meta: message ?? "", peerMeta: peer) { [weak self, weak interactor] approved in + let windowController = Window.showNew(closeOthers: false) + let windowNumber = windowController.window?.windowNumber + agent.showApprove(windowController: windowController, subject: approvalSubject, meta: message ?? "", peerMeta: peer) { [weak self, weak interactor] approved in if approved { self?.sign(id: id, payload: payload, walletId: walletId, interactor: interactor) - Window.closeAllAndActivateBrowser(force: nil) + Window.closeWindowAndActivateNext(idToClose: windowNumber, specificBrowser: nil) } else { - Window.closeAllAndActivateBrowser(force: nil) + Window.closeWindowAndActivateNext(idToClose: windowNumber, specificBrowser: nil) self?.rejectRequest(id: id, interactor: interactor, message: Strings.canceled) } } diff --git a/Tokenary macOS/Window.swift b/Tokenary macOS/Window.swift index 5494fdbd..72eddaeb 100644 --- a/Tokenary macOS/Window.swift +++ b/Tokenary macOS/Window.swift @@ -4,8 +4,10 @@ import Cocoa struct Window { - static func showNew() -> NSWindowController { - closeAll() + static func showNew(closeOthers: Bool) -> NSWindowController { + if closeOthers { + closeAll() + } let windowController = new activate(windowController) return windowController @@ -21,19 +23,31 @@ struct Window { window?.makeKeyAndOrderFront(nil) } - static func closeAllAndActivateBrowser(force browser: Browser?) { - closeAll() - activateBrowser(force: browser) - } - - static func closeAll(updateStatusBarItem: Bool = true) { - NSApplication.shared.windows.forEach { $0.close() } - if updateStatusBarItem { - Agent.shared.setupStatusBarItem() + static func closeWindowAndActivateNext(idToClose: Int?, specificBrowser: Browser?) { + if let id = idToClose, let windowToClose = NSApplication.shared.windows.first(where: { $0.windowNumber == id }) { + windowToClose.close() + } + + if let window = NSApplication.shared.windows.last(where: { $0.windowNumber != idToClose && $0.isOnActiveSpace && $0.contentViewController != nil }) { + activateWindow(window) + } else { + activateBrowser(specific: specificBrowser) } } - static func activateBrowser(force browser: Browser?) { + static func closeAllAndActivateBrowser(specific browser: Browser?) { + closeAll() + activateBrowser(specific: browser) + } + + // MARK: - Private + + private static func closeAll() { + NSApplication.shared.windows.forEach { $0.close() } + Agent.shared.setupStatusBarItem() + } + + private static func activateBrowser(specific browser: Browser?) { if let browser = browser { activateBrowser(browser) return @@ -64,11 +78,7 @@ struct Window { NSWorkspace.shared.runningApplications.first(where: { $0.bundleIdentifier == browser.rawValue })?.activate(options: .activateIgnoringOtherApps) } - static var current: NSWindowController? { - return NSApplication.shared.windows.first?.windowController - } - - static var new: NSWindowController { + private static var new: NSWindowController { return NSStoryboard.main.instantiateController(withIdentifier: "initial") as! NSWindowController } diff --git a/Tokenary.xcodeproj/project.pbxproj b/Tokenary.xcodeproj/project.pbxproj index 8330c6e1..b6787b7d 100644 --- a/Tokenary.xcodeproj/project.pbxproj +++ b/Tokenary.xcodeproj/project.pbxproj @@ -123,7 +123,6 @@ 2C8A09D726751A0C00993638 /* WalletConnect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8A09D626751A0C00993638 /* WalletConnect.swift */; }; 2C8A09DF267579EA00993638 /* AccountsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8A09DE267579EA00993638 /* AccountsListViewController.swift */; }; 2C8A09E326757FC000993638 /* AccountCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8A09E226757FC000993638 /* AccountCellView.swift */; }; - 2C8A09E82675960D00993638 /* ErrorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8A09E72675960D00993638 /* ErrorViewController.swift */; }; 2C8A09EB2675964700993638 /* ApproveViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8A09EA2675964700993638 /* ApproveViewController.swift */; }; 2C8A09EE2675965F00993638 /* WaitingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8A09ED2675965F00993638 /* WaitingViewController.swift */; }; 2C8E47A326A322E8007B8354 /* RightClickTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8E47A226A322E8007B8354 /* RightClickTableView.swift */; }; @@ -352,7 +351,6 @@ 2C8A09D626751A0C00993638 /* WalletConnect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletConnect.swift; sourceTree = ""; }; 2C8A09DE267579EA00993638 /* AccountsListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsListViewController.swift; sourceTree = ""; }; 2C8A09E226757FC000993638 /* AccountCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountCellView.swift; sourceTree = ""; }; - 2C8A09E72675960D00993638 /* ErrorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorViewController.swift; sourceTree = ""; }; 2C8A09EA2675964700993638 /* ApproveViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApproveViewController.swift; sourceTree = ""; }; 2C8A09ED2675965F00993638 /* WaitingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitingViewController.swift; sourceTree = ""; }; 2C8E47A226A322E8007B8354 /* RightClickTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RightClickTableView.swift; sourceTree = ""; }; @@ -678,7 +676,6 @@ 2C1995412674C4B900A8E370 /* ImportViewController.swift */, 2C797E7D267BB88800F2CE2D /* WelcomeViewController.swift */, 2CDAB3712675B3F0009F8B97 /* PasswordViewController.swift */, - 2C8A09E72675960D00993638 /* ErrorViewController.swift */, 2C901C462689E6D400D0926A /* ApproveTransactionViewController.swift */, 2C8A09EA2675964700993638 /* ApproveViewController.swift */, 2C8A09ED2675965F00993638 /* WaitingViewController.swift */, @@ -1380,7 +1377,6 @@ 2C4768A9282598C5005E8D4D /* CoinDerivationCellView.swift in Sources */, 2CB4031D281D745D00BAEBEE /* NSTableView.swift in Sources */, 2CD0B3F726AC619900488D92 /* AddAccountOptionCellView.swift in Sources */, - 2C8A09E82675960D00993638 /* ErrorViewController.swift in Sources */, 0D059AD226C2796200EE3023 /* ApprovalSubject.swift in Sources */, 2C264BCB27B2F2FF00234393 /* TezosSafariRequest.swift in Sources */, 2C1995422674C4B900A8E370 /* ImportViewController.swift in Sources */,