Refactor WeaponKeySelect

No longer makes an API call for each instantiation—instead we use the weapon keys downloaded on the server
This commit is contained in:
Justin Edmund 2023-07-03 19:05:05 -07:00
parent ad2b70e819
commit c65b2597aa
2 changed files with 97 additions and 122 deletions

View file

@ -3,69 +3,91 @@ import React, { useEffect, useState } from 'react'
import Select from '~components/common/Select' import Select from '~components/common/Select'
import SelectGroup from '~components/common/SelectGroup' import SelectGroup from '~components/common/SelectGroup'
import SelectItem from '~components/common/SelectItem' import SelectItem from '~components/common/SelectItem'
import api from '~utils/api'
import { appState } from '~utils/appState'
import styles from './index.module.scss' import styles from './index.module.scss'
// Props // Props
interface Props { interface Props {
open: boolean open: boolean
currentValue?: WeaponKey weaponKey?: WeaponKey
series: number series: number
slot: number slot: number
onChange?: (value: string, slot: number) => void onChange?: (value: WeaponKey, slot: number) => void
onOpenChange: () => void onOpenChange: () => void
onClose?: () => void onClose?: () => void
} }
// Constants
const pendulumNames = [
{ en: 'Pendulum', jp: 'ペンデュラム' },
{ en: 'Chain', jp: 'チェイン' },
]
const telumaNames = [{ en: 'Teluma', jp: 'テルマ' }]
const emblemNames = [{ en: 'Emblem', jp: 'エンブレム' }]
const gauphNames = [
{ en: 'Gauph Key', jp: 'ガフスキー' },
{ en: 'Ultima Key', jp: 'ガフスキーΩ' },
{ en: 'Gate of Omnipotence', jp: 'ガフスキー' },
]
const emptyWeaponKey: WeaponKey = {
id: 'no-key',
granblue_id: '-1',
series: 0,
slot: 0,
slug: '',
group: 0,
order: 0,
name: { en: '', ja: '' },
}
const WeaponKeySelect = React.forwardRef<HTMLButtonElement, Props>( const WeaponKeySelect = React.forwardRef<HTMLButtonElement, Props>(
function useFieldSet(props, ref) { function useFieldSet(
const [open, setOpen] = useState(false) { open, weaponKey, series, slot, onChange, onOpenChange, onClose },
ref
) {
const [keys, setKeys] = useState<WeaponKey[][]>([]) const [keys, setKeys] = useState<WeaponKey[][]>([])
const pendulumNames = [
{ en: 'Pendulum', jp: 'ペンデュラム' },
{ en: 'Chain', jp: 'チェイン' },
]
const telumaNames = [{ en: 'Teluma', jp: 'テルマ' }]
const emblemNames = [{ en: 'Emblem', jp: 'エンブレム' }]
const gauphNames = [
{ en: 'Gauph Key', jp: 'ガフスキー' },
{ en: 'Ultima Key', jp: 'ガフスキーΩ' },
{ en: 'Gate of Omnipotence', jp: 'ガフスキー' },
]
useEffect(() => { useEffect(() => {
const filterParams = { const keys = flattenWeaponKeys()
params: { const filteredKeys = filterWeaponKeys(keys)
series: props.series, setKeys(groupWeaponKeys(filteredKeys))
slot: props.slot, }, [series])
},
function flattenWeaponKeys() {
const keys: WeaponKey[] = []
for (let setName of Object.keys(appState.weaponKeys)) {
const set = appState.weaponKeys[setName]
set.map((key) => keys.push(key))
} }
function organizeWeaponKeys(weaponKeys: WeaponKey[]) { return keys
const numGroups = Math.max.apply( }
Math,
weaponKeys.map((key) => key.group)
)
let groupedKeys = []
for (let i = 0; i <= numGroups; i++) {
const values = weaponKeys.filter((key) => key.group == i)
if (values.length > 0) groupedKeys[i] = values
}
setKeys(groupedKeys.filter(() => true)) function filterWeaponKeys(weaponKeys: WeaponKey[]) {
// Filter weapon keys based on the series and slot provided
return weaponKeys.filter(
(key) => key.series == series && key.slot == slot
)
}
function groupWeaponKeys(weaponKeys: WeaponKey[]) {
const numGroups = Math.max.apply(
Math,
weaponKeys.map((key) => key.group)
)
let groupedKeys = []
for (let i = 0; i <= numGroups; i++) {
const values = weaponKeys.filter((key) => key.group == i)
if (values.length > 0) groupedKeys[i] = values
} }
function fetchWeaponKeys() { return groupedKeys.filter(() => true)
api.endpoints.weapon_keys }
.getAll(filterParams)
.then((response) => organizeWeaponKeys(response.data))
}
fetchWeaponKeys()
}, [props.series, props.slot])
function weaponKeyGroup(index: number) { function weaponKeyGroup(index: number) {
;['α', 'β', 'γ', 'Δ'].sort((a, b) => a.localeCompare(b, 'el')) ;['α', 'β', 'γ', 'Δ'].sort((a, b) => a.localeCompare(b, 'el'))
@ -90,19 +112,16 @@ const WeaponKeySelect = React.forwardRef<HTMLButtonElement, Props>(
}) })
let name: { [key: string]: string } = {} let name: { [key: string]: string } = {}
if (props.series == 2 && index == 0) name = pendulumNames[0] if (series == 2 && index == 0) name = pendulumNames[0]
else if (props.series == 2 && props.slot == 1 && index == 1) else if (series == 2 && slot == 1 && index == 1) name = pendulumNames[1]
name = pendulumNames[1] else if (series === 3) name = telumaNames[0]
else if (props.series == 3) name = telumaNames[0] else if (series === 17) name = gauphNames[slot]
else if (props.series == 17) name = gauphNames[props.slot] else if (series === 24) name = emblemNames[index]
else if (props.series == 24) name = emblemNames[index]
return ( return (
<SelectGroup <SelectGroup
key={index} key={index}
label={ label={series == 17 && slot == 2 ? name.en : `${name.en}s`}
props.series == 17 && props.slot == 2 ? name.en : `${name.en}s`
}
separator={false} separator={false}
> >
{options} {options}
@ -111,32 +130,34 @@ const WeaponKeySelect = React.forwardRef<HTMLButtonElement, Props>(
} }
function handleChange(value: string) { function handleChange(value: string) {
if (props.onChange) props.onChange(value, props.slot) const keys = flattenWeaponKeys()
const found = keys.find((key) => key.id == value)
const weaponKey = found ? found : emptyWeaponKey
if (onChange) onChange(weaponKey, slot)
} }
const emptyOption = () => { const emptyOption = () => {
let name = '' let name = ''
if (props.series == 2) name = pendulumNames[0].en if (series === 2) name = pendulumNames[0].en
else if (props.series == 3) name = telumaNames[0].en else if (series === 3) name = telumaNames[0].en
else if (props.series == 17) name = gauphNames[props.slot].en else if (series === 17) name = gauphNames[slot].en
else if (props.series == 24) name = emblemNames[0].en else if (series === 24) name = emblemNames[0].en
return `No ${name}` return `No ${name}`
} }
return ( return (
<Select <Select
key={`weapon-key-${props.slot}`} key={`weapon-key-${slot}`}
value={props.currentValue ? props.currentValue.id : 'no-key'} value={weaponKey ? weaponKey.id : emptyWeaponKey.id}
open={props.open} open={open}
onClose={props.onClose} onClose={onClose}
onOpenChange={props.onOpenChange} onOpenChange={onOpenChange}
onValueChange={handleChange} onValueChange={handleChange}
ref={ref} ref={ref}
triggerClass="modal"
overlayVisible={false} overlayVisible={false}
> >
<SelectItem key="no-key" value="no-key"> <SelectItem key={emptyWeaponKey.id} value={emptyWeaponKey.id}>
{emptyOption()} {emptyOption()}
</SelectItem> </SelectItem>
{Array.from(Array(keys?.length)).map((x, i) => { {Array.from(Array(keys?.length)).map((x, i) => {

View file

@ -1,68 +1,22 @@
.Weapon.DialogContent { .mods {
gap: $unit; display: flex;
min-width: 480px; flex-direction: column;
gap: $unit * 4;
padding: 0 $unit-4x;
@include breakpoint(phone) { section {
min-width: inherit;
}
.DialogHeader {
transition: 0.18s padding-top ease-in-out;
position: sticky;
top: 0;
&.Scrolled {
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: 0 1px 12px rgba(0, 0, 0, 0.34);
padding-top: $unit-2x;
}
img {
transition: 0.2s width ease-in-out;
width: $unit-6x !important;
}
.DialogTitle {
font-size: $font-large;
}
.SubTitle {
display: none;
}
}
.mods {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: $unit * 4; gap: calc($unit / 2);
padding: 0 $unit-4x;
section { h3 {
display: flex; color: $grey-55;
flex-direction: column; font-size: $font-small;
gap: calc($unit / 2); margin-bottom: $unit;
h3 {
color: $grey-55;
font-size: $font-small;
margin-bottom: $unit;
}
select {
background-color: $grey-90;
}
} }
.Button { select {
font-size: $font-regular; background-color: $grey-90;
padding: ($unit * 1.5) ($unit * 2);
width: 100%;
&.btn-disabled {
background: $grey-90;
color: $grey-70;
cursor: not-allowed;
}
} }
} }
} }