fix admin exit not redirecting, my posts crash, add error boundary
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect, useRef } from 'react'
|
import { useState, useEffect, useRef, Component, type ReactNode, type ErrorInfo } from 'react'
|
||||||
import { BrowserRouter, Routes, Route, useLocation, useNavigate, Link } from 'react-router-dom'
|
import { BrowserRouter, Routes, Route, useLocation, useNavigate, Link } from 'react-router-dom'
|
||||||
import { IconShieldCheck, IconArrowRight, IconX, IconSearch, IconChevronUp, IconMessageCircle, IconBug, IconBulb, IconCommand, IconArrowNarrowRight } from '@tabler/icons-react'
|
import { IconShieldCheck, IconArrowRight, IconX, IconSearch, IconChevronUp, IconMessageCircle, IconBug, IconBulb, IconCommand, IconArrowNarrowRight } from '@tabler/icons-react'
|
||||||
import { AuthProvider, useAuthState } from './hooks/useAuth'
|
import { AuthProvider, useAuthState } from './hooks/useAuth'
|
||||||
@@ -45,6 +45,26 @@ import ProfilePage from './pages/ProfilePage'
|
|||||||
import RecoverPage from './pages/RecoverPage'
|
import RecoverPage from './pages/RecoverPage'
|
||||||
import EmbedBoard from './pages/EmbedBoard'
|
import EmbedBoard from './pages/EmbedBoard'
|
||||||
import { api } from './lib/api'
|
import { api } from './lib/api'
|
||||||
|
|
||||||
|
class ErrorBoundary extends Component<{ children: ReactNode }, { error: Error | null }> {
|
||||||
|
state = { error: null as Error | null }
|
||||||
|
static getDerivedStateFromError(error: Error) { return { error } }
|
||||||
|
componentDidCatch(error: Error, info: ErrorInfo) { console.error('React error:', error, info) }
|
||||||
|
render() {
|
||||||
|
if (this.state.error) {
|
||||||
|
return (
|
||||||
|
<div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--bg)', padding: 24 }}>
|
||||||
|
<div style={{ textAlign: 'center', maxWidth: 400 }}>
|
||||||
|
<h1 style={{ fontFamily: 'var(--font-heading)', fontSize: '1.5rem', color: 'var(--text)', marginBottom: 8 }}>Something went wrong</h1>
|
||||||
|
<p style={{ color: 'var(--text-secondary)', fontSize: '0.875rem', marginBottom: 16 }}>{this.state.error.message}</p>
|
||||||
|
<button onClick={() => { this.setState({ error: null }); window.location.href = '/' }} className="btn btn-primary">Go home</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return this.props.children
|
||||||
|
}
|
||||||
|
}
|
||||||
import BoardIcon from './components/BoardIcon'
|
import BoardIcon from './components/BoardIcon'
|
||||||
import StatusBadge from './components/StatusBadge'
|
import StatusBadge from './components/StatusBadge'
|
||||||
import Avatar from './components/Avatar'
|
import Avatar from './components/Avatar'
|
||||||
@@ -348,16 +368,16 @@ function SearchPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function RequireAdmin({ children }: { children: React.ReactNode }) {
|
function RequireAdmin({ children }: { children: React.ReactNode }) {
|
||||||
const [ok, setOk] = useState<boolean | null>(null)
|
const admin = useAdmin()
|
||||||
const nav = useNavigate()
|
const nav = useNavigate()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
api.get('/admin/boards')
|
if (!admin.loading && !admin.isAdmin) {
|
||||||
.then(() => setOk(true))
|
nav('/', { replace: true })
|
||||||
.catch(() => nav('/admin/login', { replace: true }))
|
}
|
||||||
}, [nav])
|
}, [admin.loading, admin.isAdmin, nav])
|
||||||
|
|
||||||
if (!ok) return null
|
if (admin.loading || !admin.isAdmin) return null
|
||||||
return <>{children}</>
|
return <>{children}</>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,7 +427,7 @@ function AdminBanner() {
|
|||||||
Admin panel <IconArrowRight size={11} stroke={2.5} />
|
Admin panel <IconArrowRight size={11} stroke={2.5} />
|
||||||
</Link>
|
</Link>
|
||||||
<button
|
<button
|
||||||
onClick={admin.exitAdminMode}
|
onClick={() => { admin.exitAdminMode(); window.location.href = '/' }}
|
||||||
className="inline-flex items-center gap-1 px-2.5 py-1 rounded"
|
className="inline-flex items-center gap-1 px-2.5 py-1 rounded"
|
||||||
style={{
|
style={{
|
||||||
color: 'var(--text-tertiary)',
|
color: 'var(--text-tertiary)',
|
||||||
@@ -545,6 +565,7 @@ export default function App() {
|
|||||||
}, [auth.user?.isPasskeyUser])
|
}, [auth.user?.isPasskeyUser])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<ErrorBoundary>
|
||||||
<BrandingProvider>
|
<BrandingProvider>
|
||||||
<ToastProvider>
|
<ToastProvider>
|
||||||
<ConfirmProvider>
|
<ConfirmProvider>
|
||||||
@@ -565,5 +586,6 @@ export default function App() {
|
|||||||
</ConfirmProvider>
|
</ConfirmProvider>
|
||||||
</ToastProvider>
|
</ToastProvider>
|
||||||
</BrandingProvider>
|
</BrandingProvider>
|
||||||
|
</ErrorBoundary>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ export default function MySubmissions() {
|
|||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
api.get<Post[]>('/me/posts')
|
api.get<{ posts: Post[] }>('/me/posts')
|
||||||
.then(setPosts)
|
.then((r) => setPosts(r.posts ?? []))
|
||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
.finally(() => setLoading(false))
|
.finally(() => setLoading(false))
|
||||||
}, [])
|
}, [])
|
||||||
|
|||||||
Reference in New Issue
Block a user