mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2026-03-11 01:10:23 -07:00
refactor: main window on events
This commit is contained in:
parent
288415f31b
commit
adfb4cd0db
4 changed files with 289 additions and 318 deletions
|
|
@ -102,6 +102,7 @@ export interface WindowMeta extends Record<WindowNames, Record<string, unknown>
|
|||
[WindowNames.codeInjection]: { codeInjectionType?: CodeInjectionType };
|
||||
[WindowNames.editWorkspace]: { workspaceID?: string };
|
||||
[WindowNames.openUrlWith]: { incomingUrl?: string };
|
||||
[WindowNames.main]: { forceClose?: boolean };
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
106
src/services/windows/handleAttachToMenuBar.ts
Normal file
106
src/services/windows/handleAttachToMenuBar.ts
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
import { Menu, Tray, ipcMain, nativeImage } from 'electron';
|
||||
import windowStateKeeper from 'electron-window-state';
|
||||
import { menubar, Menubar } from 'menubar';
|
||||
import path from 'path';
|
||||
|
||||
import { REACT_PATH, isDev as isDevelopment, buildResourcePath } from '@services/constants/paths';
|
||||
|
||||
export default async function handleAttachToMenuBar(): Promise<Menubar> {
|
||||
const menubarWindowState = windowStateKeeper({
|
||||
file: 'window-state-menubar.json',
|
||||
defaultWidth: 400,
|
||||
defaultHeight: 400,
|
||||
});
|
||||
|
||||
// setImage after Tray instance is created to avoid
|
||||
// "Segmentation fault (core dumped)" bug on Linux
|
||||
// https://github.com/electron/electron/issues/22137#issuecomment-586105622
|
||||
// https://github.com/atomery/translatium/issues/164
|
||||
const tray = new Tray(nativeImage.createEmpty());
|
||||
// icon template is not supported on Windows & Linux
|
||||
const iconPath = path.resolve(buildResourcePath, process.platform === 'darwin' ? 'menubarTemplate.png' : 'menubar.png');
|
||||
tray.setImage(iconPath);
|
||||
|
||||
const menuBar = menubar({
|
||||
index: REACT_PATH,
|
||||
tray,
|
||||
preloadWindow: true,
|
||||
tooltip: 'TiddlyGit',
|
||||
browserWindow: {
|
||||
x: menubarWindowState.x,
|
||||
y: menubarWindowState.y,
|
||||
width: menubarWindowState.width,
|
||||
height: menubarWindowState.height,
|
||||
minHeight: 100,
|
||||
minWidth: 250,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
enableRemoteModule: true,
|
||||
webSecurity: !isDevelopment,
|
||||
contextIsolation: true,
|
||||
preload: path.join(__dirname, '..', 'preload', 'menubar.js'),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
menuBar.on('after-create-window', () => {
|
||||
if (menuBar.window) {
|
||||
menubarWindowState.manage(menuBar.window);
|
||||
|
||||
menuBar.window.on('focus', () => {
|
||||
const view = menuBar.window?.getBrowserView();
|
||||
if (view?.webContents) {
|
||||
view.webContents.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return await new Promise<Menubar>((resolve) => {
|
||||
menuBar.on('ready', () => {
|
||||
menuBar.tray.on('right-click', () => {
|
||||
// TODO: restore updater options here
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: 'Open TiddlyGit',
|
||||
click: async () => await menuBar.showWindow(),
|
||||
},
|
||||
{
|
||||
type: 'separator',
|
||||
},
|
||||
{
|
||||
label: 'About TiddlyGit',
|
||||
click: () => ipcMain.emit('request-show-about-window'),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Preferences...',
|
||||
click: () => ipcMain.emit('request-show-preferences-window'),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Notifications...',
|
||||
click: () => ipcMain.emit('request-show-notifications-window'),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Clear Browsing Data...',
|
||||
click: () => ipcMain.emit('request-clear-browsing-data'),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
role: 'quit',
|
||||
click: () => {
|
||||
menuBar.app.quit();
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
menuBar.tray.popUpContextMenu(contextMenu);
|
||||
});
|
||||
|
||||
resolve(menuBar);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -1,18 +1,21 @@
|
|||
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
||||
import { BrowserWindow, ipcMain, dialog, app, App, remote, clipboard } from 'electron';
|
||||
import { BrowserWindow, ipcMain, dialog, app, App, remote, clipboard, BrowserWindowConstructorOptions } from 'electron';
|
||||
import isDevelopment from 'electron-is-dev';
|
||||
import { injectable, inject } from 'inversify';
|
||||
import windowStateKeeper, { State as windowStateKeeperState } from 'electron-window-state';
|
||||
|
||||
import { IBrowserViewMetaData, WindowNames, windowDimension, WindowMeta, CodeInjectionType } from '@services/windows/WindowProperties';
|
||||
import serviceIdentifiers from '@services/serviceIdentifier';
|
||||
import { Preference } from '@services/preferences';
|
||||
import { Workspace } from '@services/workspaces';
|
||||
import { WorkspaceView } from '@services/workspacesView';
|
||||
import { MenuService } from '@services/menu';
|
||||
import { Channels, WindowChannel, MetaDataChannel } from '@/constants/channels';
|
||||
|
||||
import i18n from '@services/libs/i18n';
|
||||
import getViewBounds from '@services/libs/get-view-bounds';
|
||||
import getFromRenderer from '@services/libs/getFromRenderer';
|
||||
import handleAttachToMenuBar from './handleAttachToMenuBar';
|
||||
|
||||
@injectable()
|
||||
export class Window {
|
||||
|
|
@ -22,6 +25,7 @@ export class Window {
|
|||
constructor(
|
||||
@inject(serviceIdentifiers.Preference) private readonly preferenceService: Preference,
|
||||
@inject(serviceIdentifiers.Workspace) private readonly workspaceService: Workspace,
|
||||
@inject(serviceIdentifiers.WorkspaceView) private readonly workspaceViewService: WorkspaceView,
|
||||
@inject(serviceIdentifiers.MenuService) private readonly menuService: MenuService,
|
||||
) {
|
||||
this.initIPCHandlers();
|
||||
|
|
@ -29,53 +33,6 @@ export class Window {
|
|||
}
|
||||
|
||||
initIPCHandlers(): void {
|
||||
ipcMain.handle('request-go-home', async (_event: Electron.IpcMainInvokeEvent, windowName: WindowNames = WindowNames.main) => {
|
||||
const win = this.get(windowName);
|
||||
const contents = win?.getBrowserView()?.webContents;
|
||||
const activeWorkspace = this.workspaceService.getActiveWorkspace();
|
||||
if (contents !== undefined && activeWorkspace !== undefined && win !== undefined) {
|
||||
await contents.loadURL(activeWorkspace.homeUrl);
|
||||
contents.send('update-can-go-back', contents.canGoBack());
|
||||
contents.send('update-can-go-forward', contents.canGoForward());
|
||||
}
|
||||
});
|
||||
ipcMain.handle('request-go-back', (_event: Electron.IpcMainInvokeEvent, windowName: WindowNames = WindowNames.main) => {
|
||||
const win = this.get(windowName);
|
||||
const contents = win?.getBrowserView()?.webContents;
|
||||
if (contents?.canGoBack() === true) {
|
||||
contents.goBack();
|
||||
contents.send('update-can-go-back', contents.canGoBack());
|
||||
contents.send('update-can-go-forward', contents.canGoForward());
|
||||
}
|
||||
});
|
||||
ipcMain.handle('request-go-forward', (_event: Electron.IpcMainInvokeEvent, windowName: WindowNames = WindowNames.main) => {
|
||||
const win = this.get(windowName);
|
||||
const contents = win?.getBrowserView()?.webContents;
|
||||
if (contents?.canGoForward() === true) {
|
||||
contents.goForward();
|
||||
contents.send('update-can-go-back', contents.canGoBack());
|
||||
contents.send('update-can-go-forward', contents.canGoForward());
|
||||
}
|
||||
});
|
||||
ipcMain.handle('request-reload', (_event: Electron.IpcMainInvokeEvent, windowName: WindowNames = WindowNames.main) => {
|
||||
const win = this.get(windowName);
|
||||
win?.getBrowserView()?.webContents?.reload();
|
||||
});
|
||||
ipcMain.handle('request-show-message-box', (_event, message: Electron.MessageBoxOptions['message'], type?: Electron.MessageBoxOptions['type']) => {
|
||||
const mainWindow = this.get(WindowNames.main);
|
||||
if (mainWindow !== undefined) {
|
||||
dialog
|
||||
.showMessageBox(mainWindow, {
|
||||
type: type ?? 'error',
|
||||
message,
|
||||
buttons: ['OK'],
|
||||
cancelId: 0,
|
||||
defaultId: 0,
|
||||
})
|
||||
.catch(console.log);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle(WindowChannel.requestShowRequireRestartDialog, () => {
|
||||
const availableWindowToShowDialog = this.get(WindowNames.preferences) ?? this.get(WindowNames.main);
|
||||
if (availableWindowToShowDialog !== undefined) {
|
||||
|
|
@ -180,16 +137,47 @@ export class Window {
|
|||
const existedWindow = this.windows[windowName];
|
||||
const existedWindowMeta = this.windowMeta[windowName];
|
||||
if (existedWindow !== undefined) {
|
||||
// TODO: handle this menubar logic
|
||||
// if (attachToMenubar) {
|
||||
// if (menuBar == undefined) {
|
||||
// createAsync();
|
||||
// } else {
|
||||
// menuBar.on('ready', () => {
|
||||
// menuBar.showWindow();
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
if (recreate === true || (typeof recreate === 'function' && existedWindowMeta !== undefined && recreate(existedWindowMeta))) {
|
||||
existedWindow.close();
|
||||
} else {
|
||||
return existedWindow.show();
|
||||
}
|
||||
}
|
||||
|
||||
const attachToMenubar: boolean = this.preferenceService.get('attachToMenubar');
|
||||
let mainWindowConfig: Partial<BrowserWindowConstructorOptions> = {};
|
||||
let mainWindowState: windowStateKeeperState | undefined;
|
||||
const isMainWindow = windowName === WindowNames.main;
|
||||
if (isMainWindow) {
|
||||
if (attachToMenubar) {
|
||||
await handleAttachToMenuBar();
|
||||
}
|
||||
|
||||
mainWindowState = windowStateKeeper({
|
||||
defaultWidth: windowDimension[WindowNames.main].width,
|
||||
defaultHeight: windowDimension[WindowNames.main].height,
|
||||
});
|
||||
mainWindowConfig = {
|
||||
x: mainWindowState.x,
|
||||
y: mainWindowState.y,
|
||||
width: mainWindowState.width,
|
||||
height: mainWindowState.height,
|
||||
};
|
||||
}
|
||||
|
||||
const newWindow = new BrowserWindow({
|
||||
...windowDimension[windowName],
|
||||
...mainWindowConfig,
|
||||
resizable: false,
|
||||
maximizable: false,
|
||||
minimizable: false,
|
||||
|
|
@ -205,15 +193,107 @@ export class Window {
|
|||
},
|
||||
parent: windowName === WindowNames.main || attachToMenubar ? undefined : this.get(WindowNames.main),
|
||||
});
|
||||
newWindow.setMenuBarVisibility(false);
|
||||
|
||||
this.windows[windowName] = newWindow;
|
||||
if (isMainWindow) {
|
||||
mainWindowState?.manage(newWindow);
|
||||
await this.registerMainWindowListeners(newWindow);
|
||||
} else {
|
||||
newWindow.setMenuBarVisibility(false);
|
||||
}
|
||||
newWindow.on('closed', () => {
|
||||
this.windows[windowName] = undefined;
|
||||
});
|
||||
this.windows[windowName] = newWindow;
|
||||
return newWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
|
||||
}
|
||||
|
||||
private async registerMainWindowListeners(newWindow: BrowserWindow): Promise<void> {
|
||||
const { wasOpenedAsHidden } = app.getLoginItemSettings();
|
||||
// Enable swipe to navigate
|
||||
const swipeToNavigate = this.preferenceService.get('swipeToNavigate');
|
||||
if (swipeToNavigate) {
|
||||
const mainWindow = this.get(WindowNames.main);
|
||||
if (mainWindow === undefined) return;
|
||||
mainWindow.on('swipe', (_event, direction) => {
|
||||
const view = mainWindow?.getBrowserView();
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
if (view) {
|
||||
if (direction === 'left') {
|
||||
view.webContents.goBack();
|
||||
} else if (direction === 'right') {
|
||||
view.webContents.goForward();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Hide window instead closing on macos
|
||||
newWindow.on('close', (event) => {
|
||||
const mainWindow = this.get(WindowNames.main);
|
||||
if (mainWindow === undefined) return;
|
||||
if (process.platform === 'darwin' && this.getWindowMeta(WindowNames.main).forceClose !== true) {
|
||||
event.preventDefault();
|
||||
// https://github.com/electron/electron/issues/6033#issuecomment-242023295
|
||||
if (mainWindow.isFullScreen()) {
|
||||
mainWindow.once('leave-full-screen', () => {
|
||||
const mainWindow = this.get(WindowNames.main);
|
||||
if (mainWindow !== undefined) {
|
||||
mainWindow.hide();
|
||||
}
|
||||
});
|
||||
mainWindow.setFullScreen(false);
|
||||
} else {
|
||||
mainWindow.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
newWindow.on('focus', () => {
|
||||
const mainWindow = this.get(WindowNames.main);
|
||||
if (mainWindow === undefined) return;
|
||||
const view = mainWindow?.getBrowserView();
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
view?.webContents?.focus();
|
||||
});
|
||||
|
||||
newWindow.once('ready-to-show', () => {
|
||||
const mainWindow = this.get(WindowNames.main);
|
||||
if (mainWindow === undefined) return;
|
||||
if (!wasOpenedAsHidden) {
|
||||
mainWindow.show();
|
||||
}
|
||||
|
||||
// calling this to redundantly setBounds BrowserView
|
||||
// after the UI is fully loaded
|
||||
// if not, BrowserView mouseover event won't work correctly
|
||||
// https://github.com/atomery/webcatalog/issues/812
|
||||
this.workspaceViewService.realignActiveWorkspace();
|
||||
});
|
||||
|
||||
newWindow.on('enter-full-screen', () => {
|
||||
const mainWindow = this.get(WindowNames.main);
|
||||
if (mainWindow === undefined) return;
|
||||
mainWindow?.webContents.send('is-fullscreen-updated', true);
|
||||
this.workspaceViewService.realignActiveWorkspace();
|
||||
});
|
||||
newWindow.on('leave-full-screen', () => {
|
||||
const mainWindow = this.get(WindowNames.main);
|
||||
if (mainWindow === undefined) return;
|
||||
mainWindow?.webContents.send('is-fullscreen-updated', false);
|
||||
this.workspaceViewService.realignActiveWorkspace();
|
||||
});
|
||||
|
||||
return await new Promise<void>((resolve) => {
|
||||
const mainWindow = this.get(WindowNames.main);
|
||||
if (mainWindow === undefined) return;
|
||||
// ensure redux is loaded first
|
||||
// if not, redux might not be able catch changes sent from ipcMain
|
||||
mainWindow.webContents.once('did-stop-loading', () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public setWindowMeta<N extends WindowNames>(windowName: N, meta: WindowMeta[N]): void {
|
||||
this.windowMeta[windowName] = meta;
|
||||
}
|
||||
|
|
@ -238,6 +318,57 @@ export class Window {
|
|||
});
|
||||
};
|
||||
|
||||
public async goHome(windowName: WindowNames = WindowNames.main): Promise<void> {
|
||||
const win = this.get(windowName);
|
||||
const contents = win?.getBrowserView()?.webContents;
|
||||
const activeWorkspace = this.workspaceService.getActiveWorkspace();
|
||||
if (contents !== undefined && activeWorkspace !== undefined && win !== undefined) {
|
||||
await contents.loadURL(activeWorkspace.homeUrl);
|
||||
contents.send('update-can-go-back', contents.canGoBack());
|
||||
contents.send('update-can-go-forward', contents.canGoForward());
|
||||
}
|
||||
}
|
||||
|
||||
public goBack(windowName: WindowNames = WindowNames.main): void {
|
||||
const win = this.get(windowName);
|
||||
const contents = win?.getBrowserView()?.webContents;
|
||||
if (contents?.canGoBack() === true) {
|
||||
contents.goBack();
|
||||
contents.send('update-can-go-back', contents.canGoBack());
|
||||
contents.send('update-can-go-forward', contents.canGoForward());
|
||||
}
|
||||
}
|
||||
|
||||
public goForward(windowName: WindowNames = WindowNames.main): void {
|
||||
const win = this.get(windowName);
|
||||
const contents = win?.getBrowserView()?.webContents;
|
||||
if (contents?.canGoForward() === true) {
|
||||
contents.goForward();
|
||||
contents.send('update-can-go-back', contents.canGoBack());
|
||||
contents.send('update-can-go-forward', contents.canGoForward());
|
||||
}
|
||||
}
|
||||
|
||||
public reload(windowName: WindowNames = WindowNames.main): void {
|
||||
const win = this.get(windowName);
|
||||
win?.getBrowserView()?.webContents?.reload();
|
||||
}
|
||||
|
||||
public showMessageBox(message: Electron.MessageBoxOptions['message'], type?: Electron.MessageBoxOptions['type']): void {
|
||||
const mainWindow = this.get(WindowNames.main);
|
||||
if (mainWindow !== undefined) {
|
||||
dialog
|
||||
.showMessageBox(mainWindow, {
|
||||
type: type ?? 'error',
|
||||
message,
|
||||
buttons: ['OK'],
|
||||
cancelId: 0,
|
||||
defaultId: 0,
|
||||
})
|
||||
.catch(console.log);
|
||||
}
|
||||
}
|
||||
|
||||
private registerMenu(): void {
|
||||
this.menuService.insertMenu(
|
||||
'window',
|
||||
|
|
@ -368,6 +499,7 @@ export class Window {
|
|||
]);
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
// TODO: restore updater options here
|
||||
this.menuService.insertMenu('TiddlyGit', [
|
||||
{
|
||||
label: i18n.t('ContextMenu.About'),
|
||||
|
|
|
|||
|
|
@ -1,268 +0,0 @@
|
|||
import { BrowserWindow, Menu, Tray, app, ipcMain, nativeImage } from 'electron';
|
||||
import windowStateKeeper from 'electron-window-state';
|
||||
import { menubar, Menubar } from 'menubar';
|
||||
import path from 'path';
|
||||
|
||||
import { REACT_PATH, isDev as isDevelopment, buildResourcePath } from '@services/constants/paths';
|
||||
import { getPreference } from '../libs/preferences';
|
||||
import formatBytes from '../libs/format-bytes';
|
||||
|
||||
let win: BrowserWindow | undefined;
|
||||
let menuBar: Menubar;
|
||||
let attachToMenubar = false;
|
||||
|
||||
export const get = (): BrowserWindow | undefined => {
|
||||
if (attachToMenubar && menuBar) return menuBar.window;
|
||||
return win;
|
||||
};
|
||||
|
||||
export const createAsync = async (): Promise<void> =>
|
||||
await new Promise<void>((resolve) => {
|
||||
attachToMenubar = getPreference('attachToMenubar');
|
||||
if (attachToMenubar) {
|
||||
const menubarWindowState = windowStateKeeper({
|
||||
file: 'window-state-menubar.json',
|
||||
defaultWidth: 400,
|
||||
defaultHeight: 400,
|
||||
});
|
||||
|
||||
// setImage after Tray instance is created to avoid
|
||||
// "Segmentation fault (core dumped)" bug on Linux
|
||||
// https://github.com/electron/electron/issues/22137#issuecomment-586105622
|
||||
// https://github.com/atomery/translatium/issues/164
|
||||
const tray = new Tray(nativeImage.createEmpty());
|
||||
// icon template is not supported on Windows & Linux
|
||||
const iconPath = path.resolve(buildResourcePath, process.platform === 'darwin' ? 'menubarTemplate.png' : 'menubar.png');
|
||||
tray.setImage(iconPath);
|
||||
|
||||
menuBar = menubar({
|
||||
index: REACT_PATH,
|
||||
tray,
|
||||
preloadWindow: true,
|
||||
tooltip: 'TiddlyGit',
|
||||
browserWindow: {
|
||||
x: menubarWindowState.x,
|
||||
y: menubarWindowState.y,
|
||||
width: menubarWindowState.width,
|
||||
height: menubarWindowState.height,
|
||||
minHeight: 100,
|
||||
minWidth: 250,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
enableRemoteModule: true,
|
||||
webSecurity: !isDevelopment,
|
||||
contextIsolation: true,
|
||||
preload: path.join(__dirname, '..', 'preload', 'menubar.js'),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
menuBar.on('after-create-window', () => {
|
||||
if (menuBar.window) {
|
||||
menubarWindowState.manage(menuBar.window);
|
||||
|
||||
menuBar.window.on('focus', () => {
|
||||
const view = menuBar.window?.getBrowserView();
|
||||
if (view && view.webContents) {
|
||||
view.webContents.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
menuBar.on('ready', () => {
|
||||
menuBar.tray.on('right-click', () => {
|
||||
const updaterEnabled = process.env.SNAP == undefined && !process.mas && !process.windowsStore;
|
||||
|
||||
const updaterMenuItem = {
|
||||
label: 'Check for Updates...',
|
||||
click: () => ipcMain.emit('request-check-for-updates'),
|
||||
visible: updaterEnabled,
|
||||
enabled: true,
|
||||
};
|
||||
if (global.updaterObj && global.updaterObj.status === 'update-downloaded') {
|
||||
updaterMenuItem.label = 'Restart to Apply Updates...';
|
||||
} else if (global.updaterObj && global.updaterObj.status === 'update-available') {
|
||||
updaterMenuItem.label = 'Downloading Updates...';
|
||||
updaterMenuItem.enabled = false;
|
||||
} else if (global.updaterObj && global.updaterObj.status === 'download-progress') {
|
||||
const { transferred, total, bytesPerSecond } = global.updaterObj.info;
|
||||
updaterMenuItem.label = `Downloading Updates (${formatBytes(transferred)}/${formatBytes(total)} at ${formatBytes(bytesPerSecond)}/s)...`;
|
||||
updaterMenuItem.enabled = false;
|
||||
} else if (global.updaterObj && global.updaterObj.status === 'checking-for-update') {
|
||||
updaterMenuItem.label = 'Checking for Updates...';
|
||||
updaterMenuItem.enabled = false;
|
||||
}
|
||||
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: 'Open TiddlyGit',
|
||||
click: () => menuBar.showWindow(),
|
||||
},
|
||||
{
|
||||
type: 'separator',
|
||||
},
|
||||
{
|
||||
label: 'About TiddlyGit',
|
||||
click: () => ipcMain.emit('request-show-about-window'),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
updaterMenuItem,
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Preferences...',
|
||||
click: () => ipcMain.emit('request-show-preferences-window'),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Notifications...',
|
||||
click: () => ipcMain.emit('request-show-notifications-window'),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Clear Browsing Data...',
|
||||
click: () => ipcMain.emit('request-clear-browsing-data'),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
role: 'quit',
|
||||
click: () => {
|
||||
menuBar.app.quit();
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
menuBar.tray.popUpContextMenu(contextMenu);
|
||||
});
|
||||
|
||||
resolve();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const { wasOpenedAsHidden } = app.getLoginItemSettings();
|
||||
|
||||
const mainWindowState = windowStateKeeper({
|
||||
defaultWidth: 1000,
|
||||
defaultHeight: 768,
|
||||
});
|
||||
|
||||
win = new BrowserWindow({
|
||||
x: mainWindowState.x,
|
||||
y: mainWindowState.y,
|
||||
width: mainWindowState.width,
|
||||
height: mainWindowState.height,
|
||||
minHeight: 100,
|
||||
minWidth: 350,
|
||||
title: 'TiddlyGit',
|
||||
titleBarStyle: 'hidden',
|
||||
show: false,
|
||||
// manually set dock icon for AppImage
|
||||
// Snap icon is set correct already so no need to intervene
|
||||
icon: process.platform === 'linux' && process.env.SNAP == undefined ? path.resolve(buildResourcePath, 'icon.png') : undefined,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
enableRemoteModule: true,
|
||||
webSecurity: !isDevelopment,
|
||||
contextIsolation: true,
|
||||
// @ts-expect-error ts-migrate(2304) FIXME: Cannot find name 'MAIN_WINDOW_PRELOAD_WEBPACK_ENTR... Remove this comment to see the full error message
|
||||
preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
|
||||
},
|
||||
});
|
||||
if (getPreference('hideMenuBar')) {
|
||||
win.setMenuBarVisibility(false);
|
||||
}
|
||||
|
||||
mainWindowState.manage(win);
|
||||
|
||||
// Enable swipe to navigate
|
||||
const swipeToNavigate = getPreference('swipeToNavigate');
|
||||
if (swipeToNavigate) {
|
||||
win.on('swipe', (e, direction) => {
|
||||
const view = win?.getBrowserView();
|
||||
if (view) {
|
||||
if (direction === 'left') {
|
||||
view.webContents.goBack();
|
||||
} else if (direction === 'right') {
|
||||
view.webContents.goForward();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Hide window instead closing on macos
|
||||
win.on('close', (e) => {
|
||||
// FIXME: use custom property
|
||||
if (win && process.platform === 'darwin' && !(win as any).forceClose) {
|
||||
e.preventDefault();
|
||||
// https://github.com/electron/electron/issues/6033#issuecomment-242023295
|
||||
if (win.isFullScreen()) {
|
||||
win.once('leave-full-screen', () => {
|
||||
if (win) {
|
||||
win.hide();
|
||||
}
|
||||
});
|
||||
win.setFullScreen(false);
|
||||
} else {
|
||||
win.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
win.on('closed', () => {
|
||||
win = undefined;
|
||||
});
|
||||
|
||||
win.on('focus', () => {
|
||||
const view = win?.getBrowserView();
|
||||
if (view && view.webContents) {
|
||||
view.webContents.focus();
|
||||
}
|
||||
});
|
||||
|
||||
win.once('ready-to-show', () => {
|
||||
if (win && !wasOpenedAsHidden) {
|
||||
win.show();
|
||||
}
|
||||
|
||||
// calling this to redundantly setBounds BrowserView
|
||||
// after the UI is fully loaded
|
||||
// if not, BrowserView mouseover event won't work correctly
|
||||
// https://github.com/atomery/webcatalog/issues/812
|
||||
this.workspaceViewService.realignActiveWorkspace();
|
||||
});
|
||||
|
||||
win.on('enter-full-screen', () => {
|
||||
win?.webContents.send('is-fullscreen-updated', true);
|
||||
this.workspaceViewService.realignActiveWorkspace();
|
||||
});
|
||||
win.on('leave-full-screen', () => {
|
||||
win?.webContents.send('is-fullscreen-updated', false);
|
||||
this.workspaceViewService.realignActiveWorkspace();
|
||||
});
|
||||
|
||||
// ensure redux is loaded first
|
||||
// if not, redux might not be able catch changes sent from ipcMain
|
||||
win.webContents.once('did-stop-loading', () => {
|
||||
resolve();
|
||||
});
|
||||
|
||||
// @ts-expect-error ts-migrate(2304) FIXME: Cannot find name 'MAIN_WINDOW_WEBPACK_ENTRY'.
|
||||
void win.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
|
||||
});
|
||||
|
||||
export const show = () => {
|
||||
if (attachToMenubar) {
|
||||
if (menuBar == undefined) {
|
||||
createAsync();
|
||||
} else {
|
||||
menuBar.on('ready', () => {
|
||||
menuBar.showWindow();
|
||||
});
|
||||
}
|
||||
} else if (win == undefined) {
|
||||
createAsync();
|
||||
} else {
|
||||
win.show();
|
||||
}
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue