From ec4b95687cd9e3730fad2bd3fae0d6d7bff798f8 Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Wed, 3 Feb 2021 11:51:15 +0100
Subject: [PATCH] Implement undo/redo for whiteboard #195

---
 www/whiteboard/app-whiteboard.less |  2 +-
 www/whiteboard/inner.js            | 40 ++++++++++++++++++++++++++++++
 2 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/www/whiteboard/app-whiteboard.less b/www/whiteboard/app-whiteboard.less
index 972e4fee2..bfc9823a1 100644
--- a/www/whiteboard/app-whiteboard.less
+++ b/www/whiteboard/app-whiteboard.less
@@ -87,7 +87,7 @@
         #cp-app-whiteboard-delete {
             min-width: 40px;
         }
-        .cp-whiteboard-type {
+        .cp-whiteboard-type, .cp-whiteboard-history {
             button {
                 min-width: 40px;
                 text-align: center;
diff --git a/www/whiteboard/inner.js b/www/whiteboard/inner.js
index e0492309f..79862ffef 100644
--- a/www/whiteboard/inner.js
+++ b/www/whiteboard/inner.js
@@ -53,6 +53,9 @@ define([
         var $type = $('.cp-whiteboard-type');
         var $brush = $('.cp-whiteboard-type .brush');
         var $move = $('.cp-whiteboard-type .move');
+        var $history = $('.cp-whiteboard-history');
+        var $undo = $('.cp-whiteboard-history .undo');
+        var $redo = $('.cp-whiteboard-history .redo');
         var $deleteButton = $('#cp-app-whiteboard-delete');
 
         var metadataMgr = framework._.cpNfInner.metadataMgr;
@@ -137,6 +140,27 @@ define([
             $deleteButton.prop('disabled', '');
         });
 
+        $undo.click(function () {
+            if (typeof(APP.canvas.undo) !== "function") { return; }
+            APP.canvas.undo();
+            APP.onLocal();
+        });
+        $redo.click(function () {
+            if (typeof(APP.canvas.undo) !== "function") { return; }
+            APP.canvas.redo();
+            APP.onLocal();
+        });
+        $('body').on('keydown', function (e) {
+            if (e.which === 90 && e.ctrlKey) {
+                $undo.click();
+                return;
+            }
+            if (e.which === 89 && e.ctrlKey) {
+                $redo.click();
+                return;
+            }
+        });
+
         var deleteSelection = function () {
             if (APP.draw) { return; }
             if (canvas.getActiveObject()) {
@@ -436,7 +460,15 @@ define([
             };
         });
 
+        var cleanHistory = function () {
+            if (Array.isArray(canvas.historyUndo)) {
+                canvas.historyUndo = canvas.historyUndo.slice(-100);
+                canvas.historyRedo = canvas.historyRedo.slice(-100);
+            }
+        };
+
         framework.onContentUpdate(function (newContent, waitFor) {
+            cleanHistory();
             var content = newContent.content;
             canvas.loadFromJSON(content, waitFor(function () {
                 canvas.renderAll();
@@ -445,6 +477,7 @@ define([
         });
 
         framework.setContentGetter(function () {
+            cleanHistory();
             var content = canvas.toDatalessJSON();
             return {
                 content: content
@@ -475,6 +508,8 @@ define([
     };
 
 
+    Messages.undo = "Undo"; // XXX
+    Messages.redo = "Redo"; // XXX
     var initialContent = function () {
         return [
             h('div#cp-toolbar.cp-toolbar-container'),
@@ -494,6 +529,10 @@ define([
                     h('button.btn.brush.fa.fa-paint-brush.btn-primary', {title: Messages.canvas_brush}),
                     h('button.btn.move.fa.fa-arrows', {title: Messages.canvas_select}),
                 ]),
+                h('div.cp-whiteboard-history', [
+                    h('button.btn.undo.fa.fa-undo', {title: Messages.undo}),
+                    h('button.btn.redo.fa.fa-repeat', {title: Messages.redo}),
+                ]),
                 h('button.btn.fa.fa-trash#cp-app-whiteboard-delete', {
                     disabled: 'disabled',
                     title: Messages.canvas_delete
@@ -560,6 +599,7 @@ define([
                 $('body').append($div.html());
             }));
         }).nThen(function (waitFor) {
+            require(['/lib/fabric-history.min.js'], waitFor());
 
             // Framework initialization
             Framework.create({