/* This file is part of GNU Taler (C) 2022 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ import { AbsoluteTime, Amounts, NotificationType, ScopeType, Transaction, WalletBalance, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { startOfDay } from "date-fns"; import { Fragment, VNode, h } from "preact"; import { useEffect, useState } from "preact/hooks"; import { ErrorAlertView } from "../components/CurrentAlerts.js"; import { HistoryItem } from "../components/HistoryItem.js"; import { Loading } from "../components/Loading.js"; import { Time } from "../components/Time.js"; import { CenteredBoldText, CenteredText, DateSeparator, NiceSelect, } from "../components/styled/index.js"; import { alertFromError, useAlertContext } from "../context/alert.js"; import { useBackendContext } from "../context/backend.js"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; import { useSettings } from "../hooks/useSettings.js"; 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; goToWalletManualWithdraw: (currency?: string) => Promise; } export function HistoryPage({ currency: _c, search: showSearch, goToWalletManualWithdraw, goToWalletDeposit, }: Props): VNode { const { i18n } = useTranslationContext(); const api = useBackendContext(); const [balanceIndex, setBalanceIndex] = useState(0); const [search, setSearch] = useState(); 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 tx = await api.wallet.call(WalletApiOperation.GetTransactions, { scopeInfo: showSearch ? undefined : balance?.scopeInfo, sort: "descending", includeRefreshes: settings.showRefeshTransactions, search, }); return { b, tx }; }, [balanceIndex, search]); useEffect(() => { return api.listener.onUpdateNotification( [NotificationType.TransactionStateTransition], state?.retry, ); }); const { pushAlertOnError } = useAlertContext(); if (!state) { return ; } if (state.hasError) { return ( ); } if (!state.response.b.balances.length) { return ( ); } 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 ( { setSearch(d); }), }} transactionsByDate={byDate} /> ); } return ( setBalanceIndex(b)} balances={state.response.b.balances} goToWalletManualWithdraw={goToWalletManualWithdraw} goToWalletDeposit={goToWalletDeposit} transactionsByDate={byDate} /> ); } export function HistoryView({ balances, balanceIndex, changeBalanceIndex, transactionsByDate, goToWalletManualWithdraw, goToWalletDeposit, }: { balanceIndex: number; changeBalanceIndex: (s: number) => void; goToWalletDeposit: (currency: string) => Promise; goToWalletManualWithdraw: (currency?: string) => Promise; transactionsByDate: Record; balances: WalletBalance[]; }): VNode { const { i18n } = useTranslationContext(); const balance = balances[balanceIndex]; const available = balance ? Amounts.jsonifyAmount(balance.available) : undefined; const datesWithTransaction: string[] = Object.keys(transactionsByDate); return (
{available && Amounts.isNonZero(available) && ( )}

Balance

{balances.length === 1 ? ( {balance.scopeInfo.currency} ) : (
{balance.scopeInfo.type === ScopeType.Exchange || balance.scopeInfo.type === ScopeType.Auditor ? balance.scopeInfo.url : undefined}
)} {available && ( {Amounts.stringifyValue(available, 2)} )}
{datesWithTransaction.length === 0 ? (
Your transaction history is empty for this currency.
) : (
{datesWithTransaction.map((d, i) => { return ( {transactionsByDate[d].map((tx, i) => ( ))} ); })}
)}
); } export function FilteredHistoryView({ search, transactionsByDate, }: { search: TextFieldHandler; transactionsByDate: Record; }): VNode { const { i18n } = useTranslationContext(); const datesWithTransaction: string[] = Object.keys(transactionsByDate); return (
{datesWithTransaction.length === 0 ? (
Your transaction history is empty for this currency.
) : (
{datesWithTransaction.map((d, i) => { return ( {transactionsByDate[d].map((tx, i) => ( ))} ); })}
)}
); }