Merge pull request #69 from zeriontech/feature/safari-requests-queue

Safari requests queue
This commit is contained in:
Ivan Grachev 2022-08-01 17:48:41 +03:00 committed by GitHub
commit 9970ad1977
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 162 additions and 197 deletions

View File

@ -17,7 +17,6 @@ extension SafariRequest {
case watchAsset
case addEthereumChain
case switchEthereumChain
case switchAccount
}
let method: Method
@ -53,7 +52,7 @@ extension SafariRequest {
var responseUpdatesStoredConfiguration: Bool {
switch method {
case .switchEthereumChain, .addEthereumChain, .requestAccounts, .switchAccount:
case .switchEthereumChain, .addEthereumChain, .requestAccounts:
return true
case .ecRecover, .signMessage, .signPersonalMessage, .signTransaction, .signTypedMessage, .watchAsset:
return false

View File

@ -4,8 +4,7 @@ browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.subject === "message-to-wallet") {
browser.runtime.sendNativeMessage("mac.tokenary.io", request.message, function(response) {
sendResponse(response);
browser.tabs.update(sender.tab.id, { active: true });
didCompleteRequest(request.message.id);
didCompleteRequest(request.message.id, sender.tab.id);
storeConfigurationIfNeeded(request.host, response);
});
} else if (request.subject === "getResponse") {

File diff suppressed because one or more lines are too long

View File

@ -105,7 +105,6 @@ class TokenaryEthereum extends EventEmitter {
}
enable() {
console.log('enable() is deprecated, please use window.ethereum.request({method: "eth_requestAccounts"}) instead.');
if (!window.ethereum.address) { // avoid double accounts request in uniswap
return this.request({ method: "eth_requestAccounts", params: [] });
} else {
@ -154,7 +153,7 @@ class TokenaryEthereum extends EventEmitter {
}
_request(payload, wrapResult = true) {
this.idMapping.tryIntifyId(payload);
this.idMapping.tryFixId(payload);
return new Promise((resolve, reject) => {
if (!payload.id) {
payload.id = Utils.genId();

View File

@ -6,26 +6,21 @@
import Utils from "./utils";
class IdMapping {
constructor() {
this.intIds = new Map;
}
tryIntifyId(payload) {
tryFixId(payload) {
if (!payload.id) {
payload.id = Utils.genId();
return;
}
if (typeof payload.id !== "number") {
this.intIds.set(payload.id, payload.id);
} else if (typeof payload.id !== "number" || this.intIds.has(payload.id) ) {
let newId = Utils.genId();
this.intIds.set(newId, payload.id);
payload.id = newId;
}
}
tryRestoreId(payload) {
let id = this.tryPopId(payload.id);
if (id) {
payload.id = id;
} else {
this.intIds.set(payload.id, payload.id);
}
}

View File

@ -23,6 +23,7 @@ window.dispatchEvent(new Event('ethereum#initialized'));
// - MARK: Solana
window.solana = new TokenarySolana();
window.tokenarySolana = window.solana;
window.phantom = {solana: window.solana};
window.dispatchEvent(new Event("solana#initialized"));

View File

@ -57,6 +57,7 @@ class TokenaryNear extends EventEmitter {
}
request(payload) {
this.idMapping.tryFixId(payload);
return new Promise((resolve, reject) => {
if (!payload.id) {
payload.id = Utils.genId();

View File

@ -59,8 +59,12 @@ class TokenarySolana extends EventEmitter {
this.pendingPayloads = [];
}
connect() {
return this.request({method: "connect"});
connect(params) {
var payload = {method: "connect"};
if (typeof params !== "undefined") {
payload.params = params;
}
return this.request(payload);
}
disconnect() {
@ -104,7 +108,7 @@ class TokenarySolana extends EventEmitter {
}
request(payload) {
this.idMapping.tryIntifyId(payload);
this.idMapping.tryFixId(payload);
return new Promise((resolve, reject) => {
if (!payload.id) {
payload.id = Utils.genId();
@ -143,7 +147,12 @@ class TokenarySolana extends EventEmitter {
switch (payload.method) {
case "connect":
if (!this.publicKey) {
return this.postMessage("connect", payload.id, {});
if ("params" in payload && "onlyIfTrusted" in payload.params && payload.params.onlyIfTrusted) {
this.sendError(payload.id, "Click a button to connect");
return;
} else {
return this.postMessage("connect", payload.id, {});
}
} else {
this.isConnected = true;
this.emitConnect(this.publicKey);

View File

@ -1,6 +1,7 @@
// Copyright © 2022 Tokenary. All rights reserved.
function didCompleteRequest(id) {
function didCompleteRequest(id, tabId) {
browser.tabs.update(tabId, { active: true });
const request = {id: id, subject: "didCompleteRequest"};
browser.runtime.sendNativeMessage("mac.tokenary.io", request);
}

View File

@ -1,5 +1,5 @@
// Copyright © 2022 Tokenary. All rights reserved.
function didCompleteRequest(id) {
function didCompleteRequest(id, tabId) {
}

View File

@ -11,6 +11,8 @@ enum EthereumChain: Int {
case avalanche = 43114
case gnosisChain = 100
case fantomOpera = 250
case celo = 42220
case aurora = 1313161554
// Testnets
case arbitrumRinkeby = 421611
@ -34,7 +36,7 @@ enum EthereumChain: Int {
return "0x" + String(id, radix: 16, uppercase: false)
}
static let allMainnets: [EthereumChain] = [.ethereum, .polygon, .optimism, .binance, .arbitrum, .avalanche, .gnosisChain, .fantomOpera]
static let allMainnets: [EthereumChain] = [.ethereum, .polygon, .optimism, .binance, .arbitrum, .avalanche, .gnosisChain, .fantomOpera, .celo, .aurora]
static let allTestnets: [EthereumChain] = [.ethereumRopsten, .ethereumKovan, .ethereumRinkeby, .ethereumGoerli, .optimisticKovan, .arbitrumKovan, .arbitrumRinkeby, .polygonMumbai, .binanceTestnet, .avalancheFuji, .fantomTestnet, .neonDevnet]
var name: String {
@ -47,6 +49,8 @@ enum EthereumChain: Int {
case .avalanche: return "Avalanche"
case .gnosisChain: return "Gnosis Chain"
case .fantomOpera: return "Fantom Opera"
case .celo: return "Celo"
case .aurora: return "Aurora"
case .arbitrumRinkeby: return "Arbitrum Rinkeby"
case .optimisticKovan: return "Optimistic Kovan"
@ -69,7 +73,7 @@ enum EthereumChain: Int {
return "BNB"
case .polygon, .polygonMumbai:
return "MATIC"
case .arbitrum, .arbitrumKovan, .arbitrumRinkeby, .ethereum, .ethereumGoerli, .ethereumKovan, .ethereumRinkeby, .optimism, .optimisticKovan, .ethereumRopsten:
case .arbitrum, .arbitrumKovan, .arbitrumRinkeby, .ethereum, .ethereumGoerli, .ethereumKovan, .ethereumRinkeby, .optimism, .optimisticKovan, .ethereumRopsten, .aurora:
return "ETH"
case .avalanche, .avalancheFuji:
return "AVAX"
@ -77,6 +81,8 @@ enum EthereumChain: Int {
return "xDai"
case .fantomOpera, .fantomTestnet:
return "FTM"
case .celo:
return "CELO"
case .neonDevnet:
return "NEON"
}
@ -105,6 +111,8 @@ enum EthereumChain: Int {
case .avalanche: return "https://api.avax.network/ext/bc/C/rpc"
case .gnosisChain: return "https://rpc.gnosischain.com/"
case .fantomOpera: return "https://rpc.ftm.tools/"
case .celo: return "https://rpc.ankr.com/celo"
case .aurora: return "https://mainnet.aurora.dev"
case .arbitrumRinkeby: return "https://rinkeby.arbitrum.io/rpc"
case .arbitrumKovan: return "https://kovan5.arbitrum.io/rpc"

View File

@ -6,6 +6,7 @@ import WalletCore
enum DappRequestAction {
case none
case justShowApp
case switchAccount(SelectAccountAction)
case selectAccount(SelectAccountAction)
case approveMessage(SignMessageAction)
case approveTransaction(SendTransactionAction)

View File

@ -51,7 +51,7 @@ struct DappRequestProcessor {
respond(to: request, error: Strings.canceled, completion: completion)
}
}
return .selectAccount(action)
return .switchAccount(action)
}
}
}
@ -193,7 +193,7 @@ struct DappRequestProcessor {
lazy var account = getAccount(coin: .ethereum, address: ethereumRequest.address)
switch ethereumRequest.method {
case .switchAccount, .requestAccounts:
case .requestAccounts:
let action = SelectAccountAction(provider: .ethereum) { chain, wallet, account in
if let chain = chain, let address = wallet?.ethereumAddress, account?.coin == .ethereum {
let responseBody = ResponseToExtension.Ethereum(results: [address], chainId: chain.hexStringId, rpcURL: chain.nodeURLString)

View File

@ -151,7 +151,7 @@ class AccountsListViewController: UIViewController, DataStateContainer {
switch action {
case .none, .justShowApp:
break
case .selectAccount(let action):
case .selectAccount(let action), .switchAccount(let action):
let selectAccountViewController = instantiate(AccountsListViewController.self, from: .main)
selectAccountViewController.onSelectedWallet = action.completion
presentForSafariRequest(selectAccountViewController.inNavigationController, id: request.id)

View File

@ -54,13 +54,13 @@ class Agent: NSObject {
self?.hasPassword = true
self?.showInitialScreen(externalRequest: externalRequest)
}
let windowController = Window.showNew()
let windowController = Window.showNew(closeOthers: true)
windowController.contentViewController = welcomeViewController
return
}
guard didEnterPasswordOnStart else {
askAuthentication(on: nil, onStart: true, reason: .start) { [weak self] success in
askAuthentication(on: nil, browser: nil, onStart: true, reason: .start) { [weak self] success in
if success {
self?.didEnterPasswordOnStart = true
self?.showInitialScreen(externalRequest: externalRequest)
@ -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, browser: Browser?, 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, browser: browser, 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, browser: Browser?, 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, browser: browser, 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)
@ -250,19 +245,21 @@ class Agent: NSObject {
showInitialScreen(externalRequest: request)
}
func askAuthentication(on: NSWindow?, getBackTo: NSViewController? = nil, onStart: Bool, reason: AuthenticationReason, completion: @escaping (Bool) -> Void) {
func askAuthentication(on: NSWindow?, getBackTo: NSViewController? = nil, browser: Browser?, onStart: Bool, reason: AuthenticationReason, completion: @escaping (Bool) -> Void) {
let context = LAContext()
var error: NSError?
let policy = LAPolicy.deviceOwnerAuthenticationWithBiometrics
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 if let browser = browser {
Window.closeWindowAndActivateNext(idToClose: window?.windowNumber, specificBrowser: browser)
} else {
Window.closeAll()
Window.closeWindow(idToClose: window?.windowNumber)
}
completion(success)
}
@ -287,36 +284,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):
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, browser: .safari, 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, browser: .safari, 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
}

View File

@ -679,7 +679,7 @@
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="173" y="-44"/>
<point key="canvasLocation" x="158" y="-43"/>
</scene>
<!--Window Controller-->
<scene sceneID="R2V-B0-nI4">
@ -1218,75 +1218,6 @@ DQ
</objects>
<point key="canvasLocation" x="75" y="1188"/>
</scene>
<!--Error View Controller-->
<scene sceneID="LSi-74-yAS">
<objects>
<viewController storyboardIdentifier="ErrorViewController" id="Os3-h2-6sc" customClass="ErrorViewController" customModule="Tokenary" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="cFH-jd-zLP">
<rect key="frame" x="0.0" y="0.0" width="250" height="350"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="T7q-cN-jjc">
<rect key="frame" x="14" y="292" width="222" height="34"/>
<textFieldCell key="cell" controlSize="large" alignment="center" title="Error" id="t7e-cO-oeN">
<font key="font" metaFont="systemHeavy" size="29"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<stackView distribution="fill" orientation="horizontal" alignment="top" spacing="12" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="owz-dg-x2C">
<rect key="frame" x="106" y="32" width="39" height="28"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="bRK-Tb-oKu">
<rect key="frame" x="-6" y="-6" width="51" height="40"/>
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" controlSize="large" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="xDk-iz-zcw">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
<connections>
<action selector="actionButtonTapped:" target="Os3-h2-6sc" id="1PU-4O-ljb"/>
</connections>
</buttonCell>
</button>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="M1Q-Iu-4dD">
<rect key="frame" x="34" y="175" width="182" height="25"/>
<textFieldCell key="cell" controlSize="large" selectable="YES" alignment="center" title="Failed to connect" id="qy3-O0-p56">
<font key="font" metaFont="systemBold" size="21"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="owz-dg-x2C" firstAttribute="centerX" secondItem="cFH-jd-zLP" secondAttribute="centerX" id="0OK-ZZ-1lf"/>
<constraint firstItem="T7q-cN-jjc" firstAttribute="leading" secondItem="cFH-jd-zLP" secondAttribute="leading" constant="16" id="0mu-SM-Kvn"/>
<constraint firstAttribute="trailing" secondItem="T7q-cN-jjc" secondAttribute="trailing" constant="16" id="AOT-En-wYh"/>
<constraint firstItem="M1Q-Iu-4dD" firstAttribute="trailing" secondItem="T7q-cN-jjc" secondAttribute="trailing" constant="-20" id="Gbp-eZ-HHz"/>
<constraint firstAttribute="bottom" secondItem="owz-dg-x2C" secondAttribute="bottom" constant="32" id="O7Z-WD-F1G"/>
<constraint firstItem="M1Q-Iu-4dD" firstAttribute="centerY" secondItem="cFH-jd-zLP" secondAttribute="centerY" constant="-12" id="TQG-iy-bgb"/>
<constraint firstItem="M1Q-Iu-4dD" firstAttribute="leading" secondItem="T7q-cN-jjc" secondAttribute="leading" constant="20" id="XO8-WA-SOb"/>
<constraint firstItem="T7q-cN-jjc" firstAttribute="top" secondItem="cFH-jd-zLP" secondAttribute="top" constant="24" id="qQl-EG-gwj"/>
</constraints>
</view>
<connections>
<outlet property="messageLabel" destination="M1Q-Iu-4dD" id="s4z-1r-zIH"/>
<outlet property="titleLabel" destination="T7q-cN-jjc" id="nXO-3X-Qbu"/>
</connections>
</viewController>
<customObject id="gpa-4h-EjA" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="380" y="1177"/>
</scene>
<!--Password View Controller-->
<scene sceneID="Riu-mP-tQs">
<objects>
@ -1671,7 +1602,7 @@ DQ
</viewController>
<customObject id="E6z-rT-Kks" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-219" y="1197"/>
<point key="canvasLocation" x="380" y="1197"/>
</scene>
<!--Edit Accounts View Controller-->
<scene sceneID="PtW-Bw-c7x">

View File

@ -12,6 +12,7 @@ enum Browser: String, CaseIterable {
case firefox = "org.mozilla.firefox"
case vivaldi = "com.vivaldi.Vivaldi"
case yandex = "ru.yandex.desktop.yandex-browser"
case unknown = "com.unknown.browser.stub"
static let allBundleIds = Set(Browser.allCases.map { $0.rawValue })
}

View File

@ -67,6 +67,7 @@ class AccountsListViewController: NSViewController {
reloadHeader()
updateCellModels()
NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: NSApplication.didBecomeActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(walletsChanged), name: Notification.Name.walletsChanged, object: nil)
}
override func viewDidAppear() {
@ -120,6 +121,8 @@ class AccountsListViewController: NSViewController {
menu.addItem(.separator())
menu.addItem(submenuItem)
networkButton.menu = menu
} else if !canSelectAccount, !networkButton.isHidden {
networkButton.isHidden = true
}
}
@ -178,9 +181,6 @@ class AccountsListViewController: NSViewController {
private func createNewAccountAndShowSecretWords() {
guard let wallet = try? walletsManager.createWallet() else { return }
newWalletId = wallet.id
reloadHeader()
updateCellModels()
tableView.reloadData()
blinkNewWalletCellIfNeeded()
showKey(wallet: wallet)
}
@ -280,8 +280,6 @@ class AccountsListViewController: NSViewController {
do {
try walletsManager.update(wallet: wallet, removeAccounts: [account])
updateCellModels()
tableView.removeRows(at: [row], withAnimation: .slideUp)
} catch {
Alert.showWithMessage(Strings.somethingWentWrong, style: .informational)
}
@ -306,7 +304,7 @@ class AccountsListViewController: NSViewController {
alert.addButton(withTitle: Strings.removeAnyway)
alert.addButton(withTitle: Strings.cancel)
if alert.runModal() == .alertFirstButtonReturn {
agent.askAuthentication(on: view.window, getBackTo: self, onStart: false, reason: .removeWallet) { [weak self] allowed in
agent.askAuthentication(on: view.window, getBackTo: self, browser: nil, onStart: false, reason: .removeWallet) { [weak self] allowed in
Window.activateWindow(self?.view.window)
if allowed {
self?.removeWallet(wallet)
@ -317,6 +315,9 @@ class AccountsListViewController: NSViewController {
private func removeWallet(_ wallet: TokenaryWallet) {
try? walletsManager.delete(wallet: wallet)
}
@objc private func walletsChanged() {
reloadHeader()
updateCellModels()
tableView.reloadData()
@ -336,7 +337,7 @@ class AccountsListViewController: NSViewController {
alert.addButton(withTitle: Strings.cancel)
if alert.runModal() == .alertFirstButtonReturn {
let reason: AuthenticationReason = wallet.isMnemonic ? .showSecretWords : .showPrivateKey
agent.askAuthentication(on: view.window, getBackTo: self, onStart: false, reason: reason) { [weak self] allowed in
agent.askAuthentication(on: view.window, getBackTo: self, browser: nil, onStart: false, reason: reason) { [weak self] allowed in
Window.activateWindow(self?.view.window)
if allowed {
self?.showKey(wallet: wallet)

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -22,7 +22,7 @@ class WaitingViewController: NSViewController {
}
@IBAction func actionButtonTapped(_ sender: Any) {
Window.closeAll()
Window.closeWindowAndActivateNext(idToClose: view.window?.windowNumber, specificBrowser: nil)
}
}

View File

@ -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, browser: .unknown, 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, browser: .unknown, 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)
}
}

View File

@ -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,20 +23,36 @@ 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 closeWindow(idToClose: Int?) {
if let id = idToClose, let windowToClose = NSApplication.shared.windows.first(where: { $0.windowNumber == id }) {
windowToClose.close()
}
}
static func activateBrowser(force browser: Browser?) {
if let browser = browser {
static func closeWindowAndActivateNext(idToClose: Int?, specificBrowser: Browser?) {
closeWindow(idToClose: idToClose)
if let window = NSApplication.shared.windows.last(where: { $0.windowNumber != idToClose && $0.isOnActiveSpace && $0.contentViewController != nil }) {
activateWindow(window)
} else {
activateBrowser(specific: specificBrowser)
}
}
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, browser != .unknown {
activateBrowser(browser)
return
}
@ -64,11 +82,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
}

View File

@ -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 = "<group>"; };
2C8A09DE267579EA00993638 /* AccountsListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsListViewController.swift; sourceTree = "<group>"; };
2C8A09E226757FC000993638 /* AccountCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountCellView.swift; sourceTree = "<group>"; };
2C8A09E72675960D00993638 /* ErrorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorViewController.swift; sourceTree = "<group>"; };
2C8A09EA2675964700993638 /* ApproveViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApproveViewController.swift; sourceTree = "<group>"; };
2C8A09ED2675965F00993638 /* WaitingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitingViewController.swift; sourceTree = "<group>"; };
2C8E47A226A322E8007B8354 /* RightClickTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RightClickTableView.swift; sourceTree = "<group>"; };
@ -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 */,