aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app/pages/unlock-page
diff options
context:
space:
mode:
Diffstat (limited to 'ui/app/pages/unlock-page')
-rw-r--r--ui/app/pages/unlock-page/index.js2
-rw-r--r--ui/app/pages/unlock-page/index.scss51
-rw-r--r--ui/app/pages/unlock-page/unlock-page.component.js191
-rw-r--r--ui/app/pages/unlock-page/unlock-page.container.js64
4 files changed, 308 insertions, 0 deletions
diff --git a/ui/app/pages/unlock-page/index.js b/ui/app/pages/unlock-page/index.js
new file mode 100644
index 000000000..be80cde4f
--- /dev/null
+++ b/ui/app/pages/unlock-page/index.js
@@ -0,0 +1,2 @@
+import UnlockPage from './unlock-page.container'
+module.exports = UnlockPage
diff --git a/ui/app/pages/unlock-page/index.scss b/ui/app/pages/unlock-page/index.scss
new file mode 100644
index 000000000..3d44bd037
--- /dev/null
+++ b/ui/app/pages/unlock-page/index.scss
@@ -0,0 +1,51 @@
+.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;
+ }
+
+ &__mascot-container {
+ margin-top: 24px;
+ }
+
+ &__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;
+ }
+ }
+}
diff --git a/ui/app/pages/unlock-page/unlock-page.component.js b/ui/app/pages/unlock-page/unlock-page.component.js
new file mode 100644
index 000000000..3aeb2a59b
--- /dev/null
+++ b/ui/app/pages/unlock-page/unlock-page.component.js
@@ -0,0 +1,191 @@
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+import Button from '@material-ui/core/Button'
+import TextField from '../../components/ui/text-field'
+import getCaretCoordinates from 'textarea-caret'
+import { EventEmitter } from 'events'
+import Mascot from '../../components/ui/mascot'
+import { DEFAULT_ROUTE } from '../../helpers/constants/routes'
+
+export default class UnlockPage extends Component {
+ static contextTypes = {
+ metricsEvent: PropTypes.func,
+ t: PropTypes.func,
+ }
+
+ static propTypes = {
+ history: PropTypes.object,
+ isUnlocked: PropTypes.bool,
+ onImport: PropTypes.func,
+ onRestore: PropTypes.func,
+ onSubmit: PropTypes.func,
+ forceUpdateMetamaskState: PropTypes.func,
+ showOptInModal: 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)
+ }
+ }
+
+ handleSubmit = async event => {
+ event.preventDefault()
+ event.stopPropagation()
+
+ const { password } = this.state
+ const { onSubmit, forceUpdateMetamaskState, showOptInModal } = this.props
+
+ if (password === '' || this.submitting) {
+ return
+ }
+
+ this.setState({ error: null })
+ this.submitting = true
+
+ try {
+ await onSubmit(password)
+ const newState = await forceUpdateMetamaskState()
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Navigation',
+ action: 'Unlock',
+ name: 'Success',
+ },
+ isNewVisit: true,
+ })
+
+ if (newState.participateInMetaMetrics === null || newState.participateInMetaMetrics === undefined) {
+ showOptInModal()
+ }
+ } catch ({ message }) {
+ if (message === 'Incorrect password') {
+ const newState = await forceUpdateMetamaskState()
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Navigation',
+ action: 'Unlock',
+ name: 'Incorrect Passowrd',
+ },
+ customVariables: {
+ numberOfTokens: newState.tokens.length,
+ numberOfAccounts: Object.keys(newState.accounts).length,
+ },
+ })
+ }
+
+ this.setState({ error: message })
+ this.submitting = false
+ }
+ }
+
+ handleInputChange ({ target }) {
+ this.setState({ password: target.value, error: null })
+
+ // tell mascot to look at page action
+ const element = target
+ const boundingRect = element.getBoundingClientRect()
+ const coordinates = getCaretCoordinates(element, element.selectionEnd)
+ this.animationEventEmitter.emit('point', {
+ x: boundingRect.left + coordinates.left - element.scrollLeft,
+ y: boundingRect.top + coordinates.top - element.scrollTop,
+ })
+ }
+
+ 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={this.handleSubmit}
+ disableRipple
+ >
+ { this.context.t('login') }
+ </Button>
+ )
+ }
+
+ render () {
+ const { password, error } = this.state
+ const { t } = this.context
+ const { onImport, onRestore } = this.props
+
+ return (
+ <div className="unlock-page__container">
+ <div className="unlock-page">
+ <div className="unlock-page__mascot-container">
+ <Mascot
+ animationEventEmitter={this.animationEventEmitter}
+ width="120"
+ height="120"
+ />
+ </div>
+ <h1 className="unlock-page__title">
+ { t('welcomeBack') }
+ </h1>
+ <div>{ t('unlockMessage') }</div>
+ <form
+ className="unlock-page__form"
+ onSubmit={this.handleSubmit}
+ >
+ <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={() => onRestore()}
+ >
+ { t('restoreFromSeed') }
+ </div>
+ <div
+ className="unlock-page__link unlock-page__link--import"
+ onClick={() => onImport()}
+ >
+ { t('importUsingSeed') }
+ </div>
+ </div>
+ </div>
+ </div>
+ )
+ }
+}
diff --git a/ui/app/pages/unlock-page/unlock-page.container.js b/ui/app/pages/unlock-page/unlock-page.container.js
new file mode 100644
index 000000000..bd43666fc
--- /dev/null
+++ b/ui/app/pages/unlock-page/unlock-page.container.js
@@ -0,0 +1,64 @@
+import { connect } from 'react-redux'
+import { withRouter } from 'react-router-dom'
+import { compose } from 'recompose'
+import { getEnvironmentType } from '../../../../app/scripts/lib/util'
+import { ENVIRONMENT_TYPE_POPUP } from '../../../../app/scripts/lib/enums'
+import { DEFAULT_ROUTE, RESTORE_VAULT_ROUTE } from '../../helpers/constants/routes'
+import {
+ tryUnlockMetamask,
+ forgotPassword,
+ markPasswordForgotten,
+ forceUpdateMetamaskState,
+ showModal,
+} from '../../store/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()),
+ forceUpdateMetamaskState: () => forceUpdateMetamaskState(dispatch),
+ showOptInModal: () => dispatch(showModal({ name: 'METAMETRICS_OPT_IN_MODAL' })),
+ }
+}
+
+const mergeProps = (stateProps, dispatchProps, ownProps) => {
+ const { markPasswordForgotten, tryUnlockMetamask, ...restDispatchProps } = dispatchProps
+ const { history, onSubmit: ownPropsSubmit, ...restOwnProps } = ownProps
+
+ const onImport = () => {
+ markPasswordForgotten()
+ history.push(RESTORE_VAULT_ROUTE)
+
+ if (getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP) {
+ global.platform.openExtensionInBrowser()
+ }
+ }
+
+ const onSubmit = async password => {
+ await tryUnlockMetamask(password)
+ history.push(DEFAULT_ROUTE)
+ }
+
+ return {
+ ...stateProps,
+ ...restDispatchProps,
+ ...restOwnProps,
+ onImport,
+ onRestore: onImport,
+ onSubmit: ownPropsSubmit || onSubmit,
+ }
+}
+
+export default compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps, mergeProps)
+)(UnlockPage)