diff options
author | Sebastian <sebasjm@gmail.com> | 2021-08-24 12:00:34 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2021-08-24 12:01:22 -0300 |
commit | bbcae18f6a3f284f6fee719a9b90d156da960465 (patch) | |
tree | 4d2d8ffa0f48dd7d7384f30a9c34d25bf29732d6 | |
parent | 514395eec35f1d631f73cc36c58f3fdbb4dd6452 (diff) |
fix ui transaction list
6 files changed, 194 insertions, 344 deletions
diff --git a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx new file mode 100644 index 000000000..e5545b950 --- /dev/null +++ b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx @@ -0,0 +1,169 @@ +import { AmountString, Timestamp, Transaction, TransactionType } from '@gnu-taler/taler-util'; +import { format, formatDistance } from 'date-fns'; +import { h } from 'preact'; +import imageBank from '../../static/img/ri-bank-line.svg'; +import imageHandHeart from '../../static/img/ri-hand-heart-line.svg'; +import imageRefresh from '../../static/img/ri-refresh-line.svg'; +import imageRefund from '../../static/img/ri-refund-2-line.svg'; +import imageShoppingCart from '../../static/img/ri-shopping-cart-line.svg'; +import { Pages } from "../NavigationBar"; +import { Column, ExtraLargeText, HistoryRow, SmallTextLight, LargeText } from './styled/index'; + +export function TransactionItem(props: { tx: Transaction, multiCurrency: boolean }): JSX.Element { + const tx = props.tx; + switch (tx.type) { + case TransactionType.Withdrawal: + return ( + <TransactionLayout + id={tx.transactionId} + amount={tx.amountEffective} + debitCreditIndicator={"credit"} + title={new URL(tx.exchangeBaseUrl).hostname} + timestamp={tx.timestamp} + iconPath={imageBank} + pending={tx.pending} + multiCurrency={props.multiCurrency} + ></TransactionLayout> + ); + case TransactionType.Payment: + return ( + <TransactionLayout + id={tx.transactionId} + amount={tx.amountEffective} + debitCreditIndicator={"debit"} + title={tx.info.merchant.name} + timestamp={tx.timestamp} + iconPath={imageShoppingCart} + pending={tx.pending} + multiCurrency={props.multiCurrency} + ></TransactionLayout> + ); + case TransactionType.Refund: + return ( + <TransactionLayout + id={tx.transactionId} + amount={tx.amountEffective} + debitCreditIndicator={"credit"} + title={tx.info.merchant.name} + timestamp={tx.timestamp} + iconPath={imageRefund} + pending={tx.pending} + multiCurrency={props.multiCurrency} + ></TransactionLayout> + ); + case TransactionType.Tip: + return ( + <TransactionLayout + id={tx.transactionId} + amount={tx.amountEffective} + debitCreditIndicator={"credit"} + title={new URL(tx.merchantBaseUrl).hostname} + timestamp={tx.timestamp} + iconPath={imageHandHeart} + pending={tx.pending} + multiCurrency={props.multiCurrency} + ></TransactionLayout> + ); + case TransactionType.Refresh: + return ( + <TransactionLayout + id={tx.transactionId} + amount={tx.amountEffective} + debitCreditIndicator={"credit"} + title={new URL(tx.exchangeBaseUrl).hostname} + timestamp={tx.timestamp} + iconPath={imageRefresh} + pending={tx.pending} + multiCurrency={props.multiCurrency} + ></TransactionLayout> + ); + case TransactionType.Deposit: + return ( + <TransactionLayout + id={tx.transactionId} + amount={tx.amountEffective} + debitCreditIndicator={"debit"} + title={tx.targetPaytoUri} + timestamp={tx.timestamp} + iconPath={imageRefresh} + pending={tx.pending} + multiCurrency={props.multiCurrency} + ></TransactionLayout> + ); + } +} + +function TransactionLayout(props: TransactionLayoutProps): JSX.Element { + const date = new Date(props.timestamp.t_ms); + const dateStr = format(date, 'dd MMM, hh:mm') + + return ( + <HistoryRow href={Pages.transaction.replace(':tid', props.id)}> + <img src={props.iconPath} /> + <Column> + <LargeText> + <span>{props.title}</span> + {props.pending ? ( + <span style={{ color: "darkblue" }}> (Pending)</span> + ) : null} + </LargeText> + <SmallTextLight>{dateStr}</SmallTextLight> + </Column> + <TransactionAmount + pending={props.pending} + amount={props.amount} + multiCurrency={props.multiCurrency} + debitCreditIndicator={props.debitCreditIndicator} + /> + </HistoryRow> + ); +} + +interface TransactionLayoutProps { + debitCreditIndicator: "debit" | "credit" | "unknown"; + amount: AmountString | "unknown"; + timestamp: Timestamp; + title: string; + id: string; + iconPath: string; + pending: boolean; + multiCurrency: boolean; +} + +interface TransactionAmountProps { + debitCreditIndicator: "debit" | "credit" | "unknown"; + amount: AmountString | "unknown"; + pending: boolean; + multiCurrency: boolean; +} + +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 = ""; + } + return ( + <Column style={{ + color: + props.pending ? "gray" : + (sign === '+' ? 'darkgreen' : + (sign === '-' ? 'darkred' : + undefined)) + }}> + <ExtraLargeText> + {sign} + {amount} + </ExtraLargeText> + {props.multiCurrency && <div>{currency}</div>} + </Column> + ); +} + diff --git a/packages/taler-wallet-webextension/src/components/styled/index.tsx b/packages/taler-wallet-webextension/src/components/styled/index.tsx index 6067fa446..66595d84c 100644 --- a/packages/taler-wallet-webextension/src/components/styled/index.tsx +++ b/packages/taler-wallet-webextension/src/components/styled/index.tsx @@ -269,6 +269,7 @@ export const RowLightBorderGray = styled(Row2)` export const HistoryRow = styled.a` text-decoration: none; + color: #212121; display: flex; justify-content: space-between; @@ -312,6 +313,10 @@ export const LightText = styled.div` export const SmallText = styled.div` font-size: small; ` +export const LargeText = styled.div` + font-size: large; +` + export const ExtraLargeText = styled.div` font-size: x-large; ` diff --git a/packages/taler-wallet-webextension/src/popup/History.stories.tsx b/packages/taler-wallet-webextension/src/popup/History.stories.tsx index c92033d49..c304b86e6 100644 --- a/packages/taler-wallet-webextension/src/popup/History.stories.tsx +++ b/packages/taler-wallet-webextension/src/popup/History.stories.tsx @@ -48,7 +48,7 @@ const exampleData = { withdraw: { ...commonTransaction, type: TransactionType.Withdrawal, - exchangeBaseUrl: 'http://exchange.taler', + exchangeBaseUrl: 'http://exchange.demo.taler.net', withdrawalDetails: { confirmed: false, exchangePaytoUris: ['payto://x-taler-bank/bank/account'], diff --git a/packages/taler-wallet-webextension/src/popup/History.tsx b/packages/taler-wallet-webextension/src/popup/History.tsx index fd97d975f..77d603886 100644 --- a/packages/taler-wallet-webextension/src/popup/History.tsx +++ b/packages/taler-wallet-webextension/src/popup/History.tsx @@ -14,19 +14,13 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { AmountString, Balance, Timestamp, Transaction, TransactionsResponse, TransactionType } from "@gnu-taler/taler-util"; -import { formatDistance } from "date-fns"; -import { JSX, h } from "preact"; +import { AmountString, Balance, Transaction, TransactionsResponse } from "@gnu-taler/taler-util"; +import { h, JSX } from "preact"; import { useEffect, useState } from "preact/hooks"; -import imageBank from '../../static/img/ri-bank-line.svg'; -import imageHandHeart from '../../static/img/ri-hand-heart-line.svg'; -import imageRefresh from '../../static/img/ri-refresh-line.svg'; -import imageRefund from '../../static/img/ri-refund-2-line.svg'; -import imageShoppingCart from '../../static/img/ri-shopping-cart-line.svg'; -import { Column, ExtraLargeText, HistoryRow, PopupBox, SmallTextLight } from "../components/styled"; +import { PopupBox } from "../components/styled"; +import { TransactionItem } from "../components/TransactionItem"; import { useBalances } from "../hooks/useBalances"; import * as wxApi from "../wxApi"; -import { Pages } from "../NavigationBar"; export function HistoryPage(props: any): JSX.Element { @@ -59,20 +53,20 @@ function amountToString(c: AmountString) { export function HistoryView({ list, balances }: { list: Transaction[], balances: Balance[] }) { + const multiCurrency = balances.length > 1 return <PopupBox noPadding> {balances.length > 0 && <header> - {balances.length === 1 && <div class="title"> - Balance: <span>{amountToString(balances[0].available)}</span> - </div>} - {balances.length > 1 && <div class="title"> + {multiCurrency ? <div class="title"> Balance: <ul style={{ margin: 0 }}> {balances.map(b => <li>{b.available}</li>)} </ul> + </div> : <div class="title"> + Balance: <span>{amountToString(balances[0].available)}</span> </div>} </header>} <section> {list.slice(0, 3).map((tx, i) => ( - <TransactionItem key={i} tx={tx} /> + <TransactionItem key={i} tx={tx} multiCurrency={multiCurrency}/> ))} </section> <footer style={{ justifyContent: 'space-around' }}> @@ -83,160 +77,3 @@ export function HistoryView({ list, balances }: { list: Transaction[], balances: </footer> </PopupBox> } - -function TransactionItem(props: { tx: Transaction }): JSX.Element { - const tx = props.tx; - switch (tx.type) { - case TransactionType.Withdrawal: - return ( - <TransactionLayout - id={tx.transactionId} - amount={tx.amountEffective} - debitCreditIndicator={"credit"} - title="Withdrawal" - subtitle={`via ${tx.exchangeBaseUrl}`} - timestamp={tx.timestamp} - iconPath={imageBank} - pending={tx.pending} - ></TransactionLayout> - ); - case TransactionType.Payment: - return ( - <TransactionLayout - id={tx.transactionId} - amount={tx.amountEffective} - debitCreditIndicator={"debit"} - title="Payment" - subtitle={tx.info.summary} - timestamp={tx.timestamp} - iconPath={imageShoppingCart} - pending={tx.pending} - ></TransactionLayout> - ); - case TransactionType.Refund: - return ( - <TransactionLayout - id={tx.transactionId} - amount={tx.amountEffective} - debitCreditIndicator={"credit"} - title="Refund" - subtitle={tx.info.summary} - timestamp={tx.timestamp} - iconPath={imageRefund} - pending={tx.pending} - ></TransactionLayout> - ); - case TransactionType.Tip: - return ( - <TransactionLayout - id={tx.transactionId} - amount={tx.amountEffective} - debitCreditIndicator={"credit"} - title="Tip" - subtitle={`from ${new URL(tx.merchantBaseUrl).hostname}`} - timestamp={tx.timestamp} - iconPath={imageHandHeart} - pending={tx.pending} - ></TransactionLayout> - ); - case TransactionType.Refresh: - return ( - <TransactionLayout - id={tx.transactionId} - amount={tx.amountEffective} - debitCreditIndicator={"credit"} - title="Refresh" - subtitle={`via exchange ${tx.exchangeBaseUrl}`} - timestamp={tx.timestamp} - iconPath={imageRefresh} - pending={tx.pending} - ></TransactionLayout> - ); - case TransactionType.Deposit: - return ( - <TransactionLayout - id={tx.transactionId} - amount={tx.amountEffective} - debitCreditIndicator={"debit"} - title="Refresh" - subtitle={`to ${tx.targetPaytoUri}`} - timestamp={tx.timestamp} - iconPath={imageRefresh} - pending={tx.pending} - ></TransactionLayout> - ); - } -} - -function TransactionLayout(props: TransactionLayoutProps): JSX.Element { - const date = new Date(props.timestamp.t_ms); - const now = new Date(); - const dateStr = formatDistance(date, now, { addSuffix: true }) - return ( - <HistoryRow href={Pages.transaction.replace(':tid', props.id)}> - <img src={props.iconPath} /> - <Column> - <ExtraLargeText> - <span>{props.title}</span> - {props.pending ? ( - <span style={{ color: "darkblue" }}> (Pending)</span> - ) : null} - </ExtraLargeText> - <SmallTextLight>{dateStr}</SmallTextLight> - </Column> - <TransactionAmount - pending={props.pending} - amount={props.amount} - debitCreditIndicator={props.debitCreditIndicator} - /> - </HistoryRow> - ); -} - -interface TransactionLayoutProps { - debitCreditIndicator: "debit" | "credit" | "unknown"; - amount: AmountString | "unknown"; - timestamp: Timestamp; - title: string; - id: string; - subtitle: string; - iconPath: string; - pending: boolean; -} - -interface TransactionAmountProps { - debitCreditIndicator: "debit" | "credit" | "unknown"; - amount: AmountString | "unknown"; - pending: boolean; -} - -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 = ""; - } - return ( - <Column style={{ - color: - props.pending ? "gray" : - (sign === '+' ? 'darkgreen' : - (sign === '-' ? 'darkred' : - undefined)) - }}> - <ExtraLargeText> - {sign} - {amount} - </ExtraLargeText> - <div>{currency}</div> - </Column> - ); -} - diff --git a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx index 7db13fef5..32499a264 100644 --- a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx @@ -50,7 +50,7 @@ const exampleData = { withdraw: { ...commonTransaction(), type: TransactionType.Withdrawal, - exchangeBaseUrl: 'http://exchange.taler', + exchangeBaseUrl: 'http://exchange.demo.taler.net', withdrawalDetails: { confirmed: false, exchangePaytoUris: ['payto://x-taler-bank/bank/account'], @@ -64,7 +64,7 @@ const exampleData = { info: { contractTermsHash: 'ASDZXCASD', merchant: { - name: 'the merchant', + name: 'Blog', }, orderId: '2021.167-03NPY6MCYMVGT', products: [], @@ -88,7 +88,7 @@ const exampleData = { tip: { ...commonTransaction(), type: TransactionType.Tip, - merchantBaseUrl: 'http://merchant.taler', + merchantBaseUrl: 'http://ads.merchant.taler.net/', } as TransactionTip, refund: { ...commonTransaction(), diff --git a/packages/taler-wallet-webextension/src/wallet/History.tsx b/packages/taler-wallet-webextension/src/wallet/History.tsx index 7f9a9b1a8..2bb59fcdb 100644 --- a/packages/taler-wallet-webextension/src/wallet/History.tsx +++ b/packages/taler-wallet-webextension/src/wallet/History.tsx @@ -14,19 +14,14 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { AmountString, Balance, Timestamp, Transaction, TransactionsResponse, TransactionType } from "@gnu-taler/taler-util"; +import { AmountString, Balance, Transaction, TransactionsResponse } from "@gnu-taler/taler-util"; import { format } from "date-fns"; -import { Fragment, JSX, h } from "preact"; +import { Fragment, h, JSX } from "preact"; import { useEffect, useState } from "preact/hooks"; -import imageBank from '../../static/img/ri-bank-line.svg'; -import imageHandHeart from '../../static/img/ri-hand-heart-line.svg'; -import imageRefresh from '../../static/img/ri-refresh-line.svg'; -import imageRefund from '../../static/img/ri-refund-2-line.svg'; -import imageShoppingCart from '../../static/img/ri-shopping-cart-line.svg'; -import { Column, ExtraLargeText, HistoryRow, WalletBox, DateSeparator, SmallTextLight } from "../components/styled"; +import { DateSeparator, WalletBox } from "../components/styled"; +import { TransactionItem } from "../components/TransactionItem"; import { useBalances } from "../hooks/useBalances"; import * as wxApi from "../wxApi"; -import { Pages } from "../NavigationBar"; export function HistoryPage(props: any): JSX.Element { @@ -65,6 +60,8 @@ export function HistoryView({ list, balances }: { list: Transaction[], balances: return rv; }, {} as { [x: string]: Transaction[] }); + const multiCurrency = balances.length > 1 + return <WalletBox noPadding> {balances.length > 0 && <header> {balances.length === 1 && <div class="title"> @@ -81,168 +78,10 @@ export function HistoryView({ list, balances }: { list: Transaction[], balances: return <Fragment> <DateSeparator>{d}</DateSeparator> {byDate[d].map((tx, i) => ( - <TransactionItem key={i} tx={tx} /> + <TransactionItem key={i} tx={tx} multiCurrency={multiCurrency}/> ))} </Fragment> })} </section> </WalletBox> } - -function TransactionItem(props: { tx: Transaction }): JSX.Element { - const tx = props.tx; - switch (tx.type) { - case TransactionType.Withdrawal: - return ( - <TransactionLayout - id={tx.transactionId} - amount={tx.amountEffective} - debitCreditIndicator={"credit"} - title="Withdrawal" - subtitle={`via ${tx.exchangeBaseUrl}`} - timestamp={tx.timestamp} - iconPath={imageBank} - pending={tx.pending} - ></TransactionLayout> - ); - case TransactionType.Payment: - return ( - <TransactionLayout - id={tx.transactionId} - amount={tx.amountEffective} - debitCreditIndicator={"debit"} - title="Payment" - subtitle={tx.info.summary} - timestamp={tx.timestamp} - iconPath={imageShoppingCart} - pending={tx.pending} - ></TransactionLayout> - ); - case TransactionType.Refund: - return ( - <TransactionLayout - id={tx.transactionId} - amount={tx.amountEffective} - debitCreditIndicator={"credit"} - title="Refund" - subtitle={tx.info.summary} - timestamp={tx.timestamp} - iconPath={imageRefund} - pending={tx.pending} - ></TransactionLayout> - ); - case TransactionType.Tip: - return ( - <TransactionLayout - id={tx.transactionId} - amount={tx.amountEffective} - debitCreditIndicator={"credit"} - title="Tip" - subtitle={`from ${new URL(tx.merchantBaseUrl).hostname}`} - timestamp={tx.timestamp} - iconPath={imageHandHeart} - pending={tx.pending} - ></TransactionLayout> - ); - case TransactionType.Refresh: - return ( - <TransactionLayout - id={tx.transactionId} - amount={tx.amountEffective} - debitCreditIndicator={"credit"} - title="Refresh" - subtitle={`via exchange ${tx.exchangeBaseUrl}`} - timestamp={tx.timestamp} - iconPath={imageRefresh} - pending={tx.pending} - ></TransactionLayout> - ); - case TransactionType.Deposit: - return ( - <TransactionLayout - id={tx.transactionId} - amount={tx.amountEffective} - debitCreditIndicator={"debit"} - title="Refresh" - subtitle={`to ${tx.targetPaytoUri}`} - timestamp={tx.timestamp} - iconPath={imageRefresh} - pending={tx.pending} - ></TransactionLayout> - ); - } -} - -function TransactionLayout(props: TransactionLayoutProps): JSX.Element { - const date = new Date(props.timestamp.t_ms); - const dateStr = format(date, 'HH:mm:ss') - return ( - // <a href={Pages.transaction.replace(':tid', props.id)}> - <HistoryRow href={Pages.transaction.replace(':tid', props.id)}> - <img src={props.iconPath} /> - <Column> - <ExtraLargeText> - <span>{props.title}</span> - {props.pending ? ( - <span style={{ color: "darkblue" }}> (Pending)</span> - ) : null} - </ExtraLargeText> - <SmallTextLight>{dateStr}</SmallTextLight> - </Column> - <TransactionAmount - pending={props.pending} - amount={props.amount} - debitCreditIndicator={props.debitCreditIndicator} - /> - </HistoryRow> - // </a> - ); -} - -interface TransactionLayoutProps { - debitCreditIndicator: "debit" | "credit" | "unknown"; - amount: AmountString | "unknown"; - timestamp: Timestamp; - title: string; - id: string; - subtitle: string; - iconPath: string; - pending: boolean; -} - -interface TransactionAmountProps { - debitCreditIndicator: "debit" | "credit" | "unknown"; - amount: AmountString | "unknown"; - pending: boolean; -} - -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 = ""; - } - return ( - <Column style={{ - color: - props.pending ? "gray" : - (sign === '+' ? 'darkgreen' : - (sign === '-' ? 'darkred' : - undefined)) - }}> - <ExtraLargeText> - {sign} - {amount} - </ExtraLargeText> - <div>{currency}</div> - </Column> - ); -} - |