1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-25 04:46:40 +07:00

add comments for guests

This commit is contained in:
Fedor Katurov 2023-10-30 15:16:19 +06:00
parent cbf7b1f616
commit 8abf6177b5
16 changed files with 278 additions and 194 deletions

View file

@ -21,7 +21,7 @@ type IProps = HTMLAttributes<HTMLDivElement> & {
isSame?: boolean; isSame?: boolean;
canEdit?: boolean; canEdit?: boolean;
highlighted?: boolean; highlighted?: boolean;
saveComment: (data: IComment) => Promise<unknown>; saveComment: (data: IComment) => Promise<IComment | undefined>;
onDelete: (id: IComment['id'], isLocked: boolean) => void; onDelete: (id: IComment['id'], isLocked: boolean) => void;
onShowImageModal: (images: IFile[], index: number) => void; onShowImageModal: (images: IFile[], index: number) => void;
}; };

View file

@ -7,11 +7,15 @@ import { IUser } from '~/types/auth';
import { path } from '~/utils/ramda'; import { path } from '~/utils/ramda';
interface Props { interface Props {
user: IUser; user?: IUser;
className?: string; className?: string;
} }
const CommentAvatar: FC<Props> = ({ user, className }) => { const CommentAvatar: FC<Props> = ({ user, className }) => {
if (!user) {
return <Avatar className={className} />;
}
return ( return (
<MenuButton <MenuButton
position="auto" position="auto"

View file

@ -22,6 +22,7 @@ import { IComment, IFile } from '~/types';
import { formatCommentText, getPrettyDate, getURL } from '~/utils/dom'; import { formatCommentText, getPrettyDate, getURL } from '~/utils/dom';
import { append, assocPath, path, reduce } from '~/utils/ramda'; import { append, assocPath, path, reduce } from '~/utils/ramda';
import { CommentEditingForm } from '../CommentEditingForm';
import { CommentImageGrid } from '../CommentImageGrid'; import { CommentImageGrid } from '../CommentImageGrid';
import { CommentMenu } from '../CommentMenu'; import { CommentMenu } from '../CommentMenu';
@ -32,7 +33,7 @@ interface IProps {
nodeId: number; nodeId: number;
comment: IComment; comment: IComment;
canEdit: boolean; canEdit: boolean;
saveComment: (data: IComment) => Promise<unknown>; saveComment: (data: IComment) => Promise<IComment | undefined>;
onDelete: (id: IComment['id'], isLocked: boolean) => void; onDelete: (id: IComment['id'], isLocked: boolean) => void;
onShowImageModal: (images: IFile[], index: number) => void; onShowImageModal: (images: IFile[], index: number) => void;
} }
@ -98,7 +99,7 @@ const CommentContent: FC<IProps> = memo(
if (isEditing) { if (isEditing) {
return ( return (
<CommentForm <CommentEditingForm
saveComment={saveComment} saveComment={saveComment}
nodeId={nodeId} nodeId={nodeId}
comment={comment} comment={comment}

View file

@ -0,0 +1,38 @@
import { FC } from 'react';
import { CommentForm } from '~/components/comment/CommentForm';
import { UploadDropzone } from '~/components/upload/UploadDropzone';
import { UploadSubject, UploadTarget } from '~/constants/uploads';
import { useUploader } from '~/hooks/data/useUploader';
import { IComment, INode } from '~/types';
import { UploaderContextProvider } from '~/utils/context/UploaderContextProvider';
interface CommentEditingFormProps {
comment: IComment;
nodeId: INode['id'];
saveComment: (data: IComment) => Promise<IComment | undefined>;
onCancelEdit?: () => void;
}
const CommentEditingForm: FC<CommentEditingFormProps> = ({
saveComment,
comment,
onCancelEdit,
}) => {
const uploader = useUploader(UploadSubject.Comment, UploadTarget.Comments);
return (
<UploadDropzone onUpload={uploader.uploadFiles}>
<UploaderContextProvider value={uploader}>
<CommentForm
saveComment={saveComment}
comment={comment}
onCancelEdit={onCancelEdit}
allowUploads
/>
</UploaderContextProvider>
</UploadDropzone>
);
};
export { CommentEditingForm };

View file

@ -1,4 +1,4 @@
import React, { FC, useCallback, useState } from 'react'; import { FC, useCallback, useMemo, useState } from 'react';
import { FormikProvider } from 'formik'; import { FormikProvider } from 'formik';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
@ -12,31 +12,35 @@ import { Button } from '~/components/input/Button';
import { UploadDropzone } from '~/components/upload/UploadDropzone'; import { UploadDropzone } from '~/components/upload/UploadDropzone';
import { ERROR_LITERAL } from '~/constants/errors'; import { ERROR_LITERAL } from '~/constants/errors';
import { EMPTY_COMMENT } from '~/constants/node'; import { EMPTY_COMMENT } from '~/constants/node';
import { UploadSubject, UploadTarget } from '~/constants/uploads';
import { useCommentFormFormik } from '~/hooks/comments/useCommentFormFormik'; import { useCommentFormFormik } from '~/hooks/comments/useCommentFormFormik';
import { useUploader } from '~/hooks/data/useUploader';
import { useInputPasteUpload } from '~/hooks/dom/useInputPasteUpload'; import { useInputPasteUpload } from '~/hooks/dom/useInputPasteUpload';
import { IComment, INode } from '~/types'; import { IComment, INode } from '~/types';
import { UploaderContextProvider } from '~/utils/context/UploaderContextProvider'; import {
UploaderContextProvider,
useUploaderContext,
} from '~/utils/context/UploaderContextProvider';
import styles from './styles.module.scss'; import styles from './styles.module.scss';
interface IProps { interface IProps {
comment?: IComment; comment?: IComment;
nodeId: INode['id']; allowUploads?: boolean;
saveComment: (data: IComment) => Promise<unknown>;
saveComment: (data: IComment) => Promise<IComment | undefined>;
onCancelEdit?: () => void; onCancelEdit?: () => void;
} }
const CommentForm: FC<IProps> = observer(({ comment, nodeId, saveComment, onCancelEdit }) => { const CommentForm: FC<IProps> = observer(
({ comment, allowUploads, saveComment, onCancelEdit }) => {
const [textarea, setTextArea] = useState<HTMLTextAreaElement | null>(null); const [textarea, setTextArea] = useState<HTMLTextAreaElement | null>(null);
const uploader = useUploader(UploadSubject.Comment, UploadTarget.Comments, comment?.files); const uploader = useUploaderContext();
const formik = useCommentFormFormik( const formik = useCommentFormFormik(
comment || EMPTY_COMMENT, comment || EMPTY_COMMENT,
nodeId, uploader.files,
uploader, uploader.setFiles,
saveComment, saveComment,
onCancelEdit onCancelEdit,
); );
const isLoading = formik.isSubmitting || uploader.isUploading; const isLoading = formik.isSubmitting || uploader.isUploading;
const isEditing = !!comment?.id; const isEditing = !!comment?.id;
@ -58,10 +62,8 @@ const CommentForm: FC<IProps> = observer(({ comment, nodeId, saveComment, onCanc
const onPaste = useInputPasteUpload(uploader.uploadFiles); const onPaste = useInputPasteUpload(uploader.uploadFiles);
return ( return (
<UploadDropzone onUpload={uploader.uploadFiles}>
<form onSubmit={formik.handleSubmit} className={styles.wrap}> <form onSubmit={formik.handleSubmit} className={styles.wrap}>
<FormikProvider value={formik}> <FormikProvider value={formik}>
<UploaderContextProvider value={uploader}>
<div className={styles.input}> <div className={styles.input}>
<LocalCommentFormTextarea onPaste={onPaste} ref={setTextArea} /> <LocalCommentFormTextarea onPaste={onPaste} ref={setTextArea} />
@ -72,12 +74,14 @@ const CommentForm: FC<IProps> = observer(({ comment, nodeId, saveComment, onCanc
)} )}
</div> </div>
<CommentFormAttaches /> {allowUploads && <CommentFormAttaches />}
<div className={styles.buttons}> <div className={styles.buttons}>
{allowUploads && (
<div className={styles.button_column}> <div className={styles.button_column}>
<CommentFormAttachButtons onUpload={uploader.uploadFiles} /> <CommentFormAttachButtons onUpload={uploader.uploadFiles} />
</div> </div>
)}
<div className={styles.button_column}> <div className={styles.button_column}>
{!!textarea && ( {!!textarea && (
@ -92,7 +96,12 @@ const CommentForm: FC<IProps> = observer(({ comment, nodeId, saveComment, onCanc
<div className={styles.button_column}> <div className={styles.button_column}>
{isEditing && ( {isEditing && (
<Button size="small" color="link" type="button" onClick={onCancelEdit}> <Button
size="small"
color="link"
type="button"
onClick={onCancelEdit}
>
Отмена Отмена
</Button> </Button>
)} )}
@ -109,11 +118,10 @@ const CommentForm: FC<IProps> = observer(({ comment, nodeId, saveComment, onCanc
</Button> </Button>
</div> </div>
</div> </div>
</UploaderContextProvider>
</FormikProvider> </FormikProvider>
</form> </form>
</UploadDropzone>
); );
}); },
);
export { CommentForm }; export { CommentForm };

View file

@ -10,7 +10,7 @@ import { DivProps } from '~/utils/types';
import styles from './styles.module.scss'; import styles from './styles.module.scss';
type IProps = DivProps & { type IProps = DivProps & {
user: IUser; user?: IUser;
isEmpty?: boolean; isEmpty?: boolean;
isLoading?: boolean; isLoading?: boolean;
isForm?: boolean; isForm?: boolean;
@ -36,7 +36,10 @@ const CommentWrapper: FC<IProps> = ({
{...props} {...props}
> >
<div className={styles.thumb}> <div className={styles.thumb}>
<CommentAvatar user={user} className={styles.thumb_image} /> <CommentAvatar
user={user}
className={classNames(styles.thumb_image, { [styles.pointer]: user })}
/>
<div className={styles.thumb_user}>~{path(['username'], user)}</div> <div className={styles.thumb_user}>~{path(['username'], user)}</div>
</div> </div>

View file

@ -95,7 +95,7 @@ div.thumb_image {
background-size: cover; background-size: cover;
flex: 0 0 $comment_height; flex: 0 0 $comment_height;
will-change: transform; will-change: transform;
cursor: pointer; cursor: default;
@include tablet { @include tablet {
height: 32px; height: 32px;
@ -105,6 +105,10 @@ div.thumb_image {
} }
} }
.pointer {
cursor: pointer;
}
.thumb_user { .thumb_user {
display: none; display: none;
flex: 1; flex: 1;

View file

@ -1,22 +0,0 @@
import React, { FC } from 'react';
import { CommentForm } from '~/components/comment/CommentForm';
import { CommentWrapper } from '~/components/containers/CommentWrapper';
import { IComment } from '~/types';
import { IUser } from '~/types/auth';
export interface NodeCommentFormProps {
user: IUser;
nodeId?: number;
saveComment: (comment: IComment) => Promise<unknown>;
}
const NodeCommentForm: FC<NodeCommentFormProps> = ({ user, nodeId, saveComment }) => {
return (
<CommentWrapper user={user} isForm>
<CommentForm nodeId={nodeId} saveComment={saveComment} />
</CommentWrapper>
);
};
export { NodeCommentForm };

View file

@ -1,8 +0,0 @@
import dynamic from 'next/dynamic';
import type { NodeCommentFormProps } from './index';
export const NodeCommentFormSSR = dynamic<NodeCommentFormProps>(
() => import('./index').then(it => it.NodeCommentForm),
{ ssr: false }
);

View file

@ -1,34 +1,20 @@
import React, { FC } from 'react'; import { FC } from 'react';
import { Group } from '~/components/containers/Group'; import { Group } from '~/components/containers/Group';
import { Footer } from '~/components/main/Footer'; import { Footer } from '~/components/main/Footer';
import { NodeCommentFormSSR } from '~/components/node/NodeCommentForm/ssr';
import { NodeNoComments } from '~/components/node/NodeNoComments'; import { NodeNoComments } from '~/components/node/NodeNoComments';
import { isSSR } from '~/constants/ssr'; import { NodeCommentFormSSR } from '~/containers/node/NodeCommentForm/ssr';
import { NodeComments } from '~/containers/node/NodeComments'; import { NodeComments } from '~/containers/node/NodeComments';
import { useAuth } from '~/hooks/auth/useAuth';
import { useCommentContext } from '~/utils/context/CommentContextProvider'; import { useCommentContext } from '~/utils/context/CommentContextProvider';
import { useNodeContext } from '~/utils/context/NodeContextProvider';
import { useUserContext } from '~/utils/context/UserContextProvider';
interface IProps {} interface Props {}
const BorisComments: FC<IProps> = () => {
const user = useUserContext();
const { isUser } = useAuth();
const BorisComments: FC<Props> = () => {
const { isLoading, comments, onSaveComment } = useCommentContext(); const { isLoading, comments, onSaveComment } = useCommentContext();
const { node } = useNodeContext();
return ( return (
<Group> <Group>
{(isUser || isSSR) && ( <NodeCommentFormSSR saveComment={onSaveComment} />
<NodeCommentFormSSR
user={user}
nodeId={node.id}
saveComment={onSaveComment}
/>
)}
{isLoading || !comments?.length ? ( {isLoading || !comments?.length ? (
<NodeNoComments loading count={7} /> <NodeNoComments loading count={7} />

View file

@ -1,24 +1,21 @@
import React, { FC } from 'react'; import { FC } from 'react';
import { Card } from '~/components/containers/Card';
import { Filler } from '~/components/containers/Filler'; import { Filler } from '~/components/containers/Filler';
import { Group } from '~/components/containers/Group'; import { Group } from '~/components/containers/Group';
import { Padder } from '~/components/containers/Padder'; import { Padder } from '~/components/containers/Padder';
import { Sticky } from '~/components/containers/Sticky'; import { Sticky } from '~/components/containers/Sticky';
import { NodeAuthorBlock } from '~/components/node/NodeAuthorBlock'; import { NodeAuthorBlock } from '~/components/node/NodeAuthorBlock';
import { NodeCommentFormSSR } from '~/components/node/NodeCommentForm/ssr';
import { NodeDeletedBadge } from '~/components/node/NodeDeletedBadge'; import { NodeDeletedBadge } from '~/components/node/NodeDeletedBadge';
import { NodeNoComments } from '~/components/node/NodeNoComments'; import { NodeNoComments } from '~/components/node/NodeNoComments';
import { NodeRelatedBlock } from '~/components/node/NodeRelatedBlock'; import { NodeRelatedBlock } from '~/components/node/NodeRelatedBlock';
import { NodeTagsBlock } from '~/components/node/NodeTagsBlock'; import { NodeTagsBlock } from '~/components/node/NodeTagsBlock';
import { NodeBacklinks } from '~/containers/node/NodeBacklinks'; import { NodeBacklinks } from '~/containers/node/NodeBacklinks';
import { NodeCommentFormSSR } from '~/containers/node/NodeCommentForm/ssr';
import { NodeComments } from '~/containers/node/NodeComments'; import { NodeComments } from '~/containers/node/NodeComments';
import { useNodeBlocks } from '~/hooks/node/useNodeBlocks'; import { useNodeBlocks } from '~/hooks/node/useNodeBlocks';
import { useCommentContext } from '~/utils/context/CommentContextProvider'; import { useCommentContext } from '~/utils/context/CommentContextProvider';
import { useNodeContext } from '~/utils/context/NodeContextProvider'; import { useNodeContext } from '~/utils/context/NodeContextProvider';
import { useNodeRelatedContext } from '~/utils/context/NodeRelatedContextProvider'; import { useNodeRelatedContext } from '~/utils/context/NodeRelatedContextProvider';
import { useUserContext } from '~/utils/context/UserContextProvider';
import { useAuthProvider } from '~/utils/providers/AuthProvider';
import styles from './styles.module.scss'; import styles from './styles.module.scss';
@ -27,7 +24,6 @@ interface IProps {
} }
const NodeBottomBlock: FC<IProps> = ({ commentsOrder }) => { const NodeBottomBlock: FC<IProps> = ({ commentsOrder }) => {
const user = useUserContext();
const { node, isLoading, backlinks } = useNodeContext(); const { node, isLoading, backlinks } = useNodeContext();
const { const {
comments, comments,
@ -36,7 +32,6 @@ const NodeBottomBlock: FC<IProps> = ({ commentsOrder }) => {
} = useCommentContext(); } = useCommentContext();
const { related, isLoading: isLoadingRelated } = useNodeRelatedContext(); const { related, isLoading: isLoadingRelated } = useNodeRelatedContext();
const { inline } = useNodeBlocks(node, isLoading); const { inline } = useNodeBlocks(node, isLoading);
const { isUser } = useAuthProvider();
if (node.deleted_at) { if (node.deleted_at) {
return <NodeDeletedBadge />; return <NodeDeletedBadge />;
@ -59,13 +54,7 @@ const NodeBottomBlock: FC<IProps> = ({ commentsOrder }) => {
)} )}
</article> </article>
{isUser && !isLoading && ( <NodeCommentFormSSR saveComment={onSaveComment} />
<NodeCommentFormSSR
nodeId={node.id}
saveComment={onSaveComment}
user={user}
/>
)}
<div className={styles.subheader}> <div className={styles.subheader}>
<Filler className={styles.backlinks}> <Filler className={styles.backlinks}>

View file

@ -0,0 +1,47 @@
import { FC, useCallback } from 'react';
import { CommentForm } from '~/components/comment/CommentForm';
import { CommentWrapper } from '~/components/containers/CommentWrapper';
import { UploadDropzone } from '~/components/upload/UploadDropzone';
import { EMPTY_USER } from '~/constants/auth';
import { Dialog } from '~/constants/modal';
import { UploadSubject, UploadTarget } from '~/constants/uploads';
import { useAuth } from '~/hooks/auth/useAuth';
import { useUploader } from '~/hooks/data/useUploader';
import { useShowModal } from '~/hooks/modal/useShowModal';
import { IComment } from '~/types';
import { UploaderContextProvider } from '~/utils/context/UploaderContextProvider';
export interface Props {
saveComment: (comment: IComment) => Promise<IComment | undefined>;
}
const NodeCommentForm: FC<Props> = ({ saveComment }) => {
const { user, isUser } = useAuth();
const showLoginDialog = useShowModal(Dialog.Login);
const uploader = useUploader(UploadSubject.Comment, UploadTarget.Comments);
const onCommentSave = useCallback(
async (comment: IComment) => {
if (!isUser) {
showLoginDialog({});
return;
}
return saveComment(comment);
},
[isUser, showLoginDialog, saveComment],
);
return (
<UploadDropzone onUpload={uploader.uploadFiles}>
<UploaderContextProvider value={uploader}>
<CommentWrapper user={isUser ? user : undefined} isForm>
<CommentForm saveComment={onCommentSave} allowUploads={isUser} />
</CommentWrapper>
</UploaderContextProvider>
</UploadDropzone>
);
};
export { NodeCommentForm };

View file

@ -0,0 +1,8 @@
import dynamic from 'next/dynamic';
import type { Props } from './index';
export const NodeCommentFormSSR = dynamic<Props>(
() => import('./index').then((it) => it.NodeCommentForm),
{ ssr: false },
);

View file

@ -3,23 +3,22 @@ import { useCallback, useEffect, useRef } from 'react';
import { FormikHelpers, useFormik, useFormikContext } from 'formik'; import { FormikHelpers, useFormik, useFormikContext } from 'formik';
import { array, object, string } from 'yup'; import { array, object, string } from 'yup';
import { IComment, INode } from '~/types'; import { IComment, IFile } from '~/types';
import { Uploader } from '~/utils/context/UploaderContextProvider'; import { getErrorMessage } from '~/utils/errors/getErrorMessage';
import { showErrorToast } from '~/utils/errors/showToast'; import { showErrorToast } from '~/utils/errors/showToast';
import { hasPath, path } from '~/utils/ramda';
const validationSchema = object().shape({ const validationSchema = object().shape({
text: string(), text: string(),
files: array(), files: array(),
}); });
const onSuccess = ({ resetForm, setSubmitting, setErrors }: FormikHelpers<IComment>) => ( const onSuccess =
error?: unknown ({ resetForm, setSubmitting, setErrors }: FormikHelpers<IComment>) =>
) => { (error?: unknown) => {
setSubmitting(false); setSubmitting(false);
const message = getErrorMessage(error);
if (hasPath(['response', 'data', 'error'], error)) { if (message) {
const message = path(['response', 'data', 'error'], error) as string;
setErrors({ text: message }); setErrors({ text: message });
showErrorToast(error); showErrorToast(error);
return; return;
@ -28,34 +27,38 @@ const onSuccess = ({ resetForm, setSubmitting, setErrors }: FormikHelpers<IComme
if (resetForm) { if (resetForm) {
resetForm(); resetForm();
} }
}; };
export const useCommentFormFormik = ( export const useCommentFormFormik = (
values: IComment, comment: IComment,
nodeId: INode['id'], files: IFile[],
uploader: Uploader, setFiles: (file: IFile[]) => void,
sendData: (data: IComment) => Promise<unknown>, sendData: (data: IComment) => Promise<IComment | undefined>,
stopEditing?: () => void stopEditing?: () => void,
) => { ) => {
const { current: initialValues } = useRef(values); const { current: initialValues } = useRef(comment);
const onSubmit = useCallback( const onSubmit = useCallback(
async (values: IComment, helpers: FormikHelpers<IComment>) => { async (values: IComment, helpers: FormikHelpers<IComment>) => {
try { try {
helpers.setSubmitting(true); helpers.setSubmitting(true);
await sendData({ ...values, files: uploader.files });
const comment = await sendData({ ...values, files });
if (comment) {
onSuccess(helpers)(); onSuccess(helpers)();
}
} catch (error) { } catch (error) {
onSuccess(helpers)(error); onSuccess(helpers)(error);
} }
}, },
[sendData, uploader.files] [sendData, files],
); );
const onReset = useCallback(() => { const onReset = useCallback(() => {
uploader.setFiles([]); setFiles([]);
if (stopEditing) stopEditing(); if (stopEditing) stopEditing();
}, [stopEditing, uploader]); }, [stopEditing, setFiles]);
const formik = useFormik({ const formik = useFormik({
initialValues, initialValues,

View file

@ -26,17 +26,19 @@ export const useNodeComments = (nodeId: number, fallbackData?: IComment[]) => {
} }
await mutate( await mutate(
prev => (prev) =>
prev?.map(list => prev?.map((list) =>
list.map(comment => (comment.id === id ? { ...comment, deleted_at } : comment)) list.map((comment) =>
comment.id === id ? { ...comment, deleted_at } : comment,
), ),
false ),
false,
); );
} catch (error) { } catch (error) {
showErrorToast(error); showErrorToast(error);
} }
}, },
[data, mutate, nodeId] [data, mutate, nodeId],
); );
const onEdit = useCallback( const onEdit = useCallback(
@ -50,22 +52,37 @@ export const useNodeComments = (nodeId: number, fallbackData?: IComment[]) => {
// Comment was created // Comment was created
if (!comment.id) { if (!comment.id) {
await mutate( await mutate(
data.map((list, index) => (index === 0 ? [result.comment, ...list] : list)), data.map((list, index) =>
false index === 0 ? [result.comment, ...list] : list,
),
false,
); );
return;
return result.comment;
} }
await mutate( await mutate(
prev => (prev) =>
prev?.map(list => prev?.map((list) =>
list.map(it => (it.id === result.comment.id ? { ...it, ...result.comment } : it)) list.map((it) =>
it.id === result.comment.id ? { ...it, ...result.comment } : it,
), ),
false ),
); false,
},
[data, mutate, nodeId]
); );
return { onLoadMoreComments, onDelete, comments, hasMore, isLoading, onEdit, isLoadingMore }; return result.comment;
},
[data, mutate, nodeId],
);
return {
onLoadMoreComments,
onDelete,
comments,
hasMore,
isLoading,
onEdit,
isLoadingMore,
};
}; };

View file

@ -10,25 +10,31 @@ export interface CommentProviderProps {
isLoadingMore: boolean; isLoadingMore: boolean;
onShowImageModal: (images: IFile[], index: number) => void; onShowImageModal: (images: IFile[], index: number) => void;
onLoadMoreComments: () => void; onLoadMoreComments: () => void;
onSaveComment: (comment: IComment) => Promise<unknown>; onSaveComment: (comment: IComment) => Promise<IComment | undefined>;
onDeleteComment: (id: IComment['id'], isLocked: boolean) => void; onDeleteComment: (id: IComment['id'], isLocked: boolean) => void;
} }
const CommentContext = createContext<CommentProviderProps>({ const CommentContext = createContext<CommentProviderProps>({
// user: EMPTY_USER,
comments: [], comments: [],
hasMore: false, hasMore: false,
lastSeenCurrent: null, lastSeenCurrent: null,
isLoading: false, isLoading: false,
isLoadingMore: false, isLoadingMore: false,
onSaveComment: async () => {}, onSaveComment: async () => undefined,
onShowImageModal: () => {}, onShowImageModal: () => {},
onLoadMoreComments: () => {}, onLoadMoreComments: () => {},
onDeleteComment: () => {}, onDeleteComment: () => {},
}); });
export const CommentContextProvider: FC<CommentProviderProps> = ({ children, ...contextValue }) => { export const CommentContextProvider: FC<CommentProviderProps> = ({
return <CommentContext.Provider value={contextValue}>{children}</CommentContext.Provider>; children,
...contextValue
}) => {
return (
<CommentContext.Provider value={contextValue}>
{children}
</CommentContext.Provider>
);
}; };
export const useCommentContext = () => useContext(CommentContext); export const useCommentContext = () => useContext(CommentContext);