diff options
Diffstat (limited to 'ui/app/components/modals')
-rw-r--r-- | ui/app/components/modals/account-details-modal.js | 70 | ||||
-rw-r--r-- | ui/app/components/modals/account-modal-container.js | 70 | ||||
-rw-r--r-- | ui/app/components/modals/buy-options-modal.js | 87 | ||||
-rw-r--r-- | ui/app/components/modals/edit-account-name-modal.js | 76 | ||||
-rw-r--r-- | ui/app/components/modals/export-private-key-modal.js | 137 | ||||
-rw-r--r-- | ui/app/components/modals/index.js | 5 | ||||
-rw-r--r-- | ui/app/components/modals/modal.js | 175 | ||||
-rw-r--r-- | ui/app/components/modals/new-account-modal.js | 87 |
8 files changed, 707 insertions, 0 deletions
diff --git a/ui/app/components/modals/account-details-modal.js b/ui/app/components/modals/account-details-modal.js new file mode 100644 index 000000000..37a62e1c0 --- /dev/null +++ b/ui/app/components/modals/account-details-modal.js @@ -0,0 +1,70 @@ +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 AccountModalContainer = require('./account-modal-container') +const { getSelectedIdentity, getSelectedAddress } = require('../../selectors') +const genAccountLink = require('../../../lib/account-link.js') +const QrView = require('../qr-code') + +function mapStateToProps (state) { + return { + network: state.metamask.network, + selectedIdentity: getSelectedIdentity(state), + } +} + +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()), + } +} + +inherits(AccountDetailsModal, Component) +function AccountDetailsModal () { + Component.call(this) +} + +module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountDetailsModal) + +// Not yet pixel perfect todos: + // fonts of qr-header + +AccountDetailsModal.prototype.render = function () { + const { + selectedIdentity, + network, + showExportPrivateKeyModal, + hideModal, + } = this.props + const { name, address } = selectedIdentity + + return h(AccountModalContainer, {}, [ + h(QrView, { + Qr: { + message: name, + data: address, + }, + }), + + h('div.account-modal-divider'), + + h('button.btn-clear', { + onClick: () => global.platform.openWindow({ url: genAccountLink(address, network) }), + }, [ 'View account on Etherscan' ]), + + // Holding on redesign for Export Private Key functionality + h('button.btn-clear', { + onClick: () => { + showExportPrivateKeyModal() + }, + }, [ 'Export private key' ]), + + ]) +} diff --git a/ui/app/components/modals/account-modal-container.js b/ui/app/components/modals/account-modal-container.js new file mode 100644 index 000000000..3cad72067 --- /dev/null +++ b/ui/app/components/modals/account-modal-container.js @@ -0,0 +1,70 @@ +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 { getSelectedIdentity } = require('../../selectors') +const Identicon = require('../identicon') + +function mapStateToProps (state) { + return { + selectedIdentity: getSelectedIdentity(state), + } +} + +function mapDispatchToProps (dispatch) { + return { + hideModal: () => { + dispatch(actions.hideModal()) + }, + } +} + +inherits(AccountModalContainer, Component) +function AccountModalContainer () { + Component.call(this) +} + +module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountModalContainer) + +AccountModalContainer.prototype.render = function () { + const { + selectedIdentity, + children, + showBackButton = false, + backButtonAction, + } = this.props + + return h('div', { style: { borderRadius: '4px' }}, [ + h('div.account-modal-container', [ + + h('div', [ + + // Needs a border; requires changes to svg + h(Identicon, { + address: selectedIdentity.address, + diameter: 64, + style: {}, + }), + + ]), + + showBackButton && h('div.account-modal-back', { + onClick: backButtonAction, + }, [ + + h('i.fa.fa-angle-left.fa-lg'), + + h('span.account-modal-back__text', ' Back'), + + ]), + + h('div.account-modal-close', { + onClick: this.props.hideModal, + }), + + ...children, + + ]), + ]) +} diff --git a/ui/app/components/modals/buy-options-modal.js b/ui/app/components/modals/buy-options-modal.js new file mode 100644 index 000000000..a8033d288 --- /dev/null +++ b/ui/app/components/modals/buy-options-modal.js @@ -0,0 +1,87 @@ +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') + +function mapStateToProps (state) { + return { + network: state.metamask.network, + address: state.metamask.selectedAddress, + } +} + +function mapDispatchToProps (dispatch) { + return { + toCoinbase: (address) => { + dispatch(actions.buyEth({ network: '1', address, amount: 0 })) + }, + hideModal: () => { + dispatch(actions.hideModal()) + }, + showAccountDetailModal: () => { + dispatch(actions.showModal({ name: 'ACCOUNT_DETAILS' })) + }, + } +} + +inherits(BuyOptions, Component) +function BuyOptions () { + Component.call(this) +} + +module.exports = connect(mapStateToProps, mapDispatchToProps)(BuyOptions) + +BuyOptions.prototype.render = function () { + return h('div', {}, [ + h('div.buy-modal-content.transfers-subview', { + }, [ + h('div.buy-modal-content-title-wrapper.flex-column.flex-center', { + style: {}, + }, [ + h('div.buy-modal-content-title', { + style: {}, + }, 'Transfers'), + h('div', {}, 'How would you like to buy Ether?'), + ]), + + h('div.buy-modal-content-options.flex-column.flex-center', {}, [ + + h('div.buy-modal-content-option', { + onClick: () => { + const { toCoinbase, address } = this.props + toCoinbase(address) + }, + }, [ + h('div.buy-modal-content-option-title', {}, 'Coinbase'), + h('div.buy-modal-content-option-subtitle', {}, 'Buy with Fiat'), + ]), + + h('div.buy-modal-content-option', {}, [ + h('div.buy-modal-content-option-title', {}, 'Shapeshift'), + h('div.buy-modal-content-option-subtitle', {}, 'Trade any digital asset for any other'), + ]), + + h('div.buy-modal-content-option', { + onClick: () => this.goToAccountDetailsModal() + }, [ + h('div.buy-modal-content-option-title', {}, 'Direct Deposit'), + h('div.buy-modal-content-option-subtitle', {}, 'Deposit from another account'), + ]), + + ]), + + h('button', { + style: { + background: 'white', + }, + onClick: () => { this.props.hideModal() }, + }, h('div.buy-modal-content-footer#buy-modal-content-footer-text', {}, 'Cancel')), + ]), + ]) +} + +BuyOptions.prototype.goToAccountDetailsModal = function () { + this.props.hideModal() + this.props.showAccountDetailModal() +} diff --git a/ui/app/components/modals/edit-account-name-modal.js b/ui/app/components/modals/edit-account-name-modal.js new file mode 100644 index 000000000..5c25ac245 --- /dev/null +++ b/ui/app/components/modals/edit-account-name-modal.js @@ -0,0 +1,76 @@ +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 { getSelectedAccount } = require('../../selectors') + +function mapStateToProps (state) { + return { + selectedAccount: getSelectedAccount(state), + identity: state.appState.modal.modalState.identity, + } +} + +function mapDispatchToProps (dispatch) { + return { + hideModal: () => { + dispatch(actions.hideModal()) + }, + saveAccountLabel: (account, label) => { + dispatch(actions.saveAccountLabel(account, label)) + }, + } +} + +inherits(EditAccountNameModal, Component) +function EditAccountNameModal () { + Component.call(this) + this.state = { + inputText: '', + } +} + +module.exports = connect(mapStateToProps, mapDispatchToProps)(EditAccountNameModal) + +EditAccountNameModal.prototype.render = function () { + const { hideModal, saveAccountLabel, identity } = this.props + + return h('div', {}, [ + h('div.flex-column.edit-account-name-modal-content', { + }, [ + + h('div.edit-account-name-modal-cancel', { + onClick: () => { + hideModal() + }, + }, [ + h('i.fa.fa-times'), + ]), + + h('div.edit-account-name-modal-title', { + }, ['Edit Account Name']), + + h('input.edit-account-name-modal-input', { + placeholder: identity.name, + onChange: (event) => { + this.setState({ inputText: event.target.value }) + }, + value: this.state.inputText, + }, []), + + h('button.btn-clear.edit-account-name-modal-save-button', { + onClick: () => { + if (this.state.inputText.length !== 0) { + saveAccountLabel(identity.address, this.state.inputText) + hideModal() + } + }, + disabled: this.state.inputText.length === 0, + }, [ + 'SAVE', + ]), + + ]), + ]) +} diff --git a/ui/app/components/modals/export-private-key-modal.js b/ui/app/components/modals/export-private-key-modal.js new file mode 100644 index 000000000..ddc7f1352 --- /dev/null +++ b/ui/app/components/modals/export-private-key-modal.js @@ -0,0 +1,137 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const connect = require('react-redux').connect +const ethUtil = require('ethereumjs-util') +const actions = require('../../actions') +const AccountModalContainer = require('./account-modal-container') +const { getSelectedIdentity } = require('../../selectors') +const ReadOnlyInput = require('../readonly-input') + +function mapStateToProps (state) { + return { + warning: state.appState.warning, + privateKey: state.appState.accountDetail.privateKey, + network: state.metamask.network, + selectedIdentity: getSelectedIdentity(state), + previousModalState: state.appState.modal.previousModalState.name, + } +} + +function mapDispatchToProps (dispatch) { + return { + exportAccount: (password, address) => dispatch(actions.exportAccount(password, address)), + showAccountDetailModal: () => dispatch(actions.showModal({ name: 'ACCOUNT_DETAILS' })), + hideModal: () => dispatch(actions.hideModal()), + } +} + +inherits(ExportPrivateKeyModal, Component) +function ExportPrivateKeyModal () { + Component.call(this) + + this.state = { + password: '', + privateKey: null, + } +} + +module.exports = connect(mapStateToProps, mapDispatchToProps)(ExportPrivateKeyModal) + +ExportPrivateKeyModal.prototype.exportAccountAndGetPrivateKey = function (password, address) { + const { exportAccount } = this.props + + exportAccount(password, address) + .then(privateKey => this.setState({ privateKey })) +} + +ExportPrivateKeyModal.prototype.renderPasswordLabel = function (privateKey) { + return h('span.private-key-password-label', privateKey + ? 'This is your private key (click to copy)' + : 'Type Your Password' + ) +} + +ExportPrivateKeyModal.prototype.renderPasswordInput = function (privateKey) { + const plainKey = privateKey && ethUtil.stripHexPrefix(privateKey) + + return privateKey + ? h(ReadOnlyInput, { + wrapperClass: 'private-key-password-display-wrapper', + inputClass: 'private-key-password-display-textarea', + textarea: true, + value: plainKey, + }) + : h('input.private-key-password-input', { + type: 'password', + placeholder: 'Type password', + onChange: event => this.setState({ password: event.target.value }) + }) +} + +ExportPrivateKeyModal.prototype.renderButton = function (className, onClick, label) { + return h('button', { + className, + onClick, + }, label) +} + +ExportPrivateKeyModal.prototype.renderButtons = function (privateKey, password, address, hideModal) { + return h('div.export-private-key-buttons', {}, [ + !privateKey && this.renderButton('btn-clear btn-cancel', () => hideModal(), 'Cancel'), + + (privateKey + ? this.renderButton('btn-clear', () => hideModal(), 'Done') + : this.renderButton('btn-clear', () => this.exportAccountAndGetPrivateKey(this.state.password, address), 'Download') + ), + + ]) +} + +ExportPrivateKeyModal.prototype.render = function () { + const { + selectedIdentity, + network, + warning, + showAccountDetailModal, + hideModal, + previousModalState, + } = this.props + const { name, address } = selectedIdentity + + const { privateKey } = this.state + + return h(AccountModalContainer, { + showBackButton: previousModalState === 'ACCOUNT_DETAILS', + backButtonAction: () => showAccountDetailModal(), + }, [ + + h('span.account-name', name), + + h(ReadOnlyInput, { + wrapperClass: 'ellip-address-wrapper', + inputClass: 'qr-ellip-address ellip-address', + value: address, + }), + + h('div.account-modal-divider'), + + h('span.modal-body-title', 'Download Private Keys'), + + h('div.private-key-password', {}, [ + this.renderPasswordLabel(privateKey), + + this.renderPasswordInput(privateKey), + + !warning ? null : h('span.private-key-password-error', warning), + ]), + + h('div.private-key-password-warning', `Warning: Never disclose this key. + Anyone with your private keys can take steal any assets held in your + account.` + ), + + this.renderButtons(privateKey, this.state.password, address, hideModal), + + ]) +} diff --git a/ui/app/components/modals/index.js b/ui/app/components/modals/index.js new file mode 100644 index 000000000..1db1d33d4 --- /dev/null +++ b/ui/app/components/modals/index.js @@ -0,0 +1,5 @@ +const Modal = require('./modal') + +module.exports = { + Modal, +} diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js new file mode 100644 index 000000000..138efc3ea --- /dev/null +++ b/ui/app/components/modals/modal.js @@ -0,0 +1,175 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const connect = require('react-redux').connect +const FadeModal = require('boron').FadeModal +const actions = require('../../actions') +const isMobileView = require('../../../lib/is-mobile-view') +const isPopupOrNotification = require('../../../../app/scripts/lib/is-popup-or-notification') + +// Modal Components +const BuyOptions = require('./buy-options-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 accountModalStyle = { + mobileModalStyle: { + width: '95%', + top: isPopupOrNotification() === 'popup' ? '52vh' : '36.5vh', + boxShadow: 'rgba(0, 0, 0, 0.15) 0px 2px 2px 2px', + borderRadius: '4px', + }, + laptopModalStyle: { + width: '360px', + top: 'calc(33% + 45px)', + boxShadow: 'rgba(0, 0, 0, 0.15) 0px 2px 2px 2px', + borderRadius: '4px', + }, + contentStyle: { + borderRadius: '4px', + }, +} + +const MODALS = { + BUY: { + contents: [ + h(BuyOptions, {}, []), + ], + mobileModalStyle: { + width: '95%', + top: isPopupOrNotification() === 'popup' ? '48vh' : '36.5vh', + boxShadow: '0 0 7px 0 rgba(0,0,0,0.08)', + }, + laptopModalStyle: { + width: '66%', + maxWidth: '550px', + top: 'calc(30% + 10px)', + boxShadow: '0 0 7px 0 rgba(0,0,0,0.08)', + }, + }, + + EDIT_ACCOUNT_NAME: { + contents: [ + h(EditAccountNameModal, {}, []), + ], + mobileModalStyle: { + width: '95%', + top: isPopupOrNotification() === 'popup' ? '48vh' : '36.5vh', + boxShadow: 'rgba(0, 0, 0, 0.15) 0px 2px 2px 2px', + }, + laptopModalStyle: { + width: '375px', + top: 'calc(30% + 10px)', + boxShadow: 'rgba(0, 0, 0, 0.15) 0px 2px 2px 2px', + }, + }, + + ACCOUNT_DETAILS: { + contents: [ + h(AccountDetailsModal, {}, []), + ], + ...accountModalStyle, + }, + + EXPORT_PRIVATE_KEY: { + contents: [ + h(ExportPrivateKeyModal, {}, []), + ], + ...accountModalStyle, + }, + + NEW_ACCOUNT: { + contents: [ + h(NewAccountModal, {}, []), + ], + mobileModalStyle: { + width: '95%', + top: isPopupOrNotification() === 'popup' ? '52vh' : '36.5vh', + }, + laptopModalStyle: { + width: '449px', + top: 'calc(33% + 45px)', + }, + }, + + DEFAULT: { + contents: [], + mobileModalStyle: {}, + laptopModalStyle: {}, + }, +} + +const BACKDROPSTYLE = { + backgroundColor: 'rgba(245, 245, 245, 0.85)', +} + +function mapStateToProps (state) { + return { + active: state.appState.modal.open, + modalState: state.appState.modal.modalState, + } +} + +function mapDispatchToProps (dispatch) { + return { + hideModal: () => { + dispatch(actions.hideModal()) + }, + } +} + +// Global Modal Component +inherits(Modal, Component) +function Modal () { + Component.call(this) +} + +module.exports = connect(mapStateToProps, mapDispatchToProps)(Modal) + +Modal.prototype.render = function () { + const modal = MODALS[this.props.modalState.name || 'DEFAULT'] + + const children = modal.contents + const modalStyle = modal[isMobileView() ? 'mobileModalStyle' : 'laptopModalStyle'] + const contentStyle = modal.contentStyle || {}; + + return h(FadeModal, + { + className: 'modal', + keyboard: false, + onHide: () => { this.onHide() }, + ref: (ref) => { + this.modalRef = ref + }, + modalStyle, + contentStyle, + backdropStyle: BACKDROPSTYLE, + }, + children, + ) +} + +Modal.prototype.componentWillReceiveProps = function (nextProps) { + if (nextProps.active) { + this.show() + } else if (this.props.active) { + this.hide() + } +} + +Modal.prototype.onHide = function () { + if (this.props.onHideCallback) { + this.props.onHideCallback() + } + this.props.hideModal() +} + +Modal.prototype.hide = function () { + this.modalRef.hide() +} + +Modal.prototype.show = function () { + this.modalRef.show() +} diff --git a/ui/app/components/modals/new-account-modal.js b/ui/app/components/modals/new-account-modal.js new file mode 100644 index 000000000..25beb6745 --- /dev/null +++ b/ui/app/components/modals/new-account-modal.js @@ -0,0 +1,87 @@ +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') + +function mapStateToProps (state) { + return { + network: state.metamask.network, + address: state.metamask.selectedAddress, + } +} + +function mapDispatchToProps (dispatch) { + return { + toCoinbase: (address) => { + dispatch(actions.buyEth({ network: '1', address, amount: 0 })) + }, + hideModal: () => { + dispatch(actions.hideModal()) + }, + createAccount: (newAccountName) => { + dispatch(actions.addNewAccount()) + .then((newAccountAddress) => { + if (newAccountName) { + dispatch(actions.saveAccountLabel(newAccountAddress, newAccountName)) + } + dispatch(actions.hideModal()) + }) + }, + } +} + +inherits(NewAccountModal, Component) +function NewAccountModal () { + Component.call(this) + + this.state = { + newAccountName: '' + } +} + +module.exports = connect(mapStateToProps, mapDispatchToProps)(NewAccountModal) + +NewAccountModal.prototype.render = function () { + const { newAccountName } = this.state + + return h('div', {}, [ + h('div.new-account-modal-wrapper', { + }, [ + h('div.new-account-modal-header', {}, [ + 'New Account', + ]), + + h('div.modal-close-x', { + onClick: this.props.hideModal, + }), + + h('div.new-account-modal-content', {}, [ + 'Account Name', + ]), + + h('div.new-account-input-wrapper', {}, [ + h('input.new-account-input', { + placeholder: 'E.g. My new account', + onChange: (event) => this.setState({ newAccountName: event.target.value }) + }, []), + ]), + + h('div.new-account-modal-content.after-input', {}, [ + 'or', + ]), + + h('div.new-account-modal-content.after-input', {}, [ + 'Import an account', + ]), + + h('div.new-account-modal-content.button', {}, [ + h('button.btn-clear', { + onClick: () => this.props.createAccount(newAccountName) + }, [ + 'SAVE', + ]), + ]), + ]), + ]) +} |