summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-04-16 13:04:31 -0300
committerSebastian <sebasjm@gmail.com>2024-04-16 13:04:31 -0300
commit80fee4ee613ccfcbf951636eaac7ef61957d312d (patch)
tree6abb2a6af02f8d7a7b2c401c9bb90cc95681bb85
parent6bb782677cd14e3ec6af249f5d8a2e93dc8e8b5f (diff)
downloadwallet-core-80fee4ee613ccfcbf951636eaac7ef61957d312d.tar.gz
wallet-core-80fee4ee613ccfcbf951636eaac7ef61957d312d.tar.bz2
wallet-core-80fee4ee613ccfcbf951636eaac7ef61957d312d.zip
fix #8735
-rw-r--r--packages/taler-wallet-core/src/wallet.ts6
-rw-r--r--packages/taler-wallet-webextension/src/NavigationBar.tsx33
-rw-r--r--packages/taler-wallet-webextension/src/svg/search_24px.inline.svg4
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Application.tsx43
-rw-r--r--packages/taler-wallet-webextension/src/wallet/History.stories.tsx257
-rw-r--r--packages/taler-wallet-webextension/src/wallet/History.tsx151
6 files changed, 324 insertions, 170 deletions
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index fb5a93693..d1234d39f 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -1468,7 +1468,7 @@ export function getObservedWalletExecutionContext(
ws: InternalWalletState,
cancellationToken: CancellationToken,
oc: ObservabilityContext,
-) {
+): WalletExecutionContext {
const wex: WalletExecutionContext = {
ws,
cancellationToken,
@@ -1485,7 +1485,7 @@ export function getNormalWalletExecutionContext(
ws: InternalWalletState,
cancellationToken: CancellationToken,
oc: ObservabilityContext,
-) {
+): WalletExecutionContext {
const wex: WalletExecutionContext = {
ws,
cancellationToken,
@@ -1893,7 +1893,7 @@ export class InternalWalletState {
} finally {
for (const token of tokens) {
this.resourceLocks.delete(token);
- let waiter = (this.resourceWaiters[token] ?? []).shift();
+ const waiter = (this.resourceWaiters[token] ?? []).shift();
if (waiter) {
waiter.resolve();
}
diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx b/packages/taler-wallet-webextension/src/NavigationBar.tsx
index 527600c96..fe348f7fb 100644
--- a/packages/taler-wallet-webextension/src/NavigationBar.tsx
+++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx
@@ -34,6 +34,7 @@ import {
} from "./components/styled/index.js";
import { useBackendContext } from "./context/backend.js";
import { useAsyncAsHook } from "./hooks/useAsyncAsHook.js";
+import searchIcon from "./svg/search_24px.inline.svg";
import qrIcon from "./svg/qr_code_24px.inline.svg";
import settingsIcon from "./svg/settings_black_24dp.inline.svg";
import warningIcon from "./svg/warning_24px.inline.svg";
@@ -55,7 +56,7 @@ type PageLocation<DynamicPart extends object> = {
function replaceAll(
pattern: string,
vars: Record<string, string>,
- values: Record<string, any>,
+ values: Record<string, string>,
): string {
let result = pattern;
for (const v in vars) {
@@ -75,16 +76,20 @@ function pageDefinition<T extends object>(pattern: string): PageLocation<T> {
`page definition pattern ${pattern} doesn't have any parameter`,
);
- const vars = patternParams.reduce((prev, cur) => {
- const pName = cur.match(/(\w+)/g);
+ const vars = patternParams.reduce(
+ (prev, cur) => {
+ const pName = cur.match(/(\w+)/g);
- //skip things like :? in the path pattern
- if (!pName || !pName[0]) return prev;
- const name = pName[0];
- return { ...prev, [name]: cur };
- }, {} as Record<string, string>);
+ //skip things like :? in the path pattern
+ if (!pName || !pName[0]) return prev;
+ const name = pName[0];
+ return { ...prev, [name]: cur };
+ },
+ {} as Record<string, string>,
+ );
- const f = (values: T): string => replaceAll(pattern, vars, values ?? {});
+ const f = (values: T): string =>
+ replaceAll(pattern, vars, (values ?? {}) as Record<string, string>);
f.pattern = pattern;
return f;
}
@@ -95,6 +100,9 @@ export const Pages = {
balanceHistory: pageDefinition<{ currency?: string }>(
"/balance/history/:currency?",
),
+ searchHistory: pageDefinition<{ currency?: string }>(
+ "/search/history/:currency?",
+ ),
balanceDeposit: pageDefinition<{ amount: string }>(
"/balance/deposit/:amount",
),
@@ -268,6 +276,13 @@ export function WalletNavBar({ path }: { path?: WalletNavBarOptions }): VNode {
<div
style={{ display: "flex", paddingTop: 4, justifyContent: "right" }}
>
+ <a href={Pages.searchHistory({})}>
+ <SvgIcon
+ title={i18n.str`Search transactions`}
+ dangerouslySetInnerHTML={{ __html: searchIcon }}
+ color="white"
+ />
+ </a>
<a href={Pages.qr}>
<SvgIcon
title={i18n.str`QR Reader and Taler URI`}
diff --git a/packages/taler-wallet-webextension/src/svg/search_24px.inline.svg b/packages/taler-wallet-webextension/src/svg/search_24px.inline.svg
new file mode 100644
index 000000000..d880cbf0f
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/svg/search_24px.inline.svg
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" viewBox="0 0 24 24" height="24" width="24">
+ <path fill-rule="evenodd" d="M10.5 3.75a6.75 6.75 0 1 0 0 13.5 6.75 6.75 0 0 0 0-13.5ZM2.25 10.5a8.25 8.25 0 1 1 14.59 5.28l4.69 4.69a.75.75 0 1 1-1.06 1.06l-4.69-4.69A8.25 8.25 0 0 1 2.25 10.5Z" clip-rule="evenodd" />
+</svg>
+
diff --git a/packages/taler-wallet-webextension/src/wallet/Application.tsx b/packages/taler-wallet-webextension/src/wallet/Application.tsx
index 5c31701e2..884c2eab7 100644
--- a/packages/taler-wallet-webextension/src/wallet/Application.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Application.tsx
@@ -157,7 +157,7 @@ export function Application(): VNode {
)}
/>
- <Route
+<Route
path={Pages.balanceHistory.pattern}
component={({ currency }: { currency?: string }) => (
<WalletTemplate path="balance" goToTransaction={redirectToTxInfo} goToURL={redirectToURL}>
@@ -178,6 +178,27 @@ export function Application(): VNode {
)}
/>
<Route
+ path={Pages.searchHistory.pattern}
+ component={({ currency }: { currency?: string }) => (
+ <WalletTemplate path="balance" goToTransaction={redirectToTxInfo} goToURL={redirectToURL}>
+ <HistoryPage
+ currency={currency}
+ search
+ goToWalletDeposit={(currency: string) =>
+ redirectTo(Pages.sendCash({ amount: `${currency}:0` }))
+ }
+ goToWalletManualWithdraw={(currency?: string) =>
+ redirectTo(
+ Pages.receiveCash({
+ amount: !currency ? undefined : `${currency}:0`,
+ }),
+ )
+ }
+ />
+ </WalletTemplate>
+ )}
+ />
+ <Route
path={Pages.sendCash.pattern}
component={({ amount }: { amount?: string }) => (
<WalletTemplate path="balance" goToURL={redirectToURL}>
@@ -568,17 +589,17 @@ function Redirect({ to }: { to: string }): null {
return null;
}
-function matchesRoute(url: string, route: string): boolean {
- type MatcherFunc = (
- url: string,
- route: string,
- opts: any,
- ) => Record<string, string> | false;
+// function matchesRoute(url: string, route: string): boolean {
+// type MatcherFunc = (
+// url: string,
+// route: string,
+// opts: any,
+// ) => Record<string, string> | false;
- const internalPreactMatcher: MatcherFunc = (Router as any).exec;
- const result = internalPreactMatcher(url, route, {});
- return !result ? false : true;
-}
+// const internalPreactMatcher: MatcherFunc = (Router as any).exec;
+// const result = internalPreactMatcher(url, route, {});
+// return !result ? false : true;
+// }
function CallToActionTemplate({
title,
diff --git a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
index c28e4188f..482b8d698 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
@@ -49,17 +49,17 @@ export default {
let count = 0;
const commonTransaction = (): TransactionCommon =>
-({
- amountRaw: "USD:10",
- amountEffective: "USD:9",
- txState: {
- major: TransactionMajorState.Done,
- },
- timestamp: TalerProtocolTimestamp.fromSeconds(
- new Date().getTime() / 1000 - count++ * 60 * 60 * 7,
- ),
- transactionId: String(count),
-} as TransactionCommon);
+ ({
+ amountRaw: "USD:10",
+ amountEffective: "USD:9",
+ txState: {
+ major: TransactionMajorState.Done,
+ },
+ timestamp: TalerProtocolTimestamp.fromSeconds(
+ new Date().getTime() / 1000 - count++ * 60 * 60 * 7,
+ ),
+ transactionId: String(count),
+ }) as TransactionCommon;
const exampleData = {
withdraw: {
@@ -165,7 +165,9 @@ const exampleData = {
export const SomeBalanceWithNoTransactions = tests.createExample(
TestedComponent,
{
- transactions: [],
+ transactionsByDate: {
+ "11/11/11": [],
+ },
balances: [
{
available: "TESTKUDOS:10" as AmountString,
@@ -186,7 +188,9 @@ export const SomeBalanceWithNoTransactions = tests.createExample(
);
export const OneSimpleTransaction = tests.createExample(TestedComponent, {
- transactions: [exampleData.withdraw],
+ transactionsByDate: {
+ "11/11/11": [exampleData.withdraw],
+ },
balances: [
{
flags: [],
@@ -203,13 +207,14 @@ export const OneSimpleTransaction = tests.createExample(TestedComponent, {
},
],
balanceIndex: 0,
-
});
export const TwoTransactionsAndZeroBalance = tests.createExample(
TestedComponent,
{
- transactions: [exampleData.withdraw, exampleData.deposit],
+ transactionsByDate: {
+ "11/11/11": [exampleData.withdraw, exampleData.deposit],
+ },
balances: [
{
flags: [],
@@ -230,14 +235,16 @@ export const TwoTransactionsAndZeroBalance = tests.createExample(
);
export const OneTransactionPending = tests.createExample(TestedComponent, {
- transactions: [
- {
- ...exampleData.withdraw,
- txState: {
- major: TransactionMajorState.Pending,
+ transactionsByDate: {
+ "11/11/11": [
+ {
+ ...exampleData.withdraw,
+ txState: {
+ major: TransactionMajorState.Pending,
+ },
},
- },
- ],
+ ],
+ },
balances: [
{
flags: [],
@@ -257,22 +264,24 @@ export const OneTransactionPending = tests.createExample(TestedComponent, {
});
export const SomeTransactions = tests.createExample(TestedComponent, {
- transactions: [
- exampleData.withdraw,
- exampleData.payment,
- exampleData.withdraw,
- exampleData.payment,
- {
- ...exampleData.payment,
- info: {
- ...exampleData.payment.info,
- summary:
- "this is a long summary that may be cropped because its too long",
+ transactionsByDate: {
+ "11/11/11": [
+ exampleData.withdraw,
+ exampleData.payment,
+ exampleData.withdraw,
+ exampleData.payment,
+ {
+ ...exampleData.payment,
+ info: {
+ ...exampleData.payment.info,
+ summary:
+ "this is a long summary that may be cropped because its too long",
+ },
},
- },
- exampleData.refund,
- exampleData.deposit,
- ],
+ exampleData.refund,
+ exampleData.deposit,
+ ],
+ },
balances: [
{
flags: [],
@@ -294,79 +303,81 @@ 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,
+ transactionsByDate: {
+ "11/11/11": [
+ 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://pending/withdrawal",
+ txState: {
+ major: TransactionMajorState.Pending,
+ },
},
- },
- {
- ...exampleData.withdraw,
- exchangeBaseUrl: "https://failed/withdrawal",
- txState: {
- major: TransactionMajorState.Failed,
+ {
+ ...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",
+ {
+ ...exampleData.payment,
+ info: {
+ ...exampleData.payment.info,
+ summary: "normal payment",
+ },
},
- txState: {
- major: TransactionMajorState.Aborted,
- },
- },
- {
- ...exampleData.payment,
- info: {
- ...exampleData.payment.info,
- summary: "pending payment",
+ {
+ ...exampleData.payment,
+ info: {
+ ...exampleData.payment.info,
+ summary: "aborting in progress",
+ },
+ txState: {
+ major: TransactionMajorState.Aborting,
+ },
},
- txState: {
- major: TransactionMajorState.Pending,
+ {
+ ...exampleData.payment,
+ info: {
+ ...exampleData.payment.info,
+ summary: "aborted payment",
+ },
+ txState: {
+ major: TransactionMajorState.Aborted,
+ },
},
- },
- {
- ...exampleData.payment,
- info: {
- ...exampleData.payment.info,
- summary: "failed payment",
+ {
+ ...exampleData.payment,
+ info: {
+ ...exampleData.payment.info,
+ summary: "pending payment",
+ },
+ txState: {
+ major: TransactionMajorState.Pending,
+ },
},
- txState: {
- major: TransactionMajorState.Failed,
+ {
+ ...exampleData.payment,
+ info: {
+ ...exampleData.payment.info,
+ summary: "failed payment",
+ },
+ txState: {
+ major: TransactionMajorState.Failed,
+ },
},
- },
- exampleData.refund,
- exampleData.deposit,
- ],
+ exampleData.refund,
+ exampleData.deposit,
+ ],
+ },
balances: [
{
flags: [],
@@ -389,15 +400,17 @@ export const SomeTransactionsInDifferentStates = tests.createExample(
export const SomeTransactionsWithTwoCurrencies = tests.createExample(
TestedComponent,
{
- transactions: [
- exampleData.withdraw,
- exampleData.payment,
- exampleData.withdraw,
- exampleData.payment,
- exampleData.refresh,
- exampleData.refund,
- exampleData.deposit,
- ],
+ transactionsByDate: {
+ "11/11/11": [
+ exampleData.withdraw,
+ exampleData.payment,
+ exampleData.withdraw,
+ exampleData.payment,
+ exampleData.refresh,
+ exampleData.refund,
+ exampleData.deposit,
+ ],
+ },
balances: [
{
flags: [],
@@ -431,7 +444,9 @@ export const SomeTransactionsWithTwoCurrencies = tests.createExample(
);
export const FiveOfficialCurrencies = tests.createExample(TestedComponent, {
- transactions: [exampleData.withdraw],
+ transactionsByDate: {
+ "11/11/11": [exampleData.withdraw],
+ },
balances: [
{
flags: [],
@@ -505,7 +520,9 @@ export const FiveOfficialCurrencies = tests.createExample(TestedComponent, {
export const FiveOfficialCurrenciesWithHighValue = tests.createExample(
TestedComponent,
{
- transactions: [exampleData.withdraw],
+ transactionsByDate: {
+ "11/11/11": [exampleData.withdraw],
+ },
balances: [
{
flags: [],
@@ -578,12 +595,14 @@ export const FiveOfficialCurrenciesWithHighValue = tests.createExample(
);
export const PeerToPeer = tests.createExample(TestedComponent, {
- transactions: [
- exampleData.pull_credit,
- exampleData.pull_debit,
- exampleData.push_credit,
- exampleData.push_debit,
- ],
+ transactionsByDate: {
+ "11/11/11": [
+ exampleData.pull_credit,
+ exampleData.pull_debit,
+ exampleData.push_credit,
+ exampleData.push_debit,
+ ],
+ },
balances: [
{
flags: [],
diff --git a/packages/taler-wallet-webextension/src/wallet/History.tsx b/packages/taler-wallet-webextension/src/wallet/History.tsx
index fcd21a5ee..6006ce5e7 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.tsx
@@ -35,7 +35,7 @@ import {
CenteredBoldText,
CenteredText,
DateSeparator,
- NiceSelect
+ NiceSelect,
} from "../components/styled/index.js";
import { alertFromError, useAlertContext } from "../context/alert.js";
import { useBackendContext } from "../context/backend.js";
@@ -45,32 +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: _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 tx = await api.wallet.call(WalletApiOperation.GetTransactions, {
- scopeInfo: balance?.scopeInfo,
+ scopeInfo: showSearch ? undefined : balance?.scopeInfo,
sort: "descending",
includeRefreshes: settings.showRefeshTransactions,
+ search,
});
return { b, tx };
- }, [balanceIndex]);
+ }, [balanceIndex, search]);
useEffect(() => {
return api.listener.onUpdateNotification(
@@ -105,6 +112,41 @@ 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
+ balance={state.response.b.balances[balanceIndex]}
+ search={{
+ value: search ?? "",
+ onInput: pushAlertOnError(async (d: string) => {
+ setSearch(d);
+ }),
+ }}
+ transactionsByDate={byDate}
+ />
+ );
+ }
+
return (
<HistoryView
balanceIndex={balanceIndex}
@@ -112,7 +154,7 @@ export function HistoryPage({
balances={state.response.b.balances}
goToWalletManualWithdraw={goToWalletManualWithdraw}
goToWalletDeposit={goToWalletDeposit}
- transactions={state.response.tx.transactions}
+ transactionsByDate={byDate}
/>
);
}
@@ -121,7 +163,7 @@ export function HistoryView({
balances,
balanceIndex,
changeBalanceIndex,
- transactions,
+ transactionsByDate,
goToWalletManualWithdraw,
goToWalletDeposit,
}: {
@@ -129,7 +171,7 @@ export function HistoryView({
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();
@@ -140,25 +182,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>
@@ -195,8 +219,8 @@ export function HistoryView({
</Button>
)}
</div>
- <div style={{display:"flex", flexDirection:"column"}}>
- <h3 style={{marginBottom: 0}}>Balance</h3>
+ <div style={{ display: "flex", flexDirection: "column" }}>
+ <h3 style={{ marginBottom: 0 }}>Balance</h3>
<div
style={{
width: "fit-content",
@@ -270,7 +294,78 @@ export function HistoryView({
format="dd MMMM yyyy"
/>
</DateSeparator>
- {byDate[d].map((tx, i) => (
+ {transactionsByDate[d].map((tx, i) => (
+ <HistoryItem key={i} tx={tx} />
+ ))}
+ </Fragment>
+ );
+ })}
+ </section>
+ )}
+ </Fragment>
+ );
+}
+
+export function FilteredHistoryView({
+ balance,
+ search,
+ transactionsByDate,
+}: {
+ balance: WalletBalance;
+ search: TextFieldHandler;
+ transactionsByDate: Record<string, Transaction[]>;
+}): VNode {
+ const { i18n } = useTranslationContext();
+
+ const available = balance
+ ? Amounts.jsonifyAmount(balance.available)
+ : undefined;
+
+ 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 ? (
+ <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>