8000 Fix desktop app loading on Windows · undergroundwires/privacy.sexy@298af54 · GitHub
[go: up one dir, main page]

Skip to content

Commit 298af54

Browse files
Fix desktop app loading on Windows
Path handling changes in Node broke the application loading process on Windows. When using `path.join('file://', ...)`, newer Node versions resolve to `.\\file:\\` (%2E%5Cfile) instead of a proper URI starting with `file://`. This affects only Windows; the URL is constructured correctly on macOS and Linux. Changes: - Replace `window.loadURL` with `window.loadFile`, removing the need for file protocol handling entirely - Refactor window content loading into a dedicated module for better separation of responsibilities. - Add error dialogs when window content fails to load - Remove `setTimeout` workaround previously needed for `fs` APIs - Add logging for content loading to improve troubleshooting
1 parent 3ab34e9 commit 298af54

File tree

3 files changed

+118
-45
lines changed

3 files changed

+118
-45
lines changed

src/presentation/electron/main/ElectronConfig.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55

66
/// <reference types="electron-vite/node" />
77
import { join } from 'node:path';
8+
import { app } from 'electron/main';
89
import appIcon from '@/presentation/public/icon-512x512.png?asset';
910

1011
export const APP_ICON_PATH = appIcon;
1112

12-
export const RENDERER_URL = process.env.ELECTRON_RENDERER_URL;
13+
export const RENDERER_DEV_SERVER_URL = process.env.ELECTRON_RENDERER_URL;
1314

14-
export const RENDERER_HTML_PATH = join('file://', __dirname, '../renderer/index.html');
15+
export const RENDERER_HTML_PATH = join(__dirname, '../renderer/index.html');
1516

1617
export const PRELOADER_SCRIPT_PATH = join(__dirname, '../preload/index.mjs');
18+
19+
export const IS_DEVELOPMENT = !app.isPackaged;
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { app, BrowserWindow, dialog } from 'electron/main';
2+
import { ElectronLogger } from '@/infrastructure/Log/ElectronLogger';
3+
import {
4+
RENDERER_HTML_PATH, RENDERER_DEV_SERVER_URL,
5+
IS_DEVELOPMENT,
6+
} from './ElectronConfig';
7+
8+
const OPEN_DEV_TOOLS_ON_DEVELOPMENT = true;
9+
10+
export async function loadWindowContents(
11+
window: BrowserWindow,
12+
): Promise<void> {
13+
if (IS_DEVELOPMENT) {
14+
await loadDevServer(window);
15+
} else {
16+
await loadPackagedFile(window);
17+
}
18+
openDevTools(window);
19+
// Do not remove [WINDOW_INIT]; it's a marker used in tests.
20+
ElectronLogger.info('[WINDOW_INIT] Main window initialized and content loading.');
21+
}
22+
23+
function openDevTools(
24+
window: BrowserWindow,
25+
): void {
26+
if (!IS_DEVELOPMENT) {
27+
return;
28+
}
29+
if (!OPEN_DEV_TOOLS_ON_DEVELOPMENT) {
30+
return;
31+
}
32+
try {
33+
window.webContents.openDevTools();
34+
} catch (err: unknown) {
35+
// DevTools are a development aid only, log failures without interrupting application flow
36+
ElectronLogger.error('Failed to open DevTools', err);
37+
}
38+
}
39+
40+
function truncateMiddle(
41+
text: string,
42+
maxLength: number,
43+
separator: string = '...',
44+
): string {
45+
if (text.length <= maxLength) {
46+
return text;
47+
}
48+
const charsToShow = maxLength - separator.length;
49+
const totalFrontChars = Math.ceil(charsToShow / 2);
50+
const totalEndChars = Math.floor(charsToShow / 2);
51+
return [
52+
text.slice(0, totalFrontChars),
53+
text.slice(-totalEndChars),
54+
].join(separator);
55+
}
56+
57+
async function loadDevServer(
58+
window: BrowserWindow,
59+
): Promise<void> {
60+
const devServerUrl = RENDERER_DEV_SERVER_URL;
61+
if (!devServerUrl || devServerUrl.length === 0) {
62+
throw new Error('Developer server URL is not populated during bundling');
63+
}
64+
try {
65+
await window.loadURL(devServerUrl);
66+
} catch (error) {
67+
showLoadErrorAndExit({
68+
technicalSummary: `${truncateMiddle(devServerUrl, 20)} (URL)`,
69+
logMessage: `Failed to load URL: ${devServerUrl}`,
70+
});
71+
}
72+
}
73+
74+
async function loadPackagedFile(
75+
window: BrowserWindow,
76+
): Promise<void> {
77+
const filePath = RENDERER_HTML_PATH;
78+
try {
79+
await window.loadFile(filePath);
80+
} catch {
81+
showLoadErrorAndExit({
82+
technicalSummary: `${truncateMiddle(filePath, 20)} (file)`,
83+
logMessage: `Failed to load file: ${filePath}`,
84+
});
85+
}
86+
}
87+
88+
interface LoadErrorDetails {
89+
readonly technicalSummary: string;
90+
readonly logMessage: string;
91+
}
92+
93+
function showLoadErrorAndExit(errorDetails: LoadErrorDetails): void {
94+
ElectronLogger.error(errorDetails.logMessage);
95+
dialog.showErrorBox(
96+
'Error: Application Failed to Load',
97+
[
98+
'Please try:',
99+
'1. Restart the application',
100+
'2. Reinstall the application',
101+
'\n',
102+
'If the problem persists, report this as issue with the information below:',
103+
`Reference: ${errorDetails.technicalSummary}`,
104+
].join('\n'),
105+
);
106+
app.exit(1);
107+
}

src/presentation/electron/main/index.ts

Lines changed: 6 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@ import { validateRuntimeSanity } from '@/infrastructure/RuntimeSanity/SanityChec
1010
import { ElectronLogger } from '@/infrastructure/Log/ElectronLogger';
1111
import { setupAutoUpdater } from './Update/UpdateInitializer';
1212
import {
13-
APP_ICON_PATH, PRELOADER_SCRIPT_PATH, RENDERER_HTML_PATH, RENDERER_URL,
13+
APP_ICON_PATH, IS_DEVELOPMENT, PRELOADER_SCRIPT_PATH,
1414
} from './ElectronConfig';
1515
import { registerAllIpcChannels } from './IpcRegistration';
16+
import { loadWindowContents } from './WindowContentLoader';
1617

1718
const hideWindowUntilLoaded = true;
18-
const openDevToolsOnDevelopment = true;
19-
const isDevelopment = !app.isPackaged;
2019

2120
// Keep a global reference of the window object, if you don't, the window will
2221
// be closed automatically when the JavaScript object is garbage collected.
@@ -58,7 +57,7 @@ async function createWindow() {
5857
win.on('closed', () => {
5958
win = null;
6059
});
61-
await loadAppHtml(win);
60+
await loadWindowContents(win);
6261
}
6362

6463
configureAppQuitBehavior();
@@ -69,7 +68,7 @@ app.whenReady().then(async () => {
6968
});
7069

7170
// Exit cleanly on request from parent process in development mode.
72-
if (isDevelopment) {
71+
if (IS_DEVELOPMENT) {
7372
if (process.platform === 'win32') {
7473
process.on('message', (data) => {
7574
if (data === 'graceful-exit') {
@@ -101,7 +100,7 @@ async function initializeApplication(
101100
}
102101

103102
async function installVueDevTools() {
104-
if (!isDevelopment) {
103+
if (!IS_DEVELOPMENT) {
105104
return;
106105
}
107106
try {
@@ -112,47 +111,21 @@ async function installVueDevTools() {
112111
}
113112

114113
async function checkForUpdates() {
115-
if (isDevelopment) {
114+
if (IS_DEVELOPMENT) {
116115
ElectronLogger.debug('Development Mode: Skipping automatic update checks');
117116
return;
118117
}
119118
const updater = setupAutoUpdater();
120119
await updater.checkForUpdates();
121120
}
122121

123-
async function loadAppHtml(window: BrowserWindow) {
124-
const url = RENDERER_URL // Populated in a dev server during development
125-
?? RENDERER_HTML_PATH;
126-
await loadUrlWithNodeWorkaround(window, url);
127-
openDevTools(window);
128-
// Do not remove [WINDOW_INIT]; it's a marker used in tests.
129-
ElectronLogger.info('[WINDOW_INIT] Main window initialized and content loading.');
130-
}
131-
132122
function configureExternalsUrlsOpenBrowser(window: BrowserWindow) {
133123
window.webContents.setWindowOpenHandler(({ url }) => {
134124
shell.openExternal(url);
135125
return { action: 'deny' };
136126
});
137127
}
138128

139-
// Workaround for https://github.com/electron/electron/issues/19554 otherwise fs does not work
140-
function loadUrlWithNodeWorkaround(
141-
window: BrowserWindow,
142-
url: string,
143-
): Promise<void> {
144-
return new Promise((resolve, reject) => {
145-
setTimeout(async () => {
146-
try {
147-
await window.loadURL(url);
148-
resolve();
149-
} catch (err) {
150-
reject(err);
151-
}
152-
}, 10);
153-
});
154-
}
155-
156129
function getWindowSize(idealWidth: number, idealHeight: number) {
157130
let { width, height } = screen.getPrimaryDisplay().workAreaSize;
158131
// To ensure not creating a screen bigger than current screen size
@@ -209,13 +182,3 @@ function bringToFront(window: BrowserWindow) {
209182
window.setAlwaysOnTop(true);
210183
window.setAlwaysOnTop(false);
211184
}
212-
213-
function openDevTools(window: BrowserWindow) {
214-
if (!isDevelopment) {
215-
return;
216-
}
217-
if (!openDevToolsOnDevelopment) {
218-
return;
219-
}
220-
window.webContents.openDevTools();
221-
}

0 commit comments

Comments
 (0)
0