mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-24 20:36:40 +07:00
add comments for guests
This commit is contained in:
parent
cbf7b1f616
commit
8abf6177b5
16 changed files with 278 additions and 194 deletions
|
@ -21,7 +21,7 @@ type IProps = HTMLAttributes<HTMLDivElement> & {
|
|||
isSame?: boolean;
|
||||
canEdit?: boolean;
|
||||
highlighted?: boolean;
|
||||
saveComment: (data: IComment) => Promise<unknown>;
|
||||
saveComment: (data: IComment) => Promise<IComment | undefined>;
|
||||
onDelete: (id: IComment['id'], isLocked: boolean) => void;
|
||||
onShowImageModal: (images: IFile[], index: number) => void;
|
||||
};
|
||||
|
|
|
@ -7,11 +7,15 @@ import { IUser } from '~/types/auth';
|
|||
import { path } from '~/utils/ramda';
|
||||
|
||||
interface Props {
|
||||
user: IUser;
|
||||
user?: IUser;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const CommentAvatar: FC<Props> = ({ user, className }) => {
|
||||
if (!user) {
|
||||
return <Avatar className={className} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<MenuButton
|
||||
position="auto"
|
||||
|
|
|
@ -22,6 +22,7 @@ import { IComment, IFile } from '~/types';
|
|||
import { formatCommentText, getPrettyDate, getURL } from '~/utils/dom';
|
||||
import { append, assocPath, path, reduce } from '~/utils/ramda';
|
||||
|
||||
import { CommentEditingForm } from '../CommentEditingForm';
|
||||
import { CommentImageGrid } from '../CommentImageGrid';
|
||||
import { CommentMenu } from '../CommentMenu';
|
||||
|
||||
|
@ -32,7 +33,7 @@ interface IProps {
|
|||
nodeId: number;
|
||||
comment: IComment;
|
||||
canEdit: boolean;
|
||||
saveComment: (data: IComment) => Promise<unknown>;
|
||||
saveComment: (data: IComment) => Promise<IComment | undefined>;
|
||||
onDelete: (id: IComment['id'], isLocked: boolean) => void;
|
||||
onShowImageModal: (images: IFile[], index: number) => void;
|
||||
}
|
||||
|
@ -98,7 +99,7 @@ const CommentContent: FC<IProps> = memo(
|
|||
|
||||
if (isEditing) {
|
||||
return (
|
||||
<CommentForm
|
||||
<CommentEditingForm
|
||||
saveComment={saveComment}
|
||||
nodeId={nodeId}
|
||||
comment={comment}
|
||||
|
|
38
src/components/comment/CommentEditingForm/index.tsx
Normal file
38
src/components/comment/CommentEditingForm/index.tsx
Normal 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 };
|
|
@ -1,4 +1,4 @@
|
|||
import React, { FC, useCallback, useState } from 'react';
|
||||
import { FC, useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { FormikProvider } from 'formik';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
|
@ -12,108 +12,116 @@ import { Button } from '~/components/input/Button';
|
|||
import { UploadDropzone } from '~/components/upload/UploadDropzone';
|
||||
import { ERROR_LITERAL } from '~/constants/errors';
|
||||
import { EMPTY_COMMENT } from '~/constants/node';
|
||||
import { UploadSubject, UploadTarget } from '~/constants/uploads';
|
||||
import { useCommentFormFormik } from '~/hooks/comments/useCommentFormFormik';
|
||||
import { useUploader } from '~/hooks/data/useUploader';
|
||||
import { useInputPasteUpload } from '~/hooks/dom/useInputPasteUpload';
|
||||
import { IComment, INode } from '~/types';
|
||||
import { UploaderContextProvider } from '~/utils/context/UploaderContextProvider';
|
||||
import {
|
||||
UploaderContextProvider,
|
||||
useUploaderContext,
|
||||
} from '~/utils/context/UploaderContextProvider';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface IProps {
|
||||
comment?: IComment;
|
||||
nodeId: INode['id'];
|
||||
saveComment: (data: IComment) => Promise<unknown>;
|
||||
allowUploads?: boolean;
|
||||
|
||||
saveComment: (data: IComment) => Promise<IComment | undefined>;
|
||||
onCancelEdit?: () => void;
|
||||
}
|
||||
|
||||
const CommentForm: FC<IProps> = observer(({ comment, nodeId, saveComment, onCancelEdit }) => {
|
||||
const [textarea, setTextArea] = useState<HTMLTextAreaElement | null>(null);
|
||||
const uploader = useUploader(UploadSubject.Comment, UploadTarget.Comments, comment?.files);
|
||||
const formik = useCommentFormFormik(
|
||||
comment || EMPTY_COMMENT,
|
||||
nodeId,
|
||||
uploader,
|
||||
saveComment,
|
||||
onCancelEdit
|
||||
);
|
||||
const isLoading = formik.isSubmitting || uploader.isUploading;
|
||||
const isEditing = !!comment?.id;
|
||||
const CommentForm: FC<IProps> = observer(
|
||||
({ comment, allowUploads, saveComment, onCancelEdit }) => {
|
||||
const [textarea, setTextArea] = useState<HTMLTextAreaElement | null>(null);
|
||||
const uploader = useUploaderContext();
|
||||
|
||||
const clearError = useCallback(() => {
|
||||
if (formik.status) {
|
||||
formik.setStatus('');
|
||||
}
|
||||
const formik = useCommentFormFormik(
|
||||
comment || EMPTY_COMMENT,
|
||||
uploader.files,
|
||||
uploader.setFiles,
|
||||
saveComment,
|
||||
onCancelEdit,
|
||||
);
|
||||
const isLoading = formik.isSubmitting || uploader.isUploading;
|
||||
const isEditing = !!comment?.id;
|
||||
|
||||
if (formik.errors.text) {
|
||||
formik.setErrors({
|
||||
...formik.errors,
|
||||
text: '',
|
||||
});
|
||||
}
|
||||
}, [formik]);
|
||||
const clearError = useCallback(() => {
|
||||
if (formik.status) {
|
||||
formik.setStatus('');
|
||||
}
|
||||
|
||||
const error = formik.status || formik.errors.text;
|
||||
const onPaste = useInputPasteUpload(uploader.uploadFiles);
|
||||
if (formik.errors.text) {
|
||||
formik.setErrors({
|
||||
...formik.errors,
|
||||
text: '',
|
||||
});
|
||||
}
|
||||
}, [formik]);
|
||||
|
||||
return (
|
||||
<UploadDropzone onUpload={uploader.uploadFiles}>
|
||||
const error = formik.status || formik.errors.text;
|
||||
const onPaste = useInputPasteUpload(uploader.uploadFiles);
|
||||
|
||||
return (
|
||||
<form onSubmit={formik.handleSubmit} className={styles.wrap}>
|
||||
<FormikProvider value={formik}>
|
||||
<UploaderContextProvider value={uploader}>
|
||||
<div className={styles.input}>
|
||||
<LocalCommentFormTextarea onPaste={onPaste} ref={setTextArea} />
|
||||
<div className={styles.input}>
|
||||
<LocalCommentFormTextarea onPaste={onPaste} ref={setTextArea} />
|
||||
|
||||
{!!error && (
|
||||
<div className={styles.error} onClick={clearError}>
|
||||
{ERROR_LITERAL[error] || error}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{!!error && (
|
||||
<div className={styles.error} onClick={clearError}>
|
||||
{ERROR_LITERAL[error] || error}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<CommentFormAttaches />
|
||||
{allowUploads && <CommentFormAttaches />}
|
||||
|
||||
<div className={styles.buttons}>
|
||||
<div className={styles.buttons}>
|
||||
{allowUploads && (
|
||||
<div className={styles.button_column}>
|
||||
<CommentFormAttachButtons onUpload={uploader.uploadFiles} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={styles.button_column}>
|
||||
{!!textarea && (
|
||||
<CommentFormFormatButtons
|
||||
element={textarea}
|
||||
handler={formik.handleChange('text')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Filler />
|
||||
|
||||
<div className={styles.button_column}>
|
||||
{isEditing && (
|
||||
<Button size="small" color="link" type="button" onClick={onCancelEdit}>
|
||||
Отмена
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
size="small"
|
||||
color="gray"
|
||||
iconRight={!isEditing ? 'enter' : 'check'}
|
||||
disabled={isLoading}
|
||||
loading={isLoading}
|
||||
>
|
||||
{!isEditing ? 'Сказать' : 'Сохранить'}
|
||||
</Button>
|
||||
</div>
|
||||
<div className={styles.button_column}>
|
||||
{!!textarea && (
|
||||
<CommentFormFormatButtons
|
||||
element={textarea}
|
||||
handler={formik.handleChange('text')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</UploaderContextProvider>
|
||||
|
||||
<Filler />
|
||||
|
||||
<div className={styles.button_column}>
|
||||
{isEditing && (
|
||||
<Button
|
||||
size="small"
|
||||
color="link"
|
||||
type="button"
|
||||
onClick={onCancelEdit}
|
||||
>
|
||||
Отмена
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
size="small"
|
||||
color="gray"
|
||||
iconRight={!isEditing ? 'enter' : 'check'}
|
||||
disabled={isLoading}
|
||||
loading={isLoading}
|
||||
>
|
||||
{!isEditing ? 'Сказать' : 'Сохранить'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</FormikProvider>
|
||||
</form>
|
||||
</UploadDropzone>
|
||||
);
|
||||
});
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export { CommentForm };
|
||||
|
|
|
@ -10,7 +10,7 @@ import { DivProps } from '~/utils/types';
|
|||
import styles from './styles.module.scss';
|
||||
|
||||
type IProps = DivProps & {
|
||||
user: IUser;
|
||||
user?: IUser;
|
||||
isEmpty?: boolean;
|
||||
isLoading?: boolean;
|
||||
isForm?: boolean;
|
||||
|
@ -36,7 +36,10 @@ const CommentWrapper: FC<IProps> = ({
|
|||
{...props}
|
||||
>
|
||||
<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>
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ div.thumb_image {
|
|||
background-size: cover;
|
||||
flex: 0 0 $comment_height;
|
||||
will-change: transform;
|
||||
cursor: pointer;
|
||||
cursor: default;
|
||||
|
||||
@include tablet {
|
||||
height: 32px;
|
||||
|
@ -105,6 +105,10 @@ div.thumb_image {
|
|||
}
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.thumb_user {
|
||||
display: none;
|
||||
flex: 1;
|
||||
|
|
|
@ -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 };
|
|
@ -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 }
|
||||
);
|
Loading…
Add table
Add a link
Reference in a new issue