mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-24 20:36:40 +07:00
89 lines
2.9 KiB
TypeScript
89 lines
2.9 KiB
TypeScript
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);
|