mirror of
https://github.com/urosran/cally.git
synced 2025-08-26 06:09:40 +00:00
LOgging in with qr code added
This commit is contained in:
@ -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"/>
|
||||||
|
@ -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>
|
@ -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
|
10
app.json
10
app.json
@ -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": {
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
62
components/pages/settings/user_settings_views/UserMenu.tsx
Normal file
62
components/pages/settings/user_settings_views/UserMenu.tsx
Normal 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;
|
@ -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"});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
@ -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 {
|
||||||
|
@ -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"]})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
21
hooks/firebase/useLoginWithQrCode.ts
Normal file
21
hooks/firebase/useLoginWithQrCode.ts
Normal 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.")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
@ -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
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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",
|
||||||
|
Reference in New Issue
Block a user