| @@ -124,7 +124,7 @@ function AutoProjectBootstrap() { · 123 unmodified lines |
| 124 | | 124 | |
| 125 | function DesktopProjectBootstrap() { | 125 | function DesktopProjectBootstrap() { |
| 126 | const api = useNativeApi(); | 126 | const api = useNativeApi(); |
| 127 | const { dispatch } = useStore(); | 127 | const { state, dispatch } = useStore(); |
| 128 | const bootstrappedRef = useRef(false); | 128 | const bootstrappedRef = useRef(false); |
| 129 | | 129 | |
| 130 | useEffect(() => { | 130 | useEffect(() => { |
| @@ -138,6 +138,16 @@ function DesktopProjectBootstrap() { · 7 unmodified lines |
| 138 | try { | 138 | try { |
| 139 | const projects = await api.projects.list(); | 139 | const projects = await api.projects.list(); |
| 140 | if (disposed) return; | 140 | if (disposed) return; |
| | 141 | if (projects.length === 0 && (state.projects.length > 0 || state.threads.length > 0)) { |
| | 142 | // Avoid wiping hydrated renderer state if the initial projects.list races |
| | 143 | // backend startup and transiently returns no projects. |
| | 144 | retryTimer = setTimeout(() => { |
| | 145 | retryTimer = null; |
| | 146 | void attemptBootstrap(); |
| | 147 | }, retryDelayMs); |
| | 148 | retryDelayMs = Math.min(retryDelayMs * 2, 5_000); |
| | 149 | return; |
| | 150 | } |
| 141 | dispatch({ | 151 | dispatch({ |
| 142 | type: "SYNC_PROJECTS", | 152 | type: "SYNC_PROJECTS", |
| 143 | projects: projects.map((project) => ({ | 153 | projects: projects.map((project) => ({ |
| @@ -167,7 +177,7 @@ function DesktopProjectBootstrap() { · 23 unmodified lines |
| 167 | clearTimeout(retryTimer); | 177 | clearTimeout(retryTimer); |
| 168 | } | 178 | } |
| 169 | }; | 179 | }; |
| 170 | }, [api, dispatch]); | 180 | }, [api, dispatch, state.projects.length, state.threads.length]); |
| 171 | | 181 | |
| 172 | return null; | 182 | return null; |
| 173 | } | 183 | } |