import { BlockPreview } from '@wordpress/block-editor' import { rawHandler } from '@wordpress/blocks' import { useEffect, useState, useRef, useMemo } from '@wordpress/element' import { __, sprintf } from '@wordpress/i18n' import classNames from 'classnames' import { Templates as TemplatesApi } from '@extendify/api/Templates' import { useIsDevMode } from '@extendify/hooks/helpers' import { AuthorizationCheck, Middleware } from '@extendify/middleware' import { useGlobalStore } from '@extendify/state/GlobalState' import { useUserStore } from '@extendify/state/User' import { injectTemplateBlocks } from '@extendify/util/templateInjection' import { DevButtonOverlay } from './DevHelpers' import { NoImportModal } from './modals/NoImportModal' import { ProModal } from './modals/ProModal' const canImportMiddleware = Middleware([ 'hasRequiredPlugins', 'hasPluginsActivated', ]) export function ImportTemplateBlock({ template, maxHeight }) { const importButtonRef = useRef(null) const once = useRef(false) const hasAvailableImports = useUserStore( (state) => state.hasAvailableImports, ) const loggedIn = useUserStore((state) => state.apiKey.length) const setOpen = useGlobalStore((state) => state.setOpen) const pushModal = useGlobalStore((state) => state.pushModal) const removeAllModals = useGlobalStore((state) => state.removeAllModals) const blocks = useMemo( () => rawHandler({ HTML: template.fields.code }), [template.fields.code], ) const [loaded, setLoaded] = useState(false) const devMode = useIsDevMode() const [topValue, setTopValue] = useState(0) const focusTrapInnerBlocks = () => { if (once.current) return once.current = true Array.from( importButtonRef.current.querySelectorAll( 'a, button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])', ), ).forEach((el) => el.setAttribute('tabIndex', '-1')) } const importTemplates = async () => { await canImportMiddleware.check(template) AuthorizationCheck(canImportMiddleware) .then(() => { setTimeout(() => { injectTemplateBlocks(blocks, template) .then(() => removeAllModals()) .then(() => setOpen(false)) .then(() => canImportMiddleware.reset()) }, 100) }) .catch(() => {}) } const handleKeyDown = (event) => { if (['Enter', 'Space', ' '].includes(event.key)) { event.stopPropagation() event.preventDefault() importTemplate() } } const importTemplate = () => { // Make a note that they attempted to import TemplatesApi.maybeImport(template) if (template?.fields?.pro && !loggedIn) { pushModal() return } if (!hasAvailableImports()) { pushModal() return } importTemplates() } // Trigger resize event on the live previews to add // Grammerly/Loom/etc compatability // TODO: This can probably be removed after WP 5.9 useEffect(() => { const rafIds = [] const timeouts = [] let rafId1, rafId2, rafId3, rafId4 rafId1 = window.requestAnimationFrame(() => { rafId2 = window.requestAnimationFrame(() => { importButtonRef.current .querySelectorAll('iframe') .forEach((frame) => { const inner = frame.contentWindow.document.body const rafId = window.requestAnimationFrame(() => { const maybeRoot = inner.querySelector('.is-root-container') if (maybeRoot) { const height = maybeRoot?.offsetHeight if (height) { rafId4 = window.requestAnimationFrame( () => { frame.contentWindow.dispatchEvent( new Event('resize'), ) }, ) const id = window.setTimeout(() => { frame.contentWindow.dispatchEvent( new Event('resize'), ) }, 2000) timeouts.push(id) } } frame.contentWindow.dispatchEvent( new Event('resize'), ) }) rafIds.push(rafId) }) rafId3 = window.requestAnimationFrame(() => { window.dispatchEvent(new Event('resize')) setLoaded(true) }) }) }) return () => { ;[...rafIds, rafId1, rafId2, rafId3, rafId4].forEach((id) => window.cancelAnimationFrame(id), ) timeouts.forEach((id) => window.clearTimeout(id)) } }, []) useEffect(() => { if (!Number.isInteger(maxHeight)) return const button = importButtonRef.current const handleIn = () => { // The live component changes over time so easier to query on demand const height = button.offsetHeight button.style.transitionDuration = height * 1.5 + 'ms' setTopValue(Math.abs(height - maxHeight) * -1) } const handleOut = () => { const height = button.offsetHeight button.style.transitionDuration = height / 1.5 + 'ms' setTopValue(0) } button.addEventListener('focus', handleIn) button.addEventListener('mouseenter', handleIn) button.addEventListener('blur', handleOut) button.addEventListener('mouseleave', handleOut) return () => { button.removeEventListener('focus', handleIn) button.removeEventListener('mouseenter', handleIn) button.removeEventListener('blur', handleOut) button.removeEventListener('mouseleave', handleOut) } }, [maxHeight]) return (
{/* Show dev info after the preview is loaded to trigger observer */} {devMode && loaded && } {template?.fields?.pro && (
{__('Pro', 'extendify')}
)}
) }