mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-23 15:20:55 +01:00
Merge branch 'master' into electrumpref
This commit is contained in:
commit
2dc3efd391
13 changed files with 372 additions and 239 deletions
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,
|
||||
},
|
||||
});
|
|
@ -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 };
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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):
|
||||
|
@ -2258,7 +2277,7 @@ SPEC CHECKSUMS:
|
|||
React-utils: 02526ea15628a768b8db9517b6017a1785c734d2
|
||||
ReactCodegen: 8b5341ecb61898b8bd40a73ebc443c6bf2d14423
|
||||
ReactCommon: 36d48f542b4010786d6b2bcee615fe5f906b7105
|
||||
ReactNativeCameraKit: f058d47e0b1e55fd819bb55ee16505a2e0ca53db
|
||||
ReactNativeCameraKit: e72b838dac4ea2da19b7eb5d00b23125072790fd
|
||||
RealmJS: 9fd51c849eb552ade9f7b11db42a319b4f6cab4c
|
||||
RNCAsyncStorage: c91d753ede6dc21862c4922cd13f98f7cfde578e
|
||||
RNCClipboard: dbcf25b8f666b4685c02eeb65be981d30198e505
|
||||
|
|
|
@ -9,7 +9,6 @@ export type ScanQRCodeParamList = {
|
|||
urTotal?: number;
|
||||
urHave?: number;
|
||||
backdoorText?: string;
|
||||
onDismiss?: () => void;
|
||||
onBarScanned?: (data: string) => void;
|
||||
showFileImportButton?: boolean;
|
||||
backdoorVisible?: boolean;
|
||||
|
|
28
package-lock.json
generated
28
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",
|
||||
|
@ -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": {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -2,9 +2,7 @@ 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 { 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';
|
||||
|
@ -15,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 CameraScreen from '../../components/CameraScreen';
|
||||
|
||||
let decoder = false;
|
||||
|
||||
|
@ -23,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',
|
||||
|
@ -67,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 },
|
||||
|
@ -89,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();
|
||||
|
@ -288,7 +257,7 @@ const ScanQRCode = () => {
|
|||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const showImagePicker = () => {
|
||||
const onShowImagePickerButtonPress = () => {
|
||||
if (!isLoading) {
|
||||
setIsLoading(true);
|
||||
fs.showImagePickerAndReadImage()
|
||||
|
@ -300,16 +269,7 @@ const ScanQRCode = () => {
|
|||
};
|
||||
|
||||
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 ? (
|
||||
|
@ -330,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>
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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
|
Loading…
Add table
Reference in a new issue