0
Fork 0
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:
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)
(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)

View file

@ -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)

View file

@ -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.*,

View 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]

View file

@ -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...",

View file

@ -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)))))

View file

@ -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}))

View file

@ -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)

View file

@ -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

View file

@ -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))

View file

@ -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]

View file

@ -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."]])]))

View file

@ -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!

View file

@ -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

View file

@ -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)

View file

@ -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)}]]]]]))
;;
;;

View file

@ -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]