| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- (ns ^:no-doc frontend.handler.ui
- (:require [cljs-time.core :refer [plus days weeks]]
- [dommy.core :as dom]
- [frontend.util :as util]
- [frontend.db :as db]
- [frontend.db.model :as db-model]
- [frontend.config :as config]
- [frontend.state :as state]
- [frontend.storage :as storage]
- [frontend.fs :as fs]
- [frontend.loader :refer [load]]
- [goog.dom :as gdom]
- [goog.object :as gobj]
- [clojure.string :as string]
- [rum.core :as rum]
- [electron.ipc :as ipc]
- [promesa.core :as p]
- [logseq.common.path :as path]))
- ;; sidebars
- (def *right-sidebar-resized-at (atom (js/Date.now)))
- (defn persist-right-sidebar-width!
- [width]
- (state/set-state! :ui/sidebar-width width)
- (storage/set "ls-right-sidebar-width" width))
- (defn restore-right-sidebar-width!
- []
- (when-let [width (storage/get "ls-right-sidebar-width")]
- (state/set-state! :ui/sidebar-width width)))
- (defn close-left-sidebar!
- []
- (when-let [elem (gdom/getElement "close-left-bar")]
- (.click elem)))
- (defn toggle-right-sidebar!
- []
- (when-not (:ui/sidebar-open? @state/state) (restore-right-sidebar-width!))
- (state/toggle-sidebar-open?!))
- (defn persist-right-sidebar-state!
- []
- (let [sidebar-open? (:ui/sidebar-open? @state/state)
- data (if sidebar-open? {:blocks (:sidebar/blocks @state/state)
- :collapsed (:ui/sidebar-collapsed-blocks @state/state)
- :open? true} {:open? false})]
- (storage/set "ls-right-sidebar-state" data)))
- (defn restore-right-sidebar-state!
- []
- (when-let [data' (storage/get "ls-right-sidebar-state")]
- (let [{:keys [open? collapsed blocks]} data']
- (when open?
- (state/set-state! :ui/sidebar-open? open?)
- (state/set-state! :sidebar/blocks blocks)
- (state/set-state! :ui/sidebar-collapsed-blocks collapsed)
- (restore-right-sidebar-width!)))))
- (defn toggle-contents!
- []
- (when-let [current-repo (state/get-current-repo)]
- (let [id "contents"]
- (if (state/sidebar-block-exists? id)
- (state/sidebar-remove-block! id)
- (state/sidebar-add-block! current-repo id :contents)))))
- (defn toggle-help!
- []
- (when-let [current-repo (state/get-current-repo)]
- (let [id "help"]
- (if (state/sidebar-block-exists? id)
- (state/sidebar-remove-block! id)
- (state/sidebar-add-block! current-repo id :help)))))
- (defn toggle-settings-modal!
- []
- (when-not (:srs/mode? @state/state)
- (state/toggle-settings!)))
- ;; FIXME: re-render all embedded blocks since they will not be re-rendered automatically
- (defn re-render-root!
- ([]
- (re-render-root! {}))
- ([{:keys [clear-all-query-state?]
- :or {clear-all-query-state? false}}]
- {:post [(nil? %)]}
- (when-let [component (state/get-root-component)]
- (if clear-all-query-state?
- (db/clear-query-state!)
- (db/clear-query-state-without-refs-and-embeds!))
- (rum/request-render component))
- nil))
- (defn highlight-element!
- [fragment]
- (let [id (and
- (> (count fragment) 36)
- (subs fragment (- (count fragment) 36)))]
- (if (and id (util/uuid-string? id))
- (let [elements (array-seq (js/document.getElementsByClassName id))]
- (when (first elements)
- (util/scroll-to-element (gobj/get (first elements) "id")))
- (state/exit-editing-and-set-selected-blocks! elements))
- (when-let [element (gdom/getElement fragment)]
- (util/scroll-to-element fragment)
- (dom/add-class! element "block-highlight")
- (js/setTimeout #(dom/remove-class! element "block-highlight")
- 4000)))))
- (defn add-style-if-exists!
- []
- (when-let [style (or
- (state/get-custom-css-link)
- (some-> (db-model/get-custom-css)
- (config/expand-relative-assets-path)))]
- (util/add-style! style)))
- (defn reset-custom-css!
- []
- (when-let [el-style (gdom/getElement "logseq-custom-theme-id")]
- (dom/remove! el-style))
- (add-style-if-exists!))
- (def *js-execed (atom #{}))
- (defn exec-js-if-exists-&-allowed!
- [t]
- (when-let [href (or
- (state/get-custom-js-link)
- (config/get-custom-js-path))]
- (let [k (str "ls-js-allowed-" href)
- execed #(swap! *js-execed conj href)
- execed? (contains? @*js-execed href)
- ask-allow #(let [r (js/confirm (t :plugin/custom-js-alert))]
- (if r
- (storage/set k (js/Date.now))
- (storage/set k false))
- r)
- allowed! (storage/get k)
- should-ask? (or (nil? allowed!)
- (> (- (js/Date.now) allowed!) 604800000))]
- (when (and (not execed?)
- (not= false allowed!))
- (if (string/starts-with? href "http")
- (when (or (not should-ask?)
- (ask-allow))
- (load href #(do (js/console.log "[custom js]" href) (execed))))
- (let [repo-dir (config/get-repo-dir (state/get-current-repo))
- rpath (path/relative-path repo-dir href)]
- (p/let [exists? (fs/file-exists? repo-dir rpath)]
- (when exists?
- (util/p-handle
- (fs/read-file repo-dir rpath)
- #(when-let [scripts (and % (string/trim %))]
- (when-not (string/blank? scripts)
- (when (or (not should-ask?) (ask-allow))
- (try
- (js/eval scripts)
- (execed)
- (catch :default e
- (js/console.error "[custom js]" e)))))))))))))))
- (defn toggle-wide-mode!
- []
- (storage/set :ui/wide-mode (not (state/get-wide-mode?)))
- (state/toggle-wide-mode!))
- ;; auto-complete
- (defn auto-complete-prev
- [state e]
- (let [current-idx (get state :frontend.ui/current-idx)
- matched (first (:rum/args state))]
- (util/stop e)
- (cond
- (>= @current-idx 1)
- (swap! current-idx dec)
- (= @current-idx 0)
- (reset! current-idx (dec (count matched)))
- :else nil)
- (when-let [element (gdom/getElement (str "ac-" @current-idx))]
- (let [modal (gobj/get (gdom/getElement "ui__ac") "parentElement")
- height (or (gobj/get modal "offsetHeight") 300)
- scroll-top (- (gobj/get element "offsetTop") (/ height 2))]
- (set! (.-scrollTop modal) scroll-top)))))
- (defn auto-complete-next
- [state e]
- (let [current-idx (get state :frontend.ui/current-idx)
- matched (first (:rum/args state))]
- (util/stop e)
- (let [total (count matched)]
- (if (>= @current-idx (dec total))
- (reset! current-idx 0)
- (swap! current-idx inc)))
- (when-let [element (gdom/getElement (str "ac-" @current-idx))]
- (let [modal (gobj/get (gdom/getElement "ui__ac") "parentElement")
- height (or (gobj/get modal "offsetHeight") 300)
- scroll-top (- (gobj/get element "offsetTop") (/ height 2))]
- (set! (.-scrollTop modal) scroll-top)))))
- (defn auto-complete-complete
- [state e]
- (let [[matched {:keys [on-chosen on-enter]}] (:rum/args state)
- current-idx (get state :frontend.ui/current-idx)]
- (util/stop e)
- (if (and (seq matched)
- (> (count matched)
- @current-idx))
- (on-chosen (nth matched @current-idx) false)
- (and on-enter (on-enter state)))))
- (defn auto-complete-shift-complete
- [state e]
- (let [[matched {:keys [on-chosen on-shift-chosen on-enter]}] (:rum/args state)
- current-idx (get state :frontend.ui/current-idx)]
- (util/stop e)
- (if (and (seq matched)
- (> (count matched)
- @current-idx))
- ((or on-shift-chosen on-chosen) (nth matched @current-idx) false)
- (and on-enter (on-enter state)))))
- (defn auto-complete-open-link
- [state e]
- (let [[matched {:keys [on-chosen-open-link]}] (:rum/args state)]
- (when (and on-chosen-open-link (not (state/editing?)))
- (let [current-idx (get state :frontend.ui/current-idx)]
- (util/stop e)
- (when (and (seq matched)
- (> (count matched)
- @current-idx))
- (on-chosen-open-link (nth matched @current-idx) false))))))
- ;; date-picker
- ;; TODO: find a better way
- (def *internal-model (rum/cursor state/state :date-picker/date))
- (defn- non-edit-input?
- []
- (when-let [elem js/document.activeElement]
- (and (util/input? elem)
- (when-let [id (gobj/get elem "id")]
- (not (string/starts-with? id "edit-block-"))))))
- (defn- input-or-select?
- []
- (when-let [elem js/document.activeElement]
- (or (non-edit-input?)
- (util/select? elem))))
- (defn- inc-date [date n] (plus date (days n)))
- (defn- inc-week [date n] (plus date (weeks n)))
- (defn shortcut-complete
- [state e]
- (let [{:keys [on-change deadline-or-schedule?]} (last (:rum/args state))]
- (when (and on-change
- (not (input-or-select?)))
- (when-not deadline-or-schedule?
- (on-change e @*internal-model)))))
- (defn shortcut-prev-day
- [_state e]
- (when-not (input-or-select?)
- (util/stop e)
- (swap! *internal-model inc-date -1)))
- (defn shortcut-next-day
- [_state e]
- (when-not (input-or-select?)
- (util/stop e)
- (swap! *internal-model inc-date 1)))
- (defn shortcut-prev-week
- [_state e]
- (when-not (input-or-select?)
- (util/stop e)
- (swap! *internal-model inc-week -1)))
- (defn shortcut-next-week
- [_state e]
- (when-not (input-or-select?)
- (util/stop e)
- (swap! *internal-model inc-week 1)))
- (defn toggle-cards!
- []
- (if (:modal/show? @state/state)
- (state/close-modal!)
- (state/pub-event! [:modal/show-cards])))
- (defn open-new-window!
- "Open a new Electron window.
- No db cache persisting ensured. Should be handled by the caller."
- ([]
- (open-new-window! nil))
- ([repo]
- ;; TODO: find out a better way to open a new window with a different repo path. Using local storage for now
- ;; TODO: also write local storage with the current repo state, to make behavior consistent
- ;; then we can remove the `openNewWindowOfGraph` ipcMain call
- (when (string? repo) (storage/set :git/current-repo repo))
- (ipc/ipc "openNewWindow")))
|