Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove splash screen and fix loading issue #2731

Merged
merged 15 commits into from
Apr 7, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ module.exports = {
],
globals: {
MAIN_WINDOW_WEBPACK_ENTRY: true,
SPLASH_SCREEN_WEBPACK_ENTRY: true,
},
env: {
browser: true,
Expand Down
2 changes: 1 addition & 1 deletion backend/src/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class BackendStatusEvent(TypedDict):


class BackendStateEvent(TypedDict):
event: Literal["backend-ready"] | Literal["backend-started"]
event: Literal["backend-started"]
data: None


Expand Down
19 changes: 10 additions & 9 deletions backend/src/server_host.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def filter(self, record): # noqa: ANN001
worker: WorkerServer = WorkerServer()

setup_task = None
backend_ready = False

access_logger.addFilter(SSEFilter())

Expand Down Expand Up @@ -313,6 +314,11 @@ async def shutdown(request: Request):
return json(success_response())


@app.get("/status")
async def status(request: Request):
return json({"ready": backend_ready})
RunDevelopment marked this conversation as resolved.
Show resolved Hide resolved


async def import_packages(
config: ServerConfig,
update_progress_cb: UpdateProgressFn,
Expand Down Expand Up @@ -373,6 +379,7 @@ async def install_deps(dependencies: list[api.Dependency]):


async def setup(sanic_app: Sanic, loop: asyncio.AbstractEventLoop):
global backend_ready
setup_queue = AppContext.get(sanic_app).setup_queue

async def update_progress(
Expand Down Expand Up @@ -407,20 +414,14 @@ async def update_progress(
# Now we can load all the nodes
await import_packages(AppContext.get(sanic_app).config, update_progress)

logger.info("Sending backend ready...")
logger.info("Backend almost ready...")

await update_progress("Loading Nodes...", 1.0, None)

# Wait to send backend-ready until nodes are loaded
# Wait to set backend-ready until nodes are loaded
await worker.wait_for_ready()

await setup_queue.put_and_wait(
{
"event": "backend-ready",
"data": None,
},
timeout=1,
)
backend_ready = True

logger.info("Done.")

Expand Down
5 changes: 0 additions & 5 deletions forge.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,6 @@ const config = {
js: './src/renderer/renderer.js',
name: 'main_window',
},
{
html: './src/renderer/splash.html',
js: './src/renderer/splash_renderer.js',
name: 'splash_screen',
},
],
},
devContentSecurityPolicy: '',
Expand Down
5 changes: 4 additions & 1 deletion src/common/Backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@ export class Backend {
shutdown(): Promise<void> {
return this.fetchJson('/shutdown', 'POST');
}

status(): Promise<{ ready: boolean }> {
return this.fetchJson('/status', 'GET');
}
}

const backendCache = new Map<string, Backend>();
Expand Down Expand Up @@ -314,7 +318,6 @@ export interface BackendEventMap {
progress: number;
statusProgress?: number | null;
};
'backend-ready': null;
'package-install-status': {
message: string;
progress: number;
Expand Down
2 changes: 0 additions & 2 deletions src/common/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
"paste": "Paste"
}
},
"loading": "Loading...",
"outputs": {
"largeImage": {
"imageNotAvailable": "Image not available."
Expand All @@ -68,7 +67,6 @@
"extractingFfmpeg": "Extracting downloaded files...",
"extractingPython": "Extracting downloaded files...",
"loading": "Loading...",
"loadingApp": "Loading main application...",
"startingBackend": "Starting up backend process..."
},
"typeTags": {
Expand Down
3 changes: 1 addition & 2 deletions src/common/safeIpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,7 @@ export interface InvokeChannels {
}

export interface SendChannels {
'splash-setup-progress': SendChannelInfo<[progress: Progress]>;
'backend-ready': SendChannelInfo;
'setup-progress': SendChannelInfo<[progress: Progress]>;
'backend-started': SendChannelInfo;
'file-new': SendChannelInfo;
'file-open': SendChannelInfo<[FileOpenResult<ParsedSaveData>]>;
Expand Down
1 change: 0 additions & 1 deletion src/custom.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,3 @@ declare module '*.jpeg' {
}

declare const MAIN_WINDOW_WEBPACK_ENTRY: string;
declare const SPLASH_SCREEN_WEBPACK_ENTRY: string;
124 changes: 93 additions & 31 deletions src/main/gui/main-window.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
import { clipboard, nativeImage, shell } from 'electron/common';
import { BrowserWindow, app, dialog, nativeTheme, powerSaveBlocker } from 'electron/main';
import {
BrowserWindow,
MessageBoxOptions,
app,
dialog,
nativeTheme,
powerSaveBlocker,
} from 'electron/main';
import EventSource from 'eventsource';
import fs, { constants } from 'fs/promises';
import { t } from 'i18next';
import { BackendEventMap } from '../../common/Backend';
import { Version } from '../../common/common-types';
import { isMac } from '../../common/env';
import { log } from '../../common/log';
import { SaveFile, openSaveFile } from '../../common/SaveFile';
import { ChainnerSettings } from '../../common/settings/settings';
import { CriticalError } from '../../common/ui/error';
import { ProgressController, ProgressToken, SubProgress } from '../../common/ui/progress';
import { Progress, ProgressController, ProgressToken, SubProgress } from '../../common/ui/progress';
import { assertNever } from '../../common/util';
import { OpenArguments, parseArgs } from '../arguments';
import { BackendProcess } from '../backend/process';
import { setupBackend } from '../backend/setup';
import { getRootDir } from '../platform';
import { BrowserWindowWithSafeIpc, ipcMain } from '../safeIpc';
import { writeSettings } from '../setting-storage';
import { MenuData, setMainMenu } from './menu';
import { addSplashScreen } from './splash';

const version = app.getVersion() as Version;

Expand Down Expand Up @@ -399,7 +405,82 @@ export const createMainWindow = async (args: OpenArguments, settings: ChainnerSe
});

const progressController = new ProgressController();
addSplashScreen(progressController);

let progressFinished = false;
joeyballentine marked this conversation as resolved.
Show resolved Hide resolved
let lastProgress: Progress | undefined;

// This is a hack that solves 2 problems at one:
// 1. Even after 'ready-to-show', React might still not be ready,
// so it's hard to say when we should re-send the last progress.
// 2. The splash screen sometimes randomly reloads which resets the displayed progress.
const intervalId = setInterval(() => {
if (progressFinished || mainWindow.isDestroyed()) {
clearInterval(intervalId);
return;
}

if (lastProgress) {
mainWindow.webContents.send('setup-progress', lastProgress);
}
}, 100);

progressController.addProgressListener((progress) => {
lastProgress = { ...progress };

if (progress.totalProgress === 1) {
progressFinished = true;
}
});

progressController.addInterruptListener(async (interrupt) => {
const options = interrupt.options ?? [];

let messageBoxOptions: MessageBoxOptions;
if (interrupt.type === 'critical error') {
if (!mainWindow.isDestroyed()) {
mainWindow.hide();
}

messageBoxOptions = {
type: 'error',
title: interrupt.title ?? 'Critical error occurred',
buttons: [...options.map((o) => o.title), 'Exit'],
defaultId: options.length,
message: interrupt.message,
};
} else {
messageBoxOptions = {
type: 'warning',
title: interrupt.title ?? 'Critical error occurred',
buttons: [...options.map((o) => o.title), 'Ok'],
defaultId: options.length,
message: interrupt.message,
};
}

const { response } = await dialog.showMessageBox(messageBoxOptions);
if (response < options.length) {
const { action } = options[response];

try {
switch (action.type) {
case 'open-url': {
await shell.openExternal(action.url);
break;
}
default:
return assertNever(action.type);
}
} catch (error) {
log.error(`Failed to execute action of type ${action.type}`, error);
}
}

if (interrupt.type === 'critical error') {
progressFinished = true;
app.exit(1);
}
});

try {
registerEventHandlerPreSetup(mainWindow, args, settings);
Expand Down Expand Up @@ -433,36 +514,17 @@ export const createMainWindow = async (args: OpenArguments, settings: ChainnerSe
}
});

let opened = false;
sse.addEventListener('backend-ready', () => {
progressController.submitProgress({
totalProgress: 1,
status: t('splash.loadingApp', 'Loading main application...'),
});

if (mainWindow.isDestroyed()) {
dialog.showMessageBoxSync({
type: 'error',
title: 'Unable to start application',
message: 'The main window was closed before the backend was ready.',
});
app.quit();
return;
}

if (!opened) {
mainWindow.show();
if (settings.lastWindowSize.maximized) {
mainWindow.maximize();
}
opened = true;
}
});

if (mainWindow.isDestroyed()) {
return;
}

mainWindow.once('ready-to-show', () => {
mainWindow.show();
if (settings.lastWindowSize.maximized) {
mainWindow.maximize();
}
});

// and load the index.html of the app.
mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY).catch(log.error);
} catch (error) {
Expand Down
Loading
Loading