OPS: Upgrade RNav 7 (#7419)
This commit is contained in:
parent
632500b734
commit
d338f813cb
@ -47,6 +47,24 @@
|
||||
"device": "emulator",
|
||||
"app": "android.debug"
|
||||
},
|
||||
"android.debug.device": {
|
||||
"device": {
|
||||
"device": {
|
||||
"adbName": ".*"
|
||||
},
|
||||
"type": "android.attached"
|
||||
},
|
||||
"app": "android.debug"
|
||||
},
|
||||
"android.release.device": {
|
||||
"device": {
|
||||
"device": {
|
||||
"adbName": ".*"
|
||||
},
|
||||
"type": "android.attached"
|
||||
},
|
||||
"app": "android.release"
|
||||
},
|
||||
"android.release": {
|
||||
"device": "emulator",
|
||||
"app": "android.release"
|
||||
|
||||
17
App.tsx
17
App.tsx
@ -1,5 +1,3 @@
|
||||
import 'react-native-gesture-handler'; // should be on top
|
||||
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import React from 'react';
|
||||
import { useColorScheme } from 'react-native';
|
||||
@ -9,23 +7,26 @@ import { SettingsProvider } from './components/Context/SettingsProvider';
|
||||
import { BlueDarkTheme, BlueDefaultTheme } from './components/themes';
|
||||
import MasterView from './navigation/MasterView';
|
||||
import { navigationRef } from './NavigationService';
|
||||
import { useLogger } from '@react-navigation/devtools';
|
||||
import { StorageProvider } from './components/Context/StorageProvider';
|
||||
|
||||
const App = () => {
|
||||
const colorScheme = useColorScheme();
|
||||
|
||||
useLogger(navigationRef);
|
||||
|
||||
return (
|
||||
<LargeScreenProvider>
|
||||
<NavigationContainer ref={navigationRef} theme={colorScheme === 'dark' ? BlueDarkTheme : BlueDefaultTheme}>
|
||||
<SafeAreaProvider>
|
||||
<NavigationContainer ref={navigationRef} theme={colorScheme === 'dark' ? BlueDarkTheme : BlueDefaultTheme}>
|
||||
<SafeAreaProvider>
|
||||
<LargeScreenProvider>
|
||||
<StorageProvider>
|
||||
<SettingsProvider>
|
||||
<MasterView />
|
||||
</SettingsProvider>
|
||||
</StorageProvider>
|
||||
</SafeAreaProvider>
|
||||
</NavigationContainer>
|
||||
</LargeScreenProvider>
|
||||
</LargeScreenProvider>
|
||||
</SafeAreaProvider>
|
||||
</NavigationContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -82,6 +82,7 @@ export const BlueFormMultiInput = props => {
|
||||
multiline
|
||||
underlineColorAndroid="transparent"
|
||||
numberOfLines={4}
|
||||
editable={!props.editable}
|
||||
style={{
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 16,
|
||||
|
||||
@ -14,10 +14,6 @@ export function dispatch(action: NavigationAction) {
|
||||
}
|
||||
}
|
||||
|
||||
export function navigateToWalletsList() {
|
||||
navigate('WalletsList');
|
||||
}
|
||||
|
||||
export function reset() {
|
||||
if (navigationRef.isReady()) {
|
||||
navigationRef.current?.reset({
|
||||
|
||||
@ -19,10 +19,10 @@
|
||||
<uses-permission android:name="android.permission.ACTION_OPEN_DOCUMENT" />
|
||||
<uses-permission android:name="android.permission.ACTION_GET_CONTENT" />
|
||||
<uses-permission android:name="android.permission.ACTION_CREATE_DOCUMENT" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<uses-permission android:name="android.permission.ACTION_SCREEN_OFF"/>
|
||||
<uses-permission android:name="android.permission.ACTION_SCREEN_ON"/>
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
android:name=".MainApplication"
|
||||
|
||||
@ -4,6 +4,7 @@ import android.appwidget.AppWidgetManager
|
||||
import android.appwidget.AppWidgetProvider
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.work.WorkManager
|
||||
|
||||
class BitcoinPriceWidget : AppWidgetProvider() {
|
||||
@ -27,7 +28,8 @@ class BitcoinPriceWidget : AppWidgetProvider() {
|
||||
val sharedPref = context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
|
||||
val widgetCount = sharedPref.getInt(WIDGET_COUNT_KEY, 0)
|
||||
if (widgetCount >= 1) {
|
||||
Log.e(TAG, "Only one widget instance is allowed.")
|
||||
Toast.makeText(context, "Only one widget instance is allowed.", Toast.LENGTH_SHORT).show()
|
||||
Log.e(TAG, "Attempted to add multiple widget instances.")
|
||||
return
|
||||
}
|
||||
sharedPref.edit().putInt(WIDGET_COUNT_KEY, widgetCount + 1).apply()
|
||||
@ -37,9 +39,7 @@ class BitcoinPriceWidget : AppWidgetProvider() {
|
||||
|
||||
override fun onDisabled(context: Context) {
|
||||
super.onDisabled(context)
|
||||
val sharedPref = context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
|
||||
val widgetCount = sharedPref.getInt(WIDGET_COUNT_KEY, 1)
|
||||
sharedPref.edit().putInt(WIDGET_COUNT_KEY, widgetCount - 1).apply()
|
||||
clearWidgetCount(context)
|
||||
Log.d(TAG, "onDisabled called")
|
||||
clearCache(context)
|
||||
WorkManager.getInstance(context).cancelUniqueWork(WidgetUpdateWorker.WORK_NAME)
|
||||
@ -49,10 +49,17 @@ class BitcoinPriceWidget : AppWidgetProvider() {
|
||||
super.onDeleted(context, appWidgetIds)
|
||||
val sharedPref = context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
|
||||
val widgetCount = sharedPref.getInt(WIDGET_COUNT_KEY, 1)
|
||||
sharedPref.edit().putInt(WIDGET_COUNT_KEY, widgetCount - appWidgetIds.size).apply()
|
||||
val newCount = widgetCount - appWidgetIds.size
|
||||
sharedPref.edit().putInt(WIDGET_COUNT_KEY, if (newCount >= 0) newCount else 0).apply()
|
||||
Log.d(TAG, "onDeleted called for widgets: ${appWidgetIds.joinToString()}")
|
||||
}
|
||||
|
||||
private fun clearWidgetCount(context: Context) {
|
||||
val sharedPref = context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
|
||||
sharedPref.edit().putInt(WIDGET_COUNT_KEY, 0).apply()
|
||||
Log.d(TAG, "Widget count reset to 0")
|
||||
}
|
||||
|
||||
private fun clearCache(context: Context) {
|
||||
val sharedPref = context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
|
||||
sharedPref.edit().clear().apply()
|
||||
|
||||
@ -5,7 +5,7 @@ import { useTheme } from './themes';
|
||||
import ToolTipMenu from './TooltipMenu';
|
||||
import { CommonToolTipActions } from '../typings/CommonToolTipActions';
|
||||
import loc from '../loc';
|
||||
import { navigationRef } from '../NavigationService';
|
||||
import { useExtendedNavigation } from '../hooks/useExtendedNavigation';
|
||||
|
||||
type AddWalletButtonProps = {
|
||||
onPress?: (event: GestureResponderEvent) => void;
|
||||
@ -23,21 +23,25 @@ const styles = StyleSheet.create({
|
||||
|
||||
const AddWalletButton: React.FC<AddWalletButtonProps> = ({ onPress }) => {
|
||||
const { colors } = useTheme();
|
||||
const navigation = useExtendedNavigation();
|
||||
const stylesHook = StyleSheet.create({
|
||||
ball: {
|
||||
backgroundColor: colors.buttonBackgroundColor,
|
||||
},
|
||||
});
|
||||
|
||||
const onPressMenuItem = useCallback((action: string) => {
|
||||
switch (action) {
|
||||
case CommonToolTipActions.ImportWallet.id:
|
||||
navigationRef.current?.navigate('AddWalletRoot', { screen: 'ImportWallet' });
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}, []);
|
||||
const onPressMenuItem = useCallback(
|
||||
(action: string) => {
|
||||
switch (action) {
|
||||
case CommonToolTipActions.ImportWallet.id:
|
||||
navigation.navigate('AddWalletRoot', { screen: 'ImportWallet' });
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
[navigation],
|
||||
);
|
||||
|
||||
const actions = useMemo(() => [CommonToolTipActions.ImportWallet], []);
|
||||
|
||||
|
||||
@ -11,7 +11,6 @@ interface AddressInputProps {
|
||||
address?: string;
|
||||
placeholder?: string;
|
||||
onChangeText: (text: string) => void;
|
||||
scanButtonTapped?: () => void;
|
||||
editable?: boolean;
|
||||
inputAccessoryViewID?: string;
|
||||
onFocus?: () => void;
|
||||
@ -41,7 +40,6 @@ const AddressInput = ({
|
||||
testID = 'AddressInput',
|
||||
placeholder = loc.send.details_address,
|
||||
onChangeText,
|
||||
scanButtonTapped = () => {},
|
||||
editable = true,
|
||||
inputAccessoryViewID,
|
||||
onFocus = () => {},
|
||||
@ -104,7 +102,7 @@ const AddressInput = ({
|
||||
keyboardType={keyboardType}
|
||||
{...(skipValidation ? { onBlur } : { onBlur: onBlurEditing })}
|
||||
/>
|
||||
{editable ? <AddressInputScanButton isLoading={isLoading} scanButtonTapped={scanButtonTapped} onChangeText={onChangeText} /> : null}
|
||||
{editable ? <AddressInputScanButton isLoading={isLoading} onChangeText={onChangeText} /> : null}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@ -13,11 +13,14 @@ import { useExtendedNavigation } from '../hooks/useExtendedNavigation';
|
||||
|
||||
interface AddressInputScanButtonProps {
|
||||
isLoading: boolean;
|
||||
scanButtonTapped: () => void;
|
||||
onChangeText: (text: string) => void;
|
||||
}
|
||||
|
||||
export const AddressInputScanButton = ({ isLoading, scanButtonTapped, onChangeText }: AddressInputScanButtonProps) => {
|
||||
export const AddressInputScanButton = ({
|
||||
isLoading,
|
||||
|
||||
onChangeText,
|
||||
}: AddressInputScanButtonProps) => {
|
||||
const { colors } = useTheme();
|
||||
const { isClipboardGetContentEnabled } = useSettings();
|
||||
|
||||
@ -32,12 +35,11 @@ export const AddressInputScanButton = ({ isLoading, scanButtonTapped, onChangeTe
|
||||
});
|
||||
|
||||
const toolTipOnPress = useCallback(async () => {
|
||||
await scanButtonTapped();
|
||||
Keyboard.dismiss();
|
||||
navigation.navigate('ScanQRCode', {
|
||||
showFileImportButton: true,
|
||||
});
|
||||
}, [navigation, scanButtonTapped]);
|
||||
}, [navigation]);
|
||||
|
||||
const actions = useMemo(() => {
|
||||
const availableActions = [
|
||||
@ -57,7 +59,6 @@ export const AddressInputScanButton = ({ isLoading, scanButtonTapped, onChangeTe
|
||||
async (action: string) => {
|
||||
switch (action) {
|
||||
case CommonToolTipActions.ScanQR.id:
|
||||
scanButtonTapped();
|
||||
navigation.navigate('ScanQRCode', {
|
||||
showFileImportButton: true,
|
||||
});
|
||||
@ -124,7 +125,7 @@ export const AddressInputScanButton = ({ isLoading, scanButtonTapped, onChangeTe
|
||||
}
|
||||
Keyboard.dismiss();
|
||||
},
|
||||
[navigation, onChangeText, scanButtonTapped],
|
||||
[navigation, onChangeText],
|
||||
);
|
||||
|
||||
const buttonStyle = useMemo(() => [styles.scan, stylesHook.scan], [stylesHook.scan]);
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import 'react-native-gesture-handler'; // should be on top
|
||||
|
||||
import { CommonActions } from '@react-navigation/native';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { AppState, AppStateStatus, Linking } from 'react-native';
|
||||
|
||||
@ -351,10 +351,10 @@ export const StorageProvider = ({ children }: { children: React.ReactNode }) =>
|
||||
return;
|
||||
}
|
||||
const emptyWalletLabel = new LegacyWallet().getLabel();
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
if (w.getLabel() === emptyWalletLabel) w.setLabel(loc.wallets.import_imported + ' ' + w.typeReadable);
|
||||
w.setUserHasSavedExport(true);
|
||||
addWallet(w);
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
await saveToDisk();
|
||||
A(A.ENUM.CREATED_WALLET);
|
||||
presentAlert({
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useState, useRef, forwardRef, useImperativeHandle, useEffect } from 'react';
|
||||
import { View, Text, TextInput, StyleSheet, Animated, Easing, ViewStyle, Keyboard, Platform, UIManager, ScrollView } from 'react-native';
|
||||
import { View, Text, TextInput, StyleSheet, Animated, Easing, ViewStyle, Keyboard, Platform, UIManager } from 'react-native';
|
||||
import BottomModal, { BottomModalHandle } from './BottomModal';
|
||||
import { useTheme } from '../components/themes';
|
||||
import loc from '../loc';
|
||||
@ -43,11 +43,10 @@ const PromptPasswordConfirmationModal = forwardRef<PromptPasswordConfirmationMod
|
||||
const fadeInAnimation = useRef(new Animated.Value(0)).current;
|
||||
const scaleAnimation = useRef(new Animated.Value(1)).current;
|
||||
const shakeAnimation = useRef(new Animated.Value(0)).current;
|
||||
const explanationOpacity = useRef(new Animated.Value(1)).current; // New animated value for opacity
|
||||
const explanationOpacity = useRef(new Animated.Value(1)).current;
|
||||
const { colors } = useTheme();
|
||||
const passwordInputRef = useRef<TextInput>(null);
|
||||
const confirmPasswordInputRef = useRef<TextInput>(null);
|
||||
const scrollView = useRef<ScrollView>(null);
|
||||
const { isVisible } = useKeyboard();
|
||||
|
||||
const stylesHook = StyleSheet.create({
|
||||
@ -103,42 +102,43 @@ const PromptPasswordConfirmationModal = forwardRef<PromptPasswordConfirmationMod
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [modalType]);
|
||||
|
||||
const handleShakeAnimation = () => {
|
||||
const performShake = (shakeAnimRef: Animated.Value) => {
|
||||
Animated.sequence([
|
||||
Animated.timing(shakeAnimation, {
|
||||
Animated.timing(shakeAnimRef, {
|
||||
toValue: 10,
|
||||
duration: 100,
|
||||
easing: Easing.inOut(Easing.ease),
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
Animated.timing(shakeAnimation, {
|
||||
Animated.timing(shakeAnimRef, {
|
||||
toValue: -10,
|
||||
duration: 100,
|
||||
easing: Easing.inOut(Easing.ease),
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
Animated.timing(shakeAnimation, {
|
||||
Animated.timing(shakeAnimRef, {
|
||||
toValue: 5,
|
||||
duration: 100,
|
||||
easing: Easing.inOut(Easing.ease),
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
Animated.timing(shakeAnimation, {
|
||||
Animated.timing(shakeAnimRef, {
|
||||
toValue: -5,
|
||||
duration: 100,
|
||||
easing: Easing.inOut(Easing.ease),
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
Animated.timing(shakeAnimation, {
|
||||
Animated.timing(shakeAnimRef, {
|
||||
toValue: 0,
|
||||
duration: 100,
|
||||
easing: Easing.inOut(Easing.ease),
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
]).start(() => {
|
||||
confirmPasswordInputRef.current?.focus();
|
||||
confirmPasswordInputRef.current?.setNativeProps({ selection: { start: 0, end: confirmPassword.length } });
|
||||
});
|
||||
]).start();
|
||||
};
|
||||
|
||||
const handleShakeAnimation = () => {
|
||||
performShake(shakeAnimation);
|
||||
};
|
||||
|
||||
const handleSuccessAnimation = () => {
|
||||
@ -180,6 +180,17 @@ const PromptPasswordConfirmationModal = forwardRef<PromptPasswordConfirmationMod
|
||||
});
|
||||
};
|
||||
|
||||
const handleConfirmationFailure = () => {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
if (!isSuccess) handleShakeAnimation();
|
||||
onConfirmationFailure();
|
||||
};
|
||||
|
||||
const handleConfirmSuccess = () => {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
handleSuccessAnimation();
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
Keyboard.dismiss();
|
||||
setIsLoading(true);
|
||||
@ -189,37 +200,13 @@ const PromptPasswordConfirmationModal = forwardRef<PromptPasswordConfirmationMod
|
||||
if (modalType === MODAL_TYPES.CREATE_PASSWORD || modalType === MODAL_TYPES.CREATE_FAKE_STORAGE) {
|
||||
if (password === confirmPassword && password) {
|
||||
success = await onConfirmationSuccess(password);
|
||||
if (success) {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
handleSuccessAnimation();
|
||||
} else {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
onConfirmationFailure();
|
||||
if (!isSuccess) {
|
||||
// Prevent shake animation if success is detected
|
||||
handleShakeAnimation();
|
||||
}
|
||||
}
|
||||
success ? handleConfirmSuccess() : handleConfirmationFailure();
|
||||
} else {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
if (!isSuccess) {
|
||||
// Prevent shake animation if success is detected
|
||||
handleShakeAnimation();
|
||||
}
|
||||
handleConfirmationFailure();
|
||||
}
|
||||
} else if (modalType === MODAL_TYPES.ENTER_PASSWORD) {
|
||||
success = await onConfirmationSuccess(password);
|
||||
if (success) {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
handleSuccessAnimation();
|
||||
} else {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
if (!isSuccess) {
|
||||
// Prevent shake animation if success is detected
|
||||
handleShakeAnimation();
|
||||
}
|
||||
onConfirmationFailure();
|
||||
}
|
||||
success ? handleConfirmSuccess() : handleConfirmationFailure();
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false); // Ensure loading state is reset
|
||||
@ -258,17 +245,18 @@ const PromptPasswordConfirmationModal = forwardRef<PromptPasswordConfirmationMod
|
||||
onConfirmationFailure();
|
||||
};
|
||||
|
||||
const opacity = isVisible ? 0 : 1;
|
||||
return (
|
||||
<BottomModal
|
||||
ref={modalRef}
|
||||
onDismiss={onModalDismiss}
|
||||
onClose={onModalDismiss}
|
||||
grabber={false}
|
||||
showCloseButton={!isSuccess}
|
||||
onCloseModalPressed={handleCancel}
|
||||
backgroundColor={colors.modal}
|
||||
isGrabberVisible={!isSuccess}
|
||||
scrollRef={scrollView}
|
||||
dismissible={false}
|
||||
sizes={Platform.OS === 'ios' ? ['auto'] : [420, 'auto']}
|
||||
footer={
|
||||
!isSuccess ? (
|
||||
showExplanation && modalType === MODAL_TYPES.CREATE_PASSWORD ? (
|
||||
@ -281,16 +269,19 @@ const PromptPasswordConfirmationModal = forwardRef<PromptPasswordConfirmationMod
|
||||
/>
|
||||
</Animated.View>
|
||||
) : (
|
||||
<Animated.View style={[{ opacity: fadeOutAnimation, transform: [{ scale: scaleAnimation }] }, styles.feeModalFooter]}>
|
||||
{!isVisible && (
|
||||
<SecondButton
|
||||
title={isLoading ? '' : loc._.ok}
|
||||
onPress={handleSubmit}
|
||||
testID="OKButton"
|
||||
loading={isLoading}
|
||||
disabled={isLoading || !password || (modalType === MODAL_TYPES.CREATE_PASSWORD && !confirmPassword)}
|
||||
/>
|
||||
)}
|
||||
<Animated.View
|
||||
style={[
|
||||
{ opacity: isVisible ? opacity : fadeOutAnimation, transform: [{ scale: scaleAnimation }] },
|
||||
styles.feeModalFooterSpacing,
|
||||
]}
|
||||
>
|
||||
<SecondButton
|
||||
title={isLoading ? '' : loc._.ok}
|
||||
onPress={handleSubmit}
|
||||
testID="OKButton"
|
||||
loading={isLoading}
|
||||
disabled={isLoading || !password || (modalType === MODAL_TYPES.CREATE_PASSWORD && !confirmPassword)}
|
||||
/>
|
||||
</Animated.View>
|
||||
)
|
||||
) : null
|
||||
@ -301,14 +292,14 @@ const PromptPasswordConfirmationModal = forwardRef<PromptPasswordConfirmationMod
|
||||
{modalType === MODAL_TYPES.CREATE_PASSWORD && showExplanation && (
|
||||
<Animated.View style={{ opacity: explanationOpacity }}>
|
||||
<Text style={[styles.textLabel, stylesHook.feeModalLabel]}>{loc.settings.encrypt_storage_explanation_headline}</Text>
|
||||
<Animated.ScrollView style={styles.explanationScrollView} ref={scrollView}>
|
||||
<Text style={[styles.description, stylesHook.feeModalCustomText]}>
|
||||
<Animated.View>
|
||||
<Text style={[styles.description, stylesHook.feeModalCustomText]} maxFontSizeMultiplier={1.2}>
|
||||
{loc.settings.encrypt_storage_explanation_description_line1}
|
||||
</Text>
|
||||
<Text style={[styles.description, stylesHook.feeModalCustomText]}>
|
||||
<Text style={[styles.description, stylesHook.feeModalCustomText]} maxFontSizeMultiplier={1.2}>
|
||||
{loc.settings.encrypt_storage_explanation_description_line2}
|
||||
</Text>
|
||||
</Animated.ScrollView>
|
||||
</Animated.View>
|
||||
<View style={styles.feeModalFooter} />
|
||||
</Animated.View>
|
||||
)}
|
||||
@ -397,29 +388,30 @@ export default PromptPasswordConfirmationModal;
|
||||
const styles = StyleSheet.create({
|
||||
modalContent: {
|
||||
padding: 22,
|
||||
width: '100%', // Ensure modal content takes full width
|
||||
width: '100%',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
minHeight: {
|
||||
minHeight: 280,
|
||||
minHeight: 420,
|
||||
},
|
||||
feeModalFooter: {
|
||||
padding: 16,
|
||||
},
|
||||
feeModalFooterSpacing: {
|
||||
padding: 16,
|
||||
padding: 24,
|
||||
marginVertical: 24,
|
||||
},
|
||||
inputContainer: {
|
||||
marginBottom: 10,
|
||||
width: '100%', // Ensure full width
|
||||
width: '100%',
|
||||
},
|
||||
input: {
|
||||
borderRadius: 4,
|
||||
padding: 8,
|
||||
marginVertical: 8,
|
||||
fontSize: 16,
|
||||
width: '100%', // Ensure full width
|
||||
width: '100%',
|
||||
},
|
||||
textLabel: {
|
||||
fontSize: 20,
|
||||
@ -435,7 +427,8 @@ const styles = StyleSheet.create({
|
||||
successContainer: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: 100,
|
||||
margin: 24,
|
||||
marginBottom: 48,
|
||||
},
|
||||
circle: {
|
||||
width: 60,
|
||||
@ -449,7 +442,4 @@ const styles = StyleSheet.create({
|
||||
color: 'white',
|
||||
fontSize: 30,
|
||||
},
|
||||
explanationScrollView: {
|
||||
maxHeight: 200,
|
||||
},
|
||||
});
|
||||
|
||||
@ -109,8 +109,8 @@ const SelectFeeModal = forwardRef<BottomModalHandle, SelectFeeModalProps>(
|
||||
});
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
present: async () => feeModalRef.current?.present(),
|
||||
dismiss: async () => feeModalRef.current?.dismiss(),
|
||||
present: async () => await feeModalRef.current?.present(),
|
||||
dismiss: async () => await feeModalRef.current?.dismiss(),
|
||||
}));
|
||||
|
||||
const options: Option[] = [
|
||||
@ -163,8 +163,8 @@ const SelectFeeModal = forwardRef<BottomModalHandle, SelectFeeModalProps>(
|
||||
|
||||
const handleSelectOption = async (fee: number | null, rate: number) => {
|
||||
setFeePrecalc(fp => ({ ...fp, current: fee }));
|
||||
await feeModalRef.current?.dismiss();
|
||||
setCustomFee(rate.toString());
|
||||
await feeModalRef.current?.dismiss();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { NativeStackNavigationOptions } from '@react-navigation/native-stack';
|
||||
import React from 'react';
|
||||
import { Image, Keyboard, StyleSheet, TouchableOpacity } from 'react-native';
|
||||
import { Image, Keyboard, Platform, StyleSheet, TouchableOpacity } from 'react-native';
|
||||
|
||||
import loc from '../loc';
|
||||
import { Theme } from './themes';
|
||||
@ -59,7 +59,7 @@ const navigationStyle = (
|
||||
{
|
||||
closeButtonPosition,
|
||||
onCloseButtonPressed,
|
||||
headerBackVisible = true,
|
||||
headerBackVisible = Platform.OS === 'ios' || !closeButtonPosition,
|
||||
...opts
|
||||
}: NativeStackNavigationOptions & {
|
||||
closeButtonPosition?: CloseButtonPosition;
|
||||
@ -78,11 +78,6 @@ const navigationStyle = (
|
||||
let headerRight;
|
||||
let headerLeft;
|
||||
|
||||
if (!headerBackVisible) {
|
||||
headerLeft = () => <></>;
|
||||
opts.headerLeft = headerLeft;
|
||||
}
|
||||
|
||||
if (closeButton === CloseButtonPosition.Right) {
|
||||
headerRight = () => (
|
||||
<TouchableOpacity
|
||||
@ -115,7 +110,9 @@ const navigationStyle = (
|
||||
fontWeight: '600',
|
||||
color: theme.colors.foregroundColor,
|
||||
},
|
||||
headerBackTitleVisible: false,
|
||||
headerBackVisible,
|
||||
headerBackTitle: undefined,
|
||||
headerBackButtonDisplayMode: 'minimal',
|
||||
headerTintColor: theme.colors.foregroundColor,
|
||||
headerRight,
|
||||
headerLeft,
|
||||
|
||||
1
gesture-handler.js
Normal file
1
gesture-handler.js
Normal file
@ -0,0 +1 @@
|
||||
// Don't import react-native-gesture-handler on web
|
||||
2
gesture-handler.native.js
Normal file
2
gesture-handler.native.js
Normal file
@ -0,0 +1,2 @@
|
||||
// Only import react-native-gesture-handler on native platforms
|
||||
import 'react-native-gesture-handler';
|
||||
@ -22,8 +22,25 @@ export const useExtendedNavigation = <T extends NavigationProp<ParamListBase>>()
|
||||
const { wallets, saveToDisk } = useStorage();
|
||||
const { isBiometricUseEnabled } = useBiometrics();
|
||||
|
||||
const enhancedNavigate: NavigationProp<ParamListBase>['navigate'] = useCallback(
|
||||
(screenOrOptions: any, params?: any, options?: { merge?: boolean }) => {
|
||||
const enhancedNavigate = useCallback(
|
||||
(
|
||||
...args:
|
||||
| [string]
|
||||
| [string, object | undefined]
|
||||
| [string, object | undefined, { merge?: boolean }]
|
||||
| [{ name: string; params?: object; path?: string; merge?: boolean }]
|
||||
) => {
|
||||
let screenOrOptions: any;
|
||||
let params: any;
|
||||
let options: { merge?: boolean } | undefined;
|
||||
|
||||
if (typeof args[0] === 'string') {
|
||||
screenOrOptions = args[0];
|
||||
params = args[1];
|
||||
options = args[2];
|
||||
} else {
|
||||
screenOrOptions = args[0];
|
||||
}
|
||||
let screenName: string;
|
||||
if (typeof screenOrOptions === 'string') {
|
||||
screenName = screenOrOptions;
|
||||
|
||||
1
index.js
1
index.js
@ -1,3 +1,4 @@
|
||||
import './gesture-handler';
|
||||
import './shim.js';
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
@ -162,7 +162,7 @@
|
||||
B4D0B2682C1DED67006B6B1B /* ReceiveMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4D0B2672C1DED67006B6B1B /* ReceiveMethod.swift */; };
|
||||
B4EE583C226703320003363C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B40D4E35225841ED00428FCC /* Assets.xcassets */; };
|
||||
B4EFF73B2C3F6C5E0095D655 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4EFF73A2C3F6C5E0095D655 /* MockData.swift */; };
|
||||
C978A716948AB7DEC5B6F677 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; };
|
||||
C978A716948AB7DEC5B6F677 /* (null) in Frameworks */ = {isa = PBXBuildFile; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -427,7 +427,7 @@
|
||||
files = (
|
||||
782F075B5DD048449E2DECE9 /* libz.tbd in Frameworks */,
|
||||
764B49B1420D4AEB8109BF62 /* libsqlite3.0.tbd in Frameworks */,
|
||||
C978A716948AB7DEC5B6F677 /* BuildFile in Frameworks */,
|
||||
C978A716948AB7DEC5B6F677 /* (null) in Frameworks */,
|
||||
17CDA0718F42DB2CE856C872 /* libPods-BlueWallet.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -1471,7 +1471,7 @@
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.finance";
|
||||
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = io.bluewallet.bluewallet;
|
||||
INFOPLIST_KEY_WKExtensionDelegateClassName = "$(PRODUCT_BUNDLE_IDENTIFIER).ExtensionDelegate";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@ -1529,7 +1529,7 @@
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.finance";
|
||||
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = io.bluewallet.bluewallet;
|
||||
INFOPLIST_KEY_WKExtensionDelegateClassName = "$(PRODUCT_BUNDLE_IDENTIFIER).ExtensionDelegate";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
||||
@ -1297,7 +1297,7 @@ PODS:
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- react-native-image-picker (7.2.2):
|
||||
- react-native-image-picker (7.2.3):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
@ -1324,7 +1324,7 @@ PODS:
|
||||
- React
|
||||
- react-native-randombytes (3.6.1):
|
||||
- React-Core
|
||||
- react-native-safe-area-context (4.14.1):
|
||||
- react-native-safe-area-context (5.2.0):
|
||||
- React-Core
|
||||
- react-native-screen-capture (0.2.3):
|
||||
- React
|
||||
@ -1621,7 +1621,7 @@ PODS:
|
||||
- React-logger (= 0.76.7)
|
||||
- React-perflogger (= 0.76.7)
|
||||
- React-utils (= 0.76.7)
|
||||
- ReactNativeCameraKit (14.1.0):
|
||||
- ReactNativeCameraKit (14.2.0):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
@ -1656,7 +1656,7 @@ PODS:
|
||||
- React-Core
|
||||
- RNFS (2.20.0):
|
||||
- React-Core
|
||||
- RNGestureHandler (2.22.1):
|
||||
- RNGestureHandler (2.23.1):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
@ -1702,7 +1702,7 @@ PODS:
|
||||
- Yoga
|
||||
- RNLocalize (3.4.1):
|
||||
- React-Core
|
||||
- RNPermissions (5.2.4):
|
||||
- RNPermissions (5.2.5):
|
||||
- React-Core
|
||||
- RNQrGenerator (1.4.3):
|
||||
- React
|
||||
@ -1819,7 +1819,7 @@ PODS:
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- RNScreens (3.35.0):
|
||||
- RNScreens (4.6.0):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
@ -2258,11 +2258,11 @@ SPEC CHECKSUMS:
|
||||
react-native-blue-crypto: de5babd59b17fbf3fc31d2e1e5d59ec859093fbc
|
||||
react-native-bw-file-access: fe925b77dbf48500df0b294c6851f8c84607a203
|
||||
react-native-document-picker: 530879d9e89b490f0954bcc4ab697c5b5e35d659
|
||||
react-native-image-picker: 19a8d8471a239890675726f88f9c18dd213656d5
|
||||
react-native-image-picker: 130fad649d07e4eec8faaed361d3bba570e1e5ff
|
||||
react-native-ios-context-menu: 986da6dcba70094bcc2a8049f68410fe7d25aff1
|
||||
react-native-menu: 74230a5879e0ca697e98ee7c3087297dc774bf06
|
||||
react-native-randombytes: 3c8f3e89d12487fd03a2f966c288d495415fc116
|
||||
react-native-safe-area-context: 758e894ca5a9bd1868d2a9cfbca7326a2b6bf9dc
|
||||
react-native-safe-area-context: 3e33e7c43c8b74dba436a5a32651cb8d7064c740
|
||||
react-native-screen-capture: 7b6121f529681ed2fde36cdedadd0bb39e9a3796
|
||||
react-native-secure-key-store: eb45b44bdec3f48e9be5cdfca0f49ddf64892ea6
|
||||
react-native-tcp-socket: 61379457d7e702e83e28c213b6e085ac079e480f
|
||||
@ -2294,7 +2294,7 @@ SPEC CHECKSUMS:
|
||||
React-utils: 0342746d2cf989cf5e0d1b84c98cfa152edbdf3f
|
||||
ReactCodegen: e1c019dc68733dd2c5d3b263b4a6dc72002c0045
|
||||
ReactCommon: 81e0744ee33adfd6d586141b927024f488bc49ea
|
||||
ReactNativeCameraKit: e72b838dac4ea2da19b7eb5d00b23125072790fd
|
||||
ReactNativeCameraKit: 72cc60b69ae192fe55a3e3f294ed46833308bb22
|
||||
RealmJS: 9fd51c849eb552ade9f7b11db42a319b4f6cab4c
|
||||
RNCAsyncStorage: c91d753ede6dc21862c4922cd13f98f7cfde578e
|
||||
RNCClipboard: ee059e6006b137e369caed5eb852b4aad9f5d886
|
||||
@ -2302,17 +2302,17 @@ SPEC CHECKSUMS:
|
||||
RNDefaultPreference: 8a089ee8ce829a66c5453e3c5434f0785499d1c3
|
||||
RNDeviceInfo: 801c18d0525e86580900e7b5f562dca0c8c05021
|
||||
RNFS: 89de7d7f4c0f6bafa05343c578f61118c8282ed8
|
||||
RNGestureHandler: e597c3cf95ce53819280c34d008d63bfbb05ddeb
|
||||
RNGestureHandler: 79e731e77dbec11fb5508e646d13897f1ee45912
|
||||
RNHandoff: bc8af5a86853ff13b033e7ba1114c3c5b38e6385
|
||||
RNKeychain: 4df48b5186ca2b6a99f5ead69ad587154e084a32
|
||||
RNLocalize: 15463c4d79c7da45230064b4adcf5e9bb984667e
|
||||
RNPermissions: 257cd3630c304d59877aeb36f885c21543adde41
|
||||
RNPermissions: a19629bb6d6fdb521540f39262e356ae30d6cc4e
|
||||
RNQrGenerator: afacf12b55dfba0e3aaca963eec23691e8426431
|
||||
RNQuickAction: c2c8f379e614428be0babe4d53a575739667744d
|
||||
RNRate: 7641919330e0d6688ad885a985b4bd697ed7d14c
|
||||
RNReactNativeHapticFeedback: 00ba111b82aa266bb3ee1aa576831c2ea9a9dfad
|
||||
RNReanimated: 66cf0f600a26d2b5e74c6e0b1c77c1ab1f62fc05
|
||||
RNScreens: 35bb8e81aeccf111baa0ea01a54231390dbbcfd9
|
||||
RNScreens: b05d3b8e716e68d9e2f1364d440d23de5b6885f1
|
||||
RNShare: 6204e6a1987ba3e7c47071ef703e5449a0e3548a
|
||||
RNSVG: 86fecdfc637614ba9def63f7f3f2e7795e018356
|
||||
RNVectorIcons: 182892e7d1a2f27b52d3c627eca5d2665a22ee28
|
||||
|
||||
@ -182,6 +182,7 @@
|
||||
"input_total": "Total:",
|
||||
"permission_camera_message": "We need your permission to use your camera.",
|
||||
"psbt_sign": "Sign a transaction",
|
||||
"invalid_psbt": "Invalid PSBT provided.",
|
||||
"open_settings": "Open Settings",
|
||||
"permission_storage_denied_message": "BlueWallet is unable to save this file. Please open your device settings and enable Storage Permission.",
|
||||
"permission_storage_title": "Storage Access Permission",
|
||||
|
||||
@ -58,6 +58,7 @@ export type AddWalletStackParamList = {
|
||||
n: number;
|
||||
walletLabel: string;
|
||||
format: string;
|
||||
onBarScanned?: string;
|
||||
};
|
||||
WalletsAddMultisigHelp: undefined;
|
||||
ScanQRCode: ScanQRCodeParamList;
|
||||
@ -139,7 +140,7 @@ const AddWalletStack = () => {
|
||||
backgroundColor: '#0070FF',
|
||||
},
|
||||
headerTintColor: '#FFFFFF',
|
||||
headerBackTitleVisible: false,
|
||||
headerBackTitle: undefined,
|
||||
statusBarStyle: 'light',
|
||||
headerShadowVisible: false,
|
||||
})(theme)}
|
||||
|
||||
@ -26,7 +26,7 @@ import WalletDetails from '../screen/wallets/WalletDetails';
|
||||
import GenerateWord from '../screen/wallets/generateWord';
|
||||
import SelectWallet from '../screen/wallets/SelectWallet';
|
||||
import WalletsList from '../screen/wallets/WalletsList';
|
||||
import { NavigationDefaultOptions, NavigationFormModalOptions, StatusBarLightOptions, DetailViewStack } from './index'; // Importing the navigator
|
||||
import { NavigationDefaultOptions, StatusBarLightOptions, DetailViewStack } from './index'; // Importing the navigator
|
||||
import AddWalletStack from './AddWalletStack';
|
||||
import AztecoRedeemStackRoot from './AztecoRedeemStack';
|
||||
import PaymentCodesListComponent from './LazyLoadPaymentCodeStack';
|
||||
@ -65,12 +65,14 @@ import ReleaseNotes from '../screen/settings/ReleaseNotes';
|
||||
import ToolsScreen from '../screen/settings/tools';
|
||||
import SettingsPrivacy from '../screen/settings/SettingsPrivacy';
|
||||
import { ScanQRCodeComponent } from './LazyLoadScanQRCodeStack';
|
||||
import { useIsLargeScreen } from '../hooks/useIsLargeScreen';
|
||||
|
||||
const DetailViewStackScreensStack = () => {
|
||||
const theme = useTheme();
|
||||
const navigation = useExtendedNavigation();
|
||||
const { wallets } = useStorage();
|
||||
const { isTotalBalanceEnabled } = useSettings();
|
||||
const { isLargeScreen } = useIsLargeScreen();
|
||||
|
||||
const DetailButton = useMemo(() => <HeaderRightButton testID="DetailButton" disabled={true} title={loc.send.create_details} />, []);
|
||||
|
||||
@ -79,14 +81,17 @@ const DetailViewStackScreensStack = () => {
|
||||
}, [navigation]);
|
||||
|
||||
const RightBarButtons = useMemo(
|
||||
() => (
|
||||
<>
|
||||
<AddWalletButton onPress={navigateToAddWallet} />
|
||||
<View style={styles.width24} />
|
||||
() =>
|
||||
isLargeScreen ? (
|
||||
<SettingsButton />
|
||||
</>
|
||||
),
|
||||
[navigateToAddWallet],
|
||||
) : (
|
||||
<>
|
||||
<AddWalletButton onPress={navigateToAddWallet} />
|
||||
<View style={styles.width24} />
|
||||
<SettingsButton />
|
||||
</>
|
||||
),
|
||||
[isLargeScreen, navigateToAddWallet],
|
||||
);
|
||||
|
||||
const useWalletListScreenOptions = useMemo<NativeStackNavigationOptions>(() => {
|
||||
@ -146,9 +151,9 @@ const DetailViewStackScreensStack = () => {
|
||||
headerStyle: {
|
||||
backgroundColor: theme.colors.customHeader,
|
||||
},
|
||||
headerBackTitle: undefined,
|
||||
headerRight: () => DetailButton,
|
||||
headerBackTitleStyle: { fontSize: 0 },
|
||||
headerBackTitleVisible: true,
|
||||
})(theme)}
|
||||
/>
|
||||
<DetailViewStack.Screen name="CPFP" component={CPFP} options={navigationStyle({ title: loc.transactions.cpfp_title })(theme)} />
|
||||
@ -243,15 +248,14 @@ const DetailViewStackScreensStack = () => {
|
||||
component={WalletAddresses}
|
||||
options={navigationStyle({ title: loc.addresses.addresses_title, statusBarStyle: 'auto' })(theme)}
|
||||
/>
|
||||
|
||||
<DetailViewStack.Screen
|
||||
name="Settings"
|
||||
component={Settings}
|
||||
options={navigationStyle({
|
||||
headerTransparent: true,
|
||||
title: loc.settings.header,
|
||||
// workaround to deal with the flicker when headerBackTitleVisible is false
|
||||
headerBackTitleStyle: { fontSize: 0 },
|
||||
headerBackTitleVisible: true,
|
||||
headerBackButtonDisplayMode: 'default',
|
||||
headerShadowVisible: false,
|
||||
headerLargeTitle: true,
|
||||
animationTypeForReplace: 'push',
|
||||
@ -320,29 +324,12 @@ const DetailViewStackScreensStack = () => {
|
||||
component={SettingsPrivacy}
|
||||
options={navigationStyle({ title: loc.settings.privacy })(theme)}
|
||||
/>
|
||||
<DetailViewStack.Screen
|
||||
name="ScanQRCode"
|
||||
component={ScanQRCodeComponent}
|
||||
options={navigationStyle({
|
||||
headerShown: false,
|
||||
statusBarHidden: true,
|
||||
presentation: 'fullScreenModal',
|
||||
headerShadowVisible: false,
|
||||
})(theme)}
|
||||
/>
|
||||
<DetailViewStack.Screen
|
||||
name="ViewEditMultisigCosignersRoot"
|
||||
component={ViewEditMultisigCosignersStackRoot}
|
||||
options={{ ...NavigationDefaultOptions, ...StatusBarLightOptions, gestureEnabled: false, fullScreenGestureEnabled: false }}
|
||||
initialParams={{ walletID: undefined, cosigners: undefined }}
|
||||
/>
|
||||
|
||||
<DetailViewStack.Screen
|
||||
name="AddWalletRoot"
|
||||
component={AddWalletStack}
|
||||
options={navigationStyle({ closeButtonPosition: CloseButtonPosition.Left, ...NavigationFormModalOptions })(theme)}
|
||||
options={navigationStyle({ closeButtonPosition: CloseButtonPosition.Left, ...NavigationDefaultOptions })(theme)}
|
||||
/>
|
||||
<DetailViewStack.Screen name="SendDetailsRoot" component={SendDetailsStack} options={NavigationFormModalOptions} />
|
||||
<DetailViewStack.Screen name="SendDetailsRoot" component={SendDetailsStack} options={NavigationDefaultOptions} />
|
||||
<DetailViewStack.Screen name="LNDCreateInvoiceRoot" component={LNDCreateInvoiceRoot} options={NavigationDefaultOptions} />
|
||||
<DetailViewStack.Screen name="ScanLndInvoiceRoot" component={ScanLndInvoiceRoot} options={NavigationDefaultOptions} />
|
||||
<DetailViewStack.Screen name="AztecoRedeemRoot" component={AztecoRedeemStackRoot} options={NavigationDefaultOptions} />
|
||||
@ -357,6 +344,13 @@ const DetailViewStackScreensStack = () => {
|
||||
component={ExportMultisigCoordinationSetupStack}
|
||||
options={NavigationDefaultOptions}
|
||||
/>
|
||||
|
||||
<DetailViewStack.Screen
|
||||
name="ViewEditMultisigCosignersRoot"
|
||||
component={ViewEditMultisigCosignersStackRoot}
|
||||
options={{ ...NavigationDefaultOptions, ...StatusBarLightOptions, gestureEnabled: false, fullScreenGestureEnabled: false }}
|
||||
initialParams={{ walletID: undefined, cosigners: undefined }}
|
||||
/>
|
||||
<DetailViewStack.Screen
|
||||
name="WalletXpubRoot"
|
||||
component={WalletXpubStackRoot}
|
||||
@ -379,6 +373,16 @@ const DetailViewStackScreensStack = () => {
|
||||
statusBarStyle: 'auto',
|
||||
})(theme)}
|
||||
/>
|
||||
<DetailViewStack.Screen
|
||||
name="ScanQRCode"
|
||||
component={ScanQRCodeComponent}
|
||||
options={navigationStyle({
|
||||
headerShown: false,
|
||||
statusBarHidden: true,
|
||||
presentation: 'fullScreenModal',
|
||||
headerShadowVisible: false,
|
||||
})(theme)}
|
||||
/>
|
||||
</DetailViewStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
@ -45,11 +45,7 @@ export type DetailViewStackParamList = {
|
||||
Success: undefined;
|
||||
WalletAddresses: { walletID: string };
|
||||
AddWalletRoot: undefined;
|
||||
SendDetailsRoot: {
|
||||
screen: string;
|
||||
params: SendDetailsParams;
|
||||
merge: boolean;
|
||||
};
|
||||
SendDetailsRoot: SendDetailsParams;
|
||||
LNDCreateInvoiceRoot: undefined;
|
||||
ScanLndInvoiceRoot: {
|
||||
screen: string;
|
||||
|
||||
@ -33,11 +33,7 @@ const DrawerRoot = () => {
|
||||
|
||||
return (
|
||||
<Drawer.Navigator screenOptions={drawerStyle} drawerContent={DrawerListContent}>
|
||||
<Drawer.Screen
|
||||
name="DetailViewStackScreensStack"
|
||||
component={DetailViewStackScreensStack}
|
||||
options={{ headerShown: false, gestureHandlerProps: { enableTrackpadTwoFingerGesture: false } }}
|
||||
/>
|
||||
<Drawer.Screen name="DetailViewStackScreensStack" component={DetailViewStackScreensStack} options={{ headerShown: false }} />
|
||||
</Drawer.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
import 'react-native-gesture-handler'; // should be on top
|
||||
|
||||
import React, { lazy, Suspense } from 'react';
|
||||
import MainRoot from '../navigation';
|
||||
import { useStorage } from '../hooks/context/useStorage';
|
||||
import DevMenu from '../components/DevMenu';
|
||||
import MainRoot from './index';
|
||||
const CompanionDelegates = lazy(() => import('../components/CompanionDelegates'));
|
||||
|
||||
const MasterView = () => {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Psbt } from 'bitcoinjs-lib';
|
||||
import { CreateTransactionTarget, CreateTransactionUtxo } from '../class/wallets/types';
|
||||
import { CreateTransactionTarget, CreateTransactionUtxo, TWallet } from '../class/wallets/types';
|
||||
import { BitcoinUnit, Chain } from '../models/bitcoinUnits';
|
||||
import { ScanQRCodeParamList } from './DetailViewStackParamList';
|
||||
|
||||
@ -21,6 +21,7 @@ export type SendDetailsParams = {
|
||||
utxos?: CreateTransactionUtxo[] | null;
|
||||
isEditable?: boolean;
|
||||
uri?: string;
|
||||
paymentCode?: string;
|
||||
addRecipientParams?: {
|
||||
address: string;
|
||||
amount?: number;
|
||||
@ -77,13 +78,18 @@ export type SendDetailsStackParamList = {
|
||||
txid?: string;
|
||||
};
|
||||
SelectWallet: {
|
||||
chainType: Chain;
|
||||
chainType?: Chain;
|
||||
onWalletSelect?: (wallet: TWallet, navigation: any) => void;
|
||||
availableWallets?: TWallet[];
|
||||
noWalletExplanationText?: string;
|
||||
onChainRequireSend?: boolean;
|
||||
};
|
||||
CoinControl: {
|
||||
walletID: string;
|
||||
};
|
||||
PaymentCodeList: {
|
||||
walletID: string;
|
||||
merge?: boolean;
|
||||
};
|
||||
ScanQRCode: ScanQRCodeParamList;
|
||||
};
|
||||
|
||||
@ -29,7 +29,6 @@ const getTransactionStatusOptions = ({ route, navigation, theme }: GetTransactio
|
||||
backgroundColor: theme.colors.customHeader,
|
||||
},
|
||||
headerBackTitleStyle: { fontSize: 0 },
|
||||
headerBackTitleVisible: true,
|
||||
statusBarStyle: 'auto',
|
||||
})(theme),
|
||||
headerRight: () => (
|
||||
|
||||
@ -10,7 +10,7 @@ import { RouteProp } from '@react-navigation/native';
|
||||
export type WalletTransactionsRouteProps = RouteProp<DetailViewStackParamList, 'WalletTransactions'>;
|
||||
|
||||
const getWalletTransactionsOptions = ({ route }: { route: WalletTransactionsRouteProps }): NativeStackNavigationOptions => {
|
||||
const { isLoading, walletID, walletType } = route.params;
|
||||
const { isLoading = false, walletID, walletType } = route.params;
|
||||
|
||||
const onPress = () => {
|
||||
navigationRef.navigate('WalletDetails', {
|
||||
@ -34,7 +34,7 @@ const getWalletTransactionsOptions = ({ route }: { route: WalletTransactionsRout
|
||||
},
|
||||
headerShadowVisible: false,
|
||||
headerTintColor: '#FFFFFF',
|
||||
headerBackTitleVisible: true,
|
||||
headerBackTitle: undefined,
|
||||
headerRight: () => RightButton,
|
||||
};
|
||||
};
|
||||
|
||||
642
package-lock.json
generated
642
package-lock.json
generated
@ -19,16 +19,17 @@
|
||||
"@noble/secp256k1": "1.6.3",
|
||||
"@react-native-async-storage/async-storage": "2.1.0",
|
||||
"@react-native-clipboard/clipboard": "1.16.1",
|
||||
"@react-native-community/cli": "15.1.3",
|
||||
"@react-native-community/cli-platform-android": "15.1.3",
|
||||
"@react-native-community/cli-platform-ios": "15.1.3",
|
||||
"@react-native-community/cli": "15.0.1",
|
||||
"@react-native-community/cli-platform-android": "15.0.1",
|
||||
"@react-native-community/cli-platform-ios": "15.0.1",
|
||||
"@react-native-community/push-notification-ios": "1.11.0",
|
||||
"@react-native-menu/menu": "https://github.com/BlueWallet/menu.git#14bab79",
|
||||
"@react-native/gradle-plugin": "0.76.7",
|
||||
"@react-native/metro-config": "0.76.7",
|
||||
"@react-navigation/drawer": "6.7.2",
|
||||
"@react-navigation/native": "6.1.18",
|
||||
"@react-navigation/native-stack": "6.11.0",
|
||||
"@react-navigation/devtools": "7.0.15",
|
||||
"@react-navigation/drawer": "7.1.1",
|
||||
"@react-navigation/native": "7.0.14",
|
||||
"@react-navigation/native-stack": "7.2.0",
|
||||
"@rneui/base": "4.0.0-rc.8",
|
||||
"@rneui/themed": "4.0.0-rc.8",
|
||||
"@spsina/bip47": "github:BlueWallet/bip47#df82345",
|
||||
@ -61,27 +62,27 @@
|
||||
"payjoin-client": "1.0.1",
|
||||
"process": "0.11.10",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
"react": "18.2.0",
|
||||
"react-localization": "github:BlueWallet/react-localization#ae7969a",
|
||||
"react-native": "0.76.7",
|
||||
"react-native-biometrics": "3.0.1",
|
||||
"react-native-blue-crypto": "github:BlueWallet/react-native-blue-crypto#3cb5442",
|
||||
"react-native-camera-kit": "14.1.0",
|
||||
"react-native-camera-kit": "14.2.0",
|
||||
"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.2",
|
||||
"react-native-document-picker": "9.3.1",
|
||||
"react-native-draglist": "github:BlueWallet/react-native-draglist#a4af02f",
|
||||
"react-native-fs": "2.20.0",
|
||||
"react-native-gesture-handler": "2.22.1",
|
||||
"react-native-gesture-handler": "2.23.1",
|
||||
"react-native-handoff": "github:BlueWallet/react-native-handoff#v0.0.4",
|
||||
"react-native-haptic-feedback": "2.3.3",
|
||||
"react-native-image-picker": "7.2.2",
|
||||
"react-native-image-picker": "7.2.3",
|
||||
"react-native-ios-context-menu": "github:BlueWallet/react-native-ios-context-menu#e5c1217cd220bfab6e6d9a7c65838545082e3f8e",
|
||||
"react-native-keychain": "9.1.0",
|
||||
"react-native-linear-gradient": "2.8.3",
|
||||
"react-native-localize": "3.4.1",
|
||||
"react-native-permissions": "5.2.4",
|
||||
"react-native-permissions": "5.2.5",
|
||||
"react-native-prompt-android": "github:BlueWallet/react-native-prompt-android#ed168d66fed556bc2ed07cf498770f058b78a376",
|
||||
"react-native-push-notification": "8.1.1",
|
||||
"react-native-qrcode-svg": "6.3.12",
|
||||
@ -89,15 +90,16 @@
|
||||
"react-native-randombytes": "3.6.1",
|
||||
"react-native-rate": "1.2.12",
|
||||
"react-native-reanimated": "3.16.7",
|
||||
"react-native-safe-area-context": "4.14.1",
|
||||
"react-native-safe-area-context": "5.2.0",
|
||||
"react-native-screen-capture": "github:BlueWallet/react-native-screen-capture#18cb79f",
|
||||
"react-native-screens": "3.35.0",
|
||||
"react-native-screens": "4.6.0",
|
||||
"react-native-secure-key-store": "github:BlueWallet/react-native-secure-key-store#2076b4849e88aa0a78e08bfbb4ce3923e0925cbc",
|
||||
"react-native-share": "11.1.0",
|
||||
"react-native-svg": "15.11.1",
|
||||
"react-native-tcp-socket": "6.2.0",
|
||||
"react-native-vector-icons": "10.2.0",
|
||||
"react-native-watch-connectivity": "1.1.0",
|
||||
"react-test-renderer": "18.2.0",
|
||||
"readable-stream": "3.6.2",
|
||||
"realm": "20.1.0",
|
||||
"rn-nodeify": "10.3.0",
|
||||
@ -119,13 +121,14 @@
|
||||
"@react-native/js-polyfills": "^0.76.7",
|
||||
"@react-native/metro-babel-transformer": "^0.76.7",
|
||||
"@react-native/typescript-config": "0.76.7",
|
||||
"@testing-library/react-native": "^13.0.1",
|
||||
"@types/bip38": "^3.1.2",
|
||||
"@types/bs58check": "^2.1.0",
|
||||
"@types/create-hash": "^1.2.2",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/jest": "^29.5.2",
|
||||
"@types/react": "^18.2.16",
|
||||
"@types/react-test-renderer": "^18.0.0",
|
||||
"@types/react-test-renderer": "^19.0.0",
|
||||
"@types/wif": "^2.0.5",
|
||||
"@typescript-eslint/eslint-plugin": "^7.15.0",
|
||||
"@typescript-eslint/parser": "^7.15.0",
|
||||
@ -147,7 +150,6 @@
|
||||
"metro-react-native-babel-preset": "0.76.8",
|
||||
"node-fetch": "^2.6.7",
|
||||
"prettier": "^3.2.5",
|
||||
"react-test-renderer": "18.3.1",
|
||||
"ts-jest": "^29.1.1",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
@ -4843,18 +4845,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli": {
|
||||
"version": "15.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-15.1.3.tgz",
|
||||
"integrity": "sha512-+ih/WYUkJsEV2CMAnOHvVoSIz/Ahg5UJk+sqSIOmY79mWAglQzfLP71o7b0neJCnJWLmWiO6G6/S+kmULefD5g==",
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-15.0.1.tgz",
|
||||
"integrity": "sha512-xIGPytx2bj5HxFk0c7S25AVuJowHmEFg5LFC9XosKc0TSOjP1r6zGC6OqC/arQV/pNuqmZN2IFnpgJn0Bn+hhQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-native-community/cli-clean": "15.1.3",
|
||||
"@react-native-community/cli-config": "15.1.3",
|
||||
"@react-native-community/cli-debugger-ui": "15.1.3",
|
||||
"@react-native-community/cli-doctor": "15.1.3",
|
||||
"@react-native-community/cli-server-api": "15.1.3",
|
||||
"@react-native-community/cli-tools": "15.1.3",
|
||||
"@react-native-community/cli-types": "15.1.3",
|
||||
"@react-native-community/cli-clean": "15.0.1",
|
||||
"@react-native-community/cli-config": "15.0.1",
|
||||
"@react-native-community/cli-debugger-ui": "15.0.1",
|
||||
"@react-native-community/cli-doctor": "15.0.1",
|
||||
"@react-native-community/cli-server-api": "15.0.1",
|
||||
"@react-native-community/cli-tools": "15.0.1",
|
||||
"@react-native-community/cli-types": "15.0.1",
|
||||
"chalk": "^4.1.2",
|
||||
"commander": "^9.4.1",
|
||||
"deepmerge": "^4.3.0",
|
||||
@ -4873,12 +4875,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli-clean": {
|
||||
"version": "15.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-15.1.3.tgz",
|
||||
"integrity": "sha512-3s9NGapIkONFoCUN2s77NYI987GPSCdr74rTf0TWyGIDf4vTYgKoWKKR+Ml3VTa1BCj51r4cYuHEKE1pjUSc0w==",
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-15.0.1.tgz",
|
||||
"integrity": "sha512-flGTfT005UZvW2LAXVowZ/7ri22oiiZE4pPgMvc8klRxO5uofKIRuohgiHybHtiCo/HNqIz45JmZJvuFrhc4Ow==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-native-community/cli-tools": "15.1.3",
|
||||
"@react-native-community/cli-tools": "15.0.1",
|
||||
"chalk": "^4.1.2",
|
||||
"execa": "^5.0.0",
|
||||
"fast-glob": "^3.3.2"
|
||||
@ -4955,12 +4957,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli-config": {
|
||||
"version": "15.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-15.1.3.tgz",
|
||||
"integrity": "sha512-fJ9MrWp+/SszEVg5Wja8A57Whl5EfjRCHWFNkvFBtfjVUfi2hWvSTW3VBxzJuCHnPIIwpQafwjEgOrIRUI8y6w==",
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-15.0.1.tgz",
|
||||
"integrity": "sha512-SL3/9zIyzQQPKWei0+W1gNHxCPurrxqpODUWnVLoP38DNcvYCGtsRayw/4DsXgprZfBC+FsscNpd3IDJrG59XA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-native-community/cli-tools": "15.1.3",
|
||||
"@react-native-community/cli-tools": "15.0.1",
|
||||
"chalk": "^4.1.2",
|
||||
"cosmiconfig": "^9.0.0",
|
||||
"deepmerge": "^4.3.0",
|
||||
@ -4968,95 +4970,13 @@
|
||||
"joi": "^17.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli-config-android": {
|
||||
"version": "15.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-config-android/-/cli-config-android-15.1.3.tgz",
|
||||
"integrity": "sha512-v9okV/WQfMJEWRddI0S6no2v9Lvk54KgFkw1mvMYhJKVqloCNsIWzoqme0u7zIuYSzwsjXUQXVlGiDzbbwdkBw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-native-community/cli-tools": "15.1.3",
|
||||
"chalk": "^4.1.2",
|
||||
"fast-glob": "^3.3.2",
|
||||
"fast-xml-parser": "^4.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli-config-android/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli-config-android/node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli-config-android/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli-config-android/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@react-native-community/cli-config-android/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli-config-android/node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli-config-apple": {
|
||||
"version": "15.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-config-apple/-/cli-config-apple-15.1.3.tgz",
|
||||
"integrity": "sha512-Qv6jaEaycv+7s8wR9l9bdpIeSNFCeVANfGCX1x76SgOmGfZNIa7J3l1HaeF/5ktERMYsw/hm4u3rUn4Ks0YV1g==",
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-config-apple/-/cli-config-apple-15.0.1.tgz",
|
||||
"integrity": "sha512-GEHUx4NRp9W9or6vygn0TgNeFkcJdNjrtko0vQEJAS4gJdWqP/9LqqwJNlUfaW5jHBN7TKALAMlfRmI12Op3sg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-native-community/cli-tools": "15.1.3",
|
||||
"@react-native-community/cli-tools": "15.0.1",
|
||||
"chalk": "^4.1.2",
|
||||
"execa": "^5.0.0",
|
||||
"fast-glob": "^3.3.2"
|
||||
@ -5203,25 +5123,25 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli-debugger-ui": {
|
||||
"version": "15.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-15.1.3.tgz",
|
||||
"integrity": "sha512-m+fb9iAUNb9WiDdokCBLh0InJvollcgAM3gLjCT8DGTP6bH/jxtZ3DszzyIRqN9cMamItVrvDM0vkIg48xK7rQ==",
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-15.0.1.tgz",
|
||||
"integrity": "sha512-xkT2TLS8zg5r7Vl9l/2f7JVUoFECnVBS+B5ivrSu2PNZhKkr9lRmJFxC9aVLFb5lIxQQKNDvEyiIDNfP7wjJiA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"serve-static": "^1.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli-doctor": {
|
||||
"version": "15.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-15.1.3.tgz",
|
||||
"integrity": "sha512-WC9rawobuITAtJjyZ68E1M0geRt+b9A2CGB354L/tQp+XMKobGGVI4Y0DsattK83Wdg59GPyldE8C0Wevfgm/A==",
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-15.0.1.tgz",
|
||||
"integrity": "sha512-YCu44lZR3zZxJJYVTqYZFz9cT9KBfbKI4q2MnKOvkamt00XY3usooMqfuwBAdvM/yvpx7M5w8kbM/nPyj4YCvQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-native-community/cli-config": "15.1.3",
|
||||
"@react-native-community/cli-platform-android": "15.1.3",
|
||||
"@react-native-community/cli-platform-apple": "15.1.3",
|
||||
"@react-native-community/cli-platform-ios": "15.1.3",
|
||||
"@react-native-community/cli-tools": "15.1.3",
|
||||
"@react-native-community/cli-config": "15.0.1",
|
||||
"@react-native-community/cli-platform-android": "15.0.1",
|
||||
"@react-native-community/cli-platform-apple": "15.0.1",
|
||||
"@react-native-community/cli-platform-ios": "15.0.1",
|
||||
"@react-native-community/cli-tools": "15.0.1",
|
||||
"chalk": "^4.1.2",
|
||||
"command-exists": "^1.2.8",
|
||||
"deepmerge": "^4.3.0",
|
||||
@ -5294,9 +5214,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli-doctor/node_modules/semver": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
|
||||
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz",
|
||||
"integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@ -5318,15 +5238,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli-platform-android": {
|
||||
"version": "15.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-15.1.3.tgz",
|
||||
"integrity": "sha512-ZwrBK0UK4DRHoQm2v5m8+PlNHBK5gmibBU6rqNFLo4aZJ2Rufalbv3SF+DukgSyoI9/kI8UVrzSNj17e+HhN5A==",
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-15.0.1.tgz",
|
||||
"integrity": "sha512-QlAMomj6H6TY6pHwjTYMsHDQLP5eLzjAmyW1qb03w/kyS/72elK2bjsklNWJrscFY9TMQLqw7qoAsXf1m5t/dg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-native-community/cli-config-android": "15.1.3",
|
||||
"@react-native-community/cli-tools": "15.1.3",
|
||||
"@react-native-community/cli-tools": "15.0.1",
|
||||
"chalk": "^4.1.2",
|
||||
"execa": "^5.0.0",
|
||||
"fast-glob": "^3.3.2",
|
||||
"fast-xml-parser": "^4.4.1",
|
||||
"logkitty": "^0.7.1"
|
||||
}
|
||||
},
|
||||
@ -5401,13 +5322,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli-platform-apple": {
|
||||
"version": "15.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-apple/-/cli-platform-apple-15.1.3.tgz",
|
||||
"integrity": "sha512-awotqCGVcTdeRmTlE3wlsZgNxZUDGojUhPYOVMKejgdCzNM2bvzF8fqhETH2sc64Hbw/tQJg8pYeD4MZR0bHxw==",
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-apple/-/cli-platform-apple-15.0.1.tgz",
|
||||
"integrity": "sha512-iQj1Dt2fr/Q7X2CQhyhWnece3eLDCark1osfiwpViksOfTH2WdpNS3lIwlFcIKhsieFU7YYwbNuFqQ3tF9Dlvw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-native-community/cli-config-apple": "15.1.3",
|
||||
"@react-native-community/cli-tools": "15.1.3",
|
||||
"@react-native-community/cli-config-apple": "15.0.1",
|
||||
"@react-native-community/cli-tools": "15.0.1",
|
||||
"chalk": "^4.1.2",
|
||||
"execa": "^5.0.0",
|
||||
"fast-xml-parser": "^4.4.1"
|
||||
@ -5484,22 +5405,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli-platform-ios": {
|
||||
"version": "15.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-15.1.3.tgz",
|
||||
"integrity": "sha512-DxM8GYkqjrlGuuGiGjrcvUmKC2xv9zDzHbsc4+S160Nn0zbF2OH1DhMlnIuUeCmQnAO6QFMqU99O120iEzCAug==",
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-15.0.1.tgz",
|
||||
"integrity": "sha512-6pKzXEIgGL20eE1uOn8iSsNBlMzO1LG+pQOk+7mvD172EPhKm/lRzUVDX5gO/2jvsGoNw6VUW0JX1FI2firwqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-native-community/cli-platform-apple": "15.1.3"
|
||||
"@react-native-community/cli-platform-apple": "15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli-server-api": {
|
||||
"version": "15.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-15.1.3.tgz",
|
||||
"integrity": "sha512-kXZ0evedluLt6flWQiI3JqwnW8rSBspOoQ7JVTQYiG5lDHAeL3Om9PjAyiQBg1EZRMjiWZDV7nDxhR+m+6NX5Q==",
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-15.0.1.tgz",
|
||||
"integrity": "sha512-f3rb3t1ELLaMSX5/LWO/IykglBIgiP3+pPnyl8GphHnBpf3bdIcp7fHlHLemvHE06YxT2nANRxRPjy1gNskenA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-native-community/cli-debugger-ui": "15.1.3",
|
||||
"@react-native-community/cli-tools": "15.1.3",
|
||||
"@react-native-community/cli-debugger-ui": "15.0.1",
|
||||
"@react-native-community/cli-tools": "15.0.1",
|
||||
"compression": "^1.7.1",
|
||||
"connect": "^3.6.5",
|
||||
"errorhandler": "^1.5.1",
|
||||
@ -5510,9 +5431,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli-tools": {
|
||||
"version": "15.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-15.1.3.tgz",
|
||||
"integrity": "sha512-2RzoUKR+Y03ijBeeSoCSQihyN6dxy3fbHjraO4MheZZUzt/Yd1VMEDd0R5aa6rtiRDoknbHt45RL2GMa8MSaEA==",
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-15.0.1.tgz",
|
||||
"integrity": "sha512-N79A+u/94roanfmNohVcNGu6Xg+0idh63JHZFLC9OJJuZwTifGMLDfSTHZATpR1J7rebozQ5ClcSUePavErnSg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"appdirsjs": "^1.2.4",
|
||||
@ -5587,9 +5508,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli-tools/node_modules/semver": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
|
||||
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz",
|
||||
"integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@ -5611,9 +5532,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/cli-types": {
|
||||
"version": "15.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-15.1.3.tgz",
|
||||
"integrity": "sha512-0ZaM8LMsa7z7swBN+ObL2ysc6aA3AdS698Q6uEukym6RyX1uLbiofUdlvFSMpWSEL3D8f9OTymmL4RkCH8k6dw==",
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-15.0.1.tgz",
|
||||
"integrity": "sha512-sWiJ62kkGu2mgYni2dsPxOMBzpwTjNsDH1ubY4mqcNEI9Zmzs0vRwwDUEhYqwNGys9+KpBKoZRrT2PAlhO84xA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"joi": "^17.2.1"
|
||||
@ -6332,94 +6253,126 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-navigation/core": {
|
||||
"version": "6.4.17",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-6.4.17.tgz",
|
||||
"integrity": "sha512-Nd76EpomzChWAosGqWOYE3ItayhDzIEzzZsT7PfGcRFDgW5miHV2t4MZcq9YIK4tzxZjVVpYbIynOOQQd1e0Cg==",
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.3.1.tgz",
|
||||
"integrity": "sha512-S3KCGvNsoqVk8ErAtQI2EAhg9185lahF5OY01ofrrD4Ij/uk3QEHHjoGQhR5l5DXSCSKr1JbMQA7MEKMsBiWZA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-navigation/routers": "^6.1.9",
|
||||
"@react-navigation/routers": "^7.1.2",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"nanoid": "^3.1.23",
|
||||
"nanoid": "3.3.8",
|
||||
"query-string": "^7.1.3",
|
||||
"react-is": "^16.13.0",
|
||||
"use-latest-callback": "^0.2.1"
|
||||
"react-is": "^18.2.0",
|
||||
"use-latest-callback": "^0.2.1",
|
||||
"use-sync-external-store": "^1.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*"
|
||||
"react": ">= 18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-navigation/core/node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@react-navigation/devtools": {
|
||||
"version": "7.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/devtools/-/devtools-7.0.15.tgz",
|
||||
"integrity": "sha512-pxEBVtd6e5ocT7bs6k6ghOJNyb9Fzxm+EYHemHQ53GEin1sQKYpsSHWZEJdFj1cxYp+/+KCT+TueuNDFkJOr4Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"nanoid": "3.3.8",
|
||||
"stacktrace-parser": "^0.1.10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-navigation/drawer": {
|
||||
"version": "6.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/drawer/-/drawer-6.7.2.tgz",
|
||||
"integrity": "sha512-o4g2zgTZa2+oLd+8V33etrSM38KIqu8S/zCBTsdsHUoQyVE7JNRiv3Qgq/jMvEb8PZCqWmm7jHItcgzrBuwyOQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/drawer/-/drawer-7.1.1.tgz",
|
||||
"integrity": "sha512-34UqRS5OLFaNXPs5ocz3Du9c7em0P7fFMPYCZn/MxadDzQ4Mn/74pmJczmiyvyvz8vcWsNRbZ3Qswm0Dv6z60w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-navigation/elements": "^1.3.31",
|
||||
"@react-navigation/elements": "^2.2.5",
|
||||
"color": "^4.2.3",
|
||||
"warn-once": "^0.1.0"
|
||||
"react-native-drawer-layout": "^4.1.1",
|
||||
"use-latest-callback": "^0.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^6.0.0",
|
||||
"react": "*",
|
||||
"@react-navigation/native": "^7.0.14",
|
||||
"react": ">= 18.2.0",
|
||||
"react-native": "*",
|
||||
"react-native-gesture-handler": ">= 1.0.0",
|
||||
"react-native-reanimated": ">= 1.0.0",
|
||||
"react-native-safe-area-context": ">= 3.0.0",
|
||||
"react-native-screens": ">= 3.0.0"
|
||||
"react-native-gesture-handler": ">= 2.0.0",
|
||||
"react-native-reanimated": ">= 2.0.0",
|
||||
"react-native-safe-area-context": ">= 4.0.0",
|
||||
"react-native-screens": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-navigation/elements": {
|
||||
"version": "1.3.31",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.31.tgz",
|
||||
"integrity": "sha512-bUzP4Awlljx5RKEExw8WYtif8EuQni2glDaieYROKTnaxsu9kEIA515sXQgUDZU4Ob12VoL7+z70uO3qrlfXcQ==",
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.2.5.tgz",
|
||||
"integrity": "sha512-sDhE+W14P7MNWLMxXg1MEVXwkLUpMZJGflE6nQNzLmolJQIHgcia0Mrm8uRa3bQovhxYu1UzEojLZ+caoZt7Fg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color": "^4.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^6.0.0",
|
||||
"react": "*",
|
||||
"@react-native-masked-view/masked-view": ">= 0.2.0",
|
||||
"@react-navigation/native": "^7.0.14",
|
||||
"react": ">= 18.2.0",
|
||||
"react-native": "*",
|
||||
"react-native-safe-area-context": ">= 3.0.0"
|
||||
"react-native-safe-area-context": ">= 4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@react-native-masked-view/masked-view": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@react-navigation/native": {
|
||||
"version": "6.1.18",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.18.tgz",
|
||||
"integrity": "sha512-mIT9MiL/vMm4eirLcmw2h6h/Nm5FICtnYSdohq4vTLA2FF/6PNhByM7s8ffqoVfE5L0uAa6Xda1B7oddolUiGg==",
|
||||
"version": "7.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.0.14.tgz",
|
||||
"integrity": "sha512-Gi6lLw4VOGSWAhmUdJOMauOKGK51/YA1CprjXm91sNfgERWvznqEMw8QmUQx9SEqYfi0LfZhbzpMst09SJ00lw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-navigation/core": "^6.4.17",
|
||||
"@react-navigation/core": "^7.3.1",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"nanoid": "^3.1.23"
|
||||
"nanoid": "3.3.8",
|
||||
"use-latest-callback": "^0.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react": ">= 18.2.0",
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-navigation/native-stack": {
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-6.11.0.tgz",
|
||||
"integrity": "sha512-U5EcUB9Q2NQspCFwYGGNJm0h6wBCOv7T30QjndmvlawLkNt7S7KWbpWyxS9XBHSIKF57RgWjfxuJNTgTstpXxw==",
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.2.0.tgz",
|
||||
"integrity": "sha512-mw7Nq9qQrGsmJmCTwIIWB7yY/3tWYXvQswx+HJScGAadIjemvytJXm1fcl3+YZ9T9Ym0aERcVe5kDs+ny3X4vA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-navigation/elements": "^1.3.31",
|
||||
"warn-once": "^0.1.0"
|
||||
"@react-navigation/elements": "^2.2.5",
|
||||
"warn-once": "^0.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^6.0.0",
|
||||
"react": "*",
|
||||
"@react-navigation/native": "^7.0.14",
|
||||
"react": ">= 18.2.0",
|
||||
"react-native": "*",
|
||||
"react-native-safe-area-context": ">= 3.0.0",
|
||||
"react-native-screens": ">= 3.0.0"
|
||||
"react-native-safe-area-context": ">= 4.0.0",
|
||||
"react-native-screens": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-navigation/routers": {
|
||||
"version": "6.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-6.1.9.tgz",
|
||||
"integrity": "sha512-lTM8gSFHSfkJvQkxacGM6VJtBt61ip2XO54aNfswD+KMw6eeZ4oehl7m0me3CR9hnDE4+60iAZR8sAhvCiI3NA==",
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.1.2.tgz",
|
||||
"integrity": "sha512-emdEjpVDK8zbiu2GChC8oYIAub9i/OpNuQJekVsbyFCBz4/TzaBzms38Q53YaNhdIFNmiYLfHv/Y1Ub7KYfm3w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.1.23"
|
||||
"nanoid": "3.3.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@realm/fetch": {
|
||||
@ -6544,6 +6497,144 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/react-native": {
|
||||
"version": "13.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/react-native/-/react-native-13.0.1.tgz",
|
||||
"integrity": "sha512-sKMRNNniSOZ68qe1OBQAWvK87WCEmbfLp/MXfn2JE3x3WrNU8OFCVL0z/YKqw0/JO/d44J8Wq6FmRSaod/+VAg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "^4.1.2",
|
||||
"jest-matcher-utils": "^29.7.0",
|
||||
"pretty-format": "^29.7.0",
|
||||
"redent": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"jest": ">=29.0.0",
|
||||
"react": ">=18.2.0",
|
||||
"react-native": ">=0.71",
|
||||
"react-test-renderer": ">=18.2.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"jest": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/react-native/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/react-native/node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/react-native/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/react-native/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@testing-library/react-native/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/react-native/node_modules/pretty-format": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
|
||||
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jest/schemas": "^29.6.3",
|
||||
"ansi-styles": "^5.0.0",
|
||||
"react-is": "^18.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/react-native/node_modules/pretty-format/node_modules/ansi-styles": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
|
||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/react-native/node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@testing-library/react-native/node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||
@ -6790,9 +6881,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-test-renderer": {
|
||||
"version": "18.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.3.0.tgz",
|
||||
"integrity": "sha512-HW4MuEYxfDbOHQsVlY/XtOvNHftCVEPhJF2pQXXwcUiUF+Oyb0usgp48HSgpK5rt8m9KZb22yqOeZm+rrVG8gw==",
|
||||
"version": "19.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-19.0.0.tgz",
|
||||
"integrity": "sha512-qDVnNybqFm2eZKJ4jD34EvRd6VHD67KjgnWaEMM0Id9L22EpWe3nOSVKHWL1XWRCxUWe3lhXwlEeCKD1BlJCQA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -13170,6 +13261,16 @@
|
||||
"node": ">=0.8.19"
|
||||
}
|
||||
},
|
||||
"node_modules/indent-string": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
||||
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
@ -19696,6 +19797,16 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/min-indent": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
|
||||
"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/minimalistic-assert": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||
@ -19861,9 +19972,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
|
||||
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@ -21469,9 +21580,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
@ -21620,9 +21731,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-camera-kit": {
|
||||
"version": "14.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-camera-kit/-/react-native-camera-kit-14.1.0.tgz",
|
||||
"integrity": "sha512-idkg+Sa2KbGvF6SUqmuAr2U12qBELdiuUJ6fxgB4whUC2AyYHi5jBxiGv6whY/eTB3is7nW1S+TjyM9pEBzNzw==",
|
||||
"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==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
@ -21714,6 +21825,21 @@
|
||||
"react-native": ">=0.64.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-drawer-layout": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-drawer-layout/-/react-native-drawer-layout-4.1.1.tgz",
|
||||
"integrity": "sha512-ob6O3ph7PZ3A2FpdlsSxHuMpHDXREZPR8A6S3q0dSxV7i6d+8Z6CPCTbegfN2QZyizSow9NLrKyXP93tlqZ3dA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"use-latest-callback": "^0.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 18.2.0",
|
||||
"react-native": "*",
|
||||
"react-native-gesture-handler": ">= 2.0.0",
|
||||
"react-native-reanimated": ">= 2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-fs": {
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-fs/-/react-native-fs-2.20.0.tgz",
|
||||
@ -21734,9 +21860,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-gesture-handler": {
|
||||
"version": "2.22.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.22.1.tgz",
|
||||
"integrity": "sha512-E0C9D+Ia2UZYevoSV9rTKjhFWEVdR/3l4Z3TUoQrI/wewgzDlmJOrYvGW5aMlPUuQF2vHQOdFfAWhVEqFu4tWw==",
|
||||
"version": "2.23.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.23.1.tgz",
|
||||
"integrity": "sha512-dr5CxIu+kXlxS3snSyt0qXtXGqfDvV26gtQVPNteFIfP0qWBuye/j48XPXePe2pH48QCuXpZM2VOooig7jzooA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@egjs/hammerjs": "^2.0.17",
|
||||
@ -21770,9 +21896,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-image-picker": {
|
||||
"version": "7.2.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-image-picker/-/react-native-image-picker-7.2.2.tgz",
|
||||
"integrity": "sha512-hcSaD/ohDac1ooDbZyKltfmOEAWrQOrPbusKEfbJQ7EfGveBEIF5wfmZsCjXRc0HNpGdQ71P1x2JZckkqu7IRw==",
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-image-picker/-/react-native-image-picker-7.2.3.tgz",
|
||||
"integrity": "sha512-zKIZUlQNU3EtqizsXSH92zPeve4vpUrsqHu2kkpCxWE9TZhJFZBb+irDsBOY8J21k0+Edgt06TMQGJ+iPUIXyA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
@ -21832,9 +21958,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-permissions": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/react-native-permissions/-/react-native-permissions-5.2.4.tgz",
|
||||
"integrity": "sha512-WmFY3mDBwj0eO93ziEi8r+6uuZo6XrcEIdQ66AfzC8CeNXXclDwypTnewBj51K0mn4QVRJxM6PSgIlj0ii5qig==",
|
||||
"version": "5.2.5",
|
||||
"resolved": "https://registry.npmjs.org/react-native-permissions/-/react-native-permissions-5.2.5.tgz",
|
||||
"integrity": "sha512-pECev4EuA+XAq9kduJu9V5OtqKlOokf+5lawPmkTHvr6LoOl6VLZlI2Ue4tnOu1PLc6tQaG19kQ5gbKG4gyNAw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=18.1.0",
|
||||
@ -21956,9 +22082,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-safe-area-context": {
|
||||
"version": "4.14.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.14.1.tgz",
|
||||
"integrity": "sha512-+tUhT5WBl8nh5+P+chYhAjR470iCByf9z5EYdCEbPaAK3Yfzw+o8VRPnUgmPAKlSccOgQBxx3NOl/Wzckn9ujg==",
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.2.0.tgz",
|
||||
"integrity": "sha512-QpcGA6MRKe8Zbpf1hirCBudNQYGsMv0n/CTTROMOFcXbqRUoEXLCsYxUmYKi7JJb3ziL2DbyzWXyH2/gw4Tkfw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
@ -21975,9 +22101,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-screens": {
|
||||
"version": "3.35.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-3.35.0.tgz",
|
||||
"integrity": "sha512-rmkqb/M/SQIrXwygk6pXcOhgHltYAhidf1WceO7ujAxkr6XtwmgFyd1HIztsrJa568GrAuwPdQ11I7TpVk+XsA==",
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.6.0.tgz",
|
||||
"integrity": "sha512-PqGtR/moJLiTMSavhfo5spKXNHZrlxffq3g5UUVPmyuu7MmazFlPvYqiAYnR2iB9tkJYgvZO6sbjYAE7619M0A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-freeze": "^1.0.0",
|
||||
@ -22289,7 +22415,6 @@
|
||||
"version": "16.15.0",
|
||||
"resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz",
|
||||
"integrity": "sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"object-assign": "^4.1.1",
|
||||
@ -22300,32 +22425,29 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-test-renderer": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.3.1.tgz",
|
||||
"integrity": "sha512-KkAgygexHUkQqtvvx/otwxtuFu5cVjfzTCtjXLH9boS19/Nbtg84zS7wIQn39G8IlrhThBpQsMKkq5ZHZIYFXA==",
|
||||
"dev": true,
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.2.0.tgz",
|
||||
"integrity": "sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-is": "^18.3.1",
|
||||
"react-is": "^18.2.0",
|
||||
"react-shallow-renderer": "^16.15.0",
|
||||
"scheduler": "^0.23.2"
|
||||
"scheduler": "^0.23.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.3.1"
|
||||
"react": "^18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-test-renderer/node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-test-renderer/node_modules/scheduler": {
|
||||
"version": "0.23.2",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
||||
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
@ -22494,6 +22616,20 @@
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/redent": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
|
||||
"integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"indent-string": "^4.0.0",
|
||||
"strip-indent": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/reduce-flatten": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz",
|
||||
@ -23710,6 +23846,19 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-indent": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
|
||||
"integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"min-indent": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-json-comments": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
|
||||
@ -24742,6 +24891,15 @@
|
||||
"react": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
|
||||
"integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/utf8": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz",
|
||||
|
||||
34
package.json
34
package.json
@ -16,13 +16,14 @@
|
||||
"@react-native/js-polyfills": "^0.76.7",
|
||||
"@react-native/metro-babel-transformer": "^0.76.7",
|
||||
"@react-native/typescript-config": "0.76.7",
|
||||
"@testing-library/react-native": "^13.0.1",
|
||||
"@types/bip38": "^3.1.2",
|
||||
"@types/bs58check": "^2.1.0",
|
||||
"@types/create-hash": "^1.2.2",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/jest": "^29.5.2",
|
||||
"@types/react": "^18.2.16",
|
||||
"@types/react-test-renderer": "^18.0.0",
|
||||
"@types/react-test-renderer": "^19.0.0",
|
||||
"@types/wif": "^2.0.5",
|
||||
"@typescript-eslint/eslint-plugin": "^7.15.0",
|
||||
"@typescript-eslint/parser": "^7.15.0",
|
||||
@ -44,7 +45,6 @@
|
||||
"metro-react-native-babel-preset": "0.76.8",
|
||||
"node-fetch": "^2.6.7",
|
||||
"prettier": "^3.2.5",
|
||||
"react-test-renderer": "18.3.1",
|
||||
"ts-jest": "^29.1.1",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
@ -66,9 +66,11 @@
|
||||
"jest": "jest tests/integration/*",
|
||||
"e2e:debug-build": "detox build -c android.debug",
|
||||
"e2e:debug-test": "detox test -c android.debug -d 200000 --loglevel error --reuse",
|
||||
"e2e:debug-test-device": "detox test -c android.debug.device -d 200000 --loglevel error --reuse",
|
||||
"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",
|
||||
"e2e:release-build": "detox build -c android.release",
|
||||
"e2e:release-test": "detox test -c android.release --loglevel error",
|
||||
"e2e:release-test-device": "detox test -c android.release.device --loglevel info --record-videos all",
|
||||
"tslint": "tsc",
|
||||
"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",
|
||||
@ -79,22 +81,23 @@
|
||||
"@babel/preset-env": "7.25.8",
|
||||
"@bugsnag/react-native": "8.2.0",
|
||||
"@bugsnag/source-maps": "2.3.3",
|
||||
"@react-native-community/cli": "15.1.3",
|
||||
"@react-native-community/cli-platform-android": "15.1.3",
|
||||
"@react-native-community/cli-platform-ios": "15.1.3",
|
||||
"@keystonehq/bc-ur-registry": "0.7.0",
|
||||
"@lodev09/react-native-true-sheet": "github:BlueWallet/react-native-true-sheet#c6fee8925120d94328dc2aace45d4fa0a111d57f",
|
||||
"@ngraveio/bc-ur": "1.1.13",
|
||||
"@noble/secp256k1": "1.6.3",
|
||||
"@react-native-async-storage/async-storage": "2.1.0",
|
||||
"@react-native-clipboard/clipboard": "1.16.1",
|
||||
"@react-native-community/cli": "15.0.1",
|
||||
"@react-native-community/cli-platform-android": "15.0.1",
|
||||
"@react-native-community/cli-platform-ios": "15.0.1",
|
||||
"@react-native-community/push-notification-ios": "1.11.0",
|
||||
"@react-native-menu/menu": "https://github.com/BlueWallet/menu.git#14bab79",
|
||||
"@react-native/gradle-plugin": "0.76.7",
|
||||
"@react-native/metro-config": "0.76.7",
|
||||
"@react-navigation/drawer": "6.7.2",
|
||||
"@react-navigation/native": "6.1.18",
|
||||
"@react-navigation/native-stack": "6.11.0",
|
||||
"@react-navigation/devtools": "7.0.15",
|
||||
"@react-navigation/drawer": "7.1.1",
|
||||
"@react-navigation/native": "7.0.14",
|
||||
"@react-navigation/native-stack": "7.2.0",
|
||||
"@rneui/base": "4.0.0-rc.8",
|
||||
"@rneui/themed": "4.0.0-rc.8",
|
||||
"@spsina/bip47": "github:BlueWallet/bip47#df82345",
|
||||
@ -127,27 +130,27 @@
|
||||
"payjoin-client": "1.0.1",
|
||||
"process": "0.11.10",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
"react": "18.2.0",
|
||||
"react-localization": "github:BlueWallet/react-localization#ae7969a",
|
||||
"react-native": "0.76.7",
|
||||
"react-native-biometrics": "3.0.1",
|
||||
"react-native-blue-crypto": "github:BlueWallet/react-native-blue-crypto#3cb5442",
|
||||
"react-native-camera-kit": "14.1.0",
|
||||
"react-native-camera-kit": "14.2.0",
|
||||
"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.2",
|
||||
"react-native-document-picker": "9.3.1",
|
||||
"react-native-draglist": "github:BlueWallet/react-native-draglist#a4af02f",
|
||||
"react-native-fs": "2.20.0",
|
||||
"react-native-gesture-handler": "2.22.1",
|
||||
"react-native-gesture-handler": "2.23.1",
|
||||
"react-native-handoff": "github:BlueWallet/react-native-handoff#v0.0.4",
|
||||
"react-native-haptic-feedback": "2.3.3",
|
||||
"react-native-image-picker": "7.2.2",
|
||||
"react-native-image-picker": "7.2.3",
|
||||
"react-native-ios-context-menu": "github:BlueWallet/react-native-ios-context-menu#e5c1217cd220bfab6e6d9a7c65838545082e3f8e",
|
||||
"react-native-keychain": "9.1.0",
|
||||
"react-native-linear-gradient": "2.8.3",
|
||||
"react-native-localize": "3.4.1",
|
||||
"react-native-permissions": "5.2.4",
|
||||
"react-native-permissions": "5.2.5",
|
||||
"react-native-prompt-android": "github:BlueWallet/react-native-prompt-android#ed168d66fed556bc2ed07cf498770f058b78a376",
|
||||
"react-native-push-notification": "8.1.1",
|
||||
"react-native-qrcode-svg": "6.3.12",
|
||||
@ -155,15 +158,16 @@
|
||||
"react-native-randombytes": "3.6.1",
|
||||
"react-native-rate": "1.2.12",
|
||||
"react-native-reanimated": "3.16.7",
|
||||
"react-native-safe-area-context": "4.14.1",
|
||||
"react-native-safe-area-context": "5.2.0",
|
||||
"react-native-screen-capture": "github:BlueWallet/react-native-screen-capture#18cb79f",
|
||||
"react-native-screens": "3.35.0",
|
||||
"react-native-screens": "4.6.0",
|
||||
"react-native-secure-key-store": "github:BlueWallet/react-native-secure-key-store#2076b4849e88aa0a78e08bfbb4ce3923e0925cbc",
|
||||
"react-native-share": "11.1.0",
|
||||
"react-native-svg": "15.11.1",
|
||||
"react-native-tcp-socket": "6.2.0",
|
||||
"react-native-vector-icons": "10.2.0",
|
||||
"react-native-watch-connectivity": "1.1.0",
|
||||
"react-test-renderer": "18.2.0",
|
||||
"readable-stream": "3.6.2",
|
||||
"realm": "20.1.0",
|
||||
"rn-nodeify": "10.3.0",
|
||||
|
||||
@ -11,6 +11,7 @@ import PromptPasswordConfirmationModal, {
|
||||
MODAL_TYPES,
|
||||
} from '../components/PromptPasswordConfirmationModal';
|
||||
import { useExtendedNavigation } from '../hooks/useExtendedNavigation';
|
||||
import { StackActions } from '@react-navigation/native';
|
||||
|
||||
// Action Types
|
||||
const SET_LOADING = 'SET_LOADING';
|
||||
@ -46,13 +47,13 @@ function reducer(state: State, action: Action): State {
|
||||
const PlausibleDeniability: React.FC = () => {
|
||||
const { cachedPassword, isPasswordInUse, createFakeStorage, resetWallets } = useStorage();
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
const { navigate } = useExtendedNavigation();
|
||||
const navigation = useExtendedNavigation();
|
||||
const promptRef = useRef<PromptPasswordConfirmationModalHandle>(null);
|
||||
|
||||
const handleOnCreateFakeStorageButtonPressed = async () => {
|
||||
dispatch({ type: SET_LOADING, payload: true });
|
||||
dispatch({ type: SET_MODAL_TYPE, payload: MODAL_TYPES.CREATE_FAKE_STORAGE });
|
||||
promptRef.current?.present();
|
||||
await promptRef.current?.present();
|
||||
};
|
||||
|
||||
const handleConfirmationSuccess = async (password: string) => {
|
||||
@ -73,8 +74,9 @@ const PlausibleDeniability: React.FC = () => {
|
||||
dispatch({ type: SET_MODAL_TYPE, payload: MODAL_TYPES.SUCCESS });
|
||||
|
||||
success = true;
|
||||
setTimeout(() => {
|
||||
navigate('WalletsList');
|
||||
setTimeout(async () => {
|
||||
const popToTop = StackActions.popToTop();
|
||||
navigation.dispatch(popToTop);
|
||||
}, 3000);
|
||||
} catch {
|
||||
success = false;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { RouteProp, useRoute } from '@react-navigation/native';
|
||||
import { RouteProp, StackActions, useRoute } from '@react-navigation/native';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { Avatar, Badge, Icon, ListItem as RNElementsListItem } from '@rneui/themed';
|
||||
import {
|
||||
@ -17,7 +17,6 @@ import {
|
||||
View,
|
||||
} from 'react-native';
|
||||
import * as RNLocalize from 'react-native-localize';
|
||||
|
||||
import debounce from '../../blue_modules/debounce';
|
||||
import { BlueSpacing10, BlueSpacing20 } from '../../BlueComponents';
|
||||
import { TWallet, Utxo } from '../../class/wallets/types';
|
||||
@ -35,6 +34,7 @@ import loc, { formatBalance } from '../../loc';
|
||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
import { SendDetailsStackParamList } from '../../navigation/SendDetailsStackParamList';
|
||||
import { CommonToolTipActions } from '../../typings/CommonToolTipActions';
|
||||
import { useKeyboard } from '../../hooks/useKeyboard';
|
||||
import TipBox from '../../components/TipBox';
|
||||
|
||||
type NavigationProps = NativeStackNavigationProp<SendDetailsStackParamList, 'CoinControl'>;
|
||||
@ -315,6 +315,7 @@ const CoinControl: React.FC = () => {
|
||||
const [output, setOutput] = useState<Utxo | undefined>();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [selected, setSelected] = useState<string[]>([]);
|
||||
const { isVisible } = useKeyboard();
|
||||
|
||||
// save frozen status. Because effect called on each event, debounce it.
|
||||
const debouncedSaveFronen = useRef(
|
||||
@ -373,14 +374,8 @@ const CoinControl: React.FC = () => {
|
||||
|
||||
const handleUseCoin = async (u: Utxo[]) => {
|
||||
setOutput(undefined);
|
||||
// @ts-ignore navigation WTF
|
||||
navigation.navigate('SendDetailsRoot', {
|
||||
screen: 'SendDetails',
|
||||
params: {
|
||||
utxos: u,
|
||||
},
|
||||
merge: true,
|
||||
});
|
||||
const popToAction = StackActions.popTo('SendDetails', { walletID, utxos: u }, true);
|
||||
navigation.dispatch(popToAction);
|
||||
};
|
||||
|
||||
const handleMassFreeze = () => {
|
||||
@ -525,15 +520,17 @@ const CoinControl: React.FC = () => {
|
||||
contentContainerStyle={styles.modalMinHeight}
|
||||
footer={
|
||||
<View style={mStyles.buttonContainer}>
|
||||
<Button
|
||||
testID="UseCoin"
|
||||
title={loc.cc.use_coin}
|
||||
onPress={async () => {
|
||||
if (!output) throw new Error('output is not set');
|
||||
await bottomModalRef.current?.dismiss();
|
||||
handleUseCoin([output]);
|
||||
}}
|
||||
/>
|
||||
{!isVisible && (
|
||||
<Button
|
||||
testID="UseCoin"
|
||||
title={loc.cc.use_coin}
|
||||
onPress={async () => {
|
||||
if (!output) throw new Error('output is not set');
|
||||
await bottomModalRef.current?.dismiss();
|
||||
handleUseCoin([output]);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
}
|
||||
>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useFocusEffect, useIsFocused, useNavigation, useRoute } from '@react-navigation/native';
|
||||
import { StackActions, useFocusEffect, useIsFocused, useRoute } from '@react-navigation/native';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
import createHash from 'create-hash';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
@ -13,6 +13,7 @@ import { useTheme } from '../../components/themes';
|
||||
import { isCameraAuthorizationStatusGranted } from '../../helpers/scan-qr';
|
||||
import loc from '../../loc';
|
||||
import { useSettings } from '../../hooks/context/useSettings';
|
||||
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
|
||||
import CameraScreen from '../../components/CameraScreen';
|
||||
import SafeArea from '../../components/SafeArea';
|
||||
import presentAlert from '../../components/Alert';
|
||||
@ -34,7 +35,7 @@ const styles = StyleSheet.create({
|
||||
height: 60,
|
||||
backgroundColor: 'rgba(0,0,0,0.01)',
|
||||
position: 'absolute',
|
||||
top: 10,
|
||||
top: 60,
|
||||
left: '50%',
|
||||
transform: [{ translateX: -30 }],
|
||||
},
|
||||
@ -53,13 +54,13 @@ const styles = StyleSheet.create({
|
||||
const ScanQRCode = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { setIsDrawerShouldHide } = useSettings();
|
||||
const navigation = useNavigation();
|
||||
const navigation = useExtendedNavigation();
|
||||
const route = useRoute();
|
||||
const navigationState = navigation.getState();
|
||||
const previousRoute = navigationState.routes[navigationState.routes.length - 2];
|
||||
const defaultLaunchedBy = previousRoute ? previousRoute.name : undefined;
|
||||
|
||||
const { launchedBy = defaultLaunchedBy, onBarScanned, showFileImportButton } = route.params || {};
|
||||
const { launchedBy = defaultLaunchedBy, showFileImportButton } = route.params || {};
|
||||
const scannedCache = {};
|
||||
const { colors } = useTheme();
|
||||
const isFocused = useIsFocused();
|
||||
@ -110,16 +111,15 @@ const ScanQRCode = () => {
|
||||
decoder = false; // nullify for future use (?)
|
||||
if (launchedBy) {
|
||||
const merge = true;
|
||||
navigation.navigate({ name: launchedBy, params: { onBarScanned: data }, merge });
|
||||
} else {
|
||||
onBarScanned && onBarScanned({ data });
|
||||
const popToAction = StackActions.popTo(launchedBy, { onBarScanned: data }, merge);
|
||||
|
||||
navigation.dispatch(popToAction);
|
||||
}
|
||||
} else {
|
||||
setUrTotal(100);
|
||||
setUrHave(Math.floor(decoder.estimatedPercentComplete() * 100));
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
setIsLoading(true);
|
||||
presentAlert({
|
||||
title: loc.send.scan_error,
|
||||
@ -154,15 +154,14 @@ const ScanQRCode = () => {
|
||||
}
|
||||
if (launchedBy) {
|
||||
const merge = true;
|
||||
navigation.navigate({ name: launchedBy, params: { onBarScanned: data }, merge });
|
||||
} else {
|
||||
onBarScanned && onBarScanned({ data });
|
||||
const popToAction = StackActions.popTo(launchedBy, { onBarScanned: data }, merge);
|
||||
|
||||
navigation.dispatch(popToAction);
|
||||
}
|
||||
} else {
|
||||
setAnimatedQRCodeData(animatedQRCodeData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
setIsLoading(true);
|
||||
|
||||
presentAlert({
|
||||
@ -211,28 +210,25 @@ const ScanQRCode = () => {
|
||||
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) {
|
||||
const merge = true;
|
||||
|
||||
navigation.navigate({ name: launchedBy, params: { onBarScanned: data }, merge });
|
||||
} else {
|
||||
onBarScanned && onBarScanned({ data });
|
||||
const popToAction = StackActions.popTo(launchedBy, { onBarScanned: data }, merge);
|
||||
navigation.dispatch(popToAction);
|
||||
}
|
||||
return;
|
||||
} catch (_) {}
|
||||
|
||||
if (!isLoading) {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
if (launchedBy) {
|
||||
} catch (_) {
|
||||
if (!isLoading) {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const merge = true;
|
||||
|
||||
navigation.navigate({ name: launchedBy, params: { onBarScanned: ret.data }, merge });
|
||||
} else {
|
||||
onBarScanned && onBarScanned(ret.data);
|
||||
const popToAction = StackActions.popTo(launchedBy, { onBarScanned: ret.data }, merge);
|
||||
|
||||
navigation.dispatch(popToAction);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
setIsLoading(false);
|
||||
|
||||
@ -359,9 +359,7 @@ const SendDetails = () => {
|
||||
useCallback(() => {
|
||||
setIsLoading(false);
|
||||
setDumb(v => !v);
|
||||
return () => {
|
||||
feeModalRef.current?.dismiss();
|
||||
};
|
||||
return () => {};
|
||||
}, []),
|
||||
);
|
||||
|
||||
@ -1185,11 +1183,11 @@ const SendDetails = () => {
|
||||
|
||||
const renderWalletSelectionOrCoinsSelected = () => {
|
||||
if (isVisible) return null;
|
||||
if (utxos !== null) {
|
||||
if (utxos && utxos?.length > 0) {
|
||||
return (
|
||||
<View style={styles.select}>
|
||||
<CoinsSelected
|
||||
number={utxos?.length || 0}
|
||||
number={utxos?.length}
|
||||
onContainerPress={handleCoinControl}
|
||||
onClose={() => {
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
|
||||
@ -15,6 +15,7 @@ import loc from '../../loc';
|
||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
import { useStorage } from '../../hooks/context/useStorage';
|
||||
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
|
||||
import { combinePSBTs } from '../../utils/combinePSBTs';
|
||||
|
||||
const PsbtMultisig = () => {
|
||||
const { wallets } = useStorage();
|
||||
@ -24,7 +25,25 @@ const PsbtMultisig = () => {
|
||||
const { walletID, psbtBase64, memo, receivedPSBTBase64, launchedBy } = useRoute().params;
|
||||
/** @type MultisigHDWallet */
|
||||
const wallet = wallets.find(w => w.getID() === walletID);
|
||||
const [psbt, setPsbt] = useState(bitcoin.Psbt.fromBase64(psbtBase64));
|
||||
|
||||
const [psbt, setPsbt] = useState(() => {
|
||||
try {
|
||||
const initial = bitcoin.Psbt.fromBase64(psbtBase64);
|
||||
return initial;
|
||||
} catch (error) {
|
||||
console.error('Error loading initial PSBT:', error);
|
||||
presentAlert({ message: loc.send.invalid_psbt });
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (receivedPSBTBase64) {
|
||||
_combinePSBT();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [receivedPSBTBase64]);
|
||||
|
||||
const data = new Array(wallet.getM());
|
||||
const stylesHook = StyleSheet.create({
|
||||
root: {
|
||||
@ -65,6 +84,10 @@ const PsbtMultisig = () => {
|
||||
},
|
||||
});
|
||||
|
||||
const [isFiltered, setIsFiltered] = useState(true);
|
||||
|
||||
if (!psbt) return null;
|
||||
|
||||
// if useFilter is true, include only non-owned addresses.
|
||||
const getDestinationData = (useFilter = true) => {
|
||||
const addresses = [];
|
||||
@ -86,8 +109,6 @@ const PsbtMultisig = () => {
|
||||
|
||||
const targets = filteredData.targets;
|
||||
|
||||
const [isFiltered, setIsFiltered] = useState(true);
|
||||
|
||||
const displayData = isFiltered ? filteredData : unfilteredData;
|
||||
const displayTotalBtc = new BigNumber(displayData.totalSat).dividedBy(100000000).toNumber();
|
||||
const displayTotalFiat = satoshiToLocalCurrency(displayData.totalSat);
|
||||
@ -153,28 +174,25 @@ const PsbtMultisig = () => {
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (receivedPSBTBase64) {
|
||||
_combinePSBT();
|
||||
setParams({ receivedPSBTBase64: undefined });
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [receivedPSBTBase64]);
|
||||
|
||||
const _combinePSBT = () => {
|
||||
try {
|
||||
const receivedPSBT = bitcoin.Psbt.fromBase64(receivedPSBTBase64);
|
||||
const newPsbt = psbt.combine(receivedPSBT);
|
||||
setPsbt(newPsbt);
|
||||
} catch (error) {
|
||||
presentAlert({ message: error });
|
||||
if (receivedPSBTBase64 && receivedPSBTBase64 !== psbt.toBase64()) {
|
||||
try {
|
||||
const combined = combinePSBTs({ psbtBase64: psbt.toBase64(), newPSBTBase64: receivedPSBTBase64 });
|
||||
setPsbt(combined);
|
||||
setParams({ receivedPSBTBase64: undefined });
|
||||
} catch (error) {
|
||||
console.error('Error during PSBT combination:', error);
|
||||
presentAlert({ message: error.message });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onConfirm = () => {
|
||||
try {
|
||||
psbt.finalizeAllInputs();
|
||||
} catch (_) {} // ignore if it is already finalized
|
||||
} catch (err) {
|
||||
console.warn('Finalize error (ignored if already finalized):', err);
|
||||
}
|
||||
|
||||
if (launchedBy) {
|
||||
// we must navigate back to the screen who requested psbt (instead of broadcasting it ourselves)
|
||||
@ -277,7 +295,8 @@ const PsbtMultisig = () => {
|
||||
const footer = null;
|
||||
|
||||
const onLayout = event => {
|
||||
setFlatListHeight(event.nativeEvent.layout.height);
|
||||
const newHeight = event.nativeEvent.layout.height;
|
||||
setFlatListHeight(newHeight);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -295,6 +314,7 @@ const PsbtMultisig = () => {
|
||||
data={data}
|
||||
renderItem={_renderItem}
|
||||
keyExtractor={(_item, index) => `${index}`}
|
||||
extraData={psbt} // Ensure FlatList updates when psbt changes
|
||||
ListHeaderComponent={header}
|
||||
ListFooterComponent={footer}
|
||||
onLayout={onLayout}
|
||||
@ -305,7 +325,9 @@ const PsbtMultisig = () => {
|
||||
accessibilityRole="button"
|
||||
testID="ExportSignedPsbt"
|
||||
style={[styles.provideSignatureButton, stylesHook.provideSignatureButton]}
|
||||
onPress={navigateToPSBTMultisigQRCode}
|
||||
onPress={() => {
|
||||
navigateToPSBTMultisigQRCode();
|
||||
}}
|
||||
>
|
||||
<Text style={[styles.provideSignatureButtonText, stylesHook.provideSignatureButtonText]}>
|
||||
{loc.multisig.export_signed_psbt}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useIsFocused, useNavigation, useRoute } from '@react-navigation/native';
|
||||
import { StackActions, useIsFocused, useNavigation, useRoute } from '@react-navigation/native';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { ActivityIndicator, ScrollView, StyleSheet, View } from 'react-native';
|
||||
@ -48,16 +48,17 @@ const PsbtMultisigQRCode = () => {
|
||||
if (!ret.data) ret = { data: ret };
|
||||
if (ret.data.toUpperCase().startsWith('UR')) {
|
||||
presentAlert({ message: 'BC-UR not decoded. This should never happen' });
|
||||
} else if (ret.data.indexOf('+') === -1 && ret.data.indexOf('=') === -1 && ret.data.indexOf('=') === -1) {
|
||||
} else if (ret.data.indexOf('+') === -1 && ret.data.indexOf('=') === -1) {
|
||||
presentAlert({ message: loc.wallets.import_error });
|
||||
// this looks like NOT base64, so maybe its transaction's hex
|
||||
// we dont support it in this flow
|
||||
presentAlert({ message: loc.wallets.import_error });
|
||||
} else {
|
||||
// psbt base64?
|
||||
navigation.navigate({ name: 'PsbtMultisig', params: { receivedPSBTBase64: ret.data }, merge: true });
|
||||
const popToAction = StackActions.popTo('PsbtMultisig', { psbtBase64, receivedPSBTBase64: ret.data, ...params }, true);
|
||||
navigation.dispatch(popToAction);
|
||||
}
|
||||
},
|
||||
[navigation],
|
||||
[navigation, psbtBase64, params],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
import { useIsFocused, useRoute } from '@react-navigation/native';
|
||||
import { StackActions, useIsFocused, useRoute } from '@react-navigation/native';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { ActivityIndicator, Linking, Platform, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native';
|
||||
@ -85,7 +85,8 @@ const PsbtWithHardwareWallet = () => {
|
||||
if (launchedBy) {
|
||||
// we must navigate back to the screen who requested psbt (instead of broadcasting it ourselves)
|
||||
// most likely for LN channel opening
|
||||
navigation.navigate({ name: launchedBy, params: { psbt }, merge: true });
|
||||
const popToAction = StackActions.popTo(launchedBy, { psbt }, true);
|
||||
navigation.dispatch(popToAction);
|
||||
// ^^^ we just use `psbt` variable sinse it was finalized in the above _combinePSBT()
|
||||
// (passed by reference)
|
||||
}
|
||||
@ -225,7 +226,7 @@ const PsbtWithHardwareWallet = () => {
|
||||
useEffect(() => {
|
||||
const data = route.params.onBarScanned;
|
||||
if (data) {
|
||||
onBarScanned(data);
|
||||
onBarScanned({ data });
|
||||
navigation.setParams({ onBarScanned: undefined });
|
||||
}
|
||||
}, [navigation, onBarScanned, route.params.onBarScanned]);
|
||||
|
||||
@ -10,10 +10,10 @@ import PromptPasswordConfirmationModal, {
|
||||
MODAL_TYPES,
|
||||
PromptPasswordConfirmationModalHandle,
|
||||
} from '../../components/PromptPasswordConfirmationModal';
|
||||
import { popToTop } from '../../NavigationService';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import { Header } from '../../components/Header';
|
||||
import { BlueSpacing20 } from '../../BlueComponents';
|
||||
import { StackActions } from '@react-navigation/native';
|
||||
|
||||
enum ActionType {
|
||||
SetLoading = 'SET_LOADING',
|
||||
@ -65,7 +65,7 @@ const EncryptStorage = () => {
|
||||
const { isStorageEncrypted, encryptStorage, decryptStorage, saveToDisk } = useStorage();
|
||||
const { isDeviceBiometricCapable, biometricEnabled, setBiometricUseEnabled, deviceBiometricType } = useBiometrics();
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
const { navigate } = useExtendedNavigation();
|
||||
const navigation = useExtendedNavigation();
|
||||
const { colors } = useTheme();
|
||||
const promptRef = useRef<PromptPasswordConfirmationModalHandle>(null);
|
||||
|
||||
@ -134,7 +134,12 @@ const EncryptStorage = () => {
|
||||
};
|
||||
|
||||
const navigateToPlausibleDeniability = () => {
|
||||
navigate('PlausibleDeniability');
|
||||
navigation.navigate('PlausibleDeniability');
|
||||
};
|
||||
|
||||
const popToTop = () => {
|
||||
const action = StackActions.popToTop();
|
||||
navigation.dispatch(action);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -178,6 +183,7 @@ const EncryptStorage = () => {
|
||||
onValueChange: onEncryptStorageSwitch,
|
||||
value: state.storageIsEncryptedSwitchEnabled,
|
||||
disabled: state.currentLoadingSwitch !== null,
|
||||
testID: 'EncyptedAndPasswordProtectedSwitch',
|
||||
}}
|
||||
isLoading={state.currentLoadingSwitch === 'encrypt' && state.isLoading}
|
||||
containerStyle={[styles.row, styleHooks.root]}
|
||||
@ -212,7 +218,7 @@ const EncryptStorage = () => {
|
||||
await decryptStorage(password);
|
||||
await saveToDisk();
|
||||
popToTop();
|
||||
success = true;
|
||||
return true;
|
||||
} catch (error) {
|
||||
success = false;
|
||||
}
|
||||
|
||||
@ -289,7 +289,7 @@ export default class SelfTest extends Component {
|
||||
<BlueSpacing20 />
|
||||
|
||||
{this.state.isLoading ? (
|
||||
<BlueLoading />
|
||||
<BlueLoading testID="SelfTestLoading" />
|
||||
) : (
|
||||
(() => {
|
||||
if (this.state.isOk) {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
import { RouteProp, useFocusEffect, useRoute } from '@react-navigation/native';
|
||||
import { RouteProp, useFocusEffect, usePreventRemove, useRoute } from '@react-navigation/native';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import assert from 'assert';
|
||||
import dayjs from 'dayjs';
|
||||
@ -60,7 +60,7 @@ type NavigationProps = NativeStackNavigationProp<DetailViewStackParamList, 'Tran
|
||||
type RouteProps = RouteProp<DetailViewStackParamList, 'TransactionDetails'>;
|
||||
|
||||
const TransactionDetails = () => {
|
||||
const { addListener, navigate } = useExtendedNavigation<NavigationProps>();
|
||||
const { navigate } = useExtendedNavigation<NavigationProps>();
|
||||
const { hash, walletID } = useRoute<RouteProps>().params;
|
||||
const { saveToDisk, txMetadata, counterpartyMetadata, wallets, getTransactions } = useStorage();
|
||||
const { selectedBlockExplorer } = useSettings();
|
||||
@ -97,13 +97,9 @@ const TransactionDetails = () => {
|
||||
}
|
||||
}, [tx, txMetadata, memo, counterpartyLabel, paymentCode, saveToDisk, counterpartyMetadata]);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = addListener('beforeRemove', () => {
|
||||
saveTransactionDetails();
|
||||
});
|
||||
|
||||
return unsubscribe;
|
||||
}, [addListener, saveTransactionDetails]);
|
||||
usePreventRemove(false, () => {
|
||||
saveTransactionDetails();
|
||||
});
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { DrawerContentScrollView } from '@react-navigation/drawer';
|
||||
import { NavigationProp, ParamListBase, useIsFocused } from '@react-navigation/native';
|
||||
import { useIsFocused } from '@react-navigation/native';
|
||||
import React, { memo, useCallback, useEffect, useMemo, useReducer, useRef } from 'react';
|
||||
import { InteractionManager, LayoutAnimation, StyleSheet, View, ViewStyle } from 'react-native';
|
||||
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
|
||||
import { TWallet } from '../../class/wallets/types';
|
||||
import { Header } from '../../components/Header';
|
||||
@ -12,6 +11,7 @@ import loc from '../../loc';
|
||||
import { useStorage } from '../../hooks/context/useStorage';
|
||||
import TotalWalletsBalance from '../../components/TotalWalletsBalance';
|
||||
import { useSettings } from '../../hooks/context/useSettings';
|
||||
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
|
||||
|
||||
enum WalletActionType {
|
||||
SetWallets = 'SET_WALLETS',
|
||||
@ -58,10 +58,6 @@ interface SelectWalletAction {
|
||||
|
||||
type WalletAction = SetWalletsAction | SelectWalletAction | SetFocusAction | NavigateAction;
|
||||
|
||||
interface DrawerListProps {
|
||||
navigation: NavigationProp<ParamListBase>;
|
||||
}
|
||||
|
||||
const walletReducer = (state: WalletState, action: WalletAction): WalletState => {
|
||||
switch (action.type) {
|
||||
case WalletActionType.SetWallets: {
|
||||
@ -78,12 +74,14 @@ const walletReducer = (state: WalletState, action: WalletAction): WalletState =>
|
||||
}
|
||||
};
|
||||
|
||||
const DrawerList: React.FC<DrawerListProps> = memo(({ navigation }) => {
|
||||
const DrawerList: React.FC = memo(() => {
|
||||
const initialState: WalletState = {
|
||||
wallets: [],
|
||||
isFocused: false,
|
||||
};
|
||||
|
||||
const navigation = useExtendedNavigation();
|
||||
|
||||
const [state, dispatch] = useReducer(walletReducer, initialState);
|
||||
const walletsCarousel = useRef(null);
|
||||
const { wallets, selectedWalletID } = useStorage();
|
||||
@ -118,8 +116,6 @@ const DrawerList: React.FC<DrawerListProps> = memo(({ navigation }) => {
|
||||
params: { walletID, walletType },
|
||||
});
|
||||
});
|
||||
} else {
|
||||
navigation.navigate('AddWalletRoot');
|
||||
}
|
||||
},
|
||||
[navigation],
|
||||
|
||||
@ -126,7 +126,7 @@ const ImportCustomDerivationPath: React.FC = () => {
|
||||
if (wallets[path] === WRONG_PATH) return;
|
||||
addAndSaveWallet(wallets[path][type]);
|
||||
// @ts-ignore: Navigation
|
||||
navigation.getParent().pop();
|
||||
navigation.getParent()?.goBack();
|
||||
};
|
||||
|
||||
const renderItem = ({ item }: { item: TItem }) => {
|
||||
|
||||
@ -74,7 +74,7 @@ const ImportSpeed = () => {
|
||||
}
|
||||
await wallet.fetchBalance();
|
||||
// @ts-ignore: navigation
|
||||
navigation.getParent().pop();
|
||||
navigation.getParent()?.goBack();
|
||||
addAndSaveWallet(wallet);
|
||||
} catch (e: any) {
|
||||
presentAlert({ message: e.message });
|
||||
|
||||
@ -78,12 +78,16 @@ const ImportWallet = () => {
|
||||
console.error('Failed to clear clipboard:', error);
|
||||
}
|
||||
}
|
||||
|
||||
Keyboard.dismiss();
|
||||
|
||||
navigation.navigate('ImportWalletDiscovery', {
|
||||
importText: text,
|
||||
askPassphrase: askPassphraseMenuState,
|
||||
searchAccounts: searchAccountsMenuState,
|
||||
});
|
||||
},
|
||||
|
||||
[askPassphraseMenuState, clearClipboardMenuState, navigation, searchAccountsMenuState],
|
||||
);
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
import { RouteProp, useRoute } from '@react-navigation/native';
|
||||
import { RouteProp, StackActions, useRoute } from '@react-navigation/native';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import assert from 'assert';
|
||||
import createHash from 'create-hash';
|
||||
@ -179,16 +179,23 @@ export default function PaymentCodesList() {
|
||||
};
|
||||
|
||||
const _navigateToSend = (pc: string) => {
|
||||
navigation.navigate('SendDetailsRoot', {
|
||||
screen: 'SendDetails',
|
||||
params: {
|
||||
const previousRoute = state.routes[state.routes.length - 2];
|
||||
|
||||
if (previousRoute.name === ('SendDetails' as string)) {
|
||||
const popToAction = StackActions.popTo('SendDetails', {
|
||||
walletID,
|
||||
addRecipientParams: {
|
||||
address: pc,
|
||||
},
|
||||
},
|
||||
merge: true,
|
||||
});
|
||||
merge: true,
|
||||
});
|
||||
navigation.dispatch(popToAction);
|
||||
} else {
|
||||
navigation.navigate('SendDetailsRoot', {
|
||||
paymentCode: pc,
|
||||
walletID,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const renderItem = (pc: string, index: number) => {
|
||||
|
||||
@ -35,7 +35,7 @@ const PleaseBackup: React.FC = () => {
|
||||
|
||||
const handleBackButton = useCallback(() => {
|
||||
// @ts-ignore: Ignore
|
||||
navigation.getParent()?.pop();
|
||||
navigation.getParent()?.goBack();
|
||||
return true;
|
||||
}, [navigation]);
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useReducer, useState } from 'react';
|
||||
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
|
||||
import { RouteProp, StackActions, useNavigation, useRoute } from '@react-navigation/native';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { Icon } from '@rneui/themed';
|
||||
import BN from 'bignumber.js';
|
||||
@ -326,7 +326,10 @@ const ProvideEntropy = () => {
|
||||
/* Convert Buffer to hex string before navigating as React Navigation
|
||||
does not support passing Buffer objects between screens
|
||||
*/
|
||||
navigation.navigate('AddWallet', { entropy: buf.toString('hex'), words });
|
||||
|
||||
const popTo = StackActions.popTo('AddWallet', { entropy: buf.toString('hex'), words }, true);
|
||||
|
||||
navigation.dispatch(popTo);
|
||||
},
|
||||
style: 'default',
|
||||
},
|
||||
|
||||
@ -11,28 +11,19 @@ import { useStorage } from '../../hooks/context/useStorage';
|
||||
import WalletsCarousel from '../../components/WalletsCarousel';
|
||||
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
|
||||
import { TWallet } from '../../class/wallets/types';
|
||||
import { CloseButtonPosition } from '../../components/navigationStyle';
|
||||
import { pop } from '../../NavigationService';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { SendDetailsStackParamList } from '../../navigation/SendDetailsStackParamList';
|
||||
|
||||
type SelectWalletRouteProp = RouteProp<
|
||||
{
|
||||
SelectWallet: {
|
||||
chainType?: Chain;
|
||||
onWalletSelect?: (wallet: TWallet, navigation: any) => void;
|
||||
availableWallets?: TWallet[];
|
||||
noWalletExplanationText?: string;
|
||||
onChainRequireSend?: boolean;
|
||||
};
|
||||
},
|
||||
'SelectWallet'
|
||||
>;
|
||||
type NavigationProps = NativeStackNavigationProp<SendDetailsStackParamList, 'SelectWallet'>;
|
||||
|
||||
type RouteProps = RouteProp<SendDetailsStackParamList, 'SelectWallet'>;
|
||||
|
||||
const SelectWallet: React.FC = () => {
|
||||
const route = useRoute<SelectWalletRouteProp>();
|
||||
const route = useRoute<RouteProps>();
|
||||
const { chainType, onWalletSelect, availableWallets, noWalletExplanationText, onChainRequireSend = false } = route.params;
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const navigation = useExtendedNavigation();
|
||||
const { navigate, setOptions } = navigation;
|
||||
const navigation = useExtendedNavigation<NavigationProps>();
|
||||
const { wallets } = useStorage();
|
||||
const { colors } = useTheme();
|
||||
const isModal = useNavigationState(state => state.routes.length === 1);
|
||||
@ -67,23 +58,24 @@ const SelectWallet: React.FC = () => {
|
||||
}, [availableWallets, chainType, onChainRequireSend, wallets]);
|
||||
|
||||
useEffect(() => {
|
||||
setOptions({
|
||||
navigation.setOptions({
|
||||
statusBarStyle: isLoading || (availableWallets || filterWallets()).length === 0 ? 'light' : 'auto',
|
||||
});
|
||||
}, [isLoading, availableWallets, setOptions, filterWallets]);
|
||||
}, [isLoading, availableWallets, filterWallets, navigation]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isModal) {
|
||||
setOptions({ CloseButtonPosition: CloseButtonPosition.None });
|
||||
navigation.setOptions({ headerBackVisible: false });
|
||||
}
|
||||
}, [isModal, setOptions]);
|
||||
}, [isModal, navigation]);
|
||||
|
||||
const onPress = (item: TWallet) => {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.Selection);
|
||||
if (onWalletSelect) {
|
||||
onWalletSelect(item, { navigation: { pop, navigate } });
|
||||
onWalletSelect(item, { navigation: { pop, navigation: navigation.navigate } });
|
||||
} else {
|
||||
navigate(previousRouteName, { walletID: item.getID(), merge: true });
|
||||
// @ts-ignore: fix later
|
||||
navigation.popTo(previousRouteName, { walletID: item.getID(), merge: true });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { CommonActions, RouteProp, useFocusEffect, useRoute } from '@react-navigation/native';
|
||||
import { RouteProp, useFocusEffect, useRoute, usePreventRemove, CommonActions } from '@react-navigation/native';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Alert,
|
||||
@ -50,8 +50,8 @@ import { CommonToolTipActions } from '../../typings/CommonToolTipActions';
|
||||
import { useSettings } from '../../hooks/context/useSettings';
|
||||
import { ViewEditMultisigCosignersStackParamList } from '../../navigation/ViewEditMultisigCosignersStack';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { navigationRef } from '../../NavigationService';
|
||||
import SafeArea from '../../components/SafeArea';
|
||||
import { TWallet } from '../../class/wallets/types';
|
||||
|
||||
type RouteParams = RouteProp<ViewEditMultisigCosignersStackParamList, 'ViewEditMultisigCosigners'>;
|
||||
type NavigationProp = NativeStackNavigationProp<ViewEditMultisigCosignersStackParamList, 'ViewEditMultisigCosigners'>;
|
||||
@ -62,7 +62,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
const { wallets, setWalletsWithNewOrder } = useStorage();
|
||||
const { isBiometricUseCapableAndEnabled } = useBiometrics();
|
||||
const { isElectrumDisabled, isPrivacyBlurEnabled } = useSettings();
|
||||
const { navigate, dispatch, addListener, setParams, setOptions } = useExtendedNavigation<NavigationProp>();
|
||||
const { navigate, dispatch, setParams, setOptions } = useExtendedNavigation<NavigationProp>();
|
||||
const openScannerButtonRef = useRef();
|
||||
const route = useRoute<RouteParams>();
|
||||
const { walletID } = route.params;
|
||||
@ -82,7 +82,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
const [vaultKeyData, setVaultKeyData] = useState({ keyIndex: 1, xpub: '', seed: '', passphrase: '', path: '', fp: '', isLoading: false }); // string rendered in modal
|
||||
const [isVaultKeyIndexDataLoading, setIsVaultKeyIndexDataLoading] = useState<number | undefined>(undefined);
|
||||
const [askPassphrase, setAskPassphrase] = useState(false);
|
||||
const data = useRef<any[]>();
|
||||
const [walletData, setWalletData] = useState<TWallet[]>([]);
|
||||
/* discardChangesRef is only so the action sheet can be shown on mac catalyst when a
|
||||
user tries to leave the screen with unsaved changes.
|
||||
Why the container view ? It was the easiest to get the ref for. No other reason.
|
||||
@ -116,53 +116,37 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
color: colors.buttonTextColor,
|
||||
},
|
||||
});
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
const unsubscribe = addListener('beforeRemove', (e: { preventDefault: () => void; data: { action: any } }) => {
|
||||
// Check if there are unsaved changes
|
||||
if (isSaveButtonDisabled) {
|
||||
// If there are no unsaved changes, let the user leave the screen
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent the default action (going back)
|
||||
e.preventDefault();
|
||||
|
||||
// Show an alert asking the user to discard changes or cancel
|
||||
if (isDesktop) {
|
||||
if (!discardChangesRef.current) return dispatch(e.data.action);
|
||||
const anchor = findNodeHandle(discardChangesRef.current);
|
||||
if (!anchor) return dispatch(e.data.action);
|
||||
ActionSheet.showActionSheetWithOptions(
|
||||
{
|
||||
options: [loc._.cancel, loc._.ok],
|
||||
cancelButtonIndex: 0,
|
||||
title: loc._.discard_changes,
|
||||
message: loc._.discard_changes_explain,
|
||||
anchor,
|
||||
},
|
||||
buttonIndex => {
|
||||
if (buttonIndex === 1) {
|
||||
dispatch(e.data.action);
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
Alert.alert(loc._.discard_changes, loc._.discard_changes_explain, [
|
||||
{ text: loc._.cancel, style: 'cancel', onPress: () => {} },
|
||||
{
|
||||
text: loc._.ok,
|
||||
style: 'default',
|
||||
// If the user confirms, then we dispatch the action we blocked earlier
|
||||
onPress: () => dispatch(e.data.action),
|
||||
},
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
return unsubscribe;
|
||||
}, [isSaveButtonDisabled, addListener, dispatch]),
|
||||
);
|
||||
usePreventRemove(!isSaveButtonDisabled, ({ data }) => {
|
||||
if (isDesktop) {
|
||||
if (!discardChangesRef.current) return dispatch(data.action);
|
||||
const anchor = findNodeHandle(discardChangesRef.current);
|
||||
if (!anchor) return dispatch(data.action);
|
||||
ActionSheet.showActionSheetWithOptions(
|
||||
{
|
||||
options: [loc._.cancel, loc._.ok],
|
||||
cancelButtonIndex: 0,
|
||||
title: loc._.discard_changes,
|
||||
message: loc._.discard_changes_explain,
|
||||
anchor,
|
||||
},
|
||||
buttonIndex => {
|
||||
if (buttonIndex === 1) {
|
||||
dispatch(data.action);
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
Alert.alert(loc._.discard_changes, loc._.discard_changes_explain, [
|
||||
{ text: loc._.cancel, style: 'cancel', onPress: () => {} },
|
||||
{
|
||||
text: loc._.ok,
|
||||
style: 'default',
|
||||
onPress: () => dispatch(data.action),
|
||||
},
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
const onSave = async () => {
|
||||
if (!wallet) {
|
||||
@ -193,7 +177,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
setIsSaveButtonDisabled(true);
|
||||
setWalletsWithNewOrder(newWallets);
|
||||
setTimeout(() => {
|
||||
navigationRef.dispatch(
|
||||
dispatch(
|
||||
CommonActions.navigate({ name: 'WalletTransactions', params: { walletID: wallet.getID(), walletType: MultisigHDWallet.type } }),
|
||||
);
|
||||
}, 500);
|
||||
@ -214,7 +198,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
w.current.setNativeSegwit();
|
||||
} else {
|
||||
tempWallet.current.setSecret(w.current.getSecret());
|
||||
data.current = new Array(tempWallet.current.getN());
|
||||
setWalletData(new Array(tempWallet.current.getN()));
|
||||
setWallet(tempWallet.current);
|
||||
}
|
||||
hasLoaded.current = true;
|
||||
@ -318,8 +302,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
leftText = `${secret[0]}...${secret[secret.length - 1]}`;
|
||||
}
|
||||
|
||||
// @ts-ignore not sure which one is correct
|
||||
const length = data?.length ?? data.current?.length ?? 0;
|
||||
const length = walletData.length;
|
||||
|
||||
return (
|
||||
<View>
|
||||
@ -473,7 +456,34 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const handleUseMnemonicPhrase = async () => {
|
||||
const _handleUseMnemonicPhrase = useCallback(
|
||||
(mnemonic: string, passphrase?: string) => {
|
||||
if (!wallet || !currentlyEditingCosignerNum) {
|
||||
// failsafe
|
||||
return;
|
||||
}
|
||||
|
||||
const hd = new HDSegwitBech32Wallet();
|
||||
hd.setSecret(mnemonic);
|
||||
if (!hd.validateMnemonic()) return presentAlert({ message: loc.multisig.invalid_mnemonics });
|
||||
try {
|
||||
wallet.replaceCosignerXpubWithSeed(currentlyEditingCosignerNum, hd.getSecret(), passphrase);
|
||||
} catch (e: any) {
|
||||
console.log(e);
|
||||
return presentAlert({ message: e.message });
|
||||
}
|
||||
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
setWallet(wallet);
|
||||
provideMnemonicsModalRef.current?.dismiss();
|
||||
setIsSaveButtonDisabled(false);
|
||||
setImportText('');
|
||||
setAskPassphrase(false);
|
||||
},
|
||||
[wallet, currentlyEditingCosignerNum],
|
||||
);
|
||||
|
||||
const handleUseMnemonicPhrase = useCallback(async () => {
|
||||
let passphrase;
|
||||
if (askPassphrase) {
|
||||
try {
|
||||
@ -487,29 +497,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
}
|
||||
}
|
||||
return _handleUseMnemonicPhrase(importText, passphrase);
|
||||
};
|
||||
|
||||
const _handleUseMnemonicPhrase = (mnemonic: string, passphrase?: string) => {
|
||||
if (!wallet || !currentlyEditingCosignerNum) {
|
||||
// failsafe
|
||||
return;
|
||||
}
|
||||
|
||||
const hd = new HDSegwitBech32Wallet();
|
||||
hd.setSecret(mnemonic);
|
||||
if (!hd.validateMnemonic()) return presentAlert({ message: loc.multisig.invalid_mnemonics });
|
||||
try {
|
||||
wallet.replaceCosignerXpubWithSeed(currentlyEditingCosignerNum, hd.getSecret(), passphrase);
|
||||
} catch (e: any) {
|
||||
console.log(e);
|
||||
return presentAlert({ message: e.message });
|
||||
}
|
||||
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
setWallet(wallet);
|
||||
provideMnemonicsModalRef.current?.dismiss();
|
||||
resetModalData();
|
||||
};
|
||||
}, [askPassphrase, importText, _handleUseMnemonicPhrase]);
|
||||
|
||||
const xpubInsteadOfSeed = (index: number): Promise<void> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -537,10 +525,9 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
const scannedData = route.params.onBarScanned;
|
||||
if (scannedData) {
|
||||
setImportText(String(scannedData));
|
||||
setParams({ onBarScanned: undefined });
|
||||
provideMnemonicsModalRef.current?.present();
|
||||
handleUseMnemonicPhrase();
|
||||
}
|
||||
}, [route.params.onBarScanned, setParams]);
|
||||
}, [route.params.onBarScanned, setParams, handleUseMnemonicPhrase]);
|
||||
|
||||
const hideProvideMnemonicsModal = () => {
|
||||
Keyboard.dismiss();
|
||||
@ -583,10 +570,18 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
) : (
|
||||
<Button disabled={importText.trim().length === 0} title={loc.wallets.import_do_import} onPress={handleUseMnemonicPhrase} />
|
||||
)}
|
||||
<>
|
||||
<BlueButtonLink ref={openScannerButtonRef} disabled={isLoading} onPress={scanOrOpenFile} title={loc.wallets.import_scan_qr} />
|
||||
<BlueSpacing20 />
|
||||
</>
|
||||
|
||||
{!isLoading && (
|
||||
<>
|
||||
<BlueButtonLink
|
||||
ref={openScannerButtonRef}
|
||||
disabled={isLoading}
|
||||
onPress={scanOrOpenFile}
|
||||
title={loc.wallets.import_scan_qr}
|
||||
/>
|
||||
<BlueSpacing20 />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
@ -594,7 +589,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
<BlueTextCentered>{loc.multisig.type_your_mnemonics}</BlueTextCentered>
|
||||
<BlueSpacing20 />
|
||||
<View style={styles.multiLineTextInput}>
|
||||
<BlueFormMultiInput value={importText} onChangeText={setImportText} />
|
||||
<BlueFormMultiInput editable={!isLoading} value={importText} onChangeText={setImportText} />
|
||||
</View>
|
||||
</>
|
||||
</BottomModal>
|
||||
@ -666,7 +661,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
<View style={[styles.root, stylesHook.root]} ref={discardChangesRef}>
|
||||
<FlatList
|
||||
ListHeaderComponent={tipKeys}
|
||||
data={data.current}
|
||||
data={walletData}
|
||||
extraData={vaultKeyData}
|
||||
renderItem={_renderKeyItem}
|
||||
automaticallyAdjustKeyboardInsets
|
||||
|
||||
@ -37,7 +37,7 @@ import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
|
||||
import loc, { formatBalanceWithoutSuffix } from '../../loc';
|
||||
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
||||
import { useStorage } from '../../hooks/context/useStorage';
|
||||
import { useFocusEffect, useRoute, RouteProp } from '@react-navigation/native';
|
||||
import { useFocusEffect, useRoute, RouteProp, usePreventRemove } from '@react-navigation/native';
|
||||
import { LightningTransaction, Transaction, TWallet } from '../../class/wallets/types';
|
||||
import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList';
|
||||
import HeaderMenuButton from '../../components/HeaderMenuButton';
|
||||
@ -66,7 +66,7 @@ const WalletDetails: React.FC = () => {
|
||||
const [hideTransactionsInWalletsList, setHideTransactionsInWalletsList] = useState<boolean>(
|
||||
wallet.getHideTransactionsInWalletsList ? !wallet.getHideTransactionsInWalletsList() : true,
|
||||
);
|
||||
const { setOptions, navigate, addListener } = useExtendedNavigation();
|
||||
const { setOptions, navigate } = useExtendedNavigation();
|
||||
const { colors } = useTheme();
|
||||
const [walletName, setWalletName] = useState<string>(wallet.getLabel());
|
||||
|
||||
@ -281,12 +281,6 @@ const WalletDetails: React.FC = () => {
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setOptions({
|
||||
headerBackTitleVisible: true,
|
||||
});
|
||||
}, [setOptions]);
|
||||
|
||||
useEffect(() => {
|
||||
if (wallets.some(w => w.getID() === walletID)) {
|
||||
setSelectedWalletID(walletID);
|
||||
@ -408,13 +402,9 @@ const WalletDetails: React.FC = () => {
|
||||
}
|
||||
}, [wallet, walletName, saveToDisk]);
|
||||
|
||||
useEffect(() => {
|
||||
const subscribe = addListener('beforeRemove', () => {
|
||||
walletNameTextInputOnBlur();
|
||||
});
|
||||
|
||||
return subscribe;
|
||||
}, [addListener, walletName, walletNameTextInputOnBlur]);
|
||||
usePreventRemove(false, () => {
|
||||
walletNameTextInputOnBlur();
|
||||
});
|
||||
|
||||
const onViewMasterFingerPrintPress = () => {
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
@ -650,7 +640,7 @@ const WalletDetails: React.FC = () => {
|
||||
{wallet.allowXpub && wallet.allowXpub() && (
|
||||
<>
|
||||
<BlueSpacing20 />
|
||||
<SecondButton onPress={navigateToXPub} testID="XPub" title={loc.wallets.details_show_xpub} />
|
||||
<SecondButton onPress={navigateToXPub} testID="XpubButton" title={loc.wallets.details_show_xpub} />
|
||||
</>
|
||||
)}
|
||||
{wallet.allowSignVerifyMessage && wallet.allowSignVerifyMessage() && (
|
||||
|
||||
@ -53,7 +53,6 @@ const WalletsAddMultisig: React.FC = () => {
|
||||
});
|
||||
|
||||
const onLetsStartPress = () => {
|
||||
bottomModalRef.current?.dismiss();
|
||||
navigate('WalletsAddMultisigStep2', { m, n, format, walletLabel });
|
||||
};
|
||||
|
||||
@ -233,7 +232,7 @@ const styles = StyleSheet.create({
|
||||
flex: 0.8,
|
||||
},
|
||||
modalContentShort: {
|
||||
padding: 24,
|
||||
padding: 20,
|
||||
},
|
||||
borderRadius6: {
|
||||
borderRadius: 6,
|
||||
@ -294,7 +293,7 @@ const styles = StyleSheet.create({
|
||||
rowCenter: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
paddingVertical: 40,
|
||||
paddingVertical: 30,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -170,12 +170,14 @@ const WalletsList: React.FC = () => {
|
||||
|
||||
useEffect(() => {
|
||||
// new wallet added
|
||||
if (wallets.length > walletsCount.current) {
|
||||
walletsCarousel.current?.scrollToItem({ item: wallets[walletsCount.current], viewPosition: 0.3 });
|
||||
}
|
||||
if (!isLargeScreen) {
|
||||
if (wallets.length > walletsCount.current) {
|
||||
walletsCarousel.current?.scrollToItem({ item: wallets[walletsCount.current], viewPosition: 0.3 });
|
||||
}
|
||||
|
||||
walletsCount.current = wallets.length;
|
||||
}, [wallets]);
|
||||
walletsCount.current = wallets.length;
|
||||
}
|
||||
}, [isLargeScreen, wallets]);
|
||||
|
||||
const onBarScanned = useCallback(
|
||||
(value: any) => {
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
ActivityIndicator,
|
||||
FlatList,
|
||||
I18nManager,
|
||||
InteractionManager,
|
||||
Keyboard,
|
||||
LayoutAnimation,
|
||||
Platform,
|
||||
@ -38,6 +39,7 @@ import {
|
||||
DoneAndDismissKeyboardInputAccessory,
|
||||
DoneAndDismissKeyboardInputAccessoryViewID,
|
||||
} from '../../components/DoneAndDismissKeyboardInputAccessory';
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
import MultipleStepsListItem, {
|
||||
MultipleStepsListItemButtonType,
|
||||
MultipleStepsListItemDashType,
|
||||
@ -46,10 +48,10 @@ import MultipleStepsListItem, {
|
||||
const staticCache = {};
|
||||
|
||||
const WalletsAddMultisigStep2 = () => {
|
||||
const { addWallet, saveToDisk, isElectrumDisabled, sleep, currentSharedCosigner, setSharedCosigner } = useStorage();
|
||||
const { addAndSaveWallet, isElectrumDisabled, sleep, currentSharedCosigner, setSharedCosigner } = useStorage();
|
||||
const { colors } = useTheme();
|
||||
|
||||
const { navigate, navigateToWalletsList, setParams, setOptions } = useExtendedNavigation();
|
||||
const navigation = useExtendedNavigation();
|
||||
const params = useRoute().params;
|
||||
const { m, n, format, walletLabel } = params;
|
||||
const [cosigners, setCosigners] = useState([]); // array of cosigners user provided. if format [cosigner, fp, path]
|
||||
@ -92,19 +94,7 @@ const WalletsAddMultisigStep2 = () => {
|
||||
}, [currentSharedCosigner]);
|
||||
|
||||
const handleOnHelpPress = async () => {
|
||||
await dismissAllModals();
|
||||
navigate('WalletsAddMultisigHelp');
|
||||
};
|
||||
|
||||
const dismissAllModals = async () => {
|
||||
try {
|
||||
await mnemonicsModalRef.current?.dismiss();
|
||||
await provideMnemonicsModalRef.current?.dismiss();
|
||||
await renderCosignersXpubModalRef.current?.dismiss();
|
||||
} catch (e) {
|
||||
// in rare occasions trying to dismiss non visible modals can error out
|
||||
console.debug('dismissAllModals error', e);
|
||||
}
|
||||
navigation.navigate('WalletsAddMultisigHelp');
|
||||
};
|
||||
|
||||
const stylesHook = StyleSheet.create({
|
||||
@ -139,13 +129,13 @@ const WalletsAddMultisigStep2 = () => {
|
||||
|
||||
const onCreate = async () => {
|
||||
setIsLoading(true);
|
||||
setOptions({ headerBackVisible: false });
|
||||
navigation.setOptions({ headerBackVisible: false });
|
||||
await sleep(100);
|
||||
try {
|
||||
await _onCreate(); // this can fail with "Duplicate fingerprint" error or other
|
||||
} catch (e) {
|
||||
setIsLoading(false);
|
||||
setOptions({ headerBackVisible: true });
|
||||
navigation.setOptions({ headerBackVisible: true });
|
||||
presentAlert({ message: e.message });
|
||||
console.log('create MS wallet error', e);
|
||||
}
|
||||
@ -181,30 +171,10 @@ const WalletsAddMultisigStep2 = () => {
|
||||
await w.fetchBalance();
|
||||
}
|
||||
|
||||
addWallet(w);
|
||||
await saveToDisk();
|
||||
addAndSaveWallet(w);
|
||||
A(A.ENUM.CREATED_WALLET);
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
navigateToWalletsList();
|
||||
};
|
||||
|
||||
const generateNewKey = () => {
|
||||
const w = new HDSegwitBech32Wallet();
|
||||
w.generate().then(() => {
|
||||
const cosignersCopy = [...cosigners];
|
||||
cosignersCopy.push([w.getSecret(), false, false]);
|
||||
if (Platform.OS !== 'android') LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
setCosigners(cosignersCopy);
|
||||
setVaultKeyData({ keyIndex: cosignersCopy.length, seed: w.getSecret(), xpub: w.getXpub(), isLoading: false });
|
||||
setIsLoading(true);
|
||||
mnemonicsModalRef.current.present();
|
||||
setTimeout(() => {
|
||||
// filling cache
|
||||
setXpubCacheForMnemonics(w.getSecret());
|
||||
setFpCacheForMnemonics(w.getSecret());
|
||||
setIsLoading(false);
|
||||
}, 500);
|
||||
});
|
||||
navigation.getParent()?.goBack();
|
||||
};
|
||||
|
||||
const getPath = useCallback(() => {
|
||||
@ -227,6 +197,36 @@ const WalletsAddMultisigStep2 = () => {
|
||||
return path;
|
||||
}, [format]);
|
||||
|
||||
const setXpubCacheForMnemonics = useCallback(
|
||||
(seed, passphrase) => {
|
||||
const path = getPath();
|
||||
const w = new MultisigHDWallet();
|
||||
w.setDerivationPath(path);
|
||||
staticCache[seed + path + passphrase] = w.convertXpubToMultisignatureXpub(MultisigHDWallet.seedToXpub(seed, path, passphrase));
|
||||
return staticCache[seed + path + passphrase];
|
||||
},
|
||||
[getPath],
|
||||
);
|
||||
|
||||
const generateNewKey = () => {
|
||||
const w = new HDSegwitBech32Wallet();
|
||||
w.generate().then(() => {
|
||||
const cosignersCopy = [...cosigners];
|
||||
cosignersCopy.push([w.getSecret(), false, false]);
|
||||
if (Platform.OS !== 'android') LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
setCosigners(cosignersCopy);
|
||||
setVaultKeyData({ keyIndex: cosignersCopy.length, seed: w.getSecret(), xpub: w.getXpub(), isLoading: false });
|
||||
setIsLoading(true);
|
||||
mnemonicsModalRef.current.present();
|
||||
setTimeout(() => {
|
||||
// filling cache
|
||||
setXpubCacheForMnemonics(w.getSecret());
|
||||
setFpCacheForMnemonics(w.getSecret());
|
||||
setIsLoading(false);
|
||||
}, 500);
|
||||
});
|
||||
};
|
||||
|
||||
const viewKey = cosigner => {
|
||||
if (MultisigHDWallet.isXpubValid(cosigner[0])) {
|
||||
setCosignerXpub(MultisigCosigner.exportToJson(cosigner[1], cosigner[0], cosigner[2]));
|
||||
@ -245,18 +245,13 @@ const WalletsAddMultisigStep2 = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const getXpubCacheForMnemonics = (seed, passphrase) => {
|
||||
const path = getPath();
|
||||
return staticCache[seed + path + passphrase] || setXpubCacheForMnemonics(seed, passphrase);
|
||||
};
|
||||
|
||||
const setXpubCacheForMnemonics = (seed, passphrase) => {
|
||||
const path = getPath();
|
||||
const w = new MultisigHDWallet();
|
||||
w.setDerivationPath(path);
|
||||
staticCache[seed + path + passphrase] = w.convertXpubToMultisignatureXpub(MultisigHDWallet.seedToXpub(seed, path, passphrase));
|
||||
return staticCache[seed + path + passphrase];
|
||||
};
|
||||
const getXpubCacheForMnemonics = useCallback(
|
||||
(seed, passphrase) => {
|
||||
const path = getPath();
|
||||
return staticCache[seed + path + passphrase] || setXpubCacheForMnemonics(seed, passphrase);
|
||||
},
|
||||
[getPath, setXpubCacheForMnemonics],
|
||||
);
|
||||
|
||||
const getFpCacheForMnemonics = (seed, passphrase) => {
|
||||
return staticCache[seed + (passphrase ?? '')] || setFpCacheForMnemonics(seed, passphrase);
|
||||
@ -274,7 +269,6 @@ const WalletsAddMultisigStep2 = () => {
|
||||
const tryUsingXpub = useCallback(
|
||||
async (xpub, fp, path) => {
|
||||
if (!MultisigHDWallet.isXpubForMultisig(xpub)) {
|
||||
provideMnemonicsModalRef.current.dismiss();
|
||||
setIsLoading(false);
|
||||
setImportText('');
|
||||
setAskPassphrase(false);
|
||||
@ -308,7 +302,6 @@ const WalletsAddMultisigStep2 = () => {
|
||||
}
|
||||
}
|
||||
|
||||
provideMnemonicsModalRef.current.dismiss();
|
||||
setIsLoading(false);
|
||||
setImportText('');
|
||||
setAskPassphrase(false);
|
||||
@ -321,7 +314,134 @@ const WalletsAddMultisigStep2 = () => {
|
||||
[cosigners, getPath],
|
||||
);
|
||||
|
||||
const useMnemonicPhrase = async () => {
|
||||
const isValidMnemonicSeed = mnemonicSeed => {
|
||||
const hd = new HDSegwitBech32Wallet();
|
||||
hd.setSecret(mnemonicSeed);
|
||||
return hd.validateMnemonic();
|
||||
};
|
||||
|
||||
const onBarScanned = useCallback(
|
||||
async ret => {
|
||||
if (!ret.data) ret = { data: ret };
|
||||
|
||||
try {
|
||||
let retData = JSON.parse(ret.data);
|
||||
if (Array.isArray(retData) && retData.length === 1) {
|
||||
// UR:CRYPTO-ACCOUNT now parses as an array of accounts, even if it is just one,
|
||||
// so in case of cosigner data its gona be an array of 1 cosigner account. lets pop it for
|
||||
// the code that expects it
|
||||
retData = retData.pop();
|
||||
ret.data = JSON.stringify(retData);
|
||||
}
|
||||
} catch (e) {
|
||||
console.debug('JSON parsing failed for ret.data:', e);
|
||||
}
|
||||
|
||||
if (ret.data.toUpperCase().startsWith('UR')) {
|
||||
presentAlert({ message: 'BC-UR not decoded. This should never happen' });
|
||||
} else if (isValidMnemonicSeed(ret.data)) {
|
||||
setImportText(ret.data);
|
||||
setTimeout(async () => {
|
||||
await provideMnemonicsModalRef.current.present();
|
||||
}, 100);
|
||||
} else {
|
||||
if (MultisigHDWallet.isXpubValid(ret.data) && !MultisigHDWallet.isXpubForMultisig(ret.data)) {
|
||||
return presentAlert({ message: loc.multisig.not_a_multisignature_xpub });
|
||||
}
|
||||
if (MultisigHDWallet.isXpubValid(ret.data)) {
|
||||
return tryUsingXpub(ret.data);
|
||||
}
|
||||
let cosigner = new MultisigCosigner(ret.data);
|
||||
if (!cosigner.isValid()) {
|
||||
return presentAlert({ message: loc.multisig.invalid_cosigner });
|
||||
}
|
||||
|
||||
if (cosigner.howManyCosignersWeHave() > 1) {
|
||||
// lets look for the correct cosigner. thats probably gona be the one with specific corresponding path,
|
||||
// for example m/48'/0'/0'/2' if user chose to setup native segwit in BW
|
||||
for (const cc of cosigner.getAllCosigners()) {
|
||||
switch (format) {
|
||||
case MultisigHDWallet.FORMAT_P2WSH:
|
||||
if (cc.getPath().startsWith('m/48') && cc.getPath().endsWith("/2'")) {
|
||||
// found it
|
||||
cosigner = cc;
|
||||
}
|
||||
break;
|
||||
case MultisigHDWallet.FORMAT_P2SH_P2WSH:
|
||||
case MultisigHDWallet.FORMAT_P2SH_P2WSH_ALT:
|
||||
if (cc.getPath().startsWith('m/48') && cc.getPath().endsWith("/1'")) {
|
||||
// found it
|
||||
cosigner = cc;
|
||||
}
|
||||
break;
|
||||
case MultisigHDWallet.FORMAT_P2SH:
|
||||
if (cc.getPath().startsWith('m/45')) {
|
||||
// found it
|
||||
cosigner = cc;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.error('Unexpected format:', format);
|
||||
throw new Error('This should never happen');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const existingCosigner of cosigners) {
|
||||
let existingXpub = existingCosigner[0];
|
||||
if (!MultisigHDWallet.isXpubValid(existingXpub)) {
|
||||
// derive the xpub from mnemonic-based cosigner
|
||||
existingXpub = getXpubCacheForMnemonics(existingCosigner[0], existingCosigner[3]);
|
||||
}
|
||||
if (existingXpub === cosigner.getXpub()) {
|
||||
return presentAlert({ message: loc.multisig.this_cosigner_is_already_imported });
|
||||
}
|
||||
}
|
||||
// now, validating that cosigner is in correct format:
|
||||
|
||||
let correctFormat = false;
|
||||
switch (format) {
|
||||
case MultisigHDWallet.FORMAT_P2WSH:
|
||||
if (cosigner.getPath().startsWith('m/48') && cosigner.getPath().endsWith("/2'")) {
|
||||
correctFormat = true;
|
||||
}
|
||||
break;
|
||||
case MultisigHDWallet.FORMAT_P2SH_P2WSH:
|
||||
case MultisigHDWallet.FORMAT_P2SH_P2WSH_ALT:
|
||||
if (cosigner.getPath().startsWith('m/48') && cosigner.getPath().endsWith("/1'")) {
|
||||
correctFormat = true;
|
||||
}
|
||||
break;
|
||||
case MultisigHDWallet.FORMAT_P2SH:
|
||||
if (cosigner.getPath().startsWith('m/45')) {
|
||||
correctFormat = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.error('Unexpected format:', format);
|
||||
throw new Error('This should never happen');
|
||||
}
|
||||
if (!correctFormat) {
|
||||
return presentAlert({ message: loc.formatString(loc.multisig.invalid_cosigner_format, { format }) });
|
||||
}
|
||||
const cosignersCopy = [...cosigners];
|
||||
cosignersCopy.push([cosigner.getXpub(), cosigner.getFp(), cosigner.getPath()]);
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
setCosigners(cosignersCopy);
|
||||
}
|
||||
},
|
||||
[cosigners, format, getXpubCacheForMnemonics, tryUsingXpub],
|
||||
);
|
||||
|
||||
const scanOrOpenFile = async () => {
|
||||
await provideMnemonicsModalRef.current.dismiss();
|
||||
navigation.navigate('ScanQRCode', { showFileImportButton: true });
|
||||
};
|
||||
|
||||
const utilizeMnemonicPhrase = useCallback(async () => {
|
||||
try {
|
||||
await provideMnemonicsModalRef.current.dismiss();
|
||||
} catch {}
|
||||
setIsLoading(true);
|
||||
|
||||
if (MultisigHDWallet.isXpubValid(importText)) {
|
||||
@ -366,133 +486,21 @@ const WalletsAddMultisigStep2 = () => {
|
||||
if (Platform.OS !== 'android') LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
setCosigners(cosignersCopy);
|
||||
|
||||
provideMnemonicsModalRef.current.dismiss();
|
||||
setIsLoading(false);
|
||||
setImportText('');
|
||||
setAskPassphrase(false);
|
||||
};
|
||||
|
||||
const isValidMnemonicSeed = mnemonicSeed => {
|
||||
const hd = new HDSegwitBech32Wallet();
|
||||
hd.setSecret(mnemonicSeed);
|
||||
return hd.validateMnemonic();
|
||||
};
|
||||
|
||||
const onBarScanned = useCallback(
|
||||
ret => {
|
||||
if (!ret.data) ret = { data: ret };
|
||||
|
||||
try {
|
||||
let retData = JSON.parse(ret.data);
|
||||
if (Array.isArray(retData) && retData.length === 1) {
|
||||
// UR:CRYPTO-ACCOUNT now parses as an array of accounts, even if it is just one,
|
||||
// so in case of cosigner data its gona be an array of 1 cosigner account. lets pop it for
|
||||
// the code that expects it
|
||||
retData = retData.pop();
|
||||
ret.data = JSON.stringify(retData);
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
if (ret.data.toUpperCase().startsWith('UR')) {
|
||||
presentAlert({ message: 'BC-UR not decoded. This should never happen' });
|
||||
} else if (isValidMnemonicSeed(ret.data)) {
|
||||
setImportText(ret.data);
|
||||
setTimeout(() => {
|
||||
provideMnemonicsModalRef.current.present().then(() => {});
|
||||
}, 100);
|
||||
} else {
|
||||
if (MultisigHDWallet.isXpubValid(ret.data) && !MultisigHDWallet.isXpubForMultisig(ret.data)) {
|
||||
return presentAlert({ message: loc.multisig.not_a_multisignature_xpub });
|
||||
}
|
||||
if (MultisigHDWallet.isXpubValid(ret.data)) {
|
||||
return tryUsingXpub(ret.data);
|
||||
}
|
||||
let cosigner = new MultisigCosigner(ret.data);
|
||||
if (!cosigner.isValid()) return presentAlert({ message: loc.multisig.invalid_cosigner });
|
||||
provideMnemonicsModalRef.current.dismiss();
|
||||
if (cosigner.howManyCosignersWeHave() > 1) {
|
||||
// lets look for the correct cosigner. thats probably gona be the one with specific corresponding path,
|
||||
// for example m/48'/0'/0'/2' if user chose to setup native segwit in BW
|
||||
for (const cc of cosigner.getAllCosigners()) {
|
||||
switch (format) {
|
||||
case MultisigHDWallet.FORMAT_P2WSH:
|
||||
if (cc.getPath().startsWith('m/48') && cc.getPath().endsWith("/2'")) {
|
||||
// found it
|
||||
cosigner = cc;
|
||||
}
|
||||
break;
|
||||
case MultisigHDWallet.FORMAT_P2SH_P2WSH:
|
||||
case MultisigHDWallet.FORMAT_P2SH_P2WSH_ALT:
|
||||
if (cc.getPath().startsWith('m/48') && cc.getPath().endsWith("/1'")) {
|
||||
// found it
|
||||
cosigner = cc;
|
||||
}
|
||||
break;
|
||||
case MultisigHDWallet.FORMAT_P2SH:
|
||||
if (cc.getPath().startsWith('m/45')) {
|
||||
// found it
|
||||
cosigner = cc;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.error('Unexpected format:', format);
|
||||
throw new Error('This should never happen');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const existingCosigner of cosigners) {
|
||||
if (existingCosigner[0] === cosigner.getXpub()) return presentAlert({ message: loc.multisig.this_cosigner_is_already_imported });
|
||||
}
|
||||
|
||||
// now, validating that cosigner is in correct format:
|
||||
|
||||
let correctFormat = false;
|
||||
switch (format) {
|
||||
case MultisigHDWallet.FORMAT_P2WSH:
|
||||
if (cosigner.getPath().startsWith('m/48') && cosigner.getPath().endsWith("/2'")) {
|
||||
correctFormat = true;
|
||||
}
|
||||
break;
|
||||
case MultisigHDWallet.FORMAT_P2SH_P2WSH:
|
||||
case MultisigHDWallet.FORMAT_P2SH_P2WSH_ALT:
|
||||
if (cosigner.getPath().startsWith('m/48') && cosigner.getPath().endsWith("/1'")) {
|
||||
correctFormat = true;
|
||||
}
|
||||
break;
|
||||
case MultisigHDWallet.FORMAT_P2SH:
|
||||
if (cosigner.getPath().startsWith('m/45')) {
|
||||
correctFormat = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.error('Unexpected format:', format);
|
||||
throw new Error('This should never happen');
|
||||
}
|
||||
|
||||
if (!correctFormat) return presentAlert({ message: loc.formatString(loc.multisig.invalid_cosigner_format, { format }) });
|
||||
|
||||
const cosignersCopy = [...cosigners];
|
||||
cosignersCopy.push([cosigner.getXpub(), cosigner.getFp(), cosigner.getPath()]);
|
||||
if (Platform.OS !== 'android') LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
setCosigners(cosignersCopy);
|
||||
}
|
||||
},
|
||||
[cosigners, format, tryUsingXpub],
|
||||
);
|
||||
|
||||
const scanOrOpenFile = async () => {
|
||||
await provideMnemonicsModalRef.current.dismiss();
|
||||
navigate('ScanQRCode');
|
||||
};
|
||||
}, [askPassphrase, cosigners, importText, tryUsingXpub]);
|
||||
|
||||
useEffect(() => {
|
||||
const scannedData = params.onBarScanned;
|
||||
if (scannedData) {
|
||||
onBarScanned(scannedData);
|
||||
setParams({ onBarScanned: undefined });
|
||||
}
|
||||
}, [onBarScanned, params.onBarScanned, setParams]);
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
const scannedData = params.onBarScanned;
|
||||
if (scannedData) {
|
||||
onBarScanned(scannedData);
|
||||
navigation.setParams({ onBarScanned: undefined });
|
||||
}
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [navigation, params.onBarScanned]);
|
||||
|
||||
const dashType = ({ index, lastIndex, isChecked, isFocus }) => {
|
||||
if (isChecked) {
|
||||
@ -588,6 +596,10 @@ const WalletsAddMultisigStep2 = () => {
|
||||
return component;
|
||||
};
|
||||
|
||||
const dismissMnemonicsModal = async () => {
|
||||
await mnemonicsModalRef.current.dismiss();
|
||||
};
|
||||
|
||||
const renderMnemonicsModal = () => {
|
||||
return (
|
||||
<BottomModal
|
||||
@ -599,11 +611,7 @@ const WalletsAddMultisigStep2 = () => {
|
||||
backgroundColor={colors.modal}
|
||||
footer={
|
||||
<View style={styles.modalFooterBottomPadding}>
|
||||
{isLoading ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<Button title={loc.send.success_done} onPress={() => mnemonicsModalRef.current.dismiss()} />
|
||||
)}
|
||||
{isLoading ? <ActivityIndicator /> : <Button title={loc.send.success_done} onPress={dismissMnemonicsModal} />}
|
||||
</View>
|
||||
}
|
||||
>
|
||||
@ -635,38 +643,40 @@ const WalletsAddMultisigStep2 = () => {
|
||||
}, [askPassphrase]);
|
||||
|
||||
const renderProvideMnemonicsModal = () => {
|
||||
const opacity = isVisible ? 0 : 1;
|
||||
return (
|
||||
<BottomModal
|
||||
footer={
|
||||
!isVisible && (
|
||||
<View style={styles.modalFooterBottomPadding}>
|
||||
{isLoading ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
testID="DoImportKeyButton"
|
||||
disabled={importText.trim().length === 0}
|
||||
title={loc.wallets.import_do_import}
|
||||
onPress={useMnemonicPhrase}
|
||||
/>
|
||||
<BlueButtonLink
|
||||
testID="ScanOrOpenFile"
|
||||
ref={openScannerButton}
|
||||
disabled={isLoading}
|
||||
onPress={scanOrOpenFile}
|
||||
title={loc.wallets.import_scan_qr}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
<View style={[styles.modalFooterBottomPadding, { opacity }]} pointerEvents={isVisible ? 'none' : 'auto'}>
|
||||
{isLoading ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
testID="DoImportKeyButton"
|
||||
disabled={importText.trim().length === 0}
|
||||
title={loc.wallets.import_do_import}
|
||||
onPress={utilizeMnemonicPhrase}
|
||||
/>
|
||||
<View style={styles.height16} />
|
||||
<BlueButtonLink
|
||||
testID="ScanOrOpenFile"
|
||||
ref={openScannerButton}
|
||||
disabled={isLoading}
|
||||
onPress={scanOrOpenFile}
|
||||
title={loc.wallets.import_scan_qr}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
}
|
||||
keyboardMode="auto"
|
||||
ref={provideMnemonicsModalRef}
|
||||
backgroundColor={colors.modal}
|
||||
contentContainerStyle={styles.provideMnemonicsModalStyle}
|
||||
isGrabberVisible={false}
|
||||
showCloseButton={true}
|
||||
sizes={[Platform.OS === 'ios' ? 'auto' : '80%']}
|
||||
sizes={[Platform.OS === 'ios' ? 'auto' : 420]}
|
||||
onDismiss={() => {
|
||||
Keyboard.dismiss();
|
||||
setImportText('');
|
||||
@ -696,8 +706,15 @@ const WalletsAddMultisigStep2 = () => {
|
||||
inputAccessoryViewID={DoneAndDismissKeyboardInputAccessoryViewID}
|
||||
/>
|
||||
{Platform.select({
|
||||
ios: <DoneAndDismissKeyboardInputAccessory />,
|
||||
android: isVisible && <DoneAndDismissKeyboardInputAccessory />,
|
||||
ios: (
|
||||
<DoneAndDismissKeyboardInputAccessory
|
||||
onClearTapped={() => setImportText('')}
|
||||
onPasteTapped={async () => {
|
||||
const paste = await Clipboard.getString();
|
||||
setImportText(paste);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
})}
|
||||
|
||||
<BlueSpacing20 />
|
||||
@ -765,7 +782,7 @@ const WalletsAddMultisigStep2 = () => {
|
||||
<View style={[styles.root, stylesHook.root]}>
|
||||
{renderHelp()}
|
||||
<View style={styles.wrapBox}>
|
||||
<FlatList data={data.current} renderItem={_renderKeyItem} keyExtractor={(_item, index) => `${index}`} />
|
||||
<FlatList data={data.current} renderItem={_renderKeyItem} keyExtractor={(_item, index) => `${index}`} extraData={cosigners} />
|
||||
</View>
|
||||
{renderMnemonicsModal()}
|
||||
|
||||
@ -773,6 +790,7 @@ const WalletsAddMultisigStep2 = () => {
|
||||
|
||||
{renderCosignersXpubModal()}
|
||||
{footer}
|
||||
<BlueSpacing20 />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@ -786,6 +804,9 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
marginVertical: 24,
|
||||
},
|
||||
height16: {
|
||||
height: 16,
|
||||
},
|
||||
buttonBottom: {
|
||||
marginHorizontal: 20,
|
||||
flex: 0.12,
|
||||
@ -853,6 +874,9 @@ const styles = StyleSheet.create({
|
||||
fontWeight: 'bold',
|
||||
marginLeft: 8,
|
||||
},
|
||||
provideMnemonicsModalStyle: {
|
||||
minHeight: 420,
|
||||
},
|
||||
});
|
||||
|
||||
export default WalletsAddMultisigStep2;
|
||||
|
||||
@ -22,10 +22,13 @@ const PleaseBackupLNDHub = () => {
|
||||
const [qrCodeSize, setQRCodeSize] = useState(90);
|
||||
const { isPrivacyBlurEnabled } = useSettings();
|
||||
|
||||
const handleBackButton = useCallback(() => {
|
||||
navigation.getParent().pop();
|
||||
return true;
|
||||
const dismiss = useCallback(() => {
|
||||
navigation.getParent().goBack();
|
||||
}, [navigation]);
|
||||
const handleBackButton = useCallback(() => {
|
||||
dismiss();
|
||||
return true;
|
||||
}, [dismiss]);
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
backgroundColor: colors.elevated,
|
||||
@ -49,8 +52,6 @@ const PleaseBackupLNDHub = () => {
|
||||
};
|
||||
}, [handleBackButton, isPrivacyBlurEnabled]);
|
||||
|
||||
const pop = () => navigation.getParent().pop();
|
||||
|
||||
const onLayout = e => {
|
||||
const { height, width } = e.nativeEvent.layout;
|
||||
setQRCodeSize(height > width ? width - 40 : e.nativeEvent.layout.width / 1.5);
|
||||
@ -66,7 +67,7 @@ const PleaseBackupLNDHub = () => {
|
||||
<QRCodeComponent value={wallet.getSecret()} size={qrCodeSize} />
|
||||
<CopyTextToClipboard text={wallet.getSecret()} />
|
||||
<BlueSpacing20 />
|
||||
<Button onPress={pop} title={loc.pleasebackup.ok_lnd} />
|
||||
<Button onPress={dismiss} title={loc.pleasebackup.ok_lnd} />
|
||||
</ScrollView>
|
||||
</SafeArea>
|
||||
);
|
||||
|
||||
@ -1,9 +1,29 @@
|
||||
import assert from 'assert';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
|
||||
import { expectToBeVisible, extractTextFromElementById, hashIt, helperCreateWallet, helperDeleteWallet, sleep, sup, yo } from './helperz';
|
||||
import {
|
||||
expectToBeVisible,
|
||||
extractTextFromElementById,
|
||||
hashIt,
|
||||
helperCreateWallet,
|
||||
helperDeleteWallet,
|
||||
sleep,
|
||||
sup,
|
||||
tapAndTapAgainIfElementIsNotVisible,
|
||||
tapIfPresent,
|
||||
tapIfTextPresent,
|
||||
yo,
|
||||
} from './helperz';
|
||||
import { element } from 'detox';
|
||||
|
||||
// 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');
|
||||
};
|
||||
|
||||
/**
|
||||
* this testsuite is for test cases that require no wallets to be present
|
||||
*/
|
||||
@ -22,7 +42,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
await element(by.id('SettingsButton')).tap();
|
||||
await element(by.id('AboutButton')).tap();
|
||||
await element(by.id('AboutScrollView')).swipe('up', 'fast', 1); // in case emu screen is small and it doesnt fit
|
||||
await element(by.id('RunSelfTestButton')).tap();
|
||||
await tapAndTapAgainIfElementIsNotVisible('RunSelfTestButton', 'SelfTestLoading');
|
||||
await waitFor(element(by.id('SelfTestOk')))
|
||||
.toBeVisible()
|
||||
.withTimeout(300 * 1000);
|
||||
@ -132,8 +152,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
if (await expectToBeVisible('NotificationSettings')) {
|
||||
await element(by.id('NotificationSettings')).tap();
|
||||
await element(by.id('NotificationsSwitch')).tap();
|
||||
await sup('OK');
|
||||
await element(by.text('OK')).tap();
|
||||
await sleep(3_000);
|
||||
await element(by.id('NotificationsSwitch')).tap();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
@ -183,8 +202,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
await device.launchApp({ newInstance: true });
|
||||
await yo('WalletsList');
|
||||
await expect(element(by.id('cr34t3d'))).toBeVisible();
|
||||
await element(by.id('cr34t3d')).tap();
|
||||
await yo('ReceiveButton');
|
||||
await tapAndTapAgainIfElementIsNotVisible('cr34t3d', 'ReceiveButton');
|
||||
await element(by.id('ReceiveButton')).tap();
|
||||
await element(by.text('Yes, I have.')).tap();
|
||||
try {
|
||||
@ -197,9 +215,9 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
await yo('CopyTextToClipboard');
|
||||
await element(by.id('SetCustomAmountButton')).tap();
|
||||
await element(by.id('BitcoinAmountInput')).replaceText('1');
|
||||
await element(by.id('CustomAmountDescription')).typeText('test');
|
||||
await element(by.id('CustomAmountDescription')).replaceText('test');
|
||||
await element(by.id('CustomAmountDescription')).tapReturnKey();
|
||||
await element(by.id('CustomAmountSaveButton')).tap();
|
||||
await tapAndTapAgainIfElementIsNotVisible('CustomAmountSaveButton', 'CustomAmountDescriptionText');
|
||||
await expect(element(by.id('CustomAmountDescriptionText'))).toHaveText('test');
|
||||
await expect(element(by.id('BitcoinAmountText'))).toHaveText('1 BTC');
|
||||
|
||||
@ -239,35 +257,33 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
|
||||
// lets encrypt the storage.
|
||||
// first, trying to mistype second password:
|
||||
await element(by.type('android.widget.CompoundButton')).tap(); // thats a switch lol. lets tap it
|
||||
await element(by.id('EncyptedAndPasswordProtectedSwitch')).tap();
|
||||
await element(by.id('IUnderstandButton')).tap();
|
||||
if (process.env.TRAVIS) await sleep(3000); // hopefully helps prevent crash
|
||||
|
||||
await element(by.id('PasswordInput')).typeText('08902');
|
||||
await element(by.id('PasswordInput')).replaceText('08902');
|
||||
await element(by.id('PasswordInput')).tapReturnKey();
|
||||
await element(by.id('ConfirmPasswordInput')).typeText('666');
|
||||
await element(by.id('ConfirmPasswordInput')).replaceText('666');
|
||||
await element(by.id('ConfirmPasswordInput')).tapReturnKey();
|
||||
await element(by.id('OKButton')).tap();
|
||||
if (process.env.TRAVIS) await sleep(3000); // hopefully helps prevent crash
|
||||
|
||||
// now, lets put correct passwords and encrypt the storage
|
||||
await element(by.id('PasswordInput')).clearText();
|
||||
await element(by.id('PasswordInput')).typeText('qqq');
|
||||
await element(by.id('PasswordInput')).replaceText('qqq');
|
||||
await element(by.id('PasswordInput')).tapReturnKey();
|
||||
await element(by.id('ConfirmPasswordInput')).clearText();
|
||||
await element(by.id('ConfirmPasswordInput')).typeText('qqq');
|
||||
await element(by.id('ConfirmPasswordInput')).replaceText('qqq');
|
||||
await element(by.id('ConfirmPasswordInput')).tapReturnKey();
|
||||
await element(by.id('OKButton')).tap();
|
||||
if (process.env.TRAVIS) await sleep(3000); // hopefully helps prevent crash
|
||||
await tapIfPresent('OKButton'); // might not always work the first time
|
||||
await sleep(3000); // propagate
|
||||
|
||||
// relaunch app
|
||||
await device.launchApp({ newInstance: true });
|
||||
await waitFor(element(by.text('OK')))
|
||||
.toBeVisible()
|
||||
.withTimeout(33000);
|
||||
|
||||
// trying to decrypt with incorrect password
|
||||
await expect(element(by.text('Your storage is encrypted. Password is required to decrypt it.'))).toBeVisible();
|
||||
await sup('Your storage is encrypted. Password is required to decrypt it.');
|
||||
await element(by.type('android.widget.EditText')).typeText('wrong');
|
||||
await element(by.text('OK')).tap();
|
||||
await expect(element(by.text('Incorrect password. Please try again.'))).toBeVisible();
|
||||
@ -295,12 +311,13 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
if (process.env.TRAVIS) await sleep(3000); // hopefully helps prevent crash
|
||||
|
||||
// trying MAIN password: should fail, obviously
|
||||
await element(by.id('PasswordInput')).typeText('qqq');
|
||||
await element(by.id('PasswordInput')).replaceText('qqq');
|
||||
await element(by.id('PasswordInput')).tapReturnKey();
|
||||
await element(by.id('ConfirmPasswordInput')).typeText('qqq');
|
||||
await element(by.id('ConfirmPasswordInput')).replaceText('qqq');
|
||||
await element(by.id('ConfirmPasswordInput')).tapReturnKey();
|
||||
await element(by.id('OKButton')).tap();
|
||||
if (process.env.TRAVIS) await sleep(3000); // hopefully helps prevent crash
|
||||
await tapIfPresent('OKButton'); // first time might not always work
|
||||
await sleep(3000); // propagate
|
||||
await expect(element(by.text('Password is currently in use. Please try a different password.'))).toBeVisible();
|
||||
if (process.env.TRAVIS) await sleep(3000); // hopefully helps prevent crash
|
||||
await element(by.text('OK')).tap();
|
||||
@ -309,23 +326,24 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
// trying new password, but will mistype
|
||||
if (process.env.TRAVIS) await sleep(3000); // hopefully helps prevent crash
|
||||
await element(by.id('PasswordInput')).clearText();
|
||||
await element(by.id('PasswordInput')).typeText('passwordForFakeStorage');
|
||||
await element(by.id('PasswordInput')).replaceText('passwordForFakeStorage');
|
||||
await element(by.id('PasswordInput')).tapReturnKey();
|
||||
await element(by.id('ConfirmPasswordInput')).clearText();
|
||||
await element(by.id('ConfirmPasswordInput')).typeText('passwordForFakeStorageWithTypo'); // retyping with typo
|
||||
await element(by.id('ConfirmPasswordInput')).replaceText('passwordForFakeStorageWithTypo'); // retyping with typo
|
||||
await element(by.id('ConfirmPasswordInput')).tapReturnKey();
|
||||
await element(by.id('OKButton')).tap();
|
||||
if (process.env.TRAVIS) await sleep(3000); // hopefully helps prevent crash
|
||||
|
||||
// trying new password
|
||||
await element(by.id('PasswordInput')).clearText();
|
||||
await element(by.id('PasswordInput')).typeText('passwordForFakeStorage');
|
||||
await element(by.id('PasswordInput')).replaceText('passwordForFakeStorage');
|
||||
await element(by.id('PasswordInput')).tapReturnKey();
|
||||
await element(by.id('ConfirmPasswordInput')).clearText();
|
||||
await element(by.id('ConfirmPasswordInput')).typeText('passwordForFakeStorage'); // retyping
|
||||
await element(by.id('ConfirmPasswordInput')).replaceText('passwordForFakeStorage'); // retyping
|
||||
await element(by.id('ConfirmPasswordInput')).tapReturnKey();
|
||||
await element(by.id('OKButton')).tap();
|
||||
if (process.env.TRAVIS) await sleep(3000); // hopefully helps prevent crash
|
||||
await tapIfPresent('OKButton'); // first time might not always work
|
||||
await sleep(3_000); // propagate
|
||||
|
||||
// created fake storage.
|
||||
// creating a wallet inside this fake storage
|
||||
@ -335,11 +353,8 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
|
||||
// relaunch app
|
||||
await device.launchApp({ newInstance: true });
|
||||
await waitFor(element(by.text('OK')))
|
||||
.toBeVisible()
|
||||
.withTimeout(33000);
|
||||
//
|
||||
await expect(element(by.text('Your storage is encrypted. Password is required to decrypt it.'))).toBeVisible();
|
||||
await sup('Your storage is encrypted. Password is required to decrypt it.');
|
||||
await element(by.type('android.widget.EditText')).typeText('qqq');
|
||||
await element(by.text('OK')).tap();
|
||||
await yo('WalletsList');
|
||||
@ -349,9 +364,8 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
|
||||
// relaunch app
|
||||
await device.launchApp({ newInstance: true });
|
||||
await sleep(3000);
|
||||
//
|
||||
await expect(element(by.text('Your storage is encrypted. Password is required to decrypt it.'))).toBeVisible();
|
||||
await sup('Your storage is encrypted. Password is required to decrypt it.');
|
||||
await element(by.type('android.widget.EditText')).typeText('passwordForFakeStorage');
|
||||
await element(by.text('OK')).tap();
|
||||
await yo('WalletsList');
|
||||
@ -364,12 +378,13 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
await element(by.id('SecurityButton')).tap();
|
||||
|
||||
// correct password
|
||||
await element(by.type('android.widget.CompoundButton')).tap(); // thats a switch lol
|
||||
await element(by.id('EncyptedAndPasswordProtectedSwitch')).tap();
|
||||
await element(by.text('OK')).tap();
|
||||
await element(by.id('PasswordInput')).typeText('passwordForFakeStorage');
|
||||
await element(by.id('PasswordInput')).replaceText('passwordForFakeStorage');
|
||||
await element(by.id('PasswordInput')).tapReturnKey();
|
||||
await element(by.id('OKButton')).tap();
|
||||
if (process.env.TRAVIS) await sleep(3000); // hopefully helps prevent crash
|
||||
await tapIfPresent('OKButton'); // in case it didnt work first time
|
||||
await sleep(3000); // propagate
|
||||
|
||||
await helperDeleteWallet('fake_wallet');
|
||||
|
||||
@ -393,38 +408,37 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
|
||||
// lets encrypt the storage.
|
||||
// lets put correct passwords and encrypt the storage
|
||||
await element(by.type('android.widget.CompoundButton')).tap(); // thats a switch lol
|
||||
await element(by.id('EncyptedAndPasswordProtectedSwitch')).tap();
|
||||
if (process.env.TRAVIS) await sleep(3000); // hopefully helps prevent crash
|
||||
await element(by.id('IUnderstandButton')).tap();
|
||||
if (process.env.TRAVIS) await sleep(3000); // hopefully helps prevent crash
|
||||
await element(by.id('PasswordInput')).typeText('pass');
|
||||
await element(by.id('PasswordInput')).replaceText('pass');
|
||||
await element(by.id('PasswordInput')).tapReturnKey();
|
||||
await element(by.id('ConfirmPasswordInput')).typeText('pass');
|
||||
await element(by.id('ConfirmPasswordInput')).replaceText('pass');
|
||||
await element(by.id('ConfirmPasswordInput')).tapReturnKey();
|
||||
await element(by.id('OKButton')).tap();
|
||||
if (process.env.TRAVIS) await sleep(3000); // hopefully helps prevent crash
|
||||
await tapIfPresent('OKButton'); // might not always work first time
|
||||
await sleep(3000); // propagate
|
||||
await element(by.id('PlausibleDeniabilityButton')).tap();
|
||||
|
||||
// trying to enable plausible denability
|
||||
await element(by.id('CreateFakeStorageButton')).tap();
|
||||
if (process.env.TRAVIS) await sleep(3000); // hopefully helps prevent crash
|
||||
await element(by.id('PasswordInput')).typeText('fake');
|
||||
await element(by.id('PasswordInput')).replaceText('fake');
|
||||
await element(by.id('PasswordInput')).tapReturnKey();
|
||||
await element(by.id('ConfirmPasswordInput')).typeText('fake'); // retyping
|
||||
await element(by.id('ConfirmPasswordInput')).replaceText('fake'); // retyping
|
||||
await element(by.id('ConfirmPasswordInput')).tapReturnKey();
|
||||
await element(by.id('OKButton')).tap();
|
||||
if (process.env.TRAVIS) await sleep(3000); // hopefully helps prevent crash
|
||||
await tapIfPresent('OKButton'); // might not always work first time
|
||||
await sleep(3000); // propagate
|
||||
// created fake storage.
|
||||
// creating a wallet inside this fake storage
|
||||
await helperCreateWallet('fake_wallet');
|
||||
|
||||
// relaunch app
|
||||
await device.launchApp({ newInstance: true });
|
||||
await waitFor(element(by.text('OK')))
|
||||
.toBeVisible()
|
||||
.withTimeout(33000);
|
||||
//
|
||||
await expect(element(by.text('Your storage is encrypted. Password is required to decrypt it.'))).toBeVisible();
|
||||
await sup('Your storage is encrypted. Password is required to decrypt it.');
|
||||
await element(by.type('android.widget.EditText')).typeText('pass');
|
||||
await element(by.text('OK')).tap();
|
||||
await yo('WalletsList');
|
||||
@ -437,18 +451,20 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
await element(by.id('SecurityButton')).tap();
|
||||
|
||||
// putting FAKE storage password. should not succeed
|
||||
await element(by.type('android.widget.CompoundButton')).tap(); // thats a switch lol
|
||||
await element(by.id('EncyptedAndPasswordProtectedSwitch')).tap();
|
||||
await element(by.text('OK')).tap();
|
||||
await element(by.id('PasswordInput')).typeText('fake');
|
||||
await element(by.id('PasswordInput')).replaceText('fake');
|
||||
await element(by.id('PasswordInput')).tapReturnKey();
|
||||
await element(by.id('OKButton')).tap();
|
||||
if (process.env.TRAVIS) await sleep(3000); // hopefully helps prevent crash
|
||||
await tapIfPresent('OKButton'); // might not always work first time
|
||||
await sleep(3000); // propagate
|
||||
// correct password
|
||||
await element(by.id('PasswordInput')).clearText();
|
||||
await element(by.id('PasswordInput')).typeText('pass');
|
||||
await element(by.id('PasswordInput')).replaceText('pass');
|
||||
await element(by.id('PasswordInput')).tapReturnKey();
|
||||
await element(by.id('OKButton')).tap();
|
||||
if (process.env.TRAVIS) await sleep(3000); // hopefully helps prevent crash
|
||||
await tapIfPresent('OKButton'); // might not always work first time
|
||||
await sleep(3000); // propagate
|
||||
|
||||
// relaunch app
|
||||
await device.launchApp({ newInstance: true });
|
||||
@ -467,8 +483,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
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.
|
||||
// going to Import Wallet screen and importing Vault
|
||||
await element(by.id('CreateAWallet')).tap();
|
||||
await yo('ActivateVaultButton');
|
||||
await tapAndTapAgainIfElementIsNotVisible('CreateAWallet', 'ActivateVaultButton');
|
||||
await element(by.id('ActivateVaultButton')).tap();
|
||||
await element(by.id('Create')).tap();
|
||||
// vault settings:
|
||||
@ -486,6 +501,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
await element(by.id('ScanOrOpenFile')).tap();
|
||||
|
||||
await sleep(5000); // wait for camera screen to initialize
|
||||
await yo('ScanQrBackdoorButton');
|
||||
for (let c = 0; c <= 5; c++) {
|
||||
await element(by.id('ScanQrBackdoorButton')).tap();
|
||||
}
|
||||
@ -511,11 +527,12 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
// when xpub - it automatically closes the modal, so no need to tap the button
|
||||
|
||||
await element(by.id('CreateButton')).tap();
|
||||
await sup('OK');
|
||||
await tapIfTextPresent('OK');
|
||||
await yo('Multisig Vault');
|
||||
await element(by.id('Multisig Vault')).tap(); // go inside the wallet
|
||||
await yo('ReceiveButton');
|
||||
await element(by.id('ReceiveButton')).tap();
|
||||
await element(by.text('Yes, I have.')).tap();
|
||||
try {
|
||||
// in case emulator has no google services and doesnt support pushes
|
||||
// we just dont show this popup
|
||||
@ -545,7 +562,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
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.
|
||||
// going to Import Wallet screen and importing mnemonic
|
||||
await element(by.id('CreateAWallet')).tap();
|
||||
await tapAndTapAgainIfElementIsNotVisible('CreateAWallet', 'ImportWallet');
|
||||
await element(by.id('ImportWallet')).tap();
|
||||
await element(by.id('ScanImport')).tap();
|
||||
|
||||
@ -590,9 +607,9 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
await element(by.id('chooseFee')).tap();
|
||||
await element(by.id('feeCustom')).tap();
|
||||
await element(by.type('android.widget.EditText')).typeText(feeRate + '\n');
|
||||
await sleep(1_000); // propagate
|
||||
await element(by.text('OK')).tap();
|
||||
|
||||
if (process.env.TRAVIS) await sleep(5000);
|
||||
try {
|
||||
await element(by.id('CreateTransactionButton')).tap();
|
||||
} catch (_) {}
|
||||
@ -602,7 +619,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
|
||||
await element(by.id('ProvideSignature')).tap();
|
||||
await element(by.id('PsbtMultisigQRCodeScrollView')).swipe('up', 'fast', 1); // in case emu screen is small and it doesnt fit
|
||||
await element(by.id('CosignedScanOrImportFile')).tap();
|
||||
await tapAndTapAgainIfElementIsNotVisible('CosignedScanOrImportFile', 'ScanQrBackdoorButton');
|
||||
|
||||
const ursSignedByPassport = [
|
||||
'UR:CRYPTO-PSBT/22-4/LPCMAACFAXPLCYZTVYVOPKHDWPHKAXPYJOIHIDHNJSATRTSWEYGUHDURWYDECAGLAAHTTBHTFZFPWDRTLROXLUEHCXAHJTIHTEHDHKTEVTOTIOWFSKGEOSCFFLDRGLFTCYKELSRDNSHYGLLEVYIDGYZOEEDAAOENHGASFDHFVWNSATVYCFETATZSFROXFPMHGUJNWDSPNYMHHGPAIMGYURAYCXLEZEZSCLKBJZLFSRAOOYMSYNCEHDOSPYGTTDSODRSKLALBCAVYBNOLOEGSOYVOVLMWFDPFHGBAVDAEAEAEADADWMDTGDPTADAEADADSTENFYASFDTBCLDINBAOHFHYTPPKWYMSSNDKHKKNUOIELPDRKTOYHPCFCSWNFXPKFZNEPKVOIOCNAOAXMNPSKPLTGYFLRHLOHGUYKISWBWVEGUGMLAAYDLLDLSAAVDTDSADLIDFXYLKKFYURMTOXLKMDRSTYTERSJNHSBDPSGOGWJKJESTWLZCTKGE',
|
||||
@ -626,7 +643,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
|
||||
await element(by.id('ProvideSignature')).tap();
|
||||
await element(by.id('PsbtMultisigQRCodeScrollView')).swipe('up', 'fast', 1); // in case emu screen is small and it doesnt fit
|
||||
await element(by.id('CosignedScanOrImportFile')).tap();
|
||||
await tapAndTapAgainIfElementIsNotVisible('CosignedScanOrImportFile', 'ScanQrBackdoorButton');
|
||||
|
||||
const urSignedByPassportAndKeystone = [
|
||||
'UR:CRYPTO-PSBT/105-2/LPCSINAOCFAXOLCYSBLUFDHSHKADTEHKAXOTJOJKIDJYZMADAEKIAOAEAEAEADSGMHIEQDIAFLKPVABAJEHLLNVLRKKPCPDAHYNSOTTSOYBTIMMUCYAASSMDAMDAMKAEAEAEAEAEZMZMZMZMAOGDSRAEAEAEAEAEAECMAEBBKBOTLPWFGMRNINIMPFYNWLGEBAVTVLSWTYPAGEGUWFVLAEAEAEAEAEAECPAECXHEJTWTTTWEPDFMMDSNYKDPRYZMRLBARSPAAYLETDCLGDJKIHBNTYFXCHNNWTRHFEAEAEAEAEAEADAEWDAOAEAEAEAEADADSTENIYASISYLVDVLGWCXRPBWVYVSDALOTLCESKTTFEJTWDTBPTECMOMNLNDABSDTADAEAEAEAEADAEAELAAOGDPTADAEAEAEAEAECPAECXCLSWSSWSCPVTGTESFWSBCTCSETNSPYNLBKJLZTUEMWSOMSNNTYGSLSFPNEPKVOIOONMOAOAEAEAEAEAECMAEBBMNAMRTRKDYGUHDURWSVOLGDRRLESMEDLOLGWLYNTAOFLDYFYAOCXDYYTJOMYYAUELEDYKIYLADUROYFNURDRGLFTCYKEKEFEIAOYGSTNCPIDGYZOEEDAAOCXHGCAENYKHNJLGOHEJOGMRLBNTDWYGWJOPFPYFMKKTISROXGMIMGYURAYCXLEUOZSADCLAOJPBGWSASPTIATTPMLECMPRIHSTMDJYLOYKTKRTHHTLSTFZKPOYWKBKROASBGBAVDAEAEAEAEADADDNGDPTADAEAEAEAEAECPAECXCLSWSSWSCPVTGTESFWSBCTCSETNSPYNLBKJLZTUEMWSOMSNNTYGSLSFPNEPKVOIOCPAOAXFTRPWPCPGYBKEHRTTTFDCTNTRHKGFGCXSAHSRHWDMDTONBRLROWMSFCPBTHNTIAAFLDYFYAOCXISRERKHDRDGAPMATEHJLFL',
|
||||
@ -679,9 +696,9 @@ describe('BlueWallet UI Tests - no wallets', () => {
|
||||
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.
|
||||
await sleep(500); // Wait until bounce animation finishes.
|
||||
// going to Import Wallet screen and importing mnemonic
|
||||
await element(by.id('CreateAWallet')).tap();
|
||||
await tapAndTapAgainIfElementIsNotVisible('CreateAWallet', 'ScrollView');
|
||||
await element(by.id('ScrollView')).swipe('up', 'fast', 0.9); // in case emu screen is small and it doesnt fit
|
||||
await element(by.id('ImportWallet')).tap();
|
||||
await element(by.id('MnemonicInput')).replaceText(
|
||||
|
||||
@ -1,7 +1,18 @@
|
||||
import assert from 'assert';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
|
||||
import { extractTextFromElementById, getSwitchValue, hashIt, helperImportWallet, sleep, sup, yo } from './helperz';
|
||||
import {
|
||||
extractTextFromElementById,
|
||||
getSwitchValue,
|
||||
hashIt,
|
||||
helperImportWallet,
|
||||
sleep,
|
||||
sup,
|
||||
tapAndTapAgainIfElementIsNotVisible,
|
||||
tapAndTapAgainIfTextIsNotVisible,
|
||||
tapIfTextPresent,
|
||||
yo,
|
||||
} from './helperz';
|
||||
|
||||
/**
|
||||
* in this suite each test requires that there is one specific wallet present, thus, we import it
|
||||
@ -434,9 +445,8 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
|
||||
await element(by.text('Imported HD SegWit (BIP84 Bech32 Native)')).tap();
|
||||
await element(by.id('WalletDetails')).tap();
|
||||
await element(by.id('WalletDetailsScroll')).swipe('up', 'fast', 1); // in case emu screen is small and it doesnt fit
|
||||
await element(by.text('Contacts')).tap();
|
||||
await tapAndTapAgainIfTextIsNotVisible('Contacts', 'Add Contact');
|
||||
|
||||
await expect(element(by.text('Add Contact'))).toBeVisible();
|
||||
await expect(element(by.id('ContactListItem0'))).not.toBeVisible();
|
||||
await element(by.text('Add Contact')).tap();
|
||||
await element(by.type('android.widget.EditText')).replaceText('13HaCAB4jf7FYSZexJxoczyDDnutzZigjS');
|
||||
@ -475,10 +485,10 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
|
||||
await element(by.text('Imported HD SegWit (BIP84 Bech32 Native)')).tap();
|
||||
await yo('SendButton');
|
||||
|
||||
await element(by.id('SendButton')).tap();
|
||||
await tapAndTapAgainIfElementIsNotVisible('SendButton', 'HeaderMenuButton');
|
||||
await element(by.id('HeaderMenuButton')).tap();
|
||||
await element(by.text('Insert Contact')).tap();
|
||||
await element(by.id('ContactListItem0')).tap();
|
||||
await tapAndTapAgainIfElementIsNotVisible('ContactListItem0', 'BitcoinAmountInput');
|
||||
await element(by.id('BitcoinAmountInput')).typeText('0.0001\n');
|
||||
|
||||
await element(by.id('HeaderMenuButton')).tap();
|
||||
@ -529,6 +539,7 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
|
||||
|
||||
// rename test
|
||||
await element(by.id('WalletNameInput')).replaceText('testname');
|
||||
await element(by.id('WalletNameInput')).typeText('\n'); // newline is what triggers saving the wallet
|
||||
await device.pressBack();
|
||||
await sup('testname');
|
||||
await expect(element(by.id('WalletLabel'))).toHaveText('testname');
|
||||
@ -536,6 +547,7 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
|
||||
|
||||
// rename back
|
||||
await element(by.id('WalletNameInput')).replaceText('Imported HD SegWit (BIP84 Bech32 Native)');
|
||||
await element(by.id('WalletNameInput')).typeText('\n'); // newline is what triggers saving the wallet
|
||||
await device.pressBack();
|
||||
await sup('Imported HD SegWit (BIP84 Bech32 Native)');
|
||||
await expect(element(by.id('WalletLabel'))).toHaveText('Imported HD SegWit (BIP84 Bech32 Native)');
|
||||
@ -543,15 +555,14 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
|
||||
|
||||
// wallet export
|
||||
await element(by.id('WalletDetailsScroll')).swipe('up', 'fast', 1);
|
||||
await element(by.id('WalletExport')).tap();
|
||||
await tapAndTapAgainIfElementIsNotVisible('WalletExport', 'WalletExportScroll');
|
||||
await element(by.id('WalletExportScroll')).swipe('up', 'fast', 1);
|
||||
await expect(element(by.id('Secret'))).toHaveText(process.env.HD_MNEMONIC_BIP84);
|
||||
await device.pressBack();
|
||||
|
||||
// XPUB
|
||||
await element(by.id('WalletDetailsScroll')).swipe('up', 'fast', 1);
|
||||
await element(by.id('XPub')).tap();
|
||||
await expect(element(by.id('CopyTextToClipboard'))).toBeVisible();
|
||||
await tapAndTapAgainIfElementIsNotVisible('XpubButton', 'CopyTextToClipboard');
|
||||
await device.pressBack();
|
||||
|
||||
process.env.TRAVIS && require('fs').writeFileSync(lockFile, '1');
|
||||
@ -616,7 +627,7 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
|
||||
await element(by.text('0.00069909')).atIndex(0).tap();
|
||||
await element(by.text('Details')).tap();
|
||||
await expect(element(by.text('8b0ab2c7196312e021e0d3dc73f801693826428782970763df6134457bd2ec20'))).toBeVisible();
|
||||
await element(by.type('android.widget.EditText')).typeText('test1');
|
||||
await element(by.type('android.widget.EditText')).replaceText('test1');
|
||||
await element(by.type('android.widget.EditText')).tapReturnKey();
|
||||
|
||||
// Terminate and reopen the app to confirm the note is persisted
|
||||
@ -650,9 +661,11 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
|
||||
// setting fee rate:
|
||||
await element(by.id('chooseFee')).tap();
|
||||
await element(by.id('feeCustom')).tap();
|
||||
await element(by.type('android.widget.EditText')).typeText('1\n');
|
||||
await element(by.type('android.widget.EditText')).replaceText('1');
|
||||
await element(by.type('android.widget.EditText')).tapReturnKey();
|
||||
await element(by.text('OK')).tap();
|
||||
if (process.env.TRAVIS) await sleep(5000);
|
||||
await tapIfTextPresent('OK'); // in case it didnt work first time
|
||||
await sleep(3000);
|
||||
await element(by.id('CreateTransactionButton')).tap();
|
||||
await element(by.id('TransactionDetailsButton')).tap();
|
||||
|
||||
|
||||
@ -1,15 +1,45 @@
|
||||
import createHash from 'create-hash';
|
||||
import { element } from 'detox';
|
||||
|
||||
export function yo(id, timeout = 33000) {
|
||||
return waitFor(element(by.id(id)))
|
||||
.toBeVisible()
|
||||
.withTimeout(timeout);
|
||||
export async function yo(id, timeout = 33000) {
|
||||
try {
|
||||
await waitFor(element(by.id(id)))
|
||||
.toBeVisible()
|
||||
.withTimeout(timeout / 2);
|
||||
} catch (_) {
|
||||
// nop
|
||||
}
|
||||
|
||||
try {
|
||||
await waitFor(element(by.id(id)))
|
||||
.toBeVisible()
|
||||
.withTimeout(timeout / 2);
|
||||
return true;
|
||||
} catch (_) {
|
||||
const msg = `Assertion failed: testID ${id} is not visible`;
|
||||
throw new Error(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export function sup(text, timeout = 33000) {
|
||||
return waitFor(element(by.text(text)))
|
||||
.toBeVisible()
|
||||
.withTimeout(timeout);
|
||||
export async function sup(text, timeout = 33000) {
|
||||
try {
|
||||
await waitFor(element(by.text(text)))
|
||||
.toBeVisible()
|
||||
.withTimeout(timeout / 2);
|
||||
return true;
|
||||
} catch (_) {
|
||||
// nop
|
||||
}
|
||||
|
||||
try {
|
||||
await waitFor(element(by.text(text)))
|
||||
.toBeVisible()
|
||||
.withTimeout(timeout / 2);
|
||||
return true;
|
||||
} catch (_) {
|
||||
const msg = `Assertion failed: text "${text}" is not visible`;
|
||||
throw new Error(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSwitchValue(switchId) {
|
||||
@ -25,9 +55,9 @@ export async function helperImportWallet(importText, walletType, expectedWalletL
|
||||
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.
|
||||
await sleep(500); // Wait until bounce animation finishes.
|
||||
// going to Import Wallet screen and importing mnemonic
|
||||
await element(by.id('CreateAWallet')).tap();
|
||||
await tapAndTapAgainIfElementIsNotVisible('CreateAWallet', 'ImportWallet');
|
||||
await element(by.id('ImportWallet')).tap();
|
||||
// tapping 5 times invisible button is a backdoor:
|
||||
for (let c = 0; c < 5; c++) {
|
||||
@ -126,15 +156,14 @@ export const expectToBeVisible = async id => {
|
||||
export async function helperCreateWallet(walletName) {
|
||||
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.
|
||||
await element(by.id('CreateAWallet')).tap();
|
||||
await tapAndTapAgainIfElementIsNotVisible('CreateAWallet', 'WalletNameInput');
|
||||
await element(by.id('WalletNameInput')).replaceText(walletName || 'cr34t3d');
|
||||
await yo('ActivateBitcoinButton');
|
||||
await element(by.id('ActivateBitcoinButton')).tap();
|
||||
await element(by.id('ActivateBitcoinButton')).tap();
|
||||
// why tf we need 2 taps for it to work..? mystery
|
||||
await element(by.id('Create')).tap();
|
||||
await tapAndTapAgainIfElementIsNotVisible('Create', 'PleaseBackupScrollView');
|
||||
|
||||
await yo('PleaseBackupScrollView');
|
||||
await element(by.id('PleaseBackupScrollView')).swipe('up', 'fast', 1); // in case emu screen is small and it doesnt fit
|
||||
|
||||
await yo('PleasebackupOk');
|
||||
@ -143,3 +172,59 @@ export async function helperCreateWallet(walletName) {
|
||||
await element(by.id('WalletsList')).swipe('right', 'fast', 1); // in case emu screen is small and it doesnt fit
|
||||
await expect(element(by.id(walletName || 'cr34t3d'))).toBeVisible();
|
||||
}
|
||||
|
||||
export async function tapAndTapAgainIfElementIsNotVisible(idToTap, idToCheckVisible) {
|
||||
// tap
|
||||
await element(by.id(idToTap)).tap();
|
||||
|
||||
// check if visible
|
||||
try {
|
||||
await waitFor(element(by.id(idToCheckVisible)))
|
||||
.toBeVisible()
|
||||
.withTimeout(3_000);
|
||||
return; // did not throw? its visible, return
|
||||
} catch (_) {}
|
||||
|
||||
// did not return so its not visible, lets tap again
|
||||
await element(by.id(idToTap)).tap();
|
||||
|
||||
// check visibility again, this time no try-catch, if it fails it fails
|
||||
await waitFor(element(by.id(idToCheckVisible)))
|
||||
.toBeVisible()
|
||||
.withTimeout(3_000);
|
||||
}
|
||||
|
||||
export async function tapAndTapAgainIfTextIsNotVisible(textToTap, textToCheckVisible) {
|
||||
// tap
|
||||
await element(by.text(textToTap)).tap();
|
||||
|
||||
// check if visible
|
||||
try {
|
||||
await waitFor(element(by.text(textToCheckVisible)))
|
||||
.toBeVisible()
|
||||
.withTimeout(3_000);
|
||||
return; // did not throw? its visible, return
|
||||
} catch (_) {}
|
||||
|
||||
// did not return so its not visible, lets tap again
|
||||
await element(by.text(textToTap)).tap();
|
||||
|
||||
// check visibility again, this time no try-catch, if it fails it fails
|
||||
await waitFor(element(by.text(textToCheckVisible)))
|
||||
.toBeVisible()
|
||||
.withTimeout(3_000);
|
||||
}
|
||||
|
||||
export async function tapIfPresent(id) {
|
||||
try {
|
||||
await element(by.id(id)).tap();
|
||||
} catch (_) {}
|
||||
// no need to check for visibility, just silently ignore exception if such testID is not present
|
||||
}
|
||||
|
||||
export async function tapIfTextPresent(text) {
|
||||
try {
|
||||
await element(by.text(text)).tap();
|
||||
} catch (_) {}
|
||||
// no need to check for visibility, just silently ignore exception if such testID is not present
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import assert from 'assert';
|
||||
import React from 'react';
|
||||
import TestRenderer from 'react-test-renderer';
|
||||
|
||||
import { render } from '@testing-library/react-native';
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { Header } from '../../components/Header';
|
||||
import SelfTest from '../../screen/settings/SelfTest';
|
||||
import Settings from '../../screen/settings/Settings';
|
||||
import { BlueDefaultTheme } from '../../components/themes';
|
||||
|
||||
jest.mock('../../blue_modules/BlueElectrum', () => {
|
||||
return {
|
||||
@ -12,37 +12,33 @@ jest.mock('../../blue_modules/BlueElectrum', () => {
|
||||
};
|
||||
});
|
||||
|
||||
const Wrapper = ({ children }) => <NavigationContainer theme={BlueDefaultTheme}>{children}</NavigationContainer>;
|
||||
|
||||
it('Header works', () => {
|
||||
const rendered = TestRenderer.create(<Header />).toJSON();
|
||||
expect(rendered).toBeTruthy();
|
||||
const { toJSON } = render(
|
||||
<Wrapper>
|
||||
<Header />
|
||||
</Wrapper>,
|
||||
);
|
||||
expect(toJSON()).toBeTruthy();
|
||||
});
|
||||
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('Settings work', () => {
|
||||
const rendered = TestRenderer.create(<Settings />).toJSON();
|
||||
expect(rendered).toBeTruthy();
|
||||
const { toJSON } = render(
|
||||
<Wrapper>
|
||||
<Settings />
|
||||
</Wrapper>,
|
||||
);
|
||||
expect(toJSON()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('SelfTest work', () => {
|
||||
const component = TestRenderer.create(<SelfTest />);
|
||||
const root = component.root;
|
||||
const rendered = component.toJSON();
|
||||
expect(rendered).toBeTruthy();
|
||||
// console.log((root.findAllByType('Text')[0].props));
|
||||
|
||||
let okFound = false;
|
||||
const allTests = [];
|
||||
for (const v of root.findAllByType('Text')) {
|
||||
let text = v.props.children;
|
||||
if (text.join) {
|
||||
text = text.join('');
|
||||
}
|
||||
if (text === 'OK') {
|
||||
okFound = true;
|
||||
}
|
||||
allTests.push(text);
|
||||
// console.log(text);
|
||||
}
|
||||
|
||||
assert.ok(okFound, 'OK not found. Got: ' + allTests.join('; '));
|
||||
const { toJSON, getByText } = render(
|
||||
<Wrapper>
|
||||
<SelfTest />
|
||||
</Wrapper>,
|
||||
);
|
||||
expect(toJSON()).toBeTruthy();
|
||||
expect(getByText('OK')).toBeTruthy();
|
||||
});
|
||||
|
||||
27
utils/combinePSBTs.ts
Normal file
27
utils/combinePSBTs.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
|
||||
/**
|
||||
* Combines two PSBTs and returns the combined PSBT.
|
||||
* @param {string} psbtBase64 - The base64 string of the first PSBT.
|
||||
* @param {string} newPSBTBase64 - The base64 string of the new PSBT to combine.
|
||||
* @returns {bitcoin.Psbt} - The combined PSBT.
|
||||
*/
|
||||
interface CombinePSBTsParams {
|
||||
psbtBase64: string;
|
||||
newPSBTBase64: string;
|
||||
}
|
||||
|
||||
export const combinePSBTs = ({ psbtBase64, newPSBTBase64 }: CombinePSBTsParams): bitcoin.Psbt => {
|
||||
if (psbtBase64 === newPSBTBase64) {
|
||||
return bitcoin.Psbt.fromBase64(psbtBase64);
|
||||
}
|
||||
try {
|
||||
const psbt = bitcoin.Psbt.fromBase64(psbtBase64);
|
||||
const newPsbt = bitcoin.Psbt.fromBase64(newPSBTBase64);
|
||||
psbt.combine(newPsbt);
|
||||
return psbt;
|
||||
} catch (err) {
|
||||
console.error('Error combining PSBTs:', err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user