gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated (b961ca2c -> 4fd80ee8)


From: gnunet
Subject: [taler-wallet-core] branch master updated (b961ca2c -> 4fd80ee8)
Date: Fri, 31 Jan 2020 15:51:11 +0100

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

marcos-gutierrez pushed a change to branch master
in repository wallet-core.

    from b961ca2c bump web extension manifest version
     new d6d56479 add new history view and some global style changes
     new 5f68f060 add fulfillmentUrl to OrderShortInfo
     new 4fd80ee8 fix fulfillmentUrl in the history rener

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/operations/history.ts |   1 +
 src/types/history.ts      |   5 +
 src/webex/pages/popup.css | 129 ++++++++++++--
 src/webex/pages/popup.tsx | 432 ++++++++++++++++++++++++++++++++++++----------
 4 files changed, 464 insertions(+), 103 deletions(-)

diff --git a/src/operations/history.ts b/src/operations/history.ts
index b2b78fe1..2fb7854d 100644
--- a/src/operations/history.ts
+++ b/src/operations/history.ts
@@ -60,6 +60,7 @@ function getOrderShortInfo(
   }
   return {
     amount: Amounts.toString(download.contractData.amount),
+    fulfillmentUrl: download.contractData.fulfillmentUrl,
     orderId: download.contractData.orderId,
     merchantBaseUrl: download.contractData.merchantBaseUrl,
     proposalId: proposal.proposalId,
diff --git a/src/types/history.ts b/src/types/history.ts
index 52148cb0..30fe8e52 100644
--- a/src/types/history.ts
+++ b/src/types/history.ts
@@ -392,6 +392,11 @@ export interface OrderShortInfo {
    * Summary of the proposal, given by the merchant.
    */
   summary: string;
+
+  /**
+   * URL of the fulfillment, given by the merchant.
+   */
+  fulfillmentUrl: string;
 }
 
 /**
diff --git a/src/webex/pages/popup.css b/src/webex/pages/popup.css
index 675412c1..4cae6617 100644
--- a/src/webex/pages/popup.css
+++ b/src/webex/pages/popup.css
@@ -12,21 +12,24 @@ body {
     padding: 0;
     max-height: 800px;
     overflow: hidden;
+    background-color: #f8faf7;
+    font-family: Arial, Helvetica, sans-serif;
 }
 
 .nav {
-    background-color: #ddd;
+    background-color: #033;
     padding: 0.5em 0;
 }
 
 .nav a {
-    color: black;
-    padding: 0.5em;
+    color: #f8faf7;
+    padding: 0.7em 1.4em;
     text-decoration: none;
 }
 
 .nav a.active {
-    background-color: white;
+    background-color: #f8faf7;
+    color: #000;
     font-weight: bold;
 }
 
@@ -71,14 +74,114 @@ body {
 }
 
 .historyItem {
-    border: 1px solid black;
-    border-radius: 10px;
-    padding-left: 0.5em;
-    margin: 0.5em;
-}
-
-.historyDate {
-    font-size: 90%;
+    min-width: 380px;
+    display: flex;
+    flex-direction: row;
+    border-bottom: 1px solid #d9dbd8;
+    padding: 0.5em;
+    align-items: center;
+  }
+  
+  .historyItem .amount {
+    font-size: 110%;
+    font-weight: bold;
+    text-align: right;
+  }
+  
+  .historyDate,
+  .historyTitle,
+  .historyText,
+  .historySmall {
     margin: 0.3em;
+  }
+  
+  .historyDate {
+    font-size: 90%;
     color: slategray;
-}
+    text-align: right;
+  }
+  
+  .historyLeft {
+    display: flex;
+    flex-direction: column;
+    text-align: right;
+  }
+  
+  .historyContent {
+    flex: 1;
+  }
+  
+  .historyTitle {
+    font-weight: 400;
+  }
+  
+  .historyText {
+    font-size: 90%;
+  }
+  
+  .historySmall {
+    font-size: 70%;
+    text-transform: uppercase;
+  }
+  
+  .historyAmount {
+    flex-grow: 1;
+  }
+  
+  .historyAmount .primary {
+    font-size: 100%;
+  }
+  
+  .historyAmount .secondary {
+    font-size: 80%;
+  }
+  
+  .historyAmount .positive {
+    color: #088;
+  }
+  
+  .historyAmount .positive:before {
+    content: "+";
+  }
+  
+  .historyAmount .negative {
+    color: #800
+  }
+  
+  .historyAmount .negative:before {
+    color: #800;
+    content: "-";
+  }
+  .icon {
+    margin: 0 10px;
+    text-align: center;
+    width: 35px;
+    font-size: 20px;
+    border-radius: 50%;
+    background: #ccc;
+    color: #fff;
+    padding-top: 4px;
+    height: 30px;
+  }
+  
+  .option {
+      text-transform: uppercase;
+      text-align: right;
+      padding: 0.4em;
+      font-size: 0.9em;
+  }
+  
+  input[type=checkbox], input[type=radio] {
+    vertical-align: middle;
+    position: relative;
+    bottom: 1px;
+  }
+  
+  input[type=radio] { 
+    bottom: 2px; 
+  } 
+
+  .balance {
+      text-align: center;
+      padding-top: 2em;
+  }
\ No newline at end of file
diff --git a/src/webex/pages/popup.tsx b/src/webex/pages/popup.tsx
index b26e86e6..44f45f9d 100644
--- a/src/webex/pages/popup.tsx
+++ b/src/webex/pages/popup.tsx
@@ -42,9 +42,12 @@ import {
 } from "../renderHtml";
 import * as wxApi from "../wxApi";
 
-import * as React from "react";
+import React, { Fragment } from "react";
 import { HistoryEvent } from "../../types/history";
 
+import moment from "moment";
+import { Timestamp } from "../../util/time";
+
 function onUpdateNotification(f: () => void): () => void {
   const port = chrome.runtime.connect({ name: "notifications" });
   const listener = () => {
@@ -185,7 +188,7 @@ function bigAmount(amount: AmountJson): JSX.Element {
   const v = amount.value + amount.fraction / Amounts.fractionalBase;
   return (
     <span>
-      <span style={{ fontSize: "300%" }}>{v}</span>{" "}
+      <span style={{ fontSize: "5em", display: 'block'}}>{v}</span>{" "}
       <span>{amount.currency}</span>
     </span>
   );
@@ -193,12 +196,10 @@ function bigAmount(amount: AmountJson): JSX.Element {
 
 function EmptyBalanceView() {
   return (
-    <div>
-      <i18n.Translate wrap="p">
-        You have no balance to show. Need some{" "}
-        <PageLink pageName="welcome.html">help</PageLink> getting started?
-      </i18n.Translate>
-    </div>
+    <i18n.Translate wrap="p">
+      You have no balance to show. Need some{" "}
+      <PageLink pageName="welcome.html">help</PageLink> getting started?
+    </i18n.Translate>
   );
 }
 
@@ -299,7 +300,7 @@ class WalletBalanceView extends React.Component<any, any> {
     const wallet = this.balance;
     if (this.gotError) {
       return (
-        <div>
+        <div className='balance'>
           <p>{i18n.str`Error: could not retrieve balance information.`}</p>
           <p>
             Click <PageLink pageName="welcome.html">here</PageLink> for help 
and
@@ -320,114 +321,354 @@ class WalletBalanceView extends React.Component<any, 
any> {
         </p>
       );
     });
-    return <div>{listing.length > 0 ? listing : <EmptyBalanceView />}</div>;
+    return listing.length > 0 ? <div className='balance'>{listing}</div> : 
<EmptyBalanceView />;
   }
 }
 
+function Icon({ l }: { l: string }) {
+  return <div className={"icon"}>{l}</div>;
+}
+
+function formatAndCapitalize(text: string) {
+  text = text.replace("-", " ");
+  text = text.replace(/^./, text[0].toUpperCase());
+  return text;
+}
+
+type HistoryItemProps = {
+  title?: string | JSX.Element;
+  text?: string | JSX.Element;
+  small?: string | JSX.Element;
+  amount?: string | AmountJson;
+  fees?: string | AmountJson;
+  invalid?: string | AmountJson;
+  icon?: string;
+  timestamp: Timestamp;
+  negative?: boolean;
+};
+
+function HistoryItem({
+  title,
+  text,
+  small,
+  amount,
+  fees,
+  invalid,
+  timestamp,
+  icon,
+  negative = false
+}: HistoryItemProps) {
+  function formatDate(timestamp: number | "never") {
+    if (timestamp !== "never") {
+      const itemDate = moment(timestamp);
+      if (itemDate.isBetween(moment().subtract(2, "days"), moment())) {
+        return itemDate.fromNow();
+      }
+      return itemDate.format("lll");
+    }
+    return null;
+  }
+
+  let invalidElement, amountElement, feesElement;
+
+  if (amount) {
+    amountElement = renderAmount(amount);
+  }
+
+  if (fees) {
+    fees = typeof fees === "string" ? Amounts.parse(fees) : fees;
+    if (fees && Amounts.isNonZero(fees)) {
+      feesElement = renderAmount(fees);
+    }
+  }
+
+  if (invalid) {
+    invalid = typeof invalid === "string" ? Amounts.parse(invalid) : invalid;
+    if (invalid && Amounts.isNonZero(invalid)) {
+      invalidElement = renderAmount(invalid);
+    }
+  }
+
+  return (
+    <div className="historyItem">
+      {icon ? <Icon l={icon} /> : null}
+      <div className="historyContent">
+        {title ? <div className={"historyTitle"}>{title}</div> : null}
+        {text ? <div className={"historyText"}>{text}</div> : null}
+        {small ? <div className={"historySmall"}>{small}</div> : null}
+      </div>
+      <div className={"historyLeft"}>
+        <div className={"historyAmount"}>
+          {amountElement ? (
+            <div className={`${negative ? "negative" : "positive"}`}>
+              {amountElement}
+            </div>
+          ) : null}
+          {invalidElement ? (
+            <div className={"secondary"}>
+              {i18n.str`Invalid `}{" "}
+              <span className={"negative"}>{invalidElement}</span>
+            </div>
+          ) : null}
+          {feesElement ? (
+            <div className={"secondary"}>
+              {i18n.str`Fees `}{" "}
+              <span className={"negative"}>{feesElement}</span>
+            </div>
+          ) : null}
+        </div>
+        <div className="historyDate">{formatDate(timestamp.t_ms)}</div>
+      </div>
+    </div>
+  );
+}
+
+function amountDiff(
+  total: string | Amounts.AmountJson,
+  partial: string | Amounts.AmountJson
+): Amounts.AmountJson | string {
+  let a = typeof total === "string" ? Amounts.parse(total) : total;
+  let b = typeof partial === "string" ? Amounts.parse(partial) : partial;
+  if (a && b) {
+    return Amounts.sub(a, b).amount;
+  } else {
+    return total;
+  }
+}
+
+
+function parseSummary(summary: string) {
+  let parsed = summary.split(/: (.+)/);
+  return {
+    merchant: parsed[0],
+    item: parsed[1]
+  };
+}
+
 function formatHistoryItem(historyItem: HistoryEvent) {
-  const d = historyItem;
-  console.log("hist item", historyItem);
   switch (historyItem.type) {
-    /*
-    case "create-reserve":
+    case "refreshed": {
       return (
-        <i18n.Translate wrap="p">
-          Bank requested reserve (<span>{abbrev(d.reservePub)}</span>) for{" "}
-          <span>{renderAmount(d.requestedAmount)}</span>.
-        </i18n.Translate>
+        <HistoryItem
+          timestamp={historyItem.timestamp}
+          small={i18n.str`Refresh sessions has completed`}
+          fees={amountDiff(
+            historyItem.amountRefreshedRaw,
+            historyItem.amountRefreshedEffective
+          )}
+        />
+      );
+    }
+
+    case "order-refused": {
+      const { merchant, item } = parseSummary(
+        historyItem.orderShortInfo.summary
       );
-    case "confirm-reserve": {
-      const exchange = new URL(d.exchangeBaseUrl).host;
-      const pub = abbrev(d.reservePub);
       return (
-        <i18n.Translate wrap="p">
-          Started to withdraw
-          <span>{renderAmount(d.requestedAmount)}</span>
-          from <span>{exchange}</span> (<span>{pub}</span>).
-        </i18n.Translate>
+        <HistoryItem
+          icon={"X"}
+          timestamp={historyItem.timestamp}
+          small={i18n.str`Order Refused`}
+          title={merchant}
+          text={abbrev(item, 30)}
+        />
       );
     }
-    case "offer-contract": {
+
+    case "order-redirected": {
+      const { merchant, item } = parseSummary(
+        historyItem.newOrderShortInfo.summary
+      );
       return (
-        <i18n.Translate wrap="p">
-          Merchant <em>{abbrev(d.merchantName, 15)}</em> offered contract{" "}
-          <span>{abbrev(d.contractTermsHash)}</span>.
-        </i18n.Translate>
+        <HistoryItem
+          icon={"⟲"}
+          small={i18n.str`Order redirected`}
+          text={abbrev(item, 40)}
+          timestamp={historyItem.timestamp}
+          title={merchant}
+        />
       );
     }
-    case "depleted-reserve": {
-      const exchange = d.exchangeBaseUrl
-        ? new URL(d.exchangeBaseUrl).host
-        : "??";
-      const amount = renderAmount(d.requestedAmount);
-      const pub = abbrev(d.reservePub);
+
+    case "payment-aborted": {
+      const { merchant, item } = parseSummary(
+        historyItem.orderShortInfo.summary
+      );
       return (
-        <i18n.Translate wrap="p">
-          Withdrew <span>{amount}</span> from <span>{exchange}</span> (
-          <span>{pub}</span>).
-        </i18n.Translate>
+        <HistoryItem
+          amount={historyItem.orderShortInfo.amount}
+          fees={historyItem.amountLost}
+          icon={"P"}
+          small={i18n.str`Payment aborted`}
+          text={abbrev(item, 40)}
+          timestamp={historyItem.timestamp}
+          title={merchant}
+        />
       );
     }
-    case "pay": {
-      const url = d.fulfillmentUrl;
-      const merchantElem = <em>{abbrev(d.merchantName, 15)}</em>;
+
+    case "payment-sent": {
+      const url = historyItem.orderShortInfo.fulfillmentUrl;
+      const { merchant, item } = parseSummary(
+        historyItem.orderShortInfo.summary
+      );
+      const fees = amountDiff(
+        historyItem.amountPaidWithFees,
+        historyItem.orderShortInfo.amount
+      );
       const fulfillmentLinkElem = (
-        <a href={url} onClick={openTab(url)}>
-          view product
-        </a>
+        <Fragment>
+          <a
+            href={url}
+            onClick={openTab(url)}
+          >
+            {item ? abbrev(item, 30) : null}
+          </a>
+        </Fragment>
       );
       return (
-        <i18n.Translate wrap="p">
-          Paid <span>{renderAmount(d.amount)}</span> to merchant{" "}
-          <span>{merchantElem}</span>.<span> </span>(
-          <span>{fulfillmentLinkElem}</span>)
-        </i18n.Translate>
+        <HistoryItem
+          amount={historyItem.orderShortInfo.amount}
+          fees={fees}
+          icon={"P"}
+          negative={true}
+          small={i18n.str`Payment Sent`}
+          text={fulfillmentLinkElem}
+          timestamp={historyItem.timestamp}
+          title={merchant}
+        />
+      );
+    }
+    case "order-accepted": {
+      const url = historyItem.orderShortInfo.fulfillmentUrl;
+      const { merchant, item } = parseSummary(
+        historyItem.orderShortInfo.summary
+      );
+      const fulfillmentLinkElem = (
+        <Fragment>
+          <a
+            href={url}
+            onClick={openTab(url)}
+          >
+            {item ? abbrev(item, 40) : null}
+          </a>
+        </Fragment>
+      );
+      return (
+        <HistoryItem
+          negative={true}
+          amount={historyItem.orderShortInfo.amount}
+          icon={"P"}
+          small={i18n.str`Order accepted`}
+          text={fulfillmentLinkElem}
+          timestamp={historyItem.timestamp}
+          title={merchant}
+        />
+      );
+    }
+    case "reserve-balance-updated": {
+      return (
+        <HistoryItem
+          timestamp={historyItem.timestamp}
+          small={i18n.str`Reserve balance updated`}
+          fees={amountDiff(
+            historyItem.amountExpected,
+            historyItem.amountReserveBalance
+          )}
+        />
       );
     }
     case "refund": {
-      const merchantElem = <em>{abbrev(d.merchantName, 15)}</em>;
+      const merchantElem = (
+        <em>{abbrev(historyItem.orderShortInfo.summary, 25)}</em>
+      );
       return (
-        <i18n.Translate wrap="p">
-          Merchant <span>{merchantElem}</span> gave a refund over{" "}
-          <span>{renderAmount(d.refundAmount)}</span>.
-        </i18n.Translate>
+        <HistoryItem
+          icon={"R"}
+          timestamp={historyItem.timestamp}
+          small={i18n.str`Payment refund`}
+          text={merchantElem}
+          amount={historyItem.amountRefundedRaw}
+          invalid={historyItem.amountRefundedInvalid}
+          fees={amountDiff(
+            amountDiff(
+              historyItem.amountRefundedRaw,
+              historyItem.amountRefundedInvalid
+            ),
+            historyItem.amountRefundedEffective
+          )}
+        />
       );
     }
-    case "tip": {
-      const tipPageUrl = new 
URL(chrome.extension.getURL("/src/webex/pages/tip.html"));
-      tipPageUrl.searchParams.set("tip_id", d.tipId);
-      tipPageUrl.searchParams.set("merchant_domain", d.merchantDomain);
-      const url = tipPageUrl.href;
-      const tipLink = <a href={url} onClick={openTab(url)}>{i18n.str`tip`}</a>;
-      // i18n: Tip
+    case "withdrawn": {
+      const exchange = new URL(historyItem.exchangeBaseUrl).host;
+      const fees = amountDiff(
+        historyItem.amountWithdrawnRaw,
+        historyItem.amountWithdrawnEffective
+      );
+      return (
+        <HistoryItem
+          amount={historyItem.amountWithdrawnRaw}
+          fees={fees}
+          icon={"w"}
+          small={i18n.str`Withdrawn`}
+          title={exchange}
+          timestamp={historyItem.timestamp}
+        />
+      );
+    }
+    case "tip-accepted": {
+      return (
+        <HistoryItem
+          icon={"T"}
+          negative={true}
+          timestamp={historyItem.timestamp}
+          title={<i18n.Translate wrap={Fragment}>Tip Accepted</i18n.Translate>}
+          amount={historyItem.tipAmountRaw}
+        />
+      );
+    }
+    case "tip-declined": {
       return (
-        <>
-          <i18n.Translate wrap="p">
-            Merchant <span>{d.merchantDomain}</span> gave a{" "}
-            <span>{tipLink}</span> of <span>{renderAmount(d.amount)}</span>.
-          </i18n.Translate>
-          <span>
-            {" "}
-            {d.accepted ? null : (
-              <i18n.Translate>You did not accept the tip yet.</i18n.Translate>
-            )}
-          </span>
-        </>
+        <HistoryItem
+          icon={"T"}
+          timestamp={historyItem.timestamp}
+          title={<i18n.Translate wrap={Fragment}>Tip Declined</i18n.Translate>}
+          amount={historyItem.tipAmountRaw}
+        />
       );
     }
-    */
     default:
-      return <p>{i18n.str`Unknown event (${historyItem.type})`}</p>;
+      return (
+        <HistoryItem
+          timestamp={historyItem.timestamp}
+          small={i18n.str`${formatAndCapitalize(historyItem.type)}`}
+        />
+      );
   }
 }
 
+const HistoryComponent = (props: any) => {
+  const record = props.record;
+  return formatHistoryItem(record);
+};
+
 class WalletHistory extends React.Component<any, any> {
   private myHistory: any[];
   private gotError = false;
   private unmounted = false;
-
+  private hidenTypes: string[] = [
+    "order-accepted",
+    "order-redirected",
+    "refreshed",
+    "reserve-balance-updated",
+    "exchange-updated",
+    "exchange-added"
+  ];
+ 
   componentWillMount() {
     this.update();
+    this.setState({ filter: true });
     onUpdateNotification(() => this.update());
   }
 
@@ -468,21 +709,32 @@ class WalletHistory extends React.Component<any, any> {
     }
 
     const listing: any[] = [];
-    for (const record of history.reverse()) {
-      const item = (
-        <div className="historyItem">
-          <div className="historyDate">
-            {new Date(record.timestamp.t_ms).toString()}
-          </div>
-          {formatHistoryItem(record)}
-        </div>
-      );
-
+    const messages = history
+      .reverse()
+      .filter(hEvent => {
+        if (!this.state.filter) return true;
+        return this.hidenTypes.indexOf(hEvent.type) === -1;
+      });
+
+    for (const record of messages) {
+      const item = (<HistoryComponent key={record.eventId} record={record} />);
       listing.push(item);
     }
 
     if (listing.length > 0) {
-      return <div className="container">{listing}</div>;
+      return (
+        <div>
+          <div className="container">{listing}</div>
+          <div className="option">
+            Filtered list{" "}
+            <input
+              type="checkbox"
+              checked={this.state.filter}
+              onChange={() => this.setState({ filter: !this.state.filter })}
+            />
+          </div>
+        </div>
+      );
     }
     return <p>{i18n.str`Your wallet has no events recorded.`}</p>;
   }

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

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