Merge pull request #2539 from vladyslav-iosdev/#2517

Need to show WalletConnect is loading instead of not showing anything #2517
pull/2527/head
Hwee-Boon Yar 4 years ago committed by GitHub
commit 55184cb27c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 18
      AlphaWallet.xcodeproj/project.pbxproj
  2. 11
      AlphaWallet/Extensions/String.swift
  3. 2
      AlphaWallet/Localization/en.lproj/Localizable.strings
  4. 2
      AlphaWallet/Localization/es.lproj/Localizable.strings
  5. 2
      AlphaWallet/Localization/ja.lproj/Localizable.strings
  6. 2
      AlphaWallet/Localization/ko.lproj/Localizable.strings
  7. 2
      AlphaWallet/Localization/zh-Hans.lproj/Localizable.strings
  8. 8
      AlphaWallet/Tokens/ViewControllers/TokensViewController.swift
  9. 2
      AlphaWallet/WalletConnect/Controllers/TransactionInProgressCoordinatorBridgeToPromise.swift
  10. 52
      AlphaWallet/WalletConnect/Controllers/WalletConnectToSessionCoordinatorBridgeToPromise.swift
  11. 179
      AlphaWallet/WalletConnect/Coordinator/WalletConnectCoordinator.swift
  12. 2
      AlphaWallet/WalletConnect/Coordinator/WalletConnectSessionCoordinator.swift
  13. 112
      AlphaWallet/WalletConnect/Coordinator/WalletConnectToSessionCoordinator.swift
  14. 72
      AlphaWallet/WalletConnect/ViewController/WalletConnectSessionsViewController.swift
  15. 395
      AlphaWallet/WalletConnect/ViewController/WalletConnectToSessionViewController.swift
  16. 84
      AlphaWallet/WalletConnect/ViewModel/WalletConnectToSessionViewModel.swift
  17. 5
      AlphaWallet/WalletConnect/WalletConnectServer.swift

@ -696,6 +696,10 @@
874DED1924C1BD2C006C8FCE /* SelectAssetViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 874DED1824C1BD2C006C8FCE /* SelectAssetViewModel.swift */; };
8750F91724EE5AE100E19DFF /* RecipientResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8750F91624EE5AE100E19DFF /* RecipientResolver.swift */; };
8750F91924EE66D700E19DFF /* GasSpeedTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8750F91824EE66D700E19DFF /* GasSpeedTableViewCell.swift */; };
8757E5E025DE5A9D00812392 /* WalletConnectToSessionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8757E5DF25DE5A9D00812392 /* WalletConnectToSessionViewController.swift */; };
8757E5E225DE5ADB00812392 /* WalletConnectToSessionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8757E5E125DE5ADB00812392 /* WalletConnectToSessionViewModel.swift */; };
8757E5E425DE5EC400812392 /* WalletConnectToSessionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8757E5E325DE5EC400812392 /* WalletConnectToSessionCoordinator.swift */; };
8757E5E625DE676100812392 /* WalletConnectToSessionCoordinatorBridgeToPromise.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8757E5E525DE676100812392 /* WalletConnectToSessionCoordinatorBridgeToPromise.swift */; };
875B3C34250A75FA0085BD08 /* QRCodeResolutionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 875B3C33250A75FA0085BD08 /* QRCodeResolutionCoordinator.swift */; };
8769888D24C6ED04002BF62B /* TransactionInProgressCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8769888C24C6ED04002BF62B /* TransactionInProgressCoordinator.swift */; };
8769BCA8256D15BF0095EA5B /* BlockieImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8769BCA7256D15BF0095EA5B /* BlockieImageView.swift */; };
@ -1542,6 +1546,10 @@
8750F91624EE5AE100E19DFF /* RecipientResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipientResolver.swift; sourceTree = "<group>"; };
8750F91824EE66D700E19DFF /* GasSpeedTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GasSpeedTableViewCell.swift; sourceTree = "<group>"; };
8750F91A24EFEC0300E19DFF /* Uniswap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Uniswap.swift; sourceTree = "<group>"; };
8757E5DF25DE5A9D00812392 /* WalletConnectToSessionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletConnectToSessionViewController.swift; sourceTree = "<group>"; };
8757E5E125DE5ADB00812392 /* WalletConnectToSessionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletConnectToSessionViewModel.swift; sourceTree = "<group>"; };
8757E5E325DE5EC400812392 /* WalletConnectToSessionCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletConnectToSessionCoordinator.swift; sourceTree = "<group>"; };
8757E5E525DE676100812392 /* WalletConnectToSessionCoordinatorBridgeToPromise.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletConnectToSessionCoordinatorBridgeToPromise.swift; sourceTree = "<group>"; };
875B3C33250A75FA0085BD08 /* QRCodeResolutionCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeResolutionCoordinator.swift; sourceTree = "<group>"; };
8769888C24C6ED04002BF62B /* TransactionInProgressCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionInProgressCoordinator.swift; sourceTree = "<group>"; };
8769BCA7256D15BF0095EA5B /* BlockieImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockieImageView.swift; sourceTree = "<group>"; };
@ -3863,6 +3871,7 @@
87BBF96B2563DD7500FF4846 /* WalletConnectSessionDetailsViewModel.swift */,
87BBF96C2563DD7500FF4846 /* WallerConnectRawViewModel.swift */,
5E7C7DC4B06C1A623788EEED /* WalletConnectSessionCellViewModel.swift */,
8757E5E125DE5ADB00812392 /* WalletConnectToSessionViewModel.swift */,
);
path = ViewModel;
sourceTree = "<group>";
@ -3872,6 +3881,7 @@
children = (
87BBF9722563DD7500FF4846 /* WalletConnectSessionCoordinator.swift */,
87BBF9732563DD7500FF4846 /* WalletConnectCoordinator.swift */,
8757E5E325DE5EC400812392 /* WalletConnectToSessionCoordinator.swift */,
);
path = Coordinator;
sourceTree = "<group>";
@ -3903,7 +3913,7 @@
87BBF97E2563DD7500FF4846 /* SignMessageCoordinatorBridgeToPromise.swift */,
87BBF97F2563DD7500FF4846 /* TransactionInProgressCoordinatorBridgeToPromise.swift */,
87BBF9802563DD7500FF4846 /* TransactionConfirmationCoordinatorBridgeToPromise.swift */,
5E7C75F0DB84DB0F5449D6C1 /* WalletConnectSessionsViewController.swift */,
8757E5E525DE676100812392 /* WalletConnectToSessionCoordinatorBridgeToPromise.swift */,
);
path = Controllers;
sourceTree = "<group>";
@ -3911,7 +3921,9 @@
87BBF9812563DD7500FF4846 /* ViewController */ = {
isa = PBXGroup;
children = (
5E7C75F0DB84DB0F5449D6C1 /* WalletConnectSessionsViewController.swift */,
87BBF9822563DD7500FF4846 /* WalletConnectSessionDetailsViewController.swift */,
8757E5DF25DE5A9D00812392 /* WalletConnectToSessionViewController.swift */,
);
path = ViewController;
sourceTree = "<group>";
@ -4543,6 +4555,7 @@
87BBF9962563DD7600FF4846 /* SignMessageCoordinatorBridgeToPromise.swift in Sources */,
AA26C62320412A4100318B9B /* UIViewInspectableEnhancements.swift in Sources */,
29FC9BC61F830899000209CD /* MigrationInitializerForOneChainPerDatabase.swift in Sources */,
8757E5E425DE5EC400812392 /* WalletConnectToSessionCoordinator.swift in Sources */,
29AD8A091F93F8B2008E10E7 /* Session.swift in Sources */,
29BB94971F6FCD60009B09CC /* SendViewModel.swift in Sources */,
296106D01F778A8D0006164B /* TransactionType.swift in Sources */,
@ -4826,6 +4839,7 @@
5E7C75E5C64619ABFD246183 /* TransferTokensCardViaWalletAddressViewController.swift in Sources */,
5E7C7EAEBB435F3909DA36FB /* TransferTokensCardViaWalletAddressViewControllerViewModel.swift in Sources */,
5E7C7CCC8D376C6E5C245715 /* EthCurrencyHelper.swift in Sources */,
8757E5E225DE5ADB00812392 /* WalletConnectToSessionViewModel.swift in Sources */,
5E7C7317533D24B6A292F88D /* UIStackView+Array.swift in Sources */,
5E7C78F1D29280E3FF4EAF5E /* RoundedBackground.swift in Sources */,
5E7C7E68425E20834B898D06 /* AppLocale.swift in Sources */,
@ -4854,6 +4868,7 @@
5E7C741353DDF87133054FCC /* DeletedContract.swift in Sources */,
5E7C7788FA549A0402BB33CB /* HiddenContract.swift in Sources */,
5E7C7F60056FDD6ACC390400 /* UniversalLinkInPasteboardCoordinator.swift in Sources */,
8757E5E625DE676100812392 /* WalletConnectToSessionCoordinatorBridgeToPromise.swift in Sources */,
5E7C7402B29A987B0AF7061D /* VerifiableStatusViewController.swift in Sources */,
76F1DBCA8BAAA42BAEB14719 /* GetERC721BalanceCoordinator.swift in Sources */,
87D175EB24AEF8B5002130D2 /* UITableView.swift in Sources */,
@ -5187,6 +5202,7 @@
5E7C7B1689D3E1D51788FE26 /* Session+PromiseKit.swift in Sources */,
5E7C78C68AD1922796E6C88F /* WalletConnectSessionsViewController.swift in Sources */,
5E7C78C511CD388E882DBD83 /* WalletConnectSessionCell.swift in Sources */,
8757E5E025DE5A9D00812392 /* WalletConnectToSessionViewController.swift in Sources */,
5E7C712B11D9D9AAA02C022F /* WalletConnectSessionCellViewModel.swift in Sources */,
5E7C7B3027F3A56EF3EE5B7F /* EthCalllRequest.swift in Sources */,
5E7C7E38EE25F8837CDF6875 /* TransactionRowViewModel.swift in Sources */,

@ -3,6 +3,17 @@
import Foundation
import UIKit
extension String {
var toHexData: Data {
if self.hasPrefix("0x") {
return Data(hex: self)
} else {
return Data(hex: self.hex)
}
}
}
extension String {
var hex: String {
guard let data = self.data(using: .utf8) else {

@ -29,6 +29,7 @@
"transaction.blockNumber.label.title" = "Block #";
"transaction.nonce.label.title" = "Nonce";
"confirmPayment.confirm.button.title" = "Confirm";
"confirmPayment.reject.button.title" = "Reject";
"confirmPayment.from.label.title" = "From";
"confirmPayment.gasFee.label.title" = "Estimate Network Fee";
"confirmPayment.gasLimit.label.title" = "Gas Limit";
@ -538,4 +539,5 @@ You can check the latest gas price on gasnow.org";
"walletConnect.sendRawTransaction.title" = "Send raw transaction";
"walletConnect.activeSessions" = "Active connection to Dapps";
"walletConnect.activeSessions.plural" = "Active connections to Dapps";
"walletConnect.connection.title" = "Connect To Site?";
"send.allFunds" = "All Funds";

@ -29,6 +29,7 @@
"transaction.blockNumber.label.title" = "Bloque n.º";
"transaction.nonce.label.title" = "Nonce";
"confirmPayment.confirm.button.title" = "Confirmar";
"confirmPayment.reject.button.title" = "Reject";
"confirmPayment.from.label.title" = "De";
"confirmPayment.gasFee.label.title" = "Estimación de la tarifa de red";
"confirmPayment.gasLimit.label.title" = "Límite de gas";
@ -538,4 +539,5 @@ You can check the latest gas price on gasnow.org";
"walletConnect.activeSessions" = "Active connection to Dapps";
"walletConnect.activeSessions.plural" = "Active connections to Dapps";
"walletConnect.sendRawTransaction.title" = "Send raw transaction";
"walletConnect.connection.title" = "Connect To Site?";
"send.allFunds" = "All Funds";

@ -29,6 +29,7 @@
"transaction.blockNumber.label.title" = "ブロック番号";
"transaction.nonce.label.title" = "Nonce";
"confirmPayment.confirm.button.title" = "確認";
"confirmPayment.reject.button.title" = "Reject";
"confirmPayment.from.label.title" = "送信元";
"confirmPayment.gasFee.label.title" = "Estimate Network Fee";
"confirmPayment.gasLimit.label.title" = "ガスの制限";
@ -538,4 +539,5 @@ You can check the latest gas price on gasnow.org";
"walletConnect.sendRawTransaction.title" = "Send raw transaction";
"walletConnect.activeSessions" = "Active connection to Dapps";
"walletConnect.activeSessions.plural" = "Active connections to Dapps";
"walletConnect.connection.title" = "Connect To Site?";
"send.allFunds" = "All Funds";

@ -29,6 +29,7 @@
"transaction.blockNumber.label.title" = "블록 번호";
"transaction.nonce.label.title" = "Nonce";
"confirmPayment.confirm.button.title" = "확인";
"confirmPayment.reject.button.title" = "Reject";
"confirmPayment.from.label.title" = "발신인";
"confirmPayment.gasFee.label.title" = "Estimate Network Fee";
"confirmPayment.gasLimit.label.title" = "가스 한도";
@ -538,4 +539,5 @@ You can check the latest gas price on gasnow.org";
"walletConnect.sendRawTransaction.title" = "Send raw transaction";
"walletConnect.activeSessions" = "Active connection to Dapps";
"walletConnect.activeSessions.plural" = "Active connections to Dapps";
"walletConnect.connection.title" = "Connect To Site?";
"send.allFunds" = "All Funds";

@ -29,6 +29,7 @@
"transaction.blockNumber.label.title" = "区块 #";
"transaction.nonce.label.title" = "Nonce";
"confirmPayment.confirm.button.title" = "确认";
"confirmPayment.reject.button.title" = "Reject";
"confirmPayment.from.label.title" = "来源地址";
"confirmPayment.gasFee.label.title" = "估计网络费用";
"confirmPayment.gasLimit.label.title" = "燃料限制";
@ -538,4 +539,5 @@ You can check the latest gas price on gasnow.org";
"walletConnect.sendRawTransaction.title" = "Send raw transaction";
"walletConnect.activeSessions" = "Active connection to Dapps";
"walletConnect.activeSessions.plural" = "Active connections to Dapps";
"walletConnect.connection.title" = "Connect To Site?";
"send.allFunds" = "All Funds";

@ -240,12 +240,12 @@ class TokensViewController: UIViewController {
navigationItem.rightBarButtonItem = UIBarButtonItem.qrCodeBarButton(self, selector: #selector(scanQRCodeButtonSelected))
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: blockieImageView)
walletConnectCoordinator.walletConnectSessions.subscribe { [weak self] sessions in
guard let strongSelf = self, let sessions = sessions else { return }
if sessions.isEmpty {
walletConnectCoordinator.sessionsToURLServersMap.subscribe { [weak self] value in
guard let strongSelf = self, let sessionsToURLServersMap = value else { return }
if sessionsToURLServersMap.sessions.isEmpty {
strongSelf.sections = [.filters, .addHideToken, .tokens]
} else {
strongSelf.sections = [.filters, .addHideToken, .activeWalletSession(count: sessions.count), .tokens]
strongSelf.sections = [.filters, .addHideToken, .activeWalletSession(count: sessionsToURLServersMap.sessions.count), .tokens]
}
strongSelf.tableView.reloadData()
}

@ -43,7 +43,7 @@ extension TransactionInProgressCoordinatorBridgeToPromise: TransactionInProgress
extension TransactionInProgressCoordinator {
static func promise(navigationController: UINavigationController, coordinator: Coordinator) -> Promise<Void> {
static func promise(_ navigationController: UINavigationController, coordinator: Coordinator) -> Promise<Void> {
return TransactionInProgressCoordinatorBridgeToPromise(navigationController: navigationController, coordinator: coordinator).promise
}
}

@ -0,0 +1,52 @@
//
// WalletConnectToSessionCoordinatorBridgeToPromise.swift
// AlphaWallet
//
// Created by Vladyslav Shepitko on 18.02.2021.
//
import PromiseKit
private class WalletConnectToSessionCoordinatorBridgeToPromise {
private let (promiseToReturn, seal) = Promise<WalletConnectServer.ConnectionChoice>.pending()
private var retainCycle: WalletConnectToSessionCoordinatorBridgeToPromise?
init(navigationController: UINavigationController, coordinator: Coordinator, connection: WalletConnectConnection, serverChoices: [RPCServer]) {
retainCycle = self
let newCoordinator = WalletConnectToSessionCoordinator(connection: connection, navigationController: navigationController, serverChoices: serverChoices)
newCoordinator.delegate = self
coordinator.addCoordinator(newCoordinator)
_ = promiseToReturn.ensure {
// ensure we break the retain cycle
self.retainCycle = nil
coordinator.removeCoordinator(newCoordinator)
}
newCoordinator.start()
}
var promise: Promise<WalletConnectServer.ConnectionChoice> {
return promiseToReturn
}
}
extension WalletConnectToSessionCoordinatorBridgeToPromise: WalletConnectToSessionCoordinatorDelegate {
func coordinator(_ coordinator: WalletConnectToSessionCoordinator, didCompleteWithConnection result: WalletConnectServer.ConnectionChoice) {
seal.fulfill(result)
}
}
extension WalletConnectToSessionCoordinator {
static func promise(_ navigationController: UINavigationController, coordinator: Coordinator, connection: WalletConnectConnection, serverChoices: [RPCServer]) -> Promise<WalletConnectServer.ConnectionChoice> {
return WalletConnectToSessionCoordinatorBridgeToPromise(
navigationController: navigationController,
coordinator: coordinator,
connection: connection,
serverChoices: serverChoices
).promise
}
}

@ -18,6 +18,8 @@ enum SessionsToDisconnect {
case all
}
typealias SessionsToURLServersMap = (sessions: [WalletConnectSession], urlToServer: [WCURL: RPCServer])
class WalletConnectCoordinator: NSObject, Coordinator {
private lazy var server: WalletConnectServer = {
let server = WalletConnectServer(wallet: sessions.anyValue.account.address)
@ -28,9 +30,8 @@ class WalletConnectCoordinator: NSObject, Coordinator {
private let navigationController: UINavigationController
var coordinators: [Coordinator] = []
var walletConnectSessions: Subscribable<[WalletConnectSession]> {
server.sessions
}
var sessionsToURLServersMap: Subscribable<SessionsToURLServersMap> = .init(nil)
private let keystore: Keystore
private let sessions: ServerDictionary<WalletSession>
private let analyticsCoordinator: AnalyticsCoordinator?
@ -40,6 +41,7 @@ class WalletConnectCoordinator: NSObject, Coordinator {
private var serverChoices: [RPCServer] {
ServersCoordinator.serversOrdered.filter { config.enabledServers.contains($0) }
}
private weak var sessionsViewController: WalletConnectSessionsViewController?
init(keystore: Keystore, sessions: ServerDictionary<WalletSession>, navigationController: UINavigationController, analyticsCoordinator: AnalyticsCoordinator?, config: Config, nativeCryptoCurrencyPrices: ServerDictionary<Subscribable<Double>>) {
self.config = config
@ -50,6 +52,12 @@ class WalletConnectCoordinator: NSObject, Coordinator {
self.nativeCryptoCurrencyPrices = nativeCryptoCurrencyPrices
super.init()
start()
server.sessions.subscribe { [weak self, weak server] sessions in
guard let strongSelf = self, let strongServer = server else { return }
strongSelf.sessionsToURLServersMap.value = (sessions ?? [], strongServer.urlToServer)
}
}
//NOTE: we are using disconnection to notify dapp that we get disconnect, in other case dapp still stay connected
@ -86,23 +94,35 @@ class WalletConnectCoordinator: NSObject, Coordinator {
}
func openSession(url: WalletConnectURL) {
try? server.connect(url: url)
navigationController.setNavigationBarHidden(false, animated: true)
showSessions(state: .loading, navigationController: navigationController) {
try? self.server.connect(url: url)
}
}
func showSessionDetails(inNavigationController navigationController: UINavigationController) {
guard let sessions = server.sessions.value else { return }
guard !sessions.isEmpty else { return }
guard let sessions = server.sessions.value, !sessions.isEmpty else { return }
if sessions.count == 1 {
let session = sessions[0]
display(session: session, withNavigationController: navigationController)
} else {
let viewController = WalletConnectSessionsViewController(sessions: server.sessions, urlToServer: server.urlToServer)
viewController.delegate = self
viewController.configure()
navigationController.pushViewController(viewController, animated: true)
showSessions(state: .sessions, navigationController: navigationController)
}
}
private func showSessions(state: WalletConnectSessionsViewController.State, navigationController: UINavigationController, completion: @escaping (() -> Void) = {}) {
let viewController = WalletConnectSessionsViewController(sessionsToURLServersMap: sessionsToURLServersMap)
viewController.delegate = self
viewController.configure(state: state)
self.sessionsViewController = viewController
navigationController.pushViewController(viewController, animated: true, completion: completion)
}
private func display(session: WalletConnectSession, withNavigationController navigationController: UINavigationController) {
let coordinator = WalletConnectSessionCoordinator(navigationController: navigationController, server: server, session: session)
coordinator.delegate = self
@ -118,44 +138,52 @@ extension WalletConnectCoordinator: WalletConnectSessionCoordinatorDelegate {
}
extension WalletConnectCoordinator: WalletConnectServerDelegate {
func server(_ server: WalletConnectServer, action: WalletConnectServer.Action, request: WalletConnectRequest) {
guard let rpcServer = server.urlToServer[request.url] else {
server.reject(request)
return
func server(_ server: WalletConnectServer, didConnect session: WalletConnectSession) {
if let viewController = sessionsViewController {
viewController.set(state: .sessions)
}
let session = sessions[rpcServer]
firstly {
Promise<Void> { seal in
switch session.account.type {
case .real:
seal.fulfill(())
case .watch:
seal.reject(PMKError.cancelled)
}
func server(_ server: WalletConnectServer, action: WalletConnectServer.Action, request: WalletConnectRequest) {
if let rpcServer = server.urlToServer[request.url] {
let session = sessions[rpcServer]
firstly {
Promise<Void> { seal in
switch session.account.type {
case .real:
seal.fulfill(())
case .watch:
seal.reject(PMKError.cancelled)
}
}
}.then { _ -> Promise<WalletConnectServer.Callback> in
let account = session.account.address
switch action.type {
case .signTransaction(let transaction):
return self.executeTransaction(session: session, callbackID: action.id, url: action.url, transaction: transaction, type: .sign)
case .sendTransaction(let transaction):
return self.executeTransaction(session: session, callbackID: action.id, url: action.url, transaction: transaction, type: .signThenSend)
case .signMessage(let hexMessage):
return self.signMessage(with: .message(hexMessage.toHexData), account: account, callbackID: action.id, url: action.url)
case .signPersonalMessage(let hexMessage):
return self.signMessage(with: .personalMessage(hexMessage.toHexData), account: account, callbackID: action.id, url: action.url)
case .signTypedMessageV3(let typedData):
return self.signMessage(with: .eip712v3And4(typedData), account: account, callbackID: action.id, url: action.url)
case .sendRawTransaction(let raw):
return self.sendRawTransaction(session: session, rawTransaction: raw, callbackID: action.id, url: action.url)
case .getTransactionCount:
return self.getTransactionCount(session: session, callbackID: action.id, url: action.url)
case .unknown:
throw PMKError.cancelled
}
}.done { callback in
try? server.fulfill(callback, request: request)
}.catch { _ in
server.reject(request)
}
}.then { _ -> Promise<WalletConnectServer.Callback> in
let account = session.account.address
switch action.type {
case .signTransaction(let transaction):
return self.executeTransaction(session: session, callbackID: action.id, url: action.url, transaction: transaction, type: .sign)
case .sendTransaction(let transaction):
return self.executeTransaction(session: session, callbackID: action.id, url: action.url, transaction: transaction, type: .signThenSend)
case .signMessage(let hexMessage):
return self.signMessage(with: .message(hexMessage.toHexData), account: account, callbackID: action.id, url: action.url)
case .signPersonalMessage(let hexMessage):
return self.signMessage(with: .personalMessage(hexMessage.toHexData), account: account, callbackID: action.id, url: action.url)
case .signTypedMessageV3(let typedData):
return self.signMessage(with: .eip712v3And4(typedData), account: account, callbackID: action.id, url: action.url)
case .sendRawTransaction(let raw):
return self.sendRawTransaction(session: session, rawTransaction: raw, callbackID: action.id, url: action.url)
case .getTransactionCount:
return self.getTransactionCount(session: session, callbackID: action.id, url: action.url)
case .unknown:
throw PMKError.cancelled
}
}.done { callback in
try? server.fulfill(callback, request: request)
}.catch { _ in
} else {
server.reject(request)
}
}
@ -191,7 +219,7 @@ extension WalletConnectCoordinator: WalletConnectServerDelegate {
case .sign:
break
case .signThenSend:
TransactionInProgressCoordinator.promise(navigationController: self.navigationController, coordinator: self).done { _ in
TransactionInProgressCoordinator.promise(self.navigationController, coordinator: self).done { _ in
//no op
}.cauterize()
}
@ -220,7 +248,13 @@ extension WalletConnectCoordinator: WalletConnectServerDelegate {
}
func server(_ server: WalletConnectServer, shouldConnectFor connection: WalletConnectConnection, completion: @escaping (WalletConnectServer.ConnectionChoice) -> Void) {
showConnectToSession(connection: connection, completion: completion)
firstly {
WalletConnectToSessionCoordinator.promise(navigationController, coordinator: self, connection: connection, serverChoices: serverChoices)
}.done { choise in
completion(choise)
}.catch { _ in
completion(.cancel)
}
}
//TODO after we support sendRawTransaction in dapps (and hence a proper UI, be it the actionsheet for transaction confirmation or a simple prompt), let's modify this to use the same flow
@ -284,56 +318,21 @@ extension WalletConnectCoordinator: WalletConnectServerDelegate {
}
}
}
private func showConnectToSession(connection: WalletConnectConnection, completion: @escaping (WalletConnectServer.ConnectionChoice) -> Void) {
let style: UIAlertController.Style = UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet
if let server = connection.server {
let alertViewController = UIAlertController(title: connection.name, message: connection.url.absoluteString, preferredStyle: style)
let startAction = UIAlertAction(title: R.string.localizable.walletConnectSessionConnect(server.name), style: .default) { _ in
completion(.connect(server))
}
let cancelAction = UIAlertAction(title: R.string.localizable.cancel(), style: .cancel) { _ in
completion(.cancel)
}
alertViewController.addAction(startAction)
alertViewController.addAction(cancelAction)
navigationController.present(alertViewController, animated: true)
} else {
let alertViewController = UIAlertController(title: connection.name, message: R.string.localizable.walletConnectStart(connection.url.absoluteString), preferredStyle: style)
for each in serverChoices {
let action = UIAlertAction(title: each.name, style: .default) { _ in
completion(.connect(each))
}
alertViewController.addAction(action)
}
let cancelAction = UIAlertAction(title: R.string.localizable.cancel(), style: .cancel) { _ in
completion(.cancel)
}
alertViewController.addAction(cancelAction)
navigationController.present(alertViewController, animated: true)
}
}
}
extension String {
extension WalletConnectCoordinator: WalletConnectSessionsViewControllerDelegate {
func didClose(in viewController: WalletConnectSessionsViewController) {
//NOTE: even if we haven't sessions view controller pushed to navigation stack, we need to make sure that root NavigationBar will be hidden
navigationController.setNavigationBarHidden(true, animated: true)
var toHexData: Data {
if self.hasPrefix("0x") {
return Data(hex: self)
} else {
return Data(hex: self.hex)
}
guard let navigationController = viewController.navigationController else { return }
navigationController.popViewController(animated: true)
}
}
extension WalletConnectCoordinator: WalletConnectSessionsViewControllerDelegate {
func didSelect(session: WalletConnectSession, in viewController: WalletConnectSessionsViewController) {
guard let navigationController = viewController.navigationController else { return }
display(session: session, withNavigationController: navigationController)
}
}

@ -42,6 +42,7 @@ class WalletConnectSessionCoordinator: Coordinator {
}
extension WalletConnectSessionCoordinator: WalletConnectSessionViewControllerDelegate {
func didDismiss(in controller: WalletConnectSessionViewController) {
guard let delegate = delegate else { return }
navigationController.popViewController(animated: true)
@ -50,6 +51,7 @@ extension WalletConnectSessionCoordinator: WalletConnectSessionViewControllerDel
func controller(_ controller: WalletConnectSessionViewController, disconnectSelected sender: UIButton) {
guard let delegate = delegate else { return }
do {
try server.disconnect(session: session)
} catch {

@ -0,0 +1,112 @@
//
// WalletConnectToSessionCoordinator.swift
// AlphaWallet
//
// Created by Vladyslav Shepitko on 18.02.2021.
//
import UIKit
protocol WalletConnectToSessionCoordinatorDelegate: class {
func coordinator(_ coordinator: WalletConnectToSessionCoordinator, didCompleteWithConnection result: WalletConnectServer.ConnectionChoice)
}
class WalletConnectToSessionCoordinator: Coordinator {
var coordinators: [Coordinator] = []
private let connection: WalletConnectConnection
private let presentationNavigationController: UINavigationController
private lazy var viewModel = WalletConnectToSessionViewModel(connection: connection, serverToConnect: serverToConnect)
private lazy var viewController: WalletConnectToSessionViewController = {
let viewController = WalletConnectToSessionViewController(viewModel: viewModel)
viewController.delegate = self
return viewController
}()
private lazy var navigationController: UINavigationController = {
let controller = UINavigationController(rootViewController: viewController)
controller.modalPresentationStyle = .overFullScreen
controller.modalTransitionStyle = .crossDissolve
controller.view.backgroundColor = UIColor.black.withAlphaComponent(0.6)
return controller
}()
private var serverToConnect: RPCServer
private let serverChoices: [RPCServer]
weak var delegate: WalletConnectToSessionCoordinatorDelegate?
init(connection: WalletConnectConnection, navigationController: UINavigationController, serverChoices: [RPCServer]) {
self.connection = connection
self.serverToConnect = connection.server ?? .main
self.presentationNavigationController = navigationController
self.serverChoices = serverChoices
}
func start() {
presentationNavigationController.present(navigationController, animated: false)
viewController.configure(for: viewModel)
viewController.reloadView()
}
func dissmissAnimated(completion: @escaping () -> Void) {
viewController.dismissViewAnimated {
//Needs a strong self reference otherwise `self` might have been removed by its owner by the time animation completes and the `completion` block not called
self.navigationController.dismiss(animated: true, completion: completion)
}
}
}
extension WalletConnectToSessionCoordinator: WalletConnectToSessionViewControllerDelegate {
func changeConnectionServerSelected(in controller: WalletConnectToSessionViewController) {
showAvailableToConnectServers(completion: { [weak self] result in
guard let strongSelf = self else { return }
switch result {
case .connect(let server):
strongSelf.serverToConnect = server
strongSelf.viewModel.set(serverToConnect: server)
case .cancel:
break
}
strongSelf.viewController.configure(for: strongSelf.viewModel)
strongSelf.viewController.reloadView()
})
}
func controller(_ controller: WalletConnectToSessionViewController, continueButtonTapped sender: UIButton) {
dissmissAnimated(completion: {
guard let delegate = self.delegate else { return }
delegate.coordinator(self, didCompleteWithConnection: .connect(self.serverToConnect))
})
}
func didClose(in controller: WalletConnectToSessionViewController) {
navigationController.dismiss(animated: false) { [weak self] in
guard let strongSelf = self, let delegate = strongSelf.delegate else { return }
delegate.coordinator(strongSelf, didCompleteWithConnection: .cancel)
}
}
private func showAvailableToConnectServers(completion: @escaping (WalletConnectServer.ConnectionChoice) -> Void) {
let style: UIAlertController.Style = UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet
let alertViewController = UIAlertController(title: connection.name, message: R.string.localizable.walletConnectStart(connection.url.absoluteString), preferredStyle: style)
for each in serverChoices {
let action = UIAlertAction(title: each.name, style: .default) { _ in
completion(.connect(each))
}
alertViewController.addAction(action)
}
let cancelAction = UIAlertAction(title: R.string.localizable.cancel(), style: .cancel) { _ in
completion(.cancel)
}
alertViewController.addAction(cancelAction)
navigationController.present(alertViewController, animated: true)
}
}

@ -4,10 +4,25 @@ import UIKit
protocol WalletConnectSessionsViewControllerDelegate: class {
func didSelect(session: WalletConnectSession, in viewController: WalletConnectSessionsViewController)
func didClose(in viewController: WalletConnectSessionsViewController)
}
extension WalletConnectSessionsViewController {
enum State {
case sessions
case loading
}
}
class WalletConnectSessionsViewController: UIViewController {
private let sessions: Subscribable<[WalletConnectSession]>
private var sessionsValue: [WalletConnectSession] {
return sessionsToURLServersMap.value?.sessions ?? []
}
private var urlToServer: [WalletConnectURL: RPCServer] {
return sessionsToURLServersMap.value?.urlToServer ?? [:]
}
private let sessionsToURLServersMap: Subscribable<SessionsToURLServersMap>
private lazy var tableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .plain)
tableView.register(WalletConnectSessionCell.self)
@ -19,49 +34,82 @@ class WalletConnectSessionsViewController: UIViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
return tableView
}()
private let urlToServer: [WalletConnectURL: RPCServer]
weak var delegate: WalletConnectSessionsViewControllerDelegate?
init(sessions: Subscribable<[WalletConnectSession]>, urlToServer: [WalletConnectURL: RPCServer]) {
self.sessions = sessions
self.urlToServer = urlToServer
private lazy var spinner: UIActivityIndicatorView = {
let view = UIActivityIndicatorView(style: .gray)
view.translatesAutoresizingMaskIntoConstraints = false
view.hidesWhenStopped = true
view.tintColor = .red
return view
}()
init(sessionsToURLServersMap: Subscribable<SessionsToURLServersMap>) {
self.sessionsToURLServersMap = sessionsToURLServersMap
// self.urlToServer = urlToServer
super.init(nibName: nil, bundle: nil)
view.addSubview(tableView)
view.addSubview(spinner)
sessions.subscribe { _ in
sessionsToURLServersMap.subscribe { _ in
self.tableView.reloadData()
}
NSLayoutConstraint.activate([
tableView.anchorsConstraint(to: view),
spinner.centerXAnchor.constraint(equalTo: tableView.centerXAnchor),
spinner.centerYAnchor.constraint(equalTo: tableView.centerYAnchor)
])
navigationItem.leftBarButtonItem = UIBarButtonItem.backBarButton(self, selector: #selector(closeButtonSelected))
}
required init?(coder aDecoder: NSCoder) {
nil
}
func configure() {
func configure(state: State) {
navigationItem.largeTitleDisplayMode = .never
hidesBottomBarWhenPushed = true
title = R.string.localizable.walletConnectTitle()
set(state: state)
}
// func set(urlToServer: [WalletConnectURL: RPCServer]) {
// self.urlToServer = urlToServer
// }
func set(state: State) {
switch state {
case .loading:
spinner.startAnimating()
case .sessions:
spinner.stopAnimating()
}
}
@objc private func closeButtonSelected(_ sender: UIBarButtonItem) {
guard let delegate = self.delegate else { return }
delegate.didClose(in: self)
}
}
extension WalletConnectSessionsViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
guard let session = sessions.value?[indexPath.row] else { return }
delegate?.didSelect(session: session, in: self)
delegate?.didSelect(session: sessionsValue[indexPath.row], in: self)
}
}
extension WalletConnectSessionsViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(for: indexPath) as WalletConnectSessionCell
guard let session = sessions.value?[indexPath.row] else { return cell }
let cell: WalletConnectSessionCell = tableView.dequeueReusableCell(for: indexPath)
let session = sessionsValue[indexPath.row]
if let server = urlToServer[session.url] {
let viewModel = WalletConnectSessionCellViewModel(session: session, server: server)
cell.configure(viewModel: viewModel)
@ -72,6 +120,6 @@ extension WalletConnectSessionsViewController: UITableViewDataSource {
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
sessions.value?.count ?? 0
return sessionsValue.count
}
}

@ -0,0 +1,395 @@
//
// WalletConnectToSessionViewController.swift
// AlphaWallet
//
// Created by Vladyslav Shepitko on 17.02.2021.
//
import UIKit
protocol WalletConnectToSessionViewControllerDelegate: class {
func controller(_ controller: WalletConnectToSessionViewController, continueButtonTapped sender: UIButton)
func changeConnectionServerSelected(in controller: WalletConnectToSessionViewController)
func didClose(in controller: WalletConnectToSessionViewController)
}
class WalletConnectToSessionViewController: UIViewController {
private lazy var headerView: HeaderView = HeaderView(viewModel: .init(title: viewModel.navigationTitle))
private let buttonsBar = ButtonsBar(configuration: .custom(types: [.green, .white]))
private var viewModel: WalletConnectToSessionViewModel
private let stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.spacing = 0
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
private lazy var scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(stackView)
return scrollView
}()
private let separatorLine: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = R.color.mercury()
return view
}()
private var contentSizeObservation: NSKeyValueObservation?
private lazy var footerBar: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = viewModel.footerBackgroundColor
view.addSubview(buttonsBar)
return view
}()
private lazy var backgroundView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .clear
let tap = UITapGestureRecognizer(target: self, action: #selector(dismissViewController))
view.isUserInteractionEnabled = true
view.addGestureRecognizer(tap)
return view
}()
private lazy var containerView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
view.addSubview(scrollView)
view.addSubview(footerBar)
view.addSubview(headerView)
view.addSubview(separatorLine)
return view
}()
private lazy var heightConstraint: NSLayoutConstraint = {
return containerView.heightAnchor.constraint(equalToConstant: preferredContentSize.height)
}()
private lazy var bottomConstraint: NSLayoutConstraint = {
containerView.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: view.bottomAnchor)
}()
private var allowPresentationAnimation: Bool = true
private var allowDismissalAnimation: Bool = true
weak var delegate: WalletConnectToSessionViewControllerDelegate?
init(viewModel: WalletConnectToSessionViewModel) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
view.addSubview(backgroundView)
view.addSubview(containerView)
NSLayoutConstraint.activate([
backgroundView.bottomAnchor.constraint(equalTo: containerView.topAnchor),
backgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
backgroundView.topAnchor.constraint(equalTo: view.topAnchor),
backgroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
heightConstraint,
bottomConstraint,
containerView.safeAreaLayoutGuide.leadingAnchor.constraint(equalTo: view.leadingAnchor),
containerView.safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: view.trailingAnchor),
headerView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
headerView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
headerView.topAnchor.constraint(equalTo: containerView.topAnchor),
scrollView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
scrollView.topAnchor.constraint(equalTo: headerView.bottomAnchor),
scrollView.bottomAnchor.constraint(equalTo: footerBar.topAnchor),
stackView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor),
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
separatorLine.heightAnchor.constraint(equalToConstant: DataEntry.Metric.TransactionConfirmation.separatorHeight),
separatorLine.bottomAnchor.constraint(equalTo: footerBar.topAnchor),
separatorLine.leadingAnchor.constraint(equalTo: footerBar.leadingAnchor),
separatorLine.trailingAnchor.constraint(equalTo: footerBar.trailingAnchor),
footerBar.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
footerBar.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
footerBar.heightAnchor.constraint(equalToConstant: DataEntry.Metric.TransactionConfirmation.footerHeight),
footerBar.bottomAnchor.constraint(equalTo: containerView.safeAreaLayoutGuide.bottomAnchor),
buttonsBar.topAnchor.constraint(equalTo: footerBar.topAnchor, constant: 20),
buttonsBar.leadingAnchor.constraint(equalTo: footerBar.leadingAnchor),
buttonsBar.trailingAnchor.constraint(equalTo: footerBar.trailingAnchor),
buttonsBar.heightAnchor.constraint(equalToConstant: ButtonsBar.buttonsHeight),
])
headerView.closeButton.addTarget(self, action: #selector(dismissViewController), for: .touchUpInside)
contentSizeObservation = scrollView.observe(\.contentSize, options: [.new, .initial]) { [weak self] scrollView, _ in
guard let strongSelf = self, strongSelf.allowDismissalAnimation else { return }
let statusBarHeight = UIApplication.shared.statusBarFrame.height
let contentHeight = scrollView.contentSize.height + DataEntry.Metric.TransactionConfirmation.footerHeight + DataEntry.Metric.TransactionConfirmation.headerHeight + UIApplication.shared.bottomSafeAreaHeight
let newHeight = min(UIScreen.main.bounds.height - statusBarHeight, contentHeight)
let fillScreenPercentage = strongSelf.heightConstraint.constant / strongSelf.view.bounds.height
if fillScreenPercentage >= 0.9 {
strongSelf.heightConstraint.constant = strongSelf.containerView.bounds.height
} else {
strongSelf.heightConstraint.constant = newHeight
}
}
generateSubviews()
}
override func viewDidLoad() {
super.viewDidLoad()
configure(for: viewModel)
//NOTE: to display animation correctly we can take 'view.frame.height' and bottom view will smoothly slide up from button ;)
bottomConstraint.constant = view.frame.height
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let navigationController = navigationController {
navigationController.setNavigationBarHidden(true, animated: false)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
presentViewAnimated()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let navigationController = navigationController {
navigationController.setNavigationBarHidden(false, animated: false)
}
}
private func presentViewAnimated() {
guard allowPresentationAnimation else { return }
allowPresentationAnimation = false
bottomConstraint.constant = 0
UIView.animate(withDuration: 0.4) {
self.view.layoutIfNeeded()
}
}
func dismissViewAnimated(with completion: @escaping () -> Void) {
guard allowDismissalAnimation else { return }
allowDismissalAnimation = false
bottomConstraint.constant = heightConstraint.constant
UIView.animate(withDuration: 0.4, animations: {
self.view.layoutIfNeeded()
}, completion: { _ in
completion()
})
}
@objc private func dismissViewController() {
dismissViewAnimated(with: {
//NOTE: strong reff is required
self.delegate?.didClose(in: self)
})
}
func reloadView() {
generateSubviews()
}
func configure(for viewModel: WalletConnectToSessionViewModel) {
self.viewModel = viewModel
scrollView.backgroundColor = viewModel.backgroundColor
view.backgroundColor = viewModel.backgroundColor
navigationItem.title = viewModel.title
buttonsBar.configure()
let button1 = buttonsBar.buttons[0]
button1.shrinkBorderColor = Colors.loadingIndicatorBorder
button1.setTitle(viewModel.confirmationButtonTitle, for: .normal)
button1.addTarget(self, action: #selector(confirmButtonTapped), for: .touchUpInside)
let button2 = buttonsBar.buttons[1]
button2.shrinkBorderColor = Colors.loadingIndicatorBorder
button2.setTitle(viewModel.rejectionButtonTitle, for: .normal)
button2.addTarget(self, action: #selector(dismissViewController), for: .touchUpInside)
}
@objc private func confirmButtonTapped(_ sender: UIButton) {
delegate?.controller(self, continueButtonTapped: sender)
}
required init?(coder aDecoder: NSCoder) {
return nil
}
}
fileprivate struct HeaderViewModel {
let title: String
var backgroundColor: UIColor {
Colors.appBackground
}
var icon: UIImage? {
return R.image.awLogoSmall()
}
var attributedTitle: NSAttributedString {
let style = NSMutableParagraphStyle()
style.alignment = .center
return .init(string: title, attributes: [
.font: DataEntry.Font.text as Any,
.paragraphStyle: style,
.foregroundColor: Colors.darkGray
])
}
}
fileprivate class HeaderView: UIView {
private let separatorLine: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = R.color.mercury()
return view
}()
private let titleLabel: UILabel = {
let titleLabel = UILabel(frame: .zero)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
return titleLabel
}()
private let iconImageView: UIImageView = {
let imageView = UIImageView(frame: .zero)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
return imageView
}()
let closeButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.contentMode = .scaleAspectFit
button.setImage(R.image.close(), for: .normal)
return button
}()
init(viewModel: HeaderViewModel) {
super.init(frame: .zero)
translatesAutoresizingMaskIntoConstraints = false
addSubview(separatorLine)
addSubview(titleLabel)
addSubview(iconImageView)
addSubview(closeButton)
NSLayoutConstraint.activate([
separatorLine.heightAnchor.constraint(equalToConstant: DataEntry.Metric.TransactionConfirmation.separatorHeight),
separatorLine.bottomAnchor.constraint(equalTo: bottomAnchor),
separatorLine.leadingAnchor.constraint(equalTo: leadingAnchor),
separatorLine.trailingAnchor.constraint(equalTo: trailingAnchor),
titleLabel.leadingAnchor.constraint(equalTo: iconImageView.trailingAnchor),
titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
titleLabel.trailingAnchor.constraint(equalTo: closeButton.leadingAnchor),
iconImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
iconImageView.centerYAnchor.constraint(equalTo: centerYAnchor),
iconImageView.widthAnchor.constraint(equalToConstant: 30),
iconImageView.heightAnchor.constraint(equalToConstant: 30),
closeButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),
closeButton.centerYAnchor.constraint(equalTo: centerYAnchor),
closeButton.widthAnchor.constraint(equalToConstant: 30),
closeButton.heightAnchor.constraint(equalToConstant: 30),
heightAnchor.constraint(equalToConstant: DataEntry.Metric.TransactionConfirmation.headerHeight)
])
titleLabel.attributedText = viewModel.attributedTitle
iconImageView.image = viewModel.icon
backgroundColor = viewModel.backgroundColor
}
required init?(coder: NSCoder) {
return nil
}
}
extension WalletConnectToSessionViewController {
private func generateSubviews() {
stackView.removeAllArrangedSubviews()
var views: [UIView] = []
for (sectionIndex, section) in viewModel.sections.enumerated() {
let header = TransactionConfirmationHeaderView(viewModel: viewModel.headerViewModel(section: sectionIndex))
header.delegate = self
switch section {
case .name, .url:
break
case .network:
if viewModel.allowChangeConnectionServer {
header.enableTapAction(title: "Edit")
}
}
views.append(header)
}
stackView.addArrangedSubviews(views)
}
}
extension WalletConnectToSessionViewController: TransactionConfirmationHeaderViewDelegate {
func headerView(_ header: TransactionConfirmationHeaderView, shouldHideChildren section: Int, index: Int) -> Bool {
return true
}
func headerView(_ header: TransactionConfirmationHeaderView, shouldShowChildren section: Int, index: Int) -> Bool {
return false
}
func headerView(_ header: TransactionConfirmationHeaderView, openStateChanged section: Int) {
//no-op
}
func headerView(_ header: TransactionConfirmationHeaderView, tappedSection section: Int) {
delegate?.changeConnectionServerSelected(in: self)
}
}

@ -0,0 +1,84 @@
//
// SignatureConfirmationConfirmationViewModel.swift
// AlphaWallet
//
// Created by Vladyslav Shepitko on 17.02.2021.
//
import UIKit
struct WalletConnectToSessionViewModel {
private let connection: WalletConnectConnection
private var serverToConnect: RPCServer
init(connection: WalletConnectConnection, serverToConnect: RPCServer) {
self.connection = connection
self.serverToConnect = serverToConnect
}
mutating func set(serverToConnect: RPCServer) {
self.serverToConnect = serverToConnect
}
var navigationTitle: String {
return R.string.localizable.walletConnectConnectionTitle()
}
var title: String {
return R.string.localizable.confirmPaymentConfirmButtonTitle()
}
var confirmationButtonTitle: String {
return R.string.localizable.confirmPaymentConfirmButtonTitle()
}
var rejectionButtonTitle: String {
return R.string.localizable.confirmPaymentRejectButtonTitle()
}
var backgroundColor: UIColor {
return UIColor.clear
}
var footerBackgroundColor: UIColor {
return R.color.white()!
}
var sections: [Section] {
Section.allCases
}
enum Section: CaseIterable {
case name
case network
case url
var title: String {
switch self {
case .name:
return "Name"
case .network:
return "Network"
case .url:
return "Connected To"
}
}
}
var allowChangeConnectionServer: Bool {
return connection.server == nil
}
func headerViewModel(section: Int) -> TransactionConfirmationHeaderViewModel {
let headerName = sections[section].title
switch sections[section] {
case .name:
return .init(title: .normal(connection.name), headerName: headerName, configuration: .init(section: section))
case .network:
return .init(title: .normal(serverToConnect.displayName), headerName: headerName, configuration: .init(section: section))
case .url:
return .init(title: .normal(connection.url.absoluteString), headerName: headerName, configuration: .init(section: section))
}
}
}

@ -17,6 +17,7 @@ enum WalletConnectError: Error {
}
protocol WalletConnectServerDelegate: class {
func server(_ server: WalletConnectServer, didConnect session: WalletConnectSession)
func server(_ server: WalletConnectServer, shouldConnectFor connection: WalletConnectConnection, completion: @escaping (WalletConnectServer.ConnectionChoice) -> Void)
func server(_ server: WalletConnectServer, action: WalletConnectServer.Action, request: WalletConnectRequest)
func server(_ server: WalletConnectServer, didFail error: Error)
@ -289,6 +290,10 @@ extension WalletConnectServer: ServerDelegate {
UserDefaults.standard.walletConnectSessions = sessions
self.refresh(sessions: sessions)
if let delegate = self.delegate {
delegate.server(self, didConnect: session)
}
}
}

Loading…
Cancel
Save