mirror of
https://github.com/urosran/cally.git
synced 2025-07-16 01:56:16 +00:00
Profile picture upload added, QR code scan fix
This commit is contained in:
@ -1,33 +1,36 @@
|
|||||||
import {SafeAreaView} from "react-native-safe-area-context";
|
import {SafeAreaView} from "react-native-safe-area-context";
|
||||||
import {Button, Colors, Dialog, LoaderScreen, Text, View} from "react-native-ui-lib";
|
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 {useRouter} from "expo-router";
|
||||||
import QRIcon from "@/assets/svgs/QRIcon";
|
import QRIcon from "@/assets/svgs/QRIcon";
|
||||||
import Toast from "react-native-toast-message";
|
|
||||||
import {Camera, CameraView} from "expo-camera";
|
import {Camera, CameraView} from "expo-camera";
|
||||||
import {useLoginWithQrCode} from "@/hooks/firebase/useLoginWithQrCode";
|
import {useLoginWithQrCode} from "@/hooks/firebase/useLoginWithQrCode";
|
||||||
|
import {useAuthContext} from "@/contexts/AuthContext";
|
||||||
|
import debounce from "debounce";
|
||||||
|
|
||||||
export default function Screen() {
|
export default function Screen() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const {setRedirectOverride} = useAuthContext()
|
||||||
const [hasPermission, setHasPermission] = useState<boolean | null>(null);
|
const [hasPermission, setHasPermission] = useState<boolean | null>(null);
|
||||||
const [showCameraDialog, setShowCameraDialog] = useState<boolean>(false);
|
const [showCameraDialog, setShowCameraDialog] = useState<boolean>(false);
|
||||||
|
|
||||||
const {mutateAsync: signInWithQrCode, isLoading} = useLoginWithQrCode();
|
const {mutateAsync: signInWithQrCode, isLoading} = useLoginWithQrCode();
|
||||||
|
|
||||||
|
const debouncedRouterReplace = useCallback(
|
||||||
|
debounce(() => {
|
||||||
|
router.push("/(unauth)/cal_sync");
|
||||||
|
}, 300),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
const handleQrCodeScanned = async ({data}: { data: string }) => {
|
const handleQrCodeScanned = async ({data}: { data: string }) => {
|
||||||
setShowCameraDialog(false);
|
setShowCameraDialog(false);
|
||||||
|
setRedirectOverride(true);
|
||||||
try {
|
try {
|
||||||
await signInWithQrCode({userId: data});
|
await signInWithQrCode({userId: data});
|
||||||
Toast.show({
|
debouncedRouterReplace()
|
||||||
type: "success",
|
|
||||||
text1: "Login successful with QR code!",
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Toast.show({
|
console.log(err)
|
||||||
type: "error",
|
|
||||||
text1: "Error logging in with QR code",
|
|
||||||
text2: `${err}`,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -225,7 +225,7 @@ export const EventCalendar: React.FC<EventCalendarProps> = React.memo(
|
|||||||
mode={mode}
|
mode={mode}
|
||||||
enableEnrichedEvents={true}
|
enableEnrichedEvents={true}
|
||||||
sortedMonthView
|
sortedMonthView
|
||||||
enrichedEventsByDate={enrichedEvents}
|
// enrichedEventsByDate={enrichedEvents}
|
||||||
events={filteredEvents}
|
events={filteredEvents}
|
||||||
// eventCellStyle={memoizedEventCellStyle}
|
// eventCellStyle={memoizedEventCellStyle}
|
||||||
onPressEvent={handlePressEvent}
|
onPressEvent={handlePressEvent}
|
||||||
|
@ -1,27 +1,21 @@
|
|||||||
import {
|
import {FloatingButton, Text, TouchableOpacity, View,} from "react-native-ui-lib";
|
||||||
FloatingButton,
|
import React, {useState} from "react";
|
||||||
Text,
|
import {Ionicons} from "@expo/vector-icons";
|
||||||
TouchableOpacity,
|
import {ScrollView, StyleSheet} from "react-native";
|
||||||
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 MyProfile from "./user_settings_views/MyProfile";
|
||||||
import MyGroup from "./user_settings_views/MyGroup";
|
import MyGroup from "./user_settings_views/MyGroup";
|
||||||
import { useAtom } from "jotai";
|
import {useAtom, useSetAtom} from "jotai";
|
||||||
import { settingsPageIndex, userSettingsView } from "../calendar/atoms";
|
import {settingsPageIndex, userSettingsView} from "../calendar/atoms";
|
||||||
import { AuthContextProvider } from "@/contexts/AuthContext";
|
|
||||||
import PlusIcon from "@/assets/svgs/PlusIcon";
|
import PlusIcon from "@/assets/svgs/PlusIcon";
|
||||||
|
|
||||||
const UserSettings = () => {
|
const UserSettings = () => {
|
||||||
const [pageIndex, setPageIndex] = useAtom(settingsPageIndex);
|
const setPageIndex = useSetAtom(settingsPageIndex);
|
||||||
const [userView, setUserView] = useAtom(userSettingsView);
|
const [userView, setUserView] = useAtom(userSettingsView);
|
||||||
const [onNewUserClick, setOnNewUserClick] = useState<(boolean)>(false);
|
const [onNewUserClick, setOnNewUserClick] = useState<(boolean)>(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthContextProvider>
|
|
||||||
<View flexG>
|
<View flexG>
|
||||||
<ScrollView style={{ paddingBottom: 20, minHeight: "100%" }}>
|
<ScrollView style={{paddingBottom: 20, minHeight: "100%"}}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
setPageIndex(0);
|
setPageIndex(0);
|
||||||
@ -33,17 +27,17 @@ const UserSettings = () => {
|
|||||||
name="chevron-back"
|
name="chevron-back"
|
||||||
size={14}
|
size={14}
|
||||||
color="#979797"
|
color="#979797"
|
||||||
style={{ paddingBottom: 3 }}
|
style={{paddingBottom: 3}}
|
||||||
/>
|
/>
|
||||||
<Text
|
<Text
|
||||||
style={{ fontFamily: "Poppins_400Regular", fontSize: 14.71 }}
|
style={{fontFamily: "Poppins_400Regular", fontSize: 14.71}}
|
||||||
color="#979797"
|
color="#979797"
|
||||||
>
|
>
|
||||||
Return to main settings
|
Return to main settings
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<View marginH-26 flexG style={{ minHeight: "90%" }}>
|
<View marginH-26 flexG style={{minHeight: "90%"}}>
|
||||||
<Text text60R marginB-25>
|
<Text text60R marginB-25>
|
||||||
User Management
|
User Management
|
||||||
</Text>
|
</Text>
|
||||||
@ -79,7 +73,7 @@ const UserSettings = () => {
|
|||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
{userView && <MyProfile />}
|
{userView && <MyProfile/>}
|
||||||
{!userView && <MyGroup onNewUserClick={onNewUserClick} setOnNewUserClick={setOnNewUserClick}/>}
|
{!userView && <MyGroup onNewUserClick={onNewUserClick} setOnNewUserClick={setOnNewUserClick}/>}
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
@ -90,15 +84,14 @@ const UserSettings = () => {
|
|||||||
visible
|
visible
|
||||||
button={{
|
button={{
|
||||||
label: " Add a user device",
|
label: " Add a user device",
|
||||||
iconSource: () => <PlusIcon height={13} width={14} />,
|
iconSource: () => <PlusIcon height={13} width={14}/>,
|
||||||
onPress: () => setOnNewUserClick(true),
|
onPress: () => setOnNewUserClick(true),
|
||||||
style: styles.bottomButton,
|
style: styles.bottomButton,
|
||||||
labelStyle: { fontFamily: "Manrope_600SemiBold", fontSize: 15 },
|
labelStyle: {fontFamily: "Manrope_600SemiBold", fontSize: 15},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</AuthContextProvider>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -132,7 +125,7 @@ const styles = StyleSheet.create({
|
|||||||
width: "50%",
|
width: "50%",
|
||||||
borderRadius: 50,
|
borderRadius: 50,
|
||||||
},
|
},
|
||||||
title: { fontFamily: "Manrope_600SemiBold", fontSize: 18 },
|
title: {fontFamily: "Manrope_600SemiBold", fontSize: 18},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default UserSettings;
|
export default UserSettings;
|
||||||
|
@ -3,8 +3,7 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
Colors,
|
Colors,
|
||||||
Dialog,
|
Dialog, Image,
|
||||||
FloatingButton,
|
|
||||||
KeyboardAwareScrollView,
|
KeyboardAwareScrollView,
|
||||||
PanningProvider,
|
PanningProvider,
|
||||||
Picker,
|
Picker,
|
||||||
@ -14,31 +13,31 @@ import {
|
|||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View,
|
View,
|
||||||
} from "react-native-ui-lib";
|
} from "react-native-ui-lib";
|
||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, {useEffect, useRef, useState} from "react";
|
||||||
import { ImageBackground, Platform, StyleSheet } from "react-native";
|
import {ImageBackground, Platform, 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, useAuthContext } from "@/contexts/AuthContext";
|
import {ProfileType, useAuthContext} 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";
|
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 QRIcon from "@/assets/svgs/QRIcon";
|
||||||
import EmailIcon from "@/assets/svgs/EmailIcon";
|
import EmailIcon from "@/assets/svgs/EmailIcon";
|
||||||
import CircledXIcon from "@/assets/svgs/CircledXIcon";
|
import CircledXIcon from "@/assets/svgs/CircledXIcon";
|
||||||
import ProfileIcon from "@/assets/svgs/ProfileIcon";
|
import ProfileIcon from "@/assets/svgs/ProfileIcon";
|
||||||
import NavToDosIcon from "@/assets/svgs/NavToDosIcon";
|
import NavToDosIcon from "@/assets/svgs/NavToDosIcon";
|
||||||
import Ionicons from "@expo/vector-icons/Ionicons";
|
import Ionicons from "@expo/vector-icons/Ionicons";
|
||||||
import KeyboardManager, {
|
import KeyboardManager, {PreviousNextView,} from "react-native-keyboard-manager";
|
||||||
PreviousNextView,
|
import {ScrollView} from "react-native-gesture-handler";
|
||||||
} from "react-native-keyboard-manager";
|
import {useUploadProfilePicture} from "@/hooks/useUploadProfilePicture";
|
||||||
import { ScrollView } from "react-native-gesture-handler";
|
import {ImagePickerAsset} from "expo-image-picker";
|
||||||
|
|
||||||
type MyGroupProps = {
|
type MyGroupProps = {
|
||||||
onNewUserClick: boolean;
|
onNewUserClick: boolean;
|
||||||
setOnNewUserClick: React.Dispatch<React.SetStateAction<boolean>>;
|
setOnNewUserClick: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MyGroup: React.FC<MyGroupProps> = ({ onNewUserClick, setOnNewUserClick }) => {
|
const MyGroup: React.FC<MyGroupProps> = ({onNewUserClick, setOnNewUserClick}) => {
|
||||||
const [showAddUserDialog, setShowAddUserDialog] = useState(false);
|
const [showAddUserDialog, setShowAddUserDialog] = useState(false);
|
||||||
const [selectedStatus, setSelectedStatus] = useState<
|
const [selectedStatus, setSelectedStatus] = useState<
|
||||||
string | PickerSingleValue
|
string | PickerSingleValue
|
||||||
@ -47,6 +46,8 @@ const MyGroup: React.FC<MyGroupProps> = ({ onNewUserClick, setOnNewUserClick })
|
|||||||
const [lastName, setLastName] = useState("");
|
const [lastName, setLastName] = useState("");
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
|
|
||||||
|
const [newUserId, setNewUserId] = useState("")
|
||||||
|
|
||||||
const lNameRef = useRef<TextFieldRef>(null);
|
const lNameRef = useRef<TextFieldRef>(null);
|
||||||
const emailRef = useRef<TextFieldRef>(null);
|
const emailRef = useRef<TextFieldRef>(null);
|
||||||
|
|
||||||
@ -54,9 +55,10 @@ const MyGroup: React.FC<MyGroupProps> = ({ onNewUserClick, setOnNewUserClick })
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
const { mutateAsync: createSubUser, isLoading, isError } = useCreateSubUser();
|
const {mutateAsync: createSubUser, isLoading, isError} = useCreateSubUser();
|
||||||
const { data: familyMembers } = useGetFamilyMembers(true);
|
const {data: familyMembers} = useGetFamilyMembers(true);
|
||||||
const { user } = useAuthContext();
|
const {user} = useAuthContext();
|
||||||
|
const {pickImage, changeProfilePicture, handleClearImage, pfpUri, profileImageAsset} = useUploadProfilePicture(newUserId)
|
||||||
|
|
||||||
const parents =
|
const parents =
|
||||||
familyMembers?.filter((x) => x.userType === ProfileType.PARENT) ?? [];
|
familyMembers?.filter((x) => x.userType === ProfileType.PARENT) ?? [];
|
||||||
@ -100,10 +102,17 @@ const MyGroup: React.FC<MyGroupProps> = ({ onNewUserClick, setOnNewUserClick })
|
|||||||
setOnNewUserClick(false);
|
setOnNewUserClick(false);
|
||||||
|
|
||||||
if (res?.data?.userId) {
|
if (res?.data?.userId) {
|
||||||
|
if (profileImageAsset) {
|
||||||
|
await changeProfilePicture(profileImageAsset)
|
||||||
|
setShowQRCodeDialog(res.data.userId);
|
||||||
|
} else {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setShowQRCodeDialog(res.data.userId);
|
setShowQRCodeDialog(res.data.userId);
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleClearImage()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -147,11 +156,11 @@ const MyGroup: React.FC<MyGroupProps> = ({ onNewUserClick, setOnNewUserClick })
|
|||||||
<ImageBackground
|
<ImageBackground
|
||||||
style={styles.pfp}
|
style={styles.pfp}
|
||||||
borderRadius={10.56}
|
borderRadius={10.56}
|
||||||
source={{ uri: member.pfp || undefined }}
|
source={{uri: member.pfp || undefined}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<View
|
<View
|
||||||
style={[styles.pfp, { backgroundColor: "#ea156d" }]}
|
style={[styles.pfp, {backgroundColor: "#ea156d"}]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<View row marginL-10 centerV>
|
<View row marginL-10 centerV>
|
||||||
@ -159,7 +168,7 @@ const MyGroup: React.FC<MyGroupProps> = ({ onNewUserClick, setOnNewUserClick })
|
|||||||
{member.firstName} {member.lastName}
|
{member.firstName} {member.lastName}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View flexG />
|
<View flexG/>
|
||||||
<View row centerV gap-10>
|
<View row centerV gap-10>
|
||||||
<Text style={styles.userType}>
|
<Text style={styles.userType}>
|
||||||
{member.userType === ProfileType.PARENT
|
{member.userType === ProfileType.PARENT
|
||||||
@ -193,7 +202,7 @@ const MyGroup: React.FC<MyGroupProps> = ({ onNewUserClick, setOnNewUserClick })
|
|||||||
padding-10
|
padding-10
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
source={{ uri: member?.pfp ?? undefined }}
|
source={{uri: member?.pfp ?? undefined}}
|
||||||
size={40}
|
size={40}
|
||||||
backgroundColor={Colors.grey60}
|
backgroundColor={Colors.grey60}
|
||||||
/>
|
/>
|
||||||
@ -206,7 +215,7 @@ const MyGroup: React.FC<MyGroupProps> = ({ onNewUserClick, setOnNewUserClick })
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View flex-1 />
|
<View flex-1/>
|
||||||
|
|
||||||
<UserMenu
|
<UserMenu
|
||||||
setShowQRCodeDialog={(val) => setShowQRCodeDialog(val)}
|
setShowQRCodeDialog={(val) => setShowQRCodeDialog(val)}
|
||||||
@ -234,7 +243,7 @@ const MyGroup: React.FC<MyGroupProps> = ({ onNewUserClick, setOnNewUserClick })
|
|||||||
padding-10
|
padding-10
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
source={{ uri: member?.pfp ?? undefined }}
|
source={{uri: member?.pfp ?? undefined}}
|
||||||
size={40}
|
size={40}
|
||||||
backgroundColor={Colors.grey60}
|
backgroundColor={Colors.grey60}
|
||||||
/>
|
/>
|
||||||
@ -245,7 +254,7 @@ const MyGroup: React.FC<MyGroupProps> = ({ onNewUserClick, setOnNewUserClick })
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View flex-1 />
|
<View flex-1/>
|
||||||
|
|
||||||
<UserMenu
|
<UserMenu
|
||||||
setShowQRCodeDialog={(val) => setShowQRCodeDialog(val)}
|
setShowQRCodeDialog={(val) => setShowQRCodeDialog(val)}
|
||||||
@ -277,7 +286,7 @@ const MyGroup: React.FC<MyGroupProps> = ({ onNewUserClick, setOnNewUserClick })
|
|||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Button backgroundColor={"#FD1775"} style={styles.dialogBtn}>
|
<Button backgroundColor={"#FD1775"} style={styles.dialogBtn}>
|
||||||
<QRIcon />
|
<QRIcon/>
|
||||||
<Text style={styles.dialogBtnLbl} marginL-7>
|
<Text style={styles.dialogBtnLbl} marginL-7>
|
||||||
Show a QR Code
|
Show a QR Code
|
||||||
</Text>
|
</Text>
|
||||||
@ -292,7 +301,7 @@ const MyGroup: React.FC<MyGroupProps> = ({ onNewUserClick, setOnNewUserClick })
|
|||||||
}, 500);
|
}, 500);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<EmailIcon />
|
<EmailIcon/>
|
||||||
<Text style={styles.dialogBtnLbl} marginL-7>
|
<Text style={styles.dialogBtnLbl} marginL-7>
|
||||||
Enter email address
|
Enter email address
|
||||||
</Text>
|
</Text>
|
||||||
@ -317,7 +326,7 @@ const MyGroup: React.FC<MyGroupProps> = ({ onNewUserClick, setOnNewUserClick })
|
|||||||
<KeyboardAwareScrollView>
|
<KeyboardAwareScrollView>
|
||||||
<Card padding-25 style={styles.dialogCard}>
|
<Card padding-25 style={styles.dialogCard}>
|
||||||
<View row spread>
|
<View row spread>
|
||||||
<Text style={{ fontFamily: "Manrope_500Medium", fontSize: 16 }}>
|
<Text style={{fontFamily: "Manrope_500Medium", fontSize: 16}}>
|
||||||
New User Information
|
New User Information
|
||||||
</Text>
|
</Text>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
@ -325,27 +334,46 @@ const MyGroup: React.FC<MyGroupProps> = ({ onNewUserClick, setOnNewUserClick })
|
|||||||
setOnNewUserClick(false);
|
setOnNewUserClick(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CircledXIcon />
|
<CircledXIcon/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.divider} spread />
|
<View style={styles.divider} spread/>
|
||||||
|
|
||||||
<View row centerV gap-20 marginV-20>
|
<View row centerV gap-20 marginV-20>
|
||||||
|
{pfpUri ? (
|
||||||
|
<Image
|
||||||
|
height={65.54}
|
||||||
|
width={65.54}
|
||||||
|
style={{borderRadius: 25}}
|
||||||
|
source={{uri: pfpUri}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<View
|
<View
|
||||||
height={65.54}
|
height={65.54}
|
||||||
width={65.54}
|
width={65.54}
|
||||||
children={
|
children={
|
||||||
<ProfileIcon color={"#d6d6d6"} width={37} height={37} />
|
<ProfileIcon color={"#d6d6d6"} width={37} height={37}/>
|
||||||
}
|
}
|
||||||
backgroundColor={Colors.grey60}
|
backgroundColor={Colors.grey60}
|
||||||
style={{ borderRadius: 25 }}
|
style={{borderRadius: 25}}
|
||||||
center
|
center
|
||||||
/>
|
/>
|
||||||
<TouchableOpacity onPress={() => {}}>
|
)}
|
||||||
|
|
||||||
|
{pfpUri ? (
|
||||||
|
<TouchableOpacity onPress={handleClearImage}>
|
||||||
|
<Text color={Colors.red40} style={styles.jakarta13} marginL-15>
|
||||||
|
Clear user photo
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
) : (
|
||||||
|
<TouchableOpacity onPress={pickImage}>
|
||||||
<Text color="#50be0c" style={styles.jakarta13} marginL-15>
|
<Text color="#50be0c" style={styles.jakarta13} marginL-15>
|
||||||
Upload User Profile Photo
|
Upload User Profile Photo
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Text style={styles.jakarta12}>Member Status</Text>
|
<Text style={styles.jakarta12}>Member Status</Text>
|
||||||
@ -369,15 +397,15 @@ const MyGroup: React.FC<MyGroupProps> = ({ onNewUserClick, setOnNewUserClick })
|
|||||||
>
|
>
|
||||||
<Ionicons
|
<Ionicons
|
||||||
name={"chevron-down"}
|
name={"chevron-down"}
|
||||||
style={{ alignSelf: "center" }}
|
style={{alignSelf: "center"}}
|
||||||
size={20}
|
size={20}
|
||||||
color={"#000000"}
|
color={"#000000"}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Picker.Item label="Child" value={ProfileType.CHILD} />
|
<Picker.Item label="Child" value={ProfileType.CHILD}/>
|
||||||
<Picker.Item label="Parent" value={ProfileType.PARENT} />
|
<Picker.Item label="Parent" value={ProfileType.PARENT}/>
|
||||||
<Picker.Item
|
<Picker.Item
|
||||||
label="Caregiver"
|
label="Caregiver"
|
||||||
value={ProfileType.CAREGIVER}
|
value={ProfileType.CAREGIVER}
|
||||||
@ -460,8 +488,8 @@ const MyGroup: React.FC<MyGroupProps> = ({ onNewUserClick, setOnNewUserClick })
|
|||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
marginLeft: 7,
|
marginLeft: 7,
|
||||||
}}
|
}}
|
||||||
style={{ marginTop: 20, backgroundColor: "#fd1775" }}
|
style={{marginTop: 20, backgroundColor: "#fd1775"}}
|
||||||
iconSource={() => <NavToDosIcon width={22} color={"white"} />}
|
iconSource={() => <NavToDosIcon width={22} color={"white"}/>}
|
||||||
onPress={handleCreateSubUser}
|
onPress={handleCreateSubUser}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
@ -477,7 +505,7 @@ const styles = StyleSheet.create({
|
|||||||
height: 47,
|
height: 47,
|
||||||
width: 279,
|
width: 279,
|
||||||
},
|
},
|
||||||
dialogTitle: { fontFamily: "Manrope_600SemiBold", fontSize: 22 },
|
dialogTitle: {fontFamily: "Manrope_600SemiBold", fontSize: 22},
|
||||||
dialogBackBtn: {
|
dialogBackBtn: {
|
||||||
fontFamily: "PlusJakartaSans_500Medium",
|
fontFamily: "PlusJakartaSans_500Medium",
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
@ -565,7 +593,7 @@ const styles = StyleSheet.create({
|
|||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
color: "white",
|
color: "white",
|
||||||
},
|
},
|
||||||
divider: { height: 0.7, backgroundColor: "#e6e6e6", width: "100%" },
|
divider: {height: 0.7, backgroundColor: "#e6e6e6", width: "100%"},
|
||||||
jakarta12: {
|
jakarta12: {
|
||||||
fontFamily: "PlusJakartaSans_500Medium",
|
fontFamily: "PlusJakartaSans_500Medium",
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
@ -575,7 +603,7 @@ const styles = StyleSheet.create({
|
|||||||
fontFamily: "PlusJakartaSans_500Medium",
|
fontFamily: "PlusJakartaSans_500Medium",
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
},
|
},
|
||||||
pfp: { aspectRatio: 1, width: 37.03, borderRadius: 10.56 },
|
pfp: {aspectRatio: 1, width: 37.03, borderRadius: 10.56},
|
||||||
userType: {
|
userType: {
|
||||||
fontFamily: "Manrope_500Medium",
|
fontFamily: "Manrope_500Medium",
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
|
@ -65,7 +65,6 @@ const MyProfile = () => {
|
|||||||
if (profileData) {
|
if (profileData) {
|
||||||
setFirstName(profileData.firstName || "");
|
setFirstName(profileData.firstName || "");
|
||||||
setLastName(profileData.lastName || "");
|
setLastName(profileData.lastName || "");
|
||||||
// setProfileImage(profileData.pfp || null);
|
|
||||||
setTimeZone(
|
setTimeZone(
|
||||||
profileData.timeZone || Localization.getCalendars()[0].timeZone!
|
profileData.timeZone || Localization.getCalendars()[0].timeZone!
|
||||||
);
|
);
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { useMutation, useQueryClient } from "react-query";
|
import {useMutation, useQueryClient} from "react-query";
|
||||||
import firestore from "@react-native-firebase/firestore";
|
import firestore from "@react-native-firebase/firestore";
|
||||||
import storage from "@react-native-firebase/storage";
|
import storage from "@react-native-firebase/storage";
|
||||||
import { useAuthContext } from "@/contexts/AuthContext";
|
import {useAuthContext} from "@/contexts/AuthContext";
|
||||||
import * as ImagePicker from "expo-image-picker";
|
import * as ImagePicker from "expo-image-picker";
|
||||||
import { Platform } from "react-native";
|
import {Platform} from "react-native";
|
||||||
|
|
||||||
export const useChangeProfilePicture = () => {
|
export const useChangeProfilePicture = (customUserId?: string) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { user, refreshProfileData } = useAuthContext();
|
const {user, refreshProfileData} = useAuthContext();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationKey: ["changeProfilePicture"],
|
mutationKey: ["changeProfilePicture"],
|
||||||
@ -38,10 +38,12 @@ export const useChangeProfilePicture = () => {
|
|||||||
const downloadURL = await reference.getDownloadURL();
|
const downloadURL = await reference.getDownloadURL();
|
||||||
console.log("Download URL:", downloadURL);
|
console.log("Download URL:", downloadURL);
|
||||||
|
|
||||||
|
if(!customUserId) {
|
||||||
await firestore()
|
await firestore()
|
||||||
.collection("Profiles")
|
.collection("Profiles")
|
||||||
.doc(user?.uid)
|
.doc(user?.uid)
|
||||||
.update({ pfp: downloadURL });
|
.update({pfp: downloadURL});
|
||||||
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Error uploading profile picture:", e);
|
console.error("Error uploading profile picture:", e);
|
||||||
@ -50,8 +52,10 @@ export const useChangeProfilePicture = () => {
|
|||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
// Invalidate queries to refresh profile data
|
// Invalidate queries to refresh profile data
|
||||||
|
if (!customUserId) {
|
||||||
queryClient.invalidateQueries("Profiles");
|
queryClient.invalidateQueries("Profiles");
|
||||||
refreshProfileData();
|
refreshProfileData();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
62
hooks/useUploadProfilePicture.ts
Normal file
62
hooks/useUploadProfilePicture.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import {useState} from "react";
|
||||||
|
import * as ImagePicker from "expo-image-picker";
|
||||||
|
import {useChangeProfilePicture} from "@/hooks/firebase/useChangeProfilePicture";
|
||||||
|
import {useUpdateUserData} from "@/hooks/firebase/useUpdateUserData";
|
||||||
|
|
||||||
|
export const useUploadProfilePicture = (customUserId?: string, existingPfp?: string) => {
|
||||||
|
const [profileImage, setProfileImage] = useState<
|
||||||
|
string | ImagePicker.ImagePickerAsset | null
|
||||||
|
>(existingPfp || null);
|
||||||
|
|
||||||
|
const [profileImageAsset, setProfileImageAsset] = useState<
|
||||||
|
ImagePicker.ImagePickerAsset | null
|
||||||
|
>(null);
|
||||||
|
|
||||||
|
const {mutateAsync: updateUserData} = useUpdateUserData();
|
||||||
|
const {mutateAsync: changeProfilePicture} = useChangeProfilePicture(customUserId);
|
||||||
|
|
||||||
|
const pickImage = async () => {
|
||||||
|
const permissionResult =
|
||||||
|
await ImagePicker.requestMediaLibraryPermissionsAsync();
|
||||||
|
if (!permissionResult.granted) {
|
||||||
|
alert("Permission to access camera roll is required!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await ImagePicker.launchImageLibraryAsync({
|
||||||
|
mediaTypes: ImagePicker.MediaTypeOptions.Images,
|
||||||
|
allowsEditing: true,
|
||||||
|
aspect: [1, 1],
|
||||||
|
quality: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result.canceled) {
|
||||||
|
setProfileImage(result.assets[0].uri);
|
||||||
|
setProfileImageAsset(result.assets[0]);
|
||||||
|
if (!customUserId) {
|
||||||
|
await changeProfilePicture(result.assets[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClearImage = async () => {
|
||||||
|
if (!customUserId) {
|
||||||
|
await updateUserData({newUserData: {pfp: null}});
|
||||||
|
}
|
||||||
|
setProfileImage(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const pfpUri =
|
||||||
|
profileImage && typeof profileImage === "object" && "uri" in profileImage
|
||||||
|
? profileImage.uri
|
||||||
|
: profileImage;
|
||||||
|
|
||||||
|
return {
|
||||||
|
pfpUri,
|
||||||
|
profileImage,
|
||||||
|
profileImageAsset,
|
||||||
|
handleClearImage,
|
||||||
|
pickImage,
|
||||||
|
changeProfilePicture
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user