mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 12:56:41 +07:00
removed upload redux store
This commit is contained in:
parent
140e36b6b7
commit
95b92b643f
38 changed files with 398 additions and 691 deletions
|
@ -2,9 +2,9 @@ import { IComment, INode } from '~/redux/types';
|
|||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { FormikHelpers, useFormik, useFormikContext } from 'formik';
|
||||
import { array, object, string } from 'yup';
|
||||
import { FileUploader } from '~/hooks/data/useFileUploader';
|
||||
import { showErrorToast } from '~/utils/errors/showToast';
|
||||
import { hasPath, path } from 'ramda';
|
||||
import { Uploader } from '~/utils/context/UploaderContextProvider';
|
||||
|
||||
const validationSchema = object().shape({
|
||||
text: string(),
|
||||
|
@ -31,7 +31,7 @@ const onSuccess = ({ resetForm, setSubmitting, setErrors }: FormikHelpers<IComme
|
|||
export const useCommentFormFormik = (
|
||||
values: IComment,
|
||||
nodeId: INode['id'],
|
||||
uploader: FileUploader,
|
||||
uploader: Uploader,
|
||||
sendData: (data: IComment) => Promise<unknown>,
|
||||
stopEditing?: () => void
|
||||
) => {
|
||||
|
@ -41,20 +41,19 @@ export const useCommentFormFormik = (
|
|||
async (values: IComment, helpers: FormikHelpers<IComment>) => {
|
||||
try {
|
||||
helpers.setSubmitting(true);
|
||||
await sendData(values);
|
||||
await sendData({ ...values, files: uploader.files });
|
||||
onSuccess(helpers)();
|
||||
} catch (error) {
|
||||
onSuccess(helpers)(error);
|
||||
}
|
||||
},
|
||||
[sendData]
|
||||
[sendData, uploader.files]
|
||||
);
|
||||
|
||||
const onReset = useCallback(() => {
|
||||
uploader.setFiles([]);
|
||||
|
||||
if (stopEditing) stopEditing();
|
||||
}, [uploader, stopEditing]);
|
||||
}, [stopEditing, uploader]);
|
||||
|
||||
const formik = useFormik({
|
||||
initialValues,
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
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 '~/hooks/data/useShallowSelect';
|
||||
import { selectUploads } from '~/redux/uploads/selectors';
|
||||
import { path } from 'ramda';
|
||||
import { IUploadStatus } from '~/redux/uploads/reducer';
|
||||
|
||||
export const useFileUploader = (
|
||||
subject: typeof UPLOAD_SUBJECTS[keyof typeof UPLOAD_SUBJECTS],
|
||||
target: typeof UPLOAD_TARGETS[keyof typeof UPLOAD_TARGETS],
|
||||
initialFiles?: IFile[]
|
||||
) => {
|
||||
const dispatch = useDispatch();
|
||||
const { files: uploadedFiles, statuses } = useShallowSelect(selectUploads);
|
||||
|
||||
const [files, setFiles] = useState<IFile[]>(initialFiles || []);
|
||||
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.filter(el => !!el.temp_id).map(file => file.temp_id!);
|
||||
|
||||
setPendingIDs([...pendingIDs, ...temps]);
|
||||
dispatch(uploadUploadFiles(items));
|
||||
},
|
||||
[pendingIDs, setPendingIDs, dispatch, subject, target]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const added = pendingIDs
|
||||
.map(temp_uuid => path([temp_uuid, 'uuid'], statuses) as IUploadStatus['uuid'])
|
||||
.filter(el => el)
|
||||
.map(el => (path([String(el)], uploadedFiles) as IFile) || undefined)
|
||||
.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, setFiles, setPendingIDs, 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 | undefined>(undefined);
|
||||
|
||||
export const FileUploaderProvider: FC<{ value: FileUploader; children }> = ({
|
||||
value,
|
||||
children,
|
||||
}) => <FileUploaderContext.Provider value={value}>{children}</FileUploaderContext.Provider>;
|
||||
|
||||
export const useFileUploaderContext = () => useContext(FileUploaderContext);
|
63
src/hooks/data/useUploader.ts
Normal file
63
src/hooks/data/useUploader.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
import { UploadSubject, UploadTarget } from '~/constants/uploads';
|
||||
import { IFile } from '~/redux/types';
|
||||
import { useCallback } from 'react';
|
||||
import { apiUploadFile } from '~/api/uploads';
|
||||
import { keys } from 'ramda';
|
||||
import { useLocalObservable } from 'mobx-react-lite';
|
||||
import { UploaderStore } from '~/store/uploader/UploaderStore';
|
||||
import { showErrorToast } from '~/utils/errors/showToast';
|
||||
import uuid from 'uuid4';
|
||||
|
||||
export const useUploader = (
|
||||
subject: UploadSubject,
|
||||
target: UploadTarget,
|
||||
initialFiles?: IFile[]
|
||||
) => {
|
||||
const store = useLocalObservable(() => new UploaderStore(initialFiles));
|
||||
|
||||
const uploadFile = useCallback(
|
||||
async (file: File) => {
|
||||
const id = uuid();
|
||||
|
||||
try {
|
||||
// TODO: pass CancelationToken for axios as cancel() to pending
|
||||
// TODO: cancel all uploads on unmount
|
||||
|
||||
const pending = await store.addPending(id, file);
|
||||
const onProgress = ({ loaded, total }) => store.updateProgress(id, loaded, total);
|
||||
const result = await apiUploadFile({ file, target, type: pending.type, onProgress });
|
||||
|
||||
store.removePending(id);
|
||||
store.addFile(result);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
store.removePending(id);
|
||||
showErrorToast(error);
|
||||
}
|
||||
},
|
||||
[store, target]
|
||||
);
|
||||
|
||||
const uploadFiles = useCallback(
|
||||
async (files: File[]) => {
|
||||
await Promise.any(files.map(file => uploadFile(file)));
|
||||
},
|
||||
[uploadFile]
|
||||
);
|
||||
|
||||
const isUploading = keys(store.pending).length > 0;
|
||||
|
||||
return {
|
||||
uploadFile,
|
||||
uploadFiles,
|
||||
files: store.files,
|
||||
filesImages: store.filesImages,
|
||||
filesAudios: store.filesAudios,
|
||||
pending: store.pending,
|
||||
pendingImages: store.pendingImages,
|
||||
pendingAudios: store.pendingAudios,
|
||||
isUploading,
|
||||
setFiles: store.setFiles,
|
||||
};
|
||||
};
|
|
@ -1,13 +1,13 @@
|
|||
import { INode } from '~/redux/types';
|
||||
import { useMemo } from 'react';
|
||||
import { UPLOAD_TYPES } from '~/redux/uploads/constants';
|
||||
import { UploadType } from '~/constants/uploads';
|
||||
|
||||
export const useNodeAudios = (node: INode) => {
|
||||
if (!node?.files) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return useMemo(() => node.files.filter(file => file && file.type === UPLOAD_TYPES.AUDIO), [
|
||||
return useMemo(() => node.files.filter(file => file && file.type === UploadType.Audio), [
|
||||
node.files,
|
||||
]);
|
||||
};
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { INode } from '~/redux/types';
|
||||
import { FileUploader } from '~/hooks/data/useFileUploader';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { FormikConfig, FormikHelpers, useFormik, useFormikContext } from 'formik';
|
||||
import { object } from 'yup';
|
||||
import { keys } from 'ramda';
|
||||
import { showErrorToast } from '~/utils/errors/showToast';
|
||||
import { Uploader } from '~/utils/context/UploaderContextProvider';
|
||||
|
||||
const validationSchema = object().shape({});
|
||||
|
||||
|
@ -31,7 +31,7 @@ const afterSubmit = ({ resetForm, setSubmitting, setErrors }: FormikHelpers<INod
|
|||
|
||||
export const useNodeFormFormik = (
|
||||
values: INode,
|
||||
uploader: FileUploader,
|
||||
uploader: Uploader,
|
||||
stopEditing: () => void,
|
||||
sendSaveRequest: (node: INode) => Promise<unknown>
|
||||
) => {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { INode } from '~/redux/types';
|
||||
import { useMemo } from 'react';
|
||||
import { UPLOAD_TYPES } from '~/redux/uploads/constants';
|
||||
import { UploadType } from '~/constants/uploads';
|
||||
|
||||
export const useNodeImages = (node: INode) => {
|
||||
return useMemo(() => node.files.filter(file => file && file.type === UPLOAD_TYPES.IMAGE), [
|
||||
return useMemo(() => node.files.filter(file => file && file.type === UploadType.Image), [
|
||||
node.files,
|
||||
]);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue