Merge remote-tracking branch 'stonith404/main' into main

This commit is contained in:
Elias Schneider
2022-10-16 00:08:37 +02:00
47 changed files with 6692 additions and 881 deletions

View File

@@ -1,197 +1,229 @@
import {
Accordion,
Button,
Col,
Checkbox,
Grid,
NumberInput,
PasswordInput,
Select,
Stack,
Text,
TextInput,
Accordion,
Button,
Col,
Checkbox,
Grid,
NumberInput,
PasswordInput,
Select,
Stack,
Text,
TextInput,
} from "@mantine/core";
import {useForm, yupResolver} from "@mantine/form";
import {useModals} from "@mantine/modals";
import { useForm, yupResolver } from "@mantine/form";
import { useModals } from "@mantine/modals";
import * as yup from "yup";
import shareService from "../../services/share.service";
import {ShareSecurity} from "../../types/share.type";
import { ShareSecurity } from "../../types/share.type";
import moment from "moment";
import getConfig from "next/config";
const {publicRuntimeConfig} = getConfig();
const { publicRuntimeConfig } = getConfig();
const PreviewExpiration = ({form}: { form: any }) => {
const value = form.values.never_expires ? "never" : form.values.expiration_num + form.values.expiration_unit;
if (value === "never") return "This share will never expire.";
const PreviewExpiration = ({ form }: { form: any }) => {
const value = form.values.never_expires
? "never"
: form.values.expiration_num + form.values.expiration_unit;
if (value === "never") return "This share will never expire.";
const expirationDate = moment()
.add(
value.split("-")[0],
value.split("-")[1] as moment.unitOfTime.DurationConstructor
)
.toDate();
const expirationDate = moment()
.add(
value.split("-")[0],
value.split("-")[1] as moment.unitOfTime.DurationConstructor
)
.toDate();
if (publicRuntimeConfig.TWELVE_HOUR_TIME === "true")
return `This share will expire on ${moment(expirationDate).format("MMMM Do YYYY, h:mm a")}`;
else
return `This share will expire on ${moment(expirationDate).format("MMMM DD YYYY, HH:mm")}`;
}
if (publicRuntimeConfig.TWELVE_HOUR_TIME === "true")
return `This share will expire on ${moment(expirationDate).format(
"MMMM Do YYYY, h:mm a"
)}`;
else
return `This share will expire on ${moment(expirationDate).format(
"MMMM DD YYYY, HH:mm"
)}`;
};
const CreateUploadModalBody = ({
uploadCallback,
}: {
uploadCallback: (
id: string,
expiration: string,
security: ShareSecurity
) => void;
uploadCallback,
}: {
uploadCallback: (
id: string,
expiration: string,
security: ShareSecurity
) => void;
}) => {
const modals = useModals();
const validationSchema = yup.object().shape({
link: yup
.string()
.required()
.min(3)
.max(100)
.matches(new RegExp("^[a-zA-Z0-9_-]*$"), {
message: "Can only contain letters, numbers, underscores and hyphens",
}),
password: yup.string().min(3).max(30),
maxViews: yup.number().min(1),
});
const form = useForm({
initialValues: {
link: "",
const modals = useModals();
const validationSchema = yup.object().shape({
link: yup
.string()
.required()
.min(3)
.max(50)
.matches(new RegExp("^[a-zA-Z0-9_-]*$"), {
message: "Can only contain letters, numbers, underscores and hyphens",
}),
password: yup.string().min(3).max(30),
maxViews: yup.number().min(1),
});
const form = useForm({
initialValues: {
link: "",
password: undefined,
maxViews: undefined,
expiration_num: 1,
expiration_unit: "-days",
never_expires: false
},
validate: yupResolver(validationSchema),
});
password: undefined,
maxViews: undefined,
expiration_num: 1,
expiration_unit: "-days",
never_expires: false,
},
validate: yupResolver(validationSchema),
});
return (
<form
onSubmit={form.onSubmit(async (values) => {
if (!(await shareService.isShareIdAvailable(values.link))) {
form.setFieldError("link", "This link is already in use");
} else {
const expiration = form.values.never_expires ? "never" : form.values.expiration_num + form.values.expiration_unit;
uploadCallback(values.link, expiration, {
password: values.password,
maxViews: values.maxViews,
});
modals.closeAll();
}
})}
return (
<form
onSubmit={form.onSubmit(async (values) => {
if (!(await shareService.isShareIdAvailable(values.link))) {
form.setFieldError("link", "This link is already in use");
} else {
const expiration = form.values.never_expires
? "never"
: form.values.expiration_num + form.values.expiration_unit;
uploadCallback(values.link, expiration, {
password: values.password,
maxViews: values.maxViews,
});
modals.closeAll();
}
})}
>
<Stack align="stretch">
<Grid align={form.errors.link ? "center" : "flex-end"}>
<Col xs={9}>
<TextInput
variant="filled"
label="Link"
placeholder="myAwesomeShare"
{...form.getInputProps("link")}
/>
</Col>
<Col xs={3}>
<Button
variant="outline"
onClick={() =>
form.setFieldValue(
"link",
Buffer.from(Math.random().toString(), "utf8")
.toString("base64")
.substr(10, 7)
)
}
>
Generate
</Button>
</Col>
</Grid>
<Text
italic
size="xs"
sx={(theme) => ({
color: theme.colors.gray[6],
})}
>
<Stack align="stretch">
<Grid align={form.errors.link ? "center" : "flex-end"}>
<Col xs={9}>
<TextInput
variant="filled"
label="Link"
placeholder="myAwesomeShare"
{...form.getInputProps("link")}
/>
</Col>
<Col xs={3}>
<Button
variant="outline"
onClick={() =>
form.setFieldValue(
"link",
Buffer.from(Math.random().toString(), "utf8")
.toString("base64")
.substr(10, 7)
)
}
>
Generate
</Button>
</Col>
</Grid>
{window.location.origin}/share/
{form.values.link == "" ? "myAwesomeShare" : form.values.link}
</Text>
<Grid align={form.errors.link ? "center" : "flex-end"}>
<Col xs={6}>
<NumberInput
min={1}
max={99999}
precision={0}
variant="filled"
label="Expiration"
placeholder="n"
disabled={form.values.never_expires}
{...form.getInputProps("expiration_num")}
/>
</Col>
<Col xs={6}>
<Select
disabled={form.values.never_expires}
{...form.getInputProps("expiration_unit")}
data={[
// Set the label to singular if the number is 1, else plural
{
value: "-minutes",
label:
"Minute" + (form.values.expiration_num == 1 ? "" : "s"),
},
{
value: "-hours",
label: "Hour" + (form.values.expiration_num == 1 ? "" : "s"),
},
{
value: "-days",
label: "Day" + (form.values.expiration_num == 1 ? "" : "s"),
},
{
value: "-weeks",
label: "Week" + (form.values.expiration_num == 1 ? "" : "s"),
},
{
value: "-months",
label: "Month" + (form.values.expiration_num == 1 ? "" : "s"),
},
{
value: "-years",
label: "Year" + (form.values.expiration_num == 1 ? "" : "s"),
},
]}
/>
</Col>
</Grid>
<Checkbox
label="Never Expires"
{...form.getInputProps("never_expires")}
/>
<Text italic
size="xs"
sx={(theme) => ({
color: theme.colors.gray[6],
})}
>
{window.location.origin}/share/
{form.values.link == "" ? "myAwesomeShare" : form.values.link}
</Text>
<Grid align={form.errors.link ? "center" : "flex-end"}>
<Col xs={6}>
<NumberInput
min={1}
max={99999}
precision={0}
variant="filled"
label="Expiration"
placeholder="n"
disabled={form.values.never_expires}
{...form.getInputProps("expiration_num")}
/>
</Col>
<Col xs={6}>
<Select
disabled={form.values.never_expires}
{...form.getInputProps("expiration_unit")}
data={[
// Set the label to singular if the number is 1, else plural
{value: "-minutes", label: "Minute" + (form.values.expiration_num == 1 ? "" : "s")},
{value: "-hours", label: "Hour" + (form.values.expiration_num == 1 ? "" : "s")},
{value: "-days", label: "Day" + (form.values.expiration_num == 1 ? "" : "s")},
{value: "-weeks", label: "Week" + (form.values.expiration_num == 1 ? "" : "s")},
{value: "-months", label: "Month" + (form.values.expiration_num == 1 ? "" : "s")},
{value: "-years", label: "Year" + (form.values.expiration_num == 1 ? "" : "s")}
]}
/>
</Col>
</Grid>
<Checkbox label="Never Expires" {...form.getInputProps("never_expires")} />
{/* Preview expiration date text */}
<Text
italic
size="xs"
sx={(theme) => ({
color: theme.colors.gray[6],
})}
>
{PreviewExpiration({ form })}
</Text>
{/* Preview expiration date text */}
<Text italic
size="xs"
sx={(theme) => ({
color: theme.colors.gray[6],
})}
>
{PreviewExpiration({form})}
</Text>
<Accordion>
<Accordion.Item value="security" sx={{borderBottom: "none"}}>
<Accordion.Control>Security options</Accordion.Control>
<Accordion.Panel>
<Stack align="stretch">
<PasswordInput
variant="filled"
placeholder="No password"
label="Password protection"
{...form.getInputProps("password")}
/>
<NumberInput
min={1}
type="number"
variant="filled"
placeholder="No limit"
label="Maximal views"
{...form.getInputProps("maxViews")}
/>
</Stack>
</Accordion.Panel>
</Accordion.Item>
</Accordion>
<Button type="submit">Share</Button>
</Stack>
</form>
);
<Accordion>
<Accordion.Item value="security" sx={{ borderBottom: "none" }}>
<Accordion.Control>Security options</Accordion.Control>
<Accordion.Panel>
<Stack align="stretch">
<PasswordInput
variant="filled"
placeholder="No password"
label="Password protection"
{...form.getInputProps("password")}
/>
<NumberInput
min={1}
type="number"
variant="filled"
placeholder="No limit"
label="Maximal views"
{...form.getInputProps("maxViews")}
/>
</Stack>
</Accordion.Panel>
</Accordion.Item>
</Accordion>
<Button type="submit">Share</Button>
</Stack>
</form>
);
};
export default CreateUploadModalBody;

View File

@@ -1,6 +1,7 @@
import { Button, Tooltip } from "@mantine/core";
import { Button } from "@mantine/core";
import { useEffect, useState } from "react";
import shareService from "../../services/share.service";
import toast from "../../utils/toast.util";
const DownloadAllButton = ({ shareId }: { shareId: string }) => {
const [isZipReady, setIsZipReady] = useState(false);
@@ -25,28 +26,25 @@ const DownloadAllButton = ({ shareId }: { shareId: string }) => {
setIsZipReady(share.isZipReady);
if (share.isZipReady) clearInterval(timer);
})
.catch(() => {});
.catch(() => clearInterval(timer));
}, 5000);
return () => {
clearInterval(timer);
};
}, []);
if (!isZipReady)
return (
<Tooltip
position="bottom"
width={220}
withArrow
label="The share is preparing. This can take a few minutes."
>
<Button variant="outline" onClick={downloadAll} disabled>
Download all
</Button>
</Tooltip>
);
return (
<Button variant="outline" loading={isLoading} onClick={downloadAll}>
<Button
variant="outline"
loading={isLoading}
onClick={() => {
if (!isZipReady) {
toast.error("The share is preparing. Try again in a few minutes.");
} else {
downloadAll();
}
}}
>
Download all
</Button>
);

View File

@@ -1,5 +1,5 @@
import { ActionIcon, Loader, Skeleton, Table } from "@mantine/core";
import { CircleCheck, Download } from "tabler-icons-react";
import { TbCircleCheck, TbDownload } from "react-icons/tb";;
import shareService from "../../services/share.service";
import { byteStringToHumanSizeString } from "../../utils/math/byteStringToHumanSizeString.util";
@@ -39,7 +39,7 @@ const FileList = ({
file.uploadingState != "finished" ? (
<Loader size={22} />
) : (
<CircleCheck color="green" size={22} />
<TbCircleCheck color="green" size={22} />
)
) : (
<ActionIcon
@@ -48,7 +48,7 @@ const FileList = ({
await shareService.downloadFile(shareId, file.id);
}}
>
<Download />
<TbDownload />
</ActionIcon>
)}
</td>