diff --git a/resources/css/common.css b/resources/css/common.css index 611067d05b..f39ec81e0a 100644 --- a/resources/css/common.css +++ b/resources/css/common.css @@ -181,6 +181,11 @@ textarea { font-weight: inherit; letter-spacing: inherit; text-size-adjust: 100%; + background: var(--ls-primary-background-color); +} + +.dark-theme textarea { + background: var(--ls-tertiary-background-color); } ul { diff --git a/src/main/frontend/components/diff.cljs b/src/main/frontend/components/diff.cljs index f2c037e57c..59821beecb 100644 --- a/src/main/frontend/components/diff.cljs +++ b/src/main/frontend/components/diff.cljs @@ -230,3 +230,78 @@ :else [:div "No diffs"])])) + +(rum/defcs local-file < rum/reactive + [state repo path disk-content local-content] + (let [content disk-content + edit? (util/react *edit?)] + [:div.cp__diff-file + [:div.cp__diff-file-header + [:span.cp__diff-file-header-content {:style {:word-break "break-word"}} + path]] + (when (not= content local-content) + (let [local-content (or local-content "") + content (or content "") + diff (medley/indexed (diff/diff local-content content)) + diff? (some (fn [[_idx {:keys [added removed]}]] + (or added removed)) + diff) + diff-cp [:div.overflow-y-scroll + [:div {:style {:max-height "65vh"}} + (diff-cp diff)]]] + [:div.pre-line-white-space.p-2.overflow-y-hidden + (if edit? + [:div.grid.grid-cols-2.gap-1 + diff-cp + (ui/textarea + {:default-value local-content + :auto-focus true + :style {:border "1px solid"} + :on-change (fn [e] + (reset! *edit-content (util/evalue e)))})] + diff-cp) + + (cond + edit? + [:div.mt-2 + (ui/button "Save" + :on-click + (fn [] + (reset! *edit? false) + (let [new-content @*edit-content] + (file/alter-file repo path new-content + {:re-render-root? true + :skip-compare? true}) + (state/close-modal!))))] + + diff? + [:div.mt-2 + (ui/button "Use latest changes from the disk" + :on-click + (fn [] + (file/alter-file repo path content + {:re-render-root? true + :skip-compare? true}) + (state/close-modal!)) + :background "green") + + [:span.pl-2.pr-2 "or"] + + (ui/button "Keep local changes in Logseq" + :on-click + (fn [] + (file/alter-file repo path local-content + {:re-render-root? true + :skip-compare? true}) + (state/close-modal!)) + :background "pink") + + [:span.pl-2.pr-2 "or"] + + (ui/button "Edit" + :on-click + (fn [] + (reset! *edit? true)))] + + :else + nil)]))])) diff --git a/src/main/frontend/extensions/pdf/assets.cljs b/src/main/frontend/extensions/pdf/assets.cljs index 4968112286..f0accc0cbb 100644 --- a/src/main/frontend/extensions/pdf/assets.cljs +++ b/src/main/frontend/extensions/pdf/assets.cljs @@ -70,7 +70,7 @@ (let [repo-cur (state/get-current-repo) repo-dir (config/get-repo-dir repo-cur) data (pr-str {:highlights highlights})] - (fs/write-file! repo-cur repo-dir hls-file data {:skip-mtime? true})))) + (fs/write-file! repo-cur repo-dir hls-file data {:skip-compare? true})))) (defn resolve-hls-data-by-key$ [target-key] @@ -115,7 +115,7 @@ new-fpath (str fdir "/" fname "_" fstamp ".png") old-fpath (and old-fstamp (str fdir "/" fname "_" old-fstamp ".png")) _ (and old-fpath (apply fs/rename! repo-cur (map #(util/node-path.join repo-dir %) [old-fpath new-fpath]))) - _ (fs/write-file! repo-cur repo-dir new-fpath png {:skip-mtime? true})] + _ (fs/write-file! repo-cur repo-dir new-fpath png {:skip-compare? true})] (js/console.timeEnd :write-area-image)) diff --git a/src/main/frontend/fs/nfs.cljs b/src/main/frontend/fs/nfs.cljs index 63d0e70735..5bd00ee6e9 100644 --- a/src/main/frontend/fs/nfs.cljs +++ b/src/main/frontend/fs/nfs.cljs @@ -10,6 +10,7 @@ [frontend.config :as config] [frontend.state :as state] [frontend.handler.notification :as notification] + [frontend.encrypt :as encrypt] ["/frontend/utils" :as utils])) ;; We need to cache the file handles in the memory so that @@ -54,6 +55,14 @@ (when handle (verify-permission repo handle true))))) +(defn- contents-matched? + [disk-content db-content] + (when (and (string? disk-content) (string? db-content)) + (if (encrypt/encrypted-db? (state/get-current-repo)) + (p/let [decrypted-content (encrypt/decrypt disk-content)] + (= (string/trim decrypted-content) (string/trim db-content))) + (p/resolved (= (string/trim disk-content) (string/trim db-content)))))) + (defrecord Nfs [] protocol/Fs (mkdir! [this dir] @@ -155,35 +164,25 @@ (if file-handle (-> (p/let [local-file (.getFile file-handle) local-content (.text local-file) - local-last-modified-at (gobj/get local-file "lastModified") - current-time (util/time-ms) - new? (> current-time local-last-modified-at) - new-created? (nil? last-modified-at) - not-changed? (= last-modified-at local-last-modified-at) - format (-> (util/get-file-ext path) - (config/get-file-format)) pending-writes (state/get-write-chan-length) - draw? (and path (string/ends-with? path ".excalidraw")) - config? (and path (string/ends-with? path "/config.edn"))] - (p/let [_ (verify-permission repo file-handle true) - _ (utils/writeFile file-handle content) - file (.getFile file-handle)] - (if (and local-content new? - (or - draw? - config? - ;; Writing not finished - (> pending-writes 0) - ;; not changed by other editors - not-changed? - new-created?)) + ext (string/lower-case (util/get-file-ext path)) + db-content (db/get-file repo path)] + (when local-content + (if (and + (not (string/blank? db-content)) + (not (:skip-compare? opts)) + (not (contents-matched? local-content (or db-content ""))) + (not (contains? #{"excalidraw" "edn"} ext))) + (state/pub-event! [:file/not-matched-from-disk path local-content content]) (p/let [_ (verify-permission repo file-handle true) _ (utils/writeFile file-handle content) file (.getFile file-handle)] (when file - (nfs-saved-handler repo path file))) - (js/alert (str "The file has been modified on your local disk! File path: " path - ", please save your changes and click the refresh button to reload it."))))) + (p/let [content (if (encrypt/encrypted-db? (state/get-current-repo)) + (encrypt/decrypt content) + content)] + (db/set-file-content! repo path content)) + (nfs-saved-handler repo path file)))))) (p/catch (fn [e] (js/console.error e)))) ;; create file handle diff --git a/src/main/frontend/fs/node.cljs b/src/main/frontend/fs/node.cljs index 525aeaed3a..d4e88f2775 100644 --- a/src/main/frontend/fs/node.cljs +++ b/src/main/frontend/fs/node.cljs @@ -36,8 +36,8 @@ (p/resolved (= (string/trim disk-content) (string/trim db-content)))))) (defn- write-file-impl! - [this repo dir path content {:keys [ok-handler error-handler skip-mtime?] :as opts} stat] - (if skip-mtime? + [this repo dir path content {:keys [ok-handler error-handler skip-compare?] :as opts} stat] + (if skip-compare? (p/catch (p/let [result (ipc/ipc "writeFile" path content)] (when ok-handler @@ -56,22 +56,10 @@ db-content (or (db/get-file repo path) "") contents-matched? (contents-matched? disk-content db-content)] (cond - ;; (and (not page-empty?) (nil? disk-content) ) - ;; (notification/show! - ;; (str "The file has been renamed or deleted on your local disk! File path: " path - ;; ", please save your changes and click the refresh button to reload it.") - ;; :error - ;; false) - (and (not contents-matched?) - ;; FIXME: (not (contains? #{"excalidraw" "edn"} ext))) - (notification/show! - (str "The file has been modified on your local disk! File path: " path - ", please save your changes and click the refresh button to reload it.") - :warning - false) + (state/pub-event! [:file/not-matched-from-disk path disk-content content]) :else (-> diff --git a/src/main/frontend/handler/events.cljs b/src/main/frontend/handler/events.cljs index 54ad6e5c57..7daac50b2d 100644 --- a/src/main/frontend/handler/events.cljs +++ b/src/main/frontend/handler/events.cljs @@ -13,6 +13,7 @@ [frontend.components.encryption :as encryption] [frontend.components.shell :as shell] [frontend.components.git :as git-component] + [frontend.components.diff :as diff] [frontend.fs.nfs :as nfs] [frontend.db.conn :as conn] [frontend.extensions.srs :as srs] @@ -152,6 +153,11 @@ (defmethod handle :page/create-today-journal [[_ repo]] (page-handler/create-today-journal!)) +(defmethod handle :file/not-matched-from-disk [[_ path disk-content db-content]] + (state/clear-edit!) + (when-let [repo (state/get-current-repo)] + (state/set-modal! #(diff/local-file repo path disk-content db-content)))) + (defmethod handle :after-db-restore [[_ repos]] (mapv (fn [{url :url} repo] ;; compare :ast/version diff --git a/src/main/frontend/handler/file.cljs b/src/main/frontend/handler/file.cljs index 3b13f6c08f..cd934cb4f5 100644 --- a/src/main/frontend/handler/file.cljs +++ b/src/main/frontend/handler/file.cljs @@ -158,17 +158,20 @@ ;; TODO: Remove this function in favor of `alter-files` (defn alter-file - [repo path content {:keys [reset? re-render-root? add-history? update-status? from-disk?] + [repo path content {:keys [reset? re-render-root? add-history? update-status? from-disk? skip-compare?] :or {reset? true re-render-root? false add-history? true update-status? false - from-disk? false}}] + from-disk? false + skip-compare? false}}] (let [edit-block (state/get-edit-block) original-content (db/get-file-no-sub repo path) write-file! (if from-disk? #(p/resolved nil) - #(fs/write-file! repo (config/get-repo-dir repo) path content (when original-content {:old-content original-content})))] + #(fs/write-file! repo (config/get-repo-dir repo) path content + (assoc (when original-content {:old-content original-content}) + :skip-compare? skip-compare?)))] (p/let [_ (if reset? (do (when-let [page-id (db/get-file-page-id path)] diff --git a/src/main/logseq/api.cljs b/src/main/logseq/api.cljs index d4cc085b74..4475d67da0 100644 --- a/src/main/logseq/api.cljs +++ b/src/main/logseq/api.cljs @@ -94,7 +94,7 @@ (fn [path ^js data] (let [repo "" path (util/node-path.join path "package.json")] - (fs/write-file! repo "" path (js/JSON.stringify data nil 2) {:skip-mtime? true})))) + (fs/write-file! repo "" path (js/JSON.stringify data nil 2) {:skip-compare? true})))) (defn ^:private write_dotdir_file [file content sub-root] @@ -109,7 +109,7 @@ user-path-root (util/node-path.dirname user-path) exist? (fs/file-exists? user-path-root "") _ (when-not exist? (fs/mkdir-recur! user-path-root)) - _ (fs/write-file! repo "" user-path content {:skip-mtime? true})] + _ (fs/write-file! repo "" user-path content {:skip-compare? true})] user-path)) (defn ^:private read_dotdir_file @@ -191,7 +191,7 @@ (p/let [repo "" path (plugin-handler/get-ls-dotdir-root) path (util/node-path.join path "preferences.json")] - (fs/write-file! repo "" path (js/JSON.stringify data nil 2) {:skip-mtime? true}))))) + (fs/write-file! repo "" path (js/JSON.stringify data nil 2) {:skip-compare? true}))))) (def ^:export load_plugin_user_settings (fn [key] @@ -209,7 +209,7 @@ (p/let [repo "" path (plugin-handler/get-ls-dotdir-root) path (util/node-path.join path "settings" (str key ".json"))] - (fs/write-file! repo "" path (js/JSON.stringify data nil 2) {:skip-mtime? true})))) + (fs/write-file! repo "" path (js/JSON.stringify data nil 2) {:skip-compare? true})))) (def ^:export register_plugin_slash_command (fn [pid ^js cmd-actions]