Merge branch 'master' into rtl

This commit is contained in:
marcosrdz 2021-03-22 16:39:21 -04:00
commit b520e70c2b
59 changed files with 416 additions and 402 deletions

View File

@ -578,8 +578,10 @@ const styleCopyTextToClipboard = StyleSheet.create({
});
export const SafeBlueArea = props => {
const { style, ...nonStyleProps } = props;
const { colors } = useTheme();
return <SafeAreaView forceInset={{ horizontal: 'always' }} style={{ flex: 1, backgroundColor: colors.background }} {...props} />;
const baseStyle = { flex: 1, backgroundColor: colors.background }
return <SafeAreaView forceInset={{ horizontal: 'always' }} style={[baseStyle, style]} {...nonStyleProps} />;
};
export const BlueCard = props => {

View File

@ -2,6 +2,7 @@ import React, { useContext, useEffect, useState } from 'react';
import { View, Image, TouchableOpacity, StyleSheet, StatusBar, ActivityIndicator, useColorScheme } from 'react-native';
import { Icon } from 'react-native-elements';
import Biometric from './class/biometrics';
import LottieView from 'lottie-react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { StackActions, useNavigation, useRoute } from '@react-navigation/native';
import { BlueStorageContext } from './blue_modules/storage-context';
@ -13,21 +14,12 @@ const styles = StyleSheet.create({
flex: 1,
},
container: {
flex: 2,
flex: 1,
justifyContent: 'space-between',
alignItems: 'center',
},
qrCode: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
qrCodeImage: {
width: 120,
height: 120,
},
biometric: {
flex: 0.2,
flex: 1,
justifyContent: 'flex-end',
marginBottom: 58,
},
@ -141,9 +133,7 @@ const UnlockWith = () => {
<SafeAreaView style={styles.root}>
<StatusBar barStyle="default" />
<View style={styles.container}>
<View style={styles.qrCode}>
<Image source={require('./img/qr-code.png')} style={styles.qrCodeImage} />
</View>
<LottieView source={require('./img/bluewalletsplash.json')} progress={1} loop={false} />
<View style={styles.biometric}>
<View style={styles.biometricRow}>{renderUnlockOptions()}</View>
</View>

View File

@ -1,4 +1,4 @@
import { AppStorage, LightningCustodianWallet } from './';
import { AppStorage, LightningCustodianWallet, WatchOnlyWallet } from './';
import AsyncStorage from '@react-native-async-storage/async-storage';
import RNFS from 'react-native-fs';
import url from 'url';
@ -178,6 +178,17 @@ class DeeplinkSchemaMatch {
params: Azteco.getParamsFromUrl(event.url),
},
]);
} else if (new WatchOnlyWallet().setSecret(event.url).init().valid()) {
completionHandler([
'AddWalletRoot',
{
screen: 'ImportWallet',
params: {
triggerImport: true,
label: event.url,
},
},
]);
} else {
const urlObject = url.parse(event.url, true); // eslint-disable-line node/no-deprecated-api
(async () => {

View File

@ -56,13 +56,15 @@ export class WatchOnlyWallet extends LegacyWallet {
* this method creates appropriate HD wallet class, depending on whether we have xpub, ypub or zpub
* as a property of `this`, and in case such property exists - it recreates it and copies data from old one.
* this is needed after serialization/save/load/deserialization procedure.
*
* @return {WatchOnlyWallet} this
*/
init() {
let hdWalletInstance;
if (this.secret.startsWith('xpub')) hdWalletInstance = new HDLegacyP2PKHWallet();
else if (this.secret.startsWith('ypub')) hdWalletInstance = new HDSegwitP2SHWallet();
else if (this.secret.startsWith('zpub')) hdWalletInstance = new HDSegwitBech32Wallet();
else return;
else return this;
hdWalletInstance._xpub = this.secret;
if (this._hdWalletInstance) {
// now, porting all properties from old object to new one
@ -75,6 +77,8 @@ export class WatchOnlyWallet extends LegacyWallet {
delete hdWalletInstance._node0;
}
this._hdWalletInstance = hdWalletInstance;
return this;
}
prepareForSerialization() {

View File

@ -4,13 +4,14 @@
*
* @param navigateFunc {function}
* @param currentScreenName {string}
* @param showFileImportButton {boolean}
*
* @return {Promise<string>}
*/
module.exports = function (navigateFunc, currentScreenName) {
module.exports = function scanQrHelper(navigateFunc, currentScreenName, showFileImportButton = true) {
return new Promise(resolve => {
const params = {};
params.showFileImportButton = true;
params.showFileImportButton = !!showFileImportButton;
params.onBarScanned = function (data) {
setTimeout(() => resolve(data.data || data), 1);

View File

@ -174,7 +174,6 @@
"details_address_field_is_not_valid": "The address is not valid.",
"details_adv_fee_bump": "Allow Fee Bump",
"details_adv_full": "Use Full Balance",
"details_adv_full_remove": "Your other recipients will be removed from this transaction.",
"details_adv_full_sure": "Are you sure you want to use your wallets full balance for this transaction?",
"details_adv_import": "Import Transaction",
"details_amount_field_is_not_valid": "The amount is not valid.",

View File

@ -62,7 +62,7 @@
"cont_title": "Sopimukseni",
"filter_any": "Mikä tahansa",
"filter_buying": "Ostaminen",
"filter_country_global": "Maailmanlaajuiset tarjoukset",
"filter_country_global": "Globaalit tarjoukset",
"filter_currency": "Valuutta",
"filter_detail": "Tiedot",
"filter_filters": "Suodattimet",
@ -75,7 +75,7 @@
"item_nooffers": "Ei tarjouksia. Yritä muuttaa \"Lähellä minua\" kansainvälisiksi tarjouksiksi!",
"item_rating": "{rating} vaihdot",
"item_rating_no": "Ei luokitusta",
"local_trader": "Paikallinen kauppias",
"local_trader": "Osta välittäjältä",
"local_trader_new": "Uusi",
"login": "Kirjaudu sisään",
"logout": "kirjaudu ulos",
@ -116,7 +116,7 @@
"open_direct_channel": "Avaa suora kanava tällä solmulla:",
"please_pay": "Ole hyvä ja maksa",
"preimage": "Alkukuva",
"sats": "sats",
"sats": "sattia",
"wasnt_paid_and_expired": "Tätä laskua ei maksettu, ja se on vanhentunut."
},
"plausibledeniability": {
@ -166,19 +166,18 @@
"create_satoshi_per_byte": "Satoshia per tavu",
"create_this_is_hex": "Tämä on siirtotapahtuman hex, allekirjoitettu ja valmis lähetettävksi verkkoon.",
"create_to": "Vastaanottaja",
"create_tx_size": "TX koko",
"create_tx_size": "Siirtotapahtuman koko",
"create_verify": "Varmenna coinb.in :ssä",
"details_add_rec_add": "Lisää Vastaanottaja",
"details_add_rec_rem": "Poista Vastaanottaja",
"details_address": "osoite",
"details_address_field_is_not_valid": "Osoite ei kelpaa",
"details_adv_fee_bump": "Salli Siirtokulun Nosto",
"details_adv_full": "Käytä Koko Saldo",
"details_adv_full_remove": "Muut vastaanottajat poistetaan tästä siirtotapahtumasta.",
"details_adv_full": "Käytä koko saldo",
"details_adv_full_sure": "Haluatko varmasti käyttää lompakon koko saldon tähän siirtotapahtumaan?",
"details_adv_import": "Tuo Siirtotapahtuma",
"details_amount_field_is_not_valid": "Määrä ei kelpaa",
"details_amount_field_is_less_than_minimum_amount_sat": "Määritetty määrä on liian pieni. Anna summa, joka on yli 500 sats. ",
"details_amount_field_is_less_than_minimum_amount_sat": "Määritetty määrä on liian pieni. Anna summa, joka on yli 500 sattia. ",
"details_create": "Luo Lasku",
"details_error_decode": "Bitcoin-osoitetta ei voida dekoodata ",
"details_fee_field_is_not_valid": "Siirtokulukenttä ei ole pätevä",
@ -233,7 +232,7 @@
},
"settings": {
"about": "Tietoa",
"about_awesome": "Mahtavasti rakennettu",
"about_awesome": "Loistavasti rakennettu",
"about_backup": "Varmuuskopioi aina avaimesi!",
"about_free": "BlueWallet on ilmainen ja avoimen lähdekoodin projekti. Bitcoin käyttäjien tekemä.",
"about_license": "MIT-lisenssi",
@ -288,8 +287,8 @@
"encrypt_use": "Käytä {type}",
"encrypt_use_expl": "{type} käytetään henkilöllisyytesi vahvistamiseen ennen siirtotapahtuman tekemistä, lompakon lukituksen avaamista, vientiä tai poistamista. {type} ei käytetä salatun tallennustilan lukituksen avaamiseen.",
"general": "Yleinen",
"general_adv_mode": "Kehittynyt tila",
"general_adv_mode_e": "Kun tämä asetus on käytössä, näet lisäasetukset, kuten erilaiset lompakkotyypit, kyvyn määrittää LNDHub-instanssi, johon haluat muodostaa yhteyden, ja mukautetun entropian lompakon luomisen aikana.",
"general_adv_mode": "Lisäasetukset",
"general_adv_mode_e": "Kun tämä asetus on käytössä, näet lisäasetukset, kuten erilaiset lompakkotyypit, kyvyn määrittää LNDHub-instanssi, johon haluat muodostaa yhteyden ja mukautetun entropian lompakon luomisen aikana.",
"general_continuity": "Jatkuvuus",
"general_continuity_e": "Kun tämä asetus on käytössä, voit tarkastella valittuja lompakoita ja siirtotapahtumia muilla Apple iCloud -laitteilla.",
"groundcontrol_explanation": "GroundControl on ilmainen avoimen lähdekoodin push-ilmoituspalvelin bitcoin-lompakoille. Voit asentaa oman GroundControl-palvelimen ja laittaa sen URL-osoitteen tähän, jotta et luota BlueWallet-infrastruktuuriin. Jätä tyhjäksi käyttääksesi oletusasetusta",
@ -430,10 +429,10 @@
"list_create_a_wallet": "Lisää lompakko",
"list_create_a_wallet_text": "Se on ilmainen ja voit luoda\nniin monta kuin haluat",
"list_empty_txs1": "Siirtotapahtumasi näkyvät tässä,",
"list_empty_txs1_lightning": "Lightning-lompakkoa tulisi käyttää päivittäisiin siirtotapahtumiin. Siirtokulut ovat kohtuuttoman halpoja ja nopeus on liekehtivän kova.",
"list_empty_txs1_lightning": "Lightning-lompakkoa voit käyttää päivittäisiin siirtoihin. Siirtokulut ovat kohtuuttoman halvat ja se toimii todella nopeasti.",
"list_empty_txs2": "Aloita lompakostasi. ",
"list_empty_txs2_lightning": "Aloita sen käyttäminen napsauttamalla \"hallinnoi varoja\" ja lisää saldoasi.\n",
"list_header": "Lompakko edustaa avainparia, yhtä yksityistä ja yhtä, jonka voit jakaa vastaanottaaksesi kolikoita.",
"list_empty_txs2_lightning": "Aloita lompakon käyttäminen napsauttamalla \"hallinnoi varoja\" ja lisää saldoasi.\n",
"list_header": "Lompakko edustaa avainparia, yhtä yksityistä ja yhtä, jonka voit jakaa vastaanottaaksesi varoja.",
"list_import_error": "Tämän lompakon tuomisessa tapahtui virhe.",
"list_import_problem": "Tämän lompakon tuomisessa oli ongelma",
"list_latest_transaction": "viimeisin siirto",
@ -462,11 +461,11 @@
"xpub_title": "lompakon XPUB"
},
"multisig": {
"multisig_vault": "Holvi",
"multisig_vault": "Vault",
"default_label": "Multisig Vault",
"multisig_vault_explain": "Paras turvallisuus suurille summille",
"provide_signature": "Toimita allekirjoitus",
"vault_key": "Holvi avain {number}",
"vault_key": "Vault-avain {number}",
"required_keys_out_of_total": "Vaaditut avaimet kokonaismäärästä",
"fee": "Siirtokulu: {number}",
"fee_btc": "{number} BTC",
@ -488,12 +487,12 @@
"wrapped_segwit_title": "Paras yhteensopivuus",
"legacy_title": "Perintö",
"co_sign_transaction": "Allekirjoita siirtotapahtuma",
"what_is_vault": "Holvi on a",
"what_is_vault": "Vault on",
"what_is_vault_numberOfWallets": "{m} {n} multisig",
"what_is_vault_wallet": "lompakko",
"vault_advanced_customize": "Holvin Asetukset...",
"vault_advanced_customize": "Vault-asetukset...",
"needs": "Tarpeet",
"what_is_vault_description_number_of_vault_keys": "{m} holvin avaimet",
"what_is_vault_description_number_of_vault_keys": "{m} vault-avaimet",
"what_is_vault_description_to_spend": "kuluttaa ja kolmas sinulle\nvoidaan käyttää varmuuskopiona.",
"what_is_vault_description_to_spend_other": "kuluttaa.",
"quorum": "{m} {n} quorum",
@ -510,9 +509,9 @@
"i_have_mnemonics": "Minulla on siemen tälle avaimelle...",
"please_write_down_mnemonics": "Kirjoita tämä muistilauseke paperille. Älä huoli, voit kirjoittaa sen myöhemmin.",
"i_wrote_it_down": "Ok, kirjoitin sen ylös",
"type_your_mnemonics": "Lisää siemen tuodaksesi nykyisen holviavaimesi",
"type_your_mnemonics": "Lisää siemen tuodaksesi Vault-avaimesi",
"this_is_cosigners_xpub": "Tämä on allekirjoittajan xpub, joka on valmis tuotavaksi toiseen lompakkoon. On turvallista jakaa se.",
"wallet_key_created": "Holviavaimesi luotiin. Käytä hetki muistisiemenesi turvalliseen varmuuskopioimiseen",
"wallet_key_created": "Vault-avaimesi luotiin. Käytä hetki muistisanojen turvalliseen varmuuskopioimiseen",
"are_you_sure_seed_will_be_lost": "Oletko varma? Muistisiemenesi menetetään, jos sinulla ei ole varmuuskopiota",
"forget_this_seed": "Unohda tämä siemen ja käytä XPUB:ia",
"invalid_fingerprint": "Tämän siemenen sormenjälki ei vastaa tämän allekirjoittajan sormenjälkeä",
@ -524,18 +523,18 @@
"input_path": "Syöte johtamisen polku",
"input_path_explain": "ohita käyttääksesi oletusarvoa ({default})",
"ms_help": "Apu",
"ms_help_title": "Kuinka Multisig Vaults toimii. Vinkkejä",
"ms_help_title": "Kuinka Multisig Vault toimii. Vinkit ja neuvot.",
"ms_help_text": "Lompakko, jossa on useita avaimia, turvallisuuden eksponentiaaliseen parantamiseen tai jaettuun säilöön.",
"ms_help_title1": "Useita laitteita suositellaan",
"ms_help_1": "Vault toimii muiden BlueWallet-sovellusten ja PSBT-yhteensopivien lompakoiden kanssa, kuten Electrum, Spectre, Coldcard, Cobo vault jne.",
"ms_help_title2": "Avainten Muokkaus",
"ms_help_2": "Voit luoda kaikki Vault-avaimet tälle laitteelle ja poistaa tai muokata niitä myöhemmin. Pitämällä kaikki avaimet samalla laitteella on vastaavanlainen suojaus kuin tavallisella Bitcoin-lompakolla.",
"ms_help_title3": "Vault Varmuuskopiot",
"ms_help_title3": "Vault-varmuuskopiot",
"ms_help_3": "Lompakon vaihtoehdoista löydät Vault-varmuuskopion ja vain-lukuoikeus varmuuskopion. Tämä varmuuskopio on kuin kartta lompakkoosi. Se on välttämätöntä lompakon palauttamiseksi, jos menetät yhden siemenistäsi.",
"ms_help_title4": "Tuodaan Vaults",
"ms_help_4": "Voit tuoda Multisig:in käyttämällä multisig-varmuuskopiotiedostoa ja käyttämällä tuontiominaisuutta. Jos sinulla on vain laajennettuja avaimia ja siemeniä, voit käyttää yksittäisiä tuontikenttiä Lisää Vault -kulkuun.",
"ms_help_title4": "Tuodaan Vault:ia",
"ms_help_4": "Voit tuoda multisig:in käyttämällä varmuuskopiotiedostoasi ja Tuo-ominaisuutta. Jos sinulla on vain laajennettuja avaimia ja siemensanoja, voit käyttää yksittäistä Tuo-näppäintä, kun luot Vault -avaimia.",
"ms_help_title5": "Lisäasetukset",
"ms_help_5": "Oletuksena BlueWallet luo 2/3 Vaultin. Jos haluat luoda erilaisen päätösvallan tai muuttaa osoitetyyppiä, aktivoi lisäasetukset Asetuksissa."
"ms_help_5": "Oletuksena BlueWallet luo 2/3 Vaultin. Jos haluat luoda erilaisen päätösvallan tai muuttaa osoitetyyppiä, aktivoi Lisäasetukset Asetuksista."
},
"is_it_my_address": {
"title": "Onko se osoitteeni?",
@ -560,6 +559,6 @@
"BTC": "BTC",
"MAX": "MAX",
"sat_byte": "sat/bitti",
"sats": "sats"
"sats": "sattia"
}
}

58
package-lock.json generated
View File

@ -5744,11 +5744,11 @@
"integrity": "sha512-8IeHfDwJ9/CTUwFs6x90VlobV3BfuPgNLjTgC6dRZovfCWigaZwVNIFFJnHBakK3pW2xErAPwhdvNR4JeNoYbw=="
},
"@react-navigation/core": {
"version": "5.15.1",
"resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-5.15.1.tgz",
"integrity": "sha512-GDCpIVQd0NgHYCSdUMY69hrpeWKuYgj5SIRqHI2sYh9OguwGcV52ZZOafc+pQuyfuiLLIMidw34jiqb47QrlhA==",
"version": "5.15.2",
"resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-5.15.2.tgz",
"integrity": "sha512-jNSP0FMu1N6Pa1Slsy8b/JbmlTAXcVeXVwnxrEMVGWeiNqUVYl+tx1FuQAqi3q1m4cg9ygXkGsgLgRmnXAEC8g==",
"requires": {
"@react-navigation/routers": "^5.7.1",
"@react-navigation/routers": "^5.7.2",
"escape-string-regexp": "^4.0.0",
"nanoid": "^3.1.15",
"query-string": "^6.13.6",
@ -5792,11 +5792,11 @@
}
},
"@react-navigation/native": {
"version": "5.9.2",
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-5.9.2.tgz",
"integrity": "sha512-O8K+Lr6Vy25gTTyXAns9BVyFvwTkKqfFH0RpOimilYndUL6tlhV56oDSp7Hryjy8xsjx6ESWqr6eIu4sS3Z9nQ==",
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-5.9.3.tgz",
"integrity": "sha512-xaRlCDRVuFGxHsP/IetwLdNvLJwIJBYCUIx/ufWs6QkT9Q0EB0DtKzXCItuHydjMEVPd1Cy7lfjUlSM6hZ6Q3Q==",
"requires": {
"@react-navigation/core": "^5.15.1",
"@react-navigation/core": "^5.15.2",
"escape-string-regexp": "^4.0.0",
"nanoid": "^3.1.15"
},
@ -5809,9 +5809,9 @@
}
},
"@react-navigation/routers": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-5.7.1.tgz",
"integrity": "sha512-M5R4AFgJZ0uBUV+DjMyNy2HXRfvo0ldM+59Gj1NQWXaYnst3m0xJTfWiln94mnrbrHEq087gMP4ZLHGIJ8D1Ig==",
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-5.7.2.tgz",
"integrity": "sha512-BxNSMLHpU+oS37Xok0ql6rc9U7IC8aUD4+U5ZPbjDJ0pwzZxGGh0YOEBzfV4k/Ig3cbPdvVWbc1C9HHbCVr2oQ==",
"requires": {
"nanoid": "^3.1.15"
}
@ -16767,9 +16767,9 @@
"integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw=="
},
"nanoid": {
"version": "3.1.20",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz",
"integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw=="
"version": "3.1.22",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz",
"integrity": "sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ=="
},
"nanomatch": {
"version": "1.2.13",
@ -17889,9 +17889,9 @@
"integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ=="
},
"query-string": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.0.tgz",
"integrity": "sha512-In3o+lUxlgejoVJgwEdYtdxrmlL0cQWJXj0+kkI7RWVo7hg5AhFtybeKlC9Dpgbr8eOC4ydpEh8017WwyfzqVQ==",
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz",
"integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==",
"requires": {
"decode-uri-component": "^0.2.0",
"filter-obj": "^1.1.0",
@ -18422,9 +18422,9 @@
"integrity": "sha512-sQDYwGEdxwKwXKP/8Intc81FyH33Rv8ZvOxdmPX4NM75RAIVeBc13pdabEqycAimNZoY5IDvGp4o1cTTa5gNrA=="
},
"react-native-device-info": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/react-native-device-info/-/react-native-device-info-8.0.1.tgz",
"integrity": "sha512-5fIcrEfxsyIJ0HZ/pHd+DeYwC81wi5tupFkPSASYXz/7QhatF8W0W6qR+YlvI6gJVSFNgQKgrPrh18RGMgbZdg=="
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/react-native-device-info/-/react-native-device-info-8.0.2.tgz",
"integrity": "sha512-5X5sMO2N9WpnqA4BXHlby0ml0WKlHconLhgf8fCiXDELf7DIjOMX8z6iYZ7WDgmn0E/6Psubq9yb5D2Qdc30Wg=="
},
"react-native-document-picker": {
"version": "git+ssh://git@github.com/BlueWallet/react-native-document-picker.git#3684d4fcc2bc0b47c32be39024e4796004c3e428",
@ -18467,9 +18467,9 @@
}
},
"react-native-gesture-handler": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.10.2.tgz",
"integrity": "sha512-j9ifSk2KX/3Lg7Yiku9URJEAJH0NZLjhE52eGHG0QZQ/oKnfcQrJY1DjHefrcNwNAjvoQp+IWkBdyoGlcT2GqA==",
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.10.3.tgz",
"integrity": "sha512-cBGMi1IEsIVMgoox4RvMx7V2r6bNKw0uR1Mu1o7NbuHS6BRSVLq0dP34l2ecnPlC+jpWd3le6Yg1nrdCjby2Mw==",
"requires": {
"@egjs/hammerjs": "^2.0.17",
"fbjs": "^3.0.0",
@ -18670,18 +18670,18 @@
}
},
"react-native-screens": {
"version": "2.17.1",
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-2.17.1.tgz",
"integrity": "sha512-B4gD5e4csvlVwlhf+RNqjQZ9mHTwe/iL3rXondgZxnKz4oW0QAmtLnLRKOrYVxoaJaF9Fy7jhjo//24/472APQ=="
"version": "2.18.0",
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-2.18.0.tgz",
"integrity": "sha512-8+lCEsxzSu55GWRw6yZpyt3OszxN1OngfBsFXdqspaEfq6uIChanzlcD2PLVQl+iN82GAcrZM800Kd1pA477ZQ=="
},
"react-native-secure-key-store": {
"version": "git+ssh://git@github.com/BlueWallet/react-native-secure-key-store.git#4828fd1a67d12e4c0e21eee0bee673fde75e6f9a",
"from": "react-native-secure-key-store@git+https://github.com/BlueWallet/react-native-secure-key-store.git#4828fd1a67d12e4c0e21eee0bee673fde75e6f9a"
},
"react-native-share": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/react-native-share/-/react-native-share-5.1.3.tgz",
"integrity": "sha512-d61vVz3B3qjltnC9QBOdvbOvEEIogpYtZ5LeGJ/f+Kqeu70DUoilVJ80MMzqKBcfPlJM6GPq3YyyF0qLNJ0G3w=="
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/react-native-share/-/react-native-share-5.1.4.tgz",
"integrity": "sha512-/eNBTuimOmzeeBbnZkwW6hbzSZvJnhfkx5cSdQkFS45KRHekD9twP6F8QHn+zJxeJGAP+EgpVCXoA7yCrgKrhQ=="
},
"react-native-snap-carousel": {
"version": "3.9.1",

View File

@ -78,7 +78,7 @@
"@react-native-community/push-notification-ios": "1.8.0",
"@react-native-community/slider": "3.0.3",
"@react-navigation/drawer": "5.12.4",
"@react-navigation/native": "5.9.2",
"@react-navigation/native": "5.9.3",
"@react-navigation/stack": "5.14.3",
"@remobile/react-native-qrcode-local-image": "git+https://github.com/BlueWallet/react-native-qrcode-local-image.git",
"@sentry/react-native": "1.9.0",
@ -128,12 +128,12 @@
"react-native-blue-crypto": "git+https://github.com/Overtorment/react-native-blue-crypto.git",
"react-native-camera": "3.43.0",
"react-native-default-preference": "1.4.3",
"react-native-device-info": "8.0.1",
"react-native-device-info": "8.0.2",
"react-native-document-picker": "git+https://github.com/BlueWallet/react-native-document-picker.git#3684d4fcc2bc0b47c32be39024e4796004c3e428",
"react-native-elements": "2.3.2",
"react-native-fingerprint-scanner": "git+https://github.com/BlueWallet/react-native-fingerprint-scanner.git#ce644673681716335d786727bab998f7e632ab5e",
"react-native-fs": "2.16.6",
"react-native-gesture-handler": "1.10.2",
"react-native-gesture-handler": "1.10.3",
"react-native-handoff": "git+https://github.com/marcosrdz/react-native-handoff.git#f5becc63f3e36bf2da1ed1fc60fc690323e73602",
"react-native-haptic-feedback": "1.11.0",
"react-native-idle-timer": "git+https://github.com/BlueWallet/react-native-idle-timer.git#8587876d68ab5920e79619726aeca9e672beaf2b",
@ -157,9 +157,9 @@
"react-native-rate": "1.2.4",
"react-native-reanimated": "1.13.2",
"react-native-safe-area-context": "3.1.9",
"react-native-screens": "2.17.1",
"react-native-screens": "2.18.0",
"react-native-secure-key-store": "git+https://github.com/BlueWallet/react-native-secure-key-store.git#4828fd1a67d12e4c0e21eee0bee673fde75e6f9a",
"react-native-share": "5.1.3",
"react-native-share": "5.1.4",
"react-native-snap-carousel": "3.9.1",
"react-native-sortable-list": "0.0.24",
"react-native-svg": "12.1.0",

View File

@ -51,7 +51,7 @@ const LNDViewAdditionalInvoiceInformation = () => {
}
return (
<SafeBlueArea style={[styles.root, stylesHook.root]}>
<SafeBlueArea style={stylesHook.root}>
<View style={styles.wrapper}>
<View style={styles.qrcode}>
<QRCode
@ -89,13 +89,9 @@ const LNDViewAdditionalInvoiceInformation = () => {
const styles = StyleSheet.create({
loading: {
flex: 1,
justifyContent: 'space-between',
alignItems: 'center',
},
root: {
flex: 1,
},
wrapper: {
flex: 1,
justifyContent: 'center',

View File

@ -18,7 +18,7 @@ const LNDViewAdditionalInvoicePreImage = () => {
});
return (
<SafeBlueArea style={[styles.root, stylesHook.root]}>
<SafeBlueArea style={stylesHook.root}>
<View style={styles.wrapper}>
<BlueTextCentered>{loc.lndViewInvoice.preimage}:</BlueTextCentered>
<BlueSpacing20 />
@ -41,9 +41,6 @@ const LNDViewAdditionalInvoicePreImage = () => {
};
const styles = StyleSheet.create({
root: {
flex: 1,
},
wrapper: {
flex: 1,
justifyContent: 'center',

View File

@ -278,7 +278,7 @@ const LNDViewInvoice = () => {
};
return (
<SafeBlueArea styles={[styles.root, stylesHook.root]} onLayout={onLayout}>
<SafeBlueArea onLayout={onLayout}>
<StatusBar barStyle="default" />
<ScrollView>{render()}</ScrollView>
</SafeBlueArea>

View File

@ -157,7 +157,7 @@ export default class LnurlPay extends Component {
renderGotPayload() {
return (
<SafeBlueArea style={styles.root}>
<SafeBlueArea>
<ScrollView>
<BlueCard>
<AmountInput

View File

@ -163,8 +163,7 @@ const styles = StyleSheet.create({
alignSelf: 'center',
},
root: {
flex: 1,
paddingTop: 0,
padding: 0,
},
iconContainer: {
backgroundColor: '#ccddf9',

View File

@ -285,7 +285,7 @@ const ScanLndInvoice = () => {
}
return (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={[styles.root, stylesHook.root]}>
<SafeBlueArea style={stylesHook.root}>
<StatusBar barStyle="light-content" />
<View style={[styles.root, stylesHook.root]}>
<ScrollView contentContainerStyle={styles.scroll}>

View File

@ -1,7 +1,7 @@
/* global alert */
import React, { useContext, useState } from 'react';
import { ScrollView, StyleSheet } from 'react-native';
import { useNavigation, useTheme } from '@react-navigation/native';
import { ScrollView } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
import navigationStyle from '../components/navigationStyle';
@ -10,22 +10,10 @@ import loc from '../loc';
import { BlueStorageContext } from '../blue_modules/storage-context';
const prompt = require('../blue_modules/prompt');
const styles = StyleSheet.create({
root: {
flex: 1,
},
});
const PlausibleDeniability = () => {
const { cachedPassword, isPasswordInUse, createFakeStorage, resetWallets } = useContext(BlueStorageContext);
const [isLoading, setIsLoading] = useState(false);
const { popToTop } = useNavigation();
const { colors } = useTheme();
const stylesHook = StyleSheet.create({
root: {
backgroundColor: colors.background,
},
});
const handleOnCreateFakeStorageButtonPressed = async () => {
setIsLoading(true);
@ -59,11 +47,11 @@ const PlausibleDeniability = () => {
};
return isLoading ? (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={[styles.root, stylesHook.root]}>
<SafeBlueArea>
<BlueLoading />
</SafeBlueArea>
) : (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={[styles.root, stylesHook.root]}>
<SafeBlueArea>
<BlueCard>
<ScrollView maxHeight={450}>
<BlueText>{loc.plausibledeniability.help}</BlueText>

View File

@ -6,17 +6,12 @@ import loc from '../loc';
import { BlueSpacing20, SafeBlueArea, BlueCard, BlueText, BlueLoading } from '../BlueComponents';
import navigationStyle from '../components/navigationStyle';
import { SegwitP2SHWallet, LegacyWallet, HDSegwitP2SHWallet, HDSegwitBech32Wallet, HDAezeedWallet } from '../class';
import { BlueCurrentTheme } from '../components/themes';
const bitcoin = require('bitcoinjs-lib');
const BlueCrypto = require('react-native-blue-crypto');
const encryption = require('../blue_modules/encryption');
const BlueElectrum = require('../blue_modules/BlueElectrum');
const styles = StyleSheet.create({
root: {
flex: 1,
backgroundColor: BlueCurrentTheme.colors.background,
},
center: {
alignItems: 'center',
},
@ -226,7 +221,7 @@ export default class Selftest extends Component {
}
return (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
<SafeBlueArea>
<BlueCard>
<ScrollView>
<BlueSpacing20 />

View File

@ -315,17 +315,9 @@ const ScanQRCode = () => {
testID="scanQrBackdoorOkButton"
onPress={() => {
setBackdoorVisible(false);
let data;
try {
data = JSON.parse(backdoorText);
// this might be a json string (for convenience - in case there are "\n" in there)
} catch (_) {
data = backdoorText;
} finally {
setBackdoorText('');
}
setBackdoorText('');
if (data) onBarCodeRead({ data });
if (backdoorText) onBarCodeRead({ data: backdoorText });
}}
/>
</View>

View File

@ -116,7 +116,7 @@ const Broadcast = () => {
}
return (
<SafeBlueArea style={styles.blueArea}>
<SafeBlueArea>
<KeyboardAvoidingView
enabled={!Platform.isPad}
behavior={Platform.OS === 'ios' ? 'position' : null}
@ -176,9 +176,6 @@ const styles = StyleSheet.create({
alignItems: 'center',
justifyContent: 'flex-start',
},
blueArea: {
flex: 1,
},
broadcastResultWrapper: {
flex: 1,
flexDirection: 'column',

View File

@ -388,7 +388,7 @@ const CoinControl = () => {
if (loading) {
return (
<SafeBlueArea style={[styles.root, styles.center, { backgroundColor: colors.elevated }]}>
<SafeBlueArea style={[styles.center, { backgroundColor: colors.elevated }]}>
<ActivityIndicator testID="Loading" />
</SafeBlueArea>
);

View File

@ -174,7 +174,7 @@ export default class Confirm extends Component {
/>
<View style={styles.cardContainer}>
<BlueCard>
<Text style={styles.cardText}>
<Text style={styles.cardText} testID="TransactionFee">
{loc.send.create_fee}: {formatBalance(this.state.feeSatoshi, BitcoinUnit.BTC)} (
{currency.satoshiToLocalCurrency(this.state.feeSatoshi)})
</Text>
@ -269,7 +269,6 @@ const styles = StyleSheet.create({
margin: 16,
},
root: {
flex: 1,
paddingTop: 19,
backgroundColor: BlueCurrentTheme.colors.elevated,
},

View File

@ -210,7 +210,6 @@ const styles = StyleSheet.create({
marginVertical: 16,
},
root: {
flex: 1,
backgroundColor: BlueCurrentTheme.colors.elevated,
},
card: {

View File

@ -85,7 +85,7 @@ const SendDetails = () => {
// if cutomFee is not set, we need to choose highest possible fee for wallet balance
// if there are no funds for even Slow option, use 1 sat/byte fee
const fee = useMemo(() => {
const feeRate = useMemo(() => {
if (customFee) return customFee;
if (feePrecalc.slowFee === null) return '1'; // wait for precalculated fees
let initialFee;
@ -133,15 +133,11 @@ const SendDetails = () => {
setAmountUnit(wallet.preferredBalanceUnit); // default for whole screen
// decode route params
const addresses = [];
let initialMemo = '';
if (routeParams.uri) {
try {
const { address, amount, memo, payjoinUrl } = DeeplinkSchemaMatch.decodeBitcoinUri(routeParams.uri);
addresses.push({ address, amount, amountSats: currency.btcToSatoshi(amount), key: String(Math.random()) });
initialMemo = memo;
setAddresses(addresses);
setMemo(initialMemo);
const { address, amount, memo: initialMemo, payjoinUrl } = DeeplinkSchemaMatch.decodeBitcoinUri(routeParams.uri);
setAddresses([{ address, amount, amountSats: currency.btcToSatoshi(amount), key: String(Math.random()) }]);
setMemo(initialMemo || '');
setAmountUnit(BitcoinUnit.BTC);
setPayjoinUrl(payjoinUrl);
} catch (error) {
@ -149,13 +145,11 @@ const SendDetails = () => {
Alert.alert(loc.errors.error, loc.send.details_error_decode);
}
} else if (routeParams.address) {
addresses.push({ address: routeParams.address, key: String(Math.random()) });
if (routeParams.memo) initialMemo = routeParams.memo;
setAddresses(addresses);
setMemo(initialMemo);
setAddresses([{ address: routeParams.address, key: String(Math.random()) }]);
setMemo(routeParams.memo || '');
setAmountUnit(BitcoinUnit.BTC);
} else {
setAddresses([{ address: '', key: String(Math.random()) }]);
setAddresses([{ address: '', key: String(Math.random()) }]); // key is for the FlatList
}
// we are ready!
@ -212,7 +206,7 @@ const SendDetails = () => {
if (!wallet) return; // wait for it
const fees = networkTransactionFees;
const changeAddress = getChangeAddressFast();
const requestedSatPerByte = Number(fee);
const requestedSatPerByte = Number(feeRate);
const lutxo = utxo || wallet.getUtxo();
const options = [
@ -279,7 +273,7 @@ const SendDetails = () => {
}
setFeePrecalc(newFeePrecalc);
}, [wallet, networkTransactionFees, utxo, addresses, fee, dumb]); // eslint-disable-line react-hooks/exhaustive-deps
}, [wallet, networkTransactionFees, utxo, addresses, feeRate, dumb]); // eslint-disable-line react-hooks/exhaustive-deps
const getChangeAddressFast = () => {
if (changeAddress) return changeAddress; // cache
@ -340,14 +334,16 @@ const SendDetails = () => {
return Alert.alert(loc.errors.error, loc.send.details_address_field_is_not_valid);
}
const recipients = [...addresses];
const unitsCopy = [...units];
const dataWithoutSchema = data.replace('bitcoin:', '').replace('BITCOIN:', '');
if (wallet.isAddressValid(dataWithoutSchema)) {
recipients[scrollIndex.current].address = dataWithoutSchema;
unitsCopy[scrollIndex.current] = amountUnit;
setAddresses(recipients);
setUnits(unitsCopy);
setAddresses(addresses => {
addresses[scrollIndex.current].address = dataWithoutSchema;
return [...addresses];
});
setUnits(units => {
units[scrollIndex.current] = amountUnit;
return [...units];
});
setIsLoading(false);
return;
}
@ -369,13 +365,17 @@ const SendDetails = () => {
console.log('options', options);
if (btcAddressRx.test(address) || address.startsWith('bc1') || address.startsWith('BC1')) {
unitsCopy[scrollIndex.current] = BitcoinUnit.BTC; // also resetting current unit to BTC
recipients[scrollIndex.current].address = address;
recipients[scrollIndex.current].amount = options.amount;
recipients[scrollIndex.current].amountSats = new BigNumber(options.amount).multipliedBy(100000000).toNumber();
setAddresses(recipients);
setAddresses(addresses => {
addresses[scrollIndex.current].address = address;
addresses[scrollIndex.current].amount = options.amount;
addresses[scrollIndex.current].amountSats = new BigNumber(options.amount).multipliedBy(100000000).toNumber();
return [...addresses];
});
setUnits(units => {
units[scrollIndex.current] = BitcoinUnit.BTC; // also resetting current unit to BTC
return [...units];
});
setMemo(options.label || options.message);
setUnits(unitsCopy);
setAmountUnit(BitcoinUnit.BTC);
setPayjoinUrl(options.pj || '');
}
@ -386,7 +386,7 @@ const SendDetails = () => {
const createTransaction = async () => {
Keyboard.dismiss();
setIsLoading(true);
const requestedSatPerByte = fee;
const requestedSatPerByte = feeRate;
for (const [index, transaction] of addresses.entries()) {
let error;
if (!transaction.amount || transaction.amount < 0 || parseFloat(transaction.amount) === 0) {
@ -444,16 +444,16 @@ const SendDetails = () => {
const createPsbtTransaction = async () => {
const changeAddress = await getChangeAddressAsync();
const requestedSatPerByte = Number(fee);
const requestedSatPerByte = Number(feeRate);
const lutxo = utxo || wallet.getUtxo();
console.log({ requestedSatPerByte, utxo });
let targets = [];
const targets = [];
for (const transaction of addresses) {
if (transaction.amount === BitcoinUnit.MAX) {
// single output with MAX
targets = [{ address: transaction.address }];
break;
// output with MAX
targets.push({ address: transaction.address });
continue;
}
const value = parseInt(transaction.amountSats);
if (value > 0) {
@ -465,7 +465,7 @@ const SendDetails = () => {
}
}
const { tx, outputs, psbt } = wallet.createTransaction(
const { tx, outputs, psbt, fee } = wallet.createTransaction(
lutxo,
targets,
requestedSatPerByte,
@ -531,14 +531,16 @@ const SendDetails = () => {
[
{
text: loc._.ok,
onPress: async () => {
const firstTransaction =
addresses.find(element => {
const feeSatoshi = new BigNumber(element.amount).multipliedBy(100000000);
return element.address.length > 0 && feeSatoshi > 0;
}) || addresses[0];
onPress: () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setAddresses([firstTransaction]);
setAddresses(addresses => {
const firstTransaction =
addresses.find(element => {
const feeSatoshi = new BigNumber(element.amount).multipliedBy(100000000);
return element.address.length > 0 && feeSatoshi > 0;
}) || addresses[0];
return [firstTransaction];
});
changeWallet();
},
style: 'default',
@ -547,7 +549,10 @@ const SendDetails = () => {
],
{ cancelable: false },
);
} else if (addresses.some(element => element.amount === BitcoinUnit.MAX) && !wallet.allowSendMax()) {
return;
}
if (addresses.some(element => element.amount === BitcoinUnit.MAX) && !wallet.allowSendMax()) {
ReactNativeHapticFeedback.trigger('notificationWarning');
Alert.alert(
loc.send.details_wallet_selection,
@ -555,11 +560,13 @@ const SendDetails = () => {
[
{
text: loc._.ok,
onPress: async () => {
const firstTransaction = addresses.find(element => element.amount === BitcoinUnit.MAX) || addresses[0];
firstTransaction.amount = 0;
onPress: () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setAddresses([firstTransaction]);
setAddresses(addresses => {
const firstTransaction = addresses.find(element => element.amount === BitcoinUnit.MAX) || addresses[0];
firstTransaction.amount = 0;
return [firstTransaction];
});
changeWallet();
},
style: 'default',
@ -568,9 +575,10 @@ const SendDetails = () => {
],
{ cancelable: false },
);
} else {
changeWallet();
return;
}
changeWallet();
};
/**
@ -757,9 +765,8 @@ const SendDetails = () => {
};
const handleAddRecipient = async () => {
addresses.push({ address: '', key: String(Math.random()) }); // key is for the FlatList
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut, () => scrollView.current.scrollToEnd());
setAddresses([...addresses]);
setAddresses(addresses => [...addresses, { address: '', key: String(Math.random()) }]);
setOptionsVisible(false);
scrollView.current.scrollToEnd();
if (addresses.length === 0) return;
@ -769,9 +776,11 @@ const SendDetails = () => {
const handleRemoveRecipient = async () => {
const last = scrollIndex.current === addresses.length - 1;
addresses.splice(scrollIndex.current, 1);
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setAddresses([...addresses]);
setAddresses(addresses => {
addresses.splice(scrollIndex.current, 1);
return [...addresses];
});
setOptionsVisible(false);
if (addresses.length === 0) return;
await sleep(200); // wait for animation
@ -858,18 +867,22 @@ const SendDetails = () => {
ReactNativeHapticFeedback.trigger('notificationWarning');
Alert.alert(
loc.send.details_adv_full,
loc.send.details_adv_full_sure + ' ' + (addresses.length > 1 ? loc.send.details_adv_full_remove : ''),
loc.send.details_adv_full_sure,
[
{
text: loc._.ok,
onPress: async () => {
onPress: () => {
Keyboard.dismiss();
const recipient = addresses[scrollIndex.current];
recipient.amount = BitcoinUnit.MAX;
recipient.amountSats = BitcoinUnit.MAX;
setAddresses(addresses => {
addresses[scrollIndex.current].amount = BitcoinUnit.MAX;
addresses[scrollIndex.current].amountSats = BitcoinUnit.MAX;
return [...addresses];
});
setUnits(units => {
units[scrollIndex.current] = BitcoinUnit.BTC;
return [...units];
});
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setAddresses([recipient]);
setUnits([BitcoinUnit.BTC]);
setOptionsVisible(false);
},
style: 'default',
@ -947,21 +960,21 @@ const SendDetails = () => {
time: loc.send.fee_10m,
fee: feePrecalc.fastestFee,
rate: nf.fastestFee,
active: Number(fee) === nf.fastestFee,
active: Number(feeRate) === nf.fastestFee,
},
{
label: loc.send.fee_medium,
time: loc.send.fee_3h,
fee: feePrecalc.mediumFee,
rate: nf.mediumFee,
active: Number(fee) === nf.mediumFee,
active: Number(feeRate) === nf.mediumFee,
},
{
label: loc.send.fee_slow,
time: loc.send.fee_1d,
fee: feePrecalc.slowFee,
rate: nf.slowFee,
active: Number(fee) === nf.slowFee,
active: Number(feeRate) === nf.slowFee,
},
];
@ -1033,8 +1046,6 @@ const SendDetails = () => {
};
const renderOptionsModal = () => {
const isSendMaxUsed = addresses.some(element => element.amount === BitcoinUnit.MAX);
return (
<BottomModal deviceWidth={width + width / 2} isVisible={optionsVisible} onClose={hideOptions}>
<KeyboardAvoidingView enabled={!Platform.isPad} behavior={Platform.OS === 'ios' ? 'position' : null}>
@ -1088,7 +1099,6 @@ const SendDetails = () => {
<>
<BlueListItem
testID="AddRecipient"
disabled={isSendMaxUsed}
title={loc.send.details_add_rec_add}
hideChevron
component={TouchableOpacity}
@ -1181,42 +1191,48 @@ const SendDetails = () => {
isLoading={isLoading}
amount={item.amount ? item.amount.toString() : null}
onAmountUnitChange={unit => {
units[index] = unit;
const item = addresses[index];
setAddresses(addresses => {
const item = addresses[index];
switch (unit) {
case BitcoinUnit.SATS:
item.amountSats = parseInt(item.amount);
break;
case BitcoinUnit.BTC:
item.amountSats = currency.btcToSatoshi(item.amount);
break;
case BitcoinUnit.LOCAL_CURRENCY:
// also accounting for cached fiat->sat conversion to avoid rounding error
item.amountSats = AmountInput.getCachedSatoshis(item.amount) || currency.btcToSatoshi(currency.fiatToBTC(item.amount));
break;
}
switch (unit) {
case BitcoinUnit.SATS:
item.amountSats = parseInt(item.amount);
break;
case BitcoinUnit.BTC:
item.amountSats = currency.btcToSatoshi(item.amount);
break;
case BitcoinUnit.LOCAL_CURRENCY:
// also accounting for cached fiat->sat conversion to avoid rounding error
item.amountSats = AmountInput.getCachedSatoshis(item.amount) || currency.btcToSatoshi(currency.fiatToBTC(item.amount));
break;
}
addresses[index] = item;
setUnits([...units]);
setAddresses([...addresses]);
addresses[index] = item;
return [...addresses];
});
setUnits(units => {
units[index] = unit;
return [...units];
});
}}
onChangeText={text => {
item.amount = text;
switch (units[index] || amountUnit) {
case BitcoinUnit.BTC:
item.amountSats = currency.btcToSatoshi(item.amount);
break;
case BitcoinUnit.LOCAL_CURRENCY:
item.amountSats = currency.btcToSatoshi(currency.fiatToBTC(item.amount));
break;
default:
case BitcoinUnit.SATS:
item.amountSats = parseInt(text);
break;
}
addresses[index] = item;
setAddresses([...addresses]);
setAddresses(addresses => {
item.amount = text;
switch (units[index] || amountUnit) {
case BitcoinUnit.BTC:
item.amountSats = currency.btcToSatoshi(item.amount);
break;
case BitcoinUnit.LOCAL_CURRENCY:
item.amountSats = currency.btcToSatoshi(currency.fiatToBTC(item.amount));
break;
default:
case BitcoinUnit.SATS:
item.amountSats = parseInt(text);
break;
}
addresses[index] = item;
return [...addresses];
});
}}
unit={units[index] || amountUnit}
inputAccessoryViewID={wallet.allowSendMax() ? BlueUseAllFundsButton.InputAccessoryViewID : null}
@ -1224,12 +1240,13 @@ const SendDetails = () => {
<AddressInput
onChangeText={async text => {
text = text.trim();
const transactions = [...addresses];
const { address, amount, memo: lmemo, payjoinUrl } = DeeplinkSchemaMatch.decodeBitcoinUri(text);
item.address = address || text;
item.amount = amount || item.amount;
transactions[index] = item;
setAddresses(transactions);
setAddresses(addresses => {
item.address = address || text;
item.amount = amount || item.amount;
addresses[index] = item;
return [...addresses];
});
setMemo(lmemo || memo);
setIsLoading(false);
setPayjoinUrl(payjoinUrl);
@ -1260,6 +1277,7 @@ const SendDetails = () => {
// if utxo is limited we use it to calculate available balance
const balance = utxo ? utxo.reduce((prev, curr) => prev + curr.value, 0) : wallet.getBalance();
const allBalance = formatBalanceWithoutSuffix(balance, BitcoinUnit.BTC, true);
const isSendMaxUsed = addresses.some(element => element.amount === BitcoinUnit.MAX);
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
@ -1305,7 +1323,7 @@ const SendDetails = () => {
<Text style={[styles.feeLabel, stylesHook.feeLabel]}>{loc.send.create_fee}</Text>
<View style={[styles.feeRow, stylesHook.feeRow]}>
<Text style={stylesHook.feeValue}>
{feePrecalc.current ? formatFee(feePrecalc.current) : fee + ' ' + loc.units.sat_byte}
{feePrecalc.current ? formatFee(feePrecalc.current) : feeRate + ' ' + loc.units.sat_byte}
</Text>
</View>
</TouchableOpacity>
@ -1318,14 +1336,14 @@ const SendDetails = () => {
{Platform.select({
ios: (
<BlueUseAllFundsButton
canUseAll={wallet.allowSendMax() && allBalance > 0}
canUseAll={wallet.allowSendMax() && allBalance > 0 && !isSendMaxUsed}
onUseAllPressed={onUseAllPressed}
balance={allBalance}
/>
),
android: isAmountToolbarVisibleForAndroid && (
<BlueUseAllFundsButton
canUseAll={wallet.allowSendMax() && allBalance > 0}
canUseAll={wallet.allowSendMax() && allBalance > 0 && !isSendMaxUsed}
onUseAllPressed={onUseAllPressed}
balance={allBalance}
/>

View File

@ -21,9 +21,6 @@ const IsItMyAddress = () => {
const [result, setResult] = useState('');
const stylesHooks = StyleSheet.create({
blueArea: {
backgroundColor: colors.background,
},
text: {
color: colors.foregroundColor,
},
@ -78,7 +75,7 @@ const IsItMyAddress = () => {
};
return (
<SafeBlueArea style={[styles.blueArea, stylesHooks.blueArea]}>
<SafeBlueArea style={styles.blueArea}>
<KeyboardAvoidingView
enabled={!Platform.isPad}
behavior={Platform.OS === 'ios' ? 'position' : null}
@ -133,7 +130,6 @@ const styles = StyleSheet.create({
justifyContent: 'flex-start',
},
blueArea: {
flex: 1,
paddingTop: 19,
},
broadcastResultWrapper: {

View File

@ -264,7 +264,7 @@ const PsbtMultisig = () => {
};
return (
<SafeBlueArea style={[styles.root, stylesHook.root]}>
<SafeBlueArea style={stylesHook.root}>
<View style={styles.container}>
<View style={styles.mstopcontainer}>
<View style={styles.mscontainer}>
@ -303,9 +303,6 @@ const PsbtMultisig = () => {
};
const styles = StyleSheet.create({
root: {
flex: 1,
},
mstopcontainer: {
flex: 1,
flexDirection: 'row',

View File

@ -69,7 +69,7 @@ const PsbtMultisigQRCode = () => {
};
return (
<SafeBlueArea style={[styles.root, stylesHook.root]}>
<SafeBlueArea style={stylesHook.root}>
<ScrollView centerContent contentContainerStyle={styles.scrollViewContent}>
<View style={[styles.modalContentShort, stylesHook.modalContentShort]}>
<DynamicQRCode value={psbt.toHex()} />
@ -98,9 +98,6 @@ const PsbtMultisigQRCode = () => {
};
const styles = StyleSheet.create({
root: {
flex: 1,
},
scrollViewContent: {
flexGrow: 1,
justifyContent: 'space-between',

View File

@ -272,7 +272,7 @@ const PsbtWithHardwareWallet = () => {
<ActivityIndicator />
</View>
) : (
<SafeBlueArea style={[styles.root, stylesHook.root]}>
<SafeBlueArea style={stylesHook.root}>
<ScrollView centerContent contentContainerStyle={styles.scrollViewContent} testID="PsbtWithHardwareScrollView">
<View style={styles.container}>
<BlueCard>
@ -333,9 +333,6 @@ export default PsbtWithHardwareWallet;
PsbtWithHardwareWallet.navigationOptions = navigationStyle({}, opts => ({ ...opts, title: loc.send.header }));
const styles = StyleSheet.create({
root: {
flex: 1,
},
scrollViewContent: {
flexGrow: 1,
justifyContent: 'space-between',

View File

@ -1,6 +1,6 @@
import React from 'react';
import { ScrollView, StyleSheet } from 'react-native';
import { useNavigation, useTheme } from '@react-navigation/native';
import { ScrollView } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import navigationStyle from '../../components/navigationStyle';
import { SafeBlueArea, BlueListItem } from '../../BlueComponents';
@ -8,13 +8,6 @@ import loc from '../../loc';
const NetworkSettings = () => {
const { navigate } = useNavigation();
const { colors } = useTheme();
const styles = StyleSheet.create({
root: {
flex: 1,
backgroundColor: colors.background,
},
});
const navigateToElectrumSettings = () => {
navigate('ElectrumSettings');
@ -29,7 +22,7 @@ const NetworkSettings = () => {
};
return (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
<SafeBlueArea>
<ScrollView>
<BlueListItem title={loc.settings.network_electrum} onPress={navigateToElectrumSettings} testID="ElectrumSettings" chevron />
<BlueListItem title={loc.settings.lightning_settings} onPress={navigateToLightningSettings} testID="LightningSettings" chevron />

View File

@ -14,9 +14,6 @@ const About = () => {
const { colors } = useTheme();
const { width, height } = useWindowDimensions();
const styles = StyleSheet.create({
root: {
flex: 1,
},
center: {
justifyContent: 'center',
alignItems: 'center',
@ -105,7 +102,7 @@ const About = () => {
};
return (
<SafeBlueArea style={styles.root}>
<SafeBlueArea>
<ScrollView testID="AboutScrollView">
<BlueCard>
<View style={styles.center}>

View File

@ -46,7 +46,7 @@ const Currency = () => {
if (selectedCurrency !== null && selectedCurrency !== undefined) {
return (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.flex}>
<SafeBlueArea>
<FlatList
style={styles.flex}
keyExtractor={(_item, index) => `${index}`}

View File

@ -1,5 +1,5 @@
import React, { useContext, useEffect, useState } from 'react';
import { View, TouchableWithoutFeedback, StyleSheet } from 'react-native';
import { View, TouchableWithoutFeedback } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import navigationStyle from '../../components/navigationStyle';
@ -8,12 +8,6 @@ import OnAppLaunch from '../../class/on-app-launch';
import loc from '../../loc';
import { BlueStorageContext } from '../../blue_modules/storage-context';
const styles = StyleSheet.create({
flex: {
flex: 1,
},
});
const DefaultView = () => {
const [defaultWalletLabel, setDefaultWalletLabel] = useState('');
const [viewAllWalletsEnabled, setViewAllWalletsEnabled] = useState(true);
@ -58,7 +52,7 @@ const DefaultView = () => {
};
return (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.flex}>
<SafeBlueArea>
<View>
<BlueListItem
title={loc.settings.default_wallets}

View File

@ -215,7 +215,7 @@ export default class ElectrumSettings extends Component {
});
return (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
<SafeBlueArea>
<ScrollView>
<BlueCard>
<BlueText style={styles.status}>{loc.settings.electrum_status}</BlueText>
@ -325,9 +325,6 @@ ElectrumSettings.propTypes = {
ElectrumSettings.navigationOptions = navigationStyle({}, opts => ({ ...opts, title: loc.settings.electrum_settings_server }));
const styles = StyleSheet.create({
root: {
flex: 1,
},
status: {
textAlign: 'center',
color: BlueCurrentTheme.colors.feeText,

View File

@ -124,11 +124,11 @@ const EncryptStorage = () => {
};
return isLoading ? (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
<SafeBlueArea>
<BlueLoading />
</SafeBlueArea>
) : (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
<SafeBlueArea>
<ScrollView contentContainerStyle={styles.root}>
{biometrics.isDeviceBiometricCapable && (
<>

View File

@ -58,7 +58,7 @@ const Language = () => {
return isLoading ? (
<BlueLoading />
) : (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={[styles.flex, stylesHook.flex]}>
<SafeBlueArea>
<FlatList
style={[styles.flex, stylesHook.flex]}
keyExtractor={(_item, index) => `${index}`}

View File

@ -1,14 +1,8 @@
import React, { useState, useEffect } from 'react';
import { ScrollView, StyleSheet } from 'react-native';
import { ScrollView } from 'react-native';
import navigationStyle from '../../components/navigationStyle';
import { SafeBlueArea, BlueCard, BlueText, BlueSpacing20, BlueLoading } from '../../BlueComponents';
const styles = StyleSheet.create({
root: {
flex: 1,
},
});
const Licensing = () => {
const [isLoading, setIsLoading] = useState(true);
@ -19,7 +13,7 @@ const Licensing = () => {
return isLoading ? (
<BlueLoading />
) : (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
<SafeBlueArea>
<ScrollView>
<BlueCard>
<BlueText>MIT License</BlueText>

View File

@ -14,9 +14,6 @@ import { BlueCurrentTheme } from '../../components/themes';
import DeeplinkSchemaMatch from '../../class/deeplink-schema-match';
const styles = StyleSheet.create({
root: {
flex: 1,
},
uri: {
flexDirection: 'row',
borderColor: BlueCurrentTheme.colors.formBorder,
@ -111,7 +108,7 @@ const LightningSettings = () => {
};
return (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
<SafeBlueArea>
<BlueCard>
<BlueText>{loc.settings.lightning_settings_explain}</BlueText>
</BlueCard>

View File

@ -1,22 +1,14 @@
import React from 'react';
import { ScrollView, StyleSheet } from 'react-native';
import { useTheme } from '@react-navigation/native';
import { ScrollView } from 'react-native';
import navigationStyle from '../../components/navigationStyle';
import { SafeBlueArea, BlueCard, BlueText } from '../../BlueComponents';
import loc from '../../loc';
const ReleaseNotes = () => {
const notes = require('../../release-notes');
const { colors } = useTheme();
const styles = StyleSheet.create({
root: {
flex: 1,
backgroundColor: colors.background,
},
});
return (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
<SafeBlueArea>
<ScrollView>
<BlueCard>
<BlueText>{notes}</BlueText>

View File

@ -31,7 +31,6 @@ const styles = StyleSheet.create({
paddingTop: 20,
},
explain: {
flex: 1,
paddingBottom: 16,
},
center: {

View File

@ -1,7 +1,7 @@
/* global alert */
import React from 'react';
import PropTypes from 'prop-types';
import { ActivityIndicator, View, StyleSheet, ScrollView } from 'react-native';
import { ActivityIndicator, View, ScrollView } from 'react-native';
import { BlueSpacing20, SafeBlueArea, BlueText } from '../../BlueComponents';
import navigationStyle from '../../components/navigationStyle';
import { HDSegwitBech32Transaction, HDSegwitBech32Wallet } from '../../class';
@ -9,13 +9,6 @@ import CPFP from './CPFP';
import loc from '../../loc';
import { BlueStorageContext } from '../../blue_modules/storage-context';
const styles = StyleSheet.create({
common: {
flex: 1,
paddingTop: 16,
},
});
export default class RBFCancel extends CPFP {
static contextType = BlueStorageContext;
async componentDidMount() {
@ -83,7 +76,7 @@ export default class RBFCancel extends CPFP {
render() {
if (this.state.isLoading) {
return (
<View style={styles.root}>
<View>
<ActivityIndicator />
</View>
);
@ -95,7 +88,7 @@ export default class RBFCancel extends CPFP {
if (this.state.nonReplaceable) {
return (
<SafeBlueArea style={styles.root}>
<SafeBlueArea>
<BlueSpacing20 />
<BlueSpacing20 />
<BlueSpacing20 />
@ -108,7 +101,7 @@ export default class RBFCancel extends CPFP {
}
return (
<SafeBlueArea style={styles.root}>
<SafeBlueArea>
<ScrollView>{this.renderStage1(loc.transactions.cancel_explain)}</ScrollView>
</SafeBlueArea>
);

View File

@ -140,7 +140,7 @@ const TransactionsDetails = () => {
}
return (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
<SafeBlueArea>
<HandoffComponent
title={`Bitcoin Transaction ${tx.hash}`}
type="io.bluewallet.bluewallet"
@ -249,9 +249,6 @@ const TransactionsDetails = () => {
};
const styles = StyleSheet.create({
root: {
flex: 1,
},
scroll: {
flex: 1,
},

View File

@ -40,9 +40,6 @@ const TransactionsStatus = () => {
const [tx, setTX] = useState();
const [isLoading, setIsLoading] = useState(true);
const stylesHook = StyleSheet.create({
root: {
backgroundColor: colors.background,
},
value: {
color: colors.alternativeTextColor2,
},
@ -273,13 +270,13 @@ const TransactionsStatus = () => {
if (isLoading || !tx) {
return (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={[styles.root, stylesHook.root]}>
<SafeBlueArea>
<BlueLoading />
</SafeBlueArea>
);
}
return (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={[styles.root, stylesHook.root]}>
<SafeBlueArea>
<HandoffComponent
title={`Bitcoin Transaction ${tx.hash}`}
type="io.bluewallet.bluewallet"
@ -363,9 +360,6 @@ const TransactionsStatus = () => {
export default TransactionsStatus;
const styles = StyleSheet.create({
root: {
flex: 1,
},
container: {
flex: 1,
justifyContent: 'space-between',

View File

@ -39,7 +39,7 @@ const WalletsAddMultisigHelp = () => {
return isLoading ? (
<BlueLoading />
) : (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={stylesHook.root}>
<SafeBlueArea style={stylesHook.root}>
<ScrollView>
<View style={[styles.intro, stylesHook.intro]}>
<Text style={[styles.introTitle, stylesHook.introTitle]}>{loc.multisig.ms_help_title}</Text>

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, StatusBar, Linking, Platform } from 'react-native';
import { StatusBar, Linking, Platform } from 'react-native';
import { WebView } from 'react-native-webview';
import InAppBrowser from 'react-native-inappbrowser-reborn';
@ -11,12 +11,6 @@ import * as NavigationService from '../../NavigationService';
import { BlueStorageContext } from '../../blue_modules/storage-context';
const currency = require('../../blue_modules/currency');
const styles = StyleSheet.create({
root: {
flex: 1,
},
});
export default class BuyBitcoin extends Component {
static contextType = BlueStorageContext;
constructor(props) {
@ -86,7 +80,7 @@ export default class BuyBitcoin extends Component {
}
return (
<SafeBlueArea style={styles.root}>
<SafeBlueArea>
<StatusBar barStyle="default" />
<WebView
source={{

View File

@ -399,7 +399,7 @@ const WalletDetails = () => {
<BlueLoading />
</View>
) : (
<SafeBlueArea style={styles.root}>
<SafeBlueArea>
<StatusBar barStyle="default" />
<TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
<ScrollView contentContainerStyle={styles.scrollViewContent} testID="WalletDetailsScroll">

View File

@ -16,9 +16,6 @@ const styles = StyleSheet.create({
flex: 1,
justifyContent: 'center',
},
root: {
flex: 1,
},
scrollViewContent: {
alignItems: 'center',
justifyContent: 'center',
@ -52,7 +49,6 @@ const WalletExport = () => {
backgroundColor: colors.elevated,
},
root: {
...styles.root,
backgroundColor: colors.elevated,
},
type: { ...styles.type, color: colors.foregroundColor },

View File

@ -72,7 +72,7 @@ const ExportMultisigCoordinationSetup = () => {
<ActivityIndicator />
</View>
) : (
<SafeBlueArea style={[styles.root, stylesHook.root]}>
<SafeBlueArea style={stylesHook.root}>
<StatusBar barStyle="light-content" />
<ScrollView contentContainerStyle={styles.scrollViewContent}>
<View>
@ -98,9 +98,6 @@ const styles = StyleSheet.create({
flex: 1,
justifyContent: 'center',
},
root: {
flex: 1,
},
scrollViewContent: {
alignItems: 'center',
justifyContent: 'center',

View File

@ -23,12 +23,12 @@ const WalletsImport = () => {
const [isToolbarVisibleForAndroid, setIsToolbarVisibleForAndroid] = useState(false);
const route = useRoute();
const label = (route.params && route.params.label) || '';
const triggerImport = (route.params && route.params.triggerImport) || false;
const [importText, setImportText] = useState(label);
const navigation = useNavigation();
const { colors } = useTheme();
const styles = StyleSheet.create({
root: {
flex: 1,
paddingTop: 40,
backgroundColor: colors.elevated,
},
@ -50,6 +50,11 @@ const WalletsImport = () => {
};
}, []);
useEffect(() => {
if (triggerImport) importButtonPressed();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const importButtonPressed = () => {
if (importText.trim().length === 0) {
return;
@ -108,7 +113,7 @@ const WalletsImport = () => {
};
return (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
<SafeBlueArea style={styles.root}>
<StatusBar barStyle="light-content" />
<BlueSpacing20 />
<BlueFormLabel>{loc.wallets.import_explanation}</BlueFormLabel>

View File

@ -32,6 +32,7 @@ import { isCatalyst, isMacCatalina, isTablet } from '../../blue_modules/environm
import BlueClipboard from '../../blue_modules/clipboard';
import navigationStyle from '../../components/navigationStyle';
const scanqrHelper = require('../../helpers/scan-qr');
const A = require('../../blue_modules/analytics');
const fs = require('../../blue_modules/fs');
const WalletsListSections = { CAROUSEL: 'CAROUSEL', LOCALTRADER: 'LOCALTRADER', TRANSACTIONS: 'TRANSACTIONS' };
@ -351,18 +352,12 @@ const WalletsList = () => {
if (isMacCatalina) {
fs.showActionSheet({ anchor: walletActionButtonsRef.current }).then(onBarScanned);
} else {
navigate('ScanQRCodeRoot', {
screen: 'ScanQRCode',
params: {
launchedBy: routeName,
onBarScanned,
showFileImportButton: false,
},
});
scanqrHelper(navigate, routeName, false).then(onBarScanned);
}
};
const onBarScanned = value => {
if (!value) return;
DeeplinkSchemaMatch.navigationRouteFor({ url: value }, completionValue => {
ReactNativeHapticFeedback.trigger('impactLight', { ignoreAndroidSystemSettings: false });
navigate(...completionValue);
@ -389,14 +384,7 @@ const WalletsList = () => {
if (buttonIndex === 1) {
fs.showImagePickerAndReadImage().then(onBarScanned);
} else if (buttonIndex === 2) {
navigate('ScanQRCodeRoot', {
screen: 'ScanQRCode',
params: {
launchedBy: routeName,
onBarScanned,
showFileImportButton: false,
},
});
scanqrHelper(navigate, routeName, false).then(onBarScanned);
} else if (buttonIndex === 3) {
copyFromClipboard();
}
@ -416,15 +404,7 @@ const WalletsList = () => {
},
{
text: loc.wallets.list_long_scan,
onPress: () =>
navigate('ScanQRCodeRoot', {
screen: 'ScanQRCode',
params: {
launchedBy: routeName,
onBarScanned,
showFileImportButton: false,
},
}),
onPress: () => scanqrHelper(navigate, routeName, false).then(onBarScanned),
},
];
if (!isClipboardEmpty) {

View File

@ -70,7 +70,7 @@ const PleaseBackup = () => {
<ActivityIndicator />
</View>
) : (
<SafeBlueArea style={[styles.flex, stylesHook.flex]}>
<SafeBlueArea style={stylesHook.flex}>
<StatusBar barStyle="default" />
<ScrollView testID="PleaseBackupScrollView">
<View style={styles.please}>
@ -99,9 +99,6 @@ PleaseBackup.navigationOptions = navigationStyle(
);
const styles = StyleSheet.create({
flex: {
flex: 1,
},
loading: {
flex: 1,
justifyContent: 'center',

View File

@ -24,7 +24,6 @@ const PleaseBackupLNDHub = () => {
}, [navigation]);
const styles = StyleSheet.create({
root: {
flex: 1,
backgroundColor: colors.elevated,
},
scrollViewContent: {

View File

@ -27,10 +27,6 @@ const SelectWallet = () => {
data = availableWallets;
}
const styles = StyleSheet.create({
root: {
flex: 1,
backgroundColor: colors.background,
},
loading: {
flex: 1,
justifyContent: 'center',
@ -153,7 +149,7 @@ const SelectWallet = () => {
);
} else if (data.length <= 0) {
return (
<SafeBlueArea style={styles.root}>
<SafeBlueArea>
<StatusBar barStyle="light-content" />
<View style={styles.noWallets}>
<BlueText style={styles.center}>{loc.wallets.select_no_bitcoin}</BlueText>
@ -164,7 +160,7 @@ const SelectWallet = () => {
);
} else {
return (
<SafeBlueArea style={styles.root}>
<SafeBlueArea>
<StatusBar barStyle="default" />
<FlatList extraData={data} data={data} renderItem={renderItem} keyExtractor={(_item, index) => `${index}`} />
</SafeBlueArea>

View File

@ -511,6 +511,8 @@ describe('BlueWallet UI Tests', () => {
// created. verifying:
await yo('TransactionValue');
expect(element(by.id('TransactionValue'))).toHaveText('0.0001');
const transactionFee = await extractTextFromElementById('TransactionFee');
assert.ok(transactionFee.startsWith('Fee: 0.00000748 BTC'), 'Unexpected tx fee: ' + transactionFee);
await element(by.id('TransactionDetailsButton')).tap();
let txhex = await extractTextFromElementById('TxhexInput');
@ -525,6 +527,7 @@ describe('BlueWallet UI Tests', () => {
const totalIns = 100000 + 5526; // we hardcode it since we know it in advance
const totalOuts = transaction.outs.map(el => el.value).reduce((a, b) => a + b, 0);
assert.strictEqual(Math.round((totalIns - totalOuts) / (txhex.length / 2)), feeRate);
assert.strictEqual(transactionFee.split(' ')[1] * 100000000, totalIns - totalOuts);
if (device.getPlatform() === 'ios') {
console.warn('rest of the test is Android only, skipped');
@ -635,21 +638,58 @@ describe('BlueWallet UI Tests', () => {
await device.pressBack();
await device.pressBack();
await device.pressBack(); // go back to wallet tx list to reset the form
await element(by.id('SendButton')).tap();
// set fee rate
await element(by.id('chooseFee')).tap();
await element(by.id('feeCustom')).tap();
await element(by.type('android.widget.EditText')).typeText(feeRate + '');
await element(by.text('OK')).tap();
// first send MAX output
await element(by.id('AddressInput')).replaceText('bc1qnapskphjnwzw2w3dk4anpxntunc77v6qrua0f7');
await element(by.id('BitcoinAmountInput')).typeText('0.0001\n');
await element(by.id('advancedOptionsMenuButton')).tap();
await element(by.id('sendMaxButton')).tap();
await element(by.text('OK')).tap();
if (process.env.TRAVIS) await sleep(5000);
try {
await element(by.id('CreateTransactionButton')).tap();
} catch (_) {}
// created. verifying:
await yo('TransactionValue');
await yo('TransactionDetailsButton');
await element(by.id('TransactionDetailsButton')).tap();
txhex = await extractTextFromElementById('TxhexInput');
transaction = bitcoin.Transaction.fromHex(txhex);
assert.strictEqual(transaction.outs.length, 1, 'should be single output, no change');
assert.ok(transaction.outs[0].value > 100000);
// add second output with amount
await device.pressBack();
await device.pressBack();
await element(by.id('advancedOptionsMenuButton')).tap();
await element(by.id('AddRecipient')).tap();
await yo('Transaction1');
await element(by.id('AddressInput').withAncestor(by.id('Transaction1'))).replaceText('bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl');
await element(by.id('BitcoinAmountInput').withAncestor(by.id('Transaction1'))).typeText('0.0001\n');
if (process.env.TRAVIS) await sleep(5000);
try {
await element(by.id('CreateTransactionButton')).tap();
} catch (_) {}
// created. verifying:
await yo('TransactionDetailsButton');
await element(by.id('TransactionDetailsButton')).tap();
txhex = await extractTextFromElementById('TxhexInput');
transaction = bitcoin.Transaction.fromHex(txhex);
assert.strictEqual(transaction.outs.length, 2, 'should be single output, no change');
assert.strictEqual(bitcoin.address.fromOutputScript(transaction.outs[0].script), 'bc1qnapskphjnwzw2w3dk4anpxntunc77v6qrua0f7');
assert.ok(transaction.outs[0].value > 50000);
assert.strictEqual(bitcoin.address.fromOutputScript(transaction.outs[1].script), 'bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl');
assert.strictEqual(transaction.outs[1].value, 10000);
// now, testing cosign psbt:
await device.pressBack();

View File

@ -101,7 +101,6 @@ it('HD (BIP49) can create TX', async () => {
assert.strictEqual('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', toAddress);
// testing sendMAX
const utxo = [
{
height: 591862,
@ -135,6 +134,7 @@ it('HD (BIP49) can create TX', async () => {
},
];
// one MAX output
txNew = hd.createTransaction(
utxo,
[{ address: '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK' }],
@ -144,6 +144,19 @@ it('HD (BIP49) can create TX', async () => {
tx = bitcoin.Transaction.fromHex(txNew.tx.toHex());
assert.strictEqual(tx.outs.length, 1);
assert.ok(tx.outs[0].value > 77000);
// MAX with regular output
txNew = hd.createTransaction(
utxo,
[{ address: '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK' }, { address: 'bc1qvd6w54sydc08z3802svkxr7297ez7cusd6266p', value: 25000 }],
1,
hd._getInternalAddressByIndex(hd.next_free_change_address_index),
);
tx = bitcoin.Transaction.fromHex(txNew.tx.toHex());
assert.strictEqual(tx.outs.length, 2);
assert.ok(tx.outs[0].value > 50000);
assert.strictEqual(tx.outs[1].value, 25000
);
});
it('Segwit HD (BIP49) can fetch balance with many used addresses in hierarchy', async function () {

View File

@ -202,6 +202,81 @@ describe('unit - DeepLinkSchemaMatch', function () {
},
],
},
{
argument: {
url: require('fs').readFileSync('./tests/unit/fixtures/skeleton-cobo.txt', 'ascii'),
},
expected: [
'AddWalletRoot',
{
screen: 'ImportWallet',
params: {
triggerImport: true,
label: require('fs').readFileSync('./tests/unit/fixtures/skeleton-cobo.txt', 'ascii'),
},
},
],
},
{
argument: {
url: require('fs').readFileSync('./tests/unit/fixtures/skeleton-coldcard.txt', 'ascii'),
},
expected: [
'AddWalletRoot',
{
screen: 'ImportWallet',
params: {
triggerImport: true,
label: require('fs').readFileSync('./tests/unit/fixtures/skeleton-coldcard.txt', 'ascii'),
},
},
],
},
{
argument: {
url: require('fs').readFileSync('./tests/unit/fixtures/skeleton-electrum.txt', 'ascii'),
},
expected: [
'AddWalletRoot',
{
screen: 'ImportWallet',
params: {
triggerImport: true,
label: require('fs').readFileSync('./tests/unit/fixtures/skeleton-electrum.txt', 'ascii'),
},
},
],
},
{
argument: {
url: require('fs').readFileSync('./tests/unit/fixtures/skeleton-walletdescriptor.txt', 'ascii'),
},
expected: [
'AddWalletRoot',
{
screen: 'ImportWallet',
params: {
triggerImport: true,
label: require('fs').readFileSync('./tests/unit/fixtures/skeleton-walletdescriptor.txt', 'ascii'),
},
},
],
},
{
argument: {
url: 'zpub6rFDtF1nuXZ9PUL4XzKURh3vJBW6Kj6TUrYL4qPtFNtDXtcTVfiqjQDyrZNwjwzt5HS14qdqo3Co2282Lv3Re6Y5wFZxAVuMEpeygnnDwfx',
},
expected: [
'AddWalletRoot',
{
screen: 'ImportWallet',
params: {
triggerImport: true,
label: 'zpub6rFDtF1nuXZ9PUL4XzKURh3vJBW6Kj6TUrYL4qPtFNtDXtcTVfiqjQDyrZNwjwzt5HS14qdqo3Co2282Lv3Re6Y5wFZxAVuMEpeygnnDwfx',
},
},
],
},
];
const asyncNavigationRouteFor = async function (event) {

View File

@ -0,0 +1 @@
{"ExtPubKey":"zpub6rcabYFcdr41zyUNRWRyHYs2Sm86E5XV8RjjRzTFYsiCngteeZnkwaF2xuhjmM6kpHjuNpFW42BMhzPmFwXt48e1FhddMB7xidZzN4SF24K","MasterFingerprint":"5271c071","CoboVaultFirmwareVersion":"1.2.4(BTC-Only)"}

View File

@ -0,0 +1 @@
{"keystore": {"ckcc_xpub": "xpub661MyMwAqRbcGmUDQVKxmhEESB5xTk8hbsdTSV3Pmhm3HE9Fj3s45R9Y8LwyaQWjXXPytZjuhTKSyCBPeNrB1VVWQq1HCvjbEZ27k44oNmg", "xpub": "zpub6rFDtF1nuXZ9PUL4XzKURh3vJBW6Kj6TUrYL4qPtFNtDXtcTVfiqjQDyrZNwjwzt5HS14qdqo3Co2282Lv3Re6Y5wFZxAVuMEpeygnnDwfx", "label": "Coldcard Import 168DD603", "ckcc_xfp": 64392470, "type": "hardware", "hw_type": "coldcard", "derivation": "m/84'/0'/0'"}, "wallet_type": "standard", "use_encryption": false, "seed_version": 17}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
[8cce63f8/84h/0h/0h]zpub6s2RJ9qAEBW8Abhojs6LyDzF7gttcDr6EsR3Umu2aptZBb45e734rGtt4KqsCMmNyR1EEzUU2ugdVYez2VywQvAbBjUSKn8ho4Zk2c5otkk

File diff suppressed because one or more lines are too long