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

Gutenlypso: Disable the Publish button if the email is unverified #29594

Merged
merged 7 commits into from
Dec 20, 2018
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
/** @format */
/**
* External Dependencies
*/
import React, { Component, Fragment } from 'react';
import { get } from 'lodash';
import { translate } from 'i18n-calypso';
mmtr marked this conversation as resolved.
Show resolved Hide resolved
import { connect } from 'react-redux';

/**
* WordPress dependencies.
*/
import { compose, withState } from '@wordpress/compose';
import { withDispatch, withSelect } from '@wordpress/data';
import { PostPublishButton } from '@wordpress/editor';
import { withViewportMatch } from '@wordpress/viewport';

/**
* Internal dependencies
*/
import VerifyEmailDialog from 'components/email-verification/email-verification-dialog';
import { getSelectedSiteId } from 'state/ui/selectors';
import { isCurrentUserEmailVerified } from 'state/current-user/selectors';
import isUnlaunchedSite from 'state/selectors/is-unlaunched-site';
import isVipSite from 'state/selectors/is-vip-site';

export class PostPublishButtonOrToggle extends Component {
componentDidMount() {
this.togglePostSaving();
}

componentDidUpdate( prevProps ) {
const { userNeedsVerification, verificationNoticeLabel } = this.props;
if (
userNeedsVerification !== prevProps.userNeedsVerification ||
verificationNoticeLabel !== prevProps.verificationNoticeLabel
mmtr marked this conversation as resolved.
Show resolved Hide resolved
) {
this.togglePostSaving();
}
}

togglePostSaving() {
mmtr marked this conversation as resolved.
Show resolved Hide resolved
const {
userNeedsVerification,
lockPostSaving,
unlockPostSaving,
createWarningNotice,
setState,
verificationNoticeLabel,
} = this.props;

const lockName = 'blockEditorPostSavingLock';

if ( userNeedsVerification ) {
lockPostSaving( lockName );

createWarningNotice( verificationNoticeLabel, {
id: 'verify-email-notice',
isDismissible: false,
actions: [
{
label: translate( 'Learn More' ),
url: '#',
onClick: e => {
e.preventDefault();
setState( { showEmailVerificationNotice: true } );
},
},
],
} );
} else {
unlockPostSaving( lockName );
}
}

closeVerifyEmailDialog = () => {
this.props.setState( { showEmailVerificationNotice: false } );
};

render() {
const {
forceIsDirty,
forceIsSaving,
hasPublishAction,
isBeingScheduled,
isLessThanMediumViewport,
isPending,
isPublished,
isPublishSidebarEnabled,
isPublishSidebarOpened,
isScheduled,
togglePublishSidebar,
showEmailVerificationNotice,
} = this.props;

const IS_TOGGLE = 'toggle';
const IS_BUTTON = 'button';
let component;

/**
* Conditions to show a BUTTON (publish directly) or a TOGGLE (open publish sidebar):
*
* 1) We want to show a BUTTON when the post status is at the _final stage_
* for a particular role (see https://codex.wordpress.org/Post_Status):
*
* - is published
* - is scheduled to be published
* - is pending and can't be published (but only for viewports >= medium).
* Originally, we considered showing a button for pending posts that couldn't be published
* (for example, for an author with the contributor role). Some languages can have
* long translations for "Submit for review", so given the lack of UI real estate available
* we decided to take into account the viewport in that case.
* See: https://github.com/WordPress/gutenberg/issues/10475
*
* 2) Then, in small viewports, we'll show a TOGGLE.
*
* 3) Finally, we'll use the publish sidebar status to decide:
*
* - if it is enabled, we show a TOGGLE
* - if it is disabled, we show a BUTTON
*/
if (
isPublished ||
( isScheduled && isBeingScheduled ) ||
( isPending && ! hasPublishAction && ! isLessThanMediumViewport )
) {
component = IS_BUTTON;
} else if ( isLessThanMediumViewport ) {
component = IS_TOGGLE;
} else if ( isPublishSidebarEnabled ) {
component = IS_TOGGLE;
} else {
component = IS_BUTTON;
}

return (
<Fragment>
<PostPublishButton
forceIsDirty={ forceIsDirty }
forceIsSaving={ forceIsSaving }
isOpen={ isPublishSidebarOpened }
isToggle={ component === IS_TOGGLE }
onToggle={ togglePublishSidebar }
/>
{ showEmailVerificationNotice && (
mmtr marked this conversation as resolved.
Show resolved Hide resolved
<VerifyEmailDialog onClose={ this.closeVerifyEmailDialog } />
) }
</Fragment>
);
}
}

const applyWithSelect = withSelect( select => {
mmtr marked this conversation as resolved.
Show resolved Hide resolved
const {
getCurrentPost,
isEditedPostBeingScheduled,
isCurrentPostPending,
isCurrentPostPublished,
isPublishSidebarEnabled,
isCurrentPostScheduled,
isPostSavingLocked,
} = select( 'core/editor' );

const { isPublishSidebarOpened } = select( 'core/edit-post' );

const isPublished = isCurrentPostPublished();
const isBeingScheduled = isEditedPostBeingScheduled();

let verificationNoticeLabel;
if ( isPublished ) {
verificationNoticeLabel = translate( 'To update, check your email and confirm your address.' );
} else if ( isBeingScheduled ) {
verificationNoticeLabel = translate(
'To schedule, check your email and confirm your address.'
);
} else {
verificationNoticeLabel = translate( 'To publish, check your email and confirm your address.' );
}

return {
hasPublishAction: get( getCurrentPost(), [ '_links', 'wp:action-publish' ], false ),
isBeingScheduled,
isPending: isCurrentPostPending(),
isPublished,
isPublishSidebarEnabled: isPublishSidebarEnabled(),
isPublishSidebarOpened: isPublishSidebarOpened(),
isScheduled: isCurrentPostScheduled(),
isPostSavingLocked: isPostSavingLocked(),
verificationNoticeLabel,
};
} );

const applyWithDispatch = withDispatch( dispatch => {
const { togglePublishSidebar } = dispatch( 'core/edit-post' );
const { createWarningNotice } = dispatch( 'core/notices' );
const { lockPostSaving, unlockPostSaving } = dispatch( 'core/editor' );
return {
togglePublishSidebar,
createWarningNotice,
lockPostSaving,
unlockPostSaving,
};
} );

export default compose(
applyWithSelect,
applyWithDispatch,
withViewportMatch( { isLessThanMediumViewport: '< medium' } ),
withState( {
showEmailVerificationNotice: false,
mmtr marked this conversation as resolved.
Show resolved Hide resolved
} ),
connect( state => {
const siteId = getSelectedSiteId( state );

// do not allow publish for unverified e-mails, but allow if the site is VIP, or if the site is unlaunched
const userNeedsVerification =
! isCurrentUserEmailVerified( state ) &&
! isVipSite( state, siteId ) &&
! isUnlaunchedSite( state, siteId );
Copy link
Contributor

Choose a reason for hiding this comment

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

Testing the PR I've got into a funny case that somehow "breaks" these checks.

  • Register new user.
  • Keep the email unverified.
  • Create a new post.
  • Publish it: there are no unverified email warnings, as the site is "unlaunched".

This is correct: we allow unverified users to publish, as long as their site is not yet public.

Though, to launch a site, the user first needs to verify their email.

So, I wonder: when can it happen that a user has both a verified email and an unlaunched site—or viceversa?
Those two cases would be the only two that would enable the whole userNeedsVerification flow, with the warning and the dialog, etc. (in both Gutenberg and Classic)

cc @scruffian for adding isUnlaunchedSite to the equivalent of this in EditorGroundControl in #27962.

Copy link
Member

Choose a reason for hiding this comment

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

It's possible for a user to have an unlaunched site and a verified email address. Since the process to launch a site would be to first verify the email address and then launch the site, any users who are part way through that process will be in that state.

The reverse state should be impossible - we should never allow users with unverified email addresses to launch a site.

Does that answer your question?

Copy link
Contributor

Choose a reason for hiding this comment

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

Absolutely yes, thank you very much @scruffian!
I didn't proceed with the test because I'm that lazy to keep the user email unverified, and somehow just assumed verifying the email would also automatically launch (which would definitely be odd).

Copy link
Contributor

Choose a reason for hiding this comment

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

(Sorry for the delay from my previous answer, I just now got to test it)

So, what happens then is that:

  • Verified user can have launched and unlaunched sites.
  • Unverified user can only have unlaunched sites.

In other words, this would never be true:

const userNeedsVerification =
	! isCurrentUserEmailVerified( state ) &&
	! isUnlaunchedSite( state, siteId );

In other words, we don't need the unverified email notice and the logic that prevents publishing a post, for both Guten and Classic.

Am I wrong on this?
Like, it's almost EOD and I've got the flu, I might very well have missed something. 😄

Copy link
Member Author

Choose a reason for hiding this comment

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

Unverified user can only have unlaunched sites.

I think this is not true. According to #27962, only private sites are unlaunched, so an unverified user can have a launched site if it is public.


return {
userNeedsVerification,
};
} )
)( PostPublishButtonOrToggle );
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import HeaderToolbar from 'gutenberg/editor/components/header/header-toolbar'; /
import PostPreviewButton from 'gutenberg/editor/components/post-preview-button'; // GUTENLYPSO
import PinnedPlugins from './pinned-plugins';
import shortcuts from '../../keyboard-shortcuts';
import PostPublishButtonOrToggle from './post-publish-button-or-toggle';
import PostPublishButtonOrToggle from 'gutenberg/editor/components/post-publish-button-or-toggle'; // GUTENLYPSO

function Header( {
closeGeneralSidebar,
Expand Down

This file was deleted.