From 3b61a7dd91219cf87b2361ac57d0739090a9fa5d Mon Sep 17 00:00:00 2001
From: Andrey Antukh <niwi@niwi.nz>
Date: Sat, 31 Dec 2022 10:48:27 +0100
Subject: [PATCH 1/4] :bug: Fix incorrect arguments to process-changes

---
 .../app/main/data/workspace/persistence.cljs  | 35 ++++++++++---------
 1 file changed, 19 insertions(+), 16 deletions(-)

diff --git a/frontend/src/app/main/data/workspace/persistence.cljs b/frontend/src/app/main/data/workspace/persistence.cljs
index 29df1fd43..2d8d1d8e9 100644
--- a/frontend/src/app/main/data/workspace/persistence.cljs
+++ b/frontend/src/app/main/data/workspace/persistence.cljs
@@ -216,29 +216,32 @@
   [file-id {:keys [revn changes] :as params}]
   (us/verify! ::us/uuid file-id)
   (us/verify! ::shapes-changes-persisted params)
-  (ptk/reify ::changes-persisted
+  (ptk/reify ::shapes-changes-persisted
     ptk/UpdateEvent
     (update [_ state]
       ;; NOTE: we don't set the file features context here because
       ;; there are no useful context for code that need to be executed
       ;; on the frontend side
-      (let [changes (group-by :page-id changes)]
-        (if (= file-id (:current-file-id state))
-          (-> state
-              (update-in [:workspace-file :revn] max revn)
-              (update :workspace-data (fn [file]
-                                        (loop [fdata file
-                                               entries (seq changes)]
-                                          (if-let [[page-id changes] (first entries)]
-                                            (recur (-> fdata
-                                                       (cp/process-changes changes)
-                                                       (ctst/update-object-indices page-id))
-                                                   (rest entries))
-                                            fdata)))))
+
+      (if-let [current-file-id (:current-file-id state)]
+        (if (= file-id current-file-id)
+          (let [changes (group-by :page-id changes)]
+            (-> state
+                (update-in [:workspace-file :revn] max revn)
+                (update :workspace-data (fn [file]
+                                          (loop [fdata file
+                                                 entries (seq changes)]
+                                            (if-let [[page-id changes] (first entries)]
+                                              (recur (-> fdata
+                                                         (cp/process-changes changes)
+                                                         (ctst/update-object-indices page-id))
+                                                     (rest entries))
+                                              fdata))))))
           (-> state
               (update-in [:workspace-libraries file-id :revn] max revn)
-              (update-in [:workspace-libraries file-id :data]
-                         cp/process-changes changes)))))))
+              (update-in [:workspace-libraries file-id :data] cp/process-changes changes)))
+
+        state))))
 
 
 

From 7a8b0e710b0b5d16abed2170a5791e3d1b862ae3 Mon Sep 17 00:00:00 2001
From: Andrey Antukh <niwi@niwi.nz>
Date: Sat, 31 Dec 2022 10:49:04 +0100
Subject: [PATCH 2/4] :sparkles: Improve trace reporting on unhandled exception

---
 frontend/src/app/main/errors.cljs | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/frontend/src/app/main/errors.cljs b/frontend/src/app/main/errors.cljs
index fe9eb8744..d6d732923 100644
--- a/frontend/src/app/main/errors.cljs
+++ b/frontend/src/app/main/errors.cljs
@@ -30,7 +30,14 @@
   [error]
   (cond
     (instance? ExceptionInfo error)
-    (-> error ex-data ptk/handle-error)
+    (let [data (ex-data error)]
+      (if (contains? data :type)
+        (ptk/handle-error data)
+        (let [hint (str/ffmt "Unexpected error: '%'" (ex-message error))]
+          (ts/schedule #(st/emit! (rt/assign-exception error)))
+          (js/console.group hint)
+          (js/console.log (.-stack error))
+          (js/console.groupEnd hint))))
 
     (map? error)
     (ptk/handle-error error)
@@ -49,7 +56,7 @@
 
 (defmethod ptk/handle-error :default
   [error]
-  (let [hint (str/concat "Unexpected error: " (:hint error))]
+  (let [hint (str/ffmt "Unhandled error: '%'" (:hint error "[no hint]"))]
     (ts/schedule #(st/emit! (rt/assign-exception error)))
     (js/console.group hint)
     (ex/ignoring (js/console.error (pr-str error)))

From d68be0869b502f453fd2edaabcea07a3eabd658b Mon Sep 17 00:00:00 2001
From: Andrey Antukh <niwi@niwi.nz>
Date: Sat, 31 Dec 2022 10:49:29 +0100
Subject: [PATCH 3/4] :sparkles: Improve error report on point constructor

---
 common/src/app/common/geom/point.cljc | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/common/src/app/common/geom/point.cljc b/common/src/app/common/geom/point.cljc
index e15f0fff0..bfc55dc6d 100644
--- a/common/src/app/common/geom/point.cljc
+++ b/common/src/app/common/geom/point.cljc
@@ -13,6 +13,7 @@
       :clj [clojure.core :as c])
    [app.common.data :as d]
    [app.common.data.macros :as dm]
+   [app.common.exceptions :as ex]
    [app.common.math :as mth]
    [app.common.spec :as us]
    [clojure.spec.alpha :as s]
@@ -62,7 +63,7 @@
      (map->Point v)
 
      :else
-     (throw (ex-info "Invalid arguments" {:v v}))))
+     (ex/raise :hint "invalid arguments (on pointer constructor)" :value v)))
   ([x y]
    (Point. x y)))
 

From 7e21d827c97584ac0bbafed54cf49be2ba3edf57 Mon Sep 17 00:00:00 2001
From: Alejandro Alonso <alejandroalonsofernandez@gmail.com>
Date: Mon, 2 Jan 2023 08:41:21 +0100
Subject: [PATCH 4/4] :bug: Fix duplicate frame issues

---
 .../app/main/data/workspace/selection.cljs    | 23 +++++++++++++------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs
index 3ef4a4bf6..838f21812 100644
--- a/frontend/src/app/main/data/workspace/selection.cljs
+++ b/frontend/src/app/main/data/workspace/selection.cljs
@@ -23,6 +23,7 @@
    [app.main.data.workspace.collapse :as dwc]
    [app.main.data.workspace.state-helpers :as wsh]
    [app.main.data.workspace.thumbnails :as dwt]
+   [app.main.data.workspace.undo :as dwu]
    [app.main.data.workspace.zoom :as dwz]
    [app.main.refs :as refs]
    [app.main.streams :as ms]
@@ -502,8 +503,11 @@
   [obj state objects]
   (let [{:keys [id-original id-duplicated]}
         (get-in state [:workspace-local :duplicated])]
-    (if (and (not= id-original (:id obj))
-             (not= id-duplicated (:id obj)))
+    (if (or (and (not= id-original (:id obj))
+                 (not= id-duplicated (:id obj)))
+            ;; As we can remove duplicated elements may be we can still caching a deleted id
+            (not (contains? objects id-original))
+            (not (contains? objects id-duplicated)))
 
       ;; The default is leave normal shapes in place, but put
       ;; new frames to the right of the original.
@@ -556,16 +560,21 @@
 
                   frames (into #{}
                                (map #(get-in objects [% :frame-id]))
-                               selected)]
+                               selected)
+                  undo-id (uuid/next)]
+
               (rx/concat
                (->> (rx/from dup-frames)
                     (rx/map (fn [[old-id new-id]] (dwt/duplicate-thumbnail old-id new-id))))
 
                ;; Warning: This order is important for the focus mode.
-               (rx/of (dch/commit-changes changes)
-                      (select-shapes new-selected)
-                      (ptk/data-event :layout/update frames)
-                      (memorize-duplicated id-original id-duplicated))))))))))
+               (rx/of
+                (dwu/start-undo-transaction undo-id)
+                (dch/commit-changes changes)
+                (select-shapes new-selected)
+                (ptk/data-event :layout/update frames)
+                (memorize-duplicated id-original id-duplicated)
+                (dwu/commit-undo-transaction undo-id))))))))))
 
 (defn change-hover-state
   [id value]