From 22a899e8339539b07204a6a51e5c76520405af6c Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Fri, 4 Sep 2020 12:40:43 +0000 Subject: [PATCH 01/17] Apply translations in ru review completed for the source file '/ios/fastlane/metadata/en-US/description.txt' on the 'ru' language. --- ios/fastlane/metadata/ru/description.txt | 71 ++++++++++++------------ 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/ios/fastlane/metadata/ru/description.txt b/ios/fastlane/metadata/ru/description.txt index c481a292b..8f5e86a56 100644 --- a/ios/fastlane/metadata/ru/description.txt +++ b/ios/fastlane/metadata/ru/description.txt @@ -1,54 +1,53 @@ -Биткоин кошелек для iOS на русском языке. -Бесплатный и с открытым исходным кодом. +Биткойн-кошелёк который позволяет вам получать, хранить, посылать и покупать биткойны просто и безопасно. -* Приватные ключи всегда остаются на вашем устройстве -* Полное шифрование -* Фальшивый пароль для расшифровки фальшивых кошельков (Правдоподобное отрицание) -* Поддержка SegWit -* Поддержка замены транзакций (Replace-by-Fee - RBF) -* Watch-only (Sentinel) wallets +В BlueWallet вы владеете приватными ключами. Этокошелек, созданный пользователями биткойнов для сообщества. + +Вы можете моментально обмениваться средствами с кем угодно в мире. Новая финансовая система у вас в кармане. + +Создайте бесплатно неограниченное количество биткойн-кошельков или импортируйте существующий. Это просто и быстро. + +_____ + +Что внутри: -Особенности -=========== +1 - Разработка с фокусом на безопасность Открытый исходный код ---------------------- -Лицензия MIT. Вы можете самостоятельно собрать приложение. Сделано на ReactNative +Лицензия MIT даёт максимум свободы модифицировать код! Разработано на ReactNative -Безопасность под контролем --------------------------- -Приватные ключи всегда остаются на вашем устройстве +Правдопободное отрицание (Plausible deniability) +В случае угрозы вы можете раскрыть пароль от фальшивого биткойн-кошелька. Ваши биткоины будут скрыты от посторонних Полное шифрование ----------------- -Поверх родного многослойного шифрования iOS, BlueWallet дополнительно шифрует все данные пользовательским паролем. -Не стоит доверять биометрической безопасности +Поверх родного многослойного шифрования iOS, BlueWallet дополнительно шифрует все данные пользовательским паролем -Правдопободное отрицание -------------------- -Aka Plausible Deniability. На случай если вас заставляют раскрыть пароль под давлением, вы можете раскрыть "фальшивый" -пароль от фальшивого кошелька. Ваши биткоины будут скрыты от посторонних +Поддержка полной ноды +Используйте свой сервер Биткойн с Electrum, чтобы повысить приватность +Холодное хранение +Подключите аппаратный кошелёк и храните монеты в Холодном хранилище + +2 - Получите максимум возможностей + +Безопасность под контролем +Приватные ключи всегда остаются на вашем устройстве +Только вы управляете ключами Гибкие комиссии ---------------- От одного Сатоши. Не переплачивайте за биткоин переводы -Замена транзакций ------------------- -Aka Replace-By-Fee (RBF). Вы можете ускорить ваши подвисшие транзакции повышением комиссии (стандарт BIP125). -Вы также можете поменять адрес назначения для неподтвержденной транзакции +Замена транзакций (Replace-By-Fee) +Вы можете ускорить ваши подвисшие транзакции повышением комиссии (стандарт BIP125). Вы также можете поменять адрес назначения для неподтвержденной транзакции -SegWit --------- -Поддержка SegWit (в режиме P2SH совместимости). Экономьте на комиссии еще больше +Кошельки только для просмотра (Watch-only) +Импортировав адрес своего Холодного хранилища вы можете следить за его балансом не подвергая его риску. -Экспорт TXHEX --------------- -Получите данные транзакции не выпуская транзакцию в сеть (если хотите транслировать транакцию самостоятельно) +Lightning Network +Этот платёжный протокол позволяет проводить моментальные и почти бесплатные платежи! +Покупайте Биткойн +Прямо в вашем кошельке - это настоящая финансовая революция. -Watch-only (Sentinel) wallets ------------------------------ -You can watch status of your offline wallets. No private keys on your device! +Local Trader +Покупайте и продавайте биткоины напрямую с другим пользователям, без третьих лиц. \ No newline at end of file From 14d08303b9359732e6dc8725b341b26e212c4991 Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Fri, 4 Sep 2020 12:42:28 +0000 Subject: [PATCH 02/17] Apply translations in ru review completed for the source file '/ios/fastlane/metadata/en-US/promotional_text.txt' on the 'ru' language. --- ios/fastlane/metadata/ru/promotional_text.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ios/fastlane/metadata/ru/promotional_text.txt b/ios/fastlane/metadata/ru/promotional_text.txt index 931f035d5..64a8c87a8 100644 --- a/ios/fastlane/metadata/ru/promotional_text.txt +++ b/ios/fastlane/metadata/ru/promotional_text.txt @@ -1,10 +1,10 @@ -Features +Особенности -* Open Source -* Full encryption -* Plausible deniability -* Flexible fees -* Replace-By-Fee (RBF) +* Открытый исходный код +* Все данные шифруются +* Правдоподобное отрицание (Plausible deniability) +* Гибкие настройки комиссии +* Повышение комиссии за транзакцию (RBF) * SegWit -* Watch-only (Sentinel) wallets -* Lightning network +* Просмотр баланса кошелька без импорта приватных ключей +* Lightning network \ No newline at end of file From ed9f717e70d51ecf39cf492eca5d0f3b5dd3b7ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Rodriguez=20V=C3=A9lez?= Date: Sun, 6 Sep 2020 21:36:59 -0400 Subject: [PATCH 03/17] FIX: Center Next button for large screen devices. --- screen/send/details.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/screen/send/details.js b/screen/send/details.js index c410837f0..2d64d09ed 100644 --- a/screen/send/details.js +++ b/screen/send/details.js @@ -140,7 +140,7 @@ const styles = StyleSheet.create({ createButton: { marginHorizontal: 56, marginVertical: 16, - alignContent: 'center', + alignItems: 'center', minHeight: 44, }, select: { From 23c456c3332af0474d2aabf5321a493d2af1925a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Rodriguez=20V=C3=A9lez?= Date: Sun, 6 Sep 2020 22:30:17 -0400 Subject: [PATCH 04/17] FIX:Center Pay button --- screen/lnd/scanLndInvoice.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/screen/lnd/scanLndInvoice.js b/screen/lnd/scanLndInvoice.js index 16968cc5d..84015963b 100644 --- a/screen/lnd/scanLndInvoice.js +++ b/screen/lnd/scanLndInvoice.js @@ -54,6 +54,9 @@ const styles = StyleSheet.create({ alignItems: 'center', marginVertical: 4, }, + payButtonContainer: { + alignItems: 'center', + }, walletWrapTouch: { flexDirection: 'row', alignItems: 'center', @@ -435,7 +438,9 @@ export default class ScanLndInvoice extends React.Component { ) : ( - this.pay()} disabled={this.shouldDisablePayButton()} /> + + this.pay()} disabled={this.shouldDisablePayButton()} /> + )} From 809361c4a56282edf0e2b179bd151f4315999736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Rodriguez=20V=C3=A9lez?= Date: Sun, 6 Sep 2020 22:31:37 -0400 Subject: [PATCH 05/17] FIX:Center button --- screen/lnd/lndCreateInvoice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/screen/lnd/lndCreateInvoice.js b/screen/lnd/lndCreateInvoice.js index 663ca2235..26a557ad5 100644 --- a/screen/lnd/lndCreateInvoice.js +++ b/screen/lnd/lndCreateInvoice.js @@ -40,7 +40,7 @@ const styles = StyleSheet.create({ marginHorizontal: 56, marginVertical: 16, minHeight: 45, - alignContent: 'center', + alignItems: 'center', }, scanRoot: { height: 36, From 513dcabeb75e4f515de26409d58a4208e6e08b14 Mon Sep 17 00:00:00 2001 From: Overtorment Date: Mon, 7 Sep 2020 14:00:57 +0100 Subject: [PATCH 06/17] FIX: create-ln-invoice - button is not blocked, which can lead to multiple invoices created --- screen/lnd/lndCreateInvoice.js | 1 - 1 file changed, 1 deletion(-) diff --git a/screen/lnd/lndCreateInvoice.js b/screen/lnd/lndCreateInvoice.js index 26a557ad5..31d4cfc9e 100644 --- a/screen/lnd/lndCreateInvoice.js +++ b/screen/lnd/lndCreateInvoice.js @@ -205,7 +205,6 @@ export default class LNDCreateInvoice extends Component { async createInvoice() { this.setState({ isLoading: true }, async () => { try { - this.setState({ isLoading: false }); let amount = this.state.amount; switch (this.state.unit) { case BitcoinUnit.SATS: From cea2e925b57d77102ab1c06a9505ed1cc540affe Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Fri, 4 Sep 2020 22:05:41 +0000 Subject: [PATCH 07/17] Translate /loc/en.json in pt_PT translation completed updated for the source file '/loc/en.json' on the 'pt_PT' language. --- loc/pt_pt.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/loc/pt_pt.json b/loc/pt_pt.json index 714e72900..021f57d2b 100644 --- a/loc/pt_pt.json +++ b/loc/pt_pt.json @@ -299,6 +299,7 @@ "add_entropy_provide": "Entropia através de dados", "add_entropy_remain": "{gen} bytes de entropia gerada. Os bytes {rem} restantes serão obtidos do gerador de números aleatórios do sistema.", "add_import_wallet": "Importar wallet", + "import_file": "Importar ficheiro", "add_lightning": "Lightning", "add_lndhub": "Conecte-se ao seu LNDHub", "add_lndhub_error": "O endereço de nó fornecido não é um nó LNDHub válido.", @@ -344,7 +345,7 @@ "list_empty_txs1_lightning": "A wallet Lightning deve ser usada para as suas transações diárias. As taxas são muito baixas e a velocidade muito elevada", "list_empty_txs2": "nenhuma de momento", "list_empty_txs2_lightning": "\nPara começar a usar toque em \"gerir fundos\" e recarregue o seu saldo.", - "list_header": "Uma carteira representa um par de um segredo (chave privada) e um endereço que você pode compartilhar para receber moedas.", + "list_header": "Uma carteira representa um par de chaves, uma privada e outra que pode partilhar para receber bitcoin.", "list_import_error": "Foi encontrado um erro ao tentar importar esta carteira.", "list_import_problem": "Ocorreu um problema ao importar esta carteira", "list_latest_transaction": "últimas transacções", From 881b7cdc9136167494ce71f2666bcc35086f485a Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Fri, 4 Sep 2020 22:03:13 +0000 Subject: [PATCH 08/17] Translate /loc/en.json in pt_BR translation completed updated for the source file '/loc/en.json' on the 'pt_BR' language. --- loc/pt_br.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/loc/pt_br.json b/loc/pt_br.json index 3951fa27c..d9e5babff 100644 --- a/loc/pt_br.json +++ b/loc/pt_br.json @@ -299,6 +299,7 @@ "add_entropy_provide": "Entropia por meio de jogadas de dados", "add_entropy_remain": "{gen} bytes de entropia gerada. Os bytes {rem} restantes serão obtidos do gerador de números aleatórios do sistema.", "add_import_wallet": "Importar carteira", + "import_file": "Importar arquivo", "add_lightning": "Lightning", "add_lndhub": "Conectar ao seu LNDHub", "add_lndhub_error": "O endereço fornecido não é um LNDHub válido", @@ -344,7 +345,7 @@ "list_empty_txs1_lightning": "A carteira Relâmpago faz transações super rápidas (coisa de segundos) e tem taxas ridiculamente baratas, ideal para transações diárias e de baixo valor.", "list_empty_txs2": "nenhuma no momento", "list_empty_txs2_lightning": "\nPara começar a usar clique em \"administrar fundos\" e recarregue o seu saldo.", - "list_header": "Uma carteira representa um par de um segredo (chave privada) e um endereço que você pode compartilhar para receber moedas.", + "list_header": "Uma carteira representa um par de chaves, uma chave privada e uma que voce pode compartilhar para receber moedas.", "list_import_error": "Foi encontrado um erro ao tentar importar esta carteira.", "list_import_problem": "Ocorreu um problema ao importar esta carteira", "list_latest_transaction": "última transação", From e892dafa520b656b89d4ce309c5753c4530068e7 Mon Sep 17 00:00:00 2001 From: Overtorment Date: Mon, 7 Sep 2020 13:13:02 +0100 Subject: [PATCH 09/17] FIX: After broadcast, value of sent transaction is incorrect (closes #1072) --- screen/send/confirm.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/screen/send/confirm.js b/screen/send/confirm.js index 2c1777b14..ada25b90f 100644 --- a/screen/send/confirm.js +++ b/screen/send/confirm.js @@ -53,7 +53,7 @@ export default class Confirm extends Component { broadcast() { this.setState({ isLoading: true }, async () => { try { - await BlueElectrum.ping(); + // await BlueElectrum.ping(); await BlueElectrum.waitTillConnected(); if (this.isBiometricUseCapableAndEnabled) { @@ -72,7 +72,7 @@ export default class Confirm extends Component { EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED); // someone should fetch txs let amount = 0; const recipients = this.state.recipients; - if (recipients[0].amount === BitcoinUnit.MAX || !recipients[0].amount) { + if (recipients[0].amount === BitcoinUnit.MAX || (!recipients[0].amount && !recipients[0].value)) { amount = this.state.fromWallet.getBalance() - this.state.feeSatoshi; } else { for (const recipient of recipients) { From 1382b13f1501ccbd495b4739e4ed346c7b723ee6 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Tue, 8 Sep 2020 05:51:57 +0000 Subject: [PATCH 10/17] fix: upgrade dayjs from 1.8.32 to 1.8.33 Snyk has created this PR to upgrade dayjs from 1.8.32 to 1.8.33. See this package in npm: https://www.npmjs.com/package/dayjs See this project in Snyk: https://app.snyk.io/org/bluewallet/project/4d0df22a-0152-410a-8584-6df0d0a596d4?utm_source=github&utm_medium=upgrade-pr --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index ff8a789d6..284573354 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7318,9 +7318,9 @@ } }, "dayjs": { - "version": "1.8.32", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.32.tgz", - "integrity": "sha512-V91aTRu5btP+uzGHaaOfodckEfBWhmi9foRP7cauAO1PTB8+tZ9o0Jec7q6TIIRY1N4q1IfiKsZunkB/AEWqMQ==" + "version": "1.8.33", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.33.tgz", + "integrity": "sha512-881TDLZCdpJFKbraWRHcUG8zfMLLX400ENf9rFZDuWc5zYMss6xifo2PhlDX0ftOmR2NRmaIY47bAa4gKQfXqw==" }, "debug": { "version": "4.1.1", diff --git a/package.json b/package.json index 520fad0d8..365342271 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "buffer-reverse": "1.0.1", "coinselect": "3.1.12", "crypto-js": "3.1.9-1", - "dayjs": "1.8.32", + "dayjs": "1.8.33", "detox": "16.9.2", "ecurve": "1.0.6", "electrum-client": "git+https://github.com/BlueWallet/rn-electrum-client.git#99c75385f99e82b87fbfed2abfb2cccd322fe3e9", From 6026da4ef9b8bf860ed2d0ecc2cab2ae13511dc4 Mon Sep 17 00:00:00 2001 From: marcosrdz Date: Fri, 4 Sep 2020 20:47:34 -0400 Subject: [PATCH 11/17] REF: Use In-App browser on iOS --- ios/Podfile.lock | 6 +++++ package-lock.json | 8 ++++++ package.json | 1 + screen/wallets/buyBitcoin.js | 30 ++++++++++------------ screen/wallets/transactions.js | 47 +++++++++++++++++++--------------- 5 files changed, 54 insertions(+), 38 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b146c98d6..3ce7f77be 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -359,6 +359,8 @@ PODS: - React - RNHandoff (0.0.3): - React + - RNInAppBrowser (3.4.0): + - React - RNLocalize (1.4.0): - React - RNQuickAction (0.3.13): @@ -466,6 +468,7 @@ DEPENDENCIES: - RNFS (from `../node_modules/react-native-fs`) - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNHandoff (from `../node_modules/react-native-handoff`) + - RNInAppBrowser (from `../node_modules/react-native-inappbrowser-reborn`) - RNLocalize (from `../node_modules/react-native-localize`) - RNQuickAction (from `../node_modules/react-native-quick-actions`) - RNRate (from `../node_modules/react-native-rate`) @@ -602,6 +605,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-gesture-handler" RNHandoff: :path: "../node_modules/react-native-handoff" + RNInAppBrowser: + :path: "../node_modules/react-native-inappbrowser-reborn" RNLocalize: :path: "../node_modules/react-native-localize" RNQuickAction: @@ -694,6 +699,7 @@ SPEC CHECKSUMS: RNFS: 2bd9eb49dc82fa9676382f0585b992c424cd59df RNGestureHandler: b6b359bb800ae399a9c8b27032bdbf7c18f08a08 RNHandoff: d3b0754cca3a6bcd9b25f544f733f7f033ccf5fa + RNInAppBrowser: 6097fbc6b09051b40a6a9ec22caf7af40b115ec0 RNLocalize: fc27ee5878ce5a3af73873fb2d8e866e0d1e6d84 RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93 RNRate: 2b31dad120cd1b78e33c6034808561c386a3dddf diff --git a/package-lock.json b/package-lock.json index 284573354..af5d90c1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15049,6 +15049,14 @@ "resolved": "https://registry.npmjs.org/react-native-image-picker/-/react-native-image-picker-2.3.3.tgz", "integrity": "sha512-i/7JDnxKkUGSbFY2i7YqFNn3ncJm1YlcrPKXrXmJ/YUElz8tHkuwknuqBd9QCJivMfHX41cmq4XvdBDwIOtO+A==" }, + "react-native-inappbrowser-reborn": { + "version": "git+https://github.com/BlueWallet/react-native-inappbrowser.git#54ab29283b105b70496feb2e1edb4e002c4d9b3e", + "from": "git+https://github.com/BlueWallet/react-native-inappbrowser.git#v3.4.0", + "requires": { + "invariant": "^2.2.4", + "opencollective-postinstall": "^2.0.2" + } + }, "react-native-iphone-x-helper": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.2.1.tgz", diff --git a/package.json b/package.json index 365342271..63bfcecca 100644 --- a/package.json +++ b/package.json @@ -122,6 +122,7 @@ "react-native-handoff": "git+https://github.com/marcosrdz/react-native-handoff.git", "react-native-haptic-feedback": "1.10.0", "react-native-image-picker": "2.3.3", + "react-native-inappbrowser-reborn": "git+https://github.com/BlueWallet/react-native-inappbrowser.git#v3.4.0", "react-native-level-fs": "3.0.1", "react-native-linear-gradient": "2.5.6", "react-native-localize": " 1.4.0", diff --git a/screen/wallets/buyBitcoin.js b/screen/wallets/buyBitcoin.js index 80ac4446e..de4bf2467 100644 --- a/screen/wallets/buyBitcoin.js +++ b/screen/wallets/buyBitcoin.js @@ -1,9 +1,8 @@ import React, { Component } from 'react'; -import { StyleSheet, StatusBar, Linking } from 'react-native'; +import { StyleSheet, StatusBar } from 'react-native'; import { BlueNavigationStyle, BlueLoading, SafeBlueArea } from '../../BlueComponents'; import PropTypes from 'prop-types'; import { WebView } from 'react-native-webview'; -import { getSystemName } from 'react-native-device-info'; import { AppStorage, LightningCustodianWallet, WatchOnlyWallet } from '../../class'; const currency = require('../../blue_modules/currency'); const BlueApp: AppStorage = require('../../BlueApp'); @@ -23,19 +22,15 @@ export default class BuyBitcoin extends Component { this.state = { isLoading: true, wallet, - address: '', uri: '', }; } - async componentDidMount() { - console.log('buyBitcoin - componentDidMount'); - + static async generateURL(wallet) { let preferredCurrency = await currency.getPreferredCurrency(); preferredCurrency = preferredCurrency.endPointKey; /** @type {AbstractHDWallet|WatchOnlyWallet|LightningCustodianWallet} */ - const wallet = this.state.wallet; let address = ''; @@ -60,23 +55,24 @@ export default class BuyBitcoin extends Component { } } - const { safelloStateToken } = this.props.route.params; - let uri = 'https://bluewallet.io/buy-bitcoin-redirect.html?address=' + address; - if (safelloStateToken) { - uri += '&safelloStateToken=' + safelloStateToken; - } - if (preferredCurrency) { uri += '¤cy=' + preferredCurrency; } + return uri; + } - if (getSystemName() === 'Mac OS X') { - Linking.openURL(uri).finally(() => this.props.navigation.goBack(null)); - } else { - this.setState({ uri, isLoading: false, address }); + async componentDidMount() { + console.log('buyBitcoin - componentDidMount'); + + let uri = await BuyBitcoin.generateURL(this.state.wallet); + + const { safelloStateToken } = this.props.route.params; + if (safelloStateToken) { + uri += '&safelloStateToken=' + safelloStateToken; } + this.setState({ uri, isLoading: false }); } render() { diff --git a/screen/wallets/transactions.js b/screen/wallets/transactions.js index 119b8c87d..012b4e3c3 100644 --- a/screen/wallets/transactions.js +++ b/screen/wallets/transactions.js @@ -29,6 +29,7 @@ import { BlueWalletNavigationHeader, BlueAlertWalletExportReminder, } from '../../BlueComponents'; +import InAppBrowser from 'react-native-inappbrowser-reborn'; import WalletGradient from '../../class/wallet-gradient'; import { Icon } from 'react-native-elements'; import { LightningCustodianWallet, WatchOnlyWallet } from '../../class'; @@ -40,6 +41,7 @@ import { BlueCurrentTheme } from '../../components/themes'; import ActionSheet from '../ActionSheet'; import loc from '../../loc'; import { getSystemName } from 'react-native-device-info'; +import BuyBitcoin from './buyBitcoin'; const BlueApp = require('../../BlueApp'); const EV = require('../../blue_modules/events'); const BlueElectrum = require('../../blue_modules/BlueElectrum'); @@ -411,11 +413,7 @@ export default class WalletTransactions extends Component { hideChevron component={TouchableOpacity} onPress={a => { - this.setState({ isManageFundsModalVisible: false }, async () => { - this.props.navigation.navigate('BuyBitcoin', { - wallet: this.state.wallet, - }); - }); + this.setState({ isManageFundsModalVisible: false }, this.navigateToBuyBitcoin); }} title={loc.lnd.refill_card} /> @@ -485,14 +483,7 @@ export default class WalletTransactions extends Component { renderSellFiat = () => { return ( - - this.props.navigation.navigate('BuyBitcoin', { - wallet: this.state.wallet, - }) - } - style={styles.marketplaceButton2} - > + {loc.wallets.list_tap_here_to_buy} ); @@ -655,6 +646,27 @@ export default class WalletTransactions extends Component { } }; + navigateToBuyBitcoin = async () => { + const uri = await BuyBitcoin.generateURL(this.state.wallet); + if (getSystemName() === 'Mac OS X') { + InAppBrowser.isAvailable() + .then(_value => InAppBrowser.open(uri, { dismissButtonStyle: 'done' })) + .catch(_error => Linking.openURL(uri)); + } else if (Platform.OS === 'ios') { + InAppBrowser.isAvailable() + .then(_value => InAppBrowser.open(uri, { dismissButtonStyle: 'done' })) + .catch(_error => + this.props.navigation.navigate('BuyBitcoin', { + wallet: this.state.wallet, + }), + ); + } else { + this.props.navigation.navigate('BuyBitcoin', { + wallet: this.state.wallet, + }); + } + }; + render() { const { navigate } = this.props.navigation; return ( @@ -722,14 +734,7 @@ export default class WalletTransactions extends Component { {this.isLightning() && {loc.wallets.list_empty_txs2_lightning}} {!this.isLightning() && ( - - this.props.navigation.navigate('BuyBitcoin', { - wallet: this.state.wallet, - }) - } - style={styles.buyBitcoin} - > + {loc.wallets.list_tap_here_to_buy} )} From a590e28997427950d86cf22be09bd870c734bd1d Mon Sep 17 00:00:00 2001 From: marcosrdz Date: Fri, 4 Sep 2020 20:49:32 -0400 Subject: [PATCH 12/17] OPS: Lint --- screen/wallets/buyBitcoin.js | 3 --- screen/wallets/transactions.js | 8 ++++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/screen/wallets/buyBitcoin.js b/screen/wallets/buyBitcoin.js index de4bf2467..e39c6c3b0 100644 --- a/screen/wallets/buyBitcoin.js +++ b/screen/wallets/buyBitcoin.js @@ -101,9 +101,6 @@ BuyBitcoin.propTypes = { safelloStateToken: PropTypes.string, }), }), - navigation: PropTypes.shape({ - goBack: PropTypes.func, - }), }; BuyBitcoin.navigationOptions = ({ navigation }) => ({ diff --git a/screen/wallets/transactions.js b/screen/wallets/transactions.js index 012b4e3c3..41dc8bc84 100644 --- a/screen/wallets/transactions.js +++ b/screen/wallets/transactions.js @@ -385,7 +385,7 @@ export default class WalletTransactions extends Component { { + onPress={() => { const wallets = [...BlueApp.getWallets().filter(item => item.chain === Chain.ONCHAIN && item.allowSend())]; if (wallets.length === 0) { alert(loc.lnd.refill_create); @@ -399,7 +399,7 @@ export default class WalletTransactions extends Component { { + onPress={() => { this.setState({ isManageFundsModalVisible: false }, () => this.props.navigation.navigate('ReceiveDetails', { secret: this.state.wallet.getSecret(), @@ -412,7 +412,7 @@ export default class WalletTransactions extends Component { { + onPress={() => { this.setState({ isManageFundsModalVisible: false }, this.navigateToBuyBitcoin); }} title={loc.lnd.refill_card} @@ -422,7 +422,7 @@ export default class WalletTransactions extends Component { title={loc.lnd.exchange} hideChevron component={TouchableOpacity} - onPress={a => { + onPress={() => { this.setState({ isManageFundsModalVisible: false }); Linking.openURL('https://zigzag.io/?utm_source=integration&utm_medium=bluewallet&utm_campaign=withdrawLink'); }} From 04b155e1cc091b692493cac39e52be5b395de31b Mon Sep 17 00:00:00 2001 From: marcosrdz Date: Mon, 7 Sep 2020 11:35:51 -0400 Subject: [PATCH 13/17] REF: Single call for buy bitcoin --- screen/lnd/scanLndInvoice.js | 2 +- screen/wallets/buyBitcoin.js | 21 +++++++++++++++++++-- screen/wallets/transactions.js | 22 ++-------------------- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/screen/lnd/scanLndInvoice.js b/screen/lnd/scanLndInvoice.js index 84015963b..42c10b859 100644 --- a/screen/lnd/scanLndInvoice.js +++ b/screen/lnd/scanLndInvoice.js @@ -438,7 +438,7 @@ export default class ScanLndInvoice extends React.Component { ) : ( - + this.pay()} disabled={this.shouldDisablePayButton()} /> )} diff --git a/screen/wallets/buyBitcoin.js b/screen/wallets/buyBitcoin.js index e39c6c3b0..be7791abb 100644 --- a/screen/wallets/buyBitcoin.js +++ b/screen/wallets/buyBitcoin.js @@ -1,12 +1,13 @@ import React, { Component } from 'react'; -import { StyleSheet, StatusBar } from 'react-native'; +import { StyleSheet, StatusBar, Linking, Platform } from 'react-native'; import { BlueNavigationStyle, BlueLoading, SafeBlueArea } from '../../BlueComponents'; import PropTypes from 'prop-types'; import { WebView } from 'react-native-webview'; import { AppStorage, LightningCustodianWallet, WatchOnlyWallet } from '../../class'; +import InAppBrowser from 'react-native-inappbrowser-reborn'; +import * as NavigationService from '../../NavigationService'; const currency = require('../../blue_modules/currency'); const BlueApp: AppStorage = require('../../BlueApp'); - const styles = StyleSheet.create({ root: { flex: 1, @@ -108,3 +109,19 @@ BuyBitcoin.navigationOptions = ({ navigation }) => ({ title: '', headerLeft: null, }); + +BuyBitcoin.navigate = async wallet => { + const uri = await BuyBitcoin.generateURL(wallet); + if (Platform.OS === 'ios') { + InAppBrowser.isAvailable() + .then(_value => InAppBrowser.open(uri, { dismissButtonStyle: 'done' })) + .catch(error => { + console.log(error); + Linking.openURL(uri); + }); + } else { + NavigationService.navigate('BuyBitcoin', { + wallet, + }); + } +}; diff --git a/screen/wallets/transactions.js b/screen/wallets/transactions.js index 41dc8bc84..601ac197a 100644 --- a/screen/wallets/transactions.js +++ b/screen/wallets/transactions.js @@ -29,7 +29,6 @@ import { BlueWalletNavigationHeader, BlueAlertWalletExportReminder, } from '../../BlueComponents'; -import InAppBrowser from 'react-native-inappbrowser-reborn'; import WalletGradient from '../../class/wallet-gradient'; import { Icon } from 'react-native-elements'; import { LightningCustodianWallet, WatchOnlyWallet } from '../../class'; @@ -646,25 +645,8 @@ export default class WalletTransactions extends Component { } }; - navigateToBuyBitcoin = async () => { - const uri = await BuyBitcoin.generateURL(this.state.wallet); - if (getSystemName() === 'Mac OS X') { - InAppBrowser.isAvailable() - .then(_value => InAppBrowser.open(uri, { dismissButtonStyle: 'done' })) - .catch(_error => Linking.openURL(uri)); - } else if (Platform.OS === 'ios') { - InAppBrowser.isAvailable() - .then(_value => InAppBrowser.open(uri, { dismissButtonStyle: 'done' })) - .catch(_error => - this.props.navigation.navigate('BuyBitcoin', { - wallet: this.state.wallet, - }), - ); - } else { - this.props.navigation.navigate('BuyBitcoin', { - wallet: this.state.wallet, - }); - } + navigateToBuyBitcoin = () => { + BuyBitcoin.navigate(this.state.wallet); }; render() { From d69e58175eb1c46b21064b88f005127061c28266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Rodriguez=20V=C3=A9lez?= Date: Tue, 8 Sep 2020 12:06:41 -0400 Subject: [PATCH 14/17] Sidebar (#1596) * ADD: Split for large devices * Update Podfile.lock * FIX: Use isTablet * ADD: If wallet is selected, lower opacity on others * Update transactions.js * Update transactions.js * OPS: Podfile.lock * Update Podfile.lock * Update Navigation.js * Update transactions.js --- .gitignore | 3 + App.js | 4 +- BlueComponents.js | 74 +- Navigation.js | 69 +- UnlockWith.js | 2 +- android/app/build.gradle | 1 - android/app/src/main/AndroidManifest.xml | 1 - .../bluewallet/bluewallet/MainActivity.java | 14 + .../app/src/main/res/values-sw600dp/bools.xml | 4 + .../app/src/main/res/values-xlarge/bools.xml | 4 + android/app/src/main/res/values/bools.xml | 4 + ios/Podfile.lock | 18 +- package-lock.json | 35 +- package.json | 4 +- screen/send/details.js | 25 +- screen/wallets/drawerList.js | 312 ++++++++ screen/wallets/list.js | 52 +- screen/wallets/transactions.js | 751 ++++++++---------- screen/wallets/xpub.js | 6 +- 19 files changed, 893 insertions(+), 490 deletions(-) create mode 100644 android/app/src/main/res/values-sw600dp/bools.xml create mode 100644 android/app/src/main/res/values-xlarge/bools.xml create mode 100644 android/app/src/main/res/values/bools.xml create mode 100644 screen/wallets/drawerList.js diff --git a/.gitignore b/.gitignore index c6aef59fc..938160ec0 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,6 @@ artifacts/ # Editors .vscode/ +*.mx +*.realm +*.realm.lock \ No newline at end of file diff --git a/App.js b/App.js index d1702291e..2cee50784 100644 --- a/App.js +++ b/App.js @@ -15,7 +15,6 @@ import Clipboard from '@react-native-community/clipboard'; import Modal from 'react-native-modal'; import { NavigationContainer, CommonActions } from '@react-navigation/native'; import { SafeAreaProvider } from 'react-native-safe-area-context'; -import Navigation from './Navigation'; import { navigationRef } from './NavigationService'; import * as NavigationService from './NavigationService'; import { BlueTextCentered, BlueButton, SecondButton } from './BlueComponents'; @@ -27,6 +26,7 @@ import OnAppLaunch from './class/on-app-launch'; import DeeplinkSchemaMatch from './class/deeplink-schema-match'; import loc from './loc'; import { BlueDefaultTheme, BlueDarkTheme, BlueCurrentTheme } from './components/themes'; +import InitRoot from './Navigation'; const A = require('./blue_modules/analytics'); if (process.env.NODE_ENV !== 'development') { @@ -298,7 +298,7 @@ export default class App extends React.Component { - + {this.renderClipboardContentModal()} diff --git a/BlueComponents.js b/BlueComponents.js index 4599faa0c..5197887c4 100644 --- a/BlueComponents.js +++ b/BlueComponents.js @@ -898,6 +898,49 @@ export const BlueHeaderDefaultSubHooks = props => { ); }; +export const BlueHeaderDefaultMainHooks = props => { + const { colors } = useTheme(); + return ( +
+ + + ) + } + /> + ); +}; + export class BlueHeaderDefaultMain extends Component { render() { return ( @@ -924,6 +967,7 @@ export class BlueHeaderDefaultMain extends Component { backgroundColor: BlueCurrentTheme.colors.background, borderTopColor: BlueCurrentTheme.colors.background, borderBottomColor: BlueCurrentTheme.colors.background, + borderBottomWidth: 0, }} rightComponent={ this.props.onNewWalletPress && ( @@ -1548,7 +1592,12 @@ export class ManageFundsBigButton extends Component { export class NewWalletPanel extends Component { render() { return ( - + { +const WalletCarouselItem = ({ item, index, onPress, handleLongPress, isSelectedWallet }) => { const scaleValue = new Animated.Value(1.0); const onPressedIn = () => { @@ -1896,9 +1945,14 @@ const WalletCarouselItem = ({ item, index, onPress, handleLongPress }) => { ); } else { + let opacity = 1.0; + + if (isSelectedWallet === false) { + opacity = 0.5; + } return ( { - return ; + return ( + + ); }; snapToItem = item => { @@ -2023,7 +2085,6 @@ export class WalletsCarousel extends Component { )} ); diff --git a/Navigation.js b/Navigation.js index 2273d9a40..5f7a2fe33 100644 --- a/Navigation.js +++ b/Navigation.js @@ -1,7 +1,7 @@ -// import { createAppContainer } from '@react-navigation/native'; import React from 'react'; import { createStackNavigator, TransitionPresets } from '@react-navigation/stack'; -import { Platform, Dimensions } from 'react-native'; +import { createDrawerNavigator } from '@react-navigation/drawer'; +import { Platform, useWindowDimensions, Dimensions } from 'react-native'; import Settings from './screen/settings/settings'; import About from './screen/settings/about'; @@ -66,17 +66,18 @@ import LnurlPaySuccess from './screen/lnd/lnurlPaySuccess'; import LoadingScreen from './LoadingScreen'; import UnlockWith from './UnlockWith'; import { BlueNavigationStyle } from './BlueComponents'; +import DrawerList from './screen/wallets/drawerList'; +import { isTablet } from 'react-native-device-info'; -const SCREEN_HEIGHT = Dimensions.get('window').height; const defaultScreenOptions = Platform.OS === 'ios' ? ({ route, navigation }) => ({ gestureEnabled: true, - gestureResponseDistance: { vertical: SCREEN_HEIGHT, horizontal: 50 }, cardOverlayEnabled: true, cardStyle: { backgroundColor: '#FFFFFF' }, headerStatusBarHeight: navigation.dangerouslyGetState().routes.indexOf(route) > 0 ? 10 : undefined, ...TransitionPresets.ModalPresentationIOS, + gestureResponseDistance: { vertical: Dimensions.get('window').height, horizontal: 50 }, }) : undefined; const defaultStackScreenOptions = @@ -250,17 +251,50 @@ const HodlHodlLoginRoot = () => ( ); -const RootStack = createStackNavigator(); -const Navigation = () => ( - - {/* stacks */} - - = Dimensions.get('screen').width / 3 && isTablet(); + const drawerStyle = { width: '0%' }; + return ( + } + > + + + ); +} + +const InitStack = createStackNavigator(); +const InitRoot = () => ( + + + - + + + + +); + +const RootStack = createStackNavigator(); +const Navigation = () => ( + + {/* stacks */} + @@ -269,15 +303,7 @@ const Navigation = () => ( - + {/* screens */} @@ -286,8 +312,7 @@ const Navigation = () => ( - ); -export default Navigation; +export default InitRoot; diff --git a/UnlockWith.js b/UnlockWith.js index 53046d642..856111563 100644 --- a/UnlockWith.js +++ b/UnlockWith.js @@ -81,7 +81,7 @@ export default class UnlockWith extends Component { successfullyAuthenticated = () => { EV(EV.enum.WALLETS_INITIALIZED); - NavigationService.dispatch(StackActions.replace('WalletsRoot')); + NavigationService.dispatch(StackActions.replace('DrawerRoot')); }; unlockWithBiometrics = async () => { diff --git a/android/app/build.gradle b/android/app/build.gradle index f95d5cc93..a950f4fe6 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -122,7 +122,6 @@ def enableHermes = project.ext.react.get("enableHermes", false); android { compileSdkVersion rootProject.ext.compileSdkVersion - compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 4d9e92b90..079e0df15 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -51,7 +51,6 @@ android:label="@string/app_name" android:launchMode="singleInstance" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" - android:screenOrientation="portrait" android:windowSoftInputMode="adjustResize"> diff --git a/android/app/src/main/java/io/bluewallet/bluewallet/MainActivity.java b/android/app/src/main/java/io/bluewallet/bluewallet/MainActivity.java index 160e4ff9a..722163aab 100644 --- a/android/app/src/main/java/io/bluewallet/bluewallet/MainActivity.java +++ b/android/app/src/main/java/io/bluewallet/bluewallet/MainActivity.java @@ -1,5 +1,11 @@ package io.bluewallet.bluewallet; +import android.content.pm.ActivityInfo; +import android.os.Bundle; +import android.os.PersistableBundle; + +import androidx.annotation.Nullable; + import com.facebook.react.ReactActivity; public class MainActivity extends ReactActivity { @@ -12,4 +18,12 @@ public class MainActivity extends ReactActivity { protected String getMainComponentName() { return "BlueWallet"; } + + @Override +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getResources().getBoolean(R.bool.portrait_only)) { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } +} } diff --git a/android/app/src/main/res/values-sw600dp/bools.xml b/android/app/src/main/res/values-sw600dp/bools.xml new file mode 100644 index 000000000..4a4d8ab34 --- /dev/null +++ b/android/app/src/main/res/values-sw600dp/bools.xml @@ -0,0 +1,4 @@ + + + false + \ No newline at end of file diff --git a/android/app/src/main/res/values-xlarge/bools.xml b/android/app/src/main/res/values-xlarge/bools.xml new file mode 100644 index 000000000..4a4d8ab34 --- /dev/null +++ b/android/app/src/main/res/values-xlarge/bools.xml @@ -0,0 +1,4 @@ + + + false + \ No newline at end of file diff --git a/android/app/src/main/res/values/bools.xml b/android/app/src/main/res/values/bools.xml new file mode 100644 index 000000000..1a61e5a64 --- /dev/null +++ b/android/app/src/main/res/values/bools.xml @@ -0,0 +1,4 @@ + + + true + \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 3ce7f77be..67e65a625 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -250,13 +250,13 @@ PODS: - React - react-native-blur (0.8.0): - React - - react-native-camera (3.35.0): + - react-native-camera (3.38.0): - React - - react-native-camera/RCT (= 3.35.0) - - react-native-camera/RN (= 3.35.0) - - react-native-camera/RCT (3.35.0): + - react-native-camera/RCT (= 3.38.0) + - react-native-camera/RN (= 3.38.0) + - react-native-camera/RCT (3.38.0): - React - - react-native-camera/RN (3.35.0): + - react-native-camera/RN (3.38.0): - React - react-native-document-picker (3.5.4): - React @@ -369,6 +369,8 @@ PODS: - React - RNReactNativeHapticFeedback (1.10.0): - React + - RNReanimated (1.13.0): + - React - RNScreens (2.10.1): - React - RNSecureKeyStore (1.0.0): @@ -473,6 +475,7 @@ DEPENDENCIES: - RNQuickAction (from `../node_modules/react-native-quick-actions`) - RNRate (from `../node_modules/react-native-rate`) - RNReactNativeHapticFeedback (from `../node_modules/react-native-haptic-feedback`) + - RNReanimated (from `../node_modules/react-native-reanimated`) - RNScreens (from `../node_modules/react-native-screens`) - RNSecureKeyStore (from `../node_modules/react-native-secure-key-store/ios`) - "RNSentry (from `../node_modules/@sentry/react-native`)" @@ -615,6 +618,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-rate" RNReactNativeHapticFeedback: :path: "../node_modules/react-native-haptic-feedback" + RNReanimated: + :path: "../node_modules/react-native-reanimated" RNScreens: :path: "../node_modules/react-native-screens" RNSecureKeyStore: @@ -668,7 +673,7 @@ SPEC CHECKSUMS: react-native-biometrics: c892904948a32295b128f633bcc11eda020645c5 react-native-blue-crypto: 23f1558ad3d38d7a2edb7e2f6ed1bc520ed93e56 react-native-blur: cad4d93b364f91e7b7931b3fa935455487e5c33c - react-native-camera: 9dd96065b956306de03ef2a2efc3583019f95941 + react-native-camera: 9a0db39fc97a479fe472e86ce424545478133a2f react-native-document-picker: c5752781fbc0c126c627c1549b037c139444a4cf react-native-geolocation: cbd9d6bd06bac411eed2671810f454d4908484a8 react-native-image-picker: a6c3d644751a388b0fc8b56822ff7cbd398a3008 @@ -704,6 +709,7 @@ SPEC CHECKSUMS: RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93 RNRate: 2b31dad120cd1b78e33c6034808561c386a3dddf RNReactNativeHapticFeedback: 22c5ecf474428766c6b148f96f2ff6155cd7225e + RNReanimated: 89f5e0a04d1dd52fbf27e7e7030d8f80a646a3fc RNScreens: b748efec66e095134c7166ca333b628cd7e6f3e2 RNSecureKeyStore: f1ad870e53806453039f650720d2845c678d89c8 RNSentry: 2bae4ffee2e66376ef42cb845a494c3bde17bc56 diff --git a/package-lock.json b/package-lock.json index af5d90c1d..7824bf208 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4655,6 +4655,15 @@ } } }, + "@react-navigation/drawer": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/@react-navigation/drawer/-/drawer-5.9.0.tgz", + "integrity": "sha512-YcuJ9QD4cFyjfXJx6vMsG3u3bfOU/Nt+GvMMl+4rZOxw2LStmubY1jqfsEAli/+dTUHv5kXJf5dF+/GhUCqA5g==", + "requires": { + "color": "^3.1.2", + "react-native-iphone-x-helper": "^1.2.1" + } + }, "@react-navigation/native": { "version": "5.7.3", "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-5.7.3.tgz", @@ -7792,9 +7801,9 @@ } }, "elliptic": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", - "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", "requires": { "bn.js": "^4.4.0", "brorand": "^1.0.1", @@ -12148,9 +12157,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" }, "lodash.isequal": { "version": "4.5.0", @@ -14971,9 +14980,9 @@ "from": "git+https://github.com/Overtorment/react-native-blue-crypto.git" }, "react-native-camera": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/react-native-camera/-/react-native-camera-3.35.0.tgz", - "integrity": "sha512-hs9ja6qc2JMB2oYDzYuzOF1GUMRZY0O7KX1e7K7WJBQzAwsXnlwm4d2r6P61kGMk1FUDUn186Yc3kb5vfEAyoA==", + "version": "3.38.0", + "resolved": "https://registry.npmjs.org/react-native-camera/-/react-native-camera-3.38.0.tgz", + "integrity": "sha512-mczUap9TWHeFuGOY5fCoMnPWv704BiPyGNU5p4filB/XuQL4/M93Teqgw6gWMdd8KfElY9uqH4QNtHruJ67A5A==", "requires": { "prop-types": "^15.6.2" } @@ -15179,6 +15188,14 @@ "prop-types": "^15.7.2" } }, + "react-native-reanimated": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-1.13.0.tgz", + "integrity": "sha512-uadP/0QO+4TCsyPSvzRdl+76NPM7Bp8M25KQLB4Hg3tWBMjhrMrETnzNi33L/OPfmhU+7rceyi0QPe/DxKT5bQ==", + "requires": { + "fbjs": "^1.0.0" + } + }, "react-native-safe-area-context": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-3.1.4.tgz", diff --git a/package.json b/package.json index 63bfcecca..5841faa7d 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "@react-native-community/masked-view": "0.1.10", "@react-native-community/push-notification-ios": "1.4.0", "@react-native-community/slider": "3.0.3", + "@react-navigation/drawer": "5.9.0", "@react-navigation/native": "5.7.3", "@react-navigation/stack": "5.9.0", "@remobile/react-native-qrcode-local-image": "git+https://github.com/BlueWallet/react-native-qrcode-local-image.git", @@ -112,7 +113,7 @@ "react-native": "0.62.2", "react-native-biometrics": "git+https://github.com/BlueWallet/react-native-biometrics.git#f62b0b193e10376d4a0e8195c700a364ed4e5aaa", "react-native-blue-crypto": "git+https://github.com/Overtorment/react-native-blue-crypto.git", - "react-native-camera": "3.35.0", + "react-native-camera": "3.38.0", "react-native-default-preference": "1.4.3", "react-native-device-info": "6.0.1", "react-native-document-picker": "git+https://github.com/BlueWallet/react-native-document-picker.git#3684d4fcc2bc0b47c32be39024e4796004c3e428", @@ -137,6 +138,7 @@ "react-native-quick-actions": "0.3.13", "react-native-randombytes": "3.5.3", "react-native-rate": "1.2.4", + "react-native-reanimated": "1.13.0", "react-native-safe-area-context": "3.1.4", "react-native-screens": "2.10.1", "react-native-secure-key-store": "git+https://github.com/BlueWallet/react-native-secure-key-store.git#4ba25dedb3d5ae15c22fd0ea0555116055630966", diff --git a/screen/send/details.js b/screen/send/details.js index 2d64d09ed..f5d72da3f 100644 --- a/screen/send/details.js +++ b/screen/send/details.js @@ -46,7 +46,6 @@ import { BlueCurrentTheme } from '../../components/themes'; const bitcoin = require('bitcoinjs-lib'); const currency = require('../../blue_modules/currency'); const BigNumber = require('bignumber.js'); -const { width } = Dimensions.get('window'); const BlueApp: AppStorage = require('../../BlueApp'); const btcAddressRx = /^[a-zA-Z0-9]{26,35}$/; @@ -262,6 +261,7 @@ export default class SendDetails extends Component { feeSliderValue: 1, amountUnit: fromWallet.preferredBalanceUnit, // default for whole screen renderWalletSelectionButtonHidden: false, + width: Dimensions.get('window').width - 320, }; } } @@ -473,7 +473,7 @@ export default class SendDetails extends Component { } else if (index === this.state.addresses.length - 1) { this.scrollView.scrollToEnd(); } else { - const page = Math.round(width * (this.state.addresses.length - 2)); + const page = Math.round(this.state.width * (this.state.addresses.length - 2)); this.scrollView.scrollTo({ x: page, y: 0, animated: true }); } this.setState({ isLoading: false, recipientsScrollIndex: index }); @@ -619,9 +619,10 @@ export default class SendDetails extends Component { renderFeeSelectionModal = () => { return ( { if (this.state.fee < 1 || this.state.feeSliderValue < 1) { this.setState({ fee: Number(1), feeSliderValue: Number(1) }); @@ -726,8 +727,9 @@ export default class SendDetails extends Component { const isSendMaxUsed = this.state.addresses.some(element => element.amount === BitcoinUnit.MAX); return ( { Keyboard.dismiss(); @@ -864,7 +866,7 @@ export default class SendDetails extends Component { Keyboard.dismiss(); var offset = e.nativeEvent.contentOffset; if (offset) { - const page = Math.round(offset.x / width); + const page = Math.round(offset.x / this.state.width); if (this.state.recipientsScrollIndex !== page) { this.setState({ recipientsScrollIndex: page }); } @@ -875,7 +877,7 @@ export default class SendDetails extends Component { Keyboard.dismiss(); var offset = this.scrollView.contentOffset; if (offset) { - const page = Math.round(offset.x / width); + const page = Math.round(offset.x / this.state.width); return page; } return 0; @@ -883,9 +885,10 @@ export default class SendDetails extends Component { renderBitcoinTransactionInfoFields = () => { const rows = []; + for (const [index, item] of this.state.addresses.entries()) { rows.push( - + { + this.setState({ width: e.nativeEvent.layout.width }); + }; + render() { if (this.state.isLoading || typeof this.state.fromWallet === 'undefined') { return ( @@ -1001,7 +1008,7 @@ export default class SendDetails extends Component { } return ( - + diff --git a/screen/wallets/drawerList.js b/screen/wallets/drawerList.js new file mode 100644 index 000000000..fcd032956 --- /dev/null +++ b/screen/wallets/drawerList.js @@ -0,0 +1,312 @@ +import React, { useRef, useState, useEffect } from 'react'; +import { StatusBar, View, TouchableOpacity, InteractionManager, StyleSheet, Alert, useWindowDimensions } from 'react-native'; +import { DrawerContentScrollView } from '@react-navigation/drawer'; +import { WalletsCarousel, BlueNavigationStyle, BlueHeaderDefaultMainHooks } from '../../BlueComponents'; +import { Icon } from 'react-native-elements'; +import ReactNativeHapticFeedback from 'react-native-haptic-feedback'; +import PropTypes from 'prop-types'; +import { AppStorage, PlaceholderWallet } from '../../class'; +import WalletImport from '../../class/wallet-import'; +import * as NavigationService from '../../NavigationService'; +import loc from '../../loc'; +import { BlueCurrentTheme } from '../../components/themes'; +import { useTheme, useRoute } from '@react-navigation/native'; +import { SafeAreaView } from 'react-native-safe-area-context'; +const EV = require('../../blue_modules/events'); +const BlueApp: AppStorage = require('../../BlueApp'); +const BlueElectrum = require('../../blue_modules/BlueElectrum'); + +const DrawerList = props => { + const walletsCarousel = useRef(); + const [wallets, setWallets] = useState(BlueApp.getWallets().concat(false)); + const height = useWindowDimensions().height; + const { colors } = useTheme(); + const { selectedWallet } = useRoute().params || ''; + const stylesHook = StyleSheet.create({ + root: { + backgroundColor: colors.brandingColor, + }, + }); + let lastSnappedTo = 0; + + const refreshTransactions = () => { + InteractionManager.runAfterInteractions(async () => { + let noErr = true; + try { + // await BlueElectrum.ping(); + await BlueElectrum.waitTillConnected(); + const balanceStart = +new Date(); + await BlueApp.fetchWalletBalances(lastSnappedTo || 0); + const balanceEnd = +new Date(); + console.log('fetch balance took', (balanceEnd - balanceStart) / 1000, 'sec'); + const start = +new Date(); + await BlueApp.fetchWalletTransactions(lastSnappedTo || 0); + const end = +new Date(); + console.log('fetch tx took', (end - start) / 1000, 'sec'); + } catch (err) { + noErr = false; + console.warn(err); + } + if (noErr) await BlueApp.saveToDisk(); // caching + + redrawScreen(); + }); + }; + + useEffect(() => { + EV(EV.enum.TRANSACTIONS_COUNT_CHANGED); + console.log('drawerList wallets changed'); + }, [wallets]); + + const redrawScreen = (scrollToEnd = false) => { + console.log('drawerList redrawScreen()'); + + const newWallets = BlueApp.getWallets().concat(false); + if (scrollToEnd) { + scrollToEnd = newWallets.length > wallets.length; + } + + setWallets(newWallets); + if (scrollToEnd) { + // eslint-disable-next-line no-unused-expressions + walletsCarousel.current?.snapToItem(wallets.length - 2); + } + }; + + useEffect(() => { + // here, when we receive REMOTE_TRANSACTIONS_COUNT_CHANGED we fetch TXs and balance for current wallet. + // placing event subscription here so it gets exclusively re-subscribed more often. otherwise we would + // have to unsubscribe on unmount and resubscribe again on mount. + EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED, refreshTransactions, true); + + EV(EV.enum.WALLETS_COUNT_CHANGED, () => redrawScreen(true)); + + console.log('drawerList useEffect'); + // the idea is that upon wallet launch we will refresh + // all balances and all transactions here: + redrawScreen(); + InteractionManager.runAfterInteractions(async () => { + try { + await BlueElectrum.waitTillConnected(); + const balanceStart = +new Date(); + await BlueApp.fetchWalletBalances(); + const balanceEnd = +new Date(); + console.log('fetch all wallet balances took', (balanceEnd - balanceStart) / 1000, 'sec'); + const start = +new Date(); + await BlueApp.fetchWalletTransactions(); + const end = +new Date(); + console.log('fetch all wallet txs took', (end - start) / 1000, 'sec'); + redrawScreen(); + await BlueApp.saveToDisk(); + } catch (error) { + console.log(error); + } + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const handleClick = index => { + console.log('click', index); + const wallet = BlueApp.wallets[index]; + if (wallet) { + if (wallet.type === PlaceholderWallet.type) { + Alert.alert( + loc.wallets.add_details, + loc.wallets.list_import_problem, + [ + { + text: loc.wallets.details_delete, + onPress: () => { + WalletImport.removePlaceholderWallet(); + EV(EV.enum.WALLETS_COUNT_CHANGED); + }, + style: 'destructive', + }, + { + text: loc.wallets.list_tryagain, + onPress: () => { + props.navigation.navigate('AddWalletRoot', { screen: 'ImportWallet', params: { label: wallet.getSecret() } }); + WalletImport.removePlaceholderWallet(); + EV(EV.enum.WALLETS_COUNT_CHANGED); + }, + style: 'default', + }, + ], + { cancelable: false }, + ); + } else { + props.navigation.navigate('WalletTransactions', { + wallet: wallet, + key: `WalletTransactions-${wallet.getID()}`, + }); + } + } else { + // if its out of index - this must be last card with incentive to create wallet + if (!BlueApp.getWallets().some(wallet => wallet.type === PlaceholderWallet.type)) { + props.navigation.navigate('Navigation', { screen: 'AddWalletRoot' }); + } + } + }; + + const handleLongPress = () => { + if (BlueApp.getWallets().length > 1 && !BlueApp.getWallets().some(wallet => wallet.type === PlaceholderWallet.type)) { + props.navigation.navigate('ReorderWallets'); + } else { + ReactNativeHapticFeedback.trigger('notificationError', { ignoreAndroidSystemSettings: false }); + } + }; + + const onSnapToItem = index => { + console.log('onSnapToItem', index); + lastSnappedTo = index; + if (index < BlueApp.getWallets().length) { + // not the last + } + + if (wallets[index].type === PlaceholderWallet.type) { + return; + } + + // now, lets try to fetch balance and txs for this wallet in case it has changed + lazyRefreshWallet(index); + }; + + /** + * Decides whether wallet with such index shoud be refreshed, + * refreshes if yes and redraws the screen + * @param index {Integer} Index of the wallet. + * @return {Promise.} + */ + const lazyRefreshWallet = async index => { + /** @type {Array.} wallets */ + const wallets = BlueApp.getWallets(); + if (!wallets[index]) { + return; + } + + const oldBalance = wallets[index].getBalance(); + let noErr = true; + let didRefresh = false; + + try { + if (wallets[index] && wallets[index].type !== PlaceholderWallet.type && wallets[index].timeToRefreshBalance()) { + console.log('snapped to, and now its time to refresh wallet #', index); + await wallets[index].fetchBalance(); + if (oldBalance !== wallets[index].getBalance() || wallets[index].getUnconfirmedBalance() !== 0) { + console.log('balance changed, thus txs too'); + // balance changed, thus txs too + await wallets[index].fetchTransactions(); + redrawScreen(); + didRefresh = true; + } else if (wallets[index].timeToRefreshTransaction()) { + console.log(wallets[index].getLabel(), 'thinks its time to refresh TXs'); + await wallets[index].fetchTransactions(); + if (wallets[index].fetchPendingTransactions) { + await wallets[index].fetchPendingTransactions(); + } + if (wallets[index].fetchUserInvoices) { + await wallets[index].fetchUserInvoices(); + await wallets[index].fetchBalance(); // chances are, paid ln invoice was processed during `fetchUserInvoices()` call and altered user's balance, so its worth fetching balance again + } + redrawScreen(); + didRefresh = true; + } else { + console.log('balance not changed'); + } + } + } catch (Err) { + noErr = false; + console.warn(Err); + } + + if (noErr && didRefresh) { + await BlueApp.saveToDisk(); // caching + } + }; + + const renderWalletsCarousel = () => { + return ( + + ); + }; + + return ( + + + + + wallet.type === PlaceholderWallet.type) + ? () => props.navigation.navigate('AddWalletRoot') + : null + } + /> + + {renderWalletsCarousel()} + + + ); +}; + +export default DrawerList; +const styles = StyleSheet.create({ + contentContainerCustomStyle: { + paddingRight: 10, + paddingLeft: 20, + }, + root: { + flex: 1, + }, + headerTouch: { + height: 48, + paddingRight: 16, + paddingLeft: 32, + paddingVertical: 10, + }, +}); + +DrawerList.propTypes = { + navigation: PropTypes.shape({ + navigate: PropTypes.func, + addListener: PropTypes.func, + }), + route: PropTypes.shape({ + name: PropTypes.string, + params: PropTypes.object, + }), +}; + +DrawerList.navigationOptions = ({ navigation }) => { + return { + ...BlueNavigationStyle(navigation, true), + title: '', + headerStyle: { + backgroundColor: BlueCurrentTheme.colors.customHeader, + borderBottomWidth: 0, + elevation: 0, + shadowOpacity: 0, + shadowOffset: { height: 0, width: 0 }, + }, + headerRight: () => ( + NavigationService.navigate('Settings')}> + + + ), + }; +}; diff --git a/screen/wallets/list.js b/screen/wallets/list.js index 7606d4c22..958a83f7a 100644 --- a/screen/wallets/list.js +++ b/screen/wallets/list.js @@ -7,10 +7,10 @@ import { Text, StyleSheet, InteractionManager, - Clipboard, SectionList, Alert, Platform, + Dimensions, } from 'react-native'; import { BlueScanButton, WalletsCarousel, BlueHeaderDefaultMain, BlueTransactionListItem, BlueNavigationStyle } from '../../BlueComponents'; import { Icon } from 'react-native-elements'; @@ -21,10 +21,11 @@ import { AppStorage, PlaceholderWallet } from '../../class'; import WalletImport from '../../class/wallet-import'; import ActionSheet from '../ActionSheet'; import ImagePicker from 'react-native-image-picker'; +import Clipboard from '@react-native-community/clipboard'; import * as NavigationService from '../../NavigationService'; import loc from '../../loc'; import { BlueCurrentTheme } from '../../components/themes'; -import { getSystemName } from 'react-native-device-info'; +import { getSystemName, isTablet } from 'react-native-device-info'; import ScanQRCode from '../send/ScanQRCode'; const EV = require('../../blue_modules/events'); const A = require('../../blue_modules/analytics'); @@ -41,12 +42,16 @@ export default class WalletsList extends Component { constructor(props) { super(props); + const width = Dimensions.get('window').width; this.state = { isLoading: true, isFlatListRefreshControlHidden: true, wallets: BlueApp.getWallets().concat(false), timeElpased: 0, dataSource: [], + itemWidth: width * 0.82 > 375 ? 375 : width * 0.82, + isLargeScreen: + Platform.OS === 'android' ? isTablet() : Dimensions.get('window').width >= Dimensions.get('screen').width / 3 && isTablet(), }; EV(EV.enum.WALLETS_COUNT_CHANGED, () => this.redrawScreen(true)); @@ -61,28 +66,9 @@ export default class WalletsList extends Component { // all balances and all transactions here: this.redrawScreen(); - InteractionManager.runAfterInteractions(async () => { - try { - await BlueElectrum.waitTillConnected(); - const balanceStart = +new Date(); - await BlueApp.fetchWalletBalances(); - const balanceEnd = +new Date(); - console.log('fetch all wallet balances took', (balanceEnd - balanceStart) / 1000, 'sec'); - const start = +new Date(); - await BlueApp.fetchWalletTransactions(); - const end = +new Date(); - console.log('fetch all wallet txs took', (end - start) / 1000, 'sec'); - await BlueApp.saveToDisk(); - } catch (error) { - console.log(error); - } - }); - this.interval = setInterval(() => { this.setState(prev => ({ timeElapsed: prev.timeElapsed + 1 })); }, 60000); - this.redrawScreen(); - this._unsubscribe = this.props.navigation.addListener('focus', this.onNavigationEventFocus); } @@ -129,11 +115,6 @@ export default class WalletsList extends Component { redrawScreen = (scrollToEnd = false) => { console.log('wallets/list redrawScreen()'); - // here, when we receive REMOTE_TRANSACTIONS_COUNT_CHANGED we fetch TXs and balance for current wallet. - // placing event subscription here so it gets exclusively re-subscribed more often. otherwise we would - // have to unsubscribe on unmount and resubscribe again on mount. - EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED, this.refreshTransactions.bind(this), true); - if (BlueApp.getBalance() !== 0) { A(A.ENUM.GOT_NONZERO_BALANCE); } else { @@ -155,7 +136,8 @@ export default class WalletsList extends Component { }, () => { if (scrollToEnd) { - this.walletsCarousel.current.snapToItem(this.state.wallets.length - 2); + // eslint-disable-next-line no-unused-expressions + this.walletsCarousel.current?.snapToItem(this.state.wallets.length - 2); } }, ); @@ -344,6 +326,8 @@ export default class WalletsList extends Component { onSnapToItem={this.onSnapToItem} ref={this.walletsCarousel} testID="WalletsList" + sliderWidth={Dimensions.get('window').width} + itemWidth={this.state.itemWidth} /> ); }; @@ -351,7 +335,7 @@ export default class WalletsList extends Component { renderSectionItem = item => { switch (item.section.key) { case WalletsListSections.CAROUSEL: - return this.renderWalletsCarousel(); + return this.state.isLargeScreen ? null : this.renderWalletsCarousel(); case WalletsListSections.LOCALTRADER: return this.renderLocalTrader(); case WalletsListSections.TRANSACTIONS: @@ -364,7 +348,7 @@ export default class WalletsList extends Component { renderSectionHeader = ({ section }) => { switch (section.key) { case WalletsListSections.CAROUSEL: - return ( + return this.state.isLargeScreen ? null : ( { + const width = Dimensions.get('window').width; + this.setState({ + isLargeScreen: Platform.OS === 'android' ? isTablet() : width >= Dimensions.get('screen').width / 3 && isTablet(), + itemWidth: width * 0.82 > 375 ? 375 : width * 0.82, + }); + }; + render() { return ( - + { + const [isHandOffUseEnabled, setIsHandOffUseEnabled] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [isManageFundsModalVisible, setIsManageFundsModalVisible] = useState(false); + const { wallet } = useRoute().params; + const name = useRoute().name; + const [itemPriceUnit, setItemPriceUnit] = useState(wallet.getPreferredBalanceUnit()); + const [dataSource, setDataSource] = useState([]); + const [timeElapsed, setTimeElapsed] = useState(0); + const [limit, setLimit] = useState(15); + const [pageSize, setPageSize] = useState(20); + const { setParams, navigate } = useNavigation(); + const { colors } = useTheme(); - constructor(props) { - super(props); - - // here, when we receive REMOTE_TRANSACTIONS_COUNT_CHANGED we fetch TXs and balance for current wallet - EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED, this.refreshTransactionsFunction.bind(this), true); - const wallet = props.route.params.wallet; - this.props.navigation.setParams({ wallet: wallet, isLoading: true }); - this.state = { - isHandOffUseEnabled: false, - isLoading: true, - isManageFundsModalVisible: false, - showShowFlatListRefreshControl: false, - wallet: wallet, - itemPriceUnit: wallet.getPreferredBalanceUnit(), - dataSource: this.getTransactions(15), - timeElapsed: 0, // this is to force a re-render for FlatList items. - limit: 15, - pageSize: 20, - }; - } - - componentDidMount() { - this._unsubscribeFocus = this.props.navigation.addListener('focus', this.onFocus); - this.props.navigation.setParams({ isLoading: false }); - this.interval = setInterval(() => { - this.setState(prev => ({ timeElapsed: prev.timeElapsed + 1 })); - }, 60000); - HandoffSettings.isHandoffUseEnabled().then(enabled => this.setState({ isHandOffUseEnabled: enabled })); - this.setState({ isLoading: false }); - } - - /** - * Forcefully fetches TXs and balance for wallet - */ - refreshTransactionsFunction(delay) { - delay = delay || 4000; - const that = this; - setTimeout(function () { - that.refreshTransactions(); - }, delay); // giving a chance to remote server to propagate - } + const windowHeight = useWindowDimensions().height; + const windowWidth = useWindowDimensions().width; + const stylesHook = StyleSheet.create({ + advancedTransactionOptionsModalContent: { + backgroundColor: colors.elevated, + }, + listHeaderText: { + color: colors.foregroundColor, + }, + marketplaceButton1: { + backgroundColor: colors.lightButton, + }, + marketplaceButton2: { + backgroundColor: colors.lightButton, + }, + marketpalceText1: { + color: colors.cta2, + }, + marketpalceText2: { + color: colors.cta2, + }, + list: { + backgroundColor: colors.background, + }, + }); + const interval = setInterval(() => setTimeElapsed(prev => ({ timeElapsed: prev.timeElapsed + 1 })), 60000); /** * Simple wrapper for `wallet.getTransactions()`, where `wallet` is current wallet. @@ -236,9 +222,7 @@ export default class WalletTransactions extends Component { * @param limit {Integer} How many txs return, starting from the earliest. Default: all of them. * @returns {Array} */ - getTransactions(limit = Infinity) { - const wallet = this.props.route.params.wallet; - + const getTransactions = (limit = Infinity) => { let txs = wallet.getTransactions(); for (const tx of txs) { tx.sort_ts = +new Date(tx.received); @@ -247,92 +231,104 @@ export default class WalletTransactions extends Component { return b.sort_ts - a.sort_ts; }); return txs.slice(0, limit); - } + // eslint-disable-next-line react-hooks/exhaustive-deps + }; - redrawScreen() { - InteractionManager.runAfterInteractions(async () => { - console.log('wallets/transactions redrawScreen()'); + useEffect(() => { + EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED, refreshTransactionsFunction, true); + HandoffSettings.isHandoffUseEnabled().then(setIsHandOffUseEnabled); + return () => { + clearInterval(interval); + navigate('DrawerRoot', { selectedWallet: '' }); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - this.setState({ - isLoading: false, - showShowFlatListRefreshControl: false, - dataSource: this.getTransactions(this.state.limit), - }); - }); - } + useEffect(() => { + setIsLoading(true); + setDataSource([]); + setDataSource(wallet.getTransactions(15)); + setLimit(15); + setPageSize(20); + setTimeElapsed(0); + setItemPriceUnit(wallet.getPreferredBalanceUnit()); + setParams({ wallet, isLoading: false }); + setIsLoading(false); + navigate('DrawerRoot', { selectedWallet: wallet.getID() }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [wallet]); - isLightning() { - const w = this.state.wallet; + /** + * Forcefully fetches TXs and balance for wallet + */ + const refreshTransactionsFunction = delay => { + delay = delay || 4000; + setTimeout(function () { + refreshTransactions(); + }, delay); // giving a chance to remote server to propagate + }; + + const isLightning = () => { + const w = wallet; if (w && w.chain === Chain.OFFCHAIN) { return true; } return false; - } + }; /** * Forcefully fetches TXs and balance for wallet */ - refreshTransactions() { - if (this.state.isLoading) return; - this.setState( - { - showShowFlatListRefreshControl: true, - isLoading: true, - }, - async () => { - let noErr = true; - let smthChanged = false; - try { - // await BlueElectrum.ping(); - await BlueElectrum.waitTillConnected(); - /** @type {LegacyWallet} */ - const wallet = this.state.wallet; - const balanceStart = +new Date(); - const oldBalance = wallet.getBalance(); - await wallet.fetchBalance(); - if (oldBalance !== wallet.getBalance()) smthChanged = true; - const balanceEnd = +new Date(); - console.log(wallet.getLabel(), 'fetch balance took', (balanceEnd - balanceStart) / 1000, 'sec'); - const start = +new Date(); - const oldTxLen = wallet.getTransactions().length; - await wallet.fetchTransactions(); - if (wallet.fetchPendingTransactions) { - await wallet.fetchPendingTransactions(); - } - if (wallet.fetchUserInvoices) { - await wallet.fetchUserInvoices(); - } - if (oldTxLen !== wallet.getTransactions().length) smthChanged = true; - const end = +new Date(); - console.log(wallet.getLabel(), 'fetch tx took', (end - start) / 1000, 'sec'); - } catch (err) { - noErr = false; - alert(err.message); - this.setState({ - isLoading: false, - showShowFlatListRefreshControl: false, - }); - } - if (noErr && smthChanged) { - console.log('saving to disk'); - await BlueApp.saveToDisk(); // caching - EV(EV.enum.TRANSACTIONS_COUNT_CHANGED); // let other components know they should redraw - } - this.redrawScreen(); - }, - ); - } - - _keyExtractor = (_item, index) => index.toString(); - - renderListFooterComponent = () => { - // if not all txs rendered - display indicator - return (this.getTransactions(Infinity).length > this.state.limit && ) || ; + const refreshTransactions = async () => { + if (isLoading) return; + setIsLoading(true); + let noErr = true; + let smthChanged = false; + try { + // await BlueElectrum.ping(); + await BlueElectrum.waitTillConnected(); + /** @type {LegacyWallet} */ + const balanceStart = +new Date(); + const oldBalance = wallet.getBalance(); + await wallet.fetchBalance(); + if (oldBalance !== wallet.getBalance()) smthChanged = true; + const balanceEnd = +new Date(); + console.log(wallet.getLabel(), 'fetch balance took', (balanceEnd - balanceStart) / 1000, 'sec'); + const start = +new Date(); + const oldTxLen = wallet.getTransactions().length; + await wallet.fetchTransactions(); + if (wallet.fetchPendingTransactions) { + await wallet.fetchPendingTransactions(); + } + if (wallet.fetchUserInvoices) { + await wallet.fetchUserInvoices(); + } + if (oldTxLen !== wallet.getTransactions().length) smthChanged = true; + const end = +new Date(); + console.log(wallet.getLabel(), 'fetch tx took', (end - start) / 1000, 'sec'); + } catch (err) { + noErr = false; + alert(err.message); + setIsLoading(false); + } + if (noErr && smthChanged) { + console.log('saving to disk'); + await BlueApp.saveToDisk(); // caching + EV(EV.enum.TRANSACTIONS_COUNT_CHANGED); // let other components know they should redraw + } + setIsLoading(false); }; - renderListHeaderComponent = () => { - const style = { opacity: this.state.isLoading ? 0.5 : 1.0 }; + const _keyExtractor = (_item, index) => index.toString(); + + const renderListFooterComponent = () => { + // if not all txs rendered - display indicator + return (getTransactions(Infinity).length > limit && ) || ; + }; + + const renderListHeaderComponent = () => { + const style = { opacity: isLoading ? 0.5 : 1.0 }; return ( @@ -350,17 +346,15 @@ export default class WalletTransactions extends Component { The idea is to avoid showing on iOS an appstore/market style app that goes against the TOS. */} - {this.state.wallet.getTransactions().length > 0 && - this.state.wallet.type !== LightningCustodianWallet.type && - this.renderSellFiat()} - {this.state.wallet.type === LightningCustodianWallet.type && this.renderMarketplaceButton()} - {this.state.wallet.type === LightningCustodianWallet.type && Platform.OS === 'ios' && this.renderLappBrowserButton()} + {wallet.getTransactions().length > 0 && wallet.type !== LightningCustodianWallet.type && renderSellFiat()} + {wallet.type === LightningCustodianWallet.type && renderMarketplaceButton()} + {wallet.type === LightningCustodianWallet.type && Platform.OS === 'ios' && renderLappBrowserButton()} - - {loc.transactions.list_title} + + {loc.transactions.list_title} {isDesktop && ( - this.refreshTransactions()} disabled={this.state.isLoading}> - + + )} @@ -368,20 +362,21 @@ export default class WalletTransactions extends Component { ); }; - renderManageFundsModal = () => { + const renderManageFundsModal = () => { return ( { Keyboard.dismiss(); - this.setState({ isManageFundsModalVisible: false }); + setIsManageFundsModalVisible(false); }} > - - + { @@ -389,40 +384,41 @@ export default class WalletTransactions extends Component { if (wallets.length === 0) { alert(loc.lnd.refill_create); } else { - this.setState({ isManageFundsModalVisible: false }); - this.props.navigation.navigate('SelectWallet', { onWalletSelect: this.onWalletSelect, chainType: Chain.ONCHAIN }); + setIsManageFundsModalVisible(false); + navigate('SelectWallet', { onWalletSelect, chainType: Chain.ONCHAIN }); } }} title={loc.lnd.refill} /> - { - this.setState({ isManageFundsModalVisible: false }, () => - this.props.navigation.navigate('ReceiveDetails', { - secret: this.state.wallet.getSecret(), - }), - ); + setIsManageFundsModalVisible(false); + + navigate('ReceiveDetails', { + secret: wallet.getSecret(), + }); }} title={loc.lnd.refill_external} /> - { - this.setState({ isManageFundsModalVisible: false }, this.navigateToBuyBitcoin); + setIsManageFundsModalVisible(false); + navigateToBuyBitcoin(); }} title={loc.lnd.refill_card} /> - { - this.setState({ isManageFundsModalVisible: false }); + setIsManageFundsModalVisible(false); Linking.openURL('https://zigzag.io/?utm_source=integration&utm_medium=bluewallet&utm_campaign=withdrawLink'); }} /> @@ -432,80 +428,84 @@ export default class WalletTransactions extends Component { ); }; - renderMarketplaceButton = () => { + const navigateToBuyBitcoin = () => { + BuyBitcoin.navigate(wallet); + }; + + const renderMarketplaceButton = () => { return Platform.select({ android: ( { - if (this.state.wallet.type === LightningCustodianWallet.type) { - this.props.navigation.navigate('LappBrowser', { fromSecret: this.state.wallet.getSecret(), fromWallet: this.state.wallet }); + if (wallet.type === LightningCustodianWallet.type) { + navigate('LappBrowser', { fromSecret: wallet.getSecret(), fromWallet: wallet }); } else { - this.props.navigation.navigate('Marketplace', { fromWallet: this.state.wallet }); + navigate('Marketplace', { fromWallet: wallet }); } }} - style={styles.marketplaceButton1} + style={[styles.marketplaceButton1, stylesHook.marketplaceButton1]} > - marketplace + marketplace ), ios: - this.state.wallet.getBalance() > 0 ? ( + wallet.getBalance() > 0 ? ( { Linking.openURL('https://bluewallet.io/marketplace/'); }} - style={styles.marketplaceButton1} + style={[styles.marketplaceButton1, stylesHook.marketplaceButton1]} > - marketplace + marketplace ) : null, }); }; - renderLappBrowserButton = () => { + const renderLappBrowserButton = () => { return ( { - this.props.navigation.navigate('LappBrowser', { - fromSecret: this.state.wallet.getSecret(), - fromWallet: this.state.wallet, + navigate('LappBrowser', { + fromSecret: wallet.getSecret(), + fromWallet: wallet, url: 'https://duckduckgo.com', }); }} - style={styles.marketplaceButton2} + style={[styles.marketplaceButton2, stylesHook.marketplaceButton2]} > - LApp Browser + LApp Browser ); }; - renderSellFiat = () => { + const renderSellFiat = () => { return ( - - {loc.wallets.list_tap_here_to_buy} + + {loc.wallets.list_tap_here_to_buy} ); }; - onWalletSelect = async wallet => { + const onWalletSelect = async wallet => { if (wallet) { - NavigationService.navigate('WalletTransactions', { + navigate('WalletTransactions', { key: `WalletTransactions-${wallet.getID()}`, }); /** @type {LightningCustodianWallet} */ let toAddress = false; - if (this.state.wallet.refill_addressess.length > 0) { - toAddress = this.state.wallet.refill_addressess[0]; + if (wallet.refill_addressess.length > 0) { + toAddress = wallet.refill_addressess[0]; } else { try { - await this.state.wallet.fetchBtcAddress(); - toAddress = this.state.wallet.refill_addressess[0]; + await wallet.fetchBtcAddress(); + toAddress = wallet.refill_addressess[0]; } catch (Err) { return alert(Err.message); } } - this.props.navigation.navigate('SendDetailsRoot', { + navigate('SendDetailsRoot', { screen: 'SendDetails', params: { memo: loc.lnd.refill_lnd_balance, @@ -515,50 +515,36 @@ export default class WalletTransactions extends Component { }); } }; - - onFocus = () => { - this.redrawScreen(); - this.props.navigation.setParams({ isLoading: false }); - }; - - componentWillUnmount() { - clearInterval(this.interval); - this._unsubscribeFocus(); - } - - navigateToSendScreen = () => { - this.props.navigation.navigate('SendDetailsRoot', { + const navigateToSendScreen = () => { + navigate('SendDetailsRoot', { screen: 'SendDetails', params: { - fromWallet: this.state.wallet, + fromWallet: wallet, }, }); }; - renderItem = item => ( - - ); + const renderItem = item => ; - onBarCodeRead = ret => { - if (!this.state.isLoading) { - this.setState({ isLoading: true }, () => { - this.setState({ isLoading: false }); - const params = { - fromSecret: this.state.wallet.getSecret(), - // ScanLndInvoice actrually uses `fromSecret` so keeping it for now - uri: ret.data ? ret.data : ret, - fromWallet: this.state.wallet, - }; - if (this.state.wallet.chain === Chain.ONCHAIN) { - this.props.navigation.navigate('SendDetailsRoot', { screen: 'SendDetails', params }); - } else { - this.props.navigation.navigate('ScanLndInvoiceRoot', { screen: 'ScanLndInvoice', params }); - } - }); + const onBarCodeRead = ret => { + if (!isLoading) { + setIsLoading(true); + const params = { + fromSecret: wallet.getSecret(), + // ScanLndInvoice actrually uses `fromSecret` so keeping it for now + uri: ret.data ? ret.data : ret, + fromWallet: wallet, + }; + if (wallet.chain === Chain.ONCHAIN) { + navigate('SendDetailsRoot', { screen: 'SendDetails', params }); + } else { + navigate('ScanLndInvoiceRoot', { screen: 'ScanLndInvoice', params }); + } } + setIsLoading(false); }; - choosePhoto = () => { + const choosePhoto = () => { ImagePicker.launchImageLibrary( { title: null, @@ -570,7 +556,7 @@ export default class WalletTransactions extends Component { const uri = Platform.OS === 'ios' ? response.uri.toString().replace('file://', '') : response.path.toString(); LocalQRCode.decode(uri, (error, result) => { if (!error) { - this.onBarCodeRead({ data: result }); + onBarCodeRead({ data: result }); } else { alert(loc.send.qr_error_no_qrcode); } @@ -580,11 +566,11 @@ export default class WalletTransactions extends Component { ); }; - copyFromClipbard = async () => { - this.onBarCodeRead({ data: await Clipboard.getString() }); + const copyFromClipbard = async () => { + onBarCodeRead({ data: await Clipboard.getString() }); }; - sendButtonLongPress = async () => { + const sendButtonLongPress = async () => { const isClipboardEmpty = (await Clipboard.getString()).replace(' ', '').length === 0; if (Platform.OS === 'ios') { const options = [loc._.cancel, loc.wallets.list_long_choose, loc.wallets.list_long_scan]; @@ -593,18 +579,18 @@ export default class WalletTransactions extends Component { } ActionSheet.showActionSheetWithOptions({ options, cancelButtonIndex: 0 }, buttonIndex => { if (buttonIndex === 1) { - this.choosePhoto(); + choosePhoto(); } else if (buttonIndex === 2) { - this.props.navigation.navigate('ScanQRCodeRoot', { + navigate('ScanQRCodeRoot', { screen: 'ScanQRCode', params: { - launchedBy: this.props.route.name, - onBarScanned: this.onBarScanned, + launchedBy: name, + onBarScanned: onBarCodeRead, showFileImportButton: false, }, }); } else if (buttonIndex === 3) { - this.copyFromClipbard(); + copyFromClipbard(); } }); } else if (Platform.OS === 'android') { @@ -616,16 +602,16 @@ export default class WalletTransactions extends Component { }, { text: loc.wallets.list_long_choose, - onPress: this.choosePhoto, + onPress: choosePhoto, }, { text: loc.wallets.list_long_scan, onPress: () => - this.props.navigation.navigate('ScanQRCodeRoot', { + navigate('ScanQRCodeRoot', { screen: 'ScanQRCode', params: { - launchedBy: this.props.route.name, - onBarScanned: this.onBarScanned, + launchedBy: name, + onBarScanned: onBarCodeRead, showFileImportButton: false, }, }), @@ -634,7 +620,7 @@ export default class WalletTransactions extends Component { if (!isClipboardEmpty) { buttons.push({ text: loc.wallets.list_long_clipboard, - onPress: this.copyFromClipbard, + onPress: copyFromClipbard, }); } ActionSheet.showActionSheetWithOptions({ @@ -645,182 +631,149 @@ export default class WalletTransactions extends Component { } }; - navigateToBuyBitcoin = () => { - BuyBitcoin.navigate(this.state.wallet); - }; - - render() { - const { navigate } = this.props.navigation; - return ( - - - {this.state.wallet.chain === Chain.ONCHAIN && this.state.isHandOffUseEnabled && ( - - )} - - InteractionManager.runAfterInteractions(async () => { - this.setState({ wallet, itemPriceUnit: wallet.getPreferredBalanceUnit() }, () => - InteractionManager.runAfterInteractions(() => BlueApp.saveToDisk()), - ); - }) - } - onManageFundsPressed={() => { - if (this.state.wallet.getUserHasSavedExport()) { - this.setState({ isManageFundsModalVisible: true }); - } else { - BlueAlertWalletExportReminder({ - onSuccess: async () => { - this.state.wallet.setUserHasSavedExport(true); - await BlueApp.saveToDisk(); - this.setState({ isManageFundsModalVisible: true }); - }, - onFailure: () => - this.props.navigation.navigate('WalletExport', { - wallet: this.state.wallet, - }), - }); - } - }} + return ( + + + {wallet.chain === Chain.ONCHAIN && isHandOffUseEnabled && ( + - - { - // pagination in works. in this block we will add more txs to flatlist - // so as user scrolls closer to bottom it will render mode transactions + )} + + InteractionManager.runAfterInteractions(async () => { + setItemPriceUnit(wallet.getPreferredBalanceUnit()); + BlueApp.saveToDisk(); + }) + } + onManageFundsPressed={() => { + if (wallet.getUserHasSavedExport()) { + setIsManageFundsModalVisible(true); + } else { + BlueAlertWalletExportReminder({ + onSuccess: async () => { + wallet.setUserHasSavedExport(true); + await BlueApp.saveToDisk(); + setIsManageFundsModalVisible(true); + }, + onFailure: () => + navigate('WalletExport', { + wallet, + }), + }); + } + }} + /> + + { + // pagination in works. in this block we will add more txs to flatlist + // so as user scrolls closer to bottom it will render mode transactions - if (this.getTransactions(Infinity).length < this.state.limit) { - // all list rendered. nop - return; - } - - this.setState({ - dataSource: this.getTransactions(this.state.limit + this.state.pageSize), - limit: this.state.limit + this.state.pageSize, - pageSize: this.state.pageSize * 2, - }); - }} - ListFooterComponent={this.renderListFooterComponent} - ListEmptyComponent={ - - - {(this.isLightning() && loc.wallets.list_empty_txs1_lightning) || loc.wallets.list_empty_txs1} - - {this.isLightning() && {loc.wallets.list_empty_txs2_lightning}} - - {!this.isLightning() && ( - - {loc.wallets.list_tap_here_to_buy} - - )} - + if (getTransactions(Infinity).length < limit) { + // all list rendered. nop + return; } - onRefresh={() => this.refreshTransactions()} - refreshing={this.state.showShowFlatListRefreshControl} - data={this.state.dataSource} - extraData={this.state.timeElapsed} - keyExtractor={this._keyExtractor} - renderItem={this.renderItem} - contentInset={{ top: 0, left: 0, bottom: 90, right: 0 }} - /> - {this.renderManageFundsModal()} - - - {(() => { - if (this.state.wallet.allowReceive()) { - return ( - { - if (this.state.wallet.chain === Chain.OFFCHAIN) { - navigate('LNDCreateInvoiceRoot', { screen: 'LNDCreateInvoice', params: { fromWallet: this.state.wallet } }); - } else { - navigate('ReceiveDetails', { secret: this.state.wallet.getSecret() }); - } - }} - /> - ); - } - })()} - {(() => { - if ( - this.state.wallet.allowSend() || - (this.state.wallet.type === WatchOnlyWallet.type && - this.state.wallet.isHd() && - this.state.wallet.getSecret().startsWith('zpub')) - ) { - return ( - { - if (this.state.wallet.chain === Chain.OFFCHAIN) { - navigate('ScanLndInvoiceRoot', { screen: 'ScanLndInvoice', params: { fromSecret: this.state.wallet.getSecret() } }); - } else { - if ( - this.state.wallet.type === WatchOnlyWallet.type && - this.state.wallet.isHd() && - this.state.wallet.getSecret().startsWith('zpub') - ) { - if (this.state.wallet.useWithHardwareWalletEnabled()) { - this.navigateToSendScreen(); - } else { - Alert.alert( - loc.wallets.details_title, - loc.transactions.enable_hw, - [ - { - text: loc._.ok, - onPress: () => { - const wallet = this.state.wallet; - wallet.setUseWithHardwareWalletEnabled(true); - this.setState({ wallet }, async () => { - await BlueApp.saveToDisk(); - this.navigateToSendScreen(); - }); - }, - style: 'default', - }, + setDataSource(getTransactions(limit + pageSize)); + setLimit(prev => prev + pageSize); + setPageSize(prev => prev * 2); + }} + ListFooterComponent={renderListFooterComponent} + ListEmptyComponent={ + + + {(isLightning() && loc.wallets.list_empty_txs1_lightning) || loc.wallets.list_empty_txs1} + + {isLightning() && {loc.wallets.list_empty_txs2_lightning}} - { text: loc._.cancel, onPress: () => {}, style: 'cancel' }, - ], - { cancelable: false }, - ); - } - } else { - this.navigateToSendScreen(); - } - } - }} - /> - ); - } - })()} - + {!isLightning() && ( + + {loc.wallets.list_tap_here_to_buy} + + )} + + } + onRefresh={refreshTransactions} + refreshing={isLoading} + data={dataSource} + extraData={timeElapsed} + keyExtractor={_keyExtractor} + renderItem={renderItem} + contentInset={{ top: 0, left: 0, bottom: 90, right: 0 }} + /> + {renderManageFundsModal()} - ); - } -} + + {(() => { + if (wallet.allowReceive()) { + return ( + { + if (wallet.chain === Chain.OFFCHAIN) { + navigate('LNDCreateInvoiceRoot', { screen: 'LNDCreateInvoice', params: { fromWallet: wallet } }); + } else { + navigate('ReceiveDetails', { secret: wallet.getSecret() }); + } + }} + /> + ); + } + })()} -WalletTransactions.propTypes = { - navigation: PropTypes.shape({ - navigate: PropTypes.func, - goBack: PropTypes.func, - setParams: PropTypes.func, - addListener: PropTypes.func, - }), - route: PropTypes.shape({ - name: PropTypes.string, - params: PropTypes.object, - }), + {(() => { + if (wallet.allowSend() || (wallet.type === WatchOnlyWallet.type && wallet.isHd() && wallet.getSecret().startsWith('zpub'))) { + return ( + { + if (wallet.chain === Chain.OFFCHAIN) { + navigate('ScanLndInvoiceRoot', { screen: 'ScanLndInvoice', params: { fromSecret: wallet.getSecret() } }); + } else { + if (wallet.type === WatchOnlyWallet.type && wallet.isHd() && wallet.getSecret().startsWith('zpub')) { + if (wallet.useWithHardwareWalletEnabled()) { + navigateToSendScreen(); + } else { + Alert.alert( + loc.wallets.details_title, + loc.transactions.enable_hw, + [ + { + text: loc._.ok, + onPress: async () => { + wallet.setUseWithHardwareWalletEnabled(true); + await BlueApp.saveToDisk(); + navigateToSendScreen(); + }, + style: 'default', + }, + + { text: loc._.cancel, onPress: () => {}, style: 'cancel' }, + ], + { cancelable: false }, + ); + } + } else { + navigateToSendScreen(); + } + } + }} + /> + ); + } + })()} + + + ); }; +export default WalletTransactions; + WalletTransactions.navigationOptions = ({ navigation, route }) => { return { headerRight: () => ( diff --git a/screen/wallets/xpub.js b/screen/wallets/xpub.js index cad790e26..29a6ce697 100644 --- a/screen/wallets/xpub.js +++ b/screen/wallets/xpub.js @@ -31,7 +31,7 @@ const WalletXpub = () => { const { goBack } = useNavigation(); const { colors } = useTheme(); const { width, height } = useWindowDimensions(); - const stylesHook = { ...styles, root: { ...styles.root, backgroundColor: colors.elevated } }; + const stylesHook = StyleSheet.create({ root: { backgroundColor: colors.elevated } }); useFocusEffect( useCallback(() => { @@ -65,11 +65,11 @@ const WalletXpub = () => { ); return isLoading ? ( - + ) : ( - + From 7d65ab7d1cd2f5c048be8a2484582e394bbe67f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Rodriguez=20V=C3=A9lez?= Date: Tue, 8 Sep 2020 11:29:40 -0400 Subject: [PATCH 15/17] ADD: TWD Fiat --- models/fiatUnit.js | 1 + 1 file changed, 1 insertion(+) diff --git a/models/fiatUnit.js b/models/fiatUnit.js index 21bfa4e7b..2ff9f3bf8 100644 --- a/models/fiatUnit.js +++ b/models/fiatUnit.js @@ -25,6 +25,7 @@ export const FiatUnit = Object.freeze({ SGD: { endPointKey: 'SGD', symbol: 'S$', locale: 'zh-SG' }, SEK: { endPointKey: 'SEK', symbol: 'kr', locale: 'sv-SE' }, THB: { endPointKey: 'THB', symbol: '฿', locale: 'th-TH' }, + TWD: { endPointKey: 'TWD', symbol: '$', locale: 'zh-Hant-TW' }, UAH: { endPointKey: 'UAH', symbol: '₴', locale: 'uk-UA' }, VEF: { endPointKey: 'VEF', symbol: 'Bs.', locale: 'es-VE' }, ZAR: { endPointKey: 'ZAR', symbol: 'R', locale: 'en-ZA' }, From 732bf94221c16a80905e1bdf5e213cd846f3ba7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Rodriguez=20V=C3=A9lez?= Date: Tue, 8 Sep 2020 12:54:31 -0400 Subject: [PATCH 16/17] ADD: NGN currency --- models/fiatUnit.js | 1 + 1 file changed, 1 insertion(+) diff --git a/models/fiatUnit.js b/models/fiatUnit.js index 2ff9f3bf8..98ba85bf5 100644 --- a/models/fiatUnit.js +++ b/models/fiatUnit.js @@ -18,6 +18,7 @@ export const FiatUnit = Object.freeze({ KRW: { endPointKey: 'KRW', symbol: '₩', locale: 'ko-KR' }, MXN: { endPointKey: 'MXN', symbol: '$', locale: 'es-MX' }, MYR: { endPointKey: 'MYR', symbol: 'RM', locale: 'ms-MY' }, + NGN: { endPointKey: 'NGN', symbol: '₦', locale: 'en-NG' }, NOK: { endPointKey: 'NOK', symbol: 'kr', locale: 'nb-NO' }, NZD: { endPointKey: 'NZD', symbol: '$', locale: 'en-NZ' }, PLN: { endPointKey: 'PLN', symbol: 'zł', locale: 'pl-PL' }, From d4e48ea4beb472a5821685773a67c01ba5bc6a13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Rodriguez=20V=C3=A9lez?= Date: Tue, 8 Sep 2020 12:58:07 -0400 Subject: [PATCH 17/17] ADD: KES Fiat --- models/fiatUnit.js | 1 + 1 file changed, 1 insertion(+) diff --git a/models/fiatUnit.js b/models/fiatUnit.js index 98ba85bf5..97fc10eee 100644 --- a/models/fiatUnit.js +++ b/models/fiatUnit.js @@ -15,6 +15,7 @@ export const FiatUnit = Object.freeze({ ILS: { endPointKey: 'ILS', symbol: '₪', locale: 'he-IL' }, INR: { endPointKey: 'INR', symbol: '₹', locale: 'hi-HN' }, JPY: { endPointKey: 'JPY', symbol: '¥', locale: 'ja-JP' }, + KES: { endPointKey: 'KES', symbol: 'Ksh', locale: 'en-KE' }, KRW: { endPointKey: 'KRW', symbol: '₩', locale: 'ko-KR' }, MXN: { endPointKey: 'MXN', symbol: '$', locale: 'es-MX' }, MYR: { endPointKey: 'MYR', symbol: 'RM', locale: 'ms-MY' },