diff options
author | Sebastian <sebasjm@gmail.com> | 2024-03-21 12:11:31 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2024-03-26 16:57:55 -0300 |
commit | 8aa9ce6d20b41b7eb9b438a56ccd34cb0da35f80 (patch) | |
tree | 5cec8219a136469f2c689c47a74b28e52eb5992b /packages/merchant-backoffice-ui/src/Application.tsx | |
parent | 5a1f528d8806bcb15d5c4c8364554502c11931a7 (diff) | |
download | wallet-core-8aa9ce6d20b41b7eb9b438a56ccd34cb0da35f80.tar.xz |
wip
Diffstat (limited to 'packages/merchant-backoffice-ui/src/Application.tsx')
-rw-r--r-- | packages/merchant-backoffice-ui/src/Application.tsx | 225 |
1 files changed, 99 insertions, 126 deletions
diff --git a/packages/merchant-backoffice-ui/src/Application.tsx b/packages/merchant-backoffice-ui/src/Application.tsx index cf46a34d5..9e6a6179f 100644 --- a/packages/merchant-backoffice-ui/src/Application.tsx +++ b/packages/merchant-backoffice-ui/src/Application.tsx @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2021-2023 Taler Systems S.A. + (C) 2021-2024 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -19,147 +19,120 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { HttpStatusCode, LibtoolVersion } from "@gnu-taler/taler-util"; +import { canonicalizeBaseUrl } from "@gnu-taler/taler-util"; import { - ErrorType, - TranslationProvider, - useTranslationContext + BrowserHashNavigationProvider, + MerchantApiProvider, + TalerWalletIntegrationBrowserProvider, + TranslationProvider } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; -import { useMemo } from "preact/hooks"; -import { ApplicationReadyRoutes } from "./ApplicationReadyRoutes.js"; +import { useEffect, useState } from "preact/hooks"; +import { SWRConfig } from "swr"; +import { Routing } from "./Routing.js"; import { Loading } from "./components/exception/loading.js"; -import { - NotConnectedAppMenu, - NotificationCard -} from "./components/menu/index.js"; -import { - BackendContextProvider -} from "./context/backend.js"; -import { ConfigContextProvider } from "./context/config.js"; -import { useBackendConfig } from "./hooks/backend.js"; +import { SettingsProvider } from "./context/settings.js"; import { strings } from "./i18n/strings.js"; +import { MerchantUiSettings, buildDefaultBackendBaseURL, fetchSettings } from "./settings.js"; +const WITH_LOCAL_STORAGE_CACHE = false; export function Application(): VNode { + const [settings, setSettings] = useState<MerchantUiSettings>(); + useEffect(() => { + fetchSettings(setSettings); + }, []); + if (!settings) return <Loading />; + + const baseUrl = getInitialBackendBaseURL(settings.backendBaseURL); return ( - <BackendContextProvider> - <TranslationProvider source={strings}> - <ApplicationStatusRoutes /> + <SettingsProvider value={settings}> + <TranslationProvider + source={strings} + completeness={{ + es: strings["es"].completeness, + de: strings["de"].completeness, + }} + > + <MerchantApiProvider baseUrl={new URL("/", baseUrl)} frameOnError={({ children }) => <div>{children}</div>}> + <SWRConfig + value={{ + provider: WITH_LOCAL_STORAGE_CACHE + ? localStorageProvider + : undefined, + // normally, do not revalidate + revalidateOnFocus: false, + revalidateOnReconnect: false, + revalidateIfStale: false, + revalidateOnMount: undefined, + focusThrottleInterval: undefined, + + // normally, do not refresh + refreshInterval: undefined, + dedupingInterval: 2000, + refreshWhenHidden: false, + refreshWhenOffline: false, + + // ignore errors + shouldRetryOnError: false, + errorRetryCount: 0, + errorRetryInterval: undefined, + + // do not go to loading again if already has data + keepPreviousData: true, + }} + > + <TalerWalletIntegrationBrowserProvider> + <BrowserHashNavigationProvider> + <Routing /> + </BrowserHashNavigationProvider> + </TalerWalletIntegrationBrowserProvider> + </SWRConfig> + </MerchantApiProvider> </TranslationProvider> - </BackendContextProvider> + </SettingsProvider> ); } -/** - * Check connection testing against /config - * - * @returns - */ -function ApplicationStatusRoutes(): VNode { - const result = useBackendConfig(); - const { i18n } = useTranslationContext(); - - const configData = result.ok && result.data - ? result.data - : undefined; - const ctx = useMemo(() => (configData), [configData]); +function getInitialBackendBaseURL( + backendFromSettings: string | undefined, +): string { + /** + * For testing purpose + */ + const overrideUrl = + typeof localStorage !== "undefined" + ? localStorage.getItem("merchant-base-url") + : undefined; + let result: string; - if (!result.ok) { - if (result.loading) return <Loading />; - if ( - result.type === ErrorType.CLIENT && - result.status === HttpStatusCode.Unauthorized - ) { - return ( - <Fragment> - <NotConnectedAppMenu title="Login" /> - <NotificationCard - notification={{ - message: i18n.str`Checking the /config endpoint got authorization error`, - type: "ERROR", - description: `The /config endpoint of the backend server should be accessible`, - }} - /> - </Fragment> + if (overrideUrl) { + // testing/development path + result = overrideUrl; + } else { + // normal path + if (!backendFromSettings) { + console.error( + "ERROR: backendBaseURL was overridden by a setting file and missing. Setting value to 'window.origin'", ); + result = buildDefaultBackendBaseURL(); + } else { + result = backendFromSettings; } - if ( - result.type === ErrorType.CLIENT && - result.status === HttpStatusCode.NotFound - ) { - return ( - <Fragment> - <NotConnectedAppMenu title="Error" /> - <NotificationCard - notification={{ - message: i18n.str`Could not find /config endpoint on this URL`, - type: "ERROR", - description: `Check the URL or contact the system administrator.`, - }} - /> - </Fragment> - ); - } - if (result.type === ErrorType.SERVER) { - <Fragment> - <NotConnectedAppMenu title="Error" /> - <NotificationCard - notification={{ - message: i18n.str`Server response with an error code`, - type: "ERROR", - description: i18n.str`Got message "${result.message}" from ${result.info?.url}`, - }} - /> - </Fragment>; - } - if (result.type === ErrorType.UNREADABLE) { - <Fragment> - <NotConnectedAppMenu title="Error" /> - <NotificationCard - notification={{ - message: i18n.str`Response from server is unreadable, http status: ${result.status}`, - type: "ERROR", - description: i18n.str`Got message "${result.message}" from ${result.info?.url}`, - }} - /> - </Fragment>; - } - return ( - <Fragment> - <NotConnectedAppMenu title="Error" /> - <NotificationCard - notification={{ - message: i18n.str`Unexpected Error`, - type: "ERROR", - description: i18n.str`Got message "${result.message}" from ${result.info?.url}`, - }} - /> - </Fragment> - ); } - - const SUPPORTED_VERSION = "8:1:4" - if (result.data && !LibtoolVersion.compare( - SUPPORTED_VERSION, - result.data.version, - )?.compatible) { - return <Fragment> - <NotConnectedAppMenu title="Error" /> - <NotificationCard - notification={{ - message: i18n.str`Incompatible version`, - type: "ERROR", - description: i18n.str`Merchant backend server version ${result.data.version} is not compatible with the supported version ${SUPPORTED_VERSION}`, - }} - /> - </Fragment> + try { + return canonicalizeBaseUrl(result); + } catch (e) { + // fall back + return canonicalizeBaseUrl(window.origin); } +} - return ( - <div class="has-navbar-fixed-top"> - <ConfigContextProvider value={ctx!}> - <ApplicationReadyRoutes /> - </ConfigContextProvider> - </div> - ); +function localStorageProvider(): Map<unknown, unknown> { + const map = new Map(JSON.parse(localStorage.getItem("app-cache") || "[]")); + + window.addEventListener("beforeunload", () => { + const appCache = JSON.stringify(Array.from(map.entries())); + localStorage.setItem("app-cache", appCache); + }); + return map; } |