Improved the Share File modal (#1083)

This commit is contained in:
Alex
2021-09-25 03:00:32 -05:00
committed by GitHub
parent 19f034adf4
commit 8658ac2914
5 changed files with 298 additions and 49 deletions

View File

@@ -9,3 +9,15 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
/* Chrome, Safari, Edge, Opera */
input.removeArrows::-webkit-outer-spin-button,
input.removeArrows::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input.removeArrows[type=number] {
-moz-appearance: textfield;
}

View File

@@ -32,8 +32,8 @@ import { AppState } from "../../../../../../store";
import { ErrorResponseHandler } from "../../../../../../common/types";
import api from "../../../../../../common/api";
import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper";
import DateSelector from "../../../../Common/FormComponents/DateSelector/DateSelector";
import PredefinedList from "../../../../Common/FormComponents/PredefinedList/PredefinedList";
import DaysSelector from "../../../../Common/FormComponents/DaysSelector/DaysSelector";
const styles = (theme: Theme) =>
createStyles({
@@ -72,6 +72,8 @@ const ShareFile = ({
const [selectedDate, setSelectedDate] = useState<string>("");
const [dateValid, setDateValid] = useState<boolean>(true);
const initialDate = new Date();
const dateChanged = (newDate: string, isValid: boolean) => {
setDateValid(isValid);
if (isValid) {
@@ -79,6 +81,7 @@ const ShareFile = ({
return;
}
setSelectedDate("");
setShareURL("");
};
useEffect(() => {
@@ -86,54 +89,33 @@ const ShareFile = ({
setIsLoadingFile(true);
setShareURL("");
const slDate = new Date(`${selectedDate}T23:59:59`);
const slDate = new Date(`${selectedDate}`);
const currDate = new Date();
const diffDate = slDate.getTime() - currDate.getTime();
const versID = distributedSetup ? dataObject.version_id : "null";
if (diffDate > 0) {
const versID = distributedSetup ? dataObject.version_id : "null";
if (diffDate < 0) {
setModalErrorSnackMessage({
errorMessage: "Selected date must be greater than current time.",
detailedError: "",
});
setShareURL("");
setIsLoadingFile(false);
return;
api
.invoke(
"GET",
`/api/v1/buckets/${bucketName}/objects/share?prefix=${
dataObject.name
}&version_id=${versID || "null"}${
selectedDate !== "" ? `&expires=${diffDate}ms` : ""
}`
)
.then((res: string) => {
setShareURL(res);
setIsLoadingFile(false);
})
.catch((error: ErrorResponseHandler) => {
setModalErrorSnackMessage(error);
setShareURL("");
setIsLoadingFile(false);
});
}
if (diffDate > 604800000) {
setModalErrorSnackMessage({
errorMessage: "You can share a file only for less than 7 days.",
detailedError: "",
});
setShareURL("");
setIsLoadingFile(false);
return;
}
api
.invoke(
"GET",
`/api/v1/buckets/${bucketName}/objects/share?prefix=${
dataObject.name
}&version_id=${versID}${
selectedDate !== "" ? `&expires=${diffDate}ms` : ""
}`
)
.then((res: string) => {
setShareURL(res);
setIsLoadingFile(false);
})
.catch((error: ErrorResponseHandler) => {
setModalErrorSnackMessage(error);
setShareURL("");
setIsLoadingFile(false);
});
return;
}
}, [
dataObject,
@@ -155,13 +137,20 @@ const ShareFile = ({
}}
>
<Grid container className={classes.modalContent}>
<Grid item xs={12} className={classes.moduleDescription}>
This module generates a temporary URL with integrated access
credentials for sharing objects for up to 7 days.
<br />
The temporary URL expires after the configured time limit.
</Grid>
<Grid item xs={12} className={classes.dateContainer}>
<DateSelector
<DaysSelector
initialDate={initialDate}
id="date"
label="Active until"
borderBottom={false}
addSwitch={true}
onDateChange={dateChanged}
label="Active for"
maxDays={7}
onChange={dateChanged}
entity="Link"
/>
</Grid>
<Grid container item xs={12}>

View File

@@ -0,0 +1,240 @@
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { useState, useEffect, Fragment } from "react";
import Grid from "@material-ui/core/Grid";
import InputLabel from "@material-ui/core/InputLabel";
import moment from "moment/moment";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { fieldBasic, tooltipHelper } from "../common/styleLibrary";
import InputBoxWrapper from "../InputBoxWrapper/InputBoxWrapper";
interface IDaysSelector {
classes: any;
id: string;
initialDate: Date;
maxDays?: number;
label: string;
entity: string;
onChange: (newDate: string, isValid: boolean) => void;
}
const styles = (theme: Theme) =>
createStyles({
dateInput: {
"&:not(:last-child)": {
marginRight: 22,
},
},
...fieldBasic,
...tooltipHelper,
labelContainer: {
display: "flex",
alignItems: "center",
},
fieldContainer: {
...fieldBasic.fieldContainer,
display: "flex",
alignItems: "center",
justifyContent: "space-between",
paddingBottom: 10,
marginTop: 11,
marginBottom: 6,
},
fieldContainerBorder: {
borderBottom: "#9c9c9c 1px solid",
marginBottom: 20,
},
dateContainer: {
height: 20,
textAlign: "right",
color: "#848484",
},
});
const calculateNewTime = (
initialDate: Date,
days: number,
hours: number,
minutes: number
) => {
return moment(initialDate)
.add(days, "days")
.add(hours, "hours")
.add(minutes, "minutes");
};
const DaysSelector = ({
classes,
id,
initialDate,
label,
maxDays,
entity,
onChange,
}: IDaysSelector) => {
const [selectedDays, setSelectedDays] = useState<number>(7);
const [selectedHours, setSelectedHours] = useState<number>(0);
const [selectedMinutes, setSelectedMinutes] = useState<number>(0);
const [validDate, setValidDate] = useState<boolean>(true);
const [dateSelected, setDateSelected] = useState<moment.Moment>(moment());
useEffect(() => {
setDateSelected(
calculateNewTime(
initialDate,
selectedDays,
selectedHours,
selectedMinutes
)
);
}, [initialDate, selectedDays, selectedHours, selectedMinutes]);
useEffect(() => {
if (validDate) {
onChange(dateSelected.format("YYYY-MM-DDTHH:mm:ss"), true);
} else {
onChange("0000-00-00", false);
}
}, [dateSelected, onChange, validDate]);
// Basic validation for inputs
useEffect(() => {
let valid = true;
if (
selectedDays < 0 ||
(maxDays && selectedDays > maxDays) ||
isNaN(selectedDays)
) {
valid = false;
}
if (selectedHours < 0 || selectedHours > 23 || isNaN(selectedHours)) {
valid = false;
}
if (selectedMinutes < 0 || selectedMinutes > 59 || isNaN(selectedMinutes)) {
valid = false;
}
if (
maxDays &&
selectedDays === maxDays &&
(selectedHours !== 0 || selectedMinutes !== 0)
) {
valid = false;
}
setValidDate(valid);
}, [
dateSelected,
maxDays,
onChange,
selectedDays,
selectedHours,
selectedMinutes,
]);
return (
<Fragment>
<Grid item xs={12} className={classes.fieldContainer}>
<Grid container alignItems={"center"} justifyContent={"center"}>
<Grid item xs={12} className={classes.labelContainer}>
<InputLabel htmlFor={id} className={classes.inputLabel}>
<span>{label}</span>
</InputLabel>
</Grid>
<Grid item xs={2}>
<InputBoxWrapper
id={id}
type="number"
min="0"
max={maxDays ? maxDays.toString() : "999"}
label="Days"
name={id}
onChange={(e) => {
setSelectedDays(parseInt(e.target.value));
}}
value={selectedDays.toString()}
extraInputProps={{
style: {
textAlign: "center",
paddingRight: 5,
},
className: "removeArrows",
}}
/>
</Grid>
<Grid item xs={2}>
<InputBoxWrapper
id={id}
type="number"
min="0"
max="23"
label="Hours"
name={id}
onChange={(e) => {
setSelectedHours(parseInt(e.target.value));
}}
value={selectedHours.toString()}
extraInputProps={{
style: {
textAlign: "center",
paddingRight: 5,
},
className: "removeArrows",
}}
/>
</Grid>
<Grid item xs={2}>
<InputBoxWrapper
id={id}
type="number"
min="0"
max="59"
label="Minutes"
name={id}
onChange={(e) => {
setSelectedMinutes(parseInt(e.target.value));
}}
value={selectedMinutes.toString()}
extraInputProps={{
style: {
textAlign: "center",
paddingRight: 5,
},
className: "removeArrows",
}}
/>
</Grid>
</Grid>
</Grid>
<Grid container>
<Grid item xs={12} className={classes.dateContainer}>
{validDate && (
<Fragment>
<strong>{entity} will be available until:</strong>{" "}
{dateSelected.format("MM/DD/YYYY HH:mm:ss")}
</Fragment>
)}
</Grid>
</Grid>
<br />
</Fragment>
);
};
export default withStyles(styles)(DaysSelector);

View File

@@ -23,6 +23,7 @@ import {
Tooltip,
} from "@material-ui/core";
import { OutlinedInputProps } from "@material-ui/core/OutlinedInput";
import { InputProps as StandardInputProps } from "@material-ui/core/Input";
import {
createStyles,
makeStyles,
@@ -56,6 +57,7 @@ interface InputBoxProps {
max?: string;
overlayIcon?: any;
overlayAction?: () => void;
extraInputProps?: StandardInputProps['inputProps'];
}
const styles = (theme: Theme) =>
@@ -125,10 +127,11 @@ const InputBoxWrapper = ({
min,
max,
overlayIcon = null,
extraInputProps = {},
overlayAction,
classes,
}: InputBoxProps) => {
let inputProps: any = { "data-index": index };
let inputProps: any = { "data-index": index, ...extraInputProps };
if (type === "number" && min) {
inputProps["min"] = min;

View File

@@ -84,6 +84,11 @@ export const modalBasic = {
height: 170,
maxWidth: 840,
},
moduleDescription: {
color: "#848484",
fontSize: 12,
fontStyle: "italic" as string,
},
};
export const tooltipHelper = {