From b9a63196f2994d9c638d70861a080021b5aa65d4 Mon Sep 17 00:00:00 2001 From: maciejhirsz Date: Tue, 20 Mar 2018 16:39:30 +0100 Subject: [PATCH 01/13] Parity Signer Squashed --- common/actions/wallet/actionCreators.ts | 26 ++++ common/actions/wallet/actionTypes.ts | 14 ++- common/actions/wallet/constants.ts | 4 +- common/components/Footer/index.tsx | 2 + .../WalletDecrypt/WalletDecrypt.tsx | 10 ++ .../WalletDecrypt/components/ParitySigner.tsx | 49 ++++++++ .../WalletDecrypt/components/index.tsx | 4 + common/components/ui/Modal/ModalBody.tsx | 26 ++-- common/config/data.tsx | 3 +- common/containers/QrSignerModal.tsx | 111 ++++++++++++++++++ .../libs/wallet/non-deterministic/parity.ts | 59 ++++++++++ .../libs/wallet/non-deterministic/wallets.ts | 4 +- common/reducers/wallet.ts | 37 +++++- common/translations/lang/en.json | 3 + common/typescript/parity.d.ts | 4 + package.json | 1 + 16 files changed, 345 insertions(+), 12 deletions(-) create mode 100644 common/components/WalletDecrypt/components/ParitySigner.tsx create mode 100644 common/containers/QrSignerModal.tsx create mode 100644 common/libs/wallet/non-deterministic/parity.ts create mode 100644 common/typescript/parity.d.ts diff --git a/common/actions/wallet/actionCreators.ts b/common/actions/wallet/actionCreators.ts index ad4cfc6a0fe..9f8aea1e1ac 100644 --- a/common/actions/wallet/actionCreators.ts +++ b/common/actions/wallet/actionCreators.ts @@ -1,3 +1,4 @@ +import EthTx from 'ethereumjs-tx'; import { Wei, TokenValue } from 'libs/units'; import { IWallet, WalletConfig } from 'libs/wallet'; import * as types from './actionTypes'; @@ -166,3 +167,28 @@ export function setAccountBalance(): types.SetAccountBalanceAction { type: TypeKeys.WALLET_SET_ACCOUNT_BALANCE }; } + +export type TSetWalletQrTransaction = typeof setWalletQrTransaction; +export function setWalletQrTransaction( + tx: EthTx, + from: string, + onSignature: (signature: string) => void, + onCancel: () => void +): types.SetWalletQrTransactionAction { + return { + type: TypeKeys.WALLET_SET_QR_TRANSACTION, + payload: { + tx, + from, + onSignature, + onCancel + } + }; +} + +export type TFinalizeQrTransaction = typeof finalizeQrTransaction; +export function finalizeQrTransaction(): types.FinalizeWalletQrTransactionAction { + return { + type: TypeKeys.WALLET_FINALIZE_QR_TX + }; +} diff --git a/common/actions/wallet/actionTypes.ts b/common/actions/wallet/actionTypes.ts index bda663901d6..a81719394ea 100644 --- a/common/actions/wallet/actionTypes.ts +++ b/common/actions/wallet/actionTypes.ts @@ -1,5 +1,6 @@ import { Wei, TokenValue } from 'libs/units'; import { IWallet, WalletConfig } from 'libs/wallet'; +import { QrSignatureState } from 'reducers/wallet'; import { TypeKeys } from './constants'; /*** Unlock Private Key ***/ @@ -129,6 +130,15 @@ export interface SetAccountBalanceAction { type: TypeKeys.WALLET_SET_ACCOUNT_BALANCE; } +export interface SetWalletQrTransactionAction { + type: TypeKeys.WALLET_SET_QR_TRANSACTION; + payload: QrSignatureState; +} + +export interface FinalizeWalletQrTransactionAction { + type: TypeKeys.WALLET_FINALIZE_QR_TX; +} + /*** Union Type ***/ export type WalletAction = | UnlockPrivateKeyAction @@ -148,4 +158,6 @@ export type WalletAction = | SetWalletTokensAction | SetWalletConfigAction | SetPasswordPendingAction - | SetAccountBalanceAction; + | SetAccountBalanceAction + | SetWalletQrTransactionAction + | FinalizeWalletQrTransactionAction; diff --git a/common/actions/wallet/constants.ts b/common/actions/wallet/constants.ts index 24990ea4277..516cb09fe3d 100644 --- a/common/actions/wallet/constants.ts +++ b/common/actions/wallet/constants.ts @@ -20,5 +20,7 @@ export enum TypeKeys { WALLET_SET_CONFIG = 'WALLET_SET_CONFIG', WALLET_RESET = 'WALLET_RESET', WALLET_SET_PASSWORD_PENDING = 'WALLET_SET_PASSWORD_PENDING', - WALLET_SET_ACCOUNT_BALANCE = 'WALLET_SET_ACCOUNT_BALANCE' + WALLET_SET_ACCOUNT_BALANCE = 'WALLET_SET_ACCOUNT_BALANCE', + WALLET_SET_QR_TRANSACTION = 'WALLET_SET_QR_TRANSACTION', + WALLET_FINALIZE_QR_TX = 'WALLET_FINALIZE_QR_TX' } diff --git a/common/components/Footer/index.tsx b/common/components/Footer/index.tsx index 15bf7e38d06..52e60c4c905 100644 --- a/common/components/Footer/index.tsx +++ b/common/components/Footer/index.tsx @@ -12,6 +12,7 @@ import React from 'react'; import PreFooter from './PreFooter'; import DisclaimerModal from './DisclaimerModal'; import { NewTabLink } from 'components/ui'; +import QrSignerModal from 'containers/QrSignerModal'; import OnboardModal from 'containers/OnboardModal'; import './index.scss'; @@ -217,6 +218,7 @@ export default class Footer extends React.PureComponent { + diff --git a/common/components/WalletDecrypt/WalletDecrypt.tsx b/common/components/WalletDecrypt/WalletDecrypt.tsx index 465c9e8fccf..791b68343c6 100644 --- a/common/components/WalletDecrypt/WalletDecrypt.tsx +++ b/common/components/WalletDecrypt/WalletDecrypt.tsx @@ -27,6 +27,7 @@ import { PrivateKeyValue, TrezorDecrypt, ViewOnlyDecrypt, + ParitySignerDecrypt, Web3Decrypt, WalletButton, InsecureWalletWarning @@ -165,6 +166,15 @@ export class WalletDecrypt extends Component { unlock: this.props.setWallet, helpLink: 'https://doc.satoshilabs.com/trezor-apps/mew.html' }, + [SecureWalletName.PARITY_SIGNER]: { + lid: 'x_ParitySigner', + icon: TrezorIcon, + description: 'ADD_ParityDesc', + component: ParitySignerDecrypt, + initialParams: {}, + unlock: this.props.setWallet, + helpLink: 'https://doc.satoshilabs.com/trezor-apps/mew.html' + }, [InsecureWalletName.KEYSTORE_FILE]: { lid: 'x_Keystore2', example: 'UTC--2017-12-15T17-35-22.547Z--6be6e49e82425a5aa56396db03512f2cc10e95e8', diff --git a/common/components/WalletDecrypt/components/ParitySigner.tsx b/common/components/WalletDecrypt/components/ParitySigner.tsx new file mode 100644 index 00000000000..0fd2b67ce59 --- /dev/null +++ b/common/components/WalletDecrypt/components/ParitySigner.tsx @@ -0,0 +1,49 @@ +import React, { PureComponent } from 'react'; +import { connect } from 'react-redux'; +import QrSigner from '@parity/qr-signer'; +// import translate from 'translations'; +// import { donationAddressMap } from 'config'; +import { isValidETHAddress } from 'libs/validators'; +import { ParitySignerWallet } from 'libs/wallet'; +import { + setWalletQrTransaction, + TSetWalletQrTransaction, + finalizeQrTransaction, + TFinalizeQrTransaction +} from 'actions/wallet'; + +interface Props { + setWalletQrTransaction: TSetWalletQrTransaction; + finalizeQrTransaction: TFinalizeQrTransaction; + onUnlock(param: any): void; +} + +class ParitySignerDecrypt extends PureComponent { + public render() { + return ( +
+ +
+ ); + } + + private unlockAddress = (address: string) => { + if (!isValidETHAddress(address)) { + console.error('Invalid address!'); + return; + } + + this.props.onUnlock( + new ParitySignerWallet( + address, + this.props.setWalletQrTransaction, + this.props.finalizeQrTransaction + ) + ); + }; +} + +export default connect(() => ({}), { + setWalletQrTransaction, + finalizeQrTransaction +})(ParitySignerDecrypt); diff --git a/common/components/WalletDecrypt/components/index.tsx b/common/components/WalletDecrypt/components/index.tsx index 7e68311140c..408abc4ad2a 100644 --- a/common/components/WalletDecrypt/components/index.tsx +++ b/common/components/WalletDecrypt/components/index.tsx @@ -9,3 +9,7 @@ export * from './Trezor'; export * from './ViewOnly'; export * from './WalletButton'; export * from './Web3'; + +import Foo from './ParitySigner'; + +export const ParitySignerDecrypt = Foo; diff --git a/common/components/ui/Modal/ModalBody.tsx b/common/components/ui/Modal/ModalBody.tsx index c10b7c8100a..5c3aa218eb1 100644 --- a/common/components/ui/Modal/ModalBody.tsx +++ b/common/components/ui/Modal/ModalBody.tsx @@ -16,8 +16,8 @@ export default class ModalBody extends React.Component { private modal: HTMLElement; private modalContent: HTMLElement; private focusedElementBeforeModal: HTMLElement; - private firstTabStop: HTMLElement; - private lastTabStop: HTMLElement; + private firstTabStop: HTMLElement | null; + private lastTabStop: HTMLElement | null; public componentDidMount() { this.focusedElementBeforeModal = document.activeElement as HTMLElement; @@ -28,18 +28,30 @@ export default class ModalBody extends React.Component { this.modal.querySelectorAll(focusableElementsString) ); + // Do nothing if there are no focusable elements within the modal + if (focusableElements.length === 0) { + this.firstTabStop = null; + this.lastTabStop = null; + return; + } + + const first = focusableElements[0]; + // Convert NodeList to Array - this.firstTabStop = focusableElements[0]; + this.firstTabStop = first; this.lastTabStop = focusableElements[focusableElements.length - 1]; // Focus first child - this.firstTabStop.focus(); + first.focus(); this.modal.addEventListener('keydown', this.keyDownListener); } public componentWillUnmount() { - document.removeEventListener('keydown', this.keyDownListener); + // Remove the event listener if bound + if (this.firstTabStop) { + document.removeEventListener('keydown', this.keyDownListener); + } } public scrollContentToTop = () => { @@ -108,14 +120,14 @@ export default class ModalBody extends React.Component { if (e.keyCode === 9) { // SHIFT + TAB if (e.shiftKey) { - if (document.activeElement === this.firstTabStop) { + if (document.activeElement === this.firstTabStop && this.lastTabStop) { e.preventDefault(); this.lastTabStop.focus(); } // TAB } else { - if (document.activeElement === this.lastTabStop) { + if (document.activeElement === this.lastTabStop && this.firstTabStop) { e.preventDefault(); this.firstTabStop.focus(); } diff --git a/common/config/data.tsx b/common/config/data.tsx index 116e7a641d1..12f5b46b607 100644 --- a/common/config/data.tsx +++ b/common/config/data.tsx @@ -62,7 +62,8 @@ export const ethercardReferralURL = export enum SecureWalletName { WEB3 = 'web3', LEDGER_NANO_S = 'ledgerNanoS', - TREZOR = 'trezor' + TREZOR = 'trezor', + PARITY_SIGNER = 'paritySigner' } export enum HardwareWalletName { diff --git a/common/containers/QrSignerModal.tsx b/common/containers/QrSignerModal.tsx new file mode 100644 index 00000000000..326d58f65d2 --- /dev/null +++ b/common/containers/QrSignerModal.tsx @@ -0,0 +1,111 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import EthTx from 'ethereumjs-tx'; +import QrSigner from '@parity/qr-signer'; +import { AppState } from 'reducers'; +import Modal, { IButton } from 'components/ui/Modal'; + +interface State { + scan: boolean; +} + +interface PropsClosed { + isOpen: false; +} + +interface PropsOpen { + isOpen: true; + from: string; + tx: EthTx; + onSignature(signature: string): void; + onCancel(): void; +} + +type Props = PropsClosed | PropsOpen; + +class QrSignerModal extends React.Component { + constructor(props: Props) { + super(props); + this.state = { + scan: false + }; + } + + public render() { + if (!this.props.isOpen) { + return null; + } + + const { scan } = this.state; + const { tx, from } = this.props; + + // Poor man's serialize without signature. + // All those values are later overriden by actual signature + // values in: wallets/non-deterministic/parity.ts + tx.v = Buffer.from([tx._chainId]); + tx.r = Buffer.from([0]); + tx.s = Buffer.from([0]); + + const rlp = '0x' + tx.serialize().toString('hex'); + const buttons: IButton[] = [ + { + disabled: false, + text: scan ? 'Show Transaction' : 'Scan Signature', + type: 'primary', + onClick: () => this.setState({ scan: !scan }) + }, + { + disabled: false, + text: 'Cancel', + type: 'default', + onClick: this.onClose + } + ]; + + return ( +
+ + + +
+ ); + } + + private onClose = () => { + if (!this.props.isOpen) { + return; + } + + this.props.onCancel(); + this.setState({ scan: false }); + }; + + private onScan = (signature: string) => { + if (!this.props.isOpen) { + return; + } + + this.props.onSignature(signature); + this.setState({ scan: false }); + }; +} + +function mapStateToProps(state: AppState) { + const { signViaQr } = state.wallet; + + if (!signViaQr) { + return { isOpen: false }; + } + + const { tx, from, onSignature, onCancel } = signViaQr; + + return { + isOpen: true, + tx, + from, + onSignature, + onCancel + }; +} + +export default connect(mapStateToProps, {})(QrSignerModal); diff --git a/common/libs/wallet/non-deterministic/parity.ts b/common/libs/wallet/non-deterministic/parity.ts new file mode 100644 index 00000000000..53df9e8d2ce --- /dev/null +++ b/common/libs/wallet/non-deterministic/parity.ts @@ -0,0 +1,59 @@ +import EthTx from 'ethereumjs-tx'; +import { translateRaw } from 'translations'; +import { IFullWallet } from '../IWallet'; +import { TSetWalletQrTransaction, TFinalizeQrTransaction } from 'actions/wallet'; + +export default class ParityWallet implements IFullWallet { + public address: string; + private setWalletQrTransaction: TSetWalletQrTransaction; + private finalizeQrTransaction: TFinalizeQrTransaction; + + constructor( + address: string, + setWalletQrTransaction: TSetWalletQrTransaction, + finalizeQrTransaction: TFinalizeQrTransaction + ) { + this.address = address; + this.setWalletQrTransaction = setWalletQrTransaction; + this.finalizeQrTransaction = finalizeQrTransaction; + } + + public signRawTransaction(tx: EthTx): Promise { + return new Promise((resolve, reject) => { + const from = this.address; + + const onCancel = () => { + this.finalizeQrTransaction(); + reject(new Error(translateRaw('ADD_Parity_1'))); + }; + + const onSignature = (signature: string) => { + this.finalizeQrTransaction(); + + const sigBuf = Buffer.from(signature.substr(2), 'hex'); + + // Mimicking the way tx.sign() works + let v = sigBuf[64] + 27; + + if (tx._chainId > 0) { + v += tx._chainId * 2 + 8; + } + + tx.r = sigBuf.slice(0, 32); + tx.s = sigBuf.slice(32, 64); + tx.v = Buffer.from([v]); + + resolve(tx.serialize()); + }; + + this.setWalletQrTransaction(tx, from, onSignature, onCancel); + }); + } + + public signMessage = () => + Promise.reject(new Error('Signing via Parity Signer not yet supported.')); + + public getAddressString() { + return this.address; + } +} diff --git a/common/libs/wallet/non-deterministic/wallets.ts b/common/libs/wallet/non-deterministic/wallets.ts index 07aec4ef9ea..4746146be48 100644 --- a/common/libs/wallet/non-deterministic/wallets.ts +++ b/common/libs/wallet/non-deterministic/wallets.ts @@ -5,6 +5,7 @@ import { decryptPrivKey } from 'libs/decrypt'; import { fromV3 } from 'libs/web-workers'; import Web3Wallet from './web3'; import AddressOnlyWallet from './address'; +import ParitySignerWallet from './parity'; const EncryptedPrivateKeyWallet = (encryptedPrivateKey: string, password: string) => signWrapper(fromPrivateKey(decryptPrivKey(encryptedPrivateKey, password))); @@ -26,5 +27,6 @@ export { PrivKeyWallet, UtcWallet, Web3Wallet, - AddressOnlyWallet + AddressOnlyWallet, + ParitySignerWallet }; diff --git a/common/reducers/wallet.ts b/common/reducers/wallet.ts index 6121ebebf7b..c445e86c426 100644 --- a/common/reducers/wallet.ts +++ b/common/reducers/wallet.ts @@ -1,4 +1,8 @@ -import { SetBalanceFullfilledAction } from 'actions/wallet/actionTypes'; +import EthTx from 'ethereumjs-tx'; +import { + SetBalanceFullfilledAction, + SetWalletQrTransactionAction +} from 'actions/wallet/actionTypes'; import { SetTokenBalancesFulfilledAction, SetWalletAction, @@ -14,6 +18,7 @@ import { IWallet, Balance, WalletConfig } from 'libs/wallet'; export interface State { inst?: IWallet | null; config?: WalletConfig | null; + signViaQr?: QrSignatureState | null; // in ETH balance: Balance; tokens: { @@ -29,9 +34,21 @@ export interface State { hasSavedWalletTokens: boolean; } +export type QrTransaction = EthTx; +export type QrSignature = string; +export type QrAddress = string; + +export interface QrSignatureState { + tx: QrTransaction; + from: QrAddress; + onSignature(signature: QrSignature): void; + onCancel(): void; +} + export const INITIAL_STATE: State = { inst: null, config: null, + signViaQr: null, balance: { isPending: false, wei: null }, tokens: {}, isWalletPending: false, @@ -144,6 +161,20 @@ function setWalletConfig(state: State, action: SetWalletConfigAction): State { }; } +function setQrTransaction(state: State, action: SetWalletQrTransactionAction): State { + return { + ...state, + signViaQr: action.payload + }; +} + +function finalizeQrTx(state: State): State { + return { + ...state, + signViaQr: null + }; +} + export function wallet(state: State = INITIAL_STATE, action: WalletAction): State { switch (action.type) { case TypeKeys.WALLET_SET: @@ -178,6 +209,10 @@ export function wallet(state: State = INITIAL_STATE, action: WalletAction): Stat return setWalletConfig(state, action); case TypeKeys.WALLET_SET_PASSWORD_PENDING: return setPasswordPending(state); + case TypeKeys.WALLET_SET_QR_TRANSACTION: + return setQrTransaction(state, action); + case TypeKeys.WALLET_FINALIZE_QR_TX: + return finalizeQrTx(state); default: return state; } diff --git a/common/translations/lang/en.json b/common/translations/lang/en.json index 2f1fb36ba52..d7aee8b572f 100644 --- a/common/translations/lang/en.json +++ b/common/translations/lang/en.json @@ -137,6 +137,8 @@ "ADD_MetaMask": "Connect to MetaMask ", "x_Trezor": "TREZOR ", "ADD_Trezor_scan": "Connect to TREZOR ", + "x_ParitySigner": "Parity Signer ", + "ADD_Parity_1": "Transaction canceled ", "x_DigitalBitbox": "Digital Bitbox ", "ADD_DigitalBitbox_0a": "Re-open MyCrypto on a secure (SSL) connection ", "ADD_DigitalBitbox_0b": "Re-open MyCrypto using [Chrome](https://www.google.com/chrome/browser/desktop/) or [Opera](https://www.opera.com/) ", @@ -166,6 +168,7 @@ "ADD_Label_8": "Password (optional): ", "ADD_Web3Desc": "Connect & Sign via Your Browser or Extension", "ADD_HardwareDesc": "Connect & sign via your hardware wallet", + "ADD_ParityDesc": "Connect & Sign via Parity Signer mobile app", "MYWAL_Nick": "Wallet Nickname ", "MYWAL_Address": "Wallet Address ", "MYWAL_Bal": "Balance ", diff --git a/common/typescript/parity.d.ts b/common/typescript/parity.d.ts new file mode 100644 index 00000000000..2fa21763d76 --- /dev/null +++ b/common/typescript/parity.d.ts @@ -0,0 +1,4 @@ +declare module '@parity/qr-signer' { + const content: any; + export default content; +} diff --git a/package.json b/package.json index 51c34811e1d..9f2e1dc6996 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "npm": ">= 5.0.0" }, "dependencies": { + "@parity/qr-signer": "0.1.1", "babel-polyfill": "6.26.0", "bip39": "2.5.0", "bn.js": "4.11.8", From 83feb9a961b1e7c42cbec02b01f0833b7741a3c5 Mon Sep 17 00:00:00 2001 From: maciejhirsz Date: Wed, 21 Mar 2018 14:28:52 +0100 Subject: [PATCH 02/13] ParitySigner to be a container --- common/components/WalletDecrypt/WalletDecrypt.tsx | 2 +- common/components/WalletDecrypt/components/index.tsx | 4 ---- .../ParitySigner.tsx => containers/ParitySignerDecrypt.tsx} | 2 -- 3 files changed, 1 insertion(+), 7 deletions(-) rename common/{components/WalletDecrypt/components/ParitySigner.tsx => containers/ParitySignerDecrypt.tsx} (93%) diff --git a/common/components/WalletDecrypt/WalletDecrypt.tsx b/common/components/WalletDecrypt/WalletDecrypt.tsx index 791b68343c6..5a50f163747 100644 --- a/common/components/WalletDecrypt/WalletDecrypt.tsx +++ b/common/components/WalletDecrypt/WalletDecrypt.tsx @@ -27,11 +27,11 @@ import { PrivateKeyValue, TrezorDecrypt, ViewOnlyDecrypt, - ParitySignerDecrypt, Web3Decrypt, WalletButton, InsecureWalletWarning } from './components'; +import ParitySignerDecrypt from 'containers/ParitySignerDecrypt'; import { AppState } from 'reducers'; import { showNotification, TShowNotification } from 'actions/notifications'; import { getDisabledWallets } from 'selectors/wallet'; diff --git a/common/components/WalletDecrypt/components/index.tsx b/common/components/WalletDecrypt/components/index.tsx index 408abc4ad2a..7e68311140c 100644 --- a/common/components/WalletDecrypt/components/index.tsx +++ b/common/components/WalletDecrypt/components/index.tsx @@ -9,7 +9,3 @@ export * from './Trezor'; export * from './ViewOnly'; export * from './WalletButton'; export * from './Web3'; - -import Foo from './ParitySigner'; - -export const ParitySignerDecrypt = Foo; diff --git a/common/components/WalletDecrypt/components/ParitySigner.tsx b/common/containers/ParitySignerDecrypt.tsx similarity index 93% rename from common/components/WalletDecrypt/components/ParitySigner.tsx rename to common/containers/ParitySignerDecrypt.tsx index 0fd2b67ce59..b4abe7ebe0c 100644 --- a/common/components/WalletDecrypt/components/ParitySigner.tsx +++ b/common/containers/ParitySignerDecrypt.tsx @@ -1,8 +1,6 @@ import React, { PureComponent } from 'react'; import { connect } from 'react-redux'; import QrSigner from '@parity/qr-signer'; -// import translate from 'translations'; -// import { donationAddressMap } from 'config'; import { isValidETHAddress } from 'libs/validators'; import { ParitySignerWallet } from 'libs/wallet'; import { From ac6149b0cbb96bbf443e9bce7c96db15cd8d9804 Mon Sep 17 00:00:00 2001 From: maciejhirsz Date: Mon, 26 Mar 2018 16:19:12 +0200 Subject: [PATCH 03/13] Parity Signer: style and polish --- .../assets/images/mobile/app-store-badge.png | Bin 0 -> 9582 bytes .../images/mobile/google-play-badge.png | Bin 0 -> 11480 bytes common/assets/images/wallets/paritysigner.svg | 3 +++ .../WalletDecrypt/WalletDecrypt.tsx | 5 ++-- common/components/WalletDecrypt/disables.ts | 3 ++- .../containers/ParitySignerDecrypt/index.scss | 14 +++++++++++ .../index.tsx} | 22 +++++++++++++++++- common/containers/QrSignerModal/index.scss | 8 +++++++ .../index.tsx} | 12 +++++++--- common/translations/lang/en.json | 4 ++++ 10 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 common/assets/images/mobile/app-store-badge.png create mode 100644 common/assets/images/mobile/google-play-badge.png create mode 100644 common/assets/images/wallets/paritysigner.svg create mode 100644 common/containers/ParitySignerDecrypt/index.scss rename common/containers/{ParitySignerDecrypt.tsx => ParitySignerDecrypt/index.tsx} (57%) create mode 100644 common/containers/QrSignerModal/index.scss rename common/containers/{QrSignerModal.tsx => QrSignerModal/index.tsx} (85%) diff --git a/common/assets/images/mobile/app-store-badge.png b/common/assets/images/mobile/app-store-badge.png new file mode 100644 index 0000000000000000000000000000000000000000..24cf61bc44743a559b8b3f8fdec8ab9e076aeec2 GIT binary patch literal 9582 zcmW++2Rv2p8$Z${WMyS!W@YbD>SkqRbM3v7UD-0SL$-)ZwrnALK_y zN+WSkjjzDpbf&VZiU{J)j3B-N2yy~%`K}^}D>s6y8zG2jB7%@PqR!`@fD5-T1JGS}Fm!z}@ z^8Ut{GN#D@Ig+K1MzQVk;B0W2S7IX>85t$7whUrpW3PKr!+m`C3JVKwk&_40NVp>p zulV}NGrrGkGQ`;^9&17&(ijXz*lqVyKW|uY@agv?aW~$p_;;=&frvOGQK#2b*ped38;=7Fx>6>fVc=)mX?fB8cVNYgq7Rhs&@5_3T;H5#>F4`V4`J7gLl4nxFkX zexyP6ho4lgT{1B-L76kbF^$E=#o6_dg3w~xjf{z29633;N8QYD)t0+FJSsb-jra2- z9!007a#rZUoAh^_oSjK&XGX6WJwu~IcXypIOGJc(gmcfHJxf2bO2x49WX#${kM z`Ni&0*brUa4$f0ULs}^*siWl&_D06W&26?rbyE|SwsVV%m&L@y$SEk^1YdmqXT1m6 zN^fc_=5Y{=gRLU5I6NnZWwh9k;9)IEd_e&_gBA+aMwMAI?eouzL-)(gq@<*b7I$g~ z7nimMufMZPOYL$V`>WCY^E^Etgd&B!w`ZFZB|HRo_x1=$NCLLE9WYNw2Ohy=@60x1 zW7@5VrhkZVXlZFNGBR$nu_K`p^5`cH4&0)y>zB#M$W9k%|24G);zq~BV6(HcKc8u! z#wEK?f<_T?n#{L_{MXadw$=FWe#Je-Z&Z0D7nMUz9WnY@x1h&)zU>C4eKG3e$9I!e z_6!UR*f(z6(5`nE`1$jv!_NE@r0H zWm=^M3O)-u4vw4M30#)#larGS@Vjo+3ytUe<_DJA+KDYA*O1xrKFP=0+T^EaXRrbR z-#-W~>@Iazzuuvb=g?_ckt$PCP*C80^;-`mBf}sfLb0>6qg~^~gs!w{|Ii2}pj~c( z*ZWZ-Y;n;{ui61?WAw9H5-sAszlxQo9tv;5Y|c*FHwK1;$klH}78i5D!$l`12D57w z86EzfA{T$nZEp~Q@+&Ff_S_$kDXFUJP=0V0(AI{67B+4VCFSPhQ!7x^)zu9M4CKKj zrxtsOg2!!XY3cv0D>Ti!yu7SkYI5biUbXMVCBG*NSdA7YbqUth)!m|@dABiEeDTyU z>L(rVs5hpR@!qPlWpV0$x#cTFzyEvc!#e%Fd$JB9asHxC?QLzh2nVKWU+L_Kii%d4 zci+x_H#n&JzI&+J3hQ(NweV^^52 zs}pDTQBuOMv>Nb>@%dTpsIH^a87;uT;D7w<*RR4qUb-+owW7MZl&K{s(N3hrlx90PBKV`8|`u$Hlzdx(@1*CLhCIqMudee*x8@# zuc>QjJVBujw`Ouy`qGFARU9384ps*;JAG)seEGr{CojhqH%OIJLeIcptDy-afnsH7 zUK&<6{;XdcufpH8ljd_SqUJqXsPic&M+WBFlmv%xV7A%cjrPxUy$9dUH3Ay5d9Bar z_BQ2I(Kt7UxXGz6c2qI{prnQyeSFp@D%q)p9ehK!hH@e&aM%R}JlO@F8*I@t3L+^hcHf9@ane+s>PKRncu&E zSD1F-$8Ea){+Yza#wL3|LG9r~1dGng%L@k&kBL6RcC07_rvT&U?~lFL*A%38KgXcn z{lmsiq} zaqHXCL0IF~Pl`#rS-lPFrzOyr-Jx2iufxK^I;W>U#(i-=u!$fY9VyTX3BD&PnoL@p znc4bjcu1p88Td4$90>q$+=k*NH~KNy8{_}=^|8=rL{JHp zbv>W@8W$W4IR7oQs=VBEef?)mxe?ArL~<)&lRqq$Jtd)WS1jw%Uf+Yj+vNs7CMGT+ zX1o-{hoidoP`_A6iT#YE<~eZF9SL_5q`yFmOLL;sv;#pTB_$DGfbW)`o|`;8JQrvN zi`Ad4NVU4Un(p)O9Le8|Ig)+)^s$8n^WND~cY>_4@-^hgj~}_NcAwwipt7%DD@<6+fj~Xy5!zeF`?@4^=$%%=Kl9H&nUdzP% zsOBF(v|*TU`c}0GwaO*h{hmO*U%GVZ^~TT$>5G&-X;zMQ|Kc84we4b`GhAzH>qlW) z_OMv9Yil=PEwKv z^xV;UL2-YTz2S1RxYMF+=0`alb@j{X6L$g$+GQ1wgC2mg=t<(ch6=br%xpAPq)$iB zVc2k|JDzi4SVctz3#nr_O)j!xThw4GcS_j}K5|VjEWCO3>eZvUAX@d0TXdiB7O1jH zpa!8rqmzWN^oel&QA`Py8m1lVWZT>kzcs(Yz_Y%C};l0Yd{ zw4JTs^@!^3kLyToVIfLgo#dj9U}@_9{;vH|Zb<;h?6}-R0n69g(Ghe^rg&ESv<@x!R=jx46(Zt2%qy|8is(G;?S^|%1oLBIHFyBb|Uu}jCR59{h zkFH@WD=UjiOCtcSaddKG0z)p`$HE3rpsJ?!0cE^X|8g|pgOGhocQ>w}pdi}%B=f*o zyQrv$T-bq5I-L9sh~G@R$uDesN*@IAMn^|i0eb!X{It8ee*7SZE=v@&qm1X&eVd+6 z=egU-vO3fFz%~-|1r*tvorR8t!K|kjpFoV3dy)#Z8iBS>cRFYhUpT$)?9Zga#BiQk zlFB$&<7rEtAy4E%JDDMZ;M0g-Pd}mkv)CD9HTi|}?M({5j12m=wl@CtN@)~|?!g0E zq{MMSnw)|HE0^Z@qAedD@^PvJ}jpRd{FAI0EyP&%eS`v+Np{$r5J(0kUHN?cz3;mD5>SRJ2xZgl>>D~dI{Z$H|1sD9 zhD(+A@UXG7v$C)Ry`vVB=CkNYhqf9+Pd7Ke`J~Dk7az~-y12YNnDHbGFkIQZ25j5= zPO}Q|V49xL*{ly9q(Q^VF%nIefwW^xQjm6ZbWEwJt`@#H&1p$qvkpan7vAPg3a{#Y zgMAH(HbMJv0aQA_RX^SIX&4!o#d$D1!&=iDY?52I9>R$<;;)-J?QLx2p?R|3zb9Ib zTtC`bSX^C|1^>Vp)0qn_U^62`MMX7HW{yirO3Je<>2(sTS*R`b?c2AMQxN^|B;R|06HoDs3Zg+Zo@I6k>h=%hMTNUHA z)zy9&ZEi-!BhxFH3NK#VH#OYN2P;wEDWeN3G+d}t?w%mT%S#W2!)7F(bSrn=^@gUV z=HmSPBY-H^WdQ+!te2Y;BYDGd{-vcnHPzK6;T(ns4I*MYkpS|Hj9Y z!0%Gky}-rAy|~cY#s=;m@q6qX)OqUi?d|P}qONQK50#Y(8qfYvfxWw8-6#lbNO(zyQbN5!valze+f z&A#tiV0M0bOu02pMo4H)nXG4=^x;D$6z3yDA+9R>8EJL(*nX4X;NaZ!PL`=!*Ntx? ziw||5O&=8Fxok~S6SF8Sw#`7ljgxS+L}D;^!TIn&xx99F&(J8)EGa8{^yJAE==toWfUMaSZj}FER-Ih35S?7*~$Xfpe0XK0| zwTxhLbAzQLD4D8%n&f)eD#bJseX$gCIqY(p~d?yyz_qdNdW zxZQTmBDy8KPszbl+AP`4G)Mr-lhgjCS9}`7UI++=Db``a$$2r+1VUeDhV&ZGJ#xY4 z+L=fHy!Ceu4%%1Je0oD7BO=-XYXAok2?>eB4~ofP2}80h*F9$Mzh}A+nZZJz`TKliRmsod)Vi~jl$0jN zyGzxc$7Nk7B38BH#LaDlkJT?tL`-ZeQH?2W1s>4ZB_Sb+ijF3XfiiW~JXz?79EsO*Opfpa zju;f%s#D9MSFwO1vSH_-2fxd30zz@P=->I-Usol+y}*hJekFbAvpI>I8F{4fux=7= zyQs{sARnzENMc)Zc5s-DwFcF_H6PA44l}C5vY7k$f-KoNrC6re+1L{O`6{C!J2dKi z&*WEWIFvF6L{s?v2Y6lctjY0lj8?J1flW7b8Z01Xs&y5(v$*@8j}0hpbD{%^8_&_3 zCzF-7LAkm2`0c0lGHvI?upOY1FR~eMd>bPV@ZNwrh1l}-<`?Lix3RG#b)LuXV=Sz# zL!e`g`qQQA4%pHbKVQ_ScBM@m+G%ZVZ4T`EYalC~oxZdO9WY)(h)F=2JKpA${QHZK zL$CT-$vMg5a7rAzmd)wb$ndcEwPMysAeKd(!l}jL&+>bn zxX8=N&Bd*NLmtZA0_IwUbU!`f$@v8#NG{B)f~3B_KBcJ3y_LAs;MMVR%O1j)U0`GqV}w{)Lk{Wz z4ZyA4gWLo_UuTKb@;2KZFj<-O9qU)o+J&JNa__=E!48=zME2$V$POw=i!e z6gQlNGCj7oX5ISOoJ{acQ!^gj>e4=!Bp|1#sBw&(93SuW)^4+s(9+Vzoz<9i5*biL zL`1ChV;x(JTxUqpEeX(U<^c4^>=YQ{UU}O8c zQ#-L;mWibK?0l&LnqPS=rf6U7ro=(F>B#$BL-+>t2`k{!NaKmI8o$!5_G^xY$Oi z&vr7=+>9A@dK`qmRH31w(k#|Bp9!4z_2nkomI~y8SzKpN&tiC?&D-RwGs6s|B1$hJn@!vxWibwuJp|AHaBy@vs@^P52udmUIvnINs`1I*Q8>n~zy`a1hqgt=SGba?d&!i_TN&FLCvna`e{(>Es|)-gIDBPE>!i@Q4dnTi;U!L@7G zg50TMZ9^fu9@M^`T@rj9DyPlo-(N!v>e+{= zs3_?J-hqrKxV?k51qB6NT8}isK?j%yW)=N&Z=m%PE%G~x|D|y;+p5}#5zx%Q!L}4i z*m3?c@bw#TNT!gJm6VnmFC{(jkpv10fC#A3$>ZTc&4;eVr6o*7h5F|*&sr&)uI?aF z$@-&M1qiP;v=o8nzFj=!ZiTm|K0$_tK>880hGH`DRpThf`N_2|PnT zFQ3dKuiXUD%#$-QL-02GgOvV_{)IF6v_CUolXq!>5$O9JI5`D(HHGrtY`| zIv&p{0=ewS&Y6py3l5|ZU5NvE?k&}D^mw%+0}d`ORb@o-w~T`q`9 z|MD;r_S+O;un7D|l`w-;wxfk}zo-uO_b+xH&btm8J&%c;YElj;vqGF!Ha6Z;9`CTW z_V+(~cS$Sg=;-Kbv>fr}Lv1pURR92UpR1;c&?NW-w32NQxDV&rX=`g6HThx{Rzu6= z9psC99y#=0{SnO|W3tp0XHx}PVWwsSz_auGI(p|4|6j~#Rm90HQqp!6Z9EZ{yj}p4hxmP(nC zMId6o>1Za%XW2xEZcWv8sAvP$jLI5UzL-LP))1E@ehYPxtI;U>11J+9r?48<5!gog ztaliZY#)d^6lG!x%qnqlahZj^CP-UGbmHxwY=eBjqbHj=)sCc*FrW%#TD=qR2ihO@ zLlmrdv_XPwPlyTd3us-#ngxuuiHiscnNzTNwJ{noMjgsFoK(dlAh6_RC*i!TkZKO9 zcqEzoBe(G-%ZePOvx9c%UE&-Y$vFGD^!tYbp4pcKHq(wEOKt;>7ugZ;!i*$9%I~~9 z#H^6mPvwuOCFeDhjnUfzQy^>?6cAvg-VsFW{a+pf5VKG*TLLTDeZ6OE zL2Qt}l(z>2TMd4aOR3BMvJ4vx0}yn)-T=Iuo8pa)j>b6kQ5(ua?mm>GJXB)b`d?Gi zLvu+P85vRN)SUXlLXJ3H#cbuY-pLNvnuGC5Te2KIRaGJ#ZSC#lt?_ae%RU;F18Lia zQ-P3(2#tbb*w+y@@9yqigMBMCTVq4T1Zna8v-vv?;9IyNhDf`el zEUiF(>(8GlnTvo(uMZ1HmKW9_;Hq$0Q(cD?2zD2S+CqpmPUPifWoHlE)8&m>!R?Vl z>ae@N&-mcM19XF@h=luou#ml7?=~=6?=R4RtZZzdnnikYhTg|aSFT(cC^PR~1%IV( zV9+}`{a-YL?HR+;!s+oIzt6cR4i3)zl=#oDuC~(><*$DK#Q6FmyaEDD>Fz^&b#8mF z<20T>FO(-B_H-NBEWeP#hsi~TjY^OKrvpr0Xw}D%MGx1$vQHLvr2nj2_3q*@XvT~d zRs3XOPg_|?bdr1A(!k4-l8}^4aB*-HCbZUo1av4e{}xVR!^)AQkgYkO%up3~n%Hr*3?_Cu3BZ))pt4Co>a;^4Y=f$r158hw zxV`T2=U4NOQd1QjopHu7VWTGG-P8aC+aq7zYD$kMR9$kltCc+4SF7C28yp(qf#YTt z7Wtf$06_d@c3`Kd8;<62BZ|`OFX>Z*J?DXa9#J7n>?SfpPnDJOHp@l#-9@>Z6uhV` z>bU&QzvY&F85eA)B`4tOpdACabM*Hw13$m|LK#3X;OYWr%06@C9z>WuOQo8<`g54P_@Ox&KmMs5We=5TkrwTiK*-EJs+M+vgu#wsd z95s+BLjVq+fsc6q8J<;gbV#Nx>}fQWNRA+smMClgm??lk-QrCu-EXK=dfiGUT+1(`I`*+=oWb6y z^DobJ+rxGl(62e?$sPV7!B(MVlXtxkQV1EZ!X}^}`Lj@dY7hW+!zXI0s_XTN5TKLF z2kEP?UaH3RUGSHI`=Y3x?V>$&$(ktAClvD`@e2Ne`SiiW73z7mBjDkG&Zt>j`a3;TEBse_T+kIxBi-c;wnk%n)$bcZfA_hH3?opJOG zhqN!4UjWB^1eODNeZxo);ob;T&D&={R1smxUBhig++Nv(+t}In+dxQw_!g4{3==MqB rIqOPek!rkIeqV?Ge}8D`AJ)wW3UCFS)nU2{ literal 0 HcmV?d00001 diff --git a/common/assets/images/mobile/google-play-badge.png b/common/assets/images/mobile/google-play-badge.png new file mode 100644 index 0000000000000000000000000000000000000000..e8a1b1291bb23db09f5ed490eafdba751f2e7463 GIT binary patch literal 11480 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 + + \ No newline at end of file diff --git a/common/components/WalletDecrypt/WalletDecrypt.tsx b/common/components/WalletDecrypt/WalletDecrypt.tsx index 5a50f163747..b569645ddc0 100644 --- a/common/components/WalletDecrypt/WalletDecrypt.tsx +++ b/common/components/WalletDecrypt/WalletDecrypt.tsx @@ -50,6 +50,7 @@ import LedgerIcon from 'assets/images/wallets/ledger.svg'; import MetamaskIcon from 'assets/images/wallets/metamask.svg'; import MistIcon from 'assets/images/wallets/mist.svg'; import TrezorIcon from 'assets/images/wallets/trezor.svg'; +import ParitySignerIcon from 'assets/images/wallets/paritysigner.svg'; import './WalletDecrypt.scss'; interface OwnProps { @@ -168,12 +169,12 @@ export class WalletDecrypt extends Component { }, [SecureWalletName.PARITY_SIGNER]: { lid: 'x_ParitySigner', - icon: TrezorIcon, + icon: ParitySignerIcon, description: 'ADD_ParityDesc', component: ParitySignerDecrypt, initialParams: {}, unlock: this.props.setWallet, - helpLink: 'https://doc.satoshilabs.com/trezor-apps/mew.html' + helpLink: 'https://github.com/paritytech/parity-signer/blob/master/README.md' }, [InsecureWalletName.KEYSTORE_FILE]: { lid: 'x_Keystore2', diff --git a/common/components/WalletDecrypt/disables.ts b/common/components/WalletDecrypt/disables.ts index 3ed1fde39be..7e8ce4013f9 100644 --- a/common/components/WalletDecrypt/disables.ts +++ b/common/components/WalletDecrypt/disables.ts @@ -22,9 +22,10 @@ export const DISABLE_WALLETS: { [key in WalletMode]: DisabledWallets } = { } }, [WalletMode.UNABLE_TO_SIGN]: { - wallets: [SecureWalletName.TREZOR, MiscWalletName.VIEW_ONLY], + wallets: [SecureWalletName.TREZOR, SecureWalletName.PARITY_SIGNER, MiscWalletName.VIEW_ONLY], reasons: { [SecureWalletName.TREZOR]: 'This wallet can’t sign messages', + [SecureWalletName.PARITY_SIGNER]: 'This wallet can’t sign messages', [MiscWalletName.VIEW_ONLY]: 'This wallet can’t sign messages' } } diff --git a/common/containers/ParitySignerDecrypt/index.scss b/common/containers/ParitySignerDecrypt/index.scss new file mode 100644 index 00000000000..134658c6b08 --- /dev/null +++ b/common/containers/ParitySignerDecrypt/index.scss @@ -0,0 +1,14 @@ +.ParitySignerUnlock { + a img { + display: inline-block; + height: 3em; + margin: 0 0.4em; + } + + .qr-bounds { + width: 300px; + height: 300px; + display: inline-block; + margin-bottom: 1.5em; + } +} diff --git a/common/containers/ParitySignerDecrypt.tsx b/common/containers/ParitySignerDecrypt/index.tsx similarity index 57% rename from common/containers/ParitySignerDecrypt.tsx rename to common/containers/ParitySignerDecrypt/index.tsx index b4abe7ebe0c..da82a061d92 100644 --- a/common/containers/ParitySignerDecrypt.tsx +++ b/common/containers/ParitySignerDecrypt/index.tsx @@ -1,5 +1,6 @@ import React, { PureComponent } from 'react'; import { connect } from 'react-redux'; +import translate from 'translations'; import QrSigner from '@parity/qr-signer'; import { isValidETHAddress } from 'libs/validators'; import { ParitySignerWallet } from 'libs/wallet'; @@ -9,6 +10,9 @@ import { finalizeQrTransaction, TFinalizeQrTransaction } from 'actions/wallet'; +import './index.scss'; +import AppStoreBadge from 'assets/images/mobile/app-store-badge.png'; +import GooglePlayBadge from 'assets/images/mobile/google-play-badge.png'; interface Props { setWalletQrTransaction: TSetWalletQrTransaction; @@ -20,7 +24,23 @@ class ParitySignerDecrypt extends PureComponent { public render() { return (
- +
+ +
+

{translate('ADD_Parity_2')}

+

+ + App Store + + + Google Play + +

); } diff --git a/common/containers/QrSignerModal/index.scss b/common/containers/QrSignerModal/index.scss new file mode 100644 index 00000000000..da5eaabcb92 --- /dev/null +++ b/common/containers/QrSignerModal/index.scss @@ -0,0 +1,8 @@ +.QrSignerModal { + .qr-bounds { + width: 300px; + height: 300px; + display: inline-block; + margin: 1em 0 0.5em; + } +} diff --git a/common/containers/QrSignerModal.tsx b/common/containers/QrSignerModal/index.tsx similarity index 85% rename from common/containers/QrSignerModal.tsx rename to common/containers/QrSignerModal/index.tsx index 326d58f65d2..fd68004d78c 100644 --- a/common/containers/QrSignerModal.tsx +++ b/common/containers/QrSignerModal/index.tsx @@ -1,9 +1,11 @@ import React from 'react'; import { connect } from 'react-redux'; +import translate from 'translations'; import EthTx from 'ethereumjs-tx'; import QrSigner from '@parity/qr-signer'; import { AppState } from 'reducers'; import Modal, { IButton } from 'components/ui/Modal'; +import './index.scss'; interface State { scan: boolean; @@ -50,13 +52,13 @@ class QrSignerModal extends React.Component { const buttons: IButton[] = [ { disabled: false, - text: scan ? 'Show Transaction' : 'Scan Signature', + text: translate(scan ? 'ADD_Parity_5' : 'ADD_Parity_4'), type: 'primary', onClick: () => this.setState({ scan: !scan }) }, { disabled: false, - text: 'Cancel', + text: translate('SENDModal_No'), type: 'default', onClick: this.onClose } @@ -65,7 +67,11 @@ class QrSignerModal extends React.Component { return (
- +
{translate('ADD_Parity_3')}
+ +
+ +
); diff --git a/common/translations/lang/en.json b/common/translations/lang/en.json index d7aee8b572f..aaa6ce3bbe7 100644 --- a/common/translations/lang/en.json +++ b/common/translations/lang/en.json @@ -139,6 +139,10 @@ "ADD_Trezor_scan": "Connect to TREZOR ", "x_ParitySigner": "Parity Signer ", "ADD_Parity_1": "Transaction canceled ", + "ADD_Parity_2": "Download the app ", + "ADD_Parity_3": "Scan Signature ", + "ADD_Parity_4": "Scan ", + "ADD_Parity_5": "Back ", "x_DigitalBitbox": "Digital Bitbox ", "ADD_DigitalBitbox_0a": "Re-open MyCrypto on a secure (SSL) connection ", "ADD_DigitalBitbox_0b": "Re-open MyCrypto using [Chrome](https://www.google.com/chrome/browser/desktop/) or [Opera](https://www.opera.com/) ", From b73a4b47a41201ab946a52aec36c386781bb1adf Mon Sep 17 00:00:00 2001 From: maciejhirsz Date: Mon, 26 Mar 2018 17:16:52 +0200 Subject: [PATCH 04/13] target blank on appstore links --- common/containers/ParitySignerDecrypt/index.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/common/containers/ParitySignerDecrypt/index.tsx b/common/containers/ParitySignerDecrypt/index.tsx index d0e73de79c4..0e69dca7f8e 100644 --- a/common/containers/ParitySignerDecrypt/index.tsx +++ b/common/containers/ParitySignerDecrypt/index.tsx @@ -34,10 +34,18 @@ class ParitySignerDecrypt extends PureComponent {

{translate('ADD_PARITY_2')}

- + App Store - + Google Play

From 4a7dd0fecf3b9011dcadafcc97d26b7e9e849550 Mon Sep 17 00:00:00 2001 From: maciejhirsz Date: Mon, 26 Mar 2018 19:28:21 +0200 Subject: [PATCH 05/13] PR fixes --- .../containers/ParitySignerDecrypt/index.scss | 4 ++-- .../containers/ParitySignerDecrypt/index.tsx | 23 +++++++------------ common/containers/QrSignerModal/index.scss | 2 +- common/containers/QrSignerModal/index.tsx | 12 +++------- common/utils/helpers.ts | 19 +++++++++++++++ 5 files changed, 33 insertions(+), 27 deletions(-) diff --git a/common/containers/ParitySignerDecrypt/index.scss b/common/containers/ParitySignerDecrypt/index.scss index 134658c6b08..f1da8d098fe 100644 --- a/common/containers/ParitySignerDecrypt/index.scss +++ b/common/containers/ParitySignerDecrypt/index.scss @@ -1,11 +1,11 @@ .ParitySignerUnlock { - a img { + &-badge { display: inline-block; height: 3em; margin: 0 0.4em; } - .qr-bounds { + &-qr-bounds { width: 300px; height: 300px; display: inline-block; diff --git a/common/containers/ParitySignerDecrypt/index.tsx b/common/containers/ParitySignerDecrypt/index.tsx index 0e69dca7f8e..e08d3a7354a 100644 --- a/common/containers/ParitySignerDecrypt/index.tsx +++ b/common/containers/ParitySignerDecrypt/index.tsx @@ -1,6 +1,7 @@ import React, { PureComponent } from 'react'; import { connect } from 'react-redux'; import translate from 'translations'; +import { NewTabLink } from 'components/ui'; import QrSigner from '@parity/qr-signer'; import { isValidETHAddress } from 'libs/validators'; import { ParitySignerWallet } from 'libs/wallet'; @@ -24,7 +25,7 @@ class ParitySignerDecrypt extends PureComponent { public render() { return (
-
+
{

{translate('ADD_PARITY_2')}

- - App Store - - - Google Play - + + App Store + + + Google Play +

); diff --git a/common/containers/QrSignerModal/index.scss b/common/containers/QrSignerModal/index.scss index 3e5b4f28175..0f1b1497fc8 100644 --- a/common/containers/QrSignerModal/index.scss +++ b/common/containers/QrSignerModal/index.scss @@ -1,5 +1,5 @@ .QrSignerModal { - .qr-bounds { + &-qr-bounds { width: 300px; height: 300px; display: inline-block; diff --git a/common/containers/QrSignerModal/index.tsx b/common/containers/QrSignerModal/index.tsx index cf83449c44f..1672ddddda7 100644 --- a/common/containers/QrSignerModal/index.tsx +++ b/common/containers/QrSignerModal/index.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { connect } from 'react-redux'; import translate, { translateRaw } from 'translations'; import EthTx from 'ethereumjs-tx'; +import { transactionToRLP } from 'utils/helpers'; import QrSigner from '@parity/qr-signer'; import { AppState } from 'reducers'; import Modal, { IButton } from 'components/ui/Modal'; @@ -41,14 +42,7 @@ class QrSignerModal extends React.Component { const { scan } = this.state; const { tx, from } = this.props; - // Poor man's serialize without signature. - // All those values are later overriden by actual signature - // values in: wallets/non-deterministic/parity.ts - tx.v = Buffer.from([tx._chainId]); - tx.r = Buffer.from([0]); - tx.s = Buffer.from([0]); - - const rlp = '0x' + tx.serialize().toString('hex'); + const rlp = transactionToRLP(tx); const buttons: IButton[] = [ { disabled: false, @@ -72,7 +66,7 @@ class QrSignerModal extends React.Component { buttons={buttons} handleClose={this.onClose} > -
+
diff --git a/common/utils/helpers.ts b/common/utils/helpers.ts index 52ca845640d..63f26765d70 100644 --- a/common/utils/helpers.ts +++ b/common/utils/helpers.ts @@ -1,5 +1,6 @@ import qs from 'query-string'; import has from 'lodash/has'; +import EthTx from 'ethereumjs-tx'; interface IObjectValue { [key: string]: any; @@ -37,3 +38,21 @@ export function isPositiveInteger(n: number) { export const getValues = (...args: any[]) => args.reduce((acc, currArg) => [...acc, ...Object.values(currArg)], []); + +export function transactionToRLP(tx: EthTx): string { + const { v, r, s } = tx; + + // Poor man's serialize without signature. + tx.v = Buffer.from([tx._chainId]); + tx.r = Buffer.from([0]); + tx.s = Buffer.from([0]); + + const rlp = '0x' + tx.serialize().toString('hex'); + + // Restore previous values + tx.v = v; + tx.r = r; + tx.s = s; + + return rlp; +} From ecdfab7cb3e8947025a884313f90de05fc990a5b Mon Sep 17 00:00:00 2001 From: maciejhirsz Date: Wed, 28 Mar 2018 15:18:06 +0200 Subject: [PATCH 06/13] Move QrSignerModal to SendTransaction container --- common/components/Footer/index.tsx | 2 -- common/containers/Tabs/SendTransaction/index.tsx | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/components/Footer/index.tsx b/common/components/Footer/index.tsx index 646f0f083cb..9cb7d5f4cfd 100644 --- a/common/components/Footer/index.tsx +++ b/common/components/Footer/index.tsx @@ -12,7 +12,6 @@ import React from 'react'; import PreFooter from './PreFooter'; import DisclaimerModal from './DisclaimerModal'; import { NewTabLink } from 'components/ui'; -import QrSignerModal from 'containers/QrSignerModal'; import OnboardModal from 'containers/OnboardModal'; import './index.scss'; import { translateRaw } from 'translations'; @@ -220,7 +219,6 @@ export default class Footer extends React.PureComponent {
-
diff --git a/common/containers/Tabs/SendTransaction/index.tsx b/common/containers/Tabs/SendTransaction/index.tsx index 96cc2c7730e..6555692ec03 100644 --- a/common/containers/Tabs/SendTransaction/index.tsx +++ b/common/containers/Tabs/SendTransaction/index.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { connect } from 'react-redux'; import translate from 'translations'; import TabSection from 'containers/TabSection'; +import QrSignerModal from 'containers/QrSignerModal'; import { UnlockHeader } from 'components/ui'; import { getWalletInst } from 'selectors/wallet'; import { AppState } from 'reducers'; @@ -108,6 +109,7 @@ class SendTransaction extends React.Component { )} + ); } From e04e2e695e56e3631f66c926f2f29e1baf9b8ca2 Mon Sep 17 00:00:00 2001 From: maciejhirsz Date: Fri, 6 Apr 2018 18:40:09 +0200 Subject: [PATCH 07/13] Rework redux, use signing saga --- common/actions/paritySigner/actionCreators.ts | 21 ++++++++ common/actions/paritySigner/actionTypes.ts | 17 +++++++ common/actions/paritySigner/constants.ts | 4 ++ common/actions/paritySigner/index.ts | 3 ++ common/actions/wallet/actionCreators.ts | 26 ---------- common/actions/wallet/actionTypes.ts | 14 +----- .../containers/ParitySignerDecrypt/index.tsx | 25 ++-------- common/containers/QrSignerModal/index.tsx | 28 +++++------ .../libs/wallet/non-deterministic/parity.ts | 48 ++----------------- common/reducers/index.ts | 3 ++ common/reducers/paritySigner.ts | 40 ++++++++++++++++ common/reducers/wallet.ts | 23 +-------- common/sagas/transaction/signing/signing.ts | 28 ++++++++++- common/selectors/wallet.ts | 13 ++++- common/utils/helpers.ts | 17 +++++++ 15 files changed, 165 insertions(+), 145 deletions(-) create mode 100644 common/actions/paritySigner/actionCreators.ts create mode 100644 common/actions/paritySigner/actionTypes.ts create mode 100644 common/actions/paritySigner/constants.ts create mode 100644 common/actions/paritySigner/index.ts create mode 100644 common/reducers/paritySigner.ts diff --git a/common/actions/paritySigner/actionCreators.ts b/common/actions/paritySigner/actionCreators.ts new file mode 100644 index 00000000000..ec38ad78e6d --- /dev/null +++ b/common/actions/paritySigner/actionCreators.ts @@ -0,0 +1,21 @@ +import * as types from './actionTypes'; +import { TypeKeys } from './constants'; + +export type TRequestSignature = typeof requestSignature; +export function requestSignature(from: string, rlp: string): types.RequestSignatureAction { + return { + type: TypeKeys.PARITY_SIGNER_REQUEST_SIGNATURE, + payload: { + from, + rlp + } + }; +} + +export type TFinalize = typeof finalize; +export function finalize(signature: string | null): types.FinalizeAction { + return { + type: TypeKeys.PARITY_SIGNER_FINALIZE, + payload: signature + }; +} diff --git a/common/actions/paritySigner/actionTypes.ts b/common/actions/paritySigner/actionTypes.ts new file mode 100644 index 00000000000..4bbf1276242 --- /dev/null +++ b/common/actions/paritySigner/actionTypes.ts @@ -0,0 +1,17 @@ +import { TypeKeys } from './constants'; + +export interface RequestSignatureAction { + type: TypeKeys.PARITY_SIGNER_REQUEST_SIGNATURE; + payload: { + rlp: string; + from: string; + }; +} + +export interface FinalizeAction { + type: TypeKeys.PARITY_SIGNER_FINALIZE; + payload: string | null; +} + +/*** Union Type ***/ +export type ParitySignerAction = RequestSignatureAction | FinalizeAction; diff --git a/common/actions/paritySigner/constants.ts b/common/actions/paritySigner/constants.ts new file mode 100644 index 00000000000..a195dafe18a --- /dev/null +++ b/common/actions/paritySigner/constants.ts @@ -0,0 +1,4 @@ +export enum TypeKeys { + PARITY_SIGNER_REQUEST_SIGNATURE = 'PARITY_SIGNER_REQUEST_SIGNATURE', + PARITY_SIGNER_FINALIZE = 'PARITY_SIGNER_FINALIZE' +} diff --git a/common/actions/paritySigner/index.ts b/common/actions/paritySigner/index.ts new file mode 100644 index 00000000000..fee14683980 --- /dev/null +++ b/common/actions/paritySigner/index.ts @@ -0,0 +1,3 @@ +export * from './constants'; +export * from './actionTypes'; +export * from './actionCreators'; diff --git a/common/actions/wallet/actionCreators.ts b/common/actions/wallet/actionCreators.ts index 9f8aea1e1ac..ad4cfc6a0fe 100644 --- a/common/actions/wallet/actionCreators.ts +++ b/common/actions/wallet/actionCreators.ts @@ -1,4 +1,3 @@ -import EthTx from 'ethereumjs-tx'; import { Wei, TokenValue } from 'libs/units'; import { IWallet, WalletConfig } from 'libs/wallet'; import * as types from './actionTypes'; @@ -167,28 +166,3 @@ export function setAccountBalance(): types.SetAccountBalanceAction { type: TypeKeys.WALLET_SET_ACCOUNT_BALANCE }; } - -export type TSetWalletQrTransaction = typeof setWalletQrTransaction; -export function setWalletQrTransaction( - tx: EthTx, - from: string, - onSignature: (signature: string) => void, - onCancel: () => void -): types.SetWalletQrTransactionAction { - return { - type: TypeKeys.WALLET_SET_QR_TRANSACTION, - payload: { - tx, - from, - onSignature, - onCancel - } - }; -} - -export type TFinalizeQrTransaction = typeof finalizeQrTransaction; -export function finalizeQrTransaction(): types.FinalizeWalletQrTransactionAction { - return { - type: TypeKeys.WALLET_FINALIZE_QR_TX - }; -} diff --git a/common/actions/wallet/actionTypes.ts b/common/actions/wallet/actionTypes.ts index a81719394ea..bda663901d6 100644 --- a/common/actions/wallet/actionTypes.ts +++ b/common/actions/wallet/actionTypes.ts @@ -1,6 +1,5 @@ import { Wei, TokenValue } from 'libs/units'; import { IWallet, WalletConfig } from 'libs/wallet'; -import { QrSignatureState } from 'reducers/wallet'; import { TypeKeys } from './constants'; /*** Unlock Private Key ***/ @@ -130,15 +129,6 @@ export interface SetAccountBalanceAction { type: TypeKeys.WALLET_SET_ACCOUNT_BALANCE; } -export interface SetWalletQrTransactionAction { - type: TypeKeys.WALLET_SET_QR_TRANSACTION; - payload: QrSignatureState; -} - -export interface FinalizeWalletQrTransactionAction { - type: TypeKeys.WALLET_FINALIZE_QR_TX; -} - /*** Union Type ***/ export type WalletAction = | UnlockPrivateKeyAction @@ -158,6 +148,4 @@ export type WalletAction = | SetWalletTokensAction | SetWalletConfigAction | SetPasswordPendingAction - | SetAccountBalanceAction - | SetWalletQrTransactionAction - | FinalizeWalletQrTransactionAction; + | SetAccountBalanceAction; diff --git a/common/containers/ParitySignerDecrypt/index.tsx b/common/containers/ParitySignerDecrypt/index.tsx index e08d3a7354a..f62fa0b5ae5 100644 --- a/common/containers/ParitySignerDecrypt/index.tsx +++ b/common/containers/ParitySignerDecrypt/index.tsx @@ -5,19 +5,13 @@ import { NewTabLink } from 'components/ui'; import QrSigner from '@parity/qr-signer'; import { isValidETHAddress } from 'libs/validators'; import { ParitySignerWallet } from 'libs/wallet'; -import { - setWalletQrTransaction, - TSetWalletQrTransaction, - finalizeQrTransaction, - TFinalizeQrTransaction -} from 'actions/wallet'; +import { showNotification, TShowNotification } from 'actions/notifications'; import './index.scss'; import AppStoreBadge from 'assets/images/mobile/app-store-badge.png'; import GooglePlayBadge from 'assets/images/mobile/google-play-badge.png'; interface Props { - setWalletQrTransaction: TSetWalletQrTransaction; - finalizeQrTransaction: TFinalizeQrTransaction; + showNotification: TShowNotification; onUnlock(param: any): void; } @@ -48,21 +42,12 @@ class ParitySignerDecrypt extends PureComponent { private unlockAddress = (address: string) => { if (!isValidETHAddress(address)) { - console.error('Invalid address!'); + this.props.showNotification('danger', 'Not a valid address!'); return; } - this.props.onUnlock( - new ParitySignerWallet( - address, - this.props.setWalletQrTransaction, - this.props.finalizeQrTransaction - ) - ); + this.props.onUnlock(new ParitySignerWallet(address)); }; } -export default connect(() => ({}), { - setWalletQrTransaction, - finalizeQrTransaction -})(ParitySignerDecrypt); +export default connect(() => ({}), { showNotification })(ParitySignerDecrypt); diff --git a/common/containers/QrSignerModal/index.tsx b/common/containers/QrSignerModal/index.tsx index 1672ddddda7..bb61c591f29 100644 --- a/common/containers/QrSignerModal/index.tsx +++ b/common/containers/QrSignerModal/index.tsx @@ -1,11 +1,10 @@ import React from 'react'; import { connect } from 'react-redux'; import translate, { translateRaw } from 'translations'; -import EthTx from 'ethereumjs-tx'; -import { transactionToRLP } from 'utils/helpers'; import QrSigner from '@parity/qr-signer'; import { AppState } from 'reducers'; import Modal, { IButton } from 'components/ui/Modal'; +import { TFinalize, finalize } from 'actions/paritySigner'; import './index.scss'; interface State { @@ -14,14 +13,14 @@ interface State { interface PropsClosed { isOpen: false; + finalize: TFinalize; } interface PropsOpen { isOpen: true; + rlp: string; from: string; - tx: EthTx; - onSignature(signature: string): void; - onCancel(): void; + finalize: TFinalize; } type Props = PropsClosed | PropsOpen; @@ -40,9 +39,8 @@ class QrSignerModal extends React.Component { } const { scan } = this.state; - const { tx, from } = this.props; + const { from, rlp } = this.props; - const rlp = transactionToRLP(tx); const buttons: IButton[] = [ { disabled: false, @@ -79,7 +77,7 @@ class QrSignerModal extends React.Component { return; } - this.props.onCancel(); + this.props.finalize(null); this.setState({ scan: false }); }; @@ -88,27 +86,25 @@ class QrSignerModal extends React.Component { return; } - this.props.onSignature(signature); + this.props.finalize(signature); this.setState({ scan: false }); }; } function mapStateToProps(state: AppState) { - const { signViaQr } = state.wallet; + const { requested } = state.paritySigner; - if (!signViaQr) { + if (!requested) { return { isOpen: false }; } - const { tx, from, onSignature, onCancel } = signViaQr; + const { from, rlp } = requested; return { isOpen: true, - tx, from, - onSignature, - onCancel + rlp }; } -export default connect(mapStateToProps, {})(QrSignerModal); +export default connect(mapStateToProps, { finalize })(QrSignerModal); diff --git a/common/libs/wallet/non-deterministic/parity.ts b/common/libs/wallet/non-deterministic/parity.ts index 34ad67be6a2..ba79dc58dc3 100644 --- a/common/libs/wallet/non-deterministic/parity.ts +++ b/common/libs/wallet/non-deterministic/parity.ts @@ -1,54 +1,14 @@ -import EthTx from 'ethereumjs-tx'; -import { translateRaw } from 'translations'; import { IFullWallet } from '../IWallet'; -import { TSetWalletQrTransaction, TFinalizeQrTransaction } from 'actions/wallet'; -export default class ParityWallet implements IFullWallet { +export default class ParitySignerWallet implements IFullWallet { public address: string; - private setWalletQrTransaction: TSetWalletQrTransaction; - private finalizeQrTransaction: TFinalizeQrTransaction; - constructor( - address: string, - setWalletQrTransaction: TSetWalletQrTransaction, - finalizeQrTransaction: TFinalizeQrTransaction - ) { + constructor(address: string) { this.address = address; - this.setWalletQrTransaction = setWalletQrTransaction; - this.finalizeQrTransaction = finalizeQrTransaction; } - public signRawTransaction(tx: EthTx): Promise { - return new Promise((resolve, reject) => { - const from = this.address; - - const onCancel = () => { - this.finalizeQrTransaction(); - reject(new Error(translateRaw('ADD_PARITY_1'))); - }; - - const onSignature = (signature: string) => { - this.finalizeQrTransaction(); - - const sigBuf = Buffer.from(signature.substr(2), 'hex'); - - // Mimicking the way tx.sign() works - let v = sigBuf[64] + 27; - - if (tx._chainId > 0) { - v += tx._chainId * 2 + 8; - } - - tx.r = sigBuf.slice(0, 32); - tx.s = sigBuf.slice(32, 64); - tx.v = Buffer.from([v]); - - resolve(tx.serialize()); - }; - - this.setWalletQrTransaction(tx, from, onSignature, onCancel); - }); - } + public signRawTransaction = () => + Promise.reject(new Error('Web3 wallets cannot sign raw transactions.')); public signMessage = () => Promise.reject(new Error('Signing via Parity Signer not yet supported.')); diff --git a/common/reducers/index.ts b/common/reducers/index.ts index d342f1a46a7..1b5e7163331 100644 --- a/common/reducers/index.ts +++ b/common/reducers/index.ts @@ -12,6 +12,7 @@ import { State as TransactionState, transaction } from './transaction'; import { State as GasState, gas } from './gas'; import { onboardStatus, State as OnboardStatusState } from './onboardStatus'; import { State as TransactionsState, transactions } from './transactions'; +import { State as ParitySignerState, paritySigner } from './paritySigner'; export interface AppState { // Custom reducers @@ -26,6 +27,7 @@ export interface AppState { swap: SwapState; transaction: TransactionState; transactions: TransactionsState; + paritySigner: ParitySignerState; gas: GasState; // Third party reducers (TODO: Fill these out) routing: any; @@ -43,6 +45,7 @@ export default combineReducers({ deterministicWallets, transaction, transactions, + paritySigner, gas, routing: routerReducer }); diff --git a/common/reducers/paritySigner.ts b/common/reducers/paritySigner.ts new file mode 100644 index 00000000000..1e86ae9d932 --- /dev/null +++ b/common/reducers/paritySigner.ts @@ -0,0 +1,40 @@ +import { RequestSignatureAction } from 'actions/paritySigner/actionTypes'; +import { ParitySignerAction, TypeKeys } from 'actions/paritySigner'; + +export interface State { + requested?: QrSignatureState | null; +} + +interface QrSignatureState { + from: string; + rlp: string; +} + +export const INITIAL_STATE: State = { + requested: null +}; + +function requestSignature(state: State, action: RequestSignatureAction): State { + return { + ...state, + requested: action.payload + }; +} + +function finalize(state: State): State { + return { + ...state, + requested: null + }; +} + +export function paritySigner(state: State = INITIAL_STATE, action: ParitySignerAction): State { + switch (action.type) { + case TypeKeys.PARITY_SIGNER_REQUEST_SIGNATURE: + return requestSignature(state, action); + case TypeKeys.PARITY_SIGNER_FINALIZE: + return finalize(state); + default: + return state; + } +} diff --git a/common/reducers/wallet.ts b/common/reducers/wallet.ts index 3ab671c1666..45a9bcbe5dc 100644 --- a/common/reducers/wallet.ts +++ b/common/reducers/wallet.ts @@ -1,8 +1,5 @@ import EthTx from 'ethereumjs-tx'; -import { - SetBalanceFullfilledAction, - SetWalletQrTransactionAction -} from 'actions/wallet/actionTypes'; +import { SetBalanceFullfilledAction } from 'actions/wallet/actionTypes'; import { SetTokenBalancesFulfilledAction, SetWalletAction, @@ -162,20 +159,6 @@ function setWalletConfig(state: State, action: SetWalletConfigAction): State { }; } -function setQrTransaction(state: State, action: SetWalletQrTransactionAction): State { - return { - ...state, - signViaQr: action.payload - }; -} - -function finalizeQrTx(state: State): State { - return { - ...state, - signViaQr: null - }; -} - export function wallet(state: State = INITIAL_STATE, action: WalletAction): State { switch (action.type) { case TypeKeys.WALLET_SET: @@ -210,10 +193,6 @@ export function wallet(state: State = INITIAL_STATE, action: WalletAction): Stat return setWalletConfig(state, action); case TypeKeys.WALLET_SET_PASSWORD_PENDING: return setPasswordPending(state); - case TypeKeys.WALLET_SET_QR_TRANSACTION: - return setQrTransaction(state, action); - case TypeKeys.WALLET_FINALIZE_QR_TX: - return finalizeQrTx(state); default: return state; } diff --git a/common/sagas/transaction/signing/signing.ts b/common/sagas/transaction/signing/signing.ts index 6121e1159ef..b6677ee4bb3 100644 --- a/common/sagas/transaction/signing/signing.ts +++ b/common/sagas/transaction/signing/signing.ts @@ -1,6 +1,7 @@ import { SagaIterator } from 'redux-saga'; -import { put, apply, takeEvery, call, select } from 'redux-saga/effects'; +import { put, apply, take, takeEvery, call, select } from 'redux-saga/effects'; import { IFullWalletAndTransaction, signTransactionWrapper } from './helpers'; +import { transactionToRLP, signTransactionWithSignature } from 'utils/helpers'; import { signLocalTransactionSucceeded, signWeb3TransactionSucceeded, @@ -13,6 +14,7 @@ import { import { computeIndexingHash } from 'libs/transaction'; import { serializedAndTransactionFieldsMatch } from 'selectors/transaction'; import { showNotification } from 'actions/notifications'; +import { requestSignature, TypeKeys as ParityKeys } from 'actions/paritySigner'; import { getWalletType, IWalletType } from 'selectors/wallet'; export function* signLocalTransactionHandler({ @@ -40,6 +42,24 @@ export function* signWeb3TransactionHandler({ tx }: IFullWalletAndTransaction): const signWeb3Transaction = signTransactionWrapper(signWeb3TransactionHandler); +export function* signParitySignerTransactionHandler({ + tx, + wallet +}: IFullWalletAndTransaction): SagaIterator { + const from = yield apply(wallet, wallet.getAddressString); + const rlp = yield call(transactionToRLP, tx); + + yield put(requestSignature(from, rlp)); + + const { payload }: { payload: string } = yield take(ParityKeys.PARITY_SIGNER_FINALIZE); + const signedTransaction: Buffer = yield call(signTransactionWithSignature, tx, payload); + const indexingHash: string = yield call(computeIndexingHash, signedTransaction); + + yield put(signLocalTransactionSucceeded({ signedTransaction, indexingHash, noVerify: false })); +} + +const signParitySignerTransaction = signTransactionWrapper(signParitySignerTransactionHandler); + /** * @description Verifies that the transaction matches the fields, and if its a locally signed transaction (so it has a signature) it will verify the signature too * @param {(SignWeb3TransactionSucceededAction | SignLocalTransactionSucceededAction)} { @@ -69,7 +89,11 @@ function* verifyTransaction({ function* handleTransactionRequest(action: SignTransactionRequestedAction): SagaIterator { const walletType: IWalletType = yield select(getWalletType); - const signingHandler = walletType.isWeb3Wallet ? signWeb3Transaction : signLocalTransaction; + + const signingHandler = walletType.isWeb3Wallet + ? signWeb3Transaction + : walletType.isParitySignerWallet ? signParitySignerTransaction : signLocalTransaction; + return yield call(signingHandler, action); } diff --git a/common/selectors/wallet.ts b/common/selectors/wallet.ts index 4caae15fe84..8f3c6aa39f4 100644 --- a/common/selectors/wallet.ts +++ b/common/selectors/wallet.ts @@ -2,7 +2,14 @@ import { TokenValue, Wei } from 'libs/units'; import { SecureWalletName, WalletName } from 'config'; import { AppState } from 'reducers'; import { getNetworkConfig, getOffline, getStaticNetworkConfig } from 'selectors/config'; -import { IWallet, Web3Wallet, LedgerWallet, TrezorWallet, WalletConfig } from 'libs/wallet'; +import { + IWallet, + Web3Wallet, + LedgerWallet, + TrezorWallet, + ParitySignerWallet, + WalletConfig +} from 'libs/wallet'; import { isEtherTransaction, getUnit } from './transaction'; import { DisabledWallets } from 'components/WalletDecrypt'; import { Token } from 'types/network'; @@ -96,6 +103,7 @@ export const getTokenWithBalance = (state: AppState, unit: string): TokenBalance export interface IWalletType { isWeb3Wallet: boolean; isHardwareWallet: boolean; + isParitySignerWallet: boolean; } export const getWallet = (state: AppState) => state.wallet; @@ -105,8 +113,9 @@ export const getWalletType = (state: AppState): IWalletType => { const isWeb3Wallet = wallet instanceof Web3Wallet; const isLedgerWallet = wallet instanceof LedgerWallet; const isTrezorWallet = wallet instanceof TrezorWallet; + const isParitySignerWallet = wallet instanceof ParitySignerWallet; const isHardwareWallet = isLedgerWallet || isTrezorWallet; - return { isWeb3Wallet, isHardwareWallet }; + return { isWeb3Wallet, isHardwareWallet, isParitySignerWallet }; }; export const isUnlocked = (state: AppState) => !!getWalletInst(state); diff --git a/common/utils/helpers.ts b/common/utils/helpers.ts index 63f26765d70..9e42eff9b74 100644 --- a/common/utils/helpers.ts +++ b/common/utils/helpers.ts @@ -56,3 +56,20 @@ export function transactionToRLP(tx: EthTx): string { return rlp; } + +export function signTransactionWithSignature(tx: EthTx, signature: string): Buffer { + const sigBuf = Buffer.from(signature.substr(2), 'hex'); + + // Mimicking the way tx.sign() works + let v = sigBuf[64] + 27; + + if (tx._chainId > 0) { + v += tx._chainId * 2 + 8; + } + + tx.r = sigBuf.slice(0, 32); + tx.s = sigBuf.slice(32, 64); + tx.v = Buffer.from([v]); + + return tx.serialize(); +} From a7380d0dc8dbf42249913a7c49b4b4cba679b9f8 Mon Sep 17 00:00:00 2001 From: maciejhirsz Date: Fri, 6 Apr 2018 18:47:26 +0200 Subject: [PATCH 08/13] Cleanup --- common/actions/wallet/constants.ts | 4 +--- common/components/ui/Modal/ModalBody.tsx | 4 +--- common/reducers/wallet.ts | 14 -------------- 3 files changed, 2 insertions(+), 20 deletions(-) diff --git a/common/actions/wallet/constants.ts b/common/actions/wallet/constants.ts index 516cb09fe3d..24990ea4277 100644 --- a/common/actions/wallet/constants.ts +++ b/common/actions/wallet/constants.ts @@ -20,7 +20,5 @@ export enum TypeKeys { WALLET_SET_CONFIG = 'WALLET_SET_CONFIG', WALLET_RESET = 'WALLET_RESET', WALLET_SET_PASSWORD_PENDING = 'WALLET_SET_PASSWORD_PENDING', - WALLET_SET_ACCOUNT_BALANCE = 'WALLET_SET_ACCOUNT_BALANCE', - WALLET_SET_QR_TRANSACTION = 'WALLET_SET_QR_TRANSACTION', - WALLET_FINALIZE_QR_TX = 'WALLET_FINALIZE_QR_TX' + WALLET_SET_ACCOUNT_BALANCE = 'WALLET_SET_ACCOUNT_BALANCE' } diff --git a/common/components/ui/Modal/ModalBody.tsx b/common/components/ui/Modal/ModalBody.tsx index 2a2d6b73b87..1f4d2fb8389 100644 --- a/common/components/ui/Modal/ModalBody.tsx +++ b/common/components/ui/Modal/ModalBody.tsx @@ -28,10 +28,8 @@ export default class ModalBody extends React.Component { this.modal.querySelectorAll(focusableElementsString) ); - const first = focusableElements[0]; - // Convert NodeList to Array - this.firstTabStop = first; + this.firstTabStop = focusableElements[0]; this.lastTabStop = focusableElements[focusableElements.length - 1]; this.modal.addEventListener('keydown', this.keyDownListener); diff --git a/common/reducers/wallet.ts b/common/reducers/wallet.ts index 45a9bcbe5dc..f29a35a04f8 100644 --- a/common/reducers/wallet.ts +++ b/common/reducers/wallet.ts @@ -1,4 +1,3 @@ -import EthTx from 'ethereumjs-tx'; import { SetBalanceFullfilledAction } from 'actions/wallet/actionTypes'; import { SetTokenBalancesFulfilledAction, @@ -16,7 +15,6 @@ import { translateRaw } from 'translations'; export interface State { inst?: IWallet | null; config?: WalletConfig | null; - signViaQr?: QrSignatureState | null; // in ETH balance: Balance; tokens: { @@ -32,21 +30,9 @@ export interface State { hasSavedWalletTokens: boolean; } -export type QrTransaction = EthTx; -export type QrSignature = string; -export type QrAddress = string; - -export interface QrSignatureState { - tx: QrTransaction; - from: QrAddress; - onSignature(signature: QrSignature): void; - onCancel(): void; -} - export const INITIAL_STATE: State = { inst: null, config: null, - signViaQr: null, balance: { isPending: false, wei: null }, tokens: {}, isWalletPending: false, From 047b7a9ff762bb9b7634a1cd886ce9af53f05cfa Mon Sep 17 00:00:00 2001 From: maciejhirsz Date: Fri, 6 Apr 2018 18:59:30 +0200 Subject: [PATCH 09/13] Use new logo, change helpLink --- .../assets/images/wallets/parity-signer.svg | 149 ++++++++++++++++++ common/assets/images/wallets/paritysigner.svg | 3 - .../WalletDecrypt/WalletDecrypt.tsx | 4 +- 3 files changed, 151 insertions(+), 5 deletions(-) create mode 100644 common/assets/images/wallets/parity-signer.svg delete mode 100644 common/assets/images/wallets/paritysigner.svg diff --git a/common/assets/images/wallets/parity-signer.svg b/common/assets/images/wallets/parity-signer.svg new file mode 100644 index 00000000000..3a521834e26 --- /dev/null +++ b/common/assets/images/wallets/parity-signer.svg @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/common/assets/images/wallets/paritysigner.svg b/common/assets/images/wallets/paritysigner.svg deleted file mode 100644 index c497f431ca8..00000000000 --- a/common/assets/images/wallets/paritysigner.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/common/components/WalletDecrypt/WalletDecrypt.tsx b/common/components/WalletDecrypt/WalletDecrypt.tsx index 523dc95c07a..151c5954281 100644 --- a/common/components/WalletDecrypt/WalletDecrypt.tsx +++ b/common/components/WalletDecrypt/WalletDecrypt.tsx @@ -50,7 +50,7 @@ import LedgerIcon from 'assets/images/wallets/ledger.svg'; import MetamaskIcon from 'assets/images/wallets/metamask.svg'; import MistIcon from 'assets/images/wallets/mist.svg'; import TrezorIcon from 'assets/images/wallets/trezor.svg'; -import ParitySignerIcon from 'assets/images/wallets/paritysigner.svg'; +import ParitySignerIcon from 'assets/images/wallets/parity-signer.svg'; import './WalletDecrypt.scss'; import { withRouter, RouteComponentProps } from 'react-router'; @@ -177,7 +177,7 @@ const WalletDecrypt = withRouter( component: ParitySignerDecrypt, initialParams: {}, unlock: this.props.setWallet, - helpLink: 'https://github.com/paritytech/parity-signer/blob/master/README.md' + helpLink: 'https://wiki.parity.io/Parity-Signer-Mobile-App-MyCrypto-tutorial.md' }, [InsecureWalletName.KEYSTORE_FILE]: { lid: 'X_KEYSTORE2', From 89397e9fe0ab8fa488ba3b8208f4b1121b5368f2 Mon Sep 17 00:00:00 2001 From: Will O'Beirne Date: Fri, 6 Apr 2018 15:29:35 -0400 Subject: [PATCH 10/13] Rework finalize actions and types a bit --- common/actions/paritySigner/actionCreators.ts | 6 +++--- common/actions/paritySigner/actionTypes.ts | 6 +++--- common/actions/paritySigner/constants.ts | 2 +- common/containers/QrSignerModal/index.tsx | 18 ++++++++++-------- common/reducers/paritySigner.ts | 9 ++++----- common/sagas/transaction/signing/signing.ts | 10 ++++++++-- 6 files changed, 29 insertions(+), 22 deletions(-) diff --git a/common/actions/paritySigner/actionCreators.ts b/common/actions/paritySigner/actionCreators.ts index ec38ad78e6d..1d8979a5a39 100644 --- a/common/actions/paritySigner/actionCreators.ts +++ b/common/actions/paritySigner/actionCreators.ts @@ -12,10 +12,10 @@ export function requestSignature(from: string, rlp: string): types.RequestSignat }; } -export type TFinalize = typeof finalize; -export function finalize(signature: string | null): types.FinalizeAction { +export type TFinalizeSignature = typeof finalizeSignature; +export function finalizeSignature(signature: string | null): types.FinalizeSignatureAction { return { - type: TypeKeys.PARITY_SIGNER_FINALIZE, + type: TypeKeys.PARITY_SIGNER_FINALIZE_SIGNATURE, payload: signature }; } diff --git a/common/actions/paritySigner/actionTypes.ts b/common/actions/paritySigner/actionTypes.ts index 4bbf1276242..6863b519aca 100644 --- a/common/actions/paritySigner/actionTypes.ts +++ b/common/actions/paritySigner/actionTypes.ts @@ -8,10 +8,10 @@ export interface RequestSignatureAction { }; } -export interface FinalizeAction { - type: TypeKeys.PARITY_SIGNER_FINALIZE; +export interface FinalizeSignatureAction { + type: TypeKeys.PARITY_SIGNER_FINALIZE_SIGNATURE; payload: string | null; } /*** Union Type ***/ -export type ParitySignerAction = RequestSignatureAction | FinalizeAction; +export type ParitySignerAction = RequestSignatureAction | FinalizeSignatureAction; diff --git a/common/actions/paritySigner/constants.ts b/common/actions/paritySigner/constants.ts index a195dafe18a..79eb353e811 100644 --- a/common/actions/paritySigner/constants.ts +++ b/common/actions/paritySigner/constants.ts @@ -1,4 +1,4 @@ export enum TypeKeys { PARITY_SIGNER_REQUEST_SIGNATURE = 'PARITY_SIGNER_REQUEST_SIGNATURE', - PARITY_SIGNER_FINALIZE = 'PARITY_SIGNER_FINALIZE' + PARITY_SIGNER_FINALIZE_SIGNATURE = 'PARITY_SIGNER_FINALIZE_SIGNATURE' } diff --git a/common/containers/QrSignerModal/index.tsx b/common/containers/QrSignerModal/index.tsx index bb61c591f29..28727bb720f 100644 --- a/common/containers/QrSignerModal/index.tsx +++ b/common/containers/QrSignerModal/index.tsx @@ -4,7 +4,7 @@ import translate, { translateRaw } from 'translations'; import QrSigner from '@parity/qr-signer'; import { AppState } from 'reducers'; import Modal, { IButton } from 'components/ui/Modal'; -import { TFinalize, finalize } from 'actions/paritySigner'; +import { TFinalizeSignature, finalizeSignature } from 'actions/paritySigner'; import './index.scss'; interface State { @@ -13,17 +13,19 @@ interface State { interface PropsClosed { isOpen: false; - finalize: TFinalize; } interface PropsOpen { isOpen: true; rlp: string; from: string; - finalize: TFinalize; } -type Props = PropsClosed | PropsOpen; +interface ActionProps { + finalizeSignature: TFinalizeSignature; +} + +type Props = (PropsClosed | PropsOpen) & ActionProps; class QrSignerModal extends React.Component { constructor(props: Props) { @@ -77,7 +79,7 @@ class QrSignerModal extends React.Component { return; } - this.props.finalize(null); + this.props.finalizeSignature(null); this.setState({ scan: false }); }; @@ -86,12 +88,12 @@ class QrSignerModal extends React.Component { return; } - this.props.finalize(signature); + this.props.finalizeSignature(signature); this.setState({ scan: false }); }; } -function mapStateToProps(state: AppState) { +function mapStateToProps(state: AppState): PropsClosed | PropsOpen { const { requested } = state.paritySigner; if (!requested) { @@ -107,4 +109,4 @@ function mapStateToProps(state: AppState) { }; } -export default connect(mapStateToProps, { finalize })(QrSignerModal); +export default connect(mapStateToProps, { finalizeSignature })(QrSignerModal); diff --git a/common/reducers/paritySigner.ts b/common/reducers/paritySigner.ts index 1e86ae9d932..68f8143c5e2 100644 --- a/common/reducers/paritySigner.ts +++ b/common/reducers/paritySigner.ts @@ -1,5 +1,4 @@ -import { RequestSignatureAction } from 'actions/paritySigner/actionTypes'; -import { ParitySignerAction, TypeKeys } from 'actions/paritySigner'; +import { ParitySignerAction, RequestSignatureAction, TypeKeys } from 'actions/paritySigner'; export interface State { requested?: QrSignatureState | null; @@ -21,7 +20,7 @@ function requestSignature(state: State, action: RequestSignatureAction): State { }; } -function finalize(state: State): State { +function finalizeSignature(state: State): State { return { ...state, requested: null @@ -32,8 +31,8 @@ export function paritySigner(state: State = INITIAL_STATE, action: ParitySignerA switch (action.type) { case TypeKeys.PARITY_SIGNER_REQUEST_SIGNATURE: return requestSignature(state, action); - case TypeKeys.PARITY_SIGNER_FINALIZE: - return finalize(state); + case TypeKeys.PARITY_SIGNER_FINALIZE_SIGNATURE: + return finalizeSignature(state); default: return state; } diff --git a/common/sagas/transaction/signing/signing.ts b/common/sagas/transaction/signing/signing.ts index b6677ee4bb3..64c4f3636c7 100644 --- a/common/sagas/transaction/signing/signing.ts +++ b/common/sagas/transaction/signing/signing.ts @@ -14,7 +14,11 @@ import { import { computeIndexingHash } from 'libs/transaction'; import { serializedAndTransactionFieldsMatch } from 'selectors/transaction'; import { showNotification } from 'actions/notifications'; -import { requestSignature, TypeKeys as ParityKeys } from 'actions/paritySigner'; +import { + requestSignature, + FinalizeSignatureAction, + TypeKeys as ParityKeys +} from 'actions/paritySigner'; import { getWalletType, IWalletType } from 'selectors/wallet'; export function* signLocalTransactionHandler({ @@ -51,7 +55,9 @@ export function* signParitySignerTransactionHandler({ yield put(requestSignature(from, rlp)); - const { payload }: { payload: string } = yield take(ParityKeys.PARITY_SIGNER_FINALIZE); + const { payload }: FinalizeSignatureAction = yield take( + ParityKeys.PARITY_SIGNER_FINALIZE_SIGNATURE + ); const signedTransaction: Buffer = yield call(signTransactionWithSignature, tx, payload); const indexingHash: string = yield call(computeIndexingHash, signedTransaction); From 0753ce8bbc31d4c90444078bec4502b2d8c85f87 Mon Sep 17 00:00:00 2001 From: maciejhirsz Date: Fri, 6 Apr 2018 21:54:45 +0200 Subject: [PATCH 11/13] Webcam info + wiki link on unlock screen --- common/components/WalletDecrypt/WalletDecrypt.tsx | 3 ++- common/containers/ParitySignerDecrypt/index.scss | 2 +- common/containers/ParitySignerDecrypt/index.tsx | 3 +++ common/libs/wallet/non-deterministic/parity.ts | 2 ++ common/translations/lang/en.json | 2 ++ 5 files changed, 10 insertions(+), 2 deletions(-) diff --git a/common/components/WalletDecrypt/WalletDecrypt.tsx b/common/components/WalletDecrypt/WalletDecrypt.tsx index 151c5954281..989ba49e847 100644 --- a/common/components/WalletDecrypt/WalletDecrypt.tsx +++ b/common/components/WalletDecrypt/WalletDecrypt.tsx @@ -51,6 +51,7 @@ import MetamaskIcon from 'assets/images/wallets/metamask.svg'; import MistIcon from 'assets/images/wallets/mist.svg'; import TrezorIcon from 'assets/images/wallets/trezor.svg'; import ParitySignerIcon from 'assets/images/wallets/parity-signer.svg'; +import { wikiLink as paritySignerHelpLink } from 'libs/wallet/non-deterministic/parity'; import './WalletDecrypt.scss'; import { withRouter, RouteComponentProps } from 'react-router'; @@ -177,7 +178,7 @@ const WalletDecrypt = withRouter( component: ParitySignerDecrypt, initialParams: {}, unlock: this.props.setWallet, - helpLink: 'https://wiki.parity.io/Parity-Signer-Mobile-App-MyCrypto-tutorial.md' + helpLink: paritySignerHelpLink }, [InsecureWalletName.KEYSTORE_FILE]: { lid: 'X_KEYSTORE2', diff --git a/common/containers/ParitySignerDecrypt/index.scss b/common/containers/ParitySignerDecrypt/index.scss index f1da8d098fe..df3b7d3ac39 100644 --- a/common/containers/ParitySignerDecrypt/index.scss +++ b/common/containers/ParitySignerDecrypt/index.scss @@ -9,6 +9,6 @@ width: 300px; height: 300px; display: inline-block; - margin-bottom: 1.5em; + margin: 1em 0 1.5em 0; } } diff --git a/common/containers/ParitySignerDecrypt/index.tsx b/common/containers/ParitySignerDecrypt/index.tsx index f62fa0b5ae5..de20843f428 100644 --- a/common/containers/ParitySignerDecrypt/index.tsx +++ b/common/containers/ParitySignerDecrypt/index.tsx @@ -6,6 +6,7 @@ import QrSigner from '@parity/qr-signer'; import { isValidETHAddress } from 'libs/validators'; import { ParitySignerWallet } from 'libs/wallet'; import { showNotification, TShowNotification } from 'actions/notifications'; +import { wikiLink } from 'libs/wallet/non-deterministic/parity'; import './index.scss'; import AppStoreBadge from 'assets/images/mobile/app-store-badge.png'; import GooglePlayBadge from 'assets/images/mobile/google-play-badge.png'; @@ -19,6 +20,8 @@ class ParitySignerDecrypt extends PureComponent { public render() { return (
+

{translate('ADD_PARITY_4')}

+

{translate('ADD_PARITY_5', { $wiki_link: wikiLink })}

Date: Fri, 6 Apr 2018 16:46:03 -0400 Subject: [PATCH 12/13] Make the Parity QR Signer its own component, that has error messaging and ismore robust about adding / removing cameras. --- common/components/ParityQrSigner.scss | 30 +++++ common/components/ParityQrSigner.tsx | 110 ++++++++++++++++++ .../WalletDecrypt/WalletDecrypt.tsx | 2 +- .../components/ParitySigner.scss | 7 ++ .../components/ParitySigner.tsx} | 19 ++- .../WalletDecrypt/components/index.tsx | 1 + common/components/index.ts | 1 + .../containers/ParitySignerDecrypt/index.scss | 14 --- common/containers/QrSignerModal/index.scss | 8 -- common/containers/QrSignerModal/index.tsx | 9 +- common/translations/lang/en.json | 3 + 11 files changed, 166 insertions(+), 38 deletions(-) create mode 100644 common/components/ParityQrSigner.scss create mode 100644 common/components/ParityQrSigner.tsx create mode 100644 common/components/WalletDecrypt/components/ParitySigner.scss rename common/{containers/ParitySignerDecrypt/index.tsx => components/WalletDecrypt/components/ParitySigner.tsx} (76%) delete mode 100644 common/containers/ParitySignerDecrypt/index.scss delete mode 100644 common/containers/QrSignerModal/index.scss diff --git a/common/components/ParityQrSigner.scss b/common/components/ParityQrSigner.scss new file mode 100644 index 00000000000..221cd13bf69 --- /dev/null +++ b/common/components/ParityQrSigner.scss @@ -0,0 +1,30 @@ +@import 'common/sass/variables'; + +.ParityQrSigner { + display: flex; + align-items: center; + justify-content: center; + width: 300px; + height: 300px; + text-align: center; + margin: 0 auto 1.5em; + border-radius: 4px; + overflow: hidden; + + &.is-disabled { + background: $gray-lighter; + } + + &-error { + padding: $space; + font-size: $font-size-small; + color: $gray-light; + + &-icon { + display: block; + font-size: 60px; + margin-bottom: $space-md; + opacity: 0.8; + } + } +} diff --git a/common/components/ParityQrSigner.tsx b/common/components/ParityQrSigner.tsx new file mode 100644 index 00000000000..f53f58c2069 --- /dev/null +++ b/common/components/ParityQrSigner.tsx @@ -0,0 +1,110 @@ +import React from 'react'; +import classnames from 'classnames'; +import translate from 'translations'; +import { Spinner } from 'components/ui'; +import QrSigner from '@parity/qr-signer'; +import './ParityQrSigner.scss'; + +interface State { + webcamError: null | React.ReactElement; + isLoading: boolean; +} + +interface ScanProps { + scan: true; + onScan(data: string): void; +} + +interface ShowProps { + scan: false; + account: string; + rlp: string; +} + +interface SharedProps { + size?: number; +} + +type Props = (ScanProps | ShowProps) & SharedProps; + +export default class ParityQrSigner extends React.PureComponent { + public state: State = { + webcamError: null, + isLoading: true + }; + + public componentDidMount() { + this.checkForWebcam(); + if (navigator.mediaDevices) { + navigator.mediaDevices.addEventListener('devicechange', this.checkForWebcam); + } + } + + public componentWillUnmount() { + if (navigator.mediaDevices && navigator.mediaDevices.ondevicechange) { + navigator.mediaDevices.removeEventListener('devicechange', this.checkForWebcam); + } + } + + public checkForWebcam = async () => { + if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { + try { + await navigator.mediaDevices.getUserMedia({ video: true }); + this.setState({ + webcamError: null, + isLoading: false + }); + } catch (e) { + const err = e as DOMException; + let errorMessage; + switch (err.name) { + case 'NotAllowedError': + case 'SecurityError': + errorMessage = translate('ADD_PARITY_ERROR_DISABLED'); + break; + case 'NotFoundError': + case 'OverconstrainedError': + errorMessage = translate('ADD_PARITY_ERROR_NO_CAM'); + break; + default: + errorMessage = translate('ADD_PARITY_ERROR_UNKNOWN'); + } + this.setState({ + webcamError: errorMessage, + isLoading: false + }); + } + } + }; + + public render() { + const { webcamError, isLoading } = this.state; + const size = this.props.size || 300; + + return ( +
+ {isLoading ? ( +
+ +
+ ) : webcamError ? ( +
+ + {webcamError} +
+ ) : ( + + )} +
+ ); + } +} diff --git a/common/components/WalletDecrypt/WalletDecrypt.tsx b/common/components/WalletDecrypt/WalletDecrypt.tsx index 151c5954281..7c2475d3604 100644 --- a/common/components/WalletDecrypt/WalletDecrypt.tsx +++ b/common/components/WalletDecrypt/WalletDecrypt.tsx @@ -29,9 +29,9 @@ import { ViewOnlyDecrypt, Web3Decrypt, WalletButton, + ParitySignerDecrypt, InsecureWalletWarning } from './components'; -import ParitySignerDecrypt from 'containers/ParitySignerDecrypt'; import { AppState } from 'reducers'; import { showNotification, TShowNotification } from 'actions/notifications'; import { getDisabledWallets } from 'selectors/wallet'; diff --git a/common/components/WalletDecrypt/components/ParitySigner.scss b/common/components/WalletDecrypt/components/ParitySigner.scss new file mode 100644 index 00000000000..4e667f03c24 --- /dev/null +++ b/common/components/WalletDecrypt/components/ParitySigner.scss @@ -0,0 +1,7 @@ +.ParitySignerUnlock { + &-badge { + display: inline-block; + height: 3em; + margin: 0 0.4em; + } +} diff --git a/common/containers/ParitySignerDecrypt/index.tsx b/common/components/WalletDecrypt/components/ParitySigner.tsx similarity index 76% rename from common/containers/ParitySignerDecrypt/index.tsx rename to common/components/WalletDecrypt/components/ParitySigner.tsx index f62fa0b5ae5..28646f39868 100644 --- a/common/containers/ParitySignerDecrypt/index.tsx +++ b/common/components/WalletDecrypt/components/ParitySigner.tsx @@ -1,32 +1,25 @@ import React, { PureComponent } from 'react'; import { connect } from 'react-redux'; import translate from 'translations'; +import { ParityQrSigner } from 'components'; import { NewTabLink } from 'components/ui'; -import QrSigner from '@parity/qr-signer'; import { isValidETHAddress } from 'libs/validators'; import { ParitySignerWallet } from 'libs/wallet'; import { showNotification, TShowNotification } from 'actions/notifications'; -import './index.scss'; import AppStoreBadge from 'assets/images/mobile/app-store-badge.png'; import GooglePlayBadge from 'assets/images/mobile/google-play-badge.png'; +import './ParitySigner.scss'; interface Props { showNotification: TShowNotification; onUnlock(param: any): void; } -class ParitySignerDecrypt extends PureComponent { +class ParitySignerDecryptClass extends PureComponent { public render() { return (
-
- -
+

{translate('ADD_PARITY_2')}

@@ -50,4 +43,6 @@ class ParitySignerDecrypt extends PureComponent { }; } -export default connect(() => ({}), { showNotification })(ParitySignerDecrypt); +export const ParitySignerDecrypt = connect(() => ({}), { showNotification })( + ParitySignerDecryptClass +); diff --git a/common/components/WalletDecrypt/components/index.tsx b/common/components/WalletDecrypt/components/index.tsx index 7e68311140c..c2aed00d526 100644 --- a/common/components/WalletDecrypt/components/index.tsx +++ b/common/components/WalletDecrypt/components/index.tsx @@ -4,6 +4,7 @@ export * from './InsecureWalletWarning'; export * from './Keystore'; export * from './LedgerNano'; export * from './Mnemonic'; +export * from './ParitySigner'; export * from './PrivateKey'; export * from './Trezor'; export * from './ViewOnly'; diff --git a/common/components/index.ts b/common/components/index.ts index 421927e5af3..acdaaacf8b2 100644 --- a/common/components/index.ts +++ b/common/components/index.ts @@ -19,3 +19,4 @@ export { default as WalletDecrypt } from './WalletDecrypt'; export { default as TogglablePassword } from './TogglablePassword'; export { default as GenerateKeystoreModal } from './GenerateKeystoreModal'; export { default as TransactionStatus } from './TransactionStatus'; +export { default as ParityQrSigner } from './ParityQrSigner'; diff --git a/common/containers/ParitySignerDecrypt/index.scss b/common/containers/ParitySignerDecrypt/index.scss deleted file mode 100644 index f1da8d098fe..00000000000 --- a/common/containers/ParitySignerDecrypt/index.scss +++ /dev/null @@ -1,14 +0,0 @@ -.ParitySignerUnlock { - &-badge { - display: inline-block; - height: 3em; - margin: 0 0.4em; - } - - &-qr-bounds { - width: 300px; - height: 300px; - display: inline-block; - margin-bottom: 1.5em; - } -} diff --git a/common/containers/QrSignerModal/index.scss b/common/containers/QrSignerModal/index.scss deleted file mode 100644 index 0f1b1497fc8..00000000000 --- a/common/containers/QrSignerModal/index.scss +++ /dev/null @@ -1,8 +0,0 @@ -.QrSignerModal { - &-qr-bounds { - width: 300px; - height: 300px; - display: inline-block; - margin: 0.3em 0; - } -} diff --git a/common/containers/QrSignerModal/index.tsx b/common/containers/QrSignerModal/index.tsx index 28727bb720f..43361f95f40 100644 --- a/common/containers/QrSignerModal/index.tsx +++ b/common/containers/QrSignerModal/index.tsx @@ -1,11 +1,10 @@ import React from 'react'; import { connect } from 'react-redux'; import translate, { translateRaw } from 'translations'; -import QrSigner from '@parity/qr-signer'; import { AppState } from 'reducers'; +import { ParityQrSigner } from 'components'; import Modal, { IButton } from 'components/ui/Modal'; import { TFinalizeSignature, finalizeSignature } from 'actions/paritySigner'; -import './index.scss'; interface State { scan: boolean; @@ -67,7 +66,11 @@ class QrSignerModal extends React.Component { handleClose={this.onClose} >

- + {scan ? ( + + ) : ( + + )}
diff --git a/common/translations/lang/en.json b/common/translations/lang/en.json index 28d7b56c4fa..4f8e0b7446f 100644 --- a/common/translations/lang/en.json +++ b/common/translations/lang/en.json @@ -64,6 +64,9 @@ "ADD_PARITY_1": "Transaction canceled ", "ADD_PARITY_2": "Download the app ", "ADD_PARITY_3": "Scan", + "ADD_PARITY_ERROR_DISABLED": "You must enable your webcam to unlock your Parity Signer wallet", + "ADD_PARITY_ERROR_NO_CAM": "You need a webcam to unlock your Parity Signer wallet", + "ADD_PARITY_ERROR_UNKNOWN": "Unable to access your webcam, please refresh or try another browser", "ADD_RADIO_2": "Select Your Wallet File (Keystore / JSON) ", "ADD_RADIO_2_ALT": "Select Your Wallet File ", "ADD_RADIO_2_SHORT": "SELECT WALLET FILE", From 05419a63fda0526d766f83916cb994be225a2f1e Mon Sep 17 00:00:00 2001 From: Will O'Beirne Date: Fri, 6 Apr 2018 16:55:24 -0400 Subject: [PATCH 13/13] Unneded l10n --- common/components/WalletDecrypt/components/ParitySigner.tsx | 2 +- common/translations/lang/en.json | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/common/components/WalletDecrypt/components/ParitySigner.tsx b/common/components/WalletDecrypt/components/ParitySigner.tsx index 3cb582680fe..3d97ee25a12 100644 --- a/common/components/WalletDecrypt/components/ParitySigner.tsx +++ b/common/components/WalletDecrypt/components/ParitySigner.tsx @@ -21,7 +21,7 @@ class ParitySignerDecryptClass extends PureComponent { return (
-

{translate('ADD_PARITY_5', { $wiki_link: wikiLink })}

+

{translate('ADD_PARITY_4', { $wiki_link: wikiLink })}

{translate('ADD_PARITY_2')}

diff --git a/common/translations/lang/en.json b/common/translations/lang/en.json index 6b6a66fa291..ed8da020558 100644 --- a/common/translations/lang/en.json +++ b/common/translations/lang/en.json @@ -64,8 +64,7 @@ "ADD_PARITY_1": "Transaction canceled ", "ADD_PARITY_2": "Download the app ", "ADD_PARITY_3": "Scan", - "ADD_PARITY_4": "Please enable your Webcam", - "ADD_PARITY_5": "For more information, visit [Parity Wiki]($wiki_link).", + "ADD_PARITY_4": "For more information, visit [Parity Wiki]($wiki_link).", "ADD_PARITY_ERROR_DISABLED": "You must enable your webcam to unlock your Parity Signer wallet", "ADD_PARITY_ERROR_NO_CAM": "You need a webcam to unlock your Parity Signer wallet", "ADD_PARITY_ERROR_UNKNOWN": "Unable to access your webcam, please refresh or try another browser",