mirror of
https://github.com/aliasvault/aliasvault.git
synced 2025-12-23 22:28:22 -05:00
Replace expo-camera which uses non-FOSS libs with react-native-vision-camera (#1405)
This commit is contained in:
committed by
Leendert de Borst
parent
c459a48927
commit
6a4fbb9193
@@ -52,6 +52,9 @@ expo.webp.animated=false
|
||||
# Enable network inspector
|
||||
EX_DEV_CLIENT_NETWORK_INSPECTOR=true
|
||||
|
||||
# Enable VisionCamera code scanner
|
||||
VisionCamera_enableCodeScanner=true
|
||||
|
||||
# Use legacy packaging to compress native libraries in the resulting APK.
|
||||
expo.useLegacyPackaging=false
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { CameraView, useCameraPermissions } from 'expo-camera';
|
||||
import { Href, router, useLocalSearchParams } from 'expo-router';
|
||||
import { useEffect, useCallback, useRef } from 'react';
|
||||
import { View, Alert, StyleSheet } from 'react-native';
|
||||
import { View, Alert, StyleSheet, Linking } from 'react-native';
|
||||
import { Camera, useCameraDevice, useCameraPermission, useCodeScanner } from 'react-native-vision-camera';
|
||||
|
||||
import { useColors } from '@/hooks/useColorScheme';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
@@ -54,7 +54,8 @@ function parseQRCode(data: string): ScannedQRCode {
|
||||
export default function QRScannerScreen() : React.ReactNode {
|
||||
const colors = useColors();
|
||||
const { t } = useTranslation();
|
||||
const [permission, requestPermission] = useCameraPermissions();
|
||||
const { hasPermission, requestPermission } = useCameraPermission();
|
||||
const device = useCameraDevice('back');
|
||||
const { url } = useLocalSearchParams<{ url?: string }>();
|
||||
const hasProcessedUrl = useRef(false);
|
||||
const processedUrls = useRef(new Set<string>());
|
||||
@@ -65,35 +66,51 @@ export default function QRScannerScreen() : React.ReactNode {
|
||||
* Request camera permission.
|
||||
*/
|
||||
const requestCameraPermission = async () : Promise<void> => {
|
||||
if (!permission) {
|
||||
if (hasPermission === undefined) {
|
||||
return; // Still loading permission status
|
||||
}
|
||||
|
||||
if (!permission.granted && permission.canAskAgain) {
|
||||
// Request permission
|
||||
await requestPermission();
|
||||
} else if (!permission.granted && !permission.canAskAgain) {
|
||||
// Permission was permanently denied
|
||||
Alert.alert(
|
||||
t('settings.qrScanner.cameraPermissionTitle'),
|
||||
t('settings.qrScanner.cameraPermissionMessage'),
|
||||
[{ text: t('common.ok'), /**
|
||||
* Go back to the settings tab.
|
||||
*/
|
||||
onPress: (): void => router.back() }]
|
||||
);
|
||||
if (!hasPermission) {
|
||||
const permission = await requestPermission();
|
||||
|
||||
if (!permission) {
|
||||
// Permission was denied
|
||||
Alert.alert(
|
||||
t('settings.qrScanner.cameraPermissionTitle'),
|
||||
t('settings.qrScanner.cameraPermissionMessage'),
|
||||
[
|
||||
{ text: t('common.cancel'),
|
||||
/**
|
||||
* Go back to the settings tab.
|
||||
*/
|
||||
onPress: (): void => router.back(),
|
||||
style: 'cancel'
|
||||
},
|
||||
{
|
||||
text: t('common.openSettings'),
|
||||
/**
|
||||
* Open app settings.
|
||||
*/
|
||||
onPress: (): void => {
|
||||
Linking.openSettings();
|
||||
router.back();
|
||||
}
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
requestCameraPermission();
|
||||
}, [permission, requestPermission, t]);
|
||||
}, [hasPermission, requestPermission, t]);
|
||||
|
||||
/*
|
||||
* Handle barcode scanned - parse and navigate to appropriate page.
|
||||
* Only processes AliasVault QR codes, silently ignores others.
|
||||
* Validation is handled by the destination page.
|
||||
*/
|
||||
const handleBarcodeScanned = useCallback(({ data }: { data: string }) : void => {
|
||||
const handleBarcodeScanned = useCallback((data: string) : void => {
|
||||
// Prevent processing the same URL multiple times
|
||||
if (processedUrls.current.has(data)) {
|
||||
return;
|
||||
@@ -119,6 +136,20 @@ export default function QRScannerScreen() : React.ReactNode {
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Configure code scanner
|
||||
const codeScanner = useCodeScanner({
|
||||
codeTypes: ['qr'],
|
||||
/**
|
||||
* Handle QR code scanned.
|
||||
* @param codes - Scanned codes.
|
||||
*/
|
||||
onCodeScanned: (codes) => {
|
||||
if (codes.length > 0 && codes[0]?.value) {
|
||||
handleBarcodeScanned(codes[0].value);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Reset hasProcessedUrl when URL changes to allow processing new URLs.
|
||||
*/
|
||||
@@ -132,7 +163,7 @@ export default function QRScannerScreen() : React.ReactNode {
|
||||
useEffect(() => {
|
||||
if (url && typeof url === 'string' && !hasProcessedUrl.current) {
|
||||
hasProcessedUrl.current = true;
|
||||
handleBarcodeScanned({ data: url });
|
||||
handleBarcodeScanned(url);
|
||||
}
|
||||
}, [url, handleBarcodeScanned]);
|
||||
|
||||
@@ -179,8 +210,8 @@ export default function QRScannerScreen() : React.ReactNode {
|
||||
},
|
||||
});
|
||||
|
||||
// Show permission request screen
|
||||
if (!permission || !permission.granted) {
|
||||
// Show permission request screen or loading if device not ready
|
||||
if (hasPermission === undefined || !hasPermission || !device) {
|
||||
return (
|
||||
<ThemedContainer>
|
||||
<View style={styles.loadingContainer}>
|
||||
@@ -193,13 +224,11 @@ export default function QRScannerScreen() : React.ReactNode {
|
||||
return (
|
||||
<ThemedContainer style={styles.container}>
|
||||
<View style={styles.cameraContainer}>
|
||||
<CameraView
|
||||
<Camera
|
||||
style={styles.camera}
|
||||
facing="back"
|
||||
barcodeScannerSettings={{
|
||||
barcodeTypes: ['qr'],
|
||||
}}
|
||||
onBarcodeScanned={handleBarcodeScanned}
|
||||
device={device}
|
||||
isActive={true}
|
||||
codeScanner={codeScanner}
|
||||
>
|
||||
<View style={styles.cameraOverlay}>
|
||||
<Ionicons name="qr-code-outline" size={100} color={colors.white} />
|
||||
@@ -207,7 +236,7 @@ export default function QRScannerScreen() : React.ReactNode {
|
||||
{t('settings.qrScanner.scanningMessage')}
|
||||
</ThemedText>
|
||||
</View>
|
||||
</CameraView>
|
||||
</Camera>
|
||||
</View>
|
||||
</ThemedContainer>
|
||||
);
|
||||
|
||||
@@ -272,10 +272,6 @@ PODS:
|
||||
- ExpoModulesCore
|
||||
- ExpoBlur (14.1.5):
|
||||
- ExpoModulesCore
|
||||
- ExpoCamera (16.1.11):
|
||||
- ExpoModulesCore
|
||||
- ZXingObjC/OneD
|
||||
- ZXingObjC/PDF417
|
||||
- ExpoClipboard (7.1.5):
|
||||
- ExpoModulesCore
|
||||
- ExpoDocumentPicker (13.1.6):
|
||||
@@ -2448,12 +2444,13 @@ PODS:
|
||||
- SQLite.swift/standard (0.14.1)
|
||||
- SwiftLint (0.59.1)
|
||||
- SWXMLHash (7.0.2)
|
||||
- VisionCamera (4.7.3):
|
||||
- VisionCamera/Core (= 4.7.3)
|
||||
- VisionCamera/React (= 4.7.3)
|
||||
- VisionCamera/Core (4.7.3)
|
||||
- VisionCamera/React (4.7.3):
|
||||
- React-Core
|
||||
- Yoga (0.0.0)
|
||||
- ZXingObjC/Core (3.6.9)
|
||||
- ZXingObjC/OneD (3.6.9):
|
||||
- ZXingObjC/Core
|
||||
- ZXingObjC/PDF417 (3.6.9):
|
||||
- ZXingObjC/Core
|
||||
|
||||
DEPENDENCIES:
|
||||
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
|
||||
@@ -2468,7 +2465,6 @@ DEPENDENCIES:
|
||||
- expo-dev-menu-interface (from `../node_modules/expo-dev-menu-interface/ios`)
|
||||
- ExpoAsset (from `../node_modules/expo-asset/ios`)
|
||||
- ExpoBlur (from `../node_modules/expo-blur/ios`)
|
||||
- ExpoCamera (from `../node_modules/expo-camera/ios`)
|
||||
- ExpoClipboard (from `../node_modules/expo-clipboard/ios`)
|
||||
- ExpoDocumentPicker (from `../node_modules/expo-document-picker/ios`)
|
||||
- ExpoFileSystem (from `../node_modules/expo-file-system/ios`)
|
||||
@@ -2571,6 +2567,7 @@ DEPENDENCIES:
|
||||
- SignalArgon2 (~> 1.3.2)
|
||||
- SQLite.swift (~> 0.14.0)
|
||||
- SwiftLint
|
||||
- VisionCamera (from `../node_modules/react-native-vision-camera`)
|
||||
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
|
||||
|
||||
SPEC REPOS:
|
||||
@@ -2582,7 +2579,6 @@ SPEC REPOS:
|
||||
- SQLite.swift
|
||||
- SwiftLint
|
||||
- SWXMLHash
|
||||
- ZXingObjC
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
boost:
|
||||
@@ -2609,8 +2605,6 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/expo-asset/ios"
|
||||
ExpoBlur:
|
||||
:path: "../node_modules/expo-blur/ios"
|
||||
ExpoCamera:
|
||||
:path: "../node_modules/expo-camera/ios"
|
||||
ExpoClipboard:
|
||||
:path: "../node_modules/expo-clipboard/ios"
|
||||
ExpoDocumentPicker:
|
||||
@@ -2804,6 +2798,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-screens"
|
||||
RNSVG:
|
||||
:path: "../node_modules/react-native-svg"
|
||||
VisionCamera:
|
||||
:path: "../node_modules/react-native-vision-camera"
|
||||
Yoga:
|
||||
:path: "../node_modules/react-native/ReactCommon/yoga"
|
||||
|
||||
@@ -2820,7 +2816,6 @@ SPEC CHECKSUMS:
|
||||
expo-dev-menu-interface: 609c35ae8b97479cdd4c9e23c8cf6adc44beea0e
|
||||
ExpoAsset: ef06e880126c375f580d4923fdd1cdf4ee6ee7d6
|
||||
ExpoBlur: 3c8885b9bf9eef4309041ec87adec48b5f1986a9
|
||||
ExpoCamera: e1879906d41184e84b57d7643119f8509414e318
|
||||
ExpoClipboard: 436f6de6971f14eb75ae160e076d9cb3b19eb795
|
||||
ExpoDocumentPicker: b263a279685b6640b8c8bc70d71c83067aeaae55
|
||||
ExpoFileSystem: 7f92f7be2f5c5ed40a7c9efc8fa30821181d9d63
|
||||
@@ -2924,8 +2919,8 @@ SPEC CHECKSUMS:
|
||||
SQLite.swift: 2992550ebf3c5b268bf4352603e3df87d2a4ed72
|
||||
SwiftLint: 3d48e2fb2a3468fdaccf049e5e755df22fb40c2c
|
||||
SWXMLHash: dd733a457e9c4fe93b1538654057aefae4acb382
|
||||
VisionCamera: 7187b3dac1ff3071234ead959ce311875748e14f
|
||||
Yoga: dc7c21200195acacb62fa920c588e7c2106de45e
|
||||
ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5
|
||||
|
||||
PODFILE CHECKSUM: ac288e273086bafdd610cafff08ccca0d164f7c3
|
||||
|
||||
|
||||
58
apps/mobile-app/package-lock.json
generated
58
apps/mobile-app/package-lock.json
generated
@@ -17,10 +17,8 @@
|
||||
"@types/jsrsasign": "^10.5.15",
|
||||
"expo": "^53.0.22",
|
||||
"expo-blur": "~14.1.5",
|
||||
"expo-camera": "^16.1.11",
|
||||
"expo-clipboard": "~7.1.5",
|
||||
"expo-constants": "~17.1.7",
|
||||
"expo-dev-client": "~5.1.8",
|
||||
"expo-document-picker": "~13.1.6",
|
||||
"expo-file-system": "~18.1.11",
|
||||
"expo-font": "~13.3.2",
|
||||
@@ -56,6 +54,7 @@
|
||||
"react-native-screens": "~4.15.4",
|
||||
"react-native-svg": "15.11.2",
|
||||
"react-native-toast-message": "^2.2.1",
|
||||
"react-native-vision-camera": "^4.7.3",
|
||||
"react-native-webview": "13.13.5",
|
||||
"secure-remote-password": "github:LinusU/secure-remote-password#73e5f732b6ca0cdbdc19da1a0c5f48cdbad2cbf0",
|
||||
"yup": "^1.6.1"
|
||||
@@ -77,6 +76,7 @@
|
||||
"eslint-config-expo": "~9.2.0",
|
||||
"eslint-plugin-jsdoc": "^55.2.0",
|
||||
"eslint-plugin-react-native": "^5.0.0",
|
||||
"expo-dev-client": "~5.1.8",
|
||||
"globals": "^16.3.0",
|
||||
"jest": "^29.2.1",
|
||||
"jest-expo": "~53.0.0",
|
||||
@@ -8756,26 +8756,6 @@
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-camera": {
|
||||
"version": "16.1.11",
|
||||
"resolved": "https://registry.npmjs.org/expo-camera/-/expo-camera-16.1.11.tgz",
|
||||
"integrity": "sha512-etA5ZKoC6nPBnWWqiTmlX//zoFZ6cWQCCIdmpUHTGHAKd4qZNCkhPvBWbi8o32pDe57lix1V4+TPFgEcvPwsaA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"invariant": "^2.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"expo": "*",
|
||||
"react": "*",
|
||||
"react-native": "*",
|
||||
"react-native-web": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-native-web": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/expo-clipboard": {
|
||||
"version": "7.1.5",
|
||||
"resolved": "https://registry.npmjs.org/expo-clipboard/-/expo-clipboard-7.1.5.tgz",
|
||||
@@ -8806,6 +8786,7 @@
|
||||
"version": "5.1.8",
|
||||
"resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-5.1.8.tgz",
|
||||
"integrity": "sha512-IopYPgBi3JflksO5ieTphbKsbYHy9iIVdT/d69It++y0iBMSm0oBIoDmUijrHKjE3fV6jnrwrm8luU13/mzIQQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"expo-dev-launcher": "5.1.11",
|
||||
@@ -8822,6 +8803,7 @@
|
||||
"version": "5.1.11",
|
||||
"resolved": "https://registry.npmjs.org/expo-dev-launcher/-/expo-dev-launcher-5.1.11.tgz",
|
||||
"integrity": "sha512-bN0+nv5H038s8Gzf8i16hwCyD3sWDmHp7vb+QbL1i6B3XNnICCKS/H/3VH6H3PRMvCmoLGPlg+ODDqGlf0nu3g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "8.11.0",
|
||||
@@ -8837,6 +8819,7 @@
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
|
||||
"integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
@@ -8853,12 +8836,14 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/expo-dev-menu": {
|
||||
"version": "6.1.10",
|
||||
"resolved": "https://registry.npmjs.org/expo-dev-menu/-/expo-dev-menu-6.1.10.tgz",
|
||||
"integrity": "sha512-LaI0Bw5zzw5XefjYSX6YaMydzk0YBysjqQoxzj6ufDyKgwAfPmFwOLkZ03DOSerc9naezGLNAGgTEN6QTgMmgQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"expo-dev-menu-interface": "1.10.0"
|
||||
@@ -8871,6 +8856,7 @@
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/expo-dev-menu-interface/-/expo-dev-menu-interface-1.10.0.tgz",
|
||||
"integrity": "sha512-NxtM/qot5Rh2cY333iOE87dDg1S8CibW+Wu4WdLua3UMjy81pXYzAGCZGNOeY7k9GpNFqDPNDXWyBSlk9r2pBg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"expo": "*"
|
||||
@@ -8922,6 +8908,7 @@
|
||||
"version": "0.15.0",
|
||||
"resolved": "https://registry.npmjs.org/expo-json-utils/-/expo-json-utils-0.15.0.tgz",
|
||||
"integrity": "sha512-duRT6oGl80IDzH2LD2yEFWNwGIC2WkozsB6HF3cDYNoNNdUvFk6uN3YiwsTsqVM/D0z6LEAQ01/SlYvN+Fw0JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/expo-keep-awake": {
|
||||
@@ -8989,6 +8976,7 @@
|
||||
"version": "0.16.6",
|
||||
"resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-0.16.6.tgz",
|
||||
"integrity": "sha512-1A+do6/mLUWF9xd3uCrlXr9QFDbjbfqAYmUy8UDLOjof1lMrOhyeC4Yi6WexA/A8dhZEpIxSMCKfn7G4aHAh4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@expo/config": "~11.0.12",
|
||||
@@ -9140,6 +9128,7 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/expo-updates-interface/-/expo-updates-interface-1.1.0.tgz",
|
||||
"integrity": "sha512-DeB+fRe0hUDPZhpJ4X4bFMAItatFBUPjw/TVSbJsaf3Exeami+2qbbJhWkcTMoYHOB73nOIcaYcWXYJnCJXO0w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"expo": "*"
|
||||
@@ -14963,6 +14952,30 @@
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-vision-camera": {
|
||||
"version": "4.7.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-4.7.3.tgz",
|
||||
"integrity": "sha512-g1/neOyjSqn1kaAa2FxI/qp5KzNvPcF0bnQw6NntfbxH6tm0+8WFZszlgb5OV+iYlB6lFUztCbDtyz5IpL47OA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@shopify/react-native-skia": "*",
|
||||
"react": "*",
|
||||
"react-native": "*",
|
||||
"react-native-reanimated": "*",
|
||||
"react-native-worklets-core": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@shopify/react-native-skia": {
|
||||
"optional": true
|
||||
},
|
||||
"react-native-reanimated": {
|
||||
"optional": true
|
||||
},
|
||||
"react-native-worklets-core": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-webview": {
|
||||
"version": "13.13.5",
|
||||
"resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.13.5.tgz",
|
||||
@@ -17475,6 +17488,7 @@
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"punycode": "^2.1.0"
|
||||
|
||||
@@ -38,10 +38,8 @@
|
||||
"@types/jsrsasign": "^10.5.15",
|
||||
"expo": "^53.0.22",
|
||||
"expo-blur": "~14.1.5",
|
||||
"expo-camera": "^16.1.11",
|
||||
"expo-clipboard": "~7.1.5",
|
||||
"expo-constants": "~17.1.7",
|
||||
"expo-dev-client": "~5.1.8",
|
||||
"expo-document-picker": "~13.1.6",
|
||||
"expo-file-system": "~18.1.11",
|
||||
"expo-font": "~13.3.2",
|
||||
@@ -76,7 +74,9 @@
|
||||
"react-native-safe-area-context": "5.6.1",
|
||||
"react-native-screens": "~4.15.4",
|
||||
"react-native-svg": "15.11.2",
|
||||
"react-native-svg-transformer": "^1.5.0",
|
||||
"react-native-toast-message": "^2.2.1",
|
||||
"react-native-vision-camera": "^4.7.3",
|
||||
"react-native-webview": "13.13.5",
|
||||
"secure-remote-password": "github:LinusU/secure-remote-password#73e5f732b6ca0cdbdc19da1a0c5f48cdbad2cbf0",
|
||||
"yup": "^1.6.1"
|
||||
@@ -87,6 +87,7 @@
|
||||
"@eslint/js": "^9.35.0",
|
||||
"@react-native-community/cli": "^20.0.0",
|
||||
"@stylistic/eslint-plugin": "^5.3.1",
|
||||
"expo-dev-client": "~5.1.8",
|
||||
"@types/fbemitter": "^2.0.35",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/lodash": "^4.17.16",
|
||||
@@ -101,7 +102,6 @@
|
||||
"globals": "^16.3.0",
|
||||
"jest": "^29.2.1",
|
||||
"jest-expo": "~53.0.0",
|
||||
"react-native-svg-transformer": "^1.5.0",
|
||||
"typescript": "~5.8.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
Reference in New Issue
Block a user