Added loader & notification for files download (#634)

Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
Alex
2021-03-09 11:29:54 -06:00
committed by GitHub
parent 81087ae910
commit d4a69978fc
14 changed files with 222 additions and 42 deletions

View File

@@ -1,25 +1,25 @@
{
"files": {
"main.css": "/static/css/main.a19f3d53.chunk.css",
"main.js": "/static/js/main.35167004.chunk.js",
"main.js.map": "/static/js/main.35167004.chunk.js.map",
"main.js": "/static/js/main.69f2f0c6.chunk.js",
"main.js.map": "/static/js/main.69f2f0c6.chunk.js.map",
"runtime-main.js": "/static/js/runtime-main.f48e99e5.js",
"runtime-main.js.map": "/static/js/runtime-main.f48e99e5.js.map",
"static/css/2.76b14b73.chunk.css": "/static/css/2.76b14b73.chunk.css",
"static/js/2.9510b4a1.chunk.js": "/static/js/2.9510b4a1.chunk.js",
"static/js/2.9510b4a1.chunk.js.map": "/static/js/2.9510b4a1.chunk.js.map",
"static/js/2.1da96d0d.chunk.js": "/static/js/2.1da96d0d.chunk.js",
"static/js/2.1da96d0d.chunk.js.map": "/static/js/2.1da96d0d.chunk.js.map",
"index.html": "/index.html",
"static/css/2.76b14b73.chunk.css.map": "/static/css/2.76b14b73.chunk.css.map",
"static/css/main.a19f3d53.chunk.css.map": "/static/css/main.a19f3d53.chunk.css.map",
"static/js/2.9510b4a1.chunk.js.LICENSE.txt": "/static/js/2.9510b4a1.chunk.js.LICENSE.txt",
"static/js/2.1da96d0d.chunk.js.LICENSE.txt": "/static/js/2.1da96d0d.chunk.js.LICENSE.txt",
"static/media/minio_console_logo.0837460e.svg": "/static/media/minio_console_logo.0837460e.svg",
"static/media/minio_operator_logo.1312b7c9.svg": "/static/media/minio_operator_logo.1312b7c9.svg"
},
"entrypoints": [
"static/js/runtime-main.f48e99e5.js",
"static/css/2.76b14b73.chunk.css",
"static/js/2.9510b4a1.chunk.js",
"static/js/2.1da96d0d.chunk.js",
"static/css/main.a19f3d53.chunk.css",
"static/js/main.35167004.chunk.js"
"static/js/main.69f2f0c6.chunk.js"
]
}

View File

@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="MinIO Console"/><link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;500;700;900&display=swap" rel="stylesheet"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/><link rel="manifest" href="/manifest.json"/><link rel="mask-icon" href="/safari-pinned-tab.svg" color="#3a4e54"/><title>MinIO Console</title><link href="/static/css/2.76b14b73.chunk.css" rel="stylesheet"><link href="/static/css/main.a19f3d53.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,l,i=r[0],a=r[1],p=r[2],c=0,s=[];c<i.length;c++)l=i[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,p||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var a=t[i];0!==o[a]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var i=this["webpackJsonpportal-ui"]=this["webpackJsonpportal-ui"]||[],a=i.push.bind(i);i.push=r,i=i.slice();for(var p=0;p<i.length;p++)r(i[p]);var f=a;t()}([])</script><script src="/static/js/2.9510b4a1.chunk.js"></script><script src="/static/js/main.35167004.chunk.js"></script></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="MinIO Console"/><link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;500;700;900&display=swap" rel="stylesheet"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/><link rel="manifest" href="/manifest.json"/><link rel="mask-icon" href="/safari-pinned-tab.svg" color="#3a4e54"/><title>MinIO Console</title><link href="/static/css/2.76b14b73.chunk.css" rel="stylesheet"><link href="/static/css/main.a19f3d53.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,l,i=r[0],a=r[1],p=r[2],c=0,s=[];c<i.length;c++)l=i[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,p||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var a=t[i];0!==o[a]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var i=this["webpackJsonpportal-ui"]=this["webpackJsonpportal-ui"]||[],a=i.push.bind(i);i.push=r,i=i.slice();for(var p=0;p<i.length;p++)r(i[p]);var f=a;t()}([])</script><script src="/static/js/2.1da96d0d.chunk.js"></script><script src="/static/js/main.69f2f0c6.chunk.js"></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -43,9 +43,14 @@ import {
addRoute,
setAllRoutes,
setLastAsFile,
fileIsBeingPrepared,
fileDownloadStarted,
} from "../../../../ObjectBrowser/actions";
import { connect } from "react-redux";
import { ObjectBrowserState, Route } from "../../../../ObjectBrowser/reducers";
import {
ObjectBrowserReducer,
Route,
} from "../../../../ObjectBrowser/reducers";
import CreateFolderModal from "./CreateFolderModal";
import UploadFile from "../../../../../../icons/UploadFile";
import { download } from "../utils";
@@ -136,14 +141,13 @@ interface IListObjectsProps {
addRoute: (param1: string, param2: string, param3: string) => any;
setAllRoutes: (path: string) => any;
routesList: Route[];
downloadingFiles: string[];
setLastAsFile: () => any;
setLoadingProgress: typeof setLoadingProgress;
setSnackBarMessage: typeof setSnackBarMessage;
setErrorSnackMessage: typeof setErrorSnackMessage;
}
interface ObjectBrowserReducer {
objectBrowser: ObjectBrowserState;
fileIsBeingPrepared: typeof fileIsBeingPrepared;
fileDownloadStarted: typeof fileDownloadStarted;
}
const ListObjects = ({
@@ -152,10 +156,13 @@ const ListObjects = ({
addRoute,
setAllRoutes,
routesList,
downloadingFiles,
setLastAsFile,
setLoadingProgress,
setSnackBarMessage,
setErrorSnackMessage,
fileIsBeingPrepared,
fileDownloadStarted,
}: IListObjectsProps) => {
const [records, setRecords] = useState<BucketObject[]>([]);
const [loading, setLoading] = useState<boolean>(true);
@@ -347,8 +354,25 @@ const ListObjects = ({
setSelectedObject(object);
};
const removeDownloadAnimation = (path: string) => {
fileDownloadStarted(path);
};
const downloadObject = (object: BucketObject) => {
download(selectedBucket, object.name, object.version_id);
fileIsBeingPrepared(`${selectedBucket}/${object.name}`);
if (object.size > 104857600) {
// If file is bigger than 100MB we show a notification
setSnackBarMessage(
"Download process started, it may take a few moments to complete"
);
}
download(
selectedBucket,
object.name,
object.version_id,
removeDownloadAnimation
);
};
const openPath = (idElement: string) => {
@@ -392,7 +416,12 @@ const ListObjects = ({
const tableActions = [
{ type: "view", onClick: openPath, sendOnlyId: true },
{ type: "download", onClick: downloadObject },
{
type: "download",
onClick: downloadObject,
showLoaderFunction: (item: string) =>
downloadingFiles.includes(`${match.params["bucket"]}/${item}`),
},
{ type: "delete", onClick: confirmDeleteObject, sendOnlyId: true },
];
@@ -543,6 +572,7 @@ const ListObjects = ({
const mapStateToProps = ({ objectBrowser }: ObjectBrowserReducer) => ({
routesList: get(objectBrowser, "routesList", []),
downloadingFiles: get(objectBrowser, "downloadingFiles", []),
});
const mapDispatchToProps = {
@@ -552,6 +582,8 @@ const mapDispatchToProps = {
setLoadingProgress,
setSnackBarMessage,
setErrorSnackMessage,
fileIsBeingPrepared,
fileDownloadStarted,
};
const connector = connect(mapStateToProps, mapDispatchToProps);

View File

@@ -35,8 +35,15 @@ import {
searchField,
} from "../../../../Common/FormComponents/common/styleLibrary";
import { IFileInfo } from "./types";
import { removeRouteLevel } from "../../../../ObjectBrowser/actions";
import { Route } from "../../../../ObjectBrowser/reducers";
import {
removeRouteLevel,
fileIsBeingPrepared,
fileDownloadStarted,
} from "../../../../ObjectBrowser/actions";
import {
ObjectBrowserReducer,
Route,
} from "../../../../ObjectBrowser/reducers";
import { download } from "../utils";
import history from "../../../../../../history";
import api from "../../../../../../common/api";
@@ -52,7 +59,11 @@ import DeleteObject from "../ListObjects/DeleteObject";
import AddTagModal from "./AddTagModal";
import DeleteTagModal from "./DeleteTagModal";
import SetLegalHoldModal from "./SetLegalHoldModal";
import { setErrorSnackMessage } from "../../../../../../actions";
import {
setSnackBarMessage,
setErrorSnackMessage,
} from "../../../../../../actions";
import { CircularProgress } from "@material-ui/core";
const styles = (theme: Theme) =>
createStyles({
@@ -128,6 +139,20 @@ const styles = (theme: Theme) =>
marginRight: 0,
},
},
"@global": {
".progressDetails": {
paddingTop: 3,
display: "inline-block",
position: "relative",
width: 18,
height: 18,
},
".progressDetails > .MuiCircularProgress-root": {
position: "absolute",
left: 0,
top: 3,
},
},
...actionsTray,
...searchField,
...containerForHeader(theme.spacing(4)),
@@ -136,8 +161,12 @@ const styles = (theme: Theme) =>
interface IObjectDetailsProps {
classes: any;
routesList: Route[];
downloadingFiles: string[];
removeRouteLevel: (newRoute: string) => any;
setErrorSnackMessage: typeof setErrorSnackMessage;
setSnackBarMessage: typeof setSnackBarMessage;
fileIsBeingPrepared: typeof fileIsBeingPrepared;
fileDownloadStarted: typeof fileDownloadStarted;
}
const emptyFile: IFileInfo = {
@@ -155,8 +184,12 @@ const emptyFile: IFileInfo = {
const ObjectDetails = ({
classes,
routesList,
downloadingFiles,
removeRouteLevel,
setErrorSnackMessage,
setSnackBarMessage,
fileIsBeingPrepared,
fileDownloadStarted,
}: IObjectDetailsProps) => {
const [loadObjectData, setLoadObjectData] = useState<boolean>(true);
const [shareFileModalOpen, setShareFileModalOpen] = useState<boolean>(false);
@@ -228,13 +261,44 @@ const ObjectDetails = ({
setDeleteTagModalOpen(true);
};
const downloadObject = (object: IFileInfo) => {
download(bucketName, pathInBucket, object.version_id);
const removeDownloadAnimation = (path: string) => {
fileDownloadStarted(path);
};
const downloadObject = (object: IFileInfo, includeVersion?: boolean) => {
fileIsBeingPrepared(
`${bucketName}/${object.name}${
includeVersion ? `-${object.version_id}` : ""
}`
);
if (object.size && parseInt(object.size) > 104857600) {
// If file is bigger than 100MB we show a notification
setSnackBarMessage(
"Download process started, it may take a few moments to complete"
);
}
download(
bucketName,
pathInBucket,
object.version_id,
removeDownloadAnimation,
includeVersion
);
};
const tableActions = [
{ type: "share", onClick: shareObject, sendOnlyId: true },
{ type: "download", onClick: downloadObject },
{
type: "download",
onClick: (item: IFileInfo) => {
downloadObject(item, true);
},
showLoaderFunction: (version: string) => {
return downloadingFiles.includes(
`${bucketName}/${objectName}-${version}`
);
},
},
];
const filteredRecords = versions.filter((version) => {
@@ -420,17 +484,29 @@ const ObjectDetails = ({
</IconButton>
</div>
<div className={classes.actionsIconContainer}>
<IconButton
color="primary"
aria-label="download"
size="small"
className={classes.actionsIcon}
onClick={() => {
downloadObject(actualInfo);
}}
>
<DownloadIcon />
</IconButton>
{downloadingFiles.includes(
`${bucketName}/${actualInfo.name}`
) ? (
<div className="progressDetails">
<CircularProgress
color="primary"
size={17}
variant="indeterminate"
/>
</div>
) : (
<IconButton
color="primary"
aria-label="download"
size="small"
className={classes.actionsIcon}
onClick={() => {
downloadObject(actualInfo);
}}
>
<DownloadIcon />
</IconButton>
)}
</div>
<div className={classes.actionsIconContainer}>
<IconButton
@@ -524,11 +600,18 @@ const ObjectDetails = ({
);
};
const mapStateToProps = ({ objectBrowser }: ObjectBrowserReducer) => ({
downloadingFiles: get(objectBrowser, "downloadingFiles", []),
});
const mapDispatchToProps = {
removeRouteLevel,
setErrorSnackMessage,
fileIsBeingPrepared,
fileDownloadStarted,
setSnackBarMessage,
};
const connector = connect(null, mapDispatchToProps);
const connector = connect(mapStateToProps, mapDispatchToProps);
export default connector(withStyles(styles)(ObjectDetails));

View File

@@ -19,7 +19,9 @@ import { isNullOrUndefined } from "util";
export const download = (
bucketName: string,
objectPath: string,
versionID: any
versionID: any,
callBack?: (objIdentifier: string) => void,
includeVersionInCallback?: boolean
) => {
const anchor = document.createElement("a");
document.body.appendChild(anchor);
@@ -48,6 +50,14 @@ export const download = (
anchor.click();
window.URL.revokeObjectURL(blobUrl);
anchor.remove();
if (callBack) {
callBack(
`${bucketName}/${objectPath}${
includeVersionInCallback ? `-${versionID}` : ""
}`
);
}
}
};
xhr.send();

View File

@@ -23,6 +23,9 @@ export const OBJECT_BROWSER_SET_ALL_ROUTES = "OBJECT_BROWSER/SET_ALL_ROUTES";
export const OBJECT_BROWSER_CREATE_FOLDER = "OBJECT_BROWSER/CREATE_FOLDER";
export const OBJECT_BROWSER_SET_LAST_AS_FILE =
"OBJECT_BROWSER/SET_LAST_AS_FILE";
export const OBJECT_BROWSER_DOWNLOAD_FILE_LOADER =
"OBJECT_BROWSER/DOWNLOAD_FILE_LOADER";
export const OBJECT_BROWSER_DOWNLOADED_FILE = "OBJECT_BROWSER/DOWNLOADED_FILE";
interface AddRouteAction {
type: typeof OBJECT_BROWSER_ADD_ROUTE;
@@ -55,13 +58,25 @@ interface SetLastAsFile {
type: typeof OBJECT_BROWSER_SET_LAST_AS_FILE;
}
interface SetFileDownload {
type: typeof OBJECT_BROWSER_DOWNLOAD_FILE_LOADER;
path: string;
}
interface FileDownloaded {
type: typeof OBJECT_BROWSER_DOWNLOADED_FILE;
path: string;
}
export type ObjectBrowserActionTypes =
| AddRouteAction
| ResetRoutesList
| RemoveRouteLevel
| SetAllRoutes
| CreateFolder
| SetLastAsFile;
| SetLastAsFile
| SetFileDownload
| FileDownloaded;
export const addRoute = (route: string, label: string, routeType: string) => {
return {
@@ -105,3 +120,17 @@ export const setLastAsFile = () => {
type: OBJECT_BROWSER_SET_LAST_AS_FILE,
};
};
export const fileIsBeingPrepared = (path: string) => {
return {
type: OBJECT_BROWSER_DOWNLOAD_FILE_LOADER,
path,
};
};
export const fileDownloadStarted = (path: string) => {
return {
type: OBJECT_BROWSER_DOWNLOADED_FILE,
path,
};
};

View File

@@ -22,6 +22,8 @@ import {
OBJECT_BROWSER_RESET_ROUTES_LIST,
OBJECT_BROWSER_SET_ALL_ROUTES,
OBJECT_BROWSER_SET_LAST_AS_FILE,
OBJECT_BROWSER_DOWNLOAD_FILE_LOADER,
OBJECT_BROWSER_DOWNLOADED_FILE,
ObjectBrowserActionTypes,
} from "./actions";
@@ -33,6 +35,11 @@ export interface Route {
export interface ObjectBrowserState {
routesList: Route[];
downloadingFiles: string[];
}
export interface ObjectBrowserReducer {
objectBrowser: ObjectBrowserState;
}
const initialRoute = [
@@ -41,6 +48,7 @@ const initialRoute = [
const initialState: ObjectBrowserState = {
routesList: initialRoute,
downloadingFiles: [],
};
export function objectBrowserReducer(
@@ -131,6 +139,24 @@ export function objectBrowserReducer(
...state,
routesList: newList,
};
case OBJECT_BROWSER_DOWNLOAD_FILE_LOADER:
const actualFiles = [...state.downloadingFiles];
actualFiles.push(action.path);
return {
...state,
downloadingFiles: [...actualFiles],
};
case OBJECT_BROWSER_DOWNLOADED_FILE:
const downloadingFiles = state.downloadingFiles.filter(
(item) => item !== action.path
);
return {
...state,
downloadingFiles: [...downloadingFiles],
};
default:
return state;
}