🎉 Add media-object lifecycle handling.

This commit is contained in:
Andrey Antukh 2020-08-17 13:39:36 +02:00
parent 93aaa52e73
commit c0cd0d4a23
39 changed files with 975 additions and 323 deletions

View file

@ -23,4 +23,9 @@
(def login-with-ldap lwl)
(def worker-uri wuri)
(def public-uri puri)
(def media-uri (str puri "/media"))
(def default-theme "default")))
(defn resolve-media-path
[path]
(str media-uri "/" path))

View file

@ -534,7 +534,8 @@
shape (-> (cp/make-minimal-shape type)
(merge data)
(geom/resize width height)
(geom/absolute-move (gpt/point x y)))]
(geom/absolute-move (gpt/point x y))
(geom/transform-shape))]
(rx/of (add-shape shape))))))
@ -1177,8 +1178,13 @@
(ptk/reify ::paste-bin-impl
ptk/WatchEvent
(watch [_ state stream]
(let [file-id (get-in state [:workspace-page :file-id])]
(rx/of (dwp/upload-media-objects file-id true [image] image-uploaded))))))
(let [file-id (get-in state [:workspace-page :file-id])
params {:file-id file-id
:local? true
:js-files [image]}]
(rx/of (dwp/upload-media-objects
(with-meta params
{:on-success image-uploaded})))))))
(def paste
(ptk/reify ::paste
@ -1436,7 +1442,6 @@
(def fetch-shared-files dwp/fetch-shared-files)
(def link-file-to-library dwp/link-file-to-library)
(def unlink-file-from-library dwp/unlink-file-from-library)
(def add-media-object-from-url dwp/add-media-object-from-url)
(def upload-media-objects dwp/upload-media-objects)
(def delete-media-object dwp/delete-media-object)
(def rename-page dwp/rename-page)

View file

@ -434,92 +434,83 @@
;; --- Upload local media objects
(declare upload-media-objects-result)
(s/def ::local? ::us/boolean)
(s/def ::uri ::us/string)
(defn add-media-object-from-url
([file-id is-local url] (add-media-object-from-url file-id is-local url identity))
([file-id is-local url on-added]
(us/verify ::us/url url)
(us/verify fn? on-added)
(us/verify ::us/boolean is-local)
(ptk/reify ::add-media-object-from-url
ptk/WatchEvent
(watch [_ state stream]
(let [on-success #(do (di/notify-finished-loading)
(on-added %))
on-error #(do (di/notify-finished-loading)
(di/process-error %))
is-library (not= file-id (:id (:workspace-file state)))
prepare
(fn [url]
{:file-id file-id
:is-local is-local
:url url})]
(di/notify-start-loading)
(->> (rx/of url)
(rx/map prepare)
(rx/mapcat #(rp/mutation! :add-media-object-from-url %))
(rx/do on-success)
(rx/map (partial upload-media-objects-result file-id is-local is-library))
(rx/catch on-error)))))))
(s/def ::upload-media-objects-params
(s/keys :req-un [::file-id ::local?]
:opt-un [::uri ::di/js-files]))
(defn upload-media-objects
([file-id is-local js-files] (upload-media-objects file-id is-local js-files identity))
([file-id is-local js-files on-uploaded]
(us/verify ::us/uuid file-id)
(us/verify ::us/boolean is-local)
(us/verify ::di/js-files js-files)
(us/verify fn? on-uploaded)
[{:keys [file-id local? js-files uri] :as params}]
(us/assert ::upload-media-objects-params params)
(ptk/reify ::upload-media-objects
ptk/WatchEvent
(watch [_ state stream]
(let [on-success #(do (di/notify-finished-loading)
(on-uploaded %))
on-error #(do (di/notify-finished-loading)
(di/process-error %))
(let [{:keys [on-success on-error]
:or {on-success identity}} (meta params)
is-library (not= file-id (:id (:workspace-file state)))
prepare
prepare-js-file
(fn [js-file]
{:name (.-name js-file)
:file-id file-id
:content js-file
:is-local is-local})]
:is-local local?})
(di/notify-start-loading)
prepare-uri
(fn [uri]
{:file-id file-id
:is-local local?
:url uri})
(->> (rx/from js-files)
(rx/map di/validate-file)
(rx/map prepare)
(rx/mapcat #(rp/mutation! :upload-media-object %))
(rx/do on-success)
(rx/map (partial upload-media-objects-result file-id is-local is-library))
(rx/catch on-error)))))))
assoc-to-library
(fn [media-object state]
(cond
(true? local?)
state
(defn upload-media-objects-result
[file-id is-local is-library media-object]
(us/verify ::us/uuid file-id)
(us/verify ::us/boolean is-local)
(us/verify ::cm/media-object media-object)
(ptk/reify ::upload-media-objects-result
ptk/UpdateEvent
(update [_ state]
(if is-local ;; the media-object is local to the file, not for its library
state
(if is-library ;; the file is not the currently editing one, but a linked shared file
(update-in state
[:workspace-libraries file-id :media-objects]
#(conj % media-object))
(update-in state
[:workspace-file :media-objects]
#(conj % media-object)))))))
(true? is-library)
(update-in state
[:workspace-libraries file-id :media-objects]
#(conj % media-object))
:else
(update-in state
[:workspace-file :media-objects]
#(conj % media-object))))]
(rx/concat
(rx/of (dm/show {:content (tr "media.loading")
:type :info
:timeout nil}))
(->> (if (string? uri)
(->> (rx/of uri)
(rx/map prepare-uri)
(rx/mapcat #(rp/mutation! :add-media-object-from-url %)))
(->> (rx/from js-files)
(rx/map di/validate-file)
(rx/map prepare-js-file)
(rx/mapcat #(rp/mutation! :upload-media-object %))))
(rx/do on-success)
(rx/map (fn [mobj] (partial assoc-to-library mobj)))
(rx/catch (fn [error]
(cond
(= (:code error) :media-type-not-allowed)
(rx/of (dm/error (tr "errors.media-type-not-allowed")))
(= (:code error) :media-type-mismatch)
(rx/of (dm/error (tr "errors.media-type-mismatch")))
(fn? on-error)
(do
(on-error error)
(rx/empty))
:else
(rx/throw error))))
(rx/finalize (fn []
(st/emit! dm/hide)))))))))
;; --- Delete media object

View file

@ -10,6 +10,7 @@
(ns uxbox.main.ui.shapes.image
(:require
[rumext.alpha :as mf]
[uxbox.config :as cfg]
[uxbox.common.geom.shapes :as geom]
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.util.object :as obj]))
@ -20,8 +21,7 @@
(let [shape (unchecked-get props "shape")
{:keys [id x y width height rotation metadata]} shape
transform (geom/transform-matrix shape)
uri (:uri metadata)
uri (cfg/resolve-media-path (:path metadata))
props (-> (attrs/extract-style-attrs shape)
(obj/merge!
#js {:x x

View file

@ -36,15 +36,20 @@
on-uploaded
(fn [{:keys [id name] :as image}]
(let [shape {:name name
:metadata {:width (:width image)
:metadata {:width (:width image)
:height (:height image)
:uri (:uri image)}}
:id (:id image)
:path (:path image)}}
aspect-ratio (/ (:width image) (:height image))]
(st/emit! (dw/create-and-add-shape :image shape aspect-ratio))))
on-files-selected
(fn [js-files]
(st/emit! (dw/upload-media-objects (:id file) true js-files on-uploaded)))]
(st/emit! (dw/upload-media-objects
(with-meta {:file-id (:id file)
:local? true
:js-files js-files}
{:on-success on-uploaded}))))]
[:aside.left-toolbar
[:div.left-toolbar-inside

View file

@ -12,6 +12,7 @@
[okulary.core :as l]
[cuerdas.core :as str]
[rumext.alpha :as mf]
[uxbox.config :as cfg]
[uxbox.common.data :as d]
[uxbox.common.media :as cm]
[uxbox.common.pages :as cp]
@ -82,7 +83,10 @@
on-files-selected
(fn [js-files]
(st/emit! (dw/upload-media-objects file-id false js-files)))
(let [params {:file-id file-id
:local? false
:js-files js-files}]
(st/emit! (dw/upload-media-objects params))))
on-context-menu
(fn [object-id]
@ -98,10 +102,9 @@
:object-id object-id)))))
on-drag-start
(fn [uri]
(fn [event]
(dnd/set-data! event "text/uri-list" uri)
(dnd/set-allowed-effect! event "move")))]
(fn [path event]
(dnd/set-data! event "text/uri-list" (cfg/resolve-media-path path))
(dnd/set-allowed-effect! event "move"))]
[:div.asset-group
[:div.group-title
@ -115,22 +118,23 @@
:input-ref file-input
:on-selected on-files-selected}]])]
[:div.group-grid
(for [object media-objects]
[:div.grid-cell {:key (:id object)
:draggable true
: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 object)]])
(when local-library?
[:& context-menu
{:selectable false
:show (:menu-open @state)
:on-close #(swap! state assoc :menu-open false)
:top (:top @state)
:left (:left @state)
:options [[(tr "workspace.assets.delete") delete-graphic]]}])]]))
(for [object media-objects]
[:div.grid-cell {:key (:id object)
:draggable true
:on-context-menu (on-context-menu (:id object))
:on-drag-start (partial on-drag-start (:path object))}
[:img {:src (cfg/resolve-media-path (:thumb-path object))
:draggable false}] ;; Also need to add css pointer-events: none
[:div.cell-name (:name object)]])
(when local-library?
[:& context-menu
{:selectable false
:show (:menu-open @state)
:on-close #(swap! state assoc :menu-open false)
:top (:top @state)
:left (:left @state)
:options [[(tr "workspace.assets.delete") delete-graphic]]}])]]))
(mf/defc color-item
@ -287,7 +291,7 @@
[:a {:href (str "#" library-url) :target "_blank"} i/chain]]])]
(when @open?
(let [show-graphics (and (or (= box-filter :all) (= box-filter :graphics))
(or (> (count media-objects) 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

View file

@ -349,12 +349,15 @@
(dnd/has-type? e "text/uri-list"))
(dom/prevent-default e)))
;; TODO: seems duplicated callback is the same as one located
;; in left_toolbar
on-uploaded
(fn [{:keys [id name] :as image}]
(let [shape {:name name
:metadata {:width (:width image)
:height (:height image)
:uri (:uri image)}}
:id (:id image)
:path (:path image)}}
aspect-ratio (/ (:width image) (:height image))]
(st/emit! (dw/create-and-add-shape :image shape aspect-ratio))))
@ -379,11 +382,23 @@
urls (filter #(and (not (str/blank? %))
(not (str/starts-with? % "#")))
lines)]
(run! #(st/emit! (dw/add-media-object-from-url (:id file) true % on-uploaded)) urls))
(->> urls
(map (fn [uri]
(with-meta {:file-id (:id file)
:local? true
:uri uri}
{:on-success on-uploaded})))
(map dw/upload-media-objects)
(apply st/emit!)))
:else
(let [js-files (dnd/get-files event)]
(st/emit! (dw/upload-media-objects (:id file) true js-files on-uploaded)))))
(let [js-files (dnd/get-files event)
params {:file-id (:id file)
:local? true
:js-files js-files}]
(st/emit! (dw/upload-media-objects
(with-meta params
{:on-success on-uploaded}))))))
on-resize
(fn [event]