From 7e6e12cc57c4d56a0043de6443e4b9772442543c Mon Sep 17 00:00:00 2001
From: john <johnbarlas39@gmail.com>
Date: Fri, 6 Jan 2017 01:26:13 +0200
Subject: [PATCH] refactor and bug fixes on track change addition annotation
 handlers

---
 .../SimpleEditor/SimpleEditorWrapper.jsx      |   2 -
 .../track_change/TrackChangeComponent.js      |  49 ++-
 .../track_change/TrackChangesProvider.js      | 338 ++++++++++--------
 3 files changed, 211 insertions(+), 178 deletions(-)

diff --git a/app/components/SimpleEditor/SimpleEditorWrapper.jsx b/app/components/SimpleEditor/SimpleEditorWrapper.jsx
index 9ca0910..7d4bb7f 100644
--- a/app/components/SimpleEditor/SimpleEditorWrapper.jsx
+++ b/app/components/SimpleEditor/SimpleEditorWrapper.jsx
@@ -142,8 +142,6 @@ export class SimpleEditorWrapper extends React.Component {
 
   render () {
     const { book, fragment, history, user } = this.props
-    // user.roles = user.getRoles()
-    console.log(fragment, user)
 
     // TODO -- merge update and updateComments
     return (
diff --git a/app/components/SimpleEditor/elements/track_change/TrackChangeComponent.js b/app/components/SimpleEditor/elements/track_change/TrackChangeComponent.js
index 1617e9d..c602a5b 100644
--- a/app/components/SimpleEditor/elements/track_change/TrackChangeComponent.js
+++ b/app/components/SimpleEditor/elements/track_change/TrackChangeComponent.js
@@ -2,47 +2,40 @@ import { AnnotationComponent, createAnnotation, deleteNode } from 'substance'
 
 class TrackChangeComponent extends AnnotationComponent {
   render ($$) {
-    // const user = this.context.controller.props.user
-    const status = this.props.node.status
+    const { status } = this.props.node
     const trackChangesView = this.toggleTrackChangeView()
-    let hideDeletes = ''
-    let showAdditions = ''
-
-    if (trackChangesView === false && status === 'delete') {
-      hideDeletes = 'sc-track-delete-hide'
-    }
-
-    if (trackChangesView === false && status === 'add') {
-      showAdditions = 'sc-track-add-show'
-    }
 
     const accept = $$('a')
       .addClass('sc-track-item')
       .on('click', this.acceptTrackChange)
       .append('accept')
 
-    const reject = $$('a').addClass('sc-track-item')
-                    .on('click', this.rejectTrackChange)
-                   .append('reject')
-    const seperator = $$('span').addClass('sc-track-separator')
-                      .append(' / ')
-    const container = $$('span').addClass('sc-accept-reject-container')
-                      .append(accept)
-                      .append(seperator)
-                      .append(reject)
+    const reject = $$('a')
+      .addClass('sc-track-item')
+      .on('click', this.rejectTrackChange)
+      .append('reject')
+
+    const separator = $$('span')
+      .addClass('sc-track-separator')
+      .append(' / ')
+
+    const container = $$('span')
+      .addClass('sc-accept-reject-container')
+      .append(accept)
+      .append(separator)
+      .append(reject)
 
     let el = $$('span')
-      // .attr('data-id', this.props.node.id)
-      // .attr('data-user', user.username)
-      // .attr('data-role', user.teams[0].name)
       .addClass(this.getClassNames())
-      .addClass(hideDeletes)
-      .addClass(showAdditions)
       .append(this.props.children)
       .append(container)
 
-    const className = 'sc-track-change-' + status
-    el.addClass(className)
+    el.addClass('sc-track-change-' + status)
+
+    if (trackChangesView === false) {
+      if (status === 'delete') el.addClass('sc-track-delete-hide')
+      if (status === 'add') el.addClass('sc-track-add-show')
+    }
 
     return el
   }
diff --git a/app/components/SimpleEditor/elements/track_change/TrackChangesProvider.js b/app/components/SimpleEditor/elements/track_change/TrackChangesProvider.js
index 94ff366..ff8ced4 100644
--- a/app/components/SimpleEditor/elements/track_change/TrackChangesProvider.js
+++ b/app/components/SimpleEditor/elements/track_change/TrackChangesProvider.js
@@ -3,9 +3,9 @@ import {
   filter,
   find,
   keys,
-  last,
-  max,
-  minBy
+  last
+  // max,
+  // minBy
 } from 'lodash'
 
 import {
@@ -29,155 +29,194 @@ class TrackChangesProvider {
   */
 
   handleTransaction (options) {
-    this.handleAdd(options)
-    this.handleDelete(options)
+    options.selection = this.getSelection()
+    this.chooseHanlder(options)
   }
 
-  handleAdd (options) {
-    const { event, status } = options
-    if (status !== 'add') return
-    // console.log('adding')
+  chooseHanlder (options) {
+    if (options.status === 'add') return this.handleAdd(options)
+    if (options.status === 'delete') return this.handleDelete(options)
+  }
 
+  handleAdd (options) {
     const isSelectionCollapsed = this.isSelectionCollapsed()
-    let selection
 
-    if (isSelectionCollapsed) {
-      // this.insertText(event)
+    if (isSelectionCollapsed) return this.handleAddCollapsed(options)
+    if (!isSelectionCollapsed) return this.handleAddNonCollapsed(options)
+  }
 
-      const notOnTrack = this.isNotOnTrackAnnotation()
+  handleAddCollapsed (options) {
+    const { event } = options
 
-      if (notOnTrack) {
-        this.insertText(event)
-        selection = this.setSelectionPlusOne('left')
-        this.createAddAnnotation(selection)
-        this.moveCursorTo('end')
-        return
-      } else {
-        const isOnAdd = this.isOnAnnotation('add')
+    const notOnTrack = this.isNotOnTrackAnnotation()
+    const isOnAdd = this.isOnAnnotation('add')
+    const isOnDelete = this.isOnAnnotation('delete')
 
-        if (isOnAdd) {
-          const annotation = this.getAnnotationByStatus('add')
-          const isOnLeftEdge = this.isOnLeftEdge(annotation)
+    if (notOnTrack) return this.insertCharacterWithAddAnnotation(options)
 
-          this.insertText(event)
-          if (isOnLeftEdge) {
-            selection = this.setSelectionPlusOne('left')
-            this.expandTrackAnnotation(selection, annotation)
-            this.moveCursorTo('end')
-            return
-          }
-          return
-        }
+    if (isOnAdd) {
+      const annotation = this.getAnnotationByStatus('add')
+      const isOnLeftEdge = this.isOnLeftEdge(annotation)
 
-        const isOnDelete = this.isOnAnnotation('delete')
+      this.insertText(event)
+      if (isOnLeftEdge) this.expandAnnotationToTheLeft(annotation)
+      return
+    }
 
-        if (isOnDelete) {
-          console.log('on delete')
-          const annotation = this.getAnnotationByStatus('delete')
-          const withinAnnotation = this.isSelectionContainedWithin(annotation, true)
-
-          if (withinAnnotation) {
-            // console.log('in')
-            this.moveCursorTo(annotation.endOffset)
-            this.insertText(event)
-            selection = this.setSelectionPlusOne('left')
-            this.createAddAnnotation(selection)
-            this.separateAnnotations()
-            this.moveCursorTo('end')
-            return
-          }
+    if (isOnDelete) {
+      const annotation = this.getAnnotationByStatus('delete')
 
-          console.log('out')
-          const isOnLeftEdge = this.isOnLeftEdge(annotation)
-          if (isOnLeftEdge) {
-            // console.log('left')
-            this.insertText(event)
-            selection = this.setSelectionPlusOne('left')
-            this.createAddAnnotation(selection)
-            this.moveCursorTo('end')
-            return
-          }
+      const isOnLeftEdge = this.isOnLeftEdge(annotation)
+      const isOnRightEdge = this.isOnRightEdge(annotation)
+      const withinAnnotation = this.isSelectionContainedWithin(annotation, true)
 
-          const isOnRightEdge = this.isOnRightEdge(annotation)
-          if (isOnRightEdge) {
-            // console.log('right')
-            this.insertText(event)
-            selection = this.setSelectionPlusOne('left')
-            this.createAddAnnotation(selection)
-            this.separateAnnotations()
-            this.moveCursorTo('end')
-            return
-          }
-        }
+      if (withinAnnotation) {
+        this.moveCursorTo(annotation.endOffset)
+        this.insertCharacterWithoutExpandingAnnotation(annotation, options)
+        options.selection = this.getSelection()
+        return this.handleAdd(options)
       }
-    } else {
-      selection = this.getSelection()
 
-      const notOnTrack = this.isNotOnTrackAnnotation()
+      if (isOnLeftEdge) return this.insertCharacterWithAddAnnotation(options)
 
-      if (notOnTrack) {
-        this.createDeleteAnnotation(selection)
-        this.moveCursorTo('end')
-
-        this.insertText(event)
-        selection = this.setSelectionPlusOne('left')
-        this.createAddAnnotation(selection)
-        this.separateAnnotations()
-        this.moveCursorTo('end')
-        return
+      if (isOnRightEdge) {
+        this.insertCharacterWithoutExpandingAnnotation(annotation, options)
+        return this.handleAdd(options)
       }
+    }
+  }
 
-      const isOnAdd = this.isOnAnnotation('add')
+  handleAddNonCollapsed (options) {
+    // const { event } = options
+    let { selection } = options
 
-      if (isOnAdd) {
-        const annotation = this.getAnnotationByStatus('add')
-        const withinAnnotation = this.isSelectionContainedWithin(annotation)
+    const notOnTrack = this.isNotOnTrackAnnotation()
+    // const isOnAdd = this.isOnAnnotation('add')
+    // const isOnDelete = this.isOnAnnotation('delete')
 
-        if (withinAnnotation) return this.insertText(event)
+    if (notOnTrack) return this.deleteSelectedAndCreateAddition(options)
 
-        selection = this.getSelection()
-        this.createDeleteAnnotation(selection)
-        const allAdditions = this.getAllAnnotationsByStatus('add')
-        const firstAddition = minBy(allAdditions, 'startOffset')
-        this.deleteAllOwnAdditions()
-
-        const moveTo = max([firstAddition.startOffset, selection.startOffset])
-        this.moveCursorTo(moveTo)
-        this.insertText(event)
-        selection = this.setSelectionPlusOne('left')
-        this.createAddAnnotation(selection)
-        this.moveCursorTo('end')
-        return
-      }
+    const shortenBy = this.deleteAllOwnAdditions(selection)
+    options.selection = this.updateSelection(selection, selection.startOffset, selection.endOffset - shortenBy)
+    this.deleteSelectedAndCreateAddition(options)
 
-      const isOnDelete = this.isOnAnnotation('delete')
+    // if (isOnAdd) {
+    //   const annotation = this.getAnnotationByStatus('add')
+    //   const withinAnnotation = this.isSelectionContainedWithin(annotation)
+    //
+    //   if (withinAnnotation) return this.insertText(event)
+    //
+    //   selection = this.getSelection()
+    //   this.createDeleteAnnotation(selection)
+    //   const allAdditions = this.getAllAnnotationsByStatus('add')
+    //   const firstAddition = minBy(allAdditions, 'startOffset')
+    //   this.deleteAllOwnAdditions()
+    //
+    //   const moveTo = max([firstAddition.startOffset, selection.startOffset])
+    //   this.moveCursorTo(moveTo)
+    //   this.insertText(event)
+    //   selection = this.setSelectionPlusOne('left')
+    //   this.createAddAnnotation(selection)
+    //   this.moveCursorTo('end')
+    //   return
+    // }
+    //
+    // if (isOnDelete) {
+    //   const annotation = this.getAnnotationByStatus('delete')
+    //   const withinAnnotation = this.isSelectionContainedWithin(annotation)
+    //
+    //   if (withinAnnotation) {
+    //     this.moveCursorTo(annotation.endOffset)
+    //     // options.selection =
+    //     this.insertText(event)
+    //     selection = this.setSelectionPlusOne('left')
+    //     this.createAddAnnotation(selection)
+    //     this.separateAnnotations()
+    //     this.moveCursorTo('end')
+    //     return
+    //   }
+    //
+    //   selection = this.getSelection()
+    //   this.expandTrackAnnotation(selection, annotation)
+    //
+    //   this.moveCursorTo(annotation.endOffset)
+    //   this.insertText(event)
+    //   selection = this.setSelectionPlusOne('left')
+    //   this.createAddAnnotation(selection)
+    //   this.separateAnnotations()
+    //   this.moveCursorTo('end')
+    //   return
+    // }
+  }
 
-      if (isOnDelete) {
-        const annotation = this.getAnnotationByStatus('delete')
-        const withinAnnotation = this.isSelectionContainedWithin(annotation)
+  deleteSelectedAndCreateAddition (options) {
+    let { selection } = options
 
-        if (withinAnnotation) {
-          this.moveCursorTo(annotation.endOffset)
-          this.insertText(event)
-          selection = this.setSelectionPlusOne('left')
-          this.createAddAnnotation(selection)
-          this.separateAnnotations()
-          this.moveCursorTo('end')
-          return
-        }
+    this.createDeleteAnnotation(selection)
+    this.moveCursorTo('end', selection)
 
-        selection = this.getSelection()
-        this.expandTrackAnnotation(selection, annotation)
+    const deleteAnnotation = this.getAnnotationByStatus('delete')
+    this.insertCharacterWithoutExpandingAnnotation(deleteAnnotation, options)
+    this.createAdditionAnnotationOnLastChar()
+  }
 
-        this.moveCursorTo(annotation.endOffset)
-        this.insertText(event)
-        selection = this.setSelectionPlusOne('left')
-        this.createAddAnnotation(selection)
-        this.separateAnnotations()
-        this.moveCursorTo('end')
-        return
+  // Inserts a character and then marks it as an add annotation
+  // If an add annotation existed on the left side of the new character already
+  // substance will expand this annotation on insertion
+  insertCharacterWithAddAnnotation (options) {
+    const { event } = options
+    this.insertText(event)
+    // TODO -- watch it with additions by other users
+    this.createAdditionAnnotationOnLastChar()
+  }
+
+  createAdditionAnnotationOnLastChar () {
+    const selection = this.setSelectionPlusOne('left')
+    this.createAddAnnotation(selection)
+    this.moveCursorTo('end')
+  }
+
+  expandAnnotationToTheLeft (annotation) {
+    const selection = this.setSelectionPlusOne('left')
+    this.expandTrackAnnotation(selection, annotation)
+    this.moveCursorTo('end')
+  }
+
+  insertCharacterWithoutExpandingAnnotation (annotation, options) {
+    const { event } = options
+    let selection = options
+
+    this.insertText(event)
+    selection = this.setSelectionPlusOne('left')
+    this.truncateTrackAnnotation(selection, annotation)
+    this.moveCursorTo('end')
+
+    options.event = null
+  }
+
+  deleteAllOwnAdditions (selection) {
+    // TODO -- for same user
+    const additions = this.getAllAnnotationsByStatus('add')
+    const originalSelection = selection || this.getSelection()
+
+    let shortenBy = 0
+
+    each(additions, (annotation) => {
+      const selection = annotation.getSelection()
+
+      // make sure only part of annotation that is selected is deleted
+      if (annotation.startOffset < originalSelection.startOffset) {
+        selection.startOffset = originalSelection.startOffset
       }
-    }
+      if (annotation.endOffset > originalSelection.endOffset) {
+        selection.endOffset = originalSelection.endOffset
+      }
+
+      shortenBy += (selection.endOffset - selection.startOffset)
+      this.deleteSelection(selection)
+    })
+
+    return shortenBy    // return how much shorter the selection should now be
   }
 
   handleDelete (options) {
@@ -185,6 +224,8 @@ class TrackChangesProvider {
     if (status !== 'delete') return
     // console.log('deleting')
 
+    // console.log(this.getSelection())
+
     const isSelectionCollapsed = this.isSelectionCollapsed()
     let selection
 
@@ -264,6 +305,7 @@ class TrackChangesProvider {
       const onAdd = this.isOnAnnotation('add')
 
       if (onAdd) {
+        // console.log('on add')
         const annotation = this.getAnnotationByStatus('add')
         const withinAnnotation = this.isSelectionContainedWithin(annotation)
 
@@ -363,6 +405,19 @@ class TrackChangesProvider {
     surface.transaction(transformation, info)
   }
 
+  truncateTrackAnnotation (selection, annotation) {
+    const surface = this.getSurface()
+    const info = this.getInfo()
+
+    const transformation = (tx, args) => {
+      args.anno = annotation
+      args.selection = selection
+      truncateAnnotation(tx, args)
+    }
+
+    surface.transaction(transformation, info)
+  }
+
   separateAnnotations () {
     const surface = this.getSurface()
     const info = this.getInfo()
@@ -386,7 +441,9 @@ class TrackChangesProvider {
   }
 
   insertText (event) {
+    if (!event) return
     const surface = this.getSurface()
+
     surface.transaction(function (tx, args) {
       if (surface.domSelection) surface.domSelection.clear()
       args.text = event.data || ' '    // if no data, it's a space key
@@ -418,27 +475,6 @@ class TrackChangesProvider {
     surface.transaction(transformation, info)
   }
 
-  // TODO -- also needs multiple additions
-  deleteAllOwnAdditions () {
-    // TODO -- for same user
-    const additions = this.getAllAnnotationsByStatus('add')
-    const originalSelection = this.getSelection()
-
-    each(additions, (annotation) => {
-      const selection = annotation.getSelection()
-
-      // make sure only part of annotation that is selected is deleted
-      if (annotation.startOffset < originalSelection.startOffset) {
-        selection.startOffset = originalSelection.startOffset
-      }
-      if (annotation.endOffset > originalSelection.endOffset) {
-        selection.endOffset = originalSelection.endOffset
-      }
-
-      this.deleteSelection(selection)
-    })
-  }
-
   /*
 
     ANNOTATION HELPERS
@@ -573,6 +609,12 @@ class TrackChangesProvider {
   // is part of the selection outside the annotation
   // move cursor to
 
+  updateSelection (selection, startOffset, endOffset) {
+    selection.startOffset = startOffset
+    selection.endOffset = endOffset
+    return selection
+  }
+
   setSelectionPlusOne (direction) {
     const selection = this.getSelection()
 
@@ -582,8 +624,8 @@ class TrackChangesProvider {
     return selection
   }
 
-  moveCursorTo (point) {
-    const selection = this.getSelection()
+  moveCursorTo (point, sel) {
+    const selection = sel || this.getSelection()
     const surface = this.getSurface()
 
     // TODO -- use substance's selection.collapse(direction)
-- 
GitLab