Source: changeListeners/syncUserSettings.js

/**
 * @module changeListeners/syncUserSettings
 */

/**
 * Creates an instance of the user settings sync change listener.
 *
 * This change listener syncs certain parts of the state tree to user
 * settings when they change.
 *
 * Used for:
 *
 * * Enabled state: If the previews are enabled or disabled.
 * * Preview count: When the user dwells on a link for long enough that
 *   a preview is shown, then their preview count will be incremented (see
 *   `reducers/eventLogging.js`, and is persisted to local storage.
 *
 * @param {ext.popups.UserSettings} userSettings
 * @return {ext.popups.ChangeListener}
 */
export default function syncUserSettings( userSettings ) {
	return ( oldState, newState ) => {
		Object.keys( newState.preview.enabled ).forEach( ( key ) => {
			syncIfChanged(
				oldState, newState, `preview.enabled.${ key }`,
				( value ) => {
					userSettings.storePreviewTypeEnabled( key, value );
				}
			);
		} );
	};
}

/**
 * Given a state tree, reducer and property, safely return the value of the
 * property if the reducer and property exist
 *
 * @param {Object} state tree
 * @param {string} path dot-separated path in the state tree
 * @return {*}
 */
function get( state, path ) {
	return path.split( '.' ).reduce(
		( element, key ) => element && element[ key ],
		state
	);
}

/**
 * Calls a sync function if the property prop on the property reducer on
 * the state trees has changed value.
 *
 * @param {Object} oldState
 * @param {Object} newState
 * @param {string} path dot-separated path in the state tree
 * @param {Function} sync function to be called with the newest value if
 * changed
 */
function syncIfChanged( oldState, newState, path, sync ) {
	const current = get( newState, path );
	if ( oldState && ( get( oldState, path ) !== current ) ) {
		sync( current );
	}
}