taler-typescript-core

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

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:
Mpackages/taler-wallet-core/src/mailbox.ts | 4+++-
Mpackages/taler-wallet-webextension/src/wallet/Application.tsx | 12+++++++++++-
Mpackages/taler-wallet-webextension/src/wallet/Contacts.tsx | 19++++++++++---------
Mpackages/taler-wallet-webextension/src/wallet/Mailbox.tsx | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
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> ))