mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-22 15:04:50 +01:00
Merge branch 'master' into wexp
This commit is contained in:
commit
72619e4c86
21 changed files with 216 additions and 157 deletions
|
@ -3,7 +3,7 @@ jobs:
|
|||
|
||||
lint:
|
||||
docker:
|
||||
- image: cimg/node:16.20.2
|
||||
- image: cimg/node:20.16.0
|
||||
|
||||
working_directory: ~/repo
|
||||
|
||||
|
@ -26,7 +26,7 @@ jobs:
|
|||
|
||||
unit:
|
||||
docker:
|
||||
- image: cimg/node:16.20.2
|
||||
- image: cimg/node:20.16.0
|
||||
|
||||
working_directory: ~/repo
|
||||
|
||||
|
@ -50,7 +50,10 @@ jobs:
|
|||
|
||||
integration:
|
||||
docker:
|
||||
- image: cimg/node:16.20.2
|
||||
- image: cimg/node:20.16.0
|
||||
|
||||
environment:
|
||||
RETRY: "1"
|
||||
|
||||
working_directory: ~/repo
|
||||
|
||||
|
@ -71,7 +74,7 @@ jobs:
|
|||
|
||||
# run tests!
|
||||
- run:
|
||||
command: npm run jest || npm run jest || npm run jest
|
||||
command: npm run jest || npm run jest || npm run jest || npm run jest
|
||||
|
||||
# Orchestrate our job run sequence
|
||||
workflows:
|
||||
|
|
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
|
@ -42,7 +42,7 @@ jobs:
|
|||
run: npm install
|
||||
|
||||
- name: Run tests
|
||||
run: npm test || npm test || npm test
|
||||
run: npm test || npm test || npm test || npm test
|
||||
env:
|
||||
BIP47_HD_MNEMONIC: ${{ secrets.BIP47_HD_MNEMONIC}}
|
||||
HD_MNEMONIC: ${{ secrets.HD_MNEMONIC }}
|
||||
|
@ -53,6 +53,7 @@ jobs:
|
|||
FAULTY_ZPUB: ${{ secrets.FAULTY_ZPUB }}
|
||||
MNEMONICS_COBO: ${{ secrets.MNEMONICS_COBO }}
|
||||
MNEMONICS_COLDCARD: ${{ secrets.MNEMONICS_COLDCARD }}
|
||||
RETRY: 1
|
||||
|
||||
e2e:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -65,9 +66,7 @@ jobs:
|
|||
- name: Free Disk Space
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: false
|
||||
tool-cache: true
|
||||
android: false
|
||||
dotnet: true
|
||||
haskell: true
|
||||
|
@ -146,4 +145,4 @@ jobs:
|
|||
if: failure()
|
||||
with:
|
||||
name: e2e-test-videos
|
||||
path: /mnt/artifacts/
|
||||
path: /mnt/artifacts/
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
|
||||
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
|
||||
org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
|
|
|
@ -288,7 +288,6 @@ interface WalletsCarouselProps extends Partial<FlatListProps<any>> {
|
|||
handleLongPress?: () => void;
|
||||
data: TWallet[];
|
||||
scrollEnabled?: boolean;
|
||||
showNewWalletPanel?: boolean; // New prop
|
||||
}
|
||||
|
||||
type FlatListRefType = FlatList<any> & {
|
||||
|
@ -317,7 +316,7 @@ const cStyles = StyleSheet.create({
|
|||
const ListHeaderComponent: React.FC = () => <View style={cStyles.separatorStyle} />;
|
||||
|
||||
const WalletsCarousel = forwardRef<FlatListRefType, WalletsCarouselProps>((props, ref) => {
|
||||
const { horizontal, data, handleLongPress, onPress, selectedWallet, scrollEnabled, showNewWalletPanel, onNewWalletPress } = props;
|
||||
const { horizontal, data, handleLongPress, onPress, selectedWallet, scrollEnabled, onNewWalletPress } = props;
|
||||
const renderItem = useCallback(
|
||||
({ item, index }: ListRenderItemInfo<TWallet>) =>
|
||||
item ? (
|
||||
|
@ -357,8 +356,8 @@ const WalletsCarousel = forwardRef<FlatListRefType, WalletsCarouselProps>((props
|
|||
}, []);
|
||||
|
||||
const onScrollToIndexFailed = (error: { averageItemLength: number; index: number }): void => {
|
||||
console.log('onScrollToIndexFailed');
|
||||
console.log(error);
|
||||
console.debug('onScrollToIndexFailed');
|
||||
console.debug(error);
|
||||
flatListRef.current?.scrollToOffset({ offset: error.averageItemLength * error.index, animated: true });
|
||||
setTimeout(() => {
|
||||
if (data.length !== 0 && flatListRef.current !== null) {
|
||||
|
@ -390,7 +389,7 @@ const WalletsCarousel = forwardRef<FlatListRefType, WalletsCarouselProps>((props
|
|||
ListHeaderComponent={ListHeaderComponent}
|
||||
style={{ minHeight: sliderHeight + 12 }}
|
||||
onScrollToIndexFailed={onScrollToIndexFailed}
|
||||
ListFooterComponent={showNewWalletPanel && onNewWalletPress ? <NewWalletPanel onPress={onNewWalletPress} /> : null}
|
||||
ListFooterComponent={onNewWalletPress ? <NewWalletPanel onPress={onNewWalletPress} /> : null}
|
||||
{...props}
|
||||
/>
|
||||
) : (
|
||||
|
@ -406,7 +405,7 @@ const WalletsCarousel = forwardRef<FlatListRefType, WalletsCarouselProps>((props
|
|||
/>
|
||||
) : null,
|
||||
)}
|
||||
{showNewWalletPanel && onNewWalletPress && <NewWalletPanel onPress={onNewWalletPress} />}
|
||||
{onNewWalletPress && <NewWalletPanel onPress={onNewWalletPress} />}
|
||||
</View>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -10,53 +10,42 @@ import { navigationRef } from '../NavigationService';
|
|||
* @param showFileImportButton {boolean}
|
||||
*
|
||||
* @param onDismiss {function} - if camera is closed via X button it gets triggered
|
||||
* @param options {object} - additional options to pass to navigate
|
||||
* @return {Promise<string | null>}
|
||||
* @param useMerge {boolean} - if true, will merge the new screen with the current screen, otherwise will replace the current screen
|
||||
* @return {Promise<string>}
|
||||
*/
|
||||
function scanQrHelper(
|
||||
currentScreenName: string,
|
||||
showFileImportButton = true,
|
||||
onDismiss?: () => void,
|
||||
options: { merge: boolean } = { merge: true },
|
||||
useMerge = true,
|
||||
): Promise<string | null> {
|
||||
return requestCameraAuthorization().then(() => {
|
||||
return new Promise(resolve => {
|
||||
const params: any = {
|
||||
showFileImportButton: Boolean(showFileImportButton),
|
||||
};
|
||||
let params = {};
|
||||
|
||||
if (options?.merge) {
|
||||
if (onDismiss) {
|
||||
params.onDismiss = onDismiss;
|
||||
}
|
||||
params.onBarScanned = function (data: any) {
|
||||
if (useMerge) {
|
||||
const onBarScanned = function (data: any) {
|
||||
setTimeout(() => resolve(data.data || data), 1);
|
||||
navigationRef.navigate({
|
||||
name: currentScreenName,
|
||||
params: {},
|
||||
merge: options?.merge,
|
||||
});
|
||||
navigationRef.navigate({ name: currentScreenName, params: {}, merge: true });
|
||||
};
|
||||
|
||||
navigationRef.navigate({
|
||||
name: 'ScanQRCodeRoot',
|
||||
params: {
|
||||
screen: 'ScanQRCode',
|
||||
params,
|
||||
},
|
||||
merge: true,
|
||||
});
|
||||
params = {
|
||||
showFileImportButton: Boolean(showFileImportButton),
|
||||
onDismiss,
|
||||
onBarScanned,
|
||||
};
|
||||
} else {
|
||||
navigationRef.navigate({
|
||||
name: 'ScanQRCodeRoot',
|
||||
params: {
|
||||
screen: 'ScanQRCode',
|
||||
params: {
|
||||
showFileImportButton: Boolean(showFileImportButton),
|
||||
},
|
||||
},
|
||||
});
|
||||
params = { launchedBy: currentScreenName, showFileImportButton: Boolean(showFileImportButton) };
|
||||
}
|
||||
|
||||
navigationRef.navigate({
|
||||
name: 'ScanQRCodeRoot',
|
||||
params: {
|
||||
screen: 'ScanQRCode',
|
||||
params,
|
||||
},
|
||||
merge: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
{
|
||||
"originHash" : "52530e6b1e3a85c8854952ef703a6d1bbe1acd82713be2b3166476b9b277db23",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "bugsnag-cocoa",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/bugsnag/bugsnag-cocoa",
|
||||
"state" : {
|
||||
"revision" : "16b9145fc66e5296f16e733f6feb5d0e450574e8",
|
||||
"version" : "6.28.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "efqrcode",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/EFPrefix/EFQRCode.git",
|
||||
"state" : {
|
||||
"revision" : "2991c2f318ad9529d93b2a73a382a3f9c72c64ce",
|
||||
"version" : "6.2.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "keychain-swift",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/evgenyneu/keychain-swift.git",
|
||||
"state" : {
|
||||
"revision" : "5e1b02b6a9dac2a759a1d5dbc175c86bd192a608",
|
||||
"version" : "24.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift_qrcodejs",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/ApolloZhu/swift_qrcodejs.git",
|
||||
"state" : {
|
||||
"revision" : "374dc7f7b9e76c6aeb393f6a84590c6d387e1ecb",
|
||||
"version" : "2.2.2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 3
|
||||
}
|
|
@ -865,4 +865,4 @@ SPEC CHECKSUMS:
|
|||
|
||||
PODFILE CHECKSUM: f19eea438501edfe85fb2fa51d40ba1b57540758
|
||||
|
||||
COCOAPODS: 1.15.2
|
||||
COCOAPODS: 1.14.3
|
||||
|
|
|
@ -3,7 +3,7 @@ import { SendDetailsParams } from './SendDetailsStackParamList';
|
|||
|
||||
export type DetailViewStackParamList = {
|
||||
UnlockWithScreen: undefined;
|
||||
WalletsList: undefined;
|
||||
WalletsList: { scannedData?: string };
|
||||
WalletTransactions: { walletID: string; walletType: string };
|
||||
LDKOpenChannelRoot: undefined;
|
||||
LdkInfo: undefined;
|
||||
|
@ -18,7 +18,7 @@ export type DetailViewStackParamList = {
|
|||
LNDViewInvoice: { invoice: LightningTransaction; walletID: string };
|
||||
LNDViewAdditionalInvoiceInformation: { invoiceId: string };
|
||||
LNDViewAdditionalInvoicePreImage: { invoiceId: string };
|
||||
Broadcast: undefined;
|
||||
Broadcast: { scannedData?: string };
|
||||
IsItMyAddress: undefined;
|
||||
GenerateWord: undefined;
|
||||
LnurlPay: undefined;
|
||||
|
|
11
package-lock.json
generated
11
package-lock.json
generated
|
@ -24,7 +24,7 @@
|
|||
"@react-native/metro-config": "0.74.84",
|
||||
"@react-navigation/drawer": "6.7.2",
|
||||
"@react-navigation/native": "6.1.18",
|
||||
"@react-navigation/native-stack": "6.10.1",
|
||||
"@react-navigation/native-stack": "6.11.0",
|
||||
"@remobile/react-native-qrcode-local-image": "https://github.com/BlueWallet/react-native-qrcode-local-image",
|
||||
"@rneui/base": "4.0.0-rc.8",
|
||||
"@rneui/themed": "4.0.0-rc.8",
|
||||
|
@ -70,7 +70,7 @@
|
|||
"react-native-default-preference": "1.4.4",
|
||||
"react-native-device-info": "11.1.0",
|
||||
"react-native-document-picker": "https://github.com/BlueWallet/react-native-document-picker#6033c4e1b0dd0a6760b5f5a5a2c3b2e5d07f2ae4",
|
||||
"react-native-draggable-flatlist": "github:BlueWallet/react-native-draggable-flatlist#ebfddc4",
|
||||
"react-native-draggable-flatlist": "github:BlueWallet/react-native-draggable-flatlist#3061e30",
|
||||
"react-native-fs": "2.20.0",
|
||||
"react-native-gesture-handler": "2.17.1",
|
||||
"react-native-handoff": "https://github.com/BlueWallet/react-native-handoff#31d005f93d31099d0e564590a3bbd052b8a02b39",
|
||||
|
@ -139,6 +139,7 @@
|
|||
"eslint-plugin-react": "^7.34.1",
|
||||
"eslint-plugin-react-native": "^4.1.0",
|
||||
"jest": "^29.4.2",
|
||||
"jest-environment-node": "^29.7.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"prettier": "^3.2.5",
|
||||
"react-test-renderer": "18.2.0",
|
||||
|
@ -6235,9 +6236,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@react-navigation/native-stack": {
|
||||
"version": "6.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-6.10.1.tgz",
|
||||
"integrity": "sha512-MDTHtelO2EhY1sEbz4YMNw+1YoE0sWXSHLUYireQZFHUXKDfYiWYbxXg2Q08Ool0KEvnZjP0hh0Wm6/ZnvDCtw==",
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-6.11.0.tgz",
|
||||
"integrity": "sha512-U5EcUB9Q2NQspCFwYGGNJm0h6wBCOv7T30QjndmvlawLkNt7S7KWbpWyxS9XBHSIKF57RgWjfxuJNTgTstpXxw==",
|
||||
"dependencies": {
|
||||
"@react-navigation/elements": "^1.3.31",
|
||||
"warn-once": "^0.1.0"
|
||||
|
|
19
package.json
19
package.json
|
@ -35,6 +35,7 @@
|
|||
"eslint-plugin-react": "^7.34.1",
|
||||
"eslint-plugin-react-native": "^4.1.0",
|
||||
"jest": "^29.4.2",
|
||||
"jest-environment-node": "^29.7.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"prettier": "^3.2.5",
|
||||
"react-test-renderer": "18.2.0",
|
||||
|
@ -57,7 +58,7 @@
|
|||
"postinstall": "rn-nodeify --install buffer,events,process,stream,inherits,path,assert,crypto --hack; npm run releasenotes2json; npm run branch2json; npm run patches",
|
||||
"patches": "patch -p1 < scripts/rn-ldk.patch; patch -p1 < scripts/react-native-camera-kit.patch;",
|
||||
"test": "npm run tslint && npm run lint && npm run unit && npm run jest",
|
||||
"jest": "jest -b tests/integration/*",
|
||||
"jest": "jest tests/integration/*",
|
||||
"e2e:debug-build": "detox build -c android.debug",
|
||||
"e2e:debug-test": "detox test -c android.debug -d 200000 -l info",
|
||||
"e2e:debug": "(test -f android/app/build/outputs/apk/debug/app-debug.apk && test -f android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk) || npm run e2e:debug-build; npm run e2e:debug-test",
|
||||
|
@ -67,9 +68,17 @@
|
|||
"lint": " npm run tslint && node scripts/find-unused-loc.js && eslint --ext .js,.ts,.tsx '*.@(js|ts|tsx)' screen 'blue_modules/*.@(js|ts|tsx)' class models loc tests components navigation typings",
|
||||
"lint:fix": "npm run lint -- --fix",
|
||||
"lint:quickfix": "git status --porcelain | grep -v '\\.json' | grep -E '\\.js|\\.ts' --color=never | awk '{print $2}' | xargs eslint --fix; exit 0",
|
||||
"unit": "jest -b -i tests/unit/*"
|
||||
"unit": "jest -b -w tests/unit/*"
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "<rootDir>/tests/custom-environment.js",
|
||||
"reporters": [
|
||||
"default",
|
||||
[
|
||||
"<rootDir>/tests/custom-reporter.js",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"preset": "react-native",
|
||||
"transform": {
|
||||
"^.+\\.(ts|tsx)$": "ts-jest"
|
||||
|
@ -108,7 +117,7 @@
|
|||
"@react-native/metro-config": "0.74.84",
|
||||
"@react-navigation/drawer": "6.7.2",
|
||||
"@react-navigation/native": "6.1.18",
|
||||
"@react-navigation/native-stack": "6.10.1",
|
||||
"@react-navigation/native-stack": "6.11.0",
|
||||
"@remobile/react-native-qrcode-local-image": "https://github.com/BlueWallet/react-native-qrcode-local-image",
|
||||
"@rneui/base": "4.0.0-rc.8",
|
||||
"@rneui/themed": "4.0.0-rc.8",
|
||||
|
@ -154,7 +163,7 @@
|
|||
"react-native-default-preference": "1.4.4",
|
||||
"react-native-device-info": "11.1.0",
|
||||
"react-native-document-picker": "https://github.com/BlueWallet/react-native-document-picker#6033c4e1b0dd0a6760b5f5a5a2c3b2e5d07f2ae4",
|
||||
"react-native-draggable-flatlist": "github:BlueWallet/react-native-draggable-flatlist#ebfddc4",
|
||||
"react-native-draggable-flatlist": "github:BlueWallet/react-native-draggable-flatlist#3061e30",
|
||||
"react-native-fs": "2.20.0",
|
||||
"react-native-gesture-handler": "2.17.1",
|
||||
"react-native-handoff": "https://github.com/BlueWallet/react-native-handoff#31d005f93d31099d0e564590a3bbd052b8a02b39",
|
||||
|
@ -184,7 +193,7 @@
|
|||
"react-native-vector-icons": "10.1.0",
|
||||
"react-native-watch-connectivity": "1.1.0",
|
||||
"readable-stream": "3.6.2",
|
||||
"realm": "12.11.1",
|
||||
"realm": "12.12.1",
|
||||
"rn-ldk": "github:BlueWallet/rn-ldk#v0.8.4",
|
||||
"rn-nodeify": "10.3.0",
|
||||
"scryptsy": "2.1.0",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState } from 'react';
|
||||
import { useRoute } from '@react-navigation/native';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useRoute, RouteProp } from '@react-navigation/native';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
import { ActivityIndicator, Keyboard, KeyboardAvoidingView, Linking, Platform, StyleSheet, TextInput, View } from 'react-native';
|
||||
|
||||
|
@ -23,6 +23,7 @@ import SafeArea from '../../components/SafeArea';
|
|||
import { useTheme } from '../../components/themes';
|
||||
import { scanQrHelper } from '../../helpers/scan-qr';
|
||||
import loc from '../../loc';
|
||||
import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList';
|
||||
|
||||
const BROADCAST_RESULT = Object.freeze({
|
||||
none: 'Input transaction hex',
|
||||
|
@ -31,12 +32,10 @@ const BROADCAST_RESULT = Object.freeze({
|
|||
error: 'error',
|
||||
});
|
||||
|
||||
interface SuccessScreenProps {
|
||||
tx: string;
|
||||
}
|
||||
type RouteProps = RouteProp<DetailViewStackParamList, 'Broadcast'>;
|
||||
|
||||
const Broadcast: React.FC = () => {
|
||||
const { name } = useRoute();
|
||||
const { name, params } = useRoute<RouteProps>();
|
||||
const [tx, setTx] = useState<string | undefined>();
|
||||
const [txHex, setTxHex] = useState<string | undefined>();
|
||||
const { colors } = useTheme();
|
||||
|
@ -50,6 +49,14 @@ const Broadcast: React.FC = () => {
|
|||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const scannedData = params?.scannedData;
|
||||
if (scannedData) {
|
||||
handleScannedData(scannedData);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [params?.scannedData]);
|
||||
|
||||
const handleUpdateTxHex = (nextValue: string) => setTxHex(nextValue.trim());
|
||||
|
||||
const handleBroadcast = async () => {
|
||||
|
@ -81,10 +88,7 @@ const Broadcast: React.FC = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const handleQRScan = async () => {
|
||||
const scannedData = await scanQrHelper(name);
|
||||
if (!scannedData) return;
|
||||
|
||||
const handleScannedData = (scannedData: string) => {
|
||||
if (scannedData.indexOf('+') === -1 && scannedData.indexOf('=') === -1 && scannedData.indexOf('=') === -1) {
|
||||
// this looks like NOT base64, so maybe its transaction's hex
|
||||
return handleUpdateTxHex(scannedData);
|
||||
|
@ -97,6 +101,10 @@ const Broadcast: React.FC = () => {
|
|||
} catch (e) {}
|
||||
};
|
||||
|
||||
const handleQRScan = () => {
|
||||
scanQrHelper(name, true, undefined, false);
|
||||
};
|
||||
|
||||
let status;
|
||||
switch (broadcastResult) {
|
||||
case BROADCAST_RESULT.none:
|
||||
|
@ -159,7 +167,7 @@ const Broadcast: React.FC = () => {
|
|||
);
|
||||
};
|
||||
|
||||
const SuccessScreen: React.FC<SuccessScreenProps> = ({ tx }) => {
|
||||
const SuccessScreen: React.FC<{ tx: string }> = ({ tx }) => {
|
||||
if (!tx) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -127,9 +127,13 @@ const ScanQRCode = () => {
|
|||
const data = decoder.toString();
|
||||
decoder = false; // nullify for future use (?)
|
||||
if (launchedBy) {
|
||||
navigation.navigate({ name: launchedBy, params: {}, merge: true });
|
||||
let merge = true;
|
||||
if (typeof onBarScanned !== 'function') {
|
||||
merge = false;
|
||||
}
|
||||
navigation.navigate({ name: launchedBy, params: { scannedData: data }, merge });
|
||||
}
|
||||
onBarScanned({ data });
|
||||
onBarScanned && onBarScanned({ data });
|
||||
} else {
|
||||
setUrTotal(100);
|
||||
setUrHave(Math.floor(decoder.estimatedPercentComplete() * 100));
|
||||
|
@ -176,9 +180,13 @@ const ScanQRCode = () => {
|
|||
data = Buffer.from(payload, 'hex').toString();
|
||||
}
|
||||
if (launchedBy) {
|
||||
navigation.navigate({ name: launchedBy, params: {}, merge: true });
|
||||
let merge = true;
|
||||
if (typeof onBarScanned !== 'function') {
|
||||
merge = false;
|
||||
}
|
||||
navigation.navigate({ name: launchedBy, params: { scannedData: data }, merge });
|
||||
}
|
||||
onBarScanned({ data });
|
||||
onBarScanned && onBarScanned({ data });
|
||||
} else {
|
||||
setAnimatedQRCodeData(animatedQRCodeData);
|
||||
}
|
||||
|
@ -237,11 +245,15 @@ const ScanQRCode = () => {
|
|||
try {
|
||||
const hex = Base43.decode(ret.data);
|
||||
bitcoin.Psbt.fromHex(hex); // if it doesnt throw - all good
|
||||
|
||||
const data = Buffer.from(hex, 'hex').toString('base64');
|
||||
if (launchedBy) {
|
||||
navigation.navigate({ name: launchedBy, params: {}, merge: true });
|
||||
let merge = true;
|
||||
if (typeof onBarScanned !== 'function') {
|
||||
merge = false;
|
||||
}
|
||||
navigation.navigate({ name: launchedBy, params: { scannedData: data }, merge });
|
||||
}
|
||||
onBarScanned({ data: Buffer.from(hex, 'hex').toString('base64') });
|
||||
onBarScanned && onBarScanned({ data });
|
||||
return;
|
||||
} catch (_) {}
|
||||
|
||||
|
@ -249,9 +261,13 @@ const ScanQRCode = () => {
|
|||
setIsLoading(true);
|
||||
try {
|
||||
if (launchedBy) {
|
||||
navigation.navigate({ name: launchedBy, params: {}, merge: true });
|
||||
let merge = true;
|
||||
if (typeof onBarScanned !== 'function') {
|
||||
merge = false;
|
||||
}
|
||||
navigation.navigate({ name: launchedBy, params: { scannedData: ret.data }, merge });
|
||||
}
|
||||
onBarScanned(ret.data);
|
||||
onBarScanned && onBarScanned(ret.data);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
@ -304,7 +320,11 @@ const ScanQRCode = () => {
|
|||
|
||||
const dismiss = () => {
|
||||
if (launchedBy) {
|
||||
navigation.navigate({ name: launchedBy, params: {}, merge: true });
|
||||
let merge = true;
|
||||
if (typeof onBarScanned !== 'function') {
|
||||
merge = false;
|
||||
}
|
||||
navigation.navigate({ name: launchedBy, params: {}, merge });
|
||||
} else {
|
||||
navigation.goBack();
|
||||
}
|
||||
|
|
|
@ -328,8 +328,8 @@ const CoinControl = () => {
|
|||
|
||||
const handleChoose = item => setOutput(item);
|
||||
|
||||
const handleUseCoin = u => {
|
||||
bottomModalRef.current?.dismiss();
|
||||
const handleUseCoin = async u => {
|
||||
await bottomModalRef.current?.dismiss();
|
||||
setOutput(null);
|
||||
navigation.pop();
|
||||
onUTXOChoose(u);
|
||||
|
@ -395,8 +395,6 @@ const CoinControl = () => {
|
|||
useEffect(() => {
|
||||
if (output) {
|
||||
bottomModalRef.current?.present();
|
||||
} else {
|
||||
bottomModalRef.current?.dismiss();
|
||||
}
|
||||
}, [output]);
|
||||
|
||||
|
@ -428,6 +426,7 @@ const CoinControl = () => {
|
|||
<Button testID="UseCoin" title={loc.cc.use_coin} onPress={() => handleUseCoin([output])} />
|
||||
</View>
|
||||
}
|
||||
footerDefaultMargins
|
||||
contentContainerStyle={[styles.modalContent, styles.modalMinHeight]}
|
||||
>
|
||||
{output && renderOutputModalContent()}
|
||||
|
@ -472,9 +471,8 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
modalContent: {
|
||||
padding: 22,
|
||||
paddingTop: 66,
|
||||
},
|
||||
modalMinHeight: Platform.OS === 'android' ? { minHeight: 420 } : {},
|
||||
modalMinHeight: Platform.OS === 'android' ? { minHeight: 500 } : {},
|
||||
empty: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
|
|
|
@ -145,7 +145,6 @@ const DrawerList: React.FC<DrawerListProps> = memo(({ navigation }) => {
|
|||
<Header leftText={loc.wallets.list_title} onNewWalletPress={onNewWalletPress} isDrawerList />
|
||||
<WalletsCarousel
|
||||
data={state.wallets}
|
||||
showNewWalletPanel
|
||||
extraData={[state.wallets]}
|
||||
onPress={handleClick}
|
||||
handleLongPress={handleLongPress}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useCallback, useEffect, useReducer, useRef } from 'react';
|
||||
import { useFocusEffect, useIsFocused, useRoute } from '@react-navigation/native';
|
||||
import { useFocusEffect, useIsFocused, useRoute, RouteProp } from '@react-navigation/native';
|
||||
import { findNodeHandle, Image, InteractionManager, SectionList, StyleSheet, Text, useWindowDimensions, View } from 'react-native';
|
||||
import A from '../../blue_modules/analytics';
|
||||
import BlueClipboard from '../../blue_modules/clipboard';
|
||||
|
@ -88,6 +88,7 @@ function reducer(state: WalletListState, action: WalletListAction) {
|
|||
}
|
||||
|
||||
type NavigationProps = NativeStackNavigationProp<DetailViewStackParamList, 'WalletsList'>;
|
||||
type RouteProps = RouteProp<DetailViewStackParamList, 'WalletsList'>;
|
||||
|
||||
const WalletsList: React.FC = () => {
|
||||
const [state, dispatch] = useReducer<React.Reducer<WalletListState, WalletListAction>>(reducer, initialState);
|
||||
|
@ -108,7 +109,8 @@ const WalletsList: React.FC = () => {
|
|||
const { colors, scanImage } = useTheme();
|
||||
const { navigate } = useExtendedNavigation<NavigationProps>();
|
||||
const isFocused = useIsFocused();
|
||||
const routeName = useRoute().name;
|
||||
const route = useRoute<RouteProps>();
|
||||
const routeName = route.name;
|
||||
const dataSource = getTransactions(undefined, 10);
|
||||
const walletsCount = useRef<number>(wallets.length);
|
||||
const walletActionButtonsRef = useRef<any>();
|
||||
|
@ -149,6 +151,14 @@ const WalletsList: React.FC = () => {
|
|||
walletsCount.current = wallets.length;
|
||||
}, [wallets]);
|
||||
|
||||
useEffect(() => {
|
||||
const scannedData = route.params?.scannedData;
|
||||
if (scannedData) {
|
||||
onBarScanned(scannedData);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [route.params?.scannedData]);
|
||||
|
||||
const verifyBalance = useCallback(() => {
|
||||
if (getBalance() !== 0) {
|
||||
A(A.ENUM.GOT_NONZERO_BALANCE);
|
||||
|
@ -253,7 +263,6 @@ const WalletsList: React.FC = () => {
|
|||
handleLongPress={handleLongPress}
|
||||
onMomentumScrollEnd={onSnapToItem}
|
||||
ref={walletsCarousel}
|
||||
showNewWalletPanel
|
||||
onNewWalletPress={handleClick}
|
||||
testID="WalletsList"
|
||||
horizontal
|
||||
|
@ -332,9 +341,8 @@ const WalletsList: React.FC = () => {
|
|||
};
|
||||
|
||||
const onScanButtonPressed = useCallback(() => {
|
||||
scanQrHelper(routeName).then(onBarScanned);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
scanQrHelper(routeName, true, undefined, false);
|
||||
}, [routeName]);
|
||||
|
||||
const onBarScanned = useCallback(
|
||||
(value: any) => {
|
||||
|
@ -381,7 +389,7 @@ const WalletsList: React.FC = () => {
|
|||
});
|
||||
break;
|
||||
case 2:
|
||||
scanQrHelper(routeName, true).then(data => onBarScanned(data));
|
||||
scanQrHelper(routeName, true, undefined, false);
|
||||
break;
|
||||
case 3:
|
||||
if (!isClipboardEmpty) {
|
||||
|
|
|
@ -180,7 +180,6 @@ const WalletsAddMultisigStep2 = () => {
|
|||
await saveToDisk();
|
||||
A(A.ENUM.CREATED_WALLET);
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
await dismissAllModals();
|
||||
navigateToWalletsList();
|
||||
};
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ const WalletsImport = () => {
|
|||
const route = useRoute();
|
||||
const label = route?.params?.label ?? '';
|
||||
const triggerImport = route?.params?.triggerImport ?? false;
|
||||
const scannedData = route?.params?.scannedData ?? '';
|
||||
const { isAdvancedModeEnabled } = useSettings();
|
||||
const [importText, setImportText] = useState(label);
|
||||
const [isToolbarVisibleForAndroid, setIsToolbarVisibleForAndroid] = useState(false);
|
||||
|
@ -76,7 +77,14 @@ const WalletsImport = () => {
|
|||
useEffect(() => {
|
||||
if (triggerImport) importButtonPressed();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
}, [triggerImport]);
|
||||
|
||||
useEffect(() => {
|
||||
if (scannedData) {
|
||||
onBarScanned(scannedData);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [scannedData]);
|
||||
|
||||
const importButtonPressed = () => {
|
||||
const textToImport = onBlur();
|
||||
|
@ -102,7 +110,6 @@ const WalletsImport = () => {
|
|||
screen: 'ScanQRCode',
|
||||
params: {
|
||||
launchedBy: route.name,
|
||||
onBarScanned,
|
||||
showFileImportButton: true,
|
||||
},
|
||||
}),
|
||||
|
|
17
tests/custom-environment.js
Normal file
17
tests/custom-environment.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
import NodeEnvironment from 'jest-environment-node';
|
||||
class CustomEnvironment extends NodeEnvironment {
|
||||
async handleTestEvent(event) {
|
||||
if (event.name === 'test_start') {
|
||||
if (!process.env.RETRY) return;
|
||||
|
||||
const fullName = (event.test.parent.name === 'ROOT_DESCRIBE_BLOCK' ? '' : event.test.parent.name + ' ') + event.test.name;
|
||||
const hash = require('crypto').createHash('md5').update(fullName).digest('hex');
|
||||
if (require('fs').existsSync(`/tmp/${hash}`)) {
|
||||
event.test.mode = 'skip';
|
||||
console.log('skipping as it previously passed on CI:', fullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CustomEnvironment;
|
34
tests/custom-reporter.js
Normal file
34
tests/custom-reporter.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* @fileOverview to combat flakiness of jest integration tests we implement a diy retry mechanism:
|
||||
* a custom reporter writes a lock file in /tmp for each successfull testcase.
|
||||
* then when a test suite is restarted, a custom environment checks if a testcase passed previously and
|
||||
* forcefully skips such test cases.
|
||||
*/
|
||||
class CustomReporter {
|
||||
constructor(globalConfig, reporterOptions, reporterContext) {
|
||||
this._globalConfig = globalConfig;
|
||||
this._options = reporterOptions;
|
||||
this._context = reporterContext;
|
||||
}
|
||||
|
||||
onTestCaseResult(test, testCaseResult) {
|
||||
if (!process.env.RETRY) return;
|
||||
|
||||
// since we cant distinguish several testcases in `it.each(...)`, we just ignore them so they will always run
|
||||
if (testCaseResult.fullName.includes('can fetch balance, transactions & utxo, disableBatching=')) return;
|
||||
if (testCaseResult.fullName.includes('BlueElectrum can do multiGetBalanceByAddress(), disableBatching=')) return;
|
||||
if (testCaseResult.fullName.includes('ElectrumClient can do multiGetHistoryByAddress(), disableBatching=')) return;
|
||||
if (testCaseResult.fullName.includes('ElectrumClient can do multiGetTransactionByTxid(), disableBatching=')) return;
|
||||
if (testCaseResult.fullName.includes('ElectrumClient can do multiGetHistoryByAddress() to obtain txhex, disableBatching=')) return;
|
||||
if (testCaseResult.fullName.includes('addresses for vout missing')) return;
|
||||
if (testCaseResult.fullName.includes('txdatas were coming back null from BlueElectrum because of high batchsize')) return;
|
||||
|
||||
const hash = require('crypto').createHash('md5').update(testCaseResult.fullName).digest('hex');
|
||||
if (testCaseResult.status === 'passed') {
|
||||
// marking testcase as passed in /tmp
|
||||
require('fs').writeFileSync(`/tmp/${hash}`, '1');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CustomReporter;
|
|
@ -16,19 +16,13 @@ import {
|
|||
/**
|
||||
* this testsuite is for test cases that require no wallets to be present
|
||||
*/
|
||||
|
||||
beforeAll(async () => {
|
||||
// reinstalling the app just for any case to clean up app's storage
|
||||
await device.launchApp({ delete: true });
|
||||
}, 300_000);
|
||||
|
||||
describe('BlueWallet UI Tests - no wallets', () => {
|
||||
it('selftest passes', async () => {
|
||||
const lockFile = '/tmp/travislock.' + hashIt('t1');
|
||||
if (process.env.TRAVIS) {
|
||||
if (require('fs').existsSync(lockFile)) return console.warn('skipping', JSON.stringify('t1'), 'as it previously passed on Travis');
|
||||
}
|
||||
await device.launchApp({ newInstance: true });
|
||||
await device.launchApp({ delete: true }); // reinstalling the app just for any case to clean up app's storage
|
||||
await waitFor(element(by.id('WalletsList')))
|
||||
.toBeVisible()
|
||||
.withTimeout(300 * 1000);
|
||||
|
@ -52,7 +46,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
|||
if (process.env.TRAVIS) {
|
||||
if (require('fs').existsSync(lockFile)) return console.warn('skipping', JSON.stringify('t2'), 'as it previously passed on Travis');
|
||||
}
|
||||
await device.launchApp({ newInstance: true });
|
||||
await device.launchApp({ delete: true }); // reinstalling the app just for any case to clean up app's storage
|
||||
await yo('WalletsList');
|
||||
|
||||
// go to settings, press SelfTest and wait for OK
|
||||
|
@ -190,7 +184,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
|||
if (process.env.TRAVIS) {
|
||||
if (require('fs').existsSync(lockFile)) return console.warn('skipping', JSON.stringify('t3'), 'as it previously passed on Travis');
|
||||
}
|
||||
await device.launchApp({ newInstance: true });
|
||||
await device.launchApp({ delete: true }); // reinstalling the app just for any case to clean up app's storage
|
||||
await yo('WalletsList');
|
||||
|
||||
await helperCreateWallet();
|
||||
|
@ -230,7 +224,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
|||
if (process.env.TRAVIS) {
|
||||
if (require('fs').existsSync(lockFile)) return console.warn('skipping', JSON.stringify('t4'), 'as it previously passed on Travis');
|
||||
}
|
||||
await device.launchApp({ newInstance: true });
|
||||
await device.launchApp({ delete: true }); // reinstalling the app just for any case to clean up app's storage
|
||||
await yo('WalletsList');
|
||||
|
||||
// lets create a wallet
|
||||
|
@ -384,7 +378,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
|||
if (process.env.TRAVIS) {
|
||||
if (require('fs').existsSync(lockFile)) return console.warn('skipping', JSON.stringify('t5'), 'as it previously passed on Travis');
|
||||
}
|
||||
await device.launchApp({ newInstance: true });
|
||||
await device.launchApp({ delete: true }); // reinstalling the app just for any case to clean up app's storage
|
||||
await yo('WalletsList');
|
||||
await helperCreateWallet();
|
||||
await element(by.id('SettingsButton')).tap();
|
||||
|
@ -461,7 +455,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
|||
if (process.env.TRAVIS) {
|
||||
if (require('fs').existsSync(lockFile)) return console.warn('skipping as it previously passed on Travis');
|
||||
}
|
||||
await device.launchApp({ newInstance: true });
|
||||
await device.launchApp({ delete: true }); // reinstalling the app just for any case to clean up app's storage
|
||||
await helperSwitchAdvancedMode();
|
||||
await yo('WalletsList');
|
||||
await element(by.id('WalletsList')).swipe('left', 'fast', 1); // in case emu screen is small and it doesnt fit
|
||||
|
@ -541,7 +535,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
|||
if (process.env.TRAVIS) {
|
||||
if (require('fs').existsSync(lockFile)) return console.warn('skipping', JSON.stringify('t6'), 'as it previously passed on Travis');
|
||||
}
|
||||
await device.launchApp({ newInstance: true });
|
||||
await device.launchApp({ delete: true }); // reinstalling the app just for any case to clean up app's storage
|
||||
await yo('WalletsList');
|
||||
await element(by.id('WalletsList')).swipe('left', 'fast', 1); // in case emu screen is small and it doesnt fit
|
||||
await sleep(200); // Wait until bounce animation finishes.
|
||||
|
@ -583,6 +577,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
|||
|
||||
await element(by.id('AddressInput')).replaceText('bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl');
|
||||
await element(by.id('BitcoinAmountInput')).typeText('0.0005\n');
|
||||
await element(by.id('BitcoinAmountInput')).tapReturnKey();
|
||||
|
||||
// setting fee rate:
|
||||
const feeRate = 3;
|
||||
|
@ -672,7 +667,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
|||
if (process.env.TRAVIS) {
|
||||
if (require('fs').existsSync(lockFile)) return console.warn('skipping', JSON.stringify('t6'), 'as it previously passed on Travis');
|
||||
}
|
||||
await device.launchApp({ newInstance: true });
|
||||
await device.launchApp({ delete: true }); // reinstalling the app just for any case to clean up app's storage
|
||||
await yo('WalletsList');
|
||||
|
||||
// enable AdvancedMode to see derivation path in wallet details
|
||||
|
|
|
@ -3,12 +3,13 @@ import * as bitcoin from 'bitcoinjs-lib';
|
|||
|
||||
import { extractTextFromElementById, hashIt, helperImportWallet, sleep, sup, yo } from './helperz';
|
||||
|
||||
let importedSuccessfully = false;
|
||||
|
||||
/**
|
||||
* in this suite each test requires that there is one specific wallet present, thus, we import it
|
||||
* before anything else.
|
||||
* we dont clean it up as we expect other test suites to do clean install of the app
|
||||
*/
|
||||
|
||||
beforeAll(async () => {
|
||||
if (!process.env.HD_MNEMONIC_BIP84) {
|
||||
console.error('process.env.HD_MNEMONIC_BIP84 not set, skipped');
|
||||
|
@ -20,6 +21,7 @@ beforeAll(async () => {
|
|||
console.log('before all - importing bip48...');
|
||||
await helperImportWallet(process.env.HD_MNEMONIC_BIP84, 'HDsegwitBech32', 'Imported HD SegWit (BIP84 Bech32 Native)', '0.00105526');
|
||||
console.log('...imported!');
|
||||
importedSuccessfully = true;
|
||||
await device.pressBack();
|
||||
await sleep(15000);
|
||||
}, 1200_000);
|
||||
|
@ -34,6 +36,8 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
|
|||
console.error('process.env.HD_MNEMONIC_BIP84 not set, skipped');
|
||||
return;
|
||||
}
|
||||
if (!importedSuccessfully) throw new Error('BIP84 was not imported during the setup');
|
||||
|
||||
await device.launchApp({ newInstance: true });
|
||||
|
||||
// go inside the wallet
|
||||
|
@ -183,6 +187,8 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
|
|||
console.error('process.env.HD_MNEMONIC_BIP84 not set, skipped');
|
||||
return;
|
||||
}
|
||||
if (!importedSuccessfully) throw new Error('BIP84 was not imported during the setup');
|
||||
|
||||
await device.launchApp({ newInstance: true });
|
||||
|
||||
// go inside the wallet
|
||||
|
@ -256,6 +262,8 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
|
|||
console.error('process.env.HD_MNEMONIC_BIP84 not set, skipped');
|
||||
return;
|
||||
}
|
||||
if (!importedSuccessfully) throw new Error('BIP84 was not imported during the setup');
|
||||
|
||||
await device.launchApp({ newInstance: true });
|
||||
|
||||
// go inside the wallet
|
||||
|
@ -325,6 +333,8 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
|
|||
console.error('process.env.HD_MNEMONIC_BIP84 not set, skipped');
|
||||
return;
|
||||
}
|
||||
if (!importedSuccessfully) throw new Error('BIP84 was not imported during the setup');
|
||||
|
||||
await device.launchApp({ newInstance: true });
|
||||
|
||||
// go inside the wallet
|
||||
|
@ -368,6 +378,8 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
|
|||
console.error('process.env.HD_MNEMONIC_BIP84 not set, skipped');
|
||||
return;
|
||||
}
|
||||
if (!importedSuccessfully) throw new Error('BIP84 was not imported during the setup');
|
||||
|
||||
await device.launchApp({ newInstance: true });
|
||||
|
||||
// go inside the wallet
|
||||
|
@ -417,6 +429,8 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
|
|||
console.error('process.env.HD_MNEMONIC_BIP84 not set, skipped');
|
||||
return;
|
||||
}
|
||||
if (!importedSuccessfully) throw new Error('BIP84 was not imported during the setup');
|
||||
|
||||
await device.launchApp({ newInstance: true });
|
||||
|
||||
await device.launchApp({
|
||||
|
@ -453,6 +467,8 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
|
|||
console.error('process.env.HD_MNEMONIC_BIP84 not set, skipped');
|
||||
return;
|
||||
}
|
||||
if (!importedSuccessfully) throw new Error('BIP84 was not imported during the setup');
|
||||
|
||||
await device.launchApp({ newInstance: true });
|
||||
// go inside the wallet
|
||||
await element(by.text('Imported HD SegWit (BIP84 Bech32 Native)')).tap();
|
||||
|
|
Loading…
Add table
Reference in a new issue