diff options
author | Sebastian <sebasjm@gmail.com> | 2023-09-25 12:18:59 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2023-09-25 14:50:46 -0300 |
commit | 820f953b96b2b2852c32dc16a2fa920c6c717788 (patch) | |
tree | 584cbc88f22e4900d5e1a87156f7a05a4ecfc636 | |
parent | 4041a76a58503572c6fe8edc87658afc946a11e0 (diff) |
check config number
-rw-r--r-- | packages/demobank-ui/src/components/Routing.tsx | 31 | ||||
-rw-r--r-- | packages/demobank-ui/src/components/app.tsx | 47 | ||||
-rw-r--r-- | packages/demobank-ui/src/hooks/config.ts | 51 | ||||
-rw-r--r-- | packages/demobank-ui/src/pages/LoginForm.tsx | 232 | ||||
-rw-r--r-- | packages/demobank-ui/src/utils.ts | 22 |
5 files changed, 214 insertions, 169 deletions
diff --git a/packages/demobank-ui/src/components/Routing.tsx b/packages/demobank-ui/src/components/Routing.tsx index 05af1719b..aafc95687 100644 --- a/packages/demobank-ui/src/components/Routing.tsx +++ b/packages/demobank-ui/src/components/Routing.tsx @@ -14,36 +14,43 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ +import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { createHashHistory } from "history"; -import { VNode, h } from "preact"; +import { Fragment, VNode, h } from "preact"; import { Route, Router, route } from "preact-router"; -import { useEffect, useErrorBoundary } from "preact/hooks"; +import { useEffect } from "preact/hooks"; +import { useBackendContext } from "../context/backend.js"; import { BankFrame } from "../pages/BankFrame.js"; -import { BusinessAccount } from "../pages/business/Home.js"; import { HomePage, WithdrawalOperationPage } from "../pages/HomePage.js"; +import { LoginForm } from "../pages/LoginForm.js"; import { PublicHistoriesPage } from "../pages/PublicHistoriesPage.js"; import { RegistrationPage } from "../pages/RegistrationPage.js"; -import { useBackendContext } from "../context/backend.js"; -import { LoginForm } from "../pages/LoginForm.js"; import { AdminHome } from "../pages/admin/Home.js"; +import { BusinessAccount } from "../pages/business/Home.js"; import { bankUiSettings } from "../settings.js"; -import { notifyError } from "@gnu-taler/web-util/browser"; export function Routing(): VNode { const history = createHashHistory(); const backend = useBackendContext(); - + const {i18n} = useTranslationContext(); + if (backend.state.status === "loggedOut") { return <BankFrame > <Router history={history}> <Route path="/login" component={() => ( - <LoginForm - onRegister={() => { - route("/register"); - }} - /> + <Fragment> + <div class="sm:mx-auto sm:w-full sm:max-w-sm"> + <h2 class="text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{i18n.str`Welcome to ${bankUiSettings.bankName}!`}</h2> + </div> + + <LoginForm + onRegister={() => { + route("/register"); + }} + /> + </Fragment> )} /> <Route diff --git a/packages/demobank-ui/src/components/app.tsx b/packages/demobank-ui/src/components/app.tsx index 22752ab78..ebda31035 100644 --- a/packages/demobank-ui/src/components/app.tsx +++ b/packages/demobank-ui/src/components/app.tsx @@ -15,15 +15,20 @@ */ import { + LibtoolVersion, getGlobalLogLevel, setGlobalLogLevelFromString, } from "@gnu-taler/taler-util"; -import { TranslationProvider } from "@gnu-taler/web-util/browser"; -import { FunctionalComponent, h } from "preact"; +import { TranslationProvider, useApiContext } from "@gnu-taler/web-util/browser"; +import { ComponentChildren, Fragment, FunctionalComponent, VNode, h } from "preact"; import { SWRConfig } from "swr"; -import { BackendStateProvider } from "../context/backend.js"; +import { BackendStateProvider, useBackendContext } from "../context/backend.js"; import { strings } from "../i18n/strings.js"; import { Routing } from "./Routing.js"; +import { useEffect, useState } from "preact/hooks"; +import { Loading } from "./Loading.js"; +import { getInitialBackendBaseURL } from "../hooks/backend.js"; +import { BANK_INTEGRATION_PROTOCOL_VERSION, useConfigState } from "../hooks/config.js"; const WITH_LOCAL_STORAGE_CACHE = false; /** @@ -47,22 +52,38 @@ const App: FunctionalComponent = () => { return ( <TranslationProvider source={strings}> <BackendStateProvider> - <SWRConfig - value={{ - provider: WITH_LOCAL_STORAGE_CACHE - ? localStorageProvider - : undefined, - }} - > - <Routing /> - </SWRConfig> + <VersionCheck> + <SWRConfig + value={{ + provider: WITH_LOCAL_STORAGE_CACHE + ? localStorageProvider + : undefined, + }} + > + <Routing /> + </SWRConfig> + </VersionCheck> </BackendStateProvider> - </TranslationProvider> + </TranslationProvider > ); }; (window as any).setGlobalLogLevelFromString = setGlobalLogLevelFromString; (window as any).getGlobalLevel = getGlobalLogLevel; +function VersionCheck({ children }: { children: ComponentChildren }): VNode { + const checked = useConfigState() + + if (checked === undefined) { + return <Loading /> + } + if (checked === false) { + return <div> + the bank backend is not supported. supported version "{BANK_INTEGRATION_PROTOCOL_VERSION}" + </div> + } + return <Fragment>{children}</Fragment> +} + function localStorageProvider(): Map<unknown, unknown> { const map = new Map(JSON.parse(localStorage.getItem("app-cache") || "[]")); diff --git a/packages/demobank-ui/src/hooks/config.ts b/packages/demobank-ui/src/hooks/config.ts new file mode 100644 index 000000000..4b22e8ad3 --- /dev/null +++ b/packages/demobank-ui/src/hooks/config.ts @@ -0,0 +1,51 @@ +import { LibtoolVersion } from "@gnu-taler/taler-util"; +import { useApiContext } from "@gnu-taler/web-util/browser"; +import { useEffect, useState } from "preact/hooks"; +import { getInitialBackendBaseURL } from "./backend.js"; + +/** + * Protocol version spoken with the bank. + * + * Uses libtool's current:revision:age versioning. + */ +export const BANK_INTEGRATION_PROTOCOL_VERSION = "0:0:0"; + +async function getConfigState( + request: ReturnType<typeof useApiContext>["request"], +): Promise<SandboxBackend.Config | undefined> { + try { + const url = getInitialBackendBaseURL(); + const result = await request<SandboxBackend.Config>( + url, + `config`, + ); + return result.data; + } catch (error) { + return undefined; + } +} + +export function useConfigState(): boolean | undefined { + const [checked, setChecked] = useState<boolean>() + const { request } = useApiContext(); + + useEffect(() => { + + getConfigState(request) + .then((result) => { + if (!result) { + setChecked(false) + } else { + const r = LibtoolVersion.compare(BANK_INTEGRATION_PROTOCOL_VERSION, result.version) + setChecked(r?.compatible); + } + }) + .catch((error) => { + setChecked(false); + }); + }); + + return checked; +} + + diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx b/packages/demobank-ui/src/pages/LoginForm.tsx index f579678f2..0fbbef7c3 100644 --- a/packages/demobank-ui/src/pages/LoginForm.tsx +++ b/packages/demobank-ui/src/pages/LoginForm.tsx @@ -128,132 +128,120 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode { const isSessionExpired = !onRegister return ( - <Fragment> - <h1 class="nav"></h1> - {/* {error && ( - <ErrorBannerFloat error={error} onClear={() => saveError(undefined)} /> - )} */} - - <div class="flex min-h-full flex-col justify-center"> - {/* <div class="sm:mx-auto sm:w-full sm:max-w-sm"> - <h2 class="text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{i18n.str`Welcome to ${bankUiSettings.bankName}!`}</h2> - </div> */} - - <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm"> - <form class="space-y-6" noValidate - onSubmit={(e) => { - e.preventDefault(); - }} - autoCapitalize="none" - autoCorrect="off" - > - <div> - <label for="username" class="block text-sm font-medium leading-6 text-gray-900"> - <i18n.Translate>Username</i18n.Translate> - </label> - <div class="mt-2"> - <input - ref={ref} - autoFocus - type="text" - name="username" - id="username" - class="block w-full disabled:bg-gray-200 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" - value={username ?? ""} - disabled={isSessionExpired} - enterkeyhint="next" - placeholder="identification" - autocomplete="username" - required - onInput={(e): void => { - setUsername(e.currentTarget.value); - }} - /> - <ShowInputErrorLabel - message={errors?.username} - isDirty={username !== undefined} - /> - </div> + <div class="flex min-h-full flex-col justify-center"> + + <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm"> + <form class="space-y-6" noValidate + onSubmit={(e) => { + e.preventDefault(); + }} + autoCapitalize="none" + autoCorrect="off" + > + <div> + <label for="username" class="block text-sm font-medium leading-6 text-gray-900"> + <i18n.Translate>Username</i18n.Translate> + </label> + <div class="mt-2"> + <input + ref={ref} + autoFocus + type="text" + name="username" + id="username" + class="block w-full disabled:bg-gray-200 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + value={username ?? ""} + disabled={isSessionExpired} + enterkeyhint="next" + placeholder="identification" + autocomplete="username" + required + onInput={(e): void => { + setUsername(e.currentTarget.value); + }} + /> + <ShowInputErrorLabel + message={errors?.username} + isDirty={username !== undefined} + /> </div> + </div> - <div> - <div class="flex items-center justify-between"> - <label for="password" class="block text-sm font-medium leading-6 text-gray-900">Password</label> - </div> - <div class="mt-2"> - <input - type="password" - name="password" - id="password" - autocomplete="current-password" - class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" - enterkeyhint="send" - value={password ?? ""} - placeholder="Password" - required - onInput={(e): void => { - setPassword(e.currentTarget.value); - }} - /> - <ShowInputErrorLabel - message={errors?.password} - isDirty={password !== undefined} - /> - </div> + <div> + <div class="flex items-center justify-between"> + <label for="password" class="block text-sm font-medium leading-6 text-gray-900">Password</label> </div> - - {isSessionExpired ? <div class="flex justify-between"> - <button type="submit" - class="rounded-md bg-white-600 px-3 py-1.5 text-sm font-semibold leading-6 text-black shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600" - onClick={(e) => { - e.preventDefault() - doLogin() - }} - > - <i18n.Translate>Cancel</i18n.Translate> - </button> - - <button type="submit" - class="rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" - disabled={!!errors} - onClick={(e) => { - e.preventDefault() - doLogin() - }} - > - <i18n.Translate>Renew session</i18n.Translate> - </button> - </div> : <div> - <button type="submit" - class="flex w-full justify-center rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" - disabled={!!errors} - onClick={(e) => { - e.preventDefault() - doLogin() + <div class="mt-2"> + <input + type="password" + name="password" + id="password" + autocomplete="current-password" + class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + enterkeyhint="send" + value={password ?? ""} + placeholder="Password" + required + onInput={(e): void => { + setPassword(e.currentTarget.value); }} - > - <i18n.Translate>Log in</i18n.Translate> - </button> - </div>} - </form> - - {bankUiSettings.allowRegistrations && onRegister && - <p class="mt-10 text-center text-sm text-gray-500 border-t"> - <button type="submit" - class="flex mt-4 rounded-md bg-blue-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600" - onClick={(e) => { - e.preventDefault() - onRegister() - }} - > - <i18n.Translate>Register</i18n.Translate> - </button> - </p> - } - </div> + /> + <ShowInputErrorLabel + message={errors?.password} + isDirty={password !== undefined} + /> + </div> + </div> + + {isSessionExpired ? <div class="flex justify-between"> + <button type="submit" + class="rounded-md bg-white-600 px-3 py-1.5 text-sm font-semibold leading-6 text-black shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600" + onClick={(e) => { + e.preventDefault() + doLogin() + }} + > + <i18n.Translate>Cancel</i18n.Translate> + </button> + + <button type="submit" + class="rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" + disabled={!!errors} + onClick={(e) => { + e.preventDefault() + doLogin() + }} + > + <i18n.Translate>Renew session</i18n.Translate> + </button> + </div> : <div> + <button type="submit" + class="flex w-full justify-center rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" + disabled={!!errors} + onClick={(e) => { + e.preventDefault() + doLogin() + }} + > + <i18n.Translate>Log in</i18n.Translate> + </button> + </div>} + </form> + + {bankUiSettings.allowRegistrations && onRegister && + <p class="mt-10 text-center text-sm text-gray-500 border-t"> + <button type="submit" + class="flex mt-4 rounded-md bg-blue-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600" + onClick={(e) => { + e.preventDefault() + onRegister() + }} + > + <i18n.Translate>Register</i18n.Translate> + </button> + </p> + } </div> - - - </Fragment> + </div> ); } diff --git a/packages/demobank-ui/src/utils.ts b/packages/demobank-ui/src/utils.ts index c13b9a3cb..e7673f078 100644 --- a/packages/demobank-ui/src/utils.ts +++ b/packages/demobank-ui/src/utils.ts @@ -88,28 +88,6 @@ export enum CashoutStatus { PENDING = "pending", } -// export function partialWithObjects<T extends object>(obj: T | undefined, () => complete): WithIntermediate<T> { -// const root = obj === undefined ? {} : obj; -// return Object.entries(root).([key, value]) => { - -// }) -// return undefined as any -// } - -/** - * Craft headers with Authorization and Content-Type. - */ -// export function prepareHeaders(username?: string, password?: string): Headers { -// const headers = new Headers(); -// if (username && password) { -// headers.append( -// "Authorization", -// `Basic ${window.btoa(`${username}:${password}`)}`, -// ); -// } -// headers.append("Content-Type", "application/json"); -// return headers; -// } export const PAGE_SIZE = 20; export const MAX_RESULT_SIZE = PAGE_SIZE * 2 - 1; |