fix(web): implement offline detection (#1065)

* xtreme connected edition

* change fallbackRender to fallbackComponent on ErrorBoundary

* call healthz endpoint when error is 500

* display custom offline message

* fix eslint indentation for switchCase

* Update ErrorPage.tsx

* check against error.cause

---------

Co-authored-by: Fabricio Silva <hi@fabricio.dev>
This commit is contained in:
Kyle Sanderson 2023-09-10 08:39:58 -07:00 committed by GitHub
parent d187daa566
commit a1a16adbab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 34 additions and 25 deletions

View file

@ -19,7 +19,7 @@ module.exports = {
// Allow only double quotes and backticks
quotes: ["error", "double"],
// Warn if a line isn't indented with a multiple of 2
indent: ["warn", 2],
indent: ["warn", 2, { "SwitchCase": 1 }],
// Don't enforce any particular brace style
curly: "off",
// Allow only vars starting with _ to be ununsed vars
@ -64,7 +64,7 @@ module.exports = {
"@typescript-eslint/quotes": ["error", "double"],
semi: "off",
"@typescript-eslint/semi": ["warn", "always"],
indent: ["warn", 2],
indent: ["warn", 2, { "SwitchCase": 1 }],
"@typescript-eslint/indent": "off",
"@typescript-eslint/comma-dangle": "warn",
"keyword-spacing": "off",

View file

@ -22,7 +22,7 @@ const queryClient = new QueryClient({
// delay = Math.min(1000 * 2 ** attemptIndex, 30000)
retry: true,
useErrorBoundary: true,
suspense: true,
suspense: true
},
mutations: {
onError: (error) => {
@ -47,7 +47,7 @@ export function App() {
return (
<ErrorBoundary
onReset={reset}
fallbackRender={ErrorPage}
FallbackComponent={ErrorPage}
>
<QueryClientProvider client={queryClient}>
<Portal>

View file

@ -20,7 +20,7 @@ interface HttpConfig {
function encodeRFC3986URIComponent(str: string): string {
return encodeURIComponent(str).replace(
/[!'()*]/g,
(c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`,
(c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
);
}
@ -82,27 +82,27 @@ export async function HttpClient<T = unknown>(
const response = await window.fetch(`${baseUrl()}${endpoint}`, init);
switch (response.status) {
case 204:
case 204:
// 204 contains no data, but indicates success
return Promise.resolve<T>({} as T);
case 401:
return Promise.resolve<T>({} as T);
case 401:
// Remove auth info from localStorage
AuthContext.reset();
AuthContext.reset();
// Show an error toast to notify the user what occurred
return Promise.reject(new Error(`[401] Unauthorized: "${endpoint}"`));
case 404:
return Promise.reject(new Error(`[404] Not found: "${endpoint}"`));
case 500:
const health = await window.fetch(`${baseUrl()}api/healthz/liveness`);
if (!health.ok) {
return Promise.reject(
new Error(`[500] Offline (Internal server error): "${endpoint}"`, { cause: "OFFLINE" })
);
}
break;
default:
break;
// Show an error toast to notify the user what occurred
return Promise.reject(new Error(`[401] Unauthorized: "${endpoint}"`));
case 404:
return Promise.reject(new Error(`[404] Not found: "${endpoint}"`));
case 500:
const health = await window.fetch(`${baseUrl()}api/healthz/liveness`);
if (!health.ok) {
return Promise.reject(
new Error(`[500] Offline (Internal server error): "${endpoint}"`, { cause: "OFFLINE" })
);
}
break;
default:
break;
}
const isJson = response.headers.get("Content-Type")?.includes("application/json");

View file

@ -17,11 +17,20 @@ export const ErrorPage = ({ error, resetErrorBoundary }: FallbackProps) => {
}
});
const parseTitle = () => {
switch (error?.cause) {
case "OFFLINE":
return "Connection to Autobrr failed! Check the application state and verify your connectivity.";
default:
return "We caught an unrecoverable error!";
}
};
return (
<div className="min-h-screen flex flex-col justify-center py-12 px-2 sm:px-6 lg:px-8">
<div className="sm:mx-auto sm:w-full sm:max-w-screen-md md:max-w-screen-lg lg:max-w-screen-xl">
<h1 className="text-3xl font-bold leading-6 text-gray-900 dark:text-gray-200 mt-4 mb-3">
We caught an unrecoverable error!
{parseTitle()}
</h1>
<h3 className="text-xl leading-6 text-gray-700 dark:text-gray-400 mb-4">
Please consider reporting this error to our
@ -67,7 +76,7 @@ export const ErrorPage = ({ error, resetErrorBoundary }: FallbackProps) => {
</pre>
) : null}
<span className="block text-gray-800 mb-2 text-md">
You can try resetting the page state using the button provided below.
You can try resetting the page state using the button provided below or restarting your autobrr application.
However, this is not guaranteed to fix the error.
</span>
<button