diff options
Diffstat (limited to 'packages/taler-wallet-webextension/src/wallet/History.tsx')
-rw-r--r-- | packages/taler-wallet-webextension/src/wallet/History.tsx | 257 |
1 files changed, 181 insertions, 76 deletions
diff --git a/packages/taler-wallet-webextension/src/wallet/History.tsx b/packages/taler-wallet-webextension/src/wallet/History.tsx index 233bd8f28..f81e6db9f 100644 --- a/packages/taler-wallet-webextension/src/wallet/History.tsx +++ b/packages/taler-wallet-webextension/src/wallet/History.tsx @@ -20,7 +20,7 @@ import { NotificationType, ScopeType, Transaction, - WalletBalance + WalletBalance, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; @@ -45,31 +45,39 @@ import { Button } from "../mui/Button.js"; import { NoBalanceHelp } from "../popup/NoBalanceHelp.js"; import DownloadIcon from "../svg/download_24px.inline.svg"; import UploadIcon from "../svg/upload_24px.inline.svg"; +import { TextField } from "../mui/TextField.js"; +import { TextFieldHandler } from "../mui/handlers.js"; interface Props { currency?: string; + search?: boolean; goToWalletDeposit: (currency: string) => Promise<void>; goToWalletManualWithdraw: (currency?: string) => Promise<void>; } export function HistoryPage({ - currency, + currency: _c, + search: showSearch, goToWalletManualWithdraw, goToWalletDeposit, }: Props): VNode { const { i18n } = useTranslationContext(); const api = useBackendContext(); const [balanceIndex, setBalanceIndex] = useState<number>(0); + const [search, setSearch] = useState<string>(); + const [settings] = useSettings(); const state = useAsyncAsHook(async () => { - const b = await api.wallet.call(WalletApiOperation.GetBalances, {}) - const balance = b.balances.length > 0 ? b.balances[balanceIndex] : undefined + const b = await api.wallet.call(WalletApiOperation.GetBalances, {}); + const balance = + b.balances.length > 0 ? b.balances[balanceIndex] : undefined; const tx = await api.wallet.call(WalletApiOperation.GetTransactions, { - scopeInfo: balance?.scopeInfo, + scopeInfo: showSearch ? undefined : balance?.scopeInfo, sort: "descending", includeRefreshes: settings.showRefeshTransactions, - }) - return { b, tx } - }, [balanceIndex]); + search, + }); + return { b, tx }; + }, [balanceIndex, search]); useEffect(() => { return api.listener.onUpdateNotification( @@ -104,14 +112,48 @@ export function HistoryPage({ /> ); } + + const byDate = state.response.tx.transactions.reduce( + (rv, x) => { + const startDay = + x.timestamp.t_s === "never" + ? 0 + : startOfDay(x.timestamp.t_s * 1000).getTime(); + if (startDay) { + if (!rv[startDay]) { + rv[startDay] = []; + // datesWithTransaction.push(String(startDay)); + } + rv[startDay].push(x); + } + + return rv; + }, + {} as { [x: string]: Transaction[] }, + ); + + if (showSearch) { + return ( + <FilteredHistoryView + search={{ + value: search ?? "", + onInput: pushAlertOnError(async (d: string) => { + setSearch(d); + }), + }} + transactionsByDate={byDate} + /> + ); + } + return ( <HistoryView balanceIndex={balanceIndex} - changeBalanceIndex={b => setBalanceIndex(b)} + changeBalanceIndex={(b) => setBalanceIndex(b)} balances={state.response.b.balances} goToWalletManualWithdraw={goToWalletManualWithdraw} goToWalletDeposit={goToWalletDeposit} - transactions={state.response.tx.transactions} + transactionsByDate={byDate} /> ); } @@ -120,15 +162,15 @@ export function HistoryView({ balances, balanceIndex, changeBalanceIndex, - transactions, + transactionsByDate, goToWalletManualWithdraw, goToWalletDeposit, }: { - balanceIndex: number, + balanceIndex: number; changeBalanceIndex: (s: number) => void; goToWalletDeposit: (currency: string) => Promise<void>; goToWalletManualWithdraw: (currency?: string) => Promise<void>; - transactions: Transaction[]; + transactionsByDate: Record<string, Transaction[]>; balances: WalletBalance[]; }): VNode { const { i18n } = useTranslationContext(); @@ -139,20 +181,7 @@ export function HistoryView({ ? Amounts.jsonifyAmount(balance.available) : undefined; - const datesWithTransaction: string[] = []; - const byDate = transactions.reduce((rv, x) => { - const startDay = - x.timestamp.t_s === "never" ? 0 : startOfDay(x.timestamp.t_s * 1000).getTime(); - if (startDay) { - if (!rv[startDay]) { - rv[startDay] = []; - datesWithTransaction.push(String(startDay)); - } - rv[startDay].push(x); - } - - return rv; - }, {} as { [x: string]: Transaction[] }); + const datesWithTransaction: string[] = Object.keys(transactionsByDate); return ( <Fragment> @@ -163,62 +192,19 @@ export function HistoryView({ flexWrap: "wrap", alignItems: "center", justifyContent: "space-between", + marginRight: 20, }} > - <div - style={{ - width: "fit-content", - display: "flex", - }} - > - {balances.length === 1 ? ( - <CenteredText style={{ fontSize: "x-large", margin: 8 }}> - {balance.scopeInfo.currency} - </CenteredText> - ) : ( - <NiceSelect style={{ flexDirection: "column" }}> - <select - style={{ - fontSize: "x-large", - }} - value={balanceIndex} - onChange={(e) => { - changeBalanceIndex(Number.parseInt(e.currentTarget.value, 10)); - }} - > - {balances.map((entry, index) => { - return ( - <option value={index} key={entry.scopeInfo.currency}> - {entry.scopeInfo.currency} - </option> - ); - })} - </select> - <div style={{ fontSize: "small", color: "grey" }}> - {balance.scopeInfo.type === ScopeType.Exchange || balance.scopeInfo.type === ScopeType.Auditor ? balance.scopeInfo.url : undefined} - </div> - </NiceSelect> - )} - {available && ( - <CenteredBoldText - style={{ - display: "inline-block", - fontSize: "x-large", - margin: 8, - }} - > - {Amounts.stringifyValue(available, 2)} - </CenteredBoldText> - )} - </div> <div> <Button tooltip="Transfer money to the wallet" startIcon={DownloadIcon} variant="contained" - onClick={() => goToWalletManualWithdraw(balance.scopeInfo.currency)} + onClick={() => + goToWalletManualWithdraw(balance.scopeInfo.currency) + } > - <i18n.Translate>Add</i18n.Translate> + <i18n.Translate>Receive</i18n.Translate> </Button> {available && Amounts.isNonZero(available) && ( <Button @@ -232,6 +218,125 @@ export function HistoryView({ </Button> )} </div> + <div style={{ display: "flex", flexDirection: "column" }}> + <h3 style={{ marginBottom: 0 }}>Balance</h3> + <div + style={{ + width: "fit-content", + display: "flex", + }} + > + {balances.length === 1 ? ( + <CenteredText style={{ fontSize: "x-large", margin: 8 }}> + {balance.scopeInfo.currency} + </CenteredText> + ) : ( + <NiceSelect style={{ flexDirection: "column" }}> + <select + style={{ + fontSize: "x-large", + }} + value={balanceIndex} + onChange={(e) => { + changeBalanceIndex( + Number.parseInt(e.currentTarget.value, 10), + ); + }} + > + {balances.map((entry, index) => { + return ( + <option value={index} key={entry.scopeInfo.currency}> + {entry.scopeInfo.currency} + </option> + ); + })} + </select> + <div style={{ fontSize: "small", color: "grey" }}> + {balance.scopeInfo.type === ScopeType.Exchange || + balance.scopeInfo.type === ScopeType.Auditor + ? balance.scopeInfo.url + : undefined} + </div> + </NiceSelect> + )} + {available && ( + <CenteredBoldText + style={{ + display: "inline-block", + fontSize: "x-large", + margin: 8, + }} + > + {Amounts.stringifyValue(available, 2)} + </CenteredBoldText> + )} + </div> + </div> + </div> + </section> + {datesWithTransaction.length === 0 ? ( + <section> + <i18n.Translate> + Your transaction history is empty for this currency. + </i18n.Translate> + </section> + ) : ( + <section> + {datesWithTransaction.map((d, i) => { + return ( + <Fragment key={i}> + <DateSeparator> + <Time + timestamp={AbsoluteTime.fromMilliseconds( + Number.parseInt(d, 10), + )} + format="dd MMMM yyyy" + /> + </DateSeparator> + {transactionsByDate[d].map((tx, i) => ( + <HistoryItem key={i} tx={tx} /> + ))} + </Fragment> + ); + })} + </section> + )} + </Fragment> + ); +} + +export function FilteredHistoryView({ + search, + transactionsByDate, +}: { + search: TextFieldHandler; + transactionsByDate: Record<string, Transaction[]>; +}): VNode { + const { i18n } = useTranslationContext(); + + const datesWithTransaction: string[] = Object.keys(transactionsByDate); + + return ( + <Fragment> + <section> + <div + style={{ + display: "flex", + flexWrap: "wrap", + alignItems: "center", + justifyContent: "space-between", + marginRight: 20, + }} + > + <TextField + label="Search" + variant="filled" + error={search.error} + required + fullWidth + value={search.value} + onChange={search.onInput} + /> </div> </section> {datesWithTransaction.length === 0 ? ( @@ -253,7 +358,7 @@ export function HistoryView({ format="dd MMMM yyyy" /> </DateSeparator> - {byDate[d].map((tx, i) => ( + {transactionsByDate[d].map((tx, i) => ( <HistoryItem key={i} tx={tx} /> ))} </Fragment> |