summaryrefslogtreecommitdiff
path: root/packages/merchant-backoffice-ui/src/Application.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/merchant-backoffice-ui/src/Application.tsx')
-rw-r--r--packages/merchant-backoffice-ui/src/Application.tsx225
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;
}