Skip to content

Commit

Permalink
Merge pull request #11 from dsaltares/feat/10-open-internal-links
Browse files Browse the repository at this point in the history
feat: 10 - handle open internal links in new tab
  • Loading branch information
dsaltares authored Sep 27, 2024
2 parents 432260e + bf51e42 commit 83c67b0
Showing 1 changed file with 113 additions and 31 deletions.
144 changes: 113 additions & 31 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { app, BrowserWindow } from 'electron';
import type { BrowserWindowConstructorOptions } from 'electron';
import { app, BrowserWindow, shell } from 'electron';
import path from 'path';
import { migrateToLatest } from '@server/db/migrations';
import router from '@server/router';
Expand All @@ -11,12 +12,35 @@ if (require('electron-squirrel-startup')) {
app.quit();
}

const createWindow = async () => {
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', onReady);

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});

app.on('activate', async () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
await createWindow();
}
});

function createWindow(options?: BrowserWindowConstructorOptions) {
// Create the browser window.
const mainWindow = new BrowserWindow({
const window = new BrowserWindow({
width: 1280,
height: 720,
webPreferences: {
...(options?.webPreferences || {}),
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
},
Expand All @@ -25,48 +49,106 @@ const createWindow = async () => {

// and load the index.html of the app.
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
await mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
void window.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
} else {
await mainWindow.loadFile(
void window.loadFile(
path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`),
);
}

// Open the DevTools.
if (!app.isPackaged) {
mainWindow.webContents.openDevTools();
window.webContents.openDevTools();
}
return mainWindow;
};

const onReady = async () => {
await migrateToLatest(getUserSettings().dbPath);
const window = await createWindow();
createIPCHandler({ router, windows: [window] });
setAppMenu();
};
window.webContents.setWindowOpenHandler(({ url }) => {
const newUrl = maybeParseUrl(url);
const currentUrl = maybeParseUrl(window.webContents.getURL());

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', onReady);
if (!newUrl || !currentUrl) {
return { action: 'deny' };
}

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
if (!isOpeningApp(currentUrl, newUrl)) {
void openExternalUrl(url);
return { action: 'deny' };
}

return {
action: 'allow',
createWindow: (options) => {
const newWindow = createWindow(options);
void newWindow.webContents.loadURL(url);
window.addTabbedWindow(newWindow);
return newWindow.webContents;
},
outlivesOpener: true,
overrideBrowserWindowOptions: {
width: 1280,
height: 720,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
},
},
};
});

return window;
}

function isOpeningApp(currentUrl: URL, newUrl: URL) {
if (app.isPackaged) {
return (
currentUrl.protocol === 'file:' &&
currentUrl.protocol === newUrl.protocol &&
currentUrl.pathname === newUrl.pathname
);
}
});

app.on('activate', async () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
await createWindow();
return (
currentUrl.hostname === 'localhost' &&
currentUrl.host === newUrl.host &&
currentUrl.pathname === newUrl.pathname
);
}

async function openExternalUrl(url: string) {
const parsedUrl = maybeParseUrl(url);
if (!parsedUrl) {
return;
}
});

const { protocol } = parsedUrl;
// We could handle all possible link cases here, not only http/https
if (protocol === 'http:' || protocol === 'https:') {
try {
await shell.openExternal(url);
} catch (error: unknown) {
console.error(`Failed to open url: ${error}`);
}
}
}

function maybeParseUrl(value: string): URL | undefined {
if (typeof value === 'string') {
try {
return new URL(value);
} catch (err) {
// Errors are ignored, as we only want to check if the value is a valid url
console.error(`Failed to parse url: ${value}`);
}
}

return undefined;
}

async function onReady() {
await migrateToLatest(getUserSettings().dbPath);
const window = await createWindow();
createIPCHandler({ router, windows: [window] });
setAppMenu();
}

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and import them here.

0 comments on commit 83c67b0

Please sign in to comment.