Skip to content

Commit

Permalink
State: Update counts state in response to post status changes
Browse files Browse the repository at this point in the history
  • Loading branch information
aduth committed Jun 20, 2016
1 parent 8cd146f commit 3359e3c
Show file tree
Hide file tree
Showing 3 changed files with 328 additions and 15 deletions.
1 change: 1 addition & 0 deletions client/state/action-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export const POST_COUNTS_RECEIVE = 'POST_COUNTS_RECEIVE';
export const POST_COUNTS_REQUEST = 'POST_COUNTS_REQUEST';
export const POST_COUNTS_REQUEST_FAILURE = 'POST_COUNTS_REQUEST_FAILURE';
export const POST_COUNTS_REQUEST_SUCCESS = 'POST_COUNTS_REQUEST_SUCCESS';
export const POST_COUNTS_RESET_INTERNAL_STATE = 'POST_COUNTS_RESET_INTERNAL_STATE';
export const POST_DELETE = 'POST_DELETE';
export const POST_DELETE_SUCCESS = 'POST_DELETE_SUCCESS';
export const POST_DELETE_FAILURE = 'POST_DELETE_FAILURE';
Expand Down
153 changes: 138 additions & 15 deletions client/state/posts/counts/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,28 @@
*/
import { combineReducers } from 'redux';
import merge from 'lodash/merge';
import pick from 'lodash/pick';
import includes from 'lodash/includes';
import omit from 'lodash/omit';
import get from 'lodash/get';

/**
* Internal dependencies
*/
import {
CURRENT_USER_ID_SET,
POST_COUNTS_RECEIVE,
POST_COUNTS_REQUEST,
POST_COUNTS_REQUEST_SUCCESS,
POST_COUNTS_REQUEST_FAILURE,
POST_COUNTS_RESET_INTERNAL_STATE,
POST_DELETE,
POSTS_RECEIVE,
SERIALIZE,
DESERIALIZE
} from 'state/action-types';
import { isValidStateWithSchema } from 'state/utils';
import { countsSchema } from './schema';
import { createReducer } from 'state/utils';

/**
* Returns the updated post types requesting state after an action has been
Expand Down Expand Up @@ -55,25 +63,140 @@ export function requesting( state = {}, action ) {
* @param {Object} action Action payload
* @return {Object} Updated state
*/
export function counts( state = {}, action ) {
switch ( action.type ) {
case POST_COUNTS_RECEIVE:
return merge( {}, state, {
[ action.siteId ]: {
[ action.postType ]: action.counts
}
} );
export const counts = ( () => {
let currentUserId;
let postStatuses = {};

case DESERIALIZE:
if ( isValidStateWithSchema( state, countsSchema ) ) {
return state;
/**
* Returns a serialized key to be used in tracking post status properties
*
* @param {Number} siteId Site ID
* @param {Number} postId Post ID
* @return {String} Serialized key
*/
function getPostStatusKey( siteId, postId ) {
return [ siteId, postId ].join();
}

/**
* Returns true if the status transition will result in the post being
* permanently deleted, which is derived by the post's current status and
* type.
*
* @param {String} nextStatus Post's next (transitioning) status
* @param {String} status Post's current status
* @param {String} type Post's type
* @return {Boolean} Whether post is to be permanently deleted
*/
function isPermanentlyDeleting( nextStatus, status, type ) {
if ( 'trash' !== nextStatus ) {
return false;
}

return 'trash' === status || ! includes( [ 'post', 'page' ], type );
}

/**
* Returns the updated post count state after transitioning a post to a new
* status.
*
* @param {Object} state Current state
* @param {Number} siteId Site ID
* @param {Number} postId Post ID
* @param {String} status Post status
* @return {Object} Updated state
*/
function transitionPostStateToStatus( state, siteId, postId, status ) {
if ( ! state[ siteId ] ) {
return state;
}

const postStatusKey = getPostStatusKey( siteId, postId );
const postStatus = postStatuses[ postStatusKey ];
if ( ! postStatus || ! state[ siteId ][ postStatus.type ] ) {
return state;
}

// Determine which count subkeys need to be updated, depending on
// whether the current user authored the post
const subKeys = [ 'all' ];
if ( postStatus.authorId === currentUserId ) {
subKeys.push( 'mine' );
}

const revisions = subKeys.reduce( ( memo, subKey ) => {
const subKeyCounts = state[ siteId ][ postStatus.type ][ subKey ];

memo[ subKey ] = {};

// Decrement count from the current status before transitioning
memo[ subKey ][ postStatus.status ] = ( subKeyCounts[ postStatus.status ] || 0 ) - 1;

// So long as we're not trashing an already trashed post or page,
// increment the count for the transitioned status
if ( ! isPermanentlyDeleting( status, postStatus.status, postStatus.type ) ) {
memo[ subKey ][ status ] = ( subKeyCounts[ status ] || 0 ) + 1;
}

return {};
return memo;
}, {} );

if ( isPermanentlyDeleting( status, postStatus.status, postStatus.type ) ) {
// If post is permanently deleted, omit from tracked statuses
postStatuses = omit( postStatuses, postStatusKey );
} else {
// Otherwise, update object to reflect new status
postStatus.status = status;
}

return merge( {}, state, {
[ siteId ]: {
[ postStatus.type ]: revisions
}
} );
}

return state;
}
return createReducer( {}, {
[ POST_COUNTS_RESET_INTERNAL_STATE ]: ( state ) => {
currentUserId = undefined;
postStatuses = {};

return state;
},
[ CURRENT_USER_ID_SET ]: ( state, action ) => {
currentUserId = action.userId;

return state;
},
[ POSTS_RECEIVE ]: ( state, action ) => {
action.posts.forEach( ( post ) => {
const postStatusKey = getPostStatusKey( post.site_ID, post.ID );
const postStatus = postStatuses[ postStatusKey ];

// If the post is known to us and the status has changed,
// update state to reflect change
if ( postStatus && post.status !== postStatus.status ) {
state = transitionPostStateToStatus( state, post.site_ID, post.ID, post.status );
}

postStatuses[ postStatusKey ] = pick( post, 'type', 'status' );
postStatuses[ postStatusKey ].authorId = get( post.author, 'ID' );
} );

return state;
},
[ POST_DELETE ]: ( state, action ) => {
return transitionPostStateToStatus( state, action.siteId, action.postId, 'trash' );
},
[ POST_COUNTS_RECEIVE ]: ( state, action ) => {
return merge( {}, state, {
[ action.siteId ]: {
[ action.postType ]: action.counts
}
} );
}
}, countsSchema );
} )();

export default combineReducers( {
requesting,
Expand Down
Loading

0 comments on commit 3359e3c

Please sign in to comment.