import React, { useEffect, useState } from 'react' import { getCookie } from 'cookies-next' import { useRouter } from 'next/router' import { subscribe, useSnapshot } from 'valtio' import clonedeep from 'lodash.clonedeep' import ls from 'local-storage' import PartySegmentedControl from '~components/PartySegmentedControl' import PartyDetails from '~components/PartyDetails' import WeaponGrid from '~components/WeaponGrid' import SummonGrid from '~components/SummonGrid' import CharacterGrid from '~components/CharacterGrid' import api from '~utils/api' import { accountState } from '~utils/accountState' import { appState, initialAppState } from '~utils/appState' import { getLocalId } from '~utils/localId' import { GridType } from '~utils/enums' import { retrieveCookies } from '~utils/retrieveCookies' import { setEditKey, unsetEditKey } from '~utils/userToken' import type { DetailsObject } from '~types' import './index.scss' // Props interface Props { new?: boolean team?: Party raids: Raid[][] selectedTab: GridType pushHistory?: (path: string) => void } const defaultProps = { selectedTab: GridType.Weapon, } const Party = (props: Props) => { // Set up router const router = useRouter() // Set up states const { party } = useSnapshot(appState) const [editable, setEditable] = useState(false) const [currentTab, setCurrentTab] = useState(GridType.Weapon) const [refresh, setRefresh] = useState(false) // Retrieve cookies const cookies = retrieveCookies() // Reset state on first load useEffect(() => { const resetState = clonedeep(initialAppState) appState.grid = resetState.grid if (props.team) storeParty(props.team) }, []) // Subscribe to app state to listen for account changes and // unsubscribe when component is unmounted const unsubscribe = subscribe(accountState, () => { setRefresh(true) }) useEffect(() => () => unsubscribe(), []) // Set editable on first load useEffect(() => { // Get cookie const cookie = getCookie('account') const accountData: AccountCookie = cookie ? JSON.parse(cookie as string) : null let editable = false unsetEditKey() if (props.new) editable = true if (accountData && props.team && !props.new) { if (accountData.token) { // Authenticated if (props.team.user && accountData.userId === props.team.user.id) { editable = true } } else { // Not authenticated if (!props.team.user && accountData.userId === props.team.local_id) { // Set editable editable = true // Also set edit key header setEditKey(props.team.id, props.team.user) } } } appState.party.editable = editable setEditable(editable) }, [refresh]) // Set selected tab from props useEffect(() => { setCurrentTab(props.selectedTab) }, [props.selectedTab]) // Methods: Creating a new party async function createParty(details?: DetailsObject) { let payload = {} if (details) payload = formatDetailsObject(details) return await api.endpoints.parties .create({ ...payload, ...getLocalId() }) .then((response) => storeParty(response.data.party)) } // Methods: Updating the party's details async function updateDetails(details: DetailsObject) { if (!props.team) return await createParty(details) else updateParty(details) } function formatDetailsObject(details: DetailsObject) { const payload: { [key: string]: any } = {} if (details.name) payload.name = details.name if (details.description) payload.description = details.description if (details.raid) payload.raid_id = details.raid.id if (details.chargeAttack) payload.charge_attack = details.chargeAttack if (details.fullAuto) payload.full_auto = details.fullAuto if (details.autoGuard) payload.auto_guard = details.autoGuard if (details.clearTime) payload.clear_time = details.clearTime if (details.buttonCount) payload.button_count = details.buttonCount if (details.chainCount) payload.chain_count = details.chainCount if (details.turnCount) payload.turn_count = details.turnCount if (details.extra) payload.extra = details.extra if (details.job) payload.job_id = details.job.id if (Object.keys(payload).length > 1) return { party: payload } else return {} } async function updateParty(details: DetailsObject) { const payload = formatDetailsObject(details) if (props.team && props.team.id) { return await api.endpoints.parties .update(props.team.id, payload) .then((response) => storeParty(response.data.party)) } } function checkboxChanged(event: React.ChangeEvent) { appState.party.extra = event.target.checked // Only save if this is a saved party if (props.team && props.team.id) { api.endpoints.parties.update(props.team.id, { party: { extra: event.target.checked }, }) } } // Deleting the party function deleteTeam() { if (props.team && editable) { api.endpoints.parties .destroy({ id: props.team.id }) .then(() => { // Push to route if (cookies && cookies.account.username) { router.push(`/${cookies.account.username}`) } else { router.push('/') } // Clean state const resetState = clonedeep(initialAppState) Object.keys(resetState).forEach((key) => { appState[key] = resetState[key] }) // Set party to be editable appState.party.editable = true }) .catch((error) => { console.error(error) }) } } // Methods: Storing party data const storeParty = function (team: any) { // Store the important party and state-keeping values in global state appState.party.name = team.name appState.party.description = team.description appState.party.raid = team.raid appState.party.updated_at = team.updated_at appState.party.job = team.job appState.party.jobSkills = team.job_skills appState.party.accessory = team.accessory appState.party.id = team.id appState.party.shortcode = team.shortcode appState.party.extra = team.extra appState.party.user = team.user appState.party.favorited = team.favorited appState.party.remix = team.remix appState.party.remixes = team.remixes appState.party.sourceParty = team.source_party appState.party.created_at = team.created_at appState.party.updated_at = team.updated_at appState.party.detailsVisible = false // Store the edit key in local storage if (team.edit_key) { storeEditKey(team.id, team.edit_key) setEditKey(team.id, team.user) } // Populate state storeCharacters(team.characters) storeWeapons(team.weapons) storeSummons(team.summons) // Create a string to send the user back to the tab they're currently on let tab = '' if (currentTab === GridType.Character) { tab = 'characters' } else if (currentTab === GridType.Summon) { tab = 'summons' } // Then, push the browser history to the new party's URL if (props.pushHistory) { props.pushHistory(`/p/${team.shortcode}/${tab}`) } return team } const storeEditKey = (id: string, key: string) => { ls(id, key) } const storeCharacters = (list: Array) => { list.forEach((object: GridCharacter) => { if (object.position != null) appState.grid.characters[object.position] = object }) } const storeWeapons = (list: Array) => { list.forEach((gridObject: GridWeapon) => { if (gridObject.mainhand) { appState.grid.weapons.mainWeapon = gridObject appState.party.element = gridObject.object.element } else if (!gridObject.mainhand && gridObject.position != null) { appState.grid.weapons.allWeapons[gridObject.position] = gridObject } }) } const storeSummons = (list: Array) => { list.forEach((gridObject: GridSummon) => { if (gridObject.main) appState.grid.summons.mainSummon = gridObject else if (gridObject.friend) appState.grid.summons.friendSummon = gridObject else if ( !gridObject.main && !gridObject.friend && gridObject.position != null ) appState.grid.summons.allSummons[gridObject.position] = gridObject }) } // Methods: Navigating with segmented control function segmentClicked(event: React.ChangeEvent) { const path = [ router.asPath.split('/').filter((el) => el != '')[1], event.target.value, ].join('/') switch (event.target.value) { case 'characters': router.replace(path) setCurrentTab(GridType.Character) break case 'weapons': router.replace(path) setCurrentTab(GridType.Weapon) break case 'summons': router.replace(path) setCurrentTab(GridType.Summon) break default: break } } // Render: JSX components const navigation = ( ) const weaponGrid = ( ) const summonGrid = ( ) const characterGrid = ( ) const currentGrid = () => { switch (currentTab) { case GridType.Character: return characterGrid case GridType.Weapon: return weaponGrid case GridType.Summon: return summonGrid } } return ( {navigation}
{currentGrid()}
) } Party.defaultProps = defaultProps export default Party