diff --git a/CHANGES.md b/CHANGES.md index 966de9ca51..310170e310 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,7 @@ - Allow for nested and rotated boards inside other boards and groups [Taiga #2874](https://tree.taiga.io/project/penpot/us/2874?milestone=319982) - View mode improvements to enable access and use in different conditions [Taiga #3023](https://tree.taiga.io/project/penpot/us/3023) +- Improved share link options. Now you can allow non-team members to comment and/or inspect [Taiga #3056] (https://tree.taiga.io/project/penpot/us/3056) ### :bug: Bugs fixed ### :arrow_up: Deps updates diff --git a/backend/src/app/migrations.clj b/backend/src/app/migrations.clj index 068ff52023..9b55fb4ca7 100644 --- a/backend/src/app/migrations.clj +++ b/backend/src/app/migrations.clj @@ -228,13 +228,13 @@ :fn (mg/resource "app/migrations/sql/0072-mod-file-object-thumbnail-table.sql")} {:name "0073-mod-file-media-object-constraints" - :fn (mg/resource "app/migrations/sql/0073-mod-file-media-object-constraints.sql")} - - {:name "0073-mod-share-link-table" - :fn (mg/resource "app/migrations/sql/0073-mod-share-link-table.sql")} + :fn (mg/resource "app/migrations/sql/0073-mod-file-media-object-constraints.sql")} {:name "0074-mod-file-library-rel-constraints" :fn (mg/resource "app/migrations/sql/0074-mod-file-library-rel-constraints.sql")} + + {:name "0075-mod-share-link-table" + :fn (mg/resource "app/migrations/sql/0075-mod-share-link-table.sql")} ]) diff --git a/backend/src/app/migrations/sql/0073-mod-share-link-table.sql b/backend/src/app/migrations/sql/0075-mod-share-link-table.sql similarity index 100% rename from backend/src/app/migrations/sql/0073-mod-share-link-table.sql rename to backend/src/app/migrations/sql/0075-mod-share-link-table.sql diff --git a/backend/src/app/rpc/mutations/comments.clj b/backend/src/app/rpc/mutations/comments.clj index 438cfdebd9..45e75a626f 100644 --- a/backend/src/app/rpc/mutations/comments.clj +++ b/backend/src/app/rpc/mutations/comments.clj @@ -26,19 +26,21 @@ (s/def ::page-id ::us/uuid) (s/def ::file-id ::us/uuid) +(s/def ::share-id (s/nilable ::us/uuid)) (s/def ::profile-id ::us/uuid) (s/def ::position ::gpt/point) (s/def ::content ::us/string) (s/def ::create-comment-thread - (s/keys :req-un [::profile-id ::file-id ::position ::content ::page-id])) + (s/keys :req-un [::profile-id ::file-id ::position ::content ::page-id] + :opt-un [::share-id])) (sv/defmethod ::create-comment-thread {::retry/max-retries 3 ::retry/matches retry/conflict-db-insert?} - [{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}] + [{:keys [pool] :as cfg} {:keys [profile-id file-id share-id] :as params}] (db/with-atomic [conn pool] - (files/check-read-permissions! conn profile-id file-id) + (files/check-comment-permissions! conn profile-id file-id share-id) (create-comment-thread conn params))) (defn- retrieve-next-seqn @@ -92,18 +94,20 @@ ;; --- Mutation: Update Comment Thread Status (s/def ::id ::us/uuid) +(s/def ::share-id (s/nilable ::us/uuid)) (s/def ::update-comment-thread-status - (s/keys :req-un [::profile-id ::id])) + (s/keys :req-un [::profile-id ::id] + :opt-un [::share-id])) (sv/defmethod ::update-comment-thread-status - [{:keys [pool] :as cfg} {:keys [profile-id id] :as params}] + [{:keys [pool] :as cfg} {:keys [profile-id id share-id] :as params}] (db/with-atomic [conn pool] (let [cthr (db/get-by-id conn :comment-thread id {:for-update true})] (when-not cthr (ex/raise :type :not-found)) - (files/check-read-permissions! conn profile-id (:file-id cthr)) + (files/check-comment-permissions! conn profile-id (:file-id cthr) share-id) (upsert-comment-thread-status! conn profile-id (:id cthr))))) (def sql:upsert-comment-thread-status @@ -122,16 +126,17 @@ (s/def ::is-resolved ::us/boolean) (s/def ::update-comment-thread - (s/keys :req-un [::profile-id ::id ::is-resolved])) + (s/keys :req-un [::profile-id ::id ::is-resolved] + :opt-un [::share-id])) (sv/defmethod ::update-comment-thread - [{:keys [pool] :as cfg} {:keys [profile-id id is-resolved] :as params}] + [{:keys [pool] :as cfg} {:keys [profile-id id is-resolved share-id] :as params}] (db/with-atomic [conn pool] (let [thread (db/get-by-id conn :comment-thread id {:for-update true})] (when-not thread (ex/raise :type :not-found)) - (files/check-read-permissions! conn profile-id (:file-id thread)) + (files/check-comment-permissions! conn profile-id (:file-id thread) share-id) (db/update! conn :comment-thread {:is-resolved is-resolved} @@ -142,10 +147,11 @@ ;; --- Mutation: Add Comment (s/def ::add-comment - (s/keys :req-un [::profile-id ::thread-id ::content])) + (s/keys :req-un [::profile-id ::thread-id ::content] + :opt-un [::share-id])) (sv/defmethod ::add-comment - [{:keys [pool] :as cfg} {:keys [profile-id thread-id content] :as params}] + [{:keys [pool] :as cfg} {:keys [profile-id thread-id content share-id] :as params}] (db/with-atomic [conn pool] (let [thread (-> (db/get-by-id conn :comment-thread thread-id {:for-update true}) (comments/decode-row)) @@ -155,7 +161,7 @@ (when-not thread (ex/raise :type :not-found)) ;; Permission Checks - (files/check-read-permissions! conn profile-id (:file-id thread)) + (files/check-comment-permissions! conn profile-id (:file-id thread) share-id) ;; Update the page-name cachedattribute on comment thread table. (when (not= pname (:page-name thread)) @@ -199,10 +205,11 @@ ;; --- Mutation: Update Comment (s/def ::update-comment - (s/keys :req-un [::profile-id ::id ::content])) + (s/keys :req-un [::profile-id ::id ::content] + :opt-un [::share-id])) (sv/defmethod ::update-comment - [{:keys [pool] :as cfg} {:keys [profile-id id content] :as params}] + [{:keys [pool] :as cfg} {:keys [profile-id id content share-id] :as params}] (db/with-atomic [conn pool] (let [comment (db/get-by-id conn :comment id {:for-update true}) _ (when-not comment (ex/raise :type :not-found)) @@ -210,7 +217,7 @@ _ (when-not thread (ex/raise :type :not-found)) pname (retrieve-page-name conn thread)] - (files/check-read-permissions! conn profile-id (:file-id thread)) + (files/check-comment-permissions! conn profile-id (:file-id thread) share-id) ;; Don't allow edit comments to not owners (when-not (= (:owner-id thread) profile-id) diff --git a/backend/src/app/rpc/permissions.clj b/backend/src/app/rpc/permissions.clj index 363f967e65..773038253b 100644 --- a/backend/src/app/rpc/permissions.clj +++ b/backend/src/app/rpc/permissions.clj @@ -53,6 +53,16 @@ ([perms] (:can-read perms)) ([conn & args] (check (apply qfn conn args))))) +(defn make-comment-predicate-fn + "A simple factory for comment permission predicate functions." + [qfn] + (us/assert fn? qfn) + (fn check + ([perms] + (and (:is-logged perms) (= (:who-comment perms) "all"))) + ([conn & args] + (check (apply qfn conn args))))) + (defn make-check-fn "Helper that converts a predicate permission function to a check function (function that raises an exception)." diff --git a/backend/src/app/rpc/queries/comments.clj b/backend/src/app/rpc/queries/comments.clj index 1894022f91..3815ef31c9 100644 --- a/backend/src/app/rpc/queries/comments.clj +++ b/backend/src/app/rpc/queries/comments.clj @@ -25,16 +25,16 @@ (s/def ::team-id ::us/uuid) (s/def ::file-id ::us/uuid) +(s/def ::share-id (s/nilable ::us/uuid)) (s/def ::comment-threads (s/and (s/keys :req-un [::profile-id] - :opt-un [::file-id ::team-id]) + :opt-un [::file-id ::share-id ::team-id]) #(or (:file-id %) (:team-id %)))) (sv/defmethod ::comment-threads - [{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}] + [{:keys [pool] :as cfg} params] (with-open [conn (db/open pool)] - (files/check-read-permissions! conn profile-id file-id) (retrieve-comment-threads conn params))) (def sql:comment-threads @@ -60,8 +60,8 @@ window w as (partition by c.thread_id order by c.created_at asc)") (defn- retrieve-comment-threads - [conn {:keys [profile-id file-id]}] - (files/check-read-permissions! conn profile-id file-id) + [conn {:keys [profile-id file-id share-id]}] + (files/check-comment-permissions! conn profile-id file-id share-id) (->> (db/exec! conn [sql:comment-threads profile-id file-id]) (into [] (map decode-row)))) @@ -116,13 +116,15 @@ ;; --- Query: Single Comment Thread (s/def ::id ::us/uuid) +(s/def ::share-id (s/nilable ::us/uuid)) (s/def ::comment-thread - (s/keys :req-un [::profile-id ::file-id ::id])) + (s/keys :req-un [::profile-id ::file-id ::id] + :opt-un [::share-id])) (sv/defmethod ::comment-thread - [{:keys [pool] :as cfg} {:keys [profile-id file-id id] :as params}] + [{:keys [pool] :as cfg} {:keys [profile-id file-id id share-id] :as params}] (with-open [conn (db/open pool)] - (files/check-read-permissions! conn profile-id file-id) + (files/check-comment-permissions! conn profile-id file-id share-id) (let [sql (str "with threads as (" sql:comment-threads ")" "select * from threads where id = ?")] (-> (db/exec-one! conn [sql profile-id file-id id]) @@ -133,15 +135,17 @@ (declare retrieve-comments) (s/def ::file-id ::us/uuid) +(s/def ::share-id (s/nilable ::us/uuid)) (s/def ::thread-id ::us/uuid) (s/def ::comments - (s/keys :req-un [::profile-id ::thread-id])) + (s/keys :req-un [::profile-id ::thread-id] + :opt-un [::share-id])) (sv/defmethod ::comments - [{:keys [pool] :as cfg} {:keys [profile-id thread-id] :as params}] + [{:keys [pool] :as cfg} {:keys [profile-id thread-id share-id] :as params}] (with-open [conn (db/open pool)] (let [thread (db/get-by-id conn :comment-thread thread-id)] - (files/check-read-permissions! conn profile-id (:file-id thread)) + (files/check-comment-permissions! conn profile-id (:file-id thread) share-id) (retrieve-comments conn thread-id)))) (def sql:comments @@ -153,3 +157,40 @@ [conn thread-id] (->> (db/exec! conn [sql:comments thread-id]) (into [] (map decode-row)))) + +;; file-comments-users + +(declare retrieve-file-comments-users) + +(s/def ::file-id ::us/uuid) +(s/def ::share-id (s/nilable ::us/uuid)) + +(s/def ::file-comments-users + (s/keys :req-un [::profile-id ::file-id] + :opt-un [::share-id])) + +(sv/defmethod ::file-comments-users + [{:keys [pool] :as cfg} {:keys [profile-id file-id share-id]}] + (with-open [conn (db/open pool)] + (files/check-comment-permissions! conn profile-id file-id share-id) + (retrieve-file-comments-users conn file-id profile-id))) + +(def sql:file-comment-users + "select p.id, + p.email, + p.fullname as name, + p.fullname as fullname, + p.photo_id, + p.is_active + from profile p + where p.id in + (select owner_id from comment + where thread_id in + (select id from comment_thread + where file_id=?)) + or p.id=? + ") ;; all the users that had comment the file, plus the current user + +(defn retrieve-file-comments-users + [conn file-id profile-id] + (db/exec! conn [sql:file-comment-users file-id profile-id])) diff --git a/backend/src/app/rpc/queries/files.clj b/backend/src/app/rpc/queries/files.clj index 946a7902f8..9460be08ff 100644 --- a/backend/src/app/rpc/queries/files.clj +++ b/backend/src/app/rpc/queries/files.clj @@ -98,7 +98,9 @@ (some? perms) perms (some? ldata) {:type :share-link :can-read true - :flags (:flags ldata)})))) + :is-logged (some? profile-id) + :who-comment (:who-comment ldata) + :who-inspect (:who-inspect ldata)})))) (def has-edit-permissions? (perms/make-edition-predicate-fn get-permissions)) @@ -106,12 +108,26 @@ (def has-read-permissions? (perms/make-read-predicate-fn get-permissions)) +(def has-comment-permissions? + (perms/make-comment-predicate-fn get-permissions)) + (def check-edition-permissions! (perms/make-check-fn has-edit-permissions?)) (def check-read-permissions! (perms/make-check-fn has-read-permissions?)) +;; A user has comment permissions if she has read permissions, or comment permissions +(defn check-comment-permissions! + [conn profile-id file-id share-id] + (let [can-read (has-read-permissions? conn profile-id file-id) + can-comment (has-comment-permissions? conn profile-id file-id share-id) + ] + (when-not (or can-read can-comment) + (ex/raise :type :not-found + :code :object-not-found + :hint "not found")))) + ;; --- Query: Files search ;; TODO: this query need to a good refactor diff --git a/backend/src/app/rpc/queries/viewer.clj b/backend/src/app/rpc/queries/viewer.clj index b98de0a381..a82e08e65c 100644 --- a/backend/src/app/rpc/queries/viewer.clj +++ b/backend/src/app/rpc/queries/viewer.clj @@ -9,9 +9,9 @@ [app.common.exceptions :as ex] [app.common.spec :as us] [app.db :as db] + [app.rpc.queries.comments :as comments] [app.rpc.queries.files :as files] - [app.rpc.queries.share-link :as slnk] - [app.rpc.queries.teams :as teams] + [app.rpc.queries.share-link :as slnk] [app.util.services :as sv] [clojure.spec.alpha :as s] [promesa.core :as p])) @@ -23,11 +23,11 @@ (db/get-by-id pool :project id {:columns [:id :name :team-id]})) (defn- retrieve-bundle - [{:keys [pool] :as cfg} file-id] + [{:keys [pool] :as cfg} file-id profile-id] (p/let [file (files/retrieve-file cfg file-id) project (retrieve-project pool (:project-id file)) libs (files/retrieve-file-libraries cfg false file-id) - users (teams/retrieve-users pool (:team-id project)) + users (comments/retrieve-file-comments-users pool file-id profile-id) links (->> (db/query pool :share-link {:file-id file-id}) (mapv slnk/decode-share-link-row)) @@ -54,7 +54,7 @@ (p/let [slink (slnk/retrieve-share-link pool file-id share-id) perms (files/get-permissions pool profile-id file-id share-id) thumbs (files/retrieve-object-thumbnails cfg file-id) - bundle (p/-> (retrieve-bundle cfg file-id) + bundle (p/-> (retrieve-bundle cfg file-id profile-id) (assoc :permissions perms) (assoc-in [:file :thumbnails] thumbs))] diff --git a/backend/test/app/services_viewer_test.clj b/backend/test/app/services_viewer_test.clj index 1b9d7c0133..e8a01c255a 100644 --- a/backend/test/app/services_viewer_test.clj +++ b/backend/test/app/services_viewer_test.clj @@ -49,7 +49,8 @@ :profile-id (:id prof) :file-id (:id file) :pages #{(get-in file [:data :pages 0])} - :flags #{}} + :who-comment "team" + :who-inspect "all"} out (th/mutation! data)] ;; (th/print-result! out) diff --git a/frontend/src/app/main/data/comments.cljs b/frontend/src/app/main/data/comments.cljs index 3dd8741c00..8d749e5f35 100644 --- a/frontend/src/app/main/data/comments.cljs +++ b/frontend/src/app/main/data/comments.cljs @@ -75,20 +75,23 @@ (ptk/reify ::create-comment-thread ptk/WatchEvent - (watch [_ _ _] - (->> (rp/mutation :create-comment-thread params) - (rx/mapcat #(rp/query :comment-thread {:file-id (:file-id %) :id (:id %)})) - (rx/map #(partial created %)) - (rx/catch #(rx/throw {:type :comment-error}))))))) + (watch [_ state _] + (let [share-id (-> state :viewer-local :share-id) + params (assoc params :share-id share-id)] + (->> (rp/mutation :create-comment-thread params) + (rx/mapcat #(rp/query :comment-thread {:file-id (:file-id %) :id (:id %) :share-id share-id})) + (rx/map #(partial created %)) + (rx/catch #(rx/throw {:type :comment-error})))))))) (defn update-comment-thread-status [{:keys [id] :as thread}] (us/assert ::comment-thread thread) (ptk/reify ::update-comment-thread-status ptk/WatchEvent - (watch [_ _ _] - (let [done #(d/update-in-when % [:comment-threads id] assoc :count-unread-comments 0)] - (->> (rp/mutation :update-comment-thread-status {:id id}) + (watch [_ state _] + (let [done #(d/update-in-when % [:comment-threads id] assoc :count-unread-comments 0) + share-id (-> state :viewer-local :share-id)] + (->> (rp/mutation :update-comment-thread-status {:id id :share-id share-id}) (rx/map (constantly done)) (rx/catch #(rx/throw {:type :comment-error}))))))) @@ -105,10 +108,11 @@ (d/update-in-when state [:comment-threads id] assoc :is-resolved is-resolved)) ptk/WatchEvent - (watch [_ _ _] - (->> (rp/mutation :update-comment-thread {:id id :is-resolved is-resolved}) - (rx/catch #(rx/throw {:type :comment-error})) - (rx/ignore))))) + (watch [_ state _] + (let [share-id (-> state :viewer-local :share-id)] + (->> (rp/mutation :update-comment-thread {:id id :is-resolved is-resolved :share-id share-id}) + (rx/catch #(rx/throw {:type :comment-error})) + (rx/ignore)))))) (defn add-comment @@ -119,12 +123,13 @@ (update-in state [:comments (:id thread)] assoc (:id comment) comment))] (ptk/reify ::create-comment ptk/WatchEvent - (watch [_ _ _] - (rx/concat - (->> (rp/mutation :add-comment {:thread-id (:id thread) :content content}) - (rx/map #(partial created %)) - (rx/catch #(rx/throw {:type :comment-error}))) - (rx/of (refresh-comment-thread thread))))))) + (watch [_ state _] + (let [share-id (-> state :viewer-local :share-id)] + (rx/concat + (->> (rp/mutation :add-comment {:thread-id (:id thread) :content content :share-id share-id}) + (rx/map #(partial created %)) + (rx/catch #(rx/throw {:type :comment-error}))) + (rx/of (refresh-comment-thread thread)))))))) (defn update-comment [{:keys [id content thread-id] :as comment}] @@ -135,10 +140,11 @@ (d/update-in-when state [:comments thread-id id] assoc :content content)) ptk/WatchEvent - (watch [_ _ _] - (->> (rp/mutation :update-comment {:id id :content content}) - (rx/catch #(rx/throw {:type :comment-error})) - (rx/ignore))))) + (watch [_ state _] + (let [share-id (-> state :viewer-local :share-id)] + (->> (rp/mutation :update-comment {:id id :content content :share-id share-id}) + (rx/catch #(rx/throw {:type :comment-error})) + (rx/ignore)))))) (defn delete-comment-thread [{:keys [id] :as thread}] @@ -151,10 +157,11 @@ (update :comment-threads dissoc id))) ptk/WatchEvent - (watch [_ _ _] - (->> (rp/mutation :delete-comment-thread {:id id}) - (rx/catch #(rx/throw {:type :comment-error})) - (rx/ignore))))) + (watch [_ state _] + (let [share-id (-> state :viewer-local :share-id)] + (->> (rp/mutation :delete-comment-thread {:id id :share-id share-id}) + (rx/catch #(rx/throw {:type :comment-error})) + (rx/ignore)))))) (defn delete-comment [{:keys [id thread-id] :as comment}] @@ -165,10 +172,11 @@ (d/update-in-when state [:comments thread-id] dissoc id)) ptk/WatchEvent - (watch [_ _ _] - (->> (rp/mutation :delete-comment {:id id}) - (rx/catch #(rx/throw {:type :comment-error})) - (rx/ignore))))) + (watch [_ state _] + (let [share-id (-> state :viewer-local :share-id)] + (->> (rp/mutation :delete-comment {:id id :share-id share-id}) + (rx/catch #(rx/throw {:type :comment-error})) + (rx/ignore)))))) (defn refresh-comment-thread [{:keys [id file-id] :as thread}] @@ -177,10 +185,11 @@ (assoc-in state [:comment-threads id] thread))] (ptk/reify ::refresh-comment-thread ptk/WatchEvent - (watch [_ _ _] - (->> (rp/query :comment-thread {:file-id file-id :id id}) - (rx/map #(partial fetched %)) - (rx/catch #(rx/throw {:type :comment-error}))))))) + (watch [_ state _] + (let [share-id (-> state :viewer-local :share-id)] + (->> (rp/query :comment-thread {:file-id file-id :id id :share-id share-id}) + (rx/map #(partial fetched %)) + (rx/catch #(rx/throw {:type :comment-error})))))))) (defn retrieve-comment-threads [file-id] @@ -189,10 +198,11 @@ (assoc state :comment-threads (d/index-by :id data)))] (ptk/reify ::retrieve-comment-threads ptk/WatchEvent - (watch [_ _ _] - (->> (rp/query :comment-threads {:file-id file-id}) - (rx/map #(partial fetched %)) - (rx/catch #(rx/throw {:type :comment-error}))))))) + (watch [_ state _] + (let [share-id (-> state :viewer-local :share-id)] + (->> (rp/query :comment-threads {:file-id file-id :share-id share-id}) + (rx/map #(partial fetched %)) + (rx/catch #(rx/throw {:type :comment-error})))))))) (defn retrieve-comments [thread-id] @@ -201,10 +211,11 @@ (update state :comments assoc thread-id (d/index-by :id comments)))] (ptk/reify ::retrieve-comments ptk/WatchEvent - (watch [_ _ _] - (->> (rp/query :comments {:thread-id thread-id}) - (rx/map #(partial fetched %)) - (rx/catch #(rx/throw {:type :comment-error}))))))) + (watch [_ state _] + (let [share-id (-> state :viewer-local :share-id)] + (->> (rp/query :comments {:thread-id thread-id :share-id share-id}) + (rx/map #(partial fetched %)) + (rx/catch #(rx/throw {:type :comment-error})))))))) (defn retrieve-unread-comment-threads "A event used mainly in dashboard for retrieve all unread threads of a team." diff --git a/frontend/src/app/main/data/users.cljs b/frontend/src/app/main/data/users.cljs index 275c4b1942..7ea9454379 100644 --- a/frontend/src/app/main/data/users.cljs +++ b/frontend/src/app/main/data/users.cljs @@ -436,7 +436,6 @@ (rx/map (constantly (fetch-profile))) (rx/catch on-error)))))) - (defn fetch-users [{:keys [team-id] :as params}] (us/assert ::us/uuid team-id) @@ -450,6 +449,20 @@ (->> (rp/query :team-users {:team-id team-id}) (rx/map #(partial fetched %))))))) +(defn fetch-file-comments-users + [{:keys [team-id] :as params}] + (us/assert ::us/uuid team-id) + (letfn [(fetched [users state] + (->> users + (d/index-by :id) + (assoc state :file-comments-users)))] + (ptk/reify ::fetch-team-users + ptk/WatchEvent + (watch [_ state _] + (let [share-id (-> state :viewer-local :share-id)] + (->> (rp/query :file-comments-users {:team-id team-id :share-id share-id}) + (rx/map #(partial fetched %)))))))) + ;; --- EVENT: request-account-deletion (defn request-account-deletion diff --git a/frontend/src/app/main/data/viewer.cljs b/frontend/src/app/main/data/viewer.cljs index 98f576c940..54c0422221 100644 --- a/frontend/src/app/main/data/viewer.cljs +++ b/frontend/src/app/main/data/viewer.cljs @@ -33,7 +33,9 @@ :selected #{} :collapsed #{} :overlays [] - :hover nil}) + :hover nil + :share-id "" + :file-comments-users []}) (declare fetch-comment-threads) (declare fetch-bundle) @@ -50,7 +52,7 @@ :opt-un [::share-id ::page-id])) (defn initialize - [{:keys [file-id] :as params}] + [{:keys [file-id share-id] :as params}] (us/assert ::initialize-params params) (ptk/reify ::initialize ptk/UpdateEvent @@ -61,7 +63,8 @@ (fn [lstate] (if (nil? lstate) default-local-state - lstate))))) + lstate))) + (assoc-in [:viewer-local :share-id] share-id))) ptk/WatchEvent (watch [_ _ _] @@ -138,7 +141,7 @@ (rx/of (go-to-frame-auto)))))))) (defn fetch-comment-threads - [{:keys [file-id page-id] :as params}] + [{:keys [file-id page-id share-id] :as params}] (letfn [(fetched [data state] (->> data (filter #(= page-id (:page-id %))) @@ -153,7 +156,7 @@ (ptk/reify ::fetch-comment-threads ptk/WatchEvent (watch [_ _ _] - (->> (rp/query :comment-threads {:file-id file-id}) + (->> (rp/query :comment-threads {:file-id file-id :share-id share-id}) (rx/map #(partial fetched %)) (rx/catch on-error)))))) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 7885fbc921..d146c13e7e 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -140,7 +140,7 @@ (unchecked-set ug/global "name" name))))) (defn- file-initialized - [{:keys [file users project libraries] :as bundle}] + [{:keys [file users project libraries file-comments-users] :as bundle}] (ptk/reify ::file-initialized ptk/UpdateEvent (update [_ state] @@ -156,7 +156,8 @@ ;; the version number #_(assoc :version 17) #_(app.common.pages.migrations/migrate-data 19)) - :workspace-libraries (d/index-by :id libraries))) + :workspace-libraries (d/index-by :id libraries) + :current-file-comments-users (d/index-by :id file-comments-users))) ptk/WatchEvent (watch [_ _ _] diff --git a/frontend/src/app/main/data/workspace/persistence.cljs b/frontend/src/app/main/data/workspace/persistence.cljs index d01df8f382..7b81dc8cda 100644 --- a/frontend/src/app/main/data/workspace/persistence.cljs +++ b/frontend/src/app/main/data/workspace/persistence.cljs @@ -258,20 +258,23 @@ [project-id file-id] (ptk/reify ::fetch-bundle ptk/WatchEvent - (watch [_ _ _] - (->> (rx/zip (rp/query :file-raw {:id file-id}) - (rp/query :team-users {:file-id file-id}) - (rp/query :project {:id project-id}) - (rp/query :file-libraries {:file-id file-id})) - (rx/take 1) - (rx/map (fn [[file-raw users project libraries]] - {:file-raw file-raw - :users users - :project project - :libraries libraries})) - (rx/mapcat (fn [{:keys [project] :as bundle}] - (rx/of (ptk/data-event ::bundle-fetched bundle) - (df/load-team-fonts (:team-id project))))))))) + (watch [_ state _] + (let [share-id (-> state :viewer-local :share-id)] + (->> (rx/zip (rp/query :file-raw {:id file-id}) + (rp/query :team-users {:file-id file-id}) + (rp/query :project {:id project-id}) + (rp/query :file-libraries {:file-id file-id}) + (rp/query :file-comments-users {:file-id file-id :share-id share-id})) + (rx/take 1) + (rx/map (fn [[file-raw users project libraries file-comments-users]] + {:file-raw file-raw + :users users + :project project + :libraries libraries + :file-comments-users file-comments-users})) + (rx/mapcat (fn [{:keys [project] :as bundle}] + (rx/of (ptk/data-event ::bundle-fetched bundle) + (df/load-team-fonts (:team-id project)))))))))) ;; --- Helpers diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index 3e709aea15..81d51b461d 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -387,6 +387,9 @@ (def users (l/derived :users st/state)) +(def current-file-comments-users + (l/derived :current-file-comments-users st/state)) + (def viewer-fullscreen? (l/derived (fn [state] (dm/get-in state [:viewer-local :fullscreen?])) diff --git a/frontend/src/app/main/ui/comments.cljs b/frontend/src/app/main/ui/comments.cljs index a7019d3627..5aa993a27f 100644 --- a/frontend/src/app/main/ui/comments.cljs +++ b/frontend/src/app/main/ui/comments.cljs @@ -345,7 +345,6 @@ (mf/defc comment-thread [{:keys [item users on-click] :as props}] (let [owner (get users (:owner-id item)) - on-click* (mf/use-callback (mf/deps item) diff --git a/frontend/src/app/main/ui/dashboard/comments.cljs b/frontend/src/app/main/ui/dashboard/comments.cljs index 582ea47ac4..11d3ef7011 100644 --- a/frontend/src/app/main/ui/dashboard/comments.cljs +++ b/frontend/src/app/main/ui/dashboard/comments.cljs @@ -31,7 +31,7 @@ show-dropdown (mf/use-fn #(reset! show-dropdown? true)) hide-dropdown (mf/use-fn #(reset! show-dropdown? false)) threads-map (mf/deref refs/comment-threads) - users (mf/deref refs/users) + users (mf/deref refs/current-file-comments-users) tgroups (->> (vals threads-map) (sort-by :modified-at) diff --git a/frontend/src/app/main/ui/modal.cljs b/frontend/src/app/main/ui/modal.cljs index 9152ac4a14..e4ae3e4bc2 100644 --- a/frontend/src/app/main/ui/modal.cljs +++ b/frontend/src/app/main/ui/modal.cljs @@ -84,6 +84,5 @@ [] (let [modal (mf/deref modal-ref)] (when modal - (.log js/console "modal"(clj->js modal)) [:& modal-wrapper {:data modal :key (:id modal)}]))) diff --git a/frontend/src/app/main/ui/share_link.cljs b/frontend/src/app/main/ui/share_link.cljs index 467988c430..490cb2f96c 100644 --- a/frontend/src/app/main/ui/share_link.cljs +++ b/frontend/src/app/main/ui/share_link.cljs @@ -30,8 +30,6 @@ :who-comment who-comment :who-inspect who-inspect}) - - (mf/defc share-link-dialog {::mf/register modal/components ::mf/register-as :share-link} diff --git a/frontend/src/app/main/ui/viewer.cljs b/frontend/src/app/main/ui/viewer.cljs index 7849c7bc45..6ccbc169cb 100644 --- a/frontend/src/app/main/ui/viewer.cljs +++ b/frontend/src/app/main/ui/viewer.cljs @@ -25,7 +25,7 @@ [app.main.ui.icons :as i] [app.main.ui.share-link] [app.main.ui.static :as static] - [app.main.ui.viewer.comments :refer [comments-layer comments-sidebar]] + [app.main.ui.viewer.comments :refer [comments-layer comments-sidebar]] [app.main.ui.viewer.handoff :as handoff] [app.main.ui.viewer.header :refer [header]] [app.main.ui.viewer.interactions :as interactions] @@ -84,7 +84,7 @@ (when show-comments-list [:& comments-sidebar {:users users :frame frame :page page}]) - + [:div.viewer-wrapper {:style {:width (:width wrapper-size) :height (:height wrapper-size)}} @@ -140,7 +140,7 @@ :on-click #(when (:close-click-outside overlay) (close-overlay (:frame overlay)))}]) [:div.viewport-container.viewer-overlay - + {:id (str "overlay-" (-> overlay :frame :id)) :style {:width (:width size-over) :height (:height size-over) @@ -169,6 +169,17 @@ (let [{:keys [page-id section index]} params {:keys [file users project permissions]} data + allowed (or + (= section :interactions) + (and (= section :comments) + (or (:can-edit permissions) + (and (true? (:is-logged permissions)) + (= (:who-comment permissions) "all")))) + (and (= section :handoff) + (or (:can-edit permissions) + (and (true? (:is-logged permissions)) + (= (:who-inspect permissions) "all"))))) + local (mf/deref refs/viewer-local) nav-scroll (:nav-scroll local) @@ -241,6 +252,9 @@ (when (nil? page) (ex/raise :type :not-found)) + (when (not allowed) + (st/emit! (dv/go-to-section :interactions))) + ;; Set the page title (mf/use-effect (mf/deps (:name file)) @@ -394,24 +408,23 @@ :index index :viewer-pagination viewer-pagination}] - - [:& viewer-wrapper - {:wrapper-size wrapper-size - :scroll scroll - :orig-frame orig-frame - :orig-viewport-ref orig-viewport-ref - :orig-size orig-size - :page page - :file file - :users users - :current-viewport-ref current-viewport-ref - :size size - :frame frame - :interactions-mode interactions-mode - :overlays overlays - :zoom zoom - :section section - :index index}]))]]])) + [:& viewer-wrapper + {:wrapper-size wrapper-size + :scroll scroll + :orig-frame orig-frame + :orig-viewport-ref orig-viewport-ref + :orig-size orig-size + :page page + :file file + :users users + :current-viewport-ref current-viewport-ref + :size size + :frame frame + :interactions-mode interactions-mode + :overlays overlays + :zoom zoom + :section section + :index index}]))]]])) ;; --- Component: Viewer Page diff --git a/frontend/src/app/main/ui/viewer/header.cljs b/frontend/src/app/main/ui/viewer/header.cljs index da39c63b70..5cde91686f 100644 --- a/frontend/src/app/main/ui/viewer/header.cljs +++ b/frontend/src/app/main/ui/viewer/header.cljs @@ -188,7 +188,7 @@ [:div.main-icon [:a {:on-click go-to-dashboard ;; If the user doesn't have permission we disable the link - :style {:pointer-events (when-not permissions "none")}} i/logo-icon]] + :style {:pointer-events (when-not (:can-edit permissions) "none")}} i/logo-icon]] [:& header-sitemap {:project project :file file :page page :frame frame :index index}]] @@ -199,7 +199,9 @@ :alt (tr "viewer.header.interactions-section" (sc/get-tooltip :open-interactions))} i/play] - (when (:can-edit permissions) + (when (or (:can-edit permissions) + (and (true? (:is-logged permissions)) + (= (:who-comment permissions) "all"))) [:button.mode-zone-button.tooltip.tooltip-bottom {:on-click #(navigate :comments) :class (dom/classnames :active (= section :comments)) @@ -208,7 +210,8 @@ (when (or (= (:type permissions) :membership) (and (= (:type permissions) :share-link) - (contains? (:flags permissions) :section-handoff))) + (true? (:is-logged permissions)) + (= (:who-inspect permissions) "all"))) [:button.mode-zone-button.tooltip.tooltip-bottom {:on-click go-to-handoff :class (dom/classnames :active (= section :handoff)) diff --git a/frontend/src/app/main/ui/workspace/comments.cljs b/frontend/src/app/main/ui/workspace/comments.cljs index 3963cf9330..e66781a01a 100644 --- a/frontend/src/app/main/ui/workspace/comments.cljs +++ b/frontend/src/app/main/ui/workspace/comments.cljs @@ -60,7 +60,7 @@ [{:keys [users threads page-id]}] (let [threads-map (mf/deref refs/threads-ref) profile (mf/deref refs/profile) - users-refs (mf/deref refs/users) + users-refs (mf/deref refs/current-file-comments-users) users (or users users-refs) local (mf/deref refs/comments-local) options? (mf/use-state false) diff --git a/frontend/src/app/main/ui/workspace/viewport/comments.cljs b/frontend/src/app/main/ui/workspace/viewport/comments.cljs index da82aeaf99..f13a88cfc8 100644 --- a/frontend/src/app/main/ui/workspace/viewport/comments.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/comments.cljs @@ -20,7 +20,7 @@ pos-y (* (- (:y vbox)) zoom) profile (mf/deref refs/profile) - users (mf/deref refs/users) + users (mf/deref refs/current-file-comments-users) local (mf/deref refs/comments-local) threads-map (mf/deref refs/threads-ref)