mirror of
https://github.com/stonith404/pingvin-share.git
synced 2024-11-05 15:30:14 +01:00
Added granular control of expiration + 12/24 hour modes
This commit is contained in:
parent
56349c6f4c
commit
5988d5ffcc
@ -1,3 +1,4 @@
|
|||||||
SHOW_HOME_PAGE=true
|
SHOW_HOME_PAGE=true
|
||||||
ALLOW_REGISTRATION=true
|
ALLOW_REGISTRATION=true
|
||||||
MAX_FILE_SIZE=1000000000
|
MAX_FILE_SIZE=1000000000
|
||||||
|
TWELVE_HOUR_TIME=false
|
||||||
|
@ -5,7 +5,8 @@ const nextConfig = {
|
|||||||
ALLOW_REGISTRATION: process.env.ALLOW_REGISTRATION,
|
ALLOW_REGISTRATION: process.env.ALLOW_REGISTRATION,
|
||||||
SHOW_HOME_PAGE: process.env.SHOW_HOME_PAGE,
|
SHOW_HOME_PAGE: process.env.SHOW_HOME_PAGE,
|
||||||
MAX_FILE_SIZE: process.env.MAX_FILE_SIZE,
|
MAX_FILE_SIZE: process.env.MAX_FILE_SIZE,
|
||||||
BACKEND_URL: process.env.BACKEND_URL
|
BACKEND_URL: process.env.BACKEND_URL,
|
||||||
|
TWELVE_HOUR_TIME: process.env.TWELVE_HOUR_TIME
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import {
|
|||||||
Accordion,
|
Accordion,
|
||||||
Button,
|
Button,
|
||||||
Col,
|
Col,
|
||||||
|
Checkbox,
|
||||||
Grid,
|
Grid,
|
||||||
NumberInput,
|
NumberInput,
|
||||||
PasswordInput,
|
PasswordInput,
|
||||||
@ -10,11 +11,32 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { useForm, yupResolver } from "@mantine/form";
|
import {useForm, yupResolver} from "@mantine/form";
|
||||||
import { useModals } from "@mantine/modals";
|
import {useModals} from "@mantine/modals";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
import shareService from "../../services/share.service";
|
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 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();
|
||||||
|
|
||||||
|
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 = ({
|
const CreateUploadModalBody = ({
|
||||||
uploadCallback,
|
uploadCallback,
|
||||||
@ -44,7 +66,9 @@ const CreateUploadModalBody = ({
|
|||||||
|
|
||||||
password: undefined,
|
password: undefined,
|
||||||
maxViews: undefined,
|
maxViews: undefined,
|
||||||
expiration: "1-day",
|
expiration_num: 1,
|
||||||
|
expiration_unit: "-days",
|
||||||
|
never_expires: false
|
||||||
},
|
},
|
||||||
validate: yupResolver(validationSchema),
|
validate: yupResolver(validationSchema),
|
||||||
});
|
});
|
||||||
@ -55,7 +79,8 @@ const CreateUploadModalBody = ({
|
|||||||
if (!(await shareService.isShareIdAvailable(values.link))) {
|
if (!(await shareService.isShareIdAvailable(values.link))) {
|
||||||
form.setFieldError("link", "This link is already in use");
|
form.setFieldError("link", "This link is already in use");
|
||||||
} else {
|
} else {
|
||||||
uploadCallback(values.link, values.expiration, {
|
const expiration = form.values.never_expires ? "never" : form.values.expiration_num + form.values.expiration_unit;
|
||||||
|
uploadCallback(values.link, expiration, {
|
||||||
password: values.password,
|
password: values.password,
|
||||||
maxViews: values.maxViews,
|
maxViews: values.maxViews,
|
||||||
});
|
});
|
||||||
@ -90,7 +115,7 @@ const CreateUploadModalBody = ({
|
|||||||
</Col>
|
</Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Text
|
<Text italic
|
||||||
size="xs"
|
size="xs"
|
||||||
sx={(theme) => ({
|
sx={(theme) => ({
|
||||||
color: theme.colors.gray[6],
|
color: theme.colors.gray[6],
|
||||||
@ -99,18 +124,47 @@ const CreateUploadModalBody = ({
|
|||||||
{window.location.origin}/share/
|
{window.location.origin}/share/
|
||||||
{form.values.link == "" ? "myAwesomeShare" : form.values.link}
|
{form.values.link == "" ? "myAwesomeShare" : form.values.link}
|
||||||
</Text>
|
</Text>
|
||||||
<Select
|
<Grid align={form.errors.link ? "center" : "flex-end"}>
|
||||||
label="Expiration"
|
<Col xs={6}>
|
||||||
{...form.getInputProps("expiration")}
|
<NumberInput
|
||||||
data={[
|
min={1}
|
||||||
{value: "never", label: "Never"},
|
max={99999}
|
||||||
{value: "10-minutes", label: "10 Minutes"},
|
precision={0}
|
||||||
{value: "1-hour", label: "1 Hour"},
|
variant="filled"
|
||||||
{value: "1-day", label: "1 Day"},
|
label="Expiration"
|
||||||
{value: "1-week", label: "1 Week"},
|
placeholder="n"
|
||||||
{value: "1-month", label: "1 Month"},
|
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>
|
||||||
|
|
||||||
<Accordion>
|
<Accordion>
|
||||||
<Accordion.Item value="security" sx={{borderBottom: "none"}}>
|
<Accordion.Item value="security" sx={{borderBottom: "none"}}>
|
||||||
<Accordion.Control>Security options</Accordion.Control>
|
<Accordion.Control>Security options</Accordion.Control>
|
||||||
|
@ -14,6 +14,9 @@ import { useRouter } from "next/router";
|
|||||||
import { Copy } from "tabler-icons-react";
|
import { Copy } from "tabler-icons-react";
|
||||||
import { Share } from "../../types/share.type";
|
import { Share } from "../../types/share.type";
|
||||||
import toast from "../../utils/toast.util";
|
import toast from "../../utils/toast.util";
|
||||||
|
import getConfig from "next/config";
|
||||||
|
|
||||||
|
const {publicRuntimeConfig} = getConfig();
|
||||||
|
|
||||||
const showCompletedUploadModal = (
|
const showCompletedUploadModal = (
|
||||||
modals: ModalsContextProps,
|
modals: ModalsContextProps,
|
||||||
@ -62,7 +65,10 @@ const Body = ({share}: { share: Share }) => {
|
|||||||
{/* If our share.expiration is timestamp 0, show a different message */}
|
{/* If our share.expiration is timestamp 0, show a different message */}
|
||||||
{moment(share.expiration).unix() === 0
|
{moment(share.expiration).unix() === 0
|
||||||
? "This share will never expire."
|
? "This share will never expire."
|
||||||
: `This share will expire on ${moment(share.expiration).format("LLL")}`}
|
: `This share will expire on ${
|
||||||
|
(publicRuntimeConfig.TWELVE_HOUR_TIME === "true")
|
||||||
|
? moment(share.expiration).format("MMMM Do YYYY, h:mm a")
|
||||||
|
: moment(share.expiration).format("MMMM DD YYYY, HH:mm")}`}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
@ -1,125 +1,131 @@
|
|||||||
import {
|
import {
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
Button,
|
Button,
|
||||||
Center,
|
Center,
|
||||||
Group,
|
Group,
|
||||||
LoadingOverlay,
|
LoadingOverlay,
|
||||||
Space,
|
Space,
|
||||||
Stack,
|
Stack,
|
||||||
Table,
|
Table,
|
||||||
Text,
|
Text,
|
||||||
Title,
|
Title,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { useClipboard } from "@mantine/hooks";
|
import {useClipboard} from "@mantine/hooks";
|
||||||
import { useModals } from "@mantine/modals";
|
import {useModals} from "@mantine/modals";
|
||||||
import { NextLink } from "@mantine/next";
|
import {NextLink} from "@mantine/next";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { useEffect, useState } from "react";
|
import {useEffect, useState} from "react";
|
||||||
import { Link, Trash } from "tabler-icons-react";
|
import {Link, Trash} from "tabler-icons-react";
|
||||||
import Meta from "../../components/Meta";
|
import Meta from "../../components/Meta";
|
||||||
import shareService from "../../services/share.service";
|
import shareService from "../../services/share.service";
|
||||||
import { MyShare } from "../../types/share.type";
|
import {MyShare} from "../../types/share.type";
|
||||||
import toast from "../../utils/toast.util";
|
import toast from "../../utils/toast.util";
|
||||||
|
import getConfig from "next/config";
|
||||||
|
|
||||||
|
const {publicRuntimeConfig} = getConfig();
|
||||||
|
|
||||||
const MyShares = () => {
|
const MyShares = () => {
|
||||||
const modals = useModals();
|
const modals = useModals();
|
||||||
const clipboard = useClipboard();
|
const clipboard = useClipboard();
|
||||||
|
|
||||||
const [shares, setShares] = useState<MyShare[]>();
|
const [shares, setShares] = useState<MyShare[]>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
shareService.getMyShares().then((shares) => setShares(shares));
|
shareService.getMyShares().then((shares) => setShares(shares));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!shares) return <LoadingOverlay visible />;
|
if (!shares) return <LoadingOverlay visible/>;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Meta title="My shares" />
|
<Meta title="My shares"/>
|
||||||
<Title mb={30} order={3}>
|
<Title mb={30} order={3}>
|
||||||
My shares
|
My shares
|
||||||
</Title>
|
</Title>
|
||||||
{shares.length == 0 ? (
|
{shares.length == 0 ? (
|
||||||
<Center style={{ height: "70vh" }}>
|
<Center style={{height: "70vh"}}>
|
||||||
<Stack align="center" spacing={10}>
|
<Stack align="center" spacing={10}>
|
||||||
<Title order={3}>It's empty here 👀</Title>
|
<Title order={3}>It's empty here 👀</Title>
|
||||||
<Text>You don't have any shares.</Text>
|
<Text>You don't have any shares.</Text>
|
||||||
<Space h={5} />
|
<Space h={5}/>
|
||||||
<Button component={NextLink} href="/upload" variant="light">
|
<Button component={NextLink} href="/upload" variant="light">
|
||||||
Create one
|
Create one
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Center>
|
</Center>
|
||||||
) : (
|
) : (
|
||||||
<Table>
|
<Table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Visitors</th>
|
<th>Visitors</th>
|
||||||
<th>Expires at</th>
|
<th>Expires at</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{shares.map((share) => (
|
{shares.map((share) => (
|
||||||
<tr key={share.id}>
|
<tr key={share.id}>
|
||||||
<td>{share.id}</td>
|
<td>{share.id}</td>
|
||||||
<td>{share.views}</td>
|
<td>{share.views}</td>
|
||||||
<td>
|
<td>
|
||||||
{moment(share.expiration).unix() === 0
|
{moment(share.expiration).unix() === 0
|
||||||
? "Never"
|
? "Never"
|
||||||
: moment(share.expiration).format("MMMM DD YYYY, HH:mm")}
|
: (publicRuntimeConfig.TWELVE_HOUR_TIME === "true")
|
||||||
</td>
|
? moment(share.expiration).format("MMMM Do YYYY, h:mm a")
|
||||||
<td>
|
: moment(share.expiration).format("MMMM DD YYYY, HH:mm")
|
||||||
<Group position="right">
|
}
|
||||||
<ActionIcon
|
</td>
|
||||||
color="victoria"
|
<td>
|
||||||
variant="light"
|
<Group position="right">
|
||||||
size={25}
|
<ActionIcon
|
||||||
onClick={() => {
|
color="victoria"
|
||||||
clipboard.copy(
|
variant="light"
|
||||||
`${window.location.origin}/share/${share.id}`
|
size={25}
|
||||||
);
|
onClick={() => {
|
||||||
toast.success("Your link was copied to the keyboard.");
|
clipboard.copy(
|
||||||
}}
|
`${window.location.origin}/share/${share.id}`
|
||||||
>
|
);
|
||||||
<Link />
|
toast.success("Your link was copied to the keyboard.");
|
||||||
</ActionIcon>
|
}}
|
||||||
<ActionIcon
|
>
|
||||||
color="red"
|
<Link/>
|
||||||
variant="light"
|
</ActionIcon>
|
||||||
size={25}
|
<ActionIcon
|
||||||
onClick={() => {
|
color="red"
|
||||||
modals.openConfirmModal({
|
variant="light"
|
||||||
title: `Delete share ${share.id}`,
|
size={25}
|
||||||
children: (
|
onClick={() => {
|
||||||
<Text size="sm">
|
modals.openConfirmModal({
|
||||||
Do you really want to delete this share?
|
title: `Delete share ${share.id}`,
|
||||||
</Text>
|
children: (
|
||||||
),
|
<Text size="sm">
|
||||||
confirmProps: {
|
Do you really want to delete this share?
|
||||||
color: "red",
|
</Text>
|
||||||
},
|
),
|
||||||
labels: { confirm: "Confirm", cancel: "Cancel" },
|
confirmProps: {
|
||||||
onConfirm: () => {
|
color: "red",
|
||||||
shareService.remove(share.id);
|
},
|
||||||
setShares(
|
labels: {confirm: "Confirm", cancel: "Cancel"},
|
||||||
shares.filter((item) => item.id !== share.id)
|
onConfirm: () => {
|
||||||
);
|
shareService.remove(share.id);
|
||||||
},
|
setShares(
|
||||||
});
|
shares.filter((item) => item.id !== share.id)
|
||||||
}}
|
);
|
||||||
>
|
},
|
||||||
<Trash />
|
});
|
||||||
</ActionIcon>
|
}}
|
||||||
</Group>
|
>
|
||||||
</td>
|
<Trash/>
|
||||||
</tr>
|
</ActionIcon>
|
||||||
))}
|
</Group>
|
||||||
</tbody>
|
</td>
|
||||||
</Table>
|
</tr>
|
||||||
)}
|
))}
|
||||||
</>
|
</tbody>
|
||||||
);
|
</Table>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MyShares;
|
export default MyShares;
|
||||||
|
Loading…
Reference in New Issue
Block a user