Skip to content
Snippets Groups Projects
CommentState.js 3.12 KiB
Newer Older
chris's avatar
chris committed
/* eslint-disable no-param-reassign */
import { v4 as uuidv4 } from 'uuid';
import { Decoration, DecorationSet } from 'prosemirror-view';
import CommentDecoration from './CommentDecoration';
chris's avatar
chris committed
import { CommentDecorationPluginKey } from './CommentDecorationPlugin';
chris's avatar
chris committed

const randomId = () => {
  return uuidv4();
};

export default class CommentState {
  constructor(options) {
    this.decorations = DecorationSet.empty;
    this.options = options;
  }

  addComment(action) {
    const { map } = this.options;
    const { from, to, data } = action;
    const id = randomId();
    map.set(id, { id, from, to, data });
  }

  updateComment(action) {
    const { map } = this.options;
    const annotationToUpdate = map.get(action.id);
    if (annotationToUpdate) {
      annotationToUpdate.data = action.data;
    }
  }

  deleteComment(id) {
    const { map } = this.options;
    map.delete(id);
  }

  commentsAt(position, to) {
    return this.decorations.find(position, to || position).map(decoration => {
      return new CommentDecoration(decoration);
    });
  }

  allCommentsList() {
    const { map } = this.options;
    return Array.from(map, ([key, value]) => {
      // eslint-disable-next-line prefer-object-spread
      return Object.assign(Object.assign({}, value), {
        id: key,
      });
    }).filter(value => {
      return 'from' in value && 'to' in value;
    });
  }

  createDecorations(state) {
    const { map } = this.options;

    const decorations = [];
    const termList = Array.from(map, ([key, value]) => {
      // eslint-disable-next-line prefer-object-spread
      return Object.assign(Object.assign({}, value), {
        id: key,
      });
    }).filter(value => {
      return 'from' in value && 'to' in value;
    });

    termList.forEach(annotation => {
      const { from, to } = annotation;

      decorations.push(
        Decoration.inline(
          from,
          to,
          {
            class: 'comment',
            'data-id': annotation.id,
          },
          {
            id: annotation.id,
            data: annotation,
            inclusiveEnd: true,
          },
        ),
      );
    });
    this.decorations = DecorationSet.create(state.doc, decorations);
  }

  apply(transaction, state) {
chris's avatar
chris committed
    const action = transaction.getMeta(CommentDecorationPluginKey);
chris's avatar
chris committed
    if (action && action.type) {
      if (action.type === 'addComment') {
        this.addComment(action);
      }
      if (action.type === 'updateComment') {
        this.updateComment(action);
      }
      if (action.type === 'deleteComment') {
        this.deleteComment(action.id);
      }
      this.createDecorations(state);
      return this;
    }

    // manually map annotation positions
    this.options.map.forEach((annotation, _) => {
      if ('from' in annotation && 'to' in annotation) {
        annotation.from = transaction.mapping.map(annotation.from);
        annotation.to = transaction.mapping.map(annotation.to);
      }
    });
    this.createDecorations(state);
    return this;
  }
}

//  let res =
//    annotation.to === state.selection.to &&
//    state.selection.from === state.selection.to;
//  console.log(res, transaction.docChanged);