0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-08 16:18:11 -05:00

New plugin install workflow

This commit is contained in:
alonso.torres 2024-09-27 09:04:46 +02:00
parent a510d01136
commit d1277afee6
11 changed files with 252 additions and 41 deletions

View file

@ -941,7 +941,7 @@
(update-in [:dashboard-projects project-id :count] inc)))))
(defn create-file
[{:keys [project-id] :as params}]
[{:keys [project-id name] :as params}]
(dm/assert! (uuid? project-id))
(ptk/reify ::create-file
ev/Event
@ -955,7 +955,7 @@
files (get state :dashboard-files)
unames (cfh/get-used-names files)
name (cfh/generate-unique-name unames (str (tr "dashboard.new-file-prefix") " 1"))
name (or name (cfh/generate-unique-name unames (str (tr "dashboard.new-file-prefix") " 1")))
features (-> (features/get-team-enabled-features state)
(set/difference cfeat/frontend-only-features))
params (-> params

View file

@ -0,0 +1,42 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.main.data.plugins
(:require
[app.plugins.register :as pr]
[potok.v2.core :as ptk]))
(defn open-plugin!
[{:keys [plugin-id name description host code icon permissions]}]
(try
(.ɵloadPlugin
js/window
#js {:pluginId plugin-id
:name name
:description description
:host host
:code code
:icon icon
:permissions (apply array permissions)})
(catch :default e
(.error js/console "Error" e))))
(defn delay-open-plugin
[plugin]
(ptk/reify ::delay-open-plugin
ptk/UpdateEvent
(update [_ state]
(assoc state ::open-plugin (:plugin-id plugin)))))
(defn check-open-plugin
[]
(ptk/reify ::check-open-plugin
ptk/WatchEvent
(watch [_ state _]
(when-let [pid (::open-plugin state)]
(open-plugin! (pr/get-plugin pid))
(fn [state]
(dissoc state ::open-plugin))))))

View file

@ -42,6 +42,7 @@
[app.main.data.modal :as modal]
[app.main.data.notifications :as ntf]
[app.main.data.persistence :as dps]
[app.main.data.plugins :as dp]
[app.main.data.users :as du]
[app.main.data.workspace.bool :as dwb]
[app.main.data.workspace.collapse :as dwco]
@ -131,6 +132,7 @@
(when (and (not (boolean (-> state :profile :props :v2-info-shown)))
(features/active-feature? state "components/v2"))
(modal/show :v2-info {}))
(dp/check-open-plugin)
(fdf/fix-deleted-fonts)
(fbs/fix-broken-shapes)))))

View file

@ -8,10 +8,15 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.spec :as us]
[app.config :as cf]
[app.main.data.dashboard :as dd]
[app.main.data.dashboard.shortcuts :as sc]
[app.main.data.events :as ev]
[app.main.data.modal :as modal]
[app.main.data.notifications :as notif]
[app.main.data.plugins :as dp]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.context :as ctx]
@ -25,11 +30,17 @@
[app.main.ui.dashboard.team :refer [team-settings-page team-members-page team-invitations-page team-webhooks-page]]
[app.main.ui.dashboard.templates :refer [templates-section]]
[app.main.ui.hooks :as hooks]
[app.main.ui.workspace.plugins]
[app.plugins.register :as preg]
[app.util.dom :as dom]
[app.util.http :as http]
[app.util.keyboard :as kbd]
[app.util.object :as obj]
[app.util.router :as rt]
[beicon.v2.core :as rx]
[goog.events :as events]
[okulary.core :as l]
[potok.v2.core :as ptk]
[rumext.v2 :as mf]))
(defn ^boolean uuid-str?
@ -143,6 +154,70 @@
(def dashboard-initialized
(l/derived :current-team-id st/state))
(defn use-plugin-register
[plugin-url team-id project-id]
(let [navegate-file!
(fn [plugin {:keys [project-id id data]}]
(st/emit!
(dp/delay-open-plugin plugin)
(rt/nav :workspace
{:project-id project-id :file-id id}
{:page-id (dm/get-in data [:pages 0])})))
create-file!
(fn [plugin]
(st/emit!
(modal/hide)
(let [data
(with-meta
{:project-id project-id
:name (dm/str "Try plugin: " (:name plugin))}
{:on-success (partial navegate-file! plugin)})]
(-> (dd/create-file data)
(with-meta {::ev/origin "plugin-try-out"})))))
open-try-out-dialog
(fn [plugin]
(modal/show
:plugin-try-out
{:plugin plugin
:on-accept #(create-file! plugin)
:on-close #(modal/hide!)}))
open-permissions-dialog
(fn [plugin]
(modal/show!
:plugin-permissions
{:plugin plugin
:on-accept
#(do (preg/install-plugin! plugin)
(st/emit! (modal/hide)
(rt/nav :dashboard-projects {:team-id team-id})
(open-try-out-dialog plugin)))
:on-close
#(st/emit! (modal/hide)
(rt/nav :dashboard-projects {:team-id team-id}))}))]
(mf/with-layout-effect
[plugin-url team-id project-id]
(when plugin-url
(->> (http/send! {:method :get
:uri plugin-url
:omit-default-headers true
:response-type :json})
(rx/map :body)
(rx/subs!
(fn [body]
(if-let [plugin (preg/parse-manifest plugin-url body)]
(do
(st/emit! (ptk/event ::ev/event {::ev/name "install-plugin" :name (:name plugin) :url plugin-url}))
(open-permissions-dialog plugin))
(st/emit! (notif/error "Cannot parser the plugin manifest"))))
(fn [_]
(st/emit! (notif/error "The plugin URL is incorrect")))))))))
(mf/defc dashboard
{::mf/props :obj}
[{:keys [route profile]}]
@ -150,8 +225,12 @@
params (parse-params route)
project-id (:project-id params)
team-id (:team-id params)
search-term (:search-term params)
plugin-url (-> route :query-params :plugin)
invite-email (-> route :query-params :invite-email)
teams (mf/deref refs/teams)
@ -160,6 +239,8 @@
projects (mf/deref refs/dashboard-projects)
project (get projects project-id)
default-project (->> projects vals (d/seek :is-default))
initialized? (mf/deref dashboard-initialized)]
(hooks/use-shortcuts ::dashboard sc/shortcuts)
@ -178,6 +259,8 @@
(fn []
(events/unlistenByKey key))))
(use-plugin-register plugin-url team-id (:id default-project))
[:& (mf/provider ctx/current-team-id) {:value team-id}
[:& (mf/provider ctx/current-project-id) {:value project-id}
;; NOTE: dashboard events and other related functions assumes
@ -206,4 +289,3 @@
:search-term search-term
:team team
:invite-email invite-email}])])]]))

View file

@ -16,6 +16,7 @@
[app.util.router :as rt]
[beicon.v2.core :as rx]
[cljs.spec.alpha :as s]
[cuerdas.core :as str]
[potok.v2.core :as ptk]))
(s/def ::page-id ::us/uuid)
@ -94,10 +95,17 @@
(defn on-navigate
[router path]
(let [location (.-location js/document)
[base-path qs] (str/split path "?")
qstring
(->> (str/split qs "&")
(map #(str/split % "="))
(into {}))
location-path (dm/str (.-origin location) (.-pathname location))
valid-location? (= location-path (dm/str cf/public-uri))
match (match-path router path)
empty-path? (or (= path "") (= path "/"))]
empty-path? (or (= base-path "") (= base-path "/"))]
(cond
(not valid-location?)
(st/emit! (rt/assign-exception {:type :not-found}))
@ -116,7 +124,7 @@
(st/emit! (rt/nav :auth-login))
empty-path?
(st/emit! (rt/nav :dashboard-projects {:team-id (du/get-current-team-id profile)}))
(st/emit! (rt/nav :dashboard-projects {:team-id (du/get-current-team-id profile)} qstring))
:else
(st/emit! (rt/assign-exception {:type :not-found})))))))))

View file

@ -16,6 +16,7 @@
[app.main.data.events :as ev]
[app.main.data.exports :as de]
[app.main.data.modal :as modal]
[app.main.data.plugins :as dp]
[app.main.data.shortcuts :as scd]
[app.main.data.users :as du]
[app.main.data.workspace :as dw]
@ -29,7 +30,6 @@
[app.main.ui.context :as ctx]
[app.main.ui.hooks.resize :as r]
[app.main.ui.icons :as i]
[app.main.ui.workspace.plugins :as uwp]
[app.plugins.register :as preg]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
@ -637,7 +637,7 @@
::ev/origin "workspace:menu"
:name name
:host host}))
(uwp/open-plugin! manifest))
(dp/open-plugin! manifest))
:class (stl/css :submenu-item)
:on-key-down (fn [event]
(when (kbd/enter? event)
@ -646,7 +646,7 @@
::ev/origin "workspace:menu"
:name name
:host host}))
(uwp/open-plugin! manifest))))}
(dp/open-plugin! manifest))))}
[:span {:class (stl/css :item-name)} name]])])))
(mf/defc menu

View file

@ -12,6 +12,7 @@
[app.config :as cf]
[app.main.data.events :as ev]
[app.main.data.modal :as modal]
[app.main.data.plugins :as dp]
[app.main.store :as st]
[app.main.ui.components.search-bar :refer [search-bar]]
[app.main.ui.components.title-bar :refer [title-bar]]
@ -59,22 +60,6 @@
[:button {:class (stl/css :trash-button)
:on-click handle-delete-click} i/delete]]))
(defn open-plugin!
[{:keys [plugin-id name description host code icon permissions]}]
(try
(.ɵloadPlugin
js/window
#js {:pluginId plugin-id
:name name
:description description
:host host
:code code
:icon icon
:permissions (apply array permissions)})
(catch :default e
(.error js/console "Error" e))))
(mf/defc plugin-management-dialog
{::mf/register modal/components
::mf/register-as :plugin-management}
@ -144,7 +129,7 @@
::ev/origin "workspace:plugins"
:name (:name manifest)
:host (:host manifest)}))
(open-plugin! manifest)
(dp/open-plugin! manifest)
(modal/hide!)))
handle-remove-plugin
@ -215,7 +200,7 @@
(mf/defc plugins-permissions-dialog
{::mf/register modal/components
::mf/register-as :plugin-permissions}
[{:keys [plugin on-accept]}]
[{:keys [plugin on-accept on-close]}]
(let [{:keys [host permissions]} plugin
permissions (set permissions)
@ -224,25 +209,26 @@
(mf/use-callback
(fn [event]
(dom/prevent-default event)
(st/emit! (modal/hide))
(ptk/event ::ev/event {::ev/name "allow-plugin-permissions"
:host host
:permissions (->> permissions (str/join ", "))})
(on-accept)))
(st/emit! (ptk/event ::ev/event {::ev/name "allow-plugin-permissions"
:host host
:permissions (->> permissions (str/join ", "))})
(modal/hide))
(when on-accept (on-accept))))
handle-close-dialog
(mf/use-callback
(fn [event]
(dom/prevent-default event)
(ptk/event ::ev/event {::ev/name "reject-plugin-permissions"
:host host
:permissions (->> permissions (str/join ", "))})
(st/emit! (modal/hide))))]
(st/emit! (ptk/event ::ev/event {::ev/name "reject-plugin-permissions"
:host host
:permissions (->> permissions (str/join ", "))})
(modal/hide))
(when on-close (on-close))))]
[:div {:class (stl/css :modal-overlay)}
[:div {:class (stl/css :modal-dialog :plugin-permissions)}
[:button {:class (stl/css :close-btn) :on-click handle-close-dialog} close-icon]
[:div {:class (stl/css :modal-title)} (tr "workspace.plugins.permissions.title")]
[:div {:class (stl/css :modal-title)} (tr "workspace.plugins.permissions.title" (str/upper (:name plugin)))]
[:div {:class (stl/css :modal-content)}
[:div {:class (stl/css :permissions-list)}
@ -293,7 +279,7 @@
(tr "workspace.plugins.permissions.comment-read")]])
(cond
(contains? permissions "allow:download")
(contains? permissions "allow:downloads")
[:div {:class (stl/css :permissions-list-entry)}
i/oauth-1
[:p {:class (stl/css :permissions-list-text)}
@ -315,3 +301,55 @@
:type "button"
:value (tr "ds.confirm-allow")
:on-click handle-accept-dialog}]]]]]))
(mf/defc plugins-try-out-dialog
{::mf/register modal/components
::mf/register-as :plugin-try-out}
[{:keys [plugin on-accept on-close]}]
(let [{:keys [icon host name]} plugin
handle-accept-dialog
(mf/use-callback
(fn [event]
(dom/prevent-default event)
(st/emit! (ptk/event ::ev/event {::ev/name "try-out-accept"})
(modal/hide))
(when on-accept (on-accept))))
handle-close-dialog
(mf/use-callback
(fn [event]
(dom/prevent-default event)
(st/emit! (ptk/event ::ev/event {::ev/name "try-out-cancel"})
(modal/hide))
(when on-close (on-close))))]
[:div {:class (stl/css :modal-overlay)}
[:div {:class (stl/css :modal-dialog :plugin-try-out)}
[:button {:class (stl/css :close-btn) :on-click handle-close-dialog} close-icon]
[:div {:class (stl/css :modal-title)}
[:div {:class (stl/css :plugin-icon)}
[:img {:src (if (some? icon)
(dm/str host icon)
(avatars/generate {:name name}))}]]
(tr "workspace.plugins.try-out.title" (str/upper (:name plugin)))]
[:div {:class (stl/css :modal-content)}
[:div {:class (stl/css :modal-message)}
(tr "workspace.plugins.try-out.message")]]
[:div {:class (stl/css :modal-footer)}
[:div {:class (stl/css :action-buttons)}
[:input
{:class (stl/css :cancel-button :button-expand)
:type "button"
:value (tr "workspace.plugins.try-out.cancel")
:on-click handle-close-dialog}]
[:input
{:class (stl/css :primary-button :button-expand)
:type "button"
:value (tr "workspace.plugins.try-out.try")
:on-click handle-accept-dialog}]]]]]))

View file

@ -26,6 +26,11 @@
max-width: $s-472;
}
&.plugin-try-out {
width: $s-452;
max-width: $s-452;
}
hr {
border-color: var(--color-background-tertiary);
}
@ -48,6 +53,8 @@
@include headlineMediumTypography;
margin-block-end: $s-32;
color: var(--modal-title-foreground-color);
display: flex;
gap: $s-12;
}
.modal-content {
@ -64,6 +71,11 @@
}
}
.modal-message {
font-size: $fs-14;
color: var(--color-foreground-secondary);
}
.primary-button {
@extend .button-primary;
@include headlineSmallTypography;

View file

@ -5,7 +5,6 @@
;; Copyright (c) KALEIDOS INC
(ns app.plugins.register
"RPC for plugins runtime."
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
@ -26,6 +25,10 @@
(->> (:ids @registry)
(mapv #(dm/get-in @registry [:data %]))))
(defn get-plugin
[id]
(dm/get-in @registry [:data id]))
(defn parse-manifest
"Read the manifest.json defined by the plugins definition and transforms it into an
object that will be stored in the register."

View file

@ -5601,12 +5601,24 @@ msgstr "Start file downloads."
#: src/app/main/ui/workspace/plugins.cljs:236
msgid "workspace.plugins.permissions.title"
msgstr "THIS PLUGIN WANTS ACCESS TO:"
msgstr "'%s' PLUGIN WANTS ACCESS TO:"
#: src/app/main/ui/workspace/plugins.cljs:258
msgid "workspace.plugins.permissions.user-read"
msgstr "Read the profile information of the current user."
msgid "workspace.plugins.try-out.title"
msgstr "'%s' PLUGIN IS INSTALLED FOR YOUR USER!"
msgid "workspace.plugins.try-out.message"
msgstr "Want to take a look? It will open in a new draft for your current team. (If not, you can always find it in the installed plugins of any file.)"
msgid "workspace.plugins.try-out.cancel"
msgstr "NOT NOW"
msgid "workspace.plugins.try-out.try"
msgstr "TRY PLUGIN"
#: src/app/main/ui/workspace/plugins.cljs:192
msgid "workspace.plugins.plugin-list-link"
msgstr "Plugins List"

View file

@ -5567,7 +5567,7 @@ msgstr "Leer y modificar el contenido de sus archivos."
#: src/app/main/ui/workspace/plugins.cljs:274
msgid "workspace.plugins.permissions.disclaimer"
msgstr "Tenga en cuenta que las extensiones están desarrolladas por terceros, asegursé que confía antes de conceder el permiso. Su privacidad y seguridad es importante para nosotros. Si tiene cualquier duda, contacte soporte."
msgstr "Ten en cuenta que las extensiones están desarrolladas por terceros, aseguraté que confías antes de conceder el permiso. Tu privacidad y seguridad es importante para nosotros. Si tienes cualquier duda, contacta con soporte."
#: src/app/main/ui/workspace/plugins.cljs:271
msgid "workspace.plugins.permissions.library-read"
@ -5588,12 +5588,24 @@ msgstr "Comenzar descargas de ficheros."
#: src/app/main/ui/workspace/plugins.cljs:236
msgid "workspace.plugins.permissions.title"
msgstr "LA EXTENSIÓN SOLICITA PERMISO PARA ACCEDER:"
msgstr "LA EXTENSIÓN '%s' SOLICITA PERMISO PARA ACCEDER:"
#: src/app/main/ui/workspace/plugins.cljs:258
msgid "workspace.plugins.permissions.user-read"
msgstr "Leer la información del usuario actual."
msgid "workspace.plugins.try-out.title"
msgstr "¡LA EXTENSIÓN '%s' HA SIDO INSTALADA PARA TU USUARIO!"
msgid "workspace.plugins.try-out.message"
msgstr "¿Quieres echar un vistazo?. Crearemos un nuevo borrador en tu equipo actual. (Si no, puedes encontrar los plugins instalados en cualquier fichero.)"
msgid "workspace.plugins.try-out.cancel"
msgstr "AHORA NO"
msgid "workspace.plugins.try-out.try"
msgstr "PROBAR PLUGIN"
#: src/app/main/ui/workspace/plugins.cljs:192
msgid "workspace.plugins.plugin-list-link"
msgstr "Lista de extensiones"