From db1e65b3fac1c3b29f84a789e2e0a5bd160f0dcc Mon Sep 17 00:00:00 2001 From: Alex Erdei Date: Tue, 6 May 2025 10:10:59 +0100 Subject: [PATCH] Fix the missing data in current user's profile and bad online status --- src/components/Profile.jsx | 576 +++++++++++++++++-------------- src/features/users/usersSlice.js | 4 + 2 files changed, 325 insertions(+), 255 deletions(-) diff --git a/src/components/Profile.jsx b/src/components/Profile.jsx index a862604..c8a2791 100644 --- a/src/components/Profile.jsx +++ b/src/components/Profile.jsx @@ -1,311 +1,377 @@ -import React, { useState, useEffect, useRef } from "react"; +import React, { useState, useEffect, useRef, useMemo } from "react"; + import { - Row, - Col, - DropdownButton, - Dropdown, - Button, - Nav, - Navbar, + Row, + Col, + DropdownButton, + Dropdown, + Button, + Nav, + Navbar, } from "react-bootstrap"; + import { - Link, - Switch, - Route, - useRouteMatch, - useParams, + Link, + Switch, + Route, + useRouteMatch, + useParams, } from "react-router-dom"; + import { MdPhotoCamera } from "react-icons/md"; + import { IoTrashOutline } from "react-icons/io5"; + import { ImUpload2 } from "react-icons/im"; + import { HiOutlinePhotograph } from "react-icons/hi"; + import CircularImage from "./CircularImage"; + import NestedRoute from "./NestedRoute"; + import RemoveCoverPhotoDlg from "./RemoveCoverPhotoDlg"; + import SelectBgPhotoModal from "./SelectBgPhotoModal"; + import UpdateProfilePicModal from "./UpdateProfilePicModal"; + import UploadPhoto from "./UploadPhoto"; + import Posts from "./Posts"; + import StorageImage from "./StorageImage"; + import "./Profile.css"; + import { useDispatch, useSelector } from "react-redux"; + import { updateProfile } from "../backend/backend"; + import { linkUpdated } from "../features/link/linkSlice"; -const Profile = (props) => { - const { userName } = useParams(); +const Profile = () => { + const { userName } = useParams(); - const userID = useSelector((state) => state.user.id); - const users = useSelector((state) => state.users); - const link = useSelector((state) => state.link); + const dispatch = useDispatch(); - const user = () => { - const userNames = users.map((user) => { - if (!user.index || user.index === 0) - return `${user.lastname}.${user.firstname}`; - else return `${user.lastname}.${user.firstname}.${user.index}`; - }); - const index = userNames.indexOf(userName); - const user = users[index]; - return user; - }; + const meID = useSelector((s) => s.user.id); // logged-in id - const userId = () => user().userID; + const users = useSelector((s) => s.users); - const isCurrentUser = userID === userId(); + const link = useSelector((s) => s.link); - let { firstname, lastname, profilePictureURL, backgroundPictureURL, photos } = - user(); + /* ------------------------------------------------------------------ - const [showRemoveCoverPhotoDlg, setShowRemoveCoverPhotoDlg] = useState(false); + Resolve the viewed profile once (re-runs when users or url change) - const [showSelectPhoto, setShowSelectPhoto] = useState(false); + ------------------------------------------------------------------ */ - const [showUpdateProfilePic, setShowUpdateProfilePic] = useState(false); + const profile = useMemo(() => { + if (!users?.length) return null; - const [showUploadPhotoDlg, setShowUploadPhotoDlg] = useState(false); + const aliases = users.map((u) => + !u.index || u.index === 0 + ? `${u.lastname}.${u.firstname}` + : `${u.lastname}.${u.firstname}.${u.index}` + ); - const [nameOfURL, setNameOfURL] = useState("backgroundPictureURL"); + const idx = aliases.indexOf(userName); - const [activeLink, setActiveLink] = useState(null); + return idx === -1 ? null : users[idx]; + }, [users, userName]); - //we need the refs to handle the activeLink changes - const photosLinkRef = useRef(null); - const friendsLinkRef = useRef(null); - const postsLinkRef = useRef(null); + /* still loading or invalid username → simple fallback */ - const linkHandlingProps = { - linkRefs: { - photos: photosLinkRef, - friends: friendsLinkRef, - posts: postsLinkRef - }, - linkState: [activeLink, setActiveLink] - } + if (!profile) return

Loading …

; - const { url, path } = useRouteMatch(); + const { + userID, // id of the profile we view - function openFileInput(nameOfURL) { - setNameOfURL(nameOfURL); - setShowUploadPhotoDlg(true); - } + firstname, - function handleSelect(key) { - switch (key) { - case "3": - setShowRemoveCoverPhotoDlg(true); - break; - case "2": - openFileInput("backgroundPictureURL"); - break; - case "1": - setShowSelectPhoto(true); - break; - default: - return; - } - } + lastname, - function closeDlg() { - setShowRemoveCoverPhotoDlg(false); - } + profilePictureURL, - function removeCoverPhoto() { - closeDlg(); - return updateProfile({ backgroundPictureURL: "background-server.jpg" }); - } + backgroundPictureURL, - function hideBgPhotoModal() { - setShowSelectPhoto(false); - } + photos = [], + } = profile; - function handleBgPhotoClick(event) { - hideBgPhotoModal(); - handlePhotoClick(event, "backgroundPictureURL"); - } + const isCurrentUser = meID === userID; - function hideProfilePicModal() { - setShowUpdateProfilePic(false); - } + /* ---------------- Local UI state ---------------- */ - function handleUploadProfilePicClick() { - hideProfilePicModal(); - openFileInput("profilePictureURL"); - } + const [showRemoveCoverPhotoDlg, setShowRemoveCoverPhotoDlg] = useState(false); - function handleProfilePicClick(event) { - hideProfilePicModal(); - handlePhotoClick(event, "profilePictureURL"); - } + const [showSelectPhoto, setShowSelectPhoto] = useState(false); - function updatePhotos(file) { - const newPhoto = { fileName: file.name }; - const filenames = photos.map((photo) => photo.fileName); - const newPhotos = [...photos]; - if (filenames.indexOf(file.name) === -1) { - newPhotos.push(newPhoto); - } - const newProfile = { photos: newPhotos }; - if (nameOfURL !== "") newProfile[nameOfURL] = `${userID}/${file.name}`; - return updateProfile(newProfile); - } + const [showUpdateProfilePic, setShowUpdateProfilePic] = useState(false); - function handlePhotoClick(e, name) { - const index = Number(e.target.id); - const photo = photos[index]; - const storagePath = `${userID}/${photo.fileName}`; - return updateProfile({ [name]: storagePath }); - } + const [showUploadPhotoDlg, setShowUploadPhotoDlg] = useState(false); - const dispatch = useDispatch(); + const [nameOfURL, setNameOfURL] = useState("backgroundPictureURL"); - useEffect(() => { - //we set the active link to the profile link when it renders - //unless we are on the friends page and the window is wide - //enough to see the profile on that page - if (link.active !== "friends" || window.innerWidth < 600) - dispatch(linkUpdated("profile")); - }, [dispatch, link]); + const [activeLink, setActiveLink] = useState(null); - return ( - <> - - -
- - {isCurrentUser && ( - - - Edit Cover Photo - - } - size="sm"> - - - Select Photo - - - - Upload Photo - - - - Remove - - - )} -
- - {isCurrentUser && ( - - )} -
-
-

- - {firstname} {lastname} - -

-
- - - - -
- - - - - openFileInput("")} - //we only need the rest to handle the changes of the activeLink - linkHandling = {linkHandlingProps} - /> - - - - - - - + /* refs for the “active link” helper */ - + const photosLinkRef = useRef(null); - + const friendsLinkRef = useRef(null); - + const postsLinkRef = useRef(null); - - - ); + const linkHandlingProps = { + linkRefs: { + photos: photosLinkRef, + friends: friendsLinkRef, + posts: postsLinkRef, + }, + + linkState: [activeLink, setActiveLink], + }; + + const { url, path } = useRouteMatch(); + + /* ---------- helper fns (unchanged except userID var names) ----- */ + + function openFileInput(name) { + setNameOfURL(name); + setShowUploadPhotoDlg(true); + } + + function handleSelect(key) { + if (key === "3") setShowRemoveCoverPhotoDlg(true); + else if (key === "2") openFileInput("backgroundPictureURL"); + else if (key === "1") setShowSelectPhoto(true); + } + + function closeDlg() { + setShowRemoveCoverPhotoDlg(false); + } + + const removeCoverPhoto = () => ( + closeDlg(), updateProfile({ backgroundPictureURL: "background-server.jpg" }) + ); + + const hideBgPhotoModal = () => setShowSelectPhoto(false); + + const hideProfilePicModal = () => setShowUpdateProfilePic(false); + + function handlePhotoClick(e, field) { + const idx = Number(e.target.id); + + const file = photos[idx]?.fileName; + + if (!file) return; + + updateProfile({ [field]: `${userID}/${file}` }); + } + + function updatePhotos(file) { + const filenames = photos.map((p) => p.fileName); + + const newPhotos = filenames.includes(file.name) + ? photos + : [...photos, { fileName: file.name }]; + + const patch = { photos: newPhotos }; + + if (nameOfURL) patch[nameOfURL] = `${userID}/${file.name}`; + + return updateProfile(patch); + } + + /* ---------------------------------------------------------- + + helpers that the JSX at the bottom expects + + ---------------------------------------------------------- */ + + /* background-photo picker in SelectBgPhotoModal */ + + function handleBgPhotoClick(e) { + hideBgPhotoModal(); // close the modal + + handlePhotoClick(e, "backgroundPictureURL"); + } + + /* “Upload Photo” button in UpdateProfilePicModal */ + + function handleUploadProfilePicClick() { + hideProfilePicModal(); // close the modal + + openFileInput("profilePictureURL"); // open file chooser + } + + /* Pick an existing picture as the new profile picture */ + + function handleProfilePicClick(e) { + hideProfilePicModal(); // close modal + + handlePhotoClick(e, "profilePictureURL"); + } + + /* -------- set the active top-bar link on mount ----------------- */ + + useEffect(() => { + if (link.active !== "friends" || window.innerWidth < 600) + dispatch(linkUpdated("profile")); + }, [dispatch, link]); + + /* ------------------------------------------------------------------ + + RENDER – everything below is the original markup + + (only userId() → userID replacements) + + ------------------------------------------------------------------ */ + return ( + <> + + +
+ + {isCurrentUser && ( + + + Edit Cover Photo + + } + size='sm' + > + + + Select Photo + + + + Upload Photo + + + + Remove + + + )} +
+ + {isCurrentUser && ( + + )} +
+
+

+ + {firstname} {lastname} + +

+
+ + + + +
+ + + + + openFileInput("")} + //we only need the rest to handle the changes of the activeLink + linkHandling={linkHandlingProps} + /> + + + + + + + + + + + + + + + + + ); }; export default Profile; diff --git a/src/features/users/usersSlice.js b/src/features/users/usersSlice.js index 5f8f4a1..2f2c78c 100644 --- a/src/features/users/usersSlice.js +++ b/src/features/users/usersSlice.js @@ -45,6 +45,10 @@ export const usersSlice = createSlice({ /* add other fields you expect to arrive partially … */ + if (raw.posts !== undefined) patch.posts = JSON.parse(raw.posts); + + if (raw.photos !== undefined) patch.photos = JSON.parse(raw.photos); + state[idx] = { ...cur, ...patch }; // keep old fields }); },