using extendedStatus

This commit is contained in:
Sebastian 2023-01-13 16:09:33 -03:00
parent bc67ff0c7f
commit 0b2bf13def
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
8 changed files with 337 additions and 137 deletions

View File

@ -16,6 +16,7 @@
import { import {
AbsoluteTime, AbsoluteTime,
Amounts, Amounts,
ExtendedStatus,
NotificationType, NotificationType,
Transaction, Transaction,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
@ -56,7 +57,9 @@ export function PendingTransactions({ goToTransaction }: Props): VNode {
const transactions = const transactions =
!state || state.hasError !state || state.hasError
? cache.tx ? cache.tx
: state.response.transactions.filter((t) => t.pending); : state.response.transactions.filter(
(t) => t.extendedStatus === ExtendedStatus.Pending,
);
if (state && !state.hasError) { if (state && !state.hasError) {
cache.tx = transactions; cache.tx = transactions;

View File

@ -22,6 +22,7 @@ import {
Transaction, Transaction,
TransactionType, TransactionType,
WithdrawalType, WithdrawalType,
ExtendedStatus,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { h, VNode } from "preact"; import { h, VNode } from "preact";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
@ -52,7 +53,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)} timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
iconPath={"W"} iconPath={"W"}
pending={ pending={
tx.pending tx.extendedStatus === ExtendedStatus.Pending
? tx.withdrawalDetails.type === ? tx.withdrawalDetails.type ===
WithdrawalType.TalerBankIntegrationApi WithdrawalType.TalerBankIntegrationApi
? !tx.withdrawalDetails.confirmed ? !tx.withdrawalDetails.confirmed

View File

@ -48,7 +48,14 @@ type Type = {
alerts: Alert[]; alerts: Alert[];
pushAlert: (n: Alert) => void; pushAlert: (n: Alert) => void;
removeAlert: (n: Alert) => void; removeAlert: (n: Alert) => void;
/**
*
* @param h
* @returns
* @deprecated use safely
*/
pushAlertOnError: <T>(h: (p: T) => Promise<void>) => SafeHandler<T>; pushAlertOnError: <T>(h: (p: T) => Promise<void>) => SafeHandler<T>;
safely: <T>(h: (p: T) => Promise<void>, error: TranslatedString) => SafeHandler<T>;
}; };
const initial: Type = { const initial: Type = {
@ -56,6 +63,9 @@ const initial: Type = {
pushAlertOnError: () => { pushAlertOnError: () => {
throw Error("alert context not initialized"); throw Error("alert context not initialized");
}, },
safely: () => {
throw Error("alert context not initialized");
},
pushAlert: () => { pushAlert: () => {
null; null;
}, },
@ -100,8 +110,18 @@ export const AlertProvider = ({ children }: Props): VNode => {
}); });
} }
function safely<T>(
handler: (p: T) => Promise<void>,
message: TranslatedString
): SafeHandler<T> {
return withSafe(handler, (e) => {
const a = alertFromError(message, e);
pushAlert(a);
});
}
return h(Context.Provider, { return h(Context.Provider, {
value: { alerts, pushAlert, removeAlert, pushAlertOnError }, value: { alerts, pushAlert, removeAlert, pushAlertOnError, safely },
children, children,
}); });
}; };

View File

@ -20,6 +20,7 @@ import { theme, Colors, rippleEnabled, rippleEnabledOutlined } from "./style";
// eslint-disable-next-line import/extensions // eslint-disable-next-line import/extensions
import { alpha } from "./colors/manipulation"; import { alpha } from "./colors/manipulation";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { SafeHandler } from "./handlers.js";
export const buttonBaseStyle = css` export const buttonBaseStyle = css`
display: inline-flex; display: inline-flex;
@ -55,6 +56,7 @@ interface Props {
tooltip?: string; tooltip?: string;
color?: Colors; color?: Colors;
onClick?: () => Promise<void>; onClick?: () => Promise<void>;
// onClick?: SafeHandler<void>;
} }
const button = css` const button = css`

View File

@ -35,6 +35,7 @@ import * as popup from "./popup/index.stories.js";
import * as wallet from "./wallet/index.stories.js"; import * as wallet from "./wallet/index.stories.js";
import { renderStories } from "@gnu-taler/web-util/lib/index.browser"; import { renderStories } from "@gnu-taler/web-util/lib/index.browser";
import { AlertProvider } from "./context/alert.js";
function main(): void { function main(): void {
renderStories( renderStories(
@ -56,28 +57,28 @@ function getWrapperForGroup(group: string): FunctionComponent {
case "popup": case "popup":
return function PopupWrapper({ children }: any) { return function PopupWrapper({ children }: any) {
return ( return (
<Fragment> <AlertProvider>
<PopupNavBar /> <PopupNavBar />
<PopupBox>{children}</PopupBox> <PopupBox>{children}</PopupBox>
</Fragment> </AlertProvider>
); );
}; };
case "wallet": case "wallet":
return function WalletWrapper({ children }: any) { return function WalletWrapper({ children }: any) {
return ( return (
<Fragment> <AlertProvider>
<LogoHeader /> <LogoHeader />
<WalletNavBar /> <WalletNavBar />
<WalletBox>{children}</WalletBox> <WalletBox>{children}</WalletBox>
</Fragment> </AlertProvider>
); );
}; };
case "cta": case "cta":
return function WalletWrapper({ children }: any) { return function WalletWrapper({ children }: any) {
return ( return (
<Fragment> <AlertProvider>
<WalletAction>{children}</WalletAction> <WalletAction>{children}</WalletAction>
</Fragment> </AlertProvider>
); );
}; };
default: default:

View File

@ -556,9 +556,11 @@ function WalletTemplate({
{goToTransaction ? ( {goToTransaction ? (
<PendingTransactions goToTransaction={goToTransaction} /> <PendingTransactions goToTransaction={goToTransaction} />
) : undefined} ) : undefined}
<CurrentAlerts />
<WalletBox> <WalletBox>
<AlertProvider>{children}</AlertProvider> <AlertProvider>
<CurrentAlerts />
{children}
</AlertProvider>
</WalletBox> </WalletBox>
</Fragment> </Fragment>
); );

View File

@ -39,14 +39,13 @@ import {
WithdrawalDetails, WithdrawalDetails,
WithdrawalType, WithdrawalType,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { DevContextProviderForTesting } from "../context/devContext.js";
// import { // import {
// createExample, // createExample,
// createExampleWithCustomContext as createExampleInCustomContext, // createExampleWithCustomContext as createExampleInCustomContext,
// } from "../test-utils.js"; // } from "../test-utils.js";
import { TransactionView as TestedComponent } from "./Transaction.js";
import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { tests } from "@gnu-taler/web-util/lib/index.browser";
import beer from "../../static-dev/beer.png"; import beer from "../../static-dev/beer.png";
import { TransactionView as TestedComponent } from "./Transaction.js";
export default { export default {
title: "transaction details", title: "transaction details",
@ -54,6 +53,7 @@ export default {
argTypes: { argTypes: {
onRetry: { action: "onRetry" }, onRetry: { action: "onRetry" },
onDelete: { action: "onDelete" }, onDelete: { action: "onDelete" },
onCancel: { action: "onCancel" },
onBack: { action: "onBack" }, onBack: { action: "onBack" },
}, },
}; };
@ -62,10 +62,10 @@ const commonTransaction = {
amountRaw: "KUDOS:11", amountRaw: "KUDOS:11",
amountEffective: "KUDOS:9.2", amountEffective: "KUDOS:9.2",
extendedStatus: ExtendedStatus.Done, extendedStatus: ExtendedStatus.Done,
pending: false, pending: undefined as any as boolean, //deprecated
timestamp: TalerProtocolTimestamp.now(), timestamp: TalerProtocolTimestamp.now(),
transactionId: "txn:deposit:12", transactionId: "txn:deposit:12",
frozen: false, frozen: undefined as any as boolean, //deprecated
type: TransactionType.Deposit, type: TransactionType.Deposit,
} as TransactionCommon; } as TransactionCommon;
@ -255,7 +255,7 @@ export const WithdrawFiveMinutesAgoAndPending = tests.createExample(
timestamp: TalerProtocolTimestamp.fromSeconds( timestamp: TalerProtocolTimestamp.fromSeconds(
new Date().getTime() / 1000 - 60 * 5, new Date().getTime() / 1000 - 60 * 5,
), ),
pending: true, extendedStatus: ExtendedStatus.Pending,
}, },
}), }),
); );
@ -295,7 +295,7 @@ export const WithdrawPendingManual = tests.createExample(
exchangePaytoUris: ["payto://iban/ES8877998399652238"], exchangePaytoUris: ["payto://iban/ES8877998399652238"],
reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG", reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
} as WithdrawalDetails, } as WithdrawalDetails,
pending: true, extendedStatus: ExtendedStatus.Pending,
}, },
}), }),
); );
@ -311,7 +311,7 @@ export const WithdrawPendingTalerBankUnconfirmed = tests.createExample(
reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG", reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
bankConfirmationUrl: "http://bank.demo.taler.net", bankConfirmationUrl: "http://bank.demo.taler.net",
}, },
pending: true, extendedStatus: ExtendedStatus.Pending,
}, },
}, },
); );
@ -326,7 +326,7 @@ export const WithdrawPendingTalerBankConfirmed = tests.createExample(
confirmed: true, confirmed: true,
reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG", reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
}, },
pending: true, extendedStatus: ExtendedStatus.Pending,
}, },
}, },
); );
@ -443,7 +443,10 @@ export const PaymentWithoutFee = tests.createExample(TestedComponent, {
}); });
export const PaymentPending = tests.createExample(TestedComponent, { export const PaymentPending = tests.createExample(TestedComponent, {
transaction: { ...exampleData.payment, pending: true }, transaction: {
...exampleData.payment,
extendedStatus: ExtendedStatus.Pending,
},
}); });
export const PaymentWithProducts = tests.createExample(TestedComponent, { export const PaymentWithProducts = tests.createExample(TestedComponent, {
@ -540,7 +543,10 @@ export const DepositError = tests.createExample(TestedComponent, {
}); });
export const DepositPending = tests.createExample(TestedComponent, { export const DepositPending = tests.createExample(TestedComponent, {
transaction: { ...exampleData.deposit, pending: true }, transaction: {
...exampleData.deposit,
extendedStatus: ExtendedStatus.Pending,
},
}); });
export const Refresh = tests.createExample(TestedComponent, { export const Refresh = tests.createExample(TestedComponent, {
@ -566,7 +572,7 @@ export const TipError = tests.createExample(TestedComponent, {
}); });
export const TipPending = tests.createExample(TestedComponent, { export const TipPending = tests.createExample(TestedComponent, {
transaction: { ...exampleData.tip, pending: true }, transaction: { ...exampleData.tip, extendedStatus: ExtendedStatus.Pending },
}); });
export const Refund = tests.createExample(TestedComponent, { export const Refund = tests.createExample(TestedComponent, {
@ -581,7 +587,10 @@ export const RefundError = tests.createExample(TestedComponent, {
}); });
export const RefundPending = tests.createExample(TestedComponent, { export const RefundPending = tests.createExample(TestedComponent, {
transaction: { ...exampleData.refund, pending: true }, transaction: {
...exampleData.refund,
extendedStatus: ExtendedStatus.Pending,
},
}); });
export const InvoiceCreditComplete = tests.createExample(TestedComponent, { export const InvoiceCreditComplete = tests.createExample(TestedComponent, {
@ -591,7 +600,7 @@ export const InvoiceCreditComplete = tests.createExample(TestedComponent, {
export const InvoiceCreditIncomplete = tests.createExample(TestedComponent, { export const InvoiceCreditIncomplete = tests.createExample(TestedComponent, {
transaction: { transaction: {
...exampleData.pull_credit, ...exampleData.pull_credit,
pending: true, extendedStatus: ExtendedStatus.Pending,
}, },
}); });
@ -609,6 +618,6 @@ export const TransferDebitComplete = tests.createExample(TestedComponent, {
export const TransferDebitIncomplete = tests.createExample(TestedComponent, { export const TransferDebitIncomplete = tests.createExample(TestedComponent, {
transaction: { transaction: {
...exampleData.push_debit, ...exampleData.push_debit,
pending: true, extendedStatus: ExtendedStatus.Pending,
}, },
}); });

View File

@ -18,6 +18,7 @@ import {
AbsoluteTime, AbsoluteTime,
AmountJson, AmountJson,
Amounts, Amounts,
ExtendedStatus,
Location, Location,
MerchantInfo, MerchantInfo,
NotificationType, NotificationType,
@ -60,11 +61,12 @@ import {
WarningBox, WarningBox,
} from "../components/styled/index.js"; } from "../components/styled/index.js";
import { Time } from "../components/Time.js"; import { Time } from "../components/Time.js";
import { alertFromError } from "../context/alert.js"; import { alertFromError, useAlertContext } from "../context/alert.js";
import { useBackendContext } from "../context/backend.js"; import { useBackendContext } from "../context/backend.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
import { Button } from "../mui/Button.js"; import { Button } from "../mui/Button.js";
import { SafeHandler } from "../mui/handlers.js";
import { Pages } from "../NavigationBar.js"; import { Pages } from "../NavigationBar.js";
interface Props { interface Props {
@ -116,6 +118,12 @@ export function TransactionPage({
onSend={async () => { onSend={async () => {
null; null;
}} }}
onCancel={async () => {
await api.wallet.call(WalletApiOperation.AbortTransaction, {
transactionId,
});
goToWalletHistory(currency);
}}
onDelete={async () => { onDelete={async () => {
await api.wallet.call(WalletApiOperation.DeleteTransaction, { await api.wallet.call(WalletApiOperation.DeleteTransaction, {
transactionId, transactionId,
@ -141,6 +149,7 @@ export function TransactionPage({
export interface WalletTransactionProps { export interface WalletTransactionProps {
transaction: Transaction; transaction: Transaction;
onSend: () => Promise<void>; onSend: () => Promise<void>;
onCancel: () => Promise<void>;
onDelete: () => Promise<void>; onDelete: () => Promise<void>;
onRetry: () => Promise<void>; onRetry: () => Promise<void>;
onRefund: (id: string) => Promise<void>; onRefund: (id: string) => Promise<void>;
@ -155,18 +164,29 @@ const PurchaseDetailsTable = styled.table`
} }
`; `;
export function TransactionView({ type TransactionTemplateProps = Omit<
Omit<WalletTransactionProps, "onRefund">,
"onBack"
> & {
children: ComponentChildren;
};
function TransactionTemplate({
transaction, transaction,
onDelete, onDelete,
onRetry, onRetry,
onSend, onSend,
onRefund, onCancel,
}: WalletTransactionProps): VNode { children,
}: TransactionTemplateProps): VNode {
const { i18n } = useTranslationContext();
const [confirmBeforeForget, setConfirmBeforeForget] = useState(false); const [confirmBeforeForget, setConfirmBeforeForget] = useState(false);
const [confirmBeforeCancel, setConfirmBeforeCancel] = useState(false);
const { safely } = useAlertContext();
async function doCheckBeforeForget(): Promise<void> { async function doCheckBeforeForget(): Promise<void> {
if ( if (
transaction.pending && transaction.extendedStatus === ExtendedStatus.Pending &&
transaction.type === TransactionType.Withdrawal transaction.type === TransactionType.Withdrawal
) { ) {
setConfirmBeforeForget(true); setConfirmBeforeForget(true);
@ -175,97 +195,64 @@ export function TransactionView({
} }
} }
const SHOWING_RETRY_THRESHOLD_SECS = 30; async function doCheckBeforeCancel(): Promise<void> {
setConfirmBeforeCancel(true);
const { i18n } = useTranslationContext();
function TransactionTemplate({
children,
}: {
children: ComponentChildren;
}): VNode {
const showSend = false;
// (transaction.type === TransactionType.PeerPullCredit ||
// transaction.type === TransactionType.PeerPushDebit) &&
// !transaction.info.completed;
const showRetry =
transaction.error !== undefined ||
transaction.timestamp.t_s === "never" ||
(transaction.pending &&
differenceInSeconds(new Date(), transaction.timestamp.t_s * 1000) >
SHOWING_RETRY_THRESHOLD_SECS);
return (
<Fragment>
<section style={{ padding: 8, textAlign: "center" }}>
{transaction?.error ? (
transaction.error.code === 7025 ? (
<AlertView
alert={{
type: "warning",
message: i18n.str`KYC check required for the transaction to complete`,
description:
transaction.error.kycUrl &&
typeof transaction.error.kycUrl === "string" ? (
<div>
<i18n.Translate>
Follow this link to the{` `}
<a href={transaction.error.kycUrl}>KYC verifier</a>
</i18n.Translate>
</div>
) : (
i18n.str`No more information has been provided`
),
}}
/>
) : (
<ErrorAlertView
error={alertFromError(
i18n.str`There was an error trying to complete the transaction`,
transaction.error,
)}
/>
)
) : undefined}
{transaction.pending && (
<WarningBox>
<i18n.Translate>This transaction is not completed</i18n.Translate>
</WarningBox>
)}
</section>
<section>{children}</section>
<footer>
<div>
{showSend ? (
<Button variant="contained" onClick={onSend}>
<i18n.Translate>Send</i18n.Translate>
</Button>
) : null}
</div>
<div>
{showRetry ? (
<Button variant="contained" onClick={onRetry}>
<i18n.Translate>Retry</i18n.Translate>
</Button>
) : null}
<Button
variant="contained"
color="error"
onClick={doCheckBeforeForget}
>
<i18n.Translate>Forget</i18n.Translate>
</Button>
</div>
</footer>
</Fragment>
);
} }
if (transaction.type === TransactionType.Withdrawal) { const SHOWING_RETRY_THRESHOLD_SECS = 30;
const total = Amounts.parseOrThrow(transaction.amountEffective);
const chosen = Amounts.parseOrThrow(transaction.amountRaw); const showSend = false;
return ( // (transaction.type === TransactionType.PeerPullCredit ||
<TransactionTemplate> // transaction.type === TransactionType.PeerPushDebit) &&
// !transaction.info.completed;
const showRetry =
transaction.error !== undefined ||
transaction.timestamp.t_s === "never" ||
(transaction.extendedStatus === ExtendedStatus.Pending &&
differenceInSeconds(new Date(), transaction.timestamp.t_s * 1000) >
SHOWING_RETRY_THRESHOLD_SECS);
const transactionStillActive =
transaction.extendedStatus !== ExtendedStatus.Aborted &&
transaction.extendedStatus !== ExtendedStatus.Done &&
transaction.extendedStatus !== ExtendedStatus.Failed;
return (
<Fragment>
<section style={{ padding: 8, textAlign: "center" }}>
{transaction?.error ? (
transaction.error.code === 7025 ? (
<AlertView
alert={{
type: "warning",
message: i18n.str`KYC check required for the transaction to complete`,
description:
transaction.error.kycUrl &&
typeof transaction.error.kycUrl === "string" ? (
<div>
<i18n.Translate>
Follow this link to the{` `}
<a href={transaction.error.kycUrl}>KYC verifier</a>
</i18n.Translate>
</div>
) : (
i18n.str`No more information has been provided`
),
}}
/>
) : (
<ErrorAlertView
error={alertFromError(
i18n.str`There was an error trying to complete the transaction`,
transaction.error,
)}
/>
)
) : undefined}
{transaction.extendedStatus === ExtendedStatus.Pending && (
<WarningBox>
<i18n.Translate>This transaction is not completed</i18n.Translate>
</WarningBox>
)}
{confirmBeforeForget ? ( {confirmBeforeForget ? (
<Overlay> <Overlay>
<CenteredDialog> <CenteredDialog>
@ -282,18 +269,134 @@ export function TransactionView({
<Button <Button
variant="contained" variant="contained"
color="secondary" color="secondary"
onClick={async () => setConfirmBeforeForget(false)} onClick={
(async () =>
setConfirmBeforeForget(false)) as SafeHandler<void>
}
> >
<i18n.Translate>Cancel</i18n.Translate> <i18n.Translate>Cancel</i18n.Translate>
</Button> </Button>
<Button variant="contained" color="error" onClick={onDelete}> <Button
variant="contained"
color="error"
onClick={safely(
onDelete,
i18n.str`Could not forget transaction`,
)}
>
<i18n.Translate>Confirm</i18n.Translate> <i18n.Translate>Confirm</i18n.Translate>
</Button> </Button>
</footer> </footer>
</CenteredDialog> </CenteredDialog>
</Overlay> </Overlay>
) : undefined} ) : undefined}
{confirmBeforeCancel ? (
<Overlay>
<CenteredDialog>
<header>
<i18n.Translate>Caution!</i18n.Translate>
</header>
<section>
<i18n.Translate>
Doing a cancelation while the transaction still active might
result in lost coins. Do you still want to cancel the
transaction?
</i18n.Translate>
</section>
<footer>
<Button
variant="contained"
color="secondary"
onClick={
(async () =>
setConfirmBeforeCancel(false)) as SafeHandler<void>
}
>
<i18n.Translate>No</i18n.Translate>
</Button>
<Button
variant="contained"
color="error"
onClick={safely(
onCancel,
i18n.str`Could not cancel the active transaction`,
)}
>
<i18n.Translate>Yes</i18n.Translate>
</Button>
</footer>
</CenteredDialog>
</Overlay>
) : undefined}
</section>
<section>{children}</section>
<footer>
<div>
{showSend ? (
<Button
variant="contained"
onClick={safely(onSend, i18n.str`Could not send`)}
>
<i18n.Translate>Send</i18n.Translate>
</Button>
) : null}
</div>
<div>
{showRetry ? (
<Button
variant="contained"
onClick={safely(onRetry, i18n.str`Could not retry`)}
>
<i18n.Translate>Retry</i18n.Translate>
</Button>
) : null}
{transactionStillActive ? (
<Button
variant="contained"
color="error"
onClick={doCheckBeforeCancel as SafeHandler<void>}
>
<i18n.Translate>Cancel</i18n.Translate>
</Button>
) : (
<Button
variant="contained"
color="error"
onClick={doCheckBeforeForget as SafeHandler<void>}
>
<i18n.Translate>Forget</i18n.Translate>
</Button>
)}
</div>
</footer>
</Fragment>
);
}
export function TransactionView({
transaction,
onDelete,
onRetry,
onSend,
onRefund,
onCancel,
}: WalletTransactionProps): VNode {
const { i18n } = useTranslationContext();
const { safely } = useAlertContext();
if (transaction.type === TransactionType.Withdrawal) {
const total = Amounts.parseOrThrow(transaction.amountEffective);
const chosen = Amounts.parseOrThrow(transaction.amountRaw);
return (
<TransactionTemplate
transaction={transaction}
onDelete={onDelete}
onRetry={onRetry}
onSend={onSend}
onCancel={onCancel}
>
<Header <Header
timestamp={transaction.timestamp} timestamp={transaction.timestamp}
type={i18n.str`Withdrawal`} type={i18n.str`Withdrawal`}
@ -303,7 +406,8 @@ export function TransactionView({
{transaction.exchangeBaseUrl} {transaction.exchangeBaseUrl}
</Header> </Header>
{!transaction.pending ? undefined : transaction.withdrawalDetails {transaction.extendedStatus !==
ExtendedStatus.Pending ? undefined : transaction.withdrawalDetails
.type === WithdrawalType.ManualTransfer ? ( .type === WithdrawalType.ManualTransfer ? (
<Fragment> <Fragment>
<BankDetailsByPaytoType <BankDetailsByPaytoType
@ -418,7 +522,13 @@ export function TransactionView({
const total = Amounts.sub(price.effective, refund.effective).amount; const total = Amounts.sub(price.effective, refund.effective).amount;
return ( return (
<TransactionTemplate> <TransactionTemplate
transaction={transaction}
onDelete={onDelete}
onRetry={onRetry}
onSend={onSend}
onCancel={onCancel}
>
<Header <Header
timestamp={transaction.timestamp} timestamp={transaction.timestamp}
total={total} total={total}
@ -491,7 +601,10 @@ export function TransactionView({
<div> <div>
<Button <Button
variant="contained" variant="contained"
onClick={() => onRefund(transaction.proposalId)} onClick={safely(
() => onRefund(transaction.proposalId),
i18n.str`Could not refund`,
)}
> >
<i18n.Translate>Accept</i18n.Translate> <i18n.Translate>Accept</i18n.Translate>
</Button> </Button>
@ -529,7 +642,13 @@ export function TransactionView({
const total = Amounts.parseOrThrow(transaction.amountRaw); const total = Amounts.parseOrThrow(transaction.amountRaw);
const payto = parsePaytoUri(transaction.targetPaytoUri); const payto = parsePaytoUri(transaction.targetPaytoUri);
return ( return (
<TransactionTemplate> <TransactionTemplate
transaction={transaction}
onDelete={onDelete}
onRetry={onRetry}
onSend={onSend}
onCancel={onCancel}
>
<Header <Header
timestamp={transaction.timestamp} timestamp={transaction.timestamp}
type={i18n.str`Deposit`} type={i18n.str`Deposit`}
@ -567,7 +686,13 @@ export function TransactionView({
).amount; ).amount;
return ( return (
<TransactionTemplate> <TransactionTemplate
transaction={transaction}
onDelete={onDelete}
onRetry={onRetry}
onSend={onSend}
onCancel={onCancel}
>
<Header <Header
timestamp={transaction.timestamp} timestamp={transaction.timestamp}
type={i18n.str`Refresh`} type={i18n.str`Refresh`}
@ -588,7 +713,13 @@ export function TransactionView({
const total = Amounts.parseOrThrow(transaction.amountEffective); const total = Amounts.parseOrThrow(transaction.amountEffective);
return ( return (
<TransactionTemplate> <TransactionTemplate
transaction={transaction}
onDelete={onDelete}
onRetry={onRetry}
onSend={onSend}
onCancel={onCancel}
>
<Header <Header
timestamp={transaction.timestamp} timestamp={transaction.timestamp}
type={i18n.str`Tip`} type={i18n.str`Tip`}
@ -613,7 +744,13 @@ export function TransactionView({
if (transaction.type === TransactionType.Refund) { if (transaction.type === TransactionType.Refund) {
const total = Amounts.parseOrThrow(transaction.amountEffective); const total = Amounts.parseOrThrow(transaction.amountEffective);
return ( return (
<TransactionTemplate> <TransactionTemplate
transaction={transaction}
onDelete={onDelete}
onRetry={onRetry}
onSend={onSend}
onCancel={onCancel}
>
<Header <Header
timestamp={transaction.timestamp} timestamp={transaction.timestamp}
type={i18n.str`Refund`} type={i18n.str`Refund`}
@ -666,10 +803,10 @@ export function TransactionView({
return ( return (
<div> <div>
<QR text={text} /> <QR text={text} />
<Button onClick={copy}> <Button onClick={copy as SafeHandler<void>}>
<i18n.Translate>copy</i18n.Translate> <i18n.Translate>copy</i18n.Translate>
</Button> </Button>
<Button onClick={toggle}> <Button onClick={toggle as SafeHandler<void>}>
<i18n.Translate>hide qr</i18n.Translate> <i18n.Translate>hide qr</i18n.Translate>
</Button> </Button>
</div> </div>
@ -678,10 +815,10 @@ export function TransactionView({
return ( return (
<div> <div>
<div>{text.substring(0, 64)}...</div> <div>{text.substring(0, 64)}...</div>
<Button onClick={copy}> <Button onClick={copy as SafeHandler<void>}>
<i18n.Translate>copy</i18n.Translate> <i18n.Translate>copy</i18n.Translate>
</Button> </Button>
<Button onClick={toggle}> <Button onClick={toggle as SafeHandler<void>}>
<i18n.Translate>show qr</i18n.Translate> <i18n.Translate>show qr</i18n.Translate>
</Button> </Button>
</div> </div>
@ -691,7 +828,13 @@ export function TransactionView({
if (transaction.type === TransactionType.PeerPullCredit) { if (transaction.type === TransactionType.PeerPullCredit) {
const total = Amounts.parseOrThrow(transaction.amountEffective); const total = Amounts.parseOrThrow(transaction.amountEffective);
return ( return (
<TransactionTemplate> <TransactionTemplate
transaction={transaction}
onDelete={onDelete}
onRetry={onRetry}
onSend={onSend}
onCancel={onCancel}
>
<Header <Header
timestamp={transaction.timestamp} timestamp={transaction.timestamp}
type={i18n.str`Credit`} type={i18n.str`Credit`}
@ -713,7 +856,8 @@ export function TransactionView({
text={transaction.exchangeBaseUrl as TranslatedString} text={transaction.exchangeBaseUrl as TranslatedString}
kind="neutral" kind="neutral"
/> />
{transaction.pending /** pending is not-pay */ && ( {transaction.extendedStatus ===
ExtendedStatus.Pending /** pending is not-pay */ && (
<Part <Part
title={i18n.str`URI`} title={i18n.str`URI`}
text={<ShowQrWithCopy text={transaction.talerUri} />} text={<ShowQrWithCopy text={transaction.talerUri} />}
@ -738,7 +882,13 @@ export function TransactionView({
if (transaction.type === TransactionType.PeerPullDebit) { if (transaction.type === TransactionType.PeerPullDebit) {
const total = Amounts.parseOrThrow(transaction.amountEffective); const total = Amounts.parseOrThrow(transaction.amountEffective);
return ( return (
<TransactionTemplate> <TransactionTemplate
transaction={transaction}
onDelete={onDelete}
onRetry={onRetry}
onSend={onSend}
onCancel={onCancel}
>
<Header <Header
timestamp={transaction.timestamp} timestamp={transaction.timestamp}
type={i18n.str`Debit`} type={i18n.str`Debit`}
@ -777,7 +927,13 @@ export function TransactionView({
if (transaction.type === TransactionType.PeerPushDebit) { if (transaction.type === TransactionType.PeerPushDebit) {
const total = Amounts.parseOrThrow(transaction.amountEffective); const total = Amounts.parseOrThrow(transaction.amountEffective);
return ( return (
<TransactionTemplate> <TransactionTemplate
transaction={transaction}
onDelete={onDelete}
onRetry={onRetry}
onSend={onSend}
onCancel={onCancel}
>
<Header <Header
timestamp={transaction.timestamp} timestamp={transaction.timestamp}
type={i18n.str`Debit`} type={i18n.str`Debit`}
@ -824,7 +980,13 @@ export function TransactionView({
if (transaction.type === TransactionType.PeerPushCredit) { if (transaction.type === TransactionType.PeerPushCredit) {
const total = Amounts.parseOrThrow(transaction.amountEffective); const total = Amounts.parseOrThrow(transaction.amountEffective);
return ( return (
<TransactionTemplate> <TransactionTemplate
transaction={transaction}
onDelete={onDelete}
onRetry={onRetry}
onSend={onSend}
onCancel={onCancel}
>
<Header <Header
timestamp={transaction.timestamp} timestamp={transaction.timestamp}
type={i18n.str`Credit`} type={i18n.str`Credit`}