aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js
diff options
context:
space:
mode:
authorChi Kei Chan <chikeichan@gmail.com>2019-05-07 23:03:27 +0800
committerDan J Miller <danjm.com@gmail.com>2019-05-07 23:03:26 +0800
commit581128503c161bc3b569ca5d87e4eea8b0d15150 (patch)
tree14b4b01936a7d7241e8cc65b4cd7f4141a5c8274 /ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js
parenta58e549c3f6513d60b3b995598af14df7871546c (diff)
downloadtangerine-wallet-browser-581128503c161bc3b569ca5d87e4eea8b0d15150.tar.gz
tangerine-wallet-browser-581128503c161bc3b569ca5d87e4eea8b0d15150.tar.zst
tangerine-wallet-browser-581128503c161bc3b569ca5d87e4eea8b0d15150.zip
Allow dragging seed phrase during Confirm Seed Phrase (#6557)
* Add basic drag and drop functionality * Refactor seed phrase data structure * Insert to list when drop * Save before refactor * Finish DND * Fix linter * update package-lock.json * Address styling feedbacks * Add box shadow on hover * Finish adding unit tests * Remove describe.only
Diffstat (limited to 'ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js')
-rw-r--r--ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js187
1 files changed, 158 insertions, 29 deletions
diff --git a/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js b/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js
index f3bfc3171..04fe651e6 100644
--- a/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js
+++ b/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js
@@ -8,7 +8,9 @@ import {
INITIALIZE_SEED_PHRASE_ROUTE,
} from '../../../../helpers/constants/routes'
import { exportAsFile } from '../../../../helpers/utils/util'
-import { selectSeedWord, deselectSeedWord } from './confirm-seed-phrase.state'
+import DraggableSeed from './draggable-seed.component'
+
+const EMPTY_SEEDS = Array(12).fill(null)
export default class ConfirmSeedPhrase extends PureComponent {
static contextTypes = {
@@ -27,10 +29,32 @@ export default class ConfirmSeedPhrase extends PureComponent {
}
state = {
- selectedSeedWords: [],
+ selectedSeedIndices: [],
shuffledSeedWords: [],
- // Hash of shuffledSeedWords index {Number} to selectedSeedWords index {Number}
- selectedSeedWordsHash: {},
+ pendingSeedIndices: [],
+ draggingSeedIndex: -1,
+ hoveringIndex: -1,
+ isDragging: false,
+ }
+
+ shouldComponentUpdate (nextProps, nextState, nextContext) {
+ const { seedPhrase } = this.props
+ const {
+ selectedSeedIndices,
+ shuffledSeedWords,
+ pendingSeedIndices,
+ draggingSeedIndex,
+ hoveringIndex,
+ isDragging,
+ } = this.state
+
+ return seedPhrase !== nextProps.seedPhrase ||
+ draggingSeedIndex !== nextState.draggingSeedIndex ||
+ isDragging !== nextState.isDragging ||
+ hoveringIndex !== nextState.hoveringIndex ||
+ selectedSeedIndices.join(' ') !== nextState.selectedSeedIndices.join(' ') ||
+ shuffledSeedWords.join(' ') !== nextState.shuffledSeedWords.join(' ') ||
+ pendingSeedIndices.join(' ') !== nextState.pendingSeedIndices.join(' ')
}
componentDidMount () {
@@ -39,6 +63,26 @@ export default class ConfirmSeedPhrase extends PureComponent {
this.setState({ shuffledSeedWords })
}
+ setDraggingSeedIndex = draggingSeedIndex => this.setState({ draggingSeedIndex })
+
+ setHoveringIndex = hoveringIndex => this.setState({ hoveringIndex })
+
+ onDrop = targetIndex => {
+ const {
+ selectedSeedIndices,
+ draggingSeedIndex,
+ } = this.state
+
+ const indices = insert(selectedSeedIndices, draggingSeedIndex, targetIndex, true)
+
+ this.setState({
+ selectedSeedIndices: indices,
+ pendingSeedIndices: indices,
+ draggingSeedIndex: -1,
+ hoveringIndex: -1,
+ })
+ }
+
handleExport = () => {
exportAsFile('MetaMask Secret Backup Phrase', this.props.seedPhrase, 'text/plain')
}
@@ -65,23 +109,34 @@ export default class ConfirmSeedPhrase extends PureComponent {
}
handleSelectSeedWord = (word, shuffledIndex) => {
- this.setState(selectSeedWord(word, shuffledIndex))
+ this.setState({
+ selectedSeedIndices: [...this.state.selectedSeedIndices, shuffledIndex],
+ pendingSeedIndices: [...this.state.pendingSeedIndices, shuffledIndex],
+ })
}
handleDeselectSeedWord = shuffledIndex => {
- this.setState(deselectSeedWord(shuffledIndex))
+ this.setState({
+ selectedSeedIndices: this.state.selectedSeedIndices.filter(i => shuffledIndex !== i),
+ pendingSeedIndices: this.state.pendingSeedIndices.filter(i => shuffledIndex !== i),
+ })
}
isValid () {
const { seedPhrase } = this.props
- const { selectedSeedWords } = this.state
+ const { selectedSeedIndices, shuffledSeedWords } = this.state
+ const selectedSeedWords = selectedSeedIndices.map(i => shuffledSeedWords[i])
return seedPhrase === selectedSeedWords.join(' ')
}
render () {
const { t } = this.context
const { history } = this.props
- const { selectedSeedWords, shuffledSeedWords, selectedSeedWordsHash } = this.state
+ const {
+ selectedSeedIndices,
+ shuffledSeedWords,
+ draggingSeedIndex,
+ } = this.state
return (
<div className="confirm-seed-phrase">
@@ -102,31 +157,30 @@ export default class ConfirmSeedPhrase extends PureComponent {
<div className="first-time-flow__text-block">
{ t('selectEachPhrase') }
</div>
- <div className="confirm-seed-phrase__selected-seed-words">
- {
- selectedSeedWords.map((word, index) => (
- <div
- key={index}
- className="confirm-seed-phrase__seed-word"
- >
- { word }
- </div>
- ))
- }
+ <div
+ className={classnames('confirm-seed-phrase__selected-seed-words', {
+ 'confirm-seed-phrase__selected-seed-words--dragging': draggingSeedIndex > -1,
+ })}
+ >
+ { this.renderPendingSeeds() }
+ { this.renderSelectedSeeds() }
</div>
<div className="confirm-seed-phrase__shuffled-seed-words">
{
shuffledSeedWords.map((word, index) => {
- const isSelected = index in selectedSeedWordsHash
+ const isSelected = selectedSeedIndices.includes(index)
return (
- <div
+ <DraggableSeed
key={index}
- className={classnames(
- 'confirm-seed-phrase__seed-word',
- 'confirm-seed-phrase__seed-word--shuffled',
- { 'confirm-seed-phrase__seed-word--selected': isSelected }
- )}
+ seedIndex={index}
+ index={index}
+ draggingSeedIndex={this.state.draggingSeedIndex}
+ setDraggingSeedIndex={this.setDraggingSeedIndex}
+ setHoveringIndex={this.setHoveringIndex}
+ onDrop={this.onDrop}
+ className="confirm-seed-phrase__seed-word--shuffled"
+ selected={isSelected}
onClick={() => {
if (!isSelected) {
this.handleSelectSeedWord(word, index)
@@ -134,9 +188,8 @@ export default class ConfirmSeedPhrase extends PureComponent {
this.handleDeselectSeedWord(index)
}
}}
- >
- { word }
- </div>
+ word={word}
+ />
)
})
}
@@ -152,4 +205,80 @@ export default class ConfirmSeedPhrase extends PureComponent {
</div>
)
}
+
+ renderSelectedSeeds () {
+ const { shuffledSeedWords, selectedSeedIndices, draggingSeedIndex } = this.state
+ return EMPTY_SEEDS.map((_, index) => {
+ const seedIndex = selectedSeedIndices[index]
+ const word = shuffledSeedWords[seedIndex]
+
+ return (
+ <DraggableSeed
+ key={`selected-${seedIndex}-${index}`}
+ className="confirm-seed-phrase__selected-seed-words__selected-seed"
+ index={index}
+ seedIndex={seedIndex}
+ word={word}
+ draggingSeedIndex={draggingSeedIndex}
+ setDraggingSeedIndex={this.setDraggingSeedIndex}
+ setHoveringIndex={this.setHoveringIndex}
+ onDrop={this.onDrop}
+ draggable
+ />
+ )
+ })
+ }
+
+ renderPendingSeeds () {
+ const {
+ pendingSeedIndices,
+ shuffledSeedWords,
+ draggingSeedIndex,
+ hoveringIndex,
+ } = this.state
+
+ const indices = insert(pendingSeedIndices, draggingSeedIndex, hoveringIndex)
+
+ return EMPTY_SEEDS.map((_, index) => {
+ const seedIndex = indices[index]
+ const word = shuffledSeedWords[seedIndex]
+
+ return (
+ <DraggableSeed
+ key={`pending-${seedIndex}-${index}`}
+ index={index}
+ className={classnames('confirm-seed-phrase__selected-seed-words__pending-seed', {
+ 'confirm-seed-phrase__seed-word--hidden': draggingSeedIndex === seedIndex && index !== hoveringIndex,
+ })}
+ seedIndex={seedIndex}
+ word={word}
+ draggingSeedIndex={draggingSeedIndex}
+ setDraggingSeedIndex={this.setDraggingSeedIndex}
+ setHoveringIndex={this.setHoveringIndex}
+ onDrop={this.onDrop}
+ droppable={!!word}
+ />
+ )
+ })
+ }
+}
+
+function insert (list, value, target, removeOld) {
+ let nextList = [...list]
+
+ if (typeof list[target] === 'number') {
+ nextList = [...list.slice(0, target), value, ...list.slice(target)]
+ }
+
+ if (removeOld) {
+ nextList = nextList.filter((seed, i) => {
+ return seed !== value || i === target
+ })
+ }
+
+ if (nextList.length > 12) {
+ nextList.pop()
+ }
+
+ return nextList
}