Skip to content

Commit

Permalink
feat: Allow multiple browser windows in one main process
Browse files Browse the repository at this point in the history
  • Loading branch information
Its-treason committed Nov 19, 2024
1 parent 569ea9d commit 954b2b8
Show file tree
Hide file tree
Showing 9 changed files with 860 additions and 823 deletions.
53 changes: 31 additions & 22 deletions packages/bruno-electron/src/app/collections.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
const fs = require('fs');
const path = require('path');
const { dialog, ipcMain } = require('electron');
const { dialog, ipcMain, WebContentsView, BrowserWindow } = require('electron');
const Yup = require('yup');
const { isDirectory, normalizeAndResolvePath } = require('../utils/filesystem');
const { generateUidBasedOnHash } = require('../utils/common');
const Watcher = require('./watcher');

// todo: bruno.json config schema validation errors must be propagated to the UI
const configSchema = Yup.object({
Expand Down Expand Up @@ -41,51 +42,59 @@ const getCollectionConfigFile = async (pathname) => {
return config;
};

const openCollectionDialog = async (win, watcher) => {
const openCollectionDialog = async (win) => {
const { filePaths } = await dialog.showOpenDialog(win, {
properties: ['openDirectory', 'createDirectory']
});

if (filePaths && filePaths[0]) {
const resolvedPath = normalizeAndResolvePath(filePaths[0]);
if (isDirectory(resolvedPath)) {
openCollection(win, watcher, resolvedPath);
openCollection(resolvedPath);
} else {
console.error(`[ERROR] Cannot open unknown folder: "${resolvedPath}"`);
}
}
};

const openCollection = async (win, watcher, collectionPath, init = false) => {
const openCollection = async (collectionPath, init = false) => {
const watcher = Watcher.getInstance();
// Re-Add the watcher on init
if (init && watcher.hasWatcher(collectionPath)) {
watcher.removeWatcher(collectionPath);
if (watcher.hasWatcher(collectionPath)) {
if (init) {
watcher.removeWatcher(collectionPath);
} else {
BrowserWindow.getAllWindows().forEach((win) => {
win.webContents.send('main:collection-already-opened', collectionPath);
});
return;
}
}

if (!watcher.hasWatcher(collectionPath)) {
try {
const brunoConfig = await getCollectionConfigFile(collectionPath);
const uid = generateUidBasedOnHash(collectionPath);
try {
const brunoConfig = await getCollectionConfigFile(collectionPath);
const uid = generateUidBasedOnHash(collectionPath);

if (!brunoConfig.ignore || brunoConfig.ignore.length === 0) {
// 5 Feb 2024:
// bruno.json now supports an "ignore" field to specify which folders to ignore
// if the ignore field is not present, we default to ignoring node_modules and .git
// this is to maintain backwards compatibility with older collections
brunoConfig.ignore = ['node_modules', '.git'];
}
if (!brunoConfig.ignore || brunoConfig.ignore.length === 0) {
// 5 Feb 2024:
// bruno.json now supports an "ignore" field to specify which folders to ignore
// if the ignore field is not present, we default to ignoring node_modules and .git
// this is to maintain backwards compatibility with older collections
brunoConfig.ignore = ['node_modules', '.git'];
}

BrowserWindow.getAllWindows().forEach((win) => {
win.webContents.send('main:collection-opened', collectionPath, uid, brunoConfig);
ipcMain.emit('main:collection-opened', win, collectionPath, uid, brunoConfig);
} catch (err) {
if (!init) {
});
} catch (err) {
if (!init && win) {
BrowserWindow.getAllWindows().forEach((win) => {
win.webContents.send('main:display-error', {
error: err.message || 'An error occurred while opening the local collection'
});
}
});
}
} else {
win.webContents.send('main:collection-already-opened', collectionPath);
}
};

Expand Down
2 changes: 1 addition & 1 deletion packages/bruno-electron/src/app/menu-template.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const template = [
{
label: 'Force Quit',
click() {
process.exit();
app.quit();
}
}
]
Expand Down
23 changes: 22 additions & 1 deletion packages/bruno-electron/src/app/watcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,20 @@ const unlinkDir = (win, pathname, collectionUid, collectionPath) => {
};

class Watcher {
/**
* @private
*/
static watcher;
static getInstance() {
if (!Watcher.watcher) {
Watcher.watcher = new Watcher();
}
return Watcher.watcher;
}

/**
* @private
*/
constructor() {
this.watchers = {};
}
Expand Down Expand Up @@ -523,12 +537,19 @@ class Watcher {
return this.watchers[watchPath];
}

removeWatcher(watchPath, win) {
removeWatcher(watchPath) {
if (this.watchers[watchPath]) {
this.watchers[watchPath].close();
this.watchers[watchPath] = null;
}
}

removeAllWatcher() {
for (const watcher of this.watchers) {
watcher?.close();
}
this.watchers = {};
}
}

module.exports = Watcher;
112 changes: 112 additions & 0 deletions packages/bruno-electron/src/createBrowserWindow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { app, BrowserWindow, ipcMain, shell } from 'electron';
import path from 'node:path';
import http from 'node:http';
import fs from 'node:fs';
const { loadWindowState, saveBounds, saveMaximized } = require('./utils/window');

export function createBrowserWindow(loadState = true): BrowserWindow {
// The second windows should not be opened maximised
const windowState = loadState ? loadWindowState() : null;

const mainWindow = new BrowserWindow({
...windowState,
minWidth: 1000,
minHeight: 640,
show: false,
webPreferences: {
nodeIntegration: true,
preload: path.join(__dirname, 'preload.js'),
webviewTag: true,
spellcheck: false
},
title: 'Bruno lazer',
icon: path.join(__dirname, 'about/256x256.png')
// we will bring this back
// see https://github.com/usebruno/bruno/issues/440
// autoHideMenuBar: true
});
mainWindow.on('ready-to-show', () => {
mainWindow.show();
});

if (windowState?.maximized) {
mainWindow.maximize();
}

let launchFailed = false;
if (app.isPackaged) {
const staticPath = path.join(__dirname, '../../web/');
const server = http.createServer((req, res) => {
const filePath = path.join(staticPath, req.url === '/' ? 'index.html' : req.url!);

const stream = fs.createReadStream(filePath);

stream.on('open', () => {
res.writeHead(200);
stream.pipe(res);
});

stream.on('error', (err) => {
// @ts-expect-error
if (err.code === 'ENOENT') {
res.writeHead(404).end('File not found');
return;
}
res.writeHead(500).end('Internal server error');
});
});
server.listen(0, '127.0.0.1', () => {
// @ts-expect-error
const url = `http://127.0.0.1:${server.address().port}`;
mainWindow.loadURL(url).catch((reason) => {
console.error(`Error: Failed to load URL: "${url}" (Electron shows a blank screen because of this).`);
console.error('Original message:', reason);
console.error(
'If you are using an official production build: the above error is most likely a bug! ' +
' Please report this under: https://github.com/usebruno/bruno/issues'
);
mainWindow.loadURL(`data:text/html;charset=utf,Failed to load: ${reason}`);
launchFailed = true;
});
});
} else {
mainWindow.loadURL('http://127.0.0.1:3000').catch((reason) => {
console.error(
`Error: Failed to load URL: "http://127.0.0.1:3000" (Electron shows a blank screen because of this).`
);
console.error('Original message:', reason);
console.error(
'Could not connect to Next.Js dev server, is it running?' +
' Start the dev server using "npm run dev:web" and restart electron'
);
mainWindow.loadURL(`data:text/html;charset=utf,Failed to load: ${reason}`);
launchFailed = true;
});
}

const handleBoundsChange = () => {
if (!mainWindow.isMaximized()) {
saveBounds(mainWindow);
}
};

mainWindow.on('resize', handleBoundsChange);
mainWindow.on('move', handleBoundsChange);

mainWindow.on('maximize', () => saveMaximized(true));
mainWindow.on('unmaximize', () => saveMaximized(false));
mainWindow.on('close', (e) => {
if (launchFailed) {
return;
}
e.preventDefault();
ipcMain.emit('main:start-quit-flow', mainWindow);
});

mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url);
return { action: 'deny' };
});

return mainWindow;
}
Loading

0 comments on commit 954b2b8

Please sign in to comment.