Implement state management for Weapon grid
Summon and Character will be next. I didn't really pay attention to code cleanliness, so I'll try to do a pass before merging the PR
This commit is contained in:
parent
2e36a0455d
commit
9b505f5e20
8 changed files with 255 additions and 247 deletions
|
|
@ -10,6 +10,7 @@ interface Props {
|
|||
found?: boolean
|
||||
offset: number
|
||||
onClick: (position: number) => void
|
||||
updateObject: (object: Character | Weapon | Summon, position: number) => void
|
||||
updateUncap: (id: string, position: number, uncap: number) => void
|
||||
}
|
||||
|
||||
|
|
@ -30,6 +31,7 @@ const ExtraWeapons = (props: Props) => {
|
|||
unitType={1}
|
||||
gridWeapon={props.grid[props.offset + i]}
|
||||
onClick={() => { props.onClick(props.offset + i)}}
|
||||
updateObject={props.updateObject}
|
||||
updateUncap={props.updateUncap}
|
||||
/>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@
|
|||
overflow-y: auto;
|
||||
padding: $unit * 3;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
z-index: 21;
|
||||
|
||||
#ModalTop {
|
||||
#ModalHeader {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { useCookies } from 'react-cookie'
|
||||
|
||||
import PartyContext from '~context/PartyContext'
|
||||
|
||||
import PartySegmentedControl from '~components/PartySegmentedControl'
|
||||
import WeaponGrid from '~components/WeaponGrid'
|
||||
|
|
@ -9,6 +9,7 @@ import SummonGrid from '~components/SummonGrid'
|
|||
import CharacterGrid from '~components/CharacterGrid'
|
||||
|
||||
import api from '~utils/api'
|
||||
import state from '~utils/state'
|
||||
import { GridType, TeamElement } from '~utils/enums'
|
||||
|
||||
import './index.scss'
|
||||
|
|
@ -29,12 +30,8 @@ const Party = (props: Props) => {
|
|||
} : {}
|
||||
|
||||
// Set up states
|
||||
const { party } = useSnapshot(state)
|
||||
const [currentTab, setCurrentTab] = useState<GridType>(GridType.Weapon)
|
||||
const [id, setId] = useState('')
|
||||
const [slug, setSlug] = useState('')
|
||||
const [element, setElement] = useState<TeamElement>(TeamElement.Any)
|
||||
const [editable, setEditable] = useState(false)
|
||||
const [hasExtra, setHasExtra] = useState(false)
|
||||
|
||||
// Methods: Creating a new party
|
||||
async function createParty(extra: boolean = false) {
|
||||
|
|
@ -50,10 +47,12 @@ const Party = (props: Props) => {
|
|||
|
||||
// Methods: Updating the party's extra flag
|
||||
function checkboxChanged(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
setHasExtra(event.target.checked)
|
||||
api.endpoints.parties.update(id, {
|
||||
'party': { 'is_extra': event.target.checked }
|
||||
}, headers)
|
||||
if (party.id) {
|
||||
state.party.extra = event.target.checked
|
||||
api.endpoints.parties.update(party.id, {
|
||||
'party': { 'is_extra': event.target.checked }
|
||||
}, headers)
|
||||
}
|
||||
}
|
||||
|
||||
// Methods: Navigating with segmented control
|
||||
|
|
@ -122,10 +121,8 @@ const Party = (props: Props) => {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<PartyContext.Provider value={{ id, setId, slug, setSlug, element, setElement, editable, setEditable, hasExtra, setHasExtra }}>
|
||||
{ navigation }
|
||||
{ currentGrid() }
|
||||
</PartyContext.Provider>
|
||||
{ navigation }
|
||||
{ currentGrid() }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import React, { useContext } from 'react'
|
||||
import './index.scss'
|
||||
|
||||
import PartyContext from '~context/PartyContext'
|
||||
import state from '~utils/state'
|
||||
|
||||
import SegmentedControl from '~components/SegmentedControl'
|
||||
import Segment from '~components/Segment'
|
||||
import ToggleSwitch from '~components/ToggleSwitch'
|
||||
|
||||
import { GridType } from '~utils/enums'
|
||||
import { useSnapshot } from 'valtio'
|
||||
|
||||
interface Props {
|
||||
selectedTab: GridType
|
||||
|
|
@ -16,10 +17,10 @@ interface Props {
|
|||
}
|
||||
|
||||
const PartySegmentedControl = (props: Props) => {
|
||||
const { editable, element, hasExtra } = useContext(PartyContext)
|
||||
const { party } = useSnapshot(state)
|
||||
|
||||
function getElement() {
|
||||
switch(element) {
|
||||
switch(party.element) {
|
||||
case 1: return "wind"; break
|
||||
case 2: return "fire"; break
|
||||
case 3: return "water"; break
|
||||
|
|
@ -34,8 +35,8 @@ const PartySegmentedControl = (props: Props) => {
|
|||
Extra
|
||||
<ToggleSwitch
|
||||
name="ExtraSwitch"
|
||||
editable={editable}
|
||||
checked={hasExtra}
|
||||
editable={party.editable}
|
||||
checked={party.extra}
|
||||
onChange={props.onCheckboxChange}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -74,7 +75,7 @@ const PartySegmentedControl = (props: Props) => {
|
|||
|
||||
{
|
||||
(() => {
|
||||
if (editable && props.selectedTab == GridType.Weapon) {
|
||||
if (party.editable && props.selectedTab == GridType.Weapon) {
|
||||
return extraToggle
|
||||
}
|
||||
})()
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
.ModalContainer .Modal.SearchModal {
|
||||
.Modal.Search {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 420px;
|
||||
min-width: 600px;
|
||||
padding: 0;
|
||||
|
||||
#ModalTop {
|
||||
#ModalHeader {
|
||||
background: $grey-90;
|
||||
gap: $unit;
|
||||
margin: 0;
|
||||
|
|
@ -13,12 +13,28 @@
|
|||
position: sticky;
|
||||
top: 0;
|
||||
|
||||
button {
|
||||
background: transparent;
|
||||
border: none;
|
||||
height: 42px;
|
||||
padding: 0;
|
||||
|
||||
svg {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
width: 100%;
|
||||
|
||||
.Input {
|
||||
border: 1px solid $grey-70;
|
||||
border-radius: $unit / 2;
|
||||
box-sizing: border-box;
|
||||
padding: 12px 8px;
|
||||
font-size: $font-regular;
|
||||
padding: $unit * 1.5;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
|
@ -26,13 +42,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
.SearchModal #results_container {
|
||||
.Search.Modal #results_container {
|
||||
margin: 0;
|
||||
max-height: 330px;
|
||||
padding: 0 12px 12px 12px;
|
||||
}
|
||||
|
||||
.SearchModal #NoResults {
|
||||
.Search.Modal #NoResults {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
|
@ -40,7 +56,7 @@
|
|||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.SearchModal #NoResults h2 {
|
||||
.Search.Modal #NoResults h2 {
|
||||
color: #ccc;
|
||||
font-size: $font-large;
|
||||
font-weight: 500;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import React from 'react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useSnapshot } from 'valtio'
|
||||
|
||||
import { createPortal } from 'react-dom'
|
||||
import state from '~utils/state'
|
||||
import api from '~utils/api'
|
||||
|
||||
import Modal from '~components/Modal'
|
||||
import Overlay from '~components/Overlay'
|
||||
import * as Dialog from '@radix-ui/react-dialog'
|
||||
|
||||
import CharacterResult from '~components/CharacterResult'
|
||||
import WeaponResult from '~components/WeaponResult'
|
||||
import SummonResult from '~components/SummonResult'
|
||||
|
|
@ -13,138 +14,145 @@ import './index.scss'
|
|||
import PlusIcon from '~public/icons/Add.svg'
|
||||
|
||||
interface Props {
|
||||
close: () => void
|
||||
send: (object: Character | Weapon | Summon, position: number) => any
|
||||
grid: GridArray<Character|Weapon|Summon>
|
||||
placeholderText: string
|
||||
fromPosition: number
|
||||
object: 'weapons' | 'characters' | 'summons'
|
||||
object: 'weapons' | 'characters' | 'summons',
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
interface State {
|
||||
query: string,
|
||||
results: { [key: string]: any }
|
||||
loading: boolean
|
||||
message: string
|
||||
totalResults: number
|
||||
}
|
||||
const SearchModal = (props: Props) => {
|
||||
let { grid } = useSnapshot(state)
|
||||
|
||||
class SearchModal extends React.Component<Props, State> {
|
||||
searchInput: React.RefObject<HTMLInputElement>
|
||||
let searchInput = React.createRef<HTMLInputElement>()
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
query: '',
|
||||
results: {},
|
||||
loading: false,
|
||||
message: '',
|
||||
totalResults: 0
|
||||
const [pool, setPool] = useState(Array<Character | Weapon | Summon>())
|
||||
const [open, setOpen] = useState(false)
|
||||
const [query, setQuery] = useState('')
|
||||
const [results, setResults] = useState({})
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [message, setMessage] = useState('')
|
||||
const [totalResults, setTotalResults] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
if (props.object === 'characters') {
|
||||
setPool(Object.values(grid.characters).map(o => o.character))
|
||||
} else if (props.object === 'weapons') {
|
||||
setPool(Object.values(grid.weapons.allWeapons).map(o => o.weapon))
|
||||
} else if (props.object === 'summons') {
|
||||
setPool(Object.values(grid.summons.allSummons).map(o => o.summon))
|
||||
}
|
||||
this.searchInput = React.createRef<HTMLInputElement>()
|
||||
}, [grid, props.object])
|
||||
|
||||
useEffect(() => {
|
||||
if (searchInput.current)
|
||||
searchInput.current.focus()
|
||||
}, [searchInput])
|
||||
|
||||
function filterExclusions(object: Character | Weapon | Summon) {
|
||||
if (pool[props.fromPosition] &&
|
||||
object.granblue_id === pool[props.fromPosition].granblue_id)
|
||||
return null
|
||||
else return object
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.searchInput.current) {
|
||||
this.searchInput.current.focus()
|
||||
}
|
||||
}
|
||||
function inputChanged(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
const text = event.target.value
|
||||
if (text.length) {
|
||||
setQuery(text)
|
||||
setLoading(true)
|
||||
setMessage('')
|
||||
|
||||
filterExclusions = (o: Character | Weapon | Summon) => {
|
||||
if (this.props.grid[this.props.fromPosition] &&
|
||||
o.granblue_id == this.props.grid[this.props.fromPosition].granblue_id) {
|
||||
return null
|
||||
} else return o
|
||||
}
|
||||
|
||||
fetchResults = (query: string) => {
|
||||
const excludes = Object.values(this.props.grid).filter(this.filterExclusions).map((o) => { return o.name.en }).join(',')
|
||||
|
||||
api.search(this.props.object, query, excludes)
|
||||
.then((response) => {
|
||||
const data = response.data
|
||||
const totalResults = data.length
|
||||
this.setState({
|
||||
results: data,
|
||||
totalResults: totalResults,
|
||||
loading: false
|
||||
})
|
||||
}, (error) => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
message: error
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
inputChanged = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const query = event.target.value
|
||||
if (query.length) {
|
||||
this.setState({ query, loading: true, message: '' }, () => {
|
||||
this.fetchResults(query)
|
||||
})
|
||||
if (text.length > 2)
|
||||
fetchResults()
|
||||
} else {
|
||||
this.setState({ query, results: {}, message: '' })
|
||||
setQuery('')
|
||||
setResults({})
|
||||
setMessage('')
|
||||
}
|
||||
}
|
||||
|
||||
function fetchResults() {
|
||||
const excludes = Object.values(pool)
|
||||
.filter(filterExclusions)
|
||||
.map((o) => { return o.name.en }).join(',')
|
||||
|
||||
sendData = (result: Character | Weapon | Summon) => {
|
||||
this.props.send(result, this.props.fromPosition)
|
||||
this.props.close()
|
||||
api.search(props.object, query, excludes)
|
||||
.then(response => {
|
||||
setResults(response.data)
|
||||
setTotalResults(response.data.length)
|
||||
setLoading(false)
|
||||
})
|
||||
.catch(error => {
|
||||
setMessage(error)
|
||||
setLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
renderSearchResults = () => {
|
||||
const { results } = this.state
|
||||
|
||||
switch(this.props.object) {
|
||||
function sendData(result: Character | Weapon | Summon) {
|
||||
props.send(result, props.fromPosition)
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
function renderResults() {
|
||||
switch(props.object) {
|
||||
case 'weapons':
|
||||
return this.renderWeaponSearchResults(results)
|
||||
|
||||
return renderWeaponSearchResults(results)
|
||||
break
|
||||
case 'summons':
|
||||
return this.renderSummonSearchResults(results)
|
||||
|
||||
return renderSummonSearchResults(results)
|
||||
break
|
||||
case 'characters':
|
||||
return this.renderCharacterSearchResults(results)
|
||||
return renderCharacterSearchResults(results)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
renderWeaponSearchResults = (results: { [key: string]: any }) => {
|
||||
return (
|
||||
<ul id="results_container">
|
||||
{ results.map( (result: Weapon) => {
|
||||
return <WeaponResult key={result.id} data={result} onClick={() => { this.sendData(result) }} />
|
||||
})}
|
||||
</ul>
|
||||
)
|
||||
function renderWeaponSearchResults(results: { [key: string]: any }) {
|
||||
const elements = results.map((result: Weapon) => {
|
||||
return <WeaponResult
|
||||
key={result.id}
|
||||
data={result}
|
||||
onClick={() => { sendData(result) }}
|
||||
/>
|
||||
})
|
||||
|
||||
return (<ul id="results_container">{elements}</ul>)
|
||||
}
|
||||
|
||||
renderSummonSearchResults = (results: { [key: string]: any }) => {
|
||||
return (
|
||||
<ul id="results_container">
|
||||
{ results.map( (result: Summon) => {
|
||||
return <SummonResult key={result.id} data={result} onClick={() => { this.sendData(result) }} />
|
||||
})}
|
||||
</ul>
|
||||
)
|
||||
function renderSummonSearchResults(results: { [key: string]: any }) {
|
||||
const elements = results.map((result: Summon) => {
|
||||
return <SummonResult
|
||||
key={result.id}
|
||||
data={result}
|
||||
onClick={() => { sendData(result) }}
|
||||
/>
|
||||
})
|
||||
|
||||
return (<ul id="results_container">{elements}</ul>)
|
||||
}
|
||||
|
||||
renderCharacterSearchResults = (results: { [key: string]: any }) => {
|
||||
return (
|
||||
<ul id="results_container">
|
||||
{ results.map( (result: Character) => {
|
||||
return <CharacterResult key={result.id} data={result} onClick={() => { this.sendData(result) }} />
|
||||
})}
|
||||
</ul>
|
||||
)
|
||||
function renderCharacterSearchResults(results: { [key: string]: any }) {
|
||||
const elements = results.map((result: Character) => {
|
||||
return <CharacterResult
|
||||
key={result.id}
|
||||
data={result}
|
||||
onClick={() => { sendData(result) }}
|
||||
/>
|
||||
})
|
||||
|
||||
return (<ul id="results_container">{elements}</ul>)
|
||||
}
|
||||
|
||||
renderEmptyState = () => {
|
||||
function renderEmptyState() {
|
||||
let string = ''
|
||||
|
||||
if (this.state.query === '') {
|
||||
string = `No ${this.props.object}`
|
||||
if (query === '') {
|
||||
string = `No ${props.object}`
|
||||
} else if (query.length < 3) {
|
||||
string = `Type at least 3 characters`
|
||||
} else {
|
||||
string = `No results found for '${this.state.query}'`
|
||||
string = `No results found for '${query}'`
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -153,48 +161,46 @@ class SearchModal extends React.Component<Props, State> {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { query, loading } = this.state
|
||||
|
||||
let content: JSX.Element
|
||||
if (Object.entries(this.state.results).length == 0) {
|
||||
content = this.renderEmptyState()
|
||||
} else {
|
||||
content = this.renderSearchResults()
|
||||
}
|
||||
|
||||
return (
|
||||
createPortal(
|
||||
<div>
|
||||
<div className="ModalContainer">
|
||||
<div className="Modal SearchModal" key="search_modal">
|
||||
<div id="ModalTop">
|
||||
<label className="search_label" htmlFor="search_input">
|
||||
<input
|
||||
autoComplete="off"
|
||||
type="text"
|
||||
name="query"
|
||||
className="Input"
|
||||
id="search_input"
|
||||
ref={this.searchInput}
|
||||
value={query}
|
||||
placeholder={this.props.placeholderText}
|
||||
onChange={this.inputChanged}
|
||||
/>
|
||||
</label>
|
||||
<PlusIcon onClick={this.props.close} />
|
||||
</div>
|
||||
|
||||
{content}
|
||||
</div>
|
||||
</div>
|
||||
<Overlay onClick={this.props.close} />
|
||||
</div>,
|
||||
document.body
|
||||
)
|
||||
)
|
||||
|
||||
function resetAndClose() {
|
||||
setQuery('')
|
||||
setResults({})
|
||||
setOpen(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog.Root open={open} onOpenChange={setOpen}>
|
||||
<Dialog.Trigger asChild>
|
||||
{props.children}
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<div className="ModalContainer">
|
||||
<Dialog.Content className="Search Modal">
|
||||
<div id="ModalHeader">
|
||||
<label className="search_label" htmlFor="search_input">
|
||||
<input
|
||||
autoComplete="off"
|
||||
type="text"
|
||||
name="query"
|
||||
className="Input"
|
||||
id="search_input"
|
||||
ref={searchInput}
|
||||
value={query}
|
||||
placeholder={props.placeholderText}
|
||||
onChange={inputChanged}
|
||||
/>
|
||||
</label>
|
||||
<Dialog.Close onClick={resetAndClose}>
|
||||
<PlusIcon />
|
||||
</Dialog.Close>
|
||||
</div>
|
||||
{ ((Object.entries(results).length == 0) ? renderEmptyState() : renderResults()) }
|
||||
</Dialog.Content>
|
||||
</div>
|
||||
<Dialog.Overlay className="Overlay" />
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export default SearchModal
|
||||
|
|
@ -1,15 +1,14 @@
|
|||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
||||
import { useCookies } from 'react-cookie'
|
||||
import { useSnapshot } from 'valtio'
|
||||
|
||||
import { useModal as useModal } from '~utils/useModal'
|
||||
import state from '~utils/state'
|
||||
|
||||
import { AxiosResponse } from 'axios'
|
||||
import debounce from 'lodash.debounce'
|
||||
|
||||
import AppContext from '~context/AppContext'
|
||||
import PartyContext from '~context/PartyContext'
|
||||
|
||||
import SearchModal from '~components/SearchModal'
|
||||
import WeaponUnit from '~components/WeaponUnit'
|
||||
import ExtraWeapons from '~components/ExtraWeapons'
|
||||
|
||||
|
|
@ -36,20 +35,11 @@ const WeaponGrid = (props: Props) => {
|
|||
} : {}
|
||||
|
||||
// Set up state for view management
|
||||
const { party, grid } = useSnapshot(state)
|
||||
|
||||
const [slug, setSlug] = useState()
|
||||
const [found, setFound] = useState(false)
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
// Set up the party context
|
||||
const { setEditable: setAppEditable } = useContext(AppContext)
|
||||
const { id, setId } = useContext(PartyContext)
|
||||
const { slug, setSlug } = useContext(PartyContext)
|
||||
const { editable, setEditable } = useContext(PartyContext)
|
||||
const { hasExtra, setHasExtra } = useContext(PartyContext)
|
||||
const { setElement } = useContext(PartyContext)
|
||||
|
||||
// Set up states for Grid data
|
||||
const [weapons, setWeapons] = useState<GridArray<GridWeapon>>({})
|
||||
const [mainWeapon, setMainWeapon] = useState<GridWeapon>()
|
||||
|
||||
// Set up states for Search
|
||||
const { open, openModal, closeModal } = useModal()
|
||||
|
|
@ -66,28 +56,27 @@ const WeaponGrid = (props: Props) => {
|
|||
const shortcode = (props.slug) ? props.slug : slug
|
||||
if (shortcode) fetchGrid(shortcode)
|
||||
else {
|
||||
setEditable(true)
|
||||
setAppEditable(true)
|
||||
state.party.editable = true
|
||||
}
|
||||
}, [slug, props.slug])
|
||||
|
||||
// Initialize an array of current uncap values for each weapon
|
||||
useEffect(() => {
|
||||
let initialPreviousUncapValues: {[key: number]: number} = {}
|
||||
if (mainWeapon) initialPreviousUncapValues[-1] = mainWeapon.uncap_level
|
||||
Object.values(weapons).map(o => initialPreviousUncapValues[o.position] = o.uncap_level)
|
||||
if (state.grid.weapons.mainWeapon) initialPreviousUncapValues[-1] = state.grid.weapons.mainWeapon.uncap_level
|
||||
Object.values(state.grid.weapons.allWeapons).map(o => initialPreviousUncapValues[o.position] = o.uncap_level)
|
||||
setPreviousUncapValues(initialPreviousUncapValues)
|
||||
}, [mainWeapon, weapons])
|
||||
}, [state.grid.weapons.mainWeapon, state.grid.weapons.allWeapons])
|
||||
|
||||
// Update search grid whenever weapons or the mainhand are updated
|
||||
useEffect(() => {
|
||||
let newSearchGrid = Object.values(weapons).map((o) => o.weapon)
|
||||
let newSearchGrid = Object.values(grid.weapons.allWeapons).map((o) => o.weapon)
|
||||
|
||||
if (mainWeapon)
|
||||
newSearchGrid.unshift(mainWeapon.weapon)
|
||||
if (state.grid.weapons.mainWeapon)
|
||||
newSearchGrid.unshift(state.grid.weapons.mainWeapon.weapon)
|
||||
|
||||
setSearchGrid(newSearchGrid)
|
||||
}, [weapons, mainWeapon])
|
||||
}, [state.grid.weapons.mainWeapon, state.grid.weapons.allWeapons])
|
||||
|
||||
// Methods: Fetching an object from the server
|
||||
async function fetchGrid(shortcode: string) {
|
||||
|
|
@ -105,13 +94,13 @@ const WeaponGrid = (props: Props) => {
|
|||
const loggedInUser = (cookies.user) ? cookies.user.user_id : ''
|
||||
|
||||
if (partyUser != undefined && loggedInUser != undefined && partyUser === loggedInUser) {
|
||||
setEditable(true)
|
||||
setAppEditable(true)
|
||||
state.party.editable = true
|
||||
}
|
||||
|
||||
// Store the important party and state-keeping values
|
||||
setId(party.id)
|
||||
setHasExtra(party.is_extra)
|
||||
state.party.id = party.id
|
||||
state.party.extra = party.is_extra
|
||||
|
||||
setFound(true)
|
||||
setLoading(false)
|
||||
|
||||
|
|
@ -135,14 +124,12 @@ const WeaponGrid = (props: Props) => {
|
|||
|
||||
list.forEach((object: GridWeapon) => {
|
||||
if (object.mainhand) {
|
||||
setMainWeapon(object)
|
||||
setElement(object.weapon.element)
|
||||
state.grid.weapons.mainWeapon = object
|
||||
state.party.element = object.weapon.element
|
||||
} else if (!object.mainhand && object.position != null) {
|
||||
weapons[object.position] = object
|
||||
state.grid.weapons.allWeapons[object.position] = object
|
||||
}
|
||||
})
|
||||
|
||||
setWeapons(weapons)
|
||||
}
|
||||
|
||||
// Methods: Adding an object from search
|
||||
|
|
@ -153,21 +140,23 @@ const WeaponGrid = (props: Props) => {
|
|||
|
||||
function receiveWeaponFromSearch(object: Character | Weapon | Summon, position: number) {
|
||||
const weapon = object as Weapon
|
||||
setElement(weapon.element)
|
||||
if (position == 1)
|
||||
state.party.element = weapon.element
|
||||
|
||||
if (!id) {
|
||||
props.createParty(hasExtra)
|
||||
if (!party.id) {
|
||||
props.createParty(party.extra)
|
||||
.then(response => {
|
||||
const party = response.data.party
|
||||
setId(party.id)
|
||||
state.party.id = party.id
|
||||
setSlug(party.shortcode)
|
||||
|
||||
if (props.pushHistory) props.pushHistory(`/p/${party.shortcode}`)
|
||||
|
||||
saveWeapon(party.id, weapon, position)
|
||||
.then(response => storeGridWeapon(response.data.grid_weapon))
|
||||
})
|
||||
} else {
|
||||
saveWeapon(id, weapon, position)
|
||||
saveWeapon(party.id, weapon, position)
|
||||
.then(response => storeGridWeapon(response.data.grid_weapon))
|
||||
}
|
||||
}
|
||||
|
|
@ -190,12 +179,11 @@ const WeaponGrid = (props: Props) => {
|
|||
|
||||
function storeGridWeapon(gridWeapon: GridWeapon) {
|
||||
if (gridWeapon.position == -1) {
|
||||
setMainWeapon(gridWeapon)
|
||||
state.grid.weapons.mainWeapon = gridWeapon
|
||||
state.party.element = gridWeapon.weapon.element
|
||||
} else {
|
||||
// Store the grid unit at the correct position
|
||||
let newWeapons = Object.assign({}, weapons)
|
||||
newWeapons[gridWeapon.position] = gridWeapon
|
||||
setWeapons(newWeapons)
|
||||
state.grid.weapons.allWeapons[gridWeapon.position] = gridWeapon
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -241,32 +229,30 @@ const WeaponGrid = (props: Props) => {
|
|||
)
|
||||
|
||||
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)
|
||||
}
|
||||
if (state.grid.weapons.mainWeapon && position == -1)
|
||||
state.grid.weapons.mainWeapon.uncap_level = uncapLevel
|
||||
else
|
||||
state.grid.weapons.allWeapons[position].uncap_level = uncapLevel
|
||||
}
|
||||
|
||||
function storePreviousUncapValue(position: number) {
|
||||
// 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
|
||||
newPreviousValues[position] = (state.grid.weapons.mainWeapon && position == -1) ?
|
||||
state.grid.weapons.mainWeapon.uncap_level : state.grid.weapons.allWeapons[position].uncap_level
|
||||
setPreviousUncapValues(newPreviousValues)
|
||||
}
|
||||
|
||||
// Render: JSX components
|
||||
const mainhandElement = (
|
||||
<WeaponUnit
|
||||
gridWeapon={mainWeapon}
|
||||
editable={editable}
|
||||
gridWeapon={grid.weapons.mainWeapon}
|
||||
editable={party.editable}
|
||||
key="grid_mainhand"
|
||||
position={-1}
|
||||
unitType={0}
|
||||
onClick={() => { openSearchModal(-1) }}
|
||||
updateObject={receiveWeaponFromSearch}
|
||||
updateUncap={initiateUncapUpdate}
|
||||
/>
|
||||
)
|
||||
|
|
@ -276,11 +262,12 @@ const WeaponGrid = (props: Props) => {
|
|||
return (
|
||||
<li key={`grid_unit_${i}`} >
|
||||
<WeaponUnit
|
||||
gridWeapon={weapons[i]}
|
||||
editable={editable}
|
||||
gridWeapon={grid.weapons.allWeapons[i]}
|
||||
editable={party.editable}
|
||||
position={i}
|
||||
unitType={1}
|
||||
onClick={() => { openSearchModal(i) }}
|
||||
updateObject={receiveWeaponFromSearch}
|
||||
updateUncap={initiateUncapUpdate}
|
||||
/>
|
||||
</li>
|
||||
|
|
@ -290,10 +277,11 @@ const WeaponGrid = (props: Props) => {
|
|||
|
||||
const extraGridElement = (
|
||||
<ExtraWeapons
|
||||
grid={weapons}
|
||||
editable={editable}
|
||||
grid={state.grid.weapons.allWeapons}
|
||||
editable={party.editable}
|
||||
offset={numWeapons}
|
||||
onClick={openSearchModal}
|
||||
updateObject={receiveWeaponFromSearch}
|
||||
updateUncap={initiateUncapUpdate}
|
||||
/>
|
||||
)
|
||||
|
|
@ -305,18 +293,7 @@ const WeaponGrid = (props: Props) => {
|
|||
<ul className="grid_weapons">{ weaponGridElement }</ul>
|
||||
</div>
|
||||
|
||||
{ (() => { return (hasExtra) ? extraGridElement : '' })() }
|
||||
|
||||
{open ? (
|
||||
<SearchModal
|
||||
grid={searchGrid}
|
||||
close={closeModal}
|
||||
send={receiveWeaponFromSearch}
|
||||
fromPosition={itemPositionForSearch}
|
||||
object="weapons"
|
||||
placeholderText="Search for a weapon..."
|
||||
/>
|
||||
) : null}
|
||||
{ (() => { return (party.extra) ? extraGridElement : '' })() }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import UncapIndicator from '~components/UncapIndicator'
|
|||
import PlusIcon from '~public/icons/Add.svg'
|
||||
|
||||
import './index.scss'
|
||||
import SearchModal from '~components/SearchModal'
|
||||
|
||||
interface Props {
|
||||
gridWeapon: GridWeapon | undefined
|
||||
|
|
@ -12,6 +13,7 @@ interface Props {
|
|||
position: number
|
||||
editable: boolean
|
||||
onClick: () => void
|
||||
updateObject: (object: Character | Weapon | Summon, position: number) => void
|
||||
updateUncap: (id: string, position: number, uncap: number) => void
|
||||
}
|
||||
|
||||
|
|
@ -55,10 +57,17 @@ const WeaponUnit = (props: Props) => {
|
|||
return (
|
||||
<div>
|
||||
<div className={classes}>
|
||||
<div className="WeaponImage" onClick={ (props.editable) ? props.onClick : () => {} }>
|
||||
<img alt={weapon?.name.en} className="grid_image" src={imageUrl} />
|
||||
{ (props.editable) ? <span className='icon'><PlusIcon /></span> : '' }
|
||||
</div>
|
||||
<SearchModal
|
||||
placeholderText="Search for a weapon..."
|
||||
fromPosition={props.position}
|
||||
object="weapons"
|
||||
send={props.updateObject}>
|
||||
<div className="WeaponImage" onClick={ (props.editable) ? props.onClick : () => {} }>
|
||||
<img alt={weapon?.name.en} className="grid_image" src={imageUrl} />
|
||||
{ (props.editable) ? <span className='icon'><PlusIcon /></span> : '' }
|
||||
</div>
|
||||
</SearchModal>
|
||||
|
||||
{ (gridWeapon) ?
|
||||
<UncapIndicator
|
||||
type="weapon"
|
||||
|
|
|
|||
Loading…
Reference in a new issue