From 3136b5932a4b6e1a7fdb736bfbd1413968821f3b Mon Sep 17 00:00:00 2001 From: Overtorment Date: Sun, 14 Apr 2019 20:08:16 +0100 Subject: [PATCH 01/72] TST --- LightningCustodianWallet.test.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/LightningCustodianWallet.test.js b/LightningCustodianWallet.test.js index d81d14bd..e5f0360e 100644 --- a/LightningCustodianWallet.test.js +++ b/LightningCustodianWallet.test.js @@ -374,6 +374,14 @@ describe('LightningCustodianWallet', () => { err = true; } assert.ok(err); + + err = false; + try { + await l1.addInvoice(NaN, 'zero amt inv'); + } catch (_) { + err = true; + } + assert.ok(err); }); it('cant pay negative free amount', async () => { From ab82674b89d99f4bccdf4263e15d799f6d6d0613 Mon Sep 17 00:00:00 2001 From: Overtorment Date: Sun, 14 Apr 2019 20:10:40 +0100 Subject: [PATCH 02/72] REF: lapp browser improv --- screen/lnd/browser.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/screen/lnd/browser.js b/screen/lnd/browser.js index 160c5996..e4eb7aad 100644 --- a/screen/lnd/browser.js +++ b/screen/lnd/browser.js @@ -295,7 +295,12 @@ export default class Browser extends Component { } if (json && json.makeInvoice) { - let amount = Math.max(+json.makeInvoice.minimumAmount, +json.makeInvoice.maximumAmount, +json.makeInvoice.defaultAmount); + let amount = Math.max( + json.makeInvoice.minimumAmount || 0, + json.makeInvoice.maximumAmount || 0, + json.makeInvoice.defaultAmount || 0, + json.makeInvoice.amount || 0, + ); Alert.alert( 'Page', 'This page wants to pay you ' + amount + ' sats (' + json.makeInvoice.defaultMemo + ')', From f2b767e999277b19deafed26373a9bee45a29f7d Mon Sep 17 00:00:00 2001 From: Overtorment Date: Sat, 20 Apr 2019 12:52:15 +0100 Subject: [PATCH 03/72] REF: more electrum servers --- BlueElectrum.js | 12 +++++++++--- Electrum.test.js | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/BlueElectrum.js b/BlueElectrum.js index 303762b1..3f0524bd 100644 --- a/BlueElectrum.js +++ b/BlueElectrum.js @@ -4,7 +4,7 @@ let bitcoin = require('bitcoinjs-lib'); let reverse = require('buffer-reverse'); const storageKey = 'ELECTRUM_PEERS'; -const defaultPeer = { host: 'electrum.coinucopia.io', tcp: 50001 }; +const defaultPeer = { host: 'electrum1.bluewallet.io', tcp: 50001 }; const hardcodedPeers = [ // { host: 'noveltybobble.coinjoined.com', tcp: '50001' }, // down // { host: 'electrum.be', tcp: '50001' }, @@ -15,6 +15,12 @@ const hardcodedPeers = [ // { host: 'fullnode.coinkite.com', tcp: '50001' }, // { host: 'preperfect.eleCTruMioUS.com', tcp: '50001' }, // down { host: 'electrum1.bluewallet.io', tcp: '50001' }, + { host: 'electrum1.bluewallet.io', tcp: '50001' }, // 2x weight + { host: 'electrum2.bluewallet.io', tcp: '50001' }, + { host: 'electrum3.bluewallet.io', tcp: '50001' }, + { host: 'electrum3.bluewallet.io', tcp: '50001' }, // 2x weight + { host: 'electrum.coinop.cc', tcp: '50001' }, + { host: 'electrum-server.ninja', tcp: '50001' }, ]; let mainClient = false; @@ -26,7 +32,7 @@ async function connectMain() { console.log('begin connection:', JSON.stringify(usingPeer)); mainClient = new ElectrumClient(usingPeer.tcp, usingPeer.host, 'tcp'); await mainClient.connect(); - const ver = await mainClient.server_version('2.7.11', '1.2'); + const ver = await mainClient.server_version('2.7.11', '1.4'); let peers = await mainClient.serverPeers_subscribe(); if (peers && peers.length > 0) { console.log('connected to ', ver); @@ -35,7 +41,7 @@ async function connectMain() { } } catch (e) { mainConnected = false; - console.log('bad connection:', JSON.stringify(usingPeer)); + console.log('bad connection:', JSON.stringify(usingPeer), e); } if (!mainConnected) { diff --git a/Electrum.test.js b/Electrum.test.js index e489826d..f3f53844 100644 --- a/Electrum.test.js +++ b/Electrum.test.js @@ -30,11 +30,11 @@ describe('Electrum', () => { try { await mainClient.connect(); - await mainClient.server_version('2.7.11', '1.2'); + await mainClient.server_version('2.7.11', '1.4'); } catch (e) { mainClient.reconnect = mainClient.keepAlive = () => {}; // dirty hack to make it stop reconnecting mainClient.close(); - throw new Error('bad connection: ' + JSON.stringify(peer)); + throw new Error('bad connection: ' + JSON.stringify(peer) + ' ' + e.message); } let addr4elect = 'bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej'; From cc4b0bebdfd7f8a0f3cfd219744de2fcf3506d97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Rodriguez=20V=C3=A9lez?= Date: Tue, 9 Apr 2019 13:41:03 -0400 Subject: [PATCH 04/72] Update RN to 0.59.4 Due to a very important Android related fix --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7f430aa3..40514d56 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "prop-types": "15.7.2", "react": "16.8.6", "react-localization": "1.0.13", - "react-native": "0.59.3", + "react-native": "0.59.4", "react-native-camera": "2.2.1", "react-native-device-info": "1.4.2", "react-native-elements": "0.19.0", From 4ed492c249a2aa991f4695d9bd1150e9182240a0 Mon Sep 17 00:00:00 2001 From: Overtorment Date: Sun, 21 Apr 2019 19:50:28 +0100 Subject: [PATCH 05/72] FIX: buy bitcoin now happens in builtin webview instead of external browser --- screen/wallets/buyBitcoin.js | 43 +++--------------------------------- 1 file changed, 3 insertions(+), 40 deletions(-) diff --git a/screen/wallets/buyBitcoin.js b/screen/wallets/buyBitcoin.js index fc60ef11..76420396 100644 --- a/screen/wallets/buyBitcoin.js +++ b/screen/wallets/buyBitcoin.js @@ -1,15 +1,7 @@ import React, { Component } from 'react'; -import { Linking, View } from 'react-native'; -import { - BlueNavigationStyle, - BlueCopyTextToClipboard, - BlueLoading, - SafeBlueArea, - BlueButton, - BlueText, - BlueSpacing40, -} from '../../BlueComponents'; +import { BlueNavigationStyle, BlueLoading } from '../../BlueComponents'; import PropTypes from 'prop-types'; +import { WebView } from 'react-native-webview'; /** @type {AppStorage} */ let BlueApp = require('../../BlueApp'); let loc = require('../../loc'); @@ -71,36 +63,7 @@ export default class BuyBitcoin extends Component { return ; } - return ( - - - - {loc.buyBitcoin.tap_your_address} - - - - { - Linking.openURL('https://bluewallet.io/buy-bitcoin-redirect.html'); - }} - title="Buy Bitcoin" - /> - - - - - - - - - - - ); + return ({ + ...BlueNavigationStyle(navigation, true), + title: loc.pleasebackup.title, + headerLeft: null, + headerRight: null, + }); + + constructor(props) { + super(props); + + this.state = { + isLoading: true, + words: props.navigation.state.params.secret.split(' '), + }; + BackHandler.addEventListener('hardwareBackPress', this.handleBackButton.bind(this)); + } + + handleBackButton() { + this.props.navigation.dismiss(); + return true; + } + + componentDidMount() { + Privacy.enableBlur(); + this.setState({ + isLoading: false, + }); + } + + componentWillUnmount() { + Privacy.disableBlur(); + BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton.bind(this)); + } + + render() { + if (this.state.isLoading) { + return ( + + + + ); + } + + return ( + + {loc.pleasebackup.text} + + + + {this.state.words[0]} + + + {this.state.words[1]} + + + {this.state.words[2]} + + + {this.state.words[3]} + + + + + {this.state.words[4]} + + + {this.state.words[5]} + + + {this.state.words[6]} + + + {this.state.words[7]} + + + + + {this.state.words[8]} + + + {this.state.words[9]} + + + {this.state.words[10]} + + + {this.state.words[11]} + + + + + {this.state.words[12]} + + + {this.state.words[13]} + + + {this.state.words[14]} + + + {this.state.words[15]} + + + + + {this.state.words[16]} + + + {this.state.words[17]} + + + {this.state.words[18]} + + + {this.state.words[19]} + + + + + {this.state.words[20]} + + + {this.state.words[21]} + + + {this.state.words[22]} + + + {this.state.words[23]} + + + + + + this.props.navigation.dismiss()} title={loc.pleasebackup.ok} /> + + + + + ); + } +} + +PleaseBackup.propTypes = { + navigation: PropTypes.shape({ + state: PropTypes.shape({ + params: PropTypes.shape({ + secret: PropTypes.string, + }), + }), + dismiss: PropTypes.func, + }), +}; From 016657a5808380f06064c356480efa214e938ad1 Mon Sep 17 00:00:00 2001 From: Overtorment Date: Mon, 22 Apr 2019 20:49:04 +0100 Subject: [PATCH 07/72] REL: v3.9.7 --- android/app/build.gradle | 2 +- ios/BlueWallet/Info.plist | 2 +- ios/fastlane/metadata/en-US/release_notes.txt | 13 +++++++++++++ package.json | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 62e61e2f..111c5c11 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -102,7 +102,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 - versionName "3.9.5" + versionName "3.9.7" ndk { abiFilters "armeabi-v7a", "x86" } diff --git a/ios/BlueWallet/Info.plist b/ios/BlueWallet/Info.plist index 6ea94277..d3c28802 100644 --- a/ios/BlueWallet/Info.plist +++ b/ios/BlueWallet/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 3.9.5 + 3.9.7 CFBundleSignature ???? CFBundleURLTypes diff --git a/ios/fastlane/metadata/en-US/release_notes.txt b/ios/fastlane/metadata/en-US/release_notes.txt index 25e715d3..3d196175 100644 --- a/ios/fastlane/metadata/en-US/release_notes.txt +++ b/ios/fastlane/metadata/en-US/release_notes.txt @@ -1,3 +1,16 @@ +v3.9.7 +====== + +* ADD: intermediate step asking user to backup his mnemonic +* ADD: buy bitcoin now happens in builtin webview instead of external browser +* ADD: new deeplinking scheme +* FIX: large HD utxo fetch (closes #459) +* FIX: better ln wallet import +* FIX: rare hangs when importing wallet via qr +* REF: Remove old security alert #379 +* REF: more electrum servers +* REF: lapp browser improv + v3.9.2 ====== diff --git a/package.json b/package.json index 40514d56..5284b63f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "BlueWallet", - "version": "3.9.5", + "version": "3.9.7", "devDependencies": { "babel-eslint": "^10.0.1", "babel-jest": "^24.7.1", From c09c27ccee12bfbc0ce13c8bbc668f52ce2a30c1 Mon Sep 17 00:00:00 2001 From: Overtorment Date: Tue, 23 Apr 2019 19:49:00 +0100 Subject: [PATCH 08/72] FIX: rare crash on app startup --- loc/index.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/loc/index.js b/loc/index.js index 41577ac8..69bed65f 100644 --- a/loc/index.js +++ b/loc/index.js @@ -24,6 +24,7 @@ dayjs.extend(relativeTime); require('dayjs/locale/it'); break; case 'zh_cn': + lang = 'zh-cn'; require('dayjs/locale/zh-cn'); break; case 'ru': @@ -118,7 +119,14 @@ strings.transactionTimeToReadable = time => { if (time === 0) { return strings._.never; } - return dayjs(time).fromNow(); + let ret; + try { + ret = dayjs(time).fromNow(); + } catch (_) { + console.warn('incorrect locale set for dayjs'); + return time; + } + return ret; }; function removeTrailingZeros(value) { From c17ad2615588102658337f8125a0ae59c9de0235 Mon Sep 17 00:00:00 2001 From: Overtorment Date: Tue, 23 Apr 2019 20:03:28 +0100 Subject: [PATCH 09/72] OPS --- ios/fastlane/metadata/en-US/release_notes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/ios/fastlane/metadata/en-US/release_notes.txt b/ios/fastlane/metadata/en-US/release_notes.txt index 3d196175..4f3d9e5b 100644 --- a/ios/fastlane/metadata/en-US/release_notes.txt +++ b/ios/fastlane/metadata/en-US/release_notes.txt @@ -4,6 +4,7 @@ v3.9.7 * ADD: intermediate step asking user to backup his mnemonic * ADD: buy bitcoin now happens in builtin webview instead of external browser * ADD: new deeplinking scheme +* FIX: rare crash on app startup * FIX: large HD utxo fetch (closes #459) * FIX: better ln wallet import * FIX: rare hangs when importing wallet via qr From 33bffaa7ab53d25aa4c8b6ee091d5171eb0c4f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Rodriguez=20V=C3=A9lez?= Date: Thu, 2 May 2019 16:33:03 -0400 Subject: [PATCH 10/72] ADD: Apple Watch support --- .flowconfig | 2 +- .gitignore | 4 +- App.js | 3 +- App.test.js | 22 +- BlueApp.js | 2 +- BlueComponents.js | 61 +- BlueElectrum.js | 8 +- Electrum.test.js | 5 +- HDWallet.test.js | 2 +- WatchConnectivity.js | 138 + .../@react-native-community/async-storage.js | 1 + android/app/app.iml | 151 +- android/app/build.gradle | 3 +- .../main/assets/fonts/FontAwesome5_Brands.ttf | Bin 134160 -> 125016 bytes .../assets/fonts/FontAwesome5_Regular.ttf | Bin 40080 -> 34092 bytes .../main/assets/fonts/FontAwesome5_Solid.ttf | Bin 208792 -> 186228 bytes .../assets/fonts/MaterialCommunityIcons.ttf | Bin 416816 -> 531476 bytes .../app/src/main/assets/fonts/Octicons.ttf | Bin 27440 -> 28364 bytes .../bluewallet/MainApplication.java | 2 + android/settings.gradle | 2 + class/abstract-hd-wallet.js | 5 +- class/abstract-wallet.js | 2 - class/app-storage.js | 15 +- class/legacy-wallet.js | 3 +- class/lightning-custodian-wallet.js | 3 +- currency.js | 2 +- ios/BlueWallet.xcodeproj/project.pbxproj | 2161 +-- .../xcschemes/BlueWallet-tvOS.xcscheme | 2 +- .../xcschemes/BlueWallet.xcscheme | 32 +- .../BlueWalletWatch (Notification).xcscheme | 131 + .../xcschemes/BlueWalletWatch.xcscheme | 128 + .../xcschemes/xcschememanagement.plist | 47 +- .../contents.xcworkspacedata | 19 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + ios/BlueWallet/AppDelegate.h | 6 +- ios/BlueWallet/AppDelegate.m | 20 + ios/BlueWallet/Info.plist | 2 +- .../ExtensionDelegate.swift | 56 + ios/BlueWalletWatch Extension/Info.plist | 38 + .../InterfaceController.swift | 57 + .../NotificationController.swift | 38 + .../NumericKeypadInterfaceController.swift | 155 + .../Objects/Transaction.swift | 39 + .../Objects/TransactionTableRow.swift | 52 + .../Objects/Wallet.swift | 51 + .../Objects/WalletGradient.swift | 32 + .../Objects/WalletInformation.swift | 36 + .../Objects/WatchDataSource.swift | 103 + .../PushNotificationPayload.apns | 20 + .../ReceiveInterfaceController.swift | 115 + .../SpecifyInterfaceController.swift | 90 + .../WalletDetailsInterfaceController.swift | 68 + .../AppIcon.appiconset/1024.png | Bin 0 -> 197243 bytes .../Assets.xcassets/AppIcon.appiconset/58.png | Bin 0 -> 1816 bytes .../Assets.xcassets/AppIcon.appiconset/87.png | Bin 0 -> 2270 bytes .../AppIcon.appiconset/Contents.json | 92 + .../AppIcon.appiconset/Icon-172.png | Bin 0 -> 7700 bytes .../AppIcon.appiconset/Icon-173.png | Bin 0 -> 5168 bytes .../AppIcon.appiconset/Icon-196.png | Bin 0 -> 9170 bytes .../AppIcon.appiconset/Icon-48.png | Bin 0 -> 1770 bytes .../AppIcon.appiconset/Icon-55.png | Bin 0 -> 2226 bytes .../AppIcon.appiconset/Icon-88.png | Bin 0 -> 3526 bytes .../AppIcon.appiconset/group-copy-2@3x.png | Bin 0 -> 9579 bytes .../AppIcon.appiconset/watch.png | Bin 0 -> 1273 bytes .../Assets.xcassets/Contents.json | 6 + .../loadingIndicator.imageset/Contents.json | 13 + .../group-copy-2@3x.png | Bin 0 -> 14430 bytes .../Contents.json | 13 + .../pendingConfirmation.imageset/shape@3x.png | Bin 0 -> 692 bytes .../qr-code.imageset/Contents.json | 13 + .../qr-code.imageset/qr-code@3x.png | Bin 0 -> 114192 bytes .../receivedArrow.imageset/Contents.json | 13 + .../receivedArrow.imageset/path-copy-3@2x.png | Bin 0 -> 447 bytes .../sentArrow.imageset/Contents.json | 13 + .../sentArrow.imageset/path-copy@2x.png | Bin 0 -> 471 bytes .../wallet.imageset/Contents.json | 23 + .../Assets.xcassets/wallet.imageset/mask.png | Bin 0 -> 5531 bytes .../wallet.imageset/mask@2x.png | Bin 0 -> 13801 bytes .../wallet.imageset/mask@3x.png | Bin 0 -> 25545 bytes .../walletACINQ.imageset/Contents.json | 23 + .../walletACINQ.imageset/mask.png | Bin 0 -> 5254 bytes .../walletACINQ.imageset/mask@2x.png | Bin 0 -> 14065 bytes .../walletACINQ.imageset/mask@3x.png | Bin 0 -> 25315 bytes .../walletHD.imageset/Contents.json | 13 + .../walletHD.imageset/mask@3x.png | Bin 0 -> 25898 bytes .../Contents.json | 13 + .../mask@3x.png | Bin 0 -> 26373 bytes .../walletWatchOnly.imageset/Contents.json | 23 + .../walletWatchOnly.imageset/mask.png | Bin 0 -> 4869 bytes .../walletWatchOnly.imageset/mask@2x.png | Bin 0 -> 12959 bytes .../walletWatchOnly.imageset/mask@3x.png | Bin 0 -> 24766 bytes .../Base.lproj/Interface.storyboard | 339 + ios/BlueWalletWatch/Info.plist | 33 + ios/KeychainSwiftDistrib.swift | 454 + ios/Podfile | 127 + ios/Podfile.lock | 232 + .../Local Podspecs/RNDeviceInfo.podspec.json | 26 + ios/Pods/Local Podspecs/RNSVG.podspec.json | 23 + ios/Pods/Local Podspecs/React.podspec.json | 565 + .../react-native-camera.podspec.json | 97 + .../react-native-haptic-feedback.podspec.json | 25 + .../react-native-webview.podspec.json | 23 + ios/Pods/Local Podspecs/yoga.podspec.json | 32 + ios/Pods/Manifest.lock | 232 + ios/Pods/Pods.xcodeproj/project.pbxproj | 11426 ++++++++++++++++ .../BVLinearGradient.xcconfig | 11 + .../Pods-BlueWallet-acknowledgements.markdown | 762 ++ .../Pods-BlueWallet-acknowledgements.plist | 920 ++ .../Pods-BlueWallet.debug.xcconfig | 10 + .../Pods-BlueWallet.release.xcconfig | 10 + .../Pods-BlueWalletTests.debug.xcconfig | 8 + .../Pods-BlueWalletTests.release.xcconfig | 8 + .../Pods-RCTPrivacySnapshot.debug.xcconfig | 9 + .../Pods-RCTPrivacySnapshot.release.xcconfig | 9 + .../Pods-RCTQRCodeLocalImage.debug.xcconfig | 9 + .../Pods-RCTQRCodeLocalImage.release.xcconfig | 9 + .../Pods-TcpSockets.debug.xcconfig | 9 + .../Pods-TcpSockets.release.xcconfig | 9 + .../RNDeviceInfo/RNDeviceInfo.xcconfig | 10 + .../Target Support Files/RNFS/RNFS.xcconfig | 10 + .../RNGestureHandler.xcconfig | 10 + .../RNRate/RNRate.xcconfig | 10 + .../Target Support Files/RNSVG/RNSVG.xcconfig | 10 + .../RNVectorIcons/RNVectorIcons.xcconfig | 10 + .../RNWatch/RNWatch.xcconfig | 10 + .../SentryReactNative.xcconfig | 10 + .../react-native-camera.xcconfig | 10 + ...ct-native-google-analytics-bridge.xcconfig | 11 + .../react-native-haptic-feedback.xcconfig | 10 + .../react-native-image-picker.xcconfig | 10 + .../react-native-randombytes.xcconfig | 10 + .../react-native-slider.xcconfig | 10 + .../react-native-webview.xcconfig | 10 + loc/index.js | 13 +- package-lock.json | 2388 ++-- package.json | 49 +- podinstall.sh | 5 + screen/lnd/scanLndInvoice.js | 4 +- screen/receive/details.js | 96 +- screen/selftest.js | 2 +- screen/send/confirm.js | 2 +- screen/send/details.js | 4 +- screen/send/scanQrAddress.js | 79 +- screen/settings/language.js | 1 - screen/settings/lightningSettings.js | 3 +- screen/wallets/add.js | 3 +- screen/wallets/scanQrWif.js | 128 +- screen/wallets/walletMigrate.js | 3 +- 148 files changed, 19770 insertions(+), 2778 deletions(-) create mode 100644 WatchConnectivity.js create mode 100644 __mocks__/@react-native-community/async-storage.js create mode 100644 ios/BlueWallet.xcodeproj/xcshareddata/xcschemes/BlueWalletWatch (Notification).xcscheme create mode 100644 ios/BlueWallet.xcodeproj/xcshareddata/xcschemes/BlueWalletWatch.xcscheme create mode 100644 ios/BlueWallet.xcworkspace/contents.xcworkspacedata create mode 100644 ios/BlueWallet.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 ios/BlueWalletWatch Extension/ExtensionDelegate.swift create mode 100644 ios/BlueWalletWatch Extension/Info.plist create mode 100644 ios/BlueWalletWatch Extension/InterfaceController.swift create mode 100644 ios/BlueWalletWatch Extension/NotificationController.swift create mode 100644 ios/BlueWalletWatch Extension/NumericKeypadInterfaceController.swift create mode 100644 ios/BlueWalletWatch Extension/Objects/Transaction.swift create mode 100644 ios/BlueWalletWatch Extension/Objects/TransactionTableRow.swift create mode 100644 ios/BlueWalletWatch Extension/Objects/Wallet.swift create mode 100644 ios/BlueWalletWatch Extension/Objects/WalletGradient.swift create mode 100644 ios/BlueWalletWatch Extension/Objects/WalletInformation.swift create mode 100644 ios/BlueWalletWatch Extension/Objects/WatchDataSource.swift create mode 100644 ios/BlueWalletWatch Extension/PushNotificationPayload.apns create mode 100644 ios/BlueWalletWatch Extension/ReceiveInterfaceController.swift create mode 100644 ios/BlueWalletWatch Extension/SpecifyInterfaceController.swift create mode 100644 ios/BlueWalletWatch Extension/WalletDetailsInterfaceController.swift create mode 100644 ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/1024.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/58.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/87.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Icon-172.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Icon-173.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Icon-196.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Icon-48.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Icon-55.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/Icon-88.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/group-copy-2@3x.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/AppIcon.appiconset/watch.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/Contents.json create mode 100644 ios/BlueWalletWatch/Assets.xcassets/loadingIndicator.imageset/Contents.json create mode 100644 ios/BlueWalletWatch/Assets.xcassets/loadingIndicator.imageset/group-copy-2@3x.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/pendingConfirmation.imageset/Contents.json create mode 100644 ios/BlueWalletWatch/Assets.xcassets/pendingConfirmation.imageset/shape@3x.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/qr-code.imageset/Contents.json create mode 100644 ios/BlueWalletWatch/Assets.xcassets/qr-code.imageset/qr-code@3x.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/receivedArrow.imageset/Contents.json create mode 100644 ios/BlueWalletWatch/Assets.xcassets/receivedArrow.imageset/path-copy-3@2x.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/sentArrow.imageset/Contents.json create mode 100644 ios/BlueWalletWatch/Assets.xcassets/sentArrow.imageset/path-copy@2x.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/wallet.imageset/Contents.json create mode 100644 ios/BlueWalletWatch/Assets.xcassets/wallet.imageset/mask.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/wallet.imageset/mask@2x.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/wallet.imageset/mask@3x.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/walletACINQ.imageset/Contents.json create mode 100644 ios/BlueWalletWatch/Assets.xcassets/walletACINQ.imageset/mask.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/walletACINQ.imageset/mask@2x.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/walletACINQ.imageset/mask@3x.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/walletHD.imageset/Contents.json create mode 100644 ios/BlueWalletWatch/Assets.xcassets/walletHD.imageset/mask@3x.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/walletLightningCustodial.imageset/Contents.json create mode 100644 ios/BlueWalletWatch/Assets.xcassets/walletLightningCustodial.imageset/mask@3x.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/walletWatchOnly.imageset/Contents.json create mode 100644 ios/BlueWalletWatch/Assets.xcassets/walletWatchOnly.imageset/mask.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/walletWatchOnly.imageset/mask@2x.png create mode 100644 ios/BlueWalletWatch/Assets.xcassets/walletWatchOnly.imageset/mask@3x.png create mode 100644 ios/BlueWalletWatch/Base.lproj/Interface.storyboard create mode 100644 ios/BlueWalletWatch/Info.plist create mode 100644 ios/KeychainSwiftDistrib.swift create mode 100644 ios/Podfile create mode 100644 ios/Podfile.lock create mode 100644 ios/Pods/Local Podspecs/RNDeviceInfo.podspec.json create mode 100644 ios/Pods/Local Podspecs/RNSVG.podspec.json create mode 100644 ios/Pods/Local Podspecs/React.podspec.json create mode 100644 ios/Pods/Local Podspecs/react-native-camera.podspec.json create mode 100644 ios/Pods/Local Podspecs/react-native-haptic-feedback.podspec.json create mode 100644 ios/Pods/Local Podspecs/react-native-webview.podspec.json create mode 100644 ios/Pods/Local Podspecs/yoga.podspec.json create mode 100644 ios/Pods/Manifest.lock create mode 100644 ios/Pods/Pods.xcodeproj/project.pbxproj create mode 100644 ios/Pods/Target Support Files/BVLinearGradient/BVLinearGradient.xcconfig create mode 100644 ios/Pods/Target Support Files/Pods-BlueWallet/Pods-BlueWallet-acknowledgements.markdown create mode 100644 ios/Pods/Target Support Files/Pods-BlueWallet/Pods-BlueWallet-acknowledgements.plist create mode 100644 ios/Pods/Target Support Files/Pods-BlueWallet/Pods-BlueWallet.debug.xcconfig create mode 100644 ios/Pods/Target Support Files/Pods-BlueWallet/Pods-BlueWallet.release.xcconfig create mode 100644 ios/Pods/Target Support Files/Pods-BlueWalletTests/Pods-BlueWalletTests.debug.xcconfig create mode 100644 ios/Pods/Target Support Files/Pods-BlueWalletTests/Pods-BlueWalletTests.release.xcconfig create mode 100644 ios/Pods/Target Support Files/Pods-RCTPrivacySnapshot/Pods-RCTPrivacySnapshot.debug.xcconfig create mode 100644 ios/Pods/Target Support Files/Pods-RCTPrivacySnapshot/Pods-RCTPrivacySnapshot.release.xcconfig create mode 100644 ios/Pods/Target Support Files/Pods-RCTQRCodeLocalImage/Pods-RCTQRCodeLocalImage.debug.xcconfig create mode 100644 ios/Pods/Target Support Files/Pods-RCTQRCodeLocalImage/Pods-RCTQRCodeLocalImage.release.xcconfig create mode 100644 ios/Pods/Target Support Files/Pods-TcpSockets/Pods-TcpSockets.debug.xcconfig create mode 100644 ios/Pods/Target Support Files/Pods-TcpSockets/Pods-TcpSockets.release.xcconfig create mode 100644 ios/Pods/Target Support Files/RNDeviceInfo/RNDeviceInfo.xcconfig create mode 100644 ios/Pods/Target Support Files/RNFS/RNFS.xcconfig create mode 100644 ios/Pods/Target Support Files/RNGestureHandler/RNGestureHandler.xcconfig create mode 100644 ios/Pods/Target Support Files/RNRate/RNRate.xcconfig create mode 100644 ios/Pods/Target Support Files/RNSVG/RNSVG.xcconfig create mode 100644 ios/Pods/Target Support Files/RNVectorIcons/RNVectorIcons.xcconfig create mode 100644 ios/Pods/Target Support Files/RNWatch/RNWatch.xcconfig create mode 100644 ios/Pods/Target Support Files/SentryReactNative/SentryReactNative.xcconfig create mode 100644 ios/Pods/Target Support Files/react-native-camera/react-native-camera.xcconfig create mode 100644 ios/Pods/Target Support Files/react-native-google-analytics-bridge/react-native-google-analytics-bridge.xcconfig create mode 100644 ios/Pods/Target Support Files/react-native-haptic-feedback/react-native-haptic-feedback.xcconfig create mode 100644 ios/Pods/Target Support Files/react-native-image-picker/react-native-image-picker.xcconfig create mode 100644 ios/Pods/Target Support Files/react-native-randombytes/react-native-randombytes.xcconfig create mode 100644 ios/Pods/Target Support Files/react-native-slider/react-native-slider.xcconfig create mode 100644 ios/Pods/Target Support Files/react-native-webview/react-native-webview.xcconfig create mode 100755 podinstall.sh diff --git a/.flowconfig b/.flowconfig index 9bded78b..ebf6585f 100644 --- a/.flowconfig +++ b/.flowconfig @@ -67,4 +67,4 @@ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError [version] -^0.86.0 +^0.97.0 diff --git a/.gitignore b/.gitignore index 04555cfa..4e6fc7f1 100644 --- a/.gitignore +++ b/.gitignore @@ -57,4 +57,6 @@ buck-out/ #BlueWallet release-notes.json -release-notes.txt \ No newline at end of file +release-notes.txt + +ios/Pods/ diff --git a/App.js b/App.js index 068f3641..67c4c65d 100644 --- a/App.js +++ b/App.js @@ -1,5 +1,6 @@ import React from 'react'; -import { Linking, AppState, Clipboard, StyleSheet, KeyboardAvoidingView, Platform, View, AsyncStorage } from 'react-native'; +import { Linking, AppState, Clipboard, StyleSheet, KeyboardAvoidingView, Platform, View } from 'react-native'; +import AsyncStorage from '@react-native-community/async-storage'; import Modal from 'react-native-modal'; import { NavigationActions } from 'react-navigation'; import MainBottomTabs from './MainBottomTabs'; diff --git a/App.test.js b/App.test.js index 971c583f..b150b91e 100644 --- a/App.test.js +++ b/App.test.js @@ -5,13 +5,11 @@ import TestRenderer from 'react-test-renderer'; import Settings from './screen/settings/settings'; import Selftest from './screen/selftest'; import { BlueHeader } from './BlueComponents'; -import MockStorage from './MockStorage'; import { FiatUnit } from './models/fiatUnit'; +import AsyncStorage from '@react-native-community/async-storage'; global.crypto = require('crypto'); // shall be used by tests under nodejs CLI, but not in RN environment let assert = require('assert'); jest.mock('react-native-qrcode-svg', () => 'Video'); -const AsyncStorage = new MockStorage(); -jest.setMock('AsyncStorage', AsyncStorage); jest.useFakeTimers(); jest.mock('Picker', () => { // eslint-disable-next-line import/no-unresolved @@ -105,7 +103,6 @@ it('Selftest work', () => { }); it('Appstorage - loadFromDisk works', async () => { - AsyncStorage.storageCache = {}; // cleanup from other tests /** @type {AppStorage} */ let Storage = new AppStorage(); let w = new SegwitP2SHWallet(); @@ -125,16 +122,14 @@ it('Appstorage - loadFromDisk works', async () => { // emulating encrypted storage (and testing flag) - AsyncStorage.storageCache.data = false; - AsyncStorage.storageCache.data_encrypted = '1'; // flag + await AsyncStorage.setItem('data', false); + await AsyncStorage.setItem(AppStorage.FLAG_ENCRYPTED, '1'); let Storage3 = new AppStorage(); isEncrypted = await Storage3.storageIsEncrypted(); assert.ok(isEncrypted); }); it('Appstorage - encryptStorage & load encrypted storage works', async () => { - AsyncStorage.storageCache = {}; // cleanup from other tests - /** @type {AppStorage} */ let Storage = new AppStorage(); let w = new SegwitP2SHWallet(); @@ -236,7 +231,7 @@ it('Wallet can fetch balance', async () => { assert.ok(w.getUnconfirmedBalance() === 0); assert.ok(w._lastBalanceFetch === 0); await w.fetchBalance(); - assert.ok(w.getBalance() === 0.18262); + assert.ok(w.getBalance() === 18262000); assert.ok(w.getUnconfirmedBalance() === 0); assert.ok(w._lastBalanceFetch > 0); }); @@ -302,19 +297,18 @@ it('Wallet can fetch TXs', async () => { describe('currency', () => { it('fetches exchange rate and saves to AsyncStorage', async () => { jasmine.DEFAULT_TIMEOUT_INTERVAL = 15000; - AsyncStorage.storageCache = {}; // cleanup from other tests let currency = require('./currency'); await currency.startUpdater(); - let cur = AsyncStorage.storageCache[AppStorage.EXCHANGE_RATES]; + let cur = await AsyncStorage.getItem(AppStorage.EXCHANGE_RATES); cur = JSON.parse(cur); assert.ok(Number.isInteger(cur[currency.STRUCT.LAST_UPDATED])); assert.ok(cur[currency.STRUCT.LAST_UPDATED] > 0); assert.ok(cur['BTC_USD'] > 0); // now, setting other currency as default - AsyncStorage.storageCache[AppStorage.PREFERRED_CURRENCY] = JSON.stringify(FiatUnit.JPY); + await AsyncStorage.setItem(AppStorage.PREFERRED_CURRENCY, JSON.stringify(FiatUnit.JPY)); await currency.startUpdater(); - cur = JSON.parse(AsyncStorage.storageCache[AppStorage.EXCHANGE_RATES]); + cur = JSON.parse(await AsyncStorage.getItem(AppStorage.EXCHANGE_RATES)); assert.ok(cur['BTC_JPY'] > 0); // now setting with a proper setter @@ -322,7 +316,7 @@ describe('currency', () => { await currency.startUpdater(); let preferred = await currency.getPreferredCurrency(); assert.strictEqual(preferred.endPointKey, 'EUR'); - cur = JSON.parse(AsyncStorage.storageCache[AppStorage.EXCHANGE_RATES]); + cur = JSON.parse(await AsyncStorage.getItem(AppStorage.EXCHANGE_RATES)); assert.ok(cur['BTC_EUR'] > 0); }); }); diff --git a/BlueApp.js b/BlueApp.js index 99feb095..ae8afb73 100644 --- a/BlueApp.js +++ b/BlueApp.js @@ -10,7 +10,7 @@ let A = require('./analytics'); let BlueElectrum = require('./BlueElectrum'); // eslint-disable-line /** @type {AppStorage} */ -let BlueApp = new AppStorage(); +const BlueApp = new AppStorage(); async function startAndDecrypt(retry) { console.log('startAndDecrypt'); diff --git a/BlueComponents.js b/BlueComponents.js index 9b960438..c7a728dd 100644 --- a/BlueComponents.js +++ b/BlueComponents.js @@ -25,7 +25,6 @@ import { import LinearGradient from 'react-native-linear-gradient'; import { LightningCustodianWallet } from './class'; import Carousel from 'react-native-snap-carousel'; -import DeviceInfo from 'react-native-device-info'; import { BitcoinUnit } from './models/bitcoinUnits'; import NavigationService from './NavigationService'; import ImagePicker from 'react-native-image-picker'; @@ -36,6 +35,7 @@ let loc = require('./loc/'); let BlueApp = require('./BlueApp'); const { height, width } = Dimensions.get('window'); const aspectRatio = height / width; +const BigNumber = require('bignumber.js'); let isIpad; if (aspectRatio > 1.6) { isIpad = false; @@ -241,6 +241,14 @@ export class BlueCopyTextToClipboard extends Component { this.state = { hasTappedText: false, address: props.text }; } + static getDerivedStateFromProps(props, state) { + if (state.hasTappedText) { + return { hasTappedText: state.hasTappedText, address: state.address }; + } else { + return { hasTappedText: state.hasTappedText, address: props.text }; + } + } + copyToClipboard = () => { this.setState({ hasTappedText: true }, () => { Clipboard.setString(this.props.text); @@ -404,29 +412,6 @@ export class BlueFormMultiInput extends Component { } } -export class BlueFormInputAddress extends Component { - render() { - return ( - - ); - } -} - export class BlueHeader extends Component { render() { return ( @@ -560,13 +545,6 @@ export class is { static ipad() { return isIpad; } - - static iphone8() { - if (Platform.OS !== 'ios') { - return false; - } - return DeviceInfo.getDeviceId() === 'iPhone10,4'; - } } export class BlueSpacing20 extends Component { @@ -1733,7 +1711,7 @@ export class BlueAddressInput extends Component { export class BlueBitcoinAmount extends Component { static propTypes = { isLoading: PropTypes.bool, - amount: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + amount: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), onChangeText: PropTypes.func, disabled: PropTypes.bool, unit: PropTypes.string, @@ -1744,8 +1722,15 @@ export class BlueBitcoinAmount extends Component { }; render() { - const amount = typeof this.props.amount === 'number' ? this.props.amount.toString() : this.props.amount; - + const amount = this.props.amount || 0; + let localCurrency = loc.formatBalanceWithoutSuffix(amount, BitcoinUnit.LOCAL_CURRENCY, false); + if (this.props.unit === BitcoinUnit.BTC) { + let sat = new BigNumber(amount); + sat = sat.multipliedBy(100000000).toString(); + localCurrency = loc.formatBalanceWithoutSuffix(sat, BitcoinUnit.LOCAL_CURRENCY, false); + } else { + localCurrency = loc.formatBalanceWithoutSuffix(amount.toString(), BitcoinUnit.LOCAL_CURRENCY, false); + } return ( this.textInput.focus()}> @@ -1788,13 +1773,7 @@ export class BlueBitcoinAmount extends Component { - - {loc.formatBalance( - this.props.unit === BitcoinUnit.BTC ? amount || 0 : loc.formatBalanceWithoutSuffix(amount || 0, BitcoinUnit.BTC, false), - BitcoinUnit.LOCAL_CURRENCY, - false, - )} - + {localCurrency} diff --git a/BlueElectrum.js b/BlueElectrum.js index 3f0524bd..7c30bea9 100644 --- a/BlueElectrum.js +++ b/BlueElectrum.js @@ -1,10 +1,10 @@ -import { AsyncStorage } from 'react-native'; +import AsyncStorage from '@react-native-community/async-storage'; const ElectrumClient = require('electrum-client'); let bitcoin = require('bitcoinjs-lib'); let reverse = require('buffer-reverse'); const storageKey = 'ELECTRUM_PEERS'; -const defaultPeer = { host: 'electrum1.bluewallet.io', tcp: 50001 }; +const defaultPeer = { host: 'electrum1.bluewallet.io', tcp: '50001' }; const hardcodedPeers = [ // { host: 'noveltybobble.coinjoined.com', tcp: '50001' }, // down // { host: 'electrum.be', tcp: '50001' }, @@ -170,8 +170,8 @@ async function waitTillConnected() { async function estimateFees() { if (!mainClient) throw new Error('Electrum client is not connected'); const fast = await mainClient.blockchainEstimatefee(1); - const medium = await mainClient.blockchainEstimatefee(6); - const slow = await mainClient.blockchainEstimatefee(12); + const medium = await mainClient.blockchainEstimatefee(5); + const slow = await mainClient.blockchainEstimatefee(10); return { fast, medium, slow }; } diff --git a/Electrum.test.js b/Electrum.test.js index f3f53844..09403ba9 100644 --- a/Electrum.test.js +++ b/Electrum.test.js @@ -14,8 +14,8 @@ beforeAll(async () => { // while app starts up, but for tests we need to wait for it try { await BlueElectrum.waitTillConnected(); - } catch (Err) { - console.log('failed to connect to Electrum:', Err); + } catch (err) { + console.log('failed to connect to Electrum:', err); process.exit(1); } }); @@ -52,7 +52,6 @@ describe('Electrum', () => { hash = bitcoin.crypto.sha256(script); reversedHash = Buffer.from(hash.reverse()); balance = await mainClient.blockchainScripthash_getBalance(reversedHash.toString('hex')); - assert.ok(balance.confirmed === 51432); // let peers = await mainClient.serverPeers_subscribe(); // console.log(peers); diff --git a/HDWallet.test.js b/HDWallet.test.js index acb84c0a..df76ae23 100644 --- a/HDWallet.test.js +++ b/HDWallet.test.js @@ -207,7 +207,7 @@ it('Segwit HD (BIP49) can fetch balance with many used addresses in hierarchy', let end = +new Date(); const took = (end - start) / 1000; took > 15 && console.warn('took', took, "sec to fetch huge HD wallet's balance"); - assert.strictEqual(hd.getBalance(), 0.00051432); + assert.strictEqual(hd.getBalance(), 51432); await hd.fetchUtxo(); assert.ok(hd.utxo.length > 0); diff --git a/WatchConnectivity.js b/WatchConnectivity.js new file mode 100644 index 00000000..2efe80dd --- /dev/null +++ b/WatchConnectivity.js @@ -0,0 +1,138 @@ +import * as watch from 'react-native-watch-connectivity'; +import { InteractionManager } from 'react-native'; +const loc = require('./loc'); +export default class WatchConnectivity { + isAppInstalled = false; + BlueApp = require('./BlueApp'); + + constructor() { + this.getIsWatchAppInstalled(); + } + + getIsWatchAppInstalled() { + 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); + } + }); + } + + async handleLightningInvoiceCreateRequest(walletIndex, amount, description) { + const wallet = this.BlueApp.getWallets()[walletIndex]; + if (wallet.allowReceive() && amount > 0 && description.trim().length > 0) { + try { + const invoiceRequest = await wallet.addInvoice(amount, description); + return invoiceRequest; + } catch (error) { + return error; + } + } + } + + async sendWalletsToWatch() { + InteractionManager.runAfterInteractions(async () => { + if (this.isAppInstalled) { + const allWallets = this.BlueApp.getWallets(); + let wallets = []; + for (const wallet of allWallets) { + let receiveAddress = ''; + if (wallet.allowReceive()) { + if (wallet.getAddressAsync) { + receiveAddress = await wallet.getAddressAsync(); + } else { + receiveAddress = wallet.getAddress(); + } + } + let transactions = wallet.getTransactions(10); + let watchTransactions = []; + for (const transaction of transactions) { + let type = 'pendingConfirmation'; + let memo = ''; + let amount = 0; + + if (transaction.hasOwnProperty('confirmations') && !transaction.confirmations > 0) { + type = 'pendingConfirmation'; + } else if (transaction.type === 'user_invoice' || transaction.type === 'payment_request') { + const currentDate = new Date(); + const now = (currentDate.getTime() / 1000) | 0; + const invoiceExpiration = transaction.timestamp + transaction.expire_time; + + if (invoiceExpiration > now) { + type = 'pendingConfirmation'; + } else if (invoiceExpiration < now) { + if (transaction.ispaid) { + type = 'received'; + } else { + type = 'sent'; + } + } + } else if (transaction.value / 100000000 < 0) { + type = 'sent'; + } else { + type = 'received'; + } + if (transaction.type === 'user_invoice' || transaction.type === 'payment_request') { + if (isNaN(transaction.value)) { + amount = '0'; + } + const currentDate = new Date(); + const now = (currentDate.getTime() / 1000) | 0; + const invoiceExpiration = transaction.timestamp + transaction.expire_time; + + if (invoiceExpiration > now) { + amount = loc.formatBalance(transaction.value, wallet.getPreferredBalanceUnit(), true).toString(); + } else if (invoiceExpiration < now) { + if (transaction.ispaid) { + amount = loc.formatBalance(transaction.value, wallet.getPreferredBalanceUnit(), true).toString(); + } else { + amount = loc.lnd.expired; + } + } else { + amount = loc.formatBalance(transaction.value, wallet.getPreferredBalanceUnit(), true).toString(); + } + } 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']; + } else if (transaction.memo) { + memo = transaction.memo; + } + const watchTX = { type, amount, memo, time: loc.transactionTimeToReadable(transaction.received) }; + watchTransactions.push(watchTX); + } + wallets.push({ + label: wallet.getLabel(), + balance: loc.formatBalance(Number(wallet.getBalance()), wallet.getPreferredBalanceUnit(), true), + type: wallet.type, + preferredBalanceUnit: wallet.getPreferredBalanceUnit(), + receiveAddress: receiveAddress, + transactions: watchTransactions, + }); + } + + watch.updateApplicationContext({ wallets }); + } + }); + } +} + +WatchConnectivity.init = function() { + if (WatchConnectivity.shared) return; + WatchConnectivity.shared = new WatchConnectivity(); +}; diff --git a/__mocks__/@react-native-community/async-storage.js b/__mocks__/@react-native-community/async-storage.js new file mode 100644 index 00000000..272ea598 --- /dev/null +++ b/__mocks__/@react-native-community/async-storage.js @@ -0,0 +1 @@ +export default from '@react-native-community/async-storage/jest/async-storage-mock' diff --git a/android/app/app.iml b/android/app/app.iml index dd3e70e3..2afd4f72 100644 --- a/android/app/app.iml +++ b/android/app/app.iml @@ -17,7 +17,7 @@