obsidianmd/obsidian-api

Bug (?): `create` event fires when notes are synced via Obsidian Sync

jose-elias-alvarez opened this issue · 2 comments

Steps to reproduce:

  1. Set up the following plugin and enable it on multiple Obsidian instances, each of which is using Obsidian Sync:
import { Plugin, TFile } from "obsidian";

export default class SamplePlugin extends Plugin {
    async onload() {
        this.app.workspace.onLayoutReady(() =>
            this.app.vault.on("create", async (file) => {
                if (!(file instanceof TFile)) return;

                const newContent = "created\n";
                const existingContent = await this.app.vault.adapter.read(
                    file.path
                );
                const content = existingContent + newContent;
                await this.app.vault.adapter.write(file.path, content);
            })
        );
    }
}
  1. Create a new note in one Obsidian instance and observe that it initially contains a single line, created
  2. Wait for the other instance(s) to sync and observe that each instance adds another created line

First, I'm not sure whether this is a bug, since I suppose that from the perspective of each Obsidian instance, the synced file is indeed a newly created file. But the resulting behavior is arguably surprising, since from the consumer's perspective, the note was already "created".

To give a concrete example of an issue caused by this behavior, I am working on a plugin to automatically insert a template when creating a new note. As in the example above, I join the template with the note's existing content, to account for methods of new note creation that prefill the new note (e.g. the note composer core plugin). As a result, when the new note is synced, the template is inserted again, since each sync fires a create event.

For my use case, I see a few workarounds:

  1. Don't insert anything into new notes that already contain content (this solves the multiplication issue but limits the plugin's functionality)
  2. Use a property or some other unique identifier to determine if the note truly was created or just synced (this is easy enough on a technical level but pollutes each note)
  3. Use a separate data store (e.g. a JSON file) to track file creation (this starts to get complicated and also relies on a specific sync order)
  4. Compare the file's stat.ctime property to the time of the event as a heuristic to determine if the file was created or synced (this isn't perfect, but this is what I am doing for now)

But I can also imagine that other API consumers that want to act only when a note is created for the first time will run into the same issue, so if this is not considered a bug, I'd be curious to know if there is a recommended workaround. Thanks!

lishid commented

Unfortunately this is by design - "created" event is meant for various UI in Obsidian to know that a new file has appeared in the vault, so it is used by things like File Explorer to insert the file into the UI. You will also run into the same issue when the user uses a third party sync tool, as the files are created from the filesystem directly and seems to be a new file for the app.

I understand this is a common use case to do some work on a newly created file, and running it against existing files that came from elsewhere is problematic. Perhaps in the future we can look for some kind of API to detect files that are created internally by the user, but even that would rely on plugins and various parts of the app to properly use the API.

I would say that this fits under a feature request and thus should go on the forum under the developer section. I have a feeling there may already be such a request, but if not feel free to create one.

Thanks for the clarification - the stat.ctime solution seems okay for now, but I'll look into putting in a feature request (if there isn't one already).