From 25e68fc21a09eeeece6292ef7a65b9787d5ea102 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Date: Mon, 11 Nov 2019 01:26:39 -0500 Subject: [PATCH 001/311] OPS: upgrade rnwatch OPS: react-native-watch-connectivity-0.4.1 FIX FIX Update WatchConnectivity.ios.js Update WatchConnectivity.ios.js ADD: Add Onchain address view for Lightning Update package.json FIX: Fix communication bug FIX: Fix communication bug Update Info.plist FIX: Update for watch Update Interface.storyboard TST Update WatchConnectivity.android.js Update WatchConnectivity.ios.js --- WatchConnectivity.android.js | 12 ++ ...onnectivity.js => WatchConnectivity.ios.js | 101 ++++++----- class/abstract-hd-wallet.js | 6 +- class/app-storage.js | 17 +- ios/BlueWallet/Info.plist | 15 ++ .../InterfaceController.swift | 4 - .../Objects/WatchDataSource.swift | 16 +- .../ReceiveInterfaceController.swift | 13 +- .../SpecifyInterfaceController.swift | 7 + .../WalletDetailsInterfaceController.swift | 14 +- .../Base.lproj/Interface.storyboard | 35 ++-- ios/Podfile.lock | 12 +- package-lock.json | 164 +++++++++--------- package.json | 2 +- screen/wallets/transactions.js | 2 +- 15 files changed, 249 insertions(+), 171 deletions(-) create mode 100644 WatchConnectivity.android.js rename WatchConnectivity.js => WatchConnectivity.ios.js (60%) diff --git a/WatchConnectivity.android.js b/WatchConnectivity.android.js new file mode 100644 index 0000000000..f7156199ea --- /dev/null +++ b/WatchConnectivity.android.js @@ -0,0 +1,12 @@ +export default class WatchConnectivity { + isAppInstalled = false; + static shared = new WatchConnectivity(); + wallets; + fetchTransactionsFunction = () => {}; + + getIsWatchAppInstalled() {} + + async handleLightningInvoiceCreateRequest(_walletIndex, _amount, _description) {} + + async sendWalletsToWatch() {} +} \ No newline at end of file diff --git a/WatchConnectivity.js b/WatchConnectivity.ios.js similarity index 60% rename from WatchConnectivity.js rename to WatchConnectivity.ios.js index 1e6a18348c..32402f0b5f 100644 --- a/WatchConnectivity.js +++ b/WatchConnectivity.ios.js @@ -1,42 +1,51 @@ import * as Watch from 'react-native-watch-connectivity'; -import { InteractionManager, Platform } from 'react-native'; +import { InteractionManager } from 'react-native'; const loc = require('./loc'); export default class WatchConnectivity { isAppInstalled = false; - BlueApp = require('./BlueApp'); + static shared = new WatchConnectivity(); + wallets; + fetchTransactionsFunction = () => {}; constructor() { - if (Platform.OS === 'ios') { - this.getIsWatchAppInstalled(); - } + this.getIsWatchAppInstalled(); } getIsWatchAppInstalled() { - if (Platform.OS !== 'ios') return; Watch.getIsWatchAppInstalled((err, isAppInstalled) => { if (!err) { - this.isAppInstalled = isAppInstalled; - this.sendWalletsToWatch(); - } - }); - Watch.subscribeToMessages(async (err, message, reply) => { - if (!err) { - if (message.request === 'createInvoice') { - const createInvoiceRequest = await this.handleLightningInvoiceCreateRequest( - message.walletIndex, - message.amount, - message.description, - ); - reply({ invoicePaymentRequest: createInvoiceRequest }); - } - } else { - reply(err); + WatchConnectivity.shared.isAppInstalled = isAppInstalled; + Watch.subscribeToWatchState((err, watchState) => { + if (!err) { + if (watchState === 'Activated') { + WatchConnectivity.shared.sendWalletsToWatch(); + } + } + }); + Watch.subscribeToMessages(async (err, message, reply) => { + if (!err) { + if (message.request === 'createInvoice') { + const createInvoiceRequest = await this.handleLightningInvoiceCreateRequest( + message.walletIndex, + message.amount, + message.description, + ); + reply({ invoicePaymentRequest: createInvoiceRequest }); + } else if (message.message === 'sendApplicationContext') { + await WatchConnectivity.shared.sendWalletsToWatch(WatchConnectivity.shared.wallets); + } else if (message.message === 'fetchTransactions') { + await WatchConnectivity.shared.fetchTransactionsFunction(); + } + } else { + reply(err); + } + }); } }); } async handleLightningInvoiceCreateRequest(walletIndex, amount, description) { - const wallet = this.BlueApp.getWallets()[walletIndex]; + const wallet = WatchConnectivity.shared.wallets[walletIndex]; if (wallet.allowReceive() && amount > 0 && description.trim().length > 0) { try { const invoiceRequest = await wallet.addInvoice(amount, description); @@ -47,18 +56,31 @@ export default class WatchConnectivity { } } - async sendWalletsToWatch() { - if (Platform.OS !== 'ios') return; - InteractionManager.runAfterInteractions(async () => { - if (this.isAppInstalled) { - const allWallets = this.BlueApp.getWallets(); + async sendWalletsToWatch(allWallets) { + if (allWallets === undefined && WatchConnectivity.shared.wallets !== undefined) { + allWallets = WatchConnectivity.shared.wallets; + } + if (allWallets && allWallets.length === 0) { + return; + } + + return InteractionManager.runAfterInteractions(async () => { + console.warn(WatchConnectivity.shared.isAppInstalled); + + if (WatchConnectivity.shared.isAppInstalled) { let wallets = []; + for (const wallet of allWallets) { let receiveAddress = ''; if (wallet.allowReceive()) { if (wallet.getAddressAsync) { - await wallet.getAddressAsync(); - receiveAddress = wallet.getAddress(); + try { + await wallet.getAddressAsync(); + receiveAddress = wallet.getAddress(); + } catch (error) { + console.log(error); + receiveAddress = wallet.getAddress(); + } } else { receiveAddress = wallet.getAddress(); } @@ -70,7 +92,7 @@ export default class WatchConnectivity { let memo = ''; let amount = 0; - if (transaction.hasOwnProperty('confirmations') && !transaction.confirmations > 0) { + if (transaction.hasOwnProperty('confirmations') && !(transaction.confirmations > 0)) { type = 'pendingConfirmation'; } else if (transaction.type === 'user_invoice' || transaction.type === 'payment_request') { const currentDate = new Date(); @@ -92,9 +114,7 @@ export default class WatchConnectivity { type = 'received'; } if (transaction.type === 'user_invoice' || transaction.type === 'payment_request') { - if (isNaN(transaction.value)) { - amount = '0'; - } + amount = isNaN(transaction.value) ? '0' : amount; const currentDate = new Date(); const now = (currentDate.getTime() / 1000) | 0; const invoiceExpiration = transaction.timestamp + transaction.expire_time; @@ -113,8 +133,8 @@ export default class WatchConnectivity { } else { amount = loc.formatBalance(transaction.value, wallet.getPreferredBalanceUnit(), true).toString(); } - if (this.BlueApp.tx_metadata[transaction.hash] && this.BlueApp.tx_metadata[transaction.hash]['memo']) { - memo = this.BlueApp.tx_metadata[transaction.hash]['memo']; + if (WatchConnectivity.shared.tx_metadata[transaction.hash] && WatchConnectivity.shared.tx_metadata[transaction.hash]['memo']) { + memo = WatchConnectivity.shared.tx_metadata[transaction.hash]['memo']; } else if (transaction.memo) { memo = transaction.memo; } @@ -130,14 +150,9 @@ export default class WatchConnectivity { transactions: watchTransactions, }); } - - Watch.updateApplicationContext({ wallets }); + Watch.updateApplicationContext({ wallets, randomID: Math.floor(Math.random() * 11) }); + return { wallets }; } }); } } - -WatchConnectivity.init = function() { - if (WatchConnectivity.shared || Platform.OS !== 'ios') return; - WatchConnectivity.shared = new WatchConnectivity(); -}; diff --git a/class/abstract-hd-wallet.js b/class/abstract-hd-wallet.js index 1d140f54e5..b0ca855741 100644 --- a/class/abstract-hd-wallet.js +++ b/class/abstract-hd-wallet.js @@ -138,7 +138,7 @@ export class AbstractHDWallet extends LegacyWallet { freeAddress = this._getExternalAddressByIndex(this.next_free_address_index + c); // we didnt check this one, maybe its free this.next_free_address_index += c + 1; // now points to the one _after_ } - + this._address = freeAddress; return freeAddress; } @@ -176,7 +176,7 @@ export class AbstractHDWallet extends LegacyWallet { freeAddress = this._getExternalAddressByIndex(this.next_free_address_index + c); // we didnt check this one, maybe its free this.next_free_address_index += c + 1; // now points to the one _after_ } - + this._address = freeAddress; return freeAddress; } @@ -187,7 +187,7 @@ export class AbstractHDWallet extends LegacyWallet { * @return {string} */ getAddress() { - return ''; + return this._address; } _getExternalWIFByIndex(index) { diff --git a/class/app-storage.js b/class/app-storage.js index a4e67836de..2b0f78b818 100644 --- a/class/app-storage.js +++ b/class/app-storage.js @@ -244,8 +244,13 @@ export class AppStorage { this.tx_metadata = data.tx_metadata; } } - WatchConnectivity.init(); - WatchConnectivity.shared && (await WatchConnectivity.shared.sendWalletsToWatch()); + WatchConnectivity.shared.wallets = this.wallets; + WatchConnectivity.shared.tx_metadata = this.tx_metadata; + WatchConnectivity.shared.fetchTransactionsFunction = async () => { + await this.fetchWalletTransactions(); + await this.saveToDisk(); + }; + await WatchConnectivity.shared.sendWalletsToWatch(this.wallets); return true; } else { return false; // failed loading data or loading/decryptin data @@ -265,6 +270,7 @@ export class AppStorage { deleteWallet(wallet) { let secret = wallet.getSecret(); let tempWallets = []; + for (let value of this.wallets) { if (value.getSecret() === secret) { // the one we should delete @@ -318,8 +324,9 @@ export class AppStorage { } else { await this.setItem(AppStorage.FLAG_ENCRYPTED, ''); // drop the flag } - WatchConnectivity.init(); - WatchConnectivity.shared && WatchConnectivity.shared.sendWalletsToWatch(); + WatchConnectivity.shared.wallets = this.wallets; + WatchConnectivity.shared.tx_metadata = this.tx_metadata; + await WatchConnectivity.shared.sendWalletsToWatch(); return this.setItem('data', JSON.stringify(data)); } @@ -455,4 +462,4 @@ export class AppStorage { async sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } -} \ No newline at end of file +} diff --git a/ios/BlueWallet/Info.plist b/ios/BlueWallet/Info.plist index 0ec27310fb..ff5dfd7fa7 100644 --- a/ios/BlueWallet/Info.plist +++ b/ios/BlueWallet/Info.plist @@ -47,6 +47,21 @@ NSExceptionDomains + electrum3.bluewallet.io + + NSExceptionAllowsInsecureHTTPLoads + + + electrum2.bluewallet.io + + NSExceptionAllowsInsecureHTTPLoads + + + electrum1.bluewallet.io + + NSExceptionAllowsInsecureHTTPLoads + + localhost NSExceptionAllowsInsecureHTTPLoads diff --git a/ios/BlueWalletWatch Extension/InterfaceController.swift b/ios/BlueWalletWatch Extension/InterfaceController.swift index ce715a16f7..946d753b69 100644 --- a/ios/BlueWalletWatch Extension/InterfaceController.swift +++ b/ios/BlueWalletWatch Extension/InterfaceController.swift @@ -19,7 +19,6 @@ class InterfaceController: WKInterfaceController { override func willActivate() { // This method is called when watch view controller is about to be visible to user super.willActivate() - WCSession.default.sendMessage(["message" : "sendApplicationContext"], replyHandler: nil, errorHandler: nil) if (WatchDataSource.shared.wallets.isEmpty) { loadingIndicatorGroup.setHidden(true) @@ -31,8 +30,6 @@ class InterfaceController: WKInterfaceController { } @objc private func processWalletsTable() { - loadingIndicatorGroup.setHidden(false) - walletsTable.setHidden(true) walletsTable.setNumberOfRows(WatchDataSource.shared.wallets.count, withRowType: WalletInformation.identifier) for index in 0.. identifier else { + guard let passedContext = context as? (Int, String), WatchDataSource.shared.wallets.count >= passedContext.0 else { pop() return } + let identifier = passedContext.0 let wallet = WatchDataSource.shared.wallets[identifier] self.wallet = wallet + receiveMethod = passedContext.1 NotificationCenter.default.addObserver(forName: SpecifyInterfaceController.NotificationName.createQRCode, object: nil, queue: nil) { [weak self] (notification) in self?.isRenderingQRCode = true - if let wallet = self?.wallet, wallet.type == "lightningCustodianWallet", let object = notification.object as? SpecifyInterfaceController.SpecificQRCodeContent, let amount = object.amount { + if let wallet = self?.wallet, wallet.type == "lightningCustodianWallet", self?.receiveMethod == "createInvoice", let object = notification.object as? SpecifyInterfaceController.SpecificQRCodeContent, let amount = object.amount { self?.imageInterface.setHidden(true) self?.loadingIndicator.setHidden(false) WatchDataSource.requestLightningInvoice(walletIdentifier: identifier, amount: amount, description: object.description, responseHandler: { (invoice) in @@ -43,6 +47,7 @@ class ReceiveInterfaceController: WKInterfaceController { self?.imageInterface.setHidden(false) self?.imageInterface.setImage(nil) self?.imageInterface.setImage(image) + WCSession.default.sendMessage(["message": "fetchTransactions"], replyHandler: nil, errorHandler: nil) } else { self?.pop() self?.presentAlert(withTitle: "Error", message: "Unable to create invoice. Please, make sure your iPhone is paired and nearby.", preferredStyle: .alert, actions: [WKAlertAction(title: "OK", style: .default, handler: { [weak self] in @@ -83,7 +88,7 @@ class ReceiveInterfaceController: WKInterfaceController { } guard !wallet.receiveAddress.isEmpty, let cgImage = EFQRCode.generate( - content: wallet.receiveAddress) else { + content: wallet.receiveAddress), receiveMethod != "createInvoice" else { return } @@ -93,7 +98,7 @@ class ReceiveInterfaceController: WKInterfaceController { override func didAppear() { super.didAppear() - if wallet?.type == "lightningCustodianWallet" { + if wallet?.type == "lightningCustodianWallet" && receiveMethod == "createInvoice" { if isRenderingQRCode == nil { presentController(withName: SpecifyInterfaceController.identifier, context: wallet?.identifier) isRenderingQRCode = false diff --git a/ios/BlueWalletWatch Extension/SpecifyInterfaceController.swift b/ios/BlueWalletWatch Extension/SpecifyInterfaceController.swift index 50c4faccfa..6353556a1c 100644 --- a/ios/BlueWalletWatch Extension/SpecifyInterfaceController.swift +++ b/ios/BlueWalletWatch Extension/SpecifyInterfaceController.swift @@ -14,6 +14,8 @@ class SpecifyInterfaceController: WKInterfaceController { static let identifier = "SpecifyInterfaceController" @IBOutlet weak var descriptionButton: WKInterfaceButton! @IBOutlet weak var amountButton: WKInterfaceButton! + @IBOutlet weak var createButton: WKInterfaceButton! + struct SpecificQRCodeContent { var amount: Double? var description: String? @@ -36,6 +38,7 @@ class SpecifyInterfaceController: WKInterfaceController { } let wallet = WatchDataSource.shared.wallets[identifier] self.wallet = wallet + self.createButton.setAlpha(0.5) self.specifiedQRContent.bitcoinUnit = wallet.type == "lightningCustodianWallet" ? .SATS : .BTC NotificationCenter.default.addObserver(forName: NumericKeypadInterfaceController.NotificationName.keypadDataChanged, object: nil, queue: nil) { [weak self] (notification) in guard let amountObject = notification.object as? [String], !amountObject.isEmpty else { return } @@ -53,6 +56,10 @@ class SpecifyInterfaceController: WKInterfaceController { if let amountDouble = Double(title), let keyPadType = self?.specifiedQRContent.bitcoinUnit { self?.specifiedQRContent.amount = amountDouble self?.amountButton.setTitle("\(title) \(keyPadType)") + + let isShouldCreateButtonBeEnabled = amountDouble > 0 && !title.isEmpty + self?.createButton.setEnabled(isShouldCreateButtonBeEnabled) + self?.createButton.setAlpha(isShouldCreateButtonBeEnabled ? 1.0 : 0.5) } } } diff --git a/ios/BlueWalletWatch Extension/WalletDetailsInterfaceController.swift b/ios/BlueWalletWatch Extension/WalletDetailsInterfaceController.swift index aedd0107fb..7bd0bc4b6f 100644 --- a/ios/BlueWalletWatch Extension/WalletDetailsInterfaceController.swift +++ b/ios/BlueWalletWatch Extension/WalletDetailsInterfaceController.swift @@ -16,11 +16,13 @@ class WalletDetailsInterfaceController: WKInterfaceController { static let identifier = "WalletDetailsInterfaceController" @IBOutlet weak var walletBasicsGroup: WKInterfaceGroup! @IBOutlet weak var walletBalanceLabel: WKInterfaceLabel! + @IBOutlet weak var createInvoiceButton: WKInterfaceButton! @IBOutlet weak var walletNameLabel: WKInterfaceLabel! @IBOutlet weak var receiveButton: WKInterfaceButton! @IBOutlet weak var noTransactionsLabel: WKInterfaceLabel! @IBOutlet weak var transactionsTable: WKInterfaceTable! + override func awake(withContext context: Any?) { super.awake(withContext: context) guard let identifier = context as? Int else { @@ -32,7 +34,7 @@ class WalletDetailsInterfaceController: WKInterfaceController { walletBalanceLabel.setText(wallet.balance) walletNameLabel.setText(wallet.label) walletBasicsGroup.setBackgroundImageNamed(WalletGradient(rawValue: wallet.type)?.imageString) - + createInvoiceButton.setHidden(wallet.type != "lightningCustodianWallet") processWalletsTable() } @@ -40,12 +42,14 @@ class WalletDetailsInterfaceController: WKInterfaceController { super.willActivate() transactionsTable.setHidden(wallet?.transactions.isEmpty ?? true) noTransactionsLabel.setHidden(!(wallet?.transactions.isEmpty ?? false)) + receiveButton.setHidden(wallet?.receiveAddress.isEmpty ?? true) } @IBAction func receiveMenuItemTapped() { - presentController(withName: ReceiveInterfaceController.identifier, context: wallet) + presentController(withName: ReceiveInterfaceController.identifier, context: (wallet, "receive")) } + @objc private func processWalletsTable() { transactionsTable.setNumberOfRows(wallet?.transactions.count ?? 0, withRowType: TransactionTableRow.identifier) @@ -61,8 +65,12 @@ class WalletDetailsInterfaceController: WKInterfaceController { noTransactionsLabel.setHidden(!(wallet?.transactions.isEmpty ?? false)) } + @IBAction func createInvoiceTapped() { + pushController(withName: ReceiveInterfaceController.identifier, context: (wallet?.identifier, "createInvoice")) + } + override func contextForSegue(withIdentifier segueIdentifier: String) -> Any? { - return wallet?.identifier + return (wallet?.identifier, "receive") } } diff --git a/ios/BlueWalletWatch/Base.lproj/Interface.storyboard b/ios/BlueWalletWatch/Base.lproj/Interface.storyboard index 14d93cbc8f..9f8e41a4a1 100644 --- a/ios/BlueWalletWatch/Base.lproj/Interface.storyboard +++ b/ios/BlueWalletWatch/Base.lproj/Interface.storyboard @@ -1,18 +1,16 @@ - - - - + + - - + + - + @@ -74,7 +72,7 @@ - +
@@ -125,6 +131,7 @@
+ @@ -163,9 +170,9 @@ - + - +