Files
piratepoet/packages/js/email-editor/src/store/selectors.ts
2025-01-30 13:12:33 +01:00

375 lines
9.8 KiB
TypeScript

/**
* External dependencies
*/
import { createRegistrySelector, createSelector } from '@wordpress/data';
import { store as coreDataStore } from '@wordpress/core-data';
import { store as interfaceStore } from '@wordpress/interface';
import { store as editorStore } from '@wordpress/editor';
import { store as preferencesStore } from '@wordpress/preferences';
import { serialize, parse } from '@wordpress/blocks';
import { BlockInstance } from '@wordpress/blocks/index';
import { Post } from '@wordpress/core-data/build-types/entity-types/post';
/**
* Internal dependencies
*/
import { storeName, editorCurrentPostType } from './constants';
import { State, Feature, EmailTemplate, EmailEditorPostType } from './types';
function getContentFromEntity( entity ): string {
if ( entity?.content && typeof entity.content === 'function' ) {
return entity.content( entity ) as string;
}
if ( entity?.blocks ) {
return serialize( entity.blocks );
}
if ( entity?.content ) {
return entity.content as string;
}
return '';
}
const patternsWithParsedBlocks = new WeakMap();
function enhancePatternWithParsedBlocks( pattern ) {
let enhancedPattern = patternsWithParsedBlocks.get( pattern );
if ( ! enhancedPattern ) {
enhancedPattern = {
...pattern,
get blocks() {
return parse( pattern.content );
},
};
patternsWithParsedBlocks.set( pattern, enhancedPattern );
}
return enhancedPattern;
}
export const isFeatureActive = createRegistrySelector(
( select ) =>
( _, feature: Feature ): boolean =>
!! select( preferencesStore ).get( storeName, feature )
);
export const isSidebarOpened = createRegistrySelector(
( select ) => (): boolean =>
!! select( interfaceStore ).getActiveComplementaryArea( storeName )
);
export const hasEdits = createRegistrySelector( ( select ) => (): boolean => {
const postId = select( storeName ).getEmailPostId();
return !! select( coreDataStore ).hasEditsForEntityRecord(
'postType',
editorCurrentPostType,
postId
);
} );
export const isEmailLoaded = createRegistrySelector(
( select ) => (): boolean => {
const postId = select( storeName ).getEmailPostId();
return !! select( coreDataStore ).getEntityRecord(
'postType',
editorCurrentPostType,
postId
);
}
);
export const isSaving = createRegistrySelector( ( select ) => (): boolean => {
const postId = select( storeName ).getEmailPostId();
return !! select( coreDataStore ).isSavingEntityRecord(
'postType',
editorCurrentPostType,
postId
);
} );
export const isEmpty = createRegistrySelector( ( select ) => (): boolean => {
const postId = select( storeName ).getEmailPostId();
const post: EmailEditorPostType = select( coreDataStore ).getEntityRecord(
'postType',
editorCurrentPostType,
postId
);
if ( ! post ) {
return true;
}
const { content, mailpoet_data: mailpoetData, title } = post;
return (
! content.raw &&
! mailpoetData.subject &&
! mailpoetData.preheader &&
! title.raw
);
} );
export const hasEmptyContent = createRegistrySelector(
( select ) => (): boolean => {
const postId = select( storeName ).getEmailPostId();
const post = select( coreDataStore ).getEntityRecord(
'postType',
editorCurrentPostType,
postId
);
if ( ! post ) {
return true;
}
// @ts-expect-error Missing property in type
const { content } = post;
return ! content.raw;
}
);
export const isEmailSent = createRegistrySelector(
( select ) => (): boolean => {
const postId = select( storeName ).getEmailPostId();
const post = select( coreDataStore ).getEntityRecord(
'postType',
editorCurrentPostType,
postId
);
if ( ! post ) {
return false;
}
// @ts-expect-error Missing property in type
const status = post.status;
return status === 'sent';
}
);
/**
* Returns the content of the email being edited.
*
* @param {Object} state Global application state.
* @return {string} Post content.
*/
export const getEditedEmailContent = createRegistrySelector(
( select ) => (): string => {
const postId = select( storeName ).getEmailPostId();
const record = select( coreDataStore ).getEditedEntityRecord(
'postType',
editorCurrentPostType,
postId
) as unknown as
| { content: string | unknown; blocks: BlockInstance[] }
| undefined;
if ( record ) {
return getContentFromEntity( record );
}
return '';
}
);
export const getSentEmailEditorPosts = createRegistrySelector(
( select ) => () =>
select( coreDataStore )
.getEntityRecords( 'postType', editorCurrentPostType, {
per_page: 30, // show a maximum of 30 for now
status: 'publish,sent', // show only sent emails
} )
?.filter(
( post: EmailEditorPostType ) => post?.content?.raw !== '' // filter out empty content
) || []
);
export const getBlockPatternsForEmailTemplate = createRegistrySelector(
( select ) =>
createSelector(
() =>
select( coreDataStore )
.getBlockPatterns()
.filter(
( { templateTypes } ) =>
Array.isArray( templateTypes ) &&
templateTypes.includes( 'email-template' )
)
.map( enhancePatternWithParsedBlocks ),
() => [ select( coreDataStore ).getBlockPatterns() ]
)
);
/**
* COPIED FROM https://github.com/WordPress/gutenberg/blob/9c6d4fe59763b188d27ad937c2f0daa39e4d9341/packages/edit-post/src/store/selectors.js
* Retrieves the template of the currently edited post.
*
* @return {Object?} Post Template.
*/
export const getEditedPostTemplate = createRegistrySelector(
( select ) => (): EmailTemplate => {
const currentTemplate =
// @ts-expect-error Expected 0 arguments, but got 1.
select( editorStore ).getEditedPostAttribute( 'template' );
if ( currentTemplate ) {
const templateWithSameSlug = select( coreDataStore )
.getEntityRecords( 'postType', 'wp_template', { per_page: -1 } )
// @ts-expect-error Missing property in type
?.find( ( template ) => template.slug === currentTemplate );
if ( ! templateWithSameSlug ) {
return templateWithSameSlug as EmailTemplate;
}
return select( coreDataStore ).getEditedEntityRecord(
'postType',
'wp_template',
// @ts-expect-error getEditedPostAttribute
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
templateWithSameSlug.id
) as unknown as EmailTemplate;
}
const defaultTemplateId = select( coreDataStore ).getDefaultTemplateId(
{
slug: 'email-general',
}
);
return select( coreDataStore ).getEditedEntityRecord(
'postType',
'wp_template',
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
defaultTemplateId
) as unknown as EmailTemplate;
}
);
export const getCurrentTemplate = createRegistrySelector( ( select ) => () => {
const isEditingTemplate =
select( editorStore ).getCurrentPostType() === 'wp_template';
if ( isEditingTemplate ) {
const templateId = select( editorStore ).getCurrentPostId();
return select( coreDataStore ).getEditedEntityRecord(
'postType',
'wp_template',
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
templateId
) as unknown as EmailTemplate;
}
return getEditedPostTemplate();
} );
export const getCurrentTemplateContent = () => {
const template = getCurrentTemplate();
if ( template ) {
return getContentFromEntity( template );
}
return '';
};
export const getGlobalEmailStylesPost = createRegistrySelector(
( select ) => () => {
const postId = select( storeName ).getGlobalStylesPostId();
if ( postId ) {
return select( coreDataStore ).getEditedEntityRecord(
'postType',
'wp_global_styles',
postId
) as unknown as Post;
}
return getEditedPostTemplate();
}
);
/**
* Retrieves the email templates.
*/
export const getEmailTemplates = createRegistrySelector(
( select ) => () =>
select( coreDataStore )
.getEntityRecords( 'postType', 'wp_template', {
per_page: -1,
post_type: editorCurrentPostType,
} )
// We still need to filter the templates because, in some cases, the API also returns custom templates
// ignoring the post_type filter in the query
?.filter( ( template ) =>
// @ts-expect-error Missing property in type
template.post_types.includes( editorCurrentPostType )
)
);
export function getEmailPostId( state: State ): number {
return state.postId;
}
export function getSettingsSidebarActiveTab( state: State ): string {
return state.settingsSidebar.activeTab;
}
export function getInitialEditorSettings(
state: State
): State[ 'editorSettings' ] {
return state.editorSettings;
}
export function getPaletteColors(
state: State
): State[ 'editorSettings' ][ '__experimentalFeatures' ][ 'color' ][ 'palette' ] {
// eslint-disable-next-line no-underscore-dangle
return state.editorSettings.__experimentalFeatures.color.palette;
}
export function getPreviewState( state: State ): State[ 'preview' ] {
return state.preview;
}
export function getPersonalizationTagsState(
state: State
): State[ 'personalizationTags' ] {
return state.personalizationTags;
}
export function getPersonalizationTagsList(
state: State
): State[ 'personalizationTags' ][ 'list' ] {
return state.personalizationTags.list;
}
export const getDeviceType = createRegistrySelector(
( select ) => () =>
// @ts-expect-error getDeviceType is missing in types.
select( editorStore ).getDeviceType() as string
);
export function getStyles( state: State ): State[ 'theme' ][ 'styles' ] {
return state.theme.styles;
}
export function getAutosaveInterval(
state: State
): State[ 'autosaveInterval' ] {
return state.autosaveInterval;
}
export function getCdnUrl( state: State ): State[ 'cdnUrl' ] {
return state.cdnUrl;
}
export function isPremiumPluginActive( state: State ): boolean {
return state.isPremiumPluginActive;
}
export function getTheme( state: State ): State[ 'theme' ] {
return state.theme;
}
export function getGlobalStylesPostId( state: State ): number | null {
return state.styles.globalStylesPostId;
}
export function getUrls( state: State ): State[ 'urls' ] {
return state.urls;
}