/* This file is part of GNU Taler (C) 2022-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 Foundation; either version 3, or (at your option) any later version. GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ import { CacheEvictor, LibtoolVersion, ObservabilityEvent, ObservableHttpClientLibrary, TalerAuthenticationHttpClient, TalerBankConversionCacheEviction, TalerBankConversionHttpClient, TalerCoreBankCacheEviction, TalerCoreBankHttpClient, TalerCorebankApi, TalerError } from "@gnu-taler/taler-util"; import { ComponentChildren, FunctionComponent, VNode, createContext, h, } from "preact"; import { useContext, useEffect, useState } from "preact/hooks"; import { APIClient, ActiviyTracker, BankLib, Suscriber } from "./activity.js"; import { useTranslationContext } from "./translation.js"; import { BrowserFetchHttpLib, ErrorLoading } from "../index.browser.js"; /** * * @author Sebastian Javier Marchano (sebasjm) */ export type BankContextType = { url: URL; config: TalerCorebankApi.Config; lib: BankLib; hints: VersionHint[]; onActivity: Suscriber; cancelRequest: (eventId: string) => void; }; // @ts-expect-error default value to undefined, should it be another thing? const BankContext = createContext(undefined); export const useBankCoreApiContext = (): BankContextType => useContext(BankContext); enum VersionHint { NONE, } type Evictors = { conversion?: CacheEvictor; bank?: CacheEvictor; } type ConfigResult = | undefined | { type: "ok"; config: T; hints: VersionHint[] } | { type: "incompatible"; result: T; supported: string } | { type: "error"; error: TalerError }; export const BankApiProvider = ({ baseUrl, children, frameOnError, evictors = {}, }: { baseUrl: URL; children: ComponentChildren; evictors?: Evictors, frameOnError: FunctionComponent<{ children: ComponentChildren }>; }): VNode => { const [checked, setChecked] = useState>(); const { i18n } = useTranslationContext(); const { getRemoteConfig, VERSION, lib, cancelRequest, onActivity } = buildBankApiClient(baseUrl, evictors); useEffect(() => { getRemoteConfig() .then((config) => { if (LibtoolVersion.compare(VERSION, config.version)) { setChecked({ type: "ok", config, hints: [] }); } else { setChecked({ type: "incompatible", result: config, supported: VERSION, }); } }) .catch((error: unknown) => { if (error instanceof TalerError) { setChecked({ type: "error", error }); } }); }, []); if (checked === undefined) { return h(frameOnError, { children: h("div", {}, "checking compatibility with server...") }); } if (checked.type === "error") { return h(frameOnError, { children: h(ErrorLoading, { error: checked.error, showDetail: true }), }); } if (checked.type === "incompatible") { return h(frameOnError, { children: h( "div", {}, i18n.str`The server version is not supported. Supported version "${checked.supported}", server version "${checked.result.version}"`, ), }); } const value: BankContextType = { url: baseUrl, config: checked.config, onActivity: onActivity, lib, cancelRequest, hints: checked.hints, }; return h(BankContext.Provider, { value, children, }); }; function buildBankApiClient(url: URL, evictors: Evictors, ): APIClient { const httpFetch = new BrowserFetchHttpLib({ enableThrottling: true, requireTls: false, }); const tracker = new ActiviyTracker(); const httpLib = new ObservableHttpClientLibrary(httpFetch, { observe(ev) { tracker.notify(ev); }, }); const bank = new TalerCoreBankHttpClient( url.href, httpLib, evictors.bank, ); const conversion = new TalerBankConversionHttpClient( bank.getConversionInfoAPI().href, httpLib, evictors.conversion, ); const auth = (user: string) => new TalerAuthenticationHttpClient( bank.getAuthenticationAPI(user).href, httpLib, ); async function getRemoteConfig() { const resp = await bank.getConfig() return resp.body } return { getRemoteConfig, VERSION: bank.PROTOCOL_VERSION, lib: { bank, conversion, auth }, onActivity: tracker.subscribe, cancelRequest: httpLib.cancelRequest, }; } export const BankApiProviderTesting = ({ children, value, }: { value: BankContextType children: ComponentChildren; }): VNode => { return h(BankContext.Provider, { value, children, }); }