OSDN Git Service

add the create and confirm Mnemonic logic for the new key.
authorZhiting Lin <zlin035@uottawa.ca>
Mon, 5 Nov 2018 07:29:56 +0000 (15:29 +0800)
committerZhiting Lin <zlin035@uottawa.ca>
Mon, 5 Nov 2018 07:29:56 +0000 (15:29 +0800)
15 files changed:
src/features/mockhsm/actions.js
src/features/mockhsm/components/MnemonicStepper/MnemonicStepper.jsx [new file with mode: 0644]
src/features/mockhsm/components/MnemonicStepper/MnemonicStepper.scss [new file with mode: 0644]
src/features/mockhsm/components/Skip/Skip.jsx [new file with mode: 0644]
src/features/mockhsm/components/index.js
src/features/mockhsm/reducers.js
src/features/mockhsm/routes.js
src/features/shared/components/ConfirmMnemonic/ConfirmMnemonic.jsx
src/features/shared/components/ConfirmMnemonic/ConfirmMnemonic.scss [new file with mode: 0644]
src/features/shared/components/Mnemonic/Mnemonic.jsx
src/features/shared/components/Mnemonic/Mnemonic.scss [new file with mode: 0644]
src/features/shared/components/SingletonField.jsx
src/features/shared/components/Stepper/Step.jsx
src/features/shared/components/Stepper/stepper.scss [new file with mode: 0644]
src/locales/en/translation.json

index 0d887d9..4a2fd83 100644 (file)
@@ -14,6 +14,20 @@ const create = baseCreateActions(type, {
   clientApi,
 })
 
+create.submitForm = (data) => function (dispatch) {
+  if (typeof data.alias == 'string')  data.alias = data.alias.trim()
+
+  return clientApi().create(data)
+    .then((resp) => {
+      if (resp.status === 'fail') {
+        throw resp
+      }
+
+      dispatch({type: 'NEW_KEY', data: resp.data.mnemonic})
+      dispatch( push('/keys/mnemonic') )
+    })
+}
+
 const resetPassword = {
   submitResetForm: (params) => {
     let promise = Promise.resolve()
@@ -66,10 +80,16 @@ const createExport =  (arg, fileName) => (dispatch) => {
   })
 }
 
+const createSuccess = ()=> (dispatch) =>{
+  dispatch(create.created())
+  dispatch(push('/keys'))
+}
+
 export default {
   ...create,
   ...list,
   ...resetPassword,
   checkPassword,
-  createExport
+  createExport,
+  createSuccess
 }
diff --git a/src/features/mockhsm/components/MnemonicStepper/MnemonicStepper.jsx b/src/features/mockhsm/components/MnemonicStepper/MnemonicStepper.jsx
new file mode 100644 (file)
index 0000000..825206e
--- /dev/null
@@ -0,0 +1,67 @@
+import React from 'react'
+import {connect} from 'react-redux'
+import actions from 'actions'
+import { NotFound, Step, StepList, ConfirmMnemonic, Mnemonic, PageContent, PageTitle } from 'features/shared/components'
+import {Skip} from '../'
+import styles from './MnemonicStepper.scss'
+
+class MnemonicStepper extends React.Component {
+  constructor(props) {
+    super(props)
+  }
+
+  render() {
+    if (this.props.mnemonic.length === 0) {
+      return <NotFound />
+    }
+
+    return (
+      <div>
+        <PageTitle title={'Mnemonic'} />
+
+        <PageContent>
+          <div className={styles.main}>
+            <StepList>
+              <Step>
+                <Skip/>
+                <button className={`btn btn-primary ${styles.marginRight}`}
+                        onClick={() => this.props.succeeded()}
+                >
+                  skip
+                </button>
+              </Step>
+              <Step>
+                <Mnemonic
+                  mnemonic={this.props.mnemonic}
+                />
+              </Step>
+
+              <Step>
+                <ConfirmMnemonic
+                  mnemonic={this.props.mnemonic}
+                  succeeded={this.props.succeeded}
+                />
+
+              </Step>
+            </StepList>
+          </div>
+        </PageContent>
+      </div>
+    )
+  }
+}
+
+const mapStateToProps = (state) => {
+  const mnemonic = (state.key || {}).mnemonic || []
+  if (mnemonic) return {mnemonic}
+  return {}
+}
+
+const mapDispatchToProps = ( dispatch ) => ({
+  succeeded: () => dispatch(actions.key.createSuccess()),
+})
+
+export default connect(
+  mapStateToProps,
+  mapDispatchToProps
+)(MnemonicStepper)
diff --git a/src/features/mockhsm/components/MnemonicStepper/MnemonicStepper.scss b/src/features/mockhsm/components/MnemonicStepper/MnemonicStepper.scss
new file mode 100644 (file)
index 0000000..1c18a04
--- /dev/null
@@ -0,0 +1,10 @@
+.main{
+  background-color: $background-color;
+  border: 1px solid $border-color;
+  padding: $gutter-size;
+  word-wrap: break-word;
+}
+
+.marginRight{
+  margin-right: $gutter-size/2;
+}
\ No newline at end of file
diff --git a/src/features/mockhsm/components/Skip/Skip.jsx b/src/features/mockhsm/components/Skip/Skip.jsx
new file mode 100644 (file)
index 0000000..ebf9a5f
--- /dev/null
@@ -0,0 +1,18 @@
+import React from 'react'
+
+class Skip extends React.Component {
+  constructor(props) {
+    super(props)
+  }
+
+  render() {
+    return (
+      <div>
+        <h2>Continue with Mnemonic</h2>
+        <p>Mnemonic can be used to restore the relevant key information. You can either skip the Mnemonic step or keep it.</p>
+      </div>
+    )
+  }
+}
+
+export default Skip
index 72b69e0..d223ead 100644 (file)
@@ -1,13 +1,17 @@
 import List from './List'
 import New from './New'
+import MnemonicStepper from './MnemonicStepper/MnemonicStepper'
 import Show from './Show'
 import ResetPassword from './ResetPassword/ResetPassword'
 import CheckPassword from './CheckPassword/CheckPassword'
+import Skip from './Skip/Skip'
 
 export {
   List,
   New,
+  MnemonicStepper,
   Show,
   ResetPassword,
-  CheckPassword
+  CheckPassword,
+  Skip
 }
index 92563db..72628f1 100644 (file)
@@ -17,4 +17,10 @@ export default combineReducers({
     }
     return state
   },
+  mnemonic: (state = [], action) => {
+    if (action.type == 'NEW_KEY') {
+      return action.data
+    }
+    return state
+  },
 })
index 0fab932..810059c 100644 (file)
@@ -1,4 +1,4 @@
-import { List, New, Show, ResetPassword, CheckPassword } from './components'
+import { List, New, Show, ResetPassword, CheckPassword, MnemonicStepper } from './components'
 import { makeRoutes } from 'features/shared'
 
 export default (store) => makeRoutes(store, 'key', List, New, Show,
@@ -12,4 +12,8 @@ export default (store) => makeRoutes(store, 'key', List, New, Show,
         path: ':id/check-password',
         component: CheckPassword,
       },
+      {
+        path: 'mnemonic',
+        component: MnemonicStepper
+      }
     ],skipFilter: true, name: 'key' })
\ No newline at end of file
index 3708114..4ce8237 100644 (file)
@@ -1,6 +1,7 @@
 import React from 'react'
 import { ErrorBanner, SingletonField} from 'features/shared/components'
 import {reduxForm} from 'redux-form'
+import style from './ConfirmMnemonic.scss'
 
 class ConfirmMnemonic extends React.Component {
   constructor(props) {
@@ -63,19 +64,27 @@ class ConfirmMnemonic extends React.Component {
       <form  onSubmit={handleSubmit(this.submitWithValidation)}>
         <h2>Enter your wallet recover phrase</h2>
         <p>Confirm that you backup your Recovery phrase by filling in the missing words:</p>
-        {seedWords.map((seedWord) => {
-          return ( seedWord.show ?
-            <div key={seedWord.index} className='seedWord'>{seedWord.word}</div> :
-            (words[counter]? <SingletonField
-              key={seedWord.index}
-              fieldProps={ words[counter++].value }
-            /> : null)
-          )
-        })}
+        <div className={style.seedArea}>
+          {seedWords.map((seedWord) => {
+            return ( seedWord.show ?
+              <div key={seedWord.index} className={`${style.seed} ${style.seedWord}`}>{seedWord.word}</div> :
+              (words[counter]? <SingletonField
+                className={style.seedWord}
+                key={seedWord.index}
+                fieldProps={ words[counter++].value }
+              /> : null)
+            )
+          })}
+        </div>
 
         {error&& <ErrorBanner error={error} />}
 
-        <button type='submit' disabled={submitting}>submit</button>
+        <button
+          className={`btn btn-primary ${style.submit}`}
+          type='submit'
+          disabled={submitting}>
+          submit
+        </button>
 
       </form>
     )
diff --git a/src/features/shared/components/ConfirmMnemonic/ConfirmMnemonic.scss b/src/features/shared/components/ConfirmMnemonic/ConfirmMnemonic.scss
new file mode 100644 (file)
index 0000000..73d3617
--- /dev/null
@@ -0,0 +1,23 @@
+.submit{
+  float: left;
+}
+
+.seed{
+  user-select: none;
+  background-color: #48566e;
+  border-radius: 3px;
+  color: #fff;
+}
+
+.seedWord{
+  width: 100px;
+  height: 23px;
+  margin: 5px;
+  text-align: center;
+}
+
+.seedArea{
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+}
index 6429293..253da6c 100644 (file)
@@ -1,23 +1,34 @@
 import React from 'react'
 import { copyToClipboard } from 'utility/clipboard'
-
+import styles from './Mnemonic.scss'
 
 class Mnemonic extends React.Component {
   constructor(props) {
     super(props)
+    this.state={
+      mnemonicArray : this.props.mnemonic.split(' ')
+    }
   }
 
   render() {
+    const {mnemonicArray} = this.state
     return (
      <div>
-       {this.props.mnemonic}
+       <h2>Mnemonic</h2>
+       <p>Write down the following seed and save it in a secure location.</p>
+       <div>
+         {
+           mnemonicArray.map((seedWord) =>
+             <div className={styles.seed}>{seedWord}</div>)
+         }
+         <button
+           className={`btn btn-primary ${styles.copy}`}
+           onClick={() => copyToClipboard(this.props.mnemonic)}
+         >
+           Copy to clipboard
+         </button>
+       </div>
 
-       <button
-         className='btn btn-primary'
-         onClick={() => copyToClipboard(this.props.mnemonic)}
-       >
-         Copy to clipboard
-       </button>
      </div>
     )
   }
diff --git a/src/features/shared/components/Mnemonic/Mnemonic.scss b/src/features/shared/components/Mnemonic/Mnemonic.scss
new file mode 100644 (file)
index 0000000..b1a3809
--- /dev/null
@@ -0,0 +1,16 @@
+.seed{
+  user-select: none;
+  width: 100px;
+  height: 23px;
+  background-color: #48566e;
+  border-radius: 3px;
+  color: #fff;
+  margin: 5px;
+  float: left;
+  text-align: center;
+}
+
+.copy{
+  width: 177px;
+  margin: 4px;
+}
index 90516f6..aa78ce6 100644 (file)
@@ -22,7 +22,7 @@ class SingletonField extends React.Component {
     return(
       <div className={touched && error &&'has-error'}>
         <input
-          className='form-control'
+          className={`form-control ${this.props.className}`}
           autoFocus={!!this.props.autoFocus}
           {...disableAutocomplete}
           {...fieldProps} />
index b6c0753..16f4dfc 100644 (file)
@@ -1,4 +1,5 @@
 import React from 'react'
+import styles from './stepper.scss'
 
 class Step extends React.Component {
 
@@ -7,7 +8,6 @@ class Step extends React.Component {
       isActive,
       displayPrevious,
       displayNext,
-      displaySubmit,
       component,
       children,
     } = this.props
@@ -17,14 +17,14 @@ class Step extends React.Component {
     return (
       <div>
           {component ? React.createElement(component) : children}
-          <Previous
-            isActive={displayPrevious}
-            goToPreviousStep={() => this.props.goToPreviousStep()}
-          />
           <Next
             isActive={displayNext}
             goToNextStep={() => this.props.goToNextStep()}
           />
+          <Previous
+            isActive={displayPrevious}
+            goToPreviousStep={() => this.props.goToPreviousStep()}
+          />
       </div>
     )
   }
@@ -37,8 +37,10 @@ class Next extends React.Component {
     if (isActive === false) return null
 
     return (
-      <button onClick={() => this.props.goToNextStep()}>
-        Next
+      <button
+        className='btn btn-default'
+        onClick={() => this.props.goToNextStep()}>
+        Continue
       </button>
     )
   }
@@ -51,30 +53,13 @@ class Previous extends React.Component {
     if (isActive === false) return null
 
     return (
-      <button onClick={() => this.props.goToPreviousStep()}>
-        Previous
-      </button>
-    )
-  }
-}
-
-class Submit extends React.Component {
-
-  render() {
-    const { isActive } = this.props
-    if (isActive === false) return null
-
-    return (
-      <button
-        type='submit'
-        onClick={() => this.props.submit()}
-      >
-        { this.props.submitLabel || 'Submit' }
-      </button>
+      <a
+        className= {styles.marginLeft}
+        onClick={() => this.props.goToPreviousStep()}>
+        Cancel
+      </a>
     )
-
   }
 }
 
-
 export default Step
\ No newline at end of file
diff --git a/src/features/shared/components/Stepper/stepper.scss b/src/features/shared/components/Stepper/stepper.scss
new file mode 100644 (file)
index 0000000..1db28e5
--- /dev/null
@@ -0,0 +1,4 @@
+.marginLeft{
+  margin-left: $gutter-size/2;
+  cursor: pointer;
+}
\ No newline at end of file
index c65cf14..ee60fbb 100644 (file)
     "title":"Backup and Restore",
     "backup":"Back Up",
     "backupDescription":"This option will back up all data stored in this core, including blockchain data, accounts, assets and balances.",
-    "restoreKeystore":"Restore by File",
+    "restoreKeystore":"Restore from File",
     "restoreKeystoreDescription":"This option will restore the wallet data form a file. You might need to rescan your wallet, if you balance is not up to date.",
     "restoreMnemonic":"Restore by Mnemonic",
     "restoreMnemonicDescription":"This option will restore the key related data by mnemonic. You might need to rescan your wallet, if you balance is not up to date.",