[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-wallet-core] branch master updated (2d2873b38 -> 351096408)
From: |
gnunet |
Subject: |
[taler-wallet-core] branch master updated (2d2873b38 -> 351096408) |
Date: |
Sun, 05 Jan 2025 22:15:47 +0100 |
This is an automated email from the git hooks/post-receive script.
sebasjm pushed a change to branch master
in repository wallet-core.
from 2d2873b38 show error when link/challenger failed
new f856b4e78 onclick logic should be in the notification handler
new aa88f573b ack when /measure returns forbidden
new 9e3af6e18 remove name from search form
new 351096408 handle new measure error code
The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../aml-backoffice-ui/src/pages/CaseDetails.tsx | 42 +++++++++-
.../aml-backoffice-ui/src/pages/CaseUpdate.tsx | 95 +++++++++++++++++++---
packages/aml-backoffice-ui/src/pages/Measures.tsx | 24 ++++++
packages/aml-backoffice-ui/src/pages/Search.tsx | 18 +---
packages/taler-util/src/http-client/exchange.ts | 6 +-
packages/web-util/src/components/Button.tsx | 67 ++-------------
packages/web-util/src/hooks/useNotifications.ts | 95 +++++++++++++++++-----
7 files changed, 238 insertions(+), 109 deletions(-)
diff --git a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
index 8a2dca1bf..d9b007d1e 100644
--- a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
+++ b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
@@ -68,6 +68,7 @@ import { useOfficer } from "../hooks/officer.js";
import { getShapeFromFields, useFormState } from "../hooks/form.js";
import { privatePages } from "../Routing.js";
import { CurrentMeasureTable, MeasureInfo } from "./MeasuresTable.js";
+import { Officer } from "./Officer.js";
export type AmlEvent =
| AmlFormEvent
@@ -758,6 +759,25 @@ function ShowMesaureInfo({
if (measures instanceof TalerError) {
return <ErrorLoadingWithDebug error={measures} />;
}
+ if (measures.type === "fail") {
+ switch (measures.case) {
+ // case HttpStatusCode.Unauthorized:
+ case HttpStatusCode.Forbidden:
+ return (
+ <Fragment>
+ <Attention type="danger" title={i18n.str`Operation denied`}>
+ <i18n.Translate>
+ This account signature is wrong, contact administrator or
create
+ a new one.
+ </i18n.Translate>
+ </Attention>
+ <Officer />
+ </Fragment>
+ );
+ default:
+ assertUnreachable(measures.case);
+ }
+ }
const summary: TalerExchangeApi.AvailableMeasureSummary = measures.body;
const map: { [d: string]: MeasureInfo } = {};
@@ -1530,14 +1550,32 @@ function ShowMeasuresToSelect({
onSelect?: (m: MeasureInfo) => void;
}): VNode {
const measures = useServerMeasures();
-
+ const { i18n } = useTranslationContext();
if (!measures) {
return <Loading />;
}
if (measures instanceof TalerError) {
return <ErrorLoadingWithDebug error={measures} />;
}
-
+ if (measures.type === "fail") {
+ switch (measures.case) {
+ // case HttpStatusCode.Unauthorized:
+ case HttpStatusCode.Forbidden:
+ return (
+ <Fragment>
+ <Attention type="danger" title={i18n.str`Operation denied`}>
+ <i18n.Translate>
+ This account signature is wrong, contact administrator or
create
+ a new one.
+ </i18n.Translate>
+ </Attention>
+ <Officer />
+ </Fragment>
+ );
+ default:
+ assertUnreachable(measures.case);
+ }
+ }
const list = Object.entries(measures.body.roots).map(
([key, value]): MeasureInfo => {
if (value.check_name !== "SKIP") {
diff --git a/packages/aml-backoffice-ui/src/pages/CaseUpdate.tsx
b/packages/aml-backoffice-ui/src/pages/CaseUpdate.tsx
index 87f1aed5f..a268aaa89 100644
--- a/packages/aml-backoffice-ui/src/pages/CaseUpdate.tsx
+++ b/packages/aml-backoffice-ui/src/pages/CaseUpdate.tsx
@@ -109,13 +109,28 @@ export function CaseUpdate({
const shape: Array<UIHandlerId> = [];
const requiredFields: Array<UIHandlerId> = [];
- theForm.config.design.forEach((section) => {
- Array.prototype.push.apply(shape, getShapeFromFields(section.fields));
- Array.prototype.push.apply(
- requiredFields,
- getRequiredFields(section.fields),
- );
- });
+ switch (theForm.config.type) {
+ case "double-column": {
+ theForm.config.design.forEach((section) => {
+ Array.prototype.push.apply(shape, getShapeFromFields(section.fields));
+ Array.prototype.push.apply(
+ requiredFields,
+ getRequiredFields(section.fields),
+ );
+ });
+ break;
+ }
+ case "single-column": {
+ Array.prototype.push.apply(
+ shape,
+ getShapeFromFields(theForm.config.fields),
+ );
+ Array.prototype.push.apply(
+ requiredFields,
+ getRequiredFields(theForm.config.fields),
+ );
+ }
+ }
const { handler, status } = useFormState<FormType>(shape, initial, (st) => {
const partialErrors = undefinedIfEmpty<FormErrors<FormType>>({
@@ -199,7 +214,67 @@ export function CaseUpdate({
<Fragment>
<LocalNotificationBanner notification={notification} />
<div class="space-y-10 divide-y -mt-5 divide-gray-900/10">
- {theForm.config.design.map((section, i) => {
+ {(function () {
+ switch (theForm.config.type) {
+ case "double-column": {
+ return theForm.config.design.map((section, i) => {
+ if (!section) return <Fragment />;
+ return (
+ <div
+ key={i}
+ class="grid grid-cols-1 gap-x-8 gap-y-8 pt-5
md:grid-cols-3"
+ >
+ <div class="px-4 sm:px-0">
+ <h2 class="text-base font-semibold leading-7
text-gray-900">
+ {section.title}
+ </h2>
+ {section.description && (
+ <p class="mt-1 text-sm leading-6 text-gray-600">
+ {section.description}
+ </p>
+ )}
+ </div>
+ <div class="bg-white shadow-sm ring-1 ring-gray-900/5
rounded-md md:col-span-2">
+ <div class="p-3">
+ <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8
sm:grid-cols-6">
+ <RenderAllFieldsByUiConfig
+ key={i}
+ fields={convertUiField(
+ i18n,
+ section.fields,
+ handler,
+ getConverterById,
+ )}
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ });
+ }
+ case "single-column": {
+ return (
+ <div class="bg-white shadow-sm ring-1 ring-gray-900/5
rounded-md md:col-span-2">
+ <div class="p-3">
+ <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8
sm:grid-cols-6">
+ <RenderAllFieldsByUiConfig
+ fields={convertUiField(
+ i18n,
+ theForm.config.fields,
+ handler,
+ getConverterById,
+ )}
+ />
+ </div>
+ </div>
+ </div>
+ );
+ }
+ }
+ })()}
+ </div>
+ {/* {theForm.config.design.map((section, i) => {
if (!section) return <Fragment />;
return (
<div
@@ -234,7 +309,7 @@ export function CaseUpdate({
</div>
);
})}
- </div>
+ </div> */}
<div class="mt-6 flex items-center justify-end gap-x-6">
<a
@@ -249,7 +324,7 @@ export function CaseUpdate({
disabled={!submitHandler}
class="disabled:opacity-50 disabled:cursor-default rounded-md
bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm
hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2
focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
>
- <i18n.Translate>Confirm</i18n.Translate>
+ <i18n.Translate>Submit</i18n.Translate>
</Button>
</div>
</Fragment>
diff --git a/packages/aml-backoffice-ui/src/pages/Measures.tsx
b/packages/aml-backoffice-ui/src/pages/Measures.tsx
index 911fae82e..28236fe0c 100644
--- a/packages/aml-backoffice-ui/src/pages/Measures.tsx
+++ b/packages/aml-backoffice-ui/src/pages/Measures.tsx
@@ -15,10 +15,13 @@
*/
import {
AmlProgramRequirement,
+ assertUnreachable,
+ HttpStatusCode,
KycCheckInformation,
TalerError,
} from "@gnu-taler/taler-util";
import {
+ Attention,
Loading,
useExchangeApiContext,
useTranslationContext,
@@ -27,6 +30,7 @@ import { Fragment, h, VNode } from "preact";
import { ErrorLoadingWithDebug } from "../components/ErrorLoadingWithDebug.js";
import { useServerMeasures } from "../hooks/account.js";
import { CurrentMeasureTable, MeasureInfo } from "./MeasuresTable.js";
+import { Officer } from "./Officer.js";
export function Measures({}: {}) {
const { i18n } = useTranslationContext();
@@ -43,6 +47,26 @@ export function Measures({}: {}) {
return <ErrorLoadingWithDebug error={measures} />;
}
+ if (measures.type === "fail") {
+ switch (measures.case) {
+ // case HttpStatusCode.Unauthorized:
+ case HttpStatusCode.Forbidden:
+ return (
+ <Fragment>
+ <Attention type="danger" title={i18n.str`Operation denied`}>
+ <i18n.Translate>
+ This account signature is wrong, contact administrator or
create
+ a new one.
+ </i18n.Translate>
+ </Attention>
+ <Officer />
+ </Fragment>
+ );
+ default:
+ assertUnreachable(measures.case);
+ }
+ }
+
const list = Object.entries(measures.body.roots).map(
([key, value]): MeasureInfo => {
if (value.check_name !== "SKIP") {
diff --git a/packages/aml-backoffice-ui/src/pages/Search.tsx
b/packages/aml-backoffice-ui/src/pages/Search.tsx
index e3684d71b..635eb6582 100644
--- a/packages/aml-backoffice-ui/src/pages/Search.tsx
+++ b/packages/aml-backoffice-ui/src/pages/Search.tsx
@@ -309,9 +309,6 @@ function XTalerBankForm({
"x-taler-bank",
form.status.result.hostname,
form.status.result.account,
- {
- "receiver-name": form.status.result.name,
- },
);
return (
@@ -354,9 +351,7 @@ function IbanForm({
const paytoUri =
form.status.status === "fail"
? undefined
- : buildPayto("iban", form.status.result.account, form.status.result.bic,
{
- "receiver-name": form.status.result.name,
- });
+ : buildPayto("iban", form.status.result.account, undefined);
return (
<form
@@ -497,8 +492,6 @@ function createGenericPaytoValidator(i18n:
InternationalizationAPI) {
interface PaytoUriIBANForm {
account: string;
- name: string;
- bic: string;
}
function createIbanPaytoValidator(i18n: InternationalizationAPI) {
@@ -507,14 +500,11 @@ function createIbanPaytoValidator(i18n:
InternationalizationAPI) {
): FormStatus<PaytoUriIBANForm> {
const errors = undefinedIfEmpty<FormErrors<PaytoUriIBANForm>>({
account: !state.account ? i18n.str`required` : undefined,
- name: !state.name ? i18n.str`required` : undefined,
});
if (errors === undefined) {
const result: PaytoUriIBANForm = {
account: state.account!,
- name: state.name!,
- bic: state.bic!,
};
return {
status: "ok",
@@ -524,8 +514,6 @@ function createIbanPaytoValidator(i18n:
InternationalizationAPI) {
}
const result: RecursivePartial<PaytoUriIBANForm> = {
account: state.account,
- name: state.name,
- bic: state.bic,
};
return {
status: "fail",
@@ -537,7 +525,6 @@ function createIbanPaytoValidator(i18n:
InternationalizationAPI) {
interface PaytoUriTalerBankForm {
hostname: string;
account: string;
- name: string;
}
function createTalerBankPaytoValidator(i18n: InternationalizationAPI) {
return function check(
@@ -546,14 +533,12 @@ function createTalerBankPaytoValidator(i18n:
InternationalizationAPI) {
const errors = undefinedIfEmpty<FormErrors<PaytoUriTalerBankForm>>({
account: !state.account ? i18n.str`required` : undefined,
hostname: !state.hostname ? i18n.str`required` : undefined,
- name: !state.name ? i18n.str`required` : undefined,
});
if (errors === undefined) {
const result: PaytoUriTalerBankForm = {
account: state.account!,
hostname: state.hostname!,
- name: state.name!,
};
return {
status: "ok",
@@ -564,7 +549,6 @@ function createTalerBankPaytoValidator(i18n:
InternationalizationAPI) {
const result: RecursivePartial<PaytoUriTalerBankForm> = {
account: state.account,
hostname: state.hostname,
- name: state.name,
};
return {
status: "fail",
diff --git a/packages/taler-util/src/http-client/exchange.ts
b/packages/taler-util/src/http-client/exchange.ts
index 319ed47d8..1075921dc 100644
--- a/packages/taler-util/src/http-client/exchange.ts
+++ b/packages/taler-util/src/http-client/exchange.ts
@@ -842,8 +842,12 @@ export class TalerExchangeHttpClient {
});
switch (resp.status) {
- case HttpStatusCode.Ok:
+ case HttpStatusCode.Ok: {
return opSuccessFromHttp(resp, codecForAvailableMeasureSummary());
+ }
+ case HttpStatusCode.Forbidden: {
+ return opKnownHttpFailure(resp.status, resp);
+ }
default:
return opUnknownFailure(resp, await readTalerErrorResponse(resp));
}
diff --git a/packages/web-util/src/components/Button.tsx
b/packages/web-util/src/components/Button.tsx
index b142114e7..c45aeb752 100644
--- a/packages/web-util/src/components/Button.tsx
+++ b/packages/web-util/src/components/Button.tsx
@@ -40,14 +40,13 @@ export type OnOperationSuccesReturnType<T> = (
result: T extends OperationOk<any> ? T : never,
) => TranslatedString | void;
export type OnOperationFailReturnType<T> = (
- (d: (T extends OperationFail<any> ? T : never) | (T extends
OperationAlternative<any,any> ? T : never)) => TranslatedString)
+ d:
+ | (T extends OperationFail<any> ? T : never)
+ | (T extends OperationAlternative<any, any> ? T : never),
+) => TranslatedString;
export interface ButtonHandler<T extends OperationResult<A, B>, A, B> {
onClick: () => Promise<T | undefined>;
- onNotification: (n: NotificationMessage) => void;
- onOperationSuccess: OnOperationSuccesReturnType<T>;
- onOperationFail?: OnOperationFailReturnType<T>;
- onOperationComplete?: () => void;
}
interface Props<T extends OperationResult<A, B>, A, B>
@@ -84,61 +83,9 @@ export function Button<T extends OperationResult<A, B>, A,
B>({
return;
}
setRunning(true);
- handler
- .onClick()
- .then((resp) => {
- if (resp) {
- if (resp.type === "ok") {
- const result: OperationOk<any> = resp;
- // @ts-expect-error this is an operationOk
- const msg = handler.onOperationSuccess(result);
- if (msg) {
- notifyInfo(msg);
- }
- }
- if (resp.type === "fail") {
- const d = 'detail' in resp ? resp.detail : undefined
-
- const title = !handler.onOperationFail ? "Unexpected error."
as TranslatedString : handler.onOperationFail(resp as any);
- handler.onNotification({
- title,
- type: "error",
- description: d && d.hint ? d.hint as TranslatedString :
undefined,
- debug: d,
- when: AbsoluteTime.now(),
- });
- }
- }
- if (handler.onOperationComplete) {
- handler.onOperationComplete();
- }
- setRunning(false);
- })
- .catch((error) => {
- console.error(error);
-
- if (error instanceof TalerError) {
- handler.onNotification(
- buildUnifiedRequestErrorMessage(i18n, error),
- );
- } else {
- const description = (
- error instanceof Error ? error.message : String(error)
- ) as TranslatedString;
-
- handler.onNotification({
- title: i18n.str`Operation failed`,
- type: "error",
- description,
- when: AbsoluteTime.now(),
- });
- }
-
- if (handler.onOperationComplete) {
- handler.onOperationComplete();
- }
- setRunning(false);
- });
+ handler.onClick().finally(() => {
+ setRunning(false);
+ });
}}
>
{running ? <Wait /> : children}
diff --git a/packages/web-util/src/hooks/useNotifications.ts
b/packages/web-util/src/hooks/useNotifications.ts
index 929c54a58..1234436d5 100644
--- a/packages/web-util/src/hooks/useNotifications.ts
+++ b/packages/web-util/src/hooks/useNotifications.ts
@@ -10,7 +10,11 @@ import {
TranslatedString,
} from "@gnu-taler/taler-util";
import { useEffect, useState } from "preact/hooks";
-import { ButtonHandler, OnOperationFailReturnType, OnOperationSuccesReturnType
} from "../components/Button.js";
+import {
+ ButtonHandler,
+ OnOperationFailReturnType,
+ OnOperationSuccesReturnType,
+} from "../components/Button.js";
import {
InternationalizationAPI,
memoryMap,
@@ -106,7 +110,7 @@ export function useNotifications(): Notification[] {
useEffect(() => {
return storage.onUpdate(NOTIFICATION_KEY, () => {
- setLastUpdate(Date.now())
+ setLastUpdate(Date.now());
// const mem = storage.get(NOTIFICATION_KEY) ?? new Map();
// setter(structuredClone(mem));
});
@@ -181,11 +185,11 @@ export function useLocalNotification(): [
const notif = !value
? undefined
: {
- message: value,
- acknowledge: () => {
- setter(undefined);
- },
- };
+ message: value,
+ acknowledge: () => {
+ setter(undefined);
+ },
+ };
async function errorHandling(cb: (notify: typeof errorMap) => Promise<void>)
{
try {
@@ -218,28 +222,81 @@ export function useLocalNotificationHandler(): [
HandlerMaker,
(n: NotificationMessage) => void,
] {
+ const { i18n } = useTranslationContext();
const [value, setter] = useState<NotificationMessage>();
const notif = !value
? undefined
: {
- message: value,
- acknowledge: () => {
- setter(undefined);
- },
- };
+ message: value,
+ acknowledge: () => {
+ setter(undefined);
+ },
+ };
function makeHandler<T extends OperationResult<A, B>, A, B>(
- onClick: () => Promise<T | undefined>,
- onOperationSuccess:OnOperationSuccesReturnType<T>,
+ doAction: () => Promise<T | undefined>,
+ onOperationSuccess: OnOperationSuccesReturnType<T>,
onOperationFail?: OnOperationFailReturnType<T>,
onOperationComplete?: () => void,
): ButtonHandler<T, A, B> {
+ const onNotification = setter;
return {
- onClick,
- onNotification: setter,
- onOperationFail,
- onOperationSuccess,
- onOperationComplete,
+ onClick: async (): Promise<T | undefined> => {
+ try {
+ const resp = await doAction();
+ if (resp) {
+ if (resp.type === "ok") {
+ const result: OperationOk<any> = resp;
+ // @ts-expect-error this is an operationOk
+ const msg = onOperationSuccess(result);
+ if (msg) {
+ notifyInfo(msg);
+ }
+ }
+ if (resp.type === "fail") {
+ const d = "detail" in resp ? resp.detail : undefined;
+
+ const title = !onOperationFail
+ ? i18n.str`Unexpected error`
+ : onOperationFail(resp as any);
+ onNotification({
+ title,
+ type: "error",
+ description:
+ d && d.hint ? (d.hint as TranslatedString) : undefined,
+ debug: d,
+ when: AbsoluteTime.now(),
+ });
+ }
+ }
+ if (onOperationComplete) {
+ onOperationComplete();
+ }
+ return resp;
+ } catch (error: unknown) {
+ console.error(error);
+
+ if (error instanceof TalerError) {
+ onNotification(buildUnifiedRequestErrorMessage(i18n, error));
+ } else {
+ const description = (
+ error instanceof Error ? error.message : String(error)
+ ) as TranslatedString;
+
+ onNotification({
+ title: i18n.str`Operation failed`,
+ type: "error",
+ description,
+ when: AbsoluteTime.now(),
+ });
+ }
+ if (onOperationComplete) {
+ onOperationComplete();
+ }
+ return undefined;
+ }
+ // setRunning(false);
+ },
};
}
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [taler-wallet-core] branch master updated (2d2873b38 -> 351096408),
gnunet <=