Shiny Bookmarking to automatically save and restore state

I know this is an old discussion, but I wanted to chime in with a hack/solution I came up with recently. I had a similar request, though it was primarily due to periodic instability (network glitches, app errors, etc). The problem with doing this automatically is that you effectively have to observe every input. It does not automatically reload a snapshot, as I did not have sufficient time to verify that it would not auto-reload in the middle of a valid session.

The module code is a bit long (and currently a little sloppy), but the gist of what I had to do:

  • single-app sqlite database, single table, with session$user, the type of save (auto or manual), a timestamp, and the json-ified (jsonlite::toJSON) list of snapshot-values; could easily be a more permanent db solution (postgres, mariadb, etc)
  • inputs:
    • checkboxInput: autosave
    • selectInput: previously-stored snapshots
    • three actionButton: save-now, restore, expire-now (could be better)
  • the server function is passed a reactive list of "interesting" inputs (as a reactive dat()) from the main app
  • new observe blocks:
    • observe on dat() (and input$autosave), storing the last-snapshot to check for "effectively different" and a minimum time since last save before auto-saving; does nothing if !input$autosave;
    • observeEvent(input$savenow, ...)
    • observeEvent(input$restore, ...) using the selected snapshot
    • observeEvent(input$expirenow, ...), actually opening a modal that confirms expiration;
    • timestamps <- eventReactive that looks for the DB connection as well as an invalidateLater to periodically look for snapshots saved to the DB from other same-user sessions;
    • observeEvent(timestamps(), ...) in order to updateSelectInput; and
    • optional observe with invalidateLater to auto-expire stale snapshots.
  • server function returns a list similar to dat() that is updated with the restored snapshot (on which the main app should observeEvent and update inputs)

While I'm not averse in general to releasing the code, it was written in haste for a specific weekend flurry of activity, and really needs to be cleaned up for more general and well-behaved use. Because it tends to touch all (relevant) inputs simultaneously, it can cause a flurry of reactivity in the app, so poorly-controlled reactivity is quickly punished.

Possible extension: user-named snapshots, not just auto/manual.

2 Likes