浏览代码

Journal continued

Tienson Qin 5 年之前
父节点
当前提交
c7cd9422ba

+ 9 - 0
.projectile

@@ -0,0 +1,9 @@
+-/.git
+-/api/.cpcache
+-/api/.shadow-cljs/
+-/api/node_modules/
+-/web/.cpcache
+-/web/.shadow-cljs/
+-/web/node_modules/
+-/web/public/static/js/cljs-runtime/
+-/web/public/static/js/main.js

文件差异内容过多而无法显示
+ 0 - 0
web/public/static/css/org.css


+ 2 - 2
web/public/static/css/style.css

@@ -249,11 +249,11 @@ textarea {
     outline: none;
     outline: none;
 }
 }
 
 
-#content ol, #content ui {
+.content ol, .content ui {
     list-style: disc;
     list-style: disc;
     margin-left: 1em;
     margin-left: 1em;
 }
 }
 
 
-#content p, #content div {
+.content p, .content div {
     word-break: break-word;
     word-break: break-word;
 }
 }

文件差异内容过多而无法显示
+ 0 - 0
web/public/static/js/manifest.edn


+ 2 - 2
web/src/frontend/components/file.cljs

@@ -28,7 +28,7 @@
         suffix (last (string/split path #"\."))]
         suffix (last (string/split path #"\."))]
     (sidebar/sidebar
     (sidebar/sidebar
      (if (and suffix (contains? #{"md" "markdown" "org"} suffix))
      (if (and suffix (contains? #{"md" "markdown" "org"} suffix))
-       [:div#content
+       [:div.content
         [:a {:href (str "/file/" encoded-path "/edit")}
         [:a {:href (str "/file/" encoded-path "/edit")}
          "edit"]
          "edit"]
         (cond
         (cond
@@ -60,7 +60,7 @@
         [_encoded-path path] (get-path state)]
         [_encoded-path path] (get-path state)]
     (prn {:rows rows})
     (prn {:rows rows})
     (sidebar/sidebar
     (sidebar/sidebar
-     [:div#content
+     [:div.content
       [:h3.mb-2 (str "Update " path)]
       [:h3.mb-2 (str "Update " path)]
       [:textarea
       [:textarea
        {:rows rows
        {:rows rows

+ 38 - 46
web/src/frontend/components/journal.cljs

@@ -6,58 +6,50 @@
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.format :as format]
             [frontend.format :as format]
             [frontend.mixins :as mixins]
             [frontend.mixins :as mixins]
-            [frontend.db :as db]))
-
-(defonce content-atom (atom ""))
-(defonce edit?-atom (atom false))
-
-(defn today
-  []
-  (.toLocaleDateString (js/Date.) "default"
-                       (clj->js {:month "long"
-                                 :year "numeric"
-                                 :day "numeric"
-                                 :weekday "long"})))
-
+            [frontend.db :as db]
+            [frontend.state :as state]))
 
 
+(def edit-content (atom ""))
 (rum/defc editor-box <
 (rum/defc editor-box <
   (mixins/event-mixin
   (mixins/event-mixin
    (fn [state]
    (fn [state]
-     (mixins/close-when-esc-or-outside
+     (mixins/hide-when-esc-or-outside
       state
       state
-      edit?-atom
-      :on-close (fn []
-                  (reset! edit?-atom false)
-                  (handler/alter-file (util/current-journal-path) "Auto save" @content-atom false)))))
-  [content-atom]
-  (ui/textarea-autosize
-   {:on-change (fn [e]
-                 (reset! content-atom (util/evalue e)))
-    :default-value @content-atom
-    :auto-focus true
-    :style {:border "none"
-            :border-radius 0
-            :background "transparent"
-            :margin-top 12.5}}))
+      nil
+      :show-fn (fn []
+                 (:edit? @state/state))
+      :on-hide (fn []
+                 (handler/save-current-edit-journal! @edit-content)))))
+  [content]
+  [:div.flex-1
+   (ui/textarea-autosize
+    {:on-change (fn [e]
+                  (reset! edit-content (util/evalue e)))
+     :default-value content
+     :auto-focus true
+     :style {:border "none"
+             :border-radius 0
+             :background "transparent"
+             :margin-top 12.5}})])
+
+(rum/defc journal-cp < rum/reactive
+  [{:keys [uuid _title content] :as journal}]
+  (let [{:keys [edit? edit-journal]} (rum/react state/state)]
+    (if (and edit? (= uuid (:uuid edit-journal)))
+      (editor-box content)
+      [:div.flex-1 {:on-click (fn []
+                                (handler/edit-journal! content journal))
+                    :style {:padding 8
+                            :min-height 200}}
+       (util/raw-html (format/to-html content "org"))])))
 
 
-(rum/defcs journal < rum/reactive
+(rum/defcs journals < rum/reactive
   {:will-mount (fn [state]
   {:will-mount (fn [state]
-                 (reset! content-atom (db/get-current-journal))
+                 (handler/set-latest-journals!)
                  state)}
                  state)}
   [state]
   [state]
-  (let [content (rum/react content-atom)
-        edit? (rum/react edit?-atom)
-        show-textarea? (or edit? (string/blank? content))]
-    (when (and show-textarea? (not edit?))
-      (reset! edit?-atom true))
-    [:div#content
-     [:h1 {:style {:color "#202b33"
-                   :font-weight 450}}
-      (today)]
-     (if show-textarea?
-       (editor-box content-atom)
-       [:div.flex-1 {:on-click (fn []
-                                 (reset! edit?-atom true))
-                     :style {:padding 8
-                             :min-height 200}}
-        (util/raw-html (format/to-html content "org"))])]))
+  (let [{:keys [latest-journals]} (rum/react state/state)]
+    [:div#journals.content
+     (for [journal latest-journals]
+       [:div.journal {:key (cljs.core/random-uuid)}
+        (journal-cp journal)])]))

+ 1 - 1
web/src/frontend/components/sidebar.cljs

@@ -60,7 +60,7 @@
   (let [repos (db/get-repos)]
   (let [repos (db/get-repos)]
     [:div.max-w-7xl.mx-auto.px-4.sm:px-6.md:px-8
     [:div.max-w-7xl.mx-auto.px-4.sm:px-6.md:px-8
     (if (seq repos)
     (if (seq repos)
-      (journal/journal)
+      (journal/journals)
       (repo/add-repo))]))
       (repo/add-repo))]))
 
 
 (rum/defcs sidebar < (mixins/modal)
 (rum/defcs sidebar < (mixins/modal)

+ 33 - 4
web/src/frontend/db.cljs

@@ -5,7 +5,8 @@
             [datascript.transit :as dt]
             [datascript.transit :as dt]
             [frontend.format.org-mode :as org]
             [frontend.format.org-mode :as org]
             [frontend.format.org.block :as block]
             [frontend.format.org.block :as block]
-            [clojure.string :as string]))
+            [clojure.string :as string]
+            [frontend.utf8 :as utf8]))
 
 
 ;; TODO: don't persistent :github/token
 ;; TODO: don't persistent :github/token
 
 
@@ -305,9 +306,7 @@
   [repo-url file content]
   [repo-url file content]
   (if (string/blank? content)
   (if (string/blank? content)
     []
     []
-    (let [headings (-> content
-                       (org/parse-json)
-                       (util/json->clj))
+    (let [headings (org/->clj content)
           headings (block/extract-headings headings)]
           headings (block/extract-headings headings)]
       (map (fn [heading]
       (map (fn [heading]
              (assoc heading
              (assoc heading
@@ -400,6 +399,36 @@
   []
   []
   (get-file (util/current-journal-path)))
   (get-file (util/current-journal-path)))
 
 
+(defn get-latest-journals
+  ([]
+   (get-latest-journals {}))
+  ([{:keys [days content]
+     :or {days 7}}]
+   (when-let [content (or content (get-current-journal))]
+    (let [journal-path (util/current-journal-path)
+          content-arr (utf8/encode content)
+          end-pos (utf8/length content-arr)
+          blocks (take days (reverse (org/->clj content)))
+          headings (some->>
+                    blocks
+                    (filter (fn [block]
+                              (and (block/heading-block? block)
+                                   (= 1 (:level (second block))))))
+                    (map (fn [[_ {:keys [title meta]}]]
+                           {:title (last (first title))
+                            :file-path journal-path
+                            :start-pos (:pos meta)})))
+          [_ journals] (reduce (fn [[last-end-pos acc] heading]
+                                 (let [end-pos last-end-pos
+                                       acc (conj acc (assoc heading
+                                                            :uuid (cljs.core/random-uuid)
+                                                            :end-pos end-pos
+                                                            :content (utf8/substring content-arr
+                                                                                     (:start-pos heading)
+                                                                                     end-pos)))]
+                                   [(:start-pos heading) acc])) [end-pos []] headings)]
+      journals))))
+
 (comment
 (comment
   (d/transact! conn [{:db/id -1
   (d/transact! conn [{:db/id -1
                       :repo/url "https://github.com/tiensonqin/notes"
                       :repo/url "https://github.com/tiensonqin/notes"

+ 2 - 2
web/src/frontend/format/org/block.cljs

@@ -1,13 +1,13 @@
 (ns frontend.format.org.block
 (ns frontend.format.org.block
   (:require [frontend.util :as util]))
   (:require [frontend.util :as util]))
 
 
-(defn- heading-block?
+(defn heading-block?
   [block]
   [block]
   (and
   (and
    (vector? block)
    (vector? block)
    (= "Heading" (first block))))
    (= "Heading" (first block))))
 
 
-(defn- task-block?
+(defn task-block?
   [block]
   [block]
   (and
   (and
    (heading-block? block)
    (heading-block? block)

+ 11 - 1
web/src/frontend/format/org_mode.cljs

@@ -1,6 +1,8 @@
 (ns frontend.format.org-mode
 (ns frontend.format.org-mode
   (:require ["mldoc_org" :as org]
   (:require ["mldoc_org" :as org]
-            [frontend.format.protocol :as protocol]))
+            [frontend.format.protocol :as protocol]
+            [frontend.util :as util]
+            [clojure.string :as string]))
 
 
 (def config
 (def config
   (js/JSON.stringify
   (js/JSON.stringify
@@ -18,6 +20,14 @@
   [content]
   [content]
   (.parseJson Org content))
   (.parseJson Org content))
 
 
+(defn ->clj
+  [content]
+  (if (string/blank? content)
+    {}
+    (-> content
+       (parse-json)
+       (util/json->clj))))
+
 (defn inline-list->html
 (defn inline-list->html
   [json]
   [json]
   (.inlineListToHtmlStr Org json))
   (.inlineListToHtmlStr Org json))

+ 3 - 2
web/src/frontend/fs.cljs

@@ -30,5 +30,6 @@
    (util/p-handle
    (util/p-handle
     (stat dir path)
     (stat dir path)
     (fn [_stat] true)
     (fn [_stat] true)
-    (fn [_error]
-      (write-file dir path initial-content)))))
+    (fn [error]
+      (write-file dir path initial-content)
+      false))))

+ 53 - 6
web/src/frontend/handler.cljs

@@ -326,18 +326,23 @@
         {:keys [year month day weekday]} (util/get-date)
         {:keys [year month day weekday]} (util/get-date)
         file-path (str "/" path)
         file-path (str "/" path)
         ;; org-journal format, something like `* Tuesday, 06/04/13`
         ;; org-journal format, something like `* Tuesday, 06/04/13`
-        default-content (util/format "* %s, %d/%d/%d\n" weekday month day year)]
+        month (if (< month 10) (str "0" month) month)
+        day (if (< day 10) (str "0" day) day)
+        default-content (util/format "* %s, %s/%s/%d\n" weekday month day year)]
     (->
     (->
      (util/p-handle
      (util/p-handle
       (fs/mkdir (str repo-dir "/journals"))
       (fs/mkdir (str repo-dir "/journals"))
       (fn [result]
       (fn [result]
         (fs/create-if-not-exists repo-dir file-path default-content))
         (fs/create-if-not-exists repo-dir file-path default-content))
       (fn [error]
       (fn [error]
-        ;; exists
         (fs/create-if-not-exists repo-dir file-path default-content)))
         (fs/create-if-not-exists repo-dir file-path default-content)))
      (util/p-handle
      (util/p-handle
-      (fn [result]
-        (git-add-commit repo-url path "create a month journal" default-content))
+      (fn [file-exists?]
+        (if file-exists?
+          (prn "Month journal already exists!")
+          (do
+            (prn "create a month journal")
+            (git-add-commit repo-url path "create a month journal" default-content))))
       (fn [error]
       (fn [error]
         (prn error))))))
         (prn error))))))
 
 
@@ -363,7 +368,8 @@
 (defn re-render!
 (defn re-render!
   []
   []
   (when-let [comp (get @state/state :root-component)]
   (when-let [comp (get @state/state :root-component)]
-    (rum/request-render comp)))
+    (when-not (:edit? @state/state)
+      (rum/request-render comp))))
 
 
 (defn db-listen-to-tx!
 (defn db-listen-to-tx!
   []
   []
@@ -389,6 +395,46 @@
   (periodically-pull repo-url)
   (periodically-pull repo-url)
   (periodically-push-tasks repo-url))
   (periodically-push-tasks repo-url))
 
 
+(defn set-state-kv!
+  [key value]
+  (swap! state/state assoc key value))
+
+(defn edit-journal!
+  [content journal]
+  (swap! state/state assoc
+         :edit? true
+         :edit-journal journal))
+
+(defn set-latest-journals!
+  []
+  (set-state-kv! :latest-journals (db/get-latest-journals {})))
+
+(defn set-journal-content!
+  [uuid content]
+  (swap! state/state update :latest-journals
+         (fn [journals]
+           (mapv
+            (fn [journal]
+              (if (= (:uuid journal) uuid)
+                (assoc journal :content content)
+                journal))
+            journals))))
+
+(defn save-current-edit-journal!
+  [edit-content]
+  (let [{:keys [edit-journal]} @state/state
+        {:keys [start-pos end-pos]} edit-journal]
+    (swap! state/state assoc
+           :edit? false
+           :edit-journal nil)
+    (when-not (= edit-content (:content edit-journal)) ; if new changes
+      (let [path (:file-path edit-journal)
+            current-journals (db/get-file path)
+            new-content (utf8/insert! current-journals start-pos end-pos edit-content)]
+        (prn {:new-content new-content})
+        (set-state-kv! :latest-journals (db/get-latest-journals {:content new-content}))
+        (alter-file path "Auto save" new-content false)))))
+
 (defn start!
 (defn start!
   []
   []
   (db/restore!)
   (db/restore!)
@@ -398,4 +444,5 @@
   (let [repos (db/get-repos)]
   (let [repos (db/get-repos)]
     (doseq [repo repos]
     (doseq [repo repos]
       (create-month-journal-if-not-exists repo)
       (create-month-journal-if-not-exists repo)
-      (periodically-pull-and-push repo))))
+      ;; (periodically-pull-and-push repo)
+      )))

+ 11 - 12
web/src/frontend/mixins.cljs

@@ -48,25 +48,24 @@
 ;;        (util/clear-interval interval))
 ;;        (util/clear-interval interval))
 ;;      (dissoc state name))})
 ;;      (dissoc state name))})
 
 
-(defn close-when-esc-or-outside
-  [state open? & {:keys [on-close node]}]
-  (let [node (or node (rum/dom-node state))]
-    (when @open?
+(defn hide-when-esc-or-outside
+  [state show? & {:keys [on-hide node show-fn]}]
+  (let [node (or node (rum/dom-node state))
+        show? (if (and show-fn (fn? show-fn))
+                (show-fn)
+                @show?)]
+    (when show?
       (listen state js/window "click"
       (listen state js/window "click"
               (fn [e]
               (fn [e]
                 ;; If the click target is outside of current node
                 ;; If the click target is outside of current node
                 (when-not (dom/contains node (.. e -target))
                 (when-not (dom/contains node (.. e -target))
-                  (prn "hide b")
-                  (on-close e))))
+                  (on-hide e))))
 
 
       (listen state js/window "keydown"
       (listen state js/window "keydown"
               (fn [e]
               (fn [e]
-                (prn "key code: " (.-keyCode e))
                 (case (.-keyCode e)
                 (case (.-keyCode e)
                   ;; Esc
                   ;; Esc
-                  27 (do
-                       (prn "hide a")
-                       (on-close e))
+                  27 (on-hide e)
                   nil))))))
                   nil))))))
 
 
 (defn event-mixin
 (defn event-mixin
@@ -92,9 +91,9 @@
     (event-mixin
     (event-mixin
      (fn [state]
      (fn [state]
        (let [open? (get state k)]
        (let [open? (get state k)]
-         (close-when-esc-or-outside state
+         (hide-when-esc-or-outside state
                                     open?
                                     open?
-                                    :on-close (fn []
+                                    :on-hide (fn []
                                                 (reset! open? false)))))
                                                 (reset! open? false)))))
      (fn [state]
      (fn [state]
        (let [open? (atom false)
        (let [open? (atom false)

+ 3 - 1
web/src/frontend/state.cljs

@@ -7,4 +7,6 @@
              :notification/text nil
              :notification/text nil
              :root-component nil
              :root-component nil
              :git-status nil
              :git-status nil
-             :git-error nil}))
+             :git-error nil
+             :edit? false
+             :latest-journals []}))

+ 14 - 1
web/src/frontend/utf8.cljs

@@ -1,4 +1,6 @@
-(ns frontend.utf8)
+(ns frontend.utf8
+  (:require [goog.object :as gobj]))
+
 (defonce encoder
 (defonce encoder
   (js/TextEncoder. "utf-8"))
   (js/TextEncoder. "utf-8"))
 
 
@@ -16,3 +18,14 @@
   ([arr start end]
   ([arr start end]
    (->> (.subarray arr start end)
    (->> (.subarray arr start end)
         (.decode decoder))))
         (.decode decoder))))
+
+(defn length
+  [arr]
+  (gobj/get arr "length"))
+
+(defn insert!
+  [s start-pos end-pos content]
+  (let [arr (encode s)]
+    (str (substring arr 0 start-pos)
+         content
+         (substring arr end-pos))))

+ 10 - 1
web/src/frontend/util.cljs

@@ -123,5 +123,14 @@
 
 
 (defn current-journal-path
 (defn current-journal-path
   []
   []
-  (let [{:keys [year month]} (get-date)]
+  (let [{:keys [year month]} (get-date)
+        month (if (< month 10) (str "0" month) month)]
     (str "journals/" year "_" month ".org")))
     (str "journals/" year "_" month ".org")))
+
+(defn today
+  []
+  (.toLocaleDateString (js/Date.) "default"
+                       (clj->js {:month "long"
+                                 :year "numeric"
+                                 :day "numeric"
+                                 :weekday "long"})))

部分文件因为文件数量过多而无法显示