diff --git a/apps/frontend/src/app/Components/ToggleButton/CharacterRarityToggle.tsx b/apps/frontend/src/app/Components/ToggleButton/CharacterRarityToggle.tsx new file mode 100644 index 0000000000..5b4da58290 --- /dev/null +++ b/apps/frontend/src/app/Components/ToggleButton/CharacterRarityToggle.tsx @@ -0,0 +1,48 @@ +import type { CharacterRarityKey } from '@genshin-optimizer/consts' +import { allCharacterRarityKeys } from '@genshin-optimizer/consts' +import StarRoundedIcon from '@mui/icons-material/StarRounded' +import { Box, Chip, ToggleButton, useMediaQuery, useTheme } from '@mui/material' +import { handleMultiSelect } from '../../Util/MultiSelect' +import type { SolidToggleButtonGroupProps } from '../SolidToggleButtonGroup' +import SolidToggleButtonGroup from '../SolidToggleButtonGroup' +type CharacterRarityToggleProps = Omit< + SolidToggleButtonGroupProps, + 'onChange' | 'value' +> & { + onChange: (value: CharacterRarityKey[]) => void + value: CharacterRarityKey[] + totals: Record +} +const rarityHandler = handleMultiSelect([...allCharacterRarityKeys]) +export default function CharacterRarityToggle({ + value, + totals, + onChange, + ...props +}: CharacterRarityToggleProps) { + const theme = useTheme() + const xs = !useMediaQuery(theme.breakpoints.up('sm')) + return ( + + {allCharacterRarityKeys.map((star) => ( + onChange(rarityHandler(value, star))} + > + + {star} + + {!xs && } + + + ))} + + ) +} diff --git a/apps/frontend/src/app/Components/ToggleButton/RarityToggle.tsx b/apps/frontend/src/app/Components/ToggleButton/WeaponRarityToggle.tsx similarity index 90% rename from apps/frontend/src/app/Components/ToggleButton/RarityToggle.tsx rename to apps/frontend/src/app/Components/ToggleButton/WeaponRarityToggle.tsx index 07cfa794e7..8bc9255ec3 100644 --- a/apps/frontend/src/app/Components/ToggleButton/RarityToggle.tsx +++ b/apps/frontend/src/app/Components/ToggleButton/WeaponRarityToggle.tsx @@ -5,7 +5,7 @@ import { Box, Chip, ToggleButton, useMediaQuery, useTheme } from '@mui/material' import { handleMultiSelect } from '../../Util/MultiSelect' import type { SolidToggleButtonGroupProps } from '../SolidToggleButtonGroup' import SolidToggleButtonGroup from '../SolidToggleButtonGroup' -type RarityToggleProps = Omit< +type WeaponRarityToggleProps = Omit< SolidToggleButtonGroupProps, 'onChange' | 'value' > & { @@ -14,12 +14,12 @@ type RarityToggleProps = Omit< totals: Record } const rarityHandler = handleMultiSelect([...allRarityKeys]) -export default function RarityToggle({ +export default function WeaponRarityToggle({ value, totals, onChange, ...props -}: RarityToggleProps) { +}: WeaponRarityToggleProps) { const theme = useTheme() const xs = !useMediaQuery(theme.breakpoints.up('sm')) return ( @@ -30,7 +30,7 @@ export default function RarityToggle({ value={star} sx={{ p: xs ? 1 : undefined, - minWidth: xs ? 0 : '7em', + minWidth: xs ? 0 : '6em', display: 'flex', gap: xs ? 0 : 1, }} diff --git a/apps/frontend/src/app/Components/ToggleButton/WeaponToggle.tsx b/apps/frontend/src/app/Components/ToggleButton/WeaponToggle.tsx index 3eba269005..50311a8c8f 100644 --- a/apps/frontend/src/app/Components/ToggleButton/WeaponToggle.tsx +++ b/apps/frontend/src/app/Components/ToggleButton/WeaponToggle.tsx @@ -32,7 +32,7 @@ export default function WeaponToggle({ value={wt} sx={{ p: xs ? 1 : undefined, - minWidth: xs ? 0 : '7em', + minWidth: xs ? 0 : '6em', display: 'flex', gap: xs ? 0 : 1, }} diff --git a/apps/frontend/src/app/Components/Weapon/WeaponSelectionModal.tsx b/apps/frontend/src/app/Components/Weapon/WeaponSelectionModal.tsx index 6aa41505a9..e92b0f26c6 100644 --- a/apps/frontend/src/app/Components/Weapon/WeaponSelectionModal.tsx +++ b/apps/frontend/src/app/Components/Weapon/WeaponSelectionModal.tsx @@ -33,7 +33,7 @@ import CloseButton from '../CloseButton' import ImgIcon from '../Image/ImgIcon' import ModalWrapper from '../ModalWrapper' import { StarsDisplay } from '../StarDisplay' -import RarityToggle from '../ToggleButton/RarityToggle' +import WeaponRarityToggle from '../ToggleButton/WeaponRarityToggle' import WeaponToggle from '../ToggleButton/WeaponToggle' type WeaponSelectionModalProps = { @@ -132,7 +132,7 @@ export default function WeaponSelectionModal({ /> - database.displayWeapon.set({ rarity })} value={rarity} diff --git a/apps/frontend/src/app/Database/DataEntries/DisplayCharacterEntry.ts b/apps/frontend/src/app/Database/DataEntries/DisplayCharacterEntry.ts index 83dc3c0689..f081d88ddf 100644 --- a/apps/frontend/src/app/Database/DataEntries/DisplayCharacterEntry.ts +++ b/apps/frontend/src/app/Database/DataEntries/DisplayCharacterEntry.ts @@ -1,5 +1,13 @@ -import type { ElementKey, WeaponTypeKey } from '@genshin-optimizer/consts' -import { allElementKeys, allWeaponTypeKeys } from '@genshin-optimizer/consts' +import type { + CharacterRarityKey, + ElementKey, + WeaponTypeKey, +} from '@genshin-optimizer/consts' +import { + allElementKeys, + allWeaponTypeKeys, + allCharacterRarityKeys, +} from '@genshin-optimizer/consts' import { validateArr } from '@genshin-optimizer/util' import type { CharacterSortKey } from '../../Util/CharacterSort' import { characterSortKeys } from '../../Util/CharacterSort' @@ -11,6 +19,7 @@ export interface IDisplayCharacterEntry { ascending: boolean weaponType: WeaponTypeKey[] element: ElementKey[] + rarity: CharacterRarityKey[] pageIndex: number } @@ -19,6 +28,7 @@ const initialState = (): IDisplayCharacterEntry => ({ ascending: false, weaponType: [...allWeaponTypeKeys], element: [...allElementKeys], + rarity: [...allCharacterRarityKeys], pageIndex: 0, }) @@ -33,7 +43,7 @@ export class DisplayCharacterEntry extends DataEntry< } override validate(obj: any): IDisplayCharacterEntry | undefined { if (typeof obj !== 'object') return undefined - let { sortType, ascending, weaponType, element, pageIndex } = obj + let { sortType, ascending, weaponType, element, rarity, pageIndex } = obj //Disallow sorting by "new" explicitly. if (sortType === 'new' || !characterSortKeys.includes(sortType)) @@ -41,6 +51,7 @@ export class DisplayCharacterEntry extends DataEntry< if (typeof ascending !== 'boolean') ascending = false weaponType = validateArr(weaponType, allWeaponTypeKeys) element = validateArr(element, allElementKeys) + rarity = validateArr(rarity, allCharacterRarityKeys) if ( typeof pageIndex !== 'number' || pageIndex < 0 || @@ -52,6 +63,7 @@ export class DisplayCharacterEntry extends DataEntry< ascending, weaponType, element, + rarity, pageIndex, } return data diff --git a/apps/frontend/src/app/PageCharacter/CharacterDisplay/Tabs/TabOptimize/Components/AllowChar.tsx b/apps/frontend/src/app/PageCharacter/CharacterDisplay/Tabs/TabOptimize/Components/AllowChar.tsx index da882fd27d..3dd6c557c9 100644 --- a/apps/frontend/src/app/PageCharacter/CharacterDisplay/Tabs/TabOptimize/Components/AllowChar.tsx +++ b/apps/frontend/src/app/PageCharacter/CharacterDisplay/Tabs/TabOptimize/Components/AllowChar.tsx @@ -6,6 +6,7 @@ import { allArtifactSlotKeys, allElementKeys, allWeaponTypeKeys, + allCharacterRarityKeys, charKeyToLocCharKey, } from '@genshin-optimizer/consts' import { useBoolState, useForceUpdate } from '@genshin-optimizer/react-util' @@ -45,6 +46,7 @@ import SolidToggleButtonGroup from '../../../../../Components/SolidToggleButtonG import SqBadge from '../../../../../Components/SqBadge' import ElementToggle from '../../../../../Components/ToggleButton/ElementToggle' import WeaponToggle from '../../../../../Components/ToggleButton/WeaponToggle' +import CharacterRarityToggle from '../../../../../Components/ToggleButton/CharacterRarityToggle' import { CharacterContext } from '../../../../../Context/CharacterContext' import { SillyContext } from '../../../../../Context/SillyContext' import { getCharSheet } from '../../../../../Data/Characters' @@ -89,6 +91,10 @@ export default function AllowChar({ const deferredElementKeys = useDeferredValue(elementKeys) const [weaponTypeKeys, setWeaponTypeKeys] = useState([...allWeaponTypeKeys]) const deferredWeaponTypeKeys = useDeferredValue(weaponTypeKeys) + const [characterRarityKeys, setCharacterRarityKeys] = useState([ + ...allCharacterRarityKeys, + ]) + const deferredCharacterRarityKeys = useDeferredValue(characterRarityKeys) const charKeyMap: Dict = useMemo( () => @@ -103,6 +109,7 @@ export default function AllowChar({ { element: deferredElementKeys, weaponType: deferredWeaponTypeKeys, + rarity: deferredCharacterRarityKeys, name: deferredSearchTerm, }, characterFilterConfigs(database, silly) @@ -117,6 +124,7 @@ export default function AllowChar({ characterKey, deferredElementKeys, deferredWeaponTypeKeys, + deferredCharacterRarityKeys, deferredSearchTerm, silly, ] @@ -139,10 +147,16 @@ export default function AllowChar({ }) .map(([ck]) => charKeyToLocCharKey(ck)) - const { elementTotals, weaponTypeTotals, locListTotals } = useMemo(() => { + const { + elementTotals, + weaponTypeTotals, + characterRarityTotals, + locListTotals, + } = useMemo(() => { const catKeys = { elementTotals: [...allElementKeys], weaponTypeTotals: [...allWeaponTypeKeys], + characterRarityTotals: [...allCharacterRarityKeys], locListTotals: ['allowed', 'excluded'], } as const return bulkCatTotal(catKeys, (ctMap) => @@ -158,6 +172,11 @@ export default function AllowChar({ ctMap.weaponTypeTotals[weaponTypeKey].total++ if (charKeyMap[ck]) ctMap.weaponTypeTotals[weaponTypeKey].current++ + const characterRarityKey = sheet.rarity + ctMap.characterRarityTotals[characterRarityKey].total++ + if (charKeyMap[ck]) + ctMap.characterRarityTotals[characterRarityKey].current++ + const locKey = charKeyToLocCharKey(ck) if (locList.includes(locKey)) { ctMap.locListTotals.allowed.total++ @@ -322,6 +341,13 @@ export default function AllowChar({ totals={elementTotals} size="small" /> + diff --git a/apps/frontend/src/app/PageCharacter/index.tsx b/apps/frontend/src/app/PageCharacter/index.tsx index 907a7714cb..8c39d6520d 100644 --- a/apps/frontend/src/app/PageCharacter/index.tsx +++ b/apps/frontend/src/app/PageCharacter/index.tsx @@ -2,6 +2,7 @@ import type { CharacterKey } from '@genshin-optimizer/consts' import { allElementKeys, allWeaponTypeKeys, + allCharacterRarityKeys, charKeyToLocGenderedCharKey, } from '@genshin-optimizer/consts' import { useForceUpdate, useMediaQueryUp } from '@genshin-optimizer/react-util' @@ -46,6 +47,7 @@ import CharacterCard from '../Components/Character/CharacterCard' import SortByButton from '../Components/SortByButton' import ElementToggle from '../Components/ToggleButton/ElementToggle' import WeaponToggle from '../Components/ToggleButton/WeaponToggle' +import CharacterRarityToggle from '../Components/ToggleButton/CharacterRarityToggle' import { SillyContext } from '../Context/SillyContext' import { getCharSheet } from '../Data/Characters' import { getWeaponSheet } from '../Data/Weapons' @@ -136,11 +138,11 @@ export default function PageCharacter() { const { charKeyList, totalCharNum } = useMemo(() => { const chars = database.chars.keys const totalCharNum = chars.length - const { element, weaponType, sortType, ascending } = deferredState + const { element, weaponType, rarity, sortType, ascending } = deferredState const charKeyList = database.chars.keys .filter( filterFunction( - { element, weaponType, name: deferredSearchTerm }, + { element, weaponType, rarity, name: deferredSearchTerm }, characterFilterConfigs(database, silly) ) ) @@ -155,7 +157,14 @@ export default function PageCharacter() { return deferredDbDirty && { charKeyList, totalCharNum } }, [database, deferredState, deferredSearchTerm, silly, deferredDbDirty]) - const { weaponType, element, sortType, ascending, pageIndex = 0 } = state + const { + weaponType, + element, + rarity, + sortType, + ascending, + pageIndex = 0, + } = state const { charKeyListToShow, numPages, currentPageIndex } = useMemo(() => { const numPages = Math.ceil(charKeyList.length / maxNumToDisplay) @@ -201,6 +210,18 @@ export default function PageCharacter() { [database, charKeyList] ) + const rarityTotals = useMemo( + () => + catTotal(allCharacterRarityKeys, (ct) => + Object.entries(database.chars.data).forEach(([ck, char]) => { + const eleKey = getCharSheet(char.key, database.gender).rarity + ct[eleKey].total++ + if (charKeyList.includes(ck)) ct[eleKey].current++ + }) + ), + [database, charKeyList] + ) + return ( @@ -236,6 +257,15 @@ export default function PageCharacter() { size="small" /> + + database.displayCharacter.set({ rarity })} + value={rarity} + totals={rarityTotals} + size="small" + /> + - database.displayWeapon.set({ rarity })} value={rarity} diff --git a/apps/frontend/src/app/Util/CharacterSort.ts b/apps/frontend/src/app/Util/CharacterSort.ts index c3540e70e0..f6f557edf0 100644 --- a/apps/frontend/src/app/Util/CharacterSort.ts +++ b/apps/frontend/src/app/Util/CharacterSort.ts @@ -2,10 +2,12 @@ import type { CharacterKey, ElementKey, WeaponTypeKey, + CharacterRarityKey, } from '@genshin-optimizer/consts' import { allElementKeys, allWeaponTypeKeys, + allCharacterRarityKeys, charKeyToLocCharKey, charKeyToLocGenderedCharKey, } from '@genshin-optimizer/consts' @@ -48,6 +50,7 @@ export function characterSortConfigs( export const characterFilterKeys = [ 'element', 'weaponType', + 'rarity', 'name', 'new', ] as const @@ -65,6 +68,8 @@ export function characterFilterConfigs( element: (ck, filter) => filter.includes(getCharEle(ck)), weaponType: (ck, filter) => filter.includes(allStats.char.data[charKeyToLocCharKey(ck)].weaponType), + rarity: (ck, filter) => + filter.includes(allStats.char.data[charKeyToLocCharKey(ck)].rarity), name: (ck, filter) => filter === undefined || i18n @@ -101,11 +106,13 @@ export const initialCharacterDisplayState = (): { ascending: boolean weaponType: WeaponTypeKey[] element: ElementKey[] + rarity: CharacterRarityKey[] pageIndex: number } => ({ sortType: characterSortKeys[0], ascending: false, weaponType: [...allWeaponTypeKeys], element: [...allElementKeys], + rarity: [...allCharacterRarityKeys], pageIndex: 0, }) diff --git a/libs/consts/src/character.ts b/libs/consts/src/character.ts index a37be425dd..20a45102bc 100644 --- a/libs/consts/src/character.ts +++ b/libs/consts/src/character.ts @@ -188,6 +188,10 @@ export type LocationCharacterKey = (typeof allLocationCharacterKeys)[number] export type LocationKey = LocationCharacterKey | '' +// Genshin Impact currently only has 4-5 star characters +export const allCharacterRarityKeys = [5, 4] as const +export type CharacterRarityKey = (typeof allCharacterRarityKeys)[number] + export function charKeyToLocGenderedCharKey( charKey: CharacterKey, gender: GenderKey