mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-01-18 05:12:47 +01:00
Merge branch 'master' into marcosrdz-patch-3
This commit is contained in:
commit
e30c0b17ea
@ -125,6 +125,13 @@
|
||||
"symbol": "£",
|
||||
"country": "United Kingdom (British Pound)"
|
||||
},
|
||||
"HKD": {
|
||||
"endPointKey": "HKD",
|
||||
"locale": "zh-HK",
|
||||
"source": "CoinGecko",
|
||||
"symbol": "HK$",
|
||||
"country": "Hong Kong (Hong Kong Dollar)"
|
||||
},
|
||||
"HRK": {
|
||||
"endPointKey": "HRK",
|
||||
"locale": "hr-HR",
|
||||
|
@ -58,13 +58,20 @@
|
||||
style="@style/WidgetTextPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:autoSizeMaxTextSize="24sp"
|
||||
android:autoSizeMinTextSize="12sp"
|
||||
android:autoSizeStepGranularity="2sp"
|
||||
android:autoSizeTextType="uniform"
|
||||
android:duplicateParentState="false"
|
||||
android:editable="false"
|
||||
android:lines="1"
|
||||
android:text="Loading..."
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="end"/>
|
||||
android:visibility="gone" />
|
||||
<LinearLayout
|
||||
android:id="@+id/price_arrow_container"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -132,13 +132,19 @@ export const showImagePickerAndReadImage = async (): Promise<string | undefined>
|
||||
return undefined;
|
||||
} else if (response.errorCode) {
|
||||
throw new Error(response.errorMessage);
|
||||
} else if (response.assets?.[0]?.uri) {
|
||||
} else if (response.assets) {
|
||||
try {
|
||||
const result = await RNQRGenerator.detect({ uri: decodeURI(response.assets[0].uri.toString()) });
|
||||
return result?.values[0];
|
||||
const uri = response.assets[0].uri;
|
||||
if (uri) {
|
||||
const result = await RNQRGenerator.detect({ uri: decodeURI(uri.toString()) });
|
||||
if (result?.values.length > 0) {
|
||||
return result?.values[0];
|
||||
}
|
||||
}
|
||||
throw new Error(loc.send.qr_error_no_qrcode);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw new Error(loc.send.qr_error_no_qrcode);
|
||||
presentAlert({ message: loc.send.qr_error_no_qrcode });
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,9 +193,11 @@ export const showFilePickerAndReadFile = async function (): Promise<{ data: stri
|
||||
if (result) {
|
||||
return { data: result.values[0], uri: fileCopyUri };
|
||||
}
|
||||
presentAlert({ message: loc.send.qr_error_no_qrcode });
|
||||
return { data: false, uri: false };
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
presentAlert({ message: loc.send.qr_error_no_qrcode });
|
||||
return { data: false, uri: false };
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,8 @@ export const NOTIFICATIONS_NO_AND_DONT_ASK_FLAG = 'NOTIFICATIONS_NO_AND_DONT_ASK
|
||||
let alreadyConfigured = false;
|
||||
let baseURI = groundControlUri;
|
||||
|
||||
const deepClone = obj => JSON.parse(JSON.stringify(obj));
|
||||
|
||||
const checkAndroidNotificationPermission = async () => {
|
||||
try {
|
||||
const { status } = await checkNotifications();
|
||||
@ -323,8 +325,8 @@ export const configureNotifications = async onProcessNotifications => {
|
||||
};
|
||||
|
||||
const handleNotification = async notification => {
|
||||
// Deep clone to avoid modifying the original object
|
||||
const payload = structuredClone({
|
||||
// Deep clone to avoid modifying the original object
|
||||
const payload = deepClone({
|
||||
...notification,
|
||||
...notification.data,
|
||||
});
|
||||
|
218
components/CameraScreen.tsx
Normal file
218
components/CameraScreen.tsx
Normal file
@ -0,0 +1,218 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { Animated, ImageURISource, SafeAreaView, StatusBar, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import { Camera, CameraApi, CameraType, Orientation } from 'react-native-camera-kit';
|
||||
import loc from '../loc';
|
||||
import { Icon } from '@rneui/base';
|
||||
|
||||
interface CameraScreenProps {
|
||||
onCancelButtonPress: () => void;
|
||||
showImagePickerButton?: boolean;
|
||||
showFilePickerButton?: boolean;
|
||||
onImagePickerButtonPress?: () => void;
|
||||
onFilePickerButtonPress?: () => void;
|
||||
torchOnImage?: ImageURISource;
|
||||
torchOffImage?: ImageURISource;
|
||||
onReadCode?: (event: any) => void;
|
||||
cameraFlipImage?: ImageURISource;
|
||||
}
|
||||
|
||||
const CameraScreen: React.FC<CameraScreenProps> = ({
|
||||
onCancelButtonPress,
|
||||
showImagePickerButton,
|
||||
showFilePickerButton,
|
||||
onImagePickerButtonPress,
|
||||
onFilePickerButtonPress,
|
||||
torchOnImage,
|
||||
torchOffImage,
|
||||
onReadCode,
|
||||
cameraFlipImage,
|
||||
}) => {
|
||||
const cameraRef = useRef<CameraApi>(null);
|
||||
const [torchMode, setTorchMode] = useState(false);
|
||||
const [cameraType, setCameraType] = useState(CameraType.Back);
|
||||
const [zoom, setZoom] = useState<number | undefined>();
|
||||
const [orientationAnim] = useState(new Animated.Value(3));
|
||||
|
||||
const onSwitchCameraPressed = () => {
|
||||
const direction = cameraType === CameraType.Back ? CameraType.Front : CameraType.Back;
|
||||
setCameraType(direction);
|
||||
setZoom(1); // When changing camera type, reset to default zoom for that camera
|
||||
};
|
||||
|
||||
const onSetTorch = () => {
|
||||
setTorchMode(!torchMode);
|
||||
};
|
||||
|
||||
// Counter-rotate the icons to indicate the actual orientation of the captured photo.
|
||||
// For this example, it'll behave incorrectly since UI orientation is allowed (and already-counter rotates the entire screen)
|
||||
// For real phone apps, lock your UI orientation using a library like 'react-native-orientation-locker'
|
||||
const rotateUi = true;
|
||||
const uiRotation = orientationAnim.interpolate({
|
||||
inputRange: [1, 4],
|
||||
outputRange: ['180deg', '-90deg'],
|
||||
});
|
||||
const uiRotationStyle = rotateUi ? { transform: [{ rotate: uiRotation }] } : undefined;
|
||||
|
||||
function rotateUiTo(rotationValue: number) {
|
||||
Animated.timing(orientationAnim, {
|
||||
toValue: rotationValue,
|
||||
useNativeDriver: true,
|
||||
duration: 200,
|
||||
isInteraction: false,
|
||||
}).start();
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.screen}>
|
||||
<StatusBar hidden />
|
||||
<SafeAreaView style={styles.topButtons}>
|
||||
<TouchableOpacity style={styles.topButton} onPress={onSetTorch}>
|
||||
<Animated.Image
|
||||
source={torchMode ? torchOnImage : torchOffImage}
|
||||
resizeMode="contain"
|
||||
style={[styles.topButtonImg, uiRotationStyle]}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.rightButtonsContainer}>
|
||||
{showImagePickerButton && (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.pick_image}
|
||||
style={[styles.topButton, styles.spacing, uiRotationStyle]}
|
||||
onPress={onImagePickerButtonPress}
|
||||
>
|
||||
<Icon name="image" type="font-awesome" color="#ffffff" />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{showFilePickerButton && (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.pick_file}
|
||||
style={[styles.topButton, styles.spacing, uiRotationStyle]}
|
||||
onPress={onFilePickerButtonPress}
|
||||
>
|
||||
<Icon name="file-import" type="font-awesome-5" color="#ffffff" />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
|
||||
<View style={styles.cameraContainer}>
|
||||
<Camera
|
||||
ref={cameraRef}
|
||||
style={styles.cameraPreview}
|
||||
cameraType={cameraType}
|
||||
resetFocusWhenMotionDetected
|
||||
zoom={zoom}
|
||||
maxZoom={10}
|
||||
onZoom={e => {
|
||||
console.debug('zoom', e.nativeEvent.zoom);
|
||||
setZoom(e.nativeEvent.zoom);
|
||||
}}
|
||||
onReadCode={onReadCode}
|
||||
torchMode={torchMode ? 'on' : 'off'}
|
||||
shutterPhotoSound
|
||||
maxPhotoQualityPrioritization="quality"
|
||||
onOrientationChange={e => {
|
||||
// We recommend locking the camera UI to portrait (using a different library)
|
||||
// and rotating the UI elements counter to the orientation
|
||||
// However, we include onOrientationChange so you can match your UI to what the camera does
|
||||
switch (e.nativeEvent.orientation) {
|
||||
case Orientation.PORTRAIT_UPSIDE_DOWN:
|
||||
console.debug('orientationChange', 'PORTRAIT_UPSIDE_DOWN');
|
||||
rotateUiTo(1);
|
||||
break;
|
||||
case Orientation.LANDSCAPE_LEFT:
|
||||
console.debug('orientationChange', 'LANDSCAPE_LEFT');
|
||||
rotateUiTo(2);
|
||||
break;
|
||||
case Orientation.PORTRAIT:
|
||||
console.debug('orientationChange', 'PORTRAIT');
|
||||
rotateUiTo(3);
|
||||
break;
|
||||
case Orientation.LANDSCAPE_RIGHT:
|
||||
console.debug('orientationChange', 'LANDSCAPE_RIGHT');
|
||||
rotateUiTo(4);
|
||||
break;
|
||||
default:
|
||||
console.debug('orientationChange', e.nativeEvent);
|
||||
break;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<SafeAreaView style={styles.bottomButtons}>
|
||||
<TouchableOpacity onPress={onCancelButtonPress}>
|
||||
<Animated.Text style={[styles.backTextStyle, uiRotationStyle]}>{loc._.cancel}</Animated.Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={styles.bottomButton} onPress={onSwitchCameraPressed}>
|
||||
<Animated.Image source={cameraFlipImage as ImageURISource} resizeMode="contain" style={[styles.topButtonImg, uiRotationStyle]} />
|
||||
</TouchableOpacity>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default CameraScreen;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
screen: {
|
||||
height: '100%',
|
||||
backgroundColor: '#000000',
|
||||
},
|
||||
topButtons: {
|
||||
margin: 10,
|
||||
zIndex: 10,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
topButton: {
|
||||
backgroundColor: '#222',
|
||||
width: 44,
|
||||
height: 44,
|
||||
borderRadius: 22,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
topButtonImg: {
|
||||
margin: 10,
|
||||
width: 24,
|
||||
height: 24,
|
||||
},
|
||||
cameraContainer: {
|
||||
justifyContent: 'center',
|
||||
flex: 1,
|
||||
},
|
||||
cameraPreview: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
bottomButtons: {
|
||||
margin: 10,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
backTextStyle: {
|
||||
padding: 10,
|
||||
color: 'white',
|
||||
fontSize: 20,
|
||||
},
|
||||
rightButtonsContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
bottomButton: {
|
||||
backgroundColor: '#222',
|
||||
width: 44,
|
||||
height: 44,
|
||||
borderRadius: 22,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginLeft: 10,
|
||||
},
|
||||
spacing: {
|
||||
marginLeft: 20,
|
||||
},
|
||||
});
|
@ -183,6 +183,9 @@ const CompanionDelegates = () => {
|
||||
|
||||
if (fileName && /\.(jpe?g|png)$/i.test(fileName)) {
|
||||
try {
|
||||
if (!decodedUrl) {
|
||||
throw new Error(loc.send.qr_error_no_qrcode);
|
||||
}
|
||||
const values = await RNQRGenerator.detect({
|
||||
uri: decodedUrl,
|
||||
});
|
||||
@ -200,11 +203,12 @@ const CompanionDelegates = () => {
|
||||
},
|
||||
);
|
||||
} else {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
presentAlert({ message: loc.send.qr_error_no_qrcode });
|
||||
throw new Error(loc.send.qr_error_no_qrcode);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error detecting QR code:', error);
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
presentAlert({ message: loc.send.qr_error_no_qrcode });
|
||||
}
|
||||
} else {
|
||||
DeeplinkSchemaMatch.navigationRouteFor(event, (value: [string, any]) => navigationRef.navigate(...value), {
|
||||
|
@ -6,6 +6,7 @@ import { WalletCarouselItem } from './WalletsCarousel';
|
||||
import { TransactionListItem } from './TransactionListItem';
|
||||
import { useTheme } from './themes';
|
||||
import { BitcoinUnit } from '../models/bitcoinUnits';
|
||||
import { TouchableOpacityWrapper } from './ListItem';
|
||||
|
||||
enum ItemType {
|
||||
WalletSection = 'wallet',
|
||||
@ -29,11 +30,14 @@ interface ManageWalletsListItemProps {
|
||||
isDraggingDisabled: boolean;
|
||||
drag?: () => void;
|
||||
isPlaceHolder?: boolean;
|
||||
onPressIn?: () => void;
|
||||
onPressOut?: () => void;
|
||||
state: { wallets: TWallet[]; searchQuery: string };
|
||||
navigateToWallet: (wallet: TWallet) => void;
|
||||
renderHighlightedText: (text: string, query: string) => JSX.Element;
|
||||
handleDeleteWallet: (wallet: TWallet) => void;
|
||||
handleToggleHideBalance: (wallet: TWallet) => void;
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
interface SwipeContentProps {
|
||||
@ -67,6 +71,9 @@ const ManageWalletsListItem: React.FC<ManageWalletsListItemProps> = ({
|
||||
renderHighlightedText,
|
||||
handleDeleteWallet,
|
||||
handleToggleHideBalance,
|
||||
onPressIn,
|
||||
onPressOut,
|
||||
isActive,
|
||||
}) => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
@ -110,6 +117,10 @@ const ManageWalletsListItem: React.FC<ManageWalletsListItemProps> = ({
|
||||
containerStyle={{ backgroundColor: colors.background }}
|
||||
leftContent={leftContent}
|
||||
rightContent={rightContent}
|
||||
Component={TouchableOpacityWrapper}
|
||||
onPressOut={onPressOut}
|
||||
onPressIn={onPressIn}
|
||||
style={isActive ? styles.activeItem : undefined}
|
||||
>
|
||||
<ListItem.Content
|
||||
style={{
|
||||
@ -121,6 +132,8 @@ const ManageWalletsListItem: React.FC<ManageWalletsListItemProps> = ({
|
||||
item={item.data}
|
||||
handleLongPress={isDraggingDisabled ? undefined : drag}
|
||||
onPress={onPress}
|
||||
onPressIn={onPressIn}
|
||||
onPressOut={onPressOut}
|
||||
animationsEnabled={false}
|
||||
searchQuery={state.searchQuery}
|
||||
isPlaceHolder={isPlaceHolder}
|
||||
@ -164,6 +177,9 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'red',
|
||||
},
|
||||
activeItem: {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.1)',
|
||||
},
|
||||
});
|
||||
|
||||
export { ManageWalletsListItem, LeftSwipeContent, RightSwipeContent };
|
||||
|
@ -155,9 +155,13 @@ const MultipleStepsListItem = props => {
|
||||
style={[styles.rowPartialRightButton, stylesHook.provideKeyButton, rightButtonOpacity]}
|
||||
onPress={props.button.onPress}
|
||||
>
|
||||
<Text style={[styles.provideKeyButtonText, stylesHook.provideKeyButtonText, styles.rightButton]}>
|
||||
{props.button.text}
|
||||
</Text>
|
||||
{props.button.showActivityIndicator ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<Text style={[styles.provideKeyButtonText, stylesHook.provideKeyButtonText, styles.rightButton]}>
|
||||
{props.button.text}
|
||||
</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
@ -171,7 +175,11 @@ const MultipleStepsListItem = props => {
|
||||
style={styles.rightButton}
|
||||
onPress={props.rightButton.onPress}
|
||||
>
|
||||
<Text style={[styles.provideKeyButtonText, stylesHook.provideKeyButtonText]}>{props.rightButton.text}</Text>
|
||||
{props.rightButton.showActivityIndicator ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<Text style={[styles.provideKeyButtonText, stylesHook.provideKeyButtonText]}>{props.rightButton.text}</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
@ -194,11 +202,13 @@ MultipleStepsListItem.propTypes = {
|
||||
disabled: PropTypes.bool,
|
||||
buttonType: PropTypes.number,
|
||||
leftText: PropTypes.string,
|
||||
showActivityIndicator: PropTypes.bool,
|
||||
}),
|
||||
rightButton: PropTypes.shape({
|
||||
text: PropTypes.string,
|
||||
onPress: PropTypes.func,
|
||||
disabled: PropTypes.bool,
|
||||
showActivityIndicator: PropTypes.bool,
|
||||
}),
|
||||
};
|
||||
|
||||
|
@ -172,6 +172,8 @@ interface WalletCarouselItemProps {
|
||||
searchQuery?: string;
|
||||
renderHighlightedText?: (text: string, query: string) => JSX.Element;
|
||||
animationsEnabled?: boolean;
|
||||
onPressIn?: () => void;
|
||||
onPressOut?: () => void;
|
||||
}
|
||||
|
||||
export const WalletCarouselItem: React.FC<WalletCarouselItemProps> = React.memo(
|
||||
@ -186,6 +188,8 @@ export const WalletCarouselItem: React.FC<WalletCarouselItemProps> = React.memo(
|
||||
renderHighlightedText,
|
||||
animationsEnabled = true,
|
||||
isPlaceHolder = false,
|
||||
onPressIn,
|
||||
onPressOut,
|
||||
}) => {
|
||||
const scaleValue = useRef(new Animated.Value(1.0)).current;
|
||||
const { colors } = useTheme();
|
||||
@ -203,7 +207,8 @@ export const WalletCarouselItem: React.FC<WalletCarouselItemProps> = React.memo(
|
||||
tension: 100,
|
||||
}).start();
|
||||
}
|
||||
}, [scaleValue, animationsEnabled]);
|
||||
if (onPressIn) onPressIn();
|
||||
}, [scaleValue, animationsEnabled, onPressIn]);
|
||||
|
||||
const onPressedOut = useCallback(() => {
|
||||
if (animationsEnabled) {
|
||||
@ -214,7 +219,8 @@ export const WalletCarouselItem: React.FC<WalletCarouselItemProps> = React.memo(
|
||||
tension: 100,
|
||||
}).start();
|
||||
}
|
||||
}, [scaleValue, animationsEnabled]);
|
||||
if (onPressOut) onPressOut();
|
||||
}, [scaleValue, animationsEnabled, onPressOut]);
|
||||
|
||||
const handlePress = useCallback(() => {
|
||||
onPressedOut();
|
||||
|
@ -3,6 +3,7 @@ import { navigationRef } from '../NavigationService';
|
||||
import { presentWalletExportReminder } from '../helpers/presentWalletExportReminder';
|
||||
import { unlockWithBiometrics, useBiometrics } from './useBiometrics';
|
||||
import { useStorage } from './context/useStorage';
|
||||
import { requestCameraAuthorization } from '../helpers/scan-qr';
|
||||
|
||||
// List of screens that require biometrics
|
||||
const requiresBiometrics = ['WalletExportRoot', 'WalletXpubRoot', 'ViewEditMultisigCosignersRoot', 'ExportMultisigCoordinationSetupRoot'];
|
||||
@ -90,6 +91,10 @@ export const useExtendedNavigation = <T extends NavigationProp<ParamListBase>>()
|
||||
return; // Prevent proceeding with the original navigation if the reminder is shown
|
||||
}
|
||||
}
|
||||
|
||||
if (screenName === 'ScanQRCode') {
|
||||
await requestCameraAuthorization();
|
||||
}
|
||||
proceedWithNavigation();
|
||||
})();
|
||||
};
|
||||
|
@ -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 /* (null) in Frameworks */ = {isa = PBXBuildFile; };
|
||||
C978A716948AB7DEC5B6F677 /* BuildFile 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 /* (null) in Frameworks */,
|
||||
C978A716948AB7DEC5B6F677 /* BuildFile in Frameworks */,
|
||||
17CDA0718F42DB2CE856C872 /* libPods-BlueWallet.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -56,7 +56,5 @@
|
||||
<key>apiKey</key>
|
||||
<string>17ba9059f676f1cc4f45d98182388b01</string>
|
||||
</dict>
|
||||
<key>WKCompanionAppBundleIdentifier</key>
|
||||
<string>io.bluewallet.bluewallet</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -1315,7 +1315,7 @@ PODS:
|
||||
- Yoga
|
||||
- react-native-ios-context-menu (1.15.3):
|
||||
- React-Core
|
||||
- react-native-menu (1.1.7):
|
||||
- react-native-menu (1.2.0):
|
||||
- React
|
||||
- react-native-randombytes (3.6.1):
|
||||
- React-Core
|
||||
@ -1588,8 +1588,27 @@ PODS:
|
||||
- React-logger (= 0.75.4)
|
||||
- React-perflogger (= 0.75.4)
|
||||
- React-utils (= 0.75.4)
|
||||
- ReactNativeCameraKit (13.0.0):
|
||||
- ReactNativeCameraKit (14.1.0):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
- RCT-Folly (= 2024.01.01.00)
|
||||
- RCTRequired
|
||||
- RCTTypeSafety
|
||||
- React-Core
|
||||
- React-debug
|
||||
- React-Fabric
|
||||
- React-featureflags
|
||||
- React-graphics
|
||||
- React-ImageManager
|
||||
- React-NativeModulesApple
|
||||
- React-RCTFabric
|
||||
- React-rendererdebug
|
||||
- React-utils
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- RealmJS (20.1.0):
|
||||
- React
|
||||
- RNCAsyncStorage (2.1.0):
|
||||
@ -1650,7 +1669,7 @@ PODS:
|
||||
- Yoga
|
||||
- RNLocalize (3.3.0):
|
||||
- React-Core
|
||||
- RNPermissions (5.2.1):
|
||||
- RNPermissions (5.2.2):
|
||||
- React-Core
|
||||
- RNQrGenerator (1.4.2):
|
||||
- React
|
||||
@ -1680,7 +1699,7 @@ PODS:
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- RNReanimated (3.16.5):
|
||||
- RNReanimated (3.16.6):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
@ -1700,10 +1719,10 @@ PODS:
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- RNReanimated/reanimated (= 3.16.5)
|
||||
- RNReanimated/worklets (= 3.16.5)
|
||||
- RNReanimated/reanimated (= 3.16.6)
|
||||
- RNReanimated/worklets (= 3.16.6)
|
||||
- Yoga
|
||||
- RNReanimated/reanimated (3.16.5):
|
||||
- RNReanimated/reanimated (3.16.6):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
@ -1723,9 +1742,9 @@ PODS:
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- RNReanimated/reanimated/apple (= 3.16.5)
|
||||
- RNReanimated/reanimated/apple (= 3.16.6)
|
||||
- Yoga
|
||||
- RNReanimated/reanimated/apple (3.16.5):
|
||||
- RNReanimated/reanimated/apple (3.16.6):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
@ -1746,7 +1765,7 @@ PODS:
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- RNReanimated/worklets (3.16.5):
|
||||
- RNReanimated/worklets (3.16.6):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
@ -2226,7 +2245,7 @@ SPEC CHECKSUMS:
|
||||
react-native-document-picker: 530879d9e89b490f0954bcc4ab697c5b5e35d659
|
||||
react-native-image-picker: 19a8d8471a239890675726f88f9c18dd213656d5
|
||||
react-native-ios-context-menu: 986da6dcba70094bcc2a8049f68410fe7d25aff1
|
||||
react-native-menu: 5779f6bd7a4e58d457ca5e0a6f164651dd26cd7f
|
||||
react-native-menu: 74230a5879e0ca697e98ee7c3087297dc774bf06
|
||||
react-native-randombytes: 3c8f3e89d12487fd03a2f966c288d495415fc116
|
||||
react-native-safe-area-context: 758e894ca5a9bd1868d2a9cfbca7326a2b6bf9dc
|
||||
react-native-screen-capture: 7b6121f529681ed2fde36cdedadd0bb39e9a3796
|
||||
@ -2258,7 +2277,7 @@ SPEC CHECKSUMS:
|
||||
React-utils: 02526ea15628a768b8db9517b6017a1785c734d2
|
||||
ReactCodegen: 8b5341ecb61898b8bd40a73ebc443c6bf2d14423
|
||||
ReactCommon: 36d48f542b4010786d6b2bcee615fe5f906b7105
|
||||
ReactNativeCameraKit: f058d47e0b1e55fd819bb55ee16505a2e0ca53db
|
||||
ReactNativeCameraKit: e72b838dac4ea2da19b7eb5d00b23125072790fd
|
||||
RealmJS: 9fd51c849eb552ade9f7b11db42a319b4f6cab4c
|
||||
RNCAsyncStorage: c91d753ede6dc21862c4922cd13f98f7cfde578e
|
||||
RNCClipboard: dbcf25b8f666b4685c02eeb65be981d30198e505
|
||||
@ -2270,12 +2289,12 @@ SPEC CHECKSUMS:
|
||||
RNHandoff: bc8af5a86853ff13b033e7ba1114c3c5b38e6385
|
||||
RNKeychain: 4df48b5186ca2b6a99f5ead69ad587154e084a32
|
||||
RNLocalize: d024afa9204c13885e61dc88b8190651bcaabac9
|
||||
RNPermissions: 979aa94a1a2091e3b2c3e7130ef0a1a6e331e05a
|
||||
RNPermissions: 6f08c623b0c8ca7d95faa71c3956b159b34f25c3
|
||||
RNQrGenerator: 7c604c0eb608af64ff586ab0c040796a04eff247
|
||||
RNQuickAction: c2c8f379e614428be0babe4d53a575739667744d
|
||||
RNRate: 7641919330e0d6688ad885a985b4bd697ed7d14c
|
||||
RNReactNativeHapticFeedback: 00ba111b82aa266bb3ee1aa576831c2ea9a9dfad
|
||||
RNReanimated: ae56eba247f82fa0d8bbf52bb0e7a34a218482de
|
||||
RNReanimated: 000b758cfbcd9c20c15b7ef305f98f036b288feb
|
||||
RNScreens: 35bb8e81aeccf111baa0ea01a54231390dbbcfd9
|
||||
RNShare: 6204e6a1987ba3e7c47071ef703e5449a0e3548a
|
||||
RNSVG: 3421710ac15f4f2dc47e5c122f2c2e4282116830
|
||||
|
@ -192,7 +192,7 @@
|
||||
"outdated_rate": "Rate was last updated: {date}",
|
||||
"psbt_tx_open": "Open Signed Transaction",
|
||||
"psbt_tx_scan": "Scan Signed Transaction",
|
||||
"qr_error_no_qrcode": "We were unable to find a QR Code in the selected image. Make sure the image contains only a QR Code and no additional content such as text or buttons.",
|
||||
"qr_error_no_qrcode": "We were unable to find a valid QR Code in the selected image. Make sure the image contains only a QR Code and no additional content such as text or buttons.",
|
||||
"reset_amount": "Reset Amount",
|
||||
"reset_amount_confirm": "Would you like to reset the amount?",
|
||||
"success_done": "Done",
|
||||
|
@ -28,6 +28,7 @@
|
||||
"enter_amount": "Ingresa la cantidad",
|
||||
"qr_custom_input_button": "Pulsa 10 veces para ingresar una entrada personalizada",
|
||||
"unlock": "Desbloquear",
|
||||
"port": "Puerto",
|
||||
"suggested": "Sugerido"
|
||||
},
|
||||
"azteco": {
|
||||
@ -74,6 +75,7 @@
|
||||
"please_pay": "Pagar por favor",
|
||||
"preimage": "Imagen previa",
|
||||
"sats": "sats.",
|
||||
"date_time": "Fecha y hora",
|
||||
"wasnt_paid_and_expired": "Esta factura no se pagó y ha caducado."
|
||||
},
|
||||
"plausibledeniability": {
|
||||
@ -190,7 +192,7 @@
|
||||
"outdated_rate": "La tarifa se actualizó por última vez: {date}",
|
||||
"psbt_tx_open": "Abrir transacción firmada",
|
||||
"psbt_tx_scan": "Escanear transacción firmada",
|
||||
"qr_error_no_qrcode": "No pudimos encontrar un código QR en la imagen seleccionada. Asegúrate de que la imagen contenga solo un código QR y ningún contenido adicional como texto o botones.",
|
||||
"qr_error_no_qrcode": "No pudimos encontrar un código QR válido en la imagen seleccionada. Asegúrate de que la imagen contenga solo un código QR y ningún contenido adicional, como texto o botones.",
|
||||
"reset_amount": "Restablecer monto",
|
||||
"reset_amount_confirm": "¿Te gustaría restablecer la cantidad?",
|
||||
"success_done": "Hecho",
|
||||
@ -215,6 +217,7 @@
|
||||
"block_explorer_invalid_custom_url": "La URL proporcionada no es válida. Ingresa una URL válida que comience con http:// o https://.",
|
||||
"about_selftest_electrum_disabled": "La autocomprobación no está disponible con el modo sin conexión de Electrum. Desactiva el modo sin conexión y vuelve a intentarlo. ",
|
||||
"about_selftest_ok": "Todas las pruebas internas han pasado satisfactoriamente. La billetera funciona bien.",
|
||||
|
||||
"about_sm_github": "GitHub",
|
||||
"about_sm_discord": "Servidor Discord",
|
||||
"about_sm_telegram": "Chat de Telegram ",
|
||||
@ -249,15 +252,14 @@
|
||||
"electrum_settings_server": "Servidor Electrum",
|
||||
"electrum_status": "Estado",
|
||||
"electrum_preferred_server": "Servidor preferido",
|
||||
"electrum_preferred_server_description": "Introduce el servidor que deseas que tu billetera utilice para todas las actividades de Bitcoin. Una vez configurado, tu billetera utilizará exclusivamente este servidor para comprobar saldos, enviar transacciones y obtener datos de la red. Asegúrate de que confías en este servidor antes de configurarlo.",
|
||||
"electrum_clear_alert_title": "¿Borrar historial?",
|
||||
"electrum_preferred_server_description": "Introduce el servidor que deseas que tu billetera utilice para todas las actividades de Bitcoin. Una vez configurado, tu billetera utilizará exclusivamente este servidor para comprobar saldos, enviar transacciones y obtener datos de la red. Asegúrate de que confías en este servidor antes de configurarlo.", "electrum_clear_alert_title": "¿Borrar historial?",
|
||||
"electrum_clear_alert_message": "¿Quieres borrar el historial de los servidores de Electrum?",
|
||||
"electrum_clear_alert_cancel": "Cancelar",
|
||||
"electrum_clear_alert_ok": "Ok",
|
||||
"electrum_reset": "Restablecer a predeterminado",
|
||||
"only_use_preferred": "Conectarse únicamente al servidor preferido",
|
||||
"electrum_unable_to_connect": "No se puede conectar al {server}.",
|
||||
"electrum_history": "Historial",
|
||||
"electrum_reset_to_default": "¿Estás seguro de querer restablecer la configuración de Electrum a los valores predeterminados?",
|
||||
"electrum_reset_to_default": "Esto permitirá que BlueWallet elija aleatoriamente un servidor de la lista sugerida y del historial. El historial de tu servidor permanecerá sin cambios",
|
||||
"electrum_reset": "Restablecer a predeterminado",
|
||||
"electrum_clear": "Borrar historial",
|
||||
"encrypt_decrypt": "Descifrar Almacenamiento",
|
||||
"encrypt_decrypt_q": "¿Estás seguro de que deseas descifrar tu almacenamiento? Esto permitirá acceder a tus billeteras sin una contraseña.",
|
||||
@ -272,6 +274,7 @@
|
||||
"encrypt_title": "Seguridad",
|
||||
"encrypt_tstorage": "Almacenamiento",
|
||||
"encrypt_use": "Usar {type}",
|
||||
"set_as_preferred": "Establecer como preferido",
|
||||
"encrypted_feature_disabled": "Esta función no se puede utilizar con el almacenamiento cifrado habilitado.",
|
||||
"encrypt_use_expl": "{type} se utilizará para confirmar tu identidad antes de realizar una transacción, desbloquear, exportar o eliminar una billetera. {type} no se utilizará para desbloquear el almacenamiento encriptado.",
|
||||
"biometrics_fail": "Si {type} no está activado o no se desbloquea, puedes utilizar el código de acceso de tu dispositivo como alternativa.",
|
||||
@ -291,6 +294,7 @@
|
||||
"network": "Red",
|
||||
"network_broadcast": "Publicar transacción",
|
||||
"network_electrum": "Servidor Electrum",
|
||||
"electrum_suggested_description": "Cuando no se establece un servidor preferido, se seleccionará un servidor sugerido para su uso al azar.",
|
||||
"not_a_valid_uri": "URI inválido",
|
||||
"notifications": "Notificaciones",
|
||||
"open_link_in_explorer": "Abrir enlace en el explorador",
|
||||
@ -323,7 +327,7 @@
|
||||
"permission_denied_message": "Has denegado el envío de notificaciones. Si deseas recibirlas, actívalas en la configuración de tu dispositivo."
|
||||
},
|
||||
"transactions": {
|
||||
"cancel_explain": "Reemplazaremos esta transacción con una que te pague y tenga tarifas más altas. Esto cancela efectivamente la transacción actual. Esto se llama RBF—Replace by Fee.",
|
||||
"cancel_explain": "Reemplazaremos esta transacción con una que te pague y tenga tarifas más altas. Esto cancela efectivamente la transacción actual. Esto se llama RBF (Replace by Fee).",
|
||||
"cancel_no": "Esta transacción no es reemplazable.",
|
||||
"cancel_title": "Cancelar ésta transacción (RBF)",
|
||||
"transaction_loading_error": "Se ha producido un problema al cargar la transacción. Vuelve a intentarlo más tarde.",
|
||||
@ -332,7 +336,7 @@
|
||||
"copy_link": "Copiar enlace",
|
||||
"expand_note": "Expandir Nota",
|
||||
"cpfp_create": "Crear",
|
||||
"cpfp_exp": "Crearemos otra transacción que gaste tu transacción no confirmada. La tarifa total será más alta que la tarifa de la transacción original, por lo que debería extraerse más rápido. Esto se llama CPFP — Child Pays for Parent.",
|
||||
"cpfp_exp": "Crearemos otra transacción que gaste tu transacción no confirmada. La tarifa total será más alta que la tarifa de la transacción original, por lo que debería extraerse más rápido. Esto se llama CPFP (Child Pays for Parent).",
|
||||
"cpfp_no_bump": "Esta transacción no se puede acelerar.",
|
||||
"cpfp_title": "Aumentar Comisión (CPFP)",
|
||||
"details_balance_hide": "Ocultar Balance",
|
||||
@ -367,7 +371,7 @@
|
||||
"list_title": "Transacciones",
|
||||
"transaction": "Transacción",
|
||||
"open_url_error": "No se puede abrir el enlace con el navegador predeterminado. Cambia tu navegador predeterminado y vuelve a intentarlo.",
|
||||
"rbf_explain": "Reemplazaremos esta transacción con una con una tarifa más alta para que se extraiga más rápido. Esto se llama RBF—Replace by Fee.",
|
||||
"rbf_explain": "Reemplazaremos esta transacción con una con una tarifa más alta para que se extraiga más rápido. Esto se llama RBF (Replace by Fee)",
|
||||
"rbf_title": "Aumentar Comisión (RBF)",
|
||||
"status_bump": "Aumentar Comisión",
|
||||
"status_cancel": "Cancelar Transacción",
|
||||
@ -567,7 +571,7 @@
|
||||
"ms_help_title": "Cómo funcionan las Bóvedas Multifirma: Consejos y Trucos",
|
||||
"ms_help_text": "Una billetera con varias llaves para mayor seguridad o custodia compartida",
|
||||
"ms_help_title1": "Se recomiendan varios dispositivos.",
|
||||
"ms_help_1": "La Bóveda funcionará con otras apps de BlueWallet instalada en otros dispositivos y billeteras compatibles con PSBT, como Electrum, Spectre, Coldcard, Cobo Vault, etc.",
|
||||
"ms_help_1": "La Bóveda funcionará con BlueWallet instalada en otros dispositivos y billeteras compatibles con PSBT, como Electrum, Spectre, Coldcard, Keystone, etc.",
|
||||
"ms_help_title2": "Editar Claves",
|
||||
"ms_help_2": "Puedes crear todas las claves de la Bóveda en este dispositivo y eliminarlas o editarlas después. Tener todas las claves en el mismo dispositivo tiene la seguridad equivalente a la de un monedero de Bitcoin normal.",
|
||||
"ms_help_title3": "Copias de seguridad de la Bóveda",
|
||||
@ -654,6 +658,8 @@
|
||||
"bip47": {
|
||||
"payment_code": "Código de pago",
|
||||
"contacts": "Contactos",
|
||||
"bip47_explain": "Código reutilizable y compartible",
|
||||
"bip47_explain_subtitle": "BIP47",
|
||||
"purpose": "Código reutilizable y compartible (BIP47)",
|
||||
"pay_this_contact": "Paga a este contacto",
|
||||
"rename_contact": "Renombrar contacto",
|
||||
@ -667,7 +673,7 @@
|
||||
"notification_tx_unconfirmed": "La transacción de notificación aún no está confirmada, espera",
|
||||
"failed_create_notif_tx": "No se pudo crear una transacción en cadena",
|
||||
"onchain_tx_needed": "Se necesita transacción en cadena",
|
||||
"notif_tx_sent": "Transacción de notificación enviada. Espera a que se confirme",
|
||||
"notif_tx_sent" : "Transacción de notificación enviada. Espera a que se confirme",
|
||||
"notif_tx": "Transacción de notificación",
|
||||
"not_found": "Código de pago no encontrado"
|
||||
}
|
||||
|
@ -125,6 +125,13 @@
|
||||
"symbol": "£",
|
||||
"country": "United Kingdom (British Pound)"
|
||||
},
|
||||
"HKD": {
|
||||
"endPointKey": "HKD",
|
||||
"locale": "zh-HK",
|
||||
"source": "CoinGecko",
|
||||
"symbol": "HK$",
|
||||
"country": "Hong Kong (Hong Kong Dollar)"
|
||||
},
|
||||
"HRK": {
|
||||
"endPointKey": "HRK",
|
||||
"locale": "hr-HR",
|
||||
|
@ -9,7 +9,6 @@ export type ScanQRCodeParamList = {
|
||||
urTotal?: number;
|
||||
urHave?: number;
|
||||
backdoorText?: string;
|
||||
onDismiss?: () => void;
|
||||
onBarScanned?: (data: string) => void;
|
||||
showFileImportButton?: boolean;
|
||||
backdoorVisible?: boolean;
|
||||
|
36
package-lock.json
generated
36
package-lock.json
generated
@ -63,12 +63,12 @@
|
||||
"react-native": "0.75.4",
|
||||
"react-native-biometrics": "3.0.1",
|
||||
"react-native-blue-crypto": "github:BlueWallet/react-native-blue-crypto#3cb5442",
|
||||
"react-native-camera-kit": "13.0.0",
|
||||
"react-native-camera-kit": "14.1.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": "13.2.0",
|
||||
"react-native-document-picker": "9.3.1",
|
||||
"react-native-draggable-flatlist": "github:BlueWallet/react-native-draggable-flatlist#3a61627",
|
||||
"react-native-draglist": "github:BlueWallet/react-native-draglist#a4af02f",
|
||||
"react-native-fs": "2.20.0",
|
||||
"react-native-gesture-handler": "2.21.2",
|
||||
"react-native-handoff": "github:BlueWallet/react-native-handoff#v0.0.4",
|
||||
@ -78,7 +78,7 @@
|
||||
"react-native-keychain": "9.1.0",
|
||||
"react-native-linear-gradient": "2.8.3",
|
||||
"react-native-localize": "3.3.0",
|
||||
"react-native-permissions": "5.2.1",
|
||||
"react-native-permissions": "5.2.2",
|
||||
"react-native-prompt-android": "github:BlueWallet/react-native-prompt-android#ed168d66fed556bc2ed07cf498770f058b78a376",
|
||||
"react-native-push-notification": "8.1.1",
|
||||
"react-native-qrcode-svg": "6.3.2",
|
||||
@ -20530,12 +20530,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-camera-kit": {
|
||||
"version": "13.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-camera-kit/-/react-native-camera-kit-13.0.0.tgz",
|
||||
"integrity": "sha512-fnkyivCG2xzS+14/doP8pCAYNafYaTyg5J0t+JJltJdgKSHf328OG44Rd+fnbbEOydZxgy/bcuLB24R0kCbynw==",
|
||||
"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==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash": "^4.14.2"
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
@ -20615,17 +20615,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-draggable-flatlist": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "git+ssh://git@github.com/BlueWallet/react-native-draggable-flatlist.git#3a61627474a4e35198ae961310c77fb305507509",
|
||||
"node_modules/react-native-draglist": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "git+ssh://git@github.com/BlueWallet/react-native-draglist.git#a4af02fec803b75508a8136e35eca564bbb1d644",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/preset-typescript": "^7.17.12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react-native": ">=0.64.0",
|
||||
"react-native-gesture-handler": ">=2.0.0",
|
||||
"react-native-reanimated": ">=2.8.0"
|
||||
"react": ">=17.0.1",
|
||||
"react-native": ">=0.64.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-fs": {
|
||||
@ -20747,9 +20743,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-permissions": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-permissions/-/react-native-permissions-5.2.1.tgz",
|
||||
"integrity": "sha512-F8CaDVi+zYjl2pO4Fwh5n5SCi5s0TvvRzQnczb8nwkzHJTv6HqlO6Sj1ZmqrAeuCN06b5Ysu9BEWhK4qT4ekXg==",
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-permissions/-/react-native-permissions-5.2.2.tgz",
|
||||
"integrity": "sha512-Mae5VKT8bjliksONZ+jMYTPf90wxuhn1H1FiH/kRfw0Y5tW5WIV1P8t/KiEHKZRvimnrInimuCr+EpRzK0IPWQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=18.1.0",
|
||||
|
@ -59,7 +59,7 @@
|
||||
"android:clean": "cd android; ./gradlew clean ; cd .. ; npm run android",
|
||||
"ios": "react-native run-ios",
|
||||
"postinstall": "rn-nodeify --install buffer,events,process,stream,inherits,path,assert,crypto --hack; npm run releasenotes2json; npm run branch2json; npm run patches",
|
||||
"patches": "patch -p1 < scripts/react-native-camera-kit.patch;",
|
||||
"patches": "",
|
||||
"test": "npm run tslint && npm run lint && npm run unit && npm run jest",
|
||||
"jest": "jest tests/integration/*",
|
||||
"e2e:debug-build": "detox build -c android.debug",
|
||||
@ -127,12 +127,12 @@
|
||||
"react-native": "0.75.4",
|
||||
"react-native-biometrics": "3.0.1",
|
||||
"react-native-blue-crypto": "github:BlueWallet/react-native-blue-crypto#3cb5442",
|
||||
"react-native-camera-kit": "13.0.0",
|
||||
"react-native-camera-kit": "14.1.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": "13.2.0",
|
||||
"react-native-document-picker": "9.3.1",
|
||||
"react-native-draggable-flatlist": "github:BlueWallet/react-native-draggable-flatlist#3a61627",
|
||||
"react-native-draglist": "github:BlueWallet/react-native-draglist#a4af02f",
|
||||
"react-native-fs": "2.20.0",
|
||||
"react-native-gesture-handler": "2.21.2",
|
||||
"react-native-handoff": "github:BlueWallet/react-native-handoff#v0.0.4",
|
||||
@ -142,7 +142,7 @@
|
||||
"react-native-keychain": "9.1.0",
|
||||
"react-native-linear-gradient": "2.8.3",
|
||||
"react-native-localize": "3.3.0",
|
||||
"react-native-permissions": "5.2.1",
|
||||
"react-native-permissions": "5.2.2",
|
||||
"react-native-prompt-android": "github:BlueWallet/react-native-prompt-android#ed168d66fed556bc2ed07cf498770f058b78a376",
|
||||
"react-native-push-notification": "8.1.1",
|
||||
"react-native-qrcode-svg": "6.3.2",
|
||||
|
@ -2,23 +2,18 @@ import { useFocusEffect, useIsFocused, useNavigation, useRoute } from '@react-na
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
import createHash from 'create-hash';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { Alert, Image, Platform, StyleSheet, TextInput, TouchableOpacity, View } from 'react-native';
|
||||
import { CameraScreen } from 'react-native-camera-kit';
|
||||
import { Icon } from '@rneui/themed';
|
||||
import { launchImageLibrary } from 'react-native-image-picker';
|
||||
|
||||
import { Alert, Platform, StyleSheet, TextInput, TouchableOpacity, View } from 'react-native';
|
||||
import Base43 from '../../blue_modules/base43';
|
||||
import * as fs from '../../blue_modules/fs';
|
||||
import { BlueURDecoder, decodeUR, extractSingleWorkload } from '../../blue_modules/ur';
|
||||
import { BlueLoading, BlueSpacing40, BlueText } from '../../BlueComponents';
|
||||
import { openPrivacyDesktopSettings } from '../../class/camera';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import Button from '../../components/Button';
|
||||
import { useTheme } from '../../components/themes';
|
||||
import { isCameraAuthorizationStatusGranted } from '../../helpers/scan-qr';
|
||||
import loc from '../../loc';
|
||||
import { useSettings } from '../../hooks/context/useSettings';
|
||||
import RNQRGenerator from 'rn-qr-generator';
|
||||
import CameraScreen from '../../components/CameraScreen';
|
||||
|
||||
let decoder = false;
|
||||
|
||||
@ -27,39 +22,6 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
backgroundColor: '#000000',
|
||||
},
|
||||
closeTouch: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
backgroundColor: 'rgba(0,0,0,0.4)',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 20,
|
||||
position: 'absolute',
|
||||
left: 16,
|
||||
top: 55,
|
||||
},
|
||||
closeImage: {
|
||||
alignSelf: 'center',
|
||||
},
|
||||
imagePickerTouch: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
backgroundColor: 'rgba(0,0,0,0.4)',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 20,
|
||||
position: 'absolute',
|
||||
left: 24,
|
||||
bottom: 48,
|
||||
},
|
||||
filePickerTouch: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
backgroundColor: 'rgba(0,0,0,0.4)',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 20,
|
||||
position: 'absolute',
|
||||
left: 96,
|
||||
bottom: 48,
|
||||
},
|
||||
openSettingsContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
@ -71,6 +33,9 @@ const styles = StyleSheet.create({
|
||||
height: 60,
|
||||
backgroundColor: 'rgba(0,0,0,0.01)',
|
||||
position: 'absolute',
|
||||
top: 10,
|
||||
left: '50%',
|
||||
transform: [{ translateX: -30 }],
|
||||
},
|
||||
backdoorInputWrapper: { position: 'absolute', left: '5%', top: '0%', width: '90%', height: '70%', backgroundColor: 'white' },
|
||||
progressWrapper: { position: 'absolute', alignSelf: 'center', alignItems: 'center', top: '50%', padding: 8, borderRadius: 8 },
|
||||
@ -93,7 +58,7 @@ const ScanQRCode = () => {
|
||||
const previousRoute = navigationState.routes[navigationState.routes.length - 2];
|
||||
const defaultLaunchedBy = previousRoute ? previousRoute.name : undefined;
|
||||
|
||||
const { launchedBy = defaultLaunchedBy, onBarScanned, onDismiss, showFileImportButton } = route.params || {};
|
||||
const { launchedBy = defaultLaunchedBy, onBarScanned, showFileImportButton } = route.params || {};
|
||||
const scannedCache = {};
|
||||
const { colors } = useTheme();
|
||||
const isFocused = useIsFocused();
|
||||
@ -292,59 +257,19 @@ const ScanQRCode = () => {
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const showImagePicker = () => {
|
||||
const onShowImagePickerButtonPress = () => {
|
||||
if (!isLoading) {
|
||||
setIsLoading(true);
|
||||
launchImageLibrary(
|
||||
{
|
||||
title: null,
|
||||
mediaType: 'photo',
|
||||
takePhotoButtonTitle: null,
|
||||
maxHeight: 800,
|
||||
maxWidth: 600,
|
||||
selectionLimit: 1,
|
||||
},
|
||||
response => {
|
||||
if (response.didCancel) {
|
||||
setIsLoading(false);
|
||||
} else {
|
||||
const asset = response.assets[0];
|
||||
if (asset.uri) {
|
||||
RNQRGenerator.detect({
|
||||
uri: decodeURI(asset.uri.toString()),
|
||||
})
|
||||
.then(result => {
|
||||
if (result) {
|
||||
onBarCodeRead({ data: result.values[0] });
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
presentAlert({ message: loc.send.qr_error_no_qrcode });
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
});
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
fs.showImagePickerAndReadImage()
|
||||
.then(data => {
|
||||
if (data) onBarCodeRead({ data });
|
||||
})
|
||||
.finally(() => setIsLoading(false));
|
||||
}
|
||||
};
|
||||
|
||||
const dismiss = () => {
|
||||
if (launchedBy) {
|
||||
let merge = true;
|
||||
if (typeof onBarScanned !== 'function') {
|
||||
merge = false;
|
||||
}
|
||||
navigation.navigate({ name: launchedBy, params: {}, merge });
|
||||
} else {
|
||||
navigation.goBack();
|
||||
}
|
||||
if (onDismiss) onDismiss();
|
||||
navigation.goBack();
|
||||
};
|
||||
|
||||
const render = isLoading ? (
|
||||
@ -365,29 +290,13 @@ const ScanQRCode = () => {
|
||||
cameraFlipImage={require('../../img/camera-rotate-solid.png')}
|
||||
onReadCode={event => onBarCodeRead({ data: event?.nativeEvent?.codeStringValue })}
|
||||
showFrame={false}
|
||||
showFilePickerButton={showFileImportButton}
|
||||
showImagePickerButton={true}
|
||||
onFilePickerButtonPress={showFilePicker}
|
||||
onImagePickerButtonPress={onShowImagePickerButtonPress}
|
||||
onCancelButtonPress={dismiss}
|
||||
/>
|
||||
) : null}
|
||||
<TouchableOpacity accessibilityRole="button" accessibilityLabel={loc._.close} style={styles.closeTouch} onPress={dismiss}>
|
||||
<Image style={styles.closeImage} source={require('../../img/close-white.png')} />
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.pick_image}
|
||||
style={styles.imagePickerTouch}
|
||||
onPress={showImagePicker}
|
||||
>
|
||||
<Icon name="image" type="font-awesome" color="#ffffff" />
|
||||
</TouchableOpacity>
|
||||
{showFileImportButton && (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.pick_file}
|
||||
style={styles.filePickerTouch}
|
||||
onPress={showFilePicker}
|
||||
>
|
||||
<Icon name="file-import" type="font-awesome-5" color="#ffffff" />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{urTotal > 0 && (
|
||||
<View style={[styles.progressWrapper, stylesHook.progressWrapper]} testID="UrProgressBar">
|
||||
<BlueText>{loc.wallets.please_continue_scanning}</BlueText>
|
||||
|
@ -100,28 +100,28 @@ const LightningSettings: React.FC = () => {
|
||||
|
||||
setURI(typeof setLndHubUrl === 'string' ? setLndHubUrl.trim() : value.trim());
|
||||
};
|
||||
const save = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await DefaultPreference.setName(GROUP_IO_BLUEWALLET);
|
||||
if (URI) {
|
||||
const normalizedURI = new URL(URI.replace(/([^:]\/)\/+/g, '$1')).toString();
|
||||
await LightningCustodianWallet.isValidNodeAddress(normalizedURI);
|
||||
const save = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await DefaultPreference.setName(GROUP_IO_BLUEWALLET);
|
||||
if (URI) {
|
||||
const normalizedURI = new URL(URI.replace(/([^:]\/)\/+/g, '$1')).toString();
|
||||
await LightningCustodianWallet.isValidNodeAddress(normalizedURI);
|
||||
|
||||
await setLNDHub(normalizedURI);
|
||||
} else {
|
||||
await clearLNDHub();
|
||||
await setLNDHub(normalizedURI);
|
||||
} else {
|
||||
await clearLNDHub();
|
||||
}
|
||||
|
||||
presentAlert({ message: loc.settings.lightning_saved, type: AlertType.Toast });
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
} catch (error) {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
presentAlert({ message: loc.settings.lightning_error_lndhub_uri });
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
presentAlert({ message: loc.settings.lightning_saved, type: AlertType.Toast });
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
|
||||
} catch (error) {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.NotificationError);
|
||||
presentAlert({ message: loc.settings.lightning_error_lndhub_uri });
|
||||
console.log(error);
|
||||
}
|
||||
setIsLoading(false);
|
||||
}, [URI]);
|
||||
setIsLoading(false);
|
||||
}, [URI]);
|
||||
|
||||
const importScan = () => {
|
||||
navigate('ScanQRCode', {
|
||||
|
@ -1,13 +1,5 @@
|
||||
import React, { useEffect, useLayoutEffect, useReducer, useCallback, useMemo, useRef } from 'react';
|
||||
import { StyleSheet, TouchableOpacity, Image, Text, Alert, I18nManager, Animated, LayoutAnimation } from 'react-native';
|
||||
import {
|
||||
NestableScrollContainer,
|
||||
ScaleDecorator,
|
||||
OpacityDecorator,
|
||||
NestableDraggableFlatList,
|
||||
RenderItem,
|
||||
// @ts-expect-error: react-native-draggable-flatlist is not typed
|
||||
} from 'react-native-draggable-flatlist';
|
||||
import React, { useEffect, useLayoutEffect, useReducer, useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { StyleSheet, TouchableOpacity, Image, Text, Alert, I18nManager, Animated, LayoutAnimation, FlatList } from 'react-native';
|
||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||
import { useFocusEffect, useNavigation } from '@react-navigation/native';
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
|
||||
@ -25,6 +17,7 @@ import prompt from '../../helpers/prompt';
|
||||
import HeaderRightButton from '../../components/HeaderRightButton';
|
||||
import { ManageWalletsListItem } from '../../components/ManageWalletsListItem';
|
||||
import { useSettings } from '../../hooks/context/useSettings';
|
||||
import DragList, { DragListRenderItemInfo } from 'react-native-draglist';
|
||||
|
||||
enum ItemType {
|
||||
WalletSection = 'wallet',
|
||||
@ -206,21 +199,24 @@ const ManageWallets: React.FC = () => {
|
||||
color: colors.foregroundColor,
|
||||
},
|
||||
};
|
||||
const [data, setData] = useState(state.tempOrder);
|
||||
const listRef = useRef<FlatList<Item> | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch({
|
||||
type: SET_INITIAL_ORDER,
|
||||
payload: { wallets: walletsRef.current, txMetadata },
|
||||
});
|
||||
setData(state.tempOrder);
|
||||
}, [state.tempOrder]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch({ type: SET_INITIAL_ORDER, payload: { wallets: walletsRef.current, txMetadata } });
|
||||
}, [txMetadata]);
|
||||
|
||||
useEffect(() => {
|
||||
if (debouncedSearchQuery) {
|
||||
dispatch({ type: SET_FILTERED_ORDER, payload: debouncedSearchQuery });
|
||||
} else {
|
||||
dispatch({ type: SET_INITIAL_ORDER, payload: { wallets: walletsRef.current, txMetadata } });
|
||||
dispatch({ type: SET_TEMP_ORDER, payload: state.order });
|
||||
}
|
||||
}, [debouncedSearchQuery, txMetadata]);
|
||||
}, [debouncedSearchQuery, state.order]);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
if (state.searchQuery.length === 0 && !state.isSearchFocused) {
|
||||
@ -244,6 +240,7 @@ const ManageWallets: React.FC = () => {
|
||||
dispatch({ type: SET_IS_SEARCH_FOCUSED, payload: false });
|
||||
}
|
||||
}, [goBack, setWalletsWithNewOrder, state.searchQuery, state.isSearchFocused, state.tempOrder, navigation]);
|
||||
|
||||
const hasUnsavedChanges = useMemo(() => {
|
||||
return JSON.stringify(walletsRef.current) !== JSON.stringify(state.tempOrder.map(item => item.data));
|
||||
}, [state.tempOrder]);
|
||||
@ -319,6 +316,14 @@ const ManageWallets: React.FC = () => {
|
||||
}, [hasUnsavedChanges, navigation, setIsDrawerShouldHide]),
|
||||
);
|
||||
|
||||
// Ensure the listener is re-added every time there are unsaved changes
|
||||
useEffect(() => {
|
||||
if (beforeRemoveListenerRef.current) {
|
||||
navigation.removeListener('beforeRemove', beforeRemoveListenerRef.current);
|
||||
navigation.addListener('beforeRemove', beforeRemoveListenerRef.current);
|
||||
}
|
||||
}, [hasUnsavedChanges, navigation]);
|
||||
|
||||
const renderHighlightedText = useCallback(
|
||||
(text: string, query: string) => {
|
||||
const parts = text.split(new RegExp(`(${query})`, 'gi'));
|
||||
@ -425,60 +430,43 @@ const ManageWallets: React.FC = () => {
|
||||
},
|
||||
[goBack, navigate],
|
||||
);
|
||||
const renderWalletItem = useCallback(
|
||||
({ item, drag, isActive }: RenderItem<Item>) => (
|
||||
<ScaleDecorator drag={drag} activeScale={1.1}>
|
||||
<OpacityDecorator activeOpacity={0.5}>
|
||||
<ManageWalletsListItem
|
||||
item={item}
|
||||
isDraggingDisabled={state.searchQuery.length > 0 || state.isSearchFocused}
|
||||
drag={drag}
|
||||
state={state}
|
||||
navigateToWallet={navigateToWallet}
|
||||
renderHighlightedText={renderHighlightedText}
|
||||
handleDeleteWallet={handleDeleteWallet}
|
||||
handleToggleHideBalance={handleToggleHideBalance}
|
||||
/>
|
||||
</OpacityDecorator>
|
||||
</ScaleDecorator>
|
||||
),
|
||||
const renderItem = useCallback(
|
||||
(info: DragListRenderItemInfo<Item>) => {
|
||||
const { item, onDragStart, onDragEnd, isActive } = info;
|
||||
return (
|
||||
<ManageWalletsListItem
|
||||
item={item}
|
||||
onPressIn={state.isSearchFocused || state.searchQuery.length > 0 ? undefined : onDragStart}
|
||||
onPressOut={state.isSearchFocused || state.searchQuery.length > 0 ? undefined : onDragEnd}
|
||||
isDraggingDisabled={state.searchQuery.length > 0 || state.isSearchFocused}
|
||||
state={state}
|
||||
navigateToWallet={navigateToWallet}
|
||||
renderHighlightedText={renderHighlightedText}
|
||||
handleDeleteWallet={handleDeleteWallet}
|
||||
handleToggleHideBalance={handleToggleHideBalance}
|
||||
isActive={isActive}
|
||||
drag={state.isSearchFocused || state.searchQuery.length > 0 ? undefined : onDragStart}
|
||||
/>
|
||||
);
|
||||
},
|
||||
[state, navigateToWallet, renderHighlightedText, handleDeleteWallet, handleToggleHideBalance],
|
||||
);
|
||||
|
||||
const renderPlaceholder = useCallback(
|
||||
({ item, drag, isActive }: RenderItem<Item>) => (
|
||||
<ManageWalletsListItem
|
||||
item={item}
|
||||
isDraggingDisabled={state.searchQuery.length > 0 || state.isSearchFocused}
|
||||
state={state}
|
||||
navigateToWallet={navigateToWallet}
|
||||
renderHighlightedText={renderHighlightedText}
|
||||
isPlaceHolder
|
||||
handleDeleteWallet={handleDeleteWallet}
|
||||
handleToggleHideBalance={handleToggleHideBalance}
|
||||
/>
|
||||
),
|
||||
[handleDeleteWallet, handleToggleHideBalance, navigateToWallet, renderHighlightedText, state],
|
||||
);
|
||||
|
||||
const onChangeOrder = useCallback(() => {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.ImpactMedium);
|
||||
}, []);
|
||||
|
||||
const onDragBegin = useCallback(() => {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.Selection);
|
||||
}, []);
|
||||
|
||||
const onRelease = useCallback(() => {
|
||||
triggerHapticFeedback(HapticFeedbackTypes.ImpactLight);
|
||||
}, []);
|
||||
const onDragEnd = useCallback(
|
||||
({ data }: { data: Item[] }) => {
|
||||
const updatedWallets = data.filter((item): item is WalletItem => item.type === ItemType.WalletSection).map(item => item.data);
|
||||
|
||||
dispatch({ type: SET_INITIAL_ORDER, payload: { wallets: updatedWallets, txMetadata: state.txMetadata } });
|
||||
const onReordered = useCallback(
|
||||
(fromIndex: number, toIndex: number) => {
|
||||
const copy = [...state.order];
|
||||
const removed = copy.splice(fromIndex, 1);
|
||||
copy.splice(toIndex, 0, removed[0]);
|
||||
dispatch({ type: SET_TEMP_ORDER, payload: copy });
|
||||
dispatch({
|
||||
type: SET_INITIAL_ORDER,
|
||||
payload: {
|
||||
wallets: copy.filter(item => item.type === ItemType.WalletSection).map(item => item.data as TWallet),
|
||||
txMetadata: state.txMetadata,
|
||||
},
|
||||
});
|
||||
},
|
||||
[state.txMetadata],
|
||||
[state.order, state.txMetadata],
|
||||
);
|
||||
|
||||
const keyExtractor = useCallback((item: Item, index: number) => index.toString(), []);
|
||||
@ -499,39 +487,21 @@ const ManageWallets: React.FC = () => {
|
||||
|
||||
return (
|
||||
<GestureHandlerRootView style={[{ backgroundColor: colors.background }, styles.root]}>
|
||||
<NestableScrollContainer contentInsetAdjustmentBehavior="automatic" automaticallyAdjustContentInsets scrollEnabled>
|
||||
<>
|
||||
{renderHeader}
|
||||
<NestableDraggableFlatList
|
||||
data={state.tempOrder.filter((item): item is WalletItem => item.type === ItemType.WalletSection)}
|
||||
extraData={state.tempOrder}
|
||||
keyExtractor={keyExtractor}
|
||||
renderItem={renderWalletItem}
|
||||
onChangeOrder={onChangeOrder}
|
||||
onDragBegin={onDragBegin}
|
||||
onPlaceholderIndexChange={onChangeOrder}
|
||||
onRelease={onRelease}
|
||||
delayLongPress={150}
|
||||
useNativeDriver={true}
|
||||
dragItemOverflow
|
||||
autoscrollThreshold={1}
|
||||
renderPlaceholder={renderPlaceholder}
|
||||
autoscrollSpeed={0.5}
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
<DragList
|
||||
automaticallyAdjustContentInsets
|
||||
onDragEnd={onDragEnd}
|
||||
containerStyle={styles.root}
|
||||
/>
|
||||
<NestableDraggableFlatList
|
||||
data={state.tempOrder.filter((item): item is TransactionItem => item.type === ItemType.TransactionSection)}
|
||||
keyExtractor={keyExtractor}
|
||||
renderItem={renderWalletItem}
|
||||
dragItemOverflow
|
||||
containerStyle={styles.root}
|
||||
automaticallyAdjustKeyboardInsets
|
||||
automaticallyAdjustsScrollIndicatorInsets
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
automaticallyAdjustContentInsets
|
||||
useNativeDriver={true}
|
||||
data={data}
|
||||
containerStyle={[{ backgroundColor: colors.background }, styles.root]}
|
||||
keyExtractor={keyExtractor}
|
||||
onReordered={onReordered}
|
||||
renderItem={renderItem}
|
||||
ref={listRef}
|
||||
/>
|
||||
</NestableScrollContainer>
|
||||
</>
|
||||
</GestureHandlerRootView>
|
||||
);
|
||||
};
|
||||
|
@ -50,6 +50,7 @@ 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';
|
||||
|
||||
type RouteParams = RouteProp<ViewEditMultisigCosignersStackParamList, 'ViewEditMultisigCosigners'>;
|
||||
type NavigationProp = NativeStackNavigationProp<ViewEditMultisigCosignersStackParamList, 'ViewEditMultisigCosigners'>;
|
||||
@ -78,6 +79,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
const [exportStringURv2, setExportStringURv2] = useState(''); // used in QR
|
||||
const [exportFilename, setExportFilename] = useState('bw-cosigner.json');
|
||||
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[]>();
|
||||
/* discardChangesRef is only so the action sheet can be shown on mac catalyst when a
|
||||
@ -275,6 +277,24 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const resetModalData = () => {
|
||||
setVaultKeyData({
|
||||
keyIndex: 1,
|
||||
xpub: '',
|
||||
seed: '',
|
||||
passphrase: '',
|
||||
path: '',
|
||||
fp: '',
|
||||
isLoading: false,
|
||||
});
|
||||
setImportText('');
|
||||
setExportString('{}');
|
||||
setExportStringURv2('');
|
||||
setExportFilename('');
|
||||
setIsSaveButtonDisabled(false);
|
||||
setAskPassphrase(false);
|
||||
};
|
||||
|
||||
const _renderKeyItem = (el: ListRenderItemInfo<any>) => {
|
||||
if (!wallet) {
|
||||
// failsafe
|
||||
@ -312,29 +332,34 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
buttonType: MultipleStepsListItemButtohType.partial,
|
||||
leftText,
|
||||
text: loc.multisig.view,
|
||||
showActivityIndicator: isVaultKeyIndexDataLoading === el.index + 1,
|
||||
disabled: vaultKeyData.isLoading,
|
||||
onPress: () => {
|
||||
const keyIndex = el.index + 1;
|
||||
const xpub = wallet.getCosigner(keyIndex);
|
||||
const fp = wallet.getFingerprint(keyIndex);
|
||||
const path = wallet.getCustomDerivationPathForCosigner(keyIndex);
|
||||
if (!path) {
|
||||
presentAlert({ message: 'Cannot find derivation path for this cosigner' });
|
||||
return;
|
||||
}
|
||||
setVaultKeyData({
|
||||
keyIndex,
|
||||
seed: '',
|
||||
passphrase: '',
|
||||
xpub,
|
||||
fp,
|
||||
path,
|
||||
isLoading: false,
|
||||
});
|
||||
setExportString(MultisigCosigner.exportToJson(fp, xpub, path));
|
||||
setExportStringURv2(encodeUR(MultisigCosigner.exportToJson(fp, xpub, path))[0]);
|
||||
setExportFilename('bw-cosigner-' + fp + '.json');
|
||||
mnemonicsModalRef.current?.present();
|
||||
setIsVaultKeyIndexDataLoading(el.index + 1);
|
||||
setTimeout(() => {
|
||||
const keyIndex = el.index + 1;
|
||||
const xpub = wallet.getCosigner(keyIndex);
|
||||
const fp = wallet.getFingerprint(keyIndex);
|
||||
const path = wallet.getCustomDerivationPathForCosigner(keyIndex);
|
||||
if (!path) {
|
||||
presentAlert({ message: 'Cannot find derivation path for this cosigner' });
|
||||
return;
|
||||
}
|
||||
setVaultKeyData({
|
||||
keyIndex,
|
||||
seed: '',
|
||||
passphrase: '',
|
||||
xpub,
|
||||
fp,
|
||||
path,
|
||||
isLoading: false,
|
||||
});
|
||||
setExportString(MultisigCosigner.exportToJson(fp, xpub, path));
|
||||
setExportStringURv2(encodeUR(MultisigCosigner.exportToJson(fp, xpub, path))[0]);
|
||||
setExportFilename('bw-cosigner-' + fp + '.json');
|
||||
mnemonicsModalRef.current?.present();
|
||||
setIsVaultKeyIndexDataLoading(undefined);
|
||||
}, 100);
|
||||
},
|
||||
}}
|
||||
dashes={MultipleStepsListItemDashType.topAndBottom}
|
||||
@ -363,31 +388,36 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
leftText,
|
||||
text: loc.multisig.view,
|
||||
disabled: vaultKeyData.isLoading,
|
||||
showActivityIndicator: isVaultKeyIndexDataLoading === el.index + 1,
|
||||
buttonType: MultipleStepsListItemButtohType.partial,
|
||||
onPress: () => {
|
||||
const keyIndex = el.index + 1;
|
||||
const seed = wallet.getCosigner(keyIndex);
|
||||
const passphrase = wallet.getCosignerPassphrase(keyIndex);
|
||||
setVaultKeyData({
|
||||
keyIndex,
|
||||
seed,
|
||||
xpub: '',
|
||||
fp: '',
|
||||
path: '',
|
||||
passphrase: passphrase ?? '',
|
||||
isLoading: false,
|
||||
});
|
||||
mnemonicsModalRef.current?.present();
|
||||
const fp = wallet.getFingerprint(keyIndex);
|
||||
const path = wallet.getCustomDerivationPathForCosigner(keyIndex);
|
||||
if (!path) {
|
||||
presentAlert({ message: 'Cannot find derivation path for this cosigner' });
|
||||
return;
|
||||
}
|
||||
const xpub = wallet.convertXpubToMultisignatureXpub(MultisigHDWallet.seedToXpub(seed, path, passphrase));
|
||||
setExportString(MultisigCosigner.exportToJson(fp, xpub, path));
|
||||
setExportStringURv2(encodeUR(MultisigCosigner.exportToJson(fp, xpub, path))[0]);
|
||||
setExportFilename('bw-cosigner-' + fp + '.json');
|
||||
setIsVaultKeyIndexDataLoading(el.index + 1);
|
||||
setTimeout(() => {
|
||||
const keyIndex = el.index + 1;
|
||||
const seed = wallet.getCosigner(keyIndex);
|
||||
const passphrase = wallet.getCosignerPassphrase(keyIndex);
|
||||
setVaultKeyData({
|
||||
keyIndex,
|
||||
seed,
|
||||
xpub: '',
|
||||
fp: '',
|
||||
path: '',
|
||||
passphrase: passphrase ?? '',
|
||||
isLoading: false,
|
||||
});
|
||||
const fp = wallet.getFingerprint(keyIndex);
|
||||
const path = wallet.getCustomDerivationPathForCosigner(keyIndex);
|
||||
if (!path) {
|
||||
presentAlert({ message: 'Cannot find derivation path for this cosigner' });
|
||||
return;
|
||||
}
|
||||
const xpub = wallet.convertXpubToMultisignatureXpub(MultisigHDWallet.seedToXpub(seed, path, passphrase));
|
||||
setExportString(MultisigCosigner.exportToJson(fp, xpub, path));
|
||||
setExportStringURv2(encodeUR(MultisigCosigner.exportToJson(fp, xpub, path))[0]);
|
||||
setExportFilename('bw-cosigner-' + fp + '.json');
|
||||
mnemonicsModalRef.current?.present();
|
||||
setIsVaultKeyIndexDataLoading(undefined);
|
||||
}, 100);
|
||||
},
|
||||
}}
|
||||
dashes={MultipleStepsListItemDashType.topAndBottom}
|
||||
@ -442,6 +472,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
await provideMnemonicsModalRef.current?.dismiss();
|
||||
await shareModalRef.current?.dismiss();
|
||||
await mnemonicsModalRef.current?.dismiss();
|
||||
resetModalData();
|
||||
};
|
||||
const handleUseMnemonicPhrase = async () => {
|
||||
let passphrase;
|
||||
@ -478,9 +509,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
setWallet(wallet);
|
||||
provideMnemonicsModalRef.current?.dismiss();
|
||||
setIsSaveButtonDisabled(false);
|
||||
setImportText('');
|
||||
setAskPassphrase(false);
|
||||
resetModalData();
|
||||
};
|
||||
|
||||
const xpubInsteadOfSeed = (index: number): Promise<void> => {
|
||||
@ -517,8 +546,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
const hideProvideMnemonicsModal = () => {
|
||||
Keyboard.dismiss();
|
||||
provideMnemonicsModalRef.current?.dismiss();
|
||||
setImportText('');
|
||||
setAskPassphrase(false);
|
||||
resetModalData();
|
||||
};
|
||||
|
||||
const hideShareModal = () => {};
|
||||
@ -583,13 +611,15 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
backgroundColor={colors.elevated}
|
||||
shareContent={{ fileName: exportFilename, fileContent: exportString }}
|
||||
>
|
||||
<View style={styles.alignItemsCenter}>
|
||||
<Text style={[styles.headerText, stylesHook.textDestination]}>
|
||||
{loc.multisig.this_is_cosigners_xpub} {Platform.OS === 'ios' ? loc.multisig.this_is_cosigners_xpub_airdrop : ''}
|
||||
</Text>
|
||||
<BlueSpacing20 />
|
||||
<QRCodeComponent value={exportStringURv2} size={260} isLogoRendered={false} />
|
||||
</View>
|
||||
<SafeArea>
|
||||
<View style={styles.alignItemsCenter}>
|
||||
<Text style={[styles.headerText, stylesHook.textDestination]}>
|
||||
{loc.multisig.this_is_cosigners_xpub} {Platform.OS === 'ios' ? loc.multisig.this_is_cosigners_xpub_airdrop : ''}
|
||||
</Text>
|
||||
<BlueSpacing20 />
|
||||
<QRCodeComponent value={exportStringURv2} size={260} isLogoRendered={false} />
|
||||
</View>
|
||||
</SafeArea>
|
||||
</BottomModal>
|
||||
);
|
||||
};
|
||||
|
@ -38,6 +38,10 @@ import { CommonToolTipActions } from '../../typings/CommonToolTipActions';
|
||||
import { useSettings } from '../../hooks/context/useSettings';
|
||||
import { isDesktop } from '../../blue_modules/environment';
|
||||
import { useKeyboard } from '../../hooks/useKeyboard';
|
||||
import {
|
||||
DoneAndDismissKeyboardInputAccessory,
|
||||
DoneAndDismissKeyboardInputAccessoryViewID,
|
||||
} from '../../components/DoneAndDismissKeyboardInputAccessory';
|
||||
|
||||
const staticCache = {};
|
||||
|
||||
@ -684,7 +688,16 @@ const WalletsAddMultisigStep2 = () => {
|
||||
<BlueTextCentered>{loc.multisig.type_your_mnemonics}</BlueTextCentered>
|
||||
<BlueSpacing20 />
|
||||
<View style={styles.multiLineTextInput}>
|
||||
<BlueFormMultiInput value={importText} onChangeText={setImportText} />
|
||||
<BlueFormMultiInput
|
||||
value={importText}
|
||||
onChangeText={setImportText}
|
||||
inputAccessoryViewID={DoneAndDismissKeyboardInputAccessoryViewID}
|
||||
/>
|
||||
{Platform.select({
|
||||
ios: <DoneAndDismissKeyboardInputAccessory />,
|
||||
android: isVisible && <DoneAndDismissKeyboardInputAccessory />,
|
||||
})}
|
||||
|
||||
<BlueSpacing20 />
|
||||
</View>
|
||||
</BottomModal>
|
||||
|
@ -1,46 +0,0 @@
|
||||
--- ../node_modules/react-native-camera-kit/android/src/main/java/com/rncamerakit/CKCamera.kt 2023-11-10 11:25:36
|
||||
+++ ../node_modules/react-native-camera-kit/android/src/main/java/com/rncamerakit/CKCamera.kt 2023-11-10 11:25:42
|
||||
@@ -180,7 +180,7 @@
|
||||
orientationListener!!.enable()
|
||||
|
||||
val scaleDetector = ScaleGestureDetector(context, object: ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
||||
- override fun onScale(detector: ScaleGestureDetector?): Boolean {
|
||||
+ override fun onScale(detector: ScaleGestureDetector): Boolean {
|
||||
if (zoomMode == "off") return true
|
||||
val cameraControl = camera?.cameraControl ?: return true
|
||||
val zoom = camera?.cameraInfo?.zoomState?.value?.zoomRatio ?: return true
|
||||
|
||||
--- ../node_modules/react-native-camera-kit/dist/CameraScreen.js 2024-09-01 13:00:57
|
||||
+++ ../node_modules/react-native-camera-kit/dist/CameraScreen.js 2024-09-01 13:00:46
|
||||
@@ -61,14 +61,14 @@
|
||||
</TouchableOpacity>));
|
||||
}
|
||||
renderTorchButton() {
|
||||
- return (!this.isCaptureRetakeMode() && (<TouchableOpacity style={{ paddingHorizontal: 15 }} onPress={() => this.onSetTorch()}>
|
||||
- <Image style={[{ flex: 1, justifyContent: 'center' }, this.props.torchImageStyle]} source={this.state.torchMode ? this.props.torchOnImage : this.props.torchOffImage} resizeMode="contain"/>
|
||||
+ return (!this.isCaptureRetakeMode() && (<TouchableOpacity style={{ backgroundColor: '#FFFFFF', borderRadius: 20, height: 40, marginHorizontal: 15 }} onPress={() => this.onSetTorch()}>
|
||||
+ <Image style={[{ width: 40, height: 40, justifyContent: 'center' }, this.props.torchImageStyle]} source={this.state.torchMode ? this.props.torchOnImage : this.props.torchOffImage} resizeMode="contain"/>
|
||||
</TouchableOpacity>));
|
||||
}
|
||||
renderSwitchCameraButton() {
|
||||
return (this.props.cameraFlipImage &&
|
||||
- !this.isCaptureRetakeMode() && (<TouchableOpacity style={{ paddingHorizontal: 15 }} onPress={() => this.onSwitchCameraPressed()}>
|
||||
- <Image style={{ flex: 1, justifyContent: 'center' }} source={this.props.cameraFlipImage} resizeMode="contain"/>
|
||||
+ !this.isCaptureRetakeMode() && (<TouchableOpacity style={{ }} onPress={() => this.onSwitchCameraPressed()}>
|
||||
+ <Image style={{ width: 40, height: 40, justifyContent: 'center' }} source={this.props.cameraFlipImage} resizeMode="contain"/>
|
||||
</TouchableOpacity>));
|
||||
}
|
||||
renderTopButtons() {
|
||||
\ No newline at end of file
|
||||
@@ -228,8 +228,8 @@
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
- paddingTop: 8,
|
||||
- paddingBottom: 0,
|
||||
+ paddingTop:44,
|
||||
+ paddingHorizontal: 16,
|
||||
},
|
||||
cameraContainer: Object.assign({}, Platform.select({
|
||||
android: {
|
||||
\ No newline at end of file
|
@ -87,17 +87,17 @@ const icons = {
|
||||
ScanQR: { iconValue: Platform.OS === 'ios' ? 'qrcode.viewfinder' : 'ic_menu_camera' },
|
||||
ChoosePhoto: { iconValue: Platform.OS === 'ios' ? 'photo.on.rectangle' : 'ic_menu_gallery' },
|
||||
Clipboard: { iconValue: Platform.OS === 'ios' ? 'document.on.clipboard' : 'ic_menu_file' },
|
||||
ExportPrivateKey: { iconValue: 'key' },
|
||||
Share: { iconValue: 'square.and.arrow.up' },
|
||||
Signature: { iconValue: 'signature' },
|
||||
PasteFromClipboard: { iconValue: 'document.on.clipboard' },
|
||||
ImportFile: { iconValue: 'document.viewfinder' },
|
||||
Hide: { iconValue: 'eye.slash' },
|
||||
ClearClipboard: { iconValue: 'clipboard' },
|
||||
SortASC: { iconValue: 'arrow.down.to.line' },
|
||||
SortDESC: { iconValue: 'arrow.up.to.line' },
|
||||
SaveFile: { iconValue: 'square.and.arrow.down' },
|
||||
Delete: { iconValue: 'trash' },
|
||||
ExportPrivateKey: { iconValue: Platform.OS === 'ios' ? 'key' : 'ic_lock_idle_lock' },
|
||||
Share: { iconValue: Platform.OS === 'ios' ? 'square.and.arrow.up' : 'ic_menu_share' },
|
||||
Signature: { iconValue: Platform.OS === 'ios' ? 'signature' : 'ic_menu_edit' },
|
||||
PasteFromClipboard: { iconValue: Platform.OS === 'ios' ? 'document.on.clipboard' : 'ic_menu_paste' },
|
||||
ImportFile: { iconValue: Platform.OS === 'ios' ? 'document.viewfinder' : 'ic_menu_upload' },
|
||||
Hide: { iconValue: Platform.OS === 'ios' ? 'eye.slash' : 'ic_menu_close_clear_cancel' },
|
||||
ClearClipboard: { iconValue: Platform.OS === 'ios' ? 'clipboard' : 'ic_menu_delete' },
|
||||
SortASC: { iconValue: Platform.OS === 'ios' ? 'arrow.down.to.line' : 'ic_menu_sort_alphabetically' },
|
||||
SortDESC: { iconValue: Platform.OS === 'ios' ? 'arrow.up.to.line' : 'ic_menu_sort_by_size' },
|
||||
SaveFile: { iconValue: Platform.OS === 'ios' ? 'square.and.arrow.down' : 'ic_menu_save' },
|
||||
Delete: { iconValue: Platform.OS === 'ios' ? 'trash' : 'ic_menu_delete' },
|
||||
} as const;
|
||||
|
||||
export type ToolTipAction = {
|
||||
|
Loading…
Reference in New Issue
Block a user