Merge remote-tracking branch 'origin/staging' into develop

This commit is contained in:
Andrey Antukh 2025-02-19 17:17:54 +01:00
commit d79fac7729
29 changed files with 555 additions and 461 deletions

View file

@ -72,6 +72,7 @@
(def profile-fetched?
(ptk/type? ::profile-fetched))
;; FIXME: make it as general purpose handler, not only on profile
(defn- on-fetch-profile-exception
[cause]
(let [data (ex-data cause)]

View file

@ -206,7 +206,7 @@
nil))
(rx/of
(cond
(some? frame-id) (go-to-frame (uuid frame-id))
(some? frame-id) (go-to-frame frame-id)
(some? index) (go-to-frame-by-index index)
:else (go-to-frame-auto)))))))))

View file

@ -8,12 +8,10 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.changes-builder :as pcb]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.schema :as sm]
[app.common.types.shape-tree :as ctst]
[app.main.data.changes :as dch]
[app.main.data.comments :as dcmt]
[app.main.data.common :as dcm]
[app.main.data.event :as ev]
@ -125,38 +123,32 @@
{::ev/origin "workspace"}))))))))
(defn update-comment-thread-position
([thread [new-x new-y]]
(update-comment-thread-position thread [new-x new-y] nil))
([thread [new-x new-y]]
(update-comment-thread-position thread [new-x new-y] nil))
([thread [new-x new-y] frame-id]
([thread [new-x new-y] frame-id]
(dm/assert!
"expected valid comment thread"
(dcmt/check-comment-thread! thread))
(ptk/reify ::update-comment-thread-position
ptk/WatchEvent
(watch [it state _]
(watch [_ state _]
(let [page (dsh/lookup-page state)
page-id (:id page)
objects (dsh/lookup-page-objects state page-id)
frame-id (if (nil? frame-id)
(ctst/get-frame-id-by-position objects (gpt/point new-x new-y))
(:frame-id thread))
thread (-> thread
(assoc :position (gpt/point new-x new-y))
(assoc :frame-id frame-id))
changes (-> (pcb/empty-changes it)
(pcb/with-page page)
(pcb/set-comment-thread-position thread))]
thread-id (:id thread)]
(rx/concat
(rx/merge
(rx/of (dch/commit-changes changes))
(->> (rp/cmd! :update-comment-thread-position thread)
(rx/catch #(rx/throw {:type :update-comment-thread-position}))
(rx/ignore)))
(rx/of (dcmt/refresh-comment-thread thread))))))))
(rx/of #(update % :comment-threads assoc thread-id thread))
(->> (rp/cmd! :update-comment-thread-position thread)
(rx/catch #(rx/throw {:type :update-comment-thread-position}))
(rx/ignore))))))))
;; Move comment threads that are inside a frame when that frame is moved"
(defmethod ptk/resolve ::move-frame-comment-threads

View file

@ -587,11 +587,6 @@
:subsections [:shape]
:fn #(emit-when-no-readonly (dw/create-bool :exclude))}
:fit-content-selected {:tooltip (ds/meta-shift (ds/alt "R"))
:command (ds/c-mod "shift+alt+r")
:subsections [:shape]
:fn #(emit-when-no-readonly (dwt/selected-fit-content))}
;; THEME
:toggle-theme {:tooltip (ds/alt "M")
:command (ds/a-mod "m")

View file

@ -90,22 +90,29 @@
(dom/set-data! "fullname" fullname)
(obj/set! "textContent" fullname)))
(defn- current-text-node*
"Retrieves the text node and the offset that the cursor is positioned on"
[node anchor-node]
(when (.contains node anchor-node)
(let [span-node (if (instance? js/Text anchor-node)
(dom/get-parent anchor-node)
anchor-node)
container (dom/get-parent span-node)]
(when (= node container)
span-node))))
(defn- current-text-node
"Retrieves the text node and the offset that the cursor is positioned on"
[node]
(assert (some? node) "expected valid node")
(let [selection (wapi/get-selection)
range (wapi/get-range selection 0)
anchor-node (wapi/range-start-container range)
anchor-offset (wapi/range-start-offset range)]
(when (and node (.contains node anchor-node))
(let [span-node
(if (instance? js/Text anchor-node)
(dom/get-parent anchor-node)
anchor-node)
container (dom/get-parent span-node)]
(when (= node container)
[span-node anchor-offset])))))
(when-let [selection (wapi/get-selection)]
(let [range (wapi/get-range selection 0)
anchor-node (wapi/range-start-container range)
offset (wapi/range-start-offset range)
span-node (current-text-node* node anchor-node)]
(when span-node
[span-node offset]))))
(defn- absolute-offset
[node child offset]
@ -156,7 +163,8 @@
mentions-s (mf/use-ctx mentions-context)
cur-mention (mf/use-var nil)
prev-selection (mf/use-var nil)
prev-selection-ref
(mf/use-ref)
init-input
(mf/use-fn
@ -203,58 +211,59 @@
handle-select
(mf/use-fn
(fn []
(let [node (mf/ref-val local-ref)
selection (wapi/get-selection)
range (wapi/get-range selection 0)
anchor-node (wapi/range-start-container range)]
(when (and (= node anchor-node) (.-collapsed range))
(wapi/set-cursor-after! anchor-node)))
(when-let [node (mf/ref-val local-ref)]
(when-let [selection (wapi/get-selection)]
(let [range (wapi/get-range selection 0)
anchor-node (wapi/range-start-container range)
offset (wapi/range-start-offset range)]
(let [node (mf/ref-val local-ref)
[span-node offset] (current-text-node node)
[prev-span prev-offset] @prev-selection]
(when (and (= node anchor-node) (.-collapsed ^js range))
(wapi/set-cursor-after! anchor-node))
(reset! prev-selection #js [span-node offset])
(when-let [span-node (current-text-node* node anchor-node)]
(let [[prev-span prev-offset]
(mf/ref-val prev-selection-ref)
(when (= (dom/get-data span-node "type") "mention")
(let [from-offset (absolute-offset node prev-span prev-offset)
to-offset (absolute-offset node span-node offset)
node-text
(subs (dom/get-text span-node) 0 offset)
[_ prev next]
(->> node
(dom/seq-nodes)
(d/with-prev-next)
(filter (fn [[elem _ _]] (= elem span-node)))
(first))]
current-at-symbol
(str/last-index-of (subs node-text 0 offset) "@")
(if (> from-offset to-offset)
(wapi/set-cursor-after! prev)
(wapi/set-cursor-before! next))))
mention-text
(subs node-text current-at-symbol)
(when span-node
(let [node-text (subs (dom/get-text span-node) 0 offset)
at-symbol-inside-word?
(and (> current-at-symbol 0)
(str/word? (str/slice node-text (- current-at-symbol 1) current-at-symbol)))]
current-at-symbol
(str/last-index-of (subs node-text 0 offset) "@")
(mf/set-ref-val! prev-selection-ref #js [span-node offset])
mention-text
(subs node-text current-at-symbol)
(when (= (dom/get-data span-node "type") "mention")
(let [from-offset (absolute-offset node prev-span prev-offset)
to-offset (absolute-offset node span-node offset)
at-symbol-inside-word?
(and (> current-at-symbol 0)
(str/word? (str/slice node-text (- current-at-symbol 1) current-at-symbol)))]
[_ prev next]
(->> node
(dom/seq-nodes)
(d/with-prev-next)
(filter (fn [[elem _ _]] (= elem span-node)))
(first))]
(if (> from-offset to-offset)
(wapi/set-cursor-after! prev)
(wapi/set-cursor-before! next))))
(if (and (not at-symbol-inside-word?)
(re-matches #"@\w*" mention-text))
(do
(reset! cur-mention mention-text)
(rx/push! mentions-s {:type :display-mentions})
(let [mention (subs mention-text 1)]
(when (d/not-empty? mention)
(rx/push! mentions-s {:type :filter-mentions :data mention}))))
(do
(reset! cur-mention nil)
(rx/push! mentions-s {:type :hide-mentions}))))))))
(if (and (not at-symbol-inside-word?)
(re-matches #"@\w*" mention-text))
(do
(reset! cur-mention mention-text)
(rx/push! mentions-s {:type :display-mentions})
(let [mention (subs mention-text 1)]
(when (d/not-empty? mention)
(rx/push! mentions-s {:type :filter-mentions :data mention}))))
(do
(reset! cur-mention nil)
(rx/push! mentions-s {:type :hide-mentions}))))))))))
handle-focus
(mf/use-fn
@ -279,9 +288,8 @@
(mf/use-fn
(mf/deps on-change)
(fn [data]
(let [node (mf/ref-val local-ref)
[span-node offset] (current-text-node node)]
(when span-node
(when-let [node (mf/ref-val local-ref)]
(when-let [[span-node offset] (current-text-node node)]
(let [node-text
(dom/get-text span-node)
@ -314,8 +322,8 @@
handle-insert-at-symbol
(mf/use-fn
(fn []
(let [node (mf/ref-val local-ref) [span-node] (current-text-node node)]
(when span-node
(when-let [node (mf/ref-val local-ref)]
(when-let [[span-node] (current-text-node node)]
(let [node-text (dom/get-text span-node)
at-symbol (if (blank-content? node-text) "@" " @")]
@ -327,66 +335,62 @@
(mf/deps on-esc on-ctrl-enter handle-select handle-input)
(fn [event]
(handle-select event)
(when-let [node (mf/ref-val local-ref)]
(when-let [[span-node offset] (current-text-node node)]
(cond
(and @cur-mention (kbd/enter? event))
(do (dom/prevent-default event)
(dom/stop-propagation event)
(rx/push! mentions-s {:type :insert-selected-mention}))
(let [node (mf/ref-val local-ref)
[span-node offset] (current-text-node node)]
(and @cur-mention (kbd/down-arrow? event))
(do (dom/prevent-default event)
(dom/stop-propagation event)
(rx/push! mentions-s {:type :insert-next-mention}))
(cond
(and @cur-mention (kbd/enter? event))
(do (dom/prevent-default event)
(dom/stop-propagation event)
(rx/push! mentions-s {:type :insert-selected-mention}))
(and @cur-mention (kbd/up-arrow? event))
(do (dom/prevent-default event)
(dom/stop-propagation event)
(rx/push! mentions-s {:type :insert-prev-mention}))
(and @cur-mention (kbd/down-arrow? event))
(do (dom/prevent-default event)
(dom/stop-propagation event)
(rx/push! mentions-s {:type :insert-next-mention}))
(and @cur-mention (kbd/esc? event))
(do (dom/prevent-default event)
(dom/stop-propagation event)
(rx/push! mentions-s {:type :hide-mentions}))
(and @cur-mention (kbd/up-arrow? event))
(do (dom/prevent-default event)
(dom/stop-propagation event)
(rx/push! mentions-s {:type :insert-prev-mention}))
(and (kbd/esc? event) (fn? on-esc))
(on-esc event)
(and @cur-mention (kbd/esc? event))
(do (dom/prevent-default event)
(dom/stop-propagation event)
(rx/push! mentions-s {:type :hide-mentions}))
(and (kbd/mod? event) (kbd/enter? event) (fn? on-ctrl-enter))
(on-ctrl-enter event)
(and (kbd/esc? event) (fn? on-esc))
(on-esc event)
(and (kbd/mod? event) (kbd/enter? event) (fn? on-ctrl-enter))
(on-ctrl-enter event)
(kbd/enter? event)
(let [sel (wapi/get-selection)
range (.getRangeAt sel 0)]
(dom/prevent-default event)
(dom/stop-propagation event)
(let [[span-node offset] (current-text-node node)]
(.deleteContents range)
(handle-input)
(when span-node
(let [txt (.-textContent span-node)]
(dom/set-html! span-node (dm/str (subs txt 0 offset) "\n" zero-width-space (subs txt offset)))
(wapi/set-cursor! span-node (inc offset))
(handle-input)))))
(kbd/backspace? event)
(let [prev-node (get-prev-node node span-node)]
(when (and (some? prev-node)
(= "mention" (dom/get-data prev-node "type"))
(= offset 1))
(kbd/enter? event)
(let [sel (wapi/get-selection)
range (.getRangeAt sel 0)]
(dom/prevent-default event)
(dom/stop-propagation event)
(.remove prev-node)))))))]
(let [[span-node offset] (current-text-node node)]
(.deleteContents range)
(handle-input)
(mf/use-layout-effect
(mf/deps autofocus)
(fn []
(when autofocus
(dom/focus! (mf/ref-val local-ref)))))
(when span-node
(let [txt (.-textContent span-node)]
(dom/set-html! span-node (dm/str (subs txt 0 offset) "\n" zero-width-space (subs txt offset)))
(wapi/set-cursor! span-node (inc offset))
(handle-input)))))
(kbd/backspace? event)
(let [prev-node (get-prev-node node span-node)]
(when (and (some? prev-node)
(= "mention" (dom/get-data prev-node "type"))
(= offset 1))
(dom/prevent-default event)
(dom/stop-propagation event)
(.remove prev-node))))))))]
(mf/with-layout-effect [autofocus]
(when ^boolean autofocus
(dom/focus! (mf/ref-val local-ref))))
;; Creates the handlers for selection
(mf/with-effect [handle-select]
@ -410,12 +414,12 @@
;; Auto resize input to display the comment
(mf/with-layout-effect nil
(let [^js node (mf/ref-val local-ref)]
(when-let [^js node (mf/ref-val local-ref)]
(set! (.-height (.-style node)) "0")
(set! (.-height (.-style node)) (str (+ 2 (.-scrollHeight node)) "px"))))
(mf/with-effect [value prev-value]
(let [node (mf/ref-val local-ref)]
(when-let [node (mf/ref-val local-ref)]
(cond
(and (d/not-empty? prev-value) (empty? value))
(do (dom/set-html! node "")

View file

@ -109,7 +109,11 @@
;; avoids some race conditions that causes unexpected redirects
;; on invitations workflows (and probably other cases).
(->> (rp/cmd! :get-profile)
(rx/subs! (fn [{:keys [id] :as profile}]
(rx/mapcat (fn [profile]
(->> (rp/cmd! :get-teams {})
(rx/map (fn [teams]
(assoc profile ::teams (into #{} (map :id) teams)))))))
(rx/subs! (fn [{:keys [id ::teams] :as profile}]
(cond
(= id uuid/zero)
(do
@ -117,10 +121,12 @@
(st/emit! (rt/nav :auth-login)))
empty-path?
(let [team-id (or (dtm/get-last-team-id)
(:default-team-id profile))]
(st/emit! (rt/nav :dashboard-recent
(assoc query-params :team-id team-id))))
(let [team-id (dtm/get-last-team-id)]
(if (contains? teams team-id)
(st/emit! (rt/nav :dashboard-recent
(assoc query-params :team-id team-id)))
(st/emit! (rt/nav :dashboard-recent
(assoc query-params :team-id (:default-team-id profile))))))
:else
(st/emit! (rt/assign-exception {:type :not-found})))))))))

View file

@ -116,7 +116,7 @@
assets-tab
(mf/html [:& assets-toolbox {:size (- size 58)}])
(mf/html [:& assets-toolbox {:size (- size 58) :file-id file}])
tokens-tab
(when design-tokens?

View file

@ -8,6 +8,7 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.data.macros :as dm]
[app.common.types.components-list :as ctkl]
[app.main.data.modal :as modal]
[app.main.data.workspace :as dw]
[app.main.data.workspace.assets :as dwa]
@ -73,7 +74,7 @@
(mf/defc assets-toolbox
{::mf/wrap [mf/memo]
::mf/wrap-props false}
[{:keys [size]}]
[{:keys [size file-id]}]
(let [components-v2 (mf/use-ctx ctx/components-v2)
read-only? (mf/use-ctx ctx/workspace-read-only?)
filters* (mf/use-state
@ -89,7 +90,10 @@
section (:section filters)
ordering (:ordering filters)
reverse-sort? (= :desc ordering)
num-libs (count (mf/deref refs/libraries))
libs (mf/deref refs/libraries)
num-libs (count libs)
file (get libs (:id file-id))
components (mf/with-memo [file] (ctkl/components (:data file)))
toggle-ordering
(mf/use-fn
@ -159,7 +163,7 @@
[:article {:class (stl/css :assets-bar)}
[:div {:class (stl/css :assets-header)}
(when-not ^boolean read-only?
(if (= num-libs 1)
(if (and (= num-libs 1) (empty? components))
[:button {:class (stl/css :add-library-button)
:on-click show-libraries-dialog
:data-testid "libraries"}
@ -168,9 +172,7 @@
[:button {:class (stl/css :libraries-button)
:on-click show-libraries-dialog
:data-testid "libraries"}
[:span {:class (stl/css :libraries-icon)}
i/library]
(tr "workspace.assets.libraries")]))
(tr "workspace.assets.manage-library")]))
[:div {:class (stl/css :search-wrapper)}

View file

@ -26,42 +26,22 @@
margin-bottom: $s-4;
border-radius: $s-8;
.libraries-icon {
@include flexCenter;
width: $s-24;
height: 100%;
svg {
@include flexCenter;
@extend .button-icon;
stroke: var(--icon-foreground);
}
}
&:hover {
background-color: var(--button-secondary-background-color-hover);
color: var(--button-secondary-foreground-color-hover);
border: $s-1 solid var(--button-secondary-border-color-hover);
svg {
stroke: var(--button-secondary-foreground-color-hover);
}
}
&:focus {
background-color: var(--button-secondary-background-color-focus);
color: var(--button-secondary-foreground-color-focus);
border: $s-1 solid var(--button-secondary-border-color-focus);
svg {
stroke: var(--button-secondary-foreground-color-focus);
}
}
}
.add-library-button {
@extend .button-primary;
text-transform: uppercase;
@include uppercaseTitleTipography;
gap: $s-2;
height: $s-32;
width: 100%;

View file

@ -301,7 +301,6 @@
(when show-comments?
[:> comments/comments-layer* {:vbox vbox
:page-id page-id
:file-id file-id
:vport vport
:zoom zoom

View file

@ -9,30 +9,14 @@
(:require
[app.common.data.macros :as dm]
[app.main.data.comments :as dcm]
[app.main.data.helpers :as dsh]
[app.main.data.workspace.comments :as dwcm]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.comments :as cmt]
[okulary.core :as l]
[rumext.v2 :as mf]))
(defn- update-position
[positions {:keys [id] :as thread}]
(if (contains? positions id)
(-> thread
(assoc :position (dm/get-in positions [id :position]))
(assoc :frame-id (dm/get-in positions [id :frame-id])))
thread))
(def ^:private ref:thread-positions
(l/derived (fn [state]
(-> (dsh/lookup-page state)
(get :comment-thread-positions)))
st/state))
(mf/defc comments-layer*
[{:keys [vbox vport zoom drawing file-id page-id]}]
[{:keys [vbox vport zoom drawing file-id]}]
(let [vbox-x (dm/get-prop vbox :x)
vbox-y (dm/get-prop vbox :y)
vport-w (dm/get-prop vport :width)
@ -44,16 +28,7 @@
profile (mf/deref refs/profile)
local (mf/deref refs/comments-local)
positions (mf/deref ref:thread-positions)
threads-map (mf/deref refs/threads)
threads-map (mf/with-memo [threads-map page-id positions]
(reduce-kv (fn [threads id thread]
(if (= (:page-id thread) page-id)
(assoc threads id (update-position positions thread))
threads))
{}
threads-map))
threads
(mf/with-memo [threads-map local profile]
@ -93,7 +68,7 @@
(when-let [thread (get threads-map id)]
(when (seq (dcm/apply-filters local profile [thread]))
[:> cmt/comment-floating-thread*
{:thread (update-position positions thread)
{:thread thread
:viewport viewport
:zoom zoom}])))

View file

@ -343,7 +343,6 @@
(when show-comments?
[:> comments/comments-layer* {:vbox vbox
:page-id page-id
:vport vport
:zoom zoom
:drawing drawing}])

View file

@ -282,9 +282,12 @@
(.selectAllChildren selection node))
(defn get-selection
"Only returns valid selection"
[]
(when-let [document globals/document]
(.getSelection document)))
(let [selection (.getSelection document)]
(when (not= (.-type selection) "None")
selection))))
(defn get-anchor-node
[^js selection]

View file

@ -3618,10 +3618,6 @@ msgstr "Export shapes"
msgid "shortcuts.fit-all"
msgstr "Zoom to fit all"
#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:590
msgid "shortcuts.fit-content-selected"
msgstr "Resize board to fit content"
#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:113
msgid "shortcuts.flip-horizontal"
msgstr "Flip horizontally"
@ -4226,6 +4222,9 @@ msgstr "Align top (%s)"
msgid "workspace.assets.add-library"
msgstr "Add library"
msgid "workspace.assets.manage-library"
msgstr "Manage library"
#: src/app/main/ui/workspace/sidebar/assets.cljs
#, unused
msgid "workspace.assets.assets"
@ -4295,10 +4294,6 @@ msgstr "Group"
msgid "workspace.assets.group-name"
msgstr "Group name"
#: src/app/main/ui/workspace/sidebar/assets.cljs:186
msgid "workspace.assets.libraries"
msgstr "Libraries"
#: src/app/main/ui/workspace/sidebar/assets/components.cljs:501
msgid "workspace.assets.list-view"
msgstr "List view"

View file

@ -3514,10 +3514,6 @@ msgstr "Diferencia"
msgid "shortcuts.bool-exclude"
msgstr "Exclusión"
#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:590
msgid "shortcuts.fit-content-selected"
msgstr "Redimensionar para ajustar al contenido"
#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:86
msgid "shortcuts.bool-intersection"
msgstr "Interescción"
@ -4234,6 +4230,9 @@ msgstr "Alinear arriba (%s)"
msgid "workspace.assets.add-library"
msgstr "Añadir biblioteca"
msgid "workspace.assets.manage-library"
msgstr "Gestionar biblioteca"
#: src/app/main/ui/workspace/sidebar/assets.cljs
#, unused
msgid "workspace.assets.assets"
@ -4305,10 +4304,6 @@ msgstr "Agrupar"
msgid "workspace.assets.group-name"
msgstr "Nombre del grupo"
#: src/app/main/ui/workspace/sidebar/assets.cljs:186
msgid "workspace.assets.libraries"
msgstr "Bibliotecas"
#: src/app/main/ui/workspace/sidebar/assets/components.cljs:501
msgid "workspace.assets.list-view"
msgstr "Ver como lista"