update to mantine 7

This commit is contained in:
Athou
2023-12-29 23:09:30 +01:00
parent 2742b7fff6
commit 7e02380858
52 changed files with 653 additions and 751 deletions

View File

@@ -50,11 +50,11 @@ export function ErrorPage(props: { error: Error }) {
<Title className={classes.title}>
<Trans>Something bad just happened...</Trans>
</Title>
<Text size="lg" align="center" className={classes.description}>
<Text size="lg" ta="center" className={classes.description}>
{props.error.message}
</Text>
<Group position="center">
<Button size="md" onClick={() => window.location.reload()} leftIcon={<TbRefresh size={18} />}>
<Group justify="center">
<Button size="md" onClick={() => window.location.reload()} leftSection={<TbRefresh size={18} />}>
Refresh the page
</Button>
</Group>

View File

@@ -14,7 +14,7 @@ export function LoadingPage() {
<RingProgress
sections={[{ value: loadingPercentage, color: theme.primaryColor }]}
label={
<Text weight="bold" align="center" size="xl">
<Text fw="bold" ta="center" size="xl">
{loadingPercentage}%
</Text>
}

View File

@@ -1,5 +1,5 @@
import { Trans } from "@lingui/macro"
import { Anchor, Box, Center, Container, Divider, Group, Image, Title, useMantineColorScheme } from "@mantine/core"
import { Anchor, Box, Center, Container, Divider, Group, Image, Space, Title, useMantineColorScheme } from "@mantine/core"
import { client } from "app/client"
import { redirectToApiDocumentation, redirectToLogin, redirectToRegistration, redirectToRootCategory } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
@@ -31,7 +31,7 @@ export function WelcomePage() {
<Container>
<Header />
<Center my="xl">
<Center my="lg">
<Title order={3}>Bloat-free feed reader</Title>
</Center>
@@ -47,13 +47,15 @@ export function WelcomePage() {
</Center>
)}
<Divider my="xl" />
<Divider my="lg" />
<Image src={image} />
<Divider my="xl" />
<Divider my="lg" />
<Footer />
<Space h="lg" />
</Container>
)
}
@@ -73,9 +75,13 @@ function Header() {
}
return (
<Group position="apart">
<PageTitle />
<Buttons />
<Group justify="space-between">
<Box>
<PageTitle />
</Box>
<Box>
<Buttons />
</Box>
</Group>
)
}
@@ -88,7 +94,7 @@ function Buttons() {
const dark = colorScheme === "dark"
return (
<Group spacing={14}>
<Group gap={14}>
<ActionButton
label={<Trans>Log in</Trans>}
icon={<TbKey size={iconSize} />}
@@ -128,7 +134,7 @@ function Buttons() {
function Footer() {
const dispatch = useAppDispatch()
return (
<Group position="apart">
<Group justify="space-between">
<Group>
<span>© CommaFeed</span>
<Anchor variant="text" href="https://github.com/Athou/commafeed/" target="_blank" rel="noreferrer">

View File

@@ -66,7 +66,7 @@ export function AdminUsersPage() {
<Title order={3} mb="md">
<Group>
<Trans>Manage users</Trans>
<ActionIcon color={theme.primaryColor} onClick={() => openUserEditModal(<Trans>Add user</Trans>)}>
<ActionIcon color={theme.primaryColor} variant="subtle" onClick={() => openUserEditModal(<Trans>Add user</Trans>)}>
<TbPlus size={20} />
</ActionIcon>
</Group>
@@ -79,69 +79,74 @@ export function AdminUsersPage() {
)}
<Table striped highlightOnHover>
<thead>
<tr>
<th>
<Table.Thead>
<Table.Tr>
<Table.Th>
<Trans>Id</Trans>
</th>
<th>
</Table.Th>
<Table.Th>
<Trans>Name</Trans>
</th>
<th>
</Table.Th>
<Table.Th>
<Trans>E-mail</Trans>
</th>
<th>
</Table.Th>
<Table.Th>
<Trans>Date created</Trans>
</th>
<th>
</Table.Th>
<Table.Th>
<Trans>Last login date</Trans>
</th>
<th>
</Table.Th>
<Table.Th>
<Trans>Admin</Trans>
</th>
<th>
</Table.Th>
<Table.Th>
<Trans>Enabled</Trans>
</th>
<th>
</Table.Th>
<Table.Th>
<Trans>Actions</Trans>
</th>
</tr>
</thead>
<tbody>
</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{users?.map(u => (
<tr key={u.id}>
<td>{u.id}</td>
<td>{u.name}</td>
<td>{u.email}</td>
<td>
<Table.Tr key={u.id}>
<Table.Td>{u.id}</Table.Td>
<Table.Td>{u.name}</Table.Td>
<Table.Td>{u.email}</Table.Td>
<Table.Td>
<RelativeDate date={u.created} />
</td>
<td>
</Table.Td>
<Table.Td>
<RelativeDate date={u.lastLogin} />
</td>
<td>
</Table.Td>
<Table.Td>
<BooleanIcon value={u.admin} />
</td>
<td>
</Table.Td>
<Table.Td>
<BooleanIcon value={u.enabled} />
</td>
<td>
</Table.Td>
<Table.Td>
<Group>
<ActionIcon color={theme.primaryColor} onClick={() => openUserEditModal(<Trans>Edit user</Trans>, u)}>
<ActionIcon
color={theme.primaryColor}
variant="subtle"
onClick={() => openUserEditModal(<Trans>Edit user</Trans>, u)}
>
<TbPencil size={18} />
</ActionIcon>
<ActionIcon
color={theme.primaryColor}
variant="subtle"
onClick={() => openUserDeleteModal(u)}
loading={deleteUser.loading}
>
<TbTrash size={18} />
</ActionIcon>
</Group>
</td>
</tr>
</Table.Td>
</Table.Tr>
))}
</tbody>
</Table.Tbody>
</Table>
</Container>
)

View File

@@ -33,10 +33,10 @@ export function MetricsPage() {
return (
<Tabs defaultValue="stats">
<Tabs.List>
<Tabs.Tab value="stats" icon={<TbChartAreaLine size={14} />}>
<Tabs.Tab value="stats" leftSection={<TbChartAreaLine size={14} />}>
Stats
</Tabs.Tab>
<Tabs.Tab value="timers" icon={<TbClock size={14} />}>
<Tabs.Tab value="timers" leftSection={<TbClock size={14} />}>
Timers
</Tabs.Tab>
</Tabs.List>

View File

@@ -67,7 +67,7 @@ export function AboutPage() {
return (
<Container size="xl">
<SimpleGrid cols={2} breakpoints={[{ maxWidth: Constants.layout.mobileBreakpoint, cols: 1 }]}>
<SimpleGrid cols={{ base: 1, [Constants.layout.mobileBreakpointName]: 2 }}>
<Section title={<Trans>About</Trans>} icon={<TbHelp size={24} />}>
<Box>
<Trans>

View File

@@ -10,13 +10,13 @@ export function AddPage() {
<Container size="sm" px={0}>
<Tabs defaultValue="subscribe">
<Tabs.List>
<Tabs.Tab value="subscribe" icon={<TbRss size={16} />}>
<Tabs.Tab value="subscribe" leftSection={<TbRss size={16} />}>
<Trans>Subscribe</Trans>
</Tabs.Tab>
<Tabs.Tab value="category" icon={<TbFolderPlus size={16} />}>
<Tabs.Tab value="category" leftSection={<TbFolderPlus size={16} />}>
<Trans>Add category</Trans>
</Tabs.Tab>
<Tabs.Tab value="opml" icon={<TbFileImport size={16} />}>
<Tabs.Tab value="opml" leftSection={<TbFileImport size={16} />}>
<Trans>OPML</Trans>
</Tabs.Tab>
</Tabs.List>

View File

@@ -127,13 +127,13 @@ export function CategoryDetailsPage() {
</Button>
{editable && (
<>
<Button type="submit" leftIcon={<TbDeviceFloppy size={16} />} loading={modifyCategory.loading}>
<Button type="submit" leftSection={<TbDeviceFloppy size={16} />} loading={modifyCategory.loading}>
<Trans>Save</Trans>
</Button>
<Divider orientation="vertical" />
<Button
color="red"
leftIcon={<TbTrash size={16} />}
leftSection={<TbTrash size={16} />}
onClick={() => openDeleteCategoryModal()}
loading={deleteCategory.loading}
>

View File

@@ -166,13 +166,13 @@ export function FeedDetailsPage() {
<Button variant="default" onClick={async () => await dispatch(redirectToSelectedSource())}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" leftIcon={<TbDeviceFloppy size={16} />} loading={modifyFeed.loading}>
<Button type="submit" leftSection={<TbDeviceFloppy size={16} />} loading={modifyFeed.loading}>
<Trans>Save</Trans>
</Button>
<Divider orientation="vertical" />
<Button
color="red"
leftIcon={<TbTrash size={16} />}
leftSection={<TbTrash size={16} />}
onClick={() => openUnsubscribeModal()}
loading={unsubscribe.loading}
>

View File

@@ -73,7 +73,7 @@ export function FeedEntriesPage(props: FeedEntriesPageProps) {
return (
// add some room at the bottom of the page in order to be able to scroll the current entry at the top of the page when expanding
<Box mb={viewport.height - Constants.layout.headerHeight - 210}>
<Group spacing="xl">
<Group gap="xl">
{sourceWebsiteUrl && (
<a href={sourceWebsiteUrl} target="_blank" rel="noreferrer" className={classes.sourceWebsiteLink}>
<Title order={3}>{sourceLabel}</Title>

View File

@@ -1,88 +1,32 @@
import {
ActionIcon,
AppShell,
Box,
Burger,
Center,
DEFAULT_THEME,
Group,
Header,
type MantineTheme,
Navbar,
ScrollArea,
Title,
useMantineTheme,
} from "@mantine/core"
import { Trans } from "@lingui/macro"
import { ActionIcon, AppShell, Box, Center, Group, ScrollArea, Title, useMantineTheme } from "@mantine/core"
import { Constants } from "app/constants"
import { redirectToAdd, redirectToRootCategory } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import { setMobileMenuOpen, setSidebarWidth } from "app/tree/slice"
import { reloadTree } from "app/tree/thunks"
import { reloadProfile, reloadSettings, reloadTags } from "app/user/thunks"
import { ActionButton } from "components/ActionButton"
import { AnnouncementDialog } from "components/AnnouncementDialog"
import { Loader } from "components/Loader"
import { Logo } from "components/Logo"
import { OnDesktop } from "components/responsive/OnDesktop"
import { OnMobile } from "components/responsive/OnMobile"
import { useAppLoading } from "hooks/useAppLoading"
import { useMobile } from "hooks/useMobile"
import { useWebSocket } from "hooks/useWebSocket"
import { LoadingPage } from "pages/LoadingPage"
import { Resizable } from "re-resizable"
import { type ReactNode, Suspense, useEffect } from "react"
import { TbPlus } from "react-icons/tb"
import Draggable from "react-draggable"
import { TbMenu2, TbPlus, TbX } from "react-icons/tb"
import { Outlet } from "react-router-dom"
import { tss } from "tss"
interface LayoutProps {
sidebar: ReactNode
sidebarWidth: number
sidebarVisible: boolean
header: ReactNode
}
const sidebarPadding = DEFAULT_THEME.spacing.xs
const sidebarRightBorderWidth = "1px"
const useStyles = tss
.withParams<{
theme: MantineTheme
sidebarWidth: number
}>()
.create(({ theme, sidebarWidth }) => ({
sidebar: {
"& .mantine-ScrollArea-scrollbar[data-orientation='horizontal']": {
display: "none",
},
},
sidebarContentResizeWrapper: {
padding: sidebarPadding,
minHeight: `calc(100vh - ${Constants.layout.headerHeight}px)`,
},
sidebarContent: {
maxWidth: `calc(${sidebarWidth}px - ${sidebarPadding} * 2 - ${sidebarRightBorderWidth})`,
[theme.fn.smallerThan(Constants.layout.mobileBreakpoint)]: {
maxWidth: `calc(100vw - ${sidebarPadding} * 2 - ${sidebarRightBorderWidth})`,
},
},
mainContentWrapper: {
paddingTop: Constants.layout.headerHeight,
paddingLeft: sidebarWidth,
paddingRight: 0,
paddingBottom: 0,
[theme.fn.smallerThan(Constants.layout.mobileBreakpoint)]: {
paddingLeft: 0,
},
},
mainContent: {
maxWidth: `calc(100vw - ${sidebarWidth}px)`,
padding: theme.spacing.md,
[theme.fn.smallerThan(Constants.layout.mobileBreakpoint)]: {
maxWidth: "100vw",
padding: "6px",
},
},
}))
function LogoAndTitle() {
const dispatch = useAppDispatch()
return (
@@ -97,21 +41,13 @@ function LogoAndTitle() {
export default function Layout(props: LayoutProps) {
const theme = useMantineTheme()
const { classes } = useStyles({
theme,
sidebarWidth: props.sidebarWidth,
})
const { loading } = useAppLoading()
const mobile = useMobile()
const mobileMenuOpen = useAppSelector(state => state.tree.mobileMenuOpen)
const webSocketConnected = useAppSelector(state => state.server.webSocketConnected)
const treeReloadInterval = useAppSelector(state => state.server.serverInfos?.treeReloadInterval)
const sidebarHidden = props.sidebarWidth === 0
const dispatch = useAppDispatch()
useWebSocket()
const handleResize = (element: HTMLElement) => dispatch(setSidebarWidth(element.offsetWidth))
useEffect(() => {
// load initial data
dispatch(reloadSettings())
@@ -132,18 +68,20 @@ export default function Layout(props: LayoutProps) {
}, [dispatch, webSocketConnected, treeReloadInterval])
const burger = (
<Center>
<Burger
color={theme.fn.variant({ color: theme.primaryColor, variant: "subtle" }).color}
opened={mobileMenuOpen}
onClick={() => dispatch(setMobileMenuOpen(!mobileMenuOpen))}
size="sm"
/>
</Center>
<ActionButton
label={mobileMenuOpen ? <Trans>Open menu</Trans> : <Trans>Close menu</Trans>}
icon={mobileMenuOpen ? <TbX size={18} /> : <TbMenu2 size={18} />}
onClick={() => dispatch(setMobileMenuOpen(!mobileMenuOpen))}
></ActionButton>
)
const addButton = (
<ActionIcon color={theme.primaryColor} onClick={async () => await dispatch(redirectToAdd())} aria-label="Subscribe">
<ActionIcon
color={theme.primaryColor}
variant="subtle"
onClick={async () => await dispatch(redirectToAdd())}
aria-label="Subscribe"
>
<TbPlus size={18} />
</ActionIcon>
)
@@ -151,77 +89,80 @@ export default function Layout(props: LayoutProps) {
if (loading) return <LoadingPage />
return (
<AppShell
fixed
navbarOffsetBreakpoint={Constants.layout.mobileBreakpoint}
classNames={{ main: classes.mainContentWrapper }}
navbar={
<Navbar
id="sidebar"
hiddenBreakpoint={sidebarHidden ? 99999999 : Constants.layout.mobileBreakpoint}
hidden={sidebarHidden || !mobileMenuOpen}
width={{ md: props.sidebarWidth }}
className={classes.sidebar}
>
<Navbar.Section grow component={ScrollArea} mx={mobile ? 0 : "-sm"} px={mobile ? 0 : "sm"}>
<Resizable
enable={{
top: false,
right: !mobile,
bottom: false,
left: false,
topRight: false,
bottomRight: false,
bottomLeft: false,
topLeft: false,
}}
onResize={(e, dir, el) => handleResize(el)}
minWidth={120}
className={classes.sidebarContentResizeWrapper}
>
<Box className={classes.sidebarContent}>{props.sidebar}</Box>
</Resizable>
</Navbar.Section>
</Navbar>
}
header={
<Header id="header" height={Constants.layout.headerHeight} p="md">
<OnMobile>
{mobileMenuOpen && (
<Group position="apart">
<Box>{burger}</Box>
<Box>
<LogoAndTitle />
</Box>
<Box>{addButton}</Box>
</Group>
)}
{!mobileMenuOpen && (
<Group>
<Box>{burger}</Box>
<Box sx={{ flexGrow: 1 }}>{props.header}</Box>
</Group>
)}
</OnMobile>
<OnDesktop>
<Group>
<Group position="apart" sx={{ width: props.sidebarWidth - 16 }}>
<Box>
<LogoAndTitle />
</Box>
<Box>{addButton}</Box>
</Group>
<Box sx={{ flexGrow: 1 }}>{props.header}</Box>
</Group>
</OnDesktop>
</Header>
}
header={{ height: Constants.layout.headerHeight }}
navbar={{
width: props.sidebarWidth,
breakpoint: Constants.layout.mobileBreakpoint,
collapsed: { mobile: !mobileMenuOpen, desktop: !props.sidebarVisible },
}}
padding={{ base: 6, [Constants.layout.mobileBreakpointName]: "md" }}
>
<Box id="content" className={classes.mainContent}>
<AppShell.Header id="header">
<OnMobile>
{mobileMenuOpen && (
<Group justify="space-between" p="md">
<Box>{burger}</Box>
<Box>
<LogoAndTitle />
</Box>
<Box>{addButton}</Box>
</Group>
)}
{!mobileMenuOpen && (
<Group p="md">
<Box>{burger}</Box>
<Box style={{ flexGrow: 1 }}>{props.header}</Box>
</Group>
)}
</OnMobile>
<OnDesktop>
<Group p="md">
<Group justify="space-between" style={{ width: props.sidebarWidth - 16 }}>
<Box>
<LogoAndTitle />
</Box>
<Box>{addButton}</Box>
</Group>
<Box style={{ flexGrow: 1 }}>{props.header}</Box>
</Group>
</OnDesktop>
</AppShell.Header>
<AppShell.Navbar id="sidebar" p="xs">
<AppShell.Section grow component={ScrollArea} mx="-sm" px="sm">
<Box>{props.sidebar}</Box>
</AppShell.Section>
</AppShell.Navbar>
<Draggable
axis="x"
defaultPosition={{
x: props.sidebarWidth,
y: Constants.layout.headerHeight,
}}
bounds={{
left: 120,
right: 1000,
}}
grid={[30, 30]}
onDrag={(_e, data) => {
dispatch(setSidebarWidth(data.x))
}}
>
<Box
style={{
position: "fixed",
height: "100%",
width: "10px",
cursor: "ew-resize",
}}
></Box>
</Draggable>
<AppShell.Main id="content">
<Suspense fallback={<Loader />}>
<AnnouncementDialog />
<Outlet />
</Suspense>
</Box>
</AppShell.Main>
</AppShell>
)
}

View File

@@ -10,13 +10,13 @@ export function SettingsPage() {
<Container size="sm" px={0}>
<Tabs defaultValue="display" keepMounted={false}>
<Tabs.List>
<Tabs.Tab value="display" icon={<TbPhoto size={16} />}>
<Tabs.Tab value="display" leftSection={<TbPhoto size={16} />}>
<Trans>Display</Trans>
</Tabs.Tab>
<Tabs.Tab value="customCode" icon={<TbCode size={16} />}>
<Tabs.Tab value="customCode" leftSection={<TbCode size={16} />}>
<Trans>Custom code</Trans>
</Tabs.Tab>
<Tabs.Tab value="profile" icon={<TbUser size={16} />}>
<Tabs.Tab value="profile" leftSection={<TbUser size={16} />}>
<Trans>Profile</Trans>
</Tabs.Tab>
</Tabs.List>

View File

@@ -60,7 +60,7 @@ export function LoginPage() {
/>
{serverInfos?.smtpEnabled && (
<Anchor component={Link} to="/passwordRecovery" color="dimmed">
<Anchor component={Link} to="/passwordRecovery" c="dimmed">
<Trans>Forgot password?</Trans>
</Anchor>
)}