From 2c89cd722ed66f9f8522913b1a5bbc55ce5a7ca6 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 5 Jul 2016 15:16:40 -0700 Subject: Simplify empty account balance rendering --- ui/app/util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'ui') diff --git a/ui/app/util.js b/ui/app/util.js index db12a1282..4181b096f 100644 --- a/ui/app/util.js +++ b/ui/app/util.js @@ -99,7 +99,7 @@ function formatBalance (balance, decimalsToKeep) { var parsed = parseBalance(balance) var beforeDecimal = parsed[0] var afterDecimal = parsed[1] - var formatted = 'None' + var formatted = '0 ETH' if (decimalsToKeep === undefined) { if (beforeDecimal === '0') { if (afterDecimal !== '0') { -- cgit From 7058dc4ee3d00f4d4e407c656cf671b2d4fd62f2 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Jul 2016 17:58:46 -0700 Subject: Began reworking tx conf view --- ui/app/components/account-panel.js | 2 +- ui/app/components/mini-account-panel.js | 105 ++++++++++++++++++++++++++++++++ ui/app/components/pending-tx-details.js | 83 ++++++++++++++++++++----- ui/app/css/lib.css | 6 ++ ui/app/reducers.js | 1 + ui/app/util.js | 14 ++++- ui/lib/contract-namer.js | 17 ++++++ 7 files changed, 210 insertions(+), 18 deletions(-) create mode 100644 ui/app/components/mini-account-panel.js create mode 100644 ui/lib/contract-namer.js (limited to 'ui') diff --git a/ui/app/components/account-panel.js b/ui/app/components/account-panel.js index d50522fa2..abaaf8163 100644 --- a/ui/app/components/account-panel.js +++ b/ui/app/components/account-panel.js @@ -22,7 +22,7 @@ AccountPanel.prototype.render = function () { var panelState = { key: `accountPanel${identity.address}`, identiconKey: identity.address, - identiconLabel: identity.name, + identiconLabel: identity.name || '', attributes: [ { key: 'ADDRESS', diff --git a/ui/app/components/mini-account-panel.js b/ui/app/components/mini-account-panel.js new file mode 100644 index 000000000..745ff2077 --- /dev/null +++ b/ui/app/components/mini-account-panel.js @@ -0,0 +1,105 @@ +const inherits = require('util').inherits +const Component = require('react').Component +const h = require('react-hyperscript') +const Identicon = require('./identicon') +const formatBalance = require('../util').formatBalance +const TransactionIcon = require('./transaction-list-item-icon') + +module.exports = AccountPanel + + +inherits(AccountPanel, Component) +function AccountPanel () { + Component.call(this) +} + +AccountPanel.prototype.render = function () { + var props = this.props + var picOrder = props.picOrder || 'left' + var isFauceting = props.isFauceting + const { attrs, imageSeed } = props + + return ( + + h('.identity-panel.flex-row.flex-left', { + style: { + cursor: props.onClick ? 'pointer' : undefined, + }, + onClick: props.onClick, + }, [ + + this.genIcon(imageSeed, picOrder), + + h('div.flex-column.flex-justify-center', { + style: { + lineHeight: '15px', + order: 2, + display: 'flex', + alignItems: picOrder === 'left' ? 'flex-begin' : 'flex-end', + }, + }, [ + + props.attrs.map((attr) => { + return h('span.font-small', { + key: `mini-${attr}`, + style: { + fontFamily: 'Montserrat UltraLight, Montserrat Light, Montserrat', + }, + }, attr) + }), + + ]), + + ]) + ) +} + +AccountPanel.prototype.genIcon= function(seed, picOrder) { + const props = this.props + + // When there is no seed value, this is a contract creation. + // We then show the contract icon. + if (!seed) { + return h('.identicon-wrapper.flex-column.select-none', { + style: { + order: picOrder === 'left' ? 1 : 3, + }, + }, [ + h('i.fa.fa-file-text-o.fa-lg', { + style: { + fontSize: '42px', + transform: 'translate(0px, -16px)', + }, + }) + ]) + } + + // If there was a seed, we return an identicon for that address. + return h('.identicon-wrapper.flex-column.select-none', { + style: { + order: picOrder === 'left' ? 1 : 3, + }, + }, [ + h(Identicon, { + address: seed, + imageify: props.imageifyIdenticons, + }), + ]) +} + +function balanceOrFaucetingIndication (account, isFauceting) { + // Temporarily deactivating isFauceting indication + // because it shows fauceting for empty restored accounts. + if (/* isFauceting*/ false) { + return { + key: 'Account is auto-funding.', + value: 'Please wait.', + } + } else { + return { + key: 'BALANCE', + value: formatBalance(account.balance), + } + } +} + diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js index 2ba613f20..0d972ea05 100644 --- a/ui/app/components/pending-tx-details.js +++ b/ui/app/components/pending-tx-details.js @@ -2,10 +2,11 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits -const AccountPanel = require('./account-panel') +const MiniAccountPanel = require('./mini-account-panel') const addressSummary = require('../util').addressSummary const readableDate = require('../util').readableDate const formatBalance = require('../util').formatBalance +const nameForAddress = require('../../lib/contract-namer') module.exports = PendingTxDetails @@ -14,12 +15,10 @@ function PendingTxDetails () { Component.call(this) } -PendingTxDetails.prototype.render = function () { - var state = this.props - return this.renderGeneric(h, state) -} +const PTXP = PendingTxDetails.prototype -PendingTxDetails.prototype.renderGeneric = function (h, state) { +PTXP.render = function () { + var state = this.props var txData = state.txData var txParams = txData.txParams || {} @@ -27,17 +26,39 @@ PendingTxDetails.prototype.renderGeneric = function (h, state) { var identity = state.identities[address] || { address: address } var account = state.accounts[address] || { address: address } - return ( + var isContractDeploy = !('to' in txParams) + return ( h('div', [ - // account that will sign - h(AccountPanel, { - showFullAddress: true, - identity: identity, - account: account, - imageifyIdenticons: state.imageifyIdenticons, - }), + h('.flex-row.flex-center', { + style: { + maxWidth: '100%', + }, + }, [ + + h(MiniAccountPanel, { + attrs: [ + identity.name, + addressSummary(address, 6, 4, false), + formatBalance(identity.balance), + ], + imageSeed: address, + imageifyIdenticons: state.imageifyIdenticons, + picOrder: 'right', + }), + + h('img', { + src: 'images/forward-carrat.svg', + style: { + padding: '5px 6px 0px 10px', + height: '48px', + }, + }), + + this.miniAccountPanelForRecipient(), + + ]), // tx data h('.tx-data.flex-column.flex-justify-center.flex-grow.select-none', [ @@ -59,7 +80,39 @@ PendingTxDetails.prototype.renderGeneric = function (h, state) { ]), ]) - ) +} +PTXP.miniAccountPanelForRecipient = function() { + var state = this.props + var txData = state.txData + + var txParams = txData.txParams || {} + var address = txParams.from || state.selectedAddress + var identity = state.identities[address] || { address: address } + var account = state.accounts[address] || { address: address } + + var isContractDeploy = !('to' in txParams) + + // If it's not a contract deploy, send to the account + if (!isContractDeploy) { + return h(MiniAccountPanel, { + attrs: [ + nameForAddress(txParams.to), + addressSummary(txParams.to, 6, 4, false), + ], + imageSeed: address, + imageifyIdenticons: state.imageifyIdenticons, + picOrder: 'left', + }) + } else { + return h(MiniAccountPanel, { + attrs: [ + 'New Contract' + ], + imageifyIdenticons: state.imageifyIdenticons, + picOrder: 'left', + }) + } } + diff --git a/ui/app/css/lib.css b/ui/app/css/lib.css index a7da11e77..22b26d4f1 100644 --- a/ui/app/css/lib.css +++ b/ui/app/css/lib.css @@ -220,3 +220,9 @@ hr.horizontal-line { .invisible { visibility: hidden; } + +.one-line-concat { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/ui/app/reducers.js b/ui/app/reducers.js index f198758ea..8a8f1c72b 100644 --- a/ui/app/reducers.js +++ b/ui/app/reducers.js @@ -35,6 +35,7 @@ function rootReducer (state, action) { state.appState = reduceApp(state, action) + console.log(JSON.stringify(state)) return state } diff --git a/ui/app/util.js b/ui/app/util.js index 4181b096f..d0ee57dee 100644 --- a/ui/app/util.js +++ b/ui/app/util.js @@ -21,6 +21,7 @@ for (var currency in valueTable) { module.exports = { valuesFor: valuesFor, addressSummary: addressSummary, + miniAddressSummary: miniAddressSummary, isAllOneCase: isAllOneCase, isValidAddress: isValidAddress, numericBalance: numericBalance, @@ -43,10 +44,19 @@ function valuesFor (obj) { .map(function (key) { return obj[key] }) } -function addressSummary (address) { +function addressSummary (address, firstSegLength = 10, lastSegLength = 4, includeHex = true) { + if (!address) return '' + let checked = ethUtil.toChecksumAddress(address) + if (!includeHex) { + checked = ethUtil.stripHexPrefix(checked) + } + return checked ? checked.slice(0, firstSegLength) + '...' + checked.slice(checked.length - lastSegLength) : '...' +} + +function miniAddressSummary (address) { if (!address) return '' var checked = ethUtil.toChecksumAddress(address) - return checked ? checked.slice(0, 2 + 8) + '...' + checked.slice(-4) : '...' + return checked ? checked.slice(0, 4) + '...' + checked.slice(-4) : '...' } function isValidAddress (address) { diff --git a/ui/lib/contract-namer.js b/ui/lib/contract-namer.js new file mode 100644 index 000000000..eae066ad5 --- /dev/null +++ b/ui/lib/contract-namer.js @@ -0,0 +1,17 @@ +/* CONTRACT NAMER + * + * Takes an address, + * Returns a nicname if we have one stored, + * otherwise returns null. + */ + +const nicknames = {} + +module.exports = function(address) { + + if (address in nicknames) { + return nicknames[address] + } + + return null +} -- cgit From 7481f7c3df72f5616615fcd74537d1f091b1bc4b Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Jul 2016 21:32:36 -0700 Subject: Fleshed out pending tx view --- ui/app/components/mini-account-panel.js | 2 +- ui/app/components/pending-tx-details.js | 106 +++++++++++++++++++++++--------- ui/app/components/pending-tx.js | 6 +- ui/app/util.js | 3 +- 4 files changed, 86 insertions(+), 31 deletions(-) (limited to 'ui') diff --git a/ui/app/components/mini-account-panel.js b/ui/app/components/mini-account-panel.js index 745ff2077..e8d0debad 100644 --- a/ui/app/components/mini-account-panel.js +++ b/ui/app/components/mini-account-panel.js @@ -43,7 +43,7 @@ AccountPanel.prototype.render = function () { return h('span.font-small', { key: `mini-${attr}`, style: { - fontFamily: 'Montserrat UltraLight, Montserrat Light, Montserrat', + fontFamily: 'Montserrat Light, Montserrat, sans-serif', }, }, attr) }), diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js index 0d972ea05..04e9cd2cd 100644 --- a/ui/app/components/pending-tx-details.js +++ b/ui/app/components/pending-tx-details.js @@ -7,6 +7,7 @@ const addressSummary = require('../util').addressSummary const readableDate = require('../util').readableDate const formatBalance = require('../util').formatBalance const nameForAddress = require('../../lib/contract-namer') +const BN = require('ethereumjs-util').BN module.exports = PendingTxDetails @@ -18,16 +19,18 @@ function PendingTxDetails () { const PTXP = PendingTxDetails.prototype PTXP.render = function () { - var state = this.props - var txData = state.txData + var props = this.props + var txData = props.txData var txParams = txData.txParams || {} - var address = txParams.from || state.selectedAddress - var identity = state.identities[address] || { address: address } - var account = state.accounts[address] || { address: address } + var address = txParams.from || props.selectedAddress + var identity = props.identities[address] || { address: address } + var account = props.accounts[address] || { address: address } var isContractDeploy = !('to' in txParams) + var maxCost = (new BN(txParams.value, 16) + new BN(txParams.gas, 16)).toString(16) + return ( h('div', [ @@ -41,10 +44,10 @@ PTXP.render = function () { attrs: [ identity.name, addressSummary(address, 6, 4, false), - formatBalance(identity.balance), + formatBalance(identity.balance).formatted, ], imageSeed: address, - imageifyIdenticons: state.imageifyIdenticons, + imageifyIdenticons: props.imageifyIdenticons, picOrder: 'right', }), @@ -57,40 +60,70 @@ PTXP.render = function () { }), this.miniAccountPanelForRecipient(), - ]), - // tx data - h('.tx-data.flex-column.flex-justify-center.flex-grow.select-none', [ - - h('.flex-row.flex-space-between', [ - h('label.font-small', 'TO ADDRESS'), - h('span.font-small', addressSummary(txParams.to)), + h('style', ` + .table-box { + margin: 7px 6px 0px 6px; + } + .table-box .row { + margin: 0px; + background: rgb(236,236,236); + display: flex; + justify-content: space-between; + font-family: Montserrat Light, sans-serif; + font-size: 13px; + padding: 5px 15px; + } + `), + + h('.table-box', [ + + h('.row', [ + h('.cell.label', 'Amount'), + h('.cell.value', formatBalance(txParams.value).formatted), ]), - h('.flex-row.flex-space-between', [ - h('label.font-small', 'DATE'), - h('span.font-small', readableDate(txData.time)), + h('.cell.row', [ + h('.cell.label', 'Max Transaction Fee'), + h('.cell.value', formatBalance(txParams.gas).formatted), ]), - h('.flex-row.flex-space-between', [ - h('label.font-small', 'AMOUNT'), - h('span.font-small', formatBalance(txParams.value)), + h('.cell.row', { + style: { + fontFamily: 'Montserrat Regular', + background: 'rgb(216,216,216)', + }, + }, [ + h('.cell.label', 'Max Total'), + h('.cell.value', formatBalance(maxCost).formatted), ]), - ]), + + h('.cell.row', { + style: { + background: '#f7f7f7', + paddingBottom: '0px', + } + }, [ + h('.cell.label'), + h('.cell.value', `Data included: ${txParams.data.length - 2} bytes`) + ]) + ]), // End of Table + + this.warnIfNeeded(), ]) ) } PTXP.miniAccountPanelForRecipient = function() { - var state = this.props - var txData = state.txData + var props = this.props + var txData = props.txData var txParams = txData.txParams || {} - var address = txParams.from || state.selectedAddress - var identity = state.identities[address] || { address: address } - var account = state.accounts[address] || { address: address } + var address = txParams.from || props.selectedAddress + var identity = props.identities[address] || { address: address } + var account = props.accounts[address] || { address: address } var isContractDeploy = !('to' in txParams) @@ -102,7 +135,7 @@ PTXP.miniAccountPanelForRecipient = function() { addressSummary(txParams.to, 6, 4, false), ], imageSeed: address, - imageifyIdenticons: state.imageifyIdenticons, + imageifyIdenticons: props.imageifyIdenticons, picOrder: 'left', }) } else { @@ -110,9 +143,26 @@ PTXP.miniAccountPanelForRecipient = function() { attrs: [ 'New Contract' ], - imageifyIdenticons: state.imageifyIdenticons, + imageifyIdenticons: props.imageifyIdenticons, picOrder: 'left', }) } } +// Should analyze if there is a DELEGATECALL opcode +// in the recipient contract, and show a warning if so. +PTXP.warnIfNeeded = function() { + return null + + return h('span.error', { + style: { + fontFamily: 'Montserrat Light', + fontSize: '13px', + display: 'flex', + justifyContent: 'center', + } + }, [ + h('i.fa.fa-lg.fa-info-circle', { style: { margin: '5px' } }), + h('span', ' Your identity may be used in other contracts!'), + ]) +} diff --git a/ui/app/components/pending-tx.js b/ui/app/components/pending-tx.js index 5d1fd4c16..01225f646 100644 --- a/ui/app/components/pending-tx.js +++ b/ui/app/components/pending-tx.js @@ -33,7 +33,11 @@ PendingTx.prototype.render = function () { h(PendingTxDetails, state), // send + cancel - h('.flex-row.flex-space-around', [ + h('.flex-row.flex-space-around', { + style: { + marginTop: '14px', + } + }, [ h('button', { onClick: state.cancelTransaction, }, 'Reject'), diff --git a/ui/app/util.js b/ui/app/util.js index 86bf03667..1e92f0ea2 100644 --- a/ui/app/util.js +++ b/ui/app/util.js @@ -104,7 +104,8 @@ function parseBalance (balance) { return [beforeDecimal, afterDecimal] } -// Takes wei hex, returns "None" or "${formattedAmount} ETH" +// Takes wei hex, returns an object with three properties. +// Its "formatted" property is what we generally use to render values. function formatBalance (balance, decimalsToKeep) { var parsed = parseBalance(balance) var beforeDecimal = parsed[0] -- cgit From ce463f3aff07c3cb73370cc1446a6b64f91ceb05 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Jul 2016 22:48:02 -0700 Subject: Fixed up pending-tx-details --- ui/app/components/eth-balance.js | 3 ++- ui/app/components/pending-tx-details.js | 47 ++++++++++++++++++--------------- ui/app/reducers.js | 1 - 3 files changed, 27 insertions(+), 24 deletions(-) (limited to 'ui') diff --git a/ui/app/components/eth-balance.js b/ui/app/components/eth-balance.js index 510b620f3..288a0fcaf 100644 --- a/ui/app/components/eth-balance.js +++ b/ui/app/components/eth-balance.js @@ -14,7 +14,8 @@ function EthBalanceComponent () { EthBalanceComponent.prototype.render = function () { var state = this.props var style = state.style - var value = formatBalance(state.value) + + const value = formatBalance(state.value) return ( diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js index 04e9cd2cd..369104089 100644 --- a/ui/app/components/pending-tx-details.js +++ b/ui/app/components/pending-tx-details.js @@ -4,10 +4,14 @@ const inherits = require('util').inherits const MiniAccountPanel = require('./mini-account-panel') const addressSummary = require('../util').addressSummary -const readableDate = require('../util').readableDate const formatBalance = require('../util').formatBalance const nameForAddress = require('../../lib/contract-namer') -const BN = require('ethereumjs-util').BN +const ethUtil = require('ethereumjs-util') +const BN = ethUtil.BN + +const baseGasFee = new BN('21000', 10) +const gasCost = new BN('10000000000', 10) +const baseFeeHex = baseGasFee.mul(gasCost).toString(16) module.exports = PendingTxDetails @@ -25,11 +29,11 @@ PTXP.render = function () { var txParams = txData.txParams || {} var address = txParams.from || props.selectedAddress var identity = props.identities[address] || { address: address } - var account = props.accounts[address] || { address: address } - - var isContractDeploy = !('to' in txParams) - var maxCost = (new BN(txParams.value, 16) + new BN(txParams.gas, 16)).toString(16) + var gasCost = ethUtil.stripHexPrefix(txParams.gas || baseFeeHex) + var txValue = ethUtil.stripHexPrefix(txParams.value || '0x0') + var maxCost = ((new BN(txValue, 16)).add(new BN(gasCost, 16))).toString(16) + var dataLength = txParams.data ? txParams.data.length - 2 : 0 return ( h('div', [ @@ -86,7 +90,7 @@ PTXP.render = function () { h('.cell.row', [ h('.cell.label', 'Max Transaction Fee'), - h('.cell.value', formatBalance(txParams.gas).formatted), + h('.cell.value', formatBalance(gasCost).formatted), ]), h('.cell.row', { @@ -103,11 +107,11 @@ PTXP.render = function () { style: { background: '#f7f7f7', paddingBottom: '0px', - } + }, }, [ h('.cell.label'), - h('.cell.value', `Data included: ${txParams.data.length - 2} bytes`) - ]) + h('.cell.value', `Data included: ${dataLength} bytes`), + ]), ]), // End of Table this.warnIfNeeded(), @@ -116,15 +120,10 @@ PTXP.render = function () { ) } -PTXP.miniAccountPanelForRecipient = function() { +PTXP.miniAccountPanelForRecipient = function () { var props = this.props var txData = props.txData - var txParams = txData.txParams || {} - var address = txParams.from || props.selectedAddress - var identity = props.identities[address] || { address: address } - var account = props.accounts[address] || { address: address } - var isContractDeploy = !('to' in txParams) // If it's not a contract deploy, send to the account @@ -134,14 +133,14 @@ PTXP.miniAccountPanelForRecipient = function() { nameForAddress(txParams.to), addressSummary(txParams.to, 6, 4, false), ], - imageSeed: address, + imageSeed: txParams.to, imageifyIdenticons: props.imageifyIdenticons, picOrder: 'left', }) } else { - return h(MiniAccountPanel, { + return h(MiniAccountPanel, { attrs: [ - 'New Contract' + 'New Contract', ], imageifyIdenticons: props.imageifyIdenticons, picOrder: 'left', @@ -151,8 +150,12 @@ PTXP.miniAccountPanelForRecipient = function() { // Should analyze if there is a DELEGATECALL opcode // in the recipient contract, and show a warning if so. -PTXP.warnIfNeeded = function() { - return null +PTXP.warnIfNeeded = function () { + const containsDelegateCall = !!this.props.txData.containsDelegateCall + + if (!containsDelegateCall) { + return null + } return h('span.error', { style: { @@ -160,7 +163,7 @@ PTXP.warnIfNeeded = function() { fontSize: '13px', display: 'flex', justifyContent: 'center', - } + }, }, [ h('i.fa.fa-lg.fa-info-circle', { style: { margin: '5px' } }), h('span', ' Your identity may be used in other contracts!'), diff --git a/ui/app/reducers.js b/ui/app/reducers.js index 8a8f1c72b..f198758ea 100644 --- a/ui/app/reducers.js +++ b/ui/app/reducers.js @@ -35,7 +35,6 @@ function rootReducer (state, action) { state.appState = reduceApp(state, action) - console.log(JSON.stringify(state)) return state } -- cgit From 9e0d9b88ccf4348ab9ac21f8fc3d64738dac5b40 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Jul 2016 22:51:28 -0700 Subject: Linted --- ui/app/components/mini-account-panel.js | 29 ++++------------------------- ui/app/components/pending-tx.js | 6 ++---- 2 files changed, 6 insertions(+), 29 deletions(-) (limited to 'ui') diff --git a/ui/app/components/mini-account-panel.js b/ui/app/components/mini-account-panel.js index e8d0debad..1ec7b4d41 100644 --- a/ui/app/components/mini-account-panel.js +++ b/ui/app/components/mini-account-panel.js @@ -2,8 +2,6 @@ const inherits = require('util').inherits const Component = require('react').Component const h = require('react-hyperscript') const Identicon = require('./identicon') -const formatBalance = require('../util').formatBalance -const TransactionIcon = require('./transaction-list-item-icon') module.exports = AccountPanel @@ -16,7 +14,6 @@ function AccountPanel () { AccountPanel.prototype.render = function () { var props = this.props var picOrder = props.picOrder || 'left' - var isFauceting = props.isFauceting const { attrs, imageSeed } = props return ( @@ -39,7 +36,7 @@ AccountPanel.prototype.render = function () { }, }, [ - props.attrs.map((attr) => { + attrs.map((attr) => { return h('span.font-small', { key: `mini-${attr}`, style: { @@ -47,14 +44,12 @@ AccountPanel.prototype.render = function () { }, }, attr) }), - ]), - ]) ) } -AccountPanel.prototype.genIcon= function(seed, picOrder) { +AccountPanel.prototype.genIcon = function (seed, picOrder) { const props = this.props // When there is no seed value, this is a contract creation. @@ -63,14 +58,14 @@ AccountPanel.prototype.genIcon= function(seed, picOrder) { return h('.identicon-wrapper.flex-column.select-none', { style: { order: picOrder === 'left' ? 1 : 3, - }, + }, }, [ h('i.fa.fa-file-text-o.fa-lg', { style: { fontSize: '42px', transform: 'translate(0px, -16px)', }, - }) + }), ]) } @@ -87,19 +82,3 @@ AccountPanel.prototype.genIcon= function(seed, picOrder) { ]) } -function balanceOrFaucetingIndication (account, isFauceting) { - // Temporarily deactivating isFauceting indication - // because it shows fauceting for empty restored accounts. - if (/* isFauceting*/ false) { - return { - key: 'Account is auto-funding.', - value: 'Please wait.', - } - } else { - return { - key: 'BALANCE', - value: formatBalance(account.balance), - } - } -} - diff --git a/ui/app/components/pending-tx.js b/ui/app/components/pending-tx.js index 01225f646..77dba87ee 100644 --- a/ui/app/components/pending-tx.js +++ b/ui/app/components/pending-tx.js @@ -36,7 +36,7 @@ PendingTx.prototype.render = function () { h('.flex-row.flex-space-around', { style: { marginTop: '14px', - } + }, }, [ h('button', { onClick: state.cancelTransaction, @@ -45,9 +45,7 @@ PendingTx.prototype.render = function () { onClick: state.sendTransaction, }, 'Approve'), ]), - ]) - ) - } + -- cgit From 3bcf44509ee7377ff108ebde19d9125c77527e79 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Jul 2016 23:11:47 -0700 Subject: Fix sender balance rendering in pending tx details --- ui/app/components/pending-tx-details.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'ui') diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js index 369104089..24ab58343 100644 --- a/ui/app/components/pending-tx-details.js +++ b/ui/app/components/pending-tx-details.js @@ -29,12 +29,15 @@ PTXP.render = function () { var txParams = txData.txParams || {} var address = txParams.from || props.selectedAddress var identity = props.identities[address] || { address: address } + var balance = props.accounts[address].balance var gasCost = ethUtil.stripHexPrefix(txParams.gas || baseFeeHex) var txValue = ethUtil.stripHexPrefix(txParams.value || '0x0') var maxCost = ((new BN(txValue, 16)).add(new BN(gasCost, 16))).toString(16) var dataLength = txParams.data ? txParams.data.length - 2 : 0 + console.dir(identity) + console.dir({props}) return ( h('div', [ @@ -48,7 +51,7 @@ PTXP.render = function () { attrs: [ identity.name, addressSummary(address, 6, 4, false), - formatBalance(identity.balance).formatted, + formatBalance(balance).formatted, ], imageSeed: address, imageifyIdenticons: props.imageifyIdenticons, -- cgit From 5faa64817c2d935029bfc302eff6c7f0df8a8989 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Jul 2016 23:16:04 -0700 Subject: Update gas cost --- ui/app/components/pending-tx-details.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'ui') diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js index 24ab58343..c826d9da3 100644 --- a/ui/app/components/pending-tx-details.js +++ b/ui/app/components/pending-tx-details.js @@ -10,7 +10,7 @@ const ethUtil = require('ethereumjs-util') const BN = ethUtil.BN const baseGasFee = new BN('21000', 10) -const gasCost = new BN('10000000000', 10) +const gasCost = new BN('4a817c800', 16) const baseFeeHex = baseGasFee.mul(gasCost).toString(16) module.exports = PendingTxDetails -- cgit From c2655bef5a2852eb03f3c2b5dcd134c57e3b34f7 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Jul 2016 23:35:59 -0700 Subject: Correct data length calculation --- ui/app/components/pending-tx-details.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'ui') diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js index c826d9da3..8ecca856a 100644 --- a/ui/app/components/pending-tx-details.js +++ b/ui/app/components/pending-tx-details.js @@ -34,7 +34,7 @@ PTXP.render = function () { var gasCost = ethUtil.stripHexPrefix(txParams.gas || baseFeeHex) var txValue = ethUtil.stripHexPrefix(txParams.value || '0x0') var maxCost = ((new BN(txValue, 16)).add(new BN(gasCost, 16))).toString(16) - var dataLength = txParams.data ? txParams.data.length - 2 : 0 + var dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0 console.dir(identity) console.dir({props}) -- cgit From 21f17214be854bf07f44b0d388329ae33598d79c Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Jul 2016 11:39:24 -0700 Subject: Remove formatBalance.formatted references --- ui/app/components/pending-tx-details.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'ui') diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js index 8ecca856a..191f5d142 100644 --- a/ui/app/components/pending-tx-details.js +++ b/ui/app/components/pending-tx-details.js @@ -51,7 +51,7 @@ PTXP.render = function () { attrs: [ identity.name, addressSummary(address, 6, 4, false), - formatBalance(balance).formatted, + formatBalance(balance), ], imageSeed: address, imageifyIdenticons: props.imageifyIdenticons, @@ -88,12 +88,12 @@ PTXP.render = function () { h('.row', [ h('.cell.label', 'Amount'), - h('.cell.value', formatBalance(txParams.value).formatted), + h('.cell.value', formatBalance(txParams.value)), ]), h('.cell.row', [ h('.cell.label', 'Max Transaction Fee'), - h('.cell.value', formatBalance(gasCost).formatted), + h('.cell.value', formatBalance(gasCost)), ]), h('.cell.row', { @@ -103,7 +103,7 @@ PTXP.render = function () { }, }, [ h('.cell.label', 'Max Total'), - h('.cell.value', formatBalance(maxCost).formatted), + h('.cell.value', formatBalance(maxCost)), ]), h('.cell.row', { -- cgit From 1e92e7e9d42b503302c4633be4512c0f566a4f9a Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Jul 2016 11:50:01 -0700 Subject: Make mini account panel labels fully configurable --- ui/app/components/mini-account-panel.js | 12 +------ ui/app/components/pending-tx-details.js | 58 +++++++++++++++++++++++---------- 2 files changed, 42 insertions(+), 28 deletions(-) (limited to 'ui') diff --git a/ui/app/components/mini-account-panel.js b/ui/app/components/mini-account-panel.js index 1ec7b4d41..a8127acd7 100644 --- a/ui/app/components/mini-account-panel.js +++ b/ui/app/components/mini-account-panel.js @@ -34,17 +34,7 @@ AccountPanel.prototype.render = function () { display: 'flex', alignItems: picOrder === 'left' ? 'flex-begin' : 'flex-end', }, - }, [ - - attrs.map((attr) => { - return h('span.font-small', { - key: `mini-${attr}`, - style: { - fontFamily: 'Montserrat Light, Montserrat, sans-serif', - }, - }, attr) - }), - ]), + }, this.props.children), ]) ) } diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js index 191f5d142..9c3272faa 100644 --- a/ui/app/components/pending-tx-details.js +++ b/ui/app/components/pending-tx-details.js @@ -36,8 +36,6 @@ PTXP.render = function () { var maxCost = ((new BN(txValue, 16)).add(new BN(gasCost, 16))).toString(16) var dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0 - console.dir(identity) - console.dir({props}) return ( h('div', [ @@ -48,15 +46,28 @@ PTXP.render = function () { }, [ h(MiniAccountPanel, { - attrs: [ - identity.name, - addressSummary(address, 6, 4, false), - formatBalance(balance), - ], imageSeed: address, imageifyIdenticons: props.imageifyIdenticons, picOrder: 'right', - }), + }, [ + h('span.font-small', { + style: { + fontFamily: 'Montserrat Light, Montserrat, sans-serif', + }, + }, identity.name), + h('span.font-small', { + style: { + fontFamily: 'Montserrat Light, Montserrat, sans-serif', + }, + }, addressSummary(address, 6, 4, false)), + + h('span.font-small', { + style: { + fontFamily: 'Montserrat Light, Montserrat, sans-serif', + }, + }, formatBalance(balance)), + + ]), h('img', { src: 'images/forward-carrat.svg', @@ -132,22 +143,35 @@ PTXP.miniAccountPanelForRecipient = function () { // If it's not a contract deploy, send to the account if (!isContractDeploy) { return h(MiniAccountPanel, { - attrs: [ - nameForAddress(txParams.to), - addressSummary(txParams.to, 6, 4, false), - ], imageSeed: txParams.to, imageifyIdenticons: props.imageifyIdenticons, picOrder: 'left', - }) + }, [ + h('span.font-small', { + style: { + fontFamily: 'Montserrat Light, Montserrat, sans-serif', + }, + }, nameForAddress(txParams.to)), + h('span.font-small', { + style: { + fontFamily: 'Montserrat Light, Montserrat, sans-serif', + }, + }, addressSummary(txParams.to, 6, 4, false)), + ]) + } else { return h(MiniAccountPanel, { - attrs: [ - 'New Contract', - ], imageifyIdenticons: props.imageifyIdenticons, picOrder: 'left', - }) + }, [ + + h('span.font-small', { + style: { + fontFamily: 'Montserrat Light, Montserrat, sans-serif', + }, + }, 'New Contract'), + + ]) } } -- cgit From 97b60caac0dfabaa3652fde75cfafa9551bf47ce Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Jul 2016 12:27:18 -0700 Subject: Add configurable params to eth-balance --- ui/app/components/eth-balance.js | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'ui') diff --git a/ui/app/components/eth-balance.js b/ui/app/components/eth-balance.js index 113e698ad..d50ac771d 100644 --- a/ui/app/components/eth-balance.js +++ b/ui/app/components/eth-balance.js @@ -11,10 +11,10 @@ function EthBalanceComponent () { } EthBalanceComponent.prototype.render = function () { - var state = this.props - var style = state.style + var props = this.props + var style = props.style - const value = formatBalance(state.value) + const value = formatBalance(props.value) return ( @@ -31,26 +31,33 @@ EthBalanceComponent.prototype.render = function () { ) } EthBalanceComponent.prototype.renderBalance = function (value) { + const props = this.props if (value === 'None') return value var balance = value.split(' ')[0] var label = value.split(' ')[1] + var tagName = props.inline ? 'span' : 'div' + var topTag = props.inline ? 'div' : '.flex-column' return ( - h('.flex-column', { + h(topTag, { style: { alignItems: 'flex-end', - lineHeight: '13px', - fontFamily: 'Montserrat Thin', + lineHeight: props.fontSize || '13px', + fontFamily: 'Montserrat Regular', textRendering: 'geometricPrecision', }, }, [ - h('div', balance), - h('div', { + h(tagName, { style: { - color: ' #AEAEAE', - fontSize: '12px', + fontSize: props.fontSize || '12px', + } + }, balance + ' '), + h(tagName, { + style: { + color: props.labelColor || '#AEAEAE', + fontSize: props.fontSize || '12px', }, }, label), ]) -- cgit From 2a204624f54da65613e57c24d047af6b17014ec7 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Jul 2016 12:27:34 -0700 Subject: Implement tx confirmation style refinements --- ui/app/components/pending-tx-details.js | 46 ++++++++++++++++++++++++++------- ui/app/components/pending-tx.js | 8 ------ ui/app/conf-tx.js | 4 +-- ui/app/css/index.css | 4 --- 4 files changed, 38 insertions(+), 24 deletions(-) (limited to 'ui') diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js index 9c3272faa..e0b629e89 100644 --- a/ui/app/components/pending-tx-details.js +++ b/ui/app/components/pending-tx-details.js @@ -3,6 +3,7 @@ const h = require('react-hyperscript') const inherits = require('util').inherits const MiniAccountPanel = require('./mini-account-panel') +const EtherBalance = require('./eth-balance') const addressSummary = require('../util').addressSummary const formatBalance = require('../util').formatBalance const nameForAddress = require('../../lib/contract-namer') @@ -52,7 +53,7 @@ PTXP.render = function () { }, [ h('span.font-small', { style: { - fontFamily: 'Montserrat Light, Montserrat, sans-serif', + fontFamily: 'Montserrat Bold, Montserrat, sans-serif', }, }, identity.name), h('span.font-small', { @@ -65,7 +66,10 @@ PTXP.render = function () { style: { fontFamily: 'Montserrat Light, Montserrat, sans-serif', }, - }, formatBalance(balance)), + }, h(EtherBalance, { + value: balance, + inline: true, + })), ]), @@ -73,7 +77,7 @@ PTXP.render = function () { src: 'images/forward-carrat.svg', style: { padding: '5px 6px 0px 10px', - height: '48px', + height: '37px', }, }), @@ -82,7 +86,8 @@ PTXP.render = function () { h('style', ` .table-box { - margin: 7px 6px 0px 6px; + margin: 7px 0px 0px 0px; + width: 100%; } .table-box .row { margin: 0px; @@ -91,7 +96,10 @@ PTXP.render = function () { justify-content: space-between; font-family: Montserrat Light, sans-serif; font-size: 13px; - padding: 5px 15px; + padding: 5px 25px; + } + .table-box .row .value { + font-family: Montserrat Regular; } `), @@ -110,11 +118,24 @@ PTXP.render = function () { h('.cell.row', { style: { fontFamily: 'Montserrat Regular', - background: 'rgb(216,216,216)', + background: 'white', + padding: '10px 25px', }, }, [ h('.cell.label', 'Max Total'), - h('.cell.value', formatBalance(maxCost)), + h('.cell.value', { + style: { + display: 'flex', + alignItems: 'center', + }, + }, [ + h(EtherBalance, { + value: maxCost, + inline: true, + labelColor: 'black', + fontSize: '16px', + }), + ]), ]), h('.cell.row', { @@ -124,7 +145,12 @@ PTXP.render = function () { }, }, [ h('.cell.label'), - h('.cell.value', `Data included: ${dataLength} bytes`), + h('.cell.value', { + style: { + fontFamily: 'Montserrat Light', + fontSize: '11px', + }, + }, `Data included: ${dataLength} bytes`), ]), ]), // End of Table @@ -149,7 +175,7 @@ PTXP.miniAccountPanelForRecipient = function () { }, [ h('span.font-small', { style: { - fontFamily: 'Montserrat Light, Montserrat, sans-serif', + fontFamily: 'Montserrat Bold, Montserrat, sans-serif', }, }, nameForAddress(txParams.to)), h('span.font-small', { @@ -167,7 +193,7 @@ PTXP.miniAccountPanelForRecipient = function () { h('span.font-small', { style: { - fontFamily: 'Montserrat Light, Montserrat, sans-serif', + fontFamily: 'Montserrat Bold, Montserrat, sans-serif', }, }, 'New Contract'), diff --git a/ui/app/components/pending-tx.js b/ui/app/components/pending-tx.js index 77dba87ee..8067aef05 100644 --- a/ui/app/components/pending-tx.js +++ b/ui/app/components/pending-tx.js @@ -21,14 +21,6 @@ PendingTx.prototype.render = function () { key: txData.id, }, [ - // header - h('h3', { - style: { - fontWeight: 'bold', - textAlign: 'center', - }, - }, 'Submit Transaction'), - // tx info h(PendingTxDetails, state), diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js index 8455826b8..db876dd9b 100644 --- a/ui/app/conf-tx.js +++ b/ui/app/conf-tx.js @@ -39,14 +39,14 @@ ConfirmTxScreen.prototype.render = function () { return ( - h('.unconftx-section.flex-column.flex-grow', [ + h('.flex-column.flex-grow', [ // subtitle and nav h('.section-title.flex-row.flex-center', [ h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', { onClick: this.goHome.bind(this), }), - h('h2.page-subtitle', 'Confirmation'), + h('h2.page-subtitle', 'Confirm Transaction'), ]), h('h3', { diff --git a/ui/app/css/index.css b/ui/app/css/index.css index de694f873..3f52b6ed4 100644 --- a/ui/app/css/index.css +++ b/ui/app/css/index.css @@ -411,10 +411,6 @@ input.large-input { } /* tx confirm */ -.unconftx-section { - margin: 0 20px; -} - .unconftx-section input[type=password] { height: 22px; padding: 2px; -- cgit From 468c1ffa427321c95c6e8acbf16b5f4fdb462495 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Jul 2016 12:39:40 -0700 Subject: Refined tx confirmation button styles --- ui/app/components/pending-tx.js | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) (limited to 'ui') diff --git a/ui/app/components/pending-tx.js b/ui/app/components/pending-tx.js index 8067aef05..dce933df1 100644 --- a/ui/app/components/pending-tx.js +++ b/ui/app/components/pending-tx.js @@ -24,18 +24,30 @@ PendingTx.prototype.render = function () { // tx info h(PendingTxDetails, state), + h('style', ` + .conf-buttons button { + margin-left: 10px; + text-transform: uppercase; + } + `), + // send + cancel - h('.flex-row.flex-space-around', { + h('.flex-row.flex-space-around.conf-buttons', { style: { - marginTop: '14px', + display: 'flex', + justifyContent: 'flex-end', + margin: '14px 25px', }, }, [ - h('button', { - onClick: state.cancelTransaction, - }, 'Reject'), - h('button', { + h('button.confirm', { onClick: state.sendTransaction, - }, 'Approve'), + style: { background: 'rgb(251,117,1)' }, + }, 'Confirm'), + + h('button.cancel', { + onClick: state.cancelTransaction, + style: { background: 'rgb(254,35,17)' }, + }, 'Cancel'), ]), ]) ) -- cgit From 306035f57508cafd07c27407896a5ac32dc1b435 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Jul 2016 12:41:07 -0700 Subject: Linted --- ui/app/components/eth-balance.js | 2 +- ui/app/components/mini-account-panel.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'ui') diff --git a/ui/app/components/eth-balance.js b/ui/app/components/eth-balance.js index d50ac771d..79fc1d256 100644 --- a/ui/app/components/eth-balance.js +++ b/ui/app/components/eth-balance.js @@ -52,7 +52,7 @@ EthBalanceComponent.prototype.renderBalance = function (value) { h(tagName, { style: { fontSize: props.fontSize || '12px', - } + }, }, balance + ' '), h(tagName, { style: { diff --git a/ui/app/components/mini-account-panel.js b/ui/app/components/mini-account-panel.js index a8127acd7..c09cf5b7a 100644 --- a/ui/app/components/mini-account-panel.js +++ b/ui/app/components/mini-account-panel.js @@ -14,7 +14,7 @@ function AccountPanel () { AccountPanel.prototype.render = function () { var props = this.props var picOrder = props.picOrder || 'left' - const { attrs, imageSeed } = props + const { imageSeed } = props return ( -- cgit From e8efe84320ea791535b40e69a64525f7fdb3ea8a Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Jul 2016 13:21:45 -0700 Subject: Add nickname rendering for recipient address --- ui/app/components/pending-tx-details.js | 2 +- ui/lib/contract-namer.js | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) (limited to 'ui') diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js index e0b629e89..b2c16e772 100644 --- a/ui/app/components/pending-tx-details.js +++ b/ui/app/components/pending-tx-details.js @@ -177,7 +177,7 @@ PTXP.miniAccountPanelForRecipient = function () { style: { fontFamily: 'Montserrat Bold, Montserrat, sans-serif', }, - }, nameForAddress(txParams.to)), + }, nameForAddress(txParams.to, props.identities)), h('span.font-small', { style: { fontFamily: 'Montserrat Light, Montserrat, sans-serif', diff --git a/ui/lib/contract-namer.js b/ui/lib/contract-namer.js index eae066ad5..62c7933e8 100644 --- a/ui/lib/contract-namer.js +++ b/ui/lib/contract-namer.js @@ -5,13 +5,27 @@ * otherwise returns null. */ +// Nickname keys must be stored in lower case. const nicknames = {} -module.exports = function(address) { +module.exports = function(addr, identities = {}) { - if (address in nicknames) { - return nicknames[address] + const address = addr.toLowerCase() + const ids = hashFromIdentities(identities) + + console.dir({ addr, ids }) + return addrFromHash(address, ids) || addrFromHash(address, nicknames) +} + +function hashFromIdentities(identities) { + const result = {} + for (let key in identities) { + result[key] = identities[key].name } + return result +} - return null +function addrFromHash(addr, hash) { + const address = addr.toLowerCase() + return hash[address] || null } -- cgit From 3cd502a1639187230ef7dedd798dee6639d68143 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Jul 2016 13:22:38 -0700 Subject: Restore button naming consistency --- ui/app/components/pending-tx.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'ui') diff --git a/ui/app/components/pending-tx.js b/ui/app/components/pending-tx.js index dce933df1..1feedbbbc 100644 --- a/ui/app/components/pending-tx.js +++ b/ui/app/components/pending-tx.js @@ -42,12 +42,12 @@ PendingTx.prototype.render = function () { h('button.confirm', { onClick: state.sendTransaction, style: { background: 'rgb(251,117,1)' }, - }, 'Confirm'), + }, 'Accept'), h('button.cancel', { onClick: state.cancelTransaction, style: { background: 'rgb(254,35,17)' }, - }, 'Cancel'), + }, 'Reject'), ]), ]) ) -- cgit From f71956b2ab748af8d9a11053a00c858370f340f3 Mon Sep 17 00:00:00 2001 From: kumavis Date: Thu, 7 Jul 2016 13:45:06 -0700 Subject: lint fix --- ui/app/components/eth-balance.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'ui') diff --git a/ui/app/components/eth-balance.js b/ui/app/components/eth-balance.js index 975ba3acd..9e445fe3f 100644 --- a/ui/app/components/eth-balance.js +++ b/ui/app/components/eth-balance.js @@ -73,7 +73,7 @@ EthBalanceComponent.prototype.renderBalance = function (value) { }, }, label), ]), - ]) + ]), ]) ) -- cgit From 33150cc721432e2464874079deac6974b38cf789 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Jul 2016 15:22:10 -0700 Subject: Fixed eth_balance style, completing partial merge --- ui/app/components/eth-balance.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'ui') diff --git a/ui/app/components/eth-balance.js b/ui/app/components/eth-balance.js index 9e445fe3f..301674083 100644 --- a/ui/app/components/eth-balance.js +++ b/ui/app/components/eth-balance.js @@ -64,17 +64,8 @@ EthBalanceComponent.prototype.renderBalance = function (value) { color: props.labelColor || '#AEAEAE', fontSize: props.fontSize || '12px', }, - }, [ - h('div', balance), - h('div', { - style: { - color: '#AEAEAE', - fontSize: '12px', - }, - }, label), - ]), + }, label), ]), ]) - ) } -- cgit