Skip to content
Snippets Groups Projects
CommentBubbleComponent.js 3.6 KiB
Newer Older
chris's avatar
chris committed
/* eslint react/prop-types: 0 */
import React, { useLayoutEffect, useContext } from 'react';
import { WaxContext } from 'wax-prosemirror-core';
chris's avatar
chris committed
import {
  ySyncPluginKey,
  relativePositionToAbsolutePosition,
  absolutePositionToRelativePosition,
} from 'y-prosemirror';
chris's avatar
chris committed

chris's avatar
chris committed
import CommentBubble from './CommentBubble';
import { CommentDecorationPluginKey } from '../../../plugins/CommentDecorationPlugin';
chris's avatar
chris committed

chris's avatar
chris committed
const CommentBubbleComponent = ({ setPosition, position, group }) => {
chris's avatar
chris committed
  const context = useContext(WaxContext);
  const {
    activeView,
    activeViewId,
chris's avatar
chris committed
    options: { comments },
chris's avatar
chris committed
  } = context;
  const { state, dispatch } = activeView;
chris's avatar
chris committed

  useLayoutEffect(() => {
    const WaxSurface = activeView.dom.getBoundingClientRect();
    const { selection } = activeView.state;
    const { from, to } = selection;
    const start = activeView.coordsAtPos(from);
    const end = activeView.coordsAtPos(to);
    const difference = end.top - start.top;
chris's avatar
chris committed
    const left = WaxSurface.width - 20;
chris's avatar
chris committed
    const top = end.top - WaxSurface.top - difference / 2 - 5;
    setPosition({ ...position, left, top });
  }, [position.left]);
  const createComment = event => {
chris's avatar
chris committed
    event.preventDefault();
chris's avatar
chris committed
    const { selection } = state;
chris's avatar
chris committed

chris's avatar
chris committed
    // if (context.app.config.get('config.YjsService')) {
    //   return createYjsComments(selection);
    // }

    const fromPos = state.tr.mapping.map(selection.from);
    const toPos = state.tr.mapping.map(selection.to);
chris's avatar
chris committed

chris's avatar
chris committed
    dispatch(
      state.tr.setMeta(CommentDecorationPluginKey, {
        type: 'addComment',
chris's avatar
chris committed
        from: fromPos,
        to: toPos,
chris's avatar
chris committed
        data: {
          type: 'comment',
chris's avatar
chris committed
          yjsFrom: fromPos,
          yjsTo: toPos,
          pmFrom: fromPos,
          pmTo: toPos,
chris's avatar
chris committed
          conversation: [],
          title: '',
          group,
          viewId: activeViewId,
        },
      }),
    );
    dispatch(state.tr);
  };

chris's avatar
chris committed
  const createYjsComments = selection => {
    const ystate = ySyncPluginKey.getState(state);
    const { doc, type, binding } = ystate;
    const from = absolutePositionToRelativePosition(
      selection.from,
      type,
      binding.mapping,
    );
    const to = absolutePositionToRelativePosition(
      selection.to,
      type,
      binding.mapping,
    );

    dispatch(
      state.tr.setMeta(CommentDecorationPluginKey, {
        type: 'addComment',
        from: relativePositionToAbsolutePosition(
          doc,
          type,
          from,
          binding.mapping,
        ),
        to: relativePositionToAbsolutePosition(doc, type, to, binding.mapping),
        data: {
          yjsFrom: selection.from,
          yjsTo: selection.to,
          pmFrom: selection.from,
          pmTo: selection.to,
          type: 'comment',
          conversation: [],
          title: '',
          group,
          viewId: activeViewId,
        },
      }),
    );

    dispatch(state.tr);
  };

chris's avatar
chris committed
  const isCommentAllowed = () => {
    let allowed = true;
    state.doc.nodesBetween(
      state.selection.$from.pos,
      state.selection.$to.pos,
chris's avatar
chris committed
      node => {
chris's avatar
chris committed
        if (
          node.type.name === 'math_display' ||
chris's avatar
chris committed
          node.type.name === 'math_inline' ||
          node.type.name === 'image'
chris's avatar
chris committed
        ) {
          allowed = false;
        }
      },
    );
chris's avatar
chris committed

      comments.find(
        comm =>
chris's avatar
chris committed
          comm.data.pmFrom === state.selection.from &&
          comm.data.pmTo === state.selection.to,
      allowed = false;
chris's avatar
chris committed
    return allowed;
chris's avatar
chris committed
  };

  return (
chris's avatar
chris committed
    isCommentAllowed() && (
chris's avatar
chris committed
      <CommentBubble
        onClick={event => {
chris's avatar
chris committed
          createComment(event);
        }}
chris's avatar
chris committed
      />
chris's avatar
chris committed
    )
chris's avatar
chris committed

export default CommentBubbleComponent;