diff options
Diffstat (limited to 'ui')
20 files changed, 532 insertions, 63 deletions
diff --git a/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/advanced-tab-content.component.js b/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/advanced-tab-content.component.js new file mode 100644 index 000000000..7ddf13e51 --- /dev/null +++ b/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/advanced-tab-content.component.js @@ -0,0 +1,105 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { + MIN_GAS_PRICE_DEC, + MIN_GAS_LIMIT_DEC, +} from '../../../send/send.constants' +import GasSlider from '../../gas-slider' +import TimeRemaining from './time-remaining' + +export default class AdvancedTabContent extends Component { + static contextTypes = { + t: PropTypes.func, + } + + static propTypes = { + updateCustomGasPrice: PropTypes.func, + updateCustomGasLimit: PropTypes.func, + customGasPrice: PropTypes.number, + customGasLimit: PropTypes.number, + millisecondsRemaining: PropTypes.number, + } + + gasInput (value, onChange, min, precision, showGWEI) { + return ( + <div className="advanced-tab__gas-edit-row__input-wrapper"> + <input + className="advanced-tab__gas-edit-row__input" + type="number" + value={value} + onChange={event => onChange(Number(event.target.value))} + /> + {showGWEI + ? <span className="advanced-tab__gas-edit-row__gwei-symbol">GWEI</span> + : null} + </div> + ) + } + + infoButton (onClick) { + return <i className="fa info-circle" onClick={onClick} /> + } + + render () { + const { + updateCustomGasPrice, + updateCustomGasLimit, + millisecondsRemaining, + customGasPrice, + customGasLimit, + } = this.props + + return ( + <div className="advanced-tab"> + <div className="advanced-tab__transaction-data-summary"> + <div className="advanced-tab__transaction-data-summary__titles"> + <span>New Transaction Fee</span> + <span>~Transaction Time</span> + </div> + <div className="advanced-tab__transaction-data-summary__container"> + <div className="advanced-tab__transaction-data-summary__fee"> + $0.30 + </div> + <TimeRemaining + milliseconds={millisecondsRemaining} + /> + </div> + </div> + <div className="advanced-tab__fee-chart-title"> + Live Transaction Fee Predictions + </div> + <div className="advanced-tab__fee-chart" /> + <div className="advanced-tab__slider-container"> + <GasSlider + onChange={value => { + updateCustomGasPrice(Number(value)) + }} + lowLabel={'Cheaper'} + highLabel={'Faster'} + value={customGasPrice} + step={0.1} + max={200} + min={0} + coloredStart={{}} + /> + </div> + <div className="advanced-tab__gas-edit-rows"> + <div className="advanced-tab__gas-edit-row"> + <div className="advanced-tab__gas-edit-row__label"> + Gas Price + { this.infoButton(() => {}) } + </div> + { this.gasInput(customGasPrice, updateCustomGasPrice, MIN_GAS_PRICE_DEC, 9, true) } + </div> + <div className="advanced-tab__gas-edit-row"> + <div className="advanced-tab__gas-edit-row__label"> + Gas Limit + { this.infoButton(() => {}) } + </div> + { this.gasInput(customGasLimit, updateCustomGasLimit, MIN_GAS_LIMIT_DEC, 0) } + </div> + </div> + </div> + ) + } +} diff --git a/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/index.js b/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/index.js new file mode 100644 index 000000000..492037f25 --- /dev/null +++ b/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/index.js @@ -0,0 +1 @@ +export { default } from './advanced-tab-content.component' diff --git a/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/index.scss b/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/index.scss new file mode 100644 index 000000000..5dc30e061 --- /dev/null +++ b/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/index.scss @@ -0,0 +1,109 @@ +@import './time-remaining/index'; + +.advanced-tab { + display: flex; + flex-flow: column; + border-bottom: 1px solid $alto; + + &__transaction-data-summary, + &__fee-chart-title, + &__gas-edit-row { + padding-left: 24px; + padding-right: 24px; + } + + &__transaction-data-summary { + display: flex; + flex-flow: column; + color: $mid-gray; + margin-top: 12px; + + &__titles, + &__container { + display: flex; + flex-flow: row; + justify-content: space-between; + font-size: 12px; + } + + &__container { + font-size: 26px; + margin-top: 6px; + } + } + + &__fee-chart-title { + font-size: 14px; + color: $scorpion; + margin-top: 22px; + } + + &__fee-chart { + padding-left: 10px; + margin-top: 24px; + height: 134px; + } + + &__slider-container { + padding-left: 27px; + padding-right: 27px; + } + + &__gas-edit-rows { + margin-top: 44px; + height: 87px; + display: flex; + flex-flow: column; + justify-content: space-between; + } + + &__gas-edit-row { + display: flex; + flex-flow: row; + justify-content: space-between; + + &__label { + color: $mid-gray; + font-size: 16px; + + .info-circle { + color: $silver; + margin-left: 10px; + } + } + + + &__input-wrapper { + position: relative; + } + + &__input { + border: 1px solid $dusty-gray; + border-radius: 4px; + color: $mid-gray; + font-size: 16px; + height: 37px; + width: 163px; + padding: 8px 10px 10px 10px; + } + + input[type="number"]::-webkit-inner-spin-button { + -webkit-appearance: none; + -moz-appearance: none; + display: none; + } + + input[type="number"]:hover::-webkit-inner-spin-button { + -webkit-appearance: none; + -moz-appearance: none; + display: none; + } + + &__gwei-symbol { + position: absolute; + top: 8px; + right: 10px; + color: $dusty-gray; + } + } +}
\ No newline at end of file diff --git a/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/time-remaining/index.js b/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/time-remaining/index.js new file mode 100644 index 000000000..61b681e1a --- /dev/null +++ b/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/time-remaining/index.js @@ -0,0 +1 @@ +export { default } from './time-remaining.component' diff --git a/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/time-remaining/index.scss b/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/time-remaining/index.scss new file mode 100644 index 000000000..01bb06268 --- /dev/null +++ b/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/time-remaining/index.scss @@ -0,0 +1,13 @@ +.time-remaining { + .minutes-num, .seconds-num { + font-size: 26px; + } + + .seconds-num { + margin-left: 7px; + } + + .minutes-label, .seconds-label { + font-size: 14px; + } +}
\ No newline at end of file diff --git a/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/time-remaining/time-remaining.component.js b/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/time-remaining/time-remaining.component.js new file mode 100644 index 000000000..826d41f9c --- /dev/null +++ b/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/time-remaining/time-remaining.component.js @@ -0,0 +1,33 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { getTimeBreakdown } from './time-remaining.utils' + +export default class TimeRemaining extends Component { + static contextTypes = { + t: PropTypes.func, + } + + static propTypes = { + milliseconds: PropTypes.number, + } + + render () { + const { + milliseconds, + } = this.props + + const { + minutes, + seconds, + } = getTimeBreakdown(milliseconds) + + return ( + <div className="time-remaining"> + <span className="minutes-num">{minutes}</span> + <span className="minutes-label">{this.context.t('minutesShorthand')}</span> + <span className="seconds-num">{seconds}</span> + <span className="seconds-label">{this.context.t('secondsShorthand')}</span> + </div> + ) + } +} diff --git a/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/time-remaining/time-remaining.utils.js b/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/time-remaining/time-remaining.utils.js new file mode 100644 index 000000000..cf43e0acb --- /dev/null +++ b/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/time-remaining/time-remaining.utils.js @@ -0,0 +1,11 @@ +function getTimeBreakdown (milliseconds) { + return { + hours: Math.floor(milliseconds / 3600000), + minutes: Math.floor((milliseconds % 3600000) / 60000), + seconds: Math.floor((milliseconds % 60000) / 1000), + } +} + +module.exports = { + getTimeBreakdown, +} diff --git a/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js b/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js index 697594ec9..1d7a9d986 100644 --- a/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js +++ b/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js @@ -2,6 +2,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import PageContainer from '../../page-container' import { Tabs, Tab } from '../../tabs' +import AdvancedTabContent from './advanced-tab-content' export default class GasModalPageContainer extends Component { static contextTypes = { @@ -10,6 +11,10 @@ export default class GasModalPageContainer extends Component { static propTypes = { hideModal: PropTypes.func, + updateCustomGasPrice: PropTypes.func, + updateCustomGasLimit: PropTypes.func, + customGasPrice: PropTypes.number, + customGasLimit: PropTypes.number, } state = {} @@ -20,9 +25,22 @@ export default class GasModalPageContainer extends Component { ) } - renderAdvancedTabContent () { + renderAdvancedTabContent = () => { + const { + updateCustomGasPrice, + updateCustomGasLimit, + customGasPrice, + customGasLimit, + } = this.props + return ( - <div className="gas-modal-content__advanced-tab"/> + <AdvancedTabContent + updateCustomGasPrice={updateCustomGasPrice} + updateCustomGasLimit={updateCustomGasLimit} + customGasPrice={customGasPrice} + customGasLimit={customGasLimit} + millisecondsRemaining={91000} + /> ) } @@ -68,14 +86,16 @@ export default class GasModalPageContainer extends Component { const { hideModal } = this.props return ( - <PageContainer - title={this.context.t('customGas')} - subtitle={this.context.t('customGasSubTitle')} - tabsComponent={this.renderTabs()} - disabled={false} - onCancel={() => hideModal()} - onClose={() => hideModal()} - /> + <div className="gas-modal-page-container"> + <PageContainer + title={this.context.t('customGas')} + subtitle={this.context.t('customGasSubTitle')} + tabsComponent={this.renderTabs()} + disabled={false} + onCancel={() => hideModal()} + onClose={() => hideModal()} + /> + </div> ) } } diff --git a/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js b/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js index 71c700507..f7ac91a38 100644 --- a/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js +++ b/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js @@ -1,11 +1,28 @@ import { connect } from 'react-redux' import GasModalPageContainer from './gas-modal-page-container.component' import { hideModal } from '../../../actions' +import { + setCustomGasPrice, + setCustomGasLimit, +} from '../../../ducks/custom-gas' +import { + getCustomGasPrice, + getCustomGasLimit, +} from '../../../selectors/custom-gas' + +const mapStateToProps = state => { + return { + customGasPrice: getCustomGasPrice(state), + customGasLimit: getCustomGasLimit(state), + } +} const mapDispatchToProps = dispatch => { return { hideModal: () => dispatch(hideModal()), + updateCustomGasPrice: (newPrice) => dispatch(setCustomGasPrice(newPrice)), + updateCustomGasLimit: (newLimit) => dispatch(setCustomGasLimit(newLimit)), } } -export default connect(null, mapDispatchToProps)(GasModalPageContainer) +export default connect(mapStateToProps, mapDispatchToProps)(GasModalPageContainer) diff --git a/ui/app/components/gas-customization/gas-modal-page-container/index.scss b/ui/app/components/gas-customization/gas-modal-page-container/index.scss index a6609a385..027165b48 100644 --- a/ui/app/components/gas-customization/gas-modal-page-container/index.scss +++ b/ui/app/components/gas-customization/gas-modal-page-container/index.scss @@ -1,3 +1,11 @@ +@import './advanced-tab-content/index'; + +.gas-modal-page-container { + .page-container { + width: 391px; + } +} + .gas-modal-content { &__basic-tab { height: 219px; diff --git a/ui/app/components/gas-customization/gas-slider/gas-slider.component.js b/ui/app/components/gas-customization/gas-slider/gas-slider.component.js new file mode 100644 index 000000000..5836e7dfc --- /dev/null +++ b/ui/app/components/gas-customization/gas-slider/gas-slider.component.js @@ -0,0 +1,48 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' + +export default class AdvancedTabContent extends Component { + static propTypes = { + onChange: PropTypes.func, + lowLabel: PropTypes.string, + highLabel: PropTypes.string, + value: PropTypes.number, + step: PropTypes.number, + max: PropTypes.number, + min: PropTypes.number, + } + + render () { + const { + onChange, + lowLabel, + highLabel, + value, + step, + max, + min, + } = this.props + + return ( + <div className="gas-slider"> + <input + className="gas-slider__input" + type="range" + step={step} + max={max} + min={min} + value={value} + id="gasSlider" + onChange={event => onChange(event.target.value)} + /> + <div className="gas-slider__bar"> + <div className="gas-slider__colored"/> + </div> + <div className="gas-slider__labels"> + <span>{lowLabel}</span> + <span>{highLabel}</span> + </div> + </div> + ) + } +} diff --git a/ui/app/components/gas-customization/gas-slider/index.js b/ui/app/components/gas-customization/gas-slider/index.js new file mode 100644 index 000000000..f1752c93f --- /dev/null +++ b/ui/app/components/gas-customization/gas-slider/index.js @@ -0,0 +1 @@ +export { default } from './gas-slider.component' diff --git a/ui/app/components/gas-customization/gas-slider/index.scss b/ui/app/components/gas-customization/gas-slider/index.scss new file mode 100644 index 000000000..e6c734367 --- /dev/null +++ b/ui/app/components/gas-customization/gas-slider/index.scss @@ -0,0 +1,54 @@ +.gas-slider { + position: relative; + width: 322px; + + &__input { + width: 322px; + margin-left: -2px; + z-index: 2; + } + + input[type=range] { + -webkit-appearance: none !important; + } + + input[type=range]::-webkit-slider-thumb { + -webkit-appearance: none !important; + height: 34px; + width: 34px; + background-color: $curious-blue; + box-shadow: 0 2px 4px 0 rgba(0,0,0,0.08); + border-radius: 50%; + position: relative; + z-index: 10; + } + + &__bar { + height: 6px; + width: 322px; + background: $alto; + display: flex; + justify-content: space-between; + position: absolute; + top: 16px; + z-index: 0; + border-radius: 4px; + } + + &__colored { + height: 6px; + border-radius: 4px; + margin-left: 102px; + width: 322px; + z-index: 1; + background-color: $blizzard-blue; + } + + &__labels { + display: flex; + justify-content: space-between; + font-size: 12px; + margin-top: -6px; + color: $mid-gray; + } +}
\ No newline at end of file diff --git a/ui/app/components/gas-customization/index.scss b/ui/app/components/gas-customization/index.scss new file mode 100644 index 000000000..91ca5004e --- /dev/null +++ b/ui/app/components/gas-customization/index.scss @@ -0,0 +1,3 @@ +@import './gas-slider/index'; + +@import './gas-modal-page-container/index'; diff --git a/ui/app/components/index.scss b/ui/app/components/index.scss index 48c6496d9..156b1b9f6 100644 --- a/ui/app/components/index.scss +++ b/ui/app/components/index.scss @@ -64,4 +64,10 @@ @import './unit-input/index'; -@import './gas-customization/gas-modal-page-container/index' +@import './gas-customization/gas-modal-page-container/index'; + +@import './gas-customization/gas-modal-page-container/index'; + +@import './gas-customization/gas-modal-page-container/index'; + +@import './gas-customization/index'; diff --git a/ui/app/css/itcss/components/gas-slider.scss b/ui/app/css/itcss/components/gas-slider.scss index c27a560bd..e69de29bb 100644 --- a/ui/app/css/itcss/components/gas-slider.scss +++ b/ui/app/css/itcss/components/gas-slider.scss @@ -1,51 +0,0 @@ -.gas-slider { - position: relative; - width: 313px; - - &__input { - width: 317px; - margin-left: -2px; - z-index: 2; - } - - input[type=range] { - -webkit-appearance: none !important; - } - - input[type=range]::-webkit-slider-thumb { - -webkit-appearance: none !important; - height: 26px; - width: 26px; - border: 2px solid #B8B8B8; - background-color: #FFFFFF; - box-shadow: 0 2px 4px 0 rgba(0,0,0,0.08); - border-radius: 50%; - position: relative; - z-index: 10; - } - - &__bar { - height: 6px; - width: 313px; - background: $alto; - display: flex; - justify-content: space-between; - position: absolute; - top: 11px; - z-index: 0; - } - - &__low, &__high { - height: 6px; - width: 49px; - z-index: 1; - } - - &__low { - background-color: $crimson; - } - - &__high { - background-color: $caribbean-green; - } -}
\ No newline at end of file diff --git a/ui/app/css/itcss/settings/variables.scss b/ui/app/css/itcss/settings/variables.scss index 6c2b82f39..1c95f06a0 100644 --- a/ui/app/css/itcss/settings/variables.scss +++ b/ui/app/css/itcss/settings/variables.scss @@ -57,6 +57,7 @@ $ecstasy: #f7861c; $linen: #fdf4f4; $oslo-gray: #8C8E94; $polar: #fafcfe; +$blizzard-blue: #bfdef3; /* Z-Indicies diff --git a/ui/app/ducks/custom-gas.js b/ui/app/ducks/custom-gas.js new file mode 100644 index 000000000..f1f483e93 --- /dev/null +++ b/ui/app/ducks/custom-gas.js @@ -0,0 +1,67 @@ +import extend from 'xtend' + +// Actions +const SET_CUSTOM_GAS_PRICE = 'metamask/custom-gas/SET_CUSTOM_GAS_PRICE' +const SET_CUSTOM_GAS_LIMIT = 'metamask/custom-gas/SET_CUSTOM_GAS_LIMIT' +const SET_CUSTOM_GAS_ERRORS = 'metamask/custom-gas/SET_CUSTOM_GAS_ERRORS' +const RESET_CUSTOM_GAS_STATE = 'metamask/custom-gas/RESET_CUSTOM_GAS_STATE' + +// TODO: determine if this approach to initState is consistent with conventional ducks pattern +const initState = { + price: 0, + limit: 21000, + errors: {}, +} + +// Reducer +export default function reducer ({ customGas: customGasState = initState }, action = {}) { + const newState = extend({}, customGasState) + + switch (action.type) { + case SET_CUSTOM_GAS_PRICE: + return extend(newState, { + price: action.value, + }) + case SET_CUSTOM_GAS_LIMIT: + return extend(newState, { + limit: action.value, + }) + case SET_CUSTOM_GAS_ERRORS: + return extend(newState, { + errors: { + ...newState.errors, + ...action.value, + }, + }) + case RESET_CUSTOM_GAS_STATE: + return extend({}, initState) + default: + return newState + } +} + +// Action Creators +export function setCustomGasPrice (newPrice) { + return { + type: SET_CUSTOM_GAS_PRICE, + value: newPrice, + } +} + +export function setCustomGasLimit (newLimit) { + return { + type: SET_CUSTOM_GAS_LIMIT, + value: newLimit, + } +} + +export function setCustomGasErrors (newErrors) { + return { + type: SET_CUSTOM_GAS_ERRORS, + value: newErrors, + } +} + +export function resetCustomGasState () { + return { type: RESET_CUSTOM_GAS_STATE } +} diff --git a/ui/app/reducers.js b/ui/app/reducers.js index e1a982f93..7234c3c02 100644 --- a/ui/app/reducers.js +++ b/ui/app/reducers.js @@ -10,6 +10,7 @@ const reduceApp = require('./reducers/app') const reduceLocale = require('./reducers/locale') const reduceSend = require('./ducks/send.duck').default import reduceConfirmTransaction from './ducks/confirm-transaction.duck' +import reduceCustomGas from './ducks/custom-gas' window.METAMASK_CACHED_LOG_STATE = null @@ -49,6 +50,8 @@ function rootReducer (state, action) { state.confirmTransaction = reduceConfirmTransaction(state, action) + state.customGas = reduceCustomGas(state, action) + window.METAMASK_CACHED_LOG_STATE = state return state } diff --git a/ui/app/selectors/custom-gas.js b/ui/app/selectors/custom-gas.js new file mode 100644 index 000000000..97280190f --- /dev/null +++ b/ui/app/selectors/custom-gas.js @@ -0,0 +1,19 @@ +const selectors = { + getCustomGasPrice, + getCustomGasLimit, + getCustomGasErrors, +} + +module.exports = selectors + +function getCustomGasPrice (state) { + return state.customGas.price +} + +function getCustomGasLimit (state) { + return state.customGas.limit +} + +function getCustomGasErrors (state) { + return state.customGas.errors +} |