mirror of https://github.com/MaxLeiter/Drift
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			227 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			227 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			TypeScript
		
	
'use client';
 | 
						|
 | 
						|
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} className={styles.tab}>
 | 
						|
                        <Button
 | 
						|
                            className={activeStyle}
 | 
						|
                            auto={isMobile ? false : true}
 | 
						|
                            icon={tab.icon}
 | 
						|
                            shadow={false}
 | 
						|
                        >
 | 
						|
                            {tab.name ? tab.name : undefined}
 | 
						|
                        </Button>
 | 
						|
 | 
						|
                    </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
 |