1
0
Fork 0
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:
Fedor Katurov 2022-01-06 21:04:14 +07:00
parent 140e36b6b7
commit 95b92b643f
38 changed files with 398 additions and 691 deletions

View file

@ -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,

View file

@ -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);

View 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,
};
};

View file

@ -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,
]);
};

View file

@ -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>
) => {

View file

@ -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,
]);
};