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

Dynamic Tags - Reduce and cache replacement requests #1619

Open
wants to merge 4 commits into
base: release/2.0.0
Choose a base branch
from
Open
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
31 changes: 29 additions & 2 deletions includes/dynamic-tags/class-dynamic-tags.php
Original file line number Diff line number Diff line change
Expand Up @@ -499,15 +499,32 @@ public function register_rest_routes() {
* @return WP_REST_Response
*/
public function get_dynamic_tag_replacements( $request ) {
$content = urldecode( $request->get_param( 'content' ) );
$content = $request->get_param( 'content' );
$context = $request->get_param( 'context' );
$fallback_id = $context['postId'] ?? 0;
$client_id = $request->get_param( 'clientId' );
$post_id = $context['postId'] ?? 0;
$fallback_id = $post_id;
$instance = new stdClass();
$replacements = [];

// Set up an instance object with a context key.
$instance->context = $context;

// Create a unique cache key.
$cache_key = sprintf(
'replacements_%s_%s_%s',
md5( $content ),
$client_id,
$post_id
);

$replacements = wp_cache_get( $cache_key, 'generate_blocks_dynamic_tags' );

// Return the cache here if present.
if ( false !== $replacements ) {
return rest_ensure_response( $replacements );
}

$all_tags = GenerateBlocks_Register_Dynamic_Tag::get_tags();
$tags_list = [];

Expand Down Expand Up @@ -567,6 +584,16 @@ public function get_dynamic_tag_replacements( $request ) {
}
}

// Set the cache with filterable duration.
/**
* Set the duration of the cache for dynamic tag replacements.
*
* @since 2.0.0
*/
$cache_duration = apply_filters( 'generateblocks_dynamic_tags_replacement_cache_duration', 3600, $content, $context, $request );

wp_cache_set( $cache_key, $replacements, 'generateblocks_dynamic_tags', $cache_duration );

return rest_ensure_response( $replacements );
}

Expand Down
9 changes: 1 addition & 8 deletions src/dynamic-tags/components/DynamicTagBlockToolbar.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ToolbarGroup, ToolbarButton } from '@wordpress/components';
import { BlockControls, store as blockEditorStore } from '@wordpress/block-editor';
import { useEffect, useMemo } from '@wordpress/element';
import { useMemo } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { useSelect } from '@wordpress/data';
import { create, insert, replace, RichTextData } from '@wordpress/rich-text';
Expand Down Expand Up @@ -47,7 +47,6 @@ export function DynamicTagBlockToolbar( {
value,
contentMode,
setContentMode,
isSelected,
onChange,
context,
} ) {
Expand All @@ -74,12 +73,6 @@ export function DynamicTagBlockToolbar( {
return contentValue.substring( selectionStart.offset, selectionEnd.offset );
}, [ selectionStart, selectionEnd, value ] );

useEffect( () => {
if ( foundTags.length && ! isSelected ) {
setContentMode( 'preview' );
}
}, [ foundTags.length, isSelected ] );

return (
<BlockControls>
<ToolbarGroup>
Expand Down
5 changes: 3 additions & 2 deletions src/dynamic-tags/utils.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import apiFetch from '@wordpress/api-fetch';
import { applyFilters } from '@wordpress/hooks';

export async function replaceTags( content, context = {} ) {
export async function replaceTags( { content, context = {}, clientId } ) {
// Define an async function to fetch data
try {
const response = await apiFetch( {
path: '/generateblocks/v1/dynamic-tag-replacements',
method: 'POST',
data: {
content,
context: applyFilters( 'generateblocks.editor.preview.context', context, { content } ),
context: applyFilters( 'generateblocks.editor.preview.context', context, { content, clientId } ),
clientId,
},
} );

Expand Down
44 changes: 41 additions & 3 deletions src/editor/style-html-attribute.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,61 @@
import { addFilter } from '@wordpress/hooks';
import { replaceTags } from '../dynamic-tags/utils';

const cache = {};

function getCacheKey( clientId, context ) {
const {
'generateblocks/loopIndex': loopIndex,
postId,
} = context;

let key = '';

if ( loopIndex ) {
key += `${ loopIndex }_`;
}

if ( postId ) {
key += `${ postId }_`;
}

key += clientId;

return key;
}

addFilter(
'generateblocks.editor.htmlAttributes.style',
'generateblocks/styleWithReplacements',
async( style, props ) => {
const { context } = props;
const { context, clientId } = props;

// Check if any replacements need to be made
if ( ! style.includes( '{{' ) ) {
if ( ! style.includes( '{{' ) || ! style ) {
return style;
}

const replacements = await replaceTags( style, context );
const blockCacheKey = getCacheKey( clientId, context );

// Prime the cache for this block.
if ( ! cache[ blockCacheKey ] ) {
cache[ blockCacheKey ] = {};
}

// Get the cached result if available.
if ( cache[ clientId ][ style ] ) {
return cache[ style ];
}

const replacements = await replaceTags( { content: style, context, clientId } );

if ( ! replacements.length ) {
return style;
}

// Cache the result.
cache[ clientId ][ style ] = replacements;

const withReplacements = replacements.reduce( ( acc, { original, replacement, fallback } ) => {
if ( ! replacement ) {
return acc.replaceAll( original, fallback );
Expand Down
59 changes: 52 additions & 7 deletions src/hoc/withDynamicTag.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,37 @@
import { useState, useEffect } from '@wordpress/element';
import { useState, useEffect, useMemo } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import { replaceTags } from '../dynamic-tags/utils';

const cache = {};

function getCacheKey( clientId, context ) {
const {
'generateblocks/loopIndex': loopIndex,
postId,
} = context;

let key = '';

if ( loopIndex ) {
key += `${ loopIndex }_`;
}

if ( postId ) {
key += `${ postId }_`;
}

key += clientId;

return key;
}

export function withDynamicTag( WrappedComponent ) {
return ( ( props ) => {
const {
context,
attributes,
clientId,
isSelected,
} = props;

const {
Expand All @@ -16,10 +41,15 @@ export function withDynamicTag( WrappedComponent ) {
} = attributes;

const [ dynamicTagValue, setDynamicTagValue ] = useState( '' );
const [ contentMode, setContentMode ] = useState( 'edit' );
const [ contentMode, setContentMode ] = useState( 'preview' );
const isSavingPost = useSelect( ( select ) => select( 'core/editor' ).isSavingPost() );
const blockCacheKey = getCacheKey( clientId, context );

const getContentValue = () => {
if ( ! cache[ blockCacheKey ] ) {
cache[ blockCacheKey ] = {};
}

const contentValue = useMemo( () => {
if ( 'img' === tagName ) {
return htmlAttributes?.src;
}
Expand All @@ -29,8 +59,7 @@ export function withDynamicTag( WrappedComponent ) {
}

return content?.text ?? content;
};
const contentValue = getContentValue();
}, [ tagName, htmlAttributes?.src, content ] );

useEffect( () => {
if ( ! contentValue || ! contentValue.includes( '{{' ) ) {
Expand All @@ -43,14 +72,30 @@ export function withDynamicTag( WrappedComponent ) {
return;
}

if ( cache[ blockCacheKey ][ contentValue ] ) {
setDynamicTagValue( cache[ blockCacheKey ][ contentValue ] );
return;
}

async function fetchData() {
const response = await replaceTags( contentValue, context );
const response = await replaceTags( { content: contentValue, context, clientId } );

setDynamicTagValue( response );

// Cache the response.
cache[ blockCacheKey ][ contentValue ] = response;
}

fetchData();
}, [ contentValue, contentMode, context, tagName, isSavingPost ] );
}, [
contentValue,
contentMode,
context,
tagName,
isSavingPost,
blockCacheKey,
isSelected,
] );

return (
<WrappedComponent
Expand Down
Loading