Add a message if the server goes down
Right now the app fails silently if the server becomes unreachable. Now, we use the version check to determine if we have a connection to the server, and if not we display an error message.
This commit is contained in:
parent
9de87abd1e
commit
ab671b509d
6 changed files with 264 additions and 148 deletions
|
|
@ -17,16 +17,18 @@ const Layout = ({ children }: PropsWithChildren<Props>) => {
|
|||
const [updateToastOpen, setUpdateToastOpen] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const cookie = getToastCookie()
|
||||
const now = new Date()
|
||||
const updatedAt = new Date(appState.version.updated_at)
|
||||
const validUntil = add(updatedAt, { days: 7 })
|
||||
if (appState.version) {
|
||||
const cookie = getToastCookie()
|
||||
const now = new Date()
|
||||
const updatedAt = new Date(appState.version.updated_at)
|
||||
const validUntil = add(updatedAt, { days: 7 })
|
||||
|
||||
if (now < validUntil && !cookie.seen) setUpdateToastOpen(true)
|
||||
if (now < validUntil && !cookie.seen) setUpdateToastOpen(true)
|
||||
}
|
||||
}, [])
|
||||
|
||||
function getToastCookie() {
|
||||
if (appState.version.updated_at !== '') {
|
||||
if (appState.version && appState.version.updated_at !== '') {
|
||||
const updatedAt = new Date(appState.version.updated_at)
|
||||
const cookieValues = getCookie(
|
||||
`update-${format(updatedAt, 'yyyy-MM-dd')}`
|
||||
|
|
@ -50,23 +52,32 @@ const Layout = ({ children }: PropsWithChildren<Props>) => {
|
|||
const updateToast = () => {
|
||||
const path = router.asPath.replaceAll('/', '')
|
||||
|
||||
return !['about', 'updates', 'roadmap'].includes(path) ? (
|
||||
<UpdateToast
|
||||
open={updateToastOpen}
|
||||
updateType={appState.version.update_type}
|
||||
onActionClicked={handleToastActionClicked}
|
||||
onCloseClicked={handleToastClosed}
|
||||
lastUpdated={appState.version.updated_at}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
return (
|
||||
!['about', 'updates', 'roadmap'].includes(path) &&
|
||||
appState.version && (
|
||||
<UpdateToast
|
||||
open={updateToastOpen}
|
||||
updateType={appState.version.update_type}
|
||||
onActionClicked={handleToastActionClicked}
|
||||
onCloseClicked={handleToastClosed}
|
||||
lastUpdated={appState.version.updated_at}
|
||||
/>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const ServerAvailable = () => {
|
||||
return (
|
||||
<>
|
||||
<TopHeader />
|
||||
{updateToast()}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopHeader />
|
||||
{updateToast()}
|
||||
{appState.version && ServerAvailable}
|
||||
<main>{children}</main>
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import { appWithTranslation } from 'next-i18next'
|
||||
import Link from 'next/link'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { get } from 'local-storage'
|
||||
import { getCookie, setCookie } from 'cookies-next'
|
||||
import { subscribe } from 'valtio'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { ThemeProvider } from 'next-themes'
|
||||
|
||||
import { appState } from '~utils/appState'
|
||||
import { accountState } from '~utils/accountState'
|
||||
import { retrieveCookies } from '~utils/retrieveCookies'
|
||||
import { setHeaders } from '~utils/userToken'
|
||||
|
|
@ -16,9 +19,12 @@ import Layout from '~components/Layout'
|
|||
|
||||
import type { AppProps } from 'next/app'
|
||||
|
||||
import DiscordIcon from '~public/icons/discord.svg'
|
||||
import ShareIcon from '~public/icons/Share.svg'
|
||||
import '../styles/globals.scss'
|
||||
|
||||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
const { t } = useTranslation('common')
|
||||
const [refresh, setRefresh] = useState(false)
|
||||
|
||||
// Subscribe to app state to listen for account changes and
|
||||
|
|
@ -88,12 +94,47 @@ function MyApp({ Component, pageProps }: AppProps) {
|
|||
}
|
||||
}
|
||||
|
||||
const serverUnavailable = () => {
|
||||
return (
|
||||
<div className="ServerUnavailableWrapper">
|
||||
<div className="ServerUnavailable">
|
||||
<div className="Message">
|
||||
<h1>{t('errors.server_unavailable.title')}</h1>
|
||||
<p>{t('errors.server_unavailable.message')}</p>
|
||||
</div>
|
||||
<div className="Connect">
|
||||
<p>{t('errors.server_unavailable.discord')}</p>
|
||||
<div className="Discord 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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<ThemeProvider>
|
||||
<ToastProvider swipeDirection="right">
|
||||
<TooltipProvider>
|
||||
<Layout>
|
||||
<Component {...pageProps} />
|
||||
{appState.version ? (
|
||||
serverUnavailable()
|
||||
) : (
|
||||
<Component {...pageProps} />
|
||||
)}
|
||||
</Layout>
|
||||
<Viewport className="ToastViewport" />
|
||||
</TooltipProvider>
|
||||
|
|
|
|||
|
|
@ -87,6 +87,11 @@
|
|||
"description": "The page you're looking for couldn't be found",
|
||||
"button": "Create a new party"
|
||||
},
|
||||
"server_unavailable": {
|
||||
"title": "Server unavailable",
|
||||
"message": "We're having trouble connecting to the server right now",
|
||||
"discord": "Join us on Discord for status updates"
|
||||
},
|
||||
"validation": {
|
||||
"guidebooks": "You cannot equip more than one of each Sephira Guidebook"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -87,6 +87,11 @@
|
|||
"description": "探しているページは見つかりませんでした",
|
||||
"button": "新しい編成を作成"
|
||||
},
|
||||
"server_unavailable": {
|
||||
"title": "サーバーダウン",
|
||||
"message": "現在サーバーがダウンしています。しばらくしてから再度お試しください",
|
||||
"discord": "Discordでサービスの状態を確認する"
|
||||
},
|
||||
"validation": {
|
||||
"guidebooks": "セフィラ導本を複数個装備することはできません"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -152,6 +152,188 @@ select {
|
|||
line-height: 1.3;
|
||||
}
|
||||
|
||||
#Teams,
|
||||
#Profile {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
gap: $unit * 2;
|
||||
}
|
||||
|
||||
#NotFound {
|
||||
height: 200px;
|
||||
width: 400px;
|
||||
margin: auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
h2 {
|
||||
color: $grey-60;
|
||||
font-size: $font-regular;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
img.profile {
|
||||
background: $grey-90;
|
||||
|
||||
&.fire {
|
||||
background: $fire-bg-20;
|
||||
}
|
||||
|
||||
&.water {
|
||||
background: $water-bg-20;
|
||||
}
|
||||
|
||||
&.wind {
|
||||
background: $wind-bg-20;
|
||||
}
|
||||
|
||||
&.earth {
|
||||
background: $earth-bg-20;
|
||||
}
|
||||
|
||||
&.dark {
|
||||
background: $dark-bg-10;
|
||||
}
|
||||
|
||||
&.light {
|
||||
background: $light-bg-20;
|
||||
}
|
||||
|
||||
&.anonymous {
|
||||
background: var(--anonymous-bg);
|
||||
}
|
||||
}
|
||||
|
||||
i.tag {
|
||||
background: var(--tag-bg);
|
||||
color: var(--tag-text);
|
||||
border-radius: calc($unit / 2);
|
||||
font-size: 10px;
|
||||
font-weight: $bold;
|
||||
padding: 4px 6px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.infinite-scroll-component {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.SearchFilterBar {
|
||||
display: flex;
|
||||
gap: $unit;
|
||||
padding: 0 ($unit * 3);
|
||||
|
||||
@include breakpoint(phone) {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.Joined {
|
||||
$offset: 2px;
|
||||
|
||||
align-items: center;
|
||||
background: var(--input-bg);
|
||||
border-radius: $input-corner;
|
||||
border: $offset solid transparent;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
gap: $unit;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
padding-right: calc($unit-2x - $offset);
|
||||
|
||||
&.Bound {
|
||||
background-color: var(--input-bound-bg);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--input-bound-bg-hover);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-within {
|
||||
border: $offset solid $blue;
|
||||
// box-shadow: 0 2px rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.Counter {
|
||||
color: $grey-55;
|
||||
font-weight: $bold;
|
||||
line-height: 42px;
|
||||
}
|
||||
|
||||
.Input {
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
padding: $unit * 1.5 $unit-2x;
|
||||
padding-left: calc($unit-2x - $offset);
|
||||
|
||||
&:focus {
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ServerUnavailableWrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
.ServerUnavailable {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $unit-6x;
|
||||
margin: auto;
|
||||
max-width: 440px;
|
||||
|
||||
.Message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $unit;
|
||||
|
||||
h1 {
|
||||
color: var(--text-primary);
|
||||
font-size: $font-xxlarge;
|
||||
font-weight: $bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p {
|
||||
color: var(--text-secondary);
|
||||
font-size: $font-regular;
|
||||
font-weight: $bold;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.Connect {
|
||||
color: var(--text-tertiary);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: $unit-2x;
|
||||
width: 100%;
|
||||
|
||||
p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.LinkItem {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.LinkItem {
|
||||
$diameter: $unit-6x;
|
||||
align-items: center;
|
||||
|
|
@ -225,131 +407,3 @@ select {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Teams,
|
||||
#Profile {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
gap: $unit * 2;
|
||||
}
|
||||
|
||||
#NotFound {
|
||||
height: 200px;
|
||||
width: 400px;
|
||||
margin: auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
h2 {
|
||||
color: $grey-60;
|
||||
font-size: $font-regular;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
img.profile {
|
||||
background: $grey-90;
|
||||
|
||||
&.fire {
|
||||
background: $fire-bg-20;
|
||||
}
|
||||
|
||||
&.water {
|
||||
background: $water-bg-20;
|
||||
}
|
||||
|
||||
&.wind {
|
||||
background: $wind-bg-20;
|
||||
}
|
||||
|
||||
&.earth {
|
||||
background: $earth-bg-20;
|
||||
}
|
||||
|
||||
&.dark {
|
||||
background: $dark-bg-10;
|
||||
}
|
||||
|
||||
&.light {
|
||||
background: $light-bg-20;
|
||||
}
|
||||
|
||||
&.anonymous {
|
||||
background: var(--anonymous-bg);
|
||||
}
|
||||
}
|
||||
|
||||
i.tag {
|
||||
background: var(--tag-bg);
|
||||
color: var(--tag-text);
|
||||
border-radius: calc($unit / 2);
|
||||
font-size: 10px;
|
||||
font-weight: $bold;
|
||||
padding: 4px 6px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.infinite-scroll-component {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.SearchFilterBar {
|
||||
display: flex;
|
||||
gap: $unit;
|
||||
padding: 0 ($unit * 3);
|
||||
|
||||
@include breakpoint(phone) {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.Joined {
|
||||
$offset: 2px;
|
||||
|
||||
align-items: center;
|
||||
background: var(--input-bg);
|
||||
border-radius: $input-corner;
|
||||
border: $offset solid transparent;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
gap: $unit;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
padding-right: calc($unit-2x - $offset);
|
||||
|
||||
&.Bound {
|
||||
background-color: var(--input-bound-bg);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--input-bound-bg-hover);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-within {
|
||||
border: $offset solid $blue;
|
||||
// box-shadow: 0 2px rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.Counter {
|
||||
color: $grey-55;
|
||||
font-weight: $bold;
|
||||
line-height: 42px;
|
||||
}
|
||||
|
||||
.Input {
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
padding: $unit * 1.5 $unit-2x;
|
||||
padding-left: calc($unit-2x - $offset);
|
||||
|
||||
&:focus {
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ interface AppState {
|
|||
jobs: Job[]
|
||||
jobSkills: JobSkill[]
|
||||
weaponKeys: GroupedWeaponKeys
|
||||
version: AppUpdate
|
||||
version?: AppUpdate
|
||||
status?: ResponseStatus
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue