mirror of
https://github.com/logseq/logseq.git
synced 2026-05-28 14:39:48 +00:00
Merge branch 'feat/db' into fix/multiple-tabs
This commit is contained in:
4
deps/db/src/logseq/db/common/sqlite.cljs
vendored
4
deps/db/src/logseq/db/common/sqlite.cljs
vendored
@@ -330,9 +330,7 @@
|
||||
views
|
||||
all-files
|
||||
pages-datoms))]
|
||||
(prn :debug "db-graph?" db-graph?)
|
||||
(prn :debug "block/journal-day schema" (str (:block/journal-day (:schema db))))
|
||||
{:schema (assoc schema :block/journal-day {:db/index true})
|
||||
{:schema schema
|
||||
:initial-data data}))
|
||||
|
||||
(defn restore-initial-data
|
||||
|
||||
3
deps/db/src/logseq/db/file_based/schema.cljs
vendored
3
deps/db/src/logseq/db/file_based/schema.cljs
vendored
@@ -45,7 +45,8 @@
|
||||
|
||||
;; for pages
|
||||
:block/alias {:db/valueType :db.type/ref
|
||||
:db/cardinality :db.cardinality/many}
|
||||
:db/cardinality :db.cardinality/many
|
||||
:db/index true}
|
||||
|
||||
;; todo keywords, e.g. "TODO", "DOING", "DONE"
|
||||
:block/marker {}
|
||||
|
||||
1
deps/shui/src/logseq/shui/ui.cljs
vendored
1
deps/shui/src/logseq/shui/ui.cljs
vendored
@@ -53,6 +53,7 @@
|
||||
(def tooltip-portal (util/lsui-wrap "TooltipPortal"))
|
||||
(def tooltip-content (util/lsui-wrap "TooltipContent"))
|
||||
(def tooltip-provider (util/lsui-wrap "TooltipProvider"))
|
||||
(def tooltip-arrow (util/lsui-wrap "TooltipArrow"))
|
||||
|
||||
(def card (util/lsui-wrap "Card"))
|
||||
(def card-header (util/lsui-wrap "CardHeader"))
|
||||
|
||||
@@ -17,10 +17,9 @@
|
||||
"del": "^6.0.0",
|
||||
"glob": "9.0.0",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-replace": "^1.1.4",
|
||||
"gulp-postcss": "^10.0.0",
|
||||
"gulp-replace": "^1.1.4",
|
||||
"ip": "1.1.9",
|
||||
"semver": "7.5.2",
|
||||
"karma": "^6.4.4",
|
||||
"karma-chrome-launcher": "^3.2.0",
|
||||
"karma-cljs-test": "^0.1.0",
|
||||
@@ -33,6 +32,7 @@
|
||||
"postcss-import-ext-glob": "2.0.1",
|
||||
"postcss-nested": "6.0.0",
|
||||
"purgecss": "4.0.2",
|
||||
"semver": "7.5.2",
|
||||
"shadow-cljs": "2.26.0",
|
||||
"stylelint": "^13.8.0",
|
||||
"stylelint-config-standard": "^20.0.0",
|
||||
@@ -161,7 +161,6 @@
|
||||
"react-intersection-observer": "^9.3.5",
|
||||
"react-resize-context": "3.0.0",
|
||||
"react-textarea-autosize": "8.3.3",
|
||||
"react-tippy": "1.4.0",
|
||||
"react-transition-group": "4.3.0",
|
||||
"react-virtuoso": "4.12.5",
|
||||
"remove-accents": "0.4.2",
|
||||
|
||||
@@ -241,19 +241,16 @@
|
||||
(if db-based?
|
||||
(db-based-statuses)
|
||||
(file-based-statuses))
|
||||
(mapv (fn [m]
|
||||
(let [command (if db-based?
|
||||
[:div.flex.flex-row.items-center.gap-2 m [:div.text-xs.opacity-50 "Status"]]
|
||||
m)
|
||||
icon (if db-based?
|
||||
(case m
|
||||
(mapv (fn [command]
|
||||
(let [icon (if db-based?
|
||||
(case command
|
||||
"Canceled" "Cancelled"
|
||||
"Doing" "InProgress50"
|
||||
m)
|
||||
command)
|
||||
"square-asterisk")]
|
||||
[command (->marker m) (str "Set status to " m) icon]))))]
|
||||
[command (->marker command) (str "Set status to " command) icon]))))]
|
||||
(when (seq result)
|
||||
(map (fn [v] (conj v "TASK")) result))))
|
||||
(map (fn [v] (conj v "TASK STATUS")) result))))
|
||||
|
||||
(defn file-based-priorities
|
||||
[]
|
||||
@@ -273,17 +270,17 @@
|
||||
(db-based-priorities)
|
||||
(file-based-priorities))
|
||||
(mapv (fn [item]
|
||||
(let [command (if db-based?
|
||||
[:div.flex.flex-row.items-center.gap-2 item [:div.text-xs.opacity-50 "Priority"]]
|
||||
item)]
|
||||
[command (->priority item) (str "Set priority to " item)
|
||||
(let [command item]
|
||||
[command
|
||||
(->priority item)
|
||||
(str "Set priority to " item)
|
||||
(if db-based?
|
||||
(str "priorityLvl" item)
|
||||
(str "circle-letter-" (util/safe-lower-case item)))])))
|
||||
(with-no-priority)
|
||||
(vec))]
|
||||
(when (seq result)
|
||||
(map (fn [v] (conj v "PRIORITY")) result))))
|
||||
(map (fn [v] (into v ["PRIORITY"])) result))))
|
||||
|
||||
;; Credits to roamresearch.com
|
||||
|
||||
@@ -297,7 +294,7 @@
|
||||
[]
|
||||
(mapv (fn [level]
|
||||
(let [heading (str "Heading " level)]
|
||||
[heading (->heading level) heading (str "h-" level)])) (range 1 7)))
|
||||
[heading (->heading level) heading (str "h-" level) "Heading"])) (range 1 7)))
|
||||
|
||||
(defonce *matched-commands (atom nil))
|
||||
(defonce *initial-commands (atom nil))
|
||||
|
||||
@@ -1068,7 +1068,7 @@
|
||||
(contains? config/video-formats asset-type))))
|
||||
|
||||
(declare block-positioned-properties)
|
||||
(rum/defc page-reference < rum/reactive
|
||||
(rum/defc page-reference < rum/reactive db-mixins/query
|
||||
"Component for page reference"
|
||||
[html-export? uuid-or-title* {:keys [nested-link? show-brackets? id] :as config} label]
|
||||
(when uuid-or-title*
|
||||
@@ -1077,7 +1077,8 @@
|
||||
uuid-or-title*)
|
||||
show-brackets? (if (some? show-brackets?) show-brackets? (state/show-brackets?))
|
||||
contents-page? (= "contents" (string/lower-case (str id)))
|
||||
block (db/get-page uuid-or-title)
|
||||
block* (db/get-page uuid-or-title)
|
||||
block (or (some-> (:db/id block*) db/sub-block) block*)
|
||||
config' (assoc config
|
||||
:label (mldoc/plain->text label)
|
||||
:contents-page? contents-page?
|
||||
|
||||
@@ -782,19 +782,20 @@
|
||||
(rum/defc new-block-mode < rum/reactive
|
||||
[]
|
||||
(when (state/sub [:document/mode?])
|
||||
(ui/tippy {:html [:div.p-2
|
||||
[:p.mb-2 [:b "Document mode"]]
|
||||
[:ul
|
||||
[:li
|
||||
[:div.inline-block.mr-1 (ui/render-keyboard-shortcut (shortcut-dh/gen-shortcut-seq :editor/new-line))]
|
||||
[:p.inline-block "to create new block"]]
|
||||
[:li
|
||||
[:p.inline-block.mr-1 "Click `D` or type"]
|
||||
[:div.inline-block.mr-1 (ui/render-keyboard-shortcut (shortcut-dh/gen-shortcut-seq :ui/toggle-document-mode))]
|
||||
[:p.inline-block "to toggle document mode"]]]]}
|
||||
[:a.block.px-1.text-sm.font-medium.bg-base-2.rounded-md.mx-2
|
||||
{:on-click state/toggle-document-mode!}
|
||||
"D"])))
|
||||
(ui/tooltip
|
||||
[:a.block.px-1.text-sm.font-medium.bg-base-2.rounded-md.mx-2
|
||||
{:on-click state/toggle-document-mode!}
|
||||
"D"]
|
||||
[:div.p-2
|
||||
[:p.mb-2 [:b "Document mode"]]
|
||||
[:ul
|
||||
[:li
|
||||
[:div.inline-block.mr-1 (ui/render-keyboard-shortcut (shortcut-dh/gen-shortcut-seq :editor/new-line))]
|
||||
[:p.inline-block "to create new block"]]
|
||||
[:li
|
||||
[:p.inline-block.mr-1 "Click `D` or type"]
|
||||
[:div.inline-block.mr-1 (ui/render-keyboard-shortcut (shortcut-dh/gen-shortcut-seq :ui/toggle-document-mode))]
|
||||
[:p.inline-block "to toggle document mode"]]]])))
|
||||
|
||||
(def help-menu-items
|
||||
[{:title "Handbook" :icon "book-2" :on-click #(handbooks/toggle-handbooks)}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
(or
|
||||
(= "Add new property" (first item))
|
||||
(when (= (count item) 5)
|
||||
(contains? #{"TASK" "PRIORITY"} (last item))))) commands)
|
||||
(contains? #{"TASK STATUS" "PRIORITY"} (last item))))) commands)
|
||||
commands))
|
||||
|
||||
(rum/defcs commands < rum/reactive
|
||||
@@ -56,63 +56,59 @@
|
||||
_ (when (state/get-editor-action)
|
||||
(reset! *matched matched'))
|
||||
page? (db/page? (db/entity (:db/id (state/get-edit-block))))
|
||||
matched (filter-commands page? @*matched)]
|
||||
matched (filter-commands page? @*matched)
|
||||
filtered? (not= matched @commands/*initial-commands)]
|
||||
(ui/auto-complete
|
||||
matched
|
||||
{:get-group-name
|
||||
(fn [item]
|
||||
(when (= (count item) 5) (last item)))
|
||||
(cond->
|
||||
{:item-render
|
||||
(fn [item]
|
||||
(let [command-name (first item)
|
||||
command-doc (get item 2)
|
||||
plugin-id (get-in item [1 1 1 :pid])
|
||||
doc (when (state/show-command-doc?) command-doc)
|
||||
options (some-> item (get 3))
|
||||
icon-name (some-> (if (map? options) (:icon options) options) (name))
|
||||
command-name (if icon-name
|
||||
[:span.flex.items-center.gap-1
|
||||
(shui/tabler-icon icon-name)
|
||||
[:strong.font-normal command-name]]
|
||||
command-name)]
|
||||
(cond
|
||||
(or plugin-id (vector? doc))
|
||||
[:div.has-help
|
||||
{:title plugin-id}
|
||||
command-name
|
||||
(when doc (ui/tooltip [:small (svg/help-circle)] doc))]
|
||||
|
||||
:item-render
|
||||
(fn [item]
|
||||
(let [command-name (first item)
|
||||
command-doc (get item 2)
|
||||
plugin-id (get-in item [1 1 1 :pid])
|
||||
doc (when (state/show-command-doc?) command-doc)
|
||||
options (some-> item (get 3))
|
||||
icon-name (some-> (if (map? options) (:icon options) options) (name))
|
||||
command-name (if icon-name
|
||||
[:span.flex.items-center.gap-1
|
||||
(shui/tabler-icon icon-name)
|
||||
[:strong.font-normal command-name]]
|
||||
command-name)]
|
||||
(cond
|
||||
(or plugin-id (vector? doc))
|
||||
[:div.has-help
|
||||
{:title plugin-id}
|
||||
command-name
|
||||
(when doc (ui/tippy
|
||||
{:html doc
|
||||
:interactive true
|
||||
:fixed-position? true
|
||||
:position "right"}
|
||||
(string? doc)
|
||||
[:div {:title doc}
|
||||
command-name]
|
||||
|
||||
[:small (svg/help-circle)]))]
|
||||
:else
|
||||
[:div command-name])))
|
||||
|
||||
(string? doc)
|
||||
[:div {:title doc}
|
||||
command-name]
|
||||
|
||||
:else
|
||||
[:div command-name])))
|
||||
|
||||
:on-chosen
|
||||
(fn [chosen-item]
|
||||
(let [command (first chosen-item)]
|
||||
(reset! commands/*current-command command)
|
||||
(let [command-steps (get (into {} matched) command)
|
||||
restore-slash? (or
|
||||
(contains? #{"Today" "Yesterday" "Tomorrow" "Current time"} command)
|
||||
(and
|
||||
(not (fn? command-steps))
|
||||
(not (contains? (set (map first command-steps)) :editor/input))
|
||||
(not (contains? #{"Date picker" "Template" "Deadline" "Scheduled" "Upload an image"} command))))]
|
||||
(editor-handler/insert-command! id command-steps
|
||||
format
|
||||
{:restore? restore-slash?
|
||||
:command command}))))
|
||||
:class
|
||||
"cp__commands-slash"})))
|
||||
:on-chosen
|
||||
(fn [chosen-item]
|
||||
(let [command (first chosen-item)]
|
||||
(reset! commands/*current-command command)
|
||||
(let [command-steps (get (into {} matched) command)
|
||||
restore-slash? (or
|
||||
(contains? #{"Today" "Yesterday" "Tomorrow" "Current time"} command)
|
||||
(and
|
||||
(not (fn? command-steps))
|
||||
(not (contains? (set (map first command-steps)) :editor/input))
|
||||
(not (contains? #{"Date picker" "Template" "Deadline" "Scheduled" "Upload an image"} command))))]
|
||||
(editor-handler/insert-command! id command-steps
|
||||
format
|
||||
{:restore? restore-slash?
|
||||
:command command}))))
|
||||
:class
|
||||
"cp__commands-slash"}
|
||||
(not filtered?)
|
||||
(assoc :get-group-name
|
||||
(fn [item]
|
||||
(when (= (count item) 5) (last item))))))))
|
||||
|
||||
(defn- page-on-chosen-handler
|
||||
[embed? input id q pos format]
|
||||
|
||||
@@ -81,10 +81,9 @@
|
||||
(defn priority-cp
|
||||
[{:block/keys [pre-block? priority] :as block}]
|
||||
(when (and (not pre-block?) priority)
|
||||
(ui/tippy
|
||||
{:interactive true
|
||||
:html (set-priority block priority)}
|
||||
(priority-text priority))))
|
||||
(ui/tooltip
|
||||
(priority-text priority)
|
||||
(set-priority block priority))))
|
||||
|
||||
(defn clock-summary-cp
|
||||
[block body]
|
||||
@@ -96,22 +95,20 @@
|
||||
(not= summary "0m")
|
||||
(not (string/blank? summary)))
|
||||
[:div {:style {:max-width 100}}
|
||||
(ui/tippy {:html (fn []
|
||||
(when-let [logbook (drawer/get-logbook body)]
|
||||
(let [clocks (->> (last logbook)
|
||||
(filter #(string/starts-with? % "CLOCK:"))
|
||||
(remove string/blank?))]
|
||||
[:div.p-4
|
||||
[:div.font-bold.mb-2 "LOGBOOK:"]
|
||||
[:ul
|
||||
(for [clock (take 10 (reverse clocks))]
|
||||
[:li clock])]])))
|
||||
:interactive true
|
||||
:in-editor? true
|
||||
:delay [1000, 100]}
|
||||
[:div.text-sm.time-spent.ml-1 {:style {:padding-top 3}}
|
||||
[:a.fade-link
|
||||
summary]])]))))
|
||||
(ui/tooltip
|
||||
[:div.text-sm.time-spent.ml-1 {:style {:padding-top 3}}
|
||||
[:a.fade-link
|
||||
summary]]
|
||||
|
||||
(when-let [logbook (drawer/get-logbook body)]
|
||||
(let [clocks (->> (last logbook)
|
||||
(filter #(string/starts-with? % "CLOCK:"))
|
||||
(remove string/blank?))]
|
||||
[:div.p-4
|
||||
[:div.font-bold.mb-2 "LOGBOOK:"]
|
||||
[:ul
|
||||
(for [clock (take 10 (reverse clocks))]
|
||||
[:li clock])]])))]))))
|
||||
|
||||
(rum/defc timestamp-editor
|
||||
[ast *show-datapicker?]
|
||||
|
||||
@@ -10,22 +10,17 @@
|
||||
|
||||
(rum/defc query-refresh-button
|
||||
[query-time {:keys [on-pointer-down full-text-search?]}]
|
||||
(ui/tippy
|
||||
{:html [:div
|
||||
[:p
|
||||
(if full-text-search?
|
||||
[:span "Full-text search results will not be refreshed automatically."]
|
||||
[:span (str "This query takes " (int query-time) "ms to finish, it's a bit slow so that auto refresh is disabled.")])]
|
||||
[:p
|
||||
"Click the refresh button instead if you want to see the latest result."]]
|
||||
:interactive true
|
||||
:popperOptions {:modifiers {:preventOverflow
|
||||
{:enabled true
|
||||
:boundariesElement "viewport"}}}
|
||||
:arrow true}
|
||||
[:a.fade-link.flex
|
||||
{:on-pointer-down on-pointer-down}
|
||||
(ui/icon "refresh" {:style {:font-size 20}})]))
|
||||
(ui/tooltip
|
||||
[:a.fade-link.flex
|
||||
{:on-pointer-down on-pointer-down}
|
||||
(ui/icon "refresh" {:style {:font-size 20}})]
|
||||
[:div
|
||||
[:p
|
||||
(if full-text-search?
|
||||
[:span "Full-text search results will not be refreshed automatically."]
|
||||
[:span (str "This query takes " (int query-time) "ms to finish, it's a bit slow so that auto refresh is disabled.")])]
|
||||
[:p
|
||||
"Click the refresh button instead if you want to see the latest result."]]))
|
||||
|
||||
;; Custom query header only used by file graphs
|
||||
(rum/defc custom-query-header
|
||||
|
||||
@@ -908,30 +908,35 @@
|
||||
(reset! *excluded-pages? value)
|
||||
(set-setting! :excluded-pages? value)))
|
||||
true)]]
|
||||
|
||||
(when (config/db-based-graph? (state/get-current-repo))
|
||||
[:div.flex.flex-col.mb-2
|
||||
[:p "Created before"]
|
||||
(when created-at-filter
|
||||
[:div (.toDateString (js/Date. (+ created-at-filter (get-in graph [:all-pages :created-at-min]))))])
|
||||
(ui/tippy {:html [:div.pr-3 (str (js/Date. (+ created-at-filter (get-in graph [:all-pages :created-at-min]))))]}
|
||||
;; Slider keeps track off the range from min created-at to max created-at
|
||||
;; because there were bugs with setting min and max directly
|
||||
(ui/slider created-at-filter
|
||||
{:min 0
|
||||
:max (- (get-in graph [:all-pages :created-at-max])
|
||||
(get-in graph [:all-pages :created-at-min]))
|
||||
:on-change #(do
|
||||
(reset! *created-at-filter (int %))
|
||||
(set-setting! :created-at-filter (int %)))}))])
|
||||
|
||||
(ui/tooltip
|
||||
;; Slider keeps track off the range from min created-at to max created-at
|
||||
;; because there were bugs with setting min and max directly
|
||||
(ui/slider created-at-filter
|
||||
{:min 0
|
||||
:max (- (get-in graph [:all-pages :created-at-max])
|
||||
(get-in graph [:all-pages :created-at-min]))
|
||||
:on-change #(do
|
||||
(reset! *created-at-filter (int %))
|
||||
(set-setting! :created-at-filter (int %)))})
|
||||
[:div.px-1 (str (js/Date. (+ created-at-filter (get-in graph [:all-pages :created-at-min]))))])])
|
||||
|
||||
(when (seq focus-nodes)
|
||||
[:div.flex.flex-col.mb-2
|
||||
[:p {:title "N hops from selected nodes"}
|
||||
"N hops from selected nodes"]
|
||||
(ui/tippy {:html [:div.pr-3 n-hops]}
|
||||
(ui/slider (or n-hops 10)
|
||||
{:min 1
|
||||
:max 10
|
||||
:on-change #(reset! *n-hops (int %))}))])
|
||||
(ui/tooltip
|
||||
(ui/slider (or n-hops 10)
|
||||
{:min 1
|
||||
:max 10
|
||||
:on-change #(reset! *n-hops (int %))})
|
||||
[:div n-hops])])
|
||||
|
||||
[:a.opacity-70.opacity-100 {:on-click (fn []
|
||||
(swap! *graph-reset? not)
|
||||
@@ -977,39 +982,45 @@
|
||||
[:div.flex.flex-col.mb-2
|
||||
[:p {:title "Link Distance"}
|
||||
"Link Distance"]
|
||||
(ui/tippy {:html [:div.pr-3 link-dist]}
|
||||
(ui/slider (/ link-dist 10)
|
||||
{:min 1 ;; 10
|
||||
:max 18 ;; 180
|
||||
:on-change #(let [value (int %)]
|
||||
(reset! *link-dist (* value 10))
|
||||
(set-forcesetting! :link-dist (* value 10)))}))]
|
||||
(ui/tooltip
|
||||
(ui/slider (/ link-dist 10)
|
||||
{:min 1 ;; 10
|
||||
:max 18 ;; 180
|
||||
:on-change #(let [value (int %)]
|
||||
(reset! *link-dist (* value 10))
|
||||
(set-forcesetting! :link-dist (* value 10)))})
|
||||
[:div link-dist])]
|
||||
|
||||
[:div.flex.flex-col.mb-2
|
||||
[:p {:title "Charge Strength"}
|
||||
"Charge Strength"]
|
||||
(ui/tippy {:html [:div.pr-3 charge-strength]}
|
||||
(ui/slider (/ charge-strength 100)
|
||||
{:min -10 ;;-1000
|
||||
:max 10 ;;1000
|
||||
:on-change #(let [value (int %)]
|
||||
(reset! *charge-strength (* value 100))
|
||||
(set-forcesetting! :charge-strength (* value 100)))}))]
|
||||
(ui/tooltip
|
||||
(ui/slider (/ charge-strength 100)
|
||||
{:min -10 ;;-1000
|
||||
:max 10 ;;1000
|
||||
:on-change #(let [value (int %)]
|
||||
(reset! *charge-strength (* value 100))
|
||||
(set-forcesetting! :charge-strength (* value 100)))})
|
||||
[:div charge-strength])]
|
||||
|
||||
[:div.flex.flex-col.mb-2
|
||||
[:p {:title "Charge Range"}
|
||||
"Charge Range"]
|
||||
(ui/tippy {:html [:div.pr-3 charge-range]}
|
||||
(ui/slider (/ charge-range 100)
|
||||
{:min 5 ;;500
|
||||
:max 40 ;;4000
|
||||
:on-change #(let [value (int %)]
|
||||
(reset! *charge-range (* value 100))
|
||||
(set-forcesetting! :charge-range (* value 100)))}))]
|
||||
(ui/tooltip
|
||||
(ui/slider (/ charge-range 100)
|
||||
{:min 5 ;;500
|
||||
:max 40 ;;4000
|
||||
:on-change #(let [value (int %)]
|
||||
(reset! *charge-range (* value 100))
|
||||
(set-forcesetting! :charge-range (* value 100)))})
|
||||
[:div charge-range])]
|
||||
|
||||
[:a.opacity-70.opacity-100 {:on-click (fn []
|
||||
(swap! *graph-forcereset? not)
|
||||
(reset! *link-dist 70)
|
||||
(reset! *charge-strength -600)
|
||||
(reset! *charge-range 600))}
|
||||
[:a
|
||||
{:on-click (fn []
|
||||
(swap! *graph-forcereset? not)
|
||||
(reset! *link-dist 70)
|
||||
(reset! *charge-strength -600)
|
||||
(reset! *charge-range 600))}
|
||||
"Reset Forces"]]]))
|
||||
{})
|
||||
(graph-filter-section
|
||||
|
||||
@@ -585,14 +585,14 @@
|
||||
|
||||
(when (and develop-mode? (util/electron?) (not market?))
|
||||
[:div
|
||||
(ui/tippy {:html [:div (t :plugin/unpacked-tips)]
|
||||
:arrow true}
|
||||
(ui/button
|
||||
(t :plugin/load-unpacked)
|
||||
{:icon "upload"
|
||||
:intent "link"
|
||||
:class "load-unpacked"
|
||||
:on-click plugin-handler/load-unpacked-plugin}))
|
||||
(ui/tooltip
|
||||
(ui/button
|
||||
(t :plugin/load-unpacked)
|
||||
{:icon "upload"
|
||||
:intent "link"
|
||||
:class "load-unpacked"
|
||||
:on-click plugin-handler/load-unpacked-plugin})
|
||||
[:div (t :plugin/unpacked-tips)])
|
||||
|
||||
(when (util/electron?)
|
||||
(unpacked-plugin-loader selected-unpacked-pkg))])]
|
||||
@@ -1075,9 +1075,7 @@
|
||||
|
||||
[:div.px-4
|
||||
(when-not (string/blank? notes)
|
||||
(ui/tippy
|
||||
{:html [:p notes]}
|
||||
[:span.opacity-30.hover:opacity-80 (ui/icon "info-circle")]))]])]
|
||||
(ui/tooltip [:span.opacity-30.hover:opacity-80 (ui/icon "info-circle")] [:p notes]))]])]
|
||||
|
||||
;; all done
|
||||
[:div.py-4 [:strong.text-4xl (str "\uD83C\uDF89 " (t :plugin/all-updated))]])
|
||||
|
||||
@@ -145,11 +145,8 @@
|
||||
(when (= "Enter" (.-key e))
|
||||
(util/stop-propagation e)))} label)))))
|
||||
(when show-type-change-hints?
|
||||
(ui/tippy {:html "Changing the property type clears some property configurations."
|
||||
:class "tippy-hover ml-2"
|
||||
:interactive true
|
||||
:disabled false}
|
||||
(svg/info)))]))
|
||||
(ui/tooltip (svg/info)
|
||||
[:span "Changing the property type clears some property configurations."]))]))
|
||||
|
||||
(rum/defc property-select
|
||||
[exclude-properties select-opts]
|
||||
|
||||
@@ -601,19 +601,17 @@
|
||||
(dropdown-editor-menuitem {:icon :letter-t
|
||||
:title "Property type"
|
||||
:desc (if disabled?'
|
||||
(ui/tippy {:html [:div.w-96
|
||||
"The type of this property is locked once you start using it. This is to make sure all your existing information stays correct if the property type is changed later. To unlock, all uses of a property must be deleted."]
|
||||
:class "tippy-hover ml-2"
|
||||
:interactive true
|
||||
:disabled false}
|
||||
(str property-type-label'))
|
||||
(ui/tooltip
|
||||
[:span (str property-type-label')]
|
||||
[:div.w-96
|
||||
"The type of this property is locked once you start using it. This is to make sure all your existing information stays correct if the property type is changed later. To unlock, all uses of a property must be deleted."])
|
||||
(str property-type-label'))
|
||||
:disabled? disabled?'
|
||||
:submenu-content (fn [ops]
|
||||
(property-type-sub-pane property ops))}))
|
||||
|
||||
(when (and (= property-type :node)
|
||||
(not (contains? #{:logseq.property/parent} (:db/ident property))))
|
||||
(not (contains? #{:logseq.property/parent} (:db/ident property))))
|
||||
(dropdown-editor-menuitem {:icon :hash
|
||||
:disabled? disabled?
|
||||
:title "Specify node tags"
|
||||
|
||||
@@ -381,13 +381,7 @@
|
||||
:let [active? (= color color-accent)
|
||||
none? (= color :none)]]
|
||||
[:div.flex.items-center
|
||||
(ui/tippy
|
||||
{:html (case color
|
||||
:none [:p {:style {:max-width "300px"}}
|
||||
"Cancel accent color. This is currently in beta stage and mainly used for compatibility with custom themes."]
|
||||
:logseq "Logseq classical color"
|
||||
(str (name color) " color"))
|
||||
:delay [1000, 100]}
|
||||
(ui/tooltip
|
||||
(shui/button
|
||||
{:class "w-5 h-5 px-1 rounded-full flex justify-center items-center transition ease-in duration-100 hover:cursor-pointer hover:opacity-100"
|
||||
:auto-focus (and _in-modal? active?)
|
||||
@@ -402,7 +396,13 @@
|
||||
{:class (if none? "h-0.5 w-full bg-red-700"
|
||||
"w-2 h-2 rounded-full transition ease-in duration-100")
|
||||
:style {:background-color (if-not none? (str "var(--rx-" (name color) "-07)") "")
|
||||
:opacity (if (or none? active?) 1 0)}}]))])]]
|
||||
:opacity (if (or none? active?) 1 0)}}])
|
||||
|
||||
(case color
|
||||
:none [:p {:style {:max-width "300px"}}
|
||||
"Cancel accent color. This is currently in beta stage and mainly used for compatibility with custom themes."]
|
||||
:logseq "Logseq classical color"
|
||||
(str (name color) " color")))])]]
|
||||
|
||||
[:div
|
||||
(row-with-button-action
|
||||
@@ -431,11 +431,8 @@
|
||||
{:for "custom_date_format"}
|
||||
(t :settings-page/custom-date-format)
|
||||
(when-not (config/db-based-graph? (state/get-current-repo))
|
||||
(ui/tippy {:html (t :settings-page/custom-date-format-warning)
|
||||
:class "tippy-hover ml-2"
|
||||
:interactive true
|
||||
:disabled false}
|
||||
(svg/info)))]
|
||||
(ui/tooltip [:span.flex.px-2 (svg/info)]
|
||||
[:span (t :settings-page/custom-date-format-warning)]))]
|
||||
[:div.mt-1.sm:mt-0.sm:col-span-2
|
||||
[:div.max-w-lg.rounded-md
|
||||
[:select.form-select.is-small
|
||||
@@ -484,11 +481,8 @@
|
||||
(defn outdenting-row [t logical-outdenting?]
|
||||
(toggle "preferred_outdenting"
|
||||
[(t :settings-page/preferred-outdenting)
|
||||
(ui/tippy {:html (outdenting-hint)
|
||||
:class "tippy-hover ml-2"
|
||||
:interactive true
|
||||
:disabled false}
|
||||
(svg/info))]
|
||||
(ui/tooltip [:span.flex.px-2 (svg/info)]
|
||||
(outdenting-hint) {:content-props {:side "right"}})]
|
||||
logical-outdenting?
|
||||
config-handler/toggle-logical-outdenting!))
|
||||
|
||||
@@ -501,22 +495,16 @@
|
||||
(defn preferred-pasting-file [t preferred-pasting-file?]
|
||||
(toggle "preferred_pasting_file"
|
||||
[(t :settings-page/preferred-pasting-file)
|
||||
(ui/tippy {:html (t :settings-page/preferred-pasting-file-hint)
|
||||
:class "tippy-hover ml-2"
|
||||
:interactive true
|
||||
:disabled false}
|
||||
(svg/info))]
|
||||
(ui/tooltip [:span.flex.px-2 (svg/info)]
|
||||
[:span.block.w-64 (t :settings-page/preferred-pasting-file-hint)])]
|
||||
preferred-pasting-file?
|
||||
config-handler/toggle-preferred-pasting-file!))
|
||||
|
||||
(defn auto-expand-row [t auto-expand-block-refs?]
|
||||
(toggle "auto_expand_block_refs"
|
||||
[(t :settings-page/auto-expand-block-refs)
|
||||
(ui/tippy {:html (auto-expand-hint)
|
||||
:class "tippy-hover ml-2"
|
||||
:interactive true
|
||||
:disabled false}
|
||||
(svg/info))]
|
||||
(ui/tooltip [:span.flex.px-2 (svg/info)]
|
||||
(auto-expand-hint))]
|
||||
auto-expand-block-refs?
|
||||
config-handler/toggle-auto-expand-block-refs!))
|
||||
|
||||
@@ -866,26 +854,23 @@
|
||||
(row-with-button-action
|
||||
{:left-label (str (t :settings-page/sync-diff-merge) " (Experimental!)") ;; Not included in i18n to avoid outdating translations
|
||||
:action (sync-diff-merge-enabled-switcher enabled?)
|
||||
:desc (ui/tippy {:html [:div
|
||||
[:div (t :settings-page/sync-diff-merge-desc)]
|
||||
[:div (t :settings-page/sync-diff-merge-warn)]]
|
||||
:class "tippy-hover ml-2"
|
||||
:interactive true
|
||||
:disabled false}
|
||||
(svg/info))}))
|
||||
:desc (ui/tooltip [:span.inline-flex.px-1 (svg/info)]
|
||||
[:div
|
||||
[:div (t :settings-page/sync-diff-merge-desc)]
|
||||
[:div (t :settings-page/sync-diff-merge-warn)]])}))
|
||||
|
||||
(rum/defc rtc-enabled-switcher
|
||||
[enabled?]
|
||||
(ui/toggle enabled?
|
||||
(fn []
|
||||
(let [value (not enabled?)]
|
||||
(state/set-rtc-enabled! value)))
|
||||
true))
|
||||
(fn []
|
||||
(let [value (not enabled?)]
|
||||
(state/set-rtc-enabled! value)))
|
||||
true))
|
||||
|
||||
(defn rtc-switcher-row [enabled?]
|
||||
(row-with-button-action
|
||||
{:left-label "RTC"
|
||||
:action (rtc-enabled-switcher enabled?)}))
|
||||
{:left-label "RTC"
|
||||
:action (rtc-enabled-switcher enabled?)}))
|
||||
|
||||
(rum/defc whiteboards-enabled-switcher
|
||||
[enabled?]
|
||||
|
||||
@@ -488,16 +488,17 @@
|
||||
:on-click #(score-and-next-card 5 card card-index finished? phase review-records cb)})])
|
||||
|
||||
(when preview?
|
||||
(ui/tippy {:html [:div.text-sm
|
||||
(t :flashcards/modal-btn-reset-tip)]
|
||||
:class "tippy-hover"
|
||||
:interactive true}
|
||||
(ui/button [:span (t :flashcards/modal-btn-reset)]
|
||||
:id "card-reset"
|
||||
:class (util/hiccup->class "opacity-60.hover:opacity-100.card-reset")
|
||||
:on-click (fn [e]
|
||||
(util/stop e)
|
||||
(operation-reset! card)))))]
|
||||
(ui/tooltip
|
||||
(ui/button [:span (t :flashcards/modal-btn-reset)]
|
||||
:id "card-reset"
|
||||
:class (util/hiccup->class "opacity-60.hover:opacity-100.card-reset")
|
||||
:on-click (fn [e]
|
||||
(util/stop e)
|
||||
(operation-reset! card)))
|
||||
|
||||
[:div.text-sm
|
||||
(t :flashcards/modal-btn-reset-tip)]
|
||||
{:trigger-props {:as-child false}}))]
|
||||
[:div.my-3 (ui/button "Review cards" :small? true)])]))))
|
||||
|
||||
(rum/defc view-modal <
|
||||
@@ -629,27 +630,20 @@
|
||||
|
||||
;; FIXME: CSS issue
|
||||
(if @*preview-mode?
|
||||
(ui/tippy {:html [:div.text-sm (t :flashcards/modal-current-total)]
|
||||
:interactive true}
|
||||
[:div.opacity-60.text-sm.mr-2
|
||||
@*card-index
|
||||
[:span "/"]
|
||||
total])
|
||||
(ui/tippy {:html [:div.text-sm (t :flashcards/modal-overdue-total)]
|
||||
;; :class "tippy-hover"
|
||||
:interactive true}
|
||||
[:div.opacity-60.text-sm.mr-2
|
||||
(max 0 (- filtered-total @*card-index))
|
||||
[:span "/"]
|
||||
total]))
|
||||
|
||||
(ui/tippy
|
||||
{:html [:div.text-sm (t :flashcards/modal-toggle-preview-mode)]
|
||||
:delay [1000, 100]
|
||||
:class "tippy-hover"
|
||||
:interactive true
|
||||
:disabled false}
|
||||
(ui/tooltip
|
||||
[:div.opacity-60.text-sm.mr-2
|
||||
@*card-index
|
||||
[:span "/"]
|
||||
total]
|
||||
[:div.text-sm (t :flashcards/modal-current-total)])
|
||||
(ui/tooltip
|
||||
[:div.opacity-60.text-sm.mr-2
|
||||
(max 0 (- filtered-total @*card-index))
|
||||
[:span "/"]
|
||||
total]
|
||||
[:div.text-sm (t :flashcards/modal-overdue-total)]))
|
||||
|
||||
(ui/tooltip
|
||||
(ui/button
|
||||
(merge
|
||||
{:icon "letter-a"
|
||||
@@ -661,13 +655,11 @@
|
||||
:button-props {:id "preview-all-cards"}
|
||||
:small? true}
|
||||
(when @*preview-mode?
|
||||
{:icon-props {:style {:color "var(--ls-button-background)"}}}))))
|
||||
{:icon-props {:style {:color "var(--ls-button-background)"}}})))
|
||||
[:div.text-sm (t :flashcards/modal-toggle-preview-mode)]
|
||||
{:trigger-props {:as-child false}})
|
||||
|
||||
(ui/tippy
|
||||
{:html [:div.text-sm (t :flashcards/modal-toggle-random-mode)]
|
||||
:delay [1000, 100]
|
||||
:class "tippy-hover"
|
||||
:interactive true}
|
||||
(ui/tooltip
|
||||
(ui/button
|
||||
(merge
|
||||
{:icon "arrows-shuffle"
|
||||
@@ -677,7 +669,9 @@
|
||||
(swap! *random-mode? not))
|
||||
:small? true}
|
||||
(when @*random-mode?
|
||||
{:icon-props {:style {:color "var(--ls-button-background)"}}}))))]]
|
||||
{:icon-props {:style {:color "var(--ls-button-background)"}}})))
|
||||
[:div.text-sm (t :flashcards/modal-toggle-random-mode)]
|
||||
{:trigger-props {:as-child false}})]]
|
||||
[:div.px-1
|
||||
(when (and (not modal?) (not @*preview-mode?))
|
||||
{:on-click (fn []
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
[frontend.error :as error]
|
||||
[frontend.handler.command-palette :as command-palette]
|
||||
[frontend.handler.events :as events]
|
||||
[frontend.handler.events.ui]
|
||||
[frontend.handler.file-based.events]
|
||||
[frontend.handler.file-based.file :as file-handler]
|
||||
[frontend.handler.global-config :as global-config-handler]
|
||||
|
||||
@@ -818,7 +818,7 @@
|
||||
concat-prev-block?
|
||||
(let [children (:block/_parent (db/entity (:db/id block)))
|
||||
db-based? (config/db-based-graph? repo)
|
||||
prev-block-is-not-parent? (not= (:block/uuid (:block/parent block)) (:block/uuid prev-block))
|
||||
prev-block-is-not-parent? (empty? (:block/_parent prev-block))
|
||||
delete-prev-block? (and db-based?
|
||||
prev-block-is-not-parent?
|
||||
(empty? (:block/tags block))
|
||||
|
||||
@@ -10,24 +10,7 @@
|
||||
[clojure.core.async.interop :refer [p->c]]
|
||||
[clojure.string :as string]
|
||||
[frontend.commands :as commands]
|
||||
[frontend.components.block :as block]
|
||||
[frontend.components.cmdk.core :as cmdk]
|
||||
[frontend.components.diff :as diff]
|
||||
[frontend.components.encryption :as encryption]
|
||||
[frontend.components.file-based.git :as git-component]
|
||||
[frontend.components.file-sync :as file-sync]
|
||||
[frontend.components.page :as component-page]
|
||||
[frontend.components.plugins :as plugin]
|
||||
[frontend.components.property.dialog :as property-dialog]
|
||||
[frontend.components.repo :as repo]
|
||||
[frontend.components.select :as select]
|
||||
[frontend.components.selection :as selection]
|
||||
[frontend.components.settings :as settings]
|
||||
[frontend.components.shell :as shell]
|
||||
[frontend.components.user.login :as login]
|
||||
[frontend.components.whiteboard :as whiteboard]
|
||||
[frontend.config :as config]
|
||||
[frontend.context.i18n :refer [t]]
|
||||
[frontend.date :as date]
|
||||
[frontend.db :as db]
|
||||
[frontend.db.async :as db-async]
|
||||
@@ -36,10 +19,7 @@
|
||||
[frontend.db.persist :as db-persist]
|
||||
[frontend.db.transact :as db-transact]
|
||||
[frontend.extensions.fsrs :as fsrs]
|
||||
[frontend.extensions.srs :as srs]
|
||||
[frontend.fs :as fs]
|
||||
[frontend.fs.capacitor-fs :as capacitor-fs]
|
||||
[frontend.fs.nfs :as nfs]
|
||||
[frontend.fs.sync :as sync]
|
||||
[frontend.fs.watcher-handler :as fs-watcher]
|
||||
[frontend.handler.assets :as assets-handler]
|
||||
@@ -50,8 +30,6 @@
|
||||
[frontend.handler.db-based.rtc-flows :as rtc-flows]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
[frontend.handler.export :as export]
|
||||
[frontend.handler.file-based.file :as file-handler]
|
||||
[frontend.handler.file-based.nfs :as nfs-handler]
|
||||
[frontend.handler.file-sync :as file-sync-handler]
|
||||
[frontend.handler.graph :as graph-handler]
|
||||
[frontend.handler.notification :as notification]
|
||||
@@ -63,12 +41,9 @@
|
||||
[frontend.handler.search :as search-handler]
|
||||
[frontend.handler.shell :as shell-handler]
|
||||
[frontend.handler.ui :as ui-handler]
|
||||
[frontend.handler.user :as user-handler]
|
||||
[frontend.mobile.core :as mobile]
|
||||
[frontend.mobile.graph-picker :as graph-picker]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.modules.instrumentation.posthog :as posthog]
|
||||
[frontend.modules.instrumentation.sentry :as sentry-event]
|
||||
[frontend.modules.outliner.pipeline :as pipeline]
|
||||
[frontend.modules.outliner.ui :as ui-outliner-tx]
|
||||
[frontend.modules.shortcut.core :as st]
|
||||
@@ -76,16 +51,11 @@
|
||||
[frontend.quick-capture :as quick-capture]
|
||||
[frontend.search :as search]
|
||||
[frontend.state :as state]
|
||||
[frontend.ui :as ui]
|
||||
[frontend.util :as util]
|
||||
[frontend.util.persist-var :as persist-var]
|
||||
[goog.dom :as gdom]
|
||||
[lambdaisland.glogi :as log]
|
||||
[logseq.common.config :as common-config]
|
||||
[logseq.common.util :as common-util]
|
||||
[logseq.shui.ui :as shui]
|
||||
[promesa.core :as p]
|
||||
[rum.core :as rum]))
|
||||
[promesa.core :as p]))
|
||||
|
||||
;; TODO: should we move all events here?
|
||||
|
||||
@@ -96,54 +66,10 @@
|
||||
(async/<! (sync/<sync-stop))
|
||||
(some-> (sync/<sync-start) async/<!)))
|
||||
|
||||
(defn- file-sync-stop! []
|
||||
(defn file-sync-stop! []
|
||||
(async/go (async/<! (p->c (persist-var/load-vars)))
|
||||
(async/<! (sync/<sync-stop))))
|
||||
|
||||
(defn- enable-beta-features!
|
||||
[]
|
||||
(when-not (false? (state/enable-sync?)) ; user turns it off
|
||||
(file-sync-handler/set-sync-enabled! true)))
|
||||
|
||||
(defmethod handle :user/fetch-info-and-graphs [[_]]
|
||||
(state/set-state! [:ui/loading? :login] false)
|
||||
(async/go
|
||||
(let [result (async/<! (sync/<user-info sync/remoteapi))]
|
||||
(cond
|
||||
(instance? ExceptionInfo result)
|
||||
nil
|
||||
(map? result)
|
||||
(do
|
||||
(state/set-user-info! result)
|
||||
(when-let [uid (user-handler/user-uuid)]
|
||||
(sentry-event/set-user! uid))
|
||||
(let [status (if (user-handler/alpha-or-beta-user?) :welcome :unavailable)]
|
||||
(when (and (= status :welcome) (user-handler/logged-in?))
|
||||
(enable-beta-features!)
|
||||
(async/<! (p->c (rtc-handler/<get-remote-graphs)))
|
||||
(async/<! (file-sync-handler/load-session-graphs))
|
||||
(p/let [repos (repo-handler/refresh-repos!)]
|
||||
(when-let [repo (state/get-current-repo)]
|
||||
(when (some #(and (= (:url %) repo)
|
||||
(vector? (:sync-meta %))
|
||||
(util/uuid-string? (first (:sync-meta %)))
|
||||
(util/uuid-string? (second (:sync-meta %)))) repos)
|
||||
(sync/<sync-start)))))
|
||||
(file-sync/maybe-onboarding-show status)))))))
|
||||
|
||||
(defmethod handle :user/logout [[_]]
|
||||
(file-sync-handler/reset-session-graphs)
|
||||
(sync/remove-all-pwd!)
|
||||
(file-sync-handler/reset-user-state!)
|
||||
(login/sign-out!))
|
||||
|
||||
(defmethod handle :user/login [[_ host-ui?]]
|
||||
(if (or host-ui? (not util/electron?))
|
||||
(js/window.open config/LOGIN-URL)
|
||||
(if (mobile-util/native-platform?)
|
||||
(route-handler/redirect! {:to :user-login})
|
||||
(login/open-login-modal!))))
|
||||
|
||||
(defmethod handle :graph/added [[_ repo {:keys [empty-graph?]}]]
|
||||
(search-handler/rebuild-indices!)
|
||||
(plugin-handler/hook-plugin-app :graph-after-indexed {:repo repo :empty-graph? empty-graph?})
|
||||
@@ -215,45 +141,6 @@
|
||||
:warning))
|
||||
(graph-switch-on-persisted graph opts))))
|
||||
|
||||
(defmethod handle :graph/pull-down-remote-graph [[_ graph dir-name]]
|
||||
(if (mobile-util/native-ios?)
|
||||
(when-let [graph-name (or dir-name (:GraphName graph))]
|
||||
(let [graph-name (util/safe-sanitize-file-name graph-name)]
|
||||
(if (string/blank? graph-name)
|
||||
(notification/show! "Illegal graph folder name.")
|
||||
|
||||
;; Create graph directory under Logseq document folder (local)
|
||||
(when-let [root (state/get-local-container-root-url)]
|
||||
(let [graph-path (graph-picker/validate-graph-dirname root graph-name)]
|
||||
(->
|
||||
(p/let [exists? (fs/dir-exists? graph-path)]
|
||||
(let [overwrite? (if exists?
|
||||
(js/confirm (str "There's already a directory with the name \"" graph-name "\", do you want to overwrite it? Make sure to backup it first if you're not sure about it."))
|
||||
true)]
|
||||
(if overwrite?
|
||||
(p/let [_ (fs/mkdir-if-not-exists graph-path)]
|
||||
(nfs-handler/ls-dir-files-with-path!
|
||||
graph-path
|
||||
{:ok-handler (fn []
|
||||
(file-sync-handler/init-remote-graph graph-path graph)
|
||||
(js/setTimeout (fn [] (repo-handler/refresh-repos!)) 200))}))
|
||||
(let [graph-name (-> (js/prompt "Please specify a new directory name to download the graph:")
|
||||
str
|
||||
string/trim)]
|
||||
(when-not (string/blank? graph-name)
|
||||
(state/pub-event! [:graph/pull-down-remote-graph graph graph-name]))))))
|
||||
(p/catch (fn [^js e]
|
||||
(notification/show! (str e) :error)
|
||||
(js/console.error e)))))))))
|
||||
(when (:GraphName graph)
|
||||
(shui/dialog-open!
|
||||
(file-sync/pick-dest-to-sync-panel graph)))))
|
||||
|
||||
(defmethod handle :graph/pick-page-histories [[_ graph-uuid page-name]]
|
||||
(shui/dialog-open!
|
||||
(file-sync/pick-page-histories-panel graph-uuid page-name)
|
||||
{:id :page-histories :label "modal-page-histories"}))
|
||||
|
||||
(defmethod handle :graph/open-new-window [[_ev target-repo]]
|
||||
(p/let [current-repo (state/get-current-repo)]
|
||||
(ui-handler/open-new-window-or-tab! current-repo target-repo)))
|
||||
@@ -261,67 +148,6 @@
|
||||
(defmethod handle :graph/migrated [[_ _repo]]
|
||||
(js/alert "Graph migrated."))
|
||||
|
||||
(defn get-local-repo
|
||||
[]
|
||||
(when-let [repo (state/get-current-repo)]
|
||||
(when (config/local-file-based-graph? repo)
|
||||
repo)))
|
||||
|
||||
(defn ask-permission
|
||||
[repo]
|
||||
(when
|
||||
(and (not (util/electron?))
|
||||
(not (mobile-util/native-platform?)))
|
||||
(fn [{:keys [close]}]
|
||||
[:div
|
||||
;; TODO: fn translation with args
|
||||
[:p
|
||||
"Grant native filesystem permission for directory: "
|
||||
[:b (config/get-local-dir repo)]]
|
||||
(ui/button
|
||||
(t :settings-permission/start-granting)
|
||||
:class "ui__modal-enter"
|
||||
:on-click (fn []
|
||||
(nfs/check-directory-permission! repo)
|
||||
(close)))])))
|
||||
|
||||
(defmethod handle :modal/nfs-ask-permission []
|
||||
(when-let [repo (get-local-repo)]
|
||||
(some-> (ask-permission repo)
|
||||
(shui/dialog-open! {:align :top}))))
|
||||
|
||||
(defmethod handle :modal/show-cards [[_ cards-id]]
|
||||
(let [db-based? (config/db-based-graph? (state/get-current-repo))]
|
||||
(shui/dialog-open!
|
||||
(if db-based? (fn [] (fsrs/cards-view cards-id)) srs/global-cards)
|
||||
{:id :srs
|
||||
:label "flashcards__cp"})))
|
||||
|
||||
(defmethod handle :modal/show-instruction [_]
|
||||
(shui/dialog-open!
|
||||
capacitor-fs/instruction
|
||||
{:id :instruction
|
||||
:label "instruction__cp"}))
|
||||
|
||||
(defmethod handle :modal/show-themes-modal [[_ classic?]]
|
||||
(if classic?
|
||||
(plugin/open-select-theme!)
|
||||
(route-handler/go-to-search! :themes)))
|
||||
|
||||
(defmethod handle :ui/toggle-appearance [_]
|
||||
(let [popup-id "appearance_settings"]
|
||||
(if (gdom/getElement popup-id)
|
||||
(shui/popup-hide! popup-id)
|
||||
(shui/popup-show!
|
||||
(js/document.querySelector ".toolbar-dots-btn")
|
||||
(fn []
|
||||
(settings/appearance))
|
||||
{:id popup-id
|
||||
:align :end}))))
|
||||
|
||||
(defmethod handle :modal/set-git-username-and-email [[_ _content]]
|
||||
(shui/dialog-open! git-component/set-git-username-and-email))
|
||||
|
||||
(defmethod handle :page/create [[_ page-name opts]]
|
||||
(if (= page-name (date/today))
|
||||
(page-handler/create-today-journal!)
|
||||
@@ -333,26 +159,10 @@
|
||||
(defmethod handle :page/renamed [[_ repo data]]
|
||||
(page-common-handler/after-page-renamed! repo data))
|
||||
|
||||
(defmethod handle :page/show-delete-dialog [[_ selected-rows ok-handler]]
|
||||
(shui/dialog-open!
|
||||
(component-page/batch-delete-dialog
|
||||
selected-rows false
|
||||
ok-handler)))
|
||||
|
||||
(defmethod handle :page/create-today-journal [[_ _repo]]
|
||||
(p/let [_ (page-handler/create-today-journal!)]
|
||||
(ui-handler/re-render-root!)))
|
||||
|
||||
(defmethod handle :file/not-matched-from-disk [[_ path disk-content db-content]]
|
||||
(when-let [repo (state/get-current-repo)]
|
||||
(shui/dialog-open!
|
||||
#(diff/local-file repo path disk-content db-content)
|
||||
{:label "diff__cp"})))
|
||||
|
||||
(defmethod handle :modal/display-file-version-selector [[_ versions path get-content]]
|
||||
(shui/dialog-open!
|
||||
#(git-component/file-version-selector versions path get-content)))
|
||||
|
||||
(defmethod handle :graph/sync-context []
|
||||
(let [context {:dev? config/dev?
|
||||
:node-test? util/node-test?
|
||||
@@ -391,48 +201,6 @@
|
||||
(export/auto-db-backup! repo {:backup-now? true})
|
||||
(fs-watcher/load-graph-files! repo)))))
|
||||
|
||||
(defmethod handle :notification/show [[_ {:keys [content status clear?]}]]
|
||||
(notification/show! content status clear?))
|
||||
|
||||
(defmethod handle :command/run [_]
|
||||
(when (util/electron?)
|
||||
(shui/dialog-open! shell/shell)))
|
||||
|
||||
(defmethod handle :go/search [_]
|
||||
(shui/dialog-open!
|
||||
cmdk/cmdk-modal
|
||||
{:id :ls-dialog-cmdk
|
||||
:align :top
|
||||
:content-props {:class "ls-dialog-cmdk"}
|
||||
:close-btn? false}))
|
||||
|
||||
(defmethod handle :go/plugins [_]
|
||||
(plugin/open-plugins-modal!))
|
||||
|
||||
(defmethod handle :go/plugins-waiting-lists [_]
|
||||
(plugin/open-waiting-updates-modal!))
|
||||
|
||||
(defmethod handle :go/plugins-from-file [[_ plugins]]
|
||||
(plugin/open-plugins-from-file-modal! plugins))
|
||||
|
||||
(defmethod handle :go/install-plugin-from-github [[_]]
|
||||
(shui/dialog-open!
|
||||
(plugin/install-from-github-release-container)))
|
||||
|
||||
(defmethod handle :go/plugins-settings [[_ pid nav? title]]
|
||||
(when pid
|
||||
(state/set-state! :plugin/focused-settings pid)
|
||||
(state/set-state! :plugin/navs-settings? (not (false? nav?)))
|
||||
(plugin/open-focused-settings-modal! title)))
|
||||
|
||||
(defmethod handle :go/proxy-settings [[_ agent-opts]]
|
||||
(shui/dialog-open!
|
||||
(plugin/user-proxy-settings-container agent-opts)
|
||||
{:id :https-proxy-panel :center? true :class "lg:max-w-2xl"}))
|
||||
|
||||
(defmethod handle :redirect-to-home [_]
|
||||
(page-handler/create-today-journal!))
|
||||
|
||||
(defmethod handle :instrument [[_ {:keys [type payload] :as opts}]]
|
||||
(when-not (empty? (dissoc opts :type :payload))
|
||||
(js/console.error "instrument data-map should only contains [:type :payload]"))
|
||||
@@ -557,57 +325,12 @@
|
||||
(file-sync-restart!))))
|
||||
(state/pub-event! [:graph/ready (state/get-current-repo)])))))
|
||||
|
||||
(defmethod handle :plugin/consume-updates [[_ id prev-pending? updated?]]
|
||||
(let [downloading? (:plugin/updates-downloading? @state/state)
|
||||
auto-checking? (plugin-handler/get-auto-checking?)]
|
||||
(when-let [coming (and (not downloading?)
|
||||
(get-in @state/state [:plugin/updates-coming id]))]
|
||||
(let [error-code (:error-code coming)
|
||||
error-code (if (= error-code (str :no-new-version)) nil error-code)
|
||||
title (:title coming)]
|
||||
(when (and prev-pending? (not auto-checking?))
|
||||
(if-not error-code
|
||||
(plugin/set-updates-sub-content! (str title "...") 0)
|
||||
(notification/show!
|
||||
(str "[Checked]<" title "> " error-code) :error)))))
|
||||
|
||||
(if (and updated? downloading?)
|
||||
;; try to start consume downloading item
|
||||
(if-let [next-coming (state/get-next-selected-coming-update)]
|
||||
(plugin-handler/check-or-update-marketplace-plugin!
|
||||
(assoc next-coming :only-check false :error-code nil)
|
||||
(fn [^js e] (js/console.error "[Download Err]" next-coming e)))
|
||||
(plugin-handler/close-updates-downloading))
|
||||
|
||||
;; try to start consume pending item
|
||||
(if-let [next-pending (second (first (:plugin/updates-pending @state/state)))]
|
||||
(do
|
||||
(println "Updates: take next pending - " (:id next-pending))
|
||||
(js/setTimeout
|
||||
#(plugin-handler/check-or-update-marketplace-plugin!
|
||||
(assoc next-pending :only-check true :auto-check auto-checking? :error-code nil)
|
||||
(fn [^js e]
|
||||
(notification/show! (.toString e) :error)
|
||||
(js/console.error "[Check Err]" next-pending e))) 500))
|
||||
|
||||
;; try to open waiting updates list
|
||||
(do (when (and prev-pending? (not auto-checking?)
|
||||
(seq (state/all-available-coming-updates)))
|
||||
(plugin/open-waiting-updates-modal!))
|
||||
(plugin-handler/set-auto-checking! false))))))
|
||||
|
||||
(defmethod handle :plugin/hook-db-tx [[_ {:keys [blocks tx-data] :as payload}]]
|
||||
(when-let [payload (and (seq blocks)
|
||||
(merge payload {:tx-data (map #(into [] %) tx-data)}))]
|
||||
(plugin-handler/hook-plugin-db :changed payload)
|
||||
(plugin-handler/hook-plugin-block-changes payload)))
|
||||
|
||||
(defmethod handle :plugin/loader-perf-tip [[_ {:keys [^js o _s _e]}]]
|
||||
(when-let [opts (.-options o)]
|
||||
(notification/show!
|
||||
(plugin/perf-tip-content (.-id o) (.-name opts) (.-url opts))
|
||||
:warning false (.-id o))))
|
||||
|
||||
(defmethod handle :mobile-file-watcher/changed [[_ ^js event]]
|
||||
(let [type (.-event event)
|
||||
payload (js->clj event :keywordize-keys true)]
|
||||
@@ -621,51 +344,6 @@
|
||||
(defmethod handle :shortcut/refresh [[_]]
|
||||
(st/refresh!))
|
||||
|
||||
(defn- refresh-cb []
|
||||
(page-handler/create-today-journal!)
|
||||
(file-sync-restart!))
|
||||
|
||||
(defmethod handle :graph/ask-for-re-fresh [_]
|
||||
(shui/dialog-open!
|
||||
[:div {:style {:max-width 700}}
|
||||
[:p (t :sync-from-local-changes-detected)]
|
||||
[:div.flex.justify-end
|
||||
(ui/button
|
||||
(t :yes)
|
||||
:autoFocus "on"
|
||||
:class "ui__modal-enter"
|
||||
:on-click (fn []
|
||||
(shui/dialog-close!)
|
||||
(nfs-handler/refresh! (state/get-current-repo) refresh-cb)))]]))
|
||||
|
||||
(defmethod handle :sync/create-remote-graph [[_ current-repo]]
|
||||
(let [graph-name (js/decodeURI (util/node-path.basename current-repo))]
|
||||
(async/go
|
||||
(async/<! (sync/<sync-stop))
|
||||
(state/set-state! [:ui/loading? :graph/create-remote?] true)
|
||||
(when-let [GraphUUID (get (async/<! (file-sync-handler/create-graph graph-name)) 2)]
|
||||
(async/<! (sync/<sync-start))
|
||||
(state/set-state! [:ui/loading? :graph/create-remote?] false)
|
||||
;; update existing repo
|
||||
(state/set-repos! (map (fn [r]
|
||||
(if (= (:url r) current-repo)
|
||||
(assoc r
|
||||
:GraphUUID GraphUUID
|
||||
:GraphName graph-name
|
||||
:remote? true)
|
||||
r))
|
||||
(state/get-repos)))))))
|
||||
|
||||
(defmethod handle :modal/remote-encryption-input-pw-dialog [[_ repo-url remote-graph-info type opts]]
|
||||
(shui/dialog-open!
|
||||
(encryption/input-password
|
||||
repo-url nil (merge
|
||||
(assoc remote-graph-info
|
||||
:type (or type :create-pwd-remote)
|
||||
:repo repo-url)
|
||||
opts))
|
||||
{:center? true :close-btn? false :close-backdrop? false}))
|
||||
|
||||
(defmethod handle :journal/insert-template [[_ page-name]]
|
||||
(let [page-name (util/page-name-sanity-lc page-name)]
|
||||
(when-let [page (db/get-page page-name)]
|
||||
@@ -682,49 +360,6 @@
|
||||
(when-let [id (:block/uuid block)]
|
||||
(editor-handler/set-heading! id heading)))
|
||||
|
||||
(defmethod handle :file-sync-graph/restore-file [[_ graph page-entity content]]
|
||||
(when (db/get-db graph)
|
||||
(let [file (:block/file page-entity)]
|
||||
(when-let [path (:file/path file)]
|
||||
(when (and (not= content (:file/content file))
|
||||
(:file/content file))
|
||||
(sync/add-new-version-file graph path (:file/content file)))
|
||||
(p/let [_ (file-handler/alter-file graph
|
||||
path
|
||||
content
|
||||
{:re-render-root? true
|
||||
:skip-compare? true})]
|
||||
(state/close-modal!)
|
||||
(route-handler/redirect! {:to :page
|
||||
:path-params {:name (:block/name page-entity)}}))))))
|
||||
|
||||
(defmethod handle :whiteboard/onboarding [[_ opts]]
|
||||
(shui/dialog-open!
|
||||
(fn [{:keys [close]}] (whiteboard/onboarding-welcome close))
|
||||
(merge {:close-btn? false
|
||||
:center? true
|
||||
:close-backdrop? false} opts)))
|
||||
|
||||
(defmethod handle :file-sync/onboarding-tip [[_ type opts]]
|
||||
(let [type (keyword type)]
|
||||
(when-not (config/db-based-graph? (state/get-current-repo))
|
||||
(shui/dialog-open!
|
||||
(file-sync/make-onboarding-panel type)
|
||||
(merge {:close-btn? false
|
||||
:center? true
|
||||
:close-backdrop? (not= type :welcome)} opts)))))
|
||||
|
||||
(defmethod handle :file-sync/maybe-onboarding-show [[_ type]]
|
||||
(file-sync/maybe-onboarding-show type))
|
||||
|
||||
(defmethod handle :file-sync/storage-exceed-limit [[_]]
|
||||
(notification/show! "file sync storage exceed limit" :warning false)
|
||||
(file-sync-stop!))
|
||||
|
||||
(defmethod handle :file-sync/graph-count-exceed-limit [[_]]
|
||||
(notification/show! "file sync graph count exceed limit" :warning false)
|
||||
(file-sync-stop!))
|
||||
|
||||
(defmethod handle :graph/restored [[_ graph]]
|
||||
(when graph (assets-handler/ensure-assets-dir! graph))
|
||||
(mobile/init!)
|
||||
@@ -741,143 +376,12 @@
|
||||
(route-handler/redirect! {:to :page
|
||||
:path-params {:name link}}))
|
||||
|
||||
(defmethod handle :graph/dir-gone [[_ dir]]
|
||||
(state/pub-event! [:notification/show
|
||||
{:content (str "The directory " dir " has been renamed or deleted, the editor will be disabled for this graph, you can unlink the graph.")
|
||||
:status :error
|
||||
:clear? false}])
|
||||
(state/update-state! :file/unlinked-dirs (fn [dirs] (conj dirs dir))))
|
||||
|
||||
(defmethod handle :graph/dir-back [[_ repo dir]]
|
||||
(when (contains? (:file/unlinked-dirs @state/state) dir)
|
||||
(notification/clear-all!)
|
||||
(state/pub-event! [:notification/show
|
||||
{:content (str "The directory " dir " has been back, you can edit your graph now.")
|
||||
:status :success
|
||||
:clear? true}])
|
||||
(state/update-state! :file/unlinked-dirs (fn [dirs] (disj dirs dir)))
|
||||
(when (= dir (config/get-repo-dir repo))
|
||||
(fs/watch-dir! dir))))
|
||||
|
||||
(defmethod handle :ui/notify-skipped-downloading-files [[_ paths]]
|
||||
(notification/show!
|
||||
[:div
|
||||
[:div.mb-4
|
||||
[:div.font-semibold.mb-4.text-xl "It seems that some of your filenames are in the outdated format."]
|
||||
[:p
|
||||
"The files below that have reserved characters can't be saved on this device."]
|
||||
[:div.overflow-y-auto.max-h-96
|
||||
[:ol.my-2
|
||||
(for [path paths]
|
||||
[:li path])]]
|
||||
|
||||
[:div
|
||||
[:p
|
||||
"Check " [:a {:href "https://docs.logseq.com/#/page/logseq%20file%20and%20folder%20naming%20rules"
|
||||
:target "_blank"}
|
||||
"Logseq file and folder naming rules"]
|
||||
" for more details."]]]]
|
||||
:warning
|
||||
false))
|
||||
|
||||
(defmethod handle :graph/setup-a-repo [[_ opts]]
|
||||
(let [opts' (merge {:picked-root-fn #(state/close-modal!)
|
||||
:native-icloud? (not (string/blank? (state/get-icloud-container-root-url)))
|
||||
:logged? (user-handler/logged-in?)} opts)]
|
||||
(if (mobile-util/native-ios?)
|
||||
(shui/dialog-open!
|
||||
#(graph-picker/graph-picker-cp opts')
|
||||
{:label "graph-setup"})
|
||||
(page-handler/ls-dir-files! st/refresh! opts'))))
|
||||
|
||||
(defmethod handle :graph/new-db-graph [[_ _opts]]
|
||||
(shui/dialog-open!
|
||||
repo/new-db-graph
|
||||
{:id :new-db-graph
|
||||
:title [:h2 "Create a new graph"]
|
||||
:style {:max-width "500px"}}))
|
||||
|
||||
(defmethod handle :dialog-select/graph-open []
|
||||
(select/dialog-select! :graph-open))
|
||||
|
||||
(defmethod handle :dialog-select/graph-remove []
|
||||
(select/dialog-select! :graph-remove))
|
||||
|
||||
(defmethod handle :dialog-select/db-graph-replace []
|
||||
(select/dialog-select! :db-graph-replace))
|
||||
|
||||
(defmethod handle :graph/save-db-to-disk [[_ _opts]]
|
||||
(persist-db/export-current-graph! {:succ-notification? true}))
|
||||
|
||||
(defmethod handle :class/configure [[_ page]]
|
||||
(shui/dialog-open!
|
||||
#(block/block-container {} page)
|
||||
{:label "page-configure"
|
||||
:align :top}))
|
||||
|
||||
(defmethod handle :file/alter [[_ repo path content]]
|
||||
(p/let [_ (file-handler/alter-file repo path content {:from-disk? true})]
|
||||
(ui-handler/re-render-root!)))
|
||||
|
||||
(defmethod handle :ui/re-render-root [[_]]
|
||||
(ui-handler/re-render-root!))
|
||||
|
||||
(rum/defcs file-id-conflict-item <
|
||||
(rum/local false ::resolved?)
|
||||
[state repo file data]
|
||||
(let [resolved? (::resolved? state)
|
||||
id (last (:assertion data))]
|
||||
[:li {:key file}
|
||||
[:div
|
||||
[:a {:on-click #(js/window.apis.openPath file)} file]
|
||||
(if @resolved?
|
||||
[:div.flex.flex-row.items-center
|
||||
(ui/icon "circle-check" {:style {:font-size 20}})
|
||||
[:div.ml-1 "Resolved"]]
|
||||
[:div
|
||||
[:p
|
||||
(str "It seems that another whiteboard file already has the ID \"" id
|
||||
"\". You can fix it by changing the ID in this file with another UUID.")]
|
||||
[:p
|
||||
"Or, let me"
|
||||
(ui/button "Fix"
|
||||
:on-click (fn []
|
||||
(let [dir (config/get-repo-dir repo)]
|
||||
(p/let [content (fs/read-file dir file)]
|
||||
(let [new-content (string/replace content (str id) (str (random-uuid)))]
|
||||
(p/let [_ (fs/write-file! repo
|
||||
dir
|
||||
file
|
||||
new-content
|
||||
{})]
|
||||
(reset! resolved? true))))))
|
||||
:class "inline mx-1")
|
||||
"it."]])]]))
|
||||
|
||||
(defmethod handle :file/parse-and-load-error [[_ repo parse-errors]]
|
||||
(state/pub-event! [:notification/show
|
||||
{:content
|
||||
[:div
|
||||
[:h2.title "Oops. These files failed to import to your graph:"]
|
||||
[:ol.my-2
|
||||
(for [[file error] parse-errors]
|
||||
(let [data (ex-data error)]
|
||||
(cond
|
||||
(and (common-config/whiteboard? file)
|
||||
(= :transact/upsert (:error data))
|
||||
(uuid? (last (:assertion data))))
|
||||
(rum/with-key (file-id-conflict-item repo file data) file)
|
||||
|
||||
:else
|
||||
(do
|
||||
(state/pub-event! [:capture-error {:error error
|
||||
:payload {:type :file/parse-and-load-error}}])
|
||||
[:li.my-1 {:key file}
|
||||
[:a {:on-click #(js/window.apis.openPath file)} file]
|
||||
[:p (.-message error)]]))))]
|
||||
[:p "Don't forget to re-index your graph when all the conflicts are resolved."]]
|
||||
:status :error}]))
|
||||
|
||||
(defmethod handle :run/cli-command [[_ command content]]
|
||||
(when (and command (not (string/blank? content)))
|
||||
(shell-handler/run-cli-command-wrapper! command content)))
|
||||
@@ -930,70 +434,6 @@
|
||||
(when-let [blocks (and block (db-model/get-block-immediate-children (state/get-current-repo) (:block/uuid block)))]
|
||||
(editor-handler/toggle-blocks-as-own-order-list! blocks)))
|
||||
|
||||
(defn- editor-new-property [block target {:keys [selected-blocks] :as opts}]
|
||||
(let [editing-block (state/get-edit-block)
|
||||
pos (state/get-edit-pos)
|
||||
edit-block-or-selected (cond
|
||||
editing-block
|
||||
[editing-block]
|
||||
(seq selected-blocks)
|
||||
selected-blocks
|
||||
:else
|
||||
(seq (keep #(db/entity [:block/uuid %]) (state/get-selection-block-ids))))
|
||||
current-block (when-let [s (state/get-current-page)]
|
||||
(when (util/uuid-string? s)
|
||||
(db/entity [:block/uuid (uuid s)])))
|
||||
blocks (or (when block [block])
|
||||
edit-block-or-selected
|
||||
(when current-block [current-block]))
|
||||
opts' (cond-> opts
|
||||
editing-block
|
||||
(assoc :original-block editing-block
|
||||
:edit-original-block
|
||||
(fn [{:keys [editing-default-property?]}]
|
||||
(when editing-block
|
||||
(let [content (:block/title (db/entity (:db/id editing-block)))
|
||||
esc? (= "Escape" (state/get-ui-last-key-code))
|
||||
[content' pos] (cond
|
||||
esc?
|
||||
[nil pos]
|
||||
(and (>= (count content) pos)
|
||||
(>= pos 2)
|
||||
(= (util/nth-safe content (dec pos))
|
||||
(util/nth-safe content (- pos 2))
|
||||
";"))
|
||||
[(str (common-util/safe-subs content 0 (- pos 2))
|
||||
(common-util/safe-subs content pos))
|
||||
(- pos 2)]
|
||||
:else
|
||||
[nil pos])]
|
||||
(when content'
|
||||
(if editing-default-property?
|
||||
(editor-handler/save-block! (state/get-current-repo) (:block/uuid editing-block) content')
|
||||
(editor-handler/edit-block! editing-block (or pos :max)
|
||||
(cond-> {}
|
||||
content'
|
||||
(assoc :custom-content content'))))))))))]
|
||||
(when (seq blocks)
|
||||
(let [target' (or target
|
||||
(some-> (state/get-edit-input-id)
|
||||
(gdom/getElement))
|
||||
(first (state/get-selection-blocks)))]
|
||||
(if target'
|
||||
(shui/popup-show! target'
|
||||
#(property-dialog/dialog blocks opts')
|
||||
{:align "start"
|
||||
:auto-focus? true})
|
||||
(shui/dialog-open! #(property-dialog/dialog blocks opts')
|
||||
{:id :property-dialog
|
||||
:align "start"}))))))
|
||||
|
||||
(defmethod handle :editor/new-property [[_ {:keys [block target] :as opts}]]
|
||||
(when-not config/publishing?
|
||||
(p/do!
|
||||
(editor-handler/save-current-block!)
|
||||
(editor-new-property block target opts))))
|
||||
|
||||
(defmethod handle :editor/upsert-type-block [[_ {:keys [block type lang update-current-block?]}]]
|
||||
(p/do!
|
||||
(when-not update-current-block?
|
||||
@@ -1026,25 +466,6 @@
|
||||
(db/entity [:block/uuid (:block/uuid block)])))]
|
||||
(js/setTimeout #(editor-handler/edit-block! block :max) 100)))))
|
||||
|
||||
(rum/defc multi-tabs-dialog
|
||||
[]
|
||||
(let [word (if (util/electron?) "window" "tab")]
|
||||
[:div.flex.p-4.flex-col.gap-4.h-64
|
||||
[:span.warning.text-lg
|
||||
(util/format "Logseq doesn't support multiple %ss access to the same graph yet, please close this %s or switch to another graph."
|
||||
word word)]
|
||||
[:div.text-lg
|
||||
[:p "Switch to another repo: "]
|
||||
[:div.border.rounded.bg-gray-01.overflow-hidden.w-60
|
||||
(repo/repos-dropdown {:on-click (fn [e]
|
||||
(util/stop e)
|
||||
(state/set-state! :error/multiple-tabs-access-opfs? false)
|
||||
(shui/dialog-close!))})]]]))
|
||||
|
||||
(defmethod handle :show/multiple-tabs-error-dialog [_]
|
||||
(state/set-state! :error/multiple-tabs-access-opfs? true)
|
||||
(shui/dialog-open! multi-tabs-dialog))
|
||||
|
||||
(defmethod handle :rtc/sync-state [[_ state]]
|
||||
(state/update-state! :rtc/state (fn [old] (merge old state))))
|
||||
|
||||
@@ -1075,24 +496,6 @@
|
||||
(defmethod handle :editor/run-query-command [_]
|
||||
(editor-handler/run-query-command!))
|
||||
|
||||
(defmethod handle :editor/show-action-bar []
|
||||
(let [selection (state/get-selection-blocks)
|
||||
first-visible-block (some #(when (util/el-visible-in-viewport? % true) %) selection)]
|
||||
(when first-visible-block
|
||||
(shui/popup-hide! :selection-action-bar)
|
||||
(shui/popup-show!
|
||||
first-visible-block
|
||||
(fn []
|
||||
(selection/action-bar))
|
||||
{:id :selection-action-bar
|
||||
:content-props {:side "top"
|
||||
:class "!py-0 !px-0 !border-none"}
|
||||
:auto-side? false
|
||||
:align :start}))))
|
||||
|
||||
(defmethod handle :editor/hide-action-bar []
|
||||
(shui/popup-hide! :selection-action-bar))
|
||||
|
||||
(defmethod handle :editor/load-blocks [[_ ids]]
|
||||
(when (seq ids)
|
||||
;; not using `<get-blocks` here becuase because we want to
|
||||
|
||||
402
src/main/frontend/handler/events/ui.cljs
Normal file
402
src/main/frontend/handler/events/ui.cljs
Normal file
@@ -0,0 +1,402 @@
|
||||
(ns frontend.handler.events.ui
|
||||
"UI events"
|
||||
(:require [clojure.core.async :as async]
|
||||
[clojure.core.async.interop :refer [p->c]]
|
||||
[frontend.components.block :as block]
|
||||
[frontend.components.cmdk.core :as cmdk]
|
||||
[frontend.components.file-sync :as file-sync]
|
||||
[frontend.components.page :as component-page]
|
||||
[frontend.components.plugins :as plugin]
|
||||
[frontend.components.property.dialog :as property-dialog]
|
||||
[frontend.components.repo :as repo]
|
||||
[frontend.components.select :as select]
|
||||
[frontend.components.selection :as selection]
|
||||
[frontend.components.settings :as settings]
|
||||
[frontend.components.shell :as shell]
|
||||
[frontend.components.user.login :as login]
|
||||
[frontend.components.whiteboard :as whiteboard]
|
||||
[frontend.config :as config]
|
||||
[frontend.context.i18n :refer [t]]
|
||||
[frontend.db :as db]
|
||||
[frontend.extensions.fsrs :as fsrs]
|
||||
[frontend.extensions.srs :as srs]
|
||||
[frontend.fs.capacitor-fs :as capacitor-fs]
|
||||
[frontend.fs.nfs :as nfs]
|
||||
[frontend.fs.sync :as sync]
|
||||
[frontend.handler.db-based.rtc :as rtc-handler]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
[frontend.handler.events :as events]
|
||||
[frontend.handler.file-based.nfs :as nfs-handler]
|
||||
[frontend.handler.file-sync :as file-sync-handler]
|
||||
[frontend.handler.notification :as notification]
|
||||
[frontend.handler.page :as page-handler]
|
||||
[frontend.handler.plugin :as plugin-handler]
|
||||
[frontend.handler.repo :as repo-handler]
|
||||
[frontend.handler.route :as route-handler]
|
||||
[frontend.handler.user :as user-handler]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.modules.instrumentation.sentry :as sentry-event]
|
||||
[frontend.state :as state]
|
||||
[frontend.ui :as ui]
|
||||
[frontend.util :as util]
|
||||
[goog.dom :as gdom]
|
||||
[logseq.common.util :as common-util]
|
||||
[logseq.shui.ui :as shui]
|
||||
[promesa.core :as p]
|
||||
[rum.core :as rum]))
|
||||
|
||||
(defmethod events/handle :class/configure [[_ page]]
|
||||
(shui/dialog-open!
|
||||
#(block/block-container {} page)
|
||||
{:label "page-configure"
|
||||
:align :top}))
|
||||
|
||||
(defmethod events/handle :go/search [_]
|
||||
(shui/dialog-open!
|
||||
cmdk/cmdk-modal
|
||||
{:id :ls-dialog-cmdk
|
||||
:align :top
|
||||
:content-props {:class "ls-dialog-cmdk"}
|
||||
:close-btn? false}))
|
||||
|
||||
(defmethod events/handle :command/run [_]
|
||||
(when (util/electron?)
|
||||
(shui/dialog-open! shell/shell)))
|
||||
|
||||
(defmethod events/handle :notification/show [[_ {:keys [content status clear?]}]]
|
||||
(notification/show! content status clear?))
|
||||
|
||||
(defmethod events/handle :command/run [_]
|
||||
(when (util/electron?)
|
||||
(shui/dialog-open! shell/shell)))
|
||||
|
||||
(defmethod events/handle :go/plugins [_]
|
||||
(plugin/open-plugins-modal!))
|
||||
|
||||
(defmethod events/handle :go/plugins-waiting-lists [_]
|
||||
(plugin/open-waiting-updates-modal!))
|
||||
|
||||
(defmethod events/handle :go/plugins-from-file [[_ plugins]]
|
||||
(plugin/open-plugins-from-file-modal! plugins))
|
||||
|
||||
(defmethod events/handle :go/install-plugin-from-github [[_]]
|
||||
(shui/dialog-open!
|
||||
(plugin/install-from-github-release-container)))
|
||||
|
||||
(defmethod events/handle :go/plugins-settings [[_ pid nav? title]]
|
||||
(when pid
|
||||
(state/set-state! :plugin/focused-settings pid)
|
||||
(state/set-state! :plugin/navs-settings? (not (false? nav?)))
|
||||
(plugin/open-focused-settings-modal! title)))
|
||||
|
||||
(defmethod events/handle :go/proxy-settings [[_ agent-opts]]
|
||||
(shui/dialog-open!
|
||||
(plugin/user-proxy-settings-container agent-opts)
|
||||
{:id :https-proxy-panel :center? true :class "lg:max-w-2xl"}))
|
||||
|
||||
(defmethod events/handle :redirect-to-home [_]
|
||||
(page-handler/create-today-journal!))
|
||||
|
||||
(defmethod events/handle :page/show-delete-dialog [[_ selected-rows ok-handler]]
|
||||
(shui/dialog-open!
|
||||
(component-page/batch-delete-dialog
|
||||
selected-rows false
|
||||
ok-handler)))
|
||||
|
||||
(defn ask-permission
|
||||
[repo]
|
||||
(when
|
||||
(and (not (util/electron?))
|
||||
(not (mobile-util/native-platform?)))
|
||||
(fn [{:keys [close]}]
|
||||
[:div
|
||||
;; TODO: fn translation with args
|
||||
[:p
|
||||
"Grant native filesystem permission for directory: "
|
||||
[:b (config/get-local-dir repo)]]
|
||||
(ui/button
|
||||
(t :settings-permission/start-granting)
|
||||
:class "ui__modal-enter"
|
||||
:on-click (fn []
|
||||
(nfs/check-directory-permission! repo)
|
||||
(close)))])))
|
||||
|
||||
(defn get-local-repo
|
||||
[]
|
||||
(when-let [repo (state/get-current-repo)]
|
||||
(when (config/local-file-based-graph? repo)
|
||||
repo)))
|
||||
|
||||
(defmethod events/handle :modal/nfs-ask-permission []
|
||||
(when-let [repo (get-local-repo)]
|
||||
(some-> (ask-permission repo)
|
||||
(shui/dialog-open! {:align :top}))))
|
||||
|
||||
(defmethod events/handle :modal/show-cards [[_ cards-id]]
|
||||
(let [db-based? (config/db-based-graph? (state/get-current-repo))]
|
||||
(shui/dialog-open!
|
||||
(if db-based? (fn [] (fsrs/cards-view cards-id)) srs/global-cards)
|
||||
{:id :srs
|
||||
:label "flashcards__cp"})))
|
||||
|
||||
(defmethod events/handle :modal/show-instruction [_]
|
||||
(shui/dialog-open!
|
||||
capacitor-fs/instruction
|
||||
{:id :instruction
|
||||
:label "instruction__cp"}))
|
||||
|
||||
(defmethod events/handle :modal/show-themes-modal [[_ classic?]]
|
||||
(if classic?
|
||||
(plugin/open-select-theme!)
|
||||
(route-handler/go-to-search! :themes)))
|
||||
|
||||
(defmethod events/handle :ui/toggle-appearance [_]
|
||||
(let [popup-id "appearance_settings"]
|
||||
(if (gdom/getElement popup-id)
|
||||
(shui/popup-hide! popup-id)
|
||||
(shui/popup-show!
|
||||
(js/document.querySelector ".toolbar-dots-btn")
|
||||
(fn []
|
||||
(settings/appearance))
|
||||
{:id popup-id
|
||||
:align :end}))))
|
||||
|
||||
(defmethod events/handle :plugin/consume-updates [[_ id prev-pending? updated?]]
|
||||
(let [downloading? (:plugin/updates-downloading? @state/state)
|
||||
auto-checking? (plugin-handler/get-auto-checking?)]
|
||||
(when-let [coming (and (not downloading?)
|
||||
(get-in @state/state [:plugin/updates-coming id]))]
|
||||
(let [error-code (:error-code coming)
|
||||
error-code (if (= error-code (str :no-new-version)) nil error-code)
|
||||
title (:title coming)]
|
||||
(when (and prev-pending? (not auto-checking?))
|
||||
(if-not error-code
|
||||
(plugin/set-updates-sub-content! (str title "...") 0)
|
||||
(notification/show!
|
||||
(str "[Checked]<" title "> " error-code) :error)))))
|
||||
|
||||
(if (and updated? downloading?)
|
||||
;; try to start consume downloading item
|
||||
(if-let [next-coming (state/get-next-selected-coming-update)]
|
||||
(plugin-handler/check-or-update-marketplace-plugin!
|
||||
(assoc next-coming :only-check false :error-code nil)
|
||||
(fn [^js e] (js/console.error "[Download Err]" next-coming e)))
|
||||
(plugin-handler/close-updates-downloading))
|
||||
|
||||
;; try to start consume pending item
|
||||
(if-let [next-pending (second (first (:plugin/updates-pending @state/state)))]
|
||||
(do
|
||||
(println "Updates: take next pending - " (:id next-pending))
|
||||
(js/setTimeout
|
||||
#(plugin-handler/check-or-update-marketplace-plugin!
|
||||
(assoc next-pending :only-check true :auto-check auto-checking? :error-code nil)
|
||||
(fn [^js e]
|
||||
(notification/show! (.toString e) :error)
|
||||
(js/console.error "[Check Err]" next-pending e))) 500))
|
||||
|
||||
;; try to open waiting updates list
|
||||
(do (when (and prev-pending? (not auto-checking?)
|
||||
(seq (state/all-available-coming-updates)))
|
||||
(plugin/open-waiting-updates-modal!))
|
||||
(plugin-handler/set-auto-checking! false))))))
|
||||
|
||||
(defmethod events/handle :plugin/loader-perf-tip [[_ {:keys [^js o _s _e]}]]
|
||||
(when-let [opts (.-options o)]
|
||||
(notification/show!
|
||||
(plugin/perf-tip-content (.-id o) (.-name opts) (.-url opts))
|
||||
:warning false (.-id o))))
|
||||
|
||||
(defn- refresh-cb []
|
||||
(page-handler/create-today-journal!)
|
||||
(events/file-sync-restart!))
|
||||
|
||||
(defmethod events/handle :graph/ask-for-re-fresh [_]
|
||||
(shui/dialog-open!
|
||||
[:div {:style {:max-width 700}}
|
||||
[:p (t :sync-from-local-changes-detected)]
|
||||
[:div.flex.justify-end
|
||||
(ui/button
|
||||
(t :yes)
|
||||
:autoFocus "on"
|
||||
:class "ui__modal-enter"
|
||||
:on-click (fn []
|
||||
(shui/dialog-close!)
|
||||
(nfs-handler/refresh! (state/get-current-repo) refresh-cb)))]]))
|
||||
|
||||
(defn- editor-new-property [block target {:keys [selected-blocks] :as opts}]
|
||||
(let [editing-block (state/get-edit-block)
|
||||
pos (state/get-edit-pos)
|
||||
edit-block-or-selected (cond
|
||||
editing-block
|
||||
[editing-block]
|
||||
(seq selected-blocks)
|
||||
selected-blocks
|
||||
:else
|
||||
(seq (keep #(db/entity [:block/uuid %]) (state/get-selection-block-ids))))
|
||||
current-block (when-let [s (state/get-current-page)]
|
||||
(when (util/uuid-string? s)
|
||||
(db/entity [:block/uuid (uuid s)])))
|
||||
blocks (or (when block [block])
|
||||
edit-block-or-selected
|
||||
(when current-block [current-block]))
|
||||
opts' (cond-> opts
|
||||
editing-block
|
||||
(assoc :original-block editing-block
|
||||
:edit-original-block
|
||||
(fn [{:keys [editing-default-property?]}]
|
||||
(when editing-block
|
||||
(let [content (:block/title (db/entity (:db/id editing-block)))
|
||||
esc? (= "Escape" (state/get-ui-last-key-code))
|
||||
[content' pos] (cond
|
||||
esc?
|
||||
[nil pos]
|
||||
(and (>= (count content) pos)
|
||||
(>= pos 2)
|
||||
(= (util/nth-safe content (dec pos))
|
||||
(util/nth-safe content (- pos 2))
|
||||
";"))
|
||||
[(str (common-util/safe-subs content 0 (- pos 2))
|
||||
(common-util/safe-subs content pos))
|
||||
(- pos 2)]
|
||||
:else
|
||||
[nil pos])]
|
||||
(when content'
|
||||
(if editing-default-property?
|
||||
(editor-handler/save-block! (state/get-current-repo) (:block/uuid editing-block) content')
|
||||
(editor-handler/edit-block! editing-block (or pos :max)
|
||||
(cond-> {}
|
||||
content'
|
||||
(assoc :custom-content content'))))))))))]
|
||||
(when (seq blocks)
|
||||
(let [target' (or target
|
||||
(some-> (state/get-edit-input-id)
|
||||
(gdom/getElement))
|
||||
(first (state/get-selection-blocks)))]
|
||||
(if target'
|
||||
(shui/popup-show! target'
|
||||
#(property-dialog/dialog blocks opts')
|
||||
{:align "start"
|
||||
:auto-focus? true})
|
||||
(shui/dialog-open! #(property-dialog/dialog blocks opts')
|
||||
{:id :property-dialog
|
||||
:align "start"}))))))
|
||||
|
||||
(defmethod events/handle :editor/new-property [[_ {:keys [block target] :as opts}]]
|
||||
(when-not config/publishing?
|
||||
(p/do!
|
||||
(editor-handler/save-current-block!)
|
||||
(editor-new-property block target opts))))
|
||||
|
||||
(defmethod events/handle :graph/new-db-graph [[_ _opts]]
|
||||
(shui/dialog-open!
|
||||
repo/new-db-graph
|
||||
{:id :new-db-graph
|
||||
:title [:h2 "Create a new graph"]
|
||||
:style {:max-width "500px"}}))
|
||||
|
||||
(defmethod events/handle :dialog-select/graph-open []
|
||||
(select/dialog-select! :graph-open))
|
||||
|
||||
(defmethod events/handle :dialog-select/graph-remove []
|
||||
(select/dialog-select! :graph-remove))
|
||||
|
||||
(defmethod events/handle :dialog-select/db-graph-replace []
|
||||
(select/dialog-select! :db-graph-replace))
|
||||
|
||||
(rum/defc multi-tabs-dialog
|
||||
[]
|
||||
(let [word (if (util/electron?) "window" "tab")]
|
||||
[:div.flex.p-4.flex-col.gap-4.h-64
|
||||
[:span.warning.text-lg
|
||||
(util/format "Logseq doesn't support multiple %ss access to the same graph yet, please close this %s or switch to another graph."
|
||||
word word)]
|
||||
[:div.text-lg
|
||||
[:p "Switch to another repo: "]
|
||||
[:div.border.rounded.bg-gray-01.overflow-hidden.w-60
|
||||
(repo/repos-dropdown {:on-click (fn [e]
|
||||
(util/stop e)
|
||||
(state/set-state! :error/multiple-tabs-access-opfs? false)
|
||||
(shui/dialog-close!))})]]]))
|
||||
|
||||
(defmethod events/handle :show/multiple-tabs-error-dialog [_]
|
||||
(state/set-state! :error/multiple-tabs-access-opfs? true)
|
||||
(shui/dialog-open! multi-tabs-dialog))
|
||||
|
||||
(defmethod events/handle :editor/show-action-bar []
|
||||
(let [selection (state/get-selection-blocks)
|
||||
first-visible-block (some #(when (util/el-visible-in-viewport? % true) %) selection)]
|
||||
(when first-visible-block
|
||||
(shui/popup-hide! :selection-action-bar)
|
||||
(shui/popup-show!
|
||||
first-visible-block
|
||||
(fn []
|
||||
(selection/action-bar))
|
||||
{:id :selection-action-bar
|
||||
:content-props {:side "top"
|
||||
:class "!py-0 !px-0 !border-none"}
|
||||
:auto-side? false
|
||||
:align :start}))))
|
||||
|
||||
(defmethod events/handle :editor/hide-action-bar []
|
||||
(shui/popup-hide! :selection-action-bar))
|
||||
|
||||
(defmethod events/handle :user/logout [[_]]
|
||||
(file-sync-handler/reset-session-graphs)
|
||||
(sync/remove-all-pwd!)
|
||||
(file-sync-handler/reset-user-state!)
|
||||
(login/sign-out!))
|
||||
|
||||
(defmethod events/handle :user/login [[_ host-ui?]]
|
||||
(if (or host-ui? (not util/electron?))
|
||||
(js/window.open config/LOGIN-URL)
|
||||
(if (mobile-util/native-platform?)
|
||||
(route-handler/redirect! {:to :user-login})
|
||||
(login/open-login-modal!))))
|
||||
|
||||
(defmethod events/handle :whiteboard/onboarding [[_ opts]]
|
||||
(shui/dialog-open!
|
||||
(fn [{:keys [close]}] (whiteboard/onboarding-welcome close))
|
||||
(merge {:close-btn? false
|
||||
:center? true
|
||||
:close-backdrop? false} opts)))
|
||||
|
||||
(defn- enable-beta-features!
|
||||
[]
|
||||
(when-not (false? (state/enable-sync?)) ; user turns it off
|
||||
(file-sync-handler/set-sync-enabled! true)))
|
||||
|
||||
;; TODO: separate rtc and file-based implementation
|
||||
(defmethod events/handle :user/fetch-info-and-graphs [[_]]
|
||||
(state/set-state! [:ui/loading? :login] false)
|
||||
(async/go
|
||||
(let [result (async/<! (sync/<user-info sync/remoteapi))]
|
||||
(cond
|
||||
(instance? ExceptionInfo result)
|
||||
nil
|
||||
(map? result)
|
||||
(do
|
||||
(state/set-user-info! result)
|
||||
(when-let [uid (user-handler/user-uuid)]
|
||||
(sentry-event/set-user! uid))
|
||||
(let [status (if (user-handler/alpha-or-beta-user?) :welcome :unavailable)]
|
||||
(when (and (= status :welcome) (user-handler/logged-in?))
|
||||
(enable-beta-features!)
|
||||
(async/<! (p->c (rtc-handler/<get-remote-graphs)))
|
||||
(async/<! (file-sync-handler/load-session-graphs))
|
||||
(p/let [repos (repo-handler/refresh-repos!)]
|
||||
(when-let [repo (state/get-current-repo)]
|
||||
(when (some #(and (= (:url %) repo)
|
||||
(vector? (:sync-meta %))
|
||||
(util/uuid-string? (first (:sync-meta %)))
|
||||
(util/uuid-string? (second (:sync-meta %)))) repos)
|
||||
(sync/<sync-start)))))
|
||||
(file-sync/maybe-onboarding-show status)))))))
|
||||
|
||||
(defmethod events/handle :file-sync/onboarding-tip [[_ type opts]]
|
||||
(let [type (keyword type)]
|
||||
(when-not (config/db-based-graph? (state/get-current-repo))
|
||||
(shui/dialog-open!
|
||||
(file-sync/make-onboarding-panel type)
|
||||
(merge {:close-btn? false
|
||||
:center? true
|
||||
:close-backdrop? (not= type :welcome)} opts)))))
|
||||
@@ -2,20 +2,37 @@
|
||||
"Events that are only for file graphs"
|
||||
(:require [clojure.core.async :as async]
|
||||
[clojure.set :as set]
|
||||
[clojure.string :as string]
|
||||
[frontend.components.diff :as diff]
|
||||
[frontend.components.encryption :as encryption]
|
||||
[frontend.components.file-based.git :as git-component]
|
||||
[frontend.components.file-sync :as file-sync]
|
||||
[frontend.config :as config]
|
||||
[frontend.context.i18n :refer [t]]
|
||||
[frontend.db :as db]
|
||||
[frontend.handler.events :as events]
|
||||
[frontend.handler.page :as page-handler]
|
||||
[frontend.handler.repo :as repo-handler]
|
||||
[frontend.handler.file-based.nfs :as nfs-handler]
|
||||
[frontend.handler.common :as common-handler]
|
||||
[frontend.handler.property :as property-handler]
|
||||
[frontend.fs :as fs]
|
||||
[frontend.fs.sync :as sync]
|
||||
[frontend.handler.common :as common-handler]
|
||||
[frontend.handler.events :as events]
|
||||
[frontend.handler.file-based.file :as file-handler]
|
||||
[frontend.handler.file-based.nfs :as nfs-handler]
|
||||
[frontend.handler.file-sync :as file-sync-handler]
|
||||
[frontend.handler.notification :as notification]
|
||||
[frontend.handler.page :as page-handler]
|
||||
[frontend.handler.property :as property-handler]
|
||||
[frontend.handler.repo :as repo-handler]
|
||||
[frontend.handler.route :as route-handler]
|
||||
[frontend.handler.ui :as ui-handler]
|
||||
[frontend.handler.user :as user-handler]
|
||||
[frontend.mobile.graph-picker :as graph-picker]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.modules.shortcut.core :as st]
|
||||
[frontend.state :as state]
|
||||
[frontend.ui :as ui]
|
||||
[frontend.util :as util]
|
||||
[frontend.config :as config]
|
||||
[logseq.common.config :as common-config]
|
||||
[logseq.shui.ui :as shui]
|
||||
[promesa.core :as p]
|
||||
[rum.core :as rum]))
|
||||
|
||||
(defmethod events/handle :graph/ask-for-re-index [[_ *multiple-windows? ui]]
|
||||
@@ -23,32 +40,32 @@
|
||||
;; ui - custom message to show on asking for re-index
|
||||
(if (and (util/atom? *multiple-windows?) @*multiple-windows?)
|
||||
(shui/dialog-open!
|
||||
[:div
|
||||
(when (not (nil? ui)) ui)
|
||||
[:p (t :re-index-multiple-windows-warning)]])
|
||||
[:div
|
||||
(when (not (nil? ui)) ui)
|
||||
[:p (t :re-index-multiple-windows-warning)]])
|
||||
|
||||
(shui/dialog-open!
|
||||
[:div {:style {:max-width 700}}
|
||||
(when (not (nil? ui)) ui)
|
||||
[:p (t :re-index-discard-unsaved-changes-warning)]
|
||||
[:div.flex.justify-end.pt-2
|
||||
(ui/button
|
||||
(t :yes)
|
||||
:autoFocus "on"
|
||||
:class "ui__modal-enter"
|
||||
:on-click (fn []
|
||||
(shui/dialog-close!)
|
||||
(state/pub-event! [:graph/re-index])))]])))
|
||||
[:div {:style {:max-width 700}}
|
||||
(when (not (nil? ui)) ui)
|
||||
[:p (t :re-index-discard-unsaved-changes-warning)]
|
||||
[:div.flex.justify-end.pt-2
|
||||
(ui/button
|
||||
(t :yes)
|
||||
:autoFocus "on"
|
||||
:class "ui__modal-enter"
|
||||
:on-click (fn []
|
||||
(shui/dialog-close!)
|
||||
(state/pub-event! [:graph/re-index])))]])))
|
||||
|
||||
(defmethod events/handle :graph/re-index [[_]]
|
||||
;; Ensure the graph only has ONE window instance
|
||||
(when (config/local-file-based-graph? (state/get-current-repo))
|
||||
(async/go
|
||||
(async/<! (sync/<sync-stop))
|
||||
(repo-handler/re-index!
|
||||
nfs-handler/rebuild-index!
|
||||
#(do (page-handler/create-today-journal!)
|
||||
(events/file-sync-restart!))))))
|
||||
(async/<! (sync/<sync-stop))
|
||||
(repo-handler/re-index!
|
||||
nfs-handler/rebuild-index!
|
||||
#(do (page-handler/create-today-journal!)
|
||||
(events/file-sync-restart!))))))
|
||||
|
||||
(defn set-block-query-properties!
|
||||
[block-id all-properties key add?]
|
||||
@@ -118,5 +135,221 @@
|
||||
(set all-properties))
|
||||
shown-properties (set/intersection (set all-properties) shown-properties)]
|
||||
(shui/dialog-open!
|
||||
(query-properties-settings block shown-properties all-properties)
|
||||
{})))
|
||||
(query-properties-settings block shown-properties all-properties)
|
||||
{})))
|
||||
|
||||
(defmethod events/handle :modal/set-git-username-and-email [[_ _content]]
|
||||
(shui/dialog-open! git-component/set-git-username-and-email))
|
||||
|
||||
(defmethod events/handle :file/not-matched-from-disk [[_ path disk-content db-content]]
|
||||
(when-let [repo (state/get-current-repo)]
|
||||
(shui/dialog-open!
|
||||
#(diff/local-file repo path disk-content db-content)
|
||||
{:label "diff__cp"})))
|
||||
|
||||
(defmethod events/handle :modal/display-file-version-selector [[_ versions path get-content]]
|
||||
(shui/dialog-open!
|
||||
#(git-component/file-version-selector versions path get-content)))
|
||||
|
||||
(defmethod events/handle :modal/remote-encryption-input-pw-dialog [[_ repo-url remote-graph-info type opts]]
|
||||
(shui/dialog-open!
|
||||
(encryption/input-password
|
||||
repo-url nil (merge
|
||||
(assoc remote-graph-info
|
||||
:type (or type :create-pwd-remote)
|
||||
:repo repo-url)
|
||||
opts))
|
||||
{:center? true :close-btn? false :close-backdrop? false}))
|
||||
|
||||
(defmethod events/handle :graph/pull-down-remote-graph [[_ graph dir-name]]
|
||||
(if (mobile-util/native-ios?)
|
||||
(when-let [graph-name (or dir-name (:GraphName graph))]
|
||||
(let [graph-name (util/safe-sanitize-file-name graph-name)]
|
||||
(if (string/blank? graph-name)
|
||||
(notification/show! "Illegal graph folder name.")
|
||||
|
||||
;; Create graph directory under Logseq document folder (local)
|
||||
(when-let [root (state/get-local-container-root-url)]
|
||||
(let [graph-path (graph-picker/validate-graph-dirname root graph-name)]
|
||||
(->
|
||||
(p/let [exists? (fs/dir-exists? graph-path)]
|
||||
(let [overwrite? (if exists?
|
||||
(js/confirm (str "There's already a directory with the name \"" graph-name "\", do you want to overwrite it? Make sure to backup it first if you're not sure about it."))
|
||||
true)]
|
||||
(if overwrite?
|
||||
(p/let [_ (fs/mkdir-if-not-exists graph-path)]
|
||||
(nfs-handler/ls-dir-files-with-path!
|
||||
graph-path
|
||||
{:ok-handler (fn []
|
||||
(file-sync-handler/init-remote-graph graph-path graph)
|
||||
(js/setTimeout (fn [] (repo-handler/refresh-repos!)) 200))}))
|
||||
(let [graph-name (-> (js/prompt "Please specify a new directory name to download the graph:")
|
||||
str
|
||||
string/trim)]
|
||||
(when-not (string/blank? graph-name)
|
||||
(state/pub-event! [:graph/pull-down-remote-graph graph graph-name]))))))
|
||||
(p/catch (fn [^js e]
|
||||
(notification/show! (str e) :error)
|
||||
(js/console.error e)))))))))
|
||||
(when (:GraphName graph)
|
||||
(shui/dialog-open!
|
||||
(file-sync/pick-dest-to-sync-panel graph)))))
|
||||
|
||||
(defmethod events/handle :graph/pick-page-histories [[_ graph-uuid page-name]]
|
||||
(shui/dialog-open!
|
||||
(file-sync/pick-page-histories-panel graph-uuid page-name)
|
||||
{:id :page-histories :label "modal-page-histories"}))
|
||||
|
||||
(defmethod events/handle :file-sync/maybe-onboarding-show [[_ type]]
|
||||
(file-sync/maybe-onboarding-show type))
|
||||
|
||||
(defmethod events/handle :file-sync/storage-exceed-limit [[_]]
|
||||
(notification/show! "file sync storage exceed limit" :warning false)
|
||||
(events/file-sync-stop!))
|
||||
|
||||
(defmethod events/handle :file-sync/graph-count-exceed-limit [[_]]
|
||||
(notification/show! "file sync graph count exceed limit" :warning false)
|
||||
(events/file-sync-stop!))
|
||||
|
||||
(defmethod events/handle :graph/dir-gone [[_ dir]]
|
||||
(state/pub-event! [:notification/show
|
||||
{:content (str "The directory " dir " has been renamed or deleted, the editor will be disabled for this graph, you can unlink the graph.")
|
||||
:status :error
|
||||
:clear? false}])
|
||||
(state/update-state! :file/unlinked-dirs (fn [dirs] (conj dirs dir))))
|
||||
|
||||
(defmethod events/handle :graph/dir-back [[_ repo dir]]
|
||||
(when (contains? (:file/unlinked-dirs @state/state) dir)
|
||||
(notification/clear-all!)
|
||||
(state/pub-event! [:notification/show
|
||||
{:content (str "The directory " dir " has been back, you can edit your graph now.")
|
||||
:status :success
|
||||
:clear? true}])
|
||||
(state/update-state! :file/unlinked-dirs (fn [dirs] (disj dirs dir)))
|
||||
(when (= dir (config/get-repo-dir repo))
|
||||
(fs/watch-dir! dir))))
|
||||
|
||||
(defmethod events/handle :ui/notify-skipped-downloading-files [[_ paths]]
|
||||
(notification/show!
|
||||
[:div
|
||||
[:div.mb-4
|
||||
[:div.font-semibold.mb-4.text-xl "It seems that some of your filenames are in the outdated format."]
|
||||
[:p
|
||||
"The files below that have reserved characters can't be saved on this device."]
|
||||
[:div.overflow-y-auto.max-h-96
|
||||
[:ol.my-2
|
||||
(for [path paths]
|
||||
[:li path])]]
|
||||
|
||||
[:div
|
||||
[:p
|
||||
"Check " [:a {:href "https://docs.logseq.com/#/page/logseq%20file%20and%20folder%20naming%20rules"
|
||||
:target "_blank"}
|
||||
"Logseq file and folder naming rules"]
|
||||
" for more details."]]]]
|
||||
:warning
|
||||
false))
|
||||
|
||||
(defmethod events/handle :graph/setup-a-repo [[_ opts]]
|
||||
(let [opts' (merge {:picked-root-fn #(state/close-modal!)
|
||||
:native-icloud? (not (string/blank? (state/get-icloud-container-root-url)))
|
||||
:logged? (user-handler/logged-in?)} opts)]
|
||||
(if (mobile-util/native-ios?)
|
||||
(shui/dialog-open!
|
||||
#(graph-picker/graph-picker-cp opts')
|
||||
{:label "graph-setup"})
|
||||
(page-handler/ls-dir-files! st/refresh! opts'))))
|
||||
|
||||
(defmethod events/handle :file/alter [[_ repo path content]]
|
||||
(p/let [_ (file-handler/alter-file repo path content {:from-disk? true})]
|
||||
(ui-handler/re-render-root!)))
|
||||
|
||||
(rum/defcs file-id-conflict-item <
|
||||
(rum/local false ::resolved?)
|
||||
[state repo file data]
|
||||
(let [resolved? (::resolved? state)
|
||||
id (last (:assertion data))]
|
||||
[:li {:key file}
|
||||
[:div
|
||||
[:a {:on-click #(js/window.apis.openPath file)} file]
|
||||
(if @resolved?
|
||||
[:div.flex.flex-row.items-center
|
||||
(ui/icon "circle-check" {:style {:font-size 20}})
|
||||
[:div.ml-1 "Resolved"]]
|
||||
[:div
|
||||
[:p
|
||||
(str "It seems that another whiteboard file already has the ID \"" id
|
||||
"\". You can fix it by changing the ID in this file with another UUID.")]
|
||||
[:p
|
||||
"Or, let me"
|
||||
(ui/button "Fix"
|
||||
:on-click (fn []
|
||||
(let [dir (config/get-repo-dir repo)]
|
||||
(p/let [content (fs/read-file dir file)]
|
||||
(let [new-content (string/replace content (str id) (str (random-uuid)))]
|
||||
(p/let [_ (fs/write-file! repo
|
||||
dir
|
||||
file
|
||||
new-content
|
||||
{})]
|
||||
(reset! resolved? true))))))
|
||||
:class "inline mx-1")
|
||||
"it."]])]]))
|
||||
|
||||
(defmethod events/handle :file/parse-and-load-error [[_ repo parse-errors]]
|
||||
(state/pub-event! [:notification/show
|
||||
{:content
|
||||
[:div
|
||||
[:h2.title "Oops. These files failed to import to your graph:"]
|
||||
[:ol.my-2
|
||||
(for [[file error] parse-errors]
|
||||
(let [data (ex-data error)]
|
||||
(cond
|
||||
(and (common-config/whiteboard? file)
|
||||
(= :transact/upsert (:error data))
|
||||
(uuid? (last (:assertion data))))
|
||||
(rum/with-key (file-id-conflict-item repo file data) file)
|
||||
|
||||
:else
|
||||
(do
|
||||
(state/pub-event! [:capture-error {:error error
|
||||
:payload {:type :file/parse-and-load-error}}])
|
||||
[:li.my-1 {:key file}
|
||||
[:a {:on-click #(js/window.apis.openPath file)} file]
|
||||
[:p (.-message error)]]))))]
|
||||
[:p "Don't forget to re-index your graph when all the conflicts are resolved."]]
|
||||
:status :error}]))
|
||||
|
||||
(defmethod events/handle :file-sync-graph/restore-file [[_ graph page-entity content]]
|
||||
(when (db/get-db graph)
|
||||
(let [file (:block/file page-entity)]
|
||||
(when-let [path (:file/path file)]
|
||||
(when (and (not= content (:file/content file))
|
||||
(:file/content file))
|
||||
(sync/add-new-version-file graph path (:file/content file)))
|
||||
(p/let [_ (file-handler/alter-file graph
|
||||
path
|
||||
content
|
||||
{:re-render-root? true
|
||||
:skip-compare? true})]
|
||||
(state/close-modal!)
|
||||
(route-handler/redirect! {:to :page
|
||||
:path-params {:name (:block/name page-entity)}}))))))
|
||||
|
||||
(defmethod events/handle :sync/create-remote-graph [[_ current-repo]]
|
||||
(let [graph-name (js/decodeURI (util/node-path.basename current-repo))]
|
||||
(async/go
|
||||
(async/<! (sync/<sync-stop))
|
||||
(state/set-state! [:ui/loading? :graph/create-remote?] true)
|
||||
(when-let [GraphUUID (get (async/<! (file-sync-handler/create-graph graph-name)) 2)]
|
||||
(async/<! (sync/<sync-start))
|
||||
(state/set-state! [:ui/loading? :graph/create-remote?] false)
|
||||
;; update existing repo
|
||||
(state/set-repos! (map (fn [r]
|
||||
(if (= (:url r) current-repo)
|
||||
(assoc r
|
||||
:GraphUUID GraphUUID
|
||||
:GraphName graph-name
|
||||
:remote? true)
|
||||
r))
|
||||
(state/get-repos)))))))
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
.tippy-popper {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.tippy-popper[x-placement^=top] [x-arrow],
|
||||
.tippy-popper[x-placement^=top] [x-arrow].arrow-small,
|
||||
.tippy-popper[x-placement^=top] [x-arrow].arrow-big {
|
||||
border-top-color: var(--ls-tertiary-background-color);
|
||||
}
|
||||
|
||||
.tippy-popper[x-placement^=top] .tippy-tooltip.transparent-theme [x-circle],
|
||||
.tippy-popper[x-placement^=bottom] .tippy-tooltip.transparent-theme [x-circle],
|
||||
.tippy-popper[x-placement^=left] .tippy-tooltip.transparent-theme [x-circle],
|
||||
.tippy-popper[x-placement^=right] .tippy-tooltip.transparent-theme [x-circle],
|
||||
.tippy-popper .tippy-tooltip.transparent-theme {
|
||||
background-color: var(--ls-secondary-background-color);
|
||||
}
|
||||
|
||||
.tippy-popper[x-placement^=top] .tippy-tooltip.transparent-theme [x-arrow],
|
||||
.tippy-popper[x-placement^=top] .tippy-tooltip.transparent-theme [x-arrow].arrow-small,
|
||||
.tippy-popper[x-placement^=top] .tippy-tooltip.transparent-theme [x-arrow].arrow-big {
|
||||
border-top-color: var(--ls-secondary-background-color);
|
||||
}
|
||||
|
||||
.tippy-popper[x-placement^=bottom] [x-arrow],
|
||||
.tippy-popper[x-placement^=bottom] [x-arrow].arrow-small,
|
||||
.tippy-popper[x-placement^=bottom] [x-arrow].arrow-big {
|
||||
border-bottom-color: var(--ls-tertiary-background-color);
|
||||
}
|
||||
|
||||
.tippy-popper[x-placement^=bottom] .tippy-tooltip.transparent-theme [x-arrow],
|
||||
.tippy-popper[x-placement^=bottom] .tippy-tooltip.transparent-theme [x-arrow].arrow-small,
|
||||
.tippy-popper[x-placement^=bottom] .tippy-tooltip.transparent-theme [x-arrow].arrow-big {
|
||||
border-bottom-color: var(--ls-secondary-background-color);
|
||||
}
|
||||
|
||||
.tippy-popper[x-placement^=left] [x-arrow],
|
||||
.tippy-popper[x-placement^=left] [x-arrow].arrow-small,
|
||||
.tippy-popper[x-placement^=left] [x-arrow].arrow-big {
|
||||
border-left-color: var(--ls-tertiary-background-color);
|
||||
}
|
||||
|
||||
.tippy-popper[x-placement^=left] .tippy-tooltip.transparent-theme [x-arrow],
|
||||
.tippy-popper[x-placement^=left] .tippy-tooltip.transparent-theme [x-arrow].arrow-small,
|
||||
.tippy-popper[x-placement^=left] .tippy-tooltip.transparent-theme [x-arrow].arrow-big {
|
||||
border-left-color: var(--ls-secondary-background-color);
|
||||
}
|
||||
|
||||
.tippy-popper[x-placement^=right] [x-arrow],
|
||||
.tippy-popper[x-placement^=right] [x-arrow].arrow-small,
|
||||
.tippy-popper[x-placement^=right] [x-arrow].arrow-big {
|
||||
border-right-color: var(--ls-tertiary-background-color);
|
||||
}
|
||||
|
||||
.tippy-popper[x-placement^=right] .tippy-tooltip.transparent-theme [x-arrow],
|
||||
.tippy-popper[x-placement^=right] .tippy-tooltip.transparent-theme [x-arrow].arrow-small,
|
||||
.tippy-popper[x-placement^=right] .tippy-tooltip.transparent-theme [x-arrow].arrow-big {
|
||||
border-right-color: var(--ls-tertiary-background-color);
|
||||
}
|
||||
|
||||
.tippy-tooltip {
|
||||
@apply shadow border border-gray-07 dark:border-gray-05 px-2 py-1;
|
||||
|
||||
will-change: auto;
|
||||
color: var(--ls-primary-text-color, hsl(var(--foreground)));
|
||||
background-color: var(--lx-gray-03, var(--ls-tertiary-background-color, var(--rx-gray-03)));
|
||||
}
|
||||
|
||||
.tippy-tooltip [x-circle] {
|
||||
will-change: auto;
|
||||
}
|
||||
|
||||
.tippy-popper .tippy-tooltip.customized-theme * {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.tippy-popper .tippy-tooltip.monospace-theme {
|
||||
font-family: 'Fira Code', Monaco, Menlo, Consolas, 'COURIER NEW', monospace;
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
["emoji-mart" :as emoji-mart]
|
||||
["react-intersection-observer" :as react-intersection-observer]
|
||||
["react-textarea-autosize" :as TextareaAutosize]
|
||||
["react-tippy" :as react-tippy]
|
||||
["react-transition-group" :refer [CSSTransition TransitionGroup]]
|
||||
["react-virtuoso" :refer [Virtuoso VirtuosoGrid]]
|
||||
[cljs-bean.core :as bean]
|
||||
@@ -40,6 +39,7 @@
|
||||
[rum.core :as rum]))
|
||||
|
||||
(declare icon)
|
||||
(declare tooltip)
|
||||
|
||||
(defonce transition-group (r/adapt-class TransitionGroup))
|
||||
(defonce css-transition (r/adapt-class CSSTransition))
|
||||
@@ -47,7 +47,6 @@
|
||||
(defonce virtualized-list (r/adapt-class Virtuoso))
|
||||
(defonce virtualized-grid (r/adapt-class VirtuosoGrid))
|
||||
|
||||
(def Tippy (r/adapt-class (gobj/get react-tippy "Tooltip")))
|
||||
(def ReactTweetEmbed (r/adapt-class react-tweet-embed))
|
||||
(def useInView (gobj/get react-intersection-observer "useInView"))
|
||||
(defonce _emoji-init-data ((gobj/get emoji-mart "init") #js {:data emoji-data}))
|
||||
@@ -791,45 +790,6 @@
|
||||
:checked selected}]
|
||||
label])]))
|
||||
|
||||
(rum/defcs tippy < rum/static
|
||||
(rum/local false ::mounted?)
|
||||
[state {:keys [fixed-position? open? html] :as opts} child]
|
||||
(let [*mounted? (::mounted? state)
|
||||
manual (not= open? nil)
|
||||
open? (if manual open? @*mounted?)
|
||||
disabled? (not (state/enable-tooltip?))]
|
||||
(Tippy (->
|
||||
(merge {:arrow true
|
||||
:sticky true
|
||||
:delay 600
|
||||
:theme "customized"
|
||||
:disabled disabled?
|
||||
:unmountHTMLWhenHide true
|
||||
:open (if disabled? false open?)
|
||||
:trigger (if manual "manual" "mouseenter focus")
|
||||
;; See https://github.com/tvkhoa/react-tippy/issues/13
|
||||
:popperOptions {:modifiers {:flip {:enabled (not fixed-position?)}
|
||||
:hide {:enabled false}
|
||||
:preventOverflow {:enabled false}}}
|
||||
:onShow #(when-not (or (state/editing?)
|
||||
@(:ui/scrolling? @state/state))
|
||||
(reset! *mounted? true))
|
||||
:onHide #(reset! *mounted? false)}
|
||||
opts)
|
||||
(assoc :html (or
|
||||
(when open?
|
||||
(try
|
||||
(when html
|
||||
(if (fn? html)
|
||||
(html)
|
||||
[:div.px-2.py-1
|
||||
html]))
|
||||
(catch :default e
|
||||
(log/error :exception e)
|
||||
[:div])))
|
||||
[:div {:key "tippy"} ""])))
|
||||
(rum/fragment {:key "tippy-children"} child))))
|
||||
|
||||
(rum/defcs slider < rum/reactive
|
||||
{:init (fn [state]
|
||||
(assoc state ::value (atom (first (:rum/args state)))))}
|
||||
@@ -911,18 +871,13 @@
|
||||
|
||||
(rum/defc with-shortcut < rum/reactive
|
||||
< {:key-fn (fn [key pos] (str "shortcut-" key pos))}
|
||||
[shortcut-key position content]
|
||||
[shortcut-key _position content]
|
||||
(let [shortcut-tooltip? (state/sub :ui/shortcut-tooltip?)
|
||||
enabled-tooltip? (state/enable-tooltip?)]
|
||||
(if (and enabled-tooltip? shortcut-tooltip?)
|
||||
(tippy
|
||||
{:html [:div.text-sm.font-medium (keyboard-shortcut-from-config shortcut-key)]
|
||||
:interactive true
|
||||
:position position
|
||||
:theme "monospace"
|
||||
:delay [1000, 100]
|
||||
:arrow true}
|
||||
content)
|
||||
(tooltip content
|
||||
[:div.text-sm.font-medium (keyboard-shortcut-from-config shortcut-key)]
|
||||
{:trigger-props {:as-child true}})
|
||||
content)))
|
||||
|
||||
(rum/defc progress-bar
|
||||
@@ -1020,11 +975,14 @@
|
||||
:small? true)]]))
|
||||
|
||||
(rum/defc tooltip
|
||||
[trigger tooltip-content & {:keys [trigger-props]}]
|
||||
[trigger tooltip-content & {:keys [portal? root-props trigger-props content-props]}]
|
||||
(shui/tooltip-provider
|
||||
(shui/tooltip
|
||||
(shui/tooltip-trigger trigger-props trigger)
|
||||
(shui/tooltip-content tooltip-content))))
|
||||
(shui/tooltip root-props
|
||||
(shui/tooltip-trigger (merge {:as-child true} trigger-props) trigger)
|
||||
(if (not (false? portal?))
|
||||
(shui/tooltip-portal
|
||||
(shui/tooltip-content content-props tooltip-content))
|
||||
(shui/tooltip-content content-props tooltip-content)))))
|
||||
|
||||
(rum/defc DelDateButton
|
||||
[on-delete]
|
||||
|
||||
23
src/main/frontend/worker/db/fix.cljs
Normal file
23
src/main/frontend/worker/db/fix.cljs
Normal file
@@ -0,0 +1,23 @@
|
||||
(ns frontend.worker.db.fix
|
||||
"fix db"
|
||||
(:require [datascript.core :as d]
|
||||
[logseq.db.sqlite.util :as sqlite-util]))
|
||||
|
||||
(defn check-and-fix-schema!
|
||||
[repo conn]
|
||||
(let [schema (sqlite-util/get-schema repo)
|
||||
db-schema (:schema @conn)
|
||||
diffs (->> (keep (fn [[k v]]
|
||||
(let [schema-v (-> (get db-schema k)
|
||||
(dissoc :db/ident))
|
||||
schema-v' (cond-> schema-v
|
||||
(= (:db/cardinality schema-v) :db.cardinality/one)
|
||||
(dissoc :db/cardinality)
|
||||
(and (:db/index schema-v)
|
||||
(nil? (:db/index v)))
|
||||
(dissoc :db/index))]
|
||||
(when-not (or (= v schema-v') (= k :db/ident))
|
||||
(assoc v :db/ident k))))
|
||||
schema))]
|
||||
(when (seq diffs)
|
||||
(d/transact! conn diffs))))
|
||||
@@ -13,6 +13,7 @@
|
||||
[frontend.common.graph-view :as graph-view]
|
||||
[frontend.common.thread-api :as thread-api :refer [def-thread-api]]
|
||||
[frontend.worker.db-listener :as db-listener]
|
||||
[frontend.worker.db.fix :as db-fix]
|
||||
[frontend.worker.db.migrate :as db-migrate]
|
||||
[frontend.worker.db.validate :as worker-db-validate]
|
||||
[frontend.worker.export :as worker-export]
|
||||
@@ -332,6 +333,7 @@
|
||||
(search/create-tables-and-triggers! search-db)
|
||||
(let [schema (sqlite-util/get-schema repo)
|
||||
conn (sqlite-common-db/get-storage-conn storage schema)
|
||||
_ (db-fix/check-and-fix-schema! repo conn)
|
||||
_ (when datoms
|
||||
(let [data (map (fn [datom]
|
||||
[:db/add (:e datom) (:a datom) (:v datom)]) datoms)]
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
@import "codemirror/lib/codemirror.css";
|
||||
@import "codemirror/theme/solarized.css";
|
||||
@import "codemirror/addon/hint/show-hint.css";
|
||||
@import "react-tippy/dist/tippy.css";
|
||||
@import "pdfjs-dist/web/pdf_viewer.css";
|
||||
@import "resources/css/tabler-extension.css";
|
||||
@import "resources/css/codemirror.lsradix.css";
|
||||
|
||||
12
yarn.lock
12
yarn.lock
@@ -6584,11 +6584,6 @@ plugin-error@^2.0.1:
|
||||
dependencies:
|
||||
ansi-colors "^1.0.1"
|
||||
|
||||
popper.js@^1.11.1:
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
|
||||
integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==
|
||||
|
||||
posix-character-classes@^0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
|
||||
@@ -7249,13 +7244,6 @@ react-textarea-autosize@8.3.3:
|
||||
use-composed-ref "^1.0.0"
|
||||
use-latest "^1.0.0"
|
||||
|
||||
react-tippy@1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/react-tippy/-/react-tippy-1.4.0.tgz#e8a8b4085ec985e5c94fe128918b733b588a1465"
|
||||
integrity sha512-r/hM5XK9Ztr2ZY7IWKuRmISTlUPS/R6ddz6PO2EuxCgW+4JBcGZRPU06XcVPRDCOIiio8ryBQFrXMhFMhsuaHA==
|
||||
dependencies:
|
||||
popper.js "^1.11.1"
|
||||
|
||||
react-transition-group@4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.3.0.tgz#fea832e386cf8796c58b61874a3319704f5ce683"
|
||||
|
||||
Reference in New Issue
Block a user