WIP commit

This commit is contained in:
Justin Edmund 2020-10-18 22:10:17 -07:00
parent 6b36928b5f
commit a9c934cb2c
20 changed files with 609 additions and 220 deletions

View file

@ -26,7 +26,7 @@ h1 {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 24px; gap: 24px;
margin-top: 96px; margin-top: 24px;
} }
#NotFound { #NotFound {

View file

@ -3,14 +3,23 @@ import CharacterUnit from '~components/CharacterUnit'
import './index.css' import './index.css'
export enum GridType {
Class,
Character,
Weapon,
Summon
}
interface Props { interface Props {
editable: boolean editable: boolean
exists: boolean
onSelect: (type: GridType, character: Character, position: number) => void
} }
const CharacterGrid = (props: Props) => { const CharacterGrid = (props: Props) => {
const numCharacters: number = 5 const numCharacters: number = 5
const [characters, setCharacters] = useState<GridArray>({}) const [characters, setCharacters] = useState<GridArray<Character>>({})
const [partyId, setPartyId] = useState('') const [partyId, setPartyId] = useState('')
return ( return (

View file

@ -15,7 +15,7 @@ const GridRep = (props: Props) => {
const numWeapons: number = 9 const numWeapons: number = 9
const [mainhand, setMainhand] = useState<Weapon>() const [mainhand, setMainhand] = useState<Weapon>()
const [weapons, setWeapons] = useState<GridArray>({}) const [weapons, setWeapons] = useState<GridArray<Weapon>>({})
useEffect(() => { useEffect(() => {
configure() configure()

View file

View file

@ -0,0 +1,227 @@
import React, { useEffect, useState } from 'react'
import { useCookies } from 'react-cookie'
import api from '~utils/api'
// UI Elements
import PartySegmentedControl from '~components/PartySegmentedControl'
// Grids
import WeaponGrid from '~components/WeaponGrid'
import SummonGrid from '~components/SummonGrid'
import CharacterGrid from '~components/CharacterGrid'
// GridType
export enum GridType {
Class,
Character,
Weapon,
Summon
}
import './index.css'
interface Props {
editable: boolean
exists: boolean
pushHistory: (path: string) => void
}
const Party = (props: Props) => {
const [cookies, setCookie] = useCookies(['user'])
const headers = (cookies.user) ? {
headers: {
'Authorization': `Bearer ${cookies.user.access_token}`
}
} : {}
// Grid data
const [weapons, setWeapons] = useState<GridArray<Weapon>>({})
const [summons, setSummons] = useState<GridArray<Summon>>({})
const [mainWeapon, setMainWeapon] = useState<Weapon>()
const [mainSummon, setMainSummon] = useState<Summon>()
const [friendSummon, setFriendSummon] = useState<Summon>()
const weaponGrid = (
<WeaponGrid
userId={cookies.user ? cookies.user.userId : ''}
mainhand={mainWeapon}
grid={weapons}
editable={props.editable}
exists={props.exists}
onSelect={itemSelected}
pushHistory={props.pushHistory}
/>
)
const summonGrid = (
<SummonGrid
userId={cookies.user ? cookies.user.userId : ''}
main={mainSummon}
friend={friendSummon}
grid={summons}
editable={props.editable}
exists={props.exists}
onSelect={itemSelected}
pushHistory={props.pushHistory}
/>
)
const characterGrid = (
<CharacterGrid
editable={props.editable}
exists={props.exists}
onSelect={saveCharacter}
/>
)
const [currentGrid, setCurrentGrid] = useState<JSX.Element>(weaponGrid)
const [currentTab, setCurrentTab] = useState<GridType>(GridType.Weapon)
const [partyId, setPartyId] = useState('')
function segmentClicked(event: React.ChangeEvent<HTMLInputElement>) {
switch(event.target.value) {
case 'characters':
setCurrentTab(GridType.Character)
setCurrentGrid(characterGrid)
break
case 'weapons':
setCurrentTab(GridType.Weapon)
setCurrentGrid(weaponGrid)
break
case 'summons':
setCurrentTab(GridType.Summon)
setCurrentGrid(summonGrid)
break
default:
break
}
}
function itemSelected(type: GridType, item: Character | Weapon | Summon, position: number) {
if (!partyId) {
createParty()
.then(response => {
return response.data.party
})
.then(party => {
if (props.pushHistory) {
props.pushHistory(`/p/${party.shortcode}`)
}
return party.id
})
.then(partyId => {
setPartyId(partyId)
save(partyId, type, item, position)
})
} else {
save(partyId, type, item, position)
}
}
async function createParty() {
const body = (cookies.user.userId === undefined) ? {} : {
party: {
user_id: cookies.user.userId
}
}
return await api.endpoints.parties.create(body, headers)
}
function save(partyId: string, type: GridType, item: Character | Weapon | Summon, position: number) {
switch(type) {
case GridType.Class:
saveClass()
break
case GridType.Character:
saveCharacter(item as Character, position, partyId)
break
case GridType.Weapon:
const weapon = item as Weapon
saveWeapon(weapon, position, partyId)
.then(() => {
storeWeapon(weapon, position)
})
break
case GridType.Summon:
const summon = item as Summon
saveSummon(summon, position, partyId)
.then(() => {
storeSummon(summon, position)
})
break
}
}
function storeWeapon(weapon: Weapon, position: number) {
if (position == -1) {
setMainWeapon(weapon)
} else {
// Store the grid unit weapon at the correct position
let newWeapons = Object.assign({}, weapons)
newWeapons[position] = weapon
setWeapons(newWeapons)
}
}
async function saveWeapon(weapon: Weapon, position: number, party: string) {
await api.endpoints.weapons.create({
'weapon': {
'party_id': party,
'weapon_id': weapon.id,
'position': position,
'mainhand': (position == -1)
}
}, headers)
}
function storeSummon(summon: Summon, position: number) {
if (position == -1) {
setMainSummon(summon)
} else if (position == 4) {
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) {
await api.endpoints.summons.create({
'summon': {
'party_id': party,
'summon_id': summon.id,
'position': position,
'main': (position == -1),
'friend': (position == 4)
}
}, headers)
}
function saveCharacter(character: Character, position: number, party: string) {
// TODO: Implement this
}
function saveClass() {
// TODO: Implement this
}
return (
<div>
<PartySegmentedControl
selectedTab={currentTab}
onClick={segmentClicked}
/>
{currentGrid}
</div>
)
}
export default Party

View file

@ -2,8 +2,16 @@ import React from 'react'
import SegmentedControl from '~components/SegmentedControl' import SegmentedControl from '~components/SegmentedControl'
import Segment from '~components/Segment' import Segment from '~components/Segment'
// GridType
export enum GridType {
Class,
Character,
Weapon,
Summon
}
interface Props { interface Props {
selectedTab: string selectedTab: GridType
onClick: (event: React.ChangeEvent<HTMLInputElement>) => void onClick: (event: React.ChangeEvent<HTMLInputElement>) => void
} }
@ -11,30 +19,31 @@ const PartySegmentedControl = (props: Props) => {
return ( return (
<div> <div>
<SegmentedControl> <SegmentedControl>
<Segment {/* <Segment
groupName="grid" groupName="grid"
name="class" name="class"
selected={props.selectedTab === 'class'} selected={props.selectedTab === GridType.Class}
onClick={props.onClick} onClick={props.onClick}
>Class</Segment> >Class</Segment> */}
<Segment <Segment
groupName="grid" groupName="grid"
name="characters" name="characters"
selected={props.selectedTab === 'characters'} selected={props.selectedTab === GridType.Character}
onClick={props.onClick} onClick={props.onClick}
>Characters</Segment> >Characters</Segment>
<Segment <Segment
groupName="grid" groupName="grid"
name="weapons" name="weapons"
selected={props.selectedTab === 'weapons'} selected={props.selectedTab === GridType.Weapon}
onClick={props.onClick} onClick={props.onClick}
>Weapons</Segment> >Weapons</Segment>
<Segment <Segment
groupName="grid" groupName="grid"
name="summons" name="summons"
selected={props.selectedTab === 'summons'} selected={props.selectedTab === GridType.Summon}
onClick={props.onClick} onClick={props.onClick}
>Summons</Segment> >Summons</Segment>
</SegmentedControl> </SegmentedControl>

View file

@ -5,14 +5,16 @@ import api from '~utils/api'
import Modal from '~components/Modal' import Modal from '~components/Modal'
import Overlay from '~components/Overlay' import Overlay from '~components/Overlay'
import WeaponResult from '~components/WeaponResult' import WeaponResult from '~components/WeaponResult'
import SummonResult from '~components/SummonResult'
import './index.css' import './index.css'
interface Props { interface Props {
close: () => void close: () => void
send: (weapon: Weapon, position: number) => any send: (object: Weapon | Summon, position: number) => any
placeholderText: string placeholderText: string
fromPosition: number fromPosition: number
object: 'weapons' | 'characters' | 'summons'
} }
interface State { interface State {
@ -45,7 +47,7 @@ class SearchModal extends React.Component<Props, State> {
} }
fetchResults = (query: string) => { fetchResults = (query: string) => {
api.search(query) api.search(this.props.object, query)
.then((response) => { .then((response) => {
const data = response.data const data = response.data
const totalResults = data.length const totalResults = data.length
@ -73,13 +75,27 @@ class SearchModal extends React.Component<Props, State> {
} }
} }
sendData = (result: Weapon) => { sendData = (result: Weapon | Summon) => {
this.props.send(result, this.props.fromPosition) this.props.send(result, this.props.fromPosition)
this.props.close() this.props.close()
} }
renderSearchResults = () => { renderSearchResults = () => {
const { results } = this.state const { results } = this.state
switch(this.props.object) {
case 'weapons':
return this.renderWeaponSearchResults(results)
case 'summons':
return this.renderSummonSearchResults(results)
case 'characters':
return (<div />)
}
}
renderWeaponSearchResults = (results: { [key: string]: any }) => {
return ( return (
<ul id="results_container"> <ul id="results_container">
{ results.map( (result: Weapon) => { { results.map( (result: Weapon) => {
@ -89,11 +105,21 @@ class SearchModal extends React.Component<Props, State> {
) )
} }
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>
)
}
renderEmptyState = () => { renderEmptyState = () => {
let string = '' let string = ''
if (this.state.query === '') { if (this.state.query === '') {
string = 'No weapons' string = `No ${this.props.object}`
} else { } else {
string = `No results found for '${this.state.query}'` string = `No results found for '${this.state.query}'`
} }

View file

@ -1,6 +1,7 @@
.SegmentedControlWrapper { .SegmentedControlWrapper {
display: flex; display: flex;
justify-content: center; justify-content: center;
margin-bottom: 24px;
} }
.SegmentedControl { .SegmentedControl {

View file

@ -1,29 +1,49 @@
import React, { useState } from 'react' import React, { useEffect, useState } from 'react'
import { useCookies } from 'react-cookie'
import api from '~utils/api'
import SummonUnit from '~components/SummonUnit' import SummonUnit from '~components/SummonUnit'
import './index.css' import './index.css'
// GridType
export enum GridType {
Class,
Character,
Weapon,
Summon
}
// Props
interface Props { interface Props {
userId?: string
partyId?: string
main?: Summon | undefined
friend?: Summon | undefined
grid: GridArray<Summon>
editable: boolean editable: boolean
exists: boolean
found?: boolean
onSelect: (type: GridType, summon: Summon, position: number) => void
pushHistory?: (path: string) => void
} }
const SummonGrid = (props: Props) => { const SummonGrid = (props: Props) => {
const numSummons: number = 4 const numSummons: number = 4
const [mainSummon, setMainSummon] = useState<Summon>() function receiveSummon(summon: Summon, position: number) {
const [friendSummon, setFriendSummon] = useState<Summon>() props.onSelect(GridType.Summon, summon, position)
const [summons, setSummons] = useState<GridArray>({}) }
const [partyId, setPartyId] = useState('')
return ( return (
<div className="SummonGrid"> <div className="SummonGrid">
<SummonUnit <SummonUnit
editable={props.editable} editable={props.editable}
key="grid_main_summon" key="grid_main_summon"
onReceiveData={() => {}} onReceiveData={receiveSummon}
position={-1} position={-1}
unitType={0} unitType={0}
summon={mainSummon} summon={props.main}
/> />
<ul id="grid_summons"> <ul id="grid_summons">
@ -33,10 +53,10 @@ const SummonGrid = (props: Props) => {
<li key={`grid_unit_${i}`} > <li key={`grid_unit_${i}`} >
<SummonUnit <SummonUnit
editable={props.editable} editable={props.editable}
onReceiveData={() => {}} onReceiveData={receiveSummon}
position={i} position={i}
unitType={1} unitType={1}
summon={summons[i]} summon={props.grid[i]}
/> />
</li> </li>
) )
@ -47,10 +67,10 @@ const SummonGrid = (props: Props) => {
<SummonUnit <SummonUnit
editable={props.editable} editable={props.editable}
key="grid_friend_summon" key="grid_friend_summon"
onReceiveData={() => {}} onReceiveData={receiveSummon}
position={-1} position={4}
unitType={2} unitType={2}
summon={friendSummon} summon={props.friend}
/> />
</div> </div>
) )

View file

@ -0,0 +1,48 @@
.SummonResult {
-webkit-font-smoothing: antialiased;
border-radius: 6px;
display: flex;
font-family: -apple-system, "Helvetica Neue", "Lucida Grande";
gap: 8px;
padding: 12px;
}
.SummonResult img {
background: #e9e9e9;
border-radius: 6px;
display: inline-block;
height: 72px;
width: 120px;
}
.SummonResult h5 {
color: #555;
display: inline-block;
font-size: 18px;
font-weight: 500;
margin: 2px 4px 4px 0;
}
.SummonResult .WeaponLabelIcon {
margin-right: 4px;
}
.SummonResult .stars {
display: inline-block;
color: #FFA15E;
font-size: 21px;
}
.SummonResult .stars > span {
color: #65DAFF;
}
.SummonResult:hover {
background: #e9e9e9;
cursor: pointer;
}
.SummonResult:hover .image_placeholder {
background: #dadada;
}

View file

@ -0,0 +1,41 @@
import React from 'react'
import WeaponLabelIcon from '~components/WeaponLabelIcon'
import gridImages from '../../images/summon-grid/*.jpg'
import './index.css'
interface Props {
data: Summon
onClick: () => void
}
const Element = ['null', 'wind', 'fire', 'water', 'earth', 'dark', 'light']
class SummonResult extends React.Component<Props> {
render() {
let imgSrc
const summon = this.props.data
if (process.env.NODE_ENV === 'development') {
imgSrc = gridImages[summon.granblue_id]
} else if (process.env.NODE_ENV === 'production') {
imgSrc = `${process.env.SIERO_IMG_URL}/summon-grid/${summon.granblue_id}.jpg`
}
return (
<li className="SummonResult" onClick={this.props.onClick}>
<img alt={summon.name.en} src={imgSrc} />
<div>
<div>
<h5>{summon.name.en}</h5>
<div className="stars">{(summon.uncap.flb) ? <span></span> : ''}{(summon.uncap.ulb) ? <span></span> : ''}</div>
</div>
<WeaponLabelIcon labelType={Element[summon.element]} />
</div>
</li>
)
}
}
export default SummonResult

View file

@ -1,11 +1,16 @@
import React, { useState } from 'react' import React, { useEffect, useState } from 'react'
import classnames from 'classnames' import classnames from 'classnames'
import { useModal as useModal } from '~utils/useModal'
import SearchModal from '~components/SearchModal'
import UncapIndicator from '~components/UncapIndicator' import UncapIndicator from '~components/UncapIndicator'
import './index.css' import mainImages from '../../images/summon-main/*.jpg'
import gridImages from '../../images/summon-grid/*.jpg'
import Plus from '../../../assets/plus.svg' import Plus from '../../../assets/plus.svg'
import './index.css'
interface Props { interface Props {
onReceiveData: (summon: Summon, position: number) => void onReceiveData: (summon: Summon, position: number) => void
summon: Summon | undefined summon: Summon | undefined
@ -15,8 +20,9 @@ interface Props {
} }
const SummonUnit = (props: Props) => { const SummonUnit = (props: Props) => {
const numSummons: number = 4 const [imageUrl, setImageUrl] = useState('')
const openModal = () => {}
const { open, openModal, closeModal } = useModal()
const openModalIfEditable = (props.editable) ? openModal : () => {} const openModalIfEditable = (props.editable) ? openModal : () => {}
@ -31,10 +37,53 @@ const SummonUnit = (props: Props) => {
const summon = props.summon const summon = props.summon
useEffect(() => {
generateImageUrl()
})
function generateImageUrl() {
let imgSrc
if (props.summon) {
const summon = props.summon!
// Generate the correct source for the weapon
if (process.env.NODE_ENV === 'development') {
if (props.unitType == 0 || props.unitType == 2)
imgSrc = mainImages[summon.granblue_id]
else
imgSrc = gridImages[summon.granblue_id]
} else if (process.env.NODE_ENV === 'production') {
if (props.unitType == 0 || props.unitType == 2)
imgSrc = `${process.env.SIERO_IMG_URL}/summon-main/${summon.granblue_id}.jpg`
else
imgSrc = `${process.env.SIERO_IMG_URL}/summon-grid/${summon.granblue_id}.jpg`
}
}
setImageUrl(imgSrc)
}
function sendData(object: Weapon | Summon, position: number) {
if (isSummon(object)) {
props.onReceiveData(object, position)
}
}
function isSummon(object: Weapon | Summon): object is Summon {
// There aren't really any unique fields here
return (object as Summon).granblue_id !== undefined
}
return ( return (
<div> <div>
<div className={classes} onClick={openModalIfEditable}> <div className={classes} onClick={openModalIfEditable}>
<div className="SummonImage"> <div className="SummonImage">
{
(imageUrl != '')
? <img className="grid_image" src={imageUrl} />
: <img className="grid_image" />
}
{ (props.editable) ? <span className='icon'><Plus /></span> : '' } { (props.editable) ? <span className='icon'><Plus /></span> : '' }
</div> </div>
<UncapIndicator <UncapIndicator
@ -44,6 +93,15 @@ const SummonUnit = (props: Props) => {
/> />
<h3 className="SummonName">{summon?.name.en}</h3> <h3 className="SummonName">{summon?.name.en}</h3>
</div> </div>
{open ? (
<SearchModal
close={closeModal}
send={sendData}
fromPosition={props.position}
object="summons"
placeholderText="Search for a summon..."
/>
) : null}
</div> </div>
) )
} }

View file

@ -3,109 +3,35 @@ import { useCookies } from 'react-cookie'
import api from '~utils/api' import api from '~utils/api'
import WeaponUnit from '~components/WeaponUnit' import WeaponUnit from '~components/WeaponUnit'
import Button from '~components/Button'
import './index.css' import './index.css'
// GridType
export enum GridType {
Class,
Character,
Weapon,
Summon
}
// Props
interface Props { interface Props {
userId?: string userId?: string
partyId?: string partyId?: string
mainhand?: Weapon | undefined mainhand?: Weapon | undefined
grid?: GridArray grid: GridArray<Weapon>
editable: boolean editable: boolean
exists: boolean exists: boolean
found?: boolean found?: boolean
onSelect: (type: GridType, weapon: Weapon, position: number) => void
pushHistory?: (path: string) => void pushHistory?: (path: string) => void
} }
type GridArray = { [key: number]: Weapon }
const WeaponGrid = (props: Props) => { const WeaponGrid = (props: Props) => {
const numWeapons: number = 9 const numWeapons: number = 9
const [mainhand, setMainhand] = useState<Weapon>()
const [weapons, setWeapons] = useState<GridArray>({})
const [partyId, setPartyId] = useState('')
const [cookies, setCookie] = useCookies(['user'])
useEffect(() => {
if (props.exists && props.found)
configure()
}, [props.mainhand, props.grid, props.partyId])
function configure() {
setMainhand(props.mainhand)
setWeapons(props.grid || {})
setPartyId(props.partyId || '')
}
function createParty() {
const headers = (cookies.user != null) ? {
headers: {
'Authorization': `Bearer ${cookies.user.access_token}`
}
} : {}
const body = (props.userId === undefined) ? {} : {
party: {
user_id: props.userId
}
}
return api.endpoints.parties.create(body, headers)
}
function receiveWeapon(weapon: Weapon, position: number) { function receiveWeapon(weapon: Weapon, position: number) {
const isMainhand = position == -1 props.onSelect(GridType.Weapon, weapon, position)
if (isMainhand) {
setMainhand(weapon)
} else {
// Store the grid unit weapon at the correct position
let newWeapons = Object.assign({}, weapons)
newWeapons[position] = weapon
setWeapons(newWeapons)
}
if (partyId) {
saveWeapon(partyId, weapon, position)
} else {
createParty()
.then(response => {
return response.data.party
})
.then(party => {
if (props.pushHistory) {
props.pushHistory(`/p/${party.shortcode}`)
}
return party.id
})
.then(partyId => {
saveWeapon(partyId, weapon, position)
setPartyId(partyId)
})
}
}
function saveWeapon(pid: string, weapon: Weapon, position: number) {
const headers = (cookies.user != null) ? {
headers: {
'Authorization': `Bearer ${cookies.user.access_token}`
}
} : {}
const body = {
'weapon': {
'party_id': pid,
'weapon_id': weapon.id,
'position': position,
'mainhand': (position == -1)
}
}
api.endpoints.weapons.create(body, headers)
} }
return ( return (
@ -116,7 +42,7 @@ const WeaponGrid = (props: Props) => {
onReceiveData={receiveWeapon} onReceiveData={receiveWeapon}
position={-1} position={-1}
unitType={0} unitType={0}
weapon={mainhand} weapon={props.mainhand}
/> />
<ul id="grid_weapons"> <ul id="grid_weapons">
@ -129,7 +55,7 @@ const WeaponGrid = (props: Props) => {
onReceiveData={receiveWeapon} onReceiveData={receiveWeapon}
position={i} position={i}
unitType={1} unitType={1}
weapon={weapons[i]} weapon={props.grid[i]}
/> />
</li> </li>
) )

View file

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import WeaponLabelIcon from '~components/WeaponLabelIcon' import WeaponLabelIcon from '~components/WeaponLabelIcon'
import gridImages from '../../images/grid/*.jpg' import gridImages from '../../images/weapon-grid/*.jpg'
import './index.css' import './index.css'

View file

@ -5,8 +5,8 @@ import { useModal as useModal } from '~utils/useModal'
import SearchModal from '~components/SearchModal' import SearchModal from '~components/SearchModal'
import UncapIndicator from '~components/UncapIndicator' import UncapIndicator from '~components/UncapIndicator'
import mainhandImages from '../../images/mainhand/*.jpg' import mainImages from '../../images/weapon-main/*.jpg'
import gridImages from '../../images/grid/*.jpg' import gridImages from '../../images/weapon-grid/*.jpg'
import Plus from '../../../assets/plus.svg' import Plus from '../../../assets/plus.svg'
import './index.css' import './index.css'
@ -19,7 +19,7 @@ interface Props {
unitType: 0 | 1 unitType: 0 | 1
} }
function WeaponUnit(props: Props) { const WeaponUnit = (props: Props) => {
const [imageUrl, setImageUrl] = useState('') const [imageUrl, setImageUrl] = useState('')
const { open, openModal, closeModal } = useModal() const { open, openModal, closeModal } = useModal()
@ -48,7 +48,7 @@ function WeaponUnit(props: Props) {
// Generate the correct source for the weapon // Generate the correct source for the weapon
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
if (props.unitType == 0) if (props.unitType == 0)
imgSrc = mainhandImages[weapon.granblue_id] imgSrc = mainImages[weapon.granblue_id]
else else
imgSrc = gridImages[weapon.granblue_id] imgSrc = gridImages[weapon.granblue_id]
} else if (process.env.NODE_ENV === 'production') { } else if (process.env.NODE_ENV === 'production') {
@ -62,6 +62,16 @@ function WeaponUnit(props: Props) {
setImageUrl(imgSrc) setImageUrl(imgSrc)
} }
function sendData(object: Weapon | Summon, position: number) {
if (isWeapon(object)) {
props.onReceiveData(object, position)
}
}
function isWeapon(object: Weapon | Summon): object is Weapon {
return (object as Weapon).proficiency !== undefined
}
return ( return (
<div> <div>
<div className={classes} onClick={openModalIfEditable}> <div className={classes} onClick={openModalIfEditable}>
@ -84,8 +94,9 @@ function WeaponUnit(props: Props) {
{open ? ( {open ? (
<SearchModal <SearchModal
close={closeModal} close={closeModal}
send={props.onReceiveData} send={sendData}
fromPosition={props.position} fromPosition={props.position}
object="weapons"
placeholderText="Search for a weapon..." placeholderText="Search for a weapon..."
/> />
) : null} ) : null}

View file

@ -1,74 +1,24 @@
import React, { useState } from 'react' import React from 'react'
import { RouteComponentProps } from 'react-router-dom' import { RouteComponentProps } from 'react-router-dom'
import { useCookies } from 'react-cookie'
import WeaponGrid from '~components/WeaponGrid' import Party from '~components/Party'
import SegmentedControl from '~components/SegmentedControl'
import Segment from '~components/Segment'
import PartySegmentedControl from '~components/PartySegmentedControl'
import SummonGrid from '~components/SummonGrid'
import CharacterGrid from '~components/CharacterGrid'
interface Props {} interface Props {}
interface NewProps extends RouteComponentProps<Props> {} interface NewProps extends RouteComponentProps<Props> {}
const New: React.FC<NewProps> = (props: NewProps) => { const New: React.FC<NewProps> = () => {
const [cookies, setCookie] = useCookies(['user'])
const [selectedTab, setSelectedTab] = useState('weapons')
const [grid, setGrid] = useState<JSX.Element>(
<WeaponGrid
userId={cookies.user ? cookies.user.user_id : ''}
editable={true}
exists={false}
pushHistory={callback}
/>
)
function callback(path: string) { function callback(path: string) {
// This is scuffed, how do we do this natively? // This is scuffed, how do we do this natively?
window.history.replaceState(null, `Grid Tool`, `${path}`) window.history.replaceState(null, `Grid Tool`, `${path}`)
} }
function segmentClicked(event: React.ChangeEvent<HTMLInputElement>) {
setSelectedTab(event.target.value)
switch(event.target.value) {
case 'weapons':
setGrid((
<WeaponGrid
userId={cookies.user ? cookies.user.user_id : ''}
editable={true}
exists={false}
pushHistory={callback}
/>
))
break
case 'summons':
setGrid((
<SummonGrid
editable={true}
/>
))
break
case 'characters':
setGrid((
<CharacterGrid
editable={true}
/>
))
break
default:
break
}
}
return ( return (
<div id="Content"> <div id="Content">
<PartySegmentedControl <Party
selectedTab={selectedTab} editable={true}
onClick={segmentClicked} exists={false}
pushHistory={callback}
/> />
{grid}
</div> </div>
) )
} }

View file

@ -3,40 +3,34 @@ import { withCookies, useCookies } from 'react-cookie'
import { RouteComponentProps, withRouter } from 'react-router-dom' import { RouteComponentProps, withRouter } from 'react-router-dom'
import api from '~utils/api' import api from '~utils/api'
import PartySegmentedControl from '~components/PartySegmentedControl'
import WeaponGrid from '~components/WeaponGrid' import WeaponGrid from '~components/WeaponGrid'
import SummonGrid from '~components/SummonGrid'
import CharacterGrid from '~components/CharacterGrid'
import Button from '~components/Button' import Button from '~components/Button'
interface Props { interface Props {
hash: string hash: string
} }
interface State {
found: boolean
editable: boolean
mainhand: Weapon,
grid: GridArray,
partyId: string
}
interface PartyProps extends RouteComponentProps<Props> {} interface PartyProps extends RouteComponentProps<Props> {}
type GridArray = { [key: number]: Weapon } const Party: React.FC<PartyProps> = ({ match }) => {
interface GridWeapon {
id: string
mainhand: boolean
position: number | null
weapon: Weapon
}
const Party: React.FC<PartyProps> = ({ match }, state: State) => {
const [found, setFound] = useState(false) const [found, setFound] = useState(false)
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const [editable, setEditable] = useState(false) const [editable, setEditable] = useState(false)
const [grid, setGrid] = useState<GridArray>({})
const [mainhand, setMainhand] = useState<Weapon>() const [weapons, setWeapons] = useState<GridArray<Weapon>>({})
const [summons, setSummons] = useState<GridArray<Summon>>({})
const [mainWeapon, setMainWeapon] = useState<Weapon>()
const [mainSummon, setMainSummon] = useState<Summon>()
const [friendSummon, setFriendSummon] = useState<Summon>()
const [partyId, setPartyId] = useState('') const [partyId, setPartyId] = useState('')
const [cookies, setCookie] = useCookies(['userId']) const [cookies, setCookie] = useCookies(['userId'])
const [selectedTab, setSelectedTab] = useState('weapons')
const [tab, setTab] = useState<JSX.Element>()
const shortcode = match.params.hash || '' const shortcode = match.params.hash || ''
useEffect(() => { useEffect(() => {
@ -54,17 +48,28 @@ const Party: React.FC<PartyProps> = ({ match }, state: State) => {
if (partyUser != undefined && loggedInUser != undefined && partyUser === loggedInUser) if (partyUser != undefined && loggedInUser != undefined && partyUser === loggedInUser)
setEditable(true) setEditable(true)
let weapons: GridArray = {} let weapons: GridArray<Weapon> = {}
party.grid.forEach((gridWeapon: GridWeapon) => { let summons: GridArray<Summon> = {}
party.weapons.forEach((gridWeapon: GridWeapon) => {
if (gridWeapon.mainhand) if (gridWeapon.mainhand)
setMainhand(gridWeapon.weapon) setMainWeapon(gridWeapon.weapon)
else if (!gridWeapon.mainhand && gridWeapon.position != null) else if (!gridWeapon.mainhand && gridWeapon.position != null)
weapons[gridWeapon.position] = gridWeapon.weapon weapons[gridWeapon.position] = gridWeapon.weapon
}) })
party.summons.forEach((gridSummon: GridSummon) => {
if (gridSummon.main)
setMainSummon(gridSummon.summon)
else if (gridSummon.friend)
setFriendSummon(gridSummon.summon)
else if (!gridSummon.main && !gridSummon.friend && gridSummon.position != null)
summons[gridSummon.position] = gridSummon.summon
})
setFound(true) setFound(true)
setLoading(false) setLoading(false)
setGrid(weapons) setWeapons(weapons)
setSummons(summons)
setPartyId(party.id) setPartyId(party.id)
}) })
.catch(error => { .catch(error => {
@ -79,18 +84,68 @@ const Party: React.FC<PartyProps> = ({ match }, state: State) => {
}) })
} }
function segmentClicked(event: React.ChangeEvent<HTMLInputElement>) {
setSelectedTab(event.target.value)
switch(event.target.value) {
case 'weapons':
setTab((
<WeaponGrid
userId={cookies.user ? cookies.user.user_id : ''}
partyId={partyId}
mainhand={mainWeapon}
grid={weapons}
editable={editable}
exists={true}
found={found}
/>
))
break
case 'summons':
setTab((
<SummonGrid
userId={cookies.user ? cookies.user.user_id : ''}
partyId={partyId}
main={mainSummon}
friend={friendSummon}
grid={summons}
editable={editable}
exists={true}
found={found}
/>
))
break
case 'characters':
setTab((
<CharacterGrid
editable={true}
/>
))
break
default:
break
}
}
function render() { function render() {
return ( return (
<div> <div id="Content">
<WeaponGrid <PartySegmentedControl
userId={cookies.user ? cookies.user.user_id : ''} selectedTab={selectedTab}
partyId={partyId} onClick={segmentClicked}
mainhand={mainhand}
grid={grid}
editable={editable}
exists={true}
found={found}
/> />
{tab || (
<WeaponGrid
userId={cookies.user ? cookies.user.user_id : ''}
partyId={partyId}
mainhand={mainWeapon}
grid={weapons}
editable={editable}
exists={true}
found={found}
/>
)}
</div> </div>
) )
} }

View file

@ -1 +1 @@
type GridArray = { [key: number]: Weapon } type GridArray<T> = { [key: number]: T }

7
src/types/GridSummon.d.ts vendored Normal file
View file

@ -0,0 +1,7 @@
interface GridSummon {
id: string
main: boolean
friend: boolean
position: number | null
summon: Summon
}

View file

@ -49,9 +49,9 @@ class Api {
return axios.post(`${process.env.SIERO_OAUTH_URL}/token` || 'http://127.0.0.1:3000/oauth/token', object) return axios.post(`${process.env.SIERO_OAUTH_URL}/token` || 'http://127.0.0.1:3000/oauth/token', object)
} }
search(query: string) { search(object: string, query: string) {
const resourceUrl = `${this.url}/${name}` const resourceUrl = `${this.url}/${name}`
return axios.get(`${resourceUrl}/search?query=${query}`) return axios.get(`${resourceUrl}/search/${object}?query=${query}`)
} }
check(resource: string, value: string) { check(resource: string, value: string) {
@ -66,5 +66,6 @@ const api: Api = new Api({ url: process.env.SIERO_API_URL || 'http://127.0.0.1:3
api.createEntity( { name: 'users' }) api.createEntity( { name: 'users' })
api.createEntity( { name: 'parties' }) api.createEntity( { name: 'parties' })
api.createEntity( { name: 'weapons' }) api.createEntity( { name: 'weapons' })
api.createEntity( { name: 'summons' })
export default api export default api