mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 00:09:51 +03:00
fix upload, except cancel
This commit is contained in:
@@ -12,6 +12,7 @@ export const EVENT_SOURCE_ROOT = 'http://' + host + '/es/';
|
||||
export const alovaInstance = createAlova({
|
||||
statesHook: ReactHook,
|
||||
// timeout: 3000, // timeout not used because of uploading firmware
|
||||
// localCache: null,
|
||||
localCache: {
|
||||
GET: {
|
||||
mode: 'placeholder', // see https://alova.js.org/learning/response-cache/#cache-replaceholder-mode
|
||||
|
||||
@@ -26,13 +26,12 @@ const getBorderColor = (theme: Theme, props: DropzoneState) => {
|
||||
export interface SingleUploadProps {
|
||||
onDrop: (acceptedFiles: File[]) => void;
|
||||
onCancel: () => void;
|
||||
isUploading: boolean;
|
||||
progress?: Progress;
|
||||
// TODO remove
|
||||
// progress?: AxiosProgressEvent;
|
||||
progress: Progress;
|
||||
}
|
||||
|
||||
const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, isUploading, progress }) => {
|
||||
const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, progress }) => {
|
||||
const isUploading = progress.total > 0;
|
||||
|
||||
const dropzoneState = useDropzone({
|
||||
onDrop,
|
||||
accept: {
|
||||
@@ -43,20 +42,16 @@ const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, isUploading, pr
|
||||
disabled: isUploading,
|
||||
multiple: false
|
||||
});
|
||||
|
||||
const { getRootProps, getInputProps } = dropzoneState;
|
||||
const theme = useTheme();
|
||||
|
||||
const { LL } = useI18nContext();
|
||||
|
||||
// TODO remove debug
|
||||
console.log('progress', progress?.loaded);
|
||||
|
||||
const progressText = () => {
|
||||
if (isUploading) {
|
||||
if (progress?.total) {
|
||||
if (progress.total) {
|
||||
return LL.UPLOADING() + ': ' + Math.round((progress.loaded * 100) / progress.total) + '%';
|
||||
}
|
||||
return LL.UPLOADING() + `\u2026`;
|
||||
}
|
||||
return LL.UPLOAD_DROP_TEXT();
|
||||
};
|
||||
@@ -86,8 +81,8 @@ const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, isUploading, pr
|
||||
<Fragment>
|
||||
<Box width="100%" p={2}>
|
||||
<LinearProgress
|
||||
variant={!progress || progress.total ? 'determinate' : 'indeterminate'}
|
||||
value={!progress ? 0 : progress.total ? Math.round((progress.loaded * 100) / progress.total) : 0}
|
||||
variant="determinate"
|
||||
value={progress.total === 0 ? 0 : Math.round((progress.loaded * 100) / progress.total)}
|
||||
/>
|
||||
</Box>
|
||||
<Button startIcon={<CancelIcon />} variant="outlined" color="secondary" onClick={onCancel}>
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
export { default as SingleUpload } from './SingleUpload';
|
||||
export { default as useFileUpload } from './useFileUpload';
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
import { useRequest } from 'alova';
|
||||
// import axios from 'axios';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
// import type { FileUploadConfig } from 'api/endpoints';
|
||||
// import type { AxiosPromise, CancelTokenSource, AxiosProgressEvent } from 'axios';
|
||||
|
||||
import * as SystemApi from 'api/system';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
interface MediaUploadOptions {
|
||||
// TODO fileupload move to alova
|
||||
// upload: (file: File, config?: FileUploadConfig) => AxiosPromise<void>;
|
||||
upload: (file: File) => Promise<void>;
|
||||
}
|
||||
|
||||
const useFileUpload = ({ upload }: MediaUploadOptions) => {
|
||||
const { LL } = useI18nContext();
|
||||
|
||||
// const [uploading, setUploading] = useState<boolean>(false);
|
||||
const [md5, setMd5] = useState<string>('');
|
||||
// const [uploadProgress, setUploadProgress] = useState<AxiosProgressEvent>();
|
||||
// const [uploadCancelToken, setUploadCancelToken] = useState<CancelTokenSource>();
|
||||
|
||||
const { uploading, send: sendUpload } = useRequest(SystemApi.uploadFile, {
|
||||
immediate: false
|
||||
});
|
||||
|
||||
const resetUploadingStates = () => {
|
||||
// setUploading(false);
|
||||
// setUploadProgress(undefined);
|
||||
// setUploadCancelToken(undefined);
|
||||
setMd5('');
|
||||
};
|
||||
|
||||
// const cancelUpload = useCallback(() => {
|
||||
// uploadCancelToken?.cancel();
|
||||
// resetUploadingStates();
|
||||
// }, [uploadCancelToken]);
|
||||
|
||||
// useEffect(
|
||||
// () => () => {
|
||||
// uploadCancelToken?.cancel();
|
||||
// },
|
||||
// [uploadCancelToken]
|
||||
// );
|
||||
|
||||
// TODO fileupload move to alova
|
||||
// TODO make it single file
|
||||
const uploadFile = async (files: File[]) => {
|
||||
// TODO remove debug
|
||||
console.log('useFileUpload.ts:uploadFile:' + files[0].name, files[0].size);
|
||||
|
||||
await sendUpload(files[0]);
|
||||
|
||||
// const response = await SystemApi.startUploadFile(files[0]);
|
||||
// console.log(response.status);
|
||||
|
||||
// const response = await upload(files[0], {
|
||||
// onUploadProgress: setUploadProgress,
|
||||
// cancelToken: cancelToken.token
|
||||
// });
|
||||
|
||||
// try {
|
||||
// const cancelToken = axios.CancelToken.source();
|
||||
// setUploadCancelToken(cancelToken);
|
||||
// setUploading(true);
|
||||
// const response = await upload(images[0], {
|
||||
// onUploadProgress: setUploadProgress,
|
||||
// cancelToken: cancelToken.token
|
||||
// });
|
||||
// resetUploadingStates();
|
||||
// if (response.status === 200) {
|
||||
// toast.success(LL.UPLOAD() + ' ' + LL.SUCCESSFUL());
|
||||
// } else if (response.status === 201) {
|
||||
// setMd5(String(response.data));
|
||||
// toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL());
|
||||
// }
|
||||
// } catch (error) {
|
||||
// if (axios.isCancel(error)) {
|
||||
// toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED());
|
||||
// } else {
|
||||
// resetUploadingStates();
|
||||
// toast.error(LL.UPLOAD() + ' ' + LL.FAILED(0));
|
||||
// }
|
||||
// }
|
||||
};
|
||||
|
||||
return [uploadFile, uploading, md5] as const;
|
||||
// return [uploadFile, cancelUpload, uploading, uploadProgress, md5] as const;
|
||||
};
|
||||
|
||||
export default useFileUpload;
|
||||
@@ -1,177 +0,0 @@
|
||||
import DownloadIcon from '@mui/icons-material/GetApp';
|
||||
import { Typography, Button, Box } from '@mui/material';
|
||||
import { useRequest } from 'alova';
|
||||
import { toast } from 'react-toastify';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import * as SystemApi from 'api/system';
|
||||
import { SingleUpload, useFileUpload } from 'components';
|
||||
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import * as EMSESP from 'project/api';
|
||||
|
||||
interface UploadFileProps {
|
||||
// TODO fileupload move to alova
|
||||
// uploadGeneralFile: (file: File, config?: FileUploadConfig) => AxiosPromise<void>;
|
||||
uploadGeneralFile: (file: File) => Promise<void>;
|
||||
}
|
||||
|
||||
const GeneralFileUpload: FC<UploadFileProps> = ({ uploadGeneralFile }) => {
|
||||
const { LL } = useI18nContext();
|
||||
|
||||
// TODO remove these
|
||||
const md5 = '';
|
||||
const cancelUpload = () => {};
|
||||
const uploading = false;
|
||||
|
||||
// const [uploadFile, cancelUpload, uploading, uploadProgress, md5] = useFileUpload({ upload: uploadGeneralFile });
|
||||
// const [uploadFile, cancelUpload, uploading, md5] = useFileUpload({ upload: uploadGeneralFile });
|
||||
|
||||
const { send: getSettings, onSuccess: onSuccessGetSettings } = useRequest(EMSESP.getSettings(), {
|
||||
immediate: false
|
||||
});
|
||||
const { send: getCustomizations, onSuccess: onSuccessgetCustomizations } = useRequest(EMSESP.getCustomizations(), {
|
||||
immediate: false
|
||||
});
|
||||
const { send: getEntities, onSuccess: onSuccessGetEntities } = useRequest(EMSESP.getEntities(), {
|
||||
immediate: false
|
||||
});
|
||||
const { send: getSchedule, onSuccess: onSuccessGetSchedule } = useRequest(EMSESP.getSchedule(), {
|
||||
immediate: false
|
||||
});
|
||||
|
||||
const {
|
||||
loading: isUploading,
|
||||
uploading: progress,
|
||||
send: sendUpload
|
||||
} = useRequest(SystemApi.uploadFile, {
|
||||
immediate: false,
|
||||
force: true
|
||||
});
|
||||
|
||||
const uploadFile = async (files: File[]) => {
|
||||
// TODO remove debug
|
||||
console.log('useFileUpload.ts:uploadFile:' + files[0].name, files[0].size);
|
||||
await sendUpload(files[0]);
|
||||
};
|
||||
|
||||
// TODO see if refactor like https://alova.js.org/extension/alova-adapter-xhr/#download
|
||||
// And use revoke
|
||||
const saveFile = (json: any, endpoint: string) => {
|
||||
const a = document.createElement('a');
|
||||
const filename = 'emsesp_' + endpoint + '.json';
|
||||
a.href = URL.createObjectURL(
|
||||
new Blob([JSON.stringify(json, null, 2)], {
|
||||
type: 'text/plain'
|
||||
})
|
||||
);
|
||||
a.setAttribute('download', filename);
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
toast.info(LL.DOWNLOAD_SUCCESSFUL());
|
||||
};
|
||||
|
||||
onSuccessGetSettings((event) => {
|
||||
saveFile(event.data, 'settings');
|
||||
});
|
||||
onSuccessgetCustomizations((event) => {
|
||||
saveFile(event.data, 'customizations');
|
||||
});
|
||||
onSuccessGetEntities((event) => {
|
||||
saveFile(event.data, 'entities');
|
||||
});
|
||||
onSuccessGetSchedule((event) => {
|
||||
saveFile(event.data, 'schedule');
|
||||
});
|
||||
|
||||
const downloadSettings = async () => {
|
||||
await getSettings().catch((error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
};
|
||||
|
||||
const downloadCustomizations = async () => {
|
||||
await getCustomizations().catch((error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
};
|
||||
|
||||
const downloadEntities = async () => {
|
||||
await getEntities().catch((error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
};
|
||||
|
||||
const downloadSchedule = async () => {
|
||||
await getSchedule().catch((error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{!uploading && (
|
||||
<>
|
||||
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
|
||||
{LL.UPLOAD()}
|
||||
</Typography>
|
||||
<Box mb={2} color="warning.main">
|
||||
<Typography variant="body2">{LL.UPLOAD_TEXT()} </Typography>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
{md5 !== '' && (
|
||||
<Box mb={2}>
|
||||
<Typography variant="body2">{'MD5: ' + md5}</Typography>
|
||||
</Box>
|
||||
)}
|
||||
{/* TODO fix this hardcoded isUploading */}
|
||||
<SingleUpload onDrop={uploadFile} onCancel={cancelUpload} isUploading={isUploading} progress={progress} />
|
||||
{console.log(progress)}
|
||||
{/* <SingleUpload onDrop={uploadFile} onCancel={cancelUpload} isUploading={false} progress={uploading} /> */}
|
||||
{!isUploading && (
|
||||
<>
|
||||
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
|
||||
{LL.DOWNLOAD(0)}
|
||||
</Typography>
|
||||
<Box color="warning.main">
|
||||
<Typography mb={1} variant="body2">
|
||||
{LL.DOWNLOAD_SETTINGS_TEXT()}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Button startIcon={<DownloadIcon />} variant="outlined" color="primary" onClick={downloadSettings}>
|
||||
{LL.SETTINGS_OF('')}
|
||||
</Button>
|
||||
<Box color="warning.main">
|
||||
<Typography mt={2} mb={1} variant="body2">
|
||||
{LL.DOWNLOAD_CUSTOMIZATION_TEXT()}{' '}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Button startIcon={<DownloadIcon />} variant="outlined" color="primary" onClick={downloadCustomizations}>
|
||||
{LL.CUSTOMIZATIONS()}
|
||||
</Button>
|
||||
<Button
|
||||
sx={{ ml: 2 }}
|
||||
startIcon={<DownloadIcon />}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={downloadEntities}
|
||||
>
|
||||
{LL.CUSTOM_ENTITIES(0)}
|
||||
</Button>
|
||||
<Box color="warning.main">
|
||||
<Typography mt={2} mb={1} variant="body2">
|
||||
{LL.DOWNLOAD_SCHEDULE_TEXT()}{' '}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Button startIcon={<DownloadIcon />} variant="outlined" color="primary" onClick={downloadSchedule}>
|
||||
{LL.SCHEDULE(0)}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default GeneralFileUpload;
|
||||
@@ -1,43 +1,174 @@
|
||||
import DownloadIcon from '@mui/icons-material/GetApp';
|
||||
import { Typography, Button, Box } from '@mui/material';
|
||||
import { useRequest } from 'alova';
|
||||
import { useRef, useState } from 'react';
|
||||
import GeneralFileUpload from './GeneralFileUpload';
|
||||
import { useState, type FC } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import RestartMonitor from './RestartMonitor';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import * as SystemApi from 'api/system';
|
||||
import { SectionContent } from 'components';
|
||||
import { SectionContent, SingleUpload } from 'components';
|
||||
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import * as EMSESP from 'project/api';
|
||||
|
||||
const UploadFileForm: FC = () => {
|
||||
const [restarting, setRestarting] = useState<boolean>();
|
||||
|
||||
const { LL } = useI18nContext();
|
||||
const [restarting, setRestarting] = useState<boolean>(false);
|
||||
const [md5, setMd5] = useState<string>();
|
||||
|
||||
const {
|
||||
loading,
|
||||
data,
|
||||
uploading,
|
||||
send: sendUpload
|
||||
} = useRequest(SystemApi.uploadFile, {
|
||||
const { send: getSettings, onSuccess: onSuccessGetSettings } = useRequest(EMSESP.getSettings(), {
|
||||
immediate: false
|
||||
});
|
||||
const { send: getCustomizations, onSuccess: onSuccessgetCustomizations } = useRequest(EMSESP.getCustomizations(), {
|
||||
immediate: false
|
||||
});
|
||||
const { send: getEntities, onSuccess: onSuccessGetEntities } = useRequest(EMSESP.getEntities(), {
|
||||
immediate: false
|
||||
});
|
||||
const { send: getSchedule, onSuccess: onSuccessGetSchedule } = useRequest(EMSESP.getSchedule(), {
|
||||
immediate: false
|
||||
});
|
||||
|
||||
const uploadFile = useRef(async (file: File) => {
|
||||
// TODO fileupload move to alova
|
||||
console.log('UploadFileForm.tsx: uploadFile duplicate!!!'); // TODO do we need this function??? duplicate?
|
||||
await sendUpload(file);
|
||||
|
||||
// const response = await SystemApi.uploadFile(file);
|
||||
// if (response.status === 200) {
|
||||
// setRestarting(true);
|
||||
// }
|
||||
// return response;
|
||||
const {
|
||||
loading: isUploading,
|
||||
uploading: progress,
|
||||
send: sendUpload,
|
||||
onSuccess: onSuccessUpload,
|
||||
abort
|
||||
} = useRequest(SystemApi.uploadFile, {
|
||||
immediate: false,
|
||||
force: true
|
||||
});
|
||||
|
||||
onSuccessUpload(({ data }: any) => {
|
||||
if (data) {
|
||||
setMd5(data.md5);
|
||||
toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL());
|
||||
} else {
|
||||
toast.success(LL.UPLOAD() + ' ' + LL.SUCCESSFUL());
|
||||
setRestarting(true);
|
||||
}
|
||||
});
|
||||
|
||||
const cancelUpload = () => {
|
||||
console.log('aborting upload...'); // TODO remove debug
|
||||
abort();
|
||||
toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED());
|
||||
};
|
||||
|
||||
const startUpload = async (files: File[]) => {
|
||||
await sendUpload(files[0]);
|
||||
};
|
||||
|
||||
const saveFile = (json: any, endpoint: string) => {
|
||||
const anchor = document.createElement('a');
|
||||
anchor.href = URL.createObjectURL(
|
||||
new Blob([JSON.stringify(json, null, 2)], {
|
||||
type: 'text/plain'
|
||||
})
|
||||
);
|
||||
anchor.download = 'emsesp_' + endpoint + '.json';
|
||||
anchor.click();
|
||||
URL.revokeObjectURL(anchor.href);
|
||||
toast.info(LL.DOWNLOAD_SUCCESSFUL());
|
||||
};
|
||||
|
||||
onSuccessGetSettings((event) => {
|
||||
saveFile(event.data, 'settings');
|
||||
});
|
||||
onSuccessgetCustomizations((event) => {
|
||||
saveFile(event.data, 'customizations');
|
||||
});
|
||||
onSuccessGetEntities((event) => {
|
||||
saveFile(event.data, 'entities');
|
||||
});
|
||||
onSuccessGetSchedule((event) => {
|
||||
saveFile(event.data, 'schedule');
|
||||
});
|
||||
|
||||
const downloadSettings = async () => {
|
||||
await getSettings().catch((error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
};
|
||||
|
||||
const downloadCustomizations = async () => {
|
||||
await getCustomizations().catch((error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
};
|
||||
|
||||
const downloadEntities = async () => {
|
||||
await getEntities().catch((error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
};
|
||||
|
||||
const downloadSchedule = async () => {
|
||||
await getSchedule().catch((error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
};
|
||||
|
||||
const content = () => (
|
||||
<>
|
||||
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
|
||||
{LL.UPLOAD()}
|
||||
</Typography>
|
||||
<Box mb={2} color="warning.main">
|
||||
<Typography variant="body2">{LL.UPLOAD_TEXT()} </Typography>
|
||||
</Box>
|
||||
{md5 && (
|
||||
<Box mb={2}>
|
||||
<Typography variant="body2">{'MD5: ' + md5}</Typography>
|
||||
</Box>
|
||||
)}
|
||||
<SingleUpload onDrop={startUpload} onCancel={cancelUpload} progress={progress} />
|
||||
{!isUploading && (
|
||||
<>
|
||||
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
|
||||
{LL.DOWNLOAD(0)}
|
||||
</Typography>
|
||||
<Box color="warning.main">
|
||||
<Typography mb={1} variant="body2">
|
||||
{LL.DOWNLOAD_SETTINGS_TEXT()}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Button startIcon={<DownloadIcon />} variant="outlined" color="primary" onClick={downloadSettings}>
|
||||
{LL.SETTINGS_OF('')}
|
||||
</Button>
|
||||
<Box color="warning.main">
|
||||
<Typography mt={2} mb={1} variant="body2">
|
||||
{LL.DOWNLOAD_CUSTOMIZATION_TEXT()}{' '}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Button startIcon={<DownloadIcon />} variant="outlined" color="primary" onClick={downloadCustomizations}>
|
||||
{LL.CUSTOMIZATIONS()}
|
||||
</Button>
|
||||
<Button
|
||||
sx={{ ml: 2 }}
|
||||
startIcon={<DownloadIcon />}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={downloadEntities}
|
||||
>
|
||||
{LL.CUSTOM_ENTITIES(0)}
|
||||
</Button>
|
||||
<Box color="warning.main">
|
||||
<Typography mt={2} mb={1} variant="body2">
|
||||
{LL.DOWNLOAD_SCHEDULE_TEXT()}{' '}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Button startIcon={<DownloadIcon />} variant="outlined" color="primary" onClick={downloadSchedule}>
|
||||
{LL.SCHEDULE(0)}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<SectionContent title={LL.UPLOAD_DOWNLOAD()} titleGutter>
|
||||
{restarting ? <RestartMonitor /> : <GeneralFileUpload uploadGeneralFile={uploadFile.current} />}
|
||||
{restarting ? <RestartMonitor /> : content()}
|
||||
</SectionContent>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -45,7 +45,7 @@ export const scanDevices = () => alovaInstance.Post('/rest/scanDevices');
|
||||
// HelpInformation
|
||||
export const API = (apiCall: APIcall) => alovaInstance.Post('/api', apiCall);
|
||||
|
||||
// GeneralFileUpload
|
||||
// UploadFileForm
|
||||
export const getSettings = () => alovaInstance.Get('/rest/getSettings');
|
||||
export const getCustomizations = () => alovaInstance.Get('/rest/getCustomizations');
|
||||
export const getEntities = () => alovaInstance.Get<Entities>('/rest/getEntities');
|
||||
|
||||
Reference in New Issue
Block a user