handler.cljs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. (ns frontend.handler
  2. (:require [frontend.state :as state]
  3. [frontend.db :as db]
  4. [frontend.db-schema :as db-schema]
  5. [frontend.util :as util :refer-macros [profile]]
  6. [frontend.config :as config]
  7. [frontend.storage :as storage]
  8. [clojure.string :as string]
  9. [promesa.core :as p]
  10. [cljs-bean.core :as bean]
  11. [frontend.date :as date]
  12. [frontend.handler.notification :as notification]
  13. [frontend.handler.page :as page-handler]
  14. [frontend.handler.repo :as repo-handler]
  15. [frontend.handler.file :as file-handler]
  16. [frontend.handler.ui :as ui-handler]
  17. [frontend.handler.export :as export-handler]
  18. [frontend.handler.web.nfs :as nfs]
  19. [frontend.ui :as ui]
  20. [goog.object :as gobj]
  21. [frontend.helper :as helper]
  22. [frontend.idb :as idb]))
  23. (defn- watch-for-date!
  24. []
  25. (js/setInterval (fn []
  26. (state/set-today! (date/today))
  27. (when-let [repo (state/get-current-repo)]
  28. (when (db/cloned? repo)
  29. (let [today-page (string/lower-case (date/today))]
  30. (when (empty? (db/get-page-blocks-no-cache repo today-page))
  31. (repo-handler/create-today-journal-if-not-exists repo))))))
  32. 1000))
  33. (defn store-schema!
  34. []
  35. (storage/set :db-schema db-schema/schema))
  36. (defn schema-changed?
  37. []
  38. (when-let [schema (storage/get :db-schema)]
  39. (not= schema db-schema/schema)))
  40. (defn- get-me-and-repos
  41. []
  42. (let [me (and js/window.user (bean/->clj js/window.user))
  43. logged? (:name me)
  44. repos (if logged?
  45. (:repos me)
  46. [{:url config/local-repo}])]
  47. {:me me
  48. :logged? logged?
  49. :repos repos}))
  50. (declare restore-and-setup!)
  51. (defn clear-stores-and-refresh!
  52. []
  53. (p/let [_ (db/clear-local-storage-and-idb!)]
  54. (let [{:keys [me logged? repos]} (get-me-and-repos)]
  55. (js/window.location.reload))))
  56. (defn restore-and-setup!
  57. [me repos logged?]
  58. (let [interval (atom nil)
  59. inner-fn (fn []
  60. (when (and @interval js/window.pfs)
  61. (js/clearInterval @interval)
  62. (reset! interval nil)
  63. (-> (p/all (db/restore! (assoc me :repos repos)
  64. (fn [repo]
  65. (file-handler/restore-config! repo false)
  66. (ui-handler/add-style-if-exists!))))
  67. (p/then
  68. (fn []
  69. (cond
  70. (and (not logged?)
  71. (not (seq (db/get-files config/local-repo)))
  72. ;; Not native local directory
  73. (not (some config/local-db? (map :url repos))))
  74. (repo-handler/setup-local-repo-if-not-exists!)
  75. :else
  76. (state/set-db-restoring! false))
  77. (if (schema-changed?)
  78. (do
  79. (notification/show!
  80. [:p "Database schema changed, your notes will be exported as zip files, your repos will be re-indexed then."]
  81. :warning
  82. false)
  83. (let [export-repos (for [repo repos]
  84. (when-let [url (:url repo)]
  85. (println "Export repo: " url)
  86. (export-handler/export-repo-as-zip! url)))]
  87. (-> (p/all export-repos)
  88. (p/then (fn []
  89. (store-schema!)
  90. (js/setTimeout clear-stores-and-refresh! 5000)))
  91. (p/catch (fn [error]
  92. (log/error :export/zip {:error error
  93. :repos repos}))))))
  94. (store-schema!))
  95. (page-handler/init-commands!)
  96. (if (seq (:repos me))
  97. ;; FIXME: handle error
  98. (helper/request-app-tokens!
  99. (fn []
  100. (repo-handler/clone-and-pull-repos me))
  101. (fn []
  102. (js/console.error "Failed to request GitHub app tokens."))))
  103. (watch-for-date!))))))]
  104. ;; clear this interval
  105. (let [interval-id (js/setInterval inner-fn 50)]
  106. (reset! interval interval-id))))
  107. (defn persist-repo-to-indexeddb!
  108. ([]
  109. (persist-repo-to-indexeddb! false))
  110. ([force?]
  111. (let [status (state/get-repo-persist-status)]
  112. (doseq [[repo {:keys [last-stored-at last-modified-at] :as repo-status}] status]
  113. (when (and (> last-modified-at last-stored-at)
  114. (or force?
  115. (and (state/get-edit-input-id)
  116. (> (- (util/time-ms) last-stored-at) (* 5 60 1000)) ; 5 minutes
  117. )
  118. (nil? (state/get-edit-input-id))))
  119. (p/let [_ (repo-handler/persist-repo! repo)]
  120. (state/update-repo-last-stored-at! repo)))))))
  121. (defn periodically-persist-repo-to-indexeddb!
  122. []
  123. (js/setInterval persist-repo-to-indexeddb! (* 5 1000)))
  124. (defn set-save-before-unload! []
  125. (.addEventListener js/window "beforeunload"
  126. (fn [e]
  127. (when (state/repos-need-to-be-stored?)
  128. (let [notification-id (atom nil)]
  129. (let [id (notification/show!
  130. [:div
  131. [:p "It seems that you have some unsaved changes!"]
  132. (ui/button "Save"
  133. :on-click (fn [e]
  134. (persist-repo-to-indexeddb!)
  135. (notification/show!
  136. "Saved successfully!"
  137. :success)
  138. (and @notification-id (notification/clear! @notification-id))))]
  139. :warning
  140. false)]
  141. (reset! notification-id id)))
  142. (let [message "\\o/"]
  143. (set! (.-returnValue (or e js/window.event)) message)
  144. message)))))
  145. (defn- handle-connection-change
  146. [e]
  147. (let [online? (= (gobj/get e "type") "online")]
  148. (state/set-online! online?)))
  149. (defn set-network-watcher!
  150. []
  151. (js/window.addEventListener "online" handle-connection-change)
  152. (js/window.addEventListener "offline" handle-connection-change))
  153. (defn start!
  154. [render]
  155. (let [{:keys [me logged? repos]} (get-me-and-repos)]
  156. (when me (state/set-state! :me me))
  157. (state/set-db-restoring! true)
  158. (render)
  159. (set-network-watcher!)
  160. (util/indexeddb-check?
  161. (fn [_error]
  162. (notification/show! "Sorry, it seems that your browser doesn't support IndexedDB, we recommend to use latest Chrome(Chromium) or Firefox(Non-private mode)." :error false)
  163. (state/set-indexedb-support! false)))
  164. ;; (nfs/trigger-check!)
  165. (p/let [nfs-dbs (idb/get-nfs-dbs)
  166. nfs-dbs (map (fn [db] {:url db}) nfs-dbs)]
  167. (let [repos (cond
  168. logged?
  169. (concat
  170. (:repos me)
  171. nfs-dbs)
  172. (seq nfs-dbs)
  173. nfs-dbs
  174. :else
  175. [{:url config/local-repo}])]
  176. (restore-and-setup! me repos logged?)))
  177. (periodically-persist-repo-to-indexeddb!)
  178. (db/run-batch-txs!))
  179. (set-save-before-unload!))