mirror of
https://github.com/penpot/penpot.git
synced 2025-04-06 20:11:29 -05:00
Merge pull request #3388 from penpot/niwinz-bugfixes-2023-w26-2
⏪ Add backward compatibility layer for v1.20 and other fixes
This commit is contained in:
commit
9c0e594294
16 changed files with 230 additions and 160 deletions
|
@ -53,6 +53,10 @@
|
|||
- Fix problem with importation process [Taiga #5597](https://tree.taiga.io/project/penpot/issue/5597)
|
||||
- Fix problem with HSV color picker [#3317](https://github.com/penpot/penpot/issues/3317)
|
||||
- Fix problem with slashes in layers names for exporter [#3276](https://github.com/penpot/penpot/issues/3276)
|
||||
- Fix incorrect modified data on moving files on dashboard [Taiga #5530](https://tree.taiga.io/project/penpot/issue/5530)
|
||||
- Fix focus handling on comments edition [Taiga #5560](https://tree.taiga.io/project/penpot/issue/5560)
|
||||
- Fix incorrect fullname use on registring user after OIDC authentication [Taiga #5517](https://tree.taiga.io/project/penpot/issue/5517)
|
||||
- Fix incorrect modified-at on project after import file [Taiga #5268](https://tree.taiga.io/project/penpot/issue/5268)
|
||||
|
||||
### :arrow_up: Deps updates
|
||||
|
||||
|
|
|
@ -118,8 +118,7 @@
|
|||
(t/write! tw data)))
|
||||
(catch java.io.IOException _)
|
||||
(catch Throwable cause
|
||||
(l/warn :hint "unexpected error on encoding response"
|
||||
:cause cause))
|
||||
(l/error :hint "unexpected error on encoding response" :cause cause))
|
||||
(finally
|
||||
(.close ^OutputStream output-stream))))))
|
||||
|
||||
|
@ -132,8 +131,8 @@
|
|||
|
||||
(catch java.io.IOException _)
|
||||
(catch Throwable cause
|
||||
(l/warn :hint "unexpected error on encoding response"
|
||||
:cause cause))
|
||||
(l/error :hint "unexpected error on encoding response"
|
||||
:cause cause))
|
||||
(finally
|
||||
(.close ^OutputStream output-stream))))))
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
[::sm/word-string {:max 500}])
|
||||
|
||||
(def schema:token
|
||||
[::sm/word-string {:max 1000}])
|
||||
[::sm/word-string {:max 6000}])
|
||||
|
||||
;; ---- COMMAND: login with password
|
||||
|
||||
|
@ -323,9 +323,9 @@
|
|||
:extra-data ptoken})))
|
||||
|
||||
(defn register-profile
|
||||
[{:keys [::db/conn] :as cfg} {:keys [token] :as params}]
|
||||
[{:keys [::db/conn] :as cfg} {:keys [token fullname] :as params}]
|
||||
(let [claims (tokens/verify (::main/props cfg) {:token token :iss :prepared-register})
|
||||
params (merge params claims)
|
||||
params (assoc claims :fullname fullname)
|
||||
|
||||
is-active (or (:is-active params)
|
||||
(not (contains? cf/flags :email-verification)))
|
||||
|
|
|
@ -929,5 +929,10 @@
|
|||
::input (:path file)
|
||||
::project-id project-id
|
||||
::ignore-index-errors? true))]
|
||||
|
||||
(db/update! conn :project
|
||||
{:modified-at (dt/now)}
|
||||
{:id project-id})
|
||||
|
||||
(rph/with-meta ids
|
||||
{::audit/props {:file nil :file-ids ids}}))))
|
||||
|
|
|
@ -189,6 +189,8 @@
|
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn check-features-compatibility!
|
||||
"Function responsible to check if provided features are supported by
|
||||
the current backend"
|
||||
[features]
|
||||
(let [not-supported (set/difference features supported-features)]
|
||||
(when (seq not-supported)
|
||||
|
@ -248,47 +250,59 @@
|
|||
(into #{} (comp (filter pmap/pointer-map?)
|
||||
(map pmap/get-id)))))
|
||||
|
||||
;; FIXME: file locking
|
||||
(defn- process-components-v2-feature
|
||||
"A special case handling of the components/v2 feature."
|
||||
[conn {:keys [id features data] :as file}]
|
||||
(binding [pmap/*tracked* (atom {})]
|
||||
(let [data (ctf/migrate-to-components-v2 data)
|
||||
features (conj features "components/v2")
|
||||
features' (db/create-array conn "text" features)]
|
||||
(db/update! conn :file
|
||||
{:data (blob/encode data)
|
||||
:features features'}
|
||||
{:id id})
|
||||
(persist-pointers! conn id)
|
||||
(-> file
|
||||
(assoc :features features)
|
||||
(assoc :data data)))))
|
||||
|
||||
(defn handle-file-features!
|
||||
[conn {:keys [features] :as file} client-features]
|
||||
|
||||
;; Check features compatibility between the currently supported features on
|
||||
;; the current backend instance and the file retrieved from the database
|
||||
(check-features-compatibility! features)
|
||||
|
||||
(cond-> file
|
||||
(and (contains? features "components/v2")
|
||||
(not (contains? client-features "components/v2")))
|
||||
(as-> file (ex/raise :type :restriction
|
||||
:code :feature-mismatch
|
||||
:feature "components/v2"
|
||||
:hint "file has 'components/v2' feature enabled but frontend didn't specifies it"
|
||||
:file-id (:id file)))
|
||||
|
||||
;; This operation is needed because the components migration generates a new
|
||||
;; page with random id which is returned to the client; without persisting
|
||||
;; the migration this can cause that two simultaneous clients can have a
|
||||
;; different view of the file data and end persisting two pages with main
|
||||
;; components and breaking the whole file."
|
||||
(and (contains? client-features "components/v2")
|
||||
(not (contains? features "components/v2")))
|
||||
(as-> file (process-components-v2-feature conn file))
|
||||
|
||||
;; This operation is needed for backward comapatibility with frontends that
|
||||
;; does not support pointer-map resolution mechanism; this just resolves the
|
||||
;; pointers on backend and return a complete file.
|
||||
(and (contains? features "storage/pointer-map")
|
||||
(not (contains? client-features "storage/pointer-map")))
|
||||
(process-pointers deref)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; QUERY COMMANDS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn handle-file-features!
|
||||
[conn {:keys [id features data] :as file} client-features]
|
||||
|
||||
(when (and (contains? features "components/v2")
|
||||
(not (contains? client-features "components/v2")))
|
||||
(ex/raise :type :restriction
|
||||
:code :feature-mismatch
|
||||
:feature "components/v2"
|
||||
:hint "file has 'components/v2' feature enabled but frontend didn't specifies it"))
|
||||
|
||||
;; NOTE: this operation is needed because the components migration
|
||||
;; generates a new page with random id which is returned to the
|
||||
;; client; without persisting the migration this can cause that two
|
||||
;; simultaneous clients can have a different view of the file data
|
||||
;; and end persisting two pages with main components and breaking
|
||||
;; the whole file
|
||||
(let [file (if (and (contains? client-features "components/v2")
|
||||
(not (contains? features "components/v2")))
|
||||
(binding [pmap/*tracked* (atom {})]
|
||||
(let [data (ctf/migrate-to-components-v2 data)
|
||||
features (conj features "components/v2")
|
||||
features' (db/create-array conn "text" features)]
|
||||
(db/update! conn :file
|
||||
{:data (blob/encode data)
|
||||
:features features'}
|
||||
{:id id})
|
||||
(persist-pointers! conn id)
|
||||
(-> file
|
||||
(assoc :features features)
|
||||
(assoc :data data))))
|
||||
file)]
|
||||
|
||||
(cond-> file
|
||||
(and (contains? features "storage/pointer-map")
|
||||
(not (contains? client-features "storage/pointer-map")))
|
||||
(process-pointers deref))))
|
||||
|
||||
;; --- COMMAND QUERY: get-file (by id)
|
||||
|
||||
(sm/def! ::features
|
||||
|
@ -331,7 +345,7 @@
|
|||
([conn id client-features]
|
||||
(get-file conn id client-features nil))
|
||||
([conn id client-features project-id]
|
||||
;; here we check if client requested features are supported
|
||||
;; here we check if client requested features are supported
|
||||
(check-features-compatibility! client-features)
|
||||
(binding [pmap/*load-fn* (partial load-pointer conn id)]
|
||||
(let [params (merge {:id id}
|
||||
|
|
|
@ -323,3 +323,13 @@
|
|||
:rfn (fn [^Reader rdr]
|
||||
(let [^List x (read-object! rdr)]
|
||||
(Matrix. (.get x 0) (.get x 1) (.get x 2) (.get x 3) (.get x 4) (.get x 5))))})
|
||||
|
||||
|
||||
;; Backward compatibility for 1.19 with v1.20;
|
||||
|
||||
(add-handlers!
|
||||
{:name "penpot/geom/rect"
|
||||
:rfn read-map-like}
|
||||
{:name "penpot/shape"
|
||||
:rfn read-map-like})
|
||||
|
||||
|
|
|
@ -96,22 +96,25 @@
|
|||
"Get the parent shape linked to a component for this shape, if any"
|
||||
([objects shape] (get-component-shape objects shape nil))
|
||||
([objects shape {:keys [allow-main?] :or {allow-main? false} :as options}]
|
||||
(cond
|
||||
(nil? shape)
|
||||
nil
|
||||
(cond
|
||||
(nil? shape)
|
||||
nil
|
||||
|
||||
(and (not (ctk/in-component-copy? shape)) (not allow-main?))
|
||||
nil
|
||||
(= uuid/zero (:id shape))
|
||||
nil
|
||||
|
||||
(ctk/instance-root? shape)
|
||||
shape
|
||||
(and (not (ctk/in-component-copy? shape)) (not allow-main?))
|
||||
nil
|
||||
|
||||
:else
|
||||
(get-component-shape objects (get objects (:parent-id shape)) options))))
|
||||
(ctk/instance-root? shape)
|
||||
shape
|
||||
|
||||
:else
|
||||
(get-component-shape objects (get objects (:parent-id shape)) options))))
|
||||
|
||||
(defn in-component-main?
|
||||
"Check if the shape is inside a component non-main instance.
|
||||
|
||||
|
||||
Note that we must iterate on the parents because non-root shapes in
|
||||
a main component have not any discriminating attribute."
|
||||
[objects shape]
|
||||
|
|
|
@ -134,14 +134,13 @@
|
|||
(rx/throw {:type :comment-error})))))))))
|
||||
|
||||
(defn update-comment-thread-status
|
||||
[{:keys [id] :as thread}]
|
||||
(dm/assert! (comment-thread? thread))
|
||||
[thread-id]
|
||||
(ptk/reify ::update-comment-thread-status
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [done #(d/update-in-when % [:comment-threads id] assoc :count-unread-comments 0)
|
||||
(let [done #(d/update-in-when % [:comment-threads thread-id] assoc :count-unread-comments 0)
|
||||
share-id (-> state :viewer-local :share-id)]
|
||||
(->> (rp/cmd! :update-comment-thread-status {:id id :share-id share-id})
|
||||
(->> (rp/cmd! :update-comment-thread-status {:id thread-id :share-id share-id})
|
||||
(rx/map (constantly done))
|
||||
(rx/catch #(rx/throw {:type :comment-error})))))))
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[app.common.data.macros :as dm]
|
||||
[app.common.pages :as cp]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.time :as dt]
|
||||
[app.common.uri :as u]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
|
@ -872,10 +873,14 @@
|
|||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [origin-project (get-in state [:dashboard-files (first ids) :project-id])]
|
||||
(let [origin-project (get-in state [:dashboard-files (first ids) :project-id])
|
||||
update-project (fn [project]
|
||||
(-> project
|
||||
(update :count #(+ % (count ids)))
|
||||
(assoc :modified-at (dt/now))))]
|
||||
(-> state
|
||||
(d/update-in-when [:dashboard-projects origin-project] update :count #(- % (count ids)))
|
||||
(d/update-in-when [:dashboard-projects project-id] update :count #(+ % (count ids))))))
|
||||
(d/update-in-when [:dashboard-projects origin-project] update-project)
|
||||
(d/update-in-when [:dashboard-projects project-id] update-project))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
|
|
|
@ -165,12 +165,22 @@
|
|||
[{:keys [code] :as error}]
|
||||
(cond
|
||||
(= :feature-mismatch code)
|
||||
(let [message (tr "errors.feature-mismatch" (:feature error))]
|
||||
(st/emit! (modal/show {:type :alert :message message})))
|
||||
(let [message (tr "errors.feature-mismatch" (:feature error))
|
||||
team-id (:current-team-id @st/state)
|
||||
project-id (:current-project-id @st/state)
|
||||
on-accept #(if (and project-id team-id)
|
||||
(st/emit! (rt/nav :dashboard-files {:team-id team-id :project-id project-id}))
|
||||
(set! (.-href glob/location) ""))]
|
||||
(st/emit! (modal/show {:type :alert :message message :on-accept on-accept})))
|
||||
|
||||
(= :features-not-supported code)
|
||||
(let [message (tr "errors.feature-not-supported" (:feature error))]
|
||||
(st/emit! (modal/show {:type :alert :message message})))
|
||||
(let [message (tr "errors.feature-not-supported" (:feature error))
|
||||
team-id (:current-team-id @st/state)
|
||||
project-id (:current-project-id @st/state)
|
||||
on-accept #(if (and project-id team-id)
|
||||
(st/emit! (rt/nav :dashboard-files {:team-id team-id :project-id project-id}))
|
||||
(set! (.-href glob/location) ""))]
|
||||
(st/emit! (modal/show {:type :alert :message message :on-accept on-accept})))
|
||||
|
||||
(= :max-quote-reached code)
|
||||
(let [message (tr "errors.max-quote-reached" (:target error))]
|
||||
|
|
|
@ -10,11 +10,10 @@
|
|||
[app.main.store :as st]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr t]]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.keyboard :as k]
|
||||
[goog.events :as events]
|
||||
[rumext.v2 :as mf])
|
||||
(:import goog.events.EventType))
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc alert-dialog
|
||||
{::mf/register modal/components
|
||||
|
@ -26,29 +25,27 @@
|
|||
hint
|
||||
accept-label
|
||||
accept-style] :as props}]
|
||||
(let [locale (mf/deref i18n/locale)
|
||||
|
||||
on-accept (or on-accept identity)
|
||||
message (or message (t locale "ds.alert-title"))
|
||||
(let [on-accept (or on-accept identity)
|
||||
message (or message (tr "ds.alert-title"))
|
||||
accept-label (or accept-label (tr "ds.alert-ok"))
|
||||
accept-style (or accept-style :danger)
|
||||
title (or title (t locale "ds.alert-title"))
|
||||
title (or title (tr "ds.alert-title"))
|
||||
|
||||
accept-fn
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(st/emit! (modal/hide))
|
||||
(on-accept props)))]
|
||||
|
||||
(mf/with-effect
|
||||
(mf/with-effect []
|
||||
(letfn [(on-keydown [event]
|
||||
(when (k/enter? event)
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (modal/hide))
|
||||
(on-accept props)))]
|
||||
(->> (events/listen js/document EventType.KEYDOWN on-keydown)
|
||||
(->> (events/listen js/document "keydown" on-keydown)
|
||||
(partial events/unlistenByKey))))
|
||||
|
||||
[:div.modal-overlay
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
(ns app.main.ui.comments
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.config :as cfg]
|
||||
|
@ -19,38 +20,55 @@
|
|||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.keyboard :as kbd]
|
||||
[app.util.object :as obj]
|
||||
[app.util.time :as dt]
|
||||
[cuerdas.core :as str]
|
||||
[okulary.core :as l]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc resizing-textarea
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [value (obj/get props "value" "")
|
||||
on-focus (obj/get props "on-focus")
|
||||
on-blur (obj/get props "on-blur")
|
||||
placeholder (obj/get props "placeholder")
|
||||
on-change (obj/get props "on-change")
|
||||
on-esc (obj/get props "on-esc")
|
||||
autofocus? (obj/get props "autofocus")
|
||||
{::mf/wrap-props false
|
||||
::mf/forward-ref true}
|
||||
[props ref]
|
||||
(let [value (d/nilv (unchecked-get props "value") "")
|
||||
on-focus (unchecked-get props "on-focus")
|
||||
on-blur (unchecked-get props "on-blur")
|
||||
placeholder (unchecked-get props "placeholder")
|
||||
on-change (unchecked-get props "on-change")
|
||||
on-esc (unchecked-get props "on-esc")
|
||||
autofocus? (unchecked-get props "autofocus")
|
||||
select-on-focus? (unchecked-get props "select-on-focus")
|
||||
|
||||
ref (mf/use-ref)
|
||||
local-ref (mf/use-ref)
|
||||
ref (or ref local-ref)
|
||||
|
||||
on-key-down
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(when (and (kbd/esc? event)
|
||||
(fn? on-esc))
|
||||
(on-esc event))))
|
||||
|
||||
on-change*
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps on-change)
|
||||
(fn [event]
|
||||
(let [content (dom/get-target-val event)]
|
||||
(on-change content))))]
|
||||
(on-change content))))
|
||||
|
||||
on-focus*
|
||||
(mf/use-fn
|
||||
(mf/deps select-on-focus? on-focus)
|
||||
(fn [event]
|
||||
(when (fn? on-focus)
|
||||
(on-focus event))
|
||||
|
||||
(when ^boolean select-on-focus?
|
||||
(let [target (dom/get-target event)]
|
||||
(dom/select-text! target)
|
||||
;; In webkit browsers the mouseup event will be called after the on-focus causing and unselect
|
||||
(.addEventListener target "mouseup" dom/prevent-default #js {:once true})))))
|
||||
]
|
||||
|
||||
|
||||
|
||||
(mf/use-layout-effect
|
||||
|
@ -64,7 +82,7 @@
|
|||
{:ref ref
|
||||
:auto-focus autofocus?
|
||||
:on-key-down on-key-down
|
||||
:on-focus on-focus
|
||||
:on-focus on-focus*
|
||||
:on-blur on-blur
|
||||
:value value
|
||||
:placeholder placeholder
|
||||
|
@ -76,24 +94,24 @@
|
|||
content (mf/use-state "")
|
||||
|
||||
on-focus
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
#(reset! show-buttons? true))
|
||||
|
||||
on-blur
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
#(reset! show-buttons? false))
|
||||
|
||||
on-change
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
#(reset! content %))
|
||||
|
||||
on-cancel
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
#(do (reset! content "")
|
||||
(reset! show-buttons? false)))
|
||||
|
||||
on-submit
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps thread @content)
|
||||
(fn []
|
||||
(st/emit! (dcm/add-comment thread @content))
|
||||
|
@ -128,7 +146,7 @@
|
|||
pos-y (* (:y position) zoom)
|
||||
|
||||
on-esc
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps draft)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
|
@ -137,13 +155,13 @@
|
|||
(st/emit! :interrupt))))
|
||||
|
||||
on-change
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps draft)
|
||||
(fn [content]
|
||||
(st/emit! (dcm/update-draft-thread {:content content}))))
|
||||
|
||||
on-submit
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps draft)
|
||||
(partial on-submit draft))]
|
||||
|
||||
|
@ -179,16 +197,20 @@
|
|||
(let [content (mf/use-state content)
|
||||
|
||||
on-change
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
#(reset! content %))
|
||||
|
||||
on-submit*
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps @content)
|
||||
(fn [] (on-submit @content)))]
|
||||
(fn [] (on-submit @content)))
|
||||
]
|
||||
|
||||
|
||||
[:div.reply-form.edit-form
|
||||
[:& resizing-textarea {:value @content
|
||||
:autofocus true
|
||||
:select-on-focus true
|
||||
:on-change on-change}]
|
||||
[:div.buttons
|
||||
[:input.btn-primary {:type "button" :value "Post" :on-click on-submit*}]
|
||||
|
@ -202,24 +224,24 @@
|
|||
edition? (mf/use-state false)
|
||||
|
||||
on-show-options
|
||||
(mf/use-callback #(reset! options true))
|
||||
(mf/use-fn #(reset! options true))
|
||||
|
||||
on-hide-options
|
||||
(mf/use-callback #(reset! options false))
|
||||
(mf/use-fn #(reset! options false))
|
||||
|
||||
on-edit-clicked
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(reset! options false)
|
||||
(reset! edition? true)))
|
||||
|
||||
on-delete-comment
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps comment)
|
||||
#(st/emit! (dcm/delete-comment comment)))
|
||||
|
||||
delete-thread
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps thread)
|
||||
#(st/emit! (dcm/close-thread)
|
||||
(if (= origin :viewer)
|
||||
|
@ -228,7 +250,7 @@
|
|||
|
||||
|
||||
on-delete-thread
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps thread)
|
||||
#(st/emit! (modal/show
|
||||
{:type :confirm
|
||||
|
@ -238,17 +260,17 @@
|
|||
:on-accept delete-thread})))
|
||||
|
||||
on-submit
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps comment thread)
|
||||
(fn [content]
|
||||
(reset! edition? false)
|
||||
(st/emit! (dcm/update-comment (assoc comment :content content)))))
|
||||
|
||||
on-cancel
|
||||
(mf/use-callback #(reset! edition? false))
|
||||
(mf/use-fn #(reset! edition? false))
|
||||
|
||||
toggle-resolved
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps thread)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
|
@ -268,6 +290,7 @@
|
|||
(if (:is-resolved thread)
|
||||
[:span i/checkbox-checked]
|
||||
[:span i/checkbox-unchecked])])
|
||||
|
||||
(when (= (:id profile) (:id owner))
|
||||
[:div.options
|
||||
[:div.options-icon {:on-click on-show-options} i/actions]])]
|
||||
|
@ -287,40 +310,45 @@
|
|||
[:li {:on-click on-delete-thread} (tr "labels.delete-comment-thread")]
|
||||
[:li {:on-click on-delete-comment} (tr "labels.delete-comment")])]]]))
|
||||
|
||||
(defn comments-ref
|
||||
[{:keys [id] :as thread}]
|
||||
(l/derived (l/in [:comments id]) st/state))
|
||||
(defn make-comments-ref
|
||||
[thread-id]
|
||||
(l/derived (l/in [:comments thread-id]) st/state))
|
||||
|
||||
(mf/defc thread-comments
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [thread zoom users origin position-modifier]}]
|
||||
(let [ref (mf/use-ref)
|
||||
pos (cond-> (:position thread)
|
||||
(some? position-modifier)
|
||||
(gpt/transform position-modifier))
|
||||
(let [ref (mf/use-ref)
|
||||
|
||||
pos-x (+ (* (:x pos) zoom) 14)
|
||||
pos-y (- (* (:y pos) zoom) 14)
|
||||
|
||||
comments-ref (mf/use-memo (mf/deps thread) #(comments-ref thread))
|
||||
thread-id (:id thread)
|
||||
thread-pos (:position thread)
|
||||
|
||||
pos (cond-> thread-pos
|
||||
(some? position-modifier)
|
||||
(gpt/transform position-modifier))
|
||||
|
||||
pos-x (+ (* (:x pos) zoom) 14)
|
||||
pos-y (- (* (:y pos) zoom) 14)
|
||||
|
||||
comments-ref (mf/with-memo [thread-id]
|
||||
(make-comments-ref thread-id))
|
||||
comments-map (mf/deref comments-ref)
|
||||
comments (->> (vals comments-map)
|
||||
(sort-by :created-at))
|
||||
|
||||
comments (mf/with-memo [comments-map]
|
||||
(->> (vals comments-map)
|
||||
(sort-by :created-at)))
|
||||
|
||||
comment (first comments)]
|
||||
|
||||
(mf/use-layout-effect
|
||||
(mf/deps thread)
|
||||
#(st/emit! (dcm/retrieve-comments (:id thread))))
|
||||
(mf/with-effect [thread-id]
|
||||
(st/emit! (dcm/retrieve-comments thread-id)))
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps thread)
|
||||
#(st/emit! (dcm/update-comment-thread-status thread)))
|
||||
(mf/with-effect [thread-id]
|
||||
(st/emit! (dcm/update-comment-thread-status thread-id)))
|
||||
|
||||
(mf/use-layout-effect
|
||||
(mf/deps thread comments-map)
|
||||
(fn []
|
||||
(when-let [node (mf/ref-val ref)]
|
||||
(dom/scroll-into-view-if-needed! node))))
|
||||
(mf/with-layout-effect [thread-pos comments-map]
|
||||
(when-let [node (mf/ref-val ref)]
|
||||
(dom/scroll-into-view-if-needed! node)))
|
||||
|
||||
(when (some? comment)
|
||||
[:div.thread-content
|
||||
|
@ -345,22 +373,22 @@
|
|||
(defn use-buble
|
||||
[zoom {:keys [position frame-id]}]
|
||||
(let [dragging-ref (mf/use-ref false)
|
||||
start-ref (mf/use-ref nil)
|
||||
start-ref (mf/use-ref nil)
|
||||
|
||||
state (mf/use-state {:hover false
|
||||
:new-position-x nil
|
||||
:new-position-y nil
|
||||
:new-frame-id frame-id})
|
||||
state (mf/use-state {:hover false
|
||||
:new-position-x nil
|
||||
:new-position-y nil
|
||||
:new-frame-id frame-id})
|
||||
|
||||
on-pointer-down
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(dom/capture-pointer event)
|
||||
(mf/set-ref-val! dragging-ref true)
|
||||
(mf/set-ref-val! start-ref (dom/get-client-position event))))
|
||||
|
||||
on-pointer-up
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps (select-keys @state [:new-position-x :new-position-y :new-frame-id]))
|
||||
(fn [_ thread]
|
||||
(when (and
|
||||
|
@ -369,7 +397,7 @@
|
|||
(st/emit! (dwcm/update-comment-thread-position thread [(:new-position-x @state) (:new-position-y @state)])))))
|
||||
|
||||
on-lost-pointer-capture
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(dom/release-pointer event)
|
||||
(mf/set-ref-val! dragging-ref false)
|
||||
|
@ -378,7 +406,7 @@
|
|||
(swap! state assoc :new-position-y nil)))
|
||||
|
||||
on-pointer-move
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps position zoom)
|
||||
(fn [event]
|
||||
(when-let [_ (mf/ref-val dragging-ref)]
|
||||
|
@ -416,7 +444,7 @@
|
|||
pos-y (* (or (:new-position-y @state) (:y pos)) zoom)
|
||||
|
||||
on-pointer-down*
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps origin was-open? open? drag? on-pointer-down)
|
||||
(fn [event]
|
||||
(when (not= origin :viewer)
|
||||
|
@ -427,7 +455,7 @@
|
|||
(on-pointer-down event))))
|
||||
|
||||
on-pointer-up*
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps origin thread was-open? drag? on-pointer-up)
|
||||
(fn [event]
|
||||
(when (not= origin :viewer)
|
||||
|
@ -439,7 +467,7 @@
|
|||
(st/emit! (dcm/open-thread thread))))))
|
||||
|
||||
on-pointer-move*
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps origin drag? on-pointer-move)
|
||||
(fn [event]
|
||||
(when (not= origin :viewer)
|
||||
|
@ -448,7 +476,7 @@
|
|||
(on-pointer-move event))))
|
||||
|
||||
on-click*
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps origin thread on-click)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
|
@ -472,7 +500,7 @@
|
|||
[{:keys [item users on-click]}]
|
||||
(let [owner (get users (:owner-id item))
|
||||
on-click*
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps item)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
|
|
|
@ -199,11 +199,6 @@
|
|||
(when (and (some? current) (not (.contains current target)))
|
||||
(dom/blur! current)))))))
|
||||
|
||||
on-mouse-up
|
||||
(mf/use-callback
|
||||
(fn [event]
|
||||
(dom/prevent-default event)))
|
||||
|
||||
handle-focus
|
||||
(mf/use-callback
|
||||
(fn [event]
|
||||
|
@ -212,9 +207,9 @@
|
|||
(on-focus event))
|
||||
|
||||
(when select-on-focus?
|
||||
(-> event (dom/get-target) (.select))
|
||||
(dom/select-text! event)
|
||||
;; In webkit browsers the mouseup event will be called after the on-focus causing and unselect
|
||||
(.addEventListener target "mouseup" on-mouse-up #js {"once" true})))))
|
||||
(.addEventListener target "mouseup" dom/prevent-default #js {:once true})))))
|
||||
|
||||
props (-> props
|
||||
(obj/without ["value" "onChange" "nillable" "onFocus"])
|
||||
|
|
|
@ -255,6 +255,7 @@
|
|||
(fn []
|
||||
(st/emit! (dd/fetch-files {:project-id project-id})
|
||||
(dd/fetch-recent-files (:id team))
|
||||
(dd/fetch-projects)
|
||||
(dd/clear-selected-files))))]
|
||||
|
||||
(mf/with-effect
|
||||
|
|
|
@ -29,12 +29,12 @@
|
|||
[]
|
||||
(let [{cmode :mode cshow :show} (mf/deref refs/comments-local)
|
||||
update-mode
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(fn [mode]
|
||||
(st/emit! (dcm/update-filters {:mode mode}))))
|
||||
|
||||
update-show
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(fn [mode]
|
||||
(st/emit! (dcm/update-filters {:show mode}))))]
|
||||
|
||||
|
@ -76,7 +76,7 @@
|
|||
page-id (or page-id (mf/use-ctx ctx/current-page-id))
|
||||
|
||||
on-thread-click
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps page-id)
|
||||
(fn [thread]
|
||||
(when (not= page-id (:page-id thread))
|
||||
|
|
|
@ -234,7 +234,7 @@
|
|||
|
||||
(defn select-text!
|
||||
[^js node]
|
||||
(when (and (some? node) (or (= "INPUT" (.-tagName node)) (= "TEXTAREA" (.-tagName node))))
|
||||
(when (some? node)
|
||||
(.select ^js node)))
|
||||
|
||||
(defn ^boolean equals?
|
||||
|
|
Loading…
Add table
Reference in a new issue