Skip to content

Commit

Permalink
GO: Builds indicator for artifacts (#2620)
Browse files Browse the repository at this point in the history
* Feature: Display the amount of builds the artefact is used on

* Feature: Added translation, reafactor some code

* Refactor code

* Refactor using memo()

* Refactor changes, added loadout name and icons
  • Loading branch information
failchon authored Jan 26, 2025
1 parent 3769baf commit 2c56ff8
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 7 deletions.
3 changes: 3 additions & 0 deletions libs/gi/db/src/Database/DataManagers/BuildDataManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { defaultInitialWeaponKey, initialWeapon } from './WeaponDataManager'
export interface Build {
name: string
description: string
id: string

weaponId?: string
artifactIds: Record<ArtifactSlotKey, string | undefined>
Expand All @@ -29,6 +30,7 @@ export class BuildDataManager extends DataManager<
}
override validate(obj: unknown): Build | undefined {
let { name, description, weaponId, artifactIds } = obj as Build
const { id } = obj as Build
if (typeof name !== 'string') name = 'Build Name'
if (typeof description !== 'string') description = ''
if (weaponId && !this.database.weapons.get(weaponId)) weaponId = undefined
Expand Down Expand Up @@ -62,6 +64,7 @@ export class BuildDataManager extends DataManager<
description,
weaponId,
artifactIds,
id,
}
}

Expand Down
5 changes: 4 additions & 1 deletion libs/gi/localization/assets/locales/en/artifact.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,8 @@
"rvSliderBtn": {
"maximum": "MRV",
"current": "RV"
}
},
"builds_one": "{{count}} Build",
"builds_other": "{{count}} Builds",
"artifactUsage": "Artifact is used in the following loadouts and builds: "
}
124 changes: 118 additions & 6 deletions libs/gi/ui/src/components/artifact/ArtifactCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client'
// use client due to hydration difference between client rendering and server in translation
import { useBoolState } from '@genshin-optimizer/common/react-util'
import { iconInlineProps } from '@genshin-optimizer/common/svgicons'
import {
BootstrapTooltip,
Expand All @@ -8,13 +9,16 @@ import {
ConditionalWrapper,
InfoTooltip,
InfoTooltipInline,
ModalWrapper,
NextImage,
SqBadge,
StarsDisplay,
} from '@genshin-optimizer/common/ui'
import { clamp, clamp01, getUnitStr } from '@genshin-optimizer/common/util'
import { artifactAsset } from '@genshin-optimizer/gi/assets'
import type {
ArtifactRarity,
CharacterKey,
LocationKey,
SubstatKey,
} from '@genshin-optimizer/gi/consts'
Expand All @@ -23,7 +27,7 @@ import {
allSubstatKeys,
} from '@genshin-optimizer/gi/consts'
import type { ICachedArtifact, ICachedSubstat } from '@genshin-optimizer/gi/db'
import { useArtifact } from '@genshin-optimizer/gi/db-ui'
import { useArtifact, useDatabase } from '@genshin-optimizer/gi/db-ui'
import { SlotIcon, StatIcon } from '@genshin-optimizer/gi/svgicons'
import {
artDisplayValue,
Expand All @@ -40,18 +44,23 @@ import {
Button,
CardActionArea,
CardContent,
CardHeader,
Chip,
IconButton,
List,
ListItem,
ListItemIcon,
ListItemText,
Skeleton,
SvgIcon,
Typography,
} from '@mui/material'
import type { ReactNode } from 'react'
import { Suspense, useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { ExcludeIcon } from '../../consts'
import { CloseIcon, ExcludeIcon, LoadoutIcon } from '../../consts'
import { PercentBadge } from '../PercentBadge'
import { LocationAutocomplete, LocationName } from '../character'
import { CharIconSide, LocationAutocomplete, LocationName } from '../character'
import { ArtifactSetTooltipContent } from './ArtifactSetTooltip'
import {
ArtifactSetName,
Expand Down Expand Up @@ -96,7 +105,7 @@ export function ArtifactCardObj({
} & Data) {
const { t } = useTranslation(['artifact', 'ui'])
const { t: tk } = useTranslation('statKey_gen')

const [showUsage, onShowUsage, onHideUsage] = useBoolState(false)
const wrapperFunc = useCallback(
(children: ReactNode) => (
<CardActionArea
Expand Down Expand Up @@ -153,7 +162,29 @@ export function ArtifactCardObj({
Math.min(mainStatAssumptionLevel, rarity * 4),
level
)

const database = useDatabase()
const builds: {
loadoutName: string
buildName: string
charKey: CharacterKey
}[] = useMemo(() => {
return database.builds.values
.filter(
({ artifactIds }) => artifactIds[artifact.slotKey] === artifact.id
)
.flatMap(({ id, name }) => {
const buildName = name
return database.teamChars.values
.filter(({ buildIds }) => buildIds.includes(id))
.map(({ key, name }) => {
return {
charKey: key,
buildName,
loadoutName: name,
}
})
})
}, [database.builds, database.teamChars, artifact.slotKey, artifact.id])
const artifactValid = maxEfficiency !== 0
const slotName = <ArtifactSetSlotName setKey={setKey} slotKey={slotKey} />
const slotDesc = <ArtifactSetSlotDesc setKey={setKey} slotKey={slotKey} />
Expand All @@ -178,6 +209,14 @@ export function ArtifactCardObj({
/>
}
>
<Suspense fallback={false}>
<ArtifactBuildUsageModal
show={showUsage}
onHide={onHideUsage}
usageText={t('artifact:artifactUsage')}
builds={builds}
/>
</Suspense>
<CardThemed
bgt="light"
sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}
Expand Down Expand Up @@ -350,14 +389,32 @@ export function ArtifactCardObj({
</Typography>
)}
<Box flexGrow={1} />
<Typography color="success.main">
<Typography
color="success.main"
sx={{
display: 'flex',
gap: 1,
alignItems: 'center',
mt: 1,
}}
>
{(setKey && <ArtifactSetName setKey={setKey} />) ||
'Artifact Set'}{' '}
{setKey && (
<InfoTooltipInline
title={<ArtifactSetTooltipContent setKey={setKey} />}
/>
)}
<SqBadge
sx={{
ml: 'auto',
cursor: builds.length ? 'pointer' : 'default',
}}
color={builds.length ? 'success' : 'secondary'}
onClick={builds.length ? onShowUsage : undefined}
>
{t('builds', { count: builds.length })}
</SqBadge>
</Typography>
</CardContent>
</ConditionalWrapper>
Expand Down Expand Up @@ -488,3 +545,58 @@ function SubstatDisplay({
</Box>
)
}

function ArtifactBuildUsageModal({
show,
onHide,
usageText,
builds,
}: {
show: boolean
onHide: () => void
usageText: string
builds: {
loadoutName: string
buildName: string
charKey: CharacterKey
}[]
}) {
return (
<ModalWrapper open={show} onClose={onHide}>
<CardThemed>
<CardHeader
title={
<Typography
variant="h6"
flexGrow={1}
display="flex"
alignItems="center"
>
{usageText}
</Typography>
}
action={
<IconButton onClick={onHide}>
<CloseIcon />
</IconButton>
}
/>
<List>
{builds.map((build, index) => (
<ListItem key={index}>
<ListItemIcon>
<CharIconSide characterKey={build.charKey} />
</ListItemIcon>
<LoadoutIcon titleAccess="Loadout" fontSize="small" />
<ListItemText
disableTypography={true}
sx={{ display: 'flex', alignItems: 'center' }}
primary={`${build.loadoutName}: ${build.buildName}`}
/>
</ListItem>
))}
</List>
</CardThemed>
</ModalWrapper>
)
}

0 comments on commit 2c56ff8

Please sign in to comment.