diff --git a/android/app/src/main/java/com/logseq/app/FsWatcher.java b/android/app/src/main/java/com/logseq/app/FsWatcher.java index 5bf70be0cd..3b07e9b5d9 100644 --- a/android/app/src/main/java/com/logseq/app/FsWatcher.java +++ b/android/app/src/main/java/com/logseq/app/FsWatcher.java @@ -10,6 +10,8 @@ import android.net.Uri; import java.io.*; +import java.net.URI; +import java.text.Normalizer; import java.util.HashMap; import java.util.Map; import java.util.Stack; @@ -90,8 +92,11 @@ public class FsWatcher extends Plugin { shouldRead = true; } - obj.put("path", Uri.fromFile(f)); - obj.put("dir", Uri.fromFile(new File(mPath))); + URI dir = (new File(mPath)).toURI(); + URI fpath = f.toURI(); + + obj.put("path", Normalizer.normalize(dir.relativize(fpath).toString(), Normalizer.Form.NFC)); + obj.put("dir", Uri.fromFile(new File(mPath))); // Uri is for Android. URI is for RFC compatible JSObject stat; switch (event) { diff --git a/ios/App/App/FsWatcher.swift b/ios/App/App/FsWatcher.swift index 578d389fbe..50c9ebded7 100644 --- a/ios/App/App/FsWatcher.swift +++ b/ios/App/App/FsWatcher.swift @@ -46,12 +46,16 @@ public class FsWatcher: CAPPlugin, PollingWatcherDelegate { } public func receivedNotification(_ url: URL, _ event: PollingWatcherEvent, _ metadata: SimpleFileMetadata?) { + guard let baseUrl = baseUrl else { + // unwatch, ignore incoming + return + } // NOTE: Event in js {dir path content stat{mtime}} switch event { case .Unlink: DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { self.notifyListeners("watcher", data: ["event": "unlink", - "dir": self.baseUrl?.description as Any, + "dir": baseUrl.description as Any, "path": url.description ]) } @@ -61,8 +65,8 @@ public class FsWatcher: CAPPlugin, PollingWatcherDelegate { content = try? String(contentsOf: url, encoding: .utf8) } self.notifyListeners("watcher", data: ["event": event.description, - "dir": baseUrl?.description as Any, - "path": url.description, + "dir": baseUrl.description as Any, + "path": url.relativePath(from: baseUrl)?.precomposedStringWithCanonicalMapping as Any, "content": content as Any, "stat": ["mtime": metadata?.contentModificationTimestamp ?? 0, "ctime": metadata?.creationTimestamp ?? 0, @@ -265,3 +269,29 @@ public class PollingWatcher { self.metaDb = newMetaDb } } + + +extension URL { + func relativePath(from base: URL) -> String? { + // Ensure that both URLs represent files: + guard self.isFileURL && base.isFileURL else { + return nil + } + + // Remove/replace "." and "..", make paths absolute: + let destComponents = self.standardizedFileURL.pathComponents + let baseComponents = base.standardizedFileURL.pathComponents + + // Find number of common path components: + var i = 0 + while i < destComponents.count && i < baseComponents.count + && destComponents[i] == baseComponents[i] { + i += 1 + } + + // Build relative path: + var relComponents = Array(repeating: "..", count: baseComponents.count - i) + relComponents.append(contentsOf: destComponents[i...]) + return relComponents.joined(separator: "/") + } +} diff --git a/src/main/frontend/handler/events.cljs b/src/main/frontend/handler/events.cljs index 368ae9ebbb..ef6f78390e 100644 --- a/src/main/frontend/handler/events.cljs +++ b/src/main/frontend/handler/events.cljs @@ -69,8 +69,7 @@ [logseq.db.schema :as db-schema] [logseq.graph-parser.config :as gp-config] [promesa.core :as p] - [rum.core :as rum] - [logseq.common.path :as path])) + [rum.core :as rum])) ;; TODO: should we move all events here? @@ -622,13 +621,7 @@ (defmethod handle :mobile-file-watcher/changed [[_ ^js event]] (let [type (.-event event) - payload (js->clj event :keywordize-keys true) - dir (:dir payload) - payload (-> payload - (update :path - (fn [path] - (when (string? path) - (path/relative-path dir path)))))] + payload (js->clj event :keywordize-keys true)] (fs-watcher/handle-changed! type payload) (when (file-sync-handler/enable-sync?) (sync/file-watch-handler type payload))))