taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit 26c58e4212d92f8bb6948b612d62d90c82f2f41f
parent 6ea3ea72762e367ab8b86f13c013f31ad104b1f0
Author: Sebastian <sebasjm@gmail.com>
Date:   Mon, 14 Apr 2025 10:16:10 -0300

fix lass addr

Diffstat:
Mpackages/challenger-ui/src/Routing.tsx | 23+++++++++++++++--------
Mpackages/challenger-ui/src/pages/AnswerChallenge.tsx | 30++++++++++++++++++++++++------
Mpackages/challenger-ui/src/pages/AskChallenge.tsx | 165++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
3 files changed, 153 insertions(+), 65 deletions(-)

diff --git a/packages/challenger-ui/src/Routing.tsx b/packages/challenger-ui/src/Routing.tsx @@ -17,7 +17,8 @@ import { urlPattern, useCurrentLocation, - useNavigationContext + useNavigationContext, + useTranslationContext, } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; @@ -72,6 +73,7 @@ export function safeToURL(s: string | undefined): URL | undefined { function PublicRounting(): VNode { const loc = useCurrentLocation(publicPages); + const { i18n } = useTranslationContext(); const { navigateTo } = useNavigationContext(); useErrorBoundary((e) => { console.log("error", e); @@ -127,9 +129,9 @@ function PublicRounting(): VNode { if (!sessionId) { return ( <div> - one of the params is missing{" "} + <i18n.Translate>The application needs to be loaded with 4 request parameters. One or more are missing:</i18n.Translate> {JSON.stringify( - { clientId, redirectURL, state, nonce }, + { client_id: clientId, redirect_url: redirectURL?.href, state, nonce }, undefined, 2, )} @@ -161,7 +163,7 @@ function PublicRounting(): VNode { ); }} > - No nonce has been found + <i18n.Translate>No nonce has been found</i18n.Translate> </CheckChallengeIsUpToDate> ); } @@ -186,8 +188,12 @@ function PublicRounting(): VNode { if (!sessionId) { return ( <div> - one of the params is missing{" "} - {JSON.stringify(sessionId, undefined, 2)} + <i18n.Translate>The application needs to be loaded with 4 request parameters. One or more are missing:</i18n.Translate> + {JSON.stringify( + { client_id: clientId, redirect_url: redirectURL?.href, state, nonce }, + undefined, + 2, + )} </div> ); } @@ -234,15 +240,16 @@ function PublicRounting(): VNode { if (!sessionId) { return ( <div> - one of the params is missing{" "} + <i18n.Translate>The application needs to be loaded with 4 request parameters. One or more are missing:</i18n.Translate> {JSON.stringify( - { clientId, redirectURL, state, nonce }, + { client_id: clientId, redirect_url: redirectURL?.href, state, nonce }, undefined, 2, )} </div> ); } + return ( <AnswerChallenge focus diff --git a/packages/challenger-ui/src/pages/AnswerChallenge.tsx b/packages/challenger-ui/src/pages/AnswerChallenge.tsx @@ -18,7 +18,8 @@ import { EmptyObject, HttpStatusCode, TalerError, - assertUnreachable, + ChallengerApi, + assertUnreachable } from "@gnu-taler/taler-util"; import { Attention, @@ -38,6 +39,7 @@ import { useChallengeSession, } from "../hooks/challenge.js"; import { SessionId, useSessionState } from "../hooks/session.js"; +import { TalerFormAttributes } from "@gnu-taler/taler-util"; type Props = { focus?: boolean; @@ -64,6 +66,24 @@ function useReloadOnDeadline(deadline: AbsoluteTime): void { }, [deadline]); } +export function getAddressDescriptionFromAddrType(type: ChallengerApi.ChallengerTermsOfServiceResponse["address_type"], addr: Record<string,string>): string { + switch (type) { + case "email":{ + return addr[TalerFormAttributes.CONTACT_EMAIL]; + } + case "phone": { + return addr[TalerFormAttributes.CONTACT_PHONE]; + } + case "postal": { + return addr[TalerFormAttributes.ADDRESS_ZIPCODE]; + } + case "postal-ch": { + return addr[TalerFormAttributes.ADDRESS_ZIPCODE]; + + } + } +} + export function AnswerChallenge({ session, focus, @@ -79,8 +99,6 @@ export function AnswerChallenge({ pin: !pin ? i18n.str`Can't be empty` : undefined, }); - const restrictionKey = config.address_type; - const result = useChallengeSession(session); const lastStatus = @@ -96,12 +114,12 @@ export function AnswerChallenge({ useReloadOnDeadline(deadline ?? AbsoluteTime.never()); const lastAddr = - !restrictionKey || !lastStatus?.last_address + !lastStatus?.last_address ? undefined - : lastStatus.last_address[restrictionKey]; + : getAddressDescriptionFromAddrType(config.address_type, lastStatus.last_address); const unableToChangeAddr = !lastStatus || lastStatus.changes_left < 1; - const contact = lastAddr ? { [restrictionKey]: lastAddr } : undefined; + const contact = lastStatus?.last_address; const onSendAgain = contact === undefined || diff --git a/packages/challenger-ui/src/pages/AskChallenge.tsx b/packages/challenger-ui/src/pages/AskChallenge.tsx @@ -38,15 +38,16 @@ import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; import { useChallengeSession } from "../hooks/challenge.js"; import { SessionId, useSessionState } from "../hooks/session.js"; -import { doAutoFocus } from "./AnswerChallenge.js"; +import { + doAutoFocus, + getAddressDescriptionFromAddrType, +} from "./AnswerChallenge.js"; import { ErrorLoadingWithDebug } from "./ErrorLoadingWithDebug.js"; import { ChallengerApi } from "@gnu-taler/taler-util"; import { TalerFormAttributes } from "@gnu-taler/taler-util"; import { InternationalizationAPI } from "@gnu-taler/taler-util"; import { assertUnreachable } from "@gnu-taler/taler-util"; -export const EMAIL_REGEX = /^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/; - type Props = { onSendSuccesful: () => void; session: SessionId; @@ -137,58 +138,20 @@ export function AskChallenge({ const lastStatus = result.body; - const restrictionKey = config.address_type; - const restriction = !config.restrictions - ? undefined - : config.restrictions[restrictionKey]; - const regexText = - restriction && restriction.regex ? restriction.regex : undefined; - const restrictionHint = - restriction && restriction.hint - ? restriction.hint - : i18n.str`invalid field`; - - let restrictionRG; - if (regexText) { - try { - restrictionRG = new RegExp(regexText); - } catch (e) { - return ( - <Attention title={i18n.str`Server configuration error`} type="danger"> - <i18n.Translate> - Invalid server regular expression configuration. Server restriction - is "{regexText}" but it didn't compile: {String(e)} - </i18n.Translate> - </Attention> - ); - } - } else { - restrictionRG = EMAIL_REGEX; - } - - const design = getFormDesignBasedOnAddressType(i18n, config.address_type); + const design = getFormDesignBasedOnAddressType( + i18n, + config.address_type, + config.restrictions ?? {}, + lastStatus.last_address ?? {} + ); const form = useForm(design, lastStatus.last_address ?? {}); const prevAddr = !lastStatus?.last_address ? undefined - : lastStatus.last_address[restrictionKey]; - - // const errors = undefinedIfEmpty({ - // address: !address - // ? i18n.str`required` - // : !restrictionRG.test(address) - // ? restrictionHint - // : prevAddr !== undefined && address === prevAddr - // ? i18n.str`can't use the same address` - // : undefined, - // }); - - // const contact = address ? { [restrictionKey]: address } : undefined; - - // const usableAddrs = - // !state?.lastAddress || !state.lastAddress.length - // ? [] - // : state.lastAddress.filter((d) => !!d.address[restrictionKey]); + : getAddressDescriptionFromAddrType( + config.address_type, + lastStatus.last_address, + ); const contact = form.status.status === "fail" @@ -532,10 +495,53 @@ Grunerstraße 1 12345 City_name country_name `; +export const EMAIL_REGEX = /^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/; +export const PHONE_REGEX = /^(\+\d{1,2}\s?)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/; +export const CONTACT_REGEX = /.*/ +export const ZIPCODE_REGEX = /.*/ +export const ADDR_LINES_REGEX = /.*/ + + function getFormDesignBasedOnAddressType( i18n: InternationalizationAPI, type: ChallengerApi.ChallengerTermsOfServiceResponse["address_type"], + restrictions: Record<string, ChallengerApi.Restriction | undefined>, + prevValue: Record<string, string>, ): FormDesign { + + function getRestriction( + serverConfig: ChallengerApi.Restriction | undefined, + fallback?: RegExp, + ): { regex: undefined | RegExp; hint: TranslatedString } { + const regexText = + serverConfig && serverConfig.regex ? serverConfig.regex : undefined; + const hint = + serverConfig && serverConfig.hint + ? serverConfig.hint as TranslatedString + : i18n.str`Invalid field`; + + let regex; + if (regexText) { + try { + regex = new RegExp(regexText); + } catch (e) { + console.error(`Invalid server regular expression configuration. Server restriction + is "${regexText}" but it didn't compile: ${String(e)}`); + // return ( + // <Attention title={i18n.str`Server configuration error`} type="danger"> + // <i18n.Translate> + // Invalid server regular expression configuration. Server + // restriction is "{regexText}" but it didn't compile: {String(e)} + // </i18n.Translate> + // </Attention> + // ); + } + regex = fallback; + } else { + regex = fallback; + } + return { regex, hint }; + } switch (type) { case "email": return { @@ -546,6 +552,17 @@ function getFormDesignBasedOnAddressType( id: TalerFormAttributes.CONTACT_EMAIL, required: true, label: i18n.str`Email`, + validator(text) { + const restriction = getRestriction(restrictions[TalerFormAttributes.CONTACT_EMAIL], EMAIL_REGEX) + if (!restriction.regex?.test(text)) { + return restriction.hint + } + const prev = prevValue[TalerFormAttributes.CONTACT_EMAIL] + if (prev === text) { + return i18n.str`Can't use the same address` + } + return undefined + }, }, ], }; @@ -558,6 +575,17 @@ function getFormDesignBasedOnAddressType( id: TalerFormAttributes.CONTACT_PHONE, required: true, label: i18n.str`Phone`, + validator(text) { + const restriction = getRestriction(restrictions[TalerFormAttributes.CONTACT_PHONE], PHONE_REGEX) + if (!restriction.regex?.test(text)) { + return restriction.hint + } + const prev = prevValue[TalerFormAttributes.CONTACT_PHONE] + if (prev === text) { + return i18n.str`Can't use the same number` + } + return undefined + }, }, ], }; @@ -571,6 +599,13 @@ function getFormDesignBasedOnAddressType( required: true, label: i18n.str`Contact name`, placeholder: i18n.str`Person full name or name of the business`, + validator(text) { + const restriction = getRestriction(restrictions[TalerFormAttributes.CONTACT_NAME], CONTACT_REGEX) + if (!restriction.regex?.test(text)) { + return restriction.hint + } + return undefined + }, }, { type: "textArea", @@ -578,6 +613,13 @@ function getFormDesignBasedOnAddressType( required: true, label: i18n.str`Address`, placeholder: ADDRESS_EXAMPLE_INTERNATIONAL, + validator(text) { + const restriction = getRestriction(restrictions[TalerFormAttributes.ADDRESS_LINES], ADDR_LINES_REGEX) + if (!restriction.regex?.test(text)) { + return restriction.hint + } + return undefined + }, }, { id: TalerFormAttributes.ADDRESS_COUNTRY, @@ -586,6 +628,13 @@ function getFormDesignBasedOnAddressType( choices: countryNameList(i18n), required: true, preferredChoiceVals: ["CH", "DE"], + validator(text) { + const restriction = getRestriction(restrictions[TalerFormAttributes.ADDRESS_COUNTRY]) + if (!restriction.regex?.test(text)) { + return restriction.hint + } + return undefined + }, }, ], }; @@ -600,6 +649,13 @@ function getFormDesignBasedOnAddressType( required: true, label: i18n.str`Contact name`, placeholder: i18n.str`Your full name`, + validator(text) { + const restriction = getRestriction(restrictions[TalerFormAttributes.CONTACT_PERSON_NAME]) + if (!restriction.regex?.test(text)) { + return restriction.hint + } + return undefined + }, }, { type: "textArea", @@ -607,6 +663,13 @@ function getFormDesignBasedOnAddressType( required: true, label: i18n.str`Address`, placeholder: ADDRESS_EXAMPLE_CH, + validator(text) { + const restriction = getRestriction(restrictions[TalerFormAttributes.ADDRESS_LINES]) + if (!restriction.regex?.test(text)) { + return restriction.hint + } + return undefined + }, }, ], };