Browse Source

feat(plugin): support configurable UI api for toolbar action items

charlie 4 years ago
parent
commit
e947acf7bd

+ 4 - 2
libs/src/LSPlugin.core.ts

@@ -9,6 +9,7 @@ import {
   invokeHostExportedApi,
   isObject, withFileProtocol
 } from './helpers'
+import * as pluginHelpers from './helpers'
 import Debug from 'debug'
 import {
   LSPluginCaller,
@@ -905,7 +906,7 @@ class LSPluginCore
 
     this.emit('beforeenable')
     p.settings?.set('disabled', false)
-    // this.emit('enabled', p)
+    this.emit('enabled', p.id)
   }
 
   async disable (plugin: PluginLocalIdentity) {
@@ -914,7 +915,7 @@ class LSPluginCore
 
     this.emit('beforedisable')
     p.settings?.set('disabled', true)
-    // this.emit('disabled', p)
+    this.emit('disabled', p.id)
   }
 
   async _hook (ns: string, type: string, payload?: any, pid?: string) {
@@ -1019,5 +1020,6 @@ function setupPluginCore (options: any) {
 
 export {
   PluginLocal,
+  pluginHelpers,
   setupPluginCore
 }

+ 5 - 0
libs/src/LSPlugin.ts

@@ -183,6 +183,11 @@ export interface IAppProxy {
   showMsg: (content: string, status?: 'success' | 'warning' | string) => void
   setZoomFactor: (factor: number) => void
 
+  registerUIItem: (
+    type: 'toolbar' | 'page',
+    opts: { key: string, template: string }
+  ) => boolean
+
   // events
   onCurrentGraphChanged: IUserHook
   onThemeModeChanged: IUserHook<{ mode: 'dark' | 'light' }>

+ 17 - 2
libs/src/LSPlugin.user.ts

@@ -25,7 +25,22 @@ declare global {
 
 const debug = Debug('LSPlugin:user')
 
-const app: Partial<IAppProxy> = {}
+const app: Partial<IAppProxy> = {
+  registerUIItem (
+    type: 'toolbar' | 'page',
+    opts: { key: string, template: string }
+  ) {
+    const pid = this.baseInfo.id
+    // opts.key = `${pid}_${opts.key}`
+
+    this.caller?.call(`api:call`, {
+      method: 'register-plugin-ui-item',
+      args: [pid, type, opts]
+    })
+
+    return false
+  }
+}
 
 let registeredCmdUid = 0
 
@@ -101,7 +116,7 @@ const editor: Partial<IEditorProxy> = {
       args: [this.baseInfo.id, [{ key, label, type }, ['editor/hook', eventKey]]]
     })
 
-    return false
+    return true
   }
 }
 

+ 4 - 0
src/main/frontend/components/header.cljs

@@ -11,6 +11,7 @@
             [frontend.context.i18n :as i18n]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.user :as user-handler]
+            [frontend.handler.plugin :as plugin-handler]
             [frontend.components.svg :as svg]
             [frontend.components.repo :as repo]
             [frontend.components.search :as search]
@@ -186,6 +187,9 @@
        (when-not (util/electron?)
          (login logged?))
 
+       (when plugin-handler/lsp-enabled?
+         (plugins/hook-ui-items :toolbar))
+
        (repo/sync-status current-repo)
 
        [:div.repos

+ 35 - 2
src/main/frontend/components/plugins.cljs

@@ -23,8 +23,8 @@
      (for [opt themes]
        (let [current-selected (= selected (:url opt))]
          [:div.it.flex.px-3.py-2.mb-2.rounded-sm.justify-between
-          {:key   (:url opt)
-           :class [(if current-selected "is-selected")]
+          {:key      (:url opt)
+           :class    [(if current-selected "is-selected")]
            :on-click #(do (js/LSPluginCore.selectTheme (if current-selected nil (clj->js opt)))
                           (state/set-modal! nil))}
           [:section
@@ -173,3 +173,36 @@
       [])
      [:div.lsp-hook-ui-slot
       (merge opts {:id id})])))
+
+(rum/defc ui-item-renderer
+  [pid type {:keys [key template]}]
+  (let [*el (rum/use-ref nil)
+        uni #(str "injected-ui-item-" %)
+        ^js pl (js/LSPluginCore.registeredPlugins.get (name pid))]
+
+    (rum/use-effect!
+     (fn []
+       (when-let [^js el (rum/deref *el)]
+         (js/LSPlugin.pluginHelpers.setupInjectedUI.call
+          pl #js {:slot (.-id el) :key key :template template} #js {})))
+     [])
+
+    (if-not (nil? pl)
+      [:div {:id    (uni (str (name key) "-" (name pid)))
+             :class (uni (name type))
+             :ref   *el}]
+      [:span])))
+
+(rum/defcs hook-ui-items < rum/reactive
+  "type
+      - :toolbar
+      - :page
+   "
+  [state type]
+  (when (state/sub [:plugin/installed-ui-items])
+    (let [items (state/get-plugins-ui-items-with-type type)]
+      (when (seq items)
+        [:div {:class     (str "ui-items-container")
+               :data-type (name type)}
+         (for [[_ {:keys [key template] :as opts} pid] items]
+           (rum/with-key (ui-item-renderer pid type opts) key))]))))

+ 12 - 1
src/main/frontend/components/plugins.css

@@ -140,7 +140,8 @@
     }
   }
 
-  &-details {}
+  &-details {
+  }
 }
 
 .cp__themes {
@@ -180,6 +181,16 @@
   }
 }
 
+.ui-items-container {
+  &[data-type=toolbar] {
+    @apply flex items-center mt-1 pl-2;
+
+    > .injected-ui-item-toolbar {
+      @apply px-2 opacity-50 hover:opacity-100 transition-opacity;
+    }
+  }
+}
+
 body {
   &[data-page=page] {
     .lsp-hook-ui-slot {

+ 1 - 1
src/main/frontend/components/repo.cljs

@@ -99,7 +99,7 @@
       (when-not (= repo config/local-repo)
         (if (and nfs-repo? (nfs-handler/supported?))
           (let [syncing? (state/sub :graph/syncing?)]
-            [:div.ml-3.mr-1.mt-1.opacity-60.refresh.hover:opacity-100 {:class (if syncing? "loader" "initial")}
+            [:div.ml-2.mr-1.mt-1.opacity-60.refresh.hover:opacity-100 {:class (if syncing? "loader" "initial")}
              [:a
               {:on-click #(nfs-handler/refresh! repo refresh-cb)
                :title (str "Import files from the local directory: " (config/get-local-dir repo) ".\nVersion: "

+ 20 - 1
src/main/frontend/handler/plugin.cljs

@@ -63,6 +63,18 @@
   [pid]
   (swap! state/state md/dissoc-in [:plugin/simple-commands (keyword pid)]))
 
+(defn register-plugin-ui-item
+  [pid {:keys [key type template] :as opts}]
+  (when-let [pid (keyword pid)]
+    (when (or true (contains? (:plugin/installed-plugins @state/state) pid))
+      (do (swap! state/state update-in [:plugin/installed-ui-items pid]
+                 (fnil conj []) [type opts pid])
+          true))))
+
+(defn unregister-plugin-ui-items
+  [pid]
+  (swap! state/state assoc-in [:plugin/installed-ui-items (keyword pid)] []))
+
 (defn update-plugin-settings
   [id settings]
   (swap! state/state update-in [:plugin/installed-plugins id] assoc :settings settings))
@@ -178,7 +190,14 @@
                                         ;; plugins
                                        (swap! state/state md/dissoc-in [:plugin/installed-plugins (keyword pid)])
                                         ;; commands
-                                       (unregister-plugin-slash-command pid))))
+                                       (unregister-plugin-slash-command pid)
+                                       (unregister-plugin-simple-command pid)
+                                       (unregister-plugin-ui-items pid))))
+
+               (.on "disabled" (fn [pid]
+                                 (unregister-plugin-slash-command pid)
+                                 (unregister-plugin-simple-command pid)
+                                 (unregister-plugin-ui-items pid)))
 
                (.on "theme-changed" (fn [^js themes]
                                       (swap! state/state assoc :plugin/installed-themes

+ 7 - 0
src/main/frontend/state.cljs

@@ -123,6 +123,7 @@
     :plugin/installed-plugins     {}
     :plugin/installed-themes      []
     :plugin/installed-commands    {}
+    :plugin/installed-ui-items    {}
     :plugin/simple-commands       {}
     :plugin/selected-theme        nil
     :plugin/selected-unpacked-pkg nil
@@ -1113,6 +1114,12 @@
   (filterv #(= (keyword (first %)) (keyword type))
            (apply concat (vals (:plugin/simple-commands @state)))))
 
+(defn get-plugins-ui-items-with-type
+  [type]
+  (filterv #(= (keyword (first %)) (keyword type))
+           (apply concat (vals (:plugin/installed-ui-items @state)))))
+
+
 (defn get-scheduled-future-days
   []
   (let [days (:scheduled/future-days (get-config))]

+ 6 - 0
src/main/logseq/api.cljs

@@ -153,6 +153,12 @@
       (plugin-handler/register-plugin-simple-command
        pid cmd (assoc action 0 (keyword (first action)))))))
 
+(def ^:export register_plugin_ui_item
+  (fn [pid type ^js opts]
+    (when-let [opts (bean/->clj opts)]
+      (plugin-handler/register-plugin-ui-item
+       pid (assoc opts :type type)))))
+
 ;; app
 (def ^:export relaunch
   (fn []