-
-
Notifications
You must be signed in to change notification settings - Fork 490
Description
Issue Description
QOwnNotes doesn't detect modifications of notes if they're synced by the Nextcloud desktop client or more generally, if they're performed by a move operation. This was already discussed in #3366, but now I finally had some time to investigate this issue further.
Nextcloud Desktop File Synchronization
When the Nextcloud desktop client syncs changes to an already-existing file, it doesn't overwrite it directly.
Instead, it first creates a temporary file .<name-of-note>.~<unique-id>, e.g. .Welcome to QOwnNotes.md.~dce5e2c.
All remote changes are written to this file first. Only afterwards, the original note file is overwritten by a move to finalize the synchronization.
So why are these changes not detected by QOwnNotes?
QOwnNotes File Change Detection
QOwnNotes uses two kinds of filesystem watchers: a directory watcher and a file watcher:
Lines 637 to 653 in 025e685
| void MainWindow::connectFileWatcher(bool delayed) { | |
| if (!delayed) { | |
| connect(¬eDirectoryWatcher, &QFileSystemWatcher::directoryChanged, this, | |
| &MainWindow::notesDirectoryWasModified, Qt::UniqueConnection); | |
| connect(¬eDirectoryWatcher, &QFileSystemWatcher::fileChanged, this, | |
| &MainWindow::notesWereModified, Qt::UniqueConnection); | |
| } else { | |
| // In some cases, there are delayed signals coming in which we don't want to handle | |
| // so reconnect with delay | |
| QTimer::singleShot(300, this, [this] { | |
| connect(¬eDirectoryWatcher, &QFileSystemWatcher::directoryChanged, this, | |
| &MainWindow::notesDirectoryWasModified, Qt::UniqueConnection); | |
| connect(¬eDirectoryWatcher, &QFileSystemWatcher::fileChanged, this, | |
| &MainWindow::notesWereModified, Qt::UniqueConnection); | |
| }); | |
| } | |
| } |
The directory watcher is attached to the note directory and the file watcher to every note.
On a directory event (i.e. the creation of a new file), the notesDirectoryWasModified callback is called.
If a file is changed, notesWereModified is called instead.
Control Flow during Synchronization
If the Nextcloud client syncs a file, QOwnNotes' notesDirectoryWasModified callback is run first, since the Nextcloud desktop client first creates the temporary file. This callback also contains a workaround to call notesWereModified for the currently active note. However, since the Nextcloud client at this point only created the temporary file, the currently active note hasn't been modified yet. Thus, notesWereModified returns early. Afterwards, the temporary note file is modified by the Nextcloud sync client, which triggers notesDirectoryWasModified again for some reason, with the same result: no changes were detected because no changes to the actual note were performed yet.
Finally, the Nextcloud sync client moves the temporary note to the location of the actual note, thereby overwriting it. This triggers notesDirectoryWasModified again, but it still doesn't detect any changes, although they would be detectable by now. This is because before calling notesWereModified, notesDirectoryWasModified first rebuilds and reloads the note directory list, which results in the changes not being detected for some reason.
Lines 3112 to 3123 in 025e685
| // rebuild and reload the notes directory list | |
| buildNotesIndexAndLoadNoteDirectoryList(); | |
| // check if the current note was modified | |
| // this fixes not detected external note changes of the current note if the | |
| // event for the change in the current note comes after the event that the | |
| // note folder was modified | |
| QString noteFileName = currentNote.getFileName(); | |
| if (!noteFileName.isEmpty()) { | |
| // Use the full path, like a filesystem watcher would, instead of just the file-name | |
| notesWereModified(currentNote.fullNoteFilePath()); | |
| } |
Moving the call to buildNotesIndexAndLoadNoteDirectoryList() after notesWereModified() makes the detection functional. However, I'm not sure if this has additional unwanted side-effects.
This final move operation also triggers notesWereModified directly, because in addition to the notes directory, files are also watched. However, since the note index has been reloaded and rebuilt, no changes are detected here as well.
Another issue results from how Qt handles file replacements. Nextcloud overwriting the note file via a move operation destroys the original file's inode. This causes QFileSystemWatcher to silently detach from the note. The watcher is never reattached to the new inode, meaning subsequent modifications to that note are completely ignored.
Summary
In summary, there are two important issues with the change detection the way it's currently implemented:
- Rebuilding the notes index breaks the change detection
- File watchers aren't reattached if files are overwritten
As I don't know much about the inner workings of the note index, I don't have a suggestion for a fix there.
However, the second issue could be fixed by re-adding files to the noteDirectoryWatcher after they have been overwritten.