From dbfb7bfc87eaa41125d716450a23015b3dd3b1c1 Mon Sep 17 00:00:00 2001 From: Zakariyya Almalki Date: Sun, 3 Mar 2024 14:47:33 -0500 Subject: [PATCH] allow users to update info (#76) --- client/src/components/lib/Board.tsx | 2 + client/src/components/lib/Settings.tsx | 165 +++++++++++++++++++++++++ client/src/constants.ts | 1 + client/src/stores/AuthStore.ts | 8 ++ client/src/views/SignUpPage.tsx | 32 ++--- 5 files changed, 192 insertions(+), 16 deletions(-) create mode 100644 client/src/components/lib/Settings.tsx diff --git a/client/src/components/lib/Board.tsx b/client/src/components/lib/Board.tsx index 8a2e01f..58470de 100644 --- a/client/src/components/lib/Board.tsx +++ b/client/src/components/lib/Board.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { TopBar } from '@/components/lib/TopBar'; import { BoardScroll } from './BoardScroll'; +import { Settings } from './Settings'; import { useCanvasBoardStore } from '@/stores/CanavasBoardStore'; /** @@ -16,6 +17,7 @@ export const Board = () => {
{board === 'Folder' && } + {board == 'Settings' && } {/** To be added template and setting page */}
); diff --git a/client/src/components/lib/Settings.tsx b/client/src/components/lib/Settings.tsx new file mode 100644 index 0000000..e87e5ea --- /dev/null +++ b/client/src/components/lib/Settings.tsx @@ -0,0 +1,165 @@ +import React, { useRef, useState } from 'react'; +import { Input } from '../ui/input'; +import { Label } from '../ui/label'; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from '../ui/card'; +import { generateImageUrl } from '@/views/SignUpPage'; +import { generateRandId } from '@/lib/bytes'; +import { Button } from '../ui/button'; +import { useAuthStore } from '@/stores/AuthStore'; +import { getDownloadURL, getStorage, ref, uploadBytes } from 'firebase/storage'; +import { firebaseApp } from '@/firebaseDB/firebase'; +import { REST } from '@/constants'; +import axios from 'axios'; + +/** + * It is the setting page, allows users to change some params + * @author Zakariyya Almalki + */ + +export const Settings = () => { + const profilePictureRef = useRef(null); + const firstNameRef = useRef(null); + const lastNameRef = useRef(null); + const { updateUser } = useAuthStore(['updateUser']); + + const { userFirstName, userLastName, userPicture, userID } = useAuthStore([ + 'userFirstName', + 'userLastName', + 'userPicture', + 'userID', + ]); + + const handleProfilePictureChange = ( + e: React.ChangeEvent, + ) => { + const selectedFile = e.target.files?.[0]; + if (selectedFile) { + // Read the selected file and display it as a thumbnail + const reader = new FileReader(); + reader.onload = (event) => { + if (event.target) { + const thumbnail = event.target.result; + setProfilePictureThumbnail(thumbnail as string); + } + }; + reader.readAsDataURL(selectedFile); + } + }; + + const handleUpdateUserInfo = async () => { + try { + const pictureId = generateRandId(); + const profilePic = profilePictureRef.current?.files + ? generateImageUrl(profilePictureRef.current?.files[0]) + : ''; + const storage = getStorage(firebaseApp); + const storageRef = ref(storage, `profilePictures/${pictureId}.jpg`); // give the image a random id + profilePictureRef.current?.files?.[0] && + uploadBytes(storageRef, profilePictureRef.current?.files[0]) + .then((snapshot) => { + return getDownloadURL(snapshot.ref); + }) + .catch(() => { + alert('Error uploading'); + }); + if (profilePictureRef.current?.files?.[0]) { + updateUser({ + userFirstName: firstNameRef.current?.value ?? '', + userLastName: lastNameRef.current?.value ?? '', + userPicture: (await profilePic) ?? '', + }); + axios.put(REST.user.update, { + id: userID, + fields: { + firstname: firstNameRef.current?.value ?? '', + lastname: lastNameRef.current?.value ?? '', + avatar: pictureId, + }, + }); + } else { + updateUser({ + userFirstName: firstNameRef.current?.value ?? '', + userLastName: lastNameRef.current?.value ?? '', + }); + axios.put(REST.user.update, { + id: userID, + fields: { + firstname: firstNameRef.current?.value ?? '', + lastname: lastNameRef.current?.value ?? '', + }, + }); + } + } catch (error) { + console.error('Error updating user info:', error); + } + }; + const [profilePictureThumbnail, setProfilePictureThumbnail] = useState< + string | null + >(null); + if (userPicture.includes('googleuser')) { + // Here you can return a message or a different component indicating the page is disabled + return
This page is not available for Google Sign-In users.
; + } + return ( + + + Update Account Information + Edit Account Information Below + + +
+ + +
+
+ + +
+
+ + + {profilePictureThumbnail && ( +
+ + Profile Thumbnail +
+ )} +
+
+ +
+ +
+
+
+ ); +}; diff --git a/client/src/constants.ts b/client/src/constants.ts index 4f15eee..338dc90 100644 --- a/client/src/constants.ts +++ b/client/src/constants.ts @@ -17,6 +17,7 @@ export const REST = { user: { get: `${REST_ROOT}/user/getUser`, create: `${REST_ROOT}/user/createUser`, + update: `${REST_ROOT}/user/updateUser`, }, sfu: { poll: `${REST_ROOT}/sfu/poll`, diff --git a/client/src/stores/AuthStore.ts b/client/src/stores/AuthStore.ts index a41f233..371d92d 100644 --- a/client/src/stores/AuthStore.ts +++ b/client/src/stores/AuthStore.ts @@ -25,6 +25,7 @@ interface AuthActions { userPicture: string, userID: string, ) => void; + updateUser: (meta: Partial) => void; } type AuthStore = AuthActions & AuthState; @@ -60,9 +61,16 @@ const setUser = }; }); +const updateUser = (set: SetState) => (meta: Partial) => { + set((state) => { + return { ...state, ...meta }; + }); +}; + /** Store Hook */ const AuthStore = create()((set) => ({ ...initialAuthState, setUser: setUser(set), + updateUser: updateUser(set), })); export const useAuthStore = createStoreWithSelectors(AuthStore); diff --git a/client/src/views/SignUpPage.tsx b/client/src/views/SignUpPage.tsx index a4c038e..4bb9d4f 100644 --- a/client/src/views/SignUpPage.tsx +++ b/client/src/views/SignUpPage.tsx @@ -35,6 +35,22 @@ import { useCanvasElementStore } from '@/stores/CanvasElementsStore'; * @author Zakariyya Almalki */ +export const generateImageUrl = (file: File): Promise => { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = (e) => { + if (e.target && e.target.result) { + const generatedUrl = e.target.result as string; + resolve(generatedUrl); + } else { + reject(new Error('Failed to generate image URL')); + } + }; + // Read the file as a data URL + reader.readAsDataURL(file); + }); +}; + export function signup( email: string, password: string, @@ -108,22 +124,6 @@ export default function SignUp() { pictureId, ); - const generateImageUrl = (file: File): Promise => { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = (e) => { - if (e.target && e.target.result) { - const generatedUrl = e.target.result as string; - resolve(generatedUrl); - } else { - reject(new Error('Failed to generate image URL')); - } - }; - // Read the file as a data URL - reader.readAsDataURL(file); - }); - }; - const profilePic = profilePictureRef.current?.files ? generateImageUrl(profilePictureRef.current?.files[0]) : '';