tx state ui
This commit is contained in:
parent
699a7b453b
commit
4f726b73e6
@ -39,7 +39,7 @@ import {
|
||||
} from "./styled/index.js";
|
||||
import { Time } from "./Time.js";
|
||||
|
||||
export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
export function HistoryItem(props: { tx: Transaction }): VNode {
|
||||
const tx = props.tx;
|
||||
const { i18n } = useTranslationContext();
|
||||
/**
|
||||
@ -48,14 +48,15 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
switch (tx.type) {
|
||||
case TransactionType.Withdrawal:
|
||||
return (
|
||||
<TransactionLayout
|
||||
<Layout
|
||||
id={tx.transactionId}
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"credit"}
|
||||
title={new URL(tx.exchangeBaseUrl).hostname}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"W"}
|
||||
pending={
|
||||
currentState={tx.txState.major}
|
||||
description={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
? tx.withdrawalDetails.type ===
|
||||
WithdrawalType.TalerBankIntegrationApi
|
||||
@ -71,14 +72,15 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
);
|
||||
case TransactionType.InternalWithdrawal:
|
||||
return (
|
||||
<TransactionLayout
|
||||
<Layout
|
||||
id={tx.transactionId}
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"credit"}
|
||||
title={new URL(tx.exchangeBaseUrl).hostname}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"I"}
|
||||
pending={
|
||||
currentState={tx.txState.major}
|
||||
description={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
? tx.withdrawalDetails.type ===
|
||||
WithdrawalType.TalerBankIntegrationApi
|
||||
@ -94,7 +96,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
);
|
||||
case TransactionType.Payment:
|
||||
return (
|
||||
<TransactionLayout
|
||||
<Layout
|
||||
id={tx.transactionId}
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"debit"}
|
||||
@ -102,7 +104,8 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
subtitle={tx.info.summary}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"P"}
|
||||
pending={
|
||||
currentState={tx.txState.major}
|
||||
description={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
? i18n.str`Payment in progress`
|
||||
: undefined
|
||||
@ -111,7 +114,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
);
|
||||
case TransactionType.Refund:
|
||||
return (
|
||||
<TransactionLayout
|
||||
<Layout
|
||||
id={tx.transactionId}
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"credit"}
|
||||
@ -123,7 +126,8 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
} //FIXME: DD37 wallet-core is not returning this value
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"R"}
|
||||
pending={
|
||||
currentState={tx.txState.major}
|
||||
description={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
? i18n.str`Executing refund...`
|
||||
: undefined
|
||||
@ -132,14 +136,15 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
);
|
||||
case TransactionType.Tip:
|
||||
return (
|
||||
<TransactionLayout
|
||||
<Layout
|
||||
id={tx.transactionId}
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"credit"}
|
||||
title={new URL(tx.merchantBaseUrl).hostname}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"T"}
|
||||
pending={
|
||||
currentState={tx.txState.major}
|
||||
description={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
? i18n.str`Grabbing the tipping...`
|
||||
: undefined
|
||||
@ -148,14 +153,15 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
);
|
||||
case TransactionType.Refresh:
|
||||
return (
|
||||
<TransactionLayout
|
||||
<Layout
|
||||
id={tx.transactionId}
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"credit"}
|
||||
title={"Refresh"}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"R"}
|
||||
pending={
|
||||
currentState={tx.txState.major}
|
||||
description={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
? i18n.str`Refreshing coins...`
|
||||
: undefined
|
||||
@ -164,14 +170,15 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
);
|
||||
case TransactionType.Deposit:
|
||||
return (
|
||||
<TransactionLayout
|
||||
<Layout
|
||||
id={tx.transactionId}
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"debit"}
|
||||
title={tx.targetPaytoUri}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"D"}
|
||||
pending={
|
||||
currentState={tx.txState.major}
|
||||
description={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
? i18n.str`Deposit in progress`
|
||||
: undefined
|
||||
@ -180,14 +187,15 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
);
|
||||
case TransactionType.PeerPullCredit:
|
||||
return (
|
||||
<TransactionLayout
|
||||
<Layout
|
||||
id={tx.transactionId}
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"credit"}
|
||||
title={tx.info.summary || "Invoice"}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"I"}
|
||||
pending={
|
||||
currentState={tx.txState.major}
|
||||
description={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
? i18n.str`Waiting to be paid`
|
||||
: undefined
|
||||
@ -196,14 +204,15 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
);
|
||||
case TransactionType.PeerPullDebit:
|
||||
return (
|
||||
<TransactionLayout
|
||||
<Layout
|
||||
id={tx.transactionId}
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"debit"}
|
||||
title={tx.info.summary || "Invoice"}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"I"}
|
||||
pending={
|
||||
currentState={tx.txState.major}
|
||||
description={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
? i18n.str`Payment in progress`
|
||||
: undefined
|
||||
@ -212,14 +221,15 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
);
|
||||
case TransactionType.PeerPushCredit:
|
||||
return (
|
||||
<TransactionLayout
|
||||
<Layout
|
||||
id={tx.transactionId}
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"credit"}
|
||||
title={tx.info.summary || "Transfer"}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"T"}
|
||||
pending={
|
||||
currentState={tx.txState.major}
|
||||
description={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
? i18n.str`Receiving the transfer`
|
||||
: undefined
|
||||
@ -228,14 +238,15 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
);
|
||||
case TransactionType.PeerPushDebit:
|
||||
return (
|
||||
<TransactionLayout
|
||||
<Layout
|
||||
id={tx.transactionId}
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"debit"}
|
||||
title={tx.info.summary || "Transfer"}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"T"}
|
||||
pending={
|
||||
currentState={tx.txState.major}
|
||||
description={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
? i18n.str`Waiting to be received`
|
||||
: undefined
|
||||
@ -248,13 +259,22 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
}
|
||||
}
|
||||
|
||||
function TransactionLayout(props: TransactionLayoutProps): VNode {
|
||||
function Layout(props: LayoutProps): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
return (
|
||||
<HistoryRow
|
||||
href={Pages.balanceTransaction({ tid: props.id })}
|
||||
style={{
|
||||
backgroundColor: props.pending ? "lightcyan" : "inherit",
|
||||
backgroundColor:
|
||||
props.currentState === TransactionMajorState.Pending ||
|
||||
props.currentState === TransactionMajorState.Dialog
|
||||
? "lightcyan"
|
||||
: props.currentState === TransactionMajorState.Failed
|
||||
? "#ff000040"
|
||||
: props.currentState === TransactionMajorState.Aborted ||
|
||||
props.currentState === TransactionMajorState.Aborting
|
||||
? "#00000010"
|
||||
: "inherit",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
@ -276,9 +296,9 @@ function TransactionLayout(props: TransactionLayoutProps): VNode {
|
||||
</div>
|
||||
)}
|
||||
</LargeText>
|
||||
{props.pending && (
|
||||
{props.description && (
|
||||
<LightText style={{ marginTop: 5, marginBottom: 5 }}>
|
||||
<i18n.Translate>{props.pending}</i18n.Translate>
|
||||
<i18n.Translate>{props.description}</i18n.Translate>
|
||||
</LightText>
|
||||
)}
|
||||
<SmallLightText style={{ marginTop: 5 }}>
|
||||
@ -286,7 +306,7 @@ function TransactionLayout(props: TransactionLayoutProps): VNode {
|
||||
</SmallLightText>
|
||||
</Column>
|
||||
<TransactionAmount
|
||||
pending={props.pending !== undefined}
|
||||
currentState={props.currentState}
|
||||
amount={Amounts.parseOrThrow(props.amount)}
|
||||
debitCreditIndicator={props.debitCreditIndicator}
|
||||
/>
|
||||
@ -294,7 +314,7 @@ function TransactionLayout(props: TransactionLayoutProps): VNode {
|
||||
);
|
||||
}
|
||||
|
||||
interface TransactionLayoutProps {
|
||||
interface LayoutProps {
|
||||
debitCreditIndicator: "debit" | "credit" | "unknown";
|
||||
amount: AmountString | "unknown";
|
||||
timestamp: AbsoluteTime;
|
||||
@ -302,13 +322,14 @@ interface TransactionLayoutProps {
|
||||
subtitle?: string;
|
||||
id: string;
|
||||
iconPath: string;
|
||||
pending?: string;
|
||||
currentState: TransactionMajorState;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
interface TransactionAmountProps {
|
||||
debitCreditIndicator: "debit" | "credit" | "unknown";
|
||||
amount: AmountJson;
|
||||
pending: boolean;
|
||||
currentState: TransactionMajorState;
|
||||
}
|
||||
|
||||
function TransactionAmount(props: TransactionAmountProps): VNode {
|
||||
@ -328,24 +349,43 @@ function TransactionAmount(props: TransactionAmountProps): VNode {
|
||||
<Column
|
||||
style={{
|
||||
textAlign: "center",
|
||||
color: props.pending
|
||||
? "gray"
|
||||
: sign === "+"
|
||||
? "darkgreen"
|
||||
: sign === "-"
|
||||
? "darkred"
|
||||
: undefined,
|
||||
color:
|
||||
props.currentState !== TransactionMajorState.Done
|
||||
? "gray"
|
||||
: sign === "+"
|
||||
? "darkgreen"
|
||||
: sign === "-"
|
||||
? "darkred"
|
||||
: undefined,
|
||||
}}
|
||||
>
|
||||
<ExtraLargeText>
|
||||
{sign}
|
||||
{Amounts.stringifyValue(props.amount, 2)}
|
||||
</ExtraLargeText>
|
||||
{props.pending && (
|
||||
<div>
|
||||
<i18n.Translate>PENDING</i18n.Translate>
|
||||
{props.currentState === TransactionMajorState.Aborted ? (
|
||||
<div
|
||||
style={{
|
||||
color: "black",
|
||||
border: "1px black solid",
|
||||
borderRadius: 8,
|
||||
padding: 4,
|
||||
}}
|
||||
>
|
||||
<i18n.Translate>ABORTED</i18n.Translate>
|
||||
</div>
|
||||
)}
|
||||
) : props.currentState === TransactionMajorState.Failed ? (
|
||||
<div
|
||||
style={{
|
||||
color: "red",
|
||||
border: "1px darkred solid",
|
||||
borderRadius: 8,
|
||||
padding: 4,
|
||||
}}
|
||||
>
|
||||
<i18n.Translate>FAILED</i18n.Translate>
|
||||
</div>
|
||||
) : undefined}
|
||||
</Column>
|
||||
);
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<link rel="manifest" href="./manifest.json" />
|
||||
<style>
|
||||
.overlay {
|
||||
position: absolute;
|
||||
@ -54,8 +54,9 @@ justify-content: center;
|
||||
width="1000"
|
||||
height="100%"
|
||||
>
|
||||
<input id="page-url" type="text" />
|
||||
<button onclick="openPage()">open</button>
|
||||
</iframe>
|
||||
<!-- <input id="page-url" type="text" />
|
||||
<button onclick="openPage()">open</button> -->
|
||||
<!-- <a
|
||||
href='javascript:void(window.frames["other"].location = "http://bank.taler:5882")'
|
||||
>open local bank</a
|
||||
@ -77,17 +78,13 @@ justify-content: center;
|
||||
src="about:blank"
|
||||
width="500"
|
||||
height="325"
|
||||
>
|
||||
</iframe>
|
||||
>
|
||||
</iframe>
|
||||
</div>
|
||||
<hr />
|
||||
</iframe>
|
||||
<!-- <hr />
|
||||
<iframe src="tests.html" name="wallet" width="800" height="100%"> </iframe> -->
|
||||
<!-- <hr />
|
||||
<iframe src="stories.html" name="wallet" width="800" height="100%"> -->
|
||||
</iframe>
|
||||
<hr />
|
||||
<script type="module" src="background.dev.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -3,18 +3,17 @@
|
||||
"description": "Privacy preserving and transparent payments",
|
||||
"author": "GNU Taler Developers",
|
||||
"version": "0.9.3.13",
|
||||
"id": "gnu-taler-wallet-web-spa-development",
|
||||
"version_name": "0.9.3-dev.13",
|
||||
"icons": {
|
||||
"16": "static/img/taler-logo-16.png",
|
||||
"19": "static/img/taler-logo-19.png",
|
||||
"32": "static/img/taler-logo-32.png",
|
||||
"38": "static/img/taler-logo-38.png",
|
||||
"48": "static/img/taler-logo-48.png",
|
||||
"64": "static/img/taler-logo-64.png",
|
||||
"128": "static/img/taler-logo-128.png",
|
||||
"256": "static/img/taler-logo-256.png",
|
||||
"512": "static/img/taler-logo-512.png"
|
||||
},
|
||||
"icons": [{
|
||||
"src": "static/img/taler-logo-48.png",
|
||||
"sizes": "48x48"
|
||||
},{
|
||||
"src": "static/img/taler-logo-128.png",
|
||||
"sizes": "128x128"
|
||||
}],
|
||||
"display": "minimal-ui",
|
||||
"start_url": "http://localhost:8080/app/index.html",
|
||||
"manifest_version": 3,
|
||||
"minimum_chrome_version": "88",
|
||||
"permissions": [
|
||||
@ -75,8 +74,5 @@
|
||||
},
|
||||
"default_title": "GNU Taler Wallet",
|
||||
"default_popup": "static/popup.html"
|
||||
},
|
||||
"background": {
|
||||
"service_worker": "service_worker.js"
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 8.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.7 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 103 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
@ -43,7 +43,7 @@ import { HistoryView as TestedComponent } from "./History.js";
|
||||
import * as tests from "@gnu-taler/web-util/testing";
|
||||
|
||||
export default {
|
||||
title: "balance",
|
||||
title: "history",
|
||||
component: TestedComponent,
|
||||
};
|
||||
|
||||
@ -291,6 +291,100 @@ export const SomeTransactions = tests.createExample(TestedComponent, {
|
||||
],
|
||||
});
|
||||
|
||||
export const SomeTransactionsInDifferentStates = tests.createExample(
|
||||
TestedComponent,
|
||||
{
|
||||
transactions: [
|
||||
exampleData.withdraw,
|
||||
{
|
||||
...exampleData.withdraw,
|
||||
exchangeBaseUrl: "https://aborted/withdrawal",
|
||||
txState: {
|
||||
major: TransactionMajorState.Aborted,
|
||||
},
|
||||
},
|
||||
{
|
||||
...exampleData.withdraw,
|
||||
exchangeBaseUrl: "https://pending/withdrawal",
|
||||
txState: {
|
||||
major: TransactionMajorState.Pending,
|
||||
},
|
||||
},
|
||||
{
|
||||
...exampleData.withdraw,
|
||||
exchangeBaseUrl: "https://failed/withdrawal",
|
||||
txState: {
|
||||
major: TransactionMajorState.Failed,
|
||||
},
|
||||
},
|
||||
{
|
||||
...exampleData.payment,
|
||||
info: {
|
||||
...exampleData.payment.info,
|
||||
summary: "normal payment",
|
||||
},
|
||||
},
|
||||
{
|
||||
...exampleData.payment,
|
||||
info: {
|
||||
...exampleData.payment.info,
|
||||
summary: "aborting in progress",
|
||||
},
|
||||
txState: {
|
||||
major: TransactionMajorState.Aborting,
|
||||
},
|
||||
},
|
||||
{
|
||||
...exampleData.payment,
|
||||
info: {
|
||||
...exampleData.payment.info,
|
||||
summary: "aborted payment",
|
||||
},
|
||||
txState: {
|
||||
major: TransactionMajorState.Aborted,
|
||||
},
|
||||
},
|
||||
{
|
||||
...exampleData.payment,
|
||||
info: {
|
||||
...exampleData.payment.info,
|
||||
summary: "pending payment",
|
||||
},
|
||||
txState: {
|
||||
major: TransactionMajorState.Pending,
|
||||
},
|
||||
},
|
||||
{
|
||||
...exampleData.payment,
|
||||
info: {
|
||||
...exampleData.payment.info,
|
||||
summary: "failed payment",
|
||||
},
|
||||
txState: {
|
||||
major: TransactionMajorState.Failed,
|
||||
},
|
||||
},
|
||||
exampleData.refund,
|
||||
exampleData.tip,
|
||||
exampleData.deposit,
|
||||
],
|
||||
balances: [
|
||||
{
|
||||
available: "USD:10",
|
||||
pendingIncoming: "USD:0",
|
||||
pendingOutgoing: "USD:0",
|
||||
hasPendingTransactions: false,
|
||||
requiresUserInput: false,
|
||||
scopeInfo: {
|
||||
currency: "Ásd",
|
||||
type: ScopeType.Auditor,
|
||||
url: "",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
export const SomeTransactionsWithTwoCurrencies = tests.createExample(
|
||||
TestedComponent,
|
||||
{
|
||||
|
@ -33,7 +33,7 @@ import {
|
||||
NiceSelect,
|
||||
} from "../components/styled/index.js";
|
||||
import { Time } from "../components/Time.js";
|
||||
import { TransactionItem } from "../components/TransactionItem.js";
|
||||
import { HistoryItem } from "../components/HistoryItem.js";
|
||||
import { alertFromError, useAlertContext } from "../context/alert.js";
|
||||
import { useBackendContext } from "../context/backend.js";
|
||||
import { useTranslationContext } from "@gnu-taler/web-util/browser";
|
||||
@ -269,7 +269,7 @@ export function HistoryView({
|
||||
/>
|
||||
</DateSeparator>
|
||||
{byDate[d].map((tx, i) => (
|
||||
<TransactionItem key={i} tx={tx} />
|
||||
<HistoryItem key={i} tx={tx} />
|
||||
))}
|
||||
</Fragment>
|
||||
);
|
||||
|
@ -52,6 +52,7 @@ import { QR } from "../components/QR.js";
|
||||
import { ShowFullContractTermPopup } from "../components/ShowFullContractTermPopup.js";
|
||||
import {
|
||||
CenteredDialog,
|
||||
ErrorBox,
|
||||
InfoBox,
|
||||
ListOfProducts,
|
||||
Overlay,
|
||||
@ -128,7 +129,7 @@ export function TransactionPage({ tid, goToWalletHistory }: Props): VNode {
|
||||
goToWalletHistory(currency);
|
||||
}}
|
||||
onResume={async () => {
|
||||
await api.wallet.call(WalletApiOperation.SuspendTransaction, {
|
||||
await api.wallet.call(WalletApiOperation.ResumeTransaction, {
|
||||
transactionId,
|
||||
});
|
||||
goToWalletHistory(currency);
|
||||
@ -239,6 +240,7 @@ function TransactionTemplate({
|
||||
transaction.txState.major === TransactionMajorState.Aborting;
|
||||
|
||||
const showRetry =
|
||||
!isFinalState &&
|
||||
transaction.txState.major !== TransactionMajorState.Pending &&
|
||||
transaction.txState.major !== TransactionMajorState.Aborting;
|
||||
|
||||
@ -289,6 +291,16 @@ function TransactionTemplate({
|
||||
<i18n.Translate>This transaction is not completed</i18n.Translate>
|
||||
</WarningBox>
|
||||
)}
|
||||
{transaction.txState.major === TransactionMajorState.Aborted && (
|
||||
<InfoBox>
|
||||
<i18n.Translate>This transaction was aborted</i18n.Translate>
|
||||
</InfoBox>
|
||||
)}
|
||||
{transaction.txState.major === TransactionMajorState.Failed && (
|
||||
<ErrorBox>
|
||||
<i18n.Translate>This transaction failed</i18n.Translate>
|
||||
</ErrorBox>
|
||||
)}
|
||||
{confirmBeforeForget ? (
|
||||
<Overlay>
|
||||
<CenteredDialog>
|
||||
|
Loading…
Reference in New Issue
Block a user