mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-03-13 19:16:52 +01:00
Merge branch 'master' into headr
This commit is contained in:
commit
8d694ceb7b
16 changed files with 325 additions and 123 deletions
|
@ -73,6 +73,10 @@ def enableProguardInReleaseBuilds = false
|
|||
def jscFlavor = 'org.webkit:android-jsc-intl:+'
|
||||
|
||||
android {
|
||||
androidResources {
|
||||
noCompress += ["bundle"]
|
||||
}
|
||||
|
||||
ndkVersion rootProject.ext.ndkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
@ -136,4 +140,4 @@ dependencies {
|
|||
implementation 'androidx.constraintlayout:constraintlayout:2.2.0'
|
||||
}
|
||||
apply plugin: 'com.google.gms.google-services' // Google Services plugin
|
||||
apply plugin: "com.bugsnag.android.gradle"
|
||||
apply plugin: "com.bugsnag.android.gradle"
|
||||
|
|
|
@ -10,7 +10,6 @@ import RNQRGenerator from 'rn-qr-generator';
|
|||
import { CommonToolTipActions } from '../typings/CommonToolTipActions';
|
||||
import { useSettings } from '../hooks/context/useSettings';
|
||||
import { useExtendedNavigation } from '../hooks/useExtendedNavigation';
|
||||
import { isDesktop } from '../blue_modules/environment';
|
||||
|
||||
interface AddressInputScanButtonProps {
|
||||
isLoading?: boolean;
|
||||
|
@ -146,7 +145,6 @@ export const AddressInputScanButton = ({
|
|||
testID={testID}
|
||||
disabled={isLoading}
|
||||
onPress={toolTipOnPress}
|
||||
isMenuPrimaryAction={isDesktop}
|
||||
buttonStyle={type === 'default' ? buttonStyle : undefined}
|
||||
accessibilityLabel={loc.send.details_scan}
|
||||
accessibilityHint={loc.send.details_scan_hint}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import React, { useState, useRef } from 'react';
|
||||
import { Animated, Platform, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
// @ts-ignore: no declaration file yet
|
||||
import { Camera, CameraApi, CameraType, Orientation } from 'react-native-camera-kit';
|
||||
import loc from '../loc';
|
||||
import { Icon } from '@rneui/base';
|
||||
import { OnOrientationChangeData, OnReadCodeData } from 'react-native-camera-kit/dist/CameraProps';
|
||||
import { triggerSelectionHapticFeedback } from '../blue_modules/hapticFeedback';
|
||||
import { isDesktop } from '../blue_modules/environment';
|
||||
// @ts-ignore: no declaration file yet
|
||||
import { OnOrientationChangeData, OnReadCodeData } from 'react-native-camera-kit/dist/CameraProps';
|
||||
|
||||
interface CameraScreenProps {
|
||||
onCancelButtonPress: () => void;
|
||||
|
@ -95,74 +98,104 @@ const CameraScreen: React.FC<CameraScreenProps> = ({
|
|||
|
||||
return (
|
||||
<View style={styles.screen}>
|
||||
<View style={styles.topButtons}>
|
||||
<TouchableOpacity style={[styles.topButton, uiRotationStyle, torchMode ? styles.activeTorch : {}]} onPress={onSetTorch}>
|
||||
<Animated.View style={styles.topButtonImg}>
|
||||
{Platform.OS === 'ios' ? (
|
||||
<Icon name={torchMode ? 'flashlight-on' : 'flashlight-off'} type="font-awesome-6" color={torchMode ? '#000' : '#fff'} />
|
||||
) : (
|
||||
<Icon name={torchMode ? 'flash-on' : 'flash-off'} type="ionicons" color={torchMode ? '#000' : '#fff'} />
|
||||
{/* Render top buttons only if not desktop as they would not be relevant */}
|
||||
{!isDesktop && (
|
||||
<View style={styles.topButtons}>
|
||||
<TouchableOpacity style={[styles.topButton, uiRotationStyle, torchMode ? styles.activeTorch : {}]} onPress={onSetTorch}>
|
||||
<Animated.View style={styles.topButtonImg}>
|
||||
{Platform.OS === 'ios' ? (
|
||||
<Icon name={torchMode ? 'flashlight-on' : 'flashlight-off'} type="font-awesome-6" color={torchMode ? '#000' : '#fff'} />
|
||||
) : (
|
||||
<Icon name={torchMode ? 'flash-on' : 'flash-off'} type="ionicons" color={torchMode ? '#000' : '#fff'} />
|
||||
)}
|
||||
</Animated.View>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.rightButtonsContainer}>
|
||||
{showImagePickerButton && (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.pick_image}
|
||||
style={[styles.topButton, styles.spacing, uiRotationStyle]}
|
||||
onPress={onImagePickerButtonPress}
|
||||
>
|
||||
<Animated.View style={styles.topButtonImg}>
|
||||
<Icon name="image" type="font-awesome" color="#ffffff" />
|
||||
</Animated.View>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</Animated.View>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.rightButtonsContainer}>
|
||||
{showImagePickerButton && (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.pick_image}
|
||||
style={[styles.topButton, styles.spacing, uiRotationStyle]}
|
||||
onPress={onImagePickerButtonPress}
|
||||
>
|
||||
<Animated.View style={styles.topButtonImg}>
|
||||
<Icon name="image" type="font-awesome" color="#ffffff" />
|
||||
</Animated.View>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{showFilePickerButton && (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.pick_file}
|
||||
style={[styles.topButton, styles.spacing, uiRotationStyle]}
|
||||
onPress={onFilePickerButtonPress}
|
||||
>
|
||||
<Animated.View style={styles.topButtonImg}>
|
||||
<Icon name="file-import" type="font-awesome-5" color="#ffffff" />
|
||||
</Animated.View>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{showFilePickerButton && (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.pick_file}
|
||||
style={[styles.topButton, styles.spacing, uiRotationStyle]}
|
||||
onPress={onFilePickerButtonPress}
|
||||
>
|
||||
<Animated.View style={styles.topButtonImg}>
|
||||
<Icon name="file-import" type="font-awesome-5" color="#ffffff" />
|
||||
</Animated.View>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
)}
|
||||
<View style={styles.cameraContainer}>
|
||||
<Camera
|
||||
ref={cameraRef}
|
||||
style={styles.cameraPreview}
|
||||
cameraType={cameraType}
|
||||
resetFocusWhenMotionDetected
|
||||
zoom={zoom}
|
||||
maxZoom={10}
|
||||
scanBarcode
|
||||
resizeMode="cover"
|
||||
onZoom={handleZoom}
|
||||
onReadCode={handleReadCode}
|
||||
torchMode={torchMode ? 'on' : 'off'}
|
||||
resetFocusWhenMotionDetected
|
||||
zoom={zoom}
|
||||
onZoom={handleZoom}
|
||||
maxZoom={10}
|
||||
onOrientationChange={handleOrientationChange}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View style={styles.bottomButtons}>
|
||||
<TouchableOpacity onPress={onCancelButtonPress}>
|
||||
<Animated.Text style={[styles.backTextStyle, uiRotationStyle]}>{loc._.cancel}</Animated.Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={[styles.bottomButton, uiRotationStyle]} onPress={onSwitchCameraPressed}>
|
||||
<Animated.View style={[styles.topButtonImg, uiRotationStyle]}>
|
||||
{Platform.OS === 'ios' ? (
|
||||
<Icon name="cameraswitch" type="font-awesome-6" color="#ffffff" />
|
||||
) : (
|
||||
<Icon name={cameraType === CameraType.Back ? 'camera-rear' : 'camera-front'} type="ionicons" color="#ffffff" />
|
||||
{isDesktop ? (
|
||||
<View style={styles.rightButtonsContainer}>
|
||||
{showImagePickerButton && (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.pick_image}
|
||||
style={[styles.bottomButton, styles.spacing, uiRotationStyle]}
|
||||
onPress={onImagePickerButtonPress}
|
||||
>
|
||||
<Animated.View style={styles.topButtonImg}>
|
||||
<Icon name="image" type="font-awesome" color="#ffffff" />
|
||||
</Animated.View>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</Animated.View>
|
||||
</TouchableOpacity>
|
||||
{showFilePickerButton && (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.pick_file}
|
||||
style={[styles.bottomButton, styles.spacing, uiRotationStyle]}
|
||||
onPress={onFilePickerButtonPress}
|
||||
>
|
||||
<Animated.View style={styles.topButtonImg}>
|
||||
<Icon name="file-import" type="font-awesome-5" color="#ffffff" />
|
||||
</Animated.View>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
) : (
|
||||
<TouchableOpacity style={[styles.bottomButton, uiRotationStyle]} onPress={onSwitchCameraPressed}>
|
||||
<Animated.View style={[styles.topButtonImg, uiRotationStyle]}>
|
||||
{Platform.OS === 'ios' ? (
|
||||
<Icon name="cameraswitch" type="font-awesome-6" color="#ffffff" />
|
||||
) : (
|
||||
<Icon name={cameraType === CameraType.Back ? 'camera-rear' : 'camera-front'} type="ionicons" color="#ffffff" />
|
||||
)}
|
||||
</Animated.View>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -362,6 +362,7 @@ export const TransactionListItem: React.FC<TransactionListItemProps> = React.mem
|
|||
rightTitle={rowTitle}
|
||||
rightTitleStyle={rowTitleStyle}
|
||||
containerStyle={combinedStyle}
|
||||
testID="TransactionListItem"
|
||||
/>
|
||||
</ToolTipMenu>
|
||||
);
|
||||
|
|
|
@ -1333,7 +1333,7 @@ PODS:
|
|||
- react-native-tcp-socket (6.2.0):
|
||||
- CocoaAsyncSocket
|
||||
- React-Core
|
||||
- react-native-true-sheet (1.1.1):
|
||||
- react-native-true-sheet (2.0.0):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
|
@ -1819,7 +1819,7 @@ PODS:
|
|||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- RNScreens (4.9.0):
|
||||
- RNScreens (4.9.1):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
|
@ -2266,7 +2266,7 @@ SPEC CHECKSUMS:
|
|||
react-native-screen-capture: 7b6121f529681ed2fde36cdedadd0bb39e9a3796
|
||||
react-native-secure-key-store: eb45b44bdec3f48e9be5cdfca0f49ddf64892ea6
|
||||
react-native-tcp-socket: 61379457d7e702e83e28c213b6e085ac079e480f
|
||||
react-native-true-sheet: 58c0848a4326fd2eb7eedd266ec0f39e7c70e5bd
|
||||
react-native-true-sheet: 15f8d1bfbf2aceca472b9ba585b4116041d20f34
|
||||
React-nativeconfig: 67fa7a63ea288cb5b1d0dd2deaf240405fec164f
|
||||
React-NativeModulesApple: 34b7a4d7441a4ee78d18109ff107c1ccf7c074a9
|
||||
React-perflogger: d1149037ac466ad2141d4ae541ca16cb73b2343b
|
||||
|
@ -2312,7 +2312,7 @@ SPEC CHECKSUMS:
|
|||
RNRate: 7641919330e0d6688ad885a985b4bd697ed7d14c
|
||||
RNReactNativeHapticFeedback: 00ba111b82aa266bb3ee1aa576831c2ea9a9dfad
|
||||
RNReanimated: 66cf0f600a26d2b5e74c6e0b1c77c1ab1f62fc05
|
||||
RNScreens: ee069f569efb54804334321c916643f8cc9debaf
|
||||
RNScreens: b3975354ddafe0fb00112a9054898ccf0d92c78e
|
||||
RNShare: 6204e6a1987ba3e7c47071ef703e5449a0e3548a
|
||||
RNSVG: a07e14363aa208062c6483bad24a438d5986d490
|
||||
RNVectorIcons: 182892e7d1a2f27b52d3c627eca5d2665a22ee28
|
||||
|
|
|
@ -29,6 +29,15 @@ export type SendDetailsParams = {
|
|||
};
|
||||
};
|
||||
|
||||
export type TNavigation = {
|
||||
pop: () => void;
|
||||
navigate: () => void;
|
||||
};
|
||||
|
||||
export type TNavigationWrapper = {
|
||||
navigation: TNavigation;
|
||||
};
|
||||
|
||||
export type SendDetailsStackParamList = {
|
||||
SendDetails: SendDetailsParams;
|
||||
Confirm: {
|
||||
|
@ -79,7 +88,7 @@ export type SendDetailsStackParamList = {
|
|||
};
|
||||
SelectWallet: {
|
||||
chainType?: Chain;
|
||||
onWalletSelect?: (wallet: TWallet, navigation: any) => void;
|
||||
onWalletSelect?: (wallet: TWallet, navigationWrapper: TNavigationWrapper) => void;
|
||||
availableWallets?: TWallet[];
|
||||
noWalletExplanationText?: string;
|
||||
onChainRequireSend?: boolean;
|
||||
|
|
6
package-lock.json
generated
6
package-lock.json
generated
|
@ -67,7 +67,7 @@
|
|||
"react-native": "0.76.7",
|
||||
"react-native-biometrics": "3.0.1",
|
||||
"react-native-blue-crypto": "github:BlueWallet/react-native-blue-crypto#3cb5442",
|
||||
"react-native-camera-kit": "14.2.0",
|
||||
"react-native-camera-kit": "https://github.com/BlueWallet/react-native-camera-kit.git#3193427",
|
||||
"react-native-crypto": "2.2.0",
|
||||
"react-native-default-preference": "https://github.com/BlueWallet/react-native-default-preference.git#6338a1f1235e4130b8cfc2dd3b53015eeff2870c",
|
||||
"react-native-device-info": "14.0.4",
|
||||
|
@ -22093,8 +22093,8 @@
|
|||
},
|
||||
"node_modules/react-native-camera-kit": {
|
||||
"version": "14.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-camera-kit/-/react-native-camera-kit-14.2.0.tgz",
|
||||
"integrity": "sha512-rPk/4Ux52/Kc6oIPk0x6NsrvDkeL+kd/GAUJ4xBtTlnmiWjLTgeA2Vjgg9ik03mmyf6rV+LaqaOBT7KejhuHKQ==",
|
||||
"resolved": "git+ssh://git@github.com/BlueWallet/react-native-camera-kit.git#1e1921223bc9da636f9889d96b03df5f77dc7bf1",
|
||||
"integrity": "sha512-jwVriBGZai7b4TCM0JXR0xqBY0HPtu2NSQQMETTNLyTjYYqkHEK2uaWkq/GY5B93gbAnTGJ5bRyQAqfWkPjDEw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
|
|
@ -134,7 +134,7 @@
|
|||
"react-native": "0.76.7",
|
||||
"react-native-biometrics": "3.0.1",
|
||||
"react-native-blue-crypto": "github:BlueWallet/react-native-blue-crypto#3cb5442",
|
||||
"react-native-camera-kit": "14.2.0",
|
||||
"react-native-camera-kit": "https://github.com/BlueWallet/react-native-camera-kit.git#3193427",
|
||||
"react-native-crypto": "2.2.0",
|
||||
"react-native-default-preference": "https://github.com/BlueWallet/react-native-default-preference.git#6338a1f1235e4130b8cfc2dd3b53015eeff2870c",
|
||||
"react-native-device-info": "14.0.4",
|
||||
|
|
|
@ -129,14 +129,6 @@ const SendDetails = () => {
|
|||
return initialFee;
|
||||
}, [customFee, feePrecalc, networkTransactionFees]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('send/details - useEffect');
|
||||
if (wallet) {
|
||||
setHeaderRightOptions();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [colors, wallet, isTransactionReplaceable, balance, addresses, isEditable, isLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
// decode route params
|
||||
const currentAddress = addresses[scrollIndex.current];
|
||||
|
@ -209,6 +201,7 @@ const SendDetails = () => {
|
|||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [routeParams.uri, routeParams.address, routeParams.addRecipientParams]);
|
||||
|
||||
useEffect(() => {
|
||||
// check if we have a suitable wallet
|
||||
const suitable = wallets.filter(w => w.chain === Chain.ONCHAIN && w.allowSend());
|
||||
|
@ -295,38 +288,38 @@ const SendDetails = () => {
|
|||
|
||||
const newFeePrecalc: /* Record<string, any> */ IFee = { ...feePrecalc };
|
||||
|
||||
let targets = [];
|
||||
for (const transaction of addresses) {
|
||||
if (transaction.amount === BitcoinUnit.MAX) {
|
||||
// single output with MAX
|
||||
targets = [{ address: transaction.address }];
|
||||
break;
|
||||
}
|
||||
const value = transaction.amountSats;
|
||||
if (Number(value) > 0) {
|
||||
targets.push({ address: transaction.address, value });
|
||||
} else if (transaction.amount) {
|
||||
if (btcToSatoshi(transaction.amount) > 0) {
|
||||
targets.push({ address: transaction.address, value: btcToSatoshi(transaction.amount) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if targets is empty, insert dust
|
||||
if (targets.length === 0) {
|
||||
targets.push({ address: '36JxaUrpDzkEerkTf1FzwHNE1Hb7cCjgJV', value: 546 });
|
||||
}
|
||||
|
||||
// replace wrong addresses with dump
|
||||
targets = targets.map(t => {
|
||||
if (!wallet.isAddressValid(t.address)) {
|
||||
return { ...t, address: '36JxaUrpDzkEerkTf1FzwHNE1Hb7cCjgJV' };
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
});
|
||||
|
||||
for (const opt of options) {
|
||||
let targets = [];
|
||||
for (const transaction of addresses) {
|
||||
if (transaction.amount === BitcoinUnit.MAX) {
|
||||
// single output with MAX
|
||||
targets = [{ address: transaction.address }];
|
||||
break;
|
||||
}
|
||||
const value = transaction.amountSats;
|
||||
if (Number(value) > 0) {
|
||||
targets.push({ address: transaction.address, value });
|
||||
} else if (transaction.amount) {
|
||||
if (btcToSatoshi(transaction.amount) > 0) {
|
||||
targets.push({ address: transaction.address, value: btcToSatoshi(transaction.amount) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if targets is empty, insert dust
|
||||
if (targets.length === 0) {
|
||||
targets.push({ address: '36JxaUrpDzkEerkTf1FzwHNE1Hb7cCjgJV', value: 546 });
|
||||
}
|
||||
|
||||
// replace wrong addresses with dump
|
||||
targets = targets.map(t => {
|
||||
if (!wallet.isAddressValid(t.address)) {
|
||||
return { ...t, address: '36JxaUrpDzkEerkTf1FzwHNE1Hb7cCjgJV' };
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
});
|
||||
|
||||
let flag = false;
|
||||
while (true) {
|
||||
try {
|
||||
|
@ -1158,11 +1151,18 @@ const SendDetails = () => {
|
|||
[headerRightOnPress, isLoading, headerRightActions],
|
||||
);
|
||||
|
||||
const setHeaderRightOptions = () => {
|
||||
const setHeaderRightOptions = useCallback(() => {
|
||||
navigation.setOptions({
|
||||
headerRight: HeaderRight,
|
||||
});
|
||||
};
|
||||
}, [HeaderRight, navigation]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('send/details - useEffect');
|
||||
if (wallet) {
|
||||
setHeaderRightOptions();
|
||||
}
|
||||
}, [colors, wallet, isTransactionReplaceable, balance, addresses, isEditable, isLoading, setHeaderRightOptions]);
|
||||
|
||||
const handleRecipientsScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
||||
const contentOffset = e.nativeEvent.contentOffset;
|
||||
|
|
|
@ -72,7 +72,7 @@ const SelectWallet: React.FC = () => {
|
|||
const onPress = (item: TWallet) => {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.Selection);
|
||||
if (onWalletSelect) {
|
||||
onWalletSelect(item, { navigation: { pop, navigation: navigation.navigate } });
|
||||
onWalletSelect(item, { navigation: { pop, navigate: navigation.navigate } });
|
||||
} else {
|
||||
// @ts-ignore: fix later
|
||||
navigation.popTo(previousRouteName, { walletID: item.getID(), merge: true });
|
||||
|
|
|
@ -367,12 +367,17 @@ const WalletDetails: React.FC = () => {
|
|||
const purgeTransactions = async () => {
|
||||
if (backdoorPressed < 10) return setBackdoorPressed(backdoorPressed + 1);
|
||||
setBackdoorPressed(0);
|
||||
const msg = 'Transactions purged. Pls go to main screen and back to rerender screen';
|
||||
const msg = 'Transactions & balances purged. Pls go to main screen and back to rerender screen';
|
||||
|
||||
if (wallet.type === HDSegwitBech32Wallet.type) {
|
||||
wallet._txs_by_external_index = {};
|
||||
wallet._txs_by_internal_index = {};
|
||||
presentAlert({ message: msg });
|
||||
|
||||
wallet._balances_by_external_index = {};
|
||||
wallet._balances_by_internal_index = {};
|
||||
wallet._lastTxFetch = 0;
|
||||
wallet._lastBalanceFetch = 0;
|
||||
}
|
||||
|
||||
// @ts-expect-error: Need to fix later
|
||||
|
@ -381,6 +386,15 @@ const WalletDetails: React.FC = () => {
|
|||
wallet._hdWalletInstance._txs_by_external_index = {};
|
||||
// @ts-expect-error: Need to fix later
|
||||
wallet._hdWalletInstance._txs_by_internal_index = {};
|
||||
|
||||
// @ts-expect-error: Need to fix later
|
||||
wallet._hdWalletInstance._balances_by_external_index = {};
|
||||
// @ts-expect-error: Need to fix later
|
||||
wallet._hdWalletInstance._balances_by_internal_index = {};
|
||||
// @ts-expect-error: Need to fix later
|
||||
wallet._hdWalletInstance._lastTxFetch = 0;
|
||||
// @ts-expect-error: Need to fix later
|
||||
wallet._hdWalletInstance._lastBalanceFetch = 0;
|
||||
presentAlert({ message: msg });
|
||||
}
|
||||
};
|
||||
|
@ -528,7 +542,7 @@ const WalletDetails: React.FC = () => {
|
|||
</View>
|
||||
</>
|
||||
<>
|
||||
<Text onPress={purgeTransactions} style={[styles.textLabel2, stylesHook.textLabel2]}>
|
||||
<Text onPress={purgeTransactions} style={[styles.textLabel2, stylesHook.textLabel2]} testID="PurgeBackdoorButton">
|
||||
{loc.transactions.transactions_count.toLowerCase()}
|
||||
</Text>
|
||||
<BlueText>{wallet.getTransactions().length}</BlueText>
|
||||
|
|
|
@ -125,7 +125,9 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
|||
const txs = wallet.getTransactions();
|
||||
txs.sort((a: { received: string }, b: { received: string }) => +new Date(b.received) - +new Date(a.received));
|
||||
return txs;
|
||||
}, [wallet]);
|
||||
// we use `wallet.getLastTxFetch()` to tell if txs list changed
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [wallet, wallet?.getLastTxFetch()]);
|
||||
|
||||
const getTransactions = useCallback((lmt = Infinity): Transaction[] => sortedTransactions.slice(0, lmt), [sortedTransactions]);
|
||||
|
||||
|
@ -177,7 +179,6 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
|||
setFetchFailures(0);
|
||||
const newTimestamp = Date.now();
|
||||
setLastFetchTimestamp(newTimestamp);
|
||||
wallet._lastTxFetch = newTimestamp;
|
||||
} catch (err) {
|
||||
setFetchFailures(prev => {
|
||||
const newFailures = prev + 1;
|
||||
|
@ -298,11 +299,6 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
|||
index,
|
||||
});
|
||||
|
||||
const listData: Transaction[] = useMemo(() => {
|
||||
const transactions = getTransactions(limit);
|
||||
return transactions;
|
||||
}, [getTransactions, limit]);
|
||||
|
||||
const renderItem = useCallback(
|
||||
// eslint-disable-next-line react/no-unused-prop-types
|
||||
({ item }: { item: Transaction }) => {
|
||||
|
@ -503,13 +499,14 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => {
|
|||
onEndReachedThreshold={0.3}
|
||||
onEndReached={loadMoreTransactions}
|
||||
ListFooterComponent={renderListFooterComponent}
|
||||
data={listData}
|
||||
data={getTransactions(limit)}
|
||||
extraData={wallet}
|
||||
keyExtractor={_keyExtractor}
|
||||
renderItem={renderItem}
|
||||
initialNumToRender={10}
|
||||
contentInset={{ top: HEADER_HEIGHT }}
|
||||
removeClippedSubviews
|
||||
testID="TransactionsListView"
|
||||
contentContainerStyle={{ backgroundColor: colors.background }}
|
||||
maxToRenderPerBatch={15}
|
||||
onScroll={Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }], { useNativeDriver: true, listener: handleScroll })}
|
||||
|
|
|
@ -12,8 +12,17 @@ import {
|
|||
tapAndTapAgainIfTextIsNotVisible,
|
||||
tapIfTextPresent,
|
||||
waitForId,
|
||||
countElements,
|
||||
} from './helperz';
|
||||
|
||||
// if loglevel is set to `error`, this kind of logging will still get through
|
||||
console.warn = console.log = (...args) => {
|
||||
let output = '';
|
||||
args.map(arg => (output += String(arg)));
|
||||
|
||||
process.stdout.write(output + '\n');
|
||||
};
|
||||
|
||||
/**
|
||||
* in this suite each test requires that there is one specific wallet present, thus, we import it
|
||||
* before anything else.
|
||||
|
@ -27,7 +36,7 @@ beforeAll(async () => {
|
|||
// reinstalling the app just for any case to clean up app's storage
|
||||
await device.launchApp({ delete: true });
|
||||
|
||||
console.log('before all - importing bip48...');
|
||||
console.log('before all - importing bip84...');
|
||||
await helperImportWallet(process.env.HD_MNEMONIC_BIP84, 'HDsegwitBech32', 'Imported HD SegWit (BIP84 Bech32 Native)', '0.00105526');
|
||||
console.log('...imported!');
|
||||
await device.pressBack();
|
||||
|
@ -711,4 +720,54 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
|
|||
|
||||
process.env.TRAVIS && require('fs').writeFileSync(lockFile, '1');
|
||||
});
|
||||
|
||||
it('can purge txs and balance, then refetch data from tx list screen and see data on screen update', async () => {
|
||||
const lockFile = '/tmp/travislock.' + hashIt('t24');
|
||||
if (process.env.TRAVIS) {
|
||||
if (require('fs').existsSync(lockFile)) return console.warn('skipping', JSON.stringify('t24'), 'as it previously passed on Travis');
|
||||
}
|
||||
if (!process.env.HD_MNEMONIC_BIP84) {
|
||||
console.error('process.env.HD_MNEMONIC_BIP84 not set, skipped');
|
||||
return;
|
||||
}
|
||||
|
||||
await device.launchApp({ newInstance: true });
|
||||
// go inside the wallet
|
||||
await element(by.text('Imported HD SegWit (BIP84 Bech32 Native)')).tap();
|
||||
await element(by.id('WalletDetails')).tap();
|
||||
|
||||
// tapping backdoor button to purge txs and balance:
|
||||
for (let c = 0; c <= 10; c++) {
|
||||
await element(by.id('PurgeBackdoorButton')).tap();
|
||||
await sleep(500);
|
||||
}
|
||||
|
||||
await waitForText('OK');
|
||||
await tapIfTextPresent('OK');
|
||||
|
||||
if (device.getPlatform() === 'ios') {
|
||||
console.warn('rest of the test is Android only, skipped');
|
||||
return;
|
||||
}
|
||||
|
||||
await device.pressBack();
|
||||
|
||||
// asserting there are no transactions and balance is 0:
|
||||
|
||||
await expect(element(by.id('WalletBalance'))).toHaveText('0');
|
||||
await waitForId('TransactionsListEmpty');
|
||||
assert.strictEqual(await countElements('TransactionListItem'), 0);
|
||||
|
||||
await element(by.id('TransactionsListView')).swipe('down', 'slow'); // pul-to-refresh
|
||||
|
||||
// asserting balance and txs loaded:
|
||||
await waitForText('0.00105526'); // the wait inside allows network request to propagate
|
||||
await waitFor(element(by.id('TransactionsListEmpty')))
|
||||
.not.toBeVisible()
|
||||
.withTimeout(25_000);
|
||||
await expect(element(by.id('WalletBalance'))).toHaveText('0.00105526');
|
||||
await expect(element(by.id('TransactionsListEmpty'))).not.toBeVisible();
|
||||
|
||||
assert.ok((await countElements('TransactionListItem')) >= 3); // 3 is arbitrary, real txs on screen depend on screen size
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
import { hashIt, helperDeleteWallet, helperImportWallet, sleep, waitForId } from './helperz';
|
||||
|
||||
// if loglevel is set to `error`, this kind of logging will still get through
|
||||
console.warn = console.log = (...args) => {
|
||||
let output = '';
|
||||
args.map(arg => (output += String(arg)));
|
||||
|
||||
process.stdout.write(output + '\n');
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
// reinstalling the app just for any case to clean up app's storage
|
||||
await device.launchApp({ delete: true });
|
||||
|
|
|
@ -228,3 +228,16 @@ export async function tapIfTextPresent(text) {
|
|||
} catch (_) {}
|
||||
// no need to check for visibility, just silently ignore exception if such testID is not present
|
||||
}
|
||||
|
||||
export async function countElements(testId) {
|
||||
let count = 0;
|
||||
while (true) {
|
||||
try {
|
||||
await expect(element(by.id(testId)).atIndex(count)).toExist();
|
||||
count++;
|
||||
} catch (_) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import assert from 'assert';
|
||||
|
||||
import DeeplinkSchemaMatch from '../../class/deeplink-schema-match';
|
||||
import { HDSegwitBech32Wallet, LightningCustodianWallet } from '../../class';
|
||||
|
||||
jest.mock('../../blue_modules/BlueElectrum', () => {
|
||||
return {
|
||||
|
@ -8,6 +9,15 @@ jest.mock('../../blue_modules/BlueElectrum', () => {
|
|||
};
|
||||
});
|
||||
|
||||
// helper function that promisifies function with a callback:
|
||||
const asyncNavigationRouteFor = async function (event) {
|
||||
return new Promise(function (resolve) {
|
||||
DeeplinkSchemaMatch.navigationRouteFor(event, navValue => {
|
||||
resolve(navValue);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
describe.each(['', '//'])('unit - DeepLinkSchemaMatch', function (suffix) {
|
||||
it('hasSchema', () => {
|
||||
assert.ok(DeeplinkSchemaMatch.hasSchema(`bitcoin:${suffix}12eQ9m4sgAwTSQoNXkRABKhCXCsjm2jdVG`));
|
||||
|
@ -324,14 +334,6 @@ describe.each(['', '//'])('unit - DeepLinkSchemaMatch', function (suffix) {
|
|||
},
|
||||
];
|
||||
|
||||
const asyncNavigationRouteFor = async function (event) {
|
||||
return new Promise(function (resolve) {
|
||||
DeeplinkSchemaMatch.navigationRouteFor(event, navValue => {
|
||||
resolve(navValue);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
for (const event of events) {
|
||||
const navValue = await asyncNavigationRouteFor(event.argument);
|
||||
assert.deepStrictEqual(navValue, event.expected);
|
||||
|
@ -493,4 +495,68 @@ describe.each(['', '//'])('unit - DeepLinkSchemaMatch', function (suffix) {
|
|||
assert.strictEqual(DeeplinkSchemaMatch.hasNeededJsonKeysForMultiSigSharing(isNotAllowed1), false);
|
||||
assert.strictEqual(DeeplinkSchemaMatch.hasNeededJsonKeysForMultiSigSharing(isNotAllowed2), false);
|
||||
});
|
||||
|
||||
it('onWalletSelect should work', async () => {
|
||||
const response = await asyncNavigationRouteFor({
|
||||
url: 'bitcoin:BC1QR7P8NSYPZEJY4KP7CJS0HL5T9X0VF3AYF6UQPC?amount=0.00185579&lightning=LNBC1855790N1PNUPWSFPP5P5RVQJA067PV6NJQ3EFKLP78TN6MHUK842ZFGDCTXRDSGNTY765QDZ62PSKJEPQW3HJQSNPD36XJCEQFPHKUETEVFSKGEM9WGSRYVPJXSSZSNMJV3JHYGZFGSAZQARFVD4K2AR5V95KCMMJ9YCQZPUXQZ6GSP53E4EX9YTD2MGDN2C2CFA0J0SM3E7PVLPJ208H5LMYPNJMGZ7RLGS9QXPQYSGQ6GQMEQXJKKF2DHXJK8XQ4WGLM5NTE3RKEXGYQC6HYGFKS9SHHA6HL9X4339MXHNNQFSH7TS62PU8T9RSWTK6HQ4LV4GW3DPD25DQ8UQQYC909N',
|
||||
});
|
||||
assert.ok(response[1].onWalletSelect);
|
||||
|
||||
let popWasCalled = false;
|
||||
let navigateWasCalled = false;
|
||||
let popWasCalled2 = false;
|
||||
let navigateWasCalled2 = false;
|
||||
const lw = new LightningCustodianWallet();
|
||||
const bw = new HDSegwitBech32Wallet();
|
||||
|
||||
// navigation for a case when user selected LN wallet when was given a choice
|
||||
const navigationMock = {
|
||||
pop: () => {
|
||||
popWasCalled = true;
|
||||
// console.log('pop called');
|
||||
},
|
||||
navigate: (...args) => {
|
||||
navigateWasCalled = true;
|
||||
assert.deepStrictEqual(args, [
|
||||
'ScanLndInvoiceRoot',
|
||||
{
|
||||
params: {
|
||||
uri: 'lightning:LNBC1855790N1PNUPWSFPP5P5RVQJA067PV6NJQ3EFKLP78TN6MHUK842ZFGDCTXRDSGNTY765QDZ62PSKJEPQW3HJQSNPD36XJCEQFPHKUETEVFSKGEM9WGSRYVPJXSSZSNMJV3JHYGZFGSAZQARFVD4K2AR5V95KCMMJ9YCQZPUXQZ6GSP53E4EX9YTD2MGDN2C2CFA0J0SM3E7PVLPJ208H5LMYPNJMGZ7RLGS9QXPQYSGQ6GQMEQXJKKF2DHXJK8XQ4WGLM5NTE3RKEXGYQC6HYGFKS9SHHA6HL9X4339MXHNNQFSH7TS62PU8T9RSWTK6HQ4LV4GW3DPD25DQ8UQQYC909N',
|
||||
walletID: 'bfcacb7288cf43c6c02a1154c432ec155b813798fa4e87cd2c1e5531d6363f71',
|
||||
},
|
||||
screen: 'ScanLndInvoice',
|
||||
},
|
||||
]);
|
||||
},
|
||||
};
|
||||
|
||||
// navigation for a case when user selected ONCHAIN wallet when was given a choice
|
||||
const navigationMock2 = {
|
||||
pop: () => {
|
||||
popWasCalled2 = true;
|
||||
},
|
||||
navigate: (...args) => {
|
||||
navigateWasCalled2 = true;
|
||||
assert.deepStrictEqual(args, [
|
||||
'SendDetailsRoot',
|
||||
{
|
||||
params: {
|
||||
uri: 'bitcoin:BC1QR7P8NSYPZEJY4KP7CJS0HL5T9X0VF3AYF6UQPC?amount=0.00185579&',
|
||||
walletID: 'a1c50c266e229bb66aca0221d5b6a116720004c97437a0a6e279cfea027d0c87',
|
||||
},
|
||||
screen: 'SendDetails',
|
||||
},
|
||||
]);
|
||||
},
|
||||
};
|
||||
|
||||
response[1].onWalletSelect(lw, { navigation: navigationMock });
|
||||
response[1].onWalletSelect(bw, { navigation: navigationMock2 });
|
||||
|
||||
assert.ok(popWasCalled);
|
||||
assert.ok(navigateWasCalled);
|
||||
|
||||
assert.ok(popWasCalled2);
|
||||
assert.ok(navigateWasCalled2);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue