mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-03-13 19:16:52 +01:00
Merge branch 'master' into contr
This commit is contained in:
commit
fdd2b66d8e
101 changed files with 2000 additions and 1696 deletions
|
@ -40,9 +40,16 @@ export const BlueCard = props => {
|
|||
return <View {...props} style={{ padding: 20 }} />;
|
||||
};
|
||||
|
||||
export const BlueText = props => {
|
||||
export const BlueText = ({ bold = false, ...props }) => {
|
||||
const { colors } = useTheme();
|
||||
const style = StyleSheet.compose({ color: colors.foregroundColor, writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr' }, props.style);
|
||||
const style = StyleSheet.compose(
|
||||
{
|
||||
color: colors.foregroundColor,
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
fontWeight: bold ? 'bold' : 'normal',
|
||||
},
|
||||
props.style,
|
||||
);
|
||||
return <Text {...props} style={style} />;
|
||||
};
|
||||
|
||||
|
|
1
Gemfile
1
Gemfile
|
@ -7,5 +7,6 @@ gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1'
|
|||
gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0'
|
||||
gem "fastlane", ">= 2.225.0"
|
||||
gem 'xcodeproj', '< 1.26.0'
|
||||
gem 'concurrent-ruby', '< 1.3.4'
|
||||
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
|
||||
eval_gemfile(plugins_path) if File.exist?(plugins_path)
|
||||
|
|
18
Gemfile.lock
18
Gemfile.lock
|
@ -25,16 +25,17 @@ GEM
|
|||
artifactory (3.0.17)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.3.0)
|
||||
aws-partitions (1.1042.0)
|
||||
aws-sdk-core (3.217.0)
|
||||
aws-partitions (1.1050.0)
|
||||
aws-sdk-core (3.218.1)
|
||||
aws-eventstream (~> 1, >= 1.3.0)
|
||||
aws-partitions (~> 1, >= 1.992.0)
|
||||
aws-sigv4 (~> 1.9)
|
||||
base64
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.97.0)
|
||||
aws-sdk-kms (1.98.0)
|
||||
aws-sdk-core (~> 3, >= 3.216.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sdk-s3 (1.178.0)
|
||||
aws-sdk-s3 (1.180.0)
|
||||
aws-sdk-core (~> 3, >= 3.216.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.5)
|
||||
|
@ -86,10 +87,10 @@ GEM
|
|||
colored2 (3.1.2)
|
||||
commander (4.6.0)
|
||||
highline (~> 2.0.0)
|
||||
concurrent-ruby (1.3.5)
|
||||
concurrent-ruby (1.3.3)
|
||||
connection_pool (2.5.0)
|
||||
declarative (0.0.20)
|
||||
digest-crc (0.6.5)
|
||||
digest-crc (0.7.0)
|
||||
rake (>= 12.0.0, < 14.0.0)
|
||||
domain_name (0.6.20240107)
|
||||
dotenv (2.8.1)
|
||||
|
@ -223,14 +224,14 @@ GEM
|
|||
i18n (1.14.7)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jmespath (1.6.2)
|
||||
json (2.9.1)
|
||||
json (2.10.1)
|
||||
jwt (2.10.1)
|
||||
base64
|
||||
logger (1.6.5)
|
||||
mime-types (3.6.0)
|
||||
logger
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2025.0107)
|
||||
mime-types-data (3.2025.0204)
|
||||
mini_magick (4.13.2)
|
||||
mini_mime (1.1.5)
|
||||
minitest (5.25.4)
|
||||
|
@ -306,6 +307,7 @@ PLATFORMS
|
|||
DEPENDENCIES
|
||||
activesupport (>= 6.1.7.5, != 7.1.0)
|
||||
cocoapods (>= 1.13, != 1.15.1, != 1.15.0)
|
||||
concurrent-ruby (< 1.3.4)
|
||||
fastlane (>= 2.225.0)
|
||||
fastlane-plugin-browserstack
|
||||
fastlane-plugin-bugsnag_sourcemaps_upload
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
import 'react-native-gesture-handler'; // should be on top
|
||||
|
||||
import React, { lazy, Suspense } from 'react';
|
||||
import MainRoot from './navigation';
|
||||
import { useStorage } from './hooks/context/useStorage';
|
||||
const CompanionDelegates = lazy(() => import('./components/CompanionDelegates'));
|
||||
|
||||
const MasterView = () => {
|
||||
const { walletsInitialized } = useStorage();
|
||||
|
||||
return (
|
||||
<>
|
||||
<MainRoot />
|
||||
{walletsInitialized && (
|
||||
<Suspense>
|
||||
<CompanionDelegates />
|
||||
</Suspense>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MasterView;
|
|
@ -83,7 +83,7 @@ android {
|
|||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "7.1.0"
|
||||
versionName "7.1.1"
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
}
|
||||
|
|
|
@ -138,31 +138,40 @@ async function _getRealm() {
|
|||
}
|
||||
|
||||
export const getPreferredServer = async (): Promise<ElectrumServerItem | undefined> => {
|
||||
await DefaultPreference.setName(GROUP_IO_BLUEWALLET);
|
||||
const host = (await DefaultPreference.get(ELECTRUM_HOST)) as string;
|
||||
const tcpPort = await DefaultPreference.get(ELECTRUM_TCP_PORT);
|
||||
const sslPort = await DefaultPreference.get(ELECTRUM_SSL_PORT);
|
||||
try {
|
||||
await DefaultPreference.setName(GROUP_IO_BLUEWALLET);
|
||||
const host = (await DefaultPreference.get(ELECTRUM_HOST)) as string;
|
||||
const tcpPort = await DefaultPreference.get(ELECTRUM_TCP_PORT);
|
||||
const sslPort = await DefaultPreference.get(ELECTRUM_SSL_PORT);
|
||||
|
||||
console.log('Getting preferred server:', { host, tcpPort, sslPort });
|
||||
console.log('Getting preferred server:', { host, tcpPort, sslPort });
|
||||
|
||||
if (!host) {
|
||||
console.warn('Preferred server host is undefined');
|
||||
return;
|
||||
if (!host) {
|
||||
console.warn('Preferred server host is undefined');
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
host,
|
||||
tcp: tcpPort ? Number(tcpPort) : undefined,
|
||||
ssl: sslPort ? Number(sslPort) : undefined,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error in getPreferredServer:', error);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
host,
|
||||
tcp: tcpPort ? Number(tcpPort) : undefined,
|
||||
ssl: sslPort ? Number(sslPort) : undefined,
|
||||
};
|
||||
};
|
||||
|
||||
export const removePreferredServer = async () => {
|
||||
await DefaultPreference.setName(GROUP_IO_BLUEWALLET);
|
||||
console.log('Removing preferred server');
|
||||
await DefaultPreference.clear(ELECTRUM_HOST);
|
||||
await DefaultPreference.clear(ELECTRUM_TCP_PORT);
|
||||
await DefaultPreference.clear(ELECTRUM_SSL_PORT);
|
||||
try {
|
||||
await DefaultPreference.setName(GROUP_IO_BLUEWALLET);
|
||||
console.log('Removing preferred server');
|
||||
await DefaultPreference.clear(ELECTRUM_HOST);
|
||||
await DefaultPreference.clear(ELECTRUM_TCP_PORT);
|
||||
await DefaultPreference.clear(ELECTRUM_SSL_PORT);
|
||||
} catch (error) {
|
||||
console.error('Error in removePreferredServer:', error);
|
||||
}
|
||||
};
|
||||
|
||||
export async function isDisabled(): Promise<boolean> {
|
||||
|
@ -204,26 +213,31 @@ function getNextPeer() {
|
|||
}
|
||||
|
||||
async function getSavedPeer(): Promise<Peer | null> {
|
||||
await DefaultPreference.setName(GROUP_IO_BLUEWALLET);
|
||||
const host = (await DefaultPreference.get(ELECTRUM_HOST)) as string;
|
||||
const tcpPort = await DefaultPreference.get(ELECTRUM_TCP_PORT);
|
||||
const sslPort = await DefaultPreference.get(ELECTRUM_SSL_PORT);
|
||||
try {
|
||||
await DefaultPreference.setName(GROUP_IO_BLUEWALLET);
|
||||
const host = (await DefaultPreference.get(ELECTRUM_HOST)) as string;
|
||||
const tcpPort = await DefaultPreference.get(ELECTRUM_TCP_PORT);
|
||||
const sslPort = await DefaultPreference.get(ELECTRUM_SSL_PORT);
|
||||
|
||||
console.log('Getting saved peer:', { host, tcpPort, sslPort });
|
||||
console.log('Getting saved peer:', { host, tcpPort, sslPort });
|
||||
|
||||
if (!host) {
|
||||
if (!host) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (sslPort) {
|
||||
return { host, ssl: Number(sslPort) };
|
||||
}
|
||||
|
||||
if (tcpPort) {
|
||||
return { host, tcp: Number(tcpPort) };
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error('Error in getSavedPeer:', error);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (sslPort) {
|
||||
return { host, ssl: Number(sslPort) };
|
||||
}
|
||||
|
||||
if (tcpPort) {
|
||||
return { host, tcp: Number(tcpPort) };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function connectMain(): Promise<void> {
|
||||
|
@ -239,19 +253,6 @@ export async function connectMain(): Promise<void> {
|
|||
|
||||
console.log('Using peer:', JSON.stringify(usingPeer));
|
||||
|
||||
await DefaultPreference.setName(GROUP_IO_BLUEWALLET);
|
||||
try {
|
||||
if (usingPeer.host.endsWith('onion')) {
|
||||
const randomPeer = getCurrentPeer();
|
||||
await DefaultPreference.set(ELECTRUM_HOST, randomPeer.host);
|
||||
await DefaultPreference.set(ELECTRUM_TCP_PORT, randomPeer.tcp ?? '');
|
||||
await DefaultPreference.set(ELECTRUM_SSL_PORT, randomPeer.ssl ?? '');
|
||||
}
|
||||
} catch (e) {
|
||||
// Must be running on Android
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('begin connection:', JSON.stringify(usingPeer));
|
||||
mainClient = new ElectrumClient(net, tls, usingPeer.ssl || usingPeer.tcp, usingPeer.host, usingPeer.ssl ? 'tls' : 'tcp');
|
||||
|
@ -262,7 +263,8 @@ export async function connectMain(): Promise<void> {
|
|||
// most likely got a timeout from electrum ping. lets reconnect
|
||||
// but only if we were previously connected (mainConnected), otherwise theres other
|
||||
// code which does connection retries
|
||||
mainClient.close();
|
||||
mainClient?.close();
|
||||
mainClient = undefined;
|
||||
mainConnected = false;
|
||||
// dropping `mainConnected` flag ensures there wont be reconnection race condition if several
|
||||
// errors triggered
|
||||
|
@ -310,12 +312,15 @@ export async function connectMain(): Promise<void> {
|
|||
} catch (e) {
|
||||
mainConnected = false;
|
||||
console.log('bad connection:', JSON.stringify(usingPeer), e);
|
||||
mainClient?.close();
|
||||
mainClient = undefined;
|
||||
}
|
||||
|
||||
if (!mainConnected) {
|
||||
console.log('retry');
|
||||
connectionAttempt = connectionAttempt + 1;
|
||||
mainClient.close && mainClient.close();
|
||||
mainClient?.close();
|
||||
mainClient = undefined;
|
||||
if (connectionAttempt >= 5) {
|
||||
presentNetworkErrorAlert(usingPeer);
|
||||
} else {
|
||||
|
@ -407,7 +412,8 @@ const presentNetworkErrorAlert = async (usingPeer?: Peer) => {
|
|||
text: loc.wallets.list_tryagain,
|
||||
onPress: () => {
|
||||
connectionAttempt = 0;
|
||||
mainClient.close() && mainClient.close();
|
||||
mainClient?.close();
|
||||
mainClient = undefined;
|
||||
setTimeout(connectMain, 500);
|
||||
},
|
||||
style: 'default',
|
||||
|
@ -418,7 +424,8 @@ const presentNetworkErrorAlert = async (usingPeer?: Peer) => {
|
|||
presentResetToDefaultsAlert().then(result => {
|
||||
if (result) {
|
||||
connectionAttempt = 0;
|
||||
mainClient.close() && mainClient.close();
|
||||
mainClient?.close();
|
||||
mainClient = undefined;
|
||||
setTimeout(connectMain, 500);
|
||||
}
|
||||
});
|
||||
|
@ -429,7 +436,8 @@ const presentNetworkErrorAlert = async (usingPeer?: Peer) => {
|
|||
text: loc._.cancel,
|
||||
onPress: () => {
|
||||
connectionAttempt = 0;
|
||||
mainClient.close() && mainClient.close();
|
||||
mainClient?.close();
|
||||
mainClient = undefined;
|
||||
},
|
||||
style: 'cancel',
|
||||
},
|
||||
|
@ -474,13 +482,18 @@ async function getRandomDynamicPeer(): Promise<Peer> {
|
|||
}
|
||||
|
||||
export const getBalanceByAddress = async function (address: string): Promise<{ confirmed: number; unconfirmed: number }> {
|
||||
if (!mainClient) throw new Error('Electrum client is not connected');
|
||||
const script = bitcoin.address.toOutputScript(address);
|
||||
const hash = bitcoin.crypto.sha256(script);
|
||||
const reversedHash = Buffer.from(hash).reverse();
|
||||
const balance = await mainClient.blockchainScripthash_getBalance(reversedHash.toString('hex'));
|
||||
balance.addr = address;
|
||||
return balance;
|
||||
try {
|
||||
if (!mainClient) throw new Error('Electrum client is not connected');
|
||||
const script = bitcoin.address.toOutputScript(address);
|
||||
const hash = bitcoin.crypto.sha256(script);
|
||||
const reversedHash = Buffer.from(hash).reverse();
|
||||
const balance = await mainClient.blockchainScripthash_getBalance(reversedHash.toString('hex'));
|
||||
balance.addr = address;
|
||||
return balance;
|
||||
} catch (error) {
|
||||
console.error('Error in getBalanceByAddress:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const getConfig = async function () {
|
||||
|
@ -958,25 +971,29 @@ export async function multiGetTransactionByTxid<T extends boolean>(
|
|||
}
|
||||
|
||||
// saving cache:
|
||||
realm.write(() => {
|
||||
for (const txid of Object.keys(ret)) {
|
||||
const tx = ret[txid];
|
||||
// dont cache immature txs, but only for 'verbose', since its fully decoded tx jsons. non-verbose are just plain
|
||||
// strings txhex
|
||||
if (verbose && typeof tx !== 'string' && (!tx?.confirmations || tx.confirmations < 7)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
realm.write(() => {
|
||||
for (const txid of Object.keys(ret)) {
|
||||
const tx = ret[txid];
|
||||
// dont cache immature txs, but only for 'verbose', since its fully decoded tx jsons. non-verbose are just plain
|
||||
// strings txhex
|
||||
if (verbose && typeof tx !== 'string' && (!tx?.confirmations || tx.confirmations < 7)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
realm.create(
|
||||
'Cache',
|
||||
{
|
||||
cache_key: txid + cacheKeySuffix,
|
||||
cache_value: JSON.stringify(ret[txid]),
|
||||
},
|
||||
Realm.UpdateMode.Modified,
|
||||
);
|
||||
}
|
||||
});
|
||||
realm.create(
|
||||
'Cache',
|
||||
{
|
||||
cache_key: txid + cacheKeySuffix,
|
||||
cache_value: JSON.stringify(ret[txid]),
|
||||
},
|
||||
Realm.UpdateMode.Modified,
|
||||
);
|
||||
}
|
||||
});
|
||||
} catch (writeError) {
|
||||
console.error('Failed to write transaction cache:', writeError);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -183,23 +183,11 @@ export const showFilePickerAndReadFile = async function (): Promise<{ data: stri
|
|||
if (res.fileCopyUri.toLowerCase().endsWith('.psbt')) {
|
||||
// this is either binary file from ElectrumDesktop OR string file with base64 string in there
|
||||
const file = await _readPsbtFileIntoBase64(fileCopyUri);
|
||||
return { data: file, uri: decodeURI(res.fileCopyUri) };
|
||||
return { data: file, uri: fileCopyUri };
|
||||
}
|
||||
|
||||
if (res.type === DocumentPicker.types.images || res.type?.startsWith('image/')) {
|
||||
try {
|
||||
const uri2 = res.fileCopyUri.replace('file://', '');
|
||||
const result = await RNQRGenerator.detect({ uri: decodeURI(uri2) });
|
||||
if (result) {
|
||||
return { data: result.values[0], uri: fileCopyUri };
|
||||
}
|
||||
presentAlert({ message: loc.send.qr_error_no_qrcode });
|
||||
return { data: false, uri: false };
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
presentAlert({ message: loc.send.qr_error_no_qrcode });
|
||||
return { data: false, uri: false };
|
||||
}
|
||||
return await handleImageFile(fileCopyUri);
|
||||
}
|
||||
|
||||
const file = await RNFS.readFile(fileCopyUri);
|
||||
|
@ -212,6 +200,33 @@ export const showFilePickerAndReadFile = async function (): Promise<{ data: stri
|
|||
}
|
||||
};
|
||||
|
||||
const handleImageFile = async (fileCopyUri: string): Promise<{ data: string | false; uri: string | false }> => {
|
||||
try {
|
||||
const exists = await RNFS.exists(fileCopyUri);
|
||||
if (!exists) {
|
||||
presentAlert({ message: 'File does not exist' });
|
||||
return { data: false, uri: false };
|
||||
}
|
||||
// First attempt: use original URI
|
||||
let result = await RNQRGenerator.detect({ uri: decodeURI(fileCopyUri) });
|
||||
if (result?.values && result.values.length > 0) {
|
||||
return { data: result.values[0], uri: fileCopyUri };
|
||||
}
|
||||
// Second attempt: remove file:// prefix and try again
|
||||
const altUri = fileCopyUri.replace(/^file:\/\//, '');
|
||||
result = await RNQRGenerator.detect({ uri: decodeURI(altUri) });
|
||||
if (result?.values && result.values.length > 0) {
|
||||
return { data: result.values[0], uri: fileCopyUri };
|
||||
}
|
||||
presentAlert({ message: loc.send.qr_error_no_qrcode });
|
||||
return { data: false, uri: false };
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
presentAlert({ message: loc.send.qr_error_no_qrcode });
|
||||
return { data: false, uri: false };
|
||||
}
|
||||
};
|
||||
|
||||
export const readFileOutsideSandbox = (filePath: string) => {
|
||||
if (Platform.OS === 'ios') {
|
||||
return readFile(filePath);
|
||||
|
|
|
@ -574,6 +574,9 @@ export const isNotificationsEnabled = async () => {
|
|||
return !isDisabledByUser && !!token && !!levels.level_all;
|
||||
} catch (error) {
|
||||
console.log('Error checking notification levels:', error);
|
||||
if (error instanceof SyntaxError) {
|
||||
throw error;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import bip21, { TOptions } from 'bip21';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
import URL from 'url';
|
||||
|
||||
import { readFileOutsideSandbox } from '../blue_modules/fs';
|
||||
import { Chain } from '../models/bitcoinUnits';
|
||||
import { WatchOnlyWallet } from './';
|
||||
|
|
|
@ -310,8 +310,11 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
// then we combine it all together
|
||||
|
||||
const addresses2fetch = [];
|
||||
// Store these values to avoid a race condition if fetchBalance func changes them
|
||||
const next_free_address_index = this.next_free_address_index;
|
||||
const next_free_change_address_index = this.next_free_change_address_index;
|
||||
|
||||
for (let c = 0; c < this.next_free_address_index + this.gap_limit; c++) {
|
||||
for (let c = 0; c < next_free_address_index + this.gap_limit; c++) {
|
||||
// external addresses first
|
||||
let hasUnconfirmed = false;
|
||||
this._txs_by_external_index[c] = this._txs_by_external_index[c] || [];
|
||||
|
@ -322,7 +325,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
}
|
||||
}
|
||||
|
||||
for (let c = 0; c < this.next_free_change_address_index + this.gap_limit; c++) {
|
||||
for (let c = 0; c < next_free_change_address_index + this.gap_limit; c++) {
|
||||
// next, internal addresses
|
||||
let hasUnconfirmed = false;
|
||||
this._txs_by_internal_index[c] = this._txs_by_internal_index[c] || [];
|
||||
|
@ -389,10 +392,10 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
|
||||
// now purge all unconfirmed txs from internal hashmaps, since some may be evicted from mempool because they became invalid
|
||||
// or replaced. hashmaps are going to be re-populated anyways, since we fetched TXs for addresses with unconfirmed TXs
|
||||
for (let c = 0; c < this.next_free_address_index + this.gap_limit; c++) {
|
||||
for (let c = 0; c < next_free_address_index + this.gap_limit; c++) {
|
||||
this._txs_by_external_index[c] = this._txs_by_external_index[c].filter(tx => !!tx.confirmations);
|
||||
}
|
||||
for (let c = 0; c < this.next_free_change_address_index + this.gap_limit; c++) {
|
||||
for (let c = 0; c < next_free_change_address_index + this.gap_limit; c++) {
|
||||
this._txs_by_internal_index[c] = this._txs_by_internal_index[c].filter(tx => !!tx.confirmations);
|
||||
}
|
||||
for (const pc of this._receive_payment_codes) {
|
||||
|
@ -404,7 +407,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
// now, we need to put transactions in all relevant `cells` of internal hashmaps:
|
||||
// this._txs_by_internal_index, this._txs_by_external_index & this._txs_by_payment_code_index
|
||||
|
||||
for (let c = 0; c < this.next_free_address_index + this.gap_limit; c++) {
|
||||
for (let c = 0; c < next_free_address_index + this.gap_limit; c++) {
|
||||
for (const tx of Object.values(txdatas)) {
|
||||
for (const vin of tx.vin) {
|
||||
if (vin.addresses && vin.addresses.indexOf(this._getExternalAddressByIndex(c)) !== -1) {
|
||||
|
@ -445,7 +448,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
}
|
||||
}
|
||||
|
||||
for (let c = 0; c < this.next_free_change_address_index + this.gap_limit; c++) {
|
||||
for (let c = 0; c < next_free_change_address_index + this.gap_limit; c++) {
|
||||
for (const tx of Object.values(txdatas)) {
|
||||
for (const vin of tx.vin) {
|
||||
if (vin.addresses && vin.addresses.indexOf(this._getInternalAddressByIndex(c)) !== -1) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import b58 from 'bs58check';
|
||||
import createHash from 'create-hash';
|
||||
import wif from 'wif';
|
||||
|
||||
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
||||
import { CreateTransactionResult, CreateTransactionUtxo, Transaction, Utxo } from './types';
|
||||
|
@ -211,6 +212,17 @@ export class AbstractWallet {
|
|||
|
||||
setSecret(newSecret: string): this {
|
||||
const origSecret = newSecret;
|
||||
|
||||
// is it minikey https://en.bitcoin.it/wiki/Mini_private_key_format
|
||||
// Starts with S, is 22 length or larger, is base58
|
||||
if (newSecret.startsWith('S') && newSecret.length >= 22 && /^[1-9A-HJ-NP-Za-km-z]+$/.test(newSecret)) {
|
||||
// minikey + ? hashed with SHA256 starts with 0x00 byte
|
||||
if (createHash('sha256').update(`${newSecret}?`).digest('hex').startsWith('00')) {
|
||||
// it is a valid minikey
|
||||
newSecret = wif.encode(0x80, createHash('sha256').update(newSecret).digest(), false);
|
||||
}
|
||||
}
|
||||
|
||||
this.secret = newSecret.trim().replace('bitcoin:', '').replace('BITCOIN:', '');
|
||||
|
||||
if (this.secret.startsWith('BC1')) this.secret = this.secret.toLowerCase();
|
||||
|
|
|
@ -11,13 +11,11 @@ interface AddressInputProps {
|
|||
address?: string;
|
||||
placeholder?: string;
|
||||
onChangeText: (text: string) => void;
|
||||
onBarScanned: (ret: { data?: any }) => void;
|
||||
scanButtonTapped?: () => void;
|
||||
launchedBy?: string;
|
||||
editable?: boolean;
|
||||
inputAccessoryViewID?: string;
|
||||
onBlur?: () => void;
|
||||
onFocus?: () => void;
|
||||
onBlur?: () => void;
|
||||
testID?: string;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
keyboardType?:
|
||||
|
@ -34,6 +32,7 @@ interface AddressInputProps {
|
|||
| 'twitter'
|
||||
| 'web-search'
|
||||
| 'visible-password';
|
||||
skipValidation?: boolean;
|
||||
}
|
||||
|
||||
const AddressInput = ({
|
||||
|
@ -42,15 +41,14 @@ const AddressInput = ({
|
|||
testID = 'AddressInput',
|
||||
placeholder = loc.send.details_address,
|
||||
onChangeText,
|
||||
onBarScanned,
|
||||
scanButtonTapped = () => {},
|
||||
launchedBy,
|
||||
editable = true,
|
||||
inputAccessoryViewID,
|
||||
onBlur = () => {},
|
||||
onFocus = () => {},
|
||||
onBlur = () => {},
|
||||
keyboardType = 'default',
|
||||
style,
|
||||
skipValidation = false,
|
||||
}: AddressInputProps) => {
|
||||
const { colors } = useTheme();
|
||||
const stylesHook = StyleSheet.create({
|
||||
|
@ -64,21 +62,26 @@ const AddressInput = ({
|
|||
},
|
||||
});
|
||||
|
||||
const validateAddressWithFeedback = useCallback((value: string) => {
|
||||
const isBitcoinAddress = DeeplinkSchemaMatch.isBitcoinAddress(value);
|
||||
const isLightningInvoice = DeeplinkSchemaMatch.isLightningInvoice(value);
|
||||
const isValid = isBitcoinAddress || isLightningInvoice;
|
||||
const validateAddressWithFeedback = useCallback(
|
||||
(value: string) => {
|
||||
if (skipValidation) return;
|
||||
const isBitcoinAddress = DeeplinkSchemaMatch.isBitcoinAddress(value);
|
||||
const isLightningInvoice = DeeplinkSchemaMatch.isLightningInvoice(value);
|
||||
const isValid = isBitcoinAddress || isLightningInvoice;
|
||||
|
||||
triggerHapticFeedback(isValid ? HapticFeedbackTypes.NotificationSuccess : HapticFeedbackTypes.NotificationError);
|
||||
return {
|
||||
isValid,
|
||||
type: isBitcoinAddress ? 'bitcoin' : isLightningInvoice ? 'lightning' : 'invalid',
|
||||
};
|
||||
}, []);
|
||||
triggerHapticFeedback(isValid ? HapticFeedbackTypes.NotificationSuccess : HapticFeedbackTypes.NotificationError);
|
||||
return {
|
||||
isValid,
|
||||
type: isBitcoinAddress ? 'bitcoin' : isLightningInvoice ? 'lightning' : 'invalid',
|
||||
};
|
||||
},
|
||||
[skipValidation],
|
||||
);
|
||||
|
||||
const onBlurEditing = () => {
|
||||
validateAddressWithFeedback(address);
|
||||
onBlur();
|
||||
if (!skipValidation) {
|
||||
validateAddressWithFeedback(address);
|
||||
}
|
||||
Keyboard.dismiss();
|
||||
};
|
||||
|
||||
|
@ -95,21 +98,13 @@ const AddressInput = ({
|
|||
multiline={!editable}
|
||||
inputAccessoryViewID={inputAccessoryViewID}
|
||||
clearButtonMode="while-editing"
|
||||
onBlur={onBlurEditing}
|
||||
onFocus={onFocus}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
keyboardType={keyboardType}
|
||||
{...(skipValidation ? { onBlur } : { onBlur: onBlurEditing })}
|
||||
/>
|
||||
{editable ? (
|
||||
<AddressInputScanButton
|
||||
isLoading={isLoading}
|
||||
launchedBy={launchedBy}
|
||||
scanButtonTapped={scanButtonTapped}
|
||||
onBarScanned={onBarScanned}
|
||||
onChangeText={onChangeText}
|
||||
/>
|
||||
) : null}
|
||||
{editable ? <AddressInputScanButton isLoading={isLoading} scanButtonTapped={scanButtonTapped} onChangeText={onChangeText} /> : null}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { Image, Keyboard, Platform, StyleSheet, Text } from 'react-native';
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
import ToolTipMenu from './TooltipMenu';
|
||||
|
@ -9,33 +9,19 @@ import { useTheme } from './themes';
|
|||
import RNQRGenerator from 'rn-qr-generator';
|
||||
import { CommonToolTipActions } from '../typings/CommonToolTipActions';
|
||||
import { useSettings } from '../hooks/context/useSettings';
|
||||
import { useRoute } from '@react-navigation/native';
|
||||
import { useExtendedNavigation } from '../hooks/useExtendedNavigation';
|
||||
|
||||
interface AddressInputScanButtonProps {
|
||||
isLoading: boolean;
|
||||
launchedBy?: string;
|
||||
scanButtonTapped: () => void;
|
||||
onBarScanned: (ret: { data?: any }) => void;
|
||||
onChangeText: (text: string) => void;
|
||||
}
|
||||
|
||||
interface RouteParams {
|
||||
onBarScanned?: any;
|
||||
}
|
||||
|
||||
export const AddressInputScanButton = ({
|
||||
isLoading,
|
||||
launchedBy,
|
||||
scanButtonTapped,
|
||||
onBarScanned,
|
||||
onChangeText,
|
||||
}: AddressInputScanButtonProps) => {
|
||||
export const AddressInputScanButton = ({ isLoading, scanButtonTapped, onChangeText }: AddressInputScanButtonProps) => {
|
||||
const { colors } = useTheme();
|
||||
const { isClipboardGetContentEnabled } = useSettings();
|
||||
|
||||
const navigation = useExtendedNavigation();
|
||||
const params = useRoute().params as RouteParams;
|
||||
const stylesHook = StyleSheet.create({
|
||||
scan: {
|
||||
backgroundColor: colors.scanLabel,
|
||||
|
@ -67,17 +53,8 @@ export const AddressInputScanButton = ({
|
|||
return availableActions;
|
||||
}, [isClipboardGetContentEnabled]);
|
||||
|
||||
useEffect(() => {
|
||||
const data = params.onBarScanned;
|
||||
if (data) {
|
||||
onBarScanned({ data });
|
||||
navigation.setParams({ onBarScanned: undefined });
|
||||
}
|
||||
});
|
||||
|
||||
const onMenuItemPressed = useCallback(
|
||||
async (action: string) => {
|
||||
if (onBarScanned === undefined) throw new Error('onBarScanned is required');
|
||||
switch (action) {
|
||||
case CommonToolTipActions.ScanQR.id:
|
||||
scanButtonTapped();
|
||||
|
@ -147,7 +124,7 @@ export const AddressInputScanButton = ({
|
|||
}
|
||||
Keyboard.dismiss();
|
||||
},
|
||||
[navigation, onBarScanned, onChangeText, scanButtonTapped],
|
||||
[navigation, onChangeText, scanButtonTapped],
|
||||
);
|
||||
|
||||
const buttonStyle = useMemo(() => [styles.scan, stylesHook.scan], [stylesHook.scan]);
|
||||
|
|
|
@ -146,7 +146,9 @@ class AmountInput extends Component {
|
|||
textInput = React.createRef();
|
||||
|
||||
handleTextInputOnPress = () => {
|
||||
this.textInput.current.focus();
|
||||
if (this.textInput && this.textInput.current && typeof this.textInput.current.focus === 'function') {
|
||||
this.textInput.current.focus();
|
||||
}
|
||||
};
|
||||
|
||||
handleChangeText = text => {
|
||||
|
@ -258,7 +260,11 @@ class AmountInput extends Component {
|
|||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.enter_amount}
|
||||
disabled={this.props.pointerEvents === 'none'}
|
||||
onPress={() => this.textInput.focus()}
|
||||
onPress={() => {
|
||||
if (this.textInput && this.textInput.current && typeof this.textInput.current.focus === 'function') {
|
||||
this.textInput.current.focus();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<View style={styles.root}>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState, useRef } from 'react';
|
||||
import { Animated, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import { Animated, Platform, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import { Camera, CameraApi, CameraType, Orientation } from 'react-native-camera-kit';
|
||||
import loc from '../loc';
|
||||
import { Icon } from '@rneui/base';
|
||||
|
@ -46,8 +46,8 @@ const CameraScreen: React.FC<CameraScreenProps> = ({
|
|||
// For real phone apps, lock your UI orientation using a library like 'react-native-orientation-locker'
|
||||
const rotateUi = true;
|
||||
const uiRotation = orientationAnim.interpolate({
|
||||
inputRange: [1, 4],
|
||||
outputRange: ['180deg', '-90deg'],
|
||||
inputRange: [1, 2, 3, 4],
|
||||
outputRange: ['180deg', '90deg', '0deg', '-90deg'],
|
||||
});
|
||||
const uiRotationStyle = rotateUi ? { transform: [{ rotate: uiRotation }] } : {};
|
||||
|
||||
|
@ -96,9 +96,13 @@ const CameraScreen: React.FC<CameraScreenProps> = ({
|
|||
return (
|
||||
<View style={styles.screen}>
|
||||
<View style={styles.topButtons}>
|
||||
<TouchableOpacity style={styles.topButton} onPress={onSetTorch}>
|
||||
<Animated.View style={[styles.topButtonImg, uiRotationStyle]}>
|
||||
<Icon name={torchMode ? 'flashlight-on' : 'flashlight-off'} type="font-awesome-6" color="#ffffff" />
|
||||
<TouchableOpacity style={[styles.topButton, uiRotationStyle, torchMode ? styles.activeTorch : {}]} onPress={onSetTorch}>
|
||||
<Animated.View style={styles.topButtonImg}>
|
||||
{Platform.OS === 'ios' ? (
|
||||
<Icon name={torchMode ? 'flashlight-on' : 'flashlight-off'} type="font-awesome-6" color={torchMode ? '#000' : '#fff'} />
|
||||
) : (
|
||||
<Icon name={torchMode ? 'flash-on' : 'flash-off'} type="ionicons" color={torchMode ? '#000' : '#fff'} />
|
||||
)}
|
||||
</Animated.View>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.rightButtonsContainer}>
|
||||
|
@ -109,7 +113,7 @@ const CameraScreen: React.FC<CameraScreenProps> = ({
|
|||
style={[styles.topButton, styles.spacing, uiRotationStyle]}
|
||||
onPress={onImagePickerButtonPress}
|
||||
>
|
||||
<Animated.View style={[styles.topButtonImg, uiRotationStyle]}>
|
||||
<Animated.View style={styles.topButtonImg}>
|
||||
<Icon name="image" type="font-awesome" color="#ffffff" />
|
||||
</Animated.View>
|
||||
</TouchableOpacity>
|
||||
|
@ -121,7 +125,7 @@ const CameraScreen: React.FC<CameraScreenProps> = ({
|
|||
style={[styles.topButton, styles.spacing, uiRotationStyle]}
|
||||
onPress={onFilePickerButtonPress}
|
||||
>
|
||||
<Animated.View style={[styles.topButtonImg, uiRotationStyle]}>
|
||||
<Animated.View style={styles.topButtonImg}>
|
||||
<Icon name="file-import" type="font-awesome-5" color="#ffffff" />
|
||||
</Animated.View>
|
||||
</TouchableOpacity>
|
||||
|
@ -142,8 +146,6 @@ const CameraScreen: React.FC<CameraScreenProps> = ({
|
|||
onZoom={handleZoom}
|
||||
onReadCode={handleReadCode}
|
||||
torchMode={torchMode ? 'on' : 'off'}
|
||||
shutterPhotoSound
|
||||
maxPhotoQualityPrioritization="quality"
|
||||
onOrientationChange={handleOrientationChange}
|
||||
/>
|
||||
</View>
|
||||
|
@ -152,9 +154,13 @@ const CameraScreen: React.FC<CameraScreenProps> = ({
|
|||
<TouchableOpacity onPress={onCancelButtonPress}>
|
||||
<Animated.Text style={[styles.backTextStyle, uiRotationStyle]}>{loc._.cancel}</Animated.Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={styles.bottomButton} onPress={onSwitchCameraPressed}>
|
||||
<TouchableOpacity style={[styles.bottomButton, uiRotationStyle]} onPress={onSwitchCameraPressed}>
|
||||
<Animated.View style={[styles.topButtonImg, uiRotationStyle]}>
|
||||
<Icon name="cameraswitch" type="font-awesome-6" color="#ffffff" />
|
||||
{Platform.OS === 'ios' ? (
|
||||
<Icon name="cameraswitch" type="font-awesome-6" color="#ffffff" />
|
||||
) : (
|
||||
<Icon name={cameraType === CameraType.Back ? 'camera-rear' : 'camera-front'} type="ionicons" color="#ffffff" />
|
||||
)}
|
||||
</Animated.View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
@ -165,6 +171,9 @@ const CameraScreen: React.FC<CameraScreenProps> = ({
|
|||
export default CameraScreen;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
activeTorch: {
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
screen: {
|
||||
height: '100%',
|
||||
backgroundColor: '#000000',
|
||||
|
|
|
@ -175,40 +175,45 @@ const CompanionDelegates = () => {
|
|||
|
||||
const handleOpenURL = useCallback(
|
||||
async (event: { url: string }): Promise<void> => {
|
||||
const { url } = event;
|
||||
|
||||
if (url) {
|
||||
const decodedUrl = decodeURIComponent(url);
|
||||
const fileName = decodedUrl.split('/').pop()?.toLowerCase();
|
||||
|
||||
if (fileName && /\.(jpe?g|png)$/i.test(fileName)) {
|
||||
try {
|
||||
if (!event.url) return;
|
||||
let decodedUrl: string;
|
||||
try {
|
||||
decodedUrl = decodeURIComponent(event.url);
|
||||
} catch (e) {
|
||||
console.error('Failed to decode URL, using original', e);
|
||||
decodedUrl = event.url;
|
||||
}
|
||||
const fileName = decodedUrl.split('/').pop()?.toLowerCase() || '';
|
||||
if (/\.(jpe?g|png)$/i.test(fileName)) {
|
||||
let qrResult;
|
||||
try {
|
||||
if (!decodedUrl) {
|
||||
throw new Error(loc.send.qr_error_no_qrcode);
|
||||
qrResult = await RNQRGenerator.detect({ uri: decodedUrl });
|
||||
} catch (e) {
|
||||
console.error('QR detection first attempt failed:', e);
|
||||
}
|
||||
if (!qrResult || !qrResult.values || qrResult.values.length === 0) {
|
||||
const altUrl = decodedUrl.replace(/^file:\/\//, '');
|
||||
try {
|
||||
qrResult = await RNQRGenerator.detect({ uri: altUrl });
|
||||
} catch (e) {
|
||||
console.error('QR detection second attempt failed:', e);
|
||||
}
|
||||
const values = await RNQRGenerator.detect({
|
||||
uri: decodedUrl,
|
||||
});
|
||||
|
||||
if (values && values.values.length > 0) {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
DeeplinkSchemaMatch.navigationRouteFor(
|
||||
{ url: values.values[0] },
|
||||
(value: [string, any]) => navigationRef.navigate(...value),
|
||||
{
|
||||
wallets,
|
||||
addWallet,
|
||||
saveToDisk,
|
||||
setSharedCosigner,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
throw new Error(loc.send.qr_error_no_qrcode);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error detecting QR code:', error);
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
presentAlert({ message: loc.send.qr_error_no_qrcode });
|
||||
}
|
||||
if (qrResult?.values?.length) {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
DeeplinkSchemaMatch.navigationRouteFor(
|
||||
{ url: qrResult.values[0] },
|
||||
(value: [string, any]) => navigationRef.navigate(...value),
|
||||
{
|
||||
wallets,
|
||||
addWallet,
|
||||
saveToDisk,
|
||||
setSharedCosigner,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
throw new Error(loc.send.qr_error_no_qrcode);
|
||||
}
|
||||
} else {
|
||||
DeeplinkSchemaMatch.navigationRouteFor(event, (value: [string, any]) => navigationRef.navigate(...value), {
|
||||
|
@ -218,10 +223,15 @@ const CompanionDelegates = () => {
|
|||
setSharedCosigner,
|
||||
});
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('Error in handleOpenURL:', err);
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
presentAlert({ message: err.message || loc.send.qr_error_no_qrcode });
|
||||
}
|
||||
},
|
||||
[wallets, addWallet, saveToDisk, setSharedCosigner],
|
||||
);
|
||||
|
||||
const showClipboardAlert = useCallback(
|
||||
({ contentType }: { contentType: undefined | string }) => {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.ImpactLight);
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import React, { createContext, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { InteractionManager } from 'react-native';
|
||||
import { InteractionManager, LayoutAnimation } from 'react-native';
|
||||
import A from '../../blue_modules/analytics';
|
||||
import { BlueApp as BlueAppClass, LegacyWallet, TCounterpartyMetadata, TTXMetadata, WatchOnlyWallet } from '../../class';
|
||||
import type { TWallet } from '../../class/wallets/types';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import loc from '../../loc';
|
||||
import loc, { formatBalanceWithoutSuffix } from '../../loc';
|
||||
import * as BlueElectrum from '../../blue_modules/BlueElectrum';
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
|
||||
import { startAndDecrypt } from '../../blue_modules/start-and-decrypt';
|
||||
import { majorTomToGroundControl } from '../../blue_modules/notifications';
|
||||
import { isNotificationsEnabled, majorTomToGroundControl, unsubscribe } from '../../blue_modules/notifications';
|
||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
|
||||
const BlueApp = BlueAppClass.getInstance();
|
||||
|
||||
|
@ -49,6 +50,8 @@ interface StorageContextType {
|
|||
cachedPassword: typeof BlueApp.cachedPassword;
|
||||
getItem: typeof BlueApp.getItem;
|
||||
setItem: typeof BlueApp.setItem;
|
||||
handleWalletDeletion: (walletID: string, forceDelete?: boolean) => Promise<boolean>;
|
||||
confirmWalletDeletion: (wallet: any, onConfirmed: () => void) => void;
|
||||
}
|
||||
|
||||
export enum WalletTransactionsStatus {
|
||||
|
@ -99,6 +102,120 @@ export const StorageProvider = ({ children }: { children: React.ReactNode }) =>
|
|||
setWallets([...BlueApp.getWallets()]);
|
||||
}, []);
|
||||
|
||||
const handleWalletDeletion = useCallback(
|
||||
async (walletID: string, forceDelete = false): Promise<boolean> => {
|
||||
console.debug(`handleWalletDeletion: invoked for walletID ${walletID}`);
|
||||
const wallet = wallets.find(w => w.getID() === walletID);
|
||||
if (!wallet) {
|
||||
console.warn(`handleWalletDeletion: wallet not found for ${walletID}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (forceDelete) {
|
||||
deleteWallet(wallet);
|
||||
await saveToDisk(true);
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
return true;
|
||||
}
|
||||
|
||||
let isNotificationsSettingsEnabled = false;
|
||||
try {
|
||||
isNotificationsSettingsEnabled = await isNotificationsEnabled();
|
||||
} catch (error) {
|
||||
console.error(`handleWalletDeletion: error checking notifications for wallet ${walletID}`, error);
|
||||
return await new Promise<boolean>(resolve => {
|
||||
presentAlert({
|
||||
title: loc.errors.error,
|
||||
message: loc.wallets.details_delete_wallet_error_message,
|
||||
buttons: [
|
||||
{
|
||||
text: loc.wallets.details_delete_anyway,
|
||||
onPress: async () => {
|
||||
const result = await handleWalletDeletion(walletID, true);
|
||||
resolve(result);
|
||||
},
|
||||
style: 'destructive',
|
||||
},
|
||||
{
|
||||
text: loc.wallets.list_tryagain,
|
||||
onPress: async () => {
|
||||
const result = await handleWalletDeletion(walletID);
|
||||
resolve(result);
|
||||
},
|
||||
},
|
||||
{
|
||||
text: loc._.cancel,
|
||||
onPress: () => resolve(false),
|
||||
style: 'cancel',
|
||||
},
|
||||
],
|
||||
options: { cancelable: false },
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
if (isNotificationsSettingsEnabled) {
|
||||
const externalAddresses = wallet.getAllExternalAddresses();
|
||||
if (externalAddresses.length > 0) {
|
||||
console.debug(`handleWalletDeletion: unsubscribing addresses for wallet ${walletID}`);
|
||||
try {
|
||||
await unsubscribe(externalAddresses, [], []);
|
||||
console.debug(`handleWalletDeletion: unsubscribe succeeded for wallet ${walletID}`);
|
||||
} catch (unsubscribeError) {
|
||||
console.error(`handleWalletDeletion: unsubscribe failed for wallet ${walletID}`, unsubscribeError);
|
||||
presentAlert({
|
||||
title: loc.errors.error,
|
||||
message: loc.wallets.details_delete_wallet_error_message,
|
||||
buttons: [{ text: loc._.ok, onPress: () => {} }],
|
||||
options: { cancelable: false },
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
deleteWallet(wallet);
|
||||
console.debug(`handleWalletDeletion: wallet ${walletID} deleted successfully`);
|
||||
await saveToDisk(true);
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
return true;
|
||||
} catch (e: unknown) {
|
||||
console.error(`handleWalletDeletion: encountered error for wallet ${walletID}`, e);
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
return await new Promise<boolean>(resolve => {
|
||||
presentAlert({
|
||||
title: loc.errors.error,
|
||||
message: loc.wallets.details_delete_wallet_error_message,
|
||||
buttons: [
|
||||
{
|
||||
text: loc.wallets.details_delete_anyway,
|
||||
onPress: async () => {
|
||||
const result = await handleWalletDeletion(walletID, true);
|
||||
resolve(result);
|
||||
},
|
||||
style: 'destructive',
|
||||
},
|
||||
{
|
||||
text: loc.wallets.list_tryagain,
|
||||
onPress: async () => {
|
||||
const result = await handleWalletDeletion(walletID);
|
||||
resolve(result);
|
||||
},
|
||||
},
|
||||
{
|
||||
text: loc._.cancel,
|
||||
onPress: () => resolve(false),
|
||||
style: 'cancel',
|
||||
},
|
||||
],
|
||||
options: { cancelable: false },
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
[deleteWallet, saveToDisk, wallets],
|
||||
);
|
||||
|
||||
const resetWallets = useCallback(() => {
|
||||
setWallets(BlueApp.getWallets());
|
||||
}, []);
|
||||
|
@ -120,56 +237,71 @@ export const StorageProvider = ({ children }: { children: React.ReactNode }) =>
|
|||
}
|
||||
}, [walletsInitialized]);
|
||||
|
||||
// Add a refresh lock to prevent concurrent refreshes
|
||||
const refreshingRef = useRef<boolean>(false);
|
||||
|
||||
const refreshAllWalletTransactions = useCallback(
|
||||
async (lastSnappedTo?: number, showUpdateStatusIndicator: boolean = true) => {
|
||||
if (refreshingRef.current) {
|
||||
console.debug('[refreshAllWalletTransactions] Refresh already in progress');
|
||||
return;
|
||||
}
|
||||
console.debug('[refreshAllWalletTransactions] Starting refreshAllWalletTransactions');
|
||||
refreshingRef.current = true;
|
||||
const TIMEOUT_DURATION = 30000;
|
||||
|
||||
const timeoutPromise = new Promise<never>((_resolve, reject) =>
|
||||
setTimeout(() => {
|
||||
reject(new Error('refreshAllWalletTransactions: Timeout reached'));
|
||||
console.debug('[refreshAllWalletTransactions] Timeout reached');
|
||||
reject(new Error('Timeout reached'));
|
||||
}, TIMEOUT_DURATION),
|
||||
);
|
||||
|
||||
const mainLogicPromise = new Promise<void>((resolve, reject) => {
|
||||
InteractionManager.runAfterInteractions(async () => {
|
||||
let noErr = true;
|
||||
try {
|
||||
await BlueElectrum.waitTillConnected();
|
||||
if (showUpdateStatusIndicator) {
|
||||
setWalletTransactionUpdateStatus(WalletTransactionsStatus.ALL);
|
||||
}
|
||||
const paymentCodesStart = Date.now();
|
||||
await BlueApp.fetchSenderPaymentCodes(lastSnappedTo);
|
||||
const paymentCodesEnd = Date.now();
|
||||
console.debug('fetch payment codes took', (paymentCodesEnd - paymentCodesStart) / 1000, 'sec');
|
||||
try {
|
||||
if (showUpdateStatusIndicator) {
|
||||
console.debug('[refreshAllWalletTransactions] Setting wallet transaction status to ALL');
|
||||
setWalletTransactionUpdateStatus(WalletTransactionsStatus.ALL);
|
||||
}
|
||||
console.debug('[refreshAllWalletTransactions] Waiting for connectivity...');
|
||||
await BlueElectrum.waitTillConnected();
|
||||
console.debug('[refreshAllWalletTransactions] Connected to Electrum');
|
||||
|
||||
// Restore fetch payment codes timing measurement
|
||||
if (typeof BlueApp.fetchSenderPaymentCodes === 'function') {
|
||||
const codesStart = Date.now();
|
||||
console.debug('[refreshAllWalletTransactions] Fetching sender payment codes');
|
||||
await BlueApp.fetchSenderPaymentCodes(lastSnappedTo);
|
||||
const codesEnd = Date.now();
|
||||
console.debug('[refreshAllWalletTransactions] fetch payment codes took', (codesEnd - codesStart) / 1000, 'sec');
|
||||
} else {
|
||||
console.warn('[refreshAllWalletTransactions] fetchSenderPaymentCodes is not available');
|
||||
}
|
||||
|
||||
console.debug('[refreshAllWalletTransactions] Fetching wallet balances and transactions');
|
||||
await Promise.race([
|
||||
(async () => {
|
||||
const balanceStart = Date.now();
|
||||
await BlueApp.fetchWalletBalances(lastSnappedTo);
|
||||
const balanceEnd = Date.now();
|
||||
console.debug('fetch balance took', (balanceEnd - balanceStart) / 1000, 'sec');
|
||||
console.debug('[refreshAllWalletTransactions] fetch balance took', (balanceEnd - balanceStart) / 1000, 'sec');
|
||||
|
||||
const start = Date.now();
|
||||
const txStart = Date.now();
|
||||
await BlueApp.fetchWalletTransactions(lastSnappedTo);
|
||||
const end = Date.now();
|
||||
console.debug('fetch tx took', (end - start) / 1000, 'sec');
|
||||
} catch (err) {
|
||||
noErr = false;
|
||||
console.error(err);
|
||||
reject(err);
|
||||
} finally {
|
||||
setWalletTransactionUpdateStatus(WalletTransactionsStatus.NONE);
|
||||
}
|
||||
if (noErr) await saveToDisk();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
const txEnd = Date.now();
|
||||
console.debug('[refreshAllWalletTransactions] fetch tx took', (txEnd - txStart) / 1000, 'sec');
|
||||
|
||||
try {
|
||||
await Promise.race([mainLogicPromise, timeoutPromise]);
|
||||
} catch (err) {
|
||||
console.error('Error in refreshAllWalletTransactions:', err);
|
||||
console.debug('[refreshAllWalletTransactions] Saving data to disk');
|
||||
await saveToDisk();
|
||||
})(),
|
||||
|
||||
timeoutPromise,
|
||||
]);
|
||||
console.debug('[refreshAllWalletTransactions] Refresh completed successfully');
|
||||
} catch (error) {
|
||||
console.error('[refreshAllWalletTransactions] Error in refreshAllWalletTransactions:', error);
|
||||
} finally {
|
||||
console.debug('[refreshAllWalletTransactions] Resetting wallet transaction status and refresh lock');
|
||||
setWalletTransactionUpdateStatus(WalletTransactionsStatus.NONE);
|
||||
refreshingRef.current = false;
|
||||
}
|
||||
},
|
||||
[saveToDisk],
|
||||
|
@ -182,24 +314,26 @@ export const StorageProvider = ({ children }: { children: React.ReactNode }) =>
|
|||
let noErr = true;
|
||||
try {
|
||||
if (Date.now() - (_lastTimeTriedToRefetchWallet[walletID] || 0) < 5000) {
|
||||
console.debug('Re-fetch wallet happens too fast; NOP');
|
||||
console.debug('[fetchAndSaveWalletTransactions] Re-fetch wallet happens too fast; NOP');
|
||||
return;
|
||||
}
|
||||
_lastTimeTriedToRefetchWallet[walletID] = Date.now();
|
||||
|
||||
await BlueElectrum.waitTillConnected();
|
||||
setWalletTransactionUpdateStatus(walletID);
|
||||
|
||||
const balanceStart = Date.now();
|
||||
await BlueApp.fetchWalletBalances(index);
|
||||
const balanceEnd = Date.now();
|
||||
console.debug('fetch balance took', (balanceEnd - balanceStart) / 1000, 'sec');
|
||||
const start = Date.now();
|
||||
console.debug('[fetchAndSaveWalletTransactions] fetch balance took', (balanceEnd - balanceStart) / 1000, 'sec');
|
||||
|
||||
const txStart = Date.now();
|
||||
await BlueApp.fetchWalletTransactions(index);
|
||||
const end = Date.now();
|
||||
console.debug('fetch tx took', (end - start) / 1000, 'sec');
|
||||
const txEnd = Date.now();
|
||||
console.debug('[fetchAndSaveWalletTransactions] fetch tx took', (txEnd - txStart) / 1000, 'sec');
|
||||
} catch (err) {
|
||||
noErr = false;
|
||||
console.error(err);
|
||||
console.error('[fetchAndSaveWalletTransactions] Error:', err);
|
||||
} finally {
|
||||
setWalletTransactionUpdateStatus(WalletTransactionsStatus.NONE);
|
||||
}
|
||||
|
@ -239,6 +373,36 @@ export const StorageProvider = ({ children }: { children: React.ReactNode }) =>
|
|||
[wallets, addWallet, saveToDisk],
|
||||
);
|
||||
|
||||
function confirmWalletDeletion(wallet: any, onConfirmed: () => void) {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationWarning);
|
||||
try {
|
||||
const balance = formatBalanceWithoutSuffix(wallet.getBalance(), BitcoinUnit.SATS, true);
|
||||
presentAlert({
|
||||
title: loc.wallets.details_delete_wallet,
|
||||
message: loc.formatString(loc.wallets.details_del_wb_q, { balance }),
|
||||
buttons: [
|
||||
{
|
||||
text: loc.wallets.details_delete,
|
||||
onPress: () => {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
onConfirmed();
|
||||
},
|
||||
style: 'destructive',
|
||||
},
|
||||
{
|
||||
text: loc._.cancel,
|
||||
onPress: () => {},
|
||||
style: 'cancel',
|
||||
},
|
||||
],
|
||||
options: { cancelable: false },
|
||||
});
|
||||
} catch (error) {
|
||||
// Handle error silently if needed
|
||||
}
|
||||
}
|
||||
|
||||
const value: StorageContextType = useMemo(
|
||||
() => ({
|
||||
wallets,
|
||||
|
@ -274,6 +438,8 @@ export const StorageProvider = ({ children }: { children: React.ReactNode }) =>
|
|||
isPasswordInUse: BlueApp.isPasswordInUse,
|
||||
walletTransactionUpdateStatus,
|
||||
setWalletTransactionUpdateStatus,
|
||||
handleWalletDeletion,
|
||||
confirmWalletDeletion,
|
||||
}),
|
||||
[
|
||||
wallets,
|
||||
|
@ -292,6 +458,7 @@ export const StorageProvider = ({ children }: { children: React.ReactNode }) =>
|
|||
resetWallets,
|
||||
walletTransactionUpdateStatus,
|
||||
setWalletTransactionUpdateStatus,
|
||||
handleWalletDeletion,
|
||||
],
|
||||
);
|
||||
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
import React, { forwardRef, ReactNode, useEffect, useRef, useState } from 'react';
|
||||
import React, { forwardRef, ReactNode, useEffect, useRef, useState, useCallback } from 'react';
|
||||
import { Animated, Dimensions, PixelRatio, StyleSheet, Text, TouchableOpacity, useWindowDimensions, View } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
import { useTheme } from './themes';
|
||||
|
||||
const BORDER_RADIUS = 8;
|
||||
const PADDINGS = 24;
|
||||
const ICON_MARGIN = 7;
|
||||
|
||||
const cStyles = StyleSheet.create({
|
||||
const buttonFontSize = (() => {
|
||||
const baseSize = PixelRatio.roundToNearestPixel(Dimensions.get('window').width / 26);
|
||||
return Math.min(22, baseSize);
|
||||
})();
|
||||
|
||||
const containerStyles = StyleSheet.create({
|
||||
root: {
|
||||
alignSelf: 'center',
|
||||
height: '6.9%',
|
||||
|
@ -26,6 +30,27 @@ const cStyles = StyleSheet.create({
|
|||
flexDirection: 'row',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
rootRound: {
|
||||
borderRadius: 9999,
|
||||
},
|
||||
});
|
||||
|
||||
const buttonStyles = StyleSheet.create({
|
||||
root: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
icon: {
|
||||
alignItems: 'center',
|
||||
},
|
||||
text: {
|
||||
fontSize: buttonFontSize,
|
||||
fontWeight: '600',
|
||||
marginLeft: ICON_MARGIN,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
});
|
||||
|
||||
interface FContainerProps {
|
||||
|
@ -51,93 +76,79 @@ export const FContainer = forwardRef<View, FContainerProps>((props, ref) => {
|
|||
}).start();
|
||||
}, [height, slideAnimation]);
|
||||
|
||||
const computeNewWidth = useCallback(
|
||||
(layoutWidth: number, totalChildren: number) => {
|
||||
const maxWidth = width - BORDER_RADIUS - 140;
|
||||
const paddedWidth = Math.ceil(layoutWidth + PADDINGS * 2);
|
||||
let calculatedWidth = paddedWidth * totalChildren > maxWidth ? Math.floor(maxWidth / totalChildren) : paddedWidth;
|
||||
if (totalChildren === 1 && calculatedWidth < 90) calculatedWidth = 90;
|
||||
return calculatedWidth;
|
||||
},
|
||||
[width],
|
||||
);
|
||||
|
||||
const onLayout = (event: { nativeEvent: { layout: { width: number } } }) => {
|
||||
if (layoutCalculated.current) return;
|
||||
const maxWidth = width - BORDER_RADIUS - 140;
|
||||
const layoutWidth = event.nativeEvent.layout.width;
|
||||
const withPaddings = Math.ceil(layoutWidth + PADDINGS * 2);
|
||||
const len = React.Children.toArray(props.children).filter(Boolean).length;
|
||||
let newW = withPaddings * len > maxWidth ? Math.floor(maxWidth / len) : withPaddings;
|
||||
if (len === 1 && newW < 90) newW = 90;
|
||||
setNewWidth(newW);
|
||||
const { width: layoutWidth } = event.nativeEvent.layout;
|
||||
const totalChildren = React.Children.toArray(props.children).filter(Boolean).length;
|
||||
setNewWidth(computeNewWidth(layoutWidth, totalChildren));
|
||||
layoutCalculated.current = true;
|
||||
};
|
||||
|
||||
const renderChild = (child: ReactNode, index: number, array: ReactNode[]): ReactNode => {
|
||||
if (typeof child === 'string') {
|
||||
return (
|
||||
<View key={index} style={{ width: newWidth }}>
|
||||
<Text adjustsFontSizeToFit numberOfLines={1}>
|
||||
{child}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return React.cloneElement(child as React.ReactElement<any>, {
|
||||
width: newWidth,
|
||||
key: index,
|
||||
first: index === 0,
|
||||
last: index === array.length - 1,
|
||||
singleChild: array.length === 1,
|
||||
});
|
||||
};
|
||||
|
||||
const totalChildren = React.Children.toArray(props.children).filter(Boolean).length;
|
||||
return (
|
||||
<Animated.View
|
||||
ref={ref}
|
||||
onLayout={onLayout}
|
||||
style={[
|
||||
cStyles.root,
|
||||
props.inline ? cStyles.rootInline : cStyles.rootAbsolute,
|
||||
containerStyles.root,
|
||||
props.inline ? containerStyles.rootInline : containerStyles.rootAbsolute,
|
||||
bottomInsets,
|
||||
newWidth ? cStyles.rootPost : cStyles.rootPre,
|
||||
newWidth ? containerStyles.rootPost : containerStyles.rootPre,
|
||||
totalChildren === 1 ? containerStyles.rootRound : null,
|
||||
{ transform: [{ translateY: slideAnimation }] },
|
||||
]}
|
||||
>
|
||||
{newWidth
|
||||
? React.Children.toArray(props.children)
|
||||
.filter(Boolean)
|
||||
.map((child, index, array) => {
|
||||
if (typeof child === 'string') {
|
||||
return (
|
||||
<View key={index} style={{ width: newWidth }}>
|
||||
<Text adjustsFontSizeToFit numberOfLines={1}>
|
||||
{child}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return React.cloneElement(child as React.ReactElement<any>, {
|
||||
width: newWidth,
|
||||
key: index,
|
||||
first: index === 0,
|
||||
last: index === array.length - 1,
|
||||
});
|
||||
})
|
||||
: props.children}
|
||||
{newWidth ? React.Children.toArray(props.children).filter(Boolean).map(renderChild) : props.children}
|
||||
</Animated.View>
|
||||
);
|
||||
});
|
||||
|
||||
const buttonFontSize =
|
||||
PixelRatio.roundToNearestPixel(Dimensions.get('window').width / 26) > 22
|
||||
? 22
|
||||
: PixelRatio.roundToNearestPixel(Dimensions.get('window').width / 26);
|
||||
|
||||
const bStyles = StyleSheet.create({
|
||||
root: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
icon: {
|
||||
alignItems: 'center',
|
||||
},
|
||||
text: {
|
||||
fontSize: buttonFontSize,
|
||||
fontWeight: '600',
|
||||
marginLeft: ICON_MARGIN,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
});
|
||||
|
||||
interface FButtonProps {
|
||||
text: string;
|
||||
icon: ReactNode;
|
||||
width?: number;
|
||||
first?: boolean;
|
||||
last?: boolean;
|
||||
singleChild?: boolean;
|
||||
disabled?: boolean;
|
||||
testID?: string;
|
||||
onPress: () => void;
|
||||
onLongPress?: () => void;
|
||||
}
|
||||
|
||||
export const FButton = ({ text, icon, width, first, last, testID, ...props }: FButtonProps) => {
|
||||
export const FButton = ({ text, icon, width, first, last, singleChild, testID, ...props }: FButtonProps) => {
|
||||
const { colors } = useTheme();
|
||||
const bStylesHook = StyleSheet.create({
|
||||
const customButtonStyles = StyleSheet.create({
|
||||
root: {
|
||||
backgroundColor: colors.buttonBackgroundColor,
|
||||
borderRadius: BORDER_RADIUS,
|
||||
|
@ -151,9 +162,12 @@ export const FButton = ({ text, icon, width, first, last, testID, ...props }: FB
|
|||
marginRight: {
|
||||
marginRight: 10,
|
||||
},
|
||||
rootRound: {
|
||||
borderRadius: 9999,
|
||||
},
|
||||
});
|
||||
const style: Record<string, any> = {};
|
||||
const additionalStyles = !last ? bStylesHook.marginRight : {};
|
||||
const additionalStyles = !last ? customButtonStyles.marginRight : {};
|
||||
|
||||
if (width) {
|
||||
style.paddingHorizontal = PADDINGS;
|
||||
|
@ -165,11 +179,15 @@ export const FButton = ({ text, icon, width, first, last, testID, ...props }: FB
|
|||
accessibilityLabel={text}
|
||||
accessibilityRole="button"
|
||||
testID={testID}
|
||||
style={[bStyles.root, bStylesHook.root, style, additionalStyles]}
|
||||
style={[buttonStyles.root, customButtonStyles.root, style, additionalStyles, singleChild ? customButtonStyles.rootRound : null]}
|
||||
{...props}
|
||||
>
|
||||
<View style={bStyles.icon}>{icon}</View>
|
||||
<Text numberOfLines={1} adjustsFontSizeToFit style={[bStyles.text, props.disabled ? bStylesHook.textDisabled : bStylesHook.text]}>
|
||||
<View style={buttonStyles.icon}>{icon}</View>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
adjustsFontSizeToFit
|
||||
style={[buttonStyles.text, props.disabled ? customButtonStyles.textDisabled : customButtonStyles.text]}
|
||||
>
|
||||
{text}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
|
|
|
@ -9,7 +9,12 @@ import { HandOffComponentProps } from './types';
|
|||
|
||||
const HandOffComponent: React.FC<HandOffComponentProps> = props => {
|
||||
const { isHandOffUseEnabled } = useSettings();
|
||||
console.debug('HandOffComponent is rendering.');
|
||||
if (!props || !props.type || !props.userInfo || Object.keys(props.userInfo).length === 0) {
|
||||
console.debug('HandOffComponent: Missing required type or userInfo data');
|
||||
return null;
|
||||
}
|
||||
const userInfo = JSON.stringify(props.userInfo);
|
||||
console.debug(`HandOffComponent is rendering. Type: ${props.type}, UserInfo: ${userInfo}...`);
|
||||
return isHandOffUseEnabled ? <Handoff {...props} /> : null;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import React, { useCallback, useState } from 'react';
|
||||
import { View, StyleSheet, ViewStyle, TouchableOpacity, ActivityIndicator, Platform } from 'react-native';
|
||||
import React, { useCallback, useState, useEffect, useRef } from 'react';
|
||||
import { StyleSheet, ViewStyle, TouchableOpacity, ActivityIndicator, Platform, Animated } from 'react-native';
|
||||
import { Icon, ListItem } from '@rneui/base';
|
||||
import { ExtendedTransaction, LightningTransaction, TWallet } from '../class/wallets/types';
|
||||
import { WalletCarouselItem } from './WalletsCarousel';
|
||||
import { TransactionListItem } from './TransactionListItem';
|
||||
import { useTheme } from './themes';
|
||||
import { BitcoinUnit } from '../models/bitcoinUnits';
|
||||
import { TouchableOpacityWrapper } from './ListItem';
|
||||
import loc from '../loc';
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../blue_modules/hapticFeedback';
|
||||
|
||||
enum ItemType {
|
||||
WalletSection = 'wallet',
|
||||
|
@ -40,6 +40,7 @@ interface ManageWalletsListItemProps {
|
|||
handleToggleHideBalance: (wallet: TWallet) => void;
|
||||
isActive?: boolean;
|
||||
style?: ViewStyle;
|
||||
globalDragActive?: boolean;
|
||||
}
|
||||
|
||||
interface SwipeContentProps {
|
||||
|
@ -83,11 +84,34 @@ const ManageWalletsListItem: React.FC<ManageWalletsListItemProps> = ({
|
|||
onPressIn,
|
||||
onPressOut,
|
||||
isActive,
|
||||
globalDragActive,
|
||||
style,
|
||||
}) => {
|
||||
const { colors } = useTheme();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const CARD_SORT_ACTIVE = 1.06;
|
||||
const INACTIVE_SCALE_WHEN_ACTIVE = 0.9;
|
||||
const SCALE_DURATION = 200;
|
||||
const scaleValue = useRef(new Animated.Value(1)).current;
|
||||
const prevIsActive = useRef(isActive);
|
||||
|
||||
const DEFAULT_VERTICAL_MARGIN = -10;
|
||||
const REDUCED_VERTICAL_MARGIN = -50;
|
||||
|
||||
useEffect(() => {
|
||||
if (isActive !== prevIsActive.current) {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.ImpactMedium);
|
||||
}
|
||||
prevIsActive.current = isActive;
|
||||
|
||||
Animated.timing(scaleValue, {
|
||||
toValue: isActive ? CARD_SORT_ACTIVE : globalDragActive ? INACTIVE_SCALE_WHEN_ACTIVE : 1,
|
||||
duration: SCALE_DURATION,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
}, [isActive, globalDragActive, scaleValue]);
|
||||
|
||||
const onPress = useCallback(() => {
|
||||
if (item.type === ItemType.WalletSection) {
|
||||
setIsLoading(true);
|
||||
|
@ -112,32 +136,42 @@ const ManageWalletsListItem: React.FC<ManageWalletsListItemProps> = ({
|
|||
|
||||
const rightContent = (reset: () => void) => <RightSwipeContent onPress={() => handleRightPress(reset)} />;
|
||||
|
||||
const startDrag = useCallback(() => {
|
||||
scaleValue.setValue(CARD_SORT_ACTIVE);
|
||||
triggerHapticFeedback(HapticFeedbackTypes.ImpactMedium);
|
||||
if (drag) {
|
||||
drag();
|
||||
}
|
||||
}, [CARD_SORT_ACTIVE, drag, scaleValue]);
|
||||
|
||||
if (isLoading) {
|
||||
return <ActivityIndicator size="large" color={colors.brandingColor} />;
|
||||
}
|
||||
|
||||
if (item.type === ItemType.WalletSection) {
|
||||
const animatedStyle = {
|
||||
transform: [{ scale: scaleValue }],
|
||||
marginVertical: globalDragActive && !isActive ? REDUCED_VERTICAL_MARGIN : DEFAULT_VERTICAL_MARGIN,
|
||||
};
|
||||
|
||||
const backgroundColor = isActive || globalDragActive ? colors.brandingColor : colors.background;
|
||||
return (
|
||||
<ListItem.Swipeable
|
||||
leftWidth={80}
|
||||
rightWidth={90}
|
||||
containerStyle={[{ backgroundColor: colors.background }, style]}
|
||||
leftContent={leftContent}
|
||||
rightContent={rightContent}
|
||||
Component={TouchableOpacityWrapper}
|
||||
onPressOut={onPressOut}
|
||||
onPressIn={onPressIn}
|
||||
style={isActive ? styles.activeItem : undefined}
|
||||
>
|
||||
<ListItem.Content
|
||||
style={{
|
||||
backgroundColor: colors.background,
|
||||
}}
|
||||
<Animated.View style={animatedStyle}>
|
||||
<ListItem.Swipeable
|
||||
leftWidth={80}
|
||||
rightWidth={90}
|
||||
containerStyle={[style, { backgroundColor }, isActive || globalDragActive ? styles.transparentBackground : {}]}
|
||||
leftContent={globalDragActive ? null : isActive ? null : leftContent}
|
||||
rightContent={globalDragActive ? null : isActive ? null : rightContent}
|
||||
onPressOut={onPressOut}
|
||||
minSlideWidth={80}
|
||||
onPressIn={onPressIn}
|
||||
style={isActive || globalDragActive ? styles.transparentBackground : {}}
|
||||
>
|
||||
<View style={styles.walletCarouselItemContainer}>
|
||||
<ListItem.Content>
|
||||
<WalletCarouselItem
|
||||
item={item.data}
|
||||
handleLongPress={isDraggingDisabled ? undefined : drag}
|
||||
handleLongPress={isDraggingDisabled ? undefined : startDrag}
|
||||
onPress={onPress}
|
||||
onPressIn={onPressIn}
|
||||
onPressOut={onPressOut}
|
||||
|
@ -145,10 +179,11 @@ const ManageWalletsListItem: React.FC<ManageWalletsListItemProps> = ({
|
|||
searchQuery={state.searchQuery}
|
||||
isPlaceHolder={isPlaceHolder}
|
||||
renderHighlightedText={renderHighlightedText}
|
||||
customStyle={styles.carouselItem}
|
||||
/>
|
||||
</View>
|
||||
</ListItem.Content>
|
||||
</ListItem.Swipeable>
|
||||
</ListItem.Content>
|
||||
</ListItem.Swipeable>
|
||||
</Animated.View>
|
||||
);
|
||||
} else if (item.type === ItemType.TransactionSection && item.data) {
|
||||
const w = state.wallets.find(wallet => wallet.getTransactions().some((tx: ExtendedTransaction) => tx.hash === item.data.hash));
|
||||
|
@ -169,22 +204,22 @@ const ManageWalletsListItem: React.FC<ManageWalletsListItemProps> = ({
|
|||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
walletCarouselItemContainer: {
|
||||
width: '100%',
|
||||
},
|
||||
leftButtonContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
carouselItem: {
|
||||
width: '100%',
|
||||
},
|
||||
rightButtonContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'red',
|
||||
},
|
||||
activeItem: {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.1)',
|
||||
transparentBackground: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -268,7 +268,6 @@ const PromptPasswordConfirmationModal = forwardRef<PromptPasswordConfirmationMod
|
|||
backgroundColor={colors.modal}
|
||||
isGrabberVisible={!isSuccess}
|
||||
scrollRef={scrollView}
|
||||
keyboardMode="pan"
|
||||
dismissible={false}
|
||||
footer={
|
||||
!isSuccess ? (
|
||||
|
|
73
components/TipBox.tsx
Normal file
73
components/TipBox.tsx
Normal file
|
@ -0,0 +1,73 @@
|
|||
import React from 'react';
|
||||
import { View, StyleSheet, ViewStyle } from 'react-native';
|
||||
import { useTheme } from './themes';
|
||||
import { BlueText } from '../BlueComponents';
|
||||
|
||||
interface TipBoxProps {
|
||||
number?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
additionalDescription?: string;
|
||||
containerStyle?: ViewStyle;
|
||||
}
|
||||
|
||||
const TipBox: React.FC<TipBoxProps> = ({ number, title, description, additionalDescription, containerStyle }) => {
|
||||
const { colors } = useTheme();
|
||||
const stylesHook = StyleSheet.create({
|
||||
tipBox: {
|
||||
backgroundColor: colors.ballOutgoingExpired,
|
||||
borderRadius: 12,
|
||||
padding: 16,
|
||||
marginBottom: 24,
|
||||
...containerStyle,
|
||||
},
|
||||
tipHeader: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginBottom: number || title ? 16 : 0,
|
||||
},
|
||||
tipHeaderText: {
|
||||
marginLeft: 4,
|
||||
flex: 1,
|
||||
},
|
||||
description: {
|
||||
marginBottom: additionalDescription ? 16 : 0,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<View style={stylesHook.tipBox}>
|
||||
{(number || title) && (
|
||||
<View style={stylesHook.tipHeader}>
|
||||
{number && (
|
||||
<View style={styles.vaultKeyCircle}>
|
||||
<BlueText style={styles.vaultKeyText}>{number}</BlueText>
|
||||
</View>
|
||||
)}
|
||||
{title && (
|
||||
<BlueText bold style={stylesHook.tipHeaderText}>
|
||||
{title}
|
||||
</BlueText>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
{description && <BlueText style={stylesHook.description}>{description}</BlueText>}
|
||||
{additionalDescription && <BlueText>{additionalDescription}</BlueText>}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
vaultKeyCircle: {
|
||||
width: 32,
|
||||
height: 32,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
vaultKeyText: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
});
|
||||
|
||||
export default TipBox;
|
|
@ -46,10 +46,10 @@ export const TransactionListItem: React.FC<TransactionListItemProps> = React.mem
|
|||
const { language, selectedBlockExplorer } = useSettings();
|
||||
const containerStyle = useMemo(
|
||||
() => ({
|
||||
backgroundColor: 'transparent',
|
||||
backgroundColor: colors.background,
|
||||
borderBottomColor: colors.lightBorder,
|
||||
}),
|
||||
[colors.lightBorder],
|
||||
[colors.background, colors.lightBorder],
|
||||
);
|
||||
|
||||
const combinedStyle = useMemo(() => [containerStyle, style], [containerStyle, style]);
|
||||
|
@ -81,28 +81,23 @@ export const TransactionListItem: React.FC<TransactionListItemProps> = React.mem
|
|||
return sub || undefined;
|
||||
}, [txMemo, item.confirmations, item.memo]);
|
||||
|
||||
const formattedAmount = useMemo(() => {
|
||||
return formatBalanceWithoutSuffix(item.value && item.value, itemPriceUnit, true).toString();
|
||||
}, [item.value, itemPriceUnit]);
|
||||
|
||||
const rowTitle = useMemo(() => {
|
||||
if (item.type === 'user_invoice' || item.type === 'payment_request') {
|
||||
if (isNaN(Number(item.value))) {
|
||||
item.value = 0;
|
||||
}
|
||||
const currentDate = new Date();
|
||||
const now = (currentDate.getTime() / 1000) | 0; // eslint-disable-line no-bitwise
|
||||
const now = Math.floor(currentDate.getTime() / 1000);
|
||||
const invoiceExpiration = item.timestamp! + item.expire_time!;
|
||||
|
||||
if (invoiceExpiration > now) {
|
||||
return formatBalanceWithoutSuffix(item.value && item.value, itemPriceUnit, true).toString();
|
||||
if (invoiceExpiration > now || item.ispaid) {
|
||||
return formattedAmount;
|
||||
} else {
|
||||
if (item.ispaid) {
|
||||
return formatBalanceWithoutSuffix(item.value && item.value, itemPriceUnit, true).toString();
|
||||
} else {
|
||||
return loc.lnd.expired;
|
||||
}
|
||||
return loc.lnd.expired;
|
||||
}
|
||||
} else {
|
||||
return formatBalanceWithoutSuffix(item.value && item.value, itemPriceUnit, true).toString();
|
||||
}
|
||||
}, [item, itemPriceUnit]);
|
||||
return formattedAmount;
|
||||
}, [item, formattedAmount]);
|
||||
|
||||
const rowTitleStyle = useMemo(() => {
|
||||
let color = colors.successColor;
|
||||
|
@ -198,10 +193,9 @@ export const TransactionListItem: React.FC<TransactionListItemProps> = React.mem
|
|||
const { label: transactionTypeLabel, icon: avatar } = determineTransactionTypeAndAvatar();
|
||||
|
||||
const amountWithUnit = useMemo(() => {
|
||||
const amount = formatBalanceWithoutSuffix(item.value && item.value, itemPriceUnit, true).toString();
|
||||
const unit = itemPriceUnit === BitcoinUnit.BTC || itemPriceUnit === BitcoinUnit.SATS ? ` ${itemPriceUnit}` : ' ';
|
||||
return `${amount}${unit}`;
|
||||
}, [item.value, itemPriceUnit]);
|
||||
const unitSuffix = itemPriceUnit === BitcoinUnit.BTC || itemPriceUnit === BitcoinUnit.SATS ? ` ${itemPriceUnit}` : ' ';
|
||||
return `${formattedAmount}${unitSuffix}`;
|
||||
}, [formattedAmount, itemPriceUnit]);
|
||||
|
||||
useEffect(() => {
|
||||
setSubtitleNumberOfLines(1);
|
||||
|
|
|
@ -108,13 +108,14 @@ const TransactionsNavigationHeader: React.FC<TransactionsNavigationHeaderProps>
|
|||
];
|
||||
}, []);
|
||||
|
||||
const balance = useMemo(() => {
|
||||
const balanceFormatted =
|
||||
unit === BitcoinUnit.LOCAL_CURRENCY
|
||||
? formatBalance(wallet.getBalance(), unit, true)
|
||||
: formatBalanceWithoutSuffix(wallet.getBalance(), unit, true);
|
||||
return !hideBalance && balanceFormatted;
|
||||
}, [unit, wallet, hideBalance]);
|
||||
const currentBalance = wallet ? wallet.getBalance() : 0;
|
||||
const formattedBalance = useMemo(() => {
|
||||
return unit === BitcoinUnit.LOCAL_CURRENCY
|
||||
? formatBalance(currentBalance, unit, true)
|
||||
: formatBalanceWithoutSuffix(currentBalance, unit, true);
|
||||
}, [unit, currentBalance]);
|
||||
|
||||
const balance = !wallet.hideBalance && formattedBalance;
|
||||
|
||||
const toolTipWalletBalanceActions = useMemo(() => {
|
||||
return hideBalance
|
||||
|
|
|
@ -7,7 +7,12 @@ import { requestCameraAuthorization } from '../helpers/scan-qr';
|
|||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
// List of screens that require biometrics
|
||||
const requiresBiometrics = ['WalletExportRoot', 'WalletXpubRoot', 'ViewEditMultisigCosignersRoot', 'ExportMultisigCoordinationSetupRoot'];
|
||||
const requiresBiometrics = [
|
||||
'WalletExportRoot',
|
||||
'WalletXpubRoot',
|
||||
'ViewEditMultisigCosignersRoot',
|
||||
'ExportMultisigCoordinationSetupRoot',
|
||||
];
|
||||
|
||||
// List of screens that require wallet export to be saved
|
||||
const requiresWalletExportIsSaved = ['ReceiveDetailsRoot', 'WalletAddresses'];
|
||||
|
@ -44,6 +49,13 @@ export const useExtendedNavigation = <T extends NavigationProp<ParamListBase>>()
|
|||
};
|
||||
|
||||
(async () => {
|
||||
// NEW: If the current (active) screen is 'ScanQRCode', bypass all checks.
|
||||
const currentRouteName = navigationRef.current?.getCurrentRoute()?.name;
|
||||
if (currentRouteName === 'ScanQRCode') {
|
||||
proceedWithNavigation();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isRequiresBiometrics) {
|
||||
const isBiometricsEnabled = await isBiometricUseEnabled();
|
||||
if (isBiometricsEnabled) {
|
||||
|
@ -53,8 +65,8 @@ export const useExtendedNavigation = <T extends NavigationProp<ParamListBase>>()
|
|||
return;
|
||||
} else {
|
||||
console.error('Biometric authentication failed');
|
||||
// Decide if navigation should proceed or not after failed authentication
|
||||
return; // Prevent proceeding with the original navigation if bio fails
|
||||
// Do not proceed if authentication fails.
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -78,18 +90,17 @@ export const useExtendedNavigation = <T extends NavigationProp<ParamListBase>>()
|
|||
await saveToDisk();
|
||||
proceedWithNavigation();
|
||||
} catch (error) {
|
||||
if (error) {
|
||||
originalNavigation.navigate('WalletExportRoot', {
|
||||
screen: 'WalletExport',
|
||||
params: { walletID },
|
||||
});
|
||||
}
|
||||
// If there was an error (or the user cancelled), navigate to the wallet export screen.
|
||||
originalNavigation.navigate('WalletExportRoot', {
|
||||
screen: 'WalletExport',
|
||||
params: { walletID },
|
||||
});
|
||||
}
|
||||
|
||||
return; // Prevent proceeding with the original navigation if the reminder is shown
|
||||
return; // Do not proceed with the original navigation if reminder was shown.
|
||||
}
|
||||
}
|
||||
|
||||
// If the target screen is ScanQRCode, request camera authorization.
|
||||
if (screenName === 'ScanQRCode') {
|
||||
await requestCameraAuthorization();
|
||||
}
|
||||
|
@ -115,4 +126,4 @@ export const useExtendedNavigation = <T extends NavigationProp<ParamListBase>>()
|
|||
|
||||
// Usage example:
|
||||
// type NavigationProps = NativeStackNavigationProp<SendDetailsStackParamList, 'SendDetails'>;
|
||||
// const navigation = useExtendedNavigation<NavigationProps>();
|
||||
// const navigation = useExtendedNavigation<NavigationProps>();
|
|
@ -23,20 +23,25 @@ const useHandoffListener = () => {
|
|||
|
||||
const handleUserActivity = useCallback(
|
||||
(data: UserActivityData) => {
|
||||
if (!data || !data.activityType) {
|
||||
console.debug(`Invalid handoff data received: ${data ? JSON.stringify(data) : 'No data provided'}`);
|
||||
return;
|
||||
}
|
||||
const { activityType, userInfo } = data;
|
||||
const modifiedUserInfo = { ...(userInfo || {}), type: activityType };
|
||||
try {
|
||||
if (activityType === HandOffActivityType.ReceiveOnchain) {
|
||||
if (activityType === HandOffActivityType.ReceiveOnchain && modifiedUserInfo.address) {
|
||||
navigate('ReceiveDetailsRoot', {
|
||||
screen: 'ReceiveDetails',
|
||||
params: { address: userInfo.address },
|
||||
params: { address: modifiedUserInfo.address, type: activityType },
|
||||
});
|
||||
} else if (activityType === HandOffActivityType.Xpub) {
|
||||
} else if (activityType === HandOffActivityType.Xpub && modifiedUserInfo.xpub) {
|
||||
navigate('WalletXpubRoot', {
|
||||
screen: 'WalletXpub',
|
||||
params: { xpub: userInfo.xpub },
|
||||
params: { xpub: modifiedUserInfo.xpub, type: activityType },
|
||||
});
|
||||
} else {
|
||||
console.debug(`Unhandled activity type: ${activityType}`);
|
||||
console.debug(`Unhandled or incomplete activity type/data: ${activityType}`, modifiedUserInfo);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error handling user activity:', error);
|
||||
|
@ -52,7 +57,7 @@ const useHandoffListener = () => {
|
|||
|
||||
EventEmitter.getMostRecentUserActivity?.()
|
||||
.then(handleUserActivity)
|
||||
.catch(() => console.debug('No userActivity object sent'));
|
||||
.catch(() => console.debug('No valid user activity object received'));
|
||||
|
||||
return () => {
|
||||
activitySubscription?.remove();
|
||||
|
|
6
index.js
6
index.js
|
@ -12,7 +12,11 @@ if (!Error.captureStackTrace) {
|
|||
Error.captureStackTrace = () => {};
|
||||
}
|
||||
|
||||
LogBox.ignoreLogs(['Require cycle:', 'Battery state `unknown` and monitoring disabled, this is normal for simulators and tvOS.']);
|
||||
LogBox.ignoreLogs([
|
||||
'Require cycle:',
|
||||
'Battery state `unknown` and monitoring disabled, this is normal for simulators and tvOS.',
|
||||
'Open debugger to view warnings.',
|
||||
]);
|
||||
|
||||
const BlueAppComponent = () => {
|
||||
useEffect(() => {
|
||||
|
|
|
@ -1481,7 +1481,7 @@
|
|||
"$(SDKROOT)/System/iOSSupport/usr/lib/swift",
|
||||
"$(inherited)",
|
||||
);
|
||||
MARKETING_VERSION = 7.1.0;
|
||||
MARKETING_VERSION = 7.1.1;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
|
@ -1539,7 +1539,7 @@
|
|||
"$(SDKROOT)/System/iOSSupport/usr/lib/swift",
|
||||
"$(inherited)",
|
||||
);
|
||||
MARKETING_VERSION = 7.1.0;
|
||||
MARKETING_VERSION = 7.1.1;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
|
@ -1588,7 +1588,7 @@
|
|||
"$(SDKROOT)/System/iOSSupport/usr/lib/swift",
|
||||
"$(inherited)",
|
||||
);
|
||||
MARKETING_VERSION = 7.1.0;
|
||||
MARKETING_VERSION = 7.1.1;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES;
|
||||
|
@ -1631,7 +1631,7 @@
|
|||
"$(SDKROOT)/System/iOSSupport/usr/lib/swift",
|
||||
"$(inherited)",
|
||||
);
|
||||
MARKETING_VERSION = 7.1.0;
|
||||
MARKETING_VERSION = 7.1.1;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.Stickers;
|
||||
|
@ -1681,7 +1681,7 @@
|
|||
"$(SDKROOT)/System/iOSSupport/usr/lib/swift",
|
||||
"$(inherited)",
|
||||
);
|
||||
MARKETING_VERSION = 7.1.0;
|
||||
MARKETING_VERSION = 7.1.1;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES;
|
||||
|
@ -1737,7 +1737,7 @@
|
|||
"$(SDKROOT)/System/iOSSupport/usr/lib/swift",
|
||||
"$(inherited)",
|
||||
);
|
||||
MARKETING_VERSION = 7.1.0;
|
||||
MARKETING_VERSION = 7.1.1;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.MarketWidget;
|
||||
|
@ -1925,7 +1925,7 @@
|
|||
"$(SDKROOT)/System/iOSSupport/usr/lib/swift",
|
||||
"$(inherited)",
|
||||
);
|
||||
MARKETING_VERSION = 7.1.0;
|
||||
MARKETING_VERSION = 7.1.1;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES;
|
||||
|
@ -1978,7 +1978,7 @@
|
|||
"$(SDKROOT)/System/iOSSupport/usr/lib/swift",
|
||||
"$(inherited)",
|
||||
);
|
||||
MARKETING_VERSION = 7.1.0;
|
||||
MARKETING_VERSION = 7.1.1;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.watch.extension;
|
||||
|
@ -2024,7 +2024,7 @@
|
|||
"$(SDKROOT)/System/iOSSupport/usr/lib/swift",
|
||||
"$(inherited)",
|
||||
);
|
||||
MARKETING_VERSION = 7.1.0;
|
||||
MARKETING_VERSION = 7.1.1;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES;
|
||||
|
@ -2073,7 +2073,7 @@
|
|||
"$(SDKROOT)/System/iOSSupport/usr/lib/swift",
|
||||
"$(inherited)",
|
||||
);
|
||||
MARKETING_VERSION = 7.1.0;
|
||||
MARKETING_VERSION = 7.1.1;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.watch;
|
||||
|
|
|
@ -154,27 +154,42 @@
|
|||
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity
|
||||
restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
|
||||
{
|
||||
NSDictionary *userActivityData = @{@"activityType": userActivity.activityType, @"userInfo": userActivity.userInfo};
|
||||
// Validate userActivity and its type
|
||||
if (!userActivity || !userActivity.activityType) {
|
||||
NSLog(@"[Handoff] Invalid or missing userActivity");
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSDictionary *userActivityData = @{@"activityType": userActivity.activityType ?: @"",
|
||||
@"userInfo": userActivity.userInfo ?: @{}};
|
||||
|
||||
// Save activity data to userDefaults for potential later use
|
||||
[self.userDefaultsGroup setValue:userActivityData forKey:@"onUserActivityOpen"];
|
||||
|
||||
// Check if the activity type matches the allowed types
|
||||
// Check if the activity type matches one of the allowed types
|
||||
if ([userActivity.activityType isEqualToString:@"io.bluewallet.bluewallet.receiveonchain"] ||
|
||||
[userActivity.activityType isEqualToString:@"io.bluewallet.bluewallet.xpub"] ||
|
||||
[userActivity.activityType isEqualToString:@"io.bluewallet.bluewallet.blockexplorer"]) {
|
||||
|
||||
[EventEmitter.sharedInstance sendUserActivity:userActivityData];
|
||||
if ([EventEmitter.sharedInstance respondsToSelector:@selector(sendUserActivity:)]) {
|
||||
[EventEmitter.sharedInstance sendUserActivity:userActivityData];
|
||||
} else {
|
||||
NSLog(@"[Handoff] EventEmitter does not implement sendUserActivity:");
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) {
|
||||
// Forward web browsing activities to LinkingManager
|
||||
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
|
||||
return [RCTLinkingManager application:application
|
||||
continueUserActivity:userActivity
|
||||
restorationHandler:restorationHandler];
|
||||
}
|
||||
|
||||
// If activity type does not match any of the specified types, do nothing
|
||||
NSLog(@"[Handoff] Unhandled user activity type: %@", userActivity.activityType);
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
||||
return [RCTLinkingManager application:app openURL:url options:options];
|
||||
}
|
||||
|
|
|
@ -41,7 +41,15 @@ RCT_EXPORT_MODULE();
|
|||
|
||||
- (void)sendUserActivity:(NSDictionary *)userInfo
|
||||
{
|
||||
[self sendEventWithName:@"onUserActivityOpen" body:userInfo];
|
||||
if (![userInfo isKindOfClass:[NSDictionary class]]) {
|
||||
NSLog(@"[EventEmitter] Invalid user activity data: %@", userInfo);
|
||||
return;
|
||||
}
|
||||
@try {
|
||||
[self sendEventWithName:@"onUserActivityOpen" body:userInfo];
|
||||
} @catch (NSException *exception) {
|
||||
NSLog(@"[EventEmitter] Exception while sending event: %@", exception);
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getMostRecentUserActivity:(RCTPromiseResolveBlock)resolve
|
||||
|
|
528
ios/Podfile.lock
528
ios/Podfile.lock
File diff suppressed because it is too large
Load diff
|
@ -12,7 +12,6 @@
|
|||
"storage_is_encrypted": "وحدة التخزين مشفرة. أنت بحاجة إلى كلمة المرور لفك تشفيرها",
|
||||
"yes": "نعم",
|
||||
"no": "لا",
|
||||
"save": "حفظ",
|
||||
"seed": "عبارة الاسترداد",
|
||||
"success": "نجاح",
|
||||
"wallet_key": "مفتاح المحفظة",
|
||||
|
@ -210,13 +209,8 @@
|
|||
"set_electrum_server_as_default": "هل تريد تعيين {server} كخادم Electrum الافتراضي؟",
|
||||
"electrum_settings_server": "خادم Electrum",
|
||||
"electrum_status": "الحالة",
|
||||
"electrum_clear_alert_title": "محو السجل؟",
|
||||
"electrum_clear_alert_message": "هل تريد مسح سجل خوادم Electrum؟",
|
||||
"electrum_clear_alert_cancel": "الغاء",
|
||||
"electrum_clear_alert_ok": "موافق",
|
||||
"electrum_reset": "إعادة تعيين إلى الافتراضي",
|
||||
"electrum_unable_to_connect": "تعذر الاتصال بـ {server}.",
|
||||
"electrum_reset_to_default": "هل أنت متأكد من رغبتك في إعادة تعيين إعدادات Electrum إلى الإعدادات الافتراضية؟",
|
||||
"electrum_reset": "إعادة تعيين إلى الافتراضي",
|
||||
"encrypt_decrypt": "فك تشفير وحدة التخزين",
|
||||
"encrypt_decrypt_q": "هل أنت متأكد أنك تريد فك تشفير وحدة التخزين الخاصة بك؟ سيسمح إجراء ذلك بالوصول إلى محافظك دون كلمة مرور.",
|
||||
"encrypt_enc_and_pass": "مشفرة ومحمية بكلمة مرور",
|
||||
|
@ -337,7 +331,6 @@
|
|||
"details_export_history": "تصدير السجل ل ملف CSV",
|
||||
"details_master_fingerprint": "البصمة الرئيسية",
|
||||
"details_multisig_type": "متعدد التواقيع",
|
||||
"details_no_cancel": "لا، إلغاء",
|
||||
"details_show_xpub": "إظهار عنوان XPUB للمحفظة",
|
||||
"details_show_addresses": "عرض العناوين",
|
||||
"details_title": "المحفظة",
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
"storage_is_encrypted": "Ваша сховішча зашыфравана. Для расшыфроўкі патрабуецца пароль.",
|
||||
"yes": "Так",
|
||||
"no": "Не",
|
||||
"save": "Захаваць",
|
||||
"seed": "Семя",
|
||||
"success": "Посьпех",
|
||||
"wallet_key": "Ключ ад кашалька"
|
||||
|
@ -44,7 +43,6 @@
|
|||
"create_to": "Да"
|
||||
},
|
||||
"settings": {
|
||||
"electrum_clear_alert_cancel": "Адмяніць",
|
||||
"save": "Захаваць"
|
||||
},
|
||||
"wallets": {
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
"storage_is_encrypted": "Вашият портфейл е криптиран. Необходима е парола за декриптиране",
|
||||
"yes": "Да",
|
||||
"no": "Не",
|
||||
"save": "Запази",
|
||||
"seed": "Сиид",
|
||||
"success": "Успех",
|
||||
"wallet_key": "Парола на портфейла"
|
||||
|
@ -168,13 +167,8 @@
|
|||
"electrum_saved": "Промените бяха запазени успешно. Моля, рестартирайте Блу Уолет за да видите промените.",
|
||||
"set_electrum_server_as_default": "Задайте {server} като Електрум сървър по подразбиране? ",
|
||||
"electrum_status": "Статус",
|
||||
"electrum_clear_alert_title": "Изчисти историята?",
|
||||
"electrum_clear_alert_message": "Искате ли да изтриете електрум сървър историята?",
|
||||
"electrum_clear_alert_cancel": "Отказ",
|
||||
"electrum_clear_alert_ok": "Ок",
|
||||
"electrum_reset": "Начални настройки",
|
||||
"electrum_unable_to_connect": "Не възможно свързване със сървър {server}.",
|
||||
"electrum_reset_to_default": "Сигурни ли сте, че искате да върнете Електрум към първоначалните настройки?",
|
||||
"electrum_reset": "Начални настройки",
|
||||
"encrypt_decrypt": "Декриптирай хранилището",
|
||||
"encrypt_decrypt_q": "Сигурни ли сте, че искате да декриптирате хранилището? Това ще направи портфейлите ви достъпни без парола.",
|
||||
"encrypt_enc_and_pass": "Криптиран и защитен с парола",
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
"storage_is_encrypted": "جاگه زفت کردنی ایسا ریس رزم هڌ. سی گۊشیڌنس وا رزمسه داشته بۊی.",
|
||||
"yes": "هری",
|
||||
"no": "نه",
|
||||
"save": "زفت کردن",
|
||||
"seed": "سید",
|
||||
"success": "سر ٱنجوم گرهڌ",
|
||||
"wallet_key": "کیلیت کیف پیل",
|
||||
|
@ -187,13 +186,8 @@
|
|||
"set_electrum_server_as_default": "{server} سی سرور پؽش فرز الکترام ساموو بۊوه؟",
|
||||
"electrum_settings_server": "سرور الکترام",
|
||||
"electrum_status": "وزیت",
|
||||
"electrum_clear_alert_title": "ویرگار پاک بۊ؟",
|
||||
"electrum_clear_alert_message": "اخۊی ویرگار سرورا الکترام نه پاک کۊنی؟",
|
||||
"electrum_clear_alert_cancel": "لقو",
|
||||
"electrum_clear_alert_ok": "هری",
|
||||
"electrum_reset": "ورگندن به پؽش فرز",
|
||||
"electrum_unable_to_connect": "نا مۉفق منه منپیز به {server}",
|
||||
"electrum_reset_to_default": "الن اخۊی سامووا الکترام نه به هالت پؽش فرز وورنشۊوی کۊنی؟",
|
||||
"electrum_reset": "ورگندن به پؽش فرز",
|
||||
"encrypt_decrypt": "رزم گوشایی جاگه زفت کردنی",
|
||||
"encrypt_title": "امنیت",
|
||||
"encrypt_tstorage": "جاگه زفت کردنی",
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
"storage_is_encrypted": "L'informació està xifrada. Es requereix la contrasenya per a desxifrar-la.",
|
||||
"yes": "Si",
|
||||
"no": "No",
|
||||
"save": "Desar",
|
||||
"seed": "Llavor",
|
||||
"success": "Èxit",
|
||||
"wallet_key": "Clau del moneder",
|
||||
|
@ -168,9 +167,6 @@
|
|||
"use_ssl": "Utilitza SSL",
|
||||
"electrum_settings_server": "Servidor Electrum",
|
||||
"electrum_status": "estat",
|
||||
"electrum_clear_alert_title": "Netejar l'historial?",
|
||||
"electrum_clear_alert_cancel": "Cancel·lar",
|
||||
"electrum_clear_alert_ok": "D'acord",
|
||||
"electrum_reset": "Restableix la configuració predeterminada",
|
||||
"encrypt_title": "Seguretat",
|
||||
"encrypt_use": "Utilitza {type}",
|
||||
|
@ -237,7 +233,6 @@
|
|||
"details_delete_wallet": "Eliminar Moneder",
|
||||
"details_export_backup": "Exportar / Guardar",
|
||||
"details_master_fingerprint": "Petjada digital mestre",
|
||||
"details_no_cancel": "No, cancel·lar",
|
||||
"details_show_xpub": "Mostrar wallet XPUB",
|
||||
"details_title": "Detalls del moneder",
|
||||
"wallets": "moneders",
|
||||
|
|
|
@ -10,12 +10,11 @@
|
|||
"never": "Nikdy",
|
||||
"of": "{number} z(e) {total}",
|
||||
"ok": "OK",
|
||||
"customize": "Přizpůsobit",
|
||||
"enter_url": "Zadejte URL",
|
||||
"storage_is_encrypted": "Vaše úložiště je zašifrované. Zadejte heslo k odemčení.",
|
||||
"yes": "Ano",
|
||||
"no": "Ne",
|
||||
"save": "Uložit",
|
||||
"save": "Uložit…",
|
||||
"seed": "Seed",
|
||||
"success": "Úspěch",
|
||||
"wallet_key": "Klíč peněženky",
|
||||
|
@ -28,6 +27,8 @@
|
|||
"enter_amount": "Zadejte částku",
|
||||
"qr_custom_input_button": "Klepněte 10× k zadání vlastního vstupu",
|
||||
"unlock": "Odemknout",
|
||||
"port": "Port",
|
||||
"ssl_port": "SSL port",
|
||||
"suggested": "Doporučené"
|
||||
},
|
||||
"azteco": {
|
||||
|
@ -74,6 +75,7 @@
|
|||
"please_pay": "Zaplaťte prosím",
|
||||
"preimage": "Předobraz",
|
||||
"sats": "sats.",
|
||||
"date_time": "Datum a čas",
|
||||
"wasnt_paid_and_expired": "Tato faktura nebyla zaplacena a její platnost vypršela."
|
||||
},
|
||||
"plausibledeniability": {
|
||||
|
@ -106,7 +108,7 @@
|
|||
"minSats": "Minimální množství je {min} sats",
|
||||
"minSatsFull": "Minimální částka je {min} sats nebo {currency} ",
|
||||
"qrcode_for_the_address": "QR kód pro adresu",
|
||||
"bip47_explanation": "Platební kódy jsou univerzální adresou, která zabraňuje odhalení adres vaší peněženky. Nejsou podporovány všemi službami."
|
||||
"bip47_explanation": "Platební kódy jsou univerzální adresy, které zabraňují prozrazení adres vaší peněženky. Ne všechny služby je však podporují."
|
||||
},
|
||||
"send": {
|
||||
"provided_address_is_invoice": "Zdá se, že tato adresa je určena pro Lightning fakturu. Přejděte prosím do své Lightning peněženky, abyste mohli provést platbu této faktury.",
|
||||
|
@ -190,7 +192,7 @@
|
|||
"outdated_rate": "Kurz byl naposledy aktualizován: {date}",
|
||||
"psbt_tx_open": "Otevřít podepsanou transakci",
|
||||
"psbt_tx_scan": "Skenovat podepsanou transakci",
|
||||
"qr_error_no_qrcode": "Ve vybraném obrázku se nám nepodařilo najít QR kód. Ujistěte se, že obrázek obsahuje pouze QR kód a žádný další obsah, například text nebo tlačítka.",
|
||||
"qr_error_no_qrcode": "Ve vybraném obrázku nebyl nalezen platný QR kód. Zajistěte, aby obrázek obsahoval pouze QR kód a žádný další obsah jako třeba text nebo tlačítka.",
|
||||
"reset_amount": "Vynulovat částku",
|
||||
"reset_amount_confirm": "Chcete částku vynulovat?",
|
||||
"success_done": "Hotovo",
|
||||
|
@ -250,15 +252,11 @@
|
|||
"electrum_status": "Stav",
|
||||
"electrum_preferred_server": "Upřednostňovaný server",
|
||||
"electrum_preferred_server_description": "Zadejte server, který má vaše peněženka používat pro všechny bitcoinové aktivity. Po nastavení bude vaše peněženka používat výhradně tento server ke kontrole zůstatků, odesílání transakcí a načítání síťových dat. Před nastavením se ujistěte, že tomuto serveru důvěřujete.",
|
||||
"electrum_clear_alert_title": "Smazat historii?",
|
||||
"electrum_clear_alert_message": "Chcete vymazat historii serverů Electrum?",
|
||||
"electrum_clear_alert_cancel": "Zrušit",
|
||||
"electrum_clear_alert_ok": "Ok",
|
||||
"electrum_reset": "Obnovit do výchozího nastavení",
|
||||
"electrum_unable_to_connect": "Nelze se připojit k {server}.",
|
||||
"electrum_history": "Historie",
|
||||
"electrum_reset_to_default": "Opravdu chcete obnovit nastavení Electrum na výchozí hodnoty?",
|
||||
"electrum_clear": "Vymazat historii",
|
||||
"electrum_reset_to_default": "Toto nastavení nechá aplikaci BlueWallet náhodně vybrat server ze seznamu navrhovaných.",
|
||||
"electrum_reset": "Obnovit do výchozího nastavení",
|
||||
"electrum_reset_to_default_and_clear_history": "Obnovit výchozí nastavení a vymazat historii",
|
||||
"encrypt_decrypt": "Dešifrovat úložiště",
|
||||
"encrypt_decrypt_q": "Opravdu chcete dešifrovat úložiště? To umožní přistupovat k vašim peněženkám bez hesla.",
|
||||
"encrypt_enc_and_pass": "Šifrovat a chránit heslem",
|
||||
|
@ -272,6 +270,8 @@
|
|||
"encrypt_title": "Zabezpečení",
|
||||
"encrypt_tstorage": "Úložiště",
|
||||
"encrypt_use": "Použít {type}",
|
||||
"set_as_preferred": "Nastavit jako preferovaný",
|
||||
"set_as_preferred_electrum": "Nastavení {host}:{port} jako upřednostňovaného serveru zakáže náhodné připojování k navrhovanému serveru.",
|
||||
"encrypted_feature_disabled": "Tato funkce nemůže být použita, pokud je povoleno zašifrované úložiště.",
|
||||
"encrypt_use_expl": "{type} bude použit k potvrzení vaší identity před provedením transakce, odemknutím, exportem nebo smazáním peněženky. {type} nebude použit k odemknutí zašifrovaného úložiště.",
|
||||
"biometrics_fail": "Pokud {type} není povolen, nebo selže při odemykání, můžete jako alternativu použít přístupový kód vašeho zařízení.",
|
||||
|
@ -291,6 +291,7 @@
|
|||
"network": "Síť",
|
||||
"network_broadcast": "Odeslat transakci",
|
||||
"network_electrum": "Electrum server",
|
||||
"electrum_suggested_description": "Pokud nebude nastaven preferovaný server, bude náhodně vybrán navrhovaný server.",
|
||||
"not_a_valid_uri": "Neplatná URI",
|
||||
"notifications": "Oznámení",
|
||||
"open_link_in_explorer": "Otevřít odkaz v průzkumníku",
|
||||
|
@ -402,7 +403,6 @@
|
|||
"add_wallet_name": "Název peněženky",
|
||||
"add_wallet_type": "Typ",
|
||||
"add_wallet_seed_length": "Délka seedu",
|
||||
"add_wallet_seed_length_message": "Zvolte délku seed fráze, kterou chcete použít pro tuto peněženku.",
|
||||
"add_wallet_seed_length_12": "12 slov",
|
||||
"add_wallet_seed_length_24": "24 slov",
|
||||
"clipboard_bitcoin": "Ve schránce máte bitcoinovou adresu. Chcete ji použít pro transakci?",
|
||||
|
@ -422,7 +422,6 @@
|
|||
"details_export_history": "Exportovat historii do CSV",
|
||||
"details_master_fingerprint": "Hlavní otisk",
|
||||
"details_multisig_type": "multisig",
|
||||
"details_no_cancel": "Ne, zrušit",
|
||||
"details_show_xpub": "Zobrazit XPUB peněženky",
|
||||
"details_show_addresses": "Zobrazit adresy",
|
||||
"details_title": "Peněženka",
|
||||
|
@ -493,7 +492,9 @@
|
|||
"identity_pubkey": "Identity Pubkey",
|
||||
"xpub_title": "XPUB peněženky",
|
||||
"manage_wallets_search_placeholder": "Prohledat peněženky, poznámky",
|
||||
"more_info": "Další informace"
|
||||
"more_info": "Další informace",
|
||||
"details_delete_wallet_error_message": "Došlo k problému během potvrzování, zda byla tato peněženka odstraněna z oznámení. Toto může být způsobeno problémem se sítí nebo kvůli špatnému připojení. Pokud budete pokračovat, je možné, že i nadále budete dostávat oznámení o transakcích souvisejících s touto peněženkou, přestože už bude odstraněna.",
|
||||
"details_delete_anyway": "Přesto odstranit"
|
||||
},
|
||||
"total_balance_view": {
|
||||
"display_in_bitcoin": "Zobrazit v bitcoinech",
|
||||
|
@ -508,6 +509,10 @@
|
|||
"default_label": "Vícepodpisové Úložiště",
|
||||
"multisig_vault_explain": "Nejlepší zabezpečení pro velké částky",
|
||||
"provide_signature": "Poskytnout podpis",
|
||||
"provide_signature_details": "K podpisu této transakce použijte své zařízení a peněženku, ve které je klíč umístěn",
|
||||
"provide_signature_details_bluewallet": "V aplikaci BlueWallet přejděte do nabídky Odeslat a vyberte",
|
||||
"provide_signature_next_steps": "Naskenujte nebo importujte podepsanou transakci",
|
||||
"provide_signature_next_steps_details": "Poté, co vaše peněženka úspěšně podepsala transakci, naskenujte uvedený QR kód, nebo importujte doprovodný soubor, a poté zkontrolujte veškeré podrobnosti transakce před tím, než ji odešlete.",
|
||||
"vault_key": "{number}. klíč Úložiště",
|
||||
"required_keys_out_of_total": "Požadovaných klíčů z celkového počtu",
|
||||
"fee": "Poplatek: {number}",
|
||||
|
@ -654,6 +659,8 @@
|
|||
"bip47": {
|
||||
"payment_code": "Platební kód",
|
||||
"contacts": "Kontakty",
|
||||
"bip47_explain": "Znovu použitelný a sdílitelný kód",
|
||||
"bip47_explain_subtitle": "BIP47",
|
||||
"purpose": "Opakovaně použitelný kód, který je možné sdílet (BIP47)",
|
||||
"pay_this_contact": "Zaplatit tomuto kontaktu",
|
||||
"rename_contact": "Přejmenovat kontakt",
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
"storage_is_encrypted": "Mae'r storfa wedi encryptio. Mae angen Cyfrinair i'w ddad-gryptio. ",
|
||||
"yes": "Ie",
|
||||
"no": "Na",
|
||||
"save": "Safio",
|
||||
"seed": "Hadyn",
|
||||
"success": "Llwyddiant",
|
||||
"wallet_key": "Allwedd waled"
|
||||
|
@ -109,8 +108,6 @@
|
|||
"default_wallets": "Gweld Pob Waled",
|
||||
"electrum_connected": "Wedi Cysylltu",
|
||||
"electrum_connected_not": "Heb Gysylltu",
|
||||
"electrum_clear_alert_cancel": "Canslo",
|
||||
"electrum_clear_alert_ok": "Iawn",
|
||||
"encrypt_decrypt": "Dadgryptio'r Storfa",
|
||||
"encrypt_title": "Diogelwch",
|
||||
"encrypt_tstorage": "Storfa",
|
||||
|
@ -163,7 +160,6 @@
|
|||
"details_connected_to": "Wedi cysylltu efo",
|
||||
"details_delete": "Gwaredu",
|
||||
"details_delete_wallet": "Gwaredu Waled",
|
||||
"details_no_cancel": "Na, canslo",
|
||||
"details_show_addresses": "Dangos cyfeiriadau",
|
||||
"wallets": "Waledi",
|
||||
"details_type": "Math",
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
"never": "aldrig",
|
||||
"ok": "OK",
|
||||
"storage_is_encrypted": "Lageret er krypteret. Indtast adgangskode for at dekryptere",
|
||||
"save": "save",
|
||||
"success": "Succes"
|
||||
},
|
||||
"azteco": {
|
||||
|
@ -60,7 +59,6 @@
|
|||
"settings": {
|
||||
"about": "Andet",
|
||||
"currency": "Valuta",
|
||||
"electrum_clear_alert_cancel": "Annuller",
|
||||
"header": "indstillinger",
|
||||
"language": "Sprog",
|
||||
"lightning_settings": "Lightning settings",
|
||||
|
@ -87,7 +85,6 @@
|
|||
"details_are_you_sure": "Er du sikker?",
|
||||
"details_delete": "Slet",
|
||||
"details_export_backup": "Eksporter / backup",
|
||||
"details_no_cancel": "Nej, annuller",
|
||||
"details_show_xpub": "Vis wallet XPUB",
|
||||
"details_yes_delete": "Ja, slet",
|
||||
"export_title": "wallet eksport",
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
"storage_is_encrypted": "Zum Entschlüsseln des Speichers das Passwort eingeben.",
|
||||
"yes": "Ja",
|
||||
"no": "Nein",
|
||||
"save": "Speichern",
|
||||
"save": "Speichern...",
|
||||
"seed": "Seed",
|
||||
"success": "Erfolg",
|
||||
"wallet_key": "Wallet Schlüssel",
|
||||
|
@ -422,7 +422,6 @@
|
|||
"details_export_history": "Verlauf als CSV exportieren",
|
||||
"details_master_fingerprint": "Fingerabdruckkennung",
|
||||
"details_multisig_type": "Mehrfachsignatur",
|
||||
"details_no_cancel": "Nein, abbrechnen",
|
||||
"details_show_xpub": "Wallet xPub zeigen",
|
||||
"details_show_addresses": "Adressen anzeigen",
|
||||
"details_title": "Wallet",
|
||||
|
@ -493,7 +492,9 @@
|
|||
"identity_pubkey": "Pubkey-Identität",
|
||||
"xpub_title": "Wallet xPub",
|
||||
"manage_wallets_search_placeholder": "Suche in Wallets, Notizen",
|
||||
"more_info": "Mehr Infos"
|
||||
"more_info": "Mehr Infos",
|
||||
"details_delete_wallet_error_message": "Bei der Bestätigung, ob diese Wallet aus den Benachrichtigungen entfernt wurde, trat ein Fehler auf – möglicherweise wegen Netzwerkproblemen oder schlechter Verbindung. Falls du fortfährst, erhältst du möglicherweise weiterhin Benachrichtigungen für Transaktionen dieser Wallet, auch nachdem sie gelöscht wurde.",
|
||||
"details_delete_anyway": "Trotzdem löschen"
|
||||
},
|
||||
"total_balance_view": {
|
||||
"display_in_bitcoin": "In bitcoin anzeigen",
|
||||
|
@ -669,7 +670,7 @@
|
|||
"notification_tx_unconfirmed": "Benachrichtigungstransaktion noch unbestätigt. Bitte warten",
|
||||
"failed_create_notif_tx": "On-Chain Transaktion konnte nicht in erstellt werden",
|
||||
"onchain_tx_needed": "On-Chain Transaktion benötigt.",
|
||||
"notif_tx_sent" : "Benachrichtigungstransaktion ist gesendet. Auf Bestätigung warten.",
|
||||
"notif_tx_sent": "Benachrichtigungstransaktion ist gesendet. Auf Bestätigung warten.",
|
||||
"notif_tx": "Benachrichtigungstransaktion",
|
||||
"not_found": "Zahlungscode nicht gefunden"
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
"storage_is_encrypted": "Το αρχείο σου είναι κρυπτογραφημένο. Χρειάζεται ένας κωδικός για να αποκρυπτογραφηθεί.",
|
||||
"yes": "Ναι",
|
||||
"no": "Όχι",
|
||||
"save": "Αποθήκευση",
|
||||
"success": "Επιτυχία",
|
||||
"close": "Κλείσιμο",
|
||||
"refresh": "Ανανέωση",
|
||||
|
@ -168,11 +167,8 @@
|
|||
"use_ssl": "Χρήση SSL",
|
||||
"electrum_settings_server": "Διακομιστής Electrum",
|
||||
"electrum_status": "Κατάσταση",
|
||||
"electrum_clear_alert_title": "Καθαρισμός ιστορικού;",
|
||||
"electrum_clear_alert_cancel": "Ακύρωση",
|
||||
"electrum_clear_alert_ok": "Εντάξει",
|
||||
"electrum_reset": "Επαναφορά προεπιλογής",
|
||||
"electrum_unable_to_connect": "Αδυναμία σύνδεσης στον {server}.",
|
||||
"electrum_reset": "Επαναφορά προεπιλογής",
|
||||
"encrypt_decrypt": "Αποκρυπτογράφηση αποθηκευτικού χώρου",
|
||||
"encrypt_title": "Ασφάλεια",
|
||||
"encrypt_use": "Χρήση {type}",
|
||||
|
@ -243,7 +239,6 @@
|
|||
"details_delete": "Διαγραφή",
|
||||
"details_delete_wallet": "Διαγραφή πορτοφολιού",
|
||||
"details_export_backup": "Εξήγαγε / δημιούργησε αντίγραφο ασφαλείας",
|
||||
"details_no_cancel": "Όχι, ακύρωσε",
|
||||
"details_show_xpub": "Προβολή XPUB του πορτοφολιού",
|
||||
"details_show_addresses": "Εμφάνιση διευθύνσεων",
|
||||
"details_title": "Πορτοφόλι",
|
||||
|
|
11
loc/en.json
11
loc/en.json
|
@ -14,7 +14,7 @@
|
|||
"storage_is_encrypted": "Your storage is encrypted. Password is required to decrypt it.",
|
||||
"yes": "Yes",
|
||||
"no": "No",
|
||||
"save": "Save",
|
||||
"save": "Save...",
|
||||
"seed": "Seed",
|
||||
"success": "Success",
|
||||
"wallet_key": "Wallet key",
|
||||
|
@ -423,7 +423,6 @@
|
|||
"details_export_history": "Export History to CSV",
|
||||
"details_master_fingerprint": "Master Fingerprint",
|
||||
"details_multisig_type": "multisig",
|
||||
"details_no_cancel": "No, cancel",
|
||||
"details_show_xpub": "Show Wallet XPUB",
|
||||
"details_show_addresses": "Show addresses",
|
||||
"details_title": "Wallet",
|
||||
|
@ -494,7 +493,9 @@
|
|||
"identity_pubkey": "Identity Pubkey",
|
||||
"xpub_title": "Wallet XPUB",
|
||||
"manage_wallets_search_placeholder": "Search wallets, memos",
|
||||
"more_info": "More Info"
|
||||
"more_info": "More Info",
|
||||
"details_delete_wallet_error_message": "There was an issue confirming if this wallet was removed from notifications—this could be due to a network issue or poor connection. If you continue, you might still receive notifications for transactions related to this wallet, even after it is deleted.",
|
||||
"details_delete_anyway": "Delete anyway"
|
||||
},
|
||||
"total_balance_view": {
|
||||
"display_in_bitcoin": "Display in Bitcoin",
|
||||
|
@ -509,6 +510,10 @@
|
|||
"default_label": "Multisig Vault",
|
||||
"multisig_vault_explain": "Best security for large amounts",
|
||||
"provide_signature": "Provide signature",
|
||||
"provide_signature_details": "Use your device and wallet where the key resides to sign this transaction",
|
||||
"provide_signature_details_bluewallet": "In BlueWallet, go to the Send screen menu and select ",
|
||||
"provide_signature_next_steps": "Scan or Import Signed Transaction",
|
||||
"provide_signature_next_steps_details": "Once your wallet has successfully signed the transaction, scan the provided QR code or import the accompanying file, and then review all the transaction details before broadcasting it.",
|
||||
"vault_key": "Vault Key {number}",
|
||||
"required_keys_out_of_total": "Required keys out of the total",
|
||||
"fee": "Fee: {number}",
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
"storage_is_encrypted": "Tu almacenamiento está cifrado. Se requiere la contraseña para descifrarlo.",
|
||||
"yes": "Sí",
|
||||
"no": "No",
|
||||
"save": "Guardar",
|
||||
"seed": "Semilla",
|
||||
"success": "Completado",
|
||||
"wallet_key": "Llave de la cartera"
|
||||
|
@ -201,13 +200,8 @@
|
|||
"set_electrum_server_as_default": "¿Establecer {server} como servidor Electrum por defecto?",
|
||||
"electrum_settings_server": "Servidor Electrum",
|
||||
"electrum_status": "Estado",
|
||||
"electrum_clear_alert_title": "¿Limpiar historial?",
|
||||
"electrum_clear_alert_message": "¿Quieres eliminar el historial de los servidores de Electrum?",
|
||||
"electrum_clear_alert_cancel": "Cancelar",
|
||||
"electrum_clear_alert_ok": "Ok",
|
||||
"electrum_reset": "Restablecer valores predeterminados",
|
||||
"electrum_unable_to_connect": "Imposible conectar a {server}. ",
|
||||
"electrum_reset_to_default": "¿Estás seguro de querer reiniciar sus ajustes de Electrum por defecto? ",
|
||||
"electrum_reset": "Restablecer valores predeterminados",
|
||||
"encrypt_decrypt": "Desencriptar almacenamiento",
|
||||
"encrypt_decrypt_q": "¿Seguro que quieres desencriptar tu almacenamiento? Al hacerlo, se podrá acceder a tus carteras sin contraseña.",
|
||||
"encrypt_enc_and_pass": "Encriptado y protegido mediante contraseña",
|
||||
|
@ -327,7 +321,6 @@
|
|||
"details_export_history": "Exportar el historial a CSV",
|
||||
"details_master_fingerprint": "Huella dactilar maestra",
|
||||
"details_multisig_type": "multifirma",
|
||||
"details_no_cancel": "No, cancelar",
|
||||
"details_show_xpub": "Mostrar el XPUB de la cartera",
|
||||
"details_show_addresses": "Mostrar dirección",
|
||||
"details_title": "Cartera",
|
||||
|
|
|
@ -422,7 +422,6 @@
|
|||
"details_export_history": "Exportar historial a CSV",
|
||||
"details_master_fingerprint": "Huella Digital Maestra",
|
||||
"details_multisig_type": "multifirma",
|
||||
"details_no_cancel": "No, cancelar",
|
||||
"details_show_xpub": "Mostrar el XPUB de la Billetera",
|
||||
"details_show_addresses": "Mostrar direcciones",
|
||||
"details_title": "Billetera",
|
||||
|
@ -493,7 +492,9 @@
|
|||
"identity_pubkey": "Identidad Pubkey",
|
||||
"xpub_title": "XPUB de la billetera",
|
||||
"manage_wallets_search_placeholder": "Buscar billeteras, notas",
|
||||
"more_info": "Más información"
|
||||
"more_info": "Más información",
|
||||
"details_delete_wallet_error_message": "Hubo un problema al confirmar si esta billetera se eliminó de las notificaciones, lo que podría deberse a un problema de red o a una mala conexión. Si continúas, es posible que aún recibas notificaciones de transacciones relacionadas con esta billetera, incluso después de que se elimine.",
|
||||
"details_delete_anyway": "Borrar de todos modos"
|
||||
},
|
||||
"total_balance_view": {
|
||||
"display_in_bitcoin": "Mostrar en Bitcoin",
|
||||
|
@ -508,6 +509,10 @@
|
|||
"default_label": "Bóveda Multifirma",
|
||||
"multisig_vault_explain": "La mejor seguridad para grandes cantidades",
|
||||
"provide_signature": "Proporcionar firma",
|
||||
"provide_signature_details": "Usa tu dispositivo y billetera donde reside la llave para firmar esta transacción",
|
||||
"provide_signature_details_bluewallet": "En BlueWallet, ve al menú de la pantalla Enviar y selecciona",
|
||||
"provide_signature_next_steps": "Escanea o importa la transacción firmada",
|
||||
"provide_signature_next_steps_details": "Una vez que tu billetera haya firmado con éxito la transacción, escanea el código QR proporcionado o importa el archivo que lo acompaña, y luego revisa todos los detalles de la transacción antes de transmitirla.",
|
||||
"vault_key": "Clave de la Bóveda {number}",
|
||||
"required_keys_out_of_total": "Llaves requeridas del total",
|
||||
"fee": "Tarifa: {number}",
|
||||
|
@ -669,7 +674,7 @@
|
|||
"notification_tx_unconfirmed": "La transacción de notificación aún no está confirmada, espera",
|
||||
"failed_create_notif_tx": "No se pudo crear una transacción en cadena",
|
||||
"onchain_tx_needed": "Se necesita transacción en cadena",
|
||||
"notif_tx_sent" : "Transacción de notificación enviada. Espera a que se confirme",
|
||||
"notif_tx_sent": "Transacción de notificación enviada. Espera a que se confirme",
|
||||
"notif_tx": "Transacción de notificación",
|
||||
"not_found": "Código de pago no encontrado"
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
"storage_is_encrypted": "Sinu hoidla on krüpteeritud. Dekrüpteerimiseks on vajalik sisestada parool.",
|
||||
"yes": "Jah",
|
||||
"no": "Ei",
|
||||
"save": "Salvesta",
|
||||
"seed": "Seeme",
|
||||
"success": "Toiming õnnestus",
|
||||
"wallet_key": "Rahakoti võti"
|
||||
|
@ -41,7 +40,6 @@
|
|||
"create_to": "Sihtkoht"
|
||||
},
|
||||
"settings": {
|
||||
"electrum_clear_alert_cancel": "Katkesta",
|
||||
"save": "Salvesta"
|
||||
},
|
||||
"wallets": {
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
"storage_is_encrypted": "فضای ذخیرهسازی شما رمزگذاری شده است. برای رمزگشایی آن به گذرواژه نیاز است.",
|
||||
"yes": "بله",
|
||||
"no": "خیر",
|
||||
"save": "ذخیره",
|
||||
"seed": "سید",
|
||||
"success": "موفقیتآمیز بود",
|
||||
"wallet_key": "کلید کیف پول",
|
||||
|
@ -210,13 +209,8 @@
|
|||
"set_electrum_server_as_default": "آیا {server} بهعنوان سرور پیشفرض الکترام تعیین شود؟",
|
||||
"electrum_settings_server": "سرور الکترام",
|
||||
"electrum_status": "وضعیت",
|
||||
"electrum_clear_alert_title": "تاریخچه پاک شود؟",
|
||||
"electrum_clear_alert_message": "آیا میخواهید تاریخچهٔ سرورهای الکترام را پاک کنید؟",
|
||||
"electrum_clear_alert_cancel": "لغو",
|
||||
"electrum_clear_alert_ok": "بله",
|
||||
"electrum_reset": "بازنشانی به پیشفرض",
|
||||
"electrum_unable_to_connect": "ناموفق در اتصال به {server}",
|
||||
"electrum_reset_to_default": "آیا از بازنشانی تنظیمات الکترام به حالت پیشفرض اطمینان دارید؟",
|
||||
"electrum_reset": "بازنشانی به پیشفرض",
|
||||
"encrypt_decrypt": "رمزگشایی فضای ذخیرهسازی",
|
||||
"encrypt_decrypt_q": "آیا از رمزگشایی فضای ذخیرهسازی خود اطمینان دارید؟ این کار اجازه میدهد تا کیف پولهای شما بدون گذرواژه قابلدسترسی باشند.",
|
||||
"encrypt_enc_and_pass": "رمزگذاری و محافظت با گذرواژه",
|
||||
|
@ -338,7 +332,6 @@
|
|||
"details_export_history": "گرفتن خروجی تاریخچه به فرمت CSV",
|
||||
"details_master_fingerprint": "اثر انگشت اصلی",
|
||||
"details_multisig_type": "چندامضایی",
|
||||
"details_no_cancel": "خیر، لغو کن",
|
||||
"details_show_xpub": "نمایش XPUB کیف پول",
|
||||
"details_show_addresses": "نمایش آدرسها",
|
||||
"details_title": "کیف پول",
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
"storage_is_encrypted": "Tallennustilasi on salattu. Sen purkamiseksi vaaditaan salasana",
|
||||
"yes": "Kyllä",
|
||||
"no": "Ei",
|
||||
"save": "Tallenna",
|
||||
"seed": "Siemen",
|
||||
"success": "Onnistui",
|
||||
"wallet_key": "Lompakkoavain",
|
||||
|
@ -177,7 +176,6 @@
|
|||
"outdated_rate": "Vaihtokurssi päivitettiin viimeksi: {date}",
|
||||
"psbt_tx_open": "Avaa allekirjoitettu siirtotapahtuma",
|
||||
"psbt_tx_scan": "Skannaa allekirjoitettu siirtotapahtuma",
|
||||
"qr_error_no_qrcode": "Kuvasta ei löytynyt QR-koodia. Varmista että kuva sisältää ainoastaan QR-koodin eikä muita tietoja kuten tekstia tai nappeja.",
|
||||
"reset_amount": "Nollaa määrä",
|
||||
"reset_amount_confirm": "Haluaisitko nollata määrän?",
|
||||
"success_done": "Valmis",
|
||||
|
@ -223,13 +221,8 @@
|
|||
"set_electrum_server_as_default": "Asetetaanko {server} oletus Electrum-palvelimeksi?",
|
||||
"electrum_settings_server": "Electrum-palvelin",
|
||||
"electrum_status": "Tila",
|
||||
"electrum_clear_alert_title": "Tyhjennä historia?",
|
||||
"electrum_clear_alert_message": "Haluatko tyhjentää Electrum-palvelinten historian?",
|
||||
"electrum_clear_alert_cancel": "Peruuta",
|
||||
"electrum_clear_alert_ok": "Ok",
|
||||
"electrum_reset": "Palauta oletusasetuksiin",
|
||||
"electrum_unable_to_connect": " Ei saada yhteyttä {server}. ",
|
||||
"electrum_reset_to_default": "Haluatko varmasti palauttaa Electrumin asetukset oletusarvoihin? ",
|
||||
"electrum_reset": "Palauta oletusasetuksiin",
|
||||
"encrypt_decrypt": "Pura tallennustilan salaus",
|
||||
"encrypt_decrypt_q": "Haluatko varmasti purkaa tallennustilan salauksen? Tämä mahdollistaa lompakkoihisi pääsyn ilman salasanaa.",
|
||||
"encrypt_enc_and_pass": "Salattu ja Salasanalla suojattu",
|
||||
|
@ -357,7 +350,6 @@
|
|||
"details_export_history": "Vie historia CSV:ksi",
|
||||
"details_master_fingerprint": "Pää sormenjälki",
|
||||
"details_multisig_type": "multisig",
|
||||
"details_no_cancel": "Ei, peruuta",
|
||||
"details_show_xpub": "Näytä lompakon XPUB",
|
||||
"details_show_addresses": "Näytä osoitteet",
|
||||
"details_title": "Lompakko",
|
||||
|
|
|
@ -10,12 +10,10 @@
|
|||
"never": "Jamais",
|
||||
"of": "{number} sur {total}",
|
||||
"ok": "OK",
|
||||
"customize": "Personnaliser",
|
||||
"enter_url": "Entrer une URL",
|
||||
"storage_is_encrypted": "L'espace de stockage est chiffré. le mot de passe est requis pour le déchiffrer.",
|
||||
"yes": "Oui",
|
||||
"no": "Non",
|
||||
"save": "Enregistrer",
|
||||
"seed": "Graine",
|
||||
"success": "Succès",
|
||||
"wallet_key": "Clé du portefeuille",
|
||||
|
@ -255,8 +253,9 @@
|
|||
"electrum_preferred_server_description": "Saisissez le serveur que vous souhaitez que votre portefeuille utilise pour toutes les activités Bitcoin. Une fois défini, votre portefeuille utilisera exclusivement ce serveur pour vérifier les soldes, envoyer des transactions et récupérer les données du réseau. Assurez-vous de faire confiance à ce serveur avant de le configurer. ",
|
||||
"electrum_unable_to_connect": "Impossible de se connecter à {server}.",
|
||||
"electrum_history": "Historique",
|
||||
"electrum_reset_to_default": "Cela permettra à BlueWallet de choisir au hasard un serveur dans la liste et l'historique suggérés. L'historique de votre serveur restera inchangé. ",
|
||||
"electrum_reset_to_default": "Cela permettra à BlueWallet de choisir au hasard un serveur dans la liste des serveurs.",
|
||||
"electrum_reset": "Réinitialiser au valeurs par défaut",
|
||||
"electrum_reset_to_default_and_clear_history": "Réinitialiser les paramètres par défaut et effacer l'historique",
|
||||
"encrypt_decrypt": "Déchiffrer le stockage",
|
||||
"encrypt_decrypt_q": "Etes-vous sûr de vouloir déchiffrer le stockage ? L'accès à vos portefeuilles pourra alors se faire sans mot de passe.",
|
||||
"encrypt_enc_and_pass": "Chiffré et protégé par un mot de passe",
|
||||
|
@ -403,7 +402,6 @@
|
|||
"add_wallet_name": "nom",
|
||||
"add_wallet_type": "type",
|
||||
"add_wallet_seed_length": "Longueur de graîne",
|
||||
"add_wallet_seed_length_message": "Choisissez la longueur de la phrase de départ que vous souhaitez utiliser pour ce portefeuille.",
|
||||
"add_wallet_seed_length_12": "12 mots",
|
||||
"add_wallet_seed_length_24": "24 mots",
|
||||
"clipboard_bitcoin": "Vous avez une adresse bitcoin dans votre presse-papier. Voulez vous l'utiliser pour une transaction ?",
|
||||
|
@ -423,7 +421,6 @@
|
|||
"details_export_history": "Exporter l'historique au format CSV",
|
||||
"details_master_fingerprint": "Empreinte maitresse",
|
||||
"details_multisig_type": "multisig",
|
||||
"details_no_cancel": "Non, annuler",
|
||||
"details_show_xpub": "Afficher XPUB du portefeuille",
|
||||
"details_show_addresses": "Montrer les adresses",
|
||||
"details_title": "Portefeuille",
|
||||
|
@ -670,7 +667,7 @@
|
|||
"notification_tx_unconfirmed": "La transaction de notification n'est pas encore confirmée, veuillez patienter",
|
||||
"failed_create_notif_tx": "Échec de la création d'une transaction on-chain",
|
||||
"onchain_tx_needed": "Transaction on-chain nécessaire",
|
||||
"notif_tx_sent" : "Transaction de notification envoyée. Veuillez attendre qu'elle soit confirmée",
|
||||
"notif_tx_sent": "Transaction de notification envoyée. Veuillez attendre qu'elle soit confirmée",
|
||||
"notif_tx": "Transaction de notification ",
|
||||
"not_found": "Code de paiement introuvable"
|
||||
}
|
||||
|
|
25
loc/he.json
25
loc/he.json
|
@ -10,12 +10,10 @@
|
|||
"never": "אף פעם",
|
||||
"of": "{number} מתוך {total}",
|
||||
"ok": "אישור",
|
||||
"customize": "התאמה אישית",
|
||||
"enter_url": "הכנסת URL",
|
||||
"storage_is_encrypted": "האחסון שלך מוצפן. נדרשת סיסמה לפענוח שלו.",
|
||||
"yes": "כן",
|
||||
"no": "לא",
|
||||
"save": "שמירה",
|
||||
"seed": "גרעין",
|
||||
"success": "הצלחה",
|
||||
"wallet_key": "מפתח ארנק",
|
||||
|
@ -28,6 +26,8 @@
|
|||
"enter_amount": "הכנסת סכום",
|
||||
"qr_custom_input_button": "הקישו 10 פעמים כדי להכניס קלט מותאם",
|
||||
"unlock": "פתיחה",
|
||||
"port": "פתחה",
|
||||
"ssl_port": "פתחת SSL",
|
||||
"suggested": "מוצע"
|
||||
},
|
||||
"azteco": {
|
||||
|
@ -72,6 +72,7 @@
|
|||
"open_direct_channel": "פתח ערוץ ישיר עם צומת זה:",
|
||||
"please_pay_between_and": "אנא שלמו בין {min} לבין {max}",
|
||||
"please_pay": "אנא שלמו",
|
||||
"date_time": "תאריך ושעה",
|
||||
"wasnt_paid_and_expired": "חשבונית זו לא שולמה ופגה"
|
||||
},
|
||||
"plausibledeniability": {
|
||||
|
@ -103,8 +104,7 @@
|
|||
"maxSatsFull": "סכום מקסימלי הינו {max} sats או {currency}",
|
||||
"minSats": "סכום מינימלי הינו {min} sats",
|
||||
"minSatsFull": "סכום מינימלי הינו {min} sats או {currency}",
|
||||
"qrcode_for_the_address": "קוד QR לכתובת",
|
||||
"bip47_explanation": "קודי תשלום הם כתובת אוניברסלית שמונעת חשיפה של כתובות הארנק שלך. לא כל השירותים יתמכו בהם."
|
||||
"qrcode_for_the_address": "קוד QR לכתובת"
|
||||
},
|
||||
"send": {
|
||||
"provided_address_is_invoice": "נראה שכתובת זו היא חשבונית ברק. אנא עברו לארנק הברק שלכם כדי לבצע תשלום עבור חשבונית זו.",
|
||||
|
@ -188,7 +188,6 @@
|
|||
"outdated_rate": "תעריף עודכן לאחרונה: {date}",
|
||||
"psbt_tx_open": "פתחו פעולה חתומה",
|
||||
"psbt_tx_scan": "סרקו פעולה חתומה",
|
||||
"qr_error_no_qrcode": "לא הצלחנו למצוא קוד QR בתמונה הנבחרת. אנא ודאו כי התמונה מכילה רק קוד QR, ולא תוכן נוסף כמו טקסט, או כפתורים.",
|
||||
"reset_amount": "איפוס סכום",
|
||||
"reset_amount_confirm": "האם ברצונך לאפס את הסכום?",
|
||||
"success_done": "בוצע",
|
||||
|
@ -246,15 +245,9 @@
|
|||
"electrum_settings_server": "שרת אלקטרום",
|
||||
"electrum_status": "מצב",
|
||||
"electrum_preferred_server": "שרת מועדף",
|
||||
"electrum_clear_alert_title": "ניקוי היסטוריה?",
|
||||
"electrum_clear_alert_message": "האם ברצונך לנקות היסטורית שרתי אלקטרום?",
|
||||
"electrum_clear_alert_cancel": "ביטול",
|
||||
"electrum_clear_alert_ok": "אישור",
|
||||
"electrum_reset": "איפוס ברירת מחדל",
|
||||
"electrum_unable_to_connect": "לא מסוגל להתחבר לשרת {server}.",
|
||||
"electrum_history": "היסטוריה",
|
||||
"electrum_reset_to_default": "האם אתם בטוחים שברצונכם לשחזר את הגדרות האקלטרום שלכם לברירת מחדל?",
|
||||
"electrum_clear": "ניקוי היסטוריה",
|
||||
"electrum_reset": "איפוס ברירת מחדל",
|
||||
"encrypt_decrypt": "פתיחת אחסון מוצפן",
|
||||
"encrypt_decrypt_q": "האם לפענח אחסון מוצפן? זה יאפשר לגשת לארנקים שלך ללא סיסמה.",
|
||||
"encrypt_enc_and_pass": "מוצפן ומוגן על ידי סיסמה",
|
||||
|
@ -266,6 +259,7 @@
|
|||
"encrypt_title": "אבטחה",
|
||||
"encrypt_tstorage": "אחסון",
|
||||
"encrypt_use": "השתמש {type}",
|
||||
"set_as_preferred": "הגדרה כמועדף",
|
||||
"encrypted_feature_disabled": "לא ניתן להשתמש בתכונה זאת עם הצפנת אחסון מופעלת.",
|
||||
"encrypt_use_expl": "{type} ישמש לאמת את זהותך לפני ביצוע פעולה, פתיחה, יצוא או מחיקה של ארנק. {type} אינו ישמש לפתיחת אחסון מוצפן.",
|
||||
"biometrics_fail": "אם {type} לא מאופשר, או נכשל בפתיחה, תוכלו להשתמש בסיסמת המכשיר שלכם בתור חלופה.",
|
||||
|
@ -391,7 +385,6 @@
|
|||
"add_wallet_name": "שם",
|
||||
"add_wallet_type": "סוג",
|
||||
"add_wallet_seed_length": "אורך גרעין",
|
||||
"add_wallet_seed_length_message": "בחרו את אורך צירוף הגרעין שברצונכם להשתמש בשביל ארנק זה.",
|
||||
"add_wallet_seed_length_12": "12 מילים",
|
||||
"add_wallet_seed_length_24": "24 מילים",
|
||||
"clipboard_bitcoin": "ישנה כתובת ביטקוין בלוח. האם תרצו להשתמש בה בשביל העברה?",
|
||||
|
@ -411,7 +404,6 @@
|
|||
"details_export_history": "יצוא היסטוריה ל- CSV",
|
||||
"details_master_fingerprint": "טביעת אצבע ראשית",
|
||||
"details_multisig_type": "רב-חתימות",
|
||||
"details_no_cancel": "לא, בטל",
|
||||
"details_show_xpub": "הצגת מפתח צפייה של הארנק",
|
||||
"details_show_addresses": "הצגת כתובות",
|
||||
"details_title": "ארנק",
|
||||
|
@ -469,6 +461,10 @@
|
|||
"select_wallet": "בחירת ארנק",
|
||||
"xpub_copiedToClipboard": "הועתק ללוח.",
|
||||
"pull_to_refresh": "משכו כדי לרענן",
|
||||
"warning_do_not_disclose": "לעולם אל תשתפו את המידע למטה",
|
||||
"write_down_header": "יצירת גיבוי ידני",
|
||||
"wallet_type_this": "סוג ארנק זה הוא {type}.",
|
||||
"share_number": "שיתוף {number}",
|
||||
"add_ln_wallet_first": "עלייך להוסיף ארנק ברק קודם.",
|
||||
"identity_pubkey": "מפתח זהות ציבורי",
|
||||
"xpub_title": "מפתח צפייה של הארנק",
|
||||
|
@ -629,6 +625,7 @@
|
|||
"bip47": {
|
||||
"payment_code": "קוד תשלום",
|
||||
"contacts": "אנשי קשר",
|
||||
"bip47_explain": "קוד רב פעמי ובר שיתוף",
|
||||
"purpose": "קוד רב-פעמי ובר-שיתוף (BIP47)",
|
||||
"pay_this_contact": "תשלום לאיש קשר זה",
|
||||
"rename_contact": "שינוי שם איש קשר",
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
"storage_is_encrypted": "Vaš spremnik je kriptiran. Za dekripcoju je potrebna lozinka.",
|
||||
"yes": "Da",
|
||||
"no": "Ne",
|
||||
"save": "Spremi",
|
||||
"seed": "Izvor",
|
||||
"success": "Uspjeh"
|
||||
},
|
||||
|
@ -68,7 +67,6 @@
|
|||
"settings": {
|
||||
"about": "Informacije",
|
||||
"currency": "Valuta",
|
||||
"electrum_clear_alert_cancel": "Otkaži",
|
||||
"header": "Postavke",
|
||||
"language": "Jezik",
|
||||
"lightning_settings": "Lightning postavke",
|
||||
|
@ -95,7 +93,6 @@
|
|||
"details_are_you_sure": "Jesi li ziher?",
|
||||
"details_delete": "Obriši",
|
||||
"details_export_backup": "Izvoz / bekap",
|
||||
"details_no_cancel": "Ne, otiaži",
|
||||
"details_show_xpub": "Prikaži voletov XPUB",
|
||||
"details_title": "Volet",
|
||||
"wallets": "Voleti",
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
"storage_is_encrypted": "A Tárhely titkosítva. Jelszó szükséges a dekódoláshoz",
|
||||
"yes": "Igen",
|
||||
"no": "Nem",
|
||||
"save": "Mentés",
|
||||
"seed": "jelszó sorozat",
|
||||
"success": "Sikeres",
|
||||
"wallet_key": "Tárca kulcs",
|
||||
|
@ -162,7 +161,6 @@
|
|||
"outdated_rate": "A ráta utoljára frissítve: {date}",
|
||||
"psbt_tx_open": "Aláírt tranzakció megnyitása",
|
||||
"psbt_tx_scan": "Aláírt tranzakció szkennelése",
|
||||
"qr_error_no_qrcode": "Nem találtunk QR kódot a kiválasztott képen. Győződjön meg arról, hogy a kép csak QR kódot tartalmaz, és nem tartalmaz további tartalmat, például szöveget vagy gombokat.",
|
||||
"reset_amount": "Összeg Visszaállítása",
|
||||
"reset_amount_confirm": "Valóban visszaállítja az összeget?",
|
||||
"success_done": "Kész!",
|
||||
|
@ -206,13 +204,8 @@
|
|||
"set_electrum_server_as_default": "{server} bállítása alapértelmezett Electrum szerverként?",
|
||||
"electrum_settings_server": "Electrum Szerver",
|
||||
"electrum_status": "Állapot",
|
||||
"electrum_clear_alert_title": "Előzmények törlése?",
|
||||
"electrum_clear_alert_message": "Kiszeretné törölni az Electrum Szerver előzményeket?",
|
||||
"electrum_clear_alert_cancel": "Mégse",
|
||||
"electrum_clear_alert_ok": "OK",
|
||||
"electrum_reset": "alapértelmezett beállítások visszaállítása",
|
||||
"electrum_unable_to_connect": "Nincs kapcsolat {server} -hez.",
|
||||
"electrum_reset_to_default": "Biztosan vissza akarja alapértelmezettre állitani az Electrum szerver beállításait?",
|
||||
"electrum_reset": "alapértelmezett beállítások visszaállítása",
|
||||
"encrypt_decrypt": "Háttértár titkosításának feloldása",
|
||||
"encrypt_decrypt_q": "Biztosan feloldod a háttértár titkosítását? Ez lehetővé teszi a tárca jelszó nélküli használatát. ",
|
||||
"encrypt_enc_and_pass": "Titkosítva és jelszóval védve",
|
||||
|
@ -333,7 +326,6 @@
|
|||
"details_export_backup": "Exportálás / Biztonsági mentés",
|
||||
"details_master_fingerprint": "Mester ujjlenyomat ",
|
||||
"details_multisig_type": "multisig",
|
||||
"details_no_cancel": "Nem, megszakít",
|
||||
"details_show_xpub": "Mutasd a tárca XPUB kulcsát",
|
||||
"details_show_addresses": "Cím mutatása",
|
||||
"details_title": "Tárca",
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
"storage_is_encrypted": "Ruang penyimpanan terenkripsi. Masukkan kata sandi untuk decript:",
|
||||
"yes": "Ya",
|
||||
"no": "Tidak",
|
||||
"save": "Simpan",
|
||||
"seed": "Benih",
|
||||
"success": "Sukses",
|
||||
"wallet_key": "Kunci Dompet",
|
||||
|
@ -200,13 +199,8 @@
|
|||
"use_ssl": "Gunakan SSL",
|
||||
"electrum_settings_server": "Server Electrum",
|
||||
"electrum_status": "Status",
|
||||
"electrum_clear_alert_title": "Hapus riwayat?",
|
||||
"electrum_clear_alert_message": "Apakah Anda ingin menghapus riwayat server Electrum?",
|
||||
"electrum_clear_alert_cancel": "Batalkan",
|
||||
"electrum_clear_alert_ok": "OK",
|
||||
"electrum_reset": "Atur ulang ke bawaan",
|
||||
"electrum_unable_to_connect": "Tidak bisa mengubungkan ke {server}.",
|
||||
"electrum_reset_to_default": "Apakah Anda yakin ingin mengatur ulang Electrum Anda ke pengaturan bawaan?",
|
||||
"electrum_reset": "Atur ulang ke bawaan",
|
||||
"encrypt_enc_and_pass": "Dienkripsi dan kata sandi dilindungi",
|
||||
"encrypt_title": "Keamanan",
|
||||
"encrypt_tstorage": "Penyimpanan",
|
||||
|
@ -284,7 +278,6 @@
|
|||
"details_export_backup": "Ekspor / backup",
|
||||
"details_export_history": "Ekspor riwayat ke CSV",
|
||||
"details_master_fingerprint": "Sidik Jari Utama",
|
||||
"details_no_cancel": "Tidak, batalkan",
|
||||
"details_show_xpub": "Tampilkan XPUB dompet",
|
||||
"details_show_addresses": "Tunjukkan alamat",
|
||||
"details_title": "Dompet",
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
"storage_is_encrypted": "Il tuo archivio è criptato. È necessaria una password per decriptarlo.",
|
||||
"yes": "Sì",
|
||||
"no": "No",
|
||||
"save": "Salva",
|
||||
"seed": "Seed",
|
||||
"success": "Operazione avvenuta con successo",
|
||||
"wallet_key": "Chiave del portafoglio",
|
||||
|
@ -207,13 +206,8 @@
|
|||
"set_electrum_server_as_default": "Impostare {server} come server Electrum predefinito?",
|
||||
"electrum_settings_server": "Server Electrum",
|
||||
"electrum_status": "Stato",
|
||||
"electrum_clear_alert_title": "Cancella la cronologia?",
|
||||
"electrum_clear_alert_message": "Desideri eliminare la cronologia dei server Electrum?",
|
||||
"electrum_clear_alert_cancel": "Annulla",
|
||||
"electrum_clear_alert_ok": "Ok",
|
||||
"electrum_reset": "Ripristino delle impostazioni predefinite",
|
||||
"electrum_unable_to_connect": "Impossibile collegarsi a {server}.",
|
||||
"electrum_reset_to_default": "Desideri veramente ripristinare le impostazioni Electrum predefinite?",
|
||||
"electrum_reset": "Ripristino delle impostazioni predefinite",
|
||||
"encrypt_decrypt": "Decripta l'archivio",
|
||||
"encrypt_decrypt_q": "Desideri veramente decriptare il tuo archivio? Quest'azione avrà come conseguenza di permettere l'accesso ai portafogli senza una password.",
|
||||
"encrypt_enc_and_pass": "Criptato e protetto da password",
|
||||
|
@ -333,7 +327,6 @@
|
|||
"details_export_history": "Esporta la cronologia in un file CSV",
|
||||
"details_master_fingerprint": "Master Fingerprint",
|
||||
"details_multisig_type": "multisig",
|
||||
"details_no_cancel": "No, annulla",
|
||||
"details_show_xpub": "Mostra XPUB del portafoglio",
|
||||
"details_show_addresses": "Mostra indirizzi",
|
||||
"details_title": "Portafoglio",
|
||||
|
|
|
@ -10,12 +10,11 @@
|
|||
"never": "データなし",
|
||||
"of": "{number} / {total}",
|
||||
"ok": "OK",
|
||||
"customize": "カスタマイズ",
|
||||
"enter_url": "URLを入力",
|
||||
"storage_is_encrypted": "ウォレットは暗号化されています。復号にはパスワードが必要です。",
|
||||
"yes": "はい",
|
||||
"no": "いいえ",
|
||||
"save": "保存",
|
||||
"save": "保存...",
|
||||
"seed": "シード",
|
||||
"success": "成功",
|
||||
"wallet_key": "ウォレットキー",
|
||||
|
@ -28,6 +27,8 @@
|
|||
"enter_amount": "額を入力",
|
||||
"qr_custom_input_button": "10回タップしてカスタム入力",
|
||||
"unlock": "ロック解除",
|
||||
"port": "ポート",
|
||||
"ssl_port": "SSLポート",
|
||||
"suggested": "サジェスト"
|
||||
},
|
||||
"azteco": {
|
||||
|
@ -74,6 +75,7 @@
|
|||
"please_pay": "取引額",
|
||||
"preimage": "プリイメージ",
|
||||
"sats": "sats",
|
||||
"date_time": "日時",
|
||||
"wasnt_paid_and_expired": "この請求書は支払いが行われなかったため無効になりました"
|
||||
},
|
||||
"plausibledeniability": {
|
||||
|
@ -190,7 +192,7 @@
|
|||
"outdated_rate": "レートの最終更新:{date}",
|
||||
"psbt_tx_open": "署名トランザクションを開く",
|
||||
"psbt_tx_scan": "署名トランザクションをスキャン",
|
||||
"qr_error_no_qrcode": "選択した画像からQRコードが見つかりませんでした。画像がQRコードのみを含み、テキストやボタンなど別の内容を含まないようにしてください。",
|
||||
"qr_error_no_qrcode": "選択した画像から有効なQRコードが見つかりませんでした。画像がQRコードのみを含み、テキストやボタンなど別の内容を含まないようにしてください。",
|
||||
"reset_amount": "額をリセット",
|
||||
"reset_amount_confirm": "額をリセットしますか?",
|
||||
"success_done": "完了",
|
||||
|
@ -250,15 +252,11 @@
|
|||
"electrum_status": "ステータス",
|
||||
"electrum_preferred_server": "優先サーバー",
|
||||
"electrum_preferred_server_description": "あなたのウォレットのすべてのビットコイン取引に使うサーバーを入力します。設定すると、あなたのウォレットは残高の確認、トランザクションの送信、ネットワークデータの取得にこのサーバーだけを使います。信用するサーバーを設定するようにしてください。",
|
||||
"electrum_clear_alert_title": "履歴を削除しますか?",
|
||||
"electrum_clear_alert_message": "Electrumサーバーヒストリーをクリアしますか?",
|
||||
"electrum_clear_alert_cancel": "キャンセル",
|
||||
"electrum_clear_alert_ok": "Ok",
|
||||
"electrum_reset": "デフォルトの設定に戻す",
|
||||
"electrum_unable_to_connect": "{server}に接続できません。",
|
||||
"electrum_history": "履歴",
|
||||
"electrum_reset_to_default": "Electrumの設定をデフォルトに戻してよろしいですか?",
|
||||
"electrum_clear": "履歴を削除",
|
||||
"electrum_reset_to_default": "サーバーリストからBlueWalletがランダムにサーバーを選択するようになります。",
|
||||
"electrum_reset": "デフォルトの設定に戻す",
|
||||
"electrum_reset_to_default_and_clear_history": "デフォルトにリセットして履歴を削除",
|
||||
"encrypt_decrypt": "ストレージ復号化",
|
||||
"encrypt_decrypt_q": "本当にストレージを復号化しますか?これによりウォレットがパスワードなしでアクセス可能になります。",
|
||||
"encrypt_enc_and_pass": "暗号化しパスワードで保護",
|
||||
|
@ -272,6 +270,8 @@
|
|||
"encrypt_title": "セキュリティ",
|
||||
"encrypt_tstorage": "ストレージ",
|
||||
"encrypt_use": "{type} を使う",
|
||||
"set_as_preferred": "優先に設定",
|
||||
"set_as_preferred_electrum": "優先サーバーとして {host}:{port} を設定することで、おすすめサーバーへのランダムな接続が無効になります。",
|
||||
"encrypted_feature_disabled": "ストレージの暗号化が有効の場合この機能は使えません。",
|
||||
"encrypt_use_expl": "{type} は、トランザクションの実行、ウォレットのロック解除、エクスポート、または削除を行う前の本人確認に使用されます。{type} は暗号化されたストレージのロック解除には使用されません。",
|
||||
"biometrics_fail": "もし {type} が有効でない、または解除に失敗した場合、デバイスのパスコードを代わりに使うことができます。",
|
||||
|
@ -291,6 +291,7 @@
|
|||
"network": "ネットワーク",
|
||||
"network_broadcast": "ブロードキャストトランザクション",
|
||||
"network_electrum": "Electrum サーバー",
|
||||
"electrum_suggested_description": "優先サーバーが設定されていない場合、ランダムに選ばれたおすすめのサーバーが使われます。",
|
||||
"not_a_valid_uri": "無効なURI",
|
||||
"notifications": "通知",
|
||||
"open_link_in_explorer": "エクスプローラで開く",
|
||||
|
@ -402,7 +403,6 @@
|
|||
"add_wallet_name": "ウォレット名",
|
||||
"add_wallet_type": "タイプ",
|
||||
"add_wallet_seed_length": "シード長",
|
||||
"add_wallet_seed_length_message": "このウォレットで使いたいシードフレーズの長さを選択してください。",
|
||||
"add_wallet_seed_length_12": "12語",
|
||||
"add_wallet_seed_length_24": "24語",
|
||||
"clipboard_bitcoin": "クリップボードにビットコインのアドレスがあります。このアドレスを使って取引をしますか?",
|
||||
|
@ -422,7 +422,6 @@
|
|||
"details_export_history": "履歴をCSVにエクスポート",
|
||||
"details_master_fingerprint": "マスタフィンガープリント",
|
||||
"details_multisig_type": "マルチシグ",
|
||||
"details_no_cancel": "いいえ、中止します",
|
||||
"details_show_xpub": "ウォレット XPUB の表示",
|
||||
"details_show_addresses": "アドレスを表示",
|
||||
"details_title": "ウォレット",
|
||||
|
@ -493,7 +492,9 @@
|
|||
"identity_pubkey": "識別用公開鍵",
|
||||
"xpub_title": "ウォレット XPUB",
|
||||
"manage_wallets_search_placeholder": "ウォレット・メモを検索",
|
||||
"more_info": "詳細情報"
|
||||
"more_info": "詳細情報",
|
||||
"details_delete_wallet_error_message": "ウォレットが通知から削除されたかの確認に問題が生じました—ネットワークの問題か、接続が弱いためかもしれません。続行すると、ウォレットを削除した後でも、関連するトランザクションの通知を受け取る可能性があります。",
|
||||
"details_delete_anyway": "とにかく削除"
|
||||
},
|
||||
"total_balance_view": {
|
||||
"display_in_bitcoin": "ビットコインで表示",
|
||||
|
@ -508,6 +509,10 @@
|
|||
"default_label": "マルチシグ金庫",
|
||||
"multisig_vault_explain": "大きな資産にベストなセキュリティ",
|
||||
"provide_signature": "署名を提供",
|
||||
"provide_signature_details": "このトランザクションに署名するためのキーを持っているデバイスとウォレットを使ってください",
|
||||
"provide_signature_details_bluewallet": "BlueWalletで、送金スクリーンメニューに移動し",
|
||||
"provide_signature_next_steps": "署名済みトランザクションをスキャンまたはインポート",
|
||||
"provide_signature_next_steps_details": "ウォレットを使ったトランザクションの署名が終わったら、QRコードをスキャンまたはファイルをインポートして、トランザクションの詳細をすべて確認してから配信してください。",
|
||||
"vault_key": "金庫キー {number}",
|
||||
"required_keys_out_of_total": "全体のうち必要なキー",
|
||||
"fee": "費用: {number}",
|
||||
|
@ -654,6 +659,8 @@
|
|||
"bip47": {
|
||||
"payment_code": "支払コード",
|
||||
"contacts": "連絡先",
|
||||
"bip47_explain": "再利用・共有可能なコード",
|
||||
"bip47_explain_subtitle": "BIP47",
|
||||
"purpose": "再利用・共有可能なコード (BIP47)",
|
||||
"pay_this_contact": "この連絡先に支払う",
|
||||
"rename_contact": "連絡先をリネーム",
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
"ok": "ОК",
|
||||
"yes": "Иә",
|
||||
"no": "Жоқ",
|
||||
"save": "Сақтау",
|
||||
"close": "Жабу"
|
||||
},
|
||||
"entropy": {
|
||||
|
@ -54,7 +53,6 @@
|
|||
},
|
||||
"settings": {
|
||||
"about_license": "MIT License",
|
||||
"electrum_clear_alert_cancel": "Бас тарту",
|
||||
"header": "Баптаулар",
|
||||
"save": "Сақтау"
|
||||
},
|
||||
|
@ -65,7 +63,6 @@
|
|||
"wallets": {
|
||||
"add_create": "Жасау",
|
||||
"details_address": "Адрес",
|
||||
"details_no_cancel": "Жоқ, бас тарту",
|
||||
"import_do_import": "Еңгізу",
|
||||
"import_search_accounts": "Шот іздеу",
|
||||
"import_title": "Енгізу",
|
||||
|
|
|
@ -10,14 +10,12 @@
|
|||
"storage_is_encrypted": "ನಿಮ್ಮ ಸಂಗ್ರಹಣೆಯನ್ನು ಎನ್ಕ್ರಿಪ್ಟ್ ಮಾಡಲಾಗಿದೆ. ಅದನ್ನು ಡೀಕ್ರಿಪ್ಟ್ ಮಾಡಲು ಪಾಸ್ವರ್ಡ್ ಅಗತ್ಯವಿದೆ.",
|
||||
"yes": "ಹೌದು",
|
||||
"no": "ಇಲ್ಲ",
|
||||
"save": "ಉಳಿಸಿ",
|
||||
"wallet_key": "ವ್ಯಾಲೆಟ್ ಕೀ"
|
||||
},
|
||||
"entropy": {
|
||||
"save": "ಉಳಿಸಿ"
|
||||
},
|
||||
"settings": {
|
||||
"electrum_clear_alert_cancel": "ರದ್ದುಮಾಡಿ",
|
||||
"save": "ಉಳಿಸಿ"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,12 +10,10 @@
|
|||
"never": "존재하지 않음",
|
||||
"of": "{total} 개중 {number}",
|
||||
"ok": "확인",
|
||||
"customize": "맞춤화",
|
||||
"enter_url": "URL을 입력",
|
||||
"storage_is_encrypted": "저장된 데이터가 암호화되었습니다. 암호화를 해독하려면 비밀번호를 입력하십시오.",
|
||||
"yes": "네",
|
||||
"no": "아니요",
|
||||
"save": "저장",
|
||||
"seed": "시드",
|
||||
"success": "성공",
|
||||
"wallet_key": "지갑 키",
|
||||
|
@ -172,7 +170,6 @@
|
|||
"outdated_rate": "최근 요율 갱신: {date}",
|
||||
"psbt_tx_open": "서명된 거래 열기",
|
||||
"psbt_tx_scan": "서명된 거래 스캔하기",
|
||||
"qr_error_no_qrcode": "선택된 이미지에서 QR코드를 인식할 수 없습니다. 이미지에 QR코드를 제외한 문자, 버튼 등의 다른 내용이 포함되어 있는 지 확인해주세요.",
|
||||
"reset_amount": "금액 재설정",
|
||||
"reset_amount_confirm": "금액을 재설정하기 원하십니까?",
|
||||
"success_done": "완료되었습니다",
|
||||
|
@ -216,13 +213,8 @@
|
|||
"set_electrum_server_as_default": "{server}를 기본 일렉트럼 서버로 설정하시겠습니까?",
|
||||
"electrum_settings_server": "일렉트럼 서버",
|
||||
"electrum_status": "상태",
|
||||
"electrum_clear_alert_title": "내역을 지울까요?",
|
||||
"electrum_clear_alert_message": "일렉트럼 서버 내역을 지우길 원하십니까?",
|
||||
"electrum_clear_alert_cancel": "최소하기",
|
||||
"electrum_clear_alert_ok": "Ok",
|
||||
"electrum_reset": "기본값으로 환원",
|
||||
"electrum_unable_to_connect": "{server}에 접속할 수 없음",
|
||||
"electrum_reset_to_default": "일렉트럼 설정을 기본값으로 환원하길 원하십니까?",
|
||||
"electrum_reset": "기본값으로 환원",
|
||||
"encrypt_decrypt": "해독 저장",
|
||||
"encrypt_decrypt_q": "스토리지를 암호화하시겠습니까? 설정 후에는 패스워드 없이 월렛에 접근할 수 없습니다. ",
|
||||
"encrypt_enc_and_pass": "암호화되고 패스워드로 보호됨",
|
||||
|
@ -339,7 +331,6 @@
|
|||
"details_export_backup": "보내기/백업",
|
||||
"details_master_fingerprint": "마스터 지문",
|
||||
"details_multisig_type": "다중서명",
|
||||
"details_no_cancel": "아니요. 취소하겠습니다",
|
||||
"details_show_xpub": "지갑의 XPUB 보이기",
|
||||
"details_show_addresses": "주소 보이기",
|
||||
"details_title": "지갑",
|
||||
|
|
|
@ -94,10 +94,6 @@
|
|||
"set_electrum_server_as_default": "{server} سی سرور پؽش فرز الکترام ساموݩ بۊئه؟",
|
||||
"electrum_settings_server": "سرور الکترام",
|
||||
"electrum_status": "وزیت",
|
||||
"electrum_clear_alert_title": "ویرگار پاک بۊئه؟",
|
||||
"electrum_clear_alert_message": "مؽهای ویرگار سرور الکترامن پاک بکی؟",
|
||||
"electrum_clear_alert_cancel": "لقو",
|
||||
"electrum_clear_alert_ok": "خو",
|
||||
"encrypt_title": "امنیت",
|
||||
"encrypt_use": "{type} نه وه کار بییر",
|
||||
"header": "سامونیا",
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
"storage_is_encrypted": "Simpanan anda disulitkan. Kata laluan diperlukan untuk nyahsulit.",
|
||||
"yes": "Ya",
|
||||
"no": "Tidak",
|
||||
"save": "Simpan",
|
||||
"seed": "Benih",
|
||||
"success": "Berjaya",
|
||||
"wallet_key": "Anak kunci dompet"
|
||||
|
@ -185,13 +184,8 @@
|
|||
"set_electrum_server_as_default": "Tetapkan {server} sebagai pelayan lalai Electrum?",
|
||||
"electrum_settings_server": "Pelayan Electrum",
|
||||
"electrum_status": "Keadaan",
|
||||
"electrum_clear_alert_title": "Padam sejarah?",
|
||||
"electrum_clear_alert_message": "Adakah anda mahu memadam sejarah pelayan Electrum?",
|
||||
"electrum_clear_alert_cancel": "Batal",
|
||||
"electrum_clear_alert_ok": "Ok",
|
||||
"electrum_reset": "Tetapkan semula kepada lalai",
|
||||
"electrum_unable_to_connect": "Tidak dapat dihubungkan ke {server}.",
|
||||
"electrum_reset_to_default": "Adakah anda pasti anda mahu tetapkan semula Electrum kepada tetapan lalai?",
|
||||
"electrum_reset": "Tetapkan semula kepada lalai",
|
||||
"encrypt_decrypt": "Nyahsulit Simpanan",
|
||||
"encrypt_decrypt_q": "Adakah anda pasti anda mahu nyahkunci simpanan anda? Ini akan membolehkan dompet anda dicapai tanpa kata laluan.",
|
||||
"encrypt_enc_and_pass": "Tersulitkan dan Dilindungi dengan Kata Laluan",
|
||||
|
@ -298,7 +292,6 @@
|
|||
"details_export_backup": "Pindahkan Keluar/Sandarkan",
|
||||
"details_master_fingerprint": "Cap Jari Induk",
|
||||
"details_multisig_type": "tandamai",
|
||||
"details_no_cancel": "Tidak, batalkan",
|
||||
"details_show_xpub": "Paparkan Dompet XPUB",
|
||||
"details_show_addresses": "Paparkan alamat",
|
||||
"details_title": "Dompet",
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
"storage_is_encrypted": "Din lagring er kryptert. Passord er nødvendig for å dekryptere det.",
|
||||
"yes": "Ja",
|
||||
"no": "Nei",
|
||||
"save": "Lagre",
|
||||
"seed": "Seed",
|
||||
"success": "Vellykket",
|
||||
"wallet_key": "Lommebok-nøkkel"
|
||||
|
@ -197,13 +196,8 @@
|
|||
"set_electrum_server_as_default": "Vil du angi {server} som standard Electrum-server?",
|
||||
"electrum_settings_server": "Electrum Server",
|
||||
"electrum_status": "Status",
|
||||
"electrum_clear_alert_title": "Slett logg?",
|
||||
"electrum_clear_alert_message": "Vil du slette electrum-serverhistorikken?",
|
||||
"electrum_clear_alert_cancel": "Avbryt",
|
||||
"electrum_clear_alert_ok": "Ok",
|
||||
"electrum_reset": "Tilbakestill til standard",
|
||||
"electrum_unable_to_connect": "Kan ikke koble til {server}.",
|
||||
"electrum_reset_to_default": "Er du sikker på at du vil tilbakestille Electrum-innstillingene dine til standard?",
|
||||
"electrum_reset": "Tilbakestill til standard",
|
||||
"encrypt_decrypt": "Dekrypter Lagring",
|
||||
"encrypt_decrypt_q": "Er du sikker på at du vil dekryptere lagringen din? Dette vil tillate tilgang til lommeboken din uten passord.",
|
||||
"encrypt_enc_and_pass": "Kryptert og passordbeskyttet",
|
||||
|
@ -321,7 +315,6 @@
|
|||
"details_export_backup": "Eksporter / backup",
|
||||
"details_master_fingerprint": "Master Fingerprint",
|
||||
"details_multisig_type": "multisig",
|
||||
"details_no_cancel": "Nei, avbryt",
|
||||
"details_show_xpub": "Vis lommebok XPUB",
|
||||
"details_show_addresses": "Vis adresser",
|
||||
"details_title": "Lommebok",
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
"storage_is_encrypted": "तपाईंको स्टोरेज इन्क्रिप्टेड छ। डिक्रिप्ट गर्न पासवर्ड को आवश्यक छ।",
|
||||
"yes": "हो",
|
||||
"no": "होइन",
|
||||
"save": "सेव",
|
||||
"seed": "सीड",
|
||||
"success": "सफल",
|
||||
"wallet_key": "वालेट को सांचो",
|
||||
|
@ -189,13 +188,8 @@
|
|||
"electrum_offline_mode": "अफलाइन मोड",
|
||||
"electrum_saved": "तपाईंको परिवर्तनहरू सफलतापूर्वक सुरक्षित गरिएको छ। परिवर्तनहरू प्रभाव पार्नको लागि बुलुवालेट पुन: सुरु गर्न आवश्यक हुन सक्छ।",
|
||||
"electrum_status": "स्थिति",
|
||||
"electrum_clear_alert_title": "स्पष्ट इतिहास?",
|
||||
"electrum_clear_alert_message": "के तपाई इलेक्ट्रम सेभर्स इतिहास खाली गर्न चाहनुहुन्छ?",
|
||||
"electrum_clear_alert_cancel": "रद्द गर्नुहोस्",
|
||||
"electrum_clear_alert_ok": "ठिक छ",
|
||||
"electrum_reset": "चूकेमा रिसेट गर्नुहोस्",
|
||||
"electrum_unable_to_connect": "जडान गर्न असमर्थ {server}.",
|
||||
"electrum_reset_to_default": "के तपाइँ तपाइँको इलेक्ट्रम सेटिङहरू पूर्वनिर्धारितमा रिसेट गर्न निश्चित हुनुहुन्छ?",
|
||||
"electrum_reset": "चूकेमा रिसेट गर्नुहोस्",
|
||||
"encrypt_decrypt": "डिक्रिप्ट स्टोरेज",
|
||||
"encrypt_title": "सुरक्षा",
|
||||
"encrypt_tstorage": "स्टोरेज",
|
||||
|
@ -261,7 +255,6 @@
|
|||
"details_delete": "हटाउनुहोस्",
|
||||
"details_delete_wallet": "वालेट हटाउनुहोस्",
|
||||
"details_master_fingerprint": "मास्टर फिंगरप्रिन्ट",
|
||||
"details_no_cancel": "होइन, रद्द गर्नुहोस्",
|
||||
"details_show_xpub": "वालेटको XPUB देखाउनुहोस्",
|
||||
"details_show_addresses": "ठेगानाहरू देखाउनुहोस्",
|
||||
"details_title": "वालेट",
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
"storage_is_encrypted": "Uw opslag is versleuteld. Wachtwoord is vereist om te ontcijferen.",
|
||||
"yes": "Ja",
|
||||
"no": "Nee",
|
||||
"save": "Opslaan",
|
||||
"seed": "Seed",
|
||||
"success": "Succes",
|
||||
"wallet_key": "Wallet sleutel",
|
||||
|
@ -202,13 +201,8 @@
|
|||
"set_electrum_server_as_default": "{server} instellen als de standaard electrum server?",
|
||||
"electrum_settings_server": "Electrum Server",
|
||||
"electrum_status": "Status",
|
||||
"electrum_clear_alert_title": "Geschiedenis verwijderen?",
|
||||
"electrum_clear_alert_message": "Wil je de geschiedenis van de electrum servers wissen?",
|
||||
"electrum_clear_alert_cancel": "Annuleren",
|
||||
"electrum_clear_alert_ok": "Oké",
|
||||
"electrum_reset": "Naar standaardinstellingen resetten",
|
||||
"electrum_unable_to_connect": "Niet in staat te verbinden met {server}.",
|
||||
"electrum_reset_to_default": "Weet u zeker dat u uw Electrum instellingen wil herstellen naar de standaardinstellingen?",
|
||||
"electrum_reset": "Naar standaardinstellingen resetten",
|
||||
"encrypt_decrypt": "Versleutel opslag",
|
||||
"encrypt_decrypt_q": "Weet u zeker dat u uw opslag wilt ontsleutelen? Hierdoor kunnen uw wallets zonder wachtwoord worden geopend.",
|
||||
"encrypt_enc_and_pass": "Versleuteld en wachtwoord beveiligd",
|
||||
|
@ -322,7 +316,6 @@
|
|||
"details_export_backup": "Exporteren / back-up maken",
|
||||
"details_master_fingerprint": "Master vingerafdruk",
|
||||
"details_multisig_type": "multisig",
|
||||
"details_no_cancel": "Nee, annuleren",
|
||||
"details_show_xpub": "Toon wallet XPUB",
|
||||
"details_show_addresses": "Toon adressen",
|
||||
"details_title": "Wallet",
|
||||
|
|
|
@ -49,11 +49,7 @@
|
|||
"default_info": "Normal info",
|
||||
"default_title": "As you launch",
|
||||
"default_wallets": "See all your wallets",
|
||||
"electrum_clear_alert_message": "You wan clear electrum servers history?",
|
||||
"electrum_clear_alert_cancel": "Cancel",
|
||||
"electrum_clear_alert_ok": "Ok",
|
||||
"electrum_unable_to_connect": "We nor fit connect to [server]",
|
||||
"electrum_reset_to_default": "You sure say you wan reset your Electrum settings to as e take dey before?"
|
||||
"electrum_unable_to_connect": "We nor fit connect to [server]"
|
||||
},
|
||||
"transactions": {
|
||||
"pending": "Pend"
|
||||
|
|
18
loc/pl.json
18
loc/pl.json
|
@ -10,12 +10,11 @@
|
|||
"never": "Nigdy",
|
||||
"of": "{number} z {total}",
|
||||
"ok": "OK",
|
||||
"customize": "Dostosuj",
|
||||
"enter_url": "Wprowadź URL",
|
||||
"storage_is_encrypted": "Twoje dane są zaszyfrowane. Hasło jest wymagane do ich rozszyfrowania",
|
||||
"yes": "Tak",
|
||||
"no": "Nie",
|
||||
"save": "Zapisz",
|
||||
"save": "Zapisz...",
|
||||
"seed": "Seed",
|
||||
"success": "Sukces",
|
||||
"wallet_key": "Klucz Portfela",
|
||||
|
@ -109,7 +108,7 @@
|
|||
"minSats": "Kwota minimalna to {min} satoshi",
|
||||
"minSatsFull": "Kwota minimalna to {min} satoshi lub {currency}",
|
||||
"qrcode_for_the_address": "Kod QR dla adresu",
|
||||
"bip47_explanation": "Kody płatności są uniwersalnym adresem, który unika ujawnienia adresów twojego portfela. Nie wszystkie usługi je obsługują."
|
||||
"bip47_explanation": "Kody płatności to uniwersalne adresy, które chronią prywatność Twojego portfela. Nie wszystkie usługi je obsługują."
|
||||
},
|
||||
"send": {
|
||||
"provided_address_is_invoice": "Ten adres wygląda na fakturę Lightning. Przejdź do swojego portfela Lightning aby ją opłacić.",
|
||||
|
@ -255,8 +254,9 @@
|
|||
"electrum_preferred_server_description": "Wprowadź serwer, którego ma używać twój portfel do wszystkich operacji związanych z Bitcoinem. Po zapisaniu ustawień, portfel będzie korzystał wyłącznie z tego serwera do sprawdzania sald, wysyłania transakcji oraz pobierania danych z sieci. Upewnij się, że masz zaufanie do tego serwera przed jego wyborem.",
|
||||
"electrum_unable_to_connect": "Nie można się połączyć z {server}.",
|
||||
"electrum_history": "Historia",
|
||||
"electrum_reset_to_default": "To pozwoli BlueWallet losowo wybrać serwer z sugerowanej listy i historii. Historia Twoich serwerów pozostanie niezmieniona.",
|
||||
"electrum_reset_to_default": "To pozwoli BlueWallet losowo wybrać serwer z listy.",
|
||||
"electrum_reset": "Ustaw wartości domyślne",
|
||||
"electrum_reset_to_default_and_clear_history": "Przywróć domyślne ustawienia i wyczyść historię",
|
||||
"encrypt_decrypt": "Odszyfruj Magazyn Danych",
|
||||
"encrypt_decrypt_q": "Czy jesteś pewien, że chcesz odszyfrować schowek? To pozwoli na dostęp do twoich portfeli bez hasła.",
|
||||
"encrypt_enc_and_pass": "Szyfrowany i chroniony hasłem",
|
||||
|
@ -403,7 +403,6 @@
|
|||
"add_wallet_name": "Nazwa",
|
||||
"add_wallet_type": "Typ",
|
||||
"add_wallet_seed_length": "Długość frazy seed",
|
||||
"add_wallet_seed_length_message": "Wybierz długość frazy seed, której chcesz użyć dla tego portfela.",
|
||||
"add_wallet_seed_length_12": "12 słów",
|
||||
"add_wallet_seed_length_24": "24 słowa",
|
||||
"clipboard_bitcoin": "Masz w schowku adres Bitcoin. Czy chcesz go użyć do transakcji?",
|
||||
|
@ -423,7 +422,6 @@
|
|||
"details_export_history": "Eksportuj historię do pliku CSV",
|
||||
"details_master_fingerprint": "Główny odcisk palca",
|
||||
"details_multisig_type": "multisig",
|
||||
"details_no_cancel": "Nie, anuluj",
|
||||
"details_show_xpub": "Pokaż XPUB portfela",
|
||||
"details_show_addresses": "Pokaż adresy",
|
||||
"details_title": "Portfel",
|
||||
|
@ -494,7 +492,9 @@
|
|||
"identity_pubkey": "Klucz publiczny tożsamości",
|
||||
"xpub_title": "XPUB portfela",
|
||||
"manage_wallets_search_placeholder": "Szukaj portfeli, notatek",
|
||||
"more_info": "Więcej informacji"
|
||||
"more_info": "Więcej informacji",
|
||||
"details_delete_wallet_error_message": "Nie udało się potwierdzić usunięcia tego portfela z powiadomień – możliwe, że przyczyną jest problem z siecią lub słabe połączenie. Jeśli kontynuujesz, możesz nadal otrzymywać powiadomienia o transakcjach związanych z tym portfelem, nawet po jego usunięciu.",
|
||||
"details_delete_anyway": "Usuń mimo to"
|
||||
},
|
||||
"total_balance_view": {
|
||||
"display_in_bitcoin": "Wyświetlaj w Bitcoinie",
|
||||
|
@ -509,6 +509,10 @@
|
|||
"default_label": "Skarbiec wielopodpisowy",
|
||||
"multisig_vault_explain": "Najlepsze bezpieczeństwo dla dużych kwot",
|
||||
"provide_signature": "Podaj podpis",
|
||||
"provide_signature_details": "Użyj urządzenia i portfela, w którym znajduje się klucz, aby podpisać tę transakcję.",
|
||||
"provide_signature_details_bluewallet": "W BlueWallet przejdź do menu ekranu wysyłania i wybierz ",
|
||||
"provide_signature_next_steps": "Skanuj lub importuj podpisaną transakcję",
|
||||
"provide_signature_next_steps_details": "Gdy Twój portfel pomyślnie podpisze transakcję, zeskanuj podany kod QR lub zaimportuj dołączony plik, a następnie zweryfikuj wszystkie szczegóły przed wysłaniem jej.",
|
||||
"vault_key": "Klucz Skarbca {number}",
|
||||
"required_keys_out_of_total": "Wymagane klucze spośród wszystkich",
|
||||
"fee": "Opłata: {number}",
|
||||
|
|
|
@ -10,12 +10,10 @@
|
|||
"never": "Nunca",
|
||||
"of": "{number} de {total}",
|
||||
"ok": "OK",
|
||||
"customize": "Personalizar",
|
||||
"enter_url": "Insira URL",
|
||||
"storage_is_encrypted": "Os arquivos estão criptografados, uma senha é necessária para descriptografá-los.",
|
||||
"yes": "Sim",
|
||||
"no": "Não",
|
||||
"save": "Salvar",
|
||||
"seed": "Seed",
|
||||
"success": "Sucesso",
|
||||
"wallet_key": "Chave da Carteira",
|
||||
|
@ -28,6 +26,8 @@
|
|||
"enter_amount": "Insira o valor",
|
||||
"qr_custom_input_button": "Toque 10 vezes para inserir uma entrada personalizada",
|
||||
"unlock": "Desbloquear",
|
||||
"port": "Porta",
|
||||
"ssl_port": "Porta SSL",
|
||||
"suggested": "Sugerido"
|
||||
},
|
||||
"azteco": {
|
||||
|
@ -74,6 +74,7 @@
|
|||
"please_pay": "Por favor, pague",
|
||||
"preimage": "Imagem prévia",
|
||||
"sats": "sats.",
|
||||
"date_time": "Data e Horário",
|
||||
"wasnt_paid_and_expired": "Esta fatura não foi paga e expirou."
|
||||
},
|
||||
"plausibledeniability": {
|
||||
|
@ -248,13 +249,13 @@
|
|||
"set_lndhub_as_default": "Definir {url} como servidor LNDHub padrão?",
|
||||
"electrum_settings_server": "Servidor Electrum",
|
||||
"electrum_status": "Status",
|
||||
"electrum_clear_alert_title": "Limpar histórico?",
|
||||
"electrum_clear_alert_message": "Você deseja limpar o histórico de servidores Electrum?",
|
||||
"electrum_clear_alert_cancel": "Cancelar",
|
||||
"electrum_clear_alert_ok": "Ok",
|
||||
"electrum_reset": "Resetar para o padrão.",
|
||||
"electrum_preferred_server": "Servidor Preferencial",
|
||||
"electrum_preferred_server_description": "Insira o servidor que você quer que sua carteira use para todas as atividades de Bitcoin. Uma vez definido, sua carteira usará exclusivamente este servidor para verificar saldos, enviar transações e buscar dados de rede. Certifique-se de confiar neste servidor antes de defini-lo.",
|
||||
"electrum_unable_to_connect": "Não foi possível conectar com {server}.",
|
||||
"electrum_reset_to_default": "Tem certeza de que deseja redefinir suas configurações Electrum para o padrão?",
|
||||
"electrum_history": "Histórico",
|
||||
"electrum_reset_to_default": "Isso permitirá que o BlueWallet escolha aleatoriamente um servidor da lista de servidores.",
|
||||
"electrum_reset": "Resetar para o padrão.",
|
||||
"electrum_reset_to_default_and_clear_history": "Redefinir para o padrão e limpar histórico",
|
||||
"encrypt_decrypt": "Descriptografar armazenamento",
|
||||
"encrypt_decrypt_q": "Tem certeza de que deseja descriptografar seu armazenamento? Isso permitirá que suas carteiras sejam acessadas sem uma senha.",
|
||||
"encrypt_enc_and_pass": "Encriptado e protegido por senha",
|
||||
|
@ -262,9 +263,14 @@
|
|||
"encrypt_storage_explanation_description_line1": "A ativação da criptografia de armazenamento adiciona uma camada extra de proteção ao seu aplicativo, protegendo a forma como os dados são armazenados no dispositivo. Isso torna mais difícil para qualquer pessoa acessar suas informações sem a sua permissão.",
|
||||
"encrypt_storage_explanation_description_line2": "Porém, é importante entender que essa criptografia protege apenas o acesso às suas carteiras armazenadas nas chaves do dispositivo. Não coloca senha ou qualquer proteção extra especificamente nas carteiras.",
|
||||
"i_understand": "Eu entendo",
|
||||
"block_explorer": "Explorador de Blocos",
|
||||
"block_explorer_preferred": "Usar explorador de blocos preferido",
|
||||
"block_explorer_error_saving_custom": "Erro ao salvar o explorador de blocos favorito",
|
||||
"encrypt_title": "Segurança",
|
||||
"encrypt_tstorage": "Armazenamento",
|
||||
"encrypt_use": "Usar {type}",
|
||||
"set_as_preferred": "Definir como preferido",
|
||||
"set_as_preferred_electrum": "Definir {host}:{port} como servidor preferencial desabilitará a conexão com um servidor sugerido aleatoriamente.",
|
||||
"encrypted_feature_disabled": "Este recurso não pode ser usado com o armazenamento criptografado ativado.",
|
||||
"encrypt_use_expl": "{type} será usado para confirmar sua identidade antes de fazer uma transação, desbloquear, exportar ou deletar uma carteira. {type} não será usado para desbloquear armazenamento encriptado.",
|
||||
"biometrics_fail": "Se {type} não estiver habilitado ou falhar ao desbloquear, você pode usar o código de acesso do seu dispositivo como alternativa.",
|
||||
|
@ -284,6 +290,7 @@
|
|||
"network": "Rede",
|
||||
"network_broadcast": "Transmitir Transação",
|
||||
"network_electrum": "Servidor Electrum",
|
||||
"electrum_suggested_description": "Quando um servidor preferencial não for definido, um servidor sugerido será selecionado para uso aleatoriamente.",
|
||||
"not_a_valid_uri": "URI inválida",
|
||||
"notifications": "Notificações",
|
||||
"open_link_in_explorer": "Abrir link no navegador",
|
||||
|
@ -299,6 +306,7 @@
|
|||
"privacy_do_not_track": "Desativar analítica",
|
||||
"privacy_do_not_track_explanation": "Informações de confiabilidade e desempenho não serão enviadas para análise.",
|
||||
"rate": "Taxa",
|
||||
"push_notifications_explanation": "Ao habilitar as notificações, o token do seu dispositivo será enviado ao servidor, junto com endereços de carteira e IDs de transação para todas as carteiras e transações feitas após habilitar as notificações. O token do dispositivo é usado para enviar notificações, e as informações da carteira nos permitem notificá-lo sobre Bitcoins recebidos ou confirmações de transações.\n\nSomente informações após você habilitar as notificações são transmitidas — nada anterior é coletado..\n\nDesabilitar notificações removerá todas essas informações do servidor. Além disso, excluir uma carteira do aplicativo também removerá suas informações associadas do servidor.",
|
||||
"selfTest": "Autoteste",
|
||||
"save": "Salvar",
|
||||
"saved": "Salvo",
|
||||
|
@ -310,12 +318,16 @@
|
|||
},
|
||||
"notifications": {
|
||||
"would_you_like_to_receive_notifications": "Gostaria de receber notificações quando receber pagamentos?",
|
||||
"no_and_dont_ask": "Não, e não me perguntar novamente."
|
||||
"notifications_subtitle": "Pagamentos recebidos e confirmações de transação",
|
||||
"no_and_dont_ask": "Não, e não me perguntar novamente.",
|
||||
"permission_denied_message": "Você negou permissão para enviar notificações. Se você desejar de receber notificações, habilite-as nas configurações do seu dispositivo."
|
||||
},
|
||||
"transactions": {
|
||||
"cancel_explain": "Nós vamos substituir esta transação por uma que envia os fundos para você com taxas mais altas. Isto, efetivamente, cancela a transação atual. Este ato é denominado RBF—Replace by Fee.",
|
||||
"cancel_no": "Esta transação não é substituível",
|
||||
"cancel_title": "Cancelar esta transação (RBF)",
|
||||
"transaction_loading_error": "Houve um problema ao carregar a transação. Tente novamente mais tarde.",
|
||||
"transaction_not_available": "Transação indisponível",
|
||||
"confirmations_lowercase": "{confirmations} confirmações",
|
||||
"copy_link": "Copiar link",
|
||||
"expand_note": "Expandir anotação",
|
||||
|
@ -334,6 +346,7 @@
|
|||
"details_outputs": "Saídas",
|
||||
"date": "Data",
|
||||
"details_received": "Recebido",
|
||||
"details_view_in_browser": "Ver num navegador",
|
||||
"details_title": "Transação",
|
||||
"incoming_transaction": "Transação de Entrada",
|
||||
"outgoing_transaction": "Transação de Saída",
|
||||
|
@ -371,6 +384,8 @@
|
|||
"add_bitcoin_explain": "Carteira Bitcoin simples e poderosa",
|
||||
"add_create": "Criar",
|
||||
"total_balance": "Saldo total",
|
||||
"add_entropy_reset_title": "Resetar Entropia",
|
||||
"add_entropy_reset_message": "Alterar o tipo de carteira irá reiniciar a entropia atual. Você quer prosseguir?",
|
||||
"add_entropy": "Entropia",
|
||||
"add_entropy_bytes": "{bytes} bytes de entropia",
|
||||
"add_entropy_generated": "{gen} bytes de entropia gerada",
|
||||
|
@ -387,11 +402,11 @@
|
|||
"add_wallet_name": "Nome",
|
||||
"add_wallet_type": "Tipo",
|
||||
"add_wallet_seed_length": "Comprimento da Seed Phrase",
|
||||
"add_wallet_seed_length_message": "Escolha o comprimento da Seed Phrase que deseja usar para esta carteira.",
|
||||
"add_wallet_seed_length_12": "12 Palavras",
|
||||
"add_wallet_seed_length_24": "24 Palavras",
|
||||
"clipboard_bitcoin": "Você tem um endereço Bitcoin na área de transferência. Deseja utilizá-lo para uma transação?",
|
||||
"clipboard_lightning": "Você tem uma fatura Lightning na área de transferência. Deseja utilizá-la para uma transação?",
|
||||
"clear_clipboard_on_import": "Limpar a área de transferência ao importar",
|
||||
"details_address": "Endereço",
|
||||
"details_advanced": "Avançado",
|
||||
"details_are_you_sure": "Tem certeza?",
|
||||
|
@ -406,7 +421,6 @@
|
|||
"details_export_history": "Exportar Histórico para CSV",
|
||||
"details_master_fingerprint": "Fingerprint Soberana",
|
||||
"details_multisig_type": "multisig",
|
||||
"details_no_cancel": "Não, cancelar",
|
||||
"details_show_xpub": "Exibir XPUB da Carteira",
|
||||
"details_show_addresses": "Mostrar endereços",
|
||||
"details_title": "Carteira",
|
||||
|
@ -433,6 +447,7 @@
|
|||
"import_discovery_subtitle": "Escolha uma carteira encontrada",
|
||||
"import_discovery_derivation": "Use um caminho de derivação personalizado",
|
||||
"import_discovery_no_wallets": "Nenhuma carteira foi encontrada.",
|
||||
"import_discovery_offline": "BlueWallet está atualmente no modo offline. Neste modo, ele não pode verificar a existência da carteira, então você precisará selecionar a carteira correta manualmente",
|
||||
"import_derivation_found": "Encontrado",
|
||||
"import_derivation_found_not": "Não encontrado",
|
||||
"import_derivation_loading": "Carregando...",
|
||||
|
@ -464,6 +479,14 @@
|
|||
"select_wallet": "Escolher carteira",
|
||||
"xpub_copiedToClipboard": "Copiado para a área de transferência",
|
||||
"pull_to_refresh": "Puxe para atualizar",
|
||||
"warning_do_not_disclose": "Nunca compartilhe a informação abaixo.",
|
||||
"scan_import": "Leia este código QR para importar sua carteira em outro aplicativo.",
|
||||
"write_down_header": "Crie um backup manual",
|
||||
"write_down": "Anote e armazene essas palavras com segurança. Use-as para restaurar sua carteira mais tarde.",
|
||||
"wallet_type_this": "O tipo desta carteira é {type}.",
|
||||
"share_number": "Compartilhar {number}",
|
||||
"copy_ln_url": "Copie e armazene com segurança esta URL para restaurar sua carteira em um momento posterior.",
|
||||
"copy_ln_public": "Copie e armazene essas informações com segurança para restaurar sua carteira mais tarde.",
|
||||
"add_ln_wallet_first": "Primeiro você deve adicionar uma carteira Lightning.",
|
||||
"identity_pubkey": "Identificar chave pública",
|
||||
"xpub_title": "XPUB da Carteira",
|
||||
|
@ -471,6 +494,10 @@
|
|||
"more_info": "Mais informações"
|
||||
},
|
||||
"total_balance_view": {
|
||||
"display_in_bitcoin": "Mostrar em Bitcoin",
|
||||
"hide": "Ocultar",
|
||||
"display_in_sats": "Mostrar em sats",
|
||||
"display_in_fiat": "Mostrar em {currency}",
|
||||
"title": "Saldo total",
|
||||
"explanation": "Veja o saldo total de todas as suas carteiras na tela pricinpal."
|
||||
},
|
||||
|
@ -502,6 +529,9 @@
|
|||
"wrapped_segwit_title": "Melhor compatibilidade",
|
||||
"legacy_title": "Antigo",
|
||||
"co_sign_transaction": "Assinar uma transação",
|
||||
"what_is_vault": "Um Vault é um",
|
||||
"what_is_vault_numberOfWallets": " {m}-de-{n} multisig ",
|
||||
"what_is_vault_wallet": "carteira.",
|
||||
"vault_advanced_customize": "Configurações do Cofre",
|
||||
"needs": "Necessita",
|
||||
"what_is_vault_description_number_of_vault_keys": "{m} chaves do cofre",
|
||||
|
@ -571,8 +601,13 @@
|
|||
"use_coin": "Usar moeda",
|
||||
"use_coins": "Usar moedas",
|
||||
"tip": "Permite que você veja, marque, congele ou selecione moedas para gerenciar melhor sua carteira.",
|
||||
"sort_asc": "Crescente",
|
||||
"sort_desc": "Decrescente",
|
||||
"sort_height": "Altura",
|
||||
"sort_value": "Valor",
|
||||
"sort_label": "Nome",
|
||||
"sort_status": "Status"
|
||||
"sort_status": "Status",
|
||||
"sort_by": "Ordenar por"
|
||||
},
|
||||
"units": {
|
||||
"BTC": "BTC",
|
||||
|
@ -617,6 +652,8 @@
|
|||
"bip47": {
|
||||
"payment_code": "Código de pagamento",
|
||||
"contacts": "Contatos",
|
||||
"bip47_explain": "Código reutilizável e compartilhável",
|
||||
"bip47_explain_subtitle": "BIP47",
|
||||
"purpose": "Código para compartilhar (BIP47)",
|
||||
"pay_this_contact": "Pague este contato",
|
||||
"rename_contact": "Renomear contato",
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
"storage_is_encrypted": "O armazenamento está encriptado. Uma password é necessária para desencriptar.",
|
||||
"yes": "Sim",
|
||||
"no": "Não",
|
||||
"save": "Guardar",
|
||||
"seed": "Seed",
|
||||
"success": "Sucesso",
|
||||
"wallet_key": "Chave da carteira",
|
||||
|
@ -176,12 +175,8 @@
|
|||
"set_electrum_server_as_default": "Definir {server} como servidor Electrum predefinido?",
|
||||
"electrum_settings_server": "Electrum server",
|
||||
"electrum_status": "Estado",
|
||||
"electrum_clear_alert_title": "Limpar histórico?",
|
||||
"electrum_clear_alert_message": "Você realmente deseja limpar o histórico de servidores electrum?",
|
||||
"electrum_clear_alert_cancel": "Cancelar",
|
||||
"electrum_clear_alert_ok": "Ok",
|
||||
"electrum_reset": "Resetar para o padrão",
|
||||
"electrum_unable_to_connect": "Não é possível ligar a {server}.",
|
||||
"electrum_reset": "Resetar para o padrão",
|
||||
"encrypt_decrypt": "Desencriptar armazenamento",
|
||||
"encrypt_decrypt_q": "Tem certeza de que deseja desencriptar o seu armazenamento? Isso permitirá que suas carteiras sejam acessadas sem uma senha.",
|
||||
"encrypt_enc_and_pass": "Encriptado e protegido por password",
|
||||
|
@ -285,7 +280,6 @@
|
|||
"details_export_history": "Exportar histórico para CSV",
|
||||
"details_master_fingerprint": "Master fingerprint",
|
||||
"details_multisig_type": "multisig",
|
||||
"details_no_cancel": "Não, cancelar",
|
||||
"details_show_xpub": "Mostrar XPUB da wallet",
|
||||
"details_show_addresses": "Mostrar endereços",
|
||||
"details_title": "Carteira",
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
"storage_is_encrypted": "Spațiul tău de stocare este criptat. E necesară parola pentru decriptare.",
|
||||
"yes": "Da",
|
||||
"no": "Nu",
|
||||
"save": "Salvează",
|
||||
"seed": "Seed",
|
||||
"success": "Succes",
|
||||
"wallet_key": "Cheia portofelului"
|
||||
|
@ -187,13 +186,8 @@
|
|||
"set_electrum_server_as_default": "Setează {server} ca server Electrum implicit?",
|
||||
"electrum_settings_server": "Server Electrum",
|
||||
"electrum_status": "Status",
|
||||
"electrum_clear_alert_title": "Șterge istoricul?",
|
||||
"electrum_clear_alert_message": "Vrei să ștergi istoricul serverelor Electrum?",
|
||||
"electrum_clear_alert_cancel": "Anulează",
|
||||
"electrum_clear_alert_ok": "Ok",
|
||||
"electrum_reset": "Resetează la setările implicite",
|
||||
"electrum_unable_to_connect": "Nu s-a putut conecta la {server}.",
|
||||
"electrum_reset_to_default": "Sigur vrei să resetezi setările Electrum?",
|
||||
"electrum_reset": "Resetează la setările implicite",
|
||||
"encrypt_decrypt": "Decriptează spațiul de stocare",
|
||||
"encrypt_decrypt_q": "Ești sigur că vrei să decriptezi spațiul de stocare? Acest lucru va permite portofelelor tale să fie accesate fără parolă.",
|
||||
"encrypt_enc_and_pass": "Encriptat și protejat de parolă",
|
||||
|
@ -304,7 +298,6 @@
|
|||
"details_export_backup": "Exportă/Backup",
|
||||
"details_master_fingerprint": "Amprenta principală",
|
||||
"details_multisig_type": "multisig",
|
||||
"details_no_cancel": "Nu, anulează",
|
||||
"details_show_xpub": "Afișează XPUB-ul portofelului",
|
||||
"details_show_addresses": "Arată adresele",
|
||||
"details_title": "Portofel",
|
||||
|
|
14
loc/ru.json
14
loc/ru.json
|
@ -14,7 +14,6 @@
|
|||
"storage_is_encrypted": "Ваше хранилище зашифровано. Введите пароль для расшифровки.",
|
||||
"yes": "Да",
|
||||
"no": "Нет",
|
||||
"save": "Сохранить",
|
||||
"seed": "Сид-фраза",
|
||||
"success": "Успешно",
|
||||
"wallet_key": "Ключ кошелька",
|
||||
|
@ -102,8 +101,7 @@
|
|||
"maxSatsFull": "Максимальная сумма: {max} сатоши или {currency}",
|
||||
"minSats": "Минимальная сумма: {min} сатоши",
|
||||
"minSatsFull": "Минимальная сумма: {min} сатоши или {currency}",
|
||||
"qrcode_for_the_address": "QR-код для адреса",
|
||||
"bip47_explanation": "Коды оплаты являются универсальными адресами, которые позволяют не раскрывать адреса вашего кошелька. Не все сервисы поддерживают их."
|
||||
"qrcode_for_the_address": "QR-код для адреса"
|
||||
},
|
||||
"send": {
|
||||
"provided_address_is_invoice": "Похоже, этот адрес предназначен для Лайтнинг-инвойса. Пожалуйста, перейдите в свой кошелёк Лайтнинг, чтобы оплатить этот счет.",
|
||||
|
@ -187,7 +185,6 @@
|
|||
"outdated_rate": "Курс был обновлён: {date}",
|
||||
"psbt_tx_open": "Открыть подписанную транзакцию",
|
||||
"psbt_tx_scan": "Сканировать подписанную транзакцию",
|
||||
"qr_error_no_qrcode": "Нам не удалось найти QR-код на выбранном изображении. Убедитесь, что изображение содержит только QR-код и ничего лишнего, например текста или кнопок.",
|
||||
"reset_amount": "Сбросить сумму",
|
||||
"reset_amount_confirm": "Хотите сбросить сумму?",
|
||||
"success_done": "Готово",
|
||||
|
@ -244,13 +241,8 @@
|
|||
"set_lndhub_as_default": "Задать {url} как сервер LNDhub по умолчанию?",
|
||||
"electrum_settings_server": "Сервер Electrum",
|
||||
"electrum_status": "Статус",
|
||||
"electrum_clear_alert_title": "Очистить историю?",
|
||||
"electrum_clear_alert_message": "Удалить историю серверов Electrum?",
|
||||
"electrum_clear_alert_cancel": "Отмена",
|
||||
"electrum_clear_alert_ok": "Ок",
|
||||
"electrum_reset": "По умолчанию",
|
||||
"electrum_unable_to_connect": "Невозможно подключиться к {server}.",
|
||||
"electrum_reset_to_default": "Вы уверены, что хотите сбросить значение сервера Electrum?",
|
||||
"electrum_reset": "По умолчанию",
|
||||
"encrypt_decrypt": "Расшифровать хранилище",
|
||||
"encrypt_decrypt_q": "Вы уверены, что хотите расшифровать хранилище? Это позволит получить доступ к вашим кошелькам без пароля.",
|
||||
"encrypt_enc_and_pass": "Зашифровано и защищено паролем",
|
||||
|
@ -388,7 +380,6 @@
|
|||
"add_wallet_name": "Имя кошелька",
|
||||
"add_wallet_type": "Тип кошелька",
|
||||
"add_wallet_seed_length": "Длина сид-фразы",
|
||||
"add_wallet_seed_length_message": "Выберите длину сид-фразы, которую вы хотите использовать для этого кошелька.",
|
||||
"add_wallet_seed_length_12": "12 слов",
|
||||
"add_wallet_seed_length_24": "24 слова",
|
||||
"clipboard_bitcoin": "В буфере обмена есть Биткоин адрес. Использовать его для создания транзакции?",
|
||||
|
@ -407,7 +398,6 @@
|
|||
"details_export_history": "Экспортировать историю в CSV",
|
||||
"details_master_fingerprint": "Master Fingerprint",
|
||||
"details_multisig_type": "мультисиг",
|
||||
"details_no_cancel": "Нет, отмена",
|
||||
"details_show_xpub": "Показать XPUB",
|
||||
"details_show_addresses": "Показать адреса",
|
||||
"details_title": "Информация",
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
"storage_is_encrypted": "ඔබේ ගබඩා කිරීම සංකේතනය කර ඇත. එය විකේතනය කිරීමට මුරපදය අවශ්යයි.",
|
||||
"yes": "ඔව්",
|
||||
"no": "නැත",
|
||||
"save": "සුරකින්න",
|
||||
"seed": "බීජ",
|
||||
"success": "සාර්ථකයි",
|
||||
"wallet_key": "පසුබිම් යතුර"
|
||||
|
@ -194,13 +193,8 @@
|
|||
"set_electrum_server_as_default": "පෙරනිමි ඉලෙක්ට්රෝම් සේවාදායකය ලෙස {server} සකසන්නද?",
|
||||
"electrum_settings_server": "ඉලෙක්ට්රම් සේවාදායකය",
|
||||
"electrum_status": "තත්ත්වය",
|
||||
"electrum_clear_alert_title": "ඉතිහාසය හිස් කරන්නද?",
|
||||
"electrum_clear_alert_message": "ඔබට ඉලෙක්ට්රම් සේවාදායක ඉතිහාසය හිස් කිරීමට අවශ්යද?",
|
||||
"electrum_clear_alert_cancel": "අවලංගු කරන්න",
|
||||
"electrum_clear_alert_ok": "හරි",
|
||||
"electrum_reset": "පෙරනිමිය වෙත නැවත සකසන්න",
|
||||
"electrum_unable_to_connect": "{server} වෙත සම්බන්ධ කිරීමට නොහැකිය.",
|
||||
"electrum_reset_to_default": "ඔබේ ඉලෙක්ට්රම් සැකසුම් පෙරනිමි ලෙස නැවත සැකසීමට ඔබට අවශ්ය බව ඔබට විශ්වාසද?",
|
||||
"electrum_reset": "පෙරනිමිය වෙත නැවත සකසන්න",
|
||||
"encrypt_decrypt": "විකේතන ගබඩාව",
|
||||
"encrypt_decrypt_q": "ඔබට ඔබේ ගබඩාව විකේතනය කිරීමට අවශ්ය බව විශ්වාසද? මුරපදයක් නොමැතිව ඔබේ මුදල් පසුම්බියට ප්රවේශ වීමට මෙය ඉඩ සලසයි.",
|
||||
"encrypt_enc_and_pass": "සංකේතනය කර මුරපදය ආරක්ෂා කර ඇත",
|
||||
|
@ -317,7 +311,6 @@
|
|||
"details_export_backup": "අපනයනය/උපස්ථ කිරීම",
|
||||
"details_master_fingerprint": "ප්රධාන ඇඟිලි සලකුණ",
|
||||
"details_multisig_type": "බහු සිග්",
|
||||
"details_no_cancel": "නැහැ, අවලංගු කරන්න",
|
||||
"details_show_xpub": "පසුම්බිය XPUB පෙන්වන්න",
|
||||
"details_show_addresses": "ලිපිනයන් පෙන්වන්න",
|
||||
"details_title": "පසුම්බිය",
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
"storage_is_encrypted": "Vaše úložisko je zašifrované. Zadajte heslo k odomknutiu.",
|
||||
"yes": "Áno",
|
||||
"no": "Nie",
|
||||
"save": "Uložiť",
|
||||
"seed": "Semienko",
|
||||
"success": "Správne",
|
||||
"wallet_key": "Peňaženkový kľúč"
|
||||
|
@ -123,7 +122,6 @@
|
|||
"electrum_saved": "Vaše zmeny boli úspešne uložené. Aby sa zmeny prejavili, môže byť potrebný reštart.",
|
||||
"electrum_settings_server": "Electrum server",
|
||||
"electrum_status": "Stav",
|
||||
"electrum_clear_alert_cancel": "Zrušiť",
|
||||
"encrypt_decrypt": "Dešifrovať úložisko",
|
||||
"encrypt_decrypt_q": "Naozaj chcete dešifrovať úložisko? Peňaženky budú prístupné bez hesla.",
|
||||
"encrypt_enc_and_pass": "Zašifrované a chránené heslom",
|
||||
|
@ -187,7 +185,6 @@
|
|||
"details_delete_wallet": "Zmazať peňaženku",
|
||||
"details_export_backup": "Exportovať / zálohovať",
|
||||
"details_master_fingerprint": "Hlavný odtlačok prsta",
|
||||
"details_no_cancel": "Nie, zrušiť",
|
||||
"details_show_xpub": "Ukázať XPUB",
|
||||
"details_title": "Peňaženka",
|
||||
"wallets": "peňaženky",
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
"storage_is_encrypted": "Shramba je šifrirana. Za dešifriranje je potrebno geslo.",
|
||||
"yes": "Da",
|
||||
"no": "Ne",
|
||||
"save": "Shrani",
|
||||
"seed": "Seme",
|
||||
"success": "Uspešno",
|
||||
"wallet_key": "Ključ denarnice"
|
||||
|
@ -201,13 +200,8 @@
|
|||
"set_electrum_server_as_default": "Želite nastaviti {server} kot privzeti electrum strežnik?",
|
||||
"electrum_settings_server": "Electrum Strežnik",
|
||||
"electrum_status": "Stanje",
|
||||
"electrum_clear_alert_title": "Počisti zgodovino?",
|
||||
"electrum_clear_alert_message": "Ali želite počistiti zgodovino electrum strežnikov?",
|
||||
"electrum_clear_alert_cancel": "Prekliči",
|
||||
"electrum_clear_alert_ok": "Ok",
|
||||
"electrum_reset": "Ponastavi na privzeto",
|
||||
"electrum_unable_to_connect": "Povezave z {server} ni mogoče vzpostaviti.",
|
||||
"electrum_reset_to_default": "Ali ste prepričani, da želite ponastaviti nastavitve Electrum strežnika na privzeto?",
|
||||
"electrum_reset": "Ponastavi na privzeto",
|
||||
"encrypt_decrypt": "Dešifriraj Shrambo",
|
||||
"encrypt_decrypt_q": "Ali ste prepričani, da želite dešifrirati shrambo? To bo omogočilo dostop do vaših denarnic brez gesla.",
|
||||
"encrypt_enc_and_pass": "Šifrirano in zaščiteno z geslom",
|
||||
|
@ -326,7 +320,6 @@
|
|||
"details_export_history": "Izvozi zgodovino v CSV",
|
||||
"details_master_fingerprint": "Glavni prstni odtis (fingerprint)",
|
||||
"details_multisig_type": "multisig",
|
||||
"details_no_cancel": "Ne, prekliči",
|
||||
"details_show_xpub": "Prikaži XPUB denarnice",
|
||||
"details_show_addresses": "Prikaži naslove",
|
||||
"details_title": "Denarnica",
|
||||
|
|
|
@ -10,12 +10,10 @@
|
|||
"never": "Kurrë",
|
||||
"of": "{numri} nga {totali}",
|
||||
"ok": "OK",
|
||||
"customize": "Personalizo",
|
||||
"enter_url": "Fusni URL",
|
||||
"storage_is_encrypted": "Memoria është e kriptuar. Duhet të fusni fjalkalimin për të pasur vizibilitet.",
|
||||
"yes": "Po",
|
||||
"no": "Jo",
|
||||
"save": "Ruaj",
|
||||
"seed": "Fara",
|
||||
"success": "Sukses",
|
||||
"wallet_key": "Fjalkalimi i portofolit",
|
||||
|
@ -195,9 +193,6 @@
|
|||
"electrum_settings_server": "Serveri Elektrum",
|
||||
"electrum_status": "Gjëndja",
|
||||
"electrum_preferred_server": "Serveri i Preferuar",
|
||||
"electrum_clear_alert_title": "Fshi Historine",
|
||||
"electrum_clear_alert_message": "Deshironi te fshini historin e serverit Elektrum?",
|
||||
"electrum_clear_alert_cancel": "Anullo",
|
||||
"encrypt_tstorage": "Depozita",
|
||||
"general": "Gjenerale",
|
||||
"language": "Gjuha",
|
||||
|
@ -238,7 +233,6 @@
|
|||
"add_wallet_seed_length_12": "12 fjalet",
|
||||
"add_wallet_seed_length_24": "24 fjalet",
|
||||
"details_address": "Adresa",
|
||||
"details_no_cancel": "Jo, anullo",
|
||||
"details_show_addresses": "Trego adresen",
|
||||
"details_title": "Portofol",
|
||||
"wallets": "Portofola",
|
||||
|
|
|
@ -8,8 +8,5 @@
|
|||
"yes": "Da",
|
||||
"no": "Ne",
|
||||
"wallet_key": "Ključ novčanika"
|
||||
},
|
||||
"settings": {
|
||||
"electrum_clear_alert_cancel": "Poništi"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
"storage_is_encrypted": "Lagringen är krypterad. Lösenords krävs för att dekryptera",
|
||||
"yes": "Ja",
|
||||
"no": "Nej",
|
||||
"save": "Spara",
|
||||
"seed": "Seed",
|
||||
"success": "Framgång",
|
||||
"wallet_key": "Nyckel till plånbok",
|
||||
|
@ -207,13 +206,8 @@
|
|||
"set_electrum_server_as_default": "Vill du ställa in {server} som standard Electrum-server?",
|
||||
"electrum_settings_server": "Electrum server",
|
||||
"electrum_status": "Status",
|
||||
"electrum_clear_alert_title": "Rensa historik?",
|
||||
"electrum_clear_alert_message": "Vill du rensa electrum-servrarnas historik?",
|
||||
"electrum_clear_alert_cancel": "Avbryt",
|
||||
"electrum_clear_alert_ok": "Ok",
|
||||
"electrum_reset": "Återställ till standard",
|
||||
"electrum_unable_to_connect": "Det gick inte att ansluta till {server}.",
|
||||
"electrum_reset_to_default": "Är du säker på att du vill återställa dina Electrum-inställningar till standardinställningarna?",
|
||||
"electrum_reset": "Återställ till standard",
|
||||
"encrypt_decrypt": "Dekryptera lagring",
|
||||
"encrypt_decrypt_q": "Är du säker på att du vill dekryptera din lagring? Detta gör att dina plånböcker kan nås utan lösenord.",
|
||||
"encrypt_enc_and_pass": "Krypterad och lösenordsskyddad",
|
||||
|
@ -334,7 +328,6 @@
|
|||
"details_export_history": "Exportera historik till CSV",
|
||||
"details_master_fingerprint": "Master Fingerprint",
|
||||
"details_multisig_type": "multisig",
|
||||
"details_no_cancel": "Nej, avbryt",
|
||||
"details_show_xpub": "Visa plånbokens XPUB",
|
||||
"details_show_addresses": "Visa adresser",
|
||||
"details_title": "Plånbok",
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
"storage_is_encrypted": "ที่เก็บข้อมูลของคุณถูกเข้ารหัส. ต้องการรหัสผ่านเพื่อถอดรหัส",
|
||||
"yes": "ถูกต้อง",
|
||||
"no": "ไม่",
|
||||
"save": "บันทึก",
|
||||
"seed": "ซีด",
|
||||
"success": "สำเร็จ",
|
||||
"wallet_key": "กุญแจกระเป๋าเงิน"
|
||||
|
@ -155,10 +154,6 @@
|
|||
"electrum_saved": "บันทึกสำเร็จ ท่านอาจจำเป็นต้องรีสตาร์ท",
|
||||
"electrum_settings_server": "เซิร์ฟเวอร์ Electrum",
|
||||
"electrum_status": "สถานะ",
|
||||
"electrum_clear_alert_title": "ท่านต้องการลบประวัติการรใช้งานหรือไม่?",
|
||||
"electrum_clear_alert_message": "ท่านต้องการลบประวัติการใช้งานของ electrum server หรือไม่?",
|
||||
"electrum_clear_alert_cancel": "ยกเลิก",
|
||||
"electrum_clear_alert_ok": "ตกลง",
|
||||
"electrum_reset": "รีเซ็ตเป็นค่าเริ่มต้น",
|
||||
"encrypt_decrypt": "เข้ารหัสที่เก็บข้อมูล",
|
||||
"encrypt_title": "ความปลอดภัย",
|
||||
|
@ -228,7 +223,6 @@
|
|||
"details_delete_wallet": "ลบกระเป๋าสตางค์",
|
||||
"details_export_backup": "ส่งออก / สำรอง",
|
||||
"details_master_fingerprint": "ลายนิ้วมือต้นแบบ",
|
||||
"details_no_cancel": "ไม่ใช่, ยกเลิก",
|
||||
"details_show_xpub": "แสดง XPUB ของกระเป๋าสตางค์",
|
||||
"details_title": "กระเป๋าสตางค์",
|
||||
"wallets": "กระเป๋าสตางค์",
|
||||
|
|
|
@ -10,12 +10,10 @@
|
|||
"never": "Asla",
|
||||
"of": "{number} / {total}",
|
||||
"ok": "Evet",
|
||||
"customize": "Özelleştir",
|
||||
"enter_url": "URL girin",
|
||||
"storage_is_encrypted": "Depolama alanınız şifrelenmiş. Şifrelemeyi çözmek için şifre gereklidir.",
|
||||
"yes": "Evet",
|
||||
"no": "Hayır",
|
||||
"save": "Kaydet",
|
||||
"seed": "Seed",
|
||||
"success": "Başarılı",
|
||||
"wallet_key": "Cüzdan anahtarı",
|
||||
|
@ -147,7 +145,6 @@
|
|||
"about_sm_twitter": "Bizi Twitter'da takip edin",
|
||||
"biometrics": "Biyometrikler",
|
||||
"currency": "Para Birimi",
|
||||
"electrum_clear_alert_cancel": "Vazgeç",
|
||||
"header": "ayarlar",
|
||||
"language": "Dil",
|
||||
"lightning_settings": "Lightning Ayarları",
|
||||
|
@ -182,7 +179,6 @@
|
|||
"details_are_you_sure": "Emin misiniz?",
|
||||
"details_delete": "Sil",
|
||||
"details_export_backup": "Dışa yükle / yedekle",
|
||||
"details_no_cancel": "Hayır, vazgeç",
|
||||
"details_show_xpub": "Cüzdan XPUB göster",
|
||||
"details_title": "Cüzdan",
|
||||
"wallets": "cüzdanlar",
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
"storage_is_encrypted": "Ваше сховище зашифроване. Введіть пароль для розшифровки",
|
||||
"yes": "Так",
|
||||
"no": "Ні",
|
||||
"save": "Зберегти",
|
||||
"seed": "Сід",
|
||||
"success": "Успіх",
|
||||
"wallet_key": "Ключ гаманця",
|
||||
|
@ -173,9 +172,6 @@
|
|||
"use_ssl": "Використовувати SSL",
|
||||
"electrum_settings_server": "Electrum Сервер",
|
||||
"electrum_status": "Статус",
|
||||
"electrum_clear_alert_title": "Очистити історію?",
|
||||
"electrum_clear_alert_cancel": "Відмінити",
|
||||
"electrum_clear_alert_ok": "Ок",
|
||||
"encrypt_title": "Безпека",
|
||||
"encrypt_tstorage": "Сховище",
|
||||
"general": "Загальні",
|
||||
|
@ -234,7 +230,6 @@
|
|||
"details_delete": "Видалити",
|
||||
"details_delete_wallet": "Видалити Гаманець",
|
||||
"details_export_backup": "Експорт / резервна копія",
|
||||
"details_no_cancel": "Ні, відмінити",
|
||||
"details_show_xpub": "Показати XPUB Гаманця",
|
||||
"details_show_addresses": "Показати адреси",
|
||||
"details_title": "Гаманець",
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
"storage_is_encrypted": "Lưu trữ của bạn được mã hoá. Mật khẩu được yêu cầu để giải mã",
|
||||
"yes": "Có",
|
||||
"no": "Không",
|
||||
"save": "Lưu",
|
||||
"seed": "Hạt giống",
|
||||
"success": "Thành công",
|
||||
"wallet_key": "Khóa ví",
|
||||
|
@ -202,13 +201,8 @@
|
|||
"set_electrum_server_as_default": "Đặt làm {server} máy chủ Electrum mặc định không?",
|
||||
"electrum_settings_server": "Máy chủ Electrum",
|
||||
"electrum_status": " Trạng thái",
|
||||
"electrum_clear_alert_title": "Xoá lịch sử không?",
|
||||
"electrum_clear_alert_message": "Bạn có muốn xoá lịch sử của máy chủ Electrum không?",
|
||||
"electrum_clear_alert_cancel": "Huỷ",
|
||||
"electrum_clear_alert_ok": "OK",
|
||||
"electrum_reset": "Đặt lại về mặc định ",
|
||||
"electrum_unable_to_connect": "Không thể kết nối với máy chủ {server}.",
|
||||
"electrum_reset_to_default": "Bạn có chắc muốn đặt lại cài đặt Electrum về mặc định không?",
|
||||
"electrum_reset": "Đặt lại về mặc định ",
|
||||
"encrypt_decrypt": "Giải mã lưu trữ",
|
||||
"encrypt_decrypt_q": "Bạn có chắc muốn giải mã lưu trữ không? Điều này sẽ làm cho ví của bạn có thể truy cập được mà không cần mật khẩu.",
|
||||
"encrypt_enc_and_pass": "Được mã hóa và bảo vệ bằng mật khẩu",
|
||||
|
@ -328,7 +322,6 @@
|
|||
"details_export_history": "Xuất lịch sử sang CSV ",
|
||||
"details_master_fingerprint": "Vân tay chủ",
|
||||
"details_multisig_type": "Multisig",
|
||||
"details_no_cancel": "Không, huỷ",
|
||||
"details_show_xpub": "Hiển thị XPUB của ví",
|
||||
"details_show_addresses": "Hiển thị các địa chỉ",
|
||||
"details_title": "Ví",
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
"storage_is_encrypted": "Jou geheue spasie is nou ge-enkripteer. ‘n Wagwoord word benodig om toegang te verkry. ",
|
||||
"yes": "Ja",
|
||||
"no": "Nee",
|
||||
"save": "Berg",
|
||||
"seed": "Saad",
|
||||
"success": "Sukses",
|
||||
"wallet_key": "Beursie sleutel",
|
||||
|
@ -138,7 +137,6 @@
|
|||
"settings": {
|
||||
"about": "info",
|
||||
"currency": "Geldeenheid",
|
||||
"electrum_clear_alert_cancel": "Kanselleer",
|
||||
"header": "instellings",
|
||||
"language": "Taal",
|
||||
"lightning_settings": "Lightning Instellings",
|
||||
|
@ -168,7 +166,6 @@
|
|||
"details_are_you_sure": "Is jy seker?",
|
||||
"details_delete": "Vernietig",
|
||||
"details_export_backup": "voer uit / kopieer",
|
||||
"details_no_cancel": "Nee, kanseleerl",
|
||||
"details_show_xpub": "Wys beursie XPUB",
|
||||
"details_title": "Beursiet",
|
||||
"wallets": "beursies",
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
"storage_is_encrypted": "Indawo yakho yokugcina inoguqulelo oluntsonkothileyo. Igama lokugqitha liyafuneka ukuyisusa ngokuntsonkotha.",
|
||||
"yes": "Ewe",
|
||||
"no": "Hayi",
|
||||
"save": "Gcina",
|
||||
"seed": "Imbewu",
|
||||
"success": "Imphumelelo",
|
||||
"wallet_key": "Isitshixo sesipaji",
|
||||
|
@ -79,7 +78,6 @@
|
|||
"settings": {
|
||||
"about": "Malunga",
|
||||
"currency": "Lwemali",
|
||||
"electrum_clear_alert_cancel": "Rhoxisa",
|
||||
"header": "Izicwangciso",
|
||||
"language": "Ulwimi",
|
||||
"lightning_settings": "Izixhobo zokukhanyisa",
|
||||
|
@ -109,7 +107,6 @@
|
|||
"details_are_you_sure": "Ingaba uqinisekile?",
|
||||
"details_delete": "Cima",
|
||||
"details_export_backup": "Ukuthumela ngaphandle / yokugcina",
|
||||
"details_no_cancel": "Hayi, rhoxisa",
|
||||
"details_show_xpub": "Bonise ingxowa XPUB",
|
||||
"details_title": "Ingxowa",
|
||||
"wallets": "Ingxowa",
|
||||
|
|
|
@ -10,12 +10,10 @@
|
|||
"never": "永不",
|
||||
"of": "{total}其中之{number}",
|
||||
"ok": "好的",
|
||||
"customize": "自定义",
|
||||
"enter_url": "输入网址",
|
||||
"storage_is_encrypted": "你的储存资料已经被加密, 请输入密码解密。",
|
||||
"yes": "是",
|
||||
"no": "不",
|
||||
"save": "保存",
|
||||
"seed": "种子",
|
||||
"success": "成功",
|
||||
"wallet_key": "钱包密钥",
|
||||
|
@ -28,6 +26,8 @@
|
|||
"enter_amount": "输入金额",
|
||||
"qr_custom_input_button": "按 10 次以输入自定义的输入",
|
||||
"unlock": "解锁",
|
||||
"port": "端口",
|
||||
"ssl_port": "SSL 端口",
|
||||
"suggested": "建议"
|
||||
},
|
||||
"azteco": {
|
||||
|
@ -74,6 +74,7 @@
|
|||
"please_pay": "请支付",
|
||||
"preimage": "预像",
|
||||
"sats": "聪",
|
||||
"date_time": "日期与时间",
|
||||
"wasnt_paid_and_expired": "此账单尚未支付,已过期。"
|
||||
},
|
||||
"plausibledeniability": {
|
||||
|
@ -106,7 +107,7 @@
|
|||
"minSats": "最小金额为 {min} 聪",
|
||||
"minSatsFull": "最小金额为 {min} 聪,或 {currency}",
|
||||
"qrcode_for_the_address": "该地址的二维码",
|
||||
"bip47_explanation": "付款代码是一个通用地址,避免披露您的钱包地址。并非所有服务都会支持他们。"
|
||||
"bip47_explanation": "支付代码是一种通用的地址,可以避免泄露您的钱包地址。并非所有服务都支持它们。"
|
||||
},
|
||||
"send": {
|
||||
"provided_address_is_invoice": "该地址似乎是闪电网络发票的地址。请访问您的 “闪电网络钱包” 为该发票付款。",
|
||||
|
@ -190,7 +191,7 @@
|
|||
"outdated_rate": "最后一次更新的费率在:{date}",
|
||||
"psbt_tx_open": "打开已签署的交易",
|
||||
"psbt_tx_scan": "扫描已签署的交易",
|
||||
"qr_error_no_qrcode": "我们无法在所选图像中找到二维码。请确保图像只包含二维码,而不包含文本或按钮等其他内容。",
|
||||
"qr_error_no_qrcode": "我们无法在所选图像中找到有效的二维码。请确保图像只包含二维码,而不包含文本或按钮等其他内容。",
|
||||
"reset_amount": "重置金额",
|
||||
"reset_amount_confirm": "您想重置金额吗?",
|
||||
"success_done": "完成",
|
||||
|
@ -250,15 +251,11 @@
|
|||
"electrum_status": "状态",
|
||||
"electrum_preferred_server": "首选的服务器",
|
||||
"electrum_preferred_server_description": "输入您希望钱包用于所有比特币活动的服务器。设置后,您的钱包将只使用该服务器检查余额、发送交易和获取网络数据。在设置之前,请确保您信任该服务器。",
|
||||
"electrum_clear_alert_title": "清除历史记录?",
|
||||
"electrum_clear_alert_message": "您是否要清除electrum 服务器的历史记录?",
|
||||
"electrum_clear_alert_cancel": "取消",
|
||||
"electrum_clear_alert_ok": "好的",
|
||||
"electrum_reset": "重置为默认",
|
||||
"electrum_unable_to_connect": "无法连接至 {server}。",
|
||||
"electrum_history": "历史",
|
||||
"electrum_reset_to_default": "您确定要重置您的Electrum设置为默认值吗?",
|
||||
"electrum_clear": "清除历史",
|
||||
"electrum_reset_to_default": "这会让 BlueWallet 从服务器列表中随机选择一个服务器。",
|
||||
"electrum_reset": "重置为默认",
|
||||
"electrum_reset_to_default_and_clear_history": "重置为默认值并清除历史记录",
|
||||
"encrypt_decrypt": "解密存储",
|
||||
"encrypt_decrypt_q": "您确定要解密存储吗? 这样一来,无需密码即可访问您的钱包。",
|
||||
"encrypt_enc_and_pass": "加密和密码保护的",
|
||||
|
@ -272,6 +269,8 @@
|
|||
"encrypt_title": "安全",
|
||||
"encrypt_tstorage": "存储",
|
||||
"encrypt_use": "使用{type}",
|
||||
"set_as_preferred": "设为首选",
|
||||
"set_as_preferred_electrum": "将 {host}:{port} 设置为首选服务器将禁止随机连接到建议的服务器。",
|
||||
"encrypted_feature_disabled": "启用加密存储时不能使用此功能。",
|
||||
"encrypt_use_expl": "在进行交易、解锁、导出或删除钱包之前,{type} 将用于确认您的身份。{type} 不会用于解锁加密存储。",
|
||||
"biometrics_fail": "如果未启用 {type} 或无法解锁,可以使用设备密码作为替代。",
|
||||
|
@ -291,6 +290,7 @@
|
|||
"network": "网络",
|
||||
"network_broadcast": "广播交易",
|
||||
"network_electrum": "Electrum服务器",
|
||||
"electrum_suggested_description": "如果没有设置首选的服务器,将随机选择一个建议使用的服务器。",
|
||||
"not_a_valid_uri": "无效的 URI",
|
||||
"notifications": "通知事项",
|
||||
"open_link_in_explorer": "在资源管理器中打开链接",
|
||||
|
@ -402,7 +402,6 @@
|
|||
"add_wallet_name": "名称",
|
||||
"add_wallet_type": "类型",
|
||||
"add_wallet_seed_length": "助记词长度",
|
||||
"add_wallet_seed_length_message": "选择您希望用于此钱包的助记词长度。",
|
||||
"add_wallet_seed_length_12": "12 个单词",
|
||||
"add_wallet_seed_length_24": "24 个单词",
|
||||
"clipboard_bitcoin": "您的剪贴板上有一个比特币地址。您想使用它进行交易吗?",
|
||||
|
@ -422,7 +421,6 @@
|
|||
"details_export_history": "将历史记录导出为 CSV",
|
||||
"details_master_fingerprint": "主指纹",
|
||||
"details_multisig_type": "多重签名",
|
||||
"details_no_cancel": "不了,请取消",
|
||||
"details_show_xpub": "展示钱包公钥",
|
||||
"details_show_addresses": "展示地址",
|
||||
"details_title": "钱包",
|
||||
|
@ -654,6 +652,8 @@
|
|||
"bip47": {
|
||||
"payment_code": "支付代码",
|
||||
"contacts": "联系人",
|
||||
"bip47_explain": "可重复使用和共享的代码",
|
||||
"bip47_explain_subtitle": "BIP47",
|
||||
"purpose": "可重复使用和共享的代码 (BIP47)",
|
||||
"pay_this_contact": "支付该联系人",
|
||||
"rename_contact": "重命名联系人",
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
"storage_is_encrypted": "你的儲存資料已經被加密, 請輸入密碼解密。",
|
||||
"yes": "是",
|
||||
"no": "否",
|
||||
"save": "儲存",
|
||||
"seed": "種子",
|
||||
"success": "成功",
|
||||
"wallet_key": "錢包密鑰"
|
||||
|
@ -172,13 +171,8 @@
|
|||
"set_electrum_server_as_default": "將{server}設定為預設的Electrum伺服器?",
|
||||
"electrum_settings_server": "Electrum伺服器",
|
||||
"electrum_status": "狀態",
|
||||
"electrum_clear_alert_title": "清除歷史記錄?",
|
||||
"electrum_clear_alert_message": "您是否要清除electrum 伺服器的歷史記錄?",
|
||||
"electrum_clear_alert_cancel": "取消",
|
||||
"electrum_clear_alert_ok": "好的",
|
||||
"electrum_reset": "重設為預設值",
|
||||
"electrum_unable_to_connect": "無法連接至 {server}。",
|
||||
"electrum_reset_to_default": "您確定要重設您的Electrum設定為預設值嗎?",
|
||||
"electrum_reset": "重設為預設值",
|
||||
"encrypt_decrypt": "解密儲存",
|
||||
"encrypt_decrypt_q": "您確定要解密儲存嗎?這樣一來,無需密碼即可訪問您的錢包。",
|
||||
"encrypt_enc_and_pass": "加密和密碼保護的",
|
||||
|
@ -276,7 +270,6 @@
|
|||
"details_export_backup": "匯出備份",
|
||||
"details_master_fingerprint": "主指紋",
|
||||
"details_multisig_type": "多重簽名",
|
||||
"details_no_cancel": "不,取消",
|
||||
"details_show_xpub": "展示錢包公鑰",
|
||||
"details_show_addresses": "顯示地址",
|
||||
"details_title": "錢包",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Psbt } from 'bitcoinjs-lib';
|
||||
import { CreateTransactionTarget, CreateTransactionUtxo, TWallet } from '../class/wallets/types';
|
||||
import { CreateTransactionTarget, CreateTransactionUtxo } from '../class/wallets/types';
|
||||
import { BitcoinUnit, Chain } from '../models/bitcoinUnits';
|
||||
import { ScanQRCodeParamList } from './DetailViewStackParamList';
|
||||
|
||||
|
@ -49,7 +49,6 @@ export type SendDetailsStackParamList = {
|
|||
txhex?: string;
|
||||
};
|
||||
CreateTransaction: {
|
||||
wallet: TWallet;
|
||||
memo?: string;
|
||||
psbt?: Psbt;
|
||||
txhex?: string;
|
||||
|
|
167
package-lock.json
generated
167
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "bluewallet",
|
||||
"version": "7.1.0",
|
||||
"version": "7.1.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "bluewallet",
|
||||
"version": "7.1.0",
|
||||
"version": "7.1.1",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -24,8 +24,8 @@
|
|||
"@react-native-community/cli-platform-ios": "15.1.3",
|
||||
"@react-native-community/push-notification-ios": "1.11.0",
|
||||
"@react-native-menu/menu": "https://github.com/BlueWallet/menu.git#14bab79",
|
||||
"@react-native/gradle-plugin": "0.76.6",
|
||||
"@react-native/metro-config": "0.76.6",
|
||||
"@react-native/gradle-plugin": "0.76.7",
|
||||
"@react-native/metro-config": "0.76.7",
|
||||
"@react-navigation/drawer": "6.7.2",
|
||||
"@react-navigation/native": "6.1.18",
|
||||
"@react-navigation/native-stack": "6.11.0",
|
||||
|
@ -63,7 +63,7 @@
|
|||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
"react-localization": "github:BlueWallet/react-localization#ae7969a",
|
||||
"react-native": "0.76.6",
|
||||
"react-native": "0.76.7",
|
||||
"react-native-biometrics": "3.0.1",
|
||||
"react-native-blue-crypto": "github:BlueWallet/react-native-blue-crypto#3cb5442",
|
||||
"react-native-camera-kit": "14.1.0",
|
||||
|
@ -114,11 +114,11 @@
|
|||
"@babel/preset-env": "^7.26.0",
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@jest/reporters": "^27.5.1",
|
||||
"@react-native/babel-preset": "0.76.6",
|
||||
"@react-native/eslint-config": "^0.76.6",
|
||||
"@react-native/js-polyfills": "^0.76.6",
|
||||
"@react-native/metro-babel-transformer": "^0.76.6",
|
||||
"@react-native/typescript-config": "0.76.6",
|
||||
"@react-native/babel-preset": "0.76.7",
|
||||
"@react-native/eslint-config": "^0.76.7",
|
||||
"@react-native/js-polyfills": "^0.76.7",
|
||||
"@react-native/metro-babel-transformer": "^0.76.7",
|
||||
"@react-native/typescript-config": "0.76.7",
|
||||
"@types/bip38": "^3.1.2",
|
||||
"@types/bs58check": "^2.1.0",
|
||||
"@types/create-hash": "^1.2.2",
|
||||
|
@ -5724,30 +5724,30 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@react-native/assets-registry": {
|
||||
"version": "0.76.6",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.76.6.tgz",
|
||||
"integrity": "sha512-YI8HoReYiIwdFQs+k9Q9qpFTnsyYikZxgs/UVtVbhKixXDQF6F9LLvj2naOx4cfV+RGybNKxwmDl1vUok/dRFQ==",
|
||||
"version": "0.76.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.76.7.tgz",
|
||||
"integrity": "sha512-o79whsqL5fbPTUQO9w1FptRd4cw1TaeOrXtQSLQeDrMVAenw/wmsjyPK10VKtvqxa1KNMtWEyfgxcM8CVZVFmg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native/babel-plugin-codegen": {
|
||||
"version": "0.76.6",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.76.6.tgz",
|
||||
"integrity": "sha512-yFC9I/aDBOBz3ZMlqKn2NY/mDUtCksUNZ7AQmBiTAeVTUP0ujEjE0hTOx5Qd+kok7A7hwZEX87HdSgjiJZfr5g==",
|
||||
"version": "0.76.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.76.7.tgz",
|
||||
"integrity": "sha512-+8H4DXJREM4l/pwLF/wSVMRzVhzhGDix5jLezNrMD9J1U1AMfV2aSkWA1XuqR7pjPs/Vqf6TaPL7vJMZ4LU05Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-native/codegen": "0.76.6"
|
||||
"@react-native/codegen": "0.76.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native/babel-preset": {
|
||||
"version": "0.76.6",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.76.6.tgz",
|
||||
"integrity": "sha512-ojlVWY6S/VE/nb9hIRetPMTsW9ZmGb2R3dnToEXAtQQDz41eHMHXbkw/k2h0THp6qhas25ruNvn3N5n2o+lBzg==",
|
||||
"version": "0.76.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.76.7.tgz",
|
||||
"integrity": "sha512-/c5DYZ6y8tyg+g8tgXKndDT7mWnGmkZ9F+T3qNDfoE3Qh7ucrNeC2XWvU9h5pk8eRtj9l4SzF4aO1phzwoibyg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
|
@ -5791,7 +5791,7 @@
|
|||
"@babel/plugin-transform-typescript": "^7.25.2",
|
||||
"@babel/plugin-transform-unicode-regex": "^7.24.7",
|
||||
"@babel/template": "^7.25.0",
|
||||
"@react-native/babel-plugin-codegen": "0.76.6",
|
||||
"@react-native/babel-plugin-codegen": "0.76.7",
|
||||
"babel-plugin-syntax-hermes-parser": "^0.25.1",
|
||||
"babel-plugin-transform-flow-enums": "^0.0.2",
|
||||
"react-refresh": "^0.14.0"
|
||||
|
@ -5828,9 +5828,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@react-native/codegen": {
|
||||
"version": "0.76.6",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.76.6.tgz",
|
||||
"integrity": "sha512-BABb3e5G/+hyQYEYi0AODWh2km2d8ERoASZr6Hv90pVXdUHRYR+yxCatX7vSd9rnDUYndqRTzD0hZWAucPNAKg==",
|
||||
"version": "0.76.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.76.7.tgz",
|
||||
"integrity": "sha512-FAn585Ll65YvkSrKDyAcsdjHhhAGiMlSTUpHh0x7J5ntudUns+voYms0xMP+pEPt0XuLdjhD7zLIIlAWP407+g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.25.3",
|
||||
|
@ -5850,13 +5850,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@react-native/community-cli-plugin": {
|
||||
"version": "0.76.6",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.76.6.tgz",
|
||||
"integrity": "sha512-nETlc/+U5cESVluzzgN0OcVfcoMijGBaDWzOaJhoYUodcuqnqtu75XsSEc7yzlYjwNQG+vF83mu9CQGezruNMA==",
|
||||
"version": "0.76.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.76.7.tgz",
|
||||
"integrity": "sha512-lrcsY2WPLCEWU1pjdNV9+Ccj8vCEwCCURZiPa5aqi7lKB4C++1hPrxA8/CWWnTNcQp76DsBKGYqTFj7Ud4aupw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-native/dev-middleware": "0.76.6",
|
||||
"@react-native/metro-babel-transformer": "0.76.6",
|
||||
"@react-native/dev-middleware": "0.76.7",
|
||||
"@react-native/metro-babel-transformer": "0.76.7",
|
||||
"chalk": "^4.0.0",
|
||||
"execa": "^5.1.1",
|
||||
"invariant": "^2.2.4",
|
||||
|
@ -5938,9 +5938,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@react-native/community-cli-plugin/node_modules/semver": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
|
||||
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
|
@ -5962,26 +5962,27 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@react-native/debugger-frontend": {
|
||||
"version": "0.76.6",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.76.6.tgz",
|
||||
"integrity": "sha512-kP97xMQjiANi5/lmf8MakS7d8FTJl+BqYHQMqyvNiY+eeWyKnhqW2GL2v3eEUBAuyPBgJGivuuO4RvjZujduJg==",
|
||||
"version": "0.76.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.76.7.tgz",
|
||||
"integrity": "sha512-89ZtZXt7ZxE94i7T94qzZMhp4Gfcpr/QVpGqEaejAxZD+gvDCH21cYSF+/Rz2ttBazm0rk5MZ0mFqb0Iqp1jmw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native/dev-middleware": {
|
||||
"version": "0.76.6",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.76.6.tgz",
|
||||
"integrity": "sha512-1bAyd2/X48Nzb45s5l2omM75vy764odx/UnDs4sJfFCuK+cupU4nRPgl0XWIqgdM/2+fbQ3E4QsVS/WIKTFxvQ==",
|
||||
"version": "0.76.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.76.7.tgz",
|
||||
"integrity": "sha512-Jsw8g9DyLPnR9yHEGuT09yHZ7M88/GL9CtU9WmyChlBwdXSeE3AmRqLegsV3XcgULQ1fqdemokaOZ/MwLYkjdA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@isaacs/ttlcache": "^1.4.1",
|
||||
"@react-native/debugger-frontend": "0.76.6",
|
||||
"@react-native/debugger-frontend": "0.76.7",
|
||||
"chrome-launcher": "^0.15.2",
|
||||
"chromium-edge-launcher": "^0.2.0",
|
||||
"connect": "^3.6.5",
|
||||
"debug": "^2.2.0",
|
||||
"invariant": "^2.2.4",
|
||||
"nullthrows": "^1.1.1",
|
||||
"open": "^7.0.3",
|
||||
"selfsigned": "^2.4.1",
|
||||
|
@ -6036,15 +6037,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@react-native/eslint-config": {
|
||||
"version": "0.76.6",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/eslint-config/-/eslint-config-0.76.6.tgz",
|
||||
"integrity": "sha512-s7dsWapoB2oTgwhS8cjJmls9qsAOEbodWnFVeDkGytcjlOWDEYdDSqOhJXuwAqNmkZP1noXmcqRATo+gCnY20A==",
|
||||
"version": "0.76.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/eslint-config/-/eslint-config-0.76.7.tgz",
|
||||
"integrity": "sha512-pd5Vgcwph0gtBvPmQ6Np6fC7vfI5/4xGdS4YNT5VTtrIcK5mki9M2iWvvQ5K12C4KHHLCFXGmtghTo1VIKzHCw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
"@babel/eslint-parser": "^7.25.1",
|
||||
"@react-native/eslint-plugin": "0.76.6",
|
||||
"@react-native/eslint-plugin": "0.76.7",
|
||||
"@typescript-eslint/eslint-plugin": "^7.1.1",
|
||||
"@typescript-eslint/parser": "^7.1.1",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
|
@ -6234,9 +6235,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@react-native/eslint-plugin": {
|
||||
"version": "0.76.6",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/eslint-plugin/-/eslint-plugin-0.76.6.tgz",
|
||||
"integrity": "sha512-pAqN77lBXyfRcKN2D17AqWJLKkHNV/ZevPKKIGZ2LbLeM8POWsYcNnGtaO+glny8LbE+ZSBEXknMSIN+PrDk/A==",
|
||||
"version": "0.76.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/eslint-plugin/-/eslint-plugin-0.76.7.tgz",
|
||||
"integrity": "sha512-mT+XOZJwUVMF/wrnyQcuyhLKLuDQmeC48YD/FBhSqCq+Q7zItxe78gDnXgMmuoeZuEu66s2nV1dzBM3qKOP/Xw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
@ -6244,31 +6245,31 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@react-native/gradle-plugin": {
|
||||
"version": "0.76.6",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.76.6.tgz",
|
||||
"integrity": "sha512-sDzpf4eiynryoS6bpYCweGoxSmWgCSx9lzBoxIIW+S6siyGiTaffzZHWCm8mIn9UZsSPlEO37q62ggnR9Zu/OA==",
|
||||
"version": "0.76.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.76.7.tgz",
|
||||
"integrity": "sha512-gQI6RcrJbigU8xk7F960C5xQIgvbBj20TUvGecD+N2PHfbLpqR+92cj7hz3UcbrCONmTP40WHnbMMJ8P+kLsrA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native/js-polyfills": {
|
||||
"version": "0.76.6",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.76.6.tgz",
|
||||
"integrity": "sha512-cDD7FynxWYxHkErZzAJtzPGhJ13JdOgL+R0riTh0hCovOfIUz9ItffdLQv2nx48lnvMTQ+HZXMnGOZnsFCNzQw==",
|
||||
"version": "0.76.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.76.7.tgz",
|
||||
"integrity": "sha512-+iEikj6c6Zvrg1c3cYMeiPB+5nS8EaIC3jCtP6Muk3qc7c386IymEPM2xycIlfg04DPZvO3D4P2/vaO9/TCnUg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native/metro-babel-transformer": {
|
||||
"version": "0.76.6",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.76.6.tgz",
|
||||
"integrity": "sha512-xSBi9jPliThu5HRSJvluqUlDOLLEmf34zY/U7RDDjEbZqC0ufPcPS7c5XsSg0GDPiXc7lgjBVesPZsKFkoIBgA==",
|
||||
"version": "0.76.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.76.7.tgz",
|
||||
"integrity": "sha512-jDS1wR7q46xY5ah+jF714Mvss9l7+lmwW/tplahZgLKozkYDC8Td5o9TOCgKlv18acw9H1V7zv8ivuRSj8ICPg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
"@react-native/babel-preset": "0.76.6",
|
||||
"@react-native/babel-preset": "0.76.7",
|
||||
"hermes-parser": "0.23.1",
|
||||
"nullthrows": "^1.1.1"
|
||||
},
|
||||
|
@ -6280,13 +6281,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@react-native/metro-config": {
|
||||
"version": "0.76.6",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/metro-config/-/metro-config-0.76.6.tgz",
|
||||
"integrity": "sha512-R//+5BT/1hXv3ZjFjgF5uvR4xBpiHbw9Ci9AtCebPaAslQL8FXqAtwhn1Fjrl+ECo1Nhe25B/Lzl9WMWmI9X0w==",
|
||||
"version": "0.76.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/metro-config/-/metro-config-0.76.7.tgz",
|
||||
"integrity": "sha512-5kfw9wbL2FuLkZ/YjxJWgKuvN79hFuj/RtjlJmDntJDumeQH0GKMka2TVal2Cjv9SlV7Vixbt3yAWGLkXsQwTQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-native/js-polyfills": "0.76.6",
|
||||
"@react-native/metro-babel-transformer": "0.76.6",
|
||||
"@react-native/js-polyfills": "0.76.7",
|
||||
"@react-native/metro-babel-transformer": "0.76.7",
|
||||
"metro-config": "^0.81.0",
|
||||
"metro-runtime": "^0.81.0"
|
||||
},
|
||||
|
@ -6295,22 +6296,22 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@react-native/normalize-colors": {
|
||||
"version": "0.76.6",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.76.6.tgz",
|
||||
"integrity": "sha512-1n4udXH2Cla31iA/8eLRdhFHpYUYK1NKWCn4m1Sr9L4SarWKAYuRFliK1fcLvPPALCFoFlWvn8I0ekdUOHMzDQ==",
|
||||
"version": "0.76.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.76.7.tgz",
|
||||
"integrity": "sha512-ST1xxBuYVIXPdD81dR6+tzIgso7m3pa9+6rOBXTh5Xm7KEEFik7tnQX+GydXYMp3wr1gagJjragdXkPnxK6WNg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@react-native/typescript-config": {
|
||||
"version": "0.76.6",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/typescript-config/-/typescript-config-0.76.6.tgz",
|
||||
"integrity": "sha512-i1gh+wLaAwG6ZVe2Mkoa10RJznsfOC1crRh0JAobCHaTaA/ZmuFScBTRzRE60pO5Oz6HWM1qbQST+JM+5LiMuA==",
|
||||
"version": "0.76.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/typescript-config/-/typescript-config-0.76.7.tgz",
|
||||
"integrity": "sha512-grJJY2DjXTQ0CDReSAHxOmp4eYDnQ5coUT9ZMdXEovmeALRysq54HH3vvdsM8FRAgQx1ZhGzRIUEF0/HDrAgQw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@react-native/virtualized-lists": {
|
||||
"version": "0.76.6",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.76.6.tgz",
|
||||
"integrity": "sha512-0HUWVwJbRq1BWFOu11eOWGTSmK9nMHhoMPyoI27wyWcl/nqUx7HOxMbRVq0DsTCyATSMPeF+vZ6o1REapcNWKw==",
|
||||
"version": "0.76.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.76.7.tgz",
|
||||
"integrity": "sha512-pRUf1jUO8H9Ft04CaWv76t34QI9wY0sydoYlIwEtqXjjMJgmgDoOCAWBjArgn2mk8/rK+u/uicI67ZCYCp1pJw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"invariant": "^2.2.4",
|
||||
|
@ -12533,9 +12534,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/flow-parser": {
|
||||
"version": "0.259.1",
|
||||
"resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.259.1.tgz",
|
||||
"integrity": "sha512-xiXLmMH2Z7OmdE9Q+MjljUMr/rbemFqZIRxaeZieVScG4HzQrKKhNcCYZbWTGpoN7ZPi7z8ClQbeVPq6t5AszQ==",
|
||||
"version": "0.260.0",
|
||||
"resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.260.0.tgz",
|
||||
"integrity": "sha512-GvVx+yGdVK4U6Q5AcLCCqVjCyJTeYClOrMYlxhSG7fPNcsiYqtIOIXA+/SaTsvj1kB/xyiwZ5+iOGQXbwaE6Ew==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
|
@ -21540,19 +21541,19 @@
|
|||
}
|
||||
},
|
||||
"node_modules/react-native": {
|
||||
"version": "0.76.6",
|
||||
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.76.6.tgz",
|
||||
"integrity": "sha512-AsRi+ud6v6ADH7ZtSOY42kRB4nbM0KtSu450pGO4pDudl4AEK/AF96ai88snb2/VJJSGGa/49QyJVFXxz/qoFg==",
|
||||
"version": "0.76.7",
|
||||
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.76.7.tgz",
|
||||
"integrity": "sha512-GPJcQeO3qUi1MvuhsC2DC6tH8gJQ4uc4JWPORrdeuCGFWE3QLsN8/hiChTEvJREHLfQSV61YPI8gIOtAQ8c37g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jest/create-cache-key-function": "^29.6.3",
|
||||
"@react-native/assets-registry": "0.76.6",
|
||||
"@react-native/codegen": "0.76.6",
|
||||
"@react-native/community-cli-plugin": "0.76.6",
|
||||
"@react-native/gradle-plugin": "0.76.6",
|
||||
"@react-native/js-polyfills": "0.76.6",
|
||||
"@react-native/normalize-colors": "0.76.6",
|
||||
"@react-native/virtualized-lists": "0.76.6",
|
||||
"@react-native/assets-registry": "0.76.7",
|
||||
"@react-native/codegen": "0.76.7",
|
||||
"@react-native/community-cli-plugin": "0.76.7",
|
||||
"@react-native/gradle-plugin": "0.76.7",
|
||||
"@react-native/js-polyfills": "0.76.7",
|
||||
"@react-native/normalize-colors": "0.76.7",
|
||||
"@react-native/virtualized-lists": "0.76.7",
|
||||
"abort-controller": "^3.0.0",
|
||||
"anser": "^1.4.9",
|
||||
"ansi-regex": "^5.0.0",
|
||||
|
|
18
package.json
18
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "bluewallet",
|
||||
"version": "7.1.0",
|
||||
"version": "7.1.1",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -11,11 +11,11 @@
|
|||
"@babel/preset-env": "^7.26.0",
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@jest/reporters": "^27.5.1",
|
||||
"@react-native/babel-preset": "0.76.6",
|
||||
"@react-native/eslint-config": "^0.76.6",
|
||||
"@react-native/js-polyfills": "^0.76.6",
|
||||
"@react-native/metro-babel-transformer": "^0.76.6",
|
||||
"@react-native/typescript-config": "0.76.6",
|
||||
"@react-native/babel-preset": "0.76.7",
|
||||
"@react-native/eslint-config": "^0.76.7",
|
||||
"@react-native/js-polyfills": "^0.76.7",
|
||||
"@react-native/metro-babel-transformer": "^0.76.7",
|
||||
"@react-native/typescript-config": "0.76.7",
|
||||
"@types/bip38": "^3.1.2",
|
||||
"@types/bs58check": "^2.1.0",
|
||||
"@types/create-hash": "^1.2.2",
|
||||
|
@ -90,8 +90,8 @@
|
|||
"@react-native-clipboard/clipboard": "1.16.1",
|
||||
"@react-native-community/push-notification-ios": "1.11.0",
|
||||
"@react-native-menu/menu": "https://github.com/BlueWallet/menu.git#14bab79",
|
||||
"@react-native/gradle-plugin": "0.76.6",
|
||||
"@react-native/metro-config": "0.76.6",
|
||||
"@react-native/gradle-plugin": "0.76.7",
|
||||
"@react-native/metro-config": "0.76.7",
|
||||
"@react-navigation/drawer": "6.7.2",
|
||||
"@react-navigation/native": "6.1.18",
|
||||
"@react-navigation/native-stack": "6.11.0",
|
||||
|
@ -129,7 +129,7 @@
|
|||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
"react-localization": "github:BlueWallet/react-localization#ae7969a",
|
||||
"react-native": "0.76.6",
|
||||
"react-native": "0.76.7",
|
||||
"react-native-biometrics": "3.0.1",
|
||||
"react-native-blue-crypto": "github:BlueWallet/react-native-blue-crypto#3cb5442",
|
||||
"react-native-camera-kit": "14.1.0",
|
||||
|
|
|
@ -38,6 +38,7 @@ import { CommonToolTipActions } from '../../typings/CommonToolTipActions';
|
|||
import HeaderMenuButton from '../../components/HeaderMenuButton';
|
||||
import { useSettings } from '../../hooks/context/useSettings';
|
||||
import { majorTomToGroundControl, tryToObtainPermissions } from '../../blue_modules/notifications';
|
||||
import TipBox from '../../components/TipBox';
|
||||
|
||||
const segmentControlValues = [loc.wallets.details_address, loc.bip47.payment_code];
|
||||
|
||||
|
@ -88,9 +89,6 @@ const ReceiveDetails = () => {
|
|||
modalButton: {
|
||||
backgroundColor: colors.modalButton,
|
||||
},
|
||||
tip: {
|
||||
backgroundColor: colors.ballOutgoingExpired,
|
||||
},
|
||||
});
|
||||
|
||||
const setAddressBIP21Encoded = useCallback(
|
||||
|
@ -472,9 +470,7 @@ const ReceiveDetails = () => {
|
|||
{!qrValue && <Text>{loc.bip47.not_found}</Text>}
|
||||
{qrValue && (
|
||||
<>
|
||||
<View style={[styles.tip, stylesHook.tip]}>
|
||||
<Text style={{ color: colors.foregroundColor }}>{loc.receive.bip47_explanation}</Text>
|
||||
</View>
|
||||
<TipBox description={loc.receive.bip47_explanation} containerStyle={styles.tip} />
|
||||
<QRCodeComponent value={qrValue} />
|
||||
<CopyTextToClipboard text={qrValue} truncated={false} />
|
||||
</>
|
||||
|
|
|
@ -23,8 +23,8 @@ import loc from '../../loc';
|
|||
import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList';
|
||||
import { useSettings } from '../../hooks/context/useSettings';
|
||||
import { majorTomToGroundControl } from '../../blue_modules/notifications';
|
||||
import { navigate } from '../../NavigationService';
|
||||
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
|
||||
const BROADCAST_RESULT = Object.freeze({
|
||||
none: 'Input transaction hex',
|
||||
|
@ -34,6 +34,7 @@ const BROADCAST_RESULT = Object.freeze({
|
|||
});
|
||||
|
||||
type RouteProps = RouteProp<DetailViewStackParamList, 'Broadcast'>;
|
||||
type NavigationProps = NativeStackNavigationProp<DetailViewStackParamList, 'Broadcast'>;
|
||||
|
||||
const Broadcast: React.FC = () => {
|
||||
const { params } = useRoute<RouteProps>();
|
||||
|
@ -42,7 +43,7 @@ const Broadcast: React.FC = () => {
|
|||
const { colors } = useTheme();
|
||||
const [broadcastResult, setBroadcastResult] = useState<string>(BROADCAST_RESULT.none);
|
||||
const { selectedBlockExplorer } = useSettings();
|
||||
const { setParams } = useExtendedNavigation();
|
||||
const { setParams, navigate } = useExtendedNavigation<NavigationProps>();
|
||||
|
||||
const stylesHooks = StyleSheet.create({
|
||||
input: {
|
||||
|
@ -104,7 +105,9 @@ const Broadcast: React.FC = () => {
|
|||
};
|
||||
|
||||
const handleQRScan = () => {
|
||||
navigate('ScanQRCode');
|
||||
navigate('ScanQRCode', {
|
||||
showFileImportButton: true,
|
||||
});
|
||||
};
|
||||
|
||||
let status;
|
||||
|
|
|
@ -35,6 +35,7 @@ import loc, { formatBalance } from '../../loc';
|
|||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
import { SendDetailsStackParamList } from '../../navigation/SendDetailsStackParamList';
|
||||
import { CommonToolTipActions } from '../../typings/CommonToolTipActions';
|
||||
import TipBox from '../../components/TipBox';
|
||||
|
||||
type NavigationProps = NativeStackNavigationProp<SendDetailsStackParamList, 'CoinControl'>;
|
||||
type RouteProps = RouteProp<SendDetailsStackParamList, 'CoinControl'>;
|
||||
|
@ -344,6 +345,10 @@ const CoinControl: React.FC = () => {
|
|||
const stylesHook = StyleSheet.create({
|
||||
tip: {
|
||||
backgroundColor: colors.ballOutgoingExpired,
|
||||
borderRadius: 12,
|
||||
padding: 16,
|
||||
marginVertical: 24,
|
||||
marginHorizontal: 16,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -361,11 +366,7 @@ const CoinControl: React.FC = () => {
|
|||
text = loc.formatString(loc.cc.selected_summ, { value });
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[styles.tip, stylesHook.tip]}>
|
||||
<Text style={{ color: colors.foregroundColor }}>{text}</Text>
|
||||
</View>
|
||||
);
|
||||
return <TipBox description={text} containerStyle={stylesHook.tip} />;
|
||||
};
|
||||
|
||||
const handleChoose = (item: Utxo) => setOutput(item);
|
||||
|
@ -583,12 +584,6 @@ const styles = StyleSheet.create({
|
|||
alignItems: 'center',
|
||||
padding: 24,
|
||||
},
|
||||
tip: {
|
||||
marginHorizontal: 16,
|
||||
borderRadius: 12,
|
||||
padding: 16,
|
||||
marginVertical: 24,
|
||||
},
|
||||
sendIcon: {
|
||||
transform: [{ rotate: '225deg' }],
|
||||
},
|
||||
|
|
|
@ -131,7 +131,6 @@ const Confirm: React.FC = () => {
|
|||
memo,
|
||||
tx,
|
||||
satoshiPerByte,
|
||||
wallet,
|
||||
feeSatoshi,
|
||||
});
|
||||
}}
|
||||
|
@ -149,7 +148,6 @@ const Confirm: React.FC = () => {
|
|||
memo,
|
||||
tx,
|
||||
satoshiPerByte,
|
||||
wallet,
|
||||
feeSatoshi,
|
||||
],
|
||||
);
|
||||
|
|
|
@ -25,7 +25,6 @@ const styles = StyleSheet.create({
|
|||
backgroundColor: '#000000',
|
||||
},
|
||||
openSettingsContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignContent: 'center',
|
||||
alignItems: 'center',
|
||||
|
|
|
@ -81,7 +81,6 @@ const SendDetails = () => {
|
|||
const selectedDataProcessor = useRef<ToolTipAction | undefined>();
|
||||
const setParams = navigation.setParams;
|
||||
const route = useRoute<RouteProps>();
|
||||
const name = route.name;
|
||||
const feeUnit = route.params?.feeUnit ?? BitcoinUnit.BTC;
|
||||
const amountUnit = route.params?.amountUnit ?? BitcoinUnit.BTC;
|
||||
const frozenBalance = route.params?.frozenBalance ?? 0;
|
||||
|
@ -400,66 +399,69 @@ const SendDetails = () => {
|
|||
* @param data {String} Can be address or `bitcoin:xxxxxxx` uri scheme, or invalid garbage
|
||||
*/
|
||||
|
||||
const processAddressData = (data: string | { data?: any }) => {
|
||||
assert(wallet, 'Internal error: wallet not set');
|
||||
if (typeof data !== 'string') {
|
||||
data = String(data.data);
|
||||
}
|
||||
const currentIndex = scrollIndex.current;
|
||||
setIsLoading(true);
|
||||
if (!data.replace) {
|
||||
// user probably scanned PSBT and got an object instead of string..?
|
||||
const processAddressData = useCallback(
|
||||
(data: string | { data?: any }) => {
|
||||
assert(wallet, 'Internal error: wallet not set');
|
||||
if (typeof data !== 'string') {
|
||||
data = String(data.data);
|
||||
}
|
||||
const currentIndex = scrollIndex.current;
|
||||
setIsLoading(true);
|
||||
if (!data.replace) {
|
||||
// user probably scanned PSBT and got an object instead of string..?
|
||||
setIsLoading(false);
|
||||
return presentAlert({ title: loc.errors.error, message: loc.send.details_address_field_is_not_valid });
|
||||
}
|
||||
|
||||
const cl = new ContactList();
|
||||
|
||||
const dataWithoutSchema = data.replace('bitcoin:', '').replace('BITCOIN:', '');
|
||||
if (wallet.isAddressValid(dataWithoutSchema) || cl.isPaymentCodeValid(dataWithoutSchema)) {
|
||||
setAddresses(addrs => {
|
||||
addrs[scrollIndex.current].address = dataWithoutSchema;
|
||||
return [...addrs];
|
||||
});
|
||||
setIsLoading(false);
|
||||
setTimeout(() => scrollView.current?.scrollToIndex({ index: currentIndex, animated: false }), 50);
|
||||
return;
|
||||
}
|
||||
|
||||
let address = '';
|
||||
let options: TOptions;
|
||||
try {
|
||||
if (!data.toLowerCase().startsWith('bitcoin:')) data = `bitcoin:${data}`;
|
||||
const decoded = DeeplinkSchemaMatch.bip21decode(data);
|
||||
address = decoded.address;
|
||||
options = decoded.options;
|
||||
} catch (error) {
|
||||
data = data.replace(/(amount)=([^&]+)/g, '').replace(/(amount)=([^&]+)&/g, '');
|
||||
const decoded = DeeplinkSchemaMatch.bip21decode(data);
|
||||
decoded.options.amount = 0;
|
||||
address = decoded.address;
|
||||
options = decoded.options;
|
||||
}
|
||||
|
||||
console.log('options', options);
|
||||
if (wallet.isAddressValid(address)) {
|
||||
setAddresses(addrs => {
|
||||
addrs[scrollIndex.current].address = address;
|
||||
addrs[scrollIndex.current].amount = options?.amount ?? 0;
|
||||
addrs[scrollIndex.current].amountSats = new BigNumber(options?.amount ?? 0).multipliedBy(100000000).toNumber();
|
||||
return [...addrs];
|
||||
});
|
||||
setAddresses(addrs => {
|
||||
addrs[scrollIndex.current].unit = BitcoinUnit.BTC;
|
||||
return [...addrs];
|
||||
});
|
||||
setParams({ transactionMemo: options.label || '', amountUnit: BitcoinUnit.BTC, payjoinUrl: options.pj || '' }); // there used to be `options.message` here as well. bug?
|
||||
// RN Bug: contentOffset gets reset to 0 when state changes. Remove code once this bug is resolved.
|
||||
setTimeout(() => scrollView.current?.scrollToIndex({ index: currentIndex, animated: false }), 50);
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
return presentAlert({ title: loc.errors.error, message: loc.send.details_address_field_is_not_valid });
|
||||
}
|
||||
|
||||
const cl = new ContactList();
|
||||
|
||||
const dataWithoutSchema = data.replace('bitcoin:', '').replace('BITCOIN:', '');
|
||||
if (wallet.isAddressValid(dataWithoutSchema) || cl.isPaymentCodeValid(dataWithoutSchema)) {
|
||||
setAddresses(addrs => {
|
||||
addrs[scrollIndex.current].address = dataWithoutSchema;
|
||||
return [...addrs];
|
||||
});
|
||||
setIsLoading(false);
|
||||
setTimeout(() => scrollView.current?.scrollToIndex({ index: currentIndex, animated: false }), 50);
|
||||
return;
|
||||
}
|
||||
|
||||
let address = '';
|
||||
let options: TOptions;
|
||||
try {
|
||||
if (!data.toLowerCase().startsWith('bitcoin:')) data = `bitcoin:${data}`;
|
||||
const decoded = DeeplinkSchemaMatch.bip21decode(data);
|
||||
address = decoded.address;
|
||||
options = decoded.options;
|
||||
} catch (error) {
|
||||
data = data.replace(/(amount)=([^&]+)/g, '').replace(/(amount)=([^&]+)&/g, '');
|
||||
const decoded = DeeplinkSchemaMatch.bip21decode(data);
|
||||
decoded.options.amount = 0;
|
||||
address = decoded.address;
|
||||
options = decoded.options;
|
||||
}
|
||||
|
||||
console.log('options', options);
|
||||
if (wallet.isAddressValid(address)) {
|
||||
setAddresses(addrs => {
|
||||
addrs[scrollIndex.current].address = address;
|
||||
addrs[scrollIndex.current].amount = options?.amount ?? 0;
|
||||
addrs[scrollIndex.current].amountSats = new BigNumber(options?.amount ?? 0).multipliedBy(100000000).toNumber();
|
||||
return [...addrs];
|
||||
});
|
||||
setAddresses(addrs => {
|
||||
addrs[scrollIndex.current].unit = BitcoinUnit.BTC;
|
||||
return [...addrs];
|
||||
});
|
||||
setParams({ transactionMemo: options.label || '', amountUnit: BitcoinUnit.BTC, payjoinUrl: options.pj || '' }); // there used to be `options.message` here as well. bug?
|
||||
// RN Bug: contentOffset gets reset to 0 when state changes. Remove code once this bug is resolved.
|
||||
setTimeout(() => scrollView.current?.scrollToIndex({ index: currentIndex, animated: false }), 50);
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
},
|
||||
[setParams, wallet],
|
||||
);
|
||||
|
||||
const createTransaction = async () => {
|
||||
assert(wallet, 'Internal error: wallet is not set');
|
||||
|
@ -688,6 +690,7 @@ const SendDetails = () => {
|
|||
walletID: wallet.getID(),
|
||||
psbt,
|
||||
});
|
||||
|
||||
setIsLoading(false);
|
||||
}
|
||||
},
|
||||
|
@ -854,7 +857,6 @@ const SendDetails = () => {
|
|||
navigation.navigate('CreateTransaction', {
|
||||
fee: new BigNumber(psbt.getFee()).dividedBy(100000000).toNumber(),
|
||||
feeSatoshi: psbt.getFee(),
|
||||
wallet,
|
||||
tx: tx.toHex(),
|
||||
recipients,
|
||||
satoshiPerByte: psbt.getFeeRate(),
|
||||
|
@ -869,28 +871,39 @@ const SendDetails = () => {
|
|||
const data = routeParams.onBarScanned;
|
||||
if (data) {
|
||||
if (selectedDataProcessor.current) {
|
||||
if (
|
||||
selectedDataProcessor.current === CommonToolTipActions.ImportTransactionQR ||
|
||||
selectedDataProcessor.current === CommonToolTipActions.CoSignTransaction ||
|
||||
selectedDataProcessor.current === CommonToolTipActions.SignPSBT
|
||||
) {
|
||||
if (selectedDataProcessor.current === CommonToolTipActions.ImportTransactionQR) {
|
||||
console.debug('SendDetails - selectedDataProcessor:', selectedDataProcessor.current);
|
||||
switch (selectedDataProcessor.current) {
|
||||
case CommonToolTipActions.ImportTransactionQR:
|
||||
importQrTransactionOnBarScanned(data);
|
||||
} else if (
|
||||
selectedDataProcessor.current === CommonToolTipActions.CoSignTransaction ||
|
||||
selectedDataProcessor.current === CommonToolTipActions.SignPSBT
|
||||
) {
|
||||
break;
|
||||
case CommonToolTipActions.SignPSBT:
|
||||
handlePsbtSign(data);
|
||||
} else {
|
||||
onBarScanned(data);
|
||||
}
|
||||
} else {
|
||||
console.log('Unknown selectedDataProcessor:', selectedDataProcessor.current);
|
||||
break;
|
||||
case CommonToolTipActions.CoSignTransaction:
|
||||
case CommonToolTipActions.ImportTransactionMultsig:
|
||||
_importTransactionMultisig(data);
|
||||
break;
|
||||
case CommonToolTipActions.ImportTransaction:
|
||||
processAddressData(data);
|
||||
break;
|
||||
default:
|
||||
console.debug('Unknown selectedDataProcessor:', selectedDataProcessor.current);
|
||||
}
|
||||
} else {
|
||||
processAddressData(data);
|
||||
}
|
||||
setParams({ onBarScanned: undefined });
|
||||
}
|
||||
}, [handlePsbtSign, importQrTransactionOnBarScanned, onBarScanned, routeParams.onBarScanned, setParams]);
|
||||
selectedDataProcessor.current = undefined;
|
||||
setParams({ onBarScanned: undefined });
|
||||
}, [
|
||||
importQrTransactionOnBarScanned,
|
||||
onBarScanned,
|
||||
routeParams.onBarScanned,
|
||||
setParams,
|
||||
processAddressData,
|
||||
_importTransactionMultisig,
|
||||
handlePsbtSign,
|
||||
]);
|
||||
|
||||
const navigateToQRCodeScanner = () => {
|
||||
navigation.navigate('ScanQRCode', {
|
||||
|
@ -979,11 +992,13 @@ const SendDetails = () => {
|
|||
} else if (id === CommonToolTipActions.AllowRBF.id) {
|
||||
onReplaceableFeeSwitchValueChanged(!isTransactionReplaceable);
|
||||
} else if (id === CommonToolTipActions.ImportTransaction.id) {
|
||||
selectedDataProcessor.current = CommonToolTipActions.ImportTransaction;
|
||||
importTransaction();
|
||||
} else if (id === CommonToolTipActions.ImportTransactionQR.id) {
|
||||
selectedDataProcessor.current = CommonToolTipActions.ImportTransactionQR;
|
||||
importQrTransaction();
|
||||
} else if (id === CommonToolTipActions.ImportTransactionMultsig.id) {
|
||||
selectedDataProcessor.current = CommonToolTipActions.ImportTransactionMultsig;
|
||||
importTransactionMultisig();
|
||||
} else if (id === CommonToolTipActions.CoSignTransaction.id) {
|
||||
selectedDataProcessor.current = CommonToolTipActions.CoSignTransaction;
|
||||
|
@ -1296,11 +1311,9 @@ const SendDetails = () => {
|
|||
setIsLoading(false);
|
||||
setParams({ payjoinUrl: pjUrl });
|
||||
}}
|
||||
onBarScanned={processAddressData}
|
||||
address={item.address}
|
||||
isLoading={isLoading}
|
||||
inputAccessoryViewID={DismissKeyboardInputAccessoryViewID}
|
||||
launchedBy={name}
|
||||
editable={isEditable}
|
||||
style={styles.addressInput}
|
||||
/>
|
||||
|
|
|
@ -12,7 +12,7 @@ import Share from 'react-native-share';
|
|||
|
||||
import { satoshiToBTC } from '../../blue_modules/currency';
|
||||
import { isDesktop } from '../../blue_modules/environment';
|
||||
import { BlueText } from '../../BlueComponents';
|
||||
import { BlueSpacing20, BlueText } from '../../BlueComponents';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import { DynamicQRCode } from '../../components/DynamicQRCode';
|
||||
import { useTheme } from '../../components/themes';
|
||||
|
@ -140,7 +140,13 @@ const SendCreate = () => {
|
|||
|
||||
const ListHeaderComponent = (
|
||||
<View>
|
||||
{showAnimatedQr && psbt ? <DynamicQRCode value={psbt.toHex()} /> : null}
|
||||
{showAnimatedQr && psbt ? (
|
||||
<>
|
||||
<BlueSpacing20 />
|
||||
<DynamicQRCode value={psbt.toHex()} />
|
||||
<BlueSpacing20 />
|
||||
</>
|
||||
) : null}
|
||||
<BlueText style={[styles.cardText, styleHooks.cardText]}>{loc.send.create_this_is_hex}</BlueText>
|
||||
<TextInput testID="TxhexInput" style={styles.cardTx} height={72} multiline editable={false} value={tx} />
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
|
|||
import { useRoute } from '@react-navigation/native';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
import { FlatList, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
import { FlatList, StyleSheet, Text, TouchableOpacity, View, LayoutAnimation } from 'react-native';
|
||||
import { Icon } from '@rneui/themed';
|
||||
|
||||
import { satoshiToBTC, satoshiToLocalCurrency } from '../../blue_modules/currency';
|
||||
|
@ -16,10 +16,6 @@ import { BitcoinUnit } from '../../models/bitcoinUnits';
|
|||
import { useStorage } from '../../hooks/context/useStorage';
|
||||
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
|
||||
|
||||
const shortenAddress = addr => {
|
||||
return addr.substr(0, Math.floor(addr.length / 2) - 1) + '\n' + addr.substr(Math.floor(addr.length / 2) - 1, addr.length);
|
||||
};
|
||||
|
||||
const PsbtMultisig = () => {
|
||||
const { wallets } = useStorage();
|
||||
const { navigate, setParams } = useExtendedNavigation();
|
||||
|
@ -69,19 +65,32 @@ const PsbtMultisig = () => {
|
|||
},
|
||||
});
|
||||
|
||||
let destination = [];
|
||||
let totalSat = 0;
|
||||
const targets = [];
|
||||
for (const output of psbt.txOutputs) {
|
||||
if (output.address && !wallet.weOwnAddress(output.address)) {
|
||||
totalSat += output.value;
|
||||
destination.push(output.address);
|
||||
targets.push({ address: output.address, value: output.value });
|
||||
// if useFilter is true, include only non-owned addresses.
|
||||
const getDestinationData = (useFilter = true) => {
|
||||
const addresses = [];
|
||||
let totalSat = 0;
|
||||
const targets = [];
|
||||
for (const output of psbt.txOutputs) {
|
||||
if (output.address) {
|
||||
if (useFilter && wallet.weOwnAddress(output.address)) continue;
|
||||
totalSat += output.value;
|
||||
addresses.push(output.address);
|
||||
targets.push({ address: output.address, value: output.value });
|
||||
}
|
||||
}
|
||||
}
|
||||
destination = shortenAddress(destination.join(', '));
|
||||
const totalBtc = new BigNumber(totalSat).dividedBy(100000000).toNumber();
|
||||
const totalFiat = satoshiToLocalCurrency(totalSat);
|
||||
return { addresses, totalSat, targets };
|
||||
};
|
||||
|
||||
const filteredData = getDestinationData(true);
|
||||
const unfilteredData = getDestinationData(false);
|
||||
|
||||
const targets = filteredData.targets;
|
||||
|
||||
const [isFiltered, setIsFiltered] = useState(true);
|
||||
|
||||
const displayData = isFiltered ? filteredData : unfilteredData;
|
||||
const displayTotalBtc = new BigNumber(displayData.totalSat).dividedBy(100000000).toNumber();
|
||||
const displayTotalFiat = satoshiToLocalCurrency(displayData.totalSat);
|
||||
|
||||
const getFee = () => {
|
||||
return wallet.calculateFeeFromPsbt(psbt);
|
||||
|
@ -195,11 +204,12 @@ const PsbtMultisig = () => {
|
|||
return howManySignaturesWeHave >= wallet.getM();
|
||||
};
|
||||
|
||||
const destinationAddress = () => {
|
||||
// eslint-disable-next-line prefer-const
|
||||
let destinationAddressView = [];
|
||||
const destinationAddress = (useFilter = true) => {
|
||||
const addrs = useFilter ? filteredData.addresses : unfilteredData.addresses;
|
||||
const displayAddrs = useFilter ? addrs : [...new Set(addrs)];
|
||||
const destinationAddressView = [];
|
||||
const whitespace = '_';
|
||||
const destinations = Object.entries(destination.split(','));
|
||||
const destinations = Object.entries(displayAddrs);
|
||||
for (const [index, address] of destinations) {
|
||||
if (index > 1) {
|
||||
destinationAddressView.push(
|
||||
|
@ -213,11 +223,11 @@ const PsbtMultisig = () => {
|
|||
} else {
|
||||
const currentAddress = address;
|
||||
const firstFour = currentAddress.substring(0, 5);
|
||||
const lastFour = currentAddress.substring(currentAddress.length - 5, currentAddress.length);
|
||||
const middle = currentAddress.split(firstFour)[1].split(lastFour)[0];
|
||||
const lastFour = currentAddress.substring(currentAddress.length - 5);
|
||||
const middle = currentAddress.length > 10 ? currentAddress.slice(5, currentAddress.length - 5) : '';
|
||||
destinationAddressView.push(
|
||||
<View style={styles.destinationTextContainer} key={`${currentAddress}-${index}`}>
|
||||
<Text style={styles.textAlignCenter}>
|
||||
<Text style={styles.textAlignCenter} selectable>
|
||||
<Text numberOfLines={2} style={[styles.textDestinationFirstFour, stylesHook.textBtc]}>
|
||||
{firstFour}
|
||||
<Text style={stylesHook.whitespace}>{whitespace}</Text>
|
||||
|
@ -233,72 +243,94 @@ const PsbtMultisig = () => {
|
|||
return destinationAddressView;
|
||||
};
|
||||
|
||||
const handleToggleFilter = () => {
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
setIsFiltered(prev => !prev);
|
||||
};
|
||||
|
||||
const header = (
|
||||
<View style={stylesHook.root}>
|
||||
<View style={styles.containerText}>
|
||||
<BlueText style={[styles.textBtc, stylesHook.textBtc]}>{totalBtc}</BlueText>
|
||||
<TouchableOpacity onPress={handleToggleFilter}>
|
||||
<BlueText selectable style={[styles.textBtc, stylesHook.textBtc]}>
|
||||
{displayTotalBtc}
|
||||
</BlueText>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.textBtcUnit}>
|
||||
<BlueText style={[styles.textBtcUnitValue, stylesHook.textBtcUnitValue]}> {BitcoinUnit.BTC}</BlueText>
|
||||
<BlueText selectable style={[styles.textBtcUnitValue, stylesHook.textBtcUnitValue]}>
|
||||
{' '}
|
||||
{BitcoinUnit.BTC}
|
||||
</BlueText>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.containerText}>
|
||||
<BlueText style={[styles.textFiat, stylesHook.textFiat]}>{totalFiat}</BlueText>
|
||||
<TouchableOpacity onPress={handleToggleFilter}>
|
||||
<BlueText selectable style={[styles.textFiat, stylesHook.textFiat]}>
|
||||
{displayTotalFiat}
|
||||
</BlueText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<View>{destinationAddress()}</View>
|
||||
<View>{destinationAddress(isFiltered)}</View>
|
||||
</View>
|
||||
);
|
||||
const footer = (
|
||||
<>
|
||||
<View style={styles.bottomWrapper}>
|
||||
<View style={styles.bottomFeesWrapper}>
|
||||
<BlueText style={[styles.feeFiatText, stylesHook.feeFiatText]}>
|
||||
{loc.formatString(loc.multisig.fee, { number: satoshiToLocalCurrency(getFee()) })} -{' '}
|
||||
</BlueText>
|
||||
<BlueText>{loc.formatString(loc.multisig.fee_btc, { number: satoshiToBTC(getFee()) })}</BlueText>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.marginConfirmButton}>
|
||||
<Button disabled={!isConfirmEnabled()} title={loc.multisig.confirm} onPress={onConfirm} testID="PsbtMultisigConfirmButton" />
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
|
||||
const onLayout = e => {
|
||||
setFlatListHeight(e.nativeEvent.layout.height);
|
||||
const footer = null;
|
||||
|
||||
const onLayout = event => {
|
||||
setFlatListHeight(event.nativeEvent.layout.height);
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeArea style={stylesHook.root}>
|
||||
<View style={styles.container}>
|
||||
<View style={styles.mstopcontainer}>
|
||||
<View style={styles.mscontainer}>
|
||||
<View style={[styles.msleft, { height: flatListHeight - 260 }]} />
|
||||
<View style={styles.flexColumnSpaceBetween}>
|
||||
<View style={styles.flexOne}>
|
||||
<View style={styles.container}>
|
||||
<View style={styles.mstopcontainer}>
|
||||
<View style={styles.mscontainer}>
|
||||
<View style={[styles.msleft, { height: flatListHeight - 260 }]} />
|
||||
</View>
|
||||
<View style={styles.msright}>
|
||||
<BlueCard>
|
||||
<FlatList
|
||||
data={data}
|
||||
renderItem={_renderItem}
|
||||
keyExtractor={(_item, index) => `${index}`}
|
||||
ListHeaderComponent={header}
|
||||
ListFooterComponent={footer}
|
||||
onLayout={onLayout}
|
||||
/>
|
||||
{isConfirmEnabled() && (
|
||||
<View style={styles.height80}>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
testID="ExportSignedPsbt"
|
||||
style={[styles.provideSignatureButton, stylesHook.provideSignatureButton]}
|
||||
onPress={navigateToPSBTMultisigQRCode}
|
||||
>
|
||||
<Text style={[styles.provideSignatureButtonText, stylesHook.provideSignatureButtonText]}>
|
||||
{loc.multisig.export_signed_psbt}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
</BlueCard>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.msright}>
|
||||
<BlueCard>
|
||||
<FlatList
|
||||
data={data}
|
||||
onLayout={onLayout}
|
||||
renderItem={_renderItem}
|
||||
keyExtractor={(_item, index) => `${index}`}
|
||||
ListHeaderComponent={header}
|
||||
ListFooterComponent={footer}
|
||||
/>
|
||||
{isConfirmEnabled() && (
|
||||
<View style={styles.height80}>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
testID="ExportSignedPsbt"
|
||||
style={[styles.provideSignatureButton, stylesHook.provideSignatureButton]}
|
||||
onPress={navigateToPSBTMultisigQRCode}
|
||||
>
|
||||
<Text style={[styles.provideSignatureButtonText, stylesHook.provideSignatureButtonText]}>
|
||||
{loc.multisig.export_signed_psbt}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
</BlueCard>
|
||||
</View>
|
||||
<View style={styles.feeConfirmContainer}>
|
||||
<View style={styles.feeContainer}>
|
||||
<View style={styles.bottomWrapper}>
|
||||
<View style={styles.bottomFeesWrapper}>
|
||||
<BlueText selectable style={[styles.feeFiatText, stylesHook.feeFiatText]}>
|
||||
{loc.formatString(loc.multisig.fee, { number: satoshiToLocalCurrency(getFee()) })} -{' '}
|
||||
</BlueText>
|
||||
<BlueText selectable>{loc.formatString(loc.multisig.fee_btc, { number: satoshiToBTC(getFee()) })}</BlueText>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.flexConfirm}>
|
||||
<Button disabled={!isConfirmEnabled()} title={loc.multisig.confirm} onPress={onConfirm} testID="PsbtMultisigConfirmButton" />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
@ -314,13 +346,16 @@ const styles = StyleSheet.create({
|
|||
mscontainer: {
|
||||
flex: 10,
|
||||
},
|
||||
flexOne: {
|
||||
flex: 1,
|
||||
},
|
||||
msleft: {
|
||||
width: 1,
|
||||
borderStyle: 'dashed',
|
||||
borderWidth: 0.8,
|
||||
borderColor: '#c4c4c4',
|
||||
marginLeft: 40,
|
||||
marginTop: 130,
|
||||
marginTop: 160,
|
||||
},
|
||||
msright: {
|
||||
flex: 90,
|
||||
|
@ -328,7 +363,6 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
container: {
|
||||
flexDirection: 'column',
|
||||
paddingTop: 24,
|
||||
flex: 1,
|
||||
},
|
||||
containerText: {
|
||||
|
@ -340,6 +374,7 @@ const styles = StyleSheet.create({
|
|||
marginBottom: 4,
|
||||
paddingHorizontal: 60,
|
||||
fontSize: 14,
|
||||
marginVertical: 8,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
textFiat: {
|
||||
|
@ -397,10 +432,25 @@ const styles = StyleSheet.create({
|
|||
textBtcUnit: { justifyContent: 'flex-end' },
|
||||
bottomFeesWrapper: { justifyContent: 'center', alignItems: 'center', flexDirection: 'row' },
|
||||
bottomWrapper: { marginTop: 16 },
|
||||
marginConfirmButton: { marginTop: 16, marginHorizontal: 32, marginBottom: 48 },
|
||||
height80: {
|
||||
height: 80,
|
||||
},
|
||||
flexColumnSpaceBetween: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
flexConfirm: {
|
||||
paddingHorizontal: 32,
|
||||
paddingVertical: 16,
|
||||
},
|
||||
feeConfirmContainer: {
|
||||
paddingHorizontal: 32,
|
||||
paddingVertical: 16,
|
||||
},
|
||||
feeContainer: {
|
||||
marginBottom: 8,
|
||||
},
|
||||
});
|
||||
|
||||
export default PsbtMultisig;
|
||||
|
|
|
@ -2,15 +2,14 @@ import { useIsFocused, useNavigation, useRoute } from '@react-navigation/native'
|
|||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { ActivityIndicator, ScrollView, StyleSheet, View } from 'react-native';
|
||||
|
||||
import { BlueSpacing20 } from '../../BlueComponents';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import { DynamicQRCode } from '../../components/DynamicQRCode';
|
||||
import SafeArea from '../../components/SafeArea';
|
||||
import SaveFileButton from '../../components/SaveFileButton';
|
||||
import { SquareButton } from '../../components/SquareButton';
|
||||
import { useTheme } from '../../components/themes';
|
||||
import loc from '../../loc';
|
||||
import TipBox from '../../components/TipBox';
|
||||
|
||||
const PsbtMultisigQRCode = () => {
|
||||
const navigation = useNavigation();
|
||||
|
@ -64,8 +63,8 @@ const PsbtMultisigQRCode = () => {
|
|||
useEffect(() => {
|
||||
const data = params.onBarScanned;
|
||||
if (data) {
|
||||
onBarScanned({ data });
|
||||
navigation.setParams({ onBarScanned: undefined });
|
||||
onBarScanned({ data });
|
||||
}
|
||||
}, [onBarScanned, params.onBarScanned, navigation]);
|
||||
|
||||
|
@ -86,50 +85,72 @@ const PsbtMultisigQRCode = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<SafeArea style={stylesHook.root}>
|
||||
<ScrollView centerContent contentContainerStyle={styles.scrollViewContent}>
|
||||
<View style={[styles.modalContentShort, stylesHook.modalContentShort]}>
|
||||
<DynamicQRCode value={psbt.toHex()} ref={dynamicQRCode} />
|
||||
{!isShowOpenScanner && (
|
||||
<>
|
||||
<BlueSpacing20 />
|
||||
<SquareButton
|
||||
testID="CosignedScanOrImportFile"
|
||||
style={[styles.exportButton, stylesHook.exportButton]}
|
||||
onPress={openScanner}
|
||||
ref={openScannerButton}
|
||||
title={loc.multisig.scan_or_import_file}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<ScrollView
|
||||
centerContent
|
||||
testID="PsbtMultisigQRCodeScrollView"
|
||||
automaticallyAdjustContentInsets
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
contentContainerStyle={[styles.scrollViewContent, stylesHook.root, styles.modalContentShort, stylesHook.modalContentShort]}
|
||||
>
|
||||
<TipBox
|
||||
number="1"
|
||||
title={loc.multisig.provide_signature}
|
||||
description={loc.multisig.provide_signature_details}
|
||||
additionalDescription={`${loc.multisig.provide_signature_details_bluewallet} ${loc.multisig.co_sign_transaction}`}
|
||||
/>
|
||||
<DynamicQRCode value={psbt.toHex()} ref={dynamicQRCode} />
|
||||
{!isLoading && (
|
||||
<>
|
||||
<BlueSpacing20 />
|
||||
{isLoading ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<SaveFileButton
|
||||
fileName={fileName}
|
||||
fileContent={psbt.toBase64()}
|
||||
beforeOnPress={saveFileButtonBeforeOnPress}
|
||||
afterOnPress={saveFileButtonAfterOnPress}
|
||||
style={[styles.exportButton, stylesHook.exportButton]}
|
||||
>
|
||||
<SquareButton title={loc.multisig.share} />
|
||||
</SaveFileButton>
|
||||
)}
|
||||
</View>
|
||||
</ScrollView>
|
||||
</SafeArea>
|
||||
<View style={styles.divider} />
|
||||
<TipBox
|
||||
number="2"
|
||||
title={loc.multisig.provide_signature_next_steps}
|
||||
description={loc.multisig.provide_signature_next_steps_details}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{!isShowOpenScanner && (
|
||||
<>
|
||||
<SquareButton
|
||||
testID="CosignedScanOrImportFile"
|
||||
style={[styles.exportButton, stylesHook.exportButton]}
|
||||
onPress={openScanner}
|
||||
ref={openScannerButton}
|
||||
title={loc.multisig.scan_or_import_file}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<BlueSpacing20 />
|
||||
|
||||
{isLoading ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<SaveFileButton
|
||||
fileName={fileName}
|
||||
fileContent={psbt.toBase64()}
|
||||
beforeOnPress={saveFileButtonBeforeOnPress}
|
||||
afterOnPress={saveFileButtonAfterOnPress}
|
||||
style={[styles.exportButton, stylesHook.exportButton]}
|
||||
>
|
||||
<SquareButton title={loc.multisig.share} />
|
||||
</SaveFileButton>
|
||||
)}
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
scrollViewContent: {
|
||||
flexGrow: 1,
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
modalContentShort: {
|
||||
marginLeft: 20,
|
||||
marginRight: 20,
|
||||
paddingHorizontal: 20,
|
||||
},
|
||||
divider: {
|
||||
height: 0.5,
|
||||
backgroundColor: '#d2d2d2',
|
||||
marginVertical: 20,
|
||||
},
|
||||
exportButton: {
|
||||
height: 48,
|
||||
|
|
|
@ -87,8 +87,8 @@ const ElectrumSettings: React.FC = () => {
|
|||
console.log('Fetching data...');
|
||||
const preferredServer = await BlueElectrum.getPreferredServer();
|
||||
const savedHost = preferredServer?.host;
|
||||
const savedPort = preferredServer?.tcp;
|
||||
const savedSslPort = preferredServer?.ssl;
|
||||
const savedPort = preferredServer?.tcp ? Number(preferredServer.tcp) : undefined;
|
||||
const savedSslPort = preferredServer?.ssl ? Number(preferredServer.ssl) : undefined;
|
||||
const serverHistoryStr = (await DefaultPreference.get(BlueElectrum.ELECTRUM_SERVER_HISTORY)) as string;
|
||||
|
||||
console.log('Preferred server:', preferredServer);
|
||||
|
@ -111,15 +111,15 @@ const ElectrumSettings: React.FC = () => {
|
|||
v.host &&
|
||||
(v.tcp || v.ssl) &&
|
||||
!suggestedServers.some(s => s.host === v.host && s.tcp === v.tcp && s.ssl === v.ssl) &&
|
||||
!hardcodedPeers.some(peer => peer.host === v.host),
|
||||
!hardcodedPeers.some(peer => peer.host === v.host && peer.tcp === v.tcp && peer.ssl === v.ssl),
|
||||
),
|
||||
);
|
||||
|
||||
console.log('Filtered server history:', filteredServerHistory);
|
||||
|
||||
setHost(savedHost || '');
|
||||
setPort(savedPort ? Number(savedPort) : undefined);
|
||||
setSslPort(savedSslPort ? Number(savedSslPort) : undefined);
|
||||
setPort(savedPort);
|
||||
setSslPort(savedSslPort);
|
||||
setServerHistory(filteredServerHistory);
|
||||
|
||||
setConfig(await BlueElectrum.getConfig());
|
||||
|
@ -169,11 +169,22 @@ const ElectrumSettings: React.FC = () => {
|
|||
|
||||
try {
|
||||
const serverHost = v?.host || host;
|
||||
const serverPort = v?.tcp || port?.toString() || '';
|
||||
const serverSslPort = v?.ssl || sslPort?.toString() || '';
|
||||
const serverPort = v?.tcp ? v.tcp.toString() : port?.toString() || '';
|
||||
const serverSslPort = v?.ssl ? v.ssl.toString() : sslPort?.toString() || '';
|
||||
|
||||
if (serverHost && (serverPort || serverSslPort)) {
|
||||
const testConnect = await BlueElectrum.testConnection(serverHost, Number(serverPort), Number(serverSslPort));
|
||||
if (!testConnect) return;
|
||||
await DefaultPreference.setName(GROUP_IO_BLUEWALLET);
|
||||
|
||||
// Clear current data for the preferred host
|
||||
console.log('Clearing current data for the preferred host');
|
||||
await DefaultPreference.clear(BlueElectrum.ELECTRUM_HOST);
|
||||
await DefaultPreference.clear(BlueElectrum.ELECTRUM_TCP_PORT);
|
||||
await DefaultPreference.clear(BlueElectrum.ELECTRUM_SSL_PORT);
|
||||
|
||||
// Save the new preferred host
|
||||
console.log('Saving new preferred host');
|
||||
await DefaultPreference.set(BlueElectrum.ELECTRUM_HOST, serverHost);
|
||||
await DefaultPreference.set(BlueElectrum.ELECTRUM_TCP_PORT, serverPort);
|
||||
await DefaultPreference.set(BlueElectrum.ELECTRUM_SSL_PORT, serverSslPort);
|
||||
|
@ -188,6 +199,8 @@ const ElectrumSettings: React.FC = () => {
|
|||
await DefaultPreference.set(BlueElectrum.ELECTRUM_SERVER_HISTORY, JSON.stringify(Array.from(newServerHistory)));
|
||||
setServerHistory(newServerHistory);
|
||||
}
|
||||
} else {
|
||||
throw new Error(loc.settings.electrum_error_connect);
|
||||
}
|
||||
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
|
@ -268,7 +281,7 @@ const ElectrumSettings: React.FC = () => {
|
|||
|
||||
const isPreferred = useCallback(
|
||||
(value: ElectrumServerItem) => {
|
||||
return host === value.host && ((sslPort !== undefined && value.ssl === sslPort) || (sslPort === undefined && port === value.tcp));
|
||||
return value.host === host && ((sslPort !== undefined && value.ssl === sslPort) || (sslPort === undefined && value.tcp === port));
|
||||
},
|
||||
[host, port, sslPort],
|
||||
);
|
||||
|
@ -282,8 +295,7 @@ const ElectrumSettings: React.FC = () => {
|
|||
};
|
||||
const createServerAction = useCallback(
|
||||
({ value, seenHosts, isPreferred: _unused, isConnectedTo = false, isSuggested = false }: TCreateServerActionParameters) => {
|
||||
const hostKey = `${value.host}:${value.ssl ?? value.tcp}`;
|
||||
if (seenHosts.has(hostKey)) return null;
|
||||
const hostKey = `${value.host}:${value.tcp ?? ''}:${value.ssl ?? ''}`;
|
||||
|
||||
seenHosts.add(hostKey);
|
||||
return {
|
||||
|
@ -299,41 +311,58 @@ const ElectrumSettings: React.FC = () => {
|
|||
);
|
||||
|
||||
const generateToolTipActions = useCallback(() => {
|
||||
const actions: Action[] = [];
|
||||
const determineConnectedServer = (): string | null => {
|
||||
const allServers = [...suggestedServers, ...Array.from(serverHistory)];
|
||||
for (const value of allServers) {
|
||||
const isThisConnected = config?.host === value.host && (config.port === value.tcp || config.port === value.ssl);
|
||||
if (isThisConnected && isPreferred(value)) return JSON.stringify(value);
|
||||
}
|
||||
for (const value of allServers) {
|
||||
const isThisConnected = config?.host === value.host && (config.port === value.tcp || config.port === value.ssl);
|
||||
if (isThisConnected) return JSON.stringify(value);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const connectedServer = config?.connected ? determineConnectedServer() : null;
|
||||
|
||||
const seenHosts = new Set<string>();
|
||||
let preferredServerFound = false;
|
||||
let connectedServerFound = false;
|
||||
|
||||
const mapServers = (servers: ElectrumServerItem[], isSuggested: boolean) => {
|
||||
return servers
|
||||
.map(value => {
|
||||
const isConnectedTo = !connectedServerFound && connectedServer === JSON.stringify(value);
|
||||
if (isConnectedTo) connectedServerFound = true;
|
||||
|
||||
const isPreferredServer = !preferredServerFound && isPreferred(value);
|
||||
if (isPreferredServer) preferredServerFound = true;
|
||||
|
||||
return createServerAction({
|
||||
value,
|
||||
seenHosts,
|
||||
isPreferred: isPreferredServer,
|
||||
isConnectedTo,
|
||||
isSuggested,
|
||||
});
|
||||
})
|
||||
.filter((action): action is Action => action !== null);
|
||||
};
|
||||
|
||||
const suggestedServersAction: Action = {
|
||||
id: 'suggested_servers',
|
||||
text: loc._.suggested,
|
||||
displayInline: true,
|
||||
subtitle: loc.settings.electrum_suggested_description,
|
||||
subactions: suggestedServers
|
||||
.map(value =>
|
||||
createServerAction({
|
||||
value,
|
||||
seenHosts,
|
||||
isPreferred: isPreferred(value),
|
||||
isConnectedTo: config?.host === value.host && (config.port === value.tcp || config.port === value.ssl),
|
||||
isSuggested: true,
|
||||
}),
|
||||
)
|
||||
.filter((action): action is Action => action !== null),
|
||||
subactions: mapServers(suggestedServers, true),
|
||||
};
|
||||
|
||||
const actions: Action[] = [];
|
||||
actions.push(suggestedServersAction);
|
||||
|
||||
if (serverHistory.size > 0) {
|
||||
const serverSubactions: Action[] = Array.from(serverHistory)
|
||||
.map(value =>
|
||||
createServerAction({
|
||||
value,
|
||||
seenHosts,
|
||||
isPreferred: isPreferred(value),
|
||||
isConnectedTo: config?.host === value.host && (config.port === value.tcp || config.port === value.ssl),
|
||||
isSuggested: false,
|
||||
}),
|
||||
)
|
||||
.filter((action): action is Action => action !== null);
|
||||
const serverSubactions: Action[] = mapServers(Array.from(serverHistory), false);
|
||||
|
||||
actions.push({
|
||||
id: 'server_history',
|
||||
|
@ -346,11 +375,10 @@ const ElectrumSettings: React.FC = () => {
|
|||
|
||||
const resetToDefaults = { ...CommonToolTipActions.ResetToDefault };
|
||||
resetToDefaults.hidden = !host && serverHistory.size === 0;
|
||||
|
||||
actions.push(resetToDefaults);
|
||||
|
||||
return actions;
|
||||
}, [config?.host, config.port, createServerAction, host, isPreferred, serverHistory]);
|
||||
}, [config?.connected, config?.host, config.port, createServerAction, host, isPreferred, serverHistory]);
|
||||
|
||||
const HeaderRight = useMemo(
|
||||
() => <HeaderMenuButton actions={generateToolTipActions()} onPressMenuItem={onPressMenuItem} />,
|
||||
|
@ -392,10 +420,6 @@ const ElectrumSettings: React.FC = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const importScan = async () => {
|
||||
navigation.navigate('ScanQRCode');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const data = params.onBarScanned;
|
||||
if (data) {
|
||||
|
@ -470,8 +494,8 @@ const ElectrumSettings: React.FC = () => {
|
|||
address={host}
|
||||
onChangeText={text => setHost(text.trim())}
|
||||
editable={!isLoading}
|
||||
onBarScanned={importScan}
|
||||
keyboardType="default"
|
||||
skipValidation
|
||||
onBlur={() => setIsAndroidAddressKeyboardVisible(false)}
|
||||
onFocus={() => setIsAndroidAddressKeyboardVisible(true)}
|
||||
inputAccessoryViewID={DoneAndDismissKeyboardInputAccessoryViewID}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { RouteProp, useRoute } from '@react-navigation/native';
|
||||
import { Alert, I18nManager, Linking, ScrollView, StyleSheet, TextInput, View } from 'react-native';
|
||||
import { Alert, I18nManager, Linking, ScrollView, StyleSheet } from 'react-native';
|
||||
import { Button as ButtonRNElements } from '@rneui/themed';
|
||||
import DefaultPreference from 'react-native-default-preference';
|
||||
import { BlueButtonLink, BlueCard, BlueLoading, BlueSpacing20, BlueText } from '../../BlueComponents';
|
||||
import { BlueCard, BlueLoading, BlueSpacing40, BlueText } from '../../BlueComponents';
|
||||
import DeeplinkSchemaMatch from '../../class/deeplink-schema-match';
|
||||
import { LightningCustodianWallet } from '../../class/wallets/lightning-custodian-wallet';
|
||||
import presentAlert, { AlertType } from '../../components/Alert';
|
||||
|
@ -15,24 +15,9 @@ import { GROUP_IO_BLUEWALLET } from '../../blue_modules/currency';
|
|||
import { clearLNDHub, getLNDHub, setLNDHub } from '../../helpers/lndHub';
|
||||
import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList';
|
||||
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
|
||||
import AddressInput from '../../components/AddressInput';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
uri: {
|
||||
flexDirection: 'row',
|
||||
borderWidth: 1,
|
||||
borderBottomWidth: 0.5,
|
||||
minHeight: 44,
|
||||
height: 44,
|
||||
alignItems: 'center',
|
||||
borderRadius: 4,
|
||||
},
|
||||
uriText: {
|
||||
flex: 1,
|
||||
color: '#81868e',
|
||||
marginHorizontal: 8,
|
||||
minHeight: 36,
|
||||
height: 36,
|
||||
},
|
||||
buttonStyle: {
|
||||
backgroundColor: 'transparent',
|
||||
flexDirection: I18nManager.isRTL ? 'row-reverse' : 'row',
|
||||
|
@ -46,14 +31,7 @@ const LightningSettings: React.FC = () => {
|
|||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [URI, setURI] = useState<string>();
|
||||
const { colors } = useTheme();
|
||||
const { navigate, setParams } = useExtendedNavigation();
|
||||
const styleHook = StyleSheet.create({
|
||||
uri: {
|
||||
borderColor: colors.formBorder,
|
||||
borderBottomColor: colors.formBorder,
|
||||
backgroundColor: colors.inputBackgroundColor,
|
||||
},
|
||||
});
|
||||
const { setParams } = useExtendedNavigation();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchURI = async () => {
|
||||
|
@ -123,12 +101,6 @@ const LightningSettings: React.FC = () => {
|
|||
setIsLoading(false);
|
||||
}, [URI]);
|
||||
|
||||
const importScan = () => {
|
||||
navigate('ScanQRCode', {
|
||||
showFileImportButton: true,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const data = params?.onBarScanned;
|
||||
if (data) {
|
||||
|
@ -158,25 +130,15 @@ const LightningSettings: React.FC = () => {
|
|||
/>
|
||||
|
||||
<BlueCard>
|
||||
<View style={[styles.uri, styleHook.uri]}>
|
||||
<TextInput
|
||||
value={URI}
|
||||
placeholder={loc.formatString(loc.settings.lndhub_uri, { example: 'https://10.20.30.40:3000' })}
|
||||
onChangeText={setLndhubURI}
|
||||
numberOfLines={1}
|
||||
style={styles.uriText}
|
||||
placeholderTextColor="#81868e"
|
||||
editable={!isLoading}
|
||||
textContentType="URL"
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
underlineColorAndroid="transparent"
|
||||
testID="URIInput"
|
||||
/>
|
||||
</View>
|
||||
<BlueSpacing20 />
|
||||
<BlueButtonLink title={loc.wallets.import_scan_qr} testID="ImportScan" onPress={importScan} />
|
||||
<BlueSpacing20 />
|
||||
<AddressInput
|
||||
isLoading={isLoading}
|
||||
address={URI}
|
||||
placeholder={loc.formatString(loc.settings.lndhub_uri, { example: 'https://10.20.30.40:3000' })}
|
||||
onChangeText={setLndhubURI}
|
||||
testID="URIInput"
|
||||
editable={!isLoading}
|
||||
/>
|
||||
<BlueSpacing40 />
|
||||
{isLoading ? <BlueLoading /> : <Button testID="Save" onPress={save} title={loc.settings.save} />}
|
||||
</BlueCard>
|
||||
</ScrollView>
|
||||
|
|
|
@ -13,7 +13,6 @@ import {
|
|||
} from 'react-native';
|
||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||
import { useFocusEffect, useNavigation } from '@react-navigation/native';
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
|
||||
import { useTheme } from '../../components/themes';
|
||||
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
|
||||
import loc from '../../loc';
|
||||
|
@ -22,9 +21,6 @@ import useDebounce from '../../hooks/useDebounce';
|
|||
import { TTXMetadata } from '../../class';
|
||||
import { ExtendedTransaction, LightningTransaction, Transaction, TWallet } from '../../class/wallets/types';
|
||||
import useBounceAnimation from '../../hooks/useBounceAnimation';
|
||||
import { unlockWithBiometrics, useBiometrics } from '../../hooks/useBiometrics';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import prompt from '../../helpers/prompt';
|
||||
import HeaderRightButton from '../../components/HeaderRightButton';
|
||||
import { useSettings } from '../../hooks/context/useSettings';
|
||||
import DragList, { DragListRenderItemInfo } from 'react-native-draglist';
|
||||
|
@ -193,12 +189,11 @@ const reducer = (state: State, action: Action): State => {
|
|||
|
||||
const ManageWallets: React.FC = () => {
|
||||
const { colors, closeImage } = useTheme();
|
||||
const { wallets: storedWallets, setWalletsWithNewOrder, txMetadata } = useStorage();
|
||||
const { wallets: storedWallets, setWalletsWithNewOrder, txMetadata, handleWalletDeletion } = useStorage();
|
||||
const { setIsDrawerShouldHide } = useSettings();
|
||||
const walletsRef = useRef<TWallet[]>(deepCopyWallets(storedWallets)); // Create a deep copy of wallets for the DraggableFlatList
|
||||
const { navigate, setOptions, goBack } = useExtendedNavigation();
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
const { isBiometricUseCapableAndEnabled } = useBiometrics();
|
||||
const navigation = useNavigation();
|
||||
const debouncedSearchQuery = useDebounce(state.searchQuery, 300);
|
||||
const bounceAnim = useBounceAnimation(state.searchQuery);
|
||||
|
@ -242,6 +237,12 @@ const ManageWallets: React.FC = () => {
|
|||
|
||||
walletsRef.current = deepCopyWallets(newWalletOrder);
|
||||
|
||||
state.tempOrder.forEach(item => {
|
||||
if (item.type === ItemType.WalletSection && !newWalletOrder.some(wallet => wallet.getID() === item.data.getID())) {
|
||||
handleWalletDeletion(item.data.getID());
|
||||
}
|
||||
});
|
||||
|
||||
if (beforeRemoveListenerRef.current) {
|
||||
navigation.removeListener('beforeRemove', beforeRemoveListenerRef.current);
|
||||
}
|
||||
|
@ -251,7 +252,7 @@ const ManageWallets: React.FC = () => {
|
|||
dispatch({ type: SET_SEARCH_QUERY, payload: '' });
|
||||
dispatch({ type: SET_IS_SEARCH_FOCUSED, payload: false });
|
||||
}
|
||||
}, [goBack, setWalletsWithNewOrder, state.searchQuery, state.isSearchFocused, state.tempOrder, navigation]);
|
||||
}, [goBack, setWalletsWithNewOrder, state.searchQuery, state.isSearchFocused, state.tempOrder, navigation, handleWalletDeletion]);
|
||||
|
||||
const hasUnsavedChanges = useMemo(() => {
|
||||
return JSON.stringify(walletsRef.current) !== JSON.stringify(state.tempOrder.map(item => item.data));
|
||||
|
@ -358,58 +359,14 @@ const ManageWallets: React.FC = () => {
|
|||
[bounceAnim],
|
||||
);
|
||||
|
||||
const presentWalletHasBalanceAlert = useCallback(async (wallet: TWallet) => {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationWarning);
|
||||
try {
|
||||
const walletBalanceConfirmation = await prompt(
|
||||
loc.wallets.details_delete_wallet,
|
||||
loc.formatString(loc.wallets.details_del_wb_q, { balance: wallet.getBalance() }),
|
||||
true,
|
||||
'plain-text',
|
||||
true,
|
||||
loc.wallets.details_delete,
|
||||
);
|
||||
if (Number(walletBalanceConfirmation) === wallet.getBalance()) {
|
||||
dispatch({ type: REMOVE_WALLET, payload: wallet.getID() });
|
||||
} else {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
presentAlert({ message: loc.wallets.details_del_wb_err });
|
||||
}
|
||||
} catch (_) {}
|
||||
}, []);
|
||||
|
||||
const handleDeleteWallet = useCallback(
|
||||
async (wallet: TWallet) => {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationWarning);
|
||||
Alert.alert(
|
||||
loc.wallets.details_delete_wallet,
|
||||
loc.wallets.details_are_you_sure,
|
||||
[
|
||||
{
|
||||
text: loc.wallets.details_yes_delete,
|
||||
onPress: async () => {
|
||||
const isBiometricsEnabled = await isBiometricUseCapableAndEnabled();
|
||||
|
||||
if (isBiometricsEnabled) {
|
||||
if (!(await unlockWithBiometrics())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (wallet.getBalance() > 0 && wallet.allowSend()) {
|
||||
presentWalletHasBalanceAlert(wallet);
|
||||
} else {
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
dispatch({ type: REMOVE_WALLET, payload: wallet.getID() });
|
||||
}
|
||||
},
|
||||
style: 'destructive',
|
||||
},
|
||||
{ text: loc.wallets.details_no_cancel, onPress: () => {}, style: 'cancel' },
|
||||
],
|
||||
{ cancelable: false },
|
||||
);
|
||||
const deletionSucceeded = await handleWalletDeletion(wallet.getID());
|
||||
if (deletionSucceeded) {
|
||||
dispatch({ type: REMOVE_WALLET, payload: wallet.getID() });
|
||||
}
|
||||
},
|
||||
[isBiometricUseCapableAndEnabled, presentWalletHasBalanceAlert],
|
||||
[handleWalletDeletion],
|
||||
);
|
||||
|
||||
const handleToggleHideBalance = useCallback(
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Alert,
|
||||
I18nManager,
|
||||
InteractionManager,
|
||||
LayoutAnimation,
|
||||
|
@ -38,18 +37,17 @@ import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
|
|||
import loc, { formatBalanceWithoutSuffix } from '../../loc';
|
||||
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
||||
import { useStorage } from '../../hooks/context/useStorage';
|
||||
import { popToTop } from '../../NavigationService';
|
||||
import { useFocusEffect, useRoute, RouteProp } from '@react-navigation/native';
|
||||
import { LightningTransaction, Transaction, TWallet } from '../../class/wallets/types';
|
||||
import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList';
|
||||
import { unsubscribe } from '../../blue_modules/notifications';
|
||||
import HeaderMenuButton from '../../components/HeaderMenuButton';
|
||||
import { Action } from '../../components/types';
|
||||
import { CommonToolTipActions } from '../../typings/CommonToolTipActions';
|
||||
import { popToTop } from '../../NavigationService';
|
||||
|
||||
type RouteProps = RouteProp<DetailViewStackParamList, 'WalletDetails'>;
|
||||
const WalletDetails: React.FC = () => {
|
||||
const { saveToDisk, wallets, deleteWallet, setSelectedWalletID, txMetadata } = useStorage();
|
||||
const { saveToDisk, wallets, setSelectedWalletID, txMetadata, handleWalletDeletion } = useStorage();
|
||||
const { isBiometricUseCapableAndEnabled } = useBiometrics();
|
||||
const { walletID } = useRoute<RouteProps>().params;
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
|
@ -91,23 +89,13 @@ const WalletDetails: React.FC = () => {
|
|||
|
||||
const navigateToOverviewAndDeleteWallet = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const externalAddresses = wallet.getAllExternalAddresses();
|
||||
if (externalAddresses.length > 0) {
|
||||
await unsubscribe(externalAddresses, [], []);
|
||||
}
|
||||
deleteWallet(wallet);
|
||||
saveToDisk(true);
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
const deletionSucceeded = await handleWalletDeletion(wallet.getID());
|
||||
if (deletionSucceeded) {
|
||||
popToTop();
|
||||
} catch (e: unknown) {
|
||||
console.error(e);
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
presentAlert({ message: (e as Error).message });
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [deleteWallet, saveToDisk, wallet]);
|
||||
}, [handleWalletDeletion, wallet]);
|
||||
|
||||
const presentWalletHasBalanceAlert = useCallback(async () => {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationWarning);
|
||||
|
@ -125,7 +113,7 @@ const WalletDetails: React.FC = () => {
|
|||
const cleanedConfirmation = (walletBalanceConfirmation || '').replace(/[^0-9]/g, '');
|
||||
|
||||
if (Number(cleanedConfirmation) === wallet.getBalance()) {
|
||||
await navigateToOverviewAndDeleteWallet();
|
||||
navigateToOverviewAndDeleteWallet();
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
} else {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
|
@ -137,18 +125,18 @@ const WalletDetails: React.FC = () => {
|
|||
|
||||
const handleDeleteButtonTapped = useCallback(() => {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationWarning);
|
||||
Alert.alert(
|
||||
loc.wallets.details_delete_wallet,
|
||||
loc.wallets.details_are_you_sure,
|
||||
[
|
||||
presentAlert({
|
||||
title: loc.wallets.details_delete_wallet,
|
||||
message: loc.wallets.details_are_you_sure,
|
||||
buttons: [
|
||||
{
|
||||
text: loc.wallets.details_yes_delete,
|
||||
onPress: async () => {
|
||||
const isBiometricsEnabled = await isBiometricUseCapableAndEnabled();
|
||||
|
||||
if (isBiometricsEnabled) {
|
||||
if (!(await unlockWithBiometrics())) {
|
||||
return;
|
||||
setIsLoading(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (wallet.getBalance && wallet.getBalance() > 0 && wallet.allowSend && wallet.allowSend()) {
|
||||
|
@ -159,10 +147,17 @@ const WalletDetails: React.FC = () => {
|
|||
},
|
||||
style: 'destructive',
|
||||
},
|
||||
{ text: loc.wallets.details_no_cancel, onPress: () => {}, style: 'cancel' },
|
||||
{
|
||||
text: loc._.cancel,
|
||||
onPress: () => {
|
||||
setIsLoading(false);
|
||||
return false;
|
||||
},
|
||||
style: 'cancel',
|
||||
},
|
||||
],
|
||||
{ cancelable: false },
|
||||
);
|
||||
options: { cancelable: false },
|
||||
});
|
||||
}, [isBiometricUseCapableAndEnabled, navigateToOverviewAndDeleteWallet, presentWalletHasBalanceAlert, wallet]);
|
||||
|
||||
const exportHistoryContent = useCallback(() => {
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
RefreshControl,
|
||||
} from 'react-native';
|
||||
import { Icon } from '@rneui/themed';
|
||||
import * as BlueElectrum from '../../blue_modules/BlueElectrum';
|
||||
|
@ -28,7 +29,7 @@ import { TransactionListItem } from '../../components/TransactionListItem';
|
|||
import TransactionsNavigationHeader, { actionKeys } from '../../components/TransactionsNavigationHeader';
|
||||
import { unlockWithBiometrics, useBiometrics } from '../../hooks/useBiometrics';
|
||||
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
|
||||
import loc from '../../loc';
|
||||
import loc, { formatBalance } from '../../loc';
|
||||
import { Chain } from '../../models/bitcoinUnits';
|
||||
import ActionSheet from '../ActionSheet';
|
||||
import { useStorage } from '../../hooks/context/useStorage';
|
||||
|
@ -45,6 +46,7 @@ import { useSettings } from '../../hooks/context/useSettings';
|
|||
import { getClipboardContent } from '../../blue_modules/clipboard';
|
||||
import HandOffComponent from '../../components/HandOffComponent';
|
||||
import { HandOffActivityType } from '../../components/types';
|
||||
import WalletGradient from '../../class/wallet-gradient';
|
||||
|
||||
const buttonFontSize =
|
||||
PixelRatio.roundToNearestPixel(Dimensions.get('window').width / 26) > 22
|
||||
|
@ -53,7 +55,7 @@ const buttonFontSize =
|
|||
|
||||
type WalletTransactionsProps = NativeStackScreenProps<DetailViewStackParamList, 'WalletTransactions'>;
|
||||
type RouteProps = RouteProp<DetailViewStackParamList, 'WalletTransactions'>;
|
||||
|
||||
type TransactionListItem = Transaction & { type: 'transaction' | 'header' };
|
||||
const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
||||
const { wallets, saveToDisk, setSelectedWalletID } = useStorage();
|
||||
const { setReloadTransactionsMenuActionFunction } = useMenuElements();
|
||||
|
@ -74,9 +76,6 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
|||
listHeaderText: {
|
||||
color: colors.foregroundColor,
|
||||
},
|
||||
list: {
|
||||
backgroundColor: colors.background,
|
||||
},
|
||||
});
|
||||
|
||||
useFocusEffect(
|
||||
|
@ -112,15 +111,14 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
|||
}
|
||||
}, [navigation, onBarCodeRead, route.params]);
|
||||
|
||||
const getTransactions = useCallback(
|
||||
(lmt = Infinity): Transaction[] => {
|
||||
if (!wallet) return [];
|
||||
const txs = wallet.getTransactions();
|
||||
txs.sort((a: { received: string }, b: { received: string }) => +new Date(b.received) - +new Date(a.received));
|
||||
return txs.slice(0, lmt);
|
||||
},
|
||||
[wallet],
|
||||
);
|
||||
const sortedTransactions = useMemo(() => {
|
||||
if (!wallet) return [];
|
||||
const txs = wallet.getTransactions();
|
||||
txs.sort((a: { received: string }, b: { received: string }) => +new Date(b.received) - +new Date(a.received));
|
||||
return txs;
|
||||
}, [wallet]);
|
||||
|
||||
const getTransactions = useCallback((lmt = Infinity): Transaction[] => sortedTransactions.slice(0, lmt), [sortedTransactions]);
|
||||
|
||||
const loadMoreTransactions = useCallback(() => {
|
||||
if (getTransactions(Infinity).length > limit) {
|
||||
|
@ -179,35 +177,12 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
|||
}
|
||||
}, [wallet, setSelectedWalletID, walletID]);
|
||||
|
||||
const isLightning = (): boolean => wallet?.chain === Chain.OFFCHAIN || false;
|
||||
|
||||
const isLightning = useCallback((): boolean => wallet?.chain === Chain.OFFCHAIN || false, [wallet]);
|
||||
const renderListFooterComponent = () => {
|
||||
// if not all txs rendered - display indicator
|
||||
return wallet && wallet.getTransactions().length > limit ? <ActivityIndicator style={styles.activityIndicator} /> : <View />;
|
||||
};
|
||||
|
||||
const renderListHeaderComponent = () => {
|
||||
const style: any = {};
|
||||
if (!isDesktop) {
|
||||
// we need this button for testing
|
||||
style.opacity = 0;
|
||||
style.height = 1;
|
||||
style.width = 1;
|
||||
} else if (isLoading) {
|
||||
style.opacity = 0.5;
|
||||
} else {
|
||||
style.opacity = 1.0;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.flex}>
|
||||
<View style={styles.listHeaderTextRow}>
|
||||
<Text style={[styles.listHeaderText, stylesHook.listHeaderText]}>{loc.transactions.list_title}</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const navigateToSendScreen = () => {
|
||||
navigate('SendDetailsRoot', {
|
||||
screen: 'SendDetails',
|
||||
|
@ -217,64 +192,70 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
|||
});
|
||||
};
|
||||
|
||||
const onWalletSelect = async (selectedWallet: TWallet) => {
|
||||
assert(wallet?.type === LightningCustodianWallet.type, `internal error, wallet is not ${LightningCustodianWallet.type}`);
|
||||
navigate('WalletTransactions', {
|
||||
walletType: wallet?.type,
|
||||
walletID,
|
||||
key: `WalletTransactions-${walletID}`,
|
||||
}); // navigating back to ln wallet screen
|
||||
const onWalletSelect = useCallback(
|
||||
async (selectedWallet: TWallet) => {
|
||||
assert(wallet?.type === LightningCustodianWallet.type, `internal error, wallet is not ${LightningCustodianWallet.type}`);
|
||||
navigate('WalletTransactions', {
|
||||
walletType: wallet?.type,
|
||||
walletID,
|
||||
key: `WalletTransactions-${walletID}`,
|
||||
}); // navigating back to ln wallet screen
|
||||
|
||||
// getting refill address, either cached or from the server:
|
||||
let toAddress;
|
||||
if (wallet?.refill_addressess.length > 0) {
|
||||
toAddress = wallet.refill_addressess[0];
|
||||
} else {
|
||||
try {
|
||||
await wallet?.fetchBtcAddress();
|
||||
toAddress = wallet?.refill_addressess[0];
|
||||
} catch (Err) {
|
||||
return presentAlert({ message: (Err as Error).message, type: AlertType.Toast });
|
||||
// getting refill address, either cached or from the server:
|
||||
let toAddress;
|
||||
if (wallet?.refill_addressess.length > 0) {
|
||||
toAddress = wallet.refill_addressess[0];
|
||||
} else {
|
||||
try {
|
||||
await wallet?.fetchBtcAddress();
|
||||
toAddress = wallet?.refill_addressess[0];
|
||||
} catch (Err) {
|
||||
return presentAlert({ message: (Err as Error).message, type: AlertType.Toast });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// navigating to pay screen where user can pay to refill address:
|
||||
navigate('SendDetailsRoot', {
|
||||
screen: 'SendDetails',
|
||||
params: {
|
||||
memo: loc.lnd.refill_lnd_balance,
|
||||
address: toAddress,
|
||||
walletID: selectedWallet.getID(),
|
||||
},
|
||||
});
|
||||
};
|
||||
// navigating to pay screen where user can pay to refill address:
|
||||
navigate('SendDetailsRoot', {
|
||||
screen: 'SendDetails',
|
||||
params: {
|
||||
memo: loc.lnd.refill_lnd_balance,
|
||||
address: toAddress,
|
||||
walletID: selectedWallet.getID(),
|
||||
},
|
||||
});
|
||||
},
|
||||
[navigate, wallet, walletID],
|
||||
);
|
||||
|
||||
const navigateToViewEditCosigners = () => {
|
||||
const navigateToViewEditCosigners = useCallback(() => {
|
||||
navigate('ViewEditMultisigCosignersRoot', {
|
||||
screen: 'ViewEditMultisigCosigners',
|
||||
params: {
|
||||
walletID,
|
||||
},
|
||||
});
|
||||
};
|
||||
}, [navigate, walletID]);
|
||||
|
||||
const onManageFundsPressed = (id?: string) => {
|
||||
if (id === actionKeys.Refill) {
|
||||
const availableWallets = wallets.filter(item => item.chain === Chain.ONCHAIN && item.allowSend());
|
||||
if (availableWallets.length === 0) {
|
||||
presentAlert({ message: loc.lnd.refill_create });
|
||||
} else {
|
||||
selectWallet(navigate, name, Chain.ONCHAIN).then(onWalletSelect);
|
||||
const onManageFundsPressed = useCallback(
|
||||
(id?: string) => {
|
||||
if (id === actionKeys.Refill) {
|
||||
const availableWallets = wallets.filter(item => item.chain === Chain.ONCHAIN && item.allowSend());
|
||||
if (availableWallets.length === 0) {
|
||||
presentAlert({ message: loc.lnd.refill_create });
|
||||
} else {
|
||||
selectWallet(navigate, name, Chain.ONCHAIN).then(onWalletSelect);
|
||||
}
|
||||
} else if (id === actionKeys.RefillWithExternalWallet) {
|
||||
navigate('ReceiveDetailsRoot', {
|
||||
screen: 'ReceiveDetails',
|
||||
params: {
|
||||
walletID,
|
||||
},
|
||||
});
|
||||
}
|
||||
} else if (id === actionKeys.RefillWithExternalWallet) {
|
||||
navigate('ReceiveDetailsRoot', {
|
||||
screen: 'ReceiveDetails',
|
||||
params: {
|
||||
walletID,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
},
|
||||
[name, navigate, onWalletSelect, walletID, wallets],
|
||||
);
|
||||
|
||||
const getItemLayout = (_: any, index: number) => ({
|
||||
length: 64,
|
||||
|
@ -282,8 +263,17 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
|||
index,
|
||||
});
|
||||
|
||||
const renderItem = (item: { item: Transaction }) => (
|
||||
<TransactionListItem item={item.item} itemPriceUnit={wallet?.preferredBalanceUnit} walletID={walletID} />
|
||||
const listData: Transaction[] = useMemo(() => {
|
||||
const transactions = getTransactions(limit);
|
||||
return transactions;
|
||||
}, [getTransactions, limit]);
|
||||
|
||||
const renderItem = useCallback(
|
||||
// eslint-disable-next-line react/no-unused-prop-types
|
||||
({ item }: { item: Transaction }) => {
|
||||
return <TransactionListItem item={item} itemPriceUnit={wallet?.preferredBalanceUnit} walletID={walletID} />;
|
||||
},
|
||||
[wallet, walletID],
|
||||
);
|
||||
|
||||
const choosePhoto = () => {
|
||||
|
@ -386,92 +376,159 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
|||
}, [refreshTransactions, setReloadTransactionsMenuActionFunction]),
|
||||
);
|
||||
|
||||
const refreshProps = isDesktop || isElectrumDisabled ? {} : { refreshing: isLoading, onRefresh: refreshTransactions };
|
||||
const [balance, setBalance] = useState(wallet ? wallet.getBalance() : 0);
|
||||
useEffect(() => {
|
||||
if (!wallet) return;
|
||||
const interval = setInterval(() => setBalance(wallet.getBalance()), 1000);
|
||||
return () => clearInterval(interval);
|
||||
}, [wallet]);
|
||||
|
||||
return (
|
||||
<View style={styles.flex}>
|
||||
{wallet && (
|
||||
<TransactionsNavigationHeader
|
||||
wallet={wallet}
|
||||
onWalletUnitChange={async selectedUnit => {
|
||||
wallet.preferredBalanceUnit = selectedUnit;
|
||||
await saveToDisk();
|
||||
}}
|
||||
unit={wallet.preferredBalanceUnit}
|
||||
onWalletBalanceVisibilityChange={async isShouldBeVisible => {
|
||||
const isBiometricsEnabled = await isBiometricUseCapableAndEnabled();
|
||||
if (wallet?.hideBalance && isBiometricsEnabled) {
|
||||
const unlocked = await unlockWithBiometrics();
|
||||
if (!unlocked) throw new Error('Biometrics failed');
|
||||
}
|
||||
wallet!.hideBalance = isShouldBeVisible;
|
||||
await saveToDisk();
|
||||
}}
|
||||
onManageFundsPressed={id => {
|
||||
if (wallet?.type === MultisigHDWallet.type) {
|
||||
navigateToViewEditCosigners();
|
||||
} else if (wallet?.type === LightningCustodianWallet.type) {
|
||||
if (wallet.getUserHasSavedExport()) {
|
||||
if (!id) return;
|
||||
onManageFundsPressed(id);
|
||||
} else {
|
||||
presentWalletExportReminder()
|
||||
.then(async () => {
|
||||
if (!id) return;
|
||||
wallet!.setUserHasSavedExport(true);
|
||||
await saveToDisk();
|
||||
onManageFundsPressed(id);
|
||||
})
|
||||
.catch(() => {
|
||||
navigate('WalletExportRoot', {
|
||||
screen: 'WalletExport',
|
||||
params: {
|
||||
walletID,
|
||||
},
|
||||
});
|
||||
});
|
||||
const walletBalance = useMemo(() => {
|
||||
if (!wallet) return '';
|
||||
if (wallet.hideBalance) return '';
|
||||
if (isNaN(balance) || balance === 0) return '';
|
||||
return formatBalance(balance, wallet.preferredBalanceUnit, true);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [wallet, wallet?.hideBalance, wallet?.preferredBalanceUnit, balance]);
|
||||
|
||||
const handleScroll = useCallback(
|
||||
(event: any) => {
|
||||
const offsetY = event.nativeEvent.contentOffset.y;
|
||||
const combinedHeight = 180;
|
||||
if (offsetY < combinedHeight) {
|
||||
setOptions({ ...getWalletTransactionsOptions({ route }), headerTitle: undefined });
|
||||
} else {
|
||||
navigation.setOptions({
|
||||
headerTitle: wallet ? `${wallet.getLabel()} ${walletBalance}` : '',
|
||||
});
|
||||
}
|
||||
},
|
||||
[navigation, wallet, walletBalance, setOptions, route],
|
||||
);
|
||||
|
||||
const ListHeaderComponent = useCallback(
|
||||
() =>
|
||||
wallet ? (
|
||||
<>
|
||||
<TransactionsNavigationHeader
|
||||
wallet={wallet}
|
||||
onWalletUnitChange={async selectedUnit => {
|
||||
wallet.preferredBalanceUnit = selectedUnit;
|
||||
await saveToDisk();
|
||||
}}
|
||||
unit={wallet.preferredBalanceUnit}
|
||||
onWalletBalanceVisibilityChange={async isShouldBeVisible => {
|
||||
const isBiometricsEnabled = await isBiometricUseCapableAndEnabled();
|
||||
if (wallet.hideBalance && isBiometricsEnabled) {
|
||||
const unlocked = await unlockWithBiometrics();
|
||||
if (!unlocked) throw new Error('Biometrics failed');
|
||||
}
|
||||
wallet.hideBalance = isShouldBeVisible;
|
||||
await saveToDisk();
|
||||
}}
|
||||
onManageFundsPressed={id => {
|
||||
if (wallet.type === MultisigHDWallet.type) {
|
||||
navigateToViewEditCosigners();
|
||||
} else if (wallet.type === LightningCustodianWallet.type) {
|
||||
if (wallet.getUserHasSavedExport()) {
|
||||
if (!id) return;
|
||||
onManageFundsPressed(id);
|
||||
} else {
|
||||
presentWalletExportReminder()
|
||||
.then(async () => {
|
||||
if (!id) return;
|
||||
wallet.setUserHasSavedExport(true);
|
||||
await saveToDisk();
|
||||
onManageFundsPressed(id);
|
||||
})
|
||||
.catch(() => {
|
||||
navigate('WalletExportRoot', {
|
||||
screen: 'WalletExport',
|
||||
params: {
|
||||
walletID,
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<View style={[styles.list, stylesHook.list]}>
|
||||
{wallet?.type === WatchOnlyWallet.type && wallet.isWatchOnlyWarningVisible && (
|
||||
<WatchOnlyWarning
|
||||
handleDismiss={() => {
|
||||
wallet.isWatchOnlyWarningVisible = false;
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.linear);
|
||||
saveToDisk();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<FlatList
|
||||
getItemLayout={getItemLayout}
|
||||
updateCellsBatchingPeriod={30}
|
||||
ListHeaderComponent={renderListHeaderComponent}
|
||||
onEndReachedThreshold={0.3}
|
||||
onEndReached={loadMoreTransactions}
|
||||
ListFooterComponent={renderListFooterComponent}
|
||||
ListEmptyComponent={
|
||||
<ScrollView style={styles.flex} contentContainerStyle={styles.scrollViewContent}>
|
||||
<Text numberOfLines={0} style={styles.emptyTxs}>
|
||||
{(isLightning() && loc.wallets.list_empty_txs1_lightning) || loc.wallets.list_empty_txs1}
|
||||
</Text>
|
||||
{isLightning() && <Text style={styles.emptyTxsLightning}>{loc.wallets.list_empty_txs2_lightning}</Text>}
|
||||
</ScrollView>
|
||||
}
|
||||
{...refreshProps}
|
||||
data={getTransactions(limit)}
|
||||
extraData={wallet}
|
||||
keyExtractor={_keyExtractor}
|
||||
renderItem={renderItem}
|
||||
initialNumToRender={10}
|
||||
removeClippedSubviews
|
||||
contentInset={{ top: 0, left: 0, bottom: 90, right: 0 }}
|
||||
maxToRenderPerBatch={15}
|
||||
windowSize={25}
|
||||
/>
|
||||
</View>
|
||||
<>
|
||||
<View style={[styles.flex, { backgroundColor: colors.background }]}>
|
||||
<View style={styles.listHeaderTextRow}>
|
||||
<Text style={[styles.listHeaderText, stylesHook.listHeaderText]}>{loc.transactions.list_title}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={{ backgroundColor: colors.background }}>
|
||||
{wallet.type === WatchOnlyWallet.type && wallet.isWatchOnlyWarningVisible && (
|
||||
<WatchOnlyWarning
|
||||
handleDismiss={() => {
|
||||
wallet.isWatchOnlyWarningVisible = false;
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.linear);
|
||||
saveToDisk();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</>
|
||||
</>
|
||||
) : undefined,
|
||||
[
|
||||
wallet,
|
||||
colors.background,
|
||||
stylesHook.listHeaderText,
|
||||
saveToDisk,
|
||||
isBiometricUseCapableAndEnabled,
|
||||
navigateToViewEditCosigners,
|
||||
onManageFundsPressed,
|
||||
navigate,
|
||||
walletID,
|
||||
],
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={[styles.flex, { backgroundColor: colors.background }]}>
|
||||
{/* The color of the refresh indicator. Temporary hack */}
|
||||
<View
|
||||
style={[
|
||||
styles.refreshIndicatorBackground,
|
||||
{ backgroundColor: wallet ? WalletGradient.headerColorFor(wallet.type) : colors.background },
|
||||
]}
|
||||
/>
|
||||
|
||||
<FlatList<Transaction>
|
||||
getItemLayout={getItemLayout}
|
||||
updateCellsBatchingPeriod={30}
|
||||
onEndReachedThreshold={0.3}
|
||||
onEndReached={loadMoreTransactions}
|
||||
ListFooterComponent={renderListFooterComponent}
|
||||
data={listData}
|
||||
extraData={wallet}
|
||||
keyExtractor={_keyExtractor}
|
||||
renderItem={renderItem}
|
||||
initialNumToRender={10}
|
||||
removeClippedSubviews
|
||||
contentContainerStyle={{ backgroundColor: colors.background }}
|
||||
contentInset={{ top: 0, left: 0, bottom: 90, right: 0 }}
|
||||
maxToRenderPerBatch={15}
|
||||
onScroll={handleScroll}
|
||||
scrollEventThrottle={16}
|
||||
stickyHeaderHiddenOnScroll
|
||||
ListHeaderComponent={ListHeaderComponent}
|
||||
ListEmptyComponent={
|
||||
<ScrollView style={[styles.flex, { backgroundColor: colors.background }]} contentContainerStyle={styles.scrollViewContent}>
|
||||
<Text numberOfLines={0} style={styles.emptyTxs}>
|
||||
{(isLightning() && loc.wallets.list_empty_txs1_lightning) || loc.wallets.list_empty_txs1}
|
||||
</Text>
|
||||
{isLightning() && <Text style={styles.emptyTxsLightning}>{loc.wallets.list_empty_txs2_lightning}</Text>}
|
||||
</ScrollView>
|
||||
}
|
||||
refreshControl={
|
||||
!isDesktop && !isElectrumDisabled ? (
|
||||
<RefreshControl refreshing={isLoading} onRefresh={refreshTransactions} tintColor={colors.msSuccessCheck} />
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
<FContainer ref={walletActionButtonsRef}>
|
||||
{wallet?.allowReceive() && (
|
||||
<FButton
|
||||
|
@ -520,11 +577,17 @@ export default WalletTransactions;
|
|||
|
||||
const styles = StyleSheet.create({
|
||||
flex: { flex: 1 },
|
||||
scrollViewContent: { flex: 1, justifyContent: 'center', paddingHorizontal: 16, paddingBottom: 40 },
|
||||
scrollViewContent: { flex: 1, justifyContent: 'center', paddingHorizontal: 16, paddingBottom: 500 },
|
||||
activityIndicator: { marginVertical: 20 },
|
||||
listHeaderTextRow: { flex: 1, margin: 16, flexDirection: 'row', justifyContent: 'space-between' },
|
||||
listHeaderText: { marginTop: 8, marginBottom: 8, fontWeight: 'bold', fontSize: 24 },
|
||||
list: { flex: 1 },
|
||||
refreshIndicatorBackground: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: 140,
|
||||
},
|
||||
emptyTxs: { fontSize: 18, color: '#9aa0aa', textAlign: 'center', marginVertical: 16 },
|
||||
emptyTxsLightning: { fontSize: 18, color: '#9aa0aa', textAlign: 'center', fontWeight: '600' },
|
||||
sendIcon: { transform: [{ rotate: I18nManager.isRTL ? '-225deg' : '225deg' }] },
|
||||
|
|
|
@ -601,6 +601,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
|||
await waitFor(element(by.id('ItemSigned'))).toBeNotVisible(); // not a single green checkmark
|
||||
|
||||
await element(by.id('ProvideSignature')).tap();
|
||||
await element(by.id('PsbtMultisigQRCodeScrollView')).swipe('up', 'fast', 1); // in case emu screen is small and it doesnt fit
|
||||
await element(by.id('CosignedScanOrImportFile')).tap();
|
||||
|
||||
const ursSignedByPassport = [
|
||||
|
@ -624,6 +625,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
|||
await waitFor(element(by.id('ItemSigned'))).toBeVisible(); // one green checkmark visible
|
||||
|
||||
await element(by.id('ProvideSignature')).tap();
|
||||
await element(by.id('PsbtMultisigQRCodeScrollView')).swipe('up', 'fast', 1); // in case emu screen is small and it doesnt fit
|
||||
await element(by.id('CosignedScanOrImportFile')).tap();
|
||||
|
||||
const urSignedByPassportAndKeystone = [
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue