diff --git a/includes/class-newspack-newsletters-layouts.php b/includes/class-newspack-newsletters-layouts.php index b76190ad8..16485b8da 100644 --- a/includes/class-newspack-newsletters-layouts.php +++ b/includes/class-newspack-newsletters-layouts.php @@ -42,6 +42,7 @@ public static function instance() { */ public function __construct() { add_action( 'init', [ __CLASS__, 'register_layout_cpt' ] ); + add_action( 'init', [ __CLASS__, 'register_meta' ] ); } /** @@ -55,12 +56,51 @@ public static function register_layout_cpt() { $cpt_args = [ 'public' => false, 'show_in_rest' => true, - 'supports' => [ 'editor', 'title' ], + 'supports' => [ 'editor', 'title', 'custom-fields' ], 'taxonomies' => [], ]; \register_post_type( self::NEWSPACK_NEWSLETTERS_LAYOUT_CPT, $cpt_args ); } + /** + * Register custom fields. + */ + public static function register_meta() { + \register_meta( + 'post', + 'font_header', + [ + 'object_subtype' => self::NEWSPACK_NEWSLETTERS_LAYOUT_CPT, + 'show_in_rest' => true, + 'type' => 'string', + 'single' => true, + 'auth_callback' => '__return_true', + ] + ); + \register_meta( + 'post', + 'font_body', + [ + 'object_subtype' => self::NEWSPACK_NEWSLETTERS_LAYOUT_CPT, + 'show_in_rest' => true, + 'type' => 'string', + 'single' => true, + 'auth_callback' => '__return_true', + ] + ); + \register_meta( + 'post', + 'background_color', + [ + 'object_subtype' => self::NEWSPACK_NEWSLETTERS_LAYOUT_CPT, + 'show_in_rest' => true, + 'type' => 'string', + 'single' => true, + 'auth_callback' => '__return_true', + ] + ); + } + /** * Token replacement for newsletter layouts. * diff --git a/includes/class-newspack-newsletters.php b/includes/class-newspack-newsletters.php index cbffa9cbe..6039fe917 100644 --- a/includes/class-newspack-newsletters.php +++ b/includes/class-newspack-newsletters.php @@ -429,8 +429,19 @@ public static function api_get_layouts() { 'posts_per_page' => -1, ) ); + $user_layouts = array_map( + function ( $post ) { + $post->meta = [ + 'background_color' => get_post_meta( $post->ID, 'background_color', true ), + 'font_body' => get_post_meta( $post->ID, 'font_body', true ), + 'font_header' => get_post_meta( $post->ID, 'font_header', true ), + ]; + return $post; + }, + $layouts_query->get_posts() + ); $layouts = array_merge( - $layouts_query->get_posts(), + $user_layouts, Newspack_Newsletters_Layouts::get_default_layouts(), apply_filters( 'newspack_newsletters_templates', [] ) ); diff --git a/src/components/init-modal/screens/layout-picker/SingleLayoutPreview.js b/src/components/init-modal/screens/layout-picker/SingleLayoutPreview.js index efa53f106..f6126ab86 100644 --- a/src/components/init-modal/screens/layout-picker/SingleLayoutPreview.js +++ b/src/components/init-modal/screens/layout-picker/SingleLayoutPreview.js @@ -11,7 +11,6 @@ import { parse } from '@wordpress/blocks'; import { useState, useMemo } from '@wordpress/element'; import { Button, TextControl } from '@wordpress/components'; import { ENTER, SPACE } from '@wordpress/keycodes'; -import { BlockPreview } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; /** @@ -19,6 +18,7 @@ import { __ } from '@wordpress/i18n'; */ import { LAYOUT_CPT_SLUG } from '../../../../utils/consts'; import { setPreventDeduplicationForPostsInserter } from '../../../../editor/blocks/posts-inserter/utils'; +import NewsletterPreview from '../../../newsletter-preview'; const SingleLayoutPreview = ( { isEditable, @@ -29,6 +29,7 @@ const SingleLayoutPreview = ( { ID, post_title: title, post_content: content, + meta, } ) => { const handleDelete = () => { // eslint-disable-next-line no-alert @@ -75,7 +76,7 @@ const SingleLayoutPreview = ( { aria-label={ title } > { '' === content ? null : ( - + ) } { isEditable ? ( diff --git a/src/components/init-modal/screens/layout-picker/index.js b/src/components/init-modal/screens/layout-picker/index.js index 2a8e06cad..f547e69b6 100644 --- a/src/components/init-modal/screens/layout-picker/index.js +++ b/src/components/init-modal/screens/layout-picker/index.js @@ -13,7 +13,6 @@ import { compose } from '@wordpress/compose'; import { withSelect, withDispatch } from '@wordpress/data'; import { Button, Spinner } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { BlockPreview } from '@wordpress/block-editor'; /** * Internal dependencies @@ -22,6 +21,7 @@ import { BLANK_LAYOUT_ID } from '../../../../utils/consts'; import { isUserDefinedLayout } from '../../../../utils'; import { useLayoutsState } from '../../../../utils/hooks'; import SingleLayoutPreview from './SingleLayoutPreview'; +import NewsletterPreview from '../../../newsletter-preview'; const LAYOUTS_TABS = [ { @@ -35,11 +35,17 @@ const LAYOUTS_TABS = [ }, ]; -const LayoutPicker = ( { getBlocks, insertBlocks, replaceBlocks, savePost, setLayoutIdMeta } ) => { +const LayoutPicker = ( { + getBlocks, + insertBlocks, + replaceBlocks, + savePost, + setNewsletterMeta, +} ) => { const { layouts, isFetchingLayouts, deleteLayoutPost } = useLayoutsState(); const insertLayout = layoutId => { - const { post_content: content } = find( layouts, { ID: layoutId } ) || {}; + const { post_content: content, meta = {} } = find( layouts, { ID: layoutId } ) || {}; const blocksToInsert = content ? parse( content ) : []; const existingBlocksIds = getBlocks().map( ( { clientId } ) => clientId ); if ( existingBlocksIds.length ) { @@ -47,21 +53,25 @@ const LayoutPicker = ( { getBlocks, insertBlocks, replaceBlocks, savePost, setLa } else { insertBlocks( blocksToInsert ); } - setLayoutIdMeta( layoutId ); + const metaPayload = { + template_id: layoutId, + ...meta, + }; + setNewsletterMeta( metaPayload ); setTimeout( savePost, 1 ); }; const [ selectedLayoutId, setSelectedLayoutId ] = useState( null ); - const layoutBlocks = useMemo(() => { + const layoutPreviewProps = useMemo(() => { const layout = selectedLayoutId && find( layouts, { ID: selectedLayoutId } ); - return layout ? parse( layout.post_content ) : null; + return layout ? { blocks: parse( layout.post_content ), meta: layout.meta } : null; }, [ selectedLayoutId, layouts.length ]); - const canRenderPreview = layoutBlocks && layoutBlocks.length > 0; + const canRenderPreview = layoutPreviewProps && layoutPreviewProps.blocks.length > 0; const renderPreview = () => canRenderPreview ? ( - + ) : (

{ __( 'Select a layout to preview.', 'newspack-newsletters' ) }

); @@ -166,7 +176,7 @@ export default compose( [ savePost, insertBlocks, replaceBlocks, - setLayoutIdMeta: id => editPost( { meta: { template_id: id } } ), + setNewsletterMeta: meta => editPost( { meta } ), }; } ), ] )( LayoutPicker ); diff --git a/src/components/newsletter-preview/index.js b/src/components/newsletter-preview/index.js new file mode 100644 index 000000000..18cbb8f37 --- /dev/null +++ b/src/components/newsletter-preview/index.js @@ -0,0 +1,45 @@ +/** + * WordPress dependencies + */ +import { BlockPreview } from '@wordpress/block-editor'; +import { Fragment, useMemo } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import './style.scss'; + +const NewsletterPreview = ( { meta = {}, ...props } ) => { + const ELEMENT_ID = useMemo( () => `preview-${ Math.round( Math.random() * 1000 ) }`, [] ); + + return ( + + +
+ +
+
+ ); +}; + +export default NewsletterPreview; diff --git a/src/components/newsletter-preview/style.scss b/src/components/newsletter-preview/style.scss new file mode 100644 index 000000000..136fe2b68 --- /dev/null +++ b/src/components/newsletter-preview/style.scss @@ -0,0 +1,5 @@ +.newspack-newsletters__layout-preview { + width: 100%; + height: 100%; + position: absolute; +} diff --git a/src/newsletter-editor/layout/index.js b/src/newsletter-editor/layout/index.js index 93b34e4e2..f0780a999 100644 --- a/src/newsletter-editor/layout/index.js +++ b/src/newsletter-editor/layout/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { find } from 'lodash'; +import { isEqual, find } from 'lodash'; /** * WordPress dependencies @@ -9,7 +9,6 @@ import { find } from 'lodash'; import { compose } from '@wordpress/compose'; import { parse, serialize } from '@wordpress/blocks'; import { __ } from '@wordpress/i18n'; -import { BlockPreview } from '@wordpress/block-editor'; import { withDispatch, withSelect } from '@wordpress/data'; import { Fragment, useState, useEffect, useMemo } from '@wordpress/element'; import { Button, Modal, TextControl, Spinner } from '@wordpress/components'; @@ -22,22 +21,28 @@ import { LAYOUT_CPT_SLUG, NEWSLETTER_CPT_SLUG } from '../../utils/consts'; import { isUserDefinedLayout } from '../../utils'; import './style.scss'; import { setPreventDeduplicationForPostsInserter } from '../../editor/blocks/posts-inserter/utils'; +import NewsletterPreview from '../../components/newsletter-preview'; export default compose( [ withSelect( select => { const { getEditedPostAttribute, isEditedPostEmpty, getCurrentPostId } = select( 'core/editor' ); const { getBlocks } = select( 'core/block-editor' ); const meta = getEditedPostAttribute( 'meta' ); - const { template_id: layoutId } = meta; + const { template_id: layoutId, background_color, font_body, font_header } = meta; return { layoutId, postTitle: getEditedPostAttribute( 'title' ), postBlocks: getBlocks(), isEditedPostEmpty: isEditedPostEmpty(), currentPostId: getCurrentPostId(), + stylingMeta: { + background_color, + font_body, + font_header, + }, }; } ), - withDispatch( ( dispatch, { currentPostId } ) => { + withDispatch( ( dispatch, { currentPostId, stylingMeta } ) => { const { replaceBlocks } = dispatch( 'core/block-editor' ); const { editPost } = dispatch( 'core/editor' ); const { saveEntityRecord } = dispatch( 'core' ); @@ -48,7 +53,7 @@ export default compose( [ editPost( { meta: { template_id: id } } ); saveEntityRecord( 'postType', NEWSLETTER_CPT_SLUG, { id: currentPostId, - meta: { template_id: id }, + meta: { template_id: id, ...stylingMeta }, } ); }, saveLayout: payload => @@ -67,6 +72,7 @@ export default compose( [ postBlocks, postTitle, isEditedPostEmpty, + stylingMeta, } ) => { const [ warningModalVisible, setWarningModalVisible ] = useState( false ); const { layouts, isFetchingLayouts } = useLayoutsState(); @@ -108,13 +114,15 @@ export default compose( [ }; const postContent = useMemo( () => serialize( postBlocks ), [ postBlocks ] ); - const isPostContentSameAsLayout = postContent === usedLayout.post_content; + const isPostContentSameAsLayout = + postContent === usedLayout.post_content && isEqual( usedLayout.meta, stylingMeta ); const handleSaveAsLayout = () => { setIsSavingLayout( true ); const updatePayload = { title: newLayoutName, content: postContent, + meta: stylingMeta, }; saveLayout( updatePayload ).then( newLayout => { setIsManageModalVisible( false ); @@ -130,8 +138,9 @@ export default compose( [ ) { setIsSavingLayout( true ); const updatePayload = { - content: postContent, id: usedLayout.ID, + content: postContent, + meta: stylingMeta, }; saveLayout( updatePayload ).then( handleLayoutUpdate ); } @@ -150,7 +159,8 @@ export default compose( [
-