Remove legacy pages

This commit is contained in:
Sakura Knoll
2023-07-14 22:46:05 +09:00 Unverified
parent 06ff200ed3
commit b4eb338ea0
39 changed files with 756 additions and 4007 deletions
+93
View File
@@ -0,0 +1,93 @@
// const YAML = require('yaml')
// const fs = require('fs')
// const path = require('path')
// const rawData = fs.readFileSync(path.join(process.cwd(), 'data/honkai3rd/battlesuits.yaml')).toString()
module.exports = [
['dw', 3401],
['ta', 3501],
['vq', 3301],
['hof', 106],
['hoo', 206],
['hotr', 317],
['cn', 3201],
['jk', 3101],
['hoh', 2202],
['vill-v', 3001],
['griseo', 2901],
['aponia', 2701],
['eden', 2801],
['rc', 2601],
['pe', 804],
['spa', 706],
['sw', 315],
['sns', 2501],
['io', 2301],
['ma', 2401],
['mpe', 2201],
['hfs', 105],
['pv', 2101],
['nyx', 713],
['hb', 314],
['hos', 612],
['bh', 901],
['da', 803],
// ['ft'],
// ['ds'],
['fr', 705],
['hot', 205],
['sa', 506],
['bke', 802],
['vg', 801],
['ae', 611],
['kasumi', 214],
// ['hf', ],
['sn', 712],
// ['tp', ],
['hor', 313],
// ['vd', ],
['sp', 711],
['bb', 421],
['mc', 422],
// ['dk', ],
['aka', 704],
// ['stfu', ],
['pi', 702],
['sj', 114],
['ur', 703],
['vke', 412],
['hov', 113],
['kriegs', 411],
['ns', 603],
['6s', 111],
['lk', 511],
['px', 604],
['sk', 602],
['va', 601],
['ch', 504],
['ri', 112],
['bn', 312],
['sr', 503],
['wd', 311],
['vp', 501],
['ve', 502],
['bs', 403],
['br', 404],
['ci', 201],
['db', 304],
['dp', 104],
['fs', 213],
['meme', 212],
['miko', 211],
['km', 103],
['le', 204],
['sf', 402],
['sd', 202],
['ss', 303],
['vb', 203],
['vc', 301],
['vr', 102],
['vt', 401],
['wc', 101],
['ya', 302]
]
+38 -34
View File
@@ -1,98 +1,102 @@
const { i18n } = require('./next-i18next.config')
const legacyRedirects = require('./legacy-redirects.js')
module.exports = {
async redirects() {
return [
...legacyRedirects.map(([source, dest]) => {
console.log(source, dest)
return {
source: `/honkai3rd/battlesuits/${source}`,
destination: `/honkai3rd/battlesuits/${dest}`,
permanent: false
}
}),
{
source: '/honkai3rd/stigmata/elysia-flawless',
destination: '/honkai3rd/stigmata/elysia-pristine',
permanent: true,
permanent: true
},
{
source: '/honkai3rd/stigmata/elysia-flawless-top',
destination: '/honkai3rd/stigmata/elysia-pristine-top',
permanent: true,
permanent: true
},
{
source: '/honkai3rd/stigmata/elysia-flawless-mid',
destination: '/honkai3rd/stigmata/elysia-pristine-mid',
permanent: true,
permanent: true
},
{
source: '/honkai3rd/stigmata/elysia-flawless-bot',
destination: '/honkai3rd/stigmata/elysia-pristine-bot',
permanent: true,
},
{
source: '/honkai3rd/stigmata/elysia-flawless',
destination: '/honkai3rd/stigmata/bianka-theatre',
permanent: true,
permanent: true
},
{
source: '/honkai3rd/stigmata/bianka-stage-top',
destination: '/honkai3rd/stigmata/bianka-theatre-top',
permanent: true,
permanent: true
},
{
source: '/honkai3rd/stigmata/bianka-stage-mid',
destination: '/honkai3rd/stigmata/bianka-theatre-mid',
permanent: true,
permanent: true
},
{
source: '/honkai3rd/stigmata/bianka-stage-bot',
destination: '/honkai3rd/stigmata/bianka-theatre-bot',
permanent: true,
permanent: true
},
{
source: '/honkai3rd/elfs/hsd',
destination: '/honkai3rd/elfs/bd',
permanent: true,
permanent: true
},
{
source: '/honkai3rd/weapons/dream-of-the-past',
destination: '/honkai3rd/weapons/purana-phantasma',
permanent: true,
permanent: true
},
{
source: '/honkai3rd/weapons/dream-of-the-past-eyes-of-the-night',
destination: '/honkai3rd/weapons/purana-phantasma-nighteye',
permanent: true,
permanent: true
},
{
source: '/honkai3rd/weapons/dark-meteorite-golden-scale',
destination: '/honkai3rd/weapons/gilded-librae',
permanent: true,
permanent: true
},
{
source: '/honkai3rd/weapons/azure-flare',
destination: '/honkai3rd/weapons/cerulean-flare',
permanent: true,
permanent: true
},
{
source: '/honkai3rd/battlesuits/pardofelis',
destination: '/honkai3rd/battlesuits/rc',
permanent: true,
permanent: true
},
{
source: '/honkai3rd/weapons/prisoner-of-the-past',
destination: '/honkai3rd/weapons/lost-conviction',
permanent: true,
permanent: true
},
{
source: '/honkai3rd/weapons/prisoner-of-the-past-deed-of-fate',
destination: '/honkai3rd/weapons/lost-conviction-damnation',
permanent: true,
permanent: true
},
{
source: '/honkai3rd/weapons/beautiful-poem-of-the-past',
destination: '/honkai3rd/weapons/echo-of-paradise',
permanent: true,
permanent: true
},
{
source: '/novels/ae/index.html',
destination: '/honkai3rd/novels/ae',
permanent: false,
},
permanent: false
}
]
},
async rewrites() {
@@ -100,46 +104,46 @@ module.exports = {
{
source: '/ko-KR/honkai3rd/novels/ae',
destination: '/novels/ae/ko-KR/index.html',
locale: false,
locale: false
},
{
source: '/en-US/honkai3rd/novels/ae',
destination: '/novels/ae/en-US/index.html',
locale: false,
locale: false
},
{
source: '/honkai3rd/novels/ae',
destination: '/novels/ae/en-US/index.html',
destination: '/novels/ae/en-US/index.html'
},
{
source: '/ko-KR/honkai3rd/novels/duriduri',
destination: '/novels/duriduri/ko-KR/index.html',
locale: false,
locale: false
},
{
source: '/en-US/honkai3rd/novels/duriduri',
destination: '/novels/duriduri/en-US/index.html',
locale: false,
locale: false
},
{
source: '/honkai3rd/novels/duriduri',
destination: '/novels/duriduri/en-US/index.html',
destination: '/novels/duriduri/en-US/index.html'
},
{
source: '/ko-KR/honkai3rd/novels/7s',
destination: '/novels/7s/ko-KR/index.html',
locale: false,
locale: false
},
{
source: '/en-US/honkai3rd/novels/7s',
destination: '/novels/7s/en-US/index.html',
locale: false,
locale: false
},
{
source: '/honkai3rd/novels/7s',
destination: '/novels/7s/en-US/index.html',
},
destination: '/novels/7s/en-US/index.html'
}
]
},
i18n,
i18n
}
+145 -268
View File
@@ -1,213 +1,162 @@
/** @jsxImportSource theme-ui */
import { Box, Card, Flex, Heading, Paragraph, Text, Image } from '@theme-ui/components'
import { NextPageContext } from 'next'
import React from 'react'
import BattlesuitFeatureLabel from '../../../components/atoms/BattlesuitFeatureLabel'
import BattlesuitRankIcon from '../../../components/atoms/BattlesuitRankIcon'
import PageLink from '../../../components/atoms/PageLink'
import SecondaryLabel from '../../../components/atoms/SecondaryLabel'
import TypeLabel from '../../../components/atoms/TypeLabel'
import ValkyrieLabel from '../../../components/atoms/ValkyrieLabel'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
import { Box, Card, Flex, Heading } from 'theme-ui'
import AttributeIcon from '../../../components/v2/AttributeIcon'
import AvatarFigureImage from '../../../components/v2/AvatarFigureImage'
import AvatarSkillIcon from '../../../components/v2/AvatarSkillIcon'
import AvatarSubSkillIcon from '../../../components/v2/AvatarSubSkillIcon'
import BattlesuitAvatarIcon from '../../../components/v2/BattlesuitAvatarIcon'
import FormattedText from '../../../components/v2/FormattedText'
import { formatSkillInfo, formatSubSkillInfo } from '../../../lib/v2/data/formatText'
import { loadBattlesuitCatalog, loadBattlesuitData } from '../../../lib/v2/server/loadData'
import { Battlesuit, SkillTagItem } from '../../../lib/v2/data/types'
import { Fragment } from 'react'
import TagIcon from '../../../components/v2/TagIcon'
import {
BattlesuitData,
battlesuitFeatures,
BattlesuitSkillGroup,
battlesuitTypes,
valkyries
} from '../../../lib/honkai3rd/battlesuits'
import { generateI18NPaths, getI18NProps } from '../../../server/i18n'
import { getBattlesuitById, listBattlesuits } from '../../../server/data/honkai3rd/battlesuits'
import { translate, useTranslation } from '../../../lib/i18n'
import { useRouter } from 'next/router'
import Head from '../../../components/atoms/Head'
import { capitalize } from '../../../lib/string'
import { assetsBucketBaseUrl } from '../../../lib/consts'
getAttributeLabel,
getCharacterTypeLabel,
getSkillTypeLabel,
getTagTypeLabel,
getWeaponTypeLabel
} from '../../../lib/v2/data/text'
import StarIcon from '../../../components/v2/StarIcon'
import ChibiIcon from '../../../components/v2/ChibiIcon'
import WeaponTypeIcon from '../../../components/v2/WeaponTypeIcon'
import { sortBattlesuitSkill } from '../../../lib/v2/data/utils'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
import { WeaponData } from '../../../lib/honkai3rd/weapons'
import { getWeaponMapByIds } from '../../../server/data/honkai3rd/weapons'
import { StigmataData } from '../../../lib/honkai3rd/stigmata'
import WeaponCard from '../../../components/molecules/WeaponCard'
import StigmataCard from '../../../components/molecules/StigmataCard'
import { getStigmataMapByIds } from '../../../server/data/honkai3rd/stigmata'
type WeaponObjectMap = { [key: string]: WeaponData }
type StigmataObjectMap = { [key: string]: StigmataData }
import { getI18NProps } from '../../../server/i18n'
import Head from '../../../components/atoms/Head'
import { useTranslation } from 'next-i18next'
interface BattlesuitShowPageProps {
battlesuit: BattlesuitData
weaponMap: WeaponObjectMap
stigmataMap: StigmataObjectMap
battlesuit: Battlesuit
}
const BattlesuitShowPage = ({ battlesuit, weaponMap, stigmataMap }: BattlesuitShowPageProps) => {
const BattlesuitShowPage = ({ battlesuit }: BattlesuitShowPageProps) => {
const { t } = useTranslation()
const { locale } = useRouter()
const { label: valkyrieLabel, krLabel } = valkyries.find(aValkyrie => aValkyrie.value === battlesuit.valkyrie) || {
label: battlesuit.valkyrie,
krLabel: battlesuit.valkyrie
}
const valkyrieName = translate(locale, { 'ko-KR': krLabel }, valkyrieLabel)
const battlesuitType = battlesuitTypes.find(aType => aType.value === battlesuit.type)
const battlesuitTypeLabel = battlesuitType
? translate(locale, { 'ko-KR': battlesuitType.krLabel }, battlesuitType.label)
: capitalize(battlesuit.type)
return (
<Honkai3rdLayout>
<Head
title={`${battlesuit.name} - ${t('common.honkai-3rd')} - ${t('common.abyss-lab')}`}
description={`${t('common.honkai-3rd')} ${t(
'battlesuit-show.battlesuit'
)} / ${valkyrieName} / ${battlesuitTypeLabel} / ${battlesuit.features
.map(feature => {
const featureData = battlesuitFeatures.find(aFeature => aFeature.value === feature)
if (featureData == null) {
return feature
}
const { label, krLabel } = featureData
return translate(locale, { 'ko-KR': krLabel }, label)
title={`${battlesuit.fullName} - ${t('common.honkai-3rd')} - ${t('common.abyss-lab')}`}
description={`${t('common.honkai-3rd')} ${t('battlesuit-show.battlesuit')} / ${battlesuit.fullName} / ${
battlesuit.attributeType
} / ${battlesuit.tags
.map(tag => {
return getTagTypeLabel(tag)
})
.join(', ')}`}
canonicalHref={`/honkai3rd/battlesuits/${battlesuit.id}`}
/>
<Box p={2}>
<Heading as="h1">{battlesuit.fullName}</Heading>
<Box p={3}>
<Breadcrumb
items={[
{ href: '/honkai3rd', label: t('common.honkai-3rd') },
{
href: '/honkai3rd/battlesuits',
label: t('common.battlesuits')
},
{
href: `/honkai3rd/battlesuits/${battlesuit.id}`,
label: battlesuit.name
}
]}
/>
<Heading as="h1">{battlesuit.name}</Heading>
<AvatarFigureImage battlesuitId={battlesuit.id} sx={{ height: 400 }} />
<Box mb={3}>
<BattlesuitAvatarIcon battlesuit={battlesuit} />
</Box>
<Card mb={3}>
<Flex sx={{ borderBottom: 'default', p: 1 }}>
<Flex sx={{ alignItems: 'center' }}>
<Box mr={2}>
<StarIcon star={battlesuit.initialStar} />
</Box>
<ChibiIcon id={battlesuit.character} />
<Box ml={1} mr={2}>
{getCharacterTypeLabel(battlesuit.character)}
</Box>
<AttributeIcon attributeType={battlesuit.attributeType} size={30} />
<Box ml={1} mr={2}>
{getAttributeLabel(battlesuit.attributeType)}
</Box>
<WeaponTypeIcon type={battlesuit.weapon} />
<Box ml={1}>{getWeaponTypeLabel(battlesuit.weapon)}</Box>
</Flex>
</Flex>
<Flex sx={{ p: 1 }}>
{battlesuit.tags.map(tag => {
return (
<Flex key={tag} sx={{ mr: 2, alignItems: 'center' }}>
<TagIcon type={tag} />
<Box ml={1}>{getTagTypeLabel(tag)}</Box>
</Flex>
)
})}
</Flex>
</Card>
<Heading as="h2">Skills</Heading>
{battlesuit.skills.sort(sortBattlesuitSkill).map(skill => {
return (
<Card key={skill.id} mb={3}>
<Box sx={{ borderBottom: 'default', p: 1 }}>
<Flex sx={{ alignItems: 'center' }}>
<AvatarSkillIcon icon={skill.icon} />
<Heading as="h3" sx={{ ml: 1, mb: 0 }}>
<FormattedText>{skill.name}</FormattedText>
</Heading>
{skill.tags.length > 0 && (
<Flex sx={{ ml: 1 }}>
{skill.tags.map((tag, index) => {
return <SkillTagItem key={index} tag={tag} />
})}
</Flex>
)}
</Flex>
<Box>{getSkillTypeLabel(skill.skillType)}</Box>
</Box>
<Box sx={{ whiteSpace: 'pre-wrap', borderBottom: 'default', p: 1 }}>
<FormattedText>{formatSkillInfo(skill)}</FormattedText>
</Box>
{skill.subSkills.map(subSkill => {
return (
<Fragment key={subSkill.id}>
<Box sx={{ borderBottom: 'default', p: 1 }}>
<Flex sx={{ alignItems: 'center' }}>
<AvatarSubSkillIcon icon={subSkill.icon} />
<Heading as="h4" sx={{ ml: 1, mb: 0 }}>
<FormattedText>{subSkill.name}</FormattedText>
</Heading>
{subSkill.unlockStar.localeCompare(battlesuit.initialStar) > 0 && (
<Box ml={2}>
<StarIcon star={subSkill.unlockStar} />
</Box>
)}
{subSkill.tags.length > 0 && (
<Flex sx={{ ml: 2 }}>
{subSkill.tags.map((tag, index) => {
return <SkillTagItem key={index} tag={tag} />
})}
</Flex>
)}
</Flex>
</Box>
<Box
sx={{
position: 'relative',
overflow: 'hidden',
width: '100%',
maxWidth: [300, 400]
whiteSpace: 'pre-wrap',
borderBottom: 'default',
'&:last-child': { borderBottom: 'none' },
p: 1
}}
>
<Image
alt={battlesuit.name}
src={`${assetsBucketBaseUrl}/honkai3rd/battlesuits/${battlesuit.id}.png`}
width={400}
height={400}
/>
</Box>
</Box>
<Card
sx={{
overflow: 'hidden'
}}
mb={3}
>
<Box p={2} sx={{ borderBottom: 'default' }}>
<PageLink
href={{
pathname: '/honkai3rd/battlesuits',
query: { valkyrie: battlesuit.valkyrie }
}}
>
<ValkyrieLabel valkyrie={battlesuit.valkyrie} />
</PageLink>
</Box>
<Box p={2} sx={{ borderBottom: 'default' }}>
<PageLink
href={{
pathname: '/honkai3rd/battlesuits',
query: { valkyrie: battlesuit.type }
}}
>
<TypeLabel type={battlesuit.type} />
</PageLink>
</Box>
<Flex p={2} sx={{ flexWrap: 'wrap', rowGap: 1 }}>
{battlesuit.features.map(feature => {
return (
<Box mr={2} key={feature}>
<PageLink
href={{
pathname: '/honkai3rd/battlesuits',
query: { feature: feature }
}}
>
<BattlesuitFeatureLabel feature={feature} />
</PageLink>
</Box>
)
})}
</Flex>
</Card>
{battlesuit.equipment != null && (
<Card
sx={{
mb: 3,
p: 2,
overflow: 'hidden'
}}
>
{battlesuit.equipment.map(equipmentItem => {
return (
<Box key={equipmentItem.type}>
<Heading
as="h3"
sx={{
mb: 1
}}
>
{t(`battlesuit-show.equipmentTypes.${equipmentItem.type}`)}
</Heading>
<Flex sx={{ mx: -1 }}>
{equipmentItem.weapon != null && <WeaponCard size="sm" weapon={weaponMap[equipmentItem.weapon]} />}
{equipmentItem.stigmataTop != null && (
<StigmataCard size="sm" stigmata={stigmataMap[equipmentItem.stigmataTop]} />
)}
{equipmentItem.stigmataMid != null && (
<StigmataCard size="sm" stigmata={stigmataMap[equipmentItem.stigmataMid]} />
)}
{equipmentItem.stigmataBot != null && (
<StigmataCard size="sm" stigmata={stigmataMap[equipmentItem.stigmataBot]} />
)}
</Flex>
<FormattedText>{formatSubSkillInfo(subSkill)}</FormattedText>
</Box>
</Fragment>
)
})}
</Card>
)}
<BattlesuitSkillGroupCard heading={t('battlesuit-show.leader')} skillGroup={battlesuit.leader} />
<BattlesuitSkillGroupCard heading={t('battlesuit-show.passive')} skillGroup={battlesuit.passive} />
<BattlesuitSkillGroupCard heading={t('battlesuit-show.evasion')} skillGroup={battlesuit.evasion} />
<BattlesuitSkillGroupCard heading={t('battlesuit-show.special-attack')} skillGroup={battlesuit.special} />
<BattlesuitSkillGroupCard heading={t('battlesuit-show.ultimate')} skillGroup={battlesuit.ultimate} />
<BattlesuitSkillGroupCard heading={t('battlesuit-show.basic-attack')} skillGroup={battlesuit.basic} />
{battlesuit.sp != null && (
<BattlesuitSkillGroupCard heading={t('battlesuit-show.sp-skill')} skillGroup={battlesuit.sp} />
)}
)
})}
{/* <pre>{JSON.stringify(battlesuit, null, 2)}</pre> */}
</Box>
</Honkai3rdLayout>
)
@@ -215,103 +164,31 @@ const BattlesuitShowPage = ({ battlesuit, weaponMap, stigmataMap }: BattlesuitSh
export default BattlesuitShowPage
export async function getStaticProps({ params, locale }: NextPageContext & { params: { battlesuitId: string } }) {
const battlesuit = getBattlesuitById(params.battlesuitId, locale)!
const { weaponIds, stigmataIds } = (battlesuit.equipment || []).reduce<{
weaponIds: string[]
stigmataIds: string[]
}>(
(acc, equipmentItem) => {
acc.weaponIds.push(equipmentItem.weapon)
acc.stigmataIds.push(equipmentItem.stigmataTop, equipmentItem.stigmataMid, equipmentItem.stigmataBot)
return acc
},
{ weaponIds: [], stigmataIds: [] }
)
const weaponMap = getWeaponMapByIds(weaponIds, locale)
const stigmataMap = getStigmataMapByIds(stigmataIds, locale)
export async function getStaticProps({ locale, params }: NextPageContext & { params: { battlesuitId: string } }) {
const battlesuit = loadBattlesuitData(params.battlesuitId)
return {
props: {
battlesuit,
weaponMap,
stigmataMap,
...(await getI18NProps(locale))
}
props: { battlesuit, ...(await getI18NProps(locale)) }
}
}
export async function getStaticPaths() {
const battlesuitCatalog = loadBattlesuitCatalog()
return {
paths: generateI18NPaths(
listBattlesuits().map(battlesuit => {
paths: battlesuitCatalog.map(catalogItem => {
return {
params: { battlesuitId: battlesuit.id }
params: { battlesuitId: catalogItem.id }
}
})
),
}),
fallback: false
}
}
interface BattlesuitSkillGroupCardProps {
heading: string
skillGroup: BattlesuitSkillGroup
interface SkillTagBoxProps {
tag: SkillTagItem
}
const BattlesuitSkillGroupCard = ({ heading, skillGroup }: BattlesuitSkillGroupCardProps) => {
return (
<Card mb={3}>
<Box
sx={{
p: 2,
borderBottom: 'default'
}}
>
<Heading as="h2" mb={1}>
{skillGroup.core.name}
</Heading>
<SecondaryLabel>{heading}</SecondaryLabel>
</Box>
<Paragraph p={2} sx={{ whiteSpace: 'pre-wrap', borderBottom: 'default' }}>
{skillGroup.core.description}
</Paragraph>
{skillGroup.subskills.map(subskill => {
return (
<React.Fragment key={subskill.name}>
<Heading as="h3" p={2} m={0} sx={{ borderBottom: 'default' }}>
<Flex sx={{ alignItems: 'center' }}>
<Text>{subskill.name}</Text>
{subskill.requiredRank != null ? (
/^[0-9]/.test(subskill.requiredRank) ? (
<Box ml={2} sx={{ height: 30, lineHeight: '30px', fontSize: 2 }}>
{subskill.requiredRank.slice(0, 1)}
</Box>
) : (
<BattlesuitRankIcon rank={subskill.requiredRank} size={30} ml={2} />
)
) : (
<Box sx={{ height: 30 }} />
)}
</Flex>
</Heading>
<Paragraph
p={2}
sx={{
whiteSpace: 'pre-wrap',
borderBottom: 'default',
'&:last-child': { borderBottom: 'none' }
}}
>
{subskill.description}
</Paragraph>
</React.Fragment>
)
})}
</Card>
)
const SkillTagItem = ({ tag }: SkillTagBoxProps) => {
return <TagIcon type={tag.type} strength={tag.strength} comment={tag.comment} size="sm" />
}
+23 -198
View File
@@ -1,76 +1,22 @@
/** @jsxImportSource theme-ui */
import { Box, Heading, Flex, Button } from '@theme-ui/components'
import { useCallback, useMemo, useState } from 'react'
import FilterButton from '../../../components/atoms/FilterButton'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
import { listBattlesuits } from '../../../server/data/honkai3rd/battlesuits'
import { BattlesuitData, battlesuitFeatures, battlesuitTypes, valkyries } from '../../../lib/honkai3rd/battlesuits'
import BattlesuitCard from '../../../components/molecules/BattlesuitCard'
import { useRouter } from 'next/router'
import { Box } from '@theme-ui/components'
import { NextPageContext } from 'next'
import { getI18NProps } from '../../../server/i18n'
import { translate, useTranslation } from '../../../lib/i18n'
import { Flex, Link } from 'theme-ui'
import { loadBattlesuitCatalog } from '../../../lib/v2/server/loadData'
import { BattlesuitCatalogItem } from '../../../lib/v2/data/types'
import BattlesuitCatalogItemCard from '../../../components/v2/BattlesuitCatalogItemCard'
import Head from '../../../components/atoms/Head'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
type BattlesuitListItemData = Pick<BattlesuitData, 'id' | 'name' | 'features' | 'type' | 'valkyrie'>
import { useTranslation } from 'next-i18next'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
import { getI18NProps } from '../../../server/i18n'
interface BattlesuitListPageProps {
battlesuits: BattlesuitListItemData[]
battlesuitCatalog: BattlesuitCatalogItem[]
}
const featureFilterOptions: {
value: string
label: string
icon?: string
krLabel?: string
}[] = [{ value: 'all', label: 'All', krLabel: '전체' }, ...battlesuitFeatures]
const valkyrieFilterOptions: {
value: string
label: string
icon?: string
krLabel?: string
}[] = [{ value: 'all', label: 'All', krLabel: '전체' }, ...battlesuitTypes, ...valkyries]
const BattlesuitListPage = ({ battlesuits }: BattlesuitListPageProps) => {
const { query, locale } = useRouter()
const BattlesuitListPage = ({ battlesuitCatalog }: BattlesuitListPageProps) => {
const { t } = useTranslation()
const [hiddenFilters, setHiddenFilters] = useState(true)
const closeFilters = useCallback(() => {
setHiddenFilters(true)
}, [])
const valkyrieFilter = useMemo(() => {
if (query.valkyrie == null) {
return 'all'
}
return typeof query.valkyrie === 'string' ? query.valkyrie : query.valkyrie[0]
}, [query])
const featureFilter = useMemo(() => {
if (query.feature == null) {
return 'all'
}
return typeof query.feature === 'string' ? query.feature : query.feature[0]
}, [query])
const battlesuitsFiteredByValkyrie = useMemo(() => {
return battlesuits.filter(battlesuit => isValkyrieFilterMatching(battlesuit, valkyrieFilter))
}, [battlesuits, valkyrieFilter])
const availableFeatureSet = battlesuitsFiteredByValkyrie.reduce((set, battlesuit) => {
battlesuit.features.forEach(feature => set.add(feature))
return set
}, new Set())
const battlesuitsFilteredByFeature = useMemo(() => {
return battlesuitsFiteredByValkyrie.filter(battlesuit => isFeatureFilterMatching(battlesuit, featureFilter))
}, [battlesuitsFiteredByValkyrie, featureFilter])
return (
<Honkai3rdLayout>
<Head
@@ -78,89 +24,26 @@ const BattlesuitListPage = ({ battlesuits }: BattlesuitListPageProps) => {
description={t('battlesuits-list.description')}
canonicalHref={`/honkai3rd/battlesuits`}
/>
<Box p={3}>
<Box p={2}>
<Breadcrumb
items={[
{ href: '/honkai3rd', label: t('common.honkai-3rd') },
{
href: '/honkai3rd/battlesuits',
href: '/honkai3rd/v2/battlesuits',
label: t('common.battlesuits')
}
]}
/>
<Heading as="h1">{t('battlesuits-list.heading')}</Heading>
<Box sx={{ display: ['block', 'none'], mb: 2 }}>
<Button
className={hiddenFilters ? '' : 'active'}
sx={{ px: 2, py: 1 }}
onClick={() => {
setHiddenFilters(previousValue => !previousValue)
}}
>
{t('battlesuits-list.filters')}
</Button>
</Box>
<Box
className={hiddenFilters ? 'hidden' : ''}
sx={{
display: 'block',
'&.hidden': {
display: ['none', 'block']
}
}}
>
<Heading as="h3">{t('battlesuits-list.filter-by-valkyries')}</Heading>
<Flex mb={2} sx={{ flexWrap: 'wrap' }}>
{valkyrieFilterOptions.map(({ value, label, icon, krLabel }) => {
<h1>{t('battlesuits-list.heading')}</h1>
<Flex sx={{ flexWrap: 'wrap' }}>
{battlesuitCatalog.map(battlesuit => {
return (
<FilterButton
key={value}
active={value === valkyrieFilter}
href={`?valkyrie=${value}`}
label={translate(locale, { 'ko-KR': krLabel }, label)}
icon={icon}
m={1}
close={closeFilters}
/>
)
})}
</Flex>
<Heading as="h3">{t('battlesuits-list.filter-by-features')}</Heading>
<Flex mb={2} sx={{ flexWrap: 'wrap' }}>
{featureFilterOptions
.filter(filterOption => {
if (filterOption.value === 'all') {
return true
}
return availableFeatureSet.has(filterOption.value)
})
.map(({ value, label, icon, krLabel }) => {
return (
<FilterButton
key={value}
active={value === featureFilter}
href={`?valkyrie=${valkyrieFilter}&feature=${value}`}
label={translate(locale, { 'ko-KR': krLabel }, label)}
icon={icon}
m={1}
close={closeFilters}
/>
)
})}
</Flex>
<Box key={battlesuit.id}>
<Link href={`/honkai3rd/v2/battlesuits/${battlesuit.id}`}>
<BattlesuitCatalogItemCard battlesuit={battlesuit} />
</Link>
</Box>
<Flex
sx={{
flexWrap: 'wrap',
justifyContent: 'space-around'
}}
>
{battlesuitsFilteredByFeature.map(battlesuit => {
return <BattlesuitCard key={battlesuit.id} battlesuit={battlesuit} />
)
})}
</Flex>
</Box>
@@ -171,67 +54,9 @@ const BattlesuitListPage = ({ battlesuits }: BattlesuitListPageProps) => {
export default BattlesuitListPage
export async function getStaticProps({ locale }: NextPageContext) {
return {
props: {
battlesuits: listBattlesuits(locale).map(battlesuit => {
return {
id: battlesuit.id,
name: battlesuit.name,
features: battlesuit.features,
type: battlesuit.type,
valkyrie: battlesuit.valkyrie
}
}),
...(await getI18NProps(locale))
}
}
}
const battlesuitCatalog = loadBattlesuitCatalog()
function isValkyrieFilterMatching(battlesuit: BattlesuitListItemData, valkyrieFilter: string) {
switch (valkyrieFilter) {
case 'mecha':
case 'biologic':
case 'psychic':
case 'quantum':
case 'imaginary':
return battlesuit.type === valkyrieFilter
case 'kiana':
case 'mei':
case 'bronya':
case 'himeko':
case 'theresa':
case 'fuhua':
case 'rita':
case 'sakura':
case 'kallen':
case 'olenyevas':
case 'seele':
case 'durandal':
case 'fischl':
case 'elysia':
case 'mobius':
case 'raven':
case 'carole':
case 'asuka':
case 'pardofelis':
case 'aponia':
case 'eden':
case 'griseo':
case 'sushang':
case 'ai':
case 'susannah':
case 'misteln':
case 'prometheus':
return battlesuit.valkyrie === valkyrieFilter
default:
case 'all':
return true
return {
props: { battlesuitCatalog, ...(await getI18NProps(locale)) }
}
}
function isFeatureFilterMatching(battlesuit: BattlesuitListItemData, featureFilter: string) {
if (featureFilter === 'all') {
return true
}
return battlesuit.features.some(feature => feature === featureFilter)
}
+74 -121
View File
@@ -1,164 +1,117 @@
/** @jsxImportSource theme-ui */
import {
Box,
Card,
Flex,
Heading,
Paragraph,
Image,
} from '@theme-ui/components'
import { NextPageContext } from 'next'
import React from 'react'
import BattlesuitFeatureLabel from '../../../components/atoms/BattlesuitFeatureLabel'
import SecondaryLabel from '../../../components/atoms/SecondaryLabel'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
import { ElfData } from '../../../lib/honkai3rd/elfs'
import { getElfById, listElfs } from '../../../server/data/honkai3rd/elfs'
import { generateI18NPaths, getI18NProps } from '../../../server/i18n'
import { translate, useTranslation } from '../../../lib/i18n'
import { useRouter } from 'next/router'
import Head from '../../../components/atoms/Head'
import { battlesuitFeatures } from '../../../lib/honkai3rd/battlesuits'
import { Box, Card, Flex, Heading } from 'theme-ui'
import { useMemo } from 'react'
import { Elf, ElfSkill } from '../../../lib/v2/data/types'
import { loadElfCatalog, loadElfData } from '../../../lib/v2/server/loadData'
import SquareImage from '../../../components/v2/SquareImage'
import TagIcon from '../../../components/v2/TagIcon'
import { assetsBucketBaseUrl } from '../../../lib/consts'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
import { ListMap } from '../../../lib/utils'
import FormattedText from '../../../components/v2/FormattedText'
import { formatSubSkillInfo } from '../../../lib/v2/data/formatText'
interface ElfShowPageProps {
elf: ElfData
elf: Elf
}
const ElfShowPage = ({ elf }: ElfShowPageProps) => {
const { locale } = useRouter()
const { t } = useTranslation()
const skillRows = useMemo(() => {
const rowSkillListMap = elf.skills.reduce<ListMap<string, ElfSkill>>((map, skill) => {
map.set(skill.row.toString(), skill)
return map
}, new ListMap())
return [...rowSkillListMap.values()]
}, [elf.skills])
return (
<Honkai3rdLayout>
<Head
title={`${t('common.honkai-3rd')}: ${elf.name} - ${t(
'common.abyss-lab'
)}`}
description={`${t('common.honkai-3rd')} ${t(
'elfs-show.elf'
)} / ${'⭐'.repeat(elf.baseRank)} / ${elf.features
.map((feature) => {
const featureData = battlesuitFeatures.find(
(aFeature) => aFeature.value === feature
)
if (featureData == null) {
return feature
}
const { label, krLabel } = featureData
return translate(locale, { 'ko-KR': krLabel }, label)
})
.join(', ')}`}
canonicalHref={`/honkai3rd/elfs/${elf.id}`}
<Box>
<Heading as="h1">{elf.fullName}</Heading>
<SquareImage
src={`${assetsBucketBaseUrl}/raw/elfcardfigures/${elf.figureImage}.png`}
originalSize={720}
size={480}
/>
<Box p={3}>
<Breadcrumb
items={[
{ href: '/honkai3rd', label: t('common.honkai-3rd') },
{ href: '/honkai3rd/elfs', label: t('common.elfs') },
{
href: `/honkai3rd/elfs/${elf.id}`,
label: elf.name,
},
]}
/>
<Heading as='h1'>{elf.name}</Heading>
<Box mb={3}>
<Box
sx={{
position: 'relative',
overflow: 'hidden',
width: [300, 400],
height: [300, 400],
borderRadius: 4,
}}
>
<Image
alt={elf.name}
src={`${assetsBucketBaseUrl}/honkai3rd/elfs/${elf.id}.png`}
/>
</Box>
</Box>
<Card mb={3}>
<Box sx={{ p: 2, borderBottom: 'default' }}>
{'⭐'.repeat(elf.baseRank)}
<Box sx={{ p: 1, borderBottom: 'default' }}>
<Flex>
{elf.tags.map(tag => {
return (
<Box key={tag.type} mr={1}>
<TagIcon type={tag.type} />
</Box>
<Flex p={2}>
{elf.features.map((feature) => (
<Box key={feature} mr={2}>
<BattlesuitFeatureLabel feature={feature} />
</Box>
))}
)
})}
</Flex>
</Box>
<Box p={1}>
Ult CD: {elf.ultSkillCd} / Ult SP Cost: {elf.ultSkillCost}
</Box>
</Card>
{elf.skillRows.map((row, rowIndex) => {
<Heading as="h2">Skills</Heading>
{skillRows.map((row, index) => {
return (
<Card key={rowIndex} mb={3}>
{row.map((item, columnIndex) => {
<Card key={index} mb={2}>
{row.map(skill => {
return (
<React.Fragment key={columnIndex}>
<Box sx={{ p: 2, borderBottom: 'default' }}>
<Heading
as={columnIndex === 0 ? 'h2' : 'h3'}
sx={{ mb: 1 }}
>
{item.name}
</Heading>
<SecondaryLabel>
{t(`elfs-show.${item.type}`)}{' '}
{'⭐'.repeat(item.requiredRank)}
</SecondaryLabel>
</Box>
<Paragraph
<Box
key={skill.id}
sx={{
p: 2,
borderBottom: 'default',
'&:last-child': { borderBottom: 'none' },
whiteSpace: 'pre-wrap',
'&:last-child': {
borderBottom: 'none'
}
}}
>
{item.description}
</Paragraph>
</React.Fragment>
<Box sx={{ borderBottom: 'default', p: 1 }}>
<Flex sx={{ alignItems: 'center' }}>
<Box sx={{ flexShrink: 0 }}>
<SquareImage
src={`${assetsBucketBaseUrl}/raw/elf_skill_icon/${skill.icon}.png`}
originalSize={40}
/>
</Box>
<Heading as={'h3'} sx={{ my: 0, ml: 1 }}>
<FormattedText>{skill.name}</FormattedText>
</Heading>
{/* {skill.id} */}
<Box sx={{ color: 'textMuted', ml: 1 }}>({skill.skillType})</Box>
</Flex>
</Box>
<Box sx={{ p: 1 }}>
<FormattedText>{formatSubSkillInfo(skill)}</FormattedText>
</Box>
</Box>
)
})}
</Card>
)
})}
</Box>
</Honkai3rdLayout>
)
}
export default ElfShowPage
export async function getStaticProps({
params,
locale,
}: NextPageContext & { params: { elfId: string } }) {
const elf = getElfById(params.elfId, locale)
export async function getStaticProps({ locale, params }: NextPageContext & { params: { elfId: string } }) {
const elf = loadElfData(params.elfId)
return {
props: { elf, ...(await getI18NProps(locale)) },
props: { elf }
}
}
export async function getStaticPaths() {
const elfCatalog = loadElfCatalog()
return {
paths: generateI18NPaths(
listElfs().map((elf) => {
paths: elfCatalog.map(catalogItem => {
return {
params: { elfId: elf.id },
params: { elfId: catalogItem.id }
}
})
),
fallback: false,
}),
fallback: false
}
}
+39 -42
View File
@@ -1,64 +1,61 @@
/** @jsxImportSource theme-ui */
import { Box, Heading, Flex } from '@theme-ui/components'
import { Box, Card } from '@theme-ui/components'
import { NextPageContext } from 'next'
import { pick } from 'ramda'
import ElfCard from '../../../components/molecules/ElfCard'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
import { ElfData } from '../../../lib/honkai3rd/elfs'
import { getI18NProps } from '../../../server/i18n'
import { listElfs } from '../../../server/data/honkai3rd/elfs'
import { useTranslation } from '../../../lib/i18n'
import Head from '../../../components/atoms/Head'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
import { Flex, Link } from 'theme-ui'
import { ElfCatalogItem } from '../../../lib/v2/data/types'
import { loadElfCatalog } from '../../../lib/v2/server/loadData'
import RarityBar from '../../../components/v2/RarityBar'
import ElfIcon from '../../../components/v2/ElfIcon'
interface ElfListPageProps {
elfs: Pick<ElfData, 'id' | 'name'>[]
elfs: ElfCatalogItem[]
}
const ElfListPage = ({ elfs }: ElfListPageProps) => {
const { t } = useTranslation()
return (
<Honkai3rdLayout>
<Head
title={`${t('common.elfs')} - ${t('common.honkai-3rd')} - ${t(
'common.abyss-lab'
)}`}
description={t('elfs-list.description')}
canonicalHref={`/honkai3rd/elfs`}
/>
<Box p={3}>
<Breadcrumb
items={[
{ href: '/honkai3rd', label: t('common.honkai-3rd') },
{ href: '/honkai3rd/elfs', label: t('common.elfs') },
]}
/>
<Box>
<h1>Elfs</h1>
<Flex sx={{ flexWrap: 'wrap' }}>
{elfs
.sort((a, b) => {
const aId = getId(a.id)
const bId = getId(b.id)
return bId - aId
<Heading as='h1'>{t('elfs-list.elfs')}</Heading>
<Flex
sx={{
flexWrap: 'wrap',
justifyContent: 'space-around',
}}
function getId(id: string): number {
return parseInt(id)
}
})
.map(elf => {
return (
<Box key={elf.id} m={2}>
<Link href={`/v2-pre/elfs/${elf.id}`}>
<Card p={1}>
<Box>
<ElfIcon icon={elf.cardIcon} />
<RarityBar rarity={elf.rarity} />
</Box>
<Box
sx={{ textAlign: 'center', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}
>
{elfs.map((elf) => {
return <ElfCard key={elf.id} elf={elf} />
{elf.fullName}
</Box>
</Card>
</Link>
</Box>
)
})}
</Flex>
</Box>
</Honkai3rdLayout>
)
}
export default ElfListPage
export async function getStaticProps({ locale }: NextPageContext) {
const elfs = loadElfCatalog()
return {
props: {
elfs: listElfs(locale).map((elf) => pick(['id', 'name', 'krName'], elf)),
...(await getI18NProps(locale)),
},
props: { elfs }
}
}
@@ -1,139 +0,0 @@
/** @jsxImportSource theme-ui */
import { Box, Heading, Image } from '@theme-ui/components'
import { NextPageContext } from 'next'
import Breadcrumb from '../../../../components/organisms/Breadcrumb'
import { BattlesuitData } from '../../../../lib/honkai3rd/battlesuits'
import { generateI18NPaths, getI18NProps } from '../../../../server/i18n'
import { getBattlesuitById } from '../../../../server/data/honkai3rd/battlesuits'
import { useTranslation } from '../../../../lib/i18n'
import Head from '../../../../components/atoms/Head'
import { assetsBucketBaseUrl } from '../../../../lib/consts'
import Honkai3rdLayout from '../../../../components/layouts/Honkai3rdLayout'
import { WeaponData } from '../../../../lib/honkai3rd/weapons'
import { getWeaponMapByIds } from '../../../../server/data/honkai3rd/weapons'
import { StigmataData } from '../../../../lib/honkai3rd/stigmata'
import { getStigmataMapByIds } from '../../../../server/data/honkai3rd/stigmata'
import { getSignetGroupById } from '../../../../server/data/honkai3rd/elysianRealm'
import { erBattlesuits, SignetSet } from '../../../../lib/honkai3rd/elysianRealm'
import SignetCard from '../../../../components/molecules/SignetCard'
type WeaponObjectMap = { [key: string]: WeaponData }
type StigmataObjectMap = { [key: string]: StigmataData }
interface BattlesuitShowPageProps {
battlesuit: BattlesuitData
weaponMap: WeaponObjectMap
stigmataMap: StigmataObjectMap
signetSet: SignetSet
}
const BattlesuitShowPage = ({ battlesuit, weaponMap, stigmataMap, signetSet }: BattlesuitShowPageProps) => {
const { t } = useTranslation()
return (
<Honkai3rdLayout>
<Head
title={`${battlesuit.name} (${t('common.elysian-realm')}) - ${t('common.honkai-3rd')} - ${t(
'common.abyss-lab'
)}`}
description={`${t('common.honkai-3rd')} ${t('common.elysian-realm')} ${t('battlesuit-show.battlesuit')}`}
canonicalHref={`/honkai3rd/elysian-realm/battlesuits/${battlesuit.id}`}
/>
<Box p={3}>
<Breadcrumb
items={[
{ href: '/honkai3rd', label: t('common.honkai-3rd') },
{
href: '/honkai3rd/elysian-realm',
label: t('common.elysian-realm')
},
{
href: `/honkai3rd/elysian-realm/battlesuits/${battlesuit.id}`,
label: battlesuit.name
}
]}
/>
<Heading as="h1">{battlesuit.name}</Heading>
<Box mb={3}>
<Box
sx={{
position: 'relative',
overflow: 'hidden',
width: '100%',
maxWidth: [300, 400]
}}
>
<Image
alt={battlesuit.name}
src={`${assetsBucketBaseUrl}/honkai3rd/battlesuits/${battlesuit.id}.png`}
width={400}
height={400}
/>
</Box>
</Box>
<Heading as="h2">{t('elysian-realm.exclusive-signets')}</Heading>
<Box>
{signetSet.signets.map(signet => {
return <SignetCard key={signet.id} signet={signet} />
})}
</Box>
</Box>
</Honkai3rdLayout>
)
}
export default BattlesuitShowPage
export async function getStaticProps({ params, locale }: NextPageContext & { params: { battlesuitId: string } }) {
try {
const battlesuit = getBattlesuitById(params.battlesuitId, locale)!
const { weaponIds, stigmataIds } = (battlesuit.equipment || []).reduce<{
weaponIds: string[]
stigmataIds: string[]
}>(
(acc, equipmentItem) => {
acc.weaponIds.push(equipmentItem.weapon)
acc.stigmataIds.push(equipmentItem.stigmataTop, equipmentItem.stigmataMid, equipmentItem.stigmataBot)
return acc
},
{ weaponIds: [], stigmataIds: [] }
)
const weaponMap = getWeaponMapByIds(weaponIds)
const stigmataMap = getStigmataMapByIds(stigmataIds)
const signetGroup = getSignetGroupById('elysia', locale)
const signetSet = signetGroup.sets.find(set => {
return set.id === `elysia-${params.battlesuitId}`
})
return {
props: {
battlesuit,
weaponMap,
stigmataMap,
signetSet,
...(await getI18NProps(locale))
}
}
} catch (error) {
console.log(error)
}
}
export async function getStaticPaths() {
return {
paths: generateI18NPaths(
erBattlesuits.map(battlesuitId => {
return {
params: { battlesuitId: battlesuitId }
}
})
),
fallback: false
}
}
-198
View File
@@ -1,198 +0,0 @@
/** @jsxImportSource theme-ui */
import { Box, Heading, Flex, Card, Text } from '@theme-ui/components'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
import { getBattlesuitMapByIds } from '../../../server/data/honkai3rd/battlesuits'
import { BattlesuitData } from '../../../lib/honkai3rd/battlesuits'
import BattlesuitCard from '../../../components/molecules/BattlesuitCard'
import { NextPageContext } from 'next'
import { getI18NProps } from '../../../server/i18n'
import { useTranslation } from '../../../lib/i18n'
import Head from '../../../components/atoms/Head'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
import { erVersions } from '../../../lib/honkai3rd/elysianRealm'
import SquareImageBox from '../../../components/atoms/SquareImageBox'
import { assetsBucketBaseUrl } from '../../../lib/consts'
import PageLink from '../../../components/atoms/PageLink'
import SignetGroupNavigator from '../../../components/organisms/SignetGroupNavigator'
import {
getRemembranceSigils as getRemembranceSigils,
getSupportBattlesuits,
} from '../../../server/data/honkai3rd/elysianRealm'
import Link from 'next/link'
type BattlesuitListItemData = Pick<
BattlesuitData,
'id' | 'name' | 'features' | 'type' | 'valkyrie'
>
interface BattlesuitListPageProps {
battlesuitMap: { [id: string]: BattlesuitListItemData }
supportBattlesuits: { id: string; name: string }[]
remembranceSigils: { id: string; name: string }[]
}
const ElysianRealmIndexPage = ({
battlesuitMap,
supportBattlesuits,
remembranceSigils,
}: BattlesuitListPageProps) => {
const { t } = useTranslation()
return (
<Honkai3rdLayout>
<Head
title={`${t('common.elysian-realm')} - ${t('common.honkai-3rd')} - ${t(
'common.abyss-lab'
)}`}
description={t('common.elysian-realm')}
canonicalHref={`/honkai3rd/elysian-realm`}
/>
<Box p={3}>
<Breadcrumb
items={[
{ href: '/honkai3rd', label: t('common.honkai-3rd') },
{
href: '/honkai3rd/elysian-realm',
label: t('common.elysian-realm'),
},
]}
/>
<Heading as='h1'>{t('common.elysian-realm')}</Heading>
<Heading as='h2'>{t('common.battlesuits')}</Heading>
<Box sx={{ mb: 3 }}>
{erVersions.map((erVersion) => {
return (
<Box key={erVersion.version} sx={{ mb: 2 }}>
<Heading as='h3' m={0}>
{erVersion.version}
</Heading>
<Flex sx={{ flexWrap: 'wrap' }}>
{erVersion.battlesuits.map((battlesuitId) => {
return (
<Box
key={battlesuitId}
sx={{ width: [135, 135, 200], m: 1 }}
>
<BattlesuitCard
m={0}
battlesuit={battlesuitMap[battlesuitId]}
size={'sm'}
href={`/honkai3rd/elysian-realm/battlesuits/${battlesuitId}`}
/>
</Box>
)
})}
</Flex>
</Box>
)
})}
</Box>
<Box sx={{ mb: 3 }}>
<Heading as='h2'>{t('elysian-realm.signets')}</Heading>
<SignetGroupNavigator />
</Box>
<Box sx={{ mb: 3 }}>
<Heading as='h2'>{t('elysian-realm.supports')}</Heading>
<Flex sx={{ flexWrap: 'wrap' }}>
{supportBattlesuits.map((battlesuit) => {
return (
<Box key={battlesuit.id} sx={{ width: [135, 135, 200], m: 1 }}>
<BattlesuitCard
m={0}
battlesuit={battlesuit}
size={'sm'}
href={`/honkai3rd/elysian-realm/support-battlesuits#${battlesuit.id}`}
/>
</Box>
)
})}
</Flex>
</Box>
<Box sx={{ mb: 3 }}>
<Heading as='h2'>{t('elysian-realm.remembrance-sigil')}</Heading>
<Flex sx={{ flexWrap: 'wrap' }}>
{remembranceSigils.map(({ id, name }) => {
return (
<Card
key={id}
sx={{ p: 1, m: 1, width: [135, 135, 200] }}
title={name}
>
<PageLink
href={`/honkai3rd/elysian-realm/remembrance-sigils#${id}`}
>
<Flex sx={{ alignItems: 'center' }}>
<SquareImageBox
size={30}
src={`${assetsBucketBaseUrl}/honkai3rd/elysian-realm/remembrance-sigils/${id}.png`}
/>
<Text
sx={{
ml: 2,
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
}}
>
{name}
</Text>
</Flex>
</PageLink>
</Card>
)
})}
</Flex>
</Box>
<Box>
<Link href='/honkai3rd/elysian-realm/utils/gen-guide'>
Guide Generator (KR Only)
</Link>
</Box>
</Box>
</Honkai3rdLayout>
)
}
export default ElysianRealmIndexPage
export async function getStaticProps({ locale }: NextPageContext) {
const erBattlesuitIds = erVersions.reduce<string[]>((list, version) => {
list.push(...version.battlesuits)
return list
}, [])
const supportBattlesuits = getSupportBattlesuits(locale).map(
({ id, name }) => {
return {
id,
name,
}
}
)
const remembranceSigils = getRemembranceSigils(locale).map(({ id, name }) => {
return {
id,
name,
}
})
return {
props: {
battlesuitMap: getBattlesuitMapByIds(erBattlesuitIds, locale),
supportBattlesuits,
remembranceSigils,
...(await getI18NProps(locale)),
},
}
}
@@ -1,96 +0,0 @@
/** @jsxImportSource theme-ui */
import { Box, Heading, Flex, Card, Paragraph } from '@theme-ui/components'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
import { NextPageContext } from 'next'
import { getI18NProps } from '../../../server/i18n'
import { useTranslation } from '../../../lib/i18n'
import Head from '../../../components/atoms/Head'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
import { RemembranceSigil } from '../../../lib/honkai3rd/elysianRealm'
import SquareImageBox from '../../../components/atoms/SquareImageBox'
import { assetsBucketBaseUrl } from '../../../lib/consts'
import { getRemembranceSigils } from '../../../server/data/honkai3rd/elysianRealm'
interface BattlesuitListPageProps {
remembranceSigils: RemembranceSigil[]
}
const ElysianRealmIndexPage = ({
remembranceSigils,
}: BattlesuitListPageProps) => {
const { t } = useTranslation()
return (
<Honkai3rdLayout>
<Head
title={`${t('elysian-realm.remembrance-sigil')} (${t(
'common.elysian-realm'
)}) - ${t('common.honkai-3rd')} - ${t('common.abyss-lab')}`}
description={`${t('common.elysian-realm')} ${t(
'elysian-realm.supports'
)}`}
canonicalHref={`/honkai3rd/elysian-realm/remembrance-sigils`}
/>
<Box p={3}>
<Breadcrumb
items={[
{ href: '/honkai3rd', label: t('common.honkai-3rd') },
{
href: '/honkai3rd/elysian-realm',
label: t('common.elysian-realm'),
},
{
href: '/honkai3rd/elysian-realm/remembrance-sigils',
label: t('elysian-realm.remembrance-sigil'),
},
]}
/>
<Heading as='h1' mb={3}>
{t('elysian-realm.remembrance-sigil')}
</Heading>
<Box sx={{ mb: 3 }}>
{remembranceSigils.map(({ id, name, description }) => {
return (
<Card key={id} sx={{ p: 2, mb: 2 }} id={id}>
<Flex>
<SquareImageBox
size={50}
alt={name}
src={`${assetsBucketBaseUrl}/honkai3rd/elysian-realm/remembrance-sigils/${id}.png`}
mr={2}
/>
<Box>
<Heading as='h3' mb={2}>
{name}
</Heading>
<Paragraph sx={{ whiteSpace: 'pre-wrap' }}>
{description}
</Paragraph>
</Box>
</Flex>
</Card>
)
})}
</Box>
</Box>
</Honkai3rdLayout>
)
}
export default ElysianRealmIndexPage
export async function getStaticProps({ locale }: NextPageContext) {
const remembranceSigils = getRemembranceSigils(locale)
return {
props: {
remembranceSigils,
...(await getI18NProps(locale)),
},
}
}
@@ -1,132 +0,0 @@
/** @jsxImportSource theme-ui */
import { Box, Heading, Flex, Text } from '@theme-ui/components'
import { NextPageContext } from 'next'
import Breadcrumb from '../../../../components/organisms/Breadcrumb'
import { generateI18NPaths, getI18NProps } from '../../../../server/i18n'
import { useTranslation } from '../../../../lib/i18n'
import Head from '../../../../components/atoms/Head'
import Honkai3rdLayout from '../../../../components/layouts/Honkai3rdLayout'
import {
PopulatedSignetGroup,
signetGroups,
} from '../../../../lib/honkai3rd/elysianRealm'
import { getSignetGroupById } from '../../../../server/data/honkai3rd/elysianRealm'
import SignetCard from '../../../../components/molecules/SignetCard'
import SignetGroupNavigator from '../../../../components/organisms/SignetGroupNavigator'
import SquareImageBox from '../../../../components/atoms/SquareImageBox'
import { assetsBucketBaseUrl } from '../../../../lib/consts'
import PageLink from '../../../../components/atoms/PageLink'
interface SignetShowPageProps {
signetId: string
signetGroup: PopulatedSignetGroup
}
const SignetShowPage = ({ signetId, signetGroup }: SignetShowPageProps) => {
const { t } = useTranslation()
return (
<Honkai3rdLayout>
<Head
title={`${signetGroup.name} ${t('elysian-realm.signets')} (${t(
'common.elysian-realm'
)}) - ${t('common.honkai-3rd')} - ${t('common.abyss-lab')}`}
description={`${t('common.honkai-3rd')} ${signetGroup.name} ${t(
'elysian-realm.signets'
)}`}
canonicalHref={`/honkai3rd/elysian-realm/signets/${signetId}`}
/>
<Box p={3}>
<Breadcrumb
items={[
{ href: '/honkai3rd', label: t('common.honkai-3rd') },
{
href: '/honkai3rd/elysian-realm',
label: t('common.elysian-realm'),
},
{
href: `/honkai3rd/elysian-realm/signets/${signetId}`,
label: signetGroup.name,
},
]}
/>
<Box sx={{ mb: 4 }}>
<SignetGroupNavigator />
</Box>
<Heading as='h1' sx={{ mb: 4, display: 'flex', alignItems: 'center' }}>
<SquareImageBox
mr={2}
size={40}
alt={signetGroup.name}
src={`${assetsBucketBaseUrl}/honkai3rd/elysian-realm/signets/${signetGroup.id}.png`}
/>{' '}
{signetGroup.altName}({signetGroup.name}) {t('elysian-realm.signets')}
</Heading>
{signetGroup.sets.map((set) => {
const signetType = set.id.split('-')[1]
return (
<Box key={set.id} sx={{ mb: 4 }}>
<Heading as='h2' sx={{ mb: 3 }}>
{signetGroup.id === 'elysia' ? (
<PageLink
href={`/honkai3rd/elysian-realm/battlesuits/${signetType}`}
>
<Flex sx={{ alignItems: 'center' }}>
<SquareImageBox
size={30}
mr={2}
src={`${assetsBucketBaseUrl}/honkai3rd/battlesuits/portrait-${signetType}.png`}
/>
<Text>{set.name}</Text>
</Flex>
</PageLink>
) : (
set.name
)}
</Heading>
<Box>
{set.signets.map((signet) => {
return <SignetCard key={signet.id} signet={signet} />
})}
</Box>
</Box>
)
})}
</Box>
</Honkai3rdLayout>
)
}
export default SignetShowPage
export async function getStaticProps({
params,
locale,
}: NextPageContext & { params: { signetId: string } }) {
const { signetId } = params
return {
props: {
signetId,
signetGroup: getSignetGroupById(signetId, locale),
...(await getI18NProps(locale)),
},
}
}
export async function getStaticPaths() {
return {
paths: generateI18NPaths(
signetGroups.map((signet) => {
return {
params: { signetId: signet.id },
}
})
),
fallback: false,
}
}
@@ -1,106 +0,0 @@
/** @jsxImportSource theme-ui */
import {
Box,
Heading,
Flex,
Card,
Paragraph,
Badge,
} from '@theme-ui/components'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
import { NextPageContext } from 'next'
import { getI18NProps } from '../../../server/i18n'
import { useTranslation } from '../../../lib/i18n'
import Head from '../../../components/atoms/Head'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
import { SupportBattlesuit } from '../../../lib/honkai3rd/elysianRealm'
import SquareImageBox from '../../../components/atoms/SquareImageBox'
import { assetsBucketBaseUrl } from '../../../lib/consts'
import { getSupportBattlesuits } from '../../../server/data/honkai3rd/elysianRealm'
interface BattlesuitListPageProps {
supportBattlesuits: SupportBattlesuit[]
}
const ElysianRealmIndexPage = ({
supportBattlesuits,
}: BattlesuitListPageProps) => {
const { t } = useTranslation()
return (
<Honkai3rdLayout>
<Head
title={`${t('elysian-realm.supports')} (${t(
'common.elysian-realm'
)}) - ${t('common.honkai-3rd')} - ${t('common.abyss-lab')}`}
description={`${t('common.elysian-realm')} ${t(
'elysian-realm.supports'
)}`}
canonicalHref={`/honkai3rd/elysian-realm/support-battlesuits`}
/>
<Box p={3}>
<Breadcrumb
items={[
{ href: '/honkai3rd', label: t('common.honkai-3rd') },
{
href: '/honkai3rd/elysian-realm',
label: t('common.elysian-realm'),
},
{
href: '/honkai3rd/elysian-realm/support-battlesuits',
label: t('elysian-realm.supports'),
},
]}
/>
<Heading as='h1' mb={3}>
{t('elysian-realm.supports')}
</Heading>
<Box sx={{ mb: 3 }}>
{supportBattlesuits.map(
({ id, name, skillName, description, cooldown }) => {
return (
<Card key={id} sx={{ p: 2, mb: 2 }} id={id}>
<Flex>
<SquareImageBox
size={50}
alt={name}
src={`${assetsBucketBaseUrl}/honkai3rd/battlesuits/portrait-${id}.png`}
mr={2}
/>
<Box>
<Heading as='h3' mb={0}>
{name} - {skillName}
</Heading>
<Badge variant='secondary'>Cooldown: {cooldown}s</Badge>
<Paragraph sx={{ whiteSpace: 'pre-wrap' }}>
{description}
</Paragraph>
</Box>
</Flex>
</Card>
)
}
)}
</Box>
</Box>
</Honkai3rdLayout>
)
}
export default ElysianRealmIndexPage
export async function getStaticProps({ locale }: NextPageContext) {
const supportBattlesuits = getSupportBattlesuits(locale)
return {
props: {
supportBattlesuits,
...(await getI18NProps(locale)),
},
}
}
@@ -1,97 +0,0 @@
/* eslint-disable @next/next/no-page-custom-font */
import { NextPageContext } from 'next'
import { useRouter } from 'next/router'
import { pick } from 'ramda'
import { Box, Button, Flex, Heading } from 'theme-ui'
import ERGuideGenerator from '../../../../components/organisms/ERGuideGenerator/ERGuideGenerator'
import { BattlesuitData } from '../../../../lib/honkai3rd/battlesuits'
import {
PopulatedSignetGroup,
RemembranceSigil,
} from '../../../../lib/honkai3rd/elysianRealm'
import { WeaponData } from '../../../../lib/honkai3rd/weapons'
import { StigmataData } from '../../../../lib/honkai3rd/stigmata'
import { listBattlesuits } from '../../../../server/data/honkai3rd/battlesuits'
import {
getRemembranceSigils,
getSignetGroupById,
} from '../../../../server/data/honkai3rd/elysianRealm'
import { listStigmata } from '../../../../server/data/honkai3rd/stigmata'
import { listWeapons } from '../../../../server/data/honkai3rd/weapons'
import { getI18NProps } from '../../../../server/i18n'
import Head from '../../../../components/atoms/Head'
import { useTranslation } from 'next-i18next'
interface GenerateGuidePageProps {
weapons: WeaponData[]
stigmata: StigmataData[]
battlesuits: BattlesuitData[]
exSignetGroup: PopulatedSignetGroup
sigils: RemembranceSigil[]
}
const GenerateGuidePage = ({
weapons,
stigmata,
battlesuits,
exSignetGroup,
sigils,
}: GenerateGuidePageProps) => {
const router = useRouter()
const { t } = useTranslation()
return (
<Box>
<Head
title={`ER Guide Generator- ${t('common.honkai-3rd')} - ${t(
'common.abyss-lab'
)}`}
canonicalHref={`/honkai3rd/elysian-realm/utils/gen-guide`}
/>
<Flex m={2} sx={{ alignItems: 'center' }}>
<Button
onClick={() => {
router.push('/honkai3rd')
}}
mr={2}
>
Back
</Button>
<Heading>ER Guide Generator</Heading>
</Flex>
<ERGuideGenerator
weapons={weapons}
stigmata={stigmata}
battlesuits={battlesuits}
exSignetGroup={exSignetGroup}
sigils={sigils}
/>
</Box>
)
}
export default GenerateGuidePage
export async function getStaticProps({ locale }: NextPageContext) {
return {
props: {
weapons: listWeapons('ko-KR').map((weapon) => {
return pick(['name', 'id', 'rarity', 'category'], weapon)
}),
stigmata: listStigmata('ko-KR').map((stigmata) => {
return pick(['name', 'id', 'type'], stigmata)
}),
battlesuits: listBattlesuits('ko-KR').map((battlesuit) => {
return {
id: battlesuit.id,
name: battlesuit.name,
type: battlesuit.type,
valkyrie: battlesuit.valkyrie,
}
}),
exSignetGroup: getSignetGroupById('elysia', 'ko-KR'),
sigils: getRemembranceSigils('ko-KR'),
...(await getI18NProps('ko-KR')),
},
}
}
-180
View File
@@ -1,180 +0,0 @@
/** @jsxImportSource theme-ui */
import { Box, Heading } from '@theme-ui/components'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
import { NextPageContext } from 'next'
import { getI18NProps } from '../../../server/i18n'
import { useTranslation } from '../../../lib/i18n'
import Head from '../../../components/atoms/Head'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { times } from 'ramda'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
const enemyIdList = [
...times((i) => i + 0, 1000).map((i) => {
const stringified = i.toString()
return `BOSS_${'0'.repeat(3 - stringified.length) + stringified}`
}),
// ...times((i) => i + 65, 26)
// .reduce<[number, number][]>((array, first) => {
// array.push(
// ...times((i) => i + 65, 26).map<[number, number]>((second) => [
// first,
// second,
// ])
// )
// return array
// }, [])
// .map(([first, second]) => {
// return String.fromCharCode(first) + String.fromCharCode(second)
// })
// .reduce<string[]>((idList, prefix) => {
// idList.push(
// ...times((i) => i + 12, 1).map((i) => {
// const stringified = i.toString()
// return `${prefix}_${'0'.repeat(3 - stringified.length) + stringified}`
// })
// )
// return idList
// }, []),
]
// console.log(
// times((i) => i + 65, 26)
// .reduce<[number, number][]>((array, first) => {
// array.push(
// ...times((i) => i + 65, 26).map<[number, number]>((second) => [
// first,
// second,
// ])
// )
// return array
// }, [])
// .map(([first, second]) => {
// return String.fromCharCode(first) + String.fromCharCode(second)
// })
// .reduce<string[]>((idList, prefix) => {
// idList.push(
// ...times((i) => i + 10, 1).map((i) => {
// const stringified = i.toString()
// return `${prefix}_${'0'.repeat(3 - stringified.length) + stringified}`
// })
// )
// return idList
// }, [])
// )
// console.log(
// times(i => i + 65, 26).map(code => {
// return [...times((i) => i + 10, 10).map((i) => {
// const stringified = i.toString()
// return `_${'0'.repeat(3 - stringified.length) + stringified}`
// })]
// })
// )
const Honkai3rdIndexPage = () => {
const { t } = useTranslation()
const [validList, setValidList] = useState<string[]>([])
const validMap = useMemo(() => {
return new Map(validList.map((id) => [id, id]))
}, [validList])
const initializedRef = useRef(false)
const appendValidList = useCallback((enemyId: string) => {
setValidList((previousList) => {
return [...previousList, enemyId]
})
}, [])
useEffect(() => {
let data = validList
if (!initializedRef.current) {
initializedRef.current = true
let rawValidEnemyIdList = localStorage.getItem('valid-enemy-id-list')
try {
console.log('load')
data = JSON.parse(rawValidEnemyIdList || '[]')
if (!Array.isArray(data)) {
throw new Error('data must be array')
}
} catch (error) {
console.warn(error)
data = []
}
setValidList(data)
return
}
console.log('save')
localStorage.setItem('valid-enemy-id-list', JSON.stringify(data))
}, [validList])
return (
<Honkai3rdLayout>
<Head
title={`Enemies - ${t('common.honkai-3rd')} - ${t('common.abyss-lab')}`}
description={t('index.description')}
/>
<Box p={3}>
<Breadcrumb
items={[{ href: 'honkai3rd', label: t('common.honkai-3rd') }]}
/>
<Heading as='h1'>Enemies </Heading>
</Box>
<Box>
{enemyIdList
.filter((enemyId) => !validMap.get(enemyId))
.map((enemyId) => {
return (
<Box key={enemyId}>
<img
alt={enemyId}
src={`https://upload-os-bbs.mihoyo.com/game_record/honkai3rd/global/SpriteOutput/OpenWorld/QuestBossIcon/${enemyId}.png`}
/>
<button
onClick={() => {
appendValidList(enemyId)
}}
>
Add
</button>
</Box>
)
})}
</Box>
<h2>Valid</h2>
<Box>
{validList.map((enemyId) => {
return (
<Box key={enemyId}>
<img
alt={enemyId}
src={`https://upload-os-bbs.mihoyo.com/game_record/honkai3rd/global/SpriteOutput/OpenWorld/QuestBossIcon/${enemyId}.png`}
/>
{enemyId}
<button
onClick={() => {
appendValidList(enemyId)
}}
>
Add
</button>
</Box>
)
})}
</Box>
</Honkai3rdLayout>
)
}
export async function getStaticProps({ locale }: NextPageContext) {
return {
props: {
...(await getI18NProps(locale)),
},
}
}
export default Honkai3rdIndexPage
@@ -1,16 +1,16 @@
import { NextPageContext } from 'next'
import { Box, Card, Flex, Heading, Image, Link } from 'theme-ui'
import AvatarFigureImage from '../../../../../components/v2/AvatarFigureImage'
import { loadBattlesuitData, loadErBattlesuit, loadErBattlesuitCatalog } from '../../../../../lib/v2/server/loadData'
import { Battlesuit, ErBattlesuit, ErSignet } from '../../../../../lib/v2/data/types'
import AvatarFigureImage from '../../../../components/v2/AvatarFigureImage'
import { loadBattlesuitData, loadErBattlesuit, loadErBattlesuitCatalog } from '../../../../lib/v2/server/loadData'
import { Battlesuit, ErBattlesuit, ErSignet } from '../../../../lib/v2/data/types'
import { Fragment, useMemo } from 'react'
import { assetsBucketBaseUrl } from '../../../../../lib/consts'
import BattlesuitCatalogItemCard from '../../../../../components/v2/BattlesuitCatalogItemCard'
import Honkai3rdLayout from '../../../../../components/layouts/Honkai3rdLayout'
import Head from '../../../../../components/atoms/Head'
import { assetsBucketBaseUrl } from '../../../../lib/consts'
import BattlesuitCatalogItemCard from '../../../../components/v2/BattlesuitCatalogItemCard'
import Honkai3rdLayout from '../../../../components/layouts/Honkai3rdLayout'
import Head from '../../../../components/atoms/Head'
import { useTranslation } from 'next-i18next'
import Breadcrumb from '../../../../../components/organisms/Breadcrumb'
import { getI18NProps } from '../../../../../server/i18n'
import Breadcrumb from '../../../../components/organisms/Breadcrumb'
import { getI18NProps } from '../../../../server/i18n'
interface BattlesuitShowPageProps {
battlesuit: Battlesuit
@@ -6,20 +6,20 @@ import {
loadErBattlesuitCatalog,
loadErSigils,
loadErSupports
} from '../../../../lib/v2/server/loadData'
import { BattlesuitCatalogItem, ErBattlesuitCatalogItem } from '../../../../lib/v2/data/types'
} from '../../../lib/v2/server/loadData'
import { BattlesuitCatalogItem, ErBattlesuitCatalogItem } from '../../../lib/v2/data/types'
import { useMemo } from 'react'
import BattlesuitCatalogItemCard from '../../../../components/v2/BattlesuitCatalogItemCard'
import { assetsBucketBaseUrl } from '../../../../lib/consts'
import SquareImage from '../../../../components/v2/SquareImage'
import { signetGroups } from '../../../../lib/v2/data/er'
import BattlesuitSmallIcon from '../../../../components/v2/BattlesuitSmallIcon'
import MaterialIcon from '../../../../components/v2/MaterialIcon'
import Honkai3rdLayout from '../../../../components/layouts/Honkai3rdLayout'
import Head from '../../../../components/atoms/Head'
import Breadcrumb from '../../../../components/organisms/Breadcrumb'
import BattlesuitCatalogItemCard from '../../../components/v2/BattlesuitCatalogItemCard'
import { assetsBucketBaseUrl } from '../../../lib/consts'
import SquareImage from '../../../components/v2/SquareImage'
import { signetGroups } from '../../../lib/v2/data/er'
import BattlesuitSmallIcon from '../../../components/v2/BattlesuitSmallIcon'
import MaterialIcon from '../../../components/v2/MaterialIcon'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
import Head from '../../../components/atoms/Head'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
import { useTranslation } from 'next-i18next'
import { getI18NProps } from '../../../../server/i18n'
import { getI18NProps } from '../../../server/i18n'
interface ErPageProps {
erBattlesuitCatalog: ErBattlesuitCatalogItem[]
@@ -1,13 +1,13 @@
import { NextPageContext } from 'next'
import { useTranslation } from 'next-i18next'
import { Box, Card, Flex, Heading } from 'theme-ui'
import Head from '../../../../components/atoms/Head'
import Honkai3rdLayout from '../../../../components/layouts/Honkai3rdLayout'
import Breadcrumb from '../../../../components/organisms/Breadcrumb'
import MaterialIcon from '../../../../components/v2/MaterialIcon'
import { ErSigil } from '../../../../lib/v2/data/types'
import { loadErSigils } from '../../../../lib/v2/server/loadData'
import { getI18NProps } from '../../../../server/i18n'
import Head from '../../../components/atoms/Head'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
import MaterialIcon from '../../../components/v2/MaterialIcon'
import { ErSigil } from '../../../lib/v2/data/types'
import { loadErSigils } from '../../../lib/v2/server/loadData'
import { getI18NProps } from '../../../server/i18n'
interface SigilsPageProps {
erSigils: ErSigil[]
@@ -1,17 +1,17 @@
import { NextPageContext } from 'next'
import { Box, Card, Flex, Heading } from 'theme-ui'
import { loadErSignets } from '../../../../../lib/v2/server/loadData'
import { ErSignet, ErSignetGroup } from '../../../../../lib/v2/data/types'
import { loadErSignets } from '../../../../lib/v2/server/loadData'
import { ErSignet, ErSignetGroup } from '../../../../lib/v2/data/types'
import { Fragment, useMemo } from 'react'
import { assetsBucketBaseUrl } from '../../../../../lib/consts'
import { signetGroups } from '../../../../../lib/v2/data/er'
import SquareImage from '../../../../../components/v2/SquareImage'
import { getErSignetTypeLabel } from '../../../../../lib/v2/data/text'
import Honkai3rdLayout from '../../../../../components/layouts/Honkai3rdLayout'
import Head from '../../../../../components/atoms/Head'
import { assetsBucketBaseUrl } from '../../../../lib/consts'
import { signetGroups } from '../../../../lib/v2/data/er'
import SquareImage from '../../../../components/v2/SquareImage'
import { getErSignetTypeLabel } from '../../../../lib/v2/data/text'
import Honkai3rdLayout from '../../../../components/layouts/Honkai3rdLayout'
import Head from '../../../../components/atoms/Head'
import { useTranslation } from 'next-i18next'
import Breadcrumb from '../../../../../components/organisms/Breadcrumb'
import { getI18NProps } from '../../../../../server/i18n'
import Breadcrumb from '../../../../components/organisms/Breadcrumb'
import { getI18NProps } from '../../../../server/i18n'
interface BattlesuitShowPageProps {
group: ErSignetGroup
@@ -2,13 +2,13 @@ import { NextPageContext } from 'next'
import { useTranslation } from 'next-i18next'
import { useMemo } from 'react'
import { Box, Card, Flex, Heading } from 'theme-ui'
import Head from '../../../../components/atoms/Head'
import Honkai3rdLayout from '../../../../components/layouts/Honkai3rdLayout'
import Breadcrumb from '../../../../components/organisms/Breadcrumb'
import BattlesuitSmallIcon from '../../../../components/v2/BattlesuitSmallIcon'
import { BattlesuitCatalogItem, ErSupportBattlesuit } from '../../../../lib/v2/data/types'
import { loadBattlesuitCatalog, loadErSupports } from '../../../../lib/v2/server/loadData'
import { getI18NProps } from '../../../../server/i18n'
import Head from '../../../components/atoms/Head'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
import BattlesuitSmallIcon from '../../../components/v2/BattlesuitSmallIcon'
import { BattlesuitCatalogItem, ErSupportBattlesuit } from '../../../lib/v2/data/types'
import { loadBattlesuitCatalog, loadErSupports } from '../../../lib/v2/server/loadData'
import { getI18NProps } from '../../../server/i18n'
interface SupportsPageProps {
battlesuitCatalog: BattlesuitCatalogItem[]
@@ -1,17 +1,17 @@
import { NextPageContext } from 'next'
import { Box, Card, Flex, Heading, Link } from 'theme-ui'
import FormattedText from '../../../../components/v2/FormattedText'
import { formatSubSkillInfo } from '../../../../lib/v2/data/formatText'
import { loadStigmaData, loadStigmataSetCatalog, loadStigmataSetData } from '../../../../lib/v2/server/loadData'
import { RootStigma, StigmataSet } from '../../../../lib/v2/data/types'
import FormattedText from '../../../components/v2/FormattedText'
import { formatSubSkillInfo } from '../../../lib/v2/data/formatText'
import { loadStigmaData, loadStigmataSetCatalog, loadStigmataSetData } from '../../../lib/v2/server/loadData'
import { RootStigma, StigmataSet } from '../../../lib/v2/data/types'
import { Fragment } from 'react'
import StigmaIcon from '../../../../components/v2/StigmaIcon'
import StigmaFigureImage from '../../../../components/v2/StigmaFigureImage'
import Honkai3rdLayout from '../../../../components/layouts/Honkai3rdLayout'
import Head from '../../../../components/atoms/Head'
import StigmaIcon from '../../../components/v2/StigmaIcon'
import StigmaFigureImage from '../../../components/v2/StigmaFigureImage'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
import Head from '../../../components/atoms/Head'
import { useTranslation } from 'next-i18next'
import { getI18NProps } from '../../../../server/i18n'
import Breadcrumb from '../../../../components/organisms/Breadcrumb'
import { getI18NProps } from '../../../server/i18n'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
interface StigmataSetShowPage {
stigmataSet: StigmataSet
@@ -3,14 +3,14 @@ import { Box, Card, Heading } from '@theme-ui/components'
import { NextPageContext } from 'next'
import { Flex, Link } from 'theme-ui'
import { StigmataSetCatalogItem } from '../../../../lib/v2/data/types'
import { loadStigmataSetCatalog } from '../../../../lib/v2/server/loadData'
import StigmaIcon from '../../../../components/v2/StigmaIcon'
import Honkai3rdLayout from '../../../../components/layouts/Honkai3rdLayout'
import Head from '../../../../components/atoms/Head'
import { StigmataSetCatalogItem } from '../../../lib/v2/data/types'
import { loadStigmataSetCatalog } from '../../../lib/v2/server/loadData'
import StigmaIcon from '../../../components/v2/StigmaIcon'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
import Head from '../../../components/atoms/Head'
import { useTranslation } from 'next-i18next'
import { getI18NProps } from '../../../../server/i18n'
import Breadcrumb from '../../../../components/organisms/Breadcrumb'
import { getI18NProps } from '../../../server/i18n'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
interface StigmataSetListPageProps {
stigmataSetCatalog: StigmataSetCatalogItem[]
@@ -1,19 +1,19 @@
import { NextPageContext } from 'next'
import { Box, Card, Flex, Heading, Link } from 'theme-ui'
import FormattedText from '../../../../components/v2/FormattedText'
import { formatSubSkillInfo, replaceNewLine } from '../../../../lib/v2/data/formatText'
import { loadStigmaData, loadStigmataCatalog, loadStigmataSetData } from '../../../../lib/v2/server/loadData'
import { RootStigma, StigmataSetCatalogItem } from '../../../../lib/v2/data/types'
import FormattedText from '../../../components/v2/FormattedText'
import { formatSubSkillInfo, replaceNewLine } from '../../../lib/v2/data/formatText'
import { loadStigmaData, loadStigmataCatalog, loadStigmataSetData } from '../../../lib/v2/server/loadData'
import { RootStigma, StigmataSetCatalogItem } from '../../../lib/v2/data/types'
import { Fragment } from 'react'
import { getStigmaTypeLabel } from '../../../../lib/v2/data/text'
import StigmaTypeIcon from '../../../../components/v2/StigmaTypeIcon'
import StigmaIcon from '../../../../components/v2/StigmaIcon'
import StigmaFigureImage from '../../../../components/v2/StigmaFigureImage'
import { getI18NProps } from '../../../../server/i18n'
import Honkai3rdLayout from '../../../../components/layouts/Honkai3rdLayout'
import Head from '../../../../components/atoms/Head'
import { getStigmaTypeLabel } from '../../../lib/v2/data/text'
import StigmaTypeIcon from '../../../components/v2/StigmaTypeIcon'
import StigmaIcon from '../../../components/v2/StigmaIcon'
import StigmaFigureImage from '../../../components/v2/StigmaFigureImage'
import { getI18NProps } from '../../../server/i18n'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
import Head from '../../../components/atoms/Head'
import { useTranslation } from 'next-i18next'
import Breadcrumb from '../../../../components/organisms/Breadcrumb'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
interface StigmaShowPageProps {
rootStigma: RootStigma
@@ -1,80 +0,0 @@
/** @jsxImportSource theme-ui */
import { NextPageContext } from 'next'
import SingleStigmataPage, {
SingleStigmataPageProps,
} from '../../../components/pages/SingleStigmataPage'
import StigmataSetPage, {
StigmataSetProps,
} from '../../../components/pages/StigmataSetPage'
import { generateI18NPaths, getI18NProps } from '../../../server/i18n'
import {
listStigmata,
getStigmaById,
getStigmataListBySetId,
getStigmataSetBySetId,
listStigmataSet,
} from '../../../server/data/honkai3rd/stigmata'
type StigmataShowPageProps = SingleStigmataPageProps | StigmataSetProps
const StigmataListPage = (props: StigmataShowPageProps) => {
if (props.type === 'single') {
return <SingleStigmataPage {...props} />
}
return <StigmataSetPage {...props} />
}
export default StigmataListPage
export async function getStaticProps({
params,
locale,
}: NextPageContext & { params: { stigmataId: string } }) {
const stigmataData = getStigmaById(params.stigmataId, locale)!
if (stigmataData != null) {
return {
props: {
type: 'single',
stigmataData: stigmataData,
stigmataSetList: (
(stigmataData.set != null &&
getStigmataListBySetId(stigmataData.set!, locale)) ||
[]
).sort((a, b) => -a.type.localeCompare(b.type)),
stigmataSet: getStigmataSetBySetId(stigmataData.set!, locale) || null,
...(await getI18NProps(locale)),
},
}
}
const [stigmataSetId] = params.stigmataId.split('-set')
const stigmataSet = getStigmataSetBySetId(stigmataSetId, locale)!
return {
props: {
type: 'set',
stigmataSet,
stigmataSetList: (
getStigmataListBySetId(stigmataSet.id, locale) || []
).sort((a, b) => -a.type.localeCompare(b.type)),
...(await getI18NProps(locale)),
},
}
}
export async function getStaticPaths() {
return {
paths: generateI18NPaths([
...listStigmata().map((stigmata) => {
return {
params: { stigmataId: stigmata.id },
}
}),
...listStigmataSet().map((stigmataSet) => {
return {
params: { stigmataId: `${stigmataSet.id}-set` },
}
}),
]),
fallback: false,
}
}
+53 -90
View File
@@ -1,96 +1,53 @@
/** @jsxImportSource theme-ui */
import { Box, Heading, Flex } from '@theme-ui/components'
import { Box, Card, Heading } from '@theme-ui/components'
import { NextPageContext } from 'next'
import { useRouter } from 'next/router'
import { pick } from 'ramda'
import { useMemo } from 'react'
import PageLink from '../../../components/atoms/PageLink'
import StigmataCard from '../../../components/molecules/StigmataCard'
import StigmataSetCard from '../../../components/molecules/StigmataSetCard'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
import { StigmataData, StigmataSet } from '../../../lib/honkai3rd/stigmata'
import { getI18NProps } from '../../../server/i18n'
import {
listStigmata,
listStigmataSet,
} from '../../../server/data/honkai3rd/stigmata'
import { useTranslation } from 'next-i18next'
import Head from '../../../components/atoms/Head'
import { Flex, Link } from 'theme-ui'
import { StigmataCatalogItem } from '../../../lib/v2/data/types'
import { loadStigmataCatalog } from '../../../lib/v2/server/loadData'
import StigmaIcon from '../../../components/v2/StigmaIcon'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
import { getI18NProps } from '../../../server/i18n'
interface StigmataListPageProps {
stigmataDataList: Pick<StigmataData, 'id' | 'name' | 'rarity'>[]
stigmataSetList: Pick<StigmataSet, 'id' | 'name' | 'rarity'>[]
stigmataCatalog: StigmataCatalogItem[]
}
const StigmataListPage = ({
stigmataDataList,
stigmataSetList,
}: StigmataListPageProps) => {
const { t } = useTranslation()
const { query } = useRouter()
const listMode = useMemo(() => {
if (query.list == null) {
return 'set'
}
return typeof query.list === 'string' ? query.list : query.list[0]
}, [query])
const StigmataListPage = ({ stigmataCatalog }: StigmataListPageProps) => {
return (
<Honkai3rdLayout>
<Head
title={`${t('common.stigmata')} - ${t('common.honkai-3rd')} - ${t(
'common.abyss-lab'
)}`}
description={t('stigmata-list.description')}
canonicalHref={`/honkai3rd/stigmata`}
/>
<Box>
<Heading as="h1">Stigmata (Single)</Heading>
<Box p={3}>
<Breadcrumb
items={[
{ href: '/honkai3rd', label: t('common.honkai-3rd') },
{ href: '/honkai3rd/stigmata', label: t('common.stigmata') },
]}
/>
<Heading as='h1'>{t('stigmata-list.stigmata')}</Heading>
<Flex mb={3}>
<PageLink
href={{
query: listMode === 'set' ? { list: 'single' } : { list: 'set' },
}}
shallow={true}
m={1}
py={1}
px={2}
className={listMode === 'set' ? 'active' : ''}
sx={{ display: 'flex' }}
variant='buttons.primary'
>
{t('stigmata-list.set-list')}
</PageLink>
</Flex>
<Flex
sx={{
flexWrap: 'wrap',
justifyContent: 'space-around',
}}
>
{listMode === 'set'
? stigmataSetList.map((stigmataSet) => {
<Flex sx={{ flexWrap: 'wrap' }}>
{stigmataCatalog
.filter(filterStigmata)
.sort(sortStigmata)
.map(stigma => {
return (
<StigmataSetCard
key={stigmataSet.id}
stigmataSet={stigmataSet}
/>
<Box key={stigma.id} m={2}>
<Link href={`/v2-pre/stigmata/${stigma.id}`}>
<Card p={1}>
<Flex sx={{ justifyContent: 'center' }}>
<StigmaIcon icon={stigma.icon} rarity={stigma.maxRarity} />
</Flex>
<Box
sx={{
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
px: 1,
width: 112,
textAlign: 'center'
}}
>
{stigma.name}
</Box>
{/* {stigma.id} */}
</Card>
</Link>
</Box>
)
})
: stigmataDataList.map((stigmata) => {
return <StigmataCard key={stigmata.id} stigmata={stigmata} />
})}
</Flex>
</Box>
@@ -101,15 +58,21 @@ const StigmataListPage = ({
export default StigmataListPage
export async function getStaticProps({ locale }: NextPageContext) {
const stigmataCatalog = loadStigmataCatalog()
return {
props: {
stigmataDataList: listStigmata(locale).map((stigmata) =>
pick(['id', 'name', 'rarity'], stigmata)
),
stigmataSetList: listStigmataSet(locale).map((stigmataSet) =>
pick(['id', 'name', 'rarity'], stigmataSet)
),
...(await getI18NProps(locale)),
},
props: { stigmataCatalog, ...(await getI18NProps(locale)) }
}
}
function filterStigmata(weapon: StigmataCatalogItem) {
return true
}
function sortStigmata(a: StigmataCatalogItem, b: StigmataCatalogItem) {
let aId = parseInt(a.id)
let bId = parseInt(b.id)
return bId - aId
}
-49
View File
@@ -1,49 +0,0 @@
/* eslint-disable jsx-a11y/alt-text */
import { times } from 'ramda'
interface BossSearchPageProps {}
// const AZArray = times((index) => {
// const charCode = 'A'.charCodeAt(0) + index
// const result = String.fromCharCode(charCode)
// return result
// }, 26)
const NumberArray = times((index) => {
if (index / 100 >= 1) {
return index
}
if (index / 10 >= 1) {
return '0' + index
}
return '00' + index
}, 1000)
const BossSearchPage = ({}: BossSearchPageProps) => {
return NumberArray.map((item) => {
return (
<img
key={item}
src={`https://upload-os-bbs.mihoyo.com/game_record/honkai3rd/global/SpriteOutput/OpenWorld/QuestBossIcon/QT_${item}.png`}
/>
)
})
// return AZArray.reduce<string[]>((array, firstChar) => {
// return [
// ...array,
// ...AZArray.map((secondChar) => firstChar + secondChar + '_110.png'),
// ]
// }, []).map((item) => {
// return (
// <img
// key={item}
// src={`https://upload-os-bbs.mihoyo.com/game_record/honkai3rd/global/SpriteOutput/OpenWorld/QuestBossIcon/${item}`}
// />
// )
// })
}
export default BossSearchPage
// PSY : UL
// MECHA : RO
// BIO : DG
@@ -1,328 +0,0 @@
/** @jsxImportSource theme-ui */
import {
Box,
Card,
Flex,
Heading,
Paragraph,
Text,
Image,
} from '@theme-ui/components'
import { NextPageContext } from 'next'
import React from 'react'
import BattlesuitFeatureLabel from '../../../../../components/atoms/BattlesuitFeatureLabel'
import BattlesuitRankIcon from '../../../../../components/atoms/BattlesuitRankIcon'
import PageLink from '../../../../../components/atoms/PageLink'
import SecondaryLabel from '../../../../../components/atoms/SecondaryLabel'
import TypeLabel from '../../../../../components/atoms/TypeLabel'
import ValkyrieLabel from '../../../../../components/atoms/ValkyrieLabel'
import {
BattlesuitData,
BattlesuitSkillGroup,
} from '../../../../../lib/honkai3rd/battlesuits'
import { generateI18NPaths, getI18NProps } from '../../../../../server/i18n'
import {
getBattlesuitById,
listBattlesuits,
} from '../../../../../server/data/honkai3rd/battlesuits'
import { useTranslation } from '../../../../../lib/i18n'
import { assetsBucketBaseUrl } from '../../../../../lib/consts'
import { WeaponData } from '../../../../../lib/honkai3rd/weapons'
import { getWeaponMapByIds } from '../../../../../server/data/honkai3rd/weapons'
import { StigmataData } from '../../../../../lib/honkai3rd/stigmata'
import WeaponCard from '../../../../../components/molecules/WeaponCard'
import StigmataCard from '../../../../../components/molecules/StigmataCard'
import { getStigmataMapByIds } from '../../../../../server/data/honkai3rd/stigmata'
type WeaponObjectMap = { [key: string]: WeaponData }
type StigmataObjectMap = { [key: string]: StigmataData }
interface BattlesuitShowPageProps {
battlesuit: BattlesuitData
weaponMap: WeaponObjectMap
stigmataMap: StigmataObjectMap
}
const BattlesuitShowPage = ({
battlesuit,
weaponMap,
stigmataMap,
}: BattlesuitShowPageProps) => {
const { t } = useTranslation()
return (
<Box p={3}>
<Heading as='h1'>{battlesuit.name}</Heading>
<Box mb={3}>
<Box
sx={{
position: 'relative',
overflow: 'hidden',
width: '100%',
maxWidth: [300, 400],
}}
>
<Image
alt={battlesuit.name}
src={`${assetsBucketBaseUrl}/honkai3rd/battlesuits/${battlesuit.id}.png`}
width={400}
height={400}
/>
</Box>
</Box>
<Card
sx={{
overflow: 'hidden',
}}
mb={3}
>
<Box p={2} sx={{ borderBottom: 'default' }}>
<PageLink
href={{
pathname: '/honkai3rd/battlesuits',
query: { filter: battlesuit.valkyrie },
}}
>
<ValkyrieLabel valkyrie={battlesuit.valkyrie} />
</PageLink>
</Box>
<Box p={2} sx={{ borderBottom: 'default' }}>
<PageLink
href={{
pathname: '/honkai3rd/battlesuits',
query: { filter: battlesuit.type },
}}
>
<TypeLabel type={battlesuit.type} />
</PageLink>
</Box>
<Flex p={2} sx={{ flexWrap: 'wrap', rowGap: 1 }}>
{battlesuit.features.map((feature) => {
return (
<Box mr={2} key={feature}>
<PageLink
href={{
pathname: '/honkai3rd/battlesuits',
query: { filter: feature },
}}
>
<BattlesuitFeatureLabel feature={feature} />
</PageLink>
</Box>
)
})}
</Flex>
</Card>
{battlesuit.equipment != null && (
<Card
sx={{
mb: 3,
p: 2,
overflow: 'hidden',
}}
>
{battlesuit.equipment.map((equipmentItem) => {
return (
<Box key={equipmentItem.type}>
<Heading
as='h3'
sx={{
mb: 1,
}}
>
{t(`battlesuit-show.equipmentTypes.${equipmentItem.type}`)}
</Heading>
<Flex sx={{ mx: -1 }}>
{equipmentItem.weapon != null && (
<WeaponCard
size='sm'
weapon={weaponMap[equipmentItem.weapon]}
/>
)}
{equipmentItem.stigmataTop != null && (
<StigmataCard
size='sm'
stigmata={stigmataMap[equipmentItem.stigmataTop]}
/>
)}
{equipmentItem.stigmataMid != null && (
<StigmataCard
size='sm'
stigmata={stigmataMap[equipmentItem.stigmataMid]}
/>
)}
{equipmentItem.stigmataBot != null && (
<StigmataCard
size='sm'
stigmata={stigmataMap[equipmentItem.stigmataBot]}
/>
)}
</Flex>
</Box>
)
})}
</Card>
)}
<BattlesuitSkillGroupCard
heading={t('battlesuit-show.leader')}
skillGroup={battlesuit.leader}
/>
<BattlesuitSkillGroupCard
heading={t('battlesuit-show.passive')}
skillGroup={battlesuit.passive}
/>
<BattlesuitSkillGroupCard
heading={t('battlesuit-show.evasion')}
skillGroup={battlesuit.evasion}
/>
<BattlesuitSkillGroupCard
heading={t('battlesuit-show.special-attack')}
skillGroup={battlesuit.special}
/>
<BattlesuitSkillGroupCard
heading={t('battlesuit-show.ultimate')}
skillGroup={battlesuit.ultimate}
/>
<BattlesuitSkillGroupCard
heading={t('battlesuit-show.basic-attack')}
skillGroup={battlesuit.basic}
/>
{battlesuit.sp != null && (
<BattlesuitSkillGroupCard
heading={t('battlesuit-show.sp-skill')}
skillGroup={battlesuit.sp}
/>
)}
</Box>
)
}
export default BattlesuitShowPage
export async function getStaticProps({
params,
locale,
}: NextPageContext & { params: { battlesuitId: string } }) {
const battlesuit = getBattlesuitById(params.battlesuitId, locale)!
const { weaponIds, stigmataIds } = (battlesuit.equipment || []).reduce<{
weaponIds: string[]
stigmataIds: string[]
}>(
(acc, equipmentItem) => {
acc.weaponIds.push(equipmentItem.weapon)
acc.stigmataIds.push(
equipmentItem.stigmataTop,
equipmentItem.stigmataMid,
equipmentItem.stigmataBot
)
return acc
},
{ weaponIds: [], stigmataIds: [] }
)
const weaponMap = getWeaponMapByIds(weaponIds, locale)
const stigmataMap = getStigmataMapByIds(stigmataIds, locale)
return {
props: {
battlesuit,
weaponMap,
stigmataMap,
...(await getI18NProps(locale)),
},
}
}
export async function getStaticPaths() {
return {
paths: generateI18NPaths(
listBattlesuits().map((battlesuit) => {
return {
params: { battlesuitId: battlesuit.id },
}
})
),
fallback: false,
}
}
interface BattlesuitSkillGroupCardProps {
heading: string
skillGroup: BattlesuitSkillGroup
}
const BattlesuitSkillGroupCard = ({
heading,
skillGroup,
}: BattlesuitSkillGroupCardProps) => {
return (
<Card mb={3}>
<Box
sx={{
p: 2,
borderBottom: 'default',
}}
>
<Heading as='h2' mb={1}>
{skillGroup.core.name}
</Heading>
<SecondaryLabel>{heading}</SecondaryLabel>
</Box>
<Paragraph p={2} sx={{ whiteSpace: 'pre-wrap', borderBottom: 'default' }}>
{skillGroup.core.description}
</Paragraph>
{skillGroup.subskills.map((subskill) => {
return (
<React.Fragment key={subskill.name}>
<Heading as='h3' p={2} m={0} sx={{ borderBottom: 'default' }}>
<Flex sx={{ alignItems: 'center' }}>
<Text>{subskill.name}</Text>
{subskill.requiredRank != null ? (
/^[0-9]/.test(subskill.requiredRank) ? (
<Box
ml={2}
sx={{ height: 30, lineHeight: '30px', fontSize: 2 }}
>
{subskill.requiredRank.slice(0, 1)}
</Box>
) : (
<BattlesuitRankIcon
rank={subskill.requiredRank}
size={30}
ml={2}
/>
)
) : (
<Box sx={{ height: 30 }} />
)}
</Flex>
</Heading>
<Paragraph
p={2}
sx={{
whiteSpace: 'pre-wrap',
borderBottom: 'default',
'&:last-child': { borderBottom: 'none' },
}}
>
{subskill.description}
</Paragraph>
</React.Fragment>
)
})}
</Card>
)
}
@@ -1,158 +0,0 @@
/** @jsxImportSource theme-ui */
import { NextPageContext } from 'next'
import React from 'react'
import PageLink from '../../../../../components/atoms/PageLink'
import { StigmataSetProps } from '../../../../../components/pages/StigmataSetPage'
import { generateI18NPaths, getI18NProps } from '../../../../../server/i18n'
import {
listStigmata,
getStigmataListBySetId,
getStigmataSetBySetId,
listStigmataSet,
getStigmaById,
} from '../../../../../server/data/honkai3rd/stigmata'
import { Box, Card, Flex, Heading, Paragraph } from 'theme-ui'
import SecondaryLabel from '../../../../../components/atoms/SecondaryLabel'
import StigmataCard from '../../../../../components/molecules/StigmataCard'
import { useTranslation } from '../../../../../lib/i18n'
import { SingleStigmataPageProps } from '../../../../../components/pages/SingleStigmataPage'
type StigmataShowPageProps = StigmataSetProps | SingleStigmataPageProps
const StigmataListPage = (props: StigmataShowPageProps) => {
const { t } = useTranslation()
if (props.type === 'single') {
return null
}
const { stigmataSet, stigmataSetList } = props
return (
<Box p={3}>
<Box mb={3}>
<Heading as='h1' mb={0}>
{stigmataSet.name}
</Heading>
<SecondaryLabel>{stigmataSet.altName}</SecondaryLabel>
</Box>
<Card mb={3}>
<Flex
p={2}
sx={{
borderBottom: 'default',
justifyContent: 'space-around',
flexWrap: 'wrap',
}}
>
{stigmataSetList.map((stigmataSetItem) => {
return (
<StigmataCard
key={stigmataSetItem.id}
stigmata={stigmataSetItem}
/>
)
})}
</Flex>
{stigmataSetList.map((stigmataSetItem) => {
return (
<React.Fragment key={stigmataSetItem.id}>
<Box sx={{ p: 2, borderBottom: 'default' }}>
<Heading as='h2' mb={1}>
<PageLink href={`/honkai3rd/stigmata/${stigmataSetItem.id}`}>
{stigmataSetItem.name}
</PageLink>
</Heading>
<SecondaryLabel>
{t(`stigmata-show.${stigmataSetItem.type}`)}
</SecondaryLabel>
</Box>
<Box p={2} sx={{ borderBottom: 'default' }}>
HP : {stigmataSetItem.hp} / ATK : {stigmataSetItem.atk} / DEF :{' '}
{stigmataSetItem.def} / CRT : {stigmataSetItem.crt}
</Box>
<Paragraph
m={0}
p={2}
sx={{
borderBottom: 'default',
'&:last-child': { borderBottom: 'none' },
}}
>
{stigmataSetItem.skill.description}
</Paragraph>
</React.Fragment>
)
})}
</Card>
<Card>
<Heading as='h2' m={0} p={2} sx={{ borderBottom: 'default' }}>
{stigmataSet.twoSetSkill.name}
</Heading>
<Paragraph m={0} p={2} sx={{ borderBottom: 'default' }}>
{stigmataSet.twoSetSkill.description}
</Paragraph>
<Heading as='h2' m={0} p={2} sx={{ borderBottom: 'default' }}>
{stigmataSet.threeSetSkill.name}
</Heading>
<Paragraph m={0} p={2}>
{stigmataSet.threeSetSkill.description}
</Paragraph>
</Card>
</Box>
)
}
export default StigmataListPage
export async function getStaticProps({
params,
locale,
}: NextPageContext & { params: { stigmataId: string } }) {
const stigmataData = getStigmaById(params.stigmataId, locale)!
if (stigmataData != null) {
return {
props: {
type: 'single',
stigmataData: stigmataData,
stigmataSetList: (
getStigmataListBySetId(stigmataData.set!, locale) || []
).sort((a, b) => -a.type.localeCompare(b.type)),
stigmataSet: getStigmataSetBySetId(stigmataData.set!, locale) || null,
...(await getI18NProps(locale)),
},
}
}
const [stigmataSetId] = params.stigmataId.split('-set')
const stigmataSet = getStigmataSetBySetId(stigmataSetId, locale)!
return {
props: {
type: 'set',
stigmataSet,
stigmataSetList: (
getStigmataListBySetId(stigmataSet.id, locale) || []
).sort((a, b) => -a.type.localeCompare(b.type)),
...(await getI18NProps(locale)),
},
}
}
export async function getStaticPaths() {
return {
paths: generateI18NPaths([
...listStigmata().map((stigmata) => {
return {
params: { stigmataId: stigmata.id },
}
}),
...listStigmataSet().map((stigmataSet) => {
return {
params: { stigmataId: `${stigmataSet.id}-set` },
}
}),
]),
fallback: false,
}
}
@@ -1,195 +0,0 @@
/** @jsxImportSource theme-ui */
import { Flex, Box, Card, Heading, Paragraph } from '@theme-ui/components'
import { NextPageContext } from 'next'
import SquareImageBox from '../../../../../components/atoms/SquareImageBox'
import {
weaponCategories,
WeaponData,
} from '../../../../../lib/honkai3rd/weapons'
import { generateI18NPaths, getI18NProps } from '../../../../../server/i18n'
import {
getWeaponById,
getWeaponMapByIds,
listWeapons,
} from '../../../../../server/data/honkai3rd/weapons'
import { useTranslation } from '../../../../../lib/i18n'
import PageLink from '../../../../../components/atoms/PageLink'
import { assetsBucketBaseUrl } from '../../../../../lib/consts'
import { getBattlesuitMapByIds } from '../../../../../server/data/honkai3rd/battlesuits'
import { BattlesuitData } from '../../../../../lib/honkai3rd/battlesuits'
import BattlesuitCard from '../../../../../components/molecules/BattlesuitCard'
import WeaponCard from '../../../../../components/molecules/WeaponCard'
interface WeaponShowPageProps {
weapon: WeaponData
battlesuitMap: { [key: string]: BattlesuitData }
weaponMap: { [key: string]: WeaponData }
}
const WeaponShowPage = ({
weapon,
battlesuitMap,
weaponMap,
}: WeaponShowPageProps) => {
const { t } = useTranslation()
const weaponCategoryName = t(`weapons-show.${weapon.category}`)
const weaponCategory = weaponCategories.find(
(category) => category.value === weapon.category
)
return (
<Box p={3}>
<Heading as='h1'>{weapon.name}</Heading>
<Box mb={3}>
<SquareImageBox
size={100}
alt={weapon.name}
src={`${assetsBucketBaseUrl}/honkai3rd/weapons/${weapon.id}.png`}
/>
</Box>
<Card mb={3}>
<Box p={2} sx={{ borderBottom: 'default' }}>
<PageLink
href={{
pathname: '/honkai3rd/weapons',
query: { filter: weapon.category },
}}
>
<Flex>
{weaponCategory != null && (
<SquareImageBox
size={30}
src={`${assetsBucketBaseUrl}/honkai3rd/${weaponCategory.icon}.png`}
alt={weaponCategoryName}
mr={1}
/>
)}
{weaponCategoryName}
</Flex>
</PageLink>
</Box>
<Box p={2} sx={{ borderBottom: 'default' }}>
{'⭐'.repeat(weapon.rarity)}
</Box>
<Box p={2}>
ATK : {weapon.atk} / CRT : {weapon.crt}
</Box>
{weapon.battlesuits != null && weapon.battlesuits.length > 0 && (
<Box sx={{ p: 2, borderTop: 'default' }}>
<Heading as='h4'>{t('weapons-show.best-on')}</Heading>
{weapon.battlesuits.map(({ id: battlesuitId }) => {
return (
<BattlesuitCard
key={battlesuitId}
size='sm'
battlesuit={battlesuitMap[battlesuitId]}
/>
)
})}
</Box>
)}
{weapon.priWeapon != null && (
<Box sx={{ p: 2, borderTop: 'default' }}>
<Heading as='h4'>{t('weapons-show.pri-weapon')}</Heading>
<WeaponCard size='sm' weapon={weaponMap[weapon.priWeapon]} />
</Box>
)}
{weapon.originalWeapons != null && weapon.originalWeapons.length > 0 && (
<Box sx={{ p: 2, borderTop: 'default' }}>
<Heading as='h4'>{t('weapons-show.original-weapon')}</Heading>
{weapon.originalWeapons.map((originalWeaponId) => {
return (
<WeaponCard
key={originalWeaponId}
size='sm'
weapon={weaponMap[originalWeaponId]}
/>
)
})}
</Box>
)}
{/* {weapon.sources != null && (
<Box sx={{ p: 2, borderTop: 'default' }}>
<Heading as='h4'>{t('weapons-show.sources')}</Heading>
{weapon.sources
.sort((a, b) => a.type.localeCompare(b.type))
.map((source) => {
return <SourceCard key={source.type} source={source} />
})}
</Box>
)} */}
</Card>
<Box>
{weapon.skills.map((skill) => {
return (
<Card key={skill.name} mb={3}>
<Heading as='h3' p={2} m={0} sx={{ borderBottom: 'default' }}>
{skill.name}
</Heading>
<Paragraph
p={2}
sx={{
whiteSpace: 'pre-wrap',
}}
>
{skill.description}
</Paragraph>
</Card>
)
})}
</Box>
</Box>
)
}
export default WeaponShowPage
export async function getStaticProps({
params,
locale,
}: NextPageContext & { params: { weaponId: string } }) {
const weapon = getWeaponById(params.weaponId, locale)
const battlesuitMap = getBattlesuitMapByIds(
weapon != null && weapon.battlesuits != null
? weapon.battlesuits.map(({ id }) => id)
: [],
locale
)
const weaponIds = []
if (weapon != null) {
if (weapon.priWeapon != null) {
weaponIds.push(weapon.priWeapon)
}
if (weapon.originalWeapons != null) {
weaponIds.push(...weapon.originalWeapons)
}
}
const weaponMap = getWeaponMapByIds(weaponIds, locale)
return {
props: {
weapon,
battlesuitMap,
weaponMap,
...(await getI18NProps(locale)),
},
}
}
export async function getStaticPaths() {
return {
paths: generateI18NPaths(
listWeapons().map((weapon) => {
return {
params: { weaponId: weapon.id },
}
})
),
fallback: false,
}
}
@@ -1,194 +0,0 @@
import { NextPageContext } from 'next'
import { Box, Card, Flex, Heading } from 'theme-ui'
import AttributeIcon from '../../../../components/v2/AttributeIcon'
import AvatarFigureImage from '../../../../components/v2/AvatarFigureImage'
import AvatarSkillIcon from '../../../../components/v2/AvatarSkillIcon'
import AvatarSubSkillIcon from '../../../../components/v2/AvatarSubSkillIcon'
import BattlesuitAvatarIcon from '../../../../components/v2/BattlesuitAvatarIcon'
import FormattedText from '../../../../components/v2/FormattedText'
import { formatSkillInfo, formatSubSkillInfo } from '../../../../lib/v2/data/formatText'
import { loadBattlesuitCatalog, loadBattlesuitData } from '../../../../lib/v2/server/loadData'
import { Battlesuit, SkillTagItem } from '../../../../lib/v2/data/types'
import { Fragment } from 'react'
import TagIcon from '../../../../components/v2/TagIcon'
import {
getAttributeLabel,
getCharacterTypeLabel,
getSkillTypeLabel,
getTagTypeLabel,
getWeaponTypeLabel
} from '../../../../lib/v2/data/text'
import StarIcon from '../../../../components/v2/StarIcon'
import ChibiIcon from '../../../../components/v2/ChibiIcon'
import WeaponTypeIcon from '../../../../components/v2/WeaponTypeIcon'
import { sortBattlesuitSkill } from '../../../../lib/v2/data/utils'
import Honkai3rdLayout from '../../../../components/layouts/Honkai3rdLayout'
import { getI18NProps } from '../../../../server/i18n'
import Head from '../../../../components/atoms/Head'
import { useTranslation } from 'next-i18next'
interface BattlesuitShowPageProps {
battlesuit: Battlesuit
}
const BattlesuitShowPage = ({ battlesuit }: BattlesuitShowPageProps) => {
const { t } = useTranslation()
return (
<Honkai3rdLayout>
<Head
title={`${battlesuit.fullName} - ${t('common.honkai-3rd')} - ${t('common.abyss-lab')}`}
description={`${t('common.honkai-3rd')} ${t('battlesuit-show.battlesuit')} / ${battlesuit.fullName} / ${
battlesuit.attributeType
} / ${battlesuit.tags
.map(tag => {
return getTagTypeLabel(tag)
})
.join(', ')}`}
canonicalHref={`/honkai3rd/battlesuits/${battlesuit.id}`}
/>
<Box p={2}>
<Heading as="h1">{battlesuit.fullName}</Heading>
<AvatarFigureImage battlesuitId={battlesuit.id} sx={{ height: 400 }} />
<Box mb={3}>
<BattlesuitAvatarIcon battlesuit={battlesuit} />
</Box>
<Card mb={3}>
<Flex sx={{ borderBottom: 'default', p: 1 }}>
<Flex sx={{ alignItems: 'center' }}>
<Box mr={2}>
<StarIcon star={battlesuit.initialStar} />
</Box>
<ChibiIcon id={battlesuit.character} />
<Box ml={1} mr={2}>
{getCharacterTypeLabel(battlesuit.character)}
</Box>
<AttributeIcon attributeType={battlesuit.attributeType} size={30} />
<Box ml={1} mr={2}>
{getAttributeLabel(battlesuit.attributeType)}
</Box>
<WeaponTypeIcon type={battlesuit.weapon} />
<Box ml={1}>{getWeaponTypeLabel(battlesuit.weapon)}</Box>
</Flex>
</Flex>
<Flex sx={{ p: 1 }}>
{battlesuit.tags.map(tag => {
return (
<Flex key={tag} sx={{ mr: 2, alignItems: 'center' }}>
<TagIcon type={tag} />
<Box ml={1}>{getTagTypeLabel(tag)}</Box>
</Flex>
)
})}
</Flex>
</Card>
<Heading as="h2">Skills</Heading>
{battlesuit.skills.sort(sortBattlesuitSkill).map(skill => {
return (
<Card key={skill.id} mb={3}>
<Box sx={{ borderBottom: 'default', p: 1 }}>
<Flex sx={{ alignItems: 'center' }}>
<AvatarSkillIcon icon={skill.icon} />
<Heading as="h3" sx={{ ml: 1, mb: 0 }}>
<FormattedText>{skill.name}</FormattedText>
</Heading>
{skill.tags.length > 0 && (
<Flex sx={{ ml: 1 }}>
{skill.tags.map((tag, index) => {
return <SkillTagItem key={index} tag={tag} />
})}
</Flex>
)}
</Flex>
<Box>{getSkillTypeLabel(skill.skillType)}</Box>
</Box>
<Box sx={{ whiteSpace: 'pre-wrap', borderBottom: 'default', p: 1 }}>
<FormattedText>{formatSkillInfo(skill)}</FormattedText>
</Box>
{skill.subSkills.map(subSkill => {
return (
<Fragment key={subSkill.id}>
<Box sx={{ borderBottom: 'default', p: 1 }}>
<Flex sx={{ alignItems: 'center' }}>
<AvatarSubSkillIcon icon={subSkill.icon} />
<Heading as="h4" sx={{ ml: 1, mb: 0 }}>
<FormattedText>{subSkill.name}</FormattedText>
</Heading>
{subSkill.unlockStar.localeCompare(battlesuit.initialStar) > 0 && (
<Box ml={2}>
<StarIcon star={subSkill.unlockStar} />
</Box>
)}
{subSkill.tags.length > 0 && (
<Flex sx={{ ml: 2 }}>
{subSkill.tags.map((tag, index) => {
return <SkillTagItem key={index} tag={tag} />
})}
</Flex>
)}
</Flex>
</Box>
<Box
sx={{
whiteSpace: 'pre-wrap',
borderBottom: 'default',
'&:last-child': { borderBottom: 'none' },
p: 1
}}
>
<FormattedText>{formatSubSkillInfo(subSkill)}</FormattedText>
</Box>
</Fragment>
)
})}
</Card>
)
})}
{/* <pre>{JSON.stringify(battlesuit, null, 2)}</pre> */}
</Box>
</Honkai3rdLayout>
)
}
export default BattlesuitShowPage
export async function getStaticProps({ locale, params }: NextPageContext & { params: { battlesuitId: string } }) {
const battlesuit = loadBattlesuitData(params.battlesuitId)
return {
props: { battlesuit, ...(await getI18NProps(locale)) }
}
}
export async function getStaticPaths() {
const battlesuitCatalog = loadBattlesuitCatalog()
return {
paths: battlesuitCatalog.map(catalogItem => {
return {
params: { battlesuitId: catalogItem.id }
}
}),
fallback: false
}
}
interface SkillTagBoxProps {
tag: SkillTagItem
}
const SkillTagItem = ({ tag }: SkillTagBoxProps) => {
return <TagIcon type={tag.type} strength={tag.strength} comment={tag.comment} size="sm" />
}
@@ -1,62 +0,0 @@
/** @jsxImportSource theme-ui */
import { Box } from '@theme-ui/components'
import { NextPageContext } from 'next'
import { Flex, Link } from 'theme-ui'
import { loadBattlesuitCatalog } from '../../../../lib/v2/server/loadData'
import { BattlesuitCatalogItem } from '../../../../lib/v2/data/types'
import BattlesuitCatalogItemCard from '../../../../components/v2/BattlesuitCatalogItemCard'
import Head from '../../../../components/atoms/Head'
import Honkai3rdLayout from '../../../../components/layouts/Honkai3rdLayout'
import { useTranslation } from 'next-i18next'
import Breadcrumb from '../../../../components/organisms/Breadcrumb'
import { getI18NProps } from '../../../../server/i18n'
interface BattlesuitListPageProps {
battlesuitCatalog: BattlesuitCatalogItem[]
}
const BattlesuitListPage = ({ battlesuitCatalog }: BattlesuitListPageProps) => {
const { t } = useTranslation()
return (
<Honkai3rdLayout>
<Head
title={`${t('common.battlesuits')} - ${t('common.honkai-3rd')} - ${t('common.abyss-lab')}`}
description={t('battlesuits-list.description')}
canonicalHref={`/honkai3rd/battlesuits`}
/>
<Box p={2}>
<Breadcrumb
items={[
{ href: '/honkai3rd', label: t('common.honkai-3rd') },
{
href: '/honkai3rd/v2/battlesuits',
label: t('common.battlesuits')
}
]}
/>
<h1>{t('battlesuits-list.heading')}</h1>
<Flex sx={{ flexWrap: 'wrap' }}>
{battlesuitCatalog.map(battlesuit => {
return (
<Box key={battlesuit.id}>
<Link href={`/honkai3rd/v2/battlesuits/${battlesuit.id}`}>
<BattlesuitCatalogItemCard battlesuit={battlesuit} />
</Link>
</Box>
)
})}
</Flex>
</Box>
</Honkai3rdLayout>
)
}
export default BattlesuitListPage
export async function getStaticProps({ locale }: NextPageContext) {
const battlesuitCatalog = loadBattlesuitCatalog()
return {
props: { battlesuitCatalog, ...(await getI18NProps(locale)) }
}
}
-117
View File
@@ -1,117 +0,0 @@
import { NextPageContext } from 'next'
import { Box, Card, Flex, Heading } from 'theme-ui'
import { useMemo } from 'react'
import { Elf, ElfSkill } from '../../../../lib/v2/data/types'
import { loadElfCatalog, loadElfData } from '../../../../lib/v2/server/loadData'
import SquareImage from '../../../../components/v2/SquareImage'
import TagIcon from '../../../../components/v2/TagIcon'
import { assetsBucketBaseUrl } from '../../../../lib/consts'
import { ListMap } from '../../../../lib/utils'
import FormattedText from '../../../../components/v2/FormattedText'
import { formatSubSkillInfo } from '../../../../lib/v2/data/formatText'
interface ElfShowPageProps {
elf: Elf
}
const ElfShowPage = ({ elf }: ElfShowPageProps) => {
const skillRows = useMemo(() => {
const rowSkillListMap = elf.skills.reduce<ListMap<string, ElfSkill>>((map, skill) => {
map.set(skill.row.toString(), skill)
return map
}, new ListMap())
return [...rowSkillListMap.values()]
}, [elf.skills])
return (
<Box>
<Heading as="h1">{elf.fullName}</Heading>
<SquareImage
src={`${assetsBucketBaseUrl}/raw/elfcardfigures/${elf.figureImage}.png`}
originalSize={720}
size={480}
/>
<Card mb={3}>
<Box sx={{ p: 1, borderBottom: 'default' }}>
<Flex>
{elf.tags.map(tag => {
return (
<Box key={tag.type} mr={1}>
<TagIcon type={tag.type} />
</Box>
)
})}
</Flex>
</Box>
<Box p={1}>
Ult CD: {elf.ultSkillCd} / Ult SP Cost: {elf.ultSkillCost}
</Box>
</Card>
<Heading as="h2">Skills</Heading>
{skillRows.map((row, index) => {
return (
<Card key={index} mb={2}>
{row.map(skill => {
return (
<Box
key={skill.id}
sx={{
borderBottom: 'default',
'&:last-child': {
borderBottom: 'none'
}
}}
>
<Box sx={{ borderBottom: 'default', p: 1 }}>
<Flex sx={{ alignItems: 'center' }}>
<Box sx={{ flexShrink: 0 }}>
<SquareImage
src={`${assetsBucketBaseUrl}/raw/elf_skill_icon/${skill.icon}.png`}
originalSize={40}
/>
</Box>
<Heading as={'h3'} sx={{ my: 0, ml: 1 }}>
<FormattedText>{skill.name}</FormattedText>
</Heading>
{/* {skill.id} */}
<Box sx={{ color: 'textMuted', ml: 1 }}>({skill.skillType})</Box>
</Flex>
</Box>
<Box sx={{ p: 1 }}>
<FormattedText>{formatSubSkillInfo(skill)}</FormattedText>
</Box>
</Box>
)
})}
</Card>
)
})}
</Box>
)
}
export default ElfShowPage
export async function getStaticProps({ locale, params }: NextPageContext & { params: { elfId: string } }) {
const elf = loadElfData(params.elfId)
return {
props: { elf }
}
}
export async function getStaticPaths() {
const elfCatalog = loadElfCatalog()
return {
paths: elfCatalog.map(catalogItem => {
return {
params: { elfId: catalogItem.id }
}
}),
fallback: false
}
}
-61
View File
@@ -1,61 +0,0 @@
/** @jsxImportSource theme-ui */
import { Box, Card } from '@theme-ui/components'
import { NextPageContext } from 'next'
import { Flex, Link } from 'theme-ui'
import { ElfCatalogItem } from '../../../../lib/v2/data/types'
import { loadElfCatalog } from '../../../../lib/v2/server/loadData'
import RarityBar from '../../../../components/v2/RarityBar'
import ElfIcon from '../../../../components/v2/ElfIcon'
interface ElfListPageProps {
elfs: ElfCatalogItem[]
}
const ElfListPage = ({ elfs }: ElfListPageProps) => {
return (
<Box>
<h1>Elfs</h1>
<Flex sx={{ flexWrap: 'wrap' }}>
{elfs
.sort((a, b) => {
const aId = getId(a.id)
const bId = getId(b.id)
return bId - aId
function getId(id: string): number {
return parseInt(id)
}
})
.map(elf => {
return (
<Box key={elf.id} m={2}>
<Link href={`/v2-pre/elfs/${elf.id}`}>
<Card p={1}>
<Box>
<ElfIcon icon={elf.cardIcon} />
<RarityBar rarity={elf.rarity} />
</Box>
<Box
sx={{ textAlign: 'center', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}
>
{elf.fullName}
</Box>
</Card>
</Link>
</Box>
)
})}
</Flex>
</Box>
)
}
export default ElfListPage
export async function getStaticProps({ locale }: NextPageContext) {
const elfs = loadElfCatalog()
return {
props: { elfs }
}
}
-30
View File
@@ -1,30 +0,0 @@
import Link from 'next/link'
import { Box } from 'theme-ui'
const HomePage = () => {
return (
<div>
<h1>V2 Pre</h1>
<Box>
<Link href="/v2-pre/battlesuits">Battlesuits</Link>
</Box>
<Box>
<Link href="/v2-pre/weapons">Weapons</Link>
</Box>
<Box>
<Link href="/v2-pre/stigmata">Stigmata</Link>
</Box>
<Box>
<Link href="/v2-pre/stigmata-sets">StigmataSets</Link>
</Box>
<Box>
<Link href="/v2-pre/elfs">Elfs</Link>
</Box>
<Box>
<Link href="/v2-pre/er">Elysian Realm</Link>
</Box>
</div>
)
}
export default HomePage
-78
View File
@@ -1,78 +0,0 @@
/** @jsxImportSource theme-ui */
import { Box, Card, Heading } from '@theme-ui/components'
import { NextPageContext } from 'next'
import { Flex, Link } from 'theme-ui'
import { StigmataCatalogItem } from '../../../../lib/v2/data/types'
import { loadStigmataCatalog } from '../../../../lib/v2/server/loadData'
import StigmaIcon from '../../../../components/v2/StigmaIcon'
import Honkai3rdLayout from '../../../../components/layouts/Honkai3rdLayout'
import { getI18NProps } from '../../../../server/i18n'
interface StigmataListPageProps {
stigmataCatalog: StigmataCatalogItem[]
}
const StigmataListPage = ({ stigmataCatalog }: StigmataListPageProps) => {
return (
<Honkai3rdLayout>
<Box>
<Heading as="h1">Stigmata (Single)</Heading>
<Flex sx={{ flexWrap: 'wrap' }}>
{stigmataCatalog
.filter(filterStigmata)
.sort(sortStigmata)
.map(stigma => {
return (
<Box key={stigma.id} m={2}>
<Link href={`/v2-pre/stigmata/${stigma.id}`}>
<Card p={1}>
<Flex sx={{ justifyContent: 'center' }}>
<StigmaIcon icon={stigma.icon} rarity={stigma.maxRarity} />
</Flex>
<Box
sx={{
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
px: 1,
width: 112,
textAlign: 'center'
}}
>
{stigma.name}
</Box>
{/* {stigma.id} */}
</Card>
</Link>
</Box>
)
})}
</Flex>
</Box>
</Honkai3rdLayout>
)
}
export default StigmataListPage
export async function getStaticProps({ locale }: NextPageContext) {
const stigmataCatalog = loadStigmataCatalog()
return {
props: { stigmataCatalog, ...(await getI18NProps(locale)) }
}
}
function filterStigmata(weapon: StigmataCatalogItem) {
return true
}
function sortStigmata(a: StigmataCatalogItem, b: StigmataCatalogItem) {
let aId = parseInt(a.id)
let bId = parseInt(b.id)
return bId - aId
}
@@ -1,133 +0,0 @@
import { NextPageContext } from 'next'
import { Box, Card, Flex, Heading } from 'theme-ui'
import FormattedText from '../../../../components/v2/FormattedText'
import { formatSubSkillInfo, replaceNewLine } from '../../../../lib/v2/data/formatText'
import { loadWeaponCatalog, loadWeaponData } from '../../../../lib/v2/server/loadData'
import { RootWeaponData } from '../../../../lib/v2/data/types'
import { Fragment } from 'react'
import { getWeaponTypeLabel } from '../../../../lib/v2/data/text'
import WeaponTypeIcon from '../../../../components/v2/WeaponTypeIcon'
import WeaponIcon from '../../../../components/v2/WeaponIcon'
import Honkai3rdLayout from '../../../../components/layouts/Honkai3rdLayout'
import Head from '../../../../components/atoms/Head'
import { useTranslation } from 'next-i18next'
import { getI18NProps } from '../../../../server/i18n'
import Breadcrumb from '../../../../components/organisms/Breadcrumb'
interface WeaponShowPageProps {
rootWeapon: RootWeaponData
}
const WeaponShowPage = ({ rootWeapon }: WeaponShowPageProps) => {
const { t } = useTranslation()
const weapon = rootWeapon.weapons[rootWeapon.weapons.length - 1]
const weaponTypeLabel = getWeaponTypeLabel(weapon.type)
const atk = Math.floor(weapon.attackBase + weapon.attackAdd * (weapon.maxLv - 1))
const crt = Math.floor(weapon.criticalBase + weapon.criticalAdd * (weapon.maxLv - 1))
return (
<Honkai3rdLayout>
<Head
title={`${weapon.name} - ${t('common.honkai-3rd')} - ${t('common.abyss-lab')}`}
description={`${t('common.honkai-3rd')} ${t('weapons-show.weapon')} / ${'⭐'.repeat(
weapon.rarity
)} / ${weaponTypeLabel} / ATK : ${atk} / CRT : ${crt}`}
canonicalHref={`/honkai3rd/v2/weapons/${weapon.id}`}
/>
<Box p={2}>
<Breadcrumb
items={[
{ href: '/honkai3rd', label: t('common.honkai-3rd') },
{ href: '/honkai3rd/v2/weapons', label: t('common.weapons') },
{ href: `/honkai3rd/v2/weapons/${weapon.id}`, label: weapon.name }
]}
/>
<Heading as="h1">{weapon.name}</Heading>
<Box mb={3}>
<WeaponIcon key={weapon.id} icon={weapon.icon} rarity={weapon.rarity} />
</Box>
<Card mb={3}>
<Box sx={{ p: 1, borderBottom: 'default' }}>{replaceNewLine(weapon.description)}</Box>
<Flex sx={{ alignItems: 'center', p: 1, borderBottom: 'default' }}>
<WeaponTypeIcon type={weapon.type} />
<Box ml={1}>{getWeaponTypeLabel(weapon.type)}</Box>
</Flex>
<Box sx={{ p: 1 }}>
ATK : {atk} / CRT : {crt} (at Max Lv {weapon.maxLv})
</Box>
</Card>
<Heading as="h2">Skills</Heading>
<Card>
{weapon.skills.map(skill => {
return (
<Fragment key={skill.id}>
<Box sx={{ borderBottom: 'default' }}>
<Heading
as="h3"
sx={{
p: 1,
m: 1
}}
>
{skill.name}
</Heading>
</Box>
<Box
sx={{
p: 1,
borderBottom: 'default',
'&:last-child': {
borderBottom: 'none'
}
}}
>
<FormattedText>
{formatSubSkillInfo({
info: skill.info,
maxLv: weapon.maxLv,
paramBase1: skill.param1,
paramBase2: skill.param2,
paramBase3: skill.param3,
paramAdd1: skill.param1Add,
paramAdd2: skill.param2Add,
paramAdd3: skill.param3Add
})}
</FormattedText>
</Box>
</Fragment>
)
})}
</Card>
{/* <pre>{JSON.stringify(weapon, null, 2)}</pre> */}
</Box>
</Honkai3rdLayout>
)
}
export default WeaponShowPage
export async function getStaticProps({ locale, params }: NextPageContext & { params: { weaponId: string } }) {
const rootWeapon = loadWeaponData(params.weaponId)
return {
props: { rootWeapon, ...(await getI18NProps(locale)) }
}
}
export async function getStaticPaths() {
const weaponCatalog = loadWeaponCatalog()
return {
paths: weaponCatalog.map(catalogItem => {
return {
params: { weaponId: catalogItem.id }
}
}),
fallback: false
}
}
-146
View File
@@ -1,146 +0,0 @@
/** @jsxImportSource theme-ui */
import { Box, Card } from '@theme-ui/components'
import { NextPageContext } from 'next'
import { Flex, Heading, Link } from 'theme-ui'
import { loadWeaponCatalog } from '../../../../lib/v2/server/loadData'
import { WeaponCatalogItem } from '../../../../lib/v2/data/types'
import WeaponIcon from '../../../../components/v2/WeaponIcon'
import Head from '../../../../components/atoms/Head'
import Honkai3rdLayout from '../../../../components/layouts/Honkai3rdLayout'
import { useTranslation } from 'next-i18next'
import { getI18NProps } from '../../../../server/i18n'
import Breadcrumb from '../../../../components/organisms/Breadcrumb'
interface WeaponListPageProps {
weaponCatalog: WeaponCatalogItem[]
}
const WeaponListPage = ({ weaponCatalog }: WeaponListPageProps) => {
const { t } = useTranslation()
return (
<Honkai3rdLayout>
<Head
title={`${t('common.weapons')} - ${t('common.honkai-3rd')} - ${t('common.abyss-lab')}`}
description={t('weapons-list.description')}
canonicalHref={`/honkai3rd/v2/weapons`}
/>
<Box p={2}>
<Breadcrumb
items={[
{ href: '/honkai3rd', label: t('common.honkai-3rd') },
{ href: '/honkai3rd/v2/weapons', label: t('common.weapons') }
]}
/>
<Heading as="h1">{t('common.weapons')}</Heading>
<Flex sx={{ flexWrap: 'wrap' }}>
{weaponCatalog
.filter(filterWeapons)
.sort(sortWeapons)
.map(weapon => {
return (
<Box key={weapon.id} m={1}>
<Link href={`/honkai3rd/v2/weapons/${weapon.id}`}>
<Card p={1}>
<Flex sx={{ justifyContent: 'center' }}>
<WeaponIcon icon={weapon.icon} rarity={weapon.maxRarity} />
</Flex>
<Box
sx={{
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
px: 1,
width: 112,
textAlign: 'center'
}}
>
{weapon.name}
</Box>
{/* {weapon.id} */}
</Card>
</Link>
</Box>
)
})}
</Flex>
</Box>
</Honkai3rdLayout>
)
}
export default WeaponListPage
export async function getStaticProps({ locale }: NextPageContext) {
const weaponCatalog = loadWeaponCatalog()
return {
props: { weaponCatalog, ...(await getI18NProps(locale)) }
}
}
function filterWeapons(weapon: WeaponCatalogItem) {
// Valid weapons
// 24XXX EX Weapons
// 21XXX Sprit Weapon
// 19XXX Alloy Weapons
// 10XXX Regular Weapons
// Event Weapon?
if (weapon.id.startsWith('18') && weapon.id.length === 5) {
return false
}
// Test Data?
if (weapon.id.startsWith('1023') && weapon.id.length === 6) {
return false
}
// Unreleased(Number looks good but lacking info)
switch (weapon.id) {
case '24000': // 2nd relic alpha
case '24040': // Removed 7 Thunders
case '24034': // PE Temp Lance
case '24024': // keqing ex
case '20765': // ?
case '10243': // No Name
case '10219': // ?
case '10179': // ?
return false
}
// Divine Keys
switch (weapon.id) {
case '20000': // Taixuan Sword
case '10248': // Abyss Flower
case '10267': // Eden's star anti entropy
case '10263': // 7 Thunders
case '10247': // Abyss
case '10235': // Fenghuang pistols
case '10234': // Fenghuang fists
case '10223': // Eden twin stars
case '10212': // Pledge of Sakura
case '10211': // Pledge of Judah
case '10209': // Might of an utu
case '10199': // Cleaver of Shamash
case '10163': // Shamash pistols
// The below don't have duplicated data
// case '10180': // Eden's star
// case '10169': // Taxiuan fists
// case '10158': // Jizo Mitama
return false
}
return true
}
function sortWeapons(a: WeaponCatalogItem, b: WeaponCatalogItem) {
let aId = parseInt(a.id)
const aTypeOrder = aId > 21000 && aId < 22000 ? 2 : aId > 19000 && aId < 20000 ? 1 : 0
let bId = parseInt(b.id)
const bTypeOrder = bId > 21000 && bId < 22000 ? 2 : bId > 19000 && bId < 20000 ? 1 : 0
if (aTypeOrder !== bTypeOrder) {
return aTypeOrder - bTypeOrder
}
return bId - aId
}
@@ -1,264 +0,0 @@
/** @jsxImportSource theme-ui */
import { Box, Heading, Flex, Text, Paragraph } from '@theme-ui/components'
import { NextPageContext } from 'next'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
import GanttChart from '../../../components/organisms/GanttChart'
import {
getVersion,
listVersionData,
} from '../../../server/data/honkai3rd/versions'
import { format as formatDate } from 'date-fns'
import { getBattlesuitById } from '../../../server/data/honkai3rd/battlesuits'
import SquareImageBox from '../../../components/atoms/SquareImageBox'
import { getWeaponById } from '../../../server/data/honkai3rd/weapons'
import ScrollContainer from 'react-indiana-drag-scroll'
import { addDateToDateString, getDateString } from '../../../lib/string'
import { BattlesuitData } from '../../../lib/honkai3rd/battlesuits'
import { WeaponData } from '../../../lib/honkai3rd/weapons'
import { VersionData } from '../../../lib/honkai3rd/versions'
import { generateI18NPaths, getI18NProps } from '../../../server/i18n'
import { useTranslation } from '../../../lib/i18n'
import Head from '../../../components/atoms/Head'
import PageLink from '../../../components/atoms/PageLink'
import { assetsBucketBaseUrl } from '../../../lib/consts'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
import { useEffect, useState } from 'react'
import BossTable from '../../../components/organisms/BossTable'
import StigmataSetCard from '../../../components/molecules/StigmataSetCard'
import WeaponCard from '../../../components/molecules/WeaponCard'
import BattlesuitCard from '../../../components/molecules/BattlesuitCard'
import { getStigmataSetBySetId } from '../../../server/data/honkai3rd/stigmata'
import { StigmataSet } from '../../../lib/honkai3rd/stigmata'
interface VersionShowPageProps {
versionData: VersionData
battlesuits: BattlesuitData[]
weapons: WeaponData[]
stigmataSets: StigmataSet[]
}
const VersionShowPage = ({
versionData,
battlesuits,
weapons,
stigmataSets,
}: VersionShowPageProps) => {
const { t } = useTranslation()
const [today, setToday] = useState<Date | null>(null)
useEffect(() => {
setToday(new Date(getDateString(new Date())))
}, [])
return (
<Honkai3rdLayout>
<Head
title={`v${versionData.version} ${versionData.name} - ${t(
'common.honkai-3rd'
)} - ${t('common.abyss-lab')}`}
description={`${t('common.honkai-3rd')} ${t('versions.version')} ${
versionData.name
} / ${t('versions.new-battlesuits')}: ${battlesuits
.map((battlesuit) => {
return battlesuit.name
})
.join(',')} / ${t('versions.new-weapons')}: ${weapons
.map((weapon) => {
return weapon.name
})
.join(',')}`}
canonicalHref={`/honkai3rd/versions/${versionData.version}`}
/>
<Box p={3}>
<Breadcrumb
items={[
{ href: '/honkai3rd', label: t('common.honkai-3rd') },
{ href: '/honkai3rd/versions', label: t('common.versions') },
{
href: `/honkai3rd/versions/${versionData.version}`,
label: versionData.version,
},
]}
/>
<Box>
<Box mb={2}>
{versionData.previousVersion != null && (
<PageLink
href={`/honkai3rd/versions/${versionData.previousVersion}`}
mr={2}
>
{t('versions.previous')} (v{versionData.previousVersion})
</PageLink>
)}
{versionData.nextVersion != null && (
<PageLink href={`/honkai3rd/versions/${versionData.nextVersion}`}>
{t('versions.next')} (v{versionData.nextVersion})
</PageLink>
)}
</Box>
<Heading as='h1'>
v{versionData.version} : {versionData.name}
</Heading>
<Box mb={4}>
{formatDate(new Date(versionData.duration[0]), 'PP')} -{' '}
{versionData.duration[1] != null
? formatDate(new Date(versionData.duration[1]), 'PP')
: ''}
</Box>
<Box>
<Heading as='h3' mb={2}>
{t('versions.new-battlesuits')}
</Heading>
<Box mb={2} sx={{ display: 'inline-block' }}>
{battlesuits.map((battlesuit) => {
return (
<BattlesuitCard
key={battlesuit.id}
battlesuit={battlesuit}
size='sm'
/>
)
})}
</Box>
<Heading as='h3' mb={2}>
{t('versions.new-weapons')}
</Heading>
<Box mb={2} sx={{ display: 'inline-block' }}>
{weapons.map((weapon) => {
return <WeaponCard key={weapon.id} weapon={weapon} size='sm' />
})}
</Box>
<Heading as='h3' mb={2}>
{t('versions.new-stigmata-sets')}
</Heading>
<Box mb={2} sx={{ display: 'inline-block' }}>
{stigmataSets.map((stigmataSet) => {
return (
<StigmataSetCard
key={stigmataSet.id}
stigmataSet={stigmataSet}
size='sm'
/>
)
})}
</Box>
</Box>
<Heading as='h3' mb={2}>
{t('versions.weekly-bosses')}
</Heading>
<Box sx={{ mb: 3 }}>
<BossTable versionData={versionData} today={today} />
</Box>
<Heading as='h3' mb={2}>
{t('versions.supply-events')}
</Heading>
<Box mb={2}>
<ScrollContainer vertical={false}>
<GanttChart
items={versionData.supplyEvents.map((supplyEventData) => {
const imgSrc = getIconSrcFromItem(supplyEventData.featured[0])
return {
id: `${supplyEventData.duration[0]}/${supplyEventData.duration[1]}/${supplyEventData.name}`,
label: (
<Flex sx={{ alignItems: 'center' }}>
{!supplyEventData.verified && (
<Text sx={{ flexShrink: 0 }}></Text>
)}
{imgSrc != null && (
<SquareImageBox
size={20}
src={imgSrc}
alt={supplyEventData.featured[0].id}
mr={1}
/>
)}
<Text>{supplyEventData.name}</Text>
</Flex>
),
duration: supplyEventData.duration,
row: supplyEventData.track,
}
})}
today={today}
startDate={versionData.duration[0]}
endDate={
versionData.duration[1] != null
? versionData.duration[1]
: addDateToDateString(versionData.duration[0], {
weeks: 6,
})
}
/>
</ScrollContainer>
</Box>
<Paragraph mb={4}>{t('versions.supply-events-disclaimer')}</Paragraph>
</Box>
</Box>
</Honkai3rdLayout>
)
}
export default VersionShowPage
export async function getStaticProps({
params,
locale,
}: NextPageContext & { params: { versionId: string } }) {
const versionData = getVersion(params.versionId, locale)!
const battlesuits = versionData.newBattlesuits.map((battlesuitId) => {
return getBattlesuitById(battlesuitId, locale)
})
const weapons = versionData.newWeapons.map((weaponId) => {
return getWeaponById(weaponId, locale)
})
const stigmataSets = versionData.newStigmataSets.map((stigmataSetId) => {
return getStigmataSetBySetId(stigmataSetId, locale)
})
return {
props: {
versionData,
battlesuits,
weapons,
stigmataSets,
...(await getI18NProps(locale)),
},
}
}
export async function getStaticPaths() {
return {
paths: generateI18NPaths(
listVersionData().map((versionData) => {
return {
params: {
versionId: versionData.version.toString(),
},
}
})
),
fallback: false,
}
}
function getIconSrcFromItem(item: { type: string; id: string }): string | null {
switch (item.type) {
case 'battlesuit':
return `${assetsBucketBaseUrl}/honkai3rd/battlesuits/portrait-${item.id}.png`
case 'weapon':
return `${assetsBucketBaseUrl}/honkai3rd/weapons/${item.id}.png`
case 'stigmata':
return `${assetsBucketBaseUrl}/honkai3rd/stigmata/icon-${item.id}.png`
case 'elf':
return `${assetsBucketBaseUrl}/honkai3rd/elfs/icon-${item.id}.png`
case 'outfit':
return `${assetsBucketBaseUrl}/honkai3rd/outfits/portrait-${item.id}.webp`
}
return null
}
-86
View File
@@ -1,86 +0,0 @@
/** @jsxImportSource theme-ui */
import { Box, Heading, Link } from '@theme-ui/components'
import NextLink from 'next/link'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
import { listVersionData } from '../../../server/data/honkai3rd/versions'
import { format as formatDate } from 'date-fns'
import { BattlesuitData } from '../../../lib/honkai3rd/battlesuits'
import { WeaponData } from '../../../lib/honkai3rd/weapons'
import { VersionData } from '../../../lib/honkai3rd/versions'
import { getI18NProps } from '../../../server/i18n'
import { NextPageContext } from 'next'
import { useTranslation } from '../../../lib/i18n'
import Head from '../../../components/atoms/Head'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
import { StigmataSet } from '../../../lib/honkai3rd/stigmata'
interface VersionIndexPageProps {
versionDataList: VersionData[]
currentVersionData: VersionData
currentVersionNewBattlesuits: BattlesuitData[]
currentVersionNewWeapons: WeaponData[]
currentVersionNewStigmataSets: StigmataSet[]
}
const VersionIndexPage = ({ versionDataList }: VersionIndexPageProps) => {
const { t } = useTranslation()
return (
<Honkai3rdLayout>
<Head
title={`${t('common.versions')} - ${t('common.honkai-3rd')} - ${t(
'common.abyss-lab'
)}`}
description={t('versions.list-page-description')}
canonicalHref={`/honkai3rd/versions`}
/>
<Box p={3}>
<Breadcrumb
items={[
{ href: '/honkai3rd', label: t('common.honkai-3rd') },
{ href: '/honkai3rd/versions', label: t('common.versions') },
]}
/>
<Heading as='h1' mb={3}>
{t('versions.versions')}
</Heading>
<Box>
{versionDataList.map((versionData) => {
return (
<Box key={versionData.version} mb={2}>
<Heading as='h3'>
<NextLink
href={`/honkai3rd/versions/${versionData.version}`}
passHref
>
<Link>
{versionData.version} : {versionData.name} (
{formatDate(new Date(versionData.duration[0]), 'PP')} -{' '}
{versionData.duration[1] != null
? formatDate(new Date(versionData.duration[1]), 'PP')
: ''}
)
</Link>
</NextLink>
</Heading>
</Box>
)
})}
</Box>
</Box>
</Honkai3rdLayout>
)
}
export async function getStaticProps({ locale }: NextPageContext) {
return {
props: {
versionDataList: listVersionData(locale),
...(await getI18NProps(locale)),
},
}
}
export default VersionIndexPage
+75 -131
View File
@@ -1,32 +1,30 @@
/** @jsxImportSource theme-ui */
import { Flex, Box, Card, Heading, Paragraph } from '@theme-ui/components'
import { NextPageContext } from 'next'
import SquareImageBox from '../../../components/atoms/SquareImageBox'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
import { weaponCategories, WeaponData } from '../../../lib/honkai3rd/weapons'
import { generateI18NPaths, getI18NProps } from '../../../server/i18n'
import { getWeaponById, getWeaponMapByIds, listWeapons } from '../../../server/data/honkai3rd/weapons'
import { useTranslation } from '../../../lib/i18n'
import Head from '../../../components/atoms/Head'
import PageLink from '../../../components/atoms/PageLink'
import { assetsBucketBaseUrl } from '../../../lib/consts'
import { Box, Card, Flex, Heading } from 'theme-ui'
import FormattedText from '../../../components/v2/FormattedText'
import { formatSubSkillInfo, replaceNewLine } from '../../../lib/v2/data/formatText'
import { loadWeaponCatalog, loadWeaponData } from '../../../lib/v2/server/loadData'
import { RootWeaponData } from '../../../lib/v2/data/types'
import { Fragment } from 'react'
import { getWeaponTypeLabel } from '../../../lib/v2/data/text'
import WeaponTypeIcon from '../../../components/v2/WeaponTypeIcon'
import WeaponIcon from '../../../components/v2/WeaponIcon'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
import { getBattlesuitMapByIds } from '../../../server/data/honkai3rd/battlesuits'
import { BattlesuitData } from '../../../lib/honkai3rd/battlesuits'
import BattlesuitCard from '../../../components/molecules/BattlesuitCard'
import WeaponCard from '../../../components/molecules/WeaponCard'
import Head from '../../../components/atoms/Head'
import { useTranslation } from 'next-i18next'
import { getI18NProps } from '../../../server/i18n'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
interface WeaponShowPageProps {
weapon: WeaponData
battlesuitMap: { [key: string]: BattlesuitData }
weaponMap: { [key: string]: WeaponData }
rootWeapon: RootWeaponData
}
const WeaponShowPage = ({ weapon, battlesuitMap, weaponMap }: WeaponShowPageProps) => {
const WeaponShowPage = ({ rootWeapon }: WeaponShowPageProps) => {
const { t } = useTranslation()
const weapon = rootWeapon.weapons[rootWeapon.weapons.length - 1]
const weaponCategoryName = t(`weapons-show.${weapon.category}`)
const weaponCategory = weaponCategories.find(category => category.value === weapon.category)
const weaponTypeLabel = getWeaponTypeLabel(weapon.type)
const atk = Math.floor(weapon.attackBase + weapon.attackAdd * (weapon.maxLv - 1))
const crt = Math.floor(weapon.criticalBase + weapon.criticalAdd * (weapon.maxLv - 1))
return (
<Honkai3rdLayout>
@@ -34,112 +32,78 @@ const WeaponShowPage = ({ weapon, battlesuitMap, weaponMap }: WeaponShowPageProp
title={`${weapon.name} - ${t('common.honkai-3rd')} - ${t('common.abyss-lab')}`}
description={`${t('common.honkai-3rd')} ${t('weapons-show.weapon')} / ${'⭐'.repeat(
weapon.rarity
)} / ${weaponCategoryName} / ATK : ${weapon.atk} / CRT : ${weapon.crt}`}
canonicalHref={`/honkai3rd/weapons/${weapon.id}`}
)} / ${weaponTypeLabel} / ATK : ${atk} / CRT : ${crt}`}
canonicalHref={`/honkai3rd/v2/weapons/${weapon.id}`}
/>
<Box p={3}>
<Box p={2}>
<Breadcrumb
items={[
{ href: '/honkai3rd', label: t('common.honkai-3rd') },
{ href: '/honkai3rd/weapons', label: t('common.weapons') },
{
href: `/honkai3rd/weapons/${weapon.id}`,
label: weapon.name
}
{ href: '/honkai3rd/v2/weapons', label: t('common.weapons') },
{ href: `/honkai3rd/v2/weapons/${weapon.id}`, label: weapon.name }
]}
/>
<Heading as="h1">{weapon.name}</Heading>
<Box mb={3}>
<SquareImageBox
size={100}
alt={weapon.name}
src={`${assetsBucketBaseUrl}/honkai3rd/weapons/${weapon.id}.png`}
/>
<WeaponIcon key={weapon.id} icon={weapon.icon} rarity={weapon.rarity} />
</Box>
<Card mb={3}>
<Box p={2} sx={{ borderBottom: 'default' }}>
<PageLink
href={{
pathname: '/honkai3rd/weapons',
query: { category: weapon.category }
}}
>
<Flex>
{weaponCategory != null && (
<SquareImageBox
size={30}
src={`${assetsBucketBaseUrl}/honkai3rd/${weaponCategory.icon}.png`}
alt={weaponCategoryName}
mr={1}
/>
)}
{weaponCategoryName}
<Box sx={{ p: 1, borderBottom: 'default' }}>{replaceNewLine(weapon.description)}</Box>
<Flex sx={{ alignItems: 'center', p: 1, borderBottom: 'default' }}>
<WeaponTypeIcon type={weapon.type} />
<Box ml={1}>{getWeaponTypeLabel(weapon.type)}</Box>
</Flex>
</PageLink>
<Box sx={{ p: 1 }}>
ATK : {atk} / CRT : {crt} (at Max Lv {weapon.maxLv})
</Box>
<Box p={2} sx={{ borderBottom: 'default' }}>
{'⭐'.repeat(weapon.rarity)}
</Box>
<Box p={2}>
ATK : {weapon.atk} / CRT : {weapon.crt}
</Box>
{weapon.battlesuits != null && weapon.battlesuits.length > 0 && (
<Box sx={{ p: 2, borderTop: 'default' }}>
<Heading as="h4">{t('weapons-show.best-on')}</Heading>
{weapon.battlesuits.map(({ id: battlesuitId }) => {
return <BattlesuitCard key={battlesuitId} size="sm" battlesuit={battlesuitMap[battlesuitId]} />
})}
</Box>
)}
{weapon.priWeapon != null && (
<Box sx={{ p: 2, borderTop: 'default' }}>
<Heading as="h4">{t('weapons-show.pri-weapon')}</Heading>
<WeaponCard size="sm" weapon={weaponMap[weapon.priWeapon]} />
</Box>
)}
{weapon.originalWeapons != null && weapon.originalWeapons.length > 0 && (
<Box sx={{ p: 2, borderTop: 'default' }}>
<Heading as="h4">{t('weapons-show.original-weapon')}</Heading>
{weapon.originalWeapons.map(originalWeaponId => {
return <WeaponCard key={originalWeaponId} size="sm" weapon={weaponMap[originalWeaponId]} />
})}
</Box>
)}
{/* {weapon.sources != null && (
<Box sx={{ p: 2, borderTop: 'default' }}>
<Heading as='h4'>{t('weapons-show.sources')}</Heading>
{weapon.sources
.sort((a, b) => a.type.localeCompare(b.type))
.map((source) => {
return <SourceCard key={source.type} source={source} />
})}
</Box>
)} */}
</Card>
<Box>
<Heading as="h2">Skills</Heading>
<Card>
{weapon.skills.map(skill => {
return (
<Card key={skill.name} mb={3}>
<Heading as="h3" p={2} m={0} sx={{ borderBottom: 'default' }}>
{skill.name}
</Heading>
<Paragraph
p={2}
<Fragment key={skill.id}>
<Box sx={{ borderBottom: 'default' }}>
<Heading
as="h3"
sx={{
whiteSpace: 'pre-wrap'
p: 1,
m: 1
}}
>
{skill.description}
</Paragraph>
</Card>
{skill.name}
</Heading>
</Box>
<Box
sx={{
p: 1,
borderBottom: 'default',
'&:last-child': {
borderBottom: 'none'
}
}}
>
<FormattedText>
{formatSubSkillInfo({
info: skill.info,
maxLv: weapon.maxLv,
paramBase1: skill.param1,
paramBase2: skill.param2,
paramBase3: skill.param3,
paramAdd1: skill.param1Add,
paramAdd2: skill.param2Add,
paramAdd3: skill.param3Add
})}
</FormattedText>
</Box>
</Fragment>
)
})}
</Box>
</Card>
{/* <pre>{JSON.stringify(weapon, null, 2)}</pre> */}
</Box>
</Honkai3rdLayout>
)
@@ -147,43 +111,23 @@ const WeaponShowPage = ({ weapon, battlesuitMap, weaponMap }: WeaponShowPageProp
export default WeaponShowPage
export async function getStaticProps({ params, locale }: NextPageContext & { params: { weaponId: string } }) {
const weapon = getWeaponById(params.weaponId, locale)
const battlesuitMap = getBattlesuitMapByIds(
weapon != null && weapon.battlesuits != null ? weapon.battlesuits.map(({ id }) => id) : [],
locale
)
const weaponIds = []
if (weapon != null) {
if (weapon.priWeapon != null) {
weaponIds.push(weapon.priWeapon)
}
if (weapon.originalWeapons != null) {
weaponIds.push(...weapon.originalWeapons)
}
}
const weaponMap = getWeaponMapByIds(weaponIds, locale)
export async function getStaticProps({ locale, params }: NextPageContext & { params: { weaponId: string } }) {
const rootWeapon = loadWeaponData(params.weaponId)
return {
props: {
weapon,
battlesuitMap,
weaponMap,
...(await getI18NProps(locale))
}
props: { rootWeapon, ...(await getI18NProps(locale)) }
}
}
export async function getStaticPaths() {
const weaponCatalog = loadWeaponCatalog()
return {
paths: generateI18NPaths(
listWeapons().map(weapon => {
paths: weaponCatalog.map(catalogItem => {
return {
params: { weaponId: weapon.id }
params: { weaponId: catalogItem.id }
}
})
),
}),
fallback: false
}
}
+107 -85
View File
@@ -1,91 +1,69 @@
/** @jsxImportSource theme-ui */
import { Box, Heading, Flex } from '@theme-ui/components'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
import { listWeapons } from '../../../server/data/honkai3rd/weapons'
import { pick } from 'ramda'
import { useMemo } from 'react'
import FilterButton from '../../../components/atoms/FilterButton'
import { useRouter } from 'next/router'
import { weaponCategories, WeaponData } from '../../../lib/honkai3rd/weapons'
import WeaponCard from '../../../components/molecules/WeaponCard'
import { getI18NProps } from '../../../server/i18n'
import { Box, Card } from '@theme-ui/components'
import { NextPageContext } from 'next'
import { useTranslation, translate } from '../../../lib/i18n'
import { Flex, Heading, Link } from 'theme-ui'
import { loadWeaponCatalog } from '../../../lib/v2/server/loadData'
import { WeaponCatalogItem } from '../../../lib/v2/data/types'
import WeaponIcon from '../../../components/v2/WeaponIcon'
import Head from '../../../components/atoms/Head'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
type WeaponListItemData = Pick<WeaponData, 'id' | 'name' | 'rarity' | 'category'>
import { useTranslation } from 'next-i18next'
import { getI18NProps } from '../../../server/i18n'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
interface WeaponListPageProps {
weaponDataList: WeaponListItemData[]
weaponCatalog: WeaponCatalogItem[]
}
const weaponFilterOptions = [{ value: 'all', label: 'All', krLabel: '전체', icon: undefined }, ...weaponCategories]
const WeaponListPage = ({ weaponDataList }: WeaponListPageProps) => {
const { query, locale } = useRouter()
const WeaponListPage = ({ weaponCatalog }: WeaponListPageProps) => {
const { t } = useTranslation()
const categoryFilter = useMemo(() => {
if (query.category == null) {
return 'all'
}
return typeof query.category === 'string' ? query.category : query.category[0]
}, [query])
const weaponList = useMemo(() => {
return weaponDataList.map(weapon => {
const hidden = isWeaponHidden(weapon, categoryFilter)
return <WeaponCard key={weapon.id} weapon={weapon} hidden={hidden} />
})
}, [weaponDataList, categoryFilter])
return (
<Honkai3rdLayout>
<Box>
<Head
title={`${t('common.weapons')} - ${t('common.honkai-3rd')} - ${t('common.abyss-lab')}`}
description={t('weapons-list.description')}
canonicalHref={`/honkai3rd/weapons`}
canonicalHref={`/honkai3rd/v2/weapons`}
/>
<Box p={3}>
<Box p={2}>
<Breadcrumb
items={[
{ href: '/honkai3rd', label: t('common.honkai-3rd') },
{ href: '/honkai3rd/weapons', label: t('common.weapons') }
{ href: '/honkai3rd/v2/weapons', label: t('common.weapons') }
]}
/>
<Heading as="h1">{t('weapons-list.weapons')}</Heading>
<Box>
<Heading as="h3">{t('weapons-list.filter-by-category')}</Heading>
<Flex mb={2} sx={{ flexWrap: 'wrap' }}>
{weaponFilterOptions.map(({ value, label, krLabel, icon }) => {
<Heading as="h1">{t('common.weapons')}</Heading>
<Flex sx={{ flexWrap: 'wrap' }}>
{weaponCatalog
.filter(filterWeapons)
.sort(sortWeapons)
.map(weapon => {
return (
<FilterButton
key={value}
href={`?category=${value}`}
active={value === categoryFilter}
icon={icon}
label={translate(locale, { 'ko-KR': krLabel }, label)}
m={1}
/>
<Box key={weapon.id} m={1}>
<Link href={`/honkai3rd/v2/weapons/${weapon.id}`}>
<Card p={1}>
<Flex sx={{ justifyContent: 'center' }}>
<WeaponIcon icon={weapon.icon} rarity={weapon.maxRarity} />
</Flex>
<Box
sx={{
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
px: 1,
width: 112,
textAlign: 'center'
}}
>
{weapon.name}
</Box>
{/* {weapon.id} */}
</Card>
</Link>
</Box>
)
})}
</Flex>
</Box>
<Flex
sx={{
flexWrap: 'wrap',
justifyContent: 'space-around'
}}
>
{weaponList}
</Flex>
</Box>
</Box>
</Honkai3rdLayout>
)
}
@@ -93,32 +71,76 @@ const WeaponListPage = ({ weaponDataList }: WeaponListPageProps) => {
export default WeaponListPage
export async function getStaticProps({ locale }: NextPageContext) {
const weaponCatalog = loadWeaponCatalog()
return {
props: {
weaponDataList: listWeapons(locale).map(weapon => {
return pick(['name', 'id', 'rarity', 'category'], weapon)
}),
...(await getI18NProps(locale))
}
props: { weaponCatalog, ...(await getI18NProps(locale)) }
}
}
function isWeaponHidden(weapon: WeaponListItemData, filter: string): boolean {
switch (filter) {
case 'pistol':
case 'katana':
case 'cannon':
case 'greatsword':
case 'cross':
case 'gauntlet':
case 'scythe':
case 'lance':
case 'bow':
case 'chakram':
case 'javelin':
return weapon.category !== filter
default:
case 'all':
function filterWeapons(weapon: WeaponCatalogItem) {
// Valid weapons
// 24XXX EX Weapons
// 21XXX Sprit Weapon
// 19XXX Alloy Weapons
// 10XXX Regular Weapons
// Event Weapon?
if (weapon.id.startsWith('18') && weapon.id.length === 5) {
return false
}
// Test Data?
if (weapon.id.startsWith('1023') && weapon.id.length === 6) {
return false
}
// Unreleased(Number looks good but lacking info)
switch (weapon.id) {
case '24000': // 2nd relic alpha
case '24040': // Removed 7 Thunders
case '24034': // PE Temp Lance
case '24024': // keqing ex
case '20765': // ?
case '10243': // No Name
case '10219': // ?
case '10179': // ?
return false
}
// Divine Keys
switch (weapon.id) {
case '20000': // Taixuan Sword
case '10248': // Abyss Flower
case '10267': // Eden's star anti entropy
case '10263': // 7 Thunders
case '10247': // Abyss
case '10235': // Fenghuang pistols
case '10234': // Fenghuang fists
case '10223': // Eden twin stars
case '10212': // Pledge of Sakura
case '10211': // Pledge of Judah
case '10209': // Might of an utu
case '10199': // Cleaver of Shamash
case '10163': // Shamash pistols
// The below don't have duplicated data
// case '10180': // Eden's star
// case '10169': // Taxiuan fists
// case '10158': // Jizo Mitama
return false
}
return true
}
function sortWeapons(a: WeaponCatalogItem, b: WeaponCatalogItem) {
let aId = parseInt(a.id)
const aTypeOrder = aId > 21000 && aId < 22000 ? 2 : aId > 19000 && aId < 20000 ? 1 : 0
let bId = parseInt(b.id)
const bTypeOrder = bId > 21000 && bId < 22000 ? 2 : bId > 19000 && bId < 20000 ? 1 : 0
if (aTypeOrder !== bTypeOrder) {
return aTypeOrder - bTypeOrder
}
return bId - aId
}