Merge pull request #180 from geeeeeeeeek/issue/refinements-on-server-side

Refinements on the main process
This commit is contained in:
Akash Nimare
2017-06-19 12:28:04 +05:30
committed by GitHub
17 changed files with 252 additions and 263 deletions

View File

@@ -7,24 +7,10 @@ function appUpdater() {
const log = require('electron-log');
log.transports.file.level = 'info';
autoUpdater.logger = log;
/*
AutoUpdater.on('error', err => log.info(err));
autoUpdater.on('checking-for-update', () => log.info('checking-for-update'));
autoUpdater.on('update-available', () => log.info('update-available'));
autoUpdater.on('update-not-available', () => log.info('update-not-available'));
*/
// Ask the user if update is available
// eslint-disable-next-line no-unused-vars
autoUpdater.on('update-downloaded', (event, info) => {
// Let message = app.getName() + ' ' + info.releaseName + ' is now available. It will be installed the next time you restart the application.';
// if (info.releaseNotes) {
// const splitNotes = info.releaseNotes.split(/[^\r]\n/);
// message += '\n\nRelease notes:\n';
// splitNotes.forEach(notes => {
// message += notes + '\n\n';
// });
// }
// Ask user to update the app
dialog.showMessageBox({
type: 'question',

View File

@@ -1,16 +0,0 @@
const wurl = require('wurl');
// Check link if it's internal/external
function linkIsInternal(currentUrl, newUrl) {
const currentDomain = wurl('hostname', currentUrl);
const newDomain = wurl('hostname', newUrl);
return currentDomain === newDomain;
}
// We'll be needing this to open images in default browser
const skipImages = '.jpg|.gif|.png|.jpeg|.JPG|.PNG';
module.exports = {
linkIsInternal,
skipImages
};

View File

@@ -9,8 +9,6 @@ const BrowserWindow = electron.BrowserWindow;
const shell = electron.shell;
const appName = app.getName();
const {about} = require('./windowmanager');
function sendAction(action) {
const win = BrowserWindow.getAllWindows()[0];
@@ -135,8 +133,10 @@ const darwinTpl = [
submenu: [
{
label: 'Zulip desktop',
click() {
about();
click(item, focusedWindow) {
if (focusedWindow) {
sendAction('open-about');
}
}
},
{
@@ -270,8 +270,10 @@ const otherTpl = [
submenu: [
{
label: 'Zulip desktop',
click() {
about();
click(item, focusedWindow) {
if (focusedWindow) {
sendAction('open-about');
}
}
},
{

View File

@@ -1,95 +0,0 @@
'use strict';
const path = require('path');
const electron = require('electron');
const ipc = require('electron').ipcMain;
const APP_ICON = path.join(__dirname, '../resources', 'Icon');
const iconPath = () => {
return APP_ICON + (process.platform === 'win32' ? '.ico' : '.png');
};
let domainWindow;
let aboutWindow;
function onClosed() {
// Dereference the window
domainWindow = null;
aboutWindow = null;
}
// Change Zulip server Window
function createdomainWindow() {
const domainwin = new electron.BrowserWindow({
title: 'Switch Server',
frame: false,
height: 300,
resizable: false,
width: 400,
show: false,
icon: iconPath()
});
const domainURL = 'file://' + path.join(__dirname, '../renderer', 'pref.html');
domainwin.loadURL(domainURL);
domainwin.on('closed', onClosed);
return domainwin;
}
// Call this window onClick addDomain in tray
function addDomain() {
domainWindow = createdomainWindow();
domainWindow.once('ready-to-show', () => {
domainWindow.show();
});
setTimeout(() => {
if (domainWindow !== null) {
if (!domainWindow.isDestroyed()) {
domainWindow.destroy();
}
}
}, 15000);
}
// About window
function createAboutWindow() {
const aboutwin = new electron.BrowserWindow({
width: 500,
height: 500,
title: 'About Zulip Desktop',
show: false,
center: true,
fullscreen: false,
fullscreenable: false,
resizable: false
});
const aboutURL = 'file://' + path.join(__dirname, '../renderer', 'about.html');
aboutwin.loadURL(aboutURL);
aboutwin.on('closed', onClosed);
// Stop page to update it's title
aboutwin.on('page-title-updated', e => {
e.preventDefault();
});
aboutwin.on('closed', onClosed);
return aboutwin;
}
// Call this onClick About in tray
function about() {
aboutWindow = createAboutWindow();
aboutWindow.once('ready-to-show', () => {
aboutWindow.show();
});
}
ipc.on('trayabout', event => {
if (event) {
about();
}
});
module.exports = {
addDomain,
about
};

View File

@@ -116,11 +116,11 @@ html, body {
opacity: 0.8;
}
.tab .settings-tab {
.tab .functional-tab {
background: #eee;
}
.tab .settings-tab i {
.tab .functional-tab i {
font-size: 28px;
line-height: 36px;
}
@@ -151,6 +151,17 @@ html, body {
.tab .server-tab-badge {
display: none;
}
.tab .server-tab-badge.close-button {
width: 16px;
padding: 0 0 0 1px;
}
.tab .server-tab-badge.close-button i {
font-size: 13px;
line-height: 17px;
}
/*******************
* Webview Area *
*******************/

BIN
app/renderer/img/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,43 @@
'use strict';
const Tab = require(__dirname + '/../components/tab.js');
class FunctionalTab extends Tab {
template() {
return `<div class="tab">
<div class="server-tab-badge close-button">
<i class="material-icons">close</i>
</div>
<div class="server-tab functional-tab">
<i class="material-icons">${this.props.materialIcon}</i>
</div>
</div>`;
}
init() {
this.$el = this.generateNodeFromTemplate(this.template());
this.props.$root.appendChild(this.$el);
this.$closeButton = this.$el.getElementsByClassName('server-tab-badge')[0];
this.registerListeners();
}
registerListeners() {
super.registerListeners();
this.$el.addEventListener('mouseover', () => {
this.$closeButton.classList.add('active');
});
this.$el.addEventListener('mouseout', () => {
this.$closeButton.classList.remove('active');
});
this.$closeButton.addEventListener('click', e => {
this.props.onDestroy();
e.stopPropagation();
});
}
}
module.exports = FunctionalTab;

View File

@@ -0,0 +1,31 @@
'use strict';
const Tab = require(__dirname + '/../components/tab.js');
class ServerTab extends Tab {
template() {
return `<div class="tab">
<div class="server-tab-badge"></div>
<div class="server-tab" style="background-image: url(${this.props.icon});"></div>
</div>`;
}
init() {
super.init();
this.$badge = this.$el.getElementsByClassName('server-tab-badge')[0];
}
updateBadge(count) {
if (count > 0) {
const formattedCount = count > 999 ? '1K+' : count;
this.$badge.innerHTML = formattedCount;
this.$badge.classList.add('active');
} else {
this.$badge.classList.remove('active');
}
}
}
module.exports = ServerTab;

View File

@@ -3,65 +3,43 @@
const BaseComponent = require(__dirname + '/../components/base.js');
class Tab extends BaseComponent {
constructor(params) {
constructor(props) {
super();
const {url, icon, name, type, $root, onClick} = params;
this.url = url;
this.name = name;
this.icon = icon;
this.type = type;
this.$root = $root;
this.onClick = onClick;
this.props = props;
this.webview = this.props.webview;
this.init();
}
template() {
if (this.type === Tab.SERVER_TAB) {
return `<div class="tab" domain="${this.url}">
<div class="server-tab-badge"></div>
<div class="server-tab" style="background-image: url(${this.icon});"></div>
</div>`;
} else {
return `<div class="tab" domain="${this.url}">
<div class="server-tab-badge"></div>
<div class="server-tab settings-tab">
<i class="material-icons md-48">settings</i>
</div>
</div>`;
}
}
init() {
this.$el = this.generateNodeFromTemplate(this.template());
this.$badge = this.$el.getElementsByClassName('server-tab-badge')[0];
this.$root.appendChild(this.$el);
this.props.$root.appendChild(this.$el);
this.registerListeners();
}
updateBadge(count) {
if (count > 0) {
const formattedCount = count > 999 ? '1K+' : count;
this.$badge.innerHTML = formattedCount;
this.$badge.classList.add('active');
} else {
this.$badge.classList.remove('active');
}
registerListeners() {
this.$el.addEventListener('click', this.props.onClick);
}
registerListeners() {
this.$el.addEventListener('click', this.onClick);
isLoading() {
return this.webview.isLoading;
}
activate() {
this.$el.classList.add('active');
this.webview.load();
}
deactivate() {
this.$el.classList.remove('active');
this.webview.hide();
}
destroy() {
this.$el.parentNode.removeChild(this.$el);
this.webview.$el.parentNode.removeChild(this.webview.$el);
}
}

View File

@@ -2,38 +2,28 @@
const DomainUtil = require(__dirname + '/../utils/domain-util.js');
const SystemUtil = require(__dirname + '/../utils/system-util.js');
const {linkIsInternal, skipImages} = require(__dirname + '/../../../main/link-helper');
const LinkUtil = require(__dirname + '/../utils/link-util.js');
const {app, dialog, shell} = require('electron').remote;
const {ipcRenderer} = require('electron');
const BaseComponent = require(__dirname + '/../components/base.js');
class WebView extends BaseComponent {
constructor(params) {
constructor(props) {
super();
const {$root, url, index, name, isActive, onTitleChange, nodeIntegration} = params;
this.$root = $root;
this.index = index;
this.name = name;
this.url = url;
this.nodeIntegration = nodeIntegration;
this.props = props;
this.onTitleChange = onTitleChange;
this.zoomFactor = 1.0;
this.loading = false;
this.isActive = isActive;
this.domainUtil = new DomainUtil();
this.systemUtil = new SystemUtil();
this.badgeCount = 0;
}
template() {
return `<webview
id="webview-${this.index}"
class="disabled"
src="${this.url}"
${this.nodeIntegration ? 'nodeIntegration' : ''}
src="${this.props.url}"
${this.props.nodeIntegration ? 'nodeIntegration' : ''}
disablewebsecurity
preload="js/preload.js"
webpreferences="allowRunningInsecureContent, javascript=yes">
@@ -42,7 +32,7 @@ class WebView extends BaseComponent {
init() {
this.$el = this.generateNodeFromTemplate(this.template());
this.$root.appendChild(this.$el);
this.props.$root.appendChild(this.$el);
this.registerListeners();
}
@@ -50,9 +40,9 @@ class WebView extends BaseComponent {
registerListeners() {
this.$el.addEventListener('new-window', event => {
const {url} = event;
const domainPrefix = this.domainUtil.getDomain(this.index).url;
const domainPrefix = DomainUtil.getDomain(this.props.index).url;
if (linkIsInternal(domainPrefix, url) && url.match(skipImages) === null) {
if (LinkUtil.isInternal(domainPrefix, url)) {
event.preventDefault();
this.$el.loadURL(url);
} else {
@@ -64,14 +54,14 @@ class WebView extends BaseComponent {
this.$el.addEventListener('page-title-updated', event => {
const {title} = event;
this.badgeCount = this.getBadgeCount(title);
this.onTitleChange();
this.props.onTitleChange();
});
this.$el.addEventListener('dom-ready', this.show.bind(this));
this.$el.addEventListener('did-fail-load', event => {
const {errorDescription} = event;
const hasConnectivityErr = (this.systemUtil.connectivityERR.indexOf(errorDescription) >= 0);
const hasConnectivityErr = (SystemUtil.connectivityERR.indexOf(errorDescription) >= 0);
if (hasConnectivityErr) {
console.error('error', errorDescription);
this.checkConnectivity();
@@ -79,10 +69,10 @@ class WebView extends BaseComponent {
});
this.$el.addEventListener('did-start-loading', () => {
let userAgent = this.systemUtil.getUserAgent();
let userAgent = SystemUtil.getUserAgent();
if (!userAgent) {
this.systemUtil.setUserAgent(this.$el.getUserAgent());
userAgent = this.systemUtil.getUserAgent();
SystemUtil.setUserAgent(this.$el.getUserAgent());
userAgent = SystemUtil.getUserAgent();
}
this.$el.setUserAgent(userAgent);
});
@@ -95,14 +85,14 @@ class WebView extends BaseComponent {
show() {
// Do not show WebView if another tab was selected and this tab should be in background.
if (!this.isActive()) {
if (!this.props.isActive()) {
return;
}
this.$el.classList.remove('disabled');
this.focus();
this.loading = false;
this.onTitleChange(this.$el.getTitle());
this.props.onTitleChange(this.$el.getTitle());
}
focus() {

View File

@@ -5,7 +5,8 @@ const {ipcRenderer} = require('electron');
const DomainUtil = require(__dirname + '/js/utils/domain-util.js');
const WebView = require(__dirname + '/js/components/webview.js');
const Tab = require(__dirname + '/js/components/tab.js');
const ServerTab = require(__dirname + '/js/components/server-tab.js');
const FunctionalTab = require(__dirname + '/js/components/functional-tab.js');
class ServerManagerView {
constructor() {
@@ -17,21 +18,19 @@ class ServerManagerView {
this.$settingsButton = $actionsContainer.querySelector('#settings-action');
this.$content = document.getElementById('content');
this.settingsTabIndex = -1;
this.activeTabIndex = -1;
this.webviews = [];
this.tabs = [];
this.functionalTabs = {};
}
init() {
this.domainUtil = new DomainUtil();
this.initTabs();
this.initActions();
this.registerIpcs();
}
initTabs() {
const servers = this.domainUtil.getDomains();
const servers = DomainUtil.getDomains();
if (servers.length > 0) {
for (let i = 0; i < servers.length; i++) {
this.initServer(servers[i], i);
@@ -43,94 +42,115 @@ class ServerManagerView {
}
initServer(server, index) {
this.tabs.push(new Tab({
url: server.url,
name: server.alias,
this.tabs.push(new ServerTab({
icon: server.icon,
type: Tab.SERVER_TAB,
$root: this.$tabsContainer,
onClick: this.activateTab.bind(this, index)
}));
this.webviews.push(new WebView({
$root: this.$content,
index,
url: server.url,
name: server.alias,
isActive: () => {
return index === this.activeTabIndex;
},
onTitleChange: this.updateBadge.bind(this),
nodeIntegration: false
onClick: this.activateTab.bind(this, index),
webview: new WebView({
$root: this.$content,
index,
url: server.url,
name: server.alias,
isActive: () => {
return index === this.activeTabIndex;
},
onTitleChange: this.updateBadge.bind(this),
nodeIntegration: false
})
}));
}
initActions() {
this.$reloadButton.addEventListener('click', () => {
this.webviews[this.activeTabIndex].reload();
this.tabs[this.activeTabIndex].webview.reload();
});
this.$addServerButton.addEventListener('click', this.openSettings.bind(this));
this.$settingsButton.addEventListener('click', this.openSettings.bind(this));
}
openSettings() {
if (this.settingsTabIndex !== -1) {
this.activateTab(this.settingsTabIndex);
openFunctionalTab(tabProps) {
if (this.functionalTabs[tabProps.name]) {
this.activateTab(this.functionalTabs[tabProps.name]);
return;
}
const url = 'file://' + __dirname + '/preference.html';
this.settingsTabIndex = this.webviews.length;
this.functionalTabs[tabProps.name] = this.tabs.length;
this.tabs.push(new Tab({
url,
name: 'Settings',
type: Tab.SETTINGS_TAB,
this.tabs.push(new FunctionalTab({
materialIcon: tabProps.materialIcon,
$root: this.$tabsContainer,
onClick: this.activateTab.bind(this, this.settingsTabIndex)
onClick: this.activateTab.bind(this, this.functionalTabs[tabProps.name]),
onDestroy: this.destroyTab.bind(this, tabProps.name, this.functionalTabs[tabProps.name]),
webview: new WebView({
$root: this.$content,
index: this.functionalTabs[tabProps.name],
url: tabProps.url,
name: tabProps.name,
isActive: () => {
return this.functionalTabs[tabProps.name] === this.activeTabIndex;
},
onTitleChange: this.updateBadge.bind(this),
nodeIntegration: true
})
}));
this.webviews.push(new WebView({
$root: this.$content,
index: this.settingsTabIndex,
url,
name: 'Settings',
isActive: () => {
return this.settingsTabIndex === this.activeTabIndex;
},
onTitleChange: this.updateBadge.bind(this),
nodeIntegration: true
}));
this.activateTab(this.settingsTabIndex);
this.activateTab(this.functionalTabs[tabProps.name]);
}
activateTab(index) {
if (this.webviews[index].loading) {
openSettings() {
this.openFunctionalTab({
name: 'Settings',
materialIcon: 'settings',
url: `file://${__dirname}/preference.html`
});
}
openAbout() {
this.openFunctionalTab({
name: 'About',
materialIcon: 'sentiment_very_satisfied',
url: `file://${__dirname}/about.html`
});
}
activateTab(index, hideOldTab = true) {
if (this.tabs[index].loading) {
return;
}
if (this.activeTabIndex !== -1) {
if (this.activeTabIndex === index) {
return;
} else {
} else if (hideOldTab) {
this.tabs[this.activeTabIndex].deactivate();
this.webviews[this.activeTabIndex].hide();
}
}
this.tabs[index].activate();
this.activeTabIndex = index;
this.webviews[index].load();
this.tabs[index].activate();
}
destroyTab(name, index) {
if (this.tabs[index].loading) {
return;
}
this.tabs[index].destroy();
delete this.tabs[index];
delete this.functionalTabs[name];
this.activateTab(0, false);
}
updateBadge() {
let messageCountAll = 0;
for (let i = 0; i < this.webviews.length; i++) {
const count = this.webviews[i].badgeCount;
messageCountAll += count;
this.tabs[i].updateBadge(count);
for (let i = 0; i < this.tabs.length; i++) {
if (this.tabs[i] && this.tabs[i].updateBadge) {
const count = this.tabs[i].webview.badgeCount;
messageCountAll += count;
this.tabs[i].updateBadge(count);
}
}
ipcRenderer.send('update-badge', messageCountAll);
@@ -152,7 +172,7 @@ class ServerManagerView {
for (const key in webviewListeners) {
ipcRenderer.on(key, () => {
const activeWebview = this.webviews[this.activeTabIndex];
const activeWebview = this.tabs[this.activeTabIndex].webview;
if (activeWebview) {
activeWebview[webviewListeners[key]]();
}
@@ -160,6 +180,7 @@ class ServerManagerView {
}
ipcRenderer.on('open-settings', this.openSettings.bind(this));
ipcRenderer.on('open-about', this.openAbout.bind(this));
}
}

View File

@@ -13,13 +13,12 @@ class PreferenceView {
}
init() {
this.domainUtil = new DomainUtil();
this.initServers();
this.initActions();
}
initServers() {
const servers = this.domainUtil.getDomains();
const servers = DomainUtil.getDomains();
this.$serverInfoContainer.innerHTML = servers.length ? '' : 'Add your first server to get started!';
this.initNewServerForm();
@@ -64,7 +63,7 @@ class PreferenceView {
</div>`;
this.$serverInfoContainer.appendChild(this.insertNode(serverInfoTemplate));
document.getElementById(`delete-server-action-${index}`).addEventListener('click', () => {
this.domainUtil.removeDomain(index);
DomainUtil.removeDomain(index);
this.initServers();
// alert('Success. Reload to apply changes.');
ipcRenderer.send('reload-main');
@@ -109,13 +108,13 @@ class PreferenceView {
this.$newServerButton.classList.add('hidden');
});
this.$saveServerButton.addEventListener('click', () => {
this.domainUtil.checkDomain(this.$newServerUrl.value).then(domain => {
DomainUtil.checkDomain(this.$newServerUrl.value).then(domain => {
const server = {
alias: this.$newServerAlias.value,
url: domain,
icon: this.$newServerIcon.value
};
this.domainUtil.addDomain(server);
DomainUtil.addDomain(server);
this.$saveServerButton.classList.add('hidden');
this.$newServerButton.classList.remove('hidden');
this.$newServerForm.classList.add('hidden');

View File

@@ -117,7 +117,7 @@ const createTray = function () {
const contextMenu = Menu.buildFromTemplate([{
label: 'About',
click() {
ipcRenderer.send('trayabout');
sendAction('open-about');
}
},
{

View File

@@ -4,9 +4,17 @@ const {app} = require('electron').remote;
const JsonDB = require('node-json-db');
const request = require('request');
const defaultIconUrl = 'https://chat.zulip.org/static/images/logo/zulip-icon-128x128.271d0f6a0ca2.png';
let instance = null;
const defaultIconUrl = __dirname + '../../../img/icon.png';
class DomainUtil {
constructor() {
if (instance) {
return instance;
} else {
instance = this;
}
this.db = new JsonDB(app.getPath('userData') + '/domain.json', true, true);
// Migrate from old schema
if (this.db.getData('/').domain) {
@@ -16,6 +24,8 @@ class DomainUtil {
});
this.db.delete('/domain');
}
return instance;
}
getDomains() {
@@ -69,4 +79,4 @@ class DomainUtil {
}
}
module.exports = DomainUtil;
module.exports = new DomainUtil();

View File

@@ -0,0 +1,29 @@
'use strict';
const wurl = require('wurl');
let instance = null;
class LinkUtil {
constructor() {
if (instance) {
return instance;
} else {
instance = this;
}
return instance;
}
isInternal(currentUrl, newUrl) {
const currentDomain = wurl('hostname', currentUrl);
const newDomain = wurl('hostname', newUrl);
const skipImages = '.jpg|.gif|.png|.jpeg|.JPG|.PNG';
// We'll be needing this to open images in default browser
return (currentDomain === newDomain) && !newUrl.match(skipImages);
}
}
module.exports = new LinkUtil();

View File

@@ -52,4 +52,4 @@ class SystemUtil {
}
}
module.exports = SystemUtil;
module.exports = new SystemUtil();

View File

@@ -41,5 +41,5 @@
</div>
</div>
</body>
<script src="js/preference.js"></script>
<script src="js/pages/preference.js"></script>
</html>