From 8d0afd8c9682d6fb0114add5d7da4469da557910 Mon Sep 17 00:00:00 2001
From: Andrey Antukh <niwi@niwi.nz>
Date: Wed, 24 Jan 2024 17:42:02 +0100
Subject: [PATCH] :bug: Add migration for fix invalid shadows

---
 backend/src/app/util/time.clj                 |  1 -
 common/src/app/common/files/defaults.cljc     |  2 +-
 common/src/app/common/files/migrations.cljc   | 26 +++++++++++
 common/src/app/common/schema.cljc             |  4 +-
 common/src/app/common/time.cljc               | 45 ++++++++++++++++---
 common/src/app/common/types/color.cljc        | 26 ++++++-----
 common/src/app/common/types/shape/shadow.cljc | 12 +----
 7 files changed, 86 insertions(+), 30 deletions(-)

diff --git a/backend/src/app/util/time.clj b/backend/src/app/util/time.clj
index d3611d71e..778596624 100644
--- a/backend/src/app/util/time.clj
+++ b/backend/src/app/util/time.clj
@@ -123,7 +123,6 @@
   FileTime
   (inst-ms* [v] (.toMillis ^FileTime v)))
 
-
 (defmethod print-method Duration
   [mv ^java.io.Writer writer]
   (.write writer (str "#app/duration \"" (str/lower (subs (str mv) 2)) "\"")))
diff --git a/common/src/app/common/files/defaults.cljc b/common/src/app/common/files/defaults.cljc
index 08a590111..e35914d73 100644
--- a/common/src/app/common/files/defaults.cljc
+++ b/common/src/app/common/files/defaults.cljc
@@ -6,4 +6,4 @@
 
 (ns app.common.files.defaults)
 
-(def version 43)
+(def version 44)
diff --git a/common/src/app/common/files/migrations.cljc b/common/src/app/common/files/migrations.cljc
index d2213168c..39d448597 100644
--- a/common/src/app/common/files/migrations.cljc
+++ b/common/src/app/common/files/migrations.cljc
@@ -23,6 +23,7 @@
    [app.common.svg :as csvg]
    [app.common.text :as txt]
    [app.common.types.shape :as cts]
+   [app.common.types.shape.shadow :as ctss]
    [app.common.uuid :as uuid]
    [cuerdas.core :as str]))
 
@@ -803,3 +804,28 @@
     (-> data
         (update :pages-index update-vals update-container)
         (update :components update-vals update-container))))
+
+(def ^:private valid-shadow?
+  (sm/lazy-validator ::ctss/shadow))
+
+(defmethod migrate 44
+  [data]
+  (letfn [(fix-shadow [shadow]
+            (if (string? (:color shadow))
+              (let [color {:color (:color shadow)
+                           :opacity 1}]
+                (assoc shadow :color color))
+              shadow))
+
+          (update-object [object]
+            (d/update-when object :shadow
+                           #(into []
+                                  (comp (map fix-shadow)
+                                        (filter valid-shadow?))
+                                  %)))
+
+          (update-container [container]
+            (d/update-when container :objects update-vals update-object))]
+    (-> data
+        (update :pages-index update-vals update-container)
+        (update :components update-vals update-container))))
diff --git a/common/src/app/common/schema.cljc b/common/src/app/common/schema.cljc
index b346f66d2..b1e743f64 100644
--- a/common/src/app/common/schema.cljc
+++ b/common/src/app/common/schema.cljc
@@ -14,6 +14,7 @@
    [app.common.schema.generators :as sg]
    [app.common.schema.openapi :as-alias oapi]
    [app.common.schema.registry :as sr]
+   [app.common.time :as tm]
    [app.common.uri :as u]
    [app.common.uuid :as uuid]
    [clojure.core :as c]
@@ -625,7 +626,8 @@
    {:title "inst"
     :description "Satisfies Inst protocol"
     :error/message "expected to be number in safe range"
-    :gen/gen (sg/small-int)
+    :gen/gen (->> (sg/small-int)
+                  (sg/fmap (fn [v] (tm/instant v))))
     ::oapi/type "number"
     ::oapi/format "int64"}})
 
diff --git a/common/src/app/common/time.cljc b/common/src/app/common/time.cljc
index c32c82411..8b1ead444 100644
--- a/common/src/app/common/time.cljc
+++ b/common/src/app/common/time.cljc
@@ -4,16 +4,16 @@
 ;;
 ;; Copyright (c) KALEIDOS INC
 
-;; Here we put the time functions that are common between frontend and backend.
-;; In the future we may create an unified API for both.
-
 (ns app.common.time
+  "A new cross-platform date and time API. It should be prefered over
+  a platform specific implementation found on `app.util.time`."
   #?(:cljs
      (:require
       ["luxon" :as lxn])
      :clj
      (:import
-      java.time.Instant)))
+      java.time.Instant
+      java.time.Duration)))
 
 #?(:cljs
    (def DateTime lxn/DateTime))
@@ -24,4 +24,39 @@
 (defn now
   []
   #?(:clj (Instant/now)
-     :cljs (.local ^js DateTime)))
\ No newline at end of file
+     :cljs (.local ^js DateTime)))
+
+(defn instant
+  [s]
+  #?(:clj  (Instant/ofEpochMilli s)
+     :cljs (.fromMillis ^js DateTime s #js {:zone "local" :setZone false})))
+
+#?(:cljs
+   (extend-protocol IComparable
+     DateTime
+     (-compare [it other]
+       (if ^boolean (.equals it other)
+         0
+         (if (< (inst-ms it) (inst-ms other)) -1 1)))
+
+     Duration
+     (-compare [it other]
+       (if ^boolean (.equals it other)
+         0
+         (if (< (inst-ms it) (inst-ms other)) -1 1)))))
+
+#?(:cljs
+   (extend-protocol cljs.core/Inst
+     DateTime
+     (inst-ms* [inst] (.toMillis ^js inst))
+
+     Duration
+     (inst-ms* [inst] (.toMillis ^js inst)))
+
+   :clj
+   (extend-protocol clojure.core/Inst
+     Duration
+     (inst-ms* [v] (.toMillis ^Duration v))
+
+     Instant
+     (inst-ms* [v] (.toEpochMilli ^Instant v))))
diff --git a/common/src/app/common/types/color.cljc b/common/src/app/common/types/color.cljc
index 804962894..3a726d77a 100644
--- a/common/src/app/common/types/color.cljc
+++ b/common/src/app/common/types/color.cljc
@@ -70,18 +70,20 @@
       [:offset ::sm/safe-number]]]]])
 
 (sm/define! ::color
-  [:map {:title "Color"}
-   [:id {:optional true} ::sm/uuid]
-   [:name {:optional true} :string]
-   [:path {:optional true} [:maybe :string]]
-   [:value {:optional true} [:maybe :string]]
-   [:color {:optional true} [:maybe ::rgb-color]]
-   [:opacity {:optional true} [:maybe ::sm/safe-number]]
-   [:modified-at {:optional true} ::sm/inst]
-   [:ref-id {:optional true} ::sm/uuid]
-   [:ref-file {:optional true} ::sm/uuid]
-   [:gradient {:optional true} [:maybe ::gradient]]
-   [:image {:optional true} [:maybe ::image-color]]])
+  [:and
+   [:map {:title "Color"}
+    [:id {:optional true} ::sm/uuid]
+    [:name {:optional true} :string]
+    [:path {:optional true} [:maybe :string]]
+    [:value {:optional true} [:maybe :string]]
+    [:color {:optional true} [:maybe ::rgb-color]]
+    [:opacity {:optional true} [:maybe ::sm/safe-number]]
+    [:modified-at {:optional true} ::sm/inst]
+    [:ref-id {:optional true} ::sm/uuid]
+    [:ref-file {:optional true} ::sm/uuid]
+    [:gradient {:optional true} [:maybe ::gradient]]
+    [:image {:optional true} [:maybe ::image-color]]]
+   [::sm/contains-any {:strict true} [:color :gradient :image]]])
 
 (sm/define! ::recent-color
   [:and
diff --git a/common/src/app/common/types/shape/shadow.cljc b/common/src/app/common/types/shape/shadow.cljc
index d04886fa3..cc2fd81c3 100644
--- a/common/src/app/common/types/shape/shadow.cljc
+++ b/common/src/app/common/types/shape/shadow.cljc
@@ -7,8 +7,7 @@
 (ns app.common.types.shape.shadow
   (:require
    [app.common.schema :as sm]
-   [app.common.types.color :as ctc]
-   [app.common.types.shape.shadow.color :as-alias shadow-color]))
+   [app.common.types.color :as ctc]))
 
 (def styles #{:drop-shadow :inner-shadow})
 
@@ -21,11 +20,4 @@
    [:blur ::sm/safe-number]
    [:spread ::sm/safe-number]
    [:hidden :boolean]
-    ;;FIXME: reuse color?
-   [:color
-    [:map
-     [:color {:optional true} :string]
-     [:opacity {:optional true} ::sm/safe-number]
-     [:gradient {:optional true} [:maybe ::ctc/gradient]]
-     [:file-id {:optional true} [:maybe ::sm/uuid]]
-     [:id {:optional true} [:maybe ::sm/uuid]]]]])
+   [:color ::ctc/color]])