From b58d53dd93bd8e97aecc28fae788c5c7051fd73d Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 5 Nov 2023 18:04:22 -0300 Subject: sharing components in web-util --- packages/aml-backoffice-ui/src/App.tsx | 11 +- packages/aml-backoffice-ui/src/Dashboard.tsx | 26 +++-- packages/aml-backoffice-ui/src/account.ts | 128 --------------------- packages/aml-backoffice-ui/src/context/config.ts | 89 ++++++++++++++ .../aml-backoffice-ui/src/handlers/Caption.tsx | 7 +- packages/aml-backoffice-ui/src/hooks/useBackend.ts | 27 +++-- .../aml-backoffice-ui/src/hooks/useCaseDetails.ts | 103 ++++------------- packages/aml-backoffice-ui/src/hooks/useCases.ts | 126 +++++++++----------- packages/aml-backoffice-ui/src/hooks/useOfficer.ts | 20 ++-- .../aml-backoffice-ui/src/pages/CaseDetails.tsx | 83 ++++++------- packages/aml-backoffice-ui/src/pages/Cases.tsx | 46 ++++---- .../aml-backoffice-ui/src/pages/NewFormEntry.tsx | 19 ++- packages/aml-backoffice-ui/src/pages/Officer.tsx | 4 +- .../aml-backoffice-ui/src/pages/UnlockAccount.tsx | 3 +- packages/aml-backoffice-ui/src/utils/errors.tsx | 77 ------------- packages/demobank-ui/src/assets/lang.svg | 48 -------- packages/demobank-ui/src/components/Attention.tsx | 59 ---------- .../demobank-ui/src/components/Cashouts/index.ts | 2 +- .../demobank-ui/src/components/Cashouts/views.tsx | 5 +- packages/demobank-ui/src/components/CopyButton.tsx | 48 -------- .../src/components/EmptyComponentExample/index.ts | 5 +- .../demobank-ui/src/components/ErrorLoading.tsx | 120 ------------------- .../demobank-ui/src/components/LangSelector.tsx | 111 ------------------ packages/demobank-ui/src/components/Loading.tsx | 45 -------- packages/demobank-ui/src/components/Routing.tsx | 4 - .../src/components/ShowInputErrorLabel.tsx | 29 ----- .../src/components/ShowLocalNotification.tsx | 43 ------- .../src/components/Transactions/index.ts | 5 +- packages/demobank-ui/src/components/app.tsx | 1 + packages/demobank-ui/src/context/config.ts | 2 +- .../demobank-ui/src/pages/AccountPage/index.ts | 4 +- .../demobank-ui/src/pages/AccountPage/views.tsx | 2 +- packages/demobank-ui/src/pages/BankFrame.tsx | 10 +- packages/demobank-ui/src/pages/LoginForm.tsx | 6 +- .../demobank-ui/src/pages/OperationState/index.ts | 4 +- .../demobank-ui/src/pages/OperationState/views.tsx | 8 +- .../src/pages/PaytoWireTransferForm.tsx | 4 +- .../demobank-ui/src/pages/PublicHistoriesPage.tsx | 2 +- packages/demobank-ui/src/pages/QrCodeSection.tsx | 2 +- .../demobank-ui/src/pages/RegistrationPage.tsx | 4 +- .../demobank-ui/src/pages/ShowAccountDetails.tsx | 6 +- .../src/pages/UpdateAccountPassword.tsx | 8 +- .../demobank-ui/src/pages/WalletWithdrawForm.tsx | 4 +- .../src/pages/WithdrawalConfirmationQuestion.tsx | 4 +- .../src/pages/WithdrawalOperationPage.tsx | 2 +- .../demobank-ui/src/pages/WithdrawalQRCode.tsx | 6 +- packages/demobank-ui/src/pages/admin/Account.tsx | 4 +- .../demobank-ui/src/pages/admin/AccountForm.tsx | 4 +- .../demobank-ui/src/pages/admin/AccountList.tsx | 4 +- packages/demobank-ui/src/pages/admin/AdminHome.tsx | 2 +- .../src/pages/admin/CreateNewAccount.tsx | 4 +- .../demobank-ui/src/pages/admin/RemoveAccount.tsx | 10 +- .../src/pages/business/CreateCashout.tsx | 10 +- .../src/pages/business/ShowCashoutDetails.tsx | 10 +- packages/demobank-ui/tailwind.config.js | 8 +- packages/web-util/src/assets/lang.svg | 48 ++++++++ packages/web-util/src/components/Attention.tsx | 58 ++++++++++ packages/web-util/src/components/CopyButton.tsx | 46 ++++++++ packages/web-util/src/components/ErrorLoading.tsx | 119 +++++++++++++++++++ packages/web-util/src/components/LangSelector.tsx | 111 ++++++++++++++++++ packages/web-util/src/components/Loading.tsx | 45 ++++++++ .../src/components/ShowInputErrorLabel.tsx | 29 +++++ .../src/components/ShowLocalNotification.tsx | 43 +++++++ packages/web-util/src/components/index.ts | 7 ++ packages/web-util/src/declaration.d.ts | 35 ++++++ 65 files changed, 912 insertions(+), 1057 deletions(-) delete mode 100644 packages/aml-backoffice-ui/src/account.ts create mode 100644 packages/aml-backoffice-ui/src/context/config.ts delete mode 100644 packages/aml-backoffice-ui/src/utils/errors.tsx delete mode 100644 packages/demobank-ui/src/assets/lang.svg delete mode 100644 packages/demobank-ui/src/components/Attention.tsx delete mode 100644 packages/demobank-ui/src/components/CopyButton.tsx delete mode 100644 packages/demobank-ui/src/components/ErrorLoading.tsx delete mode 100644 packages/demobank-ui/src/components/LangSelector.tsx delete mode 100644 packages/demobank-ui/src/components/Loading.tsx delete mode 100644 packages/demobank-ui/src/components/ShowInputErrorLabel.tsx delete mode 100644 packages/demobank-ui/src/components/ShowLocalNotification.tsx create mode 100644 packages/web-util/src/assets/lang.svg create mode 100644 packages/web-util/src/components/Attention.tsx create mode 100644 packages/web-util/src/components/CopyButton.tsx create mode 100644 packages/web-util/src/components/ErrorLoading.tsx create mode 100644 packages/web-util/src/components/LangSelector.tsx create mode 100644 packages/web-util/src/components/Loading.tsx create mode 100644 packages/web-util/src/components/ShowInputErrorLabel.tsx create mode 100644 packages/web-util/src/components/ShowLocalNotification.tsx create mode 100644 packages/web-util/src/declaration.d.ts (limited to 'packages') diff --git a/packages/aml-backoffice-ui/src/App.tsx b/packages/aml-backoffice-ui/src/App.tsx index 600131219..0e29279ff 100644 --- a/packages/aml-backoffice-ui/src/App.tsx +++ b/packages/aml-backoffice-ui/src/App.tsx @@ -1,12 +1,19 @@ import { TranslationProvider } from "@gnu-taler/web-util/browser"; import { h, VNode } from "preact"; -import { Dashboard } from "./Dashboard.js"; +import { ExchangeAmlFrame, Main } from "./Dashboard.js"; import "./scss/main.css"; +import { ExchangeApiProvider } from "./context/config.js"; +import { getInitialBackendBaseURL } from "./hooks/useBackend.js"; export function App(): VNode { + const baseUrl = getInitialBackendBaseURL(); return ( - + + +
+ + ); } diff --git a/packages/aml-backoffice-ui/src/Dashboard.tsx b/packages/aml-backoffice-ui/src/Dashboard.tsx index 6794ca1f8..bd8a48c45 100644 --- a/packages/aml-backoffice-ui/src/Dashboard.tsx +++ b/packages/aml-backoffice-ui/src/Dashboard.tsx @@ -182,7 +182,7 @@ function LeftMenu() { ); } -export function Dashboard({ +export function ExchangeAmlFrame({ children, }: { children?: ComponentChildren; @@ -211,21 +211,25 @@ export function Dashboard({ }} /> -
-
- { - return
not found
; - }} - /> -
-
+ {children} ); } +export function Main(): VNode { + return
+
+ { + return
not found
; + }} + /> +
+
+} + const pageList = Object.values(Pages); function NavigationBar({ diff --git a/packages/aml-backoffice-ui/src/account.ts b/packages/aml-backoffice-ui/src/account.ts deleted file mode 100644 index 615d843c4..000000000 --- a/packages/aml-backoffice-ui/src/account.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { - Amounts, - TalerSignaturePurpose, - amountToBuffer, - bufferForUint32, - buildSigPS, - createEddsaKeyPair, - decodeCrock, - decryptWithDerivedKey, - eddsaGetPublic, - eddsaSign, - encodeCrock, - encryptWithDerivedKey, - getRandomBytesF, - hash, - hashTruncate32, - stringToBytes, - timestampRoundedToBuffer -} from "@gnu-taler/taler-util"; -import { AmlExchangeBackend } from "./types.js"; - -export interface Account { - accountId: AccountId; - signingKey: SigningKey; -} - -/** - * Restore previous session and unlock account with password - * - * @param salt string from which crypto params will be derived - * @param key secured private key - * @param password password for the private key - * @returns - */ -export async function unlockAccount( - account: LockedAccount, - password: string, -): Promise { - const rawKey = decodeCrock(account); - const rawPassword = stringToBytes(password); - - const signingKey = (await decryptWithDerivedKey( - rawKey, - rawPassword, - password, - ).catch((e: Error) => { - throw new UnwrapKeyError(e.message); - })) as SigningKey; - - const publicKey = eddsaGetPublic(signingKey); - - const accountId = encodeCrock(publicKey) as AccountId; - - return { accountId, signingKey }; -} - -export function buildQuerySignature(key: SigningKey): string { - const sigBlob = buildSigPS( - TalerSignaturePurpose.TALER_SIGNATURE_AML_QUERY, - ).build(); - - return encodeCrock(eddsaSign(sigBlob, key)); -} - -export function buildDecisionSignature( - key: SigningKey, - decision: AmlExchangeBackend.AmlDecision, -): string { - const zero = new Uint8Array(new ArrayBuffer(64)) - - const sigBlob = buildSigPS(TalerSignaturePurpose.TALER_SIGNATURE_AML_DECISION) - //TODO: new need the null terminator, also in the exchange - .put(hash(stringToBytes(decision.justification)))//check null - .put(timestampRoundedToBuffer(decision.decision_time)) - .put(amountToBuffer(decision.new_threshold)) - .put(decodeCrock(decision.h_payto)) - .put(zero) //kyc_requirement - .put(bufferForUint32(decision.new_state)) - .build(); - - return encodeCrock(eddsaSign(sigBlob, key)); -} - -declare const opaque_Account: unique symbol; -export type LockedAccount = string & { [opaque_Account]: true }; - -declare const opaque_AccountId: unique symbol; -export type AccountId = string & { [opaque_AccountId]: true }; - -declare const opaque_SigningKey: unique symbol; -export type SigningKey = Uint8Array & { [opaque_SigningKey]: true }; - -/** - * Create new account (secured private key) - * secured with the given password - * - * @param sessionId - * @param password - * @returns - */ -export async function createNewAccount( - password: string, -): Promise { - const { eddsaPriv, eddsaPub } = createEddsaKeyPair(); - - const key = stringToBytes(password); - - const protectedPrivKey = await encryptWithDerivedKey( - getRandomBytesF(24), - key, - eddsaPriv, - password, - ); - - const signingKey = eddsaPriv as SigningKey; - const accountId = encodeCrock(eddsaPub) as AccountId; - const safe = encodeCrock(protectedPrivKey) as LockedAccount; - - return { accountId, signingKey, safe }; -} - -export class UnwrapKeyError extends Error { - public cause: string; - constructor(cause: string) { - super(`Recovering private key failed on: ${cause}`); - this.cause = cause; - } -} diff --git a/packages/aml-backoffice-ui/src/context/config.ts b/packages/aml-backoffice-ui/src/context/config.ts new file mode 100644 index 000000000..2866717de --- /dev/null +++ b/packages/aml-backoffice-ui/src/context/config.ts @@ -0,0 +1,89 @@ +/* + This file is part of GNU Taler + (C) 2022 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 { TalerExchangeApi, TalerExchangeHttpClient, TalerError } from "@gnu-taler/taler-util"; +import { BrowserHttpLib, useTranslationContext } from "@gnu-taler/web-util/browser"; +import { ComponentChildren, createContext, FunctionComponent, h, VNode } from "preact"; +import { useContext, useEffect, useState } from "preact/hooks"; +import { ErrorLoading } from "@gnu-taler/web-util/browser"; + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +export type Type = { + url: URL, + config: TalerExchangeApi.ExchangeVersionResponse, + api: TalerExchangeHttpClient, +}; + +const Context = createContext(undefined as any); + +export const useExchangeApiContext = (): Type => useContext(Context); + +export type ConfigResult = undefined + | { type: "ok", config: TalerExchangeApi.ExchangeVersionResponse } + | { type: "incompatible", result: TalerExchangeApi.ExchangeVersionResponse, supported: string } + | { type: "error", error: TalerError } + +export const ExchangeApiProvider = ({ + baseUrl, + children, + frameOnError, +}: { + baseUrl: string, + children: ComponentChildren; + frameOnError: FunctionComponent<{ children: ComponentChildren }>, +}): VNode => { + const [checked, setChecked] = useState() + const { i18n } = useTranslationContext(); + const url = new URL(baseUrl) + const api = new TalerExchangeHttpClient(url.href, new BrowserHttpLib()) + useEffect(() => { + api.getConfig() + .then((resp) => { + if (api.isCompatible(resp.body.version)) { + setChecked({ type: "ok", config: resp.body }); + } else { + setChecked({ type: "incompatible", result: resp.body, supported: api.PROTOCOL_VERSION }) + } + }) + .catch((error: unknown) => { + if (error instanceof TalerError) { + setChecked({ type: "error", error }); + } + }); + }, []); + + if (checked === undefined) { + return h(frameOnError, { children: h("div", {}, "loading...") }) + } + 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 bank backend is not supported. supported version "${checked.supported}", server version "${checked.result.version}"`) }) + } + const value: Type = { + url, config: checked.config, api + } + return h(Context.Provider, { + value, + children, + }); +}; + diff --git a/packages/aml-backoffice-ui/src/handlers/Caption.tsx b/packages/aml-backoffice-ui/src/handlers/Caption.tsx index fbf154d89..8facddec3 100644 --- a/packages/aml-backoffice-ui/src/handlers/Caption.tsx +++ b/packages/aml-backoffice-ui/src/handlers/Caption.tsx @@ -1,11 +1,8 @@ +import { TranslatedString } from "@gnu-taler/taler-util"; import { VNode, h } from "preact"; import { - IconAddon, - InputLine, - LabelWithTooltipMaybeRequired, - UIFormProps, + LabelWithTooltipMaybeRequired } from "./InputLine.js"; -import { TranslatedString } from "@gnu-taler/taler-util"; interface Props { label: TranslatedString; diff --git a/packages/aml-backoffice-ui/src/hooks/useBackend.ts b/packages/aml-backoffice-ui/src/hooks/useBackend.ts index b9d66fca6..95277a915 100644 --- a/packages/aml-backoffice-ui/src/hooks/useBackend.ts +++ b/packages/aml-backoffice-ui/src/hooks/useBackend.ts @@ -1,3 +1,4 @@ +import { canonicalizeBaseUrl } from "@gnu-taler/taler-util"; import { HttpResponseOk, RequestOptions, @@ -5,9 +6,6 @@ import { } from "@gnu-taler/web-util/browser"; import { useCallback } from "preact/hooks"; import { uiSettings } from "../settings.js"; -import { canonicalizeBaseUrl } from "@gnu-taler/taler-util"; -import { useOfficer } from "./useOfficer.js"; -import { buildQuerySignature } from "../account.js"; interface useBackendType { request: ( @@ -35,7 +33,7 @@ export function usePublicBackend(): useBackendType { ); const fetcher = useCallback( - function fetcherImpl([endpoint, talerAmlOfficerSignature]: [string,string]): Promise> { + function fetcherImpl([endpoint, talerAmlOfficerSignature]: [string, string]): Promise> { return requestHandler(baseUrl, endpoint, { talerAmlOfficerSignature }); @@ -66,18 +64,29 @@ export function usePublicBackend(): useBackendType { export function getInitialBackendBaseURL(): string { const overrideUrl = typeof localStorage !== "undefined" - ? localStorage.getItem("exchange-aml-base-url") + ? localStorage.getItem("exchange-base-url") : undefined; + + let result: string; + if (!overrideUrl) { //normal path if (!uiSettings.backendBaseURL) { console.error( "ERROR: backendBaseURL was overridden by a setting file and missing. Setting value to 'window.origin'", ); - return canonicalizeBaseUrl(window.origin); + result = window.origin + } else { + result = uiSettings.backendBaseURL; } - return canonicalizeBaseUrl(uiSettings.backendBaseURL); + } else { + // testing/development path + result = overrideUrl + } + try { + return canonicalizeBaseUrl(result) + } catch (e) { + //fall back + return canonicalizeBaseUrl(window.origin) } - // testing/development path - return canonicalizeBaseUrl(overrideUrl); } diff --git a/packages/aml-backoffice-ui/src/hooks/useCaseDetails.ts b/packages/aml-backoffice-ui/src/hooks/useCaseDetails.ts index 980a35f21..9db1e2aec 100644 --- a/packages/aml-backoffice-ui/src/hooks/useCaseDetails.ts +++ b/packages/aml-backoffice-ui/src/hooks/useCaseDetails.ts @@ -1,34 +1,29 @@ import { HttpResponse, - HttpResponseOk, - RequestError + HttpResponseOk } from "@gnu-taler/web-util/browser"; import { AmlExchangeBackend } from "../types.js"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 +import { AmountString, OfficerAccount, PaytoString, TalerExchangeApi, TalerExchangeResultByMethod, TalerHttpError } from "@gnu-taler/taler-util"; import _useSWR, { SWRHook, useSWRConfig } from "swr"; -import { AccountId } from "../account.js"; +import { useExchangeApiContext } from "../context/config.js"; import { usePublicBackend } from "./useBackend.js"; +import { useOfficer } from "./useOfficer.js"; const useSWR = _useSWR as unknown as SWRHook; -export function useCaseDetails( - account: AccountId, - paytoHash: string, - signature: string | undefined, -): HttpResponse< - AmlExchangeBackend.AmlDecisionDetails, - AmlExchangeBackend.AmlError -> { - const { fetcher } = usePublicBackend(); +export function useCaseDetails(paytoHash: string) { + const officer = useOfficer(); + const session = officer.state === "ready" ? officer.account : undefined; - const { data, error } = useSWR< - HttpResponseOk, - RequestError ->( [ - `aml/${account}/decision/${(paytoHash)}`, - signature, -], -fetcher, { + const { api } = useExchangeApiContext(); + + async function fetcher([officer, account]: [OfficerAccount, PaytoString]) { + return await api.getDecisionDetails(officer, account) + } + + const { data, error } = useSWR, TalerHttpError>( + !session ? undefined : [session, paytoHash], fetcher, { refreshInterval: 0, refreshWhenHidden: false, revalidateOnFocus: false, @@ -41,11 +36,11 @@ fetcher, { }); if (data) return data; - if (error) return error.cause; - return { loading: true }; + if (error) return error; + return undefined; } -const example1: AmlExchangeBackend.AmlDecisionDetails = { +const example1: TalerExchangeApi.AmlDecisionDetails = { aml_history: [ { justification: "Lack of documentation", @@ -54,7 +49,7 @@ const example1: AmlExchangeBackend.AmlDecisionDetails = { t_s: Date.now() / 1000, }, new_state: 2, - new_threshold: "USD:0", + new_threshold: "USD:0" as AmountString, }, { justification: "Doing a transfer of high amount", @@ -63,7 +58,7 @@ const example1: AmlExchangeBackend.AmlDecisionDetails = { t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 6, }, new_state: 1, - new_threshold: "USD:2000", + new_threshold: "USD:2000" as AmountString, }, { justification: "Account is known to the system", @@ -72,7 +67,7 @@ const example1: AmlExchangeBackend.AmlDecisionDetails = { t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 9, }, new_state: 0, - new_threshold: "USD:100", + new_threshold: "USD:100" as AmountString, }, ], kyc_attributes: [ @@ -103,60 +98,4 @@ const example1: AmlExchangeBackend.AmlDecisionDetails = { ], }; -export const exampleResponse: HttpResponse = { - ok: true, - data: example1, -} - - -export function useAmlCasesAPI(): AmlCaseAPI { - const { request } = usePublicBackend(); - const mutateAll = useMatchMutate(); - - const updateDecision = async ( - officer: AccountId, - data: AmlExchangeBackend.AmlDecision, - ): Promise> => { - const res = await request(`aml/${officer}/decision`, { - method: "POST", - data, - contentType: "json", - }); - await mutateAll(/.*aml.*/); - return res; - }; - - return { - updateDecision, - }; -} - -export interface AmlCaseAPI { - updateDecision: ( - officer: AccountId, - data: AmlExchangeBackend.AmlDecision, - ) => Promise>; -} - -function useMatchMutate(): ( - re: RegExp, - value?: unknown, -) => Promise { - const { cache, mutate } = useSWRConfig(); - - if (!(cache instanceof Map)) { - throw new Error( - "matchMutate requires the cache provider to be a Map instance", - ); - } - - return function matchRegexMutate(re: RegExp, value?: unknown) { - const allKeys = Array.from(cache.keys()); - const keys = allKeys.filter((key) => re.test(key)); - const mutations = keys.map((key) => { - return mutate(key, value, true); - }); - return Promise.all(mutations); - }; -} diff --git a/packages/aml-backoffice-ui/src/hooks/useCases.ts b/packages/aml-backoffice-ui/src/hooks/useCases.ts index c07bd5f18..2a133f46d 100644 --- a/packages/aml-backoffice-ui/src/hooks/useCases.ts +++ b/packages/aml-backoffice-ui/src/hooks/useCases.ts @@ -1,16 +1,13 @@ -import { useEffect, useState } from "preact/hooks"; +import { useState } from "preact/hooks"; -import { AmlExchangeBackend } from "../types.js"; import { - HttpResponse, - HttpResponseOk, - HttpResponsePaginated, - RequestError, + HttpResponsePaginated } from "@gnu-taler/web-util/browser"; +import { AmlExchangeBackend } from "../types.js"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 +import { AmountString, OfficerAccount, TalerExchangeApi, TalerExchangeResultByMethod, TalerHttpError } from "@gnu-taler/taler-util"; import _useSWR, { SWRHook } from "swr"; -import { usePublicBackend } from "./useBackend.js"; -import { AccountId, buildQuerySignature } from "../account.js"; +import { useExchangeApiContext } from "../context/config.js"; import { useOfficer } from "./useOfficer.js"; const useSWR = _useSWR as unknown as SWRHook; @@ -22,59 +19,49 @@ const MAX_RESULT_SIZE = PAGE_SIZE * 2 - 1; * @param args * @returns */ -export function useCases( - account: AccountId, - state: AmlExchangeBackend.AmlState, - signature: string | undefined, -): HttpResponsePaginated< - AmlExchangeBackend.AmlRecords, - AmlExchangeBackend.AmlError -> { - const { paginatedFetcher } = usePublicBackend(); +export function useCases(state: AmlExchangeBackend.AmlState) { + const officer = useOfficer(); + const session = officer.state === "ready" ? officer.account : undefined; + const { api } = useExchangeApiContext(); - const [page, setPage] = useState(1); + const [offset, setOffet] = useState(); + + async function fetcher([officer, state, offset]: [OfficerAccount, AmlExchangeBackend.AmlState, string | undefined]) { + return await api.getDecisionsByState(officer, state, { + order: "asc", offset, limit: MAX_RESULT_SIZE + }) + } - const { - data: afterData, - error: afterError, - isValidating: loadingAfter, - } = useSWR< - HttpResponseOk, - RequestError - >( - [ - `aml/${account}/decisions/${AmlExchangeBackend.AmlState[state]}`, - page, - PAGE_SIZE, - signature, - ], - paginatedFetcher, + const { data, error } = useSWR, TalerHttpError>( + !session ? undefined : [session, state, offset], + fetcher, ); - const [lastAfter, setLastAfter] = useState< - HttpResponse - >({ loading: true }); + // const [lastAfter, setLastAfter] = useState< + // HttpResponse + // >({ loading: true }); - useEffect(() => { - if (afterData) setLastAfter(afterData); - }, [afterData]); + // useEffect(() => { + // if (afterData) setLastAfter(afterData); + // }, [afterData]); - if (afterError) { - return afterError.cause; - } + // if (afterError) { + // return afterError.cause; + // } // if the query returns less that we ask, then we have reach the end or beginning - const isReachingEnd = - afterData && afterData.data && afterData.data.records.length < PAGE_SIZE; - const isReachingStart = false; + const isLastPage = + data && data.type === "ok" && data.body.records.length < PAGE_SIZE; + const isFirstPage = !offset; const pagination = { - isReachingEnd, - isReachingStart, + isLastPage, + isFirstPage, loadMore: () => { - if (!afterData || isReachingEnd) return; - if (afterData.data && afterData.data.records.length < MAX_RESULT_SIZE) { - setPage(page + 1); + if (isLastPage || data?.type !== "ok") return; + const list = data.body.records + if (list.length < MAX_RESULT_SIZE) { + // setOffset(list[list.length-1].account_name); } }, loadMorePrev: () => { @@ -82,65 +69,62 @@ export function useCases( }, }; - const records = !afterData - ? [] - : ((afterData ?? lastAfter).data ?? { records: [] }).records; - if (loadingAfter) return { loading: true, data: { records } }; - if (afterData) { - return { ok: true, data: { records }, ...pagination }; + // const public_accountslist = data?.type !== "ok" ? [] : data.body.public_accounts; + if (data) { + if (data.type === "fail") { + return { data } + } + return { data, pagination } + } + if (error) { + return error; } - return { loading: true }; + return undefined; } -const example1: AmlExchangeBackend.AmlRecords = { +const example1: TalerExchangeApi.AmlRecords = { records: [ { current_state: 0, h_payto: "QWEQWEQWEQWEWQE", rowid: 1, - threshold: "USD 100", + threshold: "USD 100" as AmountString, }, { current_state: 1, h_payto: "ASDASDASD", rowid: 1, - threshold: "USD 100", + threshold: "USD 100" as AmountString, }, { current_state: 2, h_payto: "ZXCZXCZXCXZC", rowid: 1, - threshold: "USD 1000", + threshold: "USD 1000" as AmountString, }, { current_state: 0, h_payto: "QWEQWEQWEQWEWQE", rowid: 1, - threshold: "USD 100", + threshold: "USD 100" as AmountString, }, { current_state: 1, h_payto: "ASDASDASD", rowid: 1, - threshold: "USD 100", + threshold: "USD 100" as AmountString, }, { current_state: 2, h_payto: "ZXCZXCZXCXZC", rowid: 1, - threshold: "USD 1000", + threshold: "USD 1000" as AmountString, }, ].map((e, idx) => { e.rowid = idx; - e.threshold = `${e.threshold}${idx}`; + e.threshold = `${e.threshold}${idx}` as AmountString; return e; }), }; -export const exampleResponse: HttpResponsePaginated = { - ok: true, - data: example1, - loadMore: () => {}, - loadMorePrev: () => {}, -} diff --git a/packages/aml-backoffice-ui/src/hooks/useOfficer.ts b/packages/aml-backoffice-ui/src/hooks/useOfficer.ts index 4ec43569b..0747170e8 100644 --- a/packages/aml-backoffice-ui/src/hooks/useOfficer.ts +++ b/packages/aml-backoffice-ui/src/hooks/useOfficer.ts @@ -1,16 +1,14 @@ import { AbsoluteTime, Codec, + LockedAccount, + OfficerAccount, buildCodecForObject, codecForAbsoluteTime, codecForString, + createNewOfficerAccount, + unlockOfficerAccount, } from "@gnu-taler/taler-util"; -import { - Account, - LockedAccount, - createNewAccount, - unlockAccount, -} from "../account.js"; import { buildStorageKey, useLocalStorage, @@ -43,7 +41,7 @@ interface OfficerLocked { } interface OfficerReady { state: "ready"; - account: Account; + account: OfficerAccount; forget: () => void; lock: () => void; } @@ -52,7 +50,7 @@ const OFFICER_KEY = buildStorageKey("officer", codecForOfficer()); const ACCOUNT_KEY = "account"; export function useOfficer(): OfficerState { - const accountStorage = useMemoryStorage(ACCOUNT_KEY); + const accountStorage = useMemoryStorage(ACCOUNT_KEY); const officerStorage = useLocalStorage(OFFICER_KEY); const officer = officerStorage.value; @@ -62,13 +60,13 @@ export function useOfficer(): OfficerState { return { state: "not-found", create: async (pwd: string) => { - const { accountId, safe, signingKey } = await createNewAccount(pwd); + const { id, safe, signingKey } = await createNewOfficerAccount(pwd); officerStorage.update({ account: safe, when: AbsoluteTime.now(), }); - accountStorage.update({ accountId, signingKey }); + accountStorage.update({ id, signingKey }); }, }; } @@ -80,7 +78,7 @@ export function useOfficer(): OfficerState { officerStorage.reset(); }, tryUnlock: async (pwd: string) => { - const ac = await unlockAccount(officer.account, pwd); + const ac = await unlockOfficerAccount(officer.account, pwd); accountStorage.update(ac); }, }; diff --git a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx index ce820d612..f618a3592 100644 --- a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx +++ b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx @@ -1,24 +1,23 @@ -import { Fragment, VNode, h } from "preact"; import { AbsoluteTime, AmountJson, Amounts, + PaytoString, + TalerError, TranslatedString, + assertUnreachable, } from "@gnu-taler/taler-util"; -import { format } from "date-fns"; +import { ErrorLoading, Loading, useTranslationContext } from "@gnu-taler/web-util/browser"; import { ArrowDownCircleIcon, ClockIcon } from "@heroicons/react/20/solid"; +import { format } from "date-fns"; +import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; import { NiceForm } from "../NiceForm.js"; import { FlexibleForm } from "../forms/index.js"; import { UIFormField } from "../handlers/forms.js"; +import { useCaseDetails } from "../hooks/useCaseDetails.js"; import { Pages } from "../pages.js"; import { AmlExchangeBackend } from "../types.js"; -import { HandleAccountNotReady } from "./HandleAccountNotReady.js"; -import { useTranslationContext } from "@gnu-taler/web-util/browser"; -import { useOfficer } from "../hooks/useOfficer.js"; -import { buildQuerySignature } from "../account.js"; -import { useCaseDetails } from "../hooks/useCaseDetails.js"; -import { handleNotOkResult } from "../utils/errors.js"; type AmlEvent = AmlFormEvent | KycCollectionEvent | KycExpirationEvent; type AmlFormEvent = { @@ -85,30 +84,33 @@ function getEventsFromAmlHistory( return ae.concat(ke).sort(selectSooner); } -export function CaseDetails({ account: paytoHash }: { account: string }) { +export function CaseDetails({ account }: { account: string }) { const [selected, setSelected] = useState(undefined); - const officer = useOfficer(); const { i18n } = useTranslationContext(); - if (officer.state !== "ready") { - return ; + const details = useCaseDetails(account) + if (!details) { + return } - const signature = - officer.state === "ready" - ? buildQuerySignature(officer.account.signingKey) - : undefined; - const details = useCaseDetails(officer.account.accountId, paytoHash, signature) - if (!details.ok && !details.loading) { - return handleNotOkResult(i18n)(details); + if (details instanceof TalerError) { + return } - const aml_history = details.loading ? [] : details.data.aml_history - const kyc_attributes = details.loading ? [] : details.data.kyc_attributes - const events = getEventsFromAmlHistory(aml_history,kyc_attributes); - + if (details.type === "fail") { + switch (details.case) { + case "unauthorized": + case "officer-not-found": + case "officer-disabled": return
+ default: assertUnreachable(details) + } + } + const { aml_history, kyc_attributes } = details.body + + const events = getEventsFromAmlHistory(aml_history, kyc_attributes); + return (
New AML form @@ -287,23 +289,22 @@ function ShowConsolidated({ }, Object.entries(cons.kyc).length > 0 ? { - title: "KYC" as TranslatedString, - fields: Object.entries(cons.kyc).map(([key, field]) => { - const result: UIFormField = { - type: "text", - props: { - label: key as TranslatedString, - name: `kyc.${key}.value`, - help: `${field.provider} since ${ - field.since.t_ms === "never" - ? "never" - : format(field.since.t_ms, "dd/MM/yyyy") + title: "KYC" as TranslatedString, + fields: Object.entries(cons.kyc).map(([key, field]) => { + const result: UIFormField = { + type: "text", + props: { + label: key as TranslatedString, + name: `kyc.${key}.value`, + help: `${field.provider} since ${field.since.t_ms === "never" + ? "never" + : format(field.since.t_ms, "dd/MM/yyyy") }` as TranslatedString, - }, - }; - return result; - }), - } + }, + }; + return result; + }), + } : undefined, ], }; @@ -319,7 +320,7 @@ function ShowConsolidated({ key={`${String(Date.now())}`} form={form} initial={cons} - onUpdate={() => {}} + onUpdate={() => { }} /> ); diff --git a/packages/aml-backoffice-ui/src/pages/Cases.tsx b/packages/aml-backoffice-ui/src/pages/Cases.tsx index 990c0d2d4..5f79db71e 100644 --- a/packages/aml-backoffice-ui/src/pages/Cases.tsx +++ b/packages/aml-backoffice-ui/src/pages/Cases.tsx @@ -1,4 +1,5 @@ -import { TranslatedString } from "@gnu-taler/taler-util"; +import { TalerError, TranslatedString, assertUnreachable } from "@gnu-taler/taler-util"; +import { ErrorLoading, Loading, useTranslationContext } from "@gnu-taler/web-util/browser"; import { VNode, h } from "preact"; import { useState } from "preact/hooks"; import { createNewForm } from "../handlers/forms.js"; @@ -7,34 +8,37 @@ import { useOfficer } from "../hooks/useOfficer.js"; import { Pages } from "../pages.js"; import { AmlExchangeBackend } from "../types.js"; import { amlStateConverter } from "./CaseDetails.js"; -import { HandleAccountNotReady } from "./HandleAccountNotReady.js"; -import { buildQuerySignature } from "../account.js"; -import { handleNotOkResult } from "../utils/errors.js"; -import { useTranslationContext } from "@gnu-taler/web-util/browser"; export function Cases() { - const officer = useOfficer(); const { i18n } = useTranslationContext(); - if (officer.state !== "ready") { - return ; - } - const form = createNewForm<{ - state: AmlExchangeBackend.AmlState; - }>(); - const signature = - officer.state === "ready" - ? buildQuerySignature(officer.account.signingKey) - : undefined; + const form = createNewForm<{ state: AmlExchangeBackend.AmlState }>(); + const initial = AmlExchangeBackend.AmlState.pending; const [stateFilter, setStateFilter] = useState(initial); - const list = useCases(officer.account.accountId, stateFilter, signature); - if (!list.ok && !list.loading) { - return handleNotOkResult(i18n)(list); + const list = useCases(stateFilter); + + if (!list) { + return + } + + if (list instanceof TalerError) { + return } - const records = list.loading ? [] : list.data.records + + if (list.data.type === "fail") { + switch (list.data.case) { + case "unauthorized": + case "officer-not-found": + case "officer-disabled": return
+ default: assertUnreachable(list.data) + } + } + + const { records } = list.data.body + return (
@@ -52,7 +56,7 @@ export function Cases() { onUpdate={(v) => { setStateFilter(v.state ?? initial); }} - onSubmit={(v) => {}} + onSubmit={(v) => { }} > { if (formValue.state === undefined || formValue.threshold === undefined) return; - + const justification = { index: selectedForm, name: formName, value: formValue } - const decision: AmlExchangeBackend.AmlDecision = { + const decision: TalerExchangeApi.AmlDecision = { justification: JSON.stringify(justification), decision_time: TalerProtocolTimestamp.now(), h_payto: account, @@ -63,9 +62,9 @@ export function NewFormEntry({ officer_sig: "", kyc_requirements: undefined } - const signature = buildDecisionSignature(officer.account.signingKey, decision); - decision.officer_sig = signature - api.updateDecision(officer.account.accountId, decision); + // const signature = buildDecisionSignature(officer.account.signingKey, decision); + // decision.officer_sig = signature + api.addDecisionDetails(officer.account, decision); // alert(JSON.stringify(formValue)); }} diff --git a/packages/aml-backoffice-ui/src/pages/Officer.tsx b/packages/aml-backoffice-ui/src/pages/Officer.tsx index 5320369e4..4af34805a 100644 --- a/packages/aml-backoffice-ui/src/pages/Officer.tsx +++ b/packages/aml-backoffice-ui/src/pages/Officer.tsx @@ -14,12 +14,12 @@ export function Officer() { Public key
-

{officer.account.accountId}

+

{officer.account.id}

( - i18n: ReturnType["i18n"], -): ( - result: HttpResponsePaginated | HttpResponse, -) => VNode { - return function handleNotOkResult2( - result: HttpResponsePaginated | HttpResponse, - ): VNode { - if (result.loading) return ; - if (!result.ok) { - switch (result.type) { - case ErrorType.TIMEOUT: { - notifyError(i18n.str`Request timeout, try again later.`, undefined); - break; - } - case ErrorType.CLIENT: { - if (result.status === HttpStatusCode.Unauthorized) { - notifyError(i18n.str`Wrong credentials`, undefined); - return

; - } - const errorData = result.payload; - notifyError( - i18n.str`Could not load due to a client error`, - errorData.hint as TranslatedString, - JSON.stringify(result), - ); - break; - } - case ErrorType.SERVER: { - notifyError( - i18n.str`Server returned with error`, - result.payload.hint as TranslatedString, - JSON.stringify(result.payload), - ); - break; - } - case ErrorType.UNREADABLE: { - notifyError( - i18n.str`Unexpected error.`, - `Response from ${result.info?.url} is unreadable, http status: ${result.status}` as TranslatedString, - JSON.stringify(result), - ); - break; - } - case ErrorType.UNEXPECTED: { - notifyError( - i18n.str`Unexpected error.`, - `Diagnostic from ${result.info?.url} is "${result.message}"` as TranslatedString, - JSON.stringify(result), - ); - break; - } - default: { - assertUnreachable(result); - } - } - - return
error
; - } - return
; - }; -} -export function assertUnreachable(x: never): never { - throw new Error("Didn't expect to get here"); -} diff --git a/packages/demobank-ui/src/assets/lang.svg b/packages/demobank-ui/src/assets/lang.svg deleted file mode 100644 index dd72ce65e..000000000 --- a/packages/demobank-ui/src/assets/lang.svg +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/demobank-ui/src/components/Attention.tsx b/packages/demobank-ui/src/components/Attention.tsx deleted file mode 100644 index 57d0a4199..000000000 --- a/packages/demobank-ui/src/components/Attention.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { TranslatedString } from "@gnu-taler/taler-util"; -import { ComponentChildren, Fragment, VNode, h } from "preact"; -import { assertUnreachable } from "./Routing.js"; - -interface Props { - type?: "info" | "success" | "warning" | "danger", - onClose?: () => void, - title: TranslatedString, - children?: ComponentChildren , -} -export function Attention({ type = "info", title, children, onClose }: Props): VNode { - return
-
-
-
- - {(() => { - switch (type) { - case "info": - return - case "warning": - return - case "danger": - return - case "success": - return - default: - assertUnreachable(type) - } - })()} - -
-
-

- {title} -

-
- {children} -
-
- {onClose && -
- -
- } -
-
- -
-} diff --git a/packages/demobank-ui/src/components/Cashouts/index.ts b/packages/demobank-ui/src/components/Cashouts/index.ts index 09839e753..6cbb1247d 100644 --- a/packages/demobank-ui/src/components/Cashouts/index.ts +++ b/packages/demobank-ui/src/components/Cashouts/index.ts @@ -15,7 +15,7 @@ */ import { HttpError, utils } from "@gnu-taler/web-util/browser"; -import { Loading } from "../Loading.js"; +import { Loading } from "@gnu-taler/web-util/browser"; // import { compose, StateViewMap } from "../../utils/index.js"; // import { wxApi } from "../../wxApi.js"; import { AbsoluteTime, AmountJson, TalerCoreBankErrorsByMethod, TalerCorebankApi, TalerError } from "@gnu-taler/taler-util"; diff --git a/packages/demobank-ui/src/components/Cashouts/views.tsx b/packages/demobank-ui/src/components/Cashouts/views.tsx index 89f173b0d..76a3a90df 100644 --- a/packages/demobank-ui/src/components/Cashouts/views.tsx +++ b/packages/demobank-ui/src/components/Cashouts/views.tsx @@ -18,10 +18,9 @@ import { Fragment, h, VNode } from "preact"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { State } from "./index.js"; import { format } from "date-fns"; -import { Amounts } from "@gnu-taler/taler-util"; +import { Amounts, assertUnreachable } from "@gnu-taler/taler-util"; import { RenderAmount } from "../../pages/PaytoWireTransferForm.js"; -import { assertUnreachable } from "../Routing.js"; -import { Attention } from "../Attention.js"; +import { Attention } from "@gnu-taler/web-util/browser"; export function LoadingUriView({ error }: State.LoadingUriError): VNode { const { i18n } = useTranslationContext(); diff --git a/packages/demobank-ui/src/components/CopyButton.tsx b/packages/demobank-ui/src/components/CopyButton.tsx deleted file mode 100644 index ca1ceaa8a..000000000 --- a/packages/demobank-ui/src/components/CopyButton.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { h, VNode } from "preact"; -import { useEffect, useState } from "preact/hooks"; - - - -export function CopyIcon(): VNode { - return ( - - - - ) -}; - -export function CopiedIcon(): VNode { - return ( - - - - ) -}; - -export function CopyButton({ class: clazz, getContent }: { class: string, getContent: () => string }): VNode { - const [copied, setCopied] = useState(false); - function copyText(): void { - navigator.clipboard.writeText(getContent() || ""); - setCopied(true); - } - useEffect(() => { - if (copied) { - setTimeout(() => { - setCopied(false); - }, 1000); - } - }, [copied]); - - if (!copied) { - return ( - - ); - } - return ( - - ); -} diff --git a/packages/demobank-ui/src/components/EmptyComponentExample/index.ts b/packages/demobank-ui/src/components/EmptyComponentExample/index.ts index 013904ff3..d80e6bdf9 100644 --- a/packages/demobank-ui/src/components/EmptyComponentExample/index.ts +++ b/packages/demobank-ui/src/components/EmptyComponentExample/index.ts @@ -14,10 +14,7 @@ GNU Taler; see the file COPYING. If not, see */ -import { Loading } from "../../components/Loading.js"; -import { HookError, utils } from "@gnu-taler/web-util/browser"; -//import { compose, StateViewMap } from "../../utils/index.js"; -//import { wxApi } from "../../wxApi.js"; +import { HookError, Loading, utils } from "@gnu-taler/web-util/browser"; import { useComponentState } from "./state.js"; import { LoadingUriView, ReadyView } from "./views.js"; diff --git a/packages/demobank-ui/src/components/ErrorLoading.tsx b/packages/demobank-ui/src/components/ErrorLoading.tsx deleted file mode 100644 index 84e72c5a1..000000000 --- a/packages/demobank-ui/src/components/ErrorLoading.tsx +++ /dev/null @@ -1,120 +0,0 @@ -/* -/* - This file is part of GNU Taler - (C) 2022 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 { TalerError, TalerErrorCode } from "@gnu-taler/taler-util"; -import { useTranslationContext } from "@gnu-taler/web-util/browser"; -import { Fragment, VNode, h } from "preact"; -import { Attention } from "./Attention.js"; -import { assertUnreachable } from "./Routing.js"; - -export function ErrorLoading({ error, showDetail }: { error: TalerError, showDetail?: boolean }): VNode { - const { i18n } = useTranslationContext() - switch (error.errorDetail.code) { - ////////////////// - // Every error that can be produce in a Http Request - ////////////////// - case TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT: { - if (error.hasErrorCode(TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT)) { - const { requestMethod, requestUrl, timeoutMs } = error.errorDetail - return - {error.message} - {showDetail && -
-              {JSON.stringify({ requestMethod, requestUrl, timeoutMs }, undefined, 2)}
-            
- } -
- } - assertUnreachable(1 as never) - } - case TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED: { - if (error.hasErrorCode(TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED)) { - const { requestMethod, requestUrl, throttleStats } = error.errorDetail - return - {error.message} - {showDetail && -
-              {JSON.stringify({ requestMethod, requestUrl, throttleStats }, undefined, 2)}
-            
- } -
- } - assertUnreachable(1 as never) - } - case TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE: { - if (error.hasErrorCode(TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE)) { - const { requestMethod, requestUrl, httpStatusCode, validationError } = error.errorDetail - return - {error.message} - {showDetail && -
-              {JSON.stringify({ requestMethod, requestUrl, httpStatusCode, validationError }, undefined, 2)}
-            
- } -
- } - assertUnreachable(1 as never) - } - case TalerErrorCode.WALLET_NETWORK_ERROR: { - if (error.hasErrorCode(TalerErrorCode.WALLET_NETWORK_ERROR)) { - const { requestMethod, requestUrl } = error.errorDetail - return - {error.message} - {showDetail && -
-              {JSON.stringify({ requestMethod, requestUrl }, undefined, 2)}
-            
- } -
- } - assertUnreachable(1 as never) - } - case TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR: { - if (error.hasErrorCode(TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR)) { - const { requestMethod, requestUrl, httpStatusCode, errorResponse } = error.errorDetail - return - {error.message} - {showDetail && -
-              {JSON.stringify({ requestMethod, requestUrl, httpStatusCode, errorResponse }, undefined, 2)}
-            
- } -
- } - assertUnreachable(1 as never) - } - ////////////////// - // Every other error - ////////////////// - // case TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR: { - // return - // - // } - ////////////////// - // Default message for unhandled case - ////////////////// - default: return - {error.message} - {showDetail && -
-          {JSON.stringify(error.errorDetail, undefined, 2)}
-        
- } -
- } -} - diff --git a/packages/demobank-ui/src/components/LangSelector.tsx b/packages/demobank-ui/src/components/LangSelector.tsx deleted file mode 100644 index 7cf0300df..000000000 --- a/packages/demobank-ui/src/components/LangSelector.tsx +++ /dev/null @@ -1,111 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2022 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 - */ - -/** - * - * @author Sebastian Javier Marchano (sebasjm) - */ - -import { Fragment, h, VNode } from "preact"; -import { useEffect, useState } from "preact/hooks"; -import { useTranslationContext } from "@gnu-taler/web-util/browser"; -import { strings as messages } from "../i18n/strings.js"; -import langIcon from "../assets/lang.svg"; - -type LangsNames = { - [P in keyof typeof messages]: string; -}; - -const names: LangsNames = { - es: "Español [es]", - en: "English [en]", - fr: "Français [fr]", - de: "Deutsch [de]", - sv: "Svenska [sv]", - it: "Italiano [it]", -}; - -function getLangName(s: keyof LangsNames | string): string { - if (names[s]) return names[s]; - return String(s); -} - -export function LangSelector(): VNode { - const [updatingLang, setUpdatingLang] = useState(false); - const { lang, changeLanguage } = useTranslationContext(); - const [hidden, setHidden] = useState(true); - - useEffect(() => { - function bodyKeyPress(event: KeyboardEvent) { - if (event.code === "Escape") setHidden(true); - } - function bodyOnClick(event: Event) { - setHidden(true); - } - document.body.addEventListener("click", bodyOnClick); - document.body.addEventListener("keydown", bodyKeyPress as any); - return () => { - document.body.removeEventListener("keydown", bodyKeyPress as any); - document.body.removeEventListener("click", bodyOnClick); - }; - }, []); - return ( -
-
- - - {!hidden && -
    - {Object.keys(messages) - .filter((l) => l !== lang) - .map((lang) => ( -
  • { - changeLanguage(lang); - setUpdatingLang(false); - setHidden(true) - }} - > - {getLangName(lang)} - - - {/* */} - -
  • - ))} - -
- } - -
-
- ); -} diff --git a/packages/demobank-ui/src/components/Loading.tsx b/packages/demobank-ui/src/components/Loading.tsx deleted file mode 100644 index b567e9056..000000000 --- a/packages/demobank-ui/src/components/Loading.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2022 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 { h, VNode } from "preact"; - -export function Loading(): VNode { - return ( -
- -
- ); -} - -export function Spinner(): VNode { - return ( -
-
-
-
-
-
- ); -} diff --git a/packages/demobank-ui/src/components/Routing.tsx b/packages/demobank-ui/src/components/Routing.tsx index c94e74201..65a7b6e86 100644 --- a/packages/demobank-ui/src/components/Routing.tsx +++ b/packages/demobank-ui/src/components/Routing.tsx @@ -323,7 +323,3 @@ function Redirect({ to }: { to: string }): VNode { }, []); return
being redirected to {to}
; } - -export function assertUnreachable(x: never): never { - throw new Error("Didn't expect to get here"); -} diff --git a/packages/demobank-ui/src/components/ShowInputErrorLabel.tsx b/packages/demobank-ui/src/components/ShowInputErrorLabel.tsx deleted file mode 100644 index c5840cad9..000000000 --- a/packages/demobank-ui/src/components/ShowInputErrorLabel.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2022 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 { Fragment, h, VNode } from "preact"; - -export function ShowInputErrorLabel({ - isDirty, - message, -}: { - message: string | undefined; - isDirty: boolean; -}): VNode { - if (message && isDirty) - return
{message}
; - return
; -} diff --git a/packages/demobank-ui/src/components/ShowLocalNotification.tsx b/packages/demobank-ui/src/components/ShowLocalNotification.tsx deleted file mode 100644 index bb62a48f0..000000000 --- a/packages/demobank-ui/src/components/ShowLocalNotification.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { Notification } from "@gnu-taler/web-util/browser"; -import { h, Fragment, VNode } from "preact"; -import { Attention } from "./Attention.js"; -import { useSettings } from "../hooks/settings.js"; - -export function ShowLocalNotification({ notification }: { notification?: Notification }): VNode { - if (!notification) return - switch (notification.message.type) { - case "error": - return
-
- { - notification.remove() - }}> - {notification.message.description && -
- {notification.message.description} -
- } - -
-
-
- case "info": - return
-
- { - notification.remove(); - }} />
- } -} - - -function MaybeShowDebugInfo({ info }: { info: any }): VNode { - const [settings] = useSettings() - if (settings.showDebugInfo) { - return
-      {info}
-    
- } - return -} - diff --git a/packages/demobank-ui/src/components/Transactions/index.ts b/packages/demobank-ui/src/components/Transactions/index.ts index 3c4fb5ce9..b95aa4cb1 100644 --- a/packages/demobank-ui/src/components/Transactions/index.ts +++ b/packages/demobank-ui/src/components/Transactions/index.ts @@ -14,10 +14,7 @@ GNU Taler; see the file COPYING. If not, see */ -import { HttpError, utils } from "@gnu-taler/web-util/browser"; -import { Loading } from "../Loading.js"; -// import { compose, StateViewMap } from "../../utils/index.js"; -// import { wxApi } from "../../wxApi.js"; +import { Loading, utils } from "@gnu-taler/web-util/browser"; import { AbsoluteTime, AmountJson, TalerError } from "@gnu-taler/taler-util"; import { useComponentState } from "./state.js"; import { LoadingUriView, ReadyView } from "./views.js"; diff --git a/packages/demobank-ui/src/components/app.tsx b/packages/demobank-ui/src/components/app.tsx index 55e1178fe..f79bd96b0 100644 --- a/packages/demobank-ui/src/components/app.tsx +++ b/packages/demobank-ui/src/components/app.tsx @@ -72,6 +72,7 @@ function getInitialBackendBaseURL(): string { ? localStorage.getItem("bank-base-url") : undefined; let result: string; + if (!overrideUrl) { //normal path if (!bankUiSettings.backendBaseURL) { diff --git a/packages/demobank-ui/src/context/config.ts b/packages/demobank-ui/src/context/config.ts index a31d914b8..a55af719d 100644 --- a/packages/demobank-ui/src/context/config.ts +++ b/packages/demobank-ui/src/context/config.ts @@ -18,7 +18,7 @@ import { TalerCorebankApi, TalerCoreBankHttpClient, TalerError } from "@gnu-tale import { BrowserHttpLib, useTranslationContext } from "@gnu-taler/web-util/browser"; import { ComponentChildren, createContext, FunctionComponent, h, VNode } from "preact"; import { useContext, useEffect, useState } from "preact/hooks"; -import { ErrorLoading } from "../components/ErrorLoading.js"; +import { ErrorLoading } from "@gnu-taler/web-util/browser"; /** * diff --git a/packages/demobank-ui/src/pages/AccountPage/index.ts b/packages/demobank-ui/src/pages/AccountPage/index.ts index 87ed878b0..3a3001ad4 100644 --- a/packages/demobank-ui/src/pages/AccountPage/index.ts +++ b/packages/demobank-ui/src/pages/AccountPage/index.ts @@ -16,8 +16,8 @@ import { AbsoluteTime, AmountJson, TalerCorebankApi, TalerError } from "@gnu-taler/taler-util"; import { utils } from "@gnu-taler/web-util/browser"; -import { ErrorLoading } from "../../components/ErrorLoading.js"; -import { Loading } from "../../components/Loading.js"; +import { ErrorLoading } from "@gnu-taler/web-util/browser"; +import { Loading } from "@gnu-taler/web-util/browser"; import { LoginForm } from "../LoginForm.js"; import { useComponentState } from "./state.js"; import { InvalidIbanView, ReadyView } from "./views.js"; diff --git a/packages/demobank-ui/src/pages/AccountPage/views.tsx b/packages/demobank-ui/src/pages/AccountPage/views.tsx index 8fff37624..0f5236192 100644 --- a/packages/demobank-ui/src/pages/AccountPage/views.tsx +++ b/packages/demobank-ui/src/pages/AccountPage/views.tsx @@ -16,7 +16,7 @@ import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; -import { Attention } from "../../components/Attention.js"; +import { Attention } from "@gnu-taler/web-util/browser"; import { Transactions } from "../../components/Transactions/index.js"; import { useSettings } from "../../hooks/settings.js"; import { PaymentOptions } from "../PaymentOptions.js"; diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx index c0babd0c9..5561d7b42 100644 --- a/packages/demobank-ui/src/pages/BankFrame.tsx +++ b/packages/demobank-ui/src/pages/BankFrame.tsx @@ -14,15 +14,11 @@ GNU Taler; see the file COPYING. If not, see */ -import { Amounts, TalerError, TranslatedString, parsePaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util"; -import { notifyError, notifyException, useNotifications, useTranslationContext } from "@gnu-taler/web-util/browser"; +import { Amounts, TalerError, TranslatedString } from "@gnu-taler/taler-util"; +import { Attention, LangSelector, Loading, notifyError, notifyException, useNotifications, useTranslationContext } from "@gnu-taler/web-util/browser"; import { ComponentChildren, Fragment, VNode, h } from "preact"; import { useEffect, useErrorBoundary, useState } from "preact/hooks"; import logo from "../assets/logo-2021.svg"; -import { Attention } from "../components/Attention.js"; -import { CopyButton } from "../components/CopyButton.js"; -import { LangSelector } from "../components/LangSelector.js"; -import { Loading } from "../components/Loading.js"; import { useAccountDetails } from "../hooks/access.js"; import { useBackendState } from "../hooks/backend.js"; import { getAllBooleanSettings, getLabelForSetting, useSettings } from "../hooks/settings.js"; @@ -179,7 +175,7 @@ export function BankFrame({ : undefined}
  • - +
  • diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx b/packages/demobank-ui/src/pages/LoginForm.tsx index f21e98343..707c1e688 100644 --- a/packages/demobank-ui/src/pages/LoginForm.tsx +++ b/packages/demobank-ui/src/pages/LoginForm.tsx @@ -18,15 +18,15 @@ import { TranslatedString } from "@gnu-taler/taler-util"; import { Notification, useLocalNotification, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useEffect, useRef, useState } from "preact/hooks"; -import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js"; +import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser"; import { useBankCoreApiContext } from "../context/config.js"; import { useBackendState } from "../hooks/backend.js"; import { bankUiSettings } from "../settings.js"; import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js"; import { assertUnreachable } from "./WithdrawalOperationPage.js"; import { doAutoFocus } from "./PaytoWireTransferForm.js"; -import { Attention } from "../components/Attention.js"; -import { ShowLocalNotification } from "../components/ShowLocalNotification.js"; +import { Attention } from "@gnu-taler/web-util/browser"; +import { ShowLocalNotification } from "@gnu-taler/web-util/browser"; /** diff --git a/packages/demobank-ui/src/pages/OperationState/index.ts b/packages/demobank-ui/src/pages/OperationState/index.ts index b17b0d787..120fd7b45 100644 --- a/packages/demobank-ui/src/pages/OperationState/index.ts +++ b/packages/demobank-ui/src/pages/OperationState/index.ts @@ -16,8 +16,8 @@ import { AbsoluteTime, AmountJson, TalerCoreBankErrorsByMethod, TalerError, TalerErrorDetail, TranslatedString, WithdrawUriResult } from "@gnu-taler/taler-util"; import { utils } from "@gnu-taler/web-util/browser"; -import { ErrorLoading } from "../../components/ErrorLoading.js"; -import { Loading } from "../../components/Loading.js"; +import { ErrorLoading } from "@gnu-taler/web-util/browser"; +import { Loading } from "@gnu-taler/web-util/browser"; import { useComponentState } from "./state.js"; import { AbortedView, ConfirmedView, FailedView, InvalidPaytoView, InvalidReserveView, InvalidWithdrawalView, NeedConfirmationView, ReadyView } from "./views.js"; diff --git a/packages/demobank-ui/src/pages/OperationState/views.tsx b/packages/demobank-ui/src/pages/OperationState/views.tsx index e623b0dc2..2c4019de2 100644 --- a/packages/demobank-ui/src/pages/OperationState/views.tsx +++ b/packages/demobank-ui/src/pages/OperationState/views.tsx @@ -19,13 +19,13 @@ import { notifyInfo, useLocalNotification, useTranslationContext } from "@gnu-ta import { Fragment, VNode, h } from "preact"; import { useEffect, useMemo, useState } from "preact/hooks"; import { QR } from "../../components/QR.js"; -import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js"; +import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser"; import { useSettings } from "../../hooks/settings.js"; import { undefinedIfEmpty } from "../../utils.js"; import { State } from "./index.js"; -import { ShowLocalNotification } from "../../components/ShowLocalNotification.js"; -import { ErrorLoading } from "../../components/ErrorLoading.js"; -import { Attention } from "../../components/Attention.js"; +import { ShowLocalNotification } from "@gnu-taler/web-util/browser"; +import { ErrorLoading } from "@gnu-taler/web-util/browser"; +import { Attention } from "@gnu-taler/web-util/browser"; import { assertUnreachable } from "../WithdrawalOperationPage.js"; export function InvalidPaytoView({ payto, onClose }: State.InvalidPayto) { diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx index 31592039f..55eba423c 100644 --- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx +++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx @@ -32,7 +32,7 @@ import { import { Fragment, Ref, VNode, h } from "preact"; import { useState } from "preact/hooks"; import { mutate } from "swr"; -import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js"; +import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser"; import { useBankCoreApiContext } from "../context/config.js"; import { useBackendState } from "../hooks/backend.js"; import { @@ -41,7 +41,7 @@ import { withRuntimeErrorHandling } from "../utils.js"; import { assertUnreachable } from "./WithdrawalOperationPage.js"; -import { ShowLocalNotification } from "../components/ShowLocalNotification.js"; +import { ShowLocalNotification } from "@gnu-taler/web-util/browser"; const logger = new Logger("PaytoWireTransferForm"); diff --git a/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx b/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx index d33353180..d441d002e 100644 --- a/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx +++ b/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx @@ -18,7 +18,7 @@ import { Logger, TalerError } from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; -import { Loading } from "../components/Loading.js"; +import { Loading } from "@gnu-taler/web-util/browser"; import { Transactions } from "../components/Transactions/index.js"; import { usePublicAccounts } from "../hooks/access.js"; diff --git a/packages/demobank-ui/src/pages/QrCodeSection.tsx b/packages/demobank-ui/src/pages/QrCodeSection.tsx index 22bf604f2..e8c1a0e6e 100644 --- a/packages/demobank-ui/src/pages/QrCodeSection.tsx +++ b/packages/demobank-ui/src/pages/QrCodeSection.tsx @@ -29,7 +29,7 @@ import { QR } from "../components/QR.js"; import { useBankCoreApiContext } from "../context/config.js"; import { withRuntimeErrorHandling } from "../utils.js"; import { assertUnreachable } from "./WithdrawalOperationPage.js"; -import { ShowLocalNotification } from "../components/ShowLocalNotification.js"; +import { ShowLocalNotification } from "@gnu-taler/web-util/browser"; export function QrCodeSection({ withdrawUri, diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx b/packages/demobank-ui/src/pages/RegistrationPage.tsx index c2eca25e8..e8969afb9 100644 --- a/packages/demobank-ui/src/pages/RegistrationPage.tsx +++ b/packages/demobank-ui/src/pages/RegistrationPage.tsx @@ -20,13 +20,13 @@ import { } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; -import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js"; +import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser"; import { useBankCoreApiContext } from "../context/config.js"; import { useBackendState } from "../hooks/backend.js"; import { bankUiSettings } from "../settings.js"; import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js"; import { getRandomPassword, getRandomUsername } from "./rnd.js"; -import { ShowLocalNotification } from "../components/ShowLocalNotification.js"; +import { ShowLocalNotification } from "@gnu-taler/web-util/browser"; const logger = new Logger("RegistrationPage"); diff --git a/packages/demobank-ui/src/pages/ShowAccountDetails.tsx b/packages/demobank-ui/src/pages/ShowAccountDetails.tsx index c07802273..43fd39205 100644 --- a/packages/demobank-ui/src/pages/ShowAccountDetails.tsx +++ b/packages/demobank-ui/src/pages/ShowAccountDetails.tsx @@ -2,8 +2,8 @@ import { TalerCorebankApi, TalerError, TranslatedString } from "@gnu-taler/taler import { notifyInfo, useLocalNotification, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; -import { ErrorLoading } from "../components/ErrorLoading.js"; -import { Loading } from "../components/Loading.js"; +import { ErrorLoading } from "@gnu-taler/web-util/browser"; +import { Loading } from "@gnu-taler/web-util/browser"; import { useBankCoreApiContext } from "../context/config.js"; import { useAccountDetails } from "../hooks/access.js"; import { useBackendState } from "../hooks/backend.js"; @@ -12,7 +12,7 @@ import { LoginForm } from "./LoginForm.js"; import { ProfileNavigation } from "./ProfileNavigation.js"; import { assertUnreachable } from "./WithdrawalOperationPage.js"; import { AccountForm } from "./admin/AccountForm.js"; -import { ShowLocalNotification } from "../components/ShowLocalNotification.js"; +import { ShowLocalNotification } from "@gnu-taler/web-util/browser"; export function ShowAccountDetails({ account, diff --git a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx index d30216f3f..759182997 100644 --- a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx +++ b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx @@ -1,14 +1,14 @@ import { notifyInfo, useLocalNotification, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; -import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js"; +import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser"; import { useBankCoreApiContext } from "../context/config.js"; import { useBackendState } from "../hooks/backend.js"; import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js"; import { doAutoFocus } from "./PaytoWireTransferForm.js"; import { ProfileNavigation } from "./ProfileNavigation.js"; import { assertUnreachable } from "./WithdrawalOperationPage.js"; -import { ShowLocalNotification } from "../components/ShowLocalNotification.js"; +import { ShowLocalNotification } from "@gnu-taler/web-util/browser"; export function UpdateAccountPassword({ account: accountName, @@ -63,7 +63,7 @@ export function UpdateAccountPassword({ }) case "old-password-invalid-or-not-allowed": return notify({ type: "error", - title: current ? + title: current ? i18n.str`This user have no right on to change the password.` : i18n.str`This user have no right on to change the password or the old password doesn't match.` }) @@ -79,7 +79,7 @@ export function UpdateAccountPassword({ return ( - + {accountIsTheCurrentUser ? :

    diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx index e3a713fdd..9a45e6285 100644 --- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx +++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx @@ -29,7 +29,7 @@ import { import { Fragment, VNode, h } from "preact"; import { forwardRef } from "preact/compat"; import { useState } from "preact/hooks"; -import { Attention } from "../components/Attention.js"; +import { Attention } from "@gnu-taler/web-util/browser"; import { useBankCoreApiContext } from "../context/config.js"; import { useBackendState } from "../hooks/backend.js"; import { useSettings } from "../hooks/settings.js"; @@ -37,7 +37,7 @@ import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js"; import { OperationState } from "./OperationState/index.js"; import { InputAmount, doAutoFocus } from "./PaytoWireTransferForm.js"; import { assertUnreachable } from "./WithdrawalOperationPage.js"; -import { ShowLocalNotification } from "../components/ShowLocalNotification.js"; +import { ShowLocalNotification } from "@gnu-taler/web-util/browser"; const logger = new Logger("WalletWithdrawForm"); const RefAmount = forwardRef(InputAmount); diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx index b548c0d16..f34e8a919 100644 --- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx +++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx @@ -31,13 +31,13 @@ import { import { Fragment, VNode, h } from "preact"; import { useMemo, useState } from "preact/hooks"; import { mutate } from "swr"; -import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js"; +import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser"; import { useBankCoreApiContext } from "../context/config.js"; import { useSettings } from "../hooks/settings.js"; import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js"; import { RenderAmount } from "./PaytoWireTransferForm.js"; import { assertUnreachable } from "./WithdrawalOperationPage.js"; -import { ShowLocalNotification } from "../components/ShowLocalNotification.js"; +import { ShowLocalNotification } from "@gnu-taler/web-util/browser"; const logger = new Logger("WithdrawalConfirmationQuestion"); diff --git a/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx b/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx index 4620e5456..5ed57a0f7 100644 --- a/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx +++ b/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx @@ -23,7 +23,7 @@ import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; -import { Attention } from "../components/Attention.js"; +import { Attention } from "@gnu-taler/web-util/browser"; import { useBankCoreApiContext } from "../context/config.js"; import { useSettings } from "../hooks/settings.js"; import { WithdrawalQRCode } from "./WithdrawalQRCode.js"; diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx index bdd8ea585..52e3c63ee 100644 --- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx +++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx @@ -23,9 +23,9 @@ import { } from "@gnu-taler/taler-util"; import { notifyInfo, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; -import { Attention } from "../components/Attention.js"; -import { ErrorLoading } from "../components/ErrorLoading.js"; -import { Loading } from "../components/Loading.js"; +import { Attention } from "@gnu-taler/web-util/browser"; +import { ErrorLoading } from "@gnu-taler/web-util/browser"; +import { Loading } from "@gnu-taler/web-util/browser"; import { useWithdrawalDetails } from "../hooks/access.js"; import { QrCodeSection } from "./QrCodeSection.js"; import { WithdrawalConfirmationQuestion } from "./WithdrawalConfirmationQuestion.js"; diff --git a/packages/demobank-ui/src/pages/admin/Account.tsx b/packages/demobank-ui/src/pages/admin/Account.tsx index 19189bec4..588d945ba 100644 --- a/packages/demobank-ui/src/pages/admin/Account.tsx +++ b/packages/demobank-ui/src/pages/admin/Account.tsx @@ -1,8 +1,8 @@ import { Amounts, TalerError } from "@gnu-taler/taler-util"; import { notifyInfo, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; -import { ErrorLoading } from "../../components/ErrorLoading.js"; -import { Loading } from "../../components/Loading.js"; +import { ErrorLoading } from "@gnu-taler/web-util/browser"; +import { Loading } from "@gnu-taler/web-util/browser"; import { useAccountDetails } from "../../hooks/access.js"; import { assertUnreachable } from "../WithdrawalOperationPage.js"; import { LoginForm } from "../LoginForm.js"; diff --git a/packages/demobank-ui/src/pages/admin/AccountForm.tsx b/packages/demobank-ui/src/pages/admin/AccountForm.tsx index fa3a28057..7311d826e 100644 --- a/packages/demobank-ui/src/pages/admin/AccountForm.tsx +++ b/packages/demobank-ui/src/pages/admin/AccountForm.tsx @@ -1,11 +1,11 @@ import { ComponentChildren, Fragment, VNode, h } from "preact"; -import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js"; +import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser"; import { PartialButDefined, RecursivePartial, WithIntermediate, undefinedIfEmpty, validateIBAN } from "../../utils.js"; import { useEffect, useRef, useState } from "preact/hooks"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { PaytoString, TalerCorebankApi, buildPayto, parsePaytoUri } from "@gnu-taler/taler-util"; import { doAutoFocus } from "../PaytoWireTransferForm.js"; -import { CopyButton } from "../../components/CopyButton.js"; +import { CopyButton } from "@gnu-taler/web-util/browser"; import { assertUnreachable } from "../WithdrawalOperationPage.js"; const IBAN_REGEX = /^[A-Z][A-Z0-9]*$/; diff --git a/packages/demobank-ui/src/pages/admin/AccountList.tsx b/packages/demobank-ui/src/pages/admin/AccountList.tsx index be5194e6d..2aefde715 100644 --- a/packages/demobank-ui/src/pages/admin/AccountList.tsx +++ b/packages/demobank-ui/src/pages/admin/AccountList.tsx @@ -1,8 +1,8 @@ import { Amounts, TalerError } from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; -import { ErrorLoading } from "../../components/ErrorLoading.js"; -import { Loading } from "../../components/Loading.js"; +import { ErrorLoading } from "@gnu-taler/web-util/browser"; +import { Loading } from "@gnu-taler/web-util/browser"; import { useBankCoreApiContext } from "../../context/config.js"; import { useBusinessAccounts } from "../../hooks/circuit.js"; import { RenderAmount } from "../PaytoWireTransferForm.js"; diff --git a/packages/demobank-ui/src/pages/admin/AdminHome.tsx b/packages/demobank-ui/src/pages/admin/AdminHome.tsx index a30cae547..9bc2ee571 100644 --- a/packages/demobank-ui/src/pages/admin/AdminHome.tsx +++ b/packages/demobank-ui/src/pages/admin/AdminHome.tsx @@ -2,7 +2,7 @@ import { AmountString, Amounts, TalerCorebankApi, TalerError } from "@gnu-taler/ import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; -import { ErrorLoading } from "../../components/ErrorLoading.js"; +import { ErrorLoading } from "@gnu-taler/web-util/browser"; import { Transactions } from "../../components/Transactions/index.js"; import { useLastMonitorInfo } from "../../hooks/circuit.js"; import { RenderAmount } from "../PaytoWireTransferForm.js"; diff --git a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx index 3f4364c16..0369a6283 100644 --- a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx +++ b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx @@ -3,14 +3,14 @@ import { notifyInfo, useLocalNotification, useTranslationContext } from "@gnu-ta import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; import { mutate } from "swr"; -import { Attention } from "../../components/Attention.js"; +import { Attention } from "@gnu-taler/web-util/browser"; import { useBankCoreApiContext } from "../../context/config.js"; import { useBackendState } from "../../hooks/backend.js"; import { withRuntimeErrorHandling } from "../../utils.js"; import { assertUnreachable } from "../WithdrawalOperationPage.js"; import { getRandomPassword } from "../rnd.js"; import { AccountForm, AccountFormData } from "./AccountForm.js"; -import { ShowLocalNotification } from "../../components/ShowLocalNotification.js"; +import { ShowLocalNotification } from "@gnu-taler/web-util/browser"; export function CreateNewAccount({ onCancel, diff --git a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx index fa9693941..01136fdaf 100644 --- a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx +++ b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx @@ -2,10 +2,10 @@ import { Amounts, TalerError, TranslatedString } from "@gnu-taler/taler-util"; import { notifyInfo, useLocalNotification, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; -import { Attention } from "../../components/Attention.js"; -import { ErrorLoading } from "../../components/ErrorLoading.js"; -import { Loading } from "../../components/Loading.js"; -import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js"; +import { Attention } from "@gnu-taler/web-util/browser"; +import { ErrorLoading } from "@gnu-taler/web-util/browser"; +import { Loading } from "@gnu-taler/web-util/browser"; +import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser"; import { useBankCoreApiContext } from "../../context/config.js"; import { useAccountDetails } from "../../hooks/access.js"; import { useBackendState } from "../../hooks/backend.js"; @@ -13,7 +13,7 @@ import { undefinedIfEmpty } from "../../utils.js"; import { LoginForm } from "../LoginForm.js"; import { doAutoFocus } from "../PaytoWireTransferForm.js"; import { assertUnreachable } from "../WithdrawalOperationPage.js"; -import { ShowLocalNotification } from "../../components/ShowLocalNotification.js"; +import { ShowLocalNotification } from "@gnu-taler/web-util/browser"; export function RemoveAccount({ account, diff --git a/packages/demobank-ui/src/pages/business/CreateCashout.tsx b/packages/demobank-ui/src/pages/business/CreateCashout.tsx index 5c284be24..735d84847 100644 --- a/packages/demobank-ui/src/pages/business/CreateCashout.tsx +++ b/packages/demobank-ui/src/pages/business/CreateCashout.tsx @@ -25,10 +25,10 @@ import { import { Fragment, VNode, h } from "preact"; import { useEffect, useState } from "preact/hooks"; import { mutate } from "swr"; -import { Attention } from "../../components/Attention.js"; -import { ErrorLoading } from "../../components/ErrorLoading.js"; -import { Loading } from "../../components/Loading.js"; -import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js"; +import { Attention } from "@gnu-taler/web-util/browser"; +import { ErrorLoading } from "@gnu-taler/web-util/browser"; +import { Loading } from "@gnu-taler/web-util/browser"; +import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser"; import { useBankCoreApiContext } from "../../context/config.js"; import { useAccountDetails } from "../../hooks/access.js"; import { useBackendState } from "../../hooks/backend.js"; @@ -43,7 +43,7 @@ import { import { LoginForm } from "../LoginForm.js"; import { InputAmount } from "../PaytoWireTransferForm.js"; import { assertUnreachable } from "../WithdrawalOperationPage.js"; -import { ShowLocalNotification } from "../../components/ShowLocalNotification.js"; +import { ShowLocalNotification } from "@gnu-taler/web-util/browser"; interface Props { account: string; diff --git a/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx b/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx index a8b57b90c..80e585cf5 100644 --- a/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx +++ b/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx @@ -25,10 +25,10 @@ import { format } from "date-fns"; import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; import { mutate } from "swr"; -import { Attention } from "../../components/Attention.js"; -import { ErrorLoading } from "../../components/ErrorLoading.js"; -import { Loading } from "../../components/Loading.js"; -import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js"; +import { Attention } from "@gnu-taler/web-util/browser"; +import { ErrorLoading } from "@gnu-taler/web-util/browser"; +import { Loading } from "@gnu-taler/web-util/browser"; +import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser"; import { useBankCoreApiContext } from "../../context/config.js"; import { useBackendState } from "../../hooks/backend.js"; import { @@ -39,7 +39,7 @@ import { withRuntimeErrorHandling } from "../../utils.js"; import { assertUnreachable } from "../WithdrawalOperationPage.js"; -import { ShowLocalNotification } from "../../components/ShowLocalNotification.js"; +import { ShowLocalNotification } from "@gnu-taler/web-util/browser"; interface Props { id: string; diff --git a/packages/demobank-ui/tailwind.config.js b/packages/demobank-ui/tailwind.config.js index 01f058b2e..ec51dfbb8 100644 --- a/packages/demobank-ui/tailwind.config.js +++ b/packages/demobank-ui/tailwind.config.js @@ -1,6 +1,12 @@ /** @type {import('tailwindcss').Config} */ export default { - content: ["./src/**/*.{html,tsx}"], + content: { + relative: true, + files: [ + "./src/**/*.{html,tsx}", + "./node_modules/@gnu-taler/web-util/src/**/*.{html,tsx}" + ], + }, theme: { extend: {}, }, diff --git a/packages/web-util/src/assets/lang.svg b/packages/web-util/src/assets/lang.svg new file mode 100644 index 000000000..dd72ce65e --- /dev/null +++ b/packages/web-util/src/assets/lang.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/web-util/src/components/Attention.tsx b/packages/web-util/src/components/Attention.tsx new file mode 100644 index 000000000..b85230a1b --- /dev/null +++ b/packages/web-util/src/components/Attention.tsx @@ -0,0 +1,58 @@ +import { TranslatedString, assertUnreachable } from "@gnu-taler/taler-util"; +import { ComponentChildren, Fragment, VNode, h } from "preact"; + +interface Props { + type?: "info" | "success" | "warning" | "danger", + onClose?: () => void, + title: TranslatedString, + children?: ComponentChildren, +} +export function Attention({ type = "info", title, children, onClose }: Props): VNode { + return
    +
    +
    +
    + + {(() => { + switch (type) { + case "info": + return + case "warning": + return + case "danger": + return + case "success": + return + default: + assertUnreachable(type) + } + })()} + +
    +
    +

    + {title} +

    +
    + {children} +
    +
    + {onClose && +
    + +
    + } +
    +
    + +
    +} diff --git a/packages/web-util/src/components/CopyButton.tsx b/packages/web-util/src/components/CopyButton.tsx new file mode 100644 index 000000000..0096da365 --- /dev/null +++ b/packages/web-util/src/components/CopyButton.tsx @@ -0,0 +1,46 @@ +import { h, VNode } from "preact"; +import { useEffect, useState } from "preact/hooks"; + +export function CopyIcon(): VNode { + return ( + + + + ) +}; + +export function CopiedIcon(): VNode { + return ( + + + + ) +}; + +export function CopyButton({ class: clazz, getContent }: { class: string, getContent: () => string }): VNode { + const [copied, setCopied] = useState(false); + function copyText(): void { + navigator.clipboard.writeText(getContent() || ""); + setCopied(true); + } + useEffect(() => { + if (copied) { + setTimeout(() => { + setCopied(false); + }, 1000); + } + }, [copied]); + + if (!copied) { + return ( + + ); + } + return ( + + ); +} diff --git a/packages/web-util/src/components/ErrorLoading.tsx b/packages/web-util/src/components/ErrorLoading.tsx new file mode 100644 index 000000000..02f2a3282 --- /dev/null +++ b/packages/web-util/src/components/ErrorLoading.tsx @@ -0,0 +1,119 @@ +/* +/* + This file is part of GNU Taler + (C) 2022 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 { TalerError, TalerErrorCode, assertUnreachable } from "@gnu-taler/taler-util"; +import { Fragment, VNode, h } from "preact"; +import { Attention } from "./Attention.js"; +import { useTranslationContext } from "../index.browser.js"; + +export function ErrorLoading({ error, showDetail }: { error: TalerError, showDetail?: boolean }): VNode { + const { i18n } = useTranslationContext() + switch (error.errorDetail.code) { + ////////////////// + // Every error that can be produce in a Http Request + ////////////////// + case TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT: { + if (error.hasErrorCode(TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT)) { + const { requestMethod, requestUrl, timeoutMs } = error.errorDetail + return + {error.message} + {showDetail && +
    +              {JSON.stringify({ requestMethod, requestUrl, timeoutMs }, undefined, 2)}
    +            
    + } +
    + } + assertUnreachable(1 as never) + } + case TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED: { + if (error.hasErrorCode(TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED)) { + const { requestMethod, requestUrl, throttleStats } = error.errorDetail + return + {error.message} + {showDetail && +
    +              {JSON.stringify({ requestMethod, requestUrl, throttleStats }, undefined, 2)}
    +            
    + } +
    + } + assertUnreachable(1 as never) + } + case TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE: { + if (error.hasErrorCode(TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE)) { + const { requestMethod, requestUrl, httpStatusCode, validationError } = error.errorDetail + return + {error.message} + {showDetail && +
    +              {JSON.stringify({ requestMethod, requestUrl, httpStatusCode, validationError }, undefined, 2)}
    +            
    + } +
    + } + assertUnreachable(1 as never) + } + case TalerErrorCode.WALLET_NETWORK_ERROR: { + if (error.hasErrorCode(TalerErrorCode.WALLET_NETWORK_ERROR)) { + const { requestMethod, requestUrl } = error.errorDetail + return + {error.message} + {showDetail && +
    +              {JSON.stringify({ requestMethod, requestUrl }, undefined, 2)}
    +            
    + } +
    + } + assertUnreachable(1 as never) + } + case TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR: { + if (error.hasErrorCode(TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR)) { + const { requestMethod, requestUrl, httpStatusCode, errorResponse } = error.errorDetail + return + {error.message} + {showDetail && +
    +              {JSON.stringify({ requestMethod, requestUrl, httpStatusCode, errorResponse }, undefined, 2)}
    +            
    + } +
    + } + assertUnreachable(1 as never) + } + ////////////////// + // Every other error + ////////////////// + // case TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR: { + // return + // + // } + ////////////////// + // Default message for unhandled case + ////////////////// + default: return + {error.message} + {showDetail && +
    +          {JSON.stringify(error.errorDetail, undefined, 2)}
    +        
    + } +
    + } +} + diff --git a/packages/web-util/src/components/LangSelector.tsx b/packages/web-util/src/components/LangSelector.tsx new file mode 100644 index 000000000..a8d910129 --- /dev/null +++ b/packages/web-util/src/components/LangSelector.tsx @@ -0,0 +1,111 @@ +/* + This file is part of GNU Taler + (C) 2022 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 + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { Fragment, h, VNode } from "preact"; +import { useEffect, useState } from "preact/hooks"; +// import { strings as messages } from "../i18n/strings.js"; +import langIcon from "../assets/lang.svg"; +import { useTranslationContext } from "../index.browser.js"; + +type LangsNames = { + [P: string]: string; +}; + +const names: LangsNames = { + es: "Español [es]", + en: "English [en]", + fr: "Français [fr]", + de: "Deutsch [de]", + sv: "Svenska [sv]", + it: "Italiano [it]", +}; + +function getLangName(s: keyof LangsNames | string): string { + if (names[s]) return names[s]; + return String(s); +} + +export function LangSelector({ supportedLangs }: { supportedLangs: string[] }): VNode { + const [updatingLang, setUpdatingLang] = useState(false); + const { lang, changeLanguage } = useTranslationContext(); + const [hidden, setHidden] = useState(true); + + useEffect(() => { + function bodyKeyPress(event: KeyboardEvent) { + if (event.code === "Escape") setHidden(true); + } + function bodyOnClick(event: Event) { + setHidden(true); + } + document.body.addEventListener("click", bodyOnClick); + document.body.addEventListener("keydown", bodyKeyPress as any); + return () => { + document.body.removeEventListener("keydown", bodyKeyPress as any); + document.body.removeEventListener("click", bodyOnClick); + }; + }, []); + return ( +
    +
    + + + {!hidden && +
      + {supportedLangs + .filter((l) => l !== lang) + .map((lang) => ( +
    • { + changeLanguage(lang); + setUpdatingLang(false); + setHidden(true) + }} + > + {getLangName(lang)} + + + {/* */} + +
    • + ))} + +
    + } + +
    +
    + ); +} diff --git a/packages/web-util/src/components/Loading.tsx b/packages/web-util/src/components/Loading.tsx new file mode 100644 index 000000000..b567e9056 --- /dev/null +++ b/packages/web-util/src/components/Loading.tsx @@ -0,0 +1,45 @@ +/* + This file is part of GNU Taler + (C) 2022 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 { h, VNode } from "preact"; + +export function Loading(): VNode { + return ( +
    + +
    + ); +} + +export function Spinner(): VNode { + return ( +
    +
    +
    +
    +
    +
    + ); +} diff --git a/packages/web-util/src/components/ShowInputErrorLabel.tsx b/packages/web-util/src/components/ShowInputErrorLabel.tsx new file mode 100644 index 000000000..c5840cad9 --- /dev/null +++ b/packages/web-util/src/components/ShowInputErrorLabel.tsx @@ -0,0 +1,29 @@ +/* + This file is part of GNU Taler + (C) 2022 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 { Fragment, h, VNode } from "preact"; + +export function ShowInputErrorLabel({ + isDirty, + message, +}: { + message: string | undefined; + isDirty: boolean; +}): VNode { + if (message && isDirty) + return
    {message}
    ; + return
    ; +} diff --git a/packages/web-util/src/components/ShowLocalNotification.tsx b/packages/web-util/src/components/ShowLocalNotification.tsx new file mode 100644 index 000000000..cb947e536 --- /dev/null +++ b/packages/web-util/src/components/ShowLocalNotification.tsx @@ -0,0 +1,43 @@ +import { h, Fragment, VNode } from "preact"; +import { Attention } from "./Attention.js"; +import { Notification } from "../index.browser.js"; +// import { useSettings } from "../hooks/settings.js"; + +export function ShowLocalNotification({ notification }: { notification?: Notification }): VNode { + if (!notification) return + switch (notification.message.type) { + case "error": + return
    +
    + { + notification.remove() + }}> + {notification.message.description && +
    + {notification.message.description} +
    + } + {/* */} +
    +
    +
    + case "info": + return
    +
    + { + notification.remove(); + }} />
    + } +} + + +// function MaybeShowDebugInfo({ info }: { info: any }): VNode { +// const [settings] = useSettings() +// if (settings.showDebugInfo) { +// return
    +//       {info}
    +//     
    +// } +// return +// } + diff --git a/packages/web-util/src/components/index.ts b/packages/web-util/src/components/index.ts index 9441e971d..8344d4a7a 100644 --- a/packages/web-util/src/components/index.ts +++ b/packages/web-util/src/components/index.ts @@ -1 +1,8 @@ export * as utils from "./utils.js"; +export * from "./Attention.js"; +export * from "./CopyButton.js"; +export * from "./ErrorLoading.js"; +export * from "./LangSelector.js"; +export * from "./Loading.js"; +export * from "./ShowInputErrorLabel.js"; +export * from "./ShowLocalNotification.js"; diff --git a/packages/web-util/src/declaration.d.ts b/packages/web-util/src/declaration.d.ts new file mode 100644 index 000000000..c8ba3d576 --- /dev/null +++ b/packages/web-util/src/declaration.d.ts @@ -0,0 +1,35 @@ +/* + This file is part of GNU Taler + (C) 2022 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 + */ + +declare module "*.css" { + const mapping: Record; + export default mapping; +} +declare module "*.svg" { + const content: any; + export default content; +} +declare module "*.jpeg" { + const content: any; + export default content; +} +declare module "*.png" { + const content: any; + export default content; +} + +declare const __VERSION__: string; +declare const __GIT_HASH__: string; -- cgit v1.2.3