commit a0c06b5a89bd61a30f24055681c16e64b11ea225
parent 8a0eb7fefc4ee30df6ab15d3609058e4ee524979
Author: Sebastian <sebasjm@gmail.com>
Date: Mon, 3 Nov 2025 11:25:53 -0300
better error msg
Diffstat:
9 files changed, 84 insertions(+), 65 deletions(-)
diff --git a/packages/bank-ui/src/hooks/preferences.ts b/packages/bank-ui/src/hooks/preferences.ts
@@ -30,7 +30,6 @@ import { codecOptionalDefault } from "@gnu-taler/taler-util";
const TALER_SCREEN_ID = 102;
-
interface Preferences {
showWithdrawalSuccess: boolean;
hideDemo: boolean;
@@ -40,6 +39,7 @@ interface Preferences {
export const codecForPreferences = (): Codec<Preferences> =>
buildCodecForObject<Preferences>()
+ .allowExtra()
.property("showWithdrawalSuccess", codecForBoolean())
.property("hideDemo", codecOptionalDefault(codecForBoolean(), false))
.property("showInstallWallet", codecForBoolean())
@@ -89,11 +89,7 @@ export function getAllBooleanPreferences(
"fastWithdrawalForm",
];
}
- return [
- "showInstallWallet",
- "showWithdrawalSuccess",
- "fastWithdrawalForm",
- ];
+ return ["showInstallWallet", "showWithdrawalSuccess", "fastWithdrawalForm"];
}
export function getLabelForPreferences(
diff --git a/packages/bank-ui/src/pages/BankFrame.tsx b/packages/bank-ui/src/pages/BankFrame.tsx
@@ -28,6 +28,7 @@ import {
Loading,
RouteDefinition,
ToastBanner,
+ logBugForDevelopers,
notifyError,
notifyException,
useBankCoreApiContext,
@@ -67,7 +68,7 @@ export function BankFrame({
const { i18n } = useTranslationContext();
const session = useSessionState();
const settings = useSettingsContext();
- const {showDebugInfo} = useCommonPreference();
+ const { showDebugInfo } = useCommonPreference();
const [preferences, updatePreferences] = usePreferences();
const [, , resetBankState] = useBankState();
const d = useBankCoreApiContext();
@@ -77,11 +78,10 @@ export function BankFrame({
useEffect(() => {
if (error) {
+ logBugForDevelopers(error);
if (error instanceof Error) {
- console.log("Internal error, please report", error);
- notifyException(i18n.str`Internal error, please report.`, error);
+ notifyException(i18n.str`Internal error, please report. There should be more information in the console.`, error);
} else {
- console.log("Internal error, please report", error);
notifyError(
i18n.str`Internal error, please report.`,
String(error) as TranslatedString,
@@ -251,7 +251,7 @@ function AppActivity(): VNode {
const d = useBankCoreApiContext();
const onBackendActivity = !d ? undefined : d.onActivity;
const cancelRequest = !d ? undefined : d.cancelRequest;
- const {showDebugInfo} = useCommonPreference();
+ const { showDebugInfo } = useCommonPreference();
useEffect(() => {
// console.log("ASDASDS", onBackendActivity)
if (!showDebugInfo) return;
diff --git a/packages/bank-ui/src/pages/ConversionRateClassDetails.tsx b/packages/bank-ui/src/pages/ConversionRateClassDetails.tsx
@@ -134,10 +134,7 @@ function Form({
const { i18n } = useTranslationContext();
const { state: credentials } = useSessionState();
const creds = credentials.status !== "loggedIn" ? undefined : credentials;
- const {
- lib: { bank },
- config,
- } = useBankCoreApiContext();
+ const { lib, config } = useBankCoreApiContext();
const [notification, safeFunctionHandler] = useLocalNotificationBetter();
const [section, setSection] = useState<
"detail" | "cashout" | "cashin" | "users" | "test" | "delete"
@@ -168,7 +165,7 @@ function Form({
);
const deleteClass = safeFunctionHandler(
- (token: AccessToken) => bank.deleteConversionRateClass(token, classId),
+ (token: AccessToken) => lib.bank.deleteConversionRateClass(token, classId),
!creds || section !== "delete" || detailsResult.num_users > 0
? undefined
: [creds.token],
@@ -206,7 +203,7 @@ function Form({
};
const updateClass = safeFunctionHandler(
- bank.updateConversionRateClass,
+ lib.bank.updateConversionRateClass.bind(lib.bank),
!creds || !input ? undefined : [creds.token, classId, input],
);
updateClass.onSuccess = () => {
@@ -215,15 +212,15 @@ function Form({
updateClass.onFail = (fail) => {
switch (fail.case) {
case HttpStatusCode.Unauthorized:
- return i18n.str``;
+ return i18n.str`Unauthorized`;
case HttpStatusCode.Forbidden:
- return i18n.str``;
+ return i18n.str`Forbidden`;
case HttpStatusCode.NotFound:
- return i18n.str``;
+ return i18n.str`Not Found`;
case HttpStatusCode.NotImplemented:
- return i18n.str``;
+ return i18n.str`Not implemented`;
case TalerErrorCode.BANK_NAME_REUSE:
- return i18n.str``;
+ return i18n.str`The name of the conversion is already used.`;
}
};
diff --git a/packages/bank-ui/src/pages/LoginForm.tsx b/packages/bank-ui/src/pages/LoginForm.tsx
@@ -58,17 +58,13 @@ export function LoginForm({
currentUser,
fixedUser,
routeRegister,
- //
}: {
fixedUser?: boolean;
currentUser?: string;
routeRegister?: RouteDefinition;
- //
}): VNode {
const session = useSessionState();
- const sessionState =
- session.state.status === "loggedIn" ? session.state : undefined;
const sessionUser =
session.state.status !== "loggedOut" ? session.state.username : undefined;
const [username, setUsername] = useState<string | undefined>(
@@ -193,7 +189,7 @@ export function LoginForm({
autocomplete="username"
title={i18n.str`Username of the account`}
required
- onInput={(e): void => {
+ onChange={(e): void => {
setUsername(e.currentTarget.value);
}}
/>
@@ -225,7 +221,7 @@ export function LoginForm({
placeholder="Password"
title={i18n.str`Password of the account`}
required
- onInput={(e): void => {
+ onChange={(e): void => {
setPassword(e.currentTarget.value);
}}
/>
diff --git a/packages/bank-ui/src/pages/NewConversionRateClass.tsx b/packages/bank-ui/src/pages/NewConversionRateClass.tsx
@@ -60,7 +60,7 @@ export function NewConversionRateClass({
case HttpStatusCode.NotImplemented:
return i18n.str`Not implemented`;
case TalerErrorCode.BANK_NAME_REUSE:
- return i18n.str`The name is already used`;
+ return i18n.str`The name of the conversion is already used.`;
}
};
diff --git a/packages/bank-ui/src/pages/SolveMFA.tsx b/packages/bank-ui/src/pages/SolveMFA.tsx
@@ -300,7 +300,7 @@ export function SolveMFAChallenges({
}
};
- const doComplete = onCompleted.withArgs(solved);
+ const complete = onCompleted.withArgs(solved);
const selectChallenge = safeFunctionHandler(async (ch: Challenge) => {
setSelected({
@@ -457,7 +457,7 @@ export function SolveMFAChallenges({
type="submit"
name="send again"
class="disabled:opacity-50 disabled:cursor-default cursor-pointer 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"
- onClick={doComplete}
+ onClick={complete}
>
<i18n.Translate>Complete</i18n.Translate>
</ButtonBetter>
diff --git a/packages/bank-ui/src/pages/account/ShowAccountDetails.tsx b/packages/bank-ui/src/pages/account/ShowAccountDetails.tsx
@@ -155,7 +155,7 @@ export function ShowAccountDetails({
};
const repeatUpdate = update.lambda((ids: string[]) => {
- return [update.args![0], update.args![1], []];
+ return [update.args![0], update.args![1], ids];
});
const url = bank.getRevenueAPI(account);
diff --git a/packages/bank-ui/src/pages/regional/ConversionConfig.tsx b/packages/bank-ui/src/pages/regional/ConversionConfig.tsx
@@ -583,16 +583,14 @@ function useComponentState({
<i18n.Translate>Cancel</i18n.Translate>
</a>
{section == "cashin" || section == "cashout" ? (
- <Fragment>
- <ButtonBetter
- type="submit"
- name="update conversion"
- class="disabled:opacity-50 disabled:cursor-default cursor-pointer 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"
- onClick={update}
- >
- <i18n.Translate>Update</i18n.Translate>
- </ButtonBetter>
- </Fragment>
+ <ButtonBetter
+ type="submit"
+ name="update conversion"
+ class="disabled:opacity-50 disabled:cursor-default cursor-pointer 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"
+ onClick={update}
+ >
+ <i18n.Translate>Update</i18n.Translate>
+ </ButtonBetter>
) : (
<div />
)}
diff --git a/packages/web-util/src/hooks/useNotifications.ts b/packages/web-util/src/hooks/useNotifications.ts
@@ -239,28 +239,27 @@ export function useLocalNotificationBetter(): [
const thiz: SafeHandlerTemplate<Args, R> = {
args: a,
withArgs: (...newArgs) => {
- const r = buildSafeHandler(newArgs, doAction)
- r.onSuccess = thiz.onSuccess
- r.onFail = thiz.onFail
- return r
+ const r = buildSafeHandler(newArgs, doAction);
+ r.onSuccess = thiz.onSuccess;
+ r.onFail = thiz.onFail;
+ return r;
},
lambda: (converter, init) => {
type D = Parameters<typeof converter>;
type SH = SafeHandlerTemplate<D, R>;
const r = buildSafeHandler(
- // @ts-expect-error
- init ? converter(init) : undefined,
+ init ? converter(...init) : undefined,
doAction,
);
// @ts-expect-error
r.withArgs = (...args: D) => {
const d = converter(...args);
- const e = r.withArgs(...(d as any));
+ const e = thiz.withArgs(...d);
return e;
};
- r.onSuccess = thiz.onSuccess
- r.onFail = thiz.onFail
+ r.onSuccess = thiz.onSuccess;
+ r.onFail = thiz.onFail;
return r as any as SH;
},
call: async (): Promise<void> => {
@@ -278,7 +277,7 @@ export function useLocalNotificationBetter(): [
case "fail": {
const error = thiz.onFail(resp as any, ...thiz.args);
if (error) {
- save(failWithTitle(resp, error));
+ save(failWithTitle(i18n, resp, error));
}
return;
}
@@ -288,8 +287,8 @@ export function useLocalNotificationBetter(): [
}
} catch (error: unknown) {
// This functions should not throw, this is a problem.
- console.error(`Error: `, error);
- onUnexpected(i18n, save)(error);
+ logBugForDevelopers(error);
+ onUnexpected(i18n, save)(error, thiz.args);
return;
}
},
@@ -305,11 +304,18 @@ export function useLocalNotificationBetter(): [
return [notif, safeFunctionHandler];
}
+export function logBugForDevelopers(error: unknown) {
+ console.error(
+ `Internal error, this is mostly a bug in the application. Please report: `,
+ error,
+ );
+}
+
function onUnexpected(
i18n: InternationalizationAPI,
save: (m: NotificationMessage) => void,
-): (cause: unknown) => void {
- return (error) => {
+): (cause: unknown, args: any[]) => void {
+ return (error, args) => {
if (error instanceof TalerError) {
save({
title: translateTalerError(error, i18n),
@@ -318,7 +324,12 @@ function onUnexpected(
error && error.errorDetail.hint
? (error.errorDetail.hint as TranslatedString)
: undefined,
- debug: error,
+ debug: {
+ error,
+ stack: error instanceof Error ? error.stack : undefined,
+ args: sanitizeFunctionArguments(args),
+ when: AbsoluteTime.now(),
+ },
when: AbsoluteTime.now(),
});
} else {
@@ -327,16 +338,33 @@ function onUnexpected(
) as TranslatedString;
save({
- title: i18n.str`Operation failed`,
+ title: i18n.str`Operation failed.`,
type: "error",
- description,
- debug: error,
+ description: i18n.str`Unexpected error, this is likely a bug. Please report `,
+ debug: {
+ error: String(error),
+ stack: error instanceof Error ? error.stack : undefined,
+ args: sanitizeFunctionArguments(args),
+ when: AbsoluteTime.now(),
+ },
when: AbsoluteTime.now(),
});
}
};
}
+function sanitizeFunctionArguments(args: any[]): string {
+ return args
+ .map((d) =>
+ typeof d === "string" && d.startsWith("secret-token:")
+ ? "<session>"
+ : typeof d === "object"
+ ? JSON.stringify(d, undefined, 2)
+ : d,
+ )
+ .join(", ");
+}
+
/**
* A function converted into a safe handler.
*
@@ -375,15 +403,19 @@ function successWithTitle(title: TranslatedString): NotificationMessage {
}
function failWithTitle(
+ i18n: InternationalizationAPI,
fail: OperationFail<any>,
- title: TranslatedString,
+ description: TranslatedString,
): NotificationMessage {
- const d = fail.detail;
return {
- title,
+ title: i18n.str`The operation failed.`,
type: "error",
- description: d && d.hint ? (d.hint as TranslatedString) : undefined,
- debug: d,
+ description,
+ debug: {
+ detail: fail.detail,
+ case: fail.case,
+ when: AbsoluteTime.now(),
+ },
when: AbsoluteTime.now(),
};
}