From 9876a9807242f642087e6fe2f52a8cc620d99745 Mon Sep 17 00:00:00 2001 From: Shristi Sharan Date: Wed, 19 Jun 2024 18:14:24 +0530 Subject: [PATCH 1/2] enhanced-#137: Profile Image Upload Process with formatting option --- .../components/SettingsComponents/Profile.js | 101 ++++++++++++++---- 1 file changed, 82 insertions(+), 19 deletions(-) diff --git a/frontend/src/components/SettingsComponents/Profile.js b/frontend/src/components/SettingsComponents/Profile.js index 16edc4a..d09dedf 100644 --- a/frontend/src/components/SettingsComponents/Profile.js +++ b/frontend/src/components/SettingsComponents/Profile.js @@ -1,8 +1,11 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useRef} from "react"; import { Avatar } from "@mui/material"; import { useDispatch, useSelector } from "react-redux"; import { setUser } from "../../services/Actions/User/actions"; -import toast from "react-toastify"; +// import toast from "react-toastify"; +import { Cropper } from "react-cropper"; +import "cropperjs/dist/cropper.css"; +import { FaPen } from "react-icons/fa"; export default function Profile() { const dispatch = useDispatch(); @@ -11,6 +14,8 @@ export default function Profile() { const data = JSON.parse(localStorage.getItem("info")); const [Pic, setPic] = useState(data.pic); const [selectedFile, setSelectedFile] = useState(null); + const [imagePreview, setImagePreview] = useState(null); + const cropperRef = useRef(null); useEffect(() => { if (dataredux === null) return; @@ -20,19 +25,25 @@ export default function Profile() { const handleFileChange = (event) => { const file = event.target.files[0]; setSelectedFile(file); + const reader = new FileReader(); + reader.onload = () => { + setImagePreview(reader.result); + }; + reader.readAsDataURL(file); }; const handleUpload = () => { - if (selectedFile) { - // Ask for confirmation before uploading - const isConfirmed = window.confirm( - "Are you sure you want to upload this image?" - ); - - if (!isConfirmed) return; + if (cropperRef.current && cropperRef.current.cropper) { + const croppedImageDataUrl = cropperRef.current.cropper.getCroppedCanvas({ + width: 150, + height: 150, + fillColor: '#fff', + }).toDataURL(); + // Convert the base64 URL to a Blob + const blob = dataURLtoBlob(croppedImageDataUrl); const formData = new FormData(); - formData.append("photo", selectedFile); + formData.append("photo", blob); const cookie = localStorage.getItem("jwt"); fetch(`${process.env.REACT_APP_API_URL}/api/v1/users/uploadPhoto`, { @@ -47,6 +58,8 @@ export default function Profile() { // Alert if image uploaded successfully alert("Image uploaded successfully!"); dispatch(setUser(data.data.user)); + setPic(data.data.user.pic); // Update the profile picture + setImagePreview(null); }) .catch((error) => { // Handle any errors that occur during the upload. @@ -57,10 +70,29 @@ export default function Profile() { } }; + const handleCancel = () => { + setSelectedFile(null); + setImagePreview(null); + }; + + const dataURLtoBlob = (dataurl) => { + const arr = dataurl.split(','); + const mime = arr[0].match(/:(.*?);/)[1]; + const bstr = atob(arr[1]); + let n = bstr.length; + const u8arr = new Uint8Array(n); + while (n--) { + u8arr[n] = bstr.charCodeAt(n); + } + return new Blob([u8arr], { type: mime }); + }; + + let image = Pic; if (Pic.startsWith("user")) image = `${process.env.REACT_APP_API_URL}/${Pic}`; return ( +
-
-
- Upload Picture
+ {imagePreview && ( +
+
+ +
+
+
+
Upload
+
Cancel
+
+
+ )}
-
); } + + From 6e8c05e7c38493e9bc68af9e9d86a7ff00e434d4 Mon Sep 17 00:00:00 2001 From: Shristi Sharan Date: Wed, 19 Jun 2024 21:09:35 +0530 Subject: [PATCH 2/2] enhance-#137: installed react cropper for image sizing --- frontend/package-lock.json | 114 +++++++++++++------------------------ frontend/package.json | 2 + 2 files changed, 41 insertions(+), 75 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 42e95d9..90fde10 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "frontend", "version": "0.1.0", "dependencies": { "@emoji-mart/data": "^1.2.1", @@ -19,13 +20,14 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "cropperjs": "^1.6.2", "date-fns": "^3.6.0", "emoji-mart": "^5.6.0", "gender-detection-from-name": "^1.8.0", - "javascript-time-ago": "^2.5.10", "jwt-decode": "^3.1.2", "randomcolor": "^0.6.2", "react": "^18.2.0", + "react-cropper": "^2.3.3", "react-dom": "^18.2.0", "react-email-validator": "^1.0.2", "react-icons": "^4.10.1", @@ -34,7 +36,6 @@ "react-router-dom": "^6.15.0", "react-scripts": "5.0.1", "react-speech-recognition": "^3.10.0", - "react-time-ago": "^7.3.3", "react-toastify": "^9.1.3", "redux-thunk": "^2.4.2", "socket.io-client": "^4.7.2", @@ -5510,8 +5511,6 @@ "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "optional": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -5526,9 +5525,7 @@ "node_modules/ajv-formats/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "optional": true, - "peer": true + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/ajv-keywords": { "version": "3.5.2", @@ -6800,6 +6797,11 @@ "node": ">=10" } }, + "node_modules/cropperjs": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.6.2.tgz", + "integrity": "sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA==" + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -10616,14 +10618,6 @@ "node": ">=8" } }, - "node_modules/javascript-time-ago": { - "version": "2.5.10", - "resolved": "https://registry.npmjs.org/javascript-time-ago/-/javascript-time-ago-2.5.10.tgz", - "integrity": "sha512-EUxp4BP74QH8xiYHyeSHopx1XhMMJ9qEX4rcBdFtpVWmKRdzpxbNzz2GSbuekZr5wt0rmLehuyp0PE34EAJT9g==", - "dependencies": { - "relative-time-format": "^1.1.6" - } - }, "node_modules/jest": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", @@ -12905,11 +12899,6 @@ "node": ">= 4.0.0" } }, - "node_modules/memoize-one": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" - }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -15229,6 +15218,17 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, + "node_modules/react-cropper": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/react-cropper/-/react-cropper-2.3.3.tgz", + "integrity": "sha512-zghiEYkUb41kqtu+2jpX2Ntigf+Jj1dF9ew4lAobPzI2adaPE31z0p+5TcWngK6TvmWQUwK3lj4G+NDh1PDQ1w==", + "dependencies": { + "cropperjs": "^1.5.13" + }, + "peerDependencies": { + "react": ">=17.0.2" + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -15556,21 +15556,6 @@ "react": ">=16.8.0" } }, - "node_modules/react-time-ago": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/react-time-ago/-/react-time-ago-7.3.3.tgz", - "integrity": "sha512-5kh2Kuu/UhHzcZrGvf3GUrF2d+IXjkIXif5MR2iDWIfSqQuBW27/ejN/tmzJBRyPiryYTgbDIG6AZFJ4RW3yfw==", - "dependencies": { - "memoize-one": "^6.0.0", - "prop-types": "^15.8.1", - "raf": "^3.4.1" - }, - "peerDependencies": { - "javascript-time-ago": "^2.3.7", - "react": ">=0.16.8", - "react-dom": ">=0.16.8" - } - }, "node_modules/react-toastify": { "version": "9.1.3", "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz", @@ -15789,11 +15774,6 @@ "node": ">= 0.10" } }, - "node_modules/relative-time-format": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/relative-time-format/-/relative-time-format-1.1.6.tgz", - "integrity": "sha512-aCv3juQw4hT1/P/OrVltKWLlp15eW1GRcwP1XdxHrPdZE9MtgqFpegjnTjLhi2m2WI9MT/hQQtE+tjEWG1hgkQ==" - }, "node_modules/renderkid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", @@ -22428,13 +22408,14 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "requires": {}, + "requires": { + "ajv": "^8.0.0" + }, "dependencies": { "ajv": { - "version": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "optional": true, - "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -22445,9 +22426,7 @@ "json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "optional": true, - "peer": true + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" } } }, @@ -23386,6 +23365,11 @@ "yaml": "^1.10.0" } }, + "cropperjs": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.6.2.tgz", + "integrity": "sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA==" + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -26115,14 +26099,6 @@ } } }, - "javascript-time-ago": { - "version": "2.5.10", - "resolved": "https://registry.npmjs.org/javascript-time-ago/-/javascript-time-ago-2.5.10.tgz", - "integrity": "sha512-EUxp4BP74QH8xiYHyeSHopx1XhMMJ9qEX4rcBdFtpVWmKRdzpxbNzz2GSbuekZr5wt0rmLehuyp0PE34EAJT9g==", - "requires": { - "relative-time-format": "^1.1.6" - } - }, "jest": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", @@ -27812,11 +27788,6 @@ "fs-monkey": "^1.0.4" } }, - "memoize-one": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" - }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -29301,6 +29272,14 @@ } } }, + "react-cropper": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/react-cropper/-/react-cropper-2.3.3.tgz", + "integrity": "sha512-zghiEYkUb41kqtu+2jpX2Ntigf+Jj1dF9ew4lAobPzI2adaPE31z0p+5TcWngK6TvmWQUwK3lj4G+NDh1PDQ1w==", + "requires": { + "cropperjs": "^1.5.13" + } + }, "react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -29528,16 +29507,6 @@ "integrity": "sha512-EVSr4Ik8l9urwdPiK2r0+ADrLyDDrjB0qBRdUWO+w2MfwEBrj6NuRmy1GD3x7BU/V6/hab0pl8Lupen0zwlJyw==", "requires": {} }, - "react-time-ago": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/react-time-ago/-/react-time-ago-7.3.3.tgz", - "integrity": "sha512-5kh2Kuu/UhHzcZrGvf3GUrF2d+IXjkIXif5MR2iDWIfSqQuBW27/ejN/tmzJBRyPiryYTgbDIG6AZFJ4RW3yfw==", - "requires": { - "memoize-one": "^6.0.0", - "prop-types": "^15.8.1", - "raf": "^3.4.1" - } - }, "react-toastify": { "version": "9.1.3", "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz", @@ -29708,11 +29677,6 @@ "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==" }, - "relative-time-format": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/relative-time-format/-/relative-time-format-1.1.6.tgz", - "integrity": "sha512-aCv3juQw4hT1/P/OrVltKWLlp15eW1GRcwP1XdxHrPdZE9MtgqFpegjnTjLhi2m2WI9MT/hQQtE+tjEWG1hgkQ==" - }, "renderkid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 1baec9a..e3a1d50 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,12 +15,14 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "cropperjs": "^1.6.2", "date-fns": "^3.6.0", "emoji-mart": "^5.6.0", "gender-detection-from-name": "^1.8.0", "jwt-decode": "^3.1.2", "randomcolor": "^0.6.2", "react": "^18.2.0", + "react-cropper": "^2.3.3", "react-dom": "^18.2.0", "react-email-validator": "^1.0.2", "react-icons": "^4.10.1",