'use client' import React, { useState } from 'react' import { Link } from '~/i18n/navigation' import { useRouter, usePathname } from '~/i18n/navigation' import { useSearchParams } from 'next/navigation' import { getCookie } from 'cookies-next' import { useSnapshot } from 'valtio' import { useTranslations } from 'next-intl' import classNames from 'classnames' import Button from '~components/common/Button' import Tooltip from '~components/common/Tooltip' import Token from '~components/common/Token' import EditPartyModal from '~components/party/EditPartyModal' import PartyDropdown from '~components/party/PartyDropdown' import api from '~utils/api' import { accountState } from '~utils/accountState' import { appState, initialAppState } from '~utils/appState' import { formatTimeAgo } from '~utils/timeAgo' import RemixTeamAlert from '~components/dialogs/RemixTeamAlert' import RemixedToast from '~components/toasts/RemixedToast' import PartyVisibilityDialog from '~components/party/PartyVisibilityDialog' import UrlCopiedToast from '~components/toasts/UrlCopiedToast' import EditIcon from '~public/icons/Edit.svg' import RemixIcon from '~public/icons/Remix.svg' import SaveIcon from '~public/icons/Save.svg' import PrivateIcon from '~public/icons/Private.svg' import UnlistedIcon from '~public/icons/Unlisted.svg' import type { DetailsObject } from '~types' import styles from './index.module.scss' // Props interface Props { party?: Party new: boolean editable: boolean raidGroups: RaidGroup[] deleteCallback: () => void remixCallback: () => void updateCallback: (details: DetailsObject) => Promise } const PartyHeader = (props: Props) => { const { party } = useSnapshot(appState) const t = useTranslations('common') const router = useRouter() const pathname = usePathname() const locale = getCookie('NEXT_LOCALE') || 'en' const { party: partySnapshot } = useSnapshot(appState) // State: Component const [detailsOpen, setDetailsOpen] = useState(false) const [copyToastOpen, setCopyToastOpen] = useState(false) const [remixAlertOpen, setRemixAlertOpen] = useState(false) const [remixToastOpen, setRemixToastOpen] = useState(false) const [visibilityDialogOpen, setVisibilityDialogOpen] = useState(false) const userClass = classNames({ [styles.user]: true, [styles.empty]: !party.user, }) const linkClass = classNames({ wind: party && party.element == 1, fire: party && party.element == 2, water: party && party.element == 3, earth: party && party.element == 4, dark: party && party.element == 5, light: party && party.element == 6, }) // Actions: Favorites function toggleFavorite() { if (appState.party.favorited) unsaveFavorite() else saveFavorite() } function saveFavorite() { if (appState.party.id) api.saveTeam({ id: appState.party.id }).then((response) => { if (response.status == 201) appState.party.favorited = true }) else console.error('Failed to save team: No party ID') } function unsaveFavorite() { if (appState.party.id) api.unsaveTeam({ id: appState.party.id }).then((response) => { if (response.status == 200) appState.party.favorited = false }) else console.error('Failed to unsave team: No party ID') } // Methods: Navigation function goTo(shortcode?: string) { if (shortcode) router.push(`/p/${shortcode}`) } const userImage = (picture?: string, element?: string) => { if (picture && element) return ( {picture} ) else return ( {t('no_user')} ) } // Actions: Edit info function handleDetailsOpenChange(open: boolean) { setDetailsOpen(open) } // Dialogs: Visibility function visibilityDialogCallback() { setVisibilityDialogOpen(true) } function handleVisibilityDialogChange(open: boolean) { setVisibilityDialogOpen(open) } // Actions: Remix team function remixTeamCallback() { setRemixToastOpen(true) props.remixCallback() } // Actions: Copy URL function copyToClipboard() { if (pathname.split('/')[1] === 'p') { navigator.clipboard.writeText(window.location.href) setCopyToastOpen(true) } } // Alerts: Remix team function openRemixTeamAlert() { setRemixAlertOpen(true) } function handleRemixTeamAlertChange(open: boolean) { setRemixAlertOpen(open) } // Toasts: Remix team function handleRemixToastOpenChanged(open: boolean) { setRemixToastOpen(!open) } function handleRemixToastCloseClicked() { setRemixToastOpen(false) } // Toasts / Copy URL function handleCopyToastOpenChanged(open: boolean) { setCopyToastOpen(!open) } function handleCopyToastCloseClicked() { setCopyToastOpen(false) } // Rendering const userBlock = (username?: string, picture?: string, element?: string) => { return (
{userImage(picture, element)} {username ? username : t('no_user')}
) } const renderUserBlock = () => { let username, picture, element if (accountState.account.authorized && props.new) { username = accountState.account.user?.username picture = accountState.account.user?.avatar.picture element = accountState.account.user?.avatar.element } else if (party.user && !props.new) { username = party.user.username picture = party.user.avatar.picture element = party.user.avatar.element } if (username && picture && element) { return linkedUserBlock(username, picture, element) } else if (!props.new) { return userBlock() } } const linkedUserBlock = ( username?: string, picture?: string, element?: string ) => { return (
{userBlock(username, picture, element)}
) } const linkedRaidBlock = (raid: Raid) => { return (
{raid.name[locale]}
) } // Render: Tokens const chargeAttackToken = ( {`${t('party.details.labels.charge_attack')} ${ party.chargeAttack ? 'On' : 'Off' }`} ) const fullAutoToken = ( {`${t('party.details.labels.full_auto')} ${ party.fullAuto ? 'On' : 'Off' }`} ) const autoGuardToken = ( {`${t('party.details.labels.auto_guard')} ${ party.autoGuard ? 'On' : 'Off' }`} ) const autoSummonToken = ( {`${t('party.details.labels.auto_summon')} ${ party.autoSummon ? 'On' : 'Off' }`} ) const turnCountToken = ( {t('party.details.turns.with_count', { count: party.turnCount || 0, })} ) const buttonChainToken = () => { if (party.buttonCount !== undefined || party.chainCount !== undefined) { let string = '' if (party.buttonCount !== undefined) { string += `${party.buttonCount}b` } if (party.buttonCount === undefined && party.chainCount !== undefined) { string += `0${t('party.details.suffix.buttons')}${party.chainCount}${t( 'party.details.suffix.chains' )}` } else if ( party.buttonCount !== undefined && party.chainCount !== undefined ) { string += `${party.chainCount}${t('party.details.suffix.chains')}` } else if ( party.buttonCount !== undefined && party.chainCount === undefined ) { string += `0${t('party.details.suffix.chains')}` } return {string} } } const clearTimeToken = () => { const minutes = Math.floor(party.clearTime / 60) const seconds = party.clearTime - minutes * 60 let string = '' if (minutes > 0) string = `${minutes}${t('party.details.suffix.minutes')} ${seconds}${t( 'party.details.suffix.seconds' )}` else string = `${seconds}${t('party.details.suffix.seconds')}` return {string} } function renderTokens() { return ( <> {' '} {chargeAttackToken} {fullAutoToken} {autoSummonToken} {autoGuardToken} {party.turnCount !== undefined && turnCountToken} {party.clearTime > 0 && clearTimeToken()} {buttonChainToken()} ) } // Render: Notice const unlistedNotice = (

{t('party.notices.unlisted')}

) const privateNotice = (

{t('party.notices.private')}

{party.editable && (
)}
) // Render: Buttons const saveButton = () => { return (