123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- (ns frontend.components.header
- (:require ["path" :as path]
- [cljs-bean.core :as bean]
- [frontend.components.export :as export]
- [frontend.components.page-menu :as page-menu]
- [frontend.components.plugins :as plugins]
- [frontend.components.right-sidebar :as sidebar]
- [frontend.components.svg :as svg]
- [frontend.config :as config]
- [frontend.context.i18n :refer [t]]
- [frontend.fs.sync :as fs-sync]
- [frontend.handler :as handler]
- [frontend.handler.file-sync :as file-sync-handler]
- [frontend.handler.plugin :as plugin-handler]
- [frontend.handler.route :as route-handler]
- [frontend.handler.user :as user-handler]
- [frontend.handler.web.nfs :as nfs]
- [frontend.mobile.util :as mobile-util]
- [frontend.state :as state]
- [frontend.ui :as ui]
- [frontend.util :as util]
- [reitit.frontend.easy :as rfe]
- [rum.core :as rum]
- [cljs.core.async :as a]))
- (rum/defc home-button []
- (ui/with-shortcut :go/home "left"
- [:button.button.icon.inline
- {:on-click #(do
- (when (mobile-util/native-iphone?)
- (state/set-left-sidebar-open! false))
- (route-handler/redirect-to-home!))}
- (ui/icon "home" {:style {:fontSize ui/icon-size}})]))
- (rum/defc login < rum/reactive
- []
- (let [_ (state/sub :auth/id-token)]
- (when-not config/publishing?
- (if (user-handler/logged-in?)
- (ui/dropdown-with-links
- (fn [{:keys [toggle-fn]}]
- [:button.button
- {:on-click toggle-fn}
- [:span.text-sm.font-medium (user-handler/email)]])
- [{:title (t :logout)
- :options {:on-click user-handler/logout}}]
- {})
- [:button.button.text-sm.font-medium.block {:on-click #(js/window.open config/LOGIN-URL)}
- [:span (t :login)]]))))
- (rum/defcs file-sync-remote-graphs <
- (rum/local nil ::remote-graphs)
- [state]
- (let [*remote-graphs (::remote-graphs state)
- refresh-list-fn #(a/go (reset! *remote-graphs (a/<! (file-sync-handler/list-graphs))))]
- (when (nil? @*remote-graphs)
- (refresh-list-fn))
- [:div
- [:div.flex
- [:h1.title "Remote Graphs"]
- [:div
- {:on-click refresh-list-fn}
- svg/refresh]]
- [:p.text-sm "click to delete the selected graph"]
- [:ul
- (for [graph @*remote-graphs]
- [:li.mb-4
- [:a.font-medium
- {:on-click #(do (println "delete graph" (:GraphName graph) (:GraphUUID graph))
- (file-sync-handler/delete-graph (:GraphUUID graph)))}
- (:GraphName graph)]])]]))
- (rum/defcs file-sync <
- rum/reactive
- (rum/local nil ::existed-graphs)
- [state]
- (let [_ (state/sub :auth/id-token)
- sync-state (state/sub :file-sync/sync-state)
- not-syncing? (or (nil? sync-state) (fs-sync/sync-state--stopped? sync-state))
- *existed-graphs (::existed-graphs state)
- _ (rum/react file-sync-handler/refresh-file-sync-component)
- graph-txid-exists? (file-sync-handler/graph-txid-exists?)
- uploading-files (:current-local->remote-files sync-state)
- downloading-files (:current-remote->local-files sync-state)]
- (when-not config/publishing?
- (when (user-handler/logged-in?)
- (when-not (file-sync-handler/graph-txid-exists?)
- (a/go (reset! *existed-graphs (a/<! (file-sync-handler/list-graphs)))))
- (ui/dropdown-with-links
- (fn [{:keys [toggle-fn]}]
- (if not-syncing?
- [:button.button.icon.inline
- {:on-click toggle-fn}
- (ui/icon "cloud-off" {:style {:fontSize ui/icon-size}})]
- [:button.button.icon.inline
- {:on-click toggle-fn}
- (ui/icon "cloud" {:style {:fontSize ui/icon-size}})]))
- (cond-> []
- (not graph-txid-exists?)
- (concat (->> @*existed-graphs
- (filterv #(and (:GraphName %) (:GraphUUID %)))
- (mapv (fn [graph]
- {:title (:GraphName graph)
- :options {:on-click #(file-sync-handler/switch-graph (:GraphUUID graph))}})))
- [{:hr true}
- {:title "create graph"
- :options {:on-click #(file-sync-handler/create-graph (path/basename (state/get-current-repo)))}}])
- graph-txid-exists?
- (concat
- [{:title "toggle file sync"
- :options {:on-click #(if not-syncing? (fs-sync/sync-start) (fs-sync/sync-stop))}}
- {:title "remote graph list"
- :options {:on-click #(state/set-sub-modal! file-sync-remote-graphs)}}]
- [{:hr true}]
- (map (fn [f] {:title f
- :icon (ui/icon "arrow-narrow-up")}) uploading-files)
- (map (fn [f] {:title f
- :icon (ui/icon "arrow-narrow-down")}) downloading-files)
- (when sync-state
- (map-indexed (fn [i f] (:time f)
- {:title [:div {:key i} [:div (:path f)] [:div.opacity-50 (util/time-ago (:time f))]]})
- (take 10 (:history sync-state))))))
- (cond-> {}
- (not graph-txid-exists?) (assoc :links-header [:div.font-medium.text-sm.opacity-60.px-4.pt-2
- "Switch to:"])))))))
- (rum/defc left-menu-button < rum/reactive
- [{:keys [on-click]}]
- (ui/with-shortcut :ui/toggle-left-sidebar "bottom"
- [:button.#left-menu.cp__header-left-menu.button.icon
- {:on-click on-click}
- (ui/icon "menu-2" {:style {:fontSize ui/icon-size}})]))
- (rum/defc dropdown-menu < rum/reactive
- [{:keys [current-repo t]}]
- (let [page-menu (page-menu/page-menu nil)
- page-menu-and-hr (when (seq page-menu)
- (concat page-menu [{:hr true}]))]
- (ui/dropdown-with-links
- (fn [{:keys [toggle-fn]}]
- [:button.button.icon
- {:on-click toggle-fn}
- (ui/icon "dots" {:style {:fontSize ui/icon-size}})])
- (->>
- [(when (state/enable-editing?)
- {:title (t :settings)
- :options {:on-click state/open-settings!}
- :icon (ui/icon "settings")})
- (when plugin-handler/lsp-enabled?
- {:title (t :plugins)
- :options {:on-click #(plugin-handler/goto-plugins-dashboard!)}
- :icon (ui/icon "apps")})
- (when plugin-handler/lsp-enabled?
- {:title (t :themes)
- :options {:on-click #(plugins/open-select-theme!)}
- :icon (ui/icon "palette")})
- (when current-repo
- {:title (t :export-graph)
- :options {:on-click #(state/set-modal! export/export)}
- :icon (ui/icon "database-export")})
- (when (and current-repo (state/enable-editing?))
- {:title (t :import)
- :options {:href (rfe/href :import)}
- :icon (ui/icon "file-upload")})
- {:title [:div.flex-row.flex.justify-between.items-center
- [:span (t :join-community)]]
- :options {:href "https://discuss.logseq.com"
- :title (t :discourse-title)
- :target "_blank"}
- :icon (ui/icon "brand-discord")}] ;; TODO we need a discourse brand
- (concat page-menu-and-hr)
- (remove nil?))
- {})))
- (rum/defc back-and-forward
- []
- [:div.flex.flex-row
- (ui/with-shortcut :go/backward "bottom"
- [:button.it.navigation.nav-left.button.icon
- {:title "Go back" :on-click #(js/window.history.back)}
- (ui/icon "arrow-left" {:style {:fontSize ui/icon-size}})])
- (ui/with-shortcut :go/forward "bottom"
- [:button.it.navigation.nav-right.button.icon
- {:title "Go forward" :on-click #(js/window.history.forward)}
- (ui/icon "arrow-right" {:style {:fontSize ui/icon-size}})])])
- (rum/defc updater-tips-new-version
- [t]
- (let [[downloaded, set-downloaded] (rum/use-state nil)
- _ (rum/use-effect!
- (fn []
- (when-let [channel (and (util/electron?) "auto-updater-downloaded")]
- (let [callback (fn [_ args]
- (js/console.debug "[new-version downloaded] args:" args)
- (let [args (bean/->clj args)]
- (set-downloaded args)
- (state/set-state! :electron/auto-updater-downloaded args))
- nil)]
- (js/apis.addListener channel callback)
- #(js/apis.removeListener channel callback))))
- [])]
- (when downloaded
- [:div.cp__header-tips
- [:p (t :updater/new-version-install)
- [:a.restart.ml-2
- {:on-click #(handler/quit-and-install-new-version!)}
- (svg/reload 16) [:strong (t :updater/quit-and-install)]]]])))
- (rum/defc ^:large-vars/cleanup-todo header < rum/reactive
- [{:keys [open-fn current-repo default-home new-block-mode]}]
- (let [repos (->> (state/sub [:me :repos])
- (remove #(= (:url %) config/local-repo)))
- electron-mac? (and util/mac? (util/electron?))
- show-open-folder? (and (nfs/supported?)
- (or (empty? repos)
- (nil? (state/sub :git/current-repo)))
- (not (mobile-util/native-platform?))
- (not config/publishing?))
- left-menu (left-menu-button {:on-click (fn []
- (open-fn)
- (state/set-left-sidebar-open!
- (not (:ui/left-sidebar-open? @state/state))))})
- custom-home-page? (and (state/custom-home-page?)
- (= (state/sub-default-home-page) (state/get-current-page)))]
- [:div.cp__header#head
- {:class (util/classnames [{:electron-mac electron-mac?
- :native-ios (mobile-util/native-ios?)
- :native-android (mobile-util/native-android?)}])
- :on-double-click (fn [^js e]
- (when-let [target (.-target e)]
- (when (and (util/electron?)
- (.. target -classList (contains "cp__header")))
- (js/window.apis.toggleMaxOrMinActiveWindow))))
- :style {:fontSize 50}}
- [:div.l.flex
- (when-not (mobile-util/native-platform?)
- [left-menu
- (when current-repo ;; this is for the Search button
- (ui/with-shortcut :go/search "right"
- [:button.button.icon#search-button
- {:on-click #(do (when (or (mobile-util/native-android?)
- (mobile-util/native-iphone?))
- (state/set-left-sidebar-open! false))
- (state/pub-event! [:go/search]))}
- (ui/icon "search" {:style {:fontSize ui/icon-size}})]))])
- (when (mobile-util/native-platform?)
- (if (or (state/home?) custom-home-page?)
- left-menu
- (ui/with-shortcut :go/backward "bottom"
- [:button.it.navigation.nav-left.button.icon
- {:title "Go back" :on-click #(js/window.history.back)}
- (ui/icon "chevron-left" {:style {:fontSize 25}})])))]
- [:div.r.flex
- (when-not file-sync-handler/hiding-login&file-sync
- (file-sync))
- (when-not file-sync-handler/hiding-login&file-sync
- (login))
- (when plugin-handler/lsp-enabled?
- (plugins/hook-ui-items :toolbar))
- (when (and (not= (state/get-current-route) :home)
- (not custom-home-page?))
- (home-button))
- (when (util/electron?)
- (back-and-forward))
- (when-not (mobile-util/native-platform?)
- (new-block-mode))
- (when show-open-folder?
- [:a.text-sm.font-medium.button.icon.add-graph-btn.flex.items-center
- {:on-click #(route-handler/redirect! {:to :repo-add})}
- (ui/icon "folder-plus")
- (when-not config/mobile?
- [:span.ml-1 {:style {:margin-top (if electron-mac? 0 2)}}
- (t :on-boarding/add-graph)])])
- (when config/publishing?
- [:button.text-sm.font-medium.button {:href (rfe/href :graph)}
- (t :graph)])
- (dropdown-menu {:t t
- :current-repo current-repo
- :default-home default-home})
- (when (not (state/sub :ui/sidebar-open?))
- (sidebar/toggle))
- (updater-tips-new-version t)]]))
|