aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app
diff options
context:
space:
mode:
authorDan Miller <danjm.com@gmail.com>2018-10-03 21:20:05 +0800
committerDan Miller <danjm.com@gmail.com>2018-12-04 11:36:05 +0800
commit2dbae581ac3f9190ddd1c3457bd51b41eef8051b (patch)
tree774a8d9bd982f53b25e5aef54fa383197eb54b2e /ui/app
parent0ba6f7d9bb5c2183d8a370fd0955e18d45616207 (diff)
downloadtangerine-wallet-browser-2dbae581ac3f9190ddd1c3457bd51b41eef8051b.tar
tangerine-wallet-browser-2dbae581ac3f9190ddd1c3457bd51b41eef8051b.tar.gz
tangerine-wallet-browser-2dbae581ac3f9190ddd1c3457bd51b41eef8051b.tar.bz2
tangerine-wallet-browser-2dbae581ac3f9190ddd1c3457bd51b41eef8051b.tar.lz
tangerine-wallet-browser-2dbae581ac3f9190ddd1c3457bd51b41eef8051b.tar.xz
tangerine-wallet-browser-2dbae581ac3f9190ddd1c3457bd51b41eef8051b.tar.zst
tangerine-wallet-browser-2dbae581ac3f9190ddd1c3457bd51b41eef8051b.zip
Gas price chart improvements, redesign, bug fixes, and set up to receive external data
Diffstat (limited to 'ui/app')
-rw-r--r--ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/index.scss1
-rw-r--r--ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js3
-rw-r--r--ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js1
-rw-r--r--ui/app/components/gas-customization/gas-price-chart/gas-price-chart.component.js318
-rw-r--r--ui/app/components/gas-customization/gas-price-chart/index.scss65
5 files changed, 300 insertions, 88 deletions
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
index 9c89688e2..0fc9f4578 100644
--- 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
@@ -50,7 +50,6 @@
font-size: 12px;
color: #313A5E;
margin-left: 22px;
- margin-bottom: 11px;
}
&__speed-buttons {
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 88ef921c5..ac5981ab7 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
@@ -27,6 +27,7 @@ export default class GasModalPageContainer extends Component {
customModalGasPriceInHex: PropTypes.string,
customModalGasLimitInHex: PropTypes.string,
cancelAndClose: PropTypes.func,
+ transactionFee: PropTypes.string,
}
state = {}
@@ -46,6 +47,7 @@ export default class GasModalPageContainer extends Component {
customGasLimit,
newTotalFiat,
}) {
+ const { transactionFee } = this.props
return (
<AdvancedTabContent
updateCustomGasPrice={convertThenUpdateCustomGasPrice}
@@ -53,6 +55,7 @@ export default class GasModalPageContainer extends Component {
customGasPrice={customGasPrice}
customGasLimit={customGasLimit}
timeRemaining="1 min 31 sec"
+ transactionFee={transactionFee}
totalFee={newTotalFiat}
/>
)
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 0d8d9d8e4..9d6dd3fb5 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
@@ -73,6 +73,7 @@ const mapStateToProps = state => {
customGasPrice: calcCustomGasPrice(customModalGasPriceInHex),
customGasLimit: calcCustomGasLimit(customModalGasLimitInHex),
newTotalFiat,
+ transactionFee: addHexWEIsToRenderableFiat('0x0', customGasTotal, currentCurrency, conversionRate),
gasPriceButtonGroupProps: {
buttonDataLoading,
defaultActiveButtonIndex: getDefaultActiveButtonIndex(gasButtonInfo, customModalGasPriceInHex),
diff --git a/ui/app/components/gas-customization/gas-price-chart/gas-price-chart.component.js b/ui/app/components/gas-customization/gas-price-chart/gas-price-chart.component.js
index 7dadafa95..85893f771 100644
--- a/ui/app/components/gas-customization/gas-price-chart/gas-price-chart.component.js
+++ b/ui/app/components/gas-customization/gas-price-chart/gas-price-chart.component.js
@@ -18,139 +18,319 @@ function setTickPosition (axis, n, newPosition, secondNewPosition) {
.style('visibility', 'visible')
}
+function appendOrUpdateCircle ({ circle, data, itemIndex, cx, cy, cssId, appendOnly }) {
+ if (appendOnly || circle.empty()) {
+ circle.data([data])
+ .enter().append('circle')
+ .attr('class', () => this.generateClass('c3-selected-circle', itemIndex))
+ .attr('id', cssId)
+ .attr('cx', cx)
+ .attr('cy', cy)
+ .attr('stroke', () => this.color(data))
+ .attr('r', 6)
+ } else {
+ circle.data([data])
+ .attr('cx', cx)
+ .attr('cy', cy)
+ }
+}
+
export default class GasPriceChart extends Component {
static contextTypes = {
t: PropTypes.func,
}
static propTypes = {
+ priceAndTimeEstimates: PropTypes.array,
}
- renderChart () {
- c3.generate({
+ renderChart (priceAndTimeEstimates) {
+ const gasPrices = priceAndTimeEstimates.map(({ gasprice }) => gasprice)
+ const gasPricesMax = gasPrices[gasPrices.length - 1] + 1
+ const estimatedTimes = priceAndTimeEstimates.map(({ expectedTime }) => expectedTime)
+ const estimatedTimesMax = estimatedTimes[0]
+ const chart = c3.generate({
size: {
- height: 154,
+ height: 165,
},
- padding: {left: 36, right: 25, top: -5, bottom: 5},
+ transition: {
+ duration: 0,
+ },
+ padding: {left: 20, right: 15, top: 6, bottom: 10},
data: {
- x: 'x',
- columns: [
- ['x', 0, 0.01, 0.02, 0.03, 0.05, 0.07, 0.11, 0.15, 0.29, 0.35, 0.5, 0.55, 0.60, 0.63, 0.77, 0.88, 0.92, 0.93, 0.98, 0.99],
- ['data1', 100, 66, 55, 50, 45, 25, 22, 20.1, 20, 19.9, 15, 12, 10, 9.9, 8.0, 4.0, 3, 1, 0.5, 0.2],
- ],
- types: {
- data1: 'area',
- },
+ x: 'x',
+ columns: [
+ ['x', ...gasPrices],
+ ['data1', ...estimatedTimes],
+ ],
+ types: {
+ data1: 'area',
+ },
+ selection: {
+ enabled: false,
+ },
},
color: {
data1: '#259de5',
},
axis: {
x: {
- min: 0,
- max: 1,
+ min: gasPrices[0],
+ max: gasPricesMax,
tick: {
- values: ['0', '1.00'],
+ values: [Math.floor(gasPrices[0]), Math.ceil(gasPricesMax)],
outer: false,
- format: val => val === '0' ? val : '$' + val,
+ format: function (val) { return val + ' GWEI' },
},
- padding: {left: 0.005, right: 0},
+ padding: {left: gasPricesMax / 50, right: gasPricesMax / 50},
label: {
text: 'Gas Price ($)',
position: 'outer-center',
},
},
y: {
- padding: {top: 2, bottom: 0},
+ padding: {top: 7, bottom: 7},
tick: {
- values: ['5', '97'],
+ values: [Math.floor(estimatedTimesMax * 0.05), Math.ceil(estimatedTimesMax * 0.97)],
outer: false,
- format: val => val === '5' ? '0' : '100',
},
label: {
text: 'Confirmation time (sec)',
position: 'outer-middle',
},
+ min: 0,
},
},
legend: {
- show: false,
+ show: false,
},
grid: {
- x: {
- lines: [
- {value: 0.0833},
- {value: 0.1667},
- {value: 0.2500},
- {value: 0.3333},
- {value: 0.4167},
- {value: 0.5000},
- {value: 0.5833},
- {value: 0.6667},
- {value: 0.7500},
- {value: 0.8333},
- {value: 0.9167},
- {value: 1.0000},
- ],
- },
- lines: {
- front: false,
- },
+ x: {},
+ lines: {
+ front: false,
+ },
},
point: {
focus: {
expand: {
- enabled: true,
+ enabled: false,
r: 3.5,
},
},
},
tooltip: {
+ format: {
+ title: (v) => v.toPrecision(4),
+ },
contents: function (d, defaultTitleFormat, defaultValueFormat, color) {
- const $$ = this
- const config = $$.config
+ const config = this.config
const titleFormat = config.tooltip_format_title || defaultTitleFormat
-
- let text, title
- d.forEach(n => {
- if (n && (n.value || n.value === 0)) {
-
- if (!text) {
- title = titleFormat ? titleFormat(n.x) : n.x
- text = "<table class='" + 'custom-tooltip' + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + '</th></tr>' : '')
- }
+ let text
+ let title
+ d.forEach(el => {
+ if (el && (el.value || el.value === 0) && !text) {
+ title = titleFormat ? titleFormat(el.x) : el.x
+ text = "<table class='" + 'custom-tooltip' + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + '</th></tr>' : '')
}
})
- // for (i = 0; i < d.length; i++) {
- // if (! (d[i] && (d[i].value || d[i].value === 0))) { continue; }
-
- // if (! text) {
- // title = titleFormat ? titleFormat(d[i].x) : d[i].x;
- // text = "<table class='" + 'custom-tooltip' + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + "</th></tr>" : "");
- // }
- // }
return text + '</table>' + "<div class='tooltip-arrow'></div>"
},
position: function (data, width, height, element) {
- const x = d3.event.pageX - document.getElementById('chart').getBoundingClientRect().x + 19
- const y = d3.event.pageY - document.getElementById('chart').getBoundingClientRect().y + 20
- return {top: y, left: x}
+ const overlayedCircle = d3.select('#overlayed-circle')
+ if (overlayedCircle.empty()) {
+ return { top: -100, left: -100 }
+ }
+
+ const { x: circleX, y: circleY, width: circleWidth } = overlayedCircle.node().getBoundingClientRect()
+ const { x: chartXStart, y: chartYStart } = d3.select('.c3-chart').node().getBoundingClientRect()
+
+ // TODO: Confirm the below constants work with all data sets and screen sizes
+ // TODO: simplify l149-l159
+ let y = circleY - chartYStart - 19
+ if (circleY - circleWidth < chartYStart + 5) {
+ y = y + circleWidth + 38
+ d3.select('.tooltip-arrow').style('margin-top', '-16px')
+ } else {
+ d3.select('.tooltip-arrow').style('margin-top', '4px')
+ }
+ return {
+ top: y,
+ left: circleX - chartXStart + circleWidth - (gasPricesMax / 50),
+ }
},
+ show: true,
},
})
- setTimeout(() => {
- setTickPosition('y', 0, -5, 2)
+ chart.internal.selectPoint = function (data, itemIndex = (data.index || 0)) {
+ const { x: circleX, y: circleY, width: circleWidth } = d3.select('#overlayed-circle')
+ .node()
+ .getBoundingClientRect()
+ const { x: chartXStart, y: chartYStart } = d3.select('.c3-areas-data1')
+ .node()
+ .getBoundingClientRect()
+
+ d3.select('#set-circle').remove()
+
+ const circle = this.main
+ .select('.' + 'c3-selected-circles' + this.getTargetSelectorSuffix(data.id))
+ .selectAll('.' + 'c3-selected-circle' + '-' + itemIndex)
+
+ appendOrUpdateCircle.bind(this)({
+ circle,
+ data,
+ itemIndex,
+ cx: () => circleX - chartXStart + circleWidth + 2,
+ cy: () => circleY - chartYStart + circleWidth + 1,
+ cssId: 'set-circle',
+ appendOnly: true,
+ })
+ }
+
+ chart.internal.overlayPoint = function (data, itemIndex) {
+ const circle = this.main
+ .select('.' + 'c3-selected-circles' + this.getTargetSelectorSuffix(data.id))
+ .selectAll('.' + 'c3-selected-circle' + '-' + itemIndex)
+
+ appendOrUpdateCircle.bind(this)({
+ circle,
+ data,
+ itemIndex,
+ cx: this.circleX.bind(this),
+ cy: this.circleY.bind(this),
+ cssId: 'overlayed-circle',
+ })
+ }
+
+ chart.internal.setCurrentCircle = function (data, itemIndex) {
+ const circle = this.main
+ .select('.' + 'c3-selected-circles' + this.getTargetSelectorSuffix(data.id))
+ .selectAll('#current-circle')
+
+ appendOrUpdateCircle.bind(this)({
+ circle,
+ data,
+ itemIndex,
+ cx: this.circleX.bind(this),
+ cy: this.circleY.bind(this),
+ cssId: 'current-circle',
+ })
+ }
+
+ chart.internal.showTooltip = function (selectedData, element) {
+ const $$ = this
+ const config = $$.config
+ const forArc = $$.hasArcType()
+ const dataToShow = selectedData.filter((d) => d && (d.value || d.value === 0))
+ const positionFunction = config.tooltip_position || chart.internal.prototype.tooltipPosition
+ if (dataToShow.length === 0 || !config.tooltip_show) {
+ return
+ }
+ $$.tooltip.html(config.tooltip_contents.call($$, selectedData, $$.axis.getXAxisTickFormat(), $$.getYFormat(forArc), $$.color)).style('display', 'flex')
+
+ // Get tooltip dimensions
+ const tWidth = $$.tooltip.property('offsetWidth')
+ const tHeight = $$.tooltip.property('offsetHeight')
+ const position = positionFunction.call(this, dataToShow, tWidth, tHeight, element)
+ // Set tooltip
+ $$.tooltip.style('top', position.top + 'px').style('left', position.left + 'px')
+ }
+
+ setTimeout(function () {
+ setTickPosition('y', 0, -5, 8)
setTickPosition('y', 1, -3)
- setTickPosition('x', 0, 3)
- setTickPosition('x', 1, 3, -5)
+ setTickPosition('x', 0, 3, 20)
+ setTickPosition('x', 1, 3, -10)
+
+ // TODO: Confirm the below constants work with all data sets and screen sizes
d3.select('.c3-axis-x-label').attr('transform', 'translate(0,-15)')
- d3.select('.c3-axis-y-label').attr('transform', 'translate(42, 2) rotate(-90)')
+ d3.select('.c3-axis-y-label').attr('transform', 'translate(32, 2) rotate(-90)')
+ d3.select('.c3-xgrid-focus line').attr('y2', 98)
+
+ d3.select('.c3-chart').on('mouseout', () => {
+ const overLayedCircle = d3.select('#overlayed-circle')
+ if (!overLayedCircle.empty()) {
+ overLayedCircle.remove()
+ }
+ d3.select('.c3-tooltip-container').style('display', 'none !important')
+ })
+
+ const chartRect = d3.select('.c3-areas-data1')
+ const { x: chartXStart, width: chartWidth } = chartRect.node().getBoundingClientRect()
+
+ d3.select('.c3-chart').on('click', () => {
+ const overlayedCircle = d3.select('#overlayed-circle')
+ const numberOfValues = chart.internal.data.xs.data1.length
+ const { x: circleX, y: circleY } = overlayedCircle.node().getBoundingClientRect()
+ chart.internal.selectPoint({
+ x: circleX - chartXStart,
+ value: circleY - 1.5,
+ id: 'data1',
+ index: numberOfValues,
+ name: 'data1',
+ }, numberOfValues)
+ })
+
+ d3.select('.c3-chart').on('mousemove', function () {
+ const chartMouseXPos = d3.event.clientX - chartXStart
+ const posPercentile = chartMouseXPos / chartWidth
+
+
+ const currentPosValue = (gasPrices[gasPrices.length - 1] - gasPrices[0]) * posPercentile + gasPrices[0]
+ const closestLowerValueIndex = gasPrices.findIndex((e, i, a) => {
+ return e <= currentPosValue && a[i + 1] >= currentPosValue
+ })
+ const closestLowerValue = gasPrices[closestLowerValueIndex]
+ const estimatedClosestLowerTimeEstimate = estimatedTimes[closestLowerValueIndex]
+
+ const closestHigherValueIndex = gasPrices.findIndex((e, i, a) => {
+ return e > currentPosValue
+ })
+ const closestHigherValue = gasPrices[closestHigherValueIndex]
+ if (!closestHigherValue || !closestLowerValue) {
+ const overLayedCircle = d3.select('#overlayed-circle')
+ if (!overLayedCircle.empty()) {
+ overLayedCircle.remove()
+ }
+ d3.select('.c3-tooltip-container').style('display', 'none !important')
+ chart.internal.hideXGridFocus()
+ return
+ }
+ const estimatedClosestHigherTimeEstimate = estimatedTimes[closestHigherValueIndex]
+
+ const slope = (estimatedClosestHigherTimeEstimate - estimatedClosestLowerTimeEstimate) / (closestHigherValue - closestLowerValue)
+ const newTimeEstimate = -1 * (slope * (closestHigherValue - currentPosValue) - estimatedClosestHigherTimeEstimate)
+
+ const newEstimatedTimes = [...estimatedTimes, newTimeEstimate]
+ chart.internal.overlayPoint({
+ x: currentPosValue,
+ value: newTimeEstimate,
+ id: 'data1',
+ index: newEstimatedTimes.length,
+ name: 'data1',
+ }, newEstimatedTimes.length)
+ chart.internal.showTooltip([{
+ x: currentPosValue,
+ value: newTimeEstimate,
+ id: 'data1',
+ index: newEstimatedTimes.length,
+ name: 'data1',
+ }], chartRect._groups[0])
+ chart.internal.showXGridFocus([{
+ x: currentPosValue,
+ value: newTimeEstimate,
+ id: 'data1',
+ index: newEstimatedTimes.length,
+ name: 'data1',
+ }])
+ })
}, 0)
+
+
}
componentDidMount () {
- this.renderChart()
+ this.renderChart(this.props.priceAndTimeEstimates)
}
render () {
diff --git a/ui/app/components/gas-customization/gas-price-chart/index.scss b/ui/app/components/gas-customization/gas-price-chart/index.scss
index bfe9b807b..4c4640b1f 100644
--- a/ui/app/components/gas-customization/gas-price-chart/index.scss
+++ b/ui/app/components/gas-customization/gas-price-chart/index.scss
@@ -2,6 +2,13 @@
display: flex;
position: relative;
+ &__root {
+ max-height: 154px;
+ max-width: 391px;
+ position: relative;
+ overflow: hidden;
+ }
+
.tick text, .c3-axis-x-label, .c3-axis-y-label {
font-family: Roboto;
font-style: normal;
@@ -12,45 +19,61 @@
fill: #9A9CA6 !important;
}
+ .c3-tooltip-container {
+ display: flex;
+ justify-content: center !important;
+ align-items: flex-end !important;
+ }
+
.custom-tooltip {
background: rgba(0, 0, 0, 1);
- box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
- border-radius: 3px;
- transform: translate(-41px, -50px);
+ box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
+ border-radius: 3px;
opacity: 1 !important;
- width: 44px;
height: 21px;
z-index: 1;
}
.tooltip-arrow {
- background: rgba(0, 0, 0, 1);
- box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.5);
- top: -35px;
- left: -23px;
+ background: black;
+ box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.5);
+ /* top: 15px; */
+ /* left: 27px; */
+ -webkit-transform: rotate(45deg);
transform: rotate(45deg);
opacity: 1 !important;
width: 9px;
height: 9px;
- position: absolute;
- display: inline-block;
+ /* position: absolute; */
+ /* display: inline-block; */
+ margin-top: 4px;
}
.custom-tooltip th {
font-family: Roboto;
- font-style: normal;
- font-weight: 500;
- line-height: normal;
- font-size: 10px;
- text-align: center;
+ font-style: normal;
+ font-weight: 500;
+ line-height: normal;
+ font-size: 10px;
+ text-align: center;
+ padding: 3px;
+ color: #FFFFFF;
+ }
- color: #FFFFFF;
+ .c3-circle {
+ visibility: hidden;
}
- .c3-circle._expanded_ {
+ .c3-selected-circle, .c3-circle._expanded_ {
fill: #FFFFFF !important;
stroke-width: 2.4px !important;
stroke: #2d9fd9 !important;
+ /* visibility: visible; */
+ }
+
+ #set-circle {
+ fill: #313A5E !important;
+ stroke: #313A5E !important;
}
.c3-axis-x-label, .c3-axis-y-label {
@@ -84,7 +107,9 @@
stroke: #B8B8B8 !important;
}
- .c3-axis .tick line {display: none;}
+ .c3-xgrid-focus {
+ stroke: #aaa;
+ }
.c3-axis-x .domain {
fill: none;
@@ -95,6 +120,10 @@
fill: none;
stroke: #C8CCD6;
}
+
+ .c3-event-rect {
+ cursor: pointer;
+ }
}
@import url(//fonts.googleapis.com/css?family=Roboto:400,700,300);