2nd iteration: remove scss and more unused components
This commit is contained in:
parent
36b863438f
commit
0b6be5f7fd
21
gulpfile.js
21
gulpfile.js
|
@ -214,21 +214,6 @@ gulp.task('dev:copy',
|
|||
)
|
||||
)
|
||||
|
||||
// scss compilation and autoprefixing tasks
|
||||
|
||||
gulp.task('build:scss', createScssBuildTask({
|
||||
src: 'ui/app/css/index.scss',
|
||||
dest: 'ui/app/css/output',
|
||||
devMode: false,
|
||||
}))
|
||||
|
||||
gulp.task('dev:scss', createScssBuildTask({
|
||||
src: 'ui/app/css/index.scss',
|
||||
dest: 'ui/app/css/output',
|
||||
devMode: true,
|
||||
pattern: 'ui/app/**/*.scss',
|
||||
}))
|
||||
|
||||
function createScssBuildTask ({ src, dest, devMode, pattern }) {
|
||||
return function () {
|
||||
if (devMode) {
|
||||
|
@ -371,7 +356,6 @@ gulp.task('zip', gulp.parallel('zip:chrome', 'zip:firefox', 'zip:edge', 'zip:ope
|
|||
gulp.task('dev',
|
||||
gulp.series(
|
||||
'clean',
|
||||
'dev:scss',
|
||||
gulp.parallel(
|
||||
'dev:extension:js',
|
||||
'dev:mascara:js',
|
||||
|
@ -384,7 +368,6 @@ gulp.task('dev',
|
|||
gulp.task('dev:extension',
|
||||
gulp.series(
|
||||
'clean',
|
||||
'dev:scss',
|
||||
gulp.parallel(
|
||||
'dev:extension:js',
|
||||
'dev:copy',
|
||||
|
@ -396,7 +379,6 @@ gulp.task('dev:extension',
|
|||
gulp.task('dev:mascara',
|
||||
gulp.series(
|
||||
'clean',
|
||||
'dev:scss',
|
||||
gulp.parallel(
|
||||
'dev:mascara:js',
|
||||
'dev:copy',
|
||||
|
@ -408,7 +390,6 @@ gulp.task('dev:mascara',
|
|||
gulp.task('build',
|
||||
gulp.series(
|
||||
'clean',
|
||||
'build:scss',
|
||||
gulpParallel(
|
||||
'build:extension:js',
|
||||
'build:mascara:js',
|
||||
|
@ -420,7 +401,6 @@ gulp.task('build',
|
|||
gulp.task('build:extension',
|
||||
gulp.series(
|
||||
'clean',
|
||||
'build:scss',
|
||||
gulp.parallel(
|
||||
'build:extension:js',
|
||||
'copy'
|
||||
|
@ -431,7 +411,6 @@ gulp.task('build:extension',
|
|||
gulp.task('build:mascara',
|
||||
gulp.series(
|
||||
'clean',
|
||||
'build:scss',
|
||||
gulp.parallel(
|
||||
'build:mascara:js',
|
||||
'copy'
|
||||
|
|
|
@ -1,324 +0,0 @@
|
|||
const Component = require('react').Component
|
||||
const PropTypes = require('prop-types')
|
||||
const h = require('react-hyperscript')
|
||||
const actions = require('../actions')
|
||||
const ethNetProps = require('eth-net-props')
|
||||
const connect = require('react-redux').connect
|
||||
const Dropdown = require('./dropdown').Dropdown
|
||||
const DropdownMenuItem = require('./dropdown').DropdownMenuItem
|
||||
const Identicon = require('./identicon')
|
||||
const copyToClipboard = require('copy-to-clipboard')
|
||||
const { checksumAddress } = require('../util')
|
||||
|
||||
class AccountDropdowns extends Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
accountSelectorActive: false,
|
||||
optionsMenuActive: false,
|
||||
}
|
||||
this.accountSelectorToggleClassName = 'accounts-selector'
|
||||
this.optionsMenuToggleClassName = 'fa-ellipsis-h'
|
||||
}
|
||||
|
||||
renderAccounts () {
|
||||
const { identities, selected, keyrings } = this.props
|
||||
|
||||
return Object.keys(identities).map((key, index) => {
|
||||
const identity = identities[key]
|
||||
const isSelected = identity.address === selected
|
||||
|
||||
const simpleAddress = identity.address.substring(2).toLowerCase()
|
||||
|
||||
const keyring = keyrings.find((kr) => {
|
||||
return kr.accounts.includes(simpleAddress) ||
|
||||
kr.accounts.includes(identity.address)
|
||||
})
|
||||
|
||||
return h(
|
||||
DropdownMenuItem,
|
||||
{
|
||||
closeMenu: () => {},
|
||||
onClick: () => {
|
||||
this.props.actions.showAccountDetail(identity.address)
|
||||
},
|
||||
style: {
|
||||
marginTop: index === 0 ? '5px' : '',
|
||||
fontSize: '24px',
|
||||
},
|
||||
},
|
||||
[
|
||||
h(
|
||||
Identicon,
|
||||
{
|
||||
address: identity.address,
|
||||
diameter: 32,
|
||||
style: {
|
||||
marginLeft: '10px',
|
||||
},
|
||||
},
|
||||
),
|
||||
this.indicateIfLoose(keyring),
|
||||
h('span', {
|
||||
style: {
|
||||
marginLeft: '20px',
|
||||
fontSize: '24px',
|
||||
maxWidth: '145px',
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
},
|
||||
}, identity.name || ''),
|
||||
h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, isSelected ? h('.check', '✓') : null),
|
||||
]
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
indicateIfLoose (keyring) {
|
||||
try { // Sometimes keyrings aren't loaded yet:
|
||||
const type = keyring.type
|
||||
const isLoose = type !== 'HD Key Tree'
|
||||
return isLoose ? h('.keyring-label.allcaps', this.context.t('loose')) : null
|
||||
} catch (e) { return }
|
||||
}
|
||||
|
||||
renderAccountSelector () {
|
||||
const { actions } = this.props
|
||||
const { accountSelectorActive } = this.state
|
||||
|
||||
return h(
|
||||
Dropdown,
|
||||
{
|
||||
useCssTransition: true, // Hardcoded because account selector is temporarily in app-header
|
||||
style: {
|
||||
marginLeft: '-238px',
|
||||
marginTop: '38px',
|
||||
minWidth: '180px',
|
||||
overflowY: 'auto',
|
||||
maxHeight: '300px',
|
||||
width: '300px',
|
||||
},
|
||||
innerStyle: {
|
||||
padding: '8px 25px',
|
||||
},
|
||||
isOpen: accountSelectorActive,
|
||||
onClickOutside: (event) => {
|
||||
const { classList } = event.target
|
||||
const isNotToggleElement = !classList.contains(this.accountSelectorToggleClassName)
|
||||
if (accountSelectorActive && isNotToggleElement) {
|
||||
this.setState({ accountSelectorActive: false })
|
||||
}
|
||||
},
|
||||
},
|
||||
[
|
||||
...this.renderAccounts(),
|
||||
h(
|
||||
DropdownMenuItem,
|
||||
{
|
||||
closeMenu: () => {},
|
||||
onClick: () => actions.addNewAccount(),
|
||||
},
|
||||
[
|
||||
h(
|
||||
Identicon,
|
||||
{
|
||||
style: {
|
||||
marginLeft: '10px',
|
||||
},
|
||||
diameter: 32,
|
||||
},
|
||||
),
|
||||
h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, this.context.t('createAccount')),
|
||||
],
|
||||
),
|
||||
h(
|
||||
DropdownMenuItem,
|
||||
{
|
||||
closeMenu: () => {},
|
||||
onClick: () => actions.showImportPage(),
|
||||
},
|
||||
[
|
||||
h(
|
||||
Identicon,
|
||||
{
|
||||
style: {
|
||||
marginLeft: '10px',
|
||||
},
|
||||
diameter: 32,
|
||||
},
|
||||
),
|
||||
h('span', {
|
||||
style: {
|
||||
marginLeft: '20px',
|
||||
fontSize: '24px',
|
||||
marginBottom: '5px',
|
||||
},
|
||||
}, this.context.t('importAccount')),
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
renderAccountOptions () {
|
||||
const { actions } = this.props
|
||||
const { optionsMenuActive } = this.state
|
||||
|
||||
return h(
|
||||
Dropdown,
|
||||
{
|
||||
style: {
|
||||
marginLeft: '-215px',
|
||||
minWidth: '180px',
|
||||
},
|
||||
isOpen: optionsMenuActive,
|
||||
onClickOutside: (event) => {
|
||||
const { classList } = event.target
|
||||
const isNotToggleElement = !classList.contains(this.optionsMenuToggleClassName)
|
||||
if (optionsMenuActive && isNotToggleElement) {
|
||||
this.setState({ optionsMenuActive: false })
|
||||
}
|
||||
},
|
||||
},
|
||||
[
|
||||
h(
|
||||
DropdownMenuItem,
|
||||
{
|
||||
closeMenu: () => {},
|
||||
onClick: () => {
|
||||
const { selected, network } = this.props
|
||||
const url = ethNetProps.explorerLinks.getExplorerAccountLinkFor(selected, network)
|
||||
global.platform.openWindow({ url })
|
||||
},
|
||||
},
|
||||
this.context.t('etherscanView'),
|
||||
),
|
||||
h(
|
||||
DropdownMenuItem,
|
||||
{
|
||||
closeMenu: () => {},
|
||||
onClick: () => {
|
||||
const { selected, identities } = this.props
|
||||
var identity = identities[selected]
|
||||
actions.showQrView(selected, identity ? identity.name : '')
|
||||
},
|
||||
},
|
||||
this.context.t('showQRCode'),
|
||||
),
|
||||
h(
|
||||
DropdownMenuItem,
|
||||
{
|
||||
closeMenu: () => {},
|
||||
onClick: () => {
|
||||
const { selected } = this.props
|
||||
copyToClipboard(checksumAddress(selected))
|
||||
},
|
||||
},
|
||||
this.context.t('copyAddress'),
|
||||
),
|
||||
h(
|
||||
DropdownMenuItem,
|
||||
{
|
||||
closeMenu: () => {},
|
||||
onClick: () => {
|
||||
actions.requestAccountExport()
|
||||
},
|
||||
},
|
||||
this.context.t('exportPrivateKey'),
|
||||
),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const { style, enableAccountsSelector, enableAccountOptions } = this.props
|
||||
const { optionsMenuActive, accountSelectorActive } = this.state
|
||||
|
||||
return h(
|
||||
'span',
|
||||
{
|
||||
style: style,
|
||||
},
|
||||
[
|
||||
enableAccountsSelector && h(
|
||||
// 'i.fa.fa-angle-down',
|
||||
'div.cursor-pointer.color-orange.accounts-selector',
|
||||
{
|
||||
style: {
|
||||
// fontSize: '1.8em',
|
||||
background: 'url(images/switch_acc.svg) white center center no-repeat',
|
||||
height: '25px',
|
||||
width: '25px',
|
||||
transform: 'scale(0.75)',
|
||||
marginRight: '3px',
|
||||
},
|
||||
onClick: (event) => {
|
||||
event.stopPropagation()
|
||||
this.setState({
|
||||
accountSelectorActive: !accountSelectorActive,
|
||||
optionsMenuActive: false,
|
||||
})
|
||||
},
|
||||
},
|
||||
this.renderAccountSelector(),
|
||||
),
|
||||
enableAccountOptions && h(
|
||||
'i.fa.fa-ellipsis-h',
|
||||
{
|
||||
style: {
|
||||
marginRight: '0.5em',
|
||||
fontSize: '1.8em',
|
||||
},
|
||||
onClick: (event) => {
|
||||
event.stopPropagation()
|
||||
this.setState({
|
||||
accountSelectorActive: false,
|
||||
optionsMenuActive: !optionsMenuActive,
|
||||
})
|
||||
},
|
||||
},
|
||||
this.renderAccountOptions()
|
||||
),
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
AccountDropdowns.defaultProps = {
|
||||
enableAccountsSelector: false,
|
||||
enableAccountOptions: false,
|
||||
}
|
||||
|
||||
AccountDropdowns.propTypes = {
|
||||
identities: PropTypes.objectOf(PropTypes.object),
|
||||
selected: PropTypes.string,
|
||||
keyrings: PropTypes.array,
|
||||
actions: PropTypes.objectOf(PropTypes.func),
|
||||
network: PropTypes.string,
|
||||
style: PropTypes.object,
|
||||
enableAccountOptions: PropTypes.bool,
|
||||
enableAccountsSelector: PropTypes.bool,
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
actions: {
|
||||
showConfigPage: () => dispatch(actions.showConfigPage()),
|
||||
requestAccountExport: () => dispatch(actions.requestExportAccount()),
|
||||
showAccountDetail: (address) => dispatch(actions.showAccountDetail(address)),
|
||||
addNewAccount: () => dispatch(actions.addNewAccount()),
|
||||
showImportPage: () => dispatch(actions.showImportPage()),
|
||||
showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
AccountDropdowns.contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
AccountDropdowns: connect(null, mapDispatchToProps)(AccountDropdowns),
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
const Component = require('react').Component
|
||||
const h = require('react-hyperscript')
|
||||
const PropTypes = require('prop-types')
|
||||
const inherits = require('util').inherits
|
||||
const exportAsFile = require('../util').exportAsFile
|
||||
const copyToClipboard = require('copy-to-clipboard')
|
||||
const actions = require('../actions')
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
const connect = require('react-redux').connect
|
||||
|
||||
ExportAccountView.contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps)(ExportAccountView)
|
||||
|
||||
|
||||
inherits(ExportAccountView, Component)
|
||||
function ExportAccountView () {
|
||||
Component.call(this)
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
return {
|
||||
warning: state.appState.warning,
|
||||
}
|
||||
}
|
||||
|
||||
ExportAccountView.prototype.render = function () {
|
||||
const state = this.props
|
||||
const accountDetail = state.accountDetail
|
||||
const nickname = state.identities[state.address].name
|
||||
|
||||
if (!accountDetail) return h('div')
|
||||
const accountExport = accountDetail.accountExport
|
||||
|
||||
const notExporting = accountExport === 'none'
|
||||
const exportRequested = accountExport === 'requested'
|
||||
const accountExported = accountExport === 'completed'
|
||||
|
||||
if (notExporting) return h('div')
|
||||
|
||||
if (exportRequested) {
|
||||
const warning = this.context.t('exportPrivateKeyWarning')
|
||||
return (
|
||||
h('div', {
|
||||
style: {
|
||||
display: 'inline-block',
|
||||
textAlign: 'center',
|
||||
},
|
||||
},
|
||||
[
|
||||
h('div', {
|
||||
key: 'exporting',
|
||||
style: {
|
||||
margin: '0 20px',
|
||||
},
|
||||
}, [
|
||||
h('p.error', warning),
|
||||
h('input#exportAccount.sizing-input', {
|
||||
type: 'password',
|
||||
placeholder: this.context.t('confirmPassword').toLowerCase(),
|
||||
onKeyPress: this.onExportKeyPress.bind(this),
|
||||
style: {
|
||||
position: 'relative',
|
||||
top: '1.5px',
|
||||
marginBottom: '7px',
|
||||
},
|
||||
}),
|
||||
]),
|
||||
h('div', {
|
||||
key: 'buttons',
|
||||
style: {
|
||||
margin: '0 20px',
|
||||
},
|
||||
},
|
||||
[
|
||||
h('button', {
|
||||
onClick: () => this.onExportKeyPress({ key: 'Enter', preventDefault: () => {} }),
|
||||
style: {
|
||||
marginRight: '10px',
|
||||
},
|
||||
}, this.context.t('submit')),
|
||||
h('button', {
|
||||
onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)),
|
||||
}, this.context.t('cancel')),
|
||||
]),
|
||||
(this.props.warning) && (
|
||||
h('span.error', {
|
||||
style: {
|
||||
margin: '20px',
|
||||
},
|
||||
}, this.props.warning.split('-'))
|
||||
),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
if (accountExported) {
|
||||
const plainKey = ethUtil.stripHexPrefix(accountDetail.privateKey)
|
||||
|
||||
return h('div.privateKey', {
|
||||
style: {
|
||||
margin: '0 20px',
|
||||
},
|
||||
}, [
|
||||
h('label', this.context.t('copyPrivateKey') + ':'),
|
||||
h('p.error.cursor-pointer', {
|
||||
style: {
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
webkitUserSelect: 'text',
|
||||
maxWidth: '275px',
|
||||
},
|
||||
onClick: function (event) {
|
||||
copyToClipboard(ethUtil.stripHexPrefix(accountDetail.privateKey))
|
||||
},
|
||||
}, plainKey),
|
||||
h('button', {
|
||||
onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)),
|
||||
}, this.context.t('done')),
|
||||
h('button', {
|
||||
style: {
|
||||
marginLeft: '10px',
|
||||
},
|
||||
onClick: () => exportAsFile(`Nifty Wallet ${nickname} Private Key`, plainKey),
|
||||
}, this.context.t('saveAsFile')),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
ExportAccountView.prototype.onExportKeyPress = function (event) {
|
||||
if (event.key !== 'Enter') return
|
||||
event.preventDefault()
|
||||
|
||||
const input = document.getElementById('exportAccount').value
|
||||
this.props.dispatch(actions.exportAccount(input, this.props.address))
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
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 addressSummary = require('../util').addressSummary
|
||||
|
||||
module.exports = AccountPanel
|
||||
|
||||
|
||||
inherits(AccountPanel, Component)
|
||||
function AccountPanel () {
|
||||
Component.call(this)
|
||||
}
|
||||
|
||||
AccountPanel.prototype.render = function () {
|
||||
var state = this.props
|
||||
var identity = state.identity || {}
|
||||
var account = state.account || {}
|
||||
var isFauceting = state.isFauceting
|
||||
|
||||
var panelState = {
|
||||
key: `accountPanel${identity.address}`,
|
||||
identiconKey: identity.address,
|
||||
identiconLabel: identity.name || '',
|
||||
attributes: [
|
||||
{
|
||||
key: 'ADDRESS',
|
||||
value: addressSummary(identity.address),
|
||||
},
|
||||
balanceOrFaucetingIndication(account, isFauceting),
|
||||
],
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
h('.identity-panel.flex-row.flex-space-between', {
|
||||
style: {
|
||||
flex: '1 0 auto',
|
||||
cursor: panelState.onClick ? 'pointer' : undefined,
|
||||
},
|
||||
onClick: panelState.onClick,
|
||||
}, [
|
||||
|
||||
// account identicon
|
||||
h('.identicon-wrapper.flex-column.select-none', [
|
||||
h(Identicon, {
|
||||
address: panelState.identiconKey,
|
||||
imageify: state.imageifyIdenticons,
|
||||
}),
|
||||
h('span.font-small', panelState.identiconLabel.substring(0, 7) + '...'),
|
||||
]),
|
||||
|
||||
// account address, balance
|
||||
h('.identity-data.flex-column.flex-justify-center.flex-grow.select-none', [
|
||||
|
||||
panelState.attributes.map((attr) => {
|
||||
return h('.flex-row.flex-space-between', {
|
||||
key: '' + Math.round(Math.random() * 1000000),
|
||||
}, [
|
||||
h('label.font-small.no-select', attr.key),
|
||||
h('span.font-small', attr.value),
|
||||
])
|
||||
}),
|
||||
]),
|
||||
|
||||
])
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
const Component = require('react').Component
|
||||
const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
const InputNumber = require('../input-number.js')
|
||||
// const GasSlider = require('./gas-slider.js')
|
||||
|
||||
module.exports = GasModalCard
|
||||
|
||||
inherits(GasModalCard, Component)
|
||||
function GasModalCard () {
|
||||
Component.call(this)
|
||||
}
|
||||
|
||||
GasModalCard.prototype.render = function () {
|
||||
const {
|
||||
// memo,
|
||||
onChange,
|
||||
unitLabel,
|
||||
value,
|
||||
min,
|
||||
// max,
|
||||
step,
|
||||
title,
|
||||
copy,
|
||||
} = this.props
|
||||
|
||||
return h('div.send-v2__gas-modal-card', [
|
||||
|
||||
h('div.send-v2__gas-modal-card__title', {}, title),
|
||||
|
||||
h('div.send-v2__gas-modal-card__copy', {}, copy),
|
||||
|
||||
h(InputNumber, {
|
||||
unitLabel,
|
||||
step,
|
||||
// max,
|
||||
min,
|
||||
placeholder: '0',
|
||||
value,
|
||||
onChange,
|
||||
}),
|
||||
|
||||
// h(GasSlider, {
|
||||
// value,
|
||||
// step,
|
||||
// max,
|
||||
// min,
|
||||
// onChange,
|
||||
// }),
|
||||
|
||||
])
|
||||
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
// const Component = require('react').Component
|
||||
// const h = require('react-hyperscript')
|
||||
// const inherits = require('util').inherits
|
||||
|
||||
// module.exports = GasSlider
|
||||
|
||||
// inherits(GasSlider, Component)
|
||||
// function GasSlider () {
|
||||
// Component.call(this)
|
||||
// }
|
||||
|
||||
// GasSlider.prototype.render = function () {
|
||||
// const {
|
||||
// memo,
|
||||
// identities,
|
||||
// onChange,
|
||||
// unitLabel,
|
||||
// value,
|
||||
// id,
|
||||
// step,
|
||||
// max,
|
||||
// min,
|
||||
// } = this.props
|
||||
|
||||
// return h('div.gas-slider', [
|
||||
|
||||
// h('input.gas-slider__input', {
|
||||
// type: 'range',
|
||||
// step,
|
||||
// max,
|
||||
// min,
|
||||
// value,
|
||||
// id: 'gasSlider',
|
||||
// onChange: event => onChange(event.target.value),
|
||||
// }, []),
|
||||
|
||||
// h('div.gas-slider__bar', [
|
||||
|
||||
// h('div.gas-slider__low'),
|
||||
|
||||
// h('div.gas-slider__mid'),
|
||||
|
||||
// h('div.gas-slider__high'),
|
||||
|
||||
// ]),
|
||||
|
||||
// ])
|
||||
|
||||
// }
|
||||
|
|
@ -1,374 +0,0 @@
|
|||
const Component = require('react').Component
|
||||
const PropTypes = require('prop-types')
|
||||
const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('../../actions')
|
||||
const GasModalCard = require('./gas-modal-card')
|
||||
import Button from '../button'
|
||||
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
|
||||
import {
|
||||
updateSendErrors,
|
||||
} from '../../ducks/send.duck'
|
||||
|
||||
const {
|
||||
MIN_GAS_PRICE_DEC,
|
||||
MIN_GAS_LIMIT_DEC,
|
||||
MIN_GAS_PRICE_GWEI,
|
||||
} = require('../send/send.constants')
|
||||
|
||||
const {
|
||||
isBalanceSufficient,
|
||||
} = require('../send/send.utils')
|
||||
|
||||
const {
|
||||
conversionUtil,
|
||||
multiplyCurrencies,
|
||||
conversionGreaterThan,
|
||||
conversionMax,
|
||||
subtractCurrencies,
|
||||
} = require('../../conversion-util')
|
||||
|
||||
const {
|
||||
getGasIsLoading,
|
||||
getForceGasMin,
|
||||
conversionRateSelector,
|
||||
getSendAmount,
|
||||
getSelectedToken,
|
||||
getSendFrom,
|
||||
getCurrentAccountWithSendEtherInfo,
|
||||
getSelectedTokenToFiatRate,
|
||||
getSendMaxModeState,
|
||||
} = require('../../selectors')
|
||||
|
||||
const {
|
||||
getGasPrice,
|
||||
getGasLimit,
|
||||
} = require('../send/send.selectors')
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const selectedToken = getSelectedToken(state)
|
||||
const currentAccount = getSendFrom(state) || getCurrentAccountWithSendEtherInfo(state)
|
||||
const conversionRate = conversionRateSelector(state)
|
||||
|
||||
return {
|
||||
gasPrice: getGasPrice(state),
|
||||
gasLimit: getGasLimit(state),
|
||||
gasIsLoading: getGasIsLoading(state),
|
||||
forceGasMin: getForceGasMin(state),
|
||||
conversionRate,
|
||||
amount: getSendAmount(state),
|
||||
maxModeOn: getSendMaxModeState(state),
|
||||
balance: currentAccount.balance,
|
||||
primaryCurrency: selectedToken && selectedToken.symbol,
|
||||
selectedToken,
|
||||
amountConversionRate: selectedToken ? getSelectedTokenToFiatRate(state) : conversionRate,
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return {
|
||||
hideModal: () => dispatch(actions.hideModal()),
|
||||
setGasPrice: newGasPrice => dispatch(actions.setGasPrice(newGasPrice)),
|
||||
setGasLimit: newGasLimit => dispatch(actions.setGasLimit(newGasLimit)),
|
||||
setGasTotal: newGasTotal => dispatch(actions.setGasTotal(newGasTotal)),
|
||||
updateSendAmount: newAmount => dispatch(actions.updateSendAmount(newAmount)),
|
||||
updateSendErrors: error => dispatch(updateSendErrors(error)),
|
||||
}
|
||||
}
|
||||
|
||||
function getFreshState (props) {
|
||||
const gasPrice = props.gasPrice || MIN_GAS_PRICE_DEC
|
||||
const gasLimit = props.gasLimit || MIN_GAS_LIMIT_DEC
|
||||
|
||||
const gasTotal = multiplyCurrencies(gasLimit, gasPrice, {
|
||||
toNumericBase: 'hex',
|
||||
multiplicandBase: 16,
|
||||
multiplierBase: 16,
|
||||
})
|
||||
|
||||
return {
|
||||
gasPrice,
|
||||
gasLimit,
|
||||
gasTotal,
|
||||
error: null,
|
||||
priceSigZeros: '',
|
||||
priceSigDec: '',
|
||||
}
|
||||
}
|
||||
|
||||
inherits(CustomizeGasModal, Component)
|
||||
function CustomizeGasModal (props) {
|
||||
Component.call(this)
|
||||
|
||||
const originalState = getFreshState(props)
|
||||
this.state = {
|
||||
...originalState,
|
||||
originalState,
|
||||
}
|
||||
}
|
||||
|
||||
CustomizeGasModal.contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(CustomizeGasModal)
|
||||
|
||||
CustomizeGasModal.prototype.componentWillReceiveProps = function (nextProps) {
|
||||
const currentState = getFreshState(this.props)
|
||||
const {
|
||||
gasPrice: currentGasPrice,
|
||||
gasLimit: currentGasLimit,
|
||||
} = currentState
|
||||
const newState = getFreshState(nextProps)
|
||||
const {
|
||||
gasPrice: newGasPrice,
|
||||
gasLimit: newGasLimit,
|
||||
gasTotal: newGasTotal,
|
||||
} = newState
|
||||
const gasPriceChanged = currentGasPrice !== newGasPrice
|
||||
const gasLimitChanged = currentGasLimit !== newGasLimit
|
||||
|
||||
if (gasPriceChanged) {
|
||||
this.setState({
|
||||
gasPrice: newGasPrice,
|
||||
gasTotal: newGasTotal,
|
||||
priceSigZeros: '',
|
||||
priceSigDec: '',
|
||||
})
|
||||
}
|
||||
if (gasLimitChanged) {
|
||||
this.setState({ gasLimit: newGasLimit, gasTotal: newGasTotal })
|
||||
}
|
||||
if (gasLimitChanged || gasPriceChanged) {
|
||||
this.validate({ gasLimit: newGasLimit, gasTotal: newGasTotal })
|
||||
}
|
||||
}
|
||||
|
||||
CustomizeGasModal.prototype.save = function (gasPrice, gasLimit, gasTotal) {
|
||||
const {
|
||||
setGasPrice,
|
||||
setGasLimit,
|
||||
hideModal,
|
||||
setGasTotal,
|
||||
maxModeOn,
|
||||
selectedToken,
|
||||
balance,
|
||||
updateSendAmount,
|
||||
updateSendErrors,
|
||||
} = this.props
|
||||
|
||||
if (maxModeOn && !selectedToken) {
|
||||
const maxAmount = subtractCurrencies(
|
||||
ethUtil.addHexPrefix(balance),
|
||||
ethUtil.addHexPrefix(gasTotal),
|
||||
{ toNumericBase: 'hex' }
|
||||
)
|
||||
updateSendAmount(maxAmount)
|
||||
}
|
||||
|
||||
setGasPrice(ethUtil.addHexPrefix(gasPrice))
|
||||
setGasLimit(ethUtil.addHexPrefix(gasLimit))
|
||||
setGasTotal(ethUtil.addHexPrefix(gasTotal))
|
||||
updateSendErrors({ insufficientFunds: false })
|
||||
hideModal()
|
||||
}
|
||||
|
||||
CustomizeGasModal.prototype.revert = function () {
|
||||
this.setState(this.state.originalState)
|
||||
}
|
||||
|
||||
CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
|
||||
const {
|
||||
amount,
|
||||
balance,
|
||||
selectedToken,
|
||||
amountConversionRate,
|
||||
conversionRate,
|
||||
maxModeOn,
|
||||
} = this.props
|
||||
|
||||
let error = null
|
||||
|
||||
const balanceIsSufficient = isBalanceSufficient({
|
||||
amount: selectedToken || maxModeOn ? '0' : amount,
|
||||
gasTotal,
|
||||
balance,
|
||||
selectedToken,
|
||||
amountConversionRate,
|
||||
conversionRate,
|
||||
})
|
||||
|
||||
if (!balanceIsSufficient) {
|
||||
error = this.context.t('balanceIsInsufficientGas')
|
||||
}
|
||||
|
||||
const gasLimitTooLow = gasLimit && conversionGreaterThan(
|
||||
{
|
||||
value: MIN_GAS_LIMIT_DEC,
|
||||
fromNumericBase: 'dec',
|
||||
conversionRate,
|
||||
},
|
||||
{
|
||||
value: gasLimit,
|
||||
fromNumericBase: 'hex',
|
||||
},
|
||||
)
|
||||
|
||||
if (gasLimitTooLow) {
|
||||
error = this.context.t('gasLimitTooLow')
|
||||
}
|
||||
|
||||
this.setState({ error })
|
||||
return error
|
||||
}
|
||||
|
||||
CustomizeGasModal.prototype.convertAndSetGasLimit = function (newGasLimit) {
|
||||
const { gasPrice } = this.state
|
||||
|
||||
const gasLimit = conversionUtil(newGasLimit, {
|
||||
fromNumericBase: 'dec',
|
||||
toNumericBase: 'hex',
|
||||
})
|
||||
|
||||
const gasTotal = multiplyCurrencies(gasLimit, gasPrice, {
|
||||
toNumericBase: 'hex',
|
||||
multiplicandBase: 16,
|
||||
multiplierBase: 16,
|
||||
})
|
||||
|
||||
this.validate({ gasTotal, gasLimit })
|
||||
|
||||
this.setState({ gasTotal, gasLimit })
|
||||
}
|
||||
|
||||
CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) {
|
||||
const { gasLimit } = this.state
|
||||
const sigZeros = String(newGasPrice).match(/^\d+[.]\d*?(0+)$/)
|
||||
const sigDec = String(newGasPrice).match(/^\d+([.])0*$/)
|
||||
|
||||
this.setState({
|
||||
priceSigZeros: sigZeros && sigZeros[1] || '',
|
||||
priceSigDec: sigDec && sigDec[1] || '',
|
||||
})
|
||||
|
||||
const gasPrice = conversionUtil(newGasPrice, {
|
||||
fromNumericBase: 'dec',
|
||||
toNumericBase: 'hex',
|
||||
fromDenomination: 'GWEI',
|
||||
toDenomination: 'WEI',
|
||||
})
|
||||
|
||||
const gasTotal = multiplyCurrencies(gasLimit, gasPrice, {
|
||||
toNumericBase: 'hex',
|
||||
multiplicandBase: 16,
|
||||
multiplierBase: 16,
|
||||
})
|
||||
|
||||
this.validate({ gasTotal })
|
||||
|
||||
this.setState({ gasTotal, gasPrice })
|
||||
}
|
||||
|
||||
CustomizeGasModal.prototype.render = function () {
|
||||
const { hideModal, forceGasMin, gasIsLoading } = this.props
|
||||
const { gasPrice, gasLimit, gasTotal, error, priceSigZeros, priceSigDec } = this.state
|
||||
|
||||
let convertedGasPrice = conversionUtil(gasPrice, {
|
||||
fromNumericBase: 'hex',
|
||||
toNumericBase: 'dec',
|
||||
fromDenomination: 'WEI',
|
||||
toDenomination: 'GWEI',
|
||||
})
|
||||
|
||||
convertedGasPrice += convertedGasPrice.match(/[.]/) ? priceSigZeros : `${priceSigDec}${priceSigZeros}`
|
||||
|
||||
let newGasPrice = gasPrice
|
||||
if (forceGasMin) {
|
||||
const convertedMinPrice = conversionUtil(forceGasMin, {
|
||||
fromNumericBase: 'hex',
|
||||
toNumericBase: 'dec',
|
||||
})
|
||||
convertedGasPrice = conversionMax(
|
||||
{ value: convertedMinPrice, fromNumericBase: 'dec' },
|
||||
{ value: convertedGasPrice, fromNumericBase: 'dec' }
|
||||
)
|
||||
newGasPrice = conversionMax(
|
||||
{ value: gasPrice, fromNumericBase: 'hex' },
|
||||
{ value: forceGasMin, fromNumericBase: 'hex' }
|
||||
)
|
||||
}
|
||||
|
||||
const convertedGasLimit = conversionUtil(gasLimit, {
|
||||
fromNumericBase: 'hex',
|
||||
toNumericBase: 'dec',
|
||||
})
|
||||
|
||||
return !gasIsLoading && h('div.send-v2__customize-gas', {}, [
|
||||
h('div.send-v2__customize-gas__content', {
|
||||
}, [
|
||||
h('div.send-v2__customize-gas__header', {}, [
|
||||
|
||||
h('div.send-v2__customize-gas__title', this.context.t('customGas')),
|
||||
|
||||
h('div.send-v2__customize-gas__close', {
|
||||
onClick: hideModal,
|
||||
}),
|
||||
|
||||
]),
|
||||
|
||||
h('div.send-v2__customize-gas__body', {}, [
|
||||
|
||||
h(GasModalCard, {
|
||||
value: convertedGasPrice,
|
||||
min: forceGasMin || MIN_GAS_PRICE_GWEI,
|
||||
step: 1,
|
||||
onChange: value => this.convertAndSetGasPrice(value),
|
||||
title: this.context.t('gasPrice'),
|
||||
copy: this.context.t('gasPriceCalculation'),
|
||||
gasIsLoading,
|
||||
}),
|
||||
|
||||
h(GasModalCard, {
|
||||
value: convertedGasLimit,
|
||||
min: 1,
|
||||
step: 1,
|
||||
onChange: value => this.convertAndSetGasLimit(value),
|
||||
title: this.context.t('gasLimit'),
|
||||
copy: this.context.t('gasLimitCalculation'),
|
||||
gasIsLoading,
|
||||
}),
|
||||
|
||||
]),
|
||||
|
||||
h('div.send-v2__customize-gas__footer', {}, [
|
||||
|
||||
error && h('div.send-v2__customize-gas__error-message', [
|
||||
error,
|
||||
]),
|
||||
|
||||
h('div.send-v2__customize-gas__revert', {
|
||||
onClick: () => this.revert(),
|
||||
}, [this.context.t('revert')]),
|
||||
|
||||
h('div.send-v2__customize-gas__buttons', [
|
||||
h(Button, {
|
||||
type: 'default',
|
||||
className: 'send-v2__customize-gas__cancel',
|
||||
onClick: this.props.hideModal,
|
||||
}, [this.context.t('cancel')]),
|
||||
h(Button, {
|
||||
type: 'primary',
|
||||
className: 'send-v2__customize-gas__save',
|
||||
onClick: () => !error && this.save(newGasPrice, gasLimit, gasTotal),
|
||||
disabled: error,
|
||||
}, [this.context.t('save')]),
|
||||
]),
|
||||
|
||||
]),
|
||||
|
||||
]),
|
||||
])
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
const LoadingScreen = require('./loading-screen.component')
|
||||
module.exports = LoadingScreen
|
|
@ -1,31 +0,0 @@
|
|||
const { Component } = require('react')
|
||||
const h = require('react-hyperscript')
|
||||
const PropTypes = require('prop-types')
|
||||
const Spinner = require('../spinner')
|
||||
|
||||
class LoadingScreen extends Component {
|
||||
renderMessage () {
|
||||
const { loadingMessage } = this.props
|
||||
return loadingMessage && h('span', loadingMessage)
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
h('.loading-overlay', [
|
||||
h('.loading-overlay__container', [
|
||||
h(Spinner, {
|
||||
color: '#F7C06C',
|
||||
}),
|
||||
|
||||
this.renderMessage(),
|
||||
]),
|
||||
])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LoadingScreen.propTypes = {
|
||||
loadingMessage: PropTypes.string,
|
||||
}
|
||||
|
||||
module.exports = LoadingScreen
|
|
@ -1,100 +0,0 @@
|
|||
const Component = require('react').Component
|
||||
const PropTypes = require('prop-types')
|
||||
const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('../../actions')
|
||||
const AccountModalContainer = require('./account-modal-container')
|
||||
const { getSelectedIdentity } = require('../../selectors')
|
||||
const ethNetProps = require('eth-net-props')
|
||||
const QrView = require('../qr-code')
|
||||
const EditableLabel = require('../editable-label')
|
||||
|
||||
import Button from '../button'
|
||||
|
||||
function mapStateToProps (state) {
|
||||
return {
|
||||
network: state.metamask.network,
|
||||
selectedIdentity: getSelectedIdentity(state),
|
||||
keyrings: state.metamask.keyrings,
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return {
|
||||
// Is this supposed to be used somewhere?
|
||||
showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)),
|
||||
showExportPrivateKeyModal: () => {
|
||||
dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' }))
|
||||
},
|
||||
hideModal: () => dispatch(actions.hideModal()),
|
||||
setAccountLabel: (address, label) => dispatch(actions.setAccountLabel(address, label)),
|
||||
}
|
||||
}
|
||||
|
||||
inherits(AccountDetailsModal, Component)
|
||||
function AccountDetailsModal () {
|
||||
Component.call(this)
|
||||
}
|
||||
|
||||
AccountDetailsModal.contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountDetailsModal)
|
||||
|
||||
|
||||
// Not yet pixel perfect todos:
|
||||
// fonts of qr-header
|
||||
|
||||
AccountDetailsModal.prototype.render = function () {
|
||||
const {
|
||||
selectedIdentity,
|
||||
network,
|
||||
showExportPrivateKeyModal,
|
||||
setAccountLabel,
|
||||
keyrings,
|
||||
} = this.props
|
||||
const { name, address } = selectedIdentity
|
||||
|
||||
const keyring = keyrings.find((kr) => {
|
||||
return kr.accounts.includes(address)
|
||||
})
|
||||
|
||||
let exportPrivateKeyFeatureEnabled = true
|
||||
// This feature is disabled for hardware wallets
|
||||
if (keyring && keyring.type.search('Hardware') !== -1) {
|
||||
exportPrivateKeyFeatureEnabled = false
|
||||
}
|
||||
|
||||
return h(AccountModalContainer, {}, [
|
||||
h(EditableLabel, {
|
||||
className: 'account-modal__name',
|
||||
defaultValue: name,
|
||||
onSubmit: label => setAccountLabel(address, label),
|
||||
}),
|
||||
|
||||
h(QrView, {
|
||||
Qr: {
|
||||
data: address,
|
||||
},
|
||||
}),
|
||||
|
||||
h('div.account-modal-divider'),
|
||||
|
||||
h(Button, {
|
||||
type: 'primary',
|
||||
className: 'account-modal__button',
|
||||
onClick: () => global.platform.openWindow({ url: ethNetProps.explorerLinks.getExplorerAccountLinkFor(address, network) }),
|
||||
}, this.context.t('etherscanView')),
|
||||
|
||||
// Holding on redesign for Export Private Key functionality
|
||||
|
||||
exportPrivateKeyFeatureEnabled ? h(Button, {
|
||||
type: 'primary',
|
||||
className: 'account-modal__button',
|
||||
onClick: () => showExportPrivateKeyModal(),
|
||||
}, this.context.t('exportPrivateKey')) : null,
|
||||
|
||||
])
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import UserPreferencedCurrencyDisplay from '../../../user-preferenced-currency-display'
|
||||
import { PRIMARY, SECONDARY } from '../../../../constants/common'
|
||||
|
||||
export default class CancelTransaction extends PureComponent {
|
||||
static propTypes = {
|
||||
value: PropTypes.string,
|
||||
}
|
||||
|
||||
render () {
|
||||
const { value } = this.props
|
||||
|
||||
return (
|
||||
<div className="cancel-transaction-gas-fee">
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="cancel-transaction-gas-fee__eth"
|
||||
value={value}
|
||||
type={PRIMARY}
|
||||
/>
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="cancel-transaction-gas-fee__fiat"
|
||||
value={value}
|
||||
type={SECONDARY}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default } from './cancel-transaction-gas-fee.component'
|
|
@ -1,17 +0,0 @@
|
|||
.cancel-transaction-gas-fee {
|
||||
background: #F1F4F9;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
|
||||
&__eth {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&__fiat {
|
||||
font-size: .75rem;
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
import React from 'react'
|
||||
import assert from 'assert'
|
||||
import { shallow } from 'enzyme'
|
||||
import CancelTransactionGasFee from '../cancel-transaction-gas-fee.component'
|
||||
import UserPreferencedCurrencyDisplay from '../../../../user-preferenced-currency-display'
|
||||
|
||||
describe('CancelTransactionGasFee Component', () => {
|
||||
it('should render', () => {
|
||||
const wrapper = shallow(
|
||||
<CancelTransactionGasFee
|
||||
value="0x3b9aca00"
|
||||
/>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find(UserPreferencedCurrencyDisplay).length, 2)
|
||||
const ethDisplay = wrapper.find(UserPreferencedCurrencyDisplay).at(0)
|
||||
const fiatDisplay = wrapper.find(UserPreferencedCurrencyDisplay).at(1)
|
||||
|
||||
assert.equal(ethDisplay.props().value, '0x3b9aca00')
|
||||
assert.equal(ethDisplay.props().className, 'cancel-transaction-gas-fee__eth')
|
||||
|
||||
assert.equal(fiatDisplay.props().value, '0x3b9aca00')
|
||||
assert.equal(fiatDisplay.props().className, 'cancel-transaction-gas-fee__fiat')
|
||||
})
|
||||
})
|
|
@ -1,68 +0,0 @@
|
|||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Modal from '../../modal'
|
||||
import CancelTransactionGasFee from './cancel-transaction-gas-fee'
|
||||
import { SUBMITTED_STATUS } from '../../../constants/transactions'
|
||||
|
||||
export default class CancelTransaction extends PureComponent {
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
createCancelTransaction: PropTypes.func,
|
||||
hideModal: PropTypes.func,
|
||||
showTransactionConfirmedModal: PropTypes.func,
|
||||
transactionStatus: PropTypes.string,
|
||||
newGasFee: PropTypes.string,
|
||||
}
|
||||
|
||||
componentDidUpdate () {
|
||||
const { transactionStatus, showTransactionConfirmedModal } = this.props
|
||||
|
||||
if (transactionStatus !== SUBMITTED_STATUS) {
|
||||
showTransactionConfirmedModal()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
handleSubmit = async () => {
|
||||
const { createCancelTransaction, hideModal } = this.props
|
||||
|
||||
await createCancelTransaction()
|
||||
hideModal()
|
||||
}
|
||||
|
||||
handleCancel = () => {
|
||||
this.props.hideModal()
|
||||
}
|
||||
|
||||
render () {
|
||||
const { t } = this.context
|
||||
const { newGasFee } = this.props
|
||||
|
||||
return (
|
||||
<Modal
|
||||
headerText={t('attemptToCancel')}
|
||||
onClose={this.handleCancel}
|
||||
onSubmit={this.handleSubmit}
|
||||
onCancel={this.handleCancel}
|
||||
submitText={t('yesLetsTry')}
|
||||
cancelText={t('nevermind')}
|
||||
submitType="secondary"
|
||||
>
|
||||
<div>
|
||||
<div className="cancel-transaction__title">
|
||||
{ t('cancellationGasFee') }
|
||||
</div>
|
||||
<div className="cancel-transaction__cancel-transaction-gas-fee-container">
|
||||
<CancelTransactionGasFee value={newGasFee} />
|
||||
</div>
|
||||
<div className="cancel-transaction__description">
|
||||
{ t('attemptToCancelDescription') }
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
import { connect } from 'react-redux'
|
||||
import { compose } from 'recompose'
|
||||
import ethUtil from 'ethereumjs-util'
|
||||
import { multiplyCurrencies } from '../../../conversion-util'
|
||||
import withModalProps from '../../../higher-order-components/with-modal-props'
|
||||
import CancelTransaction from './cancel-transaction.component'
|
||||
import { showModal, createCancelTransaction } from '../../../actions'
|
||||
import { getHexGasTotal } from '../../../helpers/confirm-transaction/util'
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { metamask } = state
|
||||
const { transactionId, originalGasPrice } = ownProps
|
||||
const { selectedAddressTxList } = metamask
|
||||
const transaction = selectedAddressTxList.find(({ id }) => id === transactionId)
|
||||
const transactionStatus = transaction ? transaction.status : ''
|
||||
|
||||
const defaultNewGasPrice = ethUtil.addHexPrefix(
|
||||
multiplyCurrencies(originalGasPrice, 1.1, {
|
||||
toNumericBase: 'hex',
|
||||
multiplicandBase: 16,
|
||||
multiplierBase: 10,
|
||||
})
|
||||
)
|
||||
|
||||
const newGasFee = getHexGasTotal({ gasPrice: defaultNewGasPrice, gasLimit: '0x5208' })
|
||||
|
||||
return {
|
||||
transactionId,
|
||||
transactionStatus,
|
||||
originalGasPrice,
|
||||
newGasFee,
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
createCancelTransaction: txId => dispatch(createCancelTransaction(txId)),
|
||||
showTransactionConfirmedModal: () => dispatch(showModal({ name: 'TRANSACTION_CONFIRMED' })),
|
||||
}
|
||||
}
|
||||
|
||||
const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
||||
const { transactionId, ...restStateProps } = stateProps
|
||||
const {
|
||||
createCancelTransaction: dispatchCreateCancelTransaction,
|
||||
...restDispatchProps
|
||||
} = dispatchProps
|
||||
|
||||
return {
|
||||
...restStateProps,
|
||||
...restDispatchProps,
|
||||
...ownProps,
|
||||
createCancelTransaction: newGasPrice => {
|
||||
return dispatchCreateCancelTransaction(transactionId, newGasPrice)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withModalProps,
|
||||
connect(mapStateToProps, mapDispatchToProps, mergeProps),
|
||||
)(CancelTransaction)
|
|
@ -1 +0,0 @@
|
|||
export { default } from './cancel-transaction.container'
|
|
@ -1,18 +0,0 @@
|
|||
@import './cancel-transaction-gas-fee/index';
|
||||
|
||||
.cancel-transaction {
|
||||
&__title {
|
||||
font-weight: 500;
|
||||
padding-bottom: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__description {
|
||||
text-align: center;
|
||||
font-size: .875rem;
|
||||
}
|
||||
|
||||
&__cancel-transaction-gas-fee-container {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
import React from 'react'
|
||||
import assert from 'assert'
|
||||
import { shallow } from 'enzyme'
|
||||
import sinon from 'sinon'
|
||||
import CancelTransaction from '../cancel-transaction.component'
|
||||
import CancelTransactionGasFee from '../cancel-transaction-gas-fee'
|
||||
import Modal from '../../../modal'
|
||||
|
||||
describe('CancelTransaction Component', () => {
|
||||
const t = key => key
|
||||
|
||||
it('should render a CancelTransaction modal', () => {
|
||||
const wrapper = shallow(
|
||||
<CancelTransaction
|
||||
newGasFee="0x1319718a5000"
|
||||
/>,
|
||||
{ context: { t }}
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find(Modal).length, 1)
|
||||
assert.equal(wrapper.find(CancelTransactionGasFee).length, 1)
|
||||
assert.equal(wrapper.find(CancelTransactionGasFee).props().value, '0x1319718a5000')
|
||||
assert.equal(wrapper.find('.cancel-transaction__title').text(), 'cancellationGasFee')
|
||||
assert.equal(wrapper.find('.cancel-transaction__description').text(), 'attemptToCancelDescription')
|
||||
})
|
||||
|
||||
it('should pass the correct props to the Modal component', async () => {
|
||||
const createCancelTransactionSpy = sinon.stub().callsFake(() => Promise.resolve())
|
||||
const hideModalSpy = sinon.spy()
|
||||
|
||||
const wrapper = shallow(
|
||||
<CancelTransaction
|
||||
defaultNewGasPrice="0x3b9aca00"
|
||||
createCancelTransaction={createCancelTransactionSpy}
|
||||
hideModal={hideModalSpy}
|
||||
/>,
|
||||
{ context: { t }}
|
||||
)
|
||||
|
||||
assert.equal(wrapper.find(Modal).length, 1)
|
||||
const modalProps = wrapper.find(Modal).props()
|
||||
|
||||
assert.equal(modalProps.headerText, 'attemptToCancel')
|
||||
assert.equal(modalProps.submitText, 'yesLetsTry')
|
||||
assert.equal(modalProps.cancelText, 'nevermind')
|
||||
|
||||
assert.equal(createCancelTransactionSpy.callCount, 0)
|
||||
assert.equal(hideModalSpy.callCount, 0)
|
||||
await modalProps.onSubmit()
|
||||
assert.equal(createCancelTransactionSpy.callCount, 1)
|
||||
assert.equal(hideModalSpy.callCount, 1)
|
||||
modalProps.onCancel()
|
||||
assert.equal(hideModalSpy.callCount, 2)
|
||||
})
|
||||
})
|
|
@ -1,89 +0,0 @@
|
|||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Modal from '../../modal'
|
||||
import { addressSummary } from '../../../util'
|
||||
import Identicon from '../../identicon'
|
||||
import ethNetProps from 'eth-net-props'
|
||||
|
||||
export default class ConfirmRemoveAccount extends Component {
|
||||
static propTypes = {
|
||||
hideModal: PropTypes.func.isRequired,
|
||||
removeAccount: PropTypes.func.isRequired,
|
||||
identity: PropTypes.object.isRequired,
|
||||
network: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
handleRemove = () => {
|
||||
this.props.removeAccount(this.props.identity.address)
|
||||
.then(() => this.props.hideModal())
|
||||
}
|
||||
|
||||
handleCancel = () => {
|
||||
this.props.hideModal()
|
||||
}
|
||||
|
||||
renderSelectedAccount () {
|
||||
const { identity } = this.props
|
||||
return (
|
||||
<div className="confirm-remove-account__account">
|
||||
<div className="confirm-remove-account__account__identicon">
|
||||
<Identicon
|
||||
address={identity.address}
|
||||
diameter={32}
|
||||
/>
|
||||
</div>
|
||||
<div className="confirm-remove-account__account__name">
|
||||
<span className="confirm-remove-account__account__label">Name</span>
|
||||
<span className="account_value">{identity.name}</span>
|
||||
</div>
|
||||
<div className="confirm-remove-account__account__address">
|
||||
<span className="confirm-remove-account__account__label">Public Address</span>
|
||||
<span className="account_value">{ addressSummary(identity.address, 4, 4) }</span>
|
||||
</div>
|
||||
<div className="confirm-remove-account__account__link">
|
||||
<a
|
||||
className=""
|
||||
href={ethNetProps.explorerLinks.getExplorerAccountLinkFor(identity.address, this.props.network)}
|
||||
target={'_blank'}
|
||||
title={this.context.t('etherscanView')}
|
||||
>
|
||||
<img src="images/popout.svg" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const { t } = this.context
|
||||
|
||||
return (
|
||||
<Modal
|
||||
headerText={`${t('removeAccount')}?`}
|
||||
onClose={this.handleCancel}
|
||||
onSubmit={this.handleRemove}
|
||||
onCancel={this.handleCancel}
|
||||
submitText={t('remove')}
|
||||
cancelText={t('nevermind')}
|
||||
submitType="secondary"
|
||||
>
|
||||
<div>
|
||||
{ this.renderSelectedAccount() }
|
||||
<div className="confirm-remove-account__description">
|
||||
{ t('removeAccountDescription') }
|
||||
<a
|
||||
className="confirm-remove-account__link"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank" href="https://metamask.zendesk.com/hc/en-us/articles/360015289932">
|
||||
{ t('learnMore') }
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
import { connect } from 'react-redux'
|
||||
import { compose } from 'recompose'
|
||||
import ConfirmRemoveAccount from './confirm-remove-account.component'
|
||||
import withModalProps from '../../../higher-order-components/with-modal-props'
|
||||
import { removeAccount } from '../../../actions'
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
network: state.metamask.network,
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
removeAccount: (address) => dispatch(removeAccount(address)),
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withModalProps,
|
||||
connect(mapStateToProps, mapDispatchToProps)
|
||||
)(ConfirmRemoveAccount)
|
|
@ -1 +0,0 @@
|
|||
export { default } from './confirm-remove-account.container'
|
|
@ -1,58 +0,0 @@
|
|||
.confirm-remove-account {
|
||||
&__description {
|
||||
text-align: center;
|
||||
font-size: .875rem;
|
||||
}
|
||||
|
||||
&__account {
|
||||
border: 1px solid #b7b7b7;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
width: 100%;
|
||||
|
||||
&__identicon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
&__name,
|
||||
&__address {
|
||||
margin-right: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&__name {
|
||||
width: 100px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-size: 11px;
|
||||
display: block;
|
||||
color: #9b9b9b;
|
||||
}
|
||||
|
||||
&__link {
|
||||
margin-top: 14px;
|
||||
|
||||
img {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 575px) {
|
||||
&__name {
|
||||
width: 90px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__link {
|
||||
color: #2f9ae0;
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Modal, { ModalContent } from '../../modal'
|
||||
|
||||
export default class ConfirmResetAccount extends PureComponent {
|
||||
static propTypes = {
|
||||
hideModal: PropTypes.func.isRequired,
|
||||
resetAccount: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
handleReset = () => {
|
||||
this.props.resetAccount()
|
||||
.then(() => this.props.hideModal())
|
||||
}
|
||||
|
||||
render () {
|
||||
const { t } = this.context
|
||||
|
||||
return (
|
||||
<Modal
|
||||
onSubmit={this.handleReset}
|
||||
onCancel={() => this.props.hideModal()}
|
||||
submitText={t('reset')}
|
||||
cancelText={t('nevermind')}
|
||||
submitType="secondary"
|
||||
>
|
||||
<ModalContent
|
||||
title={`${t('resetAccount')}?`}
|
||||
description={t('resetAccountDescription')}
|
||||
/>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
import { connect } from 'react-redux'
|
||||
import { compose } from 'recompose'
|
||||
import withModalProps from '../../../higher-order-components/with-modal-props'
|
||||
import ConfirmResetAccount from './confirm-reset-account.component'
|
||||
import { resetAccount } from '../../../actions'
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
resetAccount: () => dispatch(resetAccount()),
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withModalProps,
|
||||
connect(null, mapDispatchToProps)
|
||||
)(ConfirmResetAccount)
|
|
@ -1 +0,0 @@
|
|||
export { default } from './confirm-reset-account.container'
|
|
@ -1,143 +0,0 @@
|
|||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import GasModalCard from '../../customize-gas-modal/gas-modal-card'
|
||||
import { MIN_GAS_PRICE_GWEI } from '../../send/send.constants'
|
||||
import Button from '../../button'
|
||||
|
||||
import {
|
||||
getDecimalGasLimit,
|
||||
getDecimalGasPrice,
|
||||
getPrefixedHexGasLimit,
|
||||
getPrefixedHexGasPrice,
|
||||
} from './customize-gas.util'
|
||||
|
||||
export default class CustomizeGas extends Component {
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
txData: PropTypes.object.isRequired,
|
||||
hideModal: PropTypes.func,
|
||||
validate: PropTypes.func,
|
||||
onSubmit: PropTypes.func,
|
||||
}
|
||||
|
||||
state = {
|
||||
gasPrice: 0,
|
||||
gasLimit: 0,
|
||||
originalGasPrice: 0,
|
||||
originalGasLimit: 0,
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
const { txData = {} } = this.props
|
||||
const { txParams: { gas: hexGasLimit, gasPrice: hexGasPrice } = {} } = txData
|
||||
|
||||
const gasLimit = getDecimalGasLimit(hexGasLimit)
|
||||
const gasPrice = getDecimalGasPrice(hexGasPrice)
|
||||
|
||||
this.setState({
|
||||
gasPrice,
|
||||
gasLimit,
|
||||
originalGasPrice: gasPrice,
|
||||
originalGasLimit: gasLimit,
|
||||
})
|
||||
}
|
||||
|
||||
handleRevert () {
|
||||
const { originalGasPrice, originalGasLimit } = this.state
|
||||
|
||||
this.setState({
|
||||
gasPrice: originalGasPrice,
|
||||
gasLimit: originalGasLimit,
|
||||
})
|
||||
}
|
||||
|
||||
handleSave () {
|
||||
const { onSubmit, hideModal } = this.props
|
||||
const { gasLimit, gasPrice } = this.state
|
||||
const prefixedHexGasPrice = getPrefixedHexGasPrice(gasPrice)
|
||||
const prefixedHexGasLimit = getPrefixedHexGasLimit(gasLimit)
|
||||
|
||||
Promise.resolve(onSubmit({ gasPrice: prefixedHexGasPrice, gasLimit: prefixedHexGasLimit }))
|
||||
.then(() => hideModal())
|
||||
}
|
||||
|
||||
validate () {
|
||||
const { gasLimit, gasPrice } = this.state
|
||||
return this.props.validate({
|
||||
gasPrice: getPrefixedHexGasPrice(gasPrice),
|
||||
gasLimit: getPrefixedHexGasLimit(gasLimit),
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
const { t } = this.context
|
||||
const { hideModal } = this.props
|
||||
const { gasPrice, gasLimit } = this.state
|
||||
const { valid, errorKey } = this.validate()
|
||||
|
||||
return (
|
||||
<div className="customize-gas">
|
||||
<div className="customize-gas__content">
|
||||
<div className="customize-gas__header">
|
||||
<div className="customize-gas__title">
|
||||
{ this.context.t('customGas') }
|
||||
</div>
|
||||
<div
|
||||
className="customize-gas__close"
|
||||
onClick={() => hideModal()}
|
||||
/>
|
||||
</div>
|
||||
<div className="customize-gas__body">
|
||||
<GasModalCard
|
||||
value={gasPrice}
|
||||
min={MIN_GAS_PRICE_GWEI}
|
||||
step={1}
|
||||
onChange={value => this.setState({ gasPrice: value })}
|
||||
title={t('gasPrice')}
|
||||
copy={t('gasPriceCalculation')}
|
||||
/>
|
||||
<GasModalCard
|
||||
value={gasLimit}
|
||||
min={1}
|
||||
step={1}
|
||||
onChange={value => this.setState({ gasLimit: value })}
|
||||
title={t('gasLimit')}
|
||||
copy={t('gasLimitCalculation')}
|
||||
/>
|
||||
</div>
|
||||
<div className="customize-gas__footer">
|
||||
{ !valid && <div className="customize-gas__error-message">{ t(errorKey) }</div> }
|
||||
<div
|
||||
className="customize-gas__revert"
|
||||
onClick={() => this.handleRevert()}
|
||||
>
|
||||
{ t('revert') }
|
||||
</div>
|
||||
<div className="customize-gas__buttons">
|
||||
<Button
|
||||
type="default"
|
||||
className="customize-gas__cancel"
|
||||
onClick={() => hideModal()}
|
||||
style={{ marginRight: '10px' }}
|
||||
>
|
||||
{ t('cancel') }
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
className="customize-gas__save"
|
||||
onClick={() => this.handleSave()}
|
||||
style={{ marginRight: '10px' }}
|
||||
disabled={!valid}
|
||||
>
|
||||
{ t('save') }
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
import { connect } from 'react-redux'
|
||||
import CustomizeGas from './customize-gas.component'
|
||||
import { hideModal } from '../../../actions'
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const { appState: { modal: { modalState: { props } } } } = state
|
||||
const { txData, onSubmit, validate } = props
|
||||
|
||||
return {
|
||||
txData,
|
||||
onSubmit,
|
||||
validate,
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
hideModal: () => dispatch(hideModal()),
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(CustomizeGas)
|
|
@ -1,34 +0,0 @@
|
|||
import ethUtil from 'ethereumjs-util'
|
||||
import { conversionUtil } from '../../../conversion-util'
|
||||
|
||||
export function getDecimalGasLimit (hexGasLimit) {
|
||||
return conversionUtil(hexGasLimit, {
|
||||
fromNumericBase: 'hex',
|
||||
toNumericBase: 'dec',
|
||||
})
|
||||
}
|
||||
|
||||
export function getDecimalGasPrice (hexGasPrice) {
|
||||
return conversionUtil(hexGasPrice, {
|
||||
fromNumericBase: 'hex',
|
||||
toNumericBase: 'dec',
|
||||
fromDenomination: 'WEI',
|
||||
toDenomination: 'GWEI',
|
||||
})
|
||||
}
|
||||
|
||||
export function getPrefixedHexGasLimit (gasLimit) {
|
||||
return ethUtil.addHexPrefix(conversionUtil(gasLimit, {
|
||||
fromNumericBase: 'dec',
|
||||
toNumericBase: 'hex',
|
||||
}))
|
||||
}
|
||||
|
||||
export function getPrefixedHexGasPrice (gasPrice) {
|
||||
return ethUtil.addHexPrefix(conversionUtil(gasPrice, {
|
||||
fromNumericBase: 'dec',
|
||||
toNumericBase: 'hex',
|
||||
fromDenomination: 'GWEI',
|
||||
toDenomination: 'WEI',
|
||||
}))
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default } from './customize-gas.container'
|
|
@ -1,110 +0,0 @@
|
|||
.customize-gas {
|
||||
border: 1px solid #D8D8D8;
|
||||
border-radius: 4px;
|
||||
background-color: #FFFFFF;
|
||||
box-shadow: 0 2px 4px 0 rgba(0,0,0,0.14);
|
||||
font-family: Roboto;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
&__header {
|
||||
height: 52px;
|
||||
border-bottom: 1px solid $alto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 22px;
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
&__title {
|
||||
margin-left: 19.25px;
|
||||
}
|
||||
|
||||
&__close::after {
|
||||
content: '\00D7';
|
||||
font-size: 1.8em;
|
||||
color: $dusty-gray;
|
||||
font-family: sans-serif;
|
||||
cursor: pointer;
|
||||
margin-right: 19.25px;
|
||||
}
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&__body {
|
||||
display: flex;
|
||||
margin-bottom: 24px;
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
flex-flow: column;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
height: 75px;
|
||||
border-top: 1px solid $alto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 22px;
|
||||
position: relative;
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-right: 21.25px;
|
||||
}
|
||||
|
||||
&__revert, &__cancel, &__save, &__save__error {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__revert {
|
||||
color: $silver-chalice;
|
||||
font-size: 16px;
|
||||
margin-left: 21.25px;
|
||||
}
|
||||
|
||||
&__cancel, &__save, &__save__error {
|
||||
width: 85.74px;
|
||||
min-width: initial;
|
||||
}
|
||||
|
||||
&__save__error {
|
||||
opacity: 0.5;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
&__error-message {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
color: $red;
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
@import './cancel-transaction/index';
|
||||
|
||||
@import './confirm-remove-account/index';
|
||||
|
||||
@import './customize-gas/index';
|
||||
|
||||
@import './qr-scanner/index';
|
||||
|
||||
@import './transaction-confirmed/index';
|
|
@ -11,24 +11,11 @@ const { ENVIRONMENT_TYPE_POPUP } = require('../../../../app/scripts/lib/enums')
|
|||
// Modal Components
|
||||
const BuyOptions = require('./buy-options-modal')
|
||||
const DepositEtherModal = require('./deposit-ether-modal')
|
||||
const AccountDetailsModal = require('./account-details-modal')
|
||||
const EditAccountNameModal = require('./edit-account-name-modal')
|
||||
const ExportPrivateKeyModal = require('./export-private-key-modal')
|
||||
const NewAccountModal = require('./new-account-modal')
|
||||
const ShapeshiftDepositTxModal = require('./shapeshift-deposit-tx-modal.js')
|
||||
const HideTokenConfirmationModal = require('./hide-token-confirmation-modal')
|
||||
const CustomizeGasModal = require('../customize-gas-modal')
|
||||
const NotifcationModal = require('./notification-modal')
|
||||
const QRScanner = require('./qr-scanner')
|
||||
|
||||
import ConfirmRemoveAccount from './confirm-remove-account'
|
||||
import ConfirmResetAccount from './confirm-reset-account'
|
||||
import TransactionConfirmed from './transaction-confirmed'
|
||||
import ConfirmCustomizeGasModal from './customize-gas'
|
||||
import CancelTransaction from './cancel-transaction'
|
||||
import WelcomeBeta from './welcome-beta'
|
||||
import TransactionDetails from './transaction-details'
|
||||
import RejectTransactions from './reject-transactions'
|
||||
|
||||
const modalContainerBaseStyle = {
|
||||
transform: 'translate3d(-50%, 0, 0px)',
|
||||
|
@ -165,13 +152,6 @@ const MODALS = {
|
|||
},
|
||||
},
|
||||
|
||||
ACCOUNT_DETAILS: {
|
||||
contents: [
|
||||
h(AccountDetailsModal, {}, []),
|
||||
],
|
||||
...accountModalStyle,
|
||||
},
|
||||
|
||||
EXPORT_PRIVATE_KEY: {
|
||||
contents: [
|
||||
h(ExportPrivateKeyModal, {}, []),
|
||||
|
@ -179,13 +159,6 @@ const MODALS = {
|
|||
...accountModalStyle,
|
||||
},
|
||||
|
||||
SHAPESHIFT_DEPOSIT_TX: {
|
||||
contents: [
|
||||
h(ShapeshiftDepositTxModal),
|
||||
],
|
||||
...accountModalStyle,
|
||||
},
|
||||
|
||||
HIDE_TOKEN_CONFIRMATION: {
|
||||
contents: [
|
||||
h(HideTokenConfirmationModal, {}, []),
|
||||
|
@ -200,19 +173,6 @@ const MODALS = {
|
|||
},
|
||||
},
|
||||
|
||||
BETA_UI_NOTIFICATION_MODAL: {
|
||||
contents: h(WelcomeBeta),
|
||||
mobileModalStyle: {
|
||||
...modalContainerMobileStyle,
|
||||
},
|
||||
laptopModalStyle: {
|
||||
...modalContainerLaptopStyle,
|
||||
},
|
||||
contentStyle: {
|
||||
borderRadius: '8px',
|
||||
},
|
||||
},
|
||||
|
||||
OLD_UI_NOTIFICATION_MODAL: {
|
||||
contents: [
|
||||
h(NotifcationModal, {
|
||||
|
@ -230,32 +190,6 @@ const MODALS = {
|
|||
},
|
||||
},
|
||||
|
||||
CONFIRM_RESET_ACCOUNT: {
|
||||
contents: h(ConfirmResetAccount),
|
||||
mobileModalStyle: {
|
||||
...modalContainerMobileStyle,
|
||||
},
|
||||
laptopModalStyle: {
|
||||
...modalContainerLaptopStyle,
|
||||
},
|
||||
contentStyle: {
|
||||
borderRadius: '8px',
|
||||
},
|
||||
},
|
||||
|
||||
CONFIRM_REMOVE_ACCOUNT: {
|
||||
contents: h(ConfirmRemoveAccount),
|
||||
mobileModalStyle: {
|
||||
...modalContainerMobileStyle,
|
||||
},
|
||||
laptopModalStyle: {
|
||||
...modalContainerLaptopStyle,
|
||||
},
|
||||
contentStyle: {
|
||||
borderRadius: '8px',
|
||||
},
|
||||
},
|
||||
|
||||
NEW_ACCOUNT: {
|
||||
contents: [
|
||||
h(NewAccountModal, {}, []),
|
||||
|
@ -280,118 +214,6 @@ const MODALS = {
|
|||
},
|
||||
},
|
||||
|
||||
CUSTOMIZE_GAS: {
|
||||
contents: [
|
||||
h(CustomizeGasModal),
|
||||
],
|
||||
mobileModalStyle: {
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
top: '0',
|
||||
transform: 'none',
|
||||
left: '0',
|
||||
right: '0',
|
||||
margin: '0 auto',
|
||||
},
|
||||
laptopModalStyle: {
|
||||
width: '720px',
|
||||
height: '377px',
|
||||
top: '80px',
|
||||
transform: 'none',
|
||||
left: '0',
|
||||
right: '0',
|
||||
margin: '0 auto',
|
||||
},
|
||||
},
|
||||
|
||||
CONFIRM_CUSTOMIZE_GAS: {
|
||||
contents: h(ConfirmCustomizeGasModal),
|
||||
mobileModalStyle: {
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
top: '0',
|
||||
transform: 'none',
|
||||
left: '0',
|
||||
right: '0',
|
||||
margin: '0 auto',
|
||||
},
|
||||
laptopModalStyle: {
|
||||
width: '720px',
|
||||
height: '377px',
|
||||
top: '80px',
|
||||
transform: 'none',
|
||||
left: '0',
|
||||
right: '0',
|
||||
margin: '0 auto',
|
||||
},
|
||||
},
|
||||
|
||||
TRANSACTION_CONFIRMED: {
|
||||
disableBackdropClick: true,
|
||||
contents: h(TransactionConfirmed),
|
||||
mobileModalStyle: {
|
||||
...modalContainerMobileStyle,
|
||||
},
|
||||
laptopModalStyle: {
|
||||
...modalContainerLaptopStyle,
|
||||
},
|
||||
contentStyle: {
|
||||
borderRadius: '8px',
|
||||
},
|
||||
},
|
||||
|
||||
QR_SCANNER: {
|
||||
contents: h(QRScanner),
|
||||
mobileModalStyle: {
|
||||
...modalContainerMobileStyle,
|
||||
},
|
||||
laptopModalStyle: {
|
||||
...modalContainerLaptopStyle,
|
||||
},
|
||||
contentStyle: {
|
||||
borderRadius: '8px',
|
||||
},
|
||||
},
|
||||
|
||||
CANCEL_TRANSACTION: {
|
||||
contents: h(CancelTransaction),
|
||||
mobileModalStyle: {
|
||||
...modalContainerMobileStyle,
|
||||
},
|
||||
laptopModalStyle: {
|
||||
...modalContainerLaptopStyle,
|
||||
},
|
||||
contentStyle: {
|
||||
borderRadius: '8px',
|
||||
},
|
||||
},
|
||||
|
||||
TRANSACTION_DETAILS: {
|
||||
contents: h(TransactionDetails),
|
||||
mobileModalStyle: {
|
||||
...modalContainerMobileStyle,
|
||||
},
|
||||
laptopModalStyle: {
|
||||
...modalContainerLaptopStyle,
|
||||
},
|
||||
contentStyle: {
|
||||
borderRadius: '8px',
|
||||
},
|
||||
},
|
||||
|
||||
REJECT_TRANSACTIONS: {
|
||||
contents: h(RejectTransactions),
|
||||
mobileModalStyle: {
|
||||
...modalContainerMobileStyle,
|
||||
},
|
||||
laptopModalStyle: {
|
||||
...modalContainerLaptopStyle,
|
||||
},
|
||||
contentStyle: {
|
||||
borderRadius: '8px',
|
||||
},
|
||||
},
|
||||
|
||||
DEFAULT: {
|
||||
contents: [],
|
||||
mobileModalStyle: {},
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
import QrScanner from './qr-scanner.container'
|
||||
module.exports = QrScanner
|
|
@ -1,83 +0,0 @@
|
|||
.qr-scanner {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
border-radius: 8px;
|
||||
|
||||
&__title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 500;
|
||||
padding: 16px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__content {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
|
||||
&__video-wrapper {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 275px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
video {
|
||||
transform: scaleX(-1);
|
||||
width: auto;
|
||||
height: 275px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__status {
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
&__image {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 500;
|
||||
padding: 16px 0 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__error {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
padding: 20px;
|
||||
flex-direction: row;
|
||||
display: flex;
|
||||
|
||||
button {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
button:last-of-type {
|
||||
margin-right: 0;
|
||||
background-color: #009eec;
|
||||
border: none;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&__close::after {
|
||||
content: '\00D7';
|
||||
font-size: 35px;
|
||||
color: #9b9b9b;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 20px;
|
||||
cursor: pointer;
|
||||
font-weight: 300;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,216 +0,0 @@
|
|||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { BrowserQRCodeReader } from '@zxing/library'
|
||||
// import adapter from 'webrtc-adapter' // eslint-disable-line import/no-nodejs-modules, no-unused-vars
|
||||
import Spinner from '../../spinner'
|
||||
import WebcamUtils from '../../../../lib/webcam-utils'
|
||||
import PageContainerFooter from '../../page-container/page-container-footer/page-container-footer.component'
|
||||
|
||||
export default class QrScanner extends Component {
|
||||
static propTypes = {
|
||||
hideModal: PropTypes.func.isRequired,
|
||||
qrCodeDetected: PropTypes.func,
|
||||
scanQrCode: PropTypes.func,
|
||||
error: PropTypes.bool,
|
||||
errorType: PropTypes.string,
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
constructor (props, context) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
ready: false,
|
||||
msg: context.t('accessingYourCamera'),
|
||||
}
|
||||
this.codeReader = null
|
||||
this.permissionChecker = null
|
||||
this.needsToReinit = false
|
||||
|
||||
// Clear pre-existing qr code data before scanning
|
||||
this.props.qrCodeDetected(null)
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.initCamera()
|
||||
}
|
||||
|
||||
async checkPermisisions () {
|
||||
const { permissions } = await WebcamUtils.checkStatus()
|
||||
if (permissions) {
|
||||
clearTimeout(this.permissionChecker)
|
||||
// Let the video stream load first...
|
||||
setTimeout(_ => {
|
||||
this.setState({
|
||||
ready: true,
|
||||
msg: this.context.t('scanInstructions'),
|
||||
})
|
||||
if (this.needsToReinit) {
|
||||
this.initCamera()
|
||||
this.needsToReinit = false
|
||||
}
|
||||
}, 2000)
|
||||
} else {
|
||||
// Keep checking for permissions
|
||||
this.permissionChecker = setTimeout(_ => {
|
||||
this.checkPermisisions()
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
clearTimeout(this.permissionChecker)
|
||||
if (this.codeReader) {
|
||||
this.codeReader.reset()
|
||||
}
|
||||
}
|
||||
|
||||
initCamera () {
|
||||
this.codeReader = new BrowserQRCodeReader()
|
||||
this.codeReader.getVideoInputDevices()
|
||||
.then(videoInputDevices => {
|
||||
clearTimeout(this.permissionChecker)
|
||||
this.checkPermisisions()
|
||||
this.codeReader.decodeFromInputVideoDevice(undefined, 'video')
|
||||
.then(content => {
|
||||
const result = this.parseContent(content.text)
|
||||
if (result.type !== 'unknown') {
|
||||
this.props.qrCodeDetected(result)
|
||||
this.stopAndClose()
|
||||
} else {
|
||||
this.setState({msg: this.context.t('unknownQrCode')})
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
if (err && err.name === 'NotAllowedError') {
|
||||
this.setState({msg: this.context.t('youNeedToAllowCameraAccess')})
|
||||
clearTimeout(this.permissionChecker)
|
||||
this.needsToReinit = true
|
||||
this.checkPermisisions()
|
||||
}
|
||||
})
|
||||
}).catch(err => {
|
||||
console.error('[QR-SCANNER]: getVideoInputDevices threw an exception: ', err)
|
||||
})
|
||||
}
|
||||
|
||||
parseContent (content) {
|
||||
let type = 'unknown'
|
||||
let values = {}
|
||||
|
||||
// Here we could add more cases
|
||||
// To parse other type of links
|
||||
// For ex. EIP-681 (https://eips.ethereum.org/EIPS/eip-681)
|
||||
|
||||
|
||||
// Ethereum address links - fox ex. ethereum:0x.....1111
|
||||
if (content.split('ethereum:').length > 1) {
|
||||
|
||||
type = 'address'
|
||||
values = {'address': content.split('ethereum:')[1] }
|
||||
|
||||
// Regular ethereum addresses - fox ex. 0x.....1111
|
||||
} else if (content.substring(0, 2).toLowerCase() === '0x') {
|
||||
|
||||
type = 'address'
|
||||
values = {'address': content }
|
||||
|
||||
}
|
||||
return {type, values}
|
||||
}
|
||||
|
||||
|
||||
stopAndClose = () => {
|
||||
if (this.codeReader) {
|
||||
this.codeReader.reset()
|
||||
}
|
||||
this.setState({ ready: false })
|
||||
this.props.hideModal()
|
||||
}
|
||||
|
||||
tryAgain = () => {
|
||||
// close the modal
|
||||
this.stopAndClose()
|
||||
// wait for the animation and try again
|
||||
setTimeout(_ => {
|
||||
this.props.scanQrCode()
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
renderVideo () {
|
||||
return (
|
||||
<div className={'qr-scanner__content__video-wrapper'}>
|
||||
<video
|
||||
id="video"
|
||||
style={{
|
||||
display: this.state.ready ? 'block' : 'none',
|
||||
}}
|
||||
/>
|
||||
{ !this.state.ready ? <Spinner color={'#F7C06C'} /> : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderErrorModal () {
|
||||
let title, msg
|
||||
|
||||
if (this.props.error) {
|
||||
if (this.props.errorType === 'NO_WEBCAM_FOUND') {
|
||||
title = this.context.t('noWebcamFoundTitle')
|
||||
msg = this.context.t('noWebcamFound')
|
||||
} else {
|
||||
title = this.context.t('unknownCameraErrorTitle')
|
||||
msg = this.context.t('unknownCameraError')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="qr-scanner">
|
||||
<div className="qr-scanner__close" onClick={this.stopAndClose}></div>
|
||||
|
||||
<div className="qr-scanner__image">
|
||||
<img src={'images/webcam.svg'} width={70} height={70} />
|
||||
</div>
|
||||
<div className="qr-scanner__title">
|
||||
{ title }
|
||||
</div>
|
||||
<div className={'qr-scanner__error'}>
|
||||
{msg}
|
||||
</div>
|
||||
<PageContainerFooter
|
||||
onCancel={this.stopAndClose}
|
||||
onSubmit={this.tryAgain}
|
||||
cancelText={this.context.t('cancel')}
|
||||
submitText={this.context.t('tryAgain')}
|
||||
submitButtonType="confirm"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const { t } = this.context
|
||||
|
||||
if (this.props.error) {
|
||||
return this.renderErrorModal()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="qr-scanner">
|
||||
<div className="qr-scanner__close" onClick={this.stopAndClose}></div>
|
||||
<div className="qr-scanner__title">
|
||||
{ `${t('scanQrCode')}` }
|
||||
</div>
|
||||
<div className="qr-scanner__content">
|
||||
{ this.renderVideo() }
|
||||
</div>
|
||||
<div className={'qr-scanner__status'}>
|
||||
{this.state.msg}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
import { connect } from 'react-redux'
|
||||
import QrScanner from './qr-scanner.component'
|
||||
|
||||
const { hideModal, qrCodeDetected, showQrScanner } = require('../../../actions')
|
||||
import {
|
||||
SEND_ROUTE,
|
||||
} from '../../../routes'
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
error: state.appState.modal.modalState.props.error,
|
||||
errorType: state.appState.modal.modalState.props.errorType,
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
hideModal: () => dispatch(hideModal()),
|
||||
qrCodeDetected: (data) => dispatch(qrCodeDetected(data)),
|
||||
scanQrCode: () => dispatch(showQrScanner(SEND_ROUTE)),
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(QrScanner)
|
|
@ -1 +0,0 @@
|
|||
export { default } from './reject-transactions.container'
|
|
@ -1,6 +0,0 @@
|
|||
.reject-transactions {
|
||||
&__description {
|
||||
text-align: center;
|
||||
font-size: .875rem;
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
import PropTypes from 'prop-types'
|
||||
import React, { PureComponent } from 'react'
|
||||
import Modal from '../../modal'
|
||||
|
||||
export default class RejectTransactionsModal extends PureComponent {
|
||||
static contextTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
hideModal: PropTypes.func.isRequired,
|
||||
unapprovedTxCount: PropTypes.number.isRequired,
|
||||
}
|
||||
|
||||
onSubmit = async () => {
|
||||
const { onSubmit, hideModal } = this.props
|
||||
|
||||
await onSubmit()
|
||||
hideModal()
|
||||
}
|
||||
|
||||
render () {
|
||||
const { t } = this.context
|
||||
const { hideModal, unapprovedTxCount } = this.props
|
||||
|
||||
return (
|
||||
<Modal
|
||||
headerText={t('rejectTxsN', [unapprovedTxCount])}
|
||||
onClose={hideModal}
|
||||
onSubmit={this.onSubmit}
|
||||
onCancel={hideModal}
|
||||
submitText={t('rejectAll')}
|
||||
cancelText={t('cancel')}
|
||||
submitType="secondary"
|
||||
>
|
||||
<div>
|
||||
<div className="reject-transactions__description">
|
||||
{ t('rejectTxsDescription', [unapprovedTxCount]) }
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
import { connect } from 'react-redux'
|
||||
import { compose } from 'recompose'
|
||||
import RejectTransactionsModal from './reject-transactions.component'
|
||||
import withModalProps from '../../../higher-order-components/with-modal-props'
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { unapprovedTxCount } = ownProps
|
||||
|
||||
return {
|
||||
unapprovedTxCount,
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withModalProps,
|
||||
connect(mapStateToProps),
|
||||
)(RejectTransactionsModal)
|
|
@ -1,40 +0,0 @@
|
|||
const Component = require('react').Component
|
||||
const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('../../actions')
|
||||
const QrView = require('../qr-code')
|
||||
const AccountModalContainer = require('./account-modal-container')
|
||||
|
||||
function mapStateToProps (state) {
|
||||
return {
|
||||
Qr: state.appState.modal.modalState.props.Qr,
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return {
|
||||
hideModal: () => {
|
||||
dispatch(actions.hideModal())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
inherits(ShapeshiftDepositTxModal, Component)
|
||||
function ShapeshiftDepositTxModal () {
|
||||
Component.call(this)
|
||||
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(ShapeshiftDepositTxModal)
|
||||
|
||||
ShapeshiftDepositTxModal.prototype.render = function () {
|
||||
const { Qr } = this.props
|
||||
|
||||
return h(AccountModalContainer, {
|
||||
}, [
|
||||
h('div', {}, [
|
||||
h(QrView, {key: 'qr', Qr}),
|
||||
]),
|
||||
])
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default } from './transaction-confirmed.container'
|
|
@ -1,22 +0,0 @@
|
|||
.transaction-confirmed {
|
||||
&__title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 500;
|
||||
padding: 16px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__description {
|
||||
text-align: center;
|
||||
font-size: .875rem;
|
||||
}
|
||||
|
||||
&__content {
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Modal from '../../modal'
|
||||
|
||||
export default class TransactionConfirmed extends PureComponent {
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
onSubmit: PropTypes.func,
|
||||
hideModal: PropTypes.func,
|
||||
}
|
||||
|
||||
handleSubmit = () => {
|
||||
const { hideModal, onSubmit } = this.props
|
||||
|
||||
hideModal()
|
||||
|
||||
if (onSubmit && typeof onSubmit === 'function') {
|
||||
onSubmit()
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { t } = this.context
|
||||
|
||||
return (
|
||||
<Modal
|
||||
onSubmit={this.handleSubmit}
|
||||
submitText={t('ok')}
|
||||
>
|
||||
<div className="transaction-confirmed__content">
|
||||
<img src="images/check-icon.svg" />
|
||||
<div className="transaction-confirmed__title">
|
||||
{ `${t('confirmed')}!` }
|
||||
</div>
|
||||
<div className="transaction-confirmed__description">
|
||||
{ t('initialTransactionConfirmed') }
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
import TransactionConfirmed from './transaction-confirmed.component'
|
||||
import withModalProps from '../../../higher-order-components/with-modal-props'
|
||||
|
||||
export default withModalProps(TransactionConfirmed)
|
|
@ -1 +0,0 @@
|
|||
export { default } from './transaction-details.container'
|
|
@ -1,54 +0,0 @@
|
|||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Modal from '../../modal'
|
||||
import TransactionListItemDetails from '../../transaction-list-item-details'
|
||||
import { hexToDecimal } from '../../../helpers/conversions.util'
|
||||
|
||||
export default class TransactionConfirmed extends PureComponent {
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
hideModal: PropTypes.func,
|
||||
transaction: PropTypes.object,
|
||||
onRetry: PropTypes.func,
|
||||
showRetry: PropTypes.bool,
|
||||
onCancel: PropTypes.func,
|
||||
showCancel: PropTypes.bool,
|
||||
}
|
||||
|
||||
handleSubmit = () => {
|
||||
this.props.hideModal()
|
||||
}
|
||||
|
||||
handleRetry = () => {
|
||||
const { onRetry, hideModal } = this.props
|
||||
|
||||
Promise.resolve(onRetry()).then(() => hideModal())
|
||||
}
|
||||
|
||||
render () {
|
||||
const { t } = this.context
|
||||
const { transaction, showRetry, onCancel, showCancel } = this.props
|
||||
const { txParams: { nonce } = {} } = transaction
|
||||
const decimalNonce = nonce && hexToDecimal(nonce)
|
||||
|
||||
return (
|
||||
<Modal
|
||||
onSubmit={this.handleSubmit}
|
||||
onClose={this.handleSubmit}
|
||||
submitText={t('ok')}
|
||||
headerText={t('transactionWithNonce', [`#${decimalNonce}`])}
|
||||
>
|
||||
<TransactionListItemDetails
|
||||
transaction={transaction}
|
||||
onRetry={this.handleRetry}
|
||||
showRetry={showRetry}
|
||||
onCancel={() => onCancel()}
|
||||
showCancel={showCancel}
|
||||
/>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
import TransactionDetails from './transaction-details.component'
|
||||
import withModalProps from '../../../higher-order-components/with-modal-props'
|
||||
|
||||
export default withModalProps(TransactionDetails)
|
|
@ -1 +0,0 @@
|
|||
export { default } from './welcome-beta.container'
|
|
@ -1,30 +0,0 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Modal, { ModalContent } from '../../modal'
|
||||
|
||||
const TransactionConfirmed = (props, context) => {
|
||||
const { t } = context
|
||||
const { hideModal } = props
|
||||
|
||||
return (
|
||||
<Modal
|
||||
onSubmit={() => hideModal()}
|
||||
submitText={t('ok')}
|
||||
>
|
||||
<ModalContent
|
||||
title={t('uiWelcome')}
|
||||
description={t('uiWelcomeMessage')}
|
||||
/>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
TransactionConfirmed.contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
TransactionConfirmed.propTypes = {
|
||||
hideModal: PropTypes.func,
|
||||
}
|
||||
|
||||
export default TransactionConfirmed
|
|
@ -1,4 +0,0 @@
|
|||
import WelcomeBeta from './welcome-beta.component'
|
||||
import withModalProps from '../../../higher-order-components/with-modal-props'
|
||||
|
||||
export default withModalProps(WelcomeBeta)
|
|
@ -1,2 +0,0 @@
|
|||
import NetworkDisplay from './network-display.container'
|
||||
module.exports = NetworkDisplay
|
|
@ -1,62 +0,0 @@
|
|||
.network-display {
|
||||
&__container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
background-color: lighten(rgb(125, 128, 130), 45%);
|
||||
padding: 0 10px;
|
||||
border-radius: 4px;
|
||||
height: 25px;
|
||||
|
||||
&--mainnet {
|
||||
background-color: lighten($blue-lagoon, 68%);
|
||||
}
|
||||
|
||||
&--ropsten {
|
||||
background-color: lighten($crimson, 45%);
|
||||
}
|
||||
|
||||
&--kovan {
|
||||
background-color: lighten($purple, 65%);
|
||||
}
|
||||
|
||||
&--rinkeby {
|
||||
background-color: lighten($tulip-tree, 35%);
|
||||
}
|
||||
|
||||
&--poa {
|
||||
background-color: lighten(#5c34a2, 45%);
|
||||
}
|
||||
}
|
||||
|
||||
&__name {
|
||||
font-size: .75rem;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
border-radius: 10px;
|
||||
|
||||
&--mainnet {
|
||||
background-color: $blue-lagoon;
|
||||
}
|
||||
|
||||
&--ropsten {
|
||||
background-color: $crimson;
|
||||
}
|
||||
|
||||
&--kovan {
|
||||
background-color: $purple;
|
||||
}
|
||||
|
||||
&--rinkeby {
|
||||
background-color: $tulip-tree;
|
||||
}
|
||||
|
||||
&--poa {
|
||||
background-color: #5c34a2;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import {
|
||||
MAINNET_CODE,
|
||||
ROPSTEN_CODE,
|
||||
RINKEYBY_CODE,
|
||||
KOVAN_CODE,
|
||||
POA_CODE,
|
||||
} from '../../../../app/scripts/controllers/network/enums'
|
||||
|
||||
const networkToClassHash = {
|
||||
[MAINNET_CODE]: 'mainnet',
|
||||
[ROPSTEN_CODE]: 'ropsten',
|
||||
[RINKEYBY_CODE]: 'rinkeby',
|
||||
[KOVAN_CODE]: 'kovan',
|
||||
[POA_CODE]: 'poa',
|
||||
}
|
||||
|
||||
export default class NetworkDisplay extends Component {
|
||||
static propTypes = {
|
||||
network: PropTypes.string,
|
||||
provider: PropTypes.object,
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
renderNetworkIcon () {
|
||||
const { network } = this.props
|
||||
const networkClass = networkToClassHash[network]
|
||||
|
||||
return networkClass
|
||||
? <div className={`network-display__icon network-display__icon--${networkClass}`} />
|
||||
: <div
|
||||
className="i fa fa-question-circle fa-med"
|
||||
style={{
|
||||
margin: '0 4px',
|
||||
color: 'rgb(125, 128, 130)',
|
||||
}}
|
||||
/>
|
||||
}
|
||||
|
||||
render () {
|
||||
const { network, provider: { type } } = this.props
|
||||
const networkClass = networkToClassHash[network]
|
||||
|
||||
return (
|
||||
<div className={classnames(
|
||||
'network-display__container',
|
||||
networkClass && ('network-display__container--' + networkClass)
|
||||
)}>
|
||||
{
|
||||
networkClass
|
||||
? <div className={`network-display__icon network-display__icon--${networkClass}`} />
|
||||
: <div
|
||||
className="i fa fa-question-circle fa-med"
|
||||
style={{
|
||||
margin: '0 4px',
|
||||
color: 'rgb(125, 128, 130)',
|
||||
}}
|
||||
/>
|
||||
}
|
||||
<div className="network-display__name">
|
||||
{ this.context.t(type) }
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
import { connect } from 'react-redux'
|
||||
import NetworkDisplay from './network-display.component'
|
||||
|
||||
const mapStateToProps = ({ metamask: { network, provider } }) => {
|
||||
return {
|
||||
network,
|
||||
provider,
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(NetworkDisplay)
|
|
@ -1,4 +0,0 @@
|
|||
import PageContainerHeader from './page-container-header'
|
||||
import PageContainerFooter from './page-container-footer'
|
||||
export { default } from './page-container.component'
|
||||
export { PageContainerHeader, PageContainerFooter }
|
|
@ -1,211 +0,0 @@
|
|||
.page-container {
|
||||
width: 408px;
|
||||
background-color: $white;
|
||||
box-shadow: 0 0 7px 0 rgba(0, 0, 0, .08);
|
||||
z-index: 25;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
border-radius: 8px;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
border-bottom: 1px solid $geyser;
|
||||
padding: 16px;
|
||||
flex: 0 0 auto;
|
||||
position: relative;
|
||||
|
||||
&--no-padding-bottom {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__header-close {
|
||||
color: $tundora;
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
|
||||
&::after {
|
||||
content: '\00D7';
|
||||
font-size: 40px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&__header-row {
|
||||
padding-bottom: 10px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
border-top: 1px solid $geyser;
|
||||
flex: 0 0 auto;
|
||||
|
||||
.btn-default,
|
||||
.btn-confirm {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
justify-content: center;
|
||||
padding: 16px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
justify-content: space-around;
|
||||
padding: 0 16px 16px;
|
||||
flex: 0 0 auto;
|
||||
|
||||
a, a:hover {
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
color: #2f9ae0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__footer-button {
|
||||
height: 55px;
|
||||
font-size: 1rem;
|
||||
text-transform: uppercase;
|
||||
margin-right: 16px;
|
||||
|
||||
&:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__back-button {
|
||||
color: #2f9ae0;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
&__title {
|
||||
color: $black;
|
||||
font-size: 2rem;
|
||||
font-weight: 500;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
padding-top: .5rem;
|
||||
line-height: initial;
|
||||
font-size: .9rem;
|
||||
color: $gray;
|
||||
}
|
||||
|
||||
&__tabs {
|
||||
display: flex;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
&__tab {
|
||||
min-width: 5rem;
|
||||
padding: 8px;
|
||||
color: $dusty-gray;
|
||||
font-family: Roboto;
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
border-bottom: none;
|
||||
margin-right: 16px;
|
||||
|
||||
&:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&--selected {
|
||||
color: $curious-blue;
|
||||
border-bottom: 2px solid $curious-blue;
|
||||
}
|
||||
}
|
||||
|
||||
&--full-width {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
&--full-height {
|
||||
height: 100% !important;
|
||||
max-height: initial !important;
|
||||
min-height: initial !important;
|
||||
}
|
||||
|
||||
&__content {
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&__warning-container {
|
||||
background: $linen;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
&__warning-message {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
&__warning-title {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&__warning-icon {
|
||||
padding-top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 250px) {
|
||||
.page-container {
|
||||
&__footer {
|
||||
flex-flow: column-reverse;
|
||||
}
|
||||
|
||||
&__footer-button {
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
margin-right: 0;
|
||||
|
||||
&:first-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 575px) {
|
||||
.page-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
background-color: $white;
|
||||
border-radius: 0;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 576px) {
|
||||
.page-container {
|
||||
max-height: 82vh;
|
||||
min-height: 570px;
|
||||
flex: 0 0 auto;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default class PageContainerContent extends Component {
|
||||
|
||||
static propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className="page-container__content">
|
||||
{this.props.children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default } from './page-container-footer.component'
|
|
@ -1,66 +0,0 @@
|
|||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Button from '../../button'
|
||||
|
||||
export default class PageContainerFooter extends Component {
|
||||
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
onCancel: PropTypes.func,
|
||||
cancelText: PropTypes.string,
|
||||
onSubmit: PropTypes.func,
|
||||
submitText: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
submitButtonType: PropTypes.string,
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
children,
|
||||
onCancel,
|
||||
cancelText,
|
||||
onSubmit,
|
||||
submitText,
|
||||
disabled,
|
||||
submitButtonType,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<div className="page-container__footer">
|
||||
|
||||
<header>
|
||||
<Button
|
||||
type="default"
|
||||
large
|
||||
className="page-container__footer-button"
|
||||
onClick={e => onCancel(e)}
|
||||
>
|
||||
{ cancelText || this.context.t('cancel') }
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type={submitButtonType || 'primary'}
|
||||
large
|
||||
className="page-container__footer-button"
|
||||
disabled={disabled}
|
||||
onClick={e => onSubmit(e)}
|
||||
>
|
||||
{ submitText || this.context.t('next') }
|
||||
</Button>
|
||||
</header>
|
||||
|
||||
{children && (
|
||||
<footer>
|
||||
{children}
|
||||
</footer>
|
||||
)}
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
import React from 'react'
|
||||
import assert from 'assert'
|
||||
import { shallow } from 'enzyme'
|
||||
import sinon from 'sinon'
|
||||
import Button from '../../../button'
|
||||
import PageFooter from '../page-container-footer.component'
|
||||
|
||||
describe('Page Footer', () => {
|
||||
let wrapper
|
||||
const onCancel = sinon.spy()
|
||||
const onSubmit = sinon.spy()
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(<PageFooter
|
||||
onCancel = {onCancel}
|
||||
onSubmit = {onSubmit}
|
||||
cancelText = {'Cancel'}
|
||||
submitText = {'Submit'}
|
||||
disabled = {false}
|
||||
submitButtonType = {'Test Type'}
|
||||
/>)
|
||||
})
|
||||
|
||||
it('renders page container footer', () => {
|
||||
assert.equal(wrapper.find('.page-container__footer').length, 1)
|
||||
})
|
||||
|
||||
it('should render a footer inside page-container__footer when given children', () => {
|
||||
const wrapper = shallow(
|
||||
<PageFooter>
|
||||
<div>Works</div>
|
||||
</PageFooter>,
|
||||
{ context: { t: sinon.spy((k) => `[${k}]`) } }
|
||||
)
|
||||
|
||||
assert.equal(wrapper.find('.page-container__footer footer').length, 1)
|
||||
})
|
||||
|
||||
it('renders two button components', () => {
|
||||
assert.equal(wrapper.find(Button).length, 2)
|
||||
})
|
||||
|
||||
describe('Cancel Button', () => {
|
||||
|
||||
it('has button type of default', () => {
|
||||
assert.equal(wrapper.find('.page-container__footer-button').first().prop('type'), 'default')
|
||||
})
|
||||
|
||||
it('has children text of Cancel', () => {
|
||||
assert.equal(wrapper.find('.page-container__footer-button').first().prop('children'), 'Cancel')
|
||||
})
|
||||
|
||||
it('should call cancel when click is simulated', () => {
|
||||
wrapper.find('.page-container__footer-button').first().prop('onClick')()
|
||||
assert.equal(onCancel.callCount, 1)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('Submit Button', () => {
|
||||
|
||||
it('assigns button type based on props', () => {
|
||||
assert.equal(wrapper.find('.page-container__footer-button').last().prop('type'), 'Test Type')
|
||||
})
|
||||
|
||||
it('has disabled prop', () => {
|
||||
assert.equal(wrapper.find('.page-container__footer-button').last().prop('disabled'), false)
|
||||
})
|
||||
|
||||
it('has children text when submitText prop exists', () => {
|
||||
assert.equal(wrapper.find('.page-container__footer-button').last().prop('children'), 'Submit')
|
||||
})
|
||||
|
||||
it('should call submit when click is simulated', () => {
|
||||
wrapper.find('.page-container__footer-button').last().prop('onClick')()
|
||||
assert.equal(onSubmit.callCount, 1)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1 +0,0 @@
|
|||
export { default } from './page-container-header.component'
|
|
@ -1,80 +0,0 @@
|
|||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
|
||||
export default class PageContainerHeader extends Component {
|
||||
static propTypes = {
|
||||
title: PropTypes.string,
|
||||
subtitle: PropTypes.string,
|
||||
onClose: PropTypes.func,
|
||||
showBackButton: PropTypes.bool,
|
||||
onBackButtonClick: PropTypes.func,
|
||||
backButtonStyles: PropTypes.object,
|
||||
backButtonString: PropTypes.string,
|
||||
tabs: PropTypes.node,
|
||||
}
|
||||
|
||||
renderTabs () {
|
||||
const { tabs } = this.props
|
||||
|
||||
return tabs && (
|
||||
<ul className="page-container__tabs">
|
||||
{ tabs }
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
renderHeaderRow () {
|
||||
const { showBackButton, onBackButtonClick, backButtonStyles, backButtonString } = this.props
|
||||
|
||||
return showBackButton && (
|
||||
<div className="page-container__header-row">
|
||||
<span
|
||||
className="page-container__back-button"
|
||||
onClick={onBackButtonClick}
|
||||
style={backButtonStyles}
|
||||
>
|
||||
{ backButtonString || 'Back' }
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const { title, subtitle, onClose, tabs } = this.props
|
||||
|
||||
return (
|
||||
<div className={
|
||||
classnames(
|
||||
'page-container__header',
|
||||
{ 'page-container__header--no-padding-bottom': Boolean(tabs) }
|
||||
)
|
||||
}>
|
||||
|
||||
{ this.renderHeaderRow() }
|
||||
|
||||
{
|
||||
title && <div className="page-container__title">
|
||||
{ title }
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
subtitle && <div className="page-container__subtitle">
|
||||
{ subtitle }
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
onClose && <div
|
||||
className="page-container__header-close"
|
||||
onClick={() => onClose()}
|
||||
/>
|
||||
}
|
||||
|
||||
{ this.renderTabs() }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
import React from 'react'
|
||||
import assert from 'assert'
|
||||
import { shallow } from 'enzyme'
|
||||
import sinon from 'sinon'
|
||||
import PageContainerHeader from '../page-container-header.component'
|
||||
|
||||
describe('Page Container Header', () => {
|
||||
let wrapper, style, onBackButtonClick, onClose
|
||||
|
||||
beforeEach(() => {
|
||||
style = {test: 'style'}
|
||||
onBackButtonClick = sinon.spy()
|
||||
onClose = sinon.spy()
|
||||
|
||||
wrapper = shallow(<PageContainerHeader
|
||||
showBackButton = {true}
|
||||
onBackButtonClick = {onBackButtonClick}
|
||||
backButtonStyles = {style}
|
||||
title = {'Test Title'}
|
||||
subtitle = {'Test Subtitle'}
|
||||
tabs = {'Test Tab'}
|
||||
onClose = {onClose}
|
||||
/>)
|
||||
})
|
||||
|
||||
describe('Render Header Row', () => {
|
||||
|
||||
it('renders back button', () => {
|
||||
assert.equal(wrapper.find('.page-container__back-button').length, 1)
|
||||
assert.equal(wrapper.find('.page-container__back-button').text(), 'Back')
|
||||
})
|
||||
|
||||
it('ensures style prop', () => {
|
||||
assert.equal(wrapper.find('.page-container__back-button').props().style, style)
|
||||
})
|
||||
|
||||
it('should call back button when click is simulated', () => {
|
||||
wrapper.find('.page-container__back-button').prop('onClick')()
|
||||
assert.equal(onBackButtonClick.callCount, 1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Render', () => {
|
||||
let header, headerRow, pageTitle, pageSubtitle, pageClose, pageTab
|
||||
|
||||
beforeEach(() => {
|
||||
header = wrapper.find('.page-container__header--no-padding-bottom')
|
||||
headerRow = wrapper.find('.page-container__header-row')
|
||||
pageTitle = wrapper.find('.page-container__title')
|
||||
pageSubtitle = wrapper.find('.page-container__subtitle')
|
||||
pageClose = wrapper.find('.page-container__header-close')
|
||||
pageTab = wrapper.find('.page-container__tabs')
|
||||
})
|
||||
|
||||
it('renders page container', () => {
|
||||
assert.equal(header.length, 1)
|
||||
assert.equal(headerRow.length, 1)
|
||||
assert.equal(pageTitle.length, 1)
|
||||
assert.equal(pageSubtitle.length, 1)
|
||||
assert.equal(pageClose.length, 1)
|
||||
assert.equal(pageTab.length, 1)
|
||||
})
|
||||
|
||||
it('renders title', () => {
|
||||
assert.equal(pageTitle.text(), 'Test Title')
|
||||
})
|
||||
|
||||
it('renders subtitle', () => {
|
||||
assert.equal(pageSubtitle.text(), 'Test Subtitle')
|
||||
})
|
||||
|
||||
it('renders tabs', () => {
|
||||
assert.equal(pageTab.text(), 'Test Tab')
|
||||
})
|
||||
|
||||
it('should call close when click is simulated', () => {
|
||||
pageClose.prop('onClick')()
|
||||
assert.equal(onClose.callCount, 1)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
|
@ -1,122 +0,0 @@
|
|||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import PageContainerHeader from './page-container-header'
|
||||
import PageContainerFooter from './page-container-footer'
|
||||
|
||||
export default class PageContainer extends PureComponent {
|
||||
static propTypes = {
|
||||
// PageContainerHeader props
|
||||
backButtonString: PropTypes.string,
|
||||
backButtonStyles: PropTypes.object,
|
||||
onBackButtonClick: PropTypes.func,
|
||||
onClose: PropTypes.func,
|
||||
showBackButton: PropTypes.bool,
|
||||
subtitle: PropTypes.string,
|
||||
title: PropTypes.string.isRequired,
|
||||
// Tabs-related props
|
||||
defaultActiveTabIndex: PropTypes.number,
|
||||
tabsComponent: PropTypes.node,
|
||||
// Content props
|
||||
contentComponent: PropTypes.node,
|
||||
// PageContainerFooter props
|
||||
cancelText: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
onCancel: PropTypes.func,
|
||||
onSubmit: PropTypes.func,
|
||||
submitText: PropTypes.string,
|
||||
}
|
||||
|
||||
state = {
|
||||
activeTabIndex: this.props.defaultActiveTabIndex || 0,
|
||||
}
|
||||
|
||||
handleTabClick (activeTabIndex) {
|
||||
this.setState({ activeTabIndex })
|
||||
}
|
||||
|
||||
renderTabs () {
|
||||
const { tabsComponent } = this.props
|
||||
|
||||
if (!tabsComponent) {
|
||||
return
|
||||
}
|
||||
|
||||
const numberOfTabs = React.Children.count(tabsComponent.props.children)
|
||||
|
||||
return React.Children.map(tabsComponent.props.children, (child, tabIndex) => {
|
||||
return child && React.cloneElement(child, {
|
||||
onClick: index => this.handleTabClick(index),
|
||||
tabIndex,
|
||||
isActive: numberOfTabs > 1 && tabIndex === this.state.activeTabIndex,
|
||||
key: tabIndex,
|
||||
className: 'page-container__tab',
|
||||
activeClassName: 'page-container__tab--selected',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
renderActiveTabContent () {
|
||||
const { tabsComponent } = this.props
|
||||
const { children } = tabsComponent.props
|
||||
const { activeTabIndex } = this.state
|
||||
|
||||
return children[activeTabIndex]
|
||||
? children[activeTabIndex].props.children
|
||||
: children.props.children
|
||||
}
|
||||
|
||||
renderContent () {
|
||||
const { contentComponent, tabsComponent } = this.props
|
||||
|
||||
if (contentComponent) {
|
||||
return contentComponent
|
||||
} else if (tabsComponent) {
|
||||
return this.renderActiveTabContent()
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
title,
|
||||
subtitle,
|
||||
onClose,
|
||||
showBackButton,
|
||||
onBackButtonClick,
|
||||
backButtonStyles,
|
||||
backButtonString,
|
||||
onCancel,
|
||||
cancelText,
|
||||
onSubmit,
|
||||
submitText,
|
||||
disabled,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<div className="page-container">
|
||||
<PageContainerHeader
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
onClose={onClose}
|
||||
showBackButton={showBackButton}
|
||||
onBackButtonClick={onBackButtonClick}
|
||||
backButtonStyles={backButtonStyles}
|
||||
backButtonString={backButtonString}
|
||||
tabs={this.renderTabs()}
|
||||
/>
|
||||
<div className="page-container__content">
|
||||
{ this.renderContent() }
|
||||
</div>
|
||||
<PageContainerFooter
|
||||
onCancel={onCancel}
|
||||
cancelText={cancelText}
|
||||
onSubmit={onSubmit}
|
||||
submitText={submitText}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
@import './unlock-page/index';
|
||||
|
||||
@import './add-token/index';
|
||||
|
||||
@import './confirm-add-token/index';
|
||||
|
||||
@import './settings/index';
|
|
@ -1,2 +0,0 @@
|
|||
import UnlockPage from './unlock-page.container'
|
||||
module.exports = UnlockPage
|
|
@ -1,48 +0,0 @@
|
|||
.unlock-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
width: 357px;
|
||||
padding: 30px;
|
||||
font-weight: 400;
|
||||
color: $silver-chalice;
|
||||
|
||||
&__container {
|
||||
background: $white;
|
||||
display: flex;
|
||||
align-self: stretch;
|
||||
justify-content: center;
|
||||
flex: 1 0 auto;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
&__title {
|
||||
margin-top: 5px;
|
||||
font-size: 2rem;
|
||||
font-weight: 800;
|
||||
color: $tundora;
|
||||
}
|
||||
|
||||
&__form {
|
||||
width: 100%;
|
||||
margin: 56px 0 8px;
|
||||
}
|
||||
|
||||
&__links {
|
||||
margin-top: 25px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__link {
|
||||
cursor: pointer;
|
||||
|
||||
&--import {
|
||||
color: $ecstasy;
|
||||
}
|
||||
|
||||
&--use-classic {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Button from '@material-ui/core/Button'
|
||||
import TextField from '../../text-field'
|
||||
import { ENVIRONMENT_TYPE_POPUP } from '../../../../../app/scripts/lib/enums'
|
||||
import { getEnvironmentType } from '../../../../../app/scripts/lib/util'
|
||||
import { EventEmitter } from 'events'
|
||||
import { DEFAULT_ROUTE, RESTORE_VAULT_ROUTE } from '../../../routes'
|
||||
|
||||
export default class UnlockPage extends Component {
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
forgotPassword: PropTypes.func,
|
||||
tryUnlockMetamask: PropTypes.func,
|
||||
markPasswordForgotten: PropTypes.func,
|
||||
history: PropTypes.object,
|
||||
isUnlocked: PropTypes.bool,
|
||||
useOldInterface: PropTypes.func,
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
password: '',
|
||||
error: null,
|
||||
}
|
||||
|
||||
this.submitting = false
|
||||
this.animationEventEmitter = new EventEmitter()
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
const { isUnlocked, history } = this.props
|
||||
|
||||
if (isUnlocked) {
|
||||
history.push(DEFAULT_ROUTE)
|
||||
}
|
||||
}
|
||||
|
||||
async handleSubmit (event) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
||||
const { password } = this.state
|
||||
const { tryUnlockMetamask, history } = this.props
|
||||
|
||||
if (password === '' || this.submitting) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setState({ error: null })
|
||||
this.submitting = true
|
||||
|
||||
try {
|
||||
await tryUnlockMetamask(password)
|
||||
this.submitting = false
|
||||
history.push(DEFAULT_ROUTE)
|
||||
} catch ({ message }) {
|
||||
this.setState({ error: message })
|
||||
this.submitting = false
|
||||
}
|
||||
}
|
||||
|
||||
handleInputChange ({ target }) {
|
||||
this.setState({ password: target.value, error: null })
|
||||
}
|
||||
|
||||
renderSubmitButton () {
|
||||
const style = {
|
||||
backgroundColor: '#f7861c',
|
||||
color: 'white',
|
||||
marginTop: '20px',
|
||||
height: '60px',
|
||||
fontWeight: '400',
|
||||
boxShadow: 'none',
|
||||
borderRadius: '4px',
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
type="submit"
|
||||
style={style}
|
||||
disabled={!this.state.password}
|
||||
fullWidth
|
||||
variant="raised"
|
||||
size="large"
|
||||
onClick={event => this.handleSubmit(event)}
|
||||
disableRipple
|
||||
>
|
||||
{ this.context.t('login') }
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const { password, error } = this.state
|
||||
const { t } = this.context
|
||||
const { markPasswordForgotten, history } = this.props
|
||||
|
||||
return (
|
||||
<div className="unlock-page__container">
|
||||
<div className="unlock-page">
|
||||
<h1 className="unlock-page__title">
|
||||
{ t('welcomeBack') }
|
||||
</h1>
|
||||
<div>{ t('unlockMessage') }</div>
|
||||
<form
|
||||
className="unlock-page__form"
|
||||
onSubmit={event => this.handleSubmit(event)}
|
||||
>
|
||||
<TextField
|
||||
id="password"
|
||||
label={t('password')}
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={event => this.handleInputChange(event)}
|
||||
error={error}
|
||||
autoFocus
|
||||
autoComplete="current-password"
|
||||
material
|
||||
fullWidth
|
||||
/>
|
||||
</form>
|
||||
{ this.renderSubmitButton() }
|
||||
<div className="unlock-page__links">
|
||||
<div
|
||||
className="unlock-page__link"
|
||||
onClick={() => {
|
||||
markPasswordForgotten()
|
||||
history.push(RESTORE_VAULT_ROUTE)
|
||||
|
||||
if (getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP) {
|
||||
global.platform.openExtensionInBrowser()
|
||||
}
|
||||
}}
|
||||
>
|
||||
{ t('restoreFromSeed') }
|
||||
</div>
|
||||
<div
|
||||
className="unlock-page__link unlock-page__link--import"
|
||||
onClick={() => {
|
||||
markPasswordForgotten()
|
||||
history.push(RESTORE_VAULT_ROUTE)
|
||||
|
||||
if (getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP) {
|
||||
global.platform.openExtensionInBrowser()
|
||||
}
|
||||
}}
|
||||
>
|
||||
{ t('importUsingSeed') }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
import { connect } from 'react-redux'
|
||||
import { withRouter } from 'react-router-dom'
|
||||
import { compose } from 'recompose'
|
||||
|
||||
const {
|
||||
tryUnlockMetamask,
|
||||
forgotPassword,
|
||||
markPasswordForgotten,
|
||||
} = require('../../../actions')
|
||||
|
||||
import UnlockPage from './unlock-page.component'
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const { metamask: { isUnlocked } } = state
|
||||
return {
|
||||
isUnlocked,
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
forgotPassword: () => dispatch(forgotPassword()),
|
||||
tryUnlockMetamask: password => dispatch(tryUnlockMetamask(password)),
|
||||
markPasswordForgotten: () => dispatch(markPasswordForgotten()),
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withRouter,
|
||||
connect(mapStateToProps, mapDispatchToProps)
|
||||
)(UnlockPage)
|
|
@ -1,63 +0,0 @@
|
|||
const Component = require('react').Component
|
||||
const h = require('react-hyperscript')
|
||||
const qrCode = require('qrcode-npm').qrcode
|
||||
const inherits = require('util').inherits
|
||||
const connect = require('react-redux').connect
|
||||
const { isHexPrefixed } = require('ethereumjs-util')
|
||||
const ReadOnlyInput = require('./readonly-input')
|
||||
const { checksumAddress } = require('../util')
|
||||
|
||||
module.exports = connect(mapStateToProps)(QrCodeView)
|
||||
|
||||
function mapStateToProps (state) {
|
||||
return {
|
||||
// Qr code is not fetched from state. 'message' and 'data' props are passed instead.
|
||||
buyView: state.appState.buyView,
|
||||
warning: state.appState.warning,
|
||||
}
|
||||
}
|
||||
|
||||
inherits(QrCodeView, Component)
|
||||
|
||||
function QrCodeView () {
|
||||
Component.call(this)
|
||||
}
|
||||
|
||||
QrCodeView.prototype.render = function () {
|
||||
const props = this.props
|
||||
const { message, data } = props.Qr
|
||||
const address = `${isHexPrefixed(data) ? 'ethereum:' : ''}${checksumAddress(data)}`
|
||||
const qrImage = qrCode(4, 'M')
|
||||
qrImage.addData(address)
|
||||
qrImage.make()
|
||||
|
||||
return h('.div.flex-column.flex-center', [
|
||||
Array.isArray(message)
|
||||
? h('.message-container', this.renderMultiMessage())
|
||||
: message && h('.qr-header', message),
|
||||
|
||||
this.props.warning ? this.props.warning && h('span.error.flex-center', {
|
||||
style: {
|
||||
},
|
||||
},
|
||||
this.props.warning) : null,
|
||||
|
||||
h('.div.qr-wrapper', {
|
||||
style: {},
|
||||
dangerouslySetInnerHTML: {
|
||||
__html: qrImage.createTableTag(4),
|
||||
},
|
||||
}),
|
||||
h(ReadOnlyInput, {
|
||||
wrapperClass: 'ellip-address-wrapper',
|
||||
inputClass: 'qr-ellip-address',
|
||||
value: checksumAddress(data),
|
||||
}),
|
||||
])
|
||||
}
|
||||
|
||||
QrCodeView.prototype.renderMultiMessage = function () {
|
||||
var Qr = this.props.Qr
|
||||
var multiMessage = Qr.message.map((message) => h('.qr-message', message))
|
||||
return multiMessage
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
import SelectedAccount from './selected-account.container'
|
||||
module.exports = SelectedAccount
|
|
@ -1,38 +0,0 @@
|
|||
.selected-account {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
&__name {
|
||||
max-width: 200px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__address {
|
||||
font-size: .75rem;
|
||||
color: $silver-chalice;
|
||||
}
|
||||
|
||||
&__clickable {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 5px 15px;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #e8e6e8;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #d9d7da;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import copyToClipboard from 'copy-to-clipboard'
|
||||
import { addressSlicer, checksumAddress } from '../../util'
|
||||
|
||||
const Tooltip = require('../tooltip-v2.js').default
|
||||
|
||||
class SelectedAccount extends Component {
|
||||
state = {
|
||||
copied: false,
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
selectedAddress: PropTypes.string,
|
||||
selectedIdentity: PropTypes.object,
|
||||
}
|
||||
|
||||
render () {
|
||||
const { t } = this.context
|
||||
const { selectedAddress, selectedIdentity } = this.props
|
||||
const checksummedAddress = checksumAddress(selectedAddress)
|
||||
|
||||
return (
|
||||
<div className="selected-account">
|
||||
<Tooltip
|
||||
position="bottom"
|
||||
title={this.state.copied ? t('copiedExclamation') : t('copyToClipboard')}
|
||||
>
|
||||
<div
|
||||
className="selected-account__clickable"
|
||||
onClick={() => {
|
||||
this.setState({ copied: true })
|
||||
setTimeout(() => this.setState({ copied: false }), 3000)
|
||||
copyToClipboard(checksummedAddress)
|
||||
}}
|
||||
>
|
||||
<div className="selected-account__name">
|
||||
{ selectedIdentity.name }
|
||||
</div>
|
||||
<div className="selected-account__address">
|
||||
{ addressSlicer(checksummedAddress) }
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default SelectedAccount
|
|
@ -1,13 +0,0 @@
|
|||
import { connect } from 'react-redux'
|
||||
import SelectedAccount from './selected-account.component'
|
||||
|
||||
const selectors = require('../../selectors')
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
selectedAddress: selectors.getSelectedAddress(state),
|
||||
selectedIdentity: selectors.getSelectedIdentity(state),
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(SelectedAccount)
|
|
@ -1,16 +0,0 @@
|
|||
import React from 'react'
|
||||
import assert from 'assert'
|
||||
import { render } from 'enzyme'
|
||||
import SelectedAccount from '../selected-account.component'
|
||||
|
||||
describe('SelectedAccount Component', () => {
|
||||
it('should render checksummed address', () => {
|
||||
const wrapper = render(<SelectedAccount
|
||||
selectedAddress="0x1b82543566f41a7db9a9a75fc933c340ffb55c9d"
|
||||
selectedIdentity={{ name: 'testName' }}
|
||||
/>, { context: { t: () => {}}})
|
||||
// Checksummed version of address is displayed
|
||||
assert.equal(wrapper.find('.selected-account__address').text(), '0x1B82...5C9D')
|
||||
assert.equal(wrapper.find('.selected-account__name').text(), 'testName')
|
||||
})
|
||||
})
|
|
@ -1 +0,0 @@
|
|||
export { default } from './sidebar.component'
|
|
@ -1,74 +0,0 @@
|
|||
.sidebar-right-enter {
|
||||
transition: transform 300ms ease-in-out;
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.sidebar-right-enter.sidebar-right-enter-active {
|
||||
transition: transform 300ms ease-in-out;
|
||||
transform: translateX(0%);
|
||||
}
|
||||
|
||||
.sidebar-right-leave {
|
||||
transition: transform 200ms ease-out;
|
||||
transform: translateX(0%);
|
||||
}
|
||||
|
||||
.sidebar-right-leave.sidebar-right-leave-active {
|
||||
transition: transform 200ms ease-out;
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.sidebar-left-enter {
|
||||
transition: transform 300ms ease-in-out;
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.sidebar-left-enter.sidebar-left-enter-active {
|
||||
transition: transform 300ms ease-in-out;
|
||||
transform: translateX(0%);
|
||||
}
|
||||
|
||||
.sidebar-left-leave {
|
||||
transition: transform 200ms ease-out;
|
||||
transform: translateX(0%);
|
||||
}
|
||||
|
||||
.sidebar-left-leave.sidebar-left-leave-active {
|
||||
transition: transform 200ms ease-out;
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.sidebar-left {
|
||||
flex: 1 0 230px;
|
||||
background: rgb(250, 250, 250);
|
||||
z-index: $sidebar-z-index;
|
||||
position: fixed;
|
||||
left: 15%;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
will-change: transform;
|
||||
overflow-y: auto;
|
||||
box-shadow: rgba(0, 0, 0, .15) 2px 2px 4px;
|
||||
width: 85%;
|
||||
height: 100%;
|
||||
|
||||
@media screen and (min-width: 769px) {
|
||||
width: 408px;
|
||||
left: calc(100% - 408px);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-overlay {
|
||||
z-index: $sidebar-overlay-z-index;
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
background-color: rgba(0, 0, 0, .3);
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { CSSTransitionGroup } from 'react-transition-group'
|
||||
import WalletView from '../wallet-view'
|
||||
import { WALLET_VIEW_SIDEBAR } from './sidebar.constants'
|
||||
|
||||
export default class Sidebar extends Component {
|
||||
|
||||
static propTypes = {
|
||||
sidebarOpen: PropTypes.bool,
|
||||
hideSidebar: PropTypes.func,
|
||||
transitionName: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
};
|
||||
|
||||
renderOverlay () {
|
||||
return <div className="sidebar-overlay" onClick={() => this.props.hideSidebar()} />
|
||||
}
|
||||
|
||||
renderSidebarContent () {
|
||||
const { type } = this.props
|
||||
|
||||
switch (type) {
|
||||
case WALLET_VIEW_SIDEBAR:
|
||||
return <WalletView responsiveDisplayClassname={'sidebar-right' } />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
render () {
|
||||
const { transitionName, sidebarOpen } = this.props
|
||||
|
||||
return (
|
||||
<div>
|
||||
<CSSTransitionGroup
|
||||
transitionName={transitionName}
|
||||
transitionEnterTimeout={300}
|
||||
transitionLeaveTimeout={200}
|
||||
>
|
||||
{ sidebarOpen ? this.renderSidebarContent() : null }
|
||||
</CSSTransitionGroup>
|
||||
{ sidebarOpen ? this.renderOverlay() : null }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export const WALLET_VIEW_SIDEBAR = 'wallet-view'
|
|
@ -1,88 +0,0 @@
|
|||
import React from 'react'
|
||||
import assert from 'assert'
|
||||
import { shallow } from 'enzyme'
|
||||
import sinon from 'sinon'
|
||||
import { CSSTransitionGroup } from 'react-transition-group'
|
||||
import Sidebar from '../sidebar.component.js'
|
||||
|
||||
import WalletView from '../../wallet-view'
|
||||
|
||||
const propsMethodSpies = {
|
||||
hideSidebar: sinon.spy(),
|
||||
}
|
||||
|
||||
describe('Sidebar Component', function () {
|
||||
let wrapper
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(<Sidebar
|
||||
sidebarOpen={false}
|
||||
hideSidebar={propsMethodSpies.hideSidebar}
|
||||
transitionName={'someTransition'}
|
||||
type={'wallet-view'}
|
||||
/>)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
propsMethodSpies.hideSidebar.resetHistory()
|
||||
})
|
||||
|
||||
describe('renderOverlay', () => {
|
||||
let renderOverlay
|
||||
|
||||
beforeEach(() => {
|
||||
renderOverlay = shallow(wrapper.instance().renderOverlay())
|
||||
})
|
||||
|
||||
it('should render a overlay element', () => {
|
||||
assert(renderOverlay.hasClass('sidebar-overlay'))
|
||||
})
|
||||
|
||||
it('should pass the correct onClick function to the element', () => {
|
||||
assert.equal(propsMethodSpies.hideSidebar.callCount, 0)
|
||||
renderOverlay.props().onClick()
|
||||
assert.equal(propsMethodSpies.hideSidebar.callCount, 1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('renderSidebarContent', () => {
|
||||
let renderSidebarContent
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper.setProps({ type: 'wallet-view' })
|
||||
renderSidebarContent = wrapper.instance().renderSidebarContent()
|
||||
})
|
||||
|
||||
it('should render sidebar content with the correct props', () => {
|
||||
wrapper.setProps({ type: 'wallet-view' })
|
||||
renderSidebarContent = wrapper.instance().renderSidebarContent()
|
||||
assert.equal(renderSidebarContent.props.responsiveDisplayClassname, 'sidebar-right')
|
||||
})
|
||||
|
||||
it('should not render with an unrecognized type', () => {
|
||||
wrapper.setProps({ type: 'foobar' })
|
||||
renderSidebarContent = wrapper.instance().renderSidebarContent()
|
||||
assert.equal(renderSidebarContent, undefined)
|
||||
})
|
||||
})
|
||||
|
||||
describe('render', () => {
|
||||
it('should render a div with one child', () => {
|
||||
assert(wrapper.is('div'))
|
||||
assert.equal(wrapper.children().length, 1)
|
||||
})
|
||||
|
||||
it('should render the CSSTransitionGroup without any children', () => {
|
||||
assert(wrapper.children().at(0).is(CSSTransitionGroup))
|
||||
assert.equal(wrapper.children().at(0).children().length, 0)
|
||||
})
|
||||
|
||||
it('should render sidebar content and the overlay if sidebarOpen is true', () => {
|
||||
wrapper.setProps({ sidebarOpen: true })
|
||||
assert.equal(wrapper.children().length, 2)
|
||||
assert(wrapper.children().at(1).hasClass('sidebar-overlay'))
|
||||
assert.equal(wrapper.children().at(0).children().length, 1)
|
||||
assert(wrapper.children().at(0).children().at(0).is(WalletView))
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,3 +0,0 @@
|
|||
import Tabs from './tabs.component'
|
||||
import Tab from './tab'
|
||||
export { Tabs, Tab }
|
|
@ -1,11 +0,0 @@
|
|||
@import './tab/index';
|
||||
|
||||
.tabs {
|
||||
&__list {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
background-color: #f9fafa;
|
||||
border-bottom: 1px solid $geyser;
|
||||
padding: 0 16px;
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
import Tab from './tab.component'
|
||||
module.exports = Tab
|
|
@ -1,15 +0,0 @@
|
|||
.tab {
|
||||
color: #8C8E94;
|
||||
font-size: .75rem;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
padding: 8px 0;
|
||||
margin: 0 8px;
|
||||
min-width: 50px;
|
||||
text-align: center;
|
||||
|
||||
&--active {
|
||||
color: $black;
|
||||
border-bottom: 2px solid $curious-blue;
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
|
||||
const Tab = props => {
|
||||
const { name, onClick, isActive, tabIndex, className, activeClassName } = props
|
||||
|
||||
return (
|
||||
<li
|
||||
className={classnames(
|
||||
className,
|
||||
{ [activeClassName]: isActive },
|
||||
)}
|
||||
onClick={event => {
|
||||
event.preventDefault()
|
||||
onClick(tabIndex)
|
||||
}}
|
||||
>
|
||||
{ name }
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
Tab.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
onClick: PropTypes.func,
|
||||
isActive: PropTypes.bool,
|
||||
tabIndex: PropTypes.number,
|
||||
className: PropTypes.string,
|
||||
activeClassName: PropTypes.string,
|
||||
}
|
||||
|
||||
Tab.defaultProps = {
|
||||
className: 'tab',
|
||||
activeClassName: 'tab--active',
|
||||
}
|
||||
|
||||
export default Tab
|
|
@ -1,62 +0,0 @@
|
|||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default class Tabs extends Component {
|
||||
static propTypes = {
|
||||
defaultActiveTabIndex: PropTypes.number,
|
||||
children: PropTypes.node,
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
activeTabIndex: props.defaultActiveTabIndex || 0,
|
||||
}
|
||||
}
|
||||
|
||||
handleTabClick (tabIndex) {
|
||||
const { activeTabIndex } = this.state
|
||||
|
||||
if (tabIndex !== activeTabIndex) {
|
||||
this.setState({
|
||||
activeTabIndex: tabIndex,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
renderTabs () {
|
||||
const numberOfTabs = React.Children.count(this.props.children)
|
||||
|
||||
return React.Children.map(this.props.children, (child, index) => {
|
||||
return child && React.cloneElement(child, {
|
||||
onClick: index => this.handleTabClick(index),
|
||||
tabIndex: index,
|
||||
isActive: numberOfTabs > 1 && index === this.state.activeTabIndex,
|
||||
key: index,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
renderActiveTabContent () {
|
||||
const { children } = this.props
|
||||
const { activeTabIndex } = this.state
|
||||
|
||||
return children[activeTabIndex]
|
||||
? children[activeTabIndex].props.children
|
||||
: children.props.children
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className="tabs">
|
||||
<ul className="tabs__list">
|
||||
{ this.renderTabs() }
|
||||
</ul>
|
||||
<div className="tabs__content">
|
||||
{ this.renderActiveTabContent() }
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
ITCSS
|
||||
|
||||
http://www.creativebloq.com/web-design/manage-large-css-projects-itcss-101517528
|
||||
https://www.xfive.co/blog/itcss-scalable-maintainable-css-architecture/
|
||||
*/
|
||||
|
||||
@import './itcss/settings/index.scss';
|
||||
|
||||
@import './itcss/tools/index.scss';
|
||||
|
||||
@import './itcss/generic/index.scss';
|
||||
|
||||
@import './itcss/base/index.scss';
|
||||
|
||||
@import './itcss/objects/index.scss';
|
||||
|
||||
@import './itcss/components/index.scss';
|
||||
|
||||
@import './itcss/trumps/index.scss';
|
|
@ -1,7 +0,0 @@
|
|||
// Base
|
||||
|
||||
.mouse-user-styles {
|
||||
button:focus {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
.account-details-dropdown {
|
||||
width: 60%;
|
||||
position: absolute;
|
||||
top: 120px;
|
||||
right: 15px;
|
||||
z-index: 2000;
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
.account-dropdown-mini {
|
||||
height: 22px;
|
||||
background-color: $white;
|
||||
font-family: Roboto;
|
||||
line-height: 16px;
|
||||
font-size: 12px;
|
||||
width: 124px;
|
||||
|
||||
&__close-area {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&__list {
|
||||
z-index: 1050;
|
||||
position: absolute;
|
||||
height: 180px;
|
||||
width: 96pxpx;
|
||||
border: 1px solid $geyser;
|
||||
border-radius: 4px;
|
||||
background-color: $white;
|
||||
box-shadow: 0 3px 6px 0 rgba(0 ,0 ,0 ,.11);
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.account-list-item {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.account-list-item__account-name {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.account-list-item__top-row {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.account-list-item__icon {
|
||||
position: initial;
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
.account-dropdown-name {
|
||||
font-family: Roboto;
|
||||
}
|
||||
|
||||
.account-dropdown-balance {
|
||||
color: $dusty-gray;
|
||||
line-height: 19px;
|
||||
}
|
||||
|
||||
.account-dropdown-edit-button {
|
||||
color: $dusty-gray;
|
||||
font-family: Roboto;
|
||||
|
||||
&:hover {
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.account-list-item {
|
||||
&__top-row {
|
||||
display: flex;
|
||||
margin-top: 10px;
|
||||
margin-left: 8px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&__account-balances {
|
||||
height: auto;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
color: #9b9b9b;
|
||||
margin-left: 34px;
|
||||
margin-top: 4px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&__account-name {
|
||||
font-size: 16px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
&__account-primary-balance,
|
||||
&__account-secondary-balance {
|
||||
font-family: Roboto;
|
||||
line-height: 16px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&__account-primary-balance {
|
||||
color: $scorpion;
|
||||
border: none;
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
&__account-secondary-balance {
|
||||
color: $dusty-gray;
|
||||
}
|
||||
|
||||
&__account-address {
|
||||
margin-left: 35px;
|
||||
width: 80%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&__dropdown {
|
||||
&:hover {
|
||||
background: rgba($alto, .2);
|
||||
cursor: pointer;
|
||||
|
||||
input {
|
||||
background: rgba($alto, .1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
.account-menu {
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
top: 58px;
|
||||
width: 310px;
|
||||
|
||||
@media screen and (max-width: 575px) {
|
||||
right: calc(((100vw - 100%) / 2) + 8px);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 576px) {
|
||||
right: calc((100vw - 85vw) / 2);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 769px) {
|
||||
right: calc((100vw - 80vw) / 2);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1281px) {
|
||||
right: calc((100vw - 65vw) / 2);
|
||||
}
|
||||
|
||||
&__icon {
|
||||
margin-left: 20px;
|
||||
cursor: pointer;
|
||||
|
||||
&--disabled {
|
||||
cursor: initial;
|
||||
}
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__logout-button {
|
||||
border: 1px solid $dusty-gray;
|
||||
background-color: transparent;
|
||||
color: $white;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
line-height: 23px;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
&__item-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
&__accounts {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
overflow-y: auto;
|
||||
max-height: 240px;
|
||||
position: relative;
|
||||
z-index: 200;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 575px) {
|
||||
max-height: 215px;
|
||||
}
|
||||
|
||||
.keyring-label {
|
||||
margin-top: 5px;
|
||||
background-color: $dusty-gray;
|
||||
color: $black;
|
||||
font-weight: normal;
|
||||
letter-spacing: .5px;
|
||||
}
|
||||
}
|
||||
|
||||
&__account {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
padding: 16px 14px;
|
||||
flex: 0 0 auto;
|
||||
|
||||
@media screen and (max-width: 575px) {
|
||||
padding: 12px 14px;
|
||||
}
|
||||
|
||||
.remove-account-icon {
|
||||
width: 15px;
|
||||
margin-left: 10px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.remove-account-icon::after {
|
||||
content: '\00D7';
|
||||
font-size: 25px;
|
||||
color: $white;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
margin-top: -5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__account-info {
|
||||
flex: 1 0 auto;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
|
||||
&__check-mark {
|
||||
width: 14px;
|
||||
margin-right: 12px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
&__check-mark-icon {
|
||||
background-image: url("images/check-white.svg");
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
.identicon {
|
||||
margin: 0 12px 0 0;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
&__name {
|
||||
color: $white;
|
||||
font-size: 18px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
&__balance {
|
||||
color: $dusty-gray;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&__action {
|
||||
font-size: 16px;
|
||||
line-height: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
.global-alert {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
background-color: #33A4E7;
|
||||
|
||||
.msg {
|
||||
width: 100%;
|
||||
display: block;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.global-alert.hidden {
|
||||
animation: alertHidden .5s ease forwards;
|
||||
}
|
||||
|
||||
.global-alert.visible {
|
||||
animation: alert .5s ease forwards;
|
||||
}
|
||||
|
||||
/* Animation */
|
||||
@keyframes alert {
|
||||
0% {
|
||||
opacity: 0;
|
||||
top: -50px;
|
||||
padding: 0px;
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 0;
|
||||
padding: 8px;
|
||||
line-height: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes alertHidden {
|
||||
0% {
|
||||
top: 0;
|
||||
opacity: 1;
|
||||
padding: 8px;
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
top: -50px;
|
||||
padding: 0px;
|
||||
line-height: 0px;
|
||||
}
|
||||
}
|
|
@ -1,217 +0,0 @@
|
|||
/*
|
||||
Buttons
|
||||
*/
|
||||
|
||||
.button {
|
||||
height: 44px;
|
||||
background: $white;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
transition: border-color .3s ease;
|
||||
padding: 0 16px;
|
||||
min-width: 140px;
|
||||
width: 100%;
|
||||
text-transform: uppercase;
|
||||
outline: none;
|
||||
font-family: Roboto;
|
||||
|
||||
&--disabled,
|
||||
&[disabled] {
|
||||
cursor: auto;
|
||||
opacity: .5;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: $curious-blue;
|
||||
border: 2px solid $spindle;
|
||||
|
||||
&:active {
|
||||
background: $zumthor;
|
||||
border-color: $curious-blue;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: $curious-blue;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
color: $monzo;
|
||||
border: 2px solid lighten($monzo, 40%);
|
||||
|
||||
&:active {
|
||||
background: lighten($monzo, 55%);
|
||||
border-color: $monzo;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: $monzo;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-default {
|
||||
color: $scorpion;
|
||||
border: 2px solid $dusty-gray;
|
||||
|
||||
&:active {
|
||||
background: $gallery;
|
||||
border-color: $dusty-gray;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: $scorpion;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-confirm {
|
||||
color: $white;
|
||||
border: 2px solid $curious-blue;
|
||||
background-color: $curious-blue;
|
||||
}
|
||||
|
||||
.btn-raised {
|
||||
color: $curious-blue;
|
||||
background-color: $white;
|
||||
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.08);
|
||||
padding: 6px;
|
||||
height: initial;
|
||||
width: initial;
|
||||
min-width: initial;
|
||||
}
|
||||
|
||||
.btn--large {
|
||||
height: 54px;
|
||||
}
|
||||
|
||||
.btn-green {
|
||||
background-color: #02c9b1; // TODO: reusable color in colors.css
|
||||
}
|
||||
|
||||
.btn-clear {
|
||||
background: $white;
|
||||
text-align: center;
|
||||
padding: .8rem 1rem;
|
||||
color: $curious-blue;
|
||||
border: 2px solid $spindle;
|
||||
border-radius: 4px;
|
||||
font-size: .85rem;
|
||||
font-weight: 400;
|
||||
transition: border-color .3s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: $curious-blue;
|
||||
}
|
||||
|
||||
&--disabled,
|
||||
&[disabled] {
|
||||
cursor: auto;
|
||||
opacity: .5;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
background: $white;
|
||||
text-align: center;
|
||||
padding: .9rem 1rem;
|
||||
color: $scorpion;
|
||||
border: 2px solid $dusty-gray;
|
||||
border-radius: 4px;
|
||||
font-size: .85rem;
|
||||
font-weight: 400;
|
||||
transition: border-color .3s ease;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
border-color: $scorpion;
|
||||
}
|
||||
}
|
||||
|
||||
// No longer used in flat design, remove when modal buttons done
|
||||
// div.wallet-btn {
|
||||
// border: 1px solid rgb(91, 93, 103);
|
||||
// border-radius: 2px;
|
||||
// height: 30px;
|
||||
// width: 75px;
|
||||
// font-size: 0.8em;
|
||||
// text-align: center;
|
||||
// line-height: 25px;
|
||||
// }
|
||||
|
||||
// .btn-red {
|
||||
// background: rgba(254, 35, 17, 1);
|
||||
// box-shadow: 0px 3px 6px rgba(254, 35, 17, 0.36);
|
||||
// }
|
||||
|
||||
button[disabled],
|
||||
input[type="submit"][disabled] {
|
||||
cursor: not-allowed;
|
||||
opacity: .5;
|
||||
// background: rgba(197, 197, 197, 1);
|
||||
// box-shadow: 0 3px 6px rgba(197, 197, 197, .36);
|
||||
}
|
||||
|
||||
// button.spaced {
|
||||
// margin: 2px;
|
||||
// }
|
||||
|
||||
// button:not([disabled]):hover, input[type="submit"]:not([disabled]):hover {
|
||||
// transform: scale(1.1);
|
||||
// }
|
||||
// button:not([disabled]):active, input[type="submit"]:not([disabled]):active {
|
||||
// transform: scale(0.95);
|
||||
// }
|
||||
|
||||
button.primary {
|
||||
padding: 8px 12px;
|
||||
background: #f7861c;
|
||||
box-shadow: 0 3px 6px rgba(247, 134, 28, .36);
|
||||
color: $white;
|
||||
font-size: 1.1em;
|
||||
font-family: Roboto;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.btn-light {
|
||||
padding: 8px 12px;
|
||||
// background: #FFFFFF; // $bg-white
|
||||
box-shadow: 0 3px 6px rgba(247, 134, 28, .36);
|
||||
color: #585d67; // TODO: make reusable light button color
|
||||
font-size: 1.1em;
|
||||
font-family: Roboto;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
line-height: 20px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #979797; // #TODO: make reusable light border color
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
// TODO: cleanup: not used anywhere
|
||||
button.btn-thin {
|
||||
border: 1px solid;
|
||||
border-color: #4d4d4d;
|
||||
color: #4d4d4d;
|
||||
background: rgb(255, 174, 41);
|
||||
border-radius: 4px;
|
||||
min-width: 200px;
|
||||
margin: 12px 0;
|
||||
padding: 6px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.btn-tertiary {
|
||||
border: 1px solid transparent;
|
||||
border-radius: 2px;
|
||||
background-color: transparent;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
padding: 16px 42px;
|
||||
}
|
|
@ -1,354 +0,0 @@
|
|||
.confirm-screen-container {
|
||||
position: relative;
|
||||
align-items: center;
|
||||
font-family: Roboto;
|
||||
flex: 1 0 auto;
|
||||
flex-flow: column nowrap;
|
||||
box-shadow: 0 2px 4px 0 rgba($black, .08);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
|
||||
@media screen and (max-width: 575px) {
|
||||
width: 100%;
|
||||
box-shadow: initial;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 576px) {
|
||||
// top: -26px;
|
||||
}
|
||||
}
|
||||
|
||||
.notification {
|
||||
.confirm-screen-wrapper {
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
height: calc(100vh - 85px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-screen-wrapper {
|
||||
height: 100%;
|
||||
width: 380px;
|
||||
background-color: $white;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
z-index: 25;
|
||||
align-items: center;
|
||||
font-family: Roboto;
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 8px;
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
top: 0;
|
||||
box-shadow: none;
|
||||
height: calc(100vh - 58px - 85px);
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-screen-wrapper > .confirm-screen-total-box {
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.confirm-screen-wrapper > .confirm-memo-wrapper {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.confirm-screen-header {
|
||||
height: 88px;
|
||||
background-color: $athens-grey;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 22px;
|
||||
line-height: 29px;
|
||||
width: 100%;
|
||||
padding: 25px 0;
|
||||
flex: 0 0 auto;
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-screen-header-tip {
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
background: $athens-grey;
|
||||
position: absolute;
|
||||
transform: rotate(45deg);
|
||||
top: 71px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.confirm-screen-title {
|
||||
line-height: 27px;
|
||||
|
||||
@media screen and (max-width: $break-small) {
|
||||
margin-left: 22px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-screen-back-button {
|
||||
color: $curious-blue;
|
||||
font-family: Roboto;
|
||||
font-size: 1rem;
|
||||
position: absolute;
|
||||
top: 38px;
|
||||
right: 38px;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.confirm-screen-account-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.confirm-screen-account-name {
|
||||
margin-top: 12px;
|
||||
font-size: 14px;
|
||||
line-height: 19px;
|
||||
color: $scorpion;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.confirm-screen-row-info {
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
}
|
||||
|
||||
.confirm-screen-account-number {
|
||||
font-size: 10px;
|
||||
line-height: 16px;
|
||||
color: $dusty-gray;
|
||||
text-align: center;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.confirm-send-ether,
|
||||
.confirm-send-token {
|
||||
i.fa-arrow-right {
|
||||
align-self: start;
|
||||
margin: 24px 14px 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-screen-identicons {
|
||||
margin-top: 24px;
|
||||
flex: 0 0 auto;
|
||||
|
||||
i.fa-arrow-right {
|
||||
align-self: start;
|
||||
margin: 42px 14px 0;
|
||||
}
|
||||
|
||||
i.fa-file-text-o {
|
||||
font-size: 60px;
|
||||
margin: 16px 8px 0 8px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-screen-sending-to-message {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
margin-top: 30px;
|
||||
font-family: 'DIN NEXT Light';
|
||||
}
|
||||
|
||||
.confirm-screen-send-amount {
|
||||
color: $scorpion;
|
||||
margin-top: 12px;
|
||||
text-align: center;
|
||||
font-size: 40px;
|
||||
line-height: 53px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.confirm-screen-send-amount-currency {
|
||||
font-size: 20px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.confirm-memo-wrapper {
|
||||
min-height: 24px;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid $alto;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.confirm-screen-send-memo {
|
||||
color: $scorpion;
|
||||
font-size: 16px;
|
||||
line-height: 19px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.confirm-screen-label {
|
||||
font-size: 16px;
|
||||
line-height: 40px;
|
||||
color: $scorpion;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
section .confirm-screen-account-name,
|
||||
section .confirm-screen-account-number,
|
||||
.confirm-screen-row-info,
|
||||
.confirm-screen-row-detail {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.confirm-screen-rows {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
width: 100%;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.confirm-screen-section-column {
|
||||
flex: .5;
|
||||
}
|
||||
|
||||
.confirm-screen-row {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
padding-left: 35px;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
|
||||
&:not(:last-of-type) {
|
||||
border-bottom: 1px solid $alto;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 379px) {
|
||||
.confirm-screen-row {
|
||||
span.confirm-screen-section-column {
|
||||
flex: 0.4;
|
||||
}
|
||||
|
||||
div.confirm-screen-section-column {
|
||||
flex: 0.6;
|
||||
}
|
||||
|
||||
.currency-display__input {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-screen-row-detail {
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: $dusty-gray;
|
||||
}
|
||||
|
||||
.confirm-screen-total-box {
|
||||
background-color: $wild-sand;
|
||||
position: relative;
|
||||
|
||||
.confirm-screen-label {
|
||||
line-height: 21px;
|
||||
}
|
||||
|
||||
.confirm-screen-row-detail {
|
||||
color: $scorpion;
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.confirm-screen-row-info {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 21px;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-screen-error {
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
color: #f00;
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
width: 80px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.confirm-screen-row.confirm-screen-total-box {
|
||||
.confirm-screen-section-column--with-error {
|
||||
flex: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 379px) {
|
||||
.confirm-screen-row.confirm-screen-total-box {
|
||||
.confirm-screen-section-column--with-error {
|
||||
flex: 0.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-screen-form {
|
||||
position: relative;
|
||||
|
||||
.confirm-screen-error {
|
||||
right: 0;
|
||||
width: 100%;
|
||||
margin-top: 7px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-screen-confirm-button {
|
||||
height: 50px;
|
||||
border-radius: 4px;
|
||||
background-color: #02c9b1;
|
||||
font-size: 16px;
|
||||
color: $white;
|
||||
text-align: center;
|
||||
font-family: Roboto;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
border-width: 0;
|
||||
box-shadow: none;
|
||||
flex: 1 0 auto;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.btn-light.confirm-screen-cancel-button {
|
||||
height: 50px;
|
||||
background: none;
|
||||
border: none;
|
||||
opacity: 1;
|
||||
font-family: Roboto;
|
||||
border-width: 0;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
font-size: 16px;
|
||||
box-shadow: none;
|
||||
cursor: pointer;
|
||||
flex: 1 0 auto;
|
||||
margin: 0 5px;
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
.currency-display {
|
||||
height: 54px;
|
||||
border: 1px solid $alto;
|
||||
border-radius: 4px;
|
||||
background-color: $white;
|
||||
color: $scorpion;
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
padding: 8px 10px;
|
||||
position: relative;
|
||||
|
||||
&__primary-row {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__input {
|
||||
color: $scorpion;
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
border: none;
|
||||
outline: 0 !important;
|
||||
max-width: 22ch;
|
||||
}
|
||||
|
||||
&__primary-currency {
|
||||
color: $scorpion;
|
||||
font-weight: 400;
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
&__converted-row {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__converted-value,
|
||||
&__converted-currency {
|
||||
color: $dusty-gray;
|
||||
font-family: Roboto;
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
&__input-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
max-width: 100%;
|
||||
|
||||
input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
&__currency-symbol {
|
||||
margin-top: 1px;
|
||||
color: $scorpion;
|
||||
}
|
||||
|
||||
.react-numeric-input {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
.editable-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
|
||||
&__value {
|
||||
max-width: 250px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&__input {
|
||||
width: 250px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
border: 1px solid $alto;
|
||||
|
||||
&--error {
|
||||
border: 1px solid $monzo;
|
||||
}
|
||||
}
|
||||
|
||||
&__icon-wrapper {
|
||||
position: absolute;
|
||||
margin-left: 10px;
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
cursor: pointer;
|
||||
color: $dusty-gray;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue