aboutsummaryrefslogtreecommitdiff
path: root/src/webex
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2019-08-30 17:27:59 +0200
committerFlorian Dold <florian.dold@gmail.com>2019-08-30 17:27:59 +0200
commit5ec344290efd937fa82c0704bc7c204a0bf14c78 (patch)
tree7d9594180bbc7b5fa2b4a8dbe24272e7a82301f3 /src/webex
parentdefbf625bdef0f8a666b72b8ce99de5e01af6b91 (diff)
downloadwallet-core-5ec344290efd937fa82c0704bc7c204a0bf14c78.tar.xz
support for tipping protocol changes
Diffstat (limited to 'src/webex')
-rw-r--r--src/webex/messages.ts6
-rw-r--r--src/webex/pages/tip.tsx203
-rw-r--r--src/webex/wxApi.ts11
-rw-r--r--src/webex/wxBackend.ts11
4 files changed, 94 insertions, 137 deletions
diff --git a/src/webex/messages.ts b/src/webex/messages.ts
index ca0e1c7e1..f1046d5c7 100644
--- a/src/webex/messages.ts
+++ b/src/webex/messages.ts
@@ -174,11 +174,11 @@ export interface MessageMap {
response: AmountJson;
};
"accept-tip": {
- request: { tipToken: talerTypes.TipToken };
- response: walletTypes.TipStatus;
+ request: { talerTipUri: string };
+ response: void;
};
"get-tip-status": {
- request: { tipToken: talerTypes.TipToken };
+ request: { talerTipUri: string };
response: walletTypes.TipStatus;
};
"clear-notification": {
diff --git a/src/webex/pages/tip.tsx b/src/webex/pages/tip.tsx
index c13120c43..a3f5c38c3 100644
--- a/src/webex/pages/tip.tsx
+++ b/src/webex/pages/tip.tsx
@@ -14,7 +14,6 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-
/**
* Page shown to the user to confirm creation
* of a reserve, usually requested by the bank.
@@ -28,152 +27,114 @@ import URI = require("urijs");
import * as i18n from "../../i18n";
-import {
- acceptTip,
- getReserveCreationInfo,
- getTipStatus,
-} from "../wxApi";
+import { acceptTip, getReserveCreationInfo, getTipStatus } from "../wxApi";
-import {
- WithdrawDetailView,
- renderAmount,
-} from "../renderHtml";
+import { WithdrawDetailView, renderAmount } from "../renderHtml";
import * as Amounts from "../../amounts";
-import { TipToken } from "../../talerTypes";
-import { ReserveCreationInfo, TipStatus } from "../../walletTypes";
+import { useState, useEffect } from "react";
+import { TipStatus } from "../../walletTypes";
-interface TipDisplayProps {
- tipToken: TipToken;
+interface LoadingButtonProps {
+ loading: boolean;
}
-interface TipDisplayState {
- tipStatus?: TipStatus;
- rci?: ReserveCreationInfo;
- working: boolean;
- discarded: boolean;
+function LoadingButton(
+ props:
+ & React.PropsWithChildren<LoadingButtonProps>
+ & React.DetailedHTMLProps<
+ React.ButtonHTMLAttributes<HTMLButtonElement>,
+ HTMLButtonElement
+ >,
+) {
+ return (
+ <button
+ className="pure-button pure-button-primary"
+ type="button"
+ {...props}
+ >
+ {props.loading ? <span><object className="svg-icon svg-baseline" data="/img/spinner-bars.svg" /></span> : null}
+ {props.children}
+ </button>
+ );
}
-class TipDisplay extends React.Component<TipDisplayProps, TipDisplayState> {
- constructor(props: TipDisplayProps) {
- super(props);
- this.state = { working: false, discarded: false };
- }
-
- async update() {
- const tipStatus = await getTipStatus(this.props.tipToken);
- this.setState({ tipStatus });
- const rci = await getReserveCreationInfo(tipStatus.exchangeUrl, tipStatus.amount);
- this.setState({ rci });
- }
-
- componentDidMount() {
- this.update();
- const port = chrome.runtime.connect();
- port.onMessage.addListener((msg: any) => {
- if (msg.notify) {
- console.log("got notified");
- this.update();
- }
- });
- this.update();
- }
-
- renderExchangeInfo() {
- const rci = this.state.rci;
- if (!rci) {
- return <p>Waiting for info about exchange ...</p>;
- }
- const totalCost = Amounts.add(rci.overhead, rci.withdrawFee).amount;
- return (
- <div>
- <p>
- The tip is handled by the exchange <strong>{rci.exchangeInfo.baseUrl}</strong>.{" "}
- The exchange provider will charge
- {" "}
- <strong>{renderAmount(totalCost)}</strong>
- {" "}.
- </p>
- <WithdrawDetailView rci={rci} />
- </div>
- );
+function TipDisplay(props: { talerTipUri: string }) {
+ const [tipStatus, setTipStatus] = useState<TipStatus | undefined>(undefined);
+ const [discarded, setDiscarded] = useState(false);
+ const [loading, setLoading] = useState(false);
+ const [finished, setFinished] = useState(false);
+
+ useEffect(() => {
+ const doFetch = async () => {
+ const ts = await getTipStatus(props.talerTipUri);
+ setTipStatus(ts);
+ };
+ doFetch();
+ }, []);
+
+ if (discarded) {
+ return <span>You've discarded the tip.</span>;
}
- accept() {
- this.setState({ working: true});
- acceptTip(this.props.tipToken);
+ if (finished) {
+ return <span>Tip has been accepted!</span>;
}
- discard() {
- this.setState({ discarded: true });
+ if (!tipStatus) {
+ return <span>Loading ...</span>;
}
- render(): JSX.Element {
- const ts = this.state.tipStatus;
- if (!ts) {
- return <p>Processing ...</p>;
- }
-
- const renderAccepted = () => (
- <>
- <p>You've accepted this tip! <a href={ts.nextUrl}>Go back to merchant</a></p>
- {this.renderExchangeInfo()}
- </>
- );
-
- const renderButtons = () => (
- <>
+ const discard = () => {
+ setDiscarded(true);
+ };
+
+ const accept = async () => {
+ setLoading(true);
+ await acceptTip(props.talerTipUri);
+ setFinished(true);
+ };
+
+ return (
+ <div>
+ <h2>Tip Received!</h2>
+ <p>
+ You received a tip of <strong>{renderAmount(tipStatus.amount)}</strong>{" "}
+ from <span> </span>
+ <strong>{tipStatus.merchantOrigin}</strong>.
+ </p>
+ <p>
+ The tip is handled by the exchange{" "}
+ <strong>{tipStatus.exchangeUrl}</strong>. This exchange will charge fees
+ of <strong>{renderAmount(tipStatus.totalFees)}</strong> for this
+ operation.
+ </p>
<form className="pure-form">
- <button
- className="pure-button pure-button-primary"
- type="button"
- disabled={!(this.state.rci && this.state.tipStatus && this.state.tipStatus.tipRecord)}
- onClick={() => this.accept()}>
- { this.state.working
- ? <span><object className="svg-icon svg-baseline" data="/img/spinner-bars.svg" /> </span>
- : null }
- Accept tip
- </button>
+ <LoadingButton loading={loading} onClick={() => accept()}>
+ AcceptTip
+ </LoadingButton>
{" "}
- <button className="pure-button" type="button" onClick={() => this.discard()}>
+ <button className="pure-button" type="button" onClick={() => discard()}>
Discard tip
</button>
</form>
- { this.renderExchangeInfo() }
- </>
- );
-
- const renderDiscarded = () => (
- <p>You've discarded this tip. <a href={ts.nextUrl}>Go back to merchant.</a></p>
- );
-
- return (
- <div>
- <h2>Tip Received!</h2>
- <p>You received a tip of <strong>{renderAmount(ts.amount)}</strong> from <span> </span>
- <strong>{ts.merchantDomain}</strong>.</p>
- {
- this.state.discarded
- ? renderDiscarded()
- : ts.accepted
- ? renderAccepted()
- : renderButtons()
- }
- </div>
- );
- }
+ </div>
+ );
}
async function main() {
try {
const url = new URI(document.location.href);
const query: any = URI.parseQuery(url.query());
+ const talerTipUri = query.talerTipUri;
+ if (typeof talerTipUri !== "string") {
+ throw Error("talerTipUri must be a string");
+ }
- const tipToken = TipToken.checked(JSON.parse(query.tip_token));
-
- ReactDOM.render(<TipDisplay tipToken={tipToken} />,
- document.getElementById("container")!);
-
+ ReactDOM.render(
+ <TipDisplay talerTipUri={talerTipUri} />,
+ document.getElementById("container")!,
+ );
} catch (e) {
// TODO: provide more context information, maybe factor it out into a
// TODO:generic error reporting function or component.
diff --git a/src/webex/wxApi.ts b/src/webex/wxApi.ts
index feabc7819..fd01aed3f 100644
--- a/src/webex/wxApi.ts
+++ b/src/webex/wxApi.ts
@@ -45,7 +45,6 @@ import {
import {
MerchantRefundPermission,
- TipToken,
} from "../talerTypes";
import { MessageMap, MessageType } from "./messages";
@@ -349,15 +348,15 @@ export function getFullRefundFees(args: { refundPermissions: MerchantRefundPermi
/**
* Get the status of processing a tip.
*/
-export function getTipStatus(tipToken: TipToken): Promise<TipStatus> {
- return callBackend("get-tip-status", { tipToken });
+export function getTipStatus(talerTipUri: string): Promise<TipStatus> {
+ return callBackend("get-tip-status", { talerTipUri });
}
/**
* Mark a tip as accepted by the user.
*/
-export function acceptTip(tipToken: TipToken): Promise<TipStatus> {
- return callBackend("accept-tip", { tipToken });
+export function acceptTip(talerTipUri: string): Promise<void> {
+ return callBackend("accept-tip", { talerTipUri });
}
@@ -423,4 +422,4 @@ export function preparePay(talerPayUri: string) {
*/
export function acceptWithdrawal(talerWithdrawUri: string, selectedExchange: string) {
return callBackend("accept-withdrawal", { talerWithdrawUri, selectedExchange });
-} \ No newline at end of file
+}
diff --git a/src/webex/wxBackend.ts b/src/webex/wxBackend.ts
index d31ea388d..5bff4fe0a 100644
--- a/src/webex/wxBackend.ts
+++ b/src/webex/wxBackend.ts
@@ -50,7 +50,6 @@ import * as wxApi from "./wxApi";
import URI = require("urijs");
import Port = chrome.runtime.Port;
import MessageSender = chrome.runtime.MessageSender;
-import { TipToken } from "../talerTypes";
import { BrowserCryptoWorkerFactory } from "../crypto/cryptoApi";
const NeedsWallet = Symbol("NeedsWallet");
@@ -182,7 +181,7 @@ function handleMessage(
return Promise.resolve({ error: "bad url" });
}
const amount = AmountJson.checked(detail.amount);
- return needsWallet().getReserveCreationInfo(detail.baseUrl, amount);
+ return needsWallet().getWithdrawDetailsForAmount(detail.baseUrl, amount);
}
case "get-history": {
// TODO: limit history length
@@ -295,12 +294,10 @@ function handleMessage(
case "accept-refund":
return needsWallet().acceptRefund(detail.refundUrl);
case "get-tip-status": {
- const tipToken = TipToken.checked(detail.tipToken);
- return needsWallet().getTipStatus(tipToken);
+ return needsWallet().getTipStatus(detail.talerTipUri);
}
case "accept-tip": {
- const tipToken = TipToken.checked(detail.tipToken);
- return needsWallet().acceptTip(tipToken);
+ return needsWallet().acceptTip(detail.talerTipUri);
}
case "clear-notification": {
return needsWallet().clearNotification();
@@ -340,7 +337,7 @@ function handleMessage(
return needsWallet().benchmarkCrypto(detail.repetitions);
}
case "get-withdraw-details": {
- return needsWallet().getWithdrawDetails(
+ return needsWallet().getWithdrawDetailsForUri(
detail.talerWithdrawUri,
detail.maybeSelectedExchange,
);