mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-22 15:04:50 +01:00
REF: resolved conflict
This commit is contained in:
commit
7288682b56
203 changed files with 83085 additions and 24127 deletions
3
.babelrc
3
.babelrc
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"presets": ["module:metro-react-native-babel-preset"]
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
|
||||
[android]
|
||||
target = Google Inc.:Google APIs:23
|
||||
|
||||
[download]
|
||||
max_number_of_retries = 3
|
||||
|
||||
[maven_repositories]
|
||||
central = https://repo1.maven.org/maven2
|
|
@ -3,7 +3,7 @@ jobs:
|
|||
|
||||
lint:
|
||||
docker:
|
||||
- image: cimg/node:16.17.1
|
||||
- image: cimg/node:16.20.0
|
||||
|
||||
working_directory: ~/repo
|
||||
|
||||
|
@ -26,7 +26,7 @@ jobs:
|
|||
|
||||
unit:
|
||||
docker:
|
||||
- image: cimg/node:16.17.1
|
||||
- image: cimg/node:16.20.0
|
||||
|
||||
working_directory: ~/repo
|
||||
|
||||
|
@ -50,10 +50,12 @@ jobs:
|
|||
|
||||
integration:
|
||||
docker:
|
||||
- image: cimg/node:16.17.1
|
||||
- image: cimg/node:16.20.0
|
||||
|
||||
working_directory: ~/repo
|
||||
|
||||
resource_class: large
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
{
|
||||
"testRunner": "jest",
|
||||
"runnerConfig": "tests/e2e/config.json",
|
||||
"skipLegacyWorkersInjection": true,
|
||||
"testRunner": {
|
||||
"$0": "jest",
|
||||
"args": {
|
||||
"config": "tests/e2e/jest.config.js",
|
||||
"_": ["e2e"]
|
||||
}
|
||||
},
|
||||
"apps": {
|
||||
"ios": {
|
||||
"type": "ios.app",
|
||||
|
|
|
@ -68,5 +68,10 @@
|
|||
"env": {
|
||||
"es6": true
|
||||
},
|
||||
"globals": { "fetch": false }
|
||||
"globals": { "fetch": false },
|
||||
"settings": {
|
||||
"react": { // this is for eslint-plugin-react
|
||||
"version": "detect" // React version. "detect" automatically picks the version you have installed.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
2
.github/workflows/build-release-apk.yml
vendored
2
.github/workflows/build-release-apk.yml
vendored
|
@ -30,7 +30,7 @@ jobs:
|
|||
cache: 'gradle'
|
||||
|
||||
- name: Install node_modules
|
||||
run: npm install
|
||||
run: npm install --production
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
|
|
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
|
@ -39,6 +39,7 @@ jobs:
|
|||
- name: Run tests
|
||||
run: npm test || npm test || npm test
|
||||
env:
|
||||
BIP47_HD_MNEMONIC: ${{ secrets.BIP47_HD_MNEMONIC}}
|
||||
HD_MNEMONIC: ${{ secrets.HD_MNEMONIC }}
|
||||
HD_MNEMONIC_BIP49: ${{ secrets.HD_MNEMONIC_BIP49 }}
|
||||
HD_MNEMONIC_BIP49_MANY_TX: ${{ secrets.HD_MNEMONIC_BIP49_MANY_TX }}
|
||||
|
@ -94,7 +95,7 @@ jobs:
|
|||
avd-name: Pixel_API_29_AOSP
|
||||
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-back none -camera-front none -partition-size 2047
|
||||
arch: x86_64
|
||||
script: npm run e2e:release-test || npm run e2e:release-test || npm run e2e:release-test
|
||||
script: npm run e2e:release-test || npm run e2e:release-test || npm run e2e:release-test || npm run e2e:release-test
|
||||
env:
|
||||
TRAVIS: 1
|
||||
HD_MNEMONIC: ${{ secrets.HD_MNEMONIC }}
|
||||
|
|
11
.gitignore
vendored
11
.gitignore
vendored
|
@ -20,7 +20,11 @@ DerivedData
|
|||
*.hmap
|
||||
*.ipa
|
||||
*.xcuserstate
|
||||
ios/.xcode.env.local
|
||||
*.hprof
|
||||
.cxx/
|
||||
*.keystore
|
||||
!debug.keystore
|
||||
|
||||
# Android/IntelliJ
|
||||
#
|
||||
|
@ -52,6 +56,7 @@ buck-out/
|
|||
*/fastlane/report.xml
|
||||
*/fastlane/Preview.html
|
||||
*/fastlane/screenshots
|
||||
**/fastlane/test_output
|
||||
|
||||
# Bundle artifact
|
||||
*.jsbundle
|
||||
|
@ -61,8 +66,12 @@ release-notes.json
|
|||
release-notes.txt
|
||||
current-branch.json
|
||||
|
||||
ios/Pods/
|
||||
# Ruby / CocoaPods
|
||||
/ios/Pods/
|
||||
/vendor/bundle/
|
||||
|
||||
# Temporary files created by Metro to check the health of the file watcher
|
||||
.metro-health-check*
|
||||
artifacts/
|
||||
|
||||
# Editors
|
||||
|
|
|
@ -1 +1 @@
|
|||
2.7.4
|
||||
2.7.6
|
||||
|
|
47
.tx/config
47
.tx/config
|
@ -1,42 +1,43 @@
|
|||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
[bluewallet.loc-en-json--master]
|
||||
file_filter = loc/<lang>.json
|
||||
minimum_perc = 30
|
||||
source_file = loc/en.json
|
||||
source_lang = en
|
||||
type = KEYVALUEJSON
|
||||
lang_map = af_ZA: zar_afr, bg_BG: bg_bg, ca: ca, cs_CZ: cs_cz, cy: cy, da_DK: da_dk, de_DE: de_de, el: el, es_ES: es, fa_IR: fa, fi_FI: fi_fi, fr_FR: fr_fr, hr_HR: hr_hr, hu_HU: hu_hu, id_ID: id_id, ja_JP: jp_jp, nb_NO: nb_no, nl_NL: nl_nl, pt_BR: pt_br, pt_PT: pt_pt, ro: ro, sk_SK: sk_sk, sv_SE: sv_se, th_TH: th_th, tr_TR: tr_tr, uk_UA: ua, vi_VN: vi_vn, xh: zar_xho, zh_CN: zh_cn, zh_TW: zh_tw
|
||||
|
||||
[bluewallet-fastlane.ios-fastlane-metadata-en-us-description-txt--master]
|
||||
[o:bluewallet:p:bluewallet-fastlane:r:ios-fastlane-metadata-en-us-description-txt--master]
|
||||
file_filter = ios/fastlane/metadata/<lang>/description.txt
|
||||
minimum_perc = 10
|
||||
source_file = ios/fastlane/metadata/en-US/description.txt
|
||||
source_lang = en_US
|
||||
type = TXT
|
||||
lang_map = ar_SA: ar-SA, de_DE: de-DE, es_ES: es-ES, es_MX: es-MX, fr_CA: fr-CA, fr_FR: fr-FR, nl_NL: nl-NL, pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-Hans, zh_HK: zh-Hant
|
||||
|
||||
[bluewallet-fastlane.ios-fastlane-metadata-en-us-keywords-txt--master]
|
||||
file_filter = ios/fastlane/metadata/<lang>/keywords.txt
|
||||
minimum_perc = 10
|
||||
lang_map = fr_FR: fr-FR, nl_NL: nl-NL, pt_BR: pt-BR, zh_CN: zh-Hans, zh_HK: zh-Hant, ar_SA: ar-SA, es_MX: es-MX, fr_CA: fr-CA, pt_PT: pt-PT, de_DE: de-DE, es_ES: es-ES
|
||||
|
||||
[o:bluewallet:p:bluewallet-fastlane:r:ios-fastlane-metadata-en-us-keywords-txt--master]
|
||||
file_filter = ios/fastlane/metadata/<lang>/keywords.txt
|
||||
source_file = ios/fastlane/metadata/en-US/keywords.txt
|
||||
source_lang = en_US
|
||||
type = TXT
|
||||
lang_map = ar_SA: ar-SA, de_DE: de-DE, es_ES: es-ES, es_MX: es-MX, fr_CA: fr-CA, fr_FR: fr-FR, nl_NL: nl-NL, pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-Hans, zh_HK: zh-Hant
|
||||
|
||||
[bluewallet-fastlane.ios-fastlane-metadata-en-us-name-txt--master]
|
||||
file_filter = ios/fastlane/metadata/<lang>/name.txt
|
||||
minimum_perc = 10
|
||||
lang_map = es_ES: es-ES, es_MX: es-MX, fr_CA: fr-CA, fr_FR: fr-FR, nl_NL: nl-NL, pt_BR: pt-BR, zh_CN: zh-Hans, ar_SA: ar-SA, de_DE: de-DE, pt_PT: pt-PT, zh_HK: zh-Hant
|
||||
|
||||
[o:bluewallet:p:bluewallet-fastlane:r:ios-fastlane-metadata-en-us-name-txt--master]
|
||||
file_filter = ios/fastlane/metadata/<lang>/name.txt
|
||||
source_file = ios/fastlane/metadata/en-US/name.txt
|
||||
source_lang = en_US
|
||||
type = TXT
|
||||
lang_map = ar_SA: ar-SA, de_DE: de-DE, es_ES: es-ES, es_MX: es-MX, fr_CA: fr-CA, fr_FR: fr-FR, nl_NL: nl-NL, pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-Hans, zh_HK: zh-Hant
|
||||
|
||||
[bluewallet-fastlane.ios-fastlane-metadata-en-us-promotional-text-txt--master]
|
||||
file_filter = ios/fastlane/metadata/<lang>/promotional_text.txt
|
||||
minimum_perc = 10
|
||||
lang_map = zh_HK: zh-Hant, ar_SA: ar-SA, es_MX: es-MX, fr_FR: fr-FR, nl_NL: nl-NL, pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-Hans, de_DE: de-DE, es_ES: es-ES, fr_CA: fr-CA
|
||||
|
||||
[o:bluewallet:p:bluewallet-fastlane:r:ios-fastlane-metadata-en-us-promotional-text-txt--master]
|
||||
file_filter = ios/fastlane/metadata/<lang>/promotional_text.txt
|
||||
source_file = ios/fastlane/metadata/en-US/promotional_text.txt
|
||||
source_lang = en_US
|
||||
type = TXT
|
||||
lang_map = ar_SA: ar-SA, de_DE: de-DE, es_ES: es-ES, es_MX: es-MX, fr_CA: fr-CA, fr_FR: fr-FR, nl_NL: nl-NL, pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-Hans, zh_HK: zh-Hant
|
||||
minimum_perc = 10
|
||||
lang_map = zh_CN: zh-Hans, zh_HK: zh-Hant, ar_SA: ar-SA, es_MX: es-MX, fr_CA: fr-CA, fr_FR: fr-FR, pt_PT: pt-PT, de_DE: de-DE, es_ES: es-ES, nl_NL: nl-NL, pt_BR: pt-BR
|
||||
|
||||
[o:bluewallet:p:bluewallet:r:loc-en-json--master]
|
||||
file_filter = loc/<lang>.json
|
||||
source_file = loc/en.json
|
||||
source_lang = en
|
||||
type = KEYVALUEJSON
|
||||
minimum_perc = 30
|
||||
lang_map = vi_VN: vi_vn, zh_TW: zh_tw, af_ZA: zar_afr, id_ID: id_id, sk_SK: sk_sk, ja_JP: jp_jp, uk_UA: ua, zh_CN: zh_cn, hr_HR: hr_hr, tr_TR: tr_tr, sv_SE: sv_se, th_TH: th_th, pt_BR: pt_br, es_ES: es, fr_FR: fr_fr, hu_HU: hu_hu, nl_NL: nl_nl, ro: ro, ca: ca, cy: cy, de_DE: de_de, nb_NO: nb_no, pt_PT: pt_pt, xh: zar_xho, bg_BG: bg_bg, cs_CZ: cs_cz, da_DK: da_dk, el: el, fa_IR: fa, fi_FI: fi_fi
|
||||
|
||||
|
|
13
.xcode-env
Normal file
13
.xcode-env
Normal file
|
@ -0,0 +1,13 @@
|
|||
# This `.xcode.env` file is versioned and is used to source the
|
||||
environment
|
||||
# used when running script phases inside Xcode.
|
||||
# To customize your local environment, you can create an
|
||||
`.xcode.env.local`
|
||||
# file that is not versioned.
|
||||
|
||||
# NODE_BINARY variable contains the PATH to the node executable.
|
||||
#
|
||||
# Customize the NODE_BINARY variable here.
|
||||
# For example, to use nvm with brew, add the following line
|
||||
# . "$(brew --prefix nvm)/nvm.sh" --no-use
|
||||
export NODE_BINARY=$(command -v node)
|
6
App.js
6
App.js
|
@ -291,7 +291,7 @@ const App = () => {
|
|||
currency.updateExchangeRate();
|
||||
const processed = await processPushNotifications();
|
||||
if (processed) return;
|
||||
const clipboard = await BlueClipboard.getClipboardContent();
|
||||
const clipboard = await BlueClipboard().getClipboardContent();
|
||||
const isAddressFromStoredWallet = wallets.some(wallet => {
|
||||
if (wallet.chain === Chain.ONCHAIN) {
|
||||
// checking address validity is faster than unwrapping hierarchy only to compare it to garbage
|
||||
|
@ -332,7 +332,9 @@ const App = () => {
|
|||
|
||||
const showClipboardAlert = ({ contentType }) => {
|
||||
ReactNativeHapticFeedback.trigger('impactLight', { ignoreAndroidSystemSettings: false });
|
||||
BlueClipboard.getClipboardContent().then(clipboard => {
|
||||
BlueClipboard()
|
||||
.getClipboardContent()
|
||||
.then(clipboard => {
|
||||
if (Platform.OS === 'ios' || Platform.OS === 'macos') {
|
||||
ActionSheet.showActionSheetWithOptions(
|
||||
{
|
||||
|
|
|
@ -53,7 +53,7 @@ export const BlueButton = props => {
|
|||
style={{
|
||||
borderWidth: 0.7,
|
||||
borderColor: 'transparent',
|
||||
backgroundColor: backgroundColor,
|
||||
backgroundColor,
|
||||
minHeight: 45,
|
||||
height: 45,
|
||||
maxHeight: 45,
|
||||
|
@ -89,7 +89,7 @@ export const SecondButton = forwardRef((props, ref) => {
|
|||
style={{
|
||||
borderWidth: 0.7,
|
||||
borderColor: 'transparent',
|
||||
backgroundColor: backgroundColor,
|
||||
backgroundColor,
|
||||
minHeight: 45,
|
||||
height: 45,
|
||||
maxHeight: 45,
|
||||
|
@ -559,7 +559,12 @@ export const BlueHeaderDefaultMain = props => {
|
|||
>
|
||||
{props.leftText}
|
||||
</Text>
|
||||
<PlusIcon onPress={props.onNewWalletPress} Component={TouchableOpacity} />
|
||||
<PlusIcon
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc.wallets.add_title}
|
||||
onPress={props.onNewWalletPress}
|
||||
Component={TouchableOpacity}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
|
4
Gemfile
4
Gemfile
|
@ -1,6 +1,6 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
|
||||
ruby '2.7.4'
|
||||
ruby '>= 2.6.10'
|
||||
|
||||
gem 'cocoapods', '~> 1.11', '>= 1.11.2'
|
||||
gem 'cocoapods', '~> 1.11', '>= 1.11.3'
|
|
@ -83,6 +83,9 @@ import { isDesktop, isTablet, isHandset } from './blue_modules/environment';
|
|||
import SettingsPrivacy from './screen/settings/SettingsPrivacy';
|
||||
import LNDViewAdditionalInvoicePreImage from './screen/lnd/lndViewAdditionalInvoicePreImage';
|
||||
import LdkViewLogs from './screen/wallets/ldkViewLogs';
|
||||
import PaymentCode from './screen/wallets/paymentCode';
|
||||
import PaymentCodesList from './screen/wallets/paymentCodesList';
|
||||
import loc from './loc';
|
||||
|
||||
const WalletsStack = createNativeStackNavigator();
|
||||
|
||||
|
@ -465,6 +468,20 @@ const ExportMultisigCoordinationSetupRoot = () => {
|
|||
);
|
||||
};
|
||||
|
||||
const PaymentCodeStack = createNativeStackNavigator();
|
||||
const PaymentCodeStackRoot = () => {
|
||||
return (
|
||||
<PaymentCodeStack.Navigator name="PaymentCodeRoot" screenOptions={{ headerHideShadow: true }} initialRouteName="PaymentCode">
|
||||
<PaymentCodeStack.Screen name="PaymentCode" component={PaymentCode} options={{ headerTitle: loc.bip47.payment_code }} />
|
||||
<PaymentCodeStack.Screen
|
||||
name="PaymentCodesList"
|
||||
component={PaymentCodesList}
|
||||
options={{ headerTitle: loc.bip47.payment_codes_list }}
|
||||
/>
|
||||
</PaymentCodeStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
const RootStack = createNativeStackNavigator();
|
||||
const NavigationDefaultOptions = { headerShown: false, stackPresentation: isDesktop ? 'containedModal' : 'modal' };
|
||||
const Navigation = () => {
|
||||
|
@ -500,6 +517,8 @@ const Navigation = () => {
|
|||
stackPresentation: isDesktop ? 'containedModal' : 'fullScreenModal',
|
||||
}}
|
||||
/>
|
||||
|
||||
<RootStack.Screen name="PaymentCodeRoot" component={PaymentCodeStackRoot} options={NavigationDefaultOptions} />
|
||||
</RootStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -83,8 +83,7 @@ npx react-native run-ios
|
|||
npm run maccatalystpatches
|
||||
```
|
||||
|
||||
Once the patches are applied, open Xcode and select "My Mac" as destination. If you are running macOS Catalina, you may need to remove all iOS 14 Widget targets.
|
||||
|
||||
Once the patches are applied, open Xcode and select "My Mac" as destination.
|
||||
|
||||
## TESTS
|
||||
|
||||
|
@ -103,7 +102,7 @@ Grab an issue from [the backlog](https://github.com/BlueWallet/BlueWallet/projec
|
|||
|
||||
## Translations
|
||||
|
||||
We accepts translations via [Transifex](https://www.transifex.com/bluewallet/bluewallet/)
|
||||
We accept translations via [Transifex](https://www.transifex.com/bluewallet/bluewallet/)
|
||||
|
||||
To participate you need to:
|
||||
1. Sign up to Transifex
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
watchEvents,
|
||||
useReachability,
|
||||
useInstalled,
|
||||
usePaired,
|
||||
transferCurrentComplicationUserInfo,
|
||||
} from 'react-native-watch-connectivity';
|
||||
import { Chain } from './models/bitcoinUnits';
|
||||
|
@ -17,13 +18,14 @@ function WatchConnectivity() {
|
|||
const { walletsInitialized, wallets, fetchWalletTransactions, saveToDisk, txMetadata, preferredFiatCurrency } =
|
||||
useContext(BlueStorageContext);
|
||||
const isReachable = useReachability();
|
||||
const isPaired = usePaired();
|
||||
const isInstalled = useInstalled(); // true | false
|
||||
const messagesListenerActive = useRef(false);
|
||||
const lastPreferredCurrency = useRef(FiatUnit.USD.endPointKey);
|
||||
|
||||
useEffect(() => {
|
||||
let messagesListener = () => {};
|
||||
if (isInstalled && isReachable && walletsInitialized && messagesListenerActive.current === false) {
|
||||
if (isPaired && isInstalled && isReachable && walletsInitialized && messagesListenerActive.current === false) {
|
||||
messagesListener = watchEvents.addListener('message', handleMessages);
|
||||
messagesListenerActive.current = true;
|
||||
} else {
|
||||
|
@ -35,14 +37,14 @@ function WatchConnectivity() {
|
|||
messagesListenerActive.current = false;
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [walletsInitialized, isReachable, isInstalled]);
|
||||
}, [walletsInitialized, isPaired, isReachable, isInstalled]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInstalled && isReachable && walletsInitialized) {
|
||||
if (isPaired && isInstalled && isReachable && walletsInitialized) {
|
||||
sendWalletsToWatch();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [walletsInitialized, wallets, isReachable, isInstalled]);
|
||||
}, [walletsInitialized, wallets, isPaired, isReachable, isInstalled]);
|
||||
|
||||
useEffect(() => {
|
||||
updateApplicationContext({ isWalletsInitialized: walletsInitialized, randomID: Math.floor(Math.random() * 11) });
|
||||
|
@ -208,7 +210,7 @@ function WatchConnectivity() {
|
|||
balance: formatBalance(Number(wallet.getBalance()), wallet.getPreferredBalanceUnit(), true),
|
||||
type: wallet.type,
|
||||
preferredBalanceUnit: wallet.getPreferredBalanceUnit(),
|
||||
receiveAddress: receiveAddress,
|
||||
receiveAddress,
|
||||
transactions: watchTransactions,
|
||||
hideBalance: wallet.hideBalance,
|
||||
};
|
||||
|
|
|
@ -1,106 +1,73 @@
|
|||
apply plugin: "com.android.application"
|
||||
apply plugin: "kotlin-android"
|
||||
apply plugin: "com.facebook.react"
|
||||
|
||||
import com.android.build.OutputFile
|
||||
|
||||
/**
|
||||
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
|
||||
* and bundleReleaseJsAndAssets).
|
||||
* These basically call `react-native bundle` with the correct arguments during the Android build
|
||||
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
|
||||
* bundle directly from the development server. Below you can see all the possible configurations
|
||||
* and their defaults. If you decide to add a configuration block, make sure to add it before the
|
||||
* `apply from: "../../node_modules/react-native/react.gradle"` line.
|
||||
*
|
||||
* project.ext.react = [
|
||||
* // the name of the generated asset file containing your JS bundle
|
||||
* bundleAssetName: "index.android.bundle",
|
||||
*
|
||||
* // the entry file for bundle generation. If none specified and
|
||||
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
|
||||
* // default. Can be overridden with ENTRY_FILE environment variable.
|
||||
* entryFile: "index.android.js",
|
||||
*
|
||||
* // https://reactnative.dev/docs/performance#enable-the-ram-format
|
||||
* bundleCommand: "ram-bundle",
|
||||
*
|
||||
* // whether to bundle JS and assets in debug mode
|
||||
* bundleInDebug: false,
|
||||
*
|
||||
* // whether to bundle JS and assets in release mode
|
||||
* bundleInRelease: true,
|
||||
*
|
||||
* // whether to bundle JS and assets in another build variant (if configured).
|
||||
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
|
||||
* // The configuration property can be in the following formats
|
||||
* // 'bundleIn${productFlavor}${buildType}'
|
||||
* // 'bundleIn${buildType}'
|
||||
* // bundleInFreeDebug: true,
|
||||
* // bundleInPaidRelease: true,
|
||||
* // bundleInBeta: true,
|
||||
*
|
||||
* // whether to disable dev mode in custom build variants (by default only disabled in release)
|
||||
* // for example: to disable dev mode in the staging build type (if configured)
|
||||
* devDisabledInStaging: true,
|
||||
* // The configuration property can be in the following formats
|
||||
* // 'devDisabledIn${productFlavor}${buildType}'
|
||||
* // 'devDisabledIn${buildType}'
|
||||
*
|
||||
* // the root of your project, i.e. where "package.json" lives
|
||||
* root: "../../",
|
||||
*
|
||||
* // where to put the JS bundle asset in debug mode
|
||||
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
|
||||
*
|
||||
* // where to put the JS bundle asset in release mode
|
||||
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in debug mode
|
||||
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in release mode
|
||||
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
|
||||
*
|
||||
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
|
||||
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
|
||||
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
|
||||
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
|
||||
* // for example, you might want to remove it from here.
|
||||
* inputExcludes: ["android/**", "ios/**"],
|
||||
*
|
||||
* // override which node gets called and with what additional arguments
|
||||
* nodeExecutableAndArgs: ["node"],
|
||||
*
|
||||
* // supply additional arguments to the packager
|
||||
* extraPackagerArgs: []
|
||||
* ]
|
||||
* This is the configuration block to customize your React Native Android app.
|
||||
* By default you don't need to apply any configuration, just uncomment the lines you need.
|
||||
*/
|
||||
react {
|
||||
/* Folders */
|
||||
// The root of your project, i.e. where "package.json" lives. Default is '..'
|
||||
// root = file("../")
|
||||
// The folder where the react-native NPM package is. Default is ../node_modules/react-native
|
||||
// reactNativeDir = file("../node_modules/react-native")
|
||||
// The folder where the react-native Codegen package is. Default is ../node_modules/react-native-codegen
|
||||
// codegenDir = file("../node_modules/react-native-codegen")
|
||||
// The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
|
||||
// cliFile = file("../node_modules/react-native/cli.js")
|
||||
|
||||
project.ext.react = [
|
||||
enableHermes: true, // clean and rebuild if changing
|
||||
]
|
||||
/* Variants */
|
||||
// The list of variants to that are debuggable. For those we're going to
|
||||
// skip the bundling of the JS bundle and the assets. By default is just 'debug'.
|
||||
// If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
|
||||
// debuggableVariants = ["liteDebug", "prodDebug"]
|
||||
|
||||
apply from: "../../node_modules/react-native/react.gradle"
|
||||
apply plugin: "com.bugsnag.android.gradle"
|
||||
/* Bundling */
|
||||
// A list containing the node command and its flags. Default is just 'node'.
|
||||
// nodeExecutableAndArgs = ["node"]
|
||||
//
|
||||
// The command to run when bundling. By default is 'bundle'
|
||||
// bundleCommand = "ram-bundle"
|
||||
//
|
||||
// The path to the CLI configuration file. Default is empty.
|
||||
// bundleConfig = file(../rn-cli.config.js)
|
||||
//
|
||||
// The name of the generated asset file containing your JS bundle
|
||||
// bundleAssetName = "MyApplication.android.bundle"
|
||||
//
|
||||
// The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
|
||||
// entryFile = file("../js/MyApplication.android.js")
|
||||
//
|
||||
// A list of extra flags to pass to the 'bundle' commands.
|
||||
// See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
|
||||
// extraPackagerArgs = []
|
||||
|
||||
/* Hermes Commands */
|
||||
// The hermes compiler command to run. By default it is 'hermesc'
|
||||
// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
|
||||
//
|
||||
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
|
||||
// hermesFlags = ["-O", "-output-source-map"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this to true to create two separate APKs instead of one:
|
||||
* - An APK that only works on ARM devices
|
||||
* - An APK that only works on x86 devices
|
||||
* The advantage is the size of the APK is reduced by about 4MB.
|
||||
* Upload all the APKs to the Play Store and people will download
|
||||
* the correct one based on the CPU architecture of their device.
|
||||
* Set this to true to create four separate APKs instead of one,
|
||||
* one for each native architecture. This is useful if you don't
|
||||
* use App Bundles (https://developer.android.com/guide/app-bundle/)
|
||||
* and want to have separate APKs to upload to the Play Store.
|
||||
*/
|
||||
def enableSeparateBuildPerCPUArchitecture = false
|
||||
|
||||
/**
|
||||
* Run Proguard to shrink the Java bytecode in release builds.
|
||||
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
|
||||
*/
|
||||
def enableProguardInReleaseBuilds = false
|
||||
|
||||
/**
|
||||
* The preferred build flavor of JavaScriptCore.
|
||||
* The preferred build flavor of JavaScriptCore (JSC)
|
||||
*
|
||||
* For example, to use the international variant, you can use:
|
||||
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
|
||||
|
@ -113,61 +80,45 @@ def enableProguardInReleaseBuilds = false
|
|||
def jscFlavor = 'org.webkit:android-jsc-intl:+'
|
||||
|
||||
/**
|
||||
* Whether to enable the Hermes VM.
|
||||
*
|
||||
* This should be set on project.ext.react and mirrored here. If it is not set
|
||||
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
|
||||
* and the benefits of using Hermes will therefore be sharply reduced.
|
||||
* Private function to get the list of Native Architectures you want to build.
|
||||
* This reads the value from reactNativeArchitectures in your gradle.properties
|
||||
* file and works together with the --active-arch-only flag of react-native run-android.
|
||||
*/
|
||||
def enableHermes = project.ext.react.get("enableHermes", false);
|
||||
def reactNativeArchitectures() {
|
||||
def value = project.getProperties().get("reactNativeArchitectures")
|
||||
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
||||
}
|
||||
|
||||
android {
|
||||
ndkVersion rootProject.ext.ndkVersion
|
||||
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "io.bluewallet.bluewallet"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "6.3.2"
|
||||
multiDexEnabled true
|
||||
missingDimensionStrategy 'react-native-camera', 'general'
|
||||
testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type
|
||||
versionName "6.4.0"
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
ndk {
|
||||
abiFilters 'arm64-v8a', 'x86_64', 'x86', 'armeabi-v7a'
|
||||
}
|
||||
|
||||
testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
missingDimensionStrategy 'detox', 'full'
|
||||
}
|
||||
splits {
|
||||
abi {
|
||||
reset()
|
||||
enable enableSeparateBuildPerCPUArchitecture
|
||||
universalApk false // If true, also generate a universal APK
|
||||
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
include (*reactNativeArchitectures())
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// Caution! In production, you need to generate your own keystore file.
|
||||
// see https://reactnative.dev/docs/signed-apk-android.
|
||||
minifyEnabled enableProguardInReleaseBuilds
|
||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||
proguardFile "${rootProject.projectDir}/../node_modules/detox/android/detox/proguard-rules-app.pro"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,7 +131,8 @@ android {
|
|||
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
|
||||
def abi = output.getFilter(OutputFile.ABI)
|
||||
if (abi != null) { // null for the universal-debug, universal-release variants
|
||||
output.versionCodeOverride = versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
|
||||
output.versionCodeOverride =
|
||||
defaultConfig.versionCode * 1000 + versionCodes.get(abi)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -188,47 +140,20 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
// The version of react-native is set by the React Native Gradle Plugin
|
||||
implementation("com.facebook.react:react-android")
|
||||
implementation files("../../node_modules/rn-ldk/android/libs/LDK-release.aar")
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0")
|
||||
implementation files("../../node_modules/react-native-tor/android/libs/sifir_android.aar")
|
||||
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
|
||||
|
||||
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.fbjni'
|
||||
}
|
||||
|
||||
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.flipper'
|
||||
exclude group:'com.squareup.okhttp3', module:'okhttp'
|
||||
}
|
||||
|
||||
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.flipper'
|
||||
}
|
||||
|
||||
if (enableHermes) {
|
||||
def hermesPath = "../../node_modules/hermes-engine/android/";
|
||||
debugImplementation files(hermesPath + "hermes-debug.aar")
|
||||
releaseImplementation files(hermesPath + "hermes-release.aar")
|
||||
if (hermesEnabled.toBoolean()) {
|
||||
implementation("com.facebook.react:hermes-android")
|
||||
} else {
|
||||
implementation jscFlavor
|
||||
}
|
||||
|
||||
androidTestImplementation(project(path: ":detox"))
|
||||
androidTestImplementation('com.wix:detox:+')
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
}
|
||||
|
||||
// Run this once to be able to run the application with BUCK
|
||||
// puts all compile dependencies into folder libs for BUCK to use
|
||||
task copyDownloadableDepsToLibs(type: Copy) {
|
||||
from configurations.implementation
|
||||
into 'libs'
|
||||
}
|
||||
|
||||
apply plugin: 'com.google.gms.google-services' // Google Services plugin
|
||||
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
|
||||
bugsnag {
|
||||
uploadReactNativeMappings = true
|
||||
}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
"""Helper definitions to glob .aar and .jar targets"""
|
||||
|
||||
def create_aar_targets(aarfiles):
|
||||
for aarfile in aarfiles:
|
||||
name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
|
||||
lib_deps.append(":" + name)
|
||||
android_prebuilt_aar(
|
||||
name = name,
|
||||
aar = aarfile,
|
||||
)
|
||||
|
||||
def create_jar_targets(jarfiles):
|
||||
for jarfile in jarfiles:
|
||||
name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
|
||||
lib_deps.append(":" + name)
|
||||
prebuilt_jar(
|
||||
name = name,
|
||||
binary_jar = jarfile,
|
||||
)
|
|
@ -1,72 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
|
||||
* directory of this source tree.
|
||||
*/
|
||||
package io.bluewallet.bluewallet;
|
||||
|
||||
import android.content.Context;
|
||||
import com.facebook.flipper.android.AndroidFlipperClient;
|
||||
import com.facebook.flipper.android.utils.FlipperUtils;
|
||||
import com.facebook.flipper.core.FlipperClient;
|
||||
import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
|
||||
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
|
||||
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
|
||||
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.modules.network.NetworkingModule;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
public class ReactNativeFlipper {
|
||||
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
|
||||
if (FlipperUtils.shouldEnableFlipper(context)) {
|
||||
final FlipperClient client = AndroidFlipperClient.getInstance(context);
|
||||
|
||||
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
|
||||
client.addPlugin(new ReactFlipperPlugin());
|
||||
client.addPlugin(new DatabasesFlipperPlugin(context));
|
||||
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
|
||||
client.addPlugin(CrashReporterPlugin.getInstance());
|
||||
|
||||
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
|
||||
NetworkingModule.setCustomClientBuilder(
|
||||
new NetworkingModule.CustomClientBuilder() {
|
||||
@Override
|
||||
public void apply(OkHttpClient.Builder builder) {
|
||||
builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
|
||||
}
|
||||
});
|
||||
client.addPlugin(networkFlipperPlugin);
|
||||
client.start();
|
||||
|
||||
// Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
|
||||
// Hence we run if after all native modules have been initialized
|
||||
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
|
||||
if (reactContext == null) {
|
||||
reactInstanceManager.addReactInstanceEventListener(
|
||||
new ReactInstanceManager.ReactInstanceEventListener() {
|
||||
@Override
|
||||
public void onReactContextInitialized(ReactContext reactContext) {
|
||||
reactInstanceManager.removeReactInstanceEventListener(this);
|
||||
reactContext.runOnNativeModulesQueueThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
client.addPlugin(new FrescoFlipperPlugin());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
client.addPlugin(new FrescoFlipperPlugin());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:allowBackup="false"
|
||||
android:largeHeap="true"
|
||||
android:extractNativeLibs="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:usesCleartextTraffic="true"
|
||||
|
@ -38,7 +39,8 @@
|
|||
|
||||
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationActions" />
|
||||
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationPublisher" />
|
||||
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationBootEventReceiver">
|
||||
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationBootEventReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
|
@ -57,7 +59,8 @@
|
|||
android:label="@string/app_name"
|
||||
android:launchMode="singleInstance"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
@ -71,8 +74,6 @@
|
|||
<data android:scheme="bluewallet" />
|
||||
<data android:scheme="lapp" />
|
||||
<data android:scheme="blue" />
|
||||
<data android:scheme="bankid" />
|
||||
<data android:scheme="swish" />
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
</intent-filter>
|
||||
|
|
|
@ -8,6 +8,10 @@ import androidx.annotation.Nullable;
|
|||
|
||||
import com.facebook.react.ReactActivity;
|
||||
|
||||
import com.facebook.react.ReactActivityDelegate;
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
|
||||
import com.facebook.react.defaults.DefaultReactActivityDelegate;
|
||||
|
||||
public class MainActivity extends ReactActivity {
|
||||
|
||||
/**
|
||||
|
@ -26,4 +30,21 @@ protected void onCreate(Bundle savedInstanceState) {
|
|||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link
|
||||
* DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React
|
||||
* (aka React 18) with two boolean flags.
|
||||
*/
|
||||
@Override
|
||||
protected ReactActivityDelegate createReactActivityDelegate() {
|
||||
return new DefaultReactActivityDelegate(
|
||||
this,
|
||||
getMainComponentName(),
|
||||
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
|
||||
DefaultNewArchitectureEntryPoint.getFabricEnabled(), // fabricEnabled
|
||||
// If you opted-in for the New Architecture, we enable Concurrent React (i.e. React 18).
|
||||
DefaultNewArchitectureEntryPoint.getConcurrentReactEnabled() // concurrentRootEnabled
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,18 +7,18 @@ import com.facebook.react.ReactApplication;
|
|||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
|
||||
import com.facebook.react.defaults.DefaultReactNativeHost;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import com.facebook.react.modules.i18nmanager.I18nUtil;
|
||||
import java.util.List;
|
||||
import com.bugsnag.android.Bugsnag;
|
||||
import com.facebook.react.bridge.JSIModulePackage;
|
||||
import com.swmansion.reanimated.ReanimatedJSIModulePackage;
|
||||
|
||||
public class MainApplication extends Application implements ReactApplication {
|
||||
|
||||
private final ReactNativeHost mReactNativeHost =
|
||||
new ReactNativeHost(this) {
|
||||
new DefaultReactNativeHost(this) {
|
||||
@Override
|
||||
public boolean getUseDeveloperSupport() {
|
||||
return BuildConfig.DEBUG;
|
||||
|
@ -39,8 +39,13 @@ public class MainApplication extends Application implements ReactApplication {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected JSIModulePackage getJSIModulePackage() {
|
||||
return new ReanimatedJSIModulePackage();
|
||||
protected boolean isNewArchEnabled() {
|
||||
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean isHermesEnabled() {
|
||||
return BuildConfig.IS_HERMES_ENABLED;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -56,37 +61,9 @@ public class MainApplication extends Application implements ReactApplication {
|
|||
I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance();
|
||||
sharedI18nUtilInstance.allowRTL(getApplicationContext(), true);
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
|
||||
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
*
|
||||
* @param context
|
||||
* @param reactInstanceManager
|
||||
*/
|
||||
private static void initializeFlipper(
|
||||
Context context, ReactInstanceManager reactInstanceManager) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
try {
|
||||
/*
|
||||
We use reflection here to pick up the class that initializes Flipper,
|
||||
since Flipper library is not available in release mode
|
||||
*/
|
||||
Class<?> aClass = Class.forName("com.rndiffapp.ReactNativeFlipper");
|
||||
aClass
|
||||
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
|
||||
.invoke(null, context, reactInstanceManager);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
// If you opted-in for the New Architecture, we load the native entry point for this app.
|
||||
DefaultNewArchitectureEntryPoint.load();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,33 +4,39 @@ buildscript {
|
|||
ext {
|
||||
minSdkVersion = 28
|
||||
supportLibVersion = "28.0.0"
|
||||
buildToolsVersion = "30.0.3"
|
||||
compileSdkVersion = 30
|
||||
targetSdkVersion = 30
|
||||
buildToolsVersion = "33.0.0"
|
||||
compileSdkVersion = 33
|
||||
targetSdkVersion = 33
|
||||
googlePlayServicesVersion = "16.+"
|
||||
googlePlayServicesIidVersion = "16.0.1"
|
||||
firebaseVersion = "17.3.4"
|
||||
firebaseMessagingVersion = "20.2.1"
|
||||
ndkVersion = "23.0.7599858"
|
||||
firebaseMessagingVersion = "21.1.0"
|
||||
|
||||
// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
|
||||
ndkVersion = "23.1.7779620"
|
||||
kotlin_version = '1.8.0'
|
||||
kotlinVersion = '1.8.0'
|
||||
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath('com.android.tools.build:gradle:4.2.2')
|
||||
classpath("com.android.tools.build:gradle:7.4.2")
|
||||
classpath("com.bugsnag:bugsnag-android-gradle-plugin:5.+")
|
||||
classpath 'com.google.gms:google-services:4.3.14' // Google Services plugin
|
||||
classpath("com.facebook.react:react-native-gradle-plugin")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
|
||||
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20")
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
maven {
|
||||
url("$rootDir/../node_modules/detox/Detox-android")
|
||||
}
|
||||
jcenter() {
|
||||
content {
|
||||
includeModule("com.facebook.yoga", "proguard-annotations")
|
||||
|
@ -73,9 +79,8 @@ subprojects {
|
|||
afterEvaluate {project ->
|
||||
if (project.hasProperty("android")) {
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion '30.0.3'
|
||||
compileSdkVersion 29
|
||||
compileSdkVersion 31
|
||||
defaultConfig {
|
||||
minSdkVersion 28
|
||||
}
|
||||
|
@ -83,3 +88,9 @@ subprojects {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
subprojects { subproject ->
|
||||
if(project['name'] == 'react-native-widget-center') {
|
||||
project.configurations { compile { } }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
# Default value: -Xmx1024m -XX:MaxPermSize=256m
|
||||
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
|
||||
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
|
@ -24,11 +24,18 @@ android.useAndroidX=true
|
|||
# Automatically convert third-party libraries to use AndroidX
|
||||
android.enableJetifier=true
|
||||
|
||||
# added when build failed because of memory:
|
||||
# https://stackoverflow.com/questions/56075455/expiring-daemon-because-jvm-heap-space-is-exhausted
|
||||
org.gradle.daemon=true
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
# Use this property to specify which architecture you want to build.
|
||||
# You can also override it from the CLI using
|
||||
# ./gradlew <task> -PreactNativeArchitectures=x86_64
|
||||
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
|
||||
|
||||
# Version of flipper SDK to use with React Native
|
||||
FLIPPER_VERSION=0.127.0
|
||||
# Use this property to enable support to the new architecture.
|
||||
# This will allow you to use TurboModules and the Fabric render in
|
||||
# your application. You should enable this flag either if you want
|
||||
# to write custom TurboModules/Fabric components OR use libraries that
|
||||
# are providing them.
|
||||
newArchEnabled=false
|
||||
|
||||
# Use this property to enable or disable the Hermes JS engine.
|
||||
# If set to false, you will be using JSC instead.
|
||||
hermesEnabled=true
|
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.2-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
|
||||
|
|
269
android/gradlew
vendored
269
android/gradlew
vendored
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
@ -17,78 +17,113 @@
|
|||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
|||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
|
@ -105,79 +140,95 @@ location of your Java installation."
|
|||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
|
@ -1,6 +1,6 @@
|
|||
rootProject.name = 'BlueWallet'
|
||||
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
|
||||
include ':app'
|
||||
|
||||
includeBuild('../node_modules/react-native-gradle-plugin')
|
||||
include ':detox'
|
||||
project(':detox').projectDir = new File(rootProject.projectDir, '../node_modules/detox/android/detox')
|
|
@ -820,9 +820,15 @@ module.exports.calcEstimateFeeFromFeeHistorgam = function (numberOfBlocks, feeHi
|
|||
|
||||
module.exports.estimateFees = async function () {
|
||||
let histogram;
|
||||
let timeoutId;
|
||||
try {
|
||||
histogram = await Promise.race([mainClient.mempool_getFeeHistogram(), new Promise(resolve => setTimeout(resolve, 29000))]);
|
||||
} catch (_) {}
|
||||
histogram = await Promise.race([
|
||||
mainClient.mempool_getFeeHistogram(),
|
||||
new Promise(resolve => (timeoutId = setTimeout(resolve, 15000))),
|
||||
]);
|
||||
} finally {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
|
||||
if (!histogram) throw new Error('timeout while getting mempool_getFeeHistogram');
|
||||
|
||||
|
@ -832,7 +838,7 @@ module.exports.estimateFees = async function () {
|
|||
const _slow = await module.exports.estimateFee(144);
|
||||
|
||||
// calculating fast fees from mempool:
|
||||
const fast = module.exports.calcEstimateFeeFromFeeHistorgam(1, histogram);
|
||||
const fast = Math.max(2, module.exports.calcEstimateFeeFromFeeHistorgam(1, histogram));
|
||||
// recalculating medium and slow fees using bitcoincore estimations only like relative weights:
|
||||
// (minimum 1 sat, just for any case)
|
||||
const medium = Math.max(1, Math.round((fast * _medium) / _fast));
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
import { useContext, useEffect } from 'react';
|
||||
import Obscure from 'react-native-obscure';
|
||||
import { BlueStorageContext } from './storage-context';
|
||||
const Privacy = () => {
|
||||
const { isPrivacyBlurEnabled } = useContext(BlueStorageContext);
|
||||
|
||||
useEffect(() => {
|
||||
Privacy.disableBlur();
|
||||
}, [isPrivacyBlurEnabled]);
|
||||
|
||||
Privacy.enableBlur = () => {
|
||||
if (!isPrivacyBlurEnabled) return;
|
||||
Obscure.activateObscure();
|
||||
};
|
||||
|
||||
Privacy.disableBlur = () => {
|
||||
Obscure.deactivateObscure();
|
||||
};
|
||||
return null;
|
||||
};
|
||||
export default Privacy;
|
29
blue_modules/Privacy.android.tsx
Normal file
29
blue_modules/Privacy.android.tsx
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { useContext, useEffect } from 'react';
|
||||
// @ts-ignore: react-native-obscure is not in the type definition
|
||||
import Obscure from 'react-native-obscure';
|
||||
import { BlueStorageContext } from './storage-context';
|
||||
interface PrivacyComponent extends React.FC {
|
||||
enableBlur: (isPrivacyBlurEnabled: boolean) => void;
|
||||
disableBlur: () => void;
|
||||
}
|
||||
|
||||
const Privacy: PrivacyComponent = () => {
|
||||
const { isPrivacyBlurEnabled } = useContext(BlueStorageContext);
|
||||
|
||||
useEffect(() => {
|
||||
Obscure.deactivateObscure();
|
||||
}, [isPrivacyBlurEnabled]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
Privacy.enableBlur = (isPrivacyBlurEnabled: boolean) => {
|
||||
if (!isPrivacyBlurEnabled) return;
|
||||
Obscure.activateObscure();
|
||||
};
|
||||
|
||||
Privacy.disableBlur = () => {
|
||||
Obscure.deactivateObscure();
|
||||
};
|
||||
|
||||
export default Privacy;
|
|
@ -1,22 +0,0 @@
|
|||
import { useContext, useEffect } from 'react';
|
||||
import { enabled } from 'react-native-privacy-snapshot';
|
||||
import { BlueStorageContext } from './storage-context';
|
||||
const Privacy = () => {
|
||||
const { isPrivacyBlurEnabled } = useContext(BlueStorageContext);
|
||||
|
||||
useEffect(() => {
|
||||
Privacy.disableBlur();
|
||||
}, [isPrivacyBlurEnabled]);
|
||||
|
||||
Privacy.enableBlur = () => {
|
||||
if (!isPrivacyBlurEnabled) return;
|
||||
enabled(true);
|
||||
};
|
||||
|
||||
Privacy.disableBlur = () => {
|
||||
enabled(false);
|
||||
};
|
||||
return null;
|
||||
};
|
||||
|
||||
export default Privacy;
|
30
blue_modules/Privacy.ios.tsx
Normal file
30
blue_modules/Privacy.ios.tsx
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { useContext, useEffect } from 'react';
|
||||
// @ts-ignore: react-native-obscure is not in the type definition
|
||||
import { enabled } from 'react-native-privacy-snapshot';
|
||||
import { BlueStorageContext } from './storage-context';
|
||||
|
||||
interface PrivacyComponent extends React.FC {
|
||||
enableBlur: (isPrivacyBlurEnabled: boolean) => void;
|
||||
disableBlur: () => void;
|
||||
}
|
||||
|
||||
const Privacy: PrivacyComponent = () => {
|
||||
const { isPrivacyBlurEnabled } = useContext(BlueStorageContext);
|
||||
|
||||
useEffect(() => {
|
||||
Privacy.disableBlur();
|
||||
}, [isPrivacyBlurEnabled]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
Privacy.enableBlur = (isPrivacyBlurEnabled: boolean) => {
|
||||
if (!isPrivacyBlurEnabled) return;
|
||||
enabled(true);
|
||||
};
|
||||
|
||||
Privacy.disableBlur = () => {
|
||||
enabled(false);
|
||||
};
|
||||
|
||||
export default Privacy;
|
|
@ -1,5 +0,0 @@
|
|||
export default class Privacy {
|
||||
static enableBlur() {}
|
||||
|
||||
static disableBlur() {}
|
||||
}
|
21
blue_modules/Privacy.tsx
Normal file
21
blue_modules/Privacy.tsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import React from 'react';
|
||||
|
||||
interface PrivacyComponent extends React.FC {
|
||||
enableBlur: () => void;
|
||||
disableBlur: () => void;
|
||||
}
|
||||
|
||||
const Privacy: PrivacyComponent = () => {
|
||||
// Define Privacy's behavior
|
||||
return null;
|
||||
};
|
||||
|
||||
Privacy.enableBlur = () => {
|
||||
// Define the enableBlur behavior
|
||||
};
|
||||
|
||||
Privacy.disableBlur = () => {
|
||||
// Define the disableBlur behavior
|
||||
};
|
||||
|
||||
export default Privacy;
|
|
@ -42,7 +42,6 @@
|
|||
"version": "1.0.2",
|
||||
"react-native": {
|
||||
"path": "path-browserify",
|
||||
"fs": "react-native-level-fs",
|
||||
"_stream_transform": "readable-stream/transform",
|
||||
"_stream_readable": "readable-stream/readable",
|
||||
"_stream_writable": "readable-stream/writable",
|
||||
|
@ -52,7 +51,6 @@
|
|||
},
|
||||
"browser": {
|
||||
"path": "path-browserify",
|
||||
"fs": "react-native-level-fs",
|
||||
"_stream_transform": "readable-stream/transform",
|
||||
"_stream_readable": "readable-stream/readable",
|
||||
"_stream_writable": "readable-stream/writable",
|
||||
|
|
|
@ -59,7 +59,6 @@
|
|||
"version": "0.1.6",
|
||||
"react-native": {
|
||||
"path": "path-browserify",
|
||||
"fs": "react-native-level-fs",
|
||||
"_stream_transform": "readable-stream/transform",
|
||||
"_stream_readable": "readable-stream/readable",
|
||||
"_stream_writable": "readable-stream/writable",
|
||||
|
@ -69,7 +68,6 @@
|
|||
},
|
||||
"browser": {
|
||||
"path": "path-browserify",
|
||||
"fs": "react-native-level-fs",
|
||||
"_stream_transform": "readable-stream/transform",
|
||||
"_stream_readable": "readable-stream/readable",
|
||||
"_stream_writable": "readable-stream/writable",
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
import { useAsyncStorage } from '@react-native-async-storage/async-storage';
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
|
||||
function BlueClipboard() {
|
||||
BlueClipboard.STORAGE_KEY = 'ClipboardReadAllowed';
|
||||
const isClipboardAccessAllowed = useAsyncStorage(BlueClipboard.STORAGE_KEY).getItem;
|
||||
const setIsClipboardAccessAllowed = useAsyncStorage(BlueClipboard.STORAGE_KEY).setItem;
|
||||
|
||||
BlueClipboard.isReadClipboardAllowed = async () => {
|
||||
try {
|
||||
const clipboardAccessAllowed = await isClipboardAccessAllowed();
|
||||
if (clipboardAccessAllowed === null) {
|
||||
await setIsClipboardAccessAllowed(JSON.stringify(true));
|
||||
return true;
|
||||
}
|
||||
return !!JSON.parse(clipboardAccessAllowed);
|
||||
} catch {
|
||||
await setIsClipboardAccessAllowed(JSON.stringify(true));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
BlueClipboard.setReadClipboardAllowed = value => {
|
||||
setIsClipboardAccessAllowed(JSON.stringify(!!value));
|
||||
};
|
||||
|
||||
BlueClipboard.getClipboardContent = async () => {
|
||||
const isAllowed = await BlueClipboard.isReadClipboardAllowed();
|
||||
if (isAllowed) {
|
||||
return Clipboard.getString();
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
BlueClipboard.default = new BlueClipboard();
|
||||
export default BlueClipboard;
|
42
blue_modules/clipboard.ts
Normal file
42
blue_modules/clipboard.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
import { useAsyncStorage } from '@react-native-async-storage/async-storage';
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
|
||||
const BlueClipboard = () => {
|
||||
const STORAGE_KEY = 'ClipboardReadAllowed';
|
||||
const { getItem, setItem } = useAsyncStorage(STORAGE_KEY);
|
||||
|
||||
const isReadClipboardAllowed = async () => {
|
||||
try {
|
||||
const clipboardAccessAllowed = await getItem();
|
||||
if (clipboardAccessAllowed === null) {
|
||||
await setItem(JSON.stringify(true));
|
||||
return true;
|
||||
}
|
||||
return !!JSON.parse(clipboardAccessAllowed);
|
||||
} catch {
|
||||
await setItem(JSON.stringify(true));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
const setReadClipboardAllowed = (value: boolean) => {
|
||||
setItem(JSON.stringify(!!value));
|
||||
};
|
||||
|
||||
const getClipboardContent = async () => {
|
||||
const isAllowed = await isReadClipboardAllowed();
|
||||
if (isAllowed) {
|
||||
return Clipboard.getString();
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
isReadClipboardAllowed,
|
||||
setReadClipboardAllowed,
|
||||
getClipboardContent,
|
||||
};
|
||||
};
|
||||
|
||||
export default BlueClipboard;
|
|
@ -53,6 +53,9 @@ async function _restoreSavedPreferredFiatCurrencyFromStorage() {
|
|||
if (preferredFiatCurrency === null) {
|
||||
throw Error('No Preferred Fiat selected');
|
||||
}
|
||||
|
||||
preferredFiatCurrency = FiatUnit[preferredFiatCurrency.endPointKey] || preferredFiatCurrency;
|
||||
// ^^^ in case configuration in json file changed (and is different from what we stored) we reload it
|
||||
} catch (_) {
|
||||
const deviceCurrencies = RNLocalize.getCurrencies();
|
||||
if (Object.keys(FiatUnit).some(unit => unit === deviceCurrencies[0])) {
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { Platform } from 'react-native';
|
||||
import { getSystemName, isTablet, getDeviceType } from 'react-native-device-info';
|
||||
|
||||
const isMacCatalina = getSystemName() === 'Mac OS X';
|
||||
const isDesktop = getDeviceType() === 'Desktop';
|
||||
const getIsTorCapable = () => {
|
||||
let capable = true;
|
||||
if (Platform.OS === 'android' && Platform.Version < 26) {
|
||||
capable = false;
|
||||
} else if (isDesktop) {
|
||||
capable = false;
|
||||
}
|
||||
return capable;
|
||||
};
|
||||
|
||||
const IS_TOR_DAEMON_DISABLED = 'is_tor_daemon_disabled';
|
||||
export async function setIsTorDaemonDisabled(disabled = true) {
|
||||
return AsyncStorage.setItem(IS_TOR_DAEMON_DISABLED, disabled ? '1' : '');
|
||||
}
|
||||
|
||||
export async function isTorDaemonDisabled() {
|
||||
let isTorDaemonDisabled;
|
||||
try {
|
||||
const savedValue = await AsyncStorage.getItem(IS_TOR_DAEMON_DISABLED);
|
||||
if (savedValue === null) {
|
||||
isTorDaemonDisabled = false;
|
||||
} else {
|
||||
isTorDaemonDisabled = savedValue;
|
||||
}
|
||||
} catch {
|
||||
isTorDaemonDisabled = true;
|
||||
}
|
||||
|
||||
return !!isTorDaemonDisabled;
|
||||
}
|
||||
|
||||
export const isHandset = getDeviceType() === 'Handset';
|
||||
export const isTorCapable = getIsTorCapable();
|
||||
export { isMacCatalina, isDesktop, isTablet };
|
41
blue_modules/environment.ts
Normal file
41
blue_modules/environment.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { Platform } from 'react-native';
|
||||
import { isTablet, getDeviceType } from 'react-native-device-info';
|
||||
|
||||
const isDesktop: boolean = getDeviceType() === 'Desktop';
|
||||
|
||||
const getIsTorCapable = (): boolean => {
|
||||
let capable = true;
|
||||
if (Platform.OS === 'android' && Platform.Version < 26) {
|
||||
capable = false;
|
||||
} else if (isDesktop) {
|
||||
capable = false;
|
||||
}
|
||||
return capable;
|
||||
};
|
||||
|
||||
const IS_TOR_DAEMON_DISABLED: string = 'is_tor_daemon_disabled';
|
||||
|
||||
export async function setIsTorDaemonDisabled(disabled: boolean = true): Promise<void> {
|
||||
return AsyncStorage.setItem(IS_TOR_DAEMON_DISABLED, disabled ? '1' : '');
|
||||
}
|
||||
|
||||
export async function isTorDaemonDisabled(): Promise<boolean> {
|
||||
let isTorDaemonDisabled: boolean;
|
||||
try {
|
||||
const savedValue = await AsyncStorage.getItem(IS_TOR_DAEMON_DISABLED);
|
||||
if (savedValue === null) {
|
||||
isTorDaemonDisabled = false;
|
||||
} else {
|
||||
isTorDaemonDisabled = savedValue === '1';
|
||||
}
|
||||
} catch {
|
||||
isTorDaemonDisabled = true;
|
||||
}
|
||||
|
||||
return isTorDaemonDisabled;
|
||||
}
|
||||
|
||||
export const isHandset: boolean = getDeviceType() === 'Handset';
|
||||
export const isTorCapable: boolean = getIsTorCapable();
|
||||
export { isDesktop, isTablet };
|
|
@ -6,8 +6,6 @@ import DocumentPicker from 'react-native-document-picker';
|
|||
import { launchCamera, launchImageLibrary } from 'react-native-image-picker';
|
||||
import { presentCameraNotAuthorizedAlert } from '../class/camera';
|
||||
import { isDesktop } from '../blue_modules/environment';
|
||||
import ActionSheet from '../screen/ActionSheet';
|
||||
import BlueClipboard from './clipboard';
|
||||
import alert from '../components/Alert';
|
||||
const LocalQRCode = require('@remobile/react-native-qrcode-local-image');
|
||||
|
||||
|
@ -205,43 +203,8 @@ const showFilePickerAndReadFile = async function () {
|
|||
}
|
||||
};
|
||||
|
||||
// Intended for macOS Catalina. Not for long press shortcut
|
||||
const showActionSheet = async props => {
|
||||
const isClipboardEmpty = (await BlueClipboard.getClipboardContent()).trim().length === 0;
|
||||
let copyFromClipboardIndex;
|
||||
const options = [loc._.cancel, loc.wallets.take_photo, loc.wallets.list_long_choose];
|
||||
if (!isClipboardEmpty) {
|
||||
options.push(loc.wallets.list_long_clipboard);
|
||||
copyFromClipboardIndex = options.length - 1;
|
||||
}
|
||||
|
||||
options.push(loc.wallets.import_file);
|
||||
const importFileButtonIndex = options.length - 1;
|
||||
|
||||
return new Promise(resolve =>
|
||||
ActionSheet.showActionSheetWithOptions({ options, cancelButtonIndex: 0, anchor: props.anchor }, async buttonIndex => {
|
||||
if (buttonIndex === 1) {
|
||||
takePhotoWithImagePickerAndReadPhoto().then(resolve);
|
||||
} else if (buttonIndex === 2) {
|
||||
showImagePickerAndReadImage()
|
||||
.then(resolve)
|
||||
.catch(error => alert(error.message));
|
||||
} else if (buttonIndex === copyFromClipboardIndex) {
|
||||
const clipboard = await BlueClipboard.getClipboardContent();
|
||||
resolve(clipboard);
|
||||
} else if (importFileButtonIndex) {
|
||||
const { data } = await showFilePickerAndReadFile();
|
||||
if (data) {
|
||||
resolve(data);
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
module.exports.writeFileAndExport = writeFileAndExport;
|
||||
module.exports.openSignedTransaction = openSignedTransaction;
|
||||
module.exports.showFilePickerAndReadFile = showFilePickerAndReadFile;
|
||||
module.exports.showImagePickerAndReadImage = showImagePickerAndReadImage;
|
||||
module.exports.takePhotoWithImagePickerAndReadPhoto = takePhotoWithImagePickerAndReadPhoto;
|
||||
module.exports.showActionSheet = showActionSheet;
|
||||
|
|
|
@ -69,7 +69,7 @@ function Notifications(props) {
|
|||
|
||||
// if user is staring at the app when he receives the notification we process it instantly
|
||||
// so app refetches related wallet
|
||||
if (payload.foreground) props.onProcessNotifications();
|
||||
if (payload.foreground) props.onProcessNotifications(); // eslint-disable-line react/prop-types
|
||||
},
|
||||
|
||||
// (optional) Called when Registered Action is pressed and invokeApp is false, if true onNotification will be called (Android)
|
||||
|
|
|
@ -6,7 +6,7 @@ import { useAsyncStorage } from '@react-native-async-storage/async-storage';
|
|||
import { FiatUnit } from '../models/fiatUnit';
|
||||
import Notifications from '../blue_modules/notifications';
|
||||
import loc, { STORAGE_KEY as LOC_STORAGE_KEY } from '../loc';
|
||||
import { LegacyWallet } from '../class';
|
||||
import { LegacyWallet, WatchOnlyWallet } from '../class';
|
||||
import { isTorDaemonDisabled, setIsTorDaemonDisabled } from './environment';
|
||||
import alert from '../components/Alert';
|
||||
const BlueApp = require('../BlueApp');
|
||||
|
@ -120,6 +120,10 @@ export const BlueStorageProvider = ({ children }) => {
|
|||
setWalletTransactionUpdateStatus(WalletTransactionsStatus.ALL);
|
||||
}
|
||||
await BlueElectrum.waitTillConnected();
|
||||
const paymentCodesStart = Date.now();
|
||||
await fetchSenderPaymentCodes(lastSnappedTo);
|
||||
const paymentCodesEnd = Date.now();
|
||||
console.log('fetch payment codes took', (paymentCodesEnd - paymentCodesStart) / 1000, 'sec');
|
||||
const balanceStart = +new Date();
|
||||
await fetchWalletBalances(lastSnappedTo);
|
||||
const balanceEnd = +new Date();
|
||||
|
@ -190,7 +194,7 @@ export const BlueStorageProvider = ({ children }) => {
|
|||
addWallet(w);
|
||||
await saveToDisk();
|
||||
A(A.ENUM.CREATED_WALLET);
|
||||
Alert.alert('', loc.wallets.import_success);
|
||||
Alert.alert('', w.type === WatchOnlyWallet.type ? loc.wallets.import_success_watchonly : loc.wallets.import_success);
|
||||
Notifications.majorTomToGroundControl(w.getAllExternalAddresses(), [], []);
|
||||
// start balance fetching at the background
|
||||
await w.fetchBalance();
|
||||
|
@ -199,8 +203,9 @@ export const BlueStorageProvider = ({ children }) => {
|
|||
|
||||
let txMetadata = BlueApp.tx_metadata || {};
|
||||
const getTransactions = BlueApp.getTransactions;
|
||||
const isAdancedModeEnabled = BlueApp.isAdancedModeEnabled;
|
||||
const isAdvancedModeEnabled = BlueApp.isAdvancedModeEnabled;
|
||||
|
||||
const fetchSenderPaymentCodes = BlueApp.fetchSenderPaymentCodes;
|
||||
const fetchWalletBalances = BlueApp.fetchWalletBalances;
|
||||
const fetchWalletTransactions = BlueApp.fetchWalletTransactions;
|
||||
const getBalance = BlueApp.getBalance;
|
||||
|
@ -214,7 +219,7 @@ export const BlueStorageProvider = ({ children }) => {
|
|||
const decryptStorage = BlueApp.decryptStorage;
|
||||
const isPasswordInUse = BlueApp.isPasswordInUse;
|
||||
const cachedPassword = BlueApp.cachedPassword;
|
||||
const setIsAdancedModeEnabled = BlueApp.setIsAdancedModeEnabled;
|
||||
const setIsAdvancedModeEnabled = BlueApp.setIsAdvancedModeEnabled;
|
||||
const getHodlHodlSignatureKey = BlueApp.getHodlHodlSignatureKey;
|
||||
const addHodlHodlContract = BlueApp.addHodlHodlContract;
|
||||
const getHodlHodlContracts = BlueApp.getHodlHodlContracts;
|
||||
|
@ -239,7 +244,7 @@ export const BlueStorageProvider = ({ children }) => {
|
|||
setItem,
|
||||
getItem,
|
||||
getHodlHodlContracts,
|
||||
isAdancedModeEnabled,
|
||||
isAdvancedModeEnabled,
|
||||
fetchWalletBalances,
|
||||
fetchWalletTransactions,
|
||||
fetchAndSaveWalletTransactions,
|
||||
|
@ -260,7 +265,7 @@ export const BlueStorageProvider = ({ children }) => {
|
|||
getHodlHodlApiKey,
|
||||
decryptStorage,
|
||||
isPasswordInUse,
|
||||
setIsAdancedModeEnabled,
|
||||
setIsAdvancedModeEnabled,
|
||||
setPreferredFiatCurrency,
|
||||
preferredFiatCurrency,
|
||||
setLanguage,
|
||||
|
|
|
@ -67,7 +67,7 @@ export class AppStorage {
|
|||
*/
|
||||
setItem = (key, value) => {
|
||||
if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
|
||||
return RNSecureKeyStore.set(key, value, { accessible: ACCESSIBLE.WHEN_UNLOCKED });
|
||||
return RNSecureKeyStore.set(key, value, { accessible: ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY });
|
||||
} else {
|
||||
return AsyncStorage.setItem(key, value);
|
||||
}
|
||||
|
@ -289,8 +289,8 @@ export class AppStorage {
|
|||
realmkeyValue.create(
|
||||
'KeyValue',
|
||||
{
|
||||
key: key,
|
||||
value: value,
|
||||
key,
|
||||
value,
|
||||
},
|
||||
Realm.UpdateMode.Modified,
|
||||
);
|
||||
|
@ -602,6 +602,11 @@ export class AppStorage {
|
|||
keyCloned._hdWalletInstance._txs_by_external_index = {};
|
||||
keyCloned._hdWalletInstance._txs_by_internal_index = {};
|
||||
}
|
||||
|
||||
if (keyCloned._bip47_instance) {
|
||||
delete keyCloned._bip47_instance; // since it wont be restored into a proper class instance
|
||||
}
|
||||
|
||||
walletsToSave.push(JSON.stringify({ ...keyCloned, type: keyCloned.type }));
|
||||
}
|
||||
if (realm) realm.close();
|
||||
|
@ -682,6 +687,7 @@ export class AppStorage {
|
|||
}
|
||||
} else {
|
||||
for (const wallet of this.wallets) {
|
||||
console.log('fetching balance for', wallet.getLabel());
|
||||
await wallet.fetchBalance();
|
||||
}
|
||||
}
|
||||
|
@ -725,6 +731,27 @@ export class AppStorage {
|
|||
}
|
||||
};
|
||||
|
||||
fetchSenderPaymentCodes = async index => {
|
||||
console.log('fetchSenderPaymentCodes for wallet#', typeof index === 'undefined' ? '(all)' : index);
|
||||
if (index || index === 0) {
|
||||
try {
|
||||
if (!(this.wallets[index].allowBIP47() && this.wallets[index].isBIP47Enabled())) return;
|
||||
await this.wallets[index].fetchBIP47SenderPaymentCodes();
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch sender payment codes for wallet', index, error);
|
||||
}
|
||||
} else {
|
||||
for (const wallet of this.wallets) {
|
||||
try {
|
||||
if (!(wallet.allowBIP47() && wallet.isBIP47Enabled())) continue;
|
||||
await wallet.fetchBIP47SenderPaymentCodes();
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch sender payment codes for wallet', wallet.label, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Array.<AbstractWallet>}
|
||||
|
@ -789,14 +816,14 @@ export class AppStorage {
|
|||
return finalBalance;
|
||||
};
|
||||
|
||||
isAdancedModeEnabled = async () => {
|
||||
isAdvancedModeEnabled = async () => {
|
||||
try {
|
||||
return !!(await AsyncStorage.getItem(AppStorage.ADVANCED_MODE_ENABLED));
|
||||
} catch (_) {}
|
||||
return false;
|
||||
};
|
||||
|
||||
setIsAdancedModeEnabled = async value => {
|
||||
setIsAdvancedModeEnabled = async value => {
|
||||
await AsyncStorage.setItem(AppStorage.ADVANCED_MODE_ENABLED, value ? '1' : '');
|
||||
};
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ export default class Azteco {
|
|||
}
|
||||
|
||||
static getParamsFromUrl(u) {
|
||||
const urlObject = url.parse(u, true); // eslint-disable-line node/no-deprecated-api
|
||||
const urlObject = url.parse(u, true); // eslint-disable-line n/no-deprecated-api
|
||||
return {
|
||||
uri: u,
|
||||
c1: urlObject.query.c1,
|
||||
|
|
|
@ -184,7 +184,7 @@ class DeeplinkSchemaMatch {
|
|||
},
|
||||
]);
|
||||
} else {
|
||||
const urlObject = url.parse(event.url, true); // eslint-disable-line node/no-deprecated-api
|
||||
const urlObject = url.parse(event.url, true); // eslint-disable-line n/no-deprecated-api
|
||||
(async () => {
|
||||
if (urlObject.protocol === 'bluewallet:' || urlObject.protocol === 'lapp:' || urlObject.protocol === 'blue:') {
|
||||
switch (urlObject.host) {
|
||||
|
@ -390,7 +390,8 @@ class DeeplinkSchemaMatch {
|
|||
break;
|
||||
}
|
||||
} else if (value.startsWith('lightning')) {
|
||||
lndInvoice = `lightning:${txInfo[index + 1]}`;
|
||||
const lnpart = txInfo[index + 1].split('&').find(el => el.toLowerCase().startsWith('ln'));
|
||||
lndInvoice = `lightning:${lnpart}`;
|
||||
if (!this.isLightningInvoice(lndInvoice)) {
|
||||
lndInvoice = false;
|
||||
break;
|
||||
|
|
|
@ -168,7 +168,7 @@ export class HDSegwitBech32Transaction {
|
|||
value = new BigNumber(value).multipliedBy(100000000).toNumber();
|
||||
wentIn += value;
|
||||
const address = SegwitBech32Wallet.witnessToAddress(inp.witness[inp.witness.length - 1]);
|
||||
utxos.push({ vout: inp.index, value: value, txId: reversedHash, address: address });
|
||||
utxos.push({ vout: inp.index, value, txId: reversedHash, address });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,7 +193,7 @@ export class HDSegwitBech32Transaction {
|
|||
changeAmount += value;
|
||||
} else {
|
||||
// this is target
|
||||
targets.push({ value: value, address: address });
|
||||
targets.push({ value, address });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,9 +205,9 @@ export class HDSegwitBech32Transaction {
|
|||
if (this._wallet.weOwnAddress(address)) {
|
||||
unconfirmedUtxos.push({
|
||||
vout: outp.n,
|
||||
value: value,
|
||||
value,
|
||||
txId: this._txid || this._txDecoded.getId(),
|
||||
address: address,
|
||||
address,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { bech32 } from 'bech32';
|
||||
import bolt11 from 'bolt11';
|
||||
import { isTorDaemonDisabled } from '../blue_modules/environment';
|
||||
import { parse } from 'url'; // eslint-disable-line node/no-deprecated-api
|
||||
import { parse } from 'url'; // eslint-disable-line n/no-deprecated-api
|
||||
import { createHmac } from 'crypto';
|
||||
import secp256k1 from 'secp256k1';
|
||||
const CryptoJS = require('crypto-js');
|
||||
|
@ -289,11 +289,23 @@ export default class Lnurl {
|
|||
return this?._lnurlPayServicePayload?.commentAllowed ? parseInt(this._lnurlPayServicePayload.commentAllowed) : false;
|
||||
}
|
||||
|
||||
getMin() {
|
||||
return this?._lnurlPayServicePayload?.min ? parseInt(this._lnurlPayServicePayload.min) : false;
|
||||
}
|
||||
|
||||
getMax() {
|
||||
return this?._lnurlPayServicePayload?.max ? parseInt(this._lnurlPayServicePayload.max) : false;
|
||||
}
|
||||
|
||||
getAmount() {
|
||||
return this.getMin();
|
||||
}
|
||||
|
||||
authenticate(secret) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this._lnurl) throw new Error('this._lnurl is not set');
|
||||
|
||||
const url = parse(Lnurl.getUrlFromLnurl(this._lnurl), true); // eslint-disable-line node/no-deprecated-api
|
||||
const url = parse(Lnurl.getUrlFromLnurl(this._lnurl), true);
|
||||
|
||||
const hmac = createHmac('sha256', secret);
|
||||
hmac.on('readable', async () => {
|
||||
|
|
|
@ -140,9 +140,9 @@ export class MultisigCosigner {
|
|||
|
||||
static exportToJson(xfp, xpub, path) {
|
||||
return JSON.stringify({
|
||||
xfp: xfp,
|
||||
xpub: xpub,
|
||||
path: path,
|
||||
xfp,
|
||||
xpub,
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -217,7 +217,7 @@ const startImport = (importTextOrig, askPassphrase = false, searchAccounts = fal
|
|||
wallet.setDerivationPath(path);
|
||||
yield { progress: `bip39 ${i.script_type} ${path}` };
|
||||
if (await wallet.wasEverUsed()) {
|
||||
yield { wallet: wallet };
|
||||
yield { wallet };
|
||||
walletFound = true;
|
||||
} else {
|
||||
break; // don't check second account if first one is empty
|
||||
|
|
|
@ -4,22 +4,26 @@ import BigNumber from 'bignumber.js';
|
|||
import b58 from 'bs58check';
|
||||
import BIP32Factory, { BIP32Interface } from 'bip32';
|
||||
|
||||
import { randomBytes } from '../rng';
|
||||
import { AbstractHDWallet } from './abstract-hd-wallet';
|
||||
import { ECPairFactory } from 'ecpair';
|
||||
import { CreateTransactionResult, CreateTransactionUtxo, Transaction, Utxo } from './types';
|
||||
import { ElectrumHistory } from '../../blue_modules/BlueElectrum';
|
||||
import type BlueElectrumNs from '../../blue_modules/BlueElectrum';
|
||||
import { ECPairInterface } from 'ecpair/src/ecpair';
|
||||
import { Psbt, Transaction as BTransaction } from 'bitcoinjs-lib';
|
||||
import { CoinSelectReturnInput, CoinSelectTarget } from 'coinselect';
|
||||
import ecc from '../../blue_modules/noble_ecc';
|
||||
|
||||
import BIP47Factory, { BIP47Interface } from '@spsina/bip47';
|
||||
import { ECPairFactory } from 'ecpair';
|
||||
|
||||
import { randomBytes } from '../rng';
|
||||
import { AbstractHDWallet } from './abstract-hd-wallet';
|
||||
import { CreateTransactionResult, CreateTransactionUtxo, Transaction, Utxo } from './types';
|
||||
import { ElectrumHistory } from '../../blue_modules/BlueElectrum';
|
||||
import type BlueElectrumNs from '../../blue_modules/BlueElectrum';
|
||||
|
||||
const ECPair = ECPairFactory(ecc);
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
const BlueElectrum: typeof BlueElectrumNs = require('../../blue_modules/BlueElectrum');
|
||||
const reverse = require('buffer-reverse');
|
||||
const bip32 = BIP32Factory(ecc);
|
||||
const bip47 = BIP47Factory(ecc);
|
||||
|
||||
type BalanceByIndex = {
|
||||
c: number;
|
||||
|
@ -45,6 +49,16 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
|
||||
_utxo: any[];
|
||||
|
||||
// BIP47
|
||||
_enable_BIP47: boolean;
|
||||
_payment_code: string;
|
||||
_sender_payment_codes: string[];
|
||||
_addresses_by_payment_code: Record<string, string[]>;
|
||||
_next_free_payment_code_address_index: Record<string, number>;
|
||||
_txs_by_payment_code_index: Record<string, Transaction[][]>;
|
||||
_balances_by_payment_code_index: Record<string, BalanceByIndex>;
|
||||
_bip47_instance?: BIP47Interface;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._balances_by_external_index = {}; // 0 => { c: 0, u: 0 } // confirmed/unconfirmed
|
||||
|
@ -54,6 +68,15 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
this._txs_by_internal_index = {};
|
||||
|
||||
this._utxo = [];
|
||||
|
||||
// BIP47
|
||||
this._enable_BIP47 = false;
|
||||
this._payment_code = '';
|
||||
this._sender_payment_codes = [];
|
||||
this._next_free_payment_code_address_index = {};
|
||||
this._txs_by_payment_code_index = {};
|
||||
this._balances_by_payment_code_index = {};
|
||||
this._addresses_by_payment_code = {};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,6 +90,9 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
for (const bal of Object.values(this._balances_by_internal_index)) {
|
||||
ret += bal.c;
|
||||
}
|
||||
for (const pc of this._sender_payment_codes) {
|
||||
ret += this._getBalancesByPaymentCodeIndex(pc).c;
|
||||
}
|
||||
return ret + (this.getUnconfirmedBalance() < 0 ? this.getUnconfirmedBalance() : 0);
|
||||
}
|
||||
|
||||
|
@ -82,6 +108,9 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
for (const bal of Object.values(this._balances_by_internal_index)) {
|
||||
ret += bal.u;
|
||||
}
|
||||
for (const pc of this._sender_payment_codes) {
|
||||
ret += this._getBalancesByPaymentCodeIndex(pc).u;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -121,7 +150,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
return child.toWIF();
|
||||
}
|
||||
|
||||
_getNodeAddressByIndex(node: number, index: number) {
|
||||
_getNodeAddressByIndex(node: number, index: number): string {
|
||||
index = index * 1; // cast to int
|
||||
if (node === 0) {
|
||||
if (this.external_addresses_cache[index]) return this.external_addresses_cache[index]; // cache hit
|
||||
|
@ -143,22 +172,20 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
this._node1 = hdNode.derive(node);
|
||||
}
|
||||
|
||||
let address;
|
||||
let address: string;
|
||||
if (node === 0) {
|
||||
// @ts-ignore
|
||||
address = this.constructor._nodeToBech32SegwitAddress(this._node0.derive(index));
|
||||
}
|
||||
|
||||
if (node === 1) {
|
||||
address = this._hdNodeToAddress(this._node0.derive(index));
|
||||
} else {
|
||||
// tbh the only possible else is node === 1
|
||||
// @ts-ignore
|
||||
address = this.constructor._nodeToBech32SegwitAddress(this._node1.derive(index));
|
||||
address = this._hdNodeToAddress(this._node1.derive(index));
|
||||
}
|
||||
|
||||
if (node === 0) {
|
||||
return (this.external_addresses_cache[index] = address);
|
||||
}
|
||||
|
||||
if (node === 1) {
|
||||
} else {
|
||||
// tbh the only possible else option is node === 1
|
||||
return (this.internal_addresses_cache[index] = address);
|
||||
}
|
||||
}
|
||||
|
@ -189,7 +216,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
throw new Error('Internal error: this._node0 or this._node1 is undefined');
|
||||
}
|
||||
|
||||
_getExternalAddressByIndex(index: number) {
|
||||
_getExternalAddressByIndex(index: number): string {
|
||||
return this._getNodeAddressByIndex(0, index);
|
||||
}
|
||||
|
||||
|
@ -266,6 +293,21 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
}
|
||||
}
|
||||
|
||||
// next, bip47 addresses
|
||||
for (const pc of this._sender_payment_codes) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
|
||||
let hasUnconfirmed = false;
|
||||
this._txs_by_payment_code_index[pc] = this._txs_by_payment_code_index[pc] || {};
|
||||
this._txs_by_payment_code_index[pc][c] = this._txs_by_payment_code_index[pc][c] || [];
|
||||
for (const tx of this._txs_by_payment_code_index[pc][c])
|
||||
hasUnconfirmed = hasUnconfirmed || !tx.confirmations || tx.confirmations < 7;
|
||||
|
||||
if (hasUnconfirmed || this._txs_by_payment_code_index[pc][c].length === 0 || this._balances_by_payment_code_index[pc].u !== 0) {
|
||||
addresses2fetch.push(this._getBIP47Address(pc, c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// first: batch fetch for all addresses histories
|
||||
const histories = await BlueElectrum.multiGetHistoryByAddress(addresses2fetch);
|
||||
const txs: Record<string, ElectrumHistory> = {};
|
||||
|
@ -296,7 +338,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
const inpTxid = txdatas[txid].vin[inpNum].txid;
|
||||
const inpVout = txdatas[txid].vin[inpNum].vout;
|
||||
// got txid and output number of _previous_ transaction we shoud look into
|
||||
if (vintxdatas[inpTxid] && vintxdatas[inpTxid].vout[inpVout]) {
|
||||
if (vintxdatas[inpTxid]?.vout[inpVout]) {
|
||||
// extracting amount & addresses from previous output and adding it to _our_ input:
|
||||
txdatas[txid].vin[inpNum].addresses = vintxdatas[inpTxid].vout[inpVout].scriptPubKey.addresses;
|
||||
txdatas[txid].vin[inpNum].value = vintxdatas[inpTxid].vout[inpVout].value;
|
||||
|
@ -312,6 +354,11 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
for (let c = 0; c < this.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._sender_payment_codes) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
|
||||
this._txs_by_payment_code_index[pc][c] = this._txs_by_payment_code_index[pc][c].filter(tx => !!tx.confirmations);
|
||||
}
|
||||
}
|
||||
|
||||
// now, we need to put transactions in all relevant `cells` of internal hashmaps: this._txs_by_internal_index && this._txs_by_external_index
|
||||
|
||||
|
@ -397,6 +444,51 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
}
|
||||
}
|
||||
|
||||
for (const pc of this._sender_payment_codes) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
|
||||
for (const tx of Object.values(txdatas)) {
|
||||
for (const vin of tx.vin) {
|
||||
if (vin.addresses && vin.addresses.indexOf(this._getBIP47Address(pc, c)) !== -1) {
|
||||
// this TX is related to our address
|
||||
this._txs_by_payment_code_index[pc] = this._txs_by_payment_code_index[pc] || {};
|
||||
this._txs_by_payment_code_index[pc][c] = this._txs_by_payment_code_index[pc][c] || [];
|
||||
const { vin: txVin, vout: txVout, ...txRest } = tx;
|
||||
const clonedTx = { ...txRest, inputs: txVin.slice(0), outputs: txVout.slice(0) };
|
||||
|
||||
// trying to replace tx if it exists already (because it has lower confirmations, for example)
|
||||
let replaced = false;
|
||||
for (let cc = 0; cc < this._txs_by_payment_code_index[pc][c].length; cc++) {
|
||||
if (this._txs_by_payment_code_index[pc][c][cc].txid === clonedTx.txid) {
|
||||
replaced = true;
|
||||
this._txs_by_payment_code_index[pc][c][cc] = clonedTx;
|
||||
}
|
||||
}
|
||||
if (!replaced) this._txs_by_payment_code_index[pc][c].push(clonedTx);
|
||||
}
|
||||
}
|
||||
for (const vout of tx.vout) {
|
||||
if (vout.scriptPubKey.addresses && vout.scriptPubKey.addresses.indexOf(this._getBIP47Address(pc, c)) !== -1) {
|
||||
// this TX is related to our address
|
||||
this._txs_by_payment_code_index[pc] = this._txs_by_payment_code_index[pc] || {};
|
||||
this._txs_by_payment_code_index[pc][c] = this._txs_by_payment_code_index[pc][c] || [];
|
||||
const { vin: txVin, vout: txVout, ...txRest } = tx;
|
||||
const clonedTx = { ...txRest, inputs: txVin.slice(0), outputs: txVout.slice(0) };
|
||||
|
||||
// trying to replace tx if it exists already (because it has lower confirmations, for example)
|
||||
let replaced = false;
|
||||
for (let cc = 0; cc < this._txs_by_internal_index[c].length; cc++) {
|
||||
if (this._txs_by_internal_index[c][cc].txid === clonedTx.txid) {
|
||||
replaced = true;
|
||||
this._txs_by_internal_index[c][cc] = clonedTx;
|
||||
}
|
||||
}
|
||||
if (!replaced) this._txs_by_internal_index[c].push(clonedTx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._lastTxFetch = +new Date();
|
||||
}
|
||||
|
||||
|
@ -409,6 +501,14 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
for (const addressTxs of Object.values(this._txs_by_internal_index)) {
|
||||
txs = txs.concat(addressTxs);
|
||||
}
|
||||
if (this._sender_payment_codes) {
|
||||
for (const pc of this._sender_payment_codes) {
|
||||
if (this._txs_by_payment_code_index[pc])
|
||||
for (const addressTxs of Object.values(this._txs_by_payment_code_index[pc])) {
|
||||
txs = txs.concat(addressTxs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (txs.length === 0) return []; // guard clause; so we wont spend time calculating addresses
|
||||
|
||||
|
@ -421,6 +521,12 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
for (let c = 0; c < this.next_free_change_address_index + 1; c++) {
|
||||
ownedAddressesHashmap[this._getInternalAddressByIndex(c)] = true;
|
||||
}
|
||||
if (this._sender_payment_codes)
|
||||
for (const pc of this._sender_payment_codes) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + 1; c++) {
|
||||
ownedAddressesHashmap[this._getBIP47Address(pc, c)] = true;
|
||||
}
|
||||
}
|
||||
// hack: in case this code is called from LegacyWallet:
|
||||
if (this.getAddress()) ownedAddressesHashmap[String(this.getAddress())] = true;
|
||||
|
||||
|
@ -434,17 +540,14 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
|
||||
for (const vin of tx.inputs) {
|
||||
// if input (spending) goes from our address - we are loosing!
|
||||
if (
|
||||
(vin.address && ownedAddressesHashmap[vin.address]) ||
|
||||
(vin.addresses && vin.addresses[0] && ownedAddressesHashmap[vin.addresses[0]])
|
||||
) {
|
||||
if ((vin.address && ownedAddressesHashmap[vin.address]) || (vin.addresses?.[0] && ownedAddressesHashmap[vin.addresses[0]])) {
|
||||
tx.value -= new BigNumber(vin.value ?? 0).multipliedBy(100000000).toNumber();
|
||||
}
|
||||
}
|
||||
|
||||
for (const vout of tx.outputs) {
|
||||
// when output goes to our address - this means we are gaining!
|
||||
if (vout.scriptPubKey.addresses && vout.scriptPubKey.addresses[0] && ownedAddressesHashmap[vout.scriptPubKey.addresses[0]]) {
|
||||
if (vout.scriptPubKey.addresses?.[0] && ownedAddressesHashmap[vout.scriptPubKey.addresses[0]]) {
|
||||
tx.value += new BigNumber(vout.value).multipliedBy(100000000).toNumber();
|
||||
}
|
||||
}
|
||||
|
@ -499,7 +602,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
) {
|
||||
const address = this._getInternalAddressByIndex(c);
|
||||
if (lastHistoriesWithUsedAddresses[address] && lastHistoriesWithUsedAddresses[address].length > 0) {
|
||||
lastUsedIndex = Math.max(c, lastUsedIndex) + 1; // point to next, which is supposed to be unsued
|
||||
lastUsedIndex = Math.max(c, lastUsedIndex) + 1; // point to next, which is supposed to be unused
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -542,7 +645,50 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
) {
|
||||
const address = this._getExternalAddressByIndex(c);
|
||||
if (lastHistoriesWithUsedAddresses[address] && lastHistoriesWithUsedAddresses[address].length > 0) {
|
||||
lastUsedIndex = Math.max(c, lastUsedIndex) + 1; // point to next, which is supposed to be unsued
|
||||
lastUsedIndex = Math.max(c, lastUsedIndex) + 1; // point to next, which is supposed to be unused
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lastUsedIndex;
|
||||
}
|
||||
|
||||
async _binarySearchIterationForBIP47Address(paymentCode: string, index: number) {
|
||||
const generateChunkAddresses = (chunkNum: number) => {
|
||||
const ret = [];
|
||||
for (let c = this.gap_limit * chunkNum; c < this.gap_limit * (chunkNum + 1); c++) {
|
||||
ret.push(this._getBIP47Address(paymentCode, c));
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
let lastChunkWithUsedAddressesNum = null;
|
||||
let lastHistoriesWithUsedAddresses = null;
|
||||
for (let c = 0; c < Math.round(index / this.gap_limit); c++) {
|
||||
const histories = await BlueElectrum.multiGetHistoryByAddress(generateChunkAddresses(c));
|
||||
// @ts-ignore
|
||||
if (this.constructor._getTransactionsFromHistories(histories).length > 0) {
|
||||
// in this particular chunk we have used addresses
|
||||
lastChunkWithUsedAddressesNum = c;
|
||||
lastHistoriesWithUsedAddresses = histories;
|
||||
} else {
|
||||
// empty chunk. no sense searching more chunks
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let lastUsedIndex = 0;
|
||||
|
||||
if (lastHistoriesWithUsedAddresses) {
|
||||
// now searching for last used address in batch lastChunkWithUsedAddressesNum
|
||||
for (
|
||||
let c = Number(lastChunkWithUsedAddressesNum) * this.gap_limit;
|
||||
c < Number(lastChunkWithUsedAddressesNum) * this.gap_limit + this.gap_limit;
|
||||
c++
|
||||
) {
|
||||
const address = this._getBIP47Address(paymentCode, c);
|
||||
if (lastHistoriesWithUsedAddresses[address] && lastHistoriesWithUsedAddresses[address].length > 0) {
|
||||
lastUsedIndex = Math.max(c, lastUsedIndex) + 1; // point to next, which is supposed to be unused
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -556,6 +702,11 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
// doing binary search for last used address:
|
||||
this.next_free_change_address_index = await this._binarySearchIterationForInternalAddress(1000);
|
||||
this.next_free_address_index = await this._binarySearchIterationForExternalAddress(1000);
|
||||
if (this._sender_payment_codes) {
|
||||
for (const pc of this._sender_payment_codes) {
|
||||
this._next_free_payment_code_address_index[pc] = await this._binarySearchIterationForBIP47Address(pc, 1000);
|
||||
}
|
||||
}
|
||||
} // end rescanning fresh wallet
|
||||
|
||||
// finally fetching balance
|
||||
|
@ -577,6 +728,15 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
for (let c = this.next_free_change_address_index; c < this.next_free_change_address_index + this.gap_limit; c++) {
|
||||
lagAddressesToFetch.push(this._getInternalAddressByIndex(c));
|
||||
}
|
||||
for (const pc of this._sender_payment_codes) {
|
||||
for (
|
||||
let c = this._next_free_payment_code_address_index[pc];
|
||||
c < this._next_free_payment_code_address_index[pc] + this.gap_limit;
|
||||
c++
|
||||
) {
|
||||
lagAddressesToFetch.push(this._getBIP47Address(pc, c));
|
||||
}
|
||||
}
|
||||
|
||||
const txs = await BlueElectrum.multiGetHistoryByAddress(lagAddressesToFetch); // <------ electrum call
|
||||
|
||||
|
@ -596,6 +756,20 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
}
|
||||
}
|
||||
|
||||
for (const pc of this._sender_payment_codes) {
|
||||
for (
|
||||
let c = this._next_free_payment_code_address_index[pc];
|
||||
c < this._next_free_payment_code_address_index[pc] + this.gap_limit;
|
||||
c++
|
||||
) {
|
||||
const address = this._getBIP47Address(pc, c);
|
||||
if (txs[address] && Array.isArray(txs[address]) && txs[address].length > 0) {
|
||||
// whoa, someone uses our wallet outside! better catch up
|
||||
this._next_free_payment_code_address_index[pc] = c + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// next, business as usuall. fetch balances
|
||||
|
||||
const addresses2fetch = [];
|
||||
|
@ -614,6 +788,12 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
addresses2fetch.push(this._getInternalAddressByIndex(c));
|
||||
}
|
||||
|
||||
for (const pc of this._sender_payment_codes) {
|
||||
for (let c = 0; c < this._next_free_payment_code_address_index[pc] + this.gap_limit; c++) {
|
||||
addresses2fetch.push(this._getBIP47Address(pc, c));
|
||||
}
|
||||
}
|
||||
|
||||
const balances = await BlueElectrum.multiGetBalanceByAddress(addresses2fetch);
|
||||
|
||||
// converting to a more compact internal format
|
||||
|
@ -658,6 +838,22 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
}
|
||||
}
|
||||
|
||||
for (const pc of this._sender_payment_codes) {
|
||||
let confirmed = 0;
|
||||
let unconfirmed = 0;
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
|
||||
const addr = this._getBIP47Address(pc, c);
|
||||
if (balances.addresses[addr].confirmed || balances.addresses[addr].unconfirmed) {
|
||||
confirmed = confirmed + balances.addresses[addr].confirmed;
|
||||
unconfirmed = unconfirmed + balances.addresses[addr].unconfirmed;
|
||||
}
|
||||
}
|
||||
this._balances_by_payment_code_index[pc] = {
|
||||
c: confirmed,
|
||||
u: unconfirmed,
|
||||
};
|
||||
}
|
||||
|
||||
this._lastBalanceFetch = +new Date();
|
||||
}
|
||||
|
||||
|
@ -667,28 +863,44 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
|
||||
// considering confirmed balance:
|
||||
for (let c = 0; c < this.next_free_address_index + this.gap_limit; c++) {
|
||||
if (this._balances_by_external_index[c] && this._balances_by_external_index[c].c && this._balances_by_external_index[c].c > 0) {
|
||||
if (this._balances_by_external_index?.[c]?.c > 0) {
|
||||
addressess.push(this._getExternalAddressByIndex(c));
|
||||
}
|
||||
}
|
||||
for (let c = 0; c < this.next_free_change_address_index + this.gap_limit; c++) {
|
||||
if (this._balances_by_internal_index[c] && this._balances_by_internal_index[c].c && this._balances_by_internal_index[c].c > 0) {
|
||||
if (this._balances_by_internal_index?.[c]?.c > 0) {
|
||||
addressess.push(this._getInternalAddressByIndex(c));
|
||||
}
|
||||
}
|
||||
|
||||
for (const pc of this._sender_payment_codes) {
|
||||
for (let c = 0; c < this._next_free_payment_code_address_index[pc] + this.gap_limit; c++) {
|
||||
if (this._balances_by_payment_code_index?.[pc]?.c > 0) {
|
||||
addressess.push(this._getBIP47Address(pc, c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// considering UNconfirmed balance:
|
||||
for (let c = 0; c < this.next_free_address_index + this.gap_limit; c++) {
|
||||
if (this._balances_by_external_index[c] && this._balances_by_external_index[c].u && this._balances_by_external_index[c].u > 0) {
|
||||
if (this._balances_by_external_index?.[c]?.u > 0) {
|
||||
addressess.push(this._getExternalAddressByIndex(c));
|
||||
}
|
||||
}
|
||||
for (let c = 0; c < this.next_free_change_address_index + this.gap_limit; c++) {
|
||||
if (this._balances_by_internal_index[c] && this._balances_by_internal_index[c].u && this._balances_by_internal_index[c].u > 0) {
|
||||
if (this._balances_by_internal_index?.[c]?.u > 0) {
|
||||
addressess.push(this._getInternalAddressByIndex(c));
|
||||
}
|
||||
}
|
||||
|
||||
for (const pc of this._sender_payment_codes) {
|
||||
for (let c = 0; c < this._next_free_payment_code_address_index[pc] + this.gap_limit; c++) {
|
||||
if (this._balances_by_payment_code_index?.[pc]?.u > 0) {
|
||||
addressess.push(this._getBIP47Address(pc, c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note: we could remove checks `.c` and `.u` to simplify code, but the resulting `addressess` array would be bigger, thus bigger batch
|
||||
// to fetch (or maybe even several fetches), which is not critical but undesirable.
|
||||
// anyway, result has `.confirmations` property for each utxo, so outside caller can easily filter out unconfirmed if he wants to
|
||||
|
@ -756,6 +968,11 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
for (let c = 0; c < this.next_free_change_address_index + 1; c++) {
|
||||
ownedAddressesHashmap[this._getInternalAddressByIndex(c)] = true;
|
||||
}
|
||||
for (const pc of this._sender_payment_codes) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + 1; c++) {
|
||||
ownedAddressesHashmap[this._getBIP47Address(pc, c)] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (const tx of this.getTransactions()) {
|
||||
for (const output of tx.outputs) {
|
||||
|
@ -811,6 +1028,12 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
for (let c = 0; c < this.next_free_change_address_index + this.gap_limit; c++) {
|
||||
if (this._getInternalAddressByIndex(c) === address) return path + '/1/' + c;
|
||||
}
|
||||
for (const pc of this._sender_payment_codes) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
|
||||
// not technically correct but well, to have at least somethign in PSBT...
|
||||
if (this._getBIP47Address(pc, c) === address) return "m/47'/0'/0'/" + c;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -827,6 +1050,11 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
for (let c = 0; c < this.next_free_change_address_index + this.gap_limit; c++) {
|
||||
if (this._getInternalAddressByIndex(c) === address) return this._getNodePubkeyByIndex(1, c);
|
||||
}
|
||||
for (const pc of this._sender_payment_codes) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
|
||||
if (this._getBIP47Address(pc, c) === address) return this._getBIP47PubkeyByIndex(pc, c);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -844,6 +1072,11 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
for (let c = 0; c < this.next_free_change_address_index + this.gap_limit; c++) {
|
||||
if (this._getInternalAddressByIndex(c) === address) return this._getWIFByIndex(true, c);
|
||||
}
|
||||
for (const pc of this._sender_payment_codes) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
|
||||
if (this._getBIP47Address(pc, c) === address) return this._getBIP47WIF(pc, c);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -861,6 +1094,11 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
for (let c = 0; c < this.next_free_change_address_index + this.gap_limit; c++) {
|
||||
if (this._getInternalAddressByIndex(c) === cleanAddress) return true;
|
||||
}
|
||||
for (const pc of this._sender_payment_codes) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
|
||||
if (this._getBIP47Address(pc, c) === address) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1052,22 +1290,29 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
|
||||
/**
|
||||
* Creates Segwit Bech32 Bitcoin address
|
||||
*
|
||||
* @param hdNode
|
||||
* @returns {String}
|
||||
*/
|
||||
static _nodeToBech32SegwitAddress(hdNode: BIP32Interface) {
|
||||
_nodeToBech32SegwitAddress(hdNode: BIP32Interface): string {
|
||||
return bitcoin.payments.p2wpkh({
|
||||
pubkey: hdNode.publicKey,
|
||||
}).address;
|
||||
}
|
||||
|
||||
static _nodeToLegacyAddress(hdNode: BIP32Interface) {
|
||||
_nodeToLegacyAddress(hdNode: BIP32Interface): string {
|
||||
return bitcoin.payments.p2pkh({
|
||||
pubkey: hdNode.publicKey,
|
||||
}).address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Segwit P2SH Bitcoin address
|
||||
*/
|
||||
_nodeToP2shSegwitAddress(hdNode: BIP32Interface): string {
|
||||
const { address } = bitcoin.payments.p2sh({
|
||||
redeem: bitcoin.payments.p2wpkh({ pubkey: hdNode.publicKey }),
|
||||
});
|
||||
return address;
|
||||
}
|
||||
|
||||
static _getTransactionsFromHistories(histories: Record<string, ElectrumHistory[]>) {
|
||||
const txs = [];
|
||||
for (const history of Object.values(histories)) {
|
||||
|
@ -1080,14 +1325,29 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
|
||||
/**
|
||||
* Probes zero address in external hierarchy for transactions, if there are any returns TRUE.
|
||||
* Zero address is a pretty good indicator, since its a first one to fund the wallet. How can you use the wallet and
|
||||
* not fund it first?
|
||||
* Zero address is a pretty good indicator, since its a first one to fund the wallet.
|
||||
* Q: How can you use the wallet and not fund it first?
|
||||
* A: You can if it is a BIP47 wallet!
|
||||
*
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async wasEverUsed() {
|
||||
const txs = await BlueElectrum.getTransactionsByAddress(this._getExternalAddressByIndex(0));
|
||||
return txs.length > 0;
|
||||
async wasEverUsed(): Promise<boolean> {
|
||||
const txs1 = await BlueElectrum.getTransactionsByAddress(this._getExternalAddressByIndex(0));
|
||||
if (txs1.length > 0) {
|
||||
return true;
|
||||
}
|
||||
if (!this.allowBIP47()) {
|
||||
return false;
|
||||
}
|
||||
// only check BIP47 if derivation path is regular, otherwise too many wallets will be found
|
||||
if (!["m/84'/0'/0'", "m/44'/0'/0'", "m/49'/0'/0'"].includes(this.getDerivationPath() as string)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bip47_instance = this.getBIP47FromSeed();
|
||||
const address = bip47_instance.getNotificationAddress();
|
||||
const txs2 = await BlueElectrum.getTransactionsByAddress(address);
|
||||
return txs2.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1192,4 +1452,114 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
const seed = this._getSeed();
|
||||
return AbstractHDElectrumWallet.seedToFingerprint(seed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether BIP47 is enabled. This is per-wallet setting that can be changed, NOT a feature-flag
|
||||
* @returns boolean
|
||||
*/
|
||||
isBIP47Enabled(): boolean {
|
||||
return this._enable_BIP47;
|
||||
}
|
||||
|
||||
switchBIP47(value: boolean): void {
|
||||
this._enable_BIP47 = value;
|
||||
}
|
||||
|
||||
getBIP47FromSeed(): BIP47Interface {
|
||||
if (!this._bip47_instance || !this._bip47_instance.getNotificationAddress) {
|
||||
this._bip47_instance = bip47.fromBip39Seed(this.secret, undefined, this.passphrase);
|
||||
}
|
||||
|
||||
return this._bip47_instance;
|
||||
}
|
||||
|
||||
getBIP47PaymentCode(): string {
|
||||
if (!this._payment_code) {
|
||||
this._payment_code = this.getBIP47FromSeed().getSerializedPaymentCode();
|
||||
}
|
||||
|
||||
return this._payment_code;
|
||||
}
|
||||
|
||||
getBIP47NotificationAddress(): string {
|
||||
const bip47 = this.getBIP47FromSeed();
|
||||
return bip47.getNotificationAddress();
|
||||
}
|
||||
|
||||
async fetchBIP47SenderPaymentCodes(): Promise<void> {
|
||||
const bip47_instance = this.getBIP47FromSeed();
|
||||
const address = bip47_instance.getNotificationAddress();
|
||||
const histories = await BlueElectrum.multiGetHistoryByAddress([address]);
|
||||
const txHashes = histories[address].map(({ tx_hash }) => tx_hash);
|
||||
|
||||
const txHexs = await BlueElectrum.multiGetTransactionByTxid(txHashes, 50, false);
|
||||
for (const txHex of Object.values(txHexs)) {
|
||||
try {
|
||||
const paymentCode = bip47_instance.getPaymentCodeFromRawNotificationTransaction(txHex);
|
||||
if (this._sender_payment_codes.includes(paymentCode)) continue; // already have it
|
||||
|
||||
// final check if PC is even valid (could've been constructed by a buggy code, and our code would crash with that):
|
||||
try {
|
||||
BIP47Factory(ecc).fromPaymentCode(paymentCode);
|
||||
} catch (_) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this._sender_payment_codes.push(paymentCode);
|
||||
this._next_free_payment_code_address_index[paymentCode] = 0; // initialize
|
||||
this._balances_by_payment_code_index[paymentCode] = { c: 0, u: 0 };
|
||||
} catch (e) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getBIP47SenderPaymentCodes(): string[] {
|
||||
return this._sender_payment_codes;
|
||||
}
|
||||
|
||||
_hdNodeToAddress(hdNode: BIP32Interface): string {
|
||||
return this._nodeToBech32SegwitAddress(hdNode);
|
||||
}
|
||||
|
||||
_getBIP47Address(paymentCode: string, index: number): string {
|
||||
if (!this._addresses_by_payment_code[paymentCode]) this._addresses_by_payment_code[paymentCode] = [];
|
||||
|
||||
if (this._addresses_by_payment_code[paymentCode][index]) {
|
||||
return this._addresses_by_payment_code[paymentCode][index];
|
||||
}
|
||||
|
||||
const bip47_instance = this.getBIP47FromSeed();
|
||||
const senderBIP47_instance = bip47.fromPaymentCode(paymentCode);
|
||||
const remotePaymentNode = senderBIP47_instance.getPaymentCodeNode();
|
||||
const hdNode = bip47_instance.getPaymentWallet(remotePaymentNode, index);
|
||||
const address = this._hdNodeToAddress(hdNode);
|
||||
this._address_to_wif_cache[address] = hdNode.toWIF();
|
||||
this._addresses_by_payment_code[paymentCode][index] = address;
|
||||
return address;
|
||||
}
|
||||
|
||||
_getNextFreePaymentCodeAddress(paymentCode: string) {
|
||||
return this._next_free_payment_code_address_index[paymentCode] || 0;
|
||||
}
|
||||
|
||||
_getBalancesByPaymentCodeIndex(paymentCode: string): BalanceByIndex {
|
||||
return this._balances_by_payment_code_index[paymentCode] || { c: 0, u: 0 };
|
||||
}
|
||||
|
||||
_getBIP47WIF(paymentCode: string, index: number): string {
|
||||
const bip47_instance = this.getBIP47FromSeed();
|
||||
const senderBIP47_instance = bip47.fromPaymentCode(paymentCode);
|
||||
const remotePaymentNode = senderBIP47_instance.getPaymentCodeNode();
|
||||
const hdNode = bip47_instance.getPaymentWallet(remotePaymentNode, index);
|
||||
return hdNode.toWIF();
|
||||
}
|
||||
|
||||
_getBIP47PubkeyByIndex(paymentCode: string, index: number): Buffer {
|
||||
const bip47_instance = this.getBIP47FromSeed();
|
||||
const senderBIP47_instance = bip47.fromPaymentCode(paymentCode);
|
||||
const remotePaymentNode = senderBIP47_instance.getPaymentCodeNode();
|
||||
const hdNode = bip47_instance.getPaymentWallet(remotePaymentNode, index);
|
||||
return hdNode.publicKey;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,6 +143,18 @@ export class AbstractWallet {
|
|||
return BitcoinUnit.BTC;
|
||||
}
|
||||
|
||||
async allowOnchainAddress(): Promise<boolean> {
|
||||
throw new Error('allowOnchainAddress: Not implemented');
|
||||
}
|
||||
|
||||
allowBIP47(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
switchBIP47(value: boolean): void {
|
||||
throw new Error('switchBIP47: not implemented');
|
||||
}
|
||||
|
||||
allowReceive(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
@ -223,6 +235,16 @@ export class AbstractWallet {
|
|||
this._derivationPath = derivationPath;
|
||||
}
|
||||
this.secret = m[2];
|
||||
|
||||
if (derivationPath.startsWith("m/84'/0'/") && this.secret.toLowerCase().startsWith('xpub')) {
|
||||
// need to convert xpub to zpub
|
||||
this.secret = this._xpubToZpub(this.secret);
|
||||
}
|
||||
|
||||
if (derivationPath.startsWith("m/49'/0'/") && this.secret.toLowerCase().startsWith('xpub')) {
|
||||
// need to convert xpub to ypub
|
||||
this.secret = this._xpubToYpub(this.secret);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -348,6 +370,10 @@ export class AbstractWallet {
|
|||
return false;
|
||||
}
|
||||
|
||||
isBIP47Enabled(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
async wasEverUsed(): Promise<boolean> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
@ -390,6 +416,22 @@ export class AbstractWallet {
|
|||
return b58.encode(data);
|
||||
}
|
||||
|
||||
_xpubToZpub(xpub: string): string {
|
||||
let data = b58.decode(xpub);
|
||||
data = data.slice(4);
|
||||
data = Buffer.concat([Buffer.from('04b24746', 'hex'), data]);
|
||||
|
||||
return b58.encode(data);
|
||||
}
|
||||
|
||||
_xpubToYpub(xpub: string): string {
|
||||
let data = b58.decode(xpub);
|
||||
data = data.slice(4);
|
||||
data = Buffer.concat([Buffer.from('049d7cb2', 'hex'), data]);
|
||||
|
||||
return b58.encode(data);
|
||||
}
|
||||
|
||||
prepareForSerialization(): void {}
|
||||
|
||||
/*
|
||||
|
|
|
@ -23,6 +23,10 @@ export class HDLegacyElectrumSeedP2PKHWallet extends HDLegacyP2PKHWallet {
|
|||
return mn.validateMnemonic(this.secret, PREFIX);
|
||||
}
|
||||
|
||||
allowBIP47() {
|
||||
return false;
|
||||
}
|
||||
|
||||
async generate() {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
|
|
@ -34,6 +34,10 @@ export class HDLegacyP2PKHWallet extends AbstractHDElectrumWallet {
|
|||
return true;
|
||||
}
|
||||
|
||||
allowBIP47() {
|
||||
return true;
|
||||
}
|
||||
|
||||
getXpub() {
|
||||
if (this._xpub) {
|
||||
return this._xpub; // cache hit
|
||||
|
@ -48,44 +52,8 @@ export class HDLegacyP2PKHWallet extends AbstractHDElectrumWallet {
|
|||
return this._xpub;
|
||||
}
|
||||
|
||||
_getNodeAddressByIndex(node, index) {
|
||||
index = index * 1; // cast to int
|
||||
if (node === 0) {
|
||||
if (this.external_addresses_cache[index]) return this.external_addresses_cache[index]; // cache hit
|
||||
}
|
||||
|
||||
if (node === 1) {
|
||||
if (this.internal_addresses_cache[index]) return this.internal_addresses_cache[index]; // cache hit
|
||||
}
|
||||
|
||||
if (node === 0 && !this._node0) {
|
||||
const xpub = this.getXpub();
|
||||
const hdNode = bip32.fromBase58(xpub);
|
||||
this._node0 = hdNode.derive(node);
|
||||
}
|
||||
|
||||
if (node === 1 && !this._node1) {
|
||||
const xpub = this.getXpub();
|
||||
const hdNode = bip32.fromBase58(xpub);
|
||||
this._node1 = hdNode.derive(node);
|
||||
}
|
||||
|
||||
let address;
|
||||
if (node === 0) {
|
||||
address = this.constructor._nodeToLegacyAddress(this._node0.derive(index));
|
||||
}
|
||||
|
||||
if (node === 1) {
|
||||
address = this.constructor._nodeToLegacyAddress(this._node1.derive(index));
|
||||
}
|
||||
|
||||
if (node === 0) {
|
||||
return (this.external_addresses_cache[index] = address);
|
||||
}
|
||||
|
||||
if (node === 1) {
|
||||
return (this.internal_addresses_cache[index] = address);
|
||||
}
|
||||
_hdNodeToAddress(hdNode) {
|
||||
return this._nodeToLegacyAddress(hdNode);
|
||||
}
|
||||
|
||||
async fetchUtxo() {
|
||||
|
|
|
@ -46,4 +46,8 @@ export class HDSegwitBech32Wallet extends AbstractHDElectrumWallet {
|
|||
allowXpub() {
|
||||
return true;
|
||||
}
|
||||
|
||||
allowBIP47() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,10 @@ export class HDSegwitElectrumSeedP2WPKHWallet extends HDSegwitBech32Wallet {
|
|||
return mn.validateMnemonic(this.secret, PREFIX);
|
||||
}
|
||||
|
||||
allowBIP47() {
|
||||
return false;
|
||||
}
|
||||
|
||||
async generate() {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
|
|
@ -40,44 +40,8 @@ export class HDSegwitP2SHWallet extends AbstractHDElectrumWallet {
|
|||
return true;
|
||||
}
|
||||
|
||||
_getNodeAddressByIndex(node, index) {
|
||||
index = index * 1; // cast to int
|
||||
if (node === 0) {
|
||||
if (this.external_addresses_cache[index]) return this.external_addresses_cache[index]; // cache hit
|
||||
}
|
||||
|
||||
if (node === 1) {
|
||||
if (this.internal_addresses_cache[index]) return this.internal_addresses_cache[index]; // cache hit
|
||||
}
|
||||
|
||||
if (node === 0 && !this._node0) {
|
||||
const xpub = this.constructor._ypubToXpub(this.getXpub());
|
||||
const hdNode = bip32.fromBase58(xpub);
|
||||
this._node0 = hdNode.derive(0);
|
||||
}
|
||||
|
||||
if (node === 1 && !this._node1) {
|
||||
const xpub = this.constructor._ypubToXpub(this.getXpub());
|
||||
const hdNode = bip32.fromBase58(xpub);
|
||||
this._node1 = hdNode.derive(1);
|
||||
}
|
||||
|
||||
let address;
|
||||
if (node === 0) {
|
||||
address = this.constructor._nodeToP2shSegwitAddress(this._node0.derive(index));
|
||||
}
|
||||
|
||||
if (node === 1) {
|
||||
address = this.constructor._nodeToP2shSegwitAddress(this._node1.derive(index));
|
||||
}
|
||||
|
||||
if (node === 0) {
|
||||
return (this.external_addresses_cache[index] = address);
|
||||
}
|
||||
|
||||
if (node === 1) {
|
||||
return (this.internal_addresses_cache[index] = address);
|
||||
}
|
||||
_hdNodeToAddress(hdNode) {
|
||||
return this._nodeToP2shSegwitAddress(hdNode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -134,18 +98,6 @@ export class HDSegwitP2SHWallet extends AbstractHDElectrumWallet {
|
|||
return psbt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Segwit P2SH Bitcoin address
|
||||
* @param hdNode
|
||||
* @returns {String}
|
||||
*/
|
||||
static _nodeToP2shSegwitAddress(hdNode) {
|
||||
const { address } = bitcoin.payments.p2sh({
|
||||
redeem: bitcoin.payments.p2wpkh({ pubkey: hdNode.publicKey }),
|
||||
});
|
||||
return address;
|
||||
}
|
||||
|
||||
isSegwit() {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ export class LightningCustodianWallet extends LegacyWallet {
|
|||
|
||||
async payInvoice(invoice, freeAmount = 0) {
|
||||
const response = await this._api.post('/payinvoice', {
|
||||
body: { invoice: invoice, amount: freeAmount },
|
||||
body: { invoice, amount: freeAmount },
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Content-Type': 'application/json',
|
||||
|
@ -215,7 +215,7 @@ export class LightningCustodianWallet extends LegacyWallet {
|
|||
|
||||
async addInvoice(amt, memo) {
|
||||
const response = await this._api.post('/addinvoice', {
|
||||
body: { amt: amt + '', memo: memo },
|
||||
body: { amt: amt + '', memo },
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Content-Type': 'application/json',
|
||||
|
@ -254,7 +254,7 @@ export class LightningCustodianWallet extends LegacyWallet {
|
|||
password = this.secret.replace('lndhub://', '').split(':')[1];
|
||||
}
|
||||
const response = await this._api.post('/auth?type=auth', {
|
||||
body: { login: login, password: password },
|
||||
body: { login, password },
|
||||
headers: { 'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json' },
|
||||
});
|
||||
|
||||
|
|
|
@ -567,11 +567,11 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
}
|
||||
if (secret.indexOf('sortedmulti(') !== -1 && json.descriptor) {
|
||||
if (json.label) this.setLabel(json.label);
|
||||
if (json.descriptor.startsWith('wsh(')) {
|
||||
this.setNativeSegwit();
|
||||
} else if (json.descriptor.startsWith('sh(wsh(')) {
|
||||
if (json.descriptor.includes('sh(wsh(')) {
|
||||
this.setWrappedSegwit();
|
||||
} else if (json.descriptor.startsWith('sh(')) {
|
||||
} else if (json.descriptor.includes('wsh(')) {
|
||||
this.setNativeSegwit();
|
||||
} else if (json.descriptor.includes('sh(')) {
|
||||
this.setLegacy();
|
||||
}
|
||||
|
||||
|
@ -624,8 +624,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
|
||||
for (const pk of json.extendedPublicKeys) {
|
||||
const path = this.constructor.isPathValid(json.bip32Path) ? json.bip32Path : "m/1'";
|
||||
// wtf, where caravan stores fingerprints..?
|
||||
this.addCosigner(pk.xpub, '00000000', path);
|
||||
this.addCosigner(pk.xpub, pk.xfp ?? '00000000', path);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -67,6 +67,10 @@ export class SLIP39LegacyP2PKHWallet extends HDLegacyP2PKHWallet {
|
|||
static type = 'SLIP39legacyP2PKH';
|
||||
static typeReadable = 'SLIP39 Legacy (P2PKH)';
|
||||
|
||||
allowBIP47() {
|
||||
return false;
|
||||
}
|
||||
|
||||
_getSeed = SLIP39Mixin._getSeed;
|
||||
validateMnemonic = SLIP39Mixin.validateMnemonic;
|
||||
setSecret = SLIP39Mixin.setSecret;
|
||||
|
@ -87,6 +91,10 @@ export class SLIP39SegwitBech32Wallet extends HDSegwitBech32Wallet {
|
|||
static type = 'SLIP39segwitBech32';
|
||||
static typeReadable = 'SLIP39 SegWit (Bech32)';
|
||||
|
||||
allowBIP47() {
|
||||
return false;
|
||||
}
|
||||
|
||||
_getSeed = SLIP39Mixin._getSeed;
|
||||
validateMnemonic = SLIP39Mixin.validateMnemonic;
|
||||
setSecret = SLIP39Mixin.setSecret;
|
||||
|
|
|
@ -100,6 +100,7 @@ export class WatchOnlyWallet extends LegacyWallet {
|
|||
if (this._hdWalletInstance) {
|
||||
delete this._hdWalletInstance._node0;
|
||||
delete this._hdWalletInstance._node1;
|
||||
delete this._hdWalletInstance._bip47_instance;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -238,7 +238,12 @@ class AmountInput extends Component {
|
|||
});
|
||||
|
||||
return (
|
||||
<TouchableWithoutFeedback disabled={this.props.pointerEvents === 'none'} onPress={() => this.textInput.focus()}>
|
||||
<TouchableWithoutFeedback
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.enter_amount}
|
||||
disabled={this.props.pointerEvents === 'none'}
|
||||
onPress={() => this.textInput.focus()}
|
||||
>
|
||||
<>
|
||||
<View style={styles.root}>
|
||||
{!disabled && <View style={[styles.center, stylesHook.center]} />}
|
||||
|
@ -289,6 +294,7 @@ class AmountInput extends Component {
|
|||
{!disabled && amount !== BitcoinUnit.MAX && (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.change_input_currency}
|
||||
testID="changeAmountUnitButton"
|
||||
style={styles.changeAmountUnit}
|
||||
onPress={this.changeAmountUnit}
|
||||
|
@ -306,6 +312,8 @@ class AmountInput extends Component {
|
|||
</BlueText>
|
||||
<View style={styles.spacing8} />
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.refresh}
|
||||
onPress={this.updateRate}
|
||||
disabled={this.state.isRateBeingUpdated}
|
||||
style={this.state.isRateBeingUpdated ? styles.disabledButton : styles.enabledButon}
|
||||
|
|
|
@ -30,6 +30,8 @@ export const ArrowPicker = (props: ArrowPickerProps) => {
|
|||
return (
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc.send.dynamic_prev}
|
||||
onPress={() => {
|
||||
Keyboard.dismiss();
|
||||
let newIndex = keyIndex;
|
||||
|
@ -48,12 +50,16 @@ export const ArrowPicker = (props: ArrowPickerProps) => {
|
|||
styles.wrapperCustom,
|
||||
]}
|
||||
>
|
||||
{/*
|
||||
// @ts-ignore: Ignore */}
|
||||
<Icon size={24} name="chevron-left" type="ionicons" tvParallaxProperties={undefined} />
|
||||
</Pressable>
|
||||
<View style={{ width: 200 }}>
|
||||
<Text style={[styles.text, stylesHook.text]}>{props.isItemUnknown ? loc.send.fee_custom : keys[keyIndex]}</Text>
|
||||
</View>
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc.send.dynamic_next}
|
||||
onPress={() => {
|
||||
Keyboard.dismiss();
|
||||
let newIndex = keyIndex;
|
||||
|
@ -72,6 +78,8 @@ export const ArrowPicker = (props: ArrowPickerProps) => {
|
|||
styles.wrapperCustom,
|
||||
]}
|
||||
>
|
||||
{/*
|
||||
// @ts-ignore: Ignore */}
|
||||
<Icon size={24} name="chevron-right" type="ionicons" tvParallaxProperties={undefined} />
|
||||
</Pressable>
|
||||
</View>
|
||||
|
|
|
@ -38,7 +38,7 @@ const Button = props => {
|
|||
const opacity = { opacity: disabled ? 0.5 : 1.0 };
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={onPress} disabled={disabled} style={opacity}>
|
||||
<TouchableOpacity accessibilityRole="button" onPress={onPress} disabled={disabled} style={opacity}>
|
||||
<View style={[styles.buttonContainer, buttonStyles()]}>
|
||||
<Text style={[styles.text, textStyles()]}>{text}</Text>
|
||||
</View>
|
||||
|
|
|
@ -7,7 +7,7 @@ import React from 'react';
|
|||
export const LdkButton = props => {
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<TouchableOpacity onPress={props.onPress}>
|
||||
<TouchableOpacity accessibilityRole="button" onPress={props.onPress}>
|
||||
<View
|
||||
style={{
|
||||
borderColor: (props.active && colors.lnborderColor) || colors.buttonDisabledBackgroundColor,
|
||||
|
|
|
@ -133,7 +133,7 @@ const MultipleStepsListItem = props => {
|
|||
</>
|
||||
)}
|
||||
{!showActivityIndicator && props.rightButton && checked && (
|
||||
<View style={styles.rightButtonContainer} accessibilityComponentType>
|
||||
<View style={styles.rightButtonContainer}>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
disabled={props.rightButton.disabled}
|
||||
|
|
|
@ -4,12 +4,58 @@ import QRCode from 'react-native-qrcode-svg';
|
|||
import ToolTipMenu from './TooltipMenu';
|
||||
import Share from 'react-native-share';
|
||||
import loc from '../loc';
|
||||
import PropTypes from 'prop-types';
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
import { useTheme } from '@react-navigation/native';
|
||||
|
||||
const QRCodeComponent = ({
|
||||
value,
|
||||
interface QRCodeComponentProps {
|
||||
value: string;
|
||||
isLogoRendered?: boolean;
|
||||
isMenuAvailable?: boolean;
|
||||
logoSize?: number;
|
||||
size?: number;
|
||||
ecl?: 'H' | 'Q' | 'M' | 'L';
|
||||
onError?: () => void;
|
||||
}
|
||||
|
||||
interface ActionIcons {
|
||||
iconType: 'SYSTEM';
|
||||
iconValue: string;
|
||||
}
|
||||
|
||||
interface ActionType {
|
||||
Share: 'share';
|
||||
Copy: 'copy';
|
||||
}
|
||||
|
||||
interface Action {
|
||||
id: string;
|
||||
text: string;
|
||||
icon: ActionIcons;
|
||||
}
|
||||
|
||||
const actionKeys: ActionType = {
|
||||
Share: 'share',
|
||||
Copy: 'copy',
|
||||
};
|
||||
|
||||
interface ActionIcons {
|
||||
iconType: 'SYSTEM';
|
||||
iconValue: string;
|
||||
}
|
||||
|
||||
const actionIcons: { [key: string]: ActionIcons } = {
|
||||
Share: {
|
||||
iconType: 'SYSTEM',
|
||||
iconValue: 'square.and.arrow.up',
|
||||
},
|
||||
Copy: {
|
||||
iconType: 'SYSTEM',
|
||||
iconValue: 'doc.on.doc',
|
||||
},
|
||||
};
|
||||
|
||||
const QRCodeComponent: React.FC<QRCodeComponentProps> = ({
|
||||
value = '',
|
||||
isLogoRendered = true,
|
||||
isMenuAvailable = true,
|
||||
logoSize = 90,
|
||||
|
@ -17,39 +63,39 @@ const QRCodeComponent = ({
|
|||
ecl = 'H',
|
||||
onError = () => {},
|
||||
}) => {
|
||||
const qrCode = useRef();
|
||||
const qrCode = useRef<any>();
|
||||
const { colors } = useTheme();
|
||||
|
||||
const handleShareQRCode = () => {
|
||||
qrCode.current.toDataURL(data => {
|
||||
qrCode.current.toDataURL((data: string) => {
|
||||
const shareImageBase64 = {
|
||||
url: `data:image/png;base64,${data}`,
|
||||
};
|
||||
Share.open(shareImageBase64).catch(error => console.log(error));
|
||||
Share.open(shareImageBase64).catch((error: any) => console.log(error));
|
||||
});
|
||||
};
|
||||
|
||||
const onPressMenuItem = id => {
|
||||
if (id === QRCodeComponent.actionKeys.Share) {
|
||||
const onPressMenuItem = (id: string) => {
|
||||
if (id === actionKeys.Share) {
|
||||
handleShareQRCode();
|
||||
} else if (id === QRCodeComponent.actionKeys.Copy) {
|
||||
} else if (id === actionKeys.Copy) {
|
||||
qrCode.current.toDataURL(Clipboard.setImage);
|
||||
}
|
||||
};
|
||||
|
||||
const menuActions = () => {
|
||||
const actions = [];
|
||||
const menuActions = (): Action[] => {
|
||||
const actions: Action[] = [];
|
||||
if (Platform.OS === 'ios' || Platform.OS === 'macos') {
|
||||
actions.push({
|
||||
id: QRCodeComponent.actionKeys.Copy,
|
||||
id: actionKeys.Copy,
|
||||
text: loc.transactions.details_copy,
|
||||
icon: QRCodeComponent.actionIcons.Copy,
|
||||
icon: actionIcons.Copy,
|
||||
});
|
||||
}
|
||||
actions.push({
|
||||
id: QRCodeComponent.actionKeys.Share,
|
||||
id: actionKeys.Share,
|
||||
text: loc.receive.details_share,
|
||||
icon: QRCodeComponent.actionIcons.Share,
|
||||
icon: actionIcons.Share,
|
||||
});
|
||||
return actions;
|
||||
};
|
||||
|
@ -61,10 +107,11 @@ const QRCodeComponent = ({
|
|||
size={size}
|
||||
logoSize={logoSize}
|
||||
color="#000000"
|
||||
// @ts-ignore: logoBackgroundColor is not in the type definition
|
||||
logoBackgroundColor={colors.brandingColor}
|
||||
backgroundColor="#FFFFFF"
|
||||
ecl={ecl}
|
||||
getRef={qrCode}
|
||||
getRef={(c: any) => (qrCode.current = c)}
|
||||
onError={onError}
|
||||
/>
|
||||
);
|
||||
|
@ -87,29 +134,3 @@ export default QRCodeComponent;
|
|||
const styles = StyleSheet.create({
|
||||
qrCodeContainer: { borderWidth: 6, borderRadius: 8, borderColor: '#FFFFFF' },
|
||||
});
|
||||
|
||||
QRCodeComponent.actionKeys = {
|
||||
Share: 'share',
|
||||
Copy: 'copy',
|
||||
};
|
||||
|
||||
QRCodeComponent.actionIcons = {
|
||||
Share: {
|
||||
iconType: 'SYSTEM',
|
||||
iconValue: 'square.and.arrow.up',
|
||||
},
|
||||
Copy: {
|
||||
iconType: 'SYSTEM',
|
||||
iconValue: 'doc.on.doc',
|
||||
},
|
||||
};
|
||||
|
||||
QRCodeComponent.propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
isMenuAvailable: PropTypes.bool,
|
||||
size: PropTypes.number,
|
||||
ecl: PropTypes.string,
|
||||
isLogoRendered: PropTypes.bool,
|
||||
onError: PropTypes.func,
|
||||
logoSize: PropTypes.number,
|
||||
};
|
|
@ -19,7 +19,7 @@ export const SquareButton = forwardRef((props, ref) => {
|
|||
flex: 1,
|
||||
borderWidth: 0.7,
|
||||
borderColor: 'transparent',
|
||||
backgroundColor: backgroundColor,
|
||||
backgroundColor,
|
||||
minHeight: 50,
|
||||
height: 50,
|
||||
maxHeight: 50,
|
||||
|
|
|
@ -59,11 +59,18 @@ const ToolTipMenu = (props, ref) => {
|
|||
}}
|
||||
style={buttonStyle}
|
||||
>
|
||||
{props.onPress ? <TouchableOpacity onPress={props.onPress}>{props.children}</TouchableOpacity> : props.children}
|
||||
{props.onPress ? (
|
||||
<TouchableOpacity accessibilityRole="button" onPress={props.onPress}>
|
||||
{props.children}
|
||||
</TouchableOpacity>
|
||||
) : (
|
||||
props.children
|
||||
)}
|
||||
</ContextMenuButton>
|
||||
) : (
|
||||
<ContextMenuView
|
||||
ref={ref}
|
||||
internalCleanupMode="viewController"
|
||||
onPressMenuItem={({ nativeEvent }) => {
|
||||
props.onPressMenuItem(nativeEvent.actionKey);
|
||||
}}
|
||||
|
@ -81,7 +88,13 @@ const ToolTipMenu = (props, ref) => {
|
|||
}
|
||||
: {})}
|
||||
>
|
||||
{props.onPress ? <TouchableOpacity onPress={props.onPress}>{props.children}</TouchableOpacity> : props.children}
|
||||
{props.onPress ? (
|
||||
<TouchableOpacity accessibilityRole="button" onPress={props.onPress}>
|
||||
{props.children}
|
||||
</TouchableOpacity>
|
||||
) : (
|
||||
props.children
|
||||
)}
|
||||
</ContextMenuView>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,305 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Image, Text, TouchableOpacity, View, InteractionManager, I18nManager, StyleSheet } from 'react-native';
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import { LightningCustodianWallet, LightningLdkWallet, MultisigHDWallet } from '../class';
|
||||
import { BitcoinUnit } from '../models/bitcoinUnits';
|
||||
import WalletGradient from '../class/wallet-gradient';
|
||||
import Biometric from '../class/biometrics';
|
||||
import loc, { formatBalance } from '../loc';
|
||||
import { BlueStorageContext } from '../blue_modules/storage-context';
|
||||
import ToolTipMenu from './TooltipMenu';
|
||||
import { BluePrivateBalance } from '../BlueComponents';
|
||||
|
||||
export default class TransactionsNavigationHeader extends Component {
|
||||
static propTypes = {
|
||||
wallet: PropTypes.shape().isRequired,
|
||||
onWalletUnitChange: PropTypes.func,
|
||||
navigation: PropTypes.shape(),
|
||||
onManageFundsPressed: PropTypes.func,
|
||||
};
|
||||
|
||||
static actionKeys = {
|
||||
CopyToClipboard: 'copyToClipboard',
|
||||
WalletBalanceVisibility: 'walletBalanceVisibility',
|
||||
Refill: 'refill',
|
||||
RefillWithExternalWallet: 'qrcode',
|
||||
};
|
||||
|
||||
static actionIcons = {
|
||||
Eye: {
|
||||
iconType: 'SYSTEM',
|
||||
iconValue: 'eye',
|
||||
},
|
||||
EyeSlash: {
|
||||
iconType: 'SYSTEM',
|
||||
iconValue: 'eye.slash',
|
||||
},
|
||||
Clipboard: {
|
||||
iconType: 'SYSTEM',
|
||||
iconValue: 'doc.on.doc',
|
||||
},
|
||||
Refill: {
|
||||
iconType: 'SYSTEM',
|
||||
iconValue: 'goforward.plus',
|
||||
},
|
||||
RefillWithExternalWallet: {
|
||||
iconType: 'SYSTEM',
|
||||
iconValue: 'qrcode',
|
||||
},
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(props) {
|
||||
return { wallet: props.wallet, onWalletUnitChange: props.onWalletUnitChange };
|
||||
}
|
||||
|
||||
static contextType = BlueStorageContext;
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
wallet: props.wallet,
|
||||
walletPreviousPreferredUnit: props.wallet.getPreferredBalanceUnit(),
|
||||
allowOnchainAddress: false,
|
||||
};
|
||||
}
|
||||
|
||||
menuRef = React.createRef();
|
||||
|
||||
handleCopyPress = _item => {
|
||||
Clipboard.setString(formatBalance(this.state.wallet.getBalance(), this.state.wallet.getPreferredBalanceUnit()).toString());
|
||||
};
|
||||
|
||||
componentDidUpdate(prevState) {
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
if (prevState.wallet.getID() !== this.state.wallet.getID() && this.state.wallet.type === LightningCustodianWallet.type) {
|
||||
this.verifyIfWalletAllowsOnchainAddress();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
verifyIfWalletAllowsOnchainAddress = () => {
|
||||
if (this.state.wallet.type === LightningCustodianWallet.type) {
|
||||
this.state.wallet
|
||||
.allowOnchainAddress()
|
||||
.then(value => this.setState({ allowOnchainAddress: value }))
|
||||
.catch(e => {
|
||||
console.log('This Lndhub wallet does not have an onchain address API.');
|
||||
this.setState({ allowOnchainAddress: false });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.verifyIfWalletAllowsOnchainAddress();
|
||||
}
|
||||
|
||||
handleBalanceVisibility = async _item => {
|
||||
const wallet = this.state.wallet;
|
||||
|
||||
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
|
||||
|
||||
if (isBiometricsEnabled && wallet.hideBalance) {
|
||||
if (!(await Biometric.unlockWithBiometrics())) {
|
||||
return this.props.navigation.goBack();
|
||||
}
|
||||
}
|
||||
|
||||
wallet.hideBalance = !wallet.hideBalance;
|
||||
this.setState({ wallet });
|
||||
await this.context.saveToDisk();
|
||||
};
|
||||
|
||||
changeWalletBalanceUnit = () => {
|
||||
this.menuRef.current?.dismissMenu();
|
||||
let walletPreviousPreferredUnit = this.state.wallet.getPreferredBalanceUnit();
|
||||
const wallet = this.state.wallet;
|
||||
if (walletPreviousPreferredUnit === BitcoinUnit.BTC) {
|
||||
wallet.preferredBalanceUnit = BitcoinUnit.SATS;
|
||||
walletPreviousPreferredUnit = BitcoinUnit.BTC;
|
||||
} else if (walletPreviousPreferredUnit === BitcoinUnit.SATS) {
|
||||
wallet.preferredBalanceUnit = BitcoinUnit.LOCAL_CURRENCY;
|
||||
walletPreviousPreferredUnit = BitcoinUnit.SATS;
|
||||
} else if (walletPreviousPreferredUnit === BitcoinUnit.LOCAL_CURRENCY) {
|
||||
wallet.preferredBalanceUnit = BitcoinUnit.BTC;
|
||||
walletPreviousPreferredUnit = BitcoinUnit.BTC;
|
||||
} else {
|
||||
wallet.preferredBalanceUnit = BitcoinUnit.BTC;
|
||||
walletPreviousPreferredUnit = BitcoinUnit.BTC;
|
||||
}
|
||||
|
||||
this.setState({ wallet, walletPreviousPreferredUnit: walletPreviousPreferredUnit }, () => {
|
||||
this.props.onWalletUnitChange(wallet);
|
||||
});
|
||||
};
|
||||
|
||||
manageFundsPressed = id => {
|
||||
this.props.onManageFundsPressed(id);
|
||||
};
|
||||
|
||||
onPressMenuItem = id => {
|
||||
if (id === TransactionsNavigationHeader.actionKeys.WalletBalanceVisibility) {
|
||||
this.handleBalanceVisibility();
|
||||
} else if (id === TransactionsNavigationHeader.actionKeys.CopyToClipboard) {
|
||||
this.handleCopyPress();
|
||||
}
|
||||
};
|
||||
|
||||
toolTipMenuActions = [
|
||||
{
|
||||
id: TransactionsNavigationHeader.actionKeys.Refill,
|
||||
text: loc.lnd.refill,
|
||||
icon: TransactionsNavigationHeader.actionIcons.Refill,
|
||||
},
|
||||
{
|
||||
id: TransactionsNavigationHeader.actionKeys.RefillWithExternalWallet,
|
||||
text: loc.lnd.refill_external,
|
||||
icon: TransactionsNavigationHeader.actionIcons.RefillWithExternalWallet,
|
||||
},
|
||||
];
|
||||
|
||||
render() {
|
||||
const balance =
|
||||
!this.state.wallet.hideBalance &&
|
||||
formatBalance(this.state.wallet.getBalance(), this.state.wallet.getPreferredBalanceUnit(), true).toString();
|
||||
|
||||
return (
|
||||
<LinearGradient
|
||||
colors={WalletGradient.gradientsFor(this.state.wallet.type)}
|
||||
style={styles.lineaderGradient}
|
||||
{...WalletGradient.linearGradientProps(this.state.wallet.type)}
|
||||
>
|
||||
<Image
|
||||
source={(() => {
|
||||
switch (this.state.wallet.type) {
|
||||
case LightningLdkWallet.type:
|
||||
case LightningCustodianWallet.type:
|
||||
return I18nManager.isRTL ? require('../img/lnd-shape-rtl.png') : require('../img/lnd-shape.png');
|
||||
case MultisigHDWallet.type:
|
||||
return I18nManager.isRTL ? require('../img/vault-shape-rtl.png') : require('../img/vault-shape.png');
|
||||
default:
|
||||
return I18nManager.isRTL ? require('../img/btc-shape-rtl.png') : require('../img/btc-shape.png');
|
||||
}
|
||||
})()}
|
||||
style={styles.chainIcon}
|
||||
/>
|
||||
<Text testID="WalletLabel" numberOfLines={1} style={styles.walletLabel}>
|
||||
{this.state.wallet.getLabel()}
|
||||
</Text>
|
||||
<ToolTipMenu
|
||||
onPress={this.changeWalletBalanceUnit}
|
||||
ref={this.menuRef}
|
||||
title={loc.wallets.balance}
|
||||
onPressMenuItem={this.onPressMenuItem}
|
||||
actions={
|
||||
this.state.wallet.hideBalance
|
||||
? [
|
||||
{
|
||||
id: TransactionsNavigationHeader.actionKeys.WalletBalanceVisibility,
|
||||
text: loc.transactions.details_balance_show,
|
||||
icon: TransactionsNavigationHeader.actionIcons.Eye,
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
id: TransactionsNavigationHeader.actionKeys.WalletBalanceVisibility,
|
||||
text: loc.transactions.details_balance_hide,
|
||||
icon: TransactionsNavigationHeader.actionIcons.EyeSlash,
|
||||
},
|
||||
{
|
||||
id: TransactionsNavigationHeader.actionKeys.CopyToClipboard,
|
||||
text: loc.transactions.details_copy,
|
||||
icon: TransactionsNavigationHeader.actionIcons.Clipboard,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View style={styles.balance}>
|
||||
{this.state.wallet.hideBalance ? (
|
||||
<BluePrivateBalance />
|
||||
) : (
|
||||
<Text
|
||||
testID="WalletBalance"
|
||||
key={balance} // force component recreation on balance change. To fix right-to-left languages, like Farsi
|
||||
numberOfLines={1}
|
||||
adjustsFontSizeToFit
|
||||
style={styles.walletBalance}
|
||||
>
|
||||
{balance}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
</ToolTipMenu>
|
||||
{this.state.wallet.type === LightningCustodianWallet.type && this.state.allowOnchainAddress && (
|
||||
<ToolTipMenu
|
||||
isMenuPrimaryAction
|
||||
isButton
|
||||
onPressMenuItem={this.manageFundsPressed}
|
||||
actions={this.toolTipMenuActions}
|
||||
buttonStyle={styles.manageFundsButton}
|
||||
>
|
||||
<Text style={styles.manageFundsButtonText}>{loc.lnd.title}</Text>
|
||||
</ToolTipMenu>
|
||||
)}
|
||||
{this.state.wallet.type === LightningLdkWallet.type && (
|
||||
<TouchableOpacity accessibilityRole="button" onPress={this.manageFundsPressed}>
|
||||
<View style={styles.manageFundsButton}>
|
||||
<Text style={styles.manageFundsButtonText}>{loc.lnd.title}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{this.state.wallet.type === MultisigHDWallet.type && (
|
||||
<TouchableOpacity accessibilityRole="button" onPress={this.manageFundsPressed}>
|
||||
<View style={styles.manageFundsButton}>
|
||||
<Text style={styles.manageFundsButtonText}>{loc.multisig.manage_keys}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</LinearGradient>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
lineaderGradient: {
|
||||
padding: 15,
|
||||
minHeight: 140,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
chainIcon: {
|
||||
width: 99,
|
||||
height: 94,
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
},
|
||||
walletLabel: {
|
||||
backgroundColor: 'transparent',
|
||||
fontSize: 19,
|
||||
color: '#fff',
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
},
|
||||
walletBalance: {
|
||||
backgroundColor: 'transparent',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 36,
|
||||
color: '#fff',
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
},
|
||||
manageFundsButton: {
|
||||
marginTop: 14,
|
||||
marginBottom: 10,
|
||||
backgroundColor: 'rgba(255,255,255,0.2)',
|
||||
borderRadius: 9,
|
||||
minHeight: 39,
|
||||
alignSelf: 'flex-start',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
manageFundsButtonText: {
|
||||
fontWeight: '500',
|
||||
fontSize: 14,
|
||||
color: '#FFFFFF',
|
||||
padding: 12,
|
||||
},
|
||||
});
|
315
components/TransactionsNavigationHeader.tsx
Normal file
315
components/TransactionsNavigationHeader.tsx
Normal file
|
@ -0,0 +1,315 @@
|
|||
import React, { useState, useEffect, useRef, useContext, useCallback, useMemo } from 'react';
|
||||
import { Image, Text, TouchableOpacity, View, I18nManager, StyleSheet } from 'react-native';
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import { AbstractWallet, LightningCustodianWallet, LightningLdkWallet, MultisigHDWallet } from '../class';
|
||||
import { BitcoinUnit } from '../models/bitcoinUnits';
|
||||
import WalletGradient from '../class/wallet-gradient';
|
||||
import Biometric from '../class/biometrics';
|
||||
import loc, { formatBalance } from '../loc';
|
||||
import { BlueStorageContext } from '../blue_modules/storage-context';
|
||||
import ToolTipMenu from './TooltipMenu';
|
||||
import { BluePrivateBalance } from '../BlueComponents';
|
||||
|
||||
interface TransactionsNavigationHeaderProps {
|
||||
wallet: AbstractWallet;
|
||||
onWalletUnitChange?: (wallet: any) => void;
|
||||
navigation: {
|
||||
navigate: (route: string, params?: any) => void;
|
||||
goBack: () => void;
|
||||
};
|
||||
onManageFundsPressed?: (id: string) => void; // Add a type definition for this prop
|
||||
actionKeys: {
|
||||
CopyToClipboard: 'copyToClipboard';
|
||||
WalletBalanceVisibility: 'walletBalanceVisibility';
|
||||
Refill: 'refill';
|
||||
RefillWithExternalWallet: 'qrcode';
|
||||
};
|
||||
}
|
||||
|
||||
const TransactionsNavigationHeader: React.FC<TransactionsNavigationHeaderProps> = ({
|
||||
// @ts-ignore: Ugh
|
||||
wallet: initialWallet,
|
||||
// @ts-ignore: Ugh
|
||||
onWalletUnitChange,
|
||||
// @ts-ignore: Ugh
|
||||
navigation,
|
||||
// @ts-ignore: Ugh
|
||||
onManageFundsPressed,
|
||||
}) => {
|
||||
const [wallet, setWallet] = useState(initialWallet);
|
||||
const [allowOnchainAddress, setAllowOnchainAddress] = useState(false);
|
||||
|
||||
const context = useContext(BlueStorageContext);
|
||||
const menuRef = useRef(null);
|
||||
|
||||
const verifyIfWalletAllowsOnchainAddress = useCallback(() => {
|
||||
if (wallet.type === LightningCustodianWallet.type) {
|
||||
wallet
|
||||
.allowOnchainAddress()
|
||||
.then((value: boolean) => setAllowOnchainAddress(value))
|
||||
.catch((e: any) => {
|
||||
console.log('This Lndhub wallet does not have an onchain address API.');
|
||||
setAllowOnchainAddress(false);
|
||||
});
|
||||
}
|
||||
}, [wallet]);
|
||||
|
||||
useEffect(() => {
|
||||
verifyIfWalletAllowsOnchainAddress();
|
||||
}, [wallet, verifyIfWalletAllowsOnchainAddress]);
|
||||
|
||||
const handleCopyPress = () => {
|
||||
Clipboard.setString(formatBalance(wallet.getBalance(), wallet.getPreferredBalanceUnit()).toString());
|
||||
};
|
||||
|
||||
const updateWalletVisibility = (wallet: AbstractWallet, newHideBalance: boolean) => {
|
||||
wallet.hideBalance = newHideBalance;
|
||||
return wallet;
|
||||
};
|
||||
|
||||
const handleBalanceVisibility = async () => {
|
||||
// @ts-ignore: Gotta update this class
|
||||
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
|
||||
|
||||
if (isBiometricsEnabled && wallet.hideBalance) {
|
||||
// @ts-ignore: Ugh
|
||||
if (!(await Biometric.unlockWithBiometrics())) {
|
||||
return navigation.goBack();
|
||||
}
|
||||
}
|
||||
|
||||
const updatedWallet = updateWalletVisibility(wallet, !wallet.hideBalance);
|
||||
setWallet(updatedWallet);
|
||||
context.saveToDisk();
|
||||
};
|
||||
|
||||
const updateWalletWithNewUnit = (wallet: AbstractWallet, newPreferredUnit: BitcoinUnit) => {
|
||||
wallet.preferredBalanceUnit = newPreferredUnit;
|
||||
return wallet;
|
||||
};
|
||||
|
||||
const changeWalletBalanceUnit = () => {
|
||||
// @ts-ignore: Ugh
|
||||
menuRef.current?.dismissMenu();
|
||||
let newWalletPreferredUnit = wallet.getPreferredBalanceUnit();
|
||||
|
||||
if (newWalletPreferredUnit === BitcoinUnit.BTC) {
|
||||
newWalletPreferredUnit = BitcoinUnit.SATS;
|
||||
} else if (newWalletPreferredUnit === BitcoinUnit.SATS) {
|
||||
newWalletPreferredUnit = BitcoinUnit.LOCAL_CURRENCY;
|
||||
} else {
|
||||
newWalletPreferredUnit = BitcoinUnit.BTC;
|
||||
}
|
||||
|
||||
const updatedWallet = updateWalletWithNewUnit(wallet, newWalletPreferredUnit);
|
||||
setWallet(updatedWallet);
|
||||
onWalletUnitChange?.(updatedWallet);
|
||||
};
|
||||
|
||||
const handleManageFundsPressed = () => {
|
||||
onManageFundsPressed?.(actionKeys.Refill);
|
||||
};
|
||||
|
||||
const onPressMenuItem = (id: string) => {
|
||||
if (id === 'walletBalanceVisibility') {
|
||||
handleBalanceVisibility();
|
||||
} else if (id === 'copyToClipboard') {
|
||||
handleCopyPress();
|
||||
}
|
||||
};
|
||||
|
||||
const balance = useMemo(() => {
|
||||
const hideBalance = wallet.hideBalance;
|
||||
const balanceUnit = wallet.getPreferredBalanceUnit();
|
||||
const balanceFormatted = formatBalance(wallet.getBalance(), balanceUnit, true).toString();
|
||||
return !hideBalance && balanceFormatted;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [wallet.hideBalance, wallet.getPreferredBalanceUnit()]);
|
||||
|
||||
return (
|
||||
<LinearGradient
|
||||
colors={WalletGradient.gradientsFor(wallet.type)}
|
||||
style={styles.lineaderGradient}
|
||||
// @ts-ignore: Ugh
|
||||
{...WalletGradient.linearGradientProps(wallet.type)}
|
||||
>
|
||||
<Image
|
||||
source={(() => {
|
||||
switch (wallet.type) {
|
||||
case LightningLdkWallet.type:
|
||||
case LightningCustodianWallet.type:
|
||||
return I18nManager.isRTL ? require('../img/lnd-shape-rtl.png') : require('../img/lnd-shape.png');
|
||||
case MultisigHDWallet.type:
|
||||
return I18nManager.isRTL ? require('../img/vault-shape-rtl.png') : require('../img/vault-shape.png');
|
||||
default:
|
||||
return I18nManager.isRTL ? require('../img/btc-shape-rtl.png') : require('../img/btc-shape.png');
|
||||
}
|
||||
})()}
|
||||
style={styles.chainIcon}
|
||||
/>
|
||||
<Text testID="WalletLabel" numberOfLines={1} style={styles.walletLabel}>
|
||||
{wallet.getLabel()}
|
||||
</Text>
|
||||
<ToolTipMenu
|
||||
onPress={changeWalletBalanceUnit}
|
||||
ref={menuRef}
|
||||
title={loc.wallets.balance}
|
||||
onPressMenuItem={onPressMenuItem}
|
||||
actions={
|
||||
wallet.hideBalance
|
||||
? [
|
||||
{
|
||||
id: 'walletBalanceVisibility',
|
||||
text: loc.transactions.details_balance_show,
|
||||
icon: {
|
||||
iconType: 'SYSTEM',
|
||||
iconValue: 'eye',
|
||||
},
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
id: 'walletBalanceVisibility',
|
||||
text: loc.transactions.details_balance_hide,
|
||||
icon: {
|
||||
iconType: 'SYSTEM',
|
||||
iconValue: 'eye.slash',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'copyToClipboard',
|
||||
text: loc.transactions.details_copy,
|
||||
icon: {
|
||||
iconType: 'SYSTEM',
|
||||
iconValue: 'doc.on.doc',
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View style={styles.walletBalance}>
|
||||
{wallet.hideBalance ? (
|
||||
<BluePrivateBalance />
|
||||
) : (
|
||||
<Text
|
||||
testID="WalletBalance"
|
||||
key={balance} // force component recreation on balance change. To fix right-to-left languages, like Farsi
|
||||
numberOfLines={1}
|
||||
adjustsFontSizeToFit
|
||||
style={styles.walletBalance}
|
||||
>
|
||||
{balance}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
</ToolTipMenu>
|
||||
{wallet.type === LightningCustodianWallet.type && allowOnchainAddress && (
|
||||
<ToolTipMenu
|
||||
isMenuPrimaryAction
|
||||
isButton
|
||||
onPressMenuItem={handleManageFundsPressed}
|
||||
actions={[
|
||||
{
|
||||
id: actionKeys.Refill,
|
||||
text: loc.lnd.refill,
|
||||
icon: actionIcons.Refill,
|
||||
},
|
||||
{
|
||||
id: actionKeys.RefillWithExternalWallet,
|
||||
text: loc.lnd.refill_external,
|
||||
icon: actionIcons.RefillWithExternalWallet,
|
||||
},
|
||||
]}
|
||||
buttonStyle={styles.manageFundsButton}
|
||||
>
|
||||
<Text style={styles.manageFundsButtonText}>{loc.lnd.title}</Text>
|
||||
</ToolTipMenu>
|
||||
)}
|
||||
|
||||
{wallet.type === MultisigHDWallet.type && (
|
||||
<TouchableOpacity accessibilityRole="button" onPress={handleManageFundsPressed}>
|
||||
<View style={styles.manageFundsButton}>
|
||||
<Text style={styles.manageFundsButtonText}>{loc.multisig.manage_keys}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</LinearGradient>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
lineaderGradient: {
|
||||
padding: 15,
|
||||
minHeight: 140,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
chainIcon: {
|
||||
width: 99,
|
||||
height: 94,
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
},
|
||||
walletLabel: {
|
||||
backgroundColor: 'transparent',
|
||||
fontSize: 19,
|
||||
color: '#fff',
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
},
|
||||
walletBalance: {
|
||||
backgroundColor: 'transparent',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 36,
|
||||
color: '#fff',
|
||||
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
||||
},
|
||||
manageFundsButton: {
|
||||
marginTop: 14,
|
||||
marginBottom: 10,
|
||||
backgroundColor: 'rgba(255,255,255,0.2)',
|
||||
borderRadius: 9,
|
||||
minHeight: 39,
|
||||
alignSelf: 'flex-start',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
manageFundsButtonText: {
|
||||
fontWeight: '500',
|
||||
fontSize: 14,
|
||||
color: '#FFFFFF',
|
||||
padding: 12,
|
||||
},
|
||||
});
|
||||
|
||||
export const actionKeys = {
|
||||
CopyToClipboard: 'copyToClipboard',
|
||||
WalletBalanceVisibility: 'walletBalanceVisibility',
|
||||
Refill: 'refill',
|
||||
RefillWithExternalWallet: 'qrcode',
|
||||
};
|
||||
|
||||
export const actionIcons = {
|
||||
Eye: {
|
||||
iconType: 'SYSTEM',
|
||||
iconValue: 'eye',
|
||||
},
|
||||
EyeSlash: {
|
||||
iconType: 'SYSTEM',
|
||||
iconValue: 'eye.slash',
|
||||
},
|
||||
Clipboard: {
|
||||
iconType: 'SYSTEM',
|
||||
iconValue: 'doc.on.doc',
|
||||
},
|
||||
Refill: {
|
||||
iconType: 'SYSTEM',
|
||||
iconValue: 'goforward.plus',
|
||||
},
|
||||
RefillWithExternalWallet: {
|
||||
iconType: 'SYSTEM',
|
||||
iconValue: 'qrcode',
|
||||
},
|
||||
};
|
||||
|
||||
export default TransactionsNavigationHeader;
|
|
@ -203,6 +203,7 @@ const WalletCarouselItem = ({ item, index, onPress, handleLongPress, isSelectedW
|
|||
shadowRadius={8}
|
||||
>
|
||||
<TouchableWithoutFeedback
|
||||
accessibilityRole="button"
|
||||
testID={item.getLabel()}
|
||||
onPressIn={onPressedIn}
|
||||
onPressOut={onPressedOut}
|
||||
|
@ -311,7 +312,7 @@ const WalletsCarousel = forwardRef((props, ref) => {
|
|||
const { width } = useWindowDimensions();
|
||||
const sliderHeight = 195;
|
||||
const itemWidth = width * 0.82 > 375 ? 375 : width * 0.82;
|
||||
return (
|
||||
return isHandset ? (
|
||||
<FlatList
|
||||
ref={flatListRef}
|
||||
renderItem={renderItem}
|
||||
|
@ -331,6 +332,19 @@ const WalletsCarousel = forwardRef((props, ref) => {
|
|||
onScrollToIndexFailed={onScrollToIndexFailed}
|
||||
{...props}
|
||||
/>
|
||||
) : (
|
||||
<View style={cStyles.contentLargeScreen}>
|
||||
{props.data.map((item, index) => (
|
||||
<WalletCarouselItem
|
||||
isSelectedWallet={!props.horizontal && props.selectedWallet && item ? props.selectedWallet === item.getID() : undefined}
|
||||
item={item}
|
||||
index={index}
|
||||
handleLongPress={props.handleLongPress}
|
||||
onPress={props.onPress}
|
||||
key={index}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
import React, { useContext } from 'react';
|
||||
import Handoff from 'react-native-handoff';
|
||||
import { BlueStorageContext } from '../blue_modules/storage-context';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const HandoffComponent = props => {
|
||||
const { isHandOffUseEnabled } = useContext(BlueStorageContext);
|
||||
|
||||
return isHandOffUseEnabled ? <Handoff {...props} /> : null;
|
||||
};
|
||||
export default HandoffComponent;
|
||||
|
||||
HandoffComponent.propTypes = {
|
||||
url: PropTypes.string,
|
||||
};
|
||||
|
||||
HandoffComponent.activityTypes = {
|
||||
ReceiveOnchain: 'io.bluewallet.bluewallet.receiveonchain',
|
||||
Xpub: 'io.bluewallet.bluewallet.xpub',
|
||||
ViewInBlockExplorer: 'io.bluewallet.bluewallet.blockexplorer',
|
||||
};
|
35
components/handoff.tsx
Normal file
35
components/handoff.tsx
Normal file
|
@ -0,0 +1,35 @@
|
|||
import React, { useContext } from 'react';
|
||||
// @ts-ignore: react-native-handoff is not in the type definition
|
||||
import Handoff from 'react-native-handoff';
|
||||
import { BlueStorageContext } from '../blue_modules/storage-context';
|
||||
|
||||
interface HandoffComponentProps {
|
||||
url?: string;
|
||||
}
|
||||
|
||||
interface HandoffComponentWithActivityTypes extends React.FC<HandoffComponentProps> {
|
||||
activityTypes: {
|
||||
ReceiveOnchain: string;
|
||||
Xpub: string;
|
||||
ViewInBlockExplorer: string;
|
||||
};
|
||||
}
|
||||
|
||||
const HandoffComponent: HandoffComponentWithActivityTypes = props => {
|
||||
const { isHandOffUseEnabled } = useContext(BlueStorageContext);
|
||||
|
||||
if (isHandOffUseEnabled) {
|
||||
return <Handoff {...props} />;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const activityTypes = {
|
||||
ReceiveOnchain: 'io.bluewallet.bluewallet.receiveonchain',
|
||||
Xpub: 'io.bluewallet.bluewallet.xpub',
|
||||
ViewInBlockExplorer: 'io.bluewallet.bluewallet.blockexplorer',
|
||||
};
|
||||
|
||||
HandoffComponent.activityTypes = activityTypes;
|
||||
|
||||
export default HandoffComponent;
|
|
@ -2,6 +2,7 @@
|
|||
import React from 'react';
|
||||
import { Image, Keyboard, TouchableOpacity, StyleSheet } from 'react-native';
|
||||
import { Theme } from './themes';
|
||||
import loc from '../loc';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
|
@ -41,6 +42,7 @@ const navigationStyle = (
|
|||
...opts
|
||||
}: NavigationOptions & {
|
||||
closeButton?: boolean;
|
||||
|
||||
closeButtonFunc?: (deps: { navigation: any; route: any }) => React.ReactElement;
|
||||
},
|
||||
formatter: OptionsFormatter,
|
||||
|
@ -56,7 +58,13 @@ const navigationStyle = (
|
|||
navigation.goBack(null);
|
||||
};
|
||||
headerRight = () => (
|
||||
<TouchableOpacity accessibilityRole="button" style={styles.button} onPress={handleClose} testID="NavigationCloseButton">
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.close}
|
||||
style={styles.button}
|
||||
onPress={handleClose}
|
||||
testID="NavigationCloseButton"
|
||||
>
|
||||
<Image source={theme.closeImage} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
@ -73,7 +81,7 @@ const navigationStyle = (
|
|||
fontWeight: '600',
|
||||
color: theme.colors.foregroundColor,
|
||||
},
|
||||
headerRight: headerRight,
|
||||
headerRight,
|
||||
headerBackTitleVisible: false,
|
||||
headerTintColor: theme.colors.foregroundColor,
|
||||
...opts,
|
||||
|
@ -108,6 +116,7 @@ export const navigationStyleTx = (opts: NavigationOptions, formatter: OptionsFor
|
|||
headerLeft: () => (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.close}
|
||||
style={styles.button}
|
||||
onPress={() => {
|
||||
Keyboard.dismiss();
|
||||
|
|
|
@ -47,7 +47,7 @@ module.exports = (
|
|||
];
|
||||
|
||||
prompt(title, text, buttons, {
|
||||
type: type,
|
||||
type,
|
||||
cancelable: isCancelable,
|
||||
// @ts-ignore suppressed because its supported only on ios and is absent from type definitions
|
||||
keyboardType,
|
||||
|
|
1
ios/.xcode.env
Normal file
1
ios/.xcode.env
Normal file
|
@ -0,0 +1 @@
|
|||
export NODE_BINARY=$(command -v node)
|
|
@ -7,7 +7,6 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||
32002D9D236FAA9F00B93396 /* TodayDataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32002D9C236FAA9F00B93396 /* TodayDataStore.swift */; };
|
||||
|
@ -56,7 +55,6 @@
|
|||
782F075B5DD048449E2DECE9 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = B9D9B3A7B2CB4255876B67AF /* libz.tbd */; };
|
||||
849047CA2702A32A008EE567 /* Handoff.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849047C92702A32A008EE567 /* Handoff.swift */; };
|
||||
84E05A842721191B001A0D3A /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 84E05A832721191B001A0D3A /* Settings.bundle */; };
|
||||
8CBB9CC9ACE4B44B45C28DF8 /* libPods-WidgetsExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E45F9A25C15BEA362225C9B /* libPods-WidgetsExtension.a */; };
|
||||
906451CAD44154C2950030EC /* libPods-BlueWallet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 731973BA0AC6EA78962CE5B6 /* libPods-BlueWallet.a */; };
|
||||
B40D4E34225841EC00428FCC /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B40D4E32225841EC00428FCC /* Interface.storyboard */; };
|
||||
B40D4E36225841ED00428FCC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B40D4E35225841ED00428FCC /* Assets.xcassets */; };
|
||||
|
@ -70,12 +68,14 @@
|
|||
B40D4E632258425500428FCC /* ReceiveInterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B40D4E5B2258425500428FCC /* ReceiveInterfaceController.swift */; };
|
||||
B40D4E642258425500428FCC /* WalletDetailsInterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B40D4E5C2258425500428FCC /* WalletDetailsInterfaceController.swift */; };
|
||||
B40D4E682258426B00428FCC /* KeychainSwiftDistrib.swift in Sources */ = {isa = PBXBuildFile; fileRef = B40D4E672258426B00428FCC /* KeychainSwiftDistrib.swift */; };
|
||||
B40FC3FA29CCD1D00007EBAC /* SwiftTCPClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = B40FC3F829CCD1AC0007EBAC /* SwiftTCPClient.swift */; };
|
||||
B43D0378225847C500FBAA95 /* WalletGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = B43D0372225847C500FBAA95 /* WalletGradient.swift */; };
|
||||
B43D0379225847C500FBAA95 /* WatchDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = B43D0373225847C500FBAA95 /* WatchDataSource.swift */; };
|
||||
B43D037A225847C500FBAA95 /* Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B43D0374225847C500FBAA95 /* Transaction.swift */; };
|
||||
B43D037B225847C500FBAA95 /* TransactionTableRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B43D0375225847C500FBAA95 /* TransactionTableRow.swift */; };
|
||||
B43D037C225847C500FBAA95 /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = B43D0376225847C500FBAA95 /* Wallet.swift */; };
|
||||
B43D037D225847C500FBAA95 /* WalletInformation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B43D0377225847C500FBAA95 /* WalletInformation.swift */; };
|
||||
B461B852299599F800E431AA /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = B461B851299599F800E431AA /* AppDelegate.mm */; };
|
||||
B4EE583C226703320003363C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B40D4E35225841ED00428FCC /* Assets.xcassets */; };
|
||||
E5D4794B26781FC0007838C1 /* fiatUnits.json in Resources */ = {isa = PBXBuildFile; fileRef = 6DD410AD266CAF1F0087DE03 /* fiatUnits.json */; };
|
||||
E5D4794C26781FC1007838C1 /* fiatUnits.json in Resources */ = {isa = PBXBuildFile; fileRef = 6DD410AD266CAF1F0087DE03 /* fiatUnits.json */; };
|
||||
|
@ -170,8 +170,6 @@
|
|||
00E356F21AD99517003FC87E /* BlueWalletTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BlueWalletTests.m; sourceTree = "<group>"; };
|
||||
04466491BA2D4876A71222FC /* Foundation.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Foundation.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Foundation.ttf"; sourceTree = "<group>"; };
|
||||
13B07F961A680F5B00A75B9A /* BlueWallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BlueWallet.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = BlueWallet/AppDelegate.h; sourceTree = "<group>"; };
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = BlueWallet/AppDelegate.m; sourceTree = "<group>"; };
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = BlueWallet/Images.xcassets; sourceTree = "<group>"; };
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = BlueWallet/Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = BlueWallet/main.m; sourceTree = "<group>"; };
|
||||
|
@ -290,12 +288,10 @@
|
|||
78A87E7251D94144A71A2F67 /* FontAwesome5_Solid.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome5_Solid.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf"; sourceTree = "<group>"; };
|
||||
7B468CC34D5B41F3950078EF /* libsqlite3.0.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.0.tbd; path = usr/lib/libsqlite3.0.tbd; sourceTree = SDKROOT; };
|
||||
7BAA8F97E61B677D33CF1944 /* Pods-WalletInformationAndMarketWidgetExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WalletInformationAndMarketWidgetExtension.release.xcconfig"; path = "Pods/Target Support Files/Pods-WalletInformationAndMarketWidgetExtension/Pods-WalletInformationAndMarketWidgetExtension.release.xcconfig"; sourceTree = "<group>"; };
|
||||
7E45F9A25C15BEA362225C9B /* libPods-WidgetsExtension.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-WidgetsExtension.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8448882949434D41A054C0B2 /* ToolTipMenuTests.xctest */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = ToolTipMenuTests.xctest; sourceTree = "<group>"; };
|
||||
849047C92702A32A008EE567 /* Handoff.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Handoff.swift; sourceTree = "<group>"; };
|
||||
84E05A832721191B001A0D3A /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
|
||||
8637D4B5E14D443A9031DA95 /* libRNFS.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFS.a; sourceTree = "<group>"; };
|
||||
8C30FBCA42C6BF0B45757763 /* Pods-WidgetsExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetsExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-WidgetsExtension/Pods-WidgetsExtension.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
90F86BC5194548CA87D729A9 /* libToolTipMenu.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libToolTipMenu.a; sourceTree = "<group>"; };
|
||||
94565BFC6A0C4235B3EC7B01 /* libRNSVG.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNSVG.a; sourceTree = "<group>"; };
|
||||
95208B2A05884A76B5BB99C0 /* libRCTGoogleAnalyticsBridge.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRCTGoogleAnalyticsBridge.a; sourceTree = "<group>"; };
|
||||
|
@ -322,6 +318,7 @@
|
|||
B40D4E5B2258425500428FCC /* ReceiveInterfaceController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReceiveInterfaceController.swift; sourceTree = "<group>"; };
|
||||
B40D4E5C2258425500428FCC /* WalletDetailsInterfaceController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletDetailsInterfaceController.swift; sourceTree = "<group>"; };
|
||||
B40D4E672258426B00428FCC /* KeychainSwiftDistrib.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainSwiftDistrib.swift; sourceTree = SOURCE_ROOT; };
|
||||
B40FC3F829CCD1AC0007EBAC /* SwiftTCPClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftTCPClient.swift; sourceTree = "<group>"; };
|
||||
B43B69B8225C462E00925B1E /* libPods-RCTLinking.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libPods-RCTLinking.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B43B69BA225C46D800925B1E /* libRCTLinking.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRCTLinking.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B43D0372225847C500FBAA95 /* WalletGradient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletGradient.swift; sourceTree = "<group>"; };
|
||||
|
@ -332,6 +329,8 @@
|
|||
B43D0377225847C500FBAA95 /* WalletInformation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletInformation.swift; sourceTree = "<group>"; };
|
||||
B43D046E22584C1B00FBAA95 /* libRNWatch.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRNWatch.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B459EE96941AE09BCB547DC0 /* Pods-BlueWallet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BlueWallet.release.xcconfig"; path = "Pods/Target Support Files/Pods-BlueWallet/Pods-BlueWallet.release.xcconfig"; sourceTree = "<group>"; };
|
||||
B461B850299599F800E431AA /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = BlueWallet/AppDelegate.h; sourceTree = "<group>"; };
|
||||
B461B851299599F800E431AA /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = BlueWallet/AppDelegate.mm; sourceTree = "<group>"; };
|
||||
B4D3235A177F4580BA52F2F9 /* libRNCSlider.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNCSlider.a; sourceTree = "<group>"; };
|
||||
B642AFB13483418CAB6FF25E /* libRCTQRCodeLocalImage.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRCTQRCodeLocalImage.a; sourceTree = "<group>"; };
|
||||
B9D9B3A7B2CB4255876B67AF /* libz.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
|
||||
|
@ -340,7 +339,6 @@
|
|||
CA741BA794714D3F80251AC9 /* Ionicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Ionicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf"; sourceTree = "<group>"; };
|
||||
CD746B955C55410793BB72C0 /* libRNGestureHandler.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNGestureHandler.a; sourceTree = "<group>"; };
|
||||
CF4A4D7AAD974D67A2D62B3E /* MaterialIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = MaterialIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf"; sourceTree = "<group>"; };
|
||||
E0A230940404CA7C51FBED37 /* Pods-WidgetsExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetsExtension.release.xcconfig"; path = "Pods/Target Support Files/Pods-WidgetsExtension/Pods-WidgetsExtension.release.xcconfig"; sourceTree = "<group>"; };
|
||||
E6B44173A8854B6D85D7F933 /* libRNVectorIcons-tvOS.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = "libRNVectorIcons-tvOS.a"; sourceTree = "<group>"; };
|
||||
E8E8CE89B3D142C6A8A56C34 /* Octicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Octicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Octicons.ttf"; sourceTree = "<group>"; };
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||
|
@ -384,7 +382,6 @@
|
|||
files = (
|
||||
6DD4109E266CADF10087DE03 /* SwiftUI.framework in Frameworks */,
|
||||
6DD4109D266CADF10087DE03 /* WidgetKit.framework in Frameworks */,
|
||||
8CBB9CC9ACE4B44B45C28DF8 /* libPods-WidgetsExtension.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -426,11 +423,11 @@
|
|||
13B07FAE1A68108700A75B9A /* BlueWallet */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B461B850299599F800E431AA /* AppDelegate.h */,
|
||||
B461B851299599F800E431AA /* AppDelegate.mm */,
|
||||
32C7944323B8879D00BE2AFA /* BlueWalletRelease.entitlements */,
|
||||
32F0A2502310B0910095C559 /* BlueWallet.entitlements */,
|
||||
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.m */,
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||
13B07FB71A68108700A75B9A /* main.m */,
|
||||
|
@ -463,7 +460,6 @@
|
|||
6D333B3C252FE1A3004D72DF /* SwiftUI.framework */,
|
||||
98455D960744E4E5DD50BA87 /* libPods-WalletInformationAndMarketWidgetExtension.a */,
|
||||
41BD3AC9FD81723B68A63C12 /* libPods-MarketWidgetExtension.a */,
|
||||
7E45F9A25C15BEA362225C9B /* libPods-WidgetsExtension.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
|
@ -583,6 +579,7 @@
|
|||
6DEB4C3A254FBF4800E9F9AA /* Colors.swift */,
|
||||
6D4AF18325D215D1009DD853 /* UserDefaultsExtension.swift */,
|
||||
6D4AF18225D215D0009DD853 /* BlueWalletWatch-Bridging-Header.h */,
|
||||
B40FC3F829CCD1AC0007EBAC /* SwiftTCPClient.swift */,
|
||||
);
|
||||
path = Shared;
|
||||
sourceTree = "<group>";
|
||||
|
@ -641,8 +638,6 @@
|
|||
FF45EB303C9601ED114589A4 /* Pods-MarketWidgetExtension.release.xcconfig */,
|
||||
AA7DCFB2C7887DF26EDB5710 /* Pods-WalletInformationAndMarketWidgetExtension.debug.xcconfig */,
|
||||
7BAA8F97E61B677D33CF1944 /* Pods-WalletInformationAndMarketWidgetExtension.release.xcconfig */,
|
||||
8C30FBCA42C6BF0B45757763 /* Pods-WidgetsExtension.debug.xcconfig */,
|
||||
E0A230940404CA7C51FBED37 /* Pods-WidgetsExtension.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
sourceTree = "<group>";
|
||||
|
@ -794,7 +789,6 @@
|
|||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 6DD410A9266CADF40087DE03 /* Build configuration list for PBXNativeTarget "WidgetsExtension" */;
|
||||
buildPhases = (
|
||||
0CD668CB2F65F36C4CADD5AB /* [CP] Check Pods Manifest.lock */,
|
||||
6DD41098266CADF10087DE03 /* Sources */,
|
||||
6DD41099266CADF10087DE03 /* Frameworks */,
|
||||
6DD4109A266CADF10087DE03 /* Resources */,
|
||||
|
@ -1013,28 +1007,6 @@
|
|||
shellPath = /bin/sh;
|
||||
shellScript = "export EXTRA_PACKAGER_ARGS=\"--sourcemap-output $TMPDIR/$(md5 -qs \"$CONFIGURATION_BUILD_DIR\")-main.jsbundle.map\"\nexport NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n";
|
||||
};
|
||||
0CD668CB2F65F36C4CADD5AB /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-WidgetsExtension-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
4B36CFF6FE55027DCA5CB6E1 /* Upload Bugsnag dSYM */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -1063,12 +1035,12 @@
|
|||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-BlueWallet/Pods-BlueWallet-frameworks.sh",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL/OpenSSL.framework/OpenSSL",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/LDKFramework/LDKFramework.framework/LDKFramework",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/rn-ldk/LDKFramework.framework/LDKFramework",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LDKFramework.framework",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -1190,9 +1162,9 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
|
||||
6D32C5C62596CE3A008C077C /* EventEmitter.m in Sources */,
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||
B461B852299599F800E431AA /* AppDelegate.mm in Sources */,
|
||||
32B5A32A2334450100F8D608 /* Bridge.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -1214,6 +1186,7 @@
|
|||
files = (
|
||||
6DD410BE266CAF5C0087DE03 /* SendReceiveButtons.swift in Sources */,
|
||||
6DD410B4266CAF5C0087DE03 /* WidgetAPI.swift in Sources */,
|
||||
B40FC3FA29CCD1D00007EBAC /* SwiftTCPClient.swift in Sources */,
|
||||
6DD410A1266CADF10087DE03 /* Widgets.swift in Sources */,
|
||||
6DD410AC266CAE470087DE03 /* PriceWidget.swift in Sources */,
|
||||
6DD410B2266CAF5C0087DE03 /* WalletInformationView.swift in Sources */,
|
||||
|
@ -1390,10 +1363,11 @@
|
|||
"@executable_path/Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/usr/lib/swift",
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)",
|
||||
);
|
||||
MARKETING_VERSION = 6.3.2;
|
||||
MARKETING_VERSION = 6.4.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
|
@ -1433,10 +1407,11 @@
|
|||
"@executable_path/Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/usr/lib/swift",
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)",
|
||||
);
|
||||
MARKETING_VERSION = 6.3.2;
|
||||
MARKETING_VERSION = 6.4.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
|
@ -1477,7 +1452,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 6.3.2;
|
||||
MARKETING_VERSION = 6.4.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.TodayExtension;
|
||||
|
@ -1516,7 +1491,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 6.3.2;
|
||||
MARKETING_VERSION = 6.4.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.TodayExtension;
|
||||
PRODUCT_NAME = "BlueWallet - Bitcoin Price";
|
||||
|
@ -1548,7 +1523,7 @@
|
|||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = Stickers/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
MARKETING_VERSION = 6.3.2;
|
||||
MARKETING_VERSION = 6.4.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.Stickers;
|
||||
|
@ -1580,7 +1555,7 @@
|
|||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = Stickers/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
MARKETING_VERSION = 6.3.2;
|
||||
MARKETING_VERSION = 6.4.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.Stickers;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -1592,7 +1567,6 @@
|
|||
};
|
||||
6DD410AA266CADF40087DE03 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 8C30FBCA42C6BF0B45757763 /* Pods-WidgetsExtension.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
|
@ -1618,7 +1592,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 6.3.2;
|
||||
MARKETING_VERSION = 6.4.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.MarketWidget;
|
||||
|
@ -1636,7 +1610,6 @@
|
|||
};
|
||||
6DD410AB266CADF40087DE03 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = E0A230940404CA7C51FBED37 /* Pods-WidgetsExtension.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
|
@ -1663,7 +1636,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 6.3.2;
|
||||
MARKETING_VERSION = 6.4.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.MarketWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -1682,7 +1655,7 @@
|
|||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
|
@ -1709,7 +1682,7 @@
|
|||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
|
@ -1726,9 +1699,10 @@
|
|||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.2;
|
||||
LIBRARY_SEARCH_PATHS = "\"$(inherited)\"";
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_VERSION = 4.2;
|
||||
};
|
||||
|
@ -1739,7 +1713,7 @@
|
|||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
|
@ -1766,7 +1740,7 @@
|
|||
COPY_PHASE_STRIP = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
|
@ -1776,8 +1750,9 @@
|
|||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.2;
|
||||
LIBRARY_SEARCH_PATHS = "\"$(inherited)\"";
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_VERSION = 4.2;
|
||||
|
@ -1808,7 +1783,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 6.3.2;
|
||||
MARKETING_VERSION = 6.4.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.watch.extension;
|
||||
|
@ -1848,7 +1823,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 6.3.2;
|
||||
MARKETING_VERSION = 6.4.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.watch.extension;
|
||||
PRODUCT_NAME = "${TARGET_NAME}";
|
||||
|
@ -1883,7 +1858,7 @@
|
|||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
IBSC_MODULE = BlueWalletWatch_Extension;
|
||||
INFOPLIST_FILE = BlueWalletWatch/Info.plist;
|
||||
MARKETING_VERSION = 6.3.2;
|
||||
MARKETING_VERSION = 6.4.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.watch;
|
||||
|
@ -1921,7 +1896,7 @@
|
|||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
IBSC_MODULE = BlueWalletWatch_Extension;
|
||||
INFOPLIST_FILE = BlueWalletWatch/Info.plist;
|
||||
MARKETING_VERSION = 6.3.2;
|
||||
MARKETING_VERSION = 6.4.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.watch;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -2011,7 +1986,7 @@
|
|||
repositoryURL = "https://github.com/EFPrefix/EFQRCode.git";
|
||||
requirement = {
|
||||
kind = exactVersion;
|
||||
version = 5.1.8;
|
||||
version = 6.2.2;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
|
|
@ -39,6 +39,21 @@
|
|||
<key>orderHint</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
<key>Stickers.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
<key>TodayExtension.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>5</integer>
|
||||
</dict>
|
||||
<key>WidgetsExtension.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>88</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/EFPrefix/EFQRCode.git",
|
||||
"state" : {
|
||||
"revision" : "62eff300b439d0088007b4e2e2e8c600f4b2e515",
|
||||
"version" : "5.1.8"
|
||||
"revision" : "2991c2f318ad9529d93b2a73a382a3f9c72c64ce",
|
||||
"version" : "6.2.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -14,8 +14,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/ApolloZhu/swift_qrcodejs.git",
|
||||
"state" : {
|
||||
"revision" : "f3fe9fdd39fa48e45b501000194b43a3883f732f",
|
||||
"version" : "1.1.4"
|
||||
"revision" : "374dc7f7b9e76c6aeb393f6a84590c6d387e1ecb",
|
||||
"version" : "2.2.2"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -1,15 +1,6 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <UserNotifications/UNUserNotificationCenter.h>
|
||||
#import <RCTAppDelegate.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate, UNUserNotificationCenterDelegate>
|
||||
|
||||
@property (nonatomic, strong) UIWindow *window;
|
||||
@interface AppDelegate : RCTAppDelegate
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,41 +1,20 @@
|
|||
#import <Bugsnag/Bugsnag.h>
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import <React/RCTLinkingManager.h>
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
#import <React/RCTI18nUtil.h>
|
||||
#import <React/RCTRootView.h>
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
#import "RNQuickActionManager.h"
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
#import <RNCPushNotificationIOS.h>
|
||||
#import "EventEmitter.h"
|
||||
@import WatchConnectivity;
|
||||
#if !TARGET_OS_MACCATALYST
|
||||
#ifdef FB_SONARKIT_ENABLED
|
||||
#import <FlipperKit/FlipperClient.h>
|
||||
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
|
||||
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
|
||||
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
|
||||
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
|
||||
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>
|
||||
#import <React/RCTRootView.h>
|
||||
#import <WatchConnectivity/WatchConnectivity.h>
|
||||
|
||||
static void InitializeFlipper(UIApplication *application) {
|
||||
FlipperClient *client = [FlipperClient sharedClient];
|
||||
SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
|
||||
[client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
|
||||
[client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
|
||||
[client addPlugin:[FlipperKitReactPlugin new]];
|
||||
[client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
|
||||
[client start];
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@interface AppDelegate() <UNUserNotificationCenterDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
|
@ -51,43 +30,39 @@ static void InitializeFlipper(UIApplication *application) {
|
|||
forKeyPath:@"deviceUIDCopy"
|
||||
options:NSKeyValueObservingOptionNew
|
||||
context:NULL];
|
||||
self.moduleName = @"BlueWallet";
|
||||
// You can add your custom initial props in the dictionary below.
|
||||
// They will be passed down to the ViewController used by React Native.
|
||||
self.initialProps = @{};
|
||||
|
||||
#if !TARGET_OS_MACCATALYST
|
||||
#ifdef FB_SONARKIT_ENABLED
|
||||
InitializeFlipper(application);
|
||||
#endif
|
||||
#endif
|
||||
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
|
||||
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
|
||||
moduleName:@"BlueWallet"
|
||||
initialProperties:nil];
|
||||
|
||||
if (@available(iOS 13.0, *)) {
|
||||
rootView.backgroundColor = [UIColor systemBackgroundColor];
|
||||
} else {
|
||||
rootView.backgroundColor = [UIColor clearColor];
|
||||
}
|
||||
[[RCTI18nUtil sharedInstance] allowRTL:YES];
|
||||
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
UIViewController *rootViewController = [UIViewController new];
|
||||
rootViewController.view = rootView;
|
||||
self.window.rootViewController = rootViewController;
|
||||
[self.window makeKeyAndVisible];
|
||||
UIView* launchScreenView = [[UIStoryboard storyboardWithName:@"LaunchScreen" bundle:nil] instantiateInitialViewController].view;
|
||||
launchScreenView.frame = self.window.bounds;
|
||||
rootView.loadingView = launchScreenView;
|
||||
// Define UNUserNotificationCenter
|
||||
|
||||
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
center.delegate = self;
|
||||
|
||||
|
||||
/* For debugging purposes since iOS Simulator does not support handoff
|
||||
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.io.bluewallet.bluewallet"];
|
||||
[defaults setValue:@{@"activityType": @"io.bluewallet.bluewallet.receiveonchain", @"userInfo": @{@"address": @""}} forKey:@"onUserActivityOpen"];
|
||||
*/
|
||||
return YES;
|
||||
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
}
|
||||
|
||||
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
|
||||
{
|
||||
#if DEBUG
|
||||
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
|
||||
#else
|
||||
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||
#endif
|
||||
}
|
||||
|
||||
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
|
||||
///
|
||||
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
|
||||
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
|
||||
/// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`.
|
||||
- (BOOL)concurrentRootEnabled
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *) keyPath ofObject:(id) object change:(NSDictionary *) change context:(void *) context
|
||||
{
|
||||
if([keyPath isEqual:@"deviceUID"] || [keyPath isEqual:@"deviceUIDCopy"])
|
||||
|
@ -103,15 +78,6 @@ static void InitializeFlipper(UIApplication *application) {
|
|||
}
|
||||
}
|
||||
|
||||
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
|
||||
{
|
||||
#if DEBUG
|
||||
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
|
||||
#else
|
||||
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity
|
||||
restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
|
||||
{
|
||||
|
@ -154,46 +120,6 @@ static void InitializeFlipper(UIApplication *application) {
|
|||
completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge);
|
||||
}
|
||||
|
||||
// Required to register for notifications
|
||||
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
|
||||
{
|
||||
[RNCPushNotificationIOS didRegisterUserNotificationSettings:notificationSettings];
|
||||
}
|
||||
|
||||
// Required for the register event.
|
||||
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
|
||||
{
|
||||
[RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
|
||||
}
|
||||
|
||||
// Required for the notification event. You must call the completion handler after handling the remote notification.
|
||||
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
|
||||
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
|
||||
{
|
||||
[RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
|
||||
}
|
||||
|
||||
// Required for the registrationError event.
|
||||
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
|
||||
{
|
||||
[RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
|
||||
}
|
||||
|
||||
// IOS 10+ Required for localNotification event
|
||||
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
|
||||
didReceiveNotificationResponse:(UNNotificationResponse *)response
|
||||
withCompletionHandler:(void (^)(void))completionHandler
|
||||
{
|
||||
[RNCPushNotificationIOS didReceiveNotificationResponse:response];
|
||||
completionHandler();
|
||||
}
|
||||
|
||||
// IOS 4-10 Required for the localNotification event.
|
||||
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
|
||||
{
|
||||
[RNCPushNotificationIOS didReceiveLocalNotification:notification];
|
||||
}
|
||||
|
||||
- (void)openPreferences {
|
||||
[EventEmitter.sharedInstance openSettings];
|
||||
}
|
||||
|
@ -225,4 +151,28 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
|
|||
}
|
||||
}
|
||||
|
||||
// Required for the register event.
|
||||
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
|
||||
{
|
||||
[RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
|
||||
}
|
||||
// Required for the notification event. You must call the completion handler after handling the remote notification.
|
||||
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
|
||||
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
|
||||
{
|
||||
[RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
|
||||
}
|
||||
// Required for the registrationError event.
|
||||
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
|
||||
{
|
||||
[RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
|
||||
}
|
||||
// Required for localNotification event
|
||||
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
|
||||
didReceiveNotificationResponse:(UNNotificationResponse *)response
|
||||
withCompletionHandler:(void (^)(void))completionHandler
|
||||
{
|
||||
[RNCPushNotificationIOS didReceiveNotificationResponse:response];
|
||||
}
|
||||
|
||||
@end
|
|
@ -101,8 +101,6 @@
|
|||
<array>
|
||||
<string>https</string>
|
||||
<string>http</string>
|
||||
<string>bankid</string>
|
||||
<string>swish</string>
|
||||
</array>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
|
@ -133,6 +131,13 @@
|
|||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>ts.net</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSAppleMusicUsageDescription</key>
|
||||
|
|
|
@ -15,7 +15,7 @@ static EventEmitter *sharedInstance;
|
|||
RCT_EXPORT_MODULE();
|
||||
|
||||
+ (BOOL)requiresMainQueueSetup {
|
||||
return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (EventEmitter *)sharedInstance {
|
||||
|
|
58
ios/Podfile
58
ios/Podfile
|
@ -1,25 +1,58 @@
|
|||
platform :ios, '13.0'
|
||||
workspace 'BlueWallet'
|
||||
require_relative '../node_modules/react-native/scripts/react_native_pods'
|
||||
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
|
||||
|
||||
workspace 'BlueWallet'
|
||||
platform :ios, '13.0'
|
||||
prepare_react_native_project!
|
||||
|
||||
# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
|
||||
# because `react-native-flipper` depends on (FlipperKit,...) that will be excluded
|
||||
#
|
||||
# To fix this you can also exclude `react-native-flipper` using a `react-native.config.js`
|
||||
# ```js
|
||||
# module.exports = {
|
||||
# dependencies: {
|
||||
# ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}),
|
||||
# ```
|
||||
# flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled
|
||||
|
||||
flipper_config = FlipperConfiguration.disabled
|
||||
linkage = ENV['USE_FRAMEWORKS']
|
||||
if linkage != nil
|
||||
Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
|
||||
use_frameworks! :linkage => linkage.to_sym
|
||||
end
|
||||
|
||||
|
||||
target 'BlueWallet' do
|
||||
config = use_native_modules!
|
||||
|
||||
# Flags change depending on the env values.
|
||||
flags = get_default_flags()
|
||||
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath],
|
||||
# to enable hermes on iOS, change `false` to `true` and then install pods
|
||||
:hermes_enabled => false
|
||||
)
|
||||
|
||||
# Hermes is now enabled by default. Disable by setting this flag to false.
|
||||
# Upcoming versions of React Native may rely on get_default_flags(), but
|
||||
# we make it explicit here to aid in the React Native upgrade process.
|
||||
:hermes_enabled => true,
|
||||
:fabric_enabled => flags[:fabric_enabled],
|
||||
# Enables Flipper.
|
||||
#
|
||||
# Note that if you have use_frameworks! enabled, Flipper will not work and
|
||||
# you should disable these next few lines.
|
||||
use_flipper!({ "Flipper-DoubleConversion" => "1.1.7" })
|
||||
# you should disable the next line.
|
||||
#:flipper_configuration => flipper_config,
|
||||
# An absolute path to your application root.
|
||||
:app_path => "#{Pod::Config.instance.installation_root}/.."
|
||||
)
|
||||
|
||||
post_install do |installer|
|
||||
react_native_post_install(installer)
|
||||
__apply_Xcode_12_5_M1_post_install_workaround(installer)
|
||||
react_native_post_install(
|
||||
installer,
|
||||
# Set `mac_catalyst_enabled` to `true` in order to apply patches
|
||||
# necessary for Mac Catalyst builds
|
||||
:mac_catalyst_enabled => false
|
||||
)
|
||||
pod 'Bugsnag'
|
||||
plugin 'cocoapods-bugsnag'
|
||||
installer.pods_project.targets.each do |target|
|
||||
|
@ -31,12 +64,9 @@ target 'BlueWallet' do
|
|||
config.build_settings['DEVELOPMENT_TEAM'] = "A7W54YZ4WU"
|
||||
end
|
||||
end
|
||||
__apply_Xcode_12_5_M1_post_install_workaround(installer)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
target 'WidgetsExtension' do
|
||||
pod 'SwiftSocket', :git => 'https://github.com/swiftsocket/SwiftSocket.git', :branch => 'master'
|
||||
end
|
||||
|
|
821
ios/Podfile.lock
821
ios/Podfile.lock
File diff suppressed because it is too large
Load diff
101
ios/Widgets/Shared/SwiftTCPClient.swift
Normal file
101
ios/Widgets/Shared/SwiftTCPClient.swift
Normal file
|
@ -0,0 +1,101 @@
|
|||
//
|
||||
// File.swift
|
||||
// BlueWallet
|
||||
//
|
||||
// Created by Marcos Rodriguez on 3/23/23.
|
||||
// Copyright © 2023 BlueWallet. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class SwiftTCPClient: NSObject {
|
||||
private var inputStream: InputStream?
|
||||
private var outputStream: OutputStream?
|
||||
private let bufferSize = 1024
|
||||
|
||||
// Define the completion block type
|
||||
typealias ReceiveCompletion = (Result<Data, Error>) -> Void
|
||||
|
||||
// Add a completion block property
|
||||
var receiveCompletion: ReceiveCompletion?
|
||||
|
||||
func connect(to host: String, port: UInt32) -> Bool {
|
||||
var readStream: Unmanaged<CFReadStream>?
|
||||
var writeStream: Unmanaged<CFWriteStream>?
|
||||
|
||||
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host as CFString, port, &readStream, &writeStream)
|
||||
|
||||
guard let read = readStream?.takeRetainedValue(), let write = writeStream?.takeRetainedValue() else {
|
||||
return false
|
||||
}
|
||||
|
||||
inputStream = read as InputStream
|
||||
outputStream = write as OutputStream
|
||||
|
||||
inputStream?.delegate = self
|
||||
outputStream?.delegate = self
|
||||
|
||||
inputStream?.schedule(in: .current, forMode: .default)
|
||||
outputStream?.schedule(in: .current, forMode: .default)
|
||||
|
||||
inputStream?.open()
|
||||
outputStream?.open()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func send(data: Data) -> Bool {
|
||||
guard let outputStream = outputStream else {
|
||||
return false
|
||||
}
|
||||
|
||||
let bytesWritten = data.withUnsafeBytes { bufferPointer -> Int in
|
||||
guard let baseAddress = bufferPointer.baseAddress else {
|
||||
return 0
|
||||
}
|
||||
return outputStream.write(baseAddress.assumingMemoryBound(to: UInt8.self), maxLength: data.count)
|
||||
}
|
||||
|
||||
return bytesWritten == data.count
|
||||
}
|
||||
|
||||
func receive() -> Data? {
|
||||
let data = NSMutableData()
|
||||
return data as Data
|
||||
}
|
||||
|
||||
func close() {
|
||||
inputStream?.close()
|
||||
outputStream?.close()
|
||||
inputStream?.remove(from: .current, forMode: .default)
|
||||
outputStream?.remove(from: .current, forMode: .default)
|
||||
inputStream = nil
|
||||
outputStream = nil
|
||||
}
|
||||
}
|
||||
|
||||
extension SwiftTCPClient: StreamDelegate {
|
||||
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
|
||||
switch eventCode {
|
||||
case .hasBytesAvailable:
|
||||
if let inputStream = aStream as? InputStream {
|
||||
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
|
||||
defer {
|
||||
buffer.deallocate()
|
||||
}
|
||||
|
||||
while inputStream.hasBytesAvailable {
|
||||
let bytesRead = inputStream.read(buffer, maxLength: bufferSize)
|
||||
if bytesRead > 0 {
|
||||
let data = Data(bytes: buffer, count: bytesRead)
|
||||
receiveCompletion?(.success(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
case .hasSpaceAvailable, .openCompleted, .endEncountered, .errorOccurred:
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,8 +6,7 @@
|
|||
// Copyright © 2020 BlueWallet. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
import SwiftSocket
|
||||
import Foundation
|
||||
|
||||
struct APIError: LocalizedError {
|
||||
var errorDescription: String = "Failed to fetch Electrum data..."
|
||||
|
@ -21,13 +20,12 @@ extension WidgetAPI {
|
|||
return
|
||||
}
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
let client = TCPClient(address: host, port: port)
|
||||
let send = "{\"id\": 1, \"method\": \"blockchain.estimatefee\", \"params\": [1]}\n"
|
||||
switch client.connect(timeout: 1) {
|
||||
case .success:
|
||||
switch client.send(string: send) {
|
||||
case .success:
|
||||
guard let data = client.read(1024*10, timeout: 1), let response = String(bytes: data, encoding: .utf8)?.data(using: .utf8) else {
|
||||
let client = SwiftTCPClient()
|
||||
client.receiveCompletion = { result in
|
||||
switch result {
|
||||
case .success(let data):
|
||||
print("Received: \(data)")
|
||||
guard let response = String(bytes: data, encoding: .utf8)?.data(using: .utf8) else {
|
||||
client.close()
|
||||
completion(nil, APIError())
|
||||
return
|
||||
|
@ -45,12 +43,21 @@ extension WidgetAPI {
|
|||
completion(nil, APIError())
|
||||
}
|
||||
case .failure(let error):
|
||||
print(error)
|
||||
print("Error: \(error.localizedDescription)")
|
||||
client.close()
|
||||
completion(nil, APIError())
|
||||
}
|
||||
case .failure(let error):
|
||||
print(error)
|
||||
}
|
||||
|
||||
if client.connect(to: host, port: UInt32(exactly: port)!) {
|
||||
let message = "{\"id\": 1, \"method\": \"blockchain.estimatefee\", \"params\": [1]}\n"
|
||||
if let data = message.data(using: .utf8), client.send(data: data) {
|
||||
print("Message sent!")
|
||||
RunLoop.current.run(until: Date(timeIntervalSinceNow: 5))
|
||||
}
|
||||
client.close()
|
||||
} else {
|
||||
print("Connection failed")
|
||||
client.close()
|
||||
if userElectrumSettings.host == DefaultElectrumPeers.last?.host {
|
||||
completion(nil, APIError())
|
||||
|
|
|
@ -35,6 +35,10 @@ class WidgetAPI {
|
|||
urlString = "https://api.exir.io/v1/ticker?symbol=btc-irt"
|
||||
case "wazirx":
|
||||
urlString = "https://api.wazirx.com/api/v2/tickers/btcinr"
|
||||
case "Bitstamp":
|
||||
urlString = "https://www.bitstamp.net/api/v2/ticker/btc\(endPointKey.lowercased())"
|
||||
case "CoinGecko":
|
||||
urlString = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=\(endPointKey.lowercased())"
|
||||
default:
|
||||
urlString = "https://api.coindesk.com/v1/bpi/currentprice/\(endPointKey).json"
|
||||
}
|
||||
|
@ -61,6 +65,12 @@ class WidgetAPI {
|
|||
let unix = Double(lastUpdated / 1_000)
|
||||
let lastUpdatedString = ISO8601DateFormatter().string(from: Date(timeIntervalSince1970: unix))
|
||||
latestRateDataStore = WidgetDataStore(rate: String(rateDouble), lastUpdate: lastUpdatedString, rateDouble: rateDouble)
|
||||
case "CoinGecko":
|
||||
guard let rateDict = json["bitcoin"] as? [String: Any],
|
||||
let rateDouble = rateDict[endPointKey.lowercased()] as? Double
|
||||
else { break }
|
||||
let lastUpdatedString = ISO8601DateFormatter().string(from: Date())
|
||||
latestRateDataStore = WidgetDataStore(rate: String(rateDouble), lastUpdate: lastUpdatedString, rateDouble: rateDouble)
|
||||
case "YadioConvert":
|
||||
guard let rateDict = json as? [String: Any],
|
||||
let rateDouble = rateDict["rate"] as? Double,
|
||||
|
@ -74,6 +84,10 @@ class WidgetAPI {
|
|||
let rateString = String(rateDouble)
|
||||
let lastUpdatedString = ISO8601DateFormatter().string(from: Date())
|
||||
latestRateDataStore = WidgetDataStore(rate: rateString, lastUpdate: lastUpdatedString, rateDouble: rateDouble)
|
||||
case "Bitstamp":
|
||||
guard let rateString = json["last"] as? String, let rateDouble = Double(rateString) else { break }
|
||||
let lastUpdatedString = ISO8601DateFormatter().string(from: Date())
|
||||
latestRateDataStore = WidgetDataStore(rate: rateString, lastUpdate: lastUpdatedString, rateDouble: rateDouble)
|
||||
case "wazirx":
|
||||
guard let tickerDict = json["ticker"] as? [String: Any],
|
||||
let rateString = tickerDict["buy"] as? String,
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
محفظة Bitcoin تتيح لك تخزين عملات Bitcoin وإرسالها واستلامها وشراءها، مع التركيز على الأمان والبساطة.
|
||||
محفظة بتكوين تتيح لك تخزين البتكوين، ارساله واستقباله مع اهتمام خاص بالأمان العالي والبساطة المتناهية.
|
||||
|
||||
في BlueWallet ستمتلك المفاتيح الخاصة لعملات Bitcoin. محفظة الـ Bitcoin هذه أنشأها مستخدمو Bitcoin من أجل المجتمع.
|
||||
في BlueWallet ستمتلك المفاتيح الخاصة للبتكوين الذي تقرر الاحتفاظ به فيها. هذه المحفظة أنشأها مستخدمو البتكوين من أجل المجتمع.
|
||||
|
||||
يمكنك إجراء المعاملات مع أي شخص في العالم على الفور، وتغيير النظام المالي من جيبك مباشرةً.
|
||||
يمكنك إجراء المعاملات مع أي شخص في العالم على الفور والمشاركة في تطوير النظام المالي العالمي من جيبك مباشرةً.
|
||||
|
||||
يمكنك إنشاء عدد غير محدود من محافظ Bitcoin مجانًا أو استيراد محفظتك الحالية. الأمر بسيط وسريع.
|
||||
يمكنك إنشاء عدد غير محدود من محافظ البتكوين مجانًا أو استيراد محفظتك الحالية. الأمر سريع بسيط.
|
||||
|
||||
_____
|
||||
|
||||
إليك ما تحصل عليه:
|
||||
إليك ما ستحصل عليه:
|
||||
|
||||
|
||||
1 - الأمان من خلال التصميم
|
||||
|
@ -22,32 +22,25 @@ _____
|
|||
التشفير الكامل
|
||||
علاوةً على التشفير متعدد الطبقات لنظام iOS، فإننا نقوم بتشفير كل شيء بكلمات مرور مضافة
|
||||
|
||||
العقدة الكاملة
|
||||
اتصل بمحفظة Bitcoin باستخدام العقدة الكاملة عن طريق برنامج Electrum
|
||||
النود التام
|
||||
اربط محفظة البتكوين بنود تام خاص بك عن طريق برنامج Electrum
|
||||
|
||||
التخزين البارد
|
||||
اتصل بمحفظة جهاز واحتفظ بعملاتك في وحدة "التخزين البارد"
|
||||
اتصل بمحفظة خارجية لتخزين البتكوين في وحدة تخزين بارد
|
||||
|
||||
2 - التركيز على تجربتك
|
||||
|
||||
كن متحكمًا
|
||||
لا تغادر المفاتيح الخاصة جهازك أبدًا.
|
||||
أنت من يتحكم في مفاتيحك الخاصة
|
||||
Private keys never leave your device.You control your private keys
|
||||
|
||||
مرونة الرسوم
|
||||
بدايةً من ساتوشي واحد. تحددها أنت؛ أي المستخدم بنفسك
|
||||
رسوم مرنة
|
||||
بدايةً من ساتوشي واحد. تحددها أنت أيها المستخدم بنفسك
|
||||
|
||||
الاستبدال بالرسوم (RBF)
|
||||
(RBF) عزز سرعة معاملاتك بزيادة الرسوم (BIP125)
|
||||
|
||||
محافظ للتحقق من الرصيد فقط
|
||||
تتيح لك محافظ التحقق من الرصيد متابعة وحدة التخزين البارد دون لمس الجهاز.
|
||||
محافظ لمراقبة الرصيد فقط
|
||||
تتيح لك محافظ مراقبة الرصيد من متابعة وحدة التخزين البارد دون الحاجة للمس المحفظة الخارجية.
|
||||
|
||||
شبكة Lightning
|
||||
محفظة Lightning دون الحاجة إلى إعدادها. معاملات رخيصة جدًا وسريعة مع توفير أفضل تجربة لمستخدمي Bitcoin.
|
||||
|
||||
شراء Bitcoin
|
||||
ادخل في أغوار الثورة المالية المفتوحة مع القدرة على شراء Bitcoin مباشرة من محفظتك.
|
||||
|
||||
متداول محلي
|
||||
منصة تداول Bitcoin من نظير إلى نظير، والتي تتيح لك شراء عملات Bitcoin وبيعها مباشرةً إلى المستخدمين الآخرين دون الاستعانة بأطراف خارجية.
|
||||
شبكة البرق
|
||||
محفظة البرق دون الحاجة إلى إعدادها. معاملات رخيصة جدًا وسريعة مع توفير أفضل تجربة لمستخدمي البتكوين.
|
46
ios/fastlane/metadata/ja/description.txt
Normal file
46
ios/fastlane/metadata/ja/description.txt
Normal file
|
@ -0,0 +1,46 @@
|
|||
ビットコインを送る・受け取る・保存することができる、セキュリティとシンプルさを重視したウォレットです。
|
||||
|
||||
BlueWalletは、秘密鍵をあなたが所有するビットコインウォレット。ビットコインユーザーが作った、コミュニティのためのビットコインウォレット。
|
||||
|
||||
世界中の誰とでもすぐに取引ができ、あなたのポケットからファイナンスの仕組みを変革します。
|
||||
|
||||
無料で数の制限なくビットコインウォレットを作ることも、お持ちのウォレットをインポートすることもできます。シンプルでスピーディです。
|
||||
|
||||
_____
|
||||
|
||||
特長:
|
||||
|
||||
|
||||
1 - デザインによるセキュリティ
|
||||
|
||||
オープンソース
|
||||
MITライセンスにより、あなた自身でビルド・実行可能! ReactNative製。
|
||||
|
||||
もっともらしい否認
|
||||
アクセスの開示を強要された時でも、フェイクのビットコインウォレットを復号できるパスワード
|
||||
|
||||
完全な暗号化
|
||||
iOSのマルチレイヤー暗号化に加え、追加のパスワードですべてを暗号化
|
||||
|
||||
フルノード
|
||||
Electrumを通じてビットコインフルノードに接続
|
||||
|
||||
コールドストレージ
|
||||
ハードウェアウォレットに接続し、コインをコールドストレージに保存
|
||||
|
||||
2 - ユーザーエクスペリエンス重視
|
||||
|
||||
思いのままに
|
||||
秘密鍵はずっとあなたのデバイスの中に。あなたが秘密鍵をコントロールします
|
||||
|
||||
柔軟な手数料
|
||||
1 Satoshiから。ユーザーのあなた自身が決定します
|
||||
|
||||
Replace-By-Fee
|
||||
(RBF) 手数料を増やして取引をスピードアップ (BIP125)
|
||||
|
||||
閲覧専用ウォレット
|
||||
閲覧専用ウォレットにより、ハードウェアに触れることなくコールドストレージを監視できます。
|
||||
|
||||
ライトニングネットワーク
|
||||
設定の要らないライトニングウォレット。有り得ないほど安く、速い取引で最高のビットコイン体験を。
|
1
ios/fastlane/metadata/ja/keywords.txt
Normal file
1
ios/fastlane/metadata/ja/keywords.txt
Normal file
|
@ -0,0 +1 @@
|
|||
ビットコイン,ウォレット,ビットコインウォレット,ブロックチェーン,btc,仮想通貨,暗号通貨,electrum,イーサリアム
|
1
ios/fastlane/metadata/ja/name.txt
Normal file
1
ios/fastlane/metadata/ja/name.txt
Normal file
|
@ -0,0 +1 @@
|
|||
BlueWallet - ビットコインウォレット
|
10
ios/fastlane/metadata/ja/promotional_text.txt
Normal file
10
ios/fastlane/metadata/ja/promotional_text.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
特長
|
||||
|
||||
* オープンソース
|
||||
* 完全な暗号化
|
||||
* もっともらしい否認
|
||||
* 柔軟な手数料
|
||||
* Replace-By-Fee (RBF)
|
||||
* SegWit
|
||||
* 閲覧専用(Sentinel)ウォレット
|
||||
* ライトニングネットワーク
|
|
@ -13,7 +13,7 @@ Entenda o que oferecemos:
|
|||
|
||||
1 - Designada para ser segura
|
||||
|
||||
De Código Aberto
|
||||
Código Aberto
|
||||
Licenciado pelo MIT, você pode compilar e rodar a sua própria versão! Feita com ReactNative
|
||||
|
||||
Negação plausível
|
||||
|
@ -26,21 +26,21 @@ Nó completo
|
|||
Conecte-se ao seu nó completo Bitcoin através da Electrum
|
||||
|
||||
Armazenamento Frio
|
||||
Conecte com sua carteira hardware e mantenha suas moedas em armazenamento frio
|
||||
Conecte-se a sua carteira hardware e mantenha suas moedas em armazenamento frio
|
||||
|
||||
2 - Foco na sua experiência
|
||||
2 - Focada na sua experiência
|
||||
|
||||
Tenha o controle
|
||||
Chaves privadas nunca saem do seu dispositivo. Você detém o controle delas.
|
||||
|
||||
Taxas flexíveis
|
||||
A partir de 1 satoshi. Você decide
|
||||
A partir de 1 Satoshi. Definidas por você, o usuário
|
||||
|
||||
Replace-By-Fee
|
||||
(RBF) Acelere suas transações aumentando a taxa depois de enviar (BIP125)
|
||||
|
||||
Carteiras somente leitura
|
||||
Carteiras somente leitura permitem a você ficar de olho no seu armazenamento frio sem tocar na hardwallet.
|
||||
Carteiras somente leitura permitem a você ficar de olho no seu armazenamento frio sem tocar na sua carteira hardware.
|
||||
|
||||
Lightning Network
|
||||
Rede Lightning
|
||||
Carteira Lightning sem precisar configurar nada. Transações muito baratas e rápidas para ter a melhor experiência com o Bitcoin.
|
136
loc/ar.json
136
loc/ar.json
|
@ -9,9 +9,7 @@
|
|||
"disabled": "معطّل",
|
||||
"of": "{number} من {total}",
|
||||
"ok": "موافق",
|
||||
"storage_is_encrypted": "وحدة التخزين الخاصة بك مشفرة. أنت بحاجة إلى كلمة المرور لفك تشفيرها",
|
||||
"allow": "السماح",
|
||||
"dont_allow": "عدم السماح",
|
||||
"storage_is_encrypted": "وحدة التخزين مشفرة. أنت بحاجة إلى كلمة المرور لفك تشفيرها",
|
||||
"yes": "نعم",
|
||||
"no": "لا",
|
||||
"save": "حفظ",
|
||||
|
@ -20,19 +18,22 @@
|
|||
"wallet_key": "مفتاح المحفظة",
|
||||
"invalid_animated_qr_code_fragment": "قطعة من رمز الـ QRcode المتحرك غير صحيحة، حاول مرة اخرى.",
|
||||
"file_saved": "تم حفظ الملف {filePath} في {destination}.",
|
||||
"file_save_title": "احفظ الملف",
|
||||
"file_save_location": "حدد مكان حفظ {filePath}",
|
||||
"downloads_folder": "مجلد التنزيلات",
|
||||
"external_storage": "التخزين خارجيًا",
|
||||
"discard_changes": "تجاهل التغييرات؟",
|
||||
"discard_changes_detail": "يوجد تغييرات غير محفوظة. هل أنت متأكد من الخروج من الشاشة وتجاهلها؟"
|
||||
"close": "اغلاق",
|
||||
"change_input_currency": "تغيير عملة الادخال",
|
||||
"refresh": "تحديث",
|
||||
"more": "المزيد",
|
||||
"pick_image": "اختر صورة من الصور",
|
||||
"pick_file": "اختر ملف",
|
||||
"enter_amount": "أدخل القيمة",
|
||||
"qr_custom_input_button": "أنقر ١٠ مرات لإدخال قيمة مخصصة"
|
||||
},
|
||||
"alert": {
|
||||
"default": "تنبيه"
|
||||
},
|
||||
"azteco": {
|
||||
"codeIs": "رمز القسيمة الخاص بك هو",
|
||||
"errorBeforeRefeem": "يجب عليك إضافة محفظة Bitcoin أولًا قبل الاسترداد.",
|
||||
"errorBeforeRefeem": "يجب عليك إضافة محفظة بتكوين أولًا قبل الاسترداد.",
|
||||
"errorSomething": "حدث خطأ. هل ما زالت هذه القسيمة صالحة؟",
|
||||
"redeem": "الاسترداد إلى المحفظة",
|
||||
"redeemButton": "الاسترداد",
|
||||
|
@ -61,28 +62,21 @@
|
|||
"force_close_channel": "هل تود فرض اغلاق القناة؟",
|
||||
"expired": "منتهية الصلاحية",
|
||||
"node_alias": "الاسم المستعار للعقدة",
|
||||
"expiredLow": "انتهت صلاحيتها",
|
||||
"expiresIn": "تنتهي بعد {time} دقيقة",
|
||||
"payButton": "دفع",
|
||||
"placeholder": "فاتورة",
|
||||
"placeholder": "برقية",
|
||||
"open_channel": "فتح قناة",
|
||||
"funding_amount_placeholder": "مبلغ التمويل، على سبيل المثال 0.001",
|
||||
"opening_channnel_for_from": "جارِ فتح قناة للمحفظة {forWalletLabel} بتمويل من {fromWalletLabel}",
|
||||
"are_you_sure_open_channel": "هل أنت متأكد أنك تريد فتح هذه القناة؟",
|
||||
"are_you_sure_exit_without_new_channel": "هل أنت متأكد أنك تريد الخروج دون فتح قناة؟",
|
||||
"public": "علني",
|
||||
"public_description": "قابلة للرؤية في الشبكة: من الممكن أن تكون عقدة توجيه وتكسب رسوم.",
|
||||
"private": "خاص",
|
||||
"private_description": "غير قابلة للرؤية في الشبكة: ستحافظ على خصوصية مدفوعاتك.",
|
||||
"local_reserve": "الاحتياطي المحلي",
|
||||
"potentialFee": "الرسوم المحتملة: {fee}",
|
||||
"remote_host": "المضيف البعيد",
|
||||
"refill": "إعادة التعبئة",
|
||||
"reconnect_peer": "إعادة الاتصال بالأقران",
|
||||
"refill_create": "للمتابعة، يُرجى إنشاء محفظة Bitcoin لإعادة التعبئة باستخدامها.",
|
||||
"refill_create": "للمتابعة، يُرجى إنشاء محفظة بتكوين لإعادة التعبئة باستخدامها.",
|
||||
"refill_external": "إعادة التعبئة باستخدام محفظة خارجية",
|
||||
"refill_lnd_balance": "إعادة تعبئة رصيد محفظة البرق (Lightning)",
|
||||
"sameWalletAsInvoiceError": "لا يمكنك دفع فاتورة بنفس المحفظة المستخدمة لإنشائها.",
|
||||
"sameWalletAsInvoiceError": "لايمكنك دفع البرقية من نفس المحفظة",
|
||||
"title": "إدارة الأموال",
|
||||
"can_send": "يمكن أن ترسل",
|
||||
"can_receive": "يمكن ان تستقبل",
|
||||
|
@ -92,13 +86,12 @@
|
|||
"additional_info": "معلومة إضافية",
|
||||
"for": "إلى:",
|
||||
"lightning_invoice": "فاتورة برق (Lightning)",
|
||||
"has_been_paid": "تم دفع هذه الفاتورة إلى",
|
||||
"open_direct_channel": "فتح قناة مباشرة مع هذه العقدة:",
|
||||
"open_direct_channel": "فتح قناة مباشرة مع هذه النود:",
|
||||
"please_pay_between_and": "يرجى دفع ما بين {min} و{max}",
|
||||
"please_pay": "يُرجى الدفع",
|
||||
"preimage": "الصورة الأصلية",
|
||||
"sats": "بالساتوشي",
|
||||
"wasnt_paid_and_expired": "لم يتم دفع هذه الفاتورة وانتهت صلاحيتها"
|
||||
"wasnt_paid_and_expired": "لم يتم دفع هذه البرقية وانتهت صلاحيتها"
|
||||
},
|
||||
"plausibledeniability": {
|
||||
"create_fake_storage": "إنشاء وحدة تخزين مشفرة",
|
||||
|
@ -108,7 +101,7 @@
|
|||
"help2": "ستعمل وحدة التخزين الجديدة بكامل طاقتها، ويمكنك تخزين بعض المبالغ البسيطة هناك بحيث تبدو أكثر مصداقية ومنطقية.",
|
||||
"password_should_not_match": "كلمة المرور قيد الاستخدام حاليًا. يُرجى تجربة كلمة مرور مختلفة.",
|
||||
"passwords_do_not_match": "كلمات المرور غير متطابقة، حاول مرة أخرى.",
|
||||
"retype_password": "إعادة إدخال كلمة المرور",
|
||||
"retype_password": "أعد إدخال كلمة المرور",
|
||||
"success": "نجحت العملية",
|
||||
"title": "الإنكار المقبول"
|
||||
},
|
||||
|
@ -120,13 +113,12 @@
|
|||
"ok_lnd": "حسنًا، لقد حفظتها.",
|
||||
"text": "يُرجى أخذ لحظة من وقتك لكتابة هذه العبارة التذكيرية على ورقة. إنها وسيلتك الاحتياطية لاستعادة المحفظة على جهاز آخر.",
|
||||
"text_lnd": "يُرجى حفظ هذه النسخة الاحتياطية للمحفظة. لكي تتتمكن من استعادة المحفظة في حالة فقدها.",
|
||||
"text_lnd2": "هذه المحفظة يتم استضافتها بواسطة BlueWallet.",
|
||||
"title": "تم إنشاء محفظتك"
|
||||
},
|
||||
"receive": {
|
||||
"details_create": "إنشاء",
|
||||
"details_label": "الوصف",
|
||||
"details_setAmount": "استلام مبلغ",
|
||||
"details_setAmount": "استلام مبلغ محدد",
|
||||
"details_share": "مشاركة",
|
||||
"header": "استلام",
|
||||
"maxSats": "الحد الأقصى للمبلغ هو {min} ساتوشي",
|
||||
|
@ -150,23 +142,23 @@
|
|||
"create_fee": "الرسوم",
|
||||
"create_memo": "مذكرة",
|
||||
"create_satoshi_per_vbyte": "ساتوشي لكل ف بايت",
|
||||
"create_this_is_hex": "هذا هو التنسيق السداسي لمعاملتك، موقَّع وجاهز للبث على الشبكة.",
|
||||
"create_this_is_hex": "هذا هو رقم العملية بصيغة ست عشرية (Hex) ، موقَّع وجاهز للبث على الشبكة.",
|
||||
"create_to": "إلى",
|
||||
"create_tx_size": "حجم المعاملة",
|
||||
"create_verify": "التحقق على coinb.in",
|
||||
"details_add_rec_add": "إضافة المستلم",
|
||||
"details_add_rec_rem": "إزالة المستلم",
|
||||
"details_address": "العنوان",
|
||||
"details_address_field_is_not_valid": "حقل العنوان غير صالح",
|
||||
"details_address_field_is_not_valid": "العنوان غير صالح",
|
||||
"details_adv_fee_bump": "السماح بزيادة الرسوم",
|
||||
"details_adv_full": "استخدام الرصيد الكامل",
|
||||
"details_adv_full_sure": "هل أنت متأكد أنك تريد استخدام الرصيد الكامل لمحفظتك لهذه المعاملة؟",
|
||||
"details_adv_full_sure_frozen": "هل أنت متأكد انك ترغب بإستخدام الرصيد الكامل لمحفظتك لهذة المعاملة؟ يرجى ملاحظة انه تم استبعاد العملات المجمدة.",
|
||||
"details_adv_import": "استيراد المعاملة",
|
||||
"details_adv_import": "استيراد العملية",
|
||||
"details_adv_import_qr": "استيراد معاملة (QR)",
|
||||
"details_amount_field_is_not_valid": "حقل المبلغ غير صالح",
|
||||
"details_amount_field_is_not_valid": "المبلغ غير صالح",
|
||||
"details_amount_field_is_less_than_minimum_amount_sat": "المبلغ المحدد صغير جدًا. الرجاء إدخال مبلغ أكبر من 500 ساتوشي.",
|
||||
"details_create": "إنشاء فاتورة",
|
||||
"details_create": "إنشاء برقية",
|
||||
"details_error_decode": "يتعذَّر فك تشفير عنوان Bitcoin",
|
||||
"details_fee_field_is_not_valid": "حقل الرسوم غير صالح",
|
||||
"details_frozen": "{amount} بتكوين تم تجميدها",
|
||||
|
@ -178,7 +170,7 @@
|
|||
"details_total_exceeds_balance": "مبلغ الإرسال يتجاوز الرصيد المتاح.",
|
||||
"details_total_exceeds_balance_frozen": "مبلغ الإرسال يتجاوز الرصيد المتاح، يرجى ملاحظة انه تم استبعاد العملات المجمدة.",
|
||||
"details_unrecognized_file_format": "تنسيق ملف غير معروف",
|
||||
"details_wallet_before_tx": "يجب عليك إضافة محفظة Bitcoin أولًا قبل إنشاء معاملة.",
|
||||
"details_wallet_before_tx": "يجب عليك إضافة محفظة بتكوين أولًا قبل إنشاء معاملة.",
|
||||
"dynamic_init": "جارٍ التهيئة...",
|
||||
"dynamic_next": "التالي",
|
||||
"dynamic_prev": "السابق",
|
||||
|
@ -199,7 +191,6 @@
|
|||
"input_paste": "اللصق",
|
||||
"input_total": "الإجمالي:",
|
||||
"permission_camera_message": "نحتاج إلى إذنك لاستخدام الكاميرا الخاصة بك",
|
||||
"permission_camera_title": "إذن باستخدام الكاميرا",
|
||||
"psbt_sign": "وقّع معاملة",
|
||||
"open_settings": "فتح الإعدادات",
|
||||
"permission_storage_later": "اسألني لاحقًا",
|
||||
|
@ -207,14 +198,13 @@
|
|||
"permission_storage_denied_message": "BlueWallet غير قادر على حفظ هذا الملف. يرجى فتح إعدادات جهازك وتمكين إذن التخزين.",
|
||||
"permission_storage_title": "إذن وصول BlueWallet إلى وحدة التخزين",
|
||||
"psbt_clipboard": "النسخ إلى الحافظة",
|
||||
"psbt_this_is_psbt": "هذه معاملة Bitcoin موقَّعة جزئيًا (PSBT). يُرجى الانتهاء من توقيعها باستخدام محفظة الجهاز الخاصة بك.",
|
||||
"psbt_this_is_psbt": "هذه معاملة بتكوين موقَّعة جزئيًا (PSBT). يُرجى توقيعها باستخدام المحفظة الخارجية.",
|
||||
"psbt_tx_export": "التصدير إلى ملف",
|
||||
"no_tx_signing_in_progress": "لا يوجد معاملة توقيع قيد التقدم.",
|
||||
"outdated_rate": "تم تحديث السعر آخر مرة في {date}",
|
||||
"psbt_tx_open": "فتح معاملة موقَّعة",
|
||||
"psbt_tx_scan": "المسح الضوئي معاملة موقَّعة",
|
||||
"qr_error_no_qrcode": "لم نتمكن من العثور على كود QR في الصورة المحددة. تأكد من أن الصورة تحتوي فقط على كود QR ولا تحتوي على محتوى إضافي مثل النص أو الأزرار.",
|
||||
"qr_error_no_wallet": "لا يحتوي الملف المحدَّد على محفظة يمكن استيرادها.",
|
||||
"psbt_tx_scan": "المسح الضوئي لمعاملة موقَّعة",
|
||||
"qr_error_no_qrcode": "لم نتمكن من قراءة رمز الاستجابة السريع (QR) في الصورة المحددة. تأكد أن الصورة تحتوي فقط على رمز (QR) دون أي محتوى إضافي آخر مثل النص أو الأزرار.",
|
||||
"reset_amount": "إعادة تعيين المبلغ",
|
||||
"reset_amount_confirm": "هل تريد إعادة تعيين المبلغ؟",
|
||||
"success_done": "تم",
|
||||
|
@ -227,14 +217,16 @@
|
|||
"about_backup": "احتفظ دائمًا بنسخة احتياطية من مفاتيحك!",
|
||||
"about_free": "محفظة BlueWallet هي مشروع مجاني ومفتوح المصدر، أنشأه مستخدمو Bitcoin.",
|
||||
"about_license": "ترخيص MIT",
|
||||
"about_release_notes": "ملاحظات الإصدار",
|
||||
"about_review": "اترك لنا مراجعتك",
|
||||
"about_release_notes": "معلومات الإصدار",
|
||||
"about_review": "اترك لنا مراجعة",
|
||||
"performance_score": "معدل الأداء: {num}",
|
||||
"run_performance_test": "اختبار الأداء",
|
||||
"about_selftest": "تشغيل اختبار ذاتي",
|
||||
"about_selftest_electrum_disabled": "لا يتوفر الاختبار الذاتي مع وضع Electrum الغير متصل بالانترنت. يرجى تعطيل وضع عدم اتصال الشبكة والمحاولة مرة أخرى.",
|
||||
"about_selftest_ok": "تم اجتياز جميع الاختبارات الداخلية بنجاح. المحفظة تعمل بشكل جيد.",
|
||||
"about_sm_github": "GitHub",
|
||||
"about_sm_discord": "سيرفر Discord",
|
||||
"about_sm_telegram": "دردشة Telegram",
|
||||
"about_sm_telegram": "قناة تيليجرام",
|
||||
"about_sm_twitter": "تابعنا على تويتر",
|
||||
"advanced_options": "الخيارات المتقدمة",
|
||||
"biometrics": "القياسات الحيوية",
|
||||
|
@ -243,7 +235,7 @@
|
|||
"biom_no_passcode": "جهازك ليس لديه رمز مرور. للمتابعة ، يرجى اضافة رمز مرور من إعدادات الجهاز.",
|
||||
"biom_remove_decrypt": "ستتم إزالة جميع محافظك وفك تشفير التخزين الخاص بك. هل انت متأكد انك تريد المتابعة؟",
|
||||
"currency": "العملة",
|
||||
"currency_source": "يتم الحصول على الأسعار من",
|
||||
"currency_source": "تم الاستعلام عن السعر عن طريق",
|
||||
"currency_fetch_error": "حدث خطأ أثناء الحصول على سعر العملة المحددة.",
|
||||
"default_desc": "عند تعطيل هذا الإعداد، ستفتح BlueWallet المحفظة المحدَّدة فور التشغيل.",
|
||||
"default_info": "المعلومات الافتراضية",
|
||||
|
@ -251,7 +243,6 @@
|
|||
"default_wallets": "عرض جميع المحافظ",
|
||||
"electrum_connected": "متصل",
|
||||
"electrum_connected_not": "غير متصل",
|
||||
"electrum_connnected_not_description": "مطلوب اتصال Electrum نشط لاستيراد المحفظة. يمكنك التحقق إذا كان قد تم إنشاء اتصال بالانتقال إلى قسم الشبكة في الإعدادات.",
|
||||
"electrum_error_connect": "يتعذَّر الاتصال بخادم Electrum المقدَّم",
|
||||
"lndhub_uri": "على سبيل المثال، {example}",
|
||||
"electrum_host": "على سبيل المثال، {example}",
|
||||
|
@ -279,7 +270,6 @@
|
|||
"tor_unsupported": "اتصالات Tor غير مدعومة.",
|
||||
"encrypt_decrypt": "فك تشفير وحدة التخزين",
|
||||
"encrypt_decrypt_q": "هل أنت متأكد أنك تريد فك تشفير وحدة التخزين الخاصة بك؟ سيسمح إجراء ذلك بالوصول إلى محافظك دون كلمة مرور.",
|
||||
"encrypt_del_uninstall": "الحذف في حال إلغاء تثبيت BlueWallet",
|
||||
"encrypt_enc_and_pass": "مشفرة ومحمية بكلمة مرور",
|
||||
"encrypt_title": "الأمان",
|
||||
"encrypt_tstorage": "وحدة التخزين",
|
||||
|
@ -287,10 +277,10 @@
|
|||
"encrypt_use_expl": "سيتم استخدام {type} لتأكيد هويتك قبل إجراء معاملة أو فتح محفظة أو تصديرها أو حذفها. ولن يتم استخدام {type} لفتح وحدة تخزين مشفرة.",
|
||||
"general": "عام",
|
||||
"general_adv_mode": "الوضع المتقدم",
|
||||
"general_adv_mode_e": "عند تمكين هذا الإعداد، سترى خيارات متقدمة في أثناء إنشاء المحفظة، مثل أنواع المحافظ المختلفة، والقدرة على تحديد مثيل LNDHub الذي ترغب في الاتصال به، وإنتروبيا (عشوائية) مخصصة.",
|
||||
"general_continuity": "الارتباط",
|
||||
"general_continuity_e": "عند تمكين هذا الإعداد، ستتمكن من عرض المحافظ والمعاملات المحدَّدة باستخدام أجهزتك الأخرى المتصلة بحساب Apple iCloud.",
|
||||
"groundcontrol_explanation": "GroundControl هو خادم إشعارات فورية مجاني مفتوح المصدر لمحافظ Bitcoin. يمكنك تثبيت خادم GroundControl الخاص بك ووضع عنوان URL له هنا لعدم الاعتماد على البنية التحتية لمحفظة BlueWallet. اترك الحقل فارغًا لاستخدام الإعدادات الافتراضية",
|
||||
"general_adv_mode_e": "عند تمكين هذا الإعداد، سترى خيارات متقدمة أثناء إنشاء المحفظة، مثل أنواع محافظ مختلفة، القدرة على الاتصال ب LNDHub محدد، وإنتروبيا (عشوائية) مخصصة.",
|
||||
"general_continuity": "الاتساق",
|
||||
"general_continuity_e": "عند تمكين هذا الإعداد، ستتمكن من عرض المحافظ والعمليات المحدَّدة باستخدام أجهزتك الأخرى المتصلة بنفس حساب Apple iCloud.",
|
||||
"groundcontrol_explanation": "GroundControl هو خادم إشعارات فورية مجاني مفتوح المصدر لمحافظ البتكوين. يمكنك تثبيت خادم GroundControl الخاص بك ووضع عنوان (URL) له هنا لعدم الاعتماد على البنية التحتية لمحفظة BlueWallet. اترك الحقل فارغًا لاستخدام الإعدادات الافتراضية",
|
||||
"header": "الإعدادات",
|
||||
"language": "اللغة",
|
||||
"last_updated": "آخر تحديث",
|
||||
|
@ -299,9 +289,9 @@
|
|||
"lightning_saved": "تم حفظ تغييراتك بنجاح",
|
||||
"lightning_settings": "إعدادات البرق (Lightning)",
|
||||
"tor_settings": "اعدادات tor",
|
||||
"lightning_settings_explain": "للاتصال بعقدة LND الخاصة بك، يُرجى تثبيت LndHub ثم وضع رابطه هنا في الإعدادات. اترك الحقل فارغًا لاستخدام LNDHub (lndhub.io) لمحفظة BlueWallet. ستتصل المحافظ التي يتم إنشاؤها بعد حفظ التغييرات بنفس عقدة LNDHub التي قمت بتحديدها.",
|
||||
"lightning_settings_explain": "للاتصال بنود LND الخاص بك، يُرجى تثبيت LNDHub ثم وضع رابطه هنا في الإعدادات. تذكر: فقط المحافظ التي يتم إنشاؤها بعد حفظ التغييرات ستتصل بنود LNDHub التي قمت بإضافتها.",
|
||||
"network": "الشبكة",
|
||||
"network_broadcast": "بث المعاملة",
|
||||
"network_broadcast": "بث العملية",
|
||||
"network_electrum": "خادم Electrum",
|
||||
"not_a_valid_uri": "معرِّف URI غير صالح",
|
||||
"notifications": "الإشعارات",
|
||||
|
@ -312,7 +302,6 @@
|
|||
"plausible_deniability": "الإنكار المقبول",
|
||||
"privacy": "الخصوصية",
|
||||
"privacy_read_clipboard": "قراءة الحافظة",
|
||||
"privacy_read_clipboard_alert": "سيعرض BlueWallet اختصاراً اذا كانت هناك فاتورة أو عنوان موجود في الحافظة للتعامل معه.",
|
||||
"privacy_system_settings": "اعدادات الجهاز",
|
||||
"privacy_quickactions": "اختصارات المحفظة",
|
||||
"privacy_quickactions_explanation": "المس مع الاستمرار ايقونة تطبيق BlueWallet على شاشتك الرئيسية للمشاهدة عرض سريع لرصيد محفظتك.",
|
||||
|
@ -344,7 +333,7 @@
|
|||
"copy_link": "نسخ الرابط",
|
||||
"expand_note": "توسيع الملاحظة",
|
||||
"cpfp_create": "إنشاء",
|
||||
"cpfp_exp": "سننشئ معاملة أخرى تستبدل معاملتك غير المؤكدة. وسيكون إجمالي الرسوم أعلى من رسوم المعاملة الأصلية؛ حتى يجري تعدينها بشكلٍ أسرع. وهذا ما يُسمَّى CPFP؛ أي دعم المعاملة الرئيسية بمعاملة فرعية.",
|
||||
"cpfp_exp": "سننشئ عملية أخرى تستبدل عمليتك غير المؤكدة. وسيكون إجمالي الرسوم أعلى من رسوم العملية الأصلية؛ حتى يجري تعدينها بشكلٍ أسرع. وهذا ما يُسمَّى CPFP؛ أي التحكم بالعملية الرئيسية بمعاملة فرعية.",
|
||||
"cpfp_no_bump": "هذه المعاملة غير قابلة للتسريع",
|
||||
"cpfp_title": "تسريع المعاملة (CPFP)",
|
||||
"details_balance_hide": "إخفاء الرصيد",
|
||||
|
@ -358,12 +347,12 @@
|
|||
"details_from": "من",
|
||||
"details_inputs": "المدخلات",
|
||||
"details_outputs": "المخرجات",
|
||||
"date": "التاريخ",
|
||||
"details_received": "التاريخ",
|
||||
"transaction_note_saved": "تم حفظ مذكرة المعاملة بنجاح.",
|
||||
"details_show_in_block_explorer": "العرض في مستكشف الكتل",
|
||||
"details_title": "المعاملة",
|
||||
"details_title": "العملية",
|
||||
"details_to": "إلى",
|
||||
"details_transaction_details": "تفاصيل المعاملة",
|
||||
"enable_offline_signing": "هذه المحفظة لا يتم استعمالها مع التوقيع دون اتصال. هل ترغب في تمكينه الآن؟",
|
||||
"list_conf": "تأكيد: {number}",
|
||||
"pending": "قيد الانتظار",
|
||||
|
@ -373,13 +362,13 @@
|
|||
"eta_3h": "الوقت المقدر للتأكيد: في حوالي 3 ساعات",
|
||||
"eta_1d": "الوقت المقدر للتأكيد: في حوالي يوم واحد",
|
||||
"view_wallet": "لمشاهدة {walletLabel}",
|
||||
"list_title": "المعاملات",
|
||||
"list_title": "العمليات",
|
||||
"open_url_error": "تعذر فتح الرابط باستخدام المتصفح الافتراضي. يرجى تغيير المتصفح الافتراضي الخاص بك وحاول مرة أخرى.",
|
||||
"rbf_explain": "سنستبدل هذه المعاملة بمعاملة جديدة تدفع رسوم اعلى؛ حتى يجري تعدينها بشكلٍ أسرع. وهذا ما يُسمَّى RBF؛ أي الاستبدال بالرسوم.",
|
||||
"rbf_title": "تسريع المعاملة (RBF)",
|
||||
"status_bump": "تسريع المعاملة",
|
||||
"status_cancel": "إلغاء المعاملة",
|
||||
"transactions_count": "عدد المعاملات",
|
||||
"status_cancel": "إلغاء العملية",
|
||||
"transactions_count": "عدد العمليات",
|
||||
"txid": "معرّف المعاملة",
|
||||
"updating": "جارٍ التحديث ..."
|
||||
},
|
||||
|
@ -393,10 +382,9 @@
|
|||
"add_import_wallet": "استيراد محفظة",
|
||||
"add_lightning": "البرق",
|
||||
"add_lightning_explain": "لإرسال المعاملات بشكل فوري عبر شبكة البرق Lightning ",
|
||||
"add_lndhub": "اتصل ببرنامج تضمين LNDHub الخاص بك",
|
||||
"add_lndhub_error": "عنوان العقدة المقدَّم هو عقدة LNDHub صالحة.",
|
||||
"add_lndhub_placeholder": "عنوان العقدة الخاص بك",
|
||||
"add_or": "أو",
|
||||
"add_lndhub": "اتصل ب LNDHub الخاص بك",
|
||||
"add_lndhub_error": "عنوان النود المقدَّم غير صالح.",
|
||||
"add_lndhub_placeholder": "عنوان النود الخاص بك",
|
||||
"add_placeholder": "محفظتي الأولى",
|
||||
"add_title": "إضافة محفظة",
|
||||
"add_wallet_name": "الاسم",
|
||||
|
@ -415,10 +403,8 @@
|
|||
"details_derivation_path": "مسار الاشتقاق (derivation path)",
|
||||
"details_display": "العرض في قائمة المحافظ",
|
||||
"details_export_backup": "التصدير/النسخ الاحتياطي",
|
||||
"details_master_fingerprint": "بصمة الإصبع الرئيسية",
|
||||
"details_ms_l": "{m} of {n} legacy (p2sh)",
|
||||
"details_ms_ns": "{m} of {n} native segwit (p2wsh)",
|
||||
"details_ms_ws": "{m} of {n} wrapped segwit (p2sh-p2wsh)",
|
||||
"details_export_history": "تصدير السجل ل ملف CSV",
|
||||
"details_master_fingerprint": "البصمة الرئيسية",
|
||||
"details_multisig_type": "متعدد التواقيع",
|
||||
"details_no_cancel": "لا، إلغاء",
|
||||
"details_save": "حفظ",
|
||||
|
@ -437,10 +423,10 @@
|
|||
"import_passphrase_message": "أدخل عبارة المرور إذا كنت قد استخدمت أيًا منها",
|
||||
"import_error": "فشل الاستيراد. يُرجى التأكد من أن البيانات المقدَّمة صالحة.",
|
||||
"import_explanation": "اكتب هنا عبارتك التذكيرية أو مفتاحك الخاص أو WIF أو أي شيء لديك. ستبذل BlueWallet قصارى جهدها لتخمين التنسيق الصحيح واستيراد محفظتك",
|
||||
"import_file": "استيراد ملف",
|
||||
"import_imported": "تم الاستيراد",
|
||||
"import_scan_qr": "المسح الضوئي أو استيراد ملف",
|
||||
"import_success": "تم استيراد محفظتك بنجاح.",
|
||||
"import_success_watchonly": "تم استيراد محفظتك بنجاح. تنويه: هذه محفظة مراقبة فقط. لا يمكنك تنفيذ العمليات.",
|
||||
"import_search_accounts": "البحث عن حسابات",
|
||||
"import_title": "الاستيراد",
|
||||
"import_discovery_title": "اكتشاف",
|
||||
|
@ -461,13 +447,11 @@
|
|||
"list_empty_txs1_lightning": "يجب استخدام محفظة البرق (Lightning) في معاملاتك اليومية. الرسوم رخيصة جدًا والسرعة كبيرة حقًا.",
|
||||
"list_empty_txs2": "ابدأ بمحفظتك",
|
||||
"list_empty_txs2_lightning": "\nللبدء في استخدامها، اضغط على \"إدارة الأموال\" واشحن رصيدك.",
|
||||
"list_header": "تمثِّل المحفظة زوجًا من المفاتيح السرية (المفتاح الخاص) وعنوان يمكنك مشاركته لاستلام العملات المعدنية.",
|
||||
"list_import_problem": "حدثت مشكلة في استيراد هذه المحفظة",
|
||||
"list_latest_transaction": "آخر معاملة",
|
||||
"list_latest_transaction": "آخر عملية",
|
||||
"list_ln_browser": "متصفح LApp",
|
||||
"list_long_choose": "اختيار صورة",
|
||||
"list_long_clipboard": "النسخ من الحافظة",
|
||||
"list_long_scan": "مسح رمز الاستجابة السرعة ضوئيًا",
|
||||
"list_long_scan": "مسح رمز الاستجابة (QR) ضوئيًا",
|
||||
"list_title": "المحافظ",
|
||||
"list_tryagain": "إعادة المحاولة",
|
||||
"no_ln_wallet_error": "قبل دفع فاتورة برق (Lightning) ، يجب عليك أولاً إضافة محفظة برق (Lightning).",
|
||||
|
@ -475,11 +459,9 @@
|
|||
"reorder_title": "إعادة ترتيب المحافظ",
|
||||
"reorder_instructions": "اضغط باستمرار على اي محفظة لتحريكها عبر القائمة",
|
||||
"please_continue_scanning": "الرجاء متابعة الفحص.",
|
||||
"scan_error": "خطأ في الفحص",
|
||||
"select_no_bitcoin": "لا توجد محافظ Bitcoin متاحة حاليًا.",
|
||||
"select_no_bitcoin": "لا توجد محافظ بتكوين متاحة حاليًا.",
|
||||
"select_no_bitcoin_exp": "تحتاج إلى محفظة بيتكوين لإعادة تعبئة محافظ البرق (Lightning). يُرجى إنشاء محفظة أو استيراد واحدة.",
|
||||
"select_wallet": "اختيار محفظة",
|
||||
"take_photo": "التقاط صورة",
|
||||
"xpub_copiedToClipboard": "تم النسخ إلى الحافظة.",
|
||||
"pull_to_refresh": "اسحب للتحديث",
|
||||
"warning_do_not_disclose": "تحذير! لا تنشر هذا.",
|
||||
|
@ -509,7 +491,6 @@
|
|||
"cosign_this_transaction": "المشاركة في التوقيع على هذه المعاملة؟",
|
||||
"lets_start": "لنبدأ",
|
||||
"create": "إنشاء",
|
||||
"provide_key": "قدم مفتاحًا",
|
||||
"native_segwit_title": "أفضل ممارسة",
|
||||
"wrapped_segwit_title": "أفضل توافق",
|
||||
"legacy_title": "تنسيق قديم",
|
||||
|
@ -526,7 +507,6 @@
|
|||
"quorum_header": "العدد",
|
||||
"of": "من",
|
||||
"wallet_type": "نوع المحفظة",
|
||||
"view_key": "عرض",
|
||||
"invalid_mnemonics": "لا يبدو أن هذه العبارة التذكرية صالحة.",
|
||||
"invalid_cosigner": "بيانات شريك توقيع ليست صحيحة",
|
||||
"not_a_multisignature_xpub": "هذه ليست XPUB من محفظة متعددة التوقيعات!",
|
||||
|
@ -534,14 +514,11 @@
|
|||
"create_new_key": "إنشاء جديد",
|
||||
"scan_or_open_file": "المسح الضوئي أو استيراد ملف",
|
||||
"i_have_mnemonics": "لدي عبارة تذكيرية لهذا المفتاح.",
|
||||
"please_write_down_mnemonics": "يُرجى كتابة هذه العبارة التذكيرية على ورقة. لا تقلق، يمكنك كتابتها لاحقًا.",
|
||||
"i_wrote_it_down": "حسنًا، لقد دوَّنتها!",
|
||||
"type_your_mnemonics": "أدخل العبارة التذكيرية لاستيراد مفتاح خزنتك الحالية.",
|
||||
"this_is_cosigners_xpub": "هذا هو XPUB الخاص بشريك التوقيع—وهو جاهز للاستيراد إلى محفظة أخرى. من الآمن مشاركته.",
|
||||
"wallet_key_created": "تم انشاء خزنتك. يُرجى أخذ لحظة من وقتك لعمل نسخ احتياطي لعبارتك التذكيرية.",
|
||||
"are_you_sure_seed_will_be_lost": "هل أنت متأكد؟ ستفقد عبارتك التذكيرية إذا لم يكن لديك نسخة احتياطية.",
|
||||
"forget_this_seed": "انسى هذه العبارة التذكيرية واستخدام XPUB بدلا من ذلك.",
|
||||
"invalid_fingerprint": "بصمة العبارة التذكيرية هذه لا تتطابق مع بصمة شريك التوقيع.",
|
||||
"view_edit_cosigners": "عرض/تحرير شركاء التوقيع",
|
||||
"this_cosigner_is_already_imported": "تم استيراد شريك التوقيع هذا بالفعل.",
|
||||
"export_signed_psbt": "تصدير توقيع PSBT",
|
||||
|
@ -594,7 +571,6 @@
|
|||
"sign_title": "التوقيع/التحقق من الرسالة",
|
||||
"sign_help": "هنا يمكنك إنشاء أو التحقق من توقيع مشفر بناءً على عنوان Bitcoin.",
|
||||
"sign_sign": "توقيع",
|
||||
"sign_sign_submit": "توقيع وارسال",
|
||||
"sign_verify": "تحقق",
|
||||
"sign_signature_correct": "نجح التحقق!",
|
||||
"sign_signature_incorrect": "فشل التحقق!",
|
||||
|
@ -622,5 +598,11 @@
|
|||
"auth_answer": "لقد قمت بالتوثيق مع {hostname} بنجاح!",
|
||||
"could_not_auth": "لم نتمكن من توثيقك على {hostname}.",
|
||||
"authenticate": "التوثيق"
|
||||
},
|
||||
"bip47": {
|
||||
"payment_code": "كود الدفع",
|
||||
"payment_codes_list": "قائمة أكواد الدفع",
|
||||
"who_can_pay_me": "من يستطيع الدفع لي:",
|
||||
"purpose": "أكواد المشاركة التي يمكن أعادة استخدامها (BIP47)"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
"of": "{number} от {total}",
|
||||
"ok": "OK",
|
||||
"storage_is_encrypted": "Вашият портфейл е криптиран. Необходима е парола за декриптиране",
|
||||
"allow": "Разреши",
|
||||
"dont_allow": "Не разрешавай",
|
||||
"yes": "Да",
|
||||
"no": "Не",
|
||||
"save": "Запази",
|
||||
|
@ -18,12 +16,7 @@
|
|||
"wallet_key": "Парола на портфейла",
|
||||
"invalid_animated_qr_code_fragment": "Невалиден анимиран QRCode фрагмент. Моля, опитай отново.",
|
||||
"file_saved": "Файлът {filePath} беше запазен в {destination}.",
|
||||
"file_save_title": "Запази",
|
||||
"file_save_location": "Избери къде да запазиш {filePath}",
|
||||
"downloads_folder": "Папка с изтегляния",
|
||||
"external_storage": "Външно хранилище",
|
||||
"discard_changes": "Отказваш промените?",
|
||||
"discard_changes_detail": "Имате не запазени промени. Искаш ли да излезеш?"
|
||||
"downloads_folder": "Папка с изтегляния"
|
||||
},
|
||||
"azteco": {
|
||||
"codeIs": "Цода на вашият ваучър е",
|
||||
|
@ -47,7 +40,6 @@
|
|||
"lnd": {
|
||||
"errorInvoiceExpired": "Изтекла фактура",
|
||||
"expired": "Изтекла",
|
||||
"expiredLow": "изтекла",
|
||||
"payButton": "Плати",
|
||||
"placeholder": "Фактура",
|
||||
"potentialFee": "Възможна такса: {fee}",
|
||||
|
@ -62,7 +54,6 @@
|
|||
"additional_info": "Допълнителна информация",
|
||||
"for": "За:",
|
||||
"lightning_invoice": "Лайтнинг фактура",
|
||||
"has_been_paid": "Фактурата е платена",
|
||||
"open_direct_channel": "Директно свързване с нода:",
|
||||
"please_pay": "Моля, плати",
|
||||
"sats": "сатоши",
|
||||
|
@ -84,8 +75,7 @@
|
|||
"ask": "Запазихте ли паролата - 12/24 думи за портфейла? В случай, че изгубите достъп до устройството, паролата е необходима за да въстановите средствата. В случай, че загубите паролата - 12/24 думи, перманентно ще изгубите достъп до средствата.",
|
||||
"ask_no": "Не, не съм",
|
||||
"ask_yes": "Да",
|
||||
"text_lnd": "Моля, запазете паролата/ думите. Те ви позволяват да възтановите портфейла и средствата си на друго устройство.",
|
||||
"text_lnd2": "Този портфейл се подържа от Блу Уолет."
|
||||
"text_lnd": "Моля, запазете паролата/ думите. Те ви позволяват да възтановите портфейла и средствата си на друго устройство."
|
||||
},
|
||||
"receive": {
|
||||
"details_create": "Създай",
|
||||
|
@ -162,7 +152,6 @@
|
|||
"no_tx_signing_in_progress": "Няма транзакция в прогрес.",
|
||||
"psbt_tx_open": "Отвори подписана транзакция",
|
||||
"psbt_tx_scan": "Подпиши транзакция",
|
||||
"qr_error_no_wallet": "Избраният файл не съдържа портфейл който може да бъде импортиран.",
|
||||
"success_done": "Готово",
|
||||
"txSaved": "Файл с трансакцията ({filePath}) беше запазен в папката Свалени.",
|
||||
"problem_with_psbt": "Проблем с ЧПБТ / PSBT"
|
||||
|
@ -188,7 +177,6 @@
|
|||
"biom_no_passcode": "Вашето устройство няма създадена парола. За да продължите, конфигурирайте парола в 'Настройки'на устройството.",
|
||||
"biom_remove_decrypt": "Всички портфейли ще бъдат изтрити и хранилището ще бъде декриптирано. Сигурни ли сте, че искате да продължите?",
|
||||
"currency": "Валута",
|
||||
"currency_source": "Котировките са предотставени от",
|
||||
"default_info": "Информация",
|
||||
"default_title": "При Стартиране",
|
||||
"default_wallets": "Виж всички портфейли",
|
||||
|
@ -211,7 +199,6 @@
|
|||
"electrum_clear": "Изчисти",
|
||||
"encrypt_decrypt": "Декриптирай хранилището",
|
||||
"encrypt_decrypt_q": "Сигурни ли сте, че искате да декриптирате хранилището? Това ще направи портфейлите ви достъпни без парола.",
|
||||
"encrypt_del_uninstall": "Изтрий в случай, че Блу Уолет е деинсталиран",
|
||||
"encrypt_enc_and_pass": "Криптиран и защитен с парола",
|
||||
"encrypt_title": "Сигурност",
|
||||
"encrypt_tstorage": "Хранилище",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue