aboutsummaryrefslogtreecommitdiff
path: root/src/webex
diff options
context:
space:
mode:
authorMarcos Gutierrez <gmarcos87@gmail.com>2020-01-27 10:11:39 -0300
committerMarcos Gutierrez <gmarcos87@gmail.com>2020-01-27 10:11:39 -0300
commitd6d56479469f0e7fc050976908c7abcacd61bc27 (patch)
tree3ab7136e544b41edf0b5d1a936d23206be38f695 /src/webex
parentb961ca2c39d813443581b8651e6b144ee8bec6e9 (diff)
downloadwallet-core-d6d56479469f0e7fc050976908c7abcacd61bc27.tar.xz
add new history view and some global style changes
Diffstat (limited to 'src/webex')
-rw-r--r--src/webex/pages/popup.css129
-rw-r--r--src/webex/pages/popup.tsx432
2 files changed, 458 insertions, 103 deletions
diff --git a/src/webex/pages/popup.css b/src/webex/pages/popup.css
index 675412c11..4cae66177 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 b26e86e65..f873414a1 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.merchantBaseUrl;
+ 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={historyItem.orderShortInfo.merchantBaseUrl}
+ 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.merchantBaseUrl;
+ const { merchant, item } = parseSummary(
+ historyItem.orderShortInfo.summary
+ );
+ const fulfillmentLinkElem = (
+ <Fragment>
+ <a
+ href={historyItem.orderShortInfo.merchantBaseUrl}
+ 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>;
}