Browse Source

Merge pull request #21 from sansegkh/master

updated from master
singleaddress
San Segkhoonthod 6 years ago
committed by GitHub
parent
commit
4bc9545852
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      App.js
  2. 2
      android/app/build.gradle
  3. 12
      class/app-storage.js
  4. 33
      class/lightning-custodian-wallet.js
  5. 4
      class/walletGradient.js
  6. BIN
      img/addWallet/bitcoin.png
  7. BIN
      img/addWallet/bitcoin@2x.png
  8. BIN
      img/addWallet/bitcoin@3x.png
  9. BIN
      img/addWallet/lightning.png
  10. BIN
      img/addWallet/lightning@2x.png
  11. BIN
      img/addWallet/lightning@3x.png
  12. 2
      ios/BlueWallet/Info.plist
  13. 51
      ios/fastlane/metadata/en-US/release_notes.txt
  14. 8
      loc/en.js
  15. 10
      loc/index.js
  16. 222
      loc/it.js
  17. 68
      package-lock.json
  18. 8
      package.json
  19. 7
      screen/lnd/scanLndInvoice.js
  20. 3
      screen/send/confirm.js
  21. 8
      screen/send/details.js
  22. 1
      screen/settings/language.js
  23. 39
      screen/settings/lightningSettings.js
  24. 12
      screen/wallets/details.js
  25. 20
      screen/wallets/export.js
  26. 11
      screen/wallets/import.js
  27. 18
      screen/wallets/transactions.js

22
App.js

@ -5,10 +5,13 @@ import { NavigationActions } from 'react-navigation';
import MainBottomTabs from './MainBottomTabs';
import NavigationService from './NavigationService';
import { BlueTextCentered, BlueButton } from './BlueComponents';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
const bitcoin = require('bitcoinjs-lib');
const bitcoinModalString = 'Bitcoin address';
const lightningModalString = 'Lightning Invoice';
let loc = require('./loc');
const loc = require('./loc');
/** @type {AppStorage} */
const BlueApp = require('./BlueApp');
export default class App extends React.Component {
navigator = null;
@ -22,9 +25,12 @@ export default class App extends React.Component {
componentDidMount() {
Linking.getInitialURL()
.then(url => this.handleOpenURL({ url }))
.then(url => {
if (this.hasSchema(url)) {
this.handleOpenURL({ url });
}
})
.catch(console.error);
Linking.addEventListener('url', this.handleOpenURL);
AppState.addEventListener('change', this._handleAppStateChange);
}
@ -35,17 +41,24 @@ export default class App extends React.Component {
}
_handleAppStateChange = async nextAppState => {
if (BlueApp.getWallets().length > 0) {
if (this.state.appState.match(/inactive|background/) && nextAppState === 'active') {
const clipboard = await Clipboard.getString();
if (this.state.clipboardContent !== clipboard && (this.isBitcoinAddress(clipboard) || this.isLightningInvoice(clipboard))) {
this.setState({ isClipboardContentModalVisible: true });
}
this.setState({ clipboardContent: clipboard });
}
this.setState({ appState: nextAppState });
}
};
hasSchema(schemaString) {
if (typeof schemaString !== 'string' || schemaString.length <= 0) return false;
const lowercaseString = schemaString.trim().toLowerCase();
return lowercaseString.startsWith('bitcoin:') || lowercaseString.startsWith('lightning:');
}
isBitcoinAddress(address) {
let isValidBitcoinAddress = false;
try {
@ -106,6 +119,7 @@ export default class App extends React.Component {
renderClipboardContentModal = () => {
return (
<Modal
onModalShow={() => ReactNativeHapticFeedback.trigger('impactLight', false)}
isVisible={this.state.isClipboardContentModalVisible}
style={styles.bottomModal}
onBackdropPress={() => {

2
android/app/build.gradle

@ -102,7 +102,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "3.8.2"
versionName "3.8.3"
ndk {
abiFilters "armeabi-v7a", "x86"
}

12
class/app-storage.js

@ -159,12 +159,16 @@ export class AppStorage {
} catch (Error) {
console.warn(Error);
}
if (lndhub) {
console.log('using', lndhub, 'for lndhub wallet');
if (unserializedWallet.baseURI) {
unserializedWallet.setBaseURI(unserializedWallet.baseURI); // not really necessary, just for the sake of readability
console.log('using saved uri for for ln wallet:', unserializedWallet.baseURI);
} else if (lndhub) {
console.log('using wallet-wide settings ', lndhub, 'for ln wallet');
unserializedWallet.setBaseURI(lndhub);
} else {
unserializedWallet.setBaseURI();
console.log('using default uri for for lndhub wallet:', unserializedWallet.baseURI);
console.log('using default', LightningCustodianWallet.defaultBaseUri, 'for ln wallet');
unserializedWallet.setBaseURI(LightningCustodianWallet.defaultBaseUri);
}
unserializedWallet.init();
break;

33
class/lightning-custodian-wallet.js

@ -6,7 +6,7 @@ let BigNumber = require('bignumber.js');
export class LightningCustodianWallet extends LegacyWallet {
static type = 'lightningCustodianWallet';
static typeReadable = 'Lightning';
static defaultBaseUri = 'https://lndhub.herokuapp.com/';
constructor(props) {
super(props);
this.setBaseURI(); // no args to init with default value
@ -32,7 +32,7 @@ export class LightningCustodianWallet extends LegacyWallet {
if (URI) {
this.baseURI = URI;
} else {
this.baseURI = 'https://lndhub.herokuapp.com/';
this.baseURI = LightningCustodianWallet.defaultBaseUri;
}
}
@ -48,6 +48,13 @@ export class LightningCustodianWallet extends LegacyWallet {
return '';
}
getSecret() {
if (this.baseURI === LightningCustodianWallet.defaultBaseUri) {
return this.secret;
}
return this.secret + '@' + this.baseURI;
}
timeToRefreshBalance() {
return (+new Date() - this._lastBalanceFetch) / 1000 > 3600; // 1hr
}
@ -548,10 +555,30 @@ export class LightningCustodianWallet extends LegacyWallet {
if (!json.identity_pubkey) {
throw new Error('API unexpected response: ' + JSON.stringify(response.body));
}
this.info_raw = json;
}
static async isValidNodeAddress(address) {
let apiCall = new Frisbee({
baseURI: address,
});
let response = await apiCall.get('/getinfo', {
headers: {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json',
},
});
let json = response.body;
if (typeof json === 'undefined') {
throw new Error('API failure: ' + response.err + ' ' + JSON.stringify(response.body));
}
if (json && json.code && json.code !== 1) {
throw new Error('API error: ' + json.message + ' (code ' + json.code + ')');
}
return true;
}
allowReceive() {
return true;
}

4
class/walletGradient.js

@ -6,12 +6,12 @@ import { HDLegacyP2PKHWallet } from './hd-legacy-p2pkh-wallet';
import { WatchOnlyWallet } from './watch-only-wallet';
export default class WalletGradient {
static defaultGradients = ['#65ceef', '#68bbe1'];
static hdSegwitP2SHWallet = ['#65ceef', '#68bbe1'];
static watchOnlyWallet = ['#7d7d7d', '#4a4a4a'];
static legacyWallet = ['#40fad1', '#15be98'];
static hdLegacyP2PKHWallet = ['#e36dfa', '#bd10e0'];
static hdLegacyBreadWallet = ['#fe6381', '#f99c42'];
static hdSegwitP2SHWallet = ['#c65afb', '#9053fe'];
static defaultGradients = ['#c65afb', '#9053fe'];
static lightningCustodianWallet = ['#f1be07', '#f79056'];
static createWallet = ['#eef0f4', '#eef0f4'];

BIN
img/addWallet/bitcoin.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
img/addWallet/bitcoin@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
img/addWallet/bitcoin@3x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
img/addWallet/lightning.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
img/addWallet/lightning@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
img/addWallet/lightning@3x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

2
ios/BlueWallet/Info.plist

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.8.2</string>
<string>3.8.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>

51
ios/fastlane/metadata/en-US/release_notes.txt

@ -1,30 +1,25 @@
v3.8.0
v3.8.2
======
* ADD: NZD currency
* FIX: BlueWallet should register to handle the BOLT11 lightning: URI #335
* ADD: Chinese locale
* REF: main screen only 15 txs. wallet screen txs lazy-load (better performance)
* FIX: Update JP translation
* ADD: CHF Currency
* REF: Manage funds and marketplace buttons
* ADD: InputAccesoryView for easy keyboard dismissal
* FIX: Fixed color on view invoice
* REF: UI responsiveness improvements
v3.7.2
======
* FIX: status bar disappears #311
* FIX: faulty back button on viewLndInvoice screen
* FIX: lnd - create invoice - cant tap bottom half of button #303
* FIX: Impossible to scan bech32 QR invoice with a specific amount (closes #296)
* REF: better lightning error reporting
* FIX: not redering QR code #302
* ADD: Indonesian Translation
* FIX: better LN wallet auto-refresh strategy
* FIX: Dismiss keyboard when pressing return
* FIX: HD wallet balance refresh
* FIX: no wallet refresh upon startup (faster to start app)
* FIX: Dismiss keyboard when pressing return
* REF: HD wallet getbalance improvements
* FIX: Create button was difficult to press
* ADD: Clipboard detection on app state change to foreground
* FIX: Statusbar restored
* ADD: Broadcast TX using Electrum
* ADD: Blur sensitive views from visibility on app switcher
* FIX: Hide context menu
* FIX: Disable third party keyboard on iOS
* FIX: Disable suggestions
* ADD: Switch Lightning wallets on send screen.
* ADD: If providing an LND address in BTC send screen, throw an error.
* ADD: Chain type in order to determine what type of wallet has been selected.
* FIX: Handle BitPay BIP70 scenarios where schema isnt present.
* ADD: Use Electrum to estimate fee
* FIX: Wallet selection
* FIX: wallets/list pull-to-refresh animation duration
* FIX: Text not displayed correctly #165
* FIX: back button
* FIX: Fix transaction fee for BIP70 payments
* REF: buy bitcoin via redirect
* FIX: Update th_TS
* ADD: Tap, and hold, on balance to display a Copy balance option
* FIX: Send BTC should ignore non-onchain wallets

8
loc/en.js

@ -2,7 +2,7 @@ module.exports = {
_: {
storage_is_encrypted: 'Your storage is encrypted. Password is required to decrypt it',
enter_password: 'Enter password',
bad_password: 'Bad pasword, try again',
bad_password: 'Bad password, try again',
never: 'never',
continue: 'Continue',
ok: 'OK',
@ -36,7 +36,7 @@ module.exports = {
create: 'Create',
label_new_segwit: 'New SegWit',
label_new_lightning: 'New Lightning',
wallet_name: 'wallet name',
wallet_name: 'name',
wallet_type: 'type',
or: 'or',
import_wallet: 'Import wallet',
@ -182,8 +182,8 @@ module.exports = {
lightning_settings: 'Lightning Settings',
lightning_settings_explain:
'To connect to your own LND node please install LndHub' +
' and put its URL here in settings. Leave blank to use default LndHub (lndhub.io)',
save: 'save',
" and put its URL here in settings. Leave blank to use BlueWallet's LNDHub (lndhub.io). Wallets created after saving changes will connect to the specified LNDHub.",
save: 'Save',
about: 'About',
language: 'Language',
currency: 'Currency',

10
loc/index.js

@ -17,7 +17,10 @@ dayjs.extend(relativeTime);
strings.setLanguage(lang);
let localeForDayJSAvailable = true;
switch (lang) {
case 'zh':
case 'it':
require('dayjs/locale/it');
break;
case 'zh_cn':
require('dayjs/locale/zh-cn');
break;
case 'ru':
@ -78,6 +81,7 @@ dayjs.extend(relativeTime);
locale === 'ru' ||
locale === 'ua' ||
locale === 'es' ||
locale === 'it' ||
locale === 'fr-fr' ||
locale === 'pt-br' ||
locale === 'pt-pt' ||
@ -92,6 +96,9 @@ dayjs.extend(relativeTime);
locale === 'zh-cn'
) {
switch (locale) {
case 'it':
require('dayjs/locale/it');
break;
case 'zh-cn':
require('dayjs/locale/zh-cn');
break;
@ -150,6 +157,7 @@ strings = new Localization({
pt_br: require('./pt_BR.js'),
pt_pt: require('./pt_PT.js'),
es: require('./es.js'),
it: require('./it.js'),
ua: require('./ua.js'),
jp_jp: require('./jp_JP.js'),
de_de: require('./de_DE.js'),

222
loc/it.js

@ -0,0 +1,222 @@
module.exports = {
_: {
storage_is_encrypted: 'Il tuo archivio è criptato. È necessaria una password per decriptarlo',
enter_password: 'Inserisci password',
bad_password: 'Password errata, riprova',
never: 'mai',
continue: 'Continua',
ok: 'OK',
},
wallets: {
select_wallet: 'Seleziona Portafoglio',
options: 'Opzioni',
createBitcoinWallet:
'Non hai un portafoglio Bitcoin attualmente. Per ricaricare un portafoglio Lightning, è necessario creare o importare un portafoglio Bitcoin. Vuoi continuare lo stesso?',
list: {
app_name: 'BlueWallet',
title: 'Portafogli',
header:
'Un portafoglio rappresenta la coppia fra un segreto (chiave privata) e un indirizzo' + 'che puoi condividere per ricevere Bitcoin.',
add: 'Aggiungi Portafoglio',
create_a_wallet: 'Crea un portafoglio',
create_a_wallet1: 'È gratuito e puoi crearne',
create_a_wallet2: 'quanti ne vuoi',
latest_transaction: 'Transazioni recenti',
empty_txs1: 'Le tue transazioni appariranno qui,',
empty_txs2: 'Nessuna transazione',
tap_here_to_buy: 'Clicca qui per comprare Bitcoin',
},
reorder: {
title: 'Riordina Portafogli',
},
add: {
title: 'Aggiungi Portafoglio',
description:
'Puoi scansionare il Backup di un Paper-Wallet (in WIF - Wallet Import Format), o creare un nuovo portafoglio. I portafogli Segwit sono supportati di default.',
scan: 'Scansiona',
create: 'Crea',
label_new_segwit: 'Nuovo SegWit',
label_new_lightning: 'Nuovo Lightning',
wallet_name: 'Nome Portafoglio',
wallet_type: 'Tipo',
or: 'o',
import_wallet: 'Importa Portafoglio',
imported: 'Importato',
coming_soon: 'In arrivo',
lightning: 'Lightning',
bitcoin: 'Bitcoin',
},
details: {
title: 'Portafoglio',
address: 'Indirizzo',
type: 'Tipo',
label: 'Etichetta',
destination: 'Destinazione',
description: 'Descrizione',
are_you_sure: 'Sei sicuro?',
yes_delete: 'Si, elimina',
no_cancel: 'No, annulla',
delete: 'Elimina',
save: 'Salva',
delete_this_wallet: 'Elimina questo portafoglio',
export_backup: 'Esporta / Backup',
buy_bitcoin: 'Compra Bitcoin',
show_xpub: 'Mostra XPUB del portafoglio',
},
export: {
title: 'Esporta portafoglio',
},
xpub: {
title: 'XPUB del Portafoglio',
copiedToClipboard: 'Copiata negli appunti.',
},
import: {
title: 'Importa',
explanation:
'Scrivi qui la tua frase mnemonica, chiave privata, WIF, o qualunque altra cosa tu abbia. BlueWallet tenterà di indovinare il formato corretto e importerà il tuo portafoglio',
imported: 'Importato',
error: 'Importazione fallita. Assicurati che le informazioni fornite siano valide.',
success: 'Fatto',
do_import: 'Importa',
scan_qr: 'o scansionare un codice QR?',
},
scanQrWif: {
go_back: 'Indietro',
cancel: 'Annulla',
decoding: 'Decodifica',
input_password: 'Inserisci password',
password_explain: 'Questa è una chiave privata BIP38 criptata',
bad_password: 'Password errata',
wallet_already_exists: 'Questo portafoglio esiste già',
bad_wif: 'WIF errato',
imported_wif: 'Importa WIF ',
with_address: ' con indirizzo ',
imported_segwit: 'SegWit importato',
imported_legacy: 'Legacy importato',
imported_watchonly: 'Watch-only importato',
},
},
transactions: {
list: {
tabBarLabel: 'Transazioni',
title: 'Transazioni',
description: 'Una lista delle transazioni in entrata e in uscita dei tuoi portafogli',
conf: 'conf',
},
details: {
title: 'Transazione',
from: 'Da',
to: 'A',
copy: 'Copia',
transaction_details: 'Dettagli transazione',
show_in_block_explorer: 'Mostra sul block explorer',
},
},
send: {
header: 'Invia',
details: {
title: 'Crea transazione',
amount_field_is_not_valid: 'Importo non valido',
fee_field_is_not_valid: 'Commissione non valida',
address_field_is_not_valid: 'Indirizzo non valido',
total_exceeds_balance: "L'importo da inviare eccede i fondi disponibili.",
create_tx_error: "Si è verificato un errore nella creazione della transazione. Assicurati che l'indirizzo sia valido",
address: 'Indirizzo',
amount_placeholder: 'Importo da inviare (in BTC)',
fee_placeholder: 'Più commissione (in BTC)',
note_placeholder: 'Nota',
cancel: 'Annulla',
scan: 'Scansiona',
send: 'Invia',
create: 'Crea',
remaining_balance: 'Fondi rimanenti',
},
confirm: {
header: 'Conferma',
sendNow: 'Invia ora',
},
success: {
done: 'Fatto',
},
create: {
details: 'Dettagli',
title: 'Crea transazione',
error: 'Errore nella creazione della transazione. Indirizzo o importo invalido',
go_back: 'Indietro',
this_is_hex: "Questo è l'hex della transazione, firmato e pronto per essere trasmesso sulla rete.",
to: 'A',
amount: 'Importo',
fee: 'Commissione',
tx_size: 'Grandezza TX',
satoshi_per_byte: 'Satoshi per byte',
memo: 'Memo',
broadcast: 'Trasmetti',
not_enough_fee: 'Commissione non sufficiente. Aumenta la commissione',
},
},
receive: {
header: 'Ricevi',
details: {
title: "Condividi questo indirizzo con l'acquirente",
share: 'Condividi',
copiedToClipboard: 'Copiato negli appunti.',
label: 'Descrizione',
create: 'Crea',
setAmount: 'Ricevi con importo',
},
},
buyBitcoin: {
header: 'Compra Bitcoin',
tap_your_address: 'Clicca sul tuo indirizzo per copiarlo negli appunti:',
copied: 'Copiato negli appunti!',
},
settings: {
header: 'Impostazioni',
plausible_deniability: 'Negazione plausibile...',
storage_not_encrypted: 'Archivio: non criptato',
storage_encrypted: 'Archivio: criptato',
password: 'Password',
password_explain: "Crea la password che userai per decriptare l'archivio",
retype_password: 'Reinserisci password',
passwords_do_not_match: 'Le password non corrispondono',
encrypt_storage: 'Cripta archivio',
lightning_settings: 'Impostazioni Lightning',
lightning_settings_explain:
'Per connetterti al tuo nodo LND personale installa LndHub' +
' e inserisci il suo URL qui nelle impostazioni. Lascialo vuoto per utilizzare il nodo LndHub di default (lndhub.io)',
save: 'Salva',
about: 'Informazioni',
language: 'Lingua',
currency: 'Valuta',
},
plausibledeniability: {
title: 'Negazione Plausibile',
help:
'In alcune circostanze, potresti essere costretto a rivelare la ' +
'password. Per mantenere i tuoi Bitcoin al sicuro, BlueWallet può creare un altro ' +
'archivio criptato, con una password diversa. Se costretto, ' +
'puoi rivelare questa password alle terze parti. Se inserita in ' +
'BlueWallet, questa sbloccherà un "falso" archivio. Esso sembrerà ' +
'autentico alle terze parti, ma manterrà segretamente il tuo archivio principale ' +
'con i Bitcoin al sicuro.',
help2: 'Il nuovo archivio sarà completamente funzionante, e puoi conservarci ' + 'piccole quantità così sembrerà più credibile.',
create_fake_storage: 'Crea archivio falso criptato',
go_back: 'Indietro',
create_password: 'Crea una password',
create_password_explanation: "La password per l'archivio falso non deve corrispondere a quella dell'archivio principale",
password_should_not_match: "La password per l'archivio falso non deve corrispondere a quella dell'archivio principale",
retype_password: 'Reinserisci password',
passwords_do_not_match: 'Le password non corrispondono, riprova',
success: 'Fatto',
},
lnd: {
title: 'Gestisci fondi',
choose_source_wallet: 'Scegli un portafoglio sorgente',
refill_lnd_balance: 'Ricarica saldo del portafoglio Lightning',
refill: 'Ricarica',
withdraw: 'Preleva',
expired: 'Scaduto',
placeholder: 'Fattura',
sameWalletAsInvoiceError: 'Non puoi pagare una fattura con lo stesso portafoglio utilizzato per crearla.',
},
};

68
package-lock.json

@ -1,6 +1,6 @@
{
"name": "BlueWallet",
"version": "3.8.2",
"version": "3.8.3",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -897,9 +897,9 @@
}
},
"@react-navigation/core": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-3.0.3.tgz",
"integrity": "sha512-cE0hfOrh+qbAs0tjvlek99gas6+ecW5rtORhTdfZQ1byDGYBKjYZnDTEMImbWqAaAobyXOLEzK7A/zNrpDfiYA==",
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-3.1.1.tgz",
"integrity": "sha512-vVPUIpCWO3VKVvE5zYDDR/lOy5hHvRm60rQAHTF19vmt3Jqnbs3qqgYovfUAnTBm0crGLcuIwzOuprRIhC4bfQ==",
"requires": {
"create-react-context": "0.2.2",
"hoist-non-react-statics": "^3.0.1",
@ -920,13 +920,13 @@
}
},
"@react-navigation/native": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-3.1.3.tgz",
"integrity": "sha512-rXGpnkBWJM1K9iVMlRJdDzYb9zDHymwl16E0IKB0vQ9odaSHtNyfbfo6R2RLrNEXBcR9OPqoAnJoKpYoQeFG+Q==",
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-3.1.5.tgz",
"integrity": "sha512-sDqgNCx98XmiJR4lF8xCMVdADR4NBrx10TkTYbYcwFJ6SJ2LzrfxAW+FwUsESXr5TcSu3/6rGrLGr4rTGjkvRQ==",
"requires": {
"hoist-non-react-statics": "^3.0.1",
"react-native-gesture-handler": "~1.0.14",
"react-native-safe-area-view": "^0.12.0",
"react-native-safe-area-view": "^0.13.0",
"react-native-screens": "^1.0.0 || ^1.0.0-alpha"
},
"dependencies": {
@ -10911,9 +10911,9 @@
"integrity": "sha512-KIJqT9jQJDQx5h5uAVPimw6yVg2SekOKu959OCtktD3FjzbpvaPr8i4zzg07DOMz+igA4W/aNM7OV8H37pFYfA=="
},
"query-string": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-6.2.0.tgz",
"integrity": "sha512-5wupExkIt8RYL4h/FE+WTg3JHk62e6fFPWtAZA9J5IWK1PfTfKkMS93HBUHcFpeYi9KsY5pFbh+ldvEyaz5MyA==",
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-6.3.0.tgz",
"integrity": "sha512-jkpCkoHiAA2BYZvni5GieU3x860QDfkh2+M6bPnrYUywqOWbGwPq5VzntTS06ixX4GVHEiq2ZhlmGy/e9LQ3zA==",
"requires": {
"decode-uri-component": "^0.2.0",
"strict-uri-encode": "^2.0.0"
@ -11372,9 +11372,9 @@
}
},
"react-native-device-info": {
"version": "0.26.1",
"resolved": "https://registry.npmjs.org/react-native-device-info/-/react-native-device-info-0.26.1.tgz",
"integrity": "sha512-775TMZcJAybqtjHDTqkkIoRs9HH5szzyDaHqI5ipPmLfKD48nI1QfkcpfwDmjXIEtU33gXWnXr+Hdj4dMnNHtw=="
"version": "0.26.4",
"resolved": "https://registry.npmjs.org/react-native-device-info/-/react-native-device-info-0.26.4.tgz",
"integrity": "sha512-gQo/hSk26E/gK0W3QvkSv+mrBbceFszezxLfUriK3JbiUwvaOlXm6kru2jAu1XGJzfxo/JUO0Mc0lopqiPgw+A=="
},
"react-native-elements": {
"version": "0.19.0",
@ -11417,9 +11417,9 @@
"integrity": "sha512-PifW2VXYqH92u6GyJfEZAiZQYHmXuJvN7eouuC9ZWadfz+m4MFJbsVFAAsTQ1JzMiuo8B5KwK97hpY0cp7HD9w=="
},
"react-native-haptic-feedback": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/react-native-haptic-feedback/-/react-native-haptic-feedback-1.5.0.tgz",
"integrity": "sha512-0iEeqrHT39s5bnXEOUhkkdZnsGVe66hN7VPlDnjKkUy7i6tocODZNRzFrzdWZc+0Z7sQ3+wovXoylTsdoSL4Pw=="
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/react-native-haptic-feedback/-/react-native-haptic-feedback-1.6.0.tgz",
"integrity": "sha512-caZ0q5UqJQVYXeGvJPt8YMrrWXJ9a+uLNu6ilvSgc61ydjw3BY2fObvOTognJnV9aNn53LjJ7AAhsaVAsbW28g=="
},
"react-native-image-picker": {
"version": "0.28.0",
@ -11510,9 +11510,9 @@
"integrity": "sha512-/vnGkMTsxI+5y1togItXd2l/6JZzlUjsvQjCj9RWsOja9mVJxWdDacCrVHTf041k83fwX2Nw6ru6xxOWyU8Y+w=="
},
"react-native-safe-area-view": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/react-native-safe-area-view/-/react-native-safe-area-view-0.12.0.tgz",
"integrity": "sha512-UrAXmBC4KNR5K2eczIDZgqceWyKsgG9gmWFerHCvoyApfei8ceBB9u/c//PWCpS5Gt8MRLTmX5jPtzdXo2yNqg==",
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/react-native-safe-area-view/-/react-native-safe-area-view-0.13.1.tgz",
"integrity": "sha512-d/pu2866jApSwLtK/xWAvMXZkNTIQcFrjjbcTATBrmIfFNnu8TNFUcMRFpfJ+eOn5nmx7uGmDvs9B53Ft7JGpQ==",
"requires": {
"hoist-non-react-statics": "^2.3.1"
}
@ -11796,29 +11796,29 @@
}
},
"react-navigation": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/react-navigation/-/react-navigation-3.1.2.tgz",
"integrity": "sha512-Nj0Mu8D6ywL8TvThTTRMsMg8mBgqjWPb4Spanyq91ANXJHw5IQSlKHjtCcWvNW9ptFl5ExlkOG9y/jETM2LMOw==",
"requires": {
"@react-navigation/core": "3.0.3",
"@react-navigation/native": "3.1.3",
"react-navigation-drawer": "1.1.0",
"react-navigation-stack": "1.0.6",
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/react-navigation/-/react-navigation-3.3.2.tgz",
"integrity": "sha512-XETRxwPGHvJh3LKkAhX5b2sg2u9QiMegmEd0H5O0GWjqYrfJ7LcwzeM0273OfwcpR5lyl+hkCVPMkM+i1xFnVw==",
"requires": {
"@react-navigation/core": "3.1.1",
"@react-navigation/native": "3.1.5",
"react-navigation-drawer": "1.2.0",
"react-navigation-stack": "1.0.10",
"react-navigation-tabs": "1.0.2"
}
},
"react-navigation-drawer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/react-navigation-drawer/-/react-navigation-drawer-1.1.0.tgz",
"integrity": "sha512-OtO8g+t0pufbL0aiyZ9y2+j7cWIu9+agiaJfOiE2vPDOqGimpVfEYEuWj0xodKVRrEC4xrb8flqoxMxpE0wjdg==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/react-navigation-drawer/-/react-navigation-drawer-1.2.0.tgz",
"integrity": "sha512-78idNMJpOGzn0jHej69yTIiqJWdCVdMy2sBtppcdnT+DHeZXQDamTuGurjluf/2WyNB2xAXipIk4N4NnvqRfvw==",
"requires": {
"react-native-tab-view": "^1.2.0"
}
},
"react-navigation-stack": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/react-navigation-stack/-/react-navigation-stack-1.0.6.tgz",
"integrity": "sha512-7vnoceO6d/KYvtOSi3Ui3u1gvZEF/dBrOn+Gb1zqiZ3t+0oWRPpU36OmXAh/SwI5aokQyoihAlH9UBMfp+fbEA=="
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/react-navigation-stack/-/react-navigation-stack-1.0.10.tgz",
"integrity": "sha512-p+1oJ6lQYzblidOopIX0Tt+0ZvzaTyoPrBU9erMI04LEoa37BovYpWd1NXBIkV5+wfRt/o5R2x+RZ3LUeWpjeA=="
},
"react-navigation-tabs": {
"version": "1.0.2",

8
package.json

@ -1,6 +1,6 @@
{
"name": "BlueWallet",
"version": "3.8.2",
"version": "3.8.3",
"devDependencies": {
"babel-eslint": "^10.0.1",
"babel-jest": "^24.0.0",
@ -64,13 +64,13 @@
"react-localization": "1.0.10",
"react-native": "0.58.6",
"react-native-camera": "1.10.0",
"react-native-device-info": "0.26.1",
"react-native-device-info": "^0.26.4",
"react-native-elements": "0.19.0",
"react-native-flexi-radio-button": "0.2.2",
"react-native-fs": "2.13.3",
"react-native-gesture-handler": "1.0.15",
"react-native-google-analytics-bridge": "7.0.0",
"react-native-haptic-feedback": "1.5.0",
"react-native-haptic-feedback": "^1.6.0",
"react-native-image-picker": "0.28.0",
"react-native-level-fs": "3.0.1",
"react-native-linear-gradient": "2.5.3",
@ -92,7 +92,7 @@
"react-native-vector-icons": "6.2.0",
"react-native-webview": "4.1.0",
"react-native-wkwebview-reborn": "2.0.0",
"react-navigation": "3.1.2",
"react-navigation": "^3.3.2",
"react-test-render": "1.1.1",
"readable-stream": "3.1.1",
"secure-random": "1.1.1",

7
screen/lnd/scanLndInvoice.js

@ -15,6 +15,7 @@ import {
import { LightningCustodianWallet } from '../../class/lightning-custodian-wallet';
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
import { Icon } from 'react-native-elements';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
let EV = require('../../events');
@ -37,6 +38,7 @@ export default class ScanLndInvoice extends React.Component {
super(props);
if (!BlueApp.getWallets().some(item => item.type === LightningCustodianWallet.type)) {
ReactNativeHapticFeedback.trigger('notificationError', false);
alert('Before paying a Lightning invoice, you must first add a Lightning wallet.');
props.navigation.dismiss();
} else {
@ -91,6 +93,7 @@ export default class ScanLndInvoice extends React.Component {
processInvoice = data => {
this.setState({ isLoading: true }, async () => {
if (!this.state.fromWallet) {
ReactNativeHapticFeedback.trigger('notificationError', false);
alert('Before paying a Lightning invoice, you must first add a Lightning wallet.');
return this.props.navigation.goBack();
}
@ -130,6 +133,7 @@ export default class ScanLndInvoice extends React.Component {
} catch (Err) {
Keyboard.dismiss();
this.setState({ isLoading: false });
ReactNativeHapticFeedback.trigger('notificationError', false);
alert(Err.message);
}
});
@ -153,12 +157,14 @@ export default class ScanLndInvoice extends React.Component {
let expiresIn = (decoded.timestamp * 1 + decoded.expiry * 1) * 1000; // ms
if (+new Date() > expiresIn) {
this.setState({ isLoading: false });
ReactNativeHapticFeedback.trigger('notificationError', false);
return alert('Invoice expired');
}
const currentUserInvoices = fromWallet.user_invoices_raw; // not fetching invoices, as we assume they were loaded previously
if (currentUserInvoices.some(invoice => invoice.payment_hash === decoded.payment_hash)) {
this.setState({ isLoading: false });
ReactNativeHapticFeedback.trigger('notificationError', false);
return alert(loc.lnd.sameWalletAsInvoiceError);
}
@ -167,6 +173,7 @@ export default class ScanLndInvoice extends React.Component {
} catch (Err) {
console.log(Err.message);
this.setState({ isLoading: false });
ReactNativeHapticFeedback.trigger('notificationError', false);
return alert(Err.message);
}

3
screen/send/confirm.js

@ -5,6 +5,7 @@ import { Text } from 'react-native-elements';
import { BlueButton, SafeBlueArea, BlueCard, BlueSpacing40, BlueNavigationStyle } from '../../BlueComponents';
import { BitcoinUnit } from '../../models/bitcoinUnits';
import PropTypes from 'prop-types';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
let loc = require('../../loc');
let EV = require('../../events');
let currency = require('../../currency');
@ -45,7 +46,6 @@ export default class Confirm extends Component {
if (result && result.code) {
if (result.code === 1) {
const message = result.message.split('\n');
console.warn(message);
throw new Error(`${message[0]}: ${message[2]}`);
}
} else {
@ -59,6 +59,7 @@ export default class Confirm extends Component {
});
}
} catch (error) {
ReactNativeHapticFeedback.trigger('notificationError', false);
this.setState({ isLoading: false });
alert(error.message);
}

8
screen/send/details.js

@ -589,7 +589,13 @@ export default class SendDetails extends Component {
} else {
try {
const { address, amount, memo } = this.decodeBitcoinUri(text);
this.setState({ address, amount, memo, isLoading: false, bip70TransactionExpiration: null });
this.setState({
address: address || this.state.address,
amount: amount || this.state.amount,
memo: memo || this.state.memo,
isLoading: false,
bip70TransactionExpiration: null,
});
} catch (_) {
this.setState({ address: text.trim(), isLoading: false, bip70TransactionExpiration: null });
}

1
screen/settings/language.js

@ -24,6 +24,7 @@ export default class Language extends Component {
{ label: 'Danish (DK)', value: 'da_dk' },
{ label: 'Deutsch (DE)', value: 'de_de' },
{ label: 'Español (ES)', value: 'es' },
{ label: 'Italian (IT)', value: 'it' },
{ label: 'Français (FR)', value: 'fr_fr' },
{ label: 'Indonesia (ID)', value: 'id_id' },
{ label: '日本語 (JP)', value: 'jp_jp' },

39
screen/settings/lightningSettings.js

@ -1,3 +1,4 @@
/* global alert */
import React, { Component } from 'react';
import { AsyncStorage, View, TextInput, Linking } from 'react-native';
import { AppStorage } from '../../class';
@ -28,29 +29,28 @@ export default class LightningSettings extends Component {
this.setState({
isLoading: false,
URI,
defaultURI: new LightningCustodianWallet().getBaseURI(),
});
}
async save() {
save = () => {
this.setState({ isLoading: true }, async () => {
this.state.URI = this.state.URI ? this.state.URI : '';
await AsyncStorage.setItem(AppStorage.LNDHUB, this.state.URI);
// set each lnd wallets and re-init api
for (/** @type {LightningCustodianWallet} */ let w of BlueApp.getWallets()) {
if (w.type === LightningCustodianWallet.type) {
w.setBaseURI(this.state.URI);
w.init();
console.log('inited', w.baseURI);
}
try {
if (this.state.URI) {
await LightningCustodianWallet.isValidNodeAddress(this.state.URI);
// validating only if its not empty. empty means use default
}
await AsyncStorage.setItem(AppStorage.LNDHUB, this.state.URI);
alert('Your changes have been saved successfully');
} catch (error) {
alert('Not a valid LndHub URI');
console.log(error);
}
this.setState({ isLoading: false });
});
};
render() {
if (this.state.isLoading) {
return <BlueLoading />;
}
return (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={{ flex: 1 }}>
<BlueCard>
@ -90,7 +90,7 @@ export default class LightningSettings extends Component {
}}
>
<TextInput
placeholder={this.state.defaultURI}
placeholder={LightningCustodianWallet.defaultBaseUri}
value={this.state.URI}
onChangeText={text => this.setState({ URI: text })}
numberOfLines={1}
@ -101,12 +101,7 @@ export default class LightningSettings extends Component {
</View>
<BlueSpacing20 />
<BlueButton
onPress={() => {
this.save();
}}
title={loc.settings.save}
/>
{this.state.isLoading ? <BlueLoading /> : <BlueButton onPress={this.save} title={loc.settings.save} />}
</BlueCard>
</SafeBlueArea>
);

12
screen/wallets/details.js

@ -1,7 +1,7 @@
/* global alert */
import React, { Component } from 'react';
import { ActivityIndicator, View, Text, TextInput, Alert, TouchableOpacity, Keyboard, TouchableWithoutFeedback } from 'react-native';
import { BlueButton, SafeBlueArea, BlueCard, BlueSpacing20, BlueNavigationStyle } from '../../BlueComponents';
import { BlueButton, SafeBlueArea, BlueCard, BlueSpacing20, BlueNavigationStyle, BlueText } from '../../BlueComponents';
import PropTypes from 'prop-types';
import { LightningCustodianWallet } from '../../class/lightning-custodian-wallet';
import { HDLegacyBreadwalletWallet } from '../../class/hd-legacy-breadwallet-wallet';
@ -22,7 +22,9 @@ export default class WalletDetails extends Component {
disabled={navigation.getParam('isLoading') === true}
style={{ marginHorizontal: 16, height: 40, width: 40, justifyContent: 'center', alignItems: 'center' }}
onPress={() => {
if (navigation.state.params.saveAction) {
navigation.getParam('saveAction')();
}
}}
>
<Text style={{ color: '#0c2550' }}>{loc.wallets.details.save}</Text>
@ -54,7 +56,7 @@ export default class WalletDetails extends Component {
setLabel() {
this.props.navigation.setParams({ isLoading: true });
this.setState({ isLoading: true }, () => {
this.setState({ isLoading: true }, async () => {
this.state.wallet.setLabel(this.state.walletName);
BlueApp.saveToDisk();
alert('Wallet updated.');
@ -120,6 +122,12 @@ export default class WalletDetails extends Component {
{loc.wallets.details.type.toLowerCase()}
</Text>
<Text style={{ color: '#81868e', fontWeight: '500', fontSize: 14 }}>{this.state.wallet.typeReadable}</Text>
{this.state.wallet.type === LightningCustodianWallet.type && (
<React.Fragment>
<Text style={{ color: '#0c2550', fontWeight: '500', fontSize: 14, marginVertical: 12 }}>{'connected to'}</Text>
<BlueText>{this.state.wallet.getBaseURI()}</BlueText>
</React.Fragment>
)}
<View>
<BlueSpacing20 />
<BlueButton

20
screen/wallets/export.js

@ -40,13 +40,7 @@ export default class WalletExport extends Component {
Privacy.enableBlur();
this.setState({
isLoading: false,
showQr: false,
});
let that = this;
setTimeout(function() {
that.setState({ showQr: true });
}, 1000);
}
componentWillUnmount() {
@ -84,9 +78,7 @@ export default class WalletExport extends Component {
}
})()}
<BlueSpacing20 />
{(() => {
if (this.state.showQr) {
return (
<QRCode
value={this.state.wallet.getSecret()}
logo={require('../../img/qr-code.png')}
@ -96,15 +88,7 @@ export default class WalletExport extends Component {
logoBackgroundColor={BlueApp.settings.brandingColor}
ecl={'H'}
/>
);
} else {
return (
<View>
<ActivityIndicator />
</View>
);
}
})()}
<BlueSpacing20 />
<BlueText style={{ alignItems: 'center', paddingHorizontal: 8 }}>{this.state.wallet.getSecret()}</BlueText>

11
screen/wallets/import.js

@ -31,10 +31,10 @@ let loc = require('../../loc');
const { width } = Dimensions.get('window');
export default class WalletsImport extends Component {
static navigationOptions = ({ navigation }) => ({
static navigationOptions = {
...BlueNavigationStyle(),
title: loc.wallets.import.title,
});
};
constructor(props) {
super(props);
@ -75,7 +75,14 @@ export default class WalletsImport extends Component {
// is it lightning custodian?
if (text.indexOf('blitzhub://') !== -1 || text.indexOf('lndhub://') !== -1) {
let lnd = new LightningCustodianWallet();
if (text.includes('@')) {
const split = text.split('@');
lnd.setBaseURI(split[1]);
lnd.setSecret(split[0]);
} else {
lnd.setBaseURI(LightningCustodianWallet.defaultBaseUri);
lnd.setSecret(text);
}
await lnd.authorize();
await lnd.fetchTransactions();
await lnd.fetchBalance();

18
screen/wallets/transactions.js

@ -1,3 +1,4 @@
/* global alert */
import React, { Component } from 'react';
import {
Text,
@ -32,6 +33,7 @@ export default class WalletTransactions extends Component {
return {
headerRight: (
<TouchableOpacity
disabled={navigation.getParam('isLoading') === true}
style={{ marginHorizontal: 16, minWidth: 150, justifyContent: 'center', alignItems: 'flex-end' }}
onPress={() =>
navigation.navigate('WalletDetails', {
@ -60,7 +62,7 @@ export default class WalletTransactions extends Component {
// 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));
const wallet = props.navigation.getParam('wallet');
this.props.navigation.setParams({ wallet: wallet });
this.props.navigation.setParams({ wallet: wallet, isLoading: true });
this.state = {
isLoading: true,
showShowFlatListRefreshControl: false,
@ -74,6 +76,7 @@ export default class WalletTransactions extends Component {
componentDidMount() {
// nop
this.props.navigation.setParams({ isLoading: false });
}
/**
@ -162,7 +165,7 @@ export default class WalletTransactions extends Component {
console.log(wallet.getLabel(), 'fetch tx took', (end - start) / 1000, 'sec');
} catch (err) {
noErr = false;
console.warn(err);
alert(err.message);
this.setState({
isLoading: false,
showShowFlatListRefreshControl: false,
@ -309,8 +312,8 @@ export default class WalletTransactions extends Component {
<Text
style={{
flex: 1,
paddingLeft: 15,
paddingTop: 15,
marginLeft: 15,
marginTop: 10,
fontWeight: 'bold',
fontSize: 24,
color: BlueApp.settings.foregroundColor,
@ -345,6 +348,7 @@ export default class WalletTransactions extends Component {
this.redrawScreen();
}}
onWillBlur={() => this.onWillBlur()}
onDidFocus={() => this.props.navigation.setParams({ isLoading: false })}
/>
{this.renderWalletHeader()}
@ -358,19 +362,17 @@ export default class WalletTransactions extends Component {
>
<View
style={{
marginVertical: 16,
margin: 16,
backgroundColor: '#f2f2f2',
borderRadius: 9,
minWidth: 343,
minHeight: 49,
width: 343,
height: 49,
justifyContent: 'center',
alignItems: 'center',
alignSelf: 'center',
}}
>
<Text>marketplace</Text>
<Text style={{ color: '#062453', fontSize: 18 }}>marketplace</Text>
</View>
</TouchableOpacity>
)}

Loading…
Cancel
Save