1
0
mirror of https://github.com/stonith404/pingvin-share.git synced 2024-07-01 23:20:13 +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 nextConfig = withPWA({
reactStrictMode: true,
pwa: {
dest: "public",
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\""
},
"dependencies": {
"@mantine/core": "^4.2.0",
"@mantine/dropzone": "^4.2.0",
"@mantine/form": "^4.2.0",
"@mantine/hooks": "^4.2.0",
"@mantine/modals": "^4.2.0",
"@mantine/next": "^4.2.0",
"@mantine/notifications": "^4.2.0",
"@mantine/core": "^5.5.2",
"@mantine/dropzone": "^5.5.2",
"@mantine/form": "^5.5.2",
"@mantine/hooks": "^5.5.2",
"@mantine/modals": "^5.5.2",
"@mantine/next": "^5.5.2",
"@mantine/notifications": "^5.5.2",
"axios": "^0.26.1",
"cookies-next": "^2.0.4",
"file-saver": "^2.0.5",

View File

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

View File

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

View File

@ -1,23 +1,26 @@
import {
Burger,
Container,
createStyles,
Group,
Header as MantineHeader,
Header,
Image,
Paper,
Stack,
Text,
Transition,
} from "@mantine/core";
import { useBooleanToggle } from "@mantine/hooks";
import { useDisclosure } from "@mantine/hooks";
import { NextLink } from "@mantine/next";
import getConfig from "next/config";
import Image from "next/image";
import { ReactNode, useEffect, useState } from "react";
import useUser from "../../hooks/user.hook";
import headerStyle from "../../styles/header.style";
import ActionAvatar from "./ActionAvatar";
const { publicRuntimeConfig } = getConfig();
const HEADER_HEIGHT = 60;
type Link = {
link?: string;
label?: string;
@ -25,14 +28,90 @@ type Link = {
action?: () => Promise<void>;
};
const Header = () => {
const [opened, toggleOpened] = useBooleanToggle(false);
const [active, setActive] = useState<string>();
const useStyles = createStyles((theme) => ({
root: {
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 [opened, toggleOpened] = useDisclosure(false);
const { classes, cx } = headerStyle();
const authenticatedLinks: Link[] = [
const [authenticatedLinks, setAuthenticatedLinks] = useState<Link[]>([
{
link: "/upload",
label: "Upload",
@ -40,61 +119,66 @@ const Header = () => {
{
component: <ActionAvatar />,
},
];
]);
const unauthenticatedLinks: Link[] | undefined = [
const [unauthenticatedLinks, setUnauthenticatedLinks] = useState<Link[]>([
{
link: "/auth/signIn",
label: "Sign in",
},
];
]);
if (publicRuntimeConfig.SHOW_HOME_PAGE == "true")
unauthenticatedLinks.unshift({
link: "/",
label: "Home",
});
useEffect(() => {
if (publicRuntimeConfig.SHOW_HOME_PAGE == "true")
setUnauthenticatedLinks((array) => [
{
link: "/",
label: "Home",
},
...array,
]);
if (publicRuntimeConfig.ALLOW_REGISTRATION == "true")
unauthenticatedLinks.push({
link: "/auth/signUp",
label: "Sign up",
});
if (publicRuntimeConfig.ALLOW_REGISTRATION == "true")
setUnauthenticatedLinks((array) => [
...array,
{
link: "/auth/signUp",
label: "Sign up",
},
]);
}, []);
const links = user ? authenticatedLinks : unauthenticatedLinks;
const items = links.map((link, i) => {
if (link.component) {
return (
<Container key={i} pl={5} py={15}>
{link.component}
</Container>
);
}
if (link) {
// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(() => {
if (window.location.pathname == link.link) {
setActive(link.link);
const { classes, cx } = useStyles();
console.log(user);
const items = (
<>
{(user ? authenticatedLinks : unauthenticatedLinks).map((link) => {
if (link.component) {
return (
<>
<Container pl={5} py={15}>
{link.component}
</Container>
</>
);
}
}, []);
return (
<NextLink
key={link.label}
href={link.link ?? ""}
onClick={link.action}
className={cx(classes.link, {
[classes.linkActive]: link.link && active === link.link,
})}
>
{link.label}
</NextLink>
);
}
});
return (
<NextLink
key={link.label}
href={link.link ?? ""}
onClick={() => toggleOpened.toggle()}
className={cx(classes.link, {
[classes.linkActive]: window.location.pathname == link.link,
})}
>
{link.label}
</NextLink>
);
})}
</>
);
return (
<MantineHeader height={60} mb={20} className={classes.root}>
<Header height={HEADER_HEIGHT} mb={40} className={classes.root}>
<Container className={classes.header}>
<NextLink href="/">
<Group>
@ -108,24 +192,24 @@ const Header = () => {
</Group>
</NextLink>
<Group spacing={5} className={classes.links}>
{items}
<Group>{items} </Group>
</Group>
<Burger
opened={opened}
onClick={() => toggleOpened()}
onClick={() => toggleOpened.toggle()}
className={classes.burger}
size="sm"
/>
<Transition transition="pop-top-right" duration={200} mounted={opened}>
{(styles) => (
<Paper className={classes.dropdown} withBorder style={styles}>
{items}
<Stack spacing={0}> {items}</Stack>
</Paper>
)}
</Transition>
</Container>
</MantineHeader>
</Header>
);
};
export default Header;
export default NavBar;

View File

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

View File

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

View File

@ -1,5 +1,4 @@
import { ActionIcon, Loader, Skeleton, Table } from "@mantine/core";
import { useRouter } from "next/router";
import { CircleCheck, Download } from "tabler-icons-react";
import shareService from "../../services/share.service";
@ -14,8 +13,6 @@ const FileList = ({
shareId: string;
isLoading: boolean;
}) => {
const router = useRouter();
const skeletonRows = [...Array(5)].map((c, i) => (
<tr key={i}>
<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 { useState } from "react";
@ -27,7 +27,7 @@ const Body = ({ submitCallback }: { submitCallback: any }) => {
const [passwordWrong, setPasswordWrong] = useState(false);
return (
<>
<Group grow direction="column">
<Stack align="stretch">
<PasswordInput
variant="filled"
placeholder="Password"
@ -50,7 +50,7 @@ const Body = ({ submitCallback }: { submitCallback: any }) => {
>
Submit
</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 { ModalsContextProps } from "@mantine/modals/lib/context";
import { useRouter } from "next/router";
@ -23,7 +23,7 @@ const Body = ({ text }: { text: string }) => {
const router = useRouter();
return (
<>
<Group grow direction="column">
<Stack align="stretch">
<Text size="sm">{text}</Text>
<Button
onClick={() => {
@ -33,7 +33,7 @@ const Body = ({ text }: { text: string }) => {
>
Go back
</Button>
</Group>
</Stack>
</>
);
};

View File

@ -3,17 +3,17 @@ import {
Center,
createStyles,
Group,
MantineTheme,
Text,
useMantineTheme,
} from "@mantine/core";
import { Dropzone as MantineDropzone, DropzoneStatus } from "@mantine/dropzone";
import { Dropzone as MantineDropzone } from "@mantine/dropzone";
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 { byteStringToHumanSizeString } from "../../utils/math/byteStringToHumanSizeString.util";
import toast from "../../utils/toast.util";
const { publicRuntimeConfig } = getConfig()
const { publicRuntimeConfig } = getConfig();
const useStyles = createStyles((theme) => ({
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 = ({
isUploading,
setFiles,
@ -76,26 +68,20 @@ const Dropzone = ({
className={classes.dropzone}
radius="md"
>
{(status) => (
<div style={{ pointerEvents: "none" }}>
<Group position="center">
<CloudUpload size={50} color={getActiveColor(status, theme)} />
</Group>
<Text
align="center"
weight={700}
size="lg"
mt="xl"
sx={{ color: getActiveColor(status, theme) }}
>
{status.accepted ? "Drop files here" : "Upload files"}
</Text>
<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>
)}
<div style={{ pointerEvents: "none" }}>
<Group position="center">
<CloudUpload size={50} />
</Group>
<Text align="center" weight={700} size="lg" mt="xl">
Upload files
</Text>
<Text align="center" size="sm" mt="xs" color="dimmed">
Drag&apos;n&apos;drop files here to start your share. We can accept
only files that are less than{" "}
{byteStringToHumanSizeString(publicRuntimeConfig.MAX_FILE_SIZE)} in
size.
</Text>
</div>
</MantineDropzone>
<Center>
<Button

View File

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

View File

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