Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SDK: Try wp.data with calypso state tree "rewrite" #26930

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 20 additions & 13 deletions client/blocks/color-scheme-picker/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@
*/
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { translate } from 'i18n-calypso';
import { compose } from '@wordpress/compose';
import { withDispatch, withSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import QueryPreferences from 'components/data/query-preferences';
import { savePreference, setPreference } from 'state/preferences/actions';
import { getPreference } from 'state/preferences/selectors';
import getColorSchemesData from './constants';
import FormRadiosBar from 'components/forms/form-radios-bar';

Expand Down Expand Up @@ -49,16 +48,24 @@ class ColorSchemePicker extends PureComponent {
}
}

const saveColorSchemePreference = ( preference, temporarySelection ) =>
temporarySelection
? setPreference( 'colorScheme', preference )
: savePreference( 'colorScheme', preference );
export default compose( [
withSelect( select => {
const { getPreference } = select( 'preferences' );
return {
colorSchemePreference: getPreference( 'colorScheme' ),
};
} ),
withDispatch( dispatch => {
const { setPreference, savePreference } = dispatch( 'preferences' );

export default connect(
state => {
return {
colorSchemePreference: getPreference( state, 'colorScheme' ),
saveColorSchemePreference( preference, temporarySelection ) {
if ( temporarySelection ) {
setPreference( 'colorScheme', preference );
} else {
savePreference( 'colorScheme', preference );
}
},
};
},
{ saveColorSchemePreference }
)( ColorSchemePicker );
} ),
] )( ColorSchemePicker );
11 changes: 10 additions & 1 deletion client/boot/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
/**
* External dependencies
*/

import debugFactory from 'debug';
import page from 'page';
import { parse } from 'qs';
import { createRegistry } from '@wordpress/data';
import { some, startsWith } from 'lodash';
import url from 'url';

Expand All @@ -26,6 +26,7 @@ import { getSections } from 'sections-helper';
import { checkFormHandler } from 'lib/protect-form';
import notices from 'notices';
import authController from 'auth/controller';
import dataStore from 'state/store';

const debug = debugFactory( 'calypso' );

Expand Down Expand Up @@ -85,6 +86,13 @@ const setupContextMiddleware = reduxStore => {
} );
};

export const setupWordPressDataStore = () => {
page( '*', ( context, next ) => {
context.wpRegistry = createRegistry( dataStore );
next();
} );
};

// We need to require sections to load React with i18n mixin
const loadSectionsMiddleware = () => setupRoutes();

Expand Down Expand Up @@ -215,6 +223,7 @@ export const setupMiddlewares = ( currentUser, reduxStore ) => {

installPerfmonPageHandlers();
setupContextMiddleware( reduxStore );
setupWordPressDataStore();
oauthTokenMiddleware();
loadSectionsMiddleware();
loggedOutMiddleware( currentUser );
Expand Down
71 changes: 25 additions & 46 deletions client/boot/project/wordpress-com.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
* External dependencies
*/
import { startsWith } from 'lodash';
import React from 'react';
import ReactDom from 'react-dom';
import store from 'store';
import page from 'page';
import debugFactory from 'debug';
Expand All @@ -31,22 +29,9 @@ import { setNextLayoutFocus, activateNextLayoutFocus } from 'state/ui/layout-foc
import Logger from 'lib/catch-js-errors';
import setupMySitesRoute from 'my-sites';
import setupGlobalKeyboardShortcuts from 'lib/keyboard-shortcuts/global';
import * as controller from 'controller';

const debug = debugFactory( 'calypso' );

function renderLayout( reduxStore ) {
const Layout = controller.ReduxWrappedLayout;

const layoutElement = React.createElement( Layout, {
store: reduxStore,
} );

ReactDom.render( layoutElement, document.getElementById( 'wpcom' ) );

debug( 'Main layout rendered.' );
}

export const configureReduxStore = ( currentUser, reduxStore ) => {
debug( 'Executing WordPress.com configure Redux store.' );

Expand Down Expand Up @@ -74,38 +59,32 @@ export function setupMiddlewares( currentUser, reduxStore ) {
analytics.setSuperProps( superProps );
}

// Render Layout only for non-isomorphic sections.
// Isomorphic sections will take care of rendering their Layout last themselves.
if ( ! document.getElementById( 'primary' ) ) {
renderLayout( reduxStore );

if ( config.isEnabled( 'catch-js-errors' ) ) {
const errorLogger = new Logger();
//Save errorLogger to a singleton for use in arbitrary logging.
require( 'lib/catch-js-errors/log' ).registerLogger( errorLogger );
//Save data to JS error logger
errorLogger.saveDiagnosticData( {
user_id: currentUser.get().ID,
calypso_env: config( 'env_id' ),
} );
errorLogger.saveDiagnosticReducer( function() {
const state = reduxStore.getState();
return {
blog_id: getSelectedSiteId( state ),
calypso_section: getSectionName( state ),
};
} );
errorLogger.saveDiagnosticReducer( () => ( { tests: getSavedVariations() } ) );
analytics.on( 'record-event', ( eventName, eventProperties ) =>
errorLogger.saveExtraData( { lastTracksEvent: eventProperties } )
if ( config.isEnabled( 'catch-js-errors' ) && ! document.getElementById( 'primary' ) ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code seems extraneous to this PR's purpose. If it's a separate improvement, would it make sense to put it in a standalone PR?

Copy link
Contributor Author

@ockham ockham Aug 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's in #26944 for separate review, but I'm leaving it included on this branch since it wouldn't work otherwise. (tl;dr this extraneous -- and obsolete -- empty Layout would leave wpRegistry undefined upon first render of RegistryProvider, and that breaks things quite badly).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. If you want them reviewed separately, you may want to switch the base of this PR to that branch then.

const errorLogger = new Logger();
//Save errorLogger to a singleton for use in arbitrary logging.
require( 'lib/catch-js-errors/log' ).registerLogger( errorLogger );
//Save data to JS error logger
errorLogger.saveDiagnosticData( {
user_id: currentUser.get().ID,
calypso_env: config( 'env_id' ),
} );
errorLogger.saveDiagnosticReducer( function() {
const state = reduxStore.getState();
return {
blog_id: getSelectedSiteId( state ),
calypso_section: getSectionName( state ),
};
} );
errorLogger.saveDiagnosticReducer( () => ( { tests: getSavedVariations() } ) );
analytics.on( 'record-event', ( eventName, eventProperties ) =>
errorLogger.saveExtraData( { lastTracksEvent: eventProperties } )
);
page( '*', function( context, next ) {
errorLogger.saveNewPath(
context.canonicalPath.replace( getSiteFragment( context.canonicalPath ), ':siteId' )
);
page( '*', function( context, next ) {
errorLogger.saveNewPath(
context.canonicalPath.replace( getSiteFragment( context.canonicalPath ), ':siteId' )
);
next();
} );
}
next();
} );
}

// If `?sb` or `?sp` are present on the path set the focus of layout
Expand Down
17 changes: 10 additions & 7 deletions client/controller/index.web.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
*/
import React from 'react';
import ReactDom from 'react-dom';
import { Provider as ReduxProvider } from 'react-redux';
import page from 'page';
import { Provider as ReduxProvider } from 'react-redux';
import { RegistryProvider } from '@wordpress/data';

/**
* Internal Dependencies
Expand All @@ -27,13 +28,15 @@ export { setSection, setUpLocale } from './shared.js';

const user = userFactory();

export const ReduxWrappedLayout = ( { store, primary, secondary, redirectUri } ) => (
export const ReduxWrappedLayout = ( { primary, redirectUri, secondary, store, wpRegistry } ) => (
<ReduxProvider store={ store }>
{ getCurrentUser( store.getState() ) ? (
<Layout primary={ primary } secondary={ secondary } user={ user } />
) : (
<LayoutLoggedOut primary={ primary } secondary={ secondary } redirectUri={ redirectUri } />
) }
<RegistryProvider value={ wpRegistry }>
{ getCurrentUser( store.getState() ) ? (
<Layout primary={ primary } secondary={ secondary } user={ user } />
) : (
<LayoutLoggedOut primary={ primary } secondary={ secondary } redirectUri={ redirectUri } />
) }
</RegistryProvider>
</ReduxProvider>
);

Expand Down
7 changes: 4 additions & 3 deletions client/controller/shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,17 @@ import isRTL from 'state/selectors/is-rtl';

export function makeLayoutMiddleware( LayoutComponent ) {
return ( context, next ) => {
const { store, primary, secondary } = context;
const { primary, secondary, store, wpRegistry } = context;

// On server, only render LoggedOutLayout when logged-out.
if ( ! context.isServerSide || ! getCurrentUser( context.store.getState() ) ) {
context.layout = (
<LayoutComponent
store={ store }
primary={ primary }
secondary={ secondary }
redirectUri={ context.originalUrl }
secondary={ secondary }
store={ store }
wpRegistry={ wpRegistry }
/>
);
}
Expand Down
38 changes: 23 additions & 15 deletions client/layout/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import React from 'react';
import createReactClass from 'create-react-class';
import { connect } from 'react-redux';
import classnames from 'classnames';
import { compose } from '@wordpress/compose';
import { withSelect } from '@wordpress/data';

/**
* Internal dependencies
Expand Down Expand Up @@ -43,7 +45,6 @@ import DocumentHead from 'components/data/document-head';
import NpsSurveyNotice from 'layout/nps-survey-notice';
import AppBanner from 'blocks/app-banner';
import GdprBanner from 'blocks/gdpr-banner';
import { getPreference } from 'state/preferences/selectors';
import JITM from 'blocks/jitm';
import KeyboardShortcutsMenu from 'lib/keyboard-shortcuts/menu';
import SupportUser from 'support/support-user';
Expand Down Expand Up @@ -164,17 +165,24 @@ const Layout = createReactClass( {
},
} );

export default connect( state => {
const { isLoading, section } = state.ui;
return {
masterbarIsHidden: ! masterbarIsVisible( state ),
isLoading,
isSupportUser: state.support.isSupportUser,
section,
hasSidebar: hasSidebar( state ),
isOffline: isOffline( state ),
currentLayoutFocus: getCurrentLayoutFocus( state ),
chatIsOpen: isHappychatOpen( state ),
colorSchemePreference: getPreference( state, 'colorScheme' ),
};
} )( Layout );
export default compose( [
connect( state => {
const { isLoading, section } = state.ui;
return {
masterbarIsHidden: ! masterbarIsVisible( state ),
isLoading,
isSupportUser: state.support.isSupportUser,
section,
hasSidebar: hasSidebar( state ),
isOffline: isOffline( state ),
currentLayoutFocus: getCurrentLayoutFocus( state ),
chatIsOpen: isHappychatOpen( state ),
};
} ),
withSelect( select => {
const { getPreference } = select( 'preferences' );
return {
colorSchemePreference: getPreference( 'colorScheme' ),
};
} ),
] )( Layout );
4 changes: 2 additions & 2 deletions client/state/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ import plugins from './plugins/reducer';
import postFormats from './post-formats/reducer';
import posts from './posts/reducer';
import postTypes from './post-types/reducer';
import preferences from './preferences/reducer';
//import preferences from './preferences/reducer';
import productsList from './products-list/reducer';
import pushNotifications from './push-notifications/reducer';
import purchases from './purchases/reducer';
Expand Down Expand Up @@ -163,7 +163,7 @@ const reducers = {
postFormats,
posts,
postTypes,
preferences,
//preferences,
productsList,
purchases,
pushNotifications,
Expand Down
19 changes: 9 additions & 10 deletions client/state/preferences/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,21 @@ import { get, find, has } from 'lodash';
*/
import { DEFAULT_PREFERENCE_VALUES } from './constants';

export const isFetchingPreferences = state => !! state.preferences.fetching;
export const isFetchingPreferences = state => !! state.fetching;

/**
* Returns the preference value associated with the specified key. Attempts to
* find in local and remote preferences, then any applicable default value,
* otherwise returning null.
*
* @param {Object} state Global state tree
* @param {Object} state Preferences state tree
* @param {String} key Preference key
* @return {*} Preference value
*/
export function getPreference( state, key ) {
return get(
find(
[ state.preferences.localValues, state.preferences.remoteValues, DEFAULT_PREFERENCE_VALUES ],
source => has( source, key )
find( [ state.localValues, state.remoteValues, DEFAULT_PREFERENCE_VALUES ], source =>
has( source, key )
),
key,
null
Expand All @@ -38,22 +37,22 @@ export function getPreference( state, key ) {
* of the object are each preference key and the values are the preference
* values.
*
* @param {Object} state Global state tree
* @param {Object} state Preferences state tree
* @return {Object} Preference value
*/
export function getAllRemotePreferences( state ) {
return state.preferences.remoteValues;
return state.remoteValues;
}

export const preferencesLastFetchedTimestamp = state => state.preferences.lastFetchedTimestamp;
export const preferencesLastFetchedTimestamp = state => state.lastFetchedTimestamp;

/**
* Returns true if preferences have been received from the remote source, or
* false otherwise.
*
* @param {Object} state Global state tree
* @param {Object} state Preferences state tree
* @return {Boolean} Whether preferences have been received
*/
export function hasReceivedRemotePreferences( state ) {
return !! state.preferences.remoteValues;
return !! state.remoteValues;
}
21 changes: 21 additions & 0 deletions client/state/preferences/store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/** @format */

/**
* Internal dependencies
*/
import reducer from './reducer';
import { fetchPreferences, setPreference, savePreference } from './actions';
import { getPreference, isFetchingPreferences } from './selectors';

export default {
reducer,
selectors: {
getPreference,
isFetchingPreferences,
},
actions: {
fetchPreferences,
setPreference,
savePreference,
},
};
Loading