Achieve decent user experience when a user leaves an app for a while (say 1 hour), comes back to the page, enters input, and hits submit.
The default behavior is pretty bad – the user gets “Your session has timed out. Please refresh the page to continue.”, clicks Refresh, loses all input, and must reenter them.
A few possible solutions
Remember all form state so that, after the refresh, the information is at least repopulated for the user.
Pro: Ok user experience
Con: More boilerplate code to save/restore input fields
Detect a long delay between window focuses, and just refresh the page immediately.
Something like this
def __init__(...):
self.last_focus_time = time.time()
# 10 min < ~30 min after which Anvil kills session
self.window_focus_threshold = 10 * 60
anvil.js.window.addEventListener("focus", self.window_focus)
def window_focus(self, event):
time_elapsed = time.time() - self.last_focus_time
if time_elapsed > self.window_focus_threshold:
anvil.js.window.removeEventListener("focus", self.window_focus)
anvil.js.window.location.reload()
self.last_focus_time = time.time()
Pros: No error message, no ability to enter input that will soon be lost
Con: Relies on JS rather than core Anvil behavior
My actual question
This seems like a very vanilla issue, so my question is
Are there any patterns that I’m missing that achieve better UX without resorting to using JS? Relying instead on core Anvil functionality?
Related posts
I found 3 somewhat related posts, but they focus on different issues, and no solutions are applicable here.
I haven’t used it, but in the docs on Sessions there’s a section on recovering from an expired session that doesn’t involve refreshing the entire app: Anvil Docs | Sessions and Cookies
I had the same issue with an earlier app I wrote. I simply dragged a timer component onto the form. Then I set the interval to 1740 seconds which equals 29 minutes. After that time has elapsed it runs the Tick event.
I just had the event trigger run a low intensity server call which refreshed the page to keep the session alive.
def timer_1_tick(self, **event_args):
“”“This method is called Every [interval] seconds. Does not trigger if [interval] is 0.”“”
I guess if you don’t want to refresh and lose any info already keyed in, you could call some code which does nothing just to keep the session alive, You will have to ensure the timer resets again.
Maybe this is useful?
Just tested on my new site… works well. Timer automatically resets after it triggers.
I import this app as a dependency into all my other apps, and in my forms:
import anvil.server # Delete this (automatically added by Anvil)
import restore_session.server as server #import
..
def call_server_func(self):
server.call('my_server_func') # call it just like anvil.server, but now with session recovery.
I see from my error logs that sometimes my users still get a SessionExpiredError, but not often.