From 6abd1665f28dbbfe6bd63958e3e8653f2dd03c16 Mon Sep 17 00:00:00 2001
From: barlas <yannisbarlas@gmail.com>
Date: Wed, 28 Dec 2016 14:44:58 +0200
Subject: [PATCH] basic deletion track changes funcionality

---
 .../SimpleEditor/ContainerEditor.js           |  64 +++++++-
 .../elements/track_change/TrackChange.js      |   4 +
 .../track_change/TrackChangeComponent.js      |   6 +-
 .../track_change/TrackChangesProvider.js      | 149 ++++++++++++++++--
 .../elements/track_change/trackChange.scss    |  13 ++
 5 files changed, 215 insertions(+), 21 deletions(-)

diff --git a/app/components/SimpleEditor/ContainerEditor.js b/app/components/SimpleEditor/ContainerEditor.js
index 05d6892..8d5ccc2 100644
--- a/app/components/SimpleEditor/ContainerEditor.js
+++ b/app/components/SimpleEditor/ContainerEditor.js
@@ -1,6 +1,8 @@
 import { each, keys, includes } from 'lodash'
 import {
   ContainerEditor as SubstanceContainerEditor,
+  // deleteCharacter,
+  // deleteSelection,
   Surface,
   uuid
 } from 'substance'
@@ -53,6 +55,7 @@ class ContainerEditor extends SubstanceContainerEditor {
     //   // console.log(this.context.commandManager.getCommandStates().strong.mode)
     //   // console.log(this.context.commandManager.getCommandStates()['track-change'].mode)
     //   console.log('\n did update')
+    //   console.log(this.getCommandStates())
     //   console.log('change', change)
     //   console.log('info', info)
     //
@@ -70,25 +73,72 @@ class ContainerEditor extends SubstanceContainerEditor {
   }
 
   onTextInput (event) {
+    // console.log(this.context)
     event.preventDefault()
     event.stopPropagation()
 
     if (!event.data) return
     this._state.skipNextObservation = true
 
-    this.transaction(function (tx, args) {
-      if (this.domSelection) this.domSelection.clear()
-      args.text = event.data
-      return this.insertText(tx, args)
-    }.bind(this), { action: 'type' })
+    if (!this.props.trackChanges) {
+      this.transaction(function (tx, args) {
+        if (this.domSelection) this.domSelection.clear()
+        args.text = event.data
+        return this.insertText(tx, args)
+      }.bind(this), { action: 'type' })
+      return
+    }
+
+    const trackChangesProvider = this.context.trackChangesProvider
+    const status = 'add'
+    trackChangesProvider.handleTransaction(event, status)
+
+    // const isSelCollapsed = this.getSelection().isCollapsed()
+    // if (!isSelCollapsed) {
+    //   const trackChangesProvider = this.context.trackChangesProvider
+    //   const status = 'delete'
+    //   trackChangesProvider.handleTransaction(status)
+    // }
+    //
+    // this.transaction(function (tx, args) {
+    //   if (this.domSelection) this.domSelection.clear()
+    //   args.text = event.data
+    //   return this.insertText(tx, args)
+    // }.bind(this), { action: 'type' })
+    //
+    // // don't rewrite the above, call it with super
+    // if (this.props.trackChanges) {
+    //   const status = 'add'
+    //   trackChangesProvider.handleTransaction(status)
+    // }
+  }
+
+  _handleDeleteKey (event) {
+    event.preventDefault()
+    event.stopPropagation()
+    let direction = (event.keyCode === keys.BACKSPACE) ? 'left' : 'right'
 
-    // don't rewrite the above, call it with super
     if (this.props.trackChanges) {
       const trackChangesProvider = this.context.trackChangesProvider
-      trackChangesProvider.handleTransaction()
+      const status = 'delete'
+      trackChangesProvider.handleTransaction(event, status, direction)
+    } else {
+      this.transaction(function (tx, args) {
+        args.direction = direction
+        return this.delete(tx, args)
+      }.bind(this), { action: 'delete' })
     }
   }
 
+  // delete (tx, args) {
+  //   let sel = args.selection
+  //   if (!sel.isCollapsed()) {
+  //     return deleteSelection(tx, args)
+  //   } else if (sel.isPropertySelection() || sel.isNodeSelection()) {
+  //     return deleteCharacter(tx, args)
+  //   }
+  // }
+
   // create an empty paragraph with an empty node
   // then select it for cursor focus
   createText () {
diff --git a/app/components/SimpleEditor/elements/track_change/TrackChange.js b/app/components/SimpleEditor/elements/track_change/TrackChange.js
index 2de7707..d768fa7 100644
--- a/app/components/SimpleEditor/elements/track_change/TrackChange.js
+++ b/app/components/SimpleEditor/elements/track_change/TrackChange.js
@@ -2,6 +2,10 @@ import { PropertyAnnotation } from 'substance'
 
 class TrackChange extends PropertyAnnotation {}
 
+TrackChange.define({
+  status: { type: 'string' }
+})
+
 TrackChange.type = 'track-change'
 
 export default TrackChange
diff --git a/app/components/SimpleEditor/elements/track_change/TrackChangeComponent.js b/app/components/SimpleEditor/elements/track_change/TrackChangeComponent.js
index 0373ac2..fb744b2 100644
--- a/app/components/SimpleEditor/elements/track_change/TrackChangeComponent.js
+++ b/app/components/SimpleEditor/elements/track_change/TrackChangeComponent.js
@@ -3,14 +3,18 @@ import { AnnotationComponent } from 'substance'
 class TrackChangeComponent extends AnnotationComponent {
   render ($$) {
     const user = this.context.controller.props.user
+    const status = this.props.node.status
 
     var el = $$('span')
       .attr('data-id', this.props.node.id)
       .attr('data-user', user.username)
       .attr('data-role', user.teams[0].name)
       .addClass(this.getClassNames())
+      .append(this.props.children)
+
+    const className = 'sc-track-change-' + status
+    el.addClass(className)
 
-    el.append(this.props.children)
     return el
   }
 
diff --git a/app/components/SimpleEditor/elements/track_change/TrackChangesProvider.js b/app/components/SimpleEditor/elements/track_change/TrackChangesProvider.js
index 059e282..4325616 100644
--- a/app/components/SimpleEditor/elements/track_change/TrackChangesProvider.js
+++ b/app/components/SimpleEditor/elements/track_change/TrackChangesProvider.js
@@ -1,5 +1,5 @@
 import { keys, last } from 'lodash'
-import { createAnnotation } from 'substance'
+import { createAnnotation, expandAnnotation } from 'substance'
 
 class TrackChangesProvider {
   constructor (config) {
@@ -7,9 +7,43 @@ class TrackChangesProvider {
     this.config.documentSession.on('didUpdate', this.handleUndoRedo, this)
   }
 
-  handleTransaction () {
+  handleTransaction (event, status) {
+    if (status === 'add') {
+      const isSelCollapsed = this.isSelectionCollapsed()
+      if (!isSelCollapsed) {
+        const options = { 'deleteCollapsed': true }
+        this.createDeletionAnnotation(options)
+      }
+      this.insertText(event)
+      this.createAdditionAnnotation()
+      return
+    }
+
+    if (status === 'delete') {
+      this.createDeletionAnnotation()
+      return
+    }
+  }
+
+  insertText (event) {
+    const surface = this.getSurface()
+    surface.transaction(function (tx, args) {
+      if (surface.domSelection) surface.domSelection.clear()
+      args.text = event.data
+      return surface.insertText(tx, args)
+    }, { action: 'type' })
+  }
+
+  onDeleteAnnotation () {
+    const anno = this.getExistingAnnotation()
+    const isDelete = (anno.status === 'delete')
+    return isDelete
+  }
+
+  createAdditionAnnotation () {
     const mode = this.getMode()
-    if (mode) return
+    const onDelete = this.onDeleteAnnotation()
+    if (mode && !onDelete) return
 
     const surface = this.getSurface()
 
@@ -18,27 +52,109 @@ class TrackChangesProvider {
 
       const newNode = {
         selection: selection,
-        node: { 'type': 'track-change' }
+        node: {
+          'status': 'add',
+          'type': 'track-change'
+        }
       }
 
       createAnnotation(tx, newNode)
-      this.clearSelection(args)
-      // this.removeAnnotationFromHistory()
+      this.clearSelection(args, 'add')
     }
 
-    const info = {
-      track: true
+    const info = {}
+    surface.transaction(transformation, info)
+
+    surface.transaction((tx, args) => {
+      if (onDelete) {
+        // console.log(this.getAllExistingAnnotations())
+        const annos = this.getAllExistingAnnotations()
+        const deleteAnno = annos.find((obj) => {
+          // console.log(obj)
+          return obj.type === 'track-change' && obj.status === 'delete'
+        })
+        const addAnno = annos.find((obj) => {
+          // console.log(obj)
+          return obj.type === 'track-change' && obj.status === 'add'
+        })
+        // console.log(deleteAnno)
+        deleteAnno.updateRange(tx, {
+          start: {
+            startPath: deleteAnno.startPath,
+            offset: deleteAnno.startOffset
+          },
+          end: {
+            offset: addAnno.startOffset
+          }
+        })
+        // deleteAnno.endOffset = addAnno.startOffset
+        // console.log(deleteAnno)
+      }
+    })
+  }
+
+  getExistingAnnotation () {
+    const documentSession = this.getDocumentSession()
+    const selectionState = documentSession.getSelectionState()
+    const annotations = selectionState.getAnnotationsForType('track-change')
+    return annotations[0]
+  }
+
+  getAllExistingAnnotations () {
+    const documentSession = this.getDocumentSession()
+    const selectionState = documentSession.getSelectionState()
+    const annotations = selectionState.getAnnotationsForType('track-change')
+    return annotations
+  }
+
+  createDeletionAnnotation (options) {
+    let deleteCollapsed
+    if (options && options.deleteCollapsed) deleteCollapsed = true
+
+    const surface = this.getSurface()
+    const mode = this.getMode()
+
+    const transformation = (tx, args) => {
+      let selection = args.selection
+      if (!deleteCollapsed) selection = this.createSelection(args)
+
+      if (mode === 'delete') {
+        args.anno = this.getExistingAnnotation()
+        expandAnnotation(tx, args)
+      } else {
+        const newNode = {
+          selection: selection,
+          node: {
+            'status': 'delete',
+            'type': 'track-change'
+          }
+        }
+
+        createAnnotation(tx, newNode)
+      }
+
+      args.deleteCollapsed = deleteCollapsed
+      this.clearSelection(args, 'delete')
     }
 
+    const info = {}
     surface.transaction(transformation, info)
   }
 
+  isSelectionCollapsed () {
+    const surface = this.getSurface()
+    const selection = surface.getSelection()
+    const isCollapsed = selection.isCollapsed()
+    return isCollapsed
+  }
+
+  // TODO -- shouldn't run both all the time
   handleUndoRedo (update, info) {
     if (!info.replay) return
-    console.log(update)
-    console.log(info)
+    // console.log(update)
+    // console.log(info)
     this.handleUndo(update)
-    this.handleRedo(update)
+    // this.handleRedo(update)
   }
 
   handleUndo (update) {
@@ -78,9 +194,16 @@ class TrackChangesProvider {
   //   }) // ensure that text insertion has finished
   // }
 
-  clearSelection (args) {
+  clearSelection (args, status) {
     const selection = args.selection
-    selection.startOffset = selection.endOffset
+    if (
+      status === 'add' ||
+      (status === 'delete' && args.deleteCollapsed)
+    ) {
+      selection.startOffset = selection.endOffset
+    } else if (status === 'delete') {
+      selection.endOffset = selection.startOffset
+    }
     return selection
   }
 
diff --git a/app/components/SimpleEditor/elements/track_change/trackChange.scss b/app/components/SimpleEditor/elements/track_change/trackChange.scss
index d995404..bd8eb65 100644
--- a/app/components/SimpleEditor/elements/track_change/trackChange.scss
+++ b/app/components/SimpleEditor/elements/track_change/trackChange.scss
@@ -1,3 +1,16 @@
 .sc-track-change {
+  // background-color: #f7f70c;
+}
+
+.sc-track-change-add {
   background-color: #f7f70c;
 }
+
+.sc-track-change-delete {
+  background-color: red;
+  text-decoration: line-through;
+
+  .sc-track-change-add {
+    text-decoration: none !important;
+  }
+}
-- 
GitLab