aboutsummaryrefslogtreecommitdiff
path: root/src/pages/confirm-contract.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/pages/confirm-contract.tsx')
-rw-r--r--src/pages/confirm-contract.tsx231
1 files changed, 231 insertions, 0 deletions
diff --git a/src/pages/confirm-contract.tsx b/src/pages/confirm-contract.tsx
new file mode 100644
index 000000000..7bae691b1
--- /dev/null
+++ b/src/pages/confirm-contract.tsx
@@ -0,0 +1,231 @@
+/*
+ This file is part of TALER
+ (C) 2015 GNUnet e.V.
+
+ 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.
+
+ 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
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Page shown to the user to confirm entering
+ * a contract.
+ *
+ * @author Florian Dold
+ */
+
+"use strict";
+
+import {substituteFulfillmentUrl} from "src/helpers";
+import {Contract, AmountJson, IExchangeInfo} from "src/types";
+import {Offer} from "src/wallet";
+import {renderContract, prettyAmount} from "src/renderHtml";
+import {getExchanges} from "src/wxApi";
+
+
+interface DetailState {
+ collapsed: boolean;
+ exchanges: null|IExchangeInfo[];
+}
+
+interface DetailProps {
+ contract: Contract
+ collapsed: boolean
+}
+
+
+class Details extends React.Component<DetailProps, DetailState> {
+ constructor(props: DetailProps) {
+ super(props);
+ console.log("new Details component created");
+ this.state = {
+ collapsed: props.collapsed,
+ exchanges: null
+ };
+
+ console.log("initial state:", this.state);
+
+ this.update();
+ }
+
+ async update() {
+ let exchanges = await getExchanges();
+ this.setState({exchanges} as any);
+ }
+
+ render() {
+ if (this.state.collapsed) {
+ return (
+ <div>
+ <button className="linky"
+ onClick={() => { this.setState({collapsed: false} as any)}}>
+ show more details
+ </button>
+ </div>
+ );
+ } else {
+ return (
+ <div>
+ <button className="linky"
+ onClick={() => this.setState({collapsed: true} as any)}>
+ show less details
+ </button>
+ <div>
+ Accepted exchanges:
+ <ul>
+ {this.props.contract.exchanges.map(
+ e => <li>{`${e.url}: ${e.master_pub}`}</li>)}
+ </ul>
+ Exchanges in the wallet:
+ <ul>
+ {(this.state.exchanges || []).map(
+ (e: IExchangeInfo) =>
+ <li>{`${e.baseUrl}: ${e.masterPublicKey}`}</li>)}
+ </ul>
+ </div>
+ </div>);
+ }
+ }
+}
+
+interface ContractPromptProps {
+ offerId: number;
+}
+
+interface ContractPromptState {
+ offer: any;
+ error: string|null;
+ payDisabled: boolean;
+}
+
+class ContractPrompt extends React.Component<ContractPromptProps, ContractPromptState> {
+ constructor() {
+ super();
+ this.state = {
+ offer: undefined,
+ error: null,
+ payDisabled: true,
+ }
+ }
+
+ componentWillMount() {
+ this.update();
+ }
+
+ componentWillUnmount() {
+ // FIXME: abort running ops
+ }
+
+ async update() {
+ let offer = await this.getOffer();
+ this.setState({offer} as any);
+ this.checkPayment();
+ }
+
+ getOffer(): Promise<Offer> {
+ return new Promise((resolve, reject) => {
+ let msg = {
+ type: 'get-offer',
+ detail: {
+ offerId: this.props.offerId
+ }
+ };
+ chrome.runtime.sendMessage(msg, (resp) => {
+ resolve(resp);
+ });
+ })
+ }
+
+ checkPayment() {
+ let msg = {
+ type: 'check-pay',
+ detail: {
+ offer: this.state.offer
+ }
+ };
+ chrome.runtime.sendMessage(msg, (resp) => {
+ if (resp.error) {
+ console.log("check-pay error", JSON.stringify(resp));
+ switch (resp.error) {
+ case "coins-insufficient":
+ this.state.error = i18n`You have insufficient funds of the requested currency in your wallet.`;
+ break;
+ default:
+ this.state.error = `Error: ${resp.error}`;
+ break;
+ }
+ this.state.payDisabled = true;
+ } else {
+ this.state.payDisabled = false;
+ this.state.error = null;
+ }
+ this.setState({} as any);
+ window.setTimeout(() => this.checkPayment(), 500);
+ });
+ }
+
+ doPayment() {
+ let d = {offer: this.state.offer};
+ chrome.runtime.sendMessage({type: 'confirm-pay', detail: d}, (resp) => {
+ if (resp.error) {
+ console.log("confirm-pay error", JSON.stringify(resp));
+ switch (resp.error) {
+ case "coins-insufficient":
+ this.state.error = "You do not have enough coins of the" +
+ " requested currency.";
+ break;
+ default:
+ this.state.error = `Error: ${resp.error}`;
+ break;
+ }
+ this.setState({} as any);
+ return;
+ }
+ let c = d.offer.contract;
+ console.log("contract", c);
+ document.location.href = substituteFulfillmentUrl(c.fulfillment_url,
+ this.state.offer);
+ });
+ }
+
+
+ render() {
+ if (!this.state.offer) {
+ return <span>...</span>;
+ }
+ let c = this.state.offer.contract;
+ return (
+ <div>
+ <div>
+ {renderContract(c)}
+ </div>
+ <button onClick={() => this.doPayment()}
+ disabled={this.state.payDisabled}
+ className="accept">
+ Confirm payment
+ </button>
+ <div>
+ {(this.state.error ? <p className="errorbox">{this.state.error}</p> : <p />)}
+ </div>
+ <Details contract={c} collapsed={!this.state.error}/>
+ </div>
+ );
+ }
+}
+
+
+export function main() {
+ let url = URI(document.location.href);
+ let query: any = URI.parseQuery(url.query());
+ let offerId = JSON.parse(query.offerId);
+
+ ReactDOM.render(<ContractPrompt offerId={offerId}/>, document.getElementById(
+ "contract")!);
+}