0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-26 22:51:29 -05:00

♻️ The big media refactor (frontend)

This commit is contained in:
Andrés Moya 2020-08-07 12:35:37 +02:00 committed by Andrey Antukh
parent bd7114182f
commit 05d54e4bea
17 changed files with 833 additions and 827 deletions

View file

@ -140,7 +140,7 @@
(when (and (string? mtype) (when (and (string? mtype)
(not= mtype mtype')) (not= mtype mtype'))
(ex/raise :type :validation (ex/raise :type :validation
:code :image-type-mismatch :code :media-type-mismatch
:hint "Seems like you are uploading a file whose content does not match the extension.")) :hint "Seems like you are uploading a file whose content does not match the extension."))
{:width (.getImageWidth instance) {:width (.getImageWidth instance)
:height (.getImageHeight instance) :height (.getImageHeight instance)

View file

@ -42,7 +42,7 @@
(declare create-file) (declare create-file)
(declare create-page) (declare create-page)
(s/def ::is-shared boolean?) (s/def ::is-shared ::us/boolean)
(s/def ::create-file (s/def ::create-file
(s/keys :req-un [::profile-id ::name ::project-id ::is-shared] (s/keys :req-un [::profile-id ::name ::project-id ::is-shared]
:opt-un [::id])) :opt-un [::id]))
@ -188,7 +188,7 @@
;; [conn {:keys [content file-id name] :as params}] ;; [conn {:keys [content file-id name] :as params}]
;; (when-not (imgs/valid-image-types? (:content-type content)) ;; (when-not (imgs/valid-image-types? (:content-type content))
;; (ex/raise :type :validation ;; (ex/raise :type :validation
;; :code :image-type-not-allowed ;; :code :media-type-not-allowed
;; :hint "Seems like you are uploading an invalid image.")) ;; :hint "Seems like you are uploading an invalid image."))
;; ;;
;; (let [info (images/run {:cmd :info :input {:path (:tempfile content) ;; (let [info (images/run {:cmd :info :input {:path (:tempfile content)

View file

@ -25,8 +25,8 @@
[uxbox.util.time :as dt])) [uxbox.util.time :as dt]))
(def thumbnail-options (def thumbnail-options
{:width 800 {:width 100
:height 800 :height 100
:quality 85 :quality 85
:format :jpeg}) :format :jpeg})
@ -122,7 +122,7 @@
(s/def ::content ::upload) (s/def ::content ::upload)
(s/def ::is-local boolean?) (s/def ::is-local ::us/boolean)
(s/def ::add-media-object-from-url (s/def ::add-media-object-from-url
(s/keys :req-un [::profile-id ::file-id ::url ::is-local] (s/keys :req-un [::profile-id ::file-id ::url ::is-local]
@ -179,18 +179,18 @@
:width (:width info) :width (:width info)
:height (:height info) :height (:height info)
:mtype (:mtype info)}) :mtype (:mtype info)})
(media/resolve-urls :path :uri) (media/resolve-urls :path :uri))
(media/resolve-urls :thumb-path :thumb-uri))
media-thumbnail (db/insert! conn :media-thumbnail media-thumbnail (-> (db/insert! conn :media-thumbnail
{:id (uuid/next) {:id (uuid/next)
:media-object-id media-object-id :media-object-id media-object-id
:path (str (:path thumb)) :path (str (:path thumb))
:width (:width thumb) :width (:width thumb)
:height (:height thumb) :height (:height thumb)
:quality (:quality thumb) :quality (:quality thumb)
:mtype (:mtype thumb)})] :mtype (:mtype thumb)})
media-object)) (media/resolve-urls :path :uri))]
(assoc media-object :thumb-uri (:uri media-thumbnail))))
(def ^:private sql:select-file-for-update (def ^:private sql:select-file-for-update
"select file.*, "select file.*,

View file

@ -77,7 +77,7 @@
(declare retrieve-media-objects) (declare retrieve-media-objects)
(declare retrieve-file) (declare retrieve-file)
(s/def ::is-local boolean?) (s/def ::is-local ::us/boolean)
(s/def ::media-objects (s/def ::media-objects
(s/keys :req-un [::profile-id ::file-id ::is-local])) (s/keys :req-un [::profile-id ::file-id ::is-local]))
@ -90,15 +90,18 @@
(let [file (retrieve-file conn file-id)] (let [file (retrieve-file conn file-id)]
(teams/check-read-permissions! conn profile-id (:team-id file)) (teams/check-read-permissions! conn profile-id (:team-id file))
(->> (retrieve-media-objects conn file-id is-local) (->> (retrieve-media-objects conn file-id is-local)
(mapv #(media/resolve-urls % :path :uri)))))) (mapv #(media/resolve-urls % :path :uri))
(mapv #(media/resolve-urls % :thumb-path :thumb-uri))))))
(def ^:private sql:media-objects (def ^:private sql:media-objects
"select * "select obj.*,
from media_object thumb.path as thumb_path
where deleted_at is null from media_object as obj
and file_id = ? inner join media_thumbnail as thumb on obj.id = thumb.media_object_id
and is_local = ? where obj.deleted_at is null
order by created_at desc") and obj.file_id = ?
and obj.is_local = ?
order by obj.created_at desc")
(defn retrieve-media-objects (defn retrieve-media-objects
[conn file-id is-local] [conn file-id is-local]

View file

@ -737,7 +737,7 @@
"es" : "Ha ocurrido algún error." "es" : "Ha ocurrido algún error."
} }
}, },
"errors.image-format-unsupported" : { "errors.media-format-unsupported" : {
"used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:406", "src/uxbox/main/data/users.cljs:177", "src/uxbox/main/data/images.cljs:376" ], "used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:406", "src/uxbox/main/data/users.cljs:177", "src/uxbox/main/data/images.cljs:376" ],
"translations" : { "translations" : {
"en" : "The image format is not supported (must be svg, jpg or png).", "en" : "The image format is not supported (must be svg, jpg or png).",
@ -746,7 +746,7 @@
"es" : "No se reconoce el formato de imagen (debe ser svg, jpg o png)." "es" : "No se reconoce el formato de imagen (debe ser svg, jpg o png)."
} }
}, },
"errors.image-too-large" : { "errors.media-too-large" : {
"used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:404", "src/uxbox/main/data/users.cljs:175", "src/uxbox/main/data/images.cljs:374" ], "used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:404", "src/uxbox/main/data/users.cljs:175", "src/uxbox/main/data/images.cljs:374" ],
"translations" : { "translations" : {
"en" : "The image is too large to be inserted (must be under 5mb).", "en" : "The image is too large to be inserted (must be under 5mb).",
@ -755,7 +755,7 @@
"es" : "La imagen es demasiado grande (debe tener menos de 5mb)." "es" : "La imagen es demasiado grande (debe tener menos de 5mb)."
} }
}, },
"errors.image-type-mismatch" : { "errors.media-type-mismatch" : {
"used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:371", "src/uxbox/main/data/workspace/persistence.cljs:421", "src/uxbox/main/data/users.cljs:191", "src/uxbox/main/data/images.cljs:391" ], "used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:371", "src/uxbox/main/data/workspace/persistence.cljs:421", "src/uxbox/main/data/users.cljs:191", "src/uxbox/main/data/images.cljs:391" ],
"translations" : { "translations" : {
"en" : "Seems that the contents of the image does not match the file extension.", "en" : "Seems that the contents of the image does not match the file extension.",
@ -764,7 +764,7 @@
"es" : "Parece que el contenido de la imagen no coincide con la etensión del archivo." "es" : "Parece que el contenido de la imagen no coincide con la etensión del archivo."
} }
}, },
"errors.image-type-not-allowed" : { "errors.media-type-not-allowed" : {
"used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:368", "src/uxbox/main/data/workspace/persistence.cljs:418", "src/uxbox/main/data/users.cljs:188", "src/uxbox/main/data/images.cljs:388" ], "used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:368", "src/uxbox/main/data/workspace/persistence.cljs:418", "src/uxbox/main/data/users.cljs:188", "src/uxbox/main/data/images.cljs:388" ],
"translations" : { "translations" : {
"en" : "Seems that this is not a valid image.", "en" : "Seems that this is not a valid image.",
@ -854,7 +854,7 @@
"es" : "Estás viendo la versión %s" "es" : "Estás viendo la versión %s"
} }
}, },
"image.loading" : { "media.loading" : {
"used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:382", "src/uxbox/main/data/workspace/persistence.cljs:433", "src/uxbox/main/data/users.cljs:201", "src/uxbox/main/data/images.cljs:403" ], "used-in" : [ "src/uxbox/main/data/workspace/persistence.cljs:382", "src/uxbox/main/data/workspace/persistence.cljs:433", "src/uxbox/main/data/users.cljs:201", "src/uxbox/main/data/images.cljs:403" ],
"translations" : { "translations" : {
"en" : "Loading image...", "en" : "Loading image...",

View file

@ -4,7 +4,7 @@
;; ;;
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz> ;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.main.data.images (ns uxbox.main.data.media
(:require (:require
[cljs.spec.alpha :as s] [cljs.spec.alpha :as s]
[cuerdas.core :as str] [cuerdas.core :as str]
@ -30,35 +30,34 @@
(s/def ::modified-at inst?) (s/def ::modified-at inst?)
(s/def ::created-at inst?) (s/def ::created-at inst?)
(s/def ::mtype string?) (s/def ::mtype string?)
(s/def ::thumbnail string?) ;; (s/def ::thumbnail string?)
(s/def ::id uuid?) (s/def ::id uuid?)
(s/def ::url string?) (s/def ::uri string?)
(s/def ::collection-id uuid?) ;; (s/def ::collection-id uuid?)
(s/def ::user-id uuid?) (s/def ::user-id uuid?)
(s/def ::collection ;; (s/def ::collection
(s/keys :req-un [::id ;; (s/keys :req-un [::id
::name ;; ::name
::created-at ;; ::created-at
::modified-at ;; ::modified-at
::user-id])) ;; ::user-id]))
(s/def ::image (s/def ::media-object
(s/keys :req-un [::id (s/keys :req-un [::id
::name ::name
::width ::width
::height ::height
::mtype ::mtype
::collection-id
::created-at ::created-at
::modified-at ::modified-at
::uri ::uri
::thumb-uri ;; ::thumb-uri
::user-id])) ::user-id]))
;; ;; --- Initialize Collection Page ;; ;; --- Initialize Collection Page
;; ;;
;; (declare fetch-images) ;; (declare fetch-media-objects)
;; ;;
;; (defn initialize ;; (defn initialize
;; [collection-id] ;; [collection-id]
@ -66,11 +65,11 @@
;; (ptk/reify ::initialize ;; (ptk/reify ::initialize
;; ptk/UpdateEvent ;; ptk/UpdateEvent
;; (update [_ state] ;; (update [_ state]
;; (assoc-in state [:dashboard-images :selected] #{})) ;; (assoc-in state [:dashboard-media-objects :selected] #{}))
;; ;;
;; ptk/WatchEvent ;; ptk/WatchEvent
;; (watch [_ state stream] ;; (watch [_ state stream]
;; (rx/of (fetch-images collection-id))))) ;; (rx/of (fetch-media-objects collection-id)))))
;; ;;
;; ;; --- Fetch Collections ;; ;; --- Fetch Collections
;; ;;
@ -80,7 +79,7 @@
;; (ptk/reify ::fetch-collections ;; (ptk/reify ::fetch-collections
;; ptk/WatchEvent ;; ptk/WatchEvent
;; (watch [_ state s] ;; (watch [_ state s]
;; (->> (rp/query! :image-collections) ;; (->> (rp/query! :media-object-collections)
;; (rx/map collections-fetched))))) ;; (rx/map collections-fetched)))))
;; ;;
;; ;;
@ -95,7 +94,7 @@
;; (reduce (fn [state {:keys [id user] :as item}] ;; (reduce (fn [state {:keys [id user] :as item}]
;; (let [type (if (uuid/zero? (:user-id item)) :builtin :own) ;; (let [type (if (uuid/zero? (:user-id item)) :builtin :own)
;; item (assoc item :type type)] ;; item (assoc item :type type)]
;; (assoc-in state [:images-collections id] item))) ;; (assoc-in state [:media-objects-collections id] item)))
;; state ;; state
;; items)))) ;; items))))
;; ;;
@ -109,7 +108,7 @@
;; ptk/WatchEvent ;; ptk/WatchEvent
;; (watch [_ state s] ;; (watch [_ state s]
;; (let [data {:name (tr "ds.default-library-title" (gensym "c"))}] ;; (let [data {:name (tr "ds.default-library-title" (gensym "c"))}]
;; (->> (rp/mutation! :create-image-collection data) ;; (->> (rp/mutation! :create-media-object-collection data)
;; (rx/map collection-created)))))) ;; (rx/map collection-created))))))
;; ;;
;; ;; --- Collection Created ;; ;; --- Collection Created
@ -121,7 +120,7 @@
;; ptk/UpdateEvent ;; ptk/UpdateEvent
;; (update [_ state] ;; (update [_ state]
;; (let [{:keys [id] :as item} (assoc item :type :own)] ;; (let [{:keys [id] :as item} (assoc item :type :own)]
;; (update state :images-collections assoc id item))))) ;; (update state :media-objects-collections assoc id item)))))
;; ;;
;; ;; --- Rename Collection ;; ;; --- Rename Collection
;; ;;
@ -130,12 +129,12 @@
;; (ptk/reify ::rename-collection ;; (ptk/reify ::rename-collection
;; ptk/UpdateEvent ;; ptk/UpdateEvent
;; (update [_ state] ;; (update [_ state]
;; (assoc-in state [:images-collections id :name] name)) ;; (assoc-in state [:media-objects-collections id :name] name))
;; ;;
;; ptk/WatchEvent ;; ptk/WatchEvent
;; (watch [_ state s] ;; (watch [_ state s]
;; (let [params {:id id :name name}] ;; (let [params {:id id :name name}]
;; (->> (rp/mutation! :rename-image-collection params) ;; (->> (rp/mutation! :rename-media-object-collection params)
;; (rx/ignore)))))) ;; (rx/ignore))))))
;; ;;
;; ;; --- Delete Collection ;; ;; --- Delete Collection
@ -145,141 +144,141 @@
;; (ptk/reify ::delete-collection ;; (ptk/reify ::delete-collection
;; ptk/UpdateEvent ;; ptk/UpdateEvent
;; (update [_ state] ;; (update [_ state]
;; (update state :images-collections dissoc id)) ;; (update state :media-objects-collections dissoc id))
;; ;;
;; ptk/WatchEvent ;; ptk/WatchEvent
;; (watch [_ state s] ;; (watch [_ state s]
;; (->> (rp/mutation! :delete-image-collection {:id id}) ;; (->> (rp/mutation! :delete-media-object-collection {:id id})
;; (rx/tap on-success) ;; (rx/tap on-success)
;; (rx/ignore))))) ;; (rx/ignore)))))
;; ;;
;; ;; --- Update Image ;; ;; --- Update Media object
;; ;;
;; (defn persist-image ;; (defn persist-media-object
;; [id] ;; [id]
;; (us/verify ::us/uuid id) ;; (us/verify ::us/uuid id)
;; (ptk/reify ::persist-image ;; (ptk/reify ::persist-media-object
;; ptk/WatchEvent ;; ptk/WatchEvent
;; (watch [_ state stream] ;; (watch [_ state stream]
;; (let [data (get-in state [:images id])] ;; (let [data (get-in state [:media-objects id])]
;; (->> (rp/mutation! :update-image data) ;; (->> (rp/mutation! :update-media-object data)
;; (rx/ignore)))))) ;; (rx/ignore))))))
;; ;;
;; ;; --- Fetch Images ;; ;; --- Fetch Media objects
;; ;;
;; (declare images-fetched) ;; (declare media-objects-fetched)
;; ;;
;; (defn fetch-images ;; (defn fetch-media-objects
;; "Fetch a list of images of the selected collection" ;; "Fetch a list of media-objects of the selected collection"
;; [id] ;; [id]
;; (us/verify ::us/uuid id) ;; (us/verify ::us/uuid id)
;; (ptk/reify ::fetch-images ;; (ptk/reify ::fetch-media-objects
;; ptk/WatchEvent ;; ptk/WatchEvent
;; (watch [_ state s] ;; (watch [_ state s]
;; (let [params {:collection-id id}] ;; (let [params {:collection-id id}]
;; (->> (rp/query! :images-by-collection params) ;; (->> (rp/query! :media-objects-by-collection params)
;; (rx/map (partial images-fetched id))))))) ;; (rx/map (partial media-objects-fetched id)))))))
;; ;;
;; ;; --- Images Fetched ;; ;; --- Media objects Fetched
;; ;;
;; (s/def ::images (s/every ::image)) ;; (s/def ::media-objects (s/every ::media-object))
;; ;;
;; (defn images-fetched ;; (defn media-objects-fetched
;; [collection-id items] ;; [collection-id items]
;; (us/verify ::us/uuid collection-id) ;; (us/verify ::us/uuid collection-id)
;; (us/verify ::images items) ;; (us/verify ::media-objects items)
;; (ptk/reify ::images-fetched ;; (ptk/reify ::media-objects-fetched
;; ptk/UpdateEvent ;; ptk/UpdateEvent
;; (update [_ state] ;; (update [_ state]
;; (let [images (d/index-by :id items)] ;; (let [media-objects (d/index-by :id items)]
;; (assoc state :images images))))) ;; (assoc state :media-objects media-objects)))))
;; ;;
;; ;; --- Fetch Image ;; ;; --- Fetch Media object
;; ;;
;; (declare image-fetched) ;; (declare media-object-fetched)
;; ;;
;; (defrecord FetchImage [id] ;; (defrecord FetchMediaObject [id]
;; ptk/WatchEvent ;; ptk/WatchEvent
;; (watch [_ state stream] ;; (watch [_ state stream]
;; (let [existing (get-in state [:images id])] ;; (let [existing (get-in state [:media-objects id])]
;; (if existing ;; (if existing
;; (rx/empty) ;; (rx/empty)
;; (->> (rp/query! :image-by-id {:id id}) ;; (->> (rp/query! :media-object-by-id {:id id})
;; (rx/map image-fetched) ;; (rx/map media-object-fetched)
;; (rx/catch rp/client-error? #(rx/empty))))))) ;; (rx/catch rp/client-error? #(rx/empty)))))))
;; ;;
;; (defn fetch-image ;; (defn fetch-media-object
;; "Conditionally fetch image by its id. If image ;; "Conditionally fetch media-object by its id. If media-object
;; is already loaded, this event is noop." ;; is already loaded, this event is noop."
;; [id] ;; [id]
;; {:pre [(uuid? id)]} ;; {:pre [(uuid? id)]}
;; (FetchImage. id)) ;; (FetchMediaObject. id))
;; ;;
;; ;; --- Image Fetched ;; ;; --- MediaObject Fetched
;; ;;
;; (defrecord ImageFetched [image] ;; (defrecord MediaObjectFetched [media-object]
;; ptk/UpdateEvent ;; ptk/UpdateEvent
;; (update [_ state] ;; (update [_ state]
;; (let [id (:id image)] ;; (let [id (:id media-object)]
;; (update state :images assoc id image)))) ;; (update state :media-objects assoc id media-object))))
;; ;;
;; (defn image-fetched ;; (defn media-object-fetched
;; [image] ;; [media-object]
;; {:pre [(map? image)]} ;; {:pre [(map? media-object)]}
;; (ImageFetched. image)) ;; (MediaObjectFetched. media-object))
;; ;;
;; ;; --- Rename Image ;; ;; --- Rename MediaObject
;; ;;
;; (defn rename-image ;; (defn rename-media-object
;; [id name] ;; [id name]
;; (us/verify ::us/uuid id) ;; (us/verify ::us/uuid id)
;; (us/verify ::us/string name) ;; (us/verify ::us/string name)
;; (ptk/reify ::rename-image ;; (ptk/reify ::rename-media-object
;; ptk/UpdateEvent ;; ptk/UpdateEvent
;; (update [_ state] ;; (update [_ state]
;; (assoc-in state [:images id :name] name)) ;; (assoc-in state [:media-objects id :name] name))
;; ;;
;; ptk/WatchEvent ;; ptk/WatchEvent
;; (watch [_ state stream] ;; (watch [_ state stream]
;; (rx/of (persist-image id))))) ;; (rx/of (persist-media-object id)))))
;; ;;
;; ;; --- Image Selection ;; ;; --- MediaObject Selection
;; ;;
;; (defn select-image ;; (defn select-media-object
;; [id] ;; [id]
;; (ptk/reify ::select-image ;; (ptk/reify ::select-media-object
;; ptk/UpdateEvent ;; ptk/UpdateEvent
;; (update [_ state] ;; (update [_ state]
;; (update-in state [:dashboard-images :selected] (fnil conj #{}) id)))) ;; (update-in state [:dashboard-media-objects :selected] (fnil conj #{}) id))))
;; ;;
;; (defn deselect-image ;; (defn deselect-media-object
;; [id] ;; [id]
;; (ptk/reify ::deselect-image ;; (ptk/reify ::deselect-media-object
;; ptk/UpdateEvent ;; ptk/UpdateEvent
;; (update [_ state] ;; (update [_ state]
;; (update-in state [:dashboard-images :selected] (fnil disj #{}) id)))) ;; (update-in state [:dashboard-media-objects :selected] (fnil disj #{}) id))))
;; ;;
;; (def deselect-all-images ;; (def deselect-all-media-objects
;; (ptk/reify ::deselect-all-images ;; (ptk/reify ::deselect-all-media-objects
;; ptk/UpdateEvent ;; ptk/UpdateEvent
;; (update [_ state] ;; (update [_ state]
;; (assoc-in state [:dashboard-images :selected] #{})))) ;; (assoc-in state [:dashboard-media-objects :selected] #{}))))
;; ;;
;; ;; --- Delete Images ;; ;; --- Delete MediaObjects
;; ;;
;; (defn delete-image ;; (defn delete-media-object
;; [id] ;; [id]
;; (us/verify ::us/uuid id) ;; (us/verify ::us/uuid id)
;; (ptk/reify ::delete-image ;; (ptk/reify ::delete-media-object
;; ptk/UpdateEvent ;; ptk/UpdateEvent
;; (update [_ state] ;; (update [_ state]
;; (update state :images dissoc id)) ;; (update state :media-objects dissoc id))
;; ;;
;; ptk/WatchEvent ;; ptk/WatchEvent
;; (watch [_ state s] ;; (watch [_ state s]
;; (rx/merge ;; (rx/merge
;; (rx/of deselect-all-images) ;; (rx/of deselect-all-media-objects)
;; (->> (rp/mutation! :delete-image {:id id}) ;; (->> (rp/mutation! :delete-media-object {:id id})
;; (rx/ignore)))))) ;; (rx/ignore))))))
;; ;;
;; ;; --- Delete Selected ;; ;; --- Delete Selected
@ -288,9 +287,9 @@
;; (ptk/reify ::delete-selected ;; (ptk/reify ::delete-selected
;; ptk/WatchEvent ;; ptk/WatchEvent
;; (watch [_ state stream] ;; (watch [_ state stream]
;; (let [selected (get-in state [:dashboard-images :selected])] ;; (let [selected (get-in state [:dashboard-media-objects :selected])]
;; (->> (rx/from selected) ;; (->> (rx/from selected)
;; (rx/map delete-image)))))) ;; (rx/map delete-media-object))))))
;; ;;
;; ;; --- Update Opts (Filtering & Ordering) ;; ;; --- Update Opts (Filtering & Ordering)
;; ;;
@ -300,48 +299,48 @@
;; (ptk/reify ::update-opts ;; (ptk/reify ::update-opts
;; ptk/UpdateEvent ;; ptk/UpdateEvent
;; (update [_ state] ;; (update [_ state]
;; (update state :dashboard-images merge ;; (update state :dashboard-media-objects merge
;; {:edition edition} ;; {:edition edition}
;; (when order {:order order}) ;; (when order {:order order})
;; (when filter {:filter filter}))))) ;; (when filter {:filter filter})))))
;; --- Copy Selected Image ;; --- Copy Selected MediaObject
;; (defrecord CopySelected [id] ;; (defrecord CopySelected [id]
;; ptk/WatchEvent ;; ptk/WatchEvent
;; (watch [_ state stream] ;; (watch [_ state stream]
;; (let [selected (get-in state [:dashboard-images :selected])] ;; (let [selected (get-in state [:dashboard-media-objects :selected])]
;; (rx/merge ;; (rx/merge
;; (->> (rx/from selected) ;; (->> (rx/from selected)
;; (rx/flat-map #(rp/mutation! :copy-image {:id % :collection-id id})) ;; (rx/flat-map #(rp/mutation! :copy-media-object {:id % :collection-id id}))
;; (rx/map image-created)) ;; (rx/map media-object-created))
;; (->> (rx/from selected) ;; (->> (rx/from selected)
;; (rx/map deselect-image)))))) ;; (rx/map deselect-media-object))))))
;; (defn copy-selected ;; (defn copy-selected
;; [id] ;; [id]
;; {:pre [(or (uuid? id) (nil? id))]} ;; {:pre [(or (uuid? id) (nil? id))]}
;; (CopySelected. id)) ;; (CopySelected. id))
;; --- Move Selected Image ;; --- Move Selected MediaObject
;; (defrecord MoveSelected [id] ;; (defrecord MoveSelected [id]
;; ptk/UpdateEvent ;; ptk/UpdateEvent
;; (update [_ state] ;; (update [_ state]
;; (let [selected (get-in state [:dashboard-images :selected])] ;; (let [selected (get-in state [:dashboard-media-objects :selected])]
;; (reduce (fn [state image] ;; (reduce (fn [state media-object]
;; (assoc-in state [:images image :collection] id)) ;; (assoc-in state [:media-objects media-object :collection] id))
;; state ;; state
;; selected))) ;; selected)))
;; ptk/WatchEvent ;; ptk/WatchEvent
;; (watch [_ state stream] ;; (watch [_ state stream]
;; (let [selected (get-in state [:dashboard-images :selected])] ;; (let [selected (get-in state [:dashboard-media-objects :selected])]
;; (rx/merge ;; (rx/merge
;; (->> (rx/from selected) ;; (->> (rx/from selected)
;; (rx/map persist-image)) ;; (rx/map persist-media-object))
;; (->> (rx/from selected) ;; (->> (rx/from selected)
;; (rx/map deselect-image)))))) ;; (rx/map deselect-media-object))))))
;; (defn move-selected ;; (defn move-selected
;; [id] ;; [id]
@ -351,29 +350,30 @@
;;;;;;; NEW ;;;;;;; NEW
;; --- Create Image ;; --- Create library Media Objects
(declare create-images-result)
(declare create-media-objects-result)
(def allowed-file-types #{"image/jpeg" "image/png" "image/webp" "image/svg+xml"}) (def allowed-file-types #{"image/jpeg" "image/png" "image/webp" "image/svg+xml"})
(def max-file-size (* 5 1024 1024)) (def max-file-size (* 5 1024 1024))
;; TODO: unify with upload-image at main/data/workspace/persistence.cljs ;; TODO: unify with upload-media-object at main/data/workspace/persistence.cljs
;; and update-photo at main/data/users.cljs ;; and update-photo at main/data/users.cljs
;; https://tree.taiga.io/project/uxboxproject/us/440 ;; https://tree.taiga.io/project/uxboxproject/us/440
(defn create-images (defn create-media-objects
([file-id files] (create-images file-id files identity)) ([file-id files] (create-media-objects file-id files identity))
([file-id files on-uploaded] ([file-id files on-uploaded]
(us/verify (s/nilable ::us/uuid) file-id) (us/verify (s/nilable ::us/uuid) file-id)
(us/verify fn? on-uploaded) (us/verify fn? on-uploaded)
(ptk/reify ::create-images (ptk/reify ::create-media-objects
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [check-file (let [check-file
(fn [file] (fn [file]
(when (> (.-size file) max-file-size) (when (> (.-size file) max-file-size)
(throw (ex-info (tr "errors.image-too-large") {}))) (throw (ex-info (tr "errors.media-too-large") {})))
(when-not (contains? allowed-file-types (.-type file)) (when-not (contains? allowed-file-types (.-type file))
(throw (ex-info (tr "errors.image-format-unsupported") {}))) (throw (ex-info (tr "errors.media-format-unsupported") {})))
file) file)
on-success #(do (st/emit! dm/hide) on-success #(do (st/emit! dm/hide)
@ -384,11 +384,11 @@
(.-message %) (.-message %)
(.-message %) (.-message %)
(= (:code %) :image-type-not-allowed) (= (:code %) :media-type-not-allowed)
(tr "errors.image-type-not-allowed") (tr "errors.media-type-not-allowed")
(= (:code %) :image-type-mismatch) (= (:code %) :media-type-mismatch)
(tr "errors.image-type-mismatch") (tr "errors.media-type-mismatch")
:else :else
(tr "errors.unexpected-error"))] (tr "errors.unexpected-error"))]
@ -398,30 +398,31 @@
(fn [file] (fn [file]
{:name (.-name file) {:name (.-name file)
:file-id file-id :file-id file-id
:content file})] :content file
:is-local false})]
(st/emit! (dm/show {:content (tr "image.loading") (st/emit! (dm/show {:content (tr "media.loading")
:type :info :type :info
:timeout nil})) :timeout nil}))
(->> (rx/from files) (->> (rx/from files)
(rx/map check-file) (rx/map check-file)
(rx/map prepare) (rx/map prepare)
(rx/mapcat #(rp/mutation! :upload-image %)) (rx/mapcat #(rp/mutation! :upload-media-object %))
(rx/reduce conj []) (rx/reduce conj [])
(rx/do on-success) (rx/do on-success)
(rx/mapcat identity) (rx/mapcat identity)
(rx/map (partial create-images-result file-id)) (rx/map (partial create-media-objects-result file-id))
(rx/catch on-error))))))) (rx/catch on-error)))))))
;; --- Image Created ;; --- Media object Created
(defn create-images-result (defn create-media-objects-result
[file-id image] [file-id media-object]
#_(us/verify ::image image) #_(us/verify ::media-object media-object)
(ptk/reify ::create-images-result (ptk/reify ::create-media-objects-result
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(-> state (-> state
(assoc-in [:workspace-images (:id image)] image))))) (assoc-in [:workspace-media (:id media-object)] media-object)))))

View file

@ -159,8 +159,8 @@
(def allowed-file-types #{"image/jpeg" "image/png" "image/webp"}) (def allowed-file-types #{"image/jpeg" "image/png" "image/webp"})
(def max-file-size (* 5 1024 1024)) (def max-file-size (* 5 1024 1024))
;; TODO: unify with create-images at main/data/images.cljs ;; TODO: unify with create-media-objects at main/data/media.cljs
;; and upload-image at main/data/workspace/persistence.cljs ;; and upload-media-object at main/data/workspace/persistence.cljs
;; https://tree.taiga.io/project/uxboxproject/us/440 ;; https://tree.taiga.io/project/uxboxproject/us/440
(defn update-photo (defn update-photo
@ -172,9 +172,9 @@
(let [check-file (let [check-file
(fn [file] (fn [file]
(when (> (.-size file) max-file-size) (when (> (.-size file) max-file-size)
(throw (ex-info (tr "errors.image-too-large") {}))) (throw (ex-info (tr "errors.media-too-large") {})))
(when-not (contains? allowed-file-types (.-type file)) (when-not (contains? allowed-file-types (.-type file))
(throw (ex-info (tr "errors.image-format-unsupported") {}))) (throw (ex-info (tr "errors.media-format-unsupported") {})))
file) file)
on-success #(do (st/emit! dm/hide)) on-success #(do (st/emit! dm/hide))
@ -184,11 +184,11 @@
(.-message %) (.-message %)
(.-message %) (.-message %)
(= (:code %) :image-type-not-allowed) (= (:code %) :media-type-not-allowed)
(tr "errors.image-type-not-allowed") (tr "errors.media-type-not-allowed")
(= (:code %) :image-type-mismatch) (= (:code %) :media-type-mismatch)
(tr "errors.image-type-mismatch") (tr "errors.media-type-mismatch")
:else :else
(tr "errors.unexpected-error"))] (tr "errors.unexpected-error"))]
@ -198,7 +198,7 @@
(fn [file] (fn [file]
{:file file})] {:file file})]
(st/emit! (dm/show {:content (tr "image.loading") (st/emit! (dm/show {:content (tr "media.loading")
:type :info :type :info
:timeout nil})) :timeout nil}))

View file

@ -1166,14 +1166,14 @@
(dws/select-shapes selected)))))) (dws/select-shapes selected))))))
(defn- image-uploaded (defn- image-uploaded
[{:keys [id name] :as image}] [image]
(let [shape {:name name (let [shape {:name (:name image)
:metadata {:width (:width image) :metadata {:width (:width image)
:height (:height image) :height (:height image)
:uri (:uri image) :uri (:uri image)}}
:thumb-width (:thumb-width image) ;; :thumb-width (:thumb-width image)
:thumb-height (:thumb-height image) ;; :thumb-height (:thumb-height image)
:thumb-uri (:thumb-uri image)}} ;; :thumb-uri (:thumb-uri image)}}
aspect-ratio (/ (:width image) (:height image))] aspect-ratio (/ (:width image) (:height image))]
(st/emit! (create-and-add-shape :image shape aspect-ratio)))) (st/emit! (create-and-add-shape :image shape aspect-ratio))))
@ -1182,7 +1182,7 @@
(ptk/reify ::paste-bin-impl (ptk/reify ::paste-bin-impl
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(rx/of (dwp/upload-image image image-uploaded))))) (rx/of (dwp/upload-media-object image image-uploaded)))))
(def paste (def paste
(ptk/reify ::paste (ptk/reify ::paste
@ -1437,10 +1437,10 @@
;; Persistence ;; Persistence
(def set-file-shared dwp/set-file-shared) (def set-file-shared dwp/set-file-shared)
(def fetch-images dwp/fetch-images) (def fetch-media-objects dwp/fetch-media-objects)
(def add-image-from-url dwp/add-image-from-url) (def add-media-object-from-url dwp/add-media-object-from-url)
(def upload-image dwp/upload-image) (def upload-media-object dwp/upload-media-object)
(def delete-file-image dwp/delete-file-image) (def delete-media-object dwp/delete-media-object)
(def fetch-colors dwp/fetch-colors) (def fetch-colors dwp/fetch-colors)
(def rename-page dwp/rename-page) (def rename-page dwp/rename-page)
(def delete-page dwp/delete-page) (def delete-page dwp/delete-page)

View file

@ -298,23 +298,23 @@
;; --- Fetch Workspace Images ;; --- Fetch Workspace Images
(declare images-fetched) (declare media-objects-fetched)
(defn fetch-images (defn fetch-media-objects
[file-id] [file-id]
(ptk/reify ::fetch-images (ptk/reify ::fetch-media-objects
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(->> (rp/query :images {:file-id file-id}) (->> (rp/query :media-objects {:file-id file-id :is-local false})
(rx/map images-fetched))))) (rx/map media-objects-fetched)))))
(defn images-fetched (defn media-objects-fetched
[images] [media-objects]
(ptk/reify ::images-fetched (ptk/reify ::media-objects-fetched
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(let [images (d/index-by :id images)] (let [media-objects (d/index-by :id media-objects)]
(assoc state :workspace-images images))))) (assoc state :workspace-media media-objects)))))
;; --- Fetch Workspace Colors ;; --- Fetch Workspace Colors
@ -337,21 +337,21 @@
(assoc state :workspace-colors colors))))) (assoc state :workspace-colors colors)))))
;; --- Upload Image ;; --- Upload local media objects
(declare image-uploaded) (declare media-object-uploaded)
(def allowed-file-types #{"image/jpeg" "image/png" "image/webp" "image/svg+xml"}) (def allowed-file-types #{"image/jpeg" "image/png" "image/webp" "image/svg+xml"})
(def max-file-size (* 5 1024 1024)) (def max-file-size (* 5 1024 1024))
;; TODO: unify with create-images at main/data/images.cljs ;; TODO: unify with create-media-objects at main/data/media.cljs
;; and update-photo at main/data/users.cljs ;; and update-photo at main/data/users.cljs
;; https://tree.taiga.io/project/uxboxproject/us/440 ;; https://tree.taiga.io/project/uxboxproject/us/440
(defn add-image-from-url (defn add-media-object-from-url
([url] (add-image-from-url url identity)) ([url] (add-media-object-from-url url identity))
([url on-added] ([url on-added]
(us/verify fn? on-added) (us/verify fn? on-added)
(ptk/reify ::add-image-from-url (ptk/reify ::add-media-object-from-url
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [file-id (get-in state [:workspace-page :file-id]) (let [file-id (get-in state [:workspace-page :file-id])
@ -364,11 +364,11 @@
(.-message %) (.-message %)
(.-message %) (.-message %)
(= (:code %) :image-type-not-allowed) (= (:code %) :media-type-not-allowed)
(tr "errors.image-type-not-allowed") (tr "errors.media-type-not-allowed")
(= (:code %) :image-type-mismatch) (= (:code %) :media-type-mismatch)
(tr "errors.image-type-mismatch") (tr "errors.media-type-mismatch")
:else :else
(tr "errors.unexpected-error"))] (tr "errors.unexpected-error"))]
@ -377,23 +377,24 @@
prepare prepare
(fn [url] (fn [url]
{:file-id file-id {:file-id file-id
:url url})] :url url
:is-local true})]
(st/emit! (dm/show {:content (tr "image.loading") (st/emit! (dm/show {:content (tr "media.loading")
:type :info :type :info
:timeout nil})) :timeout nil}))
(->> (rx/of url) (->> (rx/of url)
(rx/map prepare) (rx/map prepare)
(rx/mapcat #(rp/mutation! :add-file-image-from-url %)) (rx/mapcat #(rp/mutation! :add-media-object-from-url %))
(rx/do on-success) (rx/do on-success)
(rx/map image-uploaded) (rx/map media-object-uploaded)
(rx/catch on-error))))))) (rx/catch on-error)))))))
(defn upload-image (defn upload-media-object
([file] (upload-image file identity)) ([file] (upload-media-object file identity))
([file on-uploaded] ([file on-uploaded]
(us/verify fn? on-uploaded) (us/verify fn? on-uploaded)
(ptk/reify ::upload-image (ptk/reify ::upload-media-object
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [file-id (get-in state [:workspace-page :file-id]) (let [file-id (get-in state [:workspace-page :file-id])
@ -401,9 +402,9 @@
check-file check-file
(fn [file] (fn [file]
(when (> (.-size file) max-file-size) (when (> (.-size file) max-file-size)
(throw (ex-info (tr "errors.image-too-large") {}))) (throw (ex-info (tr "errors.media-too-large") {})))
(when-not (contains? allowed-file-types (.-type file)) (when-not (contains? allowed-file-types (.-type file))
(throw (ex-info (tr "errors.image-format-unsupported") {}))) (throw (ex-info (tr "errors.media-format-unsupported") {})))
file) file)
on-success #(do (st/emit! dm/hide) on-success #(do (st/emit! dm/hide)
@ -414,11 +415,11 @@
(.-message %) (.-message %)
(.-message %) (.-message %)
(= (:code %) :image-type-not-allowed) (= (:code %) :media-type-not-allowed)
(tr "errors.image-type-not-allowed") (tr "errors.media-type-not-allowed")
(= (:code %) :image-type-mismatch) (= (:code %) :media-type-mismatch)
(tr "errors.image-type-mismatch") (tr "errors.media-type-mismatch")
:else :else
(tr "errors.unexpected-error"))] (tr "errors.unexpected-error"))]
@ -428,17 +429,18 @@
(fn [file] (fn [file]
{:name (.-name file) {:name (.-name file)
:file-id file-id :file-id file-id
:content file})] :content file
:is-local true})]
(st/emit! (dm/show {:content (tr "image.loading") (st/emit! (dm/show {:content (tr "media.loading")
:type :info :type :info
:timeout nil})) :timeout nil}))
(->> (rx/of file) (->> (rx/of file)
(rx/map check-file) (rx/map check-file)
(rx/map prepare) (rx/map prepare)
(rx/mapcat #(rp/mutation! :upload-file-image %)) (rx/mapcat #(rp/mutation! :upload-media-object %))
(rx/do on-success) (rx/do on-success)
(rx/map image-uploaded) (rx/map media-object-uploaded)
(rx/catch on-error))))))) (rx/catch on-error)))))))
@ -448,40 +450,39 @@
(s/def ::height ::us/number) (s/def ::height ::us/number)
(s/def ::mtype ::us/string) (s/def ::mtype ::us/string)
(s/def ::uri ::us/string) (s/def ::uri ::us/string)
(s/def ::thumb-uri ::us/string) ;; (s/def ::thumb-uri ::us/string)
(s/def ::image (s/def ::media-object
(s/keys :req-un [::id (s/keys :req-un [::id
::name ::name
::width ::width
::height ::height
::uri ::uri]))
::thumb-uri])) ;; ::thumb-uri]))
(defn image-uploaded (defn media-object-uploaded
[item] [item]
(us/verify ::image item) (us/verify ::media-object item)
(ptk/reify ::image-created (ptk/reify ::media-object-uploaded
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
state))) state)))
;; (update state :workspace-images assoc (:id item) item)))) ;; (update state :workspace-media assoc (:id item) item))))
;; --- Delete image ;; --- Delete media object
(defn delete-file-image (defn delete-media-object
[file-id image-id] [id]
(ptk/reify ::delete-file-image (ptk/reify ::delete-media-object
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(update state :workspace-images dissoc image-id)) (update state :workspace-media dissoc id))
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [params {:file-id file-id (let [params {:id id}]
:image-id image-id}] (rp/mutation :delete-media-object params)))))
(rp/mutation :delete-file-image params)))))
;; --- Helpers ;; --- Helpers

View file

@ -57,8 +57,8 @@
(def workspace-project (def workspace-project
(l/derived :workspace-project st/state)) (l/derived :workspace-project st/state))
(def workspace-images (def workspace-media
(l/derived :workspace-images st/state)) (l/derived :workspace-media st/state))
(def workspace-colors (def workspace-colors
(l/derived :workspace-colors st/state)) (l/derived :workspace-colors st/state))

View file

@ -68,7 +68,7 @@
(->> (http/send! {:method :post :uri uri}) (->> (http/send! {:method :post :uri uri})
(rx/mapcat handle-response)))) (rx/mapcat handle-response))))
(defmethod mutation :upload-image (defmethod mutation :upload-media-object
[id params] [id params]
(let [form (js/FormData.)] (let [form (js/FormData.)]
(run! (fn [[key val]] (run! (fn [[key val]]
@ -76,13 +76,13 @@
(seq params)) (seq params))
(send-mutation! id form))) (send-mutation! id form)))
(defmethod mutation :upload-file-image ;; (defmethod mutation :upload-file-image
[id params] ;; [id params]
(let [form (js/FormData.)] ;; (let [form (js/FormData.)]
(run! (fn [[key val]] ;; (run! (fn [[key val]]
(.append form (name key) val)) ;; (.append form (name key) val))
(seq params)) ;; (seq params))
(send-mutation! id form))) ;; (send-mutation! id form)))
(defmethod mutation :update-profile-photo (defmethod mutation :update-profile-photo
[id params] [id params]

View file

@ -8,384 +8,384 @@
;; Copyright (c) 2015-2020 Andrey Antukh <niwi@niwi.nz> ;; Copyright (c) 2015-2020 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2015-2020 Juan de la Cruz <delacruzgarciajuan@gmail.com> ;; Copyright (c) 2015-2020 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.dashboard.library ;; (ns uxbox.main.ui.dashboard.library
(:require ;; (:require
[okulary.core :as l] ;; [okulary.core :as l]
[rumext.alpha :as mf] ;; [rumext.alpha :as mf]
[cuerdas.core :as str] ;; [cuerdas.core :as str]
[uxbox.util.router :as rt] ;; [uxbox.util.router :as rt]
[uxbox.util.i18n :as i18n :refer [t tr]] ;; [uxbox.util.i18n :as i18n :refer [t tr]]
[uxbox.util.color :as uc] ;; [uxbox.util.color :as uc]
[uxbox.util.dom :as dom] ;; [uxbox.util.dom :as dom]
[uxbox.util.time :as dt] ;; [uxbox.util.time :as dt]
[uxbox.main.data.library :as dlib] ;; [uxbox.main.data.library :as dlib]
[uxbox.main.data.icons :as dico] ;; [uxbox.main.data.icons :as dico]
[uxbox.main.data.images :as dimg] ;; [uxbox.main.data.images :as dimg]
[uxbox.main.data.colors :as dcol] ;; [uxbox.main.data.colors :as dcol]
[uxbox.main.ui.icons :as i] ;; [uxbox.main.ui.icons :as i]
[uxbox.main.store :as st] ;; [uxbox.main.store :as st]
[uxbox.main.refs :as refs] ;; [uxbox.main.refs :as refs]
[uxbox.main.ui.components.context-menu :refer [context-menu]] ;; [uxbox.main.ui.components.context-menu :refer [context-menu]]
[uxbox.main.ui.components.file-uploader :refer [file-uploader]] ;; [uxbox.main.ui.components.file-uploader :refer [file-uploader]]
[uxbox.main.ui.modal :as modal] ;; [uxbox.main.ui.modal :as modal]
[uxbox.main.ui.confirm :refer [confirm-dialog]] ;; [uxbox.main.ui.confirm :refer [confirm-dialog]]
[uxbox.main.ui.colorpicker :refer [colorpicker most-used-colors]] ;; [uxbox.main.ui.colorpicker :refer [colorpicker most-used-colors]]
[uxbox.main.ui.components.editable-label :refer [editable-label]] ;; [uxbox.main.ui.components.editable-label :refer [editable-label]]
)) ;; ))
;;
(mf/defc modal-create-color ;; (mf/defc modal-create-color
[{:keys [on-accept on-cancel] :as ctx}] ;; [{:keys [on-accept on-cancel] :as ctx}]
(let [state (mf/use-state { :current-color "#406280" })] ;; (let [state (mf/use-state { :current-color "#406280" })]
(letfn [(accept [event] ;; (letfn [(accept [event]
(dom/prevent-default event) ;; (dom/prevent-default event)
(modal/hide!) ;; (modal/hide!)
(when on-accept (on-accept (:current-color @state)))) ;; (when on-accept (on-accept (:current-color @state))))
;;
(cancel [event] ;; (cancel [event]
(dom/prevent-default event) ;; (dom/prevent-default event)
(modal/hide!) ;; (modal/hide!)
(when on-cancel (on-cancel)))] ;; (when on-cancel (on-cancel)))]
[:div.modal-create-color ;; [:div.modal-create-color
[:h3.modal-create-color-title (tr "modal.create-color.new-color")] ;; [:h3.modal-create-color-title (tr "modal.create-color.new-color")]
[:& colorpicker {:value (:current-color @state) ;; [:& colorpicker {:value (:current-color @state)
:colors (into-array @most-used-colors) ;; :colors (into-array @most-used-colors)
:on-change #(swap! state assoc :current-color %)}] ;; :on-change #(swap! state assoc :current-color %)}]
;;
[:input.btn-primary {:type "button" ;; [:input.btn-primary {:type "button"
:value (tr "ds.button.save") ;; :value (tr "ds.button.save")
:on-click accept}] ;; :on-click accept}]
;;
[:a.close {:href "#" :on-click cancel} i/close]]))) ;; [:a.close {:href "#" :on-click cancel} i/close]])))
;;
(defn create-library [section team-id] ;; (defn create-library [section team-id]
(let [name (str (str (str/title (name section)) " " (gensym "Library ")))] ;; (let [name (str (str (str/title (name section)) " " (gensym "Library ")))]
(st/emit! (dlib/create-library section team-id name)))) ;; (st/emit! (dlib/create-library section team-id name))))
;;
(defmulti create-item (fn [x _ _] x)) ;; (defmulti create-item (fn [x _ _] x))
;;
(defmethod create-item :icons [_ library-id files] ;; (defmethod create-item :icons [_ library-id files]
(st/emit! (dico/create-icons library-id files))) ;; (st/emit! (dico/create-icons library-id files)))
;;
(defmethod create-item :images [_ library-id files] ;; (defmethod create-item :images [_ library-id files]
(st/emit! (dimg/create-images library-id files))) ;; (st/emit! (dimg/create-images library-id files)))
;;
(defmethod create-item :palettes [_ library-id] ;; (defmethod create-item :palettes [_ library-id]
(letfn [(dispatch-color [color] ;; (letfn [(dispatch-color [color]
(st/emit! (dcol/create-color library-id color)))] ;; (st/emit! (dcol/create-color library-id color)))]
(modal/show! modal-create-color {:on-accept dispatch-color}))) ;; (modal/show! modal-create-color {:on-accept dispatch-color})))
;;
(mf/defc library-header ;; (mf/defc library-header
[{:keys [section team-id] :as props}] ;; [{:keys [section team-id] :as props}]
(let [icons? (= section :icons) ;; (let [icons? (= section :icons)
images? (= section :images) ;; images? (= section :images)
palettes? (= section :palettes) ;; palettes? (= section :palettes)
locale (i18n/use-locale)] ;; locale (i18n/use-locale)]
[:header#main-bar.main-bar ;; [:header#main-bar.main-bar
[:h1.dashboard-title "Libraries"] ;; [:h1.dashboard-title "Libraries"]
[:nav.library-header-navigation ;; [:nav.library-header-navigation
[:a.library-header-navigation-item ;; [:a.library-header-navigation-item
{:class-name (when icons? "current") ;; {:class-name (when icons? "current")
:on-click #(st/emit! (rt/nav :dashboard-library-icons-index {:team-id team-id}))} ;; :on-click #(st/emit! (rt/nav :dashboard-library-icons-index {:team-id team-id}))}
(t locale "dashboard.library.menu.icons")] ;; (t locale "dashboard.library.menu.icons")]
[:a.library-header-navigation-item ;; [:a.library-header-navigation-item
{:class-name (when images? "current") ;; {:class-name (when images? "current")
:on-click #(st/emit! (rt/nav :dashboard-library-images-index {:team-id team-id}))} ;; :on-click #(st/emit! (rt/nav :dashboard-library-images-index {:team-id team-id}))}
(t locale "dashboard.library.menu.images")] ;; (t locale "dashboard.library.menu.images")]
[:a.library-header-navigation-item ;; [:a.library-header-navigation-item
{:class-name (when palettes? "current") ;; {:class-name (when palettes? "current")
:on-click #(st/emit! (rt/nav :dashboard-library-palettes-index {:team-id team-id}))} ;; :on-click #(st/emit! (rt/nav :dashboard-library-palettes-index {:team-id team-id}))}
(t locale "dashboard.library.menu.palettes")]]])) ;; (t locale "dashboard.library.menu.palettes")]]]))
;;
(mf/defc library-sidebar ;; (mf/defc library-sidebar
[{:keys [section items team-id library-id]}] ;; [{:keys [section items team-id library-id]}]
(let [locale (i18n/use-locale)] ;; (let [locale (i18n/use-locale)]
[:aside.library-sidebar ;; [:aside.library-sidebar
[:button.btn-primary.btn-small ;; [:button.btn-primary.btn-small
{:type "button" ;; {:type "button"
:on-click #(create-library section team-id)} ;; :on-click #(create-library section team-id)}
(t locale (str "dashboard.library.add-library." (name section)))] ;; (t locale (str "dashboard.library.add-library." (name section)))]
[:ul.library-sidebar-list ;; [:ul.library-sidebar-list
(for [item items] ;; (for [item items]
[:li.library-sidebar-list-element ;; [:li.library-sidebar-list-element
{:key (:id item) ;; {:key (:id item)
:class-name (when (= library-id (:id item)) "current") ;; :class-name (when (= library-id (:id item)) "current")
:on-click ;; :on-click
(fn [] ;; (fn []
(let [path (keyword (str "dashboard-library-" (name section)))] ;; (let [path (keyword (str "dashboard-library-" (name section)))]
(dlib/retrieve-libraries :icons (:id item)) ;; (dlib/retrieve-libraries :icons (:id item))
(st/emit! (rt/nav path {:team-id team-id :library-id (:id item)}))))} ;; (st/emit! (rt/nav path {:team-id team-id :library-id (:id item)}))))}
[:& editable-label {:value (:name item) ;; [:& editable-label {:value (:name item)
:on-change #(st/emit! (dlib/rename-library section team-id library-id %))}] ;; :on-change #(st/emit! (dlib/rename-library section team-id library-id %))}]
])]])) ;; ])]]))
;;
(mf/defc library-top-menu ;; (mf/defc library-top-menu
[{:keys [selected section library-id team-id on-delete-selected]}] ;; [{:keys [selected section library-id team-id on-delete-selected]}]
(let [state (mf/use-state {:is-open false ;; (let [state (mf/use-state {:is-open false
:editing-name false}) ;; :editing-name false})
locale (i18n/use-locale) ;; locale (i18n/use-locale)
stop-editing #(swap! state assoc :editing-name false)] ;; stop-editing #(swap! state assoc :editing-name false)]
[:header.library-top-menu ;; [:header.library-top-menu
[:div.library-top-menu-current-element ;; [:div.library-top-menu-current-element
[:& editable-label {:edit (:editing-name @state) ;; [:& editable-label {:edit (:editing-name @state)
:on-change #(do ;; :on-change #(do
(stop-editing) ;; (stop-editing)
(st/emit! (dlib/rename-library section team-id library-id %))) ;; (st/emit! (dlib/rename-library section team-id library-id %)))
:on-cancel #(swap! state assoc :editing-name false) ;; :on-cancel #(swap! state assoc :editing-name false)
:class-name "library-top-menu-current-element-name" ;; :class-name "library-top-menu-current-element-name"
:value (:name selected)}] ;; :value (:name selected)}]
[:a.library-top-menu-current-action ;; [:a.library-top-menu-current-action
{ :on-click #(swap! state update :is-open not)} ;; { :on-click #(swap! state update :is-open not)}
[:span i/arrow-down]] ;; [:span i/arrow-down]]
[:& context-menu ;; [:& context-menu
{:show (:is-open @state) ;; {:show (:is-open @state)
:on-close #(swap! state update :is-open not) ;; :on-close #(swap! state update :is-open not)
:options [[(t locale "ds.button.rename") ;; :options [[(t locale "ds.button.rename")
#(swap! state assoc :editing-name true)] ;; #(swap! state assoc :editing-name true)]
;;
[(t locale "ds.button.delete") ;; [(t locale "ds.button.delete")
(fn [] ;; (fn []
(let [path (keyword (str "dashboard-library-" (name section) "-index"))] ;; (let [path (keyword (str "dashboard-library-" (name section) "-index"))]
(modal/show! ;; (modal/show!
confirm-dialog ;; confirm-dialog
{:on-accept #(do ;; {:on-accept #(do
(st/emit! (dlib/delete-library section team-id library-id)) ;; (st/emit! (dlib/delete-library section team-id library-id))
(st/emit! (rt/nav path {:team-id team-id}))) ;; (st/emit! (rt/nav path {:team-id team-id})))
:message "Are you sure you want to delete this library?" ;; :message "Are you sure you want to delete this library?"
:accept-text "Delete"})))]]}]] ;; :accept-text "Delete"})))]]}]]
;;
[:div.library-top-menu-actions ;; [:div.library-top-menu-actions
(when on-delete-selected ;; (when on-delete-selected
[:a.library-top-menu-actions-delete ;; [:a.library-top-menu-actions-delete
{:on-click on-delete-selected} ;; {:on-click on-delete-selected}
i/trash]) ;; i/trash])
;;
(if (= section :palettes) ;; (if (= section :palettes)
[:button.btn-secondary.btn-small ;; [:button.btn-secondary.btn-small
{:on-click #(create-item section library-id)} ;; {:on-click #(create-item section library-id)}
(t locale (str "dashboard.library.add-item." (name section)))] ;; (t locale (str "dashboard.library.add-item." (name section)))]
;;
[:& file-uploader {:accept (case section ;; [:& file-uploader {:accept (case section
:images "image/jpeg,image/png,image/webp" ;; :images "image/jpeg,image/png,image/webp"
:icons "image/svg+xml" ;; :icons "image/svg+xml"
"") ;; "")
:multi true ;; :multi true
:label-text (t locale (str "dashboard.library.add-item." (name section))) ;; :label-text (t locale (str "dashboard.library.add-item." (name section)))
:label-class "btn-secondary btn-small" ;; :label-class "btn-secondary btn-small"
:input-id "file-upload" ;; :input-id "file-upload"
:on-selected #(create-item section library-id %)}])]])) ;; :on-selected #(create-item section library-id %)}])]]))
;;
(mf/defc library-icon-card ;; (mf/defc library-icon-card
[{:keys [item on-select on-unselect on-delete]}] ;; [{:keys [item on-select on-unselect on-delete]}]
(let [{:keys [id name url content metadata library-id modified-at]} item ;; (let [{:keys [id name url content metadata library-id modified-at]} item
locale (i18n/use-locale) ;; locale (i18n/use-locale)
state (mf/use-state {:is-open false :selected false}) ;; state (mf/use-state {:is-open false :selected false})
time (dt/timeago modified-at {:locale locale}) ;; time (dt/timeago modified-at {:locale locale})
handle-change (fn [] ;; handle-change (fn []
(swap! state update :selected not) ;; (swap! state update :selected not)
(if (:selected @state) ;; (if (:selected @state)
(when on-unselect (on-unselect id)) ;; (when on-unselect (on-unselect id))
(when on-select (on-select id))))] ;; (when on-select (on-select id))))]
[:div.library-card.library-icon ;; [:div.library-card.library-icon
[:div.input-checkbox.check-primary ;; [:div.input-checkbox.check-primary
[:input {:type "checkbox" ;; [:input {:type "checkbox"
:id (str "icon-" id) ;; :id (str "icon-" id)
:on-change handle-change ;; :on-change handle-change
:value (:selected @state)}] ;; :value (:selected @state)}]
[:label {:for (str "icon-" id)}]] ;; [:label {:for (str "icon-" id)}]]
[:div.library-card-image ;; [:div.library-card-image
[:svg {:view-box (->> metadata :view-box (str/join " ")) ;; [:svg {:view-box (->> metadata :view-box (str/join " "))
:width (:width metadata) ;; :width (:width metadata)
:height (:height metadata) ;; :height (:height metadata)
:dangerouslySetInnerHTML {:__html content}}]] ;; :dangerouslySetInnerHTML {:__html content}}]]
;;
[:div.library-card-footer ;; [:div.library-card-footer
[:div.library-card-footer-name name] ;; [:div.library-card-footer-name name]
[:div.library-card-footer-timestamp time] ;; [:div.library-card-footer-timestamp time]
[:div.library-card-footer-menu ;; [:div.library-card-footer-menu
{ :on-click #(swap! state update :is-open not) } ;; { :on-click #(swap! state update :is-open not) }
i/actions] ;; i/actions]
[:& context-menu ;; [:& context-menu
{:show (:is-open @state) ;; {:show (:is-open @state)
:on-close #(swap! state update :is-open not) ;; :on-close #(swap! state update :is-open not)
:options [[(t locale "ds.button.delete") ;; :options [[(t locale "ds.button.delete")
(fn [] ;; (fn []
(modal/show! ;; (modal/show!
confirm-dialog ;; confirm-dialog
{:on-accept #(do ;; {:on-accept #(do
(st/emit! (dlib/delete-item :icons library-id id)) ;; (st/emit! (dlib/delete-item :icons library-id id))
(on-delete id)) ;; (on-delete id))
:message "Are you sure you want to delete this icon?" ;; :message "Are you sure you want to delete this icon?"
:accept-text "Delete"}))]]}]]])) ;; :accept-text "Delete"}))]]}]]]))
;;
(mf/defc library-image-card ;; (mf/defc library-image-card
[{:keys [item on-select on-unselect on-delete]}] ;; [{:keys [item on-select on-unselect on-delete]}]
(let [{:keys [id name thumb-uri library-id modified-at]} item ;; (let [{:keys [id name thumb-uri library-id modified-at]} item
locale (i18n/use-locale) ;; locale (i18n/use-locale)
state (mf/use-state {:is-open false :selected false}) ;; state (mf/use-state {:is-open false :selected false})
time (dt/timeago modified-at {:locale locale}) ;; time (dt/timeago modified-at {:locale locale})
handle-change (fn [] ;; handle-change (fn []
(swap! state update :selected not) ;; (swap! state update :selected not)
(if (:selected @state) ;; (if (:selected @state)
(when on-unselect (on-unselect id)) ;; (when on-unselect (on-unselect id))
(when on-select (on-select id))))] ;; (when on-select (on-select id))))]
[:div.library-card.library-image ;; [:div.library-card.library-image
[:div.input-checkbox.check-primary ;; [:div.input-checkbox.check-primary
[:input {:type "checkbox" ;; [:input {:type "checkbox"
:id (str "image-" id) ;; :id (str "image-" id)
:on-change handle-change ;; :on-change handle-change
:value (:selected @state)}] ;; :value (:selected @state)}]
[:label {:for (str "image-" id)}]] ;; [:label {:for (str "image-" id)}]]
[:div.library-card-image ;; [:div.library-card-image
[:img {:src thumb-uri}]] ;; [:img {:src thumb-uri}]]
[:div.library-card-footer ;; [:div.library-card-footer
[:div.library-card-footer-name name] ;; [:div.library-card-footer-name name]
[:div.library-card-footer-timestamp time] ;; [:div.library-card-footer-timestamp time]
[:div.library-card-footer-menu ;; [:div.library-card-footer-menu
{ :on-click #(swap! state update :is-open not) } ;; { :on-click #(swap! state update :is-open not) }
i/actions] ;; i/actions]
[:& context-menu ;; [:& context-menu
{:show (:is-open @state) ;; {:show (:is-open @state)
:on-close #(swap! state update :is-open not) ;; :on-close #(swap! state update :is-open not)
:options [[(t locale "ds.button.delete") ;; :options [[(t locale "ds.button.delete")
(fn [] ;; (fn []
(modal/show! ;; (modal/show!
confirm-dialog ;; confirm-dialog
{:on-accept #(do ;; {:on-accept #(do
(st/emit! (dlib/delete-item :images library-id id)) ;; (st/emit! (dlib/delete-item :images library-id id))
(on-delete id)) ;; (on-delete id))
:message "Are you sure you want to delete this image?" ;; :message "Are you sure you want to delete this image?"
:accept-text "Delete"}))]]}]]])) ;; :accept-text "Delete"}))]]}]]]))
;;
(mf/defc library-color-card ;; (mf/defc library-color-card
[{:keys [item on-select on-unselect on-delete]}] ;; [{:keys [item on-select on-unselect on-delete]}]
(let [{:keys [id content library-id modified-at]} item ;; (let [{:keys [id content library-id modified-at]} item
locale (i18n/use-locale) ;; locale (i18n/use-locale)
state (mf/use-state {:is-open false :selected false}) ;; state (mf/use-state {:is-open false :selected false})
handle-change (fn [] ;; handle-change (fn []
(swap! state update :selected not) ;; (swap! state update :selected not)
(if (:selected @state) ;; (if (:selected @state)
(when on-unselect (on-unselect id)) ;; (when on-unselect (on-unselect id))
(when on-select (on-select id))))] ;; (when on-select (on-select id))))]
(when content ;; (when content
[:div.library-card.library-color ;; [:div.library-card.library-color
[:div.input-checkbox.check-primary ;; [:div.input-checkbox.check-primary
[:input {:type "checkbox" ;; [:input {:type "checkbox"
:id (str "color-" id) ;; :id (str "color-" id)
:on-change handle-change ;; :on-change handle-change
:value (:selected @state)}] ;; :value (:selected @state)}]
[:label {:for (str "color-" id)}]] ;; [:label {:for (str "color-" id)}]]
[:div.library-card-image ;; [:div.library-card-image
{ :style { :background-color content }}] ;; { :style { :background-color content }}]
[:div.library-card-footer ;; [:div.library-card-footer
[:div.library-card-footer-name content ] ;; [:div.library-card-footer-name content ]
[:div.library-card-footer-color ;; [:div.library-card-footer-color
[:span.library-card-footer-color-label "RGB"] ;; [:span.library-card-footer-color-label "RGB"]
[:span.library-card-footer-color-rgb (str/join " " (uc/hex->rgb content))]] ;; [:span.library-card-footer-color-rgb (str/join " " (uc/hex->rgb content))]]
[:div.library-card-footer-menu ;; [:div.library-card-footer-menu
{ :on-click #(swap! state update :is-open not) } ;; { :on-click #(swap! state update :is-open not) }
i/actions] ;; i/actions]
[:& context-menu ;; [:& context-menu
{:show (:is-open @state) ;; {:show (:is-open @state)
:on-close #(swap! state update :is-open not) ;; :on-close #(swap! state update :is-open not)
:options [[(t locale "ds.button.delete") ;; :options [[(t locale "ds.button.delete")
(fn [] ;; (fn []
(modal/show! ;; (modal/show!
confirm-dialog ;; confirm-dialog
{:on-accept #(do ;; {:on-accept #(do
(st/emit! (dlib/delete-item :palettes library-id id)) ;; (st/emit! (dlib/delete-item :palettes library-id id))
(on-delete id)) ;; (on-delete id))
:message "Are you sure you want to delete this color?" ;; :message "Are you sure you want to delete this color?"
:accept-text "Delete"}))]]}]]]))) ;; :accept-text "Delete"}))]]}]]])))
;;
(defn- make-libraries-ref ;; (defn- make-libraries-ref
[section team-id] ;; [section team-id]
#(-> (l/in [:library section team-id]) ;; #(-> (l/in [:library section team-id])
(l/derived st/state =))) ;; (l/derived st/state =)))
;;
(defn- make-library-items-ref ;; (defn- make-library-items-ref
[section library-id] ;; [section library-id]
#(-> (l/in [:library-items section library-id]) ;; #(-> (l/in [:library-items section library-id])
(l/derived st/state =))) ;; (l/derived st/state =)))
;;
(def last-deleted-library-ref ;; (def last-deleted-library-ref
(-> (l/in [:library :last-deleted-library]) ;; (-> (l/in [:library :last-deleted-library])
(l/derived st/state =))) ;; (l/derived st/state =)))
;;
(mf/defc library-page ;; (mf/defc library-page
[{:keys [team-id library-id section]}] ;; [{:keys [team-id library-id section]}]
(let [state (mf/use-state {:selected #{}}) ;; (let [state (mf/use-state {:selected #{}})
libs-ref (mf/use-memo ;; libs-ref (mf/use-memo
(mf/deps section team-id) ;; (mf/deps section team-id)
(make-libraries-ref section team-id)) ;; (make-libraries-ref section team-id))
;;
libraries (mf/deref libs-ref) ;; libraries (mf/deref libs-ref)
items-ref (mf/use-memo ;; items-ref (mf/use-memo
(mf/deps section library-id) ;; (mf/deps section library-id)
(make-library-items-ref section library-id)) ;; (make-library-items-ref section library-id))
;;
items (mf/deref items-ref) ;; items (mf/deref items-ref)
;;
last-deleted-library (mf/deref last-deleted-library-ref) ;; last-deleted-library (mf/deref last-deleted-library-ref)
selected-library (first (filter #(= (:id %) library-id) libraries)) ;; selected-library (first (filter #(= (:id %) library-id) libraries))
] ;; ]
;;
(mf/use-effect ;; (mf/use-effect
(mf/deps libraries) ;; (mf/deps libraries)
#(if (and (nil? library-id) (> (count libraries) 0)) ;; #(if (and (nil? library-id) (> (count libraries) 0))
(let [path (keyword (str "dashboard-library-" (name section)))] ;; (let [path (keyword (str "dashboard-library-" (name section)))]
(st/emit! (rt/nav path {:team-id team-id :library-id (:id (first libraries))}))))) ;; (st/emit! (rt/nav path {:team-id team-id :library-id (:id (first libraries))})))))
;;
(mf/use-effect ;; (mf/use-effect
(mf/deps libraries) ;; (mf/deps libraries)
#(if (and library-id (not (some (fn [{id :id}] (= library-id id)) libraries))) ;; #(if (and library-id (not (some (fn [{id :id}] (= library-id id)) libraries)))
(let [path (keyword (str "dashboard-library-" (name section) "-index"))] ;; (let [path (keyword (str "dashboard-library-" (name section) "-index"))]
(st/emit! (rt/nav path {:team-id team-id}))))) ;; (st/emit! (rt/nav path {:team-id team-id})))))
;;
(mf/use-effect ;; (mf/use-effect
(mf/deps section team-id) ;; (mf/deps section team-id)
#(st/emit! (dlib/retrieve-libraries section team-id))) ;; #(st/emit! (dlib/retrieve-libraries section team-id)))
;;
(mf/use-effect ;; (mf/use-effect
(mf/deps library-id last-deleted-library) ;; (mf/deps library-id last-deleted-library)
#(when (and library-id (not= last-deleted-library library-id)) ;; #(when (and library-id (not= last-deleted-library library-id))
(st/emit! (dlib/retrieve-library-data section library-id)))) ;; (st/emit! (dlib/retrieve-library-data section library-id))))
;;
[:div.library-page ;; [:div.library-page
[:& library-header {:section section :team-id team-id}] ;; [:& library-header {:section section :team-id team-id}]
[:& library-sidebar {:items libraries :team-id team-id :library-id library-id :section section}] ;; [:& library-sidebar {:items libraries :team-id team-id :library-id library-id :section section}]
;;
(if library-id ;; (if library-id
[:section.library-content ;; [:section.library-content
[:& library-top-menu ;; [:& library-top-menu
{:selected selected-library ;; {:selected selected-library
:section section ;; :section section
:library-id library-id ;; :library-id library-id
:team-id team-id ;; :team-id team-id
:on-delete-selected ;; :on-delete-selected
(when-not (empty? (:selected @state)) ;; (when-not (empty? (:selected @state))
(fn [] ;; (fn []
(modal/show! ;; (modal/show!
confirm-dialog ;; confirm-dialog
{:on-accept #(do (st/emit! (dlib/batch-delete-item section library-id (:selected @state))) ;; {:on-accept #(do (st/emit! (dlib/batch-delete-item section library-id (:selected @state)))
(swap! state assoc :selected #{})) ;; (swap! state assoc :selected #{}))
:message (str "Are you sure you want to delete " (-> @state :selected count) " items?") ;; :message (str "Are you sure you want to delete " (-> @state :selected count) " items?")
:accept-text "Delete"})))}] ;; :accept-text "Delete"})))}]
[:* ;; [:*
(if (> (count items) 0) ;; (if (> (count items) 0)
[:div.library-page-cards-container ;; [:div.library-page-cards-container
(for [item items] ;; (for [item items]
(let [item (assoc item :key (:id item)) ;; (let [item (assoc item :key (:id item))
props {:item item ;; props {:item item
:key (:id item) ;; :key (:id item)
:on-select #(swap! state update :selected conj %) ;; :on-select #(swap! state update :selected conj %)
:on-unselect #(swap! state update :selected disj %) ;; :on-unselect #(swap! state update :selected disj %)
:on-delete #(swap! state update :selected disj %)}] ;; :on-delete #(swap! state update :selected disj %)}]
(case section ;; (case section
:icons [:& library-icon-card props] ;; :icons [:& library-icon-card props]
:images [:& library-image-card props] ;; :images [:& library-image-card props]
:palettes [:& library-color-card props])))] ;; :palettes [:& library-color-card props])))]
[:div.library-content-empty ;; [:div.library-content-empty
[:p.library-content-empty-text "You still have no elements in this library"]])]] ;; [:p.library-content-empty-text "You still have no elements in this library"]])]]
;;
[:div.library-content-empty ;; [:div.library-content-empty
[:p.library-content-empty-text "You still have no image libraries."]])])) ;; [:p.library-content-empty-text "You still have no image libraries."]])]))

View file

@ -20,10 +20,11 @@
(let [shape (unchecked-get props "shape") (let [shape (unchecked-get props "shape")
{:keys [id x y width height rotation metadata]} shape {:keys [id x y width height rotation metadata]} shape
transform (geom/transform-matrix shape) transform (geom/transform-matrix shape)
uri (if (or (> (:thumb-width metadata) width) uri (:uri metadata)
(> (:thumb-height metadata) height)) ;; uri (if (or (> (:thumb-width metadata) width)
(:thumb-uri metadata) ;; (> (:thumb-height metadata) height))
(:uri metadata)) ;; (:thumb-uri metadata)
;; (:uri metadata))
props (-> (attrs/extract-style-attrs shape) props (-> (attrs/extract-style-attrs shape)
(obj/merge! (obj/merge!

View file

@ -36,16 +36,16 @@
(let [shape {:name name (let [shape {:name name
:metadata {:width (:width image) :metadata {:width (:width image)
:height (:height image) :height (:height image)
:uri (:uri image) :uri (:uri image)}}
:thumb-width (:thumb-width image) ;; :thumb-width (:thumb-width image)
:thumb-height (:thumb-height image) ;; :thumb-height (:thumb-height image)
:thumb-uri (:thumb-uri image)}} ;; :thumb-uri (:thumb-uri image)}}
aspect-ratio (/ (:width image) (:height image))] aspect-ratio (/ (:width image) (:height image))]
(st/emit! (dw/create-and-add-shape :image shape aspect-ratio)))) (st/emit! (dw/create-and-add-shape :image shape aspect-ratio))))
on-files-selected on-files-selected
(fn [files] (fn [files]
(run! #(st/emit! (dw/upload-image % on-uploaded)) files))] (run! #(st/emit! (dw/upload-media-object % on-uploaded)) files))]
[:aside.left-toolbar [:aside.left-toolbar
[:div.left-toolbar-inside [:div.left-toolbar-inside

View file

@ -18,7 +18,7 @@
[uxbox.common.geom.point :as gpt] [uxbox.common.geom.point :as gpt]
[uxbox.main.ui.icons :as i] [uxbox.main.ui.icons :as i]
[uxbox.main.data.workspace :as dw] [uxbox.main.data.workspace :as dw]
[uxbox.main.data.images :as di] [uxbox.main.data.media :as di]
[uxbox.main.data.colors :as dcol] [uxbox.main.data.colors :as dcol]
[uxbox.main.refs :as refs] [uxbox.main.refs :as refs]
[uxbox.main.store :as st] [uxbox.main.store :as st]
@ -72,11 +72,11 @@
[:a.close {:href "#" :on-click cancel} i/close]]))) [:a.close {:href "#" :on-click cancel} i/close]])))
(mf/defc graphics-box (mf/defc graphics-box
[{:keys [library-id images] :as props}] [{:keys [library-id media-objects] :as props}]
(let [state (mf/use-state {:menu-open false (let [state (mf/use-state {:menu-open false
:top nil :top nil
:left nil :left nil
:image-id nil}) :object-id nil})
file-input (mf/use-ref nil) file-input (mf/use-ref nil)
@ -84,14 +84,14 @@
#(dom/click (mf/ref-val file-input)) #(dom/click (mf/ref-val file-input))
delete-graphic delete-graphic
#(st/emit! (dw/delete-file-image library-id (:image-id @state))) #(st/emit! (dw/delete-media-object (:object-id @state)))
on-files-selected on-files-selected
(fn [files] (fn [files]
(st/emit! (di/create-images library-id files))) (st/emit! (di/create-media-objects library-id files)))
on-context-menu on-context-menu
(fn [image-id] (fn [object-id]
(fn [event] (fn [event]
(let [pos (dom/get-client-position event) (let [pos (dom/get-client-position event)
top (:y pos) top (:y pos)
@ -100,7 +100,7 @@
(swap! state assoc :menu-open true (swap! state assoc :menu-open true
:top top :top top
:left left :left left
:image-id image-id)))) :object-id object-id))))
on-drag-start on-drag-start
(fn [uri] (fn [uri]
@ -111,7 +111,7 @@
[:div.asset-group [:div.asset-group
[:div.group-title [:div.group-title
(tr "workspace.assets.graphics") (tr "workspace.assets.graphics")
[:span (str "\u00A0(") (count images) ")"] ;; Unicode 00A0 is non-breaking space [:span (str "\u00A0(") (count media-objects) ")"] ;; Unicode 00A0 is non-breaking space
[:div.group-button {:on-click add-graphic} [:div.group-button {:on-click add-graphic}
i/plus i/plus
[:& file-uploader {:accept "image/jpeg,image/png,image/webp,image/svg+xml" [:& file-uploader {:accept "image/jpeg,image/png,image/webp,image/svg+xml"
@ -119,14 +119,14 @@
:input-ref file-input :input-ref file-input
:on-selected on-files-selected}]]] :on-selected on-files-selected}]]]
[:div.group-grid [:div.group-grid
(for [image (sort-by :name images)] (for [object (sort-by :name media-objects)]
[:div.grid-cell {:key (:id image) [:div.grid-cell {:key (:id object)
:draggable true :draggable true
:on-context-menu (on-context-menu (:id image)) :on-context-menu (on-context-menu (:id object))
:on-drag-start (on-drag-start (:uri image))} :on-drag-start (on-drag-start (:uri object))}
[:img {:src (:thumb-uri image) [:img {:src (:thumb-uri object)
:draggable false}] ;; Also need to add css pointer-events: none :draggable false}] ;; Also need to add css pointer-events: none
[:div.cell-name (:name image)]]) [:div.cell-name (:name object)]])
[:& context-menu [:& context-menu
{:selectable false {:selectable false
:show (:menu-open @state) :show (:menu-open @state)
@ -256,7 +256,7 @@
(mf/defc library-toolbox (mf/defc library-toolbox
[{:keys [library-id [{:keys [library-id
shared? shared?
images media-objects
colors colors
initial-open? initial-open?
search-term search-term
@ -274,12 +274,12 @@
[:span.tool-badge (tr "workspace.assets.shared")])] [:span.tool-badge (tr "workspace.assets.shared")])]
(when @open? (when @open?
(let [show-graphics (and (or (= box-filter :all) (= box-filter :graphics)) (let [show-graphics (and (or (= box-filter :all) (= box-filter :graphics))
(or (> (count images) 0) (str/empty? search-term))) (or (> (count media-objects) 0) (str/empty? search-term)))
show-colors (and (or (= box-filter :all) (= box-filter :colors)) show-colors (and (or (= box-filter :all) (= box-filter :colors))
(or (> (count colors) 0) (str/empty? search-term)))] (or (> (count colors) 0) (str/empty? search-term)))]
[:div.tool-window-content [:div.tool-window-content
(when show-graphics (when show-graphics
[:& graphics-box {:library-id library-id :images images}]) [:& graphics-box {:library-id library-id :media-objects media-objects}])
(when show-colors (when show-colors
[:& colors-box {:library-id library-id :colors colors}]) [:& colors-box {:library-id library-id :colors colors}])
(when (and (not show-graphics) (not show-colors)) (when (and (not show-graphics) (not show-colors))
@ -291,14 +291,14 @@
(let [team-id (-> refs/workspace-project mf/deref :team-id) (let [team-id (-> refs/workspace-project mf/deref :team-id)
file (mf/deref refs/workspace-file) file (mf/deref refs/workspace-file)
file-id (:id file) file-id (:id file)
file-images (mf/deref refs/workspace-images) file-media (mf/deref refs/workspace-media)
file-colors (mf/deref refs/workspace-colors) file-colors (mf/deref refs/workspace-colors)
state (mf/use-state {:search-term "" state (mf/use-state {:search-term ""
:box-filter :all}) :box-filter :all})
filtered-images (filter #(matches-search (:name %) (:search-term @state)) filtered-media-objects (filter #(matches-search (:name %) (:search-term @state))
(vals file-images)) (vals file-media))
filtered-colors (filter #(or (matches-search (:name %) (:search-term @state)) filtered-colors (filter #(or (matches-search (:name %) (:search-term @state))
(matches-search (:content %) (:search-term @state))) (matches-search (:content %) (:search-term @state)))
@ -321,7 +321,7 @@
(mf/use-effect (mf/use-effect
(mf/deps file-id) (mf/deps file-id)
#(when file-id #(when file-id
(st/emit! (dw/fetch-images file-id)) (st/emit! (dw/fetch-media-objects file-id))
(st/emit! (dw/fetch-colors file-id)))) (st/emit! (dw/fetch-colors file-id))))
[:div.assets-bar [:div.assets-bar
@ -352,7 +352,7 @@
[:& library-toolbox {:library-id file-id [:& library-toolbox {:library-id file-id
:shared? (:is-shared file) :shared? (:is-shared file)
:images filtered-images :media-objects filtered-media-objects
:colors filtered-colors :colors filtered-colors
:initial-open? true :initial-open? true
:search-term (:search-term @state) :search-term (:search-term @state)

View file

@ -7,181 +7,181 @@
;; ;;
;; Copyright (c) 2020 UXBOX Labs SL ;; Copyright (c) 2020 UXBOX Labs SL
(ns uxbox.main.ui.workspace.sidebar.libraries ;; (ns uxbox.main.ui.workspace.sidebar.libraries
(:require ;; (:require
[okulary.core :as l] ;; [okulary.core :as l]
[cuerdas.core :as str] ;; [cuerdas.core :as str]
[rumext.alpha :as mf] ;; [rumext.alpha :as mf]
[uxbox.common.data :as d] ;; [uxbox.common.data :as d]
[uxbox.common.pages :as cp] ;; [uxbox.common.pages :as cp]
[uxbox.common.geom.shapes :as geom] ;; [uxbox.common.geom.shapes :as geom]
[uxbox.common.geom.point :as gpt] ;; [uxbox.common.geom.point :as gpt]
[uxbox.main.ui.icons :as i] ;; [uxbox.main.ui.icons :as i]
[uxbox.main.data.workspace :as dw] ;; [uxbox.main.data.workspace :as dw]
[uxbox.main.refs :as refs] ;; [uxbox.main.refs :as refs]
[uxbox.main.store :as st] ;; [uxbox.main.store :as st]
[uxbox.main.ui.keyboard :as kbd] ;; [uxbox.main.ui.keyboard :as kbd]
[uxbox.main.ui.shapes.icon :as icon] ;; [uxbox.main.ui.shapes.icon :as icon]
[uxbox.util.dom :as dom] ;; [uxbox.util.dom :as dom]
[uxbox.util.dom.dnd :as dnd] ;; [uxbox.util.dom.dnd :as dnd]
[uxbox.util.timers :as timers] ;; [uxbox.util.timers :as timers]
[uxbox.common.uuid :as uuid] ;; [uxbox.common.uuid :as uuid]
[uxbox.util.i18n :as i18n :refer [tr]] ;; [uxbox.util.i18n :as i18n :refer [tr]]
[uxbox.util.data :refer [classnames]] ;; [uxbox.util.data :refer [classnames]]
[uxbox.main.ui.components.tab-container :refer [tab-container tab-element]] ;; [uxbox.main.ui.components.tab-container :refer [tab-container tab-element]]
[uxbox.main.data.library :as dlib] ;; [uxbox.main.data.library :as dlib]
[uxbox.main.ui.components.context-menu :refer [context-menu]])) ;; [uxbox.main.ui.components.context-menu :refer [context-menu]]))
;;
;; --- Refs ;; ;; --- Refs
;;
(defn libraries-ref [section] ;; (defn libraries-ref [section]
(-> (l/in [:library section]) ;; (-> (l/in [:library section])
(l/derived st/state))) ;; (l/derived st/state)))
;;
(defn selected-items-ref [section library-id] ;; (defn selected-items-ref [section library-id]
(-> (l/in [:library-items section library-id]) ;; (-> (l/in [:library-items section library-id])
(l/derived st/state))) ;; (l/derived st/state)))
;;
(defn selected-library-ref [section] ;; (defn selected-library-ref [section]
(-> (l/in [:library-selected section]) ;; (-> (l/in [:library-selected section])
(l/derived st/state))) ;; (l/derived st/state)))
;;
(defn selected-filter-ref [section] ;; (defn selected-filter-ref [section]
(-> (l/in [:library-filter section]) ;; (-> (l/in [:library-filter section])
(l/derived st/state))) ;; (l/derived st/state)))
;;
(defmulti shape-from-item (fn [type _] type)) ;; (defmulti shape-from-item (fn [type _] type))
;;
(defmethod shape-from-item :icons [_ item] ;; (defmethod shape-from-item :icons [_ item]
(-> (cp/make-minimal-shape :icon) ;; (-> (cp/make-minimal-shape :icon)
(merge item) ;; (merge item)
(geom/resize (-> item :metadata :width) (-> item :metadata :height)) ;; (geom/resize (-> item :metadata :width) (-> item :metadata :height))
(geom/absolute-move (gpt/point 0 0)))) ;; (geom/absolute-move (gpt/point 0 0))))
;;
(defmethod shape-from-item :images [_ item] ;; (defmethod shape-from-item :images [_ item]
(let [metadata (select-keys item [:width :height :thumb-width ;; (let [metadata (select-keys item [:width :height :thumb-width
:thumb-height :thumb-uri :uri])] ;; :thumb-height :thumb-uri :uri])]
(-> (cp/make-minimal-shape :image) ;; (-> (cp/make-minimal-shape :image)
(merge item) ;; (merge item)
(assoc :metadata metadata) ;; (assoc :metadata metadata)
(geom/resize (-> item :width) (-> item :height)) ;; (geom/resize (-> item :width) (-> item :height))
(geom/absolute-move (gpt/point 0 0))))) ;; (geom/absolute-move (gpt/point 0 0)))))
;;
;; --- Components ;; ;; --- Components
;;
(mf/defc library-tab [{:keys [libraries section]}] ;; (mf/defc library-tab [{:keys [libraries section]}]
(when (and libraries (-> libraries count (> 0))) ;; (when (and libraries (-> libraries count (> 0)))
(let [state (mf/use-state {:drag-style false}) ;; (let [state (mf/use-state {:drag-style false})
first-id (-> libraries first :id) ;; first-id (-> libraries first :id)
current-selection (or (mf/deref (selected-library-ref section)) first-id)] ;; current-selection (or (mf/deref (selected-library-ref section)) first-id)]
;;
;; Check if the current selection is in the list of libraries ;; ;; Check if the current selection is in the list of libraries
(mf/use-effect ;; (mf/use-effect
(mf/deps libraries) ;; (mf/deps libraries)
#(when (not (some (fn [it] (= current-selection (-> it :id))) libraries)) ;; #(when (not (some (fn [it] (= current-selection (-> it :id))) libraries))
(st/emit! (dlib/select-library section first-id)))) ;; (st/emit! (dlib/select-library section first-id))))
;;
;; Retrieve the library data given the current selected library ;; ;; Retrieve the library data given the current selected library
(mf/use-effect ;; (mf/use-effect
(mf/deps current-selection) ;; (mf/deps current-selection)
#(st/emit! (dlib/retrieve-library-data section current-selection))) ;; #(st/emit! (dlib/retrieve-library-data section current-selection)))
;;
[:div.library-tab ;; [:div.library-tab
{:class (classnames :icons-tab (= section :icons) ;; {:class (classnames :icons-tab (= section :icons)
:images-tab (= section :images))} ;; :images-tab (= section :images))}
[:select.input-select.library-tab-libraries ;; [:select.input-select.library-tab-libraries
{:value current-selection ;; {:value current-selection
:on-change #(st/emit! (dlib/select-library section (-> % dom/get-target dom/get-value uuid)))} ;; :on-change #(st/emit! (dlib/select-library section (-> % dom/get-target dom/get-value uuid)))}
(for [library libraries] ;; (for [library libraries]
[:option.library-tab-libraries-item ;; [:option.library-tab-libraries-item
{:key (:id library) ;; {:key (:id library)
:value (:id library)} ;; :value (:id library)}
(:name library)])] ;; (:name library)])]
[:div.library-tab-content ;; [:div.library-tab-content
(let [items (mf/deref (selected-items-ref section current-selection))] ;; (let [items (mf/deref (selected-items-ref section current-selection))]
(for [item items] ;; (for [item items]
[:div.library-tab-element ;; [:div.library-tab-element
{:draggable true ;; {:draggable true
:class (classnames :is-dragging (:drag-style @state)) ;; :class (classnames :is-dragging (:drag-style @state))
:key (str (:id item)) ;; :key (str (:id item))
:on-drag-start (fn [event] ;; :on-drag-start (fn [event]
(swap! state assoc :drag-style true) ;; (swap! state assoc :drag-style true)
(dnd/set-data! event "uxbox/shape" (shape-from-item section item)) ;; (dnd/set-data! event "uxbox/shape" (shape-from-item section item))
(dnd/set-allowed-effect! event "move") ;; (dnd/set-allowed-effect! event "move")
;; This state is so we can give custom css to the dragging ;; ;; This state is so we can give custom css to the dragging
(timers/schedule #(swap! state assoc :drag-style false)))} ;; (timers/schedule #(swap! state assoc :drag-style false)))}
(if (= section :icons) ;; (if (= section :icons)
[:svg {:view-box (->> item :metadata :view-box (str/join " ")) ;; [:svg {:view-box (->> item :metadata :view-box (str/join " "))
:width (-> item :metadata :width) ;; :width (-> item :metadata :width)
:height (-> item :metadata :height) ;; :height (-> item :metadata :height)
:dangerouslySetInnerHTML {:__html (:content item)}}] ;; :dangerouslySetInnerHTML {:__html (:content item)}}]
[:img {:draggable false ;; [:img {:draggable false
:src (:thumb-uri item)}]) ;; :src (:thumb-uri item)}])
[:span.library-tab-element-name (:name item)]]))]]))) ;; [:span.library-tab-element-name (:name item)]]))]])))
;;
(mf/defc libraries-toolbox ;; (mf/defc libraries-toolbox
[{:keys [key]}] ;; [{:keys [key]}]
(let [state (mf/use-state {:menu-open false}) ;; (let [state (mf/use-state {:menu-open false})
selected-filter (fn [section] (or (mf/deref (selected-filter-ref section)) :all)) ;; selected-filter (fn [section] (or (mf/deref (selected-filter-ref section)) :all))
team-id (-> refs/workspace-project mf/deref :team-id) ;; team-id (-> refs/workspace-project mf/deref :team-id)
;;
filter-to-str {:all (tr "workspace.library.all") ;; filter-to-str {:all (tr "workspace.library.all")
:own (tr "workspace.library.own") ;; :own (tr "workspace.library.own")
:store (tr "workspace.library.store")} ;; :store (tr "workspace.library.store")}
;;
select-option ;; select-option
(fn [option] ;; (fn [option]
(st/emit! ;; (st/emit!
(dlib/change-library-filter :icons option) ;; (dlib/change-library-filter :icons option)
(dlib/change-library-filter :images option))) ;; (dlib/change-library-filter :images option)))
;;
filter-libraries ;; filter-libraries
(fn [section libraries] ;; (fn [section libraries]
(case (selected-filter section) ;; (case (selected-filter section)
:all (-> libraries vals flatten) ;; :all (-> libraries vals flatten)
:own (libraries team-id) ;; :own (libraries team-id)
:store (libraries uuid/zero))) ;; :store (libraries uuid/zero)))
;;
get-libraries ;; get-libraries
(fn [section] (->> (libraries-ref section) ;; (fn [section] (->> (libraries-ref section)
mf/deref ;; mf/deref
(filter-libraries section)))] ;; (filter-libraries section)))]
;;
(mf/use-effect ;; (mf/use-effect
(mf/deps team-id) ;; (mf/deps team-id)
#(when team-id ;; #(when team-id
(st/emit! (dlib/retrieve-libraries :icons) ;; (st/emit! (dlib/retrieve-libraries :icons)
(dlib/retrieve-libraries :images) ;; (dlib/retrieve-libraries :images)
(dlib/retrieve-libraries :icons team-id) ;; (dlib/retrieve-libraries :icons team-id)
(dlib/retrieve-libraries :images team-id)))) ;; (dlib/retrieve-libraries :images team-id))))
;;
[:div#libraries.tool-window ;; [:div#libraries.tool-window
[:div.libraries-window-bar ;; [:div.libraries-window-bar
[:div.libraries-window-bar-title (tr "workspace.library.libraries")] ;; [:div.libraries-window-bar-title (tr "workspace.library.libraries")]
[:div.libraries-window-bar-options ;; [:div.libraries-window-bar-options
{:on-click #(swap! state assoc :menu-open true)} ;; {:on-click #(swap! state assoc :menu-open true)}
(filter-to-str (selected-filter :icons)) ;; (filter-to-str (selected-filter :icons))
[:button ;; [:button
{ ;; {
:type "button"} ;; :type "button"}
i/arrow-slide ;; i/arrow-slide
[:& context-menu ;; [:& context-menu
{:selectable true ;; {:selectable true
:show (:menu-open @state) ;; :show (:menu-open @state)
:selected (filter-to-str (selected-filter :icons)) ;; :selected (filter-to-str (selected-filter :icons))
:on-close #(swap! state assoc :menu-open false) ;; :on-close #(swap! state assoc :menu-open false)
:options (mapv (fn [[key val]] [val #(select-option key)]) filter-to-str)}]]]] ;; :options (mapv (fn [[key val]] [val #(select-option key)]) filter-to-str)}]]]]
;;
[:div.tool-window-content ;; [:div.tool-window-content
[:& tab-container {} ;; [:& tab-container {}
[:& tab-element ;; [:& tab-element
{:id :icons :title (tr "workspace.library.icons")} ;; {:id :icons :title (tr "workspace.library.icons")}
[:& library-tab {:section :icons ;; [:& library-tab {:section :icons
:libraries (get-libraries :icons) }]] ;; :libraries (get-libraries :icons) }]]
;;
[:& tab-element ;; [:& tab-element
{:id :images :title (tr "workspace.library.images")} ;; {:id :images :title (tr "workspace.library.images")}
[:& library-tab {:section :images ;; [:& library-tab {:section :images
:libraries (get-libraries :images)}]]]]])) ;; :libraries (get-libraries :images)}]]]]]))
;;
;;

View file

@ -353,10 +353,10 @@
(let [shape {:name name (let [shape {:name name
:metadata {:width (:width image) :metadata {:width (:width image)
:height (:height image) :height (:height image)
:uri (:uri image) :uri (:uri image)}}
:thumb-width (:thumb-width image) ;; :thumb-width (:thumb-width image)
:thumb-height (:thumb-height image) ;; :thumb-height (:thumb-height image)
:thumb-uri (:thumb-uri image)}} ;; :thumb-uri (:thumb-uri image)}}
aspect-ratio (/ (:width image) (:height image))] aspect-ratio (/ (:width image) (:height image))]
(st/emit! (dw/create-and-add-shape :image shape aspect-ratio)))) (st/emit! (dw/create-and-add-shape :image shape aspect-ratio))))
@ -381,11 +381,11 @@
urls (filter #(and (not (str/blank? %)) urls (filter #(and (not (str/blank? %))
(not (str/starts-with? % "#"))) (not (str/starts-with? % "#")))
lines)] lines)]
(run! #(st/emit! (dw/add-image-from-url % on-uploaded)) urls)) (run! #(st/emit! (dw/add-media-object-from-url % on-uploaded)) urls))
:else :else
(let [files (dnd/get-files event)] (let [files (dnd/get-files event)]
(run! #(st/emit! (dw/upload-image % on-uploaded)) files)))) (run! #(st/emit! (dw/upload-media-object % on-uploaded)) files))))
on-resize on-resize
(fn [event] (fn [event]