Move static page modals to a standalone page

This will make it easier to whisk people here in notices
This commit is contained in:
Justin Edmund 2023-01-25 14:33:53 -08:00
parent cefec18880
commit 2718942321
11 changed files with 793 additions and 0 deletions

View file

@ -0,0 +1,11 @@
.About.PageContent {
.Links {
display: grid;
gap: $unit;
margin: $unit-2x 0;
}
div.LinkItem {
margin-top: $unit-2x;
}
}

View file

@ -0,0 +1,165 @@
import React from 'react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'
import ShareIcon from '~public/icons/Share.svg'
import DiscordIcon from '~public/icons/discord.svg'
import GithubIcon from '~public/icons/github.svg'
import './index.scss'
interface Props {}
const AboutPage: React.FC<Props> = (props: Props) => {
const { t: common } = useTranslation('common')
return (
<div className="About PageContent">
<h1>{common('about.segmented_control.about')}</h1>
<section>
<p>
Granblue.team is a tool to save and share team comps for{' '}
<a
href="https://game.granbluefantasy.jp"
target="_blank"
rel="noreferrer"
>
Granblue Fantasy
</a>
.
</p>
<p>
Start adding to a team and a URL will be created for you to share
wherever you like, no account needed.
</p>
<p>
However, if you do make an account, you can save any teams you find
for future reference and keep all of your teams together in one place.
</p>
</section>
<section>
<h2>Feedback</h2>
<p>
This is an evolving project so feedback and suggestions are greatly
appreciated!
</p>
<p>
If you have a feature request, would like to report a bug, or are
enjoying the tool and want to say thanks, come hang out in Discord!
</p>
<div className="LinkItem">
<Link href="https://discord.gg/qyZ5hGdPC8">
<a
href="https://discord.gg/qyZ5hGdPC8"
target="_blank"
rel="noreferrer"
>
<div className="Left">
<DiscordIcon />
<h3>granblue-tools</h3>
</div>
<ShareIcon className="ShareIcon" />
</a>
</Link>
</div>
</section>
<section>
<h2>Credits</h2>
<p>
Granblue.team was built by{' '}
<a
href="https://twitter.com/jedmund"
target="_blank"
rel="noreferrer"
>
@jedmund
</a>{' '}
with a lot of help from{' '}
<a
href="https://twitter.com/lalalalinna"
target="_blank"
rel="noreferrer"
>
@lalalalinna
</a>{' '}
and{' '}
<a
href="https://twitter.com/tarngerine"
target="_blank"
rel="noreferrer"
>
@tarngerine
</a>
.
</p>
<p>
Many thanks also go to Disinfect, Slipper, Jif, Bless, 9highwind, and
everyone else in{' '}
<a
href="https://game.granbluefantasy.jp/#guild/detail/1190185"
target="_blank"
rel="noreferrer"
>
Fireplace
</a>{' '}
that helped with bug testing and feature requests. (P.S. We&apos;re
recruiting!) And yoey, but he won&apos;t join our crew.
</p>
</section>
<section>
<h2>Contributing</h2>
<p>
This app is open source and licensed under{' '}
<a
href="https://choosealicense.com/licenses/agpl-3.0/"
target="_blank"
rel="noreferrer"
>
GNU AGPLv3
</a>
. Plainly, that means you can download the source, modify it, and
redistribute it if you attribute this project, use the same license,
and keep it open source. You can contribute on Github.
</p>
<ul className="Links">
<li className="LinkItem">
<Link href="https://github.com/jedmund/hensei-api">
<a
href="https://github.com/jedmund/hensei-api"
target="_blank"
rel="noreferrer"
>
<div className="Left">
<GithubIcon />
<h3>jedmund/hensei-api</h3>
</div>
<ShareIcon className="ShareIcon" />
</a>
</Link>
</li>
<li className="LinkItem">
<Link href="https://github.com/jedmund/hensei-web">
<a
href="https://github.com/jedmund/hensei-web"
target="_blank"
rel="noreferrer"
>
<div className="Left">
<GithubIcon />
<h3>jedmund/hensei-web</h3>
</div>
<ShareIcon className="ShareIcon" />
</a>
</Link>
</li>
</ul>
</section>
</div>
)
}
export default AboutPage

View file

@ -0,0 +1,64 @@
.Changelog.PageContent {
.version {
&.content {
.top h3 {
color: var(--accent-yellow);
}
.update {
display: flex;
flex-direction: column;
gap: $unit-2x;
}
.characters,
.weapons,
.summons {
display: grid;
grid-template-rows: 1fr auto;
gap: $unit;
& > h4 {
font-weight: $medium;
font-size: $font-regular;
}
}
.items {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: $unit-4x;
}
}
.top {
align-items: baseline;
display: flex;
gap: $unit-half;
margin-bottom: $unit-2x;
h3 {
color: var(--accent-blue);
font-weight: $medium;
font-size: $font-large;
}
time {
color: var(--text-secondary);
font-size: $font-small;
font-weight: $medium;
}
}
}
.notes {
color: var(--text-primary);
list-style-type: disc;
list-style-position: inside;
li {
margin-bottom: $unit-half;
font-size: $font-regular;
}
}
}

View file

@ -0,0 +1,122 @@
import React from 'react'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'
import ChangelogUnit from '~components/ChangelogUnit'
import './index.scss'
interface Props {}
const ChangelogPage: React.FC<Props> = (props: Props) => {
const { t: common } = useTranslation('common')
return (
<div className="Changelog PageContent">
<h1>{common('about.segmented_control.changelog')}</h1>
<section className="version" data-version="1.0">
<div className="top">
<h3>1.0.1</h3>
<time>2023/01/08</time>
</div>
<ul className="notes">
<li>Extra party fields: Full Auto, Clear Time, and more</li>
<li>Support for Youtube short URLs</li>
<li>Responsive grids and lots of other mobile fixes</li>
<li>Many other bug fixes</li>
</ul>
</section>
<section className="content version" data-version="2022-12L">
<div className="top">
<h3>2022-12 Legend Festival</h3>
<time>2022/12/26</time>
</div>
<div className="update">
<section className="characters">
<h4>New characters</h4>
<div className="items">
<ChangelogUnit
name="Michael (Grand)"
id="3040440000"
type="character"
/>
<ChangelogUnit name="Makura" id="3040441000" type="character" />
<ChangelogUnit
name="Ultimate Friday"
id="3040442000"
type="character"
/>
</div>
</section>
<section className="weapons">
<h4>New weapons</h4>
<div className="items">
<ChangelogUnit
name="Crimson Scale"
id="1040315900"
type="weapon"
/>
<ChangelogUnit name="Leporidius" id="1040914500" type="weapon" />
<ChangelogUnit name="FRIED Spear" id="1040218200" type="weapon" />
</div>
</section>
<section className="summons">
<h4>New summons</h4>
<div className="items">
<ChangelogUnit name="Yatima" id="2040417000" type="summon" />
</div>
</section>
</div>
</section>
<section className="content version" data-version="2022-12F2">
<div className="top">
<h3>2022-12 Flash Gala</h3>
<time>2022/12/26</time>
</div>
<div className="update">
<section className="characters">
<h4>New characters</h4>
<div className="items">
<ChangelogUnit
name="Charlotta (Grand)"
id="3040438000"
type="character"
/>
<ChangelogUnit name="Erin" id="3040439000" type="character" />
</div>
</section>
<section className="weapons">
<h4>New weapons</h4>
<div className="items">
<ChangelogUnit
name="Claíomh Solais Díon"
id="1040024200"
type="weapon"
/>
<ChangelogUnit
name="Crystal Edge"
id="1040116500"
type="weapon"
/>
</div>
</section>
</div>
</section>
<section className="version" data-version="1.0">
<div className="top">
<h3>1.0.0</h3>
<time>2022/12/26</time>
</div>
<ul className="notes">
<li>First release!</li>
<li>You can embed Youtube videos now</li>
<li>Better clicking - right-click and open in a new tab</li>
<li>Manually set dark mode in Account Settings</li>
<li>Lots of bugs squashed</li>
</ul>
</section>
</div>
)
}
export default ChangelogPage

View file

@ -0,0 +1,108 @@
.Roadmap.PageContent {
h3.priority {
font-weight: $medium;
font-size: $font-large;
margin-bottom: $unit-4x;
&.in_progress {
color: $yellow;
}
&.high {
color: $red;
}
&.mid {
color: $orange-10;
}
&.low {
color: $blue;
}
}
.notes {
display: flex;
flex-direction: column;
gap: $unit;
margin-bottom: $unit-2x;
p {
margin-bottom: $unit;
}
.LinkItem {
$diameter: $unit-6x;
border: 1px solid var(--link-item-bg);
border-radius: $card-corner;
&:hover {
background-color: var(--link-item-bg);
svg {
fill: var(--link-item-image-color-hover);
}
}
a {
display: flex;
padding: $unit-2x;
&:hover {
text-decoration: none;
}
.Left {
align-items: center;
display: flex;
gap: $unit-2x;
flex-grow: 1;
}
svg {
fill: var(--link-item-image-color);
width: $diameter;
height: auto;
&.ShareIcon {
width: $unit-4x;
}
}
}
h3 {
font-weight: $bold;
max-width: 70%;
line-height: 1.3;
}
}
}
p {
color: var(--text-secondary);
font-size: $font-regular;
line-height: 1.3;
}
ul {
color: var(--text-primary);
list-style-type: none;
li {
display: flex;
flex-direction: column;
gap: $unit;
margin-bottom: $unit-2x;
h4 {
font-size: $font-medium;
font-weight: $bold;
}
p {
font-size: $font-regular;
}
}
}
}

View file

@ -0,0 +1,73 @@
import React from 'react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'
import ShareIcon from '~public/icons/Share.svg'
import GithubIcon from '~public/icons/github.svg'
import './index.scss'
interface Props {}
const RoadmapPage: React.FC<Props> = (props: Props) => {
const { t: common } = useTranslation('common')
const { t: roadmap } = useTranslation('roadmap')
return (
<div className="Roadmap PageContent">
<h1>{common('about.segmented_control.roadmap')}</h1>
<section className="notes">
<p>{roadmap('blurb')}</p>
<p>{roadmap('link.intro')}</p>
<div className="LinkItem">
<Link href="https://github.com/users/jedmund/projects/1/views/3">
<a
href="https://github.com/users/jedmund/projects/1/views/3"
target="_blank"
rel="noreferrer"
>
<div className="Left">
<GithubIcon />
<h3>{roadmap('link.title')}</h3>
</div>
<ShareIcon className="ShareIcon" />
</a>
</Link>
</div>
</section>
<section className="features">
<h3 className="priority in_progress">{roadmap('subtitle')}</h3>
<ul>
<li>
<h4>{roadmap('roadmap.item1.title')}</h4>
<p>{roadmap('roadmap.item1.description')}</p>
</li>
<li>
<h4>{roadmap('roadmap.item2.title')}</h4>
<p>{roadmap('roadmap.item2.description')}</p>
</li>
<li>
<h4>{roadmap('roadmap.item3.title')}</h4>
<p>{roadmap('roadmap.item3.description')}</p>
</li>
<li>
<h4>{roadmap('roadmap.item4.title')}</h4>
<p>{roadmap('roadmap.item4.description')}</p>
</li>
<li>
<h4>{roadmap('roadmap.item5.title')}</h4>
<p>{roadmap('roadmap.item5.description')}</p>
</li>
<li>
<h4>{roadmap('roadmap.item6.title')}</h4>
<p>{roadmap('roadmap.item6.description')}</p>
</li>
</ul>
</section>
</div>
)
}
export default RoadmapPage

150
pages/about.tsx Normal file
View file

@ -0,0 +1,150 @@
import React, { useCallback, useEffect, useState } from 'react'
import Head from 'next/head'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import api from '~utils/api'
import { printError } from '~utils/reportError'
import { AboutTabs } from '~utils/enums'
import setUserToken from '~utils/setUserToken'
import AboutPage from '~components/AboutPage'
import ChangelogPage from '~components/ChangelogPage'
import RoadmapPage from '~components/RoadmapPage'
import SegmentedControl from '~components/SegmentedControl'
import Segment from '~components/Segment'
import type { NextApiRequest, NextApiResponse } from 'next'
interface Props {}
const AboutRoute: React.FC<Props> = (props: Props) => {
// Set up router
const router = useRouter()
// Import translations
const { t } = useTranslation('common')
const [currentTab, setCurrentTab] = useState<AboutTabs>(AboutTabs.About)
function handleTabClicked(event: React.ChangeEvent<HTMLInputElement>) {
const path = [
router.asPath.split('/').filter((el) => el != '')[1],
event.target.value,
].join('/')
// TODO: Decide if we want /about/changlog or /changelog
// TODO: Then, set up Next.js rewrites
switch (event.target.value) {
case 'about':
// router.replace(path)
setCurrentTab(AboutTabs.About)
break
case 'changelog':
// router.replace(path)
setCurrentTab(AboutTabs.Changelog)
break
case 'roadmap':
// router.replace(path)
setCurrentTab(AboutTabs.Roadmap)
break
default:
break
}
}
const currentSection = () => {
switch (currentTab) {
case AboutTabs.About:
return <AboutPage />
case AboutTabs.Changelog:
return <ChangelogPage />
case AboutTabs.Roadmap:
return <RoadmapPage />
}
}
return (
<div id="About">
<Head>
{/* HTML */}
<title>{t('page.titles.about')}</title>
<meta name="description" content={t('page.descriptions.about')} />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{/* OpenGraph */}
<meta property="og:title" content={t('page.titles.about')} />
<meta
property="og:description"
content={t('page.descriptions.about')}
/>
<meta property="og:url" content="https://app.granblue.team/about" />
<meta property="og:type" content="website" />
{/* Twitter */}
<meta name="twitter:card" content="summary_large_image" />
<meta property="twitter:domain" content="app.granblue.team" />
<meta name="twitter:title" content={t('page.titles.about')} />
<meta
name="twitter:description"
content={t('page.descriptions.about')}
/>
</Head>
<section>
<SegmentedControl>
<Segment
groupName="about"
name="about"
selected={currentTab == AboutTabs.About}
onClick={handleTabClicked}
>
{t('about.segmented_control.about')}
</Segment>
<Segment
groupName="about"
name="changelog"
selected={currentTab == AboutTabs.Changelog}
onClick={handleTabClicked}
>
{t('about.segmented_control.changelog')}
</Segment>
<Segment
groupName="about"
name="roadmap"
selected={currentTab == AboutTabs.Roadmap}
onClick={handleTabClicked}
>
{t('about.segmented_control.roadmap')}
</Segment>
</SegmentedControl>
{currentSection()}
</section>
</div>
)
}
export const getServerSidePaths = async () => {
return {
paths: [],
fallback: true,
}
}
// prettier-ignore
export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => {
// Set headers for server-side requests
setUserToken(req, res)
// Fetch and organize raids
return {
props: {
...(await serverSideTranslations(locale, ['common', 'roadmap'])),
// Will be passed to the page component as props
},
}
}
export default AboutRoute

View file

@ -1,4 +1,11 @@
{
"about": {
"segmented_control": {
"about": "About",
"changelog": "Updates",
"roadmap": "Roadmap"
}
},
"alert": {
"incompatible_weapon": "You've selected a weapon that can't be added to the Additional Weapon slots."
},

View file

@ -1,4 +1,11 @@
{
"about": {
"segmented_control": {
"about": "サイトについて",
"changelog": "変更ログ",
"roadmap": "ロードマップ"
}
},
"alert": {
"incompatible_weapon": "Additional Weaponsに装備できない武器を入れました。"
},

View file

@ -119,6 +119,86 @@ select {
}
}
.PageContent {
display: flex;
flex-direction: column;
gap: $unit-4x;
max-width: $grid-width;
margin: $unit-4x auto 0;
h1 {
font-size: $font-xxlarge;
text-align: left;
}
h2 {
font-size: $font-medium;
font-weight: $medium;
margin-bottom: $unit * 3;
}
p {
color: var(--text-secondary);
font-size: $font-regular;
line-height: 1.3;
margin-bottom: $unit;
&:last-of-type {
margin-bottom: 0;
}
}
.LinkItem {
$diameter: $unit-6x;
border: 1px solid var(--link-item-bg);
border-radius: $card-corner;
&:hover {
background-color: var(--link-item-bg);
svg {
fill: var(--link-item-image-color-hover);
}
}
a {
display: flex;
padding: $unit-2x;
&:hover {
text-decoration: none;
}
.Left {
align-items: center;
display: flex;
gap: $unit-2x;
flex-grow: 1;
h3 {
font-weight: 600;
max-width: 70%;
line-height: 1.3;
}
}
svg {
fill: var(--link-item-image-color);
width: $diameter;
height: auto;
&.ShareIcon {
width: $unit-4x;
}
}
}
h3 {
font-weight: $bold;
}
}
}
.Hovercard {
background: #222;
border-radius: $unit;

View file

@ -19,3 +19,9 @@ export enum TeamElement {
Dark,
Light,
}
export enum AboutTabs {
About,
Changelog,
Roadmap,
}