diff --git a/editors/demo/src/Editoria/Editoria.js b/editors/demo/src/Editoria/Editoria.js index 077d263caea7521a03fd5b9736ec17576c2fe699..e35f93c22ab3676f47c63ca8835efee49858f7f3 100644 --- a/editors/demo/src/Editoria/Editoria.js +++ b/editors/demo/src/Editoria/Editoria.js @@ -53,9 +53,9 @@ const Editoria = () => { value={demo} // readonly layout={layout} - onChange={debounce(source => { - console.log(source); - }, 200)} + // onChange={debounce(source => { + // console.log(source); + // }, 200)} user={user} scrollMargin={200} scrollThreshold={200} diff --git a/editors/demo/src/Editoria/config/config.js b/editors/demo/src/Editoria/config/config.js index 1e0f83172e663ee599a6670a6727187af72b90fd..a7e8ea8a66a8aab1c05d250640879a33d6978db2 100644 --- a/editors/demo/src/Editoria/config/config.js +++ b/editors/demo/src/Editoria/config/config.js @@ -184,6 +184,8 @@ export default { }, CommentsService: { // showTitle: true, + readOnlyPost: false, + readOnlyResolve: false, getComments, setComments, getMentionedUsers: (users, text) => { @@ -211,7 +213,7 @@ export default { // // eslint-disable-next-line no-restricted-globals // connectionUrl: 'ws://localhost:5010', // // connectionUrl: 'ws://0.tcp.ap.ngrok.io:17607', - // docIdentifier: 'prosemirror-r5dw4q2fe2eedreeeeeweewwewerc', + // docIdentifier: 'prosemirror-r5dw4dd5eeee344w22rq254werc', // YjsType: 'prosemirror', // }, diff --git a/wax-prosemirror-services/src/CommentsService/components/ConnectedComment.js b/wax-prosemirror-services/src/CommentsService/components/ConnectedComment.js index f4a8d6c4badb7ad89f5d53f4494ff9c7cf25dafa..2abab2df7d17c72bd12fc007ec13f29c1bb86792 100644 --- a/wax-prosemirror-services/src/CommentsService/components/ConnectedComment.js +++ b/wax-prosemirror-services/src/CommentsService/components/ConnectedComment.js @@ -51,8 +51,14 @@ export default ({ }; const commentConfig = app.config.get('config.CommentsService'); - const isReadOnly = - commentConfig && commentConfig.readOnly ? commentConfig.readOnly : false; + const isReadOnlyResolve = + commentConfig && commentConfig.readOnlyResolve + ? commentConfig.readOnlyResolve + : false; + const isReadOnlyPost = + commentConfig && commentConfig.readOnlyPost + ? commentConfig.readOnlyPost + : false; const showTitle = commentConfig && commentConfig.showTitle ? commentConfig.showTitle : false; const usersMentionList = @@ -170,7 +176,8 @@ export default ({ active={isActive} commentData={conversation} commentId={commentId} - isReadOnly={isReadOnly} + isReadOnlyPost={isReadOnlyPost} + isReadOnlyResolve={isReadOnlyResolve} key={commentId} onClickBox={onClickBox} onClickPost={onClickPost} diff --git a/wax-prosemirror-services/src/CommentsService/components/RightArea.js b/wax-prosemirror-services/src/CommentsService/components/RightArea.js index b6a492f7cac960178ea6173a62c0f812a6fd0dbc..69d596b0dc75a546d227f463856c8c3dc6a2a1a0 100644 --- a/wax-prosemirror-services/src/CommentsService/components/RightArea.js +++ b/wax-prosemirror-services/src/CommentsService/components/RightArea.js @@ -1,3 +1,4 @@ +/* eslint-disable no-unused-expressions */ /* eslint-disable no-param-reassign */ /* eslint react/prop-types: 0 */ import React, { useContext, useState, useMemo, useCallback } from 'react'; @@ -24,157 +25,131 @@ export default ({ area, users }) => { const trakChangePlugin = app.PmPlugins.get('trackChangePlugin'); const [marksNodes, setMarksNodes] = useState([]); - const [position, setPosition] = useState(); const [isFirstRun, setFirstRun] = useState(true); + // Memoize the updateMarks function + const updateMarksMemoized = useMemo(() => { + return updateMarks( + pmViews, + CommentDecorationPluginKey?.getState(activeView.state)?.allCommentsList(), + ); + }, [pmViews, activeView.state]); + const setTops = useCallback(() => { const result = []; - let markNodeEl = null; - let annotationTop = 0; - let boxHeight = 0; - let top = 0; - let WaxSurface = {}; - let WaxSurfaceMarginTop = ''; const allCommentsTop = []; - let panelWrapper = {}; - let panelWrapperHeight = {}; - if (main) { - WaxSurface = main.dom.getBoundingClientRect(); - WaxSurfaceMarginTop = window.getComputedStyle(main.dom).marginTop; - } - - each(marksNodes[area], (markNode, pos) => { - let id = ''; - - if (markNode?.node?.attrs.id) { - id = markNode.node.attrs.id; - } else if (markNode?.attrs?.id) { - id = markNode.attrs.id; - } else { - id = markNode.id; - } - - let activeTrackChange = null; - if (trakChangePlugin) - activeTrackChange = trakChangePlugin.getState(activeView.state) - .trackChange; - - let isActive = false; - if ( + // Cache DOM queries + const waxSurface = main?.dom.getBoundingClientRect(); + const waxSurfaceMarginTop = main + ? window.getComputedStyle(main.dom).marginTop + : ''; + const panelWrapper = document.getElementsByClassName('panelWrapper'); + const panelWrapperHeight = + panelWrapper[0]?.getBoundingClientRect().height || 0; + const notesContainer = document.querySelector('#notes-container'); + const waxContainer = document.querySelector('#wax-container'); + const waxContainerTop = waxContainer?.getBoundingClientRect().top || 0; + + // Cache active track change state + const activeTrackChange = trakChangePlugin?.getState(activeView.state) + .trackChange; + + // Pre-calculate common values + const marginTopValue = parseInt(waxSurfaceMarginTop.slice(0, -2), 10) || 0; + const baseOffset = + area === 'main' + ? (waxSurface?.top || 0) + marginTopValue + : panelWrapperHeight + waxContainerTop + 50; + + marksNodes[area]?.forEach((markNode, pos) => { + const id = + markNode?.node?.attrs?.id || markNode?.attrs?.id || markNode.id; + const isActive = (activeComment && id === activeComment.id) || - (activeTrackChange && id === activeTrackChange.attrs.id) - ) - isActive = true; + (activeTrackChange && id === activeTrackChange.attrs.id); - // annotation top + // Get annotation position + let annotationTop = 0; if (area === 'main') { - markNodeEl = document.querySelector(`[data-id="${id}"]`); - if (!markNodeEl && marksNodes[area][pos - 1]) { - markNodeEl = document.querySelector( - `[data-id="${marksNodes[area][pos - 1].id}"]`, - ); - } - + const markNodeEl = + document.querySelector(`[data-id="${id}"]`) || + (pos > 0 + ? document.querySelector( + `[data-id="${marksNodes[area][pos - 1].id}"]`, + ) + : null); if (markNodeEl) { - annotationTop = - markNodeEl.getBoundingClientRect().top - - WaxSurface.top + - parseInt(WaxSurfaceMarginTop.slice(0, -2), 10); + annotationTop = markNodeEl.getBoundingClientRect().top - baseOffset; } - } else { - // Notes - panelWrapper = document.getElementsByClassName('panelWrapper'); - panelWrapperHeight = panelWrapper[0].getBoundingClientRect().height; - - markNodeEl = document - .querySelector('#notes-container') - .querySelector(`[data-id="${id}"]`); + } else if (notesContainer) { + const markNodeEl = notesContainer.querySelector(`[data-id="${id}"]`); if (markNodeEl) { - const WaxContainerTop = document - .querySelector('#wax-container') - .getBoundingClientRect().top; - - annotationTop = - markNodeEl.getBoundingClientRect().top - - panelWrapperHeight - - WaxContainerTop - - 50; + annotationTop = markNodeEl.getBoundingClientRect().top - baseOffset; } } - let boxEl = null; - // get height of this markNode box - if (markNodeEl) { - boxEl = document.querySelector(`div[data-box="${id}"]`); - } - if (boxEl) { - boxHeight = parseInt(boxEl.offsetHeight, 10); - // where the box should move to - top = annotationTop; - } - // if the above comment box has already taken up the height, move down + // Calculate box position + let top = annotationTop; + const boxEl = document.querySelector(`div[data-box="${id}"]`); + const boxHeight = boxEl ? parseInt(boxEl.offsetHeight, 10) : 0; + + // Handle overlaps with previous boxes if (pos > 0) { const previousBox = marksNodes[area][pos - 1]; - const previousEndHeight = previousBox.endHeight; - if (annotationTop < previousEndHeight) { - top = previousEndHeight + 2; + if (annotationTop < previousBox.endHeight) { + top = previousBox.endHeight + 2; } } - // store where the box ends to be aware of overlaps in the next box + + // Store end height for next iteration markNode.endHeight = top + boxHeight + 4; result[pos] = top; allCommentsTop.push({ [id]: result[pos] }); - // if active, move as many boxes above as needed to bring it to the annotation's height + // Handle active state positioning if (isActive) { markNode.endHeight = annotationTop + boxHeight + 3; result[pos] = annotationTop; allCommentsTop[pos][id] = result[pos]; - let b = true; - let i = pos; - - // first one active, none above - if (i === 0) b = false; - while (b) { + // Adjust positions of boxes above active comment + for (let i = pos; i > 0; i--) { const boxAbove = marksNodes[area][i - 1]; const boxAboveEnds = boxAbove.endHeight; const currentTop = result[i]; - const doesOverlap = boxAboveEnds > currentTop; - - if (doesOverlap) { + if (boxAboveEnds > currentTop) { const overlap = boxAboveEnds - currentTop; result[i - 1] -= overlap; - let previousMarkNode = ''; - - if (marksNodes[area][i - 1]?.node?.attrs.id) { - previousMarkNode = marksNodes[area][i - 1].node.attrs.id; - } else if (marksNodes[area][i - 1]?.attrs?.id) { - previousMarkNode = marksNodes[area][i - 1].attrs.id; - } else { - previousMarkNode = marksNodes[area][i - 1].id; - } - - allCommentsTop[i - 1][previousMarkNode] = result[i - 1]; + const previousId = + marksNodes[area][i - 1]?.node?.attrs?.id || + marksNodes[area][i - 1]?.attrs?.id || + marksNodes[area][i - 1].id; + allCommentsTop[i - 1][previousId] = result[i - 1]; + } else { + break; } - - if (!doesOverlap) b = false; - if (i <= 1) b = false; - i -= 1; } } }); + return allCommentsTop; - }); + }, [ + area, + marksNodes, + main, + activeView.state, + activeComment, + trakChangePlugin, + ]); - const recalculateTops = () => { + const recalculateTops = useCallback(() => { setTimeout(() => { setPosition(setTops()); }); - }; + }, [setTops]); useDeepCompareEffect(() => { if (app.config.get('config.YjsService')) { @@ -187,14 +162,7 @@ export default ({ area, users }) => { ), ); } else { - setMarksNodes( - updateMarks( - pmViews, - CommentDecorationPluginKey?.getState( - activeView.state, - )?.allCommentsList(), - ), - ); + setMarksNodes(updateMarksMemoized); } let firstRunTimeout = () => true; @@ -208,12 +176,12 @@ export default ({ area, users }) => { } return () => clearTimeout(firstRunTimeout); - }, [ - updateMarks( - pmViews, - CommentDecorationPluginKey?.getState(activeView.state)?.allCommentsList(), - ), - setTops(), + }, [updateMarksMemoized, setTops, isFirstRun, app.config]); + + // Memoize the marks nodes for the current area + const currentAreaMarks = useMemo(() => marksNodes[area] || [], [ + marksNodes, + area, ]); const CommentTrackComponent = useMemo( @@ -221,15 +189,24 @@ export default ({ area, users }) => { <BoxList activeComment={context.options.activeComment} area={area} - commentsTracks={marksNodes[area] || []} + commentsTracks={currentAreaMarks} position={position} recalculateTops={recalculateTops} users={users} view={main} /> ), - [marksNodes[area] || [], position, users, context.options.activeComment], + [ + currentAreaMarks, + position, + users, + context.options.activeComment, + area, + recalculateTops, + main, + ], ); + return <>{CommentTrackComponent}</>; }; diff --git a/wax-prosemirror-services/src/CommentsService/components/ui/comments/CommentBox.js b/wax-prosemirror-services/src/CommentsService/components/ui/comments/CommentBox.js index 9cc25d87580a7ab4df684ac17611a91426565f9a..28785f70911bda1b6be98c68bbcb479c576e06ad 100644 --- a/wax-prosemirror-services/src/CommentsService/components/ui/comments/CommentBox.js +++ b/wax-prosemirror-services/src/CommentsService/components/ui/comments/CommentBox.js @@ -59,7 +59,7 @@ const Resolve = styled.button` border: none; } - ${props => props.isReadOnly && inactiveButton} + ${props => props.isReadOnlyResolve && inactiveButton} ${override('Wax.CommentResolve')} `; @@ -76,7 +76,8 @@ const CommentBox = props => { className, commentId, commentData, - isReadOnly, + isReadOnlyResolve, + isReadOnlyPost, onClickBox, onClickPost, onClickResolve, @@ -100,9 +101,9 @@ const CommentBox = props => { {active && commentData.length > 0 && ( <Head> <Resolve - isReadOnly={isReadOnly} + isReadOnlyResolve={isReadOnlyResolve} onClick={e => { - if (!isReadOnly) return onClickResolve(e, commentId); + if (!isReadOnlyResolve) return onClickResolve(e, commentId); return false; }} > @@ -121,7 +122,7 @@ const CommentBox = props => { {active && ( <StyledReply isNewComment={commentData.length === 0} - isReadOnly={isReadOnly} + isReadOnlyPost={isReadOnlyPost} onClickPost={onClickPost} onTextAreaBlur={onTextAreaBlur} showTitle={showTitle} @@ -135,7 +136,8 @@ const CommentBox = props => { CommentBox.propTypes = { /** Whether this is the current active comment */ active: PropTypes.bool, - isReadOnly: PropTypes.bool.isRequired, + isReadOnlyResolve: PropTypes.bool.isRequired, + isReadOnlyPost: PropTypes.bool.isRequired, /** List of objects containing data for comment items */ commentData: PropTypes.arrayOf( PropTypes.shape({ diff --git a/wax-prosemirror-services/src/CommentsService/components/ui/comments/CommentBubbleComponent.js b/wax-prosemirror-services/src/CommentsService/components/ui/comments/CommentBubbleComponent.js index 3b54d591d134920d356166a3e8edeb7e5a2f904e..afea824461f2e03790ec00fa4fbce75d4345b29d 100644 --- a/wax-prosemirror-services/src/CommentsService/components/ui/comments/CommentBubbleComponent.js +++ b/wax-prosemirror-services/src/CommentsService/components/ui/comments/CommentBubbleComponent.js @@ -49,6 +49,8 @@ const CommentBubbleComponent = ({ setPosition, position, group }) => { }; const isCommentAllowed = () => { + if (activeViewId !== 'main') return false; + let allowed = true; state.doc.nodesBetween( state.selection.$from.pos, @@ -77,13 +79,12 @@ const CommentBubbleComponent = ({ setPosition, position, group }) => { return allowed; }; - const isEditable = main.props.editable(editable => { - return editable; - }); + // const isEditable = main.props.editable(editable => { + // return editable; + // }); return ( - isCommentAllowed() && - isEditable && ( + isCommentAllowed() && ( <CommentBubble onClick={event => { createComment(event); diff --git a/wax-prosemirror-services/src/CommentsService/components/ui/comments/CommentReply.js b/wax-prosemirror-services/src/CommentsService/components/ui/comments/CommentReply.js index 56d0a37f870f52c225645e182ede3e6f673a3eab..bb1945f4c43b21ec4fe20fb38e4a3311ecdec6c7 100644 --- a/wax-prosemirror-services/src/CommentsService/components/ui/comments/CommentReply.js +++ b/wax-prosemirror-services/src/CommentsService/components/ui/comments/CommentReply.js @@ -90,7 +90,7 @@ const CommentReply = props => { className, isNewComment, onClickPost, - isReadOnly, + isReadOnlyPost, onTextAreaBlur, showTitle, usersMentionList, @@ -194,7 +194,7 @@ const CommentReply = props => { <ActionWrapper> <ButtonGroup> <Button - disabled={commentValue.length === 0 || isReadOnly} + disabled={commentValue.length === 0 || isReadOnlyPost} onClick={handleSubmit} primary type="submit" @@ -218,7 +218,7 @@ const CommentReply = props => { CommentReply.propTypes = { isNewComment: PropTypes.bool.isRequired, onClickPost: PropTypes.func.isRequired, - isReadOnly: PropTypes.bool.isRequired, + isReadOnlyPost: PropTypes.bool.isRequired, onTextAreaBlur: PropTypes.func.isRequired, showTitle: PropTypes.bool.isRequired, }; diff --git a/wax-prosemirror-services/src/NoteService/Editor.js b/wax-prosemirror-services/src/NoteService/Editor.js index ac9916787f68e321a70441be64914e15ba8e133b..16f6a2524dbdf54b67018a708cdf88a17f5f3114 100644 --- a/wax-prosemirror-services/src/NoteService/Editor.js +++ b/wax-prosemirror-services/src/NoteService/Editor.js @@ -41,9 +41,13 @@ export default ({ node, view }) => { return editable; }); - // const filteredplugins = app.PmPlugins.getAll().filter( - // plugin => !plugin.key.includes('comment') && !plugin.key.includes('yjs'), - // ); + const filteredplugins = app.PmPlugins.getAll().filter( + plugin => + !plugin.key.includes('y-sync') && + !plugin.key.includes('y-undo') && + !plugin.key.includes('yjs') && + !plugin.key.includes('comment'), + ); const setEditorRef = useCallback(noteNode => { if (noteNode) { @@ -54,7 +58,7 @@ export default ({ node, view }) => { editable: () => isEditable, state: EditorState.create({ doc: node, - plugins: [keymap(createKeyBindings()), ...app.PmPlugins.getAll()], + plugins: [keymap(createKeyBindings()), ...filteredplugins], }), dispatchTransaction, disallowedTools: ['Tables', 'Images', 'Lists', 'CodeBlock'], diff --git a/wax-prosemirror-services/src/YjsService/YjsService.js b/wax-prosemirror-services/src/YjsService/YjsService.js index c9655c093086351a94014a09301ba78de928496f..d203214bd99d872f0ab2ae497da616b63a9b1c13 100644 --- a/wax-prosemirror-services/src/YjsService/YjsService.js +++ b/wax-prosemirror-services/src/YjsService/YjsService.js @@ -40,7 +40,6 @@ class YjsService extends Service { }); const type = ydoc.getXmlFragment(YjsType || 'prosemirror'); - console.log('dsf'); this.app.PmPlugins.add('ySyncPlugin', ySyncPlugin(type));