Fix the missing data in current user's profile and bad online status
This commit is contained in:
parent
5a742bf32e
commit
db1e65b3fa
|
@ -1,4 +1,5 @@
|
|||
import React, { useState, useEffect, useRef } from "react";
|
||||
import React, { useState, useEffect, useRef, useMemo } from "react";
|
||||
|
||||
import {
|
||||
Row,
|
||||
Col,
|
||||
|
@ -8,6 +9,7 @@ import {
|
|||
Nav,
|
||||
Navbar,
|
||||
} from "react-bootstrap";
|
||||
|
||||
import {
|
||||
Link,
|
||||
Switch,
|
||||
|
@ -15,47 +17,91 @@ import {
|
|||
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 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();
|
||||
/* ------------------------------------------------------------------
|
||||
|
||||
Resolve the viewed profile once (re-runs when users or url change)
|
||||
|
||||
------------------------------------------------------------------ */
|
||||
|
||||
const profile = useMemo(() => {
|
||||
if (!users?.length) return null;
|
||||
|
||||
const aliases = users.map((u) =>
|
||||
!u.index || u.index === 0
|
||||
? `${u.lastname}.${u.firstname}`
|
||||
: `${u.lastname}.${u.firstname}.${u.index}`
|
||||
);
|
||||
|
||||
const idx = aliases.indexOf(userName);
|
||||
|
||||
return idx === -1 ? null : users[idx];
|
||||
}, [users, userName]);
|
||||
|
||||
/* still loading or invalid username → simple fallback */
|
||||
|
||||
if (!profile) return <p className='text-center mt-5'>Loading …</p>;
|
||||
|
||||
const {
|
||||
userID, // id of the profile we view
|
||||
|
||||
firstname,
|
||||
|
||||
lastname,
|
||||
|
||||
profilePictureURL,
|
||||
|
||||
backgroundPictureURL,
|
||||
|
||||
photos = [],
|
||||
} = profile;
|
||||
|
||||
const isCurrentUser = meID === userID;
|
||||
|
||||
/* ---------------- Local UI state ---------------- */
|
||||
|
||||
const [showRemoveCoverPhotoDlg, setShowRemoveCoverPhotoDlg] = useState(false);
|
||||
|
||||
|
@ -69,183 +115,203 @@ const Profile = (props) => {
|
|||
|
||||
const [activeLink, setActiveLink] = useState(null);
|
||||
|
||||
//we need the refs to handle the activeLink changes
|
||||
/* 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
|
||||
posts: postsLinkRef,
|
||||
},
|
||||
linkState: [activeLink, setActiveLink]
|
||||
}
|
||||
|
||||
linkState: [activeLink, setActiveLink],
|
||||
};
|
||||
|
||||
const { url, path } = useRouteMatch();
|
||||
|
||||
function openFileInput(nameOfURL) {
|
||||
setNameOfURL(nameOfURL);
|
||||
/* ---------- helper fns (unchanged except userID var names) ----- */
|
||||
|
||||
function openFileInput(name) {
|
||||
setNameOfURL(name);
|
||||
setShowUploadPhotoDlg(true);
|
||||
}
|
||||
|
||||
function handleSelect(key) {
|
||||
switch (key) {
|
||||
case "3":
|
||||
setShowRemoveCoverPhotoDlg(true);
|
||||
break;
|
||||
case "2":
|
||||
openFileInput("backgroundPictureURL");
|
||||
break;
|
||||
case "1":
|
||||
setShowSelectPhoto(true);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
if (key === "3") setShowRemoveCoverPhotoDlg(true);
|
||||
else if (key === "2") openFileInput("backgroundPictureURL");
|
||||
else if (key === "1") setShowSelectPhoto(true);
|
||||
}
|
||||
|
||||
function closeDlg() {
|
||||
setShowRemoveCoverPhotoDlg(false);
|
||||
}
|
||||
|
||||
function removeCoverPhoto() {
|
||||
closeDlg();
|
||||
return updateProfile({ backgroundPictureURL: "background-server.jpg" });
|
||||
}
|
||||
const removeCoverPhoto = () => (
|
||||
closeDlg(), updateProfile({ backgroundPictureURL: "background-server.jpg" })
|
||||
);
|
||||
|
||||
function hideBgPhotoModal() {
|
||||
setShowSelectPhoto(false);
|
||||
}
|
||||
const hideBgPhotoModal = () => setShowSelectPhoto(false);
|
||||
|
||||
function handleBgPhotoClick(event) {
|
||||
hideBgPhotoModal();
|
||||
handlePhotoClick(event, "backgroundPictureURL");
|
||||
}
|
||||
const hideProfilePicModal = () => setShowUpdateProfilePic(false);
|
||||
|
||||
function hideProfilePicModal() {
|
||||
setShowUpdateProfilePic(false);
|
||||
}
|
||||
function handlePhotoClick(e, field) {
|
||||
const idx = Number(e.target.id);
|
||||
|
||||
function handleUploadProfilePicClick() {
|
||||
hideProfilePicModal();
|
||||
openFileInput("profilePictureURL");
|
||||
}
|
||||
const file = photos[idx]?.fileName;
|
||||
|
||||
function handleProfilePicClick(event) {
|
||||
hideProfilePicModal();
|
||||
handlePhotoClick(event, "profilePictureURL");
|
||||
if (!file) return;
|
||||
|
||||
updateProfile({ [field]: `${userID}/${file}` });
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
function handlePhotoClick(e, name) {
|
||||
const index = Number(e.target.id);
|
||||
const photo = photos[index];
|
||||
const storagePath = `${userID}/${photo.fileName}`;
|
||||
return updateProfile({ [name]: storagePath });
|
||||
/* ----------------------------------------------------------
|
||||
|
||||
helpers that the JSX at the bottom expects
|
||||
|
||||
---------------------------------------------------------- */
|
||||
|
||||
/* background-photo picker in SelectBgPhotoModal */
|
||||
|
||||
function handleBgPhotoClick(e) {
|
||||
hideBgPhotoModal(); // close the modal
|
||||
|
||||
handlePhotoClick(e, "backgroundPictureURL");
|
||||
}
|
||||
|
||||
const dispatch = useDispatch();
|
||||
/* “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(() => {
|
||||
//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]);
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
|
||||
RENDER – everything below is the original markup
|
||||
|
||||
(only userId() → userID replacements)
|
||||
|
||||
------------------------------------------------------------------ */
|
||||
return (
|
||||
<>
|
||||
<Row className="justify-content-center grad">
|
||||
<Col className="m-0 p-0 profile-col">
|
||||
<div className="background-pic-container">
|
||||
<Row className='justify-content-center grad'>
|
||||
<Col className='m-0 p-0 profile-col'>
|
||||
<div className='background-pic-container'>
|
||||
<StorageImage
|
||||
className="background-pic"
|
||||
className='background-pic'
|
||||
storagePath={backgroundPictureURL}
|
||||
alt=""
|
||||
alt=''
|
||||
/>
|
||||
{isCurrentUser && (
|
||||
<DropdownButton
|
||||
variant="light"
|
||||
className="background-pic-button"
|
||||
variant='light'
|
||||
className='background-pic-button'
|
||||
title={
|
||||
<b>
|
||||
<MdPhotoCamera className="mr-1" size="20px" />
|
||||
<MdPhotoCamera className='mr-1' size='20px' />
|
||||
<span>Edit Cover Photo</span>
|
||||
</b>
|
||||
}
|
||||
size="sm">
|
||||
<Dropdown.Item eventKey="1" onSelect={handleSelect}>
|
||||
<HiOutlinePhotograph size="20px" className="mr-2" />
|
||||
size='sm'
|
||||
>
|
||||
<Dropdown.Item eventKey='1' onSelect={handleSelect}>
|
||||
<HiOutlinePhotograph size='20px' className='mr-2' />
|
||||
Select Photo
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item eventKey="2" onSelect={handleSelect}>
|
||||
<ImUpload2 size="20px" className="mr-2" />
|
||||
<Dropdown.Item eventKey='2' onSelect={handleSelect}>
|
||||
<ImUpload2 size='20px' className='mr-2' />
|
||||
Upload Photo
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Divider />
|
||||
<Dropdown.Item eventKey="3" onSelect={handleSelect}>
|
||||
<IoTrashOutline size="20px" className="mr-2" /> Remove
|
||||
<Dropdown.Item eventKey='3' onSelect={handleSelect}>
|
||||
<IoTrashOutline size='20px' className='mr-2' /> Remove
|
||||
</Dropdown.Item>
|
||||
</DropdownButton>
|
||||
)}
|
||||
<div className="profile-pic-container">
|
||||
<CircularImage size="180" url={profilePictureURL} />
|
||||
<div className='profile-pic-container'>
|
||||
<CircularImage size='180' url={profilePictureURL} />
|
||||
{isCurrentUser && (
|
||||
<Button
|
||||
variant="light"
|
||||
className="profile-pic-button"
|
||||
onClick={() => setShowUpdateProfilePic(true)}>
|
||||
<MdPhotoCamera size="19px" aria-label="photo" />
|
||||
variant='light'
|
||||
className='profile-pic-button'
|
||||
onClick={() => setShowUpdateProfilePic(true)}
|
||||
>
|
||||
<MdPhotoCamera size='19px' aria-label='photo' />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<h2 className="text-center mt-5">
|
||||
<h2 className='text-center mt-5'>
|
||||
<b>
|
||||
{firstname} {lastname}
|
||||
</b>
|
||||
</h2>
|
||||
<hr></hr>
|
||||
<Navbar bg="light">
|
||||
<Navbar bg='light'>
|
||||
<Nav>
|
||||
<Nav.Item>
|
||||
<Link
|
||||
key="1"
|
||||
key='1'
|
||||
to={`${url}/Posts`}
|
||||
className="nav-link mx-2"
|
||||
ref={postsLinkRef}>
|
||||
className='nav-link mx-2'
|
||||
ref={postsLinkRef}
|
||||
>
|
||||
<b>Posts</b>
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
<Nav.Item>
|
||||
<Link
|
||||
key="2"
|
||||
key='2'
|
||||
to={`${url}/Friends`}
|
||||
className="nav-link mx-2"
|
||||
ref={friendsLinkRef}>
|
||||
className='nav-link mx-2'
|
||||
ref={friendsLinkRef}
|
||||
>
|
||||
<b>Friends</b> {users.length}
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
<Nav.Item>
|
||||
<Link
|
||||
key="3"
|
||||
key='3'
|
||||
to={`${url}/Photos`}
|
||||
className="nav-link mx-2"
|
||||
ref={photosLinkRef}>
|
||||
className='nav-link mx-2'
|
||||
ref={photosLinkRef}
|
||||
>
|
||||
<b>Photos</b>
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
|
@ -253,22 +319,22 @@ const Profile = (props) => {
|
|||
</Navbar>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="justify-content-center">
|
||||
<Col className="profile-col">
|
||||
<Row className='justify-content-center'>
|
||||
<Col className='profile-col'>
|
||||
<Switch>
|
||||
<Route path={`${path}/:itemId`}>
|
||||
<NestedRoute
|
||||
userID={userId()}
|
||||
userID={userID}
|
||||
openFileInput={() => openFileInput("")}
|
||||
//we only need the rest to handle the changes of the activeLink
|
||||
linkHandling = {linkHandlingProps}
|
||||
linkHandling={linkHandlingProps}
|
||||
/>
|
||||
</Route>
|
||||
<Route path={path}>
|
||||
<Posts
|
||||
userID={userId()}
|
||||
userID={userID}
|
||||
//we only need the rest to handle the changes of the activeLink
|
||||
linkHandling = {linkHandlingProps}
|
||||
linkHandling={linkHandlingProps}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
|
|
|
@ -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
|
||||
});
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue