import React, { MouseEvent, useEffect, useState } from 'react' import { useRouter } from 'next/router' import { Trans, useTranslation } from 'next-i18next' import { AxiosResponse } from 'axios' import classNames from 'classnames' import api from '~utils/api' import { appState } from '~utils/appState' import * as GridSummonTransformer from '~transformers/GridSummonTransformer' 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 | null 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 } = useTranslation('common') const router = useRouter() const locale = router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : '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.quickSummon) } // 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 const mainSummon = gridSummons.find((summon) => GridSummonTransformer.toObject(summon) ) const friendSummon = gridSummons.find((summon) => GridSummonTransformer.toObject(summon) ) for (const gridSummon of gridSummons) { if (gridSummon.main) { appState.grid.summons.mainSummon = mainSummon } else if (gridSummon.friend) { appState.grid.summons.friendSummon = friendSummon } else { appState.party.grid.summons.allSummons[gridSummon.position] = gridSummon ? GridSummonTransformer.toObject(gridSummon) : null } } } 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.xlb && gridSummon.uncapLevel == 6) { if ( gridSummon.transcendenceStep && gridSummon.transcendenceStep >= 1 && gridSummon.transcendenceStep < 5 ) { suffix = '_03' } else if (gridSummon.transcendenceStep === 5) { suffix = '_04' } } else if ( upgradedSummons.indexOf(summon.granblueId) != -1 && gridSummon.uncapLevel == 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.granblueId}${suffix}.jpg` else imgSrc = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/summon-grid/${summon.granblueId}${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 ( <>