mirror of
https://github.com/logseq/logseq.git
synced 2026-05-04 19:06:21 +00:00
Feat/plugin marketplace (#2766)
* Squashed commit of the following: commitea9af272e4Author: Tienson Qin <tiensonqin@gmail.com> Date: Fri Aug 27 00:23:24 2021 +0800 feat: type c to git commit also, fixed an issue that backspace can delete selected blocks when there's a dialog. commit78e24f7479Author: Tienson Qin <tiensonqin@gmail.com> Date: Fri Aug 27 00:01:21 2021 +0800 fix: add patch parser worker to yarn watch commit7f6e777bcdAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Thu Aug 26 23:57:46 2021 +0800 fix: add several shortcuts to the Others category commit509697b276Author: Tienson Qin <tiensonqin@gmail.com> Date: Thu Aug 26 23:55:40 2021 +0800 fix: git username and email configuration * Squashed commit of the following: commit401d85be5fAuthor: Peng Xiao <pengxiao@outlook.com> Date: Fri Aug 27 11:10:32 2021 +0800 feat: add protobuf mode commitdc1e9fdfc9Author: Tienson Qin <tiensonqin@gmail.com> Date: Fri Aug 27 13:33:47 2021 +0800 chore: replace : with comma for git path ':' is a reserved character on Windows commitea9af272e4Author: Tienson Qin <tiensonqin@gmail.com> Date: Fri Aug 27 00:23:24 2021 +0800 feat: type c to git commit also, fixed an issue that backspace can delete selected blocks when there's a dialog. commit78e24f7479Author: Tienson Qin <tiensonqin@gmail.com> Date: Fri Aug 27 00:01:21 2021 +0800 fix: add patch parser worker to yarn watch commit7f6e777bcdAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Thu Aug 26 23:57:46 2021 +0800 fix: add several shortcuts to the Others category commit509697b276Author: Tienson Qin <tiensonqin@gmail.com> Date: Thu Aug 26 23:55:40 2021 +0800 fix: git username and email configuration * feat(plugin): ui of marketplace plugins list * improve(plugin): support reload * improve(plugin): installation from marketplace * fix conflicts * improve(plugin): installation from github public repo * chore: remove unwanted dependency * chore: remove console * improve(plugin): add shortcuts * ui(plugin): polish LOADING indicator * improve(plugin): support up-to-date of marketplace plugin * fix: remove debug option * improve(plugin): better interaction of themes picker * improve(plugin): better experience when installing theme from marketplace * fix(plugin): downloads label of marketplace plugin * improve(plugin): update package name field * improve(plugin): change marketplace packages repo to `logseq/marketplace` * fix(plugin): plugin title when updating notification * fix: conflicts * enhance(plugin): i18n related marketplace & lifecycle of plugin installation * improve(plugin): handle offline situation * ui(plugin): header plugin icons container * fix(ui): add class identity for journal page with date page name * improve(plugin): remote readme for marketplace plugin * enhance(plugin): polish plugin card * chore(plugin): build libs core * Squashed commit of the following: commit751db4828cAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Tue Sep 7 16:58:25 2021 +0800 enhance: log git errors commitc2dbbc77bfAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Tue Sep 7 16:27:00 2021 +0800 enhance: display refresh status commitf734b6db37Author: Tienson Qin <tiensonqin@gmail.com> Date: Tue Sep 7 16:03:57 2021 +0800 fix: .git doesn't work well with third-party cloud services commit7e44d81f1dAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Tue Sep 7 13:52:38 2021 +0800 fix: git init into the current graph folder instead of a separate git directory because .gitdir might has different paths on multiple devices, another reason is that the graph might have different histories considering the .git directory is not synced. commitb86a801514Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 23:23:27 2021 +0800 enhance: don't show diff if there's only blank changes commit0b55d119aaAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 22:39:06 2021 +0800 fix: save the previous content in Logseq first and commit it to avoid overwritten when syncing with iCloud/Dropbox/syncthing. commite0baf4b05cAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 22:18:45 2021 +0800 fix: close file watcher when exit the app commit10e7a9fbd6Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 21:05:38 2021 +0800 fix: disable cut selections in the query result block commit90c2bd7cc2Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 18:42:17 2021 +0800 fix: terminate parser and persist dbs when reloading the app (electron) commit571c81af30Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 17:57:33 2021 +0800 enhance: add sync from local files (the old refresh way) commita16e5c98baAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 17:23:57 2021 +0800 fix: Dragging blocks to update notes does not synchronize updates to the notes file in real time. close #2744 commit6897a22a3fAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 16:43:30 2021 +0800 fix: disable page/block auto-complete once the cursor went outside commitfeb4404874Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 13:28:47 2021 +0800 fix: wrong page metadata saved for another graph commitb96332122fAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 13:08:28 2021 +0800 enhance: catch errors when app quits commit1ee0c240c3Author: Jiang Hailong <gombiuda@gmail.com> Date: Wed Sep 1 20:54:13 2021 +0800 FIX: Linked reference is not refresh after file altering #2694 commit0550c8a876Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 12:25:39 2021 +0800 fix: display logbook for scheduled tasks commit2a5f0cee7cAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 12:21:19 2021 +0800 fix: spent hours for logbook commit1f2c9e4d3fAuthor: leizhe <lzhes43@gmail.com> Date: Sat Sep 4 12:59:09 2021 +0900 fix(timestamp): remove old SCHEDULED/DEADLINE timestamp When using `date-picker` to update the SCHEDULED/DEADLINE timestamp by clicking an existing one, logseq will add a new timestamp instead of updating the old one. This patch fixs this issue. commitcb23b967e4Author: leizhe <lzhes43@gmail.com> Date: Sat Sep 4 11:36:34 2021 +0900 fix(repeat): more consistent with orgmode style Ref: https://orgmode.org/manual/Tracking-your-habits.html commit229c7f2594Author: leizhe <lzhes43@gmail.com> Date: Sat Sep 4 15:11:07 2021 +0900 enhance(property): remove empty properties drawer commita76df9ce97Author: leizhe <lzhes43@gmail.com> Date: Sat Sep 4 10:27:30 2021 +0900 fix(clock): duplicate clock-in log twice 1. `set-marker` will not log time anymore. The time logging is moved to `with-timetracking` 2. Concat `logbook` only if `new-clocks` is nil, which fixs the duplication of clock-in log. commitc79e9f9e3eAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 12:04:16 2021 +0800 code: cleanup commit9ec85db09aAuthor: DarshanSudhakar <$K3Ug1i&> Date: Mon Sep 6 08:32:07 2021 +0530 Fixing typo for the tooltip 'Block reference' commiteec677873bAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 11:49:58 2021 +0800 enhance: page history support reverting back commit7273112a00Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 11:23:31 2021 +0800 git: revert back commitcd853b5864Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 10:39:08 2021 +0800 git: should compare ignored-files with disk content commita84dfb5effAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 10:31:16 2021 +0800 git: add ignore-files to avoid repeated notifications commit86577e7ebfAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 10:07:39 2021 +0800 fix: ignore permission denied error when git add all commit8dc0ca9ff5Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 09:54:03 2021 +0800 fix: run git config core.safecrlf false on windows commit9edaae559dAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 09:20:06 2021 +0800 refactor: extract file ops commitf12f58d3faAuthor: tiagodevezas <tiagodevezas@gmail.com> Date: Fri Sep 3 23:25:07 2021 +0100 fix typos commit9e82f0117cAuthor: tiagodevezas <tiagodevezas@gmail.com> Date: Fri Sep 3 21:32:03 2021 +0100 Translate shortcuts to Portuguese (pt-PT) commit9a2c17bb05Author: tiagodevezas <tiagodevezas@gmail.com> Date: Fri Sep 3 20:58:33 2021 +0100 Translate new settings to pt-PT commitc05034cc34Author: Sebastian Bensusan <sbensu@gmail.com> Date: Sat Sep 4 07:57:07 2021 -0700 feat(calc): Understand percentages commitbfe6a5d6cbAuthor: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 6 08:46:30 2021 +0800 enhance: commit the content in logseq when detecting any disk changes * improve(plugin): add install plugin api * fix(plugin): protected plugin installation api * improve(plugin): tweak readme display for local * fix: conflicts * fix(plugin): get block option with include children * improve(plugin): copy more marketplace manifest fields to plugin * fix: conflicts * improve(plugin): shortcut for copying plugin id * Squashed commit of the following: commite51ea54a75Author: Tienson Qin <tiensonqin@gmail.com> Date: Mon Sep 13 10:40:25 2021 +0800 fix: change ack timeout * fix(plugin): non blank icon string * fix: conflicts * fix: e name * fix: remove debug from state * chore(plugin): bump libs minor version Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
This commit is contained in:
@@ -1,65 +1,106 @@
|
||||
(ns frontend.components.plugins
|
||||
(:require [cljs-bean.core :as bean]
|
||||
[clojure.string :as string]
|
||||
(:require [rum.core :as rum]
|
||||
[frontend.state :as state]
|
||||
[cljs-bean.core :as bean]
|
||||
[frontend.context.i18n :as i18n]
|
||||
[frontend.ui :as ui]
|
||||
[frontend.util :as util]
|
||||
[frontend.mixins :as mixins]
|
||||
[electron.ipc :as ipc]
|
||||
[promesa.core :as p]
|
||||
[frontend.components.svg :as svg]
|
||||
[frontend.handler.notification :as notification]
|
||||
[frontend.handler.plugin :as plugin-handler]
|
||||
[frontend.state :as state]
|
||||
[frontend.ui :as ui]
|
||||
[frontend.util :as util]
|
||||
[promesa.core :as p]
|
||||
[rum.core :as rum]))
|
||||
[clojure.string :as string]))
|
||||
|
||||
(rum/defc installed-themes
|
||||
(rum/defcs installed-themes
|
||||
< rum/reactive
|
||||
[]
|
||||
(let [themes (state/sub :plugin/installed-themes)
|
||||
selected (state/sub :plugin/selected-theme)
|
||||
themes (cons {:name "Default Theme" :url nil :description "Logseq default light/dark theme."} themes)]
|
||||
(rum/local 0 ::cursor)
|
||||
(rum/local 0 ::total)
|
||||
(mixins/event-mixin
|
||||
(fn [state]
|
||||
(let [*cursor (::cursor state)
|
||||
*total (::total state)
|
||||
^js target (rum/dom-node state)]
|
||||
(.focus target)
|
||||
(mixins/on-key-down
|
||||
state {38 ;; up
|
||||
(fn [^js e]
|
||||
(reset! *cursor
|
||||
(if (zero? @*cursor)
|
||||
(dec @*total) (dec @*cursor))))
|
||||
40 ;; down
|
||||
(fn [^js e]
|
||||
(reset! *cursor
|
||||
(if (= @*cursor (dec @*total))
|
||||
0 (inc @*cursor))))
|
||||
|
||||
[:div.cp__themes-installed
|
||||
[:h2.mb-4.text-xl "Installed Themes"]
|
||||
(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 [(when current-selected "is-selected")]
|
||||
:on-click #(do (js/LSPluginCore.selectTheme (if current-selected nil (clj->js opt)))
|
||||
(state/set-modal! nil))}
|
||||
[:section
|
||||
[:strong.block (:name opt)]
|
||||
[:small.opacity-30 (:description opt)]]
|
||||
[:small.flex-shrink-0.flex.items-center.opacity-10
|
||||
(when current-selected "current")]]))]))
|
||||
13 ;; enter
|
||||
#(when-let [^js active (.querySelector target ".is-active")]
|
||||
(.click active))
|
||||
}))))
|
||||
[state]
|
||||
(let [*cursor (::cursor state)
|
||||
*total (::total state)
|
||||
themes (state/sub :plugin/installed-themes)
|
||||
selected (state/sub :plugin/selected-theme)
|
||||
themes (cons {:name "Default Theme" :url nil :description "Logseq default light/dark theme."} themes)
|
||||
themes (sort #(:selected %) (map #(assoc % :selected (= (:url %) selected)) themes))
|
||||
_ (reset! *total (count themes))]
|
||||
|
||||
(rum/with-context
|
||||
[[t] i18n/*tongue-context*]
|
||||
|
||||
[:div.cp__themes-installed
|
||||
{:tab-index -1}
|
||||
[:h1.mb-4.text-2xl.p-2 (t :themes)]
|
||||
(map-indexed
|
||||
(fn [idx opt]
|
||||
(let [current-selected (:selected opt)
|
||||
plg (get (:plugin/installed-plugins @state/state) (keyword (:pid opt)))]
|
||||
[:div.it.flex.px-3.py-1.5.rounded-sm.justify-between
|
||||
{:key (:url opt)
|
||||
:title (if current-selected "Cancel selected theme")
|
||||
:class (util/classnames
|
||||
[{:is-selected current-selected
|
||||
:is-active (= idx @*cursor)}])
|
||||
:on-click #(do (js/LSPluginCore.selectTheme (if current-selected nil (clj->js opt)))
|
||||
(state/set-modal! nil))}
|
||||
[:section
|
||||
[:strong.block (when plg (str (:name plg) " / ")) (:name opt)]
|
||||
[:small.opacity-30.italic (:description opt)]]
|
||||
[:small.flex-shrink-0.flex.items-center.opacity-10
|
||||
(if current-selected (svg/check 28))]]))
|
||||
themes)])))
|
||||
|
||||
(rum/defc unpacked-plugin-loader
|
||||
[unpacked-pkg-path]
|
||||
(rum/use-effect!
|
||||
(fn []
|
||||
(let [err-handle
|
||||
(fn [^js e]
|
||||
(case (keyword (aget e "name"))
|
||||
:IllegalPluginPackageError
|
||||
(notification/show! "Illegal Logseq plugin package." :error)
|
||||
:ExistedImportedPluginPackageError
|
||||
(notification/show! "Existed Imported plugin package." :error)
|
||||
:default)
|
||||
(plugin-handler/reset-unpacked-state))
|
||||
reg-handle #(plugin-handler/reset-unpacked-state)]
|
||||
(when unpacked-pkg-path
|
||||
(doto js/LSPluginCore
|
||||
(.once "error" err-handle)
|
||||
(.once "registered" reg-handle)
|
||||
(.register (bean/->js {:url unpacked-pkg-path}))))
|
||||
#(doto js/LSPluginCore
|
||||
(.off "error" err-handle)
|
||||
(.off "registered" reg-handle))))
|
||||
[unpacked-pkg-path])
|
||||
(fn []
|
||||
(let [err-handle
|
||||
(fn [^js e]
|
||||
(case (keyword (aget e "name"))
|
||||
:IllegalPluginPackageError
|
||||
(notification/show! "Illegal Logseq plugin package." :error)
|
||||
:ExistedImportedPluginPackageError
|
||||
(notification/show! "Existed Imported plugin package." :error)
|
||||
:default)
|
||||
(plugin-handler/reset-unpacked-state))
|
||||
reg-handle #(plugin-handler/reset-unpacked-state)]
|
||||
(when unpacked-pkg-path
|
||||
(doto js/LSPluginCore
|
||||
(.once "error" err-handle)
|
||||
(.once "registered" reg-handle)
|
||||
(.register (bean/->js {:url unpacked-pkg-path}))))
|
||||
#(doto js/LSPluginCore
|
||||
(.off "error" err-handle)
|
||||
(.off "registered" reg-handle))))
|
||||
[unpacked-pkg-path])
|
||||
|
||||
(when unpacked-pkg-path
|
||||
[:strong.inline-flex.px-3 "Loading ..."]))
|
||||
|
||||
(rum/defc simple-markdown-display
|
||||
(rum/defc local-markdown-display
|
||||
< rum/reactive
|
||||
[]
|
||||
(let [[content item] (state/sub :plugin/active-readme)]
|
||||
@@ -73,90 +114,240 @@
|
||||
(when-let [repo (:repository item)]
|
||||
(when-let [repo (if (string? repo) repo (:url repo))]
|
||||
[:div.p-4.rounded-md.bg-base-3
|
||||
[:strong [:a.flex.items-center {:target "_blank" :href repo} [:span.mr-1 (svg/github {:width 25 :height 25})] repo]]]))
|
||||
[:strong [:a.flex.items-center {:target "_blank" :href repo}
|
||||
[:span.mr-1 (svg/github {:width 25 :height 25})] repo]]]))
|
||||
[:div.p-1.bg-transparent.border-none.ls-block
|
||||
{:style {:min-height "60vw"
|
||||
:max-width 900}
|
||||
:dangerouslySetInnerHTML {:__html content}}]]))
|
||||
|
||||
(rum/defc plugin-item-card
|
||||
[{:keys [id name settings version url description author icon usf] :as item}]
|
||||
(let [disabled (:disabled settings)]
|
||||
[:div.cp__plugins-item-card
|
||||
[:div.l.link-block
|
||||
{:on-click #(plugin-handler/open-readme! url item simple-markdown-display)}
|
||||
(if icon
|
||||
[:img.icon {:src icon}]
|
||||
svg/folder)]
|
||||
[:div.r
|
||||
[:h3.head.text-xl.font-bold.pt-1.5
|
||||
{:on-click #(plugin-handler/open-readme! url item simple-markdown-display)}
|
||||
[:span name]
|
||||
[:sup.inline-block.px-1.text-xs.opacity-30 version]]
|
||||
[:div.desc.text-xs.opacity-60
|
||||
[:p description]
|
||||
;;[:small (js/JSON.stringify (bean/->js settings))]
|
||||
]
|
||||
[:div.flag
|
||||
[:p.text-xs.text-gray-300.pr-2.flex.justify-between.dark:opacity-40
|
||||
[:small author]
|
||||
[:small (str "ID: " id)]]]
|
||||
(rum/defc remote-readme-display
|
||||
[repo content]
|
||||
|
||||
[:div.ctl
|
||||
[:div.l
|
||||
[:div.de
|
||||
[:strong svg/settings-sm]
|
||||
[:ul.menu-list
|
||||
[:li {:on-click #(when usf (js/apis.openPath usf))} "Open settings"]
|
||||
[:li {:on-click #(js/apis.openPath url)} "Open plugin package"]
|
||||
[:li {:on-click
|
||||
#(let [confirm-fn
|
||||
(ui/make-confirm-modal
|
||||
{:title (str "Are you sure uninstall plugin [" name "] ?")
|
||||
:on-confirm (fn [_ {:keys [close-fn]}]
|
||||
(close-fn)
|
||||
(plugin-handler/unregister-plugin id))})]
|
||||
(state/set-modal! confirm-fn))}
|
||||
"Uninstall plugin"]]]]
|
||||
(let [src (str "lsp://logseq.com/marketplace.html?repo=" repo)]
|
||||
[:iframe.lsp-frame-readme {:src src}]))
|
||||
|
||||
[:div.flex.items-center
|
||||
[:small.de (if disabled "Disabled" "Enabled")]
|
||||
(ui/toggle (not disabled)
|
||||
(fn []
|
||||
(js-invoke js/LSPluginCore (if disabled "enable" "disable") id))
|
||||
true)]]]]))
|
||||
|
||||
(rum/defc installed-page
|
||||
< rum/reactive
|
||||
(defn security-warning
|
||||
[]
|
||||
(let [installed-plugins (state/sub :plugin/installed-plugins)
|
||||
selected-unpacked-pkg (state/sub :plugin/selected-unpacked-pkg)]
|
||||
[:div.cp__plugins-page-installed
|
||||
[:h1 "Installed Plugins"]
|
||||
(ui/admonition
|
||||
:warning
|
||||
[:div {:style {:max-width 700}}
|
||||
"Plugins can access your graph and your local files, issue network requests. They can also cause data corruption or loss. We're working on proper access rules for your graphs. Meanwhile, make sure you have regular backups of your graphs and only install the plugins when you can read and understand the source code."])
|
||||
[:hr]
|
||||
[:div.mb-6.flex.items-center.justify-between
|
||||
(ui/button
|
||||
"Load unpacked plugin"
|
||||
:intent "logseq"
|
||||
:on-click plugin-handler/load-unpacked-plugin)
|
||||
(unpacked-plugin-loader selected-unpacked-pkg)
|
||||
(when (util/electron?)
|
||||
(ui/button
|
||||
[:span.flex.items-center
|
||||
;;svg/settings-sm
|
||||
"Open plugin preferences file"]
|
||||
:intent "logseq"
|
||||
:on-click (fn []
|
||||
(p/let [root (plugin-handler/get-ls-dotdir-root)]
|
||||
(js/apis.openPath (str root "/preferences.json"))))))]
|
||||
(ui/admonition
|
||||
:warning
|
||||
[:div.max-w-4xl
|
||||
"Plugins can access your graph and your local files, issue network requests.
|
||||
They can also cause data corruption or loss. We're working on proper access rules for your graphs.
|
||||
Meanwhile, make sure you have regular backups of your graphs and only install the plugins when you can read and
|
||||
understand the source code."]))
|
||||
|
||||
[:div.cp__plugins-item-lists.grid-cols-1.md:grid-cols-2.lg:grid-cols-3
|
||||
(for [[_ item] installed-plugins]
|
||||
(rum/with-key (plugin-item-card item) (:id item)))]]))
|
||||
(rum/defc plugin-item-card < rum/static
|
||||
[{:keys [id name title settings version url description author icon usf iir repo] :as item}
|
||||
installing-or-updating? installed? stat]
|
||||
|
||||
(let [market? (and (not (nil? repo)) (nil? usf))
|
||||
disabled (:disabled settings)
|
||||
name (or title name "Untitled")]
|
||||
(rum/with-context
|
||||
[[t] i18n/*tongue-context*]
|
||||
|
||||
[:div.cp__plugins-item-card
|
||||
{:class (util/classnames [{:market market?}])}
|
||||
|
||||
[:div.l.link-block
|
||||
{:on-click #(plugin-handler/open-readme!
|
||||
url item (if repo remote-readme-display local-markdown-display))}
|
||||
(if (and icon (not (string/blank? icon)))
|
||||
[:img.icon {:src (if market? (plugin-handler/pkg-asset id icon) icon)}]
|
||||
svg/folder)
|
||||
|
||||
(when-not (or market? iir)
|
||||
[:span.flex.justify-center.text-xs.text-red-500.pt-2 "unpacked"])]
|
||||
|
||||
[:div.r
|
||||
[:h3.head.text-xl.font-bold.pt-1.5
|
||||
|
||||
[:span name]
|
||||
(if (not market?) [:sup.inline-block.px-1.text-xs.opacity-30 version])]
|
||||
|
||||
[:div.desc.text-xs.opacity-60
|
||||
[:p description]
|
||||
;;[:small (js/JSON.stringify (bean/->js settings))]
|
||||
]
|
||||
|
||||
[:div.flag
|
||||
[:p.text-xs.pr-2.flex.justify-between
|
||||
[:small author]
|
||||
[:small {:on-click #(do
|
||||
(notification/show! "Copied!" :success)
|
||||
(util/copy-to-clipboard! id))}
|
||||
(str "ID: " id)]]]
|
||||
|
||||
[:div.flag.is-top.opacity-50
|
||||
(if repo
|
||||
[:a.flex {:target "_blank"
|
||||
:href (plugin-handler/gh-repo-url repo)}
|
||||
(svg/github {:width 16 :height 16})])]
|
||||
|
||||
(if market?
|
||||
;; market ctls
|
||||
[:div.ctl
|
||||
[:ul.l.flex.items-center
|
||||
;; downloads
|
||||
[:li.flex.text-sm.items-center.pr-3 (svg/star 16) [:span.pl-1 (:stargazers_count stat)]]
|
||||
|
||||
;; stars
|
||||
(when-let [downloads (and stat (reduce (fn [a b] (+ a (get b 2))) 0 (:releases stat)))]
|
||||
(if (and downloads (> downloads 0))
|
||||
[:li.flex.text-sm.items-center.pr-3 (svg/cloud-down 16) [:span.pl-1 downloads]]))]
|
||||
|
||||
[:div.r.flex.items-center
|
||||
|
||||
[:a.btn
|
||||
{:class (util/classnames [{:disabled (or installed? installing-or-updating?)
|
||||
:installing installing-or-updating?}])
|
||||
:on-click #(plugin-handler/install-marketplace-plugin item)}
|
||||
(if installed?
|
||||
(t :plugin/installed)
|
||||
(if installing-or-updating?
|
||||
[:span.flex.items-center [:small svg/loading]
|
||||
(t :plugin/installing)]
|
||||
(t :plugin/install)))]]]
|
||||
|
||||
;; installed ctls
|
||||
[:div.ctl
|
||||
[:div.l
|
||||
[:div.de
|
||||
[:strong (svg/settings)]
|
||||
[:ul.menu-list
|
||||
[:li {:on-click #(if usf (js/apis.openPath usf))} (t :plugin/open-settings)]
|
||||
[:li {:on-click #(js/apis.openPath url)} (t :plugin/open-package)]
|
||||
[:li {:on-click
|
||||
#(let [confirm-fn
|
||||
(ui/make-confirm-modal
|
||||
{:title (str "Are you sure uninstall plugin [" name "] ?")
|
||||
:on-confirm (fn [_ {:keys [close-fn]}]
|
||||
(close-fn)
|
||||
(plugin-handler/unregister-plugin id))})]
|
||||
(state/set-modal! confirm-fn))}
|
||||
(t :plugin/uninstall)]]]]
|
||||
|
||||
[:div.r.flex.items-center
|
||||
(if (not disabled)
|
||||
[:a.btn
|
||||
{:on-click #(js-invoke js/LSPluginCore "reload" id)}
|
||||
(t :plugin/reload)])
|
||||
|
||||
(if iir
|
||||
[:a.btn
|
||||
{:class (util/classnames [{:disabled (or installing-or-updating?)
|
||||
:updating installing-or-updating?}])
|
||||
:on-click #(plugin-handler/update-marketplace-plugin
|
||||
item (fn [e] (notification/show! e :error)))}
|
||||
|
||||
(if installing-or-updating?
|
||||
(t :plugin/updating)
|
||||
(t :plugin/update))])
|
||||
|
||||
(ui/toggle (not disabled)
|
||||
(fn []
|
||||
(js-invoke js/LSPluginCore (if disabled "enable" "disable") id))
|
||||
true)]])]])))
|
||||
|
||||
(rum/defcs marketplace-plugins
|
||||
< rum/static rum/reactive
|
||||
(rum/local false ::fetching)
|
||||
(rum/local nil ::error)
|
||||
{:did-mount (fn [s]
|
||||
(reset! (::fetching s) true)
|
||||
(reset! (::error s) nil)
|
||||
(-> (plugin-handler/load-marketplace-plugins false)
|
||||
(p/then #(plugin-handler/load-marketplace-stats false))
|
||||
(p/catch #(do (js/console.error %) (reset! (::error s) %)))
|
||||
(p/finally #(reset! (::fetching s) false)))
|
||||
s)}
|
||||
[state]
|
||||
(let [pkgs (state/sub :plugin/marketplace-pkgs)
|
||||
stats (state/sub :plugin/marketplace-stats)
|
||||
installed-plugins (state/sub :plugin/installed-plugins)
|
||||
installing (state/sub :plugin/installing)
|
||||
online? (state/sub :network/online?)
|
||||
*fetching (::fetching state)
|
||||
*error (::error state)]
|
||||
|
||||
(cond
|
||||
(not online?)
|
||||
[:p.flex.justify-center.pt-20.opacity-50
|
||||
(svg/offline 30)]
|
||||
|
||||
@*fetching
|
||||
[:p.flex.justify-center.pt-20
|
||||
svg/loading]
|
||||
|
||||
@*error
|
||||
[:p.flex.justify-center.pt-20.opacity-50
|
||||
"Remote error: " (.-message @*error)]
|
||||
|
||||
:else
|
||||
[:div.cp__plugins-marketplace
|
||||
{:class (util/classnames [{:has-installing (boolean installing)}])}
|
||||
[:div.cp__plugins-item-lists.grid-cols-1.md:grid-cols-2.lg:grid-cols-3
|
||||
(for [item pkgs]
|
||||
(rum/with-key
|
||||
(let [pid (keyword (:id item))]
|
||||
(plugin-item-card
|
||||
item (and installing (= (keyword (:id installing)) pid))
|
||||
(contains? installed-plugins pid)
|
||||
(get stats pid)))
|
||||
(:id item)))]])))
|
||||
|
||||
(rum/defcs installed-plugins
|
||||
< rum/static rum/reactive
|
||||
[state]
|
||||
(let [installed-plugins (state/sub :plugin/installed-plugins)
|
||||
updating (state/sub :plugin/installing)
|
||||
selected-unpacked-pkg (state/sub :plugin/selected-unpacked-pkg)]
|
||||
(rum/with-context
|
||||
[[t] i18n/*tongue-context*]
|
||||
|
||||
[:div.cp__plugins-installed
|
||||
[:div.mb-4.flex.items-center.justify-between
|
||||
(ui/button
|
||||
(t :plugin/load-unpacked)
|
||||
:intent "logseq"
|
||||
:on-click plugin-handler/load-unpacked-plugin)
|
||||
(unpacked-plugin-loader selected-unpacked-pkg)
|
||||
|
||||
(when (util/electron?)
|
||||
[:div.flex.align-items
|
||||
(ui/button
|
||||
(t :plugin/open-preferences)
|
||||
:intent "logseq"
|
||||
:on-click (fn []
|
||||
(p/let [root (plugin-handler/get-ls-dotdir-root)]
|
||||
(js/apis.openPath (str root "/preferences.json")))))
|
||||
|
||||
(ui/button
|
||||
[:span.flex.items-center
|
||||
[:span.pr-1
|
||||
(ui/Tippy
|
||||
{:html [:small.inline-flex.py-2.pr-2
|
||||
{:style {:max-width "180px" :text-align "left" :justify-content "flex-start"}}
|
||||
(t :plugin/marketplace-tips)]
|
||||
:arrow true
|
||||
:distance 18
|
||||
:offset -25
|
||||
:theme "transparent"}
|
||||
(svg/info))]
|
||||
|
||||
(t :plugin/restart)]
|
||||
:class "ml-2"
|
||||
:intent "logseq"
|
||||
:on-click #(plugin-handler/invoke-exported-api "relaunch"))])]
|
||||
|
||||
[:div.cp__plugins-item-lists.grid-cols-1.md:grid-cols-2.lg:grid-cols-3
|
||||
(for [[_ item] installed-plugins]
|
||||
(rum/with-key
|
||||
(let [pid (keyword (:id item))]
|
||||
(plugin-item-card
|
||||
item (and updating (= (keyword (:id updating)) pid))
|
||||
true nil)) (:id item)))]])))
|
||||
|
||||
(defn open-select-theme!
|
||||
[]
|
||||
@@ -167,10 +358,10 @@
|
||||
([type payload opts]
|
||||
(let [id (str "slot__" (util/rand-str 8))]
|
||||
(rum/use-effect!
|
||||
(fn []
|
||||
(plugin-handler/hook-plugin-app type {:slot id :payload payload} nil)
|
||||
#())
|
||||
[])
|
||||
(fn []
|
||||
(plugin-handler/hook-plugin-app type {:slot id :payload payload} nil)
|
||||
#())
|
||||
[])
|
||||
[:div.lsp-hook-ui-slot
|
||||
(merge opts {:id id})])))
|
||||
|
||||
@@ -181,11 +372,11 @@
|
||||
^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 {})))
|
||||
[])
|
||||
(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)))
|
||||
@@ -194,10 +385,10 @@
|
||||
[:span])))
|
||||
|
||||
(rum/defcs hook-ui-items < rum/reactive
|
||||
"type
|
||||
- :toolbar
|
||||
- :pagebar
|
||||
"
|
||||
"type
|
||||
- :toolbar
|
||||
- :pagebar
|
||||
"
|
||||
[state type]
|
||||
(when (state/sub [:plugin/installed-ui-items])
|
||||
(let [items (state/get-plugins-ui-items-with-type type)]
|
||||
@@ -206,3 +397,32 @@
|
||||
:data-type (name type)}
|
||||
(for [[_ {:keys [key template] :as opts} pid] items]
|
||||
(rum/with-key (ui-item-renderer pid type opts) key))]))))
|
||||
|
||||
(rum/defc plugins-page
|
||||
[]
|
||||
|
||||
(let [[active set-active!] (rum/use-state :installed)
|
||||
market? (= active :marketplace)]
|
||||
|
||||
(rum/with-context
|
||||
[[t] i18n/*tongue-context*]
|
||||
|
||||
[:div.cp__plugins-page
|
||||
[:h1 (t :plugins)]
|
||||
(security-warning)
|
||||
[:hr]
|
||||
|
||||
[:div.tabs.flex.items-center.justify-center
|
||||
[:div.tabs-inner.flex.items-center
|
||||
(ui/button [:span.it (t :plugin/installed)]
|
||||
:on-click #(set-active! :installed)
|
||||
:intent "logseq" :class (if-not market? "active" ""))
|
||||
|
||||
(ui/button [:span.mk (svg/apps 16) (t :plugin/marketplace)]
|
||||
:on-click #(set-active! :marketplace)
|
||||
:intent "logseq" :class (if market? "active" ""))]]
|
||||
|
||||
[:div.panels
|
||||
(if market?
|
||||
(marketplace-plugins)
|
||||
(installed-plugins))]])))
|
||||
|
||||
Reference in New Issue
Block a user