Ah I think I got it. Here is a decorator that can be used for client-side functions. It will work regardless of whether a server function is called or not.
Please let me know if you spot any errors.
from anvil.js import window
import anvil.server
from anvil.server import no_loading_indicator
import functools
def delayed_spinner(func, threshold=0.3):
"""
A decorator that only shows the built-in Anvil spinner if
the wrapped function takes longer than `threshold` seconds.
"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
# 1) Create controller for the default spinner
loading = anvil.server.loading_indicator()
# 2) Schedule .start() after `threshold` ms
timer_id = window.setTimeout(lambda: loading.start(),
int(threshold * 1000))
try:
# 3) Run the function with the automatic spinner suppressed
with no_loading_indicator:
return func(*args, **kwargs)
finally:
# 4) Cancel the pending start (if it hasn’t fired yet)
window.clearTimeout(timer_id)
# And stop it if it did fire
loading.stop()
return wrapper