mirror of
https://github.com/urosran/cally.git
synced 2025-07-14 09:17:19 +00:00
Added creating family devices, refetch calendar on notification received
This commit is contained in:
@ -1,322 +1,388 @@
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
Card,
|
||||
Colors,
|
||||
Dialog,
|
||||
FloatingButton,
|
||||
PanningProvider,
|
||||
Picker,
|
||||
Text,
|
||||
TextField,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
Avatar,
|
||||
Button,
|
||||
Card,
|
||||
Colors,
|
||||
Dialog,
|
||||
FloatingButton,
|
||||
PanningProvider,
|
||||
Picker,
|
||||
Text,
|
||||
TextField,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from "react-native-ui-lib";
|
||||
import React, { useState } from "react";
|
||||
import { ScrollView, StyleSheet } from "react-native";
|
||||
import { PickerSingleValue } from "react-native-ui-lib/src/components/picker/types";
|
||||
import { useCreateSubUser } from "@/hooks/firebase/useCreateSubUser";
|
||||
import { ProfileType } from "@/contexts/AuthContext";
|
||||
import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers";
|
||||
import React, {useState} from "react";
|
||||
import {ScrollView, StyleSheet} from "react-native";
|
||||
import {PickerSingleValue} from "react-native-ui-lib/src/components/picker/types";
|
||||
import {useCreateSubUser} from "@/hooks/firebase/useCreateSubUser";
|
||||
import {ProfileType} from "@/contexts/AuthContext";
|
||||
import {useGetFamilyMembers} from "@/hooks/firebase/useGetFamilyMembers";
|
||||
import UserMenu from "@/components/pages/settings/user_settings_views/UserMenu";
|
||||
import {uuidv4} from "@firebase/util";
|
||||
|
||||
const MyGroup = () => {
|
||||
const [showAddUserDialog, setShowAddUserDialog] = useState(false);
|
||||
const [showNewUserInfoDialog, setShowNewUserInfoDialog] = useState(false);
|
||||
const [selectedStatus, setSelectedStatus] = useState<
|
||||
string | PickerSingleValue
|
||||
>(ProfileType.CHILD);
|
||||
const [firstName, setFirstName] = useState("");
|
||||
const [lastName, setLastName] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [showAddUserDialog, setShowAddUserDialog] = useState(false);
|
||||
const [showNewUserInfoDialog, setShowNewUserInfoDialog] = useState(false);
|
||||
const [selectedStatus, setSelectedStatus] = useState<
|
||||
string | PickerSingleValue
|
||||
>(ProfileType.CHILD);
|
||||
const [firstName, setFirstName] = useState("");
|
||||
const [lastName, setLastName] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
|
||||
const { mutateAsync: createSubUser, isLoading, isError } = useCreateSubUser();
|
||||
const { data: familyMembers } = useGetFamilyMembers(true);
|
||||
const [showQRCodeDialog, setShowQRCodeDialog] = useState("");
|
||||
|
||||
const parents =
|
||||
familyMembers?.filter((x) => x.userType === ProfileType.PARENT) ?? [];
|
||||
const children =
|
||||
familyMembers?.filter((x) => x.userType === ProfileType.CHILD) ?? [];
|
||||
const caregivers =
|
||||
familyMembers?.filter((x) => x.userType === ProfileType.CAREGIVER) ?? [];
|
||||
const {mutateAsync: createSubUser, isLoading, isError} = useCreateSubUser();
|
||||
const {data: familyMembers} = useGetFamilyMembers(true);
|
||||
|
||||
const handleCreateSubUser = async () => {
|
||||
if (!firstName || !lastName || !email) {
|
||||
console.error("All fields are required");
|
||||
return;
|
||||
}
|
||||
const parents =
|
||||
familyMembers?.filter((x) => x.userType === ProfileType.PARENT) ?? [];
|
||||
const children =
|
||||
familyMembers?.filter((x) => x.userType === ProfileType.CHILD) ?? [];
|
||||
const caregivers =
|
||||
familyMembers?.filter((x) => x.userType === ProfileType.CAREGIVER) ?? [];
|
||||
const familyDevices =
|
||||
familyMembers?.filter((x) => x.userType === ProfileType.FAMILY_DEVICE) ?? [];
|
||||
|
||||
if (!email.includes("@")) {
|
||||
console.error("Invalid email address");
|
||||
return;
|
||||
}
|
||||
const handleCreateSubUser = async () => {
|
||||
if (!firstName || (selectedStatus !== ProfileType.FAMILY_DEVICE && !lastName)) {
|
||||
console.error("First name and last name are required");
|
||||
return;
|
||||
}
|
||||
|
||||
await createSubUser({
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
password: email,
|
||||
userType: selectedStatus as ProfileType,
|
||||
});
|
||||
if (selectedStatus !== ProfileType.FAMILY_DEVICE && !email) {
|
||||
console.error("Email is required for non-family device users");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isError) {
|
||||
setShowNewUserInfoDialog(false);
|
||||
}
|
||||
};
|
||||
if (email && !email.includes("@")) {
|
||||
console.error("Invalid email address");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(familyMembers);
|
||||
const res = await createSubUser({
|
||||
firstName,
|
||||
lastName: selectedStatus === ProfileType.FAMILY_DEVICE ? "" : lastName,
|
||||
email: email || `placeholder_${uuidv4().split("-")[0]}@family.device`,
|
||||
password: uuidv4(),
|
||||
userType: selectedStatus as ProfileType,
|
||||
});
|
||||
console.log(res)
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<View>
|
||||
<ScrollView style={styles.card}>
|
||||
{!parents.length && !children.length && !caregivers.length && (
|
||||
<Text text70 marginV-10>
|
||||
{isLoading ? "Loading...." : "No user devices added"}
|
||||
</Text>
|
||||
)}
|
||||
if (!isError) {
|
||||
setShowNewUserInfoDialog(false);
|
||||
|
||||
{(!!parents.length || !!children.length) && (
|
||||
<>
|
||||
<Text text70 marginV-10>
|
||||
Family
|
||||
</Text>
|
||||
{[...parents, ...children]?.map((member) => (
|
||||
<Card
|
||||
enableShadow={false}
|
||||
elevation={0}
|
||||
key={`${member.firstName}_${member.lastName}`}
|
||||
style={styles.familyCard}
|
||||
row
|
||||
centerV
|
||||
padding-10
|
||||
>
|
||||
<Avatar
|
||||
source={{ uri: "https://via.placeholder.com/60" }}
|
||||
size={40}
|
||||
backgroundColor={Colors.grey60}
|
||||
/>
|
||||
<View marginL-10>
|
||||
<Text text70M>
|
||||
{member.firstName} {member.lastName}
|
||||
</Text>
|
||||
<Text text90 grey40>
|
||||
{member.userType === ProfileType.PARENT
|
||||
? "Admin (You)"
|
||||
: "Child"}
|
||||
</Text>
|
||||
</View>
|
||||
if(res?.data?.userId) {
|
||||
setTimeout(() => {
|
||||
setShowQRCodeDialog(res.data.userId)
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
|
||||
<View flex-1 />
|
||||
|
||||
<UserMenu userId={member?.uid!} />
|
||||
</Card>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
};
|
||||
|
||||
{!!caregivers.length && (
|
||||
<>
|
||||
<Text text70 marginB-10 marginT-15>
|
||||
Caregivers
|
||||
</Text>
|
||||
{caregivers?.map((member) => (
|
||||
<Card
|
||||
enableShadow={false}
|
||||
elevation={0}
|
||||
key={`${member.firstName}_${member.lastName}`}
|
||||
style={styles.familyCard}
|
||||
row
|
||||
centerV
|
||||
padding-10
|
||||
>
|
||||
<Avatar
|
||||
source={{ uri: "https://via.placeholder.com/60" }}
|
||||
size={40}
|
||||
backgroundColor={Colors.grey60}
|
||||
/>
|
||||
<View marginL-10>
|
||||
<Text text70M>
|
||||
{member.firstName} {member.lastName}
|
||||
</Text>
|
||||
<Text text90 grey40>
|
||||
Caregiver
|
||||
</Text>
|
||||
</View>
|
||||
</Card>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</ScrollView>
|
||||
</View>
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
<View>
|
||||
<ScrollView style={styles.card}>
|
||||
{!parents.length && !children.length && !caregivers.length && (
|
||||
<Text text70 marginV-10>
|
||||
{isLoading ? "Loading...." : "No user devices added"}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
<FloatingButton
|
||||
fullWidth
|
||||
hideBackgroundOverlay
|
||||
visible
|
||||
button={{
|
||||
label: "+ Add a user device",
|
||||
onPress: () => setShowAddUserDialog(true),
|
||||
}}
|
||||
/>
|
||||
{(!!parents.length || !!children.length) && (
|
||||
<>
|
||||
<Text text70 marginV-10>
|
||||
Family
|
||||
</Text>
|
||||
{[...parents, ...children]?.map((member, index) => (
|
||||
<Card
|
||||
enableShadow={false}
|
||||
elevation={0}
|
||||
key={`${member.firstName}_${member.lastName}_${index}`}
|
||||
style={styles.familyCard}
|
||||
row
|
||||
centerV
|
||||
padding-10
|
||||
>
|
||||
<Avatar
|
||||
source={{uri: "https://via.placeholder.com/60"}}
|
||||
size={40}
|
||||
backgroundColor={Colors.grey60}
|
||||
/>
|
||||
<View marginL-10>
|
||||
<Text text70M>
|
||||
{member.firstName} {member.lastName}
|
||||
</Text>
|
||||
<Text text90 grey40>
|
||||
{member.userType === ProfileType.PARENT
|
||||
? "Admin (You)"
|
||||
: "Child"}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<Dialog
|
||||
visible={showAddUserDialog}
|
||||
onDismiss={() => setShowAddUserDialog(false)}
|
||||
panDirection={PanningProvider.Directions.DOWN}
|
||||
>
|
||||
<Card padding-25 gap-10>
|
||||
<Text>Add a new user device</Text>
|
||||
<View flex-1/>
|
||||
|
||||
<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>
|
||||
<UserMenu setShowQRCodeDialog={(val) => setShowQRCodeDialog("")} showQRCodeDialog={showQRCodeDialog === member?.uid} userId={member?.uid!}/>
|
||||
</Card>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
|
||||
<TouchableOpacity onPress={() => setShowAddUserDialog(false)} center>
|
||||
<Text>Return to user settings</Text>
|
||||
</TouchableOpacity>
|
||||
</Card>
|
||||
</Dialog>
|
||||
{!!caregivers.length && (
|
||||
<>
|
||||
<Text text70 marginB-10 marginT-15>
|
||||
Caregivers
|
||||
</Text>
|
||||
{caregivers?.map((member) => (
|
||||
<Card
|
||||
enableShadow={false}
|
||||
elevation={0}
|
||||
key={`${member.firstName}_${member.lastName}`}
|
||||
style={styles.familyCard}
|
||||
row
|
||||
centerV
|
||||
padding-10
|
||||
>
|
||||
<Avatar
|
||||
source={{uri: "https://via.placeholder.com/60"}}
|
||||
size={40}
|
||||
backgroundColor={Colors.grey60}
|
||||
/>
|
||||
<View marginL-10>
|
||||
<Text text70M>
|
||||
{member.firstName} {member.lastName}
|
||||
</Text>
|
||||
<Text text90 grey40>
|
||||
Caregiver
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<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>
|
||||
<UserMenu setShowQRCodeDialog={(val) => setShowQRCodeDialog("")} showQRCodeDialog={showQRCodeDialog === member?.uid} userId={member?.uid!}/>
|
||||
</Card>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
|
||||
<View row centerV gap-20 marginV-20>
|
||||
<Avatar
|
||||
imageStyle={{ borderRadius: 10 }}
|
||||
containerStyle={{ borderRadius: 10 }}
|
||||
size={60}
|
||||
backgroundColor={Colors.grey60}
|
||||
{!!familyDevices.length && (
|
||||
<>
|
||||
<Text text70 marginB-10 marginT-15>
|
||||
Family Devices
|
||||
</Text>
|
||||
{familyDevices?.map((member, index) => (
|
||||
<Card
|
||||
enableShadow={false}
|
||||
elevation={0}
|
||||
key={`${member.firstName}_${member.lastName}_${index}`}
|
||||
style={styles.familyCard}
|
||||
row
|
||||
centerV
|
||||
padding-10
|
||||
>
|
||||
<Avatar
|
||||
source={{uri: "https://via.placeholder.com/60"}}
|
||||
size={40}
|
||||
backgroundColor={Colors.grey60}
|
||||
/>
|
||||
<View marginL-10>
|
||||
<Text text70M>
|
||||
{member.firstName}
|
||||
</Text>
|
||||
<Text text90 grey40>
|
||||
Family Device
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<UserMenu setShowQRCodeDialog={(val) => setShowQRCodeDialog("")} showQRCodeDialog={showQRCodeDialog === member?.uid} userId={member?.uid!}/>
|
||||
</Card>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
||||
<FloatingButton
|
||||
fullWidth
|
||||
hideBackgroundOverlay
|
||||
visible
|
||||
button={{
|
||||
label: "+ Add a user device",
|
||||
onPress: () => setShowAddUserDialog(true),
|
||||
}}
|
||||
/>
|
||||
<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>
|
||||
<Dialog
|
||||
visible={showAddUserDialog}
|
||||
onDismiss={() => setShowAddUserDialog(false)}
|
||||
panDirection={PanningProvider.Directions.DOWN}
|
||||
>
|
||||
<Card padding-25 gap-10>
|
||||
<Text>Add a new user device</Text>
|
||||
|
||||
<Text style={styles.label}>First Name</Text>
|
||||
<TextField
|
||||
editable={!isLoading}
|
||||
placeholder="First name"
|
||||
value={firstName}
|
||||
onChangeText={setFirstName}
|
||||
style={styles.inputField}
|
||||
/>
|
||||
<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>
|
||||
|
||||
<Text style={styles.label}>Last Name</Text>
|
||||
<TextField
|
||||
editable={!isLoading}
|
||||
placeholder="Last name"
|
||||
value={lastName}
|
||||
onChangeText={setLastName}
|
||||
style={styles.inputField}
|
||||
/>
|
||||
<TouchableOpacity onPress={() => setShowAddUserDialog(false)} center>
|
||||
<Text>Return to user settings</Text>
|
||||
</TouchableOpacity>
|
||||
</Card>
|
||||
</Dialog>
|
||||
|
||||
<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}
|
||||
/>
|
||||
<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>
|
||||
|
||||
<Button
|
||||
disabled={!firstName || !lastName || !email || isLoading}
|
||||
label={isLoading ? "Adding..." : "Add group member"}
|
||||
backgroundColor="#FD1775"
|
||||
style={{ marginTop: 20 }}
|
||||
onPress={handleCreateSubUser}
|
||||
/>
|
||||
</Card>
|
||||
</Dialog>
|
||||
</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.Item label="Family Device" value={ProfileType.FAMILY_DEVICE}/>
|
||||
</Picker>
|
||||
|
||||
<Text style={styles.label}>
|
||||
{selectedStatus === ProfileType.FAMILY_DEVICE ? "Device Name" : "First Name"}
|
||||
</Text>
|
||||
<TextField
|
||||
editable={!isLoading}
|
||||
placeholder={selectedStatus === ProfileType.FAMILY_DEVICE ? "Device name" : "First name"}
|
||||
value={firstName}
|
||||
onChangeText={setFirstName}
|
||||
style={styles.inputField}
|
||||
/>
|
||||
|
||||
{selectedStatus !== ProfileType.FAMILY_DEVICE && (
|
||||
<>
|
||||
<Text style={styles.label}>Last Name</Text>
|
||||
<TextField
|
||||
editable={!isLoading}
|
||||
placeholder="Last name"
|
||||
value={lastName}
|
||||
onChangeText={setLastName}
|
||||
style={styles.inputField}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{selectedStatus !== ProfileType.FAMILY_DEVICE && (
|
||||
<>
|
||||
<Text style={styles.label}>Email Address (Optional)</Text>
|
||||
<TextField
|
||||
editable={!isLoading}
|
||||
placeholder="Email address"
|
||||
value={email}
|
||||
onChangeText={setEmail}
|
||||
keyboardType="email-address"
|
||||
autoCapitalize="none"
|
||||
style={styles.inputField}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Button
|
||||
disabled={!firstName || (selectedStatus !== ProfileType.FAMILY_DEVICE && !lastName) || isLoading}
|
||||
label={isLoading ? "Adding..." : "Add group member"}
|
||||
backgroundColor="#FD1775"
|
||||
style={{marginTop: 20}}
|
||||
onPress={handleCreateSubUser}
|
||||
/>
|
||||
</Card>
|
||||
</Dialog>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: {
|
||||
marginVertical: 15,
|
||||
backgroundColor: "white",
|
||||
width: "100%",
|
||||
borderRadius: 15,
|
||||
padding: 20,
|
||||
},
|
||||
familyCard: {
|
||||
marginBottom: 10,
|
||||
borderRadius: 10,
|
||||
backgroundColor: Colors.white,
|
||||
width: "100%",
|
||||
},
|
||||
inputField: {
|
||||
borderRadius: 50,
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 16,
|
||||
backgroundColor: Colors.grey80,
|
||||
marginBottom: 16,
|
||||
borderColor: Colors.grey50,
|
||||
borderWidth: 1,
|
||||
height: 40,
|
||||
},
|
||||
picker: {
|
||||
borderRadius: 50,
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 16,
|
||||
backgroundColor: Colors.grey80,
|
||||
marginBottom: 16,
|
||||
borderColor: Colors.grey50,
|
||||
borderWidth: 1,
|
||||
marginTop: -20,
|
||||
height: 40,
|
||||
},
|
||||
label: {
|
||||
marginBottom: 5,
|
||||
fontSize: 12,
|
||||
color: Colors.grey40,
|
||||
},
|
||||
dialogCard: {
|
||||
borderRadius: 10,
|
||||
gap: 10,
|
||||
},
|
||||
card: {
|
||||
marginVertical: 15,
|
||||
backgroundColor: "white",
|
||||
width: "100%",
|
||||
borderRadius: 15,
|
||||
padding: 20,
|
||||
},
|
||||
familyCard: {
|
||||
marginBottom: 10,
|
||||
borderRadius: 10,
|
||||
backgroundColor: Colors.white,
|
||||
width: "100%",
|
||||
},
|
||||
inputField: {
|
||||
borderRadius: 50,
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 16,
|
||||
backgroundColor: Colors.grey80,
|
||||
marginBottom: 16,
|
||||
borderColor: Colors.grey50,
|
||||
borderWidth: 1,
|
||||
height: 40,
|
||||
},
|
||||
picker: {
|
||||
borderRadius: 50,
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 16,
|
||||
backgroundColor: Colors.grey80,
|
||||
marginBottom: 16,
|
||||
borderColor: Colors.grey50,
|
||||
borderWidth: 1,
|
||||
marginTop: -20,
|
||||
height: 40,
|
||||
},
|
||||
label: {
|
||||
marginBottom: 5,
|
||||
fontSize: 12,
|
||||
color: Colors.grey40,
|
||||
},
|
||||
dialogCard: {
|
||||
borderRadius: 10,
|
||||
gap: 10,
|
||||
},
|
||||
});
|
||||
|
||||
export default MyGroup;
|
||||
|
@ -1,102 +1,102 @@
|
||||
import { View, Text, TextField } from "react-native-ui-lib";
|
||||
import React, { useState } from "react";
|
||||
import { StyleSheet } from "react-native";
|
||||
import { ScrollView } from "react-native-gesture-handler";
|
||||
import { useAuthContext } from "@/contexts/AuthContext";
|
||||
import { useSettingsContext } from "@/contexts/SettingsContext";
|
||||
import { useUpdateUserData } from "@/hooks/firebase/useUpdateUserData";
|
||||
import {Text, TextField, View} from "react-native-ui-lib";
|
||||
import React, {useState} from "react";
|
||||
import {StyleSheet} from "react-native";
|
||||
import {ScrollView} from "react-native-gesture-handler";
|
||||
import {useAuthContext} from "@/contexts/AuthContext";
|
||||
import {useUpdateUserData} from "@/hooks/firebase/useUpdateUserData";
|
||||
|
||||
const MyProfile = () => {
|
||||
const { user, profileData } = useAuthContext();
|
||||
const {user, profileData} = useAuthContext();
|
||||
|
||||
const [lastName, setLastName] = useState<string>(profileData?.lastName || "");
|
||||
const [firstName, setFirstName] = useState<string>(
|
||||
profileData?.firstName || ""
|
||||
);
|
||||
const [lastName, setLastName] = useState<string>(profileData?.lastName || "");
|
||||
const [firstName, setFirstName] = useState<string>(
|
||||
profileData?.firstName || ""
|
||||
);
|
||||
|
||||
const { mutateAsync: updateUserData } = useUpdateUserData();
|
||||
return (
|
||||
<ScrollView style={{paddingBottom: 100, flex: 1}}>
|
||||
<View style={styles.card}>
|
||||
<Text text70>Your Profile</Text>
|
||||
<View row spread paddingH-15 centerV marginV-15>
|
||||
<View style={styles.pfp}></View>
|
||||
<Text text80 color="#50be0c">
|
||||
Change Photo
|
||||
</Text>
|
||||
<Text text80>Remove Photo</Text>
|
||||
</View>
|
||||
<View paddingH-15>
|
||||
<Text text80 marginT-10 marginB-7 color="#a1a1a1">
|
||||
First name
|
||||
</Text>
|
||||
<TextField
|
||||
text70
|
||||
placeholder="First name"
|
||||
style={styles.txtBox}
|
||||
value={firstName}
|
||||
onChangeText={async (value) => {
|
||||
setFirstName(value);
|
||||
await updateUserData({ newUserData: { firstName: value } });
|
||||
}}
|
||||
/>
|
||||
<Text text80 marginT-10 marginB-7 color="#a1a1a1">
|
||||
Last name
|
||||
</Text>
|
||||
<TextField
|
||||
text70
|
||||
placeholder="Last name"
|
||||
style={styles.txtBox}
|
||||
value={lastName}
|
||||
onChangeText={async (value) => {
|
||||
setLastName(value);
|
||||
await updateUserData({ newUserData: { lastName: value } });
|
||||
}}
|
||||
/>
|
||||
<Text text80 marginT-10 marginB-7 color="#a1a1a1">
|
||||
Email address
|
||||
</Text>
|
||||
<TextField
|
||||
text70
|
||||
placeholder="Email address"
|
||||
value={user?.email?.toString()}
|
||||
style={styles.txtBox}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
const {mutateAsync: updateUserData} = useUpdateUserData();
|
||||
return (
|
||||
<ScrollView style={{paddingBottom: 100, flex: 1}}>
|
||||
<View style={styles.card}>
|
||||
<Text text70>Your Profile</Text>
|
||||
<View row spread paddingH-15 centerV marginV-15>
|
||||
<View style={styles.pfp}></View>
|
||||
<Text text80 color="#50be0c">
|
||||
Change Photo
|
||||
</Text>
|
||||
<Text text80>Remove Photo</Text>
|
||||
</View>
|
||||
<View paddingH-15>
|
||||
<Text text80 marginT-10 marginB-7 color="#a1a1a1">
|
||||
First name
|
||||
</Text>
|
||||
<TextField
|
||||
text70
|
||||
placeholder="First name"
|
||||
style={styles.txtBox}
|
||||
value={firstName}
|
||||
onChangeText={async (value) => {
|
||||
setFirstName(value);
|
||||
await updateUserData({newUserData: {firstName: value}});
|
||||
}}
|
||||
/>
|
||||
<Text text80 marginT-10 marginB-7 color="#a1a1a1">
|
||||
Last name
|
||||
</Text>
|
||||
<TextField
|
||||
text70
|
||||
placeholder="Last name"
|
||||
style={styles.txtBox}
|
||||
value={lastName}
|
||||
onChangeText={async (value) => {
|
||||
setLastName(value);
|
||||
await updateUserData({newUserData: {lastName: value}});
|
||||
}}
|
||||
/>
|
||||
<Text text80 marginT-10 marginB-7 color="#a1a1a1">
|
||||
Email address
|
||||
</Text>
|
||||
<TextField
|
||||
text70
|
||||
placeholder="Email address"
|
||||
value={user?.email?.toString()}
|
||||
style={styles.txtBox}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.card}>
|
||||
<Text text70>Settings</Text>
|
||||
<Text text80 marginT-20 marginB-7 color="#a1a1a1">
|
||||
Time Zone
|
||||
</Text>
|
||||
<TextField text70 placeholder="Time Zone" style={styles.txtBox} />
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
<View style={styles.card}>
|
||||
<Text text70>Settings</Text>
|
||||
<Text text80 marginT-20 marginB-7 color="#a1a1a1">
|
||||
Time Zone
|
||||
</Text>
|
||||
<TextField text70 placeholder="Time Zone" style={styles.txtBox}/>
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: {
|
||||
marginVertical: 15,
|
||||
backgroundColor: "white",
|
||||
width: "100%",
|
||||
borderRadius: 15,
|
||||
padding: 20,
|
||||
},
|
||||
pfp: {
|
||||
aspectRatio: 1,
|
||||
width: 60,
|
||||
backgroundColor: "green",
|
||||
borderRadius: 20,
|
||||
},
|
||||
txtBox: {
|
||||
backgroundColor: "#fafafa",
|
||||
borderRadius: 50,
|
||||
borderWidth: 2,
|
||||
borderColor: "#cecece",
|
||||
padding: 15,
|
||||
height: 45,
|
||||
},
|
||||
card: {
|
||||
marginVertical: 15,
|
||||
backgroundColor: "white",
|
||||
width: "100%",
|
||||
borderRadius: 15,
|
||||
padding: 20,
|
||||
},
|
||||
pfp: {
|
||||
aspectRatio: 1,
|
||||
width: 60,
|
||||
backgroundColor: "green",
|
||||
borderRadius: 20,
|
||||
},
|
||||
txtBox: {
|
||||
backgroundColor: "#fafafa",
|
||||
borderRadius: 50,
|
||||
borderWidth: 2,
|
||||
borderColor: "#cecece",
|
||||
padding: 15,
|
||||
height: 45,
|
||||
},
|
||||
});
|
||||
|
||||
export default MyProfile;
|
||||
|
@ -3,15 +3,22 @@ import {Button, Card, Colors, Dialog, Hint, ListItem, Text, View} from 'react-na
|
||||
import QRCode from 'react-native-qrcode-svg';
|
||||
import {PanningDirectionsEnum} from "react-native-ui-lib/src/components/panningViews/panningProvider";
|
||||
|
||||
const UserMenu = ({userId}:{userId: string}) => {
|
||||
const UserMenu = ({
|
||||
userId,
|
||||
showQRCodeDialog,
|
||||
setShowQRCodeDialog
|
||||
}: {
|
||||
userId: string,
|
||||
showQRCodeDialog: boolean,
|
||||
setShowQRCodeDialog: (value: boolean) => void
|
||||
}) => {
|
||||
const [showHint, setShowHint] = useState(false);
|
||||
const [showQRCodeDialog, setShowQRCodeDialog] = useState(false);
|
||||
|
||||
const handleShowQRCode = () => {
|
||||
setShowHint(false);
|
||||
setTimeout(() => {
|
||||
setShowQRCodeDialog(true);
|
||||
}, 500)
|
||||
}, 500);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -22,9 +29,7 @@ const UserMenu = ({userId}:{userId: string}) => {
|
||||
color={Colors.white}
|
||||
customContent={
|
||||
<View height={18}>
|
||||
<ListItem
|
||||
onPress={handleShowQRCode}
|
||||
>
|
||||
<ListItem onPress={handleShowQRCode}>
|
||||
<Text>Show Login QR Code</Text>
|
||||
</ListItem>
|
||||
</View>
|
||||
@ -34,7 +39,7 @@ const UserMenu = ({userId}:{userId: string}) => {
|
||||
backdropColor="transparent"
|
||||
>
|
||||
<View>
|
||||
<Button link onPress={() => setShowHint(x => !x)}>
|
||||
<Button link onPress={() => setShowHint(!showHint)}>
|
||||
<Text>...</Text>
|
||||
</Button>
|
||||
</View>
|
||||
@ -47,7 +52,7 @@ const UserMenu = ({userId}:{userId: string}) => {
|
||||
>
|
||||
<Card padding-20 center>
|
||||
<Text marginB-10>Scan this QR Code to Login:</Text>
|
||||
<QRCode value={userId} size={150} />
|
||||
<QRCode value={userId} size={150}/>
|
||||
<Button
|
||||
marginT-20
|
||||
label="Close"
|
||||
|
@ -9,11 +9,14 @@ import * as Notifications from 'expo-notifications';
|
||||
import * as Device from 'expo-device';
|
||||
import Constants from 'expo-constants';
|
||||
import { Platform } from 'react-native';
|
||||
import {useQueryClient} from "react-query";
|
||||
|
||||
|
||||
export enum ProfileType {
|
||||
"PARENT" = "parent",
|
||||
"CHILD" = "child",
|
||||
"CAREGIVER" = "caregiver"
|
||||
"CAREGIVER" = "caregiver",
|
||||
FAMILY_DEVICE = "FAMILY_DEVICE"
|
||||
}
|
||||
|
||||
interface IAuthContext {
|
||||
@ -91,6 +94,8 @@ export const AuthContextProvider: FC<{ children: ReactNode }> = ({children}) =>
|
||||
const {replace} = useRouter();
|
||||
const ready = !initializing;
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const onAuthStateChangedHandler = async (authUser: FirebaseAuthTypes.User | null) => {
|
||||
setUser(authUser);
|
||||
|
||||
@ -153,6 +158,17 @@ export const AuthContextProvider: FC<{ children: ReactNode }> = ({children}) =>
|
||||
}
|
||||
}, [user, ready]);
|
||||
|
||||
useEffect(() => {
|
||||
const sub = Notifications.addNotificationReceivedListener(notification => {
|
||||
const eventId = notification?.request?.content?.data?.eventId;
|
||||
|
||||
if (eventId) {
|
||||
queryClient.invalidateQueries(['events']);
|
||||
}
|
||||
});
|
||||
return () => sub.remove()
|
||||
}, []);
|
||||
|
||||
if (!ready) {
|
||||
return null;
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ exports.createSubUser = onRequest(async (request, response) => {
|
||||
|
||||
const {userType, firstName, lastName, email, password, familyId} = request.body.data;
|
||||
|
||||
if (!email || !password || !firstName || !lastName || !userType || !familyId) {
|
||||
if (!email || !password || !firstName || !userType || !familyId) {
|
||||
logger.warn("Missing required fields in request body", {requestBody: request.body.data});
|
||||
response.status(400).json({error: "Missing required fields"});
|
||||
return;
|
||||
|
@ -2,6 +2,7 @@ import {useMutation, useQueryClient} from "react-query";
|
||||
import {UserProfile} from "@/hooks/firebase/types/profileTypes";
|
||||
import functions from '@react-native-firebase/functions';
|
||||
import {ProfileType, useAuthContext} from "@/contexts/AuthContext";
|
||||
import {HttpsCallableResult} from "@firebase/functions";
|
||||
|
||||
export const useCreateSubUser = () => {
|
||||
const queryClient = useQueryClient()
|
||||
@ -9,13 +10,13 @@ export const useCreateSubUser = () => {
|
||||
|
||||
return useMutation({
|
||||
mutationKey: ["createSubUser"],
|
||||
mutationFn: async ({email, ...userProfile}: { email: string } & UserProfile) => {
|
||||
mutationFn: async ({email, ...userProfile}: { email?: string } & UserProfile) => {
|
||||
if (profileType === ProfileType.PARENT) {
|
||||
return await functions().httpsCallable("createSubUser")({
|
||||
...userProfile,
|
||||
email,
|
||||
familyId: profileData?.familyId
|
||||
})
|
||||
}) as HttpsCallableResult<{ userId: string }>
|
||||
} else {
|
||||
throw Error("Can't create sub-users as a non-parent.")
|
||||
}
|
||||
|
Reference in New Issue
Block a user