Methods

(static) beginLogin() → {object}

Cold-start (and session-reset) fast path. Runs the saved-server resolution + saved- token validation WITHOUT showing any interactive UI, and returns a decision telling main.bs which route to bring the router up on (or that we are already logged in). Mirrors LoginFlow()'s startLogin: server block + the saved-active-user branch.

Returns:
Type: 
object

(static) buildPublicUserList() → {object}

Build the public + saved user list for the picker. Public users from the server, plus saved users for this server id not already in the public list. Mirrors LoginFlow 60-104. Returns an array of PublicUserData nodes.

Returns:
Type: 
object

(static) enterDecision(decision, clearSpinneropt, variantopt) → {void}

Bring the router up on (or navigate it to) the route the decision names. Shared by cold start, session reset, and the post-server-connect step. clearSpinner: pass true when navigating here with a blocking spinner up, so it stays up across the async nav and clears at NavigationEnd instead of being stopped early (which re-shows the outgoing view for a frame). The success branch defers to finishLogin, which carries the spinner to Home.

What paint means for preLogin, which is NOT a screen

Everywhere else in this app screenLoad.paint is a screen that rendered something the user can act on. Here it is the coordinator handing a route to the router: the user is on a spinner for the whole run and CANNOT act at this moment — the destination view has not mounted yet (sgRouter resolves at NavigationEnd, on the render thread, where this ledger's m does not reach). The destination's own paint is a SEPARATE run, the same shape search established for a screen whose two loads are separated by a user.

So this run's paint/settle split says nothing: every fill is a synchronous main-thread call that has already resolved by the time we get here, so settled lands on the same millisecond as paint — the query variant of search all over again. Read the fills. They are the actionable decomposition: which of the blocking round trips cost what.

variant names WHICH load, not which destination: start is a cold start or a session reset arriving through beginLogin, connect is a server submitted from SetServerScreen. The destination is already legible from the fills (a run carrying users built the picker), and one run per entry point is what keeps two loads that route to the same place from merging into one population.

Parameters:
NameTypeAttributesDefaultDescription
decisionobject
clearSpinnerboolean<optional>
false
variantstring<optional>
"start"
Returns:
Type: 
void

(static) finishLogin() → {void}

Login complete — run the post-login bootstrap (per-user font processing) and bring up Home. loadHomeScreen()/createAndShowHomeGroup() (main.bs) navigate the router to "/", deferring until font download completes when UI fallback fonts are enabled. The single transition into the app. EVERY login path converges here — password, no-password, saved-token and Quick Connect — either directly or through enterDecision's status = "success" branch, which is where the two bootstrap paths (validateSavedToken, the no-password retry in resolveUser) land.

That convergence is why the authentication check belongs HERE and nowhere else. user.Login() is a sub: it silently returns when the payload carries no usable id (session.bs hasValidId), and having no return value it cannot say so. Before this guard, all five call sites answered that by re-implementing a WEAKER check of their own — isValid(<node>) alone — and then booting the app regardless. A payload that satisfied the caller but not Login left m.global.user holding its setGlobals() defaults and put the user on Home with nobody signed in: no message, no recovery, and every request going out with an empty Token=.

One guard at the one choke point, rather than five copies of Login's own precondition — so a sixth login path inherits it instead of having to remember it. See user.IsAuthenticated() for why this reads isLoaded rather than the id/authToken rule JellyfinUser.xml states (those are written speculatively before auth, so they answer "yes" after a failed attempt).

Returns:
Type: 
void

(static) handlePreLoginIntent(action) → {void}


Intent dispatch — called from main.bs's event loop when a pre-login view sets m.scene.preLoginIntent. The payload is read off the active routed view.

Parameters:
NameTypeDescription
actionstring
Returns:
Type: 
void

(static) onCredentialsSubmitted() → {void}

Credentials submitted from LoginScene. Mirrors CreateSigninGroup's submit handler.

Returns:
Type: 
void

(static) onLoginBack() → {void}

Back from LoginScene. Mirrors CreateSigninGroup back (showScenes 170-178): return to the user picker if there were public users, otherwise drop back to server select.

Returns:
Type: 
void

(static) onQuickConnectAuthenticated() → {void}

Quick Connect produced a session. UserSelect owns everything before this point — it initiates, polls for approval, exchanges the secret and asks whether to save credentials, all as render-thread promises — and hands over the finished AuthenticationResult. What is left is the one step that must NOT run on the render thread: user.Login() reads and writes the registry.

Deliberately the same two lines the password path ends on, because it IS the same thing: an AuthenticationResult carrying an AccessToken and a UserDto. The old Quick Connect ran its own login on a TASK thread, then re-fetched the user with AboutMe() and re-loaded preferences that user.Login() had already loaded — three round trips where the password path makes one.

Returns:
Type: 
void

(static) onServerSubmitted() → {void}

Server URL submitted from SetServerScreen. Mirrors CreateServerGroup's submit handler.

Returns:
Type: 
void

(static) onUserBack() → {void}

Back from UserSelect → change server. Mirrors LoginFlow 114-119.

Returns:
Type: 
void

(static) onUserSelected() → {void}

A public/saved user was picked in UserSelect. Mirrors LoginFlow 120-163.

Returns:
Type: 
void

(static) reenterLogin() → {void}

Enter (or re-enter) the login flow. Called at cold start AND on every session reset (Change Server / User / Sign Out). Re-resolves the pre-login locale first — without this, a changed global sign-in language would only apply after a full app restart, and the sign-in screens would render in the just-signed-out user's language rather than the device-wide default (isPostLogin defaults false → pre-login cascade).

Returns:
Type: 
void

(static) resolveUser() → {object}

Decide the user step given a valid server. Returns one of: { status: "success" } — already authenticated (fast path) { status: "users", users: [...] } — show the user picker { status: "login", username: "" } — go straight to manual sign-in enterDecision maps each status to its route; the decision carries only payload. Mirrors LoginFlow()'s user section (active-user branch + no-active-user branch).

Returns:
Type: 
object

(static) routerNav(routePath, context, clearSpinneropt) → {void}

Thin main-thread → render-thread navigation bridge. clearSpinner (login paths only) keeps a blocking login spinner up across the async nav and clears it when the destination view mounts — see JRScene.navigateThenFocus.

Parameters:
NameTypeAttributesDefaultDescription
routePathstring
contextobject
clearSpinnerboolean<optional>
false
Returns:
Type: 
void

(static) validateSavedToken(localUser, token) → {boolean}

Validate a saved auth token: set it on the local user and verify it with AboutMe(). On success, logs the user in (user.Login) and returns true. On failure returns false — the caller owns the cleanup/fallback, which diverges between the two call sites (cold start unsets active_user + recurses; the user picker falls through to a no-password login). Shared by resolveUser (cold start) and onUserSelected (user picker).

Parameters:
NameTypeDescription
localUserobject
tokenstring
Returns:
Type: 
boolean