diff --git a/src/components/modals/device/Duplicate/index.js b/src/components/modals/device/Duplicate/index.js new file mode 100644 index 00000000..01f12e55 --- /dev/null +++ b/src/components/modals/device/Duplicate/index.js @@ -0,0 +1,172 @@ +/* @flow */ +import React, { Component } from 'react'; +import styled from 'styled-components'; +import { H3 } from 'components/Heading'; +import P from 'components/Paragraph'; +import Button from 'components/buttons/Button'; +import Input from 'components/Input'; +import { getDuplicateInstanceNumber } from 'reducers/utils'; +import { FONT_SIZE } from 'config/variables'; +import Icon from 'components/Icon'; +import icons from 'config/icons'; +import colors from 'config/colors'; +import Link from 'components/Link'; + +import { Props } from './index'; + +type State = { + defaultName: string; + instance: number; + instanceName: ?string; + isUsed: boolean; +} + +const StyledLink = styled(Link)` + position: absolute; + right: 15px; + top: 15px; +`; + +const Wrapper = styled.div` + width: 360px; + padding: 24px 48px; +`; + +const Column = styled.div` + display: flex; + padding: 10px 0; + flex-direction: column; +`; + +const StyledP = styled(P)` + padding: 10px 0px; +`; + +const StyledButton = styled(Button)` + margin: 0 0 10px 0; +`; + +const Label = styled.div` + display: flex; + text-align: left; + font-size: ${FONT_SIZE.SMALLER}; + flex-direction: column; + padding-bottom: 5px; +`; + +const ErrorMessage = styled.div` + color: ${colors.ERROR_PRIMARY}; + font-size: ${FONT_SIZE.SMALLER}; + padding-top: 5px; + text-align: center; + width: 100%; +`; + +export default class DuplicateDevice extends Component { + keyboardHandler: (event: KeyboardEvent) => void; + + state: State; + + input: ?HTMLInputElement; + + constructor(props: Props) { + super(props); + + const device = props.modal.opened ? props.modal.device : null; + if (!device) return; + + const instance = getDuplicateInstanceNumber(props.devices, device); + + this.state = { + defaultName: `${device.label} (${instance.toString()})`, + instance, + instanceName: null, + isUsed: false, + }; + } + + keyboardHandler(event: KeyboardEvent): void { + if (event.keyCode === 13 && !this.state.isUsed) { + event.preventDefault(); + this.submit(); + } + } + + componentDidMount(): void { + // one time autofocus + if (this.input) this.input.focus(); + this.keyboardHandler = this.keyboardHandler.bind(this); + window.addEventListener('keydown', this.keyboardHandler, false); + } + + componentWillUnmount(): void { + window.removeEventListener('keydown', this.keyboardHandler, false); + } + + onNameChange = (value: string): void => { + let isUsed: boolean = false; + if (value.length > 0) { + isUsed = (this.props.devices.find(d => d.instanceName === value) !== undefined); + } + + this.setState({ + instanceName: value.length > 0 ? value : null, + isUsed, + }); + } + + submit() { + if (!this.props.modal.opened) return; + const extended: Object = { instanceName: this.state.instanceName, instance: this.state.instance }; + this.props.modalActions.onDuplicateDevice({ ...this.props.modal.device, ...extended }); + } + + render() { + if (!this.props.modal.opened) return null; + + const { device } = this.props.modal; + const { onCancel, onDuplicateDevice } = this.props.modalActions; + const { + defaultName, + instanceName, + isUsed, + } = this.state; + + return ( + + + + +

Clone { device.label }?

+ This will create new instance of device which can be used with different passphrase + + + { this.input = element; }} + onChange={event => this.onNameChange(event.currentTarget.value)} + value={instanceName} + /> + { isUsed && Instance name is already in use } + + + this.submit()} + >Create new instance + + Cancel + + +
+ ); + } +} \ No newline at end of file diff --git a/src/components/modals/ForgetDevice/index.js b/src/components/modals/device/Forget/index.js similarity index 100% rename from src/components/modals/ForgetDevice/index.js rename to src/components/modals/device/Forget/index.js diff --git a/src/components/modals/RememberDevice/index.js b/src/components/modals/device/Remember/index.js similarity index 100% rename from src/components/modals/RememberDevice/index.js rename to src/components/modals/device/Remember/index.js diff --git a/src/components/modals/index.js b/src/components/modals/index.js index 2838c9c1..72360183 100644 --- a/src/components/modals/index.js +++ b/src/components/modals/index.js @@ -19,7 +19,6 @@ import * as CONNECT from 'actions/constants/TrezorConnect'; import type { MapStateToProps, MapDispatchToProps } from 'react-redux'; import type { State, Dispatch } from 'flowtype'; -import ForgetDevice from 'components/modals/ForgetDevice'; import Pin from 'components/modals/Pin'; import InvalidPin from 'components/modals/InvalidPin'; @@ -27,9 +26,10 @@ import Passphrase from 'components/modals/Passphrase'; import PassphraseType from 'components/modals/PassphraseType'; import ConfirmSignTx from 'components/modals/ConfirmSignTx'; import ConfirmAddress, { ConfirmUnverifiedAddress } from 'components/modals/ConfirmAddress'; -import RememberDevice from 'components/modals/RememberDevice'; -import DuplicateDevice from 'components/modals/DuplicateDevice'; +import ForgetDevice from 'components/modals/device/Forget'; +import RememberDevice from 'components/modals/device/Remember'; +import DuplicateDevice from 'components/modals/device/Duplicate'; type OwnProps = { } @@ -92,7 +92,8 @@ class Modal extends Component { render() { if (!this.props.modal.opened) return null; - const { opened, windowType } = this.props.modal; + const { opened } = this.props.modal; + const windowType = CONNECT.TRY_TO_DUPLICATE; let component = null; switch (windowType) { diff --git a/src/styles/modal.less b/src/styles/modal.less index 06fae1ac..dd45a8c7 100644 --- a/src/styles/modal.less +++ b/src/styles/modal.less @@ -90,62 +90,6 @@ } } - .duplicate, - .remember { - width: 360px; - padding: 24px 48px; - - h3 { - word-break: break-all; - } - - p { - padding: 14px 0px; - } - - .row { - position: relative; - text-align: left; - padding-bottom: 20px; - - label { - display: block; - padding-bottom: 6px; - text-align: left; - color: @color_text_secondary; - } - - .error { - position: absolute; - left: 0px; - bottom: 0px; - font-size: 12px; - color: @color_error_primary; - } - } - - button:not(.close-modal) { - width: 100%; - margin-top: 12px; - span { - position: relative; - } - } - - .loader-circle { - position: absolute; - top: 0; - bottom: 0; - left: -36px; - margin: auto; - p { - margin: 0; - padding: 0; - color: @color_white; - } - } - } - .close-modal { position: absolute; top: 0;