gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] 01/02: wallet transaction detail view


From: gnunet
Subject: [taler-wallet-core] 01/02: wallet transaction detail view
Date: Mon, 21 Jun 2021 01:52:50 +0200

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository wallet-core.

commit 956fc35a20466ac5ae0889c08b05176207c0a54d
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Sun Jun 20 20:37:35 2021 -0300

    wallet transaction detail view
---
 .../.storybook/preview.js                          |   5 +-
 packages/taler-wallet-webextension/package.json    |   1 +
 .../taler-wallet-webextension/rollup.config.js     |   2 +
 packages/taler-wallet-webextension/src/custom.d.ts |  27 ++
 .../src/popup/History.stories.tsx                  | 256 +++++++++++++++++
 .../src/popup/History.tsx                          |  34 ++-
 .../src/popup/Transaction.stories.tsx              |  76 +++--
 .../src/popup/Transaction.tsx                      | 308 ++++++++-------------
 .../src/popupEntryPoint.tsx                        |   2 +-
 .../taler-wallet-webextension/static/img/empty.png | Bin 0 -> 2785 bytes
 .../static/style/popup.css                         |  33 ++-
 pnpm-lock.yaml                                     |  18 ++
 12 files changed, 531 insertions(+), 231 deletions(-)

diff --git a/packages/taler-wallet-webextension/.storybook/preview.js 
b/packages/taler-wallet-webextension/.storybook/preview.js
index e669398d..6a7c8e86 100644
--- a/packages/taler-wallet-webextension/.storybook/preview.js
+++ b/packages/taler-wallet-webextension/.storybook/preview.js
@@ -15,7 +15,10 @@
  */
 
 import { setupI18n } from "@gnu-taler/taler-util"
-import { strings } from '../src/i18n'
+import { strings } from '../src/i18n/strings.ts'
+import '../static/style/pure.css'
+import '../static/style/popup.css'
+import '../static/style/wallet.css'
 
 const mockConfig = {
   backendURL: 'http://demo.taler.net',
diff --git a/packages/taler-wallet-webextension/package.json 
b/packages/taler-wallet-webextension/package.json
index 60a2ea5d..206ce8c1 100644
--- a/packages/taler-wallet-webextension/package.json
+++ b/packages/taler-wallet-webextension/package.json
@@ -28,6 +28,7 @@
     "@babel/plugin-transform-react-jsx-source": "^7.12.13",
     "@babel/preset-typescript": "^7.13.0",
     "@rollup/plugin-commonjs": "^17.0.0",
+    "@rollup/plugin-image": "^2.0.6",
     "@rollup/plugin-json": "^4.1.0",
     "@rollup/plugin-node-resolve": "^11.1.0",
     "@rollup/plugin-replace": "^2.3.4",
diff --git a/packages/taler-wallet-webextension/rollup.config.js 
b/packages/taler-wallet-webextension/rollup.config.js
index c5bb936c..80b4f6ee 100644
--- a/packages/taler-wallet-webextension/rollup.config.js
+++ b/packages/taler-wallet-webextension/rollup.config.js
@@ -5,6 +5,7 @@ import json from "@rollup/plugin-json";
 import builtins from "builtin-modules";
 import replace from "@rollup/plugin-replace";
 import ignore from "rollup-plugin-ignore"
+import image from '@rollup/plugin-image';
 
 const makePlugins = () => [
     ignore(["module", "os"]),
@@ -29,6 +30,7 @@ const makePlugins = () => [
     }),
 
     json(),
+    image(),
 ];
 
 
diff --git a/packages/taler-wallet-webextension/src/custom.d.ts 
b/packages/taler-wallet-webextension/src/custom.d.ts
new file mode 100644
index 00000000..1981067d
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/custom.d.ts
@@ -0,0 +1,27 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 <http://www.gnu.org/licenses/>
+ */
+declare module "*.jpeg" {
+  const content: any;
+  export default content;
+}
+declare module "*.png" {
+  const content: any;
+  export default content;
+}
+declare module '*.svg' {
+  const content: any;
+  export default content;
+}
diff --git a/packages/taler-wallet-webextension/src/popup/History.stories.tsx 
b/packages/taler-wallet-webextension/src/popup/History.stories.tsx
new file mode 100644
index 00000000..a73b7ea3
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/popup/History.stories.tsx
@@ -0,0 +1,256 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import {
+  PaymentStatus,
+  TransactionCommon, TransactionDeposit, TransactionPayment,
+  TransactionRefresh, TransactionRefund, TransactionTip, TransactionType,
+  TransactionWithdrawal,
+  WithdrawalType
+} from '@gnu-taler/taler-util';
+import { FunctionalComponent } from 'preact';
+import { HistoryView as TestedComponent } from './History';
+
+export default {
+  title: 'popup/transaction/list',
+  component: TestedComponent,
+  decorators: [
+    (Story: any) => <div>
+      <link key="1" rel="stylesheet" type="text/css" href="/style/pure.css" />
+      <link key="2" rel="stylesheet" type="text/css" href="/style/popup.css" />
+      <link key="3" rel="stylesheet" type="text/css" href="/style/wallet.css" 
/>
+      <div style={{ margin: "1em", width: 400, display: 'flex', padding: 
'0.5em', height: 'calc(20rem - 34px)', border: 'black solid 1px' }}>
+        <Story />
+      </div>
+    </div>
+  ],
+};
+
+const commonTransaction = {
+  amountRaw: 'USD:10',
+  amountEffective: 'USD:9',
+  pending: false,
+  timestamp: {
+    t_ms: new Date().getTime()
+  },
+  transactionId: '12',
+} as TransactionCommon
+
+const exampleData = {
+  withdraw: {
+    ...commonTransaction,
+    type: TransactionType.Withdrawal,
+    exchangeBaseUrl: 'http://exchange.taler',
+    withdrawalDetails: {
+      confirmed: false,
+      exchangePaytoUris: ['payto://x-taler-bank/bank/account'],
+      type: WithdrawalType.ManualTransfer,
+    }
+  } as TransactionWithdrawal,
+  payment: {
+    ...commonTransaction,
+    amountEffective: 'USD:11',
+    type: TransactionType.Payment,
+    info: {
+      contractTermsHash: 'ASDZXCASD',
+      merchant: {
+        name: 'the merchant',
+      },
+      orderId: '2021.167-03NPY6MCYMVGT',
+      products: [],
+      summary: 'the summary',
+      fulfillmentMessage: '',
+    },
+    proposalId: '1EMJJH8EP1NX3XF7733NCYS2DBEJW4Q2KA5KEB37MCQJQ8Q5HMC0',
+    status: PaymentStatus.Accepted,
+  } as TransactionPayment,
+  deposit: {
+    ...commonTransaction,
+    type: TransactionType.Deposit,
+    depositGroupId: '#groupId',
+    targetPaytoUri: 'payto://x-taler-bank/bank/account',
+  } as TransactionDeposit,
+  refresh: {
+    ...commonTransaction,
+    type: TransactionType.Refresh,
+    exchangeBaseUrl: 'http://exchange.taler',
+  } as TransactionRefresh,
+  tip: {
+    ...commonTransaction,
+    type: TransactionType.Tip,
+    merchantBaseUrl: 'http://merchant.taler',
+  } as TransactionTip,
+  refund: {
+    ...commonTransaction,
+    type: TransactionType.Refund,
+    refundedTransactionId: 
'payment:1EMJJH8EP1NX3XF7733NCYS2DBEJW4Q2KA5KEB37MCQJQ8Q5HMC0',
+    info: {
+      contractTermsHash: 'ASDZXCASD',
+      merchant: {
+        name: 'the merchant',
+      },
+      orderId: '2021.167-03NPY6MCYMVGT',
+      products: [],
+      summary: 'the summary',
+      fulfillmentMessage: '',
+    },
+  } as TransactionRefund,
+}
+
+function createExample<Props>(Component: FunctionalComponent<Props>, props: 
Partial<Props>) {
+  const r = (args: any) => <Component {...args} />
+  r.args = props
+  return r
+}
+
+export const Empty = createExample(TestedComponent, {
+  list: []
+});
+
+
+export const One = createExample(TestedComponent, {
+  list: [exampleData.withdraw]
+});
+
+export const Several = createExample(TestedComponent, {
+  list: [
+    exampleData.withdraw,
+    exampleData.payment,
+    exampleData.withdraw,
+    exampleData.payment,
+    exampleData.refresh,
+    exampleData.refund,
+    exampleData.tip,
+    exampleData.deposit,
+  ]
+});
+
+// export const WithdrawPending = createExample(TestedComponent, {
+//   transaction: { ...exampleData.withdraw, pending: true },
+// });
+
+
+// export const Payment = createExample(TestedComponent, {
+//   transaction: exampleData.payment
+// });
+
+// export const PaymentWithoutFee = createExample(TestedComponent, {
+//   transaction: {
+//     ...exampleData.payment,
+//     amountRaw: 'USD:11',
+
+//   }
+// });
+
+// export const PaymentPending = createExample(TestedComponent, {
+//   transaction: { ...exampleData.payment, pending: true },
+// });
+
+// export const PaymentWithProducts = createExample(TestedComponent, {
+//   transaction: {
+//     ...exampleData.payment,
+//     info: {
+//       ...exampleData.payment.info,
+//       summary: 'this order has 5 products',
+//       products: [{
+//         description: 't-shirt',
+//         unit: 'shirts',
+//         quantity: 1,
+//       }, {
+//         description: 't-shirt',
+//         unit: 'shirts',
+//         quantity: 1,
+//       }, {
+//         description: 'e-book',
+//       }, {
+//         description: 'beer',
+//         unit: 'pint',
+//         quantity: 15,
+//       }, {
+//         description: 'beer',
+//         unit: 'pint',
+//         quantity: 15,
+//       }]
+//     }
+//   } as TransactionPayment,
+// });
+
+// export const PaymentWithLongSummary = createExample(TestedComponent, {
+//   transaction: {
+//     ...exampleData.payment,
+//     info: {
+//       ...exampleData.payment.info,
+//       summary: 'this is a very long summary that will occupy severals 
lines, this is a very long summary that will occupy severals lines, this is a 
very long summary that will occupy severals lines, this is a very long summary 
that will occupy severals lines, ',
+//       products: [{
+//         description: 'an xl sized t-shirt with some drawings on it, color 
pink',
+//         unit: 'shirts',
+//         quantity: 1,
+//       }, {
+//         description: 'beer',
+//         unit: 'pint',
+//         quantity: 15,
+//       }]
+//     }
+//   } as TransactionPayment,
+// });
+
+
+// export const Deposit = createExample(TestedComponent, {
+//   transaction: exampleData.deposit
+// });
+
+// export const DepositPending = createExample(TestedComponent, {
+//   transaction: { ...exampleData.deposit, pending: true }
+// });
+
+// export const Refresh = createExample(TestedComponent, {
+//   transaction: exampleData.refresh
+// });
+
+// export const Tip = createExample(TestedComponent, {
+//   transaction: exampleData.tip
+// });
+
+// export const TipPending = createExample(TestedComponent, {
+//   transaction: { ...exampleData.tip, pending: true }
+// });
+
+// export const Refund = createExample(TestedComponent, {
+//   transaction: exampleData.refund
+// });
+
+// export const RefundPending = createExample(TestedComponent, {
+//   transaction: { ...exampleData.refund, pending: true }
+// });
+
+// export const RefundWithProducts = createExample(TestedComponent, {
+//   transaction: {
+//     ...exampleData.refund,
+//     info: {
+//       ...exampleData.refund.info,
+//       products: [{
+//         description: 't-shirt',
+//       }, {
+//         description: 'beer',
+//       }]
+//     }
+//   } as TransactionRefund,
+// });
diff --git a/packages/taler-wallet-webextension/src/popup/History.tsx 
b/packages/taler-wallet-webextension/src/popup/History.tsx
index ffcec5e4..1a70f00f 100644
--- a/packages/taler-wallet-webextension/src/popup/History.tsx
+++ b/packages/taler-wallet-webextension/src/popup/History.tsx
@@ -38,17 +38,23 @@ export function HistoryPage(props: any): JSX.Element {
     return <div>Loading ...</div>;
   }
 
-  const txs = [...transactions.transactions].reverse();
+  return <HistoryView list={[...transactions.transactions].reverse()} />;
+}
 
-  return (
-    <div>
-      {txs.map((tx, i) => (
-        <TransactionItem key={i} tx={tx} />
-      ))}
-    </div>
-  );
+export function HistoryView({ list }: { list: Transaction[] }) {
+  return <div style={{ height: 'calc(20rem - 34px )', overflow: 'auto', width: 
'100%' }}>
+    {list.map((tx, i) => (
+      <TransactionItem key={i} tx={tx} />
+    ))}
+  </div>
 }
 
+import imageBank from '../../static/img/ri-bank-line.svg';
+import imageShoppingCart from '../../static/img/ri-shopping-cart-line.svg';
+import imageRefund from '../../static/img/ri-refund-2-line.svg';
+import imageHandHeart from '../../static/img/ri-hand-heart-line.svg';
+import imageRefresh from '../../static/img/ri-refresh-line.svg';
+
 function TransactionItem(props: { tx: Transaction }): JSX.Element {
   const tx = props.tx;
   switch (tx.type) {
@@ -61,7 +67,7 @@ function TransactionItem(props: { tx: Transaction }): 
JSX.Element {
           title="Withdrawal"
           subtitle={`via ${tx.exchangeBaseUrl}`}
           timestamp={tx.timestamp}
-          iconPath="/static/img/ri-bank-line.svg"
+          iconPath={imageBank}
           pending={tx.pending}
         ></TransactionLayout>
       );
@@ -74,7 +80,7 @@ function TransactionItem(props: { tx: Transaction }): 
JSX.Element {
           title="Payment"
           subtitle={tx.info.summary}
           timestamp={tx.timestamp}
-          iconPath="/static/img/ri-shopping-cart-line.svg"
+          iconPath={imageShoppingCart}
           pending={tx.pending}
         ></TransactionLayout>
       );
@@ -87,7 +93,7 @@ function TransactionItem(props: { tx: Transaction }): 
JSX.Element {
           title="Refund"
           subtitle={tx.info.summary}
           timestamp={tx.timestamp}
-          iconPath="/static/img/ri-refund-2-line.svg"
+          iconPath={imageRefund}
           pending={tx.pending}
         ></TransactionLayout>
       );
@@ -100,7 +106,7 @@ function TransactionItem(props: { tx: Transaction }): 
JSX.Element {
           title="Tip"
           subtitle={`from ${new URL(tx.merchantBaseUrl).hostname}`}
           timestamp={tx.timestamp}
-          iconPath="/static/img/ri-hand-heart-line.svg"
+          iconPath={imageHandHeart}
           pending={tx.pending}
         ></TransactionLayout>
       );
@@ -113,7 +119,7 @@ function TransactionItem(props: { tx: Transaction }): 
JSX.Element {
           title="Refresh"
           subtitle={`via exchange ${tx.exchangeBaseUrl}`}
           timestamp={tx.timestamp}
-          iconPath="/static/img/ri-refresh-line.svg"
+          iconPath={imageRefresh}
           pending={tx.pending}
         ></TransactionLayout>
       );
@@ -126,7 +132,7 @@ function TransactionItem(props: { tx: Transaction }): 
JSX.Element {
           title="Refresh"
           subtitle={`to ${tx.targetPaytoUri}`}
           timestamp={tx.timestamp}
-          iconPath="/static/img/ri-refresh-line.svg"
+          iconPath={imageRefresh}
           pending={tx.pending}
         ></TransactionLayout>
       );
diff --git 
a/packages/taler-wallet-webextension/src/popup/Transaction.stories.tsx 
b/packages/taler-wallet-webextension/src/popup/Transaction.stories.tsx
index 3df2687f..bf090cad 100644
--- a/packages/taler-wallet-webextension/src/popup/Transaction.stories.tsx
+++ b/packages/taler-wallet-webextension/src/popup/Transaction.stories.tsx
@@ -37,7 +37,7 @@ export default {
       <link key="1" rel="stylesheet" type="text/css" href="/style/pure.css" />
       <link key="2" rel="stylesheet" type="text/css" href="/style/popup.css" />
       <link key="3" rel="stylesheet" type="text/css" href="/style/wallet.css" 
/>
-      <div style={{ margin: "1em", width: 400 }}>
+      <div style={{ margin: "1em", width: 400, display: 'flex', padding: 
'0.5em', height: 'calc(20rem - 34px)', border: 'black solid 1px' }}>
         <Story />
       </div>
     </div>
@@ -74,7 +74,7 @@ const exampleData = {
       merchant: {
         name: 'the merchant',
       },
-      orderId: '#12345',
+      orderId: '2021.167-03NPY6MCYMVGT',
       products: [],
       summary: 'the summary',
       fulfillmentMessage: '',
@@ -107,7 +107,7 @@ const exampleData = {
       merchant: {
         name: 'the merchant',
       },
-      orderId: '#12345',
+      orderId: '2021.167-03NPY6MCYMVGT',
       products: [],
       summary: 'the summary',
       fulfillmentMessage: '',
@@ -121,69 +121,111 @@ function createExample<Props>(Component: 
FunctionalComponent<Props>, props: Part
   return r
 }
 
-export const NotYetLoaded = createExample(TestedComponent,{});
+export const NotYetLoaded = createExample(TestedComponent, {});
 
-export const Withdraw = createExample(TestedComponent,{
+export const Withdraw = createExample(TestedComponent, {
   transaction: exampleData.withdraw
 });
 
-export const WithdrawPending = createExample(TestedComponent,{
+export const WithdrawPending = createExample(TestedComponent, {
   transaction: { ...exampleData.withdraw, pending: true },
 });
 
 
-export const Payment = createExample(TestedComponent,{
+export const Payment = createExample(TestedComponent, {
   transaction: exampleData.payment
 });
 
-export const PaymentPending = createExample(TestedComponent,{
+export const PaymentWithoutFee = createExample(TestedComponent, {
+  transaction: {
+    ...exampleData.payment,
+    amountRaw: 'USD:11',
+
+  }
+});
+
+export const PaymentPending = createExample(TestedComponent, {
   transaction: { ...exampleData.payment, pending: true },
 });
 
-export const PaymentWithProducts = createExample(TestedComponent,{
+export const PaymentWithProducts = createExample(TestedComponent, {
   transaction: {
     ...exampleData.payment,
     info: {
       ...exampleData.payment.info,
+      summary: 'this order has 5 products',
       products: [{
         description: 't-shirt',
+        unit: 'shirts',
+        quantity: 1,
+      }, {
+        description: 't-shirt',
+        unit: 'shirts',
+        quantity: 1,
+      }, {
+        description: 'e-book',
+      }, {
+        description: 'beer',
+        unit: 'pint',
+        quantity: 15,
+      }, {
+        description: 'beer',
+        unit: 'pint',
+        quantity: 15,
+      }]
+    }
+  } as TransactionPayment,
+});
+
+export const PaymentWithLongSummary = createExample(TestedComponent, {
+  transaction: {
+    ...exampleData.payment,
+    info: {
+      ...exampleData.payment.info,
+      summary: 'this is a very long summary that will occupy severals lines, 
this is a very long summary that will occupy severals lines, this is a very 
long summary that will occupy severals lines, this is a very long summary that 
will occupy severals lines, ',
+      products: [{
+        description: 'an xl sized t-shirt with some drawings on it, color 
pink',
+        unit: 'shirts',
+        quantity: 1,
       }, {
         description: 'beer',
+        unit: 'pint',
+        quantity: 15,
       }]
     }
   } as TransactionPayment,
 });
 
 
-export const Deposit = createExample(TestedComponent,{
+export const Deposit = createExample(TestedComponent, {
   transaction: exampleData.deposit
 });
 
-export const DepositPending = createExample(TestedComponent,{
+export const DepositPending = createExample(TestedComponent, {
   transaction: { ...exampleData.deposit, pending: true }
 });
 
-export const Refresh = createExample(TestedComponent,{
+export const Refresh = createExample(TestedComponent, {
   transaction: exampleData.refresh
 });
 
-export const Tip = createExample(TestedComponent,{
+export const Tip = createExample(TestedComponent, {
   transaction: exampleData.tip
 });
 
-export const TipPending = createExample(TestedComponent,{
+export const TipPending = createExample(TestedComponent, {
   transaction: { ...exampleData.tip, pending: true }
 });
 
-export const Refund = createExample(TestedComponent,{
+export const Refund = createExample(TestedComponent, {
   transaction: exampleData.refund
 });
 
-export const RefundPending = createExample(TestedComponent,{
+export const RefundPending = createExample(TestedComponent, {
   transaction: { ...exampleData.refund, pending: true }
 });
 
-export const RefundWithProducts = createExample(TestedComponent,{
+export const RefundWithProducts = createExample(TestedComponent, {
   transaction: {
     ...exampleData.refund,
     info: {
diff --git a/packages/taler-wallet-webextension/src/popup/Transaction.tsx 
b/packages/taler-wallet-webextension/src/popup/Transaction.tsx
index b1179228..6939a08c 100644
--- a/packages/taler-wallet-webextension/src/popup/Transaction.tsx
+++ b/packages/taler-wallet-webextension/src/popup/Transaction.tsx
@@ -14,14 +14,14 @@
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { Amounts, i18n, Transaction, TransactionType } from 
"@gnu-taler/taler-util";
+import { AmountJson, Amounts, i18n, Transaction, TransactionType } from 
"@gnu-taler/taler-util";
 import { format } from "date-fns";
-import { JSX } from "preact";
+import { Fragment, JSX } from "preact";
 import { route } from 'preact-router';
 import { useEffect, useState } from "preact/hooks";
 import * as wxApi from "../wxApi";
 import { Pages } from "./popup";
-
+import emptyImg from "../../static/img/empty.png"
 
 export function TransactionPage({ tid }: { tid: string; }): JSX.Element {
   const [transaction, setTransaction] = useState<
@@ -59,11 +59,10 @@ export function TransactionView({ transaction, onDelete, 
onBack }: WalletTransac
   }
 
   function Footer() {
-    return <footer style={{ marginTop: 'auto', display: 'flex' }}>
+    return <footer style={{ marginTop: 'auto', display: 'flex', flexShrink: 0 
}}>
       <button onClick={onBack}><i18n.Translate>back</i18n.Translate></button>
       <div style={{ width: '100%', flexDirection: 'row', justifyContent: 
'flex-end', display: 'flex' }}>
-        <button 
onClick={onDelete}><i18n.Translate>remove</i18n.Translate></button>
-
+        <button class="pure-button button-destructive" 
onClick={onDelete}><i18n.Translate>forget</i18n.Translate></button>
       </div>
 
     </footer>
@@ -74,91 +73,66 @@ export function TransactionView({ transaction, onDelete, 
onBack }: WalletTransac
     return <span style={{ fontWeight: 'normal', fontSize: 16, color: 'gray' 
}}>(pending...)</span>
   }
 
+  const Fee = ({ value }: { value: AmountJson }) => Amounts.isNonZero(value) ?
+    <span style="font-size: 16px;font-weight: normal;color: gray;">(fee 
{Amounts.stringify(value)})</span> : null
+
   if (transaction.type === TransactionType.Withdrawal) {
+    const fee = Amounts.sub(
+      Amounts.parseOrThrow(transaction.amountRaw),
+      Amounts.parseOrThrow(transaction.amountEffective),
+    ).amount
     return (
-      <div style={{ display: 'flex', flexDirection: 'column', flex: 1, 
minHeight: '20rem' }} >
-        <section>
-          <h1>Withdrawal <Pending /></h1>
-          <p>
+      <div style={{ display: 'flex', flexDirection: 'column', width: '100%' }} 
>
+        <section style={{ color: transaction.pending ? 'gray' : '', flex: '1 0 
auto', height: 'calc(20rem - 34px - 45px)', overflow: 'auto' }}>
+          <span style="flat: left; font-size:small; 
color:gray">{transaction.timestamp.t_ms === "never" ? "never" : 
format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</span>
+          <span style="float: right; font-size:small; color:gray">
             From <b>{transaction.exchangeBaseUrl}</b>
-          </p>
-          <table class={transaction.pending ? "detailsTable pending" : 
"detailsTable"}>
-            <tr>
-              <td>Amount subtracted</td>
-              <td>{transaction.amountRaw}</td>
-            </tr>
-            <tr>
-              <td>Amount received</td>
-              <td>{transaction.amountEffective}</td>
-            </tr>
-            <tr>
-              <td>Exchange fee</td>
-              <td>{Amounts.stringify(
-                Amounts.sub(
-                  Amounts.parseOrThrow(transaction.amountRaw),
-                  Amounts.parseOrThrow(transaction.amountEffective),
-                ).amount
-              )}</td>
-            </tr>
-            <tr>
-              <td>When</td>
-              <td>{transaction.timestamp.t_ms === "never" ? "never" : 
format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</td>
-            </tr>
-          </table>
+          </span>
+          <h3>Withdraw <Pending /></h3>
+          <h1>{transaction.amountEffective} <Fee value={fee} /></h1>
         </section>
         <Footer />
       </div>
     );
   }
 
+  const showLargePic = () => {
+
+  }
+
   if (transaction.type === TransactionType.Payment) {
+    const fee = Amounts.sub(
+      Amounts.parseOrThrow(transaction.amountEffective),
+      Amounts.parseOrThrow(transaction.amountRaw),
+    ).amount
+
     return (
-      <div style={{ display: 'flex', flexDirection: 'column', flex: 1, 
minHeight: '20rem' }} >
-        <section>
-          <h1>Payment ({transaction.proposalId.substring(0, 10)}...) <Pending 
/></h1>
-          <p>
+      <div style={{ display: 'flex', flexDirection: 'column', width: '100%' }} 
>
+        <section style={{ color: transaction.pending ? 'gray' : '', flex: '1 0 
auto', height: 'calc(20rem - 34px - 45px)', overflow: 'auto' }}>
+          <span style="flat: left; font-size:small; 
color:gray">{transaction.timestamp.t_ms === "never" ? "never" : 
format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</span>
+          <span style="float: right; font-size:small; color:gray">
             To <b>{transaction.info.merchant.name}</b>
+          </span>
+          <h3>Payment <Pending /></h3>
+          <h1>{transaction.amountEffective} <Fee value={fee} /></h1>
+          <span style="font-size:small; 
color:gray">#{transaction.info.orderId}</span>
+          <p>
+            {transaction.info.summary}
           </p>
-          <table class={transaction.pending ? "detailsTable pending" : 
"detailsTable"}>
-            <tr>
-              <td>Order id</td>
-              <td>{transaction.info.orderId}</td>
-            </tr>
-            <tr>
-              <td>Summary</td>
-              <td>{transaction.info.summary}</td>
-            </tr>
-            {transaction.info.products && transaction.info.products.length > 0 
&&
-              <tr>
-                <td>Products</td>
-                <td><ol style={{ margin: 0, textAlign: 'left' }}>
-                  {transaction.info.products.map(p =>
-                    <li>{p.description}</li>
-                  )}</ol></td>
-              </tr>
+          <div>
+            {transaction.info.products && transaction.info.products.length > 0 
&& <div>
+              {transaction.info.products.map(p => <div style="display: flex; 
flex-direction: row; border: 1px solid gray; border-radius: 0.5em; margin: 
0.5em 0px; justify-content: left; padding: 0.5em;">
+                <a href="#" onClick={showLargePic}>
+                  <img class="pure-img" style="display:inline-block" 
src={p.image ? p.image : emptyImg} width="32" height="32" />
+                </a>
+                <div style="display: block; margin-left: 1em;">
+                  {p.quantity && p.quantity > 0 && <div style="font-size: 
small; color: gray;">x {p.quantity} {p.unit}</div>}
+                  <div style={{ textOverflow: 'ellipsis', overflow: 'hidden', 
width: 'calc(20rem - 32px - 32px - 8px - 1em)', whiteSpace: 'nowrap' 
}}>{p.description}</div>
+                </div>
+              </div>)}
+            </div>
             }
-            <tr>
-              <td>Order amount</td>
-              <td>{transaction.amountRaw}</td>
-            </tr>
-            <tr>
-              <td>Order amount and fees</td>
-              <td>{transaction.amountEffective}</td>
-            </tr>
-            <tr>
-              <td>Exchange fee</td>
-              <td>{Amounts.stringify(
-                Amounts.sub(
-                  Amounts.parseOrThrow(transaction.amountEffective),
-                  Amounts.parseOrThrow(transaction.amountRaw),
-                ).amount
-              )}</td>
-            </tr>
-            <tr>
-              <td>When</td>
-              <td>{transaction.timestamp.t_ms === "never" ? "never" : 
format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</td>
-            </tr>
-          </table>
+          </div>
         </section>
         <Footer />
       </div>
@@ -166,36 +140,19 @@ export function TransactionView({ transaction, onDelete, 
onBack }: WalletTransac
   }
 
   if (transaction.type === TransactionType.Deposit) {
+    const fee = Amounts.sub(
+      Amounts.parseOrThrow(transaction.amountRaw),
+      Amounts.parseOrThrow(transaction.amountEffective),
+    ).amount
     return (
-      <div style={{ display: 'flex', flexDirection: 'column', flex: 1, 
minHeight: '20rem' }} >
-        <section>
-          <h1>Deposit ({transaction.depositGroupId}) <Pending /></h1>
-          <p>
+      <div style={{ display: 'flex', flexDirection: 'column', width: '100%' }} 
>
+        <section style={{ color: transaction.pending ? 'gray' : '', flex: '1 0 
auto', height: 'calc(20rem - 34px - 45px)', overflow: 'auto' }}>
+          <span style="flat: left; font-size:small; 
color:gray">{transaction.timestamp.t_ms === "never" ? "never" : 
format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</span>
+          <span style="float: right; font-size:small; color:gray">
             To <b>{transaction.targetPaytoUri}</b>
-          </p>
-          <table class={transaction.pending ? "detailsTable pending" : 
"detailsTable"}>
-            <tr>
-              <td>Amount deposit</td>
-              <td>{transaction.amountRaw}</td>
-            </tr>
-            <tr>
-              <td>Amount deposit and fees</td>
-              <td>{transaction.amountEffective}</td>
-            </tr>
-            <tr>
-              <td>Exchange fee</td>
-              <td>{Amounts.stringify(
-                Amounts.sub(
-                  Amounts.parseOrThrow(transaction.amountEffective),
-                  Amounts.parseOrThrow(transaction.amountRaw),
-                ).amount
-              )}</td>
-            </tr>
-            <tr>
-              <td>When</td>
-              <td>{transaction.timestamp.t_ms === "never" ? "never" : 
format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</td>
-            </tr>
-          </table>
+          </span>
+          <h3>Deposit <Pending /></h3>
+          <h1>{transaction.amountEffective} <Fee value={fee} /></h1>
         </section>
         <Footer />
       </div>
@@ -203,27 +160,19 @@ export function TransactionView({ transaction, onDelete, 
onBack }: WalletTransac
   }
 
   if (transaction.type === TransactionType.Refresh) {
+    const fee = Amounts.sub(
+      Amounts.parseOrThrow(transaction.amountRaw),
+      Amounts.parseOrThrow(transaction.amountEffective),
+    ).amount
     return (
-      <div style={{ display: 'flex', flexDirection: 'column', flex: 1, 
minHeight: '20rem' }} >
-        <section>
-          <h1>Refresh <Pending /></h1>
-          <p>
+      <div style={{ display: 'flex', flexDirection: 'column', width: '100%' }} 
>
+        <section style={{ color: transaction.pending ? 'gray' : '', flex: '1 0 
auto', height: 'calc(20rem - 34px - 45px)', overflow: 'auto' }}>
+          <span style="flat: left; font-size:small; 
color:gray">{transaction.timestamp.t_ms === "never" ? "never" : 
format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</span>
+          <span style="float: right; font-size:small; color:gray">
             From <b>{transaction.exchangeBaseUrl}</b>
-          </p>
-          <table class={transaction.pending ? "detailsTable pending" : 
"detailsTable"}>
-            <tr>
-              <td>Amount refreshed</td>
-              <td>{transaction.amountRaw}</td>
-            </tr>
-            <tr>
-              <td>Fees</td>
-              <td>{transaction.amountEffective}</td>
-            </tr>
-            <tr>
-              <td>When</td>
-              <td>{transaction.timestamp.t_ms === "never" ? "never" : 
format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</td>
-            </tr>
-          </table>
+          </span>
+          <h3>Refresh <Pending /></h3>
+          <h1>{transaction.amountEffective} <Fee value={fee} /></h1>
         </section>
         <Footer />
       </div>
@@ -231,91 +180,58 @@ export function TransactionView({ transaction, onDelete, 
onBack }: WalletTransac
   }
 
   if (transaction.type === TransactionType.Tip) {
+    const fee = Amounts.sub(
+      Amounts.parseOrThrow(transaction.amountRaw),
+      Amounts.parseOrThrow(transaction.amountEffective),
+    ).amount
     return (
-      <div style={{ display: 'flex', flexDirection: 'column', flex: 1, 
minHeight: '20rem' }} >
-        <section>
-          <h1>Tip <Pending /></h1>
-          <p>
+      <div style={{ display: 'flex', flexDirection: 'column', width: '100%' }} 
>
+        <section style={{ color: transaction.pending ? 'gray' : '', flex: '1 0 
auto', height: 'calc(20rem - 34px - 45px)', overflow: 'auto' }}>
+          <span style="flat: left; font-size:small; 
color:gray">{transaction.timestamp.t_ms === "never" ? "never" : 
format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</span>
+          <span style="float: right; font-size:small; color:gray">
             From <b>{transaction.merchantBaseUrl}</b>
-          </p>
-          <table class={transaction.pending ? "detailsTable pending" : 
"detailsTable"}>
-            <tr>
-              <td>Amount deduce</td>
-              <td>{transaction.amountRaw}</td>
-            </tr>
-            <tr>
-              <td>Amount received</td>
-              <td>{transaction.amountEffective}</td>
-            </tr>
-            <tr>
-              <td>Exchange fee</td>
-              <td>{Amounts.stringify(
-                Amounts.sub(
-                  Amounts.parseOrThrow(transaction.amountRaw),
-                  Amounts.parseOrThrow(transaction.amountEffective),
-                ).amount
-              )}</td>
-            </tr>
-            <tr>
-              <td>When</td>
-              <td>{transaction.timestamp.t_ms === "never" ? "never" : 
format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</td>
-            </tr>
-          </table>
+          </span>
+          <h3>Tip <Pending /></h3>
+          <h1>{transaction.amountEffective} <Fee value={fee} /></h1>
         </section>
         <Footer />
       </div>
     );
   }
 
-  const TRANSACTION_FROM_REFUND = /[a-z]*:([\w]{10}).*/
   if (transaction.type === TransactionType.Refund) {
+    const fee = Amounts.sub(
+      Amounts.parseOrThrow(transaction.amountRaw),
+      Amounts.parseOrThrow(transaction.amountEffective),
+    ).amount
     return (
-      <div style={{ display: 'flex', flexDirection: 'column', flex: 1, 
minHeight: '20rem' }} >
-        <section>
-          <h1>Refund 
({TRANSACTION_FROM_REFUND.exec(transaction.refundedTransactionId)![1]}...) 
<Pending /></h1>
-          <p>
+      <div style={{ display: 'flex', flexDirection: 'column', width: '100%' }} 
>
+        <section style={{ color: transaction.pending ? 'gray' : '', flex: '1 0 
auto', height: 'calc(20rem - 34px - 45px)', overflow: 'auto' }}>
+          <span style="flat: left; font-size:small; 
color:gray">{transaction.timestamp.t_ms === "never" ? "never" : 
format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</span>
+          <span style="float: right; font-size:small; color:gray">
             From <b>{transaction.info.merchant.name}</b>
+          </span>
+          <h3>Refund <Pending /></h3>
+          <h1>{transaction.amountEffective} <Fee value={fee} /></h1>
+          <span style="font-size:small; 
color:gray">#{transaction.info.orderId}</span>
+          <p>
+            {transaction.info.summary}
           </p>
-          <table class={transaction.pending ? "detailsTable pending" : 
"detailsTable"}>
-            <tr>
-              <td>Order id</td>
-              <td>{transaction.info.orderId}</td>
-            </tr>
-            <tr>
-              <td>Summary</td>
-              <td>{transaction.info.summary}</td>
-            </tr>
-            {transaction.info.products && transaction.info.products.length > 0 
&&
-              <tr>
-                <td>Products</td>
-                <td><ol>
-                  {transaction.info.products.map(p =>
-                    <li>{p.description}</li>
-                  )}</ol></td>
-              </tr>
+          <div>
+            {transaction.info.products && transaction.info.products.length > 0 
&& <div>
+              {transaction.info.products.map(p => <div style="display: flex; 
flex-direction: row; border: 1px solid gray; border-radius: 0.5em; margin: 
0.5em 0px; justify-content: left; padding: 0.5em;">
+                <a href="#" onClick={showLargePic}>
+                  <img class="pure-img" style="display:inline-block" 
src={p.image ? p.image : emptyImg} width="32" height="32" />
+                </a>
+                <div style="display: block; margin-left: 1em;">
+                  {p.quantity && p.quantity > 0 && <div style="font-size: 
small; color: gray;">x {p.quantity} {p.unit}</div>}
+                  <div style={{ textOverflow: 'ellipsis', overflow: 'hidden', 
width: 'calc(20rem - 32px - 32px - 8px - 1em)', whiteSpace: 'nowrap' 
}}>{p.description}</div>
+                </div>
+              </div>)}
+            </div>
             }
-            <tr>
-              <td>Amount deduce</td>
-              <td>{transaction.amountRaw}</td>
-            </tr>
-            <tr>
-              <td>Amount received</td>
-              <td>{transaction.amountEffective}</td>
-            </tr>
-            <tr>
-              <td>Exchange fee</td>
-              <td>{Amounts.stringify(
-                Amounts.sub(
-                  Amounts.parseOrThrow(transaction.amountRaw),
-                  Amounts.parseOrThrow(transaction.amountEffective),
-                ).amount
-              )}</td>
-            </tr>
-            <tr>
-              <td>When</td>
-              <td>{transaction.timestamp.t_ms === "never" ? "never" : 
format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</td>
-            </tr>
-          </table>
+          </div>
+
         </section>
         <Footer />
       </div>
diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx 
b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx
index 6cc781aa..543b6872 100644
--- a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx
+++ b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx
@@ -88,7 +88,7 @@ function Application() {
   return (
     <div>
       <Match>{({ path }: any) => <WalletNavBar current={path} />}</Match >
-      <div style={{ margin: "1em", width: 400 }}>
+      <div style={{ margin: "1em", width: 400, height: 'calc(20rem - 34px)' }}>
         <Router>
           <Route path={Pages.balance} component={BalancePage} />
           <Route path={Pages.settings} component={SettingsPage} />
diff --git a/packages/taler-wallet-webextension/static/img/empty.png 
b/packages/taler-wallet-webextension/static/img/empty.png
new file mode 100644
index 00000000..5120d313
Binary files /dev/null and 
b/packages/taler-wallet-webextension/static/img/empty.png differ
diff --git a/packages/taler-wallet-webextension/static/style/popup.css 
b/packages/taler-wallet-webextension/static/style/popup.css
index a234f2a2..50440047 100644
--- a/packages/taler-wallet-webextension/static/style/popup.css
+++ b/packages/taler-wallet-webextension/static/style/popup.css
@@ -5,11 +5,11 @@
  */
 
 body {
-  min-height: 20em;
+  /* min-height: 20em; */
   /* width: 30em; */
   margin: 0;
   padding: 0;
-  max-height: 800px;
+  /* max-height: 800px; */
   overflow: hidden;
   background-color: #f8faf7;
   font-family: Arial, Helvetica, sans-serif;
@@ -259,4 +259,33 @@ table.detailsTable {
 
 table.detailsTable.pending {
   color: gray;
+}
+
+.overflow {
+  text-align: justify;
+  position: relative;
+  max-height: calc(1.2em * 2);
+  overflow: hidden;
+  padding-right: 1rem; /* space for ellipsis */
+}
+.overflow::before {
+  position: absolute;
+  content: "...";
+/*   inset-block-end: 0;
+  inset-inline-end: 0; */
+  bottom: 0;
+  right: 0;
+}
+.overflow::after {
+  content: "";
+  position: absolute;
+/*   inset-inline-end: 0; */
+  right: 0;
+  width: 1rem;
+  height: 1rem;
+  background: white;
+}
+
+button.danger {
+  background-color: 'red';
 }
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 14219b2f..ca93291b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -211,6 +211,7 @@ importers:
       '@gnu-taler/taler-util': workspace:*
       '@gnu-taler/taler-wallet-core': workspace:*
       '@rollup/plugin-commonjs': ^17.0.0
+      '@rollup/plugin-image': ^2.0.6
       '@rollup/plugin-json': ^4.1.0
       '@rollup/plugin-node-resolve': ^11.1.0
       '@rollup/plugin-replace': ^2.3.4
@@ -253,6 +254,7 @@ importers:
       '@babel/plugin-transform-react-jsx-source': 7.12.13_@babel+core@7.14.0
       '@babel/preset-typescript': 7.13.0_@babel+core@7.14.0
       '@rollup/plugin-commonjs': 17.0.0_rollup@2.37.1
+      '@rollup/plugin-image': 2.0.6_rollup@2.37.1
       '@rollup/plugin-json': 4.1.0_rollup@2.37.1
       '@rollup/plugin-node-resolve': 11.1.0_rollup@2.37.1
       '@rollup/plugin-replace': 2.3.4_rollup@2.37.1
@@ -3280,6 +3282,17 @@ packages:
       rollup: 2.37.1
     dev: true
 
+  /@rollup/plugin-image/2.0.6_rollup@2.37.1:
+    resolution: {integrity: 
sha512-bB+spXogbPiFjhBS7i8ajUOgOnVwWK3bnJ6VroxKey/q8/EPRkoSh+4O1qPCw97qMIDspF4TlzXVBhZ7nojIPw==}
+    engines: {node: '>= 8.0.0'}
+    peerDependencies:
+      rollup: ^1.20.0 || ^2.0.0
+    dependencies:
+      '@rollup/pluginutils': 3.1.0_rollup@2.37.1
+      mini-svg-data-uri: 1.3.3
+      rollup: 2.37.1
+    dev: true
+
   /@rollup/plugin-json/4.1.0_rollup@2.37.1:
     resolution: {integrity: 
sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==}
     peerDependencies:
@@ -12933,6 +12946,11 @@ packages:
       webpack-sources: 1.4.3
     dev: true
 
+  /mini-svg-data-uri/1.3.3:
+    resolution: {integrity: 
sha512-+fA2oRcR1dJI/7ITmeQJDrYWks0wodlOz0pAEhKYJ2IVc1z0AnwJUsKY2fzFmPAM3Jo9J0rBx8JAA9QQSJ5PuA==}
+    hasBin: true
+    dev: true
+
   /minimalistic-assert/1.0.1:
     resolution: {integrity: 
sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
     dev: true

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]