From 47d5716238befaa2a64cb91c2cacd8b9752d02d7 Mon Sep 17 00:00:00 2001 From: Arik Chakma Date: Thu, 4 May 2023 00:56:01 +0600 Subject: [PATCH] feat: upload profile picture --- .../Profile/UploadProfilePicture.tsx | 186 ++++++++++++++++++ src/components/Setting/UpdateProfileForm.tsx | 16 +- 2 files changed, 196 insertions(+), 6 deletions(-) create mode 100644 src/components/Profile/UploadProfilePicture.tsx diff --git a/src/components/Profile/UploadProfilePicture.tsx b/src/components/Profile/UploadProfilePicture.tsx new file mode 100644 index 000000000..137edb970 --- /dev/null +++ b/src/components/Profile/UploadProfilePicture.tsx @@ -0,0 +1,186 @@ +import { useEffect, useRef, useState } from 'preact/hooks'; +import { httpCall, httpPost } from '../../lib/http'; +import Cookies from 'js-cookie'; +import { TOKEN_COOKIE_NAME } from '../../lib/jwt'; + +interface PreviewFile extends File { + preview: string; +} +export default function UploadProfilePicture({ + user, +}: { + user: { + image: string; + }; +}) { + const [file, setFile] = useState(null); + const [error, setError] = useState(''); + const [isLoading, setIsLoading] = useState(false); + const inputRef = useRef(null); + + const handleFileChange = async (e: Event) => { + setError(''); + const file = (e.target as HTMLInputElement).files?.[0]; + if (!file) return; + + // Check file size and dimension + const dimensions = await new Promise<{ + width: number; + height: number; + }>((resolve) => { + const img = new Image(); + img.onload = () => { + resolve({ width: img.width, height: img.height }); + }; + img.src = URL.createObjectURL(file); + }); + + // Image can't be larger than 3000x3000 pixels + if (dimensions.width > 3000 || dimensions.height > 3000) { + setError('Image dimensions are too big. Maximum 3000x3000 pixels.'); + return; + // Image can't be smaller than 100x100 pixels + } else if (dimensions.width < 100 || dimensions.height < 100) { + setError('Image dimensions are too small. Minimum 100x100 pixels.'); + return; + } + + // Image can't be larger than 1MB + if (file.size > 1024 * 1024) { + setError('Image size is too big. Maximum 1MB.'); + return; + } + + setError(''); + setFile( + Object.assign(file, { + preview: URL.createObjectURL(file), + }) + ); + }; + + const handleSubmit = async (e: Event) => { + e.preventDefault(); + setError(''); + setIsLoading(true); + if (!file) return; + + const formData = new FormData(); + formData.append('name', 'avatar'); + formData.append('avatar', file); + const res = await fetch( + `${import.meta.env.PUBLIC_API_URL}/v1-upload-profile-picture`, + { + method: 'POST', + body: formData, + credentials: 'include', + } + ); + + const data = await res.json(); + + if (!res.ok) { + setError(data.message || 'Something went wrong'); + setIsLoading(false); + } + // Logout user if token is invalid + if (data.status === 401) { + Cookies.remove(TOKEN_COOKIE_NAME); + window.location.reload(); + } + + window.location.reload(); + }; + + useEffect(() => { + // Necessary to revoke the preview URL when the component unmounts for avoiding memory leaks + return () => { + if (file) URL.revokeObjectURL(file.preview); + }; + }, [file]); + + return ( +
+ +
+ + + + {file && ( +
+ + +
+ )} +
+ {error && ( +

{error}

+ )} +
+ ); +} diff --git a/src/components/Setting/UpdateProfileForm.tsx b/src/components/Setting/UpdateProfileForm.tsx index e4524c3d2..11fece902 100644 --- a/src/components/Setting/UpdateProfileForm.tsx +++ b/src/components/Setting/UpdateProfileForm.tsx @@ -3,9 +3,11 @@ import { httpGet, httpPost } from '../../lib/http'; import Cookies from 'js-cookie'; import { TOKEN_COOKIE_NAME } from '../../lib/jwt'; import { pageLoadingMessage } from '../../stores/page'; +import UploadProfilePicture from '../Profile/UploadProfilePicture'; export function UpdateProfileForm() { const [name, setName] = useState(''); + const [image, setImage] = useState(''); const [email, setEmail] = useState(''); const [github, setGithub] = useState(''); const [twitter, setTwitter] = useState(''); @@ -59,7 +61,7 @@ export function UpdateProfileForm() { return; } - const { name, email, links } = response; + const { name, email, links, image } = response; setName(name); setEmail(email); @@ -67,6 +69,7 @@ export function UpdateProfileForm() { setLinkedin(links?.linkedin || ''); setTwitter(links?.twitter || ''); setWebsite(links?.website || ''); + setImage(image || ''); setIsLoading(false); }; @@ -80,11 +83,12 @@ export function UpdateProfileForm() { }, []); return ( -
+

Profile

Update your profile details below.

-
-
+ + +
- + +
); }