diff --git a/app/(unauth)/get_started.tsx b/app/(unauth)/get_started.tsx index 44cc11f..f5788f2 100644 --- a/app/(unauth)/get_started.tsx +++ b/app/(unauth)/get_started.tsx @@ -1,33 +1,36 @@ import {SafeAreaView} from "react-native-safe-area-context"; import {Button, Colors, Dialog, LoaderScreen, Text, View} from "react-native-ui-lib"; -import React, {useState} from "react"; +import React, {useCallback, useState} from "react"; import {useRouter} from "expo-router"; import QRIcon from "@/assets/svgs/QRIcon"; -import Toast from "react-native-toast-message"; import {Camera, CameraView} from "expo-camera"; import {useLoginWithQrCode} from "@/hooks/firebase/useLoginWithQrCode"; +import {useAuthContext} from "@/contexts/AuthContext"; +import debounce from "debounce"; export default function Screen() { const router = useRouter() + const {setRedirectOverride} = useAuthContext() const [hasPermission, setHasPermission] = useState(null); const [showCameraDialog, setShowCameraDialog] = useState(false); const {mutateAsync: signInWithQrCode, isLoading} = useLoginWithQrCode(); + const debouncedRouterReplace = useCallback( + debounce(() => { + router.push("/(unauth)/cal_sync"); + }, 300), + [] + ); + const handleQrCodeScanned = async ({data}: { data: string }) => { setShowCameraDialog(false); + setRedirectOverride(true); try { await signInWithQrCode({userId: data}); - Toast.show({ - type: "success", - text1: "Login successful with QR code!", - }); + debouncedRouterReplace() } catch (err) { - Toast.show({ - type: "error", - text1: "Error logging in with QR code", - text2: `${err}`, - }); + console.log(err) } }; diff --git a/components/pages/calendar/EventCalendar.tsx b/components/pages/calendar/EventCalendar.tsx index 54a6832..da81a8f 100644 --- a/components/pages/calendar/EventCalendar.tsx +++ b/components/pages/calendar/EventCalendar.tsx @@ -225,7 +225,7 @@ export const EventCalendar: React.FC = React.memo( mode={mode} enableEnrichedEvents={true} sortedMonthView - enrichedEventsByDate={enrichedEvents} + // enrichedEventsByDate={enrichedEvents} events={filteredEvents} // eventCellStyle={memoizedEventCellStyle} onPressEvent={handlePressEvent} diff --git a/components/pages/settings/UserSettings.tsx b/components/pages/settings/UserSettings.tsx index fa45fde..bb908dd 100644 --- a/components/pages/settings/UserSettings.tsx +++ b/components/pages/settings/UserSettings.tsx @@ -1,138 +1,131 @@ -import { - FloatingButton, - Text, - TouchableOpacity, - View, -} from "react-native-ui-lib"; -import React, { useState } from "react"; -import { Ionicons } from "@expo/vector-icons"; -import { ScrollView, StyleSheet } from "react-native"; +import {FloatingButton, Text, TouchableOpacity, View,} from "react-native-ui-lib"; +import React, {useState} from "react"; +import {Ionicons} from "@expo/vector-icons"; +import {ScrollView, StyleSheet} from "react-native"; import MyProfile from "./user_settings_views/MyProfile"; import MyGroup from "./user_settings_views/MyGroup"; -import { useAtom } from "jotai"; -import { settingsPageIndex, userSettingsView } from "../calendar/atoms"; -import { AuthContextProvider } from "@/contexts/AuthContext"; +import {useAtom, useSetAtom} from "jotai"; +import {settingsPageIndex, userSettingsView} from "../calendar/atoms"; import PlusIcon from "@/assets/svgs/PlusIcon"; const UserSettings = () => { - const [pageIndex, setPageIndex] = useAtom(settingsPageIndex); - const [userView, setUserView] = useAtom(userSettingsView); - const [onNewUserClick, setOnNewUserClick] = useState<(boolean)>(false); - return ( - - - - { - setPageIndex(0); - setUserView(true); - }} - > - - - - Return to main settings - - - - - - User Management - - - setUserView(true)} - centerV - centerH - style={userView == true ? styles.btnSelected : styles.btnNot} - > - - - My Profile - + const setPageIndex = useSetAtom(settingsPageIndex); + const [userView, setUserView] = useAtom(userSettingsView); + const [onNewUserClick, setOnNewUserClick] = useState<(boolean)>(false); + + return ( + + + { + setPageIndex(0); + setUserView(true); + }} + > + + + + Return to main settings + + + + + + User Management + + + setUserView(true)} + centerV + centerH + style={userView == true ? styles.btnSelected : styles.btnNot} + > + + + My Profile + + + + setUserView(false)} + centerV + centerH + style={userView == false ? styles.btnSelected : styles.btnNot} + > + + + My Group + + + + + {userView && } + {!userView && } - - setUserView(false)} - centerV - centerH - style={userView == false ? styles.btnSelected : styles.btnNot} - > - - - My Group - - - - - {userView && } - {!userView && } - - - {!userView && ( - , - onPress: () => setOnNewUserClick(true), - style: styles.bottomButton, - labelStyle: { fontFamily: "Manrope_600SemiBold", fontSize: 15 }, - }} - /> - )} - - - ); + + {!userView && ( + , + onPress: () => setOnNewUserClick(true), + style: styles.bottomButton, + labelStyle: {fontFamily: "Manrope_600SemiBold", fontSize: 15}, + }} + /> + )} + + ); }; const styles = StyleSheet.create({ - bottomButton: { - position: "absolute", - bottom: 15, - marginHorizontal: 28, - width: 337, - backgroundColor: "#e8156c", - height: 53.26, - }, - buttonSwitch: { - borderRadius: 50, - width: "100%", - backgroundColor: "#ebebeb", - height: 45, - }, - btnSelected: { - backgroundColor: "#05a8b6", - height: "100%", - width: "50%", - borderRadius: 50, - }, - btnTxt: { - fontFamily: "Manrope_500Medium", - fontSize: 15, - }, - btnNot: { - height: "100%", - width: "50%", - borderRadius: 50, - }, - title: { fontFamily: "Manrope_600SemiBold", fontSize: 18 }, + bottomButton: { + position: "absolute", + bottom: 15, + marginHorizontal: 28, + width: 337, + backgroundColor: "#e8156c", + height: 53.26, + }, + buttonSwitch: { + borderRadius: 50, + width: "100%", + backgroundColor: "#ebebeb", + height: 45, + }, + btnSelected: { + backgroundColor: "#05a8b6", + height: "100%", + width: "50%", + borderRadius: 50, + }, + btnTxt: { + fontFamily: "Manrope_500Medium", + fontSize: 15, + }, + btnNot: { + height: "100%", + width: "50%", + borderRadius: 50, + }, + title: {fontFamily: "Manrope_600SemiBold", fontSize: 18}, }); export default UserSettings; diff --git a/components/pages/settings/user_settings_views/MyGroup.tsx b/components/pages/settings/user_settings_views/MyGroup.tsx index 1358a28..c1d6867 100644 --- a/components/pages/settings/user_settings_views/MyGroup.tsx +++ b/components/pages/settings/user_settings_views/MyGroup.tsx @@ -1,590 +1,618 @@ import { - Avatar, - Button, - Card, - Colors, - Dialog, - FloatingButton, - KeyboardAwareScrollView, - PanningProvider, - Picker, - Text, - TextField, - TextFieldRef, - TouchableOpacity, - View, + Avatar, + Button, + Card, + Colors, + Dialog, Image, + KeyboardAwareScrollView, + PanningProvider, + Picker, + Text, + TextField, + TextFieldRef, + TouchableOpacity, + View, } from "react-native-ui-lib"; -import React, { useEffect, useRef, useState } from "react"; -import { ImageBackground, Platform, StyleSheet } from "react-native"; -import { PickerSingleValue } from "react-native-ui-lib/src/components/picker/types"; -import { useCreateSubUser } from "@/hooks/firebase/useCreateSubUser"; -import { ProfileType, useAuthContext } from "@/contexts/AuthContext"; -import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers"; +import React, {useEffect, useRef, useState} from "react"; +import {ImageBackground, Platform, StyleSheet} from "react-native"; +import {PickerSingleValue} from "react-native-ui-lib/src/components/picker/types"; +import {useCreateSubUser} from "@/hooks/firebase/useCreateSubUser"; +import {ProfileType, useAuthContext} from "@/contexts/AuthContext"; +import {useGetFamilyMembers} from "@/hooks/firebase/useGetFamilyMembers"; import UserMenu from "@/components/pages/settings/user_settings_views/UserMenu"; -import { uuidv4 } from "@firebase/util"; +import {uuidv4} from "@firebase/util"; import QRIcon from "@/assets/svgs/QRIcon"; import EmailIcon from "@/assets/svgs/EmailIcon"; import CircledXIcon from "@/assets/svgs/CircledXIcon"; import ProfileIcon from "@/assets/svgs/ProfileIcon"; import NavToDosIcon from "@/assets/svgs/NavToDosIcon"; import Ionicons from "@expo/vector-icons/Ionicons"; -import KeyboardManager, { - PreviousNextView, -} from "react-native-keyboard-manager"; -import { ScrollView } from "react-native-gesture-handler"; +import KeyboardManager, {PreviousNextView,} from "react-native-keyboard-manager"; +import {ScrollView} from "react-native-gesture-handler"; +import {useUploadProfilePicture} from "@/hooks/useUploadProfilePicture"; +import {ImagePickerAsset} from "expo-image-picker"; type MyGroupProps = { - onNewUserClick: boolean; - setOnNewUserClick: React.Dispatch>; + onNewUserClick: boolean; + setOnNewUserClick: React.Dispatch>; }; -const MyGroup: React.FC = ({ onNewUserClick, setOnNewUserClick }) => { - const [showAddUserDialog, setShowAddUserDialog] = useState(false); - const [selectedStatus, setSelectedStatus] = useState< - string | PickerSingleValue - >(ProfileType.CHILD); - const [firstName, setFirstName] = useState(""); - const [lastName, setLastName] = useState(""); - const [email, setEmail] = useState(""); +const MyGroup: React.FC = ({onNewUserClick, setOnNewUserClick}) => { + const [showAddUserDialog, setShowAddUserDialog] = useState(false); + const [selectedStatus, setSelectedStatus] = useState< + string | PickerSingleValue + >(ProfileType.CHILD); + const [firstName, setFirstName] = useState(""); + const [lastName, setLastName] = useState(""); + const [email, setEmail] = useState(""); - const lNameRef = useRef(null); - const emailRef = useRef(null); + const [newUserId, setNewUserId] = useState("") - const [showQRCodeDialog, setShowQRCodeDialog] = useState( - false - ); + const lNameRef = useRef(null); + const emailRef = useRef(null); - const { mutateAsync: createSubUser, isLoading, isError } = useCreateSubUser(); - const { data: familyMembers } = useGetFamilyMembers(true); - const { user } = useAuthContext(); + const [showQRCodeDialog, setShowQRCodeDialog] = useState( + false + ); - 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) ?? - []; + const {mutateAsync: createSubUser, isLoading, isError} = useCreateSubUser(); + const {data: familyMembers} = useGetFamilyMembers(true); + const {user} = useAuthContext(); + const {pickImage, changeProfilePicture, handleClearImage, pfpUri, profileImageAsset} = useUploadProfilePicture(newUserId) - const handleCreateSubUser = async () => { - if ( - !firstName || - (selectedStatus !== ProfileType.FAMILY_DEVICE && !lastName) - ) { - console.error("First name and last name 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 (selectedStatus !== ProfileType.FAMILY_DEVICE && !email) { - // console.error("Email is required for non-family device users"); - // return; - // } + const handleCreateSubUser = async () => { + if ( + !firstName || + (selectedStatus !== ProfileType.FAMILY_DEVICE && !lastName) + ) { + console.error("First name and last name are required"); + return; + } - if (email && !email.includes("@")) { - console.error("Invalid email address"); - return; - } + // if (selectedStatus !== ProfileType.FAMILY_DEVICE && !email) { + // console.error("Email is required for non-family device users"); + // return; + // } - 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); + if (email && !email.includes("@")) { + console.error("Invalid email address"); + return; + } - if (!isError) { - setOnNewUserClick(false); + 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); - if (res?.data?.userId) { - setTimeout(() => { - setShowQRCodeDialog(res.data.userId); - }, 500); - } - } - }; + if (!isError) { + setOnNewUserClick(false); - useEffect(() => { - if (Platform.OS === "ios") KeyboardManager.setEnableAutoToolbar(true); - }, []); + if (res?.data?.userId) { + if (profileImageAsset) { + await changeProfilePicture(profileImageAsset) + setShowQRCodeDialog(res.data.userId); + } else { + setTimeout(() => { + setShowQRCodeDialog(res.data.userId); + }, 500); + } - useEffect(() => { - setFirstName(""); - setLastName(""); - setEmail(""); - }, []); + handleClearImage() + } + } + }; - // @ts-ignore - return ( - - - - {!parents.length && !children.length && !caregivers.length && ( - - {isLoading ? "Loading...." : "No user devices added"} - - )} + useEffect(() => { + if (Platform.OS === "ios") KeyboardManager.setEnableAutoToolbar(true); + }, []); - {(!!parents.length || !!children.length) && ( - - - Family - - {[...parents, ...children]?.map((member, index) => ( + useEffect(() => { + setFirstName(""); + setLastName(""); + setEmail(""); + }, []); + + // @ts-ignore + return ( + + + + {!parents.length && !children.length && !caregivers.length && ( + + {isLoading ? "Loading...." : "No user devices added"} + + )} + + {(!!parents.length || !!children.length) && ( + + + Family + + {[...parents, ...children]?.map((member, index) => ( + + {member.pfp ? ( + + ) : ( + + )} + + + {member.firstName} {member.lastName} + + + + + + {member.userType === ProfileType.PARENT + ? `Admin${member.uid === user?.uid ? " (You)" : ""}` + : "Child"} + + setShowQRCodeDialog(val)} + showQRCodeDialog={showQRCodeDialog === member?.uid} + userId={member?.uid!} + /> + + + ))} + + )} + + {!!caregivers.length && ( + + + Caregivers + + {caregivers?.map((member) => ( + + + + + {member.firstName} {member.lastName} + + + Caregiver + + + + + + setShowQRCodeDialog(val)} + showQRCodeDialog={showQRCodeDialog === member?.uid} + userId={member?.uid!} + /> + + ))} + + )} + + {!!familyDevices.length && ( + <> + + Family Devices + + {familyDevices?.map((member, index) => ( + + + + {member.firstName} + + Family Device + + + + + + setShowQRCodeDialog(val)} + showQRCodeDialog={showQRCodeDialog === member?.uid} + userId={member?.uid!} + /> + + ))} + + )} + + + + setShowAddUserDialog(false)} + panDirection={PanningProvider.Directions.DOWN} + > - {member.pfp ? ( - - ) : ( - - )} - - - {member.firstName} {member.lastName} + + Add a new user device - - - - - {member.userType === ProfileType.PARENT - ? `Admin${member.uid === user?.uid ? " (You)" : ""}` - : "Child"} - - setShowQRCodeDialog(val)} - showQRCodeDialog={showQRCodeDialog === member?.uid} - userId={member?.uid!} - /> - - - ))} - - )} - {!!caregivers.length && ( - - - Caregivers - - {caregivers?.map((member) => ( - - - - - {member.firstName} {member.lastName} - - - Caregiver - - - - - - setShowQRCodeDialog(val)} - showQRCodeDialog={showQRCodeDialog === member?.uid} - userId={member?.uid!} - /> - - ))} - - )} - - {!!familyDevices.length && ( - <> - - Family Devices - - {familyDevices?.map((member, index) => ( - - - - {member.firstName} - - Family Device - - - - - - setShowQRCodeDialog(val)} - showQRCodeDialog={showQRCodeDialog === member?.uid} - userId={member?.uid!} - /> - - ))} - - )} - - - - setShowAddUserDialog(false)} - panDirection={PanningProvider.Directions.DOWN} - > - - - Add a new user device - - - - - - setShowAddUserDialog(false)} - center - marginT-30 - > - Return to user settings - - - - - setOnNewUserClick(false)} - > - - - - - - New User Information - - { - setOnNewUserClick(false); - }} - > - - - - - - - - } - backgroundColor={Colors.grey60} - style={{ borderRadius: 25 }} - center - /> - {}}> - - Upload User Profile Photo - - - - - Member Status - - setSelectedStatus(item)} - showSearch - floatingPlaceholder - style={styles.inViewPicker} - trailingAccessory={ - + + + Show a QR Code + + + - - {selectedStatus === ProfileType.FAMILY_DEVICE - ? "Device Name" - : "First Name"} - - { - lNameRef.current?.focus(); - }} - blurOnSubmit={false} - returnKeyType="next" - /> + setShowAddUserDialog(false)} + center + marginT-30 + > + Return to user settings + + + - {selectedStatus !== ProfileType.FAMILY_DEVICE && ( - <> - Last Name - { - emailRef.current?.focus(); - }} - blurOnSubmit={false} - returnKeyType="next" - /> - - )} + setOnNewUserClick(false)} + > + + + + + + New User Information + + { + setOnNewUserClick(false); + }} + > + + + + - {selectedStatus !== ProfileType.FAMILY_DEVICE && ( - <> - Email Address (Optional) - - - )} + + {pfpUri ? ( + + ) : ( + + } + backgroundColor={Colors.grey60} + style={{borderRadius: 25}} + center + /> + )} - - - ); + {pfpUri ? ( + + + Clear user photo + + + ) : ( + + + Upload User Profile Photo + + + )} + + + + Member Status + + setSelectedStatus(item)} + showSearch + floatingPlaceholder + style={styles.inViewPicker} + trailingAccessory={ + + + + } + > + + + + + + + + + {selectedStatus === ProfileType.FAMILY_DEVICE + ? "Device Name" + : "First Name"} + + { + lNameRef.current?.focus(); + }} + blurOnSubmit={false} + returnKeyType="next" + /> + + {selectedStatus !== ProfileType.FAMILY_DEVICE && ( + <> + Last Name + { + emailRef.current?.focus(); + }} + blurOnSubmit={false} + returnKeyType="next" + /> + + )} + + {selectedStatus !== ProfileType.FAMILY_DEVICE && ( + <> + Email Address (Optional) + + + )} + +