chhoumann / MetaEdit

MetaEdit for Obsidian
https://bagerbach.com
GNU General Public License v3.0
393 stars 15 forks source link

createYamlProperty can not create multiple properties #73

Open panwanke opened 2 years ago

panwanke commented 2 years ago

When I use metaEditApi.createYamlProperty, It can only create one property.

When using forEach or loop function, the createYamlProperty just create one meta property, and the posterior property would cover the prior.

When I set timeout or seperately ran this function, it maybe create multiple properties but with multiple dividers.

Please provide some APIs that can be used to create or update more diverse properties.

jdmorg0 commented 2 years ago

Did you ever figure out the problem? I'm able to create multiple properties in a loop without any problems.

I've had a similar issue with this plugin before, and I realized that it was caused by missing a single await statement when calling one of the api async functions. I was also able to recreate an incorrect output with multiple dividers (like you described) when I ran a createYamlProperty loop without using await.

You may have already done this. but check to make sure you didn't miss an await when calling the createYamlProperty function, other async functions from the api, or async functions used from your own scripts.

If that's not the problem, I may be able to help if you provide some more details like your code/output/expected output.

Spiderbrigade commented 1 year ago

I can duplicate this, when using metaedit as part of a templater template (edited to add: this is on Win10). I'm trying to set up a templater template that uses the metaedit api to create multiple frontmatter fields. Creating a single property works as expected. In practice I want to do some logic but for testing purposes I'm running:

<%*
const {createYamlProperty } = this.app.plugins.plugins["metaedit"].api;

var active_file = await app.workspace.getActiveFile()
await createYamlProperty('AB', 1, active_file);

-%>

This works as expected, outputting

---
AB: 1
---
Note Content

But, I would like the templater template to create multiple properties at once. As in the earlier reply, I'm being careful to await everything:

<%*
const {createYamlProperty } = this.app.plugins.plugins["metaedit"].api;

var active_file = await app.workspace.getActiveFile()
var ab = await createYamlProperty('AB', 1, active_file );
var bb = await createYamlProperty('BB', 1, active_file );
var cb = await createYamlProperty('CB', 1, active_file );

-%>

I would expect this to produce the following:

---
CB: 1
BB: 1
AB: 1
---
Note Content

...and it very rarely does produce that, but usually I get:

---
CB: 1
---
---
BB: 1
---
---
AB: 1
---
Note Content

This suggests that there is some timing error happening where the frontmatter is being created three times. There are also occasional intermediate cases where AB and BB are created in one frontmatter, and CB in a second. I have also tried wrapping the property creation in an async function, as well as using a for loop. There are also occasional intermediate cases where AB and BB are created in one frontmatter, and CB in a second.

panwanke commented 1 year ago

This bug seems to occur in the file with notes below the fronmatter.

For creating a new file, I have not encounter with this probelm. But for the existing file, I occasionally get the same issues:

---
CB: 1
---
---
BB: 1
---
---
AB: 1
---
Note Content

Or get many blank lines:


---
CB: 1
BB: 1
AB: 1
---
xmacex commented 1 year ago

I have the same issue, hoping to create multiple front matter properties. My template is

<%*
const {createYamlProperty} = this.app.plugins.plugins["metaedit"].api;

await createYamlProperty("note-due", tp.date.now("YYYY-MM-DD"), tp.config.target_file.path);
await createYamlProperty("todo", true, tp.config.target_file.path);
%>
LordMartron94 commented 10 months ago

Yup. I got the same. However, my initial YAML was created by DB Folder.

I even put some delays between the calls.

I also have other functions where I use meta-edit's API to update or create values... If this is not fixed, I will have to create my own variant for myself... Because this is absolutely vital for my task manager, even a margin of 10% of cases where it happens can be catastrophic.

async updateMetadata(updatedEvent) {
        const waitAmount = 100

        let newStartDate = new Date(updatedEvent.start);
        let newEndDate = new Date(updatedEvent.end);
        let newColor = updatedEvent.extendedProps.color;
        let newDescription = updatedEvent.extendedProps.description;

        console.log(newDescription);

        let formattedStartDate = formatDate(newStartDate);
        let formattedEndDate = formatDate(newEndDate);

        let datePropName = updatedEvent.extendedProps.datePropName;
        let colorPropName = updatedEvent.extendedProps.colorPropName;
        let descriptionPropName = updatedEvent.extendedProps.descriptionPropName;

        let path = updatedEvent.extendedProps.note.file.path;

        // Delays are essential because of an issue with meta-edit where changes do not persist with consecutive edits.
        // See: https://github.com/chhoumann/MetaEdit/issues/83
        await update(datePropName, formattedStartDate, path);
        await new Promise(r => setTimeout(r, waitAmount));

        await update(`${datePropName}_End`, formattedEndDate, path);
        await new Promise(r => setTimeout(r, waitAmount));

        await update(colorPropName, newColor, path);
        await new Promise(r => setTimeout(r, waitAmount));

        await update(descriptionPropName, newDescription, path);
    }

async getId() {
        let id;

        if (this.note.id === undefined){
            id = UUIDv4();
            await createYamlProperty("id", id, this.note.file.path);
        }
        else if (this.note.id == null)
        {
            id = UUIDv4();
            await update("id", id, this.note.file.path);
        }
        else if (this.note.id === "")
        {
            id = UUIDv4();
            await update("id", id, this.note.file.path);
        }
        else
        {
            id = this.note.id;
        }

        return id;
    }

    async getEventColor(defaultColor = "rgb(117, 117, 117)") {
        let colorString = await getPropertyValue(this.colorPropName, this.note.file.path);

        if (colorString === undefined) {
            await createYamlProperty(this.colorPropName, defaultColor, this.note.file.path);

            // console.log("Undefined color for: " + this.note.file.name + " created: " + defaultColor);

            return defaultColor;
        }
        else if (colorString == null) {
            await update(this.colorPropName, defaultColor, this.note.file.path);

            // console.log("Null color for: " + this.note.file.name + " updated: " + defaultColor);

            return defaultColor;
        }
        else if (colorString === "") {
            await update(this.colorPropName, defaultColor, this.note.file.path);

            // console.log("Empty color for: " + this.note.file.name + " updated: " + defaultColor);

            return defaultColor;
        }
        else {
            // console.log("Filled color for: " + this.note.file.name + " retrieved: " + colorString);

            return colorString;
        }
    }

async parseStartDate() {
        let dateString = await getPropertyValue(this.datePropName, this.note.file.path);

        if (dateString == null || dateString === "") {
            let date = new Date(); // Assign current date and time
            await createYamlProperty(this.datePropName, formatDate(date), this.note.file.path);

            return Date.parse(date);
        }

        return Date.parse(dateString);
    }

    async parseEndDate() {
        let dateString = await getPropertyValue(this.datePropName + "_End", this.note.file.path);

        if (dateString == null || dateString === "") {
            let date = await this.parseStartDate();
            date = await this.getEndDate(date, 0, 30); // Assign default duration of half an hour

            await createYamlProperty(this.datePropName + "_End", formatDate(new Date(date)), this.note.file.path);

            return Date.parse(date);
        }

        return Date.parse(dateString);
    }

    async changeColor(colorString) {
        await update(this.colorPropName, colorString, this.note.file.path);
    }

    async getEndDate(endDate, hours = 0, minutes = 0) {
        try {
            // If endDate is a timestamp (milliseconds since epoch), convert it to a Date
            if (typeof endDate === "number") {
                endDate = new Date(endDate);
            }
            if (endDate instanceof Date && !isNaN(endDate)) {
                // Calculate the end date by adding hours and minutes to the start date
                endDate.setHours(endDate.getHours() + hours);
                endDate.setMinutes(endDate.getMinutes() + minutes);

                // Format the end date as a datetime string (e.g., "YYYY-MM-DDTHH:mm:ss")
                return formatDate(endDate);
            } else {
                console.error("Invalid start date:", endDate);
                return null; // Handle invalid start date gracefully
            }
        } catch (error) {
            console.error("Error parsing start date:", error);
            return null; // Handle errors gracefully
        }
    }

    async getDescription(defaultDescription) {
        let description = await getPropertyValue(this.descriptionPropName, this.note.file.path);

        if (description === undefined){
            description = defaultDescription;
            await createYamlProperty(this.descriptionPropName, description, this.note.file.path);
        }
        else if (description == null)
        {
            description = defaultDescription;
            await update(this.descriptionPropName, description, this.note.file.path);
        }
        else if (description === "" && description !== defaultDescription)
        {
            description = defaultDescription;
            await update(this.descriptionPropName, description, this.note.file.path);
        }
        else
        {
            return description;
        }
    }