From 114793e1e5bec8755ab02b83ca02be6a2adc340b Mon Sep 17 00:00:00 2001 From: sentialx Date: Wed, 8 Apr 2020 13:35:40 +0200 Subject: [PATCH 01/21] feat: add dialogs service class --- src/main/dialogs/dialog.ts | 12 +++- src/main/services/dialogs-service.ts | 96 ++++++++++++++++++++++++++++ src/models/dialog-store.ts | 7 +- 3 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 src/main/services/dialogs-service.ts diff --git a/src/main/dialogs/dialog.ts b/src/main/dialogs/dialog.ts index db174baab..3facb1154 100644 --- a/src/main/dialogs/dialog.ts +++ b/src/main/dialogs/dialog.ts @@ -77,7 +77,7 @@ export class Dialog { }); if (process.env.NODE_ENV === 'development') { - webContents.loadURL(`http://localhost:4444/${name}.html`); + webContents.loadURL(`about:blank`); if (devtools) { webContents.openDevTools({ mode: 'detach' }); } @@ -123,6 +123,14 @@ export class Dialog { true, ); + if (process.env.NODE_ENV === 'development') { + this.webContents.loadURL(`http://localhost:4444/${this.name}.html`); + } else { + this.webContents.loadURL( + join('file://', app.getAppPath(), `build/${this.name}.html`), + ); + } + const callback = () => { if (this.visible) { if (focus) this.webContents.focus(); @@ -171,6 +179,8 @@ export class Dialog { this.bringToTop(); } + this.browserView.webContents.loadURL('about:blank'); + clearTimeout(this.timeout); if (this.hideTimeout) { diff --git a/src/main/services/dialogs-service.ts b/src/main/services/dialogs-service.ts new file mode 100644 index 000000000..0ff8a7d20 --- /dev/null +++ b/src/main/services/dialogs-service.ts @@ -0,0 +1,96 @@ +import { BrowserView, app } from 'electron'; +import { join } from 'path'; + +interface IDialogShowOptions { + name: string; + browserWindow: Electron.BrowserWindow; + bounds: Electron.Rectangle; + hideTimeout?: number; + devtools?: boolean; +} + +interface IDialog { + name: string; + browserView: BrowserView; + id: number; + hide: () => void; +} + +export class DialogsService { + public browserViews: BrowserView[] = []; + public dialogs: IDialog[] = []; + + public run() { + for (let i = 0; i < 2; i++) { + this.createBrowserView(); + } + } + + private createBrowserView() { + const view = new BrowserView({ + webPreferences: { + nodeIntegration: true, + contextIsolation: false, + enableRemoteModule: true, + webviewTag: true, + }, + }); + + this.browserViews.push(view); + + return view; + } + + public show({ + name, + browserWindow, + bounds, + devtools, + hideTimeout, + }: IDialogShowOptions) { + let browserView = this.browserViews.find((x) => + this.dialogs.find((y) => y.browserView !== x), + ); + + if (!browserView) { + browserView = this.createBrowserView(); + } + + browserWindow.addBrowserView(browserView); + browserView.setBounds(bounds); + + if (process.env.NODE_ENV === 'development') { + browserView.webContents.loadURL(`http://localhost:4444/${name}.html`); + } else { + browserView.webContents.loadURL( + join('file://', app.getAppPath(), `build/${name}.html`), + ); + } + + if (devtools) { + browserView.webContents.openDevTools({ mode: 'detach' }); + } + + const dialog = { + browserView, + id: browserView.id, + name, + hide: () => { + this.dialogs = this.dialogs.filter((x) => x.id !== dialog.id); + + browserWindow.removeBrowserView(browserView); + + if (this.browserViews.length > 2) { + browserView.destroy(); + this.browserViews.splice(2, 1); + } else { + browserView.webContents.loadURL('about:blank'); + } + }, + }; + + this.dialogs.push(dialog); + + return dialog; + } +} diff --git a/src/models/dialog-store.ts b/src/models/dialog-store.ts index 1dc0e00bf..61a8dab95 100644 --- a/src/models/dialog-store.ts +++ b/src/models/dialog-store.ts @@ -16,7 +16,7 @@ export class DialogStore { private _windowId = -1; @observable - public visible = false; + public visible = true; public firstTime = false; @@ -58,7 +58,10 @@ export class DialogStore { }); } - ipcRenderer.send('get-settings'); + this.settings = { + ...this.settings, + ...ipcRenderer.sendSync('get-settings-sync'), + }; ipcRenderer.on('update-settings', (e, settings: ISettings) => { this.settings = { ...this.settings, ...settings }; From 7065b463a11f6e78c74434db7725921aa014546e Mon Sep 17 00:00:00 2001 From: sentialx Date: Wed, 8 Apr 2020 21:02:06 +0200 Subject: [PATCH 02/21] refactor: make search dialog persistent for instant answers --- src/main/application.ts | 4 + src/main/dialogs/dialog.ts | 67 +- src/main/dialogs/search.ts | 49 +- src/main/models/settings.ts | 4 +- src/main/services/dialogs-service.ts | 42 +- src/main/services/messaging.ts | 28 +- src/main/view-manager.ts | 4 +- src/main/windows/app.ts | 62 +- src/models/dialog-store.ts | 14 +- .../views/app/components/Toolbar/index.tsx | 673 +++++++++--------- .../views/search/components/App/index.tsx | 7 +- src/renderer/views/search/store/index.ts | 8 +- webpack.config.base.js | 2 +- 13 files changed, 460 insertions(+), 504 deletions(-) diff --git a/src/main/application.ts b/src/main/application.ts index 65a266ad4..39acc1932 100644 --- a/src/main/application.ts +++ b/src/main/application.ts @@ -9,6 +9,7 @@ import { WindowsService } from './windows-service'; import { StorageService } from './services/storage'; import { getMainMenu } from './menus/main'; import { runAutoUpdaterService } from './services'; +import { DialogsService } from './services/dialogs-service'; export class Application { public static instance = new Application(); @@ -21,6 +22,8 @@ export class Application { public windows = new WindowsService(); + public dialogs = new DialogsService(); + public start() { const gotTheLock = app.requestSingleInstanceLock(); @@ -83,6 +86,7 @@ export class Application { checkFiles(); this.storage.run(); + this.dialogs.run(); this.windows.open(); diff --git a/src/main/dialogs/dialog.ts b/src/main/dialogs/dialog.ts index 3facb1154..a3ae5979f 100644 --- a/src/main/dialogs/dialog.ts +++ b/src/main/dialogs/dialog.ts @@ -1,6 +1,5 @@ -import { BrowserView, app, ipcMain } from 'electron'; +import { BrowserView, app, ipcMain, BrowserWindow } from 'electron'; import { join } from 'path'; -import { AppWindow } from '../windows'; interface IOptions { name: string; @@ -18,8 +17,8 @@ interface IRectangle { height?: number; } -export class Dialog { - public appWindow: AppWindow; +export class PersistentDialog { + public browserWindow: BrowserWindow; public browserView: BrowserView; public visible = false; @@ -35,15 +34,16 @@ export class Dialog { private hideTimeout: number; private name: string; - public tabIds: number[] = []; - private loaded = false; private showCallback: any = null; - public constructor( - appWindow: AppWindow, - { bounds, name, devtools, hideTimeout, webPreferences }: IOptions, - ) { + public constructor({ + bounds, + name, + devtools, + hideTimeout, + webPreferences, + }: IOptions) { this.browserView = new BrowserView({ webPreferences: { nodeIntegration: true, @@ -53,7 +53,6 @@ export class Dialog { }, }); - this.appWindow = appWindow; this.bounds = { ...this.bounds, ...bounds }; this.hideTimeout = hideTimeout; this.name = name; @@ -62,9 +61,6 @@ export class Dialog { ipcMain.on(`hide-${webContents.id}`, () => { this.hide(false, false); - this.tabIds = this.tabIds.filter( - (x) => x !== appWindow.viewManager.selectedId, - ); }); webContents.once('dom-ready', () => { @@ -77,13 +73,10 @@ export class Dialog { }); if (process.env.NODE_ENV === 'development') { - webContents.loadURL(`about:blank`); - if (devtools) { - webContents.openDevTools({ mode: 'detach' }); - } + this.webContents.loadURL(`http://localhost:4444/${this.name}.html`); } else { - webContents.loadURL( - join('file://', app.getAppPath(), `build/${name}.html`), + this.webContents.loadURL( + join('file://', app.getAppPath(), `build/${this.name}.html`), ); } } @@ -109,28 +102,18 @@ export class Dialog { } } - public toggle() { - if (!this.visible) this.show(); - } - - public show(focus = true, waitForLoad = true) { + public show(browserWindow: BrowserWindow, focus = true, waitForLoad = true) { return new Promise((resolve) => { + this.browserWindow = browserWindow; + clearTimeout(this.timeout); - this.appWindow.webContents.send( + browserWindow.webContents.send( 'dialog-visibility-change', this.name, true, ); - if (process.env.NODE_ENV === 'development') { - this.webContents.loadURL(`http://localhost:4444/${this.name}.html`); - } else { - this.webContents.loadURL( - join('file://', app.getAppPath(), `build/${this.name}.html`), - ); - } - const callback = () => { if (this.visible) { if (focus) this.webContents.focus(); @@ -139,7 +122,7 @@ export class Dialog { this.visible = true; - this.appWindow.win.addBrowserView(this.browserView); + browserWindow.addBrowserView(this.browserView); this.rearrange(); if (focus) this.webContents.focus(); @@ -165,11 +148,13 @@ export class Dialog { } public hide(bringToTop = false, hideVisually = true) { + if (!this.browserWindow) return; + if (hideVisually) this.hideVisually(); if (!this.visible) return; - this.appWindow.webContents.send( + this.browserWindow.webContents.send( 'dialog-visibility-change', this.name, false, @@ -179,16 +164,14 @@ export class Dialog { this.bringToTop(); } - this.browserView.webContents.loadURL('about:blank'); - clearTimeout(this.timeout); if (this.hideTimeout) { this.timeout = setTimeout(() => { - this.appWindow.win.removeBrowserView(this.browserView); + this.browserWindow.removeBrowserView(this.browserView); }, this.hideTimeout); } else { - this.appWindow.win.removeBrowserView(this.browserView); + this.browserWindow.removeBrowserView(this.browserView); } this.visible = false; @@ -197,8 +180,8 @@ export class Dialog { } public bringToTop() { - this.appWindow.win.removeBrowserView(this.browserView); - this.appWindow.win.addBrowserView(this.browserView); + this.browserWindow.removeBrowserView(this.browserView); + this.browserWindow.addBrowserView(this.browserView); } public destroy() { diff --git a/src/main/dialogs/search.ts b/src/main/dialogs/search.ts index 028ea20d3..8e39e0972 100644 --- a/src/main/dialogs/search.ts +++ b/src/main/dialogs/search.ts @@ -1,19 +1,17 @@ -import { AppWindow } from '../windows'; -import { ipcMain } from 'electron'; -import { Dialog } from '.'; +import { ipcMain, BrowserWindow } from 'electron'; import { DIALOG_MIN_HEIGHT, DIALOG_MARGIN_TOP, TITLEBAR_HEIGHT, DIALOG_MARGIN, } from '~/constants/design'; +import { PersistentDialog } from './dialog'; +import { Application } from '../application'; const WIDTH = 800; const HEIGHT = 80; -export class SearchDialog extends Dialog { - private queueShow = false; - +export class SearchDialog extends PersistentDialog { private lastHeight = 0; private isPreviewVisible = false; @@ -23,8 +21,8 @@ export class SearchDialog extends Dialog { width: 200, }; - public constructor(appWindow: AppWindow) { - super(appWindow, { + public constructor() { + super({ name: 'search', bounds: { width: WIDTH, @@ -46,19 +44,10 @@ export class SearchDialog extends Dialog { }); ipcMain.on(`addressbar-update-input-${this.id}`, (e, data) => { - this.appWindow.send('addressbar-update-input', data); - }); - - ipcMain.on(`can-show-${this.id}`, () => { - if (this.queueShow) this.show(); + this.browserWindow.webContents.send('addressbar-update-input', data); }); } - public toggle() { - if (!this.visible) this.show(); - else this.hide(); - } - public rearrange() { super.rearrange({ x: Math.round(this.data.x - DIALOG_MARGIN), @@ -67,26 +56,15 @@ export class SearchDialog extends Dialog { }); } - public rearrangePreview(toggle: boolean) { - this.isPreviewVisible = toggle; - super.rearrange({ - height: toggle - ? Math.max(DIALOG_MIN_HEIGHT, this.bounds.height) - : this.lastHeight, - }); - } - - public async show() { - if (this.appWindow.dialogs.previewDialog.visible) { + public async show(browserWindow: BrowserWindow) { + /*if (this.appWindow.dialogs.previewDialog.visible) { this.appWindow.dialogs.previewDialog.hide(true); - } - - super.show(true, false); + }*/ - this.queueShow = true; + super.show(browserWindow, true, false); this.send('visible', true, { - id: this.appWindow.viewManager.selectedId, + id: Application.instance.windows.current.viewManager.selectedId, ...this.data, }); @@ -94,11 +72,10 @@ export class SearchDialog extends Dialog { this.send('search-tabs', tabs); }); - this.appWindow.send('get-search-tabs'); + browserWindow.webContents.send('get-search-tabs'); } public hide(bringToTop = false) { super.hide(bringToTop); - this.queueShow = false; } } diff --git a/src/main/models/settings.ts b/src/main/models/settings.ts index 2d9bbaea7..db9d04bf2 100644 --- a/src/main/models/settings.ts +++ b/src/main/models/settings.ts @@ -88,9 +88,11 @@ export class Settings extends EventEmitter { for (const window of Application.instance.windows.list) { window.send('update-settings', this.object); + /* + // TODO: dialogs Object.values(window.dialogs).forEach((dialog) => { dialog.send('update-settings', this.object); - }); + });*/ window.viewManager.views.forEach(async (v) => { if (v.webContents.getURL().startsWith(WEBUI_BASE_URL)) { diff --git a/src/main/services/dialogs-service.ts b/src/main/services/dialogs-service.ts index 0ff8a7d20..7c29c5c03 100644 --- a/src/main/services/dialogs-service.ts +++ b/src/main/services/dialogs-service.ts @@ -1,5 +1,6 @@ -import { BrowserView, app } from 'electron'; +import { BrowserView, app, ipcMain } from 'electron'; import { join } from 'path'; +import { SearchDialog } from '../dialogs/search'; interface IDialogShowOptions { name: string; @@ -18,12 +19,15 @@ interface IDialog { export class DialogsService { public browserViews: BrowserView[] = []; + public browserViewDetails = new Map(); public dialogs: IDialog[] = []; + public searchBox: SearchDialog; + public run() { - for (let i = 0; i < 2; i++) { - this.createBrowserView(); - } + this.createBrowserView(); + + this.searchBox = new SearchDialog(); } private createBrowserView() { @@ -36,8 +40,12 @@ export class DialogsService { }, }); + view.webContents.loadURL(`about:blank`); + this.browserViews.push(view); + this.browserViewDetails.set(view.id, false); + return view; } @@ -47,15 +55,21 @@ export class DialogsService { bounds, devtools, hideTimeout, - }: IDialogShowOptions) { - let browserView = this.browserViews.find((x) => - this.dialogs.find((y) => y.browserView !== x), + }: IDialogShowOptions): IDialog { + const foundDialog = this.dialogs.find((x) => x.name === name); + if (foundDialog) return foundDialog; + + let browserView = this.browserViews.find( + (x) => !this.browserViewDetails.get(x.id), ); if (!browserView) { browserView = this.createBrowserView(); } + bounds.x = Math.round(bounds.x); + bounds.y = Math.round(bounds.y); + browserWindow.addBrowserView(browserView); browserView.setBounds(bounds); @@ -67,8 +81,10 @@ export class DialogsService { ); } + browserView.webContents.focus(); + if (devtools) { - browserView.webContents.openDevTools({ mode: 'detach' }); + // browserView.webContents.openDevTools({ mode: 'detach' }); } const dialog = { @@ -76,6 +92,8 @@ export class DialogsService { id: browserView.id, name, hide: () => { + ipcMain.removeAllListeners(`hide-${browserView.webContents.id}`); + this.dialogs = this.dialogs.filter((x) => x.id !== dialog.id); browserWindow.removeBrowserView(browserView); @@ -83,12 +101,20 @@ export class DialogsService { if (this.browserViews.length > 2) { browserView.destroy(); this.browserViews.splice(2, 1); + this.browserViewDetails.delete(browserView.id); } else { browserView.webContents.loadURL('about:blank'); + this.browserViewDetails.set(browserView.id, false); } }, }; + this.browserViewDetails.set(browserView.id, true); + + ipcMain.on(`hide-${browserView.webContents.id}`, () => { + dialog.hide(); + }); + this.dialogs.push(dialog); return dialog; diff --git a/src/main/services/messaging.ts b/src/main/services/messaging.ts index af308babf..40e848cde 100644 --- a/src/main/services/messaging.ts +++ b/src/main/services/messaging.ts @@ -1,10 +1,5 @@ import { ipcMain } from 'electron'; -import { parse } from 'url'; -import { setPassword, deletePassword, getPassword } from 'keytar'; - -import { IFormFillData } from '~/interfaces'; import { AppWindow } from '../windows'; -import { getFormFillMenuItems } from '../utils'; import { Application } from '../application'; export const runMessagingService = (appWindow: AppWindow) => { @@ -35,7 +30,26 @@ export const runMessagingService = (appWindow: AppWindow) => { appWindow.fixDragging(); }); - ipcMain.on(`find-show-${id}`, () => { + ipcMain.on(`show-menu-dialog-${id}`, (e, left, top) => { + Application.instance.dialogs.show({ + name: 'menu', + browserWindow: appWindow.win, + bounds: { + width: 400, + height: 600, + x: left - 400, + y: top, + }, + devtools: true, + }); + }); + + ipcMain.on(`search-show-${id}`, (e, data) => { + Application.instance.dialogs.searchBox.data = data; + Application.instance.dialogs.searchBox.show(appWindow.win); + }); + + /*ipcMain.on(`find-show-${id}`, () => { appWindow.dialogs.findDialog.show(); }); @@ -96,7 +110,7 @@ export const runMessagingService = (appWindow: AppWindow) => { ipcMain.on(`edit-tabgroup-${id}`, (e, tabGroup) => { appWindow.send(`edit-tabgroup`, tabGroup); - }); + });*/ ipcMain.on(`is-incognito-${id}`, (e) => { e.returnValue = appWindow.incognito; diff --git a/src/main/view-manager.ts b/src/main/view-manager.ts index 9a94adf2c..d90a26599 100644 --- a/src/main/view-manager.ts +++ b/src/main/view-manager.ts @@ -130,7 +130,7 @@ export class ViewManager { this.window.webContents.focus(); } - this.window.dialogs.previewDialog.hide(true); + /*this.window.dialogs.previewDialog.hide(true); [ 'findDialog', @@ -145,7 +145,7 @@ export class ViewManager { } else { this.window.dialogs[dialog].hide(); } - }); + });*/ this.window.updateTitle(); view.updateBookmark(); diff --git a/src/main/windows/app.ts b/src/main/windows/app.ts index 3c1ce4d1a..53f8f95c1 100644 --- a/src/main/windows/app.ts +++ b/src/main/windows/app.ts @@ -5,43 +5,9 @@ import { resolve, join } from 'path'; import { getPath } from '~/utils'; import { runMessagingService } from '../services'; import { Application } from '../application'; -import { - MenuDialog, - SearchDialog, - FindDialog, - PermissionsDialog, - AuthDialog, - FormFillDialog, - CredentialsDialog, - PreviewDialog, - TabGroupDialog, - DownloadsDialog, - AddBookmarkDialog, - Dialog, - ExtensionPopup, -} from '../dialogs'; import { isNightly } from '..'; import { ViewManager } from '../view-manager'; -interface IDialogs { - searchDialog?: SearchDialog; - previewDialog?: PreviewDialog; - - tabGroupDialog?: TabGroupDialog; - menuDialog?: MenuDialog; - findDialog?: FindDialog; - downloadsDialog?: DownloadsDialog; - addBookmarkDialog?: AddBookmarkDialog; - - permissionsDialog?: PermissionsDialog; - authDialog?: AuthDialog; - formFillDialog?: FormFillDialog; - credentialsDialog?: CredentialsDialog; - extensionPopup?: ExtensionPopup; - - [key: string]: Dialog; -} - export class AppWindow { public win: BrowserWindow; @@ -49,10 +15,6 @@ export class AppWindow { public incognito: boolean; - public dialogs: IDialogs = { - searchDialog: new SearchDialog(this), - }; - public constructor(incognito: boolean) { this.win = new BrowserWindow({ frame: false, @@ -80,22 +42,6 @@ export class AppWindow { this.viewManager = new ViewManager(this, incognito); - this.webContents.once('dom-ready', () => { - this.dialogs.previewDialog = new PreviewDialog(this); - - this.dialogs.tabGroupDialog = new TabGroupDialog(this); - this.dialogs.menuDialog = new MenuDialog(this); - this.dialogs.findDialog = new FindDialog(this); - this.dialogs.downloadsDialog = new DownloadsDialog(this); - this.dialogs.addBookmarkDialog = new AddBookmarkDialog(this); - - this.dialogs.permissionsDialog = new PermissionsDialog(this); - this.dialogs.authDialog = new AuthDialog(this); - this.dialogs.formFillDialog = new FormFillDialog(this); - this.dialogs.credentialsDialog = new CredentialsDialog(this); - this.dialogs.extensionPopup = new ExtensionPopup(this); - }); - runMessagingService(this); const windowDataPath = getPath('window-data.json'); @@ -135,11 +81,11 @@ export class AppWindow { windowState.bounds = this.win.getBounds(); } - Object.values(this.dialogs).forEach((dialog) => { + /*Object.values(this.dialogs).forEach((dialog) => { if (dialog.visible) { dialog.rearrange(); } - }); + });*/ }); this.win.on('move', () => { @@ -193,12 +139,12 @@ export class AppWindow { this.win.setBrowserView(null); - Object.keys(this.dialogs).forEach((key) => { + /*Object.keys(this.dialogs).forEach((key) => { if (this.dialogs[key]) { this.dialogs[key].destroy(); } this.dialogs[key] = null; - }); + });*/ this.viewManager.clear(); diff --git a/src/models/dialog-store.ts b/src/models/dialog-store.ts index 61a8dab95..0d8f18595 100644 --- a/src/models/dialog-store.ts +++ b/src/models/dialog-store.ts @@ -16,7 +16,7 @@ export class DialogStore { private _windowId = -1; @observable - public visible = true; + public visible = false; public firstTime = false; @@ -33,12 +33,13 @@ export class DialogStore { }; if (visibilityWrapper) { ipcRenderer.on('visible', async (e, flag, ...args) => { - if (!this.firstTime) { + // TODO: diaogs + /*if (!this.firstTime) { requestAnimationFrame(() => { this.visible = true; setTimeout(() => { - this.visible = false; + this.visible = true; setTimeout(() => { this.onVisibilityChange(flag, ...args); @@ -48,7 +49,9 @@ export class DialogStore { this.firstTime = true; } else { this.onVisibilityChange(flag, ...args); - } + }*/ + + this.onVisibilityChange(flag, ...args); }); } @@ -86,8 +89,9 @@ export class DialogStore { public hide(data: any = null) { if (this.visible) { this.visible = false; - this.onHide(data); + setTimeout(() => { + this.onHide(data); ipcRenderer.send(`hide-${this.id}`); }); } diff --git a/src/renderer/views/app/components/Toolbar/index.tsx b/src/renderer/views/app/components/Toolbar/index.tsx index 7c6fd77c5..9afd0a2a1 100644 --- a/src/renderer/views/app/components/Toolbar/index.tsx +++ b/src/renderer/views/app/components/Toolbar/index.tsx @@ -1,336 +1,337 @@ -import { observer } from 'mobx-react-lite'; -import * as React from 'react'; -import { ipcRenderer, remote } from 'electron'; -import { parse } from 'url'; - -import store from '../../store'; -import { - Buttons, - StyledToolbar, - Separator, - Addressbar, - AddressbarText, - AddressbarInput, - AddressbarInputContainer, -} from './style'; -import { NavigationButtons } from '../NavigationButtons'; -import { ToolbarButton } from '../ToolbarButton'; -import { BrowserAction } from '../BrowserAction'; -import { - ICON_STAR, - ICON_STAR_FILLED, - ICON_KEY, - ICON_SHIELD, - ICON_DOWNLOAD, - ICON_INCOGNITO, - ICON_MORE, - ICON_SEARCH, - ICON_DASHBOARD, -} from '~/renderer/constants/icons'; -import { isDialogVisible } from '../../utils/dialogs'; -import { isURL } from '~/utils'; -import { callViewMethod } from '~/utils/view'; - -const onDownloadsClick = async (e: React.MouseEvent) => { - const { right, bottom } = e.currentTarget.getBoundingClientRect(); - if (!(await isDialogVisible('downloadsDialog'))) { - store.downloadNotification = false; - ipcRenderer.send(`show-downloads-dialog-${store.windowId}`, right, bottom); - } -}; - -const onKeyClick = () => { - const { hostname } = parse(store.tabs.selectedTab.url); - const list = store.autoFill.credentials.filter( - (r) => r.url === hostname && r.fields.username, - ); - - ipcRenderer.send(`credentials-show-${store.windowId}`, { - content: 'list', - list, - }); -}; - -let starRef: HTMLDivElement = null; -let menuRef: HTMLDivElement = null; - -const showAddBookmarkDialog = async () => { - if (!(await isDialogVisible('addBookmarkDialog'))) { - const { right, bottom } = starRef.getBoundingClientRect(); - ipcRenderer.send( - `show-add-bookmark-dialog-${store.windowId}`, - right, - bottom, - ); - } -}; - -const showMenuDialog = async () => { - if (!(await isDialogVisible('menuDialog'))) { - const { right, bottom } = menuRef.getBoundingClientRect(); - ipcRenderer.send(`show-menu-dialog-${store.windowId}`, right, bottom); - } -}; - -ipcRenderer.on('show-add-bookmark-dialog', () => { - showAddBookmarkDialog(); -}); - -ipcRenderer.on('show-menu-dialog', () => { - showMenuDialog(); -}); - -const onStarClick = (e: React.MouseEvent) => { - showAddBookmarkDialog(); -}; - -const onMenuClick = async () => { - showMenuDialog(); -}; - -const BrowserActions = observer(() => { - const { selectedTabId } = store.tabs; - - return ( - <> - {selectedTabId && - store.extensions.browserActions.map((item) => { - if (item.tabId === selectedTabId) { - return ; - } - return null; - })} - - ); -}); - -const onShieldContextMenu = (e: React.MouseEvent) => { - const menu = remote.Menu.buildFromTemplate([ - { - checked: store.settings.object.shield, - label: 'Enabled', - type: 'checkbox', - click: () => { - store.settings.object.shield = !store.settings.object.shield; - store.settings.save(); - }, - }, - ]); - - menu.popup(); -}; - -const RightButtons = observer(() => { - const { selectedTab } = store.tabs; - - let blockedAds = 0; - - if (selectedTab) { - blockedAds = selectedTab.blockedAds; - } - - return ( - - - {store.extensions.browserActions.length > 0 && } - - 0} - badgeText={blockedAds.toString()} - icon={ICON_SHIELD} - opacity={store.settings.object.shield ? 0.87 : 0.54} - onContextMenu={onShieldContextMenu} - > - - {store.downloadsButtonVisible && ( - - )} - {store.isIncognito && } - (menuRef = r)} - toggled={store.dialogsVisibility['menu']} - badge={store.updateAvailable} - badgeRight={10} - badgeTop={6} - onMouseDown={onMenuClick} - icon={ICON_MORE} - size={18} - /> - - ); -}); - -let mouseUpped = false; - -const onMouseDown = (e: React.MouseEvent) => { - store.addressbarTextVisible = false; - store.addressbarFocused = true; -}; - -const onFocus = (e: React.FocusEvent) => { - store.addressbarTextVisible = false; - store.addressbarFocused = true; - - if (store.tabs.selectedTab) { - store.tabs.selectedTab.addressbarFocused = true; - } -}; - -const onSelect = (e: React.MouseEvent) => { - if (store.tabs.selectedTab) { - store.tabs.selectedTab.addressbarSelectionRange = [ - e.currentTarget.selectionStart, - e.currentTarget.selectionEnd, - ]; - } -}; - -const onMouseUp = (e: React.MouseEvent) => { - if (window.getSelection().toString().length === 0 && !mouseUpped) { - e.currentTarget.select(); - } - - mouseUpped = true; -}; - -const onKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Escape' || e.key === 'Enter') { - store.tabs.selectedTab.addressbarValue = null; - } - - if (e.key === 'Escape') { - const target = e.currentTarget; - requestAnimationFrame(() => { - target.select(); - }); - } - - if (e.key === 'Enter') { - store.addressbarFocused = false; - e.currentTarget.blur(); - const { value } = e.currentTarget; - let url = value; - - if (isURL(value)) { - url = value.indexOf('://') === -1 ? `http://${value}` : value; - } else { - url = store.settings.searchEngine.url.replace('%s', value); - } - - store.tabs.selectedTab.addressbarValue = url; - callViewMethod(store.tabs.selectedTabId, 'loadURL', url); - } -}; - -const addressbarRef = React.createRef(); - -const onChange = (e: React.ChangeEvent) => { - store.tabs.selectedTab.addressbarValue = e.currentTarget.value; - - const { left, width } = addressbarRef.current.getBoundingClientRect(); - - if (e.currentTarget.value.trim() !== '') { - ipcRenderer.send(`search-show-${store.windowId}`, { - text: e.currentTarget.value, - cursorPos: e.currentTarget.selectionStart, - x: left, - width: width, - }); - store.addressbarEditing = true; - } -}; - -const onBlur = (e: React.FocusEvent) => { - e.currentTarget.blur(); - window.getSelection().removeAllRanges(); - store.addressbarTextVisible = true; - store.addressbarFocused = false; - mouseUpped = false; - - if (store.tabs.selectedTab) { - store.tabs.selectedTab.addressbarFocused = false; - } -}; - -export const Toolbar = observer(() => { - const { selectedTab } = store.tabs; - - let hasCredentials = false; - - if (selectedTab) { - hasCredentials = selectedTab.hasCredentials; - } - - return ( - - - - - - - - {store.addressbarUrlSegments.map((item, key) => ( -
- {item.value} -
- ))} -
-
- - {hasCredentials && ( - - )} - (starRef = r)} - toggled={store.dialogsVisibility['add-bookmark']} - icon={store.isBookmarked ? ICON_STAR_FILLED : ICON_STAR} - size={18} - dense - onMouseDown={onStarClick} - /> -
- -
- ); -}); +import { observer } from 'mobx-react-lite'; +import * as React from 'react'; +import { ipcRenderer, remote } from 'electron'; +import { parse } from 'url'; + +import store from '../../store'; +import { + Buttons, + StyledToolbar, + Separator, + Addressbar, + AddressbarText, + AddressbarInput, + AddressbarInputContainer, +} from './style'; +import { NavigationButtons } from '../NavigationButtons'; +import { ToolbarButton } from '../ToolbarButton'; +import { BrowserAction } from '../BrowserAction'; +import { + ICON_STAR, + ICON_STAR_FILLED, + ICON_KEY, + ICON_SHIELD, + ICON_DOWNLOAD, + ICON_INCOGNITO, + ICON_MORE, + ICON_SEARCH, + ICON_DASHBOARD, +} from '~/renderer/constants/icons'; +import { isDialogVisible } from '../../utils/dialogs'; +import { isURL } from '~/utils'; +import { callViewMethod } from '~/utils/view'; + +const onDownloadsClick = async (e: React.MouseEvent) => { + const { right, bottom } = e.currentTarget.getBoundingClientRect(); + if (!(await isDialogVisible('downloadsDialog'))) { + store.downloadNotification = false; + ipcRenderer.send(`show-downloads-dialog-${store.windowId}`, right, bottom); + } +}; + +const onKeyClick = () => { + const { hostname } = parse(store.tabs.selectedTab.url); + const list = store.autoFill.credentials.filter( + (r) => r.url === hostname && r.fields.username, + ); + + ipcRenderer.send(`credentials-show-${store.windowId}`, { + content: 'list', + list, + }); +}; + +let starRef: HTMLDivElement = null; +let menuRef: HTMLDivElement = null; + +const showAddBookmarkDialog = async () => { + if (!(await isDialogVisible('addBookmarkDialog'))) { + const { right, bottom } = starRef.getBoundingClientRect(); + ipcRenderer.send( + `show-add-bookmark-dialog-${store.windowId}`, + right, + bottom, + ); + } +}; + +const showMenuDialog = async () => { + // TODO: dialogs + //if (!(await isDialogVisible('menuDialog'))) { + const { right, bottom } = menuRef.getBoundingClientRect(); + ipcRenderer.send(`show-menu-dialog-${store.windowId}`, right, bottom); + //} +}; + +ipcRenderer.on('show-add-bookmark-dialog', () => { + showAddBookmarkDialog(); +}); + +ipcRenderer.on('show-menu-dialog', () => { + showMenuDialog(); +}); + +const onStarClick = (e: React.MouseEvent) => { + showAddBookmarkDialog(); +}; + +const onMenuClick = async () => { + showMenuDialog(); +}; + +const BrowserActions = observer(() => { + const { selectedTabId } = store.tabs; + + return ( + <> + {selectedTabId && + store.extensions.browserActions.map((item) => { + if (item.tabId === selectedTabId) { + return ; + } + return null; + })} + + ); +}); + +const onShieldContextMenu = (e: React.MouseEvent) => { + const menu = remote.Menu.buildFromTemplate([ + { + checked: store.settings.object.shield, + label: 'Enabled', + type: 'checkbox', + click: () => { + store.settings.object.shield = !store.settings.object.shield; + store.settings.save(); + }, + }, + ]); + + menu.popup(); +}; + +const RightButtons = observer(() => { + const { selectedTab } = store.tabs; + + let blockedAds = 0; + + if (selectedTab) { + blockedAds = selectedTab.blockedAds; + } + + return ( + + + {store.extensions.browserActions.length > 0 && } + + 0} + badgeText={blockedAds.toString()} + icon={ICON_SHIELD} + opacity={store.settings.object.shield ? 0.87 : 0.54} + onContextMenu={onShieldContextMenu} + > + + {store.downloadsButtonVisible && ( + + )} + {store.isIncognito && } + (menuRef = r)} + toggled={store.dialogsVisibility['menu']} + badge={store.updateAvailable} + badgeRight={10} + badgeTop={6} + onMouseDown={onMenuClick} + icon={ICON_MORE} + size={18} + /> + + ); +}); + +let mouseUpped = false; + +const onMouseDown = (e: React.MouseEvent) => { + store.addressbarTextVisible = false; + store.addressbarFocused = true; +}; + +const onFocus = (e: React.FocusEvent) => { + store.addressbarTextVisible = false; + store.addressbarFocused = true; + + if (store.tabs.selectedTab) { + store.tabs.selectedTab.addressbarFocused = true; + } +}; + +const onSelect = (e: React.MouseEvent) => { + if (store.tabs.selectedTab) { + store.tabs.selectedTab.addressbarSelectionRange = [ + e.currentTarget.selectionStart, + e.currentTarget.selectionEnd, + ]; + } +}; + +const onMouseUp = (e: React.MouseEvent) => { + if (window.getSelection().toString().length === 0 && !mouseUpped) { + e.currentTarget.select(); + } + + mouseUpped = true; +}; + +const onKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Escape' || e.key === 'Enter') { + store.tabs.selectedTab.addressbarValue = null; + } + + if (e.key === 'Escape') { + const target = e.currentTarget; + requestAnimationFrame(() => { + target.select(); + }); + } + + if (e.key === 'Enter') { + store.addressbarFocused = false; + e.currentTarget.blur(); + const { value } = e.currentTarget; + let url = value; + + if (isURL(value)) { + url = value.indexOf('://') === -1 ? `http://${value}` : value; + } else { + url = store.settings.searchEngine.url.replace('%s', value); + } + + store.tabs.selectedTab.addressbarValue = url; + callViewMethod(store.tabs.selectedTabId, 'loadURL', url); + } +}; + +const addressbarRef = React.createRef(); + +const onChange = (e: React.ChangeEvent) => { + store.tabs.selectedTab.addressbarValue = e.currentTarget.value; + + const { left, width } = addressbarRef.current.getBoundingClientRect(); + + if (e.currentTarget.value.trim() !== '') { + ipcRenderer.send(`search-show-${store.windowId}`, { + text: e.currentTarget.value, + cursorPos: e.currentTarget.selectionStart, + x: left, + width: width, + }); + store.addressbarEditing = true; + } +}; + +const onBlur = (e: React.FocusEvent) => { + e.currentTarget.blur(); + window.getSelection().removeAllRanges(); + store.addressbarTextVisible = true; + store.addressbarFocused = false; + mouseUpped = false; + + if (store.tabs.selectedTab) { + store.tabs.selectedTab.addressbarFocused = false; + } +}; + +export const Toolbar = observer(() => { + const { selectedTab } = store.tabs; + + let hasCredentials = false; + + if (selectedTab) { + hasCredentials = selectedTab.hasCredentials; + } + + return ( + + + + + + + + {store.addressbarUrlSegments.map((item, key) => ( +
+ {item.value} +
+ ))} +
+
+ + {hasCredentials && ( + + )} + (starRef = r)} + toggled={store.dialogsVisibility['add-bookmark']} + icon={store.isBookmarked ? ICON_STAR_FILLED : ICON_STAR} + size={18} + dense + onMouseDown={onStarClick} + /> +
+ +
+ ); +}); diff --git a/src/renderer/views/search/components/App/index.tsx b/src/renderer/views/search/components/App/index.tsx index ca0ee2483..e03cf7be8 100644 --- a/src/renderer/views/search/components/App/index.tsx +++ b/src/renderer/views/search/components/App/index.tsx @@ -57,10 +57,12 @@ const onKeyDown = (e: React.KeyboardEvent) => { suggestions.selected--; } - let suggestion = list.find(x => x.id === suggestions.selected); + let suggestion = list.find((x) => x.id === suggestions.selected); if (!suggestion) { - suggestion = store.searchedTabs.find(x => x.id === suggestions.selected); + suggestion = store.searchedTabs.find( + (x) => x.id === suggestions.selected, + ); } input.value = suggestion.isSearch ? suggestion.primaryText : suggestion.url; @@ -135,7 +137,6 @@ export const App = hot( onInput={onInput} ref={store.inputRef} onKeyPress={onKeyPress} - placeholder="Search or type in a URL" > diff --git a/src/renderer/views/search/store/index.ts b/src/renderer/views/search/store/index.ts index dd5b824cc..eac519a70 100644 --- a/src/renderer/views/search/store/index.ts +++ b/src/renderer/views/search/store/index.ts @@ -4,7 +4,6 @@ import { ipcRenderer } from 'electron'; import { observable, computed } from 'mobx'; import { ISuggestion, IVisitedItem } from '~/interfaces'; import { SuggestionsStore } from './suggestions'; -import { NEWTAB_URL } from '~/constants/tabs'; import { DialogStore } from '~/models/dialog-store'; let lastSuggestion: string; @@ -40,11 +39,11 @@ export class Store extends DialogStore { return this.tabs .filter( - tab => + (tab) => tab.title.indexOf(this.inputText) !== -1 || tab.url.indexOf(this.inputText) !== -1, ) - .map(tab => ({ + .map((tab) => ({ primaryText: tab.url, secondaryText: tab.title, id: id++, @@ -74,7 +73,6 @@ export class Store extends DialogStore { if (visible) { this.tabs = []; - this.suggestions.list = []; this.tabId = data.id; this.canSuggest = this.inputText.length <= data.text.length; @@ -142,7 +140,7 @@ export class Store extends DialogStore { this.autoComplete(input.value, lastSuggestion); } - suggestions.load(input).then(suggestion => { + suggestions.load(input).then((suggestion) => { lastSuggestion = suggestion; if (this.canSuggest) { this.autoComplete( diff --git a/webpack.config.base.js b/webpack.config.base.js index 3d5af37a5..f319801a2 100644 --- a/webpack.config.base.js +++ b/webpack.config.base.js @@ -64,7 +64,7 @@ const config = { loader: 'ts-loader', options: { experimentalWatchApi: dev, - transpileOnly: dev, + transpileOnly: true, // TODO: dev getCustomTransformers: () => ({ before: [styledComponentsTransformer], }), From 377739f20d6571e5de59e514e553f865dd9f831a Mon Sep 17 00:00:00 2001 From: sentialx Date: Thu, 9 Apr 2020 18:21:33 +0200 Subject: [PATCH 03/21] fix: showing menu dialog --- src/main/dialogs/menu.ts | 58 +++++++----------- src/main/services/messaging.ts | 16 ++--- src/renderer/views/menu/store/index.ts | 82 ++++++++++++++------------ 3 files changed, 67 insertions(+), 89 deletions(-) diff --git a/src/main/dialogs/menu.ts b/src/main/dialogs/menu.ts index 0f6deba73..479afd464 100644 --- a/src/main/dialogs/menu.ts +++ b/src/main/dialogs/menu.ts @@ -1,39 +1,21 @@ -import { AppWindow } from '../windows'; -import { - MENU_WIDTH, - DIALOG_MARGIN, - DIALOG_MARGIN_TOP, -} from '~/constants/design'; -import { Dialog } from '.'; +import { BrowserWindow } from 'electron'; +import { Application } from '../application'; +import { DIALOG_MARGIN_TOP, DIALOG_MARGIN } from '~/constants/design'; -const WIDTH = MENU_WIDTH; -const HEIGHT = 550; - -export class MenuDialog extends Dialog { - public visible = false; - public left = 0; - public top = 0; - - constructor(appWindow: AppWindow) { - super(appWindow, { - name: 'menu', - bounds: { - width: WIDTH, - height: HEIGHT, - }, - devtools: false, - }); - } - - public rearrange() { - super.rearrange({ - x: Math.round(this.left - WIDTH + DIALOG_MARGIN), - y: Math.round(this.top - DIALOG_MARGIN_TOP), - }); - } - - public async show() { - await super.show(); - this.send('visible', true); - } -} +export const showMenuDialog = ( + browserWindow: BrowserWindow, + x: number, + y: number, +) => { + const menuWidth = 330; + Application.instance.dialogs.show({ + name: 'menu', + browserWindow: browserWindow, + bounds: { + width: menuWidth, + height: 470, + x: x - menuWidth + DIALOG_MARGIN, + y: y - DIALOG_MARGIN_TOP, + }, + }); +}; diff --git a/src/main/services/messaging.ts b/src/main/services/messaging.ts index 40e848cde..a214ca0fa 100644 --- a/src/main/services/messaging.ts +++ b/src/main/services/messaging.ts @@ -1,6 +1,8 @@ import { ipcMain } from 'electron'; import { AppWindow } from '../windows'; import { Application } from '../application'; +import { DIALOG_MARGIN_TOP, DIALOG_MARGIN } from '~/constants/design'; +import { showMenuDialog } from '../dialogs/menu'; export const runMessagingService = (appWindow: AppWindow) => { const { id } = appWindow; @@ -30,18 +32,8 @@ export const runMessagingService = (appWindow: AppWindow) => { appWindow.fixDragging(); }); - ipcMain.on(`show-menu-dialog-${id}`, (e, left, top) => { - Application.instance.dialogs.show({ - name: 'menu', - browserWindow: appWindow.win, - bounds: { - width: 400, - height: 600, - x: left - 400, - y: top, - }, - devtools: true, - }); + ipcMain.on(`show-menu-dialog-${id}`, (e, x, y) => { + showMenuDialog(appWindow.win, x, y); }); ipcMain.on(`search-show-${id}`, (e, data) => { diff --git a/src/renderer/views/menu/store/index.ts b/src/renderer/views/menu/store/index.ts index 6be9fb692..f1049d5b4 100644 --- a/src/renderer/views/menu/store/index.ts +++ b/src/renderer/views/menu/store/index.ts @@ -1,39 +1,43 @@ -import { ipcRenderer, remote } from 'electron'; -import { observable } from 'mobx'; -import { DialogStore } from '~/models/dialog-store'; - -export class Store extends DialogStore { - @observable - public alwaysOnTop = false; - - @observable - public updateAvailable = false; - - public constructor() { - super(); - - ipcRenderer.on('update-available', () => { - this.updateAvailable = true; - }); - } - - public async onVisibilityChange(visible: boolean) { - this.visible = visible; - - if (visible) { - if (remote.getCurrentWindow()) { - this.alwaysOnTop = remote.getCurrentWindow().isAlwaysOnTop(); - } - - this.updateAvailable = await ipcRenderer.invoke('is-update-available'); - } - } - - public async save() { - ipcRenderer.send('save-settings', { - settings: JSON.stringify(this.settings), - }); - } -} - -export default new Store(); +import { ipcRenderer, remote } from 'electron'; +import { observable } from 'mobx'; +import { DialogStore } from '~/models/dialog-store'; + +export class Store extends DialogStore { + @observable + public alwaysOnTop = false; + + @observable + public updateAvailable = false; + + public constructor() { + super(); + + requestAnimationFrame(() => { + this.visible = true; + }); + + ipcRenderer.on('update-available', () => { + this.updateAvailable = true; + }); + } + + public async onVisibilityChange(visible: boolean) { + this.visible = visible; + + if (visible) { + if (remote.getCurrentWindow()) { + this.alwaysOnTop = remote.getCurrentWindow().isAlwaysOnTop(); + } + + this.updateAvailable = await ipcRenderer.invoke('is-update-available'); + } + } + + public async save() { + ipcRenderer.send('save-settings', { + settings: JSON.stringify(this.settings), + }); + } +} + +export default new Store(); From f1119297f9a08b9227893d8020348c4337b45fe3 Mon Sep 17 00:00:00 2001 From: sentialx Date: Thu, 9 Apr 2020 18:53:05 +0200 Subject: [PATCH 04/21] fix: syncing settings between dialogs --- src/main/models/settings.ts | 8 ++------ src/main/services/dialogs-service.ts | 12 ++++++++++++ src/main/view-manager.ts | 4 +++- src/main/windows/app.ts | 7 +------ 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/main/models/settings.ts b/src/main/models/settings.ts index db9d04bf2..4ae0b1ebd 100644 --- a/src/main/models/settings.ts +++ b/src/main/models/settings.ts @@ -85,15 +85,11 @@ export class Settings extends EventEmitter { this.object.theme === 'wexond-dark' ? 'dark' : 'light'; } + Application.instance.dialogs.sendToAll('update-settings', this.object); + for (const window of Application.instance.windows.list) { window.send('update-settings', this.object); - /* - // TODO: dialogs - Object.values(window.dialogs).forEach((dialog) => { - dialog.send('update-settings', this.object); - });*/ - window.viewManager.views.forEach(async (v) => { if (v.webContents.getURL().startsWith(WEBUI_BASE_URL)) { v.webContents.send('update-settings', this.object); diff --git a/src/main/services/dialogs-service.ts b/src/main/services/dialogs-service.ts index 7c29c5c03..1045ff454 100644 --- a/src/main/services/dialogs-service.ts +++ b/src/main/services/dialogs-service.ts @@ -119,4 +119,16 @@ export class DialogsService { return dialog; } + + public getBrowserViews = () => { + return this.browserViews.concat([this.searchBox.browserView]); + }; + + public destroy = () => { + this.getBrowserViews().forEach((x) => x.destroy()); + }; + + public sendToAll = (channel: string, ...args: any[]) => { + this.getBrowserViews().forEach((x) => x.webContents.send(channel, ...args)); + }; } diff --git a/src/main/view-manager.ts b/src/main/view-manager.ts index d90a26599..b69aa00d2 100644 --- a/src/main/view-manager.ts +++ b/src/main/view-manager.ts @@ -130,7 +130,9 @@ export class ViewManager { this.window.webContents.focus(); } - /*this.window.dialogs.previewDialog.hide(true); + /* + TODO: dialogs + this.window.dialogs.previewDialog.hide(true); [ 'findDialog', diff --git a/src/main/windows/app.ts b/src/main/windows/app.ts index 53f8f95c1..6ddd1a629 100644 --- a/src/main/windows/app.ts +++ b/src/main/windows/app.ts @@ -139,12 +139,7 @@ export class AppWindow { this.win.setBrowserView(null); - /*Object.keys(this.dialogs).forEach((key) => { - if (this.dialogs[key]) { - this.dialogs[key].destroy(); - } - this.dialogs[key] = null; - });*/ + Application.instance.dialogs.destroy(); this.viewManager.clear(); From fa633636e6f28fc2fed74afde2081279e056143d Mon Sep 17 00:00:00 2001 From: sentialx Date: Thu, 9 Apr 2020 19:12:42 +0200 Subject: [PATCH 05/21] feat: animation on dialog load --- src/renderer/mixins/dialogs.ts | 80 +++++++++++-------- .../views/menu/components/App/index.tsx | 2 +- .../views/menu/components/App/style.ts | 40 +++++----- src/renderer/views/menu/store/index.ts | 4 +- 4 files changed, 70 insertions(+), 56 deletions(-) diff --git a/src/renderer/mixins/dialogs.ts b/src/renderer/mixins/dialogs.ts index a5cdbe435..965d01bc0 100644 --- a/src/renderer/mixins/dialogs.ts +++ b/src/renderer/mixins/dialogs.ts @@ -1,32 +1,48 @@ -import { ITheme } from '~/interfaces'; -import styled, { css } from 'styled-components'; - -export const DIALOG_TRANSITION = `0.2s opacity`; - -export const DIALOG_BOX_SHADOW = - '0 12px 16px rgba(0, 0, 0, 0.12), 0 8px 10px rgba(0, 0, 0, 0.16)'; - -export const DIALOG_BORDER_RADIUS = '4'; - -export const DialogStyle = styled.div` - margin: 16px; - margin-top: 3px; - box-shadow: ${DIALOG_BOX_SHADOW}; - border-radius: ${DIALOG_BORDER_RADIUS}px; - overflow: hidden; - position: relative; - - ${({ - visible, - theme, - hideTransition, - }: { - visible: boolean; - theme?: ITheme; - hideTransition?: boolean; - }) => css` - transition: ${!visible && !hideTransition ? 'none' : DIALOG_TRANSITION}; - opacity: ${visible ? 1 : 0}; - background-color: ${theme['dialog.backgroundColor']}; - `} -`; +import { ITheme } from '~/interfaces'; +import styled, { css } from 'styled-components'; + +export const DIALOG_TRANSITION = `0.2s opacity`; + +export const DIALOG_BOX_SHADOW = + '0 12px 16px rgba(0, 0, 0, 0.12), 0 8px 10px rgba(0, 0, 0, 0.16)'; + +export const DIALOG_BORDER_RADIUS = '4'; + +export const DialogBaseStyle = styled.div` + margin: 16px; + margin-top: 3px; + box-shadow: ${DIALOG_BOX_SHADOW}; + border-radius: ${DIALOG_BORDER_RADIUS}px; + overflow: hidden; + position: relative; + + ${({ theme }: { theme?: ITheme }) => css` + background-color: ${theme['dialog.backgroundColor']}; + `} +`; + +export const DialogStyle = styled(DialogBaseStyle)` + @keyframes fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } + } + + animation: 0.15s ease-out 0s 1 fadeIn; +`; + +export const PersistentDialogStyle = styled(DialogBaseStyle)` + ${({ + visible, + hideTransition, + }: { + visible: boolean; + hideTransition?: boolean; + }) => css` + transition: ${!visible && !hideTransition ? 'none' : DIALOG_TRANSITION}; + opacity: ${visible ? 1 : 0}; + `} +`; diff --git a/src/renderer/views/menu/components/App/index.tsx b/src/renderer/views/menu/components/App/index.tsx index af6ba1f5e..7d138202d 100644 --- a/src/renderer/views/menu/components/App/index.tsx +++ b/src/renderer/views/menu/components/App/index.tsx @@ -14,7 +14,7 @@ export const App = hot( - + diff --git a/src/renderer/views/menu/components/App/style.ts b/src/renderer/views/menu/components/App/style.ts index f5cec9a07..b5e6eca61 100644 --- a/src/renderer/views/menu/components/App/style.ts +++ b/src/renderer/views/menu/components/App/style.ts @@ -1,20 +1,20 @@ -import styled from 'styled-components'; -import { DialogStyle } from '~/renderer/mixins/dialogs'; - -export const StyledApp = styled(DialogStyle)``; - -export const Title = styled.div` - font-size: 16px; -`; - -export const Subtitle = styled.div` - font-size: 13px; - opacity: 0.54; - margin-top: 8px; -`; - -export const Buttons = styled.div` - display: flex; - margin-top: 16px; - float: right; -`; +import styled from 'styled-components'; +import { DialogStyle } from '~/renderer/mixins/dialogs'; + +export const StyledApp = styled(DialogStyle)``; + +export const Title = styled.div` + font-size: 16px; +`; + +export const Subtitle = styled.div` + font-size: 13px; + opacity: 0.54; + margin-top: 8px; +`; + +export const Buttons = styled.div` + display: flex; + margin-top: 16px; + float: right; +`; diff --git a/src/renderer/views/menu/store/index.ts b/src/renderer/views/menu/store/index.ts index f1049d5b4..562d7b0ad 100644 --- a/src/renderer/views/menu/store/index.ts +++ b/src/renderer/views/menu/store/index.ts @@ -12,9 +12,7 @@ export class Store extends DialogStore { public constructor() { super(); - requestAnimationFrame(() => { - this.visible = true; - }); + this.visible = true; ipcRenderer.on('update-available', () => { this.updateAvailable = true; From 80709b483ad7c1cf2ee80443a6c264c0252d8fc0 Mon Sep 17 00:00:00 2001 From: sentialx Date: Thu, 9 Apr 2020 20:20:04 +0200 Subject: [PATCH 06/21] fix: dialogs visibility check --- src/main/services/dialogs-service.ts | 8 ++++++++ src/main/services/messaging.ts | 8 ++++---- src/renderer/views/app/components/Toolbar/index.tsx | 9 ++++----- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/main/services/dialogs-service.ts b/src/main/services/dialogs-service.ts index 1045ff454..91374efb2 100644 --- a/src/main/services/dialogs-service.ts +++ b/src/main/services/dialogs-service.ts @@ -67,6 +67,8 @@ export class DialogsService { browserView = this.createBrowserView(); } + browserWindow.webContents.send('dialog-visibility-change', name, true); + bounds.x = Math.round(bounds.x); bounds.y = Math.round(bounds.y); @@ -92,6 +94,8 @@ export class DialogsService { id: browserView.id, name, hide: () => { + browserWindow.webContents.send('dialog-visibility-change', name, false); + ipcMain.removeAllListeners(`hide-${browserView.webContents.id}`); this.dialogs = this.dialogs.filter((x) => x.id !== dialog.id); @@ -131,4 +135,8 @@ export class DialogsService { public sendToAll = (channel: string, ...args: any[]) => { this.getBrowserViews().forEach((x) => x.webContents.send(channel, ...args)); }; + + public isVisible = (name: string) => { + return this.dialogs.find((x) => x.name === name); + }; } diff --git a/src/main/services/messaging.ts b/src/main/services/messaging.ts index a214ca0fa..5fc860cf5 100644 --- a/src/main/services/messaging.ts +++ b/src/main/services/messaging.ts @@ -41,6 +41,10 @@ export const runMessagingService = (appWindow: AppWindow) => { Application.instance.dialogs.searchBox.show(appWindow.win); }); + ipcMain.handle(`is-dialog-visible-${id}`, (e, dialog) => { + return Application.instance.dialogs.isVisible(dialog); + }); + /*ipcMain.on(`find-show-${id}`, () => { appWindow.dialogs.findDialog.show(); }); @@ -51,10 +55,6 @@ export const runMessagingService = (appWindow: AppWindow) => { appWindow.dialogs.menuDialog.show(); }); - ipcMain.handle(`is-dialog-visible-${id}`, (e, dialog) => { - return appWindow.dialogs[dialog].visible; - }); - ipcMain.on(`search-show-${id}`, (e, data) => { appWindow.dialogs.searchDialog.data = data; appWindow.dialogs.searchDialog.show(); diff --git a/src/renderer/views/app/components/Toolbar/index.tsx b/src/renderer/views/app/components/Toolbar/index.tsx index 9afd0a2a1..fc8ea44e8 100644 --- a/src/renderer/views/app/components/Toolbar/index.tsx +++ b/src/renderer/views/app/components/Toolbar/index.tsx @@ -66,11 +66,10 @@ const showAddBookmarkDialog = async () => { }; const showMenuDialog = async () => { - // TODO: dialogs - //if (!(await isDialogVisible('menuDialog'))) { - const { right, bottom } = menuRef.getBoundingClientRect(); - ipcRenderer.send(`show-menu-dialog-${store.windowId}`, right, bottom); - //} + if (!(await isDialogVisible('menu'))) { + const { right, bottom } = menuRef.getBoundingClientRect(); + ipcRenderer.send(`show-menu-dialog-${store.windowId}`, right, bottom); + } }; ipcRenderer.on('show-add-bookmark-dialog', () => { From 1de5eadd1397f404441845769ae375d41155b995 Mon Sep 17 00:00:00 2001 From: sentialx Date: Fri, 1 May 2020 19:28:28 +0200 Subject: [PATCH 07/21] fix: preview dialog --- src/main/dialogs/dialog.ts | 3 +- src/main/dialogs/index.ts | 14 ----- src/main/dialogs/preview.ts | 51 ++++++++----------- src/main/dialogs/search.ts | 5 -- src/main/services/dialogs-service.ts | 31 +++++++++-- src/main/services/messaging.ts | 26 +++++++++- src/main/windows-service.ts | 5 ++ .../views/preview/components/App/style.ts | 4 +- 8 files changed, 81 insertions(+), 58 deletions(-) delete mode 100644 src/main/dialogs/index.ts diff --git a/src/main/dialogs/dialog.ts b/src/main/dialogs/dialog.ts index a3ae5979f..9b2a3f9b4 100644 --- a/src/main/dialogs/dialog.ts +++ b/src/main/dialogs/dialog.ts @@ -30,9 +30,10 @@ export class PersistentDialog { height: 0, }; + public name: string; + private timeout: any; private hideTimeout: number; - private name: string; private loaded = false; private showCallback: any = null; diff --git a/src/main/dialogs/index.ts b/src/main/dialogs/index.ts deleted file mode 100644 index bb8752254..000000000 --- a/src/main/dialogs/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -export * from './dialog'; -export * from './menu'; -export * from './find'; -export * from './search'; -export * from './auth'; -export * from './permissions'; -export * from './form-fill'; -export * from './credentials'; -export * from './preview'; -export * from './downloads'; -export * from './tabgroup'; -export * from './add-bookmark'; -export * from './extension-popup'; -export * from './zoom'; diff --git a/src/main/dialogs/preview.ts b/src/main/dialogs/preview.ts index 63c337a8a..c7d3a99aa 100644 --- a/src/main/dialogs/preview.ts +++ b/src/main/dialogs/preview.ts @@ -1,21 +1,22 @@ -import { AppWindow } from '../windows'; -import { MENU_WIDTH, TITLEBAR_HEIGHT } from '~/constants/design'; -import { Dialog } from '.'; +import { BrowserWindow } from 'electron'; +import { Application } from '../application'; +import { + DIALOG_MARGIN_TOP, + DIALOG_MARGIN, + TITLEBAR_HEIGHT, +} from '~/constants/design'; +import { PersistentDialog } from './dialog'; -const WIDTH = MENU_WIDTH; const HEIGHT = 256; -export class PreviewDialog extends Dialog { +export class PreviewDialog extends PersistentDialog { public visible = false; public tab: { id?: number; x?: number } = {}; - private timeout1: any; - - constructor(appWindow: AppWindow) { - super(appWindow, { + constructor() { + super({ name: 'preview', bounds: { - width: appWindow.win.getBounds().width, height: HEIGHT, y: TITLEBAR_HEIGHT, }, @@ -24,24 +25,21 @@ export class PreviewDialog extends Dialog { } public rearrange() { - const { width } = this.appWindow.win.getContentBounds(); + const { width } = this.browserWindow.getContentBounds(); super.rearrange({ width }); } - public rearrangeDialogs(toggle: boolean) { - this.appWindow.dialogs.searchDialog.rearrangePreview(toggle); - this.appWindow.dialogs.findDialog.rearrangePreview(toggle); - } - - public async show() { - clearTimeout(this.timeout1); - this.rearrangeDialogs(true); - - super.show(false); + public async show(browserWindow: BrowserWindow) { + super.show(browserWindow, false); - const { id, url, title, errorURL } = this.appWindow.viewManager.views.get( - this.tab.id, - ); + const { + id, + url, + title, + errorURL, + } = Application.instance.windows + .fromBrowserWindow(browserWindow) + .viewManager.views.get(this.tab.id); this.send('visible', true, { id, @@ -52,11 +50,6 @@ export class PreviewDialog extends Dialog { } public hide(bringToTop = true) { - clearTimeout(this.timeout1); - this.timeout1 = setTimeout(() => { - this.rearrangeDialogs(false); - }, 210); - super.hide(bringToTop); } } diff --git a/src/main/dialogs/search.ts b/src/main/dialogs/search.ts index 8e39e0972..9ce746a08 100644 --- a/src/main/dialogs/search.ts +++ b/src/main/dialogs/search.ts @@ -12,7 +12,6 @@ const WIDTH = 800; const HEIGHT = 80; export class SearchDialog extends PersistentDialog { - private lastHeight = 0; private isPreviewVisible = false; public data = { @@ -57,10 +56,6 @@ export class SearchDialog extends PersistentDialog { } public async show(browserWindow: BrowserWindow) { - /*if (this.appWindow.dialogs.previewDialog.visible) { - this.appWindow.dialogs.previewDialog.hide(true); - }*/ - super.show(browserWindow, true, false); this.send('visible', true, { diff --git a/src/main/services/dialogs-service.ts b/src/main/services/dialogs-service.ts index 91374efb2..800cd0d67 100644 --- a/src/main/services/dialogs-service.ts +++ b/src/main/services/dialogs-service.ts @@ -1,6 +1,8 @@ -import { BrowserView, app, ipcMain } from 'electron'; +import { BrowserView, app, ipcMain, Dialog } from 'electron'; import { join } from 'path'; import { SearchDialog } from '../dialogs/search'; +import { PreviewDialog } from '../dialogs/preview'; +import { PersistentDialog } from '../dialogs/dialog'; interface IDialogShowOptions { name: string; @@ -8,6 +10,7 @@ interface IDialogShowOptions { bounds: Electron.Rectangle; hideTimeout?: number; devtools?: boolean; + onHide?: (dialog: IDialog) => void; } interface IDialog { @@ -22,12 +25,13 @@ export class DialogsService { public browserViewDetails = new Map(); public dialogs: IDialog[] = []; - public searchBox: SearchDialog; + public persistentDialogs: PersistentDialog[] = []; public run() { this.createBrowserView(); - this.searchBox = new SearchDialog(); + this.persistentDialogs.push(new SearchDialog()); + this.persistentDialogs.push(new PreviewDialog()); } private createBrowserView() { @@ -54,6 +58,7 @@ export class DialogsService { browserWindow, bounds, devtools, + onHide, hideTimeout, }: IDialogShowOptions): IDialog { const foundDialog = this.dialogs.find((x) => x.name === name); @@ -110,6 +115,8 @@ export class DialogsService { browserView.webContents.loadURL('about:blank'); this.browserViewDetails.set(browserView.id, false); } + + if (onHide) onHide(dialog); }, }; @@ -125,7 +132,9 @@ export class DialogsService { } public getBrowserViews = () => { - return this.browserViews.concat([this.searchBox.browserView]); + return this.browserViews.concat( + Array.from(this.persistentDialogs).map((x) => x.browserView), + ); }; public destroy = () => { @@ -136,7 +145,19 @@ export class DialogsService { this.getBrowserViews().forEach((x) => x.webContents.send(channel, ...args)); }; - public isVisible = (name: string) => { + public get(name: string) { + return this.getDynamic(name) || this.getPersistent(name); + } + + public getDynamic(name: string) { return this.dialogs.find((x) => x.name === name); + } + + public getPersistent(name: string) { + return this.persistentDialogs.find((x) => x.name === name); + } + + public isVisible = (name: string) => { + return this.getDynamic(name) || this.getPersistent(name)?.visible; }; } diff --git a/src/main/services/messaging.ts b/src/main/services/messaging.ts index 9321460d6..272201709 100644 --- a/src/main/services/messaging.ts +++ b/src/main/services/messaging.ts @@ -3,6 +3,10 @@ import { AppWindow } from '../windows'; import { Application } from '../application'; import { DIALOG_MARGIN_TOP, DIALOG_MARGIN } from '~/constants/design'; import { showMenuDialog } from '../dialogs/menu'; +import { showTabPreviewDialog, PreviewDialog } from '../dialogs/preview'; +import { parse } from 'url'; +import { IFormFillData } from '~/interfaces'; +import { SearchDialog } from '../dialogs/search'; export const runMessagingService = (appWindow: AppWindow) => { const { id } = appWindow; @@ -37,14 +41,32 @@ export const runMessagingService = (appWindow: AppWindow) => { }); ipcMain.on(`search-show-${id}`, (e, data) => { - Application.instance.dialogs.searchBox.data = data; - Application.instance.dialogs.searchBox.show(appWindow.win); + const dialog = Application.instance.dialogs.getPersistent( + 'search', + ) as SearchDialog; + dialog.data = data; + dialog.show(appWindow.win); }); ipcMain.handle(`is-dialog-visible-${id}`, (e, dialog) => { return Application.instance.dialogs.isVisible(dialog); }); + ipcMain.on(`show-tab-preview-${id}`, (e, tab) => { + const dialog = Application.instance.dialogs.getPersistent( + 'preview', + ) as PreviewDialog; + dialog.tab = tab; + dialog.show(appWindow.win); + }); + + ipcMain.on(`hide-tab-preview-${id}`, (e, tab) => { + const dialog = Application.instance.dialogs.getPersistent( + 'preview', + ) as PreviewDialog; + dialog.hide(); + }); + /*ipcMain.on(`find-show-${id}`, () => { appWindow.dialogs.findDialog.show(); }); diff --git a/src/main/windows-service.ts b/src/main/windows-service.ts index 60ac68fd7..9fc1aefe4 100644 --- a/src/main/windows-service.ts +++ b/src/main/windows-service.ts @@ -1,5 +1,6 @@ import { AppWindow } from './windows/app'; import { extensions } from 'electron-extensions'; +import { BrowserWindow } from 'electron'; export class WindowsService { public list: AppWindow[] = []; @@ -49,4 +50,8 @@ export class WindowsService { public findByBrowserView(webContentsId: number) { return this.list.find((x) => !!x.viewManager.views.get(webContentsId)); } + + public fromBrowserWindow(browserWindow: BrowserWindow) { + return this.list.find((x) => x.id === browserWindow.id); + } } diff --git a/src/renderer/views/preview/components/App/style.ts b/src/renderer/views/preview/components/App/style.ts index fce2f3137..d7781e90f 100644 --- a/src/renderer/views/preview/components/App/style.ts +++ b/src/renderer/views/preview/components/App/style.ts @@ -2,9 +2,9 @@ import styled, { css } from 'styled-components'; import { ITheme } from '~/interfaces'; import { maxLines } from '~/renderer/mixins'; import { TAB_MAX_WIDTH } from '~/renderer/views/app/constants/tabs'; -import { DialogStyle } from '~/renderer/mixins/dialogs'; +import { PersistentDialogStyle } from '~/renderer/mixins/dialogs'; -export const StyledApp = styled(DialogStyle)` +export const StyledApp = styled(PersistentDialogStyle)` margin: 0; padding: 12px; font-size: 13px; From 7f37b82ccab1dda79091987db84903732ef34dd2 Mon Sep 17 00:00:00 2001 From: sentialx Date: Fri, 1 May 2020 20:25:26 +0200 Subject: [PATCH 08/21] fix --- src/main/view-manager.ts | 4 ++-- src/main/view.ts | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/view-manager.ts b/src/main/view-manager.ts index d7364f42c..bc4109e48 100644 --- a/src/main/view-manager.ts +++ b/src/main/view-manager.ts @@ -227,10 +227,10 @@ export class ViewManager { } } - public emitZoomUpdate(showDialog: boolean = true) { + public emitZoomUpdate(showDialog = true) { this.window.dialogs.zoomDialog.send( 'zoom-factor-updated', - this.selected.webContents.zoomFactor + this.selected.webContents.zoomFactor, ); this.window.webContents.send( 'zoom-factor-updated', diff --git a/src/main/view.ts b/src/main/view.ts index 8441e414d..eac17de0e 100644 --- a/src/main/view.ts +++ b/src/main/view.ts @@ -32,6 +32,8 @@ export class View { public bookmark: IBookmark; + public openDialogs: string[] = []; + private historyQueue = new Queue(); private lastUrl = ''; From 0f1f70154e7c422225b7cae2552c41b688968d31 Mon Sep 17 00:00:00 2001 From: sentialx Date: Sat, 2 May 2020 01:29:58 +0200 Subject: [PATCH 09/21] feat: find dialog --- src/main/dialogs/find.ts | 73 ++++++------ src/main/dialogs/menu.ts | 2 +- src/main/services/dialogs-service.ts | 138 ++++++++++++++++++---- src/main/services/messaging.ts | 125 ++++++++++---------- src/main/view-manager.ts | 29 ++--- src/main/view.ts | 9 +- src/models/dialog-store.ts | 55 ++++----- src/renderer/views/find/store/index.ts | 70 ++++++----- src/renderer/views/preview/store/index.ts | 2 +- src/renderer/views/search/store/index.ts | 1 + 10 files changed, 299 insertions(+), 205 deletions(-) diff --git a/src/main/dialogs/find.ts b/src/main/dialogs/find.ts index 15f8cceb1..595c6176a 100644 --- a/src/main/dialogs/find.ts +++ b/src/main/dialogs/find.ts @@ -1,45 +1,38 @@ -import { AppWindow } from '../windows'; -import { DIALOG_MIN_HEIGHT, VIEW_Y_OFFSET } from '~/constants/design'; -import { Dialog } from '.'; -import { ipcMain } from 'electron'; +import { VIEW_Y_OFFSET } from '~/constants/design'; +import { BrowserWindow } from 'electron'; +import { Application } from '../application'; -const WIDTH = 416; -const HEIGHT = 70; +export const showFindDialog = (browserWindow: BrowserWindow) => { + const { width } = browserWindow.getContentBounds(); + const appWindow = Application.instance.windows.fromBrowserWindow( + browserWindow, + ); + const selectedTab = appWindow.viewManager.selected; -export class FindDialog extends Dialog { - public constructor(appWindow: AppWindow) { - super(appWindow, { - name: 'find', - bounds: { - width: WIDTH, - height: HEIGHT, - y: VIEW_Y_OFFSET, - }, - }); + const dialog = Application.instance.dialogs.show({ + name: 'find', + browserWindow, + associateTab: true, + bounds: { + width: 416, + height: 70, + x: width - 416, + y: VIEW_Y_OFFSET, + }, + onVisibilityChange: (visible, tabId) => { + const tab = appWindow.viewManager.views.get(tabId); + return { tabId: tab.id, ...tab.findInfo }; + }, + }); - ipcMain.on(`show-${this.id}`, () => { - this.show(); - }); - } + if (!dialog) return; - public rearrangePreview(toggle: boolean) { - super.rearrange({ - height: toggle ? DIALOG_MIN_HEIGHT : HEIGHT, - }); - } + dialog.handle('fetch', () => { + return { tabId: selectedTab.id, ...selectedTab.findInfo }; + }); - public async show() { - super.show(); - - const tabId = this.appWindow.viewManager.selectedId; - this.tabIds.push(tabId); - this.send('visible', true, tabId); - } - - public rearrange() { - const { width } = this.appWindow.win.getContentBounds(); - super.rearrange({ - x: width - WIDTH, - }); - } -} + dialog.on('set-find-info', (e, tabId, info) => { + const tab = appWindow.viewManager.views.get(tabId); + tab.findInfo = info; + }); +}; diff --git a/src/main/dialogs/menu.ts b/src/main/dialogs/menu.ts index 479afd464..f76e98ccb 100644 --- a/src/main/dialogs/menu.ts +++ b/src/main/dialogs/menu.ts @@ -10,7 +10,7 @@ export const showMenuDialog = ( const menuWidth = 330; Application.instance.dialogs.show({ name: 'menu', - browserWindow: browserWindow, + browserWindow, bounds: { width: menuWidth, height: 470, diff --git a/src/main/services/dialogs-service.ts b/src/main/services/dialogs-service.ts index 800cd0d67..e44ae9161 100644 --- a/src/main/services/dialogs-service.ts +++ b/src/main/services/dialogs-service.ts @@ -3,6 +3,8 @@ import { join } from 'path'; import { SearchDialog } from '../dialogs/search'; import { PreviewDialog } from '../dialogs/preview'; import { PersistentDialog } from '../dialogs/dialog'; +import { Application } from '../application'; +import { extensions } from 'electron-extensions'; interface IDialogShowOptions { name: string; @@ -10,6 +12,8 @@ interface IDialogShowOptions { bounds: Electron.Rectangle; hideTimeout?: number; devtools?: boolean; + associateTab?: boolean; + onVisibilityChange?: (visible: boolean, tabId: number) => any; onHide?: (dialog: IDialog) => void; } @@ -17,7 +21,10 @@ interface IDialog { name: string; browserView: BrowserView; id: number; - hide: () => void; + tabIds: number[]; + hide: (tabId?: number) => void; + handle: (name: string, cb: (...args: any[]) => any) => void; + on: (name: string, cb: (...args: any[]) => any) => void; } export class DialogsService { @@ -53,23 +60,36 @@ export class DialogsService { return view; } - public show({ - name, - browserWindow, - bounds, - devtools, - onHide, - hideTimeout, - }: IDialogShowOptions): IDialog { - const foundDialog = this.dialogs.find((x) => x.name === name); - if (foundDialog) return foundDialog; - - let browserView = this.browserViews.find( - (x) => !this.browserViewDetails.get(x.id), + public show(options: IDialogShowOptions): IDialog { + const { + name, + browserWindow, + bounds, + devtools, + onHide, + hideTimeout, + associateTab, + onVisibilityChange, + } = options; + + const foundDialog = this.getDynamic(name); + + let browserView = foundDialog + ? foundDialog.browserView + : this.browserViews.find((x) => !this.browserViewDetails.get(x.id)); + + if (!browserView && !foundDialog) { + browserView = this.createBrowserView(); + } + + const appWindow = Application.instance.windows.fromBrowserWindow( + browserWindow, ); - if (!browserView) { - browserView = this.createBrowserView(); + const tab = appWindow.viewManager.selected; + + if (foundDialog) { + foundDialog.tabIds.push(tab.id); } browserWindow.webContents.send('dialog-visibility-change', name, true); @@ -80,6 +100,13 @@ export class DialogsService { browserWindow.addBrowserView(browserView); browserView.setBounds(bounds); + if (foundDialog) { + const data = onVisibilityChange && onVisibilityChange(true, tab.id); + browserView.webContents.send('visibility-changed', true, tab.id, data); + } + + if (foundDialog) return null; + if (process.env.NODE_ENV === 'development') { browserView.webContents.loadURL(`http://localhost:4444/${name}.html`); } else { @@ -94,19 +121,39 @@ export class DialogsService { // browserView.webContents.openDevTools({ mode: 'detach' }); } - const dialog = { + const channels: string[] = []; + + let activateHandler: any; + let closeHandler: any; + + const dialog: IDialog = { browserView, id: browserView.id, name, - hide: () => { + tabIds: [tab.id], + hide: (tabId) => { + const { selectedId } = appWindow.viewManager; + + dialog.tabIds = dialog.tabIds.filter( + (x) => x !== (tabId || selectedId), + ); + + if (tabId && tabId !== selectedId) return; + browserWindow.webContents.send('dialog-visibility-change', name, false); + browserWindow.removeBrowserView(browserView); + + if (dialog.tabIds.length > 0) return; + ipcMain.removeAllListeners(`hide-${browserView.webContents.id}`); + channels.forEach((x) => { + ipcMain.removeHandler(x); + ipcMain.removeAllListeners(x); + }); this.dialogs = this.dialogs.filter((x) => x.id !== dialog.id); - browserWindow.removeBrowserView(browserView); - if (this.browserViews.length > 2) { browserView.destroy(); this.browserViews.splice(2, 1); @@ -116,10 +163,61 @@ export class DialogsService { this.browserViewDetails.set(browserView.id, false); } + if (associateTab) { + appWindow.viewManager.off('activated', activateHandler); + appWindow.viewManager.off('activated', closeHandler); + } + if (onHide) onHide(dialog); }, + handle: (name, cb) => { + const channel = `${name}-${browserView.webContents.id}`; + ipcMain.handle(channel, (...args) => cb(...args)); + channels.push(channel); + }, + on: (name, cb) => { + const channel = `${name}-${browserView.webContents.id}`; + ipcMain.on(channel, (...args) => cb(...args)); + channels.push(channel); + }, }; + if (associateTab) { + activateHandler = (tabId: number) => { + const visible = dialog.tabIds.includes(tabId); + browserWindow.webContents.send( + 'dialog-visibility-change', + name, + visible, + ); + + const data = onVisibilityChange && onVisibilityChange(visible, tabId); + + browserView.webContents.send( + 'visibility-changed', + visible, + tabId, + data, + ); + + if (visible) { + browserWindow.removeBrowserView(browserView); + browserWindow.addBrowserView(browserView); + } else { + browserWindow.removeBrowserView(browserView); + } + }; + + closeHandler = (tabId: number) => { + dialog.hide(tabId); + }; + + // TODO: handle tab removed + + appWindow.viewManager.on('removed', closeHandler); + appWindow.viewManager.on('activated', activateHandler); + } + this.browserViewDetails.set(browserView.id, true); ipcMain.on(`hide-${browserView.webContents.id}`, () => { diff --git a/src/main/services/messaging.ts b/src/main/services/messaging.ts index f33ee96ce..2c1383c77 100644 --- a/src/main/services/messaging.ts +++ b/src/main/services/messaging.ts @@ -8,6 +8,7 @@ import { IFormFillData, IBookmark } from '~/interfaces'; import { SearchDialog } from '../dialogs/search'; import * as bookmarkMenu from '../menus/bookmarks'; +import { showFindDialog } from '../dialogs/find'; export const runMessagingService = (appWindow: AppWindow) => { const { id } = appWindow; @@ -68,74 +69,74 @@ export const runMessagingService = (appWindow: AppWindow) => { dialog.hide(); }); - /*ipcMain.on(`find-show-${id}`, () => { - appWindow.dialogs.findDialog.show(); + ipcMain.on(`find-show-${id}`, () => { + showFindDialog(appWindow.win); }); ipcMain.on(`find-in-page-${id}`, () => { appWindow.send('find'); }); - ipcMain.on(`show-menu-dialog-${id}`, (e, left, top) => { - appWindow.dialogs.menuDialog.left = left; - appWindow.dialogs.menuDialog.top = top; - appWindow.dialogs.menuDialog.show(); - }); - - ipcMain.on(`search-show-${id}`, (e, data) => { - appWindow.dialogs.searchDialog.data = data; - appWindow.dialogs.searchDialog.show(); - }); - - ipcMain.on(`show-tab-preview-${id}`, (e, tab) => { - appWindow.dialogs.previewDialog.tab = tab; - appWindow.dialogs.previewDialog.show(); - }); - - ipcMain.on(`hide-tab-preview-${id}`, (e, tab) => { - appWindow.dialogs.previewDialog.hide( - appWindow.dialogs.previewDialog.visible, - ); - }); - - ipcMain.on(`show-tabgroup-dialog-${id}`, (e, tabGroup) => { - appWindow.dialogs.tabGroupDialog.edit(tabGroup); - }); - - ipcMain.on(`show-downloads-dialog-${id}`, (e, left, top) => { - appWindow.dialogs.downloadsDialog.left = left; - appWindow.dialogs.downloadsDialog.top = top; - appWindow.dialogs.downloadsDialog.show(); - }); - - ipcMain.on(`show-extension-popup-${id}`, (e, left, top, url, inspect) => { - appWindow.dialogs.extensionPopup.left = left; - appWindow.dialogs.extensionPopup.top = top; - appWindow.dialogs.extensionPopup.url = url; - appWindow.dialogs.extensionPopup.show(inspect); - }); - - ipcMain.on(`hide-extension-popup-${id}`, (e) => { - if (appWindow.dialogs.extensionPopup.visible) { - appWindow.dialogs.extensionPopup.hideVisually(); - } - }); - - ipcMain.on(`show-add-bookmark-dialog-${id}`, (e, left, top) => { - appWindow.dialogs.addBookmarkDialog.left = left; - appWindow.dialogs.addBookmarkDialog.top = top; - appWindow.dialogs.addBookmarkDialog.show(); - }); - - ipcMain.on(`show-zoom-dialog-${id}`, (e, left, top) => { - appWindow.dialogs.zoomDialog.left = left; - appWindow.dialogs.zoomDialog.top = top; - appWindow.dialogs.zoomDialog.show(); - }); - - ipcMain.on(`edit-tabgroup-${id}`, (e, tabGroup) => { - appWindow.send(`edit-tabgroup`, tabGroup); - });*/ + // ipcMain.on(`show-menu-dialog-${id}`, (e, left, top) => { + // appWindow.dialogs.menuDialog.left = left; + // appWindow.dialogs.menuDialog.top = top; + // appWindow.dialogs.menuDialog.show(); + // }); + + // ipcMain.on(`search-show-${id}`, (e, data) => { + // appWindow.dialogs.searchDialog.data = data; + // appWindow.dialogs.searchDialog.show(); + // }); + + // ipcMain.on(`show-tab-preview-${id}`, (e, tab) => { + // appWindow.dialogs.previewDialog.tab = tab; + // appWindow.dialogs.previewDialog.show(); + // }); + + // ipcMain.on(`hide-tab-preview-${id}`, (e, tab) => { + // appWindow.dialogs.previewDialog.hide( + // appWindow.dialogs.previewDialog.visible, + // ); + // }); + + // ipcMain.on(`show-tabgroup-dialog-${id}`, (e, tabGroup) => { + // appWindow.dialogs.tabGroupDialog.edit(tabGroup); + // }); + + // ipcMain.on(`show-downloads-dialog-${id}`, (e, left, top) => { + // appWindow.dialogs.downloadsDialog.left = left; + // appWindow.dialogs.downloadsDialog.top = top; + // appWindow.dialogs.downloadsDialog.show(); + // }); + + // ipcMain.on(`show-extension-popup-${id}`, (e, left, top, url, inspect) => { + // appWindow.dialogs.extensionPopup.left = left; + // appWindow.dialogs.extensionPopup.top = top; + // appWindow.dialogs.extensionPopup.url = url; + // appWindow.dialogs.extensionPopup.show(inspect); + // }); + + // ipcMain.on(`hide-extension-popup-${id}`, (e) => { + // if (appWindow.dialogs.extensionPopup.visible) { + // appWindow.dialogs.extensionPopup.hideVisually(); + // } + // }); + + // ipcMain.on(`show-add-bookmark-dialog-${id}`, (e, left, top) => { + // appWindow.dialogs.addBookmarkDialog.left = left; + // appWindow.dialogs.addBookmarkDialog.top = top; + // appWindow.dialogs.addBookmarkDialog.show(); + // }); + + // ipcMain.on(`show-zoom-dialog-${id}`, (e, left, top) => { + // appWindow.dialogs.zoomDialog.left = left; + // appWindow.dialogs.zoomDialog.top = top; + // appWindow.dialogs.zoomDialog.show(); + // }); + + // ipcMain.on(`edit-tabgroup-${id}`, (e, tabGroup) => { + // appWindow.send(`edit-tabgroup`, tabGroup); + // }); ipcMain.on(`is-incognito-${id}`, (e) => { e.returnValue = appWindow.incognito; diff --git a/src/main/view-manager.ts b/src/main/view-manager.ts index c58d1d229..caa8bb711 100644 --- a/src/main/view-manager.ts +++ b/src/main/view-manager.ts @@ -10,8 +10,9 @@ import { ZOOM_FACTOR_INCREMENT, } from '~/constants/web-contents'; import { extensions } from 'electron-extensions'; +import { EventEmitter } from 'events'; -export class ViewManager { +export class ViewManager extends EventEmitter { public views = new Map(); public selectedId = 0; public _fullscreen = false; @@ -30,6 +31,8 @@ export class ViewManager { } public constructor(window: AppWindow, incognito: boolean) { + super(); + this.window = window; this.incognito = incognito; @@ -170,25 +173,6 @@ export class ViewManager { this.window.webContents.focus(); } - /* - TODO: dialogs - this.window.dialogs.previewDialog.hide(true); - - [ - 'findDialog', - 'authDialog', - 'permissionsDialog', - 'formFillDialog', - 'credentialsDialog', - ].forEach((dialog) => { - if (this.window.dialogs[dialog].tabIds.includes(id)) { - this.window.dialogs[dialog].show(); - this.window.dialogs[dialog].bringToTop(); - } else { - this.window.dialogs[dialog].hide(); - } - });*/ - this.window.updateTitle(); view.updateBookmark(); @@ -196,7 +180,9 @@ export class ViewManager { view.updateNavigationState(); - this.emitZoomUpdate(false); + this.emit('activated', id); + + // TODO: this.emitZoomUpdate(false); } public async fixBounds() { @@ -249,6 +235,7 @@ export class ViewManager { if (view && !view.browserView.isDestroyed()) { this.window.win.removeBrowserView(view.browserView); view.destroy(); + this.emit('removed', id); } } diff --git a/src/main/view.ts b/src/main/view.ts index eac17de0e..216a1a1af 100644 --- a/src/main/view.ts +++ b/src/main/view.ts @@ -32,7 +32,10 @@ export class View { public bookmark: IBookmark; - public openDialogs: string[] = []; + public findInfo = { + occurrences: '0/0', + text: '', + }; private historyQueue = new Queue(); @@ -85,7 +88,9 @@ export class View { }); this.webContents.addListener('found-in-page', (e, result) => { - this.window.dialogs.findDialog.send('found-in-page', result); + Application.instance.dialogs + .getDynamic('find') + .browserView.webContents.send('found-in-page', result); }); this.webContents.addListener('page-title-updated', (e, title) => { diff --git a/src/models/dialog-store.ts b/src/models/dialog-store.ts index 1ff2ac848..2bc276311 100644 --- a/src/models/dialog-store.ts +++ b/src/models/dialog-store.ts @@ -13,8 +13,12 @@ export class DialogStore { return getTheme(this.settings.theme); } + private lastId = -1; + private _windowId = -1; + private persistent = false; + @observable public visible = false; @@ -24,33 +28,22 @@ export class DialogStore { options: { hideOnBlur?: boolean; visibilityWrapper?: boolean; + persistent?: boolean; } = {}, ) { - const { visibilityWrapper, hideOnBlur } = { + const { visibilityWrapper, hideOnBlur, persistent } = { hideOnBlur: true, visibilityWrapper: true, + persistent: false, ...options, }; - if (visibilityWrapper) { - ipcRenderer.on('visible', async (e, flag, ...args) => { - // TODO: diaogs - /*if (!this.firstTime) { - requestAnimationFrame(() => { - this.visible = true; - - setTimeout(() => { - this.visible = true; - - setTimeout(() => { - this.onVisibilityChange(flag, ...args); - }, 20); - }, 20); - }); - this.firstTime = true; - } else { - this.onVisibilityChange(flag, ...args); - }*/ + if (!persistent) this.visible = true; + + this.persistent = persistent; + + if (visibilityWrapper && persistent) { + ipcRenderer.on('visible', async (e, flag, ...args) => { this.onVisibilityChange(flag, ...args); }); } @@ -71,6 +64,14 @@ export class DialogStore { }); } + public async invoke(channel: string, ...args: any[]) { + return await ipcRenderer.invoke(`${channel}-${this.id}`, ...args); + } + + public async send(channel: string, ...args: any[]) { + ipcRenderer.send(`${channel}-${this.id}`, ...args); + } + public get id() { return remote.getCurrentWebContents().id; } @@ -87,14 +88,14 @@ export class DialogStore { public onVisibilityChange(visible: boolean, ...args: any[]) {} public hide(data: any = null) { - if (this.visible) { - this.visible = false; - this.onHide(data); + if (this.persistent && !this.visible) return; - setTimeout(() => { - ipcRenderer.send(`hide-${this.id}`); - }); - } + this.visible = false; + this.onHide(data); + + setTimeout(() => { + ipcRenderer.send(`hide-${this.id}`); + }); } public onHide(data: any = null) {} diff --git a/src/renderer/views/find/store/index.ts b/src/renderer/views/find/store/index.ts index aeaac7987..b06eb9516 100644 --- a/src/renderer/views/find/store/index.ts +++ b/src/renderer/views/find/store/index.ts @@ -1,6 +1,6 @@ import * as React from 'react'; -import { observable, computed } from 'mobx'; +import { observable } from 'mobx'; import { ipcRenderer } from 'electron'; import { DialogStore } from '~/models/dialog-store'; import { callViewMethod } from '~/utils/view'; @@ -10,6 +10,11 @@ interface IFindInfo { text: string; } +const defaultFindInfo = { + occurrences: '0/0', + text: '', +}; + export class Store extends DialogStore { @observable public tabId = -1; @@ -19,51 +24,54 @@ export class Store extends DialogStore { public findInputRef = React.createRef(); - @computed - public get findInfo() { - const findInfo = this.tabsFindInfo.get(this.tabId); + @observable + public findInfo = defaultFindInfo; - if (findInfo) { - return findInfo; - } + public constructor() { + super({ hideOnBlur: false }); - return { - occurrences: '0/0', - text: '', - }; + this.init(); } - public constructor() { - super({ hideOnBlur: false, visibilityWrapper: false }); - - ipcRenderer.on('visible', (e, flag, tabId) => { - this.visible = flag; - - if (flag && tabId) { - if (!this.tabsFindInfo.get(tabId)) { - this.tabsFindInfo.set(tabId, { - occurrences: '0/0', - text: '', - }); - } - this.tabId = tabId; - } - if (this.findInputRef && this.findInputRef.current) { - this.findInputRef.current.focus(); - } - }); + public async init() { + const { occurrences, text, tabId } = await this.invoke('fetch'); + + this.findInfo = { + occurrences, + text, + }; + this.tabId = tabId; + + if (this.findInputRef && this.findInputRef.current) { + this.findInputRef.current.focus(); + } ipcRenderer.on( 'found-in-page', (e, { activeMatchOrdinal, matches }: Electron.FoundInPageResult) => { this.findInfo.occurrences = `${activeMatchOrdinal}/${matches}`; + this.sendInfo(); }, ); + + ipcRenderer.on('visibility-changed', (e, flag, tabId, findInfo) => { + if (flag) { + this.findInfo = findInfo; + this.tabId = tabId; + } else { + this.sendInfo(); + } + }); + } + + public sendInfo() { + this.send('set-find-info', this.tabId, { ...this.findInfo }); } public onHide() { callViewMethod(this.tabId, 'stopFindInPage', 'clearSelection'); - this.tabsFindInfo.delete(this.tabId); + this.findInfo = defaultFindInfo; + this.sendInfo(); ipcRenderer.send(`window-focus-${this.windowId}`); } } diff --git a/src/renderer/views/preview/store/index.ts b/src/renderer/views/preview/store/index.ts index eaccf3852..8241a8ebe 100644 --- a/src/renderer/views/preview/store/index.ts +++ b/src/renderer/views/preview/store/index.ts @@ -38,7 +38,7 @@ export class Store extends DialogStore { } constructor() { - super({ visibilityWrapper: false }); + super({ visibilityWrapper: false, persistent: true }); ipcRenderer.on('visible', (e, visible, tab) => { clearTimeout(this.timeout); diff --git a/src/renderer/views/search/store/index.ts b/src/renderer/views/search/store/index.ts index eac519a70..5907ac548 100644 --- a/src/renderer/views/search/store/index.ts +++ b/src/renderer/views/search/store/index.ts @@ -66,6 +66,7 @@ export class Store extends DialogStore { public constructor() { super({ visibilityWrapper: false, + persistent: true, }); ipcRenderer.on('visible', (e, visible, data) => { From 319de947bea419bdeb2c6f9d32db71460c9ce9e6 Mon Sep 17 00:00:00 2001 From: sentialx Date: Sat, 2 May 2020 16:58:09 +0200 Subject: [PATCH 10/21] refactor: better dialog API --- src/main/dialogs/find.ts | 28 ++++------ src/main/services/dialogs-service.ts | 72 ++++++++++++++------------ src/renderer/views/find/store/index.ts | 20 ++----- 3 files changed, 54 insertions(+), 66 deletions(-) diff --git a/src/main/dialogs/find.ts b/src/main/dialogs/find.ts index 595c6176a..d0fed39b6 100644 --- a/src/main/dialogs/find.ts +++ b/src/main/dialogs/find.ts @@ -7,32 +7,26 @@ export const showFindDialog = (browserWindow: BrowserWindow) => { const appWindow = Application.instance.windows.fromBrowserWindow( browserWindow, ); - const selectedTab = appWindow.viewManager.selected; - const dialog = Application.instance.dialogs.show({ + Application.instance.dialogs.show({ name: 'find', browserWindow, - associateTab: true, bounds: { width: 416, height: 70, x: width - 416, y: VIEW_Y_OFFSET, }, - onVisibilityChange: (visible, tabId) => { - const tab = appWindow.viewManager.views.get(tabId); - return { tabId: tab.id, ...tab.findInfo }; + tabAssociation: { + tabId: appWindow.viewManager.selectedId, + getTabInfo: (tabId) => { + const tab = appWindow.viewManager.views.get(tabId); + return tab.findInfo; + }, + setTabInfo: (tabId, info) => { + const tab = appWindow.viewManager.views.get(tabId); + tab.findInfo = info; + }, }, }); - - if (!dialog) return; - - dialog.handle('fetch', () => { - return { tabId: selectedTab.id, ...selectedTab.findInfo }; - }); - - dialog.on('set-find-info', (e, tabId, info) => { - const tab = appWindow.viewManager.views.get(tabId); - tab.findInfo = info; - }); }; diff --git a/src/main/services/dialogs-service.ts b/src/main/services/dialogs-service.ts index e44ae9161..3445fa8c7 100644 --- a/src/main/services/dialogs-service.ts +++ b/src/main/services/dialogs-service.ts @@ -1,10 +1,15 @@ -import { BrowserView, app, ipcMain, Dialog } from 'electron'; +import { BrowserView, app, ipcMain } from 'electron'; import { join } from 'path'; import { SearchDialog } from '../dialogs/search'; import { PreviewDialog } from '../dialogs/preview'; import { PersistentDialog } from '../dialogs/dialog'; import { Application } from '../application'; -import { extensions } from 'electron-extensions'; + +interface IDialogTabAssociation { + tabId?: number; + getTabInfo?: (tabId: number) => any; + setTabInfo?: (tabId: number, ...args: any[]) => void; +} interface IDialogShowOptions { name: string; @@ -12,8 +17,7 @@ interface IDialogShowOptions { bounds: Electron.Rectangle; hideTimeout?: number; devtools?: boolean; - associateTab?: boolean; - onVisibilityChange?: (visible: boolean, tabId: number) => any; + tabAssociation?: IDialogTabAssociation; onHide?: (dialog: IDialog) => void; } @@ -22,6 +26,7 @@ interface IDialog { browserView: BrowserView; id: number; tabIds: number[]; + _sendTabInfo: (tabId: number) => void; hide: (tabId?: number) => void; handle: (name: string, cb: (...args: any[]) => any) => void; on: (name: string, cb: (...args: any[]) => any) => void; @@ -68,8 +73,7 @@ export class DialogsService { devtools, onHide, hideTimeout, - associateTab, - onVisibilityChange, + tabAssociation, } = options; const foundDialog = this.getDynamic(name); @@ -86,10 +90,9 @@ export class DialogsService { browserWindow, ); - const tab = appWindow.viewManager.selected; - - if (foundDialog) { - foundDialog.tabIds.push(tab.id); + if (foundDialog && tabAssociation) { + foundDialog.tabIds.push(tabAssociation.tabId); + foundDialog._sendTabInfo(tabAssociation.tabId); } browserWindow.webContents.send('dialog-visibility-change', name, true); @@ -100,11 +103,6 @@ export class DialogsService { browserWindow.addBrowserView(browserView); browserView.setBounds(bounds); - if (foundDialog) { - const data = onVisibilityChange && onVisibilityChange(true, tab.id); - browserView.webContents.send('visibility-changed', true, tab.id, data); - } - if (foundDialog) return null; if (process.env.NODE_ENV === 'development') { @@ -130,7 +128,13 @@ export class DialogsService { browserView, id: browserView.id, name, - tabIds: [tab.id], + tabIds: [tabAssociation.tabId], + _sendTabInfo: (tabId) => { + if (tabAssociation.getTabInfo) { + const data = tabAssociation.getTabInfo(tabId); + browserView.webContents.send('update-tab-info', tabId, data); + } + }, hide: (tabId) => { const { selectedId } = appWindow.viewManager; @@ -163,7 +167,7 @@ export class DialogsService { this.browserViewDetails.set(browserView.id, false); } - if (associateTab) { + if (tabAssociation) { appWindow.viewManager.off('activated', activateHandler); appWindow.viewManager.off('activated', closeHandler); } @@ -182,25 +186,17 @@ export class DialogsService { }, }; - if (associateTab) { - activateHandler = (tabId: number) => { - const visible = dialog.tabIds.includes(tabId); + if (tabAssociation) { + activateHandler = (id: number) => { + const visible = dialog.tabIds.includes(id); browserWindow.webContents.send( 'dialog-visibility-change', name, visible, ); - const data = onVisibilityChange && onVisibilityChange(visible, tabId); - - browserView.webContents.send( - 'visibility-changed', - visible, - tabId, - data, - ); - if (visible) { + dialog._sendTabInfo(id); browserWindow.removeBrowserView(browserView); browserWindow.addBrowserView(browserView); } else { @@ -208,12 +204,10 @@ export class DialogsService { } }; - closeHandler = (tabId: number) => { - dialog.hide(tabId); + closeHandler = (id: number) => { + dialog.hide(id); }; - // TODO: handle tab removed - appWindow.viewManager.on('removed', closeHandler); appWindow.viewManager.on('activated', activateHandler); } @@ -224,6 +218,18 @@ export class DialogsService { dialog.hide(); }); + if (tabAssociation) { + dialog.browserView.webContents.once('dom-ready', () => { + dialog._sendTabInfo(tabAssociation.tabId); + }); + + if (tabAssociation.setTabInfo) { + dialog.on('update-tab-info', (e, tabId, ...args) => { + tabAssociation.setTabInfo(tabId, ...args); + }); + } + } + this.dialogs.push(dialog); return dialog; diff --git a/src/renderer/views/find/store/index.ts b/src/renderer/views/find/store/index.ts index b06eb9516..bd1b92c6b 100644 --- a/src/renderer/views/find/store/index.ts +++ b/src/renderer/views/find/store/index.ts @@ -34,14 +34,6 @@ export class Store extends DialogStore { } public async init() { - const { occurrences, text, tabId } = await this.invoke('fetch'); - - this.findInfo = { - occurrences, - text, - }; - this.tabId = tabId; - if (this.findInputRef && this.findInputRef.current) { this.findInputRef.current.focus(); } @@ -54,18 +46,14 @@ export class Store extends DialogStore { }, ); - ipcRenderer.on('visibility-changed', (e, flag, tabId, findInfo) => { - if (flag) { - this.findInfo = findInfo; - this.tabId = tabId; - } else { - this.sendInfo(); - } + ipcRenderer.on('update-tab-info', (e, tabId, info) => { + this.tabId = tabId; + this.findInfo = info; }); } public sendInfo() { - this.send('set-find-info', this.tabId, { ...this.findInfo }); + this.send('update-tab-info', this.tabId, { ...this.findInfo }); } public onHide() { From 2c28fdcbe18703fa1bdeb617f9622f00edc7eff9 Mon Sep 17 00:00:00 2001 From: sentialx Date: Sat, 2 May 2020 18:31:40 +0200 Subject: [PATCH 11/21] feat: permissions dialog --- src/main/dialogs/permissions.ts | 83 +++++++++---------- src/main/services/messaging.ts | 11 --- src/main/sessions-service.ts | 4 +- src/main/view.ts | 2 + .../permissions/components/App/index.tsx | 3 +- src/renderer/views/permissions/store/index.ts | 4 +- 6 files changed, 48 insertions(+), 59 deletions(-) diff --git a/src/main/dialogs/permissions.ts b/src/main/dialogs/permissions.ts index eb06d1cdd..ce058373e 100644 --- a/src/main/dialogs/permissions.ts +++ b/src/main/dialogs/permissions.ts @@ -1,53 +1,50 @@ -import { ipcMain } from 'electron'; import { VIEW_Y_OFFSET } from '~/constants/design'; -import { AppWindow } from '../windows'; -import { Dialog } from '.'; +import { BrowserWindow } from 'electron'; +import { Application } from '../application'; -const HEIGHT = 165; -const WIDTH = 366; +export const requestPermission = ( + browserWindow: BrowserWindow, + name: string, + url: string, + details: any, + tabId: number, +): Promise => { + return new Promise((resolve, reject) => { + if ( + name === 'unknown' || + (name === 'media' && details.mediaTypes.length === 0) || + name === 'midiSysex' + ) { + return reject('Unknown permission'); + } -export class PermissionsDialog extends Dialog { - public constructor(appWindow: AppWindow) { - super(appWindow, { + const appWindow = Application.instance.windows.fromBrowserWindow( + browserWindow, + ); + + appWindow.viewManager.selected.requestedPermission = { name, url, details }; + + const dialog = Application.instance.dialogs.show({ name: 'permissions', + browserWindow, bounds: { - height: HEIGHT, - width: WIDTH, - y: VIEW_Y_OFFSET, + width: 366, + height: 165, x: 0, + y: VIEW_Y_OFFSET, + }, + tabAssociation: { + tabId, + getTabInfo: (tabId) => { + const tab = appWindow.viewManager.views.get(tabId); + return tab.requestedPermission; + }, }, }); - } - public async requestPermission( - name: string, - url: string, - details: any, - tabId: number, - ): Promise { - return new Promise((resolve, reject) => { - if ( - name === 'unknown' || - (name === 'media' && details.mediaTypes.length === 0) || - name === 'midiSysex' - ) { - return reject('Unknown permission'); - } - - this.tabIds.push(tabId); - - this.show(); - - this.send('request-permission', { name, url, details }); - - ipcMain.once( - `request-permission-result-${this.appWindow.id}`, - (e, r: boolean) => { - resolve(r); - this.tabIds = this.tabIds.filter((x) => x !== tabId); - this.hide(); - }, - ); + dialog.on('result', (e, result) => { + resolve(result); + dialog.hide(); }); - } -} + }); +}; diff --git a/src/main/services/messaging.ts b/src/main/services/messaging.ts index 2c1383c77..7c3ff9969 100644 --- a/src/main/services/messaging.ts +++ b/src/main/services/messaging.ts @@ -77,17 +77,6 @@ export const runMessagingService = (appWindow: AppWindow) => { appWindow.send('find'); }); - // ipcMain.on(`show-menu-dialog-${id}`, (e, left, top) => { - // appWindow.dialogs.menuDialog.left = left; - // appWindow.dialogs.menuDialog.top = top; - // appWindow.dialogs.menuDialog.show(); - // }); - - // ipcMain.on(`search-show-${id}`, (e, data) => { - // appWindow.dialogs.searchDialog.data = data; - // appWindow.dialogs.searchDialog.show(); - // }); - // ipcMain.on(`show-tab-preview-${id}`, (e, tab) => { // appWindow.dialogs.previewDialog.tab = tab; // appWindow.dialogs.previewDialog.show(); diff --git a/src/main/sessions-service.ts b/src/main/sessions-service.ts index c1cc9ff7f..5c1ad5a17 100644 --- a/src/main/sessions-service.ts +++ b/src/main/sessions-service.ts @@ -10,6 +10,7 @@ import { parseCrx } from '~/utils/crx'; import { pathExists } from '~/utils/files'; import { extractZip } from '~/utils/zip'; import { extensions, _setFallbackSession } from 'electron-extensions'; +import { requestPermission } from './dialogs/permissions'; // TODO: move windows list to the corresponding sessions export class SessionsService { @@ -74,7 +75,8 @@ export class SessionsService { }); if (!perm) { - const response = await window.dialogs.permissionsDialog.requestPermission( + const response = await requestPermission( + window.win, permission, webContents.getURL(), details, diff --git a/src/main/view.ts b/src/main/view.ts index 216a1a1af..feeae9ad9 100644 --- a/src/main/view.ts +++ b/src/main/view.ts @@ -37,6 +37,8 @@ export class View { text: '', }; + public requestedPermission: any; + private historyQueue = new Queue(); private lastUrl = ''; diff --git a/src/renderer/views/permissions/components/App/index.tsx b/src/renderer/views/permissions/components/App/index.tsx index 4b9490c4f..1baed6779 100644 --- a/src/renderer/views/permissions/components/App/index.tsx +++ b/src/renderer/views/permissions/components/App/index.tsx @@ -3,14 +3,13 @@ import { observer } from 'mobx-react-lite'; import { ThemeProvider } from 'styled-components'; import { hot } from 'react-hot-loader/root'; -import { ipcRenderer } from 'electron'; import { StyledApp, Title, Permissions, Permission, Buttons } from './style'; import store from '../../store'; import { Button } from '~/renderer/components/Button'; import { UIStyle } from '~/renderer/mixins/default-styles'; const sendResult = (r: boolean) => { - ipcRenderer.send(`request-permission-result-${store.windowId}`, r); + store.send('result', r); }; const getText = (permission: string) => { diff --git a/src/renderer/views/permissions/store/index.ts b/src/renderer/views/permissions/store/index.ts index 0413c4773..f4bd09d01 100644 --- a/src/renderer/views/permissions/store/index.ts +++ b/src/renderer/views/permissions/store/index.ts @@ -12,9 +12,9 @@ export class Store extends DialogStore { public domain: string; public constructor() { - super(); + super({ hideOnBlur: false }); - ipcRenderer.on('request-permission', (e, { url, name, details }) => { + ipcRenderer.on('update-tab-info', (e, tabId, { url, name, details }) => { this.domain = getDomain(url); this.permissions = []; From c97e3685e77f261e40e008c21acd0025a67b1fc4 Mon Sep 17 00:00:00 2001 From: sentialx Date: Sat, 2 May 2020 19:50:05 +0200 Subject: [PATCH 12/21] fix: browserviews garbage collection --- src/main/services/dialogs-service.ts | 39 ++++++++++++++++---------- src/models/dialog-store.ts | 4 +-- src/renderer/views/menu/store/index.ts | 2 -- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/main/services/dialogs-service.ts b/src/main/services/dialogs-service.ts index 3445fa8c7..26c5c72af 100644 --- a/src/main/services/dialogs-service.ts +++ b/src/main/services/dialogs-service.ts @@ -82,7 +82,7 @@ export class DialogsService { ? foundDialog.browserView : this.browserViews.find((x) => !this.browserViewDetails.get(x.id)); - if (!browserView && !foundDialog) { + if (!browserView) { browserView = this.createBrowserView(); } @@ -100,10 +100,21 @@ export class DialogsService { bounds.x = Math.round(bounds.x); bounds.y = Math.round(bounds.y); + this.browserViewDetails.set(browserView.id, true); + + if (foundDialog) { + browserWindow.addBrowserView(browserView); + browserView.setBounds(bounds); + return null; + } + browserWindow.addBrowserView(browserView); - browserView.setBounds(bounds); + browserView.setBounds({ x: 0, y: 0, width: 1, height: 1 }); - if (foundDialog) return null; + browserView.webContents.once('dom-ready', () => { + browserView.setBounds(bounds); + browserView.webContents.focus(); + }); if (process.env.NODE_ENV === 'development') { browserView.webContents.loadURL(`http://localhost:4444/${name}.html`); @@ -113,10 +124,8 @@ export class DialogsService { ); } - browserView.webContents.focus(); - if (devtools) { - // browserView.webContents.openDevTools({ mode: 'detach' }); + browserView.webContents.openDevTools({ mode: 'detach' }); } const channels: string[] = []; @@ -128,7 +137,7 @@ export class DialogsService { browserView, id: browserView.id, name, - tabIds: [tabAssociation.tabId], + tabIds: [tabAssociation?.tabId], _sendTabInfo: (tabId) => { if (tabAssociation.getTabInfo) { const data = tabAssociation.getTabInfo(tabId); @@ -148,7 +157,7 @@ export class DialogsService { browserWindow.removeBrowserView(browserView); - if (dialog.tabIds.length > 0) return; + if (tabAssociation && dialog.tabIds.length > 0) return; ipcMain.removeAllListeners(`hide-${browserView.webContents.id}`); channels.forEach((x) => { @@ -158,13 +167,15 @@ export class DialogsService { this.dialogs = this.dialogs.filter((x) => x.id !== dialog.id); - if (this.browserViews.length > 2) { - browserView.destroy(); - this.browserViews.splice(2, 1); - this.browserViewDetails.delete(browserView.id); + this.browserViewDetails.set(browserView.id, false); + + if (this.browserViews.length > 1) { + // TODO: garbage collect unused BrowserViews? + // this.browserViewDetails.delete(browserView.id); + // browserView.destroy(); + // this.browserViews.splice(1, 1); } else { browserView.webContents.loadURL('about:blank'); - this.browserViewDetails.set(browserView.id, false); } if (tabAssociation) { @@ -212,8 +223,6 @@ export class DialogsService { appWindow.viewManager.on('activated', activateHandler); } - this.browserViewDetails.set(browserView.id, true); - ipcMain.on(`hide-${browserView.webContents.id}`, () => { dialog.hide(); }); diff --git a/src/models/dialog-store.ts b/src/models/dialog-store.ts index 2bc276311..2edc24db6 100644 --- a/src/models/dialog-store.ts +++ b/src/models/dialog-store.ts @@ -13,8 +13,6 @@ export class DialogStore { return getTheme(this.settings.theme); } - private lastId = -1; - private _windowId = -1; private persistent = false; @@ -94,7 +92,7 @@ export class DialogStore { this.onHide(data); setTimeout(() => { - ipcRenderer.send(`hide-${this.id}`); + this.send('hide'); }); } diff --git a/src/renderer/views/menu/store/index.ts b/src/renderer/views/menu/store/index.ts index 562d7b0ad..997bfacba 100644 --- a/src/renderer/views/menu/store/index.ts +++ b/src/renderer/views/menu/store/index.ts @@ -12,8 +12,6 @@ export class Store extends DialogStore { public constructor() { super(); - this.visible = true; - ipcRenderer.on('update-available', () => { this.updateAvailable = true; }); From c2d68d73fd65c717b559743e6e1681bfe0fb85f3 Mon Sep 17 00:00:00 2001 From: sentialx Date: Sat, 2 May 2020 20:10:16 +0200 Subject: [PATCH 13/21] fix: getTabInfo race condition --- src/main/services/dialogs-service.ts | 2 +- src/main/sessions-service.ts | 45 ++++++++++++------------ src/models/dialog-store.ts | 20 ++++++++--- src/renderer/views/find/store/index.ts | 24 ++++++------- src/renderer/views/search/store/index.ts | 28 +++++++-------- 5 files changed, 66 insertions(+), 53 deletions(-) diff --git a/src/main/services/dialogs-service.ts b/src/main/services/dialogs-service.ts index 26c5c72af..0c296b1fb 100644 --- a/src/main/services/dialogs-service.ts +++ b/src/main/services/dialogs-service.ts @@ -228,7 +228,7 @@ export class DialogsService { }); if (tabAssociation) { - dialog.browserView.webContents.once('dom-ready', () => { + dialog.on('loaded', () => { dialog._sendTabInfo(tabAssociation.tabId); }); diff --git a/src/main/sessions-service.ts b/src/main/sessions-service.ts index 5c1ad5a17..843a1e57d 100644 --- a/src/main/sessions-service.ts +++ b/src/main/sessions-service.ts @@ -74,29 +74,30 @@ export class SessionsService { }, }); - if (!perm) { - const response = await requestPermission( - window.win, + // TODO: + // if (!perm) { + const response = await requestPermission( + window.win, + permission, + webContents.getURL(), + details, + webContents.id, + ); + + callback(response); + + await Application.instance.storage.insert({ + scope: 'permissions', + item: { + url: hostname, permission, - webContents.getURL(), - details, - webContents.id, - ); - - callback(response); - - await Application.instance.storage.insert({ - scope: 'permissions', - item: { - url: hostname, - permission, - type: response ? 1 : 2, - mediaTypes: JSON.stringify(details.mediaTypes) || '', - }, - }); - } else { - callback(perm.type === 1); - } + type: response ? 1 : 2, + mediaTypes: JSON.stringify(details.mediaTypes) || '', + }, + }); + // } else { + // callback(perm.type === 1); + // } } catch (e) { callback(false); } diff --git a/src/models/dialog-store.ts b/src/models/dialog-store.ts index 2edc24db6..f7ca52108 100644 --- a/src/models/dialog-store.ts +++ b/src/models/dialog-store.ts @@ -4,6 +4,12 @@ import { getTheme } from '~/utils/themes'; import { ISettings } from '~/interfaces'; import { DEFAULT_SETTINGS } from '~/constants'; +export declare interface DialogStore { + onVisibilityChange: (visible: boolean, ...args: any[]) => void; + onUpdateTabInfo: (tabId: number, data: any) => void; + onHide: (data: any) => void; +} + export class DialogStore { @observable public settings: ISettings = DEFAULT_SETTINGS; @@ -60,6 +66,16 @@ export class DialogStore { ipcRenderer.on('update-settings', (e, settings: ISettings) => { this.settings = { ...this.settings, ...settings }; }); + + ipcRenderer.on('update-tab-info', (e, tabId, data) => + this.onUpdateTabInfo(tabId, data), + ); + + this.onHide = () => {}; + this.onUpdateTabInfo = () => {}; + this.onVisibilityChange = () => {}; + + this.send('loaded'); } public async invoke(channel: string, ...args: any[]) { @@ -83,8 +99,6 @@ export class DialogStore { return this._windowId; } - public onVisibilityChange(visible: boolean, ...args: any[]) {} - public hide(data: any = null) { if (this.persistent && !this.visible) return; @@ -95,6 +109,4 @@ export class DialogStore { this.send('hide'); }); } - - public onHide(data: any = null) {} } diff --git a/src/renderer/views/find/store/index.ts b/src/renderer/views/find/store/index.ts index bd1b92c6b..7d66433b2 100644 --- a/src/renderer/views/find/store/index.ts +++ b/src/renderer/views/find/store/index.ts @@ -31,6 +31,18 @@ export class Store extends DialogStore { super({ hideOnBlur: false }); this.init(); + + this.onUpdateTabInfo = (tabId, info) => { + this.tabId = tabId; + this.findInfo = info; + }; + + this.onHide = () => { + callViewMethod(this.tabId, 'stopFindInPage', 'clearSelection'); + this.findInfo = defaultFindInfo; + this.sendInfo(); + ipcRenderer.send(`window-focus-${this.windowId}`); + }; } public async init() { @@ -45,23 +57,11 @@ export class Store extends DialogStore { this.sendInfo(); }, ); - - ipcRenderer.on('update-tab-info', (e, tabId, info) => { - this.tabId = tabId; - this.findInfo = info; - }); } public sendInfo() { this.send('update-tab-info', this.tabId, { ...this.findInfo }); } - - public onHide() { - callViewMethod(this.tabId, 'stopFindInPage', 'clearSelection'); - this.findInfo = defaultFindInfo; - this.sendInfo(); - ipcRenderer.send(`window-focus-${this.windowId}`); - } } export default new Store(); diff --git a/src/renderer/views/search/store/index.ts b/src/renderer/views/search/store/index.ts index 5907ac548..c68e5b7d0 100644 --- a/src/renderer/views/search/store/index.ts +++ b/src/renderer/views/search/store/index.ts @@ -95,6 +95,20 @@ export class Store extends DialogStore { this.loadHistory(); ipcRenderer.send(`can-show-${this.id}`); + + this.onHide = (data) => { + ipcRenderer.send(`addressbar-update-input-${this.id}`, { + id: this.tabId, + text: this.inputRef.current.value, + selectionStart: this.inputRef.current.selectionStart, + selectionEnd: this.inputRef.current.selectionEnd, + ...data, + }); + + this.tabs = []; + this.inputRef.current.value = ''; + this.suggestions.list = []; + }; } public getCanSuggest(key: number) { @@ -115,20 +129,6 @@ export class Store extends DialogStore { return false; } - public onHide(data: any = null) { - ipcRenderer.send(`addressbar-update-input-${this.id}`, { - id: this.tabId, - text: this.inputRef.current.value, - selectionStart: this.inputRef.current.selectionStart, - selectionEnd: this.inputRef.current.selectionEnd, - ...data, - }); - - this.tabs = []; - this.inputRef.current.value = ''; - this.suggestions.list = []; - } - public async loadHistory() { this.visitedItems = await ipcRenderer.invoke('topsites-get'); } From 030fb0538b0a65541983bb2fbc81db3c34e0f23a Mon Sep 17 00:00:00 2001 From: sentialx Date: Sat, 2 May 2020 20:16:24 +0200 Subject: [PATCH 14/21] fix: remembering permission decision --- src/main/sessions-service.ts | 45 ++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/main/sessions-service.ts b/src/main/sessions-service.ts index 843a1e57d..5c1ad5a17 100644 --- a/src/main/sessions-service.ts +++ b/src/main/sessions-service.ts @@ -74,30 +74,29 @@ export class SessionsService { }, }); - // TODO: - // if (!perm) { - const response = await requestPermission( - window.win, - permission, - webContents.getURL(), - details, - webContents.id, - ); - - callback(response); - - await Application.instance.storage.insert({ - scope: 'permissions', - item: { - url: hostname, + if (!perm) { + const response = await requestPermission( + window.win, permission, - type: response ? 1 : 2, - mediaTypes: JSON.stringify(details.mediaTypes) || '', - }, - }); - // } else { - // callback(perm.type === 1); - // } + webContents.getURL(), + details, + webContents.id, + ); + + callback(response); + + await Application.instance.storage.insert({ + scope: 'permissions', + item: { + url: hostname, + permission, + type: response ? 1 : 2, + mediaTypes: JSON.stringify(details.mediaTypes) || '', + }, + }); + } else { + callback(perm.type === 1); + } } catch (e) { callback(false); } From 8acaae3831fc008cb1ac1fc0ed246cb7b85cdf19 Mon Sep 17 00:00:00 2001 From: sentialx Date: Sun, 3 May 2020 19:06:38 +0200 Subject: [PATCH 15/21] feat: add bookmark dialog --- src/main/dialogs/add-bookmark.ts | 81 +++++++------------ src/main/menus/bookmarks.ts | 5 +- src/main/services/messaging.ts | 25 ++---- .../views/add-bookmark/store/index.ts | 15 ++-- .../views/app/components/Toolbar/index.tsx | 19 ++--- 5 files changed, 52 insertions(+), 93 deletions(-) diff --git a/src/main/dialogs/add-bookmark.ts b/src/main/dialogs/add-bookmark.ts index 8dd52e470..8523137e1 100644 --- a/src/main/dialogs/add-bookmark.ts +++ b/src/main/dialogs/add-bookmark.ts @@ -1,65 +1,46 @@ -import { AppWindow } from '../windows'; -import { Dialog } from '.'; -import { DIALOG_MARGIN, DIALOG_MARGIN_TOP } from '~/constants/design'; +import { BrowserWindow } from 'electron'; import { Application } from '../application'; +import { DIALOG_MARGIN_TOP, DIALOG_MARGIN } from '~/constants/design'; import { IBookmark } from '~/interfaces'; -const WIDTH = 366; - -export class AddBookmarkDialog extends Dialog { - public visible = false; - - public left = 0; - public top = 0; - - constructor(appWindow: AppWindow) { - super(appWindow, { - name: 'add-bookmark', - bounds: { - width: WIDTH, - height: 240, - }, - }); - } - - public rearrange() { - super.rearrange({ - x: Math.round(this.left - WIDTH + DIALOG_MARGIN), - y: Math.round(this.top - DIALOG_MARGIN_TOP), - }); - } - - public async show() { - await super.show(); +export const showAddBookmarkDialog = ( + browserWindow: BrowserWindow, + x: number, + y: number, + data?: { + url: string; + title: string; + bookmark?: IBookmark; + favicon?: string; + }, +) => { + if (!data) { const { url, title, bookmark, favicon, } = Application.instance.windows.current.viewManager.selected; - - this.send('visible', true, { + data = { url, title, bookmark, favicon, - }); + }; } - public async showForBookmark(data: { - url: string; - title: string; - bookmark?: IBookmark; - favicon?: string; - }) { - await super.show(); - const { url, title, bookmark, favicon } = data; - - this.send('visible', true, { - url, - title, - bookmark, - favicon, - }); - } -} + const dialog = Application.instance.dialogs.show({ + name: 'add-bookmark', + browserWindow, + bounds: { + width: 366, + height: 240, + x: x - 366 + DIALOG_MARGIN, + y: y - DIALOG_MARGIN_TOP, + }, + }); + + dialog.on('loaded', (e) => { + e.reply('data', data); + }); +}; diff --git a/src/main/menus/bookmarks.ts b/src/main/menus/bookmarks.ts index 806fa51b9..ba4f4b61c 100644 --- a/src/main/menus/bookmarks.ts +++ b/src/main/menus/bookmarks.ts @@ -9,6 +9,7 @@ import { join } from 'path'; import { IBookmark } from '~/interfaces'; import { Application } from '../application'; import { AppWindow } from '../windows/app'; +import { showAddBookmarkDialog } from '../dialogs/add-bookmark'; function getPath(file: string) { if (process.env.NODE_ENV === 'development') { @@ -110,9 +111,7 @@ export function createMenu(appWindow: AppWindow, item: IBookmark) { label: 'Edit', click: () => { const windowBounds = appWindow.win.getBounds(); - appWindow.dialogs.addBookmarkDialog.left = windowBounds.width - 20; - appWindow.dialogs.addBookmarkDialog.top = 72; - appWindow.dialogs.addBookmarkDialog.showForBookmark({ + showAddBookmarkDialog(appWindow.win, windowBounds.width - 20, 72, { url: item.url, title: item.title, bookmark: item, diff --git a/src/main/services/messaging.ts b/src/main/services/messaging.ts index 87330fd86..fb168abbe 100644 --- a/src/main/services/messaging.ts +++ b/src/main/services/messaging.ts @@ -1,14 +1,18 @@ import { ipcMain } from 'electron'; +import { parse } from 'url'; +import { getPassword, setPassword, deletePassword } from 'keytar'; + import { AppWindow } from '../windows'; import { Application } from '../application'; import { showMenuDialog } from '../dialogs/menu'; import { PreviewDialog } from '../dialogs/preview'; -import { parse } from 'url'; import { IFormFillData, IBookmark } from '~/interfaces'; import { SearchDialog } from '../dialogs/search'; import * as bookmarkMenu from '../menus/bookmarks'; import { showFindDialog } from '../dialogs/find'; +import { getFormFillMenuItems } from '../utils'; +import { showAddBookmarkDialog } from '../dialogs/add-bookmark'; export const runMessagingService = (appWindow: AppWindow) => { const { id } = appWindow; @@ -77,16 +81,9 @@ export const runMessagingService = (appWindow: AppWindow) => { appWindow.send('find'); }); - // ipcMain.on(`show-tab-preview-${id}`, (e, tab) => { - // appWindow.dialogs.previewDialog.tab = tab; - // appWindow.dialogs.previewDialog.show(); - // }); - - // ipcMain.on(`hide-tab-preview-${id}`, (e, tab) => { - // appWindow.dialogs.previewDialog.hide( - // appWindow.dialogs.previewDialog.visible, - // ); - // }); + ipcMain.on(`show-add-bookmark-dialog-${id}`, (e, left, top) => { + showAddBookmarkDialog(appWindow.win, left, top); + }); // ipcMain.on(`show-tabgroup-dialog-${id}`, (e, tabGroup) => { // appWindow.dialogs.tabGroupDialog.edit(tabGroup); @@ -111,12 +108,6 @@ export const runMessagingService = (appWindow: AppWindow) => { // } // }); - // ipcMain.on(`show-add-bookmark-dialog-${id}`, (e, left, top) => { - // appWindow.dialogs.addBookmarkDialog.left = left; - // appWindow.dialogs.addBookmarkDialog.top = top; - // appWindow.dialogs.addBookmarkDialog.show(); - // }); - // ipcMain.on(`show-zoom-dialog-${id}`, (e, left, top) => { // appWindow.dialogs.zoomDialog.left = left; // appWindow.dialogs.zoomDialog.top = top; diff --git a/src/renderer/views/add-bookmark/store/index.ts b/src/renderer/views/add-bookmark/store/index.ts index 954baa8ea..79bb6d6df 100644 --- a/src/renderer/views/add-bookmark/store/index.ts +++ b/src/renderer/views/add-bookmark/store/index.ts @@ -25,14 +25,14 @@ export class Store extends DialogStore { this.folders = await ipcRenderer.invoke('bookmarks-get-folders'); this.currentFolder = this.folders.find((x) => x.static === 'main'); })(); - } - - public async onVisibilityChange(visible: boolean, data: any) { - this.visible = visible; - if (visible) { + ipcRenderer.on('data', async (e, data) => { const { bookmark, title, url, favicon } = data; + if (!bookmark) { + this.dialogTitle = !bookmark ? 'Bookmark added' : 'Edit bookmark'; + } + this.bookmark = bookmark; this.folders = await ipcRenderer.invoke('bookmarks-get-folders'); @@ -43,9 +43,6 @@ export class Store extends DialogStore { favicon, parent: this.folders.find((x) => x.static === 'main')._id, }); - this.dialogTitle = 'Bookmark added'; - } else { - this.dialogTitle = 'Edit bookmark'; } this.currentFolder = this.folders.find( @@ -57,7 +54,7 @@ export class Store extends DialogStore { this.titleRef.current.focus(); this.titleRef.current.select(); } - } + }); } } diff --git a/src/renderer/views/app/components/Toolbar/index.tsx b/src/renderer/views/app/components/Toolbar/index.tsx index ed8220d62..37c4d0128 100644 --- a/src/renderer/views/app/components/Toolbar/index.tsx +++ b/src/renderer/views/app/components/Toolbar/index.tsx @@ -25,7 +25,6 @@ import { ICON_INCOGNITO, ICON_MORE, ICON_SEARCH, - ICON_DASHBOARD, ICON_MAGNIFY_PLUS, ICON_MAGNIFY_MINUS, } from '~/renderer/constants/icons'; @@ -58,28 +57,20 @@ let menuRef: HTMLDivElement = null; let zoomRef: HTMLDivElement = null; const showAddBookmarkDialog = async () => { - if (!(await isDialogVisible('addBookmarkDialog'))) { - const { right, bottom } = starRef.getBoundingClientRect(); - ipcRenderer.send( - `show-add-bookmark-dialog-${store.windowId}`, - right, - bottom, - ); - } + const { right, bottom } = starRef.getBoundingClientRect(); + ipcRenderer.send(`show-add-bookmark-dialog-${store.windowId}`, right, bottom); }; const showZoomDialog = async () => { - if (!(await isDialogVisible('zoomDialog')) && store.zoomFactor != 1) { + if (store.zoomFactor != 1) { const { right, bottom } = zoomRef.getBoundingClientRect(); ipcRenderer.send(`show-zoom-dialog-${store.windowId}`, right, bottom); } }; const showMenuDialog = async () => { - if (!(await isDialogVisible('menuDialog'))) { - const { right, bottom } = menuRef.getBoundingClientRect(); - ipcRenderer.send(`show-menu-dialog-${store.windowId}`, right, bottom); - } + const { right, bottom } = menuRef.getBoundingClientRect(); + ipcRenderer.send(`show-menu-dialog-${store.windowId}`, right, bottom); }; ipcRenderer.on('show-add-bookmark-dialog', () => { From 650bf473bcf3ff91a78ac39b1fe8cb8254cb9dcb Mon Sep 17 00:00:00 2001 From: sentialx Date: Sun, 3 May 2020 19:46:33 +0200 Subject: [PATCH 16/21] feat: extension popup --- src/main/dialogs/add-bookmark.ts | 4 +- src/main/dialogs/extension-popup.ts | 99 +++++++++---------- src/main/dialogs/find.ts | 4 +- src/main/dialogs/menu.ts | 4 +- src/main/dialogs/permissions.ts | 4 +- src/main/dialogs/search.ts | 2 - src/main/services/dialogs-service.ts | 36 +++++-- src/main/services/messaging.ts | 26 ++--- src/main/sessions-service.ts | 19 ++-- src/preloads/popup-preload.ts | 6 +- .../app/components/BrowserAction/index.tsx | 2 +- src/renderer/views/extension-popup/index.tsx | 13 +-- 12 files changed, 114 insertions(+), 105 deletions(-) diff --git a/src/main/dialogs/add-bookmark.ts b/src/main/dialogs/add-bookmark.ts index 8523137e1..89048c83f 100644 --- a/src/main/dialogs/add-bookmark.ts +++ b/src/main/dialogs/add-bookmark.ts @@ -32,12 +32,12 @@ export const showAddBookmarkDialog = ( const dialog = Application.instance.dialogs.show({ name: 'add-bookmark', browserWindow, - bounds: { + getBounds: () => ({ width: 366, height: 240, x: x - 366 + DIALOG_MARGIN, y: y - DIALOG_MARGIN_TOP, - }, + }), }); dialog.on('loaded', (e) => { diff --git a/src/main/dialogs/extension-popup.ts b/src/main/dialogs/extension-popup.ts index 01259c19f..1453170a8 100644 --- a/src/main/dialogs/extension-popup.ts +++ b/src/main/dialogs/extension-popup.ts @@ -1,57 +1,50 @@ -import { AppWindow } from '../windows'; -import { Dialog } from '.'; -import { ipcMain } from 'electron'; -import { DIALOG_MARGIN, DIALOG_MARGIN_TOP } from '~/constants/design'; - -export class ExtensionPopup extends Dialog { - public visible = false; - - private height = 512; - - public left = 0; - public top = 0; - - private width = 512; - - public url = ''; - - constructor(appWindow: AppWindow) { - super(appWindow, { - name: 'extension-popup', - bounds: { - width: 512, - height: 512, - }, - devtools: false, - webPreferences: { - webviewTag: true, - }, - }); - - ipcMain.on(`bounds-${this.id}`, (e, width, height) => { - this.height = height; - this.width = width; - this.rearrange(); - }); - - this.webContents.on('will-attach-webview', (e, webPreferences, params) => { +import { BrowserWindow } from 'electron'; +import { Application } from '../application'; +import { DIALOG_MARGIN_TOP, DIALOG_MARGIN } from '~/constants/design'; + +export const showExtensionDialog = ( + browserWindow: BrowserWindow, + x: number, + y: number, + url: string, + inspect = false, +) => { + if (!process.env.ENABLE_EXTENSIONS) return; + + let height = 512; + let width = 512; + + const dialog = Application.instance.dialogs.show({ + name: 'extension-popup', + browserWindow, + getBounds: () => { + return { + x: x - width + DIALOG_MARGIN, + y: y - DIALOG_MARGIN_TOP, + height: Math.min(1024, height), + width: Math.min(1024, width), + }; + }, + }); + + if (!dialog) return; + + dialog.on('bounds', (e, w, h) => { + width = w; + height = h; + dialog.rearrange(); + }); + + dialog.browserView.webContents.on( + 'will-attach-webview', + (e, webPreferences, params) => { webPreferences.sandbox = true; webPreferences.nodeIntegration = false; webPreferences.contextIsolation = true; - }); - } - - public rearrange() { - super.rearrange({ - x: Math.round(this.left - this.width + DIALOG_MARGIN), - y: Math.round(this.top - DIALOG_MARGIN_TOP), - height: Math.round(Math.min(1024, this.height)), - width: Math.round(Math.min(1024, this.width)), - }); - } + }, + ); - public async show(inspect = false) { - await super.show(); - this.send('visible', true, { url: this.url, inspect }); - } -} + dialog.on('loaded', (e) => { + e.reply('data', { url, inspect }); + }); +}; diff --git a/src/main/dialogs/find.ts b/src/main/dialogs/find.ts index d0fed39b6..7ef0a41fe 100644 --- a/src/main/dialogs/find.ts +++ b/src/main/dialogs/find.ts @@ -11,12 +11,12 @@ export const showFindDialog = (browserWindow: BrowserWindow) => { Application.instance.dialogs.show({ name: 'find', browserWindow, - bounds: { + getBounds: () => ({ width: 416, height: 70, x: width - 416, y: VIEW_Y_OFFSET, - }, + }), tabAssociation: { tabId: appWindow.viewManager.selectedId, getTabInfo: (tabId) => { diff --git a/src/main/dialogs/menu.ts b/src/main/dialogs/menu.ts index f76e98ccb..9bec84333 100644 --- a/src/main/dialogs/menu.ts +++ b/src/main/dialogs/menu.ts @@ -11,11 +11,11 @@ export const showMenuDialog = ( Application.instance.dialogs.show({ name: 'menu', browserWindow, - bounds: { + getBounds: () => ({ width: menuWidth, height: 470, x: x - menuWidth + DIALOG_MARGIN, y: y - DIALOG_MARGIN_TOP, - }, + }), }); }; diff --git a/src/main/dialogs/permissions.ts b/src/main/dialogs/permissions.ts index ce058373e..7625173d2 100644 --- a/src/main/dialogs/permissions.ts +++ b/src/main/dialogs/permissions.ts @@ -27,12 +27,12 @@ export const requestPermission = ( const dialog = Application.instance.dialogs.show({ name: 'permissions', browserWindow, - bounds: { + getBounds: () => ({ width: 366, height: 165, x: 0, y: VIEW_Y_OFFSET, - }, + }), tabAssociation: { tabId, getTabInfo: (tabId) => { diff --git a/src/main/dialogs/search.ts b/src/main/dialogs/search.ts index 9ce746a08..91c342be8 100644 --- a/src/main/dialogs/search.ts +++ b/src/main/dialogs/search.ts @@ -38,8 +38,6 @@ export class SearchDialog extends PersistentDialog { ? Math.max(DIALOG_MIN_HEIGHT, HEIGHT + height) : HEIGHT + height, }); - - this.lastHeight = HEIGHT + height; }); ipcMain.on(`addressbar-update-input-${this.id}`, (e, data) => { diff --git a/src/main/services/dialogs-service.ts b/src/main/services/dialogs-service.ts index 0c296b1fb..7dfc5459b 100644 --- a/src/main/services/dialogs-service.ts +++ b/src/main/services/dialogs-service.ts @@ -4,6 +4,7 @@ import { SearchDialog } from '../dialogs/search'; import { PreviewDialog } from '../dialogs/preview'; import { PersistentDialog } from '../dialogs/dialog'; import { Application } from '../application'; +import { IRectangle } from '~/interfaces'; interface IDialogTabAssociation { tabId?: number; @@ -14,11 +15,11 @@ interface IDialogTabAssociation { interface IDialogShowOptions { name: string; browserWindow: Electron.BrowserWindow; - bounds: Electron.Rectangle; hideTimeout?: number; devtools?: boolean; tabAssociation?: IDialogTabAssociation; onHide?: (dialog: IDialog) => void; + getBounds: () => IRectangle; } interface IDialog { @@ -30,8 +31,17 @@ interface IDialog { hide: (tabId?: number) => void; handle: (name: string, cb: (...args: any[]) => any) => void; on: (name: string, cb: (...args: any[]) => any) => void; + rearrange: (bounds?: IRectangle) => void; } +const roundifyRectangle = (rect: IRectangle): IRectangle => { + const newRect: any = { ...rect }; + Object.keys(newRect).forEach((key) => { + if (!isNaN(newRect[key])) newRect[key] = Math.round(newRect[key]); + }); + return newRect; +}; + export class DialogsService { public browserViews: BrowserView[] = []; public browserViewDetails = new Map(); @@ -69,7 +79,7 @@ export class DialogsService { const { name, browserWindow, - bounds, + getBounds, devtools, onHide, hideTimeout, @@ -97,22 +107,31 @@ export class DialogsService { browserWindow.webContents.send('dialog-visibility-change', name, true); - bounds.x = Math.round(bounds.x); - bounds.y = Math.round(bounds.y); - this.browserViewDetails.set(browserView.id, true); + const rearrange = (rect?: IRectangle) => { + rect = rect || {}; + browserView.setBounds({ + x: 0, + y: 0, + width: 0, + height: 0, + ...roundifyRectangle(getBounds()), + ...roundifyRectangle(rect), + }); + }; + if (foundDialog) { browserWindow.addBrowserView(browserView); - browserView.setBounds(bounds); + rearrange(); return null; } browserWindow.addBrowserView(browserView); - browserView.setBounds({ x: 0, y: 0, width: 1, height: 1 }); + rearrange({ x: 0, y: 0, width: 1, height: 1 }); browserView.webContents.once('dom-ready', () => { - browserView.setBounds(bounds); + rearrange(); browserView.webContents.focus(); }); @@ -195,6 +214,7 @@ export class DialogsService { ipcMain.on(channel, (...args) => cb(...args)); channels.push(channel); }, + rearrange, }; if (tabAssociation) { diff --git a/src/main/services/messaging.ts b/src/main/services/messaging.ts index fb168abbe..d3ae32d40 100644 --- a/src/main/services/messaging.ts +++ b/src/main/services/messaging.ts @@ -13,6 +13,7 @@ import * as bookmarkMenu from '../menus/bookmarks'; import { showFindDialog } from '../dialogs/find'; import { getFormFillMenuItems } from '../utils'; import { showAddBookmarkDialog } from '../dialogs/add-bookmark'; +import { showExtensionDialog } from '../dialogs/extension-popup'; export const runMessagingService = (appWindow: AppWindow) => { const { id } = appWindow; @@ -85,6 +86,18 @@ export const runMessagingService = (appWindow: AppWindow) => { showAddBookmarkDialog(appWindow.win, left, top); }); + if (process.env.ENABLE_EXTENSIONS) { + ipcMain.on(`show-extension-popup-${id}`, (e, left, top, url, inspect) => { + showExtensionDialog(appWindow.win, left, top, url, inspect); + }); + } + + // ipcMain.on(`hide-extension-popup-${id}`, (e) => { + // if (appWindow.dialogs.extensionPopup.visible) { + // appWindow.dialogs.extensionPopup.hideVisually(); + // } + // }); + // ipcMain.on(`show-tabgroup-dialog-${id}`, (e, tabGroup) => { // appWindow.dialogs.tabGroupDialog.edit(tabGroup); // }); @@ -95,19 +108,6 @@ export const runMessagingService = (appWindow: AppWindow) => { // appWindow.dialogs.downloadsDialog.show(); // }); - // ipcMain.on(`show-extension-popup-${id}`, (e, left, top, url, inspect) => { - // appWindow.dialogs.extensionPopup.left = left; - // appWindow.dialogs.extensionPopup.top = top; - // appWindow.dialogs.extensionPopup.url = url; - // appWindow.dialogs.extensionPopup.show(inspect); - // }); - - // ipcMain.on(`hide-extension-popup-${id}`, (e) => { - // if (appWindow.dialogs.extensionPopup.visible) { - // appWindow.dialogs.extensionPopup.hideVisually(); - // } - // }); - // ipcMain.on(`show-zoom-dialog-${id}`, (e, left, top) => { // appWindow.dialogs.zoomDialog.left = left; // appWindow.dialogs.zoomDialog.top = top; diff --git a/src/main/sessions-service.ts b/src/main/sessions-service.ts index 15b230ba9..d1c9daca5 100644 --- a/src/main/sessions-service.ts +++ b/src/main/sessions-service.ts @@ -117,6 +117,7 @@ export class SessionsService { id, }); + // TODO: download dialog. // TODO(sentialx): clean up the download listeners this.view.on('will-download', (event, item, webContents) => { const fileName = item.getFilename(); @@ -142,7 +143,7 @@ export class SessionsService { const downloadItem = getDownloadItem(item, id); - window.dialogs.downloadsDialog.send('download-started', downloadItem); + // window.dialogs.downloadsDialog.send('download-started', downloadItem); window.send('download-started', downloadItem); item.on('updated', (event, state) => { @@ -156,16 +157,17 @@ export class SessionsService { const data = getDownloadItem(item, id); - window.dialogs.downloadsDialog.send('download-progress', data); + //window.dialogs.downloadsDialog.send('download-progress', data); window.send('download-progress', data); }); item.once('done', async (event, state) => { if (state === 'completed') { - window.dialogs.downloadsDialog.send('download-completed', id); + //window.dialogs.downloadsDialog.send('download-completed', id); window.send( 'download-completed', id, - !window.dialogs.downloadsDialog.visible, + false, + //!window.dialogs.downloadsDialog.visible, ); if (process.env.ENABLE_EXTENSIONS && extname(fileName) === '.crx') { @@ -218,7 +220,7 @@ export class SessionsService { const downloadItem = getDownloadItem(item, id); - window.dialogs.downloadsDialog.send('download-started', downloadItem); + // window.dialogs.downloadsDialog.send('download-started', downloadItem); window.send('download-started', downloadItem); item.on('updated', (event, state) => { @@ -232,16 +234,17 @@ export class SessionsService { const data = getDownloadItem(item, id); - window.dialogs.downloadsDialog.send('download-progress', data); + // window.dialogs.downloadsDialog.send('download-progress', data); window.send('download-progress', data); }); item.once('done', async (event, state) => { if (state === 'completed') { - window.dialogs.downloadsDialog.send('download-completed', id); + // window.dialogs.downloadsDialog.send('download-completed', id); window.send( 'download-completed', id, - !window.dialogs.downloadsDialog.visible, + false, + //!window.dialogs.downloadsDialog.visible, ); } else { console.log(`Download failed: ${state}`); diff --git a/src/preloads/popup-preload.ts b/src/preloads/popup-preload.ts index a7f8de360..449855ae8 100644 --- a/src/preloads/popup-preload.ts +++ b/src/preloads/popup-preload.ts @@ -3,10 +3,8 @@ import { ipcRenderer } from 'electron'; const updateBounds = () => { ipcRenderer.sendToHost( 'webview-size', - document.documentElement.offsetWidth || - document.documentElement.scrollWidth, - document.documentElement.offsetHeight || - document.documentElement.scrollHeight, + document.body.offsetWidth || document.body.scrollWidth, + document.body.offsetHeight || document.body.scrollHeight, ); }; diff --git a/src/renderer/views/app/components/BrowserAction/index.tsx b/src/renderer/views/app/components/BrowserAction/index.tsx index 42226e921..ca26a16f0 100644 --- a/src/renderer/views/app/components/BrowserAction/index.tsx +++ b/src/renderer/views/app/components/BrowserAction/index.tsx @@ -72,7 +72,7 @@ const onMouseDown = (data: IBrowserAction) => async (e: any) => { canOpenPopup = !store.dialogsVisibility['extension-popup'] || data.extensionId !== store.extensions.currentlyToggledPopup; - ipcRenderer.send(`hide-extension-popup-${store.windowId}`); + // ipcRenderer.send(`hide-extension-popup-${store.windowId}`); }; export const BrowserAction = observer(({ data }: Props) => { diff --git a/src/renderer/views/extension-popup/index.tsx b/src/renderer/views/extension-popup/index.tsx index e3f7d4049..92e0f143f 100644 --- a/src/renderer/views/extension-popup/index.tsx +++ b/src/renderer/views/extension-popup/index.tsx @@ -98,12 +98,9 @@ const createWebview = (url: string, inspect: boolean) => { container.appendChild(webview); }; -ipcRenderer.on('visible', (e, flag, data) => { - if (flag) { - const { url, inspect } = data; - createWebview(url, inspect); - } else { - visible = false; - hide(); - } +ipcRenderer.on('data', (e, data) => { + const { url, inspect } = data; + createWebview(url, inspect); }); + +ipcRenderer.send(`loaded-${getWebContentsId()}`); From de01a8fbb746517a3b77b355a11775ed295917a3 Mon Sep 17 00:00:00 2001 From: sentialx Date: Sun, 3 May 2020 22:31:30 +0200 Subject: [PATCH 17/21] feat: downloads dialog --- src/main/dialogs/downloads.ts | 88 +++++++++---------- src/main/services/dialogs-service.ts | 54 ++++++------ src/main/services/messaging.ts | 15 +--- src/main/sessions-service.ts | 49 +++++++---- .../views/downloads-dialog/store/index.ts | 6 +- 5 files changed, 104 insertions(+), 108 deletions(-) diff --git a/src/main/dialogs/downloads.ts b/src/main/dialogs/downloads.ts index bf197668a..f050ad427 100644 --- a/src/main/dialogs/downloads.ts +++ b/src/main/dialogs/downloads.ts @@ -1,53 +1,45 @@ -import { AppWindow } from '../windows'; -import { Dialog } from '.'; -import { ipcMain } from 'electron'; +import { BrowserWindow } from 'electron'; +import { Application } from '../application'; import { + DIALOG_MARGIN_TOP, DIALOG_MARGIN, DIALOG_TOP, - DIALOG_MARGIN_TOP, } from '~/constants/design'; -const WIDTH = 350; - -export class DownloadsDialog extends Dialog { - public visible = false; - - private height = 0; - - public left = 0; - public top = 0; - - constructor(appWindow: AppWindow) { - super(appWindow, { - name: 'downloads-dialog', - bounds: { - width: WIDTH, - height: 0, - }, - }); - - ipcMain.on(`height-${this.id}`, (e, height) => { - this.height = height; - this.rearrange(); - }); - } - - public rearrange() { - const { height } = this.appWindow.win.getContentBounds(); - - const maxHeight = height - DIALOG_TOP - 16; - - super.rearrange({ - x: Math.round(this.left - WIDTH + DIALOG_MARGIN), - height: Math.round(Math.min(height, this.height + 28)), - y: Math.round(this.top - DIALOG_MARGIN_TOP), - }); - - this.send(`max-height`, Math.min(maxHeight, this.height)); - } - - public async show() { - await super.show(); - this.send('visible', true); - } -} +export const showDownloadsDialog = ( + browserWindow: BrowserWindow, + x: number, + y: number, +) => { + let height = 0; + + const dialog = Application.instance.dialogs.show({ + name: 'downloads-dialog', + browserWindow, + getBounds: () => { + const winBounds = browserWindow.getContentBounds(); + const maxHeight = winBounds.height - DIALOG_TOP - 16; + + height = Math.round(Math.min(winBounds.height, height + 28)); + + dialog.browserView.webContents.send( + `max-height`, + Math.min(maxHeight, height), + ); + + return { + x: x - 350 + DIALOG_MARGIN, + y: y - DIALOG_MARGIN_TOP, + width: 350, + height, + }; + }, + }); + + if (!dialog) return; + + dialog.on('height', (e, h) => { + height = h; + dialog.rearrange(); + }); +}; diff --git a/src/main/services/dialogs-service.ts b/src/main/services/dialogs-service.ts index 7dfc5459b..548a935a2 100644 --- a/src/main/services/dialogs-service.ts +++ b/src/main/services/dialogs-service.ts @@ -109,39 +109,14 @@ export class DialogsService { this.browserViewDetails.set(browserView.id, true); - const rearrange = (rect?: IRectangle) => { - rect = rect || {}; - browserView.setBounds({ - x: 0, - y: 0, - width: 0, - height: 0, - ...roundifyRectangle(getBounds()), - ...roundifyRectangle(rect), - }); - }; - if (foundDialog) { browserWindow.addBrowserView(browserView); - rearrange(); + foundDialog.rearrange(); return null; } browserWindow.addBrowserView(browserView); - rearrange({ x: 0, y: 0, width: 1, height: 1 }); - - browserView.webContents.once('dom-ready', () => { - rearrange(); - browserView.webContents.focus(); - }); - - if (process.env.NODE_ENV === 'development') { - browserView.webContents.loadURL(`http://localhost:4444/${name}.html`); - } else { - browserView.webContents.loadURL( - join('file://', app.getAppPath(), `build/${name}.html`), - ); - } + browserView.setBounds({ x: 0, y: 0, width: 1, height: 1 }); if (devtools) { browserView.webContents.openDevTools({ mode: 'detach' }); @@ -214,9 +189,32 @@ export class DialogsService { ipcMain.on(channel, (...args) => cb(...args)); channels.push(channel); }, - rearrange, + rearrange: (rect) => { + rect = rect || {}; + browserView.setBounds({ + x: 0, + y: 0, + width: 0, + height: 0, + ...roundifyRectangle(getBounds()), + ...roundifyRectangle(rect), + }); + }, }; + browserView.webContents.once('dom-ready', () => { + dialog.rearrange(); + browserView.webContents.focus(); + }); + + if (process.env.NODE_ENV === 'development') { + browserView.webContents.loadURL(`http://localhost:4444/${name}.html`); + } else { + browserView.webContents.loadURL( + join('file://', app.getAppPath(), `build/${name}.html`), + ); + } + if (tabAssociation) { activateHandler = (id: number) => { const visible = dialog.tabIds.includes(id); diff --git a/src/main/services/messaging.ts b/src/main/services/messaging.ts index d3ae32d40..d24090cca 100644 --- a/src/main/services/messaging.ts +++ b/src/main/services/messaging.ts @@ -14,6 +14,7 @@ import { showFindDialog } from '../dialogs/find'; import { getFormFillMenuItems } from '../utils'; import { showAddBookmarkDialog } from '../dialogs/add-bookmark'; import { showExtensionDialog } from '../dialogs/extension-popup'; +import { showDownloadsDialog } from '../dialogs/downloads'; export const runMessagingService = (appWindow: AppWindow) => { const { id } = appWindow; @@ -92,22 +93,14 @@ export const runMessagingService = (appWindow: AppWindow) => { }); } - // ipcMain.on(`hide-extension-popup-${id}`, (e) => { - // if (appWindow.dialogs.extensionPopup.visible) { - // appWindow.dialogs.extensionPopup.hideVisually(); - // } - // }); + ipcMain.on(`show-downloads-dialog-${id}`, (e, left, top) => { + showDownloadsDialog(appWindow.win, left, top); + }); // ipcMain.on(`show-tabgroup-dialog-${id}`, (e, tabGroup) => { // appWindow.dialogs.tabGroupDialog.edit(tabGroup); // }); - // ipcMain.on(`show-downloads-dialog-${id}`, (e, left, top) => { - // appWindow.dialogs.downloadsDialog.left = left; - // appWindow.dialogs.downloadsDialog.top = top; - // appWindow.dialogs.downloadsDialog.show(); - // }); - // ipcMain.on(`show-zoom-dialog-${id}`, (e, left, top) => { // appWindow.dialogs.zoomDialog.left = left; // appWindow.dialogs.zoomDialog.top = top; diff --git a/src/main/sessions-service.ts b/src/main/sessions-service.ts index d1c9daca5..b145f6ee1 100644 --- a/src/main/sessions-service.ts +++ b/src/main/sessions-service.ts @@ -117,7 +117,16 @@ export class SessionsService { id, }); - // TODO: download dialog. + const downloadsDialog = () => + Application.instance.dialogs.getDynamic('downloads-dialog')?.browserView + ?.webContents; + + const downloads: IDownloadItem[] = []; + + ipcMain.handle('get-downloads', () => { + return downloads; + }); + // TODO(sentialx): clean up the download listeners this.view.on('will-download', (event, item, webContents) => { const fileName = item.getFilename(); @@ -142,8 +151,9 @@ export class SessionsService { } const downloadItem = getDownloadItem(item, id); + downloads.push(downloadItem); - // window.dialogs.downloadsDialog.send('download-started', downloadItem); + downloadsDialog()?.send('download-started', downloadItem); window.send('download-started', downloadItem); item.on('updated', (event, state) => { @@ -157,18 +167,18 @@ export class SessionsService { const data = getDownloadItem(item, id); - //window.dialogs.downloadsDialog.send('download-progress', data); + downloadsDialog()?.send('download-progress', data); window.send('download-progress', data); + + Object.assign(downloadItem, data); }); item.once('done', async (event, state) => { if (state === 'completed') { - //window.dialogs.downloadsDialog.send('download-completed', id); - window.send( - 'download-completed', - id, - false, - //!window.dialogs.downloadsDialog.visible, - ); + const dialog = downloadsDialog(); + dialog?.send('download-completed', id); + window.send('download-completed', id, !!dialog); + + downloadItem.completed = true; if (process.env.ENABLE_EXTENSIONS && extname(fileName) === '.crx') { const crxBuf = await promises.readFile(item.savePath); @@ -219,8 +229,9 @@ export class SessionsService { ); const downloadItem = getDownloadItem(item, id); + downloads.push(downloadItem); - // window.dialogs.downloadsDialog.send('download-started', downloadItem); + downloadsDialog()?.send('download-started', downloadItem); window.send('download-started', downloadItem); item.on('updated', (event, state) => { @@ -234,18 +245,18 @@ export class SessionsService { const data = getDownloadItem(item, id); - // window.dialogs.downloadsDialog.send('download-progress', data); + Object.assign(downloadItem, data); + + downloadsDialog()?.send('download-progress', data); window.send('download-progress', data); }); item.once('done', async (event, state) => { + const dialog = downloadsDialog(); if (state === 'completed') { - // window.dialogs.downloadsDialog.send('download-completed', id); - window.send( - 'download-completed', - id, - false, - //!window.dialogs.downloadsDialog.visible, - ); + dialog?.send('download-completed', id); + window.send('download-completed', id, !!dialog); + + downloadItem.completed = true; } else { console.log(`Download failed: ${state}`); } diff --git a/src/renderer/views/downloads-dialog/store/index.ts b/src/renderer/views/downloads-dialog/store/index.ts index 0ed0cf43a..46a115e76 100644 --- a/src/renderer/views/downloads-dialog/store/index.ts +++ b/src/renderer/views/downloads-dialog/store/index.ts @@ -13,6 +13,8 @@ export class Store extends DialogStore { public constructor() { super(); + this.init(); + ipcRenderer.on('download-started', (e, item) => { this.downloads.push(item); }); @@ -38,8 +40,8 @@ export class Store extends DialogStore { }); } - public onVisibilityChange(visible: boolean) { - this.visible = visible; + public async init() { + this.downloads = await ipcRenderer.invoke('get-downloads'); } } From a3564dbf924c6d7ccd4990750d6b0d0984b3a12d Mon Sep 17 00:00:00 2001 From: sentialx Date: Sun, 3 May 2020 23:28:48 +0200 Subject: [PATCH 18/21] feat: zoom dialog --- src/main/dialogs/zoom.ts | 53 +++++++++++--------------- src/main/services/messaging.ts | 11 +++--- src/main/view-manager.ts | 12 ++++-- src/main/windows-service.ts | 7 +++- src/renderer/views/zoom/store/index.ts | 26 ++++++------- 5 files changed, 55 insertions(+), 54 deletions(-) diff --git a/src/main/dialogs/zoom.ts b/src/main/dialogs/zoom.ts index 73c973c15..625c23c4d 100644 --- a/src/main/dialogs/zoom.ts +++ b/src/main/dialogs/zoom.ts @@ -1,34 +1,27 @@ -import { AppWindow } from '../windows'; -import { Dialog } from '.'; -import { DIALOG_MARGIN, DIALOG_MARGIN_TOP } from '~/constants/design'; +import { BrowserWindow } from 'electron'; +import { Application } from '../application'; +import { DIALOG_MARGIN_TOP, DIALOG_MARGIN } from '~/constants/design'; -const WIDTH = 280; +export const showZoomDialog = ( + browserWindow: BrowserWindow, + x: number, + y: number, +) => { + const tabId = Application.instance.windows.fromBrowserWindow(browserWindow) + .viewManager.selectedId; -export class ZoomDialog extends Dialog { - public visible = false; + const dialog = Application.instance.dialogs.show({ + name: 'zoom', + browserWindow, + getBounds: () => ({ + width: 280, + height: 100, + x: x - 280 + DIALOG_MARGIN, + y: y - DIALOG_MARGIN_TOP, + }), + }); - public left = 0; - public top = 0; + if (!dialog) return; - constructor(appWindow: AppWindow) { - super(appWindow, { - name: 'zoom', - bounds: { - width: WIDTH, - height: 100, - }, - }); - } - - public rearrange() { - super.rearrange({ - x: Math.round(this.left - WIDTH + DIALOG_MARGIN), - y: Math.round(this.top - DIALOG_MARGIN_TOP), - }); - } - - public async show() { - await super.show(); - this.send('visible', true); - } -} + dialog.handle('tab-id', () => tabId); +}; diff --git a/src/main/services/messaging.ts b/src/main/services/messaging.ts index d24090cca..a69851021 100644 --- a/src/main/services/messaging.ts +++ b/src/main/services/messaging.ts @@ -15,6 +15,7 @@ import { getFormFillMenuItems } from '../utils'; import { showAddBookmarkDialog } from '../dialogs/add-bookmark'; import { showExtensionDialog } from '../dialogs/extension-popup'; import { showDownloadsDialog } from '../dialogs/downloads'; +import { showZoomDialog } from '../dialogs/zoom'; export const runMessagingService = (appWindow: AppWindow) => { const { id } = appWindow; @@ -97,16 +98,14 @@ export const runMessagingService = (appWindow: AppWindow) => { showDownloadsDialog(appWindow.win, left, top); }); + ipcMain.on(`show-zoom-dialog-${id}`, (e, left, top) => { + showZoomDialog(appWindow.win, left, top); + }); + // ipcMain.on(`show-tabgroup-dialog-${id}`, (e, tabGroup) => { // appWindow.dialogs.tabGroupDialog.edit(tabGroup); // }); - // ipcMain.on(`show-zoom-dialog-${id}`, (e, left, top) => { - // appWindow.dialogs.zoomDialog.left = left; - // appWindow.dialogs.zoomDialog.top = top; - // appWindow.dialogs.zoomDialog.show(); - // }); - // ipcMain.on(`edit-tabgroup-${id}`, (e, tabGroup) => { // appWindow.send(`edit-tabgroup`, tabGroup); // }); diff --git a/src/main/view-manager.ts b/src/main/view-manager.ts index 3d5dca32d..75abc1e32 100644 --- a/src/main/view-manager.ts +++ b/src/main/view-manager.ts @@ -11,6 +11,7 @@ import { } from '~/constants/web-contents'; import { extensions } from 'electron-extensions'; import { EventEmitter } from 'events'; +import { Application } from './application'; export class ViewManager extends EventEmitter { public views = new Map(); @@ -248,10 +249,13 @@ export class ViewManager extends EventEmitter { } public emitZoomUpdate(showDialog = true) { - this.window.dialogs.zoomDialog.send( - 'zoom-factor-updated', - this.selected.webContents.zoomFactor, - ); + Application.instance.dialogs + .getDynamic('zoom') + ?.browserView?.webContents?.send( + 'zoom-factor-updated', + this.selected.webContents.zoomFactor, + ); + this.window.webContents.send( 'zoom-factor-updated', this.selected.webContents.zoomFactor, diff --git a/src/main/windows-service.ts b/src/main/windows-service.ts index 4fa8425e2..9f648860d 100644 --- a/src/main/windows-service.ts +++ b/src/main/windows-service.ts @@ -1,6 +1,6 @@ import { AppWindow } from './windows/app'; import { extensions } from 'electron-extensions'; -import { BrowserWindow } from 'electron'; +import { BrowserWindow, ipcMain } from 'electron'; export class WindowsService { public list: AppWindow[] = []; @@ -35,6 +35,11 @@ export class WindowsService { return view.id; }; } + + ipcMain.handle('get-tab-zoom', (e, tabId) => { + return this.findByBrowserView(tabId).viewManager.views.get(tabId) + .webContents.zoomFactor; + }); } public open(incognito = false) { diff --git a/src/renderer/views/zoom/store/index.ts b/src/renderer/views/zoom/store/index.ts index b2e008670..2472d5270 100644 --- a/src/renderer/views/zoom/store/index.ts +++ b/src/renderer/views/zoom/store/index.ts @@ -10,29 +10,29 @@ export class Store extends DialogStore { public constructor() { super(); + this.init(); + } + + public async init() { + const zoomFactorChange = reaction( + () => this.zoomFactor, + () => this.resetHideTimer(), + ); ipcRenderer.on('zoom-factor-updated', (e, zoomFactor) => { this.zoomFactor = zoomFactor; }); - const zoomFactorChange = reaction( - () => this.zoomFactor, - () => this.resetHideTimer() - ) - } + const tabId = await this.invoke('tab-id'); + this.zoomFactor = await ipcRenderer.invoke('get-tab-zoom', tabId); - public async onVisibilityChange(visible: boolean) { - this.visible = visible; - if (visible) { - this.resetHideTimer(); - } + this.resetHideTimer(); } public resetHideTimer() { clearTimeout(this.timer); - var context = this; - this.timer = setTimeout(function () { - context.hide(); + this.timer = setTimeout(() => { + this.hide(); }, 1500); } From 6c24218c7f7cba0ce12f6d90bf7b65c4cca2d364 Mon Sep 17 00:00:00 2001 From: sentialx Date: Sun, 3 May 2020 23:36:46 +0200 Subject: [PATCH 19/21] feat: tab group dialog --- src/main/dialogs/tabgroup.ts | 52 +++++++++---------- src/main/services/messaging.ts | 13 ++--- .../views/tabgroup/components/App/index.tsx | 2 +- src/renderer/views/tabgroup/store/index.ts | 19 ++++--- 4 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/main/dialogs/tabgroup.ts b/src/main/dialogs/tabgroup.ts index 85950182f..1933c8a11 100644 --- a/src/main/dialogs/tabgroup.ts +++ b/src/main/dialogs/tabgroup.ts @@ -1,31 +1,27 @@ -import { AppWindow } from '../windows'; -import { TITLEBAR_HEIGHT, DIALOG_MARGIN_TOP } from '~/constants/design'; -import { Dialog } from '.'; +import { BrowserWindow } from 'electron'; +import { Application } from '../application'; +import { + DIALOG_MARGIN_TOP, + DIALOG_MARGIN, + TITLEBAR_HEIGHT, +} from '~/constants/design'; -const WIDTH = 266; -const HEIGHT = 180; +export const showTabGroupDialog = ( + browserWindow: BrowserWindow, + tabGroup: any, +) => { + const dialog = Application.instance.dialogs.show({ + name: 'tabgroup', + browserWindow, + getBounds: () => ({ + width: 266, + height: 180, + x: tabGroup.x - DIALOG_MARGIN, + y: TITLEBAR_HEIGHT - DIALOG_MARGIN_TOP, + }), + }); -export class TabGroupDialog extends Dialog { - public visible = false; + if (!dialog) return; - constructor(appWindow: AppWindow) { - super(appWindow, { - name: 'tabgroup', - bounds: { - width: WIDTH, - height: HEIGHT, - y: TITLEBAR_HEIGHT - DIALOG_MARGIN_TOP, - }, - }); - } - - public rearrange() { - super.rearrange({ x: this.bounds.x - 20 }); - } - - public edit(tabGroup: any) { - this.bounds.x = Math.round(tabGroup.x); - super.show(); - this.send('visible', true, tabGroup); - } -} + dialog.handle('tabgroup', () => tabGroup); +}; diff --git a/src/main/services/messaging.ts b/src/main/services/messaging.ts index a69851021..c395467e6 100644 --- a/src/main/services/messaging.ts +++ b/src/main/services/messaging.ts @@ -16,6 +16,7 @@ import { showAddBookmarkDialog } from '../dialogs/add-bookmark'; import { showExtensionDialog } from '../dialogs/extension-popup'; import { showDownloadsDialog } from '../dialogs/downloads'; import { showZoomDialog } from '../dialogs/zoom'; +import { showTabGroupDialog } from '../dialogs/tabgroup'; export const runMessagingService = (appWindow: AppWindow) => { const { id } = appWindow; @@ -102,13 +103,13 @@ export const runMessagingService = (appWindow: AppWindow) => { showZoomDialog(appWindow.win, left, top); }); - // ipcMain.on(`show-tabgroup-dialog-${id}`, (e, tabGroup) => { - // appWindow.dialogs.tabGroupDialog.edit(tabGroup); - // }); + ipcMain.on(`show-tabgroup-dialog-${id}`, (e, tabGroup) => { + showTabGroupDialog(appWindow.win, tabGroup); + }); - // ipcMain.on(`edit-tabgroup-${id}`, (e, tabGroup) => { - // appWindow.send(`edit-tabgroup`, tabGroup); - // }); + ipcMain.on(`edit-tabgroup-${id}`, (e, tabGroup) => { + appWindow.send(`edit-tabgroup`, tabGroup); + }); ipcMain.on(`is-incognito-${id}`, (e) => { e.returnValue = appWindow.incognito; diff --git a/src/renderer/views/tabgroup/components/App/index.tsx b/src/renderer/views/tabgroup/components/App/index.tsx index 19fdef308..df8398878 100644 --- a/src/renderer/views/tabgroup/components/App/index.tsx +++ b/src/renderer/views/tabgroup/components/App/index.tsx @@ -45,7 +45,7 @@ export const App = hot( observer(() => { return ( - + Date: Sun, 3 May 2020 23:41:49 +0200 Subject: [PATCH 20/21] fix: minor final fixes --- src/main/services/auto-updater.ts | 4 +- src/main/services/messaging.ts | 59 ++++++++++--------- src/renderer/views/menu/store/index.ts | 14 ++--- .../views/search/components/App/index.tsx | 2 +- 4 files changed, 39 insertions(+), 40 deletions(-) diff --git a/src/main/services/auto-updater.ts b/src/main/services/auto-updater.ts index a5dc76c2c..3727c9161 100644 --- a/src/main/services/auto-updater.ts +++ b/src/main/services/auto-updater.ts @@ -24,7 +24,9 @@ export const runAutoUpdaterService = () => { for (const window of Application.instance.windows.list) { window.send('update-available'); - window.dialogs.menuDialog.send('update-available'); + Application.instance.dialogs + .getDynamic('menu') + ?.browserView?.webContents?.send('update-available'); } }); }; diff --git a/src/main/services/messaging.ts b/src/main/services/messaging.ts index c395467e6..27a8c1aca 100644 --- a/src/main/services/messaging.ts +++ b/src/main/services/messaging.ts @@ -116,27 +116,28 @@ export const runMessagingService = (appWindow: AppWindow) => { }); if (process.env.ENABLE_AUTOFILL) { - ipcMain.on(`form-fill-show-${id}`, async (e, rect, name, value) => { - const items = await getFormFillMenuItems(name, value); - - if (items.length) { - appWindow.dialogs.formFillDialog.send(`formfill-get-items`, items); - appWindow.dialogs.formFillDialog.inputRect = rect; - - appWindow.dialogs.formFillDialog.resize( - items.length, - items.find((r) => r.subtext) != null, - ); - appWindow.dialogs.formFillDialog.rearrange(); - appWindow.dialogs.formFillDialog.show(false); - } else { - appWindow.dialogs.formFillDialog.hide(); - } - }); - - ipcMain.on(`form-fill-hide-${id}`, () => { - appWindow.dialogs.formFillDialog.hide(); - }); + // TODO: autofill + // ipcMain.on(`form-fill-show-${id}`, async (e, rect, name, value) => { + // const items = await getFormFillMenuItems(name, value); + + // if (items.length) { + // appWindow.dialogs.formFillDialog.send(`formfill-get-items`, items); + // appWindow.dialogs.formFillDialog.inputRect = rect; + + // appWindow.dialogs.formFillDialog.resize( + // items.length, + // items.find((r) => r.subtext) != null, + // ); + // appWindow.dialogs.formFillDialog.rearrange(); + // appWindow.dialogs.formFillDialog.show(false); + // } else { + // appWindow.dialogs.formFillDialog.hide(); + // } + // }); + + // ipcMain.on(`form-fill-hide-${id}`, () => { + // appWindow.dialogs.formFillDialog.hide(); + // }); ipcMain.on( `form-fill-update-${id}`, @@ -166,15 +167,15 @@ export const runMessagingService = (appWindow: AppWindow) => { }, ); - ipcMain.on(`credentials-show-${id}`, (e, data) => { - appWindow.dialogs.credentialsDialog.send('credentials-update', data); - appWindow.dialogs.credentialsDialog.rearrange(); - appWindow.dialogs.credentialsDialog.show(); - }); + // ipcMain.on(`credentials-show-${id}`, (e, data) => { + // appWindow.dialogs.credentialsDialog.send('credentials-update', data); + // appWindow.dialogs.credentialsDialog.rearrange(); + // appWindow.dialogs.credentialsDialog.show(); + // }); - ipcMain.on(`credentials-hide-${id}`, () => { - appWindow.dialogs.credentialsDialog.hide(); - }); + // ipcMain.on(`credentials-hide-${id}`, () => { + // appWindow.dialogs.credentialsDialog.hide(); + // }); ipcMain.on(`credentials-save-${id}`, async (e, data) => { const { username, password, update, oldUsername } = data; diff --git a/src/renderer/views/menu/store/index.ts b/src/renderer/views/menu/store/index.ts index 997bfacba..eb33cdac8 100644 --- a/src/renderer/views/menu/store/index.ts +++ b/src/renderer/views/menu/store/index.ts @@ -17,16 +17,12 @@ export class Store extends DialogStore { }); } - public async onVisibilityChange(visible: boolean) { - this.visible = visible; - - if (visible) { - if (remote.getCurrentWindow()) { - this.alwaysOnTop = remote.getCurrentWindow().isAlwaysOnTop(); - } - - this.updateAvailable = await ipcRenderer.invoke('is-update-available'); + public async init() { + if (remote.getCurrentWindow()) { + this.alwaysOnTop = remote.getCurrentWindow().isAlwaysOnTop(); } + + this.updateAvailable = await ipcRenderer.invoke('is-update-available'); } public async save() { diff --git a/src/renderer/views/search/components/App/index.tsx b/src/renderer/views/search/components/App/index.tsx index e03cf7be8..d1b4fff88 100644 --- a/src/renderer/views/search/components/App/index.tsx +++ b/src/renderer/views/search/components/App/index.tsx @@ -119,7 +119,7 @@ export const App = hot( return ( - + Date: Sun, 3 May 2020 23:46:28 +0200 Subject: [PATCH 21/21] fix: final fixes --- src/main/menus/main.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/menus/main.ts b/src/main/menus/main.ts index a963d3d78..42a10ba8f 100644 --- a/src/main/menus/main.ts +++ b/src/main/menus/main.ts @@ -4,6 +4,7 @@ import { viewSource, saveAs, printPage } from './common-actions'; import { WEBUI_BASE_URL, WEBUI_URL_SUFFIX } from '~/constants/files'; import { AppWindow } from '../windows'; import { Application } from '../application'; +import { showMenuDialog } from '../dialogs/menu'; const isMac = process.platform === 'darwin'; @@ -126,12 +127,14 @@ export const getMainMenu = () => { // Focus address bar ...createMenuItem(['Ctrl+Space', 'CmdOrCtrl+L', 'Alt+D', 'F6'], () => { - Application.instance.windows.current.dialogs.searchDialog.show(); + Application.instance.dialogs + .getPersistent('search') + .show(Application.instance.windows.current.win); }), // Toggle menu ...createMenuItem(['Alt+F', 'Alt+E'], () => { - Application.instance.windows.current.dialogs.menuDialog.show(); + Application.instance.windows.current.send('show-menu-dialog'); }), ], },