From 6ca0a891565ec5d0dbb8a279cbe9e58c28de6c8f Mon Sep 17 00:00:00 2001 From: Evie Gauthier Date: Fri, 8 May 2026 12:33:08 -0400 Subject: [PATCH 1/2] fix: skip splash screen when cached rooms are available on resume When foregrounding after a period in the background, the app now shows the room list immediately using cached IndexedDB data instead of waiting for the first server sync response. The 'Connecting...' banner from SyncStatus remains visible during the reconnect. Fresh sessions with no cached rooms continue to wait for PREPARED/ SYNCING as before. --- src/app/pages/client/ClientRoot.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/app/pages/client/ClientRoot.tsx b/src/app/pages/client/ClientRoot.tsx index 70ba9221e..c0ad27195 100644 --- a/src/app/pages/client/ClientRoot.tsx +++ b/src/app/pages/client/ClientRoot.tsx @@ -288,6 +288,17 @@ export function ClientRoot({ children }: ClientRootProps) { } }, [mx]); + // Once the client has started (store loaded from IndexedDB, sync loop running), + // hide the splash immediately if there are cached rooms — no need to wait for + // the first server response. The SyncStatus banner shows "Connecting..." instead. + useEffect(() => { + if (startState.status !== AsyncStatus.Success || !mx) return; + if (isClientReady(mx.getSyncState())) return; + if (mx.getRooms().length > 0) { + setLoading(false); + } + }, [mx, startState.status]); + useSyncState( mx, useCallback((state: string) => { From 08726817e093240c9136291e33cf34bde629bdf5 Mon Sep 17 00:00:00 2001 From: Evie Gauthier Date: Mon, 11 May 2026 20:59:12 -0400 Subject: [PATCH 2/2] fix(client): show app with connecting banner instead of splash on reconnect Remove the loading state from the render gate so the app renders as soon as the MatrixClient is available (mx != null), rather than waiting for the first sync to complete. SyncStatus already renders a green 'Connecting...' banner during the initial PREPARED/SYNCING/CATCHUP phase, so there's no need for the full splash screen. The 'petting cats' splash now only appears before any MatrixClient exists (e.g. during very first load). Returning users no longer see the loading screen if the client needs to reconnect after being backgrounded. --- src/app/pages/client/ClientRoot.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/app/pages/client/ClientRoot.tsx b/src/app/pages/client/ClientRoot.tsx index c0ad27195..0cbfdf24d 100644 --- a/src/app/pages/client/ClientRoot.tsx +++ b/src/app/pages/client/ClientRoot.tsx @@ -175,7 +175,6 @@ type ClientRootProps = { children: ReactNode; }; export function ClientRoot({ children }: ClientRootProps) { - const [loading, setLoading] = useState(true); const navigate = useNavigate(); const clientConfig = useClientConfig(); const sessions = useAtomValue(sessionsAtom); @@ -191,6 +190,8 @@ export function ClientRoot({ children }: ClientRootProps) { const syncStartTimeRef = useRef(performance.now()); const firstSyncReadyRef = useRef(false); + const [loading, setLoading] = useState(true); + const [loadState, loadMatrix, setLoadState] = useAsyncCallback( useCallback(async () => { if (!activeSession) { @@ -238,8 +239,8 @@ export function ClientRoot({ children }: ClientRootProps) { if (mx?.clientRunning) { stopClient(mx); } - setLoading(true); loadedUserIdRef.current = undefined; + setLoading(true); setLoadState({ status: AsyncStatus.Idle }); navigate(getHomePath(), { replace: true }); } @@ -303,6 +304,7 @@ export function ClientRoot({ children }: ClientRootProps) { mx, useCallback((state: string) => { if (isClientReady(state)) { + setLoading(false); if (!firstSyncReadyRef.current) { firstSyncReadyRef.current = true; Sentry.metrics.distribution( @@ -310,7 +312,6 @@ export function ClientRoot({ children }: ClientRootProps) { performance.now() - syncStartTimeRef.current ); } - setLoading(false); } }, []) ); @@ -369,7 +370,7 @@ export function ClientRoot({ children }: ClientRootProps) { {mx && } - {loading && } + {(loading || !mx) && } {(loadState.status === AsyncStatus.Error || startState.status === AsyncStatus.Error) && (