page.cljs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. (ns frontend.handler.common.page
  2. "Common fns for file and db based page handlers, including create!, delete!
  3. and favorite fns. This ns should be agnostic of file or db concerns but there
  4. is still some file-specific tech debt to remove from create!"
  5. (:require [clojure.string :as string]
  6. [frontend.db :as db]
  7. [frontend.handler.config :as config-handler]
  8. [frontend.handler.route :as route-handler]
  9. [frontend.state :as state]
  10. [logseq.common.util :as common-util]
  11. [logseq.common.config :as common-config]
  12. [frontend.handler.ui :as ui-handler]
  13. [frontend.config :as config]
  14. [frontend.fs :as fs]
  15. [promesa.core :as p]
  16. [frontend.handler.block :as block-handler]
  17. [logseq.db :as ldb]
  18. [frontend.db.conn :as conn]
  19. [datascript.core :as d]
  20. [frontend.modules.outliner.ui :as ui-outliner-tx]
  21. [frontend.modules.outliner.op :as outliner-op]
  22. [frontend.handler.db-based.editor :as db-editor-handler]
  23. [logseq.common.util.page-ref :as page-ref]
  24. [frontend.handler.notification :as notification]))
  25. (defn- wrap-tags
  26. "Tags might have multiple words"
  27. [title]
  28. (let [parts (string/split title #" #")]
  29. (->>
  30. (cons (first parts)
  31. (map (fn [s]
  32. (if (and (string/includes? s " ") (not (page-ref/page-ref? s)))
  33. (page-ref/->page-ref s)
  34. s))
  35. (rest parts)))
  36. (string/join " #"))))
  37. (defn <create!
  38. ([title]
  39. (<create! title {}))
  40. ([title {:keys [redirect?]
  41. :or {redirect? true}
  42. :as options}]
  43. (when (string? title)
  44. (p/let [repo (state/get-current-repo)
  45. conn (db/get-db repo false)
  46. db-based? (config/db-based-graph? repo)
  47. title (if (and db-based? (string/includes? title " #")) ; tagged page
  48. (wrap-tags title)
  49. title)
  50. parsed-result (when db-based? (db-editor-handler/wrap-parse-block {:block/title title}))
  51. has-tags? (and db-based? (seq (:block/tags parsed-result)))
  52. title' (if has-tags?
  53. (some-> (first
  54. (common-util/split-first (str "#" page-ref/left-brackets) (:block/title parsed-result)))
  55. string/trim)
  56. title)]
  57. (if (and has-tags? (nil? title'))
  58. (notification/show! "Page name can't include \"#\"." :warning)
  59. (when-not (string/blank? title')
  60. (p/let [options' (if db-based?
  61. (cond->
  62. (update options :tags concat (:block/tags parsed-result))
  63. (nil? (:split-namespace? options))
  64. (assoc :split-namespace? true))
  65. options)
  66. result (ui-outliner-tx/transact!
  67. {:outliner-op :create-page}
  68. (outliner-op/create-page! title' options'))
  69. [_page-name page-uuid] (ldb/read-transit-str result)]
  70. (when redirect?
  71. (route-handler/redirect-to-page! page-uuid))
  72. (let [page (db/get-page (or page-uuid title'))]
  73. (when-let [first-block (ldb/get-first-child @conn (:db/id page))]
  74. (block-handler/edit-block! first-block :max {:container-id :unknown-container}))
  75. page))))))))
  76. ;; favorite fns
  77. ;; ============
  78. (defn file-favorited?
  79. [page-name]
  80. (let [favorites (->> (:favorites (state/get-config))
  81. (filter string?)
  82. (map string/lower-case)
  83. (set))]
  84. (contains? favorites (string/lower-case page-name))))
  85. (defn file-favorite-page!
  86. [page-name]
  87. (when-not (string/blank? page-name)
  88. (let [favorites (->
  89. (cons
  90. page-name
  91. (or (:favorites (state/get-config)) []))
  92. (distinct)
  93. (vec))]
  94. (config-handler/set-config! :favorites favorites))))
  95. (defn file-unfavorite-page!
  96. [page-name]
  97. (when-not (string/blank? page-name)
  98. (let [old-favorites (:favorites (state/get-config))
  99. new-favorites (->> old-favorites
  100. (remove #(= (string/lower-case %) (string/lower-case page-name)))
  101. (vec))]
  102. (when-not (= old-favorites new-favorites)
  103. (config-handler/set-config! :favorites new-favorites)))))
  104. (defn- find-block-in-favorites-page
  105. [page-block-uuid]
  106. (let [db (conn/get-db)]
  107. (when-let [page (db/get-page common-config/favorites-page-name)]
  108. (let [blocks (ldb/get-page-blocks db (:db/id page))]
  109. (when-let [page-block-entity (d/entity db [:block/uuid page-block-uuid])]
  110. (some (fn [block]
  111. (when (= (:db/id (:block/link block)) (:db/id page-block-entity))
  112. block))
  113. blocks))))))
  114. (defn db-favorited?
  115. [page-block-uuid]
  116. {:pre [(uuid? page-block-uuid)]}
  117. (some? (find-block-in-favorites-page page-block-uuid)))
  118. (defn <db-favorite-page!
  119. [page-block-uuid]
  120. {:pre [(uuid? page-block-uuid)]}
  121. (when (d/entity (conn/get-db) [:block/uuid page-block-uuid])
  122. (p/do!
  123. (ui-outliner-tx/transact!
  124. {:outliner-op :insert-blocks}
  125. (outliner-op/insert-blocks! [(ldb/build-favorite-tx page-block-uuid)]
  126. (db/get-page common-config/favorites-page-name)
  127. {})))))
  128. (defn <db-unfavorite-page!
  129. [page-block-uuid]
  130. {:pre [(uuid? page-block-uuid)]}
  131. (when-let [block (find-block-in-favorites-page page-block-uuid)]
  132. (ui-outliner-tx/transact!
  133. {:outliner-op :delete-blocks}
  134. (outliner-op/delete-blocks! [block] {}))))
  135. ;; favorites fns end ================
  136. (defn <delete!
  137. "Deletes a page. If delete is successful calls ok-handler. Otherwise calls error-handler
  138. if given. Note that error-handler is being called in addition to error messages that worker
  139. already provides"
  140. [page-uuid-or-name ok-handler & {:keys [error-handler]}]
  141. (when page-uuid-or-name
  142. (assert (or (uuid? page-uuid-or-name) (string? page-uuid-or-name)))
  143. (when-let [page-uuid (or (and (uuid? page-uuid-or-name) page-uuid-or-name)
  144. (:block/uuid (db/get-page page-uuid-or-name)))]
  145. (when @state/*db-worker
  146. (let [page (db/entity [:block/uuid page-uuid])
  147. default-home (state/get-default-home)
  148. home-page? (= (:block/title page) (:page default-home))]
  149. (p/do!
  150. (when home-page?
  151. (p/do!
  152. (config-handler/set-config! :default-home (dissoc default-home :page))
  153. (config-handler/set-config! :feature/enable-journals? true)
  154. (notification/show! "Journals enabled" :success)))
  155. (-> (p/let [res (ui-outliner-tx/transact!
  156. {:outliner-op :delete-page}
  157. (outliner-op/delete-page! page-uuid))
  158. res' (ldb/read-transit-str res)]
  159. (if res'
  160. (when ok-handler (ok-handler))
  161. (when error-handler (error-handler))))
  162. (p/catch (fn [error]
  163. (js/console.error error))))))))))
  164. ;; other fns
  165. ;; =========
  166. (defn after-page-deleted!
  167. [repo page-name file-path tx-meta]
  168. (let [repo-dir (config/get-repo-dir repo)]
  169. ;; TODO: move favorite && unfavorite to worker too
  170. (if (config/db-based-graph? repo)
  171. (when-let [page-block-uuid (:block/uuid (db/get-page page-name))]
  172. (<db-unfavorite-page! page-block-uuid))
  173. (file-unfavorite-page! page-name))
  174. (when (and (not= :rename-page (:real-outliner-op tx-meta))
  175. (= (some-> (state/get-current-page) common-util/page-name-sanity-lc)
  176. (common-util/page-name-sanity-lc page-name)))
  177. (route-handler/redirect-to-home!))
  178. ;; TODO: why need this?
  179. (ui-handler/re-render-root!)
  180. (when file-path
  181. (-> (p/let [exists? (fs/file-exists? repo-dir file-path)]
  182. (when exists? (fs/unlink! repo (config/get-repo-fpath repo file-path) nil)))
  183. (p/catch (fn [error] (js/console.error error)))))))
  184. (defn rename-file!
  185. "emit file-rename events to :file/rename-event-chan
  186. force-fs? - when true, rename file event the db transact is failed."
  187. [old-path new-path]
  188. (let [repo (state/get-current-repo)]
  189. (->
  190. (p/let [_ (state/offer-file-rename-event-chan! {:repo repo
  191. :old-path old-path
  192. :new-path new-path})]
  193. (fs/rename! repo old-path new-path))
  194. (p/catch (fn [error]
  195. (println "file rename failed: " error))))))
  196. (defn after-page-renamed!
  197. [repo {:keys [page-id old-name new-name old-path new-path]}]
  198. (let [db-based? (config/db-based-graph? repo)
  199. old-page-name (common-util/page-name-sanity-lc old-name)
  200. new-page-name (common-util/page-name-sanity-lc new-name)
  201. redirect? (= (some-> (state/get-current-page) common-util/page-name-sanity-lc)
  202. (common-util/page-name-sanity-lc old-page-name))
  203. page (db/entity repo page-id)]
  204. ;; Redirect to the newly renamed page
  205. (when (and redirect? (not (db/whiteboard-page? page)))
  206. (route-handler/redirect! {:to :page
  207. :push false
  208. :path-params {:name (str (:block/uuid page))}}))
  209. ;; FIXME: favorites should store db id/uuid instead of page names
  210. (when (and (config/db-based-graph? repo) (file-favorited? old-page-name))
  211. (file-unfavorite-page! old-page-name)
  212. (file-favorite-page! new-page-name))
  213. (let [home (get (state/get-config) :default-home {})]
  214. (when (= old-page-name (common-util/page-name-sanity-lc (get home :page "")))
  215. (config-handler/set-config! :default-home (assoc home :page new-name))))
  216. (when-not db-based?
  217. (when (and old-path new-path)
  218. (rename-file! old-path new-path)))
  219. (ui-handler/re-render-root!)))