fix(app): unlimited loading screen when backend is not reachable (#364)

* Initial plan

* Add error handling for unlimited loading screen bug

Co-authored-by: antontranelis <31516529+antontranelis@users.noreply.github.com>

* Complete fix for unlimited loading screen bug with proper state management

Co-authored-by: antontranelis <31516529+antontranelis@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: antontranelis <31516529+antontranelis@users.noreply.github.com>
This commit is contained in:
Copilot 2025-09-07 12:45:37 +02:00 committed by GitHub
parent dbb405fe7e
commit a9004a47ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -71,6 +71,15 @@ function App() {
const [layers, setLayers] = useState<any>()
const [layerPageRoutes, setLayerPageRoutes] = useState<any>()
const [loading, setLoading] = useState<boolean>(true)
const [error, setError] = useState<string | null>(null)
const retryConnection = () => {
setError(null)
setLoading(true)
if (mapApiInstance) {
getMap()
}
}
const [embedded, setEmbedded] = useState<boolean>(true)
@ -94,12 +103,27 @@ function App() {
}, [mapApiInstance])
const getMap = async () => {
try {
const map = await mapApiInstance?.getItems()
map && setMap(map)
map && map != 'null' && setLayersApiInstance(new layersApi(map.id))
map && map != 'null' && map.own_tag_space
? setTagsApi(new itemsApi<Tag>('tags', undefined, map.id))
: setTagsApi(new itemsApi<Tag>('tags'))
// eslint-disable-next-line no-catch-all/no-catch-all
} catch (error: any) {
// eslint-disable-next-line no-console
console.error('Failed to load map:', error)
setError(
typeof error === 'string'
? error
: error?.errors?.[0]?.message ||
error?.message ||
'Failed to connect to the server. Please check your connection and try again.',
)
setLoading(false)
// Don't rethrow since we're handling the error by setting error state
}
}
useEffect(() => {
@ -107,6 +131,7 @@ function App() {
}, [layersApiInstance])
const getLayers = async () => {
try {
const layers = await layersApiInstance?.getItems()
layers && setLayers(layers)
setLayerPageRoutes(
@ -126,6 +151,20 @@ function App() {
name: l.name, // name that appear in Sidebar
})),
)
// eslint-disable-next-line no-catch-all/no-catch-all
} catch (error: any) {
// eslint-disable-next-line no-console
console.error('Failed to load layers:', error)
setError(
typeof error === 'string'
? error
: error?.errors?.[0]?.message ||
error?.message ||
'Failed to load map layers. Please check your permissions and try again.',
)
setLoading(false)
// Don't rethrow since we're handling the error by setting error state
}
}
useEffect(() => {
@ -140,8 +179,11 @@ function App() {
link.href = map?.logo && config.apiUrl + 'assets/' + map.logo // Specify the path to your favicon
}
// Only set loading to false when both map and layers are successfully loaded
if (map && layers) {
setLoading(false)
}, [map])
}
}, [map, layers])
const currentUrl = window.location.href
const bottomRoutes = getBottomRoutes(currentUrl)
@ -253,6 +295,35 @@ function App() {
</div>
</div>
)
else if (error)
return (
<div className='tw:flex tw:items-center tw:justify-center tw:h-screen tw:bg-base-100'>
<div className='tw:max-w-md tw:mx-auto tw:p-6 tw:text-center'>
<div className='tw:mb-4'>
<svg
className='tw:w-16 tw:h-16 tw:mx-auto tw:text-error tw:mb-4'
fill='none'
stroke='currentColor'
viewBox='0 0 24 24'
>
<path
strokeLinecap='round'
strokeLinejoin='round'
strokeWidth={2}
d='M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.314 16.5c-.77.833.192 2.5 1.732 2.5z'
/>
</svg>
</div>
<h2 className='tw:text-xl tw:font-semibold tw:text-base-content tw:mb-2'>
Connection Error
</h2>
<p className='tw:text-base-content/70 tw:mb-6'>{error}</p>
<button onClick={retryConnection} className='tw:btn tw:btn-primary'>
Try Again
</button>
</div>
</div>
)
else
return (
<div className='outer'>