diff --git a/i18n/locales/en/common.json b/i18n/locales/en/common.json index 1b6a3910aa..4e849f0eac 100644 --- a/i18n/locales/en/common.json +++ b/i18n/locales/en/common.json @@ -170,6 +170,7 @@ "Send Lisk from Blockchain Application": "Send Lisk from Blockchain Application", "Send Lisk to Blockchain Application": "Send Lisk to Blockchain Application", "Send to Address": "Send to Address", + "Send to this address": "Send to this address", "Sender": "Sender", "Settings": "Settings", "Show passphrase": "Show passphrase", diff --git a/src/components/accountTransactions/accountTransactions.css b/src/components/accountTransactions/accountTransactions.css index fbf03b66ce..483d57e9ba 100644 --- a/src/components/accountTransactions/accountTransactions.css +++ b/src/components/accountTransactions/accountTransactions.css @@ -7,6 +7,10 @@ .wrapper { min-width: 100%; + + & .sendTo { + width: 100%; + } } @media (--xLarge-viewport) { diff --git a/src/components/accountTransactions/index.js b/src/components/accountTransactions/index.js index 4dc5ebae7c..824b328fce 100644 --- a/src/components/accountTransactions/index.js +++ b/src/components/accountTransactions/index.js @@ -4,7 +4,7 @@ import { translate } from 'react-i18next'; import grid from 'flexboxgrid/dist/flexboxgrid.css'; import { transactionsRequestInit } from '../../actions/transactions'; import Transactions from './../transactions'; -import Send from '../send'; +import SendTo from '../sendTo'; import styles from './accountTransactions.css'; class accountTransactions extends React.Component { @@ -17,8 +17,12 @@ class accountTransactions extends React.Component { // eslint-disable-next-line class-methods-use-this render() { return
-
- +
+
([ ) % options.length], ]); -const AccountVisual = ({ address, size = 200, className }) => { - const addressChunks = address.padStart(21, '0').match(/\d{5}/g); - const gradientScheme = gradientSchemes[addressChunks[0].substr(1, 2) % gradientSchemes.length]; - const primaryGradients = pickTwo(addressChunks[1], gradientScheme.primary); - const secondaryGradients = pickTwo(addressChunks[2], gradientScheme.secondary); - const shapes = [ - getBackgroundCircle(size, primaryGradients[0]), - getShape(addressChunks[1], size, primaryGradients[1], 1), - getShape(addressChunks[2], size, secondaryGradients[0], 0.23), - getShape(addressChunks[3], size, secondaryGradients[1], 0.18), - ]; +class AccountVisual extends React.Component { + constructor(props) { + super(props); + this.state = { isMobile: window.innerWidth < 1024 }; + } - return ( -
- - - {shapes.map((shape, i) => ( - - ))} - -
- ); -}; + shouldComponentUpdate(nextProps, state) { + return this.state.isMobile !== state.isMobile; + } + + resizeWindow() { + this.setState({ isMobile: window.innerWidth < 1024 }); + } + + componentDidMount() { + window.addEventListener('resize', this.resizeWindow.bind(this)); + } + + componentWillUnmount() { + window.removeEventListener('resize', this.resizeWindow.bind(this)); + } + + render() { + const { address, size, mobileSize, className } = this.props; + const desktopSize = size || 200; + const newSize = this.state.isMobile && mobileSize ? mobileSize : desktopSize; + + const addressChunks = address.padStart(21, '0').match(/\d{5}/g); + const gradientScheme = gradientSchemes[addressChunks[0].substr(1, 2) % gradientSchemes.length]; + const primaryGradients = pickTwo(addressChunks[1], gradientScheme.primary); + const secondaryGradients = pickTwo(addressChunks[2], gradientScheme.secondary); + const shapes = [ + getBackgroundCircle(newSize, primaryGradients[0]), + getShape(addressChunks[1], newSize, primaryGradients[1], 1), + getShape(addressChunks[2], newSize, secondaryGradients[0], 0.23), + getShape(addressChunks[3], newSize, secondaryGradients[1], 0.18), + ]; + return ( +
+ + + {shapes.map((shape, i) => ( + + ))} + +
+ ); + } +} export default AccountVisual; diff --git a/src/components/app/variables.css b/src/components/app/variables.css index 141b7d8be3..e58037897e 100644 --- a/src/components/app/variables.css +++ b/src/components/app/variables.css @@ -108,12 +108,11 @@ or "warn/action" ineastd of "red/green" --color-action-medium-2: #004aff; --color-link: #0077bd; --color-tertiary-medium: #17499b; - --gradient-theme: linear-gradient(27deg, #17499b 0%, var(--color-grayscale-light) 100%); + --gradient-theme: linear-gradient(27deg, var(--color-tertiary-medium) 0%, var(--color-grayscale-light) 100%); --gradient-action-orange: linear-gradient(-27deg, var(--color-action-dark) 0%, var(--color-action-light) 100%); --gradient-action-blue: linear-gradient(44deg, var(--color-action-medium-2) 1%, var(--color-action-light-2) 98%); --gradient-greyscale: linear-gradient(to right, var(--row-background-color), #f5f8fc); --gradient-greyscale-mobile: linear-gradient(to right, var(--color-grayscale-mobile-background), var(--color-grayscale-darker-mobile-background)); - --gradient-secondary: linear-gradient(45deg, #17499b 0%, var(--color-primary-medium) 100%); --gradient-tertiary: linear-gradient(45deg, var(--color-tertiary-medium) 0%, var(--color-primary-medium) 100%); /************************* diff --git a/src/components/header/header.css b/src/components/header/header.css index 3e4c9ad3c3..408df568c8 100644 --- a/src/components/header/header.css +++ b/src/components/header/header.css @@ -90,6 +90,11 @@ display: inline-block; } +.rightSide { + display: inline-block; + width: 32vw; /* stylelint-disable-line */ +} + .account { position: relative; height: 80px; diff --git a/src/components/header/header.js b/src/components/header/header.js index f555017ca2..ae06f75ff7 100644 --- a/src/components/header/header.js +++ b/src/components/header/header.js @@ -15,11 +15,6 @@ import RelativeLink from '../relativeLink'; import routes from './../../constants/routes'; class Header extends React.Component { - constructor(props) { - super(props); - this.state = { isMobile: window.innerWidth < 1024 }; - } - shouldShowActionButton() { return !this.props.isAuthenticated && this.props.location.pathname !== routes.login.url && @@ -32,18 +27,6 @@ class Header extends React.Component { return this.props.location.pathname.includes('explorer') && !this.props.location.pathname.includes(routes.search.long); } - resizeWindow() { - this.setState({ isMobile: window.innerWidth < 1024 }); - } - - componentDidMount() { - window.addEventListener('resize', this.resizeWindow.bind(this)); - } - - componentWillUnmount() { - window.removeEventListener('resize', this.resizeWindow.bind(this)); - } - render() { return (
@@ -84,7 +67,7 @@ class Header extends React.Component {
diff --git a/src/components/sendTo/index.js b/src/components/sendTo/index.js new file mode 100644 index 0000000000..0e514750ad --- /dev/null +++ b/src/components/sendTo/index.js @@ -0,0 +1,73 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import grid from 'flexboxgrid/dist/flexboxgrid.css'; +import { TertiaryButton } from './../toolbox/buttons/button'; +import { FontIcon } from '../fontIcon'; +import Box from '../box'; +import LiskAmount from '../liskAmount'; +import AccountVisual from '../accountVisual'; +import CopyToClipboard from '../copyToClipboard'; +import routes from './../../constants/routes'; +import styles from './sendTo.css'; + +class SendTo extends React.Component { + shouldComponentUpdate(nextProps) { + return nextProps.address !== this.props.address; + } + + render() { + return ( +
+
+ +
+

+ + + LSK +

+ +
+
+
+ + + {this.props.t('Send to this address')} + + +
+
+
+ ); + } +} + +export default SendTo; diff --git a/src/components/sendTo/sendTo.css b/src/components/sendTo/sendTo.css new file mode 100644 index 0000000000..3ab8860ebc --- /dev/null +++ b/src/components/sendTo/sendTo.css @@ -0,0 +1,145 @@ +@import '../app/variables.css'; + +:root { + --header-address-font-weight: var(--font-weight-semi-bold); + --header-balance-unit-font-size-XL: 20px; + --header-balance-unit-font-size-L: 18px; + --header-balance-unit-font-size-XS: 18px; + --header-subtitle-font-size-XL: 18px; + --header-subtitle-font-size-L: 16px; + --header-subtitle-font-size-XS: 14px; + --main-header-font-size-XL: 32px; + --main-header-font-size-L: 28px; + --main-header-font-size-XS: 24px; +} + +.wrapper { + padding: 9.2vh 42px; /* stylelint-disable-line */ + text-align: center; + line-height: 36px; + height: 100%; + display: flex; + justify-content: space-around; + + & .account { + margin-bottom: 8.5vh; /* stylelint-disable-line */ + + & .copy { + color: var(--color-grayscale-medium); + } + + & h2 { + margin-bottom: 0; + font-weight: var(--font-weight-semi-bold); + white-space: nowrap; + + & .balanceUnit { + font-weight: var(--font-weight-semi-bold); + } + } + + & .address { + color: var(--color-grayscale-dark); + white-space: nowrap; + } + } + + & .sendButton { + margin: auto 0px; + } + + & .button { + font-weight: var(--font-weight-bold); + font-size: 16px; + letter-spacing: 0; + line-height: 18px; + text-transform: none; + width: 100%; + } +} + +@media (--xLarge-viewport) { + .wrapper { + & h2 { + font-size: var(--main-header-font-size-XL); + + & .balanceUnit { + font-size: var(--header-balance-unit-font-size-XL); + } + } + + & .address { + font-size: var(--header-subtitle-font-size-XL); + } + } +} + +@media (--large-viewport) { + .wrapper { + & h2 { + font-size: var(--main-header-font-size-L); + + & .balanceUnit { + font-size: var(--header-balance-unit-font-size-L); + } + } + + & .address { + font-size: var(--header-subtitle-font-size-L); + } + } +} + +@media (--medium-viewport) { + .wrapper { + flex-direction: row; + min-height: 1px; + height: auto; + background: var(--color-grayscale-darker-mobile-background); + display: flex; + + & .content { + display: flex; + width: 100%; + } + + & .account { + margin: auto 0px; + text-align: left; + + & h2 { + margin: 0px; + } + } + + & .sendButton { + margin: auto 0px; + } + } +} + +@media (--small-viewport) { + .wrapper { + padding: 5.3vh 24px; /* stylelint-disable-line */ + + & .content { + display: block; + } + + & h2 { + font-size: var(--main-header-font-size-XS); + + & .balanceUnit { + font-size: var(--header-balance-unit-font-size-XS); + } + } + + & .address { + font-size: var(--header-subtitle-font-size-XS); + } + + & .sendButton { + margin-top: 3.2vh; /* stylelint-disable-line */ + } + } +} diff --git a/src/components/sendTo/sendTo.test.js b/src/components/sendTo/sendTo.test.js new file mode 100644 index 0000000000..b420a11407 --- /dev/null +++ b/src/components/sendTo/sendTo.test.js @@ -0,0 +1,28 @@ +import React from 'react'; +import { expect } from 'chai'; +import { mountWithContext } from './../../../test/utils/mountHelpers'; +import SendTo from './index'; +import routes from './../../constants/routes'; + +describe('SendTo Component', () => { + let wrapper; + let props; + + beforeEach(() => { + props = { + address: '12345L', + balance: 0, + t: key => key, + }; + wrapper = mountWithContext(, {}); + }); + + it('renders correct link', () => { + expect(wrapper.find('Link').prop('to')).to.equal(`${routes.wallet.long}?address=${props.address}`); + }); + + it('updates when address changes', () => { + wrapper.setProps({ address: '9876L' }); + expect(wrapper.find('Link').prop('to')).to.equal(`${routes.wallet.long}?address=9876L`); + }); +}); diff --git a/src/components/sendWritable/index.js b/src/components/sendWritable/index.js index 346ada2917..226d4eaeb4 100644 --- a/src/components/sendWritable/index.js +++ b/src/components/sendWritable/index.js @@ -1,5 +1,6 @@ import { connect } from 'react-redux'; import { translate } from 'react-i18next'; +import { withRouter } from 'react-router-dom'; import { sent } from '../../actions/account'; import Send from './send'; @@ -14,4 +15,4 @@ const mapDispatchToProps = dispatch => ({ sent: data => dispatch(sent(data)), }); -export default connect(mapStateToProps, mapDispatchToProps)(translate()(Send)); +export default withRouter(connect(mapStateToProps, mapDispatchToProps)(translate()(Send))); diff --git a/src/components/sendWritable/index.test.js b/src/components/sendWritable/index.test.js index b0ab5a431c..18a85d0c73 100644 --- a/src/components/sendWritable/index.test.js +++ b/src/components/sendWritable/index.test.js @@ -1,4 +1,5 @@ import React from 'react'; +import { MemoryRouter as Router } from 'react-router-dom'; import { expect } from 'chai'; import { mount } from 'enzyme'; import { Provider } from 'react-redux'; @@ -22,7 +23,7 @@ describe('SendWritableHOC', () => { }); store.subscribe = () => {}; store.dispatch = () => {}; - wrapper = mount(); + wrapper = mount(); }); it('should render Send', () => { diff --git a/src/components/sendWritable/send.js b/src/components/sendWritable/send.js index 8476074ea9..862e82b570 100644 --- a/src/components/sendWritable/send.js +++ b/src/components/sendWritable/send.js @@ -1,5 +1,6 @@ import React from 'react'; import { fromRawLsk } from '../../utils/lsk'; +import { parseSearchParams } from './../../utils/searchParams'; import { Button } from './../toolbox/buttons/button'; import { authStatePrefill } from '../../utils/form'; import AccountVisual from '../accountVisual'; @@ -10,11 +11,12 @@ import regex from './../../utils/regex'; import inputTheme from './input.css'; class SendWritable extends React.Component { - constructor() { - super(); + constructor(props) { + super(props); + const { address } = parseSearchParams(props.history.location.search); this.state = { recipient: { - value: '', + value: address || '', }, amount: { value: '', @@ -31,7 +33,7 @@ class SendWritable extends React.Component { componentDidMount() { const newState = { recipient: { - value: this.props.recipient || '', + value: this.props.recipient || this.state.recipient.value, }, amount: { value: this.props.amount || '', diff --git a/src/components/sendWritable/send.test.js b/src/components/sendWritable/send.test.js index 71d1b3cffe..d71fe171ee 100644 --- a/src/components/sendWritable/send.test.js +++ b/src/components/sendWritable/send.test.js @@ -29,6 +29,7 @@ describe('Send Writable Component', () => { sent: sinon.spy(), t: key => key, nextStep: () => {}, + history: { location: { search: '' } }, }; wrapper = mount(, { context: { store, i18n }, diff --git a/src/components/toolbox/buttons/button.js b/src/components/toolbox/buttons/button.js index e2e5499d9a..a32b1570f3 100644 --- a/src/components/toolbox/buttons/button.js +++ b/src/components/toolbox/buttons/button.js @@ -4,6 +4,7 @@ import { Button as ToolBoxButton } from 'react-toolbox/lib/button'; import secondaryBlueButtonTheme from './css/secondaryBlueButton.css'; import secondaryLightButtonTheme from './css/secondaryLightButton.css'; import primaryButtonTheme from './css/primaryButton.css'; +import tertiaryButtonTheme from './css/tertiaryButton.css'; import actionButtonTheme from './css/actionButton.css'; class TBSecondaryBlueButton extends React.Component { @@ -24,16 +25,24 @@ class TBSecondaryLightButton extends React.Component { } } +class TBTertiaryButton extends React.Component { + render() { + return ; + } +} + class TBActionButton extends React.Component { render() { return ; } } + const PrimaryButton = themr('importantButton', primaryButtonTheme)(TBPrimaryButton); const SecondaryLightButton = themr('lightButton', secondaryLightButtonTheme)(TBSecondaryLightButton); -const ActionButton = themr('lightButton', actionButtonTheme)(TBActionButton); +const TertiaryButton = themr('tertiaryButton', tertiaryButtonTheme)(TBTertiaryButton); +const ActionButton = themr('actionButton', actionButtonTheme)(TBActionButton); const Button = themr('button', secondaryBlueButtonTheme)(TBSecondaryBlueButton); -export { Button, PrimaryButton, SecondaryLightButton, ActionButton }; +export { Button, PrimaryButton, SecondaryLightButton, TertiaryButton, ActionButton }; export default Button; diff --git a/src/components/toolbox/buttons/css/base.css b/src/components/toolbox/buttons/css/base.css index f7a66331fb..0c3f66896f 100644 --- a/src/components/toolbox/buttons/css/base.css +++ b/src/components/toolbox/buttons/css/base.css @@ -3,8 +3,9 @@ @define-mixin buttonBasics { min-width: 250px; border-radius: 3px; - font-size: 18px; text-transform: capitalize; + font-weight: var(--font-weight-bold); + font-family: var(--content-font) !important; transition: none !important; &:disabled { @@ -50,6 +51,15 @@ } } +@define-mixin buttonTertiary { + color: var(--color-white) !important; + background: var(--gradient-tertiary); + + &:hover { + background: var(--color-tertiary-medium) !important; + } +} + @define-mixin buttonAction { color: var(--color-white) !important; background: var(--gradient-action-blue); diff --git a/src/components/toolbox/buttons/css/tertiaryButton.css b/src/components/toolbox/buttons/css/tertiaryButton.css new file mode 100644 index 0000000000..40d33fbc13 --- /dev/null +++ b/src/components/toolbox/buttons/css/tertiaryButton.css @@ -0,0 +1,8 @@ +@import './base.css'; + +.button { + min-height: 60px; + + @mixin buttonBasics; + @mixin buttonTertiary; +}