commit c8f01b1ee385083917c0337005c846ed1788a81f
parent 78e11e7fef48ca7258e0c2d1ca0104cee6f9d0a5
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date: Thu, 6 Nov 2025 16:56:48 +0100
directly interact with sent message from mailbox
Diffstat:
4 files changed, 90 insertions(+), 13 deletions(-)
diff --git a/packages/taler-wallet-core/src/mailbox.ts b/packages/taler-wallet-core/src/mailbox.ts
@@ -276,9 +276,11 @@ export async function refreshMailbox(
logger.warn(`unable to decrypt message number ${i}`);
continue; // FIXME log
}
+ // Find start of padding
+ const padIdx = uri.indexOf(0x00);
const newMessage = {
originMailboxBaseUrl: mailboxConf.mailboxBaseUrl,
- talerUri: new TextDecoder().decode(uri),
+ talerUri: new TextDecoder().decode(uri.slice(0, padIdx)),
downloadedAt: now,
};
records.push(newMessage);
diff --git a/packages/taler-wallet-webextension/src/wallet/Application.tsx b/packages/taler-wallet-webextension/src/wallet/Application.tsx
@@ -175,7 +175,17 @@ export function Application(): VNode {
goToTransaction={redirectToTxInfo}
goToURL={redirectToURL}
>
- <MailboxPage />
+ <MailboxPage
+ onTalerUri={(talerActionUrl: string) => {
+ redirectTo(
+ Pages.defaultCta({
+ uri: encodeCrockForURI(
+ talerActionUrl
+ ),
+ }),
+ );
+ }}
+ />
</WalletTemplate>
)}
/>
diff --git a/packages/taler-wallet-webextension/src/wallet/Contacts.tsx b/packages/taler-wallet-webextension/src/wallet/Contacts.tsx
@@ -109,11 +109,12 @@ export function ContactsPage({
const onDeleteContact = async (c: ContactEntry) => {
api.wallet.call(WalletApiOperation.DeleteContact, { contact: c }).then();
};
- const onSendMoneyTransferMessage = async (c: ContactEntry, t?: Transaction) => {
+ const onSendUriMessage = async (c: ContactEntry, t?: Transaction) => {
if (!t) {
return;
}
- if (t.type != TransactionType.PeerPushDebit) {
+ if ((t.type != TransactionType.PeerPushDebit) &&
+ (t.type != TransactionType.PeerPullCredit)) {
return;
}
if (!t.talerUri) {
@@ -136,7 +137,7 @@ export function ContactsPage({
transaction={state.response.transaction}
onAddContact={onAddContact}
onDeleteContact={onDeleteContact}
- onSendMoneyTransferMessage={onSendMoneyTransferMessage}
+ onSendUriMessage={onSendUriMessage}
/>
);
}
@@ -145,7 +146,7 @@ interface ContactProps {
contact: ContactEntry;
transaction?: Transaction;
onDeleteContact: (c: ContactEntry) => Promise<void>;
- onSendMoneyTransferMessage: (c: ContactEntry, t?: Transaction) => Promise<void>;
+ onSendUriMessage: (c: ContactEntry, t?: Transaction) => Promise<void>;
}
@@ -171,12 +172,12 @@ function ContactLayout(props: ContactProps): VNode {
</Button>
)}
{(props.transaction && (props.transaction.type == TransactionType.PeerPushDebit)) && (
- <Button variant="contained" color="warning" onClick={() => { return props.onSendMoneyTransferMessage(props.contact, props.transaction)}}>
+ <Button variant="contained" color="warning" onClick={() => { return props.onSendUriMessage(props.contact, props.transaction)}}>
<i18n.Translate>Send Cash</i18n.Translate>
</Button>
)}
{(props.transaction && (props.transaction.type == TransactionType.PeerPullCredit)) && (
- <Button variant="contained" color="success">
+ <Button variant="contained" color="success" onClick={() => { return props.onSendUriMessage(props.contact, props.transaction)}}>
<i18n.Translate>Request Cash</i18n.Translate>
</Button>
)}
@@ -190,14 +191,14 @@ export function ContactsView({
transaction,
onAddContact,
onDeleteContact,
- onSendMoneyTransferMessage,
+ onSendUriMessage: onSendUriMessage,
}: {
search: TextFieldHandler;
contacts: ContactEntry[];
transaction?: Transaction;
onAddContact: (c: ContactEntry) => Promise<void>;
onDeleteContact: (c: ContactEntry) => Promise<void>;
- onSendMoneyTransferMessage: (c: ContactEntry, t?: Transaction) => Promise<void>;
+ onSendUriMessage: (c: ContactEntry, t?: Transaction) => Promise<void>;
}): VNode {
const { i18n } = useTranslationContext();
return (
@@ -234,7 +235,7 @@ export function ContactsView({
contact={c}
transaction={transaction}
onDeleteContact={onDeleteContact}
- onSendMoneyTransferMessage={onSendMoneyTransferMessage}
+ onSendUriMessage={onSendUriMessage}
/>
</Grid>
))
diff --git a/packages/taler-wallet-webextension/src/wallet/Mailbox.tsx b/packages/taler-wallet-webextension/src/wallet/Mailbox.tsx
@@ -27,8 +27,12 @@ import {
MailboxMessageRecord,
TranslatedString,
succeedOrThrow,
+ succeedOrValue,
TalerProtocolTimestamp,
- AbsoluteTime
+ AbsoluteTime,
+ TalerUris,
+ TalerUriAction,
+ TalerUri
} from "@gnu-taler/taler-util";
import { SafeHandler } from "../mui/handlers.js";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
@@ -56,11 +60,13 @@ import { TextFieldHandler } from "../mui/handlers.js";
interface Props {
scope?: ScopeInfo;
search?: boolean;
+ onTalerUri: (url: string) => void;
}
export function MailboxPage({
scope,
search: showSearch,
+ onTalerUri,
}: Props): VNode {
const { i18n } = useTranslationContext();
const api = useBackendContext();
@@ -155,6 +161,7 @@ export function MailboxPage({
onFetchMessages={onFetchMessages}
onDeleteMessage={onDeleteMessage}
onInitializeMailbox={onInitializeMailbox}
+ onTalerUri={onTalerUri}
/>
);
}
@@ -162,25 +169,79 @@ export function MailboxPage({
interface MessageProps {
message: MailboxMessageRecord;
onDeleteMessage: (m: MailboxMessageRecord) => Promise<void>;
+ onTalerUri: (url: string) => void;
}
function MailboxMessageLayout(props: MessageProps): VNode {
const { i18n } = useTranslationContext();
+ const uri = succeedOrValue(TalerUris.fromString(props.message.talerUri), undefined);
function toDateString(t: TalerProtocolTimestamp) : string {
if (t.t_s === "never") {
return t.t_s;
}
return new Date(t.t_s * 1000).toLocaleString();
}
+ console.log(uri);
return (
<Paper style={{ padding: 8 }}>
<p>
- <span><i18n.Translate>Message from {toDateString(props.message.downloadedAt)}</i18n.Translate></span>
+ <span><i18n.Translate>Date</i18n.Translate>: {toDateString(props.message.downloadedAt)}</span>
<SmallText style={{ marginTop: 5 }}>
<i18n.Translate>URI</i18n.Translate>: {props.message.talerUri}
</SmallText>
</p>
+ {uri &&
+ <Button
+ variant="contained"
+ color="success"
+ onClick={async () => {
+ if (uri) props.onTalerUri(props.message.talerUri);
+ }}
+ >
+ {(function (talerUri: TalerUri): VNode {
+ switch (talerUri.type) {
+ case TalerUriAction.Pay:
+ return <i18n.Translate>Pay invoice</i18n.Translate>;
+ case TalerUriAction.Withdraw:
+ return (
+ <i18n.Translate>Withdrawal from bank</i18n.Translate>
+ );
+ case TalerUriAction.Refund:
+ return <i18n.Translate>Claim refund</i18n.Translate>;
+ case TalerUriAction.PayPull:
+ return <i18n.Translate>Pay invoice</i18n.Translate>;
+ case TalerUriAction.PayPush:
+ return <i18n.Translate>Accept payment</i18n.Translate>;
+ case TalerUriAction.PayTemplate:
+ return <i18n.Translate>Complete order</i18n.Translate>;
+ case TalerUriAction.Restore:
+ return <i18n.Translate>Restore wallet</i18n.Translate>;
+ case TalerUriAction.DevExperiment:
+ return (
+ <i18n.Translate>Enable experiment</i18n.Translate>
+ );
+ case TalerUriAction.WithdrawExchange:
+ return (
+ <i18n.Translate>
+ Withdraw from exchange
+ </i18n.Translate>
+ );
+ case TalerUriAction.AddExchange:
+ return <i18n.Translate>Add exchange</i18n.Translate>;
+ case TalerUriAction.WithdrawalTransferResult:
+ return (
+ <i18n.Translate>Notify transaction</i18n.Translate>
+ );
+ case TalerUriAction.AddContact:
+ return <i18n.Translate>Add contact</i18n.Translate>;
+ default: {
+ assertUnreachable(talerUri);
+ }
+ }
+ })(uri)}
+ </Button>
+ }
<Button variant="contained" onClick={() => { return props.onDeleteMessage(props.message)}} color="error">
<i18n.Translate>Delete</i18n.Translate>
</Button>
@@ -198,6 +259,7 @@ export function MessagesView({
onFetchMessages,
onDeleteMessage,
onInitializeMailbox,
+ onTalerUri,
}: {
search: TextFieldHandler;
error: TranslatedString | undefined;
@@ -207,6 +269,7 @@ export function MessagesView({
onFetchMessages: (mb?: MailboxConfiguration) => Promise<void>;
onDeleteMessage: (m: MailboxMessageRecord) => Promise<void>;
onInitializeMailbox: () => Promise<void>;
+ onTalerUri: (url: string) => void;
}): VNode {
const { i18n } = useTranslationContext();
async function copy(): Promise<void> {
@@ -274,6 +337,7 @@ export function MessagesView({
<MailboxMessageLayout
message={m}
onDeleteMessage={onDeleteMessage}
+ onTalerUri={onTalerUri}
/>
</Grid>
))