Merge branch 'master' into alert

This commit is contained in:
Marcos Rodriguez Velez 2024-02-15 15:43:46 -04:00
commit 9194edd4de
No known key found for this signature in database
GPG key ID: 6030B2F48CCE86D7
38 changed files with 879 additions and 532 deletions

View file

@ -40,14 +40,28 @@ jobs:
- name: Install node_modules
run: npm install --production
- name: Extract Version Name
id: version_name
run: |
VERSION_NAME=$(grep versionName android/app/build.gradle | awk '{print $2}' | tr -d '"')
echo "VERSION_NAME=$VERSION_NAME" >> $GITHUB_ENV
echo "::set-output name=version_name::$VERSION_NAME"
- name: Generate Build Number based on timestamp
run: |
NEW_BUILD_NUMBER=$(date +%s)
echo "NEW_BUILD_NUMBER=$NEW_BUILD_NUMBER" >> $GITHUB_ENV
echo "::set-output name=build_number::$NEW_BUILD_NUMBER"
- name: Build
env:
KEYSTORE_FILE_HEX: ${{ secrets.KEYSTORE_FILE_HEX }}
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
BUILD_NUMBER: ${{ env.NEW_BUILD_NUMBER }}
run: ./scripts/build-release-apk.sh
- uses: actions/upload-artifact@v2
if: success()
with:
name: apk
path: ./android/app/build/outputs/apk/release/app-release.apk
name: BlueWallet-${{ env.VERSION_NAME }}(${{ env.NEW_BUILD_NUMBER }}).apk
path: ./android/app/build/outputs/apk/release/BlueWallet-${{ env.VERSION_NAME }}(${{ env.NEW_BUILD_NUMBER }}).apk

View file

@ -1,140 +0,0 @@
import React, { useContext, useEffect, useState } from 'react';
import { View, Image, TouchableOpacity, StyleSheet, ActivityIndicator, useColorScheme, NativeModules } from 'react-native';
import { Icon } from 'react-native-elements';
import Biometric from './class/biometrics';
import { StackActions, useNavigation, useRoute } from '@react-navigation/native';
import { BlueStorageContext } from './blue_modules/storage-context';
import { isHandset } from './blue_modules/environment';
import triggerHapticFeedback, { HapticFeedbackTypes } from './blue_modules/hapticFeedback';
import SafeArea from './components/SafeArea';
const styles = StyleSheet.create({
root: {
flex: 1,
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
biometricRow: {
justifyContent: 'center',
flexDirection: 'row',
},
icon: {
width: 64,
height: 64,
},
logoImage: {
width: 100,
height: 75,
alignSelf: 'center',
},
});
const { SplashScreen } = NativeModules;
const UnlockWith = () => {
const { setWalletsInitialized, isStorageEncrypted, startAndDecrypt } = useContext(BlueStorageContext);
const { dispatch } = useNavigation();
const { unlockOnComponentMount } = useRoute().params;
const [biometricType, setBiometricType] = useState(false);
const [isStorageEncryptedEnabled, setIsStorageEncryptedEnabled] = useState(false);
const [isAuthenticating, setIsAuthenticating] = useState(false);
const colorScheme = useColorScheme();
useEffect(() => {
SplashScreen?.dismissSplashScreen();
startUnlock();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const successfullyAuthenticated = () => {
setWalletsInitialized(true);
dispatch(StackActions.replace(isHandset ? 'Navigation' : 'DrawerRoot'));
};
const unlockWithBiometrics = async () => {
if (await isStorageEncrypted()) {
unlockWithKey();
} else {
setIsAuthenticating(true);
if (await Biometric.unlockWithBiometrics()) {
setIsAuthenticating(false);
await startAndDecrypt();
return successfullyAuthenticated();
}
setIsAuthenticating(false);
}
};
const unlockWithKey = async () => {
if (isAuthenticating) return;
setIsAuthenticating(true);
if (await startAndDecrypt()) {
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
successfullyAuthenticated();
} else {
setIsAuthenticating(false);
}
};
const renderUnlockOptions = () => {
if (isAuthenticating) {
return <ActivityIndicator />;
} else {
const color = colorScheme === 'dark' ? '#FFFFFF' : '#000000';
if ((biometricType === Biometric.TouchID || biometricType === Biometric.Biometrics) && !isStorageEncryptedEnabled) {
return (
<TouchableOpacity accessibilityRole="button" disabled={isAuthenticating} onPress={unlockWithBiometrics}>
<Icon name="fingerprint" size={64} type="font-awesome5" color={color} />
</TouchableOpacity>
);
} else if (biometricType === Biometric.FaceID && !isStorageEncryptedEnabled) {
return (
<TouchableOpacity accessibilityRole="button" disabled={isAuthenticating} onPress={unlockWithBiometrics}>
<Image
source={colorScheme === 'dark' ? require('./img/faceid-default.png') : require('./img/faceid-dark.png')}
style={styles.icon}
/>
</TouchableOpacity>
);
} else if (isStorageEncryptedEnabled) {
return (
<TouchableOpacity accessibilityRole="button" disabled={isAuthenticating} onPress={unlockWithKey}>
<Icon name="lock" size={64} type="font-awesome5" color={color} />
</TouchableOpacity>
);
}
}
};
const startUnlock = async () => {
if (unlockOnComponentMount) {
const storageIsEncrypted = await isStorageEncrypted();
setIsStorageEncryptedEnabled(storageIsEncrypted);
let bt = false;
if (await Biometric.isBiometricUseCapableAndEnabled()) {
bt = await Biometric.biometricType();
}
setBiometricType(bt);
if (!bt || storageIsEncrypted) {
unlockWithKey();
} else if (typeof bt === 'string') unlockWithBiometrics();
}
};
return (
<SafeArea style={styles.root}>
<View style={styles.container}>
<Image source={require('./img/icon.png')} style={styles.logoImage} resizeMode="contain" />
</View>
<View style={styles.biometricRow}>{renderUnlockOptions()}</View>
</SafeArea>
);
};
export default UnlockWith;

196
UnlockWith.tsx Normal file
View file

@ -0,0 +1,196 @@
import React, { useContext, useEffect, useReducer, useRef } from 'react';
import { View, Image, TouchableOpacity, ActivityIndicator, useColorScheme, NativeModules, StyleSheet } from 'react-native';
import { Icon } from 'react-native-elements';
import Biometric, { BiometricType } from './class/biometrics';
import { NavigationProp, RouteProp, StackActions, useNavigation, useRoute } from '@react-navigation/native';
import { BlueStorageContext } from './blue_modules/storage-context';
import { isHandset } from './blue_modules/environment';
import triggerHapticFeedback, { HapticFeedbackTypes } from './blue_modules/hapticFeedback';
import SafeArea from './components/SafeArea';
type RootStackParamList = {
UnlockWith: { unlockOnComponentMount?: boolean };
};
type State = {
biometricType: BiometricType | undefined;
isStorageEncryptedEnabled: boolean;
isAuthenticating: boolean;
};
const SET_BIOMETRIC_TYPE = 'SET_BIOMETRIC_TYPE';
const SET_IS_STORAGE_ENCRYPTED_ENABLED = 'SET_IS_STORAGE_ENCRYPTED_ENABLED';
const SET_IS_AUTHENTICATING = 'SET_IS_AUTHENTICATING';
type Action =
| { type: typeof SET_BIOMETRIC_TYPE; payload: BiometricType | undefined }
| { type: typeof SET_IS_STORAGE_ENCRYPTED_ENABLED; payload: boolean }
| { type: typeof SET_IS_AUTHENTICATING; payload: boolean };
const initialState: State = {
biometricType: undefined,
isStorageEncryptedEnabled: false,
isAuthenticating: false,
};
function reducer(state: State, action: Action): State {
switch (action.type) {
case SET_BIOMETRIC_TYPE:
return { ...state, biometricType: action.payload };
case SET_IS_STORAGE_ENCRYPTED_ENABLED:
return { ...state, isStorageEncryptedEnabled: action.payload };
case SET_IS_AUTHENTICATING:
return { ...state, isAuthenticating: action.payload };
default:
return state;
}
}
const { SplashScreen } = NativeModules;
const UnlockWith: React.FC = () => {
const [state, dispatch] = useReducer(reducer, initialState);
const isUnlockingWallets = useRef(false);
const { setWalletsInitialized, isStorageEncrypted, startAndDecrypt } = useContext(BlueStorageContext);
const navigation = useNavigation<NavigationProp<RootStackParamList, 'UnlockWith'>>();
const route = useRoute<RouteProp<RootStackParamList, 'UnlockWith'>>();
const { unlockOnComponentMount } = route.params;
const colorScheme = useColorScheme();
useEffect(() => {
SplashScreen?.dismissSplashScreen();
startUnlock();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const successfullyAuthenticated = () => {
setWalletsInitialized(true);
navigation.dispatch(StackActions.replace(isHandset ? 'Navigation' : 'DrawerRoot'));
isUnlockingWallets.current = false;
};
const unlockWithBiometrics = async () => {
if (isUnlockingWallets.current || state.isAuthenticating) return;
isUnlockingWallets.current = true;
dispatch({ type: SET_IS_AUTHENTICATING, payload: true });
if (await Biometric.unlockWithBiometrics()) {
await startAndDecrypt();
successfullyAuthenticated();
}
dispatch({ type: SET_IS_AUTHENTICATING, payload: false });
isUnlockingWallets.current = false;
};
const unlockWithKey = async () => {
if (isUnlockingWallets.current || state.isAuthenticating) return;
isUnlockingWallets.current = true;
dispatch({ type: SET_IS_AUTHENTICATING, payload: true });
if (await startAndDecrypt()) {
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
successfullyAuthenticated();
} else {
dispatch({ type: SET_IS_AUTHENTICATING, payload: false });
isUnlockingWallets.current = false;
}
};
const renderUnlockOptions = () => {
if (state.isAuthenticating) {
return <ActivityIndicator />;
} else {
const color = colorScheme === 'dark' ? '#FFFFFF' : '#000000';
if (
(state.biometricType === BiometricType.TouchID || state.biometricType === BiometricType.Biometrics) &&
!state.isStorageEncryptedEnabled
) {
return (
<TouchableOpacity accessibilityRole="button" disabled={state.isAuthenticating} onPress={unlockWithBiometrics}>
<Icon name="fingerprint" size={64} type="font-awesome5" color={color} />
</TouchableOpacity>
);
} else if (state.biometricType === BiometricType.FaceID && !state.isStorageEncryptedEnabled) {
return (
<TouchableOpacity accessibilityRole="button" disabled={state.isAuthenticating} onPress={unlockWithBiometrics}>
<Image
source={colorScheme === 'dark' ? require('./img/faceid-default.png') : require('./img/faceid-dark.png')}
style={styles.icon}
/>
</TouchableOpacity>
);
} else if (state.isStorageEncryptedEnabled) {
return (
<TouchableOpacity accessibilityRole="button" disabled={state.isAuthenticating} onPress={unlockWithKey}>
<Icon name="lock" size={64} type="font-awesome5" color={color} />
</TouchableOpacity>
);
}
}
};
const startUnlock = async () => {
if (unlockOnComponentMount) {
const storageIsEncrypted = await isStorageEncrypted();
dispatch({ type: SET_IS_STORAGE_ENCRYPTED_ENABLED, payload: storageIsEncrypted });
const rawType = await Biometric.biometricType();
let type;
if (rawType === 'Biometrics') {
type = BiometricType.Biometrics;
} else if (rawType === 'Touch ID') {
type = BiometricType.TouchID;
} else if (rawType === 'Face ID') {
type = BiometricType.FaceID;
}
dispatch({ type: SET_BIOMETRIC_TYPE, payload: type });
if (!type || storageIsEncrypted) {
unlockWithKey();
} else {
unlockWithBiometrics();
}
}
};
return (
<SafeArea style={styles.root}>
<View style={styles.container}>
<Image source={require('./img/icon.png')} style={styles.logoImage} resizeMode="contain" />
</View>
<View style={styles.biometricRow}>{renderUnlockOptions()}</View>
</SafeArea>
);
};
const styles = StyleSheet.create({
root: {
flex: 1,
justifyContent: 'space-between',
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
biometricRow: {
justifyContent: 'center',
flexDirection: 'row',
width: 64,
height: 64,
alignSelf: 'center',
marginBottom: 20,
},
icon: {
width: 64,
height: 64,
},
logoImage: {
width: 100,
height: 75,
alignSelf: 'center',
},
});
export default UnlockWith;

View file

@ -79,7 +79,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "6.5.2"
versionName "6.5.3"
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}

View file

@ -24,7 +24,7 @@ buildscript {
dependencies {
classpath("com.android.tools.build:gradle")
classpath("com.bugsnag:bugsnag-android-gradle-plugin:5.+")
classpath 'com.google.gms:google-services:4.4.0' // Google Services plugin
classpath 'com.google.gms:google-services:4.4.1' // Google Services plugin
classpath("com.facebook.react:react-native-gradle-plugin")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")

View file

@ -6,6 +6,7 @@ import { FiatUnit, FiatUnitType, getFiatRate } from '../models/fiatUnit';
import WidgetCommunication from './WidgetCommunication';
const PREFERRED_CURRENCY_STORAGE_KEY = 'preferredCurrency';
const PREFERRED_CURRENCY_LOCALE_STORAGE_KEY = 'preferredCurrencyLocale';
const EXCHANGE_RATES_STORAGE_KEY = 'exchangeRates';
const LAST_UPDATED = 'LAST_UPDATED';
const GROUP_IO_BLUEWALLET = 'group.io.bluewallet.bluewallet';
@ -27,10 +28,9 @@ let lastTimeUpdateExchangeRateWasCalled: number = 0;
let skipUpdateExchangeRate: boolean = false;
async function setPreferredCurrency(item: FiatUnitType): Promise<void> {
await AsyncStorage.setItem(PREFERRED_CURRENCY_STORAGE_KEY, JSON.stringify(item));
await DefaultPreference.setName(GROUP_IO_BLUEWALLET);
await DefaultPreference.set(PREFERRED_CURRENCY_STORAGE_KEY, item.endPointKey);
await DefaultPreference.set('preferredCurrencyLocale', item.locale.replace('-', '_'));
await DefaultPreference.set(PREFERRED_CURRENCY_LOCALE_STORAGE_KEY, item.locale.replace('-', '_'));
// @ts-ignore: Convert to TSX later
WidgetCommunication.reloadAllTimelines();
}
@ -39,7 +39,7 @@ async function getPreferredCurrency(): Promise<FiatUnitType> {
const preferredCurrency = JSON.parse((await AsyncStorage.getItem(PREFERRED_CURRENCY_STORAGE_KEY)) || '{}');
await DefaultPreference.setName(GROUP_IO_BLUEWALLET);
await DefaultPreference.set(PREFERRED_CURRENCY_STORAGE_KEY, preferredCurrency.endPointKey);
await DefaultPreference.set('preferredCurrencyLocale', preferredCurrency.locale.replace('-', '_'));
await DefaultPreference.set(PREFERRED_CURRENCY_LOCALE_STORAGE_KEY, preferredCurrency.locale.replace('-', '_'));
return preferredCurrency;
}

View file

@ -28,6 +28,7 @@ export const BlueStorageProvider = ({ children }) => {
const [isElectrumDisabled, setIsElectrumDisabled] = useState(true);
const [isPrivacyBlurEnabled, setIsPrivacyBlurEnabled] = useState(true);
const [currentSharedCosigner, setCurrentSharedCosigner] = useState('');
const [reloadTransactionsMenuActionFunction, setReloadTransactionsMenuActionFunction] = useState(() => {});
useEffect(() => {
BlueElectrum.isDisabled().then(setIsElectrumDisabled);
@ -277,6 +278,8 @@ export const BlueStorageProvider = ({ children }) => {
setIsElectrumDisabled,
isPrivacyBlurEnabled,
setIsPrivacyBlurEnabled,
reloadTransactionsMenuActionFunction,
setReloadTransactionsMenuActionFunction,
}}
>
{children}

View file

@ -1,37 +1,41 @@
import FingerprintScanner from 'react-native-fingerprint-scanner';
import { Platform, Alert } from 'react-native';
// @ts-ignore react-native-passcode-auth wants d.ts
import { useContext } from 'react';
import { Alert, Platform } from 'react-native';
import { CommonActions, StackActions } from '@react-navigation/native';
import FingerprintScanner, { Biometrics as TBiometrics } from 'react-native-fingerprint-scanner';
import PasscodeAuth from 'react-native-passcode-auth';
import * as NavigationService from '../NavigationService';
import { StackActions, CommonActions } from '@react-navigation/native';
import RNSecureKeyStore from 'react-native-secure-key-store';
import loc from '../loc';
import { useContext } from 'react';
import * as NavigationService from '../NavigationService';
import { BlueStorageContext } from '../blue_modules/storage-context';
import presentAlert from '../components/Alert';
const STORAGEKEY = 'Biometrics';
export enum BiometricType {
FaceID = 'FaceID',
TouchID = 'TouchID',
Biometrics = 'Biometrics',
None = 'None',
}
// Define a function type with properties
type DescribableFunction = {
(): void; // Call signature
STORAGEKEY: string; // Property
FaceID: string; // Property etc...
TouchID: string;
Biometrics: string;
isBiometricUseCapableAndEnabled: () => Promise<undefined | boolean>;
isDeviceBiometricCapable: () => Promise<boolean | undefined>;
(): null; // Call signature
FaceID: 'Face ID';
TouchID: 'Touch ID';
Biometrics: 'Biometrics';
isBiometricUseCapableAndEnabled: () => Promise<boolean>;
isDeviceBiometricCapable: () => Promise<boolean>;
setBiometricUseEnabled: (arg: boolean) => Promise<void>;
biometricType: () => Promise<boolean | 'Touch ID' | 'Face ID' | 'Biometrics'>;
biometricType: () => Promise<false | TBiometrics>;
isBiometricUseEnabled: () => Promise<boolean>;
unlockWithBiometrics: () => Promise<unknown>;
clearKeychain: () => Promise<void>;
requestDevicePasscode: () => Promise<void>;
unlockWithBiometrics: () => Promise<boolean>;
showKeychainWipeAlert: () => void;
};
// @ts-ignore Bastard component/module. All properties are added in runtime, not at definition phase
const Biometric: DescribableFunction = function () {
// Bastard component/module. All properties are added in runtime
const Biometric = function () {
const { getItem, setItem } = useContext(BlueStorageContext);
Biometric.STORAGEKEY = 'Biometrics';
Biometric.FaceID = 'Face ID';
Biometric.TouchID = 'Touch ID';
Biometric.Biometrics = 'Biometrics';
@ -46,8 +50,8 @@ const Biometric: DescribableFunction = function () {
console.log('Biometrics isDeviceBiometricCapable failed');
console.log(e);
Biometric.setBiometricUseEnabled(false);
return false;
}
return false;
};
Biometric.biometricType = async () => {
@ -63,7 +67,7 @@ const Biometric: DescribableFunction = function () {
Biometric.isBiometricUseEnabled = async () => {
try {
const enabledBiometrics = await getItem(Biometric.STORAGEKEY);
const enabledBiometrics = await getItem(STORAGEKEY);
return !!enabledBiometrics;
} catch (_) {}
@ -77,7 +81,7 @@ const Biometric: DescribableFunction = function () {
};
Biometric.setBiometricUseEnabled = async value => {
await setItem(Biometric.STORAGEKEY, value === true ? '1' : '');
await setItem(STORAGEKEY, value === true ? '1' : '');
};
Biometric.unlockWithBiometrics = async () => {
@ -97,14 +101,14 @@ const Biometric: DescribableFunction = function () {
return false;
};
Biometric.clearKeychain = async () => {
const clearKeychain = async () => {
await RNSecureKeyStore.remove('data');
await RNSecureKeyStore.remove('data_encrypted');
await RNSecureKeyStore.remove(Biometric.STORAGEKEY);
await RNSecureKeyStore.remove(STORAGEKEY);
NavigationService.dispatch(StackActions.replace('WalletsRoot'));
};
Biometric.requestDevicePasscode = async () => {
const requestDevicePasscode = async () => {
let isDevicePasscodeSupported: boolean | undefined = false;
try {
isDevicePasscodeSupported = await PasscodeAuth.isSupported();
@ -118,7 +122,7 @@ const Biometric: DescribableFunction = function () {
{ text: loc._.cancel, style: 'cancel' },
{
text: loc._.ok,
onPress: () => Biometric.clearKeychain(),
onPress: () => clearKeychain(),
},
],
{ cancelable: false },
@ -153,7 +157,7 @@ const Biometric: DescribableFunction = function () {
},
{
text: loc._.ok,
onPress: () => Biometric.requestDevicePasscode(),
onPress: () => requestDevicePasscode(),
style: 'default',
},
],
@ -161,7 +165,8 @@ const Biometric: DescribableFunction = function () {
);
}
};
return null;
};
} as DescribableFunction;
export default Biometric;

View file

@ -20,43 +20,57 @@ import {
SegwitP2SHWallet,
WatchOnlyWallet,
} from '.';
import type { TWallet } from './wallets/types';
import loc from '../loc';
import bip39WalletFormats from './bip39_wallet_formats.json'; // https://github.com/spesmilo/electrum/blob/master/electrum/bip39_wallet_formats.json
import bip39WalletFormatsBlueWallet from './bip39_wallet_formats_bluewallet.json';
// https://github.com/bitcoinjs/bip32/blob/master/ts-src/bip32.ts#L43
export const validateBip32 = path => path.match(/^(m\/)?(\d+'?\/)*\d+'?$/) !== null;
export const validateBip32 = (path: string) => path.match(/^(m\/)?(\d+'?\/)*\d+'?$/) !== null;
type TReturn = {
cancelled: boolean;
stopped: boolean;
wallets: TWallet[];
};
/**
* Function that starts wallet search and import process. It has async generator inside, so
* that the process can be stoped at any time. It reporst all the progress through callbacks.
*
* @param askPassphrase {bool} If true import process will call onPassword callback for wallet with optional password.
* @param searchAccounts {bool} If true import process will scan for all known derivation path from bip39_wallet_formats.json. If false it will use limited version.
* @param askPassphrase {boolean} If true import process will call onPassword callback for wallet with optional password.
* @param searchAccounts {boolean} If true import process will scan for all known derivation path from bip39_wallet_formats.json. If false it will use limited version.
* @param onProgress {function} Callback to report scanning progress
* @param onWallet {function} Callback to report wallet found
* @param onPassword {function} Callback to ask for password if needed
* @returns {{promise: Promise, stop: function}}
*/
const startImport = (importTextOrig, askPassphrase = false, searchAccounts = false, onProgress, onWallet, onPassword) => {
const startImport = (
importTextOrig: string,
askPassphrase: boolean = false,
searchAccounts: boolean = false,
onProgress: (name: string) => void,
onWallet: (wallet: TWallet) => void,
onPassword: (title: string, text: string) => Promise<string>,
): { promise: Promise<TReturn>; stop: () => void } => {
// state
let promiseResolve;
let promiseReject;
let promiseResolve: (arg: TReturn) => void;
let promiseReject: (reason?: any) => void;
let running = true; // if you put it to false, internal generator stops
const wallets = [];
const promise = new Promise((resolve, reject) => {
const wallets: TWallet[] = [];
const promise = new Promise<TReturn>((resolve, reject) => {
promiseResolve = resolve;
promiseReject = reject;
});
// actions
const reportProgress = name => {
const reportProgress = (name: string) => {
onProgress(name);
};
const reportFinish = (cancelled, stopped) => {
const reportFinish = (cancelled: boolean = false, stopped: boolean = false) => {
promiseResolve({ cancelled, stopped, wallets });
};
const reportWallet = wallet => {
const reportWallet = (wallet: TWallet) => {
if (wallets.some(w => w.getID() === wallet.getID())) return; // do not add duplicates
wallets.push(wallet);
onWallet(wallet);
@ -134,7 +148,7 @@ const startImport = (importTextOrig, askPassphrase = false, searchAccounts = fal
}
// is it bip38 encrypted
if (text.startsWith('6P')) {
if (text.startsWith('6P') && password) {
const decryptedKey = await bip38.decryptAsync(text, password);
if (decryptedKey) {
@ -184,7 +198,9 @@ const startImport = (importTextOrig, askPassphrase = false, searchAccounts = fal
yield { progress: 'bip39' };
const hd2 = new HDSegwitBech32Wallet();
hd2.setSecret(text);
hd2.setPassphrase(password);
if (password) {
hd2.setPassphrase(password);
}
if (hd2.validateMnemonic()) {
let walletFound = false;
// by default we don't try all the paths and options
@ -214,7 +230,9 @@ const startImport = (importTextOrig, askPassphrase = false, searchAccounts = fal
for (const path of paths) {
const wallet = new WalletClass();
wallet.setSecret(text);
wallet.setPassphrase(password);
if (password) {
wallet.setPassphrase(password);
}
wallet.setDerivationPath(path);
yield { progress: `bip39 ${i.script_type} ${path}` };
if (await wallet.wasEverUsed()) {
@ -230,7 +248,9 @@ const startImport = (importTextOrig, askPassphrase = false, searchAccounts = fal
// to decide which one is it let's compare number of transactions
const m0Legacy = new HDLegacyP2PKHWallet();
m0Legacy.setSecret(text);
m0Legacy.setPassphrase(password);
if (password) {
m0Legacy.setPassphrase(password);
}
m0Legacy.setDerivationPath("m/0'");
yield { progress: "bip39 p2pkh m/0'" };
// BRD doesn't support passphrase and only works with 12 words seeds
@ -332,7 +352,9 @@ const startImport = (importTextOrig, askPassphrase = false, searchAccounts = fal
yield { progress: 'electrum p2wpkh-p2sh' };
const el1 = new HDSegwitElectrumSeedP2WPKHWallet();
el1.setSecret(text);
el1.setPassphrase(password);
if (password) {
el1.setPassphrase(password);
}
if (el1.validateMnemonic()) {
yield { wallet: el1 }; // not fetching txs or balances, fuck it, yolo, life is too short
}
@ -341,7 +363,9 @@ const startImport = (importTextOrig, askPassphrase = false, searchAccounts = fal
yield { progress: 'electrum p2pkh' };
const el2 = new HDLegacyElectrumSeedP2PKHWallet();
el2.setSecret(text);
el2.setPassphrase(password);
if (password) {
el2.setPassphrase(password);
}
if (el2.validateMnemonic()) {
yield { wallet: el2 }; // not fetching txs or balances, fuck it, yolo, life is too short
}
@ -350,7 +374,9 @@ const startImport = (importTextOrig, askPassphrase = false, searchAccounts = fal
yield { progress: 'aezeed' };
const aezeed2 = new HDAezeedWallet();
aezeed2.setSecret(text);
aezeed2.setPassphrase(password);
if (password) {
aezeed2.setPassphrase(password);
}
if (await aezeed2.validateMnemonicAsync()) {
yield { wallet: aezeed2 }; // not fetching txs or balances, fuck it, yolo, life is too short
}
@ -364,14 +390,18 @@ const startImport = (importTextOrig, askPassphrase = false, searchAccounts = fal
if (s1.validateMnemonic()) {
yield { progress: 'SLIP39 p2wpkh-p2sh' };
s1.setPassphrase(password);
if (password) {
s1.setPassphrase(password);
}
if (await s1.wasEverUsed()) {
yield { wallet: s1 };
}
yield { progress: 'SLIP39 p2pkh' };
const s2 = new SLIP39LegacyP2PKHWallet();
s2.setPassphrase(password);
if (password) {
s2.setPassphrase(password);
}
s2.setSecret(text);
if (await s2.wasEverUsed()) {
yield { wallet: s2 };
@ -380,7 +410,9 @@ const startImport = (importTextOrig, askPassphrase = false, searchAccounts = fal
yield { progress: 'SLIP39 p2wpkh' };
const s3 = new SLIP39SegwitBech32Wallet();
s3.setSecret(text);
s3.setPassphrase(password);
if (password) {
s3.setPassphrase(password);
}
yield { wallet: s3 };
}
}

View file

@ -1,5 +1,20 @@
import bitcoin from 'bitcoinjs-lib';
import { CoinSelectOutput, CoinSelectReturnInput } from 'coinselect';
import { HDAezeedWallet } from './hd-aezeed-wallet';
import { HDLegacyBreadwalletWallet } from './hd-legacy-breadwallet-wallet';
import { HDLegacyElectrumSeedP2PKHWallet } from './hd-legacy-electrum-seed-p2pkh-wallet';
import { HDLegacyP2PKHWallet } from './hd-legacy-p2pkh-wallet';
import { HDSegwitBech32Wallet } from './hd-segwit-bech32-wallet';
import { HDSegwitElectrumSeedP2WPKHWallet } from './hd-segwit-electrum-seed-p2wpkh-wallet';
import { HDSegwitP2SHWallet } from './hd-segwit-p2sh-wallet';
import { LegacyWallet } from './legacy-wallet';
import { LightningCustodianWallet } from './lightning-custodian-wallet';
import { LightningLdkWallet } from './lightning-ldk-wallet';
import { MultisigHDWallet } from './multisig-hd-wallet';
import { SegwitBech32Wallet } from './segwit-bech32-wallet';
import { SegwitP2SHWallet } from './segwit-p2sh-wallet';
import { SLIP39LegacyP2PKHWallet, SLIP39SegwitBech32Wallet, SLIP39SegwitP2SHWallet } from './slip39-wallets';
import { WatchOnlyWallet } from './watch-only-wallet';
export type Utxo = {
// Returned by BlueElectrum
@ -80,3 +95,22 @@ export type Transaction = {
received?: number;
value?: number;
};
export type TWallet =
| HDAezeedWallet
| HDLegacyBreadwalletWallet
| HDLegacyElectrumSeedP2PKHWallet
| HDLegacyP2PKHWallet
| HDSegwitBech32Wallet
| HDSegwitElectrumSeedP2WPKHWallet
| HDSegwitP2SHWallet
| LegacyWallet
| LightningCustodianWallet
| LightningLdkWallet
| MultisigHDWallet
| SLIP39LegacyP2PKHWallet
| SLIP39SegwitBech32Wallet
| SLIP39SegwitP2SHWallet
| SegwitBech32Wallet
| SegwitP2SHWallet
| WatchOnlyWallet;

View file

@ -1,5 +1,4 @@
import React from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, Platform, useWindowDimensions, View } from 'react-native';
import Modal from 'react-native-modal';
import { BlueSpacing10 } from '../BlueComponents';
@ -18,19 +17,32 @@ const styles = StyleSheet.create({
},
});
const BottomModal = ({
onBackButtonPress = undefined,
onBackdropPress = undefined,
interface BottomModalProps {
children?: React.ReactNode;
onBackButtonPress?: () => void;
onBackdropPress?: () => void;
onClose: () => void;
windowHeight?: number;
windowWidth?: number;
doneButton?: boolean;
avoidKeyboard?: boolean;
allowBackdropPress?: boolean;
isVisible: boolean;
}
const BottomModal: React.FC<BottomModalProps> = ({
onBackButtonPress,
onBackdropPress,
onClose,
windowHeight = undefined,
windowWidth = undefined,
doneButton = undefined,
windowHeight,
windowWidth,
doneButton,
isVisible,
avoidKeyboard = false,
allowBackdropPress = true,
...props
}) => {
const valueWindowHeight = useWindowDimensions().height;
const valueWindowWidth = useWindowDimensions().width;
const { height: valueWindowHeight, width: valueWindowWidth } = useWindowDimensions();
const handleBackButtonPress = onBackButtonPress ?? onClose;
const handleBackdropPress = allowBackdropPress ? onBackdropPress ?? onClose : undefined;
const { colors } = useTheme();
@ -39,6 +51,7 @@ const BottomModal = ({
backgroundColor: colors.elevated,
},
});
return (
<Modal
style={styles.root}
@ -46,6 +59,7 @@ const BottomModal = ({
deviceWidth={windowWidth ?? valueWindowWidth}
onBackButtonPress={handleBackButtonPress}
onBackdropPress={handleBackdropPress}
isVisible={isVisible}
{...props}
accessibilityViewIsModal
avoidKeyboard={avoidKeyboard}
@ -62,16 +76,4 @@ const BottomModal = ({
);
};
BottomModal.propTypes = {
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.element), PropTypes.element]),
onBackButtonPress: PropTypes.func,
onBackdropPress: PropTypes.func,
onClose: PropTypes.func,
doneButton: PropTypes.bool,
windowHeight: PropTypes.number,
windowWidth: PropTypes.number,
avoidKeyboard: PropTypes.bool,
allowBackdropPress: PropTypes.bool,
};
export default BottomModal;

View file

@ -11,7 +11,7 @@ EventEmitter on the native side should receive a payload and rebuild menus.
const eventEmitter = Platform.OS === 'ios' || Platform.OS === 'macos' ? new NativeEventEmitter(NativeModules.EventEmitter) : undefined;
const MenuElements = () => {
const { walletsInitialized } = useContext(BlueStorageContext);
const { walletsInitialized, reloadTransactionsMenuActionFunction } = useContext(BlueStorageContext);
// BlueWallet -> Settings
const openSettings = useCallback(() => {
@ -23,25 +23,50 @@ const MenuElements = () => {
dispatchNavigate('AddWalletRoot');
}, []);
const dispatchNavigate = (routeName: string) => {
NavigationService.dispatch(
CommonActions.navigate({
name: routeName,
}),
);
// File -> Add Wallet
const importWalletMenuAction = useCallback(() => {
dispatchNavigate('AddWalletRoot', 'ImportWallet');
}, []);
const dispatchNavigate = (routeName: string, screen?: string) => {
const action = screen
? CommonActions.navigate({
name: routeName,
params: { screen },
})
: CommonActions.navigate({
name: routeName,
});
NavigationService.dispatch(action);
};
const reloadTransactionsMenuElementsFunction = useCallback(() => {
reloadTransactionsMenuActionFunction();
}, [reloadTransactionsMenuActionFunction]);
useEffect(() => {
console.log('MenuElements: useEffect');
if (walletsInitialized) {
eventEmitter?.addListener('openSettings', openSettings);
eventEmitter?.addListener('addWalletMenuAction', addWalletMenuAction);
eventEmitter?.addListener('importWalletMenuAction', importWalletMenuAction);
eventEmitter?.addListener('reloadTransactionsMenuAction', reloadTransactionsMenuElementsFunction);
}
return () => {
eventEmitter?.removeAllListeners('openSettings');
eventEmitter?.removeAllListeners('addWalletMenuAction');
eventEmitter?.removeAllListeners('importWalletMenuAction');
eventEmitter?.removeAllListeners('reloadTransactionsMenuAction');
};
}, [addWalletMenuAction, openSettings, walletsInitialized]);
}, [
addWalletMenuAction,
importWalletMenuAction,
openSettings,
reloadTransactionsMenuActionFunction,
reloadTransactionsMenuElementsFunction,
walletsInitialized,
]);
return <></>;
};

View file

@ -77,11 +77,9 @@ const TransactionsNavigationHeader: React.FC<TransactionsNavigationHeaderProps>
};
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();
}

View file

@ -5,6 +5,9 @@ import { BlueStorageContext } from '../blue_modules/storage-context';
interface HandoffComponentProps {
url?: string;
title?: string;
type: (typeof HandoffComponent.activityTypes)[keyof typeof HandoffComponent.activityTypes];
userInfo?: object;
}
interface HandoffComponentWithActivityTypes extends React.FC<HandoffComponentProps> {

View file

@ -44,19 +44,23 @@ export type NavigationOptionsGetter = (theme: Theme) => (deps: { navigation: any
const navigationStyle = (
{
closeButton = false,
closeButtonFunc,
headerBackVisible = true,
...opts
}: NavigationOptions & {
closeButton?: boolean;
closeButtonFunc?: (deps: { navigation: any; route: any }) => React.ReactElement;
},
formatter: OptionsFormatter,
): NavigationOptionsGetter => {
return theme =>
({ navigation, route }) => {
// Determine if the current screen is the first one in the stack using the updated method
const isFirstRouteInStack = navigation.getState().index === 0;
// Default closeButton to true if the current screen is the first one in the stack
const closeButton = opts.closeButton !== undefined ? opts.closeButton : isFirstRouteInStack;
let headerRight;
let headerLeft;
if (closeButton) {
@ -79,12 +83,11 @@ const navigationStyle = (
);
}
// Workaround for https://github.com/BlueWallet/BlueWallet/issues/6030
if (!headerBackVisible) {
headerLeft = () => <></>;
// @ts-ignore: Fix later
opts.headerLeft = headerLeft;
}
//
let options: NavigationOptions = {
headerShadowVisible: false,
@ -92,6 +95,7 @@ const navigationStyle = (
fontWeight: '600',
color: theme.colors.foregroundColor,
},
// @ts-ignore: Fix later
headerRight,
headerBackTitleVisible: false,
headerTintColor: theme.colors.foregroundColor,
@ -124,6 +128,7 @@ export const navigationStyleTx = (opts: NavigationOptions, formatter: OptionsFor
// headerBackTitle: null,
headerBackTitleVisible: false,
headerTintColor: theme.colors.foregroundColor,
// @ts-ignore: Fix later
headerLeft: () => (
<TouchableOpacity
accessibilityRole="button"

View file

@ -28,7 +28,6 @@ function scanQrHelper(
params.onBarScanned = function (data: any) {
setTimeout(() => resolve(data.data || data), 1);
navigateFunc(currentScreenName);
navigateFunc({ name: currentScreenName, params: {}, merge: true });
};

View file

@ -13,7 +13,7 @@
import { AbstractWallet } from '../class';
module.exports = function (
navigateFunc: (scr: string, params?: any) => void,
navigateFunc: (scr: string | any, params?: any) => void,
currentScreenName: string,
chainType: string | null,
availableWallets?: AbstractWallet[],
@ -40,7 +40,7 @@ module.exports = function (
setTimeout(() => resolve(selectedWallet), 1);
console.warn('trying to navigate back to', currentScreenName);
navigateFunc(currentScreenName);
navigateFunc({ name: currentScreenName, params: {}, merge: true });
};
navigateFunc('SelectWallet', params);

View file

@ -89,7 +89,7 @@
B4AB225D2B02AD12001F4328 /* XMLParserDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4AB225C2B02AD12001F4328 /* XMLParserDelegate.swift */; };
B4AB225E2B02AD12001F4328 /* XMLParserDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4AB225C2B02AD12001F4328 /* XMLParserDelegate.swift */; };
B4EE583C226703320003363C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B40D4E35225841ED00428FCC /* Assets.xcassets */; };
C978A716948AB7DEC5B6F677 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; };
C978A716948AB7DEC5B6F677 /* (null) in Frameworks */ = {isa = PBXBuildFile; };
E5D4794B26781FC0007838C1 /* fiatUnits.json in Resources */ = {isa = PBXBuildFile; fileRef = 6DD410AD266CAF1F0087DE03 /* fiatUnits.json */; };
E5D4794C26781FC1007838C1 /* fiatUnits.json in Resources */ = {isa = PBXBuildFile; fileRef = 6DD410AD266CAF1F0087DE03 /* fiatUnits.json */; };
/* End PBXBuildFile section */
@ -393,7 +393,7 @@
files = (
782F075B5DD048449E2DECE9 /* libz.tbd in Frameworks */,
764B49B1420D4AEB8109BF62 /* libsqlite3.0.tbd in Frameworks */,
C978A716948AB7DEC5B6F677 /* BuildFile in Frameworks */,
C978A716948AB7DEC5B6F677 /* (null) in Frameworks */,
773E382FE62E836172AAB98B /* libPods-BlueWallet.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -1001,7 +1001,7 @@
);
mainGroup = 83CBB9F61A601CBA00E9B192;
packageReferences = (
6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode.git" */,
6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode" */,
B41B76832B66B2FF002C48D5 /* XCRemoteSwiftPackageReference "bugsnag-cocoa" */,
);
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
@ -1526,7 +1526,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1703112800;
CURRENT_PROJECT_VERSION = 1703112810;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = A7W54YZ4WU;
@ -1551,7 +1551,7 @@
"$(SDKROOT)/System/iOSSupport/usr/lib/swift",
"$(inherited)",
);
MARKETING_VERSION = 6.5.2;
MARKETING_VERSION = 6.5.3;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@ -1584,9 +1584,9 @@
CODE_SIGN_ENTITLEMENTS = BlueWallet/BlueWalletRelease.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1703112800;
CURRENT_PROJECT_VERSION = 1703112810;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = A7W54YZ4WU;
@ -1606,7 +1606,7 @@
"$(SDKROOT)/System/iOSSupport/usr/lib/swift",
"$(inherited)",
);
MARKETING_VERSION = 6.5.2;
MARKETING_VERSION = 6.5.3;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@ -1617,7 +1617,7 @@
PRODUCT_NAME = BlueWallet;
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore io.bluewallet.bluewallet";
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "BlueWallet Mac App Store";
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match AppStore io.bluewallet.bluewallet catalyst";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = YES;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
@ -1643,7 +1643,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1703112800;
CURRENT_PROJECT_VERSION = 1703112810;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = "";
@ -1688,7 +1688,7 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1703112800;
CURRENT_PROJECT_VERSION = 1703112810;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
@ -1733,7 +1733,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1703112800;
CURRENT_PROJECT_VERSION = 1703112810;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = "";
@ -1787,13 +1787,15 @@
CODE_SIGN_ENTITLEMENTS = WidgetsExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1703112800;
CURRENT_PROJECT_VERSION = 1703112810;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = A7W54YZ4WU;
"DEVELOPMENT_TEAM[sdk=macosx*]" = A7W54YZ4WU;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = Widgets/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
@ -1814,6 +1816,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore io.bluewallet.bluewallet.MarketWidget";
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match AppStore io.bluewallet.bluewallet.MarketWidget catalyst";
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = YES;
@ -1959,7 +1962,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1703112800;
CURRENT_PROJECT_VERSION = 1703112810;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = "";
@ -1976,7 +1979,7 @@
"$(SDKROOT)/System/iOSSupport/usr/lib/swift",
"$(inherited)",
);
MARKETING_VERSION = 6.5.2;
MARKETING_VERSION = 6.5.3;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES;
@ -2009,7 +2012,7 @@
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1703112800;
CURRENT_PROJECT_VERSION = 1703112810;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
@ -2026,7 +2029,7 @@
"$(SDKROOT)/System/iOSSupport/usr/lib/swift",
"$(inherited)",
);
MARKETING_VERSION = 6.5.2;
MARKETING_VERSION = 6.5.3;
MTL_FAST_MATH = YES;
PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES;
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.watch.extension;
@ -2058,7 +2061,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1703112800;
CURRENT_PROJECT_VERSION = 1703112810;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = "";
@ -2071,7 +2074,7 @@
"$(SDKROOT)/System/iOSSupport/usr/lib/swift",
"$(inherited)",
);
MARKETING_VERSION = 6.5.2;
MARKETING_VERSION = 6.5.3;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES;
@ -2106,7 +2109,7 @@
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1703112800;
CURRENT_PROJECT_VERSION = 1703112810;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
@ -2119,7 +2122,7 @@
"$(SDKROOT)/System/iOSSupport/usr/lib/swift",
"$(inherited)",
);
MARKETING_VERSION = 6.5.2;
MARKETING_VERSION = 6.5.3;
MTL_FAST_MATH = YES;
PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES;
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet.watch;
@ -2149,7 +2152,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1703112800;
CURRENT_PROJECT_VERSION = 1703112810;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = "";
@ -2199,7 +2202,7 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1703112800;
CURRENT_PROJECT_VERSION = 1703112810;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
@ -2238,7 +2241,7 @@
CODE_SIGN_ENTITLEMENTS = BlueWallet/BlueWallet.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1703112800;
CURRENT_PROJECT_VERSION = 1703112810;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = A7W54YZ4WU;
ENABLE_BITCODE = NO;
@ -2262,7 +2265,7 @@
"$(SDKROOT)/System/iOSSupport/usr/lib/swift",
"$(inherited)",
);
MARKETING_VERSION = 6.5.2;
MARKETING_VERSION = 6.5.3;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@ -2278,7 +2281,7 @@
SWIFT_OBJC_BRIDGING_HEADER = "BlueWallet-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = 2;
TARGETED_DEVICE_FAMILY = "2,6";
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
@ -2291,10 +2294,14 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = BlueWallet/BlueWalletRelease.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1703112800;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1703112810;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = A7W54YZ4WU;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = A7W54YZ4WU;
"DEVELOPMENT_TEAM[sdk=macosx*]" = A7W54YZ4WU;
ENABLE_BITCODE = NO;
"ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES;
HEADER_SEARCH_PATHS = "$(inherited)";
@ -2311,7 +2318,7 @@
"$(SDKROOT)/System/iOSSupport/usr/lib/swift",
"$(inherited)",
);
MARKETING_VERSION = 6.5.2;
MARKETING_VERSION = 6.5.3;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@ -2321,12 +2328,14 @@
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet;
PRODUCT_NAME = BlueWallet;
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore io.bluewallet.bluewallet";
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match AppStore io.bluewallet.bluewallet catalyst";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = YES;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "BlueWallet-Bridging-Header.h";
SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = 2;
TARGETED_DEVICE_FAMILY = "2,6";
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
@ -2409,7 +2418,7 @@
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode.git" */ = {
6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/EFPrefix/EFQRCode.git";
requirement = {
@ -2430,7 +2439,7 @@
/* Begin XCSwiftPackageProductDependency section */
6DFC806F24EA0B6C007B8700 /* EFQRCode */ = {
isa = XCSwiftPackageProductDependency;
package = 6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode.git" */;
package = 6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode" */;
productName = EFQRCode;
};
B41B76842B66B2FF002C48D5 /* Bugsnag */ = {

View file

@ -24,6 +24,11 @@
<key>orderHint</key>
<integer>100</integer>
</dict>
<key>BlueWallet-NoLDK.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>134</integer>
</dict>
<key>BlueWallet.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
@ -32,7 +37,7 @@
<key>BlueWalletUITests.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>99</integer>
<integer>97</integer>
</dict>
<key>BlueWalletWatch (Complication).xcscheme_^#shared#^_</key>
<dict>

View file

@ -127,10 +127,6 @@
completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge);
}
- (void)openSettings:(UIKeyCommand *)keyCommand {
[EventEmitter.sharedInstance openSettings];
}
- (void)buildMenuWithBuilder:(id<UIMenuBuilder>)builder {
[super buildMenuWithBuilder:builder];
[builder removeMenuForIdentifier:UIMenuServices];
@ -138,18 +134,27 @@
[builder removeMenuForIdentifier:UIMenuToolbar];
// File -> Add Wallet (Command + A)
UIKeyCommand *addWalletCommand = [UIKeyCommand keyCommandWithInput:@"A" modifierFlags:UIKeyModifierCommand action:@selector(addWalletAction:)];
[addWalletCommand setTitle:@"Add Wallet"];
UIMenu *addWalletMenu = [UIMenu menuWithTitle:@"" image:nil identifier:nil options:UIMenuOptionsDisplayInline children:@[addWalletCommand]];
UIKeyCommand *addWalletCommand = [UIKeyCommand keyCommandWithInput:@"A" modifierFlags:UIKeyModifierCommand action:@selector(addWalletAction:)];
[addWalletCommand setTitle:@"Add Wallet"];
// Modify the existing File menu
UIMenu *fileMenu = [builder menuForIdentifier:UIMenuFile];
if (fileMenu) {
// Replace the children of the File menu with the Add Wallet menu
UIMenu *newFileMenu = [UIMenu menuWithTitle:fileMenu.title image:nil identifier:fileMenu.identifier options:fileMenu.options children:@[addWalletMenu]];
[builder replaceMenuForIdentifier:UIMenuFile withMenu:newFileMenu];
}
// File -> Import Wallet
UIKeyCommand *importWalletCommand = [UIKeyCommand keyCommandWithInput:@"I" modifierFlags:UIKeyModifierCommand action:@selector(importWalletAction:)];
[importWalletCommand setTitle:@"Import Wallet"];
// Group Add Wallet and Import Wallet in a displayInline menu
UIMenu *walletOperationsMenu = [UIMenu menuWithTitle:@"" image:nil identifier:nil options:UIMenuOptionsDisplayInline children:@[addWalletCommand, importWalletCommand]];
// Modify the existing File menu to include Wallet Operations
UIMenu *fileMenu = [builder menuForIdentifier:UIMenuFile];
if (fileMenu) {
// Add "Reload Transactions"
UIKeyCommand *reloadTransactionsCommand = [UIKeyCommand keyCommandWithInput:@"R" modifierFlags:UIKeyModifierCommand action:@selector(reloadTransactionsAction:)];
[reloadTransactionsCommand setTitle:@"Reload Transactions"];
// Combine wallet operations and Reload Transactions into the new File menu
UIMenu *newFileMenu = [UIMenu menuWithTitle:fileMenu.title image:nil identifier:fileMenu.identifier options:fileMenu.options children:@[walletOperationsMenu, reloadTransactionsCommand]];
[builder replaceMenuForIdentifier:UIMenuFile withMenu:newFileMenu];
}
// BlueWallet -> Settings (Command + ,)
UIKeyCommand *settingsCommand = [UIKeyCommand keyCommandWithInput:@"," modifierFlags:UIKeyModifierCommand action:@selector(openSettings:)];
@ -159,7 +164,11 @@
[builder insertSiblingMenu:settings afterMenuForIdentifier:UIMenuAbout];
}
// Action for Add Wallet
- (void)openSettings:(UIKeyCommand *)keyCommand {
[EventEmitter.sharedInstance openSettings];
}
- (void)addWalletAction:(UIKeyCommand *)keyCommand {
// Implement the functionality for adding a wallet
[EventEmitter.sharedInstance addWalletMenuAction];
@ -167,6 +176,21 @@
NSLog(@"Add Wallet action performed");
}
- (void)importWalletAction:(UIKeyCommand *)keyCommand {
// Implement the functionality for adding a wallet
[EventEmitter.sharedInstance importWalletMenuAction];
NSLog(@"Add Wallet action performed");
}
- (void)reloadTransactionsAction:(UIKeyCommand *)keyCommand {
// Implement the functionality for adding a wallet
[EventEmitter.sharedInstance reloadTransactionsMenuAction];
NSLog(@"Add Wallet action performed");
}
-(void)showHelp:(id)sender {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://bluewallet.io/docs"] options:@{} completionHandler:nil];

View file

@ -15,6 +15,8 @@
- (void)sendNotification:(NSDictionary *)userInfo;
- (void)openSettings;
- (void)addWalletMenuAction;
- (void)importWalletMenuAction;
- (void)reloadTransactionsMenuAction;
- (void)sendUserActivity:(NSDictionary *)userInfo;
@end

View file

@ -32,7 +32,7 @@ RCT_EXPORT_MODULE();
}
- (NSArray<NSString *> *)supportedEvents {
return @[@"onNotificationReceived",@"openSettings",@"onUserActivityOpen",@"addWalletMenuAction"];
return @[@"onNotificationReceived",@"openSettings",@"onUserActivityOpen",@"addWalletMenuAction", @"importWalletMenuAction", @"reloadTransactionsMenuAction"];
}
- (void)sendNotification:(NSDictionary *)userInfo
@ -62,4 +62,12 @@ RCT_REMAP_METHOD(getMostRecentUserActivity, resolve: (RCTPromiseResolveBlock)res
[sharedInstance sendEventWithName:@"addWalletMenuAction" body:nil];
}
- (void)importWalletMenuAction {
[sharedInstance sendEventWithName:@"importWalletMenuAction" body:nil];
}
- (void)reloadTransactionsMenuAction {
[sharedInstance sendEventWithName:@"reloadTransactionsMenuAction" body:nil];
}
@end

View file

@ -352,7 +352,7 @@ PODS:
- react-native-tcp-socket (6.0.6):
- CocoaAsyncSocket
- React-Core
- react-native-webview (13.7.1):
- react-native-webview (13.8.1):
- RCT-Folly (= 2021.07.22.00)
- React-Core
- react-native-widget-center (0.0.9):
@ -504,7 +504,7 @@ PODS:
- React-Core
- RNReactNativeHapticFeedback (2.2.0):
- React-Core
- RNReanimated (3.6.2):
- RNReanimated (3.7.0):
- RCT-Folly (= 2021.07.22.00)
- React-Core
- ReactCommon/turbomodule/core
@ -823,7 +823,7 @@ SPEC CHECKSUMS:
react-native-safe-area-context: b97eb6f9e3b7f437806c2ce5983f479f8eb5de4b
react-native-secure-key-store: 910e6df6bc33cb790aba6ee24bc7818df1fe5898
react-native-tcp-socket: e724380c910c2e704816ec817ed28f1342246ff7
react-native-webview: 83525c9ed4138faf5d5e4f8eb74bf050992935be
react-native-webview: bdc091de8cf7f8397653e30182efcd9f772e03b3
react-native-widget-center: 12dfba20a4fa995850b52cf0afecf734397f4b9c
React-NativeModulesApple: c3e696ff867e4bc212266cbdf7e862e48a0166fd
React-perflogger: 43287389ea08993c300897a46f95cfac04bb6c1a
@ -860,7 +860,7 @@ SPEC CHECKSUMS:
RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93
RNRate: ef3bcff84f39bb1d1e41c5593d3eea4aab2bd73a
RNReactNativeHapticFeedback: ec56a5f81c3941206fd85625fa669ffc7b4545f9
RNReanimated: ce6bef77caeee09d7f022532cee0c8d9bad09c9a
RNReanimated: 9dfe7a19dd7c3e123b23db42e74bf051725f2ea3
RNScreens: 3c5b9f4a9dcde752466854b6109b79c0e205dad3
RNShare: 859ff710211285676b0bcedd156c12437ea1d564
RNSVG: d00c8f91c3cbf6d476451313a18f04d220d4f396

View file

@ -281,6 +281,7 @@
"language": "Idioma",
"last_updated": "Última actualización",
"language_isRTL": "Es necesario reiniciar BlueWallet para que la orientación del idioma surta efecto.",
"license": "Licencia",
"lightning_error_lndhub_uri": "URI de LNDHub inválido",
"lightning_saved": "Tus cambios han sido guardados correctamente.",
"lightning_settings": "Configuración de Lightning",

146
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "bluewallet",
"version": "6.5.2",
"version": "6.5.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "bluewallet",
"version": "6.5.2",
"version": "6.5.3",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@ -19,9 +19,9 @@
"@react-native-async-storage/async-storage": "1.21.0",
"@react-native-clipboard/clipboard": "1.13.2",
"@react-native-community/push-notification-ios": "1.11.0",
"@react-navigation/drawer": "6.6.6",
"@react-navigation/native": "6.1.9",
"@react-navigation/native-stack": "6.9.17",
"@react-navigation/drawer": "6.6.7",
"@react-navigation/native": "6.1.10",
"@react-navigation/native-stack": "6.9.18",
"@remobile/react-native-qrcode-local-image": "https://github.com/BlueWallet/react-native-qrcode-local-image",
"@spsina/bip47": "github:BlueWallet/bip47#0a2f02c90350802f2ec93afa4e6c8843be2d687c",
"aezeed": "0.0.5",
@ -42,7 +42,7 @@
"coinselect": "3.1.13",
"crypto-js": "4.2.0",
"dayjs": "1.11.10",
"detox": "20.17.0",
"detox": "20.17.1",
"ecpair": "2.0.1",
"ecurve": "1.0.6",
"electrum-client": "github:BlueWallet/rn-electrum-client#1bfe3cc",
@ -89,7 +89,7 @@
"react-native-quick-actions": "0.3.13",
"react-native-randombytes": "3.6.1",
"react-native-rate": "1.2.12",
"react-native-reanimated": "3.6.2",
"react-native-reanimated": "3.7.0",
"react-native-safe-area-context": "4.9.0",
"react-native-screens": "3.29.0",
"react-native-secure-key-store": "https://github.com/BlueWallet/react-native-secure-key-store#2076b48",
@ -98,7 +98,7 @@
"react-native-tcp-socket": "6.0.6",
"react-native-vector-icons": "10.0.3",
"react-native-watch-connectivity": "1.1.0",
"react-native-webview": "13.7.1",
"react-native-webview": "13.8.1",
"react-native-widget-center": "https://github.com/BlueWallet/react-native-widget-center#a128c38",
"readable-stream": "3.6.2",
"realm": "12.6.0",
@ -117,12 +117,14 @@
"@react-native/eslint-config": "^0.72.2",
"@react-native/metro-config": "^0.73.0",
"@tsconfig/react-native": "^3.0.2",
"@types/bip38": "^3.1.2",
"@types/bs58check": "^2.1.0",
"@types/create-hash": "^1.2.2",
"@types/jest": "^29.4.0",
"@types/react": "^18.2.16",
"@types/react-native": "^0.72.0",
"@types/react-test-renderer": "^18.0.0",
"@types/wif": "^2.0.5",
"@typescript-eslint/eslint-plugin": "^6.2.0",
"@typescript-eslint/parser": "^6.2.0",
"eslint": "^8.45.0",
@ -6379,11 +6381,11 @@
}
},
"node_modules/@react-navigation/drawer": {
"version": "6.6.6",
"resolved": "https://registry.npmjs.org/@react-navigation/drawer/-/drawer-6.6.6.tgz",
"integrity": "sha512-DW/oNRisSOGOqvZfCzfhKBxnzT97Teqtg1Gal85g+K3gnVbM1jOBE2PdnYsKU0fULfFtDwvp/QZSbcgjDpr12A==",
"version": "6.6.7",
"resolved": "https://registry.npmjs.org/@react-navigation/drawer/-/drawer-6.6.7.tgz",
"integrity": "sha512-9hSJySPQcG33vV199uzkI3jHewvAGjVFF2bhC28TkQYDKtp/DmtTvjCEiecOm+qep1Qp4ksh5vBo2P0A1hF6vQ==",
"dependencies": {
"@react-navigation/elements": "^1.3.21",
"@react-navigation/elements": "^1.3.22",
"color": "^4.2.3",
"warn-once": "^0.1.0"
},
@ -6398,9 +6400,9 @@
}
},
"node_modules/@react-navigation/elements": {
"version": "1.3.21",
"resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.21.tgz",
"integrity": "sha512-eyS2C6McNR8ihUoYfc166O1D8VYVh9KIl0UQPI8/ZJVsStlfSTgeEEh+WXge6+7SFPnZ4ewzEJdSAHH+jzcEfg==",
"version": "1.3.22",
"resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.22.tgz",
"integrity": "sha512-HYKucs0TwQT8zMvgoZbJsY/3sZfzeP8Dk9IDv4agst3zlA7ReTx4+SROCG6VGC7JKqBCyQykHIwkSwxhapoc+Q==",
"peerDependencies": {
"@react-navigation/native": "^6.0.0",
"react": "*",
@ -6409,9 +6411,9 @@
}
},
"node_modules/@react-navigation/native": {
"version": "6.1.9",
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.9.tgz",
"integrity": "sha512-AMuJDpwXE7UlfyhIXaUCCynXmv69Kb8NzKgKJO7v0k0L+u6xUTbt6xvshmJ79vsvaFyaEH9Jg5FMzek5/S5qNw==",
"version": "6.1.10",
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.10.tgz",
"integrity": "sha512-jDG89TbZItY7W7rIcS1RqT63vWOPD4XuQLNKqZO0DY7mKnKh/CGBd0eg3nDMXUl143Qp//IxJKe2TfBQRDEU4A==",
"dependencies": {
"@react-navigation/core": "^6.4.10",
"escape-string-regexp": "^4.0.0",
@ -6424,11 +6426,11 @@
}
},
"node_modules/@react-navigation/native-stack": {
"version": "6.9.17",
"resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-6.9.17.tgz",
"integrity": "sha512-X8p8aS7JptQq7uZZNFEvfEcPf6tlK4PyVwYDdryRbG98B4bh2wFQYMThxvqa+FGEN7USEuHdv2mF0GhFKfX0ew==",
"version": "6.9.18",
"resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-6.9.18.tgz",
"integrity": "sha512-PSe0qjROy8zD78ehW048NSuzWRktioSCJmB8LzWSR65ndgVaC2rO+xvgyjhHjqm01YdyVM1XTct2EorSjDV2Ow==",
"dependencies": {
"@react-navigation/elements": "^1.3.21",
"@react-navigation/elements": "^1.3.22",
"warn-once": "^0.1.0"
},
"peerDependencies": {
@ -6556,6 +6558,15 @@
"@babel/types": "^7.20.7"
}
},
"node_modules/@types/bip38": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@types/bip38/-/bip38-3.1.2.tgz",
"integrity": "sha512-KF5aiS7DUJs2llJJeg1O1Io129PETszfUfDQotJ4VPBXzytpIUmb7n2MHWEdFYRHs2LYoaRivP/aJbTlF56J+Q==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/bn.js": {
"version": "4.11.6",
"resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz",
@ -6719,6 +6730,15 @@
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="
},
"node_modules/@types/wif": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@types/wif/-/wif-2.0.5.tgz",
"integrity": "sha512-addYBlYjDxLfJxDUoyTzICnu0u4snCdGJpICIIFk65zGcdjah3twTJq1Fdy+OdeZSRiof2raFtMqSqF9KeqthQ==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/yargs": {
"version": "16.0.9",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz",
@ -9626,9 +9646,9 @@
}
},
"node_modules/detox": {
"version": "20.17.0",
"resolved": "https://registry.npmjs.org/detox/-/detox-20.17.0.tgz",
"integrity": "sha512-IasYgexfkrCoZuJTaqqKHQ2yflK+tnqifzdLwrp4hdTWlXUlG9j/YcM1Dn3ThSs3b6VNMtbSe6xoPkKD0oNiIQ==",
"version": "20.17.1",
"resolved": "https://registry.npmjs.org/detox/-/detox-20.17.1.tgz",
"integrity": "sha512-10pey6CR9D5GSloRkH60ObBGZ8VS11H7iuBNY7qq6jO2swiqqckHhPLRXfH9+WGR7l3vDnfU+G/gQs7JxQkJwA==",
"hasInstallScript": true,
"dependencies": {
"ajv": "^8.6.3",
@ -19875,9 +19895,9 @@
}
},
"node_modules/react-native-reanimated": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.6.2.tgz",
"integrity": "sha512-IIMREMOrxhtK35drfpzh2UhxNqAOHnuvGgtMofj7yHcMj16tmWZR2zFvMUf6z2MfmXv+aVgFQ6TRZ6yKYf7LNA==",
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.7.0.tgz",
"integrity": "sha512-KM+MKa3CJWqsF4GlOLLKBxTR2NEcrg5/HP9J2b6Dfgvll1sjZPywCOEEIh967SboEU8N9LjYZuoVm2UoXGxp2Q==",
"dependencies": {
"@babel/plugin-transform-object-assign": "^7.16.7",
"@babel/preset-typescript": "^7.16.7",
@ -20053,9 +20073,9 @@
}
},
"node_modules/react-native-webview": {
"version": "13.7.1",
"resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.7.1.tgz",
"integrity": "sha512-tnfTgjIYY3KnBfbN9hRCJf2y/6JZBev3aq2ZacLml0AZn4ufTe4MyCiE1NAZ/l5WHV/1eaqN0srxn45ATL0+Ow==",
"version": "13.8.1",
"resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.8.1.tgz",
"integrity": "sha512-7Jqm1WzWJrOWraBAXQfKtr/Uo5Jw/IJHzC40jYLwgV/eVGmLJ9BpGKw6QVw7wpRkjmTZ2Typ4B1aHJLJJQFslA==",
"dependencies": {
"escape-string-regexp": "2.0.0",
"invariant": "2.2.4"
@ -27344,24 +27364,24 @@
}
},
"@react-navigation/drawer": {
"version": "6.6.6",
"resolved": "https://registry.npmjs.org/@react-navigation/drawer/-/drawer-6.6.6.tgz",
"integrity": "sha512-DW/oNRisSOGOqvZfCzfhKBxnzT97Teqtg1Gal85g+K3gnVbM1jOBE2PdnYsKU0fULfFtDwvp/QZSbcgjDpr12A==",
"version": "6.6.7",
"resolved": "https://registry.npmjs.org/@react-navigation/drawer/-/drawer-6.6.7.tgz",
"integrity": "sha512-9hSJySPQcG33vV199uzkI3jHewvAGjVFF2bhC28TkQYDKtp/DmtTvjCEiecOm+qep1Qp4ksh5vBo2P0A1hF6vQ==",
"requires": {
"@react-navigation/elements": "^1.3.21",
"@react-navigation/elements": "^1.3.22",
"color": "^4.2.3",
"warn-once": "^0.1.0"
}
},
"@react-navigation/elements": {
"version": "1.3.21",
"resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.21.tgz",
"integrity": "sha512-eyS2C6McNR8ihUoYfc166O1D8VYVh9KIl0UQPI8/ZJVsStlfSTgeEEh+WXge6+7SFPnZ4ewzEJdSAHH+jzcEfg=="
"version": "1.3.22",
"resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.22.tgz",
"integrity": "sha512-HYKucs0TwQT8zMvgoZbJsY/3sZfzeP8Dk9IDv4agst3zlA7ReTx4+SROCG6VGC7JKqBCyQykHIwkSwxhapoc+Q=="
},
"@react-navigation/native": {
"version": "6.1.9",
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.9.tgz",
"integrity": "sha512-AMuJDpwXE7UlfyhIXaUCCynXmv69Kb8NzKgKJO7v0k0L+u6xUTbt6xvshmJ79vsvaFyaEH9Jg5FMzek5/S5qNw==",
"version": "6.1.10",
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.10.tgz",
"integrity": "sha512-jDG89TbZItY7W7rIcS1RqT63vWOPD4XuQLNKqZO0DY7mKnKh/CGBd0eg3nDMXUl143Qp//IxJKe2TfBQRDEU4A==",
"requires": {
"@react-navigation/core": "^6.4.10",
"escape-string-regexp": "^4.0.0",
@ -27370,11 +27390,11 @@
}
},
"@react-navigation/native-stack": {
"version": "6.9.17",
"resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-6.9.17.tgz",
"integrity": "sha512-X8p8aS7JptQq7uZZNFEvfEcPf6tlK4PyVwYDdryRbG98B4bh2wFQYMThxvqa+FGEN7USEuHdv2mF0GhFKfX0ew==",
"version": "6.9.18",
"resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-6.9.18.tgz",
"integrity": "sha512-PSe0qjROy8zD78ehW048NSuzWRktioSCJmB8LzWSR65ndgVaC2rO+xvgyjhHjqm01YdyVM1XTct2EorSjDV2Ow==",
"requires": {
"@react-navigation/elements": "^1.3.21",
"@react-navigation/elements": "^1.3.22",
"warn-once": "^0.1.0"
}
},
@ -27490,6 +27510,15 @@
"@babel/types": "^7.20.7"
}
},
"@types/bip38": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@types/bip38/-/bip38-3.1.2.tgz",
"integrity": "sha512-KF5aiS7DUJs2llJJeg1O1Io129PETszfUfDQotJ4VPBXzytpIUmb7n2MHWEdFYRHs2LYoaRivP/aJbTlF56J+Q==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/bn.js": {
"version": "4.11.6",
"resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz",
@ -27655,6 +27684,15 @@
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="
},
"@types/wif": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@types/wif/-/wif-2.0.5.tgz",
"integrity": "sha512-addYBlYjDxLfJxDUoyTzICnu0u4snCdGJpICIIFk65zGcdjah3twTJq1Fdy+OdeZSRiof2raFtMqSqF9KeqthQ==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/yargs": {
"version": "16.0.9",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz",
@ -29873,9 +29911,9 @@
"dev": true
},
"detox": {
"version": "20.17.0",
"resolved": "https://registry.npmjs.org/detox/-/detox-20.17.0.tgz",
"integrity": "sha512-IasYgexfkrCoZuJTaqqKHQ2yflK+tnqifzdLwrp4hdTWlXUlG9j/YcM1Dn3ThSs3b6VNMtbSe6xoPkKD0oNiIQ==",
"version": "20.17.1",
"resolved": "https://registry.npmjs.org/detox/-/detox-20.17.1.tgz",
"integrity": "sha512-10pey6CR9D5GSloRkH60ObBGZ8VS11H7iuBNY7qq6jO2swiqqckHhPLRXfH9+WGR7l3vDnfU+G/gQs7JxQkJwA==",
"requires": {
"ajv": "^8.6.3",
"bunyan": "^1.8.12",
@ -37593,9 +37631,9 @@
}
},
"react-native-reanimated": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.6.2.tgz",
"integrity": "sha512-IIMREMOrxhtK35drfpzh2UhxNqAOHnuvGgtMofj7yHcMj16tmWZR2zFvMUf6z2MfmXv+aVgFQ6TRZ6yKYf7LNA==",
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.7.0.tgz",
"integrity": "sha512-KM+MKa3CJWqsF4GlOLLKBxTR2NEcrg5/HP9J2b6Dfgvll1sjZPywCOEEIh967SboEU8N9LjYZuoVm2UoXGxp2Q==",
"requires": {
"@babel/plugin-transform-object-assign": "^7.16.7",
"@babel/preset-typescript": "^7.16.7",
@ -37709,9 +37747,9 @@
}
},
"react-native-webview": {
"version": "13.7.1",
"resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.7.1.tgz",
"integrity": "sha512-tnfTgjIYY3KnBfbN9hRCJf2y/6JZBev3aq2ZacLml0AZn4ufTe4MyCiE1NAZ/l5WHV/1eaqN0srxn45ATL0+Ow==",
"version": "13.8.1",
"resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.8.1.tgz",
"integrity": "sha512-7Jqm1WzWJrOWraBAXQfKtr/Uo5Jw/IJHzC40jYLwgV/eVGmLJ9BpGKw6QVw7wpRkjmTZ2Typ4B1aHJLJJQFslA==",
"requires": {
"escape-string-regexp": "2.0.0",
"invariant": "2.2.4"

View file

@ -1,6 +1,6 @@
{
"name": "bluewallet",
"version": "6.5.2",
"version": "6.5.3",
"license": "MIT",
"repository": {
"type": "git",
@ -13,12 +13,14 @@
"@react-native/eslint-config": "^0.72.2",
"@react-native/metro-config": "^0.73.0",
"@tsconfig/react-native": "^3.0.2",
"@types/bip38": "^3.1.2",
"@types/bs58check": "^2.1.0",
"@types/create-hash": "^1.2.2",
"@types/jest": "^29.4.0",
"@types/react": "^18.2.16",
"@types/react-native": "^0.72.0",
"@types/react-test-renderer": "^18.0.0",
"@types/wif": "^2.0.5",
"@typescript-eslint/eslint-plugin": "^6.2.0",
"@typescript-eslint/parser": "^6.2.0",
"eslint": "^8.45.0",
@ -95,7 +97,7 @@
},
"dependencies": {
"@babel/preset-env": "^7.20.0",
"@bugsnag/react-native": "7.22.4",
"@bugsnag/react-native": "7.22.5",
"@bugsnag/source-maps": "2.3.1",
"@keystonehq/bc-ur-registry": "0.6.4",
"@ngraveio/bc-ur": "1.1.6",
@ -173,7 +175,7 @@
"react-native-quick-actions": "0.3.13",
"react-native-randombytes": "3.6.1",
"react-native-rate": "1.2.12",
"react-native-reanimated": "3.6.2",
"react-native-reanimated": "3.7.0",
"react-native-safe-area-context": "4.9.0",
"react-native-screens": "3.29.0",
"react-native-secure-key-store": "https://github.com/BlueWallet/react-native-secure-key-store#2076b48",
@ -182,7 +184,7 @@
"react-native-tcp-socket": "6.0.6",
"react-native-vector-icons": "10.0.3",
"react-native-watch-connectivity": "1.1.0",
"react-native-webview": "13.7.1",
"react-native-webview": "13.8.1",
"react-native-widget-center": "https://github.com/BlueWallet/react-native-widget-center#a128c38",
"readable-stream": "3.6.2",
"realm": "12.6.0",

View file

@ -9,7 +9,6 @@ import { BlueStorageContext } from '../blue_modules/storage-context';
import presentAlert from '../components/Alert';
import Button from '../components/Button';
import triggerHapticFeedback, { HapticFeedbackTypes } from '../blue_modules/hapticFeedback';
import SafeArea from '../components/SafeArea';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
const prompt = require('../helpers/prompt');
@ -75,14 +74,12 @@ const PlausibleDeniability: React.FC = () => {
}
};
return state.isLoading ? (
<SafeArea>
<BlueLoading />
</SafeArea>
) : (
<SafeArea>
<BlueCard>
<ScrollView>
return (
<ScrollView centerContent={state.isLoading} automaticallyAdjustContentInsets contentInsetAdjustmentBehavior="automatic">
{state.isLoading ? (
<BlueLoading />
) : (
<BlueCard>
<BlueText>{loc.plausibledeniability.help}</BlueText>
<BlueText />
<BlueText>{loc.plausibledeniability.help2}</BlueText>
@ -92,9 +89,9 @@ const PlausibleDeniability: React.FC = () => {
title={loc.plausibledeniability.create_fake_storage}
onPress={handleOnCreateFakeStorageButtonPressed}
/>
</ScrollView>
</BlueCard>
</SafeArea>
</BlueCard>
)}
</ScrollView>
);
};

View file

@ -77,16 +77,12 @@ const LdkOpenChannel = (props: any) => {
}, [psbt]);
useEffect(() => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
Biometric.isBiometricUseCapableAndEnabled().then(setIsBiometricUseCapableAndEnabled);
}, []);
const finalizeOpenChannel = async () => {
setIsLoading(true);
if (isBiometricUseCapableAndEnabled) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (!(await Biometric.unlockWithBiometrics())) {
setIsLoading(false);
return;

View file

@ -23,8 +23,7 @@ import Button from '../../components/Button';
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
import SafeArea from '../../components/SafeArea';
import presentAlert from '../../components/Alert';
const scanqr = require('../../helpers/scan-qr');
import { scanQrHelper } from '../../helpers/scan-qr';
const BROADCAST_RESULT = Object.freeze({
none: 'Input transaction hex',
@ -78,7 +77,7 @@ const Broadcast = () => {
};
const handleQRScan = async () => {
const scannedData = await scanqr(navigate, name);
const scannedData = await scanQrHelper(navigate, name);
if (!scannedData) return;
if (scannedData.indexOf('+') === -1 && scannedData.indexOf('=') === -1 && scannedData.indexOf('=') === -1) {

View file

@ -2,40 +2,37 @@ import React from 'react';
import { ScrollView } from 'react-native';
import navigationStyle from '../../components/navigationStyle';
import { BlueCard, BlueText, BlueSpacing20 } from '../../BlueComponents';
import SafeArea from '../../components/SafeArea';
import loc from '../../loc';
const Licensing = () => {
return (
<SafeArea>
<ScrollView>
<BlueCard>
<BlueText>MIT License</BlueText>
<BlueSpacing20 />
<BlueText>Copyright (c) 2018-2024 BlueWallet developers</BlueText>
<BlueSpacing20 />
<BlueText>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
</BlueText>
<BlueSpacing20 />
<ScrollView contentInsetAdjustmentBehavior="automatic" automaticallyAdjustContentInsets>
<BlueCard>
<BlueText>MIT License</BlueText>
<BlueSpacing20 />
<BlueText>Copyright (c) 2018-2024 BlueWallet developers</BlueText>
<BlueSpacing20 />
<BlueText>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
</BlueText>
<BlueSpacing20 />
<BlueText>
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
</BlueText>
<BlueSpacing20 />
<BlueText>
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
</BlueText>
<BlueSpacing20 />
<BlueText>
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
</BlueText>
</BlueCard>
</ScrollView>
</SafeArea>
<BlueText>
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
</BlueText>
</BlueCard>
</ScrollView>
);
};

View file

@ -53,8 +53,8 @@ const EncryptStorage = () => {
popToTop();
} catch (e) {
if (password) {
presentAlert({ message: loc._.bad_password });
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
presentAlert({ message: loc._.bad_password });
}
setIsLoading(false);
@ -83,6 +83,7 @@ const EncryptStorage = () => {
saveToDisk();
} else {
setIsLoading(false);
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
presentAlert({ message: loc.settings.passwords_do_not_match });
}
} else {
@ -124,7 +125,7 @@ const EncryptStorage = () => {
};
return isLoading ? (
<ScrollView>
<ScrollView centerContent>
<BlueLoading />
</ScrollView>
) : (

View file

@ -36,8 +36,15 @@ const WalletsListSections = { CAROUSEL: 'CAROUSEL', TRANSACTIONS: 'TRANSACTIONS'
const WalletsList = () => {
const walletsCarousel = useRef();
const currentWalletIndex = useRef(0);
const { wallets, getTransactions, getBalance, refreshAllWalletTransactions, setSelectedWalletID, isElectrumDisabled } =
useContext(BlueStorageContext);
const {
wallets,
getTransactions,
getBalance,
refreshAllWalletTransactions,
setSelectedWalletID,
isElectrumDisabled,
setReloadTransactionsMenuActionFunction,
} = useContext(BlueStorageContext);
const { width } = useWindowDimensions();
const { colors, scanImage } = useTheme();
const { navigate, setOptions } = useNavigation();
@ -67,6 +74,10 @@ const WalletsList = () => {
useCallback(() => {
verifyBalance();
setSelectedWalletID(undefined);
setReloadTransactionsMenuActionFunction(onRefresh);
return () => {
setReloadTransactionsMenuActionFunction(() => {});
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []),
);
@ -352,6 +363,12 @@ const WalletsList = () => {
refreshTransactions(true, false);
};
const refreshProps = isDesktop
? {}
: {
...(isElectrumDisabled ? {} : { refreshing: isLoading, onRefresh }),
};
return (
<View style={styles.root} onLayout={onLayout}>
<View style={[styles.walletsListWrapper, stylesHook.walletsListWrapper]}>
@ -359,8 +376,7 @@ const WalletsList = () => {
removeClippedSubviews
contentInsetAdjustmentBehavior="automatic"
automaticallyAdjustContentInsets
refreshing={isLoading}
{...(isElectrumDisabled ? {} : { refreshing: isLoading, onRefresh })}
{...refreshProps}
renderItem={renderSectionItem}
keyExtractor={sectionListKeyExtractor}
renderSectionHeader={renderSectionHeader}

View file

@ -45,7 +45,14 @@ const buttonFontSize =
: PixelRatio.roundToNearestPixel(Dimensions.get('window').width / 26);
const WalletTransactions = ({ navigation }) => {
const { wallets, saveToDisk, setSelectedWalletID, walletTransactionUpdateStatus, isElectrumDisabled } = useContext(BlueStorageContext);
const {
wallets,
saveToDisk,
setSelectedWalletID,
walletTransactionUpdateStatus,
isElectrumDisabled,
setReloadTransactionsMenuActionFunction,
} = useContext(BlueStorageContext);
const [isLoading, setIsLoading] = useState(false);
const { walletID } = useRoute().params;
const { name } = useRoute();
@ -502,6 +509,22 @@ const WalletTransactions = ({ navigation }) => {
index,
});
useFocusEffect(
useCallback(() => {
setReloadTransactionsMenuActionFunction(refreshTransactions);
return () => {
setReloadTransactionsMenuActionFunction(undefined);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []),
);
const refreshProps = isDesktop
? {}
: {
...(isElectrumDisabled ? {} : { refreshing: isLoading, refreshTransactions }),
};
return (
<View style={styles.flex}>
<TransactionsNavigationHeader
@ -567,7 +590,7 @@ const WalletTransactions = ({ navigation }) => {
{isLightning() && <Text style={styles.emptyTxsLightning}>{loc.wallets.list_empty_txs2_lightning}</Text>}
</ScrollView>
}
{...(isElectrumDisabled ? {} : { refreshing: isLoading, onRefresh: refreshTransactions })}
{...refreshProps}
data={dataSource}
extraData={[timeElapsed, dataSource, wallets]}
keyExtractor={_keyExtractor}

View file

@ -1,122 +0,0 @@
import React, { useCallback, useContext, useEffect, useState } from 'react';
import { InteractionManager, ActivityIndicator, View, StyleSheet } from 'react-native';
import { useFocusEffect, useRoute, useNavigation } from '@react-navigation/native';
import Share from 'react-native-share';
import navigationStyle from '../../components/navigationStyle';
import { BlueSpacing20, BlueText, BlueCopyTextToClipboard } from '../../BlueComponents';
import Privacy from '../../blue_modules/Privacy';
import Biometric from '../../class/biometrics';
import loc from '../../loc';
import { BlueStorageContext } from '../../blue_modules/storage-context';
import QRCodeComponent from '../../components/QRCodeComponent';
import HandoffComponent from '../../components/handoff';
import { useTheme } from '../../components/themes';
import Button from '../../components/Button';
import SafeArea from '../../components/SafeArea';
const styles = StyleSheet.create({
root: {
flex: 1,
paddingTop: 20,
},
container: {
alignItems: 'center',
flex: 1,
justifyContent: 'center',
},
share: {
alignSelf: 'center',
width: '40%',
},
});
const WalletXpub = () => {
const { wallets } = useContext(BlueStorageContext);
const { walletID, xpub } = useRoute().params;
const wallet = wallets.find(w => w.getID() === walletID);
const [isLoading, setIsLoading] = useState(true);
const [xPubText, setXPubText] = useState();
const { goBack, setParams } = useNavigation();
const { colors } = useTheme();
const [qrCodeSize, setQRCodeSize] = useState(90);
const stylesHook = StyleSheet.create({ root: { backgroundColor: colors.elevated } });
useFocusEffect(
useCallback(() => {
Privacy.enableBlur();
const task = InteractionManager.runAfterInteractions(async () => {
if (wallet) {
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
if (isBiometricsEnabled) {
if (!(await Biometric.unlockWithBiometrics())) {
return goBack();
}
}
setParams({ xpub: wallet.getXpub() });
setIsLoading(false);
} else if (xpub) {
setIsLoading(false);
}
});
return () => {
task.cancel();
Privacy.disableBlur();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [goBack, walletID]),
);
useEffect(() => {
setXPubText(xpub);
}, [xpub]);
const onLayout = e => {
const { height, width } = e.nativeEvent.layout;
setQRCodeSize(height > width ? width - 40 : e.nativeEvent.layout.width / 1.8);
};
const handleShareButtonPressed = () => {
Share.open({ message: xpub }).catch(error => console.log(error));
};
return isLoading ? (
<View style={[styles.container, stylesHook.root]}>
<ActivityIndicator />
</View>
) : (
<SafeArea style={[styles.root, stylesHook.root]} onLayout={onLayout}>
<>
<View style={styles.container}>
{wallet && (
<>
<View>
<BlueText>{wallet.typeReadable}</BlueText>
</View>
<BlueSpacing20 />
</>
)}
<QRCodeComponent value={xpub} size={qrCodeSize} />
<BlueSpacing20 />
<BlueCopyTextToClipboard text={xPubText} />
</View>
<HandoffComponent title={loc.wallets.xpub_title} type={HandoffComponent.activityTypes.Xpub} userInfo={{ xpub: xPubText }} />
<View style={styles.share}>
<Button onPress={handleShareButtonPressed} title={loc.receive.details_share} />
</View>
</>
</SafeArea>
);
};
WalletXpub.navigationOptions = navigationStyle(
{
closeButton: true,
headerBackVisible: false,
},
opts => ({ ...opts, headerTitle: loc.wallets.xpub_title }),
);
export default WalletXpub;

View file

@ -0,0 +1,42 @@
import { StyleSheet } from 'react-native';
import { useTheme } from '../../components/themes';
import { useMemo } from 'react';
export const styles = StyleSheet.create({
root: {
flex: 1,
paddingTop: 20,
},
container: {
alignItems: 'center',
flex: 1,
justifyContent: 'center',
},
share: {
alignSelf: 'center',
width: '40%',
},
});
export const useDynamicStyles = () => {
const theme = useTheme();
const stylesHook = useMemo(
() =>
StyleSheet.create({
root: {
backgroundColor: theme.colors.elevated,
// Add more dynamic styles as needed
},
container: {
// Example of another dynamic style
borderColor: theme.colors.inputBorderColor,
borderWidth: 1,
},
// You can add more dynamically themed styles here
}),
[theme],
); // Recompute styles only when theme changes
return stylesHook;
};

124
screen/wallets/xpub.tsx Normal file
View file

@ -0,0 +1,124 @@
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
import { InteractionManager, ActivityIndicator, View } from 'react-native';
import { useFocusEffect, useRoute, useNavigation, RouteProp, NavigationProp } from '@react-navigation/native';
import Share from 'react-native-share';
import { styles, useDynamicStyles } from './xpub.styles';
import navigationStyle from '../../components/navigationStyle';
import { BlueSpacing20, BlueText, BlueCopyTextToClipboard } from '../../BlueComponents';
import Privacy from '../../blue_modules/Privacy';
import Biometric from '../../class/biometrics';
import loc from '../../loc';
import { BlueStorageContext } from '../../blue_modules/storage-context';
import QRCodeComponent from '../../components/QRCodeComponent';
import HandoffComponent from '../../components/handoff';
import Button from '../../components/Button';
import SafeArea from '../../components/SafeArea';
import { AbstractWallet } from '../../class';
type WalletXpubRouteProp = RouteProp<{ params: { walletID: string; xpub: string } }, 'params'>;
export type RootStackParamList = {
WalletXpub: {
walletID: string;
xpub: string;
};
};
const WalletXpub: React.FC = () => {
const { wallets } = useContext(BlueStorageContext);
const route = useRoute<WalletXpubRouteProp>();
const { walletID, xpub } = route.params;
const wallet = wallets.find((w: AbstractWallet) => w.getID() === walletID);
const [isLoading, setIsLoading] = useState<boolean>(true);
const [xPubText, setXPubText] = useState<string | undefined>(undefined);
const navigation = useNavigation<NavigationProp<RootStackParamList, 'WalletXpub'>>();
const stylesHook = useDynamicStyles(); // This now includes the theme implicitly
const [qrCodeSize, setQRCodeSize] = useState<number>(90);
const lastWalletIdRef = useRef<string | undefined>();
useFocusEffect(
useCallback(() => {
// Skip execution if walletID hasn't changed
if (lastWalletIdRef.current === walletID) {
return;
}
Privacy.enableBlur();
const task = InteractionManager.runAfterInteractions(async () => {
if (wallet) {
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
if (isBiometricsEnabled) {
if (!(await Biometric.unlockWithBiometrics())) {
return navigation.goBack();
}
}
const walletXpub = wallet.getXpub();
if (xpub !== walletXpub) {
navigation.setParams({ xpub: walletXpub });
}
setIsLoading(false);
} else if (xpub) {
setIsLoading(false);
}
});
lastWalletIdRef.current = walletID;
return () => {
task.cancel();
Privacy.disableBlur();
};
}, [wallet, xpub, walletID, navigation]),
);
useEffect(() => {
setXPubText(xpub);
}, [xpub]);
const onLayout = (e: { nativeEvent: { layout: { width: any; height?: any } } }) => {
const { height, width } = e.nativeEvent.layout;
setQRCodeSize(height > width ? width - 40 : e.nativeEvent.layout.width / 1.8);
};
const handleShareButtonPressed = useCallback(() => {
Share.open({ message: xpub }).catch(console.log);
}, [xpub]);
return (
<SafeArea style={[styles.root, stylesHook.root]} onLayout={onLayout}>
{isLoading ? (
<ActivityIndicator />
) : (
<>
<View style={styles.container}>
{wallet && (
<>
<View>
<BlueText>{wallet.typeReadable}</BlueText>
</View>
<BlueSpacing20 />
</>
)}
<QRCodeComponent value={xpub} size={qrCodeSize} />
<BlueSpacing20 />
<BlueCopyTextToClipboard text={xPubText} />
</View>
<HandoffComponent title={loc.wallets.xpub_title} type={HandoffComponent.activityTypes.Xpub} userInfo={{ xpub: xPubText }} />
<View style={styles.share}>
<Button onPress={handleShareButtonPressed} title={loc.receive.details_share} />
</View>
</>
)}
</SafeArea>
);
};
// @ts-ignore: Deal with later
WalletXpub.navigationOptions = navigationStyle(
{
closeButton: true,
headerBackVisible: false,
},
opts => ({ ...opts, headerTitle: loc.wallets.xpub_title }),
);
export default WalletXpub;

View file

@ -1,23 +1,28 @@
#!/bin/bash
# assumes 2 env variables: KEYSTORE_FILE_HEX & KEYSTORE_PASSWORD
#
# PS. to turn file to hex and back:
# $ xxd -plain test.txt > test.hex
# $ xxd -plain -revert test.hex test2.txt
echo $KEYSTORE_FILE_HEX > bluewallet-release-key.keystore.hex
xxd -plain -revert bluewallet-release-key.keystore.hex > ./android/bluewallet-release-key.keystore
rm bluewallet-release-key.keystore.hex
cd android
TIMESTAMP=$(date +%s)
sed -i'.original' "s/versionCode 1/versionCode $TIMESTAMP/g" app/build.gradle
# Use the BUILD_NUMBER environment variable set in the GitHub Actions workflow
sed -i'.original' "s/versionCode 1/versionCode $BUILD_NUMBER/g" app/build.gradle
# Extract versionName from build.gradle
VERSION_NAME=$(grep versionName app/build.gradle | awk '{print $2}' | tr -d '"')
./gradlew assembleRelease
mv ./app/build/outputs/apk/release/app-release-unsigned.apk ./app/build/outputs/apk/release/app-release.apk
# Rename the APK file to include the dynamic version and build number with parentheses
mv ./app/build/outputs/apk/release/app-release-unsigned.apk "./app/build/outputs/apk/release/BlueWallet-${VERSION_NAME}($BUILD_NUMBER).apk"
echo wheres waldo?
find $ANDROID_HOME | grep apksigner | grep -v jar
$ANDROID_HOME/build-tools/34.0.0/apksigner sign --ks ./bluewallet-release-key.keystore --ks-pass=pass:$KEYSTORE_PASSWORD ./app/build/outputs/apk/release/app-release.apk
$ANDROID_HOME/build-tools/34.0.0/apksigner sign --ks ./bluewallet-release-key.keystore --ks-pass=pass:$KEYSTORE_PASSWORD "./app/build/outputs/apk/release/BlueWallet-${VERSION_NAME}($BUILD_NUMBER).apk"

View file

@ -0,0 +1,4 @@
declare module 'react-native-passcode-auth' {
declare function isSupported(): Promise<boolean>;
declare function authenticate(): Promise<boolean>;
}