New `PersistenceManager` component
I'd like to propose a new component and model to provide out-of-the-box support for user-managed collections of named, persisted state objects - e.g. grid states or dashboards. While the proposal below would not replace all of our current bespoke in-app implementations of similar features, it would aim to cover the "80%" use case, with room for future development.
- Proposed name (tentative):
PersistenceManager/PersistenceManagerModel - Overall goal: allow a user to select from and manage a library of persisted state bundles for common use cases such as grid views, dashboards, and other app-specific state, with a simple/intuitive UI and minimal requirements for app-level integration.
User-facing UI
- UI would be minimal - e.g. a button displaying the name of the currently selected state, with a drop-down menu and modal prompts/dialogs to support such user actions as:
- Selecting another available state object to set as current.
- Saving the current state, if modified, with the option to "Save As..." a copy with a new name.
- Resetting the current state, if modified, back to its last-loaded version.
- Deleting saved state objects that are no longer needed.
- When state is dirty (e.g. grid columns adjusted, dashboard widgets added/moved), the component should clearly indicate as much, providing the user with an option to Save/Save As - if they choose to do so.
- The user can also choose not to save dirty state, in which case any changes will be discarded upon app reload or a switch to another state.
- Deliberately not proposing any kind of autosave at this point.
- Also would like to avoid "you have unsaved changes" prompts, which are often disruptive. Goal is to treat these states as relatively lightweight, easy-to-reconstruct objects that introduce minimal cognitive overhead or friction for the user - i.e. you can just not touch it, and you don't be prompted to make any decisions.
- Shared/centrally managed state options might also be displayed if available (see below). These should be clearly distinguished from user-created and managed options.
- We could target desktop initially, but should keep mobile in mind for a quick follow-on impl.
Developer API
- From the developer's perspective, the new model would expose the currently selected state as an
observableand also via aPersistenceProviderthat could be bound directly to any component/model that supports Hoist persistence.- Goal of the built-in persist provider is to make the component as easy to drop in and get started using as possible, leveraging the work we've already done around persistence.
- Backing storage of saved states could be provided by
JsonBlobService, with minimal setup required to start listing and saving blobs of a particular type. We can discuss if an alternate or customizable storage mechanism is warranted for a first iteration. - Centrally managed or shared state configs could be optionally supported by creating JsonBlobs (via Admin console or other means) of the configured type, not owned by the user, but with an open ACL.
- The component would display these to the user, marking or styling them somehow as centrally managed / shared.
- User can load a shared state and immediately start making changes. If they wish to persist their changes, they should have the option to "Save As" to create their own copy, now entirely independent from the shared blob, which they can go on to continue modifying and directly saving in place.
- End-user-driven shared state is not proposed for an initial iteration.
- We can discuss if we want to support specifying default / starting state in-code, without the need to create a shared JsonBlob.
Sounds great to me! Will be awesome to build the generic version of this, and start standardising our somewhat haphazard implementations. Particularly like the emphasis on making it easy to ignore w.r.t "you have unsaved changes" - catching and blocking on those actions is always problematic.
A couple of thoughts / questions:
- Should we always allow the user to "Save As", even if they don't have local modifications? I think so, a user should be free to make as many clone(s) of a clean saved state as they want, without needing to 'twiddle' things.
- We need to be careful about built in affordances that claim to reset state, e.g. "Restore Grid Defaults" in the context menu. What does that mean in a
PersistenceManagercontext? Current behaviour would go back to code defaults, but a user could reasonably expect it to go back to the saved state (or switch to a centrally managed 'default' state). Especially weird since in most cases "Restore Grid Defaults" would actually make a 'clean' managed state > 'dirty'. Perhaps the easiest solution is to provide a way for components to detect when they are being managed by aPersistenceManager, and drop such affordances, relying entirely on the managed state?
Have set up something very much like this in a client app, using JsonBlobs for storage - happy to demo/review that anytime
Thanks for looking over this carefully.
Should we always allow the user to "Save As", even if they don't have local modifications?
Yes, agree - no reason to prevent that, very standard operation to save as before editing.
We need to be careful about built in affordances that claim to reset state, e.g. "Restore Grid Defaults" in the context menu. [...] Perhaps the easiest solution is to provide a way for components to detect when they are being managed by a
PersistenceManager, and drop such affordances, relying entirely on the managed state?
Great point. and agree that we need to have a plan here. I think we support hiding the restore option on colChooserModel, and you can pass in a custom grid context menu, but doing that every time would be tiresome. Might cause us to revisit the work we just did around support for custom restoreDefaultsFn - in this case, you might wish to completely intercept/override the default behavior...
Let's make some time next week to discuss in more detail - I will loop back with @lbwexler and get his thoughts on priority. The use case in the client app doesn't look like it's urgent - still important, but we have some time - and while I think this will be a great win for Hoist, we don't want to rush it!