Add type alias for searchable objects

This commit is contained in:
Justin Edmund 2022-11-30 05:19:43 -08:00
parent 322964d767
commit 73944becf4
5 changed files with 368 additions and 314 deletions

View file

@ -1,41 +1,43 @@
import React, { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { useSnapshot } from 'valtio'
import { useTranslation } from 'next-i18next'
import classnames from 'classnames'
import React, { useEffect, useState } from "react"
import { useRouter } from "next/router"
import { useSnapshot } from "valtio"
import { useTranslation } from "next-i18next"
import classnames from "classnames"
import { appState } from '~utils/appState'
import { appState } from "~utils/appState"
import CharacterHovercard from '~components/CharacterHovercard'
import SearchModal from '~components/SearchModal'
import UncapIndicator from '~components/UncapIndicator'
import PlusIcon from '~public/icons/Add.svg'
import CharacterHovercard from "~components/CharacterHovercard"
import SearchModal from "~components/SearchModal"
import UncapIndicator from "~components/UncapIndicator"
import PlusIcon from "~public/icons/Add.svg"
import './index.scss'
import { getRedirectStatus } from 'next/dist/lib/load-custom-routes'
import type { SearchableObject } from "~types"
import "./index.scss"
interface Props {
gridCharacter: GridCharacter | undefined
gridCharacter?: GridCharacter
position: number
editable: boolean
updateObject: (object: Character | Weapon | Summon, position: number) => void
updateObject: (object: SearchableObject, position: number) => void
updateUncap: (id: string, position: number, uncap: number) => void
}
const CharacterUnit = (props: Props) => {
const { t } = useTranslation('common')
const { t } = useTranslation("common")
const { party, grid } = useSnapshot(appState)
const router = useRouter()
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
const locale =
router.locale && ["en", "ja"].includes(router.locale) ? router.locale : "en"
const [imageUrl, setImageUrl] = useState('')
const [imageUrl, setImageUrl] = useState("")
const classes = classnames({
CharacterUnit: true,
'editable': props.editable,
'filled': (props.gridCharacter !== undefined)
editable: props.editable,
filled: props.gridCharacter !== undefined,
})
const gridCharacter = props.gridCharacter
@ -52,16 +54,13 @@ const CharacterUnit = (props: Props) => {
const character = props.gridCharacter.object!
// Change the image based on the uncap level
let suffix = '01'
if (props.gridCharacter.uncap_level == 6)
suffix = '04'
else if (props.gridCharacter.uncap_level == 5)
suffix = '03'
else if (props.gridCharacter.uncap_level > 2)
suffix = '02'
let suffix = "01"
if (props.gridCharacter.uncap_level == 6) suffix = "04"
else if (props.gridCharacter.uncap_level == 5) suffix = "03"
else if (props.gridCharacter.uncap_level > 2) suffix = "02"
// Special casing for Lyria (and Young Cat eventually)
if (props.gridCharacter.object.granblue_id === '3030182000') {
if (props.gridCharacter.object.granblue_id === "3030182000") {
let element = 1
if (grid.weapons.mainWeapon && grid.weapons.mainWeapon.element) {
element = grid.weapons.mainWeapon.element
@ -86,24 +85,31 @@ const CharacterUnit = (props: Props) => {
const image = (
<div className="CharacterImage">
<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>
)
const editableImage = (
<SearchModal
placeholderText={t('search.placeholders.character')}
placeholderText={t("search.placeholders.character")}
fromPosition={props.position}
object="characters"
send={props.updateObject}>
send={props.updateObject}
>
{image}
</SearchModal>
)
const unitContent = (
<div className={classes}>
{ (props.editable) ? editableImage : image }
{ (gridCharacter && character) ?
{props.editable ? editableImage : image}
{gridCharacter && character ? (
<UncapIndicator
type="character"
flb={character.uncap.flb || false}
@ -111,7 +117,10 @@ const CharacterUnit = (props: Props) => {
uncapLevel={gridCharacter.uncap_level}
updateUncap={passUncapData}
special={character.special}
/> : '' }
/>
) : (
""
)}
<h3 className="CharacterName">{character?.name[locale]}</h3>
</div>
)
@ -122,9 +131,7 @@ const CharacterUnit = (props: Props) => {
</CharacterHovercard>
)
return (
(gridCharacter && !props.editable) ? withHovercard : unitContent
)
return gridCharacter && !props.editable ? withHovercard : unitContent
}
export default CharacterUnit

View file

@ -17,13 +17,14 @@ import SummonSearchFilterBar from "~components/SummonSearchFilterBar"
import CharacterResult from "~components/CharacterResult"
import WeaponResult from "~components/WeaponResult"
import SummonResult from "~components/SummonResult"
import type { SearchableObject, SearchableObjectArray } from "~types"
import "./index.scss"
import CrossIcon from "~public/icons/Cross.svg"
import cloneDeep from "lodash.clonedeep"
interface Props {
send: (object: Character | Weapon | Summon, position: number) => any
send: (object: SearchableObject, position: number) => any
placeholderText: string
fromPosition: number
object: "weapons" | "characters" | "summons"
@ -51,7 +52,7 @@ const SearchModal = (props: Props) => {
const [filters, setFilters] = useState<{ [key: string]: number[] }>()
const [open, setOpen] = useState(false)
const [query, setQuery] = useState("")
const [results, setResults] = useState<(Weapon | Summon | Character)[]>([])
const [results, setResults] = useState<SearchableObjectArray>([])
// Pagination states
const [recordCount, setRecordCount] = useState(0)
@ -99,10 +100,7 @@ const SearchModal = (props: Props) => {
})
}
function replaceResults(
count: number,
list: Weapon[] | Summon[] | Character[]
) {
function replaceResults(count: number, list: SearchableObjectArray) {
if (count > 0) {
setResults(list)
} else {
@ -110,26 +108,36 @@ const SearchModal = (props: Props) => {
}
}
function appendResults(list: Weapon[] | Summon[] | Character[]) {
function appendResults(list: SearchableObjectArray) {
setResults([...results, ...list])
}
function storeRecentResult(result: Character | Weapon | Summon) {
function storeRecentResult(result: SearchableObject) {
const key = `recent_${props.object}`
const cookie = getCookie(key)
const cookieObj: Character[] | Weapon[] | Summon[] = cookie
const cookieObj: SearchableObjectArray = cookie
? JSON.parse(cookie as string)
: []
let recents: Character[] | Weapon[] | Summon[] = []
let recents: SearchableObjectArray = []
if (props.object === "weapons") {
recents = cloneDeep(cookieObj as Weapon[]) || []
if (!recents.find((item) => item.granblue_id === result.granblue_id)) {
if (
!recents.find(
(item) =>
(item as Weapon).granblue_id === (result as Weapon).granblue_id
)
) {
recents.unshift(result as Weapon)
}
} else if (props.object === "summons") {
recents = cloneDeep(cookieObj as Summon[]) || []
if (!recents.find((item) => item.granblue_id === result.granblue_id)) {
if (
!recents.find(
(item) =>
(item as Summon).granblue_id === (result as Summon).granblue_id
)
) {
recents.unshift(result as Summon)
}
}
@ -139,7 +147,7 @@ const SearchModal = (props: Props) => {
sendData(result)
}
function sendData(result: Character | Weapon | Summon) {
function sendData(result: SearchableObject) {
props.send(result, props.fromPosition)
openChange()
}

View file

@ -1,39 +1,42 @@
import React, { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'
import classnames from 'classnames'
import React, { useEffect, useState } from "react"
import { useRouter } from "next/router"
import { useTranslation } from "next-i18next"
import classnames from "classnames"
import SearchModal from '~components/SearchModal'
import SummonHovercard from '~components/SummonHovercard'
import UncapIndicator from '~components/UncapIndicator'
import PlusIcon from '~public/icons/Add.svg'
import SearchModal from "~components/SearchModal"
import SummonHovercard from "~components/SummonHovercard"
import UncapIndicator from "~components/UncapIndicator"
import PlusIcon from "~public/icons/Add.svg"
import './index.scss'
import type { SearchableObject } from "~types"
import "./index.scss"
interface Props {
gridSummon: GridSummon | undefined
unitType: 0 | 1 | 2
position: number
editable: boolean
updateObject: (object: Character | Weapon | Summon, position: number) => void
updateObject: (object: SearchableObject, position: number) => void
updateUncap: (id: string, position: number, uncap: number) => void
}
const SummonUnit = (props: Props) => {
const { t } = useTranslation('common')
const { t } = useTranslation("common")
const [imageUrl, setImageUrl] = useState('')
const [imageUrl, setImageUrl] = useState("")
const router = useRouter()
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
const locale =
router.locale && ["en", "ja"].includes(router.locale) ? router.locale : "en"
const classes = classnames({
SummonUnit: true,
'main': props.unitType == 0,
'grid': props.unitType == 1,
'friend': props.unitType == 2,
'editable': props.editable,
'filled': (props.gridSummon !== undefined)
main: props.unitType == 0,
grid: props.unitType == 1,
friend: props.unitType == 2,
editable: props.editable,
filled: props.gridSummon !== undefined,
})
const gridSummon = props.gridSummon
@ -49,15 +52,28 @@ const SummonUnit = (props: Props) => {
const summon = props.gridSummon.object!
const upgradedSummons = [
'2040094000', '2040100000', '2040080000', '2040098000',
'2040090000', '2040084000', '2040003000', '2040056000',
'2040020000', '2040034000', '2040028000', '2040027000',
'2040046000', '2040047000'
"2040094000",
"2040100000",
"2040080000",
"2040098000",
"2040090000",
"2040084000",
"2040003000",
"2040056000",
"2040020000",
"2040034000",
"2040028000",
"2040027000",
"2040046000",
"2040047000",
]
let suffix = ''
if (upgradedSummons.indexOf(summon.granblue_id.toString()) != -1 && props.gridSummon.uncap_level == 5)
suffix = '_02'
let suffix = ""
if (
upgradedSummons.indexOf(summon.granblue_id.toString()) != -1 &&
props.gridSummon.uncap_level == 5
)
suffix = "_02"
// Generate the correct source for the summon
if (props.unitType == 0 || props.unitType == 2)
@ -77,24 +93,31 @@ const SummonUnit = (props: Props) => {
const image = (
<div className="SummonImage">
<img alt={summon?.name.en} className="grid_image" src={imageUrl} />
{ (props.editable) ? <span className='icon'><PlusIcon /></span> : '' }
{props.editable ? (
<span className="icon">
<PlusIcon />
</span>
) : (
""
)}
</div>
)
const editableImage = (
<SearchModal
placeholderText={t('search.placeholders.summon')}
placeholderText={t("search.placeholders.summon")}
fromPosition={props.position}
object="summons"
send={props.updateObject}>
send={props.updateObject}
>
{image}
</SearchModal>
)
const unitContent = (
<div className={classes}>
{ (props.editable) ? editableImage : image }
{ (gridSummon) ?
{props.editable ? editableImage : image}
{gridSummon ? (
<UncapIndicator
type="summon"
ulb={gridSummon.object.uncap.ulb || false}
@ -102,19 +125,19 @@ const SummonUnit = (props: Props) => {
uncapLevel={gridSummon.uncap_level}
updateUncap={passUncapData}
special={false}
/> : ''
}
/>
) : (
""
)}
<h3 className="SummonName">{summon?.name[locale]}</h3>
</div>
)
const withHovercard = (
<SummonHovercard gridSummon={gridSummon!}>
{unitContent}
</SummonHovercard>
<SummonHovercard gridSummon={gridSummon!}>{unitContent}</SummonHovercard>
)
return (gridSummon && !props.editable) ? withHovercard : unitContent
return gridSummon && !props.editable ? withHovercard : unitContent
}
export default SummonUnit

View file

@ -1,42 +1,44 @@
import React, { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'
import classnames from 'classnames'
import React, { useEffect, useState } from "react"
import { useRouter } from "next/router"
import { useTranslation } from "next-i18next"
import classnames from "classnames"
import SearchModal from '~components/SearchModal'
import WeaponModal from '~components/WeaponModal'
import WeaponHovercard from '~components/WeaponHovercard'
import UncapIndicator from '~components/UncapIndicator'
import Button from '~components/Button'
import SearchModal from "~components/SearchModal"
import WeaponModal from "~components/WeaponModal"
import WeaponHovercard from "~components/WeaponHovercard"
import UncapIndicator from "~components/UncapIndicator"
import Button from "~components/Button"
import { ButtonType } from '~utils/enums'
import { ButtonType } from "~utils/enums"
import type { SearchableObject } from "~types"
import PlusIcon from '~public/icons/Add.svg'
import './index.scss'
import PlusIcon from "~public/icons/Add.svg"
import "./index.scss"
interface Props {
gridWeapon: GridWeapon | undefined
unitType: 0 | 1
position: number
editable: boolean
updateObject: (object: Character | Weapon | Summon, position: number) => void
updateObject: (object: SearchableObject, position: number) => void
updateUncap: (id: string, position: number, uncap: number) => void
}
const WeaponUnit = (props: Props) => {
const { t } = useTranslation('common')
const { t } = useTranslation("common")
const [imageUrl, setImageUrl] = useState('')
const [imageUrl, setImageUrl] = useState("")
const router = useRouter()
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
const locale =
router.locale && ["en", "ja"].includes(router.locale) ? router.locale : "en"
const classes = classnames({
WeaponUnit: true,
'mainhand': props.unitType == 0,
'grid': props.unitType == 1,
'editable': props.editable,
'filled': (props.gridWeapon !== undefined)
mainhand: props.unitType == 0,
grid: props.unitType == 1,
editable: props.editable,
filled: props.gridWeapon !== undefined,
})
const gridWeapon = props.gridWeapon
@ -75,37 +77,49 @@ const WeaponUnit = (props: Props) => {
function canBeModified(gridWeapon: GridWeapon) {
const weapon = gridWeapon.object
return weapon.ax > 0 ||
(weapon.series) && [2, 3, 17, 22, 24].includes(weapon.series)
return (
weapon.ax > 0 ||
(weapon.series && [2, 3, 17, 22, 24].includes(weapon.series))
)
}
const image = (
<div className="WeaponImage">
<img alt={weapon?.name.en} className="grid_image" src={imageUrl} />
{ (props.editable) ? <span className='icon'><PlusIcon /></span> : '' }
{props.editable ? (
<span className="icon">
<PlusIcon />
</span>
) : (
""
)}
</div>
)
const editableImage = (
<SearchModal
placeholderText={t('search.placeholders.weapon')}
placeholderText={t("search.placeholders.weapon")}
fromPosition={props.position}
object="weapons"
send={props.updateObject}>
send={props.updateObject}
>
{image}
</SearchModal>
)
const unitContent = (
<div className={classes}>
{ (props.editable && gridWeapon && canBeModified(gridWeapon)) ?
{props.editable && gridWeapon && canBeModified(gridWeapon) ? (
<WeaponModal gridWeapon={gridWeapon}>
<div>
<Button icon="settings" type={ButtonType.IconOnly} />
</div>
</WeaponModal>: '' }
{ (props.editable) ? editableImage : image }
{ (gridWeapon) ?
</WeaponModal>
) : (
""
)}
{props.editable ? editableImage : image}
{gridWeapon ? (
<UncapIndicator
type="weapon"
ulb={gridWeapon.object.uncap.ulb || false}
@ -113,19 +127,19 @@ const WeaponUnit = (props: Props) => {
uncapLevel={gridWeapon.uncap_level}
updateUncap={passUncapData}
special={false}
/> : ''
}
/>
) : (
""
)}
<h3 className="WeaponName">{weapon?.name[locale]}</h3>
</div>
)
const withHovercard = (
<WeaponHovercard gridWeapon={gridWeapon!}>
{unitContent}
</WeaponHovercard>
<WeaponHovercard gridWeapon={gridWeapon!}>{unitContent}</WeaponHovercard>
)
return (gridWeapon && !props.editable) ? withHovercard : unitContent
return gridWeapon && !props.editable ? withHovercard : unitContent
}
export default WeaponUnit

2
types/index.d.ts vendored Normal file
View file

@ -0,0 +1,2 @@
export type SearchableObject = Character | Weapon | Summon | JobSkill
export type SearchableObjectArray = (Character | Weapon | Summon | JobSkill)[]