Replace version page with supply event page
This commit is contained in:
@@ -0,0 +1 @@
|
||||
https://honkaiimpact3.mihoyo.com/global/en-us/news/17390
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"version": "5.3",
|
||||
"name": "Post Dusk Odyssey",
|
||||
"duration": ["2021-12-02", "2022-01-13"],
|
||||
"newBattlesuits": ["sns"],
|
||||
"newWeapons": ["hekate-s-sombre", "miracle-kissy-pillow"],
|
||||
"verified": true
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"version": "5.4",
|
||||
"name": "Silverwing Beyond",
|
||||
"duration": ["2022-01-13"],
|
||||
"newBattlesuits": [],
|
||||
"newWeapons": [],
|
||||
"verified": false
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
import { Box, Flex, Text } from '@theme-ui/components'
|
||||
import {
|
||||
differenceInCalendarDays,
|
||||
differenceInCalendarWeeks,
|
||||
format,
|
||||
} from 'date-fns'
|
||||
import { times } from 'ramda'
|
||||
import { useMemo, MouseEventHandler } from 'react'
|
||||
|
||||
export interface GanttChartItem {
|
||||
id: string
|
||||
row: number
|
||||
duration: [string, string]
|
||||
label: React.ReactNode
|
||||
onClick?: MouseEventHandler<HTMLDivElement>
|
||||
}
|
||||
|
||||
interface GanttChartProps {
|
||||
startDate: string
|
||||
endDate: string
|
||||
today: string
|
||||
items: GanttChartItem[]
|
||||
}
|
||||
|
||||
const GanttChart = ({
|
||||
startDate: startDateString,
|
||||
endDate: endDateString,
|
||||
today: todayDateString,
|
||||
items,
|
||||
}: GanttChartProps) => {
|
||||
const startDate = new Date(startDateString)
|
||||
const endDate = new Date(endDateString)
|
||||
const todayDate = new Date(todayDateString)
|
||||
|
||||
const totalDays = differenceInCalendarDays(endDate, startDate)
|
||||
const totalWeeks = differenceInCalendarWeeks(endDate, startDate)
|
||||
|
||||
const totalRow = useMemo(() => {
|
||||
return items.reduce((max, item) => {
|
||||
return item.row > max ? item.row : max
|
||||
}, 1)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Box mb={3} sx={{ width: totalWeeks * 280 }}>
|
||||
<Flex
|
||||
sx={{
|
||||
marginLeft: -20,
|
||||
}}
|
||||
>
|
||||
{times((index) => {
|
||||
const weekNumber = index + 1
|
||||
return (
|
||||
<Box
|
||||
key={`week-${weekNumber}`}
|
||||
sx={{
|
||||
width: 280,
|
||||
textAlign: 'center',
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
Week {weekNumber}
|
||||
</Box>
|
||||
)
|
||||
}, totalWeeks)}
|
||||
</Flex>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
height: totalRow * 50 + 40,
|
||||
borderBottomStyle: 'solid',
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: 'gray.5',
|
||||
borderTopStyle: 'solid',
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: 'gray.5',
|
||||
}}
|
||||
>
|
||||
{times((index) => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
height: totalRow * 50 + 40,
|
||||
borderRightStyle: 'solid',
|
||||
borderRightWidth: 1,
|
||||
borderRightColor: index % 7 === 6 ? 'gray.5' : 'gray.3',
|
||||
left: (index + 1) * 40 - 1 - 20,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}, totalDays)}
|
||||
<Box
|
||||
sx={{
|
||||
boxSizing: 'border-box',
|
||||
position: 'absolute',
|
||||
width: 40,
|
||||
height: totalRow * 50 + 40,
|
||||
backgroundColor: 'blue.5',
|
||||
borderStyle: 'solid',
|
||||
borderColor: 'blue.7',
|
||||
opacity: 0.2,
|
||||
zIndex: 20,
|
||||
borderRadius: 4,
|
||||
left: differenceInCalendarDays(todayDate, startDate) * 40 - 20,
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
/>
|
||||
|
||||
<Text
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
color: 'blue.7',
|
||||
transform: 'rotate(90deg)',
|
||||
zIndex: 21,
|
||||
top: 150,
|
||||
left:
|
||||
differenceInCalendarDays(new Date(todayDate), startDate) * 40 -
|
||||
150,
|
||||
width: 300,
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
>
|
||||
Today ({format(todayDate, 'P')})
|
||||
</Text>
|
||||
|
||||
{items.map((item) => {
|
||||
const offset =
|
||||
differenceInCalendarDays(new Date(item.duration[0]), startDate) * 40
|
||||
const length =
|
||||
differenceInCalendarDays(
|
||||
new Date(item.duration[1]),
|
||||
new Date(item.duration[0])
|
||||
) * 40
|
||||
|
||||
return (
|
||||
<Flex
|
||||
key={item.id}
|
||||
py={2}
|
||||
px={3}
|
||||
sx={{
|
||||
boxSizing: 'border-box',
|
||||
position: 'absolute',
|
||||
borderColor: 'gray.5',
|
||||
borderWidth: 1,
|
||||
borderStyle: 'solid',
|
||||
backgroundColor: 'white',
|
||||
width: length,
|
||||
left: offset,
|
||||
top: (item.row - 1) * 50 + 20,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
height: 40,
|
||||
borderRadius: 40,
|
||||
fontWeight: 700,
|
||||
zIndex: 10,
|
||||
'&:hover': {
|
||||
minWidth: length,
|
||||
width: 'inherit',
|
||||
zIndex: 25,
|
||||
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)',
|
||||
transition: 'box-shadow 200ms ease-in-out',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{item.label}
|
||||
</Flex>
|
||||
)
|
||||
})}
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default GanttChart
|
||||
@@ -16,6 +16,11 @@ const Honkai3rdNavigator = () => {
|
||||
</NextLink>
|
||||
</Heading>
|
||||
</Flex>
|
||||
<NextLink href='/honkai3rd/versions' passHref>
|
||||
<NavLink mr={3} sx={{ fontFamily: 'monospace' }}>
|
||||
Versions
|
||||
</NavLink>
|
||||
</NextLink>
|
||||
<Flex sx={{ height: 40, alignItems: 'center' }}>
|
||||
<NextLink href='/honkai3rd/battlesuits' passHref>
|
||||
<NavLink mr={3} sx={{ fontFamily: 'monospace' }}>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { readdirSync, readJsonFileSync } from '../../lib/data'
|
||||
|
||||
export interface SupplyEvent {
|
||||
export interface SupplyEventData {
|
||||
id: string
|
||||
verified: boolean
|
||||
track: number
|
||||
@@ -33,46 +33,20 @@ export interface SupplyEvent {
|
||||
}[]
|
||||
}
|
||||
|
||||
const supplyEventFileNameList = readdirSync('supply-events/global-americas')
|
||||
const supplyEventFileList = supplyEventFileNameList
|
||||
.map((fileName) => {
|
||||
const filePathname = 'supply-events/global-americas/' + fileName
|
||||
const data = readJsonFileSync(filePathname)
|
||||
export function listSupplyEventsByVersion(version: string) {
|
||||
const supplyEventsDirectoryPathname = `versions/${version}/supply-events`
|
||||
|
||||
return { ...data, id: fileName.replace(/\.json/, '') } as SupplyEvent
|
||||
})
|
||||
.sort((a, b) => {
|
||||
return -a.id.localeCompare(b.id)
|
||||
})
|
||||
const supplyEventFileNameList = readdirSync(supplyEventsDirectoryPathname)
|
||||
const supplyEventList = supplyEventFileNameList
|
||||
.map((fileName) => {
|
||||
const filePathname = `${supplyEventsDirectoryPathname}/` + fileName
|
||||
const data = readJsonFileSync(filePathname)
|
||||
|
||||
const supplyEventMap = supplyEventFileList.reduce((map, stigmata) => {
|
||||
map.set(stigmata.id, stigmata)
|
||||
return map
|
||||
}, new Map<string, SupplyEvent>())
|
||||
return { ...data, id: fileName.replace(/\.json/, '') } as SupplyEventData
|
||||
})
|
||||
.sort((a, b) => {
|
||||
return -a.id.localeCompare(b.id)
|
||||
})
|
||||
|
||||
const versionSupplyEventListMap = supplyEventFileList.reduce(
|
||||
(map, supplyEvent) => {
|
||||
let supplyEvents = map.get(supplyEvent.version)
|
||||
if (supplyEvents == null) {
|
||||
supplyEvents = []
|
||||
map.set(supplyEvent.version, supplyEvents)
|
||||
}
|
||||
|
||||
supplyEvents.push(supplyEvent)
|
||||
|
||||
return map
|
||||
},
|
||||
new Map<number, SupplyEvent[]>()
|
||||
)
|
||||
|
||||
export function listSupplyEvents() {
|
||||
return supplyEventFileList
|
||||
}
|
||||
|
||||
export function getSupplyEventById(id: string) {
|
||||
return supplyEventMap.get(id)
|
||||
}
|
||||
|
||||
export function getSupplyEventListByVersion(version: number) {
|
||||
return versionSupplyEventListMap.get(version) || []
|
||||
return supplyEventList
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { format } from 'date-fns'
|
||||
import { readdirSync, readFileSync, readJsonFileSync } from '../../lib/data'
|
||||
import { compareVersion } from '../../lib/string'
|
||||
|
||||
export interface VersionData {
|
||||
version: string
|
||||
name: string
|
||||
duration: [string, string]
|
||||
verified: boolean
|
||||
newBattlesuits: string[]
|
||||
newWeapons: string[]
|
||||
description: string
|
||||
}
|
||||
|
||||
const versionDirectoryList = readdirSync('versions')
|
||||
|
||||
const versionDataList = versionDirectoryList
|
||||
.map((directoryName) => {
|
||||
const directoryPathname = 'versions/' + directoryName
|
||||
const data: VersionData = {
|
||||
version: directoryName,
|
||||
...readJsonFileSync(directoryPathname + '/version-data.json'),
|
||||
description: readFileSync(
|
||||
directoryPathname + '/description.md'
|
||||
).toString(),
|
||||
}
|
||||
|
||||
return data
|
||||
})
|
||||
.sort((a, b) => {
|
||||
return compareVersion(b.version, a.version)
|
||||
})
|
||||
|
||||
export function listVersionData() {
|
||||
return versionDataList
|
||||
}
|
||||
|
||||
export function getVersion(version: number | string) {
|
||||
return versionDataList.find((versionData) => {
|
||||
return versionData.version.toString() === version.toString()
|
||||
})
|
||||
}
|
||||
|
||||
export function getCurrentVersion() {
|
||||
const todayDateString = format(new Date(), 'yyyy-MM-dd')
|
||||
|
||||
return versionDataList.find((versionData) => {
|
||||
const [startDateString] = versionData.duration
|
||||
|
||||
return startDateString.localeCompare(todayDateString) < 0
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
export function isVersionString(version: string): boolean {
|
||||
return /[0-9]+\.[0-9]+(\.[0-9]+)?/.test(version)
|
||||
}
|
||||
|
||||
export function compareVersion(aVersion: string, bVersion: string): number {
|
||||
const [aMajor, aMinor = '0', aPatch = '0'] = aVersion.split('.')
|
||||
const [bMajor, bMinor = '0', bPatch = '0'] = bVersion.split('.')
|
||||
|
||||
let result = parseInt(aMajor, 10) - parseInt(bMajor, 10)
|
||||
|
||||
if (result !== 0) {
|
||||
return result
|
||||
}
|
||||
|
||||
result = parseInt(aMinor, 10) - parseInt(bMinor, 10)
|
||||
|
||||
if (result !== 0) {
|
||||
return result
|
||||
}
|
||||
|
||||
return parseInt(aPatch, 10) - parseInt(bPatch, 10)
|
||||
}
|
||||
@@ -1,420 +0,0 @@
|
||||
/** @jsxImportSource theme-ui */
|
||||
import { Text, Box, Heading, Link, Flex } from '@theme-ui/components'
|
||||
import NextLink from 'next/link'
|
||||
import Breadcrumb from '../../../components/organisms/Breadcrumb'
|
||||
import Honkai3rdNavigator from '../../../components/organisms/Honkai3rdNavigator'
|
||||
import { pick, times } from 'ramda'
|
||||
import {
|
||||
listSupplyEvents,
|
||||
SupplyEvent,
|
||||
} from '../../../data/honkai3rd/supply-events'
|
||||
import { getStigmataById, StigmataData } from '../../../data/honkai3rd/stigmata'
|
||||
import { getWeaponById, WeaponData } from '../../../data/honkai3rd/weapons'
|
||||
import {
|
||||
BattlesuitData,
|
||||
getBattlesuitById,
|
||||
} from '../../../data/honkai3rd/battlesuits'
|
||||
import { ElfData, getElfById } from '../../../data/honkai3rd/elfs'
|
||||
import {
|
||||
format as formatDate,
|
||||
differenceInCalendarDays,
|
||||
differenceInCalendarWeeks,
|
||||
format,
|
||||
} from 'date-fns'
|
||||
import SquareImageBox from '../../../components/atoms/SquareImageBox'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
type ObjectMap<T> = { [key: string]: T }
|
||||
type StigmataObjectMap = ObjectMap<Pick<StigmataData, 'id' | 'name'>>
|
||||
type WeaponObjectMap = ObjectMap<Pick<WeaponData, 'id' | 'name'>>
|
||||
type BattlesuitObjectMap = ObjectMap<Pick<BattlesuitData, 'id' | 'name'>>
|
||||
type ElfObjectMap = ObjectMap<Pick<ElfData, 'id' | 'name'>>
|
||||
|
||||
interface SupplyEventListPageProps {
|
||||
supplyEventList: Pick<
|
||||
SupplyEvent,
|
||||
'id' | 'version' | 'name' | 'featured' | 'duration' | 'track' | 'verified'
|
||||
>[]
|
||||
stigmataObjectMap: StigmataObjectMap
|
||||
weaponObjectMap: WeaponObjectMap
|
||||
battlesuitObjectMap: BattlesuitObjectMap
|
||||
elfObjectMap: ElfObjectMap
|
||||
}
|
||||
|
||||
const SupplyEventListPage = ({
|
||||
supplyEventList,
|
||||
battlesuitObjectMap,
|
||||
weaponObjectMap,
|
||||
stigmataObjectMap,
|
||||
elfObjectMap,
|
||||
}: SupplyEventListPageProps) => {
|
||||
const startDate = new Date('2021-12-02')
|
||||
const endDate = new Date('2022-01-13')
|
||||
const todayDate = new Date(formatDate(new Date(), 'yyyy-MM-dd'))
|
||||
|
||||
const totalDays = differenceInCalendarDays(endDate, startDate)
|
||||
const totalWeeks = differenceInCalendarWeeks(endDate, startDate)
|
||||
|
||||
const totalRow = useMemo(() => {
|
||||
return supplyEventList.reduce((max, supplyEvent) => {
|
||||
return supplyEvent.track > max ? supplyEvent.track : max
|
||||
}, 1)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Honkai3rdNavigator />
|
||||
|
||||
<Box p={3}>
|
||||
<Breadcrumb
|
||||
items={[
|
||||
{ href: '/honkai3rd', label: 'Honkai 3rd' },
|
||||
{ href: '/honkai3rd/supply-events', label: 'Supply Events' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<Heading as='h1' mb={3}>
|
||||
Supply Events
|
||||
</Heading>
|
||||
|
||||
<Box mb={3} sx={{ width: totalWeeks * 280 }}>
|
||||
<Flex
|
||||
sx={{
|
||||
marginLeft: -20,
|
||||
}}
|
||||
>
|
||||
{times((index) => {
|
||||
const weekNumber = index + 1
|
||||
return (
|
||||
<Box
|
||||
key={`week-${weekNumber}`}
|
||||
sx={{
|
||||
width: 280,
|
||||
textAlign: 'center',
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
Week {weekNumber}
|
||||
</Box>
|
||||
)
|
||||
}, totalWeeks)}
|
||||
</Flex>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
height: totalRow * 50 + 40,
|
||||
borderBottomStyle: 'solid',
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: 'gray.5',
|
||||
borderTopStyle: 'solid',
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: 'gray.5',
|
||||
}}
|
||||
>
|
||||
{times((index) => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
height: totalRow * 50 + 40,
|
||||
borderRightStyle: 'solid',
|
||||
borderRightWidth: 1,
|
||||
borderRightColor: index % 7 === 6 ? 'gray.5' : 'gray.3',
|
||||
left: (index + 1) * 40 - 1 - 20,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}, totalDays)}
|
||||
<Box
|
||||
sx={{
|
||||
boxSizing: 'border-box',
|
||||
position: 'absolute',
|
||||
width: 40,
|
||||
height: totalRow * 50 + 40,
|
||||
backgroundColor: 'blue.5',
|
||||
borderStyle: 'solid',
|
||||
borderColor: 'blue.7',
|
||||
opacity: 0.2,
|
||||
zIndex: 20,
|
||||
borderRadius: 4,
|
||||
left: differenceInCalendarDays(todayDate, startDate) * 40 - 20,
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
/>
|
||||
|
||||
<Text
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
color: 'blue.7',
|
||||
transform: 'rotate(90deg)',
|
||||
zIndex: 21,
|
||||
top: 150,
|
||||
left:
|
||||
differenceInCalendarDays(new Date(todayDate), startDate) *
|
||||
40 -
|
||||
150,
|
||||
width: 300,
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
>
|
||||
Today ({format(todayDate, 'P')})
|
||||
</Text>
|
||||
|
||||
{supplyEventList
|
||||
.slice()
|
||||
.reverse()
|
||||
.map((supplyEvent) => {
|
||||
const offset =
|
||||
differenceInCalendarDays(
|
||||
new Date(supplyEvent.duration[0]),
|
||||
startDate
|
||||
) * 40
|
||||
const length =
|
||||
differenceInCalendarDays(
|
||||
new Date(supplyEvent.duration[1]),
|
||||
new Date(supplyEvent.duration[0])
|
||||
) * 40
|
||||
|
||||
const featuredIconProps = getIconPropsOfItem(
|
||||
supplyEvent.featured[0],
|
||||
{
|
||||
battlesuitObjectMap,
|
||||
weaponObjectMap,
|
||||
stigmataObjectMap,
|
||||
elfObjectMap,
|
||||
}
|
||||
)
|
||||
return (
|
||||
<Flex
|
||||
key={supplyEvent.id}
|
||||
py={2}
|
||||
px={3}
|
||||
sx={{
|
||||
boxSizing: 'border-box',
|
||||
position: 'absolute',
|
||||
borderColor: 'gray.5',
|
||||
borderWidth: 1,
|
||||
borderStyle: 'solid',
|
||||
backgroundColor: 'white',
|
||||
width: length,
|
||||
left: offset,
|
||||
top: (supplyEvent.track - 1) * 50 + 20,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
height: 40,
|
||||
borderRadius: 40,
|
||||
fontWeight: 700,
|
||||
zIndex: 10,
|
||||
'&:hover': {
|
||||
minWidth: length,
|
||||
width: 'inherit',
|
||||
zIndex: 25,
|
||||
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)',
|
||||
transition: 'box-shadow 200ms ease-in-out',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{featuredIconProps != null && (
|
||||
<SquareImageBox
|
||||
size={20}
|
||||
src={featuredIconProps.src}
|
||||
alt={featuredIconProps.alt}
|
||||
mr={2}
|
||||
/>
|
||||
)}
|
||||
<Text
|
||||
sx={{
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{supplyEvent.name}
|
||||
{!supplyEvent.verified && ' (❓)'}
|
||||
</Text>
|
||||
</Flex>
|
||||
)
|
||||
})}
|
||||
</Box>
|
||||
</Box>
|
||||
<Box>
|
||||
{supplyEventList.map((supplyEvent) => {
|
||||
return (
|
||||
<Box key={supplyEvent.id}>
|
||||
<Box mb={3}>
|
||||
<Heading>
|
||||
<NextLink
|
||||
href={`/honkai3rd/supply-events/${supplyEvent.id}`}
|
||||
key={supplyEvent.id}
|
||||
passHref={true}
|
||||
>
|
||||
<Link>
|
||||
[{supplyEvent.version}] {supplyEvent.name} (
|
||||
{supplyEvent.duration.join(' ~ ')})
|
||||
</Link>
|
||||
</NextLink>
|
||||
</Heading>
|
||||
|
||||
<Flex>
|
||||
{supplyEvent.featured.map((item) => {
|
||||
const props = getIconPropsOfItem(item, {
|
||||
battlesuitObjectMap,
|
||||
weaponObjectMap,
|
||||
stigmataObjectMap,
|
||||
elfObjectMap,
|
||||
})
|
||||
if (props == null) {
|
||||
return (
|
||||
<Text>
|
||||
{item.type} : {item.id}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<SquareImageBox
|
||||
key={`${item.type}/${item.id}`}
|
||||
mr={2}
|
||||
size={50}
|
||||
src={props.src}
|
||||
alt={props.alt}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</Flex>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
})}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default SupplyEventListPage
|
||||
|
||||
export async function getStaticProps() {
|
||||
const supplyEventList = listSupplyEvents()
|
||||
|
||||
const stigmataObjectMap: {
|
||||
[key: string]: Pick<StigmataData, 'id' | 'name'>
|
||||
} = {}
|
||||
const weaponObjectMap: {
|
||||
[key: string]: Pick<WeaponData, 'id' | 'name'>
|
||||
} = {}
|
||||
const battlesuitObjectMap: {
|
||||
[key: string]: Pick<BattlesuitData, 'id' | 'name'>
|
||||
} = {}
|
||||
const elfObjectMap: {
|
||||
[key: string]: Pick<ElfData, 'id' | 'name'>
|
||||
} = {}
|
||||
|
||||
supplyEventList.forEach((supplyEvent) => {
|
||||
supplyEvent.featured.forEach((item) => {
|
||||
switch (item.type) {
|
||||
case 'battlesuit':
|
||||
const battlesuit = getBattlesuitById(item.id)
|
||||
if (battlesuit != null) {
|
||||
battlesuitObjectMap[battlesuit.id] = pick(
|
||||
['id', 'name'],
|
||||
battlesuit
|
||||
)
|
||||
}
|
||||
break
|
||||
case 'weapon':
|
||||
const weapon = getWeaponById(item.id)
|
||||
if (weapon != null) {
|
||||
weaponObjectMap[weapon.id] = pick(['id', 'name'], weapon)
|
||||
}
|
||||
break
|
||||
case 'stigmata':
|
||||
const stigmata = getStigmataById(item.id)
|
||||
if (stigmata != null) {
|
||||
stigmataObjectMap[stigmata.id] = pick(['id', 'name'], stigmata)
|
||||
}
|
||||
break
|
||||
case 'elf':
|
||||
const elf = getElfById(item.id)
|
||||
if (elf != null) {
|
||||
elfObjectMap[elf.id] = pick(['id', 'name'], elf)
|
||||
}
|
||||
break
|
||||
}
|
||||
})
|
||||
})
|
||||
return {
|
||||
props: {
|
||||
supplyEventList: supplyEventList.map((supplyEvent) => {
|
||||
return pick(
|
||||
[
|
||||
'id',
|
||||
'version',
|
||||
'name',
|
||||
'featured',
|
||||
'duration',
|
||||
'track',
|
||||
'verified',
|
||||
],
|
||||
supplyEvent
|
||||
)
|
||||
}),
|
||||
battlesuitObjectMap,
|
||||
weaponObjectMap,
|
||||
stigmataObjectMap,
|
||||
elfObjectMap,
|
||||
},
|
||||
revalidate: true,
|
||||
}
|
||||
}
|
||||
|
||||
function getIconPropsOfItem(
|
||||
item: { type: string; id: string },
|
||||
{
|
||||
battlesuitObjectMap,
|
||||
weaponObjectMap,
|
||||
stigmataObjectMap,
|
||||
elfObjectMap,
|
||||
}: {
|
||||
stigmataObjectMap: StigmataObjectMap
|
||||
weaponObjectMap: WeaponObjectMap
|
||||
battlesuitObjectMap: BattlesuitObjectMap
|
||||
elfObjectMap: ElfObjectMap
|
||||
}
|
||||
): { src: string; alt: string } | null {
|
||||
switch (item.type) {
|
||||
case 'battlesuit':
|
||||
const battlesuit = battlesuitObjectMap[item.id]
|
||||
if (battlesuit != null) {
|
||||
return {
|
||||
src: `/assets/honkai3rd/battlesuits/portrait-${battlesuit.id}.png`,
|
||||
alt: battlesuit.name,
|
||||
}
|
||||
}
|
||||
case 'weapon':
|
||||
const weapon = weaponObjectMap[item.id]
|
||||
if (weapon != null) {
|
||||
return {
|
||||
src: `/assets/honkai3rd/weapons/${weapon.id}.png`,
|
||||
alt: weapon.name,
|
||||
}
|
||||
}
|
||||
case 'stigmata':
|
||||
const stigmata = stigmataObjectMap[item.id]
|
||||
if (stigmata != null) {
|
||||
return {
|
||||
src: `/assets/honkai3rd/stigmata/icon-${stigmata.id}.png`,
|
||||
alt: stigmata.name,
|
||||
}
|
||||
}
|
||||
case 'elf':
|
||||
const elf = elfObjectMap[item.id]
|
||||
if (elf != null) {
|
||||
return {
|
||||
src: `/assets/honkai3rd/elfs/icon-${elf.id}.png`,
|
||||
alt: elf.name,
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/** @jsxImportSource theme-ui */
|
||||
import { NextPageContext } from 'next'
|
||||
import {
|
||||
getVersion,
|
||||
listVersionData,
|
||||
VersionData,
|
||||
} from '../../../data/honkai3rd/versions'
|
||||
|
||||
interface VersionShowPageProps {
|
||||
versionData: VersionData
|
||||
}
|
||||
|
||||
const VersionShowPage = ({ versionData }: VersionShowPageProps) => {
|
||||
return (
|
||||
<div>
|
||||
{versionData.version} : {versionData.name}
|
||||
<pre>
|
||||
<code>{JSON.stringify(versionData, null, 2)}</code>
|
||||
</pre>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default VersionShowPage
|
||||
|
||||
export async function getStaticProps({
|
||||
params,
|
||||
}: NextPageContext & { params: { versionId: string } }) {
|
||||
return {
|
||||
props: {
|
||||
versionData: getVersion(params.versionId),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return {
|
||||
paths: listVersionData().map((versionData) => {
|
||||
return {
|
||||
params: {
|
||||
versionId: versionData.version.toString(),
|
||||
},
|
||||
}
|
||||
}),
|
||||
fallback: false,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
/** @jsxImportSource theme-ui */
|
||||
import { Box, Flex, Heading, Link, Text } from '@theme-ui/components'
|
||||
import { format } from 'date-fns'
|
||||
import NextLink from 'next/link'
|
||||
import SquareImageBox from '../../../components/atoms/SquareImageBox'
|
||||
import Breadcrumb from '../../../components/organisms/Breadcrumb'
|
||||
import GanttChart from '../../../components/organisms/GanttChart'
|
||||
import Honkai3rdNavigator from '../../../components/organisms/Honkai3rdNavigator'
|
||||
import {
|
||||
BattlesuitData,
|
||||
getBattlesuitById,
|
||||
} from '../../../data/honkai3rd/battlesuits'
|
||||
import {
|
||||
listSupplyEventsByVersion,
|
||||
SupplyEventData as SupplyEventData,
|
||||
} from '../../../data/honkai3rd/supply-events'
|
||||
import {
|
||||
getCurrentVersion,
|
||||
listVersionData,
|
||||
VersionData,
|
||||
} from '../../../data/honkai3rd/versions'
|
||||
import { getWeaponById, WeaponData } from '../../../data/honkai3rd/weapons'
|
||||
|
||||
interface VersionIndexPageProps {
|
||||
versionDataList: VersionData[]
|
||||
currentVersionData: VersionData
|
||||
currentVersionNewBattlesuits: BattlesuitData[]
|
||||
currentVersionNewWeapons: WeaponData[]
|
||||
currentVersionSupplyEvents: SupplyEventData[]
|
||||
}
|
||||
|
||||
const VersionIndexPage = ({
|
||||
currentVersionData,
|
||||
currentVersionNewBattlesuits,
|
||||
versionDataList,
|
||||
currentVersionNewWeapons,
|
||||
currentVersionSupplyEvents,
|
||||
}: VersionIndexPageProps) => {
|
||||
return (
|
||||
<Box>
|
||||
<Honkai3rdNavigator />
|
||||
|
||||
<Box p={3}>
|
||||
<Breadcrumb
|
||||
items={[
|
||||
{ href: '/honkai3rd', label: 'Honkai 3rd' },
|
||||
{ href: '/honkai3rd/versions', label: 'Versions' },
|
||||
]}
|
||||
/>
|
||||
<Box mb={3}>
|
||||
<Heading as='h2' mb={4}>
|
||||
v{currentVersionData.version} : {currentVersionData.name}
|
||||
<br />
|
||||
<small>(Current Version)</small>
|
||||
</Heading>
|
||||
|
||||
<Box>
|
||||
<Heading as='h3'>New Battlesuits</Heading>
|
||||
<Box mb={4}>
|
||||
{currentVersionNewBattlesuits.map((battlesuit) => {
|
||||
return (
|
||||
<NextLink
|
||||
href={`/honkai3rd/battlesuits/${battlesuit.id}`}
|
||||
passHref
|
||||
>
|
||||
<Link>
|
||||
<Flex sx={{ alignItems: 'center' }} mb={2}>
|
||||
<SquareImageBox
|
||||
size={40}
|
||||
src={`/assets/honkai3rd/battlesuits/portrait-${battlesuit.id}.png`}
|
||||
alt={`${battlesuit.name}`}
|
||||
mr={2}
|
||||
/>
|
||||
<Text>{battlesuit.name}</Text>
|
||||
</Flex>
|
||||
</Link>
|
||||
</NextLink>
|
||||
)
|
||||
})}
|
||||
</Box>
|
||||
|
||||
<Heading as='h3'>New Weapons</Heading>
|
||||
<Box mb={4}>
|
||||
{currentVersionNewWeapons.map((weapon) => {
|
||||
return (
|
||||
<NextLink href={`/honkai3rd/weapons/${weapon.id}`} passHref>
|
||||
<Link>
|
||||
<Flex sx={{ alignItems: 'center' }} mb={2}>
|
||||
<SquareImageBox
|
||||
size={40}
|
||||
src={`/assets/honkai3rd/weapons/${weapon.id}.png`}
|
||||
alt={`${weapon.name}`}
|
||||
mr={2}
|
||||
/>
|
||||
<Text>{weapon.name}</Text>
|
||||
</Flex>
|
||||
</Link>
|
||||
</NextLink>
|
||||
)
|
||||
})}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Heading as='h3'>Supply Events</Heading>
|
||||
<Box mb={4}>
|
||||
<GanttChart
|
||||
items={currentVersionSupplyEvents.map((supplyEventData) => {
|
||||
const imgSrc = getIconSrcFromItem(supplyEventData.featured[0])
|
||||
return {
|
||||
id: supplyEventData.id,
|
||||
label: (
|
||||
<Flex sx={{ alignItems: 'center' }}>
|
||||
{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={format(new Date(), 'yyyy-MM-dd')}
|
||||
startDate={currentVersionData.duration[0]}
|
||||
endDate={currentVersionData.duration[1]}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<NextLink
|
||||
href={`/honkai3rd/versions/${currentVersionData.version}`}
|
||||
passHref
|
||||
>
|
||||
<Link>Learn more...</Link>
|
||||
</NextLink>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Heading as='h2'>All Versions</Heading>
|
||||
<Box>
|
||||
{versionDataList.map((versionData) => {
|
||||
return (
|
||||
<Box key={versionData.version}>
|
||||
<Heading as='h3'>
|
||||
<NextLink
|
||||
href={`/honkai3rd/versions/${versionData.version}`}
|
||||
passHref
|
||||
>
|
||||
<Link>
|
||||
{versionData.version} : {versionData.name} (
|
||||
{format(new Date(versionData.duration[0]), 'yyyy/MM/dd')}-
|
||||
{versionData.duration[1] != null
|
||||
? format(
|
||||
new Date(versionData.duration[1]),
|
||||
'yyyy/MM/dd'
|
||||
)
|
||||
: ''}
|
||||
)
|
||||
</Link>
|
||||
</NextLink>
|
||||
</Heading>
|
||||
</Box>
|
||||
)
|
||||
})}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
const currentVersionData = getCurrentVersion()!
|
||||
|
||||
const currentVersionNewBattlesuits = currentVersionData.newBattlesuits.map(
|
||||
(battlesuitId) => {
|
||||
return getBattlesuitById(battlesuitId)
|
||||
}
|
||||
)
|
||||
const currentVersionNewWeapons = currentVersionData.newWeapons.map(
|
||||
(weaponId) => {
|
||||
return getWeaponById(weaponId)
|
||||
}
|
||||
)
|
||||
|
||||
const currentVersionSupplyEvents = listSupplyEventsByVersion(
|
||||
currentVersionData.version
|
||||
)
|
||||
|
||||
return {
|
||||
props: {
|
||||
currentVersionData,
|
||||
currentVersionNewBattlesuits,
|
||||
currentVersionNewWeapons,
|
||||
currentVersionSupplyEvents,
|
||||
versionDataList: listVersionData(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default VersionIndexPage
|
||||
|
||||
function getIconSrcFromItem(item: { type: string; id: string }): string | null {
|
||||
switch (item.type) {
|
||||
case 'battlesuit':
|
||||
return `/assets/honkai3rd/battlesuits/portrait-${item.id}.png`
|
||||
case 'weapon':
|
||||
return `/assets/honkai3rd/weapons/${item.id}.png`
|
||||
case 'stigmata':
|
||||
return `/assets/honkai3rd/stigmata/icon-${item.id}.png`
|
||||
case 'elf':
|
||||
return `/assets/honkai3rd/elfs/icon-${item.id}.png`
|
||||
}
|
||||
return null
|
||||
}
|
||||
Reference in New Issue
Block a user