Merge pull request #16 from sansegkh/master

merged from master
This commit is contained in:
San Segkhoonthod 2019-02-23 10:19:29 +07:00 committed by GitHub
commit 9e2421f73f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 3916 additions and 4144 deletions

View file

@ -9,7 +9,7 @@ import MockStorage from './MockStorage';
import { FiatUnit } from './models/fiatUnit';
global.crypto = require('crypto'); // shall be used by tests under nodejs CLI, but not in RN environment
let assert = require('assert');
jest.mock('react-native-custom-qr-codes', () => 'Video');
jest.mock('react-native-qrcode-svg', () => 'Video');
const AsyncStorage = new MockStorage();
jest.setMock('AsyncStorage', AsyncStorage);
jest.useFakeTimers();

View file

@ -45,7 +45,7 @@ if (aspectRatio > 1.6) {
export class BlueButton extends Component {
render() {
let backgroundColor = '#ccddf9';
let backgroundColor = this.props.backgroundColor ? this.props.backgroundColor : '#ccddf9';
let fontColor = '#0c2550';
if (this.props.hasOwnProperty('disabled') && this.props.disabled === true) {
backgroundColor = '#eef0f4';
@ -177,7 +177,14 @@ export const BlueNavigationStyle = (navigation, withNavigationCloseButton = fals
headerRight: withNavigationCloseButton ? (
<TouchableOpacity
style={{ width: 40, height: 40, padding: 14 }}
onPress={customCloseButtonFunction === undefined ? () => navigation.goBack(null) : customCloseButtonFunction}
onPress={
customCloseButtonFunction === undefined
? () => {
Keyboard.dismiss();
navigation.goBack(null);
}
: customCloseButtonFunction
}
>
<Image style={{ alignSelf: 'center' }} source={require('./img/close.png')} />
</TouchableOpacity>
@ -566,6 +573,29 @@ export class BlueUseAllFundsButton extends Component {
}
}
export class BlueDismissKeyboardInputAccessory extends Component {
static InputAccessoryViewID = 'BlueDismissKeyboardInputAccessory';
render() {
return Platform.OS !== 'ios' ? null : (
<InputAccessoryView nativeID={BlueDismissKeyboardInputAccessory.InputAccessoryViewID}>
<View
style={{
backgroundColor: '#eef0f4',
height: 44,
flex: 1,
flexDirection: 'row',
justifyContent: 'flex-end',
alignItems: 'center',
}}
>
<BlueButtonLink title="Done" onPress={Keyboard.dismiss} />
</View>
</InputAccessoryView>
);
}
}
export class BlueLoading extends Component {
render() {
return (
@ -1539,11 +1569,13 @@ export class BlueAddressInput extends Component {
onChangeText: PropTypes.func,
onBarScanned: PropTypes.func,
address: PropTypes.string,
placeholder: PropTypes.string,
};
static defaultProps = {
isLoading: false,
address: '',
placeholder: loc.send.details.address,
};
render() {
@ -1568,16 +1600,18 @@ export class BlueAddressInput extends Component {
onChangeText={text => {
this.props.onChangeText(text);
}}
placeholder={loc.send.details.address}
placeholder={this.props.placeholder}
numberOfLines={1}
value={this.props.address}
style={{ flex: 1, marginHorizontal: 8, minHeight: 33 }}
editable={!this.props.isLoading}
onSubmitEditing={Keyboard.dismiss}
{...this.props}
/>
<TouchableOpacity
disabled={this.props.isLoading}
onPress={() => {
Keyboard.dismiss();
ImagePicker.showImagePicker(
{
title: null,

View file

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

View file

@ -20,6 +20,13 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="bitcoin" />
<data android:scheme="lightning" />
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>

View file

@ -394,9 +394,6 @@ export class AbstractHDWallet extends LegacyWallet {
}
this.next_free_address_index = await binarySearchIterationForExternalAddress(100);
this.balance = 0;
this.unconfirmed_balance = 0;
this.usedAddresses = [];
// generating all involved addresses:

View file

@ -262,6 +262,7 @@ export class AppStorage {
* @return {Promise.<void>}
*/
async fetchWalletBalances(index) {
console.log('fetchWalletBalances for wallet#', index);
if (index || index === 0) {
let c = 0;
for (let wallet of this.wallets) {
@ -287,6 +288,7 @@ export class AppStorage {
* @return {Promise.<void>}
*/
async fetchWalletTransactions(index) {
console.log('fetchWalletTransactions for wallet#', index);
if (index || index === 0) {
let c = 0;
for (let wallet of this.wallets) {
@ -325,10 +327,11 @@ export class AppStorage {
* Getter for all transactions in all wallets.
* But if index is provided - only for wallet with corresponding index
*
* @param index {Integer} Wallet index in this.wallets. Empty for all wallets.
* @param index {Integer|null} Wallet index in this.wallets. Empty (or null) for all wallets.
* @param limit {Integer} How many txs return, starting from the earliest. Default: all of them.
* @return {Array}
*/
getTransactions(index) {
getTransactions(index, limit = Infinity) {
if (index || index === 0) {
let txs = [];
let c = 0;
@ -353,9 +356,11 @@ export class AppStorage {
t.sort_ts = +new Date(t.received);
}
return txs.sort(function(a, b) {
return txs
.sort(function(a, b) {
return b.sort_ts - a.sort_ts;
});
})
.slice(0, limit);
}
/**

View file

@ -40,8 +40,7 @@ export class LightningCustodianWallet extends LegacyWallet {
}
allowSend() {
console.log(this.getBalance(), this.getBalance() > 0);
return this.getBalance() > 0;
return true;
}
getAddress() {
@ -49,13 +48,11 @@ export class LightningCustodianWallet extends LegacyWallet {
}
timeToRefreshBalance() {
// only manual refresh for now
return false;
return (+new Date() - this._lastBalanceFetch) / 1000 > 3600; // 1hr
}
timeToRefreshTransaction() {
// only manual refresh for now
return false;
return (+new Date() - this._lastTxFetch) / 1000 > 3600; // 1hr
}
static fromJson(param) {
@ -112,6 +109,17 @@ export class LightningCustodianWallet extends LegacyWallet {
Authorization: 'Bearer' + ' ' + this.access_token,
},
});
if (response.originalResponse && typeof response.originalResponse === 'string') {
try {
response.originalResponse = JSON.parse(response.originalResponse);
} catch (_) {}
}
if (response.originalResponse && response.originalResponse.status && response.originalResponse.status === 503) {
throw new Error('Payment is in transit');
}
let json = response.body;
if (typeof json === 'undefined') {
throw new Error('API failure: ' + response.err + ' ' + JSON.stringify(response.originalResponse));
@ -434,6 +442,7 @@ export class LightningCustodianWallet extends LegacyWallet {
throw new Error('API unexpected response: ' + JSON.stringify(response.body));
}
this._lastTxFetch = +new Date();
this.transactions_raw = json;
}

View file

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

View file

@ -1,29 +1,17 @@
v3.5.7
------
v3.7.2
======
* FIX: Source wallet not found #239
* FIX: Changed the way currencies are shown in order to avoid indentation issue. FIX: Lint
* FIX: Reordered the language list by alphabetical order
* FIX: Localization fixes - Fix some dutch translation
* FIX: Do not dismiss modal on error, allow for network retry.
* FIX: Use DayJS locale for formatting dates
* FIX: too small QR on ln receive invoice (closes #235)
* FIX: send amount in satoshi wasnt with minus in tx list
* FIX: Date/time specifications #186
* ADD: Use platform's rating UI in-app
* FIX: limit number of userinvoices feetched per polling request
* REF: less remote fetches
* FIX: sometimes export QR is unreadable
* REF: HD wallets address derivation
* ADD: Added 日本語 in settings
* FIX: When having multiple wallets, I was unable to see my transactions description from the main screen
* FIX: Writing numbers standards #194
* FIX: Remove Lightning wallet creation limitation
* FIX: Can't create a Lightning Wallet 3.5.6 (223) #224
* FIX: zigzag link
* FIX: Lightning balance/fees don't add up (closes #225)
* ADD: THB fiat
* FIX: Thai translation
* FIX: Can't scan Bech32 addresses (closes #222)
* ADD: Croatian language into Settings.
* ADD: HRK Fiat in currency settings
* 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
* ADD: NZD as a currency

View file

@ -214,6 +214,7 @@ module.exports = {
refill: 'Doplnit',
withdraw: 'Vybrat',
expired: 'Expirováno',
placeholder: 'Invoice',
sameWalletAsInvoiceError: 'You can not pay an invoice with the same wallet used to create it.',
},
};

View file

@ -210,6 +210,7 @@ module.exports = {
},
lnd: {
title: 'Administration',
placeholder: 'Invoice',
choose_source_wallet: 'Vælge en wallet',
refill_lnd_balance: 'Genopfyld Lightning wallet',
refill: 'Genopfyld',

View file

@ -217,6 +217,7 @@ module.exports = {
refill_lnd_balance: 'Lade deine Lightning Wallet auf',
refill: 'Aufladen',
withdraw: 'Abheben',
placeholder: 'Invoice',
sameWalletAsInvoiceError:
'Du kannst nicht die Rechnung mit der Wallet begleichen, die du für die Erstellung dieser Rechnung verwendet hast.',
},

View file

@ -215,6 +215,7 @@ module.exports = {
refill: 'Refill',
withdraw: 'Withdraw',
expired: 'Expired',
placeholder: 'Invoice',
sameWalletAsInvoiceError: 'You can not pay an invoice with the same wallet used to create it.',
},
};

View file

@ -217,6 +217,7 @@ module.exports = {
refill_lnd_balance: 'Rellenar el balance de la billetera Lightning',
refill: 'Rellenar',
withdraw: 'Retirar',
placeholder: 'Invoice',
expired: 'Expirado',
sameWalletAsInvoiceError: 'You can not pay an invoice with the same wallet used to create it.',
},

View file

@ -216,6 +216,7 @@ module.exports = {
refill_lnd_balance: 'Déposer des fonds dans votre portfeuille Lightning',
refill: 'Déposer des fonds',
withdraw: 'Retirer des fonds',
placeholder: 'Invoice',
expired: 'Expiré',
sameWalletAsInvoiceError: 'Vous ne pouvez pas payer une facture avec le même portefeuille utilisé pour la créer.',
},

View file

@ -211,6 +211,7 @@ module.exports = {
refill_lnd_balance: 'Dopuni Lightning volet saldo',
refill: 'Dopuni',
withdraw: 'Isprazni',
placeholder: 'Invoice',
expired: 'Isteklo',
sameWalletAsInvoiceError: 'Buraz! Ne možeš platiti račun s istim voletom s kojim si račun stvorio, ono.',
},

220
loc/id_ID.js Normal file
View file

@ -0,0 +1,220 @@
module.exports = {
_: {
storage_is_encrypted: 'Penyimpanan dienkripsi. Masukkan kata sandi untuk dekripsi:',
enter_password: 'Masukkan kata sandi',
bad_password: 'kata sandi salah, coba lagi',
never: 'tidak pernah',
continue: 'Lanjutkan',
ok: 'OK',
},
wallets: {
select_wallet: 'Pilih dompet',
options: 'Opsi',
createBitcoinWallet:
'Belum ada dompet bitcoin. Untuk mendanai dompet Lightning, dompet Bitcoin harus dibuat atau diimpor. Yakin ingin melanjutkan?',
list: {
app_name: 'BlueWallet',
title: 'Dompet',
header: 'Sebuah dompet mewakili sepasang kunci rahasia dan sebuah alamat' + 'yang bisa dipilih untuk menerima koin.',
add: 'Tambah dompet',
create_a_wallet: 'Buat dompet',
create_a_wallet1: 'Gratis dan bisa buat',
create_a_wallet2: 'sebanyak yang kamu mau',
latest_transaction: 'transaksi terbaru',
empty_txs1: 'Transaksimu akan muncul di sini,',
empty_txs2: 'saat ini tidak ada transaksi',
tap_here_to_buy: 'Tap di sini untuk membeli bitcoin',
},
reorder: {
title: 'Susun Dompet',
},
add: {
title: 'tambah dompet',
description:
'Kamu bisa membuat dompet atau memindai paper wallet dalam WIF (Wallet Import Format). Bluewallet mendukung dompet Segwit.',
scan: 'Pindai',
create: 'Buat',
label_new_segwit: 'Dompet SegWit baru',
label_new_lightning: 'Dompet Lightning baru',
wallet_name: 'nama dompet',
wallet_type: 'tipe',
or: 'atau',
import_wallet: 'Impor dompet',
imported: 'Diimpor',
coming_soon: 'Akan datang',
lightning: 'Lightning',
bitcoin: 'Bitcoin',
},
details: {
title: 'Dompet',
address: 'Alamat',
type: 'Tipe',
label: 'Label',
destination: 'tujuan',
description: 'deskripsi',
are_you_sure: 'Yakin?',
yes_delete: 'Ya, hapus',
no_cancel: 'Tidak, batalkan',
delete: 'Hapus',
save: 'Simpan',
delete_this_wallet: 'Hapus dompet ini',
export_backup: 'Ekspor / backup',
buy_bitcoin: 'Beli Bitcoin',
show_xpub: 'Tampilkan XPUB dompet',
},
export: {
title: 'ekspor dompet',
},
xpub: {
title: 'XPUB dompet',
copiedToClipboard: 'Disalin ke clipboard.',
},
import: {
title: 'impor',
explanation: 'Ketik kata mnemonic, private key, WIF, atau apapun yang kamu punya. BlueWallet akan mencoba mengimpor dompet kamu.',
imported: 'Diimpor',
error: 'Gagal mengimpor. Pastikan data yang diketik benar.',
success: 'Berhasil',
do_import: 'Impor',
scan_qr: 'atau mau pindai QR code?',
},
scanQrWif: {
go_back: 'Kembali',
cancel: 'Batal',
decoding: 'Membaca...',
input_password: 'Masukkan kata sandi',
password_explain: 'Ini adalah private key terenkripsi BIP38',
bad_password: 'kata sandi salah',
wallet_already_exists: 'Dompet sudah ada',
bad_wif: 'WIF salah',
imported_wif: 'WIF diimpor ',
with_address: ' dengan alamat ',
imported_segwit: 'Dompet SegWit diimpor',
imported_legacy: 'Dompet lawas diimpor',
imported_watchonly: 'Alamat tinjauan diimpor',
},
},
transactions: {
list: {
tabBarLabel: 'Transaksi',
title: 'transaksi',
description: 'Daftar transaksi keluar dan masuk dompet',
conf: 'konfirmasi',
},
details: {
title: 'Transaksi',
from: 'Input',
to: 'Output',
copy: 'Salin',
transaction_details: 'Detail transaksi',
show_in_block_explorer: 'Tampilkan di block explorer',
},
},
send: {
header: 'Kirim',
details: {
title: 'buat transaksi',
amount_field_is_not_valid: 'Jumlah tidak valid',
fee_field_is_not_valid: 'Tarif tidak valid',
address_field_is_not_valid: 'Alamat tidak valid',
total_exceeds_balance: 'Jumlah yang dikirim melebihi saldo.',
create_tx_error: 'Kesalahan dalam membuat transaksi. Cek kembali alamat tujuan.',
address: 'alamat',
amount_placeholder: 'jumlah (dalam BTC)',
fee_placeholder: 'Tambahan biaya transaksi (dalam BTC)',
note_placeholder: 'catatan pribadi',
cancel: 'Batalkan',
scan: 'Pindai',
send: 'Kirim',
create: 'Buat',
remaining_balance: 'Sisa saldo',
},
confirm: {
header: 'Konfirmasi',
sendNow: 'Kirim sekarang',
},
success: {
done: 'Selesai',
},
create: {
details: 'Detail',
title: 'buat transaksi',
error: 'Tidak bisa membuat transaksi. Cek alamat atau jumlah transfer.',
go_back: 'Kembali',
this_is_hex: 'Ini adalah hex transaksi, siap untuk disiarkan ke jaringan.',
to: 'Ke',
amount: 'Jumlah',
fee: 'Tarif',
tx_size: 'Ukuran TX',
satoshi_per_byte: 'Satoshi per byte',
memo: 'Memo',
broadcast: 'Siarkan',
not_enough_fee: 'Tarif tidak cukup. Naikkan tarif',
},
},
receive: {
header: 'Terima',
details: {
title: 'Bagikan alamat ini ke pengirim',
share: 'bagikan',
copiedToClipboard: 'Disalin ke clipboard.',
label: 'Deskripsi',
create: 'Buat',
setAmount: 'Terima sejumlah',
},
},
buyBitcoin: {
header: 'Beli bitcoin',
tap_your_address: 'Untuk menyalin, tap alamat:',
copied: 'Disalin ke Clipboard!',
},
settings: {
header: 'setting',
plausible_deniability: 'Plausible deniability...',
storage_not_encrypted: 'Penyimpanan: tidak terenkripsi',
storage_encrypted: 'Penyimpanan: terenkripsi',
password: 'kata sandi',
password_explain: 'Buat kata sandi untuk dekripsi penyimpanan',
retype_password: 'Ulangi kata sandi',
passwords_do_not_match: 'Kata sandi tidak cocok',
encrypt_storage: 'Enkripsi penyimpanan',
lightning_settings: 'Pengaturan Lightning',
lightning_settings_explain:
'Pasang LndHub untuk menghubungkan ke node LND kamu' +
' dan masukkan URL di sini. Biarkan kosong untuk menghubungkan ke LndHub standar (lndhub.io)',
save: 'simpan',
about: 'Tentang',
language: 'Bahasa',
currency: 'Mata Uang',
},
plausibledeniability: {
title: 'Plausible Deniability',
help:
'Under certain circumstances, you might be forced to disclose a ' +
'password. To keep your coins safe, BlueWallet can create another ' +
'encrypted storage, with a different password. Under pressure, ' +
'you can disclose this password to a 3rd party. If entered in ' +
"BlueWallet, it will unlock new 'fake' storage. This will seem " +
'legit to a 3rd party, but will secretly keep your main storage ' +
'with coins safe.',
help2: 'New storage will be fully functional, and you can store some ' + 'minimum amounts there so it looks more believable.',
create_fake_storage: 'Create fake encrypted storage',
go_back: 'Go Back',
create_password: 'Create a password',
create_password_explanation: 'Password for fake storage should not match password for your main storage',
password_should_not_match: 'Password for fake storage should not match password for your main storage',
retype_password: 'Retype password',
passwords_do_not_match: 'Passwords do not match, try again',
success: 'Success',
},
lnd: {
title: 'atur dana',
choose_source_wallet: 'Pilih dompet sumber',
refill_lnd_balance: 'Isi ulang saldo Lightning',
refill: 'Isi ulang',
withdraw: 'Tarik',
placeholder: 'Invoice',
expired: 'Kadaluarsa',
sameWalletAsInvoiceError: 'Kamu tidak bisa membayar invoice dengan dompet yang sama yang dipakai untuk membuat invoice.',
},
};

View file

@ -17,6 +17,9 @@ dayjs.extend(relativeTime);
strings.setLanguage(lang);
let localeForDayJSAvailable = true;
switch (lang) {
case 'zh':
require('dayjs/locale/zh-cn');
break;
case 'ru':
require('dayjs/locale/ru');
break;
@ -53,6 +56,9 @@ dayjs.extend(relativeTime);
case 'hr_hr':
require('dayjs/locale/hr');
break;
case 'id_id':
require('dayjs/locale/id');
break;
default:
localeForDayJSAvailable = false;
break;
@ -81,9 +87,14 @@ dayjs.extend(relativeTime);
locale === 'th-th' ||
locale === 'da-dk' ||
locale === 'nl-nl' ||
locale === 'hr-hr'
locale === 'hr-hr' ||
locale === 'id-id' ||
locale === 'zh-cn'
) {
switch (locale) {
case 'zh-cn':
require('dayjs/locale/zh-cn');
break;
case 'ru':
require('dayjs/locale/ru');
break;
@ -117,6 +128,9 @@ dayjs.extend(relativeTime);
case 'hr-hr':
require('dayjs/locale/hr');
break;
case 'id-id':
require('dayjs/locale/id');
break;
default:
break;
}
@ -145,6 +159,8 @@ strings = new Localization({
nl_nl: require('./nl_NL.js'),
fr_fr: require('./fr_FR.js'),
hr_hr: require('./hr_HR.js'),
id_id: require('./id_ID.js'),
zh_cn: require('./zh_cn.js'),
});
strings.saveLanguage = lang => AsyncStorage.setItem(AppStorage.LANG, lang);
@ -156,13 +172,6 @@ strings.transactionTimeToReadable = time => {
return dayjs(time).fromNow();
};
strings.transactionTimeToReadableToFuture = time => {
if (time === 0) {
return strings._.never;
}
return dayjs(time).toNow();
};
function removeTrailingZeros(value) {
value = value.toString();

View file

@ -216,6 +216,7 @@ module.exports = {
refill_lnd_balance: 'Lightning ウォレットへ送金',
refill: '送金',
withdraw: '引き出し',
placeholder: '入金依頼',
expired: '失効',
sameWalletAsInvoiceError: '以前作成したウォレットと同じウォレットへの支払いはできません。',
},

View file

@ -218,6 +218,7 @@ module.exports = {
refill: 'Bijvullen',
withdraw: 'Opvragen',
expired: 'Verlopen',
placeholder: 'Invoice',
sameWalletAsInvoiceError: 'U kunt geen factuur betalen met dezelfde portemonnee die is gebruikt om de factuur te maken.',
},
};

View file

@ -217,6 +217,7 @@ module.exports = {
choose_source_wallet: 'Escolha a carteira de origem',
refill_lnd_balance: 'Recarregar a carteira Lightning',
refill: 'Recarregar',
placeholder: 'Invoice',
withdraw: 'Sacar',
expired: 'Vencido',
sameWalletAsInvoiceError: 'Você não pode pagar uma fatura com a mesma carteira que a criou.',

View file

@ -216,6 +216,7 @@ module.exports = {
choose_source_wallet: 'Escolha a wallet',
refill_lnd_balance: 'Carregar o saldo da Lightning wallet',
refill: 'Carregar',
placeholder: 'Invoice',
withdraw: 'Transferir',
expired: 'Expired',
sameWalletAsInvoiceError: 'You can not pay an invoice with the same wallet used to create it.',

View file

@ -219,6 +219,7 @@ module.exports = {
refill: 'Пополнить',
withdraw: 'Вывести',
expired: 'Истекший',
placeholder: 'Invoice',
sameWalletAsInvoiceError: 'Ты не можешь оплатить счет тем же кошельком, который ты использовал для его создания.',
},
};

View file

@ -214,6 +214,7 @@ module.exports = {
refill_lnd_balance: 'เติมกระเป๋าสตางค์ไลท์นิง',
refill: 'เติม',
withdraw: 'ถอน',
placeholder: 'Invoice',
expired: 'หมดอายุแล้ว',
sameWalletAsInvoiceError: 'คุณไม่สามารถจ่ายใบแจ้งหนี้นี้ด้วยกระเป๋าสตางค์อันเดียวกันกับที่ใช้สร้างมัน.',
},

View file

@ -217,6 +217,7 @@ module.exports = {
choose_source_wallet: 'Оберіть гаманець с якого слати',
refill_lnd_balance: 'Збільшити баланс Lightning гаманця',
refill: 'Поповнити',
placeholder: 'Invoice',
withdraw: 'Вивести',
expired: 'Expired',
sameWalletAsInvoiceError: 'You can not pay an invoice with the same wallet used to create it.',

215
loc/zh_cn.js Executable file
View file

@ -0,0 +1,215 @@
module.exports = {
_: {
storage_is_encrypted: '你的信息已经被加密, 请输入密码解密',
enter_password: '输入密码',
bad_password: '密码无效,请重试',
never: '不',
continue: '继续',
ok: '好的',
},
wallets: {
select_wallet: '选择钱包',
options: '选项',
createBitcoinWallet: '您当前没有bitcoin钱包. 为了支持闪电钱包, 我们需要创建或者导入一个比特币钱包. 是否需要继续?',
list: {
app_name: 'BlueWallet',
title: '钱包',
header: '一个钱包代表一对的私钥和地址' + '你可以通过分享收款.',
add: '添加钱包',
create_a_wallet: '创建一个钱包',
create_a_wallet1: '创建钱包是免费的,你可以',
create_a_wallet2: '想创建多少就创建多少个',
latest_transaction: '最近的转账',
empty_txs1: '你的转账信息将展示在这里',
empty_txs2: '当前无信息',
tap_here_to_buy: '点击购买比特币',
},
reorder: {
title: '重新排列钱包',
},
add: {
title: '添加钱包',
description: '你可以扫描你的纸质备份钱包 (WIF格式), 或者创建一个新钱包. 默认支持隔离见证钱包',
scan: '扫描',
create: '创建',
label_new_segwit: '新隔离见证(Segwit)',
label_new_lightning: '新闪电',
wallet_name: '钱包名称',
wallet_type: '类型',
or: '或',
import_wallet: '导入钱包',
imported: '已经导入',
coming_soon: '即将来临',
lightning: '闪电',
bitcoin: '比特币',
},
details: {
title: '钱包',
address: '地址',
type: '类型',
label: '标签',
destination: '目的',
description: '描述',
are_you_sure: '你确认么?',
yes_delete: '是的,删除',
no_cancel: '不,取消',
delete: '删除',
save: '保存',
delete_this_wallet: '删除这个钱包',
export_backup: '导出备份',
buy_bitcoin: '购买比特币',
show_xpub: '展示钱包 XPUB',
},
export: {
title: '钱包导出',
},
xpub: {
title: '钱包 XPUB',
copiedToClipboard: '复制到粘贴板.',
},
import: {
title: '导入',
explanation: '输入你的助记词私钥或者WIF, 或者其他格式的数据. BlueWallet将尽可能的自动识别数据格式并导入钱包',
imported: '已经导入',
error: '导入失败,请确认你提供的信息是有效的',
success: '成功',
do_import: '导入',
scan_qr: '或扫面二维码',
},
scanQrWif: {
go_back: '回退',
cancel: '取消',
decoding: '解码中',
input_password: '输入密码',
password_explain: '这是一个BIP38加密的私钥',
bad_password: '密码错误',
wallet_already_exists: '当前钱包已经存在',
bad_wif: 'WIF格式无效',
imported_wif: 'WIF已经导入',
with_address: ' 地址为',
imported_segwit: 'SegWit已经导入',
imported_legacy: 'Legacy已经导入',
imported_watchonly: '导入只读',
},
},
transactions: {
list: {
tabBarLabel: '转账',
title: '转账',
description: '当前所有钱包的转入和转出记录',
conf: '配置',
},
details: {
title: '转账',
from: '输入',
to: '输出',
copy: '复制',
transaction_details: '转账详情',
show_in_block_explorer: '区块浏览器展示',
},
},
send: {
header: '发送',
details: {
title: '创建交易',
amount_field_is_not_valid: '金额格式无效',
fee_field_is_not_valid: '费用格式无效',
address_field_is_not_valid: '地址内容无效',
total_exceeds_balance: '余额不足',
create_tx_error: '创建交易失败. 请确认地址格式正确.',
address: '地址',
amount_placeholder: '发送金额(in BTC)',
fee_placeholder: '手续费用 (in BTC)',
note_placeholder: '消息',
cancel: '取消',
scan: '扫描',
send: '发送',
create: '创建',
remaining_balance: '剩余金额',
},
confirm: {
header: '确认',
sendNow: '现在发送',
},
success: {
done: '完成',
},
create: {
details: '详情',
title: '创建详情',
error: '创建交易失败. 无效地址或金额?',
go_back: '回退',
this_is_hex: '这个是交易的十六进制数据, 签名并广播到全网络.',
to: '到',
amount: '金额',
fee: '手续费',
tx_size: '交易大小',
satoshi_per_byte: '葱每byte',
memo: '消息',
broadcast: '广播',
not_enough_fee: '手续费不够,请增加手续费',
},
},
receive: {
header: '收款',
details: {
title: '分享这个地址给付款人',
share: '分享',
copiedToClipboard: '复制到粘贴板.',
label: '描述',
create: '创建',
setAmount: '收款金额',
},
},
buyBitcoin: {
header: '购买比特币',
tap_your_address: '点击地址复制到粘贴板:',
copied: '复制到粘贴板!',
},
settings: {
header: '设置',
plausible_deniability: '可否认性...',
storage_not_encrypted: '存储:未加密',
storage_encrypted: '存储:加密中',
password: '密码',
password_explain: '创建你的加密密码',
retype_password: '再次输入密码',
passwords_do_not_match: '两次输入密码不同',
encrypt_storage: '加密存储',
lightning_settings: '闪电网络设置',
lightning_settings_explain: '如要要连接你自己的闪电节点请安装LndHub' + ' 并把url地址输入到下面. 空白将使用默认的LndHub (lndhub.io)',
save: '保存',
about: '关于',
language: '语言',
currency: '货币',
},
plausibledeniability: {
title: '可否认性',
help:
'在某些情况下, 你不得不暴露 ' +
'密码. 为了让你的比特币更加安全, BlueWallet可以创建一些 ' +
'加密空间, 用不同的密码. 在压力之下, ' +
'你可以暴露这个钱包密码. 再次进入 ' +
'BlueWallet, 我们会解锁一些虚拟空间. 对第三方来说看上去' +
'是合理的, 但会偷偷的帮你保证主钱包的安全 ' +
'币也就安全了.',
help2: '新的空间具备完整的功能,你可以存在 ' + '少量的金额在里面.',
create_fake_storage: '创建虚拟加密存储',
go_back: '回退',
create_password: '创建密码',
create_password_explanation: '虚拟存储空间密码不能和主存储空间密码相同',
password_should_not_match: '虚拟存储空间密码不能和主存储空间密码相同',
retype_password: '重输密码',
passwords_do_not_match: '两次输入密码不同,请重新输入',
success: '成功',
},
lnd: {
title: '配置资金支持',
choose_source_wallet: '选择一个资金源钱包',
refill_lnd_balance: '给闪电钱包充值',
refill: '充值',
withdraw: '提取',
expired: '超时',
sameWalletAsInvoiceError: '你不能用创建账单的钱包去支付该账单',
},
};

View file

@ -3,6 +3,7 @@ export const FiatUnit = Object.freeze({
AUD: { endPointKey: 'AUD', symbol: '$', locale: 'en-AU' },
BRL: { endPointKey: 'BRL', symbol: 'R$', locale: 'pt-BR' },
CAD: { endPointKey: 'CAD', symbol: '$', locale: 'en-CA' },
CHF: { endPointKey: 'CHF', symbol: 'CHF', locale: 'de-CH' },
CZK: { endPointKey: 'CZK', symbol: 'Kč', locale: 'cs-CZ' },
CNY: { endPointKey: 'CNY', symbol: '¥', locale: 'zh-CN' },
EUR: { endPointKey: 'EUR', symbol: '€', locale: 'en-EN' },

6670
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "BlueWallet",
"version": "3.7.2",
"version": "3.8.0",
"devDependencies": {
"babel-eslint": "^10.0.1",
"babel-jest": "^24.0.0",
@ -36,67 +36,65 @@
}
},
"dependencies": {
"@babel/preset-env": "^7.3.1",
"@remobile/react-native-qrcode-local-image": "^1.0.4",
"bignumber.js": "^8.0.2",
"bip21": "^2.0.2",
"bip39": "^2.5.0",
"bitcoinjs-lib": "^3.3.2",
"buffer": "^5.2.1",
"buffer-reverse": "^1.0.1",
"crypto-js": "^3.1.9-1",
"dayjs": "^1.8.0",
"@babel/preset-env": "7.3.1",
"@remobile/react-native-qrcode-local-image": "1.0.4",
"bignumber.js": "8.0.2",
"bip21": "2.0.2",
"bip39": "2.5.0",
"bitcoinjs-lib": "3.3.2",
"buffer": "5.2.1",
"buffer-reverse": "1.0.1",
"crypto-js": "3.1.9-1",
"dayjs": "1.8.6",
"electrum-client": "git+https://github.com/Overtorment/node-electrum-client.git",
"eslint-config-prettier": "^4.0.0",
"eslint-config-standard": "^12.0.0",
"eslint-config-standard-react": "^7.0.2",
"eslint-plugin-prettier": "^3.0.1",
"eslint-plugin-standard": "^4.0.0",
"frisbee": "^2.0.5",
"intl": "^1.2.5",
"mocha": "^5.2.0",
"node-libs-react-native": "^1.0.1",
"path-browserify": "^1.0.0",
"prettier": "^1.16.3",
"process": "^0.11.10",
"prop-types": "^15.6.2",
"react": "^16.7.0",
"react-localization": "^1.0.10",
"react-native": "^0.58.1",
"react-native-camera": "^1.10.0",
"react-native-custom-qr-codes": "^2.0.0",
"react-native-device-info": "^0.26.1",
"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-image-picker": "^0.28.0",
"react-native-level-fs": "^3.0.1",
"react-native-linear-gradient": "^2.5.3",
"react-native-modal": "^7.0.2",
"react-native-permissions": "^1.1.1",
"react-native-prompt-android": "^0.3.4",
"react-native-qrcode": "^0.2.7",
"react-native-randombytes": "^3.5.2",
"react-native-rate": "^1.1.6",
"react-native-sentry": "^0.41.1",
"react-native-snap-carousel": "^3.7.5",
"eslint-config-prettier": "4.0.0",
"eslint-config-standard": "12.0.0",
"eslint-config-standard-react": "7.0.2",
"eslint-plugin-prettier": "3.0.1",
"eslint-plugin-standard": "4.0.0",
"frisbee": "2.0.5",
"intl": "1.2.5",
"mocha": "5.2.0",
"node-libs-react-native": "1.0.1",
"path-browserify": "1.0.0",
"prettier": "1.16.3",
"process": "0.11.10",
"prop-types": "15.6.2",
"react": "16.7.0",
"react-localization": "1.0.10",
"react-native": "0.58.1",
"react-native-camera": "1.10.0",
"react-native-device-info": "0.26.1",
"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-image-picker": "0.28.0",
"react-native-level-fs": "3.0.1",
"react-native-linear-gradient": "2.5.3",
"react-native-modal": "7.0.2",
"react-native-permissions": "1.1.1",
"react-native-prompt-android": "0.3.4",
"react-native-qrcode-svg": "5.1.2",
"react-native-randombytes": "3.5.2",
"react-native-rate": "1.1.6",
"react-native-sentry": "0.41.1",
"react-native-snap-carousel": "3.7.5",
"react-native-sortable-list": "0.0.22",
"react-native-svg": "^9.1.1",
"react-native-tcp": "^3.3.0",
"react-native-vector-icons": "^6.2.0",
"react-native-svg": "9.1.1",
"react-native-tcp": "3.3.0",
"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-test-render": "^1.1.1",
"readable-stream": "^3.1.1",
"request-promise-native": "^1.0.5",
"secure-random": "^1.1.1",
"stream-browserify": "^2.0.2",
"util": "^0.11.1",
"wif": "^2.0.1"
"react-native-wkwebview-reborn": "2.0.0",
"react-navigation": "3.1.2",
"react-test-render": "1.1.1",
"readable-stream": "3.1.1",
"secure-random": "1.1.1",
"stream-browserify": "2.0.2",
"util": "0.11.1",
"wif": "2.0.1"
},
"react-native": {
"path": "path-browserify",

View file

@ -1,7 +1,7 @@
/* global alert */
import React, { Component } from 'react';
import { ActivityIndicator, View, TextInput, KeyboardAvoidingView, Keyboard, TouchableWithoutFeedback, Text } from 'react-native';
import { BlueNavigationStyle, BlueButton, BlueBitcoinAmount } from '../../BlueComponents';
import { BlueNavigationStyle, BlueButton, BlueBitcoinAmount, BlueDismissKeyboardInputAccessory } from '../../BlueComponents';
import PropTypes from 'prop-types';
import { BitcoinUnit } from '../../models/bitcoinUnits';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
@ -47,7 +47,7 @@ export default class LNDCreateInvoice extends Component {
renderCreateButton = () => {
return (
<View style={{ paddingHorizontal: 56, paddingVertical: 16, alignContent: 'center', backgroundColor: '#FFFFFF' }}>
<View style={{ marginHorizontal: 56, marginVertical: 16, minHeight: 45, alignContent: 'center', backgroundColor: '#FFFFFF' }}>
{this.state.isLoading ? (
<ActivityIndicator />
) : (
@ -79,6 +79,7 @@ export default class LNDCreateInvoice extends Component {
}}
disabled={this.state.isLoading}
unit={BitcoinUnit.SATS}
inputAccessoryViewID={BlueDismissKeyboardInputAccessory.InputAccessoryViewID}
/>
<View
style={{
@ -104,8 +105,10 @@ export default class LNDCreateInvoice extends Component {
style={{ flex: 1, marginHorizontal: 8, minHeight: 33 }}
editable={!this.state.isLoading}
onSubmitEditing={Keyboard.dismiss}
inputAccessoryViewID={BlueDismissKeyboardInputAccessory.InputAccessoryViewID}
/>
</View>
<BlueDismissKeyboardInputAccessory />
{this.renderCreateButton()}
</KeyboardAvoidingView>
</View>

View file

@ -11,7 +11,7 @@ import {
BlueSpacing20,
} from '../../BlueComponents';
import PropTypes from 'prop-types';
import { QRCode } from 'react-native-custom-qr-codes';
import QRCode from 'react-native-qrcode-svg';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
const loc = require('../../loc');
@ -49,11 +49,12 @@ export default class LNDViewAdditionalInvoiceInformation extends Component {
<View style={{ flex: 1, justifyContent: 'space-between', alignItems: 'center' }}>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: 16 }}>
<QRCode
content={this.state.walletInfo.uris[0]}
size={300}
color={BlueApp.settings.foregroundColor}
backgroundColor={BlueApp.settings.brandingColor}
value={this.state.walletInfo.uris[0]}
logo={require('../../img/qr-code.png')}
size={300}
logoSize={90}
color={BlueApp.settings.foregroundColor}
logoBackgroundColor={BlueApp.settings.brandingColor}
/>
<BlueSpacing20 />
<BlueText>Open direct channel with this node:</BlueText>

View file

@ -12,11 +12,11 @@ import {
import PropTypes from 'prop-types';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
import { Icon } from 'react-native-elements';
import QRCode from 'react-native-qrcode-svg';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
const loc = require('../../loc');
const EV = require('../../events');
const QRFast = require('react-native-qrcode');
const { width, height } = Dimensions.get('window');
export default class LNDViewInvoice extends Component {
@ -42,7 +42,7 @@ export default class LNDViewInvoice extends Component {
qrCodeHeight: height > width ? width - 20 : width / 2,
};
this.fetchInvoiceInterval = undefined;
BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);
BackHandler.addEventListener('hardwareBackPress', this.handleBackButton.bind(this));
}
async componentDidMount() {
@ -92,11 +92,11 @@ export default class LNDViewInvoice extends Component {
componentWillUnmount() {
clearInterval(this.fetchInvoiceInterval);
this.fetchInvoiceInterval = undefined;
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton.bind(this));
}
handleBackButton() {
this.props.navigation.dismiss();
this.props.navigation.popToTop();
return true;
}
@ -187,11 +187,13 @@ export default class LNDViewInvoice extends Component {
onLayout={this.onLayout}
>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 16 }}>
<QRFast
<QRCode
value={typeof this.state.invoice === 'object' ? invoice.payment_request : invoice}
fgColor={BlueApp.settings.brandingColor}
bgColor={BlueApp.settings.foregroundColor}
logo={require('../../img/qr-code.png')}
size={this.state.qrCodeHeight}
logoSize={90}
color={BlueApp.settings.foregroundColor}
logoBackgroundColor={BlueApp.settings.brandingColor}
/>
</View>
@ -239,6 +241,6 @@ LNDViewInvoice.propTypes = {
goBack: PropTypes.func,
navigate: PropTypes.func,
getParam: PropTypes.func,
dismiss: PropTypes.func,
popToTop: PropTypes.func,
}),
};

View file

@ -110,6 +110,7 @@ export default class ScanLndInvoice extends React.Component {
isLoading: false,
});
} catch (Err) {
Keyboard.dismiss();
this.setState({ isLoading: false });
alert(Err.message);
}
@ -209,6 +210,7 @@ export default class ScanLndInvoice extends React.Component {
onBarScanned={this.processInvoice}
address={this.state.destination}
isLoading={this.state.isLoading}
placeholder={loc.lnd.placeholder}
/>
<View
style={{

View file

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { View, Share } from 'react-native';
import { QRCode } from 'react-native-custom-qr-codes';
import QRCode from 'react-native-qrcode-svg';
import bip21 from 'bip21';
import {
BlueLoading,
@ -36,10 +36,10 @@ export default class ReceiveDetails extends Component {
addressText: '',
};
// EV(EV.enum.RECEIVE_ADDRESS_CHANGED, this.refreshFunction.bind(this));
// EV(EV.enum.RECEIVE_ADDRESS_CHANGED, this.redrawScreen.bind(this));
}
/* refreshFunction(newAddress) {
/* redrawScreen(newAddress) {
console.log('newAddress =', newAddress);
this.setState({
address: newAddress,
@ -88,11 +88,12 @@ export default class ReceiveDetails extends Component {
<View style={{ flex: 1, justifyContent: 'space-between', alignItems: 'center' }}>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: 16 }}>
<QRCode
content={bip21.encode(this.state.address)}
size={(is.ipad() && 300) || 300}
color={BlueApp.settings.foregroundColor}
backgroundColor={BlueApp.settings.brandingColor}
value={bip21.encode(this.state.address)}
logo={require('../../img/qr-code.png')}
size={(is.ipad() && 300) || 300}
logoSize={90}
color={BlueApp.settings.foregroundColor}
logoBackgroundColor={BlueApp.settings.brandingColor}
/>
<BlueCopyTextToClipboard text={this.state.addressText} />
</View>

View file

@ -1,7 +1,6 @@
import React, { Component } from 'react';
import { View, Share, TextInput, KeyboardAvoidingView, Platform, Dimensions, ScrollView } from 'react-native';
import { QRCode as QRSlow } from 'react-native-custom-qr-codes';
import QRFast from 'react-native-qrcode';
import { View, Share, TextInput, KeyboardAvoidingView, Dimensions, ScrollView } from 'react-native';
import QRCode from 'react-native-qrcode-svg';
import bip21 from 'bip21';
import {
SafeBlueArea,
@ -105,23 +104,15 @@ export default class ReceiveAmount extends Component {
{this.state.label}
</BlueText>
<View style={{ justifyContent: 'center', alignItems: 'center', paddingHorizontal: 16 }}>
{Platform.OS === 'ios' || this.state.bip21.length < 54 ? (
<QRSlow
content={this.state.bip21}
size={this.determineSize()}
color={BlueApp.settings.foregroundColor}
backgroundColor={BlueApp.settings.brandingColor}
<QRCode
value={this.state.bip21}
logo={require('../../img/qr-code.png')}
size={this.determineSize()}
logoSize={90}
color={BlueApp.settings.foregroundColor}
logoBackgroundColor={BlueApp.settings.brandingColor}
ecl={'Q'}
/>
) : (
<QRFast
value={this.state.bip21}
size={this.determineSize()}
fgColor={BlueApp.settings.brandingColor}
bgColor={BlueApp.settings.foregroundColor}
/>
)}
</View>
<View style={{ alignItems: 'center', justifyContent: 'space-between' }}>
<BlueCopyTextToClipboard text={this.state.bip21} />

View file

@ -4,6 +4,7 @@ import {
ActivityIndicator,
View,
TextInput,
StatusBar,
TouchableOpacity,
KeyboardAvoidingView,
Keyboard,
@ -15,13 +16,20 @@ import {
Text,
} from 'react-native';
import { Icon } from 'react-native-elements';
import { BlueNavigationStyle, BlueButton, BlueBitcoinAmount, BlueAddressInput } from '../../BlueComponents';
import {
BlueNavigationStyle,
BlueButton,
BlueBitcoinAmount,
BlueAddressInput,
BlueDismissKeyboardInputAccessory,
BlueLoading,
} from '../../BlueComponents';
import PropTypes from 'prop-types';
import Modal from 'react-native-modal';
import NetworkTransactionFees, { NetworkTransactionFee } from '../../models/networkTransactionFees';
import BitcoinBIP70TransactionDecode from '../../bip70/bip70';
import { BitcoinUnit } from '../../models/bitcoinUnits';
import { HDLegacyP2PKHWallet, HDSegwitP2SHWallet } from '../../class';
import { HDLegacyP2PKHWallet, HDSegwitP2SHWallet, LightningCustodianWallet } from '../../class';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
const bip21 = require('bip21');
let BigNumber = require('bignumber.js');
@ -38,6 +46,8 @@ export default class SendDetails extends Component {
title: loc.send.header,
});
state = { isLoading: true, fromWallet: undefined };
constructor(props) {
super(props);
console.log('props.navigation.state.params=', props.navigation.state.params);
@ -51,8 +61,18 @@ export default class SendDetails extends Component {
if (props.navigation.state.params) fromSecret = props.navigation.state.params.fromSecret;
let fromWallet = null;
const wallets = BlueApp.getWallets();
const wallets = BlueApp.getWallets().filter(wallet => wallet.type !== LightningCustodianWallet.type);
if (wallets.length === 0) {
alert('Before creating a transaction, you must first add a Bitcoin wallet.');
return props.navigation.goBack(null);
} else {
if (!fromWallet && wallets.length > 0) {
fromWallet = wallets[0];
fromAddress = fromWallet.getAddress();
fromSecret = fromWallet.getSecret();
}
if (fromWallet === null) return props.navigation.goBack(null);
for (let w of wallets) {
if (w.getSecret() === fromSecret) {
fromWallet = w;
@ -64,15 +84,11 @@ export default class SendDetails extends Component {
}
}
// fallback to first wallet if it exists
if (!fromWallet && wallets[0]) fromWallet = wallets[0];
this.state = {
isFeeSelectionModalVisible: false,
fromAddress,
fromWallet,
fromSecret,
isLoading: false,
address,
memo,
fee: 1,
@ -82,7 +98,13 @@ export default class SendDetails extends Component {
renderWalletSelectionButtonHidden: false,
};
}
}
/**
* TODO: refactor this mess, get rid of regexp, use https://github.com/bitcoinjs/bitcoinjs-lib/issues/890 etc etc
*
* @param data {String} Can be address or `bitcoin:xxxxxxx` uri scheme, or invalid garbage
*/
processAddressData = data => {
this.setState(
{ isLoading: true },
@ -91,7 +113,7 @@ export default class SendDetails extends Component {
this.processBIP70Invoice(data);
} else {
const dataWithoutSchema = data.replace('bitcoin:', '');
if (btcAddressRx.test(dataWithoutSchema) || dataWithoutSchema.indexOf('bc1') === 0) {
if (btcAddressRx.test(dataWithoutSchema) || (dataWithoutSchema.indexOf('bc1') === 0 && dataWithoutSchema.indexOf('?') === -1)) {
this.setState({
address: dataWithoutSchema,
bip70TransactionExpiration: null,
@ -111,7 +133,7 @@ export default class SendDetails extends Component {
this.setState({ isLoading: false });
}
console.log(options);
if (btcAddressRx.test(address)) {
if (btcAddressRx.test(address) || address.indexOf('bc1') === 0) {
this.setState({
address,
amount: options.amount,
@ -128,6 +150,7 @@ export default class SendDetails extends Component {
};
async componentDidMount() {
StatusBar.setBarStyle('dark-content');
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow);
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
try {
@ -284,6 +307,7 @@ export default class SendDetails extends Component {
}
async createTransaction() {
Keyboard.dismiss();
this.setState({ isLoading: true });
let error = false;
let requestedSatPerByte = this.state.fee.toString().replace(/\D/g, '');
@ -450,6 +474,7 @@ export default class SendDetails extends Component {
placeholderTextColor="#37c0a1"
placeholder={this.state.networkTransactionFees.halfHourFee.toString()}
style={{ fontWeight: '600', color: '#37c0a1', marginBottom: 0, marginRight: 4, textAlign: 'right', fontSize: 36 }}
inputAccessoryViewID={BlueDismissKeyboardInputAccessory.InputAccessoryViewID}
/>
<Text
style={{
@ -529,10 +554,10 @@ export default class SendDetails extends Component {
};
render() {
if (!this.state.fromWallet.getAddress) {
if (this.state.isLoading || typeof this.state.fromWallet === 'undefined') {
return (
<View style={{ flex: 1, paddingTop: 20 }}>
<Text>System error: Source wallet not found (this should never happen)</Text>
<BlueLoading />
</View>
);
}
@ -545,6 +570,7 @@ export default class SendDetails extends Component {
isLoading={this.state.isLoading}
amount={this.state.amount}
onChangeText={text => this.setState({ amount: text })}
inputAccessoryViewID={BlueDismissKeyboardInputAccessory.InputAccessoryViewID}
/>
<BlueAddressInput
onChangeText={text => {
@ -566,6 +592,7 @@ export default class SendDetails extends Component {
onBarScanned={this.processAddressData}
address={this.state.address}
isLoading={this.state.isLoading}
inputAccessoryViewID={BlueDismissKeyboardInputAccessory.InputAccessoryViewID}
/>
<View
hide={!this.state.showMemoRow}
@ -592,6 +619,7 @@ export default class SendDetails extends Component {
style={{ flex: 1, marginHorizontal: 8, minHeight: 33 }}
editable={!this.state.isLoading}
onSubmitEditing={Keyboard.dismiss}
inputAccessoryViewID={BlueDismissKeyboardInputAccessory.InputAccessoryViewID}
/>
</View>
<TouchableOpacity
@ -620,6 +648,7 @@ export default class SendDetails extends Component {
{this.renderFeeSelectionModal()}
</KeyboardAvoidingView>
</View>
<BlueDismissKeyboardInputAccessory />
{this.renderWalletSelectionButton()}
</View>
</TouchableWithoutFeedback>
@ -657,6 +686,7 @@ const styles = StyleSheet.create({
SendDetails.propTypes = {
navigation: PropTypes.shape({
pop: PropTypes.func,
goBack: PropTypes.func,
navigate: PropTypes.func,
getParam: PropTypes.func,
state: PropTypes.shape({

View file

@ -121,7 +121,7 @@ Success.propTypes = {
dismiss: PropTypes.func,
state: PropTypes.shape({
params: PropTypes.shape({
amount: PropTypes.string,
amount: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
fee: PropTypes.number,
}),
}),

View file

@ -19,11 +19,13 @@ export default class Language extends Component {
availableLanguages: [
{ label: 'English', value: 'en' },
{ label: 'Česky (CZ)', value: 'cs_cz' },
{ label: 'Chinese (ZH)', value: 'zh_cn' },
{ label: 'Croatian (HR)', value: 'hr_hr' },
{ label: 'Danish (DK)', value: 'da_dk' },
{ label: 'Deutsch (DE)', value: 'de_de' },
{ label: 'Español (ES)', value: 'es' },
{ label: 'Français (FR)', value: 'fr_fr' },
{ label: 'Indonesia (ID)', value: 'id_id' },
{ label: '日本語 (JP)', value: 'jp_jp' },
{ label: 'Nederlands (NL)', value: 'nl_nl' },
{ label: 'Portuguese (BR)', value: 'pt_br' },

View file

@ -1,9 +1,8 @@
import React, { Component } from 'react';
import { Dimensions, Platform, ActivityIndicator, View } from 'react-native';
import { QRCode as QRSlow } from 'react-native-custom-qr-codes';
import { Dimensions, ActivityIndicator, View } from 'react-native';
import QRCode from 'react-native-qrcode-svg';
import { BlueSpacing20, SafeBlueArea, BlueNavigationStyle, BlueText } from '../../BlueComponents';
import PropTypes from 'prop-types';
const QRFast = require('react-native-qrcode');
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
let loc = require('../../loc');
@ -81,27 +80,17 @@ export default class WalletExport extends Component {
<BlueSpacing20 />
{(() => {
if (this.state.showQr) {
if (Platform.OS === 'ios' || this.state.wallet.getSecret().length < 54) {
return (
<QRSlow
content={this.state.wallet.getSecret()}
size={this.state.qrCodeHeight}
color={BlueApp.settings.foregroundColor}
backgroundColor={BlueApp.settings.brandingColor}
<QRCode
value={this.state.wallet.getSecret()}
logo={require('../../img/qr-code.png')}
size={this.state.qrCodeHeight}
logoSize={90}
color={BlueApp.settings.foregroundColor}
logoBackgroundColor={BlueApp.settings.brandingColor}
ecl={'H'}
/>
);
} else {
return (
<QRFast
value={this.state.wallet.getSecret()}
size={this.state.qrCodeHeight}
fgColor={BlueApp.settings.brandingColor}
bgColor={BlueApp.settings.foregroundColor}
/>
);
}
} else {
return (
<View>

View file

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { View, TouchableOpacity, Text, FlatList, RefreshControl, ScrollView } from 'react-native';
import { BlueLoading, SafeBlueArea, WalletsCarousel, BlueList, BlueHeaderDefaultMain, BlueListTransactionItem } from '../../BlueComponents';
import { View, TouchableOpacity, Text, FlatList, InteractionManager, RefreshControl, ScrollView } from 'react-native';
import { BlueLoading, SafeBlueArea, WalletsCarousel, BlueList, BlueHeaderDefaultMain, BlueTransactionListItem } from '../../BlueComponents';
import { Icon } from 'react-native-elements';
import { NavigationEvents } from 'react-navigation';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
@ -33,18 +33,19 @@ export default class WalletsList extends Component {
super(props);
this.state = {
isLoading: true,
isFlatListRefreshControlHidden: true,
wallets: BlueApp.getWallets().concat(false),
lastSnappedTo: 0,
};
EV(EV.enum.WALLETS_COUNT_CHANGED, this.refreshFunction.bind(this));
EV(EV.enum.WALLETS_COUNT_CHANGED, this.redrawScreen.bind(this));
// here, when we receive TRANSACTIONS_COUNT_CHANGED we do not query
// remote server, we just redraw the screen
EV(EV.enum.TRANSACTIONS_COUNT_CHANGED, this.refreshFunction.bind(this));
EV(EV.enum.TRANSACTIONS_COUNT_CHANGED, this.redrawScreen.bind(this));
}
componentDidMount() {
this.refreshFunction();
this.redrawScreen();
}
/**
@ -52,28 +53,26 @@ export default class WalletsList extends Component {
* Triggered manually by user on pull-to-refresh.
*/
refreshTransactions() {
if (!(this.lastSnappedTo < BlueApp.getWallets().length)) {
if (!(this.lastSnappedTo < BlueApp.getWallets().length) && this.lastSnappedTo !== undefined) {
// last card, nop
console.log('last card, nop');
return;
}
this.setState(
{
isTransactionsLoading: true,
isFlatListRefreshControlHidden: true,
},
async function() {
let that = this;
setTimeout(async function() {
() => {
InteractionManager.runAfterInteractions(async () => {
// more responsive
let noErr = true;
try {
let balanceStart = +new Date();
await BlueApp.fetchWalletBalances(that.lastSnappedTo || 0);
await BlueApp.fetchWalletBalances(this.lastSnappedTo || 0);
let balanceEnd = +new Date();
console.log('fetch balance took', (balanceEnd - balanceStart) / 1000, 'sec');
let start = +new Date();
await BlueApp.fetchWalletTransactions(that.lastSnappedTo || 0);
await BlueApp.fetchWalletTransactions(this.lastSnappedTo || 0);
let end = +new Date();
console.log('fetch tx took', (end - start) / 1000, 'sec');
} catch (err) {
@ -82,24 +81,22 @@ export default class WalletsList extends Component {
}
if (noErr) await BlueApp.saveToDisk(); // caching
that.refreshFunction();
}, 1);
this.redrawScreen();
});
},
);
}
/**
* Redraws the screen
*/
refreshFunction() {
redrawScreen() {
console.log('wallets/list redrawScreen()');
if (BlueApp.getBalance() !== 0) {
A(A.ENUM.GOT_NONZERO_BALANCE);
}
this.setState({
isLoading: false,
isTransactionsLoading: false,
dataSource: BlueApp.getTransactions(),
isFlatListRefreshControlHidden: true,
dataSource: BlueApp.getTransactions(null, 10),
wallets: BlueApp.getWallets().concat(false),
});
}
@ -163,7 +160,7 @@ export default class WalletsList extends Component {
console.log('balance changed, thus txs too');
// balance changed, thus txs too
await wallets[index].fetchTransactions();
this.refreshFunction();
this.redrawScreen();
didRefresh = true;
} else if (wallets[index].timeToRefreshTransaction()) {
console.log(wallets[index].getLabel(), 'thinks its time to refresh TXs');
@ -174,7 +171,7 @@ export default class WalletsList extends Component {
if (wallets[index].fetchUserInvoices) {
await wallets[index].fetchUserInvoices();
}
this.refreshFunction();
this.redrawScreen();
didRefresh = true;
} else {
console.log('balance not changed');
@ -218,8 +215,9 @@ export default class WalletsList extends Component {
}
};
_renderItem = data => <BlueListTransactionItem item={data.item} itemPriceUnit={data.item.walletPreferredBalanceUnit} />;
_renderItem = data => {
return <BlueTransactionListItem item={data.item} itemPriceUnit={data.item.walletPreferredBalanceUnit} />;
};
render() {
if (this.state.isLoading) {
return <BlueLoading />;
@ -228,11 +226,13 @@ export default class WalletsList extends Component {
<SafeBlueArea style={{ flex: 1, backgroundColor: '#FFFFFF' }}>
<NavigationEvents
onWillFocus={() => {
this.refreshFunction();
this.redrawScreen();
}}
/>
<ScrollView
refreshControl={<RefreshControl onRefresh={() => this.refreshTransactions()} refreshing={this.state.isTransactionsLoading} />}
refreshControl={
<RefreshControl onRefresh={() => this.refreshTransactions()} refreshing={!this.state.isFlatListRefreshControlHidden} />
}
>
<BlueHeaderDefaultMain leftText={loc.wallets.list.title} onNewWalletPress={() => this.props.navigation.navigate('AddWallet')} />
<WalletsCarousel

View file

@ -205,7 +205,7 @@ export default class ScanQrWif extends React.Component {
alert(loc.wallets.scanQrWif.imported_wif + ret.data + loc.wallets.scanQrWif.with_address + newWallet.getAddress());
}
await BlueApp.saveToDisk();
this.props.navigation.dismiss();
this.props.navigation.popToTop();
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500);
} // end
@ -284,7 +284,6 @@ ScanQrWif.propTypes = {
navigation: PropTypes.shape({
goBack: PropTypes.func,
popToTop: PropTypes.func,
dismiss: PropTypes.func,
navigate: PropTypes.func,
}),
};

View file

@ -1,9 +1,19 @@
import React, { Component } from 'react';
import { Text, View, Image, FlatList, RefreshControl, TouchableOpacity, StatusBar } from 'react-native';
import {
Text,
View,
ActivityIndicator,
InteractionManager,
Image,
FlatList,
RefreshControl,
TouchableOpacity,
StatusBar,
} from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
import PropTypes from 'prop-types';
import { NavigationEvents } from 'react-navigation';
import { BlueText, ManageFundsBigButton, BlueSendButtonIcon, BlueReceiveButtonIcon, BlueTransactionListItem } from '../../BlueComponents';
import { BlueSendButtonIcon, BlueReceiveButtonIcon, BlueTransactionListItem } from '../../BlueComponents';
import { Icon } from 'react-native-elements';
import { BitcoinUnit } from '../../models/bitcoinUnits';
import { LightningCustodianWallet } from '../../class';
@ -18,14 +28,14 @@ export default class WalletTransactions extends Component {
return {
headerRight: (
<TouchableOpacity
style={{ marginHorizontal: 8, minWidth: 150 }}
style={{ marginHorizontal: 16, minWidth: 150, justifyContent: 'center', alignItems: 'flex-end' }}
onPress={() =>
navigation.navigate('WalletDetails', {
wallet: navigation.state.params.wallet,
})
}
>
<Text style={{ color: '#fff', fontSize: 20, fontWeight: '500', textAlign: 'right' }}>{loc.wallets.options}</Text>
<Icon name="kebab-horizontal" size={22} type="octicon" color="#FFFFFF" />
</TouchableOpacity>
),
headerStyle: {
@ -47,15 +57,17 @@ export default class WalletTransactions extends Component {
this.props.navigation.setParams({ wallet: wallet });
this.state = {
isLoading: true,
isTransactionsLoading: false,
showShowFlatListRefreshControl: false,
wallet: wallet,
dataSource: wallet.getTransactions(),
dataSource: this.getTransactions(15),
limit: 15,
pageSize: 20,
walletPreviousPreferredUnit: wallet.getPreferredBalanceUnit(),
};
}
componentDidMount() {
this.refreshFunction();
// nop
}
/**
@ -69,28 +81,14 @@ export default class WalletTransactions extends Component {
}
/**
* Redraws the screen
* Simple wrapper for `wallet.getTransactions()`, where `wallet` is current wallet.
* Sorts. Provides limiting.
*
* @param limit {Integer} How many txs return, starting from the earliest. Default: all of them.
* @returns {Array}
*/
refreshFunction() {
setTimeout(() => {
console.log('wallets/transactions refreshFunction()');
let showSend = false;
let showReceive = false;
const wallet = this.state.wallet;
if (wallet) {
showSend = wallet.allowSend();
showReceive = wallet.allowReceive();
}
let showManageFundsBigButton = false;
let showManageFundsSmallButton = false;
if (wallet && wallet.type === LightningCustodianWallet.type && wallet.getBalance() * 1 <= 0) {
showManageFundsBigButton = true;
showManageFundsSmallButton = false;
} else if (wallet && wallet.type === LightningCustodianWallet.type && wallet.getBalance() > 0) {
showManageFundsSmallButton = true;
showManageFundsBigButton = false;
}
getTransactions(limit = Infinity) {
let wallet = this.props.navigation.getParam('wallet');
let txs = wallet.getTransactions();
for (let tx of txs) {
tx.sort_ts = +new Date(tx.received);
@ -98,17 +96,19 @@ export default class WalletTransactions extends Component {
txs = txs.sort(function(a, b) {
return b.sort_ts - a.sort_ts;
});
return txs.slice(0, limit);
}
redrawScreen() {
InteractionManager.runAfterInteractions(async () => {
console.log('wallets/transactions redrawScreen()');
this.setState({
isLoading: false,
isTransactionsLoading: false,
showReceiveButton: showReceive,
showSendButton: showSend,
showManageFundsBigButton,
showManageFundsSmallButton,
dataSource: txs,
showShowFlatListRefreshControl: false,
dataSource: this.getTransactions(this.state.limit),
});
});
}, 1);
}
isLightning() {
@ -124,19 +124,18 @@ export default class WalletTransactions extends Component {
* Forcefully fetches TXs and balance for wallet
*/
refreshTransactions() {
if (this.state.isLoading) return;
this.setState(
{
isTransactionsLoading: true,
showShowFlatListRefreshControl: true,
isLoading: true,
},
async function() {
let that = this;
setTimeout(async function() {
// more responsive
async () => {
let noErr = true;
let smthChanged = false;
try {
/** @type {LegacyWallet} */
let wallet = that.state.wallet;
let wallet = this.state.wallet;
let balanceStart = +new Date();
const oldBalance = wallet.getBalance();
await wallet.fetchBalance();
@ -158,15 +157,17 @@ export default class WalletTransactions extends Component {
} catch (err) {
noErr = false;
console.warn(err);
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
}
that.refreshFunction(); // Redraws the screen
}, 1);
this.redrawScreen();
},
);
}
@ -193,7 +194,10 @@ export default class WalletTransactions extends Component {
renderWalletHeader = () => {
return (
<LinearGradient colors={WalletGradient.gradientsFor(this.state.wallet.type)} style={{ padding: 15, minHeight: 164 }}>
<LinearGradient
colors={WalletGradient.gradientsFor(this.state.wallet.type)}
style={{ padding: 15, minHeight: 140, justifyContent: 'center' }}
>
<Image
source={
(LightningCustodianWallet.type === this.state.wallet.type && require('../../img/lnd-shape.png')) ||
@ -208,7 +212,6 @@ export default class WalletTransactions extends Component {
}}
/>
<Text style={{ backgroundColor: 'transparent' }} />
<Text
numberOfLines={1}
style={{
@ -233,34 +236,45 @@ export default class WalletTransactions extends Component {
{loc.formatBalance(this.state.wallet.getBalance(), this.state.wallet.getPreferredBalanceUnit(), true).toString()}
</Text>
</TouchableOpacity>
<Text style={{ backgroundColor: 'transparent' }} />
<Text
numberOfLines={1}
{this.state.wallet.type === LightningCustodianWallet.type && (
<TouchableOpacity onPress={() => this.props.navigation.navigate('ManageFunds', { fromWallet: this.state.wallet })}>
<View
style={{
backgroundColor: 'transparent',
fontSize: 13,
color: '#fff',
marginTop: 14,
marginBottom: 10,
backgroundColor: 'rgba(255,255,255,0.2)',
borderRadius: 9,
minWidth: 119,
minHeight: 39,
width: 119,
height: 39,
justifyContent: 'center',
alignItems: 'center',
}}
>
{loc.wallets.list.latest_transaction}
</Text>
<Text
numberOfLines={1}
style={{
backgroundColor: 'transparent',
fontWeight: 'bold',
fontSize: 16,
color: '#fff',
fontWeight: '500',
fontSize: 14,
color: '#FFFFFF',
}}
>
{loc.transactionTimeToReadable(this.state.wallet.getLatestTransactionTime())}
{loc.lnd.title}
</Text>
</View>
</TouchableOpacity>
)}
</LinearGradient>
);
};
_keyExtractor = (_item, index) => index.toString();
renderListFooterComponent = () => {
// if not all txs rendered - display indicator
return (this.getTransactions(Infinity).length > this.state.limit && <ActivityIndicator />) || <View />;
};
renderListHeaderComponent = () => {
return (
<View style={{ flexDirection: 'row', height: 50 }}>
@ -299,55 +313,57 @@ export default class WalletTransactions extends Component {
<NavigationEvents
onWillFocus={() => {
StatusBar.setBarStyle('light-content');
this.refreshFunction();
this.redrawScreen();
}}
onWillBlur={() => this.onWillBlur()}
/>
{this.renderWalletHeader()}
<View style={{ flex: 1, backgroundColor: '#FFFFFF' }}>
{(() => {
if (this.state.showManageFundsSmallButton) {
return (
<View style={{ justifyContent: 'space-between', alignContent: 'center', flexDirection: 'row', marginVertical: 8 }}>
{this.state.wallet.type === LightningCustodianWallet.type && (
<TouchableOpacity
style={{ left: 10, flexDirection: 'row', flex: 1, alignItems: 'center' }}
onPress={() => {
console.log('navigating to LappBrowser');
navigate('LappBrowser', { fromSecret: this.state.wallet.getSecret(), fromWallet: this.state.wallet });
}}
>
<BlueText style={{ fontWeight: '600', fontSize: 16 }}>marketplace</BlueText>
<Icon
name="shopping-cart"
type="font-awesome"
size={14}
color={BlueApp.settings.foregroundColor}
iconStyle={{ left: 5, top: 2 }}
/>
</TouchableOpacity>
<TouchableOpacity
style={{ marginRight: 10, flexDirection: 'row', alignItems: 'center' }}
onPress={() => {
console.log('navigating to', this.state.wallet.getLabel());
navigate('ManageFunds', { fromWallet: this.state.wallet });
<View
style={{
marginVertical: 16,
backgroundColor: '#f2f2f2',
borderRadius: 9,
minWidth: 343,
minHeight: 49,
width: 343,
height: 49,
justifyContent: 'center',
alignItems: 'center',
alignSelf: 'center',
}}
>
<BlueText style={{ fontWeight: '600', fontSize: 16 }}>{loc.lnd.title}</BlueText>
<Icon
name="link"
type="font-awesome"
size={14}
color={BlueApp.settings.foregroundColor}
iconStyle={{ left: 5, top: 2, transform: [{ rotate: '90deg' }] }}
/>
</TouchableOpacity>
<Text>marketplace</Text>
</View>
);
}
})()}
</TouchableOpacity>
)}
<FlatList
onEndReachedThreshold={0.3}
onEndReached={() => {
// 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,
});
}}
ListHeaderComponent={this.renderListHeaderComponent}
ListFooterComponent={this.renderListFooterComponent}
ListEmptyComponent={
<View style={{ top: 50, minHeight: 200, paddingHorizontal: 16 }}>
<Text
@ -396,10 +412,11 @@ export default class WalletTransactions extends Component {
)}
</View>
}
refreshControl={<RefreshControl onRefresh={() => this.refreshTransactions()} refreshing={this.state.isTransactionsLoading} />}
refreshControl={
<RefreshControl onRefresh={() => this.refreshTransactions()} refreshing={this.state.showShowFlatListRefreshControl} />
}
data={this.state.dataSource}
keyExtractor={this._keyExtractor}
initialNumToRender={10}
renderItem={this.renderItem}
/>
</View>
@ -417,11 +434,11 @@ export default class WalletTransactions extends Component {
}}
>
{(() => {
if (this.state.showReceiveButton) {
if (this.state.wallet.allowReceive()) {
return (
<BlueReceiveButtonIcon
onPress={() => {
if (this.state.wallet.type === new LightningCustodianWallet().type) {
if (this.state.wallet.type === LightningCustodianWallet.type) {
navigate('LNDCreateInvoice', { fromWallet: this.state.wallet });
} else {
navigate('ReceiveDetails', { address: this.state.wallet.getAddress(), secret: this.state.wallet.getSecret() });
@ -433,7 +450,7 @@ export default class WalletTransactions extends Component {
})()}
{(() => {
if (this.state.showSendButton) {
if (this.state.wallet.allowSend()) {
return (
<BlueSendButtonIcon
onPress={() => {
@ -447,19 +464,6 @@ export default class WalletTransactions extends Component {
);
}
})()}
{(() => {
if (this.state.showManageFundsBigButton) {
return (
<ManageFundsBigButton
onPress={() => {
console.log('navigating to', this.state.wallet.getLabel());
navigate('ManageFunds', { fromWallet: this.state.wallet });
}}
/>
);
}
})()}
</View>
</View>
);

View file

@ -1,9 +1,8 @@
import React, { Component } from 'react';
import { Dimensions, Platform, ActivityIndicator, View } from 'react-native';
import { QRCode as QRSlow } from 'react-native-custom-qr-codes';
import { Dimensions, ActivityIndicator, View } from 'react-native';
import QRCode from 'react-native-qrcode-svg';
import { BlueSpacing20, SafeBlueArea, BlueText, BlueNavigationStyle, BlueCopyTextToClipboard } from '../../BlueComponents';
import PropTypes from 'prop-types';
const QRFast = require('react-native-qrcode');
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
let loc = require('../../loc');
@ -73,27 +72,17 @@ export default class WalletXpub extends Component {
{(() => {
if (this.state.showQr) {
if (Platform.OS === 'ios' || this.state.xpub.length < 54) {
return (
<QRSlow
content={this.state.xpub}
color={BlueApp.settings.foregroundColor}
backgroundColor={BlueApp.settings.brandingColor}
<QRCode
value={this.state.xpub}
logo={require('../../img/qr-code.png')}
size={this.state.qrCodeHeight}
logoSize={90}
color={BlueApp.settings.foregroundColor}
logoBackgroundColor={BlueApp.settings.brandingColor}
ecl={'H'}
/>
);
} else {
return (
<QRFast
value={this.state.xpub}
fgColor={BlueApp.settings.brandingColor}
bgColor={BlueApp.settings.foregroundColor}
size={this.state.qrCodeHeight}
/>
);
}
} else {
return (
<View>