mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
fix: show support contact message if Studio fails to load (FE-4460) (#50872)
## Summary
- Adds a fallback message ("Taking longer than expected?... contact
support@supabase.io") shown after 7s if Studio fails to fully load, for
the Next.js runtime — mirrors the existing TanStack-only
`ShellFallback`, which had no Next.js equivalent
- Fixes the support email in the existing TanStack `ShellFallback` (was
`support@supabase.com`, should be `support@supabase.io`)
- Extracts the shared copy (message, email, delay) into one file so both
fallbacks stay in sync
## Why
Linear FE-4460: users reported the Dashboard going completely blank with
no way to reach support when a JS chunk failed to load. The Next.js
runtime (the current default) had no fallback at all for this case.
## Test plan
- [ ] Normal page load: fallback never appears
- [ ] Simulated stuck boot (mount signal disabled): fallback appears
after 7s with correct copy/email, no layout bugs
- [ ] Same two checks on the TanStack runtime
(`STUDIO_FRAMEWORK=tanstack`)
- [ ] `pnpm --filter studio run typecheck` / `lint:ratchet` pass
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added a loading fallback that appears if the app takes too long to
load, with guidance to clear browser cookies and reload.
* On self-hosted platforms, the fallback includes a support contact
link.
* The fallback is automatically hidden once the app loads.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
0003958f70
commit
1b5a806963
5 files changed
+97
-4
No files matched your search
@@ -1,5 +1,10 @@
|
||||
import { LogoLoader } from 'ui'
|
||||
|
||||
import {
|
||||
BOOT_FALLBACK_MESSAGE,
|
||||
BOOT_FALLBACK_REVEAL_DELAY_SECONDS,
|
||||
BOOT_FALLBACK_SUPPORT_EMAIL,
|
||||
} from '@/lib/boot-fallback-copy'
|
||||
import { IS_PLATFORM } from '@/lib/constants'
|
||||
|
||||
// Baked into the prerendered SPA shell (_shell.html) — this is the static HTML
|
||||
@@ -15,7 +20,7 @@ export function ShellFallback() {
|
||||
#studio-shell-help {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
animation: studio-shell-help-reveal 0.3s ease-out 7s forwards;
|
||||
animation: studio-shell-help-reveal 0.3s ease-out ${BOOT_FALLBACK_REVEAL_DELAY_SECONDS}s forwards;
|
||||
}
|
||||
@keyframes studio-shell-help-reveal {
|
||||
to {
|
||||
@@ -40,13 +45,13 @@ export function ShellFallback() {
|
||||
data-nosnippet=""
|
||||
className="max-w-md text-center text-sm text-foreground-light"
|
||||
>
|
||||
Taking longer than expected? Try clearing your browser cookies and reloading the page.
|
||||
{BOOT_FALLBACK_MESSAGE}
|
||||
{IS_PLATFORM && (
|
||||
<>
|
||||
{' '}
|
||||
If the problem persists, contact{' '}
|
||||
<a href="mailto:support@supabase.com" className="underline">
|
||||
support@supabase.com
|
||||
<a href={`mailto:${BOOT_FALLBACK_SUPPORT_EMAIL}`} className="underline">
|
||||
{BOOT_FALLBACK_SUPPORT_EMAIL}
|
||||
</a>
|
||||
.
|
||||
</>
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import {
|
||||
BOOT_FALLBACK_MESSAGE,
|
||||
BOOT_FALLBACK_REVEAL_DELAY_SECONDS,
|
||||
BOOT_FALLBACK_SUPPORT_EMAIL,
|
||||
} from '@/lib/boot-fallback-copy'
|
||||
import { IS_PLATFORM } from '@/lib/constants'
|
||||
|
||||
const FALLBACK_ID = 'studio-boot-fallback'
|
||||
|
||||
// Next.js equivalent of the TanStack build's ShellFallback (see
|
||||
// components/interfaces/App/ShellFallback.tsx): a safety net for a stuck
|
||||
// chunk load (FE-4460). Next always server-renders real page content into
|
||||
// <Main />, so — unlike TanStack's ClientOnly fallback, which shows nothing
|
||||
// until hydration — this can't be a full-screen overlay shown by default; it
|
||||
// has to reveal itself only once it's clear the page is actually stuck.
|
||||
//
|
||||
// The reveal is pure CSS (a delayed animation), not a JS timer, so it still
|
||||
// fires even if every JS chunk fails to load. Hiding it on a successful
|
||||
// mount necessarily needs JS — see `clearBootTimeoutFallback` — but a
|
||||
// missed hide is harmless: the reveal only starts after
|
||||
// BOOT_FALLBACK_REVEAL_DELAY_SECONDS, by which point a successful mount has
|
||||
// long since hidden it.
|
||||
export function BootTimeoutFallback() {
|
||||
return (
|
||||
<>
|
||||
<style>{`
|
||||
#${FALLBACK_ID} {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24px;
|
||||
background: #1c1c1c;
|
||||
pointer-events: none;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
animation: studio-boot-fallback-reveal 0.3s ease-out ${BOOT_FALLBACK_REVEAL_DELAY_SECONDS}s forwards;
|
||||
}
|
||||
#${FALLBACK_ID} p {
|
||||
color: #fff;
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
@keyframes studio-boot-fallback-reveal {
|
||||
to {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
<div id={FALLBACK_ID}>
|
||||
<p>
|
||||
{BOOT_FALLBACK_MESSAGE}
|
||||
{IS_PLATFORM && (
|
||||
<>
|
||||
{' '}
|
||||
If the problem persists, contact{' '}
|
||||
<a href={`mailto:${BOOT_FALLBACK_SUPPORT_EMAIL}`}>{BOOT_FALLBACK_SUPPORT_EMAIL}</a>.
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
/** Call once the app has actually mounted, to hide the fallback above. */
|
||||
export function clearBootTimeoutFallback() {
|
||||
const el = document.getElementById(FALLBACK_ID)
|
||||
if (el) el.style.display = 'none'
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Shared between ShellFallback (TanStack) and BootTimeoutFallback (Next.js) —
|
||||
// same "stuck loading" message, different markup per runtime (see FE-4460).
|
||||
export const BOOT_FALLBACK_REVEAL_DELAY_SECONDS = 7
|
||||
export const BOOT_FALLBACK_SUPPORT_EMAIL = 'support@supabase.io'
|
||||
export const BOOT_FALLBACK_MESSAGE =
|
||||
'Taking longer than expected? Try clearing your browser cookies and reloading the page.'
|
||||
@@ -48,6 +48,7 @@ import { MonacoThemeProvider } from '@/components/interfaces/App/MonacoThemeProv
|
||||
import { RouteValidationWrapper } from '@/components/interfaces/App/RouteValidationWrapper'
|
||||
import { MainScrollContainerProvider } from '@/components/layouts/MainScrollContainerContext'
|
||||
import { BannerStackProvider } from '@/components/ui/BannerStack/BannerStackProvider'
|
||||
import { clearBootTimeoutFallback } from '@/components/ui/BootTimeoutFallback/BootTimeoutFallback'
|
||||
import { GlobalErrorBoundaryState } from '@/components/ui/ErrorBoundary/GlobalErrorBoundaryState'
|
||||
import { GlobalShortcuts } from '@/components/ui/GlobalShortcuts/GlobalShortcuts'
|
||||
import { getCLIReleaseVersion } from '@/data/misc/cli-release-version-query'
|
||||
@@ -157,6 +158,10 @@ function CustomApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
|
||||
useThemeSandbox()
|
||||
|
||||
useEffect(() => {
|
||||
clearBootTimeoutFallback()
|
||||
}, [])
|
||||
|
||||
const isTestEnv = process.env.NEXT_PUBLIC_NODE_ENV === 'test'
|
||||
|
||||
// [Joshen] Should target hosted staging, local dev, and local CLI only
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Document, { DocumentContext, Head, Html, Main, NextScript } from 'next/document'
|
||||
|
||||
import { BootTimeoutFallback } from '@/components/ui/BootTimeoutFallback/BootTimeoutFallback'
|
||||
import { inter, manrope, sourceCodePro } from '@/fonts'
|
||||
|
||||
class MyDocument extends Document {
|
||||
@@ -14,6 +15,7 @@ class MyDocument extends Document {
|
||||
<Html lang="en">
|
||||
<Head />
|
||||
<body className={`${inter.variable} ${manrope.variable} ${sourceCodePro.variable}`}>
|
||||
<BootTimeoutFallback />
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
|
||||
Reference in new issue
Block a user