Skip to content

plugin configs + brimcap yaml config/preference#1637

Merged
mason-fish merged 6 commits intomainfrom
1545-brimcap_preferences
May 17, 2021
Merged

plugin configs + brimcap yaml config/preference#1637
mason-fish merged 6 commits intomainfrom
1545-brimcap_preferences

Conversation

@mason-fish
Copy link
Contributor

@mason-fish mason-fish commented May 13, 2021

fixes #1545, #1593

This PR extends the brimApi to expose a generic persisting storage for plugins, and adds a new UI api which corresponds to what we currently see as preference options.

Following a bit of the naming choices made in VSCode, I have named this new api and its new backing storage/reducer configs, and have kept the remaining prefs that are not related to brimcap in their current place. We should eventually move those into a core plugin and store them under the new configs as well, so that we may fully transition away from the prefs state.

As it stands, the new redux slices we've added to facilitate plugins contributing ui elements (toolbars first, and now configs, and soon we will want something for contextMenus too) are NOT persisted. Every time the app starts up, plugins will populate and maintain these slices via the brimApi, the app will "react" to those state changes appropriately, and then when the app quits it will not save that ui state. What I like about this pattern is that by not persisting that plugin-generated state, and by regulating it's manipulation through the brimApi, we have more flexibility to make changes to the underlying app state shape without breaking things or requiring migrations.

This does create a need for plugins to persist their own data though, hence including this new generic storage object on the brimApi. For now it functions as a simple key value store, where the key is a plugin namespace and the value is arbitrary. I'd like to eventually protect this better so that plugins cannot access each other's storage which is currently possible, and when the need arises we could also enhance the storage to optionally use the keychain for a more secure persisting storage. This would then align the api pretty well for transitioning the auth related work into it's own plugin too.

Copy link
Contributor

@jameskerr jameskerr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good!

It looks like the "value" of a config is stored in the plugin storage slice and in the config slice (as the default value). I also see a new command created for each config property which is like an "onChange" listener.

I propose we store the config settings in a state slice (like you have), but save the config values in another separate slice of state. (config values). Config values will be persisted. The form could handle updating the config values automatically whenever it gets submitted. Then we wouldn't need to manually save the values in storage or update the default value. We also wouldn't need the custom command.

If we did that, the brimcap plugin would become...

// brimcap-plugin.ts

setupConfig() {
  this.api.configs.add({
      name: this.pluginNamespace,
      title: "Brimcap Settings",
      properties: {
        [configPropertyName]: {
          name: configPropertyName,
          type: "file",
          label: "Brimcap YAML Config File",
          defaultValue: "" // only used in the form if the config value is unset
        }
      }
  })
}

The configs api could become...

class ConfigsApi {
  add(config: Config) {
    this.dispatch(Configs.create(config))
  }

  update(key, value) {
    this.dispatch(ConfigValues.set(key, value))
  }

  get(key) {
     ConfigValues.get(key)(this.getState())
  }
}

And the useConfigsForm hook would change the submit function to...

// usePreferencesForm.ts line 28

const submit = (value) => dispatch(ConfigValues.set(key, value))

I like the idea of having the values all stored together and managed by the preferences form. As a plugin, I just add the configuration object and I know that whenever I ask for the value, it will be up to date.

I like the storage state slice and api, so let's leave that. It can be used for any data that the plugin wants to keep around and it will be responsible for updates.

What do you think?

}
})

return <>{formInputs}</>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return <>{formInputs}</>
return formInputs

const api = new BrimApi()
const store = await initStore(api)
api.setStoreArgs(store.dispatch, store.getState)
api.init(store.dispatch, store.getState)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

this.dispatch(Configs.create(config))
}

public updatePropertyDefault(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public updatePropertyDefault(
updatePropertyDefault(

export class ConfigsApi {
constructor(private dispatch: AppDispatch, private getState: () => State) {}

public add(config: Config) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public add(config: Config) {
add(config: Config) {

@mason-fish mason-fish force-pushed the 1545-brimcap_preferences branch from cc65fe0 to e3b6487 Compare May 17, 2021 17:18
Signed-off-by: Mason Fish <[email protected]>
@mason-fish mason-fish requested a review from jameskerr May 17, 2021 18:16
Copy link
Contributor

@jameskerr jameskerr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is coming together! I left a few suggestions for ya. The most important to me is saving the config values as a flat object, instead of nested by name. What do you think of that?

}

const slice = createSlice({
name: "$configPropValues",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about calling this just "ConfigValues" ?

Comment on lines +25 to +29
ConfigPropValues.set({
configName: configName1,
propName: configProperty1,
value: data1
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about making the config values a flat object, and we trust that the keys will be prefixed properly? I think it would make for a more clear, less verbose api if it looked like: ConfigValues.set(key, value)

export type ConfigItemType = "file" | "string" // | "number" | "boolean"

export type ConfigItem = {
name: string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this name doesn't need to be stored? Since the key and the name are the same and should be in sync?

@jameskerr jameskerr self-requested a review May 17, 2021 19:43
@mason-fish mason-fish merged commit df3ad18 into main May 17, 2021
@mason-fish mason-fish deleted the 1545-brimcap_preferences branch May 17, 2021 21:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Conditional Preferences

2 participants