@@ -64,7 +64,11 @@ import { extractTransportPendingMessages } from './transport-queue.js';
6464import { ingestTimelineEventForCache } from './hooks/useTimeline.js' ;
6565import { getMobileKeyboardState } from './mobile-keyboard.js' ;
6666import { pickReadableSessionDisplay } from '@shared/session-display.js' ;
67- import { getSelectedServerName } from './server-selection.js' ;
67+ import {
68+ getSelectedServerName ,
69+ shouldResetSelectedServer ,
70+ shouldShowInitialConnectingGate ,
71+ } from './server-selection.js' ;
6872
6973// On web: if opened by the native app for passkey auth, render the bridge page.
7074const nativeCallback = typeof window !== 'undefined'
@@ -145,6 +149,8 @@ export function App() {
145149 const [ splashDone , setSplashDone ] = useState ( false ) ;
146150
147151 const [ servers , setServers ] = useState < ServerInfo [ ] > ( [ ] ) ;
152+ const [ serversLoaded , setServersLoaded ] = useState ( false ) ;
153+ const [ serversSynced , setServersSynced ] = useState ( false ) ;
148154 const [ selectedServerId , setSelectedServerId ] = useState < string | null > (
149155 ( ) => localStorage . getItem ( 'rcc_server' ) ,
150156 ) ;
@@ -174,6 +180,8 @@ export function App() {
174180
175181 useEffect ( ( ) => {
176182 if ( ! auth ) {
183+ setServersLoaded ( false ) ;
184+ setServersSynced ( false ) ;
177185 watchProjectionStore . setApiKey ( null ) ;
178186 watchProjectionStore . setSnapshotStatus ( 'switching' ) ;
179187 watchProjectionStore . setServers ( [ ] ) ;
@@ -220,6 +228,18 @@ export function App() {
220228 localStorage . removeItem ( 'rcc_server_name' ) ;
221229 } , [ resolvedSelectedServerName , selectedServerId , selectedServerName , servers . length ] ) ;
222230
231+ useEffect ( ( ) => {
232+ if ( ! serversSynced ) return ;
233+ if ( ! shouldResetSelectedServer ( selectedServerId , servers , serversLoaded ) ) return ;
234+ setSelectedServerId ( null ) ;
235+ setSelectedServerName ( null ) ;
236+ setSessionsLoaded ( true ) ;
237+ setConnecting ( false ) ;
238+ localStorage . removeItem ( 'rcc_server' ) ;
239+ localStorage . removeItem ( 'rcc_server_name' ) ;
240+ localStorage . removeItem ( 'rcc_session' ) ;
241+ } , [ selectedServerId , servers , serversLoaded ] ) ;
242+
223243 useEffect ( ( ) => {
224244 let cleanup = ( ) => { } ;
225245 void onWatchCommand ( ( command ) => {
@@ -504,10 +524,20 @@ export function App() {
504524 try {
505525 const data = await apiFetch < { servers : ServerInfo [ ] } > ( '/api/server' ) ;
506526 setServers ( data . servers ) ;
507- } catch { /* ignore */ }
527+ setServersSynced ( true ) ;
528+ } catch {
529+ // Preserve the last known list on refresh failures. The request is still
530+ // considered resolved so the UI can escape the initial connecting gate.
531+ } finally {
532+ setServersLoaded ( true ) ;
533+ }
508534 } , [ auth ] ) ;
509535
510- useEffect ( ( ) => { loadServers ( ) ; } , [ loadServers ] ) ;
536+ useEffect ( ( ) => {
537+ setServersLoaded ( false ) ;
538+ setServersSynced ( false ) ;
539+ void loadServers ( ) ;
540+ } , [ loadServers ] ) ;
511541
512542 // Periodically refresh server list so lastHeartbeatAt stays current
513543 useEffect ( ( ) => {
@@ -2207,16 +2237,24 @@ export function App() {
22072237 // Show full-screen connecting indicator while waiting for initial WS + session data.
22082238 // After 8s, show escape buttons so the user is never stuck.
22092239 const [ connectTimeout , setConnectTimeout ] = useState ( false ) ;
2240+ const showInitialConnectingGate = shouldShowInitialConnectingGate (
2241+ Boolean ( auth ) ,
2242+ selectedServerId ,
2243+ connected ,
2244+ sessionsLoaded ,
2245+ serversLoaded ,
2246+ ) ;
2247+
22102248 useEffect ( ( ) => {
2211- if ( auth && selectedServerId && ! connected && servers . length === 0 ) {
2249+ if ( showInitialConnectingGate ) {
22122250 const t = setTimeout ( ( ) => setConnectTimeout ( true ) , 5000 ) ;
22132251 return ( ) => { clearTimeout ( t ) ; setConnectTimeout ( false ) ; } ;
22142252 }
22152253 setConnectTimeout ( false ) ;
22162254 return undefined ;
2217- } , [ auth , selectedServerId , connected , servers . length ] ) ;
2255+ } , [ showInitialConnectingGate ] ) ;
22182256
2219- if ( auth && selectedServerId && ! sessionsLoaded && ! connected && servers . length === 0 ) {
2257+ if ( showInitialConnectingGate ) {
22202258 return (
22212259 < div style = { { position : 'fixed' , inset : 0 , display : 'flex' , alignItems : 'center' , justifyContent : 'center' , background : '#0a0e1a' , flexDirection : 'column' , gap : 16 } } >
22222260 < div class = "spinner" style = { { width : 32 , height : 32 } } />
0 commit comments