feat: Window And Preferences wired

This commit is contained in:
tiddlygit-test 2020-12-29 00:03:48 +08:00
parent 33b38f6314
commit 3c10dc0f6f
32 changed files with 413 additions and 467 deletions

5
package-lock.json generated
View file

@ -14413,6 +14413,11 @@
"resolved": "https://registry.npmjs.org/inversify/-/inversify-5.0.5.tgz", "resolved": "https://registry.npmjs.org/inversify/-/inversify-5.0.5.tgz",
"integrity": "sha512-60QsfPz8NAU/GZqXu8hJ+BhNf/C/c+Hp0eDc6XMIJTxBiP36AQyyQKpBkOVTLWBFDQWYVHpbbEuIsHu9dLuJDA==" "integrity": "sha512-60QsfPz8NAU/GZqXu8hJ+BhNf/C/c+Hp0eDc6XMIJTxBiP36AQyyQKpBkOVTLWBFDQWYVHpbbEuIsHu9dLuJDA=="
}, },
"inversify-inject-decorators": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/inversify-inject-decorators/-/inversify-inject-decorators-3.1.0.tgz",
"integrity": "sha512-/seBlVp5bXrLQS3DpKEmlgeZL6C7Tf/QITd+IMQrbBBGuCbxb7k3hRAWu9XSreNpFzLgSboz3sClLSEmGwHphw=="
},
"ip": { "ip": {
"version": "1.1.5", "version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",

View file

@ -53,20 +53,12 @@
"config": "./webpack.renderer.config.js", "config": "./webpack.renderer.config.js",
"entryPoints": [ "entryPoints": [
{ {
"html": "./src/index.html", "html": "./src/renderer.html",
"js": "./src/index.tsx", "js": "./src/renderer.tsx",
"preload": { "preload": {
"js": "./src/services/preload/main.ts" "js": "./src/services/preload/index.ts"
}, },
"name": "main_window" "name": "main_window"
},
{
"html": "./src/index.html",
"js": "./src/constants/hunspell-languages.ts",
"preload": {
"js": "./src/services/preload/view.ts"
},
"name": "web_view"
} }
] ]
} }
@ -92,6 +84,7 @@
"i18next-electron-fs-backend": "^1.3.5", "i18next-electron-fs-backend": "^1.3.5",
"i18next-fs-backend": "^1.0.7", "i18next-fs-backend": "^1.0.7",
"inversify": "^5.0.5", "inversify": "^5.0.5",
"inversify-inject-decorators": "^3.1.0",
"is-url": "1.2.4", "is-url": "1.2.4",
"jimp": "0.16.1", "jimp": "0.16.1",
"lodash": "4.17.20", "lodash": "4.17.20",

352
src/main.ts Executable file
View file

@ -0,0 +1,352 @@
import 'reflect-metadata';
import { ipcMain, nativeTheme, protocol, session, powerMonitor, app } from 'electron';
import isDev from 'electron-is-dev';
import fs from 'fs';
import { delay } from 'bluebird';
import settings from 'electron-settings';
import { autoUpdater } from 'electron-updater';
import loadListeners from './services/listeners';
import { container } from './services/container';
import * as openUrlWithWindow from './services/windows/open-url-with';
import createMenu from './services/libs/create-menu';
import extractHostname from './services/libs/extract-hostname';
import sendToAllWindows from './services/libs/send-to-all-windows';
import { stopWatchAllWiki } from './services/libs/wiki/watch-wiki';
import { stopAllWiki } from './services/libs/wiki/wiki-worker-mamager';
import { addView, reloadViewsDarkReader } from './services/libs/views';
import { getWorkspaces, setWorkspace } from './services/libs/workspaces';
import { logger } from './services/libs/log';
import { commitAndSync } from './services/libs/git';
import { clearMainBindings } from './services/libs/i18next-electron-fs-backend';
import MAILTO_URLS from './services/constants/mailto-urls';
import './services/libs/updater';
import serviceIdentifier from '@/services/serviceIdentifier';
import { Window } from '@/services/windows';
import { WindowNames } from '@/services/windows/WindowProperties';
import { Preference } from '@/services/preferences';
const gotTheLock = app.requestSingleInstanceLock();
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace NodeJS {
interface Global {
attachToMenubar: any;
sidebar: any;
titleBar: any;
navigationBar: any;
updaterObj: any;
MAILTO_URLS: any;
}
}
}
container.bind<Window>(serviceIdentifier.Window).to(Window);
container.bind<Preference>(serviceIdentifier.Preference).to(Preference);
const windows = container.resolve(Window);
const preferences = container.resolve(Preference);
app.on('second-instance', () => {
// Someone tried to run a second instance, we should focus our window.
const mainWindow = windows.get(WindowNames.main);
if (mainWindow !== undefined) {
if (mainWindow.isMinimized()) mainWindow.restore();
mainWindow.focus();
}
});
if (!gotTheLock) {
logger.info('Quitting dut to we only allow one instance to run.');
console.info('Quitting dut to we only allow one instance to run.');
app.quit();
} else {
// make sure "Settings" file exists
// if not, ignore this chunk of code
// as using electron-settings before app.on('ready') and "Settings" is created
// would return error
// https://github.com/nathanbuchar/electron-settings/issues/111
if (fs.existsSync(settings.file())) {
const useHardwareAcceleration = preferences.get('useHardwareAcceleration');
if (!useHardwareAcceleration) {
app.disableHardwareAcceleration();
}
const ignoreCertificateErrors = preferences.get('ignoreCertificateErrors');
if (ignoreCertificateErrors) {
// https://www.electronjs.org/docs/api/command-line-switches
app.commandLine.appendSwitch('ignore-certificate-errors');
}
}
let commonInitFinished = false;
/** mock app.whenReady */
const customCommonInitFinishedEvent = 'common-init-finished';
ipcMain.once(customCommonInitFinishedEvent, () => {
commonInitFinished = true;
});
/**
* Make sure some logic only run after window and services are truly ready
*/
const whenCommonInitFinished = async (): Promise<void> => {
if (commonInitFinished) return await Promise.resolve();
return await new Promise((resolve) => {
ipcMain.once(customCommonInitFinishedEvent, () => {
commonInitFinished = true;
resolve();
});
});
};
protocol.registerSchemesAsPrivileged([
{ scheme: 'http', privileges: { standard: true } },
{ scheme: 'https', privileges: { standard: true } },
{ scheme: 'mailto', privileges: { standard: true } },
]);
loadListeners();
const commonInit = async (): Promise<void> => {
// eslint-disable-next-line promise/catch-or-return
return await app
.whenReady()
.then(
() =>
isDev &&
protocol.registerFileProtocol('file', (request, callback) => {
// TODO: this might be useless after use electron-forge, this is for loading html file after bundle, forge handle this now
const pathname = decodeURIComponent(request.url.replace('file:///', ''));
callback(pathname);
}),
)
.then(async () => await windows.open(WindowNames.main))
.then(async () => {
const { hibernateUnusedWorkspacesAtLaunch, proxyBypassRules, proxyPacScript, proxyRules, proxyType, themeSource } = preferences.getPreferences();
// configure proxy for default session
if (proxyType === 'rules') {
await session.defaultSession.setProxy({
proxyRules,
proxyBypassRules,
});
} else if (proxyType === 'pacScript') {
await session.defaultSession.setProxy({
// FIXME: 'proxyPacScript' does not exist in type 'Config'
// proxyPacScript,
proxyBypassRules,
});
}
nativeTheme.themeSource = themeSource;
createMenu();
nativeTheme.addListener('updated', () => {
sendToAllWindows('native-theme-updated');
reloadViewsDarkReader();
});
const workspaceObjects = getWorkspaces();
Object.keys(workspaceObjects).forEach(
async (id: string): Promise<void> => {
const workspace = workspaceObjects[id];
if ((hibernateUnusedWorkspacesAtLaunch || workspace.hibernateWhenUnused) && !workspace.active) {
if (!workspace.hibernated) {
setWorkspace(workspace.id, { hibernated: true });
}
return;
}
const mainWindow = windows.get(WindowNames.main);
await addView(mainWindow, workspace);
try {
const userInfo = preferences.get('github-user-info');
const { name: wikiPath, gitUrl: githubRepoUrl, isSubWiki } = workspace;
// wait for main wiki's watch-fs plugin to be fully initialized
// and also wait for wiki BrowserView to be able to receive command
// eslint-disable-next-line global-require
const { getWorkspaceMeta } = require('./libs/workspace-metas');
let meta = getWorkspaceMeta(id);
if (!isSubWiki) {
while (!meta.didFailLoad && !meta.isLoading) {
// eslint-disable-next-line no-await-in-loop
await delay(500);
meta = getWorkspaceMeta(id);
}
}
if (!isSubWiki && !meta.didFailLoad) {
await commitAndSync(wikiPath, githubRepoUrl, userInfo);
}
} catch {
logger.warning(`Can't sync at wikiStartup()`);
}
},
);
ipcMain.emit('request-update-pause-notifications-info');
})
.then(() => {
// Fix webview is not resized automatically
// when window is maximized on Linux
// https://github.com/atomery/webcatalog/issues/561
// run it here not in mainWindow.createAsync()
// because if the `mainWindow` is maximized or minimized
// before the workspaces's BrowserView fully loaded
// error will occur
// see https://github.com/atomery/webcatalog/issues/637
// eslint-disable-next-line promise/always-return
if (process.platform === 'linux') {
const mainWindow = windows.get(WindowNames.main);
if (mainWindow !== undefined) {
const handleMaximize = (): void => {
// getContentSize is not updated immediately
// try once after 0.2s (for fast computer), another one after 1s (to be sure)
setTimeout(() => {
ipcMain.emit('request-realign-active-workspace');
}, 200);
setTimeout(() => {
ipcMain.emit('request-realign-active-workspace');
}, 1000);
};
mainWindow.on('maximize', handleMaximize);
mainWindow.on('unmaximize', handleMaximize);
}
}
})
// eslint-disable-next-line promise/always-return
.then(() => {
// trigger whenTrulyReady
ipcMain.emit(customCommonInitFinishedEvent);
});
};
app.on('ready', () => {
const { allowPrerelease, attachToMenubar, sidebar, titleBar, navigationBar } = preferences.getPreferences();
// TODO: use IPC to get these config
global.attachToMenubar = attachToMenubar;
global.sidebar = sidebar;
global.titleBar = titleBar;
global.navigationBar = navigationBar;
global.MAILTO_URLS = MAILTO_URLS;
autoUpdater.allowPrerelease = allowPrerelease;
autoUpdater.logger = logger;
whenCommonInitFinished()
// eslint-disable-next-line promise/always-return
.then(() => {
ipcMain.emit('request-check-for-updates', undefined, true);
})
.catch((error) => console.error(error));
powerMonitor.on('shutdown', () => {
app.quit();
});
void commonInit();
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
const mainWindow = windows.get(WindowNames.main);
if (mainWindow === undefined) {
void commonInit();
} else {
mainWindow.show();
}
});
app.on(
'open-url',
// eslint-disable-next-line @typescript-eslint/no-misused-promises
async (event, url): Promise<void> => {
event.preventDefault();
await whenCommonInitFinished();
// focus on window
const mainWindow = windows.get(WindowNames.main);
if (mainWindow === undefined) {
await commonInit();
} else {
mainWindow.show();
}
const workspaces: any[] = Object.values(getWorkspaces());
if (workspaces.length === 0) return;
// handle mailto:
if (url.startsWith('mailto:')) {
const mailtoWorkspaces: any[] = workspaces.filter((workspace: any) => {
const hostName = extractHostname(workspace.homeUrl);
return hostName !== undefined && hostName in MAILTO_URLS;
});
// pick automatically if there's only one choice
if (mailtoWorkspaces.length === 0) {
ipcMain.emit('request-show-message-box', undefined, 'None of your workspaces supports composing email messages.', 'error');
return;
}
if (mailtoWorkspaces.length === 1) {
const hostName = extractHostname(mailtoWorkspaces[0].homeUrl);
if (hostName !== undefined) {
const mailtoUrl = MAILTO_URLS[hostName];
const u = mailtoUrl.replace('%s', url);
ipcMain.emit('request-load-url', undefined, u, mailtoWorkspaces[0].id);
}
return;
}
return openUrlWithWindow.show(url);
}
// handle https/http
// pick automically if there's only one choice
if (workspaces.length === 1) {
ipcMain.emit('request-load-url', undefined, url, workspaces[0].id);
return;
}
return openUrlWithWindow.show(url);
},
);
app.on(
'before-quit',
// eslint-disable-next-line @typescript-eslint/no-misused-promises
async (): Promise<void> => {
logger.info('Quitting worker threads and watcher.');
await Promise.all([stopAllWiki(), stopWatchAllWiki()]);
logger.info('Worker threads and watchers all terminated.');
logger.info('Quitting I18N server.');
clearMainBindings(ipcMain);
logger.info('Quitted I18N server.');
// https://github.com/atom/electron/issues/444#issuecomment-76492576
if (process.platform === 'darwin') {
const mainWindow = windows.get(WindowNames.main);
if (mainWindow !== undefined) {
logger.info('App force quit on MacOS');
// FIXME: set custom property
// @ts-expect-error don't set custom property to window
mainWindow.forceClose = true;
}
}
app.exit(0);
},
);
app.on('quit', () => {
logger.info('App quit');
});
}

View file

@ -28,10 +28,9 @@ const rawMailtoUrls = [
}, },
]; ];
const MAILTO_URLS = {}; const MAILTO_URLS: Record<string, string> = {};
rawMailtoUrls.forEach((item) => { rawMailtoUrls.forEach((item) => {
item.hostnames.forEach((hostname) => { item.hostnames.forEach((hostname) => {
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
MAILTO_URLS[hostname] = item.mailtoUrl; MAILTO_URLS[hostname] = item.mailtoUrl;
}); });
}); });

View file

@ -0,0 +1,2 @@
import { Container } from 'inversify';
export const container = new Container();

View file

@ -1,316 +0,0 @@
import { ipcMain, nativeTheme, protocol, session, powerMonitor, app } from 'electron';
import isDev from 'electron-is-dev';
import fs from 'fs';
// @ts-expect-error ts-migrate(2529) FIXME: Duplicate identifier 'Promise'. Compiler reserves ... Remove this comment to see the full error message
import Promise from 'bluebird';
import settings from 'electron-settings';
import { autoUpdater } from 'electron-updater';
import loadListeners from './listeners';
import * as mainWindow from './windows/main';
import * as openUrlWithWindow from './windows/open-url-with';
import createMenu from './libs/create-menu';
import extractHostname from './libs/extract-hostname';
import sendToAllWindows from './libs/send-to-all-windows';
import { stopWatchAllWiki } from './libs/wiki/watch-wiki';
import { stopAllWiki } from './libs/wiki/wiki-worker-mamager';
import { addView, reloadViewsDarkReader } from './libs/views';
import { getPreference, getPreferences } from './libs/preferences';
import { getWorkspaces, setWorkspace } from './libs/workspaces';
import { logger } from './libs/log';
import { commitAndSync } from './libs/git';
import { clearMainBindings } from './libs/i18next-electron-fs-backend';
import MAILTO_URLS from './constants/mailto-urls';
import './libs/updater';
const gotTheLock = app.requestSingleInstanceLock();
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace NodeJS {
interface Global {
attachToMenubar: any;
sidebar: any;
titleBar: any;
navigationBar: any;
updaterObj: any;
MAILTO_URLS: any;
}
}
}
app.on('second-instance', () => {
// Someone tried to run a second instance, we should focus our window.
const win = mainWindow.get();
if (win !== undefined) {
if (win.isMinimized()) win.restore();
win.focus();
}
});
if (!gotTheLock) {
logger.info('Quitting dut to we only allow one instance to run.');
console.info('Quitting dut to we only allow one instance to run.');
app.quit();
} else {
// make sure "Settings" file exists
// if not, ignore this chunk of code
// as using electron-settings before app.on('ready') and "Settings" is created
// would return error
// https://github.com/nathanbuchar/electron-settings/issues/111
if (fs.existsSync(settings.file())) {
const useHardwareAcceleration = getPreference('useHardwareAcceleration');
if (!useHardwareAcceleration) {
app.disableHardwareAcceleration();
}
const ignoreCertificateErrors = getPreference('ignoreCertificateErrors');
if (ignoreCertificateErrors) {
// https://www.electronjs.org/docs/api/command-line-switches
app.commandLine.appendSwitch('ignore-certificate-errors');
}
}
// mock app.whenReady
let trulyReady = false;
ipcMain.once('truly-ready', () => {
trulyReady = true;
});
const whenTrulyReady = () => {
if (trulyReady) return Promise.resolve();
return new Promise((resolve) => {
ipcMain.once('truly-ready', () => {
trulyReady = true;
resolve();
});
});
};
protocol.registerSchemesAsPrivileged([
{ scheme: 'http', privileges: { standard: true } },
{ scheme: 'https', privileges: { standard: true } },
{ scheme: 'mailto', privileges: { standard: true } },
]);
loadListeners();
const commonInit = async (): Promise<void> => {
// eslint-disable-next-line promise/catch-or-return
return await app
.whenReady()
.then(
() =>
isDev &&
protocol.registerFileProtocol('file', (request, callback) => {
const pathname = decodeURIComponent(request.url.replace('file:///', ''));
callback(pathname);
}),
)
.then(async () => await mainWindow.createAsync())
.then(() => {
const { hibernateUnusedWorkspacesAtLaunch, proxyBypassRules, proxyPacScript, proxyRules, proxyType, themeSource } = getPreferences();
// configure proxy for default session
if (proxyType === 'rules') {
session.defaultSession.setProxy({
proxyRules,
proxyBypassRules,
});
} else if (proxyType === 'pacScript') {
session.defaultSession.setProxy(({
proxyPacScript,
proxyBypassRules,
} as any) as Electron.Config);
}
nativeTheme.themeSource = themeSource;
createMenu();
nativeTheme.addListener('updated', () => {
sendToAllWindows('native-theme-updated');
reloadViewsDarkReader();
});
const workspaceObjects = getWorkspaces();
Object.keys(workspaceObjects).forEach(async (id) => {
const workspace = workspaceObjects[id];
if ((hibernateUnusedWorkspacesAtLaunch || workspace.hibernateWhenUnused) && !workspace.active) {
if (!workspace.hibernated) {
setWorkspace(workspace.id, { hibernated: true });
}
return;
}
await addView(mainWindow.get(), workspace);
try {
const userInfo = getPreference('github-user-info');
const { name: wikiPath, gitUrl: githubRepoUrl, isSubWiki } = workspace;
// wait for main wiki's watch-fs plugin to be fully initialized
// and also wait for wiki BrowserView to be able to receive command
// eslint-disable-next-line global-require
const { getWorkspaceMeta } = require('./libs/workspace-metas');
let meta = getWorkspaceMeta(id);
if (!isSubWiki) {
while (!meta.didFailLoad && !meta.isLoading) {
// eslint-disable-next-line no-await-in-loop
await Promise.delay(500);
meta = getWorkspaceMeta(id);
}
}
if (!isSubWiki && !meta.didFailLoad) {
await commitAndSync(wikiPath, githubRepoUrl, userInfo);
}
} catch {
logger.warning(`Can't sync at wikiStartup()`);
}
});
ipcMain.emit('request-update-pause-notifications-info');
})
.then(() => {
// Fix webview is not resized automatically
// when window is maximized on Linux
// https://github.com/atomery/webcatalog/issues/561
// run it here not in mainWindow.createAsync()
// because if the `mainWindow` is maximized or minimized
// before the workspaces's BrowserView fully loaded
// error will occur
// see https://github.com/atomery/webcatalog/issues/637
// eslint-disable-next-line promise/always-return
if (process.platform === 'linux') {
const win = mainWindow.get();
if (win) {
const handleMaximize = () => {
// getContentSize is not updated immediately
// try once after 0.2s (for fast computer), another one after 1s (to be sure)
setTimeout(() => {
ipcMain.emit('request-realign-active-workspace');
}, 200);
setTimeout(() => {
ipcMain.emit('request-realign-active-workspace');
}, 1000);
};
win.on('maximize', handleMaximize);
win.on('unmaximize', handleMaximize);
}
}
})
// eslint-disable-next-line promise/always-return
.then(() => {
// trigger whenTrulyReady
ipcMain.emit('truly-ready');
});
};
app.on('ready', () => {
const { allowPrerelease, attachToMenubar, sidebar, titleBar, navigationBar } = getPreferences();
// TODO: use IPC to get these config
global.attachToMenubar = attachToMenubar;
global.sidebar = sidebar;
global.titleBar = titleBar;
global.navigationBar = navigationBar;
global.MAILTO_URLS = MAILTO_URLS;
autoUpdater.allowPrerelease = allowPrerelease;
autoUpdater.logger = logger;
whenTrulyReady()
// eslint-disable-next-line promise/always-return
.then(() => {
ipcMain.emit('request-check-for-updates', null, true);
})
.catch((error) => console.error(error));
powerMonitor.on('shutdown', () => {
app.quit();
});
commonInit();
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
const win = mainWindow.get();
if (win == undefined) {
commonInit();
} else {
mainWindow.show();
}
});
app.on('open-url', (e, url) => {
e.preventDefault();
whenTrulyReady().then(() => {
// focus on window
mainWindow.show();
const workspaces: any[] = Object.values(getWorkspaces());
if (workspaces.length === 0) return null;
// handle mailto:
if (url.startsWith('mailto:')) {
const mailtoWorkspaces: any[] = workspaces.filter((workspace: any) => extractHostname(workspace.homeUrl) in MAILTO_URLS);
// pick automically if there's only one choice
if (mailtoWorkspaces.length === 0) {
ipcMain.emit('request-show-message-box', null, 'None of your workspaces supports composing email messages.', 'error');
return null;
}
if (mailtoWorkspaces.length === 1) {
const mailtoUrl = (MAILTO_URLS as any)[extractHostname(mailtoWorkspaces[0].homeUrl)];
const u = mailtoUrl.replace('%s', url);
ipcMain.emit('request-load-url', null, u, mailtoWorkspaces[0].id);
return null;
}
return openUrlWithWindow.show(url);
}
// handle https/http
// pick automically if there's only one choice
if (workspaces.length === 1) {
ipcMain.emit('request-load-url', null, url, workspaces[0].id);
return null;
}
return openUrlWithWindow.show(url);
});
});
app.on('before-quit', async (event) => {
logger.info('Quitting worker threads and watcher.');
await Promise.all([stopAllWiki(), stopWatchAllWiki()]);
logger.info('Worker threads and watchers all terminated.');
logger.info('Quitting I18N server.');
clearMainBindings(ipcMain);
logger.info('Quitted I18N server.');
// https://github.com/atom/electron/issues/444#issuecomment-76492576
if (process.platform === 'darwin') {
const win = mainWindow.get();
if (win) {
logger.info('App force quit on MacOS');
// FIXME: set custom property
(win as any).forceClose = true;
}
}
app.exit(0);
});
app.on('quit', async () => {
logger.info('App quit');
});
}

View file

@ -1,5 +1,4 @@
/* eslint-disable prefer-destructuring */ const extractHostname = (url: string): string | undefined => {
const extractHostname = (url: any) => {
try { try {
let hostname = url.trim(); let hostname = url.trim();
@ -17,7 +16,7 @@ const extractHostname = (url: any) => {
return hostname; return hostname;
} catch { } catch {
return null; // return undefined;
} }
}; };

View file

@ -10,8 +10,7 @@ import { setWorkspaceMeta, getWorkspaceMetas, getWorkspaceMeta } from './workspa
import sendToAllWindows from './send-to-all-windows'; import sendToAllWindows from './send-to-all-windows';
import getViewBounds from './get-view-bounds'; import getViewBounds from './get-view-bounds';
declare const WEB_VIEW_WEBPACK_ENTRY: string; declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string;
declare const WEB_VIEW_PRELOAD_WEBPACK_ENTRY: string;
const views = {}; const views = {};
let shouldMuteAudio: any; let shouldMuteAudio: any;
@ -134,7 +133,7 @@ export const addView = async (browserWindow: any, workspace: any) => {
contextIsolation: true, contextIsolation: true,
enableRemoteModule: true, enableRemoteModule: true,
session: ses, session: ses,
preload: WEB_VIEW_PRELOAD_WEBPACK_ENTRY, preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
}; };
const view = new BrowserView({ const view = new BrowserView({
webPreferences: sharedWebPreferences, webPreferences: sharedWebPreferences,

View file

@ -1,12 +1,16 @@
import { injectable, inject } from 'inversify'; import { injectable, inject } from 'inversify';
import { app, App, nativeTheme, ipcMain, remote } from 'electron'; import getDecorators from 'inversify-inject-decorators';
import { app, App, remote } from 'electron';
import path from 'path'; import path from 'path';
import semver from 'semver'; import semver from 'semver';
import settings from 'electron-settings'; import settings from 'electron-settings';
import serviceIdentifiers from '@services/serviceIdentifier'; import serviceIdentifiers from '@services/serviceIdentifier';
import { Window } from '@/services/window'; import { Window } from '@/services/windows';
import { PreferenceChannel } from '@/services/channels'; import { PreferenceChannel } from '@/services/channels';
import { container } from '@/services/container';
const { lazyInject } = getDecorators(container);
/** get path, note that if use this from the preload script, app will be undefined, so have to use remote.app here */ /** get path, note that if use this from the preload script, app will be undefined, so have to use remote.app here */
const getDefaultDownloadsPath = (): string => { const getDefaultDownloadsPath = (): string => {
@ -67,7 +71,7 @@ const defaultPreferences = {
spellcheckLanguages: ['en-US'], spellcheckLanguages: ['en-US'],
swipeToNavigate: true, swipeToNavigate: true,
syncDebounceInterval: 1000 * 60 * 30, syncDebounceInterval: 1000 * 60 * 30,
themeSource: 'system', themeSource: 'system' as 'system' | 'light' | 'dark',
titleBar: true, titleBar: true,
unreadCountBadge: true, unreadCountBadge: true,
useHardwareAcceleration: true, useHardwareAcceleration: true,
@ -76,13 +80,12 @@ export type IPreferences = typeof defaultPreferences;
@injectable() @injectable()
export class Preference { export class Preference {
windowService: Window; @lazyInject(serviceIdentifiers.Window) private readonly windowService!: Window;
cachedPreferences: IPreferences; cachedPreferences: IPreferences;
readonly version = '2018.2'; readonly version = '2018.2';
constructor(@inject(serviceIdentifiers.Window) windowService: Window) { constructor() {
this.windowService = windowService;
this.cachedPreferences = this.getInitPreferencesForCache(); this.cachedPreferences = this.getInitPreferencesForCache();
} }

View file

@ -1,8 +0,0 @@
import { contextBridge } from 'electron';
import path from 'path';
import './common/simple-context-menu';
import './common/require-nodejs';
import './common/i18n';
contextBridge.exposeInMainWorld('meta', { mode: 'about', iconPath: path.join(__dirname, '..', 'icon@5x.png') });

View file

@ -1,8 +0,0 @@
import { contextBridge } from 'electron';
import './common/i18n';
import './common/require-nodejs';
import './common/simple-context-menu';
import './common/authing-postmessage';
contextBridge.exposeInMainWorld('meta', { mode: 'add-workspace' });

View file

@ -1,7 +0,0 @@
import './common/simple-context-menu';
import './common/require-nodejs';
import './common/i18n';
import { contextBridge } from 'electron';
contextBridge.exposeInMainWorld('meta', { mode: 'auth' });

View file

@ -1,7 +0,0 @@
import './common/simple-context-menu';
import './common/require-nodejs';
import './common/i18n';
import { contextBridge } from 'electron';
contextBridge.exposeInMainWorld('meta', { mode: 'code-injection' });

View file

@ -1,7 +0,0 @@
import './common/simple-context-menu';
import './common/require-nodejs';
import './common/i18n';
import { contextBridge } from 'electron';
contextBridge.exposeInMainWorld('meta', { mode: 'custom-user-agent' });

View file

@ -1,7 +0,0 @@
import './common/simple-context-menu';
import './common/require-nodejs';
import './common/i18n';
import { contextBridge } from 'electron';
contextBridge.exposeInMainWorld('meta', { mode: 'display-media' });

View file

@ -1,7 +0,0 @@
import './common/simple-context-menu';
import './common/require-nodejs';
import './common/i18n';
import { contextBridge } from 'electron';
contextBridge.exposeInMainWorld('meta', { mode: 'edit-workspace' });

View file

@ -1,7 +0,0 @@
import './common/simple-context-menu';
import './common/require-nodejs';
import './common/i18n';
import { contextBridge } from 'electron';
contextBridge.exposeInMainWorld('meta', { mode: 'go-to-url' });

View file

@ -4,10 +4,15 @@ import './common/i18n';
import './common/require-nodejs'; import './common/require-nodejs';
import './common/simple-context-menu'; import './common/simple-context-menu';
import './common/authing-postmessage'; import './common/authing-postmessage';
import { WindowNames } from '@/services/windows/WindowProperties';
const windowName = process.argv[0]; const windowName = process.argv[0] as WindowNames;
// DEBUG: console // DEBUG: console
console.log(`windowName`, windowName); console.log(`windowName`, windowName);
contextBridge.exposeInMainWorld('meta', { windowName }); contextBridge.exposeInMainWorld('meta', { windowName });
if (windowName === WindowNames.view) {
void import('./view');
}

View file

@ -1,7 +0,0 @@
import './common/simple-context-menu';
import './common/require-nodejs';
import './common/i18n';
import { contextBridge } from 'electron';
contextBridge.exposeInMainWorld('meta', { mode: 'main' });

View file

@ -1,7 +0,0 @@
import './common/simple-context-menu';
import './common/require-nodejs';
import './common/i18n';
import { contextBridge } from 'electron';
contextBridge.exposeInMainWorld('meta', { mode: 'menubar' });

View file

@ -1,7 +0,0 @@
import './common/simple-context-menu';
import './common/require-nodejs';
import './common/i18n';
import { contextBridge } from 'electron';
contextBridge.exposeInMainWorld('meta', { mode: 'notifications' });

View file

@ -1,7 +0,0 @@
import './common/simple-context-menu';
import './common/require-nodejs';
import './common/i18n';
import { contextBridge } from 'electron';
contextBridge.exposeInMainWorld('meta', { mode: 'open-url-with' });

View file

@ -1,7 +0,0 @@
import './common/simple-context-menu';
import './common/require-nodejs';
import './common/i18n';
import './common/authing-postmessage';
import { contextBridge } from 'electron';
contextBridge.exposeInMainWorld('meta', { mode: 'preferences' });

View file

@ -1,7 +0,0 @@
import './common/simple-context-menu';
import './common/require-nodejs';
import './common/i18n';
import { contextBridge } from 'electron';
contextBridge.exposeInMainWorld('meta', { mode: 'proxy' });

View file

@ -1,7 +0,0 @@
import './common/simple-context-menu';
import './common/require-nodejs';
import './common/i18n';
import { contextBridge } from 'electron';
contextBridge.exposeInMainWorld('meta', { mode: 'spellcheck-languages' });

View file

@ -0,0 +1,23 @@
export enum WindowNames {
main = 'main',
view = 'view',
addWorkspace = 'addWorkspace',
}
/**
* Width height of windows
*/
export const windowDimension: Record<WindowNames, { width?: number; height?: number }> = {
[WindowNames.main]: {
width: 1200,
height: 768,
},
[WindowNames.view]: {
width: undefined,
height: undefined,
},
[WindowNames.addWorkspace]: {
width: 600,
height: 800,
},
};

View file

@ -5,28 +5,18 @@ import { injectable, inject } from 'inversify';
import serviceIdentifiers from '@services/serviceIdentifier'; import serviceIdentifiers from '@services/serviceIdentifier';
import { Preference } from '@services/preferences'; import { Preference } from '@services/preferences';
import { Channels } from '@/services/channels'; import { Channels } from '@/services/channels';
import { WindowNames, windowDimension } from '@/services/windows/WindowProperties';
export enum WindowNames {
main = 'main',
view = 'view',
addWorkspace = 'addWorkspace',
}
declare const MAIN_WINDOW_WEBPACK_ENTRY: string; declare const MAIN_WINDOW_WEBPACK_ENTRY: string;
declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string; declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string;
@injectable() @injectable()
export class Window { export class Window {
preferenceService: Preference;
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
private windows = {} as Record<WindowNames, BrowserWindow | undefined>; private windows = {} as Record<WindowNames, BrowserWindow | undefined>;
constructor(@inject(serviceIdentifiers.Preference) preferenceService: Preference) { constructor(@inject(serviceIdentifiers.Preference) private readonly preferenceService: Preference) {}
this.preferenceService = preferenceService;
}
private get(name: WindowNames): BrowserWindow | undefined { public get(name: WindowNames): BrowserWindow | undefined {
return this.windows[name]; return this.windows[name];
} }
@ -34,8 +24,7 @@ export class Window {
const attachToMenubar: boolean = this.preferenceService.get('attachToMenubar'); const attachToMenubar: boolean = this.preferenceService.get('attachToMenubar');
const newWindow = new BrowserWindow({ const newWindow = new BrowserWindow({
width: 600, ...windowDimension[name],
height: 800,
resizable: false, resizable: false,
maximizable: false, maximizable: false,
minimizable: false, minimizable: false,

View file

@ -7,8 +7,6 @@ import { REACT_PATH, isDev as isDevelopment } from '../constants/paths';
import { getPreference } from '../libs/preferences'; import { getPreference } from '../libs/preferences';
import formatBytes from '../libs/format-bytes'; import formatBytes from '../libs/format-bytes';
declare const MAIN_WINDOW_WEBPACK_ENTRY: string;
declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string;
let win: BrowserWindow | undefined; let win: BrowserWindow | undefined;
let menuBar: Menubar; let menuBar: Menubar;

View file

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('path'); const path = require('path');
const fs = require('fs-extra'); const fs = require('fs-extra');

View file

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const { webpackAlias } = require('./webpack.alias'); const { webpackAlias } = require('./webpack.alias');
module.exports = { module.exports = {
@ -5,7 +6,7 @@ module.exports = {
* This is the main entry point for your application, it's the first file * This is the main entry point for your application, it's the first file
* that runs in the main process. * that runs in the main process.
*/ */
entry: './src/services/electron.ts', entry: './src/main.ts',
// Put your normal webpack config below here // Put your normal webpack config below here
module: { module: {
rules: require('./webpack.rules'), rules: require('./webpack.rules'),