Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Settings Page - allows users to change info (first, last, avatar) #76

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions client/src/components/lib/Board.tsx
Original file line number Diff line number Diff line change
@@ -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';

/**
Expand All @@ -16,6 +17,7 @@ export const Board = () => {
<div className="flex flex-col w-5/6 h-full bg-[#FEFDFF]">
<TopBar />
{board === 'Folder' && <BoardScroll />}
{board == 'Settings' && <Settings />}
{/** To be added template and setting page */}
</div>
);
Expand Down
165 changes: 165 additions & 0 deletions client/src/components/lib/Settings.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLInputElement | null>(null);
const firstNameRef = useRef<HTMLInputElement | null>(null);
const lastNameRef = useRef<HTMLInputElement | null>(null);
const { updateUser } = useAuthStore(['updateUser']);

const { userFirstName, userLastName, userPicture, userID } = useAuthStore([
'userFirstName',
'userLastName',
'userPicture',
'userID',
]);

const handleProfilePictureChange = (
e: React.ChangeEvent<HTMLInputElement>,
) => {
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);
}
};
zackzouk marked this conversation as resolved.
Show resolved Hide resolved

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 <div>This page is not available for Google Sign-In users.</div>;
}
return (
<Card className="rounded-none">
<CardHeader className="space-y-1">
<CardTitle className="text-2xl">Update Account Information</CardTitle>
<CardDescription>Edit Account Information Below</CardDescription>
</CardHeader>
<CardContent className="grid gap-1">
<div className="grid grid-cols-1 gap-1">
<Label htmlFor="firstName">Enter New First Name</Label>
<Input
id="firstName"
type="text"
defaultValue={userFirstName}
ref={firstNameRef}
required
/>
</div>
<div className="grid grid-cols-1 gap-1">
<Label htmlFor="lastName">Enter New Last Name</Label>
<Input
id="lastName"
type="text"
defaultValue={userLastName}
ref={lastNameRef}
required
/>
</div>
<div className="grid grid-cols-1 gap-1">
<Label htmlFor="profilePicture">Upload New Profile Picture</Label>
<Input
ref={profilePictureRef}
id="profilePicture"
type="file"
accept=".png, .jpg, .jpeg"
onChange={handleProfilePictureChange}
/>
{profilePictureThumbnail && (
<div>
<Label>Profile Picture Thumbnail</Label>
<img
src={profilePictureThumbnail}
alt="Profile Thumbnail"
className="max-w-xs h-auto"
/>
</div>
)}
</div>
</CardContent>
<CardFooter>
<div className="">
<Button onClick={handleUpdateUserInfo}>Save</Button>
</div>
</CardFooter>
</Card>
);
};
1 change: 1 addition & 0 deletions client/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`,
Expand Down
8 changes: 8 additions & 0 deletions client/src/stores/AuthStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface AuthActions {
userPicture: string,
userID: string,
) => void;
updateUser: (meta: Partial<AuthState>) => void;
}

type AuthStore = AuthActions & AuthState;
Expand Down Expand Up @@ -60,9 +61,16 @@ const setUser =
};
});

const updateUser = (set: SetState<AuthStore>) => (meta: Partial<AuthState>) => {
set((state) => {
return { ...state, ...meta };
});
};

/** Store Hook */
const AuthStore = create<AuthStore>()((set) => ({
...initialAuthState,
setUser: setUser(set),
updateUser: updateUser(set),
}));
export const useAuthStore = createStoreWithSelectors(AuthStore);
32 changes: 16 additions & 16 deletions client/src/views/SignUpPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,22 @@ import { useCanvasElementStore } from '@/stores/CanvasElementsStore';
* @author Zakariyya Almalki
*/

export const generateImageUrl = (file: File): Promise<string> => {
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,
Expand Down Expand Up @@ -108,22 +124,6 @@ export default function SignUp() {
pictureId,
);

const generateImageUrl = (file: File): Promise<string> => {
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])
: '';
Expand Down
Loading