postiz/apps/frontend/src/components/media/new.uploader.tsx

206 lines
5.9 KiB
TypeScript

import React, { useCallback, useEffect, useMemo, useState } from 'react';
// @ts-ignore
import Uppy, { UploadResult } from '@uppy/core';
// @ts-ignore
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import { getUppyUploadPlugin } from '@gitroom/react/helpers/uppy.upload';
import { FileInput, ProgressBar } from '@uppy/react';
// Uppy styles
import '@uppy/core/dist/style.min.css';
import '@uppy/dashboard/dist/style.min.css';
import { useVariables } from '@gitroom/react/helpers/variable.context';
import Compressor from '@uppy/compressor';
import { useT } from '@gitroom/react/translation/get.transation.service.client';
import { useToaster } from '@gitroom/react/toaster/toaster';
import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store';
export function MultipartFileUploader({
onUploadSuccess,
allowedFileTypes,
uppRef,
}: {
// @ts-ignore
onUploadSuccess: (result: UploadResult) => void;
allowedFileTypes: string;
uppRef?: any;
}) {
const [loaded, setLoaded] = useState(false);
const [reload, setReload] = useState(false);
const onUploadSuccessExtended = useCallback(
(result: UploadResult<any, any>) => {
setReload(true);
onUploadSuccess(result);
},
[onUploadSuccess]
);
useEffect(() => {
if (reload) {
setTimeout(() => {
setReload(false);
}, 1);
}
}, [reload]);
useEffect(() => {
setLoaded(true);
}, []);
if (!loaded || reload) {
return null;
}
return (
<MultipartFileUploaderAfter
uppRef={uppRef || {}}
onUploadSuccess={onUploadSuccessExtended}
allowedFileTypes={allowedFileTypes}
/>
);
}
export function useUppyUploader(props: {
// @ts-ignore
onUploadSuccess: (result: UploadResult) => void;
allowedFileTypes: string;
}) {
const setLocked = useLaunchStore(state => state.setLocked);
const toast = useToaster();
const { storageProvider, backendUrl, disableImageCompression, transloadit } =
useVariables();
const { onUploadSuccess, allowedFileTypes } = props;
const fetch = useFetch();
return useMemo(() => {
const uppy2 = new Uppy({
autoProceed: true,
restrictions: {
maxNumberOfFiles: 5,
allowedFileTypes: allowedFileTypes.split(','),
maxFileSize: 1000000000, // Default 1GB, but we'll override with custom validation
},
});
// Custom file size validation based on file type
uppy2.addPreProcessor((fileIDs) => {
return new Promise<void>((resolve, reject) => {
const files = uppy2.getFiles();
for (const file of files) {
if (fileIDs.includes(file.id)) {
const isImage = file.type?.startsWith('image/');
const isVideo = file.type?.startsWith('video/');
const maxImageSize = 30 * 1024 * 1024; // 30MB
const maxVideoSize = 1000 * 1024 * 1024; // 1GB
if (isImage && file.size > maxImageSize) {
const error = new Error(
`Image file "${file.name}" is too large. Maximum size allowed is 30MB.`
);
uppy2.log(error.message, 'error');
uppy2.info(error.message, 'error', 5000);
toast.show(
`Image file is too large. Maximum size allowed is 30MB.`
);
uppy2.removeFile(file.id); // Remove file from queue
return reject(error);
}
if (isVideo && file.size > maxVideoSize) {
const error = new Error(
`Video file "${file.name}" is too large. Maximum size allowed is 1GB.`
);
uppy2.log(error.message, 'error');
uppy2.info(error.message, 'error', 5000);
toast.show(
`Video file is too large. Maximum size allowed is 1GB.`
);
uppy2.removeFile(file.id); // Remove file from queue
return reject(error);
}
}
}
resolve();
});
});
const { plugin, options } = getUppyUploadPlugin(
storageProvider,
fetch,
backendUrl,
transloadit
);
uppy2.use(plugin, options);
if (!disableImageCompression) {
uppy2.use(Compressor, {
convertTypes: ['image/jpeg'],
maxWidth: 1000,
maxHeight: 1000,
quality: 1,
});
}
// Set additional metadata when a file is added
uppy2.on('file-added', (file) => {
setLocked(true);
uppy2.setFileMeta(file.id, {
useCloudflare: storageProvider === 'cloudflare' ? 'true' : 'false', // Example of adding a custom field
// Add more fields as needed
});
});
uppy2.on('error', (result) => {
setLocked(false);
})
uppy2.on('complete', (result) => {
setLocked(false);
onUploadSuccess(result);
});
uppy2.on('upload-success', (file, response) => {
// @ts-ignore
uppy2.setFileState(file.id, {
// @ts-ignore
progress: uppy2.getState().files[file.id].progress,
// @ts-ignore
uploadURL: response.body.Location,
response: response,
isPaused: false,
});
});
return uppy2;
}, []);
}
export function MultipartFileUploaderAfter({
onUploadSuccess,
allowedFileTypes,
uppRef,
}: {
// @ts-ignore
onUploadSuccess: (result: UploadResult) => void;
allowedFileTypes: string;
uppRef: any;
}) {
const t = useT();
const uppy = useUppyUploader({
onUploadSuccess,
allowedFileTypes,
});
const uppyInstance = useMemo(() => {
uppRef.current = uppy;
return uppy;
}, []);
return (
<>
{/* <Dashboard uppy={uppy} /> */}
<div className="pointer-events-none">
<ProgressBar uppy={uppyInstance} />
</div>
<FileInput
uppy={uppyInstance}
locale={{
strings: {
chooseFiles: t('upload', 'Upload'),
},
// @ts-ignore
pluralize: (n: any) => n,
}}
/>
</>
);
}