Browse Source

Refactor of top banner and nano-x promotional banner

develop
Juan Cortes Ross 6 years ago
parent
commit
b7b81ad420
No known key found for this signature in database GPG Key ID: 34A99C03E9455EB8
  1. 26
      src/components/DashboardPage/index.js
  2. 129
      src/components/TopBanner.js
  3. 88
      src/components/Updater/Banner.js
  4. 3
      src/config/urls.js
  5. 3
      static/i18n/en/app.json

26
src/components/DashboardPage/index.js

@ -3,6 +3,7 @@
import React, { PureComponent, Fragment } from 'react'
import uniq from 'lodash/uniq'
import { compose } from 'redux'
import IconExternalLink from 'icons/ExternalLink'
import { translate } from 'react-i18next'
import { connect } from 'react-redux'
import { push } from 'react-router-redux'
@ -31,10 +32,14 @@ import Box from 'components/base/Box'
import PillsDaysCount from 'components/PillsDaysCount'
import OperationsList from 'components/OperationsList'
import StickyBackToTop from 'components/StickyBackToTop'
import styled from 'styled-components'
import { openURL } from 'helpers/linking'
import EmptyState from './EmptyState'
import CurrentGreetings from './CurrentGreetings'
import SummaryDesc from './SummaryDesc'
import AccountCardList from './AccountCardList'
import TopBanner, { FakeLink } from '../TopBanner'
import { urls } from '../../config/urls'
const mapStateToProps = createStructuredSelector({
accounts: accountsSelector,
@ -84,7 +89,20 @@ class DashboardPage extends PureComponent<Props> {
return (
<Fragment>
<UpdateBanner />
<TopBannerContainer>
<UpdateBanner />
<TopBanner
content={{
message: t('banners.promoteMobile'),
Icon: IconExternalLink,
right: (
<FakeLink onClick={() => openURL(urls.nanoX)}>{t('common.learnMore')}</FakeLink>
),
}}
bannerId={'promoteMobile'}
dismissable
/>
</TopBannerContainer>
<RefreshAccountsOrdering onMount />
<TrackPage
category="Portfolio"
@ -143,6 +161,12 @@ class DashboardPage extends PureComponent<Props> {
)
}
}
// This forces only one visible top banner at a time
const TopBannerContainer = styled.div`
& > *:not(:first-child) {
display: none;
}
`
export default compose(
connect(

129
src/components/TopBanner.js

@ -0,0 +1,129 @@
// @flow
import React, { PureComponent } from 'react'
import styled from 'styled-components'
import { connect } from 'react-redux'
import Box from 'components/base/Box'
import { radii } from 'styles/theme'
import IconCross from 'icons/Cross'
import { createStructuredSelector } from 'reselect'
import { dismissBanner } from '../actions/settings'
import { dismissedBannersSelector } from '../reducers/settings'
export type Content = {
Icon?: React$ComponentType<*>,
message: React$Node,
right?: React$Node,
}
type Props = {
content?: Content,
status: string,
dismissable: boolean,
bannerId?: string,
dismissedBanners: string[],
dismissBanner: string => void,
}
const mapStateToProps = createStructuredSelector({
dismissedBanners: dismissedBannersSelector,
})
const mapDispatchToProps = {
dismissBanner,
}
class TopBanner extends PureComponent<Props> {
static defaultProps = {
status: '',
dismissable: false,
}
onDismiss = () => {
const { bannerId, dismissBanner } = this.props
if (bannerId) {
dismissBanner(bannerId)
}
}
render() {
const { dismissedBanners, bannerId, dismissable, content, status } = this.props
if (!content || (bannerId && dismissedBanners.includes(bannerId))) return null
const { Icon, message, right } = content
return (
<Container status={status}>
{Icon && (
<IconContainer>
<Icon size={16} />
</IconContainer>
)}
{message}
<RightContainer>{right}</RightContainer>
{dismissable && (
<CloseContainer onClick={this.onDismiss}>
<IconCross size={16} />
</CloseContainer>
)}
</Container>
)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps,
)(TopBanner)
const IconContainer = styled.div`
margin-right: 15px;
display: flex;
align-items: center;
`
const colorForStatus = {
error: 'alertRed',
}
const Container = styled(Box).attrs({
horizontal: true,
align: 'center',
py: '8px',
px: 3,
bg: p => colorForStatus[p.status] || 'wallet',
color: 'white',
mt: -20,
mb: 20,
fontSize: 4,
ff: 'Open Sans|SemiBold',
})`
border-radius: ${radii[1]}px;
`
const RightContainer = styled.div`
margin-left: auto;
`
export const FakeLink = styled.span`
color: white;
text-decoration: underline;
cursor: pointer;
`
const CloseContainer = styled(Box).attrs({
color: 'white',
})`
z-index: 1;
margin-left: 10px;
cursor: pointer;
&:hover {
color: ${p => p.theme.colors.graphite};
}
&:active {
color: ${p => p.theme.colors.graphite};
}
`

88
src/components/Updater/Banner.js

@ -1,21 +1,20 @@
// @flow
import React, { PureComponent } from 'react'
import styled from 'styled-components'
import { Trans } from 'react-i18next'
import { urls } from 'config/urls'
import { radii } from 'styles/theme'
import { openURL } from 'helpers/linking'
import Spinner from 'components/base/Spinner'
import Box from 'components/base/Box'
import IconUpdate from 'icons/Update'
import IconDonjon from 'icons/Donjon'
import IconWarning from 'icons/TriangleWarning'
import { withUpdaterContext } from './UpdaterContext'
import type { UpdaterContextType } from './UpdaterContext'
import TopBanner, { FakeLink } from '../TopBanner'
import type { Content } from '../TopBanner'
type Props = {
context: UpdaterContextType,
@ -23,25 +22,11 @@ type Props = {
export const VISIBLE_STATUS = ['download-progress', 'checking', 'check-success', 'error']
type Content = {
Icon: React$ComponentType<*>,
message: React$Node,
Right?: React$ComponentType<*>,
}
type RightProps = {
downloadProgress: number, // eslint-disable-line react/no-unused-prop-types
quitAndInstall: () => void, // eslint-disable-line react/no-unused-prop-types
reDownload: () => void, // eslint-disable-line react/no-unused-prop-types
}
const CONTENT_BY_STATUS: { [_: string]: Content } = {
const CONTENT_BY_STATUS = (quitAndInstall, reDownload, progress): { [string]: Content } => ({
'download-progress': {
Icon: Spinner,
message: <Trans i18nKey="update.downloadInProgress" />,
Right: ({ downloadProgress }: RightProps) => (
<Trans i18nKey="update.downloadProgress" values={{ progress: downloadProgress }} />
),
right: <Trans i18nKey="update.downloadProgress" values={{ progress }} />,
},
checking: {
Icon: IconDonjon,
@ -50,7 +35,7 @@ const CONTENT_BY_STATUS: { [_: string]: Content } = {
'check-success': {
Icon: IconUpdate,
message: <Trans i18nKey="update.checkSuccess" />,
Right: ({ quitAndInstall }: RightProps) => (
right: (
<FakeLink onClick={quitAndInstall}>
<Trans i18nKey="update.quitAndInstall" />
</FakeLink>
@ -59,13 +44,13 @@ const CONTENT_BY_STATUS: { [_: string]: Content } = {
error: {
Icon: IconWarning,
message: <Trans i18nKey="update.error" />,
Right: ({ reDownload }: RightProps) => (
right: (
<FakeLink onClick={reDownload}>
<Trans i18nKey="update.reDownload" />
</FakeLink>
),
},
}
})
class UpdaterTopBanner extends PureComponent<Props> {
reDownload = () => {
@ -75,66 +60,15 @@ class UpdaterTopBanner extends PureComponent<Props> {
render() {
const { context } = this.props
const { status, quitAndInstall, downloadProgress } = context
if (!VISIBLE_STATUS.includes(status)) return null
const content: ?Content = CONTENT_BY_STATUS[status]
const content: ?Content = CONTENT_BY_STATUS(quitAndInstall, this.reDownload, downloadProgress)[
status
]
if (!content) return null
const { Icon, message, Right } = content
return (
<Container status={status}>
{Icon && (
<IconContainer>
{/* $FlowFixMe let me do my stuff, flow */}
<Icon size={16} />
</IconContainer>
)}
{message}
{Right && (
<RightContainer>
<Right
downloadProgress={downloadProgress}
quitAndInstall={quitAndInstall}
reDownload={this.reDownload}
/>
</RightContainer>
)}
</Container>
)
return <TopBanner content={content} status={status} />
}
}
const IconContainer = styled.div`
margin-right: 15px;
display: flex;
align-items: center;
`
const Container = styled(Box).attrs({
horizontal: true,
align: 'center',
py: '8px',
px: 3,
bg: p => (p.status === 'error' ? 'alertRed' : 'wallet'),
color: 'white',
mt: -20,
mb: 20,
fontSize: 4,
ff: 'Open Sans|SemiBold',
})`
border-radius: ${radii[1]}px;
`
const FakeLink = styled.span`
color: white;
text-decoration: underline;
cursor: pointer;
`
const RightContainer = styled.div`
margin-left: auto;
`
export default withUpdaterContext(UpdaterTopBanner)

3
src/config/urls.js

@ -8,6 +8,9 @@ export const urls = {
github: 'https://github.com/LedgerHQ/ledger-live-desktop',
reddit: 'https://www.reddit.com/r/ledgerwallet/',
// Products
nanoX: 'https://www.ledger.com/pages/ledger-nano-x',
// Ledger support
faq: 'https://support.ledgerwallet.com/hc/en-us',
terms: 'https://www.ledger.com/pages/terms-of-use-and-disclaimer',

3
static/i18n/en/app.json

@ -111,6 +111,9 @@
}
}
},
"banners": {
"promoteMobile": "Enjoy the Ledger Live experience, now available on mobile with the Ledger Nano X"
},
"dashboard": {
"title": "Portfolio",
"emptyAccountTile": {

Loading…
Cancel
Save