import React, { useEffect, useState } from 'react' import { subscribe, useSnapshot } from 'valtio' import { setCookie, deleteCookie } from 'cookies-next' import { useRouter } from 'next/router' import { Trans, useTranslation } from 'next-i18next' import classNames from 'classnames' import clonedeep from 'lodash.clonedeep' import Link from 'next/link' import api from '~utils/api' import { accountState, initialAccountState } from '~utils/accountState' import { appState, initialAppState } from '~utils/appState' import { getLocalId } from '~utils/localId' import { retrieveLocaleCookies } from '~utils/retrieveCookies' import { setEditKey, storeEditKey } from '~utils/userToken' import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuLabel, } from '~components/common/DropdownMenuContent' import LoginModal from '~components/auth/LoginModal' import SignupModal from '~components/auth/SignupModal' import AccountModal from '~components/auth/AccountModal' import Toast from '~components/common/Toast' import Button from '~components/common/Button' import Tooltip from '~components/common/Tooltip' import * as Switch from '@radix-ui/react-switch' import ArrowIcon from '~public/icons/Arrow.svg' import LinkIcon from '~public/icons/Link.svg' import MenuIcon from '~public/icons/Menu.svg' import RemixIcon from '~public/icons/Remix.svg' import PlusIcon from '~public/icons/Add.svg' import SaveIcon from '~public/icons/Save.svg' import './index.scss' const Header = () => { // Localization const { t } = useTranslation('common') // Router const router = useRouter() const locale = router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' const localeData = retrieveLocaleCookies() // State management const [copyToastOpen, setCopyToastOpen] = useState(false) const [remixToastOpen, setRemixToastOpen] = useState(false) const [loginModalOpen, setLoginModalOpen] = useState(false) const [signupModalOpen, setSignupModalOpen] = useState(false) const [settingsModalOpen, setSettingsModalOpen] = useState(false) const [leftMenuOpen, setLeftMenuOpen] = useState(false) const [rightMenuOpen, setRightMenuOpen] = useState(false) const [languageChecked, setLanguageChecked] = useState(false) const [name, setName] = useState('') const [originalName, setOriginalName] = useState('') // Snapshots const { account } = useSnapshot(accountState) const { party: partySnapshot } = useSnapshot(appState) // Subscribe to app state to listen for party name and // unsubscribe when component is unmounted const unsubscribe = subscribe(appState, () => { const newName = appState.party && appState.party.name ? appState.party.name : '' setName(newName) }) useEffect(() => () => unsubscribe(), []) // Hooks useEffect(() => { setLanguageChecked(localeData === 'ja' ? true : false) }, [localeData]) // Methods: Event handlers (Buttons) function handleLeftMenuButtonClicked() { setLeftMenuOpen(!leftMenuOpen) } function handleRightMenuButtonClicked() { setRightMenuOpen(!rightMenuOpen) } // Methods: Event handlers (Menus) function handleLeftMenuOpenChange(open: boolean) { setLeftMenuOpen(open) } function handleRightMenuOpenChange(open: boolean) { setRightMenuOpen(open) } function closeLeftMenu() { setLeftMenuOpen(false) } function closeRightMenu() { setRightMenuOpen(false) } // Methods: Event handlers (Copy toast) function handleCopyToastOpenChanged(open: boolean) { setCopyToastOpen(open) } function handleCopyToastCloseClicked() { setCopyToastOpen(false) } // Methods: Event handlers (Remix toasts) function handleRemixToastOpenChanged(open: boolean) { setRemixToastOpen(open) } function handleRemixToastCloseClicked() { setRemixToastOpen(false) } // Methods: Actions function handleNewTeam(event: React.MouseEvent) { event.preventDefault() newTeam() closeRightMenu() } function changeLanguage(value: boolean) { const language = value ? 'ja' : 'en' const expiresAt = new Date() expiresAt.setDate(expiresAt.getDate() + 120) setCookie('NEXT_LOCALE', language, { path: '/', expires: expiresAt }) router.push(router.asPath, undefined, { locale: language }) } function copyToClipboard() { const path = router.asPath.split('/')[1] if (path === 'p') { const el = document.createElement('input') el.value = window.location.href el.id = 'url-input' document.body.appendChild(el) el.select() document.execCommand('copy') el.remove() setCopyToastOpen(true) } } function logout() { // Close menu closeRightMenu() // Delete cookies deleteCookie('account') deleteCookie('user') // Clean state const resetState = clonedeep(initialAccountState) Object.keys(resetState).forEach((key) => { if (key !== 'language') accountState[key] = resetState[key] }) router.reload() return false } function newTeam() { // Clean state const resetState = clonedeep(initialAppState) Object.keys(resetState).forEach((key) => { appState[key] = resetState[key] }) // Push the root URL router.push('/new') } function remixTeam() { setOriginalName(partySnapshot.name ? partySnapshot.name : t('no_title')) if (partySnapshot.shortcode) { const body = getLocalId() api .remix({ shortcode: partySnapshot.shortcode, body: body }) .then((response) => { const remix = response.data.party // Store the edit key in local storage if (remix.edit_key) { storeEditKey(remix.id, remix.edit_key) setEditKey(remix.id, remix.user) } router.push(`/p/${remix.shortcode}`) setRemixToastOpen(true) }) } } function toggleFavorite() { if (partySnapshot.favorited) unsaveFavorite() else saveFavorite() } function saveFavorite() { if (partySnapshot.id) api.saveTeam({ id: partySnapshot.id }).then((response) => { if (response.status == 201) appState.party.favorited = true }) else console.error('Failed to save team: No party ID') } function unsaveFavorite() { if (partySnapshot.id) api.unsaveTeam({ id: partySnapshot.id }).then((response) => { if (response.status == 200) appState.party.favorited = false }) else console.error('Failed to unsave team: No party ID') } // Rendering: Elements const pageTitle = () => { let title = '' let hasAccessory = false const path = router.asPath.split('/')[1] if (path === 'p') { hasAccessory = true if (appState.party && appState.party.name) { title = appState.party.name } else { title = t('no_title') } } else { title = '' } return title !== '' ? (