diff --git a/package-lock.json b/package-lock.json index efad4656..3f1a69f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14413,6 +14413,11 @@ "resolved": "https://registry.npmjs.org/inversify/-/inversify-5.0.5.tgz", "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": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", diff --git a/package.json b/package.json index 9aabe5ee..4fc4e539 100755 --- a/package.json +++ b/package.json @@ -53,20 +53,12 @@ "config": "./webpack.renderer.config.js", "entryPoints": [ { - "html": "./src/index.html", - "js": "./src/index.tsx", + "html": "./src/renderer.html", + "js": "./src/renderer.tsx", "preload": { - "js": "./src/services/preload/main.ts" + "js": "./src/services/preload/index.ts" }, "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-fs-backend": "^1.0.7", "inversify": "^5.0.5", + "inversify-inject-decorators": "^3.1.0", "is-url": "1.2.4", "jimp": "0.16.1", "lodash": "4.17.20", diff --git a/src/main.ts b/src/main.ts new file mode 100755 index 00000000..5a1ed5fa --- /dev/null +++ b/src/main.ts @@ -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(serviceIdentifier.Window).to(Window); +container.bind(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 => { + 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 => { + // 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 => { + 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 => { + 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 => { + 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'); + }); +} diff --git a/src/index.html b/src/renderer.html similarity index 100% rename from src/index.html rename to src/renderer.html diff --git a/src/index.tsx b/src/renderer.tsx similarity index 100% rename from src/index.tsx rename to src/renderer.tsx diff --git a/src/services/constants/mailto-urls.ts b/src/services/constants/mailto-urls.ts index e37d4e64..6c303d6e 100644 --- a/src/services/constants/mailto-urls.ts +++ b/src/services/constants/mailto-urls.ts @@ -28,10 +28,9 @@ const rawMailtoUrls = [ }, ]; -const MAILTO_URLS = {}; +const MAILTO_URLS: Record = {}; rawMailtoUrls.forEach((item) => { 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; }); }); diff --git a/src/services/container.ts b/src/services/container.ts new file mode 100644 index 00000000..89f074c9 --- /dev/null +++ b/src/services/container.ts @@ -0,0 +1,2 @@ +import { Container } from 'inversify'; +export const container = new Container(); diff --git a/src/services/electron.ts b/src/services/electron.ts deleted file mode 100755 index 0da3d1d2..00000000 --- a/src/services/electron.ts +++ /dev/null @@ -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 => { - // 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'); - }); -} diff --git a/src/services/libs/extract-hostname.ts b/src/services/libs/extract-hostname.ts index 9d91c9df..ad1c80fc 100644 --- a/src/services/libs/extract-hostname.ts +++ b/src/services/libs/extract-hostname.ts @@ -1,5 +1,4 @@ -/* eslint-disable prefer-destructuring */ -const extractHostname = (url: any) => { +const extractHostname = (url: string): string | undefined => { try { let hostname = url.trim(); @@ -17,7 +16,7 @@ const extractHostname = (url: any) => { return hostname; } catch { - return null; + // return undefined; } }; diff --git a/src/services/libs/views.ts b/src/services/libs/views.ts index 791e01f5..e5f105e3 100644 --- a/src/services/libs/views.ts +++ b/src/services/libs/views.ts @@ -10,8 +10,7 @@ import { setWorkspaceMeta, getWorkspaceMetas, getWorkspaceMeta } from './workspa import sendToAllWindows from './send-to-all-windows'; import getViewBounds from './get-view-bounds'; -declare const WEB_VIEW_WEBPACK_ENTRY: string; -declare const WEB_VIEW_PRELOAD_WEBPACK_ENTRY: string; +declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string; const views = {}; let shouldMuteAudio: any; @@ -134,7 +133,7 @@ export const addView = async (browserWindow: any, workspace: any) => { contextIsolation: true, enableRemoteModule: true, session: ses, - preload: WEB_VIEW_PRELOAD_WEBPACK_ENTRY, + preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY, }; const view = new BrowserView({ webPreferences: sharedWebPreferences, diff --git a/src/services/preferences.ts b/src/services/preferences.ts index 364d519f..7362d36d 100755 --- a/src/services/preferences.ts +++ b/src/services/preferences.ts @@ -1,12 +1,16 @@ 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 semver from 'semver'; import settings from 'electron-settings'; import serviceIdentifiers from '@services/serviceIdentifier'; -import { Window } from '@/services/window'; +import { Window } from '@/services/windows'; 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 */ const getDefaultDownloadsPath = (): string => { @@ -67,7 +71,7 @@ const defaultPreferences = { spellcheckLanguages: ['en-US'], swipeToNavigate: true, syncDebounceInterval: 1000 * 60 * 30, - themeSource: 'system', + themeSource: 'system' as 'system' | 'light' | 'dark', titleBar: true, unreadCountBadge: true, useHardwareAcceleration: true, @@ -76,13 +80,12 @@ export type IPreferences = typeof defaultPreferences; @injectable() export class Preference { - windowService: Window; + @lazyInject(serviceIdentifiers.Window) private readonly windowService!: Window; cachedPreferences: IPreferences; readonly version = '2018.2'; - constructor(@inject(serviceIdentifiers.Window) windowService: Window) { - this.windowService = windowService; + constructor() { this.cachedPreferences = this.getInitPreferencesForCache(); } diff --git a/src/services/preload/about.ts b/src/services/preload/about.ts deleted file mode 100644 index 85c7d58d..00000000 --- a/src/services/preload/about.ts +++ /dev/null @@ -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') }); diff --git a/src/services/preload/add-workspace.ts b/src/services/preload/add-workspace.ts deleted file mode 100644 index 5f7c3f10..00000000 --- a/src/services/preload/add-workspace.ts +++ /dev/null @@ -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' }); diff --git a/src/services/preload/auth.ts b/src/services/preload/auth.ts deleted file mode 100644 index 8c38a7c5..00000000 --- a/src/services/preload/auth.ts +++ /dev/null @@ -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' }); diff --git a/src/services/preload/code-injection.ts b/src/services/preload/code-injection.ts deleted file mode 100644 index 52a420fa..00000000 --- a/src/services/preload/code-injection.ts +++ /dev/null @@ -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' }); diff --git a/src/services/preload/custom-user-agent.ts b/src/services/preload/custom-user-agent.ts deleted file mode 100644 index 14dffa67..00000000 --- a/src/services/preload/custom-user-agent.ts +++ /dev/null @@ -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' }); diff --git a/src/services/preload/display-media.ts b/src/services/preload/display-media.ts deleted file mode 100644 index 5dc1bf7b..00000000 --- a/src/services/preload/display-media.ts +++ /dev/null @@ -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' }); diff --git a/src/services/preload/edit-workspace.ts b/src/services/preload/edit-workspace.ts deleted file mode 100644 index 1b930ad7..00000000 --- a/src/services/preload/edit-workspace.ts +++ /dev/null @@ -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' }); diff --git a/src/services/preload/go-to-url.ts b/src/services/preload/go-to-url.ts deleted file mode 100644 index 14d52815..00000000 --- a/src/services/preload/go-to-url.ts +++ /dev/null @@ -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' }); diff --git a/src/services/preload/index.ts b/src/services/preload/index.ts index 88be7d85..98ab3b9c 100644 --- a/src/services/preload/index.ts +++ b/src/services/preload/index.ts @@ -4,10 +4,15 @@ import './common/i18n'; import './common/require-nodejs'; import './common/simple-context-menu'; import './common/authing-postmessage'; +import { WindowNames } from '@/services/windows/WindowProperties'; -const windowName = process.argv[0]; +const windowName = process.argv[0] as WindowNames; // DEBUG: console console.log(`windowName`, windowName); contextBridge.exposeInMainWorld('meta', { windowName }); + +if (windowName === WindowNames.view) { + void import('./view'); +} diff --git a/src/services/preload/main.ts b/src/services/preload/main.ts deleted file mode 100644 index f62858f0..00000000 --- a/src/services/preload/main.ts +++ /dev/null @@ -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' }); diff --git a/src/services/preload/menubar.ts b/src/services/preload/menubar.ts deleted file mode 100644 index 4559a0f7..00000000 --- a/src/services/preload/menubar.ts +++ /dev/null @@ -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' }); diff --git a/src/services/preload/notifications.ts b/src/services/preload/notifications.ts deleted file mode 100644 index 7cc4ed18..00000000 --- a/src/services/preload/notifications.ts +++ /dev/null @@ -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' }); diff --git a/src/services/preload/open-url-with.ts b/src/services/preload/open-url-with.ts deleted file mode 100644 index 88b3a44f..00000000 --- a/src/services/preload/open-url-with.ts +++ /dev/null @@ -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' }); diff --git a/src/services/preload/preferences.ts b/src/services/preload/preferences.ts deleted file mode 100644 index 962fca82..00000000 --- a/src/services/preload/preferences.ts +++ /dev/null @@ -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' }); diff --git a/src/services/preload/proxy.ts b/src/services/preload/proxy.ts deleted file mode 100644 index c54499ef..00000000 --- a/src/services/preload/proxy.ts +++ /dev/null @@ -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' }); diff --git a/src/services/preload/spellcheck-languages.ts b/src/services/preload/spellcheck-languages.ts deleted file mode 100644 index 039435c0..00000000 --- a/src/services/preload/spellcheck-languages.ts +++ /dev/null @@ -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' }); diff --git a/src/services/windows/WindowProperties.ts b/src/services/windows/WindowProperties.ts new file mode 100644 index 00000000..c4e5d8fa --- /dev/null +++ b/src/services/windows/WindowProperties.ts @@ -0,0 +1,23 @@ +export enum WindowNames { + main = 'main', + view = 'view', + addWorkspace = 'addWorkspace', +} + +/** + * Width height of windows + */ +export const windowDimension: Record = { + [WindowNames.main]: { + width: 1200, + height: 768, + }, + [WindowNames.view]: { + width: undefined, + height: undefined, + }, + [WindowNames.addWorkspace]: { + width: 600, + height: 800, + }, +}; diff --git a/src/services/window.ts b/src/services/windows/index.ts similarity index 83% rename from src/services/window.ts rename to src/services/windows/index.ts index 9fc55397..9f7581e6 100644 --- a/src/services/window.ts +++ b/src/services/windows/index.ts @@ -5,28 +5,18 @@ import { injectable, inject } from 'inversify'; import serviceIdentifiers from '@services/serviceIdentifier'; import { Preference } from '@services/preferences'; import { Channels } from '@/services/channels'; - -export enum WindowNames { - main = 'main', - view = 'view', - addWorkspace = 'addWorkspace', -} - +import { WindowNames, windowDimension } from '@/services/windows/WindowProperties'; declare const MAIN_WINDOW_WEBPACK_ENTRY: string; declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string; @injectable() export class Window { - preferenceService: Preference; - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions private windows = {} as Record; - constructor(@inject(serviceIdentifiers.Preference) preferenceService: Preference) { - this.preferenceService = preferenceService; - } + constructor(@inject(serviceIdentifiers.Preference) private readonly preferenceService: Preference) {} - private get(name: WindowNames): BrowserWindow | undefined { + public get(name: WindowNames): BrowserWindow | undefined { return this.windows[name]; } @@ -34,8 +24,7 @@ export class Window { const attachToMenubar: boolean = this.preferenceService.get('attachToMenubar'); const newWindow = new BrowserWindow({ - width: 600, - height: 800, + ...windowDimension[name], resizable: false, maximizable: false, minimizable: false, diff --git a/src/services/windows/main.ts b/src/services/windows/main.ts index 933c72b0..ee5de57d 100644 --- a/src/services/windows/main.ts +++ b/src/services/windows/main.ts @@ -7,8 +7,6 @@ import { REACT_PATH, isDev as isDevelopment } from '../constants/paths'; import { getPreference } from '../libs/preferences'; 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 menuBar: Menubar; diff --git a/webpack.alias.js b/webpack.alias.js index 34c97b2d..2517367f 100644 --- a/webpack.alias.js +++ b/webpack.alias.js @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ const path = require('path'); const fs = require('fs-extra'); diff --git a/webpack.main.config.js b/webpack.main.config.js index c0facaa2..d4dbc462 100644 --- a/webpack.main.config.js +++ b/webpack.main.config.js @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ const { webpackAlias } = require('./webpack.alias'); module.exports = { @@ -5,7 +6,7 @@ module.exports = { * This is the main entry point for your application, it's the first file * that runs in the main process. */ - entry: './src/services/electron.ts', + entry: './src/main.ts', // Put your normal webpack config below here module: { rules: require('./webpack.rules'),