Skip to content

BrowserView: Rebased and cleaned up #1018

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
57a5147
BrowserView: Add View wrapper Class for BrowserView.
vsvipul Jul 18, 2019
39642e4
BrowserView: Add ViewManager for managing multiple Views.
vsvipul Jul 18, 2019
24a98fb
BrowserView: Add BrowserView to index.ts.
vsvipul Jul 18, 2019
9a9e8e8
BrowserView: Remove webview props from tab.ts.
vsvipul Jul 18, 2019
cf8fb41
BrowserView: Remove webview usage and initialize BrowserView.
vsvipul Jul 19, 2019
152d045
BrowserView: Add logic for badgeCount update.
vsvipul Jul 22, 2019
4a63713
BrowserView: Remove tooltips and use title instead.
vsvipul Jul 22, 2019
9c1e01b
BrowserView: Add favicon-update event and fix view function calls.
vsvipul Jul 23, 2019
517f365
BrowserView: Destroy all views during reload-app.
vsvipul Jul 25, 2019
de902e5
BrowserView: Improvements to loading indicator.
vsvipul Jul 25, 2019
c3f5405
BrowserView: Improve ipc names.
vsvipul Jul 26, 2019
ed41a04
BrowserView: Add logic to show network error page.
vsvipul Jul 30, 2019
e8754f7
BrowserView: Add logic for custom CSS.
vsvipul Jul 30, 2019
d4754cf
BrowserView: Call fixBounds before setting view.
vsvipul Jul 31, 2019
d6012c8
gitignore: Add .vscode to gitignore.
vsvipul Aug 4, 2019
32b69d8
BrowserView: Show views only when dom content loaded.
vsvipul Aug 4, 2019
9af2b3e
BrowserView: Rename webview instances to view and handle tray toggle.
vsvipul Aug 7, 2019
3066088
BrowserView: Make separate callViewFunction.
vsvipul Aug 11, 2019
80b887a
BrowserView: Fix focus switching.
vsvipul Aug 12, 2019
3ebb226
BrowserView: Remove show dom content on load.
vsvipul Aug 15, 2019
97071c5
BrowserView: Add documentation and fix fullScreen.
vsvipul Aug 16, 2019
051086c
BrowserView: Port reconnection logic to BV.
vsvipul Sep 2, 2019
b2bf6d8
BrowserView: Get enabled flag for notifs settings.
kanishk98 Oct 9, 2019
fd8a26f
BrowserView: Port notification settings to BV.
kanishk98 Oct 9, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,6 @@ config.gypi

# Ignore all the typescript compiled files
app/**/*.js

# text editor files
.vscode/
16 changes: 12 additions & 4 deletions app/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as BadgeSettings from '../renderer/js/pages/preference/badge-settings';
import * as ConfigUtil from '../renderer/js/utils/config-util';
import * as ProxyUtil from '../renderer/js/utils/proxy-util';
import {sentryInit} from '../renderer/js/utils/sentry-util';
import ViewManager = require('./viewmanager');

import {appUpdater} from './autoupdater';
import * as AppMenu from './menu';
Expand All @@ -18,7 +19,8 @@ import {setAutoLaunch} from './startup';
let mainWindowState: windowStateKeeper.State;

// Prevent window being garbage collected
let mainWindow: Electron.BrowserWindow;
// eslint-disable-next-line import/no-mutable-exports
export let mainWindow: Electron.BrowserWindow;
let badgeCount: number;

let isQuitting = false;
Expand Down Expand Up @@ -88,8 +90,7 @@ function createMainWindow(): Electron.BrowserWindow {
webPreferences: {
enableRemoteModule: true,
nodeIntegration: true,
partition: 'persist:webviewsession',
webviewTag: true
partition: 'persist:viewsession' // May be persist:view
},
show: false
});
Expand Down Expand Up @@ -120,10 +121,12 @@ function createMainWindow(): Electron.BrowserWindow {
win.setTitle('Zulip');

win.on('enter-full-screen', () => {
ViewManager.fixBounds();
win.webContents.send('enter-fullscreen');
});

win.on('leave-full-screen', () => {
ViewManager.fixBounds();
win.webContents.send('leave-fullscreen');
});

Expand Down Expand Up @@ -259,6 +262,7 @@ ${error}`

// Reload full app not just webview, useful in debugging
ipcMain.on('reload-full-app', () => {
ViewManager.destroyAll();
mainWindow.reload();
page.send('destroytray');
});
Expand All @@ -283,6 +287,10 @@ ${error}`
page.send('toggle-autohide-menubar', showMenubar, true);
});

ipcMain.on('fix-bounds', () => {
ViewManager.fixBounds();
});

ipcMain.on('update-badge', (_event: Electron.IpcMainEvent, messageCount: number) => {
badgeCount = messageCount;
BadgeSettings.updateBadge(badgeCount, mainWindow);
Expand All @@ -301,7 +309,7 @@ ${error}`
AppMenu.setMenu(props);
const activeTab = props.tabs[props.activeTabIndex];
if (activeTab) {
mainWindow.setTitle(`Zulip - ${activeTab.webviewName}`);
mainWindow.setTitle(`Zulip - ${activeTab.name}`);
}
});

Expand Down
227 changes: 227 additions & 0 deletions app/main/view.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
'use strict';

import { BrowserView, BrowserWindow, app, dialog } from 'electron';

import path from 'path';
import fs from 'fs';
import * as ConfigUtil from '../renderer/js/utils/config-util';
import * as SystemUtil from '../renderer/js/utils/system-util';
const shouldSilentView = ConfigUtil.getConfigItem('silent');

export interface ViewProps {
index: number;
url: string;
name: string;
nodeIntegration: boolean;
preload: boolean;
}

// Class View is a wrapper around electron's BrowserView
// Multiples views are handled by ViewManager
export class View extends BrowserView {
index: number;
url: string;
zoomFactor: number;
customCSS: string | null;
loading: boolean;

constructor(public props: ViewProps) {
super({
webPreferences: {
preload: props.preload ? `${__dirname}/../renderer/js/preload.js` : '',
nodeIntegration: props.nodeIntegration,
partition: 'persist:view',
plugins: true
}
});
this.index = props.index;
this.url = props.url;
this.zoomFactor = 1.0;
this.loading = false;
this.customCSS = ConfigUtil.getConfigItem('customCSS');
this.registerListeners();
}

registerListeners(): void {
if (shouldSilentView) {
this.webContents.addListener('dom-ready', () => {
this.webContents.setAudioMuted(true);
});
}

this.webContents.addListener('did-navigate-in-page', () => {
const isSettingsPage = this.url.includes('renderer/preference.html');
if (isSettingsPage) {
return;
}
this.canGoBackButton();
});

this.webContents.addListener('did-navigate', () => {
const url = this.webContents.getURL();
const loggedIn = !url.endsWith('/login/');
this.sendAction('set-logged-in', loggedIn, this.index);
this.canGoBackButton();
});

this.webContents.addListener('did-start-loading', () => {
this.switchLoadingIndicator(true);
});

this.webContents.addListener('dom-ready', () => {
this.switchLoadingIndicator(false);
this.handleCSS();
});

this.webContents.addListener('did-fail-load', (e: Event, errorCode: number, errorDescription: string) => {
const hasConnectivityErr = SystemUtil.connectivityERR.includes(errorDescription);
if (hasConnectivityErr) {
console.error('error', errorDescription);
this.sendAction('network-error');
}
});

this.webContents.addListener('did-stop-loading', () => {
this.switchLoadingIndicator(false);
});

this.webContents.addListener('page-title-updated', (e: Event, title: string) => {
const badgeCount = this.getBadgeCount(title);
this.sendAction('update-badge-count', badgeCount, this.url);
});

this.webContents.addListener('page-favicon-updated', (e: Event, favicons: string[]) => {
// This returns a string of favicons URL. If there is a PM counts in unread messages then the URL would be like
// https://chat.zulip.org/static/images/favicon/favicon-pms.png
if (favicons[0].indexOf('favicon-pms') > 0 && process.platform === 'darwin') {
// This api is only supported on macOS
app.dock.setBadge('●');
// bounce the dock
if (ConfigUtil.getConfigItem('dockBouncing')) {
app.dock.bounce();
}
}
});

// this.webContents.addListener('new-window', (e: Event, urlToOpen: string) => { // Add logic to handle external link
// e.preventDefault();
// this.sendAction('handle-link', this.index, urlToOpen);
// });
}

handleCSS(): void {
// Injecting preload css in view to override some css rules
this.webContents.insertCSS(fs.readFileSync(path.join(__dirname, '../renderer/css/preload.css'), 'utf8'));

// get customCSS again from config util to avoid warning user again
this.customCSS = ConfigUtil.getConfigItem('customCSS');
if (this.customCSS) {
if (!fs.existsSync(this.customCSS)) {
this.customCSS = null;
ConfigUtil.setConfigItem('customCSS', null);
const errMsg = 'The custom css previously set is deleted!';
dialog.showErrorBox('custom css file deleted!', errMsg);
return;
}

this.webContents.insertCSS(fs.readFileSync(path.resolve(__dirname, this.customCSS), 'utf8'));
}
}

zoomIn(): void {
this.zoomFactor += 0.1;
this.webContents.setZoomFactor(this.zoomFactor);
}

zoomOut(): void {
this.zoomFactor -= 0.1;
this.webContents.setZoomFactor(this.zoomFactor);
}

zoomActualSize(): void {
this.zoomFactor = 1.0;
this.webContents.setZoomFactor(this.zoomFactor);
}

focus(): void {
this.webContents.focus();
}

reload(): void {
this.switchLoadingIndicator(true);
this.webContents.reload();
}

switchLoadingIndicator(state: boolean): void {
this.loading = state;
const isSettingsPage = this.url.includes('renderer/preference.html');
if (!isSettingsPage) {
this.sendAction('switch-loading', state, this.url);
}
}

forward(): void {
if (this.webContents.canGoForward()) {
this.webContents.goForward();
}
}

back(): void {
if (this.webContents.canGoBack()) {
this.webContents.goBack();
}
}

logOut(): void {
this.webContents.executeJavaScript('logout()');
}

showShortcut(): void {
this.webContents.executeJavaScript('shortcut()');
}

showNotificationSettings(): void {
this.webContents.executeJavaScript('showNotificationSettings()');
}

toggleDevTools(): void {
this.webContents.toggleDevTools();
}

canGoBackButton(): void {
if (this.webContents.canGoBack()) {
this.sendAction('switch-back', true);
} else {
this.sendAction('switch-back', false);
}
}

getBadgeCount(title: string): number {
const messageCountInTitle = (/\((\d+)\)/).exec(title);
return messageCountInTitle ? Number(messageCountInTitle[1]) : 0;
}

downloadUrl(url: string): void {
this.webContents.downloadURL(url);
}

loadUrl(url: string): void {
this.webContents.loadURL(url);
}

updateBadgeCount(title: string): void {
const badgeCount = this.getBadgeCount(title);
this.sendAction('update-badge-count', badgeCount, this.url);
}

// Send an action to renderer process.
sendAction(action: any, ...params: any[]): void {
const win = BrowserWindow.getAllWindows()[0];

if (process.platform === 'darwin') {
win.restore();
}

win.webContents.send(action, ...params);
}
}
Loading