PluginProbe
Extendify / 3.1.6
Extendify v3.1.6
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
extendify / src / PageCreator / components / Modal.jsx

Modal.jsx in Extendify 3.1.6, at src/PageCreator/components/Modal.jsx

218 lines 6.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { Dialog, DialogTitle } from '@headlessui/react';
2 import { Topbar } from '@page-creator/components/topbar/Topbar';
3 import { MainPage } from '@page-creator/pages/MainPage';
4 import { useGlobalsStore } from '@page-creator/state/global';
5 import { usePagesStore } from '@page-creator/state/pages';
6 import { useUserStore } from '@page-creator/state/user';
7 import { insertBlocks } from '@page-creator/util/insert';
8 import { useActivityStore } from '@shared/state/activity';
9 import { dispatch, select, useDispatch, useSelect } from '@wordpress/data';
10 import { store as editPostStore } from '@wordpress/edit-post';
11 import { store as editorStore } from '@wordpress/editor';
12 import { useEffect, useLayoutEffect, useRef } from '@wordpress/element';
13 import { __ } from '@wordpress/i18n';
14 import { motion } from 'framer-motion';
15
16 export const Modal = () => {
17 const { incrementActivity } = useActivityStore();
18 const { open, setOpen } = useGlobalsStore();
19 const { updateUserOption, openOnNewPage } = useUserStore();
20 const { setPage } = usePagesStore();
21 const { resetBlocks } = dispatch('core/block-editor');
22 const { closeGeneralSidebar } = useDispatch(editPostStore);
23
24 const renderingModes = useSelect(
25 (s) => s('core/preferences').get('core', 'renderingModes') || {},
26 [],
27 );
28 const isTemplateShown =
29 renderingModes?.extendable?.page === 'template-locked';
30 const { set: setPreference } = useDispatch('core/preferences');
31
32 const setRenderingMode = (mode) =>
33 setPreference('core', 'renderingModes', {
34 ...renderingModes,
35 extendable: { ...(renderingModes.extendable || {}), page: mode },
36 });
37
38 const { createNotice } = dispatch('core/notices');
39 const once = useRef(false);
40 const onClose = () => {
41 incrementActivity('page-creator-modal-close');
42 setOpen(false);
43 // Reset the page view back to the dashboard (page 0)
44 setPage(0);
45 };
46
47 const hasOnlyTopEmptyParagraph = () => {
48 const { getBlocks } = select('core/block-editor');
49 const blocks = getBlocks();
50
51 return (
52 blocks.length === 1 &&
53 blocks[0].name === 'core/paragraph' &&
54 blocks[0].attributes?.content?.text === ''
55 );
56 };
57
58 // Get post attributes using WordPress's useSelect hook
59 const postAttribute = useSelect((select) => {
60 const editor = select(editorStore);
61
62 return {
63 isPage: editor.getCurrentPostType() === 'page',
64 isNew: editor.isCleanNewPost(),
65 isEmptyPost: editor.isEditedPostEmpty(),
66 };
67 }, []); // Empty dependency array since we want it to update based on store changes
68
69 // Function to handle inserting a new page with the given blocks
70 const insertPage = async (blocks, title) => {
71 // Close sidebar
72 closeGeneralSidebar();
73
74 try {
75 if (isTemplateShown) {
76 setRenderingMode('post-only');
77 // Use raf for a re-render
78 await new Promise((resolve) => requestAnimationFrame(resolve));
79 }
80 // Delete the blocks before we insert our own.
81 if (hasOnlyTopEmptyParagraph() || !postAttribute.isEmptyPost)
82 resetBlocks([]);
83
84 // Insert the blocks into the editor
85 await insertBlocks(blocks);
86
87 // Update the post title
88 dispatch('core/editor').editPost({ title });
89
90 // Track the activity of inserting a page
91 incrementActivity('page-creator-page-insert');
92 // Close the modal/dialog
93 onClose();
94 // Show a success notification to the user
95 createNotice('info', __('Page added', 'extendify-local'), {
96 isDismissible: true, // Allow the notice to be dismissed
97 type: 'snackbar', // Display as a snackbar-style notification
98 });
99 } catch (error) {
100 console.error('Failed to insert page:', error);
101 createNotice('error', __('Failed to add page', 'extendify-local'), {
102 isDismissible: true,
103 type: 'snackbar',
104 });
105 } finally {
106 // Set back to the previous rendering mode
107 if (isTemplateShown) setRenderingMode('template-locked');
108 }
109 };
110
111 useLayoutEffect(() => {
112 if (open || once.current) return;
113 once.current = true;
114
115 if (openOnNewPage && postAttribute.isNew) {
116 // Minimize HC if its open
117 window.dispatchEvent(new CustomEvent('extendify-hc:minimize'));
118 // Close library
119 window.dispatchEvent(new CustomEvent('extendify::close-library'));
120 incrementActivity('page-creator-auto-open');
121 setOpen(true);
122 }
123 const search = new URLSearchParams(window.location.search);
124 if (search.has('ext-open-ai-creator')) {
125 setOpen(true);
126 incrementActivity('page-creator-search-param-auto-open');
127 }
128 }, [openOnNewPage, setOpen, incrementActivity, open, postAttribute.isNew]);
129
130 useEffect(() => {
131 const search = new URLSearchParams(window.location.search);
132 const { pathname } = window.location;
133
134 if (search.has('ext-page-creator-close')) {
135 setOpen(false);
136 search.delete('ext-page-creator-close');
137 window.history.replaceState({}, '', `${pathname}?${search.toString()}`);
138 incrementActivity('page-creator-search-param-auto-close');
139 }
140
141 if (search.has('ext-open')) {
142 // Close library
143 window.dispatchEvent(new CustomEvent('extendify::open-library'));
144 search.delete('ext-open');
145 window.history.replaceState({}, '', `${pathname}?${search.toString()}`);
146 }
147 }, [setOpen, incrementActivity]);
148
149 useEffect(() => {
150 const openModal = () => setOpen(true);
151 const closeModal = () => setOpen(false);
152
153 window.addEventListener('extendify::open-page-creator', openModal);
154 window.addEventListener('extendify::close-page-creator', closeModal);
155 return () => {
156 window.removeEventListener('extendify::open-page-creator', openModal);
157 window.removeEventListener('extendify::close-page-creator', closeModal);
158 };
159 }, [setOpen]);
160
161 useEffect(() => {
162 if (!open) return;
163 const welcomeGuide =
164 select('core/edit-post').isFeatureActive('welcomeGuide');
165 if (welcomeGuide) {
166 dispatch('core/edit-post').toggleFeature('welcomeGuide');
167 }
168 }, [open]);
169
170 if (!open) return null;
171
172 return (
173 <Dialog
174 className="extendify-page-creator extendify-page-creator-modal"
175 open={open}
176 static
177 aria-labelledby="page-creator-modal"
178 role="dialog"
179 onClose={() => undefined}
180 >
181 <div className="mx-auto flex h-full w-full items-center justify-center pt-10 md:p-10 absolute inset-0">
182 <div
183 onClick={onClose}
184 className="fixed inset-0 bg-black/30"
185 style={{ backdropFilter: 'blur(2px)' }}
186 aria-hidden="true"
187 />
188 <motion.div
189 key="ai-page-generator-modal"
190 initial={{ y: 30, opacity: 0 }}
191 animate={{ y: 0, opacity: 1 }}
192 exit={{ y: 0, opacity: 0 }}
193 transition={{ duration: 0.3 }}
194 className="relative mx-auto h-full max-h-full w-full max-w-4xl rounded-lg bg-white shadow-2xl sm:flex sm:overflow-hidden md:h-auto"
195 >
196 <DialogTitle className="sr-only">
197 {__('AI Page Creator', 'extendify-local')}
198 </DialogTitle>
199
200 <div className="relative flex w-full flex-col bg-white">
201 <Topbar
202 openOnNewPage={openOnNewPage}
203 updateUserOption={updateUserOption}
204 onClose={onClose}
205 />
206 <div
207 id="extendify-page-creator-pages"
208 className="mx-8 grow overflow-y-auto"
209 >
210 <MainPage insertPage={insertPage} />
211 </div>
212 </div>
213 </motion.div>
214 </div>
215 </Dialog>
216 );
217 };
218