(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/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/ [] (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)]]))