Skip to content

Commit

Permalink
✨Finished reply feature on comment
Browse files Browse the repository at this point in the history
  • Loading branch information
jgudo committed Mar 20, 2021
1 parent b3cf489 commit 1bc3210
Show file tree
Hide file tree
Showing 20 changed files with 327 additions and 140 deletions.
2 changes: 1 addition & 1 deletion frontend/.eslintcache

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion frontend/src/components/main/Comments/CommentInput.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { forwardRef } from "react";
import React, { forwardRef, MutableRefObject, useEffect } from "react";
import { Avatar } from "~/components/shared";

interface IProps {
Expand All @@ -15,6 +15,10 @@ interface IProps {
const CommentInput = forwardRef<HTMLInputElement, IProps>((props, ref) => {
const { isUpdateMode, isSubmitting, userPicture, isLoading, ...rest } = props;

useEffect(() => {
ref && (ref as MutableRefObject<HTMLInputElement>).current.focus();
}, [ref])

return (
<div className={`flex items-center w-full`}>
{!isUpdateMode && <Avatar url={userPicture?.url} className="mr-2 flex-shrink-0" size="sm" />}
Expand Down
34 changes: 28 additions & 6 deletions frontend/src/components/main/Comments/CommentItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useEffect, useRef, useState } from "react";
import { Link } from "react-router-dom";
import { Avatar } from "~/components/shared";
import { useDidMount } from "~/hooks";
import { getCommentReplies, replyOnComment, updateComment } from "~/services/api";
import { getCommentReplies, likeComment, replyOnComment, updateComment } from "~/services/api";
import { IComment, IError } from "~/types/types";
import { CommentOptions } from "../Options";
import CommentInput from "./CommentInput";
Expand All @@ -28,6 +28,7 @@ const CommentItem: React.FC<IProps> = (props) => {
const [newCommentBody, setNewCommentBody] = useState('');
const [isGettingReplies, setGettingReplies] = useState(false);
const [isSubmitting, setSubmitting] = useState(false);
const [isLiking, setIsLiking] = useState(false);
const [isUpdateMode, setUpdateMode] = useState(false);
const [error, setError] = useState<IError | null>(null);
const didMount = useDidMount(true);
Expand Down Expand Up @@ -122,13 +123,28 @@ const CommentItem: React.FC<IProps> = (props) => {
};

const onClickEdit = () => {
editCommentInputRef.current && editCommentInputRef.current.focus();
// props.setInputCommentVisible(true);
setUpdateMode(true);
setEditCommentBody(comment.body);
setOpenInput(false);
}

const onClickLike = async () => {
if (isLiking) return;

try {
setIsLiking(true);
const { state, likesCount } = await likeComment(comment.id);

if (didMount) {
setIsLiking(false);
setComment({ ...comment, isLiked: state, likesCount });
}
} catch (err) {
didMount && setIsLiking(false);
console.log(err);
}
}

const updateCommentCallback = (comment: IComment) => {
if (didMount) {
setReplies(oldComments => oldComments.filter((cmt) => cmt.id !== comment.id));
Expand Down Expand Up @@ -178,9 +194,15 @@ const CommentItem: React.FC<IProps> = (props) => {
{/* ---- DATE AND LIKE BUTTON ----- */}
<div className="mt-1 flex items-center space-x-2">
{/* ---- LIKE BUTTON ---- */}
<span className="text-gray-400 hover:cursor-pointer hover:text-gray-800 dark:hover:text-gray-200 text-xs">
Like
</span>
{comment.likesCount > 0 && (
<span className="text-sm text-gray-500">{comment.likesCount}</span>
)}
<span
className={`text-gray-400 hover:cursor-pointer hover:text-gray-800 dark:hover:text-gray-200 text-xs ${comment.isLiked && 'font-bold text-indigo-500 dark:text-indigo-300'} ${isLiking && 'opacity-50 hover:cursor-default'}`}
onClick={onClickLike}
>
{comment.isLiked ? 'Unlike' : 'Like'}
</span>
{/* ---- REPLY BUTTON */}
{comment.depth < 3 && (
<span
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ const NotificationList: React.FC<IProps> = (props) => {
{
notif.type === 'like' ? 'likes your post.'
: notif.type === 'comment' ? 'commented on your post.'
: notif.type === 'follow' ? 'started following you.'
: notif.type === 'reply' ? 'replied to your comment'
: ''
: notif.type === 'comment-like' ? 'likes your comment.'
: notif.type === 'follow' ? 'started following you.'
: notif.type === 'reply' ? 'replied to your comment'
: ''
}
</span>
<br />
Expand All @@ -71,7 +72,7 @@ const NotificationList: React.FC<IProps> = (props) => {
</span>
</div>
</div>
{notif.type === 'like' ? (
{(notif.type === 'like' || notif.type === 'comment-like') ? (
<LikeOutlined className="text-2xl text-indigo-700 dark:text-indigo-400 absolute right-4 top-0 bottom-0 my-auto" />
) : (notif.type === 'comment' || notif.type === 'reply') ? (
<CommentOutlined className="text-2xl text-indigo-700 dark:text-indigo-400 absolute right-4 top-0 bottom-0 my-auto" />
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/components/main/PostItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ const PostItem: React.FC<IProps> = (props) => {
}
}

const handleClickPrivacyChange = () => {
if (post.isOwnPost) {
dispatch(setTargetPost(post));
dispatch(showModal(EModalType.EDIT_POST));
}
}

return (
<div className="flex flex-col bg-white rounded-lg my-4 p-4 first:mt-0 shadow-lg dark:bg-indigo-1000">
{/* --- AVATAR AND OPTIONS */}
Expand All @@ -81,7 +88,7 @@ const PostItem: React.FC<IProps> = (props) => {
<span className="text-sm text-gray-500">{dayjs(post.createdAt).fromNow()}</span>
<div
className={`w-4 h-4 rounded-full flex items-center justify-center ${post.isOwnPost && 'cursor-pointer hover:bg-gray-100 dark:hover:bg-indigo-900'}`}
onClick={() => post.isOwnPost && updateModal.openModal()}
onClick={handleClickPrivacyChange}
title={post.isOwnPost ? 'Change Privacy' : ''}
>
{post.privacy === 'private'
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/pages/home/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CoffeeOutlined, UndoOutlined } from "@ant-design/icons";
import { useEffect } from "react";
import useInfiniteScroll from 'react-infinite-scroll-hook';
import { useDispatch, useSelector } from "react-redux";
import { shallowEqual, useDispatch, useSelector } from "react-redux";
import { Link, RouteComponentProps } from "react-router-dom";
import { CSSTransition, TransitionGroup } from "react-transition-group";
import { withAuth } from "~/components/hoc";
Expand Down Expand Up @@ -29,7 +29,7 @@ const Home: React.FC<IProps> = (props) => {
error: state.error.newsFeedError,
isLoadingFeed: state.loading.isLoadingFeed,
isLoadingCreatePost: state.loading.isLoadingCreatePost
}));
}), shallowEqual);
const dispatch = useDispatch();
const { isOpen, openModal, closeModal } = useModal();
const from = props.location.state?.from || null;
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/pages/post/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ const Post: React.FC<RouteComponentProps<{ post_id: string; }>> = ({ history, ma
} as IPost);
}

const updateSuccessCallback = (post: IPost) => {
setPost(post);
const updateSuccessCallback = (updatedPost: IPost) => {
setPost({ ...post, ...updatedPost });
}

const deleteSuccessCallback = () => {
Expand Down
24 changes: 10 additions & 14 deletions frontend/src/pages/profile/Tabs/Posts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,6 @@ const Posts: React.FC<IProps> = (props) => {
};

const updateSuccessCallback = (post: IPost) => {
updatePostState(post);
}

const deleteSuccessCallback = (postID: string) => {
// eslint-disable-next-line array-callback-return
const filteredPosts = posts.filter((item) => {
if (item.id !== postID) {
return item;
}
});
setPosts(filteredPosts);
}

const updatePostState = (post: IPost) => {
const updatedPosts = posts.map((item) => {
if (item.id === post.id) {
return {
Expand All @@ -73,6 +59,16 @@ const Posts: React.FC<IProps> = (props) => {
setPosts(updatedPosts);
}

const deleteSuccessCallback = (postID: string) => {
// eslint-disable-next-line array-callback-return
const filteredPosts = posts.filter((item) => {
if (item.id !== postID) {
return item;
}
});
setPosts(filteredPosts);
}

const fetchPosts = async () => {
try {
setIsLoading(true);
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/redux/reducer/newsFeedReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ const newsFeedReducer = (state = initState, action: TNewsFeedActionType) => {
...state,
items: state.items.map((post: IPost) => {
if (post.id === action.payload.id) {
return action.payload;
return {
...post,
...action.payload
};
}
return post;
})
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,19 @@ export const updateComment = async (commentID: string, body: string): Promise<an
}
}

export const likeComment = async (commentID: string): Promise<any> => {
try {
const req = await axios({
method: 'POST',
url: `/like/comment/${commentID}`
});

return Promise.resolve(req.data.data)
} catch (e) {
return Promise.reject(e?.response?.data || {});
}
}

export const getNotifications = async (params: IFetchParams): Promise<any> => {
try {
const req = await axios({
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,11 @@ export interface IComment {
isEdited: boolean;
depth: number;
replyCount: number;
likesCount: number;
post_id: string;
isOwnComment: boolean;
isPostOwner: boolean;

isLiked: boolean;
author: IUser;
}

Expand Down
Loading

0 comments on commit 1bc3210

Please sign in to comment.