loadingStages

What the scene's loading spinner shows and tells the viewer as a wait goes on. Pure apart from nowMs(): JRScene passes what is waiting and the time, so the rule is the same on any screen and in a test.

Three things can be waiting: the scene's own spinner (startLoadingSpinner(), a cast), the app waits the whole app is in (appWaits, a playback start), and the named waits the active screen has open (screenWaits). showing() decides between them.

Nothing is said before STILL_LOADING_MS: a wait that short is normal, and even a 512 MB Stick shows a large library's first page in about 3.4 s from a server that keeps up (measured 2026-09-24, 8,643-movie library). Past it the viewer is told the app is still working, which is the usual ~10 s point where a wait needs feedback; past SERVER_SLOW_MS, that the wait is the server's. That is true of every kind that gets text, because each waits on a server answer. Kinds whose wait is not a server answer get no text at all.

Members

(static, constant) MAX_AGE_MS

Past every stage; ageMs() stops counting here.

Default Value
  • 86400000

(static, constant) SERVER_SLOW_MS

Default Value
  • 30000

(static, constant) STILL_LOADING_MS

Default Value
  • 8000

Methods

(static) ageMs(startedMs, now) → {integer}

Milliseconds from startedMs to now, both from nowMs(), as an Integer the stage rules take. Clamped: a wall clock can step backwards (a time sync), and a wait a day old is past every stage anyway.

Parameters:
NameTypeDescription
startedMsdynamic
nowdynamic
Returns:
Type: 
integer

(static) hasStages(kind) → {boolean}

Whether kind tells the viewer anything as its wait goes on.

Parameters:
NameTypeDescription
kindstring
Returns:
Type: 
boolean

(static) longestWait(entries) → {dynamic}

The wait whose text shows among entries ({ kind, startedMs }): the oldest one with stages, or the oldest one when none has any. invalid when entries is empty.

Parameters:
NameTypeDescription
entriesobject
Returns:
Type: 
dynamic

(static) msUntilNextChange(kind, elapsedMs) → {integer}

Milliseconds from elapsedMs until the text for kind next changes, or -1 when it never will: the kind has no stages, or the last one is already showing.

Parameters:
NameTypeDescription
kindstring
elapsedMsinteger
Returns:
Type: 
integer

(static) nowMs() → {dynamic}

Wall-clock milliseconds as a LongInteger: the one clock a screen's waits and the scene both read, so a wait's age means the same on either side. Not an roTimespan, which belongs to one component and whose Integer milliseconds overflow after ~24.8 days of the app running; not UpTime(), a 32-bit Float, too coarse on a device that has been up for months. A wall clock can step (a time sync); the only effect is stage text shown early or late once.

Returns:
Type: 
dynamic

(static) showing(sceneWait, appWaits, waits, now) → {object}

What the spinner shows now: { visible, kind, elapsedMs, label }.

Waits the whole app is in (the scene's own spinner and its app waits) win while any is open, and the screen's waits show again, still counting from when they started, once the last of those ends. Among the waits that show, the text counts from the OLDEST one with stages, since that is how long the viewer has been waiting. label is the newest app wait's label (a channel's name), "" when none has one; the scene's own spinner carries its text in loadingText instead, so the scene chooses between the two.

Parameters:
NameTypeDescription
sceneWaitobject

{ isLoading, kind, startedMs } of the scene's own spinner

appWaitsdynamic

the scene's open app waits (key -> { kind, label, startedMs }), or invalid

waitsdynamic

the active screen's loadingWaits (name -> { kind, startedMs }), or invalid

nowdynamic
Returns:
Type: 
object

(static) textKeyAt(kind, elapsedMs) → {string}

The translation key for the text under the spinner after elapsedMs, or "" for none.

Parameters:
NameTypeDescription
kindstring
elapsedMsinteger
Returns:
Type: 
string