mirror of
https://github.com/urosran/cally.git
synced 2025-11-26 00:24:53 +00:00
Added creating family devices, refetch calendar on notification received
This commit is contained in:
@ -19,6 +19,7 @@ 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);
|
||||
@ -30,6 +31,8 @@ const MyGroup = () => {
|
||||
const [lastName, setLastName] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
|
||||
const [showQRCodeDialog, setShowQRCodeDialog] = useState("");
|
||||
|
||||
const {mutateAsync: createSubUser, isLoading, isError} = useCreateSubUser();
|
||||
const {data: familyMembers} = useGetFamilyMembers(true);
|
||||
|
||||
@ -39,32 +42,46 @@ const MyGroup = () => {
|
||||
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) ?? [];
|
||||
|
||||
const handleCreateSubUser = async () => {
|
||||
if (!firstName || !lastName || !email) {
|
||||
console.error("All fields are required");
|
||||
if (!firstName || (selectedStatus !== ProfileType.FAMILY_DEVICE && !lastName)) {
|
||||
console.error("First name and last name are required");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!email.includes("@")) {
|
||||
if (selectedStatus !== ProfileType.FAMILY_DEVICE && !email) {
|
||||
console.error("Email is required for non-family device users");
|
||||
return;
|
||||
}
|
||||
|
||||
if (email && !email.includes("@")) {
|
||||
console.error("Invalid email address");
|
||||
return;
|
||||
}
|
||||
|
||||
await createSubUser({
|
||||
const res = await createSubUser({
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
password: email,
|
||||
lastName: selectedStatus === ProfileType.FAMILY_DEVICE ? "" : lastName,
|
||||
email: email || `placeholder_${uuidv4().split("-")[0]}@family.device`,
|
||||
password: uuidv4(),
|
||||
userType: selectedStatus as ProfileType,
|
||||
});
|
||||
console.log(res)
|
||||
|
||||
if (!isError) {
|
||||
setShowNewUserInfoDialog(false);
|
||||
}
|
||||
};
|
||||
|
||||
console.log(familyMembers);
|
||||
if(res?.data?.userId) {
|
||||
setTimeout(() => {
|
||||
setShowQRCodeDialog(res.data.userId)
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
@ -81,11 +98,11 @@ const MyGroup = () => {
|
||||
<Text text70 marginV-10>
|
||||
Family
|
||||
</Text>
|
||||
{[...parents, ...children]?.map((member) => (
|
||||
{[...parents, ...children]?.map((member, index) => (
|
||||
<Card
|
||||
enableShadow={false}
|
||||
elevation={0}
|
||||
key={`${member.firstName}_${member.lastName}`}
|
||||
key={`${member.firstName}_${member.lastName}_${index}`}
|
||||
style={styles.familyCard}
|
||||
row
|
||||
centerV
|
||||
@ -109,7 +126,7 @@ const MyGroup = () => {
|
||||
|
||||
<View flex-1/>
|
||||
|
||||
<UserMenu userId={member?.uid!} />
|
||||
<UserMenu setShowQRCodeDialog={(val) => setShowQRCodeDialog("")} showQRCodeDialog={showQRCodeDialog === member?.uid} userId={member?.uid!}/>
|
||||
</Card>
|
||||
))}
|
||||
</>
|
||||
@ -143,6 +160,43 @@ const MyGroup = () => {
|
||||
Caregiver
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<UserMenu setShowQRCodeDialog={(val) => setShowQRCodeDialog("")} showQRCodeDialog={showQRCodeDialog === member?.uid} userId={member?.uid!}/>
|
||||
</Card>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
|
||||
{!!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>
|
||||
))}
|
||||
</>
|
||||
@ -209,7 +263,8 @@ const MyGroup = () => {
|
||||
size={60}
|
||||
backgroundColor={Colors.grey60}
|
||||
/>
|
||||
<TouchableOpacity onPress={() => {}}>
|
||||
<TouchableOpacity onPress={() => {
|
||||
}}>
|
||||
<Text style={{color: Colors.green10}}>
|
||||
Upload User Profile Photo
|
||||
</Text>
|
||||
@ -229,17 +284,22 @@ const MyGroup = () => {
|
||||
<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}>First Name</Text>
|
||||
<Text style={styles.label}>
|
||||
{selectedStatus === ProfileType.FAMILY_DEVICE ? "Device Name" : "First Name"}
|
||||
</Text>
|
||||
<TextField
|
||||
editable={!isLoading}
|
||||
placeholder="First name"
|
||||
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}
|
||||
@ -248,8 +308,12 @@ const MyGroup = () => {
|
||||
onChangeText={setLastName}
|
||||
style={styles.inputField}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Text style={styles.label}>Email Address</Text>
|
||||
{selectedStatus !== ProfileType.FAMILY_DEVICE && (
|
||||
<>
|
||||
<Text style={styles.label}>Email Address (Optional)</Text>
|
||||
<TextField
|
||||
editable={!isLoading}
|
||||
placeholder="Email address"
|
||||
@ -259,9 +323,11 @@ const MyGroup = () => {
|
||||
autoCapitalize="none"
|
||||
style={styles.inputField}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Button
|
||||
disabled={!firstName || !lastName || !email || isLoading}
|
||||
disabled={!firstName || (selectedStatus !== ProfileType.FAMILY_DEVICE && !lastName) || isLoading}
|
||||
label={isLoading ? "Adding..." : "Add group member"}
|
||||
backgroundColor="#FD1775"
|
||||
style={{marginTop: 20}}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { View, Text, TextField } from "react-native-ui-lib";
|
||||
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 { useSettingsContext } from "@/contexts/SettingsContext";
|
||||
import {useUpdateUserData} from "@/hooks/firebase/useUpdateUserData";
|
||||
|
||||
const MyProfile = () => {
|
||||
const {user, profileData} = useAuthContext();
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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