feat: support login using QR code (#26)

This commit is contained in:
IZUMI-Zu 2024-09-08 23:43:34 +08:00 committed by GitHub
parent 368c9672b1
commit aa7219ac65
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 282 additions and 163 deletions

View File

@ -12,12 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import React from "react";
import React, {useState} from "react";
import {ScrollView, Text, View} from "react-native";
import {Button, IconButton, Portal, TextInput} from "react-native-paper";
import Toast from "react-native-toast-message";
import DefaultCasdoorSdkConfig from "./DefaultCasdoorSdkConfig";
import PropTypes from "prop-types";
import ScanQRCodeForLogin from "./ScanLogin";
import useStore from "./useStorage";
const EnterCasdoorSdkConfig = ({onClose, onWebviewClose}) => {
@ -39,6 +40,8 @@ const EnterCasdoorSdkConfig = ({onClose, onWebviewClose}) => {
setCasdoorConfig,
} = useStore();
const [showScanner, setShowScanner] = useState(false);
const closeConfigPage = () => {
onClose();
onWebviewClose();
@ -58,12 +61,16 @@ const EnterCasdoorSdkConfig = ({onClose, onWebviewClose}) => {
};
const handleScanToLogin = () => {
Toast.show({
type: "info",
text1: "Info",
text2: "Scan to Login functionality not implemented yet.",
autoHide: true,
});
setShowScanner(true);
};
const handleLogin = (loginInfo) => {
setServerUrl(loginInfo.serverUrl);
setClientId(loginInfo.clientId);
setAppName(loginInfo.appName);
setOrganizationName(loginInfo.organizationName);
setShowScanner(false);
onClose();
};
const handleUseDefault = () => {
@ -144,6 +151,13 @@ const EnterCasdoorSdkConfig = ({onClose, onWebviewClose}) => {
</Button>
</View>
</ScrollView>
{showScanner && (
<ScanQRCodeForLogin
showScanner={showScanner}
onClose={() => setShowScanner(false)}
onLogin={handleLogin}
/>
)}
</Portal>
);
};

94
QRScanner.js Normal file
View File

@ -0,0 +1,94 @@
// Copyright 2024 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React, {useEffect, useState} from "react";
import {Text, View} from "react-native";
import {Button, IconButton, Portal} from "react-native-paper";
import {Camera, CameraView, scanFromURLAsync} from "expo-camera";
import * as ImagePicker from "expo-image-picker";
import PropTypes from "prop-types";
const QRScanner = ({onScan, onClose}) => {
const [hasPermission, setHasPermission] = useState(null);
useEffect(() => {
const getPermissions = async() => {
const {status: cameraStatus} = await Camera.requestCameraPermissionsAsync();
setHasPermission(cameraStatus === "granted");
};
getPermissions();
}, []);
const handleBarCodeScanned = ({type, data}) => {
onScan(type, data);
};
const pickImage = async() => {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
quality: 1,
});
if (!result.canceled && result.assets[0]) {
const scannedData = await scanFromURLAsync(result.assets[0].uri, ["qr", "pdf417"]);
if (scannedData[0]) {
handleBarCodeScanned({type: scannedData[0].type, data: scannedData[0].data});
}
}
};
if (hasPermission === null) {
return <Text style={{margin: "20%"}}>Requesting permissions...</Text>;
}
if (hasPermission === false) {
return <Text style={{margin: "20%"}}>No access to camera or media library</Text>;
}
return (
<View style={{marginTop: "50%", flex: 1}}>
<Portal>
<CameraView
onBarcodeScanned={handleBarCodeScanned}
barcodeScannerSettings={{
barcodeTypes: ["qr", "pdf417"],
}}
style={{flex: 1}}
/>
<IconButton
icon="close"
size={40}
onPress={onClose}
style={{position: "absolute", top: 30, right: 5}}
/>
<Button
icon="image"
mode="contained"
onPress={pickImage}
style={{position: "absolute", bottom: 20, alignSelf: "center"}}
>
Choose Image
</Button>
</Portal>
</View>
);
};
QRScanner.propTypes = {
onScan: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
};
export default QRScanner;

57
ScanLogin.js Normal file
View File

@ -0,0 +1,57 @@
// Copyright 2024 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React from "react";
import PropTypes from "prop-types";
import QRScanner from "./QRScanner";
const ScanQRCodeForLogin = ({onClose, showScanner, onLogin}) => {
const handleScan = (type, data) => {
if (isValidLoginQR(data)) {
const loginInfo = parseLoginQR(data);
onLogin(loginInfo);
onClose();
}
};
const isValidLoginQR = (data) => {
return data.startsWith("casdoor-app://login/into?");
};
const parseLoginQR = (data) => {
const url = new URL(data);
const params = new URLSearchParams(url.search);
return {
serverUrl: params.get("serverUrl"),
clientId: params.get("clientId"),
appName: params.get("appName"),
organizationName: params.get("organizationName"),
};
};
if (!showScanner) {
return null;
}
return <QRScanner onScan={handleScan} onClose={onClose} />;
};
ScanQRCodeForLogin.propTypes = {
onClose: PropTypes.func.isRequired,
onLogin: PropTypes.func.isRequired,
showScanner: PropTypes.bool.isRequired,
};
export default ScanQRCodeForLogin;

View File

@ -12,37 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import React, {useEffect, useState} from "react";
import {Text, View} from "react-native";
import {Button, IconButton, Portal} from "react-native-paper";
import {Camera, CameraView, scanFromURLAsync} from "expo-camera";
import * as ImagePicker from "expo-image-picker";
import React from "react";
import PropTypes from "prop-types";
import QRScanner from "./QRScanner";
import useProtobufDecoder from "./useProtobufDecoder";
const ScanQRCode = ({onClose, showScanner, onAdd}) => {
ScanQRCode.propTypes = {
onClose: PropTypes.func.isRequired,
onAdd: PropTypes.func.isRequired,
showScanner: PropTypes.bool.isRequired,
};
const [hasPermission, setHasPermission] = useState(null);
const decoder = useProtobufDecoder(require("./google/google_auth.proto"));
useEffect(() => {
const getPermissions = async() => {
const {status: cameraStatus} = await Camera.requestCameraPermissionsAsync();
setHasPermission(cameraStatus === "granted");
// const {status: mediaLibraryStatus} = await ImagePicker.requestMediaLibraryPermissionsAsync();
// setHasMediaLibraryPermission(mediaLibraryStatus === "granted");
};
getPermissions();
}, []);
const handleBarCodeScanned = ({type, data}) => {
// console.log(`Bar code with type ${type} and data ${data} has been scanned!`);
const handleScan = (type, data) => {
const supportedProtocols = ["otpauth", "otpauth-migration"];
const protocolMatch = data.match(new RegExp(`^(${supportedProtocols.join("|")}):`));
if (protocolMatch) {
@ -76,55 +54,17 @@ const ScanQRCode = ({onClose, showScanner, onAdd}) => {
onAdd(accounts.map(({accountName, issuer, totpSecret}) => ({accountName, issuer, secretKey: totpSecret})));
};
const pickImage = async() => {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
quality: 1,
});
if (!result.canceled && result.assets[0]) {
const scannedData = await scanFromURLAsync(result.assets[0].uri, ["qr", "pdf417"]);
if (scannedData[0]) {
handleBarCodeScanned({type: scannedData[0].type, data: scannedData[0].data});
}
}
};
if (hasPermission === null) {
return <Text style={{margin: "20%"}}>Requesting permissions...</Text>;
if (!showScanner) {
return null;
}
if (hasPermission === false) {
return <Text style={{margin: "20%"}}>No access to camera or media library</Text>;
}
return <QRScanner onScan={handleScan} onClose={onClose} />;
};
return (
<View style={{marginTop: "50%", flex: 1}}>
<Portal>
<CameraView
onBarcodeScanned={handleBarCodeScanned}
barcodeScannerSettings={{
barcodeTypes: ["qr", "pdf417"],
}}
style={{flex: 1}}
/>
<IconButton
icon="close"
size={40}
onPress={onClose}
style={{position: "absolute", top: 30, right: 5}}
/>
<Button
icon="image"
mode="contained"
onPress={pickImage}
style={{position: "absolute", bottom: 20, alignSelf: "center"}}
>
Choose Image
</Button>
</Portal>
</View>
);
ScanQRCode.propTypes = {
onClose: PropTypes.func.isRequired,
onAdd: PropTypes.func.isRequired,
showScanner: PropTypes.bool.isRequired,
};
export default ScanQRCode;

View File

@ -7,14 +7,13 @@
"icon": "./assets/icon.png",
"userInterfaceStyle": "light",
"sdkVersion": "51.0.0",
"scheme": "casdoor-app",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"assetBundlePatterns": [
"**/*"
],
"assetBundlePatterns": ["**/*"],
"ios": {
"supportsTablet": true,
"bundleIdentifier": "org.casdoor.casdoorapp",

177
package-lock.json generated
View File

@ -22,7 +22,7 @@
"expo-asset": "~10.0.10",
"expo-camera": "~15.0.15",
"expo-crypto": "~13.0.2",
"expo-dev-client": "~4.0.25",
"expo-dev-client": "~4.0.26",
"expo-drizzle-studio-plugin": "^0.0.2",
"expo-image": "~1.12.15",
"expo-image-picker": "~15.0.7",
@ -98,28 +98,30 @@
}
},
"node_modules/@babel/compat-data": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz",
"integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==",
"version": "7.25.4",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz",
"integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/core": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz",
"integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==",
"version": "7.25.2",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz",
"integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==",
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.24.7",
"@babel/generator": "^7.24.7",
"@babel/helper-compilation-targets": "^7.24.7",
"@babel/helper-module-transforms": "^7.24.7",
"@babel/helpers": "^7.24.7",
"@babel/parser": "^7.24.7",
"@babel/template": "^7.24.7",
"@babel/traverse": "^7.24.7",
"@babel/types": "^7.24.7",
"@babel/generator": "^7.25.0",
"@babel/helper-compilation-targets": "^7.25.2",
"@babel/helper-module-transforms": "^7.25.2",
"@babel/helpers": "^7.25.0",
"@babel/parser": "^7.25.0",
"@babel/template": "^7.25.0",
"@babel/traverse": "^7.25.2",
"@babel/types": "^7.25.2",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
@ -153,11 +155,12 @@
}
},
"node_modules/@babel/generator": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz",
"integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==",
"version": "7.25.6",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz",
"integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==",
"license": "MIT",
"dependencies": {
"@babel/types": "^7.24.7",
"@babel/types": "^7.25.6",
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25",
"jsesc": "^2.5.1"
@ -191,13 +194,14 @@
}
},
"node_modules/@babel/helper-compilation-targets": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz",
"integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==",
"version": "7.25.2",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz",
"integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==",
"license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.24.7",
"@babel/helper-validator-option": "^7.24.7",
"browserslist": "^4.22.2",
"@babel/compat-data": "^7.25.2",
"@babel/helper-validator-option": "^7.24.8",
"browserslist": "^4.23.1",
"lru-cache": "^5.1.1",
"semver": "^6.3.1"
},
@ -285,6 +289,7 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz",
"integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==",
"peer": true,
"dependencies": {
"@babel/types": "^7.24.7"
},
@ -317,15 +322,15 @@
}
},
"node_modules/@babel/helper-module-transforms": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz",
"integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==",
"version": "7.25.2",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz",
"integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==",
"license": "MIT",
"dependencies": {
"@babel/helper-environment-visitor": "^7.24.7",
"@babel/helper-module-imports": "^7.24.7",
"@babel/helper-simple-access": "^7.24.7",
"@babel/helper-split-export-declaration": "^7.24.7",
"@babel/helper-validator-identifier": "^7.24.7"
"@babel/helper-validator-identifier": "^7.24.7",
"@babel/traverse": "^7.25.2"
},
"engines": {
"node": ">=6.9.0"
@ -346,9 +351,10 @@
}
},
"node_modules/@babel/helper-plugin-utils": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz",
"integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==",
"version": "7.24.8",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz",
"integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
@ -421,9 +427,10 @@
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz",
"integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==",
"version": "7.24.8",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz",
"integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
@ -437,9 +444,10 @@
}
},
"node_modules/@babel/helper-validator-option": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz",
"integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==",
"version": "7.24.8",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz",
"integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
@ -459,12 +467,13 @@
}
},
"node_modules/@babel/helpers": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz",
"integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==",
"version": "7.25.6",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz",
"integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==",
"license": "MIT",
"dependencies": {
"@babel/template": "^7.24.7",
"@babel/types": "^7.24.7"
"@babel/template": "^7.25.0",
"@babel/types": "^7.25.6"
},
"engines": {
"node": ">=6.9.0"
@ -485,9 +494,13 @@
}
},
"node_modules/@babel/parser": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz",
"integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==",
"version": "7.25.6",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz",
"integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==",
"license": "MIT",
"dependencies": {
"@babel/types": "^7.25.6"
},
"bin": {
"parser": "bin/babel-parser.js"
},
@ -1647,15 +1660,16 @@
}
},
"node_modules/@babel/plugin-transform-react-jsx": {
"version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.15.tgz",
"integrity": "sha512-oKckg2eZFa8771O/5vi7XeTvmM6+O9cxZu+kanTU7tD4sin5nO/G8jGJhq8Hvt2Z0kUoEDRayuZLaUlYl8QuGA==",
"version": "7.25.2",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.2.tgz",
"integrity": "sha512-KQsqEAVBpU82NM/B/N9j9WOdphom1SZH3R+2V7INrQUH+V9EBFwZsEJl8eBIVeQE62FxJCc70jzEZwqU7RcVqA==",
"license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.22.5",
"@babel/helper-module-imports": "^7.22.15",
"@babel/helper-plugin-utils": "^7.22.5",
"@babel/plugin-syntax-jsx": "^7.22.5",
"@babel/types": "^7.22.15"
"@babel/helper-annotate-as-pure": "^7.24.7",
"@babel/helper-module-imports": "^7.24.7",
"@babel/helper-plugin-utils": "^7.24.8",
"@babel/plugin-syntax-jsx": "^7.24.7",
"@babel/types": "^7.25.2"
},
"engines": {
"node": ">=6.9.0"
@ -2208,9 +2222,10 @@
"integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA=="
},
"node_modules/@babel/runtime": {
"version": "7.23.1",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.1.tgz",
"integrity": "sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==",
"version": "7.25.6",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz",
"integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==",
"license": "MIT",
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
@ -2219,31 +2234,30 @@
}
},
"node_modules/@babel/template": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz",
"integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==",
"version": "7.25.0",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz",
"integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.24.7",
"@babel/parser": "^7.24.7",
"@babel/types": "^7.24.7"
"@babel/parser": "^7.25.0",
"@babel/types": "^7.25.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz",
"integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==",
"version": "7.25.6",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz",
"integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.24.7",
"@babel/generator": "^7.24.7",
"@babel/helper-environment-visitor": "^7.24.7",
"@babel/helper-function-name": "^7.24.7",
"@babel/helper-hoist-variables": "^7.24.7",
"@babel/helper-split-export-declaration": "^7.24.7",
"@babel/parser": "^7.24.7",
"@babel/types": "^7.24.7",
"@babel/generator": "^7.25.6",
"@babel/parser": "^7.25.6",
"@babel/template": "^7.25.0",
"@babel/types": "^7.25.6",
"debug": "^4.3.1",
"globals": "^11.1.0"
},
@ -2252,11 +2266,12 @@
}
},
"node_modules/@babel/types": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz",
"integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==",
"version": "7.25.6",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz",
"integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==",
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.24.7",
"@babel/helper-string-parser": "^7.24.8",
"@babel/helper-validator-identifier": "^7.24.7",
"to-fast-properties": "^2.0.0"
},
@ -10480,9 +10495,9 @@
}
},
"node_modules/expo-dev-client": {
"version": "4.0.25",
"resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-4.0.25.tgz",
"integrity": "sha512-yChhepKXvdw+1vXIayvnvU9s42DJfgmAtBC9JLu7Q+Bk/SqgLxmEBpcxj9iBhu9x79bnrbgHEkGaLi+N1PljyA==",
"version": "4.0.26",
"resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-4.0.26.tgz",
"integrity": "sha512-GM+X7bngAK2vr0YMkPnQFUFVW22eG3CjoxTJ0yUwW3RgCqFdMkTeAIS/1sEXjyNYjGkigtgtch+bdYtJxfqpuw==",
"license": "MIT",
"dependencies": {
"expo-dev-launcher": "4.0.27",

View File

@ -24,7 +24,7 @@
"expo-asset": "~10.0.10",
"expo-camera": "~15.0.15",
"expo-crypto": "~13.0.2",
"expo-dev-client": "~4.0.25",
"expo-dev-client": "~4.0.26",
"expo-drizzle-studio-plugin": "^0.0.2",
"expo-image": "~1.12.15",
"expo-image-picker": "~15.0.7",