UNPKG

@wordpress/editor

Version:
333 lines (331 loc) 11.2 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // packages/editor/src/components/collab-sidebar/hooks.js var hooks_exports = {}; __export(hooks_exports, { useEnableFloatingSidebar: () => useEnableFloatingSidebar, useFloatingBoard: () => useFloatingBoard, useNoteActions: () => useNoteActions, useNoteThreads: () => useNoteThreads }); module.exports = __toCommonJS(hooks_exports); var import_i18n = require("@wordpress/i18n"); var import_element = require("@wordpress/element"); var import_core_data = require("@wordpress/core-data"); var import_data = require("@wordpress/data"); var import_block_editor = require("@wordpress/block-editor"); var import_notices = require("@wordpress/notices"); var import_dom = require("@wordpress/dom"); var import_html_entities = require("@wordpress/html-entities"); var import_interface = require("@wordpress/interface"); var import_store = require("../../store/index.cjs"); var import_constants = require("./constants.cjs"); var import_lock_unlock = require("../../lock-unlock.cjs"); var import_board_store = require("./board-store.cjs"); var import_utils = require("./utils.cjs"); var { cleanEmptyObject } = (0, import_lock_unlock.unlock)(import_block_editor.privateApis); function useNoteThreads(postId) { const queryArgs = { post: postId, type: "note", status: "all", per_page: -1 }; const { records: threads } = (0, import_core_data.useEntityRecords)( "root", "comment", queryArgs, { enabled: !!postId && typeof postId === "number" } ); const { getBlockAttributes } = (0, import_data.useSelect)(import_block_editor.store); const { clientIds } = (0, import_data.useSelect)((select) => { const { getClientIdsWithDescendants } = select(import_block_editor.store); return { clientIds: getClientIdsWithDescendants() }; }, []); const { notes, unresolvedNotes } = (0, import_element.useMemo)(() => { if (!threads || threads.length === 0) { return { notes: [], unresolvedNotes: [] }; } const blocksWithNotes = {}; const clientIdByNoteId = /* @__PURE__ */ new Map(); for (const clientId of clientIds) { const noteId = getBlockAttributes(clientId)?.metadata?.noteId; if (noteId) { const key = String(noteId); blocksWithNotes[clientId] = key; clientIdByNoteId.set(key, clientId); } } const threadsById = /* @__PURE__ */ new Map(); const rootThreads = []; for (const item of threads) { const thread = { ...item, reply: [], blockClientId: item.parent === 0 ? clientIdByNoteId.get(String(item.id)) ?? null : null }; threadsById.set(item.id, thread); if (item.parent === 0) { rootThreads.push(thread); } } for (const item of threads) { if (item.parent !== 0) { threadsById.get(item.parent)?.reply.unshift(threadsById.get(item.id)); } } if (rootThreads.length === 0) { return { notes: [], unresolvedNotes: [] }; } const unresolved = []; const resolved = []; for (const noteId of Object.values(blocksWithNotes)) { const thread = threadsById.get(Number(noteId)) ?? threadsById.get(noteId); if (!thread) { continue; } if (thread.status === "hold") { unresolved.push(thread); } else if (thread.status === "approved") { resolved.push(thread); } } const orphans = rootThreads.filter( (thread) => !thread.blockClientId ); return { notes: [...unresolved, ...resolved, ...orphans], unresolvedNotes: unresolved }; }, [clientIds, threads, getBlockAttributes]); return { notes, unresolvedNotes }; } function useNoteActions() { const { createNotice } = (0, import_data.useDispatch)(import_notices.store); const { saveEntityRecord, deleteEntityRecord } = (0, import_data.useDispatch)(import_core_data.store); const { getCurrentPostId } = (0, import_data.useSelect)(import_store.store); const { getBlockAttributes, getSelectedBlockClientId } = (0, import_data.useSelect)(import_block_editor.store); const { updateBlockAttributes } = (0, import_data.useDispatch)(import_block_editor.store); const onError = (error) => { const errorMessage = error.message && error.code !== "unknown_error" ? (0, import_html_entities.decodeEntities)(error.message) : (0, import_i18n.__)("An error occurred while performing an update."); createNotice("error", errorMessage, { type: "snackbar", isDismissible: true }); }; const onCreate = async ({ content, parent }) => { try { const savedRecord = await saveEntityRecord( "root", "comment", { post: getCurrentPostId(), content, status: "hold", type: "note", parent: parent || 0 }, { throwOnError: true } ); if (!parent && savedRecord?.id) { const clientId = getSelectedBlockClientId(); const metadata = getBlockAttributes(clientId)?.metadata; updateBlockAttributes(clientId, { metadata: { ...metadata, noteId: savedRecord.id } }); } createNotice( "snackbar", parent ? (0, import_i18n.__)("Reply added.") : (0, import_i18n.__)("Note added."), { type: "snackbar", isDismissible: true } ); return savedRecord; } catch (error) { onError(error); } }; const onEdit = async ({ id, content, status }) => { const messageType = status ? status : "updated"; const messages = { approved: (0, import_i18n.__)("Note marked as resolved."), hold: (0, import_i18n.__)("Note reopened."), updated: (0, import_i18n.__)("Note updated.") }; try { if (status === "approved" || status === "hold") { await saveEntityRecord( "root", "comment", { id, status }, { throwOnError: true } ); const newNoteData = { post: getCurrentPostId(), content: content || "", // Empty content for resolve, content for reopen. type: "note", status, parent: id, meta: { _wp_note_status: status === "approved" ? "resolved" : "reopen" } }; await saveEntityRecord("root", "comment", newNoteData, { throwOnError: true }); } else { const updateData = { id, content, status }; await saveEntityRecord("root", "comment", updateData, { throwOnError: true }); } createNotice( "snackbar", messages[messageType] ?? (0, import_i18n.__)("Note updated."), { type: "snackbar", isDismissible: true } ); } catch (error) { onError(error); } }; const onDelete = async (note) => { try { await deleteEntityRecord("root", "comment", note.id, void 0, { throwOnError: true }); if (!note.parent) { const clientId = getSelectedBlockClientId(); const metadata = getBlockAttributes(clientId)?.metadata; updateBlockAttributes(clientId, { metadata: cleanEmptyObject({ ...metadata, noteId: void 0 }) }); } createNotice("snackbar", (0, import_i18n.__)("Note deleted."), { type: "snackbar", isDismissible: true }); } catch (error) { onError(error); } }; return { onCreate, onEdit, onDelete }; } function useEnableFloatingSidebar(enabled = false) { const registry = (0, import_data.useRegistry)(); (0, import_element.useEffect)(() => { if (!enabled) { return; } const { getActiveComplementaryArea } = registry.select(import_interface.store); const { disableComplementaryArea, enableComplementaryArea } = registry.dispatch(import_interface.store); const unsubscribe = registry.subscribe(() => { if (getActiveComplementaryArea("core") === null) { enableComplementaryArea("core", import_constants.FLOATING_NOTES_SIDEBAR); } }); return () => { unsubscribe(); if (getActiveComplementaryArea("core") === import_constants.FLOATING_NOTES_SIDEBAR) { disableComplementaryArea("core", import_constants.FLOATING_NOTES_SIDEBAR); } }; }, [enabled, registry]); } function useFloatingBoard({ threads, selectedNoteId, isFloating, sidebarRef }) { const [notePositions, setNotePositions] = (0, import_element.useState)({}); const [store] = (0, import_element.useState)(import_board_store.createBoardStore); const heights = (0, import_element.useSyncExternalStore)(store.subscribe, store.getSnapshot); (0, import_element.useEffect)(() => { if (!isFloating || !sidebarRef?.current) { return; } const panel = sidebarRef.current; const blockEl = store.getFirstBlockElement(); const rootEl = blockEl?.closest(".is-root-container") ?? blockEl; const canvas = rootEl ? (0, import_dom.getScrollContainer)(rootEl) : null; const applyScroll = () => { panel.style.setProperty( "--canvas-scroll", `${-(canvas?.scrollTop ?? 0)}px` ); }; const rafId = window.requestAnimationFrame(() => { const result = (0, import_utils.calculateNotePositions)({ threads, selectedNoteId, blockRects: store.getBlockRects(), heights, scrollTop: canvas?.scrollTop ?? 0 }); setNotePositions(result.positions); applyScroll(); }); const view = canvas?.ownerDocument?.defaultView; const listenerOptions = { passive: true, capture: true }; view?.addEventListener("scroll", applyScroll, listenerOptions); return () => { window.cancelAnimationFrame(rafId); view?.removeEventListener("scroll", applyScroll, listenerOptions); }; }, [sidebarRef, heights, isFloating, selectedNoteId, store, threads]); return { notePositions, registerThread: store.registerThread, unregisterThread: store.unregisterThread }; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { useEnableFloatingSidebar, useFloatingBoard, useNoteActions, useNoteThreads }); //# sourceMappingURL=hooks.cjs.map