From 06b1d27e641875c3faa4fafa92d697124166d2f9 Mon Sep 17 00:00:00 2001 From: Tom Kirkpatrick Date: Tue, 2 Oct 2018 20:39:34 +0200 Subject: [PATCH] fix(styles): clean up and standardize buttons Implement Button component throughout the app. Fix #830 --- app/components/Activity/Activity.js | 11 +-- app/components/Activity/Activity.scss | 19 ---- app/components/App/App.js | 6 +- .../Contacts/AddChannel/AddChannel.js | 7 +- .../Contacts/AddChannel/AddChannel.scss | 13 --- .../ConnectManually/ConnectManually.js | 8 +- .../ConnectManually/ConnectManually.scss | 22 ----- .../SubmitChannelForm/SubmitChannelForm.js | 8 +- .../SubmitChannelForm/SubmitChannelForm.scss | 21 ----- app/components/Form/Pay/Pay.js | 8 +- app/components/Form/Pay/Pay.scss | 22 ----- app/components/Form/Request/Request.js | 8 +- app/components/Form/Request/Request.scss | 22 ----- .../Onboarding/FormContainer/FormContainer.js | 16 ++-- .../FormContainer/FormContainer.scss | 28 +----- app/components/Onboarding/Login/Login.js | 29 +++--- app/components/Onboarding/Login/Login.scss | 70 +-------------- app/components/Onboarding/Login/messages.js | 3 +- app/components/Settings/Theme/Theme.js | 35 ++++---- app/components/Wallet/Wallet.js | 9 +- app/components/Wallet/Wallet.scss | 22 ----- app/containers/Activity.js | 12 ++- app/containers/App.js | 2 + app/containers/Root.js | 72 ++++++++------- app/index.js | 5 +- app/lib/lnd/subscribe/channelgraph.js | 4 +- app/reducers/index.js | 3 + app/reducers/settings.js | 23 +---- app/reducers/theme.js | 67 ++++++++++++++ app/styles/app.global.scss | 90 ------------------- app/translations/en.json | 5 ++ test/unit/components/Form/Pay.spec.js | 10 +-- .../__snapshots__/settings.spec.js.snap | 4 - 33 files changed, 214 insertions(+), 470 deletions(-) create mode 100644 app/reducers/theme.js diff --git a/app/components/Activity/Activity.js b/app/components/Activity/Activity.js index 8f164054..878346ff 100644 --- a/app/components/Activity/Activity.js +++ b/app/components/Activity/Activity.js @@ -6,7 +6,8 @@ import xIcon from 'icons/x.svg' import FaRepeat from 'react-icons/lib/fa/repeat' import { FormattedMessage, injectIntl } from 'react-intl' - +import { Flex } from 'rebass' +import Button from 'components/UI/Button' import Wallet from 'components/Wallet' import Invoice from './Invoice' import Payment from './Payment' @@ -217,15 +218,15 @@ class Activity extends Component { ))} {showExpiredToggle && currentActivity.length > 0 && ( -
  • -
    + +
    -
  • + + )} diff --git a/app/components/Activity/Activity.scss b/app/components/Activity/Activity.scss index 03fa499f..f18b1b00 100644 --- a/app/components/Activity/Activity.scss +++ b/app/components/Activity/Activity.scss @@ -140,25 +140,6 @@ } } -.toggleExpired { - margin: 0 auto; - font-size: 14px; - font-weight: bold; - color: var(--primaryText); - background: var(--lightBackground); - padding: 10px 7.5px; - width: 200px; - text-align: center; - border-radius: 5px; - cursor: pointer; - opacity: 1; - transition: all 0.25s; - - &:hover { - opacity: 0.5; - } -} - .activity { position: relative; padding: 0 60px; diff --git a/app/components/App/App.js b/app/components/App/App.js index 096156fb..9ff46ce3 100644 --- a/app/components/App/App.js +++ b/app/components/App/App.js @@ -26,6 +26,7 @@ class App extends Component { render() { const { + currentTheme, currentTicker, form, @@ -41,8 +42,6 @@ class App extends Component { activityModalProps, channelFormProps, - settings, - children } = this.props @@ -51,7 +50,7 @@ class App extends Component { } return ( -
    +
    @@ -79,6 +78,7 @@ App.propTypes = { formProps: PropTypes.object.isRequired, closeForm: PropTypes.func.isRequired, error: PropTypes.object.isRequired, + currentTheme: PropTypes.string.isRequired, currentTicker: PropTypes.object, contactsFormProps: PropTypes.object, networkTabProps: PropTypes.object, diff --git a/app/components/Contacts/AddChannel/AddChannel.js b/app/components/Contacts/AddChannel/AddChannel.js index d6c42177..aa3c6dc7 100644 --- a/app/components/Contacts/AddChannel/AddChannel.js +++ b/app/components/Contacts/AddChannel/AddChannel.js @@ -5,8 +5,8 @@ import Isvg from 'react-inlinesvg' import x from 'icons/x.svg' import { FormattedMessage } from 'react-intl' +import Button from 'components/UI/Button' import messages from './messages' - import styles from './AddChannel.scss' const AddChannel = ({ @@ -145,9 +145,10 @@ const AddChannel = ({

    -
    + +
    + )}
    diff --git a/app/components/Contacts/AddChannel/AddChannel.scss b/app/components/Contacts/AddChannel/AddChannel.scss index 7cdc0d23..c3f0a01a 100644 --- a/app/components/Contacts/AddChannel/AddChannel.scss +++ b/app/components/Contacts/AddChannel/AddChannel.scss @@ -125,17 +125,4 @@ font-size: 14px; margin: 20px 0; } - - div { - background: var(--lightningOrange); - font-size: 16px; - padding: 10px; - cursor: pointer; - transition: all 0.25s; - border-radius: 5px; - - &:hover { - opacity: 0.75; - } - } } diff --git a/app/components/Contacts/ConnectManually/ConnectManually.js b/app/components/Contacts/ConnectManually/ConnectManually.js index 7cb5e805..1352ca49 100644 --- a/app/components/Contacts/ConnectManually/ConnectManually.js +++ b/app/components/Contacts/ConnectManually/ConnectManually.js @@ -2,6 +2,7 @@ import React from 'react' import PropTypes from 'prop-types' import { FormattedMessage, injectIntl } from 'react-intl' +import Button from 'components/UI/Button' import messages from './messages' import styles from './ConnectManually.scss' @@ -81,12 +82,9 @@ class ConnectManually extends React.Component {
    -
    +
    +
    ) diff --git a/app/components/Contacts/ConnectManually/ConnectManually.scss b/app/components/Contacts/ConnectManually/ConnectManually.scss index d3c418ef..938c9f2c 100644 --- a/app/components/Contacts/ConnectManually/ConnectManually.scss +++ b/app/components/Contacts/ConnectManually/ConnectManually.scss @@ -100,26 +100,4 @@ .submit { margin-top: 50px; text-align: center; - - .button { - width: 235px; - margin: 0 auto; - padding: 20px 10px; - background: #31343f; - opacity: 0.5; - cursor: pointer; - transition: 0.25s all; - - &.active { - background: var(--lightningOrange); - color: var(--white); - font-weight: bold; - opacity: 1; - transition: all 0.25s; - - &:hover { - opacity: 0.75; - } - } - } } diff --git a/app/components/Contacts/SubmitChannelForm/SubmitChannelForm.js b/app/components/Contacts/SubmitChannelForm/SubmitChannelForm.js index ffe67632..bc807254 100644 --- a/app/components/Contacts/SubmitChannelForm/SubmitChannelForm.js +++ b/app/components/Contacts/SubmitChannelForm/SubmitChannelForm.js @@ -5,6 +5,7 @@ import FaAngleDown from 'react-icons/lib/fa/angle-down' import FaExclamationCircle from 'react-icons/lib/fa/exclamation-circle' import AmountInput from 'components/AmountInput' +import Button from 'components/UI/Button' import { FormattedNumber, FormattedMessage } from 'react-intl' import messages from './messages' @@ -155,12 +156,9 @@ class SubmitChannelForm extends React.Component {
    -
    0 ? styles.active : undefined}`} - onClick={formSubmitted} - > +
    +
    ) diff --git a/app/components/Contacts/SubmitChannelForm/SubmitChannelForm.scss b/app/components/Contacts/SubmitChannelForm/SubmitChannelForm.scss index 46f177e6..ab7cc6ba 100644 --- a/app/components/Contacts/SubmitChannelForm/SubmitChannelForm.scss +++ b/app/components/Contacts/SubmitChannelForm/SubmitChannelForm.scss @@ -145,25 +145,4 @@ .submit { margin-top: 50px; text-align: center; - - .button { - width: 235px; - margin: 0 auto; - padding: 20px 10px; - background: var(--lightBackground); - opacity: 0.5; - cursor: pointer; - transition: 0.25s all; - - &.active { - background: var(--lightningOrange); - color: var(--white); - font-weight: bold; - opacity: 1; - - &:hover { - opacity: 0.5; - } - } - } } diff --git a/app/components/Form/Pay/Pay.js b/app/components/Form/Pay/Pay.js index a817f030..bfc34453 100644 --- a/app/components/Form/Pay/Pay.js +++ b/app/components/Form/Pay/Pay.js @@ -8,6 +8,7 @@ import FaAngleDown from 'react-icons/lib/fa/angle-down' import { btc } from 'lib/utils' import AmountInput from 'components/AmountInput' +import Button from 'components/UI/Button' import { FormattedNumber, FormattedMessage, injectIntl } from 'react-intl' import messages from './messages' @@ -203,12 +204,9 @@ class Pay extends Component {
    -
    +
    +
    diff --git a/app/components/Form/Pay/Pay.scss b/app/components/Form/Pay/Pay.scss index 44302779..422fa3e5 100644 --- a/app/components/Form/Pay/Pay.scss +++ b/app/components/Form/Pay/Pay.scss @@ -155,28 +155,6 @@ .submit { margin-top: 20px; text-align: center; - - .button { - width: 200px; - margin: 0 auto; - padding: 15px 7.5px; - background: var(--lightBackground); - border-radius: 5px; - opacity: 0.5; - cursor: pointer; - transition: 0.25s all; - - &.active { - background: var(--lightningOrange); - color: var(--white); - font-weight: bold; - opacity: 1; - - &:hover { - // background: darken(var(--lightningOrange), 5%); - } - } - } } } diff --git a/app/components/Form/Request/Request.js b/app/components/Form/Request/Request.js index 82e50010..037025f8 100644 --- a/app/components/Form/Request/Request.js +++ b/app/components/Form/Request/Request.js @@ -7,6 +7,7 @@ import FaAngleDown from 'react-icons/lib/fa/angle-down' import { btc } from 'lib/utils' import AmountInput from 'components/AmountInput' +import Button from 'components/UI/Button' import { FormattedNumber, FormattedMessage, injectIntl } from 'react-intl' import messages from './messages' @@ -109,12 +110,9 @@ const Request = ({
    -
    0 ? styles.active : undefined}`} - onClick={onRequestSubmit} - > +
    +
    diff --git a/app/components/Form/Request/Request.scss b/app/components/Form/Request/Request.scss index bebec416..08fc7b49 100644 --- a/app/components/Form/Request/Request.scss +++ b/app/components/Form/Request/Request.scss @@ -134,27 +134,5 @@ .submit { margin-top: 50px; text-align: center; - - .button { - width: 200px; - margin: 0 auto; - padding: 15px 7.5px; - background: var(--lightBackground); - border-radius: 5px; - opacity: 0.5; - cursor: pointer; - transition: 0.25s all; - - &.active { - background: var(--lightningOrange); - color: var(--white); - font-weight: bold; - opacity: 1; - - &:hover { - opacity: 0.75; - } - } - } } } diff --git a/app/components/Onboarding/FormContainer/FormContainer.js b/app/components/Onboarding/FormContainer/FormContainer.js index 28fecab7..b1008247 100644 --- a/app/components/Onboarding/FormContainer/FormContainer.js +++ b/app/components/Onboarding/FormContainer/FormContainer.js @@ -5,7 +5,7 @@ import Isvg from 'react-inlinesvg' import FaAngleLeft from 'react-icons/lib/fa/angle-left' import FaAngleRight from 'react-icons/lib/fa/angle-right' - +import Button from 'components/UI/Button' import zapLogo from 'icons/zap-logo.svg' import { FormattedMessage } from 'react-intl' import zapLogoBlack from 'icons/zap-logo-black.svg' @@ -42,18 +42,18 @@ const FormContainer = ({ title, description, back, next, children, theme }) => (
    {back && ( -
    - {' '} +
    + )}
    {next && ( -
    - {' '} - -
    + )}
    diff --git a/app/components/Onboarding/FormContainer/FormContainer.scss b/app/components/Onboarding/FormContainer/FormContainer.scss index 5ef25263..6cf1a21e 100644 --- a/app/components/Onboarding/FormContainer/FormContainer.scss +++ b/app/components/Onboarding/FormContainer/FormContainer.scss @@ -3,7 +3,7 @@ .container { position: relative; height: 100vh; - background: var(--darkestBackground); + background: var(--lightBackground); } .titleBar { @@ -51,7 +51,7 @@ .content { position: relative; - background: var(--lightBackground); + background: var(--darkestBackground); height: 100vh; padding: 40px 40px; } @@ -67,29 +67,5 @@ display: flex; flex-direction: row; justify-content: space-between; - - .nextButton { - cursor: pointer; - transition: all 0.25s; - background: var(--lightningOrange); - padding: 8px 20px 8px 30px; - text-align: center; - border-radius: 5px; - - &:hover { - opacity: 0.5; - } - } - - .backButton { - cursor: pointer; - transition: all 0.25s; - padding: 8px 20px 8px 0; - text-align: center; - - &:hover { - opacity: 0.5; - } - } } } diff --git a/app/components/Onboarding/Login/Login.js b/app/components/Onboarding/Login/Login.js index 4e0d53ff..4461064f 100644 --- a/app/components/Onboarding/Login/Login.js +++ b/app/components/Onboarding/Login/Login.js @@ -1,11 +1,13 @@ import React from 'react' import PropTypes from 'prop-types' import { FormattedMessage, injectIntl } from 'react-intl' +import Button from 'components/UI/Button' import messages from './messages' import styles from './Login.scss' const Login = ({ password, + passwordIsValid, updatePassword, unlockingWallet, unlockWallet, @@ -31,26 +33,25 @@ const Login = ({

    -
    - - unlockWallet(password)} - > - {unlockingWallet ? ( - - ) : ( - - )} - - -
    +
    ) Login.propTypes = { password: PropTypes.string.isRequired, + passwordIsValid: PropTypes.bool.isRequired, updatePassword: PropTypes.func.isRequired, unlockingWallet: PropTypes.bool.isRequired, unlockWallet: PropTypes.func.isRequired, diff --git a/app/components/Onboarding/Login/Login.scss b/app/components/Onboarding/Login/Login.scss index d5530849..03cd4748 100644 --- a/app/components/Onboarding/Login/Login.scss +++ b/app/components/Onboarding/Login/Login.scss @@ -27,6 +27,7 @@ .error { margin-top: 20px; + height: 20px; color: $red; visibility: hidden; font-size: 12px; @@ -40,73 +41,4 @@ .buttons { margin-top: 15%; text-align: center; - - div { - color: var(--primaryText); - text-align: center; - margin-bottom: 40px; - - .button { - padding: 15px 35px; - background: var(--lightningOrange); - font-size: 14px; - transition: all 0.25s; - border-radius: 5px; - - &.button { - position: relative; - } - - &.active { - opacity: 1; - cursor: pointer; - - &:hover { - opacity: 0.5; - } - } - } - } -} - -.spinner { - height: 20px; - width: 20px; - border: 1px solid rgba(235, 184, 100, 0.1); - border-left-color: rgba(235, 184, 100, 0.4); - -webkit-border-radius: 999px; - -moz-border-radius: 999px; - border-radius: 999px; - -webkit-animation: animation-rotate 1000ms linear infinite; - -moz-animation: animation-rotate 1000ms linear infinite; - -o-animation: animation-rotate 1000ms linear infinite; - animation: animation-rotate 1000ms linear infinite; - display: inline-block; - position: absolute; - top: calc(50% - 10px); - left: calc(50% - 10px); -} - -@-webkit-keyframes animation-rotate { - 100% { - -webkit-transform: rotate(360deg); - } -} - -@-moz-keyframes animation-rotate { - 100% { - -moz-transform: rotate(360deg); - } -} - -@-o-keyframes animation-rotate { - 100% { - -o-transform: rotate(360deg); - } -} - -@keyframes animation-rotate { - 100% { - transform: rotate(360deg); - } } diff --git a/app/components/Onboarding/Login/messages.js b/app/components/Onboarding/Login/messages.js index 8669d4ef..93223933 100644 --- a/app/components/Onboarding/Login/messages.js +++ b/app/components/Onboarding/Login/messages.js @@ -3,5 +3,6 @@ import { defineMessages } from 'react-intl' /* eslint-disable max-len */ export default defineMessages({ password_placeholder: 'Password', - unlock: 'Unlock' + unlock: 'Unlock', + unlocking: 'Unlocking' }) diff --git a/app/components/Settings/Theme/Theme.js b/app/components/Settings/Theme/Theme.js index 9f549cf9..348cada9 100644 --- a/app/components/Settings/Theme/Theme.js +++ b/app/components/Settings/Theme/Theme.js @@ -9,7 +9,7 @@ import messages from './messages' import styles from './Theme.scss' -const Fiat = ({ theme, disableSubMenu, setTheme }) => ( +const Theme = ({ currentTheme, disableSubMenu, setTheme, themes }) => (
    @@ -18,26 +18,27 @@ const Fiat = ({ theme, disableSubMenu, setTheme }) => (
      -
    • setTheme('dark')}> - - - - {theme === 'dark' && } -
    • -
    • setTheme('light')}> - - - - {theme === 'light' && } -
    • + {Object.keys(themes).map(theme => { + return ( +
    • setTheme(theme)} + > + + {currentTheme === theme && } +
    • + ) + })}
    ) -Fiat.propTypes = { - theme: PropTypes.string.isRequired, +Theme.propTypes = { + currentTheme: PropTypes.string.isRequired, disableSubMenu: PropTypes.func.isRequired, - setTheme: PropTypes.func + setTheme: PropTypes.func, + themes: PropTypes.object.isRequired } -export default Fiat +export default Theme diff --git a/app/components/Wallet/Wallet.js b/app/components/Wallet/Wallet.js index c5634f0d..9f6cb7d4 100644 --- a/app/components/Wallet/Wallet.js +++ b/app/components/Wallet/Wallet.js @@ -8,6 +8,7 @@ import { btc, blockExplorer } from 'lib/utils' import Value from 'components/Value' import AnimatedCheckmark from 'components/AnimatedCheckmark' import Settings from 'components/Settings' +import Button from 'components/UI/Button' import zapLogo from 'icons/zap-logo.svg' import zapLogoBlack from 'icons/zap-logo-black.svg' @@ -115,12 +116,12 @@ const Wallet = ({
    -
    +
    -
    + +
    +
    {showPayLoadingScreen && ( diff --git a/app/components/Wallet/Wallet.scss b/app/components/Wallet/Wallet.scss index 55ed59ba..6b797c39 100644 --- a/app/components/Wallet/Wallet.scss +++ b/app/components/Wallet/Wallet.scss @@ -173,28 +173,6 @@ flex-direction: row; justify-content: flex-end; align-items: right; - - .pay, - .request { - font-size: 13px; - font-weight: bold; - color: var(--white); - background: var(--lightningOrange); - padding: 10px 7.5px; - width: 80px; - text-align: center; - border-radius: 5px; - cursor: pointer; - transition: all 0.25s; - - &:hover { - opacity: 0.75; - } - - &:nth-child(1) { - margin-right: 20px; - } - } } .notificationBox { diff --git a/app/containers/Activity.js b/app/containers/Activity.js index 0b02ac11..5fb1f6b4 100644 --- a/app/containers/Activity.js +++ b/app/containers/Activity.js @@ -23,7 +23,9 @@ import { payFormSelectors } from 'reducers/payform' import { setWalletCurrencyFilters } from 'reducers/info' -import { setSettingsOpen, setActiveSubMenu, disableSubMenu, setTheme } from 'reducers/settings' +import { setSettingsOpen, setActiveSubMenu, disableSubMenu } from 'reducers/settings' + +import { setTheme, themeSelectors } from 'reducers/theme' import Activity from 'components/Activity' @@ -77,6 +79,9 @@ const mapStateToProps = state => ({ currentLocale: state.intl.locale, locales: state.locale, + currentTheme: themeSelectors.currentTheme(state), + themes: themeSelectors.themes(state), + paymentModalOpen: paymentSelectors.paymentModalOpen(state), invoiceModalOpen: invoiceSelectors.invoiceModalOpen(state), @@ -110,7 +115,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => ({ currencyName: stateProps.currencyName, network: stateProps.info.network, paymentTimeout: stateProps.payment.paymentTimeout, - theme: stateProps.settings.theme, + theme: stateProps.currentTheme, setCurrency: dispatchProps.setCurrency, setWalletCurrencyFilters: dispatchProps.setWalletCurrencyFilters, @@ -148,7 +153,8 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => ({ }, themeProps: { - theme: stateProps.settings.theme, + themes: stateProps.themes, + currentTheme: stateProps.currentTheme, setTheme: dispatchProps.setTheme, disableSubMenu: dispatchProps.disableSubMenu } diff --git a/app/containers/App.js b/app/containers/App.js index d893b948..5c4b7166 100644 --- a/app/containers/App.js +++ b/app/containers/App.js @@ -4,6 +4,7 @@ import get from 'lodash.get' import { btc } from 'lib/utils' +import { themeSelectors } from 'reducers/theme' import { setCurrency, tickerSelectors } from 'reducers/ticker' import { closeWalletModal } from 'reducers/address' @@ -165,6 +166,7 @@ const mapStateToProps = state => ({ activityModalItem: activitySelectors.activityModalItem(state), + currentTheme: themeSelectors.currentTheme(state), currentTicker: tickerSelectors.currentTicker(state), currentCurrencyFilters: tickerSelectors.currentCurrencyFilters(state), currencyName: tickerSelectors.currencyName(state), diff --git a/app/containers/Root.js b/app/containers/Root.js index 50e150ba..b71e5932 100644 --- a/app/containers/Root.js +++ b/app/containers/Root.js @@ -3,8 +3,8 @@ import { connect } from 'react-redux' import { ConnectedRouter } from 'react-router-redux' import { Switch, Route } from 'react-router' import PropTypes from 'prop-types' +import { ThemeProvider } from 'styled-components' import GlobalError from 'components/GlobalError' - import { clearError } from 'reducers/error' import { @@ -32,6 +32,7 @@ import { setReEnterSeedIndexes } from 'reducers/onboarding' import { fetchTicker, tickerSelectors } from 'reducers/ticker' +import { themeSelectors } from 'reducers/theme' import { lndSelectors } from 'reducers/lnd' import { walletAddress } from 'reducers/address' import LoadingBolt from 'components/LoadingBolt' @@ -73,8 +74,9 @@ const mapStateToProps = state => ({ onboarding: state.onboarding, address: state.address, info: state.info, - theme: state.settings.theme, balance: state.balance, + currentTheme: themeSelectors.currentTheme(state), + currentThemeSettings: themeSelectors.currentThemeSettings(state), currentTicker: tickerSelectors.currentTicker(state), error: state.error, syncPercentage: lndSelectors.syncPercentage(state), @@ -97,7 +99,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { hasSynced: stateProps.info.hasSynced, syncPercentage: stateProps.syncPercentage, address: stateProps.address.address, - theme: stateProps.theme + theme: stateProps.currentTheme } const connectionTypeProps = { @@ -188,7 +190,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { const onboardingProps = { onboarding: stateProps.onboarding, - theme: stateProps.theme, + theme: stateProps.currentTheme, changeStep: dispatchProps.changeStep, startLnd: dispatchProps.startLnd, submitNewWallet: dispatchProps.submitNewWallet, @@ -225,6 +227,7 @@ class Root extends Component { const { balance, clearError, + currentThemeSettings, currentTicker, error: { error }, history, @@ -235,15 +238,17 @@ class Root extends Component { if (!onboardingProps.onboarding.onboarded) { return ( -
    - - - - -
    + +
    + + + + +
    +
    ) } @@ -253,27 +258,33 @@ class Root extends Component { onboardingProps.onboarding.connectionType === 'local' && lnd.syncStatus !== 'complete' ) { - return + return ( + + + + ) } return ( -
    - - - - - - -
    + +
    + + + + + + +
    +
    ) } @@ -284,6 +295,7 @@ Root.propTypes = { clearError: PropTypes.func.isRequired, error: PropTypes.object.isRequired, fetchTicker: PropTypes.func.isRequired, + currentThemeSettings: PropTypes.object.isRequired, currentTicker: PropTypes.object, history: PropTypes.object.isRequired, lnd: PropTypes.object.isRequired, diff --git a/app/index.js b/app/index.js index 38ff307a..91fa1302 100644 --- a/app/index.js +++ b/app/index.js @@ -2,12 +2,9 @@ import React from 'react' import ReactDOM from 'react-dom' import { Provider } from 'react-intl-redux' import jstz from 'jstimezonedetect' - -import Root from './containers/Root' import { configureStore, history } from './store/configureStore' import { getLocale } from './lib/i18n' - -// Load global styles. +import Root from './containers/Root' import './styles/app.global.scss' // Register supported locales. diff --git a/app/lib/lnd/subscribe/channelgraph.js b/app/lib/lnd/subscribe/channelgraph.js index b7624032..910b19ae 100644 --- a/app/lib/lnd/subscribe/channelgraph.js +++ b/app/lib/lnd/subscribe/channelgraph.js @@ -5,7 +5,7 @@ export default function subscribeToChannelGraph() { const call = this.service.subscribeChannelGraph({}) call.on('data', channelGraphData => { - mainLog.info('CHANNELGRAPH:', channelGraphData) + mainLog.debug('CHANNELGRAPH:', channelGraphData) if (this.mainWindow) { this.mainWindow.send('channelGraphData', { channelGraphData }) } @@ -13,7 +13,7 @@ export default function subscribeToChannelGraph() { call.on('end', () => mainLog.info('end')) call.on('error', error => error.code !== status.CANCELLED && mainLog.error(error)) call.on('status', channelGraphStatus => { - mainLog.info('CHANNELGRAPHSTATUS:', channelGraphStatus) + mainLog.debug('CHANNELGRAPHSTATUS:', channelGraphStatus) if (this.mainWindow) { this.mainWindow.send('channelGraphStatus', { channelGraphStatus }) } diff --git a/app/reducers/index.js b/app/reducers/index.js index 0948648b..94859e67 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -2,6 +2,7 @@ import { combineReducers } from 'redux' import { routerReducer as router } from 'react-router-redux' import { intlReducer as intl } from 'react-intl-redux' import locale from './locale' +import theme from './theme' import onboarding from './onboarding' import lnd from './lnd' import ticker from './ticker' @@ -29,6 +30,8 @@ const rootReducer = combineReducers({ router, intl, locale, + theme, + onboarding, lnd, ticker, diff --git a/app/reducers/settings.js b/app/reducers/settings.js index b67d4755..04a676f5 100644 --- a/app/reducers/settings.js +++ b/app/reducers/settings.js @@ -1,7 +1,3 @@ -import Store from 'electron-store' - -// Settings store -const store = new Store({ name: 'settings' }) // ------------------------------------ // Constants // ------------------------------------ @@ -9,8 +5,6 @@ export const SET_SETTINGS_OPEN = 'SET_SETTINGS_OPEN' export const SET_ACTIVE_SUBMENU = 'SET_ACTIVE_SUBMENU' export const DISABLE_SUBMENU = 'DISABLE_SUBMENU' -export const SET_THEME = 'SET_THEME' - // ------------------------------------ // Actions // ------------------------------------ @@ -34,25 +28,13 @@ export function disableSubMenu() { } } -export function setTheme(theme) { - // Persist the new fiatTicker in our ticker store - store.set('theme', theme) - - return { - type: SET_THEME, - theme - } -} - // ------------------------------------ // Action Handlers // ------------------------------------ const ACTION_HANDLERS = { [SET_SETTINGS_OPEN]: (state, { settingsOpen }) => ({ ...state, settingsOpen }), [SET_ACTIVE_SUBMENU]: (state, { activeSubMenu }) => ({ ...state, activeSubMenu }), - [DISABLE_SUBMENU]: state => ({ ...state, activeSubMenu: null }), - - [SET_THEME]: (state, { theme }) => ({ ...state, theme }) + [DISABLE_SUBMENU]: state => ({ ...state, activeSubMenu: null }) } // ------------------------------------ @@ -60,8 +42,7 @@ const ACTION_HANDLERS = { // ------------------------------------ const initialState = { settingsOpen: false, - activeSubMenu: null, - theme: store.get('theme', 'dark') + activeSubMenu: null } export default function settingsReducer(state = initialState, action) { diff --git a/app/reducers/theme.js b/app/reducers/theme.js new file mode 100644 index 00000000..de4ab2f5 --- /dev/null +++ b/app/reducers/theme.js @@ -0,0 +1,67 @@ +import { createSelector } from 'reselect' +import Store from 'electron-store' +import { dark, light } from 'themes' + +// Settings store +const store = new Store({ name: 'settings' }) + +// ------------------------------------ +// Constants +// ------------------------------------ + +export const SET_THEME = 'SET_THEME' +const DEFAULT_THEME = 'dark' + +// ------------------------------------ +// Actions +// ------------------------------------ + +export function setTheme(currentTheme) { + // Persist the new fiatTicker in our ticker store + store.set('theme', currentTheme) + + return { + type: SET_THEME, + currentTheme + } +} + +// ------------------------------------ +// Action Handlers +// ------------------------------------ +const ACTION_HANDLERS = { + [SET_THEME]: (state, { currentTheme }) => ({ ...state, currentTheme }) +} + +// ------------------------------------ +// Selectors +// ------------------------------------ + +const themeSelectors = {} +const currentThemeSelector = state => state.theme.currentTheme +const themesSelector = state => state.theme.themes + +themeSelectors.themes = createSelector(themesSelector, themes => themes) +themeSelectors.currentTheme = createSelector(currentThemeSelector, currentTheme => currentTheme) +themeSelectors.currentThemeSettings = createSelector( + themesSelector, + currentThemeSelector, + (themes, currentTheme) => themes[currentTheme] +) + +export { themeSelectors } + +// ------------------------------------ +// Reducer +// ------------------------------------ + +const initialState = { + currentTheme: store.get('theme', DEFAULT_THEME), + themes: { dark, light } +} + +export default function themeReducer(state = initialState, action) { + const handler = ACTION_HANDLERS[action.type] + + return handler ? handler(state, action) : state +} diff --git a/app/styles/app.global.scss b/app/styles/app.global.scss index 20f40f49..657e934a 100644 --- a/app/styles/app.global.scss +++ b/app/styles/app.global.scss @@ -74,96 +74,6 @@ input[type=number] { } } -// buttons - -.buttonPrimary, -.buttonSecondary { - -webkit-user-select: none; - cursor: pointer; - display: block; - padding: 18px 30px 15px; - border-radius: 2px; - text-align: center; - font-size: 16px; - letter-spacing: 1.5px; - transition: none; - position: relative; - color: $white; -} - -.buttonPrimary { - background-color: $main; - box-shadow: 0 3px 0 0 darken($main, 10%); - - &:active { - box-shadow: inset 0 1px 1px 1px darken($main, 10%); - } -} - -.buttonPrimary.inactive { - opacity: 0.5; - cursor: auto; -} - -.buttonPrimary.inactive:active { - box-shadow: 0 3px 0 0 darken($main, 10%); - transform: none; -} - -.buttonSecondary { - background-color: $secondary; - box-shadow: 0 3px 0 0 darken($secondary, 10%); - - &:active { - box-shadow: inset 0 1px 1px 1px darken($secondary, 10%); - } -} - -.buttonPrimary:active, -.buttonSecondary:active { - transform: translate(0, 3px); - outline: 0; -} - -.buttonContainer.circleContainer { - display: inline-block; - width: auto; - min-width: 0; - background: none; - border: none; - box-shadow: none; -} - -.buttonContainer .circle { - display: inline-block; - border-radius: 50px; -} - -.buttonContainer .circle.small { - width: 50px; - padding: 10px 0; -} - -.buttonContainer.small { - min-width: auto; - padding: 0; - border: none; - margin: 0 auto; - width: 80%; -} - -.buttonContainer .small.active { - box-shadow: inset 0 1px 1px 1px #1f4b2e; - transform: translate(0, 3px); - outline: 0; - background: #002280; -} - -.buttonContainer.small .buttonPrimary { - padding: 10px 5px; - font-size: 15px; -} - // network @keyframes dash { diff --git a/app/translations/en.json b/app/translations/en.json index c59908eb..15348ab5 100644 --- a/app/translations/en.json +++ b/app/translations/en.json @@ -99,6 +99,7 @@ "components.Onboarding.FormContainer.next": "Next", "components.Onboarding.Login.password_placeholder": "Password", "components.Onboarding.Login.unlock": "Unlock", + "components.Onboarding.Login.unlocking": "Unlocking", "components.Onboarding.NewWalletPassword.password_confirm_placeholder": "Confirm Password", "components.Onboarding.NewWalletPassword.password_error_length": "Password must be at least {passwordMinLength} characters long", "components.Onboarding.NewWalletPassword.password_error_match": "Passwords do not match", @@ -145,6 +146,10 @@ "components.Settings.Locale.title": "Language", "components.Settings.Menu.fiat": "Fiat Currency", "components.Settings.Menu.locale": "Language", + "components.Settings.Menu.theme": "Theme", + "components.Settings.Theme.dark": "Dark", + "components.Settings.Theme.light": "Light", + "components.Settings.Theme.title": "Theme", "components.Wallet.ReceiveModal.bitcoin_address": "Bitcoin Address", "components.Wallet.ReceiveModal.copy_address": "Copy address", "components.Wallet.ReceiveModal.copy_pubkey": "Copy Pubkey", diff --git a/test/unit/components/Form/Pay.spec.js b/test/unit/components/Form/Pay.spec.js index 8afeab72..be1881fe 100644 --- a/test/unit/components/Form/Pay.spec.js +++ b/test/unit/components/Form/Pay.spec.js @@ -1,9 +1,9 @@ import React from 'react' import { configure } from 'enzyme' import Adapter from 'enzyme-adapter-react-16' - +import 'jest-styled-components' import Pay from 'components/Form/Pay' - +import { dark as theme } from 'themes' import { mountWithIntl } from '../../__helpers__/intl-enzyme-test-helper' configure({ adapter: new Adapter() }) @@ -48,7 +48,7 @@ const defaultProps = { describe('Form', () => { describe('should show the form without an input', () => { - const el = mountWithIntl() + const el = mountWithIntl() it('should contain Pay', () => { expect(el.find('input#paymentRequest').props.value).toBe(undefined) @@ -57,7 +57,7 @@ describe('Form', () => { describe('should show lightning with a lightning input', () => { const props = { ...defaultProps, isLn: true } - const el = mountWithIntl() + const el = mountWithIntl() it('should contain Pay', () => { expect(el.find('input#paymentRequest').props.value).toBe(undefined) @@ -66,7 +66,7 @@ describe('Form', () => { describe('should show on-chain with an on-chain input', () => { const props = { ...defaultProps, isOnchain: true } - const el = mountWithIntl() + const el = mountWithIntl() it('should contain Pay', () => { expect(el.find('input#paymentRequest').props.value).toBe(undefined) diff --git a/test/unit/reducers/__snapshots__/settings.spec.js.snap b/test/unit/reducers/__snapshots__/settings.spec.js.snap index 2a8690e9..1006c724 100644 --- a/test/unit/reducers/__snapshots__/settings.spec.js.snap +++ b/test/unit/reducers/__snapshots__/settings.spec.js.snap @@ -4,7 +4,6 @@ exports[`reducers settingsReducer should correctly disableSubmenu 1`] = ` Object { "activeSubMenu": null, "settingsOpen": false, - "theme": "dark", } `; @@ -12,7 +11,6 @@ exports[`reducers settingsReducer should correctly setActiveSubmenu 1`] = ` Object { "activeSubMenu": true, "settingsOpen": false, - "theme": "dark", } `; @@ -20,7 +18,6 @@ exports[`reducers settingsReducer should correctly setSettingsOpen 1`] = ` Object { "activeSubMenu": null, "settingsOpen": true, - "theme": "dark", } `; @@ -28,6 +25,5 @@ exports[`reducers settingsReducer should handle initial state 1`] = ` Object { "activeSubMenu": null, "settingsOpen": false, - "theme": "dark", } `;