♻️ Integrate new storage subsystem.

This commit is contained in:
Andrey Antukh 2021-01-04 18:41:05 +01:00 committed by Alonso Torres
parent 3d88749976
commit ab944fb9ae
48 changed files with 950 additions and 632 deletions

View file

@ -15,6 +15,7 @@
[app.common.version :as v]
[app.util.object :as obj]
[app.util.dom :as dom]
[app.util.avatars :as avatars]
[cuerdas.core :as str]))
;; --- Auxiliar Functions
@ -72,7 +73,7 @@
(def worker-uri (obj/get global "appWorkerURI" "/js/worker.js"))
(def public-uri (or (obj/get global "appPublicURI")
(.-origin ^js js/location)))
(def media-uri (str public-uri "/media"))
(def media-uri (str public-uri "/assets"))
(def version (delay (parse-version global)))
(def target (delay (parse-target global)))
(def browser (delay (parse-browser)))
@ -85,7 +86,6 @@
;; --- Helper Functions
(defn ^boolean check-browser? [candidate]
(us/verify ::browser candidate)
(= candidate @browser))
@ -94,9 +94,22 @@
(us/verify ::platform candidate)
(= candidate @platform))
(defn resolve-media-path
[path]
(when path
(if (str/starts-with? path "data:")
path
(str media-uri "/" path))))
(defn resolve-profile-photo-url
[{:keys [photo-id fullname name] :as profile}]
(if (nil? photo-id)
(avatars/generate {:name (or fullname name)})
(str public-uri "/assets/by-id/" photo-id)))
(defn resolve-team-photo-url
[{:keys [photo-id name] :as team}]
(if (nil? photo-id)
(avatars/generate {:name name})
(str public-uri "/assets/by-id/" photo-id)))
(defn resolve-file-media
([media]
(resolve-file-media media false))
([{:keys [id] :as media} thumnail?]
(str public-uri "/assets/by-file-media-id/" id (when thumnail? "/thumbnail"))))

View file

@ -74,14 +74,13 @@
(watch [_ state stream]
(let [profile (:profile state)]
(->> (rp/query :team params)
(rx/map #(avatars/assoc-avatar % :name))
(rx/map #(partial fetched %))))))))
(defn fetch-team-members
[{:keys [id] :as params}]
(us/assert ::us/uuid id)
(letfn [(fetched [members state]
(->> (map #(avatars/assoc-avatar % :name) members)
(->> members
(d/index-by :id)
(assoc-in state [:team-members id])))]
(ptk/reify ::fetch-team-members

View file

@ -25,10 +25,24 @@
[cuerdas.core :as str]
[potok.core :as ptk]))
;; --- Predicates
(defn ^boolean file?
[o]
(instance? js/File o))
(defn ^boolean blob?
[o]
(instance? js/Blob o))
;; --- Specs
(s/def ::js-file #(instance? js/Blob %))
(s/def ::js-files (s/coll-of ::js-file))
(s/def ::blob blob?)
(s/def ::blobs (s/coll-of ::blob))
(s/def ::file file?)
(s/def ::files (s/coll-of ::file))
;; --- Utility functions

View file

@ -59,9 +59,6 @@
(update [_ state]
(assoc state :profile
(cond-> data
(empty? (:photo data))
(assoc :photo (avatars/generate {:name fullname}))
(nil? (:lang data))
(assoc :lang cfg/default-language)
@ -197,7 +194,7 @@
[{:keys [team-id] :as params}]
(us/assert ::us/uuid team-id)
(letfn [(fetched [users state]
(->> (map #(avatars/assoc-avatar % :fullname) users)
(->> users
(d/index-by :id)
(assoc state :users)))]
(ptk/reify ::fetch-team-users

View file

@ -116,8 +116,7 @@
ptk/UpdateEvent
(update [_ state]
(let [objects (:objects page)
frames (extract-frames objects)
users (map #(avatars/assoc-avatar % :fullname) users)]
frames (extract-frames objects)]
(assoc state
:viewer-libraries (d/index-by :id libraries)
:viewer-data {:project project

View file

@ -1185,8 +1185,7 @@
(prepare-object [objects selected {:keys [type] :as obj}]
(let [obj (maybe-translate obj objects selected)]
(if (= type :image)
(let [path (get-in obj [:metadata :path])
url (cfg/resolve-media-path path)]
(let [url (cfg/resolve-file-media (:metadata obj))]
(->> (http/fetch-as-data-url url)
(rx/map #(assoc obj ::data %))
(rx/take 1)))
@ -1314,7 +1313,7 @@
(fn [blob]
{:name (:name imgpart)
:file-id file-id
:content (list blob (:file-name imgpart))
:content blob
:is-local true}))
(rx/mapcat #(rp/mutation! :upload-media-object %))
(rx/map (fn [media]
@ -1441,7 +1440,7 @@
(let [file-id (get-in state [:workspace-file :id])
params {:file-id file-id
:local? true
:js-files [image]}]
:data [image]}]
(rx/of (dwp/upload-media-objects
(with-meta params
{:on-success image-uploaded})))))))
@ -1560,6 +1559,7 @@
(d/export dwp/link-file-to-library)
(d/export dwp/unlink-file-from-library)
(d/export dwp/upload-media-objects)
(d/export dwp/clone-media-object)
;; Selection

View file

@ -12,6 +12,7 @@
[app.common.data :as d]
[app.common.spec :as us]
[app.common.uuid :as uuid]
[app.common.pages :as cp]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as geom]
[app.main.data.messages :as dm]
@ -109,8 +110,9 @@
(ptk/reify ::add-media
ptk/WatchEvent
(watch [_ state stream]
(let [rchg {:type :add-media
:object media}
(let [obj (select-keys media [:id :name :width :height :mtype])
rchg {:type :add-media
:object obj}
uchg {:type :del-media
:id id}]
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true}))))))
@ -370,7 +372,7 @@
update-new-shape
(fn [new-shape original-shape]
(let [new-name
(let [new-name
(dwc/generate-unique-name @unames (:name new-shape))]
(swap! unames conj new-name)

View file

@ -147,8 +147,7 @@
(let [session {:id id
:fullname (:fullname profile)
:updated-at (dt/now)
:photo-uri (or (and (:photo profile) (cfg/resolve-media-path (:photo profile)))
(avatars/generate {:name (:fullname profile)}))}
:photo-uri (cfg/resolve-profile-photo-url profile)}
session (assign-color sessions session)]
(assoc sessions id session)))

View file

@ -239,14 +239,13 @@
ptk/UpdateEvent
(update [_ state]
(let [users (map avatars/assoc-profile-avatar users)]
(assoc state
:users (d/index-by :id users)
:workspace-undo {}
:workspace-project project
:workspace-file file
:workspace-data (:data file)
:workspace-libraries (d/index-by :id libraries))))))
(assoc state
:users (d/index-by :id users)
:workspace-undo {}
:workspace-project project
:workspace-file file
:workspace-data (:data file)
:workspace-libraries (d/index-by :id libraries)))))
;; --- Set File shared
@ -339,70 +338,108 @@
(assoc-in state [:workspace-pages id] page))))
;; --- Upload local media objects
;; --- Upload File Media objects
(s/def ::local? ::us/boolean)
(s/def ::uri ::us/string)
(s/def ::data ::di/blobs)
(s/def ::name ::us/string)
(s/def ::uri ::us/string)
(s/def ::uris (s/coll-of ::uri))
(s/def ::upload-media-objects-params
(s/keys :req-un [::file-id ::local?]
:opt-un [::uri ::di/js-files]))
(s/def ::upload-media-objects
(s/and
(s/keys :req-un [::file-id ::local?]
:opt-in [::name ::data ::uris])
(fn [props]
(or (contains? props :data)
(contains? props :uris)))))
(defn upload-media-objects
[{:keys [file-id local? js-files uri name] :as params}]
(us/assert ::upload-media-objects-params params)
(ptk/reify ::upload-media-objects
[{:keys [file-id local? data name uris] :as params}]
(us/assert ::upload-media-objects params)
(ptk/reify ::upload-media-objects
ptk/WatchEvent
(watch [_ state stream]
(let [{:keys [on-success on-error]
:or {on-success identity}} (meta params)
prepare-file
(fn [blob]
(let [name (or name (if (di/file? blob) (.-name blob) "blob"))]
{:name name
:file-id file-id
:content blob
:is-local local?}))
prepare-uri
(fn [uri]
{:file-id file-id
:is-local local?
:url uri
:name name})]
(rx/concat
(rx/of (dm/show {:content (tr "media.loading")
:type :info
:timeout nil
:tag :media-loading}))
(->> (if (seq uris)
(->> (rx/from uris)
(rx/map prepare-uri)
(rx/mapcat #(rp/mutation! :create-file-media-object-from-url %)))
(->> (rx/from data)
(rx/map di/validate-file)
(rx/map prepare-file)
(rx/mapcat #(rp/mutation! :upload-file-media-object %))))
(rx/do on-success)
(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-tag :media-loading))))))))))
;; --- Upload File Media objects
(s/def ::object-id ::us/uuid)
(s/def ::clone-media-objects-params
(s/keys :req-un [::file-id ::local? ::object-id]))
(defn clone-media-object
[{:keys [file-id local? object-id] :as params}]
(us/assert ::clone-media-objects-params params)
(ptk/reify ::clone-media-objects
ptk/WatchEvent
(watch [_ state stream]
(let [{:keys [on-success on-error]
:or {on-success identity}} (meta params)
is-library (not= file-id (:id (:workspace-file state)))
prepare-js-file
(fn [js-file]
{:name (.-name js-file)
:file-id file-id
:content js-file
:is-local local?})
prepare-uri
(fn [uri]
{:file-id file-id
:is-local local?
:url uri
:name name})]
:or {on-success identity
on-error identity}} (meta params)
params {:is-local local?
:file-id file-id
:id object-id}]
(rx/concat
(rx/of (dm/show {:content (tr "media.loading")
:type :info
:timeout nil
:tag :media-loading}))
(->> (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 %))))
(->> (rp/mutation! :clone-file-media-object params)
(rx/do on-success)
(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-tag :media-loading))))))))))
(rx/catch on-error)
(rx/finalize #(st/emit! (dm/hide-tag :media-loading)))))))))
;; --- Helpers

View file

@ -91,7 +91,7 @@
(->> (http/send! {:method :post :uri uri})
(rx/mapcat handle-response))))
(defmethod mutation :upload-media-object
(defmethod mutation :upload-file-media-object
[id params]
(let [form (js/FormData.)]
(run! (fn [[key val]]

View file

@ -248,7 +248,7 @@
[:div.comment
[:div.author
[:div.avatar
[:img {:src (cfg/resolve-media-path (:photo owner))}]]
[:img {:src (cfg/resolve-profile-photo-url owner)}]]
[:div.name
[:div.fullname (:fullname owner)]
[:div.timeago (dt/timeago (:modified-at comment))]]
@ -366,7 +366,7 @@
:unread (pos? (:count-unread-comments item)))}
(:seqn item)]
[:div.avatar
[:img {:src (cfg/resolve-media-path (:photo owner))}]]
[:img {:src (cfg/resolve-profile-photo-url owner)}]]
[:div.name
[:div.fullname (:fullname owner) ", "]
[:div.timeago (dt/timeago (:modified-at item))]]]

View file

@ -136,8 +136,6 @@
(mf/deps (:id team))
(fn []
(->> (rp/query! :teams)
(rx/map (fn [teams]
(mapv #(avatars/assoc-avatar % :name) teams)))
(rx/subs #(reset! teams %)))))
[:ul.dropdown.teams-dropdown
@ -151,7 +149,7 @@
[:* {:key (:id team)}
[:li.team-name {:on-click (partial go-projects (:id team))}
[:span.team-icon
[:img {:src (cfg/resolve-media-path (:photo team))}]]
[:img {:src (cfg/resolve-team-photo-url team)}]]
[:span.team-text {:title (:name team)} (:name team)]]])
[:hr]
@ -329,7 +327,7 @@
[:span.team-text (t locale "dashboard.default-team-name")]]
[:div.team-name
[:span.team-icon
[:img {:src (cfg/resolve-media-path (:photo team))}]]
[:img {:src (cfg/resolve-team-photo-url team)}]]
[:span.team-text {:title (:name team)} (:name team)]])
[:span.switch-icon
@ -427,7 +425,7 @@
(mf/defc profile-section
[{:keys [profile locale team] :as props}]
(let [show (mf/use-state false)
photo (cfg/resolve-media-path (:photo profile))
photo (cfg/resolve-profile-photo-url profile)
on-click
(mf/use-callback

View file

@ -283,7 +283,7 @@
[:div.name (:name team)]
[:div.icon
[:span.update-overlay {:on-click on-image-click} i/exit]
[:img {:src (cfg/resolve-media-path (:photo team))}]
[:img {:src (cfg/resolve-team-photo-url team)}]
[:& file-uploader {:accept "image/jpeg,image/png"
:multi false
:input-ref finput
@ -292,7 +292,7 @@
[:div.block.owner-block
[:div.label (tr "dashboard.team-members")]
[:div.owner
[:span.icon [:img {:src (cfg/resolve-media-path (:photo owner))}]]
[:span.icon [:img {:src (cfg/resolve-profile-photo-url owner)}]]
[:span.text (str (:name owner) " (" (tr "labels.owner") ")") ]]
[:div.summary
[:span.icon i/user]

View file

@ -26,7 +26,7 @@
[:div.attributes-block {:key (str "image-" (:id shape))}
[:div.attributes-image-row
[:div.attributes-image
[:img {:src (cfg/resolve-media-path (-> shape :metadata :path))}]]]
[:img {:src (cfg/resolve-file-media (-> shape :metadata))}]]]
[:div.attributes-unit-row
[:div.attributes-label (t locale "handoff.attributes.image.width")]
@ -41,5 +41,5 @@
(let [filename (last (str/split (-> shape :metadata :path) "/"))]
[:a.download-button {:target "_blank"
:download filename
:href (cfg/resolve-media-path (-> shape :metadata :path))}
:href (cfg/resolve-file-media (-> shape :metadata))}
(t locale "handoff.attributes.image.download")])])))

View file

@ -9,6 +9,7 @@
(ns app.main.ui.settings.profile
(:require
[app.config :as cfg]
[app.common.spec :as us]
[app.main.data.messages :as dm]
[app.main.data.modal :as modal]
@ -91,11 +92,7 @@
[{:keys [locale] :as props}]
(let [file-input (mf/use-ref nil)
profile (mf/deref refs/profile)
photo (:photo profile)
photo (if (or (str/empty? photo) (nil? photo))
"images/avatar.jpg"
(cfg/resolve-media-path photo))
photo (cfg/resolve-profile-photo-url profile)
on-image-click #(dom/click (mf/ref-val file-input))
on-file-selected

View file

@ -24,9 +24,9 @@
(let [shape (unchecked-get props "shape")
{:keys [id x y width height rotation metadata]} shape
uri (cfg/resolve-media-path (:path metadata))
uri (cfg/resolve-file-media metadata)
embed-resources? (mf/use-ctx muc/embed-ctx)
data-uri (mf/use-state (when (not embed-resources?) uri))]
data-uri (mf/use-state (when (not embed-resources?) uri))]
(mf/use-effect
(mf/deps uri)

View file

@ -31,25 +31,25 @@
on-uploaded
(mf/use-callback
(fn [{:keys [id name] :as image}]
(let [shape {:name name
:width (:width image)
:height (:height image)
:metadata {:width (:width image)
:height (:height image)
:id (:id image)
:path (:path image)}}
aspect-ratio (/ (:width image) (:height image))]
(st/emit! (dw/create-and-add-shape :image 0 0 shape)))))
(fn [image]
(->> {:name (:name image)
:width (:width image)
:height (:height image)
:metadata {:width (:width image)
:height (:height image)
:mtype (:mtype image)
:id (:id image)}}
(dw/create-and-add-shape :image 0 0)
(st/emit!))))
on-files-selected
(mf/use-callback
(mf/deps file)
(fn [js-files]
(fn [blobs]
(st/emit! (dw/upload-media-objects
(with-meta {:file-id (:id file)
:local? true
:js-files js-files}
:data (seq blobs)}
{:on-success on-uploaded})))))]
[:li.tooltip.tooltip-right

View file

@ -163,10 +163,10 @@
on-selected
(mf/use-callback
(mf/deps file-id)
(fn [js-files]
(fn [blobs]
(let [params (with-meta {:file-id file-id
:local? false
:js-files js-files}
:data (seq blobs)}
{:on-success on-media-uploaded})]
(st/emit! (dw/upload-media-objects params)))))
@ -212,8 +212,8 @@
on-drag-start
(mf/use-callback
(fn [path name event]
(dnd/set-data! event "text/uri-list" (cfg/resolve-media-path path))
(fn [{:keys [name id]} event]
(dnd/set-data! event "text/asset-id" (str id))
(dnd/set-data! event "text/asset-name" name)
(dnd/set-allowed-effect! event "move")))]
@ -234,8 +234,8 @@
[: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) (:name object))}
[:img {:src (cfg/resolve-media-path (:thumb-path object))
:on-drag-start (partial on-drag-start object)}
[:img {:src (cfg/resolve-file-media object true)
:draggable false}] ;; Also need to add css pointer-events: none
#_[:div.cell-name (:name object)]

View file

@ -46,6 +46,7 @@
[app.util.object :as obj]
[app.util.perf :as perf]
[app.util.timers :as timers]
[app.util.http :as http]
[beicon.core :as rx]
[clojure.set :as set]
[cuerdas.core :as str]
@ -437,7 +438,8 @@
(when (or (dnd/has-type? e "app/shape")
(dnd/has-type? e "app/component")
(dnd/has-type? e "Files")
(dnd/has-type? e "text/uri-list"))
(dnd/has-type? e "text/uri-list")
(dnd/has-type? e "text/asset-id"))
(dom/prevent-default e))))
on-drag-over
@ -446,24 +448,24 @@
(when (or (dnd/has-type? e "app/shape")
(dnd/has-type? e "app/component")
(dnd/has-type? e "Files")
(dnd/has-type? e "text/uri-list"))
(dnd/has-type? e "text/uri-list")
(dnd/has-type? e "text/asset-id"))
(dom/prevent-default e))))
;; TODO: seems duplicated callback is the same as one located
;; in left_toolbar
on-uploaded
(mf/use-callback
(fn [{:keys [id name] :as image} {:keys [x y]}]
(let [shape {:name name
:width (:width image)
:height (:height image)
:x (- x (/ (:width image) 2))
:y (- y (/ (:height image) 2))
:metadata {:width (:width image)
(fn [image {:keys [x y]}]
(prn "on-uploaded" image x y)
(let [shape {:name (:name image)
:width (:width image)
:height (:height image)
:x (- x (/ (:width image) 2))
:y (- y (/ (:height image) 2))
:metadata {:width (:width image)
:height (:height image)
:id (:id image)
:path (:path image)}}
aspect-ratio (/ (:width image) (:height image))]
:name (:name image)
:id (:id image)
:mtype (:mtype image)}}]
(st/emit! (dw/create-and-add-shape :image x y shape)))))
on-drop
@ -492,28 +494,36 @@
(gpt/point final-x final-y))))
(dnd/has-type? event "text/uri-list")
(let [data (dnd/get-data event "text/uri-list")
name (dnd/get-data event "text/asset-name")
(let [data (dnd/get-data event "text/uri-list")
name (dnd/get-data event "text/asset-name")
lines (str/lines data)
urls (filter #(and (not (str/blank? %))
(not (str/starts-with? % "#")))
lines)]
(->> urls
(map (fn [uri]
(with-meta {:file-id (:id file)
:local? true
:uri uri
:name name}
{:on-success #(on-uploaded % viewport-coord)})))
(map dw/upload-media-objects)
(apply st/emit!)))
urls (filter #(and (not (str/blank? %))
(not (str/starts-with? % "#")))
lines)]
(st/emit!
(dw/upload-media-objects
(with-meta {:file-id (:id file)
:local? true
:uris urls
:name name}
{:on-success #(on-uploaded % viewport-coord)}))))
(dnd/has-type? event "text/asset-id")
(let [id (-> (dnd/get-data event "text/asset-id") uuid/uuid)
name (dnd/get-data event "text/asset-name")
params {:file-id (:id file)
:local? true
:object-id id
:name name}]
(st/emit! (dw/clone-media-object
(with-meta params
{:on-success #(on-uploaded % viewport-coord)}))))
:else
(let [js-files (dnd/get-files event)
params {:file-id (:id file)
:local? true
:js-files js-files
}]
(let [files (dnd/get-files event)
params {:file-id (:id file)
:local? true
:data (seq files)}]
(st/emit! (dw/upload-media-objects
(with-meta params
{:on-success #(on-uploaded % viewport-coord)}))))))))

View file

@ -13,7 +13,7 @@
[app.util.object :as obj]
["randomcolor" :as rdcolor]))
(defn generate
(defn generate*
[{:keys [name color size]
:or {color "#000000" size 128}}]
(let [parts (str/words (str/upper name))
@ -36,13 +36,5 @@
(.toDataURL canvas)))
(defn assoc-avatar
[{:keys [photo] :as object} key]
(cond-> object
(or (nil? photo) (empty? photo))
(assoc :photo (generate {:name (get object key)}))))
(defn assoc-profile-avatar
[object]
(assoc-avatar object :fullname))
(def generate (memoize generate*))