mirror of
https://github.com/penpot/penpot.git
synced 2025-08-07 14:38:33 +02:00
Merge remote-tracking branch 'origin/staging'
This commit is contained in:
commit
bbb64b8be9
9 changed files with 41 additions and 218 deletions
|
@ -16,6 +16,7 @@
|
||||||
[app.db.sql :as sql]
|
[app.db.sql :as sql]
|
||||||
[app.features.components-v2 :as feat.compv2]
|
[app.features.components-v2 :as feat.compv2]
|
||||||
[app.features.fdata :as fdata]
|
[app.features.fdata :as fdata]
|
||||||
|
[app.loggers.audit :as audit]
|
||||||
[app.rpc :as-alias rpc]
|
[app.rpc :as-alias rpc]
|
||||||
[app.rpc.commands.files :as files]
|
[app.rpc.commands.files :as files]
|
||||||
[app.rpc.commands.files-create :as files.create]
|
[app.rpc.commands.files-create :as files.create]
|
||||||
|
@ -23,6 +24,7 @@
|
||||||
[app.rpc.commands.projects :as projects]
|
[app.rpc.commands.projects :as projects]
|
||||||
[app.rpc.commands.teams :as teams]
|
[app.rpc.commands.teams :as teams]
|
||||||
[app.rpc.doc :as-alias doc]
|
[app.rpc.doc :as-alias doc]
|
||||||
|
[app.rpc.helpers :as rph]
|
||||||
[app.util.blob :as blob]
|
[app.util.blob :as blob]
|
||||||
[app.util.pointer-map :as pmap]
|
[app.util.pointer-map :as pmap]
|
||||||
[app.util.services :as sv]
|
[app.util.services :as sv]
|
||||||
|
@ -100,7 +102,9 @@
|
||||||
:revn revn
|
:revn revn
|
||||||
:data nil
|
:data nil
|
||||||
:changes (blob/encode changes)})
|
:changes (blob/encode changes)})
|
||||||
nil)))
|
(rph/with-meta (rph/wrap nil)
|
||||||
|
{::audit/replace-props {:file-id id
|
||||||
|
:revn revn}}))))
|
||||||
|
|
||||||
;; --- MUTATION COMMAND: persist-temp-file
|
;; --- MUTATION COMMAND: persist-temp-file
|
||||||
|
|
||||||
|
|
|
@ -143,18 +143,12 @@
|
||||||
(map-indexed vector)
|
(map-indexed vector)
|
||||||
(filter #(#{(:id group)} (second %)))
|
(filter #(#{(:id group)} (second %)))
|
||||||
(ffirst)
|
(ffirst)
|
||||||
inc)
|
inc)]
|
||||||
|
|
||||||
;; Shapes that are in a component (including root) must be detached,
|
|
||||||
;; because cannot be easyly synchronized back to the main component.
|
|
||||||
shapes-to-detach (filter ctk/in-component-copy?
|
|
||||||
(cfh/get-children-with-self objects (:id group)))]
|
|
||||||
|
|
||||||
(-> (pcb/empty-changes it page-id)
|
(-> (pcb/empty-changes it page-id)
|
||||||
(pcb/with-objects objects)
|
(pcb/with-objects objects)
|
||||||
(pcb/change-parent parent-id children index-in-parent)
|
(pcb/change-parent parent-id children index-in-parent)
|
||||||
(pcb/remove-objects [(:id group)])
|
(pcb/remove-objects [(:id group)]))))
|
||||||
(pcb/update-shapes (map :id shapes-to-detach) ctk/detach-shape))))
|
|
||||||
|
|
||||||
(defn remove-frame-changes
|
(defn remove-frame-changes
|
||||||
[it page-id frame objects]
|
[it page-id frame objects]
|
||||||
|
|
|
@ -133,7 +133,10 @@
|
||||||
(defn- fetch-gfont-css
|
(defn- fetch-gfont-css
|
||||||
[url]
|
[url]
|
||||||
(->> (http/send! {:method :get :uri url :mode :cors :response-type :text})
|
(->> (http/send! {:method :get :uri url :mode :cors :response-type :text})
|
||||||
(rx/map :body)))
|
(rx/map :body)
|
||||||
|
(rx/catch (fn [err]
|
||||||
|
(.warn js/console "Cannot find the font" (obj/get err "message"))
|
||||||
|
(rx/empty)))))
|
||||||
|
|
||||||
(defmethod load-font :google
|
(defmethod load-font :google
|
||||||
[{:keys [id ::on-loaded] :as font}]
|
[{:keys [id ::on-loaded] :as font}]
|
||||||
|
|
|
@ -7,15 +7,11 @@
|
||||||
(ns app.main.ui.dashboard.projects
|
(ns app.main.ui.dashboard.projects
|
||||||
(:require-macros [app.main.style :as stl])
|
(:require-macros [app.main.style :as stl])
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.config :as cf]
|
|
||||||
[app.main.data.dashboard :as dd]
|
[app.main.data.dashboard :as dd]
|
||||||
[app.main.data.events :as ev]
|
[app.main.data.events :as ev]
|
||||||
[app.main.data.messages :as msg]
|
|
||||||
[app.main.data.modal :as modal]
|
[app.main.data.modal :as modal]
|
||||||
[app.main.data.users :as du]
|
[app.main.data.users :as du]
|
||||||
[app.main.errors :as errors]
|
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.dashboard.grid :refer [line-grid]]
|
[app.main.ui.dashboard.grid :refer [line-grid]]
|
||||||
|
@ -100,80 +96,6 @@
|
||||||
(def builtin-templates
|
(def builtin-templates
|
||||||
(l/derived :builtin-templates st/state))
|
(l/derived :builtin-templates st/state))
|
||||||
|
|
||||||
(mf/defc tutorial-project
|
|
||||||
[{:keys [close-tutorial default-project-id] :as props}]
|
|
||||||
(let [state (mf/use-state {:status :waiting
|
|
||||||
:file nil})
|
|
||||||
|
|
||||||
templates (mf/deref builtin-templates)
|
|
||||||
template (d/seek #(= (:id %) "tutorial-for-beginners") templates)
|
|
||||||
|
|
||||||
on-template-cloned-success
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps default-project-id)
|
|
||||||
(fn [response]
|
|
||||||
(swap! state #(assoc % :status :success :file (:first response)))
|
|
||||||
(st/emit! (dd/go-to-workspace {:id (first response) :project-id default-project-id :name "tutorial"})
|
|
||||||
(du/update-profile-props {:viewed-tutorial? true}))))
|
|
||||||
|
|
||||||
on-template-cloned-error
|
|
||||||
(mf/use-fn
|
|
||||||
(fn [cause]
|
|
||||||
(swap! state assoc :status :error)
|
|
||||||
(errors/print-error! cause)
|
|
||||||
(st/emit! (msg/error (tr "dashboard.libraries-and-templates.import-error")))))
|
|
||||||
|
|
||||||
download-tutorial
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps template default-project-id)
|
|
||||||
(fn []
|
|
||||||
(let [mdata {:on-success on-template-cloned-success
|
|
||||||
:on-error on-template-cloned-error}
|
|
||||||
params {:project-id default-project-id
|
|
||||||
:template-id (:id template)}]
|
|
||||||
(swap! state #(assoc % :status :importing))
|
|
||||||
(st/emit! (with-meta (dd/clone-template (with-meta params mdata))
|
|
||||||
{::ev/origin "get-started-hero-block"})))))]
|
|
||||||
[:article {:class (stl/css :tutorial)}
|
|
||||||
[:div {:class (stl/css :thumbnail)}]
|
|
||||||
[:div {:class (stl/css :text)}
|
|
||||||
[:h2 {:class (stl/css :title)} (tr "dasboard.tutorial-hero.title")]
|
|
||||||
[:p {:class (stl/css :info)} (tr "dasboard.tutorial-hero.info")]
|
|
||||||
[:button {:class (stl/css :btn-primary :action)
|
|
||||||
:on-click download-tutorial}
|
|
||||||
(case (:status @state)
|
|
||||||
:waiting (tr "dasboard.tutorial-hero.start")
|
|
||||||
:importing [:span.loader i/loader-pencil]
|
|
||||||
:success "")]]
|
|
||||||
|
|
||||||
[:button {:class (stl/css :close)
|
|
||||||
:on-click close-tutorial
|
|
||||||
:aria-label (tr "labels.close")}
|
|
||||||
close-icon]]))
|
|
||||||
|
|
||||||
(mf/defc interface-walkthrough
|
|
||||||
{::mf/wrap [mf/memo]}
|
|
||||||
[{:keys [close-walkthrough] :as props}]
|
|
||||||
(let [handle-walkthrough-link
|
|
||||||
(fn []
|
|
||||||
(st/emit! (ptk/event ::ev/event {::ev/name "show-walkthrough"
|
|
||||||
::ev/origin "get-started-hero-block"
|
|
||||||
:section "dashboard"})))]
|
|
||||||
[:article {:class (stl/css :walkthrough)}
|
|
||||||
[:div {:class (stl/css :thumbnail)}]
|
|
||||||
[:div {:class (stl/css :text)}
|
|
||||||
[:h2 {:class (stl/css :title)} (tr "dasboard.walkthrough-hero.title")]
|
|
||||||
[:p {:class (stl/css :info)} (tr "dasboard.walkthrough-hero.info")]
|
|
||||||
[:a {:class (stl/css :btn-primary :action)
|
|
||||||
:href " https://design.penpot.app/walkthrough"
|
|
||||||
:target "_blank"
|
|
||||||
:on-click handle-walkthrough-link}
|
|
||||||
(tr "dasboard.walkthrough-hero.start")]]
|
|
||||||
[:button {:class (stl/css :close)
|
|
||||||
:on-click close-walkthrough
|
|
||||||
:aria-label (tr "labels.close")}
|
|
||||||
close-icon]]))
|
|
||||||
|
|
||||||
(mf/defc project-item
|
(mf/defc project-item
|
||||||
[{:keys [project first? team files] :as props}]
|
[{:keys [project first? team files] :as props}]
|
||||||
(let [locale (mf/deref i18n/locale)
|
(let [locale (mf/deref i18n/locale)
|
||||||
|
@ -365,7 +287,7 @@
|
||||||
(l/derived :dashboard-recent-files st/state))
|
(l/derived :dashboard-recent-files st/state))
|
||||||
|
|
||||||
(mf/defc projects-section
|
(mf/defc projects-section
|
||||||
[{:keys [team projects profile default-project-id] :as props}]
|
[{:keys [team projects profile] :as props}]
|
||||||
(let [projects (->> (vals projects)
|
(let [projects (->> (vals projects)
|
||||||
(sort-by :modified-at)
|
(sort-by :modified-at)
|
||||||
(reverse))
|
(reverse))
|
||||||
|
@ -378,8 +300,6 @@
|
||||||
(:team-hero? props true)
|
(:team-hero? props true)
|
||||||
(not (:is-default team)))
|
(not (:is-default team)))
|
||||||
|
|
||||||
tutorial-viewed? (:viewed-tutorial? props true)
|
|
||||||
walkthrough-viewed? (:viewed-walkthrough? props true)
|
|
||||||
is-my-penpot (= (:default-team-id profile) (:id team))
|
is-my-penpot (= (:default-team-id profile) (:id team))
|
||||||
|
|
||||||
team-id (:id team)
|
team-id (:id team)
|
||||||
|
@ -391,28 +311,6 @@
|
||||||
(ptk/data-event ::ev/event {::ev/name "dont-show-team-up-hero"
|
(ptk/data-event ::ev/event {::ev/name "dont-show-team-up-hero"
|
||||||
::ev/origin "dashboard"}))))
|
::ev/origin "dashboard"}))))
|
||||||
|
|
||||||
close-tutorial
|
|
||||||
(mf/use-fn
|
|
||||||
(fn []
|
|
||||||
(st/emit! (du/update-profile-props {:viewed-tutorial? true})
|
|
||||||
(ptk/data-event ::ev/event {::ev/name "dont-show-tutorial"
|
|
||||||
::ev/origin "get-started-hero"
|
|
||||||
:type "tutorial"
|
|
||||||
:section "dashboard"}))))
|
|
||||||
|
|
||||||
close-walkthrough
|
|
||||||
(mf/use-fn
|
|
||||||
(fn []
|
|
||||||
(st/emit! (du/update-profile-props {:viewed-walkthrough? true})
|
|
||||||
(ptk/data-event ::ev/event {::ev/name "dont-show-walkthrough"
|
|
||||||
::ev/origin "get-started-hero"
|
|
||||||
:type "walkthrough"
|
|
||||||
:section "dashboard"}))))
|
|
||||||
|
|
||||||
show-hero? (and is-my-penpot
|
|
||||||
(or (not tutorial-viewed?)
|
|
||||||
(not walkthrough-viewed?)))
|
|
||||||
|
|
||||||
show-team-hero? (and (not is-my-penpot) team-hero?)]
|
show-team-hero? (and (not is-my-penpot) team-hero?)]
|
||||||
|
|
||||||
(mf/with-effect [team]
|
(mf/with-effect [team]
|
||||||
|
@ -433,22 +331,9 @@
|
||||||
(when team-hero?
|
(when team-hero?
|
||||||
[:& team-hero {:team team :close-fn close-banner}])
|
[:& team-hero {:team team :close-fn close-banner}])
|
||||||
|
|
||||||
(when (and (contains? cf/flags :dashboard-templates-section)
|
|
||||||
show-hero?)
|
|
||||||
[:div {:class (stl/css :hero-projects)}
|
|
||||||
(when (and (not tutorial-viewed?) (:is-default team))
|
|
||||||
[:& tutorial-project
|
|
||||||
{:close-tutorial close-tutorial
|
|
||||||
:default-project-id default-project-id}])
|
|
||||||
|
|
||||||
(when (and (not walkthrough-viewed?) (:is-default team))
|
|
||||||
[:& interface-walkthrough
|
|
||||||
{:close-walkthrough close-walkthrough}])])
|
|
||||||
|
|
||||||
[:div {:class (stl/css-case :dashboard-container true
|
[:div {:class (stl/css-case :dashboard-container true
|
||||||
:no-bg true
|
:no-bg true
|
||||||
:dashboard-projects true
|
:dashboard-projects true
|
||||||
:with-hero show-hero?
|
|
||||||
:with-team-hero show-team-hero?)}
|
:with-team-hero show-team-hero?)}
|
||||||
(for [{:keys [id] :as project} projects]
|
(for [{:keys [id] :as project} projects]
|
||||||
(let [files (when recent-map
|
(let [files (when recent-map
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
height: calc(100vh - $s-64);
|
height: calc(100vh - $s-64);
|
||||||
}
|
}
|
||||||
|
|
||||||
.with-hero,
|
|
||||||
.with-team-hero {
|
.with-team-hero {
|
||||||
height: calc(100vh - $s-280);
|
height: calc(100vh - $s-280);
|
||||||
}
|
}
|
||||||
|
@ -242,88 +241,3 @@
|
||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-projects {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
grid-gap: $s-32;
|
|
||||||
margin: 0 $s-16 $s-16 $s-20;
|
|
||||||
|
|
||||||
@media (max-width: 1366px) {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tutorial,
|
|
||||||
.walkthrough {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: auto 1fr;
|
|
||||||
position: relative;
|
|
||||||
border-radius: $br-8;
|
|
||||||
min-height: $s-216;
|
|
||||||
background-color: $db-tertiary;
|
|
||||||
padding: $s-8;
|
|
||||||
|
|
||||||
.thumbnail {
|
|
||||||
width: $s-200;
|
|
||||||
height: $s-200;
|
|
||||||
border-radius: $br-6;
|
|
||||||
padding: $s-32;
|
|
||||||
display: block;
|
|
||||||
background-color: var(--color-canvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
border-radius: $br-4;
|
|
||||||
margin-bottom: 0;
|
|
||||||
width: $s-232;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text {
|
|
||||||
padding: $s-32;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
color: $df-primary;
|
|
||||||
font-size: $fs-24;
|
|
||||||
font-weight: $fw400;
|
|
||||||
margin-bottom: $s-8;
|
|
||||||
}
|
|
||||||
.info {
|
|
||||||
flex: 1;
|
|
||||||
color: $df-secondary;
|
|
||||||
margin-bottom: $s-20;
|
|
||||||
font-size: $fs-16;
|
|
||||||
}
|
|
||||||
.invite {
|
|
||||||
height: $s-32;
|
|
||||||
}
|
|
||||||
.action {
|
|
||||||
width: $s-180;
|
|
||||||
height: $s-40;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.walkthrough {
|
|
||||||
.thumbnail {
|
|
||||||
background-image: url("/images/walkthrough-cover.png");
|
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.tutorial {
|
|
||||||
.thumbnail {
|
|
||||||
background-image: url("/images/hands-on-tutorial.png");
|
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
.loader {
|
|
||||||
display: flex;
|
|
||||||
svg#loader-pencil {
|
|
||||||
width: $s-32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -122,7 +122,11 @@
|
||||||
(add! :stroke-cap-end)))
|
(add! :stroke-cap-end)))
|
||||||
|
|
||||||
(cond-> text?
|
(cond-> text?
|
||||||
(-> (add! :grow-type)
|
(-> (add! :x)
|
||||||
|
(add! :y)
|
||||||
|
(add! :width)
|
||||||
|
(add! :height)
|
||||||
|
(add! :grow-type)
|
||||||
(add! :content (comp json/encode uuid->string))
|
(add! :content (comp json/encode uuid->string))
|
||||||
(add! :position-data (comp json/encode uuid->string))))
|
(add! :position-data (comp json/encode uuid->string))))
|
||||||
|
|
||||||
|
|
|
@ -94,6 +94,13 @@
|
||||||
(obj/unset! "disable-shadows?")
|
(obj/unset! "disable-shadows?")
|
||||||
(obj/set! "ref" ref)
|
(obj/set! "ref" ref)
|
||||||
(obj/set! "id" (dm/fmt "shape-%" shape-id))
|
(obj/set! "id" (dm/fmt "shape-%" shape-id))
|
||||||
|
|
||||||
|
;; TODO: This is added for backward compatibility.
|
||||||
|
(cond-> (and (cfh/text-shape? shape) (empty? (:position-data shape)))
|
||||||
|
(-> (obj/set! "x" (:x shape))
|
||||||
|
(obj/set! "y" (:y shape))
|
||||||
|
(obj/set! "width" (:width shape))
|
||||||
|
(obj/set! "height" (:height shape))))
|
||||||
(obj/set! "style" styles))
|
(obj/set! "style" styles))
|
||||||
|
|
||||||
wrapper-props
|
wrapper-props
|
||||||
|
|
|
@ -332,7 +332,7 @@
|
||||||
:title (tr "viewer.header.comments-section" (sc/get-tooltip :open-comments))}
|
:title (tr "viewer.header.comments-section" (sc/get-tooltip :open-comments))}
|
||||||
i/comments])
|
i/comments])
|
||||||
|
|
||||||
(when (or (= (:type permissions) :membership)
|
(when (or (:in-team permissions)
|
||||||
(and (= (:type permissions) :share-link)
|
(and (= (:type permissions) :share-link)
|
||||||
(= (:who-inspect permissions) "all")))
|
(= (:who-inspect permissions) "all")))
|
||||||
[:button {:on-click go-to-inspect
|
[:button {:on-click go-to-inspect
|
||||||
|
|
|
@ -272,9 +272,21 @@
|
||||||
(def has-position? #{:frame :rect :image :text})
|
(def has-position? #{:frame :rect :image :text})
|
||||||
|
|
||||||
(defn parse-position
|
(defn parse-position
|
||||||
[props svg-data]
|
[props node svg-data]
|
||||||
(let [values (->> (select-keys svg-data [:x :y :width :height])
|
(let [x (get-meta node :x d/parse-double)
|
||||||
(d/mapm (fn [_ val] (d/parse-double val))))]
|
y (get-meta node :y d/parse-double)
|
||||||
|
width (get-meta node :width d/parse-double)
|
||||||
|
height (get-meta node :height d/parse-double)
|
||||||
|
|
||||||
|
values (->> (select-keys svg-data [:x :y :width :height])
|
||||||
|
(d/mapm (fn [_ val] (d/parse-double val))))
|
||||||
|
|
||||||
|
values
|
||||||
|
(cond-> values
|
||||||
|
(some? x) (assoc :x x)
|
||||||
|
(some? y) (assoc :y y)
|
||||||
|
(some? width) (assoc :width width)
|
||||||
|
(some? height) (assoc :height height))]
|
||||||
(d/merge props values)))
|
(d/merge props values)))
|
||||||
|
|
||||||
(defn parse-circle
|
(defn parse-circle
|
||||||
|
@ -392,7 +404,7 @@
|
||||||
center (gpt/point center-x center-y)]
|
center (gpt/point center-x center-y)]
|
||||||
(cond-> props
|
(cond-> props
|
||||||
(has-position? type)
|
(has-position? type)
|
||||||
(parse-position svg-data)
|
(parse-position node svg-data)
|
||||||
|
|
||||||
(= type :svg-raw)
|
(= type :svg-raw)
|
||||||
(add-svg-position node)
|
(add-svg-position node)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue