diff --git a/client/components/data/current-theme/README.md b/client/components/data/current-theme/README.md deleted file mode 100644 index 5faa41e07acd8c..00000000000000 --- a/client/components/data/current-theme/README.md +++ /dev/null @@ -1,8 +0,0 @@ -CurrentThemeData -================ - -A component to decouple the fetching of a site's current theme from any rendering. - -## Usage - -A child component wrapped with `` will be passed the prop `currentTheme`, a theme object. diff --git a/client/components/data/current-theme/index.js b/client/components/data/current-theme/index.js deleted file mode 100644 index 8b9efe443232e9..00000000000000 --- a/client/components/data/current-theme/index.js +++ /dev/null @@ -1,65 +0,0 @@ -/** - * External dependencies - */ -import React from 'react'; -import { connect } from 'react-redux'; -import omit from 'lodash/omit'; - -/** - * Internal dependencies - */ -import { fetchCurrentTheme } from 'state/themes/actions'; -import { getCurrentTheme } from 'state/themes/current-theme/selectors'; - -/** - * Fetches the currently active theme of the supplied site - * and passes it to the supplied child component. - */ -const CurrentThemeData = React.createClass( { - - propTypes: { - children: React.PropTypes.element.isRequired, - site: React.PropTypes.oneOfType( [ - React.PropTypes.object, - React.PropTypes.bool - ] ).isRequired, - // Connected props - currentTheme: React.PropTypes.shape( { - name: React.PropTypes.string, - id: React.PropTypes.string, - cost: React.PropTypes.shape( { - currency: React.PropTypes.string.isRequired, - number: React.PropTypes.number.isRequired, - display: React.PropTypes.string - } ) - } ), - fetchCurrentTheme: React.PropTypes.func.isRequired - }, - - componentDidMount() { - this.refresh( this.props ); - }, - - componentWillReceiveProps( nextProps ) { - if ( nextProps.site && nextProps.site !== this.props.site ) { - this.refresh( nextProps ); - } - }, - - refresh( props ) { - if ( ! this.props.currentTheme && props.site ) { - this.props.fetchCurrentTheme( props.site ); - } - }, - - render() { - return React.cloneElement( this.props.children, omit( this.props, 'children' ) ); - } -} ); - -export default connect( - ( state, props ) => ( { - currentTheme: getCurrentTheme( state, props.site.ID ) - } ), - { fetchCurrentTheme } -)( CurrentThemeData ); diff --git a/client/components/data/query-current-theme/index.jsx b/client/components/data/query-current-theme/index.jsx new file mode 100644 index 00000000000000..4384b9ee691992 --- /dev/null +++ b/client/components/data/query-current-theme/index.jsx @@ -0,0 +1,52 @@ +/** @ssr-ready **/ + +/** + * External dependencies + */ +import { Component, PropTypes } from 'react'; +import { connect } from 'react-redux'; + +/** + * Internal dependencies + */ +import { fetchCurrentTheme } from 'state/themes/actions'; +import { isRequestingCurrentTheme } from 'state/themes/current-theme/selectors'; + +class QueryCurrentTheme extends Component { + + componentDidMount() { + const { requestingCurrentTheme, site } = this.props; + this.refresh( requestingCurrentTheme, site ); + } + + componentWillReceiveProps( nextProps ) { + if ( nextProps.site === this.props.site ) { + return; + } + const { requestingCurrentTheme, site } = nextProps; + this.refresh( requestingCurrentTheme, site ); + } + + refresh( requestingCurrentTheme, site ) { + if ( ! requestingCurrentTheme && site ) { + this.props.fetchCurrentTheme( site.ID ); + } + } + + render() { + return null; + } +} + +QueryCurrentTheme.propTypes = { + site: PropTypes.oneOfType( [ PropTypes.object, PropTypes.bool ] ), + requestingCurrentTheme: PropTypes.bool, + fetchCurrentTheme: PropTypes.func, +}; + +export default connect( + ( state, props ) => ( + { requestingCurrentTheme: isRequestingCurrentTheme( state, props.site && props.site.ID ) } + ), + { fetchCurrentTheme } +)( QueryCurrentTheme ); diff --git a/client/my-sites/themes/current-theme/index.jsx b/client/my-sites/themes/current-theme/index.jsx index 7de2f03e5136c3..85ed171158f248 100644 --- a/client/my-sites/themes/current-theme/index.jsx +++ b/client/my-sites/themes/current-theme/index.jsx @@ -3,6 +3,7 @@ */ var React = require( 'react' ), classNames = require( 'classnames' ); +import { connect } from 'react-redux'; /** * Internal dependencies @@ -10,6 +11,8 @@ var React = require( 'react' ), var Card = require( 'components/card' ), Helpers = require( '../helpers' ), CurrentThemeButton = require( './button' ); +import { getCurrentTheme } from 'state/themes/current-theme/selectors'; +import QueryCurrentTheme from 'components/data/query-current-theme'; /** * Show current active theme for a site, with @@ -31,12 +34,13 @@ var CurrentTheme = React.createClass( { render: function() { var currentTheme = this.props.currentTheme, placeholderText = loading..., - text = currentTheme ? currentTheme.name : placeholderText, + text = currentTheme && currentTheme.name ? currentTheme.name : placeholderText, site = this.props.site, displaySupportButton = Helpers.isPremium( currentTheme ) && ! site.jetpack; return ( +
{ this.translate( 'Current Theme' ) } @@ -70,4 +74,8 @@ var CurrentTheme = React.createClass( { } } ); -module.exports = CurrentTheme; +export default connect( + ( state, props ) => ( + { currentTheme: getCurrentTheme( state, props.site && props.site.ID ) } + ) +)( CurrentTheme ); diff --git a/client/my-sites/themes/single-site.jsx b/client/my-sites/themes/single-site.jsx index e597ebc9818d09..404218af560750 100644 --- a/client/my-sites/themes/single-site.jsx +++ b/client/my-sites/themes/single-site.jsx @@ -11,7 +11,6 @@ var React = require( 'react' ), * Internal dependencies */ var Main = require( 'components/main' ), - CurrentThemeData = require( 'components/data/current-theme' ), ActivatingTheme = require( 'components/data/activating-theme' ), Action = require( 'state/themes/actions' ), ThemePreview = require( './theme-preview' ), @@ -153,11 +152,9 @@ var ThemesSingleSite = React.createClass( { source={ 'list' } clearActivated={ bindActionCreators( Action.clearActivated, this.props.dispatch ) } /> - - - + { - wpcom.undocumented().activeTheme( site.ID ) + dispatch( { + type: THEME_REQUEST_CURRENT, + siteId, + } ); + + wpcom.undocumented().activeTheme( siteId ) .then( theme => { debug( 'Received current theme', theme ); dispatch( { type: THEME_RECEIVE_CURRENT, - site: site, + siteId, themeId: theme.id, themeName: theme.name, themeCost: theme.cost } ); - } ); // TODO: add .catch() error handler + } ).catch( error => { + dispatch( { + type: THEME_REQUEST_CURRENT_FAILURE, + siteId, + error, + } ); + } ); }; } diff --git a/client/state/themes/current-theme/reducer.js b/client/state/themes/current-theme/reducer.js index 9019d2ffc380b6..75b44bfd0c38ab 100644 --- a/client/state/themes/current-theme/reducer.js +++ b/client/state/themes/current-theme/reducer.js @@ -13,30 +13,39 @@ import { THEME_ACTIVATE, THEME_ACTIVATED, THEME_CLEAR_ACTIVATED, - THEME_RECEIVE_CURRENT + THEME_RECEIVE_CURRENT, + THEME_REQUEST_CURRENT, + THEME_REQUEST_CURRENT_FAILURE, } from 'state/action-types'; export const initialState = fromJS( { isActivating: false, hasActivated: false, - currentThemes: {} + currentThemes: {}, + requesting: {}, } ); export default ( state = initialState, action ) => { switch ( action.type ) { case THEME_RECEIVE_CURRENT: - return state.setIn( [ 'currentThemes', action.site.ID ], { + return state.setIn( [ 'currentThemes', action.siteId ], { name: action.themeName, id: action.themeId, - cost: action.themeCost - } ); + cost: action.themeCost, + } ) + .setIn( [ 'requesting', action.siteId ], false ); + case THEME_REQUEST_CURRENT: + return state.setIn( [ 'requesting', action.siteId ], true ); + case THEME_REQUEST_CURRENT_FAILURE: + //TODO: show notification + return state.setIn( [ 'requesting', action.siteId ], false ); case THEME_ACTIVATE: return state.set( 'isActivating', true ); case THEME_ACTIVATED: return state .set( 'isActivating', false ) .set( 'hasActivated', true ) - .setIn( [ 'currentThemes', action.site.ID ], action.theme ); + .setIn( [ 'currentThemes', action.siteId ], action.theme ); case THEME_CLEAR_ACTIVATED: return state.set( 'hasActivated', false ); case DESERIALIZE: diff --git a/client/state/themes/current-theme/selectors.js b/client/state/themes/current-theme/selectors.js index 7e9a0aeefc51da..b93f0c1c6829c3 100644 --- a/client/state/themes/current-theme/selectors.js +++ b/client/state/themes/current-theme/selectors.js @@ -11,3 +11,7 @@ export function isActivating( state ) { export function hasActivated( state ) { return state.themes.currentTheme.get( 'hasActivated' ); } + +export function isRequestingCurrentTheme( state, siteId ) { + return !! state.themes.currentTheme.get( 'requesting' ).get( siteId ); +}