import { useNavigation } from '@react-navigation/native'
import { useQueryErrorResetBoundary } from '@tanstack/react-query'
import React, { PropsWithChildren, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { onAuthRefresh } from '../../utils/auth_events'
import { ResponseError } from '../../utils/response_error'
import BlankState from '../primitive/blank_state_primitive'
import { ErrorBoundary, ErrorBoundaryFallback, ErrorBoundaryProps } from './error_boundary'
const renderPageFallback: ErrorBoundaryFallback = (error, reset) => (
)
export function PageErrorBoundary({ children, ...props }: PropsWithChildren) {
return (
{children}
)
}
function ErrorView({ error, reset }: { error: Error; reset: () => void }) {
const { reset: resetQueries } = useQueryErrorResetBoundary()
const handleReset = useCallback(() => {
resetQueries()
reset()
}, [resetQueries, reset])
useEffect(() => handleReset, [handleReset])
if (error instanceof ResponseError) {
return
}
if (isNetworkError(error)) {
return (
)
}
return
}
function isNetworkError(error: Error) {
const networkFailedMessages = [
'Network request failed',
'Network request timed out',
'Failed to fetch',
]
return new RegExp(networkFailedMessages.join('|'), 'i').test(error.message)
}
function ResponseErrorView({ response, onReset }: { response: Response; onReset: () => void }) {
const { status } = response
const heading = useMemo(() => {
switch (status) {
case 403:
return 'Permission required'
case 404:
return 'Content not found'
default:
return 'Oops!'
}
}, [status])
const body = useMemo(() => {
switch (status) {
case 403:
return 'Contact your administrator for access.'
case 404:
return 'If you believe something should be here, please reach out to your administrator.'
default:
return 'Something unexpected happened.'
}
}, [status])
useEffect(() => {
if (status !== 401) return
return onAuthRefresh(onReset)
}, [status, onReset])
if (status === 429) {
return
}
return
}
const RATE_LIMIT_WINDOW_S = 20
function RateLimitedView({ response, onReset }: { response: Response; onReset: () => void }) {
const retryAfterS = parseInt(
response.headers?.get('Retry-After') ?? String(RATE_LIMIT_WINDOW_S),
10
)
const readyAt = useRef(Date.now() + retryAfterS * 1000).current
const [now, setNow] = useState(Date.now)
useEffect(() => {
const id = setInterval(() => {
setNow(prev => (prev >= readyAt ? prev : Date.now()))
}, 1000)
return () => clearInterval(id)
}, [readyAt])
const navigation = useNavigation()
const secondsLeft = Math.max(0, Math.ceil((readyAt - now) / 1000))
return (
Too many requests
Chat is being accessed too frequently. Please wait a moment.
0 ? `Retry in ${secondsLeft}s` : 'Try again'}
disabled={secondsLeft > 0}
onPress={onReset}
size="md"
accessibilityRole="button"
/>
Go back
)
}
function ErrorContent({ heading, body }: { heading: string; body: string }) {
const navigation = useNavigation()
return (
{heading}
{body}
navigation.navigate('BugReport')}>
Report a bug
)
}