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:
parent
ad2b70e819
commit
c65b2597aa
2 changed files with 97 additions and 122 deletions
|
|
@ -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) => {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue