From 7f95c83f2f993fc3a64d4f5cad1d2d5fd29b08b3 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Fri, 7 Oct 2016 17:10:22 +0200 Subject: refactor reserve creation dialog --- pages/confirm-create-reserve.tsx | 325 +++++++++++++++++---------------------- 1 file changed, 138 insertions(+), 187 deletions(-) (limited to 'pages/confirm-create-reserve.tsx') diff --git a/pages/confirm-create-reserve.tsx b/pages/confirm-create-reserve.tsx index 666f8c68a..d0a08aac3 100644 --- a/pages/confirm-create-reserve.tsx +++ b/pages/confirm-create-reserve.tsx @@ -32,35 +32,37 @@ import {getReserveCreationInfo} from "../lib/wallet/wxApi"; let h = preact.h; -/** - * Execute something after a delay, with the possibility - * to reset the delay. - */ -class DelayTimer { - ms: number; - f: () => void; - timerId: number|undefined = undefined; - - constructor(ms: number, f: () => void) { - this.f = f; - this.ms = ms; +function delay(delayMs: number, value: T): Promise { + return new Promise((resolve, reject) => { + setTimeout(() => resolve(value), delayMs); + }); +} + +class EventTrigger { + triggerResolve: any; + triggerPromise: Promise; + + constructor() { + this.reset(); } - bump() { - this.stop(); - const handler = () => { - this.f(); - }; - this.timerId = window.setTimeout(handler, this.ms); + private reset() { + this.triggerPromise = new Promise((resolve, reject) => { + this.triggerResolve = resolve; + }); } - stop() { - if (this.timerId != undefined) { - window.clearTimeout(this.timerId); - } + trigger() { + this.triggerResolve(false); + this.reset(); + } + + async wait(delayMs: number): Promise { + return await Promise.race([this.triggerPromise, delay(delayMs, true)]); } } + interface StateHolder { (): T; (newState: T): void; @@ -85,7 +87,11 @@ abstract class ImplicitStateComponent extends preact.ComponentDetails will be displayed when a valid exchange provider URL is entered.

+ } + let denoms = rci.selectedDenoms; let countByPub: {[s: string]: number} = {}; @@ -153,6 +159,17 @@ function getSuggestedExchange(currency: string): Promise { return Promise.resolve(exchange); } + +function WithdrawFee(props: {reserveCreationInfo: ReserveCreationInfo|null}): JSX.Element { + if (props.reserveCreationInfo) { + let {overhead, withdrawFee} = props.reserveCreationInfo; + let totalCost = Amounts.add(overhead, withdrawFee).amount; + return

Withdraw fees: {amountToPretty(totalCost)}

; + } + return

; +} + + interface ExchangeSelectionProps { suggestedExchangeUrl: string; amount: AmountJson; @@ -162,84 +179,77 @@ interface ExchangeSelectionProps { class ExchangeSelection extends ImplicitStateComponent { - complexViewRequested: StateHolder = this.makeState(false); statusString: StateHolder = this.makeState(null); reserveCreationInfo: StateHolder = this.makeState( null); url: StateHolder = this.makeState(null); detailCollapsed: StateHolder = this.makeState(true); - private timer: DelayTimer; - - isValidExchange: boolean; + updateEvent = new EventTrigger(); constructor(props: ExchangeSelectionProps) { super(props); - this.timer = new DelayTimer(800, () => this.update()); - this.url(props.suggestedExchangeUrl || null); - this.update(); + this.onUrlChanged(props.suggestedExchangeUrl || null); } - render(props: ExchangeSelectionProps): JSX.Element { - - console.log("props", props); - - let header = ( -

- {"You are about to withdraw "} - {amountToPretty(props.amount)} - {" from your bank account into your wallet."} -

- ); - if (this.complexViewRequested() || !props.suggestedExchangeUrl) { + renderAdvanced(): JSX.Element { + if (this.detailCollapsed()) { return ( -
- {header} - {this.viewComplex()} -
); + + ); } - return (
- {header} - {this.viewSimple()} -
); +

Provider Selection

+ + this.onUrlChanged((e.target as HTMLInputElement).value)}/> +
+ {this.renderStatus()} +

Detailed Fee Structure

+ {renderReserveCreationDetails(this.reserveCreationInfo())} + ) } + renderFee() { + if (!this.reserveCreationInfo()) { + return "??"; + } + let rci = this.reserveCreationInfo()!; + let totalCost = Amounts.add(rci.overhead, rci.withdrawFee).amount; + return `${amountToPretty(totalCost)}`; + } - viewSimple() { - let advancedButton = ( - + render(props: ExchangeSelectionProps): JSX.Element { + return ( +
+

+ {"You are about to withdraw "} + {amountToPretty(props.amount)} + {" from your bank account into your wallet."} +

+

+ The exchange provider will charge + {" "} + {this.renderFee()} + {" "} + in fees. +

+ +
+ {this.renderAdvanced()} +
); - if (this.statusString()) { - return ( -
-

Error: {this.statusString()}

- {advancedButton} -
- ); - } - else if (this.reserveCreationInfo() != undefined) { - let {overhead, withdrawFee} = this.reserveCreationInfo()!; - let totalCost = Amounts.add(overhead, withdrawFee).amount; - return ( -
-

{`Withdraw fees: ${amountToPretty(totalCost)}`}

- - - {advancedButton} -
- ); - } else { - return

Please wait...

- } } @@ -250,53 +260,41 @@ class ExchangeSelection extends ImplicitStateComponent { this.props.callback_url); } + /** + * Do an update of the reserve creation info, without any debouncing. + */ + async forceReserveUpdate() { + this.reserveCreationInfo(null); + if (!this.url()) { + this.statusString(i18n`Error: URL is empty`); + return; + } - update() { - this.timer.stop(); - const doUpdate = () => { - this.reserveCreationInfo(null); - if (!this.url()) { - this.statusString = i18n`Error: URL is empty`; - m.redraw(true); - return; - } - this.statusString(null); - let parsedUrl = URI(this.url()!); - if (parsedUrl.is("relative")) { - this.statusString = i18n`Error: URL may not be relative`; - this.forceUpdate(); - return; - } - - this.forceUpdate(); - - console.log("doing get exchange info"); - - getReserveCreationInfo(this.url()!, this.props.amount) - .then((r: ReserveCreationInfo) => { - console.log("get exchange info resolved"); - this.isValidExchange = true; - this.reserveCreationInfo(r); - console.dir(r); - }) - .catch((e) => { - console.log("get exchange info rejected"); - if (e.hasOwnProperty("httpStatus")) { - this.statusString(`Error: request failed with status ${e.httpStatus}`); - } else if (e.hasOwnProperty("errorResponse")) { - let resp = e.errorResponse; - this.statusString(`Error: ${resp.error} (${resp.hint})`); - } - }); - }; - - doUpdate(); + this.statusString(null); + let parsedUrl = URI(this.url()!); + if (parsedUrl.is("relative")) { + this.statusString(i18n`Error: URL may not be relative`); + return; + } - console.log("got update", this.url()); + try { + let r = await getReserveCreationInfo(this.url()!, + this.props.amount); + console.log("get exchange info resolved"); + this.reserveCreationInfo(r); + console.dir(r); + } catch (e) { + console.log("get exchange info rejected"); + if (e.hasOwnProperty("httpStatus")) { + this.statusString(`Error: request failed with status ${e.httpStatus}`); + } else if (e.hasOwnProperty("errorResponse")) { + let resp = e.errorResponse; + this.statusString(`Error: ${resp.error} (${resp.hint})`); + } + } } reset() { - this.isValidExchange = false; this.statusString(null); this.reserveCreationInfo(null); } @@ -338,75 +336,28 @@ class ExchangeSelection extends ImplicitStateComponent { chrome.runtime.sendMessage({type: 'create-reserve', detail: d}, cb); } - onUrlChanged(url: string) { + async onUrlChanged(url: string|null) { this.reset(); this.url(url); - this.timer.bump(); + if (url == undefined) { + return; + } + this.updateEvent.trigger(); + let waited = await this.updateEvent.wait(200); + if (waited) { + // Run the actual update if nobody else preempted us. + this.forceReserveUpdate(); + this.forceUpdate(); + } } - viewComplex() { - function *f(): IterableIterator { - if (this.reserveCreationInfo()) { - let {overhead, withdrawFee} = this.reserveCreationInfo()!; - let totalCost = Amounts.add(overhead, withdrawFee).amount; - yield

Withdraw fees: {amountToPretty(totalCost)}

; - } - - yield ( - - ); - - yield ; - - yield ( - - ); - } else { - yield ( - - ); - yield ( -
- {renderReserveCreationDetails(this.reserveCreationInfo()!)} -
- ); - } - } + renderStatus(): any { + if (this.statusString()) { + return

{this.statusString()}

; + } else if (!this.reserveCreationInfo()) { + return

Checking URL, please wait ...

; } - - return Array.from(f.call(this)); + return ""; } } -- cgit v1.2.3