mirror of
https://github.com/penpot/penpot.git
synced 2025-05-15 03:16:38 +02:00
🚧 More work on dashboard base model refactor (frontend).
This commit is contained in:
parent
932d5bb004
commit
9bb5be306f
16 changed files with 563 additions and 522 deletions
|
@ -49,7 +49,7 @@
|
||||||
|
|
||||||
(cond
|
(cond
|
||||||
(and (= path "") (:auth storage))
|
(and (= path "") (:auth storage))
|
||||||
(st/emit! (rt/nav :dashboard/projects))
|
(st/emit! (rt/nav :dashboard-projects))
|
||||||
|
|
||||||
(and (= path "") (not (:auth storage)))
|
(and (= path "") (not (:auth storage)))
|
||||||
(st/emit! (rt/nav :auth/login))
|
(st/emit! (rt/nav :auth/login))
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
|
||||||
(ns uxbox.main.data.pages
|
(ns uxbox.main.data.pages
|
||||||
|
"Page related events (for workspace mainly)."
|
||||||
(:require
|
(:require
|
||||||
[cljs.spec.alpha :as s]
|
[cljs.spec.alpha :as s]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
|
@ -22,7 +23,7 @@
|
||||||
(s/def ::name ::us/string)
|
(s/def ::name ::us/string)
|
||||||
(s/def ::inst ::us/inst)
|
(s/def ::inst ::us/inst)
|
||||||
(s/def ::type ::us/keyword)
|
(s/def ::type ::us/keyword)
|
||||||
(s/def ::project-id ::us/uuid)
|
(s/def ::file-id ::us/uuid)
|
||||||
(s/def ::created-at ::us/inst)
|
(s/def ::created-at ::us/inst)
|
||||||
(s/def ::modified-at ::us/inst)
|
(s/def ::modified-at ::us/inst)
|
||||||
(s/def ::version ::us/number)
|
(s/def ::version ::us/number)
|
||||||
|
@ -56,10 +57,10 @@
|
||||||
(s/def ::data
|
(s/def ::data
|
||||||
(s/keys :req-un [::shapes ::canvas ::shapes-by-id]))
|
(s/keys :req-un [::shapes ::canvas ::shapes-by-id]))
|
||||||
|
|
||||||
(s/def ::page-entity
|
(s/def ::page
|
||||||
(s/keys :req-un [::id
|
(s/keys :req-un [::id
|
||||||
::name
|
::name
|
||||||
::project-id
|
::file-id
|
||||||
::created-at
|
::created-at
|
||||||
::modified-at
|
::modified-at
|
||||||
::user-id
|
::user-id
|
||||||
|
@ -67,21 +68,19 @@
|
||||||
::metadata
|
::metadata
|
||||||
::data]))
|
::data]))
|
||||||
|
|
||||||
|
(s/def ::pages
|
||||||
|
(s/every ::page :kind vector?))
|
||||||
|
|
||||||
;; --- Protocols
|
;; --- Protocols
|
||||||
|
|
||||||
(defprotocol IPageUpdate
|
(defprotocol IPageDataUpdate
|
||||||
"A marker protocol for mark events that alters the
|
|
||||||
page and is subject to perform a backend synchronization.")
|
|
||||||
|
|
||||||
(defprotocol IMetadataUpdate
|
|
||||||
"A marker protocol for mark events that alters the
|
"A marker protocol for mark events that alters the
|
||||||
page and is subject to perform a backend synchronization.")
|
page and is subject to perform a backend synchronization.")
|
||||||
|
|
||||||
(defn page-update?
|
(defn page-update?
|
||||||
[o]
|
[o]
|
||||||
(or (satisfies? IPageUpdate o)
|
(or (satisfies? IPageDataUpdate o)
|
||||||
(satisfies? IMetadataUpdate o)
|
(= ::page-data-update o)))
|
||||||
(= ::page-update o)))
|
|
||||||
|
|
||||||
;; --- Helpers
|
;; --- Helpers
|
||||||
|
|
||||||
|
@ -114,53 +113,39 @@
|
||||||
(update :pages-data dissoc id))
|
(update :pages-data dissoc id))
|
||||||
state))
|
state))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Generic Page Events (mostly Fetch & CRUD)
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
;; --- Fetch Pages (by File ID)
|
||||||
|
|
||||||
|
(declare pages-fetched)
|
||||||
|
|
||||||
|
(defn fetch-pages
|
||||||
|
[file-id]
|
||||||
|
(s/assert ::us/uuid file-id)
|
||||||
|
(reify
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state s]
|
||||||
|
(->> (rp/query :project-pages {:file-id file-id})
|
||||||
|
(rx/map pages-fetched)))))
|
||||||
|
|
||||||
;; --- Pages Fetched
|
;; --- Pages Fetched
|
||||||
|
|
||||||
(defn pages-fetched
|
(defn pages-fetched
|
||||||
[id pages]
|
[pages]
|
||||||
(s/assert ::us/uuid id)
|
(s/assert ::pages pages)
|
||||||
(s/assert ::us/coll pages)
|
|
||||||
(ptk/reify ::pages-fetched
|
(ptk/reify ::pages-fetched
|
||||||
IDeref
|
IDeref
|
||||||
(-deref [_] (list id pages))
|
(-deref [_] pages)
|
||||||
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(reduce unpack-page state pages))))
|
(reduce unpack-page state pages))))
|
||||||
|
|
||||||
(defn pages-fetched?
|
;; --- Fetch Page (By ID)
|
||||||
[v]
|
|
||||||
(= ::pages-fetched (ptk/type v)))
|
|
||||||
|
|
||||||
;; --- Fetch Pages (by project id)
|
(declare page-fetched)
|
||||||
|
|
||||||
(defn fetch-pages
|
|
||||||
[id]
|
|
||||||
(s/assert ::us/uuid id)
|
|
||||||
(reify
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state s]
|
|
||||||
(->> (rp/query :pages-by-project {:project-id id})
|
|
||||||
(rx/map #(pages-fetched id %))))))
|
|
||||||
|
|
||||||
;; --- Page Fetched
|
|
||||||
|
|
||||||
(defn page-fetched
|
|
||||||
[data]
|
|
||||||
(s/assert ::page-entity data)
|
|
||||||
(ptk/reify ::page-fetched
|
|
||||||
IDeref
|
|
||||||
(-deref [_] data)
|
|
||||||
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(unpack-page state data))))
|
|
||||||
|
|
||||||
(defn page-fetched?
|
|
||||||
[v]
|
|
||||||
(= ::page-fetched (ptk/type v)))
|
|
||||||
|
|
||||||
;; --- Fetch Pages (by project id)
|
|
||||||
|
|
||||||
(defn fetch-page
|
(defn fetch-page
|
||||||
"Fetch page by id."
|
"Fetch page by id."
|
||||||
|
@ -169,35 +154,26 @@
|
||||||
(reify
|
(reify
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state s]
|
(watch [_ state s]
|
||||||
(->> (rp/query :page {:id id})
|
(->> (rp/query :project-page {:id id})
|
||||||
(rx/map page-fetched)))))
|
(rx/map page-fetched)))))
|
||||||
|
|
||||||
;; --- Page Created
|
;; --- Page Fetched
|
||||||
|
|
||||||
(defn page-created
|
(defn page-fetched
|
||||||
[{:keys [id project-id] :as page}]
|
[data]
|
||||||
(s/assert ::page-entity page)
|
(s/assert ::page data)
|
||||||
(ptk/reify ::page-created
|
(ptk/reify ::page-fetched
|
||||||
cljs.core/IDeref
|
IDeref
|
||||||
(-deref [_] page)
|
(-deref [_] data)
|
||||||
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [data (:data page)
|
(unpack-page state data))))
|
||||||
page (dissoc page :data)]
|
|
||||||
(-> state
|
|
||||||
(update-in [:projects project-id :pages] (fnil conj []) (:id page))
|
|
||||||
(update :pages assoc id page)
|
|
||||||
(update :pages-data assoc id data))))))
|
|
||||||
|
|
||||||
(defn page-created?
|
;; --- Create Page
|
||||||
[v]
|
|
||||||
(= ::page-created (ptk/type v)))
|
|
||||||
|
|
||||||
;; --- Create Page Form
|
|
||||||
|
|
||||||
(s/def ::create-page
|
(s/def ::create-page
|
||||||
(s/keys :req-un [::name ::project-id]))
|
(s/keys :req-un [::name ::file-id]))
|
||||||
|
|
||||||
(defn create-page
|
(defn create-page
|
||||||
[{:keys [project-id name] :as data}]
|
[{:keys [project-id name] :as data}]
|
||||||
|
@ -205,7 +181,7 @@
|
||||||
(ptk/reify ::create-page
|
(ptk/reify ::create-page
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [this state s]
|
(watch [this state s]
|
||||||
(let [ordering (count (get-in state [:projects project-id :pages]))
|
#_(let [ordering (count (get-in state [:projects project-id :pages]))
|
||||||
params {:name name
|
params {:name name
|
||||||
:project-id project-id
|
:project-id project-id
|
||||||
:ordering ordering
|
:ordering ordering
|
||||||
|
@ -216,9 +192,28 @@
|
||||||
(->> (rp/mutation :create-page params)
|
(->> (rp/mutation :create-page params)
|
||||||
(rx/map page-created))))))
|
(rx/map page-created))))))
|
||||||
|
|
||||||
;; --- Update Page Form
|
;; --- Page Created
|
||||||
|
|
||||||
(declare page-renamed)
|
(defn page-created
|
||||||
|
[{:keys [id project-id] :as page}]
|
||||||
|
(s/assert ::page page)
|
||||||
|
(ptk/reify ::page-created
|
||||||
|
cljs.core/IDeref
|
||||||
|
(-deref [_] page)
|
||||||
|
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(let [data (:data page)
|
||||||
|
page (dissoc page :data)]
|
||||||
|
(-> state
|
||||||
|
(update :pages assoc id page)
|
||||||
|
(update :pages-data assoc id data))))))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Workspace-Aware Page Events
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
;; --- Rename Page
|
||||||
|
|
||||||
(s/def ::rename-page
|
(s/def ::rename-page
|
||||||
(s/keys :req-un [::id ::name]))
|
(s/keys :req-un [::id ::name]))
|
||||||
|
@ -229,7 +224,10 @@
|
||||||
(ptk/reify ::rename-page
|
(ptk/reify ::rename-page
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(update-in state [:pages id] assoc :name name))
|
(let [pid (get-in state [:workspace-page :id])
|
||||||
|
state (assoc-in state [:pages id :name] name)]
|
||||||
|
(cond-> state
|
||||||
|
(= pid id) (assoc-in [:workspace-page :name] name))))
|
||||||
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
|
@ -237,59 +235,63 @@
|
||||||
(->> (rp/mutation :rename-page params)
|
(->> (rp/mutation :rename-page params)
|
||||||
(rx/map #(ptk/data-event ::page-renamed data)))))))
|
(rx/map #(ptk/data-event ::page-renamed data)))))))
|
||||||
|
|
||||||
;; --- Page Metadata Persisted
|
;; --- Persist Page
|
||||||
|
|
||||||
(s/def ::metadata-persisted-params
|
(declare page-persisted)
|
||||||
(s/keys :req-un [::id ::version]))
|
|
||||||
|
(def persist-current-page
|
||||||
|
(ptk/reify ::persist-page
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [this state s]
|
||||||
|
(let [local (:workspace-local state)
|
||||||
|
page (:workspace-page state)
|
||||||
|
data (:workspace-data state)]
|
||||||
|
(if (:history local)
|
||||||
|
(rx/empty)
|
||||||
|
(let [page (assoc page :data data)]
|
||||||
|
(->> (rp/mutation :update-page page)
|
||||||
|
(rx/map (fn [res] (merge page res)))
|
||||||
|
(rx/map page-persisted)
|
||||||
|
(rx/catch (fn [err] (rx/of ::page-persist-error))))))))))
|
||||||
|
|
||||||
|
;; --- Page Persisted
|
||||||
|
|
||||||
|
(defn page-persisted
|
||||||
|
[{:keys [id] :as page}]
|
||||||
|
(s/assert ::page page)
|
||||||
|
(ptk/reify ::page-persisted
|
||||||
|
cljs.core/IDeref
|
||||||
|
(-deref [_] page)
|
||||||
|
|
||||||
(defn metadata-persisted
|
|
||||||
[{:keys [id] :as data}]
|
|
||||||
(s/assert ::metadata-persisted-params data)
|
|
||||||
(ptk/reify ::metadata-persisted
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(assoc-in state [:pages id :version] (:version data)))))
|
(let [data (:data page)
|
||||||
|
page (dissoc page :data)]
|
||||||
(defn metadata-persisted?
|
(-> state
|
||||||
[v]
|
(assoc :workspace-data data)
|
||||||
(= ::metadata-persisted (ptk/type v)))
|
(assoc :workspace-page page)
|
||||||
|
(update :pages assoc id page)
|
||||||
;; --- Persist Page Metadata
|
(update :pages-data assoc id data))))))
|
||||||
|
|
||||||
;; This is a simplified version of `PersistPage` event
|
|
||||||
;; that does not sends the heavyweiht `:data` attribute
|
|
||||||
;; and only serves for update other page data.
|
|
||||||
|
|
||||||
(defn persist-metadata
|
|
||||||
[id]
|
|
||||||
(s/assert ::us/uuid id)
|
|
||||||
(ptk/reify ::persist-metadata
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state stream]
|
|
||||||
#_(let [page (get-in state [:pages id])]
|
|
||||||
(->> (rp/req :update/page-metadata page)
|
|
||||||
(rx/map :payload)
|
|
||||||
(rx/map metadata-persisted))))))
|
|
||||||
|
|
||||||
;; --- Update Page
|
;; --- Update Page
|
||||||
|
|
||||||
|
;; TODO: deprecated, need refactor (this is used on page options)
|
||||||
(defn update-page-attrs
|
(defn update-page-attrs
|
||||||
[{:keys [id] :as data}]
|
[{:keys [id] :as data}]
|
||||||
(s/assert ::page-entity data)
|
(s/assert ::page data)
|
||||||
(ptk/reify
|
(ptk/reify ::update-page-attrs
|
||||||
IPageUpdate
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(update-in state [:pages id] merge (dissoc data :id :version)))))
|
(update state :workspace-page merge (dissoc data :id :version)))))
|
||||||
|
|
||||||
;; --- Update Page Metadata
|
;; --- Update Page Metadata
|
||||||
|
|
||||||
|
;; TODO: deprecated, need refactor (this is used on page options)
|
||||||
(defn update-metadata
|
(defn update-metadata
|
||||||
[id metadata]
|
[id metadata]
|
||||||
(s/assert ::id id)
|
(s/assert ::id id)
|
||||||
(s/assert ::metadata metadata)
|
(s/assert ::metadata metadata)
|
||||||
(reify
|
(reify
|
||||||
IMetadataUpdate
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [this state]
|
(update [this state]
|
||||||
(assoc-in state [:pages id :metadata] metadata))))
|
(assoc-in state [:pages id :metadata] metadata))))
|
||||||
|
@ -307,4 +309,4 @@
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state s]
|
(watch [_ state s]
|
||||||
(->> (rp/mutation :delete-page {:id id})
|
(->> (rp/mutation :delete-page {:id id})
|
||||||
(rx/map (constantly ::delete-completed))))))
|
(rx/map (ptk/data-event ::page-deleted {:id id}))))))
|
||||||
|
|
|
@ -34,6 +34,9 @@
|
||||||
::created-at
|
::created-at
|
||||||
::modified-at]))
|
::modified-at]))
|
||||||
|
|
||||||
|
(declare fetch-projects)
|
||||||
|
(declare projects-fetched?)
|
||||||
|
|
||||||
;; --- Helpers
|
;; --- Helpers
|
||||||
|
|
||||||
(defn assoc-project
|
(defn assoc-project
|
||||||
|
@ -49,21 +52,43 @@
|
||||||
|
|
||||||
;; --- Initialize
|
;; --- Initialize
|
||||||
|
|
||||||
(declare fetch-projects)
|
(declare fetch-files)
|
||||||
(declare projects-fetched?)
|
(declare initialized)
|
||||||
|
|
||||||
(defrecord Initialize []
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(assoc-in state [:dashboard :section] :dashboard/projects))
|
|
||||||
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state s]
|
|
||||||
(rx/of (fetch-projects))))
|
|
||||||
|
|
||||||
(defn initialize
|
(defn initialize
|
||||||
[]
|
[id]
|
||||||
(Initialize.))
|
(ptk/reify ::initialize
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(update state :dashboard-projects assoc :id id))
|
||||||
|
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(rx/merge
|
||||||
|
(rx/of (fetch-files id))
|
||||||
|
(->> stream
|
||||||
|
(rx/filter (ptk/type? ::files-fetched))
|
||||||
|
(rx/take 1)
|
||||||
|
(rx/map #(initialized id (deref %))))))))
|
||||||
|
|
||||||
|
(defn initialized
|
||||||
|
[id files]
|
||||||
|
(ptk/reify ::initialized
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(let [files (into #{} (map :id) files)]
|
||||||
|
(update-in state [:dashboard-projects :files] assoc id files)))))
|
||||||
|
|
||||||
|
;; --- Update Opts (Filtering & Ordering)
|
||||||
|
|
||||||
|
(defn update-opts
|
||||||
|
[& {:keys [order filter] :as opts}]
|
||||||
|
(ptk/reify ::update-opts
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(update state :dashboard-projects merge
|
||||||
|
(when order {:order order})
|
||||||
|
(when filter {:filter filter})))))
|
||||||
|
|
||||||
;; --- Projects Fetched
|
;; --- Projects Fetched
|
||||||
|
|
||||||
|
@ -81,14 +106,52 @@
|
||||||
|
|
||||||
;; --- Fetch Projects
|
;; --- Fetch Projects
|
||||||
|
|
||||||
(defn fetch-projects
|
(def fetch-projects
|
||||||
[]
|
|
||||||
(ptk/reify ::fetch-projects
|
(ptk/reify ::fetch-projects
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(->> (rp/query :projects)
|
(->> (rp/query :projects)
|
||||||
(rx/map projects-fetched)))))
|
(rx/map projects-fetched)))))
|
||||||
|
|
||||||
|
;; --- Fetch Files
|
||||||
|
|
||||||
|
(declare files-fetched)
|
||||||
|
|
||||||
|
(defn fetch-files
|
||||||
|
[project-id]
|
||||||
|
(ptk/reify ::fetch-files
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(->> (rp/query :project-files {:project-id project-id})
|
||||||
|
(rx/map files-fetched)))))
|
||||||
|
|
||||||
|
;; --- Fetch File (by ID)
|
||||||
|
|
||||||
|
(defn fetch-file
|
||||||
|
[id]
|
||||||
|
(s/assert ::us/uuid id)
|
||||||
|
(ptk/reify ::fetch-file
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(->> (rp/query :project-file {:id id})
|
||||||
|
(rx/map #(files-fetched [%]))))))
|
||||||
|
|
||||||
|
;; --- Files Fetched
|
||||||
|
|
||||||
|
(s/def ::files any?)
|
||||||
|
|
||||||
|
(defn files-fetched
|
||||||
|
[files]
|
||||||
|
(s/assert ::files files)
|
||||||
|
(ptk/reify ::files-fetched
|
||||||
|
cljs.core/IDeref
|
||||||
|
(-deref [_] files)
|
||||||
|
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(let [assoc-file #(assoc-in %1 [:files (:id %2)] %2)]
|
||||||
|
(reduce assoc-file state files)))))
|
||||||
|
|
||||||
;; --- Project Persisted
|
;; --- Project Persisted
|
||||||
|
|
||||||
(defrecord ProjectPersisted [data]
|
(defrecord ProjectPersisted [data]
|
||||||
|
@ -149,43 +212,38 @@
|
||||||
|
|
||||||
;; --- Create Project
|
;; --- Create Project
|
||||||
|
|
||||||
(s/def ::create-project-params
|
(declare project-created)
|
||||||
(s/keys :req-un [::name ::width ::height]))
|
|
||||||
|
(s/def ::create-project
|
||||||
|
(s/keys :req-un [::name]))
|
||||||
|
|
||||||
(defn create-project
|
(defn create-project
|
||||||
[{:keys [name] :as params}]
|
[{:keys [name] :as params}]
|
||||||
(s/assert ::create-project-params params)
|
(s/assert ::create-project params)
|
||||||
(reify
|
(ptk/reify ::create-project
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [this state stream]
|
(watch [this state stream]
|
||||||
#_(->> (rp/req :create/project {:name name})
|
(->> (rp/mutation :create-project {:name name})
|
||||||
(rx/map :payload)
|
(rx/map project-created)))))
|
||||||
(rx/mapcat (fn [{:keys [id] :as project}]
|
|
||||||
(rx/of #(assoc-project % project)
|
;; --- Project Created
|
||||||
(udp/form->create-page (assoc params :project id)))))))))
|
|
||||||
|
(defn project-created
|
||||||
|
[data]
|
||||||
|
(ptk/reify ::project-created
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(assoc-project state data))))
|
||||||
|
|
||||||
;; --- Go To Project
|
;; --- Go To Project
|
||||||
|
|
||||||
(defn go-to
|
(defn go-to
|
||||||
[id]
|
[file-id]
|
||||||
(s/assert ::us/uuid id)
|
(s/assert ::us/uuid file-id)
|
||||||
(ptk/reify ::go-to
|
(ptk/reify ::go-to
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(let [page-ids (get-in state [:projects id :pages])]
|
(let [page-ids (get-in state [:files file-id :pages])]
|
||||||
(let [params {:project id :page (first page-ids)}]
|
(let [path-params {:file-id file-id}
|
||||||
(rx/of (rt/nav :workspace/page params)))))))
|
query-params {:page-id (first page-ids)}]
|
||||||
|
(rx/of (rt/nav :workspace path-params query-params)))))))
|
||||||
|
|
||||||
;; --- Update Opts (Filtering & Ordering)
|
|
||||||
|
|
||||||
(defrecord UpdateOpts [order filter]
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(update-in state [:dashboard :projects] merge
|
|
||||||
(when order {:order order})
|
|
||||||
(when filter {:filter filter}))))
|
|
||||||
|
|
||||||
(defn update-opts
|
|
||||||
[& {:keys [order filter] :as opts}]
|
|
||||||
(UpdateOpts. order filter))
|
|
||||||
|
|
|
@ -106,12 +106,6 @@
|
||||||
(s/def ::set-of-uuid
|
(s/def ::set-of-uuid
|
||||||
(s/every ::us/uuid :kind set?))
|
(s/every ::us/uuid :kind set?))
|
||||||
|
|
||||||
;; --- Protocols
|
|
||||||
|
|
||||||
(defprotocol IPageDataUpdate
|
|
||||||
"A marker protocol for mark events that alters the
|
|
||||||
page and is subject to perform a backend synchronization.")
|
|
||||||
|
|
||||||
;; --- Expose inner functions
|
;; --- Expose inner functions
|
||||||
|
|
||||||
(defn interrupt? [e] (= e :interrupt))
|
(defn interrupt? [e] (= e :interrupt))
|
||||||
|
@ -136,70 +130,79 @@
|
||||||
|
|
||||||
(declare initialized)
|
(declare initialized)
|
||||||
(declare watch-page-changes)
|
(declare watch-page-changes)
|
||||||
(declare watch-events)
|
;; (declare watch-events)
|
||||||
|
|
||||||
(defn initialize
|
(defn initialize
|
||||||
"Initialize the workspace state."
|
"Initialize the workspace state."
|
||||||
[project-id page-id]
|
[file-id page-id]
|
||||||
(s/assert ::us/uuid project-id)
|
(s/assert ::us/uuid file-id)
|
||||||
(s/assert ::us/uuid page-id)
|
(s/assert ::us/uuid page-id)
|
||||||
(ptk/reify ::initialize
|
(ptk/reify ::initialize
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(-> state
|
(let [local (assoc workspace-default
|
||||||
(assoc :workspace-layout default-layout)
|
:file-id file-id
|
||||||
;; (update :workspace-layout
|
:page-id page-id)]
|
||||||
;; (fn [data]
|
(-> state
|
||||||
;; (if (nil? data) default-layout data)))
|
(assoc :workspace-layout default-layout)
|
||||||
(assoc :workspace-local
|
;; (update :workspace-layout
|
||||||
(assoc workspace-default :id page-id))))
|
;; (fn [data]
|
||||||
|
;; (if (nil? data) default-layout data)))
|
||||||
|
(assoc :workspace-local local))))
|
||||||
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
|
(prn "initialize" file-id page-id)
|
||||||
#_(when-not (get-in state [:pages page-id])
|
#_(when-not (get-in state [:pages page-id])
|
||||||
(reset! st/loader true))
|
(reset! st/loader true))
|
||||||
|
|
||||||
(rx/merge
|
(rx/merge
|
||||||
;; Stop possible previous watchers and re-fetch the main page
|
;; Stop possible previous watchers and re-fetch the main page
|
||||||
;; and all project related pages.
|
;; and all project related pages.
|
||||||
(rx/of ::stop-watcher
|
(rx/of ::stop-watcher
|
||||||
(udp/fetch-page page-id)
|
(udp/fetch-page page-id)
|
||||||
(udp/fetch-pages project-id))
|
(dp/fetch-file file-id)
|
||||||
|
(udp/fetch-pages file-id))
|
||||||
|
|
||||||
;; When main page is fetched, schedule the main initialization.
|
;; When main page is fetched, schedule the main initialization.
|
||||||
(->> (rx/filter udp/page-fetched? stream)
|
(->> (rx/zip (rx/filter (ptk/type? ::udp/page-fetched) stream)
|
||||||
|
(rx/filter (ptk/type? ::dp/files-fetched) stream))
|
||||||
(rx/take 1)
|
(rx/take 1)
|
||||||
(rx/do #(reset! st/loader false))
|
(rx/do #(reset! st/loader false))
|
||||||
(rx/mapcat #(rx/of (initialized page-id)
|
(rx/mapcat #(rx/of (initialized file-id page-id)
|
||||||
#_(initialize-alignment page-id))))
|
#_(initialize-alignment page-id))))
|
||||||
|
|
||||||
|
|
||||||
;; When workspace is initialized, run the event watchers.
|
;; When workspace is initialized, run the event watchers.
|
||||||
(->> (rx/filter (ptk/type? ::initialized) stream)
|
(->> (rx/filter (ptk/type? ::initialized) stream)
|
||||||
(rx/take 1)
|
(rx/take 1)
|
||||||
(rx/mapcat #(rx/of watch-page-changes
|
(rx/mapcat #(rx/of watch-page-changes)))))
|
||||||
watch-events)))))
|
|
||||||
ptk/EffectEvent
|
ptk/EffectEvent
|
||||||
(effect [_ state stream]
|
(effect [_ state stream]
|
||||||
;; Optimistic prefetch of projects if them are not already fetched
|
;; Optimistic prefetch of projects if them are not already fetched
|
||||||
(when-not (seq (:projects state))
|
#_(when-not (seq (:projects state))
|
||||||
(st/emit! (dp/fetch-projects))))))
|
(st/emit! (dp/fetch-projects))))))
|
||||||
|
|
||||||
(defn- initialized
|
(defn- initialized
|
||||||
[page-id]
|
[file-id page-id]
|
||||||
|
(s/assert ::us/uuid file-id)
|
||||||
(s/assert ::us/uuid page-id)
|
(s/assert ::us/uuid page-id)
|
||||||
(ptk/reify ::initialized
|
(ptk/reify ::initialized
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [page (get-in state [:pages page-id])
|
(let [file (get-in state [:files file-id])
|
||||||
|
page (get-in state [:pages page-id])
|
||||||
data (get-in state [:pages-data page-id])]
|
data (get-in state [:pages-data page-id])]
|
||||||
|
(prn "initialized" file)
|
||||||
(assoc state
|
(assoc state
|
||||||
|
:workspace-file file
|
||||||
:workspace-data data
|
:workspace-data data
|
||||||
:workspace-page page)))))
|
:workspace-page page)))))
|
||||||
|
|
||||||
;; --- Workspace Flags
|
;; --- Workspace Flags
|
||||||
|
|
||||||
(defn activate-flag
|
(defn activate-flag
|
||||||
[flag]
|
[flag]
|
||||||
(s/assert keyword? flag)
|
(s/assert keyword? flag)
|
||||||
(ptk/reify ::activate-flag
|
(ptk/reify ::activate-flag
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
|
@ -335,7 +338,6 @@
|
||||||
(CopyToClipboard.))
|
(CopyToClipboard.))
|
||||||
|
|
||||||
(defrecord PasteFromClipboard [id]
|
(defrecord PasteFromClipboard [id]
|
||||||
udp/IPageUpdate
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
state
|
state
|
||||||
|
@ -442,7 +444,7 @@
|
||||||
(defn add-shape
|
(defn add-shape
|
||||||
[data]
|
[data]
|
||||||
(ptk/reify ::add-shape
|
(ptk/reify ::add-shape
|
||||||
IPageDataUpdate
|
udp/IPageDataUpdate
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
;; TODO: revisit the `setup-proportions` seems unnecesary
|
;; TODO: revisit the `setup-proportions` seems unnecesary
|
||||||
|
@ -460,7 +462,7 @@
|
||||||
|
|
||||||
(def duplicate-selected
|
(def duplicate-selected
|
||||||
(ptk/reify ::duplicate-selected
|
(ptk/reify ::duplicate-selected
|
||||||
IPageDataUpdate
|
udp/IPageDataUpdate
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [selected (get-in state [:workspace-local :selected])
|
(let [selected (get-in state [:workspace-local :selected])
|
||||||
|
@ -485,7 +487,7 @@
|
||||||
[id]
|
[id]
|
||||||
(s/assert ::us/uuid id)
|
(s/assert ::us/uuid id)
|
||||||
(ptk/reify ::delete-shape
|
(ptk/reify ::delete-shape
|
||||||
IPageDataUpdate
|
udp/IPageDataUpdate
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [shape (get-in state [:workspace-data :shapes-by-id id])]
|
(let [shape (get-in state [:workspace-data :shapes-by-id id])]
|
||||||
|
@ -495,7 +497,7 @@
|
||||||
[ids]
|
[ids]
|
||||||
(s/assert ::us/set ids)
|
(s/assert ::us/set ids)
|
||||||
(ptk/reify ::delete-many-shapes
|
(ptk/reify ::delete-many-shapes
|
||||||
IPageDataUpdate
|
udp/IPageDataUpdate
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(reduce impl-dissoc-shape state
|
(reduce impl-dissoc-shape state
|
||||||
|
@ -656,7 +658,6 @@
|
||||||
;; --- Update Shape Position
|
;; --- Update Shape Position
|
||||||
|
|
||||||
(deftype UpdateShapePosition [id point]
|
(deftype UpdateShapePosition [id point]
|
||||||
udp/IPageUpdate
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(update-in state [:shapes id] geom/absolute-move point)))
|
(update-in state [:shapes id] geom/absolute-move point)))
|
||||||
|
@ -683,7 +684,6 @@
|
||||||
[id name]
|
[id name]
|
||||||
{:pre [(uuid? id) (string? name)]}
|
{:pre [(uuid? id) (string? name)]}
|
||||||
(ptk/reify ::rename-shape
|
(ptk/reify ::rename-shape
|
||||||
udp/IPageUpdate
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(assoc-in state [:shapes id :name] name))))
|
(assoc-in state [:shapes id :name] name))))
|
||||||
|
@ -722,7 +722,7 @@
|
||||||
[loc]
|
[loc]
|
||||||
(s/assert ::direction loc)
|
(s/assert ::direction loc)
|
||||||
(ptk/reify ::move-selected-layer
|
(ptk/reify ::move-selected-layer
|
||||||
IPageDataUpdate
|
udp/IPageDataUpdate
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [id (first (get-in state [:workspace-local :selected]))
|
(let [id (first (get-in state [:workspace-local :selected]))
|
||||||
|
@ -804,7 +804,6 @@
|
||||||
(update-in [:workspace-data :shapes-by-id id] dissoc :modifier-mtx))
|
(update-in [:workspace-data :shapes-by-id id] dissoc :modifier-mtx))
|
||||||
state)))]
|
state)))]
|
||||||
(ptk/reify ::materialize-current-modifier-in-bulk
|
(ptk/reify ::materialize-current-modifier-in-bulk
|
||||||
udp/IPageUpdate
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(reduce process-shape state ids)))))
|
(reduce process-shape state ids)))))
|
||||||
|
@ -890,7 +889,6 @@
|
||||||
;; TODO: revisit
|
;; TODO: revisit
|
||||||
|
|
||||||
(deftype UnlockShapeProportions [id]
|
(deftype UnlockShapeProportions [id]
|
||||||
udp/IPageUpdate
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(assoc-in state [:shapes id :proportion-lock] false)))
|
(assoc-in state [:shapes id :proportion-lock] false)))
|
||||||
|
@ -918,7 +916,6 @@
|
||||||
(s/assert ::us/uuid id)
|
(s/assert ::us/uuid id)
|
||||||
(s/assert ::update-dimensions dimensions)
|
(s/assert ::update-dimensions dimensions)
|
||||||
(ptk/reify ::update-dimensions
|
(ptk/reify ::update-dimensions
|
||||||
udp/IPageUpdate
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(update-in state [:shapes id] geom/resize-dim dimensions))))
|
(update-in state [:shapes id] geom/resize-dim dimensions))))
|
||||||
|
@ -927,7 +924,6 @@
|
||||||
|
|
||||||
;; TODO: revisit
|
;; TODO: revisit
|
||||||
(deftype UpdateInteraction [shape interaction]
|
(deftype UpdateInteraction [shape interaction]
|
||||||
udp/IPageUpdate
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [id (or (:id interaction)
|
(let [id (or (:id interaction)
|
||||||
|
@ -943,7 +939,6 @@
|
||||||
|
|
||||||
;; TODO: revisit
|
;; TODO: revisit
|
||||||
(deftype DeleteInteracton [shape id]
|
(deftype DeleteInteracton [shape id]
|
||||||
udp/IPageUpdate
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(update-in state [:shapes shape :interactions] dissoc id)))
|
(update-in state [:shapes shape :interactions] dissoc id)))
|
||||||
|
@ -1006,7 +1001,6 @@
|
||||||
(reduce impl-set-hidden $ (sequence xform shapes)))
|
(reduce impl-set-hidden $ (sequence xform shapes)))
|
||||||
$))))]
|
$))))]
|
||||||
(ptk/reify ::set-hidden-attr
|
(ptk/reify ::set-hidden-attr
|
||||||
udp/IPageUpdate
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(impl-set-hidden state id)))))
|
(impl-set-hidden state id)))))
|
||||||
|
@ -1030,7 +1024,6 @@
|
||||||
(reduce impl-set-blocked $ (sequence xform shapes)))
|
(reduce impl-set-blocked $ (sequence xform shapes)))
|
||||||
$))))]
|
$))))]
|
||||||
(ptk/reify ::set-blocked-attr
|
(ptk/reify ::set-blocked-attr
|
||||||
udp/IPageUpdate
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(impl-set-blocked state id)))))
|
(impl-set-blocked state id)))))
|
||||||
|
@ -1039,7 +1032,6 @@
|
||||||
|
|
||||||
;; TODO: revisit
|
;; TODO: revisit
|
||||||
(deftype LockShape [id]
|
(deftype LockShape [id]
|
||||||
udp/IPageUpdate
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(letfn [(mark-locked [state id]
|
(letfn [(mark-locked [state id]
|
||||||
|
@ -1058,7 +1050,6 @@
|
||||||
(LockShape. id))
|
(LockShape. id))
|
||||||
|
|
||||||
(deftype UnlockShape [id]
|
(deftype UnlockShape [id]
|
||||||
udp/IPageUpdate
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(letfn [(mark-unlocked [state id]
|
(letfn [(mark-unlocked [state id]
|
||||||
|
@ -1146,20 +1137,6 @@
|
||||||
#_(rx/of (udp/update-metadata id metadata)
|
#_(rx/of (udp/update-metadata id metadata)
|
||||||
(initialize-alignment id)))))
|
(initialize-alignment id)))))
|
||||||
|
|
||||||
;; (defrecord OpenView [page-id]
|
|
||||||
;; ptk/WatchEvent
|
|
||||||
;; (watch [_ state s]
|
|
||||||
;; (let [page-id (get-in state [:workspace :page])]
|
|
||||||
;; (rx/of (udp/persist-page page-id))))
|
|
||||||
|
|
||||||
;; ptk/EffectEvent
|
|
||||||
;; (effect [_ state s]
|
|
||||||
;; (let [rval (rand-int 1000000)
|
|
||||||
;; page (get-in state [:pages page-id])
|
|
||||||
;; project (get-in state [:projects (:project page)])
|
|
||||||
;; url (str cfg/viewurl "?v=" rval "#/preview/" (:share-token project) "/" page-id)]
|
|
||||||
;; (js/open url "new tab" ""))))
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Navigation
|
;; Navigation
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
@ -1173,53 +1150,21 @@
|
||||||
params {:project project-id :page (first page-ids)}]
|
params {:project project-id :page (first page-ids)}]
|
||||||
(rx/of (rt/nav :workspace/page params))))))
|
(rx/of (rt/nav :workspace/page params))))))
|
||||||
|
|
||||||
|
(defn go-to-page
|
||||||
|
[page-id]
|
||||||
|
(s/assert ::us/uuid page-id)
|
||||||
|
(ptk/reify ::go-to
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(let [file-id (get-in state [:workspace-local :file-id])
|
||||||
|
path-params {:file-id file-id}
|
||||||
|
query-params {:page-id page-id}]
|
||||||
|
(rx/of (rt/nav :workspace path-params query-params))))))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Page Changes Reactions
|
;; Page Changes Reactions
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(defn page-update?
|
|
||||||
[o]
|
|
||||||
(or (satisfies? IPageDataUpdate o)
|
|
||||||
(= ::page-data-update o)))
|
|
||||||
|
|
||||||
(defn page-persisted
|
|
||||||
[{:keys [id] :as page}]
|
|
||||||
(s/assert ::page-entity page)
|
|
||||||
(ptk/reify ::page-persisted
|
|
||||||
cljs.core/IDeref
|
|
||||||
(-deref [_] page)
|
|
||||||
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(let [data (:data page)
|
|
||||||
page (dissoc page :data)]
|
|
||||||
(-> state
|
|
||||||
(assoc :workspace-data data)
|
|
||||||
(assoc :workspace-page page)
|
|
||||||
(update :pages assoc id page)
|
|
||||||
(update :pages-data assoc id data))))))
|
|
||||||
|
|
||||||
(defn page-persisted?
|
|
||||||
[v]
|
|
||||||
(= ::page-persisted (ptk/type v)))
|
|
||||||
|
|
||||||
;; --- Persist Page
|
|
||||||
|
|
||||||
(def persist-page
|
|
||||||
(ptk/reify ::persist-page
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [this state s]
|
|
||||||
(let [local (:workspace-local state)
|
|
||||||
page (:workspace-page state)
|
|
||||||
data (:workspace-data state)]
|
|
||||||
(if (:history local)
|
|
||||||
(rx/empty)
|
|
||||||
(let [page (assoc page :data data)]
|
|
||||||
(->> (rp/mutation :update-page page)
|
|
||||||
(rx/map (fn [res] (merge page res)))
|
|
||||||
(rx/map page-persisted)
|
|
||||||
(rx/catch (fn [err] (rx/of ::page-persist-error))))))))))
|
|
||||||
|
|
||||||
;; --- Change Page Order (D&D Ordering)
|
;; --- Change Page Order (D&D Ordering)
|
||||||
|
|
||||||
(defn change-page-order
|
(defn change-page-order
|
||||||
|
@ -1236,7 +1181,7 @@
|
||||||
(assoc-in state [:projects (:project-id page) :pages] pages)))))
|
(assoc-in state [:projects (:project-id page) :pages] pages)))))
|
||||||
|
|
||||||
;; --- Delete Page
|
;; --- Delete Page
|
||||||
|
;; TODO: join with udp/delete-page
|
||||||
(defn delete-page
|
(defn delete-page
|
||||||
[id]
|
[id]
|
||||||
(s/assert ::us/uuid id)
|
(s/assert ::us/uuid id)
|
||||||
|
@ -1259,30 +1204,15 @@
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(let [stopper (rx/filter #(= % ::stop-watcher) stream)]
|
(let [stopper (rx/filter #(= % ::stop-watcher) stream)]
|
||||||
(->> stream
|
(->> stream
|
||||||
(rx/filter page-update?)
|
(rx/filter udp/page-update?)
|
||||||
(rx/debounce 500)
|
(rx/debounce 500)
|
||||||
(rx/mapcat #(rx/merge (rx/of rehash-shapes-relationships persist-page)
|
(rx/mapcat #(rx/merge (rx/of rehash-shapes-relationships udp/persist-current-page)
|
||||||
(->> (rx/filter page-persisted? stream)
|
(->> (rx/filter (ptk/type? ::udp/page-persisted) stream)
|
||||||
(rx/timeout 1000 (rx/empty))
|
(rx/timeout 1000 (rx/empty))
|
||||||
(rx/take 1)
|
(rx/take 1)
|
||||||
(rx/ignore))))
|
(rx/ignore))))
|
||||||
(rx/take-until stopper))))))
|
(rx/take-until stopper))))))
|
||||||
|
|
||||||
(def watch-events
|
|
||||||
(letfn [(on-page-renamed [event]
|
|
||||||
(rx/of #(update % :workspace-page assoc :name (:name @event))))]
|
|
||||||
(ptk/reify ::watch-events
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state stream]
|
|
||||||
(let [stopper (rx/filter #(= % ::stop-watcher) stream)
|
|
||||||
page-id (get-in state [:workspace-page :id])]
|
|
||||||
(->> stream
|
|
||||||
(rx/filter (ptk/type? ::udp/page-renamed))
|
|
||||||
(rx/filter #(= (:id (deref %)) page-id))
|
|
||||||
(rx/map on-page-renamed)
|
|
||||||
(rx/filter ptk/event?)
|
|
||||||
(rx/take-until stopper)))))))
|
|
||||||
|
|
||||||
;; (def watch-shapes-changes
|
;; (def watch-shapes-changes
|
||||||
;; (letfn [(look-for-changes [[old new]]
|
;; (letfn [(look-for-changes [[old new]]
|
||||||
;; (reduce-kv (fn [acc k v]
|
;; (reduce-kv (fn [acc k v]
|
||||||
|
@ -1299,7 +1229,7 @@
|
||||||
;; (watch [_ state stream]
|
;; (watch [_ state stream]
|
||||||
;; (let [stopper (rx/filter #(= % ::stop-page-watcher) stream)]
|
;; (let [stopper (rx/filter #(= % ::stop-page-watcher) stream)]
|
||||||
;; (->> stream
|
;; (->> stream
|
||||||
;; (rx/filter page-update?)
|
;; (rx/filter udp/page-update?)
|
||||||
;; (rx/debounce 1000)
|
;; (rx/debounce 1000)
|
||||||
;; (rx/mapcat #(rx/merge (rx/of persist-page
|
;; (rx/mapcat #(rx/merge (rx/of persist-page
|
||||||
;; (->> (rx/filter page-persisted? stream)
|
;; (->> (rx/filter page-persisted? stream)
|
||||||
|
|
|
@ -12,6 +12,9 @@
|
||||||
"ds.num-projects" ["No projects"
|
"ds.num-projects" ["No projects"
|
||||||
"%s project"
|
"%s project"
|
||||||
"%s projects"]
|
"%s projects"]
|
||||||
|
"ds.num-files" ["No files"
|
||||||
|
"%s file"
|
||||||
|
"%s files"]
|
||||||
"ds.project-title" "Your projects"
|
"ds.project-title" "Your projects"
|
||||||
"ds.project-new" "+ New project"
|
"ds.project-new" "+ New project"
|
||||||
"ds.project-thumbnail.alt" "Project title"
|
"ds.project-thumbnail.alt" "Project title"
|
||||||
|
|
|
@ -27,6 +27,10 @@
|
||||||
(-> (l/key :workspace-page)
|
(-> (l/key :workspace-page)
|
||||||
(l/derive st/state)))
|
(l/derive st/state)))
|
||||||
|
|
||||||
|
(def workspace-file
|
||||||
|
(-> (l/key :workspace-file)
|
||||||
|
(l/derive st/state)))
|
||||||
|
|
||||||
(def workspace-data
|
(def workspace-data
|
||||||
(-> (l/key :workspace-data)
|
(-> (l/key :workspace-data)
|
||||||
(l/derive st/state)))
|
(l/derive st/state)))
|
||||||
|
|
|
@ -53,11 +53,7 @@
|
||||||
nil))
|
nil))
|
||||||
|
|
||||||
(def initial-state
|
(def initial-state
|
||||||
{:dashboard {:project-order :name
|
{:route nil
|
||||||
:project-filter ""
|
|
||||||
:images-order :name
|
|
||||||
:images-filter ""}
|
|
||||||
:route nil
|
|
||||||
:router nil
|
:router nil
|
||||||
:auth (:auth storage)
|
:auth (:auth storage)
|
||||||
:profile (:profile storage)
|
:profile (:profile storage)
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
(:require
|
(:require
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
|
[expound.alpha :as expound]
|
||||||
[lentes.core :as l]
|
[lentes.core :as l]
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[expound.alpha :as expound]
|
|
||||||
[uxbox.builtins.icons :as i]
|
[uxbox.builtins.icons :as i]
|
||||||
[uxbox.main.data.auth :refer [logout]]
|
[uxbox.main.data.auth :refer [logout]]
|
||||||
[uxbox.main.data.projects :as dp]
|
[uxbox.main.data.projects :as dp]
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
[uxbox.main.ui.dashboard :as dashboard]
|
[uxbox.main.ui.dashboard :as dashboard]
|
||||||
[uxbox.main.ui.settings :as settings]
|
[uxbox.main.ui.settings :as settings]
|
||||||
[uxbox.main.ui.shapes]
|
[uxbox.main.ui.shapes]
|
||||||
[uxbox.main.ui.workspace :refer [workspace-page]]
|
[uxbox.main.ui.workspace :as workspace]
|
||||||
[uxbox.util.data :refer [parse-int uuid-str?]]
|
[uxbox.util.data :refer [parse-int uuid-str?]]
|
||||||
[uxbox.util.dom :as dom]
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.util.html.history :as html-history]
|
[uxbox.util.html.history :as html-history]
|
||||||
|
@ -38,17 +38,19 @@
|
||||||
["/register" :auth/register]
|
["/register" :auth/register]
|
||||||
["/recovery/request" :auth/recovery-request]
|
["/recovery/request" :auth/recovery-request]
|
||||||
["/recovery/token/:token" :auth/recovery]]
|
["/recovery/token/:token" :auth/recovery]]
|
||||||
|
|
||||||
["/settings"
|
["/settings"
|
||||||
["/profile" :settings/profile]
|
["/profile" :settings/profile]
|
||||||
["/password" :settings/password]
|
["/password" :settings/password]
|
||||||
["/notifications" :settings/notifications]]
|
["/notifications" :settings/notifications]]
|
||||||
|
|
||||||
["/dashboard"
|
["/dashboard"
|
||||||
["/projects" :dashboard/projects]
|
["/projects" :dashboard-projects]
|
||||||
["/elements" :dashboard/elements]
|
["/icons" :dashboard-icons]
|
||||||
["/icons" :dashboard/icons]
|
["/images" :dashboard-images]
|
||||||
["/images" :dashboard/images]
|
["/colors" :dashboard-colors]]
|
||||||
["/colors" :dashboard/colors]]
|
|
||||||
["/workspace/:project/:page" :workspace/page]])
|
["/workspace/:file-id" :workspace]])
|
||||||
|
|
||||||
;; --- Error Handling
|
;; --- Error Handling
|
||||||
|
|
||||||
|
@ -100,8 +102,8 @@
|
||||||
(case (get-in route [:data :name])
|
(case (get-in route [:data :name])
|
||||||
:auth/login (mf/element auth/login-page)
|
:auth/login (mf/element auth/login-page)
|
||||||
:auth/register (mf/element auth/register-page)
|
:auth/register (mf/element auth/register-page)
|
||||||
;; :auth/recovery-request (auth/recovery-request-page)
|
|
||||||
|
|
||||||
|
;; :auth/recovery-request (auth/recovery-request-page)
|
||||||
;; :auth/recovery
|
;; :auth/recovery
|
||||||
;; (let [token (get-in route [:params :path :token])]
|
;; (let [token (get-in route [:params :path :token])]
|
||||||
;; (auth/recovery-page token))
|
;; (auth/recovery-page token))
|
||||||
|
@ -111,18 +113,19 @@
|
||||||
:settings/notifications)
|
:settings/notifications)
|
||||||
(mf/element settings/settings #js {:route route})
|
(mf/element settings/settings #js {:route route})
|
||||||
|
|
||||||
(:dashboard/projects
|
:dashboard-projects
|
||||||
:dashboard/icons
|
(mf/element dashboard/dashboard-projects #js {:route route})
|
||||||
:dashboard/images
|
|
||||||
:dashboard/colors)
|
|
||||||
(mf/element dashboard/dashboard #js {:route route})
|
|
||||||
|
|
||||||
:workspace/page
|
(:dashboard-icons
|
||||||
(let [project-id (uuid (get-in route [:params :path :project]))
|
:dashboard-images
|
||||||
page-id (uuid (get-in route [:params :path :page]))]
|
:dashboard-colors)
|
||||||
[:& workspace-page {:project-id project-id
|
(mf/element dashboard/dashboard-assets #js {:route route})
|
||||||
:page-id page-id
|
|
||||||
:key page-id}])
|
|
||||||
|
|
||||||
|
:workspace
|
||||||
|
(let [file-id (uuid (get-in route [:params :path :file-id]))
|
||||||
|
page-id (uuid (get-in route [:params :query :page-id]))]
|
||||||
|
[:& workspace/workspace {:file-id file-id
|
||||||
|
:page-id page-id
|
||||||
|
:key file-id}])
|
||||||
nil)))
|
nil)))
|
||||||
|
|
||||||
|
|
|
@ -11,19 +11,25 @@
|
||||||
[uxbox.main.ui.dashboard.colors :as colors]
|
[uxbox.main.ui.dashboard.colors :as colors]
|
||||||
[uxbox.main.ui.messages :refer [messages-widget]]))
|
[uxbox.main.ui.messages :refer [messages-widget]]))
|
||||||
|
|
||||||
(defn- parse-route
|
(mf/defc dashboard-projects
|
||||||
[{:keys [params data] :as route}]
|
|
||||||
(let [{:keys [id type]} (:query params)
|
|
||||||
id (cond
|
|
||||||
(str/digits? id) (parse-int id)
|
|
||||||
(uuid-str? id) (uuid id)
|
|
||||||
:else nil)
|
|
||||||
type (when (str/alpha? type) (keyword type))]
|
|
||||||
[(:name data) type id]))
|
|
||||||
|
|
||||||
(mf/defc dashboard
|
|
||||||
[{:keys [route] :as props}]
|
[{:keys [route] :as props}]
|
||||||
(let [[section type id] (parse-route route)]
|
(let [id (get-in route [:params :query :project-id])
|
||||||
|
id (when (uuid-str? id) (uuid id))]
|
||||||
|
[:main.dashboard-main
|
||||||
|
[:& messages-widget]
|
||||||
|
[:& header {:section :dashboard/projects}]
|
||||||
|
[:& projects/projects-page {:id id}]]))
|
||||||
|
|
||||||
|
(mf/defc dashboard-assets
|
||||||
|
[{:keys [route] :as props}]
|
||||||
|
(let [section (:name route)
|
||||||
|
{:keys [id type]} (get-in route [:params :query])
|
||||||
|
id (cond
|
||||||
|
;; (str/digits? id) (parse-int id)
|
||||||
|
(uuid-str? id) (uuid id)
|
||||||
|
(str/empty-or-nil? id) nil
|
||||||
|
:else id)
|
||||||
|
type (when (str/alpha? type) (keyword type))]
|
||||||
[:main.dashboard-main
|
[:main.dashboard-main
|
||||||
[:& messages-widget]
|
[:& messages-widget]
|
||||||
[:& header {:section section}]
|
[:& header {:section section}]
|
||||||
|
@ -34,8 +40,5 @@
|
||||||
:dashboard/images
|
:dashboard/images
|
||||||
[:& images/images-page {:type type :id id}]
|
[:& images/images-page {:type type :id id}]
|
||||||
|
|
||||||
:dashboard/projects
|
|
||||||
[:& projects/projects-page]
|
|
||||||
|
|
||||||
:dashboard/colors
|
:dashboard/colors
|
||||||
[:& colors/colors-page {:type type :id id}])]))
|
[:& colors/colors-page {:type type :id id}])]))
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
(-> (l/in [:dashboard :icons])
|
(-> (l/in [:dashboard :icons])
|
||||||
(l/derive st/state)))
|
(l/derive st/state)))
|
||||||
|
|
||||||
;; --- Page Title
|
;; --- Component: Grid Header
|
||||||
|
|
||||||
(mf/defc grid-header
|
(mf/defc grid-header
|
||||||
[{:keys [coll] :as props}]
|
[{:keys [coll] :as props}]
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||||
|
|
||||||
(ns uxbox.main.ui.dashboard.projects
|
(ns uxbox.main.ui.dashboard.projects
|
||||||
|
(:refer-clojure :exclude [sort-by])
|
||||||
(:require
|
(:require
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[lentes.core :as l]
|
[lentes.core :as l]
|
||||||
|
@ -18,10 +19,11 @@
|
||||||
[uxbox.main.ui.keyboard :as kbd]
|
[uxbox.main.ui.keyboard :as kbd]
|
||||||
[uxbox.main.ui.confirm :refer [confirm-dialog]]
|
[uxbox.main.ui.confirm :refer [confirm-dialog]]
|
||||||
[uxbox.main.ui.dashboard.projects-forms :refer [create-project-dialog]]
|
[uxbox.main.ui.dashboard.projects-forms :refer [create-project-dialog]]
|
||||||
|
[uxbox.main.ui.dashboard.common :as common]
|
||||||
[uxbox.util.data :refer [read-string]]
|
[uxbox.util.data :refer [read-string]]
|
||||||
[uxbox.util.dom :as dom]
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.util.i18n :as t :refer [tr]]
|
[uxbox.util.i18n :as t :refer [tr]]
|
||||||
[uxbox.util.router :as r]
|
[uxbox.util.router :as rt]
|
||||||
[uxbox.util.time :as dt]))
|
[uxbox.util.time :as dt]))
|
||||||
|
|
||||||
;; --- Helpers & Constants
|
;; --- Helpers & Constants
|
||||||
|
@ -33,7 +35,7 @@
|
||||||
;; --- Refs
|
;; --- Refs
|
||||||
|
|
||||||
(def opts-iref
|
(def opts-iref
|
||||||
(-> (l/in [:dashboard :projects])
|
(-> (l/key :dashboard-projects)
|
||||||
(l/derive st/state)))
|
(l/derive st/state)))
|
||||||
|
|
||||||
(def projects-iref
|
(def projects-iref
|
||||||
|
@ -42,160 +44,246 @@
|
||||||
|
|
||||||
;; --- Helpers
|
;; --- Helpers
|
||||||
|
|
||||||
(defn sort-projects-by
|
(defn sort-by
|
||||||
[ordering projs]
|
[ordering files]
|
||||||
(case ordering
|
(case ordering
|
||||||
:name (sort-by :name projs)
|
:name (cljs.core/sort-by :name files)
|
||||||
:created (reverse (sort-by :created-at projs))
|
:created (reverse (cljs.core/sort-by :created-at files))
|
||||||
projs))
|
files))
|
||||||
|
|
||||||
(defn contains-term?
|
(defn contains-term?
|
||||||
[phrase term]
|
[phrase term]
|
||||||
(let [term (name term)]
|
(let [term (name term)]
|
||||||
(str/includes? (str/lower phrase) (str/trim (str/lower term)))))
|
(str/includes? (str/lower phrase) (str/trim (str/lower term)))))
|
||||||
|
|
||||||
(defn filter-projects-by
|
(defn filter-by
|
||||||
[term projs]
|
[term files]
|
||||||
(if (str/blank? term)
|
(if (str/blank? term)
|
||||||
projs
|
files
|
||||||
(filter #(contains-term? (:name %) term) projs)))
|
(filter #(contains-term? (:name %) term) files)))
|
||||||
|
|
||||||
;; --- Menu (Filter & Sort)
|
;; --- Menu (Filter & Sort)
|
||||||
|
|
||||||
(mf/def menu
|
(mf/defc menu
|
||||||
:mixins #{mf/memo mf/reactive}
|
[{:keys [opts files] :as props}]
|
||||||
:init
|
(let [ordering (:order opts :created)
|
||||||
(fn [own props]
|
filtering (:filter opts "")
|
||||||
(assoc own ::num-projects (-> (comp (l/key :projects)
|
|
||||||
(l/lens #(-> % vals count)))
|
|
||||||
(l/derive st/state))))
|
|
||||||
:render
|
|
||||||
(fn [own {:keys [opts] :as props}]
|
|
||||||
(let [ordering (:order opts :created)
|
|
||||||
filtering (:filter opts "")
|
|
||||||
num-projects (mf/react (::num-projects own))]
|
|
||||||
(letfn [(on-term-change [event]
|
|
||||||
(let [term (-> (dom/get-target event)
|
|
||||||
(dom/get-value))]
|
|
||||||
(st/emit! (udp/update-opts :filter term))))
|
|
||||||
(on-ordering-change [event]
|
|
||||||
(let [value (dom/event->value event)
|
|
||||||
value (read-string value)]
|
|
||||||
(st/emit! (udp/update-opts :order value))))
|
|
||||||
(on-clear [event]
|
|
||||||
(st/emit! (udp/update-opts :filter "")))]
|
|
||||||
[:section.dashboard-bar
|
|
||||||
[:div.dashboard-info
|
|
||||||
|
|
||||||
;; Counter
|
on-term-change
|
||||||
[:span.dashboard-images (tr "ds.num-projects" (t/c num-projects))]
|
(fn [event]
|
||||||
|
(let [term (-> (dom/get-target event)
|
||||||
|
(dom/get-value))]
|
||||||
|
(st/emit! (udp/update-opts :filter term))))
|
||||||
|
|
||||||
;; Sorting
|
on-order-change
|
||||||
[:div
|
(fn [event]
|
||||||
[:span (tr "ds.ordering")]
|
(let [value (dom/event->value event)
|
||||||
[:select.input-select
|
value (read-string value)]
|
||||||
{:on-change on-ordering-change
|
(st/emit! (udp/update-opts :order value))))
|
||||||
:value (pr-str ordering)}
|
|
||||||
(for [[key value] (seq +ordering-options+)]
|
on-clear
|
||||||
(let [key (pr-str key)]
|
(fn [event]
|
||||||
[:option {:key key :value key} (tr value)]))]]
|
(st/emit! (udp/update-opts :filter "")))]
|
||||||
;; Search
|
|
||||||
[:form.dashboard-search
|
[:section.dashboard-bar.library-gap
|
||||||
[:input.input-text
|
[:div.dashboard-info
|
||||||
{:key :images-search-box
|
|
||||||
:type "text"
|
;; Counter
|
||||||
:on-change on-term-change
|
[:span.dashboard-images (tr "ds.num-files" (t/c (count files)))]
|
||||||
:auto-focus true
|
|
||||||
:placeholder (tr "ds.search.placeholder")
|
[:div
|
||||||
:value (or filtering "")}]
|
;; Sorting
|
||||||
[:div.clear-search {:on-click on-clear} i/close]]]]))))
|
;; TODO: convert to separate component?
|
||||||
|
[:span (tr "ds.ordering")]
|
||||||
|
[:select.input-select {:on-change on-order-change
|
||||||
|
:value (pr-str ordering)}
|
||||||
|
(for [[key value] (seq +ordering-options+)]
|
||||||
|
(let [key (pr-str key)]
|
||||||
|
[:option {:key key :value key} (tr value)]))]]
|
||||||
|
|
||||||
|
;; Search
|
||||||
|
;; TODO: convert to separate component?
|
||||||
|
[:form.dashboard-search
|
||||||
|
[:input.input-text
|
||||||
|
{:key :images-search-box
|
||||||
|
:type "text"
|
||||||
|
:on-change on-term-change
|
||||||
|
:auto-focus true
|
||||||
|
:placeholder (tr "ds.search.placeholder")
|
||||||
|
:value (or filtering "")}]
|
||||||
|
[:div.clear-search {:on-click on-clear} i/close]]]]))
|
||||||
|
|
||||||
;; --- Grid Item Thumbnail
|
;; --- Grid Item Thumbnail
|
||||||
|
|
||||||
(mf/defc grid-item-thumbnail
|
(mf/defc grid-item-thumbnail
|
||||||
[{:keys [project] :as props}]
|
[{:keys [project] :as props}]
|
||||||
[:div.grid-item-th
|
[:div.grid-item-th
|
||||||
[:img.img-th {:src "/images/project-placeholder.svg"
|
[:img.img-th {:src "/images/project-placeholder.svg"}]])
|
||||||
:alt (tr "ds.project-thumbnail.alt")}]])
|
|
||||||
|
|
||||||
;; --- Grid Item
|
;; --- Grid Item
|
||||||
|
|
||||||
(mf/defc grid-item
|
(mf/defc grid-item
|
||||||
{:wrap [mf/wrap-memo]}
|
{:wrap [mf/wrap-memo]}
|
||||||
[{:keys [project] :as props}]
|
[{:keys [file] :as props}]
|
||||||
(let [local (mf/use-state {})
|
(let [local (mf/use-state {})
|
||||||
on-navigate #(st/emit! (udp/go-to (:id project)))
|
on-navigate #(st/emit! (udp/go-to (:id file)))
|
||||||
delete #(st/emit! (udp/delete-project project))
|
;; delete #(st/emit! (udp/delete-project project))
|
||||||
on-delete #(do
|
;; on-delete #(do
|
||||||
(dom/stop-propagation %)
|
;; (dom/stop-propagation %)
|
||||||
(modal/show! confirm-dialog {:on-accept delete}))
|
;; (modal/show! confirm-dialog {:on-accept delete}))
|
||||||
on-blur #(let [target (dom/event->target %)
|
;; on-blur #(let [target (dom/event->target %)
|
||||||
name (dom/get-value target)
|
;; name (dom/get-value target)
|
||||||
id (:id project)]
|
;; id (:id project)]
|
||||||
(swap! local assoc :edition false)
|
;; (swap! local assoc :edition false)
|
||||||
(st/emit! (udp/rename-project id name)))
|
;; (st/emit! (udp/rename-project id name)))
|
||||||
on-key-down #(when (kbd/enter? %) (on-blur %))
|
;; on-key-down #(when (kbd/enter? %) (on-blur %))
|
||||||
on-edit #(do
|
;; on-edit #(do
|
||||||
(dom/stop-propagation %)
|
;; (dom/stop-propagation %)
|
||||||
(dom/prevent-default %)
|
;; (dom/prevent-default %)
|
||||||
(swap! local assoc :edition true))]
|
;; (swap! local assoc :edition true))
|
||||||
|
]
|
||||||
[:div.grid-item.project-th {:on-click on-navigate}
|
[:div.grid-item.project-th {:on-click on-navigate}
|
||||||
[:& grid-item-thumbnail {:project project
|
[:& grid-item-thumbnail {:file file}]
|
||||||
:key (select-keys project [:id :page-id])}]
|
|
||||||
[:div.item-info
|
[:div.item-info
|
||||||
(if (:edition @local)
|
(if (:edition @local)
|
||||||
[:input.element-name {:type "text"
|
[:input.element-name {:type "text"
|
||||||
:auto-focus true
|
:auto-focus true
|
||||||
:on-key-down on-key-down
|
;; :on-key-down on-key-down
|
||||||
:on-blur on-blur
|
;; :on-blur on-blur
|
||||||
:on-click on-edit
|
;; :on-click on-edit
|
||||||
:default-value (:name project)}]
|
:default-value (:name file)}]
|
||||||
[:h3 (:name project)])
|
[:h3 (:name file)])
|
||||||
[:span.date
|
[:span.date
|
||||||
(str (tr "ds.updated-at" (dt/timeago (:modified-at project))))]]
|
(str (tr "ds.updated-at" (dt/timeago (:modified-at file))))]]
|
||||||
|
|
||||||
[:div.project-th-actions
|
[:div.project-th-actions
|
||||||
[:div.project-th-icon.pages
|
[:div.project-th-icon.pages
|
||||||
i/page
|
i/page
|
||||||
[:span (:total-pages project)]]
|
#_[:span (:total-pages project)]]
|
||||||
#_[:div.project-th-icon.comments
|
#_[:div.project-th-icon.comments
|
||||||
i/chat
|
i/chat
|
||||||
[:span "0"]]
|
[:span "0"]]
|
||||||
[:div.project-th-icon.edit
|
[:div.project-th-icon.edit
|
||||||
{:on-click on-edit}
|
#_{:on-click on-edit}
|
||||||
i/pencil]
|
i/pencil]
|
||||||
[:div.project-th-icon.delete
|
[:div.project-th-icon.delete
|
||||||
{:on-click on-delete}
|
#_{:on-click on-delete}
|
||||||
i/trash]]]))
|
i/trash]]]))
|
||||||
|
|
||||||
;; --- Grid
|
;; --- Grid
|
||||||
|
|
||||||
(mf/defc grid
|
(mf/defc grid
|
||||||
[{:keys [opts] :as props}]
|
[{:keys [opts files] :as props}]
|
||||||
(let [order (:order opts :created)
|
(let [order (:order opts :created)
|
||||||
filter (:filter opts "")
|
filter (:filter opts "")
|
||||||
projects (mf/deref projects-iref)
|
files (->> files
|
||||||
projects (->> (vals projects)
|
(filter-by filter)
|
||||||
(filter-projects-by filter)
|
(sort-by order))
|
||||||
(sort-projects-by order))
|
|
||||||
on-click #(do
|
on-click #(do
|
||||||
(dom/prevent-default %)
|
(dom/prevent-default %)
|
||||||
(modal/show! create-project-dialog {})
|
#_(modal/show! create-project-dialog {})
|
||||||
#_(udl/open! :create-project))]
|
#_(udl/open! :create-project))
|
||||||
|
]
|
||||||
[:section.dashboard-grid
|
[:section.dashboard-grid
|
||||||
[:h2 (tr "ds.project-title")]
|
[:h2 (tr "ds.projects.file-name")]
|
||||||
[:div.dashboard-grid-content
|
[:div.dashboard-grid-content
|
||||||
[:div.dashboard-grid-row
|
[:div.dashboard-grid-row
|
||||||
[:div.grid-item.add-project {:on-click on-click}
|
[:div.grid-item.add-project #_{:on-click on-click}
|
||||||
[:span (tr "ds.project-new")]]
|
[:span (tr "ds.project-file")]]
|
||||||
|
(for [item files]
|
||||||
|
[:& grid-item {:file item :key (:id item)}])]]]))
|
||||||
|
|
||||||
|
;; --- Component: Nav
|
||||||
|
|
||||||
|
;; (letfn [(on-click [event]
|
||||||
|
;; #_(let [type (or type :own)]
|
||||||
|
;; (st/emit! (rt/nav :dashboard/icons {} {:type type :id id}))))
|
||||||
|
;; (on-input-change [event]
|
||||||
|
;; #_(-> (dom/get-target event)
|
||||||
|
;; (dom/get-value)
|
||||||
|
;; (swap! local assoc :name)))
|
||||||
|
;; (on-cancel [event]
|
||||||
|
;; #_(swap! local dissoc :name :edit))
|
||||||
|
;; (on-double-click [event]
|
||||||
|
;; #_(when editable?
|
||||||
|
;; (swap! local assoc :edit true)))
|
||||||
|
;; (on-input-keyup [event]
|
||||||
|
;; #_(when (kbd/enter? event)
|
||||||
|
;; (let [value (-> (dom/get-target event) (dom/get-value))]
|
||||||
|
;; (st/emit! (di/rename-collection id (str/trim (:name @local))))
|
||||||
|
;; (swap! local assoc :edit false))))]
|
||||||
|
|
||||||
|
(mf/defc nav-item
|
||||||
|
[{:keys [id name selected?] :as props}]
|
||||||
|
(let [local (mf/use-state {})
|
||||||
|
editable? (not (nil? id))
|
||||||
|
on-click (fn [event]
|
||||||
|
(st/emit! (rt/nav :dashboard-projects {} {:project-id (str id)})))]
|
||||||
|
[:li {:on-click on-click
|
||||||
|
;; :on-double-click on-double-click
|
||||||
|
:class-name (when selected? "current")}
|
||||||
|
(if (:edit @local)
|
||||||
|
[:div
|
||||||
|
[:input.element-title #_{:value (if (:name @local)
|
||||||
|
(:name @local)
|
||||||
|
(if id name "Storage"))
|
||||||
|
:on-change on-input-change
|
||||||
|
:on-key-down on-input-keyup}]
|
||||||
|
[:span.close #_{:on-click on-cancel} i/close]]
|
||||||
|
[:span.element-title (if id name "Recent")])
|
||||||
|
#_[:span.element-subtitle (tr "ds.num-elements" (t/c num-icons))]
|
||||||
|
]))
|
||||||
|
|
||||||
|
|
||||||
|
(mf/defc nav
|
||||||
|
[{:keys [id] :as props}]
|
||||||
|
(let [projects (->> (mf/deref projects-iref)
|
||||||
|
(vals)
|
||||||
|
(sort-by :created-at))]
|
||||||
|
[:div.library-bar
|
||||||
|
[:div.library-bar-inside
|
||||||
|
[:ul.library-elements
|
||||||
|
[:li
|
||||||
|
[:a.btn-primary #_{:on-click #(st/emit! di/create-collection)}
|
||||||
|
"new project +"]]
|
||||||
|
|
||||||
|
[:li {:style {:marginBottom "20px"}
|
||||||
|
:on-click #(st/emit! (rt/nav :dashboard/projects {} {}))
|
||||||
|
:class-name (when (nil? id) "current")}
|
||||||
|
[:span.element-title "Recent"]]
|
||||||
|
|
||||||
(for [item projects]
|
(for [item projects]
|
||||||
[:& grid-item {:project item :key (:id item)}])]]]))
|
[:& nav-item {:id (:id item)
|
||||||
|
:key (:id item)
|
||||||
|
:name (:name item)
|
||||||
|
:selected? (= (:id item) id)}])]]]))
|
||||||
|
|
||||||
|
;; --- Component: Content
|
||||||
|
|
||||||
|
(def files-ref
|
||||||
|
(letfn [(selector [state]
|
||||||
|
(let [id (get-in state [:dashboard-projects :id])
|
||||||
|
ids (get-in state [:dashboard-projects :files id])]
|
||||||
|
(mapv #(get-in state [:files %]) ids)))]
|
||||||
|
(-> (l/lens selector)
|
||||||
|
(l/derive st/state))))
|
||||||
|
|
||||||
|
(mf/defc content
|
||||||
|
[{:keys [id] :as props}]
|
||||||
|
(let [opts (mf/deref opts-iref)
|
||||||
|
files (mf/deref files-ref)]
|
||||||
|
[:*
|
||||||
|
[:& menu {:opts opts :files files}]
|
||||||
|
[:section.dashboard-grid.library
|
||||||
|
[:& grid {:id id :opts opts :files files}]]]))
|
||||||
|
|
||||||
;; --- Projects Page
|
;; --- Projects Page
|
||||||
|
|
||||||
(mf/defc projects-page
|
(mf/defc projects-page
|
||||||
[_]
|
[{:keys [id] :as props}]
|
||||||
(mf/use-effect #(st/emit! (udp/initialize)))
|
(mf/use-effect #(st/emit! udp/fetch-projects))
|
||||||
(let [opts (mf/deref opts-iref)]
|
(mf/use-effect {:fn #(st/emit! (udp/initialize id))
|
||||||
[:section.dashboard-content
|
:deps #js [id]})
|
||||||
[:& menu {:opts opts}]
|
[:section.dashboard-content
|
||||||
[:& grid {:opts opts}]]))
|
[:& nav {:id id}]
|
||||||
|
[:& content {:id id}]])
|
||||||
|
|
|
@ -22,12 +22,9 @@
|
||||||
(s/def ::height ::fm/number-str)
|
(s/def ::height ::fm/number-str)
|
||||||
|
|
||||||
(s/def ::project-form
|
(s/def ::project-form
|
||||||
(s/keys :req-un [::name ::width ::height]))
|
(s/keys :req-un [::name]))
|
||||||
|
|
||||||
(def defaults
|
(def defaults {:name ""})
|
||||||
{:name ""
|
|
||||||
:width "1366"
|
|
||||||
:height "768"})
|
|
||||||
|
|
||||||
;; --- Create Project Form
|
;; --- Create Project Form
|
||||||
|
|
||||||
|
@ -57,33 +54,6 @@
|
||||||
:on-blur (fm/on-input-blur form :name)
|
:on-blur (fm/on-input-blur form :name)
|
||||||
:on-change (fm/on-input-change form :name)
|
:on-change (fm/on-input-change form :name)
|
||||||
:auto-focus true}]
|
:auto-focus true}]
|
||||||
[:div.project-size
|
|
||||||
[:div.input-element.pixels
|
|
||||||
[:span (tr "ds.width")]
|
|
||||||
[:input#project-witdh.input-text
|
|
||||||
{:placeholder (tr "ds.width")
|
|
||||||
:name "width"
|
|
||||||
:type "number"
|
|
||||||
:min 0
|
|
||||||
:max 5000
|
|
||||||
:class (fm/error-class form :width)
|
|
||||||
:on-blur (fm/on-input-blur form :width)
|
|
||||||
:on-change (fm/on-input-change form :width)
|
|
||||||
:value (:width data)}]]
|
|
||||||
[:a.toggle-layout {:on-click #(swap-size % form)} i/toggle]
|
|
||||||
[:div.input-element.pixels
|
|
||||||
[:span (tr "ds.height")]
|
|
||||||
[:input#project-height.input-text
|
|
||||||
{:placeholder (tr "ds.height")
|
|
||||||
:type "number"
|
|
||||||
:name "height"
|
|
||||||
:min 0
|
|
||||||
:max 5000
|
|
||||||
:class (fm/error-class form :height)
|
|
||||||
:on-blur (fm/on-input-blur form :height)
|
|
||||||
:on-change (fm/on-input-change form :height)
|
|
||||||
:value (:height data)}]]]
|
|
||||||
|
|
||||||
;; Submit
|
;; Submit
|
||||||
[:input#project-btn.btn-primary
|
[:input#project-btn.btn-primary
|
||||||
{:value (tr "ds.go")
|
{:value (tr "ds.go")
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
(scroll/scroll-to-point dom mouse-point scroll-position))))
|
(scroll/scroll-to-point dom mouse-point scroll-position))))
|
||||||
|
|
||||||
(mf/defc workspace-content
|
(mf/defc workspace-content
|
||||||
[{:keys [layout page] :as params}]
|
[{:keys [layout page file] :as params}]
|
||||||
(let [canvas (mf/use-ref nil)
|
(let [canvas (mf/use-ref nil)
|
||||||
left-sidebar? (not (empty? (keep layout [:layers :sitemap
|
left-sidebar? (not (empty? (keep layout [:layers :sitemap
|
||||||
:document-history])))
|
:document-history])))
|
||||||
|
@ -89,17 +89,26 @@
|
||||||
|
|
||||||
;; Aside
|
;; Aside
|
||||||
(when left-sidebar?
|
(when left-sidebar?
|
||||||
[:& left-sidebar {:page page :layout layout}])
|
[:& left-sidebar {:file file :page page :layout layout}])
|
||||||
(when right-sidebar?
|
(when right-sidebar?
|
||||||
[:& right-sidebar {:page page :layout layout}])]))
|
[:& right-sidebar {:page page :layout layout}])]))
|
||||||
|
|
||||||
(mf/defc workspace
|
(mf/defc workspace
|
||||||
[{:keys [page-id] :as props}]
|
[{:keys [file-id page-id] :as props}]
|
||||||
(let [layout (mf/deref refs/workspace-layout)
|
|
||||||
flags (mf/deref refs/selected-flags)
|
|
||||||
page (mf/deref refs/workspace-page)]
|
|
||||||
|
|
||||||
[:*
|
(mf/use-effect
|
||||||
|
{:deps #js [file-id page-id]
|
||||||
|
:fn (fn []
|
||||||
|
(let [sub (shortcuts/init)]
|
||||||
|
(st/emit! (udw/initialize file-id page-id))
|
||||||
|
#(rx/cancel! sub)))})
|
||||||
|
|
||||||
|
(let [layout (mf/deref refs/workspace-layout)
|
||||||
|
file (mf/deref refs/workspace-file)
|
||||||
|
page (mf/deref refs/workspace-page)
|
||||||
|
flags (mf/deref refs/selected-flags)]
|
||||||
|
|
||||||
|
[:> rdnd/provider {:backend rdnd/html5}
|
||||||
[:& messages-widget]
|
[:& messages-widget]
|
||||||
[:& header {:page page :flags flags}]
|
[:& header {:page page :flags flags}]
|
||||||
|
|
||||||
|
@ -107,17 +116,6 @@
|
||||||
[:& colorpalette])
|
[:& colorpalette])
|
||||||
|
|
||||||
(when (and layout page)
|
(when (and layout page)
|
||||||
[:& workspace-content {:layout layout :page page}])]))
|
[:& workspace-content {:layout layout
|
||||||
|
:file file
|
||||||
(mf/defc workspace-page
|
:page page}])]))
|
||||||
[{:keys [project-id page-id] :as props}]
|
|
||||||
|
|
||||||
(mf/use-effect
|
|
||||||
{:deps #js [page-id]
|
|
||||||
:fn (fn []
|
|
||||||
(let [sub (shortcuts/init)]
|
|
||||||
(st/emit! (udw/initialize project-id page-id))
|
|
||||||
#(rx/cancel! sub)))})
|
|
||||||
|
|
||||||
[:> rdnd/provider {:backend rdnd/html5}
|
|
||||||
[:& workspace {:page-id page-id :key page-id}]])
|
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
]
|
]
|
||||||
[:header#workspace-bar.workspace-bar
|
[:header#workspace-bar.workspace-bar
|
||||||
[:div.main-icon
|
[:div.main-icon
|
||||||
[:a {:on-click #(st/emit! (rt/nav :dashboard/projects))} i/logo-icon]]
|
[:a {:on-click #(st/emit! (rt/nav :dashboard-projects))} i/logo-icon]]
|
||||||
[:div.project-tree-btn
|
[:div.project-tree-btn
|
||||||
{:alt (tr "header.sitemap")
|
{:alt (tr "header.sitemap")
|
||||||
:class (when (contains? flags :sitemap) "selected")
|
:class (when (contains? flags :sitemap) "selected")
|
||||||
|
|
|
@ -20,13 +20,11 @@
|
||||||
|
|
||||||
(mf/defc left-sidebar
|
(mf/defc left-sidebar
|
||||||
{:wrap [mf/wrap-memo]}
|
{:wrap [mf/wrap-memo]}
|
||||||
[{:keys [layout page] :as props}]
|
[{:keys [layout page file] :as props}]
|
||||||
[:aside.settings-bar.settings-bar-left
|
[:aside.settings-bar.settings-bar-left
|
||||||
[:div.settings-bar-inside
|
[:div.settings-bar-inside
|
||||||
(when (contains? layout :sitemap)
|
(when (contains? layout :sitemap)
|
||||||
[:& sitemap-toolbox {:project-id (:project-id page)
|
[:& sitemap-toolbox {:file file :page page}])
|
||||||
:current-page-id (:id page)
|
|
||||||
:page page}])
|
|
||||||
(when (contains? layout :document-history)
|
(when (contains? layout :document-history)
|
||||||
[:& history-toolbox])
|
[:& history-toolbox])
|
||||||
(when (contains? layout :layers)
|
(when (contains? layout :layers)
|
||||||
|
|
|
@ -29,39 +29,36 @@
|
||||||
|
|
||||||
(mf/defc page-item
|
(mf/defc page-item
|
||||||
[{:keys [page index deletable? selected?] :as props}]
|
[{:keys [page index deletable? selected?] :as props}]
|
||||||
(letfn [(on-edit [event]
|
(let [on-edit #(modal/show! page-form-dialog {:page page})
|
||||||
(modal/show! page-form-dialog {:page page}))
|
delete-fn #(st/emit! (dw/delete-page (:id page)))
|
||||||
(delete []
|
on-delete #(do
|
||||||
(st/emit! (dw/delete-page (:id page))))
|
(dom/prevent-default %)
|
||||||
(on-delete [event]
|
(dom/stop-propagation %)
|
||||||
(dom/prevent-default event)
|
(modal/show! confirm-dialog {:on-accept delete-fn}))
|
||||||
(dom/stop-propagation event)
|
on-drop #(do (prn "TODO"))
|
||||||
(modal/show! confirm-dialog {:on-accept delete}))
|
on-hover #(st/emit! (dw/change-page-order {:id (:id page)
|
||||||
(on-drop [item monitor]
|
:index index}))
|
||||||
(prn "TODO"))
|
|
||||||
(on-hover [item monitor]
|
|
||||||
(st/emit! (dw/change-page-order {:id (:id item)
|
|
||||||
:index index})))]
|
|
||||||
(let [[dprops ref] (use-sortable {:type "page-item"
|
|
||||||
:data {:id (:id page)
|
|
||||||
:index index}
|
|
||||||
:on-hover on-hover
|
|
||||||
:on-drop on-drop})]
|
|
||||||
[:li {:ref ref :class (classnames :selected selected?)}
|
|
||||||
[:div.element-list-body
|
|
||||||
{:class (classnames :selected selected?
|
|
||||||
:dragging (:dragging? dprops))
|
|
||||||
:on-click #(st/emit! (rt/nav :workspace/page {:project (:project-id page)
|
|
||||||
:page (:id page)}))
|
|
||||||
:on-double-click #(dom/stop-propagation %)
|
|
||||||
:draggable true}
|
|
||||||
|
|
||||||
[:div.page-icon i/page]
|
navigate-fn #(st/emit! (dw/go-to-page (:id page)))
|
||||||
[:span (:name page)]
|
[dprops ref] (use-sortable {:type "page-item"
|
||||||
[:div.page-actions {}
|
:data {:id (:id page)
|
||||||
[:a {:on-click on-edit} i/pencil]
|
:index index}
|
||||||
(when deletable?
|
:on-hover on-hover
|
||||||
[:a {:on-click on-delete} i/trash])]]])))
|
:on-drop on-drop})]
|
||||||
|
[:li {:ref ref :class (classnames :selected selected?)}
|
||||||
|
[:div.element-list-body
|
||||||
|
{:class (classnames :selected selected?
|
||||||
|
:dragging (:dragging? dprops))
|
||||||
|
:on-click navigate-fn
|
||||||
|
:on-double-click #(dom/stop-propagation %)
|
||||||
|
:draggable true}
|
||||||
|
|
||||||
|
[:div.page-icon i/page]
|
||||||
|
[:span (:name page)]
|
||||||
|
[:div.page-actions {}
|
||||||
|
[:a {:on-click on-edit} i/pencil]
|
||||||
|
(when deletable?
|
||||||
|
[:a {:on-click on-delete} i/trash])]]]))
|
||||||
|
|
||||||
|
|
||||||
;; --- Page Item Wrapper
|
;; --- Page Item Wrapper
|
||||||
|
@ -84,8 +81,8 @@
|
||||||
;; --- Pages List
|
;; --- Pages List
|
||||||
|
|
||||||
(mf/defc pages-list
|
(mf/defc pages-list
|
||||||
[{:keys [project current-page-id] :as props}]
|
[{:keys [file current-page] :as props}]
|
||||||
(let [pages (enumerate (:pages project))
|
(let [pages (enumerate (:pages file))
|
||||||
deletable? (> (count pages) 1)]
|
deletable? (> (count pages) 1)]
|
||||||
[:ul.element-list
|
[:ul.element-list
|
||||||
(for [[index page-id] pages]
|
(for [[index page-id] pages]
|
||||||
|
@ -93,31 +90,22 @@
|
||||||
{:page-id page-id
|
{:page-id page-id
|
||||||
:index index
|
:index index
|
||||||
:deletable? deletable?
|
:deletable? deletable?
|
||||||
:selected? (= page-id current-page-id)
|
:selected? (= page-id (:id current-page))
|
||||||
:key page-id}])]))
|
:key page-id}])]))
|
||||||
|
|
||||||
;; --- Sitemap Toolbox
|
;; --- Sitemap Toolbox
|
||||||
|
|
||||||
(def ^:private workspace-project
|
|
||||||
(letfn [(selector [state]
|
|
||||||
(let [project-id (get-in state [:workspace-page :project-id])]
|
|
||||||
(get-in state [:projects project-id])))]
|
|
||||||
(-> (l/lens selector)
|
|
||||||
(l/derive st/state))))
|
|
||||||
|
|
||||||
(mf/defc sitemap-toolbox
|
(mf/defc sitemap-toolbox
|
||||||
[{:keys [project-id current-page-id] :as props}]
|
[{:keys [file page] :as props}]
|
||||||
(let [project (mf/deref workspace-project)
|
(let [create-fn #(modal/show! page-form-dialog {:page {:file-id (:file-id page)}})
|
||||||
create #(modal/show! page-form-dialog {:page {:project-id project-id}})
|
close-fn #(st/emit! (dw/toggle-flag :sitemap))]
|
||||||
close #(st/emit! (dw/toggle-flag :sitemap))]
|
|
||||||
[:div.sitemap.tool-window
|
[:div.sitemap.tool-window
|
||||||
[:div.tool-window-bar
|
[:div.tool-window-bar
|
||||||
[:div.tool-window-icon i/project-tree]
|
[:div.tool-window-icon i/project-tree]
|
||||||
[:span (tr "ds.settings.sitemap")]
|
[:span (tr "ds.settings.sitemap")]
|
||||||
[:div.tool-window-close {:on-click close} i/close]]
|
[:div.tool-window-close {:on-click close-fn} i/close]]
|
||||||
[:div.tool-window-content
|
[:div.tool-window-content
|
||||||
[:div.project-title
|
[:div.project-title
|
||||||
[:span (:name project)]
|
#_[:span (:name project)]
|
||||||
[:div.add-page {:on-click create} i/close]]
|
[:div.add-page {:on-click create-fn} i/close]]
|
||||||
[:& pages-list {:project project
|
[:& pages-list {:file file :current-page page}]]]))
|
||||||
:current-page-id current-page-id}]]]))
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue