1
0
mirror of https://github.com/stonith404/pingvin-share.git synced 2024-07-04 00:10:17 +02:00

refactor: migrate to mantine v5

This commit is contained in:
Elias Schneider 2022-10-10 22:14:23 +02:00
parent 68ce8af197
commit db7edc4cd6
14 changed files with 1073 additions and 859 deletions

View File

@ -3,7 +3,6 @@
const withPWA = require("next-pwa"); const withPWA = require("next-pwa");
const nextConfig = withPWA({ const nextConfig = withPWA({
reactStrictMode: true,
pwa: { pwa: {
dest: "public", dest: "public",
disable: process.env.NODE_ENV == "development" disable: process.env.NODE_ENV == "development"

File diff suppressed because it is too large Load Diff

View File

@ -9,13 +9,13 @@
"format": "prettier --write \"src/**/*.ts\"" "format": "prettier --write \"src/**/*.ts\""
}, },
"dependencies": { "dependencies": {
"@mantine/core": "^4.2.0", "@mantine/core": "^5.5.2",
"@mantine/dropzone": "^4.2.0", "@mantine/dropzone": "^5.5.2",
"@mantine/form": "^4.2.0", "@mantine/form": "^5.5.2",
"@mantine/hooks": "^4.2.0", "@mantine/hooks": "^5.5.2",
"@mantine/modals": "^4.2.0", "@mantine/modals": "^5.5.2",
"@mantine/next": "^4.2.0", "@mantine/next": "^5.5.2",
"@mantine/notifications": "^4.2.0", "@mantine/notifications": "^5.5.2",
"axios": "^0.26.1", "axios": "^0.26.1",
"cookies-next": "^2.0.4", "cookies-next": "^2.0.4",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",

View File

@ -9,6 +9,7 @@ import {
Title, Title,
} from "@mantine/core"; } from "@mantine/core";
import { useForm, yupResolver } from "@mantine/form"; import { useForm, yupResolver } from "@mantine/form";
import { NextLink } from "@mantine/next";
import getConfig from "next/config"; import getConfig from "next/config";
import * as yup from "yup"; import * as yup from "yup";
import authService from "../../services/auth.service"; import authService from "../../services/auth.service";
@ -27,7 +28,7 @@ const AuthForm = ({ mode }: { mode: "signUp" | "signIn" }) => {
email: "", email: "",
password: "", password: "",
}, },
schema: yupResolver(validationSchema), validate: yupResolver(validationSchema),
}); });
const signIn = (email: string, password: string) => { const signIn = (email: string, password: string) => {
@ -59,7 +60,11 @@ const AuthForm = ({ mode }: { mode: "signUp" | "signIn" }) => {
{mode == "signUp" {mode == "signUp"
? "You have an account already?" ? "You have an account already?"
: "You don't have an account yet?"}{" "} : "You don't have an account yet?"}{" "}
<Anchor href={mode == "signUp" ? "signIn" : "signUp"} size="sm"> <Anchor
component={NextLink}
href={mode == "signUp" ? "signIn" : "signUp"}
size="sm"
>
{mode == "signUp" ? "Sign in" : "Sign up"} {mode == "signUp" ? "Sign in" : "Sign up"}
</Anchor> </Anchor>
</Text> </Text>

View File

@ -6,40 +6,34 @@ import ToggleThemeButton from "./ToggleThemeButton";
const ActionAvatar = () => { const ActionAvatar = () => {
return ( return (
<Menu <Menu>
control={ <Menu.Target>
<ActionIcon> <ActionIcon>
<Avatar size={28} radius="xl" /> <Avatar size={28} radius="xl" />
</ActionIcon> </ActionIcon>
} </Menu.Target>
> <Menu.Dropdown>
<Menu.Label>My account</Menu.Label> <Menu.Label>My account</Menu.Label>
<Menu.Item <Menu.Item
component={NextLink} component={NextLink}
href="/account/shares" href="/account/shares"
icon={<Link size={14} />} icon={<Link size={14} />}
> >
Shares Shares
</Menu.Item> </Menu.Item>
{/* <Menu.Item <Menu.Item
component={NextLink} onClick={async () => {
href="/account/shares" authService.signOut();
icon={<Settings size={14} />} }}
> icon={<DoorExit size={14} />}
Settings >
</Menu.Item> */} Sign out
<Menu.Item </Menu.Item>
onClick={async () => { <Menu.Label>Settings</Menu.Label>
await authService.signOut(); <Menu.Item icon={<Moon size={14} />}>
}} <ToggleThemeButton />
icon={<DoorExit size={14} />} </Menu.Item>
> </Menu.Dropdown>
Sign out
</Menu.Item>
<Menu.Label>Settings</Menu.Label>
<Menu.Item icon={<Moon size={14} />}>
<ToggleThemeButton />
</Menu.Item>
</Menu> </Menu>
); );
}; };

View File

@ -1,23 +1,26 @@
import { import {
Burger, Burger,
Container, Container,
createStyles,
Group, Group,
Header as MantineHeader, Header,
Image,
Paper, Paper,
Stack,
Text, Text,
Transition, Transition,
} from "@mantine/core"; } from "@mantine/core";
import { useBooleanToggle } from "@mantine/hooks"; import { useDisclosure } from "@mantine/hooks";
import { NextLink } from "@mantine/next"; import { NextLink } from "@mantine/next";
import getConfig from "next/config"; import getConfig from "next/config";
import Image from "next/image";
import { ReactNode, useEffect, useState } from "react"; import { ReactNode, useEffect, useState } from "react";
import useUser from "../../hooks/user.hook"; import useUser from "../../hooks/user.hook";
import headerStyle from "../../styles/header.style";
import ActionAvatar from "./ActionAvatar"; import ActionAvatar from "./ActionAvatar";
const { publicRuntimeConfig } = getConfig(); const { publicRuntimeConfig } = getConfig();
const HEADER_HEIGHT = 60;
type Link = { type Link = {
link?: string; link?: string;
label?: string; label?: string;
@ -25,14 +28,90 @@ type Link = {
action?: () => Promise<void>; action?: () => Promise<void>;
}; };
const Header = () => { const useStyles = createStyles((theme) => ({
const [opened, toggleOpened] = useBooleanToggle(false); root: {
const [active, setActive] = useState<string>(); position: "relative",
zIndex: 1,
},
dropdown: {
position: "absolute",
top: HEADER_HEIGHT,
left: 0,
right: 0,
zIndex: 0,
borderTopRightRadius: 0,
borderTopLeftRadius: 0,
borderTopWidth: 0,
overflow: "hidden",
[theme.fn.largerThan("sm")]: {
display: "none",
},
},
header: {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
height: "100%",
},
links: {
[theme.fn.smallerThan("sm")]: {
display: "none",
},
},
burger: {
[theme.fn.largerThan("sm")]: {
display: "none",
},
},
link: {
display: "block",
lineHeight: 1,
padding: "8px 12px",
borderRadius: theme.radius.sm,
textDecoration: "none",
color:
theme.colorScheme === "dark"
? theme.colors.dark[0]
: theme.colors.gray[7],
fontSize: theme.fontSizes.sm,
fontWeight: 500,
"&:hover": {
backgroundColor:
theme.colorScheme === "dark"
? theme.colors.dark[6]
: theme.colors.gray[0],
},
[theme.fn.smallerThan("sm")]: {
borderRadius: 0,
padding: theme.spacing.md,
},
},
linkActive: {
"&, &:hover": {
backgroundColor:
theme.colorScheme === "dark"
? theme.fn.rgba(theme.colors[theme.primaryColor][9], 0.25)
: theme.colors[theme.primaryColor][0],
color:
theme.colors[theme.primaryColor][theme.colorScheme === "dark" ? 3 : 7],
},
},
}));
const NavBar = () => {
const user = useUser(); const user = useUser();
const [opened, toggleOpened] = useDisclosure(false);
const { classes, cx } = headerStyle(); const [authenticatedLinks, setAuthenticatedLinks] = useState<Link[]>([
const authenticatedLinks: Link[] = [
{ {
link: "/upload", link: "/upload",
label: "Upload", label: "Upload",
@ -40,61 +119,66 @@ const Header = () => {
{ {
component: <ActionAvatar />, component: <ActionAvatar />,
}, },
]; ]);
const unauthenticatedLinks: Link[] | undefined = [ const [unauthenticatedLinks, setUnauthenticatedLinks] = useState<Link[]>([
{ {
link: "/auth/signIn", link: "/auth/signIn",
label: "Sign in", label: "Sign in",
}, },
]; ]);
if (publicRuntimeConfig.SHOW_HOME_PAGE == "true") useEffect(() => {
unauthenticatedLinks.unshift({ if (publicRuntimeConfig.SHOW_HOME_PAGE == "true")
link: "/", setUnauthenticatedLinks((array) => [
label: "Home", {
}); link: "/",
label: "Home",
},
...array,
]);
if (publicRuntimeConfig.ALLOW_REGISTRATION == "true") if (publicRuntimeConfig.ALLOW_REGISTRATION == "true")
unauthenticatedLinks.push({ setUnauthenticatedLinks((array) => [
link: "/auth/signUp", ...array,
label: "Sign up", {
}); link: "/auth/signUp",
label: "Sign up",
},
]);
}, []);
const links = user ? authenticatedLinks : unauthenticatedLinks; const { classes, cx } = useStyles();
console.log(user);
const items = links.map((link, i) => { const items = (
if (link.component) { <>
return ( {(user ? authenticatedLinks : unauthenticatedLinks).map((link) => {
<Container key={i} pl={5} py={15}> if (link.component) {
{link.component} return (
</Container> <>
); <Container pl={5} py={15}>
} {link.component}
if (link) { </Container>
// eslint-disable-next-line react-hooks/rules-of-hooks </>
useEffect(() => { );
if (window.location.pathname == link.link) {
setActive(link.link);
} }
}, []); return (
return ( <NextLink
<NextLink key={link.label}
key={link.label} href={link.link ?? ""}
href={link.link ?? ""} onClick={() => toggleOpened.toggle()}
onClick={link.action} className={cx(classes.link, {
className={cx(classes.link, { [classes.linkActive]: window.location.pathname == link.link,
[classes.linkActive]: link.link && active === link.link, })}
})} >
> {link.label}
{link.label} </NextLink>
</NextLink> );
); })}
} </>
}); );
return ( return (
<MantineHeader height={60} mb={20} className={classes.root}> <Header height={HEADER_HEIGHT} mb={40} className={classes.root}>
<Container className={classes.header}> <Container className={classes.header}>
<NextLink href="/"> <NextLink href="/">
<Group> <Group>
@ -108,24 +192,24 @@ const Header = () => {
</Group> </Group>
</NextLink> </NextLink>
<Group spacing={5} className={classes.links}> <Group spacing={5} className={classes.links}>
{items} <Group>{items} </Group>
</Group> </Group>
<Burger <Burger
opened={opened} opened={opened}
onClick={() => toggleOpened()} onClick={() => toggleOpened.toggle()}
className={classes.burger} className={classes.burger}
size="sm" size="sm"
/> />
<Transition transition="pop-top-right" duration={200} mounted={opened}> <Transition transition="pop-top-right" duration={200} mounted={opened}>
{(styles) => ( {(styles) => (
<Paper className={classes.dropdown} withBorder style={styles}> <Paper className={classes.dropdown} withBorder style={styles}>
{items} <Stack spacing={0}> {items}</Stack>
</Paper> </Paper>
)} )}
</Transition> </Transition>
</Container> </Container>
</MantineHeader> </Header>
); );
}; };
export default Header;
export default NavBar;

View File

@ -3,10 +3,10 @@ import {
Button, Button,
Col, Col,
Grid, Grid,
Group,
NumberInput, NumberInput,
PasswordInput, PasswordInput,
Select, Select,
Stack,
Text, Text,
TextInput, TextInput,
} from "@mantine/core"; } from "@mantine/core";
@ -46,7 +46,7 @@ const CreateUploadModalBody = ({
maxViews: undefined, maxViews: undefined,
expiration: "1-day", expiration: "1-day",
}, },
schema: yupResolver(validationSchema), validate: yupResolver(validationSchema),
}); });
return ( return (
@ -63,7 +63,7 @@ const CreateUploadModalBody = ({
} }
})} })}
> >
<Group direction="column" grow> <Stack align="stretch">
<Grid align={form.errors.link ? "center" : "flex-end"}> <Grid align={form.errors.link ? "center" : "flex-end"}>
<Col xs={9}> <Col xs={9}>
<TextInput <TextInput
@ -115,10 +115,10 @@ const CreateUploadModalBody = ({
/> />
<Accordion> <Accordion>
<Accordion.Item <Accordion.Item
label="Security options" value="Security options"
sx={{ borderBottom: "none" }} sx={{ borderBottom: "none" }}
> >
<Group direction="column" grow> <Stack align="stretch">
<PasswordInput <PasswordInput
variant="filled" variant="filled"
placeholder="No password" placeholder="No password"
@ -134,11 +134,11 @@ const CreateUploadModalBody = ({
label="Maximal views" label="Maximal views"
{...form.getInputProps("maxViews")} {...form.getInputProps("maxViews")}
/> />
</Group> </Stack>
</Accordion.Item> </Accordion.Item>
</Accordion> </Accordion>
<Button type="submit">Share</Button> <Button type="submit">Share</Button>
</Group> </Stack>
</form> </form>
); );
}; };

View File

@ -19,10 +19,13 @@ const DownloadAllButton = ({ shareId }: { shareId: string }) => {
.catch(() => {}); .catch(() => {});
const timer = setInterval(() => { const timer = setInterval(() => {
shareService.getMetaData(shareId).then((share) => { shareService
setIsZipReady(share.isZipReady); .getMetaData(shareId)
if (share.isZipReady) clearInterval(timer); .then((share) => {
}).catch(() => {}); setIsZipReady(share.isZipReady);
if (share.isZipReady) clearInterval(timer);
})
.catch(() => {});
}, 5000); }, 5000);
return () => { return () => {
clearInterval(timer); clearInterval(timer);
@ -32,7 +35,6 @@ const DownloadAllButton = ({ shareId }: { shareId: string }) => {
if (!isZipReady) if (!isZipReady)
return ( return (
<Tooltip <Tooltip
wrapLines
position="bottom" position="bottom"
width={220} width={220}
withArrow withArrow

View File

@ -1,5 +1,4 @@
import { ActionIcon, Loader, Skeleton, Table } from "@mantine/core"; import { ActionIcon, Loader, Skeleton, Table } from "@mantine/core";
import { useRouter } from "next/router";
import { CircleCheck, Download } from "tabler-icons-react"; import { CircleCheck, Download } from "tabler-icons-react";
import shareService from "../../services/share.service"; import shareService from "../../services/share.service";
@ -14,8 +13,6 @@ const FileList = ({
shareId: string; shareId: string;
isLoading: boolean; isLoading: boolean;
}) => { }) => {
const router = useRouter();
const skeletonRows = [...Array(5)].map((c, i) => ( const skeletonRows = [...Array(5)].map((c, i) => (
<tr key={i}> <tr key={i}>
<td> <td>

View File

@ -1,4 +1,4 @@
import { Button, Group, PasswordInput, Text, Title } from "@mantine/core"; import { Button, Group, PasswordInput, Stack, Text, Title } from "@mantine/core";
import { ModalsContextProps } from "@mantine/modals/lib/context"; import { ModalsContextProps } from "@mantine/modals/lib/context";
import { useState } from "react"; import { useState } from "react";
@ -27,7 +27,7 @@ const Body = ({ submitCallback }: { submitCallback: any }) => {
const [passwordWrong, setPasswordWrong] = useState(false); const [passwordWrong, setPasswordWrong] = useState(false);
return ( return (
<> <>
<Group grow direction="column"> <Stack align="stretch">
<PasswordInput <PasswordInput
variant="filled" variant="filled"
placeholder="Password" placeholder="Password"
@ -50,7 +50,7 @@ const Body = ({ submitCallback }: { submitCallback: any }) => {
> >
Submit Submit
</Button> </Button>
</Group> </Stack>
</> </>
); );
}; };

View File

@ -1,4 +1,4 @@
import { Button, Group, Text, Title } from "@mantine/core"; import { Button, Group, Stack, Text, Title } from "@mantine/core";
import { useModals } from "@mantine/modals"; import { useModals } from "@mantine/modals";
import { ModalsContextProps } from "@mantine/modals/lib/context"; import { ModalsContextProps } from "@mantine/modals/lib/context";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
@ -23,7 +23,7 @@ const Body = ({ text }: { text: string }) => {
const router = useRouter(); const router = useRouter();
return ( return (
<> <>
<Group grow direction="column"> <Stack align="stretch">
<Text size="sm">{text}</Text> <Text size="sm">{text}</Text>
<Button <Button
onClick={() => { onClick={() => {
@ -33,7 +33,7 @@ const Body = ({ text }: { text: string }) => {
> >
Go back Go back
</Button> </Button>
</Group> </Stack>
</> </>
); );
}; };

View File

@ -3,17 +3,17 @@ import {
Center, Center,
createStyles, createStyles,
Group, Group,
MantineTheme,
Text, Text,
useMantineTheme, useMantineTheme,
} from "@mantine/core"; } from "@mantine/core";
import { Dropzone as MantineDropzone, DropzoneStatus } from "@mantine/dropzone"; import { Dropzone as MantineDropzone } from "@mantine/dropzone";
import getConfig from "next/config"; import getConfig from "next/config";
import React, { Dispatch, ForwardedRef, SetStateAction, useRef } from "react"; import { Dispatch, ForwardedRef, SetStateAction, useRef } from "react";
import { CloudUpload, Upload } from "tabler-icons-react"; import { CloudUpload, Upload } from "tabler-icons-react";
import { byteStringToHumanSizeString } from "../../utils/math/byteStringToHumanSizeString.util";
import toast from "../../utils/toast.util"; import toast from "../../utils/toast.util";
const { publicRuntimeConfig } = getConfig() const { publicRuntimeConfig } = getConfig();
const useStyles = createStyles((theme) => ({ const useStyles = createStyles((theme) => ({
wrapper: { wrapper: {
@ -39,14 +39,6 @@ const useStyles = createStyles((theme) => ({
}, },
})); }));
function getActiveColor(status: DropzoneStatus, theme: MantineTheme) {
return status.accepted
? theme.colors[theme.primaryColor][6]
: theme.colorScheme === "dark"
? theme.colors.dark[2]
: theme.black;
}
const Dropzone = ({ const Dropzone = ({
isUploading, isUploading,
setFiles, setFiles,
@ -76,26 +68,20 @@ const Dropzone = ({
className={classes.dropzone} className={classes.dropzone}
radius="md" radius="md"
> >
{(status) => ( <div style={{ pointerEvents: "none" }}>
<div style={{ pointerEvents: "none" }}> <Group position="center">
<Group position="center"> <CloudUpload size={50} />
<CloudUpload size={50} color={getActiveColor(status, theme)} /> </Group>
</Group> <Text align="center" weight={700} size="lg" mt="xl">
<Text Upload files
align="center" </Text>
weight={700} <Text align="center" size="sm" mt="xs" color="dimmed">
size="lg" Drag&apos;n&apos;drop files here to start your share. We can accept
mt="xl" only files that are less than{" "}
sx={{ color: getActiveColor(status, theme) }} {byteStringToHumanSizeString(publicRuntimeConfig.MAX_FILE_SIZE)} in
> size.
{status.accepted ? "Drop files here" : "Upload files"} </Text>
</Text> </div>
<Text align="center" size="sm" mt="xs" color="dimmed">
Drag and drop your files or use the upload button to start your
share.
</Text>
</div>
)}
</MantineDropzone> </MantineDropzone>
<Center> <Center>
<Button <Button

View File

@ -2,9 +2,10 @@ import {
ActionIcon, ActionIcon,
Button, Button,
Group, Group,
Stack,
Text, Text,
TextInput, TextInput,
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";
@ -24,9 +25,9 @@ const showCompletedUploadModal = (
withCloseButton: false, withCloseButton: false,
closeOnEscape: false, closeOnEscape: false,
title: ( title: (
<Group grow direction="column" spacing={0}> <Stack align="stretch" spacing={0}>
<Title order={4}>Share ready</Title> <Title order={4}>Share ready</Title>
</Group> </Stack>
), ),
children: <Body share={share} />, children: <Body share={share} />,
}); });
@ -38,7 +39,7 @@ const Body = ({ share }: { share: Share }) => {
const router = useRouter(); const router = useRouter();
const link = `${window.location.origin}/share/${share.id}`; const link = `${window.location.origin}/share/${share.id}`;
return ( return (
<Group grow direction="column"> <Stack align="stretch">
<TextInput <TextInput
variant="filled" variant="filled"
value={link} value={link}
@ -70,7 +71,7 @@ const Body = ({ share }: { share: Share }) => {
> >
Done Done
</Button> </Button>
</Group> </Stack>
); );
}; };

View File

@ -15,6 +15,7 @@ const signUp = async (email: string, password: string) => {
const signOut = () => { const signOut = () => {
setCookies("access_token", null); setCookies("access_token", null);
setCookies("refresh_token", null);
window.location.reload(); window.location.reload();
}; };