/**
* Smart Post Show Pro - Divi 5 Module
*
* React component for Divi 5 Visual Builder
* Uses useFetch pattern following official Divi 5 Dynamic Module example
*
* @package Smart_Post_Show_Pro
*/
import metadata from "./module.json";
import { SettingsContent } from "./settings-content";
import "./module-icons"; // Import to register custom icon
// Access React from global scope (Divi provides this).
const React = window?.vendor?.React;
const { useEffect, useRef } = React || {};
// Access Divi globals - these are provided by Divi in the window object
const ModuleContainer = window?.divi?.module?.ModuleContainer;
const StyleContainer = window?.divi?.module?.StyleContainer;
const elementClassnames = window?.divi?.module?.elementClassnames;
const registerModule = window?.divi?.moduleLibrary?.registerModule;
const addAction = window?.vendor?.wp?.hooks?.addAction;
const useFetch = window?.divi?.rest?.useFetch;
/**
* Styles Component
* Generates module styles
*/
const ModuleStyles = ({ elements, settings, mode, state, noStyleTag }) => {
return (
{elements.style({
attrName: "module",
styleProps: { disabledOn: { disabledModuleVisibility: settings?.disabledModuleVisibility } },
})}
);
};
/**
* Script Data Component
* Outputs module script data
*/
const ModuleScriptData = ({ elements }) => {
return <>{elements.scriptData({ attrName: "module" })}>;
};
/**
* Classnames Function
* Adds module CSS classes
*/
const moduleClassnames = ({ classnamesInstance, attrs }) => {
classnamesInstance.add(elementClassnames({ attrs: attrs?.module?.decoration ?? {} }));
};
/**
* Edit Renderer Component
*
* Uses useFetch pattern following official Divi 5 Dynamic Module example.
* Fetches template HTML from REST API for visual builder.
* CSS and Google Fonts are now included inline in the HTML response.
*/
const EditRenderer = ({ attrs, id, name, elements }) => {
const templateId = attrs?.templateId?.innerContent?.desktop?.value || "0";
// useFetch hook for fetching template HTML from REST API.
const { fetch, response, isLoading } = useFetch(null);
// Reference for handling fetch abort (following Divi 5 pattern).
const fetchAbortRef = useRef(null);
// Track last fetched template ID to prevent duplicate requests.
const lastFetchedRef = useRef(null);
// Load template HTML when templateId changes (following Divi 5 pattern).
useEffect(() => {
// Don't fetch if no template selected.
if (!templateId || templateId === "0") {
return;
}
// Prevent duplicate requests for the same template ID.
if (lastFetchedRef.current === templateId) {
return;
}
// Mark this template as being fetched.
lastFetchedRef.current = templateId;
// Abort previous fetch if there's any.
if (fetchAbortRef.current) {
fetchAbortRef.current.abort();
}
// Create new AbortController instance.
fetchAbortRef.current = new AbortController();
// Fetch template HTML from REST API (following Divi 5 useFetch pattern).
// Response includes HTML with inline CSS and Google Fonts.
fetch({
method: "GET",
restRoute: `/spsp/divi5/v1/template-html?template_id=${templateId}`,
})
.then(() => {
setTimeout(() => {
if (typeof window !== "undefined" && typeof window.init === "function") {
window.init();
}
}, 0);
})
.catch((error) => {
// Only log non-abort errors.
if (error.name !== "AbortError") {
console.error("Smart Post Show Pro: Error loading template", error);
}
});
// Cleanup function - abort fetch on unmount or templateId change.
return () => {
if (fetchAbortRef.current) {
fetchAbortRef.current.abort();
fetchAbortRef.current = null;
}
};
// Only depend on templateId - NOT fetch (causes infinite loop)
}, [templateId]);
// Compute template HTML from response.
let templateHtml = '
Failed to load template
';
if (response && response.success && response.html) {
templateHtml = response.html;
} else if (response && !response.success) {
templateHtml = 'Failed to load template
';
} else if (!response) {
templateHtml = 'No response from server
';
}
// Following Divi 5 pattern: conditional rendering inside return statement.
return (
{elements.styleComponents({ attrName: "module" })}
{!templateId || templateId === "0" ? (
Please select a saved template
) : isLoading ? (
) : (
)}
);
};
/**
* Smart Post Show Pro Module Definition
* Exported for Divi 5 to pick up
*/
export const spspDivi5SmartPostShow = {
metadata,
renderers: {
edit: EditRenderer,
},
settings: {
content: SettingsContent,
},
};
// Also register via hooks for compatibility
addAction("divi.moduleLibrary.registerModuleLibraryStore.after", "spsp.divi5SmartPostShow", () => {
registerModule(spspDivi5SmartPostShow.metadata, spspDivi5SmartPostShow);
});