import { ButtonGroup, Button, Page, Spacer, useBodyScroll, useMediaQuery } from "@geist-ui/core" import { useCallback, useEffect, useMemo, useState } from "react" import styles from "./header.module.css" import useSignedIn from "../../lib/hooks/use-signed-in" import HomeIcon from "@geist-ui/icons/home" import MenuIcon from "@geist-ui/icons/menu" import GitHubIcon from "@geist-ui/icons/github" import SignOutIcon from "@geist-ui/icons/userX" import SignInIcon from "@geist-ui/icons/user" import SignUpIcon from "@geist-ui/icons/userPlus" import NewIcon from "@geist-ui/icons/plusCircle" import YourIcon from "@geist-ui/icons/list" import MoonIcon from "@geist-ui/icons/moon" import SettingsIcon from "@geist-ui/icons/settings" import SunIcon from "@geist-ui/icons/sun" import { useTheme } from "next-themes" import useUserData from "@lib/hooks/use-user-data" import Link from "next/link" import { useRouter } from "next/router" type Tab = { name: string icon: JSX.Element value: string onClick?: () => void href?: string } const Header = () => { const router = useRouter() const [expanded, setExpanded] = useState<boolean>(false) const [, setBodyHidden] = useBodyScroll(null, { scrollLayer: true }) const isMobile = useMediaQuery("xs", { match: "down" }) const { signedIn: isSignedIn } = useSignedIn() const userData = useUserData() const [pages, setPages] = useState<Tab[]>([]) const { setTheme, resolvedTheme } = useTheme() useEffect(() => { setBodyHidden(expanded) }, [expanded, setBodyHidden]) useEffect(() => { if (!isMobile) { setExpanded(false) } }, [isMobile]) useEffect(() => { const defaultPages: Tab[] = [ { name: isMobile ? "GitHub" : "", href: "https://github.com/maxleiter/drift", icon: <GitHubIcon />, value: "github" }, { name: isMobile ? "Change theme" : "", onClick: function () { if (typeof window !== "undefined") setTheme(resolvedTheme === "light" ? "dark" : "light") }, icon: resolvedTheme === "light" ? <MoonIcon /> : <SunIcon />, value: "theme" } ] if (isSignedIn) setPages([ { name: "new", icon: <NewIcon />, value: "new", href: "/new" }, { name: "yours", icon: <YourIcon />, value: "yours", href: "/mine" }, { name: "settings", icon: <SettingsIcon />, value: "settings", href: "/settings" }, { name: "sign out", icon: <SignOutIcon />, value: "signout", href: "/signout" }, ...defaultPages ]) else setPages([ { name: "home", icon: <HomeIcon />, value: "home", href: "/" }, { name: "Sign in", icon: <SignInIcon />, value: "signin", href: "/signin" }, { name: "Sign up", icon: <SignUpIcon />, value: "signup", href: "/signup" }, ...defaultPages ]) if (userData?.role === "admin") { setPages((pages) => [ ...pages, { name: "admin", icon: <SettingsIcon />, value: "admin", href: "/admin" } ]) } // TODO: investigate deps causing infinite loop // eslint-disable-next-line react-hooks/exhaustive-deps }, [isMobile, isSignedIn, resolvedTheme, userData]) const onTabChange = useCallback( (tab: string) => { if (typeof window === "undefined") return const match = pages.find((page) => page.value === tab) if (match?.onClick) { match.onClick() } }, [pages] ) const getButton = useCallback( (tab: Tab) => { const activeStyle = router.pathname === tab.href ? styles.active : "" if (tab.onClick) { return ( <Button auto={isMobile ? false : true} key={tab.value} icon={tab.icon} onClick={() => onTabChange(tab.value)} className={`${styles.tab} ${activeStyle}`} shadow={false} > {tab.name ? tab.name : undefined} </Button> ) } else if (tab.href) { return ( <Link key={tab.value} href={tab.href}> <a className={styles.tab}> <Button className={activeStyle} auto={isMobile ? false : true} icon={tab.icon} shadow={false} > {tab.name ? tab.name : undefined} </Button> </a> </Link> ) } }, [isMobile, onTabChange, router.pathname] ) const buttons = useMemo(() => pages.map(getButton), [pages, getButton]) return ( <Page.Header> <div className={styles.tabs}> <div className={styles.buttons}>{buttons}</div> </div> <div className={styles.controls}> <Button effect={false} auto type="abort" onClick={() => setExpanded(!expanded)} aria-label="Menu" > <Spacer height={5 / 6} width={0} /> <MenuIcon /> </Button> </div> {/* setExpanded should occur elsewhere; we don't want to close if they change themes */} {isMobile && expanded && ( <div className={styles.mobile} onClick={() => setExpanded(!expanded)}> <ButtonGroup vertical style={{ background: "var(--bg)" }} > {buttons} </ButtonGroup> </div> )} </Page.Header> ) } export default Header