Skip to content

Commit

Permalink
Merge pull request #3682 from WordPress/add/busy-button-pubish-flow
Browse files Browse the repository at this point in the history
- Add "busy button" variation.
- Improve publishing flow with a busy button and label updates.
  • Loading branch information
mtias authored Nov 28, 2017
2 parents c575dd5 + ec4dc7c commit 8d3e230
Show file tree
Hide file tree
Showing 9 changed files with 284 additions and 6 deletions.
2 changes: 2 additions & 0 deletions components/button/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class Button extends Component {
isLarge,
isSmall,
isToggled,
isBusy,
className,
disabled,
...additionalProps
Expand All @@ -47,6 +48,7 @@ class Button extends Component {
'button-large': isLarge,
'button-small': isSmall,
'is-toggled': isToggled,
'is-busy': isBusy,
} );

const tag = href !== undefined && ! disabled ? 'a' : 'button';
Expand Down
20 changes: 20 additions & 0 deletions components/button/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,24 @@
&:focus {
@include button-style__focus-active;
}

&.is-busy,
&.is-busy[disabled] {
animation: components-button__busy-animation 2500ms infinite linear;
background-size: 100px 100% !important;
background-image: linear-gradient( -45deg, $light-gray-500 28%, $white 28%, $white 72%, $light-gray-500 72%) !important;
opacity: 1;
}

&.button-primary.is-busy,
&.button-primary.is-busy[disabled] {
color: $white !important;
background-size: 100px 100% !important;
background-image: linear-gradient( -45deg, $blue-medium-500 28%, $blue-dark-900 28%, $blue-dark-900 72%, $blue-medium-500 72%) !important;
border-color: $blue-dark-900 !important;
}
}

@keyframes components-button__busy-animation {
0% { background-position: 200px 0; }
}
13 changes: 13 additions & 0 deletions editor/components/post-publish-button/label.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,25 @@ import './style.scss';
import {
isCurrentPostPublished,
isEditedPostBeingScheduled,
isSavingPost,
isPublishingPost,
} from '../../selectors';

export function PublishButtonLabel( {
isPublished,
isBeingScheduled,
isSaving,
isPublishing,
user,
} ) {
const isContributor = user.data && ! user.data.capabilities.publish_posts;

if ( isPublishing ) {
return __( 'Publishing…' );
} else if ( isSaving ) {
return __( 'Updating…' );
}

if ( isContributor ) {
return __( 'Submit for Review' );
} else if ( isPublished ) {
Expand All @@ -41,6 +51,9 @@ const applyConnect = connect(
( state ) => ( {
isPublished: isCurrentPostPublished( state ),
isBeingScheduled: isEditedPostBeingScheduled( state ),
isSaving: isSavingPost( state ),
// Need a selector
isPublishing: isPublishingPost( state ),
} )
);

Expand Down
10 changes: 10 additions & 0 deletions editor/components/post-publish-button/test/label.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ describe( 'PublishButtonLabel', () => {
},
} );

it( 'should show publishing if publishing in progress', () => {
const label = PublishButtonLabel( { user, isPublishing: true } );
expect( label ).toBe( 'Publishing…' );
} );

it( 'should show updating if saving in progress', () => {
const label = PublishButtonLabel( { user, isSaving: true } );
expect( label ).toBe( 'Updating…' );
} );

it( 'should show publish if user unknown', () => {
const label = PublishButtonLabel( { user: {} } );
expect( label ).toBe( 'Publish' );
Expand Down
5 changes: 4 additions & 1 deletion editor/components/post-publish-with-dropdown/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ import {
isSavingPost,
isEditedPostSaveable,
isEditedPostPublishable,
isCurrentPostPublished,
} from '../../selectors';

function PostPublishWithDropdown( { isSaving, isPublishable, isSaveable } ) {
function PostPublishWithDropdown( { isSaving, isPublishable, isSaveable, isPublished } ) {
const isButtonEnabled = ! isSaving && isPublishable && isSaveable;

return (
Expand All @@ -34,6 +35,7 @@ function PostPublishWithDropdown( { isSaving, isPublishable, isSaveable } ) {
onClick={ onToggle }
aria-expanded={ isOpen }
disabled={ ! isButtonEnabled }
isBusy={ isSaving && isPublished }
>
<PostPublishButtonLabel />
<Dashicon icon="arrow-down" />
Expand All @@ -49,5 +51,6 @@ export default connect(
isSaving: isSavingPost( state ),
isSaveable: isEditedPostSaveable( state ),
isPublishable: isEditedPostPublishable( state ),
isPublished: isCurrentPostPublished( state ),
} ),
)( PostPublishWithDropdown );
4 changes: 4 additions & 0 deletions editor/components/post-publish-with-dropdown/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@
.wp-core-ui .editor-post-publish-with-dropdown__button.button-primary {
display: inline-flex;
align-items: center;

&.is-busy .dashicon {
display: none;
}
}
10 changes: 5 additions & 5 deletions editor/effects.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* External dependencies
*/
import { BEGIN, COMMIT, REVERT } from 'redux-optimist';
import { get, uniqueId, map, filter, some, castArray } from 'lodash';
import { get, map, filter, some, castArray } from 'lodash';

/**
* WordPress dependencies
Expand Down Expand Up @@ -54,6 +54,7 @@ import {
const SAVE_POST_NOTICE_ID = 'SAVE_POST_NOTICE_ID';
const TRASH_POST_NOTICE_ID = 'TRASH_POST_NOTICE_ID';
const SAVE_REUSABLE_BLOCK_NOTICE_ID = 'SAVE_REUSABLE_BLOCK_NOTICE_ID';
export const POST_UPDATE_TRANSACTION_ID = 'post-update';

export default {
REQUEST_POST_UPDATE( action, store ) {
Expand All @@ -66,12 +67,11 @@ export default {
content: getEditedPostContent( state ),
id: post.id,
};
const transactionId = uniqueId();

dispatch( {
type: 'UPDATE_POST',
edits: toSend,
optimist: { type: BEGIN, id: transactionId },
optimist: { type: BEGIN, id: POST_UPDATE_TRANSACTION_ID },
} );
dispatch( removeNotice( SAVE_POST_NOTICE_ID ) );
const Model = wp.api.getPostTypeModel( getCurrentPostType( state ) );
Expand All @@ -84,7 +84,7 @@ export default {
type: 'REQUEST_POST_UPDATE_SUCCESS',
previousPost: post,
post: newPost,
optimist: { type: COMMIT, id: transactionId },
optimist: { type: COMMIT, id: POST_UPDATE_TRANSACTION_ID },
} );
} ).fail( ( err ) => {
dispatch( {
Expand All @@ -95,7 +95,7 @@ export default {
} ),
post,
edits,
optimist: { type: REVERT, id: transactionId },
optimist: { type: REVERT, id: POST_UPDATE_TRANSACTION_ID },
} );
} );
},
Expand Down
52 changes: 52 additions & 0 deletions editor/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
keys,
without,
compact,
find,
} from 'lodash';
import createSelector from 'rememo';

Expand All @@ -21,6 +22,11 @@ import { serialize, getBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import { addQueryArgs } from '@wordpress/url';

/**
* Internal dependencies
*/
import { POST_UPDATE_TRANSACTION_ID } from './effects';

/***
* Module constants
*/
Expand Down Expand Up @@ -1072,3 +1078,49 @@ export function isSavingReusableBlock( state, ref ) {
export function getReusableBlocks( state ) {
return Object.values( state.reusableBlocks.data );
}

/**
* Returns state object prior to a specified optimist transaction ID, or `null`
* if the transaction corresponding to the given ID cannot be found.
*
* @param {Object} state Current global application state
* @param {Object} transactionId Optimist transaction ID
* @return {Object} Global application state prior to transaction
*/
export function getStateBeforeOptimisticTransaction( state, transactionId ) {
const transaction = find( state.optimist, ( entry ) => (
entry.beforeState &&
get( entry.action, [ 'optimist', 'id' ] ) === transactionId
) );

return transaction ? transaction.beforeState : null;
}

/**
* Returns true if the post is being published, or false otherwise
*
* @param {Object} state Global application state
* @return {Boolean} Whether post is being published
*/
export function isPublishingPost( state ) {
if ( ! isSavingPost( state ) ) {
return false;
}

// Saving is optimistic, so assume that current post would be marked as
// published if publishing
if ( ! isCurrentPostPublished( state ) ) {
return false;
}

// Use post update transaction ID to retrieve the state prior to the
// optimistic transaction
const stateBeforeRequest = getStateBeforeOptimisticTransaction(
state,
POST_UPDATE_TRANSACTION_ID
);

// Consider as publishing when current post prior to request was not
// considered published
return !! stateBeforeRequest && ! isCurrentPostPublished( stateBeforeRequest );
}
Loading

0 comments on commit 8d3e230

Please sign in to comment.