6.4 KiB
Splash & mascot — open notes
State of play after the connection-indicator (SyncIndicator) work landed.
Anything in this file is deferred / future work, not active. Treat it as
context for the next person to pick up the splash topic.
Current visible chain (cold load, web/desktop)
- Initial blank — HTML loaded, React mounts. ~50ms.
bodybackground is#0d0e11(the--vojo-safe-area-bgvar), so it's a dark frame, not white. ConfigConfigLoadingmascot splash —pages/ConfigConfig.tsxrenders<AuthSplashScreen />(mascot + footer) while/config.jsonis fetched. ~50–300ms on Caddy-served static.SpecVersionsmascot splash —pages/client/SpecVersions.tsxrenders<AuthSplashScreen />while/_matrix/client/versionsis fetched from the homeserver. 0.5–2s, network-bound, the longest of the chain.ClientRootmascot splash —pages/client/ClientRoot.tsxloading || !mx ? <ClientRootLoading /> : .... Shown whileinitClientruns (IndexedDB open + crypto WASM init, ~300–700ms) AND while waiting forSyncState.Prepared(theuseSyncStategate at line ~200). The Prepared wait can be 1–30s depending on cache warmth and account size.- App tree mounts.
SyncIndicatortakes over for the residual sync activity (green slide while sync is still settling, hidden once Syncing).
Current visible chain (Android native)
- OS system splash — Android 12+ enforces a system splash with the
launcher icon centered over
windowBackground. Configured viaandroid/app/src/main/res/values/styles.xmlAppTheme.NoActionBarLaunch→windowBackground=@android:color/black. Cannot be fully eliminated; ~300–500ms while Android starts the process and Capacitor loads the WebView. - Then the web chain (steps 1–5 above).
So an Android cold-launch sees: black-icon (OS) → black blank (HTML) → mascot (config + spec-versions + initClient + Prepared) → app.
What we tried and reverted (during the SyncIndicator work)
In the 0.3.0 attempt that got reverted, we made several aggressive changes to shorten the chain. They worked, but introduced subtle bugs that the user chose to roll back to keep the diff clean. Specifically:
A. SpecVersions made non-blocking
[Commit reverted in ff01e6c.] The change made SpecVersions render
children immediately with versions: [] and hydrate the real value in the
background (with retry on online event). This eliminated the 0.5–2s
mascot in step 3.
Why reverted: it surfaced a latent bug in
components/message/content/ImageContent.tsx
and VideoContent.tsx. While versions: [], useMediaAuthentication()
returns false, autoPlay images load with unauth URLs, the server rejects,
the local error=true flag latches and is never cleared on the post-
hydration retry. The reviewer flagged this as a blocker.
Fix attempted: reset error and load state in the loadSrc-deps
useEffect AND in handleLoad — both ImageContent.tsx and
VideoContent.tsx. The fix was correct but the user judged it as out-of-
scope scope-creep for the connection-indicator feature and rolled the
whole change back.
To redo cleanly: ship SpecVersions-non-blocking and the ImageContent/VideoContent fix together, in a separate dedicated commit (scope = "make boot non-blocking", not bundled with the indicator).
B. ClientRoot loading paths replaced with null
pages/ConfigConfig.tsx::ConfigConfigLoading
returned null instead of <AuthSplashScreen />. Same for the !mx branch
in ClientRoot. Eliminated the mascot during steps 2 and 4-init-only.
Reverted as part of the same revert.
To redo: low risk on its own. Change those two callsites to render
null (the dark body background shows through). Skip the Prepared gate
removal (that's the deeper change).
C. ClientRoot Prepared gate removed
The ambitious move: render MatrixClientProvider as soon as mx is created
(after initClient), not after Prepared. Eliminates the 1–30s wait for
first sync — app shell renders against the IndexedDB cache while sync
populates new events in background.
Why reverted: needed an extra fix in
pages/client/direct/RoomProvider.tsx
to add useAtomValue(allRoomsAtom) so cold-start deep-links (push tap)
re-render when the room arrives. Plus arguably exposes a brief empty-UI
flash on first-time logins.
To redo: do it after a careful audit of every consumer of
mx.getRoom() / mx.getRooms() to make sure they all tolerate empty
stores. The deep-link re-render fix in direct/RoomProvider.tsx is the
mechanical trigger; there may be others.
What's open
In rough priority order:
- SpecVersions non-blocking + media-auth race fix (B above). Biggest win for cold-start time-to-interactive (~1.5s saved). Bundle with the ImageContent/VideoContent error reset in one commit.
nullinstead of mascot during ConfigConfig +!mxloading (B above, simpler half). ~400ms shaved.- Drop the Prepared gate (C above). Most invasive; needs full audit.
Could wait until
dm_1x1_redesign.mdor another major-mode change is ready to absorb the risk. - OS system splash on Android — currently iconic-on-black via
AppTheme.NoActionBarLaunch. Can't eliminate on Android 12+ but can be tuned (different background color, different icon) viaandroid/app/src/main/res/values/styles.xml. Not user-reported as a problem, just noting the lever exists.
Don't break
body { background: var(--vojo-safe-area-bg, #0d0e11) }insrc/index.css— the dark frame is what makes anullloading path look intentional. Without it the user sees a white flash.- Mascot on auth pages (
/login,/register) is INTENDED — those pages design the mascot in deliberately. Don't gate-remove it there. Only the loading-state mascot is the candidate for removal. - The error-retry mascot in
ClientRoot(the<AuthSplashScreen>{retry-dialog}</AuthSplashScreen>branch inClientRoot.tsx) is the rare case where the mascot is fine to keep — it's a hard-error context that needs anchoring for the dialog.