diff --git a/src/api/SwaggerApi.js b/src/api/SwaggerApi.js index fe3e035ea569dbd5c79ec740d0bdbb5af5ebe676..69c86b2137d69aaa15d7e703390c075873f68b9e 100644 --- a/src/api/SwaggerApi.js +++ b/src/api/SwaggerApi.js @@ -1,4 +1,4 @@ -import React, { useCallback } from "react"; +import React, { useCallback, useRef } from "react"; import SwaggerClient from 'swagger-client'; import { createContext, useContext, useEffect, useState } from "react"; import { CircularProgress } from '@material-ui/core'; @@ -49,7 +49,7 @@ export function UserProvider({ children }) { const resetLoginError = useCallback(() => setLoginError(null), [setLoginError]); console.log("rendering UserProvider") - useEffect(getUser, [Boolean(loginResponse), getUser]); + useEffect(getUser, [loginResponse, getUser]); const logout = useCallback(async () => { console.log("Logging out!") @@ -57,11 +57,10 @@ export function UserProvider({ children }) { logoutFcn(); }, [resetUser, logoutFcn]) - const login = useCallback(async (username, password) => { + const login = useCallback((username, password) => { console.log("Logging in!"); - await loginFcn(username, password); - getUser(); - }, [loginFcn, getUser]) + loginFcn(username, password); + }, [loginFcn]) const createValue = useCallback(() => { return { user: user?.userInfo, userRoles: user?.userRoles, getUser, login, loginError, resetLoginError, logout }; @@ -118,31 +117,63 @@ export function useAPIErrorHandler(onError) { return useCallback(errorHandler, [logout]); } -export function useAsync({ fcn, call = true, init = null, onError = null }) { +export function useQueuedResponse({ init = null, onError = null }) { + const queueRef = useRef([]); + const busyRef = useRef(false); const [response, setResponse] = useState(init); const [loading, setLoading] = useState(false); const errorHandler = useAPIErrorHandler(onError); + const MAX_QUEUE_LENGTH = 10; - const reset = useCallback(() => setResponse(init), [setResponse, init]); + const processQueue = useCallback( + async () => { + if (busyRef.current) { + return; + } - const f = async (...args) => { - try { + busyRef.current = true; setLoading(true); - const r = await fcn(...args); - console.log(r); - setResponse(r); - } catch (e) { - console.warn(e); - errorHandler(e); + + let responses = []; + const queue = queueRef.current; + while (responses.length !== queue.length) { + responses = await Promise.allSettled(queue); + } + + queueRef.current = [] + setLoading(false); + busyRef.current = false; + + const { status, value, reason } = responses.pop(); + if (status === "rejected") { + errorHandler(reason); + return; + } + + setResponse(value); + }, [errorHandler]) + + const enqueue = useCallback((promise) => { + if (queueRef.current.length > MAX_QUEUE_LENGTH) { + throw new Error(`Max queue length (${MAX_QUEUE_LENGTH}) exceeded in useQueuedResponse.`) } - setLoading(false); - }; + queueRef.current.push(promise); + processQueue(); + }, [queueRef, processQueue]) + + return { response, setResponse, loading, enqueue }; +} + +export function useAsync({ fcn, call = true, init = null, onError = null }) { + const { response, setResponse, loading, enqueue } = useQueuedResponse({ init, onError }); + + const reset = useCallback(() => setResponse(init), [setResponse, init]); - const wrapper = useCallback(f, [fcn, setResponse, setLoading, errorHandler]); - // const [loading, lwrapper] = useLoading(wrapper, response); + const wrapper = useCallback((...args) => { + enqueue(fcn(...args)); + }, [fcn, enqueue]); useEffect(() => { - console.log("i'm callin"); if (call) wrapper(); }, [wrapper, call]); diff --git a/src/components/navigation/ErrorBoundary/ErrorBoundary.js b/src/components/navigation/ErrorBoundary/ErrorBoundary.js index f27ff0fad67a1e6cebbc8677f1343521bd775943..93063197026074e22635a52b85af50cdc988db8c 100644 --- a/src/components/navigation/ErrorBoundary/ErrorBoundary.js +++ b/src/components/navigation/ErrorBoundary/ErrorBoundary.js @@ -35,14 +35,16 @@ function AwSnap({ error, resetErrorBoundary }) { function WindowErrorRedirect({ children }) { const handleError = useErrorHandler(); - const wtf = useCallback((event) => { - handleError(event.error); + const onError = useCallback((event) => { + handleError(event.error ?? event.reason); }, [handleError]); useEffectOnMount(() => { - window.addEventListener('error', wtf); + window.addEventListener('error', onError); + window.addEventListener('unhandledrejection', onError); return function cleanup() { - window.removeEventListener('error', wtf); + window.removeEventListener('error', onError); + window.removeEventListener('unhandledrejection', onError); } }) @@ -54,10 +56,7 @@ export function AppErrorBoundary({ children }) { <ErrorBoundary FallbackComponent={AwSnap} onReset={() => { - const reload = window.confirm("The application will reload"); - if (reload) { window.location.href = "/"; - } }} > <WindowErrorRedirect>