Merge branch 'master' into isitmyaddressdesktop

This commit is contained in:
marcosrdz 2020-12-19 15:32:29 -05:00
commit e36f003f7b
21 changed files with 470 additions and 197 deletions

View File

@ -1397,7 +1397,7 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco
const [subtitleNumberOfLines, setSubtitleNumberOfLines] = useState(1);
const { colors } = useTheme();
const { navigate } = useNavigation();
const { txMetadata, wallets } = useContext(BlueStorageContext);
const { txMetadata, wallets, preferredFiatCurrency, language } = useContext(BlueStorageContext);
const containerStyle = useMemo(
() => ({
backgroundColor: 'transparent',
@ -1415,7 +1415,8 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco
} else {
return transactionTimeToReadable(item.received);
}
}, [item.confirmations, item.received]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [item.confirmations, item.received, language]);
const txMemo = txMetadata[item.hash]?.memo ?? '';
const subtitle = useMemo(() => {
let sub = item.confirmations < 7 ? loc.formatString(loc.transactions.list_conf, { number: item.confirmations }) : '';
@ -1446,7 +1447,8 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco
} else {
return formatBalanceWithoutSuffix(item.value && item.value, itemPriceUnit, true).toString();
}
}, [item, itemPriceUnit]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [item, itemPriceUnit, preferredFiatCurrency]);
const rowTitleStyle = useMemo(() => {
let color = colors.successColor;

View File

@ -1,5 +1,7 @@
/* eslint-disable react/prop-types */
import { useAsyncStorage } from '@react-native-community/async-storage';
import React, { createContext, useEffect, useState } from 'react';
import { AppStorage } from '../class';
const BlueApp = require('../BlueApp');
const BlueElectrum = require('./BlueElectrum');
@ -9,6 +11,10 @@ export const BlueStorageProvider = ({ children }) => {
const [pendingWallets, setPendingWallets] = useState([]);
const [selectedWallet, setSelectedWallet] = useState('');
const [walletsInitialized, setWalletsInitialized] = useState(false);
const [preferredFiatCurrency, _setPreferredFiatCurrency] = useState();
const [language, _setLanguage] = useState();
const getPreferredCurrencyAsyncStorage = useAsyncStorage(AppStorage.PREFERRED_CURRENCY).getItem;
const getLanguageAsyncStorage = useAsyncStorage(AppStorage.LANG).getItem;
const [newWalletAdded, setNewWalletAdded] = useState(false);
const saveToDisk = async () => {
BlueApp.tx_metadata = txMetadata;
@ -21,6 +27,30 @@ export const BlueStorageProvider = ({ children }) => {
setWallets(BlueApp.getWallets());
}, []);
const getPreferredCurrency = async () => {
const item = await getPreferredCurrencyAsyncStorage();
_setPreferredFiatCurrency(item);
};
const setPreferredFiatCurrency = () => {
getPreferredCurrency();
};
const getLanguage = async () => {
const item = await getLanguageAsyncStorage();
_setLanguage(item);
};
const setLanguage = () => {
getLanguage();
};
useEffect(() => {
getPreferredCurrency();
getLanguageAsyncStorage();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const resetWallets = () => {
setWallets(BlueApp.getWallets());
};
@ -149,6 +179,10 @@ export const BlueStorageProvider = ({ children }) => {
setResetOnAppUninstallTo,
isPasswordInUse,
setIsAdancedModeEnabled,
setPreferredFiatCurrency,
preferredFiatCurrency,
setLanguage,
language,
}}
>
{children}

View File

@ -1,4 +1,4 @@
import React, { useRef, useCallback, useState, useImperativeHandle, forwardRef } from 'react';
import React, { useRef, useCallback, useState, useImperativeHandle, forwardRef, useContext } from 'react';
import PropTypes from 'prop-types';
import {
ActivityIndicator,
@ -21,6 +21,8 @@ import { LightningCustodianWallet, MultisigHDWallet, PlaceholderWallet } from '.
import WalletGradient from '../class/wallet-gradient';
import { BluePrivateBalance } from '../BlueComponents';
import { BlueStorageContext } from '../blue_modules/storage-context';
const nStyles = StyleSheet.create({
root: {
marginVertical: 17,
@ -268,6 +270,7 @@ const cStyles = StyleSheet.create({
const WalletsCarousel = forwardRef((props, ref) => {
const carouselRef = useRef();
const [loading, setLoading] = useState(true);
const { preferredFiatCurrency, language } = useContext(BlueStorageContext);
const renderItem = useCallback(
({ item, index }) => (
<WalletCarouselItem
@ -278,7 +281,8 @@ const WalletsCarousel = forwardRef((props, ref) => {
onPress={props.onPress}
/>
),
[props.vertical, props.selectedWallet, props.handleLongPress, props.onPress],
// eslint-disable-next-line react-hooks/exhaustive-deps
[props.vertical, props.selectedWallet, props.handleLongPress, props.onPress, preferredFiatCurrency, language],
);
useImperativeHandle(ref, () => ({

View File

@ -0,0 +1,9 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"filename" : "bitcoin@3x.png"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -12,6 +12,18 @@
},
{
"filename" : "BlueWallet.sticker"
},
{
"filename" : "bluebeast.sticker"
},
{
"filename" : "Lightning.sticker"
},
{
"filename" : "Vault.sticker"
},
{
"filename" : "Bitcoin (Blue).sticker"
}
]
}

View File

@ -0,0 +1,9 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"filename" : "lightning@3x.png"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,9 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"filename" : "vault_main@3x.png"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,9 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"filename" : "bluebeast.png"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -232,7 +232,8 @@
"about_review": "Leave us a review",
"about_selftest": "Run self-test",
"about_sm_github": "GitHub",
"about_sm_telegram": "Telegram chat",
"about_sm_discord": "Discord Server",
"about_sm_telegram": "Telegram channel",
"about_sm_twitter": "Follow us on Twitter",
"advanced_options": "Advanced Options",
"currency": "Currency",
@ -402,6 +403,7 @@
"list_tryagain": "Try again",
"looks_like_bip38": "This looks like a password-protected private key (BIP38)",
"reorder_title": "Reorder Wallets",
"please_continue_scanning": "Please continue scanning",
"select_no_bitcoin": "There are currently no Bitcoin wallets available.",
"select_no_bitcoin_exp": "A Bitcoin wallet is required to refill Lightning wallets. Please create or import one.",
"select_wallet": "Select Wallet",

View File

@ -304,6 +304,7 @@
"cancel_explain": "Korvaamme tämän siirtotapahtuman sillä, joka maksaa sinulle ja on korkeammat siirtokulut. Tämä peruuttaa siirtotapahtuman tehokkaasti. Tätä kutsutaan RBF - Replace By Fee - Korvaa korkeammilla kuluilla.",
"cancel_no": "Tämä siirtotapahtuma ei ole vaihdettavissa",
"cancel_title": "Peruuta tämä siirtotapahtuma (RBF)",
"confirmations_lowercase": "{confirmations} vahvistukset",
"cpfp_create": "Luo",
"cpfp_exp": "Luomme toisen siirtotapahtuman, joka kuluttaa vahvistamattoman siirtotapahtuman. Kokonaiskulu on suurempi kuin alkuperäinen siirtokulu, joten sen pitäisi olla louhittu nopeammin. Tätä kutsutaan CPFP - Child Pays For Parent - Lapsi Maksaa Vanhemmalle.",
"cpfp_no_bump": "Tämä siirtotapahtuma ei ole nostettavissa",
@ -442,6 +443,7 @@
"needs": "Tarpeet",
"what_is_vault_description_number_of_vault_keys": "{m} holvin 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",
"quorum_header": "Quorum",
"of": "n",

View File

@ -8,7 +8,15 @@
"of": "{number} sur {total}",
"ok": "OK",
"storage_is_encrypted": "L'espace de stockage est chiffré. le mot de passe requis pour le déchiffrer.",
"yes": "Oui"
"allow": "Autoriser",
"dont_allow": "Ne pas autoriser",
"yes": "Oui",
"no": "Non",
"save": "Enregistrer",
"seed": "Graine",
"wallet_key": "Clé de portefeuille",
"invalid_animated_qr_code_fragment" : "Fragment du QR Code animé invalide. Veuillez essayer encore.",
"file_saved": "Le fichier ({filePath}) a été enregistré dans le dossier Téléchargements."
},
"azteco": {
"codeIs": "Votre code promo est",
@ -99,6 +107,7 @@
"lndViewInvoice": {
"additional_info": "Informations complémentaires",
"for": "A :",
"lightning_invoice": "Facture Ligthning",
"has_been_paid": "Cette requête a été réglée",
"open_direct_channel": "Ouvrir un canal direct avec ce noeud :",
"please_pay": "Veuillez payer",
@ -170,34 +179,49 @@
"details_next": "Suivant",
"details_no_maximum": "Le portefeuille sélectionné ne supporte pas le calcul automatique du solde maximum. Etes-vous sûr de vouloir utiliser ce portefeuille ?",
"details_no_multiple": "Le portefeuille sélectionné ne permet pas d'envoyer du Bitcoin à plusieurs destinataires. Etes-vous sûr de vouloir sélectionner ce portefeuille ?",
"details_no_signed_tx": "Le fichier sélectionné ne contient pas de transaction pouvant être importée.",
"details_note_placeholder": "note (optionnelle)",
"details_scan": "Scanner",
"details_total_exceeds_balance": "Le montant à envoyer excède le montant disponible.",
"details_wallet_before_tx": "Avant de créer une transaction, vous devez d'abord importer un ajouter un portefeuille Bitcoin.",
"details_wallet_selection": "Sélection du portefeuille",
"dynamic_init": "Initialisation",
"dynamic_next": "Suivant",
"dynamic_prev": "Précédent",
"dynamic_start": "Commencer",
"dynamic_stop": "Arrêter",
"fee_10m": "10min",
"fee_1d": "1j",
"fee_3h": "3h",
"fee_custom": "Personnalisé ",
"fee_fast": "Rapide",
"fee_medium": "Moyen",
"fee_replace_min": "Le taux des frais (satoshi par octet) que vous voulez payer devrais etre plus que {min} sat/octet",
"fee_satbyte": "En sat/octet",
"fee_slow": "Lent",
"header": "Envoyer",
"input_clear": "Tout effacer",
"input_done": "Terminé",
"input_paste": "Coller",
"input_total": "Total :",
"open_settings": "Ouvrir les paramètres",
"permission_camera_message": "Nous avons besoin de votre permission pour utiliser l'appareil photo",
"permission_camera_title": "Permission d'utiliser l'appareil photo",
"open_settings": "Ouvrir les paramètres",
"permission_storage_later": "Redemander Plus Tard",
"permission_storage_message": "BlueWallet a besoin de votre permission pour accéder a votre stockage pour enregistrer ce fichier.",
"permission_storage_denied_message": "BlueWallet est incapable d'enregistrer ce fichier. Veuillez ouvrir les paramètres de l'appareil et activer Permission Stockage.",
"permission_storage_title": "Permission d'accès au stockage pour BlueWallet",
"psbt_clipboard": "Copier dans le presse-papier",
"psbt_this_is_psbt": "Ceci est une Transaction Bitcoin Partiellement Signée (PSBT). Veuillez la compléter avec votre portefeuille matériel.",
"psbt_tx_export": "Exporter sous forme de fichier",
"no_tx_signing_in_progress": "Il n'y a pas de signature de transaction en cours.",
"psbt_tx_open": "Ouvrir la transaction signée",
"psbt_tx_scan": "Scanner la transaction signée",
"qr_error_no_qrcode": "L'image sélectionnée ne contient pas de QR Code",
"qr_error_no_wallet": "Le fichier sélectionné ne contient pas de portefeuille à importer.",
"success_done": "Terminé",
"txSaved": "Le fichier de transaction ({filePath}) a été enregistré dans votre dossier Téléchargements."
"txSaved": "Le fichier de transaction ({filePath}) a été enregistré dans votre dossier Téléchargements.",
"problem_with_psbt": "Problème avec PSBT"
},
"settings": {
"about": "À propos",
@ -253,34 +277,54 @@
"network_electrum": "Serveur Electrum",
"not_a_valid_uri": "URI invalide",
"notifications": "Notifications",
"open_link_in_explorer" : "Ouvrir le lien dans l'explorateur",
"password": "Mot de passe",
"password_explain": "Créer le mot de passe utilisé pour déchiffrer l'espace de stockage principal",
"passwords_do_not_match": "Les mots de passe ne correspondent pas",
"plausible_deniability": "Déni plausible...",
"privacy": "Vie privée",
"privacy_read_clipboard": "Lecture du presse-papier ",
"privacy_read_clipboard_alert": "BlueWallet affichera des raccourci pour gérer les factures et adresses trouvées dans le presse-papier.",
"privacy_system_settings": "Paramètres système",
"privacy_quickactions": "Raccourci Portefeuille",
"privacy_quickactions_explanation": "Touchez et maintenez l'icone BlueWallet sur votre écran d'accueil pour voir rapidement le solde de vos portefeuilles.",
"privacy_clipboard_explanation": "Fourni un raccourci si une adresse ou une facture est trouvée dans le presse-papier.",
"push_notifications": "Notifications push",
"retype_password": "Re-saisir votre mot de passe",
"save": "save",
"saved": "Enregistré"
"saved": "Enregistré",
"success_transaction_broadcasted" : "Succès! Votre transaction a été difusée!"
},
"notifications": {
"would_you_like_to_receive_notifications": "Voulez vous recevoir les notifications quand vous recevez des paiements entrants ?",
"no_and_dont_ask": "Non, et ne pas me redemander ",
"ask_me_later": "Me demander plus tard"
},
"transactions": {
"cancel_explain": "Nous allons remplacer cette transaction par celle où les fonds vous reviennet et a de plus hauts frais. Cela annulera la transaction. On parle de RBF - Replace By Fee.",
"cancel_no": "Cette transaction n'est pas remplaçable",
"cancel_title": "Annuler cette transaction (RBF)",
"confirmations_lowercase": "{confirmations} confirmations",
"cpfp_create": "Créer",
"cpfp_exp": "Nous allons créer une autre transaction qui dépense votre transaction non-confirmée. Les frais totaux seront supérieurs aux frais de la transaction originale, donc cela devrait être miné plus rapidement. On parle de CPFP - Child Pays For Parent.",
"cpfp_no_bump": "Cette transaction n'est pas propulsable",
"cpfp_title": "Frais de propulsion (CPFP)",
"details_balance_hide": "Cacher le solde",
"details_balance_show": "Montrer le solde",
"details_block": "Hauteur de bloc",
"details_copy": "Copier",
"details_from": "De",
"details_inputs": "Inputs",
"details_outputs": "Outputs",
"details_received": "Reçu",
"transaction_note_saved":"La note de transaction a été enregistrée avec succès.",
"details_show_in_block_explorer": "Afficher dans le \"block explorer\"",
"details_title": "Transaction",
"details_to": "À",
"details_transaction_details": "Détails de la transaction",
"enable_hw": "Ce portefeuille n'est pas utilisé en conjonction avec un portefeuille matériel. Voulez-vous permettre l'utilisation de portefeuilles matériels ?",
"list_conf": "Conf: {number}",
"pending": "En attente",
"list_title": "transactions",
"rbf_explain": "Nous allons remplacer cette transaction par celle avec des frais plus élevés. Elle devrait donc être minée plus vite. On parle de RBF - Replace By Fee.",
"rbf_title": "Frais de propulsion (RBF)",
@ -290,12 +334,14 @@
},
"wallets": {
"add_bitcoin": "Bitcoin",
"add_bitcoin_explain": "Portefeuille Bitcoin simple et puissant",
"add_create": "Créer",
"add_entropy_generated": "{gen} octets d'entropie générée",
"add_entropy_provide": "Créer de l'entropie par des jets de dé",
"add_entropy_remain": "{gen} octets d'entropie générée. Les {rem} octets restants seront obtenus auprès du générateur de nombres aléatoires du système.",
"add_import_wallet": "Importer un portefeuille",
"add_lightning": "Lightning",
"add_lightning_explain": "Pour payer avec des transactions instantanées",
"add_lndhub": "Connexion à votre LNDHub",
"add_lndhub_error": "L'adresse de noeud fournie n'est pas un noeud LNDHub valide.",
"add_lndhub_placeholder": "l'adresse de votre noeud",
@ -303,6 +349,8 @@
"add_title": "ajouter un portefeuille",
"add_wallet_name": "nom du portefeuille",
"add_wallet_type": "type",
"clipboard_bitcoin": "Vous avez une adresse bitcoin dans votre presse-papier. Voulez vous l'utiliser pour une transaction ?",
"clipboard_lightning": "Vous avez une facture ligthning dans votre presse-papier. Voulez vous l'utiliser pour une transaction ?",
"details_address": "Adresse",
"details_advanced": "Avancé",
"details_are_you_sure": "Êtes vous sur?",
@ -324,6 +372,7 @@
"details_use_with_hardware_wallet": "Utiliser avec un portefeuille matériel",
"details_wallet_updated": "Portefeuille mis à jour",
"details_yes_delete": "Oui, supprimer",
"enter_bip38_password": "Entrez le mots de passe de déchiffrement",
"export_title": "export du portefeuille",
"import_do_import": "Importer",
"import_error": "Échec de l'import. Merci, de vérifier que les données saisies sont valides.",
@ -335,6 +384,7 @@
"import_title": "importer",
"list_create_a_button": "Ajouter maintenant",
"list_create_a_wallet": "Ajouter un portefeuille",
"list_create_a_wallet_text": "Cest gratuit et vous pouvez en créer \nautant que vous voulez.",
"list_empty_txs1": "Vos transactions apparaîtront ici,",
"list_empty_txs1_lightning": "Un portefeuille Lightning devrait être utilisé pour les transactions quotidiennes. Les frais sont très bas et la vitesse est étourdissante.",
"list_empty_txs2": "Aucune pour le moment",
@ -347,14 +397,108 @@
"list_long_clipboard": "Copier depuis le presse-papier",
"list_long_scan": "Scanner le QR Code",
"list_tap_here_to_buy": "Cliquez ici pour acheter du Bitcoin",
"no_ln_wallet_error": "Avant de payer une facture Ligthning, vous devez créer un portefeuille Ligthning.",
"list_title": "portefeuilles",
"list_tryagain": "Réessayer",
"looks_like_bip38": "Ceci ressemble a une clé privée protégée par un mot de passe (BIP38)",
"reorder_title": "Trier vos portefeuilles",
"select_no_bitcoin": "Il n'y a aucun portefeuille Bitcoin disponible pour le moment.",
"select_no_bitcoin_exp": "Un portefeuille Bitcoin est nécessaire pour approvisionner les portefeuilles Lightning. Veuillez en créer ou en importer un.",
"select_wallet": "Choix du portefeuille",
"take_photo": "Prendre une photo",
"xpub_copiedToClipboard": "Copié dans le presse-papiers.",
"pull_to_refresh": "Tirer pour rafraichir",
"warning_do_not_disclose": "Attention! Ne pas divulguer",
"add_ln_wallet_first": "Vous devez d'abord ajouter un portefeuille Lightning.",
"xpub_title": "XPUB portefeuille"
},
"multisig": {
"multisig_vault": "Coffre",
"multisig_vault_explain": "Meilleur sécurité pour les gros montants",
"provide_signature": "Fournir la signature",
"vault_key": "Clé du coffre {number}",
"required_keys_out_of_total": "Clés requises par rapport au total",
"fee": "Frais: {number}",
"fee_btc": "{number} BTC",
"confirm": "Confirmer",
"header": "Envoyer",
"share": "Partager",
"view": "Vue",
"manage_keys": "Gérer les clés",
"how_many_signatures_can_bluewallet_make": "Combien de signatures BleuWallet peut faire",
"scan_or_import_file": "Scanner ou importer fichier",
"export_coordination_setup": "Exporter la configuration de coordination",
"cosign_this_transaction": "Co-signer cette transaction?",
"lets_start": "C'est parti",
"create": "Créer",
"provide_key": "Fournir la clé",
"native_segwit_title": "Pratique recommandée",
"wrapped_segwit_title": "Meilleur compatibilité",
"legacy_title": "Ancien format",
"co_sign_transaction": "Signer une transaction",
"what_is_vault": "Un coffre est un portefeuille de ",
"what_is_vault_numberOfWallets": " {m}-de-{n} multi signaures",
"what_is_vault_wallet": ".",
"vault_advanced_customize": "Paramètres du coffre",
"needs": "Il a besoin",
"what_is_vault_description_number_of_vault_keys": " {m} clés de coffre",
"what_is_vault_description_to_spend": "pour dépenser et une troisième \nque vous pouvez utiliser en backup.",
"what_is_vault_description_to_spend_other": "pour dépenser.",
"quorum": "{m} de {n} quorum",
"quorum_header": "Quorum",
"of": "de",
"wallet_type": "Type portefeuille",
"view_key": "Vue",
"invalid_mnemonics": "Cette phrase mnémonique ne semble pas valide ",
"invalid_cosigner": "Donnée co-signeur non valide",
"invalid_cosigner_format": "Co-signeur incorrecte: Ceci nest pas un co-signeur au {format} format",
"create_new_key": "Créer nouveau",
"scan_or_open_file": "Scanner ou ouvrir fichier",
"i_have_mnemonics": "J'ai une graine pour cette clé...",
"please_write_down_mnemonics": "Veuillez écrire a la main cette phrase mnémonique sur papier. Ne vous inquiétez pas, vous pouvez l'écrire plus tard.",
"i_wrote_it_down": "Ok, Je l'ai écrite sur un papier.",
"type_your_mnemonics": "Insérez une graine pour importer votre clé de coffre existante",
"this_is_cosigners_xpub": "Ceci est l'XPUB du co-signeur, prêt a être importé dans un autre portefeuille. C'est sure de le partager.",
"wallet_key_created": "Votre clé de coffre a été créé. Prenez un moment pour sauvegarder votre graine sous forme de mnémonique ",
"are_you_sure_seed_will_be_lost": "Etes Vous sûr? ",
"forget_this_seed": "Oublier cette graine et utiliser l'XPUB à la place",
"invalid_fingerprint": "L'empreinte pour cette graine ne correspond pas avec l'empreinte des co-signeurs",
"view_edit_cosigners": "Voir/Editer les co-signeurs",
"this_cosigner_is_already_imported": "Ce co-signeur a été déjà importé.",
"export_signed_psbt": "Exporter la PSBT signée",
"input_fp": "Entrer l'empreinte",
"input_fp_explain": "Passer pour utiliser celle par défaut (00000000)",
"input_path": "Saisir le chemin de dérivation",
"input_path_explain": "Passer pour utiliser celui par défaut ({default})",
"ms_help": "Aide",
"ms_help_title": "Comment les coffres Multisig marchent. Conseils et astuces",
"ms_help_text": "Un portefeuille avec plusieurs clés, pour augmenter de façon exponentielle la sécurité ou pour partager la détention.",
"ms_help_title1": "Plusieurs appareils est conseillé",
"ms_help_1": "Le coffre fonctionnera avec d'autre BlueWallet et les portefeuille compatible PSBT, comme Electrum, Specter, Coldcard, Cobo vault, etc...",
"ms_help_title2": "Edition des clés",
"ms_help_2": "Vous pouvez créer toutes les clés du coffre sur cet appareil et supprimer ou modifier ces clés ultérieurement. Avoir toutes les clés sur le même appareil offre la sécurité équivalente à celle d'un portefeuille bitcoin ordinaire.",
"ms_help_title3": "Sauvegardes coffre",
"ms_help_3": "Dans les options de portefeuille, vous trouverez votre sauvegarde Coffre et votre sauvegarde Vue-Seulement. Cette sauvegarde est comme une carte de votre portefeuille. Il est essentiel pour la récupération du portefeuille au cas où vous perdriez l'une de vos graines.",
"ms_help_title4": "importation Coffre",
"ms_help_4": "Pour importer un coffre Multi-signature, utilisez votre fichier de sauvegarde multisig et utilisez la fonction d'importation. Si vous ne disposez que de clés étendues et des graines, vous pouvez utiliser les champs d'importation individuels du flux Ajouter un coffre.\n",
"ms_help_title5": "Options avancées",
"ms_help_5": "Par défaut, BlueWallet générera un coffre 2de3. Pour créer un quorum différent ou pour changer le type d'adresse, activez les options avancées dans les paramètres."
},
"is_it_my_address": {
"title": "Est ce mon adresse?",
"owns": "{label} possède {address}",
"enter_address": "Entrez l'adresse",
"check_address": "Vérifiez l'adresse",
"no_wallet_owns_address": "Aucun des portefeuilles ne possède l'adresse fournie."
},
"cc": {
"change": "monnaie",
"coins_selected": "Pièces sélectionnées ({number})",
"empty": "Ce portefeuille ne possède aucune pièces en ce moment",
"freeze": "Geler",
"freezeLabel": "Gelé",
"header": "Controle de Pièces",
"use_coin": "Utiliser la pièces",
"tip": "Autorise la vue, l'étiquetage, le gel ou la sélection des pièces pour une gestion améliorée du portefeuille."
}
}

View File

@ -13,125 +13,131 @@ const currency = require('../blue_modules/currency');
dayjs.extend(relativeTime);
// first-time loading sequence
(async () => {
const setDateTimeLocale = async () => {
let lang = await AsyncStorage.getItem('lang');
let localeForDayJSAvailable = true;
switch (lang) {
case 'ar':
require('dayjs/locale/ar');
break;
case 'bg_bg':
lang = 'bg';
require('dayjs/locale/bg');
break;
case 'ca':
require('dayjs/locale/ca');
break;
case 'cy':
require('dayjs/locale/cy');
break;
case 'da_dk':
require('dayjs/locale/da');
break;
case 'de_de':
require('dayjs/locale/de');
break;
case 'el':
require('dayjs/locale/el');
break;
case 'es':
require('dayjs/locale/es');
break;
case 'es_419':
// es-do it is the closes one to es_419
lang = 'es-do';
require('dayjs/locale/es-do');
break;
case 'fi_fi':
require('dayjs/locale/fi');
break;
case 'fa':
require('dayjs/locale/fa');
break;
case 'fr_fr':
require('dayjs/locale/fr');
break;
case 'he':
require('dayjs/locale/he');
break;
case 'hr_hr':
require('dayjs/locale/hr');
break;
case 'hu_hu':
require('dayjs/locale/hu');
break;
case 'id_id':
require('dayjs/locale/id');
break;
case 'it':
require('dayjs/locale/it');
break;
case 'jp_jp':
lang = 'ja';
require('dayjs/locale/ja');
break;
case 'nb_no':
require('dayjs/locale/nb');
break;
case 'nl_nl':
require('dayjs/locale/nl');
break;
case 'pt_br':
lang = 'pt-br';
require('dayjs/locale/pt-br');
break;
case 'pt_pt':
lang = 'pt';
require('dayjs/locale/pt');
break;
case 'pl':
require('dayjs/locale/pl');
break;
case 'ru':
require('dayjs/locale/ru');
break;
case 'sk_sk':
require('dayjs/locale/sk');
break;
case 'sl_si':
require('dayjs/locale/sl');
break;
case 'sv_se':
require('dayjs/locale/sv');
break;
case 'th_th':
require('dayjs/locale/th');
break;
case 'tr_tr':
require('dayjs/locale/tr');
break;
case 'vi_vn':
require('dayjs/locale/vi');
break;
case 'zh_cn':
lang = 'zh-cn';
require('dayjs/locale/zh-cn');
break;
case 'zh_tw':
lang = 'zh-tw';
require('dayjs/locale/zh-tw');
break;
default:
localeForDayJSAvailable = false;
break;
}
if (localeForDayJSAvailable) {
dayjs.locale(lang.split('_')[0]);
}
};
const setLanguageLocale = async () => {
// finding out whether lang preference was saved
// For some reason using the AppStorage.LANG constant is not working. Hard coding string for now.
let lang = await AsyncStorage.getItem('lang');
const lang = await AsyncStorage.getItem('lang');
if (lang) {
strings.setLanguage(lang);
let localeForDayJSAvailable = true;
switch (lang) {
case 'ar':
require('dayjs/locale/ar');
break;
case 'bg_bg':
lang = 'bg';
require('dayjs/locale/bg');
break;
case 'ca':
require('dayjs/locale/ca');
break;
case 'cy':
require('dayjs/locale/cy');
break;
case 'da_dk':
require('dayjs/locale/da');
break;
case 'de_de':
require('dayjs/locale/de');
break;
case 'el':
require('dayjs/locale/el');
break;
case 'es':
require('dayjs/locale/es');
break;
case 'es_419':
// es-do it is the closes one to es_419
lang = 'es-do';
require('dayjs/locale/es-do');
break;
case 'fi_fi':
require('dayjs/locale/fi');
break;
case 'fa':
require('dayjs/locale/fa');
break;
case 'fr_fr':
require('dayjs/locale/fr');
break;
case 'he':
require('dayjs/locale/he');
break;
case 'hr_hr':
require('dayjs/locale/hr');
break;
case 'hu_hu':
require('dayjs/locale/hu');
break;
case 'id_id':
require('dayjs/locale/id');
break;
case 'it':
require('dayjs/locale/it');
break;
case 'jp_jp':
lang = 'ja';
require('dayjs/locale/ja');
break;
case 'nb_no':
require('dayjs/locale/nb');
break;
case 'nl_nl':
require('dayjs/locale/nl');
break;
case 'pt_br':
lang = 'pt-br';
require('dayjs/locale/pt-br');
break;
case 'pt_pt':
lang = 'pt';
require('dayjs/locale/pt');
break;
case 'pl':
require('dayjs/locale/pl');
break;
case 'ru':
require('dayjs/locale/ru');
break;
case 'sk_sk':
require('dayjs/locale/sk');
break;
case 'sl_si':
require('dayjs/locale/sl');
break;
case 'sv_se':
require('dayjs/locale/sv');
break;
case 'th_th':
require('dayjs/locale/th');
break;
case 'tr_tr':
require('dayjs/locale/tr');
break;
case 'vi_vn':
require('dayjs/locale/vi');
break;
case 'zh_cn':
lang = 'zh-cn';
require('dayjs/locale/zh-cn');
break;
case 'zh_tw':
lang = 'zh-tw';
require('dayjs/locale/zh-tw');
break;
default:
localeForDayJSAvailable = false;
break;
}
if (localeForDayJSAvailable) {
dayjs.locale(lang.split('_')[0]);
}
await setDateTimeLocale();
} else {
const locales = RNLocalize.getLocales();
if (Object.keys(AvailableLanguages).some(language => language === locales[0])) {
@ -142,7 +148,8 @@ dayjs.extend(relativeTime);
strings.setLanguage('en');
}
}
})();
};
setLanguageLocale();
const strings = new Localization({
en: require('./en.json'),
@ -184,7 +191,11 @@ const strings = new Localization({
zh_tw: require('./zh_tw.json'),
});
strings.saveLanguage = lang => AsyncStorage.setItem(AppStorage.LANG, lang);
strings.saveLanguage = async lang => {
await AsyncStorage.setItem(AppStorage.LANG, lang);
strings.setLanguage(lang);
await setDateTimeLocale();
};
export const transactionTimeToReadable = time => {
if (time === 0) {

View File

@ -70,7 +70,7 @@ const styles = StyleSheet.create({
position: 'absolute',
},
backdoorInputWrapper: { position: 'absolute', left: '5%', top: '0%', width: '90%', height: '70%', backgroundColor: 'white' },
progressWrapper: { position: 'absolute', right: '50%', top: '50%', backgroundColor: 'rgba(255, 255, 255, 0.1)' },
progressWrapper: { position: 'absolute', alignSelf: 'center', alignItems: 'center', top: '50%', padding: 8, borderRadius: 8 },
backdoorInput: {
height: '50%',
marginTop: 5,
@ -105,6 +105,7 @@ const ScanQRCode = () => {
openSettingsContainer: {
backgroundColor: colors.brandingColor,
},
progressWrapper: { backgroundColor: colors.brandingColor, borderColor: colors.foregroundColor, borderWidth: 4 },
});
const HashIt = function (s) {
return createHash('sha256').update(s).digest().toString('hex');
@ -270,13 +271,13 @@ const ScanQRCode = () => {
</TouchableOpacity>
)}
{urTotal > 0 && (
<View style={styles.progressWrapper} testID="UrProgressBar">
<View style={[styles.progressWrapper, stylesHook.progressWrapper]} testID="UrProgressBar">
<BlueText>{loc.wallets.please_continue_scanning}</BlueText>
<BlueText>
{urHave} / {urTotal}
</BlueText>
</View>
)}
{backdoorVisible && (
<View style={styles.backdoorInputWrapper}>
<BlueText>Provide QR code contents manually:</BlueText>

View File

@ -1,5 +1,5 @@
import React from 'react';
import { ScrollView, Linking, Image, View, Text, StyleSheet, useWindowDimensions } from 'react-native';
import { TouchableOpacity, ScrollView, Linking, Image, View, Text, StyleSheet, useWindowDimensions } from 'react-native';
import { useNavigation, useTheme } from '@react-navigation/native';
import {
BlueTextCentered,
@ -10,6 +10,7 @@ import {
BlueListItem,
BlueNavigationStyle,
} from '../../BlueComponents';
import { Icon } from 'react-native-elements';
import { getApplicationName, getVersion, getBundleId, getBuildNumber } from 'react-native-device-info';
import Rate, { AndroidMarket } from 'react-native-rate';
import loc from '../../loc';
@ -53,6 +54,18 @@ const About = () => {
paddingTop: 0,
borderRadius: 8,
},
buttonLink :{
backgroundColor: colors.lightButton,
borderRadius: 12,
justifyContent: 'center',
padding: 8,
flexDirection: 'row',
},
textLink :{
color: colors.foregroundColor,
marginLeft: 8,
fontWeight: '600',
},
});
const handleOnReleaseNotesPress = () => {
@ -71,14 +84,16 @@ const About = () => {
Linking.openURL('https://twitter.com/bluewalletio');
};
const handleOnGithubPress = () => {
Linking.openURL('https://github.com/BlueWallet/BlueWallet');
const handleOnDiscordPress = () => {
Linking.openURL('https://discord.gg/btWq2Aby2z');
};
const handleOnTelegramPress = () => {
Linking.openURL('https://t.me/bluewallet');
Linking.openURL('https://t.me/bluewallethat');
};
const handleOnGithubPress = () => {
Linking.openURL('https://github.com/BlueWallet/BlueWallet');
};
const handleOnRatePress = () => {
const options = {
AppleAppID: '1376878040',
@ -126,12 +141,12 @@ const About = () => {
/>
<BlueListItem
leftIcon={{
name: 'github',
type: 'font-awesome',
color: colors.foregroundColor,
name: 'discord',
type: 'font-awesome-5',
color: '#7289da',
}}
onPress={handleOnGithubPress}
title={loc.settings.about_sm_github}
onPress={handleOnDiscordPress}
title={loc.settings.about_sm_discord}
/>
<BlueCard>
<View style={styles.buildWith}>
@ -143,6 +158,15 @@ const About = () => {
<BlueTextCentered>bitcoinjs-lib</BlueTextCentered>
<BlueTextCentered>Nodejs</BlueTextCentered>
<BlueTextCentered>Electrum server</BlueTextCentered>
<BlueSpacing20 />
<TouchableOpacity
onPress={handleOnGithubPress}
style={styles.buttonLink}
>
<Icon size={22} name="github" type="font-awesome-5" color={colors.foregroundColor} />
<Text style={styles.textLink}>{loc.settings.about_sm_github}</Text>
</TouchableOpacity>
</View>
</BlueCard>
<BlueListItem

View File

@ -1,15 +1,17 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useContext } from 'react';
import { FlatList, ActivityIndicator, View, StyleSheet } from 'react-native';
import { SafeBlueArea, BlueListItem, BlueText, BlueCard, BlueNavigationStyle } from '../../BlueComponents';
import PropTypes from 'prop-types';
import { FiatUnit, FiatUnitSource } from '../../models/fiatUnit';
import loc from '../../loc';
import { useTheme } from '@react-navigation/native';
import { BlueStorageContext } from '../../blue_modules/storage-context';
const currency = require('../../blue_modules/currency');
const data = Object.values(FiatUnit);
const Currency = () => {
const { setPreferredFiatCurrency } = useContext(BlueStorageContext);
const [isSavingNewPreferredCurrency, setIsSavingNewPreferredCurrency] = useState(false);
const [selectedCurrency, setSelectedCurrency] = useState(null);
const { colors } = useTheme();
@ -62,6 +64,7 @@ const Currency = () => {
await currency.setPrefferedCurrency(item);
await currency.startUpdater();
setIsSavingNewPreferredCurrency(false);
setPreferredFiatCurrency();
}}
/>
);

View File

@ -1,9 +1,12 @@
import React, { useState, useEffect, useCallback } from 'react';
import React, { useState, useEffect, useContext } from 'react';
import { FlatList, StyleSheet } from 'react-native';
import { SafeBlueArea, BlueListItem, BlueCard, BlueLoading, BlueNavigationStyle, BlueText } from '../../BlueComponents';
import { SafeBlueArea, BlueListItem, BlueLoading, BlueNavigationStyle } from '../../BlueComponents';
import { AvailableLanguages } from '../../loc/languages';
import loc from '../../loc';
import { BlueStorageContext } from '../../blue_modules/storage-context';
import { useNavigation, useTheme } from '@react-navigation/native';
const styles = StyleSheet.create({
flex: {
flex: 1,
@ -11,42 +14,50 @@ const styles = StyleSheet.create({
});
const Language = () => {
const { setLanguage, language } = useContext(BlueStorageContext);
const [isLoading, setIsLoading] = useState(true);
const [language, setLanguage] = useState(loc.getLanguage());
const [selectedLanguage, setSelectedLanguage] = useState(loc.getLanguage());
const { setOptions } = useNavigation();
const { colors } = useTheme();
const stylesHook = StyleSheet.create({
flex: {
backgroundColor: colors.background,
},
});
useEffect(() => {
setIsLoading(false);
}, []);
const renderItem = useCallback(
({ item }) => {
return (
<BlueListItem
onPress={() => {
console.log('setLanguage', item.value);
loc.saveLanguage(item.value);
setLanguage(item.value);
}}
title={item.label}
{...(language === item.value
? {
rightIcon: { name: 'check', type: 'octaicon', color: '#0070FF' },
}
: { hideChevron: true })}
/>
);
},
[language],
);
useEffect(() => {
setOptions({ headerTitle: loc.settings.language });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [language]);
const renderItem = item => {
return (
<BlueListItem
onPress={async () => {
await loc.saveLanguage(item.item.value);
setSelectedLanguage(item.item.value);
setLanguage();
}}
title={item.item.label}
checkmark={selectedLanguage === item.item.value}
/>
);
};
return isLoading ? (
<BlueLoading />
) : (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.flex}>
<FlatList style={styles.flex} keyExtractor={(_item, index) => `${index}`} data={AvailableLanguages} renderItem={renderItem} />
<BlueCard>
<BlueText>{loc.settings.language_restart}</BlueText>
</BlueCard>
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={[styles.flex, stylesHook.flex]}>
<FlatList
style={[styles.flex, stylesHook.flex]}
keyExtractor={(_item, index) => `${index}`}
data={AvailableLanguages}
renderItem={renderItem}
initialNumToRender={25}
/>
</SafeBlueArea>
);
};

View File

@ -1,8 +1,9 @@
import React from 'react';
import { ScrollView, TouchableOpacity, StyleSheet, StatusBar } from 'react-native';
import React, { useContext } from 'react';
import { ScrollView, StyleSheet, StatusBar } from 'react-native';
import { BlueListItem, BlueNavigationStyle, BlueHeaderDefaultSubHooks } from '../../BlueComponents';
import { useNavigation } from '@react-navigation/native';
import loc from '../../loc';
import { BlueStorageContext } from '../../blue_modules/storage-context';
const styles = StyleSheet.create({
root: {
@ -12,36 +13,22 @@ const styles = StyleSheet.create({
const Settings = () => {
const { navigate } = useNavigation();
// By simply having it here, it'll re-render the UI if language is changed
// eslint-disable-next-line no-unused-vars
const { language } = useContext(BlueStorageContext);
return (
<ScrollView style={styles.root}>
<StatusBar barStyle="default" />
<BlueHeaderDefaultSubHooks leftText={loc.settings.header} rightComponent={null} />
<BlueListItem title={loc.settings.general} component={TouchableOpacity} onPress={() => navigate('GeneralSettings')} chevron />
<BlueListItem title={loc.settings.currency} component={TouchableOpacity} onPress={() => navigate('Currency')} chevron />
<BlueListItem title={loc.settings.language} component={TouchableOpacity} onPress={() => navigate('Language')} chevron />
<BlueListItem
title={loc.settings.encrypt_title}
onPress={() => navigate('EncryptStorage')}
component={TouchableOpacity}
testID="SecurityButton"
chevron
/>
<BlueListItem title={loc.settings.network} component={TouchableOpacity} onPress={() => navigate('NetworkSettings')} chevron />
<BlueListItem
title={loc.settings.notifications}
component={TouchableOpacity}
onPress={() => navigate('NotificationSettings')}
chevron
/>
<BlueListItem title={loc.settings.privacy} component={TouchableOpacity} onPress={() => navigate('SettingsPrivacy')} chevron />
<BlueListItem
title={loc.settings.about}
component={TouchableOpacity}
onPress={() => navigate('About')}
testID="AboutButton"
chevron
/>
<BlueListItem title={loc.settings.general} onPress={() => navigate('GeneralSettings')} chevron />
<BlueListItem title={loc.settings.currency} onPress={() => navigate('Currency')} chevron />
<BlueListItem title={loc.settings.language} onPress={() => navigate('Language')} chevron />
<BlueListItem title={loc.settings.encrypt_title} onPress={() => navigate('EncryptStorage')} testID="SecurityButton" chevron />
<BlueListItem title={loc.settings.network} onPress={() => navigate('NetworkSettings')} chevron />
<BlueListItem title={loc.settings.notifications} onPress={() => navigate('NotificationSettings')} chevron />
<BlueListItem title={loc.settings.privacy} onPress={() => navigate('SettingsPrivacy')} chevron />
<BlueListItem title={loc.settings.about} onPress={() => navigate('About')} testID="AboutButton" chevron />
</ScrollView>
);
};