Improve consistency on event handling on dashboard

This commit is contained in:
Andrey Antukh 2022-10-10 15:55:14 +02:00 committed by Alejandro Alonso
parent cad2201c54
commit 582a20d369
4 changed files with 162 additions and 131 deletions

View file

@ -23,29 +23,38 @@
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(mf/defc header (mf/defc header
[{:keys [project on-create-clicked] :as props}] [{:keys [project create-fn] :as props}]
(let [local (mf/use-state {:menu-open false (let [local (mf/use-state
:edition false}) {:menu-open false
:edition false})
on-create-click
(mf/use-fn
(mf/deps create-fn)
(fn [event]
(dom/prevent-default event)
(create-fn "dashboard:header")))
on-menu-click on-menu-click
(mf/use-callback (mf/use-fn
(fn [event] (fn [event]
(let [position (dom/get-client-position event)] (let [position (dom/get-client-position event)]
(dom/prevent-default event) (dom/prevent-default event)
(swap! local assoc :menu-open true :menu-pos position)))) (swap! local assoc :menu-open true :menu-pos position))))
on-menu-close on-menu-close
(mf/use-callback #(swap! local assoc :menu-open false)) (mf/use-fn #(swap! local assoc :menu-open false))
on-edit on-edit
(mf/use-callback #(swap! local assoc :edition true :menu-open false)) (mf/use-fn #(swap! local assoc :edition true :menu-open false))
toggle-pin toggle-pin
(mf/use-callback (mf/use-fn
(mf/deps project) (mf/deps project)
#(st/emit! (dd/toggle-project-pin project))) #(st/emit! (dd/toggle-project-pin project)))
on-import on-import
(mf/use-callback (mf/use-fn
(mf/deps (:id project)) (mf/deps (:id project))
(fn [] (fn []
(st/emit! (dd/fetch-files {:project-id (:id project)}) (st/emit! (dd/fetch-files {:project-id (:id project)})
@ -78,7 +87,7 @@
:on-import on-import}] :on-import on-import}]
[:div.dashboard-header-actions [:div.dashboard-header-actions
[:a.btn-secondary.btn-small {:on-click (partial on-create-clicked project "dashboard:header") :data-test "new-file"} [:a.btn-secondary.btn-small {:on-click on-create-click :data-test "new-file"}
(tr "dashboard.new-file")] (tr "dashboard.new-file")]
(when-not (:is-default project) (when-not (:is-default project)
@ -95,69 +104,65 @@
(mf/defc files-section (mf/defc files-section
[{:keys [project team] :as props}] [{:keys [project team] :as props}]
(let [files-map (mf/deref refs/dashboard-files) (let [files-map (mf/deref refs/dashboard-files)
width (mf/use-state nil) project-id (:id project)
rowref (mf/use-ref) width (mf/use-state nil)
itemsize (if (>= @width 1030) rowref (mf/use-ref)
280 itemsize (if (>= @width 1030)
230) 280
230)
ratio (if (some? @width) (/ @width itemsize) 0) ratio (if (some? @width) (/ @width itemsize) 0)
nitems (mth/floor ratio) nitems (mth/floor ratio)
limit (min 10 nitems) limit (min 10 nitems)
limit (max 1 limit) limit (max 1 limit)
files (->> (vals files-map) files (mf/with-memo [project-id files-map]
(filter #(= (:id project) (:project-id %))) (->> (vals files-map)
(sort-by :modified-at) (filter #(= project-id (:project-id %)))
(reverse)) (sort-by :modified-at)
(reverse)))
on-create-clicked create-file
(mf/use-callback (mf/use-fn
(fn [project origin event] (fn [origin]
(dom/prevent-default event)
(st/emit! (with-meta (dd/create-file {:project-id (:id project)}) (st/emit! (with-meta (dd/create-file {:project-id (:id project)})
{::ev/origin origin}))))] {::ev/origin origin}))))]
(mf/use-effect (mf/with-effect []
(fn [] (let [node (mf/ref-val rowref)
(let [node (mf/ref-val rowref) mnt? (volatile! true)
mnt? (volatile! true) sub (->> (wapi/observe-resize node)
sub (->> (wapi/observe-resize node) (rx/observe-on :af)
(rx/observe-on :af) (rx/subs (fn [entries]
(rx/subs (fn [entries] (let [row (first entries)
(let [row (first entries) row-rect (.-contentRect ^js row)
row-rect (.-contentRect ^js row) row-width (.-width ^js row-rect)]
row-width (.-width ^js row-rect)] (when @mnt?
(when @mnt? (reset! width row-width))))))]
(reset! width row-width))))))] (fn []
(fn [] (vreset! mnt? false)
(vreset! mnt? false) (rx/dispose! sub))))
(rx/dispose! sub)))))
(mf/with-effect [project]
(when project
(let [pname (if (:is-default project)
(tr "labels.drafts")
(:name project))]
(dom/set-html-title (tr "title.dashboard.files" pname)))))
(mf/use-effect (mf/with-effect [project-id]
(mf/deps project) (st/emit! (dd/fetch-files {:project-id project-id})
(fn [] (dd/clear-selected-files)))
(when project
(let [pname (if (:is-default project)
(tr "labels.drafts")
(:name project))]
(dom/set-html-title (tr "title.dashboard.files" pname))))))
(mf/use-effect
(mf/deps project)
(fn []
(st/emit! (dd/fetch-files {:project-id (:id project)})
(dd/clear-selected-files))))
[:* [:*
[:& header {:team team :project project [:& header {:team team
:on-create-clicked on-create-clicked}] :project project
:create-fn create-file}]
[:section.dashboard-container.no-bg {:ref rowref} [:section.dashboard-container.no-bg {:ref rowref}
[:& grid {:project project [:& grid {:project project
:files files :files files
:on-create-clicked on-create-clicked
:origin :files :origin :files
:create-fn create-file
:limit limit}]]])) :limit limit}]]]))

View file

@ -177,8 +177,8 @@
[{:keys [file navigate? origin library-view?] :as props}] [{:keys [file navigate? origin library-view?] :as props}]
(let [file-id (:id file) (let [file-id (:id file)
local (mf/use-state {:menu-open false local (mf/use-state {:menu-open false
:menu-pos nil :menu-pos nil
:edition false}) :edition false})
selected-files (mf/deref refs/dashboard-selected-files) selected-files (mf/deref refs/dashboard-selected-files)
dashboard-local (mf/deref refs/dashboard-local) dashboard-local (mf/deref refs/dashboard-local)
node-ref (mf/use-ref) node-ref (mf/use-ref)
@ -313,7 +313,7 @@
(mf/defc grid (mf/defc grid
[{:keys [files project on-create-clicked origin limit library-view?] :as props}] [{:keys [files project origin limit library-view? create-fn] :as props}]
(let [dragging? (mf/use-state false) (let [dragging? (mf/use-state false)
project-id (:id project) project-id (:id project)
node-ref (mf/use-var nil) node-ref (mf/use-var nil)
@ -384,10 +384,8 @@
:else :else
[:& empty-placeholder [:& empty-placeholder
{:default? (:is-default project) {:limit limit
:on-create-clicked on-create-clicked :create-fn create-fn
:project project
:limit limit
:origin origin}])])) :origin origin}])]))
(mf/defc line-grid-row (mf/defc line-grid-row
@ -407,7 +405,7 @@
:navigate? false}])])) :navigate? false}])]))
(mf/defc line-grid (mf/defc line-grid
[{:keys [project team files limit on-create-clicked] :as props}] [{:keys [project team files limit create-fn] :as props}]
(let [dragging? (mf/use-state false) (let [dragging? (mf/use-state false)
project-id (:id project) project-id (:id project)
team-id (:id team) team-id (:id team)
@ -494,8 +492,8 @@
:limit limit}] :limit limit}]
:else :else
[:& empty-placeholder {:dragging? @dragging? [:& empty-placeholder
:default? (:is-default project) {:dragging? @dragging?
:on-create-clicked on-create-clicked :limit limit
:limit limit}])])) :create-fn create-fn}])]))

View file

@ -11,21 +11,27 @@
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(mf/defc empty-placeholder (mf/defc empty-placeholder
[{:keys [dragging? on-create-clicked project limit origin] :as props}] [{:keys [dragging? limit origin create-fn] :as props}]
(cond (let [on-click
(true? dragging?) (mf/use-fn
[:div.grid-row.no-wrap (mf/deps create-fn)
{:style {:grid-template-columns (str "repeat(" limit ", 1fr)")}} (fn [_]
[:div.grid-item]] (create-fn "dashboard:empty-folder-placeholder")))]
(= :libraries origin) (cond
[:div.grid-empty-placeholder.libs {:data-test "empty-placeholder"} (true? dragging?)
[:div.text [:div.grid-row.no-wrap
[:& i18n/tr-html {:label "dashboard.empty-placeholder-drafts"}]]] {:style {:grid-template-columns (str "repeat(" limit ", 1fr)")}}
:else [:div.grid-item]]
[:div.grid-empty-placeholder
{:style {:grid-template-columns (str "repeat(" limit ", 1fr)")}} (= :libraries origin)
[:button.create-new {:on-click (partial on-create-clicked project "dashboard:empty-folder-placeholder")} [:div.grid-empty-placeholder.libs {:data-test "empty-placeholder"}
(tr "dashboard.new-file")]])) [:div.text
[:& i18n/tr-html {:label "dashboard.empty-placeholder-drafts"}]]]
:else
[:div.grid-empty-placeholder
{:style {:grid-template-columns (str "repeat(" limit ", 1fr)")}}
[:button.create-new {:on-click on-click} (tr "dashboard.new-file")]])))
(mf/defc loading-placeholder (mf/defc loading-placeholder
[] []

View file

@ -33,37 +33,47 @@
(mf/defc header (mf/defc header
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]}
[] []
(let [create #(st/emit! (dd/create-project))] (let [on-click (mf/use-fn #(st/emit! (dd/create-project)))]
[:header.dashboard-header [:header.dashboard-header
[:div.dashboard-title [:div.dashboard-title
[:h1 (tr "dashboard.projects-title")]] [:h1 (tr "dashboard.projects-title")]]
[:a.btn-secondary.btn-small
[:a.btn-secondary.btn-small {:on-click create :data-test "new-project-button"} {:on-click on-click
:data-test "new-project-button"}
(tr "dashboard.new-project")]])) (tr "dashboard.new-project")]]))
(mf/defc team-hero (mf/defc team-hero
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]}
[{:keys [team close-banner] :as props}] [{:keys [team close-fn] :as props}]
(let [go-members (mf/use-fn #(st/emit! (dd/go-to-team-members))) (let [on-nav-members-click (mf/use-fn #(st/emit! (dd/go-to-team-members)))
invite-member on-invite-click
(mf/use-fn (mf/use-fn
(mf/deps team) (mf/deps team)
(fn [] (fn []
(st/emit! (modal/show {:type :invite-members (st/emit! (modal/show {:type :invite-members
:team team :team team
:origin :hero}))))] :origin :hero}))))
on-close-click
(mf/use-fn
(mf/deps close-fn)
(fn [event]
(dom/prevent-default event)
(close-fn)))]
[:div.team-hero [:div.team-hero
[:img {:src "images/deco-team-banner.png" :border "0"}] [:img {:src "images/deco-team-banner.png" :border "0"}]
[:div.text [:div.text
[:div.title (tr "dasboard.team-hero.title")] [:div.title (tr "dasboard.team-hero.title")]
[:div.info [:div.info
[:span (tr "dasboard.team-hero.text")] [:span (tr "dasboard.team-hero.text")]
[:a {:on-click go-members} (tr "dasboard.team-hero.management")]]] [:a {:on-click on-nav-members-click} (tr "dasboard.team-hero.management")]]]
[:button.btn-primary.invite [:button.btn-primary.invite
{:on-click invite-member} {:on-click on-invite-click}
(tr "onboarding.choice.team-up.invite-members")] (tr "onboarding.choice.team-up.invite-members")]
[:button.close {:on-click close-banner} [:button.close
{:on-click on-close-click
:aria-label (tr "labels.close")}
[:span i/close]]])) [:span i/close]]]))
(def builtin-templates (def builtin-templates
@ -140,6 +150,7 @@
(let [locale (mf/deref i18n/locale) (let [locale (mf/deref i18n/locale)
file-count (or (:count project) 0) file-count (or (:count project) 0)
project-id (:id project) project-id (:id project)
team-id (:id team)
dstate (mf/deref refs/dashboard-local) dstate (mf/deref refs/dashboard-local)
edit-id (:project-for-edit dstate) edit-id (:project-for-edit dstate)
@ -161,30 +172,33 @@
on-nav on-nav
(mf/use-fn (mf/use-fn
(mf/deps project) (mf/deps project-id team-id)
(fn [] (fn []
(st/emit! (rt/nav :dashboard-files {:team-id (:team-id project) (st/emit! (rt/nav :dashboard-files
:project-id project-id})))) {:team-id team-id
:project-id project-id}))))
toggle-pin toggle-pin
(mf/use-callback (mf/use-fn
(mf/deps project) (mf/deps project)
#(st/emit! (dd/toggle-project-pin project))) #(st/emit! (dd/toggle-project-pin project)))
on-menu-click on-menu-click
(mf/use-callback (fn [event] (mf/use-fn
(let [position (dom/get-client-position event)] (fn [event]
(dom/prevent-default event) (let [position (dom/get-client-position event)]
(swap! local assoc :menu-open true (dom/prevent-default event)
:menu-pos position)))) (swap! local assoc
:menu-open true
:menu-pos position))))
on-menu-close on-menu-close
(mf/use-callback #(swap! local assoc :menu-open false)) (mf/use-fn #(swap! local assoc :menu-open false))
on-edit-open on-edit-open
(mf/use-callback #(swap! local assoc :edition? true)) (mf/use-fn #(swap! local assoc :edition? true))
on-edit on-edit
(mf/use-callback (mf/use-fn
(mf/deps project) (mf/deps project)
(fn [name] (fn [name]
(let [name (str/trim name)] (let [name (str/trim name)]
@ -194,29 +208,33 @@
(swap! local assoc :edition? false)))) (swap! local assoc :edition? false))))
on-file-created on-file-created
(mf/use-callback (mf/use-fn
(mf/deps project)
(fn [data] (fn [data]
(let [pparams {:project-id (:project-id data) (let [pparams {:project-id (:project-id data)
:file-id (:id data)} :file-id (:id data)}
qparams {:page-id (get-in data [:data :pages 0])}] qparams {:page-id (get-in data [:data :pages 0])}]
(st/emit! (rt/nav :workspace pparams qparams))))) (st/emit! (rt/nav :workspace pparams qparams)))))
create-file create-file
(mf/use-callback (mf/use-fn
(mf/deps project) (mf/deps project-id on-file-created)
(fn [origin] (fn [origin]
(let [mdata {:on-success on-file-created} (let [mdata {:on-success on-file-created}
params {:project-id (:id project)}] params {:project-id project-id}]
(st/emit! (with-meta (dd/create-file (with-meta params mdata)) (st/emit! (-> (dd/create-file (with-meta params mdata))
{::ev/origin origin}))))) (with-meta {::ev/origin origin}))))))
on-create-click
(mf/use-fn
(mf/deps create-file)
(fn [_]
(create-file "dashboard:grid-header-plus-button")))
on-import on-import
(mf/use-callback (mf/use-fn
(mf/deps (:id project) (:id team)) (mf/deps project-id (:id team))
(fn [] (fn []
(st/emit! (dd/fetch-files {:project-id (:id project)}) (st/emit! (dd/fetch-files {:project-id project-id})
(dd/fetch-recent-files (:id team)) (dd/fetch-recent-files (:id team))
(dd/clear-selected-files))))] (dd/clear-selected-files))))]
@ -235,7 +253,6 @@
(vreset! mnt? false) (vreset! mnt? false)
(rx/dispose! sub)))) (rx/dispose! sub))))
[:div.dashboard-project-row [:div.dashboard-project-row
{:class (when first? "first")} {:class (when first? "first")}
[:div.project {:ref rowref} [:div.project {:ref rowref}
@ -249,13 +266,14 @@
(tr "labels.drafts") (tr "labels.drafts")
(:name project))]) (:name project))])
[:& project-menu {:project project [:& project-menu
:show? (:menu-open @local) {:project project
:left (:x (:menu-pos @local)) :show? (:menu-open @local)
:top (:y (:menu-pos @local)) :left (:x (:menu-pos @local))
:on-edit on-edit-open :top (:y (:menu-pos @local))
:on-menu-close on-menu-close :on-edit on-edit-open
:on-import on-import}] :on-menu-close on-menu-close
:on-import on-import}]
[:span.info (str (tr "labels.num-of-files" (i18n/c file-count)))] [:span.info (str (tr "labels.num-of-files" (i18n/c file-count)))]
(when (> file-count 0) (when (> file-count 0)
@ -272,11 +290,15 @@
i/pin)]) i/pin)])
[:a.btn-secondary.btn-small.tooltip.tooltip-bottom [:a.btn-secondary.btn-small.tooltip.tooltip-bottom
{:on-click create-file :alt (tr "dashboard.new-file") :data-test "project-new-file"} {:on-click on-create-click
:alt (tr "dashboard.new-file")
:data-test "project-new-file"}
i/close] i/close]
[:a.btn-secondary.btn-small.tooltip.tooltip-bottom [:a.btn-secondary.btn-small.tooltip.tooltip-bottom
{:on-click on-menu-click :alt (tr "dashboard.options") :data-test "project-options"} {:on-click on-menu-click
:alt (tr "dashboard.options")
:data-test "project-options"}
i/actions]]] i/actions]]]
(when (and (> limit 0) (when (and (> limit 0)
@ -290,7 +312,7 @@
{:project project {:project project
:team team :team team
:files files :files files
:on-create-clicked (partial create-file "dashboard:empty-folder-placeholder") :create-fn create-file
:limit limit}]])) :limit limit}]]))
@ -349,7 +371,7 @@
[:& header] [:& header]
(when team-hero? (when team-hero?
[:& team-hero {:team team :close-banner close-banner}]) [:& team-hero {:team team :close-fn close-banner}])
(when (or (not tutorial-viewed?) (not walkthrough-viewed?)) (when (or (not tutorial-viewed?) (not walkthrough-viewed?))
[:div.hero-projects [:div.hero-projects