gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: account creation and show log


From: gnunet
Subject: [taler-wallet-core] branch master updated: account creation and show login when required
Date: Mon, 04 Dec 2023 17:54:24 +0100

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new 7f2170057 account creation and show login when required
7f2170057 is described below

commit 7f21700576b4de0f5479ea258b75fb141d18a41b
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Mon Dec 4 13:45:03 2023 -0300

    account creation and show login when required
---
 packages/demobank-ui/src/pages/LoginForm.tsx       |  22 +--
 packages/demobank-ui/src/pages/WireTransfer.tsx    |   4 +-
 .../src/pages/WithdrawalConfirmationQuestion.tsx   |  17 ++-
 .../src/pages/account/ShowAccountDetails.tsx       |   4 +-
 .../demobank-ui/src/pages/admin/AccountForm.tsx    | 167 +++++++++++++--------
 .../demobank-ui/src/pages/admin/RemoveAccount.tsx  |   4 +-
 .../src/pages/business/CreateCashout.tsx           |   4 +-
 packages/demobank-ui/src/utils.ts                  |  37 +++--
 8 files changed, 155 insertions(+), 104 deletions(-)

diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx 
b/packages/demobank-ui/src/pages/LoginForm.tsx
index 02ec75dbf..e0ff77417 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -28,9 +28,9 @@ import { assertUnreachable } from 
"./WithdrawalOperationPage.js";
 /**
  * Collect and submit login data.
  */
-export function LoginForm({ reason, onRegister }: { reason?: "not-found" | 
"forbidden", onRegister?: () => void }): VNode {
+export function LoginForm({ currentUser, fixedUser, onRegister }: { 
fixedUser?: boolean, currentUser?: string, onRegister?: () => void }): VNode {
   const backend = useBackendState();
-  const currentUser = backend.state.status !== "loggedOut" ? 
backend.state.username : undefined
+
   const [username, setUsername] = useState<string | undefined>(currentUser);
   const [password, setPassword] = useState<string | undefined>();
   const { i18n } = useTranslationContext();
@@ -38,21 +38,11 @@ export function LoginForm({ reason, onRegister }: { 
reason?: "not-found" | "forb
   const [notification, notify, handleError] = useLocalNotification()
   const {config} = useBankCoreApiContext();
 
-  /** 
-   * Register form may be shown in the initialization step.
-   * If no register handler then this is invoke
-   * to show a session expired or unauthorized
-   */
-  const isLogginAgain = !onRegister
-
   const ref = useRef<HTMLInputElement>(null);
   useEffect(function focusInput() {
-    if (isLogginAgain && backend.state.status !== "expired") {
-      backend.expired()
-      window.location.reload()
-    }
     ref.current?.focus();
   }, []);
+
   const [busy, setBusy] = useState<Record<string, undefined>>()
 
   const errors = undefinedIfEmpty({
@@ -128,7 +118,7 @@ export function LoginForm({ reason, onRegister }: { 
reason?: "not-found" | "forb
                 id="username"
                 class="block w-full disabled:bg-gray-200 rounded-md border-0 
py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 
placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 
sm:text-sm sm:leading-6"
                 value={username ?? ""}
-                disabled={isLogginAgain}
+                disabled={fixedUser}
                 enterkeyhint="next"
                 placeholder="identification"
                 autocomplete="username"
@@ -170,7 +160,7 @@ export function LoginForm({ reason, onRegister }: { 
reason?: "not-found" | "forb
             </div>
           </div>
 
-          {isLogginAgain ? <div class="flex justify-between">
+          {currentUser ? <div class="flex justify-between">
             <button type="submit"
               class="rounded-md bg-white-600 px-3 py-1.5 text-sm font-semibold 
leading-6 text-black shadow-sm hover:bg-gray-100 focus-visible:outline 
focus-visible:outline-2 focus-visible:outline-offset-2 
focus-visible:outline-gray-600"
               onClick={(e) => {
@@ -189,7 +179,7 @@ export function LoginForm({ reason, onRegister }: { 
reason?: "not-found" | "forb
                 doLogin()
               }}
             >
-              <i18n.Translate>Renew session</i18n.Translate>
+              <i18n.Translate>Check</i18n.Translate>
             </button>
           </div> : <div>
             <button type="submit"
diff --git a/packages/demobank-ui/src/pages/WireTransfer.tsx 
b/packages/demobank-ui/src/pages/WireTransfer.tsx
index a68c085c9..88bdc70a6 100644
--- a/packages/demobank-ui/src/pages/WireTransfer.tsx
+++ b/packages/demobank-ui/src/pages/WireTransfer.tsx
@@ -24,8 +24,8 @@ export function WireTransfer({ toAccount, onRegister, 
onCancel, onSuccess }: { o
   }
   if (result.type === "fail") {
     switch (result.case) {
-      case "unauthorized": return <LoginForm reason="forbidden" />
-      case "not-found": return <LoginForm reason="not-found" />
+      case "unauthorized": return <LoginForm currentUser={account}/>
+      case "not-found": return <LoginForm currentUser={account} />
       default: assertUnreachable(result)
     }
   }
diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx 
b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
index f8913f0ec..3af619c2d 100644
--- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
@@ -47,6 +47,7 @@ import { useWithdrawalDetails } from "../hooks/access.js";
 import { OperationState } from "./OperationState/index.js";
 import { OperationNotFound } from "./WithdrawalQRCode.js";
 import { ErrorLoadingWithDebug } from "../components/ErrorLoadingWithDebug.js";
+import { LoginForm } from "./LoginForm.js";
 
 const logger = new Logger("WithdrawalConfirmationQuestion");
 
@@ -340,16 +341,16 @@ export function ShouldBeSameUser({ username, children }: 
{ username: string, chi
   const { state: credentials } = useBackendState();
   const { i18n } = useTranslationContext()
   if (credentials.status === "loggedOut") {
-    return <Attention type="info" title={i18n.str`Authentication required`}>
-      <p>You should login as "{username}"</p>
-    </Attention>
+    return <Fragment>
+      <Attention type="info" title={i18n.str`Authentication required`} />
+      <LoginForm currentUser={username} fixedUser/>
+    </Fragment>
   }
   if (credentials.username !== username) {
-    return <Attention type="warning" title={i18n.str`This operation was 
created with other username`}>
-      <p>
-        You can switch to account "{username}" and complete the operation.
-      </p>
-    </Attention>
+    return <Fragment>
+      <Attention type="warning" title={i18n.str`This operation was created 
with other username`} />
+      <LoginForm currentUser={username} fixedUser/>
+    </Fragment>
   }
   return <Fragment>
     {children}
diff --git a/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx 
b/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx
index 06a88c1c6..d435673a2 100644
--- a/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx
+++ b/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx
@@ -44,8 +44,8 @@ export function ShowAccountDetails({
   }
   if (result.type === "fail") {
     switch (result.case) {
-      case "not-found": return <LoginForm reason="not-found" />
-      case "unauthorized": return <LoginForm reason="forbidden" />
+      case "not-found": return <LoginForm currentUser={account} />
+      case "unauthorized": return <LoginForm currentUser={account} />
       default: assertUnreachable(result)
     }
   }
diff --git a/packages/demobank-ui/src/pages/admin/AccountForm.tsx 
b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
index c8abde74b..e76204a81 100644
--- a/packages/demobank-ui/src/pages/admin/AccountForm.tsx
+++ b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
@@ -1,8 +1,8 @@
-import { AmountString, Amounts, PaytoString, TalerCorebankApi, buildPayto, 
parsePaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
+import { AmountString, Amounts, ChallengeContactData, PaytoString, 
TalerCorebankApi, TranslatedString, buildPayto, parsePaytoUri, 
stringifyPaytoUri } from "@gnu-taler/taler-util";
 import { CopyButton, ShowInputErrorLabel, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { ComponentChildren, Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
-import { PartialButDefined, RecursivePartial, WithIntermediate, 
undefinedIfEmpty, validateIBAN } from "../../utils.js";
+import { ErrorMessageMappingFor, PartialButDefined, RecursivePartial, 
WithIntermediate, undefinedIfEmpty, validateIBAN } from "../../utils.js";
 import { InputAmount, doAutoFocus } from "../PaytoWireTransferForm.js";
 import { assertUnreachable } from "../WithdrawalOperationPage.js";
 import { useBackendContext } from "../../context/backend.js";
@@ -14,9 +14,14 @@ const EMAIL_REGEX =
   
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
 const REGEX_JUST_NUMBERS_REGEX = /^\+[0-9 ]*$/;
 
-export type AccountFormData = TalerCorebankApi.AccountData & { username: 
string }
+export type AccountFormData = TalerCorebankApi.AccountData & { 
+  username: string,
+  debitAmount: string,
+  isExchange: boolean,
+  isPublic: boolean,
+}
 
-type MM = {
+type ChangeByPurposeType = {
   "create": (a: TalerCorebankApi.RegisterAccountRequest | undefined) => void,
   "update": (a: TalerCorebankApi.AccountReconfiguration | undefined) => void,
   "show": undefined
@@ -29,12 +34,13 @@ type MM = {
  * @param param0
  * @returns
  */
-export function AccountForm<T extends keyof MM>({
+export function AccountForm<PurposeType extends keyof ChangeByPurposeType>({
   template,
   username,
   purpose,
   onChange,
   focus,
+  admin,
   noCashout,
   children,
 }: {
@@ -44,31 +50,27 @@ export function AccountForm<T extends keyof MM>({
   noCashout?: boolean,
   admin?: boolean,
   template: TalerCorebankApi.AccountData | undefined;
-  onChange: MM[T];
-  purpose: T;
+  onChange: ChangeByPurposeType[PurposeType];
+  purpose: PurposeType;
 }): VNode {
   const initial = initializeFromTemplate(username, template);
   const [form, setForm] = useState(initial);
   const [errors, setErrors] = useState<
-    RecursivePartial<typeof initial> | undefined
+    ErrorMessageMappingFor<typeof initial> | undefined
   >(undefined);
   const { i18n } = useTranslationContext();
 
   const { config } = useBankCoreApiContext()
-  const [debitAmount, setDebitAmount] = useState<string>()
-
-  const [isExchange, setIsExchange] = useState<boolean>();
-  const [isPublic, setIsPublic] = useState<boolean>();
 
   function updateForm(newForm: typeof initial): void {
     const parsed = !newForm.cashout_payto_uri
       ? undefined
       : buildPayto("iban", newForm.cashout_payto_uri, undefined);;
 
-    const trimmedAmountStr = debitAmount?.trim();
+    const trimmedAmountStr = newForm.debitAmount?.trim();
     const parsedAmount = 
Amounts.parse(`${config.currency}:${trimmedAmountStr}`);
 
-    const errors = undefinedIfEmpty<RecursivePartial<typeof initial>>({
+    const errors = undefinedIfEmpty<ErrorMessageMappingFor<typeof initial>>({
       cashout_payto_uri: (!newForm.cashout_payto_uri
         ? undefined
         : !parsed
@@ -77,7 +79,7 @@ export function AccountForm<T extends keyof MM>({
             ? i18n.str`only "IBAN" target are supported`
             : !IBAN_REGEX.test(parsed.iban)
               ? i18n.str`IBAN should have just uppercased letters and numbers`
-              : validateIBAN(parsed.iban, i18n)) as PaytoString,
+              : validateIBAN(parsed.iban, i18n)),
       contact_data: undefinedIfEmpty({
         email: !newForm.contact_data?.email
           ? undefined
@@ -93,7 +95,7 @@ export function AccountForm<T extends keyof MM>({
               : undefined,
       }),
       debit_threshold: !trimmedAmountStr
-        ? i18n.str`required`
+        ? (purpose === "create" ? i18n.str`required` : undefined)
         : !parsedAmount
           ? i18n.str`not valid`
           : Amounts.isZero(parsedAmount)
@@ -111,8 +113,14 @@ export function AccountForm<T extends keyof MM>({
     } else {
       const cashout = !newForm.cashout_payto_uri ? undefined : 
buildPayto("iban", newForm.cashout_payto_uri, undefined)
       const cashoutURI = !cashout ? undefined : stringifyPaytoUri(cashout)
+
+      const internal = !newForm.payto_uri ? undefined : buildPayto("iban", 
newForm.payto_uri, undefined);
+      const internalURI = !internal ? undefined : stringifyPaytoUri(internal)
+
       switch (purpose) {
         case "create": {
+          //typescript doesn't correctly narrow a generic type
+          const callback = onChange as ChangeByPurposeType["create"]
           const result: TalerCorebankApi.RegisterAccountRequest = {
             cashout_payto_uri: cashoutURI,
             name: newForm.name!,
@@ -123,20 +131,36 @@ export function AccountForm<T extends keyof MM>({
               phone: newForm.contact_data?.phone,
             }),
             debit_threshold: newForm.debit_threshold as AmountString,
-            // ,
-            // internal_payto_uri
+            internal_payto_uri: internalURI,
+            is_public: newForm.isPublic,
+            is_taler_exchange: newForm.isExchange,
           }
-          onChange(result)
+          callback(result)
           return;
         }
         case "update": {
+          //typescript doesn't correctly narrow a generic type
+          const callback = onChange as ChangeByPurposeType["update"]
           const result: TalerCorebankApi.AccountReconfiguration = {
-            cashout_payto_uri: cashoutURI
+            cashout_payto_uri: cashoutURI,
+            challenge_contact_data: undefinedIfEmpty({
+              email: newForm.contact_data?.email,
+              phone: newForm.contact_data?.phone,
+            }),
+            debit_threshold: newForm.debit_threshold as AmountString,
+            is_taler_exchange: newForm.isExchange,
+            name: newForm.name
+            // is_public: newForm.isPublic
           }
-          onChange(result as any)
+          callback(result)
           return;
         }
-        case "show":
+        case "show": {
+          return;
+        }
+        default: {
+          assertUnreachable(purpose)
+        }
       }
     }
   }
@@ -203,7 +227,7 @@ export function AccountForm<T extends keyof MM>({
                 name="name"
                 data-error={!!errors?.name && form.name !== undefined}
                 id="name"
-                disabled={purpose === "show"}
+                disabled={purpose !== "create"}
                 value={form.name ?? ""}
                 onChange={(e) => {
                   form.name = e.currentTarget.value;
@@ -332,53 +356,70 @@ export function AccountForm<T extends keyof MM>({
             </div>
           }
 
-          <div class="sm:col-span-5">
-            <label for="debit" class="block text-sm font-medium leading-6 
text-gray-900">{i18n.str`Max debt`}</label>
-            <InputAmount
-              name="debit"
-              left
-              currency={config.currency}
-              value={debitAmount ?? ""}
-              onChange={(e) => {
-                setDebitAmount(e);
-              }}
-            />
-            <ShowInputErrorLabel
-              message={errors?.debit_threshold ? 
String(errors?.debit_threshold) : undefined}
-              isDirty={form.debit_threshold !== undefined}
-            />
-            <p class="mt-2 text-sm text-gray-500" >allow user debt</p>
-          </div>
+          {admin ? <Fragment>
+            <div class="sm:col-span-5">
+              <label for="debit" class="block text-sm font-medium leading-6 
text-gray-900">{i18n.str`Max debt`}</label>
+              <InputAmount
+                name="debit"
+                left
+                currency={config.currency}
+                value={form.debitAmount ?? ""}
+                onChange={(e) => {
+                  form.debitAmount = e
+                  updateForm(structuredClone(form))
+                }}
+              />
+              <ShowInputErrorLabel
+                message={errors?.debit_threshold ? 
String(errors?.debit_threshold) : undefined}
+                isDirty={form.debit_threshold !== undefined}
+              />
+              <p class="mt-2 text-sm text-gray-500" >how much is user able to 
transfer </p>
+            </div>
 
-          <div class="sm:col-span-5">
-            <div class="flex items-center justify-between">
-              <span class="flex flex-grow flex-col">
-                <span class="text-sm text-black font-medium leading-6 " 
id="availability-label">
-                  <i18n.Translate>Is an exchange</i18n.Translate>
+            <div class="sm:col-span-5">
+              <div class="flex items-center justify-between">
+                <span class="flex flex-grow flex-col">
+                  <span class="text-sm text-black font-medium leading-6 " 
id="availability-label">
+                    <i18n.Translate>Is an exchange</i18n.Translate>
+                  </span>
                 </span>
-              </span>
-              <button type="button" data-enabled={!!isExchange} 
class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 
w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent 
transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 
focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" 
aria-labelledby="availability-label" aria-describedby="availability-description"
+                <button type="button" data-enabled={!!form.isExchange} 
class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 
w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent 
transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 
focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" 
aria-labelledby="availability-label" aria-describedby="availability-description"
 
-                onClick={() => setIsExchange(!isExchange)}>
-                <span aria-hidden="true" data-enabled={!!isExchange} 
class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none 
inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition 
duration-200 ease-in-out"></span>
-              </button>
+                  onClick={() => {
+                    form.isExchange = !form.isExchange
+                    updateForm(structuredClone(form))
+                  }}>
+                  <span aria-hidden="true" data-enabled={!!form.isExchange} 
class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none 
inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition 
duration-200 ease-in-out"></span>
+                </button>
+              </div>
             </div>
-          </div>
+          </Fragment> :
+            undefined
+          }
 
-          <div class="sm:col-span-5">
-            <div class="flex items-center justify-between">
-              <span class="flex flex-grow flex-col">
-                <span class="text-sm text-black font-medium leading-6 " 
id="availability-label">
-                  <i18n.Translate>Is public</i18n.Translate>
+          {purpose === "create" ?
+            <div class="sm:col-span-5">
+              <div class="flex items-center justify-between">
+                <span class="flex flex-grow flex-col">
+                  <span class="text-sm text-black font-medium leading-6 " 
id="availability-label">
+                    <i18n.Translate>Is public</i18n.Translate>
+                  </span>
                 </span>
-              </span>
-              <button type="button" data-enabled={!!isPublic} 
class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 
w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent 
transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 
focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" 
aria-labelledby="availability-label" aria-describedby="availability-description"
+                <button type="button" data-enabled={!!form.isPublic} 
class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 
w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent 
transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 
focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" 
aria-labelledby="availability-label" aria-describedby="availability-description"
 
-                onClick={() => setIsPublic(!isPublic)}>
-                <span aria-hidden="true" data-enabled={!!isPublic} 
class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none 
inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition 
duration-200 ease-in-out"></span>
-              </button>
+                  onClick={() => {
+                    form.isPublic = !form.isPublic
+                    updateForm(structuredClone(form))
+                  }}>
+                  <span aria-hidden="true" data-enabled={!!form.isPublic} 
class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none 
inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition 
duration-200 ease-in-out"></span>
+                </button>
+              </div>
+              <p class="mt-2 text-sm text-gray-500" >
+                <i18n.Translate>public accounts have their balance publicly 
accesible</i18n.Translate>
+              </p>
             </div>
-          </div>
+            : undefined
+          }
 
         </div>
       </div>
@@ -386,7 +427,7 @@ export function AccountForm<T extends keyof MM>({
     </form>
   );
 }
-
+// JNTMECG7RM3AAQB6SRAZNWDSM8
 function initializeFromTemplate(
   username: string | undefined,
   account: TalerCorebankApi.AccountData | undefined,
diff --git a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx 
b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
index 5ee887128..57144177c 100644
--- a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
+++ b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
@@ -39,8 +39,8 @@ export function RemoveAccount({
   }
   if (result.type === "fail") {
     switch (result.case) {
-      case "unauthorized": return <LoginForm reason="forbidden" />
-      case "not-found": return <LoginForm reason="not-found" />
+      case "unauthorized": return <LoginForm currentUser={account} />
+      case "not-found": return <LoginForm currentUser={account} />
       default: assertUnreachable(result)
     }
   }
diff --git a/packages/demobank-ui/src/pages/business/CreateCashout.tsx 
b/packages/demobank-ui/src/pages/business/CreateCashout.tsx
index b2ff41e63..ce1a6cf49 100644
--- a/packages/demobank-ui/src/pages/business/CreateCashout.tsx
+++ b/packages/demobank-ui/src/pages/business/CreateCashout.tsx
@@ -102,8 +102,8 @@ export function CreateCashout({
   }
   if (resultAccount.type === "fail") {
     switch (resultAccount.case) {
-      case "unauthorized": return <LoginForm reason="forbidden" />
-      case "not-found": return <LoginForm reason="not-found" />
+      case "unauthorized": return <LoginForm currentUser={accountName} />
+      case "not-found": return <LoginForm currentUser={accountName} />
       default: assertUnreachable(resultAccount)
     }
   }
diff --git a/packages/demobank-ui/src/utils.ts 
b/packages/demobank-ui/src/utils.ts
index 805d68660..7cdd8a861 100644
--- a/packages/demobank-ui/src/utils.ts
+++ b/packages/demobank-ui/src/utils.ts
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { HttpStatusCode, TalerError, TalerErrorCode, TranslatedString } from 
"@gnu-taler/taler-util";
+import { AmountString, HttpStatusCode, PaytoString, TalerError, 
TalerErrorCode, TranslatedString } from "@gnu-taler/taler-util";
 import {
   ErrorNotification,
   ErrorType,
@@ -62,17 +62,36 @@ export type PartialButDefined<T> = {
   [P in keyof T]: T[P] | undefined;
 };
 
-export type WithIntermediate<Type extends object> = {
-  [prop in keyof Type]: Type[prop] extends object
+/**
+ * every non-map field can be undefined
+ */
+export type WithIntermediate<Type> = {
+  [prop in keyof Type]:
+  Type[prop] extends PaytoString ? Type[prop] | undefined :
+  Type[prop] extends AmountString ? Type[prop] | undefined :
+  Type[prop] extends TranslatedString ? Type[prop] | undefined :
+  Type[prop] extends object
   ? WithIntermediate<Type[prop]>
   : Type[prop] | undefined;
 };
-export type RecursivePartial<T> = {
-  [P in keyof T]?: T[P] extends (infer U)[]
+export type RecursivePartial<Type> = {
+  [P in keyof Type]?: Type[P] extends (infer U)[]
   ? RecursivePartial<U>[]
-  : T[P] extends object
-  ? RecursivePartial<T[P]>
-  : T[P];
+  : Type[P] extends object
+  ? RecursivePartial<Type[P]>
+  : Type[P];
+};
+export type ErrorMessageMappingFor<Type> = {
+  [prop in keyof Type]+?:
+  //enumerate known object
+  Exclude<Type[prop],undefined> extends PaytoString ? TranslatedString :
+  Exclude<Type[prop],undefined> extends AmountString ? TranslatedString :
+  Exclude<Type[prop],undefined> extends TranslatedString ? TranslatedString :
+  // arrays: every element
+  Exclude<Type[prop],undefined> extends (infer U)[] ? 
ErrorMessageMappingFor<U>[] : 
+  // map: every field
+  Exclude<Type[prop],undefined> extends object ? 
ErrorMessageMappingFor<Type[prop]>
+  : TranslatedString;
 };
 
 export enum TanChannel {
@@ -337,7 +356,7 @@ export const COUNTRY_TABLE = {
 export function validateIBAN(
   iban: string,
   i18n: ReturnType<typeof useTranslationContext>["i18n"],
-): string | undefined {
+): TranslatedString | undefined {
   // Check total length
   if (iban.length < 4)
     return i18n.str`IBAN numbers usually have more that 4 digits`;

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]