commit 0c4bcac46aab100d8b43510bcdd8f1746bd485c6
parent acdf884eae79284eb5fb88ff076b90f645e64733
Author: Sebastian <sebasjm@taler-systems.com>
Date: Mon, 1 Dec 2025 12:23:13 -0300
fix #10679
Diffstat:
5 files changed, 97 insertions(+), 27 deletions(-)
diff --git a/packages/taler-util/src/types-taler-wallet.ts b/packages/taler-util/src/types-taler-wallet.ts
@@ -577,6 +577,17 @@ export interface BalancesResponse {
donauSummary?: DonauSummaryItem[];
}
+export interface BalancesScopeRequest {
+ /** Electronic cash balances, per currency scope. */
+ balances: WalletBalance[];
+ /* Summary of donations, per donau/year/currency. */
+ donauSummary?: DonauSummaryItem[];
+}
+export interface BalancesScopeResponse {
+ /** Electronic cash balance. */
+ balance: WalletBalance;
+}
+
export const codecForBalance = (): Codec<WalletBalance> =>
buildCodecForObject<WalletBalance>()
.property("scopeInfo", codecForAny()) // FIXME
diff --git a/packages/taler-wallet-core/src/balance.ts b/packages/taler-wallet-core/src/balance.ts
@@ -58,6 +58,7 @@ import {
assertUnreachable,
BalanceFlag,
BalancesResponse,
+ BalancesScopeRequest,
checkDbInvariant,
DonauSummaryItem,
GetBalanceDetailRequest,
@@ -738,6 +739,27 @@ export async function getBalances(
return wbal;
}
+/**
+ * Get detailed balance information of one exchange and currency.
+ */
+export async function getBalanceOfScope(
+ wex: WalletExecutionContext,
+ req: BalancesScopeRequest,
+): Promise<BalancesResponse> {
+ logger.trace("starting to compute balance");
+
+ const wbal = await wex.db.runAllStoresReadOnlyTx({}, async (tx) => {
+ return getBalancesInsideTransaction(wex, tx);
+ });
+
+ wbal.balances.find((w) =>{
+ w.scopeInfo
+ })
+ logger.trace("finished computing wallet balance");
+
+ return wbal;
+}
+
export interface PaymentRestrictionsForBalance {
currency: string;
diff --git a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/index.ts b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/index.ts
@@ -76,6 +76,7 @@ export namespace State {
previous: WalletBankAccountInfo[];
goToBank: ButtonHandler;
goToWallet: ButtonHandler;
+ disablePeerPayments: boolean;
}
}
diff --git a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts
@@ -21,7 +21,9 @@ import {
ScopeType,
parseScopeInfoShort,
stringifyScopeInfoShort,
- PaytoString
+ PaytoString,
+ ScopeInfo,
+ WalletBalance,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
@@ -35,19 +37,26 @@ import { Props, State } from "./index.js";
export function useComponentState(props: Props): RecursiveState<State> {
const api = useBackendContext();
const { pushAlertOnError } = useAlertContext();
- const { i18n } = useTranslationContext();
const [scope, setScope] = useState(props.scope);
const hook = useAsyncAsHook(async () => {
- const resp = await api.wallet.call(
+ const { accounts } = await api.wallet.call(
WalletApiOperation.ListBankAccounts,
{},
);
- return resp
+ const { balances } = await api.wallet.call(
+ WalletApiOperation.GetBalances,
+ {},
+ );
+
+ return { accounts, balances };
});
- const previous: WalletBankAccountInfo[] = props.type === "send" && hook && !hook.hasError ? hook.response.accounts : [];
+ const previous: WalletBankAccountInfo[] =
+ props.type === "send" && hook && !hook.hasError
+ ? hook.response.accounts
+ : [];
if (!scope) {
return () => {
@@ -120,6 +129,11 @@ export function useComponentState(props: Props): RecursiveState<State> {
};
}
+ const disablePeerPayments: boolean =
+ (hook && !hook.hasError
+ ? getBalanceForScope(hook.response.balances, scope)?.disablePeerPayments
+ : undefined) ?? false;
+
switch (props.type) {
case "send":
return {
@@ -139,6 +153,7 @@ export function useComponentState(props: Props): RecursiveState<State> {
props.goToWalletWalletSend(scope);
}),
},
+ disablePeerPayments,
type: props.type,
};
case "get":
@@ -151,15 +166,30 @@ export function useComponentState(props: Props): RecursiveState<State> {
props.goToWalletManualWithdraw(scope);
}),
},
- onSelectAccount: () => { },
+ onSelectAccount: () => {},
goToWallet: {
onClick: pushAlertOnError(async () => {
props.goToWalletWalletInvoice(scope);
}),
},
+ disablePeerPayments,
type: props.type,
};
default:
assertUnreachable(props);
}
}
+
+function getBalanceForScope(
+ balances: WalletBalance[],
+ scope: ScopeInfo,
+): WalletBalance | undefined {
+ return balances.find((b) => {
+ const bs = b.scopeInfo;
+ if (bs.type !== scope.type) return false;
+ if (bs.currency !== scope.currency) return false;
+ if (scope.type === ScopeType.Global || bs.type === ScopeType.Global) return false;
+ if (scope.url !== bs.url) return false;
+ return true;
+ });
+}
diff --git a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx
@@ -18,7 +18,7 @@ import {
WalletBankAccountInfo,
parsePaytoUri,
stringifyPaytoUri,
- assertUnreachable
+ assertUnreachable,
} from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { styled } from "@linaria/react";
@@ -197,6 +197,7 @@ export function ReadyGetView({
goToBank,
goToWallet,
previous,
+ disablePeerPayments,
}: State.Ready): VNode {
const { i18n } = useTranslationContext();
@@ -241,16 +242,18 @@ export function ReadyGetView({
</Button>
</Paper>
</Grid>
- <Grid item xs={1}>
- <Paper style={{ padding: 8 }}>
- <p>
- <i18n.Translate>From another wallet</i18n.Translate>
- </p>
- <Button onClick={goToWallet.onClick}>
- <i18n.Translate>Invoice</i18n.Translate>
- </Button>
- </Paper>
- </Grid>
+ {disablePeerPayments ? undefined : (
+ <Grid item xs={1}>
+ <Paper style={{ padding: 8 }}>
+ <p>
+ <i18n.Translate>From another wallet</i18n.Translate>
+ </p>
+ <Button onClick={goToWallet.onClick}>
+ <i18n.Translate>Invoice</i18n.Translate>
+ </Button>
+ </Paper>
+ </Grid>
+ )}
<Grid item xs={1}>
<Paper style={{ padding: 8 }}>
<p>
@@ -274,6 +277,7 @@ export function ReadySendView({
onSelectAccount,
goToWallet,
previous,
+ disablePeerPayments
}: State.Ready): VNode {
const { i18n } = useTranslationContext();
@@ -321,16 +325,18 @@ export function ReadySendView({
</Button>
</Paper>
</Grid>
- <Grid item xs={1}>
- <Paper style={{ padding: 8 }}>
- <p>
- <i18n.Translate>To another wallet</i18n.Translate>
- </p>
- <Button onClick={goToWallet.onClick}>
- <i18n.Translate>Send</i18n.Translate>
- </Button>
- </Paper>
- </Grid>
+ {disablePeerPayments ? undefined : (
+ <Grid item xs={1}>
+ <Paper style={{ padding: 8 }}>
+ <p>
+ <i18n.Translate>To another wallet</i18n.Translate>
+ </p>
+ <Button onClick={goToWallet.onClick}>
+ <i18n.Translate>Send</i18n.Translate>
+ </Button>
+ </Paper>
+ </Grid>
+ )}
<Grid item xs={1}>
<Paper style={{ padding: 8 }}>
<p>