Фикс ошибок

This commit is contained in:
2026-08-01 03:39:07 -07:00
parent bff945e701
commit 12f6384856
5 changed files with 114 additions and 22 deletions

View File

@@ -13,6 +13,7 @@ server {
} }
location / { location / {
add_header Cache-Control "no-cache" always;
try_files $uri $uri/ /index.html; try_files $uri $uri/ /index.html;
} }
} }

View File

@@ -1,7 +1,7 @@
import '@mantine/core/styles.css'; import '@mantine/core/styles.css';
import { Center, createTheme, Loader, MantineProvider } from '@mantine/core'; import { Button, Center, createTheme, Loader, MantineProvider, Stack, Text, Title } from '@mantine/core';
import { lazy, Suspense } from 'react'; import { Component, lazy, Suspense, type ReactNode } from 'react';
import { Route, Routes } from 'react-router-dom'; import { Route, Routes } from 'react-router-dom';
import { ScrollToTop } from './components/ScrollToTop/ScrollToTop'; import { ScrollToTop } from './components/ScrollToTop/ScrollToTop';
import './style.css'; import './style.css';
@@ -39,27 +39,60 @@ const theme = createTheme({
}, },
}); });
class PageErrorBoundary extends Component<
{ children: ReactNode },
{ hasError: boolean }
> {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
if (this.state.hasError) {
return (
<Center mih="100vh" p="xl">
<Stack align="center" gap="md" maw={460}>
<Title order={2} ta="center">
This page could not be loaded
</Title>
<Text c="dimmed" ta="center">
Rosetta may have been updated while this tab was open. Reload the page to continue.
</Text>
<Button onClick={() => window.location.reload()}>Reload page</Button>
</Stack>
</Center>
);
}
return this.props.children;
}
}
export default function App() { export default function App() {
return ( return (
<MantineProvider theme={theme}> <MantineProvider theme={theme}>
<ScrollToTop /> <PageErrorBoundary>
<Suspense <ScrollToTop />
fallback={ <Suspense
<Center mih="100vh"> fallback={
<Loader aria-label="Loading page" /> <Center mih="100vh">
</Center> <Loader aria-label="Loading page" />
} </Center>
> }
<Routes> >
<Route path="/" element={<LandingPage />} /> <Routes>
<Route path="/encryption" element={<EncryptionPage />} /> <Route path="/" element={<LandingPage />} />
<Route path="/proto" element={<ProtoPage />} /> <Route path="/encryption" element={<EncryptionPage />} />
<Route path="/proto/docs" element={<ProtoDocsPage />} /> <Route path="/proto" element={<ProtoPage />} />
<Route path="/support" element={<SupportPage />} /> <Route path="/proto/docs" element={<ProtoDocsPage />} />
<Route path="/privacy" element={<PrivacyPage />} /> <Route path="/support" element={<SupportPage />} />
<Route path="/terms" element={<TermsPage />} /> <Route path="/privacy" element={<PrivacyPage />} />
</Routes> <Route path="/terms" element={<TermsPage />} />
</Suspense> </Routes>
</Suspense>
</PageErrorBoundary>
</MantineProvider> </MantineProvider>
); );
} }

View File

@@ -1,18 +1,22 @@
.page { .page {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-width: 0;
min-height: 100vh; min-height: 100vh;
background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-9)); background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-9));
} }
.shell { .shell {
flex: 1; flex: 1;
width: 100%;
min-width: 0;
padding-top: 72px; padding-top: 72px;
padding-bottom: 96px; padding-bottom: 96px;
} }
.main { .main {
width: 100%; width: 100%;
min-width: 0;
max-width: 900px; max-width: 900px;
margin-inline: auto; margin-inline: auto;
padding: clamp(30px, 5vw, 60px); padding: clamp(30px, 5vw, 60px);
@@ -21,6 +25,7 @@
} }
.intro { .intro {
min-width: 0;
max-width: 760px; max-width: 760px;
padding-bottom: 36px; padding-bottom: 36px;
border-bottom: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); border-bottom: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
@@ -69,6 +74,7 @@
} }
.section { .section {
min-width: 0;
margin-top: 44px; margin-top: 44px;
padding-top: 36px; padding-top: 36px;
border-top: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); border-top: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));

View File

@@ -2,6 +2,42 @@ import { createRoot } from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom' import { BrowserRouter } from 'react-router-dom'
import App from './App.tsx' import App from './App.tsx'
const PRELOAD_RELOAD_KEY = 'rosetta:preload-reload-at'
const PRELOAD_RELOAD_COOLDOWN = 10_000
if (import.meta.env.PROD) {
window.addEventListener('vite:preloadError', (event) => {
let lastReload = 0
try {
lastReload = Number(window.sessionStorage.getItem(PRELOAD_RELOAD_KEY) || 0)
} catch {
// Storage can be unavailable in restricted browser modes.
}
if (Date.now() - lastReload < PRELOAD_RELOAD_COOLDOWN) {
return
}
event.preventDefault()
try {
window.sessionStorage.setItem(PRELOAD_RELOAD_KEY, String(Date.now()))
} catch {
// Reload recovery still works when storage is unavailable.
}
window.location.reload()
})
window.setTimeout(() => {
try {
window.sessionStorage.removeItem(PRELOAD_RELOAD_KEY)
} catch {
// Nothing to clear when storage is unavailable.
}
}, PRELOAD_RELOAD_COOLDOWN)
}
createRoot(document.getElementById('root')!).render( createRoot(document.getElementById('root')!).render(
<BrowserRouter> <BrowserRouter>

View File

@@ -1,3 +1,18 @@
function withFreshHtml(response) {
if (!response.headers.get('content-type')?.includes('text/html')) {
return response;
}
const headers = new Headers(response.headers);
headers.set('Cache-Control', 'no-cache');
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers,
});
}
const worker = { const worker = {
async fetch(request, env) { async fetch(request, env) {
const response = await env.ASSETS.fetch(request); const response = await env.ASSETS.fetch(request);
@@ -7,11 +22,12 @@ const worker = {
request.method !== 'GET' || request.method !== 'GET' ||
!request.headers.get('accept')?.includes('text/html') !request.headers.get('accept')?.includes('text/html')
) { ) {
return response; return withFreshHtml(response);
} }
const indexUrl = new URL('/index.html', request.url); const indexUrl = new URL('/index.html', request.url);
return env.ASSETS.fetch(new Request(indexUrl, request)); const indexResponse = await env.ASSETS.fetch(new Request(indexUrl, request));
return withFreshHtml(indexResponse);
}, },
}; };