Skip to content
This repository has been archived by the owner on Sep 29, 2024. It is now read-only.

Commit

Permalink
Merge pull request #952 from SE-TINF22B6/search_and_user_page
Browse files Browse the repository at this point in the history
Finish user Page and few little fixes
  • Loading branch information
Nahlam4 authored Jun 12, 2024
2 parents f72752d + 5ab706a commit 5f2b404
Show file tree
Hide file tree
Showing 7 changed files with 597 additions and 67 deletions.
10 changes: 5 additions & 5 deletions src/main/web/src/scenes/Friends/UserFriend.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.user-container {
.friend-user-container {
font-size: var(--headline-font-size);
align-items: center;
text-align: center;
Expand All @@ -10,24 +10,24 @@
color: white;
}

.user-image {
.friend-user-image {
width: 100px;
height: 100px;
border-radius: 50px;
object-fit: cover;
}

.custom-image {
.friend-custom-image {
width: 100px;
height: 100px;
border-radius: 50px;
object-fit: cover;
}

.user-button {
.friend-user-button {
text-decoration: none;
color: inherit;
}
.user-name{
.friend-user-name{
padding-top: 10px;
}
8 changes: 4 additions & 4 deletions src/main/web/src/scenes/Friends/UserFriend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ export const UserFriend: React.FC<UserFriendModel> = (props: UserFriendModel) =>
const truncatedUsername = username.length > 17 ? username.slice(0, 15) + "..." : username;

return (
<div className="user-container">
<div className="friend-user-container">
<Link to={`/user/?id=${userId}`} aria-label="To the user" className="user-button">
{image ? (
<img className="user-image" alt={username} src={image} loading="lazy"/>
<img className="friend-user-image" alt={username} src={image} loading="lazy"/>
) : (
<img className="custom-image" alt={"random"} src={getDefaultOrRandomPicture(false)}/>
<img className="friend-custom-image" alt={"random"} src={getDefaultOrRandomPicture(false)}/>
)}
<div className="user-name">{truncatedUsername}</div>
<div className="friend-user-name">{truncatedUsername}</div>
</Link>
</div>
);
Expand Down
11 changes: 7 additions & 4 deletions src/main/web/src/scenes/Search/components/SearchService.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import {Post} from "../../Home/components/post/Post";
import {UserModel} from "./models/UserModel";




interface SearchedPostsProps {
sortOption: string;
query: string | null;
Expand Down Expand Up @@ -45,8 +43,13 @@ export const SearchService: React.FC<SearchedPostsProps> = ({sortOption, query,
});
if (response.ok) {
const data = await response.json();
setSearchResults(data);
setNotFound(false);
if (data.length === 0) {
setNotFound(true);
} else {
setSearchResults(data);
setNotFound(false);
}

} else {
setNotFound(true);
}
Expand Down
258 changes: 258 additions & 0 deletions src/main/web/src/scenes/User/UserPost.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
import React, {useCallback, useEffect, useRef, useState} from 'react';
import '../../scenes/Home/components/post/Post.css';
import {Share} from "../../organisms/share/Share";
import {Link, useLocation} from "react-router-dom";
import {Report} from "../../organisms/report/Report";
import {PostMenu} from "../../organisms/post-menu/PostMenu";
import {Tag} from "../../atoms/Tag";
import {handleLike} from '../../services/LikeService';
import {timeDifference} from "../../services/TimeService";
import {PostModel} from "../Home/components/post/models/PostModel";
import {Interaction} from "../../organisms/interaction/Interaction";
import {shortenDescription} from "../../services/DescriptionService";
import {useMediaQuery} from "@mui/system";
import config from "../../config/config";
import {getJWT, getUserId} from "../../services/AuthService";
import {sendReportToBackend} from "../../services/ReportService";


export const UserPost: React.FC<PostModel> = (props: PostModel) => {
const {
id,
title,
description,
tags,
likeAmount,
commentAmount,
timestamp,
postImage,
accountId,
username
} = props;

const matches: boolean = useMediaQuery('(max-width: 412px)')
const formattedTime: string = timeDifference(new Date(timestamp));
const [comments] = useState(commentAmount);
const [menuOpen, setMenuOpen] = useState(false);
const [shareWindowOpen, setShareWindowOpen] = useState(false);
const currentPageURL: string = window.location.href;
const location = useLocation();
const [shortDescription, setShortDescription] = useState('');
const searchParams: URLSearchParams = new URLSearchParams(window.location.search);
const userSelfid: string | null = searchParams.get('id');
const userSelfId: number | null = getUserId();
const jwt: string | null = getJWT();
const headersWithJwt = {
...config.headers,
'Authorization': jwt ? `Bearer ${jwt}` : ''
};

const [reportOpen, setReportOpen] = useState(false);
const [reportReason, setReportReason] = useState('');
const [reportDescription, setReportDescription] = useState('');

const handleReportClick = (): void => {
setReportOpen(!reportOpen);
};

const handleReportSubmit = (): void => {
sendReportToBackend(reportReason, reportDescription, id, accountId, "post");
setReportOpen(!reportOpen);
setReportReason('');
setReportDescription('');
};

const [likes, setLikes] = useState(likeAmount);
const [userLiked, setUserLiked] = useState(false);
const [heartClass, setHeartClass] = useState('heart-empty');

useEffect((): void => {
const userLikedPost: string | null = localStorage.getItem(`post_liked_${id}`);
if (userLikedPost) {
setUserLiked(true);
setHeartClass('heart-filled');
}
}, [location, id]);

useEffect((): void => {
if (description){
setShortDescription(shortenDescription(description, postImage ? 190 : matches ? 190 : 280));
}
else {
setShortDescription(description);
}
}, [description, postImage, matches]);

const handleMenuClick = (): void => {
setMenuOpen(!menuOpen);
setShareWindowOpen(false);
};

const handleShareClick = (): void => {
setShareWindowOpen(!shareWindowOpen);
};

const handleSaveClick = async (): Promise<void> => {
try {
const response: Response = await fetch(config.apiUrl + "saved-post", {
method: 'POST',
credentials: 'include',
body: JSON.stringify({
postId: id,
userId: userSelfId,
}),
headers: headersWithJwt
});
if (response.ok) {
console.log('Post has been saved!');
alert('Post has been saved!');
} else {
console.error('Error saving the post: ', response.statusText);
}
} catch (err) {
console.error('Error saving the post: ', err);
alert('Error saving the post');
}
};

const handleUnsaveClick = async (): Promise<void> => {
try {
const response: Response = await fetch(config.apiUrl + `saved-post`, {
method: 'DELETE',
credentials: 'include',
body: JSON.stringify({
postId: id,
userId: userSelfId,
}),
headers: headersWithJwt
});
if (response.ok) {
alert('Post has been unsaved!');
} else {
console.error('Error unsaving the post: ', response.statusText);
alert('Error unsaving the post');
}
} catch (err) {
console.error('Error unsaving the post: ', err);
}
};

const imageRef = useRef(null);
const [imageWidth, setImageWidth] = useState<number | null>();
const [marginLeft, setMarginLeft] = useState<string>();
const [width, setWidth] = useState<string>();
const [marginTop, setMarginTop] = useState<string>();

const handleClose = useCallback((): void => {
setReportOpen(false);
}, [setReportOpen]);

useEffect((): () => void => {
const updateMargins = (): void => {
if (matches) {
setWidth(postImage ? '240px' : '260px');
setMarginLeft(tags ? '110px' : '0');
setMarginTop(postImage ? '140px' : '5px');
} else {
setWidth(postImage ? '360px' : '600px');
setMarginLeft(postImage ? (imageWidth ? `${imageWidth + 20}px` : '280px') : '10px');
setMarginTop('0');
}
};
updateMargins();
const imageElement = imageRef.current;
const handleResize = (): void => {
if (imageElement) {
const computedStyle: CSSStyleDeclaration = window.getComputedStyle(imageElement);
const width: number = parseInt(computedStyle.getPropertyValue('width'));
setImageWidth(width);
updateMargins();
}
};
if (imageElement) {
handleResize();
window.addEventListener('resize', handleResize);
}
return (): void => {
window.removeEventListener('resize', handleResize);
};
}, [matches, postImage, imageWidth, tags]);

useEffect(() => {
const handleEsc = (event: KeyboardEvent): void => {
if (event.key === 'Escape' && reportOpen) {
handleClose();
}
};
document.addEventListener('keydown', handleEsc);
return (): void => {
document.removeEventListener('keydown', handleEsc);
};
}, [reportOpen, handleClose]);

return (
<div className="post-container">
<div className="post">
<Link to={`/post/?id=${id}`} aria-label="To the post" className="post-button">
{postImage && <img className="post-image" ref={imageRef} alt="Post" src={postImage} />}
</Link>
<img className="post-menu-points" onClick={handleMenuClick} alt="Menu dots"
src={process.env.PUBLIC_URL + '/assets/menu-dots.svg'}/>
<div className="post-infos" style={{marginLeft: marginLeft}}>
<Link to={`/post/?id=${id}`} className="post-button">
<p className="post-title">
{title ? shortenDescription(title, 50): title }</p>
</Link>
<div className="post-tags" style={{marginTop: marginTop}}>
{tags && tags.slice(0, 3).map((tag: string, index: number) => (
<Tag name={tag} key={index} index={index} isEventTag={false}/>
))}
</div>

<p className="short-description" style={{ width: width}}>
{postImage && shortDescription ? shortenDescription(shortDescription, 150) : shortDescription}
</p>

<div className="footer">
<Link to={`/user/?id=${userSelfid}`} className="author-link" aria-label="To the author">
{username}
</Link>
&nbsp;· {formattedTime}
</div>
</div>
<div className="home-post-interaction"
style={{marginTop: `${parseInt(marginTop as string) + 110}px`}}>
<Interaction
likes={likes}
userLiked={userLiked}
heartClass={heartClass}
comments={comments}
handleLike={() => handleLike(id, "post", likes, setLikes, setUserLiked, setHeartClass)}
id={id}
isHomepage={true}
/>
</div>
</div>

{menuOpen && (
<div className="post-menu-container">
<PostMenu
handleShareClick={handleShareClick}
handleSaveClick={handleSaveClick}
handleUnsaveClick={handleUnsaveClick}
handleReportClick={handleReportClick}
id={id}
/>
</div>
)}
{shareWindowOpen && (
<div className="post-share-container">
<Share postId={id} currentPageURL={currentPageURL}></Share>
</div>
)}
{reportOpen && (
<Report reportOpen={reportOpen} handleReportSubmit={handleReportSubmit} handleClose={handleClose}/>
)}
</div>
);
};
Loading

0 comments on commit 5f2b404

Please sign in to comment.