'use client' import React, { MouseEvent, useEffect, useState } from 'react' import { useRouter } from 'next/navigation' import { useTranslations } from 'next-intl' import { AxiosResponse } from 'axios' import classNames from 'classnames' import { getCookie } from 'cookies-next' import api from '~utils/api' import { appState } from '~utils/appState' import Alert from '~components/common/Alert' import Button from '~components/common/Button' import { ContextMenu, ContextMenuTrigger, ContextMenuContent, } from '~components/common/ContextMenu' import ContextMenuItem from '~components/common/ContextMenuItem' import SearchModal from '~components/search/SearchModal' import SummonHovercard from '~components/summon/SummonHovercard' import UncapIndicator from '~components/uncap/UncapIndicator' import type { SearchableObject } from '~types' import PlusIcon from '~public/icons/Add.svg' import SettingsIcon from '~public/icons/Settings.svg' import styles from './index.module.scss' interface Props { gridSummon: GridSummon | undefined unitType: 0 | 1 | 2 position: number editable: boolean removeSummon: (id: string) => void updateObject: (object: SearchableObject, position: number) => void updateUncap: (id: string, position: number, uncap: number) => void updateTranscendence: (id: string, position: number, stage: number) => void } const SummonUnit = ({ gridSummon, unitType, position, editable, removeSummon: sendSummonToRemove, updateObject, updateUncap, updateTranscendence, }: Props) => { // Translations and locale const t = useTranslations('common') const router = useRouter() const locale = getCookie('NEXT_LOCALE') && ['en', 'ja'].includes(getCookie('NEXT_LOCALE') as string) ? (getCookie('NEXT_LOCALE') as string) : 'en' // State: UI const [searchModalOpen, setSearchModalOpen] = useState(false) const [contextMenuOpen, setContextMenuOpen] = useState(false) const [alertOpen, setAlertOpen] = useState(false) // State: Other const [imageUrl, setImageUrl] = useState('') // Classes const classes = classNames({ unit: true, [styles.unit]: true, [styles.main]: unitType == 0, [styles.grid]: unitType == 1, [styles.friend]: unitType == 2, [styles.subaura]: position == 4 || position == 5, [styles.editable]: editable, [styles.filled]: gridSummon !== undefined, }) // Other const summon = gridSummon?.object // Hooks useEffect(() => { generateImageUrl() }) // Methods: Open layer function openSearchModal() { if (editable) setSearchModalOpen(true) } function openRemoveSummonAlert() { setAlertOpen(true) } // Methods: Handle button clicked function handleButtonClicked() { setContextMenuOpen(!contextMenuOpen) } function handleQuickSummonClick() { if (gridSummon) updateQuickSummon(!gridSummon.quick_summon) } // Methods: Handle open change function handleContextMenuOpenChange(open: boolean) { if (!open) setContextMenuOpen(false) } function handleSearchModalOpenChange(open: boolean) { setSearchModalOpen(open) } // Methods: Mutate data // Send the GridSummonObject to the server async function updateQuickSummon(value: boolean) { if (gridSummon) return await api .updateQuickSummon({ id: gridSummon.id, value: value }) .then((response) => processResult(response)) .catch((error) => processError(error)) } // Save the server's response to state function processResult(response: AxiosResponse) { // TODO: We will have to update multiple grid summons at once // because there can only be one at once. // If a user sets a quick summon while one is already set, // the previous one will be unset. const gridSummons: GridSummon[] = response.data.summons for (const gridSummon of gridSummons) { if (gridSummon.main) { appState.grid.summons.mainSummon = gridSummon } else if (gridSummon.friend) { appState.grid.summons.friendSummon = gridSummon } else { appState.grid.summons.allSummons[gridSummon.position] = gridSummon } } } function processError(error: any) { console.error(error) } function passUncapData(uncap: number) { if (gridSummon) updateUncap(gridSummon.id, position, uncap) } function passTranscendenceData(stage: number) { if (gridSummon) updateTranscendence(gridSummon.id, position, stage) } function removeSummon() { if (gridSummon) sendSummonToRemove(gridSummon.id) setAlertOpen(false) } // Methods: Image string generation function generateImageUrl() { let imgSrc = '' if (gridSummon) { const summon = gridSummon.object! const upgradedSummons = [ '2040094000', '2040100000', '2040080000', '2040098000', '2040090000', '2040084000', '2040003000', '2040056000', '2040020000', '2040034000', '2040028000', '2040027000', '2040046000', '2040047000', ] let suffix = '' if ( gridSummon.object.uncap.transcendence && gridSummon.uncap_level == 6 ) { if ( gridSummon.transcendence_step >= 1 && gridSummon.transcendence_step < 5 ) { suffix = '_03' } else if (gridSummon.transcendence_step === 5) { suffix = '_04' } } else if ( upgradedSummons.indexOf(summon.granblue_id.toString()) != -1 && gridSummon.uncap_level == 5 ) { suffix = '_02' } // Generate the correct source for the summon if (unitType == 0 || unitType == 2) imgSrc = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/summon-main/${summon.granblue_id}${suffix}.jpg` else imgSrc = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/summon-grid/${summon.granblue_id}${suffix}.jpg` } setImageUrl(imgSrc) } function placeholderImageUrl() { return unitType == 0 || unitType == 2 ? '/images/placeholders/placeholder-summon-main.png' : '/images/placeholders/placeholder-summon-grid.png' } // Methods: Layer element rendering const contextMenu = () => { if (editable && gridSummon && gridSummon.id) { return ( <>