Skip to content

Commit

Permalink
perf(navigation): create NavigateProvider for improve navigate rerend…
Browse files Browse the repository at this point in the history
…er components
  • Loading branch information
Stéphane committed Oct 12, 2023
1 parent 2d24382 commit 150f305
Show file tree
Hide file tree
Showing 16 changed files with 206 additions and 144 deletions.
104 changes: 53 additions & 51 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import { I18nextProvider } from "react-i18next";
import { MobileNavigationContainer } from "../containers/MobileNavigation";
import { PlayerModeProvider } from "../providers/PlayerMode";
import { TrendingFiltersProvider } from "../providers/TrendingFilters";
import { StableNavigateProvider } from "../providers/Navigate";
import { Outlet } from "react-router-dom";

const useStyles = createStyles(() => ({
scrollArea: {
Expand All @@ -32,62 +34,62 @@ const useStyles = createStyles(() => ({
},
}));

interface AppProps {
children: React.ReactNode;
}

export const App: React.FC<AppProps> = ({ children }) => {
export const App = () => {
const { cx, classes } = useStyles();

return (
<I18nextProvider i18n={i18n}>
<QueryClientProvider client={queryClient}>
<SettingsProvider>
<SearchProvider>
<TrendingFiltersProvider>
<FavoriteProvider>
<PlaylistProvider>
<PlayerProvider>
<PreviousNextTrackProvider>
<PlayerPlaylistProvider>
<PlayerModeProvider>
<HistoryProvider>
<ColorSchemeProvider>
<MantineProvider>
<SpotlightProvider>
<Notifications />
<Flex>
<NavigationContainer />
<ScrollArea
className={cx(classes.scrollArea)}
style={{
position: "static",
}} // Stay in inline-styles for now
>
<Flex>
<Box style={{ flex: 1 }}>
<Header />
<Main>{children}</Main>
<MobileNavigationContainer />
</Box>
<DrawerPlayerContainer />
</Flex>
<PlayerContainer />
</ScrollArea>
</Flex>
</SpotlightProvider>
</MantineProvider>
</ColorSchemeProvider>
</HistoryProvider>
</PlayerModeProvider>
</PlayerPlaylistProvider>
</PreviousNextTrackProvider>
</PlayerProvider>
</PlaylistProvider>
</FavoriteProvider>
</TrendingFiltersProvider>
</SearchProvider>
</SettingsProvider>
<StableNavigateProvider>
<SettingsProvider>
<SearchProvider>
<TrendingFiltersProvider>
<FavoriteProvider>
<PlaylistProvider>
<PlayerProvider>
<PreviousNextTrackProvider>
<PlayerPlaylistProvider>
<PlayerModeProvider>
<HistoryProvider>
<ColorSchemeProvider>
<MantineProvider>
<SpotlightProvider>
<Notifications />
<Flex>
<NavigationContainer />
<ScrollArea
className={cx(classes.scrollArea)}
style={{
position: "static",
}} // Stay in inline-styles for now
>
<Flex>
<Box style={{ flex: 1 }}>
<Header />
<Main>
<Outlet />
</Main>
<MobileNavigationContainer />
</Box>
<DrawerPlayerContainer />
</Flex>
<PlayerContainer />
</ScrollArea>
</Flex>
</SpotlightProvider>
</MantineProvider>
</ColorSchemeProvider>
</HistoryProvider>
</PlayerModeProvider>
</PlayerPlaylistProvider>
</PreviousNextTrackProvider>
</PlayerProvider>
</PlaylistProvider>
</FavoriteProvider>
</TrendingFiltersProvider>
</SearchProvider>
</SettingsProvider>
</StableNavigateProvider>
<AppUpdate />
</QueryClientProvider>
</I18nextProvider>
Expand Down
4 changes: 2 additions & 2 deletions src/components/ButtonHistoryBack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { ActionIcon, Tooltip } from "@mantine/core";
import { IconArrowLeft } from "@tabler/icons-react";
import { memo } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { useStableNavigate } from "../providers/Navigate";

export const ButtonHistoryBack = memo(() => {
const navigate = useNavigate();
const navigate = useStableNavigate();
const { t } = useTranslation();

const handleClick = () => {
Expand Down
5 changes: 1 addition & 4 deletions src/components/CardList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Box, createStyles, rem } from "@mantine/core";
import { memo } from "react";
import { useLocation } from "react-router-dom";
import { Video } from "../types/interfaces/Video";
import { Card } from "./Card";
import { PlaylistCard } from "./PlaylistCard";
Expand Down Expand Up @@ -55,8 +54,6 @@ interface CardListProps {

export const CardList: React.FC<CardListProps> = memo(
({ data, scrollable = false }) => {
const location = useLocation();
const currentPath = location.pathname.replace("/", "");
const { classes } = useStyles();

if (!data.length) return null;
Expand All @@ -65,7 +62,7 @@ export const CardList: React.FC<CardListProps> = memo(
<Box className={scrollable ? classes.flexGrid : classes.grid}>
{data.map((item, index) => (
<Box
key={`${currentPath}${item.title}-${index}`}
key={`${document.location.pathname}${item.title}-${index}`}
className={scrollable ? classes.flexColumn : classes.column}
>
{(() => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/ChannelCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import {
} from "@mantine/core";
import { IconDiscountCheckFilled } from "@tabler/icons-react";
import { memo } from "react";
import { useNavigate } from "react-router-dom";
import { VideoThumbnail } from "../types/interfaces/Video";
import { Channel } from "../types/interfaces/Channel";
import { useTranslation } from "react-i18next";
import { CardImage } from "./CardImage";
import { ButtonFavorite } from "./ButtonFavorite";
import { useStableNavigate } from "../providers/Navigate";

const useStyles = createStyles((theme) => ({
card: {
Expand All @@ -32,7 +32,7 @@ interface ChannelCardProps {

export const ChannelCard: React.FC<ChannelCardProps> = memo(({ channel }) => {
const { classes } = useStyles();
const navigate = useNavigate();
const navigate = useStableNavigate();
const { t } = useTranslation();

const goToChannel = () => {
Expand Down
13 changes: 9 additions & 4 deletions src/components/Logo.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
import { ColorScheme } from "@mantine/core";
import { ColorScheme, UnstyledButton } from "@mantine/core";
import { useLocalStorage } from "@mantine/hooks";
import { memo } from "react";
import { Link } from "react-router-dom";
import { useStableNavigate } from "../providers/Navigate";

export const Logo = memo(() => {
const [colorScheme] = useLocalStorage<ColorScheme>({
key: "mantine-color-scheme",
});
const navigate = useStableNavigate();

const isDarkMode = colorScheme === "dark";
const imageSrc = isDarkMode
? "/logo-holoplay-white.png"
: "/logo-holoplay-blue.png";

return (
<Link to="/" style={{ marginTop: 8 }}>
<UnstyledButton
component="a"
onClick={() => navigate("/")}
style={{ marginTop: 8 }}
>
<img src={imageSrc} width={40} alt="Logo HoloPlay" />
</Link>
</UnstyledButton>
);
});
40 changes: 22 additions & 18 deletions src/components/MobileNavigation.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { createStyles, Divider, Space } from "@mantine/core";
import { memo } from "react";
import { Box, NavLink } from "@mantine/core";
import { Box } from "@mantine/core";
import {
IconHistory,
IconInfoCircle,
IconSettings,
IconTrendingUp,
IconUsers,
} from "@tabler/icons-react";
import { useNavigation } from "./Navigation";
import { useTranslation } from "react-i18next";
import { MobileNavbarLink } from "./NavbarLink";

const useStyles = createStyles((theme) => ({
container: {
Expand All @@ -27,39 +27,43 @@ interface MobileNavigationProps {
export const MobileNavigation: React.FC<MobileNavigationProps> = memo(
({ onClose }) => {
const { classes } = useStyles();
const { isActive } = useNavigation();
const { t } = useTranslation();

return (
<Box className={classes.container} role="navigation">
<Space h="xs" />
<NavLink
icon={<IconTrendingUp size={20} stroke={1.5} />}
<MobileNavbarLink
icon={IconTrendingUp}
label={t("navigation.trending")}
{...isActive("/trending", onClose)}
onClose={onClose}
activePath="/trending"
/>
<NavLink
icon={<IconUsers size={20} stroke={1.5} />}
<MobileNavbarLink
icon={IconUsers}
label={t("navigation.most-popular")}
{...isActive("/most-popular", onClose)}
onClose={onClose}
activePath="/most-popular"
/>
<NavLink
icon={<IconHistory size={20} stroke={1.5} />}
<MobileNavbarLink
icon={IconHistory}
label={t("navigation.history")}
{...isActive("/history", onClose)}
onClose={onClose}
activePath="/history"
/>
<Space h="xs" />
<Divider />
<Space h="xs" />
<NavLink
icon={<IconSettings size={20} stroke={1.5} />}
<MobileNavbarLink
icon={IconSettings}
label={t("navigation.settings")}
{...isActive("/settings", onClose)}
onClose={onClose}
activePath="/settings"
/>
<NavLink
icon={<IconInfoCircle size={20} stroke={1.5} />}
<MobileNavbarLink
icon={IconInfoCircle}
label={t("navigation.about")}
{...isActive("/about", onClose)}
onClose={onClose}
activePath="/about"
/>
<Space h="xs" />
</Box>
Expand Down
56 changes: 53 additions & 3 deletions src/components/NavbarLink.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
NavLink,
Tooltip,
UnstyledButton,
createStyles,
Expand All @@ -7,6 +8,8 @@ import {
} from "@mantine/core";
import { useMediaQuery } from "@mantine/hooks";
import { memo } from "react";
import { useStableNavigate } from "../providers/Navigate";
import { useLocation } from "react-router-dom";

const useStyles = createStyles((theme) => ({
link: {
Expand Down Expand Up @@ -44,16 +47,44 @@ const useStyles = createStyles((theme) => ({
},
}));

export type RoutePath =
| "/"
| "/search"
| "/favorites"
| "/trending"
| "/most-popular"
| "/playlists"
| "/history"
| "/about"
| "/settings";

interface NavbarLinkProps {
icon: any;
label: string;
active?: boolean;
onClick?(): void;
activePath?: RoutePath;
}

const useNavbarLink = (routeName: string, callback?: () => void) => {
const navigate = useStableNavigate();
const location = useLocation();

return {
onClick: () => {
navigate(routeName);
if (callback) {
callback();
}
},
active: location.pathname === routeName,
};
};

export const NavbarLink = memo(
({ icon: Icon, label, active, onClick }: NavbarLinkProps) => {
({ icon: Icon, label, onClick, activePath }: NavbarLinkProps) => {
const { classes, cx } = useStyles();
const link = useNavbarLink(activePath as RoutePath);
const theme = useMantineTheme();
const matches = useMediaQuery(
`screen and (max-width: ${theme.breakpoints.sm})`
Expand All @@ -62,12 +93,31 @@ export const NavbarLink = memo(
return (
<Tooltip label={label} position="right" disabled={matches}>
<UnstyledButton
onClick={onClick}
className={cx(classes.link, { [classes.active]: active })}
onClick={onClick ?? link.onClick}
className={cx(classes.link, { [classes.active]: link.active })}
>
<Icon stroke={1.5} />
</UnstyledButton>
</Tooltip>
);
}
);

interface MobileNavbarLinkProps extends NavbarLinkProps {
onClose: () => void;
}

export const MobileNavbarLink = memo(
({ icon: Icon, label, activePath, onClose }: MobileNavbarLinkProps) => {
const link = useNavbarLink(activePath as RoutePath, onClose);

return (
<NavLink
icon={<Icon />}
label={label}
onClick={link.onClick}
active={link.active}
/>
);
}
);
Loading

0 comments on commit 150f305

Please sign in to comment.