diff options
Diffstat (limited to 'src/webex/pages')
| -rw-r--r-- | src/webex/pages/tip.tsx | 203 | 
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. | 
