File "PageContentShell.jsx"
Full Path: /home/shadsolw/public_html/wp-content/plugins/extendify/src/Agent/workflows/content/components/PageContentShell.jsx
File size: 5.48 KB
MIME-type: text/x-java
Charset: utf-8
import { addIdAttributeToBlock } from '@launch/lib/blocks';
import { usePageCustomContent } from '@page-creator/hooks/usePageCustomContent';
import { processPatterns } from '@page-creator/lib/processPatterns.js';
import { usePageDescriptionStore } from '@page-creator/state/cache';
import { installBlocks } from '@page-creator/util/installBlocks.js';
import { syncPageTitleTemplate } from '@page-creator/util/syncPageTitleTemplate.js';
import { render } from '@shared/lib/dom';
import { pageNames } from '@shared/lib/pages';
import apiFetch from '@wordpress/api-fetch';
import { registerCoreBlocks } from '@wordpress/block-library';
import { getBlockTypes, rawHandler, serialize } from '@wordpress/blocks';
import { useDispatch, useSelect } from '@wordpress/data';
import { store as editorStore } from '@wordpress/editor';
import { useEffect, useRef, useState } from '@wordpress/element';
const { pageTitlePattern } = window.extPageCreator ?? {};
const PageContentShell = ({ pageDescription, onComplete }) => {
const { page, loading } = usePageCustomContent();
const { setDescription } = usePageDescriptionStore();
const { editPost } = useDispatch(editorStore);
const [patterns, setPatterns] = useState([]);
const once = useRef(false);
const { theme, templates } = useSelect((select) => {
const core = select('core');
const current = core.getCurrentTheme();
return {
theme: current,
templates: core.getEntityRecords('postType', 'wp_template', {
per_page: -1,
context: 'edit',
theme: current?.stylesheet,
}),
};
}, []);
useEffect(() => {
if (getBlockTypes().length !== 0) return;
registerCoreBlocks();
}, []);
useEffect(() => {
setDescription(pageDescription);
}, [pageDescription, setDescription]);
useEffect(() => {
if (!page && loading) return;
if (once.current) return;
once.current = true;
(async () => {
// If page-with-title template isn’t customized and a page-title pattern is stashed, update the template with it.
await syncPageTitleTemplate(pageTitlePattern);
const patterns = await processPatterns(page?.patterns);
await installBlocks({ patterns });
setPatterns(patterns);
})();
}, [loading, page, setPatterns]);
useEffect(() => {
if (!patterns?.length || !once.current) return;
if (!theme || !Array.isArray(templates)) return;
const isExtendable = theme.textdomain === 'extendable';
const hasPageWithTitle =
isExtendable && templates.some((t) => t.slug === 'page-with-title');
const id = setTimeout(async () => {
const result = await insertPage({
hasPageWithTitle,
patterns,
title: page.title,
});
onComplete(result);
}, 1000);
return () => clearTimeout(id);
}, [patterns, editPost, page, theme, templates, onComplete]);
return null; // Renders nothing
};
const insertPage = async ({ hasPageWithTitle, patterns, title }) => {
const patternsToInsert = hasPageWithTitle
? patterns.filter((p) => !p.patternTypes?.includes('page-title'))
: patterns;
const pagePatterns = patternsToInsert.map(({ code, ...prop }) => {
// find links with #extendify- like href="#extendify-hero-cta"
const linksRegex = /href="#extendify-([^"]+)"/g;
return {
...prop,
// replaceAll() is an ES2021 method. Since the regex already has the global flag (/g),
// we can use the older replace() method for broader browser compatibility.
code: code.replace(linksRegex, 'href="#"'),
};
});
const HTML = pagePatterns.map(({ code }) => code).join('');
const blocks = rawHandler({ HTML });
const content = [];
// Use this to avoid adding duplicate Ids to patterns
const seenPatternTypes = new Set();
for (const [i, pattern] of blocks.entries()) {
const patternType = pagePatterns[i].patternTypes?.[0];
const serializedBlock = serialize(pattern);
// Get the translated slug
const { slug } =
Object.values(pageNames).find(({ alias }) =>
alias.includes(patternType),
) || {};
// If we've already seen this slug, or no slug found, return the pattern unchanged
if (seenPatternTypes.has(slug) || !slug) {
content.push(serializedBlock);
continue;
}
// Add the slug to the seen list so we don't add it again
seenPatternTypes.add(slug);
content.push(addIdAttributeToBlock(serializedBlock, slug));
}
const data = {
title,
status: 'draft',
content: content.join(''),
template: !hasPageWithTitle ? 'no-title-sticky-header' : 'page-with-title',
meta: { made_with_extendify_launch: true },
};
return await apiFetch({ path: '/wp/v2/pages', method: 'POST', data });
};
// This is used as a bridge between this hidden component and the tool.
export const generatePage = (pageDescription) => {
return new Promise((resolve, reject) => {
const container = document.createElement('div');
container.style.display = 'none';
document.body.appendChild(container);
let isCompleted = false;
let timeout;
const cleanup = () => {
if (timeout) clearTimeout(timeout);
if (container.parentNode) container.remove();
};
const handleComplete = (data) => {
if (isCompleted) return;
isCompleted = true;
cleanup();
!data
? reject(new Error('Something went wrong while creating the page'))
: resolve(data);
};
timeout = setTimeout(() => {
if (!isCompleted) {
handleComplete(null);
}
}, 180000);
try {
render(
<PageContentShell
onComplete={(data) => {
clearTimeout(timeout);
handleComplete(data);
}}
pageDescription={pageDescription}
/>,
container,
);
} catch (error) {
clearTimeout(timeout);
cleanup();
reject(error);
}
});
};