UNPKG

@wordpress/editor

Version:
368 lines (366 loc) 11.9 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, { useBlockComments: () => useBlockComments, useBlockCommentsActions: () => useBlockCommentsActions, useEnableFloatingSidebar: () => useEnableFloatingSidebar, useFloatingThread: () => useFloatingThread }); module.exports = __toCommonJS(hooks_exports); var import_react_dom = require("@floating-ui/react-dom"); 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_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_utils = require("./utils.cjs"); var { useBlockElement, cleanEmptyObject } = (0, import_lock_unlock.unlock)(import_block_editor.privateApis); function useBlockComments(postId) { const [commentLastUpdated, reflowComments] = (0, import_element.useReducer)( () => Date.now(), 0 ); 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 { resultComments, unresolvedSortedThreads } = (0, import_element.useMemo)(() => { if (!threads || threads.length === 0) { return { resultComments: [], unresolvedSortedThreads: [] }; } const blocksWithComments = clientIds.reduce((results, clientId) => { const commentId = getBlockAttributes(clientId)?.metadata?.noteId; if (commentId) { results[clientId] = commentId; } return results; }, {}); const compare = {}; const result = []; const commentIdToBlockClientId = Object.keys( blocksWithComments ).reduce((mapping, clientId) => { mapping[blocksWithComments[clientId]] = clientId; return mapping; }, {}); threads.forEach((item) => { const itemBlock = commentIdToBlockClientId[item.id]; compare[item.id] = { ...item, reply: [], blockClientId: item.parent === 0 ? itemBlock : null }; }); threads.forEach((item) => { if (item.parent === 0) { result.push(compare[item.id]); } else if (compare[item.parent]) { compare[item.parent].reply.push(compare[item.id]); } }); if (0 === result?.length) { return { resultComments: [], unresolvedSortedThreads: [] }; } const updatedResult = result.map((item) => ({ ...item, reply: [...item.reply].reverse() })); const threadIdMap = new Map( updatedResult.map((thread) => [String(thread.id), thread]) ); const mappedIds = new Set( Object.values(blocksWithComments).map((id) => String(id)) ); const unresolvedSortedComments = Object.values(blocksWithComments).map((commentId) => threadIdMap.get(String(commentId))).filter( (thread) => thread !== void 0 && thread.status === "hold" ); const resolvedSortedComments = Object.values(blocksWithComments).map((commentId) => threadIdMap.get(String(commentId))).filter( (thread) => thread !== void 0 && thread.status === "approved" ); const orphanedComments = updatedResult.filter( (thread) => !mappedIds.has(String(thread.id)) ); const allSortedComments = [ ...unresolvedSortedComments, ...resolvedSortedComments, ...orphanedComments ]; return { resultComments: allSortedComments, unresolvedSortedThreads: unresolvedSortedComments }; }, [clientIds, threads, getBlockAttributes]); return { resultComments, unresolvedSortedThreads, reflowComments, commentLastUpdated }; } function useBlockCommentsActions(reflowComments = import_utils.noop) { 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 } ); setTimeout(reflowComments, 300); return savedRecord; } catch (error) { reflowComments(); 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 newCommentData = { 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", newCommentData, { 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 } ); reflowComments(); } catch (error) { reflowComments(); onError(error); } }; const onDelete = async (comment) => { try { await deleteEntityRecord( "root", "comment", comment.id, void 0, { throwOnError: true } ); if (!comment.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 }); reflowComments(); } catch (error) { reflowComments(); 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 useFloatingThread({ thread, calculatedOffset, setHeights, selectedThread, setBlockRef, commentLastUpdated }) { const blockElement = useBlockElement(thread.blockClientId); const updateHeight = (0, import_element.useCallback)( (id, newHeight) => { setHeights((prev) => { if (prev[id] !== newHeight) { return { ...prev, [id]: newHeight }; } return prev; }); }, [setHeights] ); const { y, refs } = (0, import_react_dom.useFloating)({ placement: "right-start", middleware: [ (0, import_react_dom.offset)({ crossAxis: calculatedOffset || -16 }) ], whileElementsMounted: import_react_dom.autoUpdate }); (0, import_element.useEffect)(() => { if (blockElement) { refs.setReference(blockElement); } }, [blockElement, refs, commentLastUpdated]); (0, import_element.useEffect)(() => { if (refs.floating?.current) { setBlockRef(thread.id, blockElement); } }, [blockElement, thread.id, refs.floating, setBlockRef]); (0, import_element.useEffect)(() => { if (refs.floating?.current) { const newHeight = refs.floating.current.scrollHeight; updateHeight(thread.id, newHeight); } }, [ thread.id, updateHeight, refs.floating, selectedThread, commentLastUpdated ]); return { y, refs }; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { useBlockComments, useBlockCommentsActions, useEnableFloatingSidebar, useFloatingThread }); //# sourceMappingURL=hooks.cjs.map