mirror of
https://github.com/lil-org/tokenary.git
synced 2025-01-06 04:36:45 +03:00
Merge pull request #71 from zeriontech/feature/better-account-selection
Better account selection
This commit is contained in:
commit
2eb2e88830
@ -55,13 +55,17 @@ struct SafariRequest {
|
||||
self.name = name
|
||||
self.host = host
|
||||
|
||||
if let favicon = json["favicon"] as? String {
|
||||
if favicon.first == "/" {
|
||||
if let favicon = json["favicon"] as? String, !favicon.isEmpty {
|
||||
if favicon.hasPrefix("//") {
|
||||
self.favicon = "https:" + favicon
|
||||
} else if favicon.first == "/" {
|
||||
self.favicon = "https://" + host + favicon
|
||||
} else if favicon.first == "." {
|
||||
self.favicon = "https://" + host + favicon.dropFirst()
|
||||
} else if favicon.hasPrefix("http") {
|
||||
self.favicon = favicon
|
||||
} else {
|
||||
self.favicon = nil
|
||||
self.favicon = "https://" + host + "/" + favicon
|
||||
}
|
||||
} else {
|
||||
self.favicon = nil
|
||||
@ -88,7 +92,7 @@ struct SafariRequest {
|
||||
if let request = Near(name: name, json: jsonBody) {
|
||||
body = .near(request)
|
||||
}
|
||||
case .unknown:
|
||||
case .unknown, .multiple:
|
||||
if let request = Unknown(name: name, json: jsonBody) {
|
||||
body = .unknown(request)
|
||||
}
|
||||
|
@ -11,11 +11,47 @@ extension SafariRequest {
|
||||
case switchAccount
|
||||
}
|
||||
|
||||
struct ProviderConfiguration {
|
||||
let provider: Web3Provider
|
||||
let address: String
|
||||
}
|
||||
|
||||
let method: Method
|
||||
let providerConfigurations: [ProviderConfiguration]
|
||||
|
||||
init?(name: String, json: [String: Any]) {
|
||||
guard let method = Method(rawValue: name) else { return nil }
|
||||
self.method = method
|
||||
|
||||
var configurations = [ProviderConfiguration]()
|
||||
let jsonDecoder = JSONDecoder()
|
||||
if let latestConfigurations = json["latestConfigurations"] as? [[String: Any]] {
|
||||
for configuration in latestConfigurations {
|
||||
guard let providerString = configuration["provider"] as? String,
|
||||
let provider = Web3Provider(rawValue: providerString),
|
||||
let data = try? JSONSerialization.data(withJSONObject: configuration)
|
||||
else { continue }
|
||||
|
||||
switch provider {
|
||||
case .ethereum:
|
||||
guard let response = try? jsonDecoder.decode(ResponseToExtension.Ethereum.self, from: data),
|
||||
let address = response.results?.first else { continue }
|
||||
configurations.append(ProviderConfiguration(provider: provider, address: address))
|
||||
case .solana:
|
||||
guard let response = try? jsonDecoder.decode(ResponseToExtension.Solana.self, from: data),
|
||||
let address = response.publicKey else { continue }
|
||||
configurations.append(ProviderConfiguration(provider: provider, address: address))
|
||||
case .near:
|
||||
guard let response = try? jsonDecoder.decode(ResponseToExtension.Near.self, from: data),
|
||||
let address = response.account else { continue }
|
||||
configurations.append(ProviderConfiguration(provider: provider, address: address))
|
||||
case .tezos, .unknown, .multiple:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.providerConfigurations = configurations
|
||||
}
|
||||
|
||||
var responseUpdatesStoredConfiguration: Bool {
|
||||
|
@ -0,0 +1,14 @@
|
||||
// Copyright © 2022 Tokenary. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
extension ResponseToExtension {
|
||||
|
||||
struct Multiple {
|
||||
|
||||
let bodies: [Body]
|
||||
let providersToDisconnect: [Web3Provider]
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -12,6 +12,7 @@ struct ResponseToExtension {
|
||||
case solana(Solana)
|
||||
case tezos(Tezos)
|
||||
case near(Near)
|
||||
case multiple(Multiple)
|
||||
|
||||
var json: [String: Any] {
|
||||
let data: Data?
|
||||
@ -26,9 +27,17 @@ struct ResponseToExtension {
|
||||
data = try? jsonEncoder.encode(body)
|
||||
case .tezos(let body):
|
||||
data = try? jsonEncoder.encode(body)
|
||||
case .multiple(let body):
|
||||
let dict: [String: Any] = [
|
||||
"bodies": body.bodies.map { $0.json },
|
||||
"providersToDisconnect": body.providersToDisconnect.map { $0.rawValue },
|
||||
"provider": provider.rawValue
|
||||
]
|
||||
return dict
|
||||
}
|
||||
|
||||
if let data = data, let dict = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
||||
if let data = data, var dict = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
||||
dict["provider"] = provider.rawValue
|
||||
return dict
|
||||
} else {
|
||||
return [:]
|
||||
@ -45,17 +54,16 @@ struct ResponseToExtension {
|
||||
return .near
|
||||
case .tezos:
|
||||
return .tezos
|
||||
case .multiple:
|
||||
return .multiple
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(for request: SafariRequest, body: Body? = nil, error: String? = nil) {
|
||||
self.id = request.id
|
||||
let provider = (body?.provider ?? request.provider).rawValue
|
||||
|
||||
var json: [String: Any] = [
|
||||
"id": request.id,
|
||||
"provider": provider,
|
||||
"name": request.name
|
||||
]
|
||||
|
||||
@ -63,15 +71,15 @@ struct ResponseToExtension {
|
||||
json["error"] = error
|
||||
}
|
||||
|
||||
var bodyJSON = body?.json ?? [:]
|
||||
let bodyJSON = body?.json ?? [:]
|
||||
json.merge(bodyJSON) { (current, _) in current }
|
||||
|
||||
if request.body.value.responseUpdatesStoredConfiguration {
|
||||
if !bodyJSON.isEmpty {
|
||||
bodyJSON["provider"] = provider
|
||||
|
||||
if request.body.value.responseUpdatesStoredConfiguration, error == nil {
|
||||
if let bodies = bodyJSON["bodies"] {
|
||||
json["configurationToStore"] = bodies
|
||||
} else {
|
||||
json["configurationToStore"] = bodyJSON
|
||||
}
|
||||
|
||||
json["configurationToStore"] = bodyJSON
|
||||
}
|
||||
|
||||
self.json = json
|
||||
|
@ -3,5 +3,5 @@
|
||||
import Foundation
|
||||
|
||||
enum Web3Provider: String, Codable {
|
||||
case ethereum, solana, tezos, near, unknown
|
||||
case ethereum, solana, tezos, near, unknown, multiple
|
||||
}
|
||||
|
@ -2,11 +2,14 @@
|
||||
|
||||
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);
|
||||
didCompleteRequest(request.message.id, sender.tab.id);
|
||||
storeConfigurationIfNeeded(request.host, response);
|
||||
});
|
||||
if ("name" in request.message, request.message.name == "switchAccount") {
|
||||
getLatestConfiguration(request.host, function(currentConfiguration) {
|
||||
request.message.body = currentConfiguration;
|
||||
sendNativeMessage(request, sender, sendResponse);
|
||||
});
|
||||
} else {
|
||||
sendNativeMessage(request, sender, sendResponse);
|
||||
}
|
||||
} else if (request.subject === "getResponse") {
|
||||
browser.runtime.sendNativeMessage("mac.tokenary.io", request, function(response) {
|
||||
sendResponse(response);
|
||||
@ -21,6 +24,14 @@ browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
var latestConfigurations = {};
|
||||
var didReadLatestConfigurations = false;
|
||||
|
||||
function sendNativeMessage(request, sender, sendResponse) {
|
||||
browser.runtime.sendNativeMessage("mac.tokenary.io", request.message, function(response) {
|
||||
sendResponse(response);
|
||||
didCompleteRequest(request.message.id, sender.tab.id);
|
||||
storeConfigurationIfNeeded(request.host, response);
|
||||
});
|
||||
}
|
||||
|
||||
function respondWithLatestConfiguration(host, sendResponse) {
|
||||
var response = {};
|
||||
const latest = latestConfigurations[host];
|
||||
@ -38,7 +49,10 @@ function respondWithLatestConfiguration(host, sendResponse) {
|
||||
|
||||
function storeLatestConfiguration(host, configuration) {
|
||||
var latestArray = [];
|
||||
if ("provider" in configuration) {
|
||||
|
||||
if (Array.isArray(configuration)) {
|
||||
latestArray = configuration;
|
||||
} else if ("provider" in configuration) {
|
||||
const latest = latestConfigurations[host];
|
||||
|
||||
if (Array.isArray(latest)) {
|
||||
@ -97,13 +111,22 @@ function storeConfigurationIfNeeded(host, response) {
|
||||
}
|
||||
}
|
||||
|
||||
function justShowApp() {
|
||||
const id = genId();
|
||||
const showAppMessage = {name: "justShowApp", id: id, provider: "unknown", body: {}, host: ""};
|
||||
browser.runtime.sendNativeMessage("mac.tokenary.io", showAppMessage);
|
||||
}
|
||||
|
||||
browser.browserAction.onClicked.addListener(function(tab) {
|
||||
const message = {didTapExtensionButton: true};
|
||||
browser.tabs.sendMessage(tab.id, message);
|
||||
browser.tabs.sendMessage(tab.id, message, function(pong) {
|
||||
if (pong != true) {
|
||||
justShowApp();
|
||||
}
|
||||
});
|
||||
|
||||
if (tab.url == "" && tab.pendingUrl == "") {
|
||||
const id = genId();
|
||||
const showAppMessage = {name: "justShowApp", id: id, provider: "unknown", body: {}, host: ""};
|
||||
browser.runtime.sendNativeMessage("mac.tokenary.io", showAppMessage);
|
||||
justShowApp();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -113,13 +113,13 @@ function sendMessageToNativeApp(message) {
|
||||
function didTapExtensionButton() {
|
||||
const id = genId();
|
||||
const message = {name: "switchAccount", id: id, provider: "unknown", body: {}};
|
||||
// TODO: pass current network id for ethereum. or maybe just pass latestConfiguration here as well
|
||||
sendMessageToNativeApp(message);
|
||||
}
|
||||
|
||||
// Receive from background
|
||||
browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
if ("didTapExtensionButton" in request) {
|
||||
sendResponse(true);
|
||||
didTapExtensionButton();
|
||||
}
|
||||
});
|
||||
@ -132,10 +132,18 @@ window.addEventListener("message", function(event) {
|
||||
});
|
||||
|
||||
var getFavicon = function() {
|
||||
if (document.favicon) {
|
||||
return document.favicon;
|
||||
}
|
||||
|
||||
var nodeList = document.getElementsByTagName("link");
|
||||
for (var i = 0; i < nodeList.length; i++) {
|
||||
if ((nodeList[i].getAttribute("rel") == "icon") || (nodeList[i].getAttribute("rel") == "shortcut icon")) {
|
||||
return nodeList[i].getAttribute("href");
|
||||
if ((nodeList[i].getAttribute("rel") == "apple-touch-icon") || (nodeList[i].getAttribute("rel") == "icon") || (nodeList[i].getAttribute("rel") == "shortcut icon")) {
|
||||
const favicon = nodeList[i].getAttribute("href");
|
||||
if (!favicon.endsWith("svg")) {
|
||||
document.favicon = favicon;
|
||||
return favicon;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
|
File diff suppressed because one or more lines are too long
@ -53,6 +53,12 @@ class TokenaryEthereum extends EventEmitter {
|
||||
setTimeout( function() { window.ethereum.emit("_initialized"); }, 1);
|
||||
}
|
||||
|
||||
externalDisconnect() {
|
||||
this.setAddress("");
|
||||
window.ethereum.emit("disconnect");
|
||||
window.ethereum.emit("accountsChanged", []);
|
||||
}
|
||||
|
||||
setAddress(address) {
|
||||
const lowerAddress = (address || "").toLowerCase();
|
||||
this.address = lowerAddress;
|
||||
|
@ -69,6 +69,30 @@ function deliverResponseToSpecificProvider(id, response, provider) {
|
||||
break;
|
||||
case "near":
|
||||
window.near.processTokenaryResponse(id, response);
|
||||
break;
|
||||
case "multiple":
|
||||
response.bodies.forEach((body) => {
|
||||
body.id = id;
|
||||
body.name = response.name;
|
||||
deliverResponseToSpecificProvider(id, body, body.provider);
|
||||
});
|
||||
|
||||
response.providersToDisconnect.forEach((provider) => {
|
||||
switch (provider) {
|
||||
case "ethereum":
|
||||
window.ethereum.externalDisconnect();
|
||||
break;
|
||||
case "solana":
|
||||
window.solana.externalDisconnect();
|
||||
break;
|
||||
case "near":
|
||||
window.near.externalDisconnect();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
break;
|
||||
default:
|
||||
// pass unknown provider message to all providers
|
||||
|
@ -31,7 +31,13 @@ class TokenaryNear extends EventEmitter {
|
||||
return this.accountId;
|
||||
}
|
||||
|
||||
externalDisconnect() {
|
||||
this.accountId = null;
|
||||
this.emit("signOut");
|
||||
}
|
||||
|
||||
signOut() {
|
||||
this.accountId = null;
|
||||
this.emit("signOut");
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(true);
|
||||
|
@ -67,9 +67,14 @@ class TokenarySolana extends EventEmitter {
|
||||
return this.request(payload);
|
||||
}
|
||||
|
||||
externalDisconnect() {
|
||||
this.disconnect();
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
// TODO: implement
|
||||
// support also via request "disconnect" method
|
||||
this.isConnected = false;
|
||||
this.publicKey = null;
|
||||
this.emit("disconnect");
|
||||
}
|
||||
|
||||
signTransaction(transaction) {
|
||||
@ -108,6 +113,10 @@ class TokenarySolana extends EventEmitter {
|
||||
}
|
||||
|
||||
request(payload) {
|
||||
if (payload.method == "disconnect") {
|
||||
return this.disconnect();
|
||||
}
|
||||
|
||||
this.idMapping.tryFixId(payload);
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!payload.id) {
|
||||
|
@ -9,7 +9,7 @@ extension CoinType {
|
||||
case .solana:
|
||||
return "Solana"
|
||||
case .ethereum:
|
||||
return "Ethereum"
|
||||
return "Ethereum & L2s"
|
||||
case .near:
|
||||
return "Near"
|
||||
default:
|
||||
@ -43,4 +43,19 @@ extension CoinType {
|
||||
}
|
||||
}
|
||||
|
||||
static func correspondingToWeb3Provider(_ web3Provider: Web3Provider) -> CoinType? {
|
||||
switch web3Provider {
|
||||
case .ethereum:
|
||||
return .ethereum
|
||||
case .solana:
|
||||
return .solana
|
||||
case .tezos:
|
||||
return .tezos
|
||||
case .near:
|
||||
return .near
|
||||
case .unknown, .multiple:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
12
Shared/Models/AccountSelectionConfiguration.swift
Normal file
12
Shared/Models/AccountSelectionConfiguration.swift
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright © 2022 Tokenary. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import WalletCore
|
||||
|
||||
struct AccountSelectionConfiguration {
|
||||
let peer: PeerMeta?
|
||||
let coinType: CoinType?
|
||||
var selectedAccounts: Set<SpecificWalletAccount>
|
||||
let initiallyConnectedProviders: Set<Web3Provider>
|
||||
let completion: ((EthereumChain?, [SpecificWalletAccount]?) -> Void)
|
||||
}
|
@ -14,7 +14,9 @@ enum DappRequestAction {
|
||||
|
||||
struct SelectAccountAction {
|
||||
let provider: Web3Provider
|
||||
let completion: (EthereumChain?, TokenaryWallet?, Account?) -> Void
|
||||
let initiallyConnectedProviders: Set<Web3Provider>
|
||||
let preselectedAccounts: [SpecificWalletAccount]
|
||||
let completion: (EthereumChain?, [SpecificWalletAccount]?) -> Void
|
||||
}
|
||||
|
||||
struct SignMessageAction {
|
||||
|
@ -23,3 +23,11 @@ struct PeerMeta {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension SafariRequest {
|
||||
|
||||
var peerMeta: PeerMeta {
|
||||
return PeerMeta(title: host, iconURLString: favicon)
|
||||
}
|
||||
|
||||
}
|
||||
|
9
Shared/Models/SpecificWalletAccount.swift
Normal file
9
Shared/Models/SpecificWalletAccount.swift
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright © 2022 Tokenary. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import WalletCore
|
||||
|
||||
struct SpecificWalletAccount: Hashable {
|
||||
let walletId: String
|
||||
let account: Account
|
||||
}
|
@ -32,21 +32,44 @@ struct DappRequestProcessor {
|
||||
ExtensionBridge.respond(response: ResponseToExtension(for: request))
|
||||
return .justShowApp
|
||||
case .switchAccount:
|
||||
let action = SelectAccountAction(provider: .unknown) { chain, _, account in
|
||||
if let chain = chain, let account = account {
|
||||
switch account.coin {
|
||||
case .ethereum:
|
||||
let responseBody = ResponseToExtension.Ethereum(results: [account.address], chainId: chain.hexStringId, rpcURL: chain.nodeURLString)
|
||||
respond(to: request, body: .ethereum(responseBody), completion: completion)
|
||||
case .solana:
|
||||
let responseBody = ResponseToExtension.Solana(publicKey: account.address)
|
||||
respond(to: request, body: .solana(responseBody), completion: completion)
|
||||
case .near:
|
||||
let responseBody = ResponseToExtension.Near(account: account.address)
|
||||
respond(to: request, body: .near(responseBody), completion: completion)
|
||||
default:
|
||||
fatalError("Can't select that coin")
|
||||
let preselectedAccounts = body.providerConfigurations.compactMap { (configuration) -> SpecificWalletAccount? in
|
||||
guard let coin = CoinType.correspondingToWeb3Provider(configuration.provider) else { return nil }
|
||||
return walletsManager.getSpecificAccount(coin: coin, address: configuration.address)
|
||||
}
|
||||
let initiallyConnectedProviders = Set(body.providerConfigurations.map { $0.provider })
|
||||
let action = SelectAccountAction(provider: .unknown,
|
||||
initiallyConnectedProviders: initiallyConnectedProviders,
|
||||
preselectedAccounts: preselectedAccounts) { chain, specificWalletAccounts in
|
||||
if let chain = chain, let specificWalletAccounts = specificWalletAccounts {
|
||||
var specificProviderBodies = [ResponseToExtension.Body]()
|
||||
for specificWalletAccount in specificWalletAccounts {
|
||||
let account = specificWalletAccount.account
|
||||
switch account.coin {
|
||||
case .ethereum:
|
||||
let responseBody = ResponseToExtension.Ethereum(results: [account.address], chainId: chain.hexStringId, rpcURL: chain.nodeURLString)
|
||||
specificProviderBodies.append(.ethereum(responseBody))
|
||||
case .solana:
|
||||
let responseBody = ResponseToExtension.Solana(publicKey: account.address)
|
||||
specificProviderBodies.append(.solana(responseBody))
|
||||
case .near:
|
||||
let responseBody = ResponseToExtension.Near(account: account.address)
|
||||
specificProviderBodies.append(.near(responseBody))
|
||||
default:
|
||||
fatalError("Can't select that coin")
|
||||
}
|
||||
}
|
||||
|
||||
let providersToDisconnect = initiallyConnectedProviders.filter { provider in
|
||||
if let coin = CoinType.correspondingToWeb3Provider(provider),
|
||||
specificWalletAccounts.contains(where: { $0.account.coin == coin }) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
let body = ResponseToExtension.Multiple(bodies: specificProviderBodies, providersToDisconnect: Array(providersToDisconnect))
|
||||
respond(to: request, body: .multiple(body), completion: completion)
|
||||
} else {
|
||||
respond(to: request, error: Strings.canceled, completion: completion)
|
||||
}
|
||||
@ -57,15 +80,16 @@ struct DappRequestProcessor {
|
||||
}
|
||||
|
||||
private static func process(request: SafariRequest, nearRequest body: SafariRequest.Near, completion: @escaping () -> Void) -> DappRequestAction {
|
||||
let peerMeta = PeerMeta(title: request.host, iconURLString: request.favicon)
|
||||
let peerMeta = request.peerMeta
|
||||
lazy var account = getAccount(coin: .near, address: body.account)
|
||||
lazy var privateKey = getPrivateKey(coin: .near, address: body.account)
|
||||
|
||||
switch body.method {
|
||||
case .signIn:
|
||||
let action = SelectAccountAction(provider: .near) { _, _, account in
|
||||
if let account = account, account.coin == .near {
|
||||
let responseBody = ResponseToExtension.Near(account: account.address)
|
||||
let suggestedAccounts = walletsManager.suggestedAccounts(coin: .near)
|
||||
let action = SelectAccountAction(provider: .near, initiallyConnectedProviders: Set(), preselectedAccounts: suggestedAccounts) { _, specificWalletAccounts in
|
||||
if let specificWalletAccount = specificWalletAccounts?.first, specificWalletAccount.account.coin == .near {
|
||||
let responseBody = ResponseToExtension.Near(account: specificWalletAccount.account.address)
|
||||
respond(to: request, body: .near(responseBody), completion: completion)
|
||||
} else {
|
||||
respond(to: request, error: Strings.canceled, completion: completion)
|
||||
@ -108,15 +132,16 @@ struct DappRequestProcessor {
|
||||
}
|
||||
|
||||
private static func process(request: SafariRequest, solanaRequest body: SafariRequest.Solana, completion: @escaping () -> Void) -> DappRequestAction {
|
||||
let peerMeta = PeerMeta(title: request.host, iconURLString: request.favicon)
|
||||
let peerMeta = request.peerMeta
|
||||
lazy var account = getAccount(coin: .solana, address: body.publicKey)
|
||||
lazy var privateKey = getPrivateKey(coin: .solana, address: body.publicKey)
|
||||
|
||||
switch body.method {
|
||||
case .connect:
|
||||
let action = SelectAccountAction(provider: .solana) { _, _, account in
|
||||
if let account = account, account.coin == .solana {
|
||||
let responseBody = ResponseToExtension.Solana(publicKey: account.address)
|
||||
let suggestedAccounts = walletsManager.suggestedAccounts(coin: .solana)
|
||||
let action = SelectAccountAction(provider: .solana, initiallyConnectedProviders: Set(), preselectedAccounts: suggestedAccounts) { _, specificWalletAccounts in
|
||||
if let specificWalletAccount = specificWalletAccounts?.first, specificWalletAccount.account.coin == .solana {
|
||||
let responseBody = ResponseToExtension.Solana(publicKey: specificWalletAccount.account.address)
|
||||
respond(to: request, body: .solana(responseBody), completion: completion)
|
||||
} else {
|
||||
respond(to: request, error: Strings.canceled, completion: completion)
|
||||
@ -189,14 +214,15 @@ struct DappRequestProcessor {
|
||||
}
|
||||
|
||||
private static func process(request: SafariRequest, ethereumRequest: SafariRequest.Ethereum, completion: @escaping () -> Void) -> DappRequestAction {
|
||||
let peerMeta = PeerMeta(title: request.host, iconURLString: request.favicon)
|
||||
let peerMeta = request.peerMeta
|
||||
lazy var account = getAccount(coin: .ethereum, address: ethereumRequest.address)
|
||||
|
||||
switch ethereumRequest.method {
|
||||
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)
|
||||
let suggestedAccounts = walletsManager.suggestedAccounts(coin: .ethereum)
|
||||
let action = SelectAccountAction(provider: .ethereum, initiallyConnectedProviders: Set(), preselectedAccounts: suggestedAccounts) { chain, specificWalletAccounts in
|
||||
if let chain = chain, let specificWalletAccount = specificWalletAccounts?.first, specificWalletAccount.account.coin == .ethereum {
|
||||
let responseBody = ResponseToExtension.Ethereum(results: [specificWalletAccount.account.address], chainId: chain.hexStringId, rpcURL: chain.nodeURLString)
|
||||
respond(to: request, body: .ethereum(responseBody), completion: completion)
|
||||
} else {
|
||||
respond(to: request, error: Strings.canceled, completion: completion)
|
||||
|
@ -88,5 +88,6 @@ struct Strings {
|
||||
static let data = "Data"
|
||||
static let viewOnNearExplorer = "View on Near explorer"
|
||||
static let sendingTransaction = "Sending a transaction"
|
||||
static let disconnect = "Disconnect"
|
||||
|
||||
}
|
||||
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
@ -64,6 +64,7 @@ final class WalletsManager {
|
||||
return wallets.first(where: { $0.id == id })
|
||||
}
|
||||
|
||||
// TODO: deprecate
|
||||
func getWallet(ethereumAddress: String) -> TokenaryWallet? {
|
||||
return wallets.first(where: { $0.ethereumAddress?.lowercased() == ethereumAddress.lowercased() })
|
||||
}
|
||||
@ -83,6 +84,24 @@ final class WalletsManager {
|
||||
}
|
||||
}
|
||||
|
||||
func getSpecificAccount(coin: CoinType, address: String) -> SpecificWalletAccount? {
|
||||
for wallet in wallets {
|
||||
if let account = wallet.accounts.first(where: { $0.coin == coin && $0.address == address }) {
|
||||
return SpecificWalletAccount(walletId: wallet.id, account: account)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func suggestedAccounts(coin: CoinType) -> [SpecificWalletAccount] {
|
||||
for wallet in wallets {
|
||||
if let account = wallet.accounts.first(where: { $0.coin == coin }) {
|
||||
return [SpecificWalletAccount(walletId: wallet.id, account: account)]
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
private func createWallet(name: String, password: String) throws -> TokenaryWallet {
|
||||
let key = StoredKey(name: name, password: Data(password.utf8))
|
||||
let id = makeNewWalletId()
|
||||
|
@ -78,11 +78,15 @@ class Agent: NSObject {
|
||||
} else {
|
||||
let accountsList = instantiate(AccountsListViewController.self)
|
||||
|
||||
if case let .wcSession(session) = request {
|
||||
accountsList.onSelectedWallet = onSelectedWallet(session: session)
|
||||
if case let .wcSession(session) = request, let completion = onSelectedWallet(session: session) {
|
||||
accountsList.accountSelectionConfiguration = AccountSelectionConfiguration(peer: nil,
|
||||
coinType: .ethereum,
|
||||
selectedAccounts: Set(),
|
||||
initiallyConnectedProviders: Set(),
|
||||
completion: completion)
|
||||
}
|
||||
|
||||
let windowController = Window.showNew(closeOthers: accountsList.onSelectedWallet == nil)
|
||||
let windowController = Window.showNew(closeOthers: accountsList.accountSelectionConfiguration == nil)
|
||||
windowController.contentViewController = accountsList
|
||||
}
|
||||
}
|
||||
@ -116,7 +120,7 @@ class Agent: NSObject {
|
||||
windowController.contentViewController = approveViewController
|
||||
}
|
||||
|
||||
func getWalletSelectionCompletionIfShouldSelect() -> ((EthereumChain?, TokenaryWallet?, Account?) -> Void)? {
|
||||
func getWalletSelectionCompletionIfShouldSelect() -> ((EthereumChain?, [SpecificWalletAccount]?) -> Void)? {
|
||||
let session = getSessionFromPasteboard()
|
||||
return onSelectedWallet(session: session)
|
||||
}
|
||||
@ -160,11 +164,14 @@ class Agent: NSObject {
|
||||
alert.alertStyle = .warning
|
||||
alert.addButton(withTitle: Strings.ok)
|
||||
alert.addButton(withTitle: Strings.cancel)
|
||||
if alert.runModal() == .alertFirstButtonReturn {
|
||||
NSApp.terminate(nil)
|
||||
}
|
||||
if updateStatusBarAfterwards {
|
||||
setupStatusBarItem()
|
||||
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
if alert.runModal() == .alertFirstButtonReturn {
|
||||
NSApp.terminate(nil)
|
||||
}
|
||||
if updateStatusBarAfterwards {
|
||||
self?.setupStatusBarItem()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,14 +219,14 @@ class Agent: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
private func onSelectedWallet(session: WCSession?) -> ((EthereumChain?, TokenaryWallet?, Account?) -> Void)? {
|
||||
private func onSelectedWallet(session: WCSession?) -> ((EthereumChain?, [SpecificWalletAccount]?) -> Void)? {
|
||||
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 {
|
||||
return { [weak self] chain, specificWalletAccounts in
|
||||
guard let chain = chain, let specificWalletAccount = specificWalletAccounts?.first, specificWalletAccount.account.coin == .ethereum else {
|
||||
Window.closeAllAndActivateBrowser(specific: nil)
|
||||
return
|
||||
}
|
||||
self?.connectWallet(session: session, chainId: chain.id, wallet: wallet)
|
||||
self?.connectWallet(session: session, chainId: chain.id, walletId: specificWalletAccount.walletId)
|
||||
}
|
||||
}
|
||||
|
||||
@ -283,12 +290,12 @@ class Agent: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
private func connectWallet(session: WCSession, chainId: Int, wallet: TokenaryWallet) {
|
||||
private func connectWallet(session: WCSession, chainId: Int, walletId: String) {
|
||||
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
|
||||
walletConnect.connect(session: session, chainId: chainId, walletId: walletId) { [weak window] _ in
|
||||
if window?.isVisible == true {
|
||||
Window.closeAllAndActivateBrowser(specific: nil)
|
||||
}
|
||||
@ -315,7 +322,12 @@ class Agent: NSObject {
|
||||
let windowController = Window.showNew(closeOthers: closeOtherWindows)
|
||||
windowNumber = windowController.window?.windowNumber
|
||||
let accountsList = instantiate(AccountsListViewController.self)
|
||||
accountsList.onSelectedWallet = accountAction.completion
|
||||
let coinType = CoinType.correspondingToWeb3Provider(accountAction.provider)
|
||||
accountsList.accountSelectionConfiguration = AccountSelectionConfiguration(peer: safariRequest.peerMeta,
|
||||
coinType: coinType,
|
||||
selectedAccounts: Set(accountAction.preselectedAccounts),
|
||||
initiallyConnectedProviders: accountAction.initiallyConnectedProviders,
|
||||
completion: accountAction.completion)
|
||||
windowController.contentViewController = accountsList
|
||||
case .approveMessage(let action):
|
||||
let windowController = Window.showNew(closeOthers: false)
|
||||
|
@ -808,11 +808,11 @@ DQ
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="AccountsListViewController" id="29s-Rd-OUf" customClass="AccountsListViewController" customModule="Tokenary" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" id="Yjc-Zm-uZY">
|
||||
<rect key="frame" x="0.0" y="0.0" width="250" height="350"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="250" height="412"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dkh-kG-EFj">
|
||||
<rect key="frame" x="53" y="292" width="144" height="34"/>
|
||||
<rect key="frame" x="53" y="354" width="144" height="34"/>
|
||||
<textFieldCell key="cell" controlSize="large" enabled="NO" allowsUndo="NO" alignment="center" title="Accounts" id="9No-vQ-vBK">
|
||||
<font key="font" metaFont="systemHeavy" size="29"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -820,7 +820,7 @@ DQ
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<scrollView autohidesScrollers="YES" horizontalLineScroll="40" horizontalPageScroll="0.0" verticalLineScroll="40" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="7bs-Kr-ija">
|
||||
<rect key="frame" x="0.0" y="0.0" width="250" height="280"/>
|
||||
<rect key="frame" x="0.0" y="62" width="250" height="280"/>
|
||||
<clipView key="contentView" id="RjU-hi-SHx">
|
||||
<rect key="frame" x="1" y="1" width="248" height="278"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
@ -944,6 +944,7 @@ DQ
|
||||
</subviews>
|
||||
</clipView>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="280" id="wta-1a-4RU"/>
|
||||
<constraint firstAttribute="width" constant="250" id="zxW-2l-wsU"/>
|
||||
</constraints>
|
||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="IT1-B8-OHF">
|
||||
@ -956,7 +957,7 @@ DQ
|
||||
</scroller>
|
||||
</scrollView>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ngQ-Bn-Kwd">
|
||||
<rect key="frame" x="205" y="292" width="33" height="34"/>
|
||||
<rect key="frame" x="205" y="354" width="33" height="34"/>
|
||||
<buttonCell key="cell" type="inline" title="+" bezelStyle="inline" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" state="on" imageScaling="proportionallyDown" inset="2" id="JVh-da-a0h">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="systemBold" size="29"/>
|
||||
@ -966,7 +967,7 @@ DQ
|
||||
</connections>
|
||||
</button>
|
||||
<button hidden="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="eNt-GB-Tzb">
|
||||
<rect key="frame" x="17" y="294.5" width="26" height="26"/>
|
||||
<rect key="frame" x="17" y="356.5" width="26" height="26"/>
|
||||
<buttonCell key="cell" type="inline" bezelStyle="inline" image="globe" catalog="system" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" state="on" imageScaling="proportionallyDown" inset="2" id="tZs-rG-rml">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="systemBold" size="21"/>
|
||||
@ -975,32 +976,125 @@ DQ
|
||||
<action selector="networkButtonTapped:" target="29s-Rd-OUf" id="JUo-sQ-bBT"/>
|
||||
</connections>
|
||||
</button>
|
||||
<stackView distribution="fill" orientation="vertical" alignment="leading" spacing="0.0" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="1000" verticalHuggingPriority="1000" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="teC-wK-5y1">
|
||||
<rect key="frame" x="125" y="396" width="0.0" height="0.0"/>
|
||||
<subviews>
|
||||
<stackView hidden="YES" distribution="fill" orientation="horizontal" alignment="top" spacing="6" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="1000" verticalCompressionResistancePriority="1000" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="H9M-qt-ZuF">
|
||||
<rect key="frame" x="0.0" y="-16" width="119" height="16"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="25J-Lu-4aq">
|
||||
<rect key="frame" x="0.0" y="0.0" width="16" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="16" id="9BR-I3-Qat"/>
|
||||
<constraint firstAttribute="width" constant="16" id="c28-0n-EPd"/>
|
||||
</constraints>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="9eJ-C5-e6g"/>
|
||||
</imageView>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="Khi-cn-EGb">
|
||||
<rect key="frame" x="20" y="0.0" width="101" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" title="Unknown dapp" id="CM5-MZ-VQq">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<visibilityPriorities>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
</visibilityPriorities>
|
||||
<customSpacing>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<visibilityPriorities>
|
||||
<integer value="1000"/>
|
||||
</visibilityPriorities>
|
||||
<customSpacing>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<stackView distribution="fill" orientation="horizontal" alignment="top" spacing="12" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="CWc-Db-L0d">
|
||||
<rect key="frame" x="52" y="20" width="146" height="28"/>
|
||||
<subviews>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="x8R-fO-ffb">
|
||||
<rect key="frame" x="-6" y="-6" width="74" height="40"/>
|
||||
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" controlSize="large" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="tEk-C9-Zxh">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<string key="keyEquivalent" base64-UTF8="YES">
|
||||
Gw
|
||||
</string>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="didClickSecondaryButton:" target="29s-Rd-OUf" id="kaH-C7-Dx1"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="sfH-TT-fxb">
|
||||
<rect key="frame" x="68" y="-6" width="84" height="40"/>
|
||||
<buttonCell key="cell" type="push" title="Connect" bezelStyle="rounded" alignment="center" controlSize="large" enabled="NO" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="A0m-eU-I2u">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<string key="keyEquivalent" base64-UTF8="YES">
|
||||
DQ
|
||||
</string>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="didClickPrimaryButton:" target="29s-Rd-OUf" id="bQt-bZ-sLx"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<visibilityPriorities>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
</visibilityPriorities>
|
||||
<customSpacing>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="CWc-Db-L0d" secondAttribute="bottom" constant="20" id="0Ia-R2-NPx"/>
|
||||
<constraint firstItem="CWc-Db-L0d" firstAttribute="width" relation="lessThanOrEqual" secondItem="Yjc-Zm-uZY" secondAttribute="width" constant="-20" id="22A-wv-4e9"/>
|
||||
<constraint firstItem="ngQ-Bn-Kwd" firstAttribute="leading" secondItem="dkh-kG-EFj" secondAttribute="trailing" constant="10" id="2jH-ov-jln"/>
|
||||
<constraint firstItem="7bs-Kr-ija" firstAttribute="leading" secondItem="Yjc-Zm-uZY" secondAttribute="leading" id="3n0-jB-KwV"/>
|
||||
<constraint firstAttribute="trailing" secondItem="7bs-Kr-ija" secondAttribute="trailing" id="6m3-S7-WJp"/>
|
||||
<constraint firstItem="teC-wK-5y1" firstAttribute="top" secondItem="Yjc-Zm-uZY" secondAttribute="top" constant="16" id="D2B-2h-top"/>
|
||||
<constraint firstItem="CWc-Db-L0d" firstAttribute="centerX" secondItem="Yjc-Zm-uZY" secondAttribute="centerX" id="KMO-vQ-oM4"/>
|
||||
<constraint firstAttribute="trailing" secondItem="ngQ-Bn-Kwd" secondAttribute="trailing" constant="12" id="Ln4-b5-NfT"/>
|
||||
<constraint firstItem="teC-wK-5y1" firstAttribute="centerX" secondItem="Yjc-Zm-uZY" secondAttribute="centerX" id="QdA-64-M4F"/>
|
||||
<constraint firstItem="ngQ-Bn-Kwd" firstAttribute="firstBaseline" secondItem="dkh-kG-EFj" secondAttribute="firstBaseline" id="RDE-f4-Heb"/>
|
||||
<constraint firstItem="eNt-GB-Tzb" firstAttribute="top" secondItem="Yjc-Zm-uZY" secondAttribute="top" constant="35" id="dRD-EV-wsJ"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="teC-wK-5y1" secondAttribute="trailing" constant="16" id="c7u-4P-5OL"/>
|
||||
<constraint firstAttribute="bottom" secondItem="7bs-Kr-ija" secondAttribute="bottom" constant="62" id="cFH-vX-gKa"/>
|
||||
<constraint firstItem="eNt-GB-Tzb" firstAttribute="centerY" secondItem="ngQ-Bn-Kwd" secondAttribute="centerY" constant="2" id="dq4-lW-ssv"/>
|
||||
<constraint firstItem="7bs-Kr-ija" firstAttribute="top" secondItem="dkh-kG-EFj" secondAttribute="bottom" constant="12" id="fXb-zL-LLv"/>
|
||||
<constraint firstItem="dkh-kG-EFj" firstAttribute="top" secondItem="teC-wK-5y1" secondAttribute="bottom" constant="8" id="fig-45-iff"/>
|
||||
<constraint firstItem="dkh-kG-EFj" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="eNt-GB-Tzb" secondAttribute="trailing" constant="3" id="imS-CC-tSQ"/>
|
||||
<constraint firstItem="dkh-kG-EFj" firstAttribute="top" secondItem="Yjc-Zm-uZY" secondAttribute="top" constant="24" id="oaa-yR-g0U"/>
|
||||
<constraint firstItem="dkh-kG-EFj" firstAttribute="centerX" secondItem="Yjc-Zm-uZY" secondAttribute="centerX" id="qoF-Dr-xWA"/>
|
||||
<constraint firstAttribute="bottom" secondItem="7bs-Kr-ija" secondAttribute="bottom" id="zCE-cx-u4r"/>
|
||||
<constraint firstItem="eNt-GB-Tzb" firstAttribute="leading" secondItem="Yjc-Zm-uZY" secondAttribute="leading" constant="17" id="zcY-Q0-K0f"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="accountsListBottomConstraint" destination="cFH-vX-gKa" id="vbK-Ea-dro"/>
|
||||
<outlet property="addButton" destination="ngQ-Bn-Kwd" id="FlT-oq-C5e"/>
|
||||
<outlet property="bottomButtonsStackView" destination="CWc-Db-L0d" id="RH4-Z6-Fs8"/>
|
||||
<outlet property="networkButton" destination="eNt-GB-Tzb" id="cvm-05-aZj"/>
|
||||
<outlet property="primaryButton" destination="sfH-TT-fxb" id="OJf-Ua-1SW"/>
|
||||
<outlet property="secondaryButton" destination="x8R-fO-ffb" id="F9u-nW-BbM"/>
|
||||
<outlet property="tableView" destination="glA-FK-Kdd" id="9aW-Qr-UuF"/>
|
||||
<outlet property="titleLabel" destination="dkh-kG-EFj" id="xDn-bP-HWG"/>
|
||||
<outlet property="titleLabelTopConstraint" destination="fig-45-iff" id="oIf-XA-wf5"/>
|
||||
<outlet property="websiteLogoImageView" destination="25J-Lu-4aq" id="GrB-Xe-SUU"/>
|
||||
<outlet property="websiteNameLabel" destination="Khi-cn-EGb" id="Qf9-nL-7eP"/>
|
||||
<outlet property="websiteNameStackView" destination="H9M-qt-ZuF" id="598-jz-EmW"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<customObject id="JTb-7y-Jwq" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="380" y="741"/>
|
||||
<point key="canvasLocation" x="980" y="346"/>
|
||||
</scene>
|
||||
<!--Waiting View Controller-->
|
||||
<scene sceneID="xkb-7p-mUK">
|
||||
@ -1775,7 +1869,7 @@ DQ
|
||||
</viewController>
|
||||
<customObject id="pvA-vl-oOf" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="989" y="1177"/>
|
||||
<point key="canvasLocation" x="380" y="740"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
|
@ -8,10 +8,9 @@ class AccountsListViewController: NSViewController {
|
||||
private let agent = Agent.shared
|
||||
private let walletsManager = WalletsManager.shared
|
||||
private var cellModels = [CellModel]()
|
||||
|
||||
private var chain = EthereumChain.ethereum
|
||||
private var didCallCompletion = false
|
||||
var onSelectedWallet: ((EthereumChain?, TokenaryWallet?, Account?) -> Void)?
|
||||
var accountSelectionConfiguration: AccountSelectionConfiguration?
|
||||
var newWalletId: String?
|
||||
var getBackToRect: CGRect?
|
||||
|
||||
@ -44,6 +43,21 @@ class AccountsListViewController: NSViewController {
|
||||
}
|
||||
}
|
||||
|
||||
@IBOutlet weak var websiteLogoImageView: NSImageView! {
|
||||
didSet {
|
||||
websiteLogoImageView.wantsLayer = true
|
||||
websiteLogoImageView.layer?.backgroundColor = NSColor.systemGray.withAlphaComponent(0.5).cgColor
|
||||
websiteLogoImageView.layer?.cornerRadius = 5
|
||||
}
|
||||
}
|
||||
|
||||
@IBOutlet weak var secondaryButton: NSButton!
|
||||
@IBOutlet weak var primaryButton: NSButton!
|
||||
@IBOutlet weak var bottomButtonsStackView: NSStackView!
|
||||
@IBOutlet weak var accountsListBottomConstraint: NSLayoutConstraint!
|
||||
@IBOutlet weak var titleLabelTopConstraint: NSLayoutConstraint!
|
||||
@IBOutlet weak var websiteNameStackView: NSStackView!
|
||||
@IBOutlet weak var websiteNameLabel: NSTextField!
|
||||
@IBOutlet weak var networkButton: NSButton!
|
||||
@IBOutlet weak var titleLabel: NSTextField!
|
||||
@IBOutlet weak var tableView: RightClickTableView! {
|
||||
@ -65,9 +79,13 @@ class AccountsListViewController: NSViewController {
|
||||
super.viewDidLoad()
|
||||
|
||||
reloadHeader()
|
||||
updateBottomButtons()
|
||||
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)
|
||||
|
||||
if let preselectedAccount = accountSelectionConfiguration?.selectedAccounts.first {
|
||||
scrollTo(specificWalletAccount: preselectedAccount)
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidAppear() {
|
||||
@ -84,19 +102,60 @@ class AccountsListViewController: NSViewController {
|
||||
Alert.showSafariPrompt()
|
||||
}
|
||||
|
||||
private func callCompletion(wallet: TokenaryWallet?, account: Account?) {
|
||||
private func callCompletion(specificWalletAccounts: [SpecificWalletAccount]?) {
|
||||
if !didCallCompletion {
|
||||
didCallCompletion = true
|
||||
onSelectedWallet?(chain, wallet, account)
|
||||
accountSelectionConfiguration?.completion(chain, specificWalletAccounts)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateBottomButtons() {
|
||||
if let accountSelectionConfiguration = accountSelectionConfiguration {
|
||||
accountsListBottomConstraint.constant = 62
|
||||
bottomButtonsStackView.isHidden = false
|
||||
|
||||
if !accountSelectionConfiguration.initiallyConnectedProviders.isEmpty {
|
||||
primaryButton.title = Strings.ok
|
||||
secondaryButton.title = Strings.disconnect
|
||||
secondaryButton.keyEquivalent = ""
|
||||
}
|
||||
|
||||
} else {
|
||||
accountsListBottomConstraint.constant = 0
|
||||
bottomButtonsStackView.isHidden = true
|
||||
}
|
||||
updatePrimaryButton()
|
||||
}
|
||||
|
||||
private func updatePrimaryButton() {
|
||||
primaryButton.isEnabled = accountSelectionConfiguration?.selectedAccounts.isEmpty == false
|
||||
}
|
||||
|
||||
private func reloadHeader() {
|
||||
let canSelectAccount = onSelectedWallet != nil && !wallets.isEmpty
|
||||
let canSelectAccount = accountSelectionConfiguration != nil && !wallets.isEmpty
|
||||
titleLabel.stringValue = canSelectAccount ? Strings.selectAccountTwoLines : Strings.wallets
|
||||
addButton.isHidden = wallets.isEmpty
|
||||
|
||||
if canSelectAccount, networkButton.isHidden {
|
||||
if canSelectAccount, let peer = accountSelectionConfiguration?.peer {
|
||||
websiteNameLabel.stringValue = peer.name
|
||||
titleLabelTopConstraint.constant = 14
|
||||
websiteNameStackView.isHidden = false
|
||||
|
||||
if websiteLogoImageView.image == nil, let urlString = peer.iconURLString, let url = URL(string: urlString) {
|
||||
websiteLogoImageView.kf.setImage(with: url) { [weak websiteLogoImageView] result in
|
||||
if case .success = result {
|
||||
websiteLogoImageView?.layer?.backgroundColor = NSColor.clear.cgColor
|
||||
websiteLogoImageView?.layer?.cornerRadius = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
titleLabelTopConstraint.constant = 8
|
||||
websiteNameStackView.isHidden = true
|
||||
}
|
||||
|
||||
let canSelectNetworkForCurrentProvider = accountSelectionConfiguration?.coinType == .ethereum || accountSelectionConfiguration?.coinType == nil
|
||||
if canSelectAccount, networkButton.isHidden, canSelectNetworkForCurrentProvider {
|
||||
networkButton.isHidden = false
|
||||
let menu = NSMenu()
|
||||
let titleItem = NSMenuItem(title: Strings.selectNetworkOptionally, action: nil, keyEquivalent: "")
|
||||
@ -121,19 +180,11 @@ class AccountsListViewController: NSViewController {
|
||||
menu.addItem(.separator())
|
||||
menu.addItem(submenuItem)
|
||||
networkButton.menu = menu
|
||||
} else if !canSelectAccount, !networkButton.isHidden {
|
||||
} else if !(canSelectAccount && canSelectNetworkForCurrentProvider), !networkButton.isHidden {
|
||||
networkButton.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func didBecomeActive() {
|
||||
guard view.window?.isVisible == true else { return }
|
||||
if let completion = agent.getWalletSelectionCompletionIfShouldSelect() {
|
||||
onSelectedWallet = completion
|
||||
}
|
||||
reloadHeader()
|
||||
}
|
||||
|
||||
@IBAction func addButtonTapped(_ sender: NSButton) {
|
||||
let menu = sender.menu
|
||||
|
||||
@ -158,6 +209,18 @@ class AccountsListViewController: NSViewController {
|
||||
sender.menu?.popUp(positioning: nil, at: origin, in: view)
|
||||
}
|
||||
|
||||
@IBAction func didClickSecondaryButton(_ sender: Any) {
|
||||
if accountSelectionConfiguration?.initiallyConnectedProviders.isEmpty == false {
|
||||
callCompletion(specificWalletAccounts: [])
|
||||
} else {
|
||||
callCompletion(specificWalletAccounts: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func didClickPrimaryButton(_ sender: Any) {
|
||||
callCompletion(specificWalletAccounts: accountSelectionConfiguration?.selectedAccounts.map { $0 })
|
||||
}
|
||||
|
||||
@objc private func didSelectChain(_ sender: AnyObject) {
|
||||
guard let menuItem = sender as? NSMenuItem,
|
||||
let selectedChain = EthereumChain(rawValue: menuItem.tag) else { return }
|
||||
@ -217,10 +280,37 @@ class AccountsListViewController: NSViewController {
|
||||
|
||||
@objc private func didClickImportAccount() {
|
||||
let importViewController = instantiate(ImportViewController.self)
|
||||
importViewController.onSelectedWallet = onSelectedWallet
|
||||
importViewController.accountSelectionConfiguration = accountSelectionConfiguration
|
||||
view.window?.contentViewController = importViewController
|
||||
}
|
||||
|
||||
private func scrollTo(specificWalletAccount: SpecificWalletAccount) {
|
||||
guard let specificWalletIndex = wallets.firstIndex(where: { $0.id == specificWalletAccount.walletId }),
|
||||
let specificAccountIndex = wallets[specificWalletIndex].accounts.firstIndex(where: { $0 == specificWalletAccount.account })
|
||||
else { return }
|
||||
|
||||
let row = cellModels.firstIndex { cellModel in
|
||||
switch cellModel {
|
||||
case let .mnemonicAccount(walletIndex, accountIndex):
|
||||
return walletIndex == specificWalletIndex && accountIndex == specificAccountIndex
|
||||
case let .privateKeyAccount(walletIndex):
|
||||
return walletIndex == specificWalletIndex
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if let row = row {
|
||||
tableView.scrollRowToVisible(row)
|
||||
}
|
||||
}
|
||||
|
||||
override func cancelOperation(_ sender: Any?) {
|
||||
if accountSelectionConfiguration?.initiallyConnectedProviders.isEmpty == false {
|
||||
callCompletion(specificWalletAccounts: nil)
|
||||
}
|
||||
}
|
||||
|
||||
private func walletForRow(_ row: Int) -> TokenaryWallet? {
|
||||
guard row >= 0 else { return nil }
|
||||
let item = cellModels[row]
|
||||
@ -317,8 +407,10 @@ class AccountsListViewController: NSViewController {
|
||||
try? walletsManager.delete(wallet: wallet)
|
||||
}
|
||||
|
||||
@objc private func walletsChanged() {
|
||||
@objc private func walletsChanged() {
|
||||
validateSelectedAccounts()
|
||||
reloadHeader()
|
||||
updateBottomButtons()
|
||||
updateCellModels()
|
||||
tableView.reloadData()
|
||||
}
|
||||
@ -403,6 +495,38 @@ class AccountsListViewController: NSViewController {
|
||||
}
|
||||
}
|
||||
|
||||
private func validateSelectedAccounts() {
|
||||
guard let specificWalletAccounts = accountSelectionConfiguration?.selectedAccounts else { return }
|
||||
for specificWalletAccount in specificWalletAccounts {
|
||||
if let wallet = wallets.first(where: { $0.id == specificWalletAccount.walletId }),
|
||||
wallet.accounts.contains(specificWalletAccount.account) {
|
||||
continue
|
||||
} else {
|
||||
accountSelectionConfiguration?.selectedAccounts.remove(specificWalletAccount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func didClickAccountInSelectionMode(specificWalletAccount: SpecificWalletAccount) {
|
||||
let wasSelected = accountSelectionConfiguration?.selectedAccounts.contains(specificWalletAccount) == true
|
||||
|
||||
if !wasSelected, let toDeselect = accountSelectionConfiguration?.selectedAccounts.first(where: { $0.account.coin == specificWalletAccount.account.coin }) {
|
||||
accountSelectionConfiguration?.selectedAccounts.remove(toDeselect)
|
||||
}
|
||||
|
||||
if wasSelected {
|
||||
accountSelectionConfiguration?.selectedAccounts.remove(specificWalletAccount)
|
||||
} else {
|
||||
accountSelectionConfiguration?.selectedAccounts.insert(specificWalletAccount)
|
||||
}
|
||||
|
||||
updatePrimaryButton()
|
||||
}
|
||||
|
||||
private func accountCanBeSelected(_ account: Account) -> Bool {
|
||||
return accountSelectionConfiguration?.coinType == nil || accountSelectionConfiguration?.coinType == account.coin
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension AccountsListViewController: TableViewMenuSource {
|
||||
@ -452,7 +576,7 @@ extension AccountsListViewController: AccountsHeaderDelegate {
|
||||
guard let wallet = walletForRow(row) else { return }
|
||||
|
||||
let editAccountsViewController = instantiate(EditAccountsViewController.self)
|
||||
editAccountsViewController.onSelectedWallet = onSelectedWallet
|
||||
editAccountsViewController.accountSelectionConfiguration = accountSelectionConfiguration
|
||||
editAccountsViewController.wallet = wallet
|
||||
editAccountsViewController.getBackToRect = tableView.visibleRect
|
||||
view.window?.contentViewController = editAccountsViewController
|
||||
@ -500,12 +624,17 @@ extension AccountsListViewController: NSTableViewDelegate {
|
||||
return false
|
||||
}
|
||||
|
||||
if onSelectedWallet != nil {
|
||||
callCompletion(wallet: wallet, account: account)
|
||||
if accountSelectionConfiguration != nil {
|
||||
if accountCanBeSelected(account) {
|
||||
let specificWalletAccount = SpecificWalletAccount(walletId: wallet.id, account: account)
|
||||
didClickAccountInSelectionMode(specificWalletAccount: specificWalletAccount)
|
||||
tableView.reloadData()
|
||||
}
|
||||
return false
|
||||
} else {
|
||||
showMenuOnCellSelection(row: row)
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
@ -518,12 +647,18 @@ extension AccountsListViewController: NSTableViewDataSource {
|
||||
case let .privateKeyAccount(walletIndex: walletIndex):
|
||||
let wallet = wallets[walletIndex]
|
||||
let rowView = tableView.makeViewOfType(AccountCellView.self, owner: self)
|
||||
rowView.setup(account: wallet.accounts[0])
|
||||
let account = wallet.accounts[0]
|
||||
let specificWalletAccount = SpecificWalletAccount(walletId: wallet.id, account: account)
|
||||
let isSelected = accountSelectionConfiguration?.selectedAccounts.contains(specificWalletAccount) == true
|
||||
rowView.setup(account: account, isSelected: isSelected, isDisabled: !accountCanBeSelected(account))
|
||||
return rowView
|
||||
case let .mnemonicAccount(walletIndex: walletIndex, accountIndex: accountIndex):
|
||||
let wallet = wallets[walletIndex]
|
||||
let rowView = tableView.makeViewOfType(AccountCellView.self, owner: self)
|
||||
rowView.setup(account: wallet.accounts[accountIndex])
|
||||
let account = wallet.accounts[accountIndex]
|
||||
let specificWalletAccount = SpecificWalletAccount(walletId: wallet.id, account: account)
|
||||
let isSelected = accountSelectionConfiguration?.selectedAccounts.contains(specificWalletAccount) == true
|
||||
rowView.setup(account: account, isSelected: isSelected, isDisabled: !accountCanBeSelected(account))
|
||||
return rowView
|
||||
case .mnemonicWalletHeader:
|
||||
let rowView = tableView.makeViewOfType(AccountsHeaderRowView.self, owner: self)
|
||||
@ -573,7 +708,7 @@ extension AccountsListViewController: NSMenuDelegate {
|
||||
extension AccountsListViewController: NSWindowDelegate {
|
||||
|
||||
func windowWillClose(_ notification: Notification) {
|
||||
callCompletion(wallet: nil, account: nil)
|
||||
callCompletion(specificWalletAccounts: nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ class ApproveTransactionViewController: NSViewController {
|
||||
didSet {
|
||||
peerLogoImageView.wantsLayer = true
|
||||
peerLogoImageView.layer?.backgroundColor = NSColor.systemGray.withAlphaComponent(0.5).cgColor
|
||||
peerLogoImageView.layer?.cornerRadius = 5
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,6 +58,7 @@ class ApproveTransactionViewController: NSViewController {
|
||||
peerLogoImageView.kf.setImage(with: url) { [weak peerLogoImageView] result in
|
||||
if case .success = result {
|
||||
peerLogoImageView?.layer?.backgroundColor = NSColor.clear.cgColor
|
||||
peerLogoImageView?.layer?.cornerRadius = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -66,6 +68,7 @@ class ApproveTransactionViewController: NSViewController {
|
||||
override func viewDidAppear() {
|
||||
super.viewDidAppear()
|
||||
view.window?.delegate = self
|
||||
view.window?.makeFirstResponder(view)
|
||||
}
|
||||
|
||||
private func callCompletion(result: Transaction?) {
|
||||
|
@ -15,6 +15,7 @@ class ApproveViewController: NSViewController {
|
||||
didSet {
|
||||
peerLogoImageView.wantsLayer = true
|
||||
peerLogoImageView.layer?.backgroundColor = NSColor.systemGray.withAlphaComponent(0.5).cgColor
|
||||
peerLogoImageView.layer?.cornerRadius = 5
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,6 +51,7 @@ class ApproveViewController: NSViewController {
|
||||
peerLogoImageView.kf.setImage(with: url) { [weak peerLogoImageView] result in
|
||||
if case .success = result {
|
||||
peerLogoImageView?.layer?.backgroundColor = NSColor.clear.cgColor
|
||||
peerLogoImageView?.layer?.cornerRadius = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -59,6 +61,7 @@ class ApproveViewController: NSViewController {
|
||||
override func viewDidAppear() {
|
||||
super.viewDidAppear()
|
||||
view.window?.delegate = self
|
||||
view.window?.makeFirstResponder(view)
|
||||
}
|
||||
|
||||
func enableWaiting() {
|
||||
|
@ -7,7 +7,7 @@ class EditAccountsViewController: NSViewController {
|
||||
|
||||
var wallet: TokenaryWallet!
|
||||
var getBackToRect: CGRect?
|
||||
var onSelectedWallet: ((EthereumChain?, TokenaryWallet?, Account?) -> Void)?
|
||||
var accountSelectionConfiguration: AccountSelectionConfiguration?
|
||||
|
||||
struct CoinDerivationCellModel {
|
||||
let coinDerivation: CoinDerivation
|
||||
@ -64,7 +64,7 @@ class EditAccountsViewController: NSViewController {
|
||||
|
||||
private func showAccountsList() {
|
||||
let accountsListViewController = instantiate(AccountsListViewController.self)
|
||||
accountsListViewController.onSelectedWallet = onSelectedWallet
|
||||
accountsListViewController.accountSelectionConfiguration = accountSelectionConfiguration
|
||||
accountsListViewController.getBackToRect = getBackToRect
|
||||
view.window?.contentViewController = accountsListViewController
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import WalletCore
|
||||
class ImportViewController: NSViewController {
|
||||
|
||||
private let walletsManager = WalletsManager.shared
|
||||
var onSelectedWallet: ((EthereumChain?, TokenaryWallet?, Account?) -> Void)?
|
||||
var accountSelectionConfiguration: AccountSelectionConfiguration?
|
||||
private var inputValidationResult = WalletsManager.InputValidationResult.invalid
|
||||
|
||||
@IBOutlet weak var textField: NSTextField! {
|
||||
@ -62,7 +62,7 @@ class ImportViewController: NSViewController {
|
||||
|
||||
private func showAccountsList(newWalletId: String?) {
|
||||
let accountsListViewController = instantiate(AccountsListViewController.self)
|
||||
accountsListViewController.onSelectedWallet = onSelectedWallet
|
||||
accountsListViewController.accountSelectionConfiguration = accountSelectionConfiguration
|
||||
accountsListViewController.newWalletId = newWalletId
|
||||
view.window?.contentViewController = accountsListViewController
|
||||
}
|
||||
|
@ -15,9 +15,26 @@ class AccountCellView: NSTableRowView {
|
||||
}
|
||||
@IBOutlet weak var addressTextField: NSTextField!
|
||||
|
||||
func setup(account: Account) {
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
wantsLayer = true
|
||||
}
|
||||
|
||||
func setup(account: Account, isSelected: Bool, isDisabled: Bool) {
|
||||
addressImageView.image = account.image
|
||||
addressTextField.stringValue = account.croppedAddress
|
||||
setSelected(isSelected)
|
||||
setDisabled(isDisabled)
|
||||
}
|
||||
|
||||
private func setDisabled(_ disabled: Bool) {
|
||||
addressImageView.alphaValue = disabled ? 0.4 : 1
|
||||
addressTextField.alphaValue = disabled ? 0.4 : 1
|
||||
}
|
||||
|
||||
private func setSelected(_ selected: Bool) {
|
||||
layer?.backgroundColor = (selected ? NSColor.selectedContentBackgroundColor : NSColor.clear).cgColor
|
||||
addressTextField.textColor = selected ? NSColor.selectedMenuItemTextColor : NSColor.labelColor
|
||||
}
|
||||
|
||||
func blink() {
|
||||
|
@ -71,6 +71,12 @@
|
||||
2C264BE927B5AC6800234393 /* TezosResponseToExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C264BE527B5AC6800234393 /* TezosResponseToExtension.swift */; };
|
||||
2C264BEB27B6B50700234393 /* DappRequestProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C264BEA27B6B50700234393 /* DappRequestProcessor.swift */; };
|
||||
2C264BEC27B6B50700234393 /* DappRequestProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C264BEA27B6B50700234393 /* DappRequestProcessor.swift */; };
|
||||
2C2AA1D228AD1DC100E35DBF /* SpecificWalletAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2AA1D128AD1DC100E35DBF /* SpecificWalletAccount.swift */; };
|
||||
2C2AA1D328AD1DC100E35DBF /* SpecificWalletAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2AA1D128AD1DC100E35DBF /* SpecificWalletAccount.swift */; };
|
||||
2C2AA1D528AFB1AD00E35DBF /* MultipleResponseToExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2AA1D428AFB1AD00E35DBF /* MultipleResponseToExtension.swift */; };
|
||||
2C2AA1D628AFB1AD00E35DBF /* MultipleResponseToExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2AA1D428AFB1AD00E35DBF /* MultipleResponseToExtension.swift */; };
|
||||
2C2AA1D728AFB1AD00E35DBF /* MultipleResponseToExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2AA1D428AFB1AD00E35DBF /* MultipleResponseToExtension.swift */; };
|
||||
2C2AA1D828AFB1AD00E35DBF /* MultipleResponseToExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2AA1D428AFB1AD00E35DBF /* MultipleResponseToExtension.swift */; };
|
||||
2C3B7F022756A08600931264 /* Identifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C3B7F012756A08600931264 /* Identifiers.swift */; };
|
||||
2C40379428199110004C7263 /* Solana.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C40379328199110004C7263 /* Solana.swift */; };
|
||||
2C40379528199110004C7263 /* Solana.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C40379328199110004C7263 /* Solana.swift */; };
|
||||
@ -102,6 +108,8 @@
|
||||
2C6F6D5A28273FE500D6E8FB /* EditAccountsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6F6D5928273FE500D6E8FB /* EditAccountsViewController.swift */; };
|
||||
2C6F6D5D2827434800D6E8FB /* CoinDerivationTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2C6F6D5B2827434800D6E8FB /* CoinDerivationTableViewCell.xib */; };
|
||||
2C6F6D5E2827434800D6E8FB /* CoinDerivationTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6F6D5C2827434800D6E8FB /* CoinDerivationTableViewCell.swift */; };
|
||||
2C71175328AA62DE00ABBF2C /* AccountSelectionConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C71175228AA62DE00ABBF2C /* AccountSelectionConfiguration.swift */; };
|
||||
2C71175428AA62DE00ABBF2C /* AccountSelectionConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C71175228AA62DE00ABBF2C /* AccountSelectionConfiguration.swift */; };
|
||||
2C773F5E27450B97007B04E7 /* ExtensionBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C773F5D27450B97007B04E7 /* ExtensionBridge.swift */; };
|
||||
2C773F5F27450FBD007B04E7 /* ExtensionBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C773F5D27450B97007B04E7 /* ExtensionBridge.swift */; };
|
||||
2C773F62274523DC007B04E7 /* ResponseToExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C773F61274523DC007B04E7 /* ResponseToExtension.swift */; };
|
||||
@ -304,6 +312,8 @@
|
||||
2C264BE027B5AC6000234393 /* SolanaResponseToExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SolanaResponseToExtension.swift; sourceTree = "<group>"; };
|
||||
2C264BE527B5AC6800234393 /* TezosResponseToExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TezosResponseToExtension.swift; sourceTree = "<group>"; };
|
||||
2C264BEA27B6B50700234393 /* DappRequestProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DappRequestProcessor.swift; sourceTree = "<group>"; };
|
||||
2C2AA1D128AD1DC100E35DBF /* SpecificWalletAccount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpecificWalletAccount.swift; sourceTree = "<group>"; };
|
||||
2C2AA1D428AFB1AD00E35DBF /* MultipleResponseToExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipleResponseToExtension.swift; sourceTree = "<group>"; };
|
||||
2C3B7F012756A08600931264 /* Identifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Identifiers.swift; sourceTree = "<group>"; };
|
||||
2C40379328199110004C7263 /* Solana.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Solana.swift; sourceTree = "<group>"; };
|
||||
2C40708E27667A6600AB3D55 /* MultilineLabelTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultilineLabelTableViewCell.swift; sourceTree = "<group>"; };
|
||||
@ -336,6 +346,7 @@
|
||||
2C6F6D5928273FE500D6E8FB /* EditAccountsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAccountsViewController.swift; sourceTree = "<group>"; };
|
||||
2C6F6D5B2827434800D6E8FB /* CoinDerivationTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CoinDerivationTableViewCell.xib; sourceTree = "<group>"; };
|
||||
2C6F6D5C2827434800D6E8FB /* CoinDerivationTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoinDerivationTableViewCell.swift; sourceTree = "<group>"; };
|
||||
2C71175228AA62DE00ABBF2C /* AccountSelectionConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSelectionConfiguration.swift; sourceTree = "<group>"; };
|
||||
2C74386E28297DAC00EC9304 /* near.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; name = near.js; path = "web3-provider/near.js"; sourceTree = "<group>"; };
|
||||
2C773F5D27450B97007B04E7 /* ExtensionBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionBridge.swift; sourceTree = "<group>"; };
|
||||
2C773F61274523DC007B04E7 /* ResponseToExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseToExtension.swift; sourceTree = "<group>"; };
|
||||
@ -567,6 +578,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2C773F61274523DC007B04E7 /* ResponseToExtension.swift */,
|
||||
2C2AA1D428AFB1AD00E35DBF /* MultipleResponseToExtension.swift */,
|
||||
2C264BDB27B5AC5400234393 /* EthereumResponseToExtension.swift */,
|
||||
2C264BE027B5AC6000234393 /* SolanaResponseToExtension.swift */,
|
||||
2C86A266282D1F220028EA11 /* NearResponseToExtension.swift */,
|
||||
@ -860,7 +872,9 @@
|
||||
0D059AD126C2796200EE3023 /* ApprovalSubject.swift */,
|
||||
2C09FC652828331D00DE9C27 /* Image.swift */,
|
||||
2C89D26727BADCA9006C0C8D /* DappRequestAction.swift */,
|
||||
2C71175228AA62DE00ABBF2C /* AccountSelectionConfiguration.swift */,
|
||||
0DC850E626B73A5900809E82 /* AuthenticationReason.swift */,
|
||||
2C2AA1D128AD1DC100E35DBF /* SpecificWalletAccount.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
@ -1319,6 +1333,7 @@
|
||||
2C264BD727B5806200234393 /* Web3Provider.swift in Sources */,
|
||||
2C90E61D27B2C5C100C8991E /* InternalSafariRequest.swift in Sources */,
|
||||
2C86A269282D1F220028EA11 /* NearResponseToExtension.swift in Sources */,
|
||||
2C2AA1D728AFB1AD00E35DBF /* MultipleResponseToExtension.swift in Sources */,
|
||||
2C264BE327B5AC6000234393 /* SolanaResponseToExtension.swift in Sources */,
|
||||
2C773F5E27450B97007B04E7 /* ExtensionBridge.swift in Sources */,
|
||||
2C264BDE27B5AC5400234393 /* EthereumResponseToExtension.swift in Sources */,
|
||||
@ -1338,6 +1353,7 @@
|
||||
files = (
|
||||
2CB4031B281C99C800BAEBEE /* AccountsHeaderRowView.swift in Sources */,
|
||||
2C901C4A2689F01700D0926A /* Strings.swift in Sources */,
|
||||
2C2AA1D228AD1DC100E35DBF /* SpecificWalletAccount.swift in Sources */,
|
||||
2C6706A5267A6BFE006AAEF2 /* Bundle.swift in Sources */,
|
||||
2CC0CDBE2692027E0072922A /* PriceService.swift in Sources */,
|
||||
2C9B7735283FBA18008C191C /* UInt2x.swift in Sources */,
|
||||
@ -1393,6 +1409,7 @@
|
||||
2C264BE127B5AC6000234393 /* SolanaResponseToExtension.swift in Sources */,
|
||||
2C264BBC27B2F25E00234393 /* SafariRequest.swift in Sources */,
|
||||
2CD0B3F526A0DAA900488D92 /* NSPasteboard.swift in Sources */,
|
||||
2C2AA1D528AFB1AD00E35DBF /* MultipleResponseToExtension.swift in Sources */,
|
||||
2C264BEB27B6B50700234393 /* DappRequestProcessor.swift in Sources */,
|
||||
2C264BE627B5AC6800234393 /* TezosResponseToExtension.swift in Sources */,
|
||||
2C10DE4528367637001D8694 /* Near.swift in Sources */,
|
||||
@ -1406,6 +1423,7 @@
|
||||
2C40379428199110004C7263 /* Solana.swift in Sources */,
|
||||
2C8A09DF267579EA00993638 /* AccountsListViewController.swift in Sources */,
|
||||
2C917429267D2A6E00049075 /* Keychain.swift in Sources */,
|
||||
2C71175328AA62DE00ABBF2C /* AccountSelectionConfiguration.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1421,6 +1439,7 @@
|
||||
2CE059372763D60A0042D844 /* KeyboardObserver.swift in Sources */,
|
||||
2CF255A0275A47DD00AE54B9 /* Bundle.swift in Sources */,
|
||||
2CF255AC275A48CF00AE54B9 /* EthereumNetwork.swift in Sources */,
|
||||
2C2AA1D328AD1DC100E35DBF /* SpecificWalletAccount.swift in Sources */,
|
||||
2C40709027667A6600AB3D55 /* MultilineLabelTableViewCell.swift in Sources */,
|
||||
2CF255A6275A48BB00AE54B9 /* GasService.swift in Sources */,
|
||||
2C96D392276232A300687301 /* UITableView.swift in Sources */,
|
||||
@ -1428,6 +1447,7 @@
|
||||
2CF255B6275A746000AE54B9 /* AccountsListViewController.swift in Sources */,
|
||||
2C264BCC27B2F2FF00234393 /* TezosSafariRequest.swift in Sources */,
|
||||
2C96D3A42763C6A800687301 /* UIView.swift in Sources */,
|
||||
2C71175428AA62DE00ABBF2C /* AccountSelectionConfiguration.swift in Sources */,
|
||||
2CF25597275A46D300AE54B9 /* Defaults.swift in Sources */,
|
||||
2CF255A2275A47DD00AE54B9 /* String.swift in Sources */,
|
||||
2CF2559D275A479800AE54B9 /* TokenaryWallet.swift in Sources */,
|
||||
@ -1485,6 +1505,7 @@
|
||||
2C5FF97426C84F7B00B32ACC /* SceneDelegate.swift in Sources */,
|
||||
2CF2559E275A479800AE54B9 /* WalletsManager.swift in Sources */,
|
||||
2C90E62327B2ED2D00C8991E /* SafariRequest+Helpers.swift in Sources */,
|
||||
2C2AA1D628AFB1AD00E35DBF /* MultipleResponseToExtension.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1508,6 +1529,7 @@
|
||||
2C264BD827B5806200234393 /* Web3Provider.swift in Sources */,
|
||||
2C90E61E27B2C5C100C8991E /* InternalSafariRequest.swift in Sources */,
|
||||
2C86A26A282D1F220028EA11 /* NearResponseToExtension.swift in Sources */,
|
||||
2C2AA1D828AFB1AD00E35DBF /* MultipleResponseToExtension.swift in Sources */,
|
||||
2C264BE427B5AC6000234393 /* SolanaResponseToExtension.swift in Sources */,
|
||||
2CE0594327640EAB0042D844 /* ExtensionBridge.swift in Sources */,
|
||||
2C264BDF27B5AC5400234393 /* EthereumResponseToExtension.swift in Sources */,
|
||||
|
78
Tokenary.xcodeproj/xcshareddata/xcschemes/Tokenary.xcscheme
Normal file
78
Tokenary.xcodeproj/xcshareddata/xcschemes/Tokenary.xcscheme
Normal file
@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1340"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "2C19953B2674C4B900A8E370"
|
||||
BuildableName = "Tokenary.app"
|
||||
BlueprintName = "Tokenary"
|
||||
ReferencedContainer = "container:Tokenary.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "2C19953B2674C4B900A8E370"
|
||||
BuildableName = "Tokenary.app"
|
||||
BlueprintName = "Tokenary"
|
||||
ReferencedContainer = "container:Tokenary.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "2C19953B2674C4B900A8E370"
|
||||
BuildableName = "Tokenary.app"
|
||||
BlueprintName = "Tokenary"
|
||||
ReferencedContainer = "container:Tokenary.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
Loading…
Reference in New Issue
Block a user