summaryrefslogtreecommitdiff
path: root/src/webex/pages
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/pages
parentdefbf625bdef0f8a666b72b8ce99de5e01af6b91 (diff)
downloadwallet-core-5ec344290efd937fa82c0704bc7c204a0bf14c78.tar.gz
wallet-core-5ec344290efd937fa82c0704bc7c204a0bf14c78.tar.bz2
wallet-core-5ec344290efd937fa82c0704bc7c204a0bf14c78.zip
support for tipping protocol changes
Diffstat (limited to 'src/webex/pages')
-rw-r--r--src/webex/pages/tip.tsx203
1 files changed, 82 insertions, 121 deletions
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.