mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 04:46:40 +07:00
moved comments load to hook
This commit is contained in:
parent
5e1e575ee3
commit
639c952c2c
11 changed files with 184 additions and 59 deletions
|
@ -8,16 +8,27 @@ import { Footer } from '~/components/main/Footer';
|
||||||
import { Card } from '~/components/containers/Card';
|
import { Card } from '~/components/containers/Card';
|
||||||
import { useShallowSelect } from '~/utils/hooks/useShallowSelect';
|
import { useShallowSelect } from '~/utils/hooks/useShallowSelect';
|
||||||
import { selectAuthUser } from '~/redux/auth/selectors';
|
import { selectAuthUser } from '~/redux/auth/selectors';
|
||||||
import { IComment, INode } from '~/redux/types';
|
import { IComment, IFile, INode } from '~/redux/types';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
isLoadingComments: boolean;
|
isLoadingComments: boolean;
|
||||||
commentCount: number;
|
commentCount: number;
|
||||||
node: INode;
|
node: INode;
|
||||||
comments: IComment[];
|
comments: IComment[];
|
||||||
|
onDelete: (id: IComment['id'], locked: boolean) => void;
|
||||||
|
onLoadMoreComments: () => void;
|
||||||
|
onShowPhotoswipe: (images: IFile[], index: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BorisComments: FC<IProps> = ({ isLoadingComments, node, commentCount, comments }) => {
|
const BorisComments: FC<IProps> = ({
|
||||||
|
node,
|
||||||
|
commentCount,
|
||||||
|
comments,
|
||||||
|
isLoadingComments,
|
||||||
|
onLoadMoreComments,
|
||||||
|
onDelete,
|
||||||
|
onShowPhotoswipe,
|
||||||
|
}) => {
|
||||||
const user = useShallowSelect(selectAuthUser);
|
const user = useShallowSelect(selectAuthUser);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -28,7 +39,15 @@ const BorisComments: FC<IProps> = ({ isLoadingComments, node, commentCount, comm
|
||||||
{isLoadingComments ? (
|
{isLoadingComments ? (
|
||||||
<NodeNoComments is_loading count={7} />
|
<NodeNoComments is_loading count={7} />
|
||||||
) : (
|
) : (
|
||||||
<NodeComments comments={comments} count={commentCount} user={user} order="ASC" />
|
<NodeComments
|
||||||
|
comments={comments}
|
||||||
|
count={commentCount}
|
||||||
|
user={user}
|
||||||
|
order="ASC"
|
||||||
|
onLoadMoreComments={onLoadMoreComments}
|
||||||
|
onDelete={onDelete}
|
||||||
|
onShowPhotoswipe={onShowPhotoswipe}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { FC, HTMLAttributes, memo } from 'react';
|
import React, { FC, HTMLAttributes, memo } from 'react';
|
||||||
import { CommentWrapper } from '~/components/containers/CommentWrapper';
|
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 { CommentContent } from '~/components/comment/CommentContent';
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
import { CommendDeleted } from '../../node/CommendDeleted';
|
import { CommendDeleted } from '../../node/CommendDeleted';
|
||||||
|
@ -13,7 +13,7 @@ type IProps = HTMLAttributes<HTMLDivElement> & {
|
||||||
is_same?: boolean;
|
is_same?: boolean;
|
||||||
can_edit?: boolean;
|
can_edit?: boolean;
|
||||||
onDelete: (id: IComment['id'], isLocked: boolean) => void;
|
onDelete: (id: IComment['id'], isLocked: boolean) => void;
|
||||||
modalShowPhotoswipe: typeof MODAL_ACTIONS.modalShowPhotoswipe;
|
modalShowPhotoswipe: (images: IFile[], index: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Comment: FC<IProps> = memo(
|
const Comment: FC<IProps> = memo(
|
||||||
|
|
|
@ -21,7 +21,7 @@ interface IProps {
|
||||||
comment: IComment;
|
comment: IComment;
|
||||||
can_edit: boolean;
|
can_edit: boolean;
|
||||||
onDelete: (id: IComment['id'], isLocked: boolean) => void;
|
onDelete: (id: IComment['id'], isLocked: boolean) => void;
|
||||||
modalShowPhotoswipe: typeof MODAL_ACTIONS.modalShowPhotoswipe;
|
modalShowPhotoswipe: (images: IFile[], index: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CommentContent: FC<IProps> = memo(({ comment, can_edit, onDelete, modalShowPhotoswipe }) => {
|
const CommentContent: FC<IProps> = memo(({ comment, can_edit, onDelete, modalShowPhotoswipe }) => {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { NodeCommentsBlock } from '~/components/node/NodeCommentsBlock';
|
||||||
import { NodeCommentForm } from '~/components/node/NodeCommentForm';
|
import { NodeCommentForm } from '~/components/node/NodeCommentForm';
|
||||||
import { NodeRelatedBlock } from '~/components/node/NodeRelatedBlock';
|
import { NodeRelatedBlock } from '~/components/node/NodeRelatedBlock';
|
||||||
import { useNodeBlocks } from '~/utils/hooks/node/useNodeBlocks';
|
import { useNodeBlocks } from '~/utils/hooks/node/useNodeBlocks';
|
||||||
import { IComment, INode } from '~/redux/types';
|
import { IComment, IFile, INode } from '~/redux/types';
|
||||||
import { useUser } from '~/utils/hooks/user/userUser';
|
import { useUser } from '~/utils/hooks/user/userUser';
|
||||||
import { NodeTagsBlock } from '~/components/node/NodeTagsBlock';
|
import { NodeTagsBlock } from '~/components/node/NodeTagsBlock';
|
||||||
import { INodeRelated } from '~/redux/node/types';
|
import { INodeRelated } from '~/redux/node/types';
|
||||||
|
@ -21,6 +21,9 @@ interface IProps {
|
||||||
commentsCount: number;
|
commentsCount: number;
|
||||||
isLoadingComments: boolean;
|
isLoadingComments: boolean;
|
||||||
related: INodeRelated;
|
related: INodeRelated;
|
||||||
|
onDeleteComment: (id: IComment['id'], locked: boolean) => void;
|
||||||
|
onLoadMoreComments: () => void;
|
||||||
|
onShowPhotoswipe: (images: IFile[], index: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NodeBottomBlock: FC<IProps> = ({
|
const NodeBottomBlock: FC<IProps> = ({
|
||||||
|
@ -31,6 +34,9 @@ const NodeBottomBlock: FC<IProps> = ({
|
||||||
commentsCount,
|
commentsCount,
|
||||||
commentsOrder,
|
commentsOrder,
|
||||||
related,
|
related,
|
||||||
|
onDeleteComment,
|
||||||
|
onLoadMoreComments,
|
||||||
|
onShowPhotoswipe,
|
||||||
}) => {
|
}) => {
|
||||||
const { inline } = useNodeBlocks(node, isLoading);
|
const { inline } = useNodeBlocks(node, isLoading);
|
||||||
const { is_user } = useUser();
|
const { is_user } = useUser();
|
||||||
|
@ -53,6 +59,9 @@ const NodeBottomBlock: FC<IProps> = ({
|
||||||
count={commentsCount}
|
count={commentsCount}
|
||||||
order={commentsOrder}
|
order={commentsOrder}
|
||||||
node={node}
|
node={node}
|
||||||
|
onDelete={onDeleteComment}
|
||||||
|
onLoadMoreComments={onLoadMoreComments}
|
||||||
|
onShowPhotoswipe={onShowPhotoswipe}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{is_user && !isLoading && <NodeCommentForm nodeId={node?.id} />}
|
{is_user && !isLoading && <NodeCommentForm nodeId={node?.id} />}
|
||||||
|
|
|
@ -18,10 +18,21 @@ interface IProps {
|
||||||
count: INodeState['comment_count'];
|
count: INodeState['comment_count'];
|
||||||
user: IUser;
|
user: IUser;
|
||||||
order?: 'ASC' | 'DESC';
|
order?: 'ASC' | 'DESC';
|
||||||
|
onDelete: (id: IComment['id'], locked: boolean) => void;
|
||||||
|
onLoadMoreComments: () => void;
|
||||||
|
onShowPhotoswipe: (images: IFile[], index: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NodeComments: FC<IProps> = memo(({ comments, user, count = 0, order = 'DESC' }) => {
|
const NodeComments: FC<IProps> = memo(
|
||||||
const dispatch = useDispatch();
|
({
|
||||||
|
onLoadMoreComments,
|
||||||
|
onDelete,
|
||||||
|
onShowPhotoswipe,
|
||||||
|
comments,
|
||||||
|
user,
|
||||||
|
count = 0,
|
||||||
|
order = 'DESC',
|
||||||
|
}) => {
|
||||||
const left = useMemo(() => Math.max(0, count - comments.length), [comments, count]);
|
const left = useMemo(() => Math.max(0, count - comments.length), [comments, count]);
|
||||||
|
|
||||||
const groupped: ICommentGroup[] = useMemo(
|
const groupped: ICommentGroup[] = useMemo(
|
||||||
|
@ -29,16 +40,6 @@ const NodeComments: FC<IProps> = memo(({ comments, user, count = 0, order = 'DES
|
||||||
[comments, order]
|
[comments, order]
|
||||||
);
|
);
|
||||||
|
|
||||||
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(
|
const more = useMemo(
|
||||||
() =>
|
() =>
|
||||||
left > 0 && (
|
left > 0 && (
|
||||||
|
@ -68,6 +69,7 @@ const NodeComments: FC<IProps> = memo(({ comments, user, count = 0, order = 'DES
|
||||||
{order === 'ASC' && more}
|
{order === 'ASC' && more}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export { NodeComments };
|
export { NodeComments };
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
import { NodeNoComments } from '~/components/node/NodeNoComments';
|
import { NodeNoComments } from '~/components/node/NodeNoComments';
|
||||||
import { NodeComments } from '~/components/node/NodeComments';
|
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 { useNodeBlocks } from '~/utils/hooks/node/useNodeBlocks';
|
||||||
import { useUser } from '~/utils/hooks/user/userUser';
|
import { useUser } from '~/utils/hooks/user/userUser';
|
||||||
|
|
||||||
|
@ -12,16 +12,36 @@ interface IProps {
|
||||||
count: number;
|
count: number;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
isLoadingComments: boolean;
|
isLoadingComments: boolean;
|
||||||
|
onDelete: (id: IComment['id'], locked: boolean) => void;
|
||||||
|
onLoadMoreComments: () => void;
|
||||||
|
onShowPhotoswipe: (images: IFile[], index: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NodeCommentsBlock: FC<IProps> = ({ isLoading, isLoadingComments, node, comments, count }) => {
|
const NodeCommentsBlock: FC<IProps> = ({
|
||||||
|
onLoadMoreComments,
|
||||||
|
onDelete,
|
||||||
|
onShowPhotoswipe,
|
||||||
|
isLoading,
|
||||||
|
isLoadingComments,
|
||||||
|
node,
|
||||||
|
comments,
|
||||||
|
count,
|
||||||
|
}) => {
|
||||||
const user = useUser();
|
const user = useUser();
|
||||||
const { inline } = useNodeBlocks(node, isLoading);
|
const { inline } = useNodeBlocks(node, isLoading);
|
||||||
|
|
||||||
return isLoading || isLoadingComments || (!comments.length && !inline) ? (
|
return isLoading || isLoadingComments || (!comments.length && !inline) ? (
|
||||||
<NodeNoComments is_loading={isLoadingComments || isLoading} />
|
<NodeNoComments is_loading={isLoadingComments || isLoading} />
|
||||||
) : (
|
) : (
|
||||||
<NodeComments count={count} comments={comments} user={user} order="DESC" />
|
<NodeComments
|
||||||
|
count={count}
|
||||||
|
comments={comments}
|
||||||
|
user={user}
|
||||||
|
order="DESC"
|
||||||
|
onLoadMoreComments={onLoadMoreComments}
|
||||||
|
onDelete={onDelete}
|
||||||
|
onShowPhotoswipe={onShowPhotoswipe}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { IComment, INode } from '~/redux/types';
|
import { IComment, INode } from '~/redux/types';
|
||||||
import { ISocialProvider } from '~/redux/auth/types';
|
import { ISocialProvider } from '~/redux/auth/types';
|
||||||
|
import { COMMENTS_DISPLAY } from '~/redux/node/constants';
|
||||||
|
|
||||||
export const API = {
|
export const API = {
|
||||||
BASE: process.env.REACT_APP_API_HOST,
|
BASE: process.env.REACT_APP_API_HOST,
|
||||||
|
@ -28,6 +29,8 @@ export const API = {
|
||||||
GET_NODE: (id: number | string) => `/node/${id}`,
|
GET_NODE: (id: number | string) => `/node/${id}`,
|
||||||
|
|
||||||
COMMENT: (id: INode['id']) => `/node/${id}/comment`,
|
COMMENT: (id: INode['id']) => `/node/${id}/comment`,
|
||||||
|
COMMENT_INFINITE: (id: INode['id'], skip: number) =>
|
||||||
|
`/node/${id}/comment?take=${COMMENTS_DISPLAY}&skip=${skip}`,
|
||||||
RELATED: (id: INode['id']) => `/node/${id}/related`,
|
RELATED: (id: INode['id']) => `/node/${id}/related`,
|
||||||
UPDATE_TAGS: (id: INode['id']) => `/node/${id}/tags`,
|
UPDATE_TAGS: (id: INode['id']) => `/node/${id}/tags`,
|
||||||
POST_LIKE: (id: INode['id']) => `/node/${id}/like`,
|
POST_LIKE: (id: INode['id']) => `/node/${id}/like`,
|
||||||
|
|
|
@ -115,6 +115,9 @@ const BorisLayout: FC<IProps> = () => {
|
||||||
commentCount={node.comment_count}
|
commentCount={node.comment_count}
|
||||||
node={node.current}
|
node={node.current}
|
||||||
comments={node.comments}
|
comments={node.comments}
|
||||||
|
onDelete={console.log}
|
||||||
|
onLoadMoreComments={console.log}
|
||||||
|
onShowPhotoswipe={console.log}
|
||||||
/>
|
/>
|
||||||
</Switch>
|
</Switch>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { FC, memo } from 'react';
|
import React, { FC, memo, useCallback } from 'react';
|
||||||
import { Route, RouteComponentProps } from 'react-router';
|
import { Route, RouteComponentProps } from 'react-router';
|
||||||
import { selectNode } from '~/redux/node/selectors';
|
import { selectNode } from '~/redux/node/selectors';
|
||||||
import { Card } from '~/components/containers/Card';
|
import { Card } from '~/components/containers/Card';
|
||||||
|
@ -20,6 +20,10 @@ import { useOnNodeSeen } from '~/utils/hooks/node/useOnNodeSeen';
|
||||||
|
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
import { useNodeFetcher } from '~/utils/hooks/node/useNodeFetcher';
|
import { useNodeFetcher } from '~/utils/hooks/node/useNodeFetcher';
|
||||||
|
import { useNodeComments } from '~/utils/hooks/node/useNodeComments';
|
||||||
|
import { IFile } from '~/redux/types';
|
||||||
|
import { modalShowPhotoswipe } from '~/redux/modal/actions';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
|
||||||
type IProps = RouteComponentProps<{ id: string }> & {};
|
type IProps = RouteComponentProps<{ id: string }> & {};
|
||||||
|
|
||||||
|
@ -30,8 +34,16 @@ const NodeLayout: FC<IProps> = memo(
|
||||||
},
|
},
|
||||||
}) => {
|
}) => {
|
||||||
const { node, isLoading } = useNodeFetcher(parseInt(id, 10));
|
const { node, isLoading } = useNodeFetcher(parseInt(id, 10));
|
||||||
|
const {
|
||||||
|
comments,
|
||||||
|
isLoading: isLoadingComments,
|
||||||
|
count: commentsCount,
|
||||||
|
onDelete,
|
||||||
|
onLoadMoreComments,
|
||||||
|
onShowPhotoswipe,
|
||||||
|
} = useNodeComments(parseInt(id, 10));
|
||||||
|
|
||||||
const { comments, comment_count, is_loading_comments, related } = useShallowSelect(selectNode);
|
const { related } = useShallowSelect(selectNode);
|
||||||
|
|
||||||
useNodeCoverImage(node);
|
useNodeCoverImage(node);
|
||||||
useScrollToTop([id]);
|
useScrollToTop([id]);
|
||||||
|
@ -52,12 +64,15 @@ const NodeLayout: FC<IProps> = memo(
|
||||||
|
|
||||||
<NodeBottomBlock
|
<NodeBottomBlock
|
||||||
node={node}
|
node={node}
|
||||||
isLoadingComments={is_loading_comments}
|
|
||||||
comments={comments}
|
comments={comments}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
commentsCount={comment_count}
|
isLoadingComments={isLoadingComments}
|
||||||
|
commentsCount={commentsCount}
|
||||||
commentsOrder="DESC"
|
commentsOrder="DESC"
|
||||||
related={related}
|
related={related}
|
||||||
|
onShowPhotoswipe={onShowPhotoswipe}
|
||||||
|
onDeleteComment={onDelete}
|
||||||
|
onLoadMoreComments={onLoadMoreComments}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|
54
src/utils/hooks/node/useNodeComments.ts
Normal file
54
src/utils/hooks/node/useNodeComments.ts
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import { IComment, IFile, INode } from '~/redux/types';
|
||||||
|
import useSWRInfinite from 'swr/infinite';
|
||||||
|
import { ApiGetNodeCommentsResponse } from '~/redux/node/api';
|
||||||
|
import { api, cleanResult } from '~/utils/api';
|
||||||
|
import { API } from '~/constants/api';
|
||||||
|
import { useCallback, useMemo } from 'react';
|
||||||
|
import { COMMENTS_DISPLAY } from '~/redux/node/constants';
|
||||||
|
import { nodeLockComment } from '~/redux/node/actions';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import { modalShowPhotoswipe } from '~/redux/modal/actions';
|
||||||
|
|
||||||
|
export const fetcher = (url: string) => api.get<ApiGetNodeCommentsResponse>(url).then(cleanResult);
|
||||||
|
|
||||||
|
export const useNodeComments = (id: INode['id']) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const getKey = useCallback(
|
||||||
|
(pageIndex, previousPageData) => {
|
||||||
|
if (previousPageData && !previousPageData?.comments?.length) return null;
|
||||||
|
return API.NODE.COMMENT_INFINITE(id, pageIndex * COMMENTS_DISPLAY);
|
||||||
|
},
|
||||||
|
[id]
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data, error, isValidating, size, setSize } = useSWRInfinite(getKey, fetcher);
|
||||||
|
|
||||||
|
const comments = useMemo<IComment[]>(
|
||||||
|
() => (data || []).reduce((acc, { comments }) => [...acc, ...comments], [] as IComment[]),
|
||||||
|
[data]
|
||||||
|
);
|
||||||
|
|
||||||
|
const count = useMemo<number>(() => {
|
||||||
|
if (!data) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data[data.length - 1].comment_count || 0;
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
const isLoading = !data && !isValidating;
|
||||||
|
|
||||||
|
const onDelete = useCallback(
|
||||||
|
(id: IComment['id'], locked: boolean) => dispatch(nodeLockComment(id, locked)),
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
const onLoadMoreComments = useCallback(() => setSize(size + 1), [size, setSize]);
|
||||||
|
|
||||||
|
const onShowPhotoswipe = useCallback(
|
||||||
|
(images: IFile[], index: number) => dispatch(modalShowPhotoswipe(images, index)),
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
|
return { comments, count, error, isLoading, onDelete, onLoadMoreComments, onShowPhotoswipe };
|
||||||
|
};
|
|
@ -5,7 +5,7 @@ import { apiGetNode } from '~/redux/node/api';
|
||||||
export const useNodeFetcher = (id: INode['id']) => {
|
export const useNodeFetcher = (id: INode['id']) => {
|
||||||
const { data, error, isValidating } = useSWR(`${id}`, apiGetNode);
|
const { data, error, isValidating } = useSWR(`${id}`, apiGetNode);
|
||||||
const node = data?.node;
|
const node = data?.node;
|
||||||
const isLoading = !node && !isValidating;
|
const isLoading = !data && !isValidating;
|
||||||
|
|
||||||
return { node, error, isLoading };
|
return { node, error, isLoading };
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue