mirror of
https://github.com/Llewellynvdm/nativefier.git
synced 2024-11-10 23:31:10 +00:00
* Catch promise errors better * Move subFunctions to bottom of createNewWindow * Use parents when creating child BrowserWindow instances * Some about:blank pages have an anchor (for some reason) * Inject browserWindowOptions better * Interim refactor to MainWindow object * Split up the window functions/helpers/events some * Further separate out window functions + tests * Add a mock for unit testing functions that access electron * Add unit tests for onWillPreventUnload * Improve windowEvents tests * Add the first test for windowHelpers * Move WebRequest event handling to node * insertCSS completely under test * clearAppData completely under test * Fix contextMenu require bug * More tests + fixes * Fix + add to createNewTab tests * Convert createMainWindow back to func + work out gremlins * Move setupWindow away from main since its shared * Make sure contextMenu is handling promises * Fix issues with fullscreen not working + menu refactor * Run jest against app/dist so that we can hit app unit tests as well * Requested PR changes
This commit is contained in:
parent
16cacb0915
commit
cdc6fa79c2
@ -10,21 +10,12 @@ import {
|
|||||||
getCounterValue,
|
getCounterValue,
|
||||||
isOSX,
|
isOSX,
|
||||||
nativeTabsSupported,
|
nativeTabsSupported,
|
||||||
openExternal,
|
|
||||||
} from '../helpers/helpers';
|
} from '../helpers/helpers';
|
||||||
import { setupNativefierWindow } from '../helpers/windowEvents';
|
import { setupNativefierWindow } from '../helpers/windowEvents';
|
||||||
import {
|
import {
|
||||||
clearAppData,
|
|
||||||
clearCache,
|
clearCache,
|
||||||
getCurrentURL,
|
|
||||||
getDefaultWindowOptions,
|
getDefaultWindowOptions,
|
||||||
goBack,
|
|
||||||
goForward,
|
|
||||||
goToURL,
|
|
||||||
hideWindow,
|
hideWindow,
|
||||||
zoomIn,
|
|
||||||
zoomOut,
|
|
||||||
zoomReset,
|
|
||||||
} from '../helpers/windowHelpers';
|
} from '../helpers/windowHelpers';
|
||||||
import { initContextMenu } from './contextMenu';
|
import { initContextMenu } from './contextMenu';
|
||||||
import { createMenu } from './menu';
|
import { createMenu } from './menu';
|
||||||
@ -47,12 +38,10 @@ type SessionInteractionResult = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {{}} nativefierOptions AppArgs from nativefier.json
|
* @param {{}} nativefierOptions AppArgs from nativefier.json
|
||||||
* @param {function} onAppQuit
|
|
||||||
* @param {function} setDockBadge
|
* @param {function} setDockBadge
|
||||||
*/
|
*/
|
||||||
export async function createMainWindow(
|
export async function createMainWindow(
|
||||||
nativefierOptions,
|
nativefierOptions,
|
||||||
onAppQuit: () => void,
|
|
||||||
setDockBadge: (value: number | string, bounce?: boolean) => void,
|
setDockBadge: (value: number | string, bounce?: boolean) => void,
|
||||||
): Promise<BrowserWindow> {
|
): Promise<BrowserWindow> {
|
||||||
const options = { ...nativefierOptions };
|
const options = { ...nativefierOptions };
|
||||||
@ -74,8 +63,7 @@ export async function createMainWindow(
|
|||||||
y: options.y,
|
y: options.y,
|
||||||
autoHideMenuBar: !options.showMenuBar,
|
autoHideMenuBar: !options.showMenuBar,
|
||||||
icon: getAppIcon(),
|
icon: getAppIcon(),
|
||||||
// set to undefined and not false because explicitly setting to false will disable full screen
|
fullscreen: options.fullScreen,
|
||||||
fullscreen: options.fullScreen ?? undefined,
|
|
||||||
// Whether the window should always stay on top of other windows. Default is false.
|
// Whether the window should always stay on top of other windows. Default is false.
|
||||||
alwaysOnTop: options.alwaysOnTop,
|
alwaysOnTop: options.alwaysOnTop,
|
||||||
titleBarStyle: options.titleBarStyle,
|
titleBarStyle: options.titleBarStyle,
|
||||||
@ -96,8 +84,7 @@ export async function createMainWindow(
|
|||||||
if (options.tray === 'start-in-tray') {
|
if (options.tray === 'start-in-tray') {
|
||||||
mainWindow.hide();
|
mainWindow.hide();
|
||||||
}
|
}
|
||||||
|
createMenu(options, mainWindow);
|
||||||
createMainMenu(options, mainWindow, onAppQuit);
|
|
||||||
createContextMenu(options, mainWindow);
|
createContextMenu(options, mainWindow);
|
||||||
setupNativefierWindow(options, mainWindow);
|
setupNativefierWindow(options, mainWindow);
|
||||||
|
|
||||||
@ -180,30 +167,6 @@ function setupCounter(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createMainMenu(
|
|
||||||
options: any,
|
|
||||||
window: BrowserWindow,
|
|
||||||
onAppQuit: () => void,
|
|
||||||
) {
|
|
||||||
const menuOptions = {
|
|
||||||
nativefierVersion: options.nativefierVersion,
|
|
||||||
appQuit: onAppQuit,
|
|
||||||
clearAppData: () => clearAppData(window),
|
|
||||||
disableDevTools: options.disableDevTools,
|
|
||||||
getCurrentURL,
|
|
||||||
goBack,
|
|
||||||
goForward,
|
|
||||||
goToURL,
|
|
||||||
openExternal,
|
|
||||||
zoomBuildTimeValue: options.zoom,
|
|
||||||
zoomIn,
|
|
||||||
zoomOut,
|
|
||||||
zoomReset,
|
|
||||||
};
|
|
||||||
|
|
||||||
createMenu(menuOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupNotificationBadge(
|
function setupNotificationBadge(
|
||||||
options,
|
options,
|
||||||
window: BrowserWindow,
|
window: BrowserWindow,
|
||||||
@ -275,7 +238,7 @@ function setupSessionInteraction(options, window: BrowserWindow): void {
|
|||||||
result.value = window.webContents.session[request.property];
|
result.value = window.webContents.session[request.property];
|
||||||
} else {
|
} else {
|
||||||
// Why even send the event if you're going to do this? You're just wasting time! ;)
|
// Why even send the event if you're going to do this? You're just wasting time! ;)
|
||||||
throw Error(
|
throw new Error(
|
||||||
'Received neither a func nor a property in the request. Unable to process.',
|
'Received neither a func nor a property in the request. Unable to process.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
159
app/src/components/menu.test.ts
Normal file
159
app/src/components/menu.test.ts
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
import { BrowserWindow } from 'electron';
|
||||||
|
|
||||||
|
jest.mock('../helpers/helpers');
|
||||||
|
import { isOSX } from '../helpers/helpers';
|
||||||
|
import { generateMenu } from './menu';
|
||||||
|
|
||||||
|
describe('generateMenu', () => {
|
||||||
|
let window: BrowserWindow;
|
||||||
|
const mockIsOSX: jest.SpyInstance = isOSX as jest.Mock;
|
||||||
|
let mockIsFullScreen: jest.SpyInstance;
|
||||||
|
let mockIsFullScreenable: jest.SpyInstance;
|
||||||
|
let mockIsSimpleFullScreen: jest.SpyInstance;
|
||||||
|
let mockSetFullScreen: jest.SpyInstance;
|
||||||
|
let mockSetSimpleFullScreen: jest.SpyInstance;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window = new BrowserWindow();
|
||||||
|
mockIsOSX.mockReset();
|
||||||
|
mockIsFullScreen = jest.spyOn(window, 'isFullScreen');
|
||||||
|
mockIsFullScreenable = jest.spyOn(window, 'isFullScreenable');
|
||||||
|
mockIsSimpleFullScreen = jest.spyOn(window, 'isSimpleFullScreen');
|
||||||
|
mockSetFullScreen = jest.spyOn(window, 'setFullScreen');
|
||||||
|
mockSetSimpleFullScreen = jest.spyOn(window, 'setSimpleFullScreen');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mockIsFullScreen.mockRestore();
|
||||||
|
mockIsFullScreenable.mockRestore();
|
||||||
|
mockIsSimpleFullScreen.mockRestore();
|
||||||
|
mockSetFullScreen.mockRestore();
|
||||||
|
mockSetSimpleFullScreen.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not have fullscreen if not supported', () => {
|
||||||
|
mockIsOSX.mockReturnValue(false);
|
||||||
|
mockIsFullScreenable.mockReturnValue(false);
|
||||||
|
|
||||||
|
const menu = generateMenu(
|
||||||
|
{
|
||||||
|
nativefierVersion: '1.0.0',
|
||||||
|
zoomBuildTimeValue: 1.0,
|
||||||
|
disableDevTools: false,
|
||||||
|
},
|
||||||
|
window,
|
||||||
|
);
|
||||||
|
|
||||||
|
const editMenu = menu.filter((item) => item.label === '&View');
|
||||||
|
|
||||||
|
const fullscreen = (editMenu[0].submenu as any[]).filter(
|
||||||
|
(item) => item.label === 'Toggle Full Screen',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(fullscreen).toHaveLength(1);
|
||||||
|
expect(fullscreen[0].enabled).toBe(false);
|
||||||
|
expect(fullscreen[0].visible).toBe(false);
|
||||||
|
|
||||||
|
expect(mockIsOSX).toHaveBeenCalled();
|
||||||
|
expect(mockIsFullScreenable).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('has fullscreen no matter what on mac', () => {
|
||||||
|
mockIsOSX.mockReturnValue(true);
|
||||||
|
mockIsFullScreenable.mockReturnValue(false);
|
||||||
|
|
||||||
|
const menu = generateMenu(
|
||||||
|
{
|
||||||
|
nativefierVersion: '1.0.0',
|
||||||
|
zoomBuildTimeValue: 1.0,
|
||||||
|
disableDevTools: false,
|
||||||
|
},
|
||||||
|
window,
|
||||||
|
);
|
||||||
|
|
||||||
|
const editMenu = menu.filter((item) => item.label === '&View');
|
||||||
|
|
||||||
|
const fullscreen = (editMenu[0].submenu as any[]).filter(
|
||||||
|
(item) => item.label === 'Toggle Full Screen',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(fullscreen).toHaveLength(1);
|
||||||
|
expect(fullscreen[0].enabled).toBe(true);
|
||||||
|
expect(fullscreen[0].visible).toBe(true);
|
||||||
|
|
||||||
|
expect(mockIsOSX).toHaveBeenCalled();
|
||||||
|
expect(mockIsFullScreenable).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each([true, false])(
|
||||||
|
'has a fullscreen menu item that toggles fullscreen',
|
||||||
|
(isFullScreen) => {
|
||||||
|
mockIsOSX.mockReturnValue(false);
|
||||||
|
mockIsFullScreenable.mockReturnValue(true);
|
||||||
|
mockIsFullScreen.mockReturnValue(isFullScreen);
|
||||||
|
|
||||||
|
const menu = generateMenu(
|
||||||
|
{
|
||||||
|
nativefierVersion: '1.0.0',
|
||||||
|
zoomBuildTimeValue: 1.0,
|
||||||
|
disableDevTools: false,
|
||||||
|
},
|
||||||
|
window,
|
||||||
|
);
|
||||||
|
|
||||||
|
const editMenu = menu.filter((item) => item.label === '&View');
|
||||||
|
|
||||||
|
const fullscreen = (editMenu[0].submenu as any[]).filter(
|
||||||
|
(item) => item.label === 'Toggle Full Screen',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(fullscreen).toHaveLength(1);
|
||||||
|
expect(fullscreen[0].enabled).toBe(true);
|
||||||
|
expect(fullscreen[0].visible).toBe(true);
|
||||||
|
|
||||||
|
expect(mockIsOSX).toHaveBeenCalled();
|
||||||
|
expect(mockIsFullScreenable).toHaveBeenCalled();
|
||||||
|
|
||||||
|
fullscreen[0].click(null, window);
|
||||||
|
|
||||||
|
expect(mockSetFullScreen).toHaveBeenCalledWith(!isFullScreen);
|
||||||
|
expect(mockSetSimpleFullScreen).not.toHaveBeenCalled();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test.each([true, false])(
|
||||||
|
'has a fullscreen menu item that toggles simplefullscreen as a fallback on mac',
|
||||||
|
(isFullScreen) => {
|
||||||
|
mockIsOSX.mockReturnValue(true);
|
||||||
|
mockIsFullScreenable.mockReturnValue(false);
|
||||||
|
mockIsSimpleFullScreen.mockReturnValue(isFullScreen);
|
||||||
|
|
||||||
|
const menu = generateMenu(
|
||||||
|
{
|
||||||
|
nativefierVersion: '1.0.0',
|
||||||
|
zoomBuildTimeValue: 1.0,
|
||||||
|
disableDevTools: false,
|
||||||
|
},
|
||||||
|
window,
|
||||||
|
);
|
||||||
|
|
||||||
|
const editMenu = menu.filter((item) => item.label === '&View');
|
||||||
|
|
||||||
|
const fullscreen = (editMenu[0].submenu as any[]).filter(
|
||||||
|
(item) => item.label === 'Toggle Full Screen',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(fullscreen).toHaveLength(1);
|
||||||
|
expect(fullscreen[0].enabled).toBe(true);
|
||||||
|
expect(fullscreen[0].visible).toBe(true);
|
||||||
|
|
||||||
|
expect(mockIsOSX).toHaveBeenCalled();
|
||||||
|
expect(mockIsFullScreenable).toHaveBeenCalled();
|
||||||
|
|
||||||
|
fullscreen[0].click(null, window);
|
||||||
|
|
||||||
|
expect(mockSetSimpleFullScreen).toHaveBeenCalledWith(!isFullScreen);
|
||||||
|
expect(mockSetFullScreen).not.toHaveBeenCalled();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
@ -1,39 +1,60 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import { clipboard, Menu, MenuItemConstructorOptions } from 'electron';
|
import {
|
||||||
|
BrowserWindow,
|
||||||
|
clipboard,
|
||||||
|
Menu,
|
||||||
|
MenuItem,
|
||||||
|
MenuItemConstructorOptions,
|
||||||
|
} from 'electron';
|
||||||
import * as log from 'loglevel';
|
import * as log from 'loglevel';
|
||||||
|
|
||||||
|
import { isOSX, openExternal } from '../helpers/helpers';
|
||||||
|
import {
|
||||||
|
clearAppData,
|
||||||
|
getCurrentURL,
|
||||||
|
goBack,
|
||||||
|
goForward,
|
||||||
|
goToURL,
|
||||||
|
zoomIn,
|
||||||
|
zoomOut,
|
||||||
|
zoomReset,
|
||||||
|
} from '../helpers/windowHelpers';
|
||||||
|
|
||||||
type BookmarksLink = {
|
type BookmarksLink = {
|
||||||
type: 'link';
|
type: 'link';
|
||||||
title: string;
|
title: string;
|
||||||
url: string;
|
url: string;
|
||||||
shortcut?: string;
|
shortcut?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type BookmarksSeparator = {
|
type BookmarksSeparator = {
|
||||||
type: 'separator';
|
type: 'separator';
|
||||||
};
|
};
|
||||||
|
|
||||||
type BookmarkConfig = BookmarksLink | BookmarksSeparator;
|
type BookmarkConfig = BookmarksLink | BookmarksSeparator;
|
||||||
|
|
||||||
type BookmarksMenuConfig = {
|
type BookmarksMenuConfig = {
|
||||||
menuLabel: string;
|
menuLabel: string;
|
||||||
bookmarks: BookmarkConfig[];
|
bookmarks: BookmarkConfig[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export function createMenu({
|
export function createMenu(options, mainWindow: BrowserWindow): void {
|
||||||
nativefierVersion,
|
log.debug('createMenu', { options, mainWindow });
|
||||||
appQuit,
|
const menuTemplate = generateMenu(options, mainWindow);
|
||||||
zoomIn,
|
|
||||||
zoomOut,
|
injectBookmarks(menuTemplate);
|
||||||
zoomReset,
|
|
||||||
zoomBuildTimeValue,
|
const menu = Menu.buildFromTemplate(menuTemplate);
|
||||||
goBack,
|
Menu.setApplicationMenu(menu);
|
||||||
goForward,
|
}
|
||||||
getCurrentURL,
|
|
||||||
goToURL,
|
export function generateMenu(
|
||||||
clearAppData,
|
options,
|
||||||
disableDevTools,
|
mainWindow: BrowserWindow,
|
||||||
openExternal,
|
): MenuItemConstructorOptions[] {
|
||||||
}): void {
|
const { nativefierVersion, zoomBuildTimeValue, disableDevTools } = options;
|
||||||
const zoomResetLabel =
|
const zoomResetLabel =
|
||||||
zoomBuildTimeValue === 1.0
|
zoomBuildTimeValue === 1.0
|
||||||
? 'Reset Zoom'
|
? 'Reset Zoom'
|
||||||
@ -69,8 +90,7 @@ export function createMenu({
|
|||||||
label: 'Copy Current URL',
|
label: 'Copy Current URL',
|
||||||
accelerator: 'CmdOrCtrl+L',
|
accelerator: 'CmdOrCtrl+L',
|
||||||
click: () => {
|
click: () => {
|
||||||
const currentURL = getCurrentURL();
|
clipboard.writeText(getCurrentURL());
|
||||||
clipboard.writeText(currentURL);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -90,7 +110,19 @@ export function createMenu({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Clear App Data',
|
label: 'Clear App Data',
|
||||||
click: clearAppData,
|
click: (item: MenuItem, focusedWindow: BrowserWindow) => {
|
||||||
|
log.debug('Clear App Data.click', {
|
||||||
|
item,
|
||||||
|
focusedWindow,
|
||||||
|
mainWindow,
|
||||||
|
});
|
||||||
|
if (!focusedWindow) {
|
||||||
|
focusedWindow = mainWindow;
|
||||||
|
}
|
||||||
|
clearAppData(focusedWindow).catch((err) =>
|
||||||
|
log.error('clearAppData ERROR', err),
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@ -100,11 +132,7 @@ export function createMenu({
|
|||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
label: 'Back',
|
label: 'Back',
|
||||||
accelerator: (() => {
|
accelerator: isOSX() ? 'CmdOrAlt+Left' : 'Alt+Left',
|
||||||
const backKbShortcut =
|
|
||||||
process.platform === 'darwin' ? 'Cmd+Left' : 'Alt+Left';
|
|
||||||
return backKbShortcut;
|
|
||||||
})(),
|
|
||||||
click: goBack,
|
click: goBack,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -116,11 +144,7 @@ export function createMenu({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Forward',
|
label: 'Forward',
|
||||||
accelerator: (() => {
|
accelerator: isOSX() ? 'Cmd+Right' : 'Alt+Right',
|
||||||
const forwardKbShortcut =
|
|
||||||
process.platform === 'darwin' ? 'Cmd+Right' : 'Alt+Right';
|
|
||||||
return forwardKbShortcut;
|
|
||||||
})(),
|
|
||||||
click: goForward,
|
click: goForward,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -132,27 +156,32 @@ export function createMenu({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Reload',
|
label: 'Reload',
|
||||||
accelerator: 'CmdOrCtrl+R',
|
role: 'reload',
|
||||||
click: (item, focusedWindow) => {
|
|
||||||
if (focusedWindow) {
|
|
||||||
focusedWindow.reload();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'separator',
|
type: 'separator',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Toggle Full Screen',
|
label: 'Toggle Full Screen',
|
||||||
accelerator: (() => {
|
accelerator: isOSX() ? 'Ctrl+Cmd+F' : 'F11',
|
||||||
if (process.platform === 'darwin') {
|
enabled: mainWindow.isFullScreenable() || isOSX(),
|
||||||
return 'Ctrl+Cmd+F';
|
visible: mainWindow.isFullScreenable() || isOSX(),
|
||||||
|
click: (item: MenuItem, focusedWindow: BrowserWindow) => {
|
||||||
|
log.debug('Toggle Full Screen.click()', {
|
||||||
|
item,
|
||||||
|
focusedWindow,
|
||||||
|
isFullScreen: focusedWindow?.isFullScreen(),
|
||||||
|
isFullScreenable: focusedWindow?.isFullScreenable(),
|
||||||
|
});
|
||||||
|
if (!focusedWindow) {
|
||||||
|
focusedWindow = mainWindow;
|
||||||
}
|
}
|
||||||
return 'F11';
|
if (focusedWindow.isFullScreenable()) {
|
||||||
})(),
|
|
||||||
click: (item, focusedWindow) => {
|
|
||||||
if (focusedWindow) {
|
|
||||||
focusedWindow.setFullScreen(!focusedWindow.isFullScreen());
|
focusedWindow.setFullScreen(!focusedWindow.isFullScreen());
|
||||||
|
} else if (isOSX()) {
|
||||||
|
focusedWindow.setSimpleFullScreen(
|
||||||
|
!focusedWindow.isSimpleFullScreen(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -202,16 +231,13 @@ export function createMenu({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Toggle Developer Tools',
|
label: 'Toggle Developer Tools',
|
||||||
accelerator: (() => {
|
accelerator: isOSX() ? 'Alt+Cmd+I' : 'Ctrl+Shift+I',
|
||||||
if (process.platform === 'darwin') {
|
click: (item: MenuItem, focusedWindow: BrowserWindow) => {
|
||||||
return 'Alt+Cmd+I';
|
log.debug('Toggle Developer Tools.click()', { item, focusedWindow });
|
||||||
|
if (!focusedWindow) {
|
||||||
|
focusedWindow = mainWindow;
|
||||||
}
|
}
|
||||||
return 'Ctrl+Shift+I';
|
|
||||||
})(),
|
|
||||||
click: (item, focusedWindow) => {
|
|
||||||
if (focusedWindow) {
|
|
||||||
focusedWindow.webContents.toggleDevTools();
|
focusedWindow.webContents.toggleDevTools();
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -241,13 +267,21 @@ export function createMenu({
|
|||||||
{
|
{
|
||||||
label: `Built with Nativefier v${nativefierVersion}`,
|
label: `Built with Nativefier v${nativefierVersion}`,
|
||||||
click: () => {
|
click: () => {
|
||||||
openExternal('https://github.com/nativefier/nativefier');
|
openExternal('https://github.com/nativefier/nativefier').catch(
|
||||||
|
(err) =>
|
||||||
|
log.error(
|
||||||
|
'Built with Nativefier v${nativefierVersion}.click ERROR',
|
||||||
|
err,
|
||||||
|
),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Report an Issue',
|
label: 'Report an Issue',
|
||||||
click: () => {
|
click: () => {
|
||||||
openExternal('https://github.com/nativefier/nativefier/issues');
|
openExternal('https://github.com/nativefier/nativefier/issues').catch(
|
||||||
|
(err) => log.error('Report an Issue.click ERROR', err),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -255,7 +289,7 @@ export function createMenu({
|
|||||||
|
|
||||||
let menuTemplate: MenuItemConstructorOptions[];
|
let menuTemplate: MenuItemConstructorOptions[];
|
||||||
|
|
||||||
if (process.platform === 'darwin') {
|
if (isOSX()) {
|
||||||
const electronMenu: MenuItemConstructorOptions = {
|
const electronMenu: MenuItemConstructorOptions = {
|
||||||
label: 'E&lectron',
|
label: 'E&lectron',
|
||||||
submenu: [
|
submenu: [
|
||||||
@ -287,7 +321,7 @@ export function createMenu({
|
|||||||
{
|
{
|
||||||
label: 'Quit',
|
label: 'Quit',
|
||||||
accelerator: 'Cmd+Q',
|
accelerator: 'Cmd+Q',
|
||||||
click: appQuit,
|
role: 'quit',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@ -305,43 +339,50 @@ export function createMenu({
|
|||||||
menuTemplate = [editMenu, viewMenu, windowMenu, helpMenu];
|
menuTemplate = [editMenu, viewMenu, windowMenu, helpMenu];
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
return menuTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
function injectBookmarks(menuTemplate: MenuItemConstructorOptions[]): void {
|
||||||
const bookmarkConfigPath = path.join(__dirname, '..', 'bookmarks.json');
|
const bookmarkConfigPath = path.join(__dirname, '..', 'bookmarks.json');
|
||||||
if (fs.existsSync(bookmarkConfigPath)) {
|
|
||||||
|
if (!fs.existsSync(bookmarkConfigPath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
const bookmarksMenuConfig: BookmarksMenuConfig = JSON.parse(
|
const bookmarksMenuConfig: BookmarksMenuConfig = JSON.parse(
|
||||||
fs.readFileSync(bookmarkConfigPath, 'utf-8'),
|
fs.readFileSync(bookmarkConfigPath, 'utf-8'),
|
||||||
);
|
);
|
||||||
const bookmarksMenu: MenuItemConstructorOptions = {
|
const bookmarksMenu: MenuItemConstructorOptions = {
|
||||||
label: bookmarksMenuConfig.menuLabel,
|
label: bookmarksMenuConfig.menuLabel,
|
||||||
submenu: bookmarksMenuConfig.bookmarks.map((bookmark) => {
|
submenu: bookmarksMenuConfig.bookmarks.map((bookmark) => {
|
||||||
if (bookmark.type === 'link') {
|
switch (bookmark.type) {
|
||||||
|
case 'link':
|
||||||
if (!('title' in bookmark && 'url' in bookmark)) {
|
if (!('title' in bookmark && 'url' in bookmark)) {
|
||||||
throw Error(
|
throw new Error(
|
||||||
'All links in the bookmarks menu must have a title and url.',
|
'All links in the bookmarks menu must have a title and url.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
new URL(bookmark.url);
|
new URL(bookmark.url);
|
||||||
} catch (_) {
|
} catch {
|
||||||
throw Error('Bookmark URL "' + bookmark.url + '"is invalid.');
|
throw new Error('Bookmark URL "' + bookmark.url + '"is invalid.');
|
||||||
}
|
|
||||||
let accelerator = null;
|
|
||||||
if ('shortcut' in bookmark) {
|
|
||||||
accelerator = bookmark.shortcut;
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
label: bookmark.title,
|
label: bookmark.title,
|
||||||
click: () => {
|
click: () => {
|
||||||
goToURL(bookmark.url);
|
goToURL(bookmark.url).catch((err) =>
|
||||||
|
log.error(`${bookmark.title}.click ERROR`, err),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
accelerator: accelerator,
|
accelerator: 'shortcut' in bookmark ? bookmark.shortcut : null,
|
||||||
};
|
};
|
||||||
} else if (bookmark.type === 'separator') {
|
case 'separator':
|
||||||
return {
|
return {
|
||||||
type: 'separator',
|
type: 'separator',
|
||||||
};
|
};
|
||||||
} else {
|
default:
|
||||||
throw Error(
|
throw new Error(
|
||||||
'A bookmarks menu entry has an invalid type; type must be one of "link", "separator".',
|
'A bookmarks menu entry has an invalid type; type must be one of "link", "separator".',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -349,11 +390,7 @@ export function createMenu({
|
|||||||
};
|
};
|
||||||
// Insert custom bookmarks menu between menus "View" and "Window"
|
// Insert custom bookmarks menu between menus "View" and "Window"
|
||||||
menuTemplate.splice(menuTemplate.length - 2, 0, bookmarksMenu);
|
menuTemplate.splice(menuTemplate.length - 2, 0, bookmarksMenu);
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log.error('Failed to load & parse bookmarks configuration JSON file.', err);
|
log.error('Failed to load & parse bookmarks configuration JSON file.', err);
|
||||||
}
|
}
|
||||||
|
|
||||||
const menu = Menu.buildFromTemplate(menuTemplate);
|
|
||||||
Menu.setApplicationMenu(menu);
|
|
||||||
}
|
}
|
||||||
|
@ -137,16 +137,16 @@ export function getDefaultWindowOptions(
|
|||||||
...(options.browserwindowOptions?.webPreferences ?? {}),
|
...(options.browserwindowOptions?.webPreferences ?? {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultOptions = {
|
const defaultOptions: BrowserWindowConstructorOptions = {
|
||||||
// Convert dashes to spaces because on linux the app name is joined with dashes
|
fullscreenable: true,
|
||||||
title: options.name,
|
|
||||||
tabbingIdentifier: nativeTabsSupported() ? options.name : undefined,
|
tabbingIdentifier: nativeTabsSupported() ? options.name : undefined,
|
||||||
|
title: options.name,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
javascript: true,
|
javascript: true,
|
||||||
plugins: true,
|
|
||||||
nodeIntegration: false, // `true` is *insecure*, and cause trouble with messenger.com
|
nodeIntegration: false, // `true` is *insecure*, and cause trouble with messenger.com
|
||||||
webSecurity: !options.insecure,
|
|
||||||
preload: path.join(__dirname, 'preload.js'),
|
preload: path.join(__dirname, 'preload.js'),
|
||||||
|
plugins: true,
|
||||||
|
webSecurity: !options.insecure,
|
||||||
zoomFactor: options.zoom,
|
zoomFactor: options.zoom,
|
||||||
...webPreferences,
|
...webPreferences,
|
||||||
},
|
},
|
||||||
|
@ -263,11 +263,7 @@ if (shouldQuit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function onReady(): Promise<void> {
|
async function onReady(): Promise<void> {
|
||||||
const mainWindow = await createMainWindow(
|
const mainWindow = await createMainWindow(appArgs, setDockBadge);
|
||||||
appArgs,
|
|
||||||
app.quit.bind(this),
|
|
||||||
setDockBadge,
|
|
||||||
);
|
|
||||||
|
|
||||||
createTrayIcon(appArgs, mainWindow);
|
createTrayIcon(appArgs, mainWindow);
|
||||||
|
|
||||||
|
@ -42,9 +42,29 @@ class MockBrowserWindow extends EventEmitter {
|
|||||||
return window ?? new MockBrowserWindow();
|
return window ?? new MockBrowserWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isSimpleFullScreen(): boolean {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
isFullScreen(): boolean {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
isFullScreenable(): boolean {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
loadURL(url: string, options?: any): Promise<void> {
|
loadURL(url: string, options?: any): Promise<void> {
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setFullScreen(flag: boolean): void {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSimpleFullScreen(flag: boolean): void {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockDialog {
|
class MockDialog {
|
||||||
|
@ -101,14 +101,14 @@
|
|||||||
"<rootDir>/src.*",
|
"<rootDir>/src.*",
|
||||||
"<rootDir>/node_modules.*",
|
"<rootDir>/node_modules.*",
|
||||||
"<rootDir>/app/src.*",
|
"<rootDir>/app/src.*",
|
||||||
"<rootDir>/app/dist.*",
|
"<rootDir>/app/lib.*",
|
||||||
"<rootDir>/app/node_modules.*"
|
"<rootDir>/app/node_modules.*"
|
||||||
],
|
],
|
||||||
"watchPathIgnorePatterns": [
|
"watchPathIgnorePatterns": [
|
||||||
"<rootDir>/src.*",
|
"<rootDir>/src.*",
|
||||||
"<rootDir>/tsconfig.json",
|
"<rootDir>/tsconfig.json",
|
||||||
"<rootDir>/app/src.*",
|
"<rootDir>/app/src.*",
|
||||||
"<rootDir>/app/dist.*",
|
"<rootDir>/app/lib.*",
|
||||||
"<rootDir>/app/tsconfig.json"
|
"<rootDir>/app/tsconfig.json"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user