Repository navigation
v6.2.3 — per-registration periodic refresh; FsTabPanel disposes tab contents (breaking)
Changed (BREAKING — removes public API)
-
The periodic-refresh scheduler is now owned per registration, not globally. Registration,
scheduling and lifetime used to be braided together inViewDataContainer: a companion map keyed
by instance identity, onesetIntervalcreated by whichever container happened to mount first
(its closure capturing that instance'speriodicUpdate,periodicUpdateViewIntervaland
lastUiActivity), and ahandleIntervalsetter that onnulldidclearIntervaland
dataUpdateFuncs.clear().onBeforeDispose()did exactly that, so closing any one view
stopped the periodic refresh of every other live view — silently: no error, the table just stops
updating. A single globalstartTimealso meant the table that ran postponed all the others.The new
PeriodicRefreshSchedulerhands out a token per registration;unregister(token)removes
that entry and nothing else; the shared timer belongs to the scheduler (its handler captures no
instance), starts with the first registration and stops only when the last one leaves; each entry
carries its own interval andlastRun.Four public members were removed from
ViewDataContainer:startTime,dataUpdateFuncs,
handleIntervalandrunPeriodicBlock().installUpdate(),clearStartTime(),
allowInstallPeriodicUpdate,suspendPeriodicUpdate()andresumePeriodicUpdate()keep their
names and behaviour;uninstallUpdate()is new. -
periodicUpdateDataView = falsenow means what its name says. A container that opted out was
still added to the map and got refreshed whenever another view's timer was running — the flag
only decided who created the timer. It is read at registration and again, live, on every sweep:
turning it off in flight takes effect at once; turning it on requires the nextinstallUpdate()
(whichfsTabulatorissues on each render andViewItemon each item load). -
View.lastUiActivitymoved from instance scope toView's companion. It was an instance field
read from the timer closure, so the inactivity threshold was measured against the activity of
whichever instance had created the timer. User activity is one value, not one per view.
Added
-
FsTabPanel/fsTabPanel(com.fonrouge.fullStack.panel), a drop-in replacement for
KVision'sTabPanelthat disposes its tabs.TabPanelkeeps its tabs in its owntabslist
—addTabsetstab.parent = navand never adds them tochildren/privateChildren— and
overridesdisposeAll()but notdispose(). SinceSimplePanel.dispose()only walks
childrenandprivateChildren, disposing aTabPanelnever reaches its tabs, so no
addBeforeDisposeHookregistered inside a tab ever fires — periodic refreshes, subscriptions,
timers, any cleanup. It is a general KVision lifecycle defect, not specific to fsLib; verified
against KVision 9.5.0 and 9.6.0.The wrapper removes each tab through the protected
removeTab(over a copy of the list, which
removeTabmutates) before disposing it, so no graph of disposed components is retained.
disposeAll()is deliberately not reused: it ends inremoveAll(), which iteratestabswhile
removeTabremoves from it.FsTabPanelDisposeTest.tabPanelCrudoNoDestruyeSusPestanaspins the upstream behaviour: when
KVision overridesdispose(), that test fails — the signal to retire this class.
Fixed
- A periodic refresh registered inside a tab no longer outlives its view. Measured in the
consumer app: with an item open in a modal, four refreshes ran on an exact 5 s grid; on closing,
the item's own refresh and the route graph unregistered, but a list embedded in a tab kept
fetching — 40 s later it still was — and every re-activation of that tab added another call per
cycle. A latent KVision lifecycle defect, previously masked by this library's own global
destruction of every registration on any view's disposal: the two bugs concealed each other. - An embedded list rebuilt inside a
bind { … }no longer leaks its previous registration. A list
used throughfsTabulatornever goes throughView.startDisplayPage, so it never receives
onBeforeDispose(); the mounted panel now owns the token viaownPeriodicUpdateOf. inactivityUiSecsToNoRefreshis now honoured. The gate'sletreceived the configured
threshold and then compared against a literal60, so the setting worked only as an on/off switch
and the real cut-off was always 60 s. It went unnoticed becauseIUserSessionParamsCollseeds
exactly60: for the default configuration both behaviours coincide, and only an installation
that changed the number could see that its number did nothing. Following thesessionMaxSecs
idiom (IUserColl.kt:52),0now means disabled.
Added (tests)
- 25 browser tests over the scheduler's contract, plus 9 over tab lifecycle: ownership isolation, timer lifetime, per-entry
cadence, sweep robustness, install/uninstall idempotency, and — against KVision's real lifecycle —
panel destruction andbindrebuild (ownPeriodicUpdateOf). The clock and the timer are injected
so "exactly one timer", the inactivity pause and sweep re-entrancy are exercised without waiting.
The inactivity threshold is covered in both directions (a short threshold cuts before 60 s, a
long one is not truncated at 60 s) plus the0= disabled case. All guards are mutation-tested.
Migration Guide
- See MIGRATION.md.
Nothing to change unless you reference the four removed members or set
periodicUpdateDataView = falseon a view that you still expect to refresh.