diff --git a/electron/main.ts b/electron/main.ts index 397d1b9..d94abcd 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -8,6 +8,7 @@ import { screen, session, safeStorage, + shell, powerMonitor, powerSaveBlocker, } from 'electron' @@ -43,6 +44,7 @@ let closingWindow: BrowserWindow | null = null let openingOutput = false let outputEpoch = 0 let quitting = false +let startup: BrowserWindow | null = null let selectedDisplay: number | null = null let online: Online | null = null let settings: WindowSettings @@ -269,13 +271,40 @@ async function show(displayId: number) { // Две копии приложения не должны одновременно менять указатели пакетов. if (!app.requestSingleInstanceLock()) app.exit(0) app.on('second-instance', () => { - control?.show() - control?.focus() + const window = control ?? startup + window?.show() + window?.focus() +}) +app.on('before-quit', () => { + quitting = true }) app .whenReady() .then(async () => { + startup = new BrowserWindow({ + width: 440, + height: 200, + resizable: false, + title: 'Проверка файлов', + webPreferences: { contextIsolation: true, sandbox: true, nodeIntegration: false }, + }) + const startupWindow = startup + startupWindow.setMenu(null) + startupWindow.webContents.setWindowOpenHandler(() => ({ action: 'deny' })) + startupWindow.webContents.on('will-navigate', (event) => event.preventDefault()) + startupWindow.on('closed', () => { + if (startup === startupWindow) { + startup = null + app.quit() + } + }) + await startupWindow.loadURL( + 'data:text/html;charset=utf-8,' + + encodeURIComponent( + `
Проверка загруженных файлов…
Поиск и проверка пакетов…
`, + ), + ) diagnostics = new DiagnosticLog(join(app.getPath('userData'), 'logs')) diagnostics.record('app-start', { version: app.getVersion(), @@ -295,7 +324,29 @@ app const packages = new PackageStore(join(app.getPath('userData'), 'packages'), { fetch: fetchNetwork, }) - await packages.initialize() + let lastProgressUpdate = 0 + await packages.initialize((progress) => { + if (startupWindow.isDestroyed()) return + const now = Date.now() + if ( + progress.total !== null && + progress.completed !== progress.total && + now - lastProgressUpdate < 100 + ) + return + lastProgressUpdate = now + const { completed, total } = progress + void startupWindow.webContents + .executeJavaScript( + `(() => { + const bar = document.getElementById('progress'); + const count = document.getElementById('count'); + ${total === null ? "bar.removeAttribute('value');" : `bar.max = ${Math.max(total, 1)}; bar.value = ${completed}; count.textContent = 'Проверено файлов: ${completed} из ${total}';`} + })()`, + ) + .catch(() => {}) + }) + if (quitting || startupWindow.isDestroyed()) return online = new Online( join(app.getPath('userData'), 'connection'), packages, @@ -324,6 +375,7 @@ app }, ) await online.initialize() + if (quitting || startupWindow.isDestroyed()) return let previousConnection = online.state.connection const logConnection = () => { const status = online!.state.connection @@ -425,6 +477,8 @@ app keepControlOnPrimary() if (settings.value.control?.maximized) control!.maximize() control!.show() + startup = null + if (!startupWindow.isDestroyed()) startupWindow.destroy() }) const scheduleSave = () => { clearTimeout(saveTimer) @@ -503,6 +557,15 @@ app selectingPackage = false } }) + ipcMain.handle('open-materials-folder', async (event, key: unknown) => { + requireControl(event) + if (typeof key !== 'string' || key !== controller.state.catalog.key) + throw new Error('Выберите текущую игру') + for (const path of await packages.folders(key)) { + const error = await shell.openPath(path) + if (error) throw new Error('Не удалось открыть папку материалов') + } + }) let localSession = '' let localSequence = 0 const pendingCommands = new Set{{ state.server }}
{{ connectionLabels[state.connection] }}
Нет игр.
+Нет игр.
Текущая игра: {{ selectedName }}
Материалы: {{ state.preparation.completed }}/{{ state.preparation.total }} · {{ (state.preparation.downloadedBytes / 1048576).toFixed(1) }} МиБ @@ -158,12 +281,240 @@ const connectionLabels = {
В игре нет материалов.
+Войдите на сервер, чтобы выбрать игру.