diff options
author | Florian Dold <florian@dold.me> | 2020-11-18 17:33:02 +0100 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2020-11-18 17:33:02 +0100 |
commit | 9cd1062f1bd608b3f5b6910983b339401a227452 (patch) | |
tree | 2d3769985545e5f31f2cd54bcef211afa8007edd /packages | |
parent | d6409f185d332f6ade298f143cdb7803391f4b0b (diff) |
towards a nicer transaction history
Diffstat (limited to 'packages')
10 files changed, 179 insertions, 12 deletions
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 4f318faba..e4cbdd8c3 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -332,6 +332,7 @@ export async function getTransactions( TransactionType.Tip, tipRecord.walletTipId, ), + merchantBaseUrl: tipRecord.merchantBaseUrl, error: tipRecord.lastError, }); }); diff --git a/packages/taler-wallet-core/src/types/transactions.ts b/packages/taler-wallet-core/src/types/transactions.ts index b9d18944a..0a683f298 100644 --- a/packages/taler-wallet-core/src/types/transactions.ts +++ b/packages/taler-wallet-core/src/types/transactions.ts @@ -288,6 +288,8 @@ interface TransactionTip extends TransactionCommon { // Amount will be (or was) added to the wallet's balance after fees and refreshing amountEffective: AmountString; + + merchantBaseUrl: string; } // A transaction shown for refreshes that are not associated to other transactions diff --git a/packages/taler-wallet-webextension/src/pages/popup.tsx b/packages/taler-wallet-webextension/src/pages/popup.tsx index d7cdd548e..8d8d5a85d 100644 --- a/packages/taler-wallet-webextension/src/pages/popup.tsx +++ b/packages/taler-wallet-webextension/src/pages/popup.tsx @@ -36,6 +36,8 @@ import { TransactionsResponse, Transaction, TransactionType, + AmountString, + Timestamp, } from "taler-wallet-core"; import { abbrev, renderAmount, PageLink } from "../renderHtml"; @@ -301,19 +303,161 @@ class WalletBalanceView extends React.Component<any, any> { } } -function Icon({ l }: { l: string }): JSX.Element { - return <div className={"icon"}>{l}</div>; +interface TransactionAmountProps { + debitCreditIndicator: "debit" | "credit" | "unknown"; + amount: AmountString | "unknown"; + pending: boolean; } -function formatAndCapitalize(text: string): string { - text = text.replace("-", " "); - text = text.replace(/^./, text[0].toUpperCase()); - return text; +function TransactionAmount(props: TransactionAmountProps): JSX.Element { + const [currency, amount] = props.amount.split(":"); + let sign: string; + switch (props.debitCreditIndicator) { + case "credit": + sign = "+"; + break; + case "debit": + sign = "-"; + break; + case "unknown": + sign = ""; + } + const style: React.CSSProperties = { + marginLeft: "auto", + display: "flex", + flexDirection: "column", + alignItems: "center", + alignSelf: "center" + }; + if (props.pending) { + style.color = "gray"; + } + return ( + <div style={{ ...style }}> + <div style={{ fontSize: "x-large" }}> + {sign} + {amount} + </div> + <div>{currency}</div> + </div> + ); +} + +interface TransactionLayoutProps { + debitCreditIndicator: "debit" | "credit" | "unknown"; + amount: AmountString | "unknown"; + timestamp: Timestamp; + title: string; + subtitle: string; + iconPath: string; + pending: boolean; +} + +function TransactionLayout(props: TransactionLayoutProps): JSX.Element { + const date = new Date(props.timestamp.t_ms); + const dateStr = date.toLocaleString([], { + dateStyle: "medium", + timeStyle: "short", + } as any); + return ( + <div + style={{ + display: "flex", + flexDirection: "row", + border: "1px solid gray", + borderRadius: "0.5em", + margin: "0.5em 0", + justifyContent: "space-between", + padding: "0.5em", + }} + > + <img src={props.iconPath} /> + <div + style={{ display: "flex", flexDirection: "column", marginLeft: "1em" }} + > + <div style={{ fontSize: "small", color: "gray" }}>{dateStr}</div> + <div style={{ fontVariant: "small-caps", fontSize: "x-large" }}> + <span>{props.title}</span> + {props.pending ? ( + <span style={{ color: "darkblue" }}> (Pending)</span> + ) : null} + </div> + + <div>{props.subtitle}</div> + </div> + <TransactionAmount + pending={props.pending} + amount={props.amount} + debitCreditIndicator={props.debitCreditIndicator} + /> + </div> + ); } function TransactionItem(props: { tx: Transaction }): JSX.Element { const tx = props.tx; - return <pre>{JSON.stringify(tx)}</pre> + switch (tx.type) { + case TransactionType.Withdrawal: + return ( + <TransactionLayout + amount={tx.amountEffective} + debitCreditIndicator={"credit"} + title="Withdrawal" + subtitle={`via ${tx.exchangeBaseUrl}`} + timestamp={tx.timestamp} + iconPath="/static/img/ri-bank-line.svg" + pending={tx.pending} + ></TransactionLayout> + ); + case TransactionType.Payment: + return ( + <TransactionLayout + amount={tx.amountEffective} + debitCreditIndicator={"debit"} + title="Payment" + subtitle={tx.info.summary} + timestamp={tx.timestamp} + iconPath="/static/img/ri-shopping-cart-line.svg" + pending={tx.pending} + ></TransactionLayout> + ); + case TransactionType.Refund: + return ( + <TransactionLayout + amount={tx.amountEffective} + debitCreditIndicator={"credit"} + title="Refund" + subtitle={tx.info.summary} + timestamp={tx.timestamp} + iconPath="/static/img/ri-refund-2-line.svg" + pending={tx.pending} + ></TransactionLayout> + ); + case TransactionType.Tip: + return ( + <TransactionLayout + amount={tx.amountEffective} + debitCreditIndicator={"credit"} + title="Tip" + subtitle={`from ${new URL(tx.merchantBaseUrl).hostname}`} + timestamp={tx.timestamp} + iconPath="/static/img/ri-hand-heart-line.svg" + pending={tx.pending} + ></TransactionLayout> + ); + case TransactionType.Refresh: + return ( + <TransactionLayout + amount={tx.amountEffective} + debitCreditIndicator={"credit"} + title="Refresh" + subtitle={`via exchange ${tx.exchangeBaseUrl}`} + timestamp={tx.timestamp} + iconPath="/static/img/ri-refresh-line.svg" + pending={tx.pending} + ></TransactionLayout> + ); + } } function WalletHistory(props: any): JSX.Element { @@ -334,9 +478,11 @@ function WalletHistory(props: any): JSX.Element { return <div>Loading ...</div>; } + const txs = [...transactions.transactions].reverse(); + return ( <div> - {transactions.transactions.map((tx) => ( + {txs.map((tx) => ( <TransactionItem tx={tx} /> ))} </div> @@ -379,10 +525,7 @@ function WalletDebug(props: any): JSX.Element { return ( <div> <p>Debug tools:</p> - <button onClick={openExtensionPage("/popup.html")}>wallet tab</button> - <button onClick={openExtensionPage("/benchmark.html")}>benchmark</button> - <button onClick={openExtensionPage("/show-db.html")}>show db</button> - <button onClick={openExtensionPage("/tree.html")}>show tree</button> + <button onClick={openExtensionPage("/static/popup.html")}>wallet tab</button> <br /> <button onClick={confirmReset}>reset</button> <button onClick={reload}>reload chrome extension</button> diff --git a/packages/taler-wallet-webextension/static/img/COPYRIGHT b/packages/taler-wallet-webextension/static/img/COPYRIGHT new file mode 100644 index 000000000..d1a2bf1de --- /dev/null +++ b/packages/taler-wallet-webextension/static/img/COPYRIGHT @@ -0,0 +1,15 @@ +# Icon Copyright Info + +## Remix Icons + +https://github.com/Remix-Design/RemixIcon + +Remix Icon is licensed under the Apache License Version 2.0. Feel free to use +these icons in your products and distribute them. We would be very grateful if +you mention "Remix Icon" in your product info, but it's not required. The only +thing we ask is that these icons are not for sale. + + +### Used Icons + +* ri-*.svg diff --git a/packages/taler-wallet-webextension/static/img/ri-bank-line.svg b/packages/taler-wallet-webextension/static/img/ri-bank-line.svg new file mode 100644 index 000000000..8d987df79 --- /dev/null +++ b/packages/taler-wallet-webextension/static/img/ri-bank-line.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M2 20h20v2H2v-2zm2-8h2v7H4v-7zm5 0h2v7H9v-7zm4 0h2v7h-2v-7zm5 0h2v7h-2v-7zM2 7l10-5 10 5v4H2V7zm2 1.236V9h16v-.764l-8-4-8 4zM12 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></svg> diff --git a/packages/taler-wallet-webextension/static/img/ri-file-unknown-line.svg b/packages/taler-wallet-webextension/static/img/ri-file-unknown-line.svg new file mode 100644 index 000000000..5203d49f5 --- /dev/null +++ b/packages/taler-wallet-webextension/static/img/ri-file-unknown-line.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11 15h2v2h-2v-2zm2-1.645V14h-2v-1.5a1 1 0 0 1 1-1 1.5 1.5 0 1 0-1.471-1.794l-1.962-.393A3.501 3.501 0 1 1 13 13.355zM15 4H5v16h14V8h-4V4zM3 2.992C3 2.444 3.447 2 3.999 2H16l5 5v13.993A1 1 0 0 1 20.007 22H3.993A1 1 0 0 1 3 21.008V2.992z"/></svg> diff --git a/packages/taler-wallet-webextension/static/img/ri-hand-heart-line.svg b/packages/taler-wallet-webextension/static/img/ri-hand-heart-line.svg new file mode 100644 index 000000000..a9c195eac --- /dev/null +++ b/packages/taler-wallet-webextension/static/img/ri-hand-heart-line.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M5 9a1 1 0 0 1 1 1 6.97 6.97 0 0 1 4.33 1.5h2.17c1.332 0 2.53.579 3.353 1.499L19 13a5 5 0 0 1 4.516 2.851C21.151 18.972 17.322 21 13 21c-2.79 0-5.15-.603-7.06-1.658A.998.998 0 0 1 5 20H2a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1h3zm1.001 3L6 17.021l.045.033C7.84 18.314 10.178 19 13 19c3.004 0 5.799-1.156 7.835-3.13l.133-.133-.12-.1a2.994 2.994 0 0 0-1.643-.63L19 15l-2.112-.001c.073.322.112.657.112 1.001v1H8v-2l6.79-.001-.034-.078a2.501 2.501 0 0 0-2.092-1.416L12.5 13.5H9.57A4.985 4.985 0 0 0 6.002 12zM4 11H3v7h1v-7zm9.646-7.425L14 3.93l.354-.354a2.5 2.5 0 1 1 3.535 3.536L14 11l-3.89-3.89a2.5 2.5 0 1 1 3.536-3.535zm-2.12 1.415a.5.5 0 0 0-.06.637l.058.069L14 8.17l2.476-2.474a.5.5 0 0 0 .058-.638l-.058-.07a.5.5 0 0 0-.638-.057l-.07.058-1.769 1.768-1.767-1.77-.068-.056a.5.5 0 0 0-.638.058z"/></svg> diff --git a/packages/taler-wallet-webextension/static/img/ri-refresh-line.svg b/packages/taler-wallet-webextension/static/img/ri-refresh-line.svg new file mode 100644 index 000000000..6efa8554b --- /dev/null +++ b/packages/taler-wallet-webextension/static/img/ri-refresh-line.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M5.463 4.433A9.961 9.961 0 0 1 12 2c5.523 0 10 4.477 10 10 0 2.136-.67 4.116-1.81 5.74L17 12h3A8 8 0 0 0 6.46 6.228l-.997-1.795zm13.074 15.134A9.961 9.961 0 0 1 12 22C6.477 22 2 17.523 2 12c0-2.136.67-4.116 1.81-5.74L7 12H4a8 8 0 0 0 13.54 5.772l.997 1.795z"/></svg> diff --git a/packages/taler-wallet-webextension/static/img/ri-refund-2-line.svg b/packages/taler-wallet-webextension/static/img/ri-refund-2-line.svg new file mode 100644 index 000000000..5805daf09 --- /dev/null +++ b/packages/taler-wallet-webextension/static/img/ri-refund-2-line.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M5.671 4.257c3.928-3.219 9.733-2.995 13.4.672 3.905 3.905 3.905 10.237 0 14.142-3.905 3.905-10.237 3.905-14.142 0A9.993 9.993 0 0 1 2.25 9.767l.077-.313 1.934.51a8 8 0 1 0 3.053-4.45l-.221.166 1.017 1.017-4.596 1.06 1.06-4.596 1.096 1.096zM13 6v2h2.5v2H10a.5.5 0 0 0-.09.992L10 11h4a2.5 2.5 0 1 1 0 5h-1v2h-2v-2H8.5v-2H14a.5.5 0 0 0 .09-.992L14 13h-4a2.5 2.5 0 1 1 0-5h1V6h2z"/></svg> diff --git a/packages/taler-wallet-webextension/static/img/ri-shopping-cart-line.svg b/packages/taler-wallet-webextension/static/img/ri-shopping-cart-line.svg new file mode 100644 index 000000000..50dabf446 --- /dev/null +++ b/packages/taler-wallet-webextension/static/img/ri-shopping-cart-line.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M4 16V4H2V2h3a1 1 0 0 1 1 1v12h12.438l2-8H8V5h13.72a1 1 0 0 1 .97 1.243l-2.5 10a1 1 0 0 1-.97.757H5a1 1 0 0 1-1-1zm2 7a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm12 0a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></svg> |