From 80fee4ee613ccfcbf951636eaac7ef61957d312d Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 16 Apr 2024 13:04:31 -0300 Subject: fix #8735 --- .../src/NavigationBar.tsx | 33 ++- .../src/svg/search_24px.inline.svg | 4 + .../src/wallet/Application.tsx | 43 +++- .../src/wallet/History.stories.tsx | 257 +++++++++++---------- .../src/wallet/History.tsx | 151 +++++++++--- 5 files changed, 321 insertions(+), 167 deletions(-) create mode 100644 packages/taler-wallet-webextension/src/svg/search_24px.inline.svg (limited to 'packages/taler-wallet-webextension') 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 = { function replaceAll( pattern: string, vars: Record, - values: Record, + values: Record, ): string { let result = pattern; for (const v in vars) { @@ -75,16 +76,20 @@ function pageDefinition(pattern: string): PageLocation { `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); + //skip things like :? in the path pattern + if (!pName || !pName[0]) return prev; + const name = pName[0]; + return { ...prev, [name]: cur }; + }, + {} as Record, + ); - const f = (values: T): string => replaceAll(pattern, vars, values ?? {}); + const f = (values: T): string => + replaceAll(pattern, vars, (values ?? {}) as Record); 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 {
+ + + + + + 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 { )} /> - ( @@ -177,6 +177,27 @@ export function Application(): VNode { )} /> + ( + + + redirectTo(Pages.sendCash({ amount: `${currency}:0` })) + } + goToWalletManualWithdraw={(currency?: string) => + redirectTo( + Pages.receiveCash({ + amount: !currency ? undefined : `${currency}:0`, + }), + ) + } + /> + + )} + /> ( @@ -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 | false; +// function matchesRoute(url: string, route: string): boolean { +// type MatcherFunc = ( +// url: string, +// route: string, +// opts: any, +// ) => Record | 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; 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: 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 ( + { + setSearch(d); + }), + }} + transactionsByDate={byDate} + /> + ); + } + return ( ); } @@ -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; goToWalletManualWithdraw: (currency?: string) => Promise; - transactions: Transaction[]; + transactionsByDate: Record; 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 ( @@ -195,8 +219,8 @@ export function HistoryView({ )}
-
-

Balance

+
+

Balance

- {byDate[d].map((tx, i) => ( + {transactionsByDate[d].map((tx, i) => ( + + ))} + + ); + })} + + )} + + ); +} + +export function FilteredHistoryView({ + balance, + search, + transactionsByDate, +}: { + balance: WalletBalance; + search: TextFieldHandler; + transactionsByDate: Record; +}): VNode { + const { i18n } = useTranslationContext(); + + const available = balance + ? Amounts.jsonifyAmount(balance.available) + : undefined; + + 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) => ( ))} -- cgit v1.2.3