mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 12:56:41 +07:00
#34 made local comment form uploads
This commit is contained in:
parent
f45e34f330
commit
051b199d5d
14 changed files with 422 additions and 189 deletions
77
src/utils/hooks/fileUploader.tsx
Normal file
77
src/utils/hooks/fileUploader.tsx
Normal file
|
@ -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<IFile[]>([]);
|
||||
const [pendingIDs, setPendingIDs] = useState<string[]>([]);
|
||||
|
||||
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<typeof useFileUploader>;
|
||||
const FileUploaderContext = createContext<FileUploader>(null);
|
||||
|
||||
export const FileUploaderProvider: FC<{ value: FileUploader; children }> = ({
|
||||
value,
|
||||
children,
|
||||
}) => <FileUploaderContext.Provider value={value}>{children}</FileUploaderContext.Provider>;
|
||||
|
||||
export const useFileUploaderContext = () => useContext(FileUploaderContext);
|
|
@ -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<IComment>) => (
|
||||
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<CommentFormValues>) => (e: string) => {
|
||||
setSubmitting(false);
|
||||
|
||||
if (e) {
|
||||
setStatus(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (resetForm) {
|
||||
resetForm();
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const onSubmit = useCallback(
|
||||
(values: CommentFormValues, helpers: FormikHelpers<CommentFormValues>) => {
|
||||
(values: IComment, helpers: FormikHelpers<IComment>) => {
|
||||
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<CommentFormValues>();
|
||||
export const useCommentFormContext = () => useFormikContext<IComment>();
|
||||
|
|
5
src/utils/hooks/useShallowSelect.ts
Normal file
5
src/utils/hooks/useShallowSelect.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { shallowEqual, useSelector } from 'react-redux';
|
||||
import { IState } from '~/redux/store';
|
||||
|
||||
export const useShallowSelect = <T extends (state: IState) => any>(selector: T): ReturnType<T> =>
|
||||
useSelector(selector, shallowEqual);
|
Loading…
Add table
Add a link
Reference in a new issue