diff --git a/package.json b/package.json index 9faa2025..7745f9b0 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "react": "^17.0.1", "react-dom": "^17.0.1", "react-popper": "^2.2.3", - "react-redux": "^6.0.1", + "react-redux": "^7.2.2", "react-router": "^5.1.2", "react-router-dom": "^5.1.2", "react-scripts": "3.4.4", diff --git a/src/components/comment/Comment/index.tsx b/src/components/comment/Comment/index.tsx index 7608e702..b7a8b22f 100644 --- a/src/components/comment/Comment/index.tsx +++ b/src/components/comment/Comment/index.tsx @@ -5,7 +5,6 @@ import { CommentContent } from '~/components/comment/CommentContent'; import styles from './styles.module.scss'; import { nodeEditComment, nodeLockComment } from '~/redux/node/actions'; import { INodeState } from '~/redux/node/reducer'; -import { CommentForm } from '../CommentForm'; import { CommendDeleted } from '../../node/CommendDeleted'; import * as MODAL_ACTIONS from '~/redux/modal/actions'; @@ -50,17 +49,12 @@ const Comment: FC = memo( return ; } - if (Object.prototype.hasOwnProperty.call(comment_data, comment.id)) { - return ; - } - return ( ); diff --git a/src/components/comment/CommentContent/index.tsx b/src/components/comment/CommentContent/index.tsx index a1a48d40..b79f67d7 100644 --- a/src/components/comment/CommentContent/index.tsx +++ b/src/components/comment/CommentContent/index.tsx @@ -1,112 +1,116 @@ -import React, { FC, useMemo, memo, createElement, useCallback, Fragment } from 'react'; +import React, { createElement, FC, Fragment, memo, useCallback, useMemo, useState } from 'react'; import { IComment, IFile } from '~/redux/types'; -import { path } from 'ramda'; -import { formatCommentText, getURL, getPrettyDate } from '~/utils/dom'; +import { append, assocPath, path } from 'ramda'; +import { formatCommentText, getPrettyDate, getURL } from '~/utils/dom'; import { Group } from '~/components/containers/Group'; import styles from './styles.module.scss'; import { UPLOAD_TYPES } from '~/redux/uploads/constants'; -import { assocPath } from 'ramda'; -import { append } from 'ramda'; import reduce from 'ramda/es/reduce'; import { AudioPlayer } from '~/components/media/AudioPlayer'; import classnames from 'classnames'; import { PRESETS } from '~/constants/urls'; import { COMMENT_BLOCK_RENDERERS } from '~/constants/comment'; -import { nodeLockComment, nodeEditComment } from '~/redux/node/actions'; +import { nodeLockComment } from '~/redux/node/actions'; import { CommentMenu } from '../CommentMenu'; import * as MODAL_ACTIONS from '~/redux/modal/actions'; +import { LocalCommentForm } from '~/components/comment/LocalCommentForm'; +import { useShallowSelect } from '~/utils/hooks/useShallowSelect'; +import { selectNode } from '~/redux/node/selectors'; interface IProps { comment: IComment; can_edit: boolean; onDelete: typeof nodeLockComment; - onEdit: typeof nodeEditComment; modalShowPhotoswipe: typeof MODAL_ACTIONS.modalShowPhotoswipe; } -const CommentContent: FC = memo( - ({ comment, can_edit, onDelete, onEdit, modalShowPhotoswipe }) => { - const groupped = useMemo>( - () => - reduce( - (group, file) => assocPath([file.type], append(file, group[file.type]), group), - {}, - comment.files - ), - [comment] - ); +const CommentContent: FC = memo(({ comment, can_edit, onDelete, modalShowPhotoswipe }) => { + const [isEditing, setIsEditing] = useState(false); + const { current } = useShallowSelect(selectNode); - const onLockClick = useCallback(() => { - onDelete(comment.id, !comment.deleted_at); - }, [comment, onDelete]); + const startEditing = useCallback(() => setIsEditing(true), [setIsEditing]); + const stopEditing = useCallback(() => setIsEditing(false), [setIsEditing]); - const onEditClick = useCallback(() => { - onEdit(comment.id); - }, [comment, onEdit]); + const groupped = useMemo>( + () => + reduce( + (group, file) => assocPath([file.type], append(file, group[file.type]), group), + {}, + comment.files + ), + [comment] + ); - const menu = useMemo( - () => can_edit && , - [can_edit, comment, onEditClick, onLockClick] - ); + const onLockClick = useCallback(() => { + onDelete(comment.id, !comment.deleted_at); + }, [comment, onDelete]); - const blocks = useMemo( - () => - !!comment.text.trim() - ? formatCommentText(path(['user', 'username'], comment), comment.text) - : [], - [comment.text] - ); + const menu = useMemo( + () => can_edit && , + [can_edit, startEditing, onLockClick] + ); - return ( -
- {comment.text && ( - - {menu} + const blocks = useMemo( + () => + !!comment.text.trim() + ? formatCommentText(path(['user', 'username'], comment), comment.text) + : [], + [comment] + ); - - {blocks.map( - (block, key) => - COMMENT_BLOCK_RENDERERS[block.type] && - createElement(COMMENT_BLOCK_RENDERERS[block.type], { block, key }) - )} - + if (isEditing) { + return ; + } -
{getPrettyDate(comment.created_at)}
+ return ( +
+ {comment.text && ( + + {menu} + + + {blocks.map( + (block, key) => + COMMENT_BLOCK_RENDERERS[block.type] && + createElement(COMMENT_BLOCK_RENDERERS[block.type], { block, key }) + )} - )} - {groupped.image && groupped.image.length > 0 && ( -
- {menu} +
{getPrettyDate(comment.created_at)}
+ + )} -
- {groupped.image.map((file, index) => ( -
modalShowPhotoswipe(groupped.image, index)}> - {file.name} -
- ))} -
+ {groupped.image && groupped.image.length > 0 && ( +
+ {menu} -
{getPrettyDate(comment.created_at)}
-
- )} - - {groupped.audio && groupped.audio.length > 0 && ( - - {groupped.audio.map(file => ( -
- {menu} - - - -
{getPrettyDate(comment.created_at)}
+
+ {groupped.image.map((file, index) => ( +
modalShowPhotoswipe(groupped.image, index)}> + {file.name}
))} - - )} -
- ); - } -); +
+ +
{getPrettyDate(comment.created_at)}
+
+ )} + + {groupped.audio && groupped.audio.length > 0 && ( + + {groupped.audio.map(file => ( +
+ {menu} + + + +
{getPrettyDate(comment.created_at)}
+
+ ))} +
+ )} +
+ ); +}); export { CommentContent }; diff --git a/src/components/comment/CommentForm/index.tsx b/src/components/comment/CommentForm/index.tsx index 559b7cea..ee246687 100644 --- a/src/components/comment/CommentForm/index.tsx +++ b/src/components/comment/CommentForm/index.tsx @@ -1,12 +1,4 @@ -import React, { - FC, - KeyboardEventHandler, - memo, - useCallback, - useEffect, - useMemo, - useState, -} from 'react'; +import React, { FC, KeyboardEventHandler, memo, useCallback, useEffect, useMemo, useState } from 'react'; import { Textarea } from '~/components/input/Textarea'; import styles from './styles.module.scss'; import { Filler } from '~/components/containers/Filler'; @@ -30,7 +22,6 @@ import { CommentFormAttaches } from '~/components/comment/CommentFormAttaches'; import { CommentFormAttachButtons } from '~/components/comment/CommentFormAttachButtons'; import { CommentFormDropzone } from '~/components/comment/CommentFormDropzone'; import { CommentFormFormatButtons } from '~/components/comment/CommentFormFormatButtons'; -import { LocalCommentForm } from '~/components/comment/LocalCommentForm'; const mapStateToProps = (state: IState) => ({ node: selectNode(state), @@ -237,8 +228,6 @@ const CommentFormUnconnected: FC = memo(
- - ); } diff --git a/src/components/comment/LocalCommentForm/index.tsx b/src/components/comment/LocalCommentForm/index.tsx index d3dcac18..1511cb91 100644 --- a/src/components/comment/LocalCommentForm/index.tsx +++ b/src/components/comment/LocalCommentForm/index.tsx @@ -1,30 +1,64 @@ import React, { FC, useState } from 'react'; -import { CommentFormValues, useCommentFormFormik } from '~/utils/hooks/useCommentFormFormik'; +import { useCommentFormFormik } from '~/utils/hooks/useCommentFormFormik'; import { FormikProvider } from 'formik'; import { LocalCommentFormTextarea } from '~/components/comment/LocalCommentFormTextarea'; import { Button } from '~/components/input/Button'; +import { FileUploaderProvider, useFileUploader } from '~/utils/hooks/fileUploader'; +import { UPLOAD_SUBJECTS, UPLOAD_TARGETS } from '~/redux/uploads/constants'; +import { CommentFormAttachButtons } from '~/components/comment/CommentFormAttachButtons'; +import { CommentFormFormatButtons } from '~/components/comment/CommentFormFormatButtons'; +import { LocalCommentFormAttaches } from '~/components/comment/LocalCommentFormAttaches'; +import { LoaderCircle } from '~/components/input/LoaderCircle'; +import { IComment, INode } from '~/redux/types'; +import { EMPTY_COMMENT } from '~/redux/node/constants'; -interface IProps {} +interface IProps { + comment?: IComment; + nodeId: INode['id']; + isBefore?: boolean; + onCancelEdit?: () => void; +} -const initialValues: CommentFormValues = { - text: '', - images: [], - songs: [], -}; - -const LocalCommentForm: FC = () => { +const LocalCommentForm: FC = ({ comment, nodeId, isBefore, onCancelEdit }) => { const [textarea, setTextarea] = useState(); - const { formik } = useCommentFormFormik(initialValues); + const uploader = useFileUploader(UPLOAD_SUBJECTS.COMMENT, UPLOAD_TARGETS.COMMENTS); + const formik = useCommentFormFormik( + comment || EMPTY_COMMENT, + nodeId, + uploader, + onCancelEdit, + isBefore + ); + const isLoading = formik.isSubmitting || uploader.isUploading; + const isEditing = !!comment?.id; return (
- - {formik.isSubmitting &&
LOADING
} - {!!formik.status &&
error: {formik.status}
} - + + + + + + + + {isLoading && } + + {isEditing && ( + + )} + + +
); diff --git a/src/components/comment/LocalCommentFormAttaches/index.tsx b/src/components/comment/LocalCommentFormAttaches/index.tsx new file mode 100644 index 00000000..2b5094f3 --- /dev/null +++ b/src/components/comment/LocalCommentFormAttaches/index.tsx @@ -0,0 +1,116 @@ +import React, { FC, useCallback, useMemo } from 'react'; +import styles from '~/components/comment/CommentForm/styles.module.scss'; +import { SortableImageGrid } from '~/components/editors/SortableImageGrid'; +import { SortableAudioGrid } from '~/components/editors/SortableAudioGrid'; +import { IFile } from '~/redux/types'; +import { SortEnd } from 'react-sortable-hoc'; +import { moveArrItem } from '~/utils/fn'; +import { useDropZone } from '~/utils/hooks'; +import { COMMENT_FILE_TYPES, UPLOAD_TYPES } from '~/redux/uploads/constants'; +import { useFileUploaderContext } from '~/utils/hooks/fileUploader'; + +const LocalCommentFormAttaches: FC = () => { + const { files, pending, setFiles, uploadFiles } = useFileUploaderContext(); + + const images = useMemo(() => files.filter(file => file && file.type === UPLOAD_TYPES.IMAGE), [ + files, + ]); + + const pendingImages = useMemo(() => pending.filter(item => item.type === UPLOAD_TYPES.IMAGE), [ + pending, + ]); + + const audios = useMemo(() => files.filter(file => file && file.type === UPLOAD_TYPES.AUDIO), [ + files, + ]); + + const pendingAudios = useMemo(() => pending.filter(item => item.type === UPLOAD_TYPES.AUDIO), [ + pending, + ]); + + const onDrop = useDropZone(uploadFiles, COMMENT_FILE_TYPES); + + const hasImageAttaches = images.length > 0 || pendingImages.length > 0; + const hasAudioAttaches = audios.length > 0 || pendingAudios.length > 0; + const hasAttaches = hasImageAttaches || hasAudioAttaches; + + const onImageMove = useCallback( + ({ oldIndex, newIndex }: SortEnd) => { + setFiles([ + ...audios, + ...(moveArrItem( + oldIndex, + newIndex, + images.filter(file => !!file) + ) as IFile[]), + ]); + }, + [images, audios, setFiles] + ); + + const onAudioMove = useCallback( + ({ oldIndex, newIndex }: SortEnd) => { + setFiles([ + ...images, + ...(moveArrItem( + oldIndex, + newIndex, + audios.filter(file => !!file) + ) as IFile[]), + ]); + }, + [images, audios, setFiles] + ); + + const onFileDelete = useCallback( + (fileId: IFile['id']) => { + setFiles(files.filter(file => file.id !== fileId)); + }, + [setFiles, files] + ); + + const onAudioTitleChange = useCallback( + (fileId: IFile['id'], title: IFile['metadata']['title']) => { + setFiles( + files.map(file => + file.id === fileId ? { ...file, metadata: { ...file.metadata, title } } : file + ) + ); + }, + [files, setFiles] + ); + + return ( + hasAttaches && ( +
+ {hasImageAttaches && ( + + )} + + {hasAudioAttaches && ( + + )} +
+ ) + ); +}; + +export { LocalCommentFormAttaches }; diff --git a/src/components/input/Textarea/index.tsx b/src/components/input/Textarea/index.tsx index fc2019ae..d51fa31e 100644 --- a/src/components/input/Textarea/index.tsx +++ b/src/components/input/Textarea/index.tsx @@ -6,7 +6,7 @@ import React, { useCallback, useEffect, useRef, - useState, + useState } from 'react'; import classNames from 'classnames'; import autosize from 'autosize'; @@ -59,14 +59,21 @@ const Textarea = memo( const onFocus = useCallback(() => setFocused(true), [setFocused]); const onBlur = useCallback(() => setFocused(false), [setFocused]); + useEffect(() => { + const target = ref?.current; + if (!target) return; + + autosize(target); + setRef(target); + + return () => autosize.destroy(target); + }, [ref, setRef]); + useEffect(() => { if (!ref.current) return; - autosize(ref.current); - setRef(ref.current); - - return () => autosize.destroy(ref.current); - }, [ref.current]); + autosize.update(ref.current); + }, [value]); return (
( onFocus={onFocus} onBlur={onBlur} style={{ - maxHeight: maxRows * 20, + // maxHeight: maxRows * 20, minHeight: minRows * 20, }} {...props} diff --git a/src/components/node/NodeCommentForm/index.tsx b/src/components/node/NodeCommentForm/index.tsx index ee6647b2..87638160 100644 --- a/src/components/node/NodeCommentForm/index.tsx +++ b/src/components/node/NodeCommentForm/index.tsx @@ -1,32 +1,25 @@ -import React, { FC, useCallback, KeyboardEventHandler, useEffect, useMemo } from 'react'; -import { Textarea } from '~/components/input/Textarea'; +import React, { FC } from 'react'; import { CommentWrapper } from '~/components/containers/CommentWrapper'; -import styles from './styles.module.scss'; -import { Filler } from '~/components/containers/Filler'; -import { Button } from '~/components/input/Button'; -import { assocPath } from 'ramda'; -import { InputHandler, IFileWithUUID, IFile } from '~/redux/types'; import { connect } from 'react-redux'; -import * as NODE_ACTIONS from '~/redux/node/actions'; -import { selectNode } from '~/redux/node/selectors'; -import * as UPLOAD_ACTIONS from '~/redux/uploads/actions'; -import { selectUploads } from '~/redux/uploads/selectors'; -import { IState } from '~/redux/store'; -import { selectUser, selectAuthUser } from '~/redux/auth/selectors'; +import { selectAuthUser } from '~/redux/auth/selectors'; import { CommentForm } from '../../comment/CommentForm'; +import { LocalCommentForm } from '~/components/comment/LocalCommentForm'; +import { INode } from '~/redux/types'; const mapStateToProps = state => ({ user: selectAuthUser(state), }); type IProps = ReturnType & { - is_before?: boolean; + isBefore?: boolean; + nodeId: INode['id']; }; -const NodeCommentFormUnconnected: FC = ({ user, is_before }) => { +const NodeCommentFormUnconnected: FC = ({ user, isBefore, nodeId }) => { return ( - + + ); }; diff --git a/src/containers/node/BorisLayout/index.tsx b/src/containers/node/BorisLayout/index.tsx index 46a8f4c1..7b252bf6 100644 --- a/src/containers/node/BorisLayout/index.tsx +++ b/src/containers/node/BorisLayout/index.tsx @@ -45,7 +45,7 @@ type IProps = ReturnType & const id = 696; const BorisLayoutUnconnected: FC = ({ - node: { is_loading, is_loading_comments, comments = [], comment_data, comment_count }, + node: { is_loading, is_loading_comments, comments = [], comment_data, comment_count, id }, user, user: { is_user, last_seen_boris }, nodeLoadNode, @@ -92,7 +92,7 @@ const BorisLayoutUnconnected: FC = ({
- {is_user && } + {is_user && } {is_loading_comments ? ( diff --git a/src/containers/node/NodeLayout/index.tsx b/src/containers/node/NodeLayout/index.tsx index 12d69f3c..0484b242 100644 --- a/src/containers/node/NodeLayout/index.tsx +++ b/src/containers/node/NodeLayout/index.tsx @@ -12,7 +12,7 @@ import { NodeNoComments } from '~/components/node/NodeNoComments'; import { NodeRelated } from '~/components/node/NodeRelated'; import { NodeComments } from '~/components/node/NodeComments'; import { NodeTags } from '~/components/node/NodeTags'; -import { INodeComponentProps, NODE_COMPONENTS, NODE_HEADS, NODE_INLINES, } from '~/redux/node/constants'; +import { INodeComponentProps, NODE_COMPONENTS, NODE_HEADS, NODE_INLINES } from '~/redux/node/constants'; import { selectUser } from '~/redux/auth/selectors'; import { pick } from 'ramda'; import { NodeRelatedPlaceholder } from '~/components/node/NodeRelated/placeholder'; @@ -30,6 +30,7 @@ import { selectModal } from '~/redux/modal/selectors'; import { SidebarRouter } from '~/containers/main/SidebarRouter'; import { ITag } from '~/redux/types'; import { URLS } from '~/constants/urls'; +import { useShallowSelect } from '~/utils/hooks/useShallowSelect'; const mapStateToProps = (state: IState) => ({ node: selectNode(state), @@ -60,15 +61,6 @@ const NodeLayoutUnconnected: FC = memo( match: { params: { id }, }, - node: { - is_loading, - is_loading_comments, - comments = [], - current: node, - related, - comment_data, - comment_count, - }, modal: { is_shown: is_modal_shown }, user, user: { is_user }, @@ -86,7 +78,15 @@ const NodeLayoutUnconnected: FC = memo( }) => { const [layout, setLayout] = useState({}); const history = useHistory(); - + const { + is_loading, + is_loading_comments, + comments = [], + current: node, + related, + comment_data, + comment_count, + } = useShallowSelect(selectNode); const updateLayout = useCallback(() => setLayout({}), []); useEffect(() => { @@ -193,7 +193,7 @@ const NodeLayoutUnconnected: FC = memo( /> )} - {is_user && !is_loading && } + {is_user && !is_loading && }
diff --git a/src/styles/common/inputs.module.scss b/src/styles/common/inputs.module.scss index 99016f58..22c566be 100644 --- a/src/styles/common/inputs.module.scss +++ b/src/styles/common/inputs.module.scss @@ -230,6 +230,7 @@ padding: 12px 0; box-sizing: border-box; width: 100%; + max-height: 60vh; } .status, diff --git a/src/utils/hooks/fileUploader.tsx b/src/utils/hooks/fileUploader.tsx new file mode 100644 index 00000000..9a3bc743 --- /dev/null +++ b/src/utils/hooks/fileUploader.tsx @@ -0,0 +1,77 @@ +import React, { createContext, FC, useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import { IFile, IFileWithUUID } from '~/redux/types'; +import { UPLOAD_SUBJECTS, UPLOAD_TARGETS } from '~/redux/uploads/constants'; +import { getFileType } from '~/utils/uploader'; +import uuid from 'uuid4'; +import { useDispatch } from 'react-redux'; +import { uploadUploadFiles } from '~/redux/uploads/actions'; +import { useShallowSelect } from '~/utils/hooks/useShallowSelect'; +import { selectUploads } from '~/redux/uploads/selectors'; + +export const useFileUploader = ( + subject: typeof UPLOAD_SUBJECTS[keyof typeof UPLOAD_SUBJECTS], + target: typeof UPLOAD_TARGETS[keyof typeof UPLOAD_TARGETS] +) => { + const dispatch = useDispatch(); + const { files: uploadedFiles, statuses } = useShallowSelect(selectUploads); + + const [files, setFiles] = useState([]); + const [pendingIDs, setPendingIDs] = useState([]); + + const uploadFiles = useCallback( + (files: File[]) => { + const items: IFileWithUUID[] = files.map( + (file: File): IFileWithUUID => ({ + file, + temp_id: uuid(), + subject, + target, + type: getFileType(file), + }) + ); + + const temps = items.map(file => file.temp_id); + + setPendingIDs([...pendingIDs, ...temps]); + dispatch(uploadUploadFiles(items)); + }, + [pendingIDs, setPendingIDs, dispatch, subject, target] + ); + + useEffect(() => { + const added = pendingIDs + .map(temp_uuid => statuses[temp_uuid] && statuses[temp_uuid].uuid) + .map(el => !!el && uploadedFiles[el]) + .filter(el => !!el && !files.some(file => file && file.id === el.id)); + + const newPending = pendingIDs.filter( + temp_id => + statuses[temp_id] && + (!statuses[temp_id].uuid || !added.some(file => file.id === statuses[temp_id].uuid)) + ); + + if (added.length) { + setPendingIDs(newPending); + setFiles([...files, ...added]); + } + }, [statuses, files, pendingIDs, uploadedFiles]); + + const pending = useMemo(() => pendingIDs.map(id => statuses[id]).filter(el => !!el), [ + statuses, + pendingIDs, + ]); + + const isLoading = pending.length > 0; + + return { uploadFiles, pending, files, setFiles, isUploading: isLoading }; +}; + +export type FileUploader = ReturnType; +const FileUploaderContext = createContext(null); + +export const FileUploaderProvider: FC<{ value: FileUploader; children }> = ({ + value, + children, +}) => {children}; + +export const useFileUploaderContext = () => useContext(FileUploaderContext); diff --git a/src/utils/hooks/useCommentFormFormik.ts b/src/utils/hooks/useCommentFormFormik.ts index 9cc1302a..073f4d9a 100644 --- a/src/utils/hooks/useCommentFormFormik.ts +++ b/src/utils/hooks/useCommentFormFormik.ts @@ -1,64 +1,77 @@ -import { IComment, IFile } from '~/redux/types'; +import { IComment, INode } from '~/redux/types'; import { useCallback, useRef } from 'react'; import { FormikHelpers, useFormik, useFormikContext } from 'formik'; import { array, object, string } from 'yup'; - -export interface CommentFormValues { - text: string; - images: IFile[]; - songs: IFile[]; -} +import { FileUploader } from '~/utils/hooks/fileUploader'; const validationSchema = object().shape({ text: string(), - images: array(), - songs: array(), + files: array(), }); -const submitComment = async ( - id: IComment['id'], - values: CommentFormValues, - callback: (e: string) => void +const submitComment = ( + id: INode['id'], + values: IComment, + isBefore: boolean, + callback: (e?: string) => void ) => { - await new Promise(resolve => setTimeout(resolve, 500)); - callback('wrong'); + console.log('Submitting', id, values); + new Promise(resolve => setTimeout(resolve, 500)).then(() => callback()); }; -export const useCommentFormFormik = (values: CommentFormValues) => { +const onSuccess = ({ resetForm, setStatus, setSubmitting }: FormikHelpers) => ( + e: string +) => { + setSubmitting(false); + + if (e) { + setStatus(e); + return; + } + + if (resetForm) { + resetForm(); + } +}; + +export const useCommentFormFormik = ( + values: IComment, + nodeId: INode['id'], + uploader: FileUploader, + stopEditing?: () => void, + isBefore: boolean = false +) => { const { current: initialValues } = useRef(values); - const onSuccess = useCallback( - ({ resetForm, setStatus, setSubmitting }: FormikHelpers) => (e: string) => { - setSubmitting(false); - - if (e) { - setStatus(e); - return; - } - - if (resetForm) { - resetForm(); - } - }, - [] - ); - const onSubmit = useCallback( - (values: CommentFormValues, helpers: FormikHelpers) => { + (values: IComment, helpers: FormikHelpers) => { helpers.setSubmitting(true); - submitComment(0, values, onSuccess(helpers)); + submitComment( + nodeId, + { + ...values, + files: uploader.files, + }, + isBefore, + onSuccess(helpers) + ); }, - [values, onSuccess] + [isBefore, nodeId, uploader.files] ); - const formik = useFormik({ + const onReset = useCallback(() => { + uploader.setFiles([]); + + if (stopEditing) stopEditing(); + }, [uploader, stopEditing]); + + return useFormik({ initialValues, validationSchema, onSubmit, initialStatus: '', + onReset, }); - - return { formik }; }; -export const useCommentFormContext = () => useFormikContext(); +export const useCommentFormContext = () => useFormikContext(); diff --git a/src/utils/hooks/useShallowSelect.ts b/src/utils/hooks/useShallowSelect.ts new file mode 100644 index 00000000..dcc33961 --- /dev/null +++ b/src/utils/hooks/useShallowSelect.ts @@ -0,0 +1,5 @@ +import { shallowEqual, useSelector } from 'react-redux'; +import { IState } from '~/redux/store'; + +export const useShallowSelect = any>(selector: T): ReturnType => + useSelector(selector, shallowEqual);