Refactor modal to pass insert callback and isOpened value

[MAILPOET-6354]
This commit is contained in:
Jan Lysý
2024-12-12 13:37:55 +01:00
committed by Aschepikov
parent 64264460e8
commit b264c74ce9
7 changed files with 53 additions and 69 deletions

View File

@ -2,13 +2,14 @@ import { registerFormatType, unregisterFormatType } from '@wordpress/rich-text';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { BlockControls } from '@wordpress/block-editor'; import { BlockControls } from '@wordpress/block-editor';
import { ToolbarButton, ToolbarGroup } from '@wordpress/components'; import { ToolbarButton, ToolbarGroup } from '@wordpress/components';
import { storeName } from '../../store';
import { useSelect, useDispatch } from '@wordpress/data'; import { useSelect, useDispatch } from '@wordpress/data';
import { import {
createTextToHtmlMap, createTextToHtmlMap,
getCursorPosition, getCursorPosition,
isMatchingComment, isMatchingComment,
} from '../../components/personalization-tags/rich-text-utils'; } from '../../components/personalization-tags/rich-text-utils';
import { PersonalizationTagsModal } from '../../components/personalization-tags/personalization-tags-modal';
import { useState } from '@wordpress/element';
/** /**
* Disable Rich text formats we currently cannot support * Disable Rich text formats we currently cannot support
@ -36,8 +37,7 @@ type Props = {
* @param root0.contentRef * @param root0.contentRef
*/ */
function PersonalizationTagsButton( { contentRef }: Props ) { function PersonalizationTagsButton( { contentRef }: Props ) {
const { togglePersonalizationTagsModal } = useDispatch( storeName ); const [ isModalOpened, setIsModalOpened ] = useState( false );
const selectedBlockId = useSelect( ( select ) => const selectedBlockId = useSelect( ( select ) =>
select( 'core/block-editor' ).getSelectedBlockClientId() select( 'core/block-editor' ).getSelectedBlockClientId()
); );
@ -87,11 +87,15 @@ function PersonalizationTagsButton( { contentRef }: Props ) {
<ToolbarButton <ToolbarButton
icon="shortcode" icon="shortcode"
title={ __( 'Personalization Tags', 'mailpoet' ) } title={ __( 'Personalization Tags', 'mailpoet' ) }
onClick={ () => { onClick={ () => setIsModalOpened( true ) }
togglePersonalizationTagsModal( true, { />
onInsert: handleInsert, <PersonalizationTagsModal
} ); isOpened={ isModalOpened }
onInsert={ ( value ) => {
handleInsert( value );
setIsModalOpened( false );
} } } }
closeCallback={ () => setIsModalOpened( false ) }
/> />
</ToolbarGroup> </ToolbarGroup>
</BlockControls> </BlockControls>

View File

@ -24,7 +24,6 @@ import { StylesSidebar } from '../styles-sidebar';
import { VisualEditor } from './visual-editor/visual-editor'; import { VisualEditor } from './visual-editor/visual-editor';
import { TemplateSelection } from '../template-select'; import { TemplateSelection } from '../template-select';
import { PersonalizationTagsModal } from '../personalization-tags/personalization-tags-modal';
export function Layout() { export function Layout() {
const { const {
@ -93,7 +92,6 @@ export function Layout() {
<SentEmailNotice /> <SentEmailNotice />
<Sidebar /> <Sidebar />
<StylesSidebar /> <StylesSidebar />
<PersonalizationTagsModal />
<TemplateSelection /> <TemplateSelection />
<InterfaceSkeleton <InterfaceSkeleton
className={ className } className={ className }

View File

@ -1,22 +1,16 @@
import { Button } from '@wordpress/components'; import { Button } from '@wordpress/components';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { PersonalizationTag, storeName } from '../../store'; import { PersonalizationTag } from '../../store';
import { useDispatch, useSelect } from '@wordpress/data';
const CategorySection = ( { const CategorySection = ( {
groupedTags, groupedTags,
activeCategory, activeCategory,
onInsert,
}: { }: {
groupedTags: Record< string, PersonalizationTag[] >; groupedTags: Record< string, PersonalizationTag[] >;
activeCategory: string | null; activeCategory: string | null;
onInsert: ( tag: string ) => void;
} ) => { } ) => {
const { togglePersonalizationTagsModal } = useDispatch( storeName );
const { onInsert } = useSelect(
( select ) => select( storeName ).getPersonalizationTagsState(),
[]
);
const categoriesToRender: [ string, PersonalizationTag[] ][] = const categoriesToRender: [ string, PersonalizationTag[] ][] =
activeCategory === null activeCategory === null
? Object.entries( groupedTags ) // Render all categories ? Object.entries( groupedTags ) // Render all categories
@ -46,9 +40,6 @@ const CategorySection = ( {
if ( onInsert ) { if ( onInsert ) {
onInsert( item.token ); onInsert( item.token );
} }
togglePersonalizationTagsModal(
false
);
} } } }
> >
{ __( 'Insert' ) } { __( 'Insert' ) }

View File

@ -1,31 +1,23 @@
import { Modal, SearchControl } from '@wordpress/components'; import { Modal, SearchControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { PersonalizationTag, storeName } from '../../store'; import { PersonalizationTag, storeName } from '../../store';
import { useDispatch, useSelect } from '@wordpress/data'; import { select } from '@wordpress/data';
import { external, Icon } from '@wordpress/icons'; import { external, Icon } from '@wordpress/icons';
import './index.scss'; import './index.scss';
import { useState } from '@wordpress/element'; import { useState } from '@wordpress/element';
import { CategoryMenu } from './category-menu'; import { CategoryMenu } from './category-menu';
import { CategorySection } from './category-section'; import { CategorySection } from './category-section';
const PersonalizationTagsModal = () => { const PersonalizationTagsModal = ( { onInsert, isOpened, closeCallback } ) => {
const [ activeCategory, setActiveCategory ] = useState( null ); const [ activeCategory, setActiveCategory ] = useState( null );
const [ searchQuery, setSearchQuery ] = useState( '' ); const [ searchQuery, setSearchQuery ] = useState( '' );
const { togglePersonalizationTagsModal } = useDispatch( storeName ); if ( ! isOpened ) {
const { isModalOpened, list } = useSelect( ( select ) => {
const store = select( storeName );
return {
isModalOpened: store.getPersonalizationTagsState().isModalOpened,
list: store.getPersonalizationTagsList(),
};
}, [] );
if ( ! isModalOpened ) {
return null; return null;
} }
const list = select( storeName ).getPersonalizationTagsList();
const groupedTags: Record< string, PersonalizationTag[] > = list.reduce( const groupedTags: Record< string, PersonalizationTag[] > = list.reduce(
( groups, item ) => { ( groups, item ) => {
const { category, name, token } = item; const { category, name, token } = item;
@ -49,7 +41,7 @@ const PersonalizationTagsModal = () => {
<Modal <Modal
size="medium" size="medium"
title={ __( 'Personalization Tags', 'mailpoet' ) } title={ __( 'Personalization Tags', 'mailpoet' ) }
onRequestClose={ () => togglePersonalizationTagsModal( false ) } onRequestClose={ closeCallback }
className="mailpoet-personalization-tags-modal" className="mailpoet-personalization-tags-modal"
> >
<p> <p>
@ -68,6 +60,7 @@ const PersonalizationTagsModal = () => {
<CategorySection <CategorySection
groupedTags={ groupedTags } groupedTags={ groupedTags }
activeCategory={ activeCategory } activeCategory={ activeCategory }
onInsert={ onInsert }
/> />
</Modal> </Modal>
); );

View File

@ -16,6 +16,7 @@ import {
getCursorPosition, getCursorPosition,
isMatchingComment, isMatchingComment,
} from '../personalization-tags/rich-text-utils'; } from '../personalization-tags/rich-text-utils';
import { PersonalizationTagsModal } from '../personalization-tags/personalization-tags-modal';
const previewTextMaxLength = 150; const previewTextMaxLength = 150;
const previewTextRecommendedLength = 80; const previewTextRecommendedLength = 80;
@ -38,36 +39,33 @@ export function DetailsPanel() {
'mailpoet_data' 'mailpoet_data'
); );
const { togglePersonalizationTagsModal, updateEmailMailPoetProperty } = const { updateEmailMailPoetProperty } = useDispatch( storeName );
useDispatch( storeName );
const [ activeRichText, setActiveRichText ] = useState( null );
const [ selectionRange, setSelectionRange ] = useState( null ); const [ selectionRange, setSelectionRange ] = useState( null );
const [ subjectModalIsOpened, setSubjectModalIsOpened ] = useState( false );
const [ preheaderModalIsOpened, setPreheaderModalIsOpened ] =
useState( false );
const subjectRef = useRef( null ); const subjectRef = useRef( null );
const preheaderRef = useRef( null ); const preheaderRef = useRef( null );
const handleInsertPersonalizationTag = async ( value ) => { const handleInsertPersonalizationTag = async ( value, activeRichText ) => {
if ( ! activeRichText || ! selectionRange ) {
return;
}
const ref = activeRichText === 'subject' ? subjectRef : preheaderRef;
if ( ! ref ) {
return;
}
// Retrieve the current value of the active RichText // Retrieve the current value of the active RichText
const currentValue = const currentValue =
activeRichText === 'subject' activeRichText === 'subject'
? mailpoetEmailData?.subject ?? '' ? mailpoetEmailData?.subject ?? ''
: mailpoetEmailData?.preheader ?? ''; : mailpoetEmailData?.preheader ?? '';
const ref = activeRichText === 'subject' ? subjectRef : preheaderRef;
if ( ! ref ) {
return;
}
// Generate text-to-HTML mapping // Generate text-to-HTML mapping
const { mapping } = createTextToHtmlMap( currentValue ); const { mapping } = createTextToHtmlMap( currentValue );
// Ensure selection range is within bounds // Ensure selection range is within bounds
const start = selectionRange.start; const start = selectionRange?.start ?? currentValue.length;
const end = selectionRange.end; const end = selectionRange?.end ?? currentValue.length;
// Default values for starting and ending indexes. // Default values for starting and ending indexes.
let htmlStart = start; let htmlStart = start;
@ -124,10 +122,7 @@ export function DetailsPanel() {
<span>{ __( 'Subject', 'mailpoet' ) }</span> <span>{ __( 'Subject', 'mailpoet' ) }</span>
<PersonalizationTagsButton <PersonalizationTagsButton
onClick={ () => { onClick={ () => {
setActiveRichText( 'subject' ); setSubjectModalIsOpened( true );
togglePersonalizationTagsModal( true, {
onInsert: handleInsertPersonalizationTag,
} );
} } } }
/> />
<ExternalLink href="https://kb.mailpoet.com/article/215-personalize-newsletter-with-shortcodes#list"> <ExternalLink href="https://kb.mailpoet.com/article/215-personalize-newsletter-with-shortcodes#list">
@ -163,10 +158,7 @@ export function DetailsPanel() {
<span>{ __( 'Preview text', 'mailpoet' ) }</span> <span>{ __( 'Preview text', 'mailpoet' ) }</span>
<PersonalizationTagsButton <PersonalizationTagsButton
onClick={ () => { onClick={ () => {
setActiveRichText( 'preheader' ); setPreheaderModalIsOpened( true );
togglePersonalizationTagsModal( true, {
onInsert: handleInsertPersonalizationTag,
} );
} } } }
/> />
<span <span
@ -196,6 +188,14 @@ export function DetailsPanel() {
className="mailpoet-settings-panel__subject" className="mailpoet-settings-panel__subject"
help={ subjectHelp } help={ subjectHelp }
> >
<PersonalizationTagsModal
isOpened={ subjectModalIsOpened }
onInsert={ ( value ) => {
handleInsertPersonalizationTag( value, 'subject' );
setSubjectModalIsOpened( false );
} }
closeCallback={ () => setSubjectModalIsOpened( false ) }
/>
<RichText <RichText
ref={ subjectRef } ref={ subjectRef }
className="mailpoet-settings-panel__richtext" className="mailpoet-settings-panel__richtext"
@ -204,7 +204,6 @@ export function DetailsPanel() {
'mailpoet' 'mailpoet'
) } ) }
onFocus={ () => { onFocus={ () => {
setActiveRichText( 'subject' );
setSelectionRange( setSelectionRange(
getCursorPosition( getCursorPosition(
subjectRef, subjectRef,
@ -213,7 +212,6 @@ export function DetailsPanel() {
); );
} } } }
onKeyUp={ () => { onKeyUp={ () => {
setActiveRichText( 'subject' );
setSelectionRange( setSelectionRange(
getCursorPosition( getCursorPosition(
subjectRef, subjectRef,
@ -222,7 +220,6 @@ export function DetailsPanel() {
); );
} } } }
onClick={ () => { onClick={ () => {
setActiveRichText( 'subject' );
setSelectionRange( setSelectionRange(
getCursorPosition( getCursorPosition(
subjectRef, subjectRef,
@ -244,6 +241,14 @@ export function DetailsPanel() {
className="mailpoet-settings-panel__preview-text" className="mailpoet-settings-panel__preview-text"
help={ preheaderHelp } help={ preheaderHelp }
> >
<PersonalizationTagsModal
isOpened={ preheaderModalIsOpened }
onInsert={ ( value ) => {
handleInsertPersonalizationTag( value, 'preheader' );
setPreheaderModalIsOpened( false );
} }
closeCallback={ () => setPreheaderModalIsOpened( false ) }
/>
<RichText <RichText
ref={ preheaderRef } ref={ preheaderRef }
className="mailpoet-settings-panel__richtext" className="mailpoet-settings-panel__richtext"
@ -252,7 +257,6 @@ export function DetailsPanel() {
'mailpoet' 'mailpoet'
) } ) }
onFocus={ () => { onFocus={ () => {
setActiveRichText( 'preheader' );
setSelectionRange( setSelectionRange(
getCursorPosition( getCursorPosition(
preheaderRef, preheaderRef,
@ -261,7 +265,6 @@ export function DetailsPanel() {
); );
} } } }
onKeyUp={ () => { onKeyUp={ () => {
setActiveRichText( 'preheader' );
setSelectionRange( setSelectionRange(
getCursorPosition( getCursorPosition(
preheaderRef, preheaderRef,
@ -270,7 +273,6 @@ export function DetailsPanel() {
); );
} } } }
onClick={ () => { onClick={ () => {
setActiveRichText( 'preheader' );
setSelectionRange( setSelectionRange(
getCursorPosition( getCursorPosition(
preheaderRef, preheaderRef,

View File

@ -39,9 +39,7 @@ export function getInitialState(): State {
sendingPreviewStatus: null, sendingPreviewStatus: null,
}, },
personalizationTags: { personalizationTags: {
isModalOpened: false,
list: [], list: [],
onInsert: null,
isFetching: false, isFetching: false,
}, },
}; };

View File

@ -201,9 +201,7 @@ export type State = {
sendingPreviewStatus: SendingPreviewStatus | null; sendingPreviewStatus: SendingPreviewStatus | null;
}; };
personalizationTags: { personalizationTags: {
isModalOpened: boolean;
list: PersonalizationTag[]; list: PersonalizationTag[];
onInsert: ( ( value: string ) => void ) | null;
isFetching: boolean; isFetching: boolean;
}; };
}; };