Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: fixed

Verbum Comments: explain why commenting is unavailable when login is required but the login options cannot be shown, instead of offering a form that will be rejected.
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ public function enqueue_assets() {
/* translators: %s is the name of the provider (WordPress, Facebook, Twitter) */
'Logged in via %s' => __( 'Logged in via %s', 'jetpack-mu-wpcom' ),
'Log out' => __( 'Log out', 'jetpack-mu-wpcom' ),
'Your browser is blocking the cookies needed to log in and comment here. Allow cookies in your privacy settings, then reload the page.' => __( 'Your browser is blocking the cookies needed to log in and comment here. Allow cookies in your privacy settings, then reload the page.', 'jetpack-mu-wpcom' ),
'Email' => __( 'Email', 'jetpack-mu-wpcom' ),
'(Address never made public)' => __( '(Address never made public)', 'jetpack-mu-wpcom'), // phpcs:ignore PEAR.Functions.FunctionCallSignature.SpaceBeforeCloseBracket
'Instantly' => __( 'Instantly', 'jetpack-mu-wpcom' ),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import type { SocialServiceName } from '../hooks/useSocialLogin';
const { mustLogIn, requireNameEmail, commentRegistration } = VerbumComments;
interface LoggedOutProps {
login: ( service: SocialServiceName ) => void;
canWeAccessCookies: boolean;
loginWindow: Window | null;
}

Expand Down Expand Up @@ -40,7 +39,7 @@ const getLoginCommentText = ( commentParent: Signal ) => {
return <span>{ defaultText }</span>;
};

export const LoggedOut = ( { login, canWeAccessCookies, loginWindow }: LoggedOutProps ) => {
export const LoggedOut = ( { login, loginWindow }: LoggedOutProps ) => {
const [ activeService, setActiveService ] = useState( '' );
const closeLoginPopupService = requireNameEmail && ! mustLogIn ? 'mail' : '';

Expand Down Expand Up @@ -82,72 +81,83 @@ export const LoggedOut = ( { login, canWeAccessCookies, loginWindow }: LoggedOut
setActiveService( service );
};

const { commentParent } = useContext( VerbumSignals );
const { canAccessCookies, commentParent, isCommentBlocked } = useContext( VerbumSignals );

return (
<div className="verbum-subscriptions logged-out">
<div className="verbum-subscriptions__wrapper">
<div className="verbum-subscriptions__login">
{ canWeAccessCookies && (
<>
<div className="verbum-subscriptions__login-header">
{ getLoginCommentText( commentParent ) }
</div>
{ ( canAccessCookies || mustLogIn ) && (
<div className="verbum-subscriptions__login-header">
{ getLoginCommentText( commentParent ) }
</div>
) }
{ isCommentBlocked.value && (
<p className="verbum-subscriptions__cookie-notice" role="status">
{ translate(
'Your browser is blocking the cookies needed to log in and comment here. Allow cookies in your privacy settings, then reload the page.'
) }
</p>
) }
{ canAccessCookies && (
<div
className={ clsx( 'verbum-logins', {
'logging-in': activeService,
} ) }
>
<div
className={ clsx( 'verbum-logins', {
'logging-in': activeService,
className={ clsx( 'verbum-logins__social-buttons', {
'show-form-content': ! mustLogIn,
} ) }
>
<div
className={ clsx( 'verbum-logins__social-buttons', {
'show-form-content': ! mustLogIn,
} ) }
>
{ Object.entries( serviceData ).map( ( [ service, value ] ) => {
// Don't show mail login if "Users must be registered and logged in to comment" enabled.
if ( mustLogIn && service === 'mail' ) {
// eslint-disable-next-line array-callback-return
return;
}

return (
<button
aria-label={ value.name }
type="button"
key={ service }
onClick={ e => handleClick( e, service ) }
className={ clsx( 'social-button', service, {
active: service === activeService,
} ) }
>
<value.icon />
</button>
);
} ) }
</div>
{ [ 'wordpress', 'facebook' ].includes( activeService ) && (
<div
className={ clsx( 'verbum-login__social-loading', {
'must-login': mustLogIn,
} ) }
>
<p></p>
{ Object.entries( serviceData ).map( ( [ service, value ] ) => {
// Don't show mail login if "Users must be registered and logged in to comment" enabled.
if ( mustLogIn && service === 'mail' ) {
// eslint-disable-next-line array-callback-return
return;
}

return (
<button
aria-label={ value.name }
type="button"
className="components-button is-link"
onClick={ () => {
setActiveService( closeLoginPopupService );
loginWindow?.close();
} }
key={ service }
onClick={ e => handleClick( e, service ) }
className={ clsx( 'social-button', service, {
active: service === activeService,
} ) }
>
{ translate( 'Cancel' ) }
<value.icon />
</button>
</div>
) }
);
} ) }
</div>
</>
{ [ 'wordpress', 'facebook' ].includes( activeService ) && (
<div
className={ clsx( 'verbum-login__social-loading', {
'must-login': mustLogIn,
} ) }
>
<p></p>
<button
type="button"
className="components-button is-link"
onClick={ () => {
setActiveService( closeLoginPopupService );
loginWindow?.close();
} }
>
{ translate( 'Cancel' ) }
</button>
</div>
) }
</div>
) }
<EmailForm shouldShowEmailForm={ activeService === 'mail' || ! canWeAccessCookies } />
<EmailForm
shouldShowEmailForm={
activeService === 'mail' || ( ! canAccessCookies && ! mustLogIn )
}
/>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,15 @@ import useFormMutations from './hooks/useFormMutations';
import useSocialLogin from './hooks/useSocialLogin';
import { translate } from './i18n';
import { createSignals, VerbumSignals } from './state';
import {
canWeAccessCookies,
setUserInfoCookie,
addWordPressDomain,
hasSubscriptionOptionsVisible,
} from './utils';
import { setUserInfoCookie, addWordPressDomain, hasSubscriptionOptionsVisible } from './utils';
import type { VerbumAppProps } from './types';

import './style.scss';

const Verbum = ( { siteId, parentForm }: VerbumAppProps ) => {
const {
hasOpenedTrayOnce,
isCommentBlocked,
isEmptyComment,
isSavingComment,
isTrayOpen,
Expand Down Expand Up @@ -160,6 +156,11 @@ const Verbum = ( { siteId, parentForm }: VerbumAppProps ) => {
};

const handleCommentSubmit = async ( event: Event ) => {
if ( isCommentBlocked.value ) {
event.preventDefault();
return;
}

window.removeEventListener( 'beforeunload', handleBeforeUnload );
if ( userInfo.value?.service === 'guest' ) {
if ( shouldStoreEmailData.value ) {
Expand Down Expand Up @@ -226,11 +227,7 @@ const Verbum = ( { siteId, parentForm }: VerbumAppProps ) => {
{ userLoggedIn.value ? (
<LoggedIn siteId={ siteId } toggleTray={ handleTrayToggle } logout={ logout! } />
) : (
<LoggedOut
login={ login! }
canWeAccessCookies={ canWeAccessCookies() }
loginWindow={ loginWindowRef ?? null }
/>
<LoggedOut login={ login! } loginWindow={ loginWindowRef ?? null } />
) }
</div>
<CommentFooter toggleTray={ handleTrayToggle } />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import type { Signal } from '@preact/signals';
* @return An object containing all the signals used in the app.
*/
export function createSignals() {
/*
* Cookie access can't change without a page reload, so probe once and share the answer.
*/
const canAccessCookies = canWeAccessCookies();

/*
* In userInfo we store the user data for logged-in users.
*/
Expand All @@ -22,7 +27,7 @@ export function createSignals() {
const userLoggedIn = computed( () => {
return (
VerbumComments.isJetpackCommentsLoggedIn ||
( canWeAccessCookies() &&
( canAccessCookies &&
userInfo.value &&
userInfo.value?.service !== 'guest' &&
userInfo.value?.service !== 'jetpack' )
Expand Down Expand Up @@ -77,12 +82,22 @@ export function createSignals() {
return ! mailLoginData.value.email || ! mailLoginData.value.author;
} );

/*
* Login is required but the login options can't render, so there is no way for this comment to
* be accepted. `mustLogIn` tracks the WordPress.com session, which is absent in the Jetpack
* iframe even for visitors logged in to the site itself, hence the userLoggedIn check.
*/
const isCommentBlocked = computed( () => {
return Boolean( VerbumComments.mustLogIn ) && ! userLoggedIn.value && ! canAccessCookies;
} );

/*
* Calculate if the reply button should be disabled. When we have no user data we check the shouldDisableReply value,
* otherwise we check if the comment is empty or saving.
*/
const isReplyDisabled = computed( () => {
return (
isCommentBlocked.value ||
( isAuthRequired() &&
! userLoggedIn.value &&
( isMailFormMissingInput.value || isMailFormInvalid.value ) ) ||
Expand Down Expand Up @@ -117,12 +132,14 @@ export function createSignals() {
const subscribeModalStatus: Signal< string | undefined > = signal( undefined );

return {
canAccessCookies,
userInfo,
userLoggedIn,
mailLoginData,
isTrayOpen,
hasOpenedTrayOnce,
commentValue,
isCommentBlocked,
isEmptyComment,
isSavingComment,
isMailFormInvalid,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,17 @@
padding-bottom: 16px;
}

// Matches the accent-bar treatment of .verbum-message below. Colors are
// pinned rather than themed so the notice stays legible in every scheme.
.verbum-subscriptions__cookie-notice {
margin-block: 0 16px;
padding: 8px 12px;
border-inline-start: 4px solid #f0b849;
background-color: #fef8ee;
color: #3c434a;
font-size: 14px;
}

.verbum-logins {
display: flex;
align-items: center;
Expand Down