Refactor object grids to handle business logic instead of Party
This commit is contained in:
parent
44966fe8fe
commit
827473ee5a
16 changed files with 726 additions and 477 deletions
|
|
@ -1,11 +1,18 @@
|
||||||
import React, { useState } from 'react'
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
|
import { useCookies } from 'react-cookie'
|
||||||
import { useModal as useModal } from '~utils/useModal'
|
import { useModal as useModal } from '~utils/useModal'
|
||||||
|
|
||||||
|
import { AxiosResponse } from 'axios'
|
||||||
|
import debounce from 'lodash.debounce'
|
||||||
|
|
||||||
import CharacterUnit from '~components/CharacterUnit'
|
import CharacterUnit from '~components/CharacterUnit'
|
||||||
import SearchModal from '~components/SearchModal'
|
import SearchModal from '~components/SearchModal'
|
||||||
|
|
||||||
|
import api from '~utils/api'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
|
// GridType
|
||||||
export enum GridType {
|
export enum GridType {
|
||||||
Class,
|
Class,
|
||||||
Character,
|
Character,
|
||||||
|
|
@ -13,67 +20,185 @@ export enum GridType {
|
||||||
Summon
|
Summon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Props
|
||||||
interface Props {
|
interface Props {
|
||||||
userId?: string
|
partyId?: string
|
||||||
grid: GridArray<Character>
|
characters: GridArray<GridCharacter>
|
||||||
editable: boolean
|
editable: boolean
|
||||||
exists: boolean
|
createParty: () => Promise<AxiosResponse<any, any>>
|
||||||
onSelect: (type: GridType, character: Character, position: number) => void
|
pushHistory?: (path: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const CharacterGrid = (props: Props) => {
|
const CharacterGrid = (props: Props) => {
|
||||||
const { open, openModal, closeModal } = useModal()
|
// Constants
|
||||||
const [searchPosition, setSearchPosition] = useState(0)
|
|
||||||
|
|
||||||
const numCharacters: number = 5
|
const numCharacters: number = 5
|
||||||
|
|
||||||
function isCharacter(object: Character | Weapon | Summon): object is Character {
|
// Cookies
|
||||||
// There aren't really any unique fields here
|
const [cookies, _] = useCookies(['user'])
|
||||||
return (object as Character).gender !== undefined
|
const headers = (cookies.user != null) ? {
|
||||||
}
|
headers: {
|
||||||
|
'Authorization': `Bearer ${cookies.user.access_token}`
|
||||||
|
}
|
||||||
|
} : {}
|
||||||
|
|
||||||
|
// Set up states for Grid data
|
||||||
|
const [characters, setCharacters] = useState<GridArray<GridCharacter>>({})
|
||||||
|
|
||||||
|
// Set up states for Search
|
||||||
|
const { open, openModal, closeModal } = useModal()
|
||||||
|
const [itemPositionForSearch, setItemPositionForSearch] = useState(0)
|
||||||
|
|
||||||
|
// Create a temporary state to store previous character uncap values
|
||||||
|
const [previousUncapValues, setPreviousUncapValues] = useState<{[key: number]: number}>({})
|
||||||
|
|
||||||
|
// Create a state dictionary to store pure objects for Search
|
||||||
|
const [searchGrid, setSearchGrid] = useState<GridArray<Character>>({})
|
||||||
|
|
||||||
|
// Set states from props
|
||||||
|
useEffect(() => {
|
||||||
|
setCharacters(props.characters || {})
|
||||||
|
}, [props])
|
||||||
|
|
||||||
|
// Update search grid whenever characters are updated
|
||||||
|
useEffect(() => {
|
||||||
|
let newSearchGrid = Object.values(characters).map((o) => o.character)
|
||||||
|
setSearchGrid(newSearchGrid)
|
||||||
|
}, [characters])
|
||||||
|
|
||||||
|
// Methods: Adding an object from search
|
||||||
function openSearchModal(position: number) {
|
function openSearchModal(position: number) {
|
||||||
setSearchPosition(position)
|
setItemPositionForSearch(position)
|
||||||
openModal()
|
openModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
function receiveCharacter(character: Character, position: number) {
|
function receiveCharacterFromSearch(object: Character | Weapon | Summon, position: number) {
|
||||||
props.onSelect(GridType.Character, character, position)
|
const character = object as Character
|
||||||
}
|
|
||||||
|
|
||||||
function sendData(object: Character | Weapon | Summon, position: number) {
|
if (!props.partyId) {
|
||||||
if (isCharacter(object)) {
|
props.createParty()
|
||||||
receiveCharacter(object, position)
|
.then(response => {
|
||||||
|
const party = response.data.party
|
||||||
|
if (props.pushHistory) props.pushHistory(`/p/${party.shortcode}`)
|
||||||
|
saveCharacter(party.id, character, position)
|
||||||
|
.then(response => storeGridCharacter(response.data.grid_character))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
saveCharacter(props.partyId, character, position)
|
||||||
|
.then(response => storeGridCharacter(response.data.grid_character))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function saveCharacter(partyId: string, character: Character, position: number) {
|
||||||
|
return await api.endpoints.characters.create({
|
||||||
|
'character': {
|
||||||
|
'party_id': partyId,
|
||||||
|
'character_id': character.id,
|
||||||
|
'position': position,
|
||||||
|
'mainhand': (position == -1),
|
||||||
|
'uncap_level': characterUncapLevel(character)
|
||||||
|
}
|
||||||
|
}, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
function storeGridCharacter(gridCharacter: GridCharacter) {
|
||||||
|
// Store the grid unit at the correct position
|
||||||
|
let newCharacters = Object.assign({}, characters)
|
||||||
|
newCharacters[gridCharacter.position] = gridCharacter
|
||||||
|
setCharacters(newCharacters)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods: Helpers
|
||||||
|
function characterUncapLevel(character: Character) {
|
||||||
|
let uncapLevel
|
||||||
|
|
||||||
|
if (character.special) {
|
||||||
|
uncapLevel = 3
|
||||||
|
if (character.uncap.ulb) uncapLevel = 5
|
||||||
|
else if (character.uncap.flb) uncapLevel = 4
|
||||||
|
} else {
|
||||||
|
uncapLevel = 4
|
||||||
|
if (character.uncap.ulb) uncapLevel = 6
|
||||||
|
else if (character.uncap.flb) uncapLevel = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
return uncapLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods: Updating uncap level
|
||||||
|
// Note: Saves, but debouncing is not working properly
|
||||||
|
async function saveUncap(id: string, position: number, uncapLevel: number) {
|
||||||
|
try {
|
||||||
|
await api.updateUncap('weapon', id, uncapLevel)
|
||||||
|
.then(response => {
|
||||||
|
storeGridCharacter(response.data.grid_character)
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
|
||||||
|
// Revert optimistic UI
|
||||||
|
updateUncapLevel(position, previousUncapValues[position])
|
||||||
|
|
||||||
|
// Remove optimistic key
|
||||||
|
let newPreviousValues = {...previousUncapValues}
|
||||||
|
delete newPreviousValues[position]
|
||||||
|
setPreviousUncapValues(newPreviousValues)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const initiateUncapUpdate = useCallback(
|
||||||
|
(id: string, position: number, uncapLevel: number) => {
|
||||||
|
debouncedAction(id, position, uncapLevel)
|
||||||
|
|
||||||
|
// Save the current value in case of an unexpected result
|
||||||
|
let newPreviousValues = {...previousUncapValues}
|
||||||
|
newPreviousValues[position] = characters[position].uncap_level
|
||||||
|
setPreviousUncapValues(newPreviousValues)
|
||||||
|
|
||||||
|
// Optimistically update UI
|
||||||
|
updateUncapLevel(position, uncapLevel)
|
||||||
|
}, [previousUncapValues, characters]
|
||||||
|
)
|
||||||
|
|
||||||
|
const debouncedAction = useMemo(() =>
|
||||||
|
debounce((id, position, number) => {
|
||||||
|
saveUncap(id, position, number)
|
||||||
|
}, 1000), [saveUncap]
|
||||||
|
)
|
||||||
|
|
||||||
|
const updateUncapLevel = (position: number, uncapLevel: number) => {
|
||||||
|
let newCharacters = Object.assign({}, characters)
|
||||||
|
newCharacters[position].uncap_level = uncapLevel
|
||||||
|
setCharacters(newCharacters)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render: JSX components
|
||||||
return (
|
return (
|
||||||
<div className="CharacterGrid">
|
<div className="CharacterGrid">
|
||||||
<ul id="grid_characters">
|
<ul id="grid_characters">
|
||||||
{
|
{Array.from(Array(numCharacters)).map((x, i) => {
|
||||||
Array.from(Array(numCharacters)).map((x, i) => {
|
return (
|
||||||
return (
|
<li key={`grid_unit_${i}`} >
|
||||||
<li key={`grid_unit_${i}`} >
|
<CharacterUnit
|
||||||
<CharacterUnit
|
gridCharacter={props.characters[i]}
|
||||||
onClick={() => { openSearchModal(i) }}
|
editable={props.editable}
|
||||||
editable={props.editable}
|
position={i}
|
||||||
position={i}
|
onClick={() => { openSearchModal(i) }}
|
||||||
character={props.grid[i]}
|
updateUncap={initiateUncapUpdate}
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
})
|
})}
|
||||||
}
|
|
||||||
{open ? (
|
{open ? (
|
||||||
<SearchModal
|
<SearchModal
|
||||||
grid={props.grid}
|
grid={searchGrid}
|
||||||
close={closeModal}
|
close={closeModal}
|
||||||
send={sendData}
|
send={receiveCharacterFromSearch}
|
||||||
fromPosition={searchPosition}
|
fromPosition={itemPositionForSearch}
|
||||||
object="characters"
|
object="characters"
|
||||||
placeholderText="Search for a character..."
|
placeholderText="Search for a character..."
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,45 +2,53 @@ import React, { useEffect, useState } from 'react'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
|
|
||||||
import UncapIndicator from '~components/UncapIndicator'
|
import UncapIndicator from '~components/UncapIndicator'
|
||||||
|
|
||||||
import PlusIcon from '~public/icons/plus.svg'
|
import PlusIcon from '~public/icons/plus.svg'
|
||||||
|
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onClick: () => void
|
gridCharacter: GridCharacter | undefined
|
||||||
character: Character | undefined
|
|
||||||
position: number
|
position: number
|
||||||
editable: boolean
|
editable: boolean
|
||||||
|
onClick: () => void
|
||||||
|
updateUncap: (id: string, position: number, uncap: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const CharacterUnit = (props: Props) => {
|
const CharacterUnit = (props: Props) => {
|
||||||
|
console.log(props.gridCharacter?.character.name.en, props.gridCharacter?.uncap_level)
|
||||||
|
|
||||||
const [imageUrl, setImageUrl] = useState('')
|
const [imageUrl, setImageUrl] = useState('')
|
||||||
|
|
||||||
const classes = classnames({
|
const classes = classnames({
|
||||||
CharacterUnit: true,
|
CharacterUnit: true,
|
||||||
'editable': props.editable,
|
'editable': props.editable,
|
||||||
'filled': (props.character !== undefined)
|
'filled': (props.gridCharacter !== undefined)
|
||||||
})
|
})
|
||||||
|
|
||||||
const character = props.character
|
const gridCharacter = props.gridCharacter
|
||||||
|
const character = gridCharacter?.character
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
generateImageUrl()
|
generateImageUrl()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
function generateImageUrl() {
|
function generateImageUrl() {
|
||||||
let imgSrc = ""
|
let imgSrc = ""
|
||||||
|
|
||||||
if (props.character) {
|
if (props.gridCharacter) {
|
||||||
const character = props.character!
|
const character = props.gridCharacter.character!
|
||||||
imgSrc = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/chara-main/${character.granblue_id}_01.jpg`
|
imgSrc = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/chara-main/${character.granblue_id}_01.jpg`
|
||||||
}
|
}
|
||||||
|
|
||||||
setImageUrl(imgSrc)
|
setImageUrl(imgSrc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function passUncapData(uncap: number) {
|
||||||
|
console.log(`passuncapdata ${uncap}`)
|
||||||
|
if (props.gridCharacter)
|
||||||
|
props.updateUncap(props.gridCharacter.id, props.position, uncap)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className={classes}>
|
<div className={classes}>
|
||||||
|
|
@ -48,11 +56,15 @@ const CharacterUnit = (props: Props) => {
|
||||||
<img alt={character?.name.en} className="grid_image" src={imageUrl} />
|
<img alt={character?.name.en} className="grid_image" src={imageUrl} />
|
||||||
{ (props.editable) ? <span className='icon'><PlusIcon /></span> : '' }
|
{ (props.editable) ? <span className='icon'><PlusIcon /></span> : '' }
|
||||||
</div>
|
</div>
|
||||||
<UncapIndicator
|
{ (gridCharacter && character) ?
|
||||||
type="character"
|
<UncapIndicator
|
||||||
flb={character?.uncap.flb || false}
|
type="character"
|
||||||
uncapLevel={(character?.rarity == 2) ? 3 : 4}
|
flb={character.uncap.flb || false}
|
||||||
/>
|
ulb={character.uncap.ulb || false}
|
||||||
|
uncapLevel={gridCharacter.uncap_level}
|
||||||
|
updateUncap={passUncapData}
|
||||||
|
special={character.special}
|
||||||
|
/> : '' }
|
||||||
<h3 className="CharacterName">{character?.name.en}</h3>
|
<h3 className="CharacterName">{character?.name.en}</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ interface Props {
|
||||||
found?: boolean
|
found?: boolean
|
||||||
offset: number
|
offset: number
|
||||||
onClick: (position: number) => void
|
onClick: (position: number) => void
|
||||||
updateUncap: (id: string, uncap: number) => void
|
updateUncap: (id: string, position: number, uncap: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ExtraSummons = (props: Props) => {
|
const ExtraSummons = (props: Props) => {
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,10 @@ export enum GridType {
|
||||||
interface Props {
|
interface Props {
|
||||||
grid: GridArray<GridWeapon>
|
grid: GridArray<GridWeapon>
|
||||||
editable: boolean
|
editable: boolean
|
||||||
exists: boolean
|
|
||||||
found?: boolean
|
found?: boolean
|
||||||
offset: number
|
offset: number
|
||||||
onClick: (position: number) => void
|
onClick: (position: number) => void
|
||||||
updateUncap: (id: string, uncap: number) => void
|
updateUncap: (id: string, position: number, uncap: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ExtraWeapons = (props: Props) => {
|
const ExtraWeapons = (props: Props) => {
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
import React, { ChangeEvent, useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useCookies } from 'react-cookie'
|
import { useCookies } from 'react-cookie'
|
||||||
import api from '~utils/api'
|
|
||||||
|
|
||||||
// UI Elements
|
|
||||||
import PartySegmentedControl from '~components/PartySegmentedControl'
|
import PartySegmentedControl from '~components/PartySegmentedControl'
|
||||||
|
|
||||||
// Grids
|
|
||||||
import WeaponGrid from '~components/WeaponGrid'
|
import WeaponGrid from '~components/WeaponGrid'
|
||||||
import SummonGrid from '~components/SummonGrid'
|
import SummonGrid from '~components/SummonGrid'
|
||||||
import CharacterGrid from '~components/CharacterGrid'
|
import CharacterGrid from '~components/CharacterGrid'
|
||||||
|
|
||||||
|
import api from '~utils/api'
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
// GridType
|
// GridType
|
||||||
enum GridType {
|
enum GridType {
|
||||||
Class,
|
Class,
|
||||||
|
|
@ -17,96 +16,58 @@ enum GridType {
|
||||||
Weapon,
|
Weapon,
|
||||||
Summon
|
Summon
|
||||||
}
|
}
|
||||||
export { GridType }
|
|
||||||
|
|
||||||
import './index.scss'
|
|
||||||
|
|
||||||
|
// Props
|
||||||
interface Props {
|
interface Props {
|
||||||
partyId?: string
|
partyId?: string
|
||||||
mainWeapon?: GridWeapon
|
mainWeapon?: GridWeapon
|
||||||
mainSummon?: GridSummon
|
mainSummon?: GridSummon
|
||||||
friendSummon?: GridSummon
|
friendSummon?: GridSummon
|
||||||
characters?: GridArray<Character>
|
characters?: GridArray<GridCharacter>
|
||||||
weapons?: GridArray<GridWeapon>
|
weapons?: GridArray<GridWeapon>
|
||||||
summons?: GridArray<GridSummon>
|
summons?: GridArray<GridSummon>
|
||||||
extra: boolean
|
extra: boolean
|
||||||
editable: boolean
|
editable: boolean
|
||||||
exists: boolean
|
|
||||||
pushHistory?: (path: string) => void
|
pushHistory?: (path: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const Party = (props: Props) => {
|
const Party = (props: Props) => {
|
||||||
|
// Cookies
|
||||||
const [cookies, _] = useCookies(['user'])
|
const [cookies, _] = useCookies(['user'])
|
||||||
|
|
||||||
const headers = (cookies.user != null) ? {
|
const headers = (cookies.user != null) ? {
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${cookies.user.access_token}`
|
'Authorization': `Bearer ${cookies.user.access_token}`
|
||||||
}
|
}
|
||||||
} : {}
|
} : {}
|
||||||
|
|
||||||
// Grid data
|
// Set up states
|
||||||
const [characters, setCharacters] = useState<GridArray<Character>>({})
|
|
||||||
const [weapons, setWeapons] = useState<GridArray<GridWeapon>>({})
|
|
||||||
const [summons, setSummons] = useState<GridArray<GridSummon>>({})
|
|
||||||
|
|
||||||
const [mainWeapon, setMainWeapon] = useState<GridWeapon>()
|
|
||||||
const [mainSummon, setMainSummon] = useState<GridSummon>()
|
|
||||||
const [friendSummon, setFriendSummon] = useState<GridSummon>()
|
|
||||||
|
|
||||||
const [extra, setExtra] = useState<boolean>(false)
|
const [extra, setExtra] = useState<boolean>(false)
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setPartyId(props.partyId || '')
|
|
||||||
setMainWeapon(props.mainWeapon)
|
|
||||||
setMainSummon(props.mainSummon)
|
|
||||||
setFriendSummon(props.friendSummon)
|
|
||||||
setCharacters(props.characters || {})
|
|
||||||
setWeapons(props.weapons || {})
|
|
||||||
setSummons(props.summons || {})
|
|
||||||
setExtra(props.extra || false)
|
|
||||||
}, [props.partyId, props.mainWeapon, props.mainSummon, props.friendSummon, props.characters, props.weapons, props.summons, props.extra])
|
|
||||||
|
|
||||||
const weaponGrid = (
|
|
||||||
<WeaponGrid
|
|
||||||
userId={cookies.user ? cookies.user.userId : ''}
|
|
||||||
mainhand={mainWeapon}
|
|
||||||
grid={weapons}
|
|
||||||
editable={props.editable}
|
|
||||||
exists={props.exists}
|
|
||||||
extra={extra}
|
|
||||||
onSelect={itemSelected}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
const summonGrid = (
|
|
||||||
<SummonGrid
|
|
||||||
userId={cookies.user ? cookies.user.userId : ''}
|
|
||||||
main={mainSummon}
|
|
||||||
friend={friendSummon}
|
|
||||||
grid={summons}
|
|
||||||
editable={props.editable}
|
|
||||||
exists={props.exists}
|
|
||||||
onSelect={itemSelected}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
const characterGrid = (
|
|
||||||
<CharacterGrid
|
|
||||||
userId={cookies.user ? cookies.user.userId : ''}
|
|
||||||
grid={characters}
|
|
||||||
editable={props.editable}
|
|
||||||
exists={props.exists}
|
|
||||||
onSelect={itemSelected}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
const [currentTab, setCurrentTab] = useState<GridType>(GridType.Weapon)
|
const [currentTab, setCurrentTab] = useState<GridType>(GridType.Weapon)
|
||||||
const [partyId, setPartyId] = useState('')
|
|
||||||
|
|
||||||
|
// Set states from props
|
||||||
|
useEffect(() => {
|
||||||
|
setExtra(props.extra || false)
|
||||||
|
}, [props])
|
||||||
|
|
||||||
|
// Methods: Creating a new party
|
||||||
|
async function createParty() {
|
||||||
|
let body = {
|
||||||
|
party: {
|
||||||
|
...(cookies.user) && { user_id: cookies.user.user_id },
|
||||||
|
is_extra: extra
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await api.endpoints.parties.create(body, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods: Updating the party's extra flag
|
||||||
|
// Note: This doesn't save to the server yet.
|
||||||
function checkboxChanged(event: React.ChangeEvent<HTMLInputElement>) {
|
function checkboxChanged(event: React.ChangeEvent<HTMLInputElement>) {
|
||||||
setExtra(event.target.checked)
|
setExtra(event.target.checked)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Methods: Navigating with segmented control
|
||||||
function segmentClicked(event: React.ChangeEvent<HTMLInputElement>) {
|
function segmentClicked(event: React.ChangeEvent<HTMLInputElement>) {
|
||||||
switch(event.target.value) {
|
switch(event.target.value) {
|
||||||
case 'class':
|
case 'class':
|
||||||
|
|
@ -126,174 +87,66 @@ const Party = (props: Props) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function itemSelected(type: GridType, item: Character | Weapon | Summon, position: number) {
|
// Render: JSX components
|
||||||
if (!partyId) {
|
const navigation = (
|
||||||
createParty()
|
<PartySegmentedControl
|
||||||
.then(response => {
|
extra={props.extra}
|
||||||
return response.data.party
|
editable={props.editable}
|
||||||
})
|
selectedTab={currentTab}
|
||||||
.then(party => {
|
onClick={segmentClicked}
|
||||||
if (props.pushHistory) {
|
onCheckboxChange={checkboxChanged}
|
||||||
props.pushHistory(`/p/${party.shortcode}`)
|
/>
|
||||||
}
|
)
|
||||||
|
|
||||||
return party.id
|
const weaponGrid = (
|
||||||
})
|
<WeaponGrid
|
||||||
.then(partyId => {
|
partyId={props.partyId}
|
||||||
setPartyId(partyId)
|
mainhand={props.mainWeapon}
|
||||||
saveItem(partyId, type, item, position)
|
weapons={props.weapons || {}}
|
||||||
})
|
extra={props.extra}
|
||||||
} else {
|
editable={props.editable}
|
||||||
saveItem(partyId, type, item, position)
|
createParty={createParty}
|
||||||
}
|
pushHistory={props.pushHistory}
|
||||||
}
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
async function createParty() {
|
const summonGrid = (
|
||||||
const body = (!cookies.user) ? {
|
<SummonGrid
|
||||||
party: {
|
partyId={props.partyId}
|
||||||
is_extra: extra
|
mainSummon={props.mainSummon}
|
||||||
}
|
friendSummon={props.friendSummon}
|
||||||
} : {
|
summons={props.summons || {}}
|
||||||
party: {
|
editable={props.editable}
|
||||||
user_id: cookies.user.userId,
|
createParty={createParty}
|
||||||
is_extra: extra
|
pushHistory={props.pushHistory}
|
||||||
}
|
/>
|
||||||
}
|
)
|
||||||
|
|
||||||
return await api.endpoints.parties.create(body, headers)
|
const characterGrid = (
|
||||||
}
|
<CharacterGrid
|
||||||
|
partyId={props.partyId}
|
||||||
|
characters={props.characters || {}}
|
||||||
|
editable={props.editable}
|
||||||
|
createParty={createParty}
|
||||||
|
pushHistory={props.pushHistory}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
function saveItem(partyId: string, type: GridType, item: Character | Weapon | Summon, position: number) {
|
const currentGrid = () => {
|
||||||
switch(type) {
|
switch(currentTab) {
|
||||||
case GridType.Class:
|
|
||||||
saveClass()
|
|
||||||
break
|
|
||||||
case GridType.Character:
|
case GridType.Character:
|
||||||
const character = item as Character
|
return characterGrid
|
||||||
saveCharacter(character, position, partyId)
|
|
||||||
.then(() => {
|
|
||||||
storeCharacter(character, position)
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case GridType.Weapon:
|
case GridType.Weapon:
|
||||||
const weapon = item as Weapon
|
return weaponGrid
|
||||||
saveWeapon(weapon, position, partyId)
|
|
||||||
.then((response) => {
|
|
||||||
storeWeapon(response.data.grid_weapon)
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case GridType.Summon:
|
case GridType.Summon:
|
||||||
const summon = item as Summon
|
return summonGrid
|
||||||
saveSummon(summon, position, partyId)
|
|
||||||
.then((response) => {
|
|
||||||
storeSummon(response.data.grid_summon, position)
|
|
||||||
})
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Weapons
|
|
||||||
function storeWeapon(weapon: GridWeapon) {
|
|
||||||
if (weapon.position == -1) {
|
|
||||||
setMainWeapon(weapon)
|
|
||||||
} else {
|
|
||||||
// Store the grid unit weapon at the correct position
|
|
||||||
let newWeapons = Object.assign({}, weapons)
|
|
||||||
newWeapons[weapon.position!] = weapon
|
|
||||||
setWeapons(newWeapons)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveWeapon(weapon: Weapon, position: number, party: string) {
|
|
||||||
let uncapLevel = 3
|
|
||||||
|
|
||||||
if (weapon.uncap.ulb)
|
|
||||||
uncapLevel = 5
|
|
||||||
else if (weapon.uncap.flb)
|
|
||||||
uncapLevel = 4
|
|
||||||
|
|
||||||
return await api.endpoints.weapons.create({
|
|
||||||
'weapon': {
|
|
||||||
'party_id': party,
|
|
||||||
'weapon_id': weapon.id,
|
|
||||||
'position': position,
|
|
||||||
'mainhand': (position == -1),
|
|
||||||
'uncap_level': uncapLevel
|
|
||||||
}
|
|
||||||
}, headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Summons
|
|
||||||
function storeSummon(summon: GridSummon, position: number) {
|
|
||||||
if (position == -1) {
|
|
||||||
setMainSummon(summon)
|
|
||||||
} else if (position == 6) {
|
|
||||||
setFriendSummon(summon)
|
|
||||||
} else {
|
|
||||||
// Store the grid unit summon at the correct position
|
|
||||||
let newSummons = Object.assign({}, summons)
|
|
||||||
newSummons[position] = summon
|
|
||||||
setSummons(newSummons)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveSummon(summon: Summon, position: number, party: string) {
|
|
||||||
return await api.endpoints.summons.create({
|
|
||||||
'summon': {
|
|
||||||
'party_id': party,
|
|
||||||
'summon_id': summon.id,
|
|
||||||
'position': position,
|
|
||||||
'main': (position == -1),
|
|
||||||
'friend': (position == 6)
|
|
||||||
}
|
|
||||||
}, headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Character
|
|
||||||
function storeCharacter(character: Character, position: number) {
|
|
||||||
// Store the grid unit character at the correct position
|
|
||||||
let newCharacters = Object.assign({}, characters)
|
|
||||||
newCharacters[position] = character
|
|
||||||
setCharacters(newCharacters)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveCharacter(character: Character, position: number, party: string) {
|
|
||||||
await api.endpoints.characters.create({
|
|
||||||
'character': {
|
|
||||||
'party_id': party,
|
|
||||||
'character_id': character.id,
|
|
||||||
'position': position
|
|
||||||
}
|
|
||||||
}, headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Class
|
|
||||||
function saveClass() {
|
|
||||||
// TODO: Implement this
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PartySegmentedControl
|
{ navigation }
|
||||||
extra={extra}
|
{ currentGrid() }
|
||||||
editable={props.editable}
|
|
||||||
selectedTab={currentTab}
|
|
||||||
onClick={segmentClicked}
|
|
||||||
onCheckboxChange={checkboxChanged}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{
|
|
||||||
(() => {
|
|
||||||
switch(currentTab) {
|
|
||||||
case GridType.Character:
|
|
||||||
return characterGrid
|
|
||||||
case GridType.Weapon:
|
|
||||||
return weaponGrid
|
|
||||||
case GridType.Summon:
|
|
||||||
return summonGrid
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
import React, { useCallback, useState } from 'react'
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
|
import { useCookies } from 'react-cookie'
|
||||||
import { useModal as useModal } from '~utils/useModal'
|
import { useModal as useModal } from '~utils/useModal'
|
||||||
|
|
||||||
|
import { AxiosResponse } from 'axios'
|
||||||
import debounce from 'lodash.debounce'
|
import debounce from 'lodash.debounce'
|
||||||
|
|
||||||
import SearchModal from '~components/SearchModal'
|
import SearchModal from '~components/SearchModal'
|
||||||
|
|
@ -20,128 +23,237 @@ export enum GridType {
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
interface Props {
|
interface Props {
|
||||||
userId?: string
|
|
||||||
partyId?: string
|
partyId?: string
|
||||||
main?: GridSummon | undefined
|
mainSummon: GridSummon | undefined
|
||||||
friend?: GridSummon | undefined
|
friendSummon: GridSummon | undefined
|
||||||
grid: GridArray<GridSummon>
|
summons: GridArray<GridSummon>
|
||||||
editable: boolean
|
editable: boolean
|
||||||
exists: boolean
|
createParty: () => Promise<AxiosResponse<any, any>>
|
||||||
found?: boolean
|
pushHistory?: (path: string) => void
|
||||||
onSelect: (type: GridType, summon: Summon, position: number) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const SummonGrid = (props: Props) => {
|
const SummonGrid = (props: Props) => {
|
||||||
const { open, openModal, closeModal } = useModal()
|
// Constants
|
||||||
const [searchPosition, setSearchPosition] = useState(0)
|
|
||||||
|
|
||||||
const numSummons: number = 4
|
const numSummons: number = 4
|
||||||
const searchGrid: GridArray<Summon> = Object.values(props.grid).map((o) => o.summon)
|
|
||||||
|
|
||||||
function receiveSummon(summon: Summon, position: number) {
|
// Cookies
|
||||||
props.onSelect(GridType.Summon, summon, position)
|
const [cookies, _] = useCookies(['user'])
|
||||||
}
|
const headers = (cookies.user != null) ? {
|
||||||
|
headers: {
|
||||||
function sendData(object: Character | Weapon | Summon, position: number) {
|
'Authorization': `Bearer ${cookies.user.access_token}`
|
||||||
if (isSummon(object)) {
|
|
||||||
receiveSummon(object, position)
|
|
||||||
}
|
}
|
||||||
}
|
} : {}
|
||||||
|
|
||||||
function isSummon(object: Character | Weapon | Summon): object is Summon {
|
// Set up states for Grid data
|
||||||
// There aren't really any unique fields here
|
const [summons, setSummons] = useState<GridArray<GridSummon>>({})
|
||||||
return (object as Summon).granblue_id !== undefined
|
const [mainSummon, setMainSummon] = useState<GridSummon>()
|
||||||
}
|
const [friendSummon, setFriendSummon] = useState<GridSummon>()
|
||||||
|
|
||||||
|
// Set up states for Search
|
||||||
|
const { open, openModal, closeModal } = useModal()
|
||||||
|
const [itemPositionForSearch, setItemPositionForSearch] = useState(0)
|
||||||
|
|
||||||
|
// Create a temporary state to store previous weapon uncap value
|
||||||
|
const [previousUncapValues, setPreviousUncapValues] = useState<{[key: number]: number}>({})
|
||||||
|
|
||||||
|
// Create a state dictionary to store pure objects for Search
|
||||||
|
const [searchGrid, setSearchGrid] = useState<GridArray<Summon>>({})
|
||||||
|
|
||||||
|
// Set states from props
|
||||||
|
useEffect(() => {
|
||||||
|
setSummons(props.summons || {})
|
||||||
|
setMainSummon(props.mainSummon)
|
||||||
|
setFriendSummon(props.friendSummon)
|
||||||
|
}, [props])
|
||||||
|
|
||||||
|
// Update search grid whenever any summon is updated
|
||||||
|
useEffect(() => {
|
||||||
|
let newSearchGrid = Object.values(summons).map((o) => o.summon)
|
||||||
|
|
||||||
|
if (mainSummon)
|
||||||
|
newSearchGrid.unshift(mainSummon.summon)
|
||||||
|
|
||||||
|
if (friendSummon)
|
||||||
|
newSearchGrid.unshift(friendSummon.summon)
|
||||||
|
|
||||||
|
setSearchGrid(newSearchGrid)
|
||||||
|
}, [summons, mainSummon, friendSummon])
|
||||||
|
|
||||||
|
// Methods: Adding an object from search
|
||||||
function openSearchModal(position: number) {
|
function openSearchModal(position: number) {
|
||||||
setSearchPosition(position)
|
setItemPositionForSearch(position)
|
||||||
openModal()
|
openModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateUncap(id: string, level: number) {
|
function receiveSummonFromSearch(object: Character | Weapon | Summon, position: number) {
|
||||||
await api.updateUncap('summon', id, level)
|
const summon = object as Summon
|
||||||
.catch(error => {
|
|
||||||
console.error(error)
|
if (!props.partyId) {
|
||||||
})
|
props.createParty()
|
||||||
|
.then(response => {
|
||||||
|
const party = response.data.party
|
||||||
|
if (props.pushHistory) props.pushHistory(`/p/${party.shortcode}`)
|
||||||
|
saveSummon(party.id, summon, position)
|
||||||
|
.then(response => storeGridSummon(response.data.grid_summon))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
saveSummon(props.partyId, summon, position)
|
||||||
|
.then(response => storeGridSummon(response.data.grid_summon))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const initiateUncapUpdate = (id: string, uncapLevel: number) => {
|
async function saveSummon(partyId: string, summon: Summon, position: number) {
|
||||||
debouncedAction(id, uncapLevel)
|
let uncapLevel = 3
|
||||||
|
if (summon.uncap.ulb) uncapLevel = 5
|
||||||
|
else if (summon.uncap.flb) uncapLevel = 4
|
||||||
|
|
||||||
|
return await api.endpoints.summons.create({
|
||||||
|
'summon': {
|
||||||
|
'party_id': partyId,
|
||||||
|
'summon_id': summon.id,
|
||||||
|
'position': position,
|
||||||
|
'main': (position == -1),
|
||||||
|
'friend': (position == 6),
|
||||||
|
'uncap_level': uncapLevel
|
||||||
|
}
|
||||||
|
}, headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
const debouncedAction = useCallback(
|
function storeGridSummon(gridSummon: GridSummon) {
|
||||||
() => debounce((id, number) => {
|
if (gridSummon.position == -1) {
|
||||||
updateUncap(id, number)
|
setMainSummon(gridSummon)
|
||||||
}, 1000), []
|
} else if (gridSummon.position == 6) {
|
||||||
)()
|
setFriendSummon(gridSummon)
|
||||||
|
} else {
|
||||||
|
// Store the grid unit at the correct position
|
||||||
|
let newSummons = Object.assign({}, summons)
|
||||||
|
newSummons[gridSummon.position] = gridSummon
|
||||||
|
setSummons(newSummons)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods: Updating uncap level
|
||||||
|
// Note: Saves, but debouncing is not working properly
|
||||||
|
async function saveUncap(id: string, position: number, uncapLevel: number) {
|
||||||
|
try {
|
||||||
|
await api.updateUncap('summon', id, uncapLevel)
|
||||||
|
.then(response => {
|
||||||
|
storeGridSummon(response.data.grid_summon)
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
|
||||||
|
// Revert optimistic UI
|
||||||
|
updateUncapLevel(position, previousUncapValues[position])
|
||||||
|
|
||||||
|
// Remove optimistic key
|
||||||
|
let newPreviousValues = {...previousUncapValues}
|
||||||
|
delete newPreviousValues[position]
|
||||||
|
setPreviousUncapValues(newPreviousValues)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const initiateUncapUpdate = useCallback(
|
||||||
|
(id: string, position: number, uncapLevel: number) => {
|
||||||
|
debouncedAction(id, position, uncapLevel)
|
||||||
|
|
||||||
|
// Save the current value in case of an unexpected result
|
||||||
|
let newPreviousValues = {...previousUncapValues}
|
||||||
|
newPreviousValues[position] = summons[position].uncap_level
|
||||||
|
setPreviousUncapValues(newPreviousValues)
|
||||||
|
|
||||||
|
// Optimistically update UI
|
||||||
|
updateUncapLevel(position, uncapLevel)
|
||||||
|
}, [previousUncapValues, summons]
|
||||||
|
)
|
||||||
|
|
||||||
|
const debouncedAction = useMemo(() =>
|
||||||
|
debounce((id, position, number) => {
|
||||||
|
saveUncap(id, position, number)
|
||||||
|
}, 1000), [saveUncap]
|
||||||
|
)
|
||||||
|
|
||||||
|
const updateUncapLevel = (position: number, uncapLevel: number) => {
|
||||||
|
let newSummons = Object.assign({}, summons)
|
||||||
|
newSummons[position].uncap_level = uncapLevel
|
||||||
|
setSummons(newSummons)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render: JSX components
|
||||||
|
const mainSummonElement = (
|
||||||
|
<div className="LabeledUnit">
|
||||||
|
<div className="Label">Main Summon</div>
|
||||||
|
<SummonUnit
|
||||||
|
gridSummon={props.mainSummon}
|
||||||
|
editable={props.editable}
|
||||||
|
key="grid_main_summon"
|
||||||
|
position={-1}
|
||||||
|
unitType={0}
|
||||||
|
onClick={() => { openSearchModal(-1) }}
|
||||||
|
updateUncap={initiateUncapUpdate}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const friendSummonElement = (
|
||||||
|
<div className="LabeledUnit">
|
||||||
|
<div className="Label">Friend Summon</div>
|
||||||
|
<SummonUnit
|
||||||
|
gridSummon={props.friendSummon}
|
||||||
|
editable={props.editable}
|
||||||
|
key="grid_friend_summon"
|
||||||
|
position={6}
|
||||||
|
unitType={2}
|
||||||
|
onClick={() => { openSearchModal(6) }}
|
||||||
|
updateUncap={initiateUncapUpdate}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
const summonGridElement = (
|
||||||
|
<div id="LabeledGrid">
|
||||||
|
<div className="Label">Summons</div>
|
||||||
|
<ul id="grid_summons">
|
||||||
|
{Array.from(Array(numSummons)).map((x, i) => {
|
||||||
|
return (<li key={`grid_unit_${i}`} >
|
||||||
|
<SummonUnit
|
||||||
|
gridSummon={props.summons[i]}
|
||||||
|
editable={props.editable}
|
||||||
|
position={i}
|
||||||
|
unitType={1}
|
||||||
|
onClick={() => { openSearchModal(i) }}
|
||||||
|
updateUncap={initiateUncapUpdate}
|
||||||
|
/>
|
||||||
|
</li>)
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
const subAuraSummonElement = (
|
||||||
|
<ExtraSummons
|
||||||
|
grid={props.summons}
|
||||||
|
editable={props.editable}
|
||||||
|
exists={false}
|
||||||
|
offset={numSummons}
|
||||||
|
onClick={openSearchModal}
|
||||||
|
updateUncap={initiateUncapUpdate}
|
||||||
|
/>
|
||||||
|
)
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="SummonGrid">
|
<div className="SummonGrid">
|
||||||
<div className="LabeledUnit">
|
{ mainSummonElement }
|
||||||
<div className="Label">Main Summon</div>
|
{ friendSummonElement }
|
||||||
<SummonUnit
|
{ summonGridElement }
|
||||||
editable={props.editable}
|
|
||||||
key="grid_main_summon"
|
|
||||||
position={-1}
|
|
||||||
unitType={0}
|
|
||||||
gridSummon={props.main}
|
|
||||||
onClick={() => { openSearchModal(-1) }}
|
|
||||||
updateUncap={initiateUncapUpdate}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="LabeledUnit">
|
|
||||||
<div className="Label">Friend Summon</div>
|
|
||||||
<SummonUnit
|
|
||||||
editable={props.editable}
|
|
||||||
key="grid_friend_summon"
|
|
||||||
position={6}
|
|
||||||
unitType={2}
|
|
||||||
gridSummon={props.friend}
|
|
||||||
onClick={() => { openSearchModal(6) }}
|
|
||||||
updateUncap={initiateUncapUpdate}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="LabeledGrid">
|
|
||||||
<div className="Label">Summons</div>
|
|
||||||
<ul id="grid_summons">
|
|
||||||
{
|
|
||||||
Array.from(Array(numSummons)).map((x, i) => {
|
|
||||||
return (
|
|
||||||
<li key={`grid_unit_${i}`} >
|
|
||||||
<SummonUnit
|
|
||||||
editable={props.editable}
|
|
||||||
position={i}
|
|
||||||
unitType={1}
|
|
||||||
gridSummon={props.grid[i]}
|
|
||||||
onClick={() => { openSearchModal(i) }}
|
|
||||||
updateUncap={initiateUncapUpdate}
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ExtraSummons
|
{ subAuraSummonElement }
|
||||||
grid={props.grid}
|
|
||||||
editable={props.editable}
|
|
||||||
exists={false}
|
|
||||||
offset={numSummons}
|
|
||||||
onClick={openSearchModal}
|
|
||||||
updateUncap={initiateUncapUpdate}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{open ? (
|
{open ? (
|
||||||
<SearchModal
|
<SearchModal
|
||||||
grid={searchGrid}
|
grid={searchGrid}
|
||||||
close={closeModal}
|
close={closeModal}
|
||||||
send={sendData}
|
send={receiveSummonFromSearch}
|
||||||
fromPosition={searchPosition}
|
fromPosition={itemPositionForSearch}
|
||||||
object="summons"
|
object="summons"
|
||||||
placeholderText="Search for a summon..."
|
placeholderText="Search for a summon..."
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import './index.scss'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onClick: () => void
|
onClick: () => void
|
||||||
updateUncap: (id: string, uncap: number) => void
|
updateUncap: (id: string, position: number, uncap: number) => void
|
||||||
gridSummon: GridSummon | undefined
|
gridSummon: GridSummon | undefined
|
||||||
position: number
|
position: number
|
||||||
editable: boolean
|
editable: boolean
|
||||||
|
|
@ -67,7 +67,8 @@ const SummonUnit = (props: Props) => {
|
||||||
ulb={summon?.uncap.ulb || false}
|
ulb={summon?.uncap.ulb || false}
|
||||||
flb={summon?.uncap.flb || false}
|
flb={summon?.uncap.flb || false}
|
||||||
uncapLevel={gridSummon?.uncap_level}
|
uncapLevel={gridSummon?.uncap_level}
|
||||||
updateUncap={passUncapData}
|
updateUncap={passUncapData}
|
||||||
|
special={false}
|
||||||
/> : '' }
|
/> : '' }
|
||||||
<h3 className="SummonName">{summon?.name.en}</h3>
|
<h3 className="SummonName">{summon?.name.en}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -8,27 +8,36 @@ interface Props {
|
||||||
rarity?: number
|
rarity?: number
|
||||||
uncapLevel: number
|
uncapLevel: number
|
||||||
flb: boolean
|
flb: boolean
|
||||||
ulb?: boolean
|
ulb: boolean
|
||||||
|
special: boolean
|
||||||
updateUncap: (uncap: number) => void
|
updateUncap: (uncap: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const UncapIndicator = (props: Props) => {
|
const UncapIndicator = (props: Props) => {
|
||||||
const [uncap, setUncap] = useState(props.uncapLevel)
|
const [uncap, setUncap] = useState(props.uncapLevel)
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
props.updateUncap(uncap)
|
|
||||||
}, [uncap])
|
|
||||||
|
|
||||||
const numStars = setNumStars()
|
const numStars = setNumStars()
|
||||||
function setNumStars() {
|
function setNumStars() {
|
||||||
let numStars
|
let numStars
|
||||||
|
|
||||||
if (props.type === 'character') {
|
if (props.type === 'character') {
|
||||||
if (props.flb) {
|
if (props.special) {
|
||||||
numStars = 5
|
if (props.ulb) {
|
||||||
|
numStars = 5
|
||||||
|
} else if (props.flb) {
|
||||||
|
numStars = 4
|
||||||
|
} else {
|
||||||
|
numStars = 3
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
numStars = 4
|
if (props.ulb) {
|
||||||
}
|
numStars = 6
|
||||||
|
} else if (props.flb) {
|
||||||
|
numStars = 5
|
||||||
|
} else {
|
||||||
|
numStars = 4
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (props.ulb) {
|
if (props.ulb) {
|
||||||
numStars = 5
|
numStars = 5
|
||||||
|
|
@ -43,31 +52,38 @@ const UncapIndicator = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleStar(index: number, empty: boolean) {
|
function toggleStar(index: number, empty: boolean) {
|
||||||
if (empty) setUncap(index + 1)
|
if (empty) props.updateUncap(index + 1)
|
||||||
else setUncap(index)
|
else props.updateUncap(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
const transcendence = (i: number) => {
|
const transcendence = (i: number) => {
|
||||||
return <UncapStar ulb={true} empty={i >= uncap} key={`star_${i}`} index={i} onClick={toggleStar} />
|
return <UncapStar ulb={true} empty={i >= props.uncapLevel} key={`star_${i}`} index={i} onClick={toggleStar} />
|
||||||
|
}
|
||||||
|
|
||||||
|
const ulb = (i: number) => {
|
||||||
|
return <UncapStar ulb={true} empty={i >= props.uncapLevel} key={`star_${i}`} index={i} onClick={toggleStar} />
|
||||||
}
|
}
|
||||||
|
|
||||||
const flb = (i: number) => {
|
const flb = (i: number) => {
|
||||||
return <UncapStar flb={true} empty={i >= uncap} key={`star_${i}`} index={i} onClick={toggleStar} />
|
return <UncapStar flb={true} empty={i >= props.uncapLevel} key={`star_${i}`} index={i} onClick={toggleStar} />
|
||||||
}
|
}
|
||||||
|
|
||||||
const mlb = (i: number) => {
|
const mlb = (i: number) => {
|
||||||
return <UncapStar empty={i >= uncap} key={`star_${i}`} index={i} onClick={toggleStar} />
|
// console.log("MLB; Number of stars:", props.uncapLevel)
|
||||||
|
return <UncapStar empty={i >= props.uncapLevel} key={`star_${i}`} index={i} onClick={toggleStar} />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul className="UncapIndicator">
|
<ul className="UncapIndicator">
|
||||||
{
|
{
|
||||||
Array.from(Array(numStars)).map((x, i) => {
|
Array.from(Array(numStars)).map((x, i) => {
|
||||||
if (props.type === 'character' && i > 4) {
|
if (props.type === 'character' && i > 4) {
|
||||||
return transcendence(i)
|
if (props.special)
|
||||||
|
return ulb(i)
|
||||||
|
else
|
||||||
|
return transcendence(i)
|
||||||
} else if (
|
} else if (
|
||||||
|
props.special && props.type === 'character' && i == 3 ||
|
||||||
props.type === 'character' && i == 4 ||
|
props.type === 'character' && i == 4 ||
|
||||||
props.type !== 'character' && i > 2) {
|
props.type !== 'character' && i > 2) {
|
||||||
return flb(i)
|
return flb(i)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
import React, { useCallback, useState } from 'react'
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
|
import { useCookies } from 'react-cookie'
|
||||||
import { useModal as useModal } from '~utils/useModal'
|
import { useModal as useModal } from '~utils/useModal'
|
||||||
|
|
||||||
|
import { AxiosResponse } from 'axios'
|
||||||
import debounce from 'lodash.debounce'
|
import debounce from 'lodash.debounce'
|
||||||
|
|
||||||
import SearchModal from '~components/SearchModal'
|
import SearchModal from '~components/SearchModal'
|
||||||
|
|
@ -20,65 +23,199 @@ export enum GridType {
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
interface Props {
|
interface Props {
|
||||||
userId?: string
|
|
||||||
partyId?: string
|
partyId?: string
|
||||||
mainhand?: GridWeapon | undefined
|
mainhand: GridWeapon | undefined
|
||||||
grid: GridArray<GridWeapon>
|
weapons: GridArray<GridWeapon>
|
||||||
extra: boolean
|
extra: boolean
|
||||||
editable: boolean
|
editable: boolean
|
||||||
exists: boolean
|
createParty: () => Promise<AxiosResponse<any, any>>
|
||||||
found?: boolean
|
pushHistory?: (path: string) => void
|
||||||
onSelect: (type: GridType, weapon: Weapon, position: number) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const WeaponGrid = (props: Props) => {
|
const WeaponGrid = (props: Props) => {
|
||||||
const { open, openModal, closeModal } = useModal()
|
// Constants
|
||||||
const [searchPosition, setSearchPosition] = useState(0)
|
|
||||||
|
|
||||||
const numWeapons: number = 9
|
const numWeapons: number = 9
|
||||||
const searchGrid: GridArray<Weapon> = Object.values(props.grid).map((o) => o.weapon)
|
|
||||||
|
|
||||||
function receiveWeapon(weapon: Weapon, position: number) {
|
// Cookies
|
||||||
props.onSelect(GridType.Weapon, weapon, position)
|
const [cookies, _] = useCookies(['user'])
|
||||||
}
|
const headers = (cookies.user != null) ? {
|
||||||
|
headers: {
|
||||||
function sendData(object: Character | Weapon | Summon, position: number) {
|
'Authorization': `Bearer ${cookies.user.access_token}`
|
||||||
if (isWeapon(object)) {
|
|
||||||
receiveWeapon(object, position)
|
|
||||||
}
|
}
|
||||||
}
|
} : {}
|
||||||
|
|
||||||
function isWeapon(object: Character | Weapon | Summon): object is Weapon {
|
// Set up states for Grid data
|
||||||
return (object as Weapon).proficiency !== undefined
|
const [weapons, setWeapons] = useState<GridArray<GridWeapon>>({})
|
||||||
}
|
const [mainWeapon, setMainWeapon] = useState<GridWeapon>()
|
||||||
|
|
||||||
|
// Set up states for Search
|
||||||
|
const { open, openModal, closeModal } = useModal()
|
||||||
|
const [itemPositionForSearch, setItemPositionForSearch] = useState(0)
|
||||||
|
|
||||||
|
// Create a temporary state to store previous weapon uncap values
|
||||||
|
const [previousUncapValues, setPreviousUncapValues] = useState<{[key: number]: number}>({})
|
||||||
|
|
||||||
|
// Create a state dictionary to store pure objects for Search
|
||||||
|
const [searchGrid, setSearchGrid] = useState<GridArray<Weapon>>({})
|
||||||
|
|
||||||
|
// Set states from props
|
||||||
|
useEffect(() => {
|
||||||
|
setWeapons(props.weapons || {})
|
||||||
|
setMainWeapon(props.mainhand)
|
||||||
|
}, [props])
|
||||||
|
|
||||||
|
// Update search grid whenever weapons or the mainhand are updated
|
||||||
|
useEffect(() => {
|
||||||
|
let newSearchGrid = Object.values(weapons).map((o) => o.weapon)
|
||||||
|
|
||||||
|
if (mainWeapon)
|
||||||
|
newSearchGrid.unshift(mainWeapon.weapon)
|
||||||
|
|
||||||
|
setSearchGrid(newSearchGrid)
|
||||||
|
}, [weapons, mainWeapon])
|
||||||
|
|
||||||
|
// Methods: Adding an object from search
|
||||||
function openSearchModal(position: number) {
|
function openSearchModal(position: number) {
|
||||||
setSearchPosition(position)
|
setItemPositionForSearch(position)
|
||||||
openModal()
|
openModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateUncap(id: string, level: number) {
|
function receiveWeaponFromSearch(object: Character | Weapon | Summon, position: number) {
|
||||||
await api.updateUncap('weapon', id, level)
|
const weapon = object as Weapon
|
||||||
.catch(error => {
|
|
||||||
console.error(error)
|
if (!props.partyId) {
|
||||||
})
|
props.createParty()
|
||||||
|
.then(response => {
|
||||||
|
const party = response.data.party
|
||||||
|
if (props.pushHistory) props.pushHistory(`/p/${party.shortcode}`)
|
||||||
|
saveWeapon(party.id, weapon, position)
|
||||||
|
.then(response => storeGridWeapon(response.data.grid_weapon))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
saveWeapon(props.partyId, weapon, position)
|
||||||
|
.then(response => storeGridWeapon(response.data.grid_weapon))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const initiateUncapUpdate = (id: string, uncapLevel: number) => {
|
async function saveWeapon(partyId: string, weapon: Weapon, position: number) {
|
||||||
debouncedAction(id, uncapLevel)
|
let uncapLevel = 3
|
||||||
|
if (weapon.uncap.ulb) uncapLevel = 5
|
||||||
|
else if (weapon.uncap.flb) uncapLevel = 4
|
||||||
|
|
||||||
|
return await api.endpoints.weapons.create({
|
||||||
|
'weapon': {
|
||||||
|
'party_id': partyId,
|
||||||
|
'weapon_id': weapon.id,
|
||||||
|
'position': position,
|
||||||
|
'mainhand': (position == -1),
|
||||||
|
'uncap_level': uncapLevel
|
||||||
|
}
|
||||||
|
}, headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
const debouncedAction = useCallback(
|
function storeGridWeapon(gridWeapon: GridWeapon) {
|
||||||
() => debounce((id, number) => {
|
if (gridWeapon.position == -1) {
|
||||||
updateUncap(id, number)
|
setMainWeapon(gridWeapon)
|
||||||
}, 1000), []
|
} else {
|
||||||
)()
|
// Store the grid unit at the correct position
|
||||||
|
let newWeapons = Object.assign({}, props.weapons)
|
||||||
|
newWeapons[gridWeapon.position] = gridWeapon
|
||||||
|
setWeapons(newWeapons)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const extraGrid = (
|
// Methods: Updating uncap level
|
||||||
|
// Note: Saves, but debouncing is not working properly
|
||||||
|
async function saveUncap(id: string, position: number, uncapLevel: number) {
|
||||||
|
// TODO: Don't make an API call if the new uncapLevel is the same as the current uncapLevel
|
||||||
|
try {
|
||||||
|
await api.updateUncap('weapon', id, uncapLevel)
|
||||||
|
.then(response => {
|
||||||
|
storeGridWeapon(response.data.grid_weapon)
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
|
||||||
|
// Revert optimistic UI
|
||||||
|
updateUncapLevel(position, previousUncapValues[position])
|
||||||
|
|
||||||
|
// Remove optimistic key
|
||||||
|
let newPreviousValues = {...previousUncapValues}
|
||||||
|
delete newPreviousValues[position]
|
||||||
|
setPreviousUncapValues(newPreviousValues)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const memoizeAction = useCallback(
|
||||||
|
(id: string, position: number, uncapLevel: number) => {
|
||||||
|
debouncedAction(id, position, uncapLevel)
|
||||||
|
}, []
|
||||||
|
)
|
||||||
|
|
||||||
|
function initiateUncapUpdate(id: string, position: number, uncapLevel: number) {
|
||||||
|
memoizeAction(id, position, uncapLevel)
|
||||||
|
|
||||||
|
// Save the current value in case of an unexpected result
|
||||||
|
let newPreviousValues = {...previousUncapValues}
|
||||||
|
newPreviousValues[position] = (mainWeapon && position == -1) ? mainWeapon.uncap_level : weapons[position].uncap_level
|
||||||
|
setPreviousUncapValues(newPreviousValues)
|
||||||
|
|
||||||
|
// Optimistically update UI
|
||||||
|
updateUncapLevel(position, uncapLevel)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const debouncedAction = useMemo(() =>
|
||||||
|
debounce((id, position, number) => {
|
||||||
|
saveUncap(id, position, number)
|
||||||
|
}, 1000), [saveUncap]
|
||||||
|
)
|
||||||
|
|
||||||
|
const updateUncapLevel = (position: number, uncapLevel: number) => {
|
||||||
|
if (mainWeapon && position == -1) {
|
||||||
|
mainWeapon.uncap_level = uncapLevel
|
||||||
|
setMainWeapon(mainWeapon)
|
||||||
|
} else {
|
||||||
|
let newWeapons = Object.assign({}, weapons)
|
||||||
|
newWeapons[position].uncap_level = uncapLevel
|
||||||
|
setWeapons(newWeapons)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render: JSX components
|
||||||
|
const mainhandElement = (
|
||||||
|
<WeaponUnit
|
||||||
|
gridWeapon={mainWeapon}
|
||||||
|
editable={props.editable}
|
||||||
|
key="grid_mainhand"
|
||||||
|
position={-1}
|
||||||
|
unitType={0}
|
||||||
|
onClick={() => { openSearchModal(-1) }}
|
||||||
|
updateUncap={initiateUncapUpdate}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
const weaponGridElement = (
|
||||||
|
Array.from(Array(numWeapons)).map((x, i) => {
|
||||||
|
return (
|
||||||
|
<li key={`grid_unit_${i}`} >
|
||||||
|
<WeaponUnit
|
||||||
|
gridWeapon={weapons[i]}
|
||||||
|
editable={props.editable}
|
||||||
|
position={i}
|
||||||
|
unitType={1}
|
||||||
|
onClick={() => { openSearchModal(i) }}
|
||||||
|
updateUncap={initiateUncapUpdate}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const extraGridElement = (
|
||||||
<ExtraWeapons
|
<ExtraWeapons
|
||||||
grid={props.grid}
|
grid={weapons}
|
||||||
editable={props.editable}
|
editable={props.editable}
|
||||||
exists={false}
|
|
||||||
offset={numWeapons}
|
offset={numWeapons}
|
||||||
onClick={openSearchModal}
|
onClick={openSearchModal}
|
||||||
updateUncap={initiateUncapUpdate}
|
updateUncap={initiateUncapUpdate}
|
||||||
|
|
@ -88,48 +225,18 @@ const WeaponGrid = (props: Props) => {
|
||||||
return (
|
return (
|
||||||
<div id="weapon_grids">
|
<div id="weapon_grids">
|
||||||
<div id="WeaponGrid">
|
<div id="WeaponGrid">
|
||||||
<WeaponUnit
|
{ mainhandElement }
|
||||||
editable={props.editable}
|
<ul id="grid_weapons">{ weaponGridElement }</ul>
|
||||||
key="grid_mainhand"
|
|
||||||
position={-1}
|
|
||||||
unitType={0}
|
|
||||||
gridWeapon={props.mainhand}
|
|
||||||
onClick={() => { openSearchModal(-1) }}
|
|
||||||
updateUncap={initiateUncapUpdate}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ul id="grid_weapons">
|
|
||||||
{
|
|
||||||
Array.from(Array(numWeapons)).map((x, i) => {
|
|
||||||
return (
|
|
||||||
<li key={`grid_unit_${i}`} >
|
|
||||||
<WeaponUnit
|
|
||||||
editable={props.editable}
|
|
||||||
position={i}
|
|
||||||
unitType={1}
|
|
||||||
gridWeapon={props.grid[i]}
|
|
||||||
onClick={() => { openSearchModal(i) }}
|
|
||||||
updateUncap={initiateUncapUpdate}
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{ (() => {
|
{ (() => { return (props.extra) ? extraGridElement : '' })() }
|
||||||
if(props.extra) {
|
|
||||||
return extraGrid
|
|
||||||
}
|
|
||||||
})() }
|
|
||||||
|
|
||||||
{open ? (
|
{open ? (
|
||||||
<SearchModal
|
<SearchModal
|
||||||
grid={searchGrid}
|
grid={searchGrid}
|
||||||
close={closeModal}
|
close={closeModal}
|
||||||
send={sendData}
|
send={receiveWeaponFromSearch}
|
||||||
fromPosition={searchPosition}
|
fromPosition={itemPositionForSearch}
|
||||||
object="weapons"
|
object="weapons"
|
||||||
placeholderText="Search for a weapon..."
|
placeholderText="Search for a weapon..."
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ import PlusIcon from '~public/icons/plus.svg'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onClick: () => void
|
|
||||||
updateUncap: (id: string, uncap: number) => void
|
|
||||||
gridWeapon: GridWeapon | undefined
|
gridWeapon: GridWeapon | undefined
|
||||||
|
unitType: 0 | 1
|
||||||
position: number
|
position: number
|
||||||
editable: boolean
|
editable: boolean
|
||||||
unitType: 0 | 1
|
onClick: () => void
|
||||||
|
updateUncap: (id: string, position: number, uncap: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const WeaponUnit = (props: Props) => {
|
const WeaponUnit = (props: Props) => {
|
||||||
|
|
@ -48,8 +48,9 @@ const WeaponUnit = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function passUncapData(uncap: number) {
|
function passUncapData(uncap: number) {
|
||||||
|
console.log("Passing uncap data to updateUncap callback...")
|
||||||
if (props.gridWeapon)
|
if (props.gridWeapon)
|
||||||
props.updateUncap(props.gridWeapon.id, uncap)
|
props.updateUncap(props.gridWeapon.id, props.position, uncap)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -66,7 +67,8 @@ const WeaponUnit = (props: Props) => {
|
||||||
ulb={gridWeapon.weapon.uncap.ulb || false}
|
ulb={gridWeapon.weapon.uncap.ulb || false}
|
||||||
flb={gridWeapon.weapon.uncap.flb || false}
|
flb={gridWeapon.weapon.uncap.flb || false}
|
||||||
uncapLevel={gridWeapon.uncap_level}
|
uncapLevel={gridWeapon.uncap_level}
|
||||||
updateUncap={passUncapData}
|
updateUncap={passUncapData}
|
||||||
|
special={false}
|
||||||
/> : ''
|
/> : ''
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
18
package-lock.json
generated
18
package-lock.json
generated
|
|
@ -12,6 +12,7 @@
|
||||||
"@radix-ui/react-label": "^0.1.4",
|
"@radix-ui/react-label": "^0.1.4",
|
||||||
"@radix-ui/react-switch": "^0.1.4",
|
"@radix-ui/react-switch": "^0.1.4",
|
||||||
"@svgr/webpack": "^6.2.0",
|
"@svgr/webpack": "^6.2.0",
|
||||||
|
"@types/axios": "^0.14.0",
|
||||||
"axios": "^0.25.0",
|
"axios": "^0.25.0",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
|
|
@ -2982,6 +2983,15 @@
|
||||||
"node": ">=10.13.0"
|
"node": ">=10.13.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/axios": {
|
||||||
|
"version": "0.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.0.tgz",
|
||||||
|
"integrity": "sha1-7CMA++fX3d1+udOr+HmZlkyvzkY=",
|
||||||
|
"deprecated": "This is a stub types definition for axios (https://github.com/mzabriskie/axios). axios provides its own type definitions, so you don't need @types/axios installed!",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/cookie": {
|
"node_modules/@types/cookie": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz",
|
||||||
|
|
@ -8754,6 +8764,14 @@
|
||||||
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
|
||||||
"integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA=="
|
"integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA=="
|
||||||
},
|
},
|
||||||
|
"@types/axios": {
|
||||||
|
"version": "0.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.0.tgz",
|
||||||
|
"integrity": "sha1-7CMA++fX3d1+udOr+HmZlkyvzkY=",
|
||||||
|
"requires": {
|
||||||
|
"axios": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/cookie": {
|
"@types/cookie": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz",
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
"@radix-ui/react-label": "^0.1.4",
|
"@radix-ui/react-label": "^0.1.4",
|
||||||
"@radix-ui/react-switch": "^0.1.4",
|
"@radix-ui/react-switch": "^0.1.4",
|
||||||
"@svgr/webpack": "^6.2.0",
|
"@svgr/webpack": "^6.2.0",
|
||||||
|
"@types/axios": "^0.14.0",
|
||||||
"axios": "^0.25.0",
|
"axios": "^0.25.0",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ const PartyRoute: React.FC = () => {
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [editable, setEditable] = useState(false)
|
const [editable, setEditable] = useState(false)
|
||||||
|
|
||||||
const [characters, setCharacters] = useState<GridArray<Character>>({})
|
const [characters, setCharacters] = useState<GridArray<GridCharacter>>({})
|
||||||
const [weapons, setWeapons] = useState<GridArray<GridWeapon>>({})
|
const [weapons, setWeapons] = useState<GridArray<GridWeapon>>({})
|
||||||
const [summons, setSummons] = useState<GridArray<GridSummon>>({})
|
const [summons, setSummons] = useState<GridArray<GridSummon>>({})
|
||||||
|
|
||||||
|
|
@ -73,11 +73,11 @@ const PartyRoute: React.FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function populateCharacters(list: [GridCharacter]) {
|
function populateCharacters(list: [GridCharacter]) {
|
||||||
let characters: GridArray<Character> = {}
|
let characters: GridArray<GridCharacter> = {}
|
||||||
|
|
||||||
list.forEach((object: GridCharacter) => {
|
list.forEach((object: GridCharacter) => {
|
||||||
if (object.position != null)
|
if (object.position != null)
|
||||||
characters[object.position] = object.character
|
characters[object.position] = object
|
||||||
})
|
})
|
||||||
|
|
||||||
return characters
|
return characters
|
||||||
|
|
|
||||||
2
types/Character.d.ts
vendored
2
types/Character.d.ts
vendored
|
|
@ -21,6 +21,7 @@ interface Character {
|
||||||
}
|
}
|
||||||
uncap: {
|
uncap: {
|
||||||
flb: boolean
|
flb: boolean
|
||||||
|
ulb: boolean
|
||||||
}
|
}
|
||||||
race: {
|
race: {
|
||||||
race1: number
|
race1: number
|
||||||
|
|
@ -31,4 +32,5 @@ interface Character {
|
||||||
proficiency2: number
|
proficiency2: number
|
||||||
}
|
}
|
||||||
position?: number
|
position?: number
|
||||||
|
special: boolean
|
||||||
}
|
}
|
||||||
3
types/GridCharacter.d.ts
vendored
3
types/GridCharacter.d.ts
vendored
|
|
@ -1,5 +1,6 @@
|
||||||
interface GridCharacter {
|
interface GridCharacter {
|
||||||
id: string
|
id: string
|
||||||
position: number | null
|
position: number
|
||||||
character: Character
|
character: Character
|
||||||
|
uncap_level: number
|
||||||
}
|
}
|
||||||
2
types/GridSummon.d.ts
vendored
2
types/GridSummon.d.ts
vendored
|
|
@ -2,7 +2,7 @@ interface GridSummon {
|
||||||
id: string
|
id: string
|
||||||
main: boolean
|
main: boolean
|
||||||
friend: boolean
|
friend: boolean
|
||||||
position: number | null
|
position: number
|
||||||
summon: Summon
|
summon: Summon
|
||||||
uncap_level: number
|
uncap_level: number
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue