mirror of
https://github.com/penpot/penpot.git
synced 2025-07-31 21:18:28 +02:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
31661d5484
57 changed files with 1838 additions and 495 deletions
|
@ -9,7 +9,7 @@
|
|||
|
||||
funcool/beicon {:mvn/version "2021.07.05-1"}
|
||||
funcool/okulary {:mvn/version "2022.04.11-16"}
|
||||
funcool/potok {:mvn/version "2022.04.28-67"}
|
||||
funcool/potok {:mvn/version "2022.12.16-71"}
|
||||
funcool/tubax {:mvn/version "2021.05.20-0"}
|
||||
|
||||
funcool/rumext
|
||||
|
|
|
@ -184,6 +184,7 @@ ul.palette-menu .color-bullet {
|
|||
|
||||
.color-bullet.is-not-library-color {
|
||||
border-radius: $br-small;
|
||||
overflow: hidden;
|
||||
|
||||
& .color-bullet-wrapper {
|
||||
clip-path: none;
|
||||
|
|
|
@ -57,9 +57,10 @@
|
|||
color: $color-gray-30;
|
||||
height: 40px;
|
||||
padding: $size-1 $size-5;
|
||||
|
||||
font-weight: 400;
|
||||
&:hover {
|
||||
color: $color-black;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -203,7 +203,11 @@
|
|||
flex-shrink: 0;
|
||||
padding: $size-2;
|
||||
a {
|
||||
font-weight: 400;
|
||||
width: 100%;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
|
@ -279,7 +283,9 @@
|
|||
}
|
||||
|
||||
&.current {
|
||||
font-weight: bold;
|
||||
a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&::before {
|
||||
background-color: $color-primary;
|
||||
|
|
|
@ -96,7 +96,11 @@
|
|||
(->> (rp/cmd! :create-comment-thread params)
|
||||
(rx/mapcat #(rp/cmd! :get-comment-thread {:file-id (:file-id %) :id (:id %)}))
|
||||
(rx/map created-thread-on-workspace)
|
||||
(rx/catch #(rx/throw {:type :comment-error})))))))
|
||||
(rx/catch (fn [{:keys [type code] :as cause}]
|
||||
(if (and (= type :restriction)
|
||||
(= code :max-quote-reached))
|
||||
(rx/throw cause)
|
||||
(rx/throw {:type :comment-error})))))))))
|
||||
|
||||
(defn created-thread-on-viewer
|
||||
[{:keys [id comment page-id] :as thread}]
|
||||
|
@ -114,8 +118,7 @@
|
|||
|
||||
(defn create-thread-on-viewer
|
||||
[params]
|
||||
(us/assert ::create-thread-on-viewer-params params)
|
||||
|
||||
(us/assert! ::create-thread-on-viewer-params params)
|
||||
(ptk/reify ::create-thread-on-viewer
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
|
@ -125,7 +128,11 @@
|
|||
(->> (rp/cmd! :create-comment-thread params)
|
||||
(rx/mapcat #(rp/cmd! :get-comment-thread {:file-id (:file-id %) :id (:id %) :share-id share-id}))
|
||||
(rx/map created-thread-on-viewer)
|
||||
(rx/catch #(rx/throw {:type :comment-error})))))))
|
||||
(rx/catch (fn [{:keys [type code] :as cause}]
|
||||
(if (and (= type :restriction)
|
||||
(= code :max-quote-reached))
|
||||
(rx/throw cause)
|
||||
(rx/throw {:type :comment-error})))))))))
|
||||
|
||||
(defn update-comment-thread-status
|
||||
[{:keys [id] :as thread}]
|
||||
|
@ -154,7 +161,11 @@
|
|||
(watch [_ state _]
|
||||
(let [share-id (-> state :viewer-local :share-id)]
|
||||
(->> (rp/cmd! :update-comment-thread {:id id :is-resolved is-resolved :share-id share-id})
|
||||
(rx/catch #(rx/throw {:type :comment-error}))
|
||||
(rx/catch (fn [{:keys [type code] :as cause}]
|
||||
(if (and (= type :restriction)
|
||||
(= code :max-quote-reached))
|
||||
(rx/throw cause)
|
||||
(rx/throw {:type :comment-error}))))
|
||||
(rx/ignore))))))
|
||||
|
||||
(defn add-comment
|
||||
|
@ -170,7 +181,11 @@
|
|||
(rx/concat
|
||||
(->> (rp/cmd! :create-comment {:thread-id (:id thread) :content content :share-id share-id})
|
||||
(rx/map #(partial created %))
|
||||
(rx/catch #(rx/throw {:type :comment-error})))
|
||||
(rx/catch (fn [{:keys [type code] :as cause}]
|
||||
(if (and (= type :restriction)
|
||||
(= code :max-quote-reached))
|
||||
(rx/throw cause)
|
||||
(rx/throw {:type :comment-error})))))
|
||||
(rx/of (refresh-comment-thread thread))))))))
|
||||
|
||||
(defn update-comment
|
||||
|
|
|
@ -696,7 +696,7 @@
|
|||
(pcb/resize-parents parents))))
|
||||
|
||||
(defn relocate-shapes
|
||||
[ids parent-id to-index]
|
||||
[ids parent-id to-index & [ignore-parents?]]
|
||||
(us/verify (s/coll-of ::us/uuid) ids)
|
||||
(us/verify ::us/uuid parent-id)
|
||||
(us/verify number? to-index)
|
||||
|
@ -712,7 +712,9 @@
|
|||
|
||||
;; If we try to move a parent into a child we remove it
|
||||
ids (filter #(not (cph/is-parent? objects parent-id %)) ids)
|
||||
parents (into #{parent-id} (map #(cph/get-parent-id objects %)) ids)
|
||||
parents (if ignore-parents?
|
||||
#{parent-id}
|
||||
(into #{parent-id} (map #(cph/get-parent-id objects %)) ids))
|
||||
|
||||
groups-to-delete
|
||||
(loop [current-id (first parents)
|
||||
|
@ -1832,6 +1834,21 @@
|
|||
(rx/empty)))))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Orphan Shapes
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
|
||||
(defn fix-orphan-shapes
|
||||
[]
|
||||
(ptk/reify ::fix-orphan-shapes
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [orphans (set (into [] (keys (wsh/find-orphan-shapes state))))]
|
||||
(rx/of (relocate-shapes orphans uuid/zero 0 true))))))
|
||||
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Inspect
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
(rx/filter ms/mouse-click?)
|
||||
(rx/switch-map #(rx/take 1 ms/mouse-position))
|
||||
(rx/with-latest-from ms/keyboard-space)
|
||||
(rx/tap prn)
|
||||
(rx/filter (fn [[_ space]] (not space)) )
|
||||
(rx/map first)
|
||||
(rx/map handle-comment-layer-click)
|
||||
|
|
|
@ -190,26 +190,27 @@
|
|||
[(get-in objects [k :name]) v]))
|
||||
modif-tree)))
|
||||
|
||||
(defn apply-text-modifier
|
||||
[shape {:keys [width height]}]
|
||||
(cond-> shape
|
||||
(some? width)
|
||||
(assoc :width width)
|
||||
|
||||
(some? height)
|
||||
(assoc :height height)
|
||||
|
||||
(or (some? width) (some? height))
|
||||
(cts/setup-rect-selrect)))
|
||||
|
||||
(defn apply-text-modifiers
|
||||
[objects text-modifiers]
|
||||
(letfn [(apply-text-modifier
|
||||
[shape {:keys [width height]}]
|
||||
(cond-> shape
|
||||
(some? width)
|
||||
(assoc :width width)
|
||||
|
||||
(some? height)
|
||||
(assoc :height height)
|
||||
|
||||
(or (some? width) (some? height))
|
||||
(cts/setup-rect-selrect)))]
|
||||
(loop [modifiers (seq text-modifiers)
|
||||
result objects]
|
||||
(if (empty? modifiers)
|
||||
result
|
||||
(let [[id text-modifier] (first modifiers)]
|
||||
(recur (rest modifiers)
|
||||
(update objects id apply-text-modifier text-modifier)))))))
|
||||
(loop [modifiers (seq text-modifiers)
|
||||
result objects]
|
||||
(if (empty? modifiers)
|
||||
result
|
||||
(let [[id text-modifier] (first modifiers)]
|
||||
(recur (rest modifiers)
|
||||
(update objects id apply-text-modifier text-modifier))))))
|
||||
|
||||
#_(defn apply-path-modifiers
|
||||
[objects path-modifiers]
|
||||
|
@ -242,6 +243,33 @@
|
|||
;;(apply-path-modifiers $ (get-in state [:workspace-local :edit-path]))
|
||||
(gsh/set-objects-modifiers modif-tree $ ignore-constraints snap-pixel?)))))
|
||||
|
||||
(defn- calculate-update-modifiers
|
||||
[old-modif-tree state ignore-constraints ignore-snap-pixel modif-tree]
|
||||
(let [objects
|
||||
(wsh/lookup-page-objects state)
|
||||
|
||||
snap-pixel?
|
||||
(and (not ignore-snap-pixel) (contains? (:workspace-layout state) :snap-pixel-grid))
|
||||
|
||||
objects
|
||||
(-> objects
|
||||
(apply-text-modifiers (get state :workspace-text-modifier)))]
|
||||
|
||||
(gsh/set-objects-modifiers old-modif-tree modif-tree objects ignore-constraints snap-pixel?)))
|
||||
|
||||
(defn update-modifiers
|
||||
([modif-tree]
|
||||
(update-modifiers modif-tree false))
|
||||
|
||||
([modif-tree ignore-constraints]
|
||||
(update-modifiers modif-tree ignore-constraints false))
|
||||
|
||||
([modif-tree ignore-constraints ignore-snap-pixel]
|
||||
(ptk/reify ::update-modifiers
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :workspace-modifiers calculate-update-modifiers state ignore-constraints ignore-snap-pixel modif-tree)))))
|
||||
|
||||
(defn set-modifiers
|
||||
([modif-tree]
|
||||
(set-modifiers modif-tree false))
|
||||
|
|
|
@ -189,7 +189,6 @@
|
|||
(s/def ::file-change-event
|
||||
(s/keys :req-un [::type ::profile-id ::file-id ::session-id ::revn ::changes]))
|
||||
|
||||
|
||||
(defn handle-file-change
|
||||
[{:keys [file-id changes] :as msg}]
|
||||
(us/assert ::file-change-event msg)
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
(declare persist-changes)
|
||||
(declare persist-synchronous-changes)
|
||||
(declare shapes-changes-persisted)
|
||||
(declare shapes-changes-persisted-finished)
|
||||
(declare update-persistence-status)
|
||||
|
||||
;; --- Persistence
|
||||
|
@ -42,6 +43,7 @@
|
|||
(log/debug :hint "initialize persistence")
|
||||
(let [stoper (rx/filter (ptk/type? ::initialize-persistence) stream)
|
||||
commits (l/atom [])
|
||||
saving? (l/atom false)
|
||||
|
||||
local-file?
|
||||
#(as-> (:file-id %) event-file-id
|
||||
|
@ -61,13 +63,15 @@
|
|||
|
||||
on-saving
|
||||
(fn []
|
||||
(reset! saving? true)
|
||||
(st/emit! (update-persistence-status {:status :saving})))
|
||||
|
||||
on-saved
|
||||
(fn []
|
||||
;; Disable reload stoper
|
||||
(swap! st/ongoing-tasks disj :workspace-change)
|
||||
(st/emit! (update-persistence-status {:status :saved})))]
|
||||
(st/emit! (update-persistence-status {:status :saved}))
|
||||
(reset! saving? false))]
|
||||
|
||||
(rx/merge
|
||||
(->> stream
|
||||
|
@ -88,12 +92,15 @@
|
|||
|
||||
(->> (rx/from-atom commits)
|
||||
(rx/filter (complement empty?))
|
||||
(rx/sample-when (rx/merge
|
||||
(rx/interval 5000)
|
||||
(rx/filter #(= ::force-persist %) stream)
|
||||
(->> (rx/from-atom commits)
|
||||
(rx/filter (complement empty?))
|
||||
(rx/debounce 2000))))
|
||||
(rx/sample-when
|
||||
(->> (rx/merge
|
||||
(rx/interval 5000)
|
||||
(rx/filter #(= ::force-persist %) stream)
|
||||
(->> (rx/from-atom commits)
|
||||
(rx/filter (complement empty?))
|
||||
(rx/debounce 2000)))
|
||||
;; Not sample while saving so there are no race conditions
|
||||
(rx/filter #(not @saving?))))
|
||||
(rx/tap #(reset! commits []))
|
||||
(rx/tap on-saving)
|
||||
(rx/mapcat (fn [changes]
|
||||
|
@ -101,9 +108,11 @@
|
|||
;; next persistence before this one is
|
||||
;; finished.
|
||||
(rx/merge
|
||||
(rx/of (persist-changes file-id changes))
|
||||
(->> (rx/of (persist-changes file-id changes commits))
|
||||
(rx/observe-on :async))
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::changes-persisted))
|
||||
;; We wait for every change to be persisted
|
||||
(rx/filter (ptk/type? ::shapes-changes-persisted-finished))
|
||||
(rx/take 1)
|
||||
(rx/tap on-saved)
|
||||
(rx/ignore)))))
|
||||
|
@ -123,7 +132,7 @@
|
|||
(log/debug :hint "finalize persistence: synchronous save loop")))))))))
|
||||
|
||||
(defn persist-changes
|
||||
[file-id changes]
|
||||
[file-id changes pending-commits]
|
||||
(log/debug :hint "persist changes" :changes (count changes))
|
||||
(us/verify ::us/uuid file-id)
|
||||
(ptk/reify ::persist-changes
|
||||
|
@ -150,20 +159,29 @@
|
|||
(log/debug :hint "changes persisted" :lagged (count lagged))
|
||||
(let [frame-updates
|
||||
(-> (group-by :page-id changes)
|
||||
(update-vals #(into #{} (mapcat :frames) %)))]
|
||||
(update-vals #(into #{} (mapcat :frames) %)))
|
||||
|
||||
(rx/merge
|
||||
(->> (rx/from frame-updates)
|
||||
(rx/mapcat (fn [[page-id frames]]
|
||||
(->> frames (map #(vector page-id %)))))
|
||||
(rx/map (fn [[page-id frame-id]] (dwt/update-thumbnail (:id file) page-id frame-id))))
|
||||
(->> (rx/from lagged)
|
||||
(rx/merge-map (fn [{:keys [changes] :as entry}]
|
||||
(rx/merge
|
||||
(rx/from
|
||||
(for [[page-id changes] (group-by :page-id changes)]
|
||||
(dch/update-indices page-id changes)))
|
||||
(rx/of (shapes-changes-persisted file-id entry))))))))))
|
||||
commits
|
||||
(->> @pending-commits
|
||||
(map #(assoc % :revn (:revn file))))]
|
||||
|
||||
(rx/concat
|
||||
(rx/merge
|
||||
(->> (rx/from frame-updates)
|
||||
(rx/mapcat (fn [[page-id frames]]
|
||||
(->> frames (map #(vector page-id %)))))
|
||||
(rx/map (fn [[page-id frame-id]] (dwt/update-thumbnail (:id file) page-id frame-id))))
|
||||
|
||||
(->> (rx/from (concat lagged commits))
|
||||
(rx/merge-map
|
||||
(fn [{:keys [changes] :as entry}]
|
||||
(rx/merge
|
||||
(rx/from
|
||||
(for [[page-id changes] (group-by :page-id changes)]
|
||||
(dch/update-indices page-id changes)))
|
||||
(rx/of (shapes-changes-persisted file-id entry)))))))
|
||||
|
||||
(rx/of (shapes-changes-persisted-finished))))))
|
||||
(rx/catch (fn [cause]
|
||||
(rx/concat
|
||||
(if (= :authentication (:type cause))
|
||||
|
@ -171,6 +189,11 @@
|
|||
(rx/of (rt/assign-exception cause)))
|
||||
(rx/throw cause))))))))))
|
||||
|
||||
;; Event to be thrown after the changes have been persisted
|
||||
(defn shapes-changes-persisted-finished
|
||||
[]
|
||||
(ptk/reify ::shapes-changes-persisted-finished))
|
||||
|
||||
(defn persist-synchronous-changes
|
||||
[{:keys [file-id changes]}]
|
||||
(us/verify ::us/uuid file-id)
|
||||
|
@ -216,29 +239,32 @@
|
|||
[file-id {:keys [revn changes] :as params}]
|
||||
(us/verify! ::us/uuid file-id)
|
||||
(us/verify! ::shapes-changes-persisted params)
|
||||
(ptk/reify ::changes-persisted
|
||||
(ptk/reify ::shapes-changes-persisted
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
;; NOTE: we don't set the file features context here because
|
||||
;; there are no useful context for code that need to be executed
|
||||
;; on the frontend side
|
||||
(let [changes (group-by :page-id changes)]
|
||||
(if (= file-id (:current-file-id state))
|
||||
(-> state
|
||||
(update-in [:workspace-file :revn] max revn)
|
||||
(update :workspace-data (fn [file]
|
||||
(loop [fdata file
|
||||
entries (seq changes)]
|
||||
(if-let [[page-id changes] (first entries)]
|
||||
(recur (-> fdata
|
||||
(cp/process-changes changes)
|
||||
(ctst/update-object-indices page-id))
|
||||
(rest entries))
|
||||
fdata)))))
|
||||
|
||||
(if-let [current-file-id (:current-file-id state)]
|
||||
(if (= file-id current-file-id)
|
||||
(let [changes (group-by :page-id changes)]
|
||||
(-> state
|
||||
(update-in [:workspace-file :revn] max revn)
|
||||
(update :workspace-data (fn [file]
|
||||
(loop [fdata file
|
||||
entries (seq changes)]
|
||||
(if-let [[page-id changes] (first entries)]
|
||||
(recur (-> fdata
|
||||
(cp/process-changes changes)
|
||||
(ctst/update-object-indices page-id))
|
||||
(rest entries))
|
||||
fdata))))))
|
||||
(-> state
|
||||
(update-in [:workspace-libraries file-id :revn] max revn)
|
||||
(update-in [:workspace-libraries file-id :data]
|
||||
cp/process-changes changes)))))))
|
||||
(update-in [:workspace-libraries file-id :data] cp/process-changes changes)))
|
||||
|
||||
state))))
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
[app.main.data.workspace.collapse :as dwc]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.thumbnails :as dwt]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.data.workspace.zoom :as dwz]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.streams :as ms]
|
||||
|
@ -502,8 +503,11 @@
|
|||
[obj state objects]
|
||||
(let [{:keys [id-original id-duplicated]}
|
||||
(get-in state [:workspace-local :duplicated])]
|
||||
(if (and (not= id-original (:id obj))
|
||||
(not= id-duplicated (:id obj)))
|
||||
(if (or (and (not= id-original (:id obj))
|
||||
(not= id-duplicated (:id obj)))
|
||||
;; As we can remove duplicated elements may be we can still caching a deleted id
|
||||
(not (contains? objects id-original))
|
||||
(not (contains? objects id-duplicated)))
|
||||
|
||||
;; The default is leave normal shapes in place, but put
|
||||
;; new frames to the right of the original.
|
||||
|
@ -556,16 +560,21 @@
|
|||
|
||||
frames (into #{}
|
||||
(map #(get-in objects [% :frame-id]))
|
||||
selected)]
|
||||
selected)
|
||||
undo-id (uuid/next)]
|
||||
|
||||
(rx/concat
|
||||
(->> (rx/from dup-frames)
|
||||
(rx/map (fn [[old-id new-id]] (dwt/duplicate-thumbnail old-id new-id))))
|
||||
|
||||
;; Warning: This order is important for the focus mode.
|
||||
(rx/of (dch/commit-changes changes)
|
||||
(select-shapes new-selected)
|
||||
(ptk/data-event :layout/update frames)
|
||||
(memorize-duplicated id-original id-duplicated))))))))))
|
||||
(rx/of
|
||||
(dwu/start-undo-transaction undo-id)
|
||||
(dch/commit-changes changes)
|
||||
(select-shapes new-selected)
|
||||
(ptk/data-event :layout/update frames)
|
||||
(memorize-duplicated id-original id-duplicated)
|
||||
(dwu/commit-undo-transaction undo-id))))))))))
|
||||
|
||||
(defn change-hover-state
|
||||
[id value]
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.path.commands :as upc]))
|
||||
[app.common.path.commands :as upc]
|
||||
[app.common.uuid :as uuid]))
|
||||
|
||||
(defn lookup-page
|
||||
([state]
|
||||
|
@ -146,4 +147,14 @@
|
|||
(let [{:keys [x y width height]} (get-in state [:workspace-local :vbox])]
|
||||
(gpt/point (+ x (/ width 2)) (+ y (/ height 2)))))
|
||||
|
||||
|
||||
(defn find-orphan-shapes
|
||||
([state]
|
||||
(find-orphan-shapes state (:current-page-id state)))
|
||||
([state page-id]
|
||||
(let [objects (lookup-page-objects state page-id)
|
||||
objects (filter (fn [item]
|
||||
(and
|
||||
(not= (key item) uuid/zero)
|
||||
(not (contains? objects (:parent-id (val item))))))
|
||||
objects)]
|
||||
objects)))
|
||||
|
|
|
@ -408,8 +408,7 @@
|
|||
(not (mth/close? (:height props) current-height))))
|
||||
|
||||
(let [modif-tree (dwm/create-modif-tree [id] (ctm/reflow-modifiers))]
|
||||
(->> (rx/of (dwm/set-modifiers modif-tree))
|
||||
(rx/observe-on :async)))
|
||||
(rx/of (dwm/update-modifiers modif-tree)))
|
||||
(rx/empty)))))))
|
||||
|
||||
(defn clean-text-modifier
|
||||
|
|
|
@ -30,7 +30,14 @@
|
|||
[error]
|
||||
(cond
|
||||
(instance? ExceptionInfo error)
|
||||
(-> error ex-data ptk/handle-error)
|
||||
(let [data (ex-data error)]
|
||||
(if (contains? data :type)
|
||||
(ptk/handle-error data)
|
||||
(let [hint (str/ffmt "Unexpected error: '%'" (ex-message error))]
|
||||
(ts/schedule #(st/emit! (rt/assign-exception error)))
|
||||
(js/console.group hint)
|
||||
(js/console.log (.-stack error))
|
||||
(js/console.groupEnd hint))))
|
||||
|
||||
(map? error)
|
||||
(ptk/handle-error error)
|
||||
|
@ -49,7 +56,7 @@
|
|||
|
||||
(defmethod ptk/handle-error :default
|
||||
[error]
|
||||
(let [hint (str/concat "Unexpected error: " (:hint error))]
|
||||
(let [hint (str/ffmt "Unhandled error: '%'" (:hint error "[no hint]"))]
|
||||
(ts/schedule #(st/emit! (rt/assign-exception error)))
|
||||
(js/console.group hint)
|
||||
(ex/ignoring (js/console.error (pr-str error)))
|
||||
|
@ -173,20 +180,18 @@
|
|||
(cond
|
||||
(= :feature-mismatch code)
|
||||
(let [message (tr "errors.feature-mismatch" (:feature error))]
|
||||
(st/emit! (modal/show
|
||||
{:type :alert
|
||||
:message message
|
||||
:on-accept #(prn "kaka")})))
|
||||
(st/emit! (modal/show {:type :alert :message message})))
|
||||
|
||||
(= :features-not-supported code)
|
||||
(let [message (tr "errors.feature-not-supported" (:feature error))]
|
||||
(st/emit! (modal/show
|
||||
{:type :alert
|
||||
:message message
|
||||
:on-accept #(prn "kaka")})))
|
||||
(st/emit! (modal/show {:type :alert :message message})))
|
||||
|
||||
(= :max-quote-reached code)
|
||||
(let [message (tr "errors.max-quote-reached" (:target error))]
|
||||
(st/emit! (modal/show {:type :alert :message message})))
|
||||
|
||||
:else
|
||||
(ptk/handle-error (assoc error :type :server-error))))
|
||||
(ptk/handle-error {:type :server-error :data error})))
|
||||
|
||||
;; This happens when the backed server fails to process the
|
||||
;; request. This can be caused by an internal assertion or any other
|
||||
|
|
|
@ -23,8 +23,22 @@
|
|||
[type data]
|
||||
(ptk/data-event type data))
|
||||
|
||||
;;(def debug-exclude-events
|
||||
;; #{:app.main.data.workspace.notifications/handle-pointer-update
|
||||
;; :app.main.data.workspace.notifications/handle-pointer-send
|
||||
;; :app.main.data.workspace.persistence/update-persistence-status
|
||||
;; :app.main.data.workspace.changes/update-indices
|
||||
;; :app.main.data.websocket/send-message
|
||||
;; :app.main.data.workspace.selection/change-hover-state})
|
||||
;; (def ^:dynamic *debug-events* false)
|
||||
|
||||
(defonce state
|
||||
(ptk/store {:resolve ptk/resolve
|
||||
;;:on-event (fn [e]
|
||||
;; (when (and *debug-events*
|
||||
;; (ptk/event? e)
|
||||
;; (not (debug-exclude-events (ptk/type e))))
|
||||
;; (.log js/console (str "[stream]: " (ptk/repr-event e)) )))
|
||||
:on-error (fn [e] (@on-error e))}))
|
||||
|
||||
(defonce stream
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
(ns app.main.ui.comments
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.config :as cfg]
|
||||
[app.main.data.comments :as dcm]
|
||||
|
@ -333,7 +334,7 @@
|
|||
:thread thread
|
||||
:origin origin}]
|
||||
(for [item (rest comments)]
|
||||
[:*
|
||||
[:* {:key (dm/str (:id item))}
|
||||
[:hr]
|
||||
[:& comment-item {:comment item
|
||||
:users users
|
||||
|
|
|
@ -31,12 +31,12 @@
|
|||
|
||||
;; No multiple selection
|
||||
(let [color (if (string? color) {:color color :opacity 1} color)]
|
||||
[:div.color-bullet.tooltip.tooltip-right
|
||||
[:div.color-bullet
|
||||
{:class (dom/classnames :is-library-color (some? (:id color))
|
||||
:is-not-library-color (nil? (:id color))
|
||||
:is-gradient (some? (:gradient color)))
|
||||
:on-click on-click
|
||||
:title (or (:name color) (:color color) (gradient-type->string (:type (:gradient color))))}
|
||||
:title (or (:color-library-name color) (:name color) (:color color) (gradient-type->string (:type (:gradient color))))}
|
||||
(if (:gradient color)
|
||||
[:div.color-bullet-wrapper {:style {:background (uc/color->background color)}}]
|
||||
[:div.color-bullet-wrapper
|
||||
|
|
|
@ -68,7 +68,7 @@
|
|||
[:a {:on-click go-settings} (tr "labels.settings")]]]]
|
||||
[:div.dashboard-buttons
|
||||
(if (and (or invitations-section? members-section?) (:is-admin permissions))
|
||||
[:a.btn-primary.btn-small {:on-click invite-member :data-test "invite-member"}
|
||||
[:a.btn-secondary.btn-small {:on-click invite-member :data-test "invite-member"}
|
||||
(tr "dashboard.invite-profile")]
|
||||
[:div.blank-space])]]))
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.config :as cf]
|
||||
[app.main.ui.context :as muc]
|
||||
|
@ -32,23 +30,6 @@
|
|||
(d/update-when :position-data #(mapv update-color %))
|
||||
(assoc :stroke-color "#FFFFFF" :stroke-opacity 1))))
|
||||
|
||||
(defn position-data-transform
|
||||
[shape {:keys [x y width height]}]
|
||||
(let [rect (gsh/make-rect x (- y height) width height)
|
||||
center (gsh/center-rect rect)]
|
||||
(when (or (:flip-x shape) (:flip-y shape))
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate center)
|
||||
|
||||
(cond-> (:flip-x shape)
|
||||
(gmt/scale (gpt/point -1 1))
|
||||
|
||||
(:flip-y shape)
|
||||
(gmt/scale (gpt/point 1 -1)))
|
||||
|
||||
(gmt/translate (gpt/negate center))
|
||||
(dm/str)))))
|
||||
|
||||
(mf/defc text-shape
|
||||
{::mf/wrap-props false
|
||||
::mf/wrap [mf/memo]}
|
||||
|
@ -60,7 +41,7 @@
|
|||
|
||||
{:keys [x y width height position-data]} shape
|
||||
|
||||
transform (gsh/transform-str shape {:no-flip true})
|
||||
transform (gsh/transform-str shape)
|
||||
|
||||
;; These position attributes are not really necessary but they are convenient for for the export
|
||||
group-props (-> #js {:transform transform
|
||||
|
@ -96,7 +77,6 @@
|
|||
:y (- (:y data) (:height data))
|
||||
:textLength (:width data)
|
||||
:lengthAdjust "spacingAndGlyphs"
|
||||
:transform (position-data-transform shape data)
|
||||
:alignmentBaseline alignment-bl
|
||||
:dominantBaseline dominant-bl
|
||||
:style (-> #js {:fontFamily (:font-family data)
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
(ns app.main.ui.viewer.inspect.attributes.common
|
||||
(:require
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.color-bullet :refer [color-bullet color-name]]
|
||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||
|
@ -20,21 +21,31 @@
|
|||
(def file-colors-ref
|
||||
(l/derived (l/in [:viewer :file :data :colors]) st/state))
|
||||
|
||||
(defn make-colors-library-ref [file-id]
|
||||
(defn make-colors-library-ref [libraries-place file-id]
|
||||
(let [get-library
|
||||
(fn [state]
|
||||
(get-in state [:viewer-libraries file-id :data :colors]))]
|
||||
#(l/derived get-library st/state)))
|
||||
(get-in state [libraries-place file-id :data :colors]))]
|
||||
(l/derived get-library st/state)))
|
||||
|
||||
(defn- get-colors-library [color]
|
||||
(let [colors-library-v (-> (mf/use-memo
|
||||
(mf/deps (:file-id color))
|
||||
#(make-colors-library-ref :viewer-libraries (:file-id color)))
|
||||
mf/deref)
|
||||
colors-library-ws (-> (mf/use-memo
|
||||
(mf/deps (:file-id color))
|
||||
#(make-colors-library-ref :workspace-libraries (:file-id color)))
|
||||
mf/deref)]
|
||||
(or colors-library-v colors-library-ws)))
|
||||
|
||||
(defn- get-file-colors []
|
||||
(or (mf/deref file-colors-ref) (mf/deref refs/workspace-file-colors)))
|
||||
|
||||
(mf/defc color-row [{:keys [color format copy-data on-change-format]}]
|
||||
(let [colors-library-ref (mf/use-memo
|
||||
(mf/deps (:file-id color))
|
||||
(make-colors-library-ref (:file-id color)))
|
||||
colors-library (mf/deref colors-library-ref)
|
||||
|
||||
file-colors (mf/deref file-colors-ref)
|
||||
|
||||
color-library-name (get-in (or colors-library file-colors) [(:id color) :name])]
|
||||
(let [colors-library (get-colors-library color)
|
||||
file-colors (get-file-colors)
|
||||
color-library-name (get-in (or colors-library file-colors) [(:id color) :name])
|
||||
color (assoc color :color-library-name color-library-name)]
|
||||
[:div.attributes-color-row
|
||||
(when color-library-name
|
||||
[:div.attributes-color-id
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
[:& text/text-shape {:shape shape}]]
|
||||
|
||||
(when (and (debug? :text-outline) (d/not-empty? (:position-data shape)))
|
||||
[:g {:transform (gsh/transform-str shape {:no-flip true})}
|
||||
[:g {:transform (gsh/transform-str shape)}
|
||||
(let [bounding-box (gsht/position-data-selrect shape)]
|
||||
[:rect {
|
||||
:x (:x bounding-box)
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
(some? text-modifier)
|
||||
(dwt/apply-text-modifier text-modifier))
|
||||
|
||||
transform (gsh/transform-str shape {:no-flip true})
|
||||
transform (gsh/transform-str shape)
|
||||
{:keys [x y width height]} shape]
|
||||
|
||||
[:rect.main.viewport-selrect
|
||||
|
|
|
@ -462,6 +462,7 @@
|
|||
on-change-ref (mf/use-ref nil)
|
||||
workspace-read-only? (mf/use-ctx ctx/workspace-read-only?)
|
||||
editable? (and local? (not workspace-read-only?))
|
||||
open? (if (nil? open?) (mf/use-state editing?) open?)
|
||||
|
||||
on-name-blur
|
||||
(mf/use-callback
|
||||
|
|
|
@ -247,6 +247,18 @@
|
|||
(= (:type shape) :path)
|
||||
(dissoc :content)))
|
||||
|
||||
(defn- is-bool-descendant?
|
||||
[shape all-shapes selected-shape-ids]
|
||||
(let [parent-id (:parent-id shape)
|
||||
parent (->> all-shapes
|
||||
(filter #(= (:id %) parent-id))
|
||||
first)]
|
||||
(cond
|
||||
(nil? shape) false ;; failsafe
|
||||
(some #{(:id shape)} selected-shape-ids) false ;; if it is one of the selected shapes, it is considerer not a bool descendant
|
||||
(= :bool (:type parent)) true ;; if its parent is of type bool, it is a bool descendant
|
||||
:else (is-bool-descendant? parent all-shapes selected-shape-ids)))) ;; else, check its parent
|
||||
|
||||
(mf/defc options
|
||||
{::mf/wrap [#(mf/memo' % (mf/check-props ["shapes" "shapes-with-children" "page-id" "file-id"]))]
|
||||
::mf/wrap-props false}
|
||||
|
@ -254,6 +266,10 @@
|
|||
(let [shapes (unchecked-get props "shapes")
|
||||
shapes-with-children (unchecked-get props "shapes-with-children")
|
||||
|
||||
;; remove children from bool shapes
|
||||
shape-ids (map :id shapes)
|
||||
shapes-with-children (filter #(not (is-bool-descendant? % shapes-with-children shape-ids)) shapes-with-children)
|
||||
|
||||
workspace-modifiers (mf/deref refs/workspace-modifiers)
|
||||
shapes (map #(gsh/transform-shape % (get-in workspace-modifiers [(:id %) :modifiers])) shapes)
|
||||
|
||||
|
@ -279,7 +295,6 @@
|
|||
|
||||
[measure-ids measure-values] (get-attrs shapes objects :measure)
|
||||
|
||||
|
||||
[layer-ids layer-values
|
||||
constraint-ids constraint-values
|
||||
fill-ids fill-values
|
||||
|
|
|
@ -126,7 +126,6 @@
|
|||
(mf/deps page-id)
|
||||
(fn [point]
|
||||
(let [zoom (mf/ref-val zoom-ref)
|
||||
mod? (mf/ref-val mod-ref)
|
||||
rect (gsh/center->rect point (/ 5 zoom) (/ 5 zoom))]
|
||||
(if (mf/ref-val hover-disabled-ref)
|
||||
(rx/of nil)
|
||||
|
@ -135,7 +134,7 @@
|
|||
:page-id page-id
|
||||
:rect rect
|
||||
:include-frames? true
|
||||
:clip-children? (not mod?)})
|
||||
:clip-children? true})
|
||||
;; When the ask-buffered is canceled returns null. We filter them
|
||||
;; to improve the behavior
|
||||
(rx/filter some?))))))
|
||||
|
|
|
@ -272,7 +272,7 @@
|
|||
current-transform (mf/deref refs/current-transform)
|
||||
|
||||
selrect (:selrect shape)
|
||||
transform (gsh/transform-str shape {:no-flip true})]
|
||||
transform (gsh/transform-str shape)]
|
||||
|
||||
(when (not (#{:move :rotate} current-transform))
|
||||
[:g.controls {:pointer-events (if disable-handlers "none" "visible")}
|
||||
|
@ -297,7 +297,7 @@
|
|||
workspace-read-only? (mf/use-ctx ctx/workspace-read-only?)
|
||||
|
||||
selrect (:selrect shape)
|
||||
transform (gsh/transform-matrix shape {:no-flip true})
|
||||
transform (gsh/transform-matrix shape)
|
||||
|
||||
rotation (-> (gpt/point 1 0)
|
||||
(gpt/transform (:transform shape))
|
||||
|
@ -309,7 +309,22 @@
|
|||
[:g.controls {:pointer-events (if disable-handlers "none" "visible")}
|
||||
;; Handlers
|
||||
(for [{:keys [type position props]} (handlers-for-selection selrect shape zoom)]
|
||||
(let [common-props {:key (dm/str (name type) "-" (name position))
|
||||
(let [rotation
|
||||
(cond
|
||||
(and (#{:top-left :bottom-right} position)
|
||||
(or (and (:flip-x shape) (not (:flip-y shape)))
|
||||
(and (:flip-y shape) (not (:flip-x shape)))))
|
||||
(- rotation 90)
|
||||
|
||||
(and (#{:top-right :bottom-left} position)
|
||||
(or (and (:flip-x shape) (not (:flip-y shape)))
|
||||
(and (:flip-y shape) (not (:flip-x shape)))))
|
||||
(+ rotation 90)
|
||||
|
||||
:else
|
||||
rotation)
|
||||
|
||||
common-props {:key (dm/str (name type) "-" (name position))
|
||||
:zoom zoom
|
||||
:position position
|
||||
:on-rotate on-rotate
|
||||
|
|
|
@ -346,3 +346,6 @@
|
|||
[read-only?]
|
||||
(st/emit! (dw/set-workspace-read-only read-only?)))
|
||||
|
||||
(defn ^:export fix-orphan-shapes
|
||||
[]
|
||||
(st/emit! (dw/fix-orphan-shapes)))
|
||||
|
|
|
@ -765,6 +765,10 @@ msgstr "Your browser cannot do this operation"
|
|||
msgid "errors.feature-not-supported"
|
||||
msgstr "Feature '%s' is not supported."
|
||||
|
||||
#: src/app/main/errors.cljs
|
||||
msgid "errors.max-quote-reached"
|
||||
msgstr "You have reached the '%s' quote. Contact with support."
|
||||
|
||||
#: src/app/main/errors.cljs
|
||||
msgid "errors.feature-mismatch"
|
||||
msgstr "Looks like you are opening a file that has the feature '%s' enabled bug your penpot frontend does not supports it or has it disabled."
|
||||
|
|
|
@ -743,6 +743,18 @@ msgstr "Webhook modificado con éxito"
|
|||
msgid "dashboard.webhooks.create.success"
|
||||
msgstr "Webhook creado con éxito"
|
||||
|
||||
#: src/app/main/errors.cljs
|
||||
msgid "errors.feature-not-supported"
|
||||
msgstr "Caracteristica no soportada: '%s'."
|
||||
|
||||
#: src/app/main/errors.cljs
|
||||
msgid "errors.max-quote-reached"
|
||||
msgstr "Ha alcalzando el maximo de la quota '%s'. Contacte con soporte tecnico."
|
||||
|
||||
#: src/app/main/errors.cljs
|
||||
msgid "errors.feature-mismatch"
|
||||
msgstr "Parece que esta abriendo un fichero con la caracteristica '%s' habilitada pero la aplicacion web de penpot que esta usando no tiene soporte para ella o esta deshabilitada."
|
||||
|
||||
msgid "errors.webhooks.timeout"
|
||||
msgstr "Timeout"
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue