Skip to content

Commit

Permalink
Merge pull request #897 from roflcoopter/feature/drawer-rerender
Browse files Browse the repository at this point in the history
fix so that opening drawer doesnt rerender page
  • Loading branch information
roflcoopter authored Jan 18, 2025
2 parents ae3d18a + 416d6fe commit c8fe5f8
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 136 deletions.
237 changes: 119 additions & 118 deletions frontend/src/components/header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,14 @@ import { Link as RouterLink, useNavigate } from "react-router-dom";
import ViseronLogo from "svg/viseron-logo.svg?react";

import Breadcrumbs from "components/header/Breadcrumbs";
import Drawer from "components/header/Drawer";
import { useAuthContext } from "context/AuthContext";
import { ColorModeContext } from "context/ColorModeContext";
import { ViseronContext } from "context/ViseronContext";
import { useScrollPosition } from "hooks/UseScrollPosition";
import { useToast } from "hooks/UseToast";
import { useAuthLogout } from "lib/api/auth";

interface AppHeaderProps {
setDrawerOpen: React.Dispatch<React.SetStateAction<boolean>>;
}

interface HeaderProps {
showHeader: boolean;
}
Expand Down Expand Up @@ -57,11 +54,12 @@ const Header = styled("header", {
],
}));

export default function AppHeader({ setDrawerOpen }: AppHeaderProps) {
export default function AppHeader() {
const colorMode = useContext(ColorModeContext);
const theme = useTheme();
const mediaQuerySmall = useMediaQuery(theme.breakpoints.up("sm"));
const [showHeader, setShowHeader] = useState(true);
const [drawerOpen, setDrawerOpen] = useState(false);
const lastTogglePos = useRef(0);
const { auth } = useAuthContext();
const { safeMode } = useContext(ViseronContext);
Expand Down Expand Up @@ -96,129 +94,132 @@ export default function AppHeader({ setDrawerOpen }: AppHeaderProps) {
const toast = useToast();

return (
<Header showHeader={showHeader}>
<Container
maxWidth={false}
sx={{
display: "flex",
alignItems: "center",
minHeight: theme.headerHeight,
}}
>
<Stack
direction="row"
spacing={1}
justifyContent="left"
alignItems="center"
sx={{ width: !mediaQuerySmall ? "12%" : undefined }}
>
<Tooltip title="Menu" enterDelay={300}>
<IconButton
color="primary"
onClick={() => {
setDrawerOpen(true);
}}
>
<MenuIcon fontSize="small" />
</IconButton>
</Tooltip>
<Tooltip title="Home" enterDelay={300}>
<Box
component={RouterLink}
to={"/"}
aria-label="Home"
sx={{ marginLeft: "16px" }}
>
<ViseronLogo
width={45}
height={45}
style={{ marginTop: "4px" }}
/>
</Box>
</Tooltip>
</Stack>
<Box
sx={
!mediaQuerySmall
? { width: "76%", pointerEvents: "none" }
: undefined
}
>
<Breadcrumbs />
</Box>
<Stack
direction="row"
spacing={1}
justifyContent="end"
sx={{ width: !mediaQuerySmall ? "12%" : { marginLeft: "auto" } }}
<>
<Drawer drawerOpen={drawerOpen} setDrawerOpen={setDrawerOpen} />
<Header showHeader={showHeader}>
<Container
maxWidth={false}
sx={{
display: "flex",
alignItems: "center",
minHeight: theme.headerHeight,
}}
>
<Tooltip
title={
theme.palette.mode === "dark"
? "In a Light mood today?"
: "Join the Dark Side"
<Stack
direction="row"
spacing={1}
justifyContent="left"
alignItems="center"
sx={{ width: !mediaQuerySmall ? "12%" : undefined }}
>
<Tooltip title="Menu" enterDelay={300}>
<IconButton
color="primary"
onClick={() => {
setDrawerOpen(true);
}}
>
<MenuIcon fontSize="small" />
</IconButton>
</Tooltip>
<Tooltip title="Home" enterDelay={300}>
<Box
component={RouterLink}
to={"/"}
aria-label="Home"
sx={{ marginLeft: "16px" }}
>
<ViseronLogo
width={45}
height={45}
style={{ marginTop: "4px" }}
/>
</Box>
</Tooltip>
</Stack>
<Box
sx={
!mediaQuerySmall
? { width: "76%", pointerEvents: "none" }
: undefined
}
enterDelay={300}
>
<IconButton color="primary" onClick={colorMode.toggleColorMode}>
{theme.palette.mode === "dark" ? (
<Brightness7Icon />
) : (
<Brightness4Icon />
)}
</IconButton>
</Tooltip>
<Tooltip title={"Edit Configuration"} enterDelay={300}>
<IconButton
component={RouterLink}
color="primary"
to={"/configuration"}
<Breadcrumbs />
</Box>
<Stack
direction="row"
spacing={1}
justifyContent="end"
sx={{ width: !mediaQuerySmall ? "12%" : { marginLeft: "auto" } }}
>
<Tooltip
title={
theme.palette.mode === "dark"
? "In a Light mood today?"
: "Join the Dark Side"
}
enterDelay={300}
>
<SettingsIcon />
</IconButton>
</Tooltip>
{auth.enabled && (
<Tooltip title={"Logout"} enterDelay={300}>
<IconButton color="primary" onClick={colorMode.toggleColorMode}>
{theme.palette.mode === "dark" ? (
<Brightness7Icon />
) : (
<Brightness4Icon />
)}
</IconButton>
</Tooltip>
<Tooltip title={"Edit Configuration"} enterDelay={300}>
<IconButton
component={RouterLink}
color="primary"
onClick={() =>
logout.mutate(undefined, {
onSuccess: async (_data, _variables, _context) => {
toast.success("Successfully logged out");
navigate("/login");
},
})
}
to={"/configuration"}
>
<LogoutIcon />
<SettingsIcon />
</IconButton>
</Tooltip>
)}
</Stack>
</Container>
{safeMode ? (
<Box
sx={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
gap: 2,
backgroundColor: theme.palette.error.main,
}}
>
<Typography
align="center"
style={{
textShadow: "rgba(0, 0, 0, 1) 0px 0px 4px",
margin: "5px",
{auth.enabled && (
<Tooltip title={"Logout"} enterDelay={300}>
<IconButton
color="primary"
onClick={() =>
logout.mutate(undefined, {
onSuccess: async (_data, _variables, _context) => {
toast.success("Successfully logged out");
navigate("/login");
},
})
}
>
<LogoutIcon />
</IconButton>
</Tooltip>
)}
</Stack>
</Container>
{safeMode ? (
<Box
sx={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
gap: 2,
backgroundColor: theme.palette.error.main,
}}
>
Viseron is running in safe mode. Cameras are not loaded and no
recordings are made. Please check the logs for more information.
</Typography>
</Box>
) : null}
</Header>
<Typography
align="center"
style={{
textShadow: "rgba(0, 0, 0, 1) 0px 0px 4px",
margin: "5px",
}}
>
Viseron is running in safe mode. Cameras are not loaded and no
recordings are made. Please check the logs for more information.
</Typography>
</Box>
) : null}
</Header>
</>
);
}
7 changes: 2 additions & 5 deletions frontend/src/layouts/PrivateLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ import Button from "@mui/material/Button";
import Container from "@mui/material/Container";
import { styled } from "@mui/material/styles";
import Cookies from "js-cookie";
import { Suspense, useRef, useState } from "react";
import { Suspense, useRef } from "react";
import { Link, Navigate, Outlet, useLocation } from "react-router-dom";
import { CSSTransition, SwitchTransition } from "react-transition-group";

import { ScrollToTopFab } from "components/ScrollToTop";
import { ErrorMessage } from "components/error/ErrorMessage";
import Footer from "components/footer/Footer";
import AppDrawer from "components/header/Drawer";
import Header from "components/header/Header";
import { Loading } from "components/loading/Loading";
import { useAuthContext } from "context/AuthContext";
Expand All @@ -24,7 +23,6 @@ const FullHeightContainer = styled("div")(() => ({

export default function PrivateLayout() {
const nodeRef = useRef(null);
const [drawerOpen, setDrawerOpen] = useState(false);
const location = useLocation();

const { auth } = useAuthContext();
Expand Down Expand Up @@ -99,8 +97,7 @@ export default function PrivateLayout() {
<ViseronProvider>
<FullHeightContainer>
<FullHeightContainer>
<AppDrawer drawerOpen={drawerOpen} setDrawerOpen={setDrawerOpen} />
<Header setDrawerOpen={setDrawerOpen} />
<Header />
<Suspense fallback={<Loading text="Loading" />}>
<SwitchTransition>
<CSSTransition
Expand Down
20 changes: 7 additions & 13 deletions frontend/tests/components/header/Header.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,23 @@ import AppHeader from "components/header/Header";

describe("Loading Component", () => {
test("renders app header with auth", () => {
const setDrawerOpen = vi.fn();

const { getByRole } = renderWithContext(
<AppHeader setDrawerOpen={setDrawerOpen} />
);
const { getByRole } = renderWithContext(<AppHeader />);
expect(
getByRole("button", {
name: "Logout",
})
}),
).toBeDefined();
});

test("renders app header without auth", () => {
const setDrawerOpen = vi.fn();

const { queryByRole } = renderWithContext(
<AppHeader setDrawerOpen={setDrawerOpen} />,
{ enabled: false, onboarding_complete: false }
);
const { queryByRole } = renderWithContext(<AppHeader />, {
enabled: false,
onboarding_complete: false,
});
expect(
queryByRole("button", {
name: "Logout",
})
}),
).toBeNull();
});
});

0 comments on commit c8fe5f8

Please sign in to comment.