0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-12 07:41:43 -05:00

Simplify users loading on dashboard and workspace.

And fix some issues on teams settings page.
This commit is contained in:
Andrey Antukh 2020-11-23 13:52:21 +01:00 committed by Alonso Torres
parent 2dafbeddb0
commit 013c866137
13 changed files with 151 additions and 111 deletions

View file

@ -185,39 +185,6 @@
(let [file (retrieve-file conn file-id)] (let [file (retrieve-file conn file-id)]
(get-in file [:data :pages-index id])))) (get-in file [:data :pages-index id]))))
;; --- Query: File users
(def ^:private sql:file-users
"select pf.id, pf.fullname, pf.photo
from profile as pf
inner join file_profile_rel as fpr on (fpr.profile_id = pf.id)
where fpr.file_id = ?
union
select pf.id, pf.fullname, pf.photo
from profile as pf
inner join project_profile_rel as ppr on (ppr.profile_id = pf.id)
inner join file as f on (f.project_id = ppr.project_id)
where f.id = ?
union
select pf.id, pf.fullname, pf.photo
from profile as pf
inner join team_profile_rel as tpr on (tpr.profile_id = pf.id)
inner join project as p on (tpr.team_id = p.team_id)
inner join file as f on (p.id = f.project_id)
where f.id = ?")
(defn retrieve-file-users
[conn id]
(db/exec! conn [sql:file-users id id id]))
(s/def ::file-users
(s/keys :req-un [::profile-id ::id]))
(sq/defquery ::file-users
[{:keys [profile-id id] :as params}]
(db/with-atomic [conn db/pool]
(check-edition-permissions! conn profile-id id)
(retrieve-file-users conn id)))
;; --- Query: Shared Library Files ;; --- Query: Shared Library Files

View file

@ -131,8 +131,29 @@
[conn team-id] [conn team-id]
(db/exec! conn [sql:team-members team-id])) (db/exec! conn [sql:team-members team-id]))
;; --- Query: Team Users ;; --- Query: Team Users
(declare retrieve-users)
(declare retrieve-team-for-file)
(s/def ::file-id ::us/uuid)
(s/def ::team-users
(s/and (s/keys :req-un [::profile-id]
:opt-un [::team-id ::file-id])
#(or (:team-id %) (:file-id %))))
(sq/defquery ::team-users
[{:keys [profile-id team-id file-id]}]
(with-open [conn (db/open)]
(if team-id
(do
(check-edition-permissions! conn profile-id team-id)
(retrieve-users conn team-id))
(let [{team-id :id} (retrieve-team-for-file conn file-id)]
(check-edition-permissions! conn profile-id team-id)
(retrieve-users conn team-id)))))
;; This is a similar query to team members but can contain more data ;; This is a similar query to team members but can contain more data
;; because some user can be explicitly added to project or file (not ;; because some user can be explicitly added to project or file (not
;; implemented in UI) ;; implemented in UI)
@ -156,12 +177,38 @@
inner join project as p on (f.project_id = p.id) inner join project as p on (f.project_id = p.id)
where p.team_id = ?") where p.team_id = ?")
(s/def ::team-users (def sql:team-by-file
"select p.team_id as id
from project as p
join file as f on (p.id = f.project_id)
where f.id = ?")
(defn retrieve-users
[conn team-id]
(db/exec! conn [sql:team-users team-id team-id team-id]))
(defn retrieve-team-for-file
[conn file-id]
(->> [sql:team-by-file file-id]
(db/exec-one! conn)))
;; --- Query: Team Stats
(declare retrieve-team-stats)
(s/def ::team-stats
(s/keys :req-un [::profile-id ::team-id])) (s/keys :req-un [::profile-id ::team-id]))
(sq/defquery ::team-users (sq/defquery ::team-stats
[{:keys [profile-id team-id]}] [{:keys [profile-id team-id]}]
(with-open [conn (db/open)] (with-open [conn (db/open)]
(check-edition-permissions! conn profile-id team-id) (check-read-permissions! conn profile-id team-id)
(db/exec! conn [sql:team-users team-id team-id team-id]))) (retrieve-team-stats conn team-id)))
(def sql:team-stats
"select (select count(*) from project where team_id = ?) as projects,
(select count(*) from file as f join project as p on (p.id = f.project_id) where p.team_id = ?) as files")
(defn retrieve-team-stats
[conn team-id]
(db/exec-one! conn [sql:team-stats team-id team-id]))

View file

@ -14,6 +14,7 @@
[app.db :as db] [app.db :as db]
[app.services.queries :as sq] [app.services.queries :as sq]
[app.services.queries.files :as files] [app.services.queries.files :as files]
[app.services.queries.teams :as teams]
[clojure.spec.alpha :as s])) [clojure.spec.alpha :as s]))
;; --- Query: Viewer Bundle (by Page ID) ;; --- Query: Viewer Bundle (by Page ID)
@ -50,13 +51,14 @@
file (merge (dissoc file :data) file (merge (dissoc file :data)
(select-keys (:data file) [:colors :media :typographies])) (select-keys (:data file) [:colors :media :typographies]))
libs (files/retrieve-file-libraries conn false file-id) libs (files/retrieve-file-libraries conn false file-id)
users (files/retrieve-file-users conn file-id) users (teams/retrieve-users conn (:team-id project))
bundle {:file file bundle {:file file
:page page :page page
:users users :users users
:project project :project project
:libraries libs}] :libraries libs}]
(if (string? token) (if (string? token)
(do (do
(check-shared-token! conn file-id page-id token) (check-shared-token! conn file-id page-id token)

View file

@ -460,6 +460,19 @@
"es" : "+ Nuevo proyecto" "es" : "+ Nuevo proyecto"
} }
}, },
"labels.num-of-projects" : {
"translations" : {
"en" : ["1 project", "%s projects"]
}
},
"labels.num-of-files" : {
"translations" : {
"en" : ["1 file", "%s files"]
}
},
"dashboard.no-matches-for" : { "dashboard.no-matches-for" : {
"used-in" : [ "src/app/main/ui/dashboard/search.cljs:48" ], "used-in" : [ "src/app/main/ui/dashboard/search.cljs:48" ],
"translations" : { "translations" : {

View file

@ -11,6 +11,7 @@
[app.common.spec :as us] [app.common.spec :as us]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.data.users :as du]
[app.util.router :as rt] [app.util.router :as rt]
[app.util.time :as dt] [app.util.time :as dt]
[app.util.timers :as ts] [app.util.timers :as ts]
@ -89,18 +90,14 @@
(->> (rp/query :team-members {:team-id id}) (->> (rp/query :team-members {:team-id id})
(rx/map #(partial fetched %))))))) (rx/map #(partial fetched %)))))))
(defn fetch-team-stats
(defn fetch-team-users [{:keys [id] :as team}]
[{:keys [id] :as params}]
(us/assert ::us/uuid id) (us/assert ::us/uuid id)
(letfn [(fetched [users state] (ptk/reify ::fetch-team-members
(->> (map #(avatars/assoc-avatar % :fullname) users) ptk/WatchEvent
(d/index-by :id) (watch [_ state stream]
(assoc-in state [:team-users id])))] (let [fetched #(assoc-in %2 [:team-stats id] %1)]
(ptk/reify ::fetch-team-users (->> (rp/query :team-stats {:team-id id})
ptk/WatchEvent
(watch [_ state stream]
(->> (rp/query :team-users {:team-id id})
(rx/map #(partial fetched %))))))) (rx/map #(partial fetched %)))))))
;; --- Fetch Projects ;; --- Fetch Projects
@ -125,7 +122,7 @@
(let [profile (:profile state)] (let [profile (:profile state)]
(->> (rx/merge (ptk/watch (fetch-team params) state stream) (->> (rx/merge (ptk/watch (fetch-team params) state stream)
(ptk/watch (fetch-projects {:team-id id}) state stream) (ptk/watch (fetch-projects {:team-id id}) state stream)
(ptk/watch (fetch-team-users params) state stream)) (ptk/watch (du/fetch-users {:team-id id}) state stream))
(rx/catch (fn [{:keys [type code] :as error}] (rx/catch (fn [{:keys [type code] :as error}]
(cond (cond
(and (= :not-found type) (and (= :not-found type)

View file

@ -2,25 +2,29 @@
;; License, v. 2.0. If a copy of the MPL was not distributed with this ;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/. ;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;; ;;
;; Copyright (c) 2016-2019 Andrey Antukh <niwi@niwi.nz> ;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns app.main.data.users (ns app.main.data.users
(:require (:require
[app.config :as cfg]
[app.common.data :as d]
[app.common.spec :as us]
[app.main.data.media :as di]
[app.main.data.messages :as dm]
[app.main.repo :as rp]
[app.main.store :as st]
[app.util.avatars :as avatars]
[app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
[app.util.storage :refer [storage]]
[app.util.theme :as theme]
[beicon.core :as rx] [beicon.core :as rx]
[cljs.spec.alpha :as s] [cljs.spec.alpha :as s]
[cuerdas.core :as str] [cuerdas.core :as str]
[potok.core :as ptk] [potok.core :as ptk]))
[app.common.spec :as us]
[app.config :as cfg]
[app.main.store :as st]
[app.main.repo :as rp]
[app.main.data.messages :as dm]
[app.main.data.media :as di]
[app.util.router :as rt]
[app.util.i18n :as i18n :refer [tr]]
[app.util.storage :refer [storage]]
[app.util.avatars :as avatars]
[app.util.theme :as theme]))
;; --- Common Specs ;; --- Common Specs
@ -179,3 +183,18 @@
(rx/map (constantly fetch-profile)) (rx/map (constantly fetch-profile))
(rx/catch on-error)))))) (rx/catch on-error))))))
(defn fetch-users
[{:keys [team-id] :as params}]
(us/assert ::us/uuid team-id)
(letfn [(fetched [users state]
(->> (map #(avatars/assoc-avatar % :fullname) users)
(d/index-by :id)
(assoc state :users)))]
(ptk/reify ::fetch-team-users
ptk/WatchEvent
(watch [_ state stream]
(->> (rp/query :team-users {:team-id team-id})
(rx/map #(partial fetched %)))))))

View file

@ -203,7 +203,6 @@
:workspace-file :workspace-file
:workspace-project :workspace-project
:workspace-media-objects :workspace-media-objects
:workspace-users
:workspace-persistence)) :workspace-persistence))
ptk/WatchEvent ptk/WatchEvent

View file

@ -163,7 +163,7 @@
(ptk/reify ::handle-presence (ptk/reify ::handle-presence
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(let [profiles (:workspace-users state)] (let [profiles (:users state)]
(update state :workspace-presence update-sessions profiles)))))) (update state :workspace-presence update-sessions profiles))))))
(defn handle-pointer-update (defn handle-pointer-update

View file

@ -207,7 +207,7 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(->> (rx/zip (rp/query :file {:id file-id}) (->> (rx/zip (rp/query :file {:id file-id})
(rp/query :file-users {:id file-id}) (rp/query :team-users {:file-id file-id})
(rp/query :project {:id project-id}) (rp/query :project {:id project-id})
(rp/query :file-libraries {:file-id file-id})) (rp/query :file-libraries {:file-id file-id}))
(rx/first) (rx/first)
@ -238,11 +238,11 @@
(update [_ state] (update [_ state]
(let [users (map avatars/assoc-profile-avatar users)] (let [users (map avatars/assoc-profile-avatar users)]
(assoc state (assoc state
:users (d/index-by :id users)
:workspace-undo {} :workspace-undo {}
:workspace-project project :workspace-project project
:workspace-file file :workspace-file file
:workspace-data (:data file) :workspace-data (:data file)
:workspace-users (d/index-by :id users)
:workspace-libraries (d/index-by :id libraries)))))) :workspace-libraries (d/index-by :id libraries))))))

View file

@ -122,9 +122,6 @@
(def workspace-libraries (def workspace-libraries
(l/derived :workspace-libraries st/state)) (l/derived :workspace-libraries st/state))
(def workspace-users
(l/derived :workspace-users st/state))
(def workspace-presence (def workspace-presence
(l/derived :workspace-presence st/state)) (l/derived :workspace-presence st/state))
@ -214,3 +211,6 @@
(def comments-local (def comments-local
(l/derived :comments-local st/state)) (l/derived :comments-local st/state))
(def users
(l/derived :users st/state))

View file

@ -35,14 +35,8 @@
[cuerdas.core :as str] [cuerdas.core :as str]
[rumext.alpha :as mf])) [rumext.alpha :as mf]))
(defn team-members-ref
[{:keys [id] :as team}]
(l/derived (l/in [:team-users id]) st/state))
(mf/defc comments-section (mf/defc comments-section
[{:keys [profile team]}] [{:keys [profile team]}]
(mf/use-effect (mf/use-effect
(mf/deps team) (mf/deps team)
(st/emitf (dcm/retrieve-unread-comment-threads (:id team)))) (st/emitf (dcm/retrieve-unread-comment-threads (:id team))))
@ -51,9 +45,7 @@
show-dropdown (mf/use-fn #(reset! show-dropdown? true)) show-dropdown (mf/use-fn #(reset! show-dropdown? true))
hide-dropdown (mf/use-fn #(reset! show-dropdown? false)) hide-dropdown (mf/use-fn #(reset! show-dropdown? false))
threads-map (mf/deref refs/comment-threads) threads-map (mf/deref refs/comment-threads)
users (mf/deref refs/users)
users-ref (mf/use-memo (mf/deps team) #(team-members-ref team))
users (mf/deref users-ref)
tgroups (->> (vals threads-map) tgroups (->> (vals threads-map)
(sort-by :modified-at) (sort-by :modified-at)
@ -61,7 +53,6 @@
(dcm/apply-filters {} profile) (dcm/apply-filters {} profile)
(dcm/group-threads-by-file-and-page)) (dcm/group-threads-by-file-and-page))
on-navigate on-navigate
(mf/use-callback (mf/use-callback
(fn [thread] (fn [thread]

View file

@ -25,7 +25,7 @@
[app.main.ui.dashboard.team-form] [app.main.ui.dashboard.team-form]
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :as i18n :refer [t tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt] [app.util.router :as rt]
[app.util.time :as dt] [app.util.time :as dt]
[cljs.spec.alpha :as s] [cljs.spec.alpha :as s]
@ -34,7 +34,7 @@
(mf/defc header (mf/defc header
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]}
[{:keys [section locale team] :as props}] [{:keys [section team] :as props}]
(let [go-members (let [go-members
(mf/use-callback (mf/use-callback
(mf/deps team) (mf/deps team)
@ -57,19 +57,19 @@
[:header.dashboard-header [:header.dashboard-header
[:div.dashboard-title [:div.dashboard-title
[:h1 (cond [:h1 (cond
members-section? (t locale "labels.members") members-section? (tr "labels.members")
settings-section? (t locale "labels.settings") settings-section? (tr "labels.settings")
nil)]] nil)]]
[:nav [:nav
[:ul [:ul
[:li {:class (when members-section? "active")} [:li {:class (when members-section? "active")}
[:a {:on-click go-members} (t locale "labels.members")]] [:a {:on-click go-members} (tr "labels.members")]]
[:li {:class (when settings-section? "active")} [:li {:class (when settings-section? "active")}
[:a {:on-click go-settings} (t locale "labels.settings")]]]] [:a {:on-click go-settings} (tr "labels.settings")]]]]
(if members-section? (if members-section?
[:a.btn-secondary.btn-small {:on-click invite-member} [:a.btn-secondary.btn-small {:on-click invite-member}
(t locale "dashboard.invite-profile")] (tr "dashboard.invite-profile")]
[:div])])) [:div])]))
(s/def ::email ::us/email) (s/def ::email ::us/email)
@ -220,13 +220,12 @@
[:& team-member {:member item :team team :profile profile :key (:id item)}])]])) [:& team-member {:member item :team team :profile profile :key (:id item)}])]]))
(defn- members-ref (defn- members-ref
[team-id] [{:keys [id] :as team}]
(l/derived (l/in [:team-members team-id]) st/state)) (l/derived (l/in [:team-members id]) st/state))
(mf/defc team-members-page (mf/defc team-members-page
[{:keys [team profile] :as props}] [{:keys [team profile] :as props}]
(let [locale (mf/deref i18n/locale) (let [members-ref (mf/use-memo (mf/deps team) #(members-ref team))
members-ref (mf/use-memo (mf/deps team) #(members-ref (:id team)))
members-map (mf/deref members-ref)] members-map (mf/deref members-ref)]
(mf/use-effect (mf/use-effect
@ -234,24 +233,30 @@
(st/emitf (dd/fetch-team-members team))) (st/emitf (dd/fetch-team-members team)))
[:* [:*
[:& header {:locale locale [:& header {:section :dashboard-team-members
:section :dashboard-team-members
:team team}] :team team}]
[:section.dashboard-container.dashboard-team-members [:section.dashboard-container.dashboard-team-members
[:& team-members {:locale locale [:& team-members {:profile profile
:profile profile
:team team :team team
:members-map members-map}]]])) :members-map members-map}]]]))
(defn- stats-ref
[{:keys [id] :as team}]
(l/derived (l/in [:team-stats id]) st/state))
(mf/defc team-settings-page (mf/defc team-settings-page
[{:keys [team profile] :as props}] [{:keys [team profile] :as props}]
(let [locale (mf/deref i18n/locale) (let [finput (mf/use-ref)
finput (mf/use-ref)
members-ref (mf/use-memo (mf/deps team) #(members-ref (:id team))) members-ref (mf/use-memo (mf/deps team) #(members-ref team))
members-map (mf/deref members-ref) members-map (mf/deref members-ref)
owner (->> (vals members-map)
(d/seek :is-owner))
stats-ref (mf/use-memo (mf/deps team) #(stats-ref team))
stats (mf/deref stats-ref)
on-image-click on-image-click
(mf/use-callback #(dom/click (mf/ref-val finput))) (mf/use-callback #(dom/click (mf/ref-val finput)))
@ -264,17 +269,17 @@
(mf/use-effect (mf/use-effect
(mf/deps team) (mf/deps team)
(st/emitf (dd/fetch-team-members team))) (st/emitf (dd/fetch-team-members team)
(dd/fetch-team-stats team)))
[:* [:*
[:& header {:locale locale [:& header {:section :dashboard-team-settings
:section :dashboard-team-settings
:team team}] :team team}]
[:section.dashboard-container.dashboard-team-settings [:section.dashboard-container.dashboard-team-settings
[:div.team-settings [:div.team-settings
[:div.horizontal-blocks [:div.horizontal-blocks
[:div.block.info-block [:div.block.info-block
[:div.label (t locale "dashboard.team-info")] [:div.label (tr "dashboard.team-info")]
[:div.name (:name team)] [:div.name (:name team)]
[:div.icon [:div.icon
[:span.update-overlay {:on-click on-image-click} i/exit] [:span.update-overlay {:on-click on-image-click} i/exit]
@ -285,19 +290,19 @@
:on-selected on-file-selected}]]] :on-selected on-file-selected}]]]
[:div.block.owner-block [:div.block.owner-block
[:div.label (t locale "dashboard.team-members")] [:div.label (tr "dashboard.team-members")]
[:div.owner [:div.owner
[:span.icon [:img {:src (cfg/resolve-media-path (:photo-uri profile))}]] [:span.icon [:img {:src (cfg/resolve-media-path (:photo owner))}]]
[:span.text (str (:fullname profile) " (" (t locale "labels.owner") ")") ]] [:span.text (str (:name owner) " (" (tr "labels.owner") ")") ]]
[:div.summary [:div.summary
[:span.icon i/user] [:span.icon i/user]
[:span.text (t locale "dashboard.num-of-members" (count members-map))]]] [:span.text (tr "dashboard.num-of-members" (count members-map))]]]
[:div.block.stats-block [:div.block.stats-block
[:div.label (t locale "dashboard.team-projects")] [:div.label (tr "dashboard.team-projects")]
[:div.projects [:div.projects
[:span.icon i/folder] [:span.icon i/folder]
[:span.text "4 projects"]] [:span.text (tr "labels.num-of-projects" (i18n/c (:projects stats)))]]
[:div.files [:div.files
[:span.icon i/file-html] [:span.icon i/file-html]
[:span.text "4 files"]]]]]]])) [:span.text (tr "labels.num-of-files" (i18n/c (:files stats)))]]]]]]]))

View file

@ -36,7 +36,7 @@
pos-y (* (- (:y vbox)) zoom) pos-y (* (- (:y vbox)) zoom)
profile (mf/deref refs/profile) profile (mf/deref refs/profile)
users (mf/deref refs/workspace-users) users (mf/deref refs/users)
local (mf/deref refs/comments-local) local (mf/deref refs/comments-local)
threads-map (mf/deref threads-ref) threads-map (mf/deref threads-ref)
@ -132,7 +132,7 @@
[] []
(let [threads-map (mf/deref threads-ref) (let [threads-map (mf/deref threads-ref)
profile (mf/deref refs/profile) profile (mf/deref refs/profile)
users (mf/deref refs/workspace-users) users (mf/deref refs/users)
local (mf/deref refs/comments-local) local (mf/deref refs/comments-local)
options? (mf/use-state false) options? (mf/use-state false)