LOgging in with qr code added

This commit is contained in:
Milan Paunovic
2024-09-29 23:03:21 +02:00
parent 92f335543f
commit 5922179584
15 changed files with 820 additions and 993 deletions

View File

@ -1,6 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

View File

@ -2,5 +2,5 @@
<string name="app_name">Cally - Planner</string> <string name="app_name">Cally - Planner</string>
<string name="expo_splash_screen_resize_mode" translatable="false">contain</string> <string name="expo_splash_screen_resize_mode" translatable="false">contain</string>
<string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string> <string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string>
<string name="expo_system_ui_user_interface_style" translatable="false">automatic</string> <string name="expo_system_ui_user_interface_style" translatable="false">light</string>
</resources> </resources>

View File

@ -41,3 +41,7 @@ allprojects {
maven { url 'https://www.jitpack.io' } maven { url 'https://www.jitpack.io' }
} }
} }
// @generated begin expo-camera-import - expo prebuild (DO NOT MODIFY) sync-f244f4f3d8bf7229102e8f992b525b8602c74770
def expoCameraMavenPath = new File(["node", "--print", "require.resolve('expo-camera/package.json')"].execute(null, rootDir).text.trim(), "../android/maven")
allprojects { repositories { maven { url(expoCameraMavenPath) } } }
// @generated end expo-camera-import

View File

@ -6,7 +6,7 @@
"orientation": "portrait", "orientation": "portrait",
"icon": "./assets/images/icon.png", "icon": "./assets/images/icon.png",
"scheme": "myapp", "scheme": "myapp",
"userInterfaceStyle": "automatic", "userInterfaceStyle": "light",
"splash": { "splash": {
"image": "./assets/images/splash.png", "image": "./assets/images/splash.png",
"resizeMode": "contain", "resizeMode": "contain",
@ -43,6 +43,14 @@
"useFrameworks": "static" "useFrameworks": "static"
} }
} }
],
[
"expo-camera",
{
"cameraPermission": "Allow $(PRODUCT_NAME) to access your camera",
"microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone",
"recordAudioAndroid": true
}
] ]
], ],
"experiments": { "experiments": {

View File

@ -1,22 +1,60 @@
import {Button, ButtonSize, Text, TextField, View} from "react-native-ui-lib"; import {Button, ButtonSize, Dialog, Text, TextField, View} from "react-native-ui-lib";
import React, {useState} from "react"; import React, {useEffect, useState} from "react";
import {useSignIn} from "@/hooks/firebase/useSignIn"; import {useSignIn} from "@/hooks/firebase/useSignIn";
import {StyleSheet} from "react-native"; import {StyleSheet} from "react-native";
import Toast from 'react-native-toast-message'; import Toast from 'react-native-toast-message';
import {useLoginWithQrCode} from "@/hooks/firebase/useLoginWithQrCode";
import {Camera, CameraView} from 'expo-camera';
import {BarCodeScanner} from "expo-barcode-scanner";
const SignInPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"register" | "login" | "reset-password">> }) => { const SignInPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"register" | "login" | "reset-password">> }) => {
const [email, setEmail] = useState<string>(""); const [email, setEmail] = useState<string>("");
const [password, setPassword] = useState<string>(""); const [password, setPassword] = useState<string>("");
const [hasPermission, setHasPermission] = useState<boolean | null>(null);
const [scanned, setScanned] = useState<boolean>(false);
const [showCameraDialog, setShowCameraDialog] = useState<boolean>(false);
const {mutateAsync: signIn, error, isError} = useSignIn(); const {mutateAsync: signIn, error, isError} = useSignIn();
const {mutateAsync: signInWithQrCode} = useLoginWithQrCode()
const handleSignIn = async () => { const handleSignIn = async () => {
await signIn({email, password}); await signIn({email, password});
if(!isError) { if(!isError) {
Toast.show({ Toast.show({
type: "success", type: "success",
text1: "Password successfully reset, please check your messages." text1: "Login successful!"
}) });
} else {
Toast.show({
type: "error",
text1: "Error logging in",
text2: `${error}`
});
}
};
const handleQrCodeScanned = async ({ data }: { data: string }) => {
setShowCameraDialog(false);
try {
await signInWithQrCode({ userId: data });
Toast.show({
type: "success",
text1: "Login successful with QR code!"
});
} catch (err) {
Toast.show({
type: "error",
text1: "Error logging in with QR code",
text2: `${err}`
});
}
};
const getCameraPermissions = async (callback: () => void) => {
const { status } = await Camera.requestCameraPermissionsAsync();
setHasPermission(status === 'granted');
if(status === 'granted') {
callback();
} }
}; };
@ -41,6 +79,15 @@ const SignInPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"re
style={{marginBottom: 20}} style={{marginBottom: 20}}
backgroundColor="#fd1775" backgroundColor="#fd1775"
/> />
<Button
label="Login with a QR Code"
onPress={() => {
getCameraPermissions(() => setShowCameraDialog(true));
}
}
style={{marginBottom: 20}}
backgroundColor="#fd1775"
/>
{isError && <Text center style={{marginBottom: 20}}>{`${error}`}</Text>} {isError && <Text center style={{marginBottom: 20}}>{`${error}`}</Text>}
<View row centerH marginB-5 gap-5> <View row centerH marginB-5 gap-5>
@ -76,6 +123,36 @@ const SignInPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"re
color="#fd1775" color="#fd1775"
/> />
</View> </View>
{/* Camera Dialog */}
<Dialog
visible={showCameraDialog}
onDismiss={() => setShowCameraDialog(false)}
bottom
width="100%"
height="70%"
containerStyle={{ padding: 0 }}
>
{hasPermission === null ? (
<Text>Requesting camera permissions...</Text>
) : !hasPermission ? (
<Text>No access to camera</Text>
) : (
<CameraView
style={{ flex: 1 }}
onBarcodeScanned={handleQrCodeScanned}
barcodeScannerSettings={{
barcodeTypes: ["qr"],
}}
/>
)}
<Button
label="Cancel"
onPress={() => setShowCameraDialog(false)}
backgroundColor="#fd1775"
style={{ margin: 10 }}
/>
</Dialog>
</View> </View>
); );
}; };

View File

@ -1,20 +1,34 @@
import {Avatar, Card, Colors, FloatingButton, Text, View} from "react-native-ui-lib"; import {
Avatar,
Button,
Card,
Colors,
Dialog,
FloatingButton,
PanningProvider,
Picker,
Text,
TextField,
TouchableOpacity,
View
} from "react-native-ui-lib";
import React, {useState} from "react"; import React, {useState} from "react";
import {ScrollView, StyleSheet} from "react-native"; import {ScrollView, StyleSheet} from "react-native";
import {PickerSingleValue} from "react-native-ui-lib/src/components/picker/types"; import {PickerSingleValue} from "react-native-ui-lib/src/components/picker/types";
import {useCreateSubUser} from "@/hooks/firebase/useCreateSubUser"; import {useCreateSubUser} from "@/hooks/firebase/useCreateSubUser";
import {ProfileType} from "@/contexts/AuthContext"; import {ProfileType} from "@/contexts/AuthContext";
import {useGetFamilyMembers} from "@/hooks/firebase/useGetFamilyMembers"; import {useGetFamilyMembers} from "@/hooks/firebase/useGetFamilyMembers";
import UserMenu from "@/components/pages/settings/user_settings_views/UserMenu";
const MyGroup = () => { const MyGroup = () => {
const [showAddUserDialog, setShowAddUserDialog] = useState(false); const [showAddUserDialog, setShowAddUserDialog] = useState(false);
const [showNewUserInfoDialog, setShowNewUserInfoDialog] = useState(false); const [showNewUserInfoDialog, setShowNewUserInfoDialog] = useState(false);
const [selectedStatus, setSelectedStatus] = useState<string | PickerSingleValue>('CHILD'); const [selectedStatus, setSelectedStatus] = useState<string | PickerSingleValue>(ProfileType.CHILD);
const [firstName, setFirstName] = useState(''); const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState(''); const [lastName, setLastName] = useState('');
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
const {mutateAsync: createSubUser, isLoading: loading, isError} = useCreateSubUser(); const {mutateAsync: createSubUser, isLoading, isError} = useCreateSubUser();
const {data: familyMembers} = useGetFamilyMembers(true); const {data: familyMembers} = useGetFamilyMembers(true);
const parents = familyMembers?.filter(x => x.userType === ProfileType.PARENT) ?? []; const parents = familyMembers?.filter(x => x.userType === ProfileType.PARENT) ?? [];
@ -45,13 +59,15 @@ const MyGroup = () => {
} }
}; };
console.log(familyMembers)
return ( return (
<View style={{flex: 1, height: "100%"}}> <View style={{flex: 1, height: "100%"}}>
<View> <View>
<ScrollView style={styles.card}> <ScrollView style={styles.card}>
{(!parents.length && !children.length && !caregivers.length) && ( {(!parents.length && !children.length && !caregivers.length) && (
<Text text70 marginV-10> <Text text70 marginV-10>
No user devices added {isLoading ? "Loading...." : "No user devices added"}
</Text> </Text>
)} )}
@ -73,6 +89,10 @@ const MyGroup = () => {
<Text text90 <Text text90
grey40>{member.userType === ProfileType.PARENT ? "Admin (You)" : "Child"}</Text> grey40>{member.userType === ProfileType.PARENT ? "Admin (You)" : "Child"}</Text>
</View> </View>
<View flex-1/>
<UserMenu userId={member?.uid!}/>
</Card> </Card>
))} ))}
</> </>
@ -105,10 +125,108 @@ const MyGroup = () => {
<FloatingButton fullWidth hideBackgroundOverlay visible <FloatingButton fullWidth hideBackgroundOverlay visible
button={{label: '+ Add a user device', onPress: () => setShowAddUserDialog(true)}}/> button={{label: '+ Add a user device', onPress: () => setShowAddUserDialog(true)}}/>
{/* Add user dialog here */} <Dialog
visible={showAddUserDialog}
onDismiss={() => setShowAddUserDialog(false)}
panDirection={PanningProvider.Directions.DOWN}
>
<Card padding-25 gap-10>
<Text>
Add a new user device
</Text>
{/* New user information dialog here */} <Button backgroundColor={"#FD1775"}><Text white>Show a QR Code</Text></Button>
<Button backgroundColor={"#05A8B6"} onPress={() => {
setShowAddUserDialog(false)
setTimeout(() => {
setShowNewUserInfoDialog(true)
}, 500)
}}><Text white>Enter email address</Text></Button>
<TouchableOpacity onPress={() => setShowAddUserDialog(false)} center><Text>Return to user
settings</Text></TouchableOpacity>
</Card>
</Dialog>
<Dialog
panDirection={PanningProvider.Directions.DOWN}
visible={showNewUserInfoDialog}
onDismiss={() => setShowNewUserInfoDialog(false)}
>
<Card padding-25 style={styles.dialogCard}>
<View row spread>
<Text text60M>
New User Information
</Text>
<TouchableOpacity onPress={() => setShowAddUserDialog(false)}>
<Text>X</Text>
</TouchableOpacity>
</View>
<View row centerV gap-20 marginV-20>
<Avatar imageStyle={{borderRadius: 10}} containerStyle={{borderRadius: 10,}} size={60}
backgroundColor={Colors.grey60}/>
<TouchableOpacity onPress={() => {
}}>
<Text style={{color: Colors.green10}}>Upload User Profile Photo</Text>
</TouchableOpacity>
</View>
<Text style={styles.label}>Member Status</Text>
<Picker
editable={!isLoading}
value={selectedStatus}
//@ts-ignore
onChange={item => setSelectedStatus(item)}
style={styles.picker}
showSearch
floatingPlaceholder
>
<Picker.Item label="Child" value={ProfileType.CHILD}/>
<Picker.Item label="Parent" value={ProfileType.PARENT}/>
<Picker.Item label="Caregiver" value={ProfileType.CAREGIVER}/>
</Picker>
<Text style={styles.label}>First Name</Text>
<TextField
editable={!isLoading}
placeholder="First name"
value={firstName}
onChangeText={setFirstName}
style={styles.inputField}
/>
<Text style={styles.label}>Last Name</Text>
<TextField
editable={!isLoading}
placeholder="Last name"
value={lastName}
onChangeText={setLastName}
style={styles.inputField}
/>
<Text style={styles.label}>Email Address</Text>
<TextField
editable={!isLoading}
placeholder="Email address"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
style={styles.inputField}
/>
<Button
disabled={!firstName || !lastName || !email || isLoading}
label={isLoading ? "Adding..." : "Add group member"}
backgroundColor="#FD1775"
style={{marginTop: 20}}
onPress={handleCreateSubUser}
/>
</Card>
</Dialog>
</View> </View>
); );
}; };

View File

@ -0,0 +1,62 @@
import React, {useState} from 'react';
import {Button, Card, Colors, Dialog, Hint, ListItem, Text, View} from 'react-native-ui-lib';
import QRCode from 'react-native-qrcode-svg';
import {PanningDirectionsEnum} from "react-native-ui-lib/src/components/panningViews/panningProvider";
const UserMenu = ({userId}:{userId: string}) => {
const [showHint, setShowHint] = useState(false);
const [showQRCodeDialog, setShowQRCodeDialog] = useState(false);
const handleShowQRCode = () => {
setShowHint(false);
setTimeout(() => {
setShowQRCodeDialog(true);
}, 500)
};
return (
<>
<Hint
onBackgroundPress={() => setShowHint(false)}
onPress={() => setShowHint(true)}
color={Colors.white}
customContent={
<View height={18}>
<ListItem
onPress={handleShowQRCode}
>
<Text>Show Login QR Code</Text>
</ListItem>
</View>
}
enableShadow
visible={showHint}
backdropColor="transparent"
>
<View>
<Button link onPress={() => setShowHint(x => !x)}>
<Text>...</Text>
</Button>
</View>
</Hint>
<Dialog
visible={showQRCodeDialog}
onDismiss={() => setShowQRCodeDialog(false)}
panDirection={PanningDirectionsEnum.DOWN}
>
<Card padding-20 center>
<Text marginB-10>Scan this QR Code to Login:</Text>
<QRCode value={userId} size={150} />
<Button
marginT-20
label="Close"
onPress={() => setShowQRCodeDialog(false)}
/>
</Card>
</Dialog>
</>
);
};
export default UserMenu;

View File

@ -35,9 +35,9 @@ exports.createSubUser = onRequest(async (request, response) => {
logger.info("Processing user creation", {requestBody: request.body.data}); logger.info("Processing user creation", {requestBody: request.body.data});
const {userType, firstName, lastName, email, password} = request.body.data; const {userType, firstName, lastName, email, password, familyId} = request.body.data;
if (!email || !password || !firstName || !lastName || !userType) { if (!email || !password || !firstName || !lastName || !userType || !familyId) {
logger.warn("Missing required fields in request body", {requestBody: request.body.data}); logger.warn("Missing required fields in request body", {requestBody: request.body.data});
response.status(400).json({error: "Missing required fields"}); response.status(400).json({error: "Missing required fields"});
return; return;
@ -56,7 +56,7 @@ exports.createSubUser = onRequest(async (request, response) => {
} }
const userProfile = { const userProfile = {
userType, name: `${firstName} ${lastName}`, email, uid: userRecord.uid, userType, firstName, lastName, familyId, email, uid: userRecord.uid
}; };
try { try {
@ -78,3 +78,22 @@ exports.createSubUser = onRequest(async (request, response) => {
response.status(500).json({data: {error: error.message}}); response.status(500).json({data: {error: error.message}});
} }
}); });
exports.generateCustomToken = onRequest(async (request, response) => {
try {
const {userId} = request.body.data;
console.log("Generating custom token for userId", {userId});
if (!userId) {
response.status(400).json({error: 'Missing userId'});
return;
}
const customToken = await getAuth().createCustomToken(userId);
response.status(200).json({data: {token: customToken}});
} catch (error) {
console.error("Error generating custom token", {error: error.message});
response.status(500).json({error: "Failed to generate custom token"});
}
});

View File

@ -16,6 +16,7 @@ export interface User {
email: string email: string
password: string password: string
familyId?: string familyId?: string
uid?: string
} }
export interface ParentProfile extends UserProfile { export interface ParentProfile extends UserProfile {

View File

@ -12,8 +12,9 @@ export const useCreateSubUser = () => {
mutationFn: async ({email, ...userProfile}: { email: string } & UserProfile) => { mutationFn: async ({email, ...userProfile}: { email: string } & UserProfile) => {
if (profileType === ProfileType.PARENT) { if (profileType === ProfileType.PARENT) {
return await functions().httpsCallable("createSubUser")({ return await functions().httpsCallable("createSubUser")({
...userProfile,
email, email,
userProfile: {...userProfile, familyId: profileData?.familyId} familyId: profileData?.familyId
}) })
} else { } else {
throw Error("Can't create sub-users as a non-parent.") throw Error("Can't create sub-users as a non-parent.")
@ -21,6 +22,7 @@ export const useCreateSubUser = () => {
}, },
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({queryKey: ["getChildrenByParentId"]}) queryClient.invalidateQueries({queryKey: ["getChildrenByParentId"]})
queryClient.invalidateQueries({queryKey: ["familyMembers"]})
} }
}); });
} }

View File

@ -0,0 +1,21 @@
import {useMutation} from "react-query";
import functions, {FirebaseFunctionsTypes} from '@react-native-firebase/functions';
import auth from "@react-native-firebase/auth";
export const useLoginWithQrCode = () => {
return useMutation({
mutationKey: ["loginWithQrCode"],
mutationFn: async ({userId}: { userId: string }) => {
try {
const res = await functions().httpsCallable("generateCustomToken")({userId}) as FirebaseFunctionsTypes.HttpsCallableResult<{
token: string
}>
console.log(res)
await auth().signInWithCustomToken(res?.data?.token)
} catch (e) {
console.error(e)
throw Error("Failed to login with QR code.")
}
},
});
}

View File

@ -956,23 +956,31 @@ PODS:
- BVLinearGradient (2.8.3): - BVLinearGradient (2.8.3):
- React-Core - React-Core
- DoubleConversion (1.1.6) - DoubleConversion (1.1.6)
- EXBarCodeScanner (13.0.1):
- EXImageLoader
- ExpoModulesCore
- ZXingObjC/OneD
- ZXingObjC/PDF417
- EXConstants (16.0.2): - EXConstants (16.0.2):
- ExpoModulesCore - ExpoModulesCore
- EXImageLoader (4.7.0):
- ExpoModulesCore
- React-Core
- EXJSONUtils (0.13.1) - EXJSONUtils (0.13.1)
- EXManifests (0.14.3): - EXManifests (0.14.3):
- ExpoModulesCore - ExpoModulesCore
- Expo (51.0.34): - Expo (51.0.34):
- ExpoModulesCore - ExpoModulesCore
- expo-dev-client (4.0.24): - expo-dev-client (4.0.27):
- EXManifests - EXManifests
- expo-dev-launcher - expo-dev-launcher
- expo-dev-menu - expo-dev-menu
- expo-dev-menu-interface - expo-dev-menu-interface
- EXUpdatesInterface - EXUpdatesInterface
- expo-dev-launcher (4.0.26): - expo-dev-launcher (4.0.27):
- DoubleConversion - DoubleConversion
- EXManifests - EXManifests
- expo-dev-launcher/Main (= 4.0.26) - expo-dev-launcher/Main (= 4.0.27)
- expo-dev-menu - expo-dev-menu
- expo-dev-menu-interface - expo-dev-menu-interface
- ExpoModulesCore - ExpoModulesCore
@ -998,7 +1006,7 @@ PODS:
- ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- Yoga - Yoga
- expo-dev-launcher/Main (4.0.26): - expo-dev-launcher/Main (4.0.27):
- DoubleConversion - DoubleConversion
- EXManifests - EXManifests
- expo-dev-launcher/Unsafe - expo-dev-launcher/Unsafe
@ -1027,7 +1035,7 @@ PODS:
- ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- Yoga - Yoga
- expo-dev-launcher/Unsafe (4.0.26): - expo-dev-launcher/Unsafe (4.0.27):
- DoubleConversion - DoubleConversion
- EXManifests - EXManifests
- expo-dev-menu - expo-dev-menu
@ -1055,10 +1063,10 @@ PODS:
- ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- Yoga - Yoga
- expo-dev-menu (5.0.20): - expo-dev-menu (5.0.21):
- DoubleConversion - DoubleConversion
- expo-dev-menu/Main (= 5.0.20) - expo-dev-menu/Main (= 5.0.21)
- expo-dev-menu/ReactNativeCompatibles (= 5.0.20) - expo-dev-menu/ReactNativeCompatibles (= 5.0.21)
- glog - glog
- hermes-engine - hermes-engine
- RCT-Folly (= 2024.01.01.00) - RCT-Folly (= 2024.01.01.00)
@ -1079,7 +1087,7 @@ PODS:
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- Yoga - Yoga
- expo-dev-menu-interface (1.8.3) - expo-dev-menu-interface (1.8.3)
- expo-dev-menu/Main (5.0.20): - expo-dev-menu/Main (5.0.21):
- DoubleConversion - DoubleConversion
- EXManifests - EXManifests
- expo-dev-menu-interface - expo-dev-menu-interface
@ -1105,7 +1113,7 @@ PODS:
- ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- Yoga - Yoga
- expo-dev-menu/ReactNativeCompatibles (5.0.20): - expo-dev-menu/ReactNativeCompatibles (5.0.21):
- DoubleConversion - DoubleConversion
- glog - glog
- hermes-engine - hermes-engine
@ -1126,7 +1134,7 @@ PODS:
- ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- Yoga - Yoga
- expo-dev-menu/SafeAreaView (5.0.20): - expo-dev-menu/SafeAreaView (5.0.21):
- DoubleConversion - DoubleConversion
- ExpoModulesCore - ExpoModulesCore
- glog - glog
@ -1148,7 +1156,7 @@ PODS:
- ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- Yoga - Yoga
- expo-dev-menu/Vendored (5.0.20): - expo-dev-menu/Vendored (5.0.21):
- DoubleConversion - DoubleConversion
- expo-dev-menu/SafeAreaView - expo-dev-menu/SafeAreaView
- glog - glog
@ -1172,6 +1180,10 @@ PODS:
- Yoga - Yoga
- ExpoAsset (10.0.10): - ExpoAsset (10.0.10):
- ExpoModulesCore - ExpoModulesCore
- ExpoCamera (15.0.16):
- ExpoModulesCore
- ZXingObjC/OneD
- ZXingObjC/PDF417
- ExpoFileSystem (17.0.1): - ExpoFileSystem (17.0.1):
- ExpoModulesCore - ExpoModulesCore
- ExpoFont (12.0.10): - ExpoFont (12.0.10):
@ -2713,12 +2725,19 @@ PODS:
- React-Core - React-Core
- SocketRocket (0.7.0) - SocketRocket (0.7.0)
- Yoga (0.0.0) - Yoga (0.0.0)
- ZXingObjC/Core (3.6.9)
- ZXingObjC/OneD (3.6.9):
- ZXingObjC/Core
- ZXingObjC/PDF417 (3.6.9):
- ZXingObjC/Core
DEPENDENCIES: DEPENDENCIES:
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
- BVLinearGradient (from `../node_modules/react-native-linear-gradient`) - BVLinearGradient (from `../node_modules/react-native-linear-gradient`)
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- EXBarCodeScanner (from `../node_modules/expo-barcode-scanner/ios`)
- EXConstants (from `../node_modules/expo-constants/ios`) - EXConstants (from `../node_modules/expo-constants/ios`)
- EXImageLoader (from `../node_modules/expo-image-loader/ios`)
- EXJSONUtils (from `../node_modules/expo-json-utils/ios`) - EXJSONUtils (from `../node_modules/expo-json-utils/ios`)
- EXManifests (from `../node_modules/expo-manifests/ios`) - EXManifests (from `../node_modules/expo-manifests/ios`)
- Expo (from `../node_modules/expo`) - Expo (from `../node_modules/expo`)
@ -2727,6 +2746,7 @@ DEPENDENCIES:
- expo-dev-menu (from `../node_modules/expo-dev-menu`) - expo-dev-menu (from `../node_modules/expo-dev-menu`)
- expo-dev-menu-interface (from `../node_modules/expo-dev-menu-interface/ios`) - expo-dev-menu-interface (from `../node_modules/expo-dev-menu-interface/ios`)
- ExpoAsset (from `../node_modules/expo-asset/ios`) - ExpoAsset (from `../node_modules/expo-asset/ios`)
- ExpoCamera (from `../node_modules/expo-camera/ios`)
- ExpoFileSystem (from `../node_modules/expo-file-system/ios`) - ExpoFileSystem (from `../node_modules/expo-file-system/ios`)
- ExpoFont (from `../node_modules/expo-font/ios`) - ExpoFont (from `../node_modules/expo-font/ios`)
- ExpoHead (from `../node_modules/expo-router/ios`) - ExpoHead (from `../node_modules/expo-router/ios`)
@ -2835,6 +2855,7 @@ SPEC REPOS:
- PromisesSwift - PromisesSwift
- RecaptchaInterop - RecaptchaInterop
- SocketRocket - SocketRocket
- ZXingObjC
EXTERNAL SOURCES: EXTERNAL SOURCES:
boost: boost:
@ -2843,8 +2864,12 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-linear-gradient" :path: "../node_modules/react-native-linear-gradient"
DoubleConversion: DoubleConversion:
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
EXBarCodeScanner:
:path: "../node_modules/expo-barcode-scanner/ios"
EXConstants: EXConstants:
:path: "../node_modules/expo-constants/ios" :path: "../node_modules/expo-constants/ios"
EXImageLoader:
:path: "../node_modules/expo-image-loader/ios"
EXJSONUtils: EXJSONUtils:
:path: "../node_modules/expo-json-utils/ios" :path: "../node_modules/expo-json-utils/ios"
EXManifests: EXManifests:
@ -2861,6 +2886,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/expo-dev-menu-interface/ios" :path: "../node_modules/expo-dev-menu-interface/ios"
ExpoAsset: ExpoAsset:
:path: "../node_modules/expo-asset/ios" :path: "../node_modules/expo-asset/ios"
ExpoCamera:
:path: "../node_modules/expo-camera/ios"
ExpoFileSystem: ExpoFileSystem:
:path: "../node_modules/expo-file-system/ios" :path: "../node_modules/expo-file-system/ios"
ExpoFont: ExpoFont:
@ -3017,15 +3044,18 @@ SPEC CHECKSUMS:
BoringSSL-GRPC: 1e2348957acdbcad360b80a264a90799984b2ba6 BoringSSL-GRPC: 1e2348957acdbcad360b80a264a90799984b2ba6
BVLinearGradient: 880f91a7854faff2df62518f0281afb1c60d49a3 BVLinearGradient: 880f91a7854faff2df62518f0281afb1c60d49a3
DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5
EXBarCodeScanner: e2dd9b42c1b522a2adc9202b1dfbc64cb34456d1
EXConstants: 409690fbfd5afea964e5e9d6c4eb2c2b59222c59 EXConstants: 409690fbfd5afea964e5e9d6c4eb2c2b59222c59
EXImageLoader: ab589d67d6c5f2c33572afea9917304418566334
EXJSONUtils: 30c17fd9cc364d722c0946a550dfbf1be92ef6a4 EXJSONUtils: 30c17fd9cc364d722c0946a550dfbf1be92ef6a4
EXManifests: c1fab4c3237675e7b0299ea8df0bcb14baca4f42 EXManifests: c1fab4c3237675e7b0299ea8df0bcb14baca4f42
Expo: 963ef4ae102e4f4ac114a8510d70127c44adffbf Expo: 963ef4ae102e4f4ac114a8510d70127c44adffbf
expo-dev-client: 44e9bb8afbf444bc380c606475030fe8929de203 expo-dev-client: 85deba11af998ea86e62093b17fce56aa2c6f26b
expo-dev-launcher: 012fd9aea425d902b5404e75e58d0bacf8e2542f expo-dev-launcher: fe4f2c0a0aa627449eeaec5f9f11e04090f97c40
expo-dev-menu: 045ace71676316ecac9bff8c2ac34fa4d8ef8392 expo-dev-menu: 5b14897ecce3a8cf9e9cf9109344c2c192a3766a
expo-dev-menu-interface: be32c09f1e03833050f0ee290dcc86b3ad0e73e4 expo-dev-menu-interface: be32c09f1e03833050f0ee290dcc86b3ad0e73e4
ExpoAsset: 323700f291684f110fb55f0d4022a3362ea9f875 ExpoAsset: 323700f291684f110fb55f0d4022a3362ea9f875
ExpoCamera: 929be541d1c1319fcf32f9f5d9df8b97804346b5
ExpoFileSystem: 80bfe850b1f9922c16905822ecbf97acd711dc51 ExpoFileSystem: 80bfe850b1f9922c16905822ecbf97acd711dc51
ExpoFont: 00756e6c796d8f7ee8d211e29c8b619e75cbf238 ExpoFont: 00756e6c796d8f7ee8d211e29c8b619e75cbf238
ExpoHead: fcb28a68ed4ba28f177394d2dfb8a0a8824cd103 ExpoHead: fcb28a68ed4ba28f177394d2dfb8a0a8824cd103
@ -3127,6 +3157,7 @@ SPEC CHECKSUMS:
RNSVG: 4590aa95758149fa27c5c83e54a6a466349a1688 RNSVG: 4590aa95758149fa27c5c83e54a6a466349a1688
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
Yoga: bd92064a0d558be92786820514d74fc4dddd1233 Yoga: bd92064a0d558be92786820514d74fc4dddd1233
ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5
PODFILE CHECKSUM: 50f618790da7cbbfd5c5e988b7f9370bd45d34a6 PODFILE CHECKSUM: 50f618790da7cbbfd5c5e988b7f9370bd45d34a6

View File

@ -55,6 +55,10 @@
<key>NSAllowsLocalNetworking</key> <key>NSAllowsLocalNetworking</key>
<true/> <true/>
</dict> </dict>
<key>NSCameraUsageDescription</key>
<string>Allow $(PRODUCT_NAME) to access your camera</string>
<key>NSMicrophoneUsageDescription</key>
<string>Allow $(PRODUCT_NAME) to access your microphone</string>
<key>NSUserActivityTypes</key> <key>NSUserActivityTypes</key>
<array> <array>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
@ -65,6 +69,7 @@
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
</array> </array>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>SplashScreen</string> <string>SplashScreen</string>
@ -89,7 +94,7 @@
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UIUserInterfaceStyle</key> <key>UIUserInterfaceStyle</key>
<string>Automatic</string> <string>Light</string>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<false/> <false/>
</dict> </dict>

View File

@ -39,9 +39,11 @@
"@react-navigation/native": "^6.0.2", "@react-navigation/native": "^6.0.2",
"date-fns": "^3.6.0", "date-fns": "^3.6.0",
"expo": "~51.0.24", "expo": "~51.0.24",
"expo-barcode-scanner": "~13.0.1",
"expo-build-properties": "~0.12.4", "expo-build-properties": "~0.12.4",
"expo-camera": "~15.0.16",
"expo-constants": "~16.0.2", "expo-constants": "~16.0.2",
"expo-dev-client": "~4.0.21", "expo-dev-client": "~4.0.27",
"expo-font": "~12.0.9", "expo-font": "~12.0.9",
"expo-linking": "~6.3.1", "expo-linking": "~6.3.1",
"expo-router": "~3.5.20", "expo-router": "~3.5.20",
@ -62,6 +64,7 @@
"react-native-gifted-charts": "^1.4.41", "react-native-gifted-charts": "^1.4.41",
"react-native-linear-gradient": "^2.8.3", "react-native-linear-gradient": "^2.8.3",
"react-native-onboarding-swiper": "^1.3.0", "react-native-onboarding-swiper": "^1.3.0",
"react-native-qrcode-svg": "^6.3.2",
"react-native-reanimated": "~3.10.1", "react-native-reanimated": "~3.10.1",
"react-native-safe-area-context": "4.10.5", "react-native-safe-area-context": "4.10.5",
"react-native-screens": "3.31.1", "react-native-screens": "3.31.1",

1394
yarn.lock

File diff suppressed because it is too large Load Diff