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

View file

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

View file

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

View file

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