diff --git a/deploy_cdn.sh b/deploy_cdn.sh index 113a9fc93b..3665e4ab63 100755 --- a/deploy_cdn.sh +++ b/deploy_cdn.sh @@ -7,4 +7,4 @@ aws s3 sync ./resources/static/ s3://logseq-site/static/ aws cloudfront create-invalidation \ --distribution-id $AWS_LOGSEQ_CLOUDFRONT_ID \ - --paths "/static/js/main.js" "/static/js/sci.js" "/static/js/mldoc.min.js" "/static/style.css" "/static/index.html" "/static/img/logo.png" + --paths "/static/js/main.js" "/static/js/sci.js" "/static/js/mldoc.min.js" "/static/style.css" "/static/img/logo.png" diff --git a/readme.org b/readme.org index 28ca4ef906..e9586ee2ac 100644 --- a/readme.org +++ b/readme.org @@ -39,3 +39,6 @@ *** Or if you are using Emacs 1. M-x cider-jack-in 2. type ~(cljs-repl)~ in the repl +*** Notes + 1. deps should be synced between the two files: ~project.clj~ and ~deps.edn~. + ~project.clj~ is for dokku deployment. diff --git a/web/postcss.config.js b/web/postcss.config.js index c48c12a095..e20d65513e 100644 --- a/web/postcss.config.js +++ b/web/postcss.config.js @@ -2,7 +2,7 @@ const purgecss = require('@fullhuman/postcss-purgecss')({ // Specify the paths to all of the template files in your project content: [ - '../resources/static/index.html', + // '../resources/static/index.html', // '../resources/static/js/main.js', // etc. ], diff --git a/web/src/main/frontend/commands.cljs b/web/src/main/frontend/commands.cljs index b483cfd328..f53de3ad66 100644 --- a/web/src/main/frontend/commands.cljs +++ b/web/src/main/frontend/commands.cljs @@ -51,12 +51,12 @@ [["NOW" (->marker "NOW")] ["LATER" (->marker "LATER")] ["DONE" (->marker "DONE")] - ["TODO" (->marker "TODO")] - ["DOING" (->marker "DOING")] - ["WAIT" (->marker "WAIT")] - ["WAITING" (->marker "WAITING")] - ["IN-PROGRESS" (->marker "IN-PROGRESS")] - ["CANCELED" (->marker "CANCELED")] + ;; ["TODO" (->marker "TODO")] + ;; ["DOING" (->marker "DOING")] + ;; ["WAIT" (->marker "WAIT")] + ;; ["WAITING" (->marker "WAITING")] + ;; ["IN-PROGRESS" (->marker "IN-PROGRESS")] + ;; ["CANCELED" (->marker "CANCELED")] ["Tomorrow" (->page-reference (date/tomorrow))] ["Yesterday" (->page-reference (date/yesterday))] ["Today" (->page-reference (date/today))] diff --git a/web/src/main/frontend/components/docs.cljs b/web/src/main/frontend/components/docs.cljs index 69275a102a..14040ec8ee 100644 --- a/web/src/main/frontend/components/docs.cljs +++ b/web/src/main/frontend/components/docs.cljs @@ -14,8 +14,6 @@ [:div#docs [:h1.title {:style {:margin-bottom "0.25rem"}} "What's your preferred mode?"] - [:span.text-gray-500.text-sm.ml-1 - "It'll be used for new pages."] [:div.mt-4.ml-1 (ui/button diff --git a/web/src/main/frontend/components/draw.cljs b/web/src/main/frontend/components/draw.cljs index 32a3d54110..223dc96c36 100644 --- a/web/src/main/frontend/components/draw.cljs +++ b/web/src/main/frontend/components/draw.cljs @@ -409,27 +409,25 @@ (when (or (and file elements) (nil? file)) (excalidraw-component - (cond-> - {:width (get option :width width) - :height (get option :height height) - :on-resize (fn [] - (reset! layout [js/window.innerWidth js/window.innerHeight])) + {:width (get option :width width) + :height (get option :height height) + :on-resize (fn [] + (reset! layout [js/window.innerWidth js/window.innerHeight])) - :on-change (or (:on-change option) - (fn [elements state] - (when (not= (bean/->clj elements) - (bean/->clj @*elements)) - (reset! *unsaved? true)) - (set-last-elements! elements) - (set-last-app-state! state) - (reset! *elements elements))) - :options options - :user (bean/->js {:name (or (:user-name option) - (:name (state/get-me)) - (util/unique-id))}) - :on-username-change (fn [])} - (seq elements) - (assoc :initial-data elements)))) + :on-change (or (:on-change option) + (fn [elements state] + (when (not= (bean/->clj elements) + (bean/->clj @*elements)) + (reset! *unsaved? true)) + (set-last-elements! elements) + (set-last-app-state! state) + (reset! *elements elements))) + :options options + :user (bean/->js {:name (or (:user-name option) + (:name (state/get-me)) + (util/unique-id))}) + :on-username-change (fn []) + :initial-data (or elements #js [])})) [:div.absolute.top-4.left-4.hidden.md:block [:div.flex.flex-row.items-center [:a.mr-3 {:href "/" @@ -482,13 +480,16 @@ (let [loaded? (or (loaded?) (rum/react *loaded?)) current-repo (state/sub :git/current-repo)] - (if (and loaded? current-repo) + (if loaded? (let [current-file (rum/react *current-file) current-file (or current-file - (get-last-file current-repo))] - (let [key (str current-repo "-" - (or (and current-file (str "draw-" current-file)) - "draw-with-no-file"))] + (and current-repo + (get-last-file current-repo)))] + (let [key (if current-repo + (str current-repo "-" + (or (and current-file (str "draw-" current-file)) + "draw-with-no-file")) + "draw-with-no-file")] (rum/with-key (draw-inner option) key))) [:div.center [:span.lds-dual-ring.ml-3]]))) diff --git a/web/src/main/frontend/components/page.cljs b/web/src/main/frontend/components/page.cljs index ac362f8627..9f5b46c7fa 100644 --- a/web/src/main/frontend/components/page.cljs +++ b/web/src/main/frontend/components/page.cljs @@ -193,11 +193,11 @@ [:div {:key "page-references"} (reference/references page-name false)])])))) -(defonce layout (atom [js/window.innerWidth js/window.innerHeight])) +(defonce layout (atom [js/window.outerWidth js/window.outerHeight])) (defonce graph-ref (atom nil)) (rum/defcs global-graph < rum/reactive - (rum/local false ::show-journal?) + (rum/local true ::show-journal?) [state] (let [show-journal? (get state ::show-journal?) theme (state/sub :ui/theme) diff --git a/web/src/main/frontend/components/right_sidebar.cljs b/web/src/main/frontend/components/right_sidebar.cljs index cbe1b51879..4c8fe4ba1b 100644 --- a/web/src/main/frontend/components/right_sidebar.cljs +++ b/web/src/main/frontend/components/right_sidebar.cljs @@ -108,10 +108,11 @@ theme (:ui/theme @state/state) page (get-page match) graph (db/build-page-graph page theme)] - [:div.sidebar-item.flex-col.flex-1 - (ui/force-graph-2d (graph/build-graph-opts graph dark? - {:width 600 - :height 600}))])) + (when (seq (:nodes graph)) + [:div.sidebar-item.flex-col.flex-1 + (ui/force-graph-2d (graph/build-graph-opts graph dark? + {:width 600 + :height 600}))]))) (rum/defcs starred-cp < (rum/local true ::show?) diff --git a/web/src/main/frontend/components/search.cljs b/web/src/main/frontend/components/search.cljs index 5a9cdd424d..3c5b00807f 100644 --- a/web/src/main/frontend/components/search.cljs +++ b/web/src/main/frontend/components/search.cljs @@ -104,32 +104,32 @@ show-result? (boolean (seq search-result))] [:div#search.flex-1.flex.ml-0.md:ml-12 [:div.w-full.flex.md:ml-0 - [:label.sr-only {:for "search_field"} "Search"] - [:div#search-wrapper.relative.w-full.text-gray-400.focus-within:text-gray-600 - [:div.absolute.inset-y-0.flex.items-center.pointer-events-none.left-0 - [:svg.h-5.w-5 - {:view-box "0 0 20 20", :fill "currentColor"} - [:path - {:d - "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z", - :clip-rule "evenodd", - :fill-rule "evenodd"}]]] - [:input#search_field.block.w-full.h-full.pr-3.py-2.rounded-md.focus:outline-none.placeholder-gray-500.focus:placeholder-gray-400.sm:text-sm.bg-base-3.sm:bg-transparent + [:label.sr-only {:for "search_field"} "Search"] + [:div#search-wrapper.relative.w-full.text-gray-400.focus-within:text-gray-600 + [:div.absolute.inset-y-0.flex.items-center.pointer-events-none.left-0 + [:svg.h-5.w-5 + {:view-box "0 0 20 20", :fill "currentColor"} + [:path + {:d + "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z", + :clip-rule "evenodd", + :fill-rule "evenodd"}]]] + [:input#search_field.block.w-full.h-full.pr-3.py-2.rounded-md.focus:outline-none.placeholder-gray-500.focus:placeholder-gray-400.sm:text-sm.bg-base-3.sm:bg-transparent - {:style {:padding-left "2rem"} - :placeholder "Search" - :auto-complete "off" - :value search-q - :on-change (fn [e] - (let [value (util/evalue e)] - (if (string/blank? value) - (handler/clear-search!) - (do - (state/set-q! value) - (handler/search value)))))}] - (when-not (string/blank? search-q) - (ui/css-transition - {:class-names "fade" - :timeout {:enter 500 - :exit 300}} - (search-auto-complete search-result search-q)))]]])) + {:style {:padding-left "2rem"} + :placeholder "Search" + :auto-complete "off" + :value search-q + :on-change (fn [e] + (let [value (util/evalue e)] + (if (string/blank? value) + (handler/clear-search!) + (do + (state/set-q! value) + (handler/search value)))))}] + (when-not (string/blank? search-q) + (ui/css-transition + {:class-names "fade" + :timeout {:enter 500 + :exit 300}} + (search-auto-complete search-result search-q)))]]])) diff --git a/web/src/main/frontend/components/sidebar.cljs b/web/src/main/frontend/components/sidebar.cljs index b5dd5212c0..e99d9a16a1 100644 --- a/web/src/main/frontend/components/sidebar.cljs +++ b/web/src/main/frontend/components/sidebar.cljs @@ -20,7 +20,7 @@ (rum/defc logo [] [:div#logo.p-4 - [:a.opacity-50.hover:opacity-100 {:href "/docs"} + [:a.opacity-50.hover:opacity-100 {:href "/"} [:img.h-6.w-auto {:alt "Logseq", :src "/static/img/white_logo.png"}]]]) @@ -99,7 +99,10 @@ token (state/sub :encrypt/token)] [:div.max-w-7xl.mx-auto (cond - (not preferred-format) + (and (not logged?) (seq latest-journals)) + (journal/journals latest-journals) + + (and logged? (not preferred-format)) (widgets/choose-preferred-format) ;; TODO: delay this @@ -112,7 +115,7 @@ (widgets/set-personal-access-token) cloning? - (widgets/loading "Cloning ") + (widgets/loading "Cloning") (seq latest-journals) (journal/journals latest-journals) @@ -159,7 +162,8 @@ current-repo (state/sub :git/current-repo) theme (state/sub :ui/theme) white? (= "white" (state/sub :ui/theme)) - global-graph-pages? (= :graph (get-in route-match [:data :name]))] + global-graph-pages? (= :graph (get-in route-match [:data :name])) + logged? (:name me)] [:div {:class (if white? "white-theme" "dark-theme") :on-click (fn [] (handler/unhighlight-heading!))} @@ -202,7 +206,9 @@ :stroke-linejoin "round", :stroke-linecap "round"}]]] [:div.flex-1.px-4.flex.justify-between - (search/search) + (if current-repo + (search/search) + [:div.w-full.flex.md:ml-0]) [:div.ml-4.flex.items-center.md:ml-6 (when current-repo (widgets/sync-status)) [:div.repos.hidden.md:block @@ -220,7 +226,7 @@ (if-let [avatar (:avatar me)] [:img.h-7.w-7.rounded-full {:src avatar}] - [:div.h-7.w-7.rounded-full.bg-base-3])]) + [:div.h-7.w-7.rounded-full.bg-base-2])]) (let [logged? (:name me)] (->> [(when current-repo @@ -232,10 +238,10 @@ (when logged? {:title "All repos" :options {:href "/repos"}}) - (when logged? + (when current-repo {:title "All pages" :options {:href "/all-pages"}}) - (when logged? + (when current-repo {:title "All files" :options {:href "/all-files"}}) (when current-repo @@ -247,8 +253,6 @@ {:title "Feature request" :options {:href "https://github.com/logseq/logseq/issues/new" :target "_blank"}} - {:title "Logseq documentation" - :options {:href "/docs"}} {:title [:div.flex-row.flex.justify-between.items-center [:span "Join the community"] svg/discord] diff --git a/web/src/main/frontend/components/widgets.cljs b/web/src/main/frontend/components/widgets.cljs index 9c94728eb6..78148244b1 100644 --- a/web/src/main/frontend/components/widgets.cljs +++ b/web/src/main/frontend/components/widgets.cljs @@ -12,8 +12,6 @@ [:div [:h1.title {:style {:margin-bottom "0.25rem"}} "What's your preferred mode?"] - [:span.text-gray-500.text-sm.ml-1 - "It'll be used for new pages."] [:div.mt-4.ml-1 (ui/button diff --git a/web/src/main/frontend/config.cljs b/web/src/main/frontend/config.cljs index 2f5533e102..05b387abe7 100644 --- a/web/src/main/frontend/config.cljs +++ b/web/src/main/frontend/config.cljs @@ -162,3 +162,5 @@ (apply str (repeat n heading-pattern))))) (defonce default-draw-directory "draws") + +(defonce local-repo "local") diff --git a/web/src/main/frontend/db.cljs b/web/src/main/frontend/db.cljs index 32deb68127..275512ffc5 100644 --- a/web/src/main/frontend/db.cljs +++ b/web/src/main/frontend/db.cljs @@ -35,8 +35,10 @@ (defn get-repo-path [url] - (->> (take-last 2 (string/split url #"/")) - (string/join "/"))) + (if (string/starts-with? url "http") + (->> (take-last 2 (string/split url #"/")) + (string/join "/")) + url)) (defn datascript-db [repo] @@ -112,7 +114,11 @@ :me/email {} :me/avatar {} - ;; repo + ;; local, github, dropbox, etc. + :db/type {} + :encrypted-token {} + + ;; Git :repo/url {:db/unique :db.unique/identity} :repo/cloned? {} :git/latest-commit {} @@ -1305,39 +1311,42 @@ (swap! conns assoc files-db-name files-db-conn) (swap! conns assoc db-name db-conn) (listen-handler repo db-conn) - (d/transact! db-conn [(me-tx (d/db db-conn) me)]))) + (when me + (d/transact! db-conn [(me-tx (d/db db-conn) me)])))) (defn restore! [{:keys [repos] :as me} listen-handler restore-config-handler] - (doall - (for [{:keys [id url]} repos] - (let [repo url - db-name (datascript-files-db repo) - db-conn (d/create-conn files-db-schema)] - (swap! conns assoc db-name db-conn) - (-> - (p/let [stored (-> (.getItem localforage-instance db-name) - (p/then (fn [result] - result)) - (p/catch (fn [error] - nil))) - _ (when stored - (let [stored-db (string->db stored) - attached-db (d/db-with stored-db [(me-tx stored-db me)])] - (when (= (:schema stored-db) files-db-schema) ;; check for code update - (reset-conn! db-conn attached-db)))) - db-name (datascript-db repo) - db-conn (d/create-conn schema) - _ (swap! conns assoc db-name db-conn) - stored (.getItem localforage-instance db-name) - _ (if stored - (let [stored-db (string->db stored) - attached-db (d/db-with stored-db [(me-tx stored-db me)])] - (when (= (:schema stored-db) schema) ;; check for code update - (reset-conn! db-conn attached-db))) - (d/transact! db-conn [(me-tx (d/db db-conn) me)])) - _ (restore-config-handler repo)] - (listen-handler repo db-conn))))))) + (let [logged? (:name me)] + (doall + (for [{:keys [url]} repos] + (let [repo url + db-name (datascript-files-db repo) + db-conn (d/create-conn files-db-schema)] + (swap! conns assoc db-name db-conn) + (-> + (p/let [stored (-> (.getItem localforage-instance db-name) + (p/then (fn [result] + result)) + (p/catch (fn [error] + nil))) + _ (when stored + (let [stored-db (string->db stored) + attached-db (d/db-with stored-db [(me-tx stored-db me)])] + (when (= (:schema stored-db) files-db-schema) ;; check for code update + (reset-conn! db-conn attached-db)))) + db-name (datascript-db repo) + db-conn (d/create-conn schema) + _ (swap! conns assoc db-name db-conn) + stored (.getItem localforage-instance db-name) + _ (if stored + (let [stored-db (string->db stored) + attached-db (d/db-with stored-db [(me-tx stored-db me)])] + (when (= (:schema stored-db) schema) ;; check for code update + (reset-conn! db-conn attached-db))) + (when logged? + (d/transact! db-conn [(me-tx (d/db db-conn) me)]))) + _ (restore-config-handler repo)] + (listen-handler repo db-conn)))))))) (defn- build-edges [edges] diff --git a/web/src/main/frontend/handler.cljs b/web/src/main/frontend/handler.cljs index 932f03752d..4e89886483 100644 --- a/web/src/main/frontend/handler.cljs +++ b/web/src/main/frontend/handler.cljs @@ -184,6 +184,8 @@ path (date/current-journal-path format) file-path (str "/" path) default-content (default-month-journal-content format)] + (prn {:repo-dir repo-dir + :dir (str repo-dir "/journals")}) (p/let [_ (-> (fs/mkdir (str repo-dir "/journals")) (p/catch (fn [_e]))) file-exists? (fs/create-if-not-exists repo-dir file-path default-content)] @@ -594,8 +596,6 @@ "Git diff" :draw "Draw" - :docs - "Logseq documents" :else "Logseq")) @@ -1302,17 +1302,37 @@ (defn watch-for-date! [] (js/setInterval #(state/set-today! (date/today)) - 1000)) + 10000)) + +(defn setup-local-repo-if-not-exists! + [] + (if js/window.pfs + (let [repo config/local-repo] + (p/let [result (-> (fs/mkdir (str "/" repo)) + (p/catch (fn [_e] nil)))] + (state/set-current-repo! repo) + (db/start-db-conn! nil + repo + db-listen-to-tx!) + (create-month-journal-if-not-exists repo) + (create-config-file-if-not-exists repo))) + (js/setTimeout setup-local-repo-if-not-exists! 100))) (defn start! [render] - (let [me (and js/window.user (bean/->clj js/window.user))] - ;; async - (-> (p/all (db/restore! me db-listen-to-tx! restore-config!)) + (let [me (and js/window.user (bean/->clj js/window.user)) + logged? (:name me) + repos (if logged? + (:repos me) + [{:url config/local-repo}])] + (-> (p/all (db/restore! (assoc me :repos repos) db-listen-to-tx! restore-config!)) (p/then (fn [] (when me (set-state-kv! :me me)) (render) + (when (and (not logged?) + (not (db/get-conn config/local-repo))) + (setup-local-repo-if-not-exists!)) (watch-for-date!) (when me (when-let [object-key (:encrypt_object_key me)] @@ -1326,8 +1346,7 @@ (p/catch (fn [error] (println "Token decrypted failed") - (state/clear-encrypt-token!))))))) - ))))) + (state/clear-encrypt-token!)))))))))))) (defn load-docs! [] diff --git a/web/src/main/frontend/routes.cljs b/web/src/main/frontend/routes.cljs index 8fb61e233b..f8e174d2e1 100644 --- a/web/src/main/frontend/routes.cljs +++ b/web/src/main/frontend/routes.cljs @@ -5,8 +5,7 @@ [frontend.components.file :as file] [frontend.components.page :as page] [frontend.components.diff :as diff] - [frontend.components.draw :as draw] - [frontend.components.docs :as docs])) + [frontend.components.draw :as draw])) (def routes [["/" @@ -51,8 +50,4 @@ ["/draw" {:name :draw - :view draw/draw}] - - ["/docs" - {:name :doc - :view docs/docs}]]) + :view draw/draw}]]) diff --git a/web/src/main/frontend/state.cljs b/web/src/main/frontend/state.cljs index 26354e9067..b74347e2a8 100644 --- a/web/src/main/frontend/state.cljs +++ b/web/src/main/frontend/state.cljs @@ -293,10 +293,6 @@ (set-state! :git/clone-repo repo) (storage/set :git/clone-repo repo)) -(defn logged? - [] - (get-in @state [:me :name])) - (defn set-github-token! [token] (swap! state assoc-in [:me :access-token] token)) @@ -433,6 +429,10 @@ [] (:me @state)) +(defn logged? + [] + (some? (:name (get-me)))) + (defn set-draw! [value] (set-state! :draw? value)) diff --git a/web/src/main/frontend/sync/dropbox.cljs b/web/src/main/frontend/sync/dropbox.cljs new file mode 100644 index 0000000000..ceaf41debd --- /dev/null +++ b/web/src/main/frontend/sync/dropbox.cljs @@ -0,0 +1,56 @@ +(ns frontend.sync.dropbox + (:require ["dropbox" :as dropbox] + [goog.object :as gobj] + [frontend.sync.protocol :refer [Sync] :as sync] + [promesa.core :as p] + [cljs-bean.core :as bean])) + +;; Note: there's also a `DropboxTeam` +(defonce DropboxModule (gobj/get dropbox "Dropbox")) + +(defonce *dropbox-client (atom nil)) + +(defn upload-file + [client path contents] + (.filesUpload ^Object client + (bean/->js {:path path + :contents contents + :mode {".tag" "overwrite"} + :autorename true}))) + +(defrecord Dropbox [token] + Sync + (get-client [this] + (if-let [client @*dropbox-client] + client + (let [client (DropboxModule. #js {:accessToken token})] + (reset! *dropbox-client client) + client))) + (signed? [this] + true) + (get-dir [this path] + (p/let [resp (.filesListFolder ^Object (sync/get-client this) (bean/->js {:path path}))] + (bean/->clj resp))) + (get-more-dir [this cursor] + (p/let [resp (.filesListFolderContinue ^Object (sync/get-client this) (bean/->js {:cursor cursor}))] + (bean/->clj resp))) + (create-file [this path contents] + (upload-file ^Object (sync/get-client this) path contents)) + (update-file [this path contents] + (upload-file ^Object (sync/get-client this) path contents)) + (get-file-contents-and-metadata [this path] + (-> + (p/let [resp (.filesDownload ^Object (sync/get-client this) (bean/->js {:path path})) + file-binary (gobj/get resp "fileBinary") + contents (.toString file-binary) + last-modified-at (gobj/get resp "server_modified")] + {:contents contents + :last-modified-at last-modified-at}) + (p/catch + (fn [error] + ;; TODO: + (println "Dropbox get file " path " failed:") + (js/console.dir error)) + ))) + (delete-file [this path] + (.filesDelete ^Object (sync/get-client this) (bean/->js {:path path})))) diff --git a/web/src/main/frontend/sync/protocol.cljs b/web/src/main/frontend/sync/protocol.cljs new file mode 100644 index 0000000000..26dbe2a29c --- /dev/null +++ b/web/src/main/frontend/sync/protocol.cljs @@ -0,0 +1,11 @@ +(ns frontend.sync.protocol) + +(defprotocol Sync + (get-client [this]) + (signed? [this]) + (get-dir [this path]) + (get-more-dir [this more-state]) + (create-file [this path contents]) + (update-file [this path contents]) + (get-file-contents-and-metadata [this path]) + (delete-file [this path]))