hensei-web/components/Party/index.tsx

267 lines
7.6 KiB
TypeScript

import React, { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { useSnapshot } from 'valtio'
import clonedeep from 'lodash.clonedeep'
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 { appState, initialAppState } from '~utils/appState'
import { GridType } from '~utils/enums'
import type { DetailsObject } from '~types'
import './index.scss'
// Props
interface Props {
new?: boolean
team?: Party
raids: Raid[][]
pushHistory?: (path: string) => void
}
const Party = (props: Props) => {
// Set up router
const router = useRouter()
// Set up states
const { party } = useSnapshot(appState)
const [currentTab, setCurrentTab] = useState<GridType>(GridType.Weapon)
// Reset state on first load
useEffect(() => {
const resetState = clonedeep(initialAppState)
appState.grid = resetState.grid
if (props.team) storeParty(props.team)
}, [])
// Methods: Creating a new party
async function createParty(details?: DetailsObject) {
let payload = {}
if (details) payload = formatDetailsObject(details)
return await api.endpoints.parties
.create(payload)
.then((response) => storeParty(response.data.party))
}
// Methods: Updating the party's details
async function updateDetails(details: DetailsObject) {
if (!appState.party.id) 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 (appState.party.id) {
return await api.endpoints.parties
.update(appState.party.id, payload)
.then((response) => storeParty(response.data.party))
}
}
function checkboxChanged(event: React.ChangeEvent<HTMLInputElement>) {
appState.party.extra = event.target.checked
// Only save if this is a saved party
if (appState.party.id) {
api.endpoints.parties.update(appState.party.id, {
party: { extra: event.target.checked },
})
}
}
// Deleting the party
function deleteTeam(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
if (appState.party.editable && appState.party.id) {
api.endpoints.parties
.destroy({ id: appState.party.id })
.then(() => {
// Push to route
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: Party) {
// 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.id = team.id
appState.party.extra = team.extra
appState.party.user = team.user
appState.party.favorited = team.favorited
appState.party.created_at = team.created_at
appState.party.updated_at = team.updated_at
appState.party.detailsVisible = false
// Populate state
storeCharacters(team.characters)
storeWeapons(team.weapons)
storeSummons(team.summons)
// Then, push the browser history to the new party's URL
if (props.pushHistory) props.pushHistory(`/p/${team.shortcode}`)
return team
}
const storeCharacters = (list: Array<GridCharacter>) => {
list.forEach((object: GridCharacter) => {
if (object.position != null)
appState.grid.characters[object.position] = object
})
}
const storeWeapons = (list: Array<GridWeapon>) => {
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<GridSummon>) => {
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<HTMLInputElement>) {
switch (event.target.value) {
case 'class':
setCurrentTab(GridType.Class)
break
case 'characters':
setCurrentTab(GridType.Character)
break
case 'weapons':
setCurrentTab(GridType.Weapon)
break
case 'summons':
setCurrentTab(GridType.Summon)
break
default:
break
}
}
// Render: JSX components
const navigation = (
<PartySegmentedControl
selectedTab={currentTab}
onClick={segmentClicked}
onCheckboxChange={checkboxChanged}
/>
)
const weaponGrid = (
<WeaponGrid
new={props.new || false}
weapons={props.team?.weapons}
createParty={createParty}
pushHistory={props.pushHistory}
/>
)
const summonGrid = (
<SummonGrid
new={props.new || false}
summons={props.team?.summons}
createParty={createParty}
pushHistory={props.pushHistory}
/>
)
const characterGrid = (
<CharacterGrid
new={props.new || false}
characters={props.team?.characters}
createParty={createParty}
pushHistory={props.pushHistory}
/>
)
const currentGrid = () => {
switch (currentTab) {
case GridType.Character:
return characterGrid
case GridType.Weapon:
return weaponGrid
case GridType.Summon:
return summonGrid
}
}
return (
<React.Fragment>
{navigation}
<section id="Party">{currentGrid()}</section>
<PartyDetails
party={props.team}
new={props.new || false}
editable={party.editable}
updateCallback={updateDetails}
deleteCallback={deleteTeam}
/>
</React.Fragment>
)
}
export default Party