aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packages/instant/src/components/time_counter.tsx64
-rw-r--r--packages/instant/src/components/timed_progress_bar.tsx101
-rw-r--r--packages/instant/src/constants.ts3
-rw-r--r--packages/instant/src/containers/selected_asset_simulated_progress_bar.tsx18
-rw-r--r--packages/instant/src/types.ts1
5 files changed, 180 insertions, 7 deletions
diff --git a/packages/instant/src/components/time_counter.tsx b/packages/instant/src/components/time_counter.tsx
new file mode 100644
index 000000000..26deb82bd
--- /dev/null
+++ b/packages/instant/src/components/time_counter.tsx
@@ -0,0 +1,64 @@
+import * as React from 'react';
+
+import { timeUtil } from '../util/time';
+
+import { Flex } from './ui/flex';
+import { Text } from './ui/text';
+
+export interface TimeCounterProps {
+ estimatedTimeMs: number;
+ ended: boolean;
+}
+interface TimeCounterState {
+ elapsedSeconds: number;
+}
+
+export class TimeCounter extends React.Component<TimeCounterProps, TimeCounterState> {
+ public state = {
+ elapsedSeconds: 0,
+ };
+ private _timerId?: number;
+
+ public componentDidMount(): void {
+ this._setupTimerBasedOnProps();
+ }
+
+ public componentWillUnmount(): void {
+ this._clearTimer();
+ }
+
+ public componentDidUpdate(prevProps: TimeCounterProps): void {
+ if (prevProps.ended !== this.props.ended) {
+ this._setupTimerBasedOnProps();
+ }
+ }
+
+ public render(): React.ReactNode {
+ const estimatedTimeSeconds = this.props.estimatedTimeMs / 1000;
+ return (
+ <Flex justify="space-between">
+ <Text>Est. Time ({timeUtil.secondsToHumanDescription(estimatedTimeSeconds)})</Text>
+ <Text>Time: {timeUtil.secondsToStopwatchTime(this.state.elapsedSeconds)}</Text>
+ </Flex>
+ );
+ }
+
+ private _setupTimerBasedOnProps(): void {
+ this.props.ended ? this._clearTimer() : this._newTimer();
+ }
+
+ private _newTimer(): void {
+ this._clearTimer();
+ this._timerId = window.setInterval(() => {
+ this.setState({
+ elapsedSeconds: this.state.elapsedSeconds + 1,
+ });
+ }, 1000);
+ }
+
+ private _clearTimer(): void {
+ if (this._timerId) {
+ window.clearInterval(this._timerId);
+ }
+ }
+}
diff --git a/packages/instant/src/components/timed_progress_bar.tsx b/packages/instant/src/components/timed_progress_bar.tsx
new file mode 100644
index 000000000..7fdfe1a25
--- /dev/null
+++ b/packages/instant/src/components/timed_progress_bar.tsx
@@ -0,0 +1,101 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import { Keyframes } from 'styled-components';
+
+import { PROGRESS_FINISH_ANIMATION_TIME_MS, PROGRESS_STALL_AT_PERCENTAGE } from '../constants';
+import { ColorOption, keyframes, styled } from '../style/theme';
+import { timeUtil } from '../util/time';
+
+import { Container } from './ui/container';
+import { Flex } from './ui/flex';
+import { Text } from './ui/text';
+
+export interface TimedProgressBarProps {
+ expectedTimeMs: number;
+ ended: boolean;
+}
+
+interface TimedProgressBarState {
+ animationTimeMs: number;
+ animationStartingWidth: string;
+ maxWidthPercent: number;
+}
+
+export const beginningState = (props: TimedProgressBarProps): TimedProgressBarState => {
+ return {
+ animationTimeMs: props.expectedTimeMs,
+ animationStartingWidth: '0%',
+ maxWidthPercent: PROGRESS_STALL_AT_PERCENTAGE,
+ };
+};
+
+export class TimedProgressBar extends React.Component<TimedProgressBarProps, TimedProgressBarState> {
+ private readonly _barRef = React.createRef<HTMLDivElement>();
+
+ public constructor(props: TimedProgressBarProps) {
+ super(props);
+ this.state = beginningState(props);
+ }
+
+ public componentDidUpdate(prevProps: TimedProgressBarProps, prevState: TimedProgressBarState): void {
+ if (prevProps.ended === false && this.props.ended === true) {
+ // Show nice animation going to end
+ // barRef current should always exist, but checking for typesafety
+ if (this._barRef.current) {
+ const curProgressWidth = this._barRef.current.offsetWidth;
+ this.setState({
+ animationTimeMs: PROGRESS_FINISH_ANIMATION_TIME_MS,
+ animationStartingWidth: `${curProgressWidth}px`,
+ maxWidthPercent: 100,
+ });
+ }
+ return;
+ }
+
+ if (prevProps.expectedTimeMs !== this.props.expectedTimeMs || prevProps.ended !== this.props.ended) {
+ // things changed, get fresh state
+ this.setState(beginningState(this.props));
+ }
+ }
+
+ public render(): React.ReactNode {
+ return (
+ <Container width="100%" backgroundColor={ColorOption.lightGrey} borderRadius="6px">
+ <TimedProgress
+ fromWidth={this.state.animationStartingWidth}
+ timeMs={this.state.animationTimeMs}
+ maxWidthPercent={this.state.maxWidthPercent}
+ ref={this._barRef as any}
+ />
+ </Container>
+ );
+ }
+}
+
+const expandingWidthKeyframes = (fromWidth: string, maxWidthPercent: number) => {
+ return keyframes`
+ from {
+ width: ${fromWidth}
+ }
+ to {
+ width: ${maxWidthPercent}%;
+ }
+ `;
+};
+
+interface TimedProgressProps {
+ timeMs: number;
+ fromWidth: string;
+ maxWidthPercent: number;
+}
+// TODO use PrimaryColor instead of black
+export const TimedProgress =
+ styled.div <
+ TimedProgressProps >
+ `
+ background-color: black;
+ border-radius: 6px;
+ height: 6px;
+ animation: ${props => expandingWidthKeyframes(props.fromWidth, props.maxWidthPercent)}
+ ${props => props.timeMs}ms linear 1 forwards;
+ `;
diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts
index 3b320ed36..9fdbf2830 100644
--- a/packages/instant/src/constants.ts
+++ b/packages/instant/src/constants.ts
@@ -8,5 +8,6 @@ export const DEFAULT_GAS_PRICE = GWEI_IN_WEI.mul(6);
export const DEFAULT_ESTIMATED_TRANSACTION_TIME_MS = 2 * 60 * 1000; // 2 minutes
export const ETH_GAS_STATION_API_BASE_URL = 'https://ethgasstation.info';
export const COINBASE_API_BASE_URL = 'https://api.coinbase.com/v2';
-export const PROGRESS_TICK_INTERVAL_MS = 250;
+export const PROGRESS_TICK_INTERVAL_MS = 250; // TODO: remove
export const PROGRESS_STALL_AT_PERCENTAGE = 95;
+export const PROGRESS_FINISH_ANIMATION_TIME_MS = 200;
diff --git a/packages/instant/src/containers/selected_asset_simulated_progress_bar.tsx b/packages/instant/src/containers/selected_asset_simulated_progress_bar.tsx
index a7acc4cb7..a989407d5 100644
--- a/packages/instant/src/containers/selected_asset_simulated_progress_bar.tsx
+++ b/packages/instant/src/containers/selected_asset_simulated_progress_bar.tsx
@@ -3,10 +3,15 @@ import * as React from 'react';
import { connect } from 'react-redux';
import { SimulatedProgressBar } from '../components/simulated_progress_bar';
+import { TimedProgressBar } from '../components/timed_progress_bar';
+import { TimeCounter } from '../components/time_counter';
+import { Container } from '../components/ui';
import { State } from '../redux/reducer';
import { OrderProcessState, OrderState, SimulatedProgress } from '../types';
+// TODO: rename this
+// TODO: delete SimulatedProgressBar code and anything else remaining
interface SelectedAssetProgressComponentProps {
buyOrderState: OrderState;
}
@@ -21,12 +26,15 @@ export const SelectedAssetSimulatedProgressComponent: React.StatelessComponent<
buyOrderState.processState === OrderProcessState.FAILURE
) {
const progress = buyOrderState.progress;
+ const ended = buyOrderState.processState !== OrderProcessState.PROCESSING;
+ const expectedTimeMs = progress.expectedEndTimeUnix - progress.startTimeUnix;
return (
- <SimulatedProgressBar
- startTimeUnix={progress.startTimeUnix}
- expectedEndTimeUnix={progress.expectedEndTimeUnix}
- ended={progress.ended}
- />
+ <Container padding="20px 20px 0px 20px" width="100%">
+ <Container marginBottom="5px">
+ <TimeCounter estimatedTimeMs={expectedTimeMs} ended={ended} key={progress.startTimeUnix} />
+ </Container>
+ <TimedProgressBar expectedTimeMs={expectedTimeMs} ended={ended} key={progress.startTimeUnix} />
+ </Container>
);
}
diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts
index 34893676d..288a6d111 100644
--- a/packages/instant/src/types.ts
+++ b/packages/instant/src/types.ts
@@ -19,7 +19,6 @@ export enum OrderProcessState {
export interface SimulatedProgress {
startTimeUnix: number;
expectedEndTimeUnix: number;
- ended: boolean;
}
interface OrderStatePreTx {