Skip to content

Commit 98127ec

Browse files
committed
Add detailed banners page
1 parent 782719a commit 98127ec

File tree

11 files changed

+185
-6
lines changed

11 files changed

+185
-6
lines changed

pages/events/banners.tsx

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/* eslint-disable @next/next/no-img-element */
2+
import { GetStaticPropsContext, GetStaticPropsResult } from "next"
3+
import Head from "next/head"
4+
import { useState } from "react"
5+
import { ExclusiveButton } from "../../components/Filters"
6+
import FormattedLink from "../../components/FormattedLink"
7+
import { SmallIcon } from "../../components/Icon"
8+
import Main from "../../components/Main"
9+
import { config } from "../../utils/config"
10+
import { getCharacters, getWeapons } from "../../utils/data-cache"
11+
import { SmallChar, SmallWeapon, Wish } from "../../utils/types"
12+
import { createSmallChar, createSmallWeapon } from "../../utils/utils"
13+
import styles from "../style.module.css"
14+
15+
16+
interface Props {
17+
banners: (Wish & {index: number})[]
18+
weapons: SmallWeapon[]
19+
chars: SmallChar[]
20+
}
21+
22+
export default function Banners(props: Props & { location: string }) {
23+
const desc = "View ongoing and future events of Genshin Impact."
24+
const [filter, setFilter] = useState(false)
25+
26+
const [bannerType, setBannerType] = useState("both" as "both" | "char" | "weapons")
27+
28+
29+
return (
30+
<Main>
31+
<Head>
32+
<title>Banners | Hu Tao</title>
33+
<meta name="twitter:card" content="summary" />
34+
<meta property="og:title" content="Banners | Hu Tao" />
35+
<meta property="og:description" content={desc} />
36+
<meta name="description" content={desc} />
37+
</Head>
38+
39+
<h2 className="font-semibold">
40+
<FormattedLink href={"/events"} location={props.location} className="font-semibold text-lg">
41+
All Events
42+
</FormattedLink>
43+
</h2>
44+
<h1 className="text-5xl font-bold pb-2">
45+
Banners
46+
</h1>
47+
48+
{filter ? <div className="bg-slate-100 dark:bg-slate-600 flex flex-col p-2 rounded-2xl font-semibold gap-2">
49+
<div className="pb-2">
50+
<div className="flex flex-row font-semibold float-right">
51+
<ExclusiveButton type={filter} value={false} setter={setFilter}>
52+
Hide filters
53+
</ExclusiveButton>
54+
</div>
55+
<div>
56+
Banner type filter
57+
</div>
58+
<div className="flex flex-row flex-wrap gap-2 pt-2">
59+
<ExclusiveButton type={bannerType} value={"both"} setter={setBannerType}>
60+
All banners
61+
</ExclusiveButton>
62+
<ExclusiveButton type={bannerType} value={"char"} setter={setBannerType}>
63+
Character banners only
64+
</ExclusiveButton>
65+
<ExclusiveButton type={bannerType} value={"weapons"} setter={setBannerType}>
66+
Weapon banners only
67+
</ExclusiveButton>
68+
</div>
69+
</div>
70+
</div> : <div className="flex flex-row font-semibold justify-end">
71+
<ExclusiveButton type={filter} value={true} setter={setFilter}>
72+
Show filters
73+
</ExclusiveButton>
74+
</div>
75+
}
76+
77+
<table className={`table-auto w-full ${styles.table} my-2 sm:text-sm md:text-base text-xs`}>
78+
<thead>
79+
<tr className="divide-x divide-gray-200 dark:divide-gray-500">
80+
<th>Title</th>
81+
<th>Main</th>
82+
<th>Other</th>
83+
<th>Start</th>
84+
</tr>
85+
</thead>
86+
<tbody className="divide-y divide-gray-200 dark:divide-gray-500">
87+
{props.banners
88+
.filter(w => bannerType == "both" || (bannerType == "weapons" && w.title.includes("Epitome Invocation")) || (bannerType == "char" && !w.title.includes("Epitome Invocation")))
89+
.map((event, i) => <WishLine key={"ongoing"+i} w={event} chars={props.chars} weapons={props.weapons} location={props.location} />)}
90+
</tbody>
91+
</table>
92+
</Main>
93+
)
94+
}
95+
96+
function WishLine({ w, chars, weapons, location }: { w: Wish, chars: SmallChar[], weapons: SmallWeapon[], location: string }) {
97+
const durations = w.duration.match(/(.*?)(|| - )(.*)/)
98+
const duration = durations ? [durations[1], durations[3]] : [w.duration]
99+
100+
const img = w.img ?? "img/Event_No_Img.png"
101+
102+
return <tr className="pr-1 divide-x divide-gray-200 dark:divide-gray-500">
103+
<td className={"w-56 text-center pb-2"}>
104+
<span className="font-semibold">{w.title.match(/".*?"/)?.[0]?.replace(/"/g, "") ?? w.title}</span>
105+
<img loading="lazy" className="rounded-lg" src={img.startsWith("img/") ? `/${img}` : img} width={1080} height={533} alt={w.title} />
106+
</td>
107+
<td>
108+
{w.main.map(o => <Link key={o} target={o} chars={chars} weapons={weapons} location={location}/>)}
109+
</td>
110+
<td>
111+
{w.other.map(o => <Link key={o} target={o} chars={chars} weapons={weapons} location={location}/>)}
112+
</td>
113+
<td>{duration.map(o => <div key={o}>{o}</div>)}</td>
114+
</tr>
115+
}
116+
117+
function Link({ target, chars, weapons, location }: { target: string, chars: SmallChar[], weapons: SmallWeapon[], location: string }) {
118+
const char = chars.find(x => x.name == target)
119+
if (char)
120+
return <div className="flex flex-wrap justify-start text-center items-center gap-1">
121+
<SmallIcon sizeSet="s" thing={char} location={location}>{target}</SmallIcon>
122+
</div>
123+
const weapon = weapons.find(x => x.name == target)
124+
if (weapon)
125+
return <div className="flex flex-wrap justify-start text-center items-center gap-1">
126+
<SmallIcon sizeSet="s" thing={weapon} location={location}>{target}</SmallIcon>
127+
</div>
128+
129+
return <div>{target}</div>
130+
}
131+
132+
export async function getStaticProps(context: GetStaticPropsContext): Promise<GetStaticPropsResult<Props>> {
133+
const banners: Wish[] = await fetch(`${config.discordUri}/banners`, {
134+
headers: { Authorization: `${config.discordSecret}` },
135+
}).then((res) => res.json())
136+
137+
const weapons = await getWeapons()
138+
const chars = await getCharacters()
139+
140+
141+
if (!banners || !weapons || !chars) {
142+
return {
143+
notFound: true,
144+
revalidate: 15 * 60
145+
}
146+
}
147+
148+
return {
149+
props: {
150+
banners: banners
151+
.map((e, i) => Object.assign({ index: i }, e))
152+
.sort((a, b) => {
153+
if (Math.abs(a.roughDate - b.roughDate) < 3600) {
154+
if (a.duration == b.duration)
155+
return a.title.includes("Epitome") ? 1 : b.title.includes("Epitome") ? -1 : 0
156+
157+
return b.duration.localeCompare(a.duration)
158+
} else
159+
return b.roughDate - a.roughDate
160+
}),
161+
weapons: Object.values(weapons).map(x => createSmallWeapon(x)),
162+
chars: Object.values(chars).map(x => createSmallChar(x)),
163+
},
164+
revalidate: 60 * 30
165+
}
166+
}
Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
import { GetStaticPropsContext, GetStaticPropsResult } from "next"
33
import Head from "next/head"
44
import { useEffect, useState } from "react"
5-
import FormattedLink from "../components/FormattedLink"
6-
import Main from "../components/Main"
7-
import { getEvents } from "../utils/data-cache"
8-
import { Event as GenshinEvent, EventType } from "../utils/types"
9-
import { formatTime, getDate, timeLeft } from "../utils/utils"
10-
import styles from "./style.module.css"
5+
import FormattedLink from "../../components/FormattedLink"
6+
import Main from "../../components/Main"
7+
import { getEvents } from "../../utils/data-cache"
8+
import { Event as GenshinEvent, EventType } from "../../utils/types"
9+
import { formatTime, getDate, timeLeft } from "../../utils/utils"
10+
import styles from "../style.module.css"
1111

1212

1313
interface Props {
@@ -130,6 +130,11 @@ export default function Events(props: Props & { location: string }) {
130130
<h1 className="text-5xl font-bold pb-2">
131131
Events
132132
</h1>
133+
<h2 className="font-semibold">
134+
<FormattedLink href={"/events/banners"} location={props.location} className="font-semibold text-lg">
135+
Detailed banners view
136+
</FormattedLink>
137+
</h2>
133138
<div>
134139
Select server:
135140
<div>

public/img/banners/1.0/char1.png

907 KB
Loading

public/img/banners/1.0/char2.png

856 KB
Loading

public/img/banners/1.0/weapon1.png

695 KB
Loading

public/img/banners/1.0/weapon2.png

706 KB
Loading

public/img/banners/1.1/char2.png

953 KB
Loading

public/img/banners/1.1/weapon2.png

718 KB
Loading

public/img/banners/1.3/char1.png

1020 KB
Loading

public/img/banners/1.3/weapon1.png

689 KB
Loading

0 commit comments

Comments
 (0)