marcusolsson/obsidian-projects

Update table after "Hide fields" popup goes away

Closed this issue ยท 8 comments

What happened?

With a table having 178 notes and 20+ fields, most of those fields aren't needed in the table. Right now, hiding a field refreshes the whole table which is slow. Toggling off many fields takes far too long.

What did you expect to happen?

I should be able to toggle many fields, and the update should happen after the popup closes

How can we reproduce it (as minimally and precisely as possible)?

generate a bunch of notes, with many fields, and random data in each field. Toggle multiple fields.

Anything else we need to know?

No response

Plugin version

1.10

Obsidian version

1.1.9

OS

Windows

This is a broader technical limitation that applies to all the views at the moment. Basically, each of the views are recreated whenever their settings changes. I'm aware of the issue, and I agree it's frustrating.

I need to do some more reading on Svelte's reactivity model to understand how to fix this. I had a go at it a while back but it turned into a mess that I had to revert.

(Did some debugging. This is mostly a note to my future self.)

The data source is updated whenever the project definition changes:

dataSource.set(resolveDataSource(selectedProject, $app));
)

... which causes the plugin to perform a new query:

dataFrame.set(await $dataSource.queryAll());

... which in turn removes the view while the query is running:

While the data source depends on the project definition, we don't need to run a new query whenever the data source changes.

For starters, we could change it so that the query only runs when the project id changes, or the dataSource changes. Changes to views shouldn't trigger new queries.

This issue is the bane of my existence at the moment. It's a Svelte reactivity issue and after three separate rewrites, I haven't been able to figure this out.

The plugin configuration right now looks something like this:

const settings = {
  projects: [
    {
      id: "...",
      name: "...",
      views: [
        {
          id: "...",
          name: "...",
          config: {
            // ... 
          }
        }
      ]
    }
  ]
}

... which means that a change to the view config triggers a change to the project. And any changes to the project triggers a new query to run. Since queries may take a while, the view gets unmounted and replaced with a loading indicator.

No changes to the view should result in a new query.

I need to step away from this now. If any Svelte developers out there can help me figure this out, I'd be eternally grateful.

I can take a look later, but just from skimming it sounds like: if updating settings causes it, you can just not update them right? Save it in a temporary until the modal is destroyed then save it in settings

If it helps, I tried to simplify the problem with two examples:

In the meantime, I'll see if I can start simplifying state management a bit. I'm definitely over-using stores which complicates things.

I've pushed a reactivity-issue branch with a simple playground to troubleshoot this.

I think if we can figure out a way to change the name of the view without running this statement, we should have a solution.

@marcusolsson I'm not really following. It looks to me that this issue is caused by this line:

config = { ...config, includeFields: [...includedFields] };

If the config update is routed into the "onDestroy" method of the Switcher I believe that fixes the problem.

Granted, the problems you're looking at are still concerning, but maybe deserving of a separate issue.

Found a solution (finally! ๐ŸŽ‰ ) to this, something I consider to be a "beautiful" hack.

By serializing the project definition for the data source into text, after having removed the views, Svelte can do a deep comparison of the project definition. Since the text representation of the project hasn't changed, it won't trigger an update.

// These shenanigans prevent queries to run when any of the views change.
// Even if an object didn't change, reassigning it still causes an update.
$: disassembedProject = disassemble(project);
// Strings are different though. Even if you reassign a string value, it won't
// trigger an update if it's the same string.
$: projectAsText = JSON.stringify(disassembedProject);
// This only runs if the JSON representation of a project (without views) has
// changed.
$: reassembledProject = reassemble(projectAsText);
// Setting a new data source causes the query to run.
$: dataSource.set(resolveDataSource(reassembledProject, $app));

@ggstrader Thanks for looking into this! ๐Ÿ™