Feat/plugin marketplace (#2766)

* Squashed commit of the following:

commit ea9af272e4
Author: 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.

commit 78e24f7479
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Fri Aug 27 00:01:21 2021 +0800

    fix: add patch parser worker to yarn watch

commit 7f6e777bcd
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Thu Aug 26 23:57:46 2021 +0800

    fix: add several shortcuts to the Others category

commit 509697b276
Author: 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:

commit 401d85be5f
Author: Peng Xiao <pengxiao@outlook.com>
Date:   Fri Aug 27 11:10:32 2021 +0800

    feat: add protobuf mode

commit dc1e9fdfc9
Author: 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

commit ea9af272e4
Author: 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.

commit 78e24f7479
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Fri Aug 27 00:01:21 2021 +0800

    fix: add patch parser worker to yarn watch

commit 7f6e777bcd
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Thu Aug 26 23:57:46 2021 +0800

    fix: add several shortcuts to the Others category

commit 509697b276
Author: 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:

commit 751db4828c
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Tue Sep 7 16:58:25 2021 +0800

    enhance: log git errors

commit c2dbbc77bf
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Tue Sep 7 16:27:00 2021 +0800

    enhance: display refresh status

commit f734b6db37
Author: 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

commit 7e44d81f1d
Author: 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.

commit b86a801514
Author: 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

commit 0b55d119aa
Author: 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.

commit e0baf4b05c
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Mon Sep 6 22:18:45 2021 +0800

    fix: close file watcher when exit the app

commit 10e7a9fbd6
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Mon Sep 6 21:05:38 2021 +0800

    fix: disable cut selections in the query result block

commit 90c2bd7cc2
Author: 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)

commit 571c81af30
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Mon Sep 6 17:57:33 2021 +0800

    enhance: add sync from local files (the old refresh way)

commit a16e5c98ba
Author: 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

commit 6897a22a3f
Author: 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

commit feb4404874
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Mon Sep 6 13:28:47 2021 +0800

    fix: wrong page metadata saved for another graph

commit b96332122f
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Mon Sep 6 13:08:28 2021 +0800

    enhance: catch errors when app quits

commit 1ee0c240c3
Author: Jiang Hailong <gombiuda@gmail.com>
Date:   Wed Sep 1 20:54:13 2021 +0800

    FIX: Linked reference is not refresh after file altering #2694

commit 0550c8a876
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Mon Sep 6 12:25:39 2021 +0800

    fix: display logbook for scheduled tasks

commit 2a5f0cee7c
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Mon Sep 6 12:21:19 2021 +0800

    fix: spent hours for logbook

commit 1f2c9e4d3f
Author: 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.

commit cb23b967e4
Author: 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

commit 229c7f2594
Author: leizhe <lzhes43@gmail.com>
Date:   Sat Sep 4 15:11:07 2021 +0900

    enhance(property): remove empty properties drawer

commit a76df9ce97
Author: 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.

commit c79e9f9e3e
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Mon Sep 6 12:04:16 2021 +0800

    code: cleanup

commit 9ec85db09a
Author: DarshanSudhakar <$K3Ug1i&>
Date:   Mon Sep 6 08:32:07 2021 +0530

    Fixing typo  for the tooltip 'Block reference'

commit eec677873b
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Mon Sep 6 11:49:58 2021 +0800

    enhance: page history support reverting back

commit 7273112a00
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Mon Sep 6 11:23:31 2021 +0800

    git: revert back

commit cd853b5864
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Mon Sep 6 10:39:08 2021 +0800

    git: should compare ignored-files with disk content

commit a84dfb5eff
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Mon Sep 6 10:31:16 2021 +0800

    git: add ignore-files to avoid repeated notifications

commit 86577e7ebf
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Mon Sep 6 10:07:39 2021 +0800

    fix: ignore permission denied error when git add all

commit 8dc0ca9ff5
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Mon Sep 6 09:54:03 2021 +0800

    fix: run git config core.safecrlf false on windows

commit 9edaae559d
Author: Tienson Qin <tiensonqin@gmail.com>
Date:   Mon Sep 6 09:20:06 2021 +0800

    refactor: extract file ops

commit f12f58d3fa
Author: tiagodevezas <tiagodevezas@gmail.com>
Date:   Fri Sep 3 23:25:07 2021 +0100

    fix typos

commit 9e82f0117c
Author: tiagodevezas <tiagodevezas@gmail.com>
Date:   Fri Sep 3 21:32:03 2021 +0100

    Translate shortcuts to Portuguese (pt-PT)

commit 9a2c17bb05
Author: tiagodevezas <tiagodevezas@gmail.com>
Date:   Fri Sep 3 20:58:33 2021 +0100

    Translate new settings to pt-PT

commit c05034cc34
Author: Sebastian Bensusan <sbensu@gmail.com>
Date:   Sat Sep 4 07:57:07 2021 -0700

    feat(calc): Understand percentages

commit bfe6a5d6cb
Author: 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:

commit e51ea54a75
Author: 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:
Charlie
2021-09-13 19:49:51 +08:00
committed by GitHub
parent 790ba856a3
commit 18b7a7864b
36 changed files with 8314 additions and 373 deletions

View File

@@ -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))]])))