diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d36980f1..0fc29c48 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,6 +3,10 @@ image: node:10.15.1 variables: CYPRESS_CACHE_FOLDER: "$CI_PROJECT_DIR/cache/Cypress" +before_script: + - "apt-get update" + - "apt-get install -y build-essential" + cache: key: ${CI_COMMIT_REF_SLUG} paths: diff --git a/CHANGELOG.md b/CHANGELOG.md index d3ed0cac..77c7312a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ __changed__ - static (without animation) active tab indicator - input validation - mandatory leading 0 for float numbers - regexps refactored to functions, added unit tests +- limit passphrase length to 50 bytes __removed__ - Text "already used" from token select in case of already added tokens diff --git a/package.json b/package.json index 37ae1f73..3c632632 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ }, "dependencies": { "@babel/polyfill": "^7.2.5", + "@hot-loader/react-dom": "16.8.6", "bignumber.js": "8.0.2", "color-hash": "^1.0.3", "commander": "^2.19.0", @@ -81,7 +82,7 @@ "styled-normalize": "^8.0.6", "trezor-bridge-communicator": "1.0.2", "trezor-connect": "7.0.2", - "trezor-ui-components": "^1.0.0-beta.14", + "trezor-ui-components": "^1.0.0-beta.18", "wallet-address-validator": "^0.2.4", "web3": "1.0.0-beta.35", "webpack": "^4.29.3", diff --git a/src/actions/DiscoveryActions.js b/src/actions/DiscoveryActions.js index 9347ea84..c3734327 100644 --- a/src/actions/DiscoveryActions.js +++ b/src/actions/DiscoveryActions.js @@ -178,7 +178,7 @@ const begin = (device: TrezorDevice, networkName: string): AsyncAction => async dispatch({ type: NOTIFICATION.ADD, payload: { - type: 'error', + variant: 'error', title: , message: error.message, cancelable: true, @@ -266,7 +266,7 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy dispatch({ type: NOTIFICATION.ADD, payload: { - type: 'error', + variant: 'error', title: , message: error.message, cancelable: true, diff --git a/src/actions/ImportAccountActions.js b/src/actions/ImportAccountActions.js index 5e673f60..91a16277 100644 --- a/src/actions/ImportAccountActions.js +++ b/src/actions/ImportAccountActions.js @@ -80,7 +80,7 @@ export const importAddress = ( dispatch({ type: NOTIFICATION.ADD, payload: { - type: 'success', + variant: 'success', title: 'The account has been successfully imported', cancelable: true, }, @@ -132,7 +132,7 @@ export const importAddress = ( dispatch({ type: NOTIFICATION.ADD, payload: { - type: 'success', + variant: 'success', title: 'The account has been successfully imported', cancelable: true, }, @@ -147,7 +147,7 @@ export const importAddress = ( dispatch({ type: NOTIFICATION.ADD, payload: { - type: 'error', + variant: 'error', title: 'Import account error', message: error.message, cancelable: true, diff --git a/src/actions/NotificationActions.js b/src/actions/NotificationActions.js index 9f847a33..9d9af9c2 100644 --- a/src/actions/NotificationActions.js +++ b/src/actions/NotificationActions.js @@ -16,7 +16,7 @@ export type NotificationAction = | { type: typeof NOTIFICATION.ADD, payload: { - +type: string, + +variant: string, +title: React.Node | MessageDescriptor | string, +message?: ?(React.Node | string), +cancelable: boolean, diff --git a/src/actions/ReceiveActions.js b/src/actions/ReceiveActions.js index 2aee2821..7516d195 100644 --- a/src/actions/ReceiveActions.js +++ b/src/actions/ReceiveActions.js @@ -114,7 +114,7 @@ export const showAddress = (path: Array): AsyncAction => async ( dispatch({ type: NOTIFICATION.ADD, payload: { - type: 'error', + variant: 'error', title: , message: response.payload.error, cancelable: true, diff --git a/src/actions/SelectedAccountActions.js b/src/actions/SelectedAccountActions.js index 75ef722d..a0459b9f 100644 --- a/src/actions/SelectedAccountActions.js +++ b/src/actions/SelectedAccountActions.js @@ -139,6 +139,7 @@ const getAccountNotification = ( if (blockchain && !blockchain.connected) { return { type: 'backend', + variant: 'error', title: `${network.name} backend is not connected`, shouldRender: false, }; @@ -148,6 +149,7 @@ const getAccountNotification = ( if (account && discovery && !discovery.completed && !discovery.waitingForDevice) { return { type: 'info', + variant: 'info', title: 'Loading other accounts...', shouldRender: true, }; @@ -157,6 +159,7 @@ const getAccountNotification = ( if (!device.connected) { return { type: 'info', + variant: 'info', title: `Device ${device.instanceLabel} is disconnected`, shouldRender: true, }; @@ -167,6 +170,7 @@ const getAccountNotification = ( if (!device.available) { return { type: 'info', + variant: 'info', title: `Device ${device.instanceLabel} is unavailable`, message: 'Change passphrase settings to use this device', shouldRender: true, diff --git a/src/actions/SignVerifyActions.js b/src/actions/SignVerifyActions.js index 30878e30..a0722196 100644 --- a/src/actions/SignVerifyActions.js +++ b/src/actions/SignVerifyActions.js @@ -67,7 +67,7 @@ const sign = (path: Array, message: string, hex: boolean = false): Async dispatch({ type: NOTIFICATION.ADD, payload: { - type: 'error', + variant: 'error', title: , message: response.payload.error, cancelable: true, @@ -112,7 +112,7 @@ const verify = ( dispatch({ type: NOTIFICATION.ADD, payload: { - type: 'success', + variant: 'success', title: , message: , cancelable: true, @@ -122,7 +122,7 @@ const verify = ( dispatch({ type: NOTIFICATION.ADD, payload: { - type: 'error', + variant: 'error', title: , message: response.payload.error, cancelable: true, diff --git a/src/actions/TrezorConnectActions.js b/src/actions/TrezorConnectActions.js index 87c3d4be..e60931ab 100644 --- a/src/actions/TrezorConnectActions.js +++ b/src/actions/TrezorConnectActions.js @@ -268,7 +268,7 @@ export const authorizeDevice = (): AsyncAction => async ( type: NOTIFICATION.ADD, payload: { devicePath: selected.path, - type: 'error', + variant: 'error', title: , message: response.payload.error, cancelable: false, @@ -355,6 +355,7 @@ export function acquire(): AsyncAction { type: NOTIFICATION.ADD, payload: { type: 'error', + variant: 'error', title: , message: response.payload.error, cancelable: true, diff --git a/src/actions/ethereum/SendFormActions.js b/src/actions/ethereum/SendFormActions.js index 4fec6257..92877a12 100644 --- a/src/actions/ethereum/SendFormActions.js +++ b/src/actions/ethereum/SendFormActions.js @@ -717,7 +717,7 @@ export const onSend = (): AsyncAction => async ( dispatch({ type: NOTIFICATION.ADD, payload: { - type: 'error', + variant: 'error', title: , message: signedTransaction.payload.error, cancelable: true, @@ -807,7 +807,7 @@ export const onSend = (): AsyncAction => async ( dispatch({ type: NOTIFICATION.ADD, payload: { - type: 'success', + variant: 'success', title: , message: ( @@ -822,7 +822,7 @@ export const onSend = (): AsyncAction => async ( dispatch({ type: NOTIFICATION.ADD, payload: { - type: 'error', + variant: 'error', title: , message: error.message || error, cancelable: true, diff --git a/src/actions/ripple/SendFormActions.js b/src/actions/ripple/SendFormActions.js index ae237a5a..fce5ef8b 100644 --- a/src/actions/ripple/SendFormActions.js +++ b/src/actions/ripple/SendFormActions.js @@ -457,7 +457,7 @@ export const onSend = (): AsyncAction => async ( dispatch({ type: NOTIFICATION.ADD, payload: { - type: 'error', + variant: 'error', title: , message: signedTransaction.payload.error, cancelable: true, @@ -476,7 +476,7 @@ export const onSend = (): AsyncAction => async ( dispatch({ type: NOTIFICATION.ADD, payload: { - type: 'error', + variant: 'error', title: , message: push.payload.error, cancelable: true, @@ -499,7 +499,7 @@ export const onSend = (): AsyncAction => async ( dispatch({ type: NOTIFICATION.ADD, payload: { - type: 'success', + variant: 'success', title: , message: txid, cancelable: true, diff --git a/src/components/Header/index.js b/src/components/Header/index.js index 24a7fc8b..622bc178 100644 --- a/src/components/Header/index.js +++ b/src/components/Header/index.js @@ -1,237 +1,49 @@ /* eslint-disable jsx-a11y/accessible-emoji */ /* @flow */ -import React from 'react'; -import styled from 'styled-components'; -import { NavLink } from 'react-router-dom'; -import { SCREEN_SIZE } from 'config/variables'; -import { Icon, icons, colors } from 'trezor-ui-components'; +import * as React from 'react'; +import { Header } from 'trezor-ui-components'; import { FormattedMessage } from 'react-intl'; +import { Link } from 'react-router-dom'; import type { toggleSidebar as toggleSidebarType } from 'actions/WalletActions'; import l10nMessages from './index.messages'; import LanguagePicker from './components/LanguagePicker/Container'; -const Wrapper = styled.header` - width: 100%; - height: 52px; - background: ${colors.HEADER}; - z-index: 200; -`; - -const LayoutWrapper = styled.div` - width: 100%; - height: 100%; - max-width: 1170px; - margin: 0 auto; - display: flex; - align-items: center; - justify-content: space-between; - - @media screen and (max-width: 1170px) { - padding: 0 25px; - } -`; - -const Left = styled.div` - display: none; - flex: 0 0 33%; - - @media screen and (max-width: ${SCREEN_SIZE.SM}) { - display: initial; - } -`; - -const MenuToggler = styled.div` - display: none; - white-space: nowrap; - color: ${colors.WHITE}; - align-self: center; - align-items: center; - cursor: pointer; - user-select: none; - padding: 10px 0px; - transition: all 0.1s ease-in; - - @media screen and (max-width: ${SCREEN_SIZE.SM}) { - display: flex; - } -`; - -const TogglerText = styled.div` - margin-left: 6px; -`; - -const TREZOR = styled.div``; -const T = styled.div``; - -const Logo = styled.div` - flex: 1; - justify-content: flex-start; - display: flex; - - ${T} { - display: none; - width: 20px; - } - - ${TREZOR} { - width: 100px; - } - - svg { - fill: ${colors.WHITE}; - height: 28px; - } - - @media screen and (max-width: ${SCREEN_SIZE.SM}) { - flex: 1 0 33%; - justify-content: center; - } - - @media screen and (max-width: ${SCREEN_SIZE.XS}) { - /* hides full width trezor logo, shows only trezor icon */ - ${TREZOR} { - display: none; - } - - ${T} { - display: inherit; - } - } -`; - -const MenuLinks = styled.div` - display: flex; - align-content: center; - justify-content: flex-end; - flex: 0; - height: 100%; - - @media screen and (max-width: ${SCREEN_SIZE.SM}) { - flex: 0 1 33%; - } -`; - -const Projects = styled.div` - display: flex; - align-items: center; - height: 100%; - border-right: 1px solid ${colors.HEADER_DIVIDER}; - padding-right: 24px; - margin-right: 24px; - - @media screen and (max-width: ${SCREEN_SIZE.SM}) { - display: none; - } -`; - -const A = styled.a` - color: ${colors.WHITE}; - margin-left: 24px; - transition: all 0.1s ease-in; - white-space: nowrap; - - &:visited { - color: ${colors.WHITE}; - margin-left: 24px; - } - - &:first-child { - margin: 0px; - } - - &:hover, - &:active { - color: ${colors.TEXT_SECONDARY}; - } -`; - -type Props = { +type MyProps = { sidebarEnabled?: boolean, sidebarOpened?: ?boolean, toggleSidebar?: toggleSidebarType, }; -const Header = ({ sidebarEnabled, sidebarOpened, toggleSidebar }: Props) => ( - - - - {sidebarEnabled && ( - - {sidebarOpened ? ( - <> - - - - - - ) : ( - <> - - - - - - )} - - )} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const MyHeader = ({ sidebarEnabled, sidebarOpened, toggleSidebar }: MyProps) => ( +
} + togglerCloseText={} + links={[ + { + href: 'https://trezor.io/', + title: , + }, + { + href: 'https://wiki.trezor.io/', + title: , + }, + { + href: 'https://blog.trezor.io/', + title: , + }, + { + href: 'https://trezor.io/support/', + title: , + }, + ]} + logoLinkComponent={} + rightAddon={} + /> ); -export default Header; +export default MyHeader; diff --git a/src/components/Log/index.js b/src/components/Log/index.js index acd51053..7993bd6d 100644 --- a/src/components/Log/index.js +++ b/src/components/Log/index.js @@ -28,7 +28,7 @@ type Props = {| ...OwnProps, ...StateProps, ...DispatchProps |}; const Wrapper = styled.div` position: relative; color: ${colors.INFO_PRIMARY}; - background: ${colors.INFO_SECONDARY}; + background: ${colors.INFO_LIGHT}; padding: 24px; display: flex; flex-direction: column; diff --git a/src/components/modals/confirm/NoBackup/index.js b/src/components/modals/confirm/NoBackup/index.js index 2bd46830..37b1b840 100644 --- a/src/components/modals/confirm/NoBackup/index.js +++ b/src/components/modals/confirm/NoBackup/index.js @@ -36,20 +36,6 @@ const BackupButton = styled(Button)` margin-bottom: 10px; `; -const ProceedButton = styled(Button)` - background: transparent; - border-color: ${colors.WARNING_PRIMARY}; - color: ${colors.WARNING_PRIMARY}; - - &:focus, - &:hover, - &:active { - color: ${colors.WHITE}; - background: ${colors.WARNING_PRIMARY}; - box-shadow: none; - } -`; - const StyledP = styled(P)` /* boost-specificity hack to override P base styling */ && { @@ -80,9 +66,9 @@ const Confirmation = (props: Props) => ( - props.onReceiveConfirmation(true)}> + ); diff --git a/src/components/modals/confirm/UnverifiedAddress/index.js b/src/components/modals/confirm/UnverifiedAddress/index.js index 9f8c75eb..790a6406 100644 --- a/src/components/modals/confirm/UnverifiedAddress/index.js +++ b/src/components/modals/confirm/UnverifiedAddress/index.js @@ -64,20 +64,6 @@ const BackupButton = styled(Button)` width: 100%; `; -const WarnButton = styled(Button)` - background: transparent; - border-color: ${colors.WARNING_PRIMARY}; - color: ${colors.WARNING_PRIMARY}; - - &:focus, - &:hover, - &:active { - color: ${colors.WHITE}; - background: ${colors.WARNING_PRIMARY}; - box-shadow: none; - } -`; - class ConfirmUnverifiedAddress extends PureComponent { componentDidMount(): void { this.keyboardHandler = this.keyboardHandler.bind(this); @@ -161,9 +147,13 @@ class ConfirmUnverifiedAddress extends PureComponent { - this.showUnverifiedAddress()}> + {needsBackup && } diff --git a/src/components/modals/passphrase/Passphrase/index.js b/src/components/modals/passphrase/Passphrase/index.js index e0ef1989..4377ce47 100644 --- a/src/components/modals/passphrase/Passphrase/index.js +++ b/src/components/modals/passphrase/Passphrase/index.js @@ -2,6 +2,7 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; +import { byteLength } from 'utils/formatUtils'; import { FormattedMessage } from 'react-intl'; import { Button, Input, Checkbox, P, H5, colors } from 'trezor-ui-components'; import { FONT_SIZE } from 'config/variables'; @@ -24,6 +25,7 @@ type State = { passphraseCheckInputValue: string, doPassphraseInputsMatch: boolean, isPassphraseHidden: boolean, + byteLength: number, }; const Wrapper = styled.div` @@ -91,6 +93,7 @@ class Passphrase extends PureComponent { passphraseCheckInputValue: '', doPassphraseInputsMatch: true, isPassphraseHidden: true, + byteLength: 0, }; } @@ -117,6 +120,9 @@ class Passphrase extends PureComponent { let doPassphraseInputsMatch = false; if (inputName === 'passphraseInputValue') { + this.setState({ + byteLength: byteLength(inputValue), + }); // If passphrase is not hidden the second input should get filled automatically // and should be disabled if (this.state.isPassphraseHidden) { @@ -181,13 +187,28 @@ class Passphrase extends PureComponent { handleKeyPress(event: KeyboardEvent) { if (event.key === 'Enter') { event.preventDefault(); - if (this.state.doPassphraseInputsMatch) { + if (this.state.doPassphraseInputsMatch && this.state.byteLength <= 50) { this.submitPassphrase(); } } } render() { + let error = null; + if (this.state.byteLength > 50) { + error = ( + + + + ); + } else if (!this.state.doPassphraseInputsMatch) { + error = ( + + + + ); + } + return (
@@ -236,11 +257,7 @@ class Passphrase extends PureComponent { /> )} - {!this.state.doPassphraseInputsMatch && ( - - - - )} + {error} { - diff --git a/src/components/modals/passphrase/Passphrase/index.messages.js b/src/components/modals/passphrase/Passphrase/index.messages.js index 4f6e14f0..4b6c1c49 100644 --- a/src/components/modals/passphrase/Passphrase/index.messages.js +++ b/src/components/modals/passphrase/Passphrase/index.messages.js @@ -24,6 +24,10 @@ const definedMessages: Messages = defineMessages({ id: 'TR_PASSPHRASES_DO_NOT_MATCH', defaultMessage: 'Passphrases do not match!', }, + TR_PASSPHRASE_IS_TOO_LONG: { + id: 'TR_PASSPHRASE_IS_TOO_LONG', + defaultMessage: 'Passphrase is too long!', + }, TR_SHOW_PASSPHRASE: { id: 'TR_SHOW_PASSPHRASE', defaultMessage: 'Show passphrase', diff --git a/src/components/modals/pin/Pin/index.js b/src/components/modals/pin/Pin/index.js index d87e7fb9..4a6e4878 100644 --- a/src/components/modals/pin/Pin/index.js +++ b/src/components/modals/pin/Pin/index.js @@ -23,12 +23,12 @@ type State = { const Wrapper = styled.div` padding: 30px 48px; + max-width: 260px; `; const InputWrapper = styled.div` margin-top: 24px; margin-bottom: 10px; - max-width: 260px; `; const PinRow = styled.div` display: flex; diff --git a/src/components/notifications/App/components/NoBackup/index.js b/src/components/notifications/App/components/NoBackup/index.js index bbe7ffb1..6d6846d3 100644 --- a/src/components/notifications/App/components/NoBackup/index.js +++ b/src/components/notifications/App/components/NoBackup/index.js @@ -13,7 +13,7 @@ export default (props: Props) => { return ( } message={} actions={[ diff --git a/src/components/notifications/App/components/OnlineStatus/index.js b/src/components/notifications/App/components/OnlineStatus/index.js index 529c40a1..5002ee67 100644 --- a/src/components/notifications/App/components/OnlineStatus/index.js +++ b/src/components/notifications/App/components/OnlineStatus/index.js @@ -11,7 +11,7 @@ export default (props: Props) => { return ( diff --git a/src/components/notifications/App/components/UpdateBridge/index.js b/src/components/notifications/App/components/UpdateBridge/index.js index d5bb1131..1e451bd1 100644 --- a/src/components/notifications/App/components/UpdateBridge/index.js +++ b/src/components/notifications/App/components/UpdateBridge/index.js @@ -11,7 +11,7 @@ export default (props: Props) => { return ( { return ( { // TODO: this is a different component with "auto resolve" button return ( { } return ( diff --git a/src/components/notifications/Context/components/Action/components/NotificationsGroups/components/Group/index.js b/src/components/notifications/Context/components/Action/components/NotificationsGroups/components/Group/index.js index 2e9fffae..74232118 100644 --- a/src/components/notifications/Context/components/Action/components/NotificationsGroups/components/Group/index.js +++ b/src/components/notifications/Context/components/Action/components/NotificationsGroups/components/Group/index.js @@ -77,17 +77,17 @@ class Group extends PureComponent { }; render() { - const { type, groupNotifications, close } = this.props; - const color = getPrimaryColor(type); + const { variant, groupNotifications, close } = this.props; + const color = getPrimaryColor(variant); return ( {groupNotifications.length > 1 && (
- + {groupNotifications.length}{' '} - {groupNotifications.length > 1 ? `${type}s` : type} + {groupNotifications.length > 1 ? `${variant}s` : variant} @@ -105,7 +105,7 @@ class Group extends PureComponent { {groupNotifications.slice(0, this.state.visibleCount).map(notification => ( notifications.reduce((acc, obj) => { - const key = obj.type; + const key = obj.variant; if (!acc[key]) { acc[key] = []; } @@ -20,6 +20,7 @@ class NotificationsGroup extends PureComponent { }, {}); sortByPriority(notifications) { + //TODO return notifications; } @@ -29,51 +30,53 @@ class NotificationsGroup extends PureComponent { // { // key: 1, // title: 'this is a title of error notification', - // type: 'error', + // variant: 'error', // message: 'this is a message of error notification', // }, // { // key: 2, // title: 'this is a title of warning notification', - // type: 'warning', + // variant: 'warning', // message: 'this is a message of warning notification', // }, // { // key: 3, // title: 'this is a title of warning notification', - // type: 'warning', + // variant: 'warning', // message: 'this is a message of warning notification', // }, // { // key: 4, // title: 'this is a title of warning notification sds d', - // type: 'warning', + // variant: 'warning', // message: 'this is a message of warning notification', // }, // { // key: 5, // title: 'this is a title of warning notification as', - // type: 'success', + // variant: 'success', // }, // { // key: 6, // title: 'this is a title of warning notification as', - // type: 'info', + // variant: 'info', // message: 'this is a message of warning notification', // }, // { // key: 7, // title: 'this is a title of info notification s ', - // type: 'info', + // variant: 'info', // message: 'this is a message of info notification', - // actions: - // [{ + // actions: [ + // { // label: 'Update', // callback: 'props.routerActions.gotoBridgeUpdate', - // }], + // }, + // ], // }, // ]; const notificationGroups = this.groupNotifications(notifications); + console.log(notificationGroups); const sortedNotifications = this.sortByPriority(notificationGroups); return ( @@ -82,7 +85,7 @@ class NotificationsGroup extends PureComponent { ))} diff --git a/src/components/notifications/Context/components/Static/index.js b/src/components/notifications/Context/components/Static/index.js index d14c5aa0..fa30ff68 100644 --- a/src/components/notifications/Context/components/Static/index.js +++ b/src/components/notifications/Context/components/Static/index.js @@ -30,7 +30,7 @@ export default withRouter((props: {| ...Props, ...ContextRouter |}) => { notifications.push( } @@ -61,7 +61,7 @@ export default withRouter((props: {| ...Props, ...ContextRouter |}) => { notifications.push( @@ -72,7 +72,7 @@ export default withRouter((props: {| ...Props, ...ContextRouter |}) => { notifications.push( diff --git a/src/reducers/NotificationReducer.js b/src/reducers/NotificationReducer.js index a13cd240..4e387698 100644 --- a/src/reducers/NotificationReducer.js +++ b/src/reducers/NotificationReducer.js @@ -16,7 +16,7 @@ export type NotificationEntry = { +key: string, // React.Key +id: ?string, +devicePath: ?string, - +type: string, + +variant: string, +title: React.Node | string, +message: ?(React.Node | string), +cancelable: boolean, @@ -42,7 +42,7 @@ const addNotification = (state: State, payload: any): State => { key: new Date().getTime().toString(), id: payload.id, devicePath: payload.devicePath, - type: payload.type, + variant: payload.variant, title: payload.title, message: payload.message, cancelable: payload.cancelable, diff --git a/src/reducers/SelectedAccountReducer.js b/src/reducers/SelectedAccountReducer.js index 4ae2fa08..ee58ab0e 100644 --- a/src/reducers/SelectedAccountReducer.js +++ b/src/reducers/SelectedAccountReducer.js @@ -10,7 +10,7 @@ export type Loader = { }; export type Notification = { - type: string, + variant: string, title: string, message?: string, }; diff --git a/src/store.js b/src/store.js index e6895fa7..aceb7240 100644 --- a/src/store.js +++ b/src/store.js @@ -20,8 +20,8 @@ const enhancers = []; const middlewares = [thunk, routerMiddleware(history)]; -// sentry io middleware only in dev and beta build -if (buildUtils.isDev() || buildUtils.isBeta()) { +// sentry io middleware only in beta build +if (buildUtils.isBeta()) { const RAVEN_KEY = 'https://34b8c09deb6c4cd2a4dc3f0029cd02d8@sentry.io/1279550'; const ravenMiddleware = RavenMiddleware(RAVEN_KEY); Raven.config(RAVEN_KEY).install(); diff --git a/src/utils/__tests__/formatUtils.test.js b/src/utils/__tests__/formatUtils.test.js index 4d57d0d1..67defb4a 100644 --- a/src/utils/__tests__/formatUtils.test.js +++ b/src/utils/__tests__/formatUtils.test.js @@ -21,4 +21,14 @@ describe('format utils', () => { expect(utils.fromDecimalAmount('a', 'a')).toBe('0'); expect(utils.fromDecimalAmount('a', '1')).toBe('0'); }); + + describe('byteLength', () => { + it('should return correct byte length for strings with special ASCII characters', () => { + expect(utils.byteLength('testString')).toEqual(10); + expect(utils.byteLength('~!@#$%^&*()_+{}|:?><')).toEqual(20); + expect(utils.byteLength('😀')).toEqual(4); + expect(utils.byteLength('ä')).toEqual(2); + expect(utils.byteLength('áľščť')).toEqual(10); + }); + }); }); diff --git a/src/utils/__tests__/notification.test.js b/src/utils/__tests__/notification.test.js index c2ef912d..176c6a80 100644 --- a/src/utils/__tests__/notification.test.js +++ b/src/utils/__tests__/notification.test.js @@ -11,13 +11,13 @@ describe('notification utils', () => { expect(utils.getPrimaryColor('')).toBe(null); }); - it('get secondary color from status', () => { - expect(utils.getSecondaryColor('info')).toBe(colors.INFO_SECONDARY); - expect(utils.getSecondaryColor('warning')).toBe(colors.WARNING_SECONDARY); - expect(utils.getSecondaryColor('error')).toBe(colors.ERROR_SECONDARY); - expect(utils.getSecondaryColor('success')).toBe(colors.SUCCESS_SECONDARY); - expect(utils.getSecondaryColor('kdsjflds')).toBe(null); - expect(utils.getSecondaryColor('')).toBe(null); + it('get notification bg color from status', () => { + expect(utils.getNotificationBgColor('info')).toBe(colors.INFO_LIGHT); + expect(utils.getNotificationBgColor('warning')).toBe(colors.WARNING_LIGHT); + expect(utils.getNotificationBgColor('error')).toBe(colors.ERROR_LIGHT); + expect(utils.getNotificationBgColor('success')).toBe(colors.SUCCESS_LIGHT); + expect(utils.getNotificationBgColor('kdsjflds')).toBe(null); + expect(utils.getNotificationBgColor('')).toBe(null); }); it('get icon according to status', () => { diff --git a/src/utils/formatUtils.js b/src/utils/formatUtils.js index 4cea3e00..392ef109 100644 --- a/src/utils/formatUtils.js +++ b/src/utils/formatUtils.js @@ -27,3 +27,14 @@ export const fromDecimalAmount = (amount: string | number, decimals: number): st return '0'; } }; + +export const byteLength = (text: string): number => { + // returns length of the text in bytes, 0 in case of error. + try { + // regexp is handling cases when encodeURI returns '%uXXXX' or %XX%XX + return encodeURI(text).split(/%(?:u[0-9A-F]{2})?[0-9A-F]{2}|./).length - 1; + } catch (error) { + console.error(error); + return 0; + } +}; diff --git a/src/utils/notification.js b/src/utils/notification.js index 884a6dd8..2e9c62e8 100644 --- a/src/utils/notification.js +++ b/src/utils/notification.js @@ -22,20 +22,20 @@ const getPrimaryColor = type => { return color; }; -const getSecondaryColor = type => { +const getNotificationBgColor = type => { let color; switch (type) { case 'info': - color = colors.INFO_SECONDARY; + color = colors.INFO_LIGHT; break; case 'error': - color = colors.ERROR_SECONDARY; + color = colors.ERROR_LIGHT; break; case 'warning': - color = colors.WARNING_SECONDARY; + color = colors.WARNING_LIGHT; break; case 'success': - color = colors.SUCCESS_SECONDARY; + color = colors.SUCCESS_LIGHT; break; default: color = null; @@ -46,4 +46,4 @@ const getSecondaryColor = type => { const getIcon = type => icons[type.toUpperCase()]; -export { getPrimaryColor, getSecondaryColor, getIcon }; +export { getPrimaryColor, getNotificationBgColor, getIcon }; diff --git a/src/views/Landing/components/InitializationError/index.js b/src/views/Landing/components/InitializationError/index.js index bb19573f..f39bc2e9 100644 --- a/src/views/Landing/components/InitializationError/index.js +++ b/src/views/Landing/components/InitializationError/index.js @@ -13,7 +13,7 @@ const InitializationError = (props: { error: string }) => ( diff --git a/src/views/Wallet/views/Account/Receive/ethereum/index.js b/src/views/Wallet/views/Account/Receive/ethereum/index.js index c5813bf0..044a7e0c 100644 --- a/src/views/Wallet/views/Account/Receive/ethereum/index.js +++ b/src/views/Wallet/views/Account/Receive/ethereum/index.js @@ -78,7 +78,7 @@ const QrWrapper = styled.div` `; const StyledDeviceIcon = styled(DeviceIcon)` - margin: 0 6px; + margin-right: 6px; `; const AccountReceive = (props: Props) => { @@ -118,7 +118,7 @@ const AccountReceive = (props: Props) => { topLabel={props.intl.formatMessage(l10nCommonMessages.TR_ADDRESS)} value={address} isPartiallyHidden={isAddressHidden} - trezorAction={ + tooltipAction={ isAddressVerifying ? ( { @@ -124,7 +124,7 @@ const AccountReceive = (props: Props) => { topLabel={props.intl.formatMessage(l10nCommonMessages.TR_ADDRESS)} value={address} isPartiallyHidden={isAddressHidden} - trezorAction={ + tooltipAction={ isAddressVerifying ? ( { } }} loadOptions={input => props.loadTokens(input, account.network)} - formatOptionLabel={option => option.name} + formatOptionLabel={option => { + const isAdded = tokens.find(t => t.symbol === option.symbol); + if (isAdded) { + return `${option.name} (${props.intl.formatMessage( + l10nSummaryMessages.TR_ALREADY_ADDED + )})`; + } + return option.name; + }} getOptionLabel={option => option.name} getOptionValue={option => option.symbol} /> diff --git a/src/views/Wallet/views/Acquire/index.js b/src/views/Wallet/views/Acquire/index.js index 8e84263f..de2dede0 100644 --- a/src/views/Wallet/views/Acquire/index.js +++ b/src/views/Wallet/views/Acquire/index.js @@ -37,7 +37,7 @@ const Acquire = (props: Props) => ( { isDisabled={ !selectedNetwork || address === '' || props.importAccount.loading } + isLoading={props.importAccount.loading} onClick={() => props.importAddress( address, diff --git a/src/views/Wallet/views/UnreadableDevice/index.js b/src/views/Wallet/views/UnreadableDevice/index.js index 847f59a5..2ef951d9 100644 --- a/src/views/Wallet/views/UnreadableDevice/index.js +++ b/src/views/Wallet/views/UnreadableDevice/index.js @@ -14,7 +14,7 @@ const UnreadableDevice = ({ intl }: { intl: IntlShape }) => ( diff --git a/translations/master.json b/translations/master.json index 8e42c6e0..2931d9ca 100644 --- a/translations/master.json +++ b/translations/master.json @@ -1615,6 +1615,14 @@ ] } }, + "TR_ALREADY_ADDED": { + "source": "Already added", + "meta": { + "occurrences": [ + "src/views/Wallet/views/Account/Summary/common.messages.js" + ] + } + }, "TR_HISTORY": { "source": "History", "meta": { diff --git a/webpack/dev.babel.js b/webpack/dev.babel.js index 21e25db7..3cc7f6e9 100644 --- a/webpack/dev.babel.js +++ b/webpack/dev.babel.js @@ -88,6 +88,7 @@ module.exports = { alias: { public: PUBLIC, react: path.resolve('./node_modules/react'), + 'react-dom': '@hot-loader/react-dom', }, modules: [SRC, 'node_modules'], symlinks: false, diff --git a/yarn.lock b/yarn.lock index 9780960c..dfb730d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -858,6 +858,16 @@ resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-0.8.2.tgz#576ff7fb1230185b619a75d258cbc98f0867a8dc" integrity sha512-rLu3wcBWH4P5q1CGoSSH/i9hrXs7SlbRLkoq9IGuoPYNGQvDJ3pt/wmOM+XgYjIDRMVIdkUWt0RsfzF50JfnCw== +"@hot-loader/react-dom@16.8.6": + version "16.8.6" + resolved "https://registry.yarnpkg.com/@hot-loader/react-dom/-/react-dom-16.8.6.tgz#7923ba27db1563a7cc48d4e0b2879a140df461ea" + integrity sha512-+JHIYh33FVglJYZAUtRjfT5qZoT2mueJGNzU5weS2CVw26BgbxGKSujlJhO85BaRbg8sqNWyW1hYBILgK3ZCgA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.13.6" + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -11194,10 +11204,10 @@ trezor-translations-manager@^1.0.5: request "^2.88.0" request-promise-native "^1.0.5" -trezor-ui-components@^1.0.0-beta.14: - version "1.0.0-beta.14" - resolved "https://registry.yarnpkg.com/trezor-ui-components/-/trezor-ui-components-1.0.0-beta.14.tgz#5d6dd3a8f5e1ace33a03eae7bb6d089ae0866575" - integrity sha512-jJ8hMn6Y97lg1a3YMUgQoCosYiPH3L43IJPapAw1c6PSVCPk43QUcbPxtA629Iv32EdCiMlDvANlZgyY9kH98g== +trezor-ui-components@^1.0.0-beta.18: + version "1.0.0-beta.18" + resolved "https://registry.yarnpkg.com/trezor-ui-components/-/trezor-ui-components-1.0.0-beta.18.tgz#2408dbdedc83e986abcde9d8b2aec3ec37bbdc43" + integrity sha512-mwS7dqrAUto25ytZpF9K2BQpjTBH78/PJjHVGBoZrLJkONogqOvCCdIbkrUSkNR+ZTep4MoxDF3ZN7SXGsFr+g== dependencies: "@tippy.js/react" "^2.1.1" prop-types "^15.7.2"