From ab15a10d0171908717d9402ac7528606c18c531a Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Sat, 6 Nov 2021 21:14:50 +0700 Subject: [PATCH] refactored node comments to component --- src/components/boris/BorisComments/index.tsx | 37 ++++++-- src/components/comment/Comment/index.tsx | 9 +- .../comment/CommentContent/index.tsx | 9 +- src/components/node/NodeAuthorBlock/index.tsx | 16 ++-- src/components/node/NodeBottomBlock/index.tsx | 23 ++++- src/components/node/NodeComments/index.tsx | 35 ++++--- .../node/NodeCommentsBlock/index.tsx | 20 +++- src/layouts/BorisLayout/index.tsx | 30 +++--- src/layouts/NodeLayout/index.tsx | 94 ++++++++++++------- src/pages/node/[id].tsx | 11 +++ src/redux/types.ts | 5 +- src/utils/hooks/node/useNodeComments.ts | 17 ++++ src/utils/hooks/useImageModal.ts | 13 +++ 13 files changed, 208 insertions(+), 111 deletions(-) create mode 100644 src/utils/hooks/node/useNodeComments.ts create mode 100644 src/utils/hooks/useImageModal.ts diff --git a/src/components/boris/BorisComments/index.tsx b/src/components/boris/BorisComments/index.tsx index b4350c44..6b15049a 100644 --- a/src/components/boris/BorisComments/index.tsx +++ b/src/components/boris/BorisComments/index.tsx @@ -5,21 +5,30 @@ import { NodeCommentForm } from '~/components/node/NodeCommentForm'; import { NodeNoComments } from '~/components/node/NodeNoComments'; import { NodeComments } from '~/components/node/NodeComments'; import { Footer } from '~/components/main/Footer'; -import { Card } from '~/components/containers/Card'; -import { useShallowSelect } from '~/utils/hooks/useShallowSelect'; -import { selectAuthUser } from '~/redux/auth/selectors'; -import { IComment, INode } from '~/redux/types'; +import { IComment, IFile, INode } from '~/redux/types'; +import { IUser } from '~/redux/auth/types'; interface IProps { - isLoadingComments: boolean; - commentCount: number; node: INode; + user: IUser; + commentCount: number; comments: IComment[]; + isLoadingComments: boolean; + onShowImageModal: (images: IFile[], index: number) => void; + onLoadMoreComments: () => void; + onDeleteComment: (id: IComment['id'], isLocked: boolean) => void; } -const BorisComments: FC = ({ isLoadingComments, node, commentCount, comments }) => { - const user = useShallowSelect(selectAuthUser); - +const BorisComments: FC = ({ + node, + user, + isLoadingComments, + commentCount, + comments, + onLoadMoreComments, + onDeleteComment, + onShowImageModal, +}) => { return ( <> @@ -28,7 +37,15 @@ const BorisComments: FC = ({ isLoadingComments, node, commentCount, comm {isLoadingComments ? ( ) : ( - + )} diff --git a/src/components/comment/Comment/index.tsx b/src/components/comment/Comment/index.tsx index cdd2efe9..50d9cd82 100644 --- a/src/components/comment/Comment/index.tsx +++ b/src/components/comment/Comment/index.tsx @@ -1,10 +1,9 @@ import React, { FC, HTMLAttributes, memo } from 'react'; import { CommentWrapper } from '~/components/containers/CommentWrapper'; -import { IComment, ICommentGroup } from '~/redux/types'; +import { IComment, ICommentGroup, IFile } from '~/redux/types'; import { CommentContent } from '~/components/comment/CommentContent'; import styles from './styles.module.scss'; import { CommendDeleted } from '../../node/CommendDeleted'; -import * as MODAL_ACTIONS from '~/redux/modal/actions'; import classNames from 'classnames'; import { NEW_COMMENT_CLASSNAME } from '~/constants/comment'; @@ -15,7 +14,7 @@ type IProps = HTMLAttributes & { isSame?: boolean; canEdit?: boolean; onDelete: (id: IComment['id'], isLocked: boolean) => void; - modalShowPhotoswipe: typeof MODAL_ACTIONS.modalShowPhotoswipe; + onShowImageModal: (images: IFile[], index: number) => void; }; const Comment: FC = memo( @@ -27,7 +26,7 @@ const Comment: FC = memo( className, canEdit, onDelete, - modalShowPhotoswipe, + onShowImageModal, ...props }) => { return ( @@ -53,7 +52,7 @@ const Comment: FC = memo( key={comment.id} can_edit={!!canEdit} onDelete={onDelete} - modalShowPhotoswipe={modalShowPhotoswipe} + onShowImageModal={onShowImageModal} /> ); })} diff --git a/src/components/comment/CommentContent/index.tsx b/src/components/comment/CommentContent/index.tsx index cccdb985..bc280fbc 100644 --- a/src/components/comment/CommentContent/index.tsx +++ b/src/components/comment/CommentContent/index.tsx @@ -8,23 +8,22 @@ import { UPLOAD_TYPES } from '~/redux/uploads/constants'; import reduce from 'ramda/es/reduce'; import { AudioPlayer } from '~/components/media/AudioPlayer'; import classnames from 'classnames'; +import classNames from 'classnames'; import { PRESETS } from '~/constants/urls'; import { COMMENT_BLOCK_RENDERERS } from '~/constants/comment'; import { CommentMenu } from '../CommentMenu'; -import * as MODAL_ACTIONS from '~/redux/modal/actions'; import { CommentForm } from '~/components/comment/CommentForm'; import { useShallowSelect } from '~/utils/hooks/useShallowSelect'; import { selectNode } from '~/redux/node/selectors'; -import classNames from 'classnames'; interface IProps { comment: IComment; can_edit: boolean; onDelete: (id: IComment['id'], isLocked: boolean) => void; - modalShowPhotoswipe: typeof MODAL_ACTIONS.modalShowPhotoswipe; + onShowImageModal: (images: IFile[], index: number) => void; } -const CommentContent: FC = memo(({ comment, can_edit, onDelete, modalShowPhotoswipe }) => { +const CommentContent: FC = memo(({ comment, can_edit, onDelete, onShowImageModal }) => { const [isEditing, setIsEditing] = useState(false); const { current } = useShallowSelect(selectNode); @@ -89,7 +88,7 @@ const CommentContent: FC = memo(({ comment, can_edit, onDelete, modalSho className={classNames(styles.images, { [styles.multiple]: groupped.image.length > 1 })} > {groupped.image.map((file, index) => ( -
modalShowPhotoswipe(groupped.image, index)}> +
onShowImageModal(groupped.image, index)}> {file.name}
))} diff --git a/src/components/node/NodeAuthorBlock/index.tsx b/src/components/node/NodeAuthorBlock/index.tsx index 9ea9b3d0..d1b1b998 100644 --- a/src/components/node/NodeAuthorBlock/index.tsx +++ b/src/components/node/NodeAuthorBlock/index.tsx @@ -1,26 +1,24 @@ import React, { FC, useCallback } from 'react'; -import { INode } from '~/redux/types'; import styles from './styles.module.scss'; import { Avatar } from '~/components/common/Avatar'; import { openUserProfile } from '~/utils/user'; import { useUserDescription } from '~/utils/hooks/user/useUserDescription'; +import { INodeUser } from '~/redux/types'; interface Props { - node?: INode; + user?: INodeUser; } -const NodeAuthorBlock: FC = ({ node }) => { - const onOpenProfile = useCallback(() => openUserProfile(node?.user?.username), [ - node?.user?.username, - ]); +const NodeAuthorBlock: FC = ({ user }) => { + const onOpenProfile = useCallback(() => openUserProfile(user?.username), [user?.username]); - const description = useUserDescription(node?.user); + const description = useUserDescription(user); - if (!node?.user) { + if (!user) { return null; } - const { fullname, username, photo } = node.user; + const { fullname, username, photo } = user; return (
diff --git a/src/components/node/NodeBottomBlock/index.tsx b/src/components/node/NodeBottomBlock/index.tsx index 314c9d22..28087c8f 100644 --- a/src/components/node/NodeBottomBlock/index.tsx +++ b/src/components/node/NodeBottomBlock/index.tsx @@ -6,16 +6,18 @@ import { NodeCommentsBlock } from '~/components/node/NodeCommentsBlock'; import { NodeCommentForm } from '~/components/node/NodeCommentForm'; import { NodeRelatedBlock } from '~/components/node/NodeRelatedBlock'; import { useNodeBlocks } from '~/utils/hooks/node/useNodeBlocks'; -import { IComment, INode } from '~/redux/types'; -import { useUser } from '~/utils/hooks/user/userUser'; +import { IComment, IFile, INode } from '~/redux/types'; import { NodeTagsBlock } from '~/components/node/NodeTagsBlock'; import { INodeRelated } from '~/redux/node/types'; import StickyBox from 'react-sticky-box/dist/esnext'; import styles from './styles.module.scss'; import { NodeAuthorBlock } from '~/components/node/NodeAuthorBlock'; +import { IUser } from '~/redux/auth/types'; interface IProps { node: INode; + user: IUser; + isUser: boolean; canEdit: boolean; isLoading: boolean; commentsOrder: 'ASC' | 'DESC'; @@ -24,21 +26,28 @@ interface IProps { isLoadingComments: boolean; related: INodeRelated; lastSeenCurrent?: string; + onShowImageModal: (images: IFile[], index: number) => void; + onLoadMoreComments: () => void; + onDeleteComment: (id: IComment['id'], isLocked: boolean) => void; } const NodeBottomBlock: FC = ({ node, + user, canEdit, isLoading, + isUser, isLoadingComments, comments, commentsCount, commentsOrder, related, lastSeenCurrent, + onLoadMoreComments, + onDeleteComment, + onShowImageModal, }) => { const { inline } = useNodeBlocks(node, isLoading); - const { is_user } = useUser(); if (node.deleted_at) { return ; @@ -58,17 +67,21 @@ const NodeBottomBlock: FC = ({ comments={comments} count={commentsCount} order={commentsOrder} + user={user} node={node} + onShowImageModal={onShowImageModal} + onLoadMoreComments={onLoadMoreComments} + onDeleteComment={onDeleteComment} /> - {is_user && !isLoading && } + {isUser && !isLoading && }
- +
diff --git a/src/components/node/NodeComments/index.tsx b/src/components/node/NodeComments/index.tsx index 446d32ab..a01b29b7 100644 --- a/src/components/node/NodeComments/index.tsx +++ b/src/components/node/NodeComments/index.tsx @@ -1,17 +1,13 @@ -import React, { FC, memo, useCallback, useMemo } from 'react'; +import React, { FC, memo, useMemo } from 'react'; import { Comment } from '../../comment/Comment'; import styles from './styles.module.scss'; import { IComment, ICommentGroup, IFile } from '~/redux/types'; -import { groupCommentsByUser } from '~/utils/fn'; import { IUser } from '~/redux/auth/types'; import { canEditComment } from '~/utils/node'; -import { nodeLoadMoreComments, nodeLockComment } from '~/redux/node/actions'; import { INodeState } from '~/redux/node/reducer'; import { COMMENTS_DISPLAY } from '~/redux/node/constants'; import { plural } from '~/utils/dom'; -import { modalShowPhotoswipe } from '~/redux/modal/actions'; -import { useDispatch } from 'react-redux'; import { useGrouppedComments } from '~/utils/hooks/node/useGrouppedComments'; interface IProps { @@ -20,25 +16,26 @@ interface IProps { user: IUser; order?: 'ASC' | 'DESC'; lastSeenCurrent?: string; + onShowImageModal: (images: IFile[], index: number) => void; + onLoadMoreComments: () => void; + onDeleteComment: (id: IComment['id'], isLocked: boolean) => void; } const NodeComments: FC = memo( - ({ comments, user, count = 0, order = 'DESC', lastSeenCurrent }) => { - const dispatch = useDispatch(); + ({ + comments, + user, + count = 0, + order = 'DESC', + onLoadMoreComments, + onDeleteComment, + onShowImageModal, + lastSeenCurrent, + }) => { const left = useMemo(() => Math.max(0, count - comments.length), [comments, count]); const groupped: ICommentGroup[] = useGrouppedComments(comments, order, lastSeenCurrent); - const onDelete = useCallback( - (id: IComment['id'], locked: boolean) => dispatch(nodeLockComment(id, locked)), - [dispatch] - ); - const onLoadMoreComments = useCallback(() => dispatch(nodeLoadMoreComments()), [dispatch]); - const onShowPhotoswipe = useCallback( - (images: IFile[], index: number) => dispatch(modalShowPhotoswipe(images, index)), - [dispatch] - ); - const more = useMemo( () => left > 0 && ( @@ -60,8 +57,8 @@ const NodeComments: FC = memo( key={group.ids.join()} group={group} canEdit={canEditComment(group, user)} - onDelete={onDelete} - modalShowPhotoswipe={onShowPhotoswipe} + onDelete={onDeleteComment} + onShowImageModal={onShowImageModal} isSame={group.user.id === user.id} /> ))} diff --git a/src/components/node/NodeCommentsBlock/index.tsx b/src/components/node/NodeCommentsBlock/index.tsx index d23c017e..b2c7dff5 100644 --- a/src/components/node/NodeCommentsBlock/index.tsx +++ b/src/components/node/NodeCommentsBlock/index.tsx @@ -1,29 +1,36 @@ import React, { FC } from 'react'; import { NodeNoComments } from '~/components/node/NodeNoComments'; import { NodeComments } from '~/components/node/NodeComments'; -import { IComment, INode } from '~/redux/types'; +import { IComment, IFile, INode } from '~/redux/types'; import { useNodeBlocks } from '~/utils/hooks/node/useNodeBlocks'; -import { useUser } from '~/utils/hooks/user/userUser'; +import { IUser } from '~/redux/auth/types'; interface IProps { order: 'ASC' | 'DESC'; node: INode; + user: IUser; comments: IComment[]; count: number; lastSeenCurrent?: string; isLoading: boolean; isLoadingComments: boolean; + onShowImageModal: (images: IFile[], index: number) => void; + onLoadMoreComments: () => void; + onDeleteComment: (id: IComment['id'], isLocked: boolean) => void; } const NodeCommentsBlock: FC = ({ - isLoading, - isLoadingComments, node, + user, comments, count, lastSeenCurrent, + isLoading, + isLoadingComments, + onLoadMoreComments, + onDeleteComment, + onShowImageModal, }) => { - const user = useUser(); const { inline } = useNodeBlocks(node, isLoading); return isLoading || isLoadingComments || (!comments.length && !inline) ? ( @@ -35,6 +42,9 @@ const NodeCommentsBlock: FC = ({ user={user} order="DESC" lastSeenCurrent={lastSeenCurrent} + onShowImageModal={onShowImageModal} + onLoadMoreComments={onLoadMoreComments} + onDeleteComment={onDeleteComment} /> ); }; diff --git a/src/layouts/BorisLayout/index.tsx b/src/layouts/BorisLayout/index.tsx index 138edca5..1461c513 100644 --- a/src/layouts/BorisLayout/index.tsx +++ b/src/layouts/BorisLayout/index.tsx @@ -1,13 +1,12 @@ import React, { FC, useCallback, useEffect } from 'react'; import { selectNode, selectNodeComments } from '~/redux/node/selectors'; -import { selectAuthIsTester, selectUser } from '~/redux/auth/selectors'; +import { selectAuthIsTester } from '~/redux/auth/selectors'; import { useDispatch } from 'react-redux'; import styles from './styles.module.scss'; import { Group } from '~/components/containers/Group'; import boris from '~/sprites/boris_robot.svg'; import { useRandomPhrase } from '~/constants/phrases'; import isBefore from 'date-fns/isBefore'; -import { BorisStats } from '~/components/boris/BorisStats'; import { useShallowSelect } from '~/utils/hooks/useShallowSelect'; import { selectBorisStats } from '~/redux/boris/selectors'; import { authSetState, authSetUser } from '~/redux/auth/actions'; @@ -16,20 +15,12 @@ import { borisLoadStats } from '~/redux/boris/actions'; import { Container } from '~/containers/main/Container'; import StickyBox from 'react-sticky-box/dist/esnext'; import { BorisComments } from '~/components/boris/BorisComments'; -import { URLS } from '~/constants/urls'; -import { Route, Switch } from 'react-router-dom'; -import { BorisUIDemo } from '~/components/boris/BorisUIDemo'; -import { BorisSuperpowers } from '~/components/boris/BorisSuperpowers'; -import { Superpower } from '~/components/boris/Superpower'; -import { Tabs } from '~/components/dialogs/Tabs'; -import { Tab } from '~/components/dialogs/Tab'; -import { useHistory, useLocation } from 'react-router'; import { Card } from '~/components/containers/Card'; import { SidebarRouter } from '~/containers/main/SidebarRouter'; -import { BorisContactItem } from '~/components/boris/BorisContactItem'; -import { BorisContacts } from '~/components/boris/BorisContacts'; import { BorisSidebar } from '~/components/boris/BorisSidebar'; -import { LoaderCircle } from '~/components/input/LoaderCircle'; +import { useImageModal } from '~/utils/hooks/useImageModal'; +import { useNodeComments } from '~/utils/hooks/node/useNodeComments'; +import { useUser } from '~/utils/hooks/user/userUser'; type IProps = {}; @@ -37,10 +28,10 @@ const BorisLayout: FC = () => { const title = useRandomPhrase('BORIS_TITLE'); const dispatch = useDispatch(); const node = useShallowSelect(selectNode); - const user = useShallowSelect(selectUser); const stats = useShallowSelect(selectBorisStats); const comments = useShallowSelect(selectNodeComments); const isTester = useShallowSelect(selectAuthIsTester); + const user = useUser(); useEffect(() => { const last_comment = comments[0]; @@ -73,6 +64,9 @@ const BorisLayout: FC = () => { [dispatch] ); + const onShowImageModal = useImageModal(); + const { onLoadMoreComments, onDelete: onDeleteComment } = useNodeComments('696'); + return (
@@ -89,10 +83,14 @@ const BorisLayout: FC = () => {
diff --git a/src/layouts/NodeLayout/index.tsx b/src/layouts/NodeLayout/index.tsx index 1bea63b4..5c0514ae 100644 --- a/src/layouts/NodeLayout/index.tsx +++ b/src/layouts/NodeLayout/index.tsx @@ -1,4 +1,4 @@ -import React, { FC, memo } from 'react'; +import React, { FC } from 'react'; import { Route } from 'react-router'; import { Card } from '~/components/containers/Card'; @@ -13,62 +13,84 @@ import { useNodeCoverImage } from '~/utils/hooks/node/useNodeCoverImage'; import { URLS } from '~/constants/urls'; import { EditorEditDialog } from '~/containers/dialogs/EditorEditDialog'; import { useNodePermissions } from '~/utils/hooks/node/useNodePermissions'; -import { IComment, INode } from '~/redux/types'; +import { IComment, IFile, INode } from '~/redux/types'; import { INodeRelated } from '~/redux/node/types'; import styles from './styles.module.scss'; +import { IUser } from '~/redux/auth/types'; type IProps = { node: INode; + user: IUser; lastSeenCurrent?: string; related: INodeRelated; comments: IComment[]; commentsCount: number; + isUser: boolean; isLoading: boolean; isLoadingComments: boolean; + onShowImageModal: (images: IFile[], index: number) => void; + onLoadMoreComments: () => void; + onDeleteComment: (id: IComment['id'], isLocked: boolean) => void; }; -const NodeLayout: FC = memo( - ({ node, comments, commentsCount, related, lastSeenCurrent, isLoading, isLoadingComments }) => { - useNodeCoverImage(node); +const NodeLayout: FC = ({ + node, + user, + comments, + commentsCount, + related, + lastSeenCurrent, + isUser, + isLoading, + isLoadingComments, + onLoadMoreComments, + onDeleteComment, + onShowImageModal, +}) => { + useNodeCoverImage(node); - const { head, block } = useNodeBlocks(node, isLoading); - const [canEdit] = useNodePermissions(node); + const { head, block } = useNodeBlocks(node, isLoading); + const [canEdit] = useNodePermissions(node); - return ( -
- {head} + return ( +
+ {head} - - - {block} + + + {block} -
- -
+
+ +
- + -
- - +
+ + - + - -
- ); - } -); + +
+ ); +}; export { NodeLayout }; diff --git a/src/pages/node/[id].tsx b/src/pages/node/[id].tsx index e612707d..58d87da6 100644 --- a/src/pages/node/[id].tsx +++ b/src/pages/node/[id].tsx @@ -3,6 +3,9 @@ import { NodeLayout } from '~/layouts/NodeLayout'; import { RouteComponentProps } from 'react-router'; import { useScrollToTop } from '~/utils/hooks/useScrollToTop'; import { useFullNode } from '~/utils/hooks/node/useFullNode'; +import { useImageModal } from '~/utils/hooks/useImageModal'; +import { useNodeComments } from '~/utils/hooks/node/useNodeComments'; +import { useUser } from '~/utils/hooks/user/userUser'; type Props = RouteComponentProps<{ id: string }> & {}; @@ -21,17 +24,25 @@ const NodePage: FC = ({ lastSeenCurrent, } = useFullNode(id); + const onShowImageModal = useImageModal(); + const { onLoadMoreComments, onDelete: onDeleteComment } = useNodeComments(id); + const user = useUser(); useScrollToTop([id, isLoadingComments]); return ( ); }; diff --git a/src/redux/types.ts b/src/redux/types.ts index 6a93719e..facf6e94 100644 --- a/src/redux/types.ts +++ b/src/redux/types.ts @@ -118,9 +118,12 @@ export interface FlowDisplay { dominant_color?: string; } +// TODO: Pick<> +export type INodeUser = Partial; + export interface INode { id?: number; - user?: Partial; + user?: INodeUser; title: string; files: IFile[]; diff --git a/src/utils/hooks/node/useNodeComments.ts b/src/utils/hooks/node/useNodeComments.ts new file mode 100644 index 00000000..d07f7495 --- /dev/null +++ b/src/utils/hooks/node/useNodeComments.ts @@ -0,0 +1,17 @@ +import { useCallback } from 'react'; +import { nodeLoadMoreComments, nodeLockComment } from '~/redux/node/actions'; +import { IComment } from '~/redux/types'; +import { useDispatch } from 'react-redux'; + +export const useNodeComments = (id: string) => { + const dispatch = useDispatch(); + + const onLoadMoreComments = useCallback(() => dispatch(nodeLoadMoreComments()), [dispatch]); + + const onDelete = useCallback( + (id: IComment['id'], locked: boolean) => dispatch(nodeLockComment(id, locked)), + [dispatch] + ); + + return { onLoadMoreComments, onDelete }; +}; diff --git a/src/utils/hooks/useImageModal.ts b/src/utils/hooks/useImageModal.ts new file mode 100644 index 00000000..9091cfed --- /dev/null +++ b/src/utils/hooks/useImageModal.ts @@ -0,0 +1,13 @@ +import { useCallback } from 'react'; +import { IFile } from '~/redux/types'; +import { modalShowPhotoswipe } from '~/redux/modal/actions'; +import { useDispatch } from 'react-redux'; + +export const useImageModal = () => { + const dispatch = useDispatch(); + + return useCallback( + (images: IFile[], index: number) => dispatch(modalShowPhotoswipe(images, index)), + [dispatch] + ); +};