mirror of
https://github.com/penpot/penpot.git
synced 2025-05-21 10:36:11 +02:00
🎉 Add comments to dashboard.
This commit is contained in:
parent
420294aef4
commit
742af4e066
28 changed files with 968 additions and 438 deletions
|
@ -113,6 +113,9 @@
|
|||
|
||||
{:name "0032-del-unused-tables"
|
||||
:fn (mg/resource "app/migrations/sql/0032-del-unused-tables.sql")}
|
||||
|
||||
{:name "0033-mod-comment-thread-table"
|
||||
:fn (mg/resource "app/migrations/sql/0033-mod-comment-thread-table.sql")}
|
||||
]})
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE comment_thread
|
||||
ADD COLUMN page_name text NULL;
|
|
@ -29,12 +29,13 @@
|
|||
|
||||
(declare upsert-comment-thread-status!)
|
||||
(declare create-comment-thread)
|
||||
(declare retrieve-page-name)
|
||||
|
||||
(s/def ::page-id ::us/uuid)
|
||||
(s/def ::file-id ::us/uuid)
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
(s/def ::position ::us/point)
|
||||
(s/def ::content ::us/string)
|
||||
(s/def ::page-id ::us/uuid)
|
||||
|
||||
(s/def ::create-comment-thread
|
||||
(s/keys :req-un [::profile-id ::file-id ::position ::content ::page-id]))
|
||||
|
@ -53,13 +54,14 @@
|
|||
|
||||
(defn- create-comment-thread*
|
||||
[conn {:keys [profile-id file-id page-id position content] :as params}]
|
||||
(let [seqn (retrieve-next-seqn conn file-id)
|
||||
now (dt/now)
|
||||
|
||||
(let [seqn (retrieve-next-seqn conn file-id)
|
||||
now (dt/now)
|
||||
pname (retrieve-page-name conn params)
|
||||
thread (db/insert! conn :comment-thread
|
||||
{:file-id file-id
|
||||
:owner-id profile-id
|
||||
:participants (db/tjson #{profile-id})
|
||||
:page-name pname
|
||||
:page-id page-id
|
||||
:created-at now
|
||||
:modified-at now
|
||||
|
@ -81,10 +83,7 @@
|
|||
{:comment-thread-seqn seqn}
|
||||
{:id file-id})
|
||||
|
||||
(-> (assoc thread
|
||||
:content content
|
||||
:comment comment)
|
||||
(comments/decode-row))))
|
||||
(select-keys thread [:id :file-id :page-id])))
|
||||
|
||||
(defn- create-comment-thread
|
||||
[conn params]
|
||||
|
@ -104,6 +103,12 @@
|
|||
|
||||
:else res))))
|
||||
|
||||
(defn- retrieve-page-name
|
||||
[conn {:keys [file-id page-id]}]
|
||||
(let [{:keys [data]} (db/get-by-id conn :file file-id)
|
||||
data (blob/decode data)]
|
||||
(get-in data [:pages-index page-id :name])))
|
||||
|
||||
|
||||
;; --- Mutation: Update Comment Thread Status
|
||||
|
||||
|
@ -164,14 +169,21 @@
|
|||
[{:keys [profile-id thread-id content] :as params}]
|
||||
(db/with-atomic [conn db/pool]
|
||||
(let [thread (-> (db/get-by-id conn :comment-thread thread-id {:for-update true})
|
||||
(comments/decode-row))]
|
||||
(comments/decode-row))
|
||||
pname (retrieve-page-name conn thread)]
|
||||
|
||||
;; Standard Checks
|
||||
(when-not thread
|
||||
(ex/raise :type :not-found))
|
||||
(when-not thread (ex/raise :type :not-found))
|
||||
|
||||
;; Permission Checks
|
||||
(files/check-read-permissions! conn profile-id (:file-id thread))
|
||||
|
||||
;; Update the page-name cachedattribute on comment thread table.
|
||||
(when (not= pname (:page-name thread))
|
||||
(db/update! conn :comment-thread
|
||||
{:page-name pname}
|
||||
{:id thread-id}))
|
||||
|
||||
;; NOTE: is important that all timestamptz related fields are
|
||||
;; created or updated on the database level for avoid clock
|
||||
;; inconsistencies (some user sees something read that is not
|
||||
|
@ -216,15 +228,19 @@
|
|||
(let [comment (db/get-by-id conn :comment id {:for-update true})
|
||||
_ (when-not comment (ex/raise :type :not-found))
|
||||
thread (db/get-by-id conn :comment-thread (:thread-id comment) {:for-update true})
|
||||
_ (when-not thread (ex/raise :type :not-found))]
|
||||
_ (when-not thread (ex/raise :type :not-found))
|
||||
pname (retrieve-page-name conn thread)]
|
||||
|
||||
(files/check-read-permissions! conn profile-id (:file-id thread))
|
||||
|
||||
(db/update! conn :comment
|
||||
{:content content
|
||||
:modified-at (dt/now)}
|
||||
{:id (:id comment)})
|
||||
|
||||
(db/update! conn :comment-thread
|
||||
{:modified-at (dt/now)}
|
||||
{:modified-at (dt/now)
|
||||
:page-name pname}
|
||||
{:id (:id thread)})
|
||||
nil)))
|
||||
|
||||
|
@ -244,6 +260,7 @@
|
|||
(db/delete! conn :comment-thread {:id id})
|
||||
nil)))
|
||||
|
||||
|
||||
;; --- Mutation: Delete comment
|
||||
|
||||
(s/def ::delete-comment
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
[app.db :as db]
|
||||
[app.services.queries :as sq]
|
||||
[app.services.queries.files :as files]
|
||||
[app.services.queries.teams :as teams]
|
||||
[app.util.time :as dt]
|
||||
[app.util.transit :as t]
|
||||
[clojure.spec.alpha :as s]
|
||||
|
@ -32,9 +33,13 @@
|
|||
|
||||
(declare retrieve-comment-threads)
|
||||
|
||||
(s/def ::team-id ::us/uuid)
|
||||
(s/def ::file-id ::us/uuid)
|
||||
|
||||
(s/def ::comment-threads
|
||||
(s/keys :req-un [::profile-id ::file-id]))
|
||||
(s/and (s/keys :req-un [::profile-id]
|
||||
:opt-un [::file-id ::team-id])
|
||||
#(or (:file-id %) (:team-id %))))
|
||||
|
||||
(sq/defquery ::comment-threads
|
||||
[{:keys [profile-id file-id] :as params}]
|
||||
|
@ -45,6 +50,8 @@
|
|||
(def sql:comment-threads
|
||||
"select distinct on (ct.id)
|
||||
ct.*,
|
||||
f.name as file_name,
|
||||
f.project_id as project_id,
|
||||
first_value(c.content) over w as content,
|
||||
(select count(1)
|
||||
from comment as c
|
||||
|
@ -55,6 +62,7 @@
|
|||
and c.created_at >= coalesce(cts.modified_at, ct.created_at)) as count_unread_comments
|
||||
from comment_thread as ct
|
||||
inner join comment as c on (c.thread_id = ct.id)
|
||||
inner join file as f on (f.id = ct.file_id)
|
||||
left join comment_thread_status as cts
|
||||
on (cts.thread_id = ct.id and
|
||||
cts.profile_id = ?)
|
||||
|
@ -62,10 +70,59 @@
|
|||
window w as (partition by c.thread_id order by c.created_at asc)")
|
||||
|
||||
(defn- retrieve-comment-threads
|
||||
[conn {:keys [profile-id file-id]}]
|
||||
[conn {:keys [profile-id file-id team-id]}]
|
||||
(files/check-read-permissions! conn profile-id file-id)
|
||||
(->> (db/exec! conn [sql:comment-threads profile-id file-id])
|
||||
(into [] (map decode-row))))
|
||||
|
||||
|
||||
;; --- Query: Unread Comment Threads
|
||||
|
||||
(declare retrieve-unread-comment-threads)
|
||||
|
||||
(s/def ::team-id ::us/uuid)
|
||||
(s/def ::unread-comment-threads
|
||||
(s/keys :req-un [::profile-id ::team-id]))
|
||||
|
||||
(sq/defquery ::unread-comment-threads
|
||||
[{:keys [profile-id team-id] :as params}]
|
||||
(with-open [conn (db/open)]
|
||||
(teams/check-read-permissions! conn profile-id team-id)
|
||||
(retrieve-unread-comment-threads conn params)))
|
||||
|
||||
(def sql:comment-threads-by-team
|
||||
"select distinct on (ct.id)
|
||||
ct.*,
|
||||
f.name as file_name,
|
||||
f.project_id as project_id,
|
||||
first_value(c.content) over w as content,
|
||||
(select count(1)
|
||||
from comment as c
|
||||
where c.thread_id = ct.id) as count_comments,
|
||||
(select count(1)
|
||||
from comment as c
|
||||
where c.thread_id = ct.id
|
||||
and c.created_at >= coalesce(cts.modified_at, ct.created_at)) as count_unread_comments
|
||||
from comment_thread as ct
|
||||
inner join comment as c on (c.thread_id = ct.id)
|
||||
inner join file as f on (f.id = ct.file_id)
|
||||
inner join project as p on (p.id = f.project_id)
|
||||
left join comment_thread_status as cts
|
||||
on (cts.thread_id = ct.id and
|
||||
cts.profile_id = ?)
|
||||
where p.team_id = ?
|
||||
window w as (partition by c.thread_id order by c.created_at asc)")
|
||||
|
||||
(def sql:unread-comment-threads-by-team
|
||||
(str "with threads as (" sql:comment-threads-by-team ")"
|
||||
"select * from threads where count_unread_comments > 0"))
|
||||
|
||||
(defn retrieve-unread-comment-threads
|
||||
[conn {:keys [profile-id team-id]}]
|
||||
(->> (db/exec! conn [sql:unread-comment-threads-by-team profile-id team-id])
|
||||
(into [] (map decode-row))))
|
||||
|
||||
|
||||
;; --- Query: Single Comment Thread
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
|
|
|
@ -193,6 +193,12 @@
|
|||
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)
|
||||
|
@ -202,7 +208,7 @@
|
|||
|
||||
(defn retrieve-file-users
|
||||
[conn id]
|
||||
(db/exec! conn [sql:file-users id id]))
|
||||
(db/exec! conn [sql:file-users id id id]))
|
||||
|
||||
(s/def ::file-users
|
||||
(s/keys :req-un [::profile-id ::id]))
|
||||
|
@ -215,17 +221,8 @@
|
|||
|
||||
;; --- Query: Shared Library Files
|
||||
|
||||
;; TODO: remove the counts, because they are no longer needed.
|
||||
|
||||
(def ^:private sql:shared-files
|
||||
"select f.*,
|
||||
(select count(*) from color as c
|
||||
where c.file_id = f.id
|
||||
and c.deleted_at is null) as colors_count,
|
||||
(select count(*) from media_object as m
|
||||
where m.file_id = f.id
|
||||
and m.is_local = false
|
||||
and m.deleted_at is null) as graphics_count
|
||||
"select f.*
|
||||
from file as f
|
||||
inner join project as p on (p.id = f.project_id)
|
||||
where f.is_shared = true
|
||||
|
|
|
@ -130,3 +130,38 @@
|
|||
(defn retrieve-team-members
|
||||
[conn team-id]
|
||||
(db/exec! conn [sql:team-members team-id]))
|
||||
|
||||
;; --- Query: Team Users
|
||||
|
||||
;; 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
|
||||
;; implemented in UI)
|
||||
|
||||
(def sql:team-users
|
||||
"select pf.id, pf.fullname, pf.photo
|
||||
from profile as pf
|
||||
inner join team_profile_rel as tpr on (tpr.profile_id = pf.id)
|
||||
where tpr.team_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 project as p on (ppr.project_id = p.id)
|
||||
where p.team_id = ?
|
||||
union
|
||||
select pf.id, pf.fullname, pf.photo
|
||||
from profile as pf
|
||||
inner join file_profile_rel as fpr on (fpr.profile_id = pf.id)
|
||||
inner join file as f on (fpr.file_id = f.id)
|
||||
inner join project as p on (f.project_id = p.id)
|
||||
where p.team_id = ?")
|
||||
|
||||
(s/def ::team-users
|
||||
(s/keys :req-un [::profile-id ::team-id]))
|
||||
|
||||
(sq/defquery ::team-users
|
||||
[{:keys [profile-id team-id]}]
|
||||
(with-open [conn (db/open)]
|
||||
(check-edition-permissions! conn profile-id team-id)
|
||||
(db/exec! conn [sql:team-users team-id team-id team-id])))
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue