mirror of
https://github.com/penpot/penpot.git
synced 2025-03-18 10:41:29 -05:00
♻️ The big media refactor (frontend)
This commit is contained in:
parent
bd7114182f
commit
05d54e4bea
17 changed files with 833 additions and 827 deletions
|
@ -140,7 +140,7 @@
|
|||
(when (and (string? mtype)
|
||||
(not= mtype mtype'))
|
||||
(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."))
|
||||
{:width (.getImageWidth instance)
|
||||
:height (.getImageHeight instance)
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
(declare create-file)
|
||||
(declare create-page)
|
||||
|
||||
(s/def ::is-shared boolean?)
|
||||
(s/def ::is-shared ::us/boolean)
|
||||
(s/def ::create-file
|
||||
(s/keys :req-un [::profile-id ::name ::project-id ::is-shared]
|
||||
:opt-un [::id]))
|
||||
|
@ -188,7 +188,7 @@
|
|||
;; [conn {:keys [content file-id name] :as params}]
|
||||
;; (when-not (imgs/valid-image-types? (:content-type content))
|
||||
;; (ex/raise :type :validation
|
||||
;; :code :image-type-not-allowed
|
||||
;; :code :media-type-not-allowed
|
||||
;; :hint "Seems like you are uploading an invalid image."))
|
||||
;;
|
||||
;; (let [info (images/run {:cmd :info :input {:path (:tempfile content)
|
||||
|
|
|
@ -25,8 +25,8 @@
|
|||
[uxbox.util.time :as dt]))
|
||||
|
||||
(def thumbnail-options
|
||||
{:width 800
|
||||
:height 800
|
||||
{:width 100
|
||||
:height 100
|
||||
:quality 85
|
||||
:format :jpeg})
|
||||
|
||||
|
@ -122,7 +122,7 @@
|
|||
|
||||
(s/def ::content ::upload)
|
||||
|
||||
(s/def ::is-local boolean?)
|
||||
(s/def ::is-local ::us/boolean)
|
||||
|
||||
(s/def ::add-media-object-from-url
|
||||
(s/keys :req-un [::profile-id ::file-id ::url ::is-local]
|
||||
|
@ -179,18 +179,18 @@
|
|||
:width (:width info)
|
||||
:height (:height info)
|
||||
:mtype (:mtype info)})
|
||||
(media/resolve-urls :path :uri)
|
||||
(media/resolve-urls :thumb-path :thumb-uri))
|
||||
(media/resolve-urls :path :uri))
|
||||
|
||||
media-thumbnail (db/insert! conn :media-thumbnail
|
||||
{:id (uuid/next)
|
||||
:media-object-id media-object-id
|
||||
:path (str (:path thumb))
|
||||
:width (:width thumb)
|
||||
:height (:height thumb)
|
||||
:quality (:quality thumb)
|
||||
:mtype (:mtype thumb)})]
|
||||
media-object))
|
||||
media-thumbnail (-> (db/insert! conn :media-thumbnail
|
||||
{:id (uuid/next)
|
||||
:media-object-id media-object-id
|
||||
:path (str (:path thumb))
|
||||
:width (:width thumb)
|
||||
:height (:height thumb)
|
||||
:quality (:quality thumb)
|
||||
:mtype (:mtype thumb)})
|
||||
(media/resolve-urls :path :uri))]
|
||||
(assoc media-object :thumb-uri (:uri media-thumbnail))))
|
||||
|
||||
(def ^:private sql:select-file-for-update
|
||||
"select file.*,
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
(declare retrieve-media-objects)
|
||||
(declare retrieve-file)
|
||||
|
||||
(s/def ::is-local boolean?)
|
||||
(s/def ::is-local ::us/boolean)
|
||||
(s/def ::media-objects
|
||||
(s/keys :req-un [::profile-id ::file-id ::is-local]))
|
||||
|
||||
|
@ -90,15 +90,18 @@
|
|||
(let [file (retrieve-file conn file-id)]
|
||||
(teams/check-read-permissions! conn profile-id (:team-id file))
|
||||
(->> (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
|
||||
"select *
|
||||
from media_object
|
||||
where deleted_at is null
|
||||
and file_id = ?
|
||||
and is_local = ?
|
||||
order by created_at desc")
|
||||
"select obj.*,
|
||||
thumb.path as thumb_path
|
||||
from media_object as obj
|
||||
inner join media_thumbnail as thumb on obj.id = thumb.media_object_id
|
||||
where obj.deleted_at is null
|
||||
and obj.file_id = ?
|
||||
and obj.is_local = ?
|
||||
order by obj.created_at desc")
|
||||
|
||||
(defn retrieve-media-objects
|
||||
[conn file-id is-local]
|
||||
|
|
|
@ -737,7 +737,7 @@
|
|||
"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" ],
|
||||
"translations" : {
|
||||
"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)."
|
||||
}
|
||||
},
|
||||
"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" ],
|
||||
"translations" : {
|
||||
"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)."
|
||||
}
|
||||
},
|
||||
"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" ],
|
||||
"translations" : {
|
||||
"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."
|
||||
}
|
||||
},
|
||||
"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" ],
|
||||
"translations" : {
|
||||
"en" : "Seems that this is not a valid image.",
|
||||
|
@ -854,7 +854,7 @@
|
|||
"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" ],
|
||||
"translations" : {
|
||||
"en" : "Loading image...",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.main.data.images
|
||||
(ns uxbox.main.data.media
|
||||
(:require
|
||||
[cljs.spec.alpha :as s]
|
||||
[cuerdas.core :as str]
|
||||
|
@ -30,35 +30,34 @@
|
|||
(s/def ::modified-at inst?)
|
||||
(s/def ::created-at inst?)
|
||||
(s/def ::mtype string?)
|
||||
(s/def ::thumbnail string?)
|
||||
;; (s/def ::thumbnail string?)
|
||||
(s/def ::id uuid?)
|
||||
(s/def ::url string?)
|
||||
(s/def ::collection-id uuid?)
|
||||
(s/def ::uri string?)
|
||||
;; (s/def ::collection-id uuid?)
|
||||
(s/def ::user-id uuid?)
|
||||
|
||||
(s/def ::collection
|
||||
(s/keys :req-un [::id
|
||||
::name
|
||||
::created-at
|
||||
::modified-at
|
||||
::user-id]))
|
||||
;; (s/def ::collection
|
||||
;; (s/keys :req-un [::id
|
||||
;; ::name
|
||||
;; ::created-at
|
||||
;; ::modified-at
|
||||
;; ::user-id]))
|
||||
|
||||
(s/def ::image
|
||||
(s/def ::media-object
|
||||
(s/keys :req-un [::id
|
||||
::name
|
||||
::width
|
||||
::height
|
||||
::mtype
|
||||
::collection-id
|
||||
::created-at
|
||||
::modified-at
|
||||
::uri
|
||||
::thumb-uri
|
||||
;; ::thumb-uri
|
||||
::user-id]))
|
||||
|
||||
;; ;; --- Initialize Collection Page
|
||||
;;
|
||||
;; (declare fetch-images)
|
||||
;; (declare fetch-media-objects)
|
||||
;;
|
||||
;; (defn initialize
|
||||
;; [collection-id]
|
||||
|
@ -66,11 +65,11 @@
|
|||
;; (ptk/reify ::initialize
|
||||
;; ptk/UpdateEvent
|
||||
;; (update [_ state]
|
||||
;; (assoc-in state [:dashboard-images :selected] #{}))
|
||||
;; (assoc-in state [:dashboard-media-objects :selected] #{}))
|
||||
;;
|
||||
;; ptk/WatchEvent
|
||||
;; (watch [_ state stream]
|
||||
;; (rx/of (fetch-images collection-id)))))
|
||||
;; (rx/of (fetch-media-objects collection-id)))))
|
||||
;;
|
||||
;; ;; --- Fetch Collections
|
||||
;;
|
||||
|
@ -80,7 +79,7 @@
|
|||
;; (ptk/reify ::fetch-collections
|
||||
;; ptk/WatchEvent
|
||||
;; (watch [_ state s]
|
||||
;; (->> (rp/query! :image-collections)
|
||||
;; (->> (rp/query! :media-object-collections)
|
||||
;; (rx/map collections-fetched)))))
|
||||
;;
|
||||
;;
|
||||
|
@ -95,7 +94,7 @@
|
|||
;; (reduce (fn [state {:keys [id user] :as item}]
|
||||
;; (let [type (if (uuid/zero? (:user-id item)) :builtin :own)
|
||||
;; item (assoc item :type type)]
|
||||
;; (assoc-in state [:images-collections id] item)))
|
||||
;; (assoc-in state [:media-objects-collections id] item)))
|
||||
;; state
|
||||
;; items))))
|
||||
;;
|
||||
|
@ -109,7 +108,7 @@
|
|||
;; ptk/WatchEvent
|
||||
;; (watch [_ state s]
|
||||
;; (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))))))
|
||||
;;
|
||||
;; ;; --- Collection Created
|
||||
|
@ -121,7 +120,7 @@
|
|||
;; ptk/UpdateEvent
|
||||
;; (update [_ state]
|
||||
;; (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
|
||||
;;
|
||||
|
@ -130,12 +129,12 @@
|
|||
;; (ptk/reify ::rename-collection
|
||||
;; ptk/UpdateEvent
|
||||
;; (update [_ state]
|
||||
;; (assoc-in state [:images-collections id :name] name))
|
||||
;; (assoc-in state [:media-objects-collections id :name] name))
|
||||
;;
|
||||
;; ptk/WatchEvent
|
||||
;; (watch [_ state s]
|
||||
;; (let [params {:id id :name name}]
|
||||
;; (->> (rp/mutation! :rename-image-collection params)
|
||||
;; (->> (rp/mutation! :rename-media-object-collection params)
|
||||
;; (rx/ignore))))))
|
||||
;;
|
||||
;; ;; --- Delete Collection
|
||||
|
@ -145,141 +144,141 @@
|
|||
;; (ptk/reify ::delete-collection
|
||||
;; ptk/UpdateEvent
|
||||
;; (update [_ state]
|
||||
;; (update state :images-collections dissoc id))
|
||||
;; (update state :media-objects-collections dissoc id))
|
||||
;;
|
||||
;; ptk/WatchEvent
|
||||
;; (watch [_ state s]
|
||||
;; (->> (rp/mutation! :delete-image-collection {:id id})
|
||||
;; (->> (rp/mutation! :delete-media-object-collection {:id id})
|
||||
;; (rx/tap on-success)
|
||||
;; (rx/ignore)))))
|
||||
;;
|
||||
;; ;; --- Update Image
|
||||
;; ;; --- Update Media object
|
||||
;;
|
||||
;; (defn persist-image
|
||||
;; (defn persist-media-object
|
||||
;; [id]
|
||||
;; (us/verify ::us/uuid id)
|
||||
;; (ptk/reify ::persist-image
|
||||
;; (ptk/reify ::persist-media-object
|
||||
;; ptk/WatchEvent
|
||||
;; (watch [_ state stream]
|
||||
;; (let [data (get-in state [:images id])]
|
||||
;; (->> (rp/mutation! :update-image data)
|
||||
;; (let [data (get-in state [:media-objects id])]
|
||||
;; (->> (rp/mutation! :update-media-object data)
|
||||
;; (rx/ignore))))))
|
||||
;;
|
||||
;; ;; --- Fetch Images
|
||||
;; ;; --- Fetch Media objects
|
||||
;;
|
||||
;; (declare images-fetched)
|
||||
;; (declare media-objects-fetched)
|
||||
;;
|
||||
;; (defn fetch-images
|
||||
;; "Fetch a list of images of the selected collection"
|
||||
;; (defn fetch-media-objects
|
||||
;; "Fetch a list of media-objects of the selected collection"
|
||||
;; [id]
|
||||
;; (us/verify ::us/uuid id)
|
||||
;; (ptk/reify ::fetch-images
|
||||
;; (ptk/reify ::fetch-media-objects
|
||||
;; ptk/WatchEvent
|
||||
;; (watch [_ state s]
|
||||
;; (let [params {:collection-id id}]
|
||||
;; (->> (rp/query! :images-by-collection params)
|
||||
;; (rx/map (partial images-fetched id)))))))
|
||||
;; (->> (rp/query! :media-objects-by-collection params)
|
||||
;; (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]
|
||||
;; (us/verify ::us/uuid collection-id)
|
||||
;; (us/verify ::images items)
|
||||
;; (ptk/reify ::images-fetched
|
||||
;; (us/verify ::media-objects items)
|
||||
;; (ptk/reify ::media-objects-fetched
|
||||
;; ptk/UpdateEvent
|
||||
;; (update [_ state]
|
||||
;; (let [images (d/index-by :id items)]
|
||||
;; (assoc state :images images)))))
|
||||
;; (let [media-objects (d/index-by :id items)]
|
||||
;; (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
|
||||
;; (watch [_ state stream]
|
||||
;; (let [existing (get-in state [:images id])]
|
||||
;; (let [existing (get-in state [:media-objects id])]
|
||||
;; (if existing
|
||||
;; (rx/empty)
|
||||
;; (->> (rp/query! :image-by-id {:id id})
|
||||
;; (rx/map image-fetched)
|
||||
;; (->> (rp/query! :media-object-by-id {:id id})
|
||||
;; (rx/map media-object-fetched)
|
||||
;; (rx/catch rp/client-error? #(rx/empty)))))))
|
||||
;;
|
||||
;; (defn fetch-image
|
||||
;; "Conditionally fetch image by its id. If image
|
||||
;; (defn fetch-media-object
|
||||
;; "Conditionally fetch media-object by its id. If media-object
|
||||
;; is already loaded, this event is noop."
|
||||
;; [id]
|
||||
;; {:pre [(uuid? id)]}
|
||||
;; (FetchImage. id))
|
||||
;; (FetchMediaObject. id))
|
||||
;;
|
||||
;; ;; --- Image Fetched
|
||||
;; ;; --- MediaObject Fetched
|
||||
;;
|
||||
;; (defrecord ImageFetched [image]
|
||||
;; (defrecord MediaObjectFetched [media-object]
|
||||
;; ptk/UpdateEvent
|
||||
;; (update [_ state]
|
||||
;; (let [id (:id image)]
|
||||
;; (update state :images assoc id image))))
|
||||
;; (let [id (:id media-object)]
|
||||
;; (update state :media-objects assoc id media-object))))
|
||||
;;
|
||||
;; (defn image-fetched
|
||||
;; [image]
|
||||
;; {:pre [(map? image)]}
|
||||
;; (ImageFetched. image))
|
||||
;; (defn media-object-fetched
|
||||
;; [media-object]
|
||||
;; {:pre [(map? media-object)]}
|
||||
;; (MediaObjectFetched. media-object))
|
||||
;;
|
||||
;; ;; --- Rename Image
|
||||
;; ;; --- Rename MediaObject
|
||||
;;
|
||||
;; (defn rename-image
|
||||
;; (defn rename-media-object
|
||||
;; [id name]
|
||||
;; (us/verify ::us/uuid id)
|
||||
;; (us/verify ::us/string name)
|
||||
;; (ptk/reify ::rename-image
|
||||
;; (ptk/reify ::rename-media-object
|
||||
;; ptk/UpdateEvent
|
||||
;; (update [_ state]
|
||||
;; (assoc-in state [:images id :name] name))
|
||||
;; (assoc-in state [:media-objects id :name] name))
|
||||
;;
|
||||
;; ptk/WatchEvent
|
||||
;; (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]
|
||||
;; (ptk/reify ::select-image
|
||||
;; (ptk/reify ::select-media-object
|
||||
;; ptk/UpdateEvent
|
||||
;; (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]
|
||||
;; (ptk/reify ::deselect-image
|
||||
;; (ptk/reify ::deselect-media-object
|
||||
;; ptk/UpdateEvent
|
||||
;; (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
|
||||
;; (ptk/reify ::deselect-all-images
|
||||
;; (def deselect-all-media-objects
|
||||
;; (ptk/reify ::deselect-all-media-objects
|
||||
;; ptk/UpdateEvent
|
||||
;; (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]
|
||||
;; (us/verify ::us/uuid id)
|
||||
;; (ptk/reify ::delete-image
|
||||
;; (ptk/reify ::delete-media-object
|
||||
;; ptk/UpdateEvent
|
||||
;; (update [_ state]
|
||||
;; (update state :images dissoc id))
|
||||
;; (update state :media-objects dissoc id))
|
||||
;;
|
||||
;; ptk/WatchEvent
|
||||
;; (watch [_ state s]
|
||||
;; (rx/merge
|
||||
;; (rx/of deselect-all-images)
|
||||
;; (->> (rp/mutation! :delete-image {:id id})
|
||||
;; (rx/of deselect-all-media-objects)
|
||||
;; (->> (rp/mutation! :delete-media-object {:id id})
|
||||
;; (rx/ignore))))))
|
||||
;;
|
||||
;; ;; --- Delete Selected
|
||||
|
@ -288,9 +287,9 @@
|
|||
;; (ptk/reify ::delete-selected
|
||||
;; ptk/WatchEvent
|
||||
;; (watch [_ state stream]
|
||||
;; (let [selected (get-in state [:dashboard-images :selected])]
|
||||
;; (let [selected (get-in state [:dashboard-media-objects :selected])]
|
||||
;; (->> (rx/from selected)
|
||||
;; (rx/map delete-image))))))
|
||||
;; (rx/map delete-media-object))))))
|
||||
;;
|
||||
;; ;; --- Update Opts (Filtering & Ordering)
|
||||
;;
|
||||
|
@ -300,48 +299,48 @@
|
|||
;; (ptk/reify ::update-opts
|
||||
;; ptk/UpdateEvent
|
||||
;; (update [_ state]
|
||||
;; (update state :dashboard-images merge
|
||||
;; (update state :dashboard-media-objects merge
|
||||
;; {:edition edition}
|
||||
;; (when order {:order order})
|
||||
;; (when filter {:filter filter})))))
|
||||
|
||||
;; --- Copy Selected Image
|
||||
;; --- Copy Selected MediaObject
|
||||
|
||||
;; (defrecord CopySelected [id]
|
||||
;; ptk/WatchEvent
|
||||
;; (watch [_ state stream]
|
||||
;; (let [selected (get-in state [:dashboard-images :selected])]
|
||||
;; (let [selected (get-in state [:dashboard-media-objects :selected])]
|
||||
;; (rx/merge
|
||||
;; (->> (rx/from selected)
|
||||
;; (rx/flat-map #(rp/mutation! :copy-image {:id % :collection-id id}))
|
||||
;; (rx/map image-created))
|
||||
;; (rx/flat-map #(rp/mutation! :copy-media-object {:id % :collection-id id}))
|
||||
;; (rx/map media-object-created))
|
||||
;; (->> (rx/from selected)
|
||||
;; (rx/map deselect-image))))))
|
||||
;; (rx/map deselect-media-object))))))
|
||||
|
||||
;; (defn copy-selected
|
||||
;; [id]
|
||||
;; {:pre [(or (uuid? id) (nil? id))]}
|
||||
;; (CopySelected. id))
|
||||
|
||||
;; --- Move Selected Image
|
||||
;; --- Move Selected MediaObject
|
||||
|
||||
;; (defrecord MoveSelected [id]
|
||||
;; ptk/UpdateEvent
|
||||
;; (update [_ state]
|
||||
;; (let [selected (get-in state [:dashboard-images :selected])]
|
||||
;; (reduce (fn [state image]
|
||||
;; (assoc-in state [:images image :collection] id))
|
||||
;; (let [selected (get-in state [:dashboard-media-objects :selected])]
|
||||
;; (reduce (fn [state media-object]
|
||||
;; (assoc-in state [:media-objects media-object :collection] id))
|
||||
;; state
|
||||
;; selected)))
|
||||
|
||||
;; ptk/WatchEvent
|
||||
;; (watch [_ state stream]
|
||||
;; (let [selected (get-in state [:dashboard-images :selected])]
|
||||
;; (let [selected (get-in state [:dashboard-media-objects :selected])]
|
||||
;; (rx/merge
|
||||
;; (->> (rx/from selected)
|
||||
;; (rx/map persist-image))
|
||||
;; (rx/map persist-media-object))
|
||||
;; (->> (rx/from selected)
|
||||
;; (rx/map deselect-image))))))
|
||||
;; (rx/map deselect-media-object))))))
|
||||
|
||||
;; (defn move-selected
|
||||
;; [id]
|
||||
|
@ -351,29 +350,30 @@
|
|||
|
||||
;;;;;;; NEW
|
||||
|
||||
;; --- Create Image
|
||||
(declare create-images-result)
|
||||
;; --- Create library Media Objects
|
||||
|
||||
(declare create-media-objects-result)
|
||||
(def allowed-file-types #{"image/jpeg" "image/png" "image/webp" "image/svg+xml"})
|
||||
(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
|
||||
;; https://tree.taiga.io/project/uxboxproject/us/440
|
||||
|
||||
(defn create-images
|
||||
([file-id files] (create-images file-id files identity))
|
||||
(defn create-media-objects
|
||||
([file-id files] (create-media-objects file-id files identity))
|
||||
([file-id files on-uploaded]
|
||||
(us/verify (s/nilable ::us/uuid) file-id)
|
||||
(us/verify fn? on-uploaded)
|
||||
(ptk/reify ::create-images
|
||||
(ptk/reify ::create-media-objects
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [check-file
|
||||
(fn [file]
|
||||
(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))
|
||||
(throw (ex-info (tr "errors.image-format-unsupported") {})))
|
||||
(throw (ex-info (tr "errors.media-format-unsupported") {})))
|
||||
file)
|
||||
|
||||
on-success #(do (st/emit! dm/hide)
|
||||
|
@ -384,11 +384,11 @@
|
|||
(.-message %)
|
||||
(.-message %)
|
||||
|
||||
(= (:code %) :image-type-not-allowed)
|
||||
(tr "errors.image-type-not-allowed")
|
||||
(= (:code %) :media-type-not-allowed)
|
||||
(tr "errors.media-type-not-allowed")
|
||||
|
||||
(= (:code %) :image-type-mismatch)
|
||||
(tr "errors.image-type-mismatch")
|
||||
(= (:code %) :media-type-mismatch)
|
||||
(tr "errors.media-type-mismatch")
|
||||
|
||||
:else
|
||||
(tr "errors.unexpected-error"))]
|
||||
|
@ -398,30 +398,31 @@
|
|||
(fn [file]
|
||||
{:name (.-name file)
|
||||
: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
|
||||
:timeout nil}))
|
||||
|
||||
(->> (rx/from files)
|
||||
(rx/map check-file)
|
||||
(rx/map prepare)
|
||||
(rx/mapcat #(rp/mutation! :upload-image %))
|
||||
(rx/mapcat #(rp/mutation! :upload-media-object %))
|
||||
(rx/reduce conj [])
|
||||
(rx/do on-success)
|
||||
(rx/mapcat identity)
|
||||
(rx/map (partial create-images-result file-id))
|
||||
(rx/map (partial create-media-objects-result file-id))
|
||||
(rx/catch on-error)))))))
|
||||
|
||||
;; --- Image Created
|
||||
;; --- Media object Created
|
||||
|
||||
(defn create-images-result
|
||||
[file-id image]
|
||||
#_(us/verify ::image image)
|
||||
(ptk/reify ::create-images-result
|
||||
(defn create-media-objects-result
|
||||
[file-id media-object]
|
||||
#_(us/verify ::media-object media-object)
|
||||
(ptk/reify ::create-media-objects-result
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc-in [:workspace-images (:id image)] image)))))
|
||||
(assoc-in [:workspace-media (:id media-object)] media-object)))))
|
||||
|
|
@ -159,8 +159,8 @@
|
|||
(def allowed-file-types #{"image/jpeg" "image/png" "image/webp"})
|
||||
(def max-file-size (* 5 1024 1024))
|
||||
|
||||
;; TODO: unify with create-images at main/data/images.cljs
|
||||
;; and upload-image at main/data/workspace/persistence.cljs
|
||||
;; TODO: unify with create-media-objects at main/data/media.cljs
|
||||
;; and upload-media-object at main/data/workspace/persistence.cljs
|
||||
;; https://tree.taiga.io/project/uxboxproject/us/440
|
||||
|
||||
(defn update-photo
|
||||
|
@ -172,9 +172,9 @@
|
|||
(let [check-file
|
||||
(fn [file]
|
||||
(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))
|
||||
(throw (ex-info (tr "errors.image-format-unsupported") {})))
|
||||
(throw (ex-info (tr "errors.media-format-unsupported") {})))
|
||||
file)
|
||||
|
||||
on-success #(do (st/emit! dm/hide))
|
||||
|
@ -184,11 +184,11 @@
|
|||
(.-message %)
|
||||
(.-message %)
|
||||
|
||||
(= (:code %) :image-type-not-allowed)
|
||||
(tr "errors.image-type-not-allowed")
|
||||
(= (:code %) :media-type-not-allowed)
|
||||
(tr "errors.media-type-not-allowed")
|
||||
|
||||
(= (:code %) :image-type-mismatch)
|
||||
(tr "errors.image-type-mismatch")
|
||||
(= (:code %) :media-type-mismatch)
|
||||
(tr "errors.media-type-mismatch")
|
||||
|
||||
:else
|
||||
(tr "errors.unexpected-error"))]
|
||||
|
@ -198,7 +198,7 @@
|
|||
(fn [file]
|
||||
{:file file})]
|
||||
|
||||
(st/emit! (dm/show {:content (tr "image.loading")
|
||||
(st/emit! (dm/show {:content (tr "media.loading")
|
||||
:type :info
|
||||
:timeout nil}))
|
||||
|
||||
|
|
|
@ -1166,14 +1166,14 @@
|
|||
(dws/select-shapes selected))))))
|
||||
|
||||
(defn- image-uploaded
|
||||
[{:keys [id name] :as image}]
|
||||
(let [shape {:name name
|
||||
[image]
|
||||
(let [shape {:name (:name image)
|
||||
:metadata {:width (:width image)
|
||||
:height (:height image)
|
||||
:uri (:uri image)
|
||||
:thumb-width (:thumb-width image)
|
||||
:thumb-height (:thumb-height image)
|
||||
:thumb-uri (:thumb-uri image)}}
|
||||
:uri (:uri image)}}
|
||||
;; :thumb-width (:thumb-width image)
|
||||
;; :thumb-height (:thumb-height image)
|
||||
;; :thumb-uri (:thumb-uri image)}}
|
||||
aspect-ratio (/ (:width image) (:height image))]
|
||||
(st/emit! (create-and-add-shape :image shape aspect-ratio))))
|
||||
|
||||
|
@ -1182,7 +1182,7 @@
|
|||
(ptk/reify ::paste-bin-impl
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(rx/of (dwp/upload-image image image-uploaded)))))
|
||||
(rx/of (dwp/upload-media-object image image-uploaded)))))
|
||||
|
||||
(def paste
|
||||
(ptk/reify ::paste
|
||||
|
@ -1437,10 +1437,10 @@
|
|||
;; Persistence
|
||||
|
||||
(def set-file-shared dwp/set-file-shared)
|
||||
(def fetch-images dwp/fetch-images)
|
||||
(def add-image-from-url dwp/add-image-from-url)
|
||||
(def upload-image dwp/upload-image)
|
||||
(def delete-file-image dwp/delete-file-image)
|
||||
(def fetch-media-objects dwp/fetch-media-objects)
|
||||
(def add-media-object-from-url dwp/add-media-object-from-url)
|
||||
(def upload-media-object dwp/upload-media-object)
|
||||
(def delete-media-object dwp/delete-media-object)
|
||||
(def fetch-colors dwp/fetch-colors)
|
||||
(def rename-page dwp/rename-page)
|
||||
(def delete-page dwp/delete-page)
|
||||
|
|
|
@ -298,23 +298,23 @@
|
|||
|
||||
;; --- Fetch Workspace Images
|
||||
|
||||
(declare images-fetched)
|
||||
(declare media-objects-fetched)
|
||||
|
||||
(defn fetch-images
|
||||
(defn fetch-media-objects
|
||||
[file-id]
|
||||
(ptk/reify ::fetch-images
|
||||
(ptk/reify ::fetch-media-objects
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(->> (rp/query :images {:file-id file-id})
|
||||
(rx/map images-fetched)))))
|
||||
(->> (rp/query :media-objects {:file-id file-id :is-local false})
|
||||
(rx/map media-objects-fetched)))))
|
||||
|
||||
(defn images-fetched
|
||||
[images]
|
||||
(ptk/reify ::images-fetched
|
||||
(defn media-objects-fetched
|
||||
[media-objects]
|
||||
(ptk/reify ::media-objects-fetched
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [images (d/index-by :id images)]
|
||||
(assoc state :workspace-images images)))))
|
||||
(let [media-objects (d/index-by :id media-objects)]
|
||||
(assoc state :workspace-media media-objects)))))
|
||||
|
||||
;; --- Fetch Workspace Colors
|
||||
|
||||
|
@ -337,21 +337,21 @@
|
|||
(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 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
|
||||
;; https://tree.taiga.io/project/uxboxproject/us/440
|
||||
|
||||
(defn add-image-from-url
|
||||
([url] (add-image-from-url url identity))
|
||||
(defn add-media-object-from-url
|
||||
([url] (add-media-object-from-url url identity))
|
||||
([url on-added]
|
||||
(us/verify fn? on-added)
|
||||
(ptk/reify ::add-image-from-url
|
||||
(ptk/reify ::add-media-object-from-url
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [file-id (get-in state [:workspace-page :file-id])
|
||||
|
@ -364,11 +364,11 @@
|
|||
(.-message %)
|
||||
(.-message %)
|
||||
|
||||
(= (:code %) :image-type-not-allowed)
|
||||
(tr "errors.image-type-not-allowed")
|
||||
(= (:code %) :media-type-not-allowed)
|
||||
(tr "errors.media-type-not-allowed")
|
||||
|
||||
(= (:code %) :image-type-mismatch)
|
||||
(tr "errors.image-type-mismatch")
|
||||
(= (:code %) :media-type-mismatch)
|
||||
(tr "errors.media-type-mismatch")
|
||||
|
||||
:else
|
||||
(tr "errors.unexpected-error"))]
|
||||
|
@ -377,23 +377,24 @@
|
|||
prepare
|
||||
(fn [url]
|
||||
{: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
|
||||
:timeout nil}))
|
||||
(->> (rx/of url)
|
||||
(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/map image-uploaded)
|
||||
(rx/map media-object-uploaded)
|
||||
(rx/catch on-error)))))))
|
||||
|
||||
(defn upload-image
|
||||
([file] (upload-image file identity))
|
||||
(defn upload-media-object
|
||||
([file] (upload-media-object file identity))
|
||||
([file on-uploaded]
|
||||
(us/verify fn? on-uploaded)
|
||||
(ptk/reify ::upload-image
|
||||
(ptk/reify ::upload-media-object
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [file-id (get-in state [:workspace-page :file-id])
|
||||
|
@ -401,9 +402,9 @@
|
|||
check-file
|
||||
(fn [file]
|
||||
(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))
|
||||
(throw (ex-info (tr "errors.image-format-unsupported") {})))
|
||||
(throw (ex-info (tr "errors.media-format-unsupported") {})))
|
||||
file)
|
||||
|
||||
on-success #(do (st/emit! dm/hide)
|
||||
|
@ -414,11 +415,11 @@
|
|||
(.-message %)
|
||||
(.-message %)
|
||||
|
||||
(= (:code %) :image-type-not-allowed)
|
||||
(tr "errors.image-type-not-allowed")
|
||||
(= (:code %) :media-type-not-allowed)
|
||||
(tr "errors.media-type-not-allowed")
|
||||
|
||||
(= (:code %) :image-type-mismatch)
|
||||
(tr "errors.image-type-mismatch")
|
||||
(= (:code %) :media-type-mismatch)
|
||||
(tr "errors.media-type-mismatch")
|
||||
|
||||
:else
|
||||
(tr "errors.unexpected-error"))]
|
||||
|
@ -428,17 +429,18 @@
|
|||
(fn [file]
|
||||
{:name (.-name file)
|
||||
: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
|
||||
:timeout nil}))
|
||||
(->> (rx/of file)
|
||||
(rx/map check-file)
|
||||
(rx/map prepare)
|
||||
(rx/mapcat #(rp/mutation! :upload-file-image %))
|
||||
(rx/mapcat #(rp/mutation! :upload-media-object %))
|
||||
(rx/do on-success)
|
||||
(rx/map image-uploaded)
|
||||
(rx/map media-object-uploaded)
|
||||
(rx/catch on-error)))))))
|
||||
|
||||
|
||||
|
@ -448,40 +450,39 @@
|
|||
(s/def ::height ::us/number)
|
||||
(s/def ::mtype ::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
|
||||
::name
|
||||
::width
|
||||
::height
|
||||
::uri
|
||||
::thumb-uri]))
|
||||
::uri]))
|
||||
;; ::thumb-uri]))
|
||||
|
||||
(defn image-uploaded
|
||||
(defn media-object-uploaded
|
||||
[item]
|
||||
(us/verify ::image item)
|
||||
(ptk/reify ::image-created
|
||||
(us/verify ::media-object item)
|
||||
(ptk/reify ::media-object-uploaded
|
||||
ptk/UpdateEvent
|
||||
(update [_ 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
|
||||
[file-id image-id]
|
||||
(ptk/reify ::delete-file-image
|
||||
(defn delete-media-object
|
||||
[id]
|
||||
(ptk/reify ::delete-media-object
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :workspace-images dissoc image-id))
|
||||
(update state :workspace-media dissoc id))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [params {:file-id file-id
|
||||
:image-id image-id}]
|
||||
(rp/mutation :delete-file-image params)))))
|
||||
(let [params {:id id}]
|
||||
(rp/mutation :delete-media-object params)))))
|
||||
|
||||
|
||||
;; --- Helpers
|
||||
|
|
|
@ -57,8 +57,8 @@
|
|||
(def workspace-project
|
||||
(l/derived :workspace-project st/state))
|
||||
|
||||
(def workspace-images
|
||||
(l/derived :workspace-images st/state))
|
||||
(def workspace-media
|
||||
(l/derived :workspace-media st/state))
|
||||
|
||||
(def workspace-colors
|
||||
(l/derived :workspace-colors st/state))
|
||||
|
|
|
@ -68,7 +68,7 @@
|
|||
(->> (http/send! {:method :post :uri uri})
|
||||
(rx/mapcat handle-response))))
|
||||
|
||||
(defmethod mutation :upload-image
|
||||
(defmethod mutation :upload-media-object
|
||||
[id params]
|
||||
(let [form (js/FormData.)]
|
||||
(run! (fn [[key val]]
|
||||
|
@ -76,13 +76,13 @@
|
|||
(seq params))
|
||||
(send-mutation! id form)))
|
||||
|
||||
(defmethod mutation :upload-file-image
|
||||
[id params]
|
||||
(let [form (js/FormData.)]
|
||||
(run! (fn [[key val]]
|
||||
(.append form (name key) val))
|
||||
(seq params))
|
||||
(send-mutation! id form)))
|
||||
;; (defmethod mutation :upload-file-image
|
||||
;; [id params]
|
||||
;; (let [form (js/FormData.)]
|
||||
;; (run! (fn [[key val]]
|
||||
;; (.append form (name key) val))
|
||||
;; (seq params))
|
||||
;; (send-mutation! id form)))
|
||||
|
||||
(defmethod mutation :update-profile-photo
|
||||
[id params]
|
||||
|
|
|
@ -8,384 +8,384 @@
|
|||
;; Copyright (c) 2015-2020 Andrey Antukh <niwi@niwi.nz>
|
||||
;; Copyright (c) 2015-2020 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(ns uxbox.main.ui.dashboard.library
|
||||
(:require
|
||||
[okulary.core :as l]
|
||||
[rumext.alpha :as mf]
|
||||
[cuerdas.core :as str]
|
||||
[uxbox.util.router :as rt]
|
||||
[uxbox.util.i18n :as i18n :refer [t tr]]
|
||||
[uxbox.util.color :as uc]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.time :as dt]
|
||||
[uxbox.main.data.library :as dlib]
|
||||
[uxbox.main.data.icons :as dico]
|
||||
[uxbox.main.data.images :as dimg]
|
||||
[uxbox.main.data.colors :as dcol]
|
||||
[uxbox.main.ui.icons :as i]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.ui.components.context-menu :refer [context-menu]]
|
||||
[uxbox.main.ui.components.file-uploader :refer [file-uploader]]
|
||||
[uxbox.main.ui.modal :as modal]
|
||||
[uxbox.main.ui.confirm :refer [confirm-dialog]]
|
||||
[uxbox.main.ui.colorpicker :refer [colorpicker most-used-colors]]
|
||||
[uxbox.main.ui.components.editable-label :refer [editable-label]]
|
||||
))
|
||||
|
||||
(mf/defc modal-create-color
|
||||
[{:keys [on-accept on-cancel] :as ctx}]
|
||||
(let [state (mf/use-state { :current-color "#406280" })]
|
||||
(letfn [(accept [event]
|
||||
(dom/prevent-default event)
|
||||
(modal/hide!)
|
||||
(when on-accept (on-accept (:current-color @state))))
|
||||
|
||||
(cancel [event]
|
||||
(dom/prevent-default event)
|
||||
(modal/hide!)
|
||||
(when on-cancel (on-cancel)))]
|
||||
[:div.modal-create-color
|
||||
[:h3.modal-create-color-title (tr "modal.create-color.new-color")]
|
||||
[:& colorpicker {:value (:current-color @state)
|
||||
:colors (into-array @most-used-colors)
|
||||
:on-change #(swap! state assoc :current-color %)}]
|
||||
|
||||
[:input.btn-primary {:type "button"
|
||||
:value (tr "ds.button.save")
|
||||
:on-click accept}]
|
||||
|
||||
[:a.close {:href "#" :on-click cancel} i/close]])))
|
||||
|
||||
(defn create-library [section team-id]
|
||||
(let [name (str (str (str/title (name section)) " " (gensym "Library ")))]
|
||||
(st/emit! (dlib/create-library section team-id name))))
|
||||
|
||||
(defmulti create-item (fn [x _ _] x))
|
||||
|
||||
(defmethod create-item :icons [_ library-id files]
|
||||
(st/emit! (dico/create-icons library-id files)))
|
||||
|
||||
(defmethod create-item :images [_ library-id files]
|
||||
(st/emit! (dimg/create-images library-id files)))
|
||||
|
||||
(defmethod create-item :palettes [_ library-id]
|
||||
(letfn [(dispatch-color [color]
|
||||
(st/emit! (dcol/create-color library-id color)))]
|
||||
(modal/show! modal-create-color {:on-accept dispatch-color})))
|
||||
|
||||
(mf/defc library-header
|
||||
[{:keys [section team-id] :as props}]
|
||||
(let [icons? (= section :icons)
|
||||
images? (= section :images)
|
||||
palettes? (= section :palettes)
|
||||
locale (i18n/use-locale)]
|
||||
[:header#main-bar.main-bar
|
||||
[:h1.dashboard-title "Libraries"]
|
||||
[:nav.library-header-navigation
|
||||
[:a.library-header-navigation-item
|
||||
{:class-name (when icons? "current")
|
||||
:on-click #(st/emit! (rt/nav :dashboard-library-icons-index {:team-id team-id}))}
|
||||
(t locale "dashboard.library.menu.icons")]
|
||||
[:a.library-header-navigation-item
|
||||
{:class-name (when images? "current")
|
||||
:on-click #(st/emit! (rt/nav :dashboard-library-images-index {:team-id team-id}))}
|
||||
(t locale "dashboard.library.menu.images")]
|
||||
[:a.library-header-navigation-item
|
||||
{:class-name (when palettes? "current")
|
||||
:on-click #(st/emit! (rt/nav :dashboard-library-palettes-index {:team-id team-id}))}
|
||||
(t locale "dashboard.library.menu.palettes")]]]))
|
||||
|
||||
(mf/defc library-sidebar
|
||||
[{:keys [section items team-id library-id]}]
|
||||
(let [locale (i18n/use-locale)]
|
||||
[:aside.library-sidebar
|
||||
[:button.btn-primary.btn-small
|
||||
{:type "button"
|
||||
:on-click #(create-library section team-id)}
|
||||
(t locale (str "dashboard.library.add-library." (name section)))]
|
||||
[:ul.library-sidebar-list
|
||||
(for [item items]
|
||||
[:li.library-sidebar-list-element
|
||||
{:key (:id item)
|
||||
:class-name (when (= library-id (:id item)) "current")
|
||||
:on-click
|
||||
(fn []
|
||||
(let [path (keyword (str "dashboard-library-" (name section)))]
|
||||
(dlib/retrieve-libraries :icons (:id item))
|
||||
(st/emit! (rt/nav path {:team-id team-id :library-id (:id item)}))))}
|
||||
[:& editable-label {:value (:name item)
|
||||
:on-change #(st/emit! (dlib/rename-library section team-id library-id %))}]
|
||||
])]]))
|
||||
|
||||
(mf/defc library-top-menu
|
||||
[{:keys [selected section library-id team-id on-delete-selected]}]
|
||||
(let [state (mf/use-state {:is-open false
|
||||
:editing-name false})
|
||||
locale (i18n/use-locale)
|
||||
stop-editing #(swap! state assoc :editing-name false)]
|
||||
[:header.library-top-menu
|
||||
[:div.library-top-menu-current-element
|
||||
[:& editable-label {:edit (:editing-name @state)
|
||||
:on-change #(do
|
||||
(stop-editing)
|
||||
(st/emit! (dlib/rename-library section team-id library-id %)))
|
||||
:on-cancel #(swap! state assoc :editing-name false)
|
||||
:class-name "library-top-menu-current-element-name"
|
||||
:value (:name selected)}]
|
||||
[:a.library-top-menu-current-action
|
||||
{ :on-click #(swap! state update :is-open not)}
|
||||
[:span i/arrow-down]]
|
||||
[:& context-menu
|
||||
{:show (:is-open @state)
|
||||
:on-close #(swap! state update :is-open not)
|
||||
:options [[(t locale "ds.button.rename")
|
||||
#(swap! state assoc :editing-name true)]
|
||||
|
||||
[(t locale "ds.button.delete")
|
||||
(fn []
|
||||
(let [path (keyword (str "dashboard-library-" (name section) "-index"))]
|
||||
(modal/show!
|
||||
confirm-dialog
|
||||
{:on-accept #(do
|
||||
(st/emit! (dlib/delete-library section team-id library-id))
|
||||
(st/emit! (rt/nav path {:team-id team-id})))
|
||||
:message "Are you sure you want to delete this library?"
|
||||
:accept-text "Delete"})))]]}]]
|
||||
|
||||
[:div.library-top-menu-actions
|
||||
(when on-delete-selected
|
||||
[:a.library-top-menu-actions-delete
|
||||
{:on-click on-delete-selected}
|
||||
i/trash])
|
||||
|
||||
(if (= section :palettes)
|
||||
[:button.btn-secondary.btn-small
|
||||
{:on-click #(create-item section library-id)}
|
||||
(t locale (str "dashboard.library.add-item." (name section)))]
|
||||
|
||||
[:& file-uploader {:accept (case section
|
||||
:images "image/jpeg,image/png,image/webp"
|
||||
:icons "image/svg+xml"
|
||||
"")
|
||||
:multi true
|
||||
:label-text (t locale (str "dashboard.library.add-item." (name section)))
|
||||
:label-class "btn-secondary btn-small"
|
||||
:input-id "file-upload"
|
||||
:on-selected #(create-item section library-id %)}])]]))
|
||||
|
||||
(mf/defc library-icon-card
|
||||
[{:keys [item on-select on-unselect on-delete]}]
|
||||
(let [{:keys [id name url content metadata library-id modified-at]} item
|
||||
locale (i18n/use-locale)
|
||||
state (mf/use-state {:is-open false :selected false})
|
||||
time (dt/timeago modified-at {:locale locale})
|
||||
handle-change (fn []
|
||||
(swap! state update :selected not)
|
||||
(if (:selected @state)
|
||||
(when on-unselect (on-unselect id))
|
||||
(when on-select (on-select id))))]
|
||||
[:div.library-card.library-icon
|
||||
[:div.input-checkbox.check-primary
|
||||
[:input {:type "checkbox"
|
||||
:id (str "icon-" id)
|
||||
:on-change handle-change
|
||||
:value (:selected @state)}]
|
||||
[:label {:for (str "icon-" id)}]]
|
||||
[:div.library-card-image
|
||||
[:svg {:view-box (->> metadata :view-box (str/join " "))
|
||||
:width (:width metadata)
|
||||
:height (:height metadata)
|
||||
:dangerouslySetInnerHTML {:__html content}}]]
|
||||
|
||||
[:div.library-card-footer
|
||||
[:div.library-card-footer-name name]
|
||||
[:div.library-card-footer-timestamp time]
|
||||
[:div.library-card-footer-menu
|
||||
{ :on-click #(swap! state update :is-open not) }
|
||||
i/actions]
|
||||
[:& context-menu
|
||||
{:show (:is-open @state)
|
||||
:on-close #(swap! state update :is-open not)
|
||||
:options [[(t locale "ds.button.delete")
|
||||
(fn []
|
||||
(modal/show!
|
||||
confirm-dialog
|
||||
{:on-accept #(do
|
||||
(st/emit! (dlib/delete-item :icons library-id id))
|
||||
(on-delete id))
|
||||
:message "Are you sure you want to delete this icon?"
|
||||
:accept-text "Delete"}))]]}]]]))
|
||||
|
||||
(mf/defc library-image-card
|
||||
[{:keys [item on-select on-unselect on-delete]}]
|
||||
(let [{:keys [id name thumb-uri library-id modified-at]} item
|
||||
locale (i18n/use-locale)
|
||||
state (mf/use-state {:is-open false :selected false})
|
||||
time (dt/timeago modified-at {:locale locale})
|
||||
handle-change (fn []
|
||||
(swap! state update :selected not)
|
||||
(if (:selected @state)
|
||||
(when on-unselect (on-unselect id))
|
||||
(when on-select (on-select id))))]
|
||||
[:div.library-card.library-image
|
||||
[:div.input-checkbox.check-primary
|
||||
[:input {:type "checkbox"
|
||||
:id (str "image-" id)
|
||||
:on-change handle-change
|
||||
:value (:selected @state)}]
|
||||
[:label {:for (str "image-" id)}]]
|
||||
[:div.library-card-image
|
||||
[:img {:src thumb-uri}]]
|
||||
[:div.library-card-footer
|
||||
[:div.library-card-footer-name name]
|
||||
[:div.library-card-footer-timestamp time]
|
||||
[:div.library-card-footer-menu
|
||||
{ :on-click #(swap! state update :is-open not) }
|
||||
i/actions]
|
||||
[:& context-menu
|
||||
{:show (:is-open @state)
|
||||
:on-close #(swap! state update :is-open not)
|
||||
:options [[(t locale "ds.button.delete")
|
||||
(fn []
|
||||
(modal/show!
|
||||
confirm-dialog
|
||||
{:on-accept #(do
|
||||
(st/emit! (dlib/delete-item :images library-id id))
|
||||
(on-delete id))
|
||||
:message "Are you sure you want to delete this image?"
|
||||
:accept-text "Delete"}))]]}]]]))
|
||||
|
||||
(mf/defc library-color-card
|
||||
[{:keys [item on-select on-unselect on-delete]}]
|
||||
(let [{:keys [id content library-id modified-at]} item
|
||||
locale (i18n/use-locale)
|
||||
state (mf/use-state {:is-open false :selected false})
|
||||
handle-change (fn []
|
||||
(swap! state update :selected not)
|
||||
(if (:selected @state)
|
||||
(when on-unselect (on-unselect id))
|
||||
(when on-select (on-select id))))]
|
||||
(when content
|
||||
[:div.library-card.library-color
|
||||
[:div.input-checkbox.check-primary
|
||||
[:input {:type "checkbox"
|
||||
:id (str "color-" id)
|
||||
:on-change handle-change
|
||||
:value (:selected @state)}]
|
||||
[:label {:for (str "color-" id)}]]
|
||||
[:div.library-card-image
|
||||
{ :style { :background-color content }}]
|
||||
[:div.library-card-footer
|
||||
[:div.library-card-footer-name content ]
|
||||
[:div.library-card-footer-color
|
||||
[:span.library-card-footer-color-label "RGB"]
|
||||
[:span.library-card-footer-color-rgb (str/join " " (uc/hex->rgb content))]]
|
||||
[:div.library-card-footer-menu
|
||||
{ :on-click #(swap! state update :is-open not) }
|
||||
i/actions]
|
||||
[:& context-menu
|
||||
{:show (:is-open @state)
|
||||
:on-close #(swap! state update :is-open not)
|
||||
:options [[(t locale "ds.button.delete")
|
||||
(fn []
|
||||
(modal/show!
|
||||
confirm-dialog
|
||||
{:on-accept #(do
|
||||
(st/emit! (dlib/delete-item :palettes library-id id))
|
||||
(on-delete id))
|
||||
:message "Are you sure you want to delete this color?"
|
||||
:accept-text "Delete"}))]]}]]])))
|
||||
|
||||
(defn- make-libraries-ref
|
||||
[section team-id]
|
||||
#(-> (l/in [:library section team-id])
|
||||
(l/derived st/state =)))
|
||||
|
||||
(defn- make-library-items-ref
|
||||
[section library-id]
|
||||
#(-> (l/in [:library-items section library-id])
|
||||
(l/derived st/state =)))
|
||||
|
||||
(def last-deleted-library-ref
|
||||
(-> (l/in [:library :last-deleted-library])
|
||||
(l/derived st/state =)))
|
||||
|
||||
(mf/defc library-page
|
||||
[{:keys [team-id library-id section]}]
|
||||
(let [state (mf/use-state {:selected #{}})
|
||||
libs-ref (mf/use-memo
|
||||
(mf/deps section team-id)
|
||||
(make-libraries-ref section team-id))
|
||||
|
||||
libraries (mf/deref libs-ref)
|
||||
items-ref (mf/use-memo
|
||||
(mf/deps section library-id)
|
||||
(make-library-items-ref section library-id))
|
||||
|
||||
items (mf/deref items-ref)
|
||||
|
||||
last-deleted-library (mf/deref last-deleted-library-ref)
|
||||
selected-library (first (filter #(= (:id %) library-id) libraries))
|
||||
]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps libraries)
|
||||
#(if (and (nil? library-id) (> (count libraries) 0))
|
||||
(let [path (keyword (str "dashboard-library-" (name section)))]
|
||||
(st/emit! (rt/nav path {:team-id team-id :library-id (:id (first libraries))})))))
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps libraries)
|
||||
#(if (and library-id (not (some (fn [{id :id}] (= library-id id)) libraries)))
|
||||
(let [path (keyword (str "dashboard-library-" (name section) "-index"))]
|
||||
(st/emit! (rt/nav path {:team-id team-id})))))
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps section team-id)
|
||||
#(st/emit! (dlib/retrieve-libraries section team-id)))
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps library-id last-deleted-library)
|
||||
#(when (and library-id (not= last-deleted-library library-id))
|
||||
(st/emit! (dlib/retrieve-library-data section library-id))))
|
||||
|
||||
[:div.library-page
|
||||
[:& library-header {:section section :team-id team-id}]
|
||||
[:& library-sidebar {:items libraries :team-id team-id :library-id library-id :section section}]
|
||||
|
||||
(if library-id
|
||||
[:section.library-content
|
||||
[:& library-top-menu
|
||||
{:selected selected-library
|
||||
:section section
|
||||
:library-id library-id
|
||||
:team-id team-id
|
||||
:on-delete-selected
|
||||
(when-not (empty? (:selected @state))
|
||||
(fn []
|
||||
(modal/show!
|
||||
confirm-dialog
|
||||
{:on-accept #(do (st/emit! (dlib/batch-delete-item section library-id (:selected @state)))
|
||||
(swap! state assoc :selected #{}))
|
||||
:message (str "Are you sure you want to delete " (-> @state :selected count) " items?")
|
||||
:accept-text "Delete"})))}]
|
||||
[:*
|
||||
(if (> (count items) 0)
|
||||
[:div.library-page-cards-container
|
||||
(for [item items]
|
||||
(let [item (assoc item :key (:id item))
|
||||
props {:item item
|
||||
:key (:id item)
|
||||
:on-select #(swap! state update :selected conj %)
|
||||
:on-unselect #(swap! state update :selected disj %)
|
||||
:on-delete #(swap! state update :selected disj %)}]
|
||||
(case section
|
||||
:icons [:& library-icon-card props]
|
||||
:images [:& library-image-card props]
|
||||
:palettes [:& library-color-card props])))]
|
||||
[:div.library-content-empty
|
||||
[:p.library-content-empty-text "You still have no elements in this library"]])]]
|
||||
|
||||
[:div.library-content-empty
|
||||
[:p.library-content-empty-text "You still have no image libraries."]])]))
|
||||
;; (ns uxbox.main.ui.dashboard.library
|
||||
;; (:require
|
||||
;; [okulary.core :as l]
|
||||
;; [rumext.alpha :as mf]
|
||||
;; [cuerdas.core :as str]
|
||||
;; [uxbox.util.router :as rt]
|
||||
;; [uxbox.util.i18n :as i18n :refer [t tr]]
|
||||
;; [uxbox.util.color :as uc]
|
||||
;; [uxbox.util.dom :as dom]
|
||||
;; [uxbox.util.time :as dt]
|
||||
;; [uxbox.main.data.library :as dlib]
|
||||
;; [uxbox.main.data.icons :as dico]
|
||||
;; [uxbox.main.data.images :as dimg]
|
||||
;; [uxbox.main.data.colors :as dcol]
|
||||
;; [uxbox.main.ui.icons :as i]
|
||||
;; [uxbox.main.store :as st]
|
||||
;; [uxbox.main.refs :as refs]
|
||||
;; [uxbox.main.ui.components.context-menu :refer [context-menu]]
|
||||
;; [uxbox.main.ui.components.file-uploader :refer [file-uploader]]
|
||||
;; [uxbox.main.ui.modal :as modal]
|
||||
;; [uxbox.main.ui.confirm :refer [confirm-dialog]]
|
||||
;; [uxbox.main.ui.colorpicker :refer [colorpicker most-used-colors]]
|
||||
;; [uxbox.main.ui.components.editable-label :refer [editable-label]]
|
||||
;; ))
|
||||
;;
|
||||
;; (mf/defc modal-create-color
|
||||
;; [{:keys [on-accept on-cancel] :as ctx}]
|
||||
;; (let [state (mf/use-state { :current-color "#406280" })]
|
||||
;; (letfn [(accept [event]
|
||||
;; (dom/prevent-default event)
|
||||
;; (modal/hide!)
|
||||
;; (when on-accept (on-accept (:current-color @state))))
|
||||
;;
|
||||
;; (cancel [event]
|
||||
;; (dom/prevent-default event)
|
||||
;; (modal/hide!)
|
||||
;; (when on-cancel (on-cancel)))]
|
||||
;; [:div.modal-create-color
|
||||
;; [:h3.modal-create-color-title (tr "modal.create-color.new-color")]
|
||||
;; [:& colorpicker {:value (:current-color @state)
|
||||
;; :colors (into-array @most-used-colors)
|
||||
;; :on-change #(swap! state assoc :current-color %)}]
|
||||
;;
|
||||
;; [:input.btn-primary {:type "button"
|
||||
;; :value (tr "ds.button.save")
|
||||
;; :on-click accept}]
|
||||
;;
|
||||
;; [:a.close {:href "#" :on-click cancel} i/close]])))
|
||||
;;
|
||||
;; (defn create-library [section team-id]
|
||||
;; (let [name (str (str (str/title (name section)) " " (gensym "Library ")))]
|
||||
;; (st/emit! (dlib/create-library section team-id name))))
|
||||
;;
|
||||
;; (defmulti create-item (fn [x _ _] x))
|
||||
;;
|
||||
;; (defmethod create-item :icons [_ library-id files]
|
||||
;; (st/emit! (dico/create-icons library-id files)))
|
||||
;;
|
||||
;; (defmethod create-item :images [_ library-id files]
|
||||
;; (st/emit! (dimg/create-images library-id files)))
|
||||
;;
|
||||
;; (defmethod create-item :palettes [_ library-id]
|
||||
;; (letfn [(dispatch-color [color]
|
||||
;; (st/emit! (dcol/create-color library-id color)))]
|
||||
;; (modal/show! modal-create-color {:on-accept dispatch-color})))
|
||||
;;
|
||||
;; (mf/defc library-header
|
||||
;; [{:keys [section team-id] :as props}]
|
||||
;; (let [icons? (= section :icons)
|
||||
;; images? (= section :images)
|
||||
;; palettes? (= section :palettes)
|
||||
;; locale (i18n/use-locale)]
|
||||
;; [:header#main-bar.main-bar
|
||||
;; [:h1.dashboard-title "Libraries"]
|
||||
;; [:nav.library-header-navigation
|
||||
;; [:a.library-header-navigation-item
|
||||
;; {:class-name (when icons? "current")
|
||||
;; :on-click #(st/emit! (rt/nav :dashboard-library-icons-index {:team-id team-id}))}
|
||||
;; (t locale "dashboard.library.menu.icons")]
|
||||
;; [:a.library-header-navigation-item
|
||||
;; {:class-name (when images? "current")
|
||||
;; :on-click #(st/emit! (rt/nav :dashboard-library-images-index {:team-id team-id}))}
|
||||
;; (t locale "dashboard.library.menu.images")]
|
||||
;; [:a.library-header-navigation-item
|
||||
;; {:class-name (when palettes? "current")
|
||||
;; :on-click #(st/emit! (rt/nav :dashboard-library-palettes-index {:team-id team-id}))}
|
||||
;; (t locale "dashboard.library.menu.palettes")]]]))
|
||||
;;
|
||||
;; (mf/defc library-sidebar
|
||||
;; [{:keys [section items team-id library-id]}]
|
||||
;; (let [locale (i18n/use-locale)]
|
||||
;; [:aside.library-sidebar
|
||||
;; [:button.btn-primary.btn-small
|
||||
;; {:type "button"
|
||||
;; :on-click #(create-library section team-id)}
|
||||
;; (t locale (str "dashboard.library.add-library." (name section)))]
|
||||
;; [:ul.library-sidebar-list
|
||||
;; (for [item items]
|
||||
;; [:li.library-sidebar-list-element
|
||||
;; {:key (:id item)
|
||||
;; :class-name (when (= library-id (:id item)) "current")
|
||||
;; :on-click
|
||||
;; (fn []
|
||||
;; (let [path (keyword (str "dashboard-library-" (name section)))]
|
||||
;; (dlib/retrieve-libraries :icons (:id item))
|
||||
;; (st/emit! (rt/nav path {:team-id team-id :library-id (:id item)}))))}
|
||||
;; [:& editable-label {:value (:name item)
|
||||
;; :on-change #(st/emit! (dlib/rename-library section team-id library-id %))}]
|
||||
;; ])]]))
|
||||
;;
|
||||
;; (mf/defc library-top-menu
|
||||
;; [{:keys [selected section library-id team-id on-delete-selected]}]
|
||||
;; (let [state (mf/use-state {:is-open false
|
||||
;; :editing-name false})
|
||||
;; locale (i18n/use-locale)
|
||||
;; stop-editing #(swap! state assoc :editing-name false)]
|
||||
;; [:header.library-top-menu
|
||||
;; [:div.library-top-menu-current-element
|
||||
;; [:& editable-label {:edit (:editing-name @state)
|
||||
;; :on-change #(do
|
||||
;; (stop-editing)
|
||||
;; (st/emit! (dlib/rename-library section team-id library-id %)))
|
||||
;; :on-cancel #(swap! state assoc :editing-name false)
|
||||
;; :class-name "library-top-menu-current-element-name"
|
||||
;; :value (:name selected)}]
|
||||
;; [:a.library-top-menu-current-action
|
||||
;; { :on-click #(swap! state update :is-open not)}
|
||||
;; [:span i/arrow-down]]
|
||||
;; [:& context-menu
|
||||
;; {:show (:is-open @state)
|
||||
;; :on-close #(swap! state update :is-open not)
|
||||
;; :options [[(t locale "ds.button.rename")
|
||||
;; #(swap! state assoc :editing-name true)]
|
||||
;;
|
||||
;; [(t locale "ds.button.delete")
|
||||
;; (fn []
|
||||
;; (let [path (keyword (str "dashboard-library-" (name section) "-index"))]
|
||||
;; (modal/show!
|
||||
;; confirm-dialog
|
||||
;; {:on-accept #(do
|
||||
;; (st/emit! (dlib/delete-library section team-id library-id))
|
||||
;; (st/emit! (rt/nav path {:team-id team-id})))
|
||||
;; :message "Are you sure you want to delete this library?"
|
||||
;; :accept-text "Delete"})))]]}]]
|
||||
;;
|
||||
;; [:div.library-top-menu-actions
|
||||
;; (when on-delete-selected
|
||||
;; [:a.library-top-menu-actions-delete
|
||||
;; {:on-click on-delete-selected}
|
||||
;; i/trash])
|
||||
;;
|
||||
;; (if (= section :palettes)
|
||||
;; [:button.btn-secondary.btn-small
|
||||
;; {:on-click #(create-item section library-id)}
|
||||
;; (t locale (str "dashboard.library.add-item." (name section)))]
|
||||
;;
|
||||
;; [:& file-uploader {:accept (case section
|
||||
;; :images "image/jpeg,image/png,image/webp"
|
||||
;; :icons "image/svg+xml"
|
||||
;; "")
|
||||
;; :multi true
|
||||
;; :label-text (t locale (str "dashboard.library.add-item." (name section)))
|
||||
;; :label-class "btn-secondary btn-small"
|
||||
;; :input-id "file-upload"
|
||||
;; :on-selected #(create-item section library-id %)}])]]))
|
||||
;;
|
||||
;; (mf/defc library-icon-card
|
||||
;; [{:keys [item on-select on-unselect on-delete]}]
|
||||
;; (let [{:keys [id name url content metadata library-id modified-at]} item
|
||||
;; locale (i18n/use-locale)
|
||||
;; state (mf/use-state {:is-open false :selected false})
|
||||
;; time (dt/timeago modified-at {:locale locale})
|
||||
;; handle-change (fn []
|
||||
;; (swap! state update :selected not)
|
||||
;; (if (:selected @state)
|
||||
;; (when on-unselect (on-unselect id))
|
||||
;; (when on-select (on-select id))))]
|
||||
;; [:div.library-card.library-icon
|
||||
;; [:div.input-checkbox.check-primary
|
||||
;; [:input {:type "checkbox"
|
||||
;; :id (str "icon-" id)
|
||||
;; :on-change handle-change
|
||||
;; :value (:selected @state)}]
|
||||
;; [:label {:for (str "icon-" id)}]]
|
||||
;; [:div.library-card-image
|
||||
;; [:svg {:view-box (->> metadata :view-box (str/join " "))
|
||||
;; :width (:width metadata)
|
||||
;; :height (:height metadata)
|
||||
;; :dangerouslySetInnerHTML {:__html content}}]]
|
||||
;;
|
||||
;; [:div.library-card-footer
|
||||
;; [:div.library-card-footer-name name]
|
||||
;; [:div.library-card-footer-timestamp time]
|
||||
;; [:div.library-card-footer-menu
|
||||
;; { :on-click #(swap! state update :is-open not) }
|
||||
;; i/actions]
|
||||
;; [:& context-menu
|
||||
;; {:show (:is-open @state)
|
||||
;; :on-close #(swap! state update :is-open not)
|
||||
;; :options [[(t locale "ds.button.delete")
|
||||
;; (fn []
|
||||
;; (modal/show!
|
||||
;; confirm-dialog
|
||||
;; {:on-accept #(do
|
||||
;; (st/emit! (dlib/delete-item :icons library-id id))
|
||||
;; (on-delete id))
|
||||
;; :message "Are you sure you want to delete this icon?"
|
||||
;; :accept-text "Delete"}))]]}]]]))
|
||||
;;
|
||||
;; (mf/defc library-image-card
|
||||
;; [{:keys [item on-select on-unselect on-delete]}]
|
||||
;; (let [{:keys [id name thumb-uri library-id modified-at]} item
|
||||
;; locale (i18n/use-locale)
|
||||
;; state (mf/use-state {:is-open false :selected false})
|
||||
;; time (dt/timeago modified-at {:locale locale})
|
||||
;; handle-change (fn []
|
||||
;; (swap! state update :selected not)
|
||||
;; (if (:selected @state)
|
||||
;; (when on-unselect (on-unselect id))
|
||||
;; (when on-select (on-select id))))]
|
||||
;; [:div.library-card.library-image
|
||||
;; [:div.input-checkbox.check-primary
|
||||
;; [:input {:type "checkbox"
|
||||
;; :id (str "image-" id)
|
||||
;; :on-change handle-change
|
||||
;; :value (:selected @state)}]
|
||||
;; [:label {:for (str "image-" id)}]]
|
||||
;; [:div.library-card-image
|
||||
;; [:img {:src thumb-uri}]]
|
||||
;; [:div.library-card-footer
|
||||
;; [:div.library-card-footer-name name]
|
||||
;; [:div.library-card-footer-timestamp time]
|
||||
;; [:div.library-card-footer-menu
|
||||
;; { :on-click #(swap! state update :is-open not) }
|
||||
;; i/actions]
|
||||
;; [:& context-menu
|
||||
;; {:show (:is-open @state)
|
||||
;; :on-close #(swap! state update :is-open not)
|
||||
;; :options [[(t locale "ds.button.delete")
|
||||
;; (fn []
|
||||
;; (modal/show!
|
||||
;; confirm-dialog
|
||||
;; {:on-accept #(do
|
||||
;; (st/emit! (dlib/delete-item :images library-id id))
|
||||
;; (on-delete id))
|
||||
;; :message "Are you sure you want to delete this image?"
|
||||
;; :accept-text "Delete"}))]]}]]]))
|
||||
;;
|
||||
;; (mf/defc library-color-card
|
||||
;; [{:keys [item on-select on-unselect on-delete]}]
|
||||
;; (let [{:keys [id content library-id modified-at]} item
|
||||
;; locale (i18n/use-locale)
|
||||
;; state (mf/use-state {:is-open false :selected false})
|
||||
;; handle-change (fn []
|
||||
;; (swap! state update :selected not)
|
||||
;; (if (:selected @state)
|
||||
;; (when on-unselect (on-unselect id))
|
||||
;; (when on-select (on-select id))))]
|
||||
;; (when content
|
||||
;; [:div.library-card.library-color
|
||||
;; [:div.input-checkbox.check-primary
|
||||
;; [:input {:type "checkbox"
|
||||
;; :id (str "color-" id)
|
||||
;; :on-change handle-change
|
||||
;; :value (:selected @state)}]
|
||||
;; [:label {:for (str "color-" id)}]]
|
||||
;; [:div.library-card-image
|
||||
;; { :style { :background-color content }}]
|
||||
;; [:div.library-card-footer
|
||||
;; [:div.library-card-footer-name content ]
|
||||
;; [:div.library-card-footer-color
|
||||
;; [:span.library-card-footer-color-label "RGB"]
|
||||
;; [:span.library-card-footer-color-rgb (str/join " " (uc/hex->rgb content))]]
|
||||
;; [:div.library-card-footer-menu
|
||||
;; { :on-click #(swap! state update :is-open not) }
|
||||
;; i/actions]
|
||||
;; [:& context-menu
|
||||
;; {:show (:is-open @state)
|
||||
;; :on-close #(swap! state update :is-open not)
|
||||
;; :options [[(t locale "ds.button.delete")
|
||||
;; (fn []
|
||||
;; (modal/show!
|
||||
;; confirm-dialog
|
||||
;; {:on-accept #(do
|
||||
;; (st/emit! (dlib/delete-item :palettes library-id id))
|
||||
;; (on-delete id))
|
||||
;; :message "Are you sure you want to delete this color?"
|
||||
;; :accept-text "Delete"}))]]}]]])))
|
||||
;;
|
||||
;; (defn- make-libraries-ref
|
||||
;; [section team-id]
|
||||
;; #(-> (l/in [:library section team-id])
|
||||
;; (l/derived st/state =)))
|
||||
;;
|
||||
;; (defn- make-library-items-ref
|
||||
;; [section library-id]
|
||||
;; #(-> (l/in [:library-items section library-id])
|
||||
;; (l/derived st/state =)))
|
||||
;;
|
||||
;; (def last-deleted-library-ref
|
||||
;; (-> (l/in [:library :last-deleted-library])
|
||||
;; (l/derived st/state =)))
|
||||
;;
|
||||
;; (mf/defc library-page
|
||||
;; [{:keys [team-id library-id section]}]
|
||||
;; (let [state (mf/use-state {:selected #{}})
|
||||
;; libs-ref (mf/use-memo
|
||||
;; (mf/deps section team-id)
|
||||
;; (make-libraries-ref section team-id))
|
||||
;;
|
||||
;; libraries (mf/deref libs-ref)
|
||||
;; items-ref (mf/use-memo
|
||||
;; (mf/deps section library-id)
|
||||
;; (make-library-items-ref section library-id))
|
||||
;;
|
||||
;; items (mf/deref items-ref)
|
||||
;;
|
||||
;; last-deleted-library (mf/deref last-deleted-library-ref)
|
||||
;; selected-library (first (filter #(= (:id %) library-id) libraries))
|
||||
;; ]
|
||||
;;
|
||||
;; (mf/use-effect
|
||||
;; (mf/deps libraries)
|
||||
;; #(if (and (nil? library-id) (> (count libraries) 0))
|
||||
;; (let [path (keyword (str "dashboard-library-" (name section)))]
|
||||
;; (st/emit! (rt/nav path {:team-id team-id :library-id (:id (first libraries))})))))
|
||||
;;
|
||||
;; (mf/use-effect
|
||||
;; (mf/deps libraries)
|
||||
;; #(if (and library-id (not (some (fn [{id :id}] (= library-id id)) libraries)))
|
||||
;; (let [path (keyword (str "dashboard-library-" (name section) "-index"))]
|
||||
;; (st/emit! (rt/nav path {:team-id team-id})))))
|
||||
;;
|
||||
;; (mf/use-effect
|
||||
;; (mf/deps section team-id)
|
||||
;; #(st/emit! (dlib/retrieve-libraries section team-id)))
|
||||
;;
|
||||
;; (mf/use-effect
|
||||
;; (mf/deps library-id last-deleted-library)
|
||||
;; #(when (and library-id (not= last-deleted-library library-id))
|
||||
;; (st/emit! (dlib/retrieve-library-data section library-id))))
|
||||
;;
|
||||
;; [:div.library-page
|
||||
;; [:& library-header {:section section :team-id team-id}]
|
||||
;; [:& library-sidebar {:items libraries :team-id team-id :library-id library-id :section section}]
|
||||
;;
|
||||
;; (if library-id
|
||||
;; [:section.library-content
|
||||
;; [:& library-top-menu
|
||||
;; {:selected selected-library
|
||||
;; :section section
|
||||
;; :library-id library-id
|
||||
;; :team-id team-id
|
||||
;; :on-delete-selected
|
||||
;; (when-not (empty? (:selected @state))
|
||||
;; (fn []
|
||||
;; (modal/show!
|
||||
;; confirm-dialog
|
||||
;; {:on-accept #(do (st/emit! (dlib/batch-delete-item section library-id (:selected @state)))
|
||||
;; (swap! state assoc :selected #{}))
|
||||
;; :message (str "Are you sure you want to delete " (-> @state :selected count) " items?")
|
||||
;; :accept-text "Delete"})))}]
|
||||
;; [:*
|
||||
;; (if (> (count items) 0)
|
||||
;; [:div.library-page-cards-container
|
||||
;; (for [item items]
|
||||
;; (let [item (assoc item :key (:id item))
|
||||
;; props {:item item
|
||||
;; :key (:id item)
|
||||
;; :on-select #(swap! state update :selected conj %)
|
||||
;; :on-unselect #(swap! state update :selected disj %)
|
||||
;; :on-delete #(swap! state update :selected disj %)}]
|
||||
;; (case section
|
||||
;; :icons [:& library-icon-card props]
|
||||
;; :images [:& library-image-card props]
|
||||
;; :palettes [:& library-color-card props])))]
|
||||
;; [:div.library-content-empty
|
||||
;; [:p.library-content-empty-text "You still have no elements in this library"]])]]
|
||||
;;
|
||||
;; [:div.library-content-empty
|
||||
;; [:p.library-content-empty-text "You still have no image libraries."]])]))
|
||||
|
|
|
@ -20,10 +20,11 @@
|
|||
(let [shape (unchecked-get props "shape")
|
||||
{:keys [id x y width height rotation metadata]} shape
|
||||
transform (geom/transform-matrix shape)
|
||||
uri (if (or (> (:thumb-width metadata) width)
|
||||
(> (:thumb-height metadata) height))
|
||||
(:thumb-uri metadata)
|
||||
(:uri metadata))
|
||||
uri (:uri metadata)
|
||||
;; uri (if (or (> (:thumb-width metadata) width)
|
||||
;; (> (:thumb-height metadata) height))
|
||||
;; (:thumb-uri metadata)
|
||||
;; (:uri metadata))
|
||||
|
||||
props (-> (attrs/extract-style-attrs shape)
|
||||
(obj/merge!
|
||||
|
|
|
@ -36,16 +36,16 @@
|
|||
(let [shape {:name name
|
||||
:metadata {:width (:width image)
|
||||
:height (:height image)
|
||||
:uri (:uri image)
|
||||
:thumb-width (:thumb-width image)
|
||||
:thumb-height (:thumb-height image)
|
||||
:thumb-uri (:thumb-uri image)}}
|
||||
:uri (:uri image)}}
|
||||
;; :thumb-width (:thumb-width image)
|
||||
;; :thumb-height (:thumb-height image)
|
||||
;; :thumb-uri (:thumb-uri image)}}
|
||||
aspect-ratio (/ (:width image) (:height image))]
|
||||
(st/emit! (dw/create-and-add-shape :image shape aspect-ratio))))
|
||||
|
||||
on-files-selected
|
||||
(fn [files]
|
||||
(run! #(st/emit! (dw/upload-image % on-uploaded)) files))]
|
||||
(run! #(st/emit! (dw/upload-media-object % on-uploaded)) files))]
|
||||
|
||||
[:aside.left-toolbar
|
||||
[:div.left-toolbar-inside
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
[uxbox.common.geom.point :as gpt]
|
||||
[uxbox.main.ui.icons :as i]
|
||||
[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.refs :as refs]
|
||||
[uxbox.main.store :as st]
|
||||
|
@ -72,11 +72,11 @@
|
|||
[:a.close {:href "#" :on-click cancel} i/close]])))
|
||||
|
||||
(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
|
||||
:top nil
|
||||
:left nil
|
||||
:image-id nil})
|
||||
:object-id nil})
|
||||
|
||||
file-input (mf/use-ref nil)
|
||||
|
||||
|
@ -84,14 +84,14 @@
|
|||
#(dom/click (mf/ref-val file-input))
|
||||
|
||||
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
|
||||
(fn [files]
|
||||
(st/emit! (di/create-images library-id files)))
|
||||
(st/emit! (di/create-media-objects library-id files)))
|
||||
|
||||
on-context-menu
|
||||
(fn [image-id]
|
||||
(fn [object-id]
|
||||
(fn [event]
|
||||
(let [pos (dom/get-client-position event)
|
||||
top (:y pos)
|
||||
|
@ -100,7 +100,7 @@
|
|||
(swap! state assoc :menu-open true
|
||||
:top top
|
||||
:left left
|
||||
:image-id image-id))))
|
||||
:object-id object-id))))
|
||||
|
||||
on-drag-start
|
||||
(fn [uri]
|
||||
|
@ -111,7 +111,7 @@
|
|||
[:div.asset-group
|
||||
[:div.group-title
|
||||
(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}
|
||||
i/plus
|
||||
[:& file-uploader {:accept "image/jpeg,image/png,image/webp,image/svg+xml"
|
||||
|
@ -119,14 +119,14 @@
|
|||
:input-ref file-input
|
||||
:on-selected on-files-selected}]]]
|
||||
[:div.group-grid
|
||||
(for [image (sort-by :name images)]
|
||||
[:div.grid-cell {:key (:id image)
|
||||
(for [object (sort-by :name media-objects)]
|
||||
[:div.grid-cell {:key (:id object)
|
||||
:draggable true
|
||||
:on-context-menu (on-context-menu (:id image))
|
||||
:on-drag-start (on-drag-start (:uri image))}
|
||||
[:img {:src (:thumb-uri image)
|
||||
:on-context-menu (on-context-menu (:id object))
|
||||
:on-drag-start (on-drag-start (:uri object))}
|
||||
[:img {:src (:thumb-uri object)
|
||||
:draggable false}] ;; Also need to add css pointer-events: none
|
||||
[:div.cell-name (:name image)]])
|
||||
[:div.cell-name (:name object)]])
|
||||
[:& context-menu
|
||||
{:selectable false
|
||||
:show (:menu-open @state)
|
||||
|
@ -256,7 +256,7 @@
|
|||
(mf/defc library-toolbox
|
||||
[{:keys [library-id
|
||||
shared?
|
||||
images
|
||||
media-objects
|
||||
colors
|
||||
initial-open?
|
||||
search-term
|
||||
|
@ -274,12 +274,12 @@
|
|||
[:span.tool-badge (tr "workspace.assets.shared")])]
|
||||
(when @open?
|
||||
(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))
|
||||
(or (> (count colors) 0) (str/empty? search-term)))]
|
||||
[:div.tool-window-content
|
||||
(when show-graphics
|
||||
[:& graphics-box {:library-id library-id :images images}])
|
||||
[:& graphics-box {:library-id library-id :media-objects media-objects}])
|
||||
(when show-colors
|
||||
[:& colors-box {:library-id library-id :colors colors}])
|
||||
(when (and (not show-graphics) (not show-colors))
|
||||
|
@ -291,14 +291,14 @@
|
|||
(let [team-id (-> refs/workspace-project mf/deref :team-id)
|
||||
file (mf/deref refs/workspace-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)
|
||||
|
||||
state (mf/use-state {:search-term ""
|
||||
:box-filter :all})
|
||||
|
||||
filtered-images (filter #(matches-search (:name %) (:search-term @state))
|
||||
(vals file-images))
|
||||
filtered-media-objects (filter #(matches-search (:name %) (:search-term @state))
|
||||
(vals file-media))
|
||||
|
||||
filtered-colors (filter #(or (matches-search (:name %) (:search-term @state))
|
||||
(matches-search (:content %) (:search-term @state)))
|
||||
|
@ -321,7 +321,7 @@
|
|||
(mf/use-effect
|
||||
(mf/deps 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))))
|
||||
|
||||
[:div.assets-bar
|
||||
|
@ -352,7 +352,7 @@
|
|||
|
||||
[:& library-toolbox {:library-id file-id
|
||||
:shared? (:is-shared file)
|
||||
:images filtered-images
|
||||
:media-objects filtered-media-objects
|
||||
:colors filtered-colors
|
||||
:initial-open? true
|
||||
:search-term (:search-term @state)
|
||||
|
|
|
@ -7,181 +7,181 @@
|
|||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
|
||||
(ns uxbox.main.ui.workspace.sidebar.libraries
|
||||
(:require
|
||||
[okulary.core :as l]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.alpha :as mf]
|
||||
[uxbox.common.data :as d]
|
||||
[uxbox.common.pages :as cp]
|
||||
[uxbox.common.geom.shapes :as geom]
|
||||
[uxbox.common.geom.point :as gpt]
|
||||
[uxbox.main.ui.icons :as i]
|
||||
[uxbox.main.data.workspace :as dw]
|
||||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.ui.keyboard :as kbd]
|
||||
[uxbox.main.ui.shapes.icon :as icon]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.dom.dnd :as dnd]
|
||||
[uxbox.util.timers :as timers]
|
||||
[uxbox.common.uuid :as uuid]
|
||||
[uxbox.util.i18n :as i18n :refer [tr]]
|
||||
[uxbox.util.data :refer [classnames]]
|
||||
[uxbox.main.ui.components.tab-container :refer [tab-container tab-element]]
|
||||
[uxbox.main.data.library :as dlib]
|
||||
[uxbox.main.ui.components.context-menu :refer [context-menu]]))
|
||||
|
||||
;; --- Refs
|
||||
|
||||
(defn libraries-ref [section]
|
||||
(-> (l/in [:library section])
|
||||
(l/derived st/state)))
|
||||
|
||||
(defn selected-items-ref [section library-id]
|
||||
(-> (l/in [:library-items section library-id])
|
||||
(l/derived st/state)))
|
||||
|
||||
(defn selected-library-ref [section]
|
||||
(-> (l/in [:library-selected section])
|
||||
(l/derived st/state)))
|
||||
|
||||
(defn selected-filter-ref [section]
|
||||
(-> (l/in [:library-filter section])
|
||||
(l/derived st/state)))
|
||||
|
||||
(defmulti shape-from-item (fn [type _] type))
|
||||
|
||||
(defmethod shape-from-item :icons [_ item]
|
||||
(-> (cp/make-minimal-shape :icon)
|
||||
(merge item)
|
||||
(geom/resize (-> item :metadata :width) (-> item :metadata :height))
|
||||
(geom/absolute-move (gpt/point 0 0))))
|
||||
|
||||
(defmethod shape-from-item :images [_ item]
|
||||
(let [metadata (select-keys item [:width :height :thumb-width
|
||||
:thumb-height :thumb-uri :uri])]
|
||||
(-> (cp/make-minimal-shape :image)
|
||||
(merge item)
|
||||
(assoc :metadata metadata)
|
||||
(geom/resize (-> item :width) (-> item :height))
|
||||
(geom/absolute-move (gpt/point 0 0)))))
|
||||
|
||||
;; --- Components
|
||||
|
||||
(mf/defc library-tab [{:keys [libraries section]}]
|
||||
(when (and libraries (-> libraries count (> 0)))
|
||||
(let [state (mf/use-state {:drag-style false})
|
||||
first-id (-> libraries first :id)
|
||||
current-selection (or (mf/deref (selected-library-ref section)) first-id)]
|
||||
|
||||
;; Check if the current selection is in the list of libraries
|
||||
(mf/use-effect
|
||||
(mf/deps libraries)
|
||||
#(when (not (some (fn [it] (= current-selection (-> it :id))) libraries))
|
||||
(st/emit! (dlib/select-library section first-id))))
|
||||
|
||||
;; Retrieve the library data given the current selected library
|
||||
(mf/use-effect
|
||||
(mf/deps current-selection)
|
||||
#(st/emit! (dlib/retrieve-library-data section current-selection)))
|
||||
|
||||
[:div.library-tab
|
||||
{:class (classnames :icons-tab (= section :icons)
|
||||
:images-tab (= section :images))}
|
||||
[:select.input-select.library-tab-libraries
|
||||
{:value current-selection
|
||||
:on-change #(st/emit! (dlib/select-library section (-> % dom/get-target dom/get-value uuid)))}
|
||||
(for [library libraries]
|
||||
[:option.library-tab-libraries-item
|
||||
{:key (:id library)
|
||||
:value (:id library)}
|
||||
(:name library)])]
|
||||
[:div.library-tab-content
|
||||
(let [items (mf/deref (selected-items-ref section current-selection))]
|
||||
(for [item items]
|
||||
[:div.library-tab-element
|
||||
{:draggable true
|
||||
:class (classnames :is-dragging (:drag-style @state))
|
||||
:key (str (:id item))
|
||||
:on-drag-start (fn [event]
|
||||
(swap! state assoc :drag-style true)
|
||||
(dnd/set-data! event "uxbox/shape" (shape-from-item section item))
|
||||
(dnd/set-allowed-effect! event "move")
|
||||
;; This state is so we can give custom css to the dragging
|
||||
(timers/schedule #(swap! state assoc :drag-style false)))}
|
||||
(if (= section :icons)
|
||||
[:svg {:view-box (->> item :metadata :view-box (str/join " "))
|
||||
:width (-> item :metadata :width)
|
||||
:height (-> item :metadata :height)
|
||||
:dangerouslySetInnerHTML {:__html (:content item)}}]
|
||||
[:img {:draggable false
|
||||
:src (:thumb-uri item)}])
|
||||
[:span.library-tab-element-name (:name item)]]))]])))
|
||||
|
||||
(mf/defc libraries-toolbox
|
||||
[{:keys [key]}]
|
||||
(let [state (mf/use-state {:menu-open false})
|
||||
selected-filter (fn [section] (or (mf/deref (selected-filter-ref section)) :all))
|
||||
team-id (-> refs/workspace-project mf/deref :team-id)
|
||||
|
||||
filter-to-str {:all (tr "workspace.library.all")
|
||||
:own (tr "workspace.library.own")
|
||||
:store (tr "workspace.library.store")}
|
||||
|
||||
select-option
|
||||
(fn [option]
|
||||
(st/emit!
|
||||
(dlib/change-library-filter :icons option)
|
||||
(dlib/change-library-filter :images option)))
|
||||
|
||||
filter-libraries
|
||||
(fn [section libraries]
|
||||
(case (selected-filter section)
|
||||
:all (-> libraries vals flatten)
|
||||
:own (libraries team-id)
|
||||
:store (libraries uuid/zero)))
|
||||
|
||||
get-libraries
|
||||
(fn [section] (->> (libraries-ref section)
|
||||
mf/deref
|
||||
(filter-libraries section)))]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps team-id)
|
||||
#(when team-id
|
||||
(st/emit! (dlib/retrieve-libraries :icons)
|
||||
(dlib/retrieve-libraries :images)
|
||||
(dlib/retrieve-libraries :icons team-id)
|
||||
(dlib/retrieve-libraries :images team-id))))
|
||||
|
||||
[:div#libraries.tool-window
|
||||
[:div.libraries-window-bar
|
||||
[:div.libraries-window-bar-title (tr "workspace.library.libraries")]
|
||||
[:div.libraries-window-bar-options
|
||||
{:on-click #(swap! state assoc :menu-open true)}
|
||||
(filter-to-str (selected-filter :icons))
|
||||
[:button
|
||||
{
|
||||
:type "button"}
|
||||
i/arrow-slide
|
||||
[:& context-menu
|
||||
{:selectable true
|
||||
:show (:menu-open @state)
|
||||
:selected (filter-to-str (selected-filter :icons))
|
||||
:on-close #(swap! state assoc :menu-open false)
|
||||
:options (mapv (fn [[key val]] [val #(select-option key)]) filter-to-str)}]]]]
|
||||
|
||||
[:div.tool-window-content
|
||||
[:& tab-container {}
|
||||
[:& tab-element
|
||||
{:id :icons :title (tr "workspace.library.icons")}
|
||||
[:& library-tab {:section :icons
|
||||
:libraries (get-libraries :icons) }]]
|
||||
|
||||
[:& tab-element
|
||||
{:id :images :title (tr "workspace.library.images")}
|
||||
[:& library-tab {:section :images
|
||||
:libraries (get-libraries :images)}]]]]]))
|
||||
|
||||
|
||||
;; (ns uxbox.main.ui.workspace.sidebar.libraries
|
||||
;; (:require
|
||||
;; [okulary.core :as l]
|
||||
;; [cuerdas.core :as str]
|
||||
;; [rumext.alpha :as mf]
|
||||
;; [uxbox.common.data :as d]
|
||||
;; [uxbox.common.pages :as cp]
|
||||
;; [uxbox.common.geom.shapes :as geom]
|
||||
;; [uxbox.common.geom.point :as gpt]
|
||||
;; [uxbox.main.ui.icons :as i]
|
||||
;; [uxbox.main.data.workspace :as dw]
|
||||
;; [uxbox.main.refs :as refs]
|
||||
;; [uxbox.main.store :as st]
|
||||
;; [uxbox.main.ui.keyboard :as kbd]
|
||||
;; [uxbox.main.ui.shapes.icon :as icon]
|
||||
;; [uxbox.util.dom :as dom]
|
||||
;; [uxbox.util.dom.dnd :as dnd]
|
||||
;; [uxbox.util.timers :as timers]
|
||||
;; [uxbox.common.uuid :as uuid]
|
||||
;; [uxbox.util.i18n :as i18n :refer [tr]]
|
||||
;; [uxbox.util.data :refer [classnames]]
|
||||
;; [uxbox.main.ui.components.tab-container :refer [tab-container tab-element]]
|
||||
;; [uxbox.main.data.library :as dlib]
|
||||
;; [uxbox.main.ui.components.context-menu :refer [context-menu]]))
|
||||
;;
|
||||
;; ;; --- Refs
|
||||
;;
|
||||
;; (defn libraries-ref [section]
|
||||
;; (-> (l/in [:library section])
|
||||
;; (l/derived st/state)))
|
||||
;;
|
||||
;; (defn selected-items-ref [section library-id]
|
||||
;; (-> (l/in [:library-items section library-id])
|
||||
;; (l/derived st/state)))
|
||||
;;
|
||||
;; (defn selected-library-ref [section]
|
||||
;; (-> (l/in [:library-selected section])
|
||||
;; (l/derived st/state)))
|
||||
;;
|
||||
;; (defn selected-filter-ref [section]
|
||||
;; (-> (l/in [:library-filter section])
|
||||
;; (l/derived st/state)))
|
||||
;;
|
||||
;; (defmulti shape-from-item (fn [type _] type))
|
||||
;;
|
||||
;; (defmethod shape-from-item :icons [_ item]
|
||||
;; (-> (cp/make-minimal-shape :icon)
|
||||
;; (merge item)
|
||||
;; (geom/resize (-> item :metadata :width) (-> item :metadata :height))
|
||||
;; (geom/absolute-move (gpt/point 0 0))))
|
||||
;;
|
||||
;; (defmethod shape-from-item :images [_ item]
|
||||
;; (let [metadata (select-keys item [:width :height :thumb-width
|
||||
;; :thumb-height :thumb-uri :uri])]
|
||||
;; (-> (cp/make-minimal-shape :image)
|
||||
;; (merge item)
|
||||
;; (assoc :metadata metadata)
|
||||
;; (geom/resize (-> item :width) (-> item :height))
|
||||
;; (geom/absolute-move (gpt/point 0 0)))))
|
||||
;;
|
||||
;; ;; --- Components
|
||||
;;
|
||||
;; (mf/defc library-tab [{:keys [libraries section]}]
|
||||
;; (when (and libraries (-> libraries count (> 0)))
|
||||
;; (let [state (mf/use-state {:drag-style false})
|
||||
;; first-id (-> libraries first :id)
|
||||
;; current-selection (or (mf/deref (selected-library-ref section)) first-id)]
|
||||
;;
|
||||
;; ;; Check if the current selection is in the list of libraries
|
||||
;; (mf/use-effect
|
||||
;; (mf/deps libraries)
|
||||
;; #(when (not (some (fn [it] (= current-selection (-> it :id))) libraries))
|
||||
;; (st/emit! (dlib/select-library section first-id))))
|
||||
;;
|
||||
;; ;; Retrieve the library data given the current selected library
|
||||
;; (mf/use-effect
|
||||
;; (mf/deps current-selection)
|
||||
;; #(st/emit! (dlib/retrieve-library-data section current-selection)))
|
||||
;;
|
||||
;; [:div.library-tab
|
||||
;; {:class (classnames :icons-tab (= section :icons)
|
||||
;; :images-tab (= section :images))}
|
||||
;; [:select.input-select.library-tab-libraries
|
||||
;; {:value current-selection
|
||||
;; :on-change #(st/emit! (dlib/select-library section (-> % dom/get-target dom/get-value uuid)))}
|
||||
;; (for [library libraries]
|
||||
;; [:option.library-tab-libraries-item
|
||||
;; {:key (:id library)
|
||||
;; :value (:id library)}
|
||||
;; (:name library)])]
|
||||
;; [:div.library-tab-content
|
||||
;; (let [items (mf/deref (selected-items-ref section current-selection))]
|
||||
;; (for [item items]
|
||||
;; [:div.library-tab-element
|
||||
;; {:draggable true
|
||||
;; :class (classnames :is-dragging (:drag-style @state))
|
||||
;; :key (str (:id item))
|
||||
;; :on-drag-start (fn [event]
|
||||
;; (swap! state assoc :drag-style true)
|
||||
;; (dnd/set-data! event "uxbox/shape" (shape-from-item section item))
|
||||
;; (dnd/set-allowed-effect! event "move")
|
||||
;; ;; This state is so we can give custom css to the dragging
|
||||
;; (timers/schedule #(swap! state assoc :drag-style false)))}
|
||||
;; (if (= section :icons)
|
||||
;; [:svg {:view-box (->> item :metadata :view-box (str/join " "))
|
||||
;; :width (-> item :metadata :width)
|
||||
;; :height (-> item :metadata :height)
|
||||
;; :dangerouslySetInnerHTML {:__html (:content item)}}]
|
||||
;; [:img {:draggable false
|
||||
;; :src (:thumb-uri item)}])
|
||||
;; [:span.library-tab-element-name (:name item)]]))]])))
|
||||
;;
|
||||
;; (mf/defc libraries-toolbox
|
||||
;; [{:keys [key]}]
|
||||
;; (let [state (mf/use-state {:menu-open false})
|
||||
;; selected-filter (fn [section] (or (mf/deref (selected-filter-ref section)) :all))
|
||||
;; team-id (-> refs/workspace-project mf/deref :team-id)
|
||||
;;
|
||||
;; filter-to-str {:all (tr "workspace.library.all")
|
||||
;; :own (tr "workspace.library.own")
|
||||
;; :store (tr "workspace.library.store")}
|
||||
;;
|
||||
;; select-option
|
||||
;; (fn [option]
|
||||
;; (st/emit!
|
||||
;; (dlib/change-library-filter :icons option)
|
||||
;; (dlib/change-library-filter :images option)))
|
||||
;;
|
||||
;; filter-libraries
|
||||
;; (fn [section libraries]
|
||||
;; (case (selected-filter section)
|
||||
;; :all (-> libraries vals flatten)
|
||||
;; :own (libraries team-id)
|
||||
;; :store (libraries uuid/zero)))
|
||||
;;
|
||||
;; get-libraries
|
||||
;; (fn [section] (->> (libraries-ref section)
|
||||
;; mf/deref
|
||||
;; (filter-libraries section)))]
|
||||
;;
|
||||
;; (mf/use-effect
|
||||
;; (mf/deps team-id)
|
||||
;; #(when team-id
|
||||
;; (st/emit! (dlib/retrieve-libraries :icons)
|
||||
;; (dlib/retrieve-libraries :images)
|
||||
;; (dlib/retrieve-libraries :icons team-id)
|
||||
;; (dlib/retrieve-libraries :images team-id))))
|
||||
;;
|
||||
;; [:div#libraries.tool-window
|
||||
;; [:div.libraries-window-bar
|
||||
;; [:div.libraries-window-bar-title (tr "workspace.library.libraries")]
|
||||
;; [:div.libraries-window-bar-options
|
||||
;; {:on-click #(swap! state assoc :menu-open true)}
|
||||
;; (filter-to-str (selected-filter :icons))
|
||||
;; [:button
|
||||
;; {
|
||||
;; :type "button"}
|
||||
;; i/arrow-slide
|
||||
;; [:& context-menu
|
||||
;; {:selectable true
|
||||
;; :show (:menu-open @state)
|
||||
;; :selected (filter-to-str (selected-filter :icons))
|
||||
;; :on-close #(swap! state assoc :menu-open false)
|
||||
;; :options (mapv (fn [[key val]] [val #(select-option key)]) filter-to-str)}]]]]
|
||||
;;
|
||||
;; [:div.tool-window-content
|
||||
;; [:& tab-container {}
|
||||
;; [:& tab-element
|
||||
;; {:id :icons :title (tr "workspace.library.icons")}
|
||||
;; [:& library-tab {:section :icons
|
||||
;; :libraries (get-libraries :icons) }]]
|
||||
;;
|
||||
;; [:& tab-element
|
||||
;; {:id :images :title (tr "workspace.library.images")}
|
||||
;; [:& library-tab {:section :images
|
||||
;; :libraries (get-libraries :images)}]]]]]))
|
||||
;;
|
||||
;;
|
||||
|
|
|
@ -353,10 +353,10 @@
|
|||
(let [shape {:name name
|
||||
:metadata {:width (:width image)
|
||||
:height (:height image)
|
||||
:uri (:uri image)
|
||||
:thumb-width (:thumb-width image)
|
||||
:thumb-height (:thumb-height image)
|
||||
:thumb-uri (:thumb-uri image)}}
|
||||
:uri (:uri image)}}
|
||||
;; :thumb-width (:thumb-width image)
|
||||
;; :thumb-height (:thumb-height image)
|
||||
;; :thumb-uri (:thumb-uri image)}}
|
||||
aspect-ratio (/ (:width image) (:height image))]
|
||||
(st/emit! (dw/create-and-add-shape :image shape aspect-ratio))))
|
||||
|
||||
|
@ -381,11 +381,11 @@
|
|||
urls (filter #(and (not (str/blank? %))
|
||||
(not (str/starts-with? % "#")))
|
||||
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
|
||||
(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
|
||||
(fn [event]
|
||||
|
|
Loading…
Add table
Reference in a new issue