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

Fix Verbum comments in Query Loop #40933

Merged
merged 5 commits into from
Jan 10, 2025
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

To support adding a comment form inside a query loop
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public function verbum_render_element() {
$color_scheme = 'transparent';
}

$verbum = '<div id="comment-form__verbum" class="' . $color_scheme . '"></div>' . $this->hidden_fields();
$verbum = '<div class="comment-form__verbum ' . $color_scheme . '"></div>' . $this->hidden_fields();

// If the blog requires login, Verbum need to be wrapped in a <form> to work.
// Verbum is given `mustLogIn` to handle the login flow.
Expand Down Expand Up @@ -535,8 +535,12 @@ public function add_verbum_meta_data( $comment_id ) {
* Get the hidden fields for the comment form.
*/
public function hidden_fields() {
// Ironically, get_queried_post_id doesn't work inside query loop.
// See: https://github.com/Automattic/wp-calypso/issues/98136
$queried_post = get_post();
$queried_post_id = $queried_post ? $queried_post->ID : 0;
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$post_id = isset( $_GET['postid'] ) ? intval( $_GET['postid'] ) : get_queried_object_id();
$post_id = isset( $_GET['postid'] ) ? intval( $_GET['postid'] ) : $queried_post_id;
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$is_current_user_subscribed = isset( $_GET['is_current_user_subscribed'] ) ? intval( $_GET['is_current_user_subscribed'] ) : 0;
$nonce = wp_create_nonce( 'highlander_comment' );
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { useContext, useCallback } from 'preact/hooks';
import { translate } from '../../i18n';
import { shouldStoreEmailData } from '../../state';
import { VerbumSignals } from '../../state';
import { ToggleControl } from '../ToggleControl';

const handleChange = ( e: boolean ) => {
shouldStoreEmailData.value = e;
};

export const EmailFormCookieConsent = () => {
const { shouldStoreEmailData } = useContext( VerbumSignals );

const handleChange = useCallback(
( e: boolean ) => {
shouldStoreEmailData.value = e;
},
[ shouldStoreEmailData ]
);

const label = (
<div className="verbum-toggle-control__label">
<p className="primary">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { signal, effect, batch, computed } from '@preact/signals';
import { effect, batch, useSignal, useComputed } from '@preact/signals';
import clsx from 'clsx';
import { useState, useEffect } from 'preact/hooks';
import { useState, useEffect, useContext } from 'preact/hooks';
import { translate } from '../../i18n';
import { Name, Website, Email } from '../../images';
import { mailLoginData, isMailFormInvalid, shouldStoreEmailData } from '../../state';
import { VerbumSignals } from '../../state';
import { getUserInfoCookie, isAuthRequired } from '../../utils';
import { NewCommentEmail } from '../new-comment-email';
import { NewPostsEmail } from '../new-posts-email';
Expand All @@ -15,32 +15,34 @@ interface EmailFormProps {
shouldShowEmailForm: boolean;
}

const isValidEmail = signal( true );
const isEmailTouched = signal( false );
const isNameTouched = signal( false );
const isValidAuthor = signal( true );
const userEmail = computed( () => mailLoginData.value.email || '' );
const userName = computed( () => mailLoginData.value.author || '' );
const userUrl = computed( () => mailLoginData.value.url || '' );
export const EmailForm = ( { shouldShowEmailForm }: EmailFormProps ) => {
const { mailLoginData, isMailFormInvalid, shouldStoreEmailData } = useContext( VerbumSignals );

const validateFormData = () => {
const emailRegex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
batch( () => {
isValidEmail.value =
Boolean( userEmail.value ) && Boolean( emailRegex.test( userEmail.value ) );
isValidAuthor.value = Boolean( userName.value.length > 0 );
} );
};
const isValidEmail = useSignal( true );
const isEmailTouched = useSignal( false );
const isNameTouched = useSignal( false );
const isValidAuthor = useSignal( true );
const userEmail = useComputed( () => mailLoginData.value.email || '' );
const userName = useComputed( () => mailLoginData.value.author || '' );
const userUrl = useComputed( () => mailLoginData.value.url || '' );

const setFormData = ( event: ChangeEvent< HTMLInputElement > ) => {
mailLoginData.value = {
...mailLoginData.peek(),
[ event.currentTarget.name ]: event.currentTarget.value,
const validateFormData = () => {
const emailRegex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
batch( () => {
isValidEmail.value =
Boolean( userEmail.value ) && Boolean( emailRegex.test( userEmail.value ) );
isValidAuthor.value = Boolean( userName.value.length > 0 );
} );
};

const setFormData = ( event: ChangeEvent< HTMLInputElement > ) => {
mailLoginData.value = {
...mailLoginData.peek(),
[ event.currentTarget.name ]: event.currentTarget.value,
};
validateFormData();
};
validateFormData();
};

export const EmailForm = ( { shouldShowEmailForm }: EmailFormProps ) => {
const { subscribeToComment, subscribeToBlog } = VerbumComments;
const [ emailNewComment, setEmailNewComment ] = useState( false );
const [ emailNewPosts, setEmailNewPosts ] = useState( false );
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#comment-form__verbum .verbum-subscriptions .verbum-form
.comment-form__verbum .verbum-subscriptions .verbum-form
{
.verbum-form__content {
// protect the button from style leaks from the site; reset all.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import clsx from 'clsx';
import { useEffect, useState, useRef } from 'preact/hooks';
import { useEffect, useState, useRef, useContext } from 'preact/hooks';
import { translate } from '../../i18n';
import { userInfo, userLoggedIn, commentUrl, subscribeModalStatus } from '../../state';
import { VerbumSignals } from '../../state';
import { SimpleSubscribeModalProps } from '../../types';
import {
getSubscriptionModalViewCount,
Expand All @@ -13,6 +13,7 @@ import { SimpleSubscribeModalLoggedOut } from './logged-out';
import './style.scss';

export const SimpleSubscribeModal = ( { closeModalHandler, email }: SimpleSubscribeModalProps ) => {
const { userInfo, userLoggedIn, commentUrl, subscribeModalStatus } = useContext( VerbumSignals );
const [ subscribeState, setSubscribeState ] = useState<
'SUBSCRIBING' | 'LOADING' | 'SUBSCRIBED'
>();
Expand Down Expand Up @@ -51,6 +52,13 @@ export const SimpleSubscribeModal = ( { closeModalHandler, email }: SimpleSubscr
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [] );

// This is used to track how many times the modal was shown to the user.
useEffect( () => {
const userId = userInfo.value?.uid || 0;
const currentViewCount = getSubscriptionModalViewCount( userId );
setSubscriptionModalViewCount( currentViewCount + 1, userId );
}, [ userInfo ] );

if ( ! commentUrl.value ) {
// When not showing the modal, we check for modal conditions to show it.
// This is done to avoid subscriptionApi calls for logged out users.
Expand All @@ -69,14 +77,6 @@ export const SimpleSubscribeModal = ( { closeModalHandler, email }: SimpleSubscr
return null;
}

// This is used to track how many times the modal was shown to the user.
// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect( () => {
const userId = userInfo.value?.uid || 0;
const currentViewCount = getSubscriptionModalViewCount( userId );
setSubscriptionModalViewCount( currentViewCount + 1, userId );
}, [] );

if ( subscribeState === 'LOADING' ) {
return (
<div className="verbum-simple-subscribe-modal loading-your-comments">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { useContext } from 'preact/hooks';
import useSubscriptionApi from '../../hooks/useSubscriptionApi';
import { translate } from '../../i18n';
import { subscriptionSettings, userInfo, commentUrl, subscribeModalStatus } from '../../state';
import { VerbumSignals } from '../../state';
import { SimpleSubscribeModalProps } from '../../types';
import { shouldShowSubscriptionModal } from '../../utils';
import SubscriptionModal from './subscription-modal';

// This determines if the modal should be shown to the user.
// It's called before the modal is rendered.
export const SimpleSubscribeSetModalShowLoggedIn = () => {
const { subscriptionSettings, userInfo, subscribeModalStatus } = useContext( VerbumSignals );
const { email } = subscriptionSettings.value ?? {
email: {
send_posts: false,
Expand All @@ -27,6 +29,7 @@ export const SimpleSubscribeModalLoggedIn = ( {
closeModalHandler,
}: SimpleSubscribeModalProps ) => {
const { setEmailPostsSubscription } = useSubscriptionApi();
const { userInfo, commentUrl } = useContext( VerbumSignals );

/**
* Handle the subscribe button click.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEffect, useState } from 'preact/hooks';
import { useContext, useEffect, useState } from 'preact/hooks';
import { translate } from '../../i18n';
import { commentUrl } from '../../state';
import { VerbumSignals } from '../../state';
import { SimpleSubscribeModalProps } from '../../types';
import SubscriptionModal from './subscription-modal';
import type { ChangeEvent } from 'preact/compat';
Expand All @@ -16,6 +16,7 @@ export const SimpleSubscribeModalLoggedOut = ( {
const [ userEmail, setUserEmail ] = useState( '' );
const [ iframeUrl, setIframeUrl ] = useState( '' );
const [ subscribeDisabled, setSubscribeDisabled ] = useState( false );
const { commentUrl } = useContext( VerbumSignals );

// Only want this to run once, when email is set for the first time
useEffect( () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
import clsx from 'clsx';
import { useContext } from 'preact/hooks';
import { translate } from '../i18n';
import {
commentParent,
isReplyDisabled,
isSavingComment,
isTrayOpen,
userLoggedIn,
} from '../state';
import { VerbumSignals } from '../state';
import { SettingsButton } from './settings-button';

interface CommentFooterProps {
toggleTray: ( event: MouseEvent ) => void;
}

export const CommentFooter = ( { toggleTray }: CommentFooterProps ) => {
const { commentParent, isReplyDisabled, isSavingComment, isTrayOpen, userLoggedIn } =
useContext( VerbumSignals );
return (
<div
className={ clsx( 'verbum-footer', {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/* global verbumBlockEditor */
import clsx from 'clsx';
import { forwardRef, type TargetedEvent } from 'preact/compat';
import { useEffect, useState } from 'preact/hooks';
import { useContext, useEffect, useState } from 'preact/hooks';
import { translate } from '../i18n';
import { commentParent, commentValue } from '../state';
import { VerbumSignals } from '../state';
import { isFastConnection } from '../utils';
import { EditorPlaceholder } from './editor-placeholder';

Expand Down Expand Up @@ -35,6 +36,7 @@ export const CommentInputField = forwardRef(
{ handleOnKeyUp }: CommentInputFieldProps,
ref: React.MutableRefObject< HTMLTextAreaElement | null >
) => {
const { commentParent, commentValue } = useContext( VerbumSignals );
const [ editorState, setEditorState ] = useState< 'LOADING' | 'LOADED' | 'ERROR' >( null );
const [ isGBEditorEnabled, setIsGBEditorEnabled ] = useState( false );

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import clsx from 'clsx';
import { useContext } from 'preact/hooks';
import { translate } from '../i18n';
import { commentParent } from '../state';
import { VerbumSignals } from '../state';
import { CustomLoadingSpinner } from './custom-loading-spinner';

export const EditorPlaceholder = ( { onClick, loading } ) => {
const { commentParent } = useContext( VerbumSignals );
return (
<div
className="verbum-editor-wrapper"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import clsx from 'clsx';
import { useContext } from 'preact/hooks';
import useSubscriptionApi from '../hooks/useSubscriptionApi';
import { translate } from '../i18n';
import { Close } from '../images';
import { isTrayOpen, subscriptionSettings, userInfo } from '../state';
import { VerbumSignals } from '../state';
import { serviceData, isFastConnection } from '../utils';
import { NewCommentEmail } from './new-comment-email';
import { NewPostsEmail } from './new-posts-email';
Expand All @@ -25,6 +26,7 @@ interface LoggedInProps {
}

export const LoggedIn = ( { toggleTray, logout }: LoggedInProps ) => {
const { isTrayOpen, subscriptionSettings, userInfo } = useContext( VerbumSignals );
const { setEmailPostsSubscription, setCommentSubscription, setNotificationSubscription } =
useSubscriptionApi();
const { subscribeToComment, subscribeToBlog } = VerbumComments;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Signal } from '@preact/signals';
import clsx from 'clsx';
import { useEffect, useState } from 'preact/hooks';
import { useContext, useEffect, useState } from 'preact/hooks';
import { translate } from '../i18n';
import { commentParent } from '../state';
import { VerbumSignals } from '../state';
import { serviceData } from '../utils';
import { EmailForm } from './EmailForm';

Expand All @@ -12,7 +13,7 @@ interface LoggedOutProps {
loginWindow: Window | null;
}

const getLoginCommentText = () => {
const getLoginCommentText = ( commentParent: Signal ) => {
let defaultText = translate( 'Log in to leave a comment.' );
let optionalText = translate( 'Leave a comment. (log in optional)' );
let nameAndEmailRequired = translate(
Expand Down Expand Up @@ -80,13 +81,17 @@ export const LoggedOut = ( { login, canWeAccessCookies, loginWindow }: LoggedOut
setActiveService( service );
};

const { commentParent } = 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() }</div>
<div className="verbum-subscriptions__login-header">
{ getLoginCommentText( commentParent ) }
</div>
<div
className={ clsx( 'verbum-logins', {
'logging-in': activeService,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import clsx from 'clsx';
import { userInfo } from '../state';
import { useContext } from 'preact/hooks';
import { VerbumSignals } from '../state';
import { hasSubscriptionOptionsVisible } from '../utils';

interface SettingsButtonProps {
Expand All @@ -8,6 +9,7 @@ interface SettingsButtonProps {
}

export const SettingsButton = ( { expanded, toggleSubscriptionTray }: SettingsButtonProps ) => {
const { userInfo } = useContext( VerbumSignals );
const subscriptionOptionsVisible = hasSubscriptionOptionsVisible();

const handleOnClick = ( event: MouseEvent ) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ type ScriptLoader = {

declare global {
const VerbumComments: VerbumCommentsType;
const verbumBlockEditor: {
attachGutenberg: (
textarea: HTMLTextAreaElement,
content: ( embedUrl: string ) => void,
isRtl: boolean,
onEmbedContent: ( embedUrl: string ) => void
) => void;
};
const vbeCacheBuster: string;
const WP_Enqueue_Dynamic_Script: ScriptLoader;
const wp: Record< string, unknown >;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { useEffect } from 'preact/hooks';
import { commentParent } from '../state';
import { useEffect, useContext } from 'preact/hooks';
import { VerbumSignals } from '../state';

/**
* Hook to observe comment form changes and update state according to comment_parent changes.
*
* @param formElement - The form element to observe.
*/
export default function useFormMutations() {
export default function useFormMutations( formElement: HTMLFormElement ) {
const { commentParent } = useContext( VerbumSignals );

useEffect( () => {
const formElement = document.querySelector( '#commentform' ) as HTMLFormElement;
const commentParentInput = formElement.querySelector( '#comment_parent' );

if ( ! formElement || ! commentParentInput ) {
Expand All @@ -28,5 +31,5 @@ export default function useFormMutations() {
return () => {
mutationObserver.disconnect();
};
}, [] );
}, [ formElement, commentParent ] );
}
Loading
Loading