Skip to content

Commit 362d177

Browse files
Apply PR #21553: fix(app): tighter startup sequence
2 parents d8cc854 + 943d82c commit 362d177

File tree

11 files changed

+287
-139
lines changed

11 files changed

+287
-139
lines changed

packages/app/src/app.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/solid-query"
1313
import { type Duration, Effect } from "effect"
1414
import {
1515
type Component,
16+
createEffect,
1617
createMemo,
1718
createResource,
1819
createSignal,
@@ -161,7 +162,7 @@ const effectMinDuration =
161162
<A, E, R>(e: Effect.Effect<A, E, R>) =>
162163
Effect.all([e, Effect.sleep(duration)], { concurrency: "unbounded" }).pipe(Effect.map((v) => v[0]))
163164

164-
function ConnectionGate(props: ParentProps<{ disableHealthCheck?: boolean }>) {
165+
function ConnectionGate(props: ParentProps<{ disableHealthCheck?: boolean; onReady?: () => void }>) {
165166
const server = useServer()
166167
const checkServerHealth = useCheckServerHealth()
167168

@@ -189,6 +190,16 @@ function ConnectionGate(props: ParentProps<{ disableHealthCheck?: boolean }>) {
189190
),
190191
)
191192

193+
let sent = false
194+
195+
createEffect(() => {
196+
if (sent) return
197+
const ready = checkMode() === "blocking" ? !startupHealthCheck.loading : startupHealthCheck.state !== "pending"
198+
if (!ready) return
199+
sent = true
200+
props.onReady?.()
201+
})
202+
192203
return (
193204
<Show
194205
when={checkMode() === "blocking" ? !startupHealthCheck.loading : startupHealthCheck.state !== "pending"}
@@ -281,14 +292,15 @@ export function AppInterface(props: {
281292
servers?: Array<ServerConnection.Any>
282293
router?: Component<BaseRouterProps>
283294
disableHealthCheck?: boolean
295+
onReady?: () => void
284296
}) {
285297
return (
286298
<ServerProvider
287299
defaultServer={props.defaultServer}
288300
disableHealthCheck={props.disableHealthCheck}
289301
servers={props.servers}
290302
>
291-
<ConnectionGate disableHealthCheck={props.disableHealthCheck}>
303+
<ConnectionGate disableHealthCheck={props.disableHealthCheck} onReady={props.onReady}>
292304
<ServerKey>
293305
<GlobalSDKProvider>
294306
<GlobalSyncProvider>

packages/desktop-electron/electron.vite.config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ export default defineConfig({
6464
rollupOptions: {
6565
input: {
6666
main: "src/renderer/index.html",
67-
loading: "src/renderer/loading.html",
6867
},
6968
},
7069
},

packages/desktop-electron/src/main/index.ts

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ let initStep: InitStep = { phase: "server_waiting" }
4343

4444
let mainWindow: BrowserWindow | null = null
4545
let server: Server.Listener | null = null
46+
let splash: BrowserWindow | null = null
47+
let ready = false
4648
const loadingComplete = defer<void>()
49+
const mainReady = defer<void>()
4750

4851
const pendingDeepLinks: string[] = []
4952

@@ -111,6 +114,11 @@ function emitDeepLinks(urls: string[]) {
111114
}
112115

113116
function focusMainWindow() {
117+
if (!ready) {
118+
splash?.show()
119+
splash?.focus()
120+
return
121+
}
114122
if (!mainWindow) return
115123
mainWindow.show()
116124
mainWindow.focus()
@@ -120,12 +128,14 @@ function setInitStep(step: InitStep) {
120128
initStep = step
121129
logger.log("init step", { step })
122130
initEmitter.emit("step", step)
131+
BrowserWindow.getAllWindows().forEach((win) => {
132+
win.webContents.send("init-step", step)
133+
})
123134
}
124135

125136
async function initialize() {
126137
const needsMigration = !sqliteFileExists()
127138
const sqliteDone = needsMigration ? defer<void>() : undefined
128-
let overlay: BrowserWindow | null = null
129139

130140
const port = await getSidecarPort()
131141
const hostname = "127.0.0.1"
@@ -146,7 +156,7 @@ async function initialize() {
146156

147157
initEmitter.on("sqlite", (progress: SqliteMigrationProgress) => {
148158
setInitStep({ phase: "sqlite_waiting" })
149-
if (overlay) sendSqliteMigrationProgress(overlay, progress)
159+
if (splash) sendSqliteMigrationProgress(splash, progress)
150160
if (mainWindow) sendSqliteMigrationProgress(mainWindow, progress)
151161
if (progress.type === "Done") sqliteDone?.resolve()
152162
})
@@ -172,25 +182,31 @@ async function initialize() {
172182
deepLinks: pendingDeepLinks,
173183
}
174184

175-
if (needsMigration) {
176-
const show = await Promise.race([loadingTask.then(() => false), delay(1_000).then(() => true)])
177-
if (show) {
178-
overlay = createLoadingWindow(globals)
179-
await delay(1_000)
180-
}
181-
}
185+
const startup = (async () => {
186+
await loadingTask
187+
setInitStep({ phase: "app_waiting" })
188+
mainWindow = createMainWindow(globals, { show: false })
182189

183-
await loadingTask
190+
const ok = await Promise.race([mainReady.promise.then(() => true), delay(15_000).then(() => false)])
191+
if (!ok) logger.warn("main window ready timed out")
192+
})()
193+
194+
splash = createLoadingWindow(globals)
195+
196+
await startup
184197
setInitStep({ phase: "done" })
185198

186-
if (overlay) {
187-
await loadingComplete.promise
199+
if (splash) {
200+
const ok = await Promise.race([loadingComplete.promise.then(() => true), delay(2_000).then(() => false)])
201+
if (!ok) logger.warn("loading window complete timed out")
202+
splash.close()
203+
splash = null
188204
}
189205

190-
mainWindow = createMainWindow(globals)
191206
wireMenu()
192-
193-
overlay?.close()
207+
ready = true
208+
mainWindow?.show()
209+
mainWindow?.focus()
194210
}
195211

196212
function wireMenu() {
@@ -235,6 +251,7 @@ registerIpcHandlers({
235251
wslPath: async (path, mode) => wslPath(path, mode),
236252
resolveAppPath: async (appName) => resolveAppPath(appName),
237253
loadingWindowComplete: () => loadingComplete.resolve(),
254+
mainWindowReady: () => mainReady.resolve(),
238255
runUpdater: async (alertOnFail) => checkForUpdates(alertOnFail),
239256
checkUpdate: async () => checkUpdate(),
240257
installUpdate: async () => installUpdate(),

packages/desktop-electron/src/main/ipc.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type Deps = {
2525
wslPath: (path: string, mode: "windows" | "linux" | null) => Promise<string>
2626
resolveAppPath: (appName: string) => Promise<string | null>
2727
loadingWindowComplete: () => void
28+
mainWindowReady: () => void
2829
runUpdater: (alertOnFail: boolean) => Promise<void> | void
2930
checkUpdate: () => Promise<{ updateAvailable: boolean; version?: string }>
3031
installUpdate: () => Promise<void> | void
@@ -54,6 +55,7 @@ export function registerIpcHandlers(deps: Deps) {
5455
)
5556
ipcMain.handle("resolve-app-path", (_event: IpcMainInvokeEvent, appName: string) => deps.resolveAppPath(appName))
5657
ipcMain.on("loading-window-complete", () => deps.loadingWindowComplete())
58+
ipcMain.on("main-window-ready", () => deps.mainWindowReady())
5759
ipcMain.handle("run-updater", (_event: IpcMainInvokeEvent, alertOnFail: boolean) => deps.runUpdater(alertOnFail))
5860
ipcMain.handle("check-update", () => deps.checkUpdate())
5961
ipcMain.handle("install-update", () => deps.installUpdate())

0 commit comments

Comments
 (0)