file.cljs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. (ns frontend.modules.outliner.file
  2. (:require [clojure.core.async :as async]
  3. [clojure.string :as string]
  4. [frontend.config :as config]
  5. [frontend.db :as db]
  6. [frontend.db.model :as model]
  7. [frontend.handler.notification :as notification]
  8. [frontend.modules.file.core :as file]
  9. [frontend.modules.outliner.tree :as tree]
  10. [frontend.util :as util]
  11. [goog.object :as gobj]
  12. [lambdaisland.glogi :as log]
  13. [frontend.state :as state]
  14. [cljs-time.core :as t]
  15. [cljs-time.coerce :as tc]))
  16. (def batch-write-interval 1000)
  17. (def whiteboard-blocks-pull-keys-with-persisted-ids
  18. '[:block/properties
  19. :block/uuid
  20. :block/content
  21. :block/format
  22. :block/created-at
  23. :block/updated-at
  24. :block/collapsed?
  25. {:block/page [:block/uuid]}
  26. {:block/left [:block/uuid]}
  27. {:block/parent [:block/uuid]}])
  28. (defn- cleanup-whiteboard-block
  29. [block]
  30. (if (get-in block [:block/properties :ls-type] false)
  31. (dissoc block
  32. :db/id
  33. :block/uuid ;; shape block uuid is read from properties
  34. :block/collapsed?
  35. :block/content
  36. :block/format
  37. :block/left
  38. :block/page
  39. :block/parent) ;; these are auto-generated for whiteboard shapes
  40. (dissoc block :db/id :block/page)))
  41. (defn do-write-file!
  42. [repo page-db-id outliner-op]
  43. (let [page-block (db/pull repo '[*] page-db-id)
  44. page-db-id (:db/id page-block)
  45. whiteboard? (= "whiteboard" (:block/type page-block))
  46. blocks-count (model/get-page-blocks-count repo page-db-id)
  47. blocks-just-deleted? (and (zero? blocks-count)
  48. (contains? #{:delete-blocks :move-blocks} outliner-op))]
  49. (when (or (>= blocks-count 1) blocks-just-deleted?)
  50. (if (or (and (> blocks-count 500)
  51. (not (state/input-idle? repo {:diff 3000}))) ;; long page
  52. ;; when this whiteboard page is just being updated
  53. (and whiteboard? (not (state/whiteboard-idle? repo))))
  54. (async/put! (state/get-file-write-chan) [repo page-db-id outliner-op (tc/to-long (t/now))])
  55. (let [pull-keys (if whiteboard? whiteboard-blocks-pull-keys-with-persisted-ids '[*])
  56. blocks (model/get-page-blocks-no-cache repo (:block/name page-block) {:pull-keys pull-keys})
  57. blocks (if whiteboard? (map cleanup-whiteboard-block blocks) blocks)]
  58. (when-not (and (= 1 (count blocks))
  59. (string/blank? (:block/content (first blocks)))
  60. (nil? (:block/file page-block)))
  61. (let [tree-or-blocks (if whiteboard? blocks
  62. (tree/blocks->vec-tree repo blocks (:block/name page-block)))]
  63. (if page-block
  64. (file/save-tree! page-block tree-or-blocks blocks-just-deleted?)
  65. (js/console.error (str "can't find page id: " page-db-id))))))))))
  66. (defn write-files!
  67. [pages]
  68. (when (seq pages)
  69. (when-not config/publishing?
  70. (doseq [[repo page-id outliner-op] (set (map #(take 3 %) pages))] ; remove time to dedupe pages to write
  71. (try (do-write-file! repo page-id outliner-op)
  72. (catch :default e
  73. (notification/show!
  74. [:div
  75. [:p "Write file failed, please copy the changes to other editors in case of losing data."]
  76. "Error: " (str (gobj/get e "stack"))]
  77. :error)
  78. (log/error :file/write-file-error {:error e})))))))
  79. (defn sync-to-file
  80. ([page]
  81. (sync-to-file page nil))
  82. ([{page-db-id :db/id} outliner-op]
  83. (if (nil? page-db-id)
  84. (notification/show!
  85. "Write file failed, can't find the current page!"
  86. :error)
  87. (when-let [repo (state/get-current-repo)]
  88. (if (:graph/importing @state/state) ; write immediately
  89. (write-files! [[repo page-db-id outliner-op]])
  90. (async/put! (state/get-file-write-chan) [repo page-db-id outliner-op (tc/to-long (t/now))]))))))
  91. (def *writes-finished? (atom {}))
  92. (defn <ratelimit-file-writes!
  93. []
  94. (util/<ratelimit (state/get-file-write-chan) batch-write-interval
  95. :filter-fn
  96. (fn [[repo _ _ time]]
  97. (swap! *writes-finished? assoc repo {:time time
  98. :value false})
  99. true)
  100. :flush-fn
  101. (fn [col]
  102. (let [start-time (tc/to-long (t/now))
  103. repos (distinct (map first col))]
  104. (write-files! col)
  105. (doseq [repo repos]
  106. (let [last-write-time (get-in @*writes-finished? [repo :time])]
  107. (when (> start-time last-write-time)
  108. (swap! *writes-finished? assoc repo {:value true}))))))))