diff --git a/__tests__/components/button.test.js b/__tests__/components/button.test.js index f40f618..fc055a2 100644 --- a/__tests__/components/button.test.js +++ b/__tests__/components/button.test.js @@ -6,7 +6,7 @@ import { ThemeProvider } from 'styled-components'; import 'jest-dom/extend-expect'; import { Button } from '../../app/components/button'; -import appTheme from '../../app/theme'; +import { appTheme } from '../../app/theme'; afterEach(cleanup); diff --git a/__tests__/components/clipboard.test.js b/__tests__/components/clipboard.test.js index c77f3ce..2db0a8e 100644 --- a/__tests__/components/clipboard.test.js +++ b/__tests__/components/clipboard.test.js @@ -6,7 +6,7 @@ import { ThemeProvider } from 'styled-components'; import 'jest-dom/extend-expect'; import { Clipboard } from '../../app/components/clipboard'; -import appTheme from '../../app/theme'; +import { appTheme } from '../../app/theme'; afterEach(cleanup); diff --git a/__tests__/components/column.test.js b/__tests__/components/column.test.js index 5cdb0ef..56479ec 100644 --- a/__tests__/components/column.test.js +++ b/__tests__/components/column.test.js @@ -6,13 +6,12 @@ import { ThemeProvider } from 'styled-components'; import 'jest-dom/extend-expect'; import { ColumnComponent } from '../../app/components/column'; -import appTheme from '../../app/theme'; +import { appTheme } from '../../app/theme'; afterEach(cleanup); describe('', () => { test('should render correctly', () => { - // $FlowFixMe const { container } = render( diff --git a/__tests__/components/divider.test.js b/__tests__/components/divider.test.js index fe2c1c4..881def0 100644 --- a/__tests__/components/divider.test.js +++ b/__tests__/components/divider.test.js @@ -6,13 +6,12 @@ import { ThemeProvider } from 'styled-components'; import 'jest-dom/extend-expect'; import { Divider } from '../../app/components/divider'; -import appTheme from '../../app/theme'; +import { appTheme } from '../../app/theme'; afterEach(cleanup); describe('', () => { test('should render correctly', () => { - // $FlowFixMe const { container } = render( diff --git a/__tests__/components/dropdown.test.js b/__tests__/components/dropdown.test.js index 811e313..0cef628 100644 --- a/__tests__/components/dropdown.test.js +++ b/__tests__/components/dropdown.test.js @@ -7,7 +7,7 @@ import 'jest-dom/extend-expect'; import { DropdownComponent } from '../../app/components/dropdown'; import { Button } from '../../app/components/button'; -import appTheme from '../../app/theme'; +import { appTheme } from '../../app/theme'; afterEach(cleanup); @@ -28,8 +28,8 @@ describe('', () => { /> )} options={[ - { label: 'asbh1yeasbdh23848asdasd', onClick: console.log }, - { label: 'urtyruhjr374hbfdjdhuh', onClick: console.log }, + { label: 'asbh1yeasbdh23848asdasd', onClick: console.log }, // eslint-disable-line + { label: 'urtyruhjr374hbfdjdhuh', onClick: console.log }, // eslint-disable-line ]} /> @@ -53,8 +53,8 @@ describe('', () => { /> )} options={[ - { label: 'asbh1yeasbdh23848asdasd', onClick: console.log }, - { label: 'urtyruhjr374hbfdjdhuh', onClick: console.log }, + { label: 'asbh1yeasbdh23848asdasd', onClick: console.log }, // eslint-disable-line + { label: 'urtyruhjr374hbfdjdhuh', onClick: console.log }, // eslint-disable-line ]} /> diff --git a/__tests__/components/empty-transactions.test.js b/__tests__/components/empty-transactions.test.js index 95e3fd4..779ebee 100644 --- a/__tests__/components/empty-transactions.test.js +++ b/__tests__/components/empty-transactions.test.js @@ -6,7 +6,7 @@ import { ThemeProvider } from 'styled-components'; import 'jest-dom/extend-expect'; import { EmptyTransactionsComponent } from '../../app/components/empty-transactions'; -import appTheme from '../../app/theme'; +import { appTheme } from '../../app/theme'; afterEach(cleanup); diff --git a/__tests__/components/input-label.test.js b/__tests__/components/input-label.test.js index 0cbb732..42d822e 100644 --- a/__tests__/components/input-label.test.js +++ b/__tests__/components/input-label.test.js @@ -6,7 +6,7 @@ import { ThemeProvider } from 'styled-components'; import 'jest-dom/extend-expect'; import { InputLabelComponent } from '../../app/components/input-label'; -import appTheme from '../../app/theme'; +import { appTheme } from '../../app/theme'; afterEach(cleanup); diff --git a/__tests__/components/input.test.js b/__tests__/components/input.test.js index 695a0f5..78a439d 100644 --- a/__tests__/components/input.test.js +++ b/__tests__/components/input.test.js @@ -6,7 +6,7 @@ import { ThemeProvider } from 'styled-components'; import 'jest-dom/extend-expect'; import { InputComponent } from '../../app/components/input'; -import appTheme from '../../app/theme'; +import { appTheme } from '../../app/theme'; afterEach(cleanup); diff --git a/__tests__/components/loading-screen.test.js b/__tests__/components/loading-screen.test.js index 3932a59..2d69527 100644 --- a/__tests__/components/loading-screen.test.js +++ b/__tests__/components/loading-screen.test.js @@ -6,7 +6,7 @@ import { ThemeProvider } from 'styled-components'; import 'jest-dom/extend-expect'; import { LoadingScreen } from '../../app/components/loading-screen'; -import appTheme from '../../app/theme'; +import { appTheme } from '../../app/theme'; afterEach(cleanup); diff --git a/__tests__/components/qrcode.test.js b/__tests__/components/qrcode.test.js index 7960b31..229257f 100644 --- a/__tests__/components/qrcode.test.js +++ b/__tests__/components/qrcode.test.js @@ -11,7 +11,7 @@ afterEach(cleanup); describe('', () => { test('should render qrcode component correctly', () => { const { queryByTestId } = render( - , + , ); expect(queryByTestId('QRCode')).toBeInTheDocument(); diff --git a/__tests__/components/row.test.js b/__tests__/components/row.test.js index 1b08c33..8b4b241 100644 --- a/__tests__/components/row.test.js +++ b/__tests__/components/row.test.js @@ -6,7 +6,7 @@ import { ThemeProvider } from 'styled-components'; import 'jest-dom/extend-expect'; import { RowComponent } from '../../app/components/row'; -import appTheme from '../../app/theme'; +import { appTheme } from '../../app/theme'; afterEach(cleanup); diff --git a/__tests__/components/select.test.js b/__tests__/components/select.test.js index b4ae8d1..61cb4d2 100644 --- a/__tests__/components/select.test.js +++ b/__tests__/components/select.test.js @@ -6,7 +6,7 @@ import { ThemeProvider } from 'styled-components'; import 'jest-dom/extend-expect'; import { SelectComponent } from '../../app/components/select'; -import appTheme from '../../app/theme'; +import { appTheme } from '../../app/theme'; afterEach(cleanup); @@ -33,7 +33,7 @@ describe('', () => { const { queryByTestId } = render( ', () => { const { container } = render( ', () => { amount: 1.7891, zecPrice: 1.345, date: new Date().toISOString(), + theme: appTheme, }, { type: 'send', @@ -34,6 +35,7 @@ describe('', () => { amount: 0.8458, zecPrice: 1.344, date: new Date().toISOString(), + theme: appTheme, }, ]} /> diff --git a/__tests__/components/wallet-address.test.js b/__tests__/components/wallet-address.test.js index d5550c7..9940fe9 100644 --- a/__tests__/components/wallet-address.test.js +++ b/__tests__/components/wallet-address.test.js @@ -6,7 +6,7 @@ import { ThemeProvider } from 'styled-components'; import 'jest-dom/extend-expect'; import { WalletAddress } from '../../app/components/wallet-address'; -import appTheme from '../../app/theme'; +import { appTheme } from '../../app/theme'; afterEach(cleanup); diff --git a/app/app.js b/app/app.js index 411b12a..f7b161d 100644 --- a/app/app.js +++ b/app/app.js @@ -7,9 +7,9 @@ import { ThemeProvider } from 'styled-components'; import { configureStore, history } from './redux/create'; import { Router } from './router/container'; -import theme, { GlobalStyle } from './theme'; +import { appTheme as theme, GlobalStyle } from './theme'; import electronStore from '../config/electron-store'; -import { DARK } from './constants/themes'; +import { DARK, THEME_MODE } from './constants/themes'; const store = configureStore({}); @@ -20,15 +20,15 @@ type State = { export class App extends Component { state = { - themeMode: electronStore.get('THEME_MODE') || DARK, + themeMode: electronStore.get(THEME_MODE) || DARK, }; componentDidMount() { - if (!electronStore.has('THEME_MODE')) { - electronStore.set('THEME_MODE', DARK); + if (!electronStore.has(THEME_MODE)) { + electronStore.set(THEME_MODE, DARK); } - electronStore.onDidChange('THEME_MODE', newValue => this.setState({ themeMode: newValue })); + electronStore.onDidChange(THEME_MODE, newValue => this.setState({ themeMode: newValue })); } render() { diff --git a/app/assets/images/arrow_up.png b/app/assets/images/arrow_up_dark.png similarity index 100% rename from app/assets/images/arrow_up.png rename to app/assets/images/arrow_up_dark.png diff --git a/app/assets/images/arrow_up_light.png b/app/assets/images/arrow_up_light.png new file mode 100644 index 0000000..70dd315 Binary files /dev/null and b/app/assets/images/arrow_up_light.png differ diff --git a/app/assets/images/chevron-down.svg b/app/assets/images/chevron_down_icon_dark.svg similarity index 100% rename from app/assets/images/chevron-down.svg rename to app/assets/images/chevron_down_icon_dark.svg diff --git a/app/assets/images/chevron_down_icon_light.svg b/app/assets/images/chevron_down_icon_light.svg new file mode 100644 index 0000000..da091ea --- /dev/null +++ b/app/assets/images/chevron_down_icon_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/chevron-up.svg b/app/assets/images/chevron_up_icon_dark.svg similarity index 100% rename from app/assets/images/chevron-up.svg rename to app/assets/images/chevron_up_icon_dark.svg diff --git a/app/assets/images/chevron_up_icon_light.svg b/app/assets/images/chevron_up_icon_light.svg new file mode 100644 index 0000000..74f4b1b --- /dev/null +++ b/app/assets/images/chevron_up_icon_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/console_icon.svg b/app/assets/images/console_icon.svg deleted file mode 100644 index b079435..0000000 --- a/app/assets/images/console_icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/console_icon_dark.svg b/app/assets/images/console_icon_dark.svg new file mode 100644 index 0000000..6d53b10 --- /dev/null +++ b/app/assets/images/console_icon_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/console_icon_light.svg b/app/assets/images/console_icon_light.svg new file mode 100644 index 0000000..b25c7fe --- /dev/null +++ b/app/assets/images/console_icon_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/console_zcash.png b/app/assets/images/console_zcash_dark.png similarity index 100% rename from app/assets/images/console_zcash.png rename to app/assets/images/console_zcash_dark.png diff --git a/app/assets/images/console_zcash_light.png b/app/assets/images/console_zcash_light.png new file mode 100644 index 0000000..831e005 Binary files /dev/null and b/app/assets/images/console_zcash_light.png differ diff --git a/app/assets/images/copy_icon_dark.svg b/app/assets/images/copy_icon_dark.svg new file mode 100644 index 0000000..6951bb9 --- /dev/null +++ b/app/assets/images/copy_icon_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/copy_icon_light.svg b/app/assets/images/copy_icon_light.svg new file mode 100644 index 0000000..8d04ea7 --- /dev/null +++ b/app/assets/images/copy_icon_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/dashboard_icon.svg b/app/assets/images/dashboard_icon.svg deleted file mode 100644 index 12bd57c..0000000 --- a/app/assets/images/dashboard_icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/dashboard_icon_dark.svg b/app/assets/images/dashboard_icon_dark.svg new file mode 100644 index 0000000..3d30513 --- /dev/null +++ b/app/assets/images/dashboard_icon_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/dashboard_icon_light.svg b/app/assets/images/dashboard_icon_light.svg new file mode 100644 index 0000000..b72b433 --- /dev/null +++ b/app/assets/images/dashboard_icon_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/error_icon.png b/app/assets/images/error_icon_dark.png similarity index 100% rename from app/assets/images/error_icon.png rename to app/assets/images/error_icon_dark.png diff --git a/app/assets/images/error_icon_light.png b/app/assets/images/error_icon_light.png new file mode 100644 index 0000000..f15d38a Binary files /dev/null and b/app/assets/images/error_icon_light.png differ diff --git a/app/assets/images/green_check.png b/app/assets/images/green_check_dark.png similarity index 100% rename from app/assets/images/green_check.png rename to app/assets/images/green_check_dark.png diff --git a/app/assets/images/green_check_light.png b/app/assets/images/green_check_light.png new file mode 100644 index 0000000..0014bc4 Binary files /dev/null and b/app/assets/images/green_check_light.png differ diff --git a/app/assets/images/menu_icon.svg b/app/assets/images/menu_icon_dark.svg similarity index 100% rename from app/assets/images/menu_icon.svg rename to app/assets/images/menu_icon_dark.svg diff --git a/app/assets/images/menu_icon_light.svg b/app/assets/images/menu_icon_light.svg new file mode 100644 index 0000000..2297682 --- /dev/null +++ b/app/assets/images/menu_icon_light.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/assets/images/plus_icon.svg b/app/assets/images/plus_icon_dark.svg similarity index 100% rename from app/assets/images/plus_icon.svg rename to app/assets/images/plus_icon_dark.svg diff --git a/app/assets/images/plus_icon_light.svg b/app/assets/images/plus_icon_light.svg new file mode 100644 index 0000000..e4bf654 --- /dev/null +++ b/app/assets/images/plus_icon_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/receive_icon.svg b/app/assets/images/receive_icon.svg deleted file mode 100644 index 5576d8f..0000000 --- a/app/assets/images/receive_icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/receive_icon_dark.svg b/app/assets/images/receive_icon_dark.svg new file mode 100644 index 0000000..31e0e55 --- /dev/null +++ b/app/assets/images/receive_icon_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/receive_icon_light.svg b/app/assets/images/receive_icon_light.svg new file mode 100644 index 0000000..1d0aabc --- /dev/null +++ b/app/assets/images/receive_icon_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/scan_icon_dark.svg b/app/assets/images/scan_icon_dark.svg new file mode 100644 index 0000000..fdbd8c5 --- /dev/null +++ b/app/assets/images/scan_icon_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/scan_icon_light.svg b/app/assets/images/scan_icon_light.svg new file mode 100644 index 0000000..6814320 --- /dev/null +++ b/app/assets/images/scan_icon_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/send_icon.svg b/app/assets/images/send_icon.svg deleted file mode 100644 index 1c0a162..0000000 --- a/app/assets/images/send_icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/send_icon_dark.svg b/app/assets/images/send_icon_dark.svg new file mode 100644 index 0000000..cd62ff0 --- /dev/null +++ b/app/assets/images/send_icon_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/send_icon_light.svg b/app/assets/images/send_icon_light.svg new file mode 100644 index 0000000..ea8a109 --- /dev/null +++ b/app/assets/images/send_icon_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/settings_icon.svg b/app/assets/images/settings_icon.svg deleted file mode 100644 index 0503b15..0000000 --- a/app/assets/images/settings_icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/settings_icon_dark.svg b/app/assets/images/settings_icon_dark.svg new file mode 100644 index 0000000..a97105d --- /dev/null +++ b/app/assets/images/settings_icon_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/settings_icon_light.svg b/app/assets/images/settings_icon_light.svg new file mode 100644 index 0000000..15fc861 --- /dev/null +++ b/app/assets/images/settings_icon_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/sync_icon.png b/app/assets/images/sync_icon_dark.png similarity index 100% rename from app/assets/images/sync_icon.png rename to app/assets/images/sync_icon_dark.png diff --git a/app/assets/images/sync_icon_light.png b/app/assets/images/sync_icon_light.png new file mode 100644 index 0000000..7a71042 Binary files /dev/null and b/app/assets/images/sync_icon_light.png differ diff --git a/app/assets/images/transaction_received_icon.svg b/app/assets/images/transaction_received_icon_dark.svg similarity index 100% rename from app/assets/images/transaction_received_icon.svg rename to app/assets/images/transaction_received_icon_dark.svg diff --git a/app/assets/images/transaction_received_icon_light.svg b/app/assets/images/transaction_received_icon_light.svg new file mode 100644 index 0000000..0fa464f --- /dev/null +++ b/app/assets/images/transaction_received_icon_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/transaction_sent_icon.svg b/app/assets/images/transaction_sent_icon_dark.svg similarity index 100% rename from app/assets/images/transaction_sent_icon.svg rename to app/assets/images/transaction_sent_icon_dark.svg diff --git a/app/assets/images/transaction_sent_icon_light.svg b/app/assets/images/transaction_sent_icon_light.svg new file mode 100644 index 0000000..132c9b0 --- /dev/null +++ b/app/assets/images/transaction_sent_icon_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/transactions_icon.svg b/app/assets/images/transactions_icon.svg deleted file mode 100644 index becaac7..0000000 --- a/app/assets/images/transactions_icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/transactions_icon_dark.svg b/app/assets/images/transactions_icon_dark.svg new file mode 100644 index 0000000..a642a8b --- /dev/null +++ b/app/assets/images/transactions_icon_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/transactions_icon_light.svg b/app/assets/images/transactions_icon_light.svg new file mode 100644 index 0000000..590e3c6 --- /dev/null +++ b/app/assets/images/transactions_icon_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/components/button.js b/app/components/button.js index 6900f13..e6207bf 100644 --- a/app/components/button.js +++ b/app/components/button.js @@ -15,13 +15,13 @@ const DefaultButton = styled.button` cursor: pointer; outline: none; min-width: 100px; - border-radius: 100px; + border-radius: 3px; transition: background-color 0.1s ${props => props.theme.transitionEase}; `; const Primary = styled(DefaultButton)` background-color: ${props => props.theme.colors.primary}; - color: ${props => props.theme.colors.secondary}; + color: ${props => props.theme.colors.buttonPrimaryText}; border: none; &:hover { @@ -29,23 +29,24 @@ const Primary = styled(DefaultButton)` } &:disabled { - background-color: ${props => props.theme.colors.buttonBorderColor}; + background-color: ${props => props.theme.colors.buttonPrimaryDisabledBg}; cursor: not-allowed; - opacity: 0.8; + opacity: 0.45; } `; const Secondary = styled(DefaultButton)` - background-color: transparent; - color: ${props => props.theme.colors.secondary}; - border: 2px solid ${props => props.theme.colors.buttonBorderColor}; + background-color: ${props => props.theme.colors.buttonSecondaryBg}; + color: ${props => props.theme.colors.buttonSecondaryText}; + border: 1px solid ${props => props.theme.colors.buttonSecondaryBorder}; &:hover { - border-color: ${props => props.theme.colors.primary}; + background-color: ${props => props.theme.colors.buttonSecondaryHoveredBg} + border-color: ${props => props.theme.colors.buttonSecondaryHoveredBg} } &:disabled { - background-color: Transparent; + background-color: ${props => props.theme.colors.buttonSecondaryDisabledBg}; cursor: not-allowed; color: ${props => props.theme.colors.buttonBorderColor}; diff --git a/app/components/confirm-dialog.js b/app/components/confirm-dialog.js index cea0baa..7a76b5d 100644 --- a/app/components/confirm-dialog.js +++ b/app/components/confirm-dialog.js @@ -1,6 +1,6 @@ // @flow -import React, { Fragment, type Element } from 'react'; +import React, { type Element } from 'react'; import styled from 'styled-components'; import { TextComponent } from './text'; @@ -17,7 +17,7 @@ const Wrapper = styled.div` flex-direction: column; align-items: center; border-radius: 6px; - box-shadow: 0px 0px 30px 0px black; + box-shadow: ${props => props.theme.colors.transactionDetailsShadow} position: relative; `; @@ -42,6 +42,22 @@ const CloseIconImg = styled.img` cursor: pointer; `; +const ButtonWrapper = styled.div` + display: flex; + flex-direction: row; + flex: 1; + width: 100%; + padding: 20px 40px 10px; + + & > :first-child { + margin-right: 5px; + } + + & > :last-child { + margin-left: 5px; + } +`; + const Btn = styled(Button)` width: 95%; margin-bottom: 10px; @@ -91,7 +107,7 @@ export const ConfirmDialogComponent = ({ {children(handleClose(toggle))} {showButtons && ( - + - + )} )} diff --git a/app/components/copy-to-clipboard.js b/app/components/copy-to-clipboard.js new file mode 100644 index 0000000..b5a7e19 --- /dev/null +++ b/app/components/copy-to-clipboard.js @@ -0,0 +1,61 @@ +// @flow + +import React, { PureComponent } from 'react'; +import copy from 'copy-to-clipboard'; + +type Props = { + text: string, + children: any, + onCopy?: Function, + options?: Object, +} + +export class CopyToClipboard extends PureComponent { + static defaultProps = { + onCopy: () => {}, + options: {}, + }; + + + onClick = (event: Object) => { + const { + text, + onCopy, + children, + options, + } = this.props; + + const elem = React.Children.only(children); + + const result = copy(text, options); + + if (onCopy) { + onCopy(text, result); + } + + // Bypass onClick if it was present + if ( + elem + && elem.props + && typeof elem.props.onClick === 'function' + ) { + elem.props.onClick(event); + } + }; + + render() { + const { + text: _text, + onCopy: _onCopy, + options: _options, + children, + ...props + } = this.props; + const elem = React.Children.only(children); + + return React.cloneElement( + elem, + { ...props, onClick: this.onClick }, + ); + } +} diff --git a/app/components/divider.js b/app/components/divider.js index bb0649f..af60dc1 100644 --- a/app/components/divider.js +++ b/app/components/divider.js @@ -12,7 +12,7 @@ type Props = PropsWithTheme<{ export const Divider = styled.div` width: 100%; height: 1px; - background-color: ${(props: Props) => props.color || props.theme.colors.text}; + background-color: ${(props: Props) => props.color || props.theme.colors.divider}; opacity: ${(props: Props) => String(props.opacity || 1)}; margin-bottom: ${(props: Props) => props.marginBottom || '0'}; margin-top: ${(props: Props) => props.marginTop || '0'}; diff --git a/app/components/dropdown.js b/app/components/dropdown.js index 2621cd4..3c2da2c 100644 --- a/app/components/dropdown.js +++ b/app/components/dropdown.js @@ -21,6 +21,7 @@ const MenuWrapper = styled.div` margin-left: -10px; max-width: 400px; overflow: hidden; + border: 1px solid ${props => props.theme.colors.border} `; const MenuItem = styled.button` diff --git a/app/components/error-modal.js b/app/components/error-modal.js index a6a1678..c42829c 100644 --- a/app/components/error-modal.js +++ b/app/components/error-modal.js @@ -7,7 +7,7 @@ import styled from 'styled-components'; import { TextComponent } from './text'; import { Button } from './button'; -import ErrorIcon from '../assets/images/error_icon.png'; +import ErrorIcon from '../assets/images/error_icon_dark.png'; const ModalWrapper = styled.div` width: 100vw; diff --git a/app/components/header.js b/app/components/header.js index 6c67477..97b23ed 100644 --- a/app/components/header.js +++ b/app/components/header.js @@ -55,6 +55,7 @@ const Title = styled(TextComponent)` text-transform: capitalize; letter-spacing: 0.25px; font-weight: ${props => String(props.theme.fontWeight.bold)}; + color: ${props => props.theme.colors.headerTitle}; `; type Props = { @@ -73,7 +74,7 @@ export const HeaderComponent = ({ title }: Props) => ( <Status type='syncing' progress={0} /> </TitleRow> - <Divider opacity={0.1} /> + <Divider opacity={0.2} /> </TitleWrapper> </Wrapper> ); diff --git a/app/components/input.js b/app/components/input.js index 5fe1dc1..49ed4d1 100644 --- a/app/components/input.js +++ b/app/components/input.js @@ -3,7 +3,7 @@ import React, { type Element } from 'react'; import styled from 'styled-components'; -import theme from '../theme'; +import { appTheme } from '../theme'; type Props = { inputType?: 'input' | 'textarea', @@ -28,17 +28,23 @@ type DefaultStylesProps = PropsWithTheme<{ const getDefaultStyles: ($PropertyType<Props, 'inputType'>) => Element<*> = t => styled[t]` border-radius: ${(props: DefaultStylesProps) => props.theme.boxBorderRadius}; border: none; - background-color: ${(props: DefaultStylesProps) => props.bgColor || props.theme.colors.inputBackground}; + background-color: ${(props: DefaultStylesProps) => props.bgColor || props.theme.colors.inputBg}; color: ${(props: DefaultStylesProps) => props.theme.colors.text}; padding: 15px; padding-right: ${(props: DefaultStylesProps) => (props.withRightElement ? '85px' : '15px')}; width: 100%; outline: none; font-family: ${(props: DefaultStylesProps) => props.theme.fontFamily}; + border: 1px solid ${(props: DefaultStylesProps) => props.theme.colors.inputBorder}; ::placeholder { opacity: 0.5; } + + &:focus, + &:active { + border-color: ${(props: DefaultStylesProps) => props.theme.colors.inputBorderActive}; + } `; const Wrapper = styled.div` @@ -104,5 +110,5 @@ InputComponent.defaultProps = { onChange: () => {}, onFocus: () => {}, step: 1, - bgColor: theme.colors.inputBackground, + bgColor: appTheme.colors.inputBg, }; diff --git a/app/components/layout.js b/app/components/layout.js index 39cf658..4ef57c3 100644 --- a/app/components/layout.js +++ b/app/components/layout.js @@ -14,6 +14,10 @@ const Layout = styled.div` padding-left: ${props => props.theme.layoutPaddingLeft}; padding-right: ${props => props.theme.layoutPaddingRight}; overflow: auto; + + ::-webkit-scrollbar { + display: none; + } `; type Props = { diff --git a/app/components/loading-screen.js b/app/components/loading-screen.js index bfac084..8e50d26 100644 --- a/app/components/loading-screen.js +++ b/app/components/loading-screen.js @@ -9,7 +9,7 @@ import { TextComponent } from './text'; import zcashLogo from '../assets/images/zcash-simple-icon.svg'; -import theme from '../theme'; +import { appTheme } from '../theme'; const Wrapper = styled.div` width: 100vw; @@ -18,7 +18,19 @@ const Wrapper = styled.div` flex-direction: column; align-items: center; justify-content: center; - background-color: ${props => props.theme.colors.cardBackgroundColor}; + background-color: ${props => props.theme.colors.loadingScreenBg}; +`; + +const LoadingCard = styled.div` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background: #000000; + padding: 60px; + min-width: 300px; + min-height: 200px; + border-radius: 3px; `; const CircleWrapper = styled.div` @@ -37,6 +49,10 @@ const Logo = styled.img` left: calc(50% - 25px); `; +const LoadingText = styled(TextComponent)` + color: ${props => props.theme.colors.loadingScreenText}; +`; + type Props = { progress: number, message: string, @@ -86,18 +102,19 @@ export class LoadingScreen extends PureComponent<Props, State> { justifyContent: 'center', }} > - <CircleWrapper> - <Logo src={zcashLogo} alt='Zcash Logo' /> - <CircleProgressComponent - progress={progress} - s // TODO: check if this has any effect - responsive - showPercentage={false} - progressColor={theme.colors.activeItem} - bgColor={theme.colors.inactiveItem} - /> - </CircleWrapper> - <TextComponent value={message} /> + <LoadingCard> + <CircleWrapper> + <Logo src={zcashLogo} alt='Zcash Logo' /> + <CircleProgressComponent + progress={progress} + responsive + showPercentage={false} + progressColor={appTheme.colors.activeItem} + bgColor={appTheme.colors.inactiveItem} + /> + </CircleWrapper> + <LoadingText value={message} /> + </LoadingCard> </animated.div> )} </Transition> diff --git a/app/components/modal.js b/app/components/modal.js index 2e2d1d9..8e4345d 100644 --- a/app/components/modal.js +++ b/app/components/modal.js @@ -14,7 +14,7 @@ const ModalWrapper = styled.div` top: 0; left: 0; z-index: 10; - background-color: rgba(0, 0, 0, 0.5); + background-color: rgba(0, 0, 0, 0.4); `; const ChildrenWrapper = styled.div` @@ -103,7 +103,9 @@ export class ModalComponent extends PureComponent<Props, State> { ) this.close(); }} > - <ChildrenWrapper>{children(toggleVisibility)}</ChildrenWrapper> + <ChildrenWrapper> + {children(toggleVisibility)} + </ChildrenWrapper> </ModalWrapper>, this.element, )} diff --git a/app/components/select.js b/app/components/select.js index 9d0dc65..b63ed0c 100644 --- a/app/components/select.js +++ b/app/components/select.js @@ -1,13 +1,18 @@ // @flow import React, { PureComponent } from 'react'; -import styled from 'styled-components'; +import styled, { withTheme } from 'styled-components'; + +import { DARK, LIGHT } from '../constants/themes'; import { TextComponent } from './text'; -import ChevronUp from '../assets/images/chevron-up.svg'; -import ChevronDown from '../assets/images/chevron-down.svg'; -import theme from '../theme'; +import ChevronUpLight from '../assets/images/chevron_up_icon_light.svg'; +import ChevronUpDark from '../assets/images/chevron_up_icon_dark.svg'; +import ChevronDownLight from '../assets/images/chevron_down_icon_light.svg'; +import ChevronDownDark from '../assets/images/chevron_down_icon_dark.svg'; + +import { appTheme } from '../theme'; /* eslint-disable max-len */ type SelectWrapperProps = PropsWithTheme<{ @@ -21,8 +26,8 @@ const SelectWrapper = styled.div` display: flex; flex-direction: row; border-radius: ${(props: SelectWrapperProps) => props.theme.boxBorderRadius}; - border: none; - background-color: ${(props: SelectWrapperProps) => props.bgColor || props.theme.colors.inputBackground}; + border: 1px solid ${(props: SelectWrapperProps) => props.theme.colors.inputBorder}; + background-color: ${(props: SelectWrapperProps) => props.bgColor || props.theme.colors.inputBg}; color: ${(props: SelectWrapperProps) => props.theme.colors.text}; width: 100%; outline: none; @@ -62,9 +67,14 @@ const SelectMenuButton = styled.button` padding: 3px 7px; outline: none; background-color: transparent; - border: 1px solid - ${(props: PropsWithTheme<{ isOpen: boolean }>) => (props.isOpen ? props.theme.colors.primary : '#29292D')}; border-radius: 100%; + cursor: pointer; + border: 1px solid + ${(props: PropsWithTheme<{ isOpen: boolean }>) => ( + props.isOpen + ? props.theme.colors.dropdownOpenedIconBorder + : props.theme.colors.dropdownIconBorder + )}; `; const Icon = styled.img` @@ -76,24 +86,28 @@ const OptionsWrapper = styled.div` display: flex; flex-direction: column; position: absolute; - width: 100%; - ${(props: PropsWithTheme<{ placement: string, optionsAmount: number }>) => `${String(props.placement)}: ${`-${String((props.optionsAmount || 0) * 40)}px`}`}; + width: 100.5%; + margin-left: -0.25%; + border-radius: ${props => props.theme.colors.boxBorderRadius}; + border: 1px solid ${props => props.theme.colors.inputBorder}; + ${(props: PropsWithTheme<{ placement: string, optionsAmount: number }>) => `${String(props.placement)}: ${`-${String(((props.optionsAmount || 0) * 40) + 10)}px`}`}; overflow-y: auto; + box-shadow: 1px 3px 20px rgba(16, 19, 20, 0.1); `; const Option = styled.button` border: none; background: none; height: 40px; - background-color: #5d5d5d; + background-color: ${props => props.theme.colors.dropdownBg}; cursor: pointer; z-index: 99; text-transform: ${(props: PropsWithTheme<{ capitalize: boolean }>) => (props.capitalize ? 'capitalize' : 'none')}; padding: 5px 10px; - border-bottom: 1px solid #4e4b4b; + border-bottom: 1px solid ${props => props.theme.colors.dropdownBorder}; &:hover { - background-color: ${props => props.theme.colors.background}; + background-color: ${props => props.theme.colors.dropdownHoveredBg}; } &:last-child { @@ -111,13 +125,14 @@ type Props = { placement?: 'top' | 'bottom', bgColor?: string, capitalize?: boolean, + theme: AppTheme, }; type State = { isOpen: boolean, }; -export class SelectComponent extends PureComponent<Props, State> { +class Component extends PureComponent<Props, State> { state = { isOpen: false, }; @@ -125,7 +140,7 @@ export class SelectComponent extends PureComponent<Props, State> { static defaultProps = { placeholder: '', placement: 'bottom', - bgColor: theme.colors.inputBackground, + bgColor: appTheme.colors.inputBg, capitalize: true, }; @@ -151,14 +166,24 @@ export class SelectComponent extends PureComponent<Props, State> { }; getSelectIcon = () => { - const { placement } = this.props; + const { placement, theme } = this.props; const { isOpen } = this.state; - if (placement === 'top') { - return !isOpen ? ChevronUp : ChevronDown; + if (theme.mode === DARK) { + if (placement === 'top') { + return !isOpen ? ChevronUpDark : ChevronDownDark; + } + + return !isOpen ? ChevronDownDark : ChevronUpDark; } - return !isOpen ? ChevronDown : ChevronUp; + if (theme.mode === LIGHT) { + if (placement === 'top') { + return !isOpen ? ChevronUpLight : ChevronDownLight; + } + + return !isOpen ? ChevronDownLight : ChevronUpLight; + } }; render() { @@ -176,7 +201,10 @@ export class SelectComponent extends PureComponent<Props, State> { onClick={() => this.setState(() => ({ isOpen: !isOpen }))} bgColor={bgColor} > - <ValueWrapper hasValue={Boolean(value)} capitalize={capitalize}> + <ValueWrapper + hasValue={Boolean(value)} + capitalize={capitalize} + > {this.getSelectedLabel(value) || placeholder} </ValueWrapper> <SelectMenuButtonWrapper> @@ -207,3 +235,5 @@ export class SelectComponent extends PureComponent<Props, State> { ); } } + +export const SelectComponent = withTheme(Component); diff --git a/app/components/sidebar.js b/app/components/sidebar.js index d852e34..2264273 100644 --- a/app/components/sidebar.js +++ b/app/components/sidebar.js @@ -2,8 +2,9 @@ /* eslint-disable max-len */ import React from 'react'; -import styled from 'styled-components'; +import styled, { withTheme } from 'styled-components'; import type { Location, RouterHistory } from 'react-router-dom'; + import { MENU_OPTIONS } from '../constants/sidebar'; const Wrapper = styled.div` @@ -13,6 +14,7 @@ const Wrapper = styled.div` height: ${props => `calc(100vh - ${props.theme.headerHeight})`}; font-family: ${props => props.theme.fontFamily}; background-color: ${props => props.theme.colors.sidebarBg}; + border-right: 1px solid ${props => props.theme.colors.sidebarBorderRight}; padding-top: 15px; position: relative; `; @@ -20,11 +22,17 @@ const Wrapper = styled.div` /* eslint-disable max-len */ type StyledLinkProps = PropsWithTheme<{ isActive: boolean }>; const StyledLink = styled.a` - color: ${(props: StyledLinkProps) => (props.isActive ? props.theme.colors.sidebarItemActive : props.theme.colors.sidebarItem)}; + color: ${(props: StyledLinkProps) => (props.isActive + ? props.theme.colors.sidebarItemActive + : props.theme.colors.sidebarItem + )}; + background-color: ${(props: StyledLinkProps) => (props.isActive + ? props.theme.colors.sidebarItemHoveredBg + : 'transparent' + )}; font-size: ${(props: StyledLinkProps) => `${props.theme.fontSize.regular}em`}; text-decoration: none; font-weight: ${(props: StyledLinkProps) => String(props.theme.fontWeight.bold)}; - background-color: ${(props: StyledLinkProps) => (props.isActive ? `${props.theme.colors.sidebarHoveredItem}` : 'transparent')}; letter-spacing: 0.25px; padding: 25px 20px; height: 35px; @@ -32,13 +40,19 @@ const StyledLink = styled.a` display: flex; align-items: center; outline: none; - border-right: ${(props: StyledLinkProps) => (props.isActive ? `3px solid ${props.theme.colors.sidebarItemActive(props)}` : 'none')}; cursor: pointer; + outline: none; transition: all 0.03s ${(props: StyledLinkProps) => props.theme.transitionEase}; + border-right: ${(props: StyledLinkProps) => (props.isActive ? `3px solid ${props.theme.colors.sidebarActiveItemBorder}` : 'none')}; + border-top: 1px solid ${(props: StyledLinkProps) => (props.isActive ? props.theme.colors.sidebarBorderRight : 'transparent')}; + border-bottom: 1px solid ${(props: StyledLinkProps) => (props.isActive ? props.theme.colors.sidebarBorderRight : 'transparent')}; &:hover { - color: ${(props: StyledLinkProps) => (props.isActive ? props.theme.colors.sidebarItemActive : '#ddd')} - background-color: ${(props: StyledLinkProps) => props.theme.colors.sidebarHoveredItem}; + border-top: 1px solid ${props => props.theme.colors.sidebarBorderRight}; + border-bottom: 1px solid ${props => props.theme.colors.sidebarBorderRight}; + + background-color: ${(props: StyledLinkProps) => props.theme.colors.sidebarItemHoveredBg}; + color: ${(props: StyledLinkProps) => (props.isActive ? props.theme.colors.sidebarItemActive : props.theme.colors.sidebarItemHovered)} } `; @@ -47,24 +61,29 @@ const Icon = styled.img` height: 17px; margin-right: 13px; + opacity: ${(props: any) => (props.isActive ? '1' : '0.3')}; + ${StyledLink}:hover & { - filter: ${(props: StyledLinkProps) => (props.isActive ? 'none' : 'brightness(300%)')}; + opacity: 1; } `; type MenuItem = { route: string, label: string, - icon: (isActive: boolean) => string, + icon: (isActive: boolean, themeMode: string) => string, }; type Props = { history: RouterHistory, options?: MenuItem[], location: Location, + theme: AppTheme, }; -export const SidebarComponent = ({ options, location, history }: Props) => ( +export const Component = ({ + options, location, history, theme, +}: Props) => ( <Wrapper id='sidebar'> {(options || []).map((item) => { const isActive = location.pathname === item.route; @@ -75,7 +94,11 @@ export const SidebarComponent = ({ options, location, history }: Props) => ( key={item.route} onClick={() => (isActive ? {} : history.push(item.route))} > - <Icon isActive={isActive} src={item.icon(isActive)} Alt={`${item.route}`} /> + <Icon + isActive={isActive} + src={item.icon(isActive, theme.mode)} + Alt={`${item.route}`} + /> {item.label} </StyledLink> ); @@ -83,6 +106,8 @@ export const SidebarComponent = ({ options, location, history }: Props) => ( </Wrapper> ); -SidebarComponent.defaultProps = { +Component.defaultProps = { options: MENU_OPTIONS, }; + +export const SidebarComponent = withTheme(Component); diff --git a/app/components/status-pill.js b/app/components/status-pill.js index e888a61..33842cc 100644 --- a/app/components/status-pill.js +++ b/app/components/status-pill.js @@ -1,16 +1,20 @@ // @flow -import React, { Component } from 'react'; -import styled, { keyframes } from 'styled-components'; +import React, { PureComponent } from 'react'; +import styled, { keyframes, withTheme } from 'styled-components'; import eres from 'eres'; import { TextComponent } from './text'; import rpc from '../../services/api'; +import { DARK } from '../constants/themes'; -import readyIcon from '../assets/images/green_check.png'; -import syncIcon from '../assets/images/sync_icon.png'; -import errorIcon from '../assets/images/error_icon.png'; +import readyIconDark from '../assets/images/green_check_dark.png'; +import readyIconLight from '../assets/images/green_check_light.png'; +import syncIconDark from '../assets/images/sync_icon_dark.png'; +import syncIconLight from '../assets/images/sync_icon_light.png'; +import errorIconDark from '../assets/images/error_icon_dark.png'; +import errorIconLight from '../assets/images/error_icon_light.png'; const rotate = keyframes` from { @@ -24,7 +28,8 @@ const rotate = keyframes` const Wrapper = styled.div` align-items: center; display: flex; - background-color: #000; + background: ${props => props.theme.colors.statusPillBg}; + border: 1px solid ${props => props.theme.colors.statusPillBorder} border-radius: 27px; padding: 8px 16px; `; @@ -48,7 +53,9 @@ const StatusPillLabel = styled(TextComponent)` user-select: none; `; -type Props = {}; +type Props = { + theme: AppTheme, +}; type State = { type: string, @@ -57,15 +64,25 @@ type State = { isSyncing: boolean, }; -export class StatusPill extends Component<Props, State> { +class Component extends PureComponent<Props, State> { timer: ?IntervalID = null; - state = { - type: 'syncing', - icon: syncIcon, - progress: 0, - isSyncing: true, - }; + constructor(props: Props) { + super(props); + + const { theme } = props; + + const syncIcon = theme.mode === DARK + ? syncIconDark + : syncIconLight; + + this.state = { + type: 'syncing', + icon: syncIcon, + progress: 0, + isSyncing: true, + }; + } componentDidMount() { this.timer = setInterval(() => { @@ -81,6 +98,15 @@ export class StatusPill extends Component<Props, State> { } getBlockchainStatus = async () => { + const { theme } = this.props; + + const readyIcon = theme.mode === DARK + ? readyIconDark + : readyIconLight; + const errorIcon = theme.mode === DARK + ? errorIconDark + : errorIconLight; + const [blockchainErr, blockchaininfo] = await eres(rpc.getblockchaininfo()); if (blockchainErr || !blockchaininfo) return; @@ -99,7 +125,10 @@ export class StatusPill extends Component<Props, State> { }); if (blockchainErr) { - this.setState(() => ({ type: 'error', icon: errorIcon })); + this.setState(() => ({ + type: 'error', + icon: errorIcon, + })); } }; @@ -118,3 +147,5 @@ export class StatusPill extends Component<Props, State> { ); } } + +export const StatusPill = withTheme(Component); diff --git a/app/components/text.js b/app/components/text.js index 30fe4a4..66f34ec 100644 --- a/app/components/text.js +++ b/app/components/text.js @@ -5,7 +5,7 @@ import React from 'react'; import styled from 'styled-components'; import type { ElementProps } from 'react'; -import theme from '../theme'; +import { appTheme } from '../theme'; export type Props = { ...ElementProps<'p'>, @@ -15,6 +15,8 @@ export type Props = { className?: string, size?: string | number, align?: string, + onClick?: Function, + onDoubleClick?: Function, }; const Text = styled.p` @@ -28,7 +30,8 @@ const Text = styled.p` `; export const TextComponent = ({ - value, isBold, color, className, size, align, id, + value, isBold, color, className, size, + align, id, onClick, onDoubleClick, }: Props) => ( <Text id={id} @@ -37,6 +40,8 @@ export const TextComponent = ({ color={color} size={`${String(size)}em`} align={align} + onClick={onClick} + onDoubleClick={onDoubleClick} > {value} </Text> @@ -45,7 +50,9 @@ export const TextComponent = ({ TextComponent.defaultProps = { className: '', isBold: false, - color: theme.colors.text, - size: theme.fontSize.regular, + color: appTheme.colors.text, + size: appTheme.fontSize.regular, align: 'left', + onClick: () => {}, + onDoubleClick: () => {}, }; diff --git a/app/components/transaction-daily.js b/app/components/transaction-daily.js index 1433134..b2d13dd 100644 --- a/app/components/transaction-daily.js +++ b/app/components/transaction-daily.js @@ -5,7 +5,6 @@ import styled from 'styled-components'; import { TransactionItemComponent, type Transaction } from './transaction-item'; import { TextComponent } from './text'; -import { Divider } from './divider'; const Wrapper = styled.div` margin-top: 20px; @@ -41,7 +40,7 @@ export const TransactionDailyComponent = ({ transactionsDate, transactions, zecP <TransactionsWrapper> {transactions.map(({ date, type, address, amount, transactionId, - }, idx) => ( + }) => ( <Fragment key={`${address}-${type}-${amount}-${date}`}> <TransactionItemComponent transactionId={transactionId} @@ -51,7 +50,6 @@ export const TransactionDailyComponent = ({ transactionsDate, transactions, zecP amount={amount} zecPrice={zecPrice} /> - {idx < transactions.length - 1 && <Divider />} </Fragment> ))} </TransactionsWrapper> diff --git a/app/components/transaction-details.js b/app/components/transaction-details.js index aff6d21..777109c 100644 --- a/app/components/transaction-details.js +++ b/app/components/transaction-details.js @@ -1,21 +1,24 @@ // @flow import React from 'react'; -import styled from 'styled-components'; +import styled, { withTheme } from 'styled-components'; import dateFns from 'date-fns'; import { BigNumber } from 'bignumber.js'; import { ZCASH_EXPLORER_BASE_URL } from '../constants/explorer'; +import { DARK } from '../constants/themes'; -import SentIcon from '../assets/images/transaction_sent_icon.svg'; -import ReceivedIcon from '../assets/images/transaction_received_icon.svg'; +import SentIconDark from '../assets/images/transaction_sent_icon_dark.svg'; +import ReceivedIconDark from '../assets/images/transaction_received_icon_dark.svg'; +import SentIconLight from '../assets/images/transaction_sent_icon_light.svg'; +import ReceivedIconLight from '../assets/images/transaction_received_icon_light.svg'; import CloseIcon from '../assets/images/close_icon.svg'; import { TextComponent } from './text'; import { RowComponent } from './row'; import { ColumnComponent } from './column'; -import theme from '../theme'; +import { appTheme } from '../theme'; import { formatNumber } from '../utils/format-number'; import { truncateAddress } from '../utils/truncate-address'; @@ -23,12 +26,12 @@ import { openExternal } from '../utils/open-external'; const Wrapper = styled.div` width: 460px; - background-color: ${props => props.theme.colors.background}; + background-color: ${props => props.theme.colors.transactionDetailsBg}; display: flex; flex-direction: column; align-items: center; border-radius: 6px; - box-shadow: 0px 0px 30px 0px black; + box-shadow: ${props => props.theme.colors.transactionDetailsShadow}; position: relative; `; @@ -71,7 +74,7 @@ const InfoRow = styled(RowComponent)` } &:hover { - background: #1d1d1d; + background: ${props => props.theme.colors.transactionDetailsRowHover}; } `; @@ -85,14 +88,14 @@ const DetailsWrapper = styled.div` const Divider = styled.div` width: 100%; - background-color: #3a3a3a; + background-color: ${props => props.theme.colors.transactionDetailsDivider}; height: 1px; opacity: 0.5; `; const Label = styled(TextComponent)` font-weight: ${props => String(props.theme.fontWeight.bold)}; - color: ${props => props.theme.colors.transactionsDetailsLabel}; + color: ${props => props.theme.colors.transactionDetailsLabel}; margin-bottom: 5px; letter-spacing: 0.25px; `; @@ -127,9 +130,10 @@ type Props = { from: string, to: string, handleClose: () => void, + theme: AppTheme, }; -export const TransactionDetailsComponent = ({ +const Component = ({ amount, type, zecPrice, @@ -138,19 +142,32 @@ export const TransactionDetailsComponent = ({ from, to, handleClose, + theme, }: Props) => { const isReceived = type === 'receive'; + const receivedIcon = theme.mode === DARK + ? ReceivedIconDark + : ReceivedIconLight; + const sentIcon = theme.mode === DARK + ? SentIconDark + : SentIconLight; return ( <Wrapper> <CloseIconWrapper> - <CloseIconImg src={CloseIcon} onClick={handleClose} /> + <CloseIconImg + src={CloseIcon} + onClick={handleClose} + /> </CloseIconWrapper> <TitleWrapper> <TextComponent value='Transaction Details' align='center' /> </TitleWrapper> <DetailsWrapper> - <Icon src={isReceived ? ReceivedIcon : SentIcon} alt='Transaction Type Icon' /> + <Icon + src={isReceived ? receivedIcon : sentIcon} + alt='Transaction Type Icon' + /> <TextComponent isBold size={2.625} @@ -158,7 +175,7 @@ export const TransactionDetailsComponent = ({ append: `${isReceived ? '+' : '-'}ZEC `, value: amount, })} - color={isReceived ? theme.colors.transactionReceived : theme.colors.transactionSent} + color={isReceived ? appTheme.colors.transactionReceived : appTheme.colors.transactionSent} /> <TextComponent value={formatNumber({ @@ -166,7 +183,7 @@ export const TransactionDetailsComponent = ({ value: new BigNumber(amount).times(zecPrice).toNumber(), })} size={1.5} - color={theme.colors.transactionsDetailsLabel} + color={appTheme.colors.transactionDetailsLabel} /> </DetailsWrapper> <InfoRow> @@ -175,7 +192,7 @@ export const TransactionDetailsComponent = ({ <TextComponent value={dateFns.format(new Date(date), 'MMMM D, YYYY HH:MMA')} /> </ColumnComponent> <ColumnComponent> - <TextComponent value='FEES' isBold color={theme.colors.transactionsDetailsLabel} /> + <TextComponent value='FEES' isBold color={appTheme.colors.transactionDetailsLabel} /> <TextComponent value={formatNumber({ value: new BigNumber(amount).times(0.1).toNumber(), @@ -203,16 +220,18 @@ export const TransactionDetailsComponent = ({ <InfoRow> <ColumnComponent width='100%'> <Label value='FROM' /> - <Ellipsis value={truncateAddress(from)} /> + <Ellipsis value={from} /> </ColumnComponent> </InfoRow> <Divider /> <InfoRow> <ColumnComponent width='100%'> <Label value='TO' /> - <Ellipsis value={truncateAddress(to)} /> + <Ellipsis value={truncateAddress(to) || 'N/A'} /> </ColumnComponent> </InfoRow> </Wrapper> ); }; + +export const TransactionDetailsComponent = withTheme(Component); diff --git a/app/components/transaction-item.js b/app/components/transaction-item.js index 8f946f4..2052120 100644 --- a/app/components/transaction-item.js +++ b/app/components/transaction-item.js @@ -1,11 +1,15 @@ // @flow import React from 'react'; -import styled from 'styled-components'; +import styled, { withTheme } from 'styled-components'; import dateFns from 'date-fns'; -import SentIcon from '../assets/images/transaction_sent_icon.svg'; -import ReceivedIcon from '../assets/images/transaction_received_icon.svg'; +import { DARK } from '../constants/themes'; + +import SentIconDark from '../assets/images/transaction_sent_icon_dark.svg'; +import ReceivedIconDark from '../assets/images/transaction_received_icon_dark.svg'; +import SentIconLight from '../assets/images/transaction_sent_icon_light.svg'; +import ReceivedIconLight from '../assets/images/transaction_received_icon_light.svg'; import { RowComponent } from './row'; import { ColumnComponent } from './column'; @@ -13,18 +17,22 @@ import { TextComponent } from './text'; import { ModalComponent } from './modal'; import { TransactionDetailsComponent } from './transaction-details'; -import theme from '../theme'; - import { formatNumber } from '../utils/format-number'; import { truncateAddress } from '../utils/truncate-address'; const Wrapper = styled(RowComponent)` - background-color: ${props => props.theme.colors.cardBackgroundColor}; + background-color: ${props => props.theme.colors.transactionItemBg}; padding: 15px 17px; cursor: pointer; + border: 1px solid ${props => props.theme.colors.transactionItemBorder}; + border-bottom: none; + + &:last-child { + border-bottom: 1px solid ${props => props.theme.colors.transactionItemBorder}; + } &:hover { - background-color: #0a0a0a; + background-color: ${props => props.theme.colors.transactionItemHoverBg}; } `; @@ -41,10 +49,10 @@ const TransactionTypeLabel = styled(TextComponent)` /* eslint-enable max-len */ const TransactionAddress = styled(TextComponent)` - color: #a7a7a7; + color: ${props => props.theme.colors.transactionItemAddress}; ${String(Wrapper)}:hover & { - color: #fff; + color: ${props => props.theme.colors.transactionItemAddressHover}; } `; @@ -65,15 +73,17 @@ export type Transaction = { amount: number, zecPrice: number, transactionId: string, + theme: AppTheme, }; -export const TransactionItemComponent = ({ +const Component = ({ type, date, address, amount, zecPrice, transactionId, + theme, }: Transaction) => { const isReceived = type === 'receive'; const transactionTime = dateFns.format(new Date(date), 'HH:mm A'); @@ -87,6 +97,13 @@ export const TransactionItemComponent = ({ }); const transactionAddress = truncateAddress(address); + const receivedIcon = theme.mode === DARK + ? ReceivedIconDark + : ReceivedIconLight; + const sentIcon = theme.mode === DARK + ? SentIconDark + : SentIconLight; + return ( <ModalComponent renderTrigger={toggleVisibility => ( @@ -98,7 +115,7 @@ export const TransactionItemComponent = ({ > <RowComponent alignItems='center'> <RowComponent alignItems='center'> - <Icon src={isReceived ? ReceivedIcon : SentIcon} alt='Transaction Type Icon' /> + <Icon src={isReceived ? receivedIcon : sentIcon} alt='Transaction Type Icon' /> <TransactionColumn> <TransactionTypeLabel isReceived={isReceived} value={type} isBold /> <TransactionTime value={transactionTime} /> @@ -110,7 +127,10 @@ export const TransactionItemComponent = ({ <TextComponent isBold value={transactionValueInZec} - color={isReceived ? theme.colors.transactionReceived : theme.colors.transactionSent} + color={isReceived + ? theme.colors.transactionReceived + : theme.colors.transactionSent + } /> <TextComponent value={transactionValueInUsd} color={theme.colors.inactiveItem} /> </ColumnComponent> @@ -132,3 +152,5 @@ export const TransactionItemComponent = ({ </ModalComponent> ); }; + +export const TransactionItemComponent = withTheme(Component); diff --git a/app/components/wallet-address.js b/app/components/wallet-address.js index 3d2a4fd..298c06d 100644 --- a/app/components/wallet-address.js +++ b/app/components/wallet-address.js @@ -1,94 +1,193 @@ // @flow -import React, { Component } from 'react'; -import styled from 'styled-components'; +import React, { PureComponent } from 'react'; +import styled, { withTheme } from 'styled-components'; import { ColumnComponent } from './column'; -import { Button } from './button'; +import { TextComponent } from './text'; import { QRCode } from './qrcode'; +import { CopyToClipboard } from './copy-to-clipboard'; -import { truncateAddress } from '../utils/truncate-address'; - -import eyeIcon from '../assets/images/eye.png'; +import CopyIconDark from '../assets/images/copy_icon_dark.svg'; +import CopyIconLight from '../assets/images/copy_icon_light.svg'; +import ScanIconDark from '../assets/images/scan_icon_dark.svg'; +import ScanIconLight from '../assets/images/scan_icon_light.svg'; +import { DARK } from '../constants/themes'; const AddressWrapper = styled.div` align-items: center; display: flex; - background-color: #000; - border-radius: 6px; - padding: 7px 13px; + border-radius: ${props => props.theme.boxBorderRadius}; + padding: 0 13px 0 0; margin-bottom: 10px; width: 100%; + background: ${props => props.theme.colors.walletAddressBg}; + border: 1px solid ${props => props.theme.colors.walletAddressBorder}; `; -const Input = styled.input` +const Address = styled(TextComponent)` border-radius: ${props => props.theme.boxBorderRadius}; border: none; - background-color: ${props => props.theme.colors.inputBackground}; - color: ${props => props.theme.colors.text}; + background-color: ${props => props.theme.colors.inputBg}; padding: 15px; - width: 100%; + width: 92%; outline: none; font-family: ${props => props.theme.fontFamily}; + font-size: 13px; + color: ${props => props.theme.colors.walletAddressInput}; + line-height: 1; + letter-spacing: 0.5px; + overflow-x: scroll; + cursor: pointer; + user-select: text; + + ::-webkit-scrollbar { + display: none; + } + + /* // todo: make this theme supported */ + ${AddressWrapper}:hover & { + color: ${props => props.theme.colors.walletAddressInputHovered}; + } ::placeholder { opacity: 0.5; } `; -const QRCodeWrapper = styled.div` +const QRCodeContainer = styled.div` align-items: center; display: flex; - background-color: #000; - border-radius: 6px; + background-color: ${props => props.theme.colors.qrCodeWrapperBg} + border: 1px solid ${props => props.theme.colors.qrCodeWrapperBorder} + border-radius: 3px; padding: 20px; margin-bottom: 10px; width: 100%; `; +const QRCodeWrapper = styled.div` + background-color: #FFFFFF; + padding: 10px; +`; + +const IconButton = styled.button` + background: transparent; + cursor: pointer; + outline: none; + border: none; + display: flex; + width: 28px; + margin-left: 3px; + position: relative; +`; + +const IconImage = styled.img` + max-width: 23px; + opacity: 0.4; + + ${IconButton}:hover & { + opacity: 1; + } +`; + +const CopyTooltip = styled.div` + background: ${props => props.theme.colors.walletAddressTooltipBg}; + position: absolute; + top: -27px; + left: -8px; + padding: 6px 10px; + border-radius: 3px; +`; + +const TooltipText = styled(TextComponent)` + color: ${props => props.theme.colors.walletAddressTooltip}; + font-size: 10px; + font-weight: 700; +`; + type Props = { address: string, + theme: AppTheme, }; type State = { - isVisible: boolean, + showCopiedTooltip: boolean, + showQRCode: boolean, }; -export class WalletAddress extends Component<Props, State> { +class Component extends PureComponent<Props, State> { state = { - isVisible: false, + showQRCode: false, + showCopiedTooltip: false, }; - show = () => this.setState(() => ({ isVisible: true })); + showMoreInfo = () => this.setState(() => ({ showQRCode: true })); - hide = () => this.setState(() => ({ isVisible: false })); + toggleMoreInfo = () => this.setState(prevState => ({ + showQRCode: !prevState.showQRCode, + })); + + hideTooltip = () => this.setState(() => ({ showCopiedTooltip: false })); + + copyAddress = () => this.setState( + () => ({ showCopiedTooltip: true }), + () => setTimeout(() => this.hideTooltip(), 1500), + ); render() { - const { address } = this.props; - const { isVisible } = this.state; - const toggleVisibility = isVisible ? this.hide : this.show; + const { address, theme } = this.props; + const { showQRCode, showCopiedTooltip } = this.state; + + const qrCodeIcon = theme.mode === DARK + ? ScanIconDark + : ScanIconLight; + + const copyIcon = theme.mode === DARK + ? CopyIconDark + : CopyIconLight; return ( <ColumnComponent width='100%'> <AddressWrapper> - <Input - value={isVisible ? address : truncateAddress(address)} - onChange={() => {}} - onFocus={event => event.currentTarget.select()} - /> - <Button - icon={eyeIcon} - label={`${isVisible ? 'Hide' : 'Show'} full address and QR Code`} - onClick={toggleVisibility} - variant='secondary' + <Address + value={address} + onClick={this.toggleMoreInfo} + onDoubleClick={this.showMoreInfo} /> + <CopyToClipboard + text={address} + onCopy={this.copyAddress} + > + <IconButton onClick={() => {}}> + {!showCopiedTooltip ? null : ( + <CopyTooltip> + <TooltipText value='Copied!' /> + </CopyTooltip> + )} + <IconImage + src={copyIcon} + alt='Copy Address' + /> + </IconButton> + </CopyToClipboard> + <IconButton onClick={this.toggleMoreInfo}> + <IconImage + src={qrCodeIcon} + alt='See QRCode' + /> + </IconButton> </AddressWrapper> - {!isVisible ? null : ( - <QRCodeWrapper> - <QRCode value={address} /> - </QRCodeWrapper> + {!showQRCode ? null : ( + <QRCodeContainer> + <QRCodeWrapper> + <QRCode value={address} /> + </QRCodeWrapper> + </QRCodeContainer> )} </ColumnComponent> ); } } + +export const WalletAddress = withTheme(Component); diff --git a/app/components/wallet-summary.js b/app/components/wallet-summary.js index 199bbac..63acad6 100644 --- a/app/components/wallet-summary.js +++ b/app/components/wallet-summary.js @@ -8,12 +8,13 @@ import { RowComponent } from './row'; import { formatNumber } from '../utils/format-number'; -import theme from '../theme'; +import { appTheme } from '../theme'; const Wrapper = styled.div` display: flex; flex-direction: column; - background-color: ${props => props.theme.colors.cardBackgroundColor}; + background-color: ${props => props.theme.colors.walletSummaryBg}; + border: 1px solid ${props => props.theme.colors.walletSummaryBorder}; border-radius: ${props => props.theme.boxBorderRadius}; padding: 37px 45px; min-height: 250px; @@ -60,31 +61,31 @@ export const WalletSummaryComponent = ({ <AllAddresses value='ALL ADDRESSES' isBold /> <ValueBox> <TextComponent - size={theme.fontSize.medium * 2.5} + size={appTheme.fontSize.medium * 2.5} value={`ZEC ${formatNumber({ value: total })}`} isBold /> <USDValue value={`USD $${formatNumber({ value: total * zecPrice })}`} - size={theme.fontSize.medium * 2} + size={appTheme.fontSize.medium * 2} /> </ValueBox> <RowComponent> <ValueBox> - <ShieldedValue value='● SHIELDED' isBold size={theme.fontSize.small} /> + <ShieldedValue value='● SHIELDED' isBold size={appTheme.fontSize.small} /> <TextComponent value={`ZEC ${formatNumber({ value: shielded })}`} isBold - size={theme.fontSize.medium} + size={appTheme.fontSize.medium} /> <USDValue value={`USD $${formatNumber({ value: shielded * zecPrice })}`} /> </ValueBox> <ValueBox> - <Label value='● TRANSPARENT' isBold size={theme.fontSize.small} /> + <Label value='● TRANSPARENT' isBold size={appTheme.fontSize.small} /> <TextComponent value={`ZEC ${formatNumber({ value: transparent })}`} isBold - size={theme.fontSize.medium} + size={appTheme.fontSize.medium} /> <USDValue value={`USD $${formatNumber({ value: transparent * zecPrice })}`} /> </ValueBox> diff --git a/app/constants/sidebar.js b/app/constants/sidebar.js index 45868c8..137051e 100644 --- a/app/constants/sidebar.js +++ b/app/constants/sidebar.js @@ -1,16 +1,22 @@ // @flow -import DashboardIcon from '../assets/images/dashboard_icon.svg'; +import DashboardIconDark from '../assets/images/dashboard_icon_dark.svg'; +import DashboardIconLight from '../assets/images/dashboard_icon_light.svg'; import DashboardIconActive from '../assets/images/dashboard_icon_active.svg'; -import ConsoleIcon from '../assets/images/console_icon.svg'; +import ConsoleIconDark from '../assets/images/console_icon_dark.svg'; +import ConsoleIconLight from '../assets/images/console_icon_light.svg'; import ConsoleIconActive from '../assets/images/console_icon_active.svg'; -import SendIcon from '../assets/images/send_icon.svg'; +import SendIconDark from '../assets/images/send_icon_dark.svg'; +import SendIconLight from '../assets/images/send_icon_light.svg'; import SendIconActive from '../assets/images/send_icon_active.svg'; -import ReceiveIcon from '../assets/images/receive_icon.svg'; +import ReceiveIconDark from '../assets/images/receive_icon_dark.svg'; +import ReceiveIconLight from '../assets/images/receive_icon_light.svg'; import ReceiveIconActive from '../assets/images/receive_icon_active.svg'; -import TransactionsIcon from '../assets/images/transactions_icon.svg'; +import TransactionsIconDark from '../assets/images/transactions_icon_dark.svg'; +import TransactionsIconLight from '../assets/images/transactions_icon_light.svg'; import TransactionsIconActive from '../assets/images/transactions_icon_active.svg'; -import SettingsIcon from '../assets/images/settings_icon.svg'; +import SettingsIconDark from '../assets/images/settings_icon_dark.svg'; +import SettingsIconLight from '../assets/images/settings_icon_light.svg'; import SettingsIconActive from '../assets/images/settings_icon_active.svg'; import { @@ -21,36 +27,73 @@ import { CONSOLE_ROUTE, TRANSACTIONS_ROUTE, } from './routes'; +import { LIGHT } from './themes'; export const MENU_OPTIONS = [ { label: 'Dashboard', route: DASHBOARD_ROUTE, - icon: (isActive: boolean) => (isActive ? DashboardIconActive : DashboardIcon), + icon: (isActive: boolean, themeMode: string) => { + if (themeMode === LIGHT) { + return DashboardIconLight; + } + + return (isActive) ? DashboardIconActive : DashboardIconDark; + }, }, { label: 'Send', route: SEND_ROUTE, - icon: (isActive: boolean) => (isActive ? SendIconActive : SendIcon), + icon: (isActive: boolean, themeMode: string) => { + if (themeMode === LIGHT) { + return SendIconLight; + } + + return (isActive) ? SendIconActive : SendIconDark; + }, }, { label: 'Receive', route: RECEIVE_ROUTE, - icon: (isActive: boolean) => (isActive ? ReceiveIconActive : ReceiveIcon), + icon: (isActive: boolean, themeMode: string) => { + if (themeMode === LIGHT) { + return ReceiveIconLight; + } + + return (isActive) ? ReceiveIconActive : ReceiveIconDark; + }, }, { label: 'Transactions', route: TRANSACTIONS_ROUTE, - icon: (isActive: boolean) => (isActive ? TransactionsIconActive : TransactionsIcon), + icon: (isActive: boolean, themeMode: string) => { + if (themeMode === LIGHT) { + return TransactionsIconLight; + } + + return (isActive) ? TransactionsIconActive : TransactionsIconDark; + }, }, { label: 'Settings', route: SETTINGS_ROUTE, - icon: (isActive: boolean) => (isActive ? SettingsIconActive : SettingsIcon), + icon: (isActive: boolean, themeMode: string) => { + if (themeMode === LIGHT) { + return SettingsIconLight; + } + + return (isActive) ? SettingsIconActive : SettingsIconDark; + }, }, { label: 'Console', route: CONSOLE_ROUTE, - icon: (isActive: boolean) => (isActive ? ConsoleIconActive : ConsoleIcon), + icon: (isActive: boolean, themeMode: string) => { + if (themeMode === LIGHT) { + return ConsoleIconLight; + } + + return (isActive) ? ConsoleIconActive : ConsoleIconDark; + }, }, ]; diff --git a/app/constants/themes.js b/app/constants/themes.js index 34bbef4..86f736d 100644 --- a/app/constants/themes.js +++ b/app/constants/themes.js @@ -1,4 +1,8 @@ // @flow -export const LIGHT = 'light'; -export const DARK = 'dark'; +// Theme Modifier +export const THEME_MODE = 'THEME_MODE'; + +// Themes +export const LIGHT = 'LIGHT'; +export const DARK = 'DARK'; diff --git a/app/theme.js b/app/theme.js deleted file mode 100644 index a540ac8..0000000 --- a/app/theme.js +++ /dev/null @@ -1,176 +0,0 @@ -// @flow - -import React, { Fragment, type Node } from 'react'; -import theme from 'styled-theming'; -import { ThemeProvider, createGlobalStyle } from 'styled-components'; -// $FlowFixMe -import { normalize } from 'polished'; // eslint-disable-line - -import { DARK } from './constants/themes'; - -const darkOne = '#F4B728'; -const blackTwo = '#171717'; -const lightOne = '#ffffff'; -const brandOne = '#000'; -const brandThree = '#5d5d65'; -const buttonBorderColor = '#3e3c42'; -const activeItem = '#F4B728'; -const text = '#FFF'; -const cardBackgroundColor = '#000'; -const sidebarLogoGradientBegin = '#F4B728'; -const sidebarLogoGradientEnd = '#FFE240'; -const sidebarHoveredItem = '#1C1C1C'; -const sidebarHoveredItemLabel = '#8e8e96'; -const background = '#212124'; -const transactionSent = '#FF6C6C'; -const transactionReceived = '#6AEAC0'; -const transactionsDate = '#777777'; -const transactionsItemHovered = '#222222'; -const selectButtonShadow = 'rgba(238,201,76,0.65)'; -const statusPillLabel = '#727272'; -const transactionsDetailsLabel = transactionsDate; - -const appTheme: AppTheme = { - mode: DARK, - fontFamily: 'Roboto', - fontWeight: { - bold: 700, - default: 400, - light: 300, - }, - fontSize: { - large: 1.25, - medium: 1.125, - regular: 0.84375, - small: 0.667, - }, - colors: { - primary: theme('mode', { - light: lightOne, - dark: darkOne, - }), - secondary: theme('mode', { - light: darkOne, - dark: lightOne, - }), - sidebarBg: theme('mode', { - light: brandOne, - dark: brandOne, - }), - sidebarItem: theme('mode', { - light: brandThree, - dark: brandThree, - }), - sidebarItemActive: theme('mode', { - light: activeItem, - dark: activeItem, - }), - sidebarHoveredItem: theme('mode', { - light: sidebarHoveredItem, - dark: sidebarHoveredItem, - }), - sidebarHoveredItemLabel: theme('mode', { - light: sidebarHoveredItemLabel, - dark: sidebarHoveredItemLabel, - }), - cardBackgroundColor: theme('mode', { - light: cardBackgroundColor, - dark: cardBackgroundColor, - }), - text: theme('mode', { - light: '#000', - dark: text, - }), - activeItem: theme('mode', { - light: activeItem, - dark: activeItem, - }), - inactiveItem: theme('mode', { - light: brandThree, - dark: brandThree, - }), - sidebarLogoGradientBegin: theme('mode', { - light: sidebarLogoGradientBegin, - dark: sidebarLogoGradientBegin, - }), - sidebarLogoGradientEnd: theme('mode', { - light: sidebarLogoGradientEnd, - dark: sidebarLogoGradientEnd, - }), - background: theme('mode', { - light: '#FFF', - dark: background, - }), - transactionSent: theme('mode', { - light: transactionSent, - dark: transactionSent, - }), - transactionReceived: theme('mode', { - light: transactionReceived, - dark: transactionReceived, - }), - transactionsDate: theme('mode', { - light: transactionsDate, - dark: transactionsDate, - }), - transactionsItemHovered: theme('mode', { - light: transactionsItemHovered, - dark: transactionsItemHovered, - }), - inputBackground: theme('mode', { - light: brandOne, - dark: brandOne, - }), - selectButtonShadow: theme('mode', { - light: selectButtonShadow, - dark: selectButtonShadow, - }), - transactionsDetailsLabel: theme('mode', { - light: transactionsDetailsLabel, - dark: transactionsDetailsLabel, - }), - statusPillLabel: theme('mode', { - light: statusPillLabel, - dark: statusPillLabel, - }), - modalItemLabel: theme('mode', { - light: transactionsDate, - dark: transactionsDate, - }), - blackTwo: theme('mode', { - light: blackTwo, - dark: blackTwo, - }), - buttonBorderColor: theme('mode', { - light: buttonBorderColor, - dark: buttonBorderColor, - }), - }, - sidebarWidth: '180px', - headerHeight: '60px', - layoutPaddingLeft: '35px', - layoutPaddingRight: '35px', - layoutContentPaddingTop: '20px', - boxBorderRadius: '3px', - transitionEase: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)', -}; - -export const GlobalStyle = createGlobalStyle` - ${normalize()} - - * { - box-sizing: border-box; - } -`; - -export const DoczWrapper = ({ children }: { children: () => Node<*> }) => ( - <ThemeProvider theme={appTheme}> - <Fragment> - <GlobalStyle /> - {children()} - </Fragment> - </ThemeProvider> -); - -// eslint-disable-next-line -export default appTheme; diff --git a/app/theme/colors/dark.js b/app/theme/colors/dark.js new file mode 100644 index 0000000..965d8c7 --- /dev/null +++ b/app/theme/colors/dark.js @@ -0,0 +1,123 @@ +// @flow + +const white = '#FFFFFF'; +const text = white; +const brand = '#F4B728'; +const brand2 = '#FFE240'; +const black = '#000000'; +const error = '#FF6C6C'; +const success = '#6AEAC0'; +const darkBrand = '#212124'; +const brandThree = '#5d5d65'; + +export const DARK_COLORS = { + darkOne: brand, + blackTwo: '#171717', + lightOne: text, + brandOne: black, + brandThree: '#5d5d65', + buttonBorderColor: '#3e3c42', + activeItem: brand, + text, + background: '#212124', + + // Dropdown + dropdownBg: '#5d5d5d', + dropdownHoveredBg: '#212124', + dropdownBorder: '#4e4b4b', + dropdownIconBorder: '#828282', + dropdownOpenedIconBorder: '#ddd', + + // Card + cardBackgroundColor: black, + sendCardBg: black, + sendCardBorder: 'transparent', + + // Buttons + buttonPrimaryBg: brand, + buttonPrimaryDisabledBg: brand, + buttonPrimaryText: black, + buttonSecondaryBg: '#ddd', + buttonSecondaryDisabledBg: brand, + buttonSecondaryText: black, + buttonSecondaryBorder: '#ddd', + buttonSecondaryHoveredBg: '#bbb', + + // Transactions + transactionSent: error, + transactionReceived: success, + transactionsDate: '#777777', + transactionsItemHovered: '#222222', + + transactionItemBg: black, + transactionItemHoverBg: '#0A0A0A', + transactionItemBorder: 'transparent', + transactionItemAddress: '#A7A7A7', + transactionItemAddressHover: white, + + transactionDetailsShadow: `0px 0px 20px 0px ${black}`, + transactionDetailsBg: darkBrand, + transactionDetailsRowHover: '#1D1D1D', + transactionDetailsDivider: '#3A3A3A', + transactionDetailsLabel: '#777777', + + // Status Pill + statusPillLabel: '#727272', + statusPillBg: black, + statusPillBorder: black, + + // Sidebar + sidebarBg: black, + sidebarBorderRight: black, + sidebarLogoGradientBegin: brand, + sidebarLogoGradientEnd: brand2, + sidebarHoveredItemLabel: '#8e8e96', + sidebarActiveItemBorder: 'red', + sidebarItem: brandThree, + sidebarItemActive: brand, + sidebarItemHovered: '#FFF', + sidebarItemHoveredBg: darkBrand, + + // Misc + divider: black, + qrCodeWrapperBg: black, + qrCodeWrapperBorder: black, + headerTitle: text, + selectButtonShadow: 'rgba(238,201,76,0.65)', + + // Forms + inputBg: black, + inputBorder: 'transparent', + inputBorderActive: '#222', + + // Wallet Summary + walletSummaryBg: black, + walletSummaryBorder: black, + + // Wallet Address + walletAddressBg: black, + walletAddressBorder: black, + walletAddressInput: '#828282', + walletAddressInputHovered: white, + walletAddressTooltip: black, + walletAddressTooltipBg: white, + + // Console + consoleBg: black, + consoleBorder: 'transparent', + + // Settings + settingsCardBg: black, + settingsLearnMore: '#AAAAAA', + settingsLearnMoreHovered: '#fff', + + // Loading + loadingScreenBg: black, + loadingScreenText: white, + + // Misc + sendAdditionalOptionsBg: black, + sendAdditionalOptionsBorder: black, + sendAdditionalInputBg: darkBrand, + sendAdditionalInputText: white, +}; diff --git a/app/theme/colors/index.js b/app/theme/colors/index.js new file mode 100644 index 0000000..0fc8e3b --- /dev/null +++ b/app/theme/colors/index.js @@ -0,0 +1,4 @@ +// @flow + +export { DARK_COLORS } from './dark'; +export { LIGHT_COLORS } from './light'; diff --git a/app/theme/colors/light.js b/app/theme/colors/light.js new file mode 100644 index 0000000..668ac04 --- /dev/null +++ b/app/theme/colors/light.js @@ -0,0 +1,136 @@ +// @flow + +const white = '#FFFFFF'; +const whiteHover = '#F9FBFE'; +const offWhite = '#F9F9F9'; +const black = '#000000'; +const text = '#142533'; +const secondaryText = '#777777'; +const brand = '#5684EB'; +const error = '#FF6C6C'; +const success = '#66BE54'; +const logo = '#F4B728'; +const logo2 = '#FFE240'; +const border = '#DDDDDD'; + +export const LIGHT_COLORS = { + // General + background: offWhite, + text, + darkOne: brand, + blackTwo: '#171717', + lightOne: white, + brandOne: '#000', + brandThree: '#5D5D65', + buttonBorderColor: '#3E3C42', + activeItem: brand, + + // Dropdown + dropdownBg: offWhite, + dropdownHoveredBg: white, + dropdownBorder: border, + dropdownIconBorder: '#c1c1c1', + dropdownOpenedIconBorder: '#828282', + + // Buttons + buttonPrimaryBg: brand, + buttonPrimaryDisabledBg: brand, + buttonPrimaryText: white, + buttonSecondaryBg: '#989898', + buttonSecondaryDisabledBg: brand, + buttonSecondaryBorder: '#989898', + buttonSecondaryText: white, + buttonSecondaryHoveredBg: '#aaa', + + // Card + cardBackgroundColor: black, + sendCardBg: white, + sendCardBorder: border, + + // Sidebar + sidebarBg: white, + sidebarBorderRight: border, + sidebarLogoGradientBegin: logo, + sidebarLogoGradientEnd: logo2, + sidebarHoveredItemLabel: '#8E8E96', + sidebarItem: '#aaa', + sidebarItemActive: text, + sidebarItemHovered: text, + sidebarItemHoveredBg: offWhite, + sidebarActiveItemLabel: '#8E8E96', + sidebarActiveItemBorder: 'red', + + // Transactions + transactionSent: error, + transactionReceived: success, + transactionsDate: secondaryText, + transactionsItemHovered: '#222222', + + // Transaction Item + transactionItemBg: white, + transactionItemHoverBg: whiteHover, + transactionItemBorder: border, + transactionItemAddress: '#666666', + transactionItemAddressHover: '#666666', + + // Transaction Details + transactionDetailsShadow: `0px 0px 1px 0px ${black}`, + transactionDetailsBg: white, + transactionDetailsRowHover: whiteHover, + transactionDetailsDivider: border, + transactionDetailsLabel: '#999999', + + // Select + selectButtonShadow: 'rgba(238,201,76,0.65)', + + // Status Pill + statusPillLabel: text, + statusPillBg: '#F9FBFE', + statusPillBorder: border, + + // QR Code + qrCodeWrapperBg: white, + qrCodeWrapperBorder: border, + + // Header + headerTitle: text, + + // Wallet Summary + walletSummaryBg: white, + walletSummaryBorder: border, + + // Wallet Address + walletAddressBg: white, + walletAddressBorder: border, + walletAddressInput: '#666', + walletAddressInputHovered: black, + walletAddressTooltip: white, + walletAddressTooltipBg: black, + + // Forms + inputBg: white, + inputBorder: border, + inputBorderActive: '#828282', + + // Console + consoleBg: white, + consoleBorder: border, + + // Misc + divider: '#AAAAAA', + + // Settings + settingsCardBg: white, + settingsLearnMore: '#a0a0a0', + settingsLearnMoreHovered: '#000', + + // Loading + loadingScreenBg: offWhite, + loadingScreenText: white, + + // Additional Panes + sendAdditionalOptionsBg: white, + sendAdditionalOptionsBorder: border, + sendAdditionalInputBg: offWhite, + sendAdditionalInputText: white, +}; diff --git a/app/theme/docz.js b/app/theme/docz.js new file mode 100644 index 0000000..a5d0b5e --- /dev/null +++ b/app/theme/docz.js @@ -0,0 +1,15 @@ +// @flow + +import React, { Fragment, type Node } from 'react'; +import { ThemeProvider } from 'styled-components'; +import { appTheme } from './theme'; +import { GlobalStyle } from './global'; + +export const DoczWrapper = ({ children }: { children: () => Node<*> }) => ( + <ThemeProvider theme={appTheme}> + <Fragment> + <GlobalStyle /> + {children()} + </Fragment> + </ThemeProvider> +); diff --git a/app/theme/global.js b/app/theme/global.js new file mode 100644 index 0000000..24c9478 --- /dev/null +++ b/app/theme/global.js @@ -0,0 +1,15 @@ +// @flow + +// $FlowFixMe +import { normalize } from 'polished'; // eslint-disable-line +import { createGlobalStyle } from 'styled-components'; + +export const GlobalStyle = createGlobalStyle` + ${normalize()} + + * { + box-sizing: border-box; + user-select: none; + outline: none; + } +`; diff --git a/app/theme/index.js b/app/theme/index.js new file mode 100644 index 0000000..da7221b --- /dev/null +++ b/app/theme/index.js @@ -0,0 +1,5 @@ +// @flow + +export { appTheme } from './theme'; +export { DoczWrapper } from './docz'; +export { GlobalStyle } from './global'; diff --git a/app/theme/theme.js b/app/theme/theme.js new file mode 100644 index 0000000..fc61058 --- /dev/null +++ b/app/theme/theme.js @@ -0,0 +1,346 @@ +// @flow + +import theme from 'styled-theming'; + +import { DARK, LIGHT } from '../constants/themes'; +import { typography } from './typography'; +import { DARK_COLORS, LIGHT_COLORS } from './colors'; + +export const appTheme: AppTheme = { + // General + mode: DARK, + + // Typography + ...typography, + + // Spacing + sidebarWidth: '180px', + headerHeight: '60px', + layoutPaddingLeft: '35px', + layoutPaddingRight: '35px', + layoutContentPaddingTop: '20px', + boxBorderRadius: '3px', + + // Misc + transitionEase: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)', + + // Colors + colors: { + primary: theme('mode', { + [LIGHT]: LIGHT_COLORS.darkOne, + [DARK]: DARK_COLORS.darkOne, + }), + secondary: theme('mode', { + [LIGHT]: LIGHT_COLORS.darkOne, + [DARK]: DARK_COLORS.darkOne, + }), + divider: theme('mode', { + [LIGHT]: LIGHT_COLORS.divider, + [DARK]: DARK_COLORS.divider, + }), + sidebarBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.sidebarBg, + [DARK]: DARK_COLORS.sidebarBg, + }), + sidebarItem: theme('mode', { + [LIGHT]: LIGHT_COLORS.sidebarItem, + [DARK]: DARK_COLORS.sidebarItem, + }), + sidebarItemActive: theme('mode', { + [LIGHT]: LIGHT_COLORS.sidebarItemActive, + [DARK]: DARK_COLORS.sidebarItemActive, + }), + sidebarItemHovered: theme('mode', { + [LIGHT]: LIGHT_COLORS.sidebarItemHovered, + [DARK]: DARK_COLORS.sidebarItemHovered, + }), + sidebarHoveredItemLabel: theme('mode', { + [LIGHT]: LIGHT_COLORS.sidebarHoveredItemLabel, + [DARK]: DARK_COLORS.sidebarHoveredItemLabel, + }), + cardBackgroundColor: theme('mode', { + [LIGHT]: LIGHT_COLORS.cardBackgroundColor, + [DARK]: DARK_COLORS.cardBackgroundColor, + }), + text: theme('mode', { + [LIGHT]: LIGHT_COLORS.text, + [DARK]: DARK_COLORS.text, + }), + activeItem: theme('mode', { + [LIGHT]: LIGHT_COLORS.activeItem, + [DARK]: DARK_COLORS.activeItem, + }), + inactiveItem: theme('mode', { + [LIGHT]: LIGHT_COLORS.brandThree, + [DARK]: DARK_COLORS.brandThree, + }), + sidebarLogoGradientBegin: theme('mode', { + [LIGHT]: LIGHT_COLORS.sidebarLogoGradientBegin, + [DARK]: DARK_COLORS.sidebarLogoGradientBegin, + }), + sidebarLogoGradientEnd: theme('mode', { + [LIGHT]: LIGHT_COLORS.sidebarLogoGradientEnd, + [DARK]: DARK_COLORS.sidebarLogoGradientEnd, + }), + background: theme('mode', { + [LIGHT]: LIGHT_COLORS.background, + [DARK]: DARK_COLORS.background, + }), + transactionSent: theme('mode', { + [LIGHT]: LIGHT_COLORS.transactionSent, + [DARK]: DARK_COLORS.transactionSent, + }), + transactionReceived: theme('mode', { + [LIGHT]: LIGHT_COLORS.transactionReceived, + [DARK]: DARK_COLORS.transactionReceived, + }), + transactionsDate: theme('mode', { + [LIGHT]: LIGHT_COLORS.transactionsDate, + [DARK]: DARK_COLORS.transactionsDate, + }), + transactionsItemHovered: theme('mode', { + [LIGHT]: LIGHT_COLORS.transactionsItemHovered, + [DARK]: DARK_COLORS.transactionsItemHovered, + }), + inputBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.inputBg, + [DARK]: DARK_COLORS.inputBg, + }), + selectButtonShadow: theme('mode', { + [LIGHT]: LIGHT_COLORS.selectButtonShadow, + [DARK]: DARK_COLORS.selectButtonShadow, + }), + transactionDetailsLabel: theme('mode', { + [LIGHT]: LIGHT_COLORS.transactionDetailsLabel, + [DARK]: DARK_COLORS.transactionDetailsLabel, + }), + statusPillLabel: theme('mode', { + [LIGHT]: LIGHT_COLORS.statusPillLabel, + [DARK]: DARK_COLORS.statusPillLabel, + }), + modalItemLabel: theme('mode', { + [LIGHT]: LIGHT_COLORS.transactionsDate, + [DARK]: DARK_COLORS.transactionsDate, + }), + blackTwo: theme('mode', { + [LIGHT]: LIGHT_COLORS.blackTwo, + [DARK]: DARK_COLORS.blackTwo, + }), + buttonBorderColor: theme('mode', { + [LIGHT]: LIGHT_COLORS.buttonBorderColor, + [DARK]: DARK_COLORS.buttonBorderColor, + }), + headerTitle: theme('mode', { + [LIGHT]: LIGHT_COLORS.headerTitle, + [DARK]: DARK_COLORS.headerTitle, + }), + statusPillBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.statusPillBg, + [DARK]: DARK_COLORS.statusPillBg, + }), + walletSummaryBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.walletSummaryBg, + [DARK]: DARK_COLORS.walletSummaryBg, + }), + walletSummaryBorder: theme('mode', { + [LIGHT]: LIGHT_COLORS.walletSummaryBorder, + [DARK]: DARK_COLORS.walletSummaryBorder, + }), + consoleBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.consoleBg, + [DARK]: DARK_COLORS.consoleBg, + }), + sidebarBorderRight: theme('mode', { + [LIGHT]: LIGHT_COLORS.sidebarBorderRight, + [DARK]: DARK_COLORS.sidebarBorderRight, + }), + qrCodeWrapperBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.qrCodeWrapperBg, + [DARK]: DARK_COLORS.qrCodeWrapperBg, + }), + statusPillBorder: theme('mode', { + [LIGHT]: LIGHT_COLORS.statusPillBorder, + [DARK]: DARK_COLORS.statusPillBorder, + }), + sidebarActiveItemBorder: theme('mode', { + [LIGHT]: LIGHT_COLORS.sidebarActiveItemBorder, + [DARK]: DARK_COLORS.sidebarActiveItemBorder, + }), + transactionItemBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.transactionItemBg, + [DARK]: DARK_COLORS.transactionItemBg, + }), + transactionItemHoverBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.transactionItemHoverBg, + [DARK]: DARK_COLORS.transactionItemHoverBg, + }), + transactionItemBorder: theme('mode', { + [LIGHT]: LIGHT_COLORS.transactionItemBorder, + [DARK]: DARK_COLORS.transactionItemBorder, + }), + transactionItemAddress: theme('mode', { + [LIGHT]: LIGHT_COLORS.transactionItemAddress, + [DARK]: DARK_COLORS.transactionItemAddress, + }), + transactionItemAddressHover: theme('mode', { + [LIGHT]: LIGHT_COLORS.transactionItemAddressHover, + [DARK]: DARK_COLORS.transactionItemAddressHover, + }), + transactionDetailsShadow: theme('mode', { + [LIGHT]: LIGHT_COLORS.transactionDetailsShadow, + [DARK]: DARK_COLORS.transactionDetailsShadow, + }), + transactionDetailsBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.transactionDetailsBg, + [DARK]: DARK_COLORS.transactionDetailsBg, + }), + transactionDetailsRowHover: theme('mode', { + [LIGHT]: LIGHT_COLORS.transactionDetailsRowHover, + [DARK]: DARK_COLORS.transactionDetailsRowHover, + }), + transactionDetailsDivider: theme('mode', { + [LIGHT]: LIGHT_COLORS.transactionDetailsDivider, + [DARK]: DARK_COLORS.transactionDetailsDivider, + }), + inputBorder: theme('mode', { + [LIGHT]: LIGHT_COLORS.inputBorder, + [DARK]: DARK_COLORS.inputBorder, + }), + sidebarItemHoveredBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.sidebarItemHoveredBg, + [DARK]: DARK_COLORS.sidebarItemHoveredBg, + }), + consoleBorder: theme('mode', { + [LIGHT]: LIGHT_COLORS.consoleBorder, + [DARK]: DARK_COLORS.consoleBorder, + }), + sendCardBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.sendCardBg, + [DARK]: DARK_COLORS.sendCardBg, + }), + sendCardBorder: theme('mode', { + [LIGHT]: LIGHT_COLORS.sendCardBorder, + [DARK]: DARK_COLORS.sendCardBorder, + }), + walletAddressBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.walletAddressBg, + [DARK]: DARK_COLORS.walletAddressBg, + }), + walletAddressBorder: theme('mode', { + [LIGHT]: LIGHT_COLORS.walletAddressBorder, + [DARK]: DARK_COLORS.walletAddressBorder, + }), + walletAddressInput: theme('mode', { + [LIGHT]: LIGHT_COLORS.walletAddressInput, + [DARK]: DARK_COLORS.walletAddressInput, + }), + walletAddressInputHovered: theme('mode', { + [LIGHT]: LIGHT_COLORS.walletAddressInputHovered, + [DARK]: DARK_COLORS.walletAddressInputHovered, + }), + dropdownBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.dropdownBg, + [DARK]: DARK_COLORS.dropdownBg, + }), + dropdownHoveredBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.dropdownHoveredBg, + [DARK]: DARK_COLORS.dropdownHoveredBg, + }), + dropdownBorder: theme('mode', { + [LIGHT]: LIGHT_COLORS.dropdownBorder, + [DARK]: DARK_COLORS.dropdownBorder, + }), + settingsCardBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.settingsCardBg, + [DARK]: DARK_COLORS.settingsCardBg, + }), + settingsLearnMore: theme('mode', { + [LIGHT]: LIGHT_COLORS.settingsLearnMore, + [DARK]: DARK_COLORS.settingsLearnMore, + }), + settingsLearnMoreHovered: theme('mode', { + [LIGHT]: LIGHT_COLORS.settingsLearnMoreHovered, + [DARK]: DARK_COLORS.settingsLearnMoreHovered, + }), + buttonPrimaryText: theme('mode', { + [LIGHT]: LIGHT_COLORS.buttonPrimaryText, + [DARK]: DARK_COLORS.buttonPrimaryText, + }), + buttonSecondaryText: theme('mode', { + [LIGHT]: LIGHT_COLORS.buttonSecondaryText, + [DARK]: DARK_COLORS.buttonSecondaryText, + }), + buttonPrimaryBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.buttonPrimaryBg, + [DARK]: DARK_COLORS.buttonPrimaryBg, + }), + buttonSecondaryBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.buttonSecondaryBg, + [DARK]: DARK_COLORS.buttonSecondaryBg, + }), + buttonPrimaryDisabledBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.buttonPrimaryDisabledBg, + [DARK]: DARK_COLORS.buttonPrimaryDisabledBg, + }), + buttonSecondaryDisabledBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.buttonSecondaryDisabledBg, + [DARK]: DARK_COLORS.buttonSecondaryDisabledBg, + }), + buttonSecondaryBorder: theme('mode', { + [LIGHT]: LIGHT_COLORS.buttonSecondaryBorder, + [DARK]: DARK_COLORS.buttonSecondaryBorder, + }), + buttonSecondaryHoveredBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.buttonSecondaryHoveredBg, + [DARK]: DARK_COLORS.buttonSecondaryHoveredBg, + }), + dropdownIconBorder: theme('mode', { + [LIGHT]: LIGHT_COLORS.dropdownIconBorder, + [DARK]: DARK_COLORS.dropdownIconBorder, + }), + dropdownOpenedIconBorder: theme('mode', { + [LIGHT]: LIGHT_COLORS.dropdownOpenedIconBorder, + [DARK]: DARK_COLORS.dropdownOpenedIconBorder, + }), + inputBorderActive: theme('mode', { + [LIGHT]: LIGHT_COLORS.inputBorderActive, + [DARK]: DARK_COLORS.inputBorderActive, + }), + walletAddressTooltipBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.walletAddressTooltipBg, + [DARK]: DARK_COLORS.walletAddressTooltipBg, + }), + qrCodeWrapperBorder: theme('mode', { + [LIGHT]: LIGHT_COLORS.qrCodeWrapperBorder, + [DARK]: DARK_COLORS.qrCodeWrapperBorder, + }), + walletAddressTooltip: theme('mode', { + [LIGHT]: LIGHT_COLORS.walletAddressTooltip, + [DARK]: DARK_COLORS.walletAddressTooltip, + }), + loadingScreenBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.loadingScreenBg, + [DARK]: DARK_COLORS.loadingScreenBg, + }), + loadingScreenText: theme('mode', { + [LIGHT]: LIGHT_COLORS.loadingScreenText, + [DARK]: DARK_COLORS.loadingScreenText, + }), + sendAdditionalOptionsBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.sendAdditionalOptionsBg, + [DARK]: DARK_COLORS.sendAdditionalOptionsBg, + }), + sendAdditionalOptionsBorder: theme('mode', { + [LIGHT]: LIGHT_COLORS.sendAdditionalOptionsBorder, + [DARK]: DARK_COLORS.sendAdditionalOptionsBorder, + }), + sendAdditionalInputBg: theme('mode', { + [LIGHT]: LIGHT_COLORS.sendAdditionalInputBg, + [DARK]: DARK_COLORS.sendAdditionalInputBg, + }), + sendAdditionalInputText: theme('mode', { + [LIGHT]: LIGHT_COLORS.sendAdditionalInputText, + [DARK]: DARK_COLORS.sendAdditionalInputText, + }), + }, +}; diff --git a/app/theme/typography.js b/app/theme/typography.js new file mode 100644 index 0000000..1c017be --- /dev/null +++ b/app/theme/typography.js @@ -0,0 +1,18 @@ +// @flow + +const FONT_FAMILY = 'Roboto'; + +export const typography = { + fontFamily: FONT_FAMILY, + fontWeight: { + bold: 700, + default: 400, + light: 300, + }, + fontSize: { + large: 1.25, + medium: 1.125, + regular: 0.84375, + small: 0.667, + }, +}; diff --git a/app/theme/utils.js b/app/theme/utils.js new file mode 100644 index 0000000..e69de29 diff --git a/app/views/console.js b/app/views/console.js index 11e60a5..08d97f5 100644 --- a/app/views/console.js +++ b/app/views/console.js @@ -1,21 +1,25 @@ // @flow -import React, { Component, Fragment } from 'react'; +import React, { PureComponent, Fragment } from 'react'; // eslint-disable-next-line import/no-extraneous-dependencies import { ipcRenderer } from 'electron'; -import styled from 'styled-components'; +import styled, { withTheme } from 'styled-components'; import uuid from 'uuid/v4'; + import { TextComponent } from '../components/text'; -import ConsoleSymbol from '../assets/images/console_zcash.png'; +import ConsoleSymbolDark from '../assets/images/console_zcash_dark.png'; +import ConsoleSymbolLight from '../assets/images/console_zcash_light.png'; +import { DARK } from '../constants/themes'; const Wrapper = styled.div` max-height: 100%; overflow-y: auto; - background-color: ${props => props.theme.colors.cardBackgroundColor}; + background-color: ${props => props.theme.colors.consoleBg}; + border: 1px solid ${props => props.theme.colors.consoleBorder}; margin-top: ${props => props.theme.layoutContentPaddingTop}; border-radius: ${props => props.theme.boxBorderRadius}; - padding: 38px 33.5px; + padding: 30px; `; const ConsoleText = styled(TextComponent)` @@ -53,13 +57,15 @@ const defaultState = ` const breakpoints = [1, 4, 7, 10, 13]; -type Props = {}; +type Props = { + theme: AppTheme, +}; type State = { log: string, }; -export class ConsoleView extends Component<Props, State> { +class Component extends PureComponent<Props, State> { state = { log: defaultState, }; @@ -76,6 +82,11 @@ export class ConsoleView extends Component<Props, State> { render() { const { log } = this.state; + const { theme } = this.props; + + const ConsoleSymbol = theme.mode === DARK + ? ConsoleSymbolDark + : ConsoleSymbolLight; return ( <Wrapper id='console-wrapper'> @@ -92,3 +103,5 @@ export class ConsoleView extends Component<Props, State> { ); } } + +export const ConsoleView = withTheme(Component); diff --git a/app/views/receive.js b/app/views/receive.js index 3f98ab1..a5d7d59 100644 --- a/app/views/receive.js +++ b/app/views/receive.js @@ -1,16 +1,20 @@ // @flow import React, { PureComponent } from 'react'; -import styled from 'styled-components'; +import styled, { withTheme } from 'styled-components'; import { Transition, animated } from 'react-spring'; +import { DARK } from '../constants/themes'; + import { InputLabelComponent } from '../components/input-label'; import { RowComponent } from '../components/row'; import { TextComponent } from '../components/text'; import { WalletAddress } from '../components/wallet-address'; -import MenuIcon from '../assets/images/menu_icon.svg'; -import PlusIcon from '../assets/images/plus_icon.svg'; +import MenuIconDark from '../assets/images/menu_icon_dark.svg'; +import MenuIconLight from '../assets/images/menu_icon_light.svg'; +import PlusIconDark from '../assets/images/plus_icon_dark.svg'; +import PlusIconLight from '../assets/images/plus_icon_light.svg'; import type { addressType } from '../redux/modules/receive'; @@ -35,21 +39,30 @@ const ActionButton = styled.button` outline: none; display: flex; align-items: center; - margin-top: 30px; + margin: 15px 0; opacity: 0.7; + width: auto; &:hover { opacity: 1; } `; +const ActionText = styled(TextComponent)` + white-space: nowrap; +`; + const ActionIcon = styled.img` width: 25px; height: 25px; border: 1px solid ${props => props.theme.colors.text}; border-radius: 100%; margin-right: 11.5px; - padding: 5px; + padding: 2px; +`; + +const PlusIcon = styled(ActionIcon)` + padding: 6.5px; `; const RevealsMain = styled.div` @@ -65,13 +78,14 @@ type Props = { addresses: Array<string>, loadAddresses: () => void, getNewAddress: ({ type: addressType }) => void, + theme: AppTheme, }; type State = { showAdditionalOptions: boolean, }; -export class ReceiveView extends PureComponent<Props, State> { +class Component extends PureComponent<Props, State> { state = { showAdditionalOptions: false, }; @@ -93,31 +107,49 @@ export class ReceiveView extends PureComponent<Props, State> { }; render() { - const { addresses } = this.props; + const { addresses, theme } = this.props; const { showAdditionalOptions } = this.state; const buttonText = `${showAdditionalOptions ? 'Hide' : 'Show'} Other Address Types`; const shieldedAddresses = addresses.filter(addr => addr.startsWith('z')); const transparentAddresses = addresses.filter(addr => addr.startsWith('t')); + + const seeMoreIcon = theme.mode === DARK + ? MenuIconDark + : MenuIconLight; + + const plusIcon = theme.mode === DARK + ? PlusIconDark + : PlusIconLight; + return ( <div> <Label value='Shielded Address' /> {shieldedAddresses.map(addr => ( - <WalletAddress key={addr} address={addr} /> + <WalletAddress + key={addr} + address={addr} + /> ))} - <Row> - <ActionButton onClick={this.toggleAdditionalOptions} isActive={showAdditionalOptions}> - <ActionIcon isActive={showAdditionalOptions} src={MenuIcon} alt='More Options' /> - <TextComponent value={buttonText} /> - </ActionButton> + <Row justifyContent='space-between'> <ActionButton onClick={() => this.generateNewAddress('shielded')}> - <ActionIcon src={PlusIcon} alt='New Shielded Address' /> - <TextComponent value='New Shielded Address' /> + <PlusIcon + src={plusIcon} + alt='New Shielded Address' + /> + <ActionText value='New Shielded Address' /> </ActionButton> - <ActionButton onClick={() => this.generateNewAddress('transparent')}> - <ActionIcon src={PlusIcon} alt='New Transparent Address' /> - <TextComponent value='New Transparent Address' /> + <ActionButton + onClick={this.toggleAdditionalOptions} + isActive={showAdditionalOptions} + > + <ActionIcon + isActive={showAdditionalOptions} + src={seeMoreIcon} + alt='More Options' + /> + <ActionText value={buttonText} /> </ActionButton> </Row> <RevealsMain> @@ -146,6 +178,10 @@ export class ReceiveView extends PureComponent<Props, State> { {transparentAddresses.map(addr => ( <WalletAddress key={addr} address={addr} /> ))} + <ActionButton onClick={() => this.generateNewAddress('transparent')}> + <PlusIcon src={plusIcon} alt='New Transparent Address' /> + <ActionText value='New Transparent Address' /> + </ActionButton> </animated.div> )) } @@ -155,3 +191,5 @@ export class ReceiveView extends PureComponent<Props, State> { ); } } + +export const ReceiveView = withTheme(Component); diff --git a/app/views/send.js b/app/views/send.js index 52a0e70..e7c424a 100644 --- a/app/views/send.js +++ b/app/views/send.js @@ -1,11 +1,12 @@ // @flow import React, { Fragment, PureComponent } from 'react'; -import styled, { keyframes } from 'styled-components'; +import styled, { withTheme, keyframes } from 'styled-components'; import { BigNumber } from 'bignumber.js'; import { Transition, animated } from 'react-spring'; import { FEES } from '../constants/fees'; +import { DARK } from '../constants/themes'; import { InputLabelComponent } from '../components/input-label'; import { InputComponent } from '../components/input'; @@ -20,18 +21,18 @@ import { ConfirmDialogComponent } from '../components/confirm-dialog'; import { formatNumber } from '../utils/format-number'; import { ascii2hex } from '../utils/ascii-to-hexadecimal'; +import SentIcon from '../assets/images/transaction_sent_icon_dark.svg'; +import MenuIconDark from '../assets/images/menu_icon_dark.svg'; +import MenuIconLight from '../assets/images/menu_icon_light.svg'; +import ValidIcon from '../assets/images/green_check_dark.png'; +import InvalidIcon from '../assets/images/error_icon_dark.png'; +import LoadingIcon from '../assets/images/sync_icon_dark.png'; +import ArrowUpIconDark from '../assets/images/arrow_up_dark.png'; +import ArrowUpIconLight from '../assets/images/arrow_up_light.png'; + import type { SendTransactionInput } from '../containers/send'; import type { State as SendState } from '../redux/modules/send'; -import SentIcon from '../assets/images/transaction_sent_icon.svg'; -import MenuIcon from '../assets/images/menu_icon.svg'; -import ValidIcon from '../assets/images/green_check.png'; -import InvalidIcon from '../assets/images/error_icon.png'; -import LoadingIcon from '../assets/images/sync_icon.png'; -import ArrowUpIcon from '../assets/images/arrow_up.png'; - -import theme from '../theme'; - const rotate = keyframes` from { transform: rotate(0deg); @@ -51,13 +52,19 @@ const Loader = styled.img` `; const FormWrapper = styled.div` - margin-top: ${props => props.theme.layoutContentPaddingTop}; width: 71%; `; const SendWrapper = styled(ColumnComponent)` width: 25%; - margin-top: 60px; + margin-top: 42px; +`; + +const Label = styled(InputLabelComponent)` + text-transform: uppercase; + color: ${props => props.theme.colors.transactionsDate}; + font-size: ${props => `${props.theme.fontSize.regular * 0.9}em`}; + font-weight: ${props => String(props.theme.fontWeight.bold)}; `; type AmountProps = @@ -96,7 +103,6 @@ const ShowFeeButton = styled.button` outline: none; display: flex; align-items: center; - margin: 30px 0; opacity: 0.7; &:hover { @@ -113,16 +119,19 @@ const SeeMoreIcon = styled.img` `; const FeeWrapper = styled.div` - background-color: #000; - border-radius: 4px; - padding: 13px 19px; + background-color: ${props => props.theme.colors.sendAdditionalOptionsBg}; + border: 1px solid ${props => props.theme.colors.sendAdditionalOptionsBorder}; + border-radius: 3px; + padding: 0 20px 15px; margin-bottom: 20px; `; const InfoCard = styled.div` width: 100%; - background-color: ${props => props.theme.colors.cardBackgroundColor}; + background-color: ${props => props.theme.colors.sendCardBg}; + border: 1px solid ${props => props.theme.colors.sendCardBorder} border-radius: ${props => props.theme.boxBorderRadius}; + margin-bottom: 10px; `; const InfoContent = styled.div` @@ -141,7 +150,11 @@ const InfoCardUSD = styled(TextComponent)` const FormButton = styled(Button)` width: 100%; - margin: 10px 0; + margin: 5px 0; + + &:first-child { + margin-top: 0; + } `; const ModalContent = styled(ColumnComponent)` @@ -170,6 +183,10 @@ const ItemLabel = styled(TextComponent)` margin-bottom: 3.5px; `; +const ValidateItemLabel = styled(ItemLabel)` + margin-bottom: -1px; +`; + const SendZECValue = styled(TextComponent)` color: ${props => props.theme.colors.transactionSent}; font-size: ${props => `${props.theme.fontSize.large}em`}; @@ -225,8 +242,12 @@ const MaxAvailableAmount = styled.button` background: none; color: white; cursor: pointer; - border-left: ${props => `1px solid ${props.theme.colors.background(props)}`}; + border-left: 1px solid ${props => props.theme.colors.inputBorder}; opacity: 0.8; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; &:hover { opacity: 1; @@ -238,6 +259,32 @@ const MaxAvailableAmountImg = styled.img` height: 20px; `; +const ValidateWrapper = styled(RowComponent)` + margin-top: 3px; +`; + + +const ActionsWrapper = styled(RowComponent)` + padding: 30px 0; + align-items: center; + flex-direction: row; + justify-content: space-between; +`; + +const HexadecimalWrapper = styled.div` + display: flex; + opacity: 0.7; + cursor: pointer; + + &:hover { + opacity: 1; + } +`; + +const HexadecimalText = styled(TextComponent)` + white-space: nowrap; +`; + type Props = { ...SendState, balance: number, @@ -250,6 +297,7 @@ type Props = { loadAddresses: () => void, loadZECPrice: () => void, getAddressBalance: ({ address: string }) => void, + theme: AppTheme, }; type State = { @@ -274,7 +322,7 @@ const initialState = { isHexMemo: false, }; -export class SendView extends PureComponent<Props, State> { +class Component extends PureComponent<Props, State> { state = initialState; componentDidMount() { @@ -383,18 +431,24 @@ export class SendView extends PureComponent<Props, State> { }; renderValidationStatus = () => { - const { isToAddressValid } = this.props; + const { isToAddressValid, theme } = this.props; return isToAddressValid ? ( - <RowComponent alignItems='center'> + <ValidateWrapper alignItems='center'> <ValidateStatusIcon src={ValidIcon} /> - <ItemLabel value='VALID' color={theme.colors.transactionReceived} /> - </RowComponent> + <ValidateItemLabel + value='VALID' + color={theme.colors.transactionReceived} + /> + </ValidateWrapper> ) : ( - <RowComponent alignItems='center'> + <ValidateWrapper alignItems='center'> <ValidateStatusIcon src={InvalidIcon} /> - <ItemLabel value='INVALID' color={theme.colors.transactionSent} /> - </RowComponent> + <ValidateItemLabel + value='INVALID' + color={theme.colors.transactionSent} + /> + </ValidateWrapper> ); }; @@ -481,10 +535,10 @@ export class SendView extends PureComponent<Props, State> { render() { const { - addresses, balance, zecPrice, isSending, error, operationId, + addresses, balance, zecPrice, isSending, error, operationId, theme, } = this.props; const { - showFee, from, amount, to, memo, fee, feeType, + showFee, from, amount, to, memo, fee, feeType, isHexMemo, } = this.state; const isEmpty = amount === ''; @@ -505,10 +559,18 @@ export class SendView extends PureComponent<Props, State> { append: 'USD $', }); + const seeMoreIcon = theme.mode === DARK + ? MenuIconDark + : MenuIconLight; + + const arrowUpIcon = theme.mode === DARK + ? ArrowUpIconDark + : ArrowUpIconLight; + return ( <RowComponent id='send-wrapper' justifyContent='space-between'> <FormWrapper> - <InputLabelComponent value='From an address' /> + <Label value='From an address' /> <SelectComponent onChange={this.handleChange('from')} value={from} @@ -516,7 +578,7 @@ export class SendView extends PureComponent<Props, State> { options={addresses.map(addr => ({ value: addr, label: addr }))} capitalize={false} /> - <InputLabelComponent value='Amount' /> + <Label value='Amount' /> <AmountWrapper isEmpty={isEmpty}> <AmountInput renderRight={() => ( @@ -524,7 +586,7 @@ export class SendView extends PureComponent<Props, State> { onClick={() => this.handleChange('amount')(balance)} disabled={!from} > - <MaxAvailableAmountImg src={ArrowUpIcon} /> + <MaxAvailableAmountImg src={arrowUpIcon} /> </MaxAvailableAmount> )} isEmpty={isEmpty} @@ -537,7 +599,7 @@ export class SendView extends PureComponent<Props, State> { disabled={!from} /> </AmountWrapper> - <InputLabelComponent value='To' /> + <Label value='To' /> <InputComponent onChange={this.handleChange('to')} value={to} @@ -545,7 +607,7 @@ export class SendView extends PureComponent<Props, State> { renderRight={to ? this.renderValidationStatus : () => null} name='to' /> - <InputLabelComponent value='Memo' /> + <Label value='Memo' /> <InputComponent onChange={this.handleChange('memo')} value={memo} @@ -553,20 +615,28 @@ export class SendView extends PureComponent<Props, State> { placeholder='Enter a text here' name='memo' /> - <RowComponent justifyContent='flex-end'> - <Checkbox onChange={event => this.setState({ isHexMemo: event.target.checked })} /> - <TextComponent value='Hexadecimal memo' /> - </RowComponent> - <ShowFeeButton - id='send-show-additional-options-button' - onClick={() => this.setState(state => ({ - showFee: !state.showFee, - })) - } - > - <SeeMoreIcon src={MenuIcon} alt='Show more icon' /> - <TextComponent value={`${showFee ? 'Hide' : 'Show'} Additional Options`} /> - </ShowFeeButton> + <ActionsWrapper> + <ShowFeeButton + id='send-show-additional-options-button' + onClick={() => this.setState(state => ({ + showFee: !state.showFee, + })) + } + > + <SeeMoreIcon src={seeMoreIcon} alt='Show more icon' /> + <TextComponent value={`${showFee ? 'Hide' : 'Show'} Additional Options`} /> + </ShowFeeButton> + <HexadecimalWrapper> + <Checkbox + onChange={event => this.setState({ isHexMemo: event.target.checked })} + checked={isHexMemo} + /> + <HexadecimalText + onClick={() => this.setState(prevState => ({ isHexMemo: !prevState.isHexMemo }))} + value='Hexadecimal Memo' + /> + </HexadecimalWrapper> + </ActionsWrapper> <RevealsMain> <Transition native @@ -591,22 +661,23 @@ export class SendView extends PureComponent<Props, State> { <animated.div style={props}> <FeeWrapper id='send-fee-wrapper'> <RowComponent alignItems='flex-end' justifyContent='space-between'> - <ColumnComponent width='74%'> - <InputLabelComponent value='Fee' /> + <ColumnComponent width='64%'> + <Label value='Fee' /> <InputComponent type='number' onChange={this.handleChange('fee')} value={String(fee)} disabled={feeType !== FEES.CUSTOM} - bgColor={theme.colors.blackTwo} + bgColor={theme.colors.sendAdditionalInputBg} + color={theme.colors.sendAdditionalInputText} name='fee' /> </ColumnComponent> - <ColumnComponent width='25%'> + <ColumnComponent width='35%'> <SelectComponent placement='top' value={String(feeType)} - bgColor={theme.colors.blackTwo} + bgColor={theme.colors.sendAdditionalInputBg} onChange={this.handleChangeFeeType} options={Object.keys(FEES).map(cur => ({ label: cur.toLowerCase(), @@ -649,7 +720,6 @@ export class SendView extends PureComponent<Props, State> { onClick={() => this.showModal(toggle)} id='send-submit-button' label='Send' - variant='secondary' focused isFluid disabled={!from || !amount || !to || !fee} @@ -666,9 +736,11 @@ export class SendView extends PureComponent<Props, State> { </ModalContent> )} </ConfirmDialogComponent> - <FormButton label='Cancel' variant='secondary' /> + <FormButton label='Clear Form' variant='secondary' /> </SendWrapper> </RowComponent> ); } } + +export const SendView = withTheme(Component); diff --git a/app/views/settings.js b/app/views/settings.js index 2cf19d0..5b23462 100644 --- a/app/views/settings.js +++ b/app/views/settings.js @@ -20,18 +20,30 @@ import { Clipboard } from '../components/clipboard'; import { SelectComponent } from '../components/select'; import rpc from '../../services/api'; -import { DARK, LIGHT } from '../constants/themes'; +import { DARK, LIGHT, THEME_MODE } from '../constants/themes'; import electronStore from '../../config/electron-store'; +import { openExternal } from '../utils/open-external'; + import type { MapDispatchToProps, MapStateToProps } from '../containers/settings'; const HOME_DIR = electron.remote.app.getPath('home'); +const EXPORT_VIEW_KEYS_TITLE = 'Export View Keys'; +const EXPORT_VIEW_KEYS_CONTENT = 'Viewing keys for shielded addresses allow for the disclosure of all transaction information to a preffered party. Anyone who holds these keys can see all shielded transaction details, but cannot spend coins as it is not a private key.'; +const EXPORT_VIEW_KEYS_LEARN_MORE = 'https://z.cash/blog/viewing-keys-selective-disclosure'; +const IMPORT_PRIV_KEYS_TITLE = 'Import Private Keys'; +const IMPORT_PRIV_KEYS_CONTENT = 'Importing private keys will add the spendable coins to this wallet.'; +const EXPORT_PRIV_KEYS_TITLE = 'Export Private Keys'; +const EXPORT_PRIV_KEYS_CONTENT = 'Beware: exporting your private keys will allow anyone controlling them to spend your coins. Only perform this action on a trusted machine.'; +const BACKUP_WALLET_TITLE = 'Backup Wallet'; +const BACKUP_WALLET_CONTENT = 'It is recommended that you backup your wallet often.'; + const Wrapper = styled.div` margin-top: ${props => props.theme.layoutContentPaddingTop}; `; const ModalContent = styled.div` - padding: 20px; + padding: 20px 30px; width: 100%; max-height: 600px; overflow-y: auto; @@ -53,9 +65,37 @@ const ClipboardButton = styled(Clipboard)` `; const SettingsWrapper = styled.div` - margin-bottom: 45px; + margin-bottom: 20px; min-width: 200px; - width: 37%; + width: 70%; + max-width: 600px; + min-width: 350px; + background: ${props => props.theme.colors.settingsCardBg}; + padding: 20px 20px 10px 20px; + border: 1px solid ${props => props.theme.colors.inputBorder}; + border-radius: ${props => props.theme.boxBorderRadius}; +`; + +const SettingsInnerWrapper = styled.div` + margin-bottom: 50px; + + &:last-child { + margin-bottom: 0; + } +`; + +const LearnMore = styled.div` + cursor: pointer; + text-transform: uppercase; + font-size: 10px; + font-family: ${props => props.theme.fontFamily};; + letter-spacing: 1px; + color: ${props => props.theme.colors.settingsLearnMore}; + + &:hover { + color: ${props => props.theme.colors.settingsLearnMoreHovered};; + } +} `; const SettingsTitle = styled(TextComponent)` @@ -67,12 +107,25 @@ const SettingsTitle = styled(TextComponent)` `; const SettingsContent = styled(TextComponent)` - margin-bottom: 20px; - margin-top: 10px; + margin-bottom: 30px; + margin-top: 15px; + font-weight: 300; + letter-spacing: 0.5px; + line-height: 1.4; `; const ThemeSelectWrapper = styled.div` margin-bottom: 20px; + width: 70%; + max-width: 600px; + min-width: 350px; +`; + +const SettingsActionWrapper = styled.div` + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; `; type Key = { @@ -221,37 +274,38 @@ export class SettingsView extends PureComponent<Props, State> { error, } = this.state; + const themeOptions = [ + { label: 'Dark', value: DARK }, + { label: 'Light', value: LIGHT }, + ]; + return ( <Wrapper> <ThemeSelectWrapper> <SettingsTitle value='Theme' /> <SelectComponent - onChange={newMode => electronStore.set('THEME_MODE', newMode)} - options={[ - { - label: 'Dark', - value: DARK, - }, - { - label: 'Light', - value: LIGHT, - }, - ]} - value={electronStore.get('THEME_MODE')} + onChange={newMode => electronStore.set(THEME_MODE, newMode)} + value={electronStore.get(THEME_MODE)} + options={themeOptions} /> </ThemeSelectWrapper> <ConfirmDialogComponent - title='Export view keys' + title={EXPORT_VIEW_KEYS_TITLE} renderTrigger={toggleVisibility => ( <SettingsWrapper> - <SettingsTitle value='Export view keys' /> - <SettingsContent value='Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.' /> - <Btn label='Export view keys' onClick={toggleVisibility} /> + <SettingsTitle value={EXPORT_VIEW_KEYS_TITLE} /> + <SettingsContent value={EXPORT_VIEW_KEYS_CONTENT} /> + <SettingsActionWrapper> + <Btn label={EXPORT_VIEW_KEYS_TITLE} onClick={toggleVisibility} /> + <LearnMore onClick={() => openExternal(EXPORT_VIEW_KEYS_LEARN_MORE)}> + Learn More + </LearnMore> + </SettingsActionWrapper> </SettingsWrapper> )} onConfirm={this.exportViewKeys} showButtons={!successExportViewKeys} - width={750} + width={450} > {() => ( <ModalContent> @@ -277,78 +331,79 @@ export class SettingsView extends PureComponent<Props, State> { )} </ConfirmDialogComponent> - <ConfirmDialogComponent - title='Export private keys' - renderTrigger={toggleVisibility => ( - <SettingsWrapper> - <SettingsTitle value='Export private keys' /> - <SettingsContent value='Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.' /> - <Btn label='Export private keys' onClick={toggleVisibility} /> - </SettingsWrapper> - )} - onConfirm={this.exportPrivateKeys} - showButtons={!successExportPrivateKeys} - width={750} - > - {() => ( - <ModalContent> - {successExportPrivateKeys ? ( - privateKeys.map(({ zAddress, key }) => ( - <> - <InputLabelComponent value={zAddress} /> - <RowComponent alignItems='center'> - <InputComponent - value={key} - onFocus={(event) => { - event.currentTarget.select(); - }} - /> - <ClipboardButton text={key} /> - </RowComponent> - </> - )) - ) : ( - <TextComponent value='Ut id vulputate arcu. Curabitur mattis aliquam magna sollicitudin vulputate. Morbi tempus bibendum porttitor. Quisque dictum ac ipsum a luctus. Donec et lacus ac erat consectetur molestie a id erat.' /> - )} - </ModalContent> - )} - </ConfirmDialogComponent> - - <ConfirmDialogComponent - title='Import private keys' - renderTrigger={toggleVisibility => ( - <SettingsWrapper> - <SettingsTitle value='Import private keys' /> - <SettingsContent value='Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.' /> - <Btn label='Import private keys' onClick={toggleVisibility} /> - </SettingsWrapper> - )} - onConfirm={this.importPrivateKeys} - showButtons={!successImportPrivateKeys} - width={900} - isLoading={isLoading} - > - {() => ( - <ModalContent> - <InputLabelComponent value='Please paste your private keys here, one per line. The keys will be imported into your zcashd node' /> - <InputComponent - value={importedPrivateKeys} - onChange={value => this.setState({ importedPrivateKeys: value })} - inputType='textarea' - rows={10} - /> - {successImportPrivateKeys && ( - <TextComponent value='Private keys imported in your node' align='center' /> - )} - {error && <TextComponent value={error} align='center' />} - </ModalContent> - )} - </ConfirmDialogComponent> + <SettingsWrapper> + <ConfirmDialogComponent + title={EXPORT_PRIV_KEYS_TITLE} + renderTrigger={toggleVisibility => ( + <SettingsInnerWrapper> + <SettingsTitle value={EXPORT_PRIV_KEYS_TITLE} /> + <SettingsContent value={EXPORT_PRIV_KEYS_CONTENT} /> + <Btn label={EXPORT_PRIV_KEYS_TITLE} onClick={toggleVisibility} /> + </SettingsInnerWrapper> + )} + onConfirm={this.exportPrivateKeys} + showButtons={!successExportPrivateKeys} + width={450} + > + {() => ( + <ModalContent> + {successExportPrivateKeys ? ( + privateKeys.map(({ zAddress, key }) => ( + <> + <InputLabelComponent value={zAddress} /> + <RowComponent alignItems='center'> + <InputComponent + value={key} + onFocus={(event) => { + event.currentTarget.select(); + }} + /> + <ClipboardButton text={key} /> + </RowComponent> + </> + )) + ) : ( + <TextComponent value='Ut id vulputate arcu. Curabitur mattis aliquam magna sollicitudin vulputate. Morbi tempus bibendum porttitor. Quisque dictum ac ipsum a luctus. Donec et lacus ac erat consectetur molestie a id erat.' /> + )} + </ModalContent> + )} + </ConfirmDialogComponent> + <ConfirmDialogComponent + title={IMPORT_PRIV_KEYS_TITLE} + renderTrigger={toggleVisibility => ( + <SettingsInnerWrapper> + <SettingsTitle value={IMPORT_PRIV_KEYS_TITLE} /> + <SettingsContent value={IMPORT_PRIV_KEYS_CONTENT} /> + <Btn label={IMPORT_PRIV_KEYS_TITLE} onClick={toggleVisibility} /> + </SettingsInnerWrapper> + )} + onConfirm={this.importPrivateKeys} + showButtons={!successImportPrivateKeys} + width={450} + isLoading={isLoading} + > + {() => ( + <ModalContent> + <InputLabelComponent value='Please paste your private keys here, one per line. The keys will be imported into your zcashd node' /> + <InputComponent + value={importedPrivateKeys} + onChange={value => this.setState({ importedPrivateKeys: value })} + inputType='textarea' + rows={10} + /> + {successImportPrivateKeys && ( + <TextComponent value='Private keys imported in your node' align='center' /> + )} + {error && <TextComponent value={error} align='center' />} + </ModalContent> + )} + </ConfirmDialogComponent> + </SettingsWrapper> <SettingsWrapper> - <SettingsTitle value='Backup Wallet' /> - <SettingsContent value='Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.' /> - <Btn label='Backup wallet.dat' onClick={this.backupWalletDat} /> + <SettingsTitle value={BACKUP_WALLET_TITLE} /> + <SettingsContent value={BACKUP_WALLET_CONTENT} /> + <Btn label={BACKUP_WALLET_TITLE} onClick={this.backupWalletDat} /> </SettingsWrapper> </Wrapper> ); diff --git a/app/views/transactions.js b/app/views/transactions.js index 7d78881..0c61066 100644 --- a/app/views/transactions.js +++ b/app/views/transactions.js @@ -31,17 +31,30 @@ const RoundedTransactionWrapper = styled.div` ${props => (props.roundPosition === 'top' ? ` - border-top-left-radius: ${props.theme.boxBorderRadius}; - border-top-right-radius: ${props.theme.boxBorderRadius};` - : `border-bottom-left-radius: ${props.theme.boxBorderRadius}; - border-bottom-right-radius: ${props.theme.boxBorderRadius};`)} + border-top-left-radius: ${props.theme.boxBorderRadius}; + border-top-right-radius: ${props.theme.boxBorderRadius}; + ` + : ` + border-bottom-left-radius: ${props.theme.boxBorderRadius}; + border-bottom-right-radius: ${props.theme.boxBorderRadius}; + ` + )} +`; + +const ListWrapper = styled.div` + margin-top: 10px; `; export class TransactionsView extends PureComponent<Props> { componentDidMount() { const { getTransactions, resetTransactionsList } = this.props; + resetTransactionsList(); - getTransactions({ count: PAGE_SIZE, offset: 0, shieldedTransactionsCount: 0 }); + getTransactions({ + count: PAGE_SIZE, + offset: 0, + shieldedTransactionsCount: 0, + }); } isRowLoaded = ({ index }: { index: number }) => { @@ -122,9 +135,14 @@ export class TransactionsView extends PureComponent<Props> { return transactionItem; }; - renderRow = ({ index, key, style }: { index: number, key: string, style: Object }) => ( + renderRow = ( + { index, key, style }: { index: number, key: string, style: Object }, + ) => ( <div key={key} style={style}> - {this.isRowLoaded({ index }) ? this.renderTransactions({ index }) : 'Loading...'} + {this.isRowLoaded({ index }) + ? this.renderTransactions({ index }) + : 'Loading...' + } </div> ); @@ -179,16 +197,18 @@ export class TransactionsView extends PureComponent<Props> { {({ onRowsRendered, registerChild }) => ( <AutoSizer> {({ width, height }) => ( - <List - noRowsRenderer={EmptyTransactionsComponent} - ref={registerChild} - onRowsRendered={onRowsRendered} - rowRenderer={this.renderRow} - rowHeight={this.getRowHeight} - rowCount={transactionsSize} - width={width} - height={height - 20} - /> + <ListWrapper> + <List + noRowsRenderer={EmptyTransactionsComponent} + ref={registerChild} + onRowsRendered={onRowsRendered} + rowRenderer={this.renderRow} + rowHeight={this.getRowHeight} + rowCount={transactionsSize} + width={width} + height={height - 20} + /> + </ListWrapper> )} </AutoSizer> )} diff --git a/flow-custom-typedefs/theme.js b/flow-custom-typedefs/theme.js index 9d4d673..0ef622a 100644 --- a/flow-custom-typedefs/theme.js +++ b/flow-custom-typedefs/theme.js @@ -1,31 +1,126 @@ +// @flow + import { ThemeSet } from 'styled-theming'; type Colors = { primary: ThemeSet, secondary: ThemeSet, sidebarBg: ThemeSet, - sidebarItem: ThemeSet, - sidebarItemActive: ThemeSet, - sidebarHoveredItem: ThemeSet, - sidebarHoveredItemLabel: ThemeSet, + + // Card cardBackgroundColor: ThemeSet, + sendCardBg: ThemeSet, + sendCardBorder: ThemeSet, + text: ThemeSet, activeItem: ThemeSet, inactiveItem: ThemeSet, - sidebarLogoGradientBegin: ThemeSet, - sidebarLogoGradientEnd: ThemeSet, background: ThemeSet, + + selectButtonShadow: ThemeSet, + + modalItemLabel: ThemeSet, + blackTwo: ThemeSet, + buttonBorderColor: ThemeSet, + + // Dropdown + dropdownBg: ThemeSet, + dropdownHoveredBg: ThemeSet, + dropdownBorder: ThemeSet, + dropdownIconBorder: ThemeSet, + dropdownOpenedIconBorder: ThemeSet, + + // Divider + divider: ThemeSet, + + // Header + headerTitle: ThemeSet, + + // Status Pill + statusPillBg: ThemeSet, + statusPillLabel: ThemeSet, + statusPillBorder: ThemeSet, + + // Wallet Summary + walletSummaryBg: ThemeSet, + walletSummaryBorder: ThemeSet, + + // Wallet Address + walletAddressBg: ThemeSet, + walletAddressBorder: ThemeSet, + walletAddressInput: ThemeSet, + walletAddressInputHovered: ThemeSet, + walletAddressTooltip: ThemeSet, + walletAddressTooltipBg: ThemeSet, + + // Console + consoleBg: ThemeSet, + consoleBorder: ThemeSet, + + // Buttons + buttonPrimaryBg: ThemeSet, + buttonPrimaryText: ThemeSet, + buttonPrimaryDisabledBg: ThemeSet, + buttonSecondaryBg: ThemeSet, + buttonSecondaryText: ThemeSet, + buttonSecondaryDisabledBg: ThemeSet, + buttonSecondaryBorder: ThemeSet, + buttonSecondaryHoveredBg: ThemeSet, + + // QR Code + qrCodeWrapperBg: ThemeSet, + qrCodeWrapperBorder: ThemeSet, + + // Transactions transactionSent: ThemeSet, transactionReceived: ThemeSet, transactionsDate: ThemeSet, transactionsItemHovered: ThemeSet, - inputBackground: ThemeSet, - selectButtonShadow: ThemeSet, - transactionsDetailsLabel: ThemeSet, - statusPillLabel: ThemeSet, - modalItemLabel: ThemeSet, - blackTwo: ThemeSet, - buttonBorderColor: ThemeSet, + + // Transaction Item + transactionItemBg: ThemeSet, + transactionItemHoverBg: ThemeSet, + transactionItemAddress: ThemeSet, + transactionItemAddressHover: ThemeSet, + + // Transaction Details + transactionDetailsShadow: ThemeSet, + transactionDetailsBg: ThemeSet, + transactionDetailsRowHover: ThemeSet, + transactionDetailsDivider: ThemeSet, + transactionDetailsLabel: ThemeSet, + + // Input + inputBg: ThemeSet, + inputBorder: ThemeSet, + inputBorderActive: ThemeSet, + + // Sidebar + sidebarBg: ThemeSet, + sidebarItem: ThemeSet, + sidebarItemActive: ThemeSet, + sidebarActiveItemBorder: ThemeSet, + sidebarBorderRight: ThemeSet, + sidebarItemHovered: ThemeSet, + sidebarHoveredItemLabel: ThemeSet, + sidebarLogoGradientBegin: ThemeSet, + sidebarLogoGradientEnd: ThemeSet, + sidebarItemHoveredBg: ThemeSet, + + // Settings + settingsCardBg: ThemeSet, + settingsLearnMore: ThemeSet, + settingsLearnMoreHovered: ThemeSet, + + // Loading + loadingScreenBg: ThemeSet, + loadingScreenText: ThemeSet, + + // Misc + sendAdditionalOptionsBg: ThemeSet, + sendAdditionalOptionsBorder: ThemeSet, + sendAdditionalInputBg: ThemeSet, + sendAdditionalInputText: ThemeSet, }; type FontSize = { @@ -42,17 +137,26 @@ type FontWeight = { }; type AppTheme = { + // General mode: string, + + // Typography fontFamily: string, fontWeight: FontWeight, fontSize: FontSize, + + // Colors colors: Colors, + + // Spacing sidebarWidth: string, headerHeight: string, layoutPaddingLeft: string, layoutPaddingRight: string, layoutContentPaddingTop: string, boxBorderRadius: string, + + // Misc transitionEase: string, }; diff --git a/package.json b/package.json index e84b109..68e2fa0 100644 --- a/package.json +++ b/package.json @@ -97,6 +97,7 @@ "autoprefixer": "^9.3.1", "bignumber.js": "^8.0.1", "connected-react-router": "^5.0.1", + "copy-to-clipboard": "^3.0.8", "date-fns": "^1.30.1", "dotenv": "^6.2.0", "electron-compile": "^6.4.4", diff --git a/yarn.lock b/yarn.lock index afaffc2..45e09e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4045,6 +4045,13 @@ copy-text-to-clipboard@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/copy-text-to-clipboard/-/copy-text-to-clipboard-1.0.4.tgz#2286ff6c53495962c5318d34746d256939069c49" +copy-to-clipboard@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.0.8.tgz#f4e82f4a8830dce4666b7eb8ded0c9bcc313aba9" + integrity sha512-c3GdeY8qxCHGezVb1EFQfHYK/8NZRemgcTIzPq7PuxjHAf/raKibn2QdhHPb/y6q74PMgH6yizaDZlRmw6QyKw== + dependencies: + toggle-selection "^1.0.3" + core-js@^1.0.0: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" @@ -14115,6 +14122,11 @@ to-vfile@^5.0.1, to-vfile@^5.0.2: is-buffer "^2.0.0" vfile "^3.0.0" +toggle-selection@^1.0.3: + version "1.0.6" + resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" + integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI= + toidentifier@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"