mirror of
https://github.com/zulip/zulip-desktop.git
synced 2025-10-28 02:23:37 +00:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3dade768a7 | ||
|
|
7b3c7ba5fa | ||
|
|
616bc0f73b | ||
|
|
8ecdf1f18a | ||
|
|
d2daa65059 | ||
|
|
487ee538e3 | ||
|
|
6382c6d2b3 | ||
|
|
cbcff67d28 | ||
|
|
61a429365b | ||
|
|
e55f38a962 | ||
|
|
81798583ae | ||
|
|
9cab61cebc | ||
|
|
f290732cb6 | ||
|
|
2dd44852fa | ||
|
|
bd2f17deec | ||
|
|
06faf46bcc | ||
|
|
468e9d539b | ||
|
|
449f407236 | ||
|
|
7efe90e709 | ||
|
|
184e1a5bc4 | ||
|
|
85e7b337a7 | ||
|
|
83759bde1c | ||
|
|
d9b1d45e0e | ||
|
|
df91c20f36 | ||
|
|
c1f6159d69 | ||
|
|
5a0461211a | ||
|
|
1394f790c3 | ||
|
|
4d374ff40c | ||
|
|
d4448ba086 | ||
|
|
ca078cbbfd | ||
|
|
6d45105b69 | ||
|
|
40f81af2dd | ||
|
|
d5e6184e75 | ||
|
|
0ec38ba41d | ||
|
|
460a64710a | ||
|
|
9ec62a748f | ||
|
|
a7a80cef99 |
@@ -25,8 +25,6 @@ cache:
|
||||
directories:
|
||||
- node_modules
|
||||
- app/node_modules
|
||||
- $HOME/.electron
|
||||
- $HOME/.cache
|
||||
|
||||
script:
|
||||
- npm run travis
|
||||
@@ -35,4 +33,4 @@ notifications:
|
||||
urls:
|
||||
- https://zulip.org/zulipbot/travis
|
||||
on_success: always
|
||||
on_failure: always
|
||||
on_failure: always
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
This is an experimental replacement for the [Zulip Desktop
|
||||
app](https://github.com/zulip/zulip-desktop) implemented in
|
||||
[Electron](http://electron.atom.io/).eeee
|
||||
[Electron](http://electron.atom.io/).
|
||||
|
||||
The goal is to achieve feature-compatibility with the old desktop app
|
||||
and then start adding cool features like easy support for
|
||||
|
||||
@@ -8,7 +8,7 @@ function appUpdater() {
|
||||
log.transports.file.level = 'info';
|
||||
autoUpdater.logger = log;
|
||||
/*
|
||||
autoUpdater.on('error', err => log.info(err));
|
||||
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'));
|
||||
@@ -17,7 +17,7 @@ function appUpdater() {
|
||||
// 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.';
|
||||
// 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';
|
||||
@@ -38,10 +38,10 @@ function appUpdater() {
|
||||
}
|
||||
});
|
||||
});
|
||||
// init for updates
|
||||
// Init for updates
|
||||
autoUpdater.checkForUpdates();
|
||||
}
|
||||
|
||||
exports = module.exports = {
|
||||
module.exports = {
|
||||
appUpdater
|
||||
};
|
||||
|
||||
@@ -12,7 +12,6 @@ const electronLocalshortcut = require('electron-localshortcut');
|
||||
const Configstore = require('electron-config');
|
||||
const JsonDB = require('node-json-db');
|
||||
const isDev = require('electron-is-dev');
|
||||
const tray = require('./tray');
|
||||
const appMenu = require('./menu');
|
||||
const {linkIsInternal, skipImages} = require('./link-helper');
|
||||
const {appUpdater} = require('./autoupdater');
|
||||
@@ -20,7 +19,7 @@ const {appUpdater} = require('./autoupdater');
|
||||
const db = new JsonDB(app.getPath('userData') + '/domain.json', true, true);
|
||||
const data = db.getData('/');
|
||||
|
||||
// adds debug features like hotkeys for triggering dev tools and reload
|
||||
// Adds debug features like hotkeys for triggering dev tools and reload
|
||||
require('electron-debug')();
|
||||
|
||||
const conf = new Configstore();
|
||||
@@ -41,10 +40,10 @@ function userOS() {
|
||||
}
|
||||
}
|
||||
|
||||
// setting userAgent so that server-side code can identify the desktop app
|
||||
// Setting userAgent so that server-side code can identify the desktop app
|
||||
const isUserAgent = 'ZulipElectron/' + app.getVersion() + ' ' + userOS();
|
||||
|
||||
// prevent window being garbage collected
|
||||
// Prevent window being garbage collected
|
||||
let mainWindow;
|
||||
let targetLink;
|
||||
|
||||
@@ -68,7 +67,13 @@ function serverError(targetURL) {
|
||||
}
|
||||
});
|
||||
req.on('error', e => {
|
||||
console.error(e);
|
||||
if (e.toString().indexOf('Error: self signed certificate') >= 0) {
|
||||
const url = targetURL.replace(/^https?:\/\//, '');
|
||||
console.log('Server StatusCode:', 200);
|
||||
console.log('You are connected to:', url);
|
||||
} else {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
req.end();
|
||||
} else if (data.domain) {
|
||||
@@ -93,18 +98,27 @@ function checkConnectivity() {
|
||||
}, index => {
|
||||
if (index === 0) {
|
||||
mainWindow.webContents.reload();
|
||||
mainWindow.webContents.send('destroytray');
|
||||
}
|
||||
if (index === 1) {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
}
|
||||
const connectivityERR = [
|
||||
'ERR_INTERNET_DISCONNECTED',
|
||||
'ERR_PROXY_CONNECTION_FAILED',
|
||||
'ERR_CONNECTION_RESET',
|
||||
'ERR_NOT_CONNECTED',
|
||||
'ERR_NAME_NOT_RESOLVED'
|
||||
];
|
||||
|
||||
function checkConnection() {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription, validatedURL) => {
|
||||
if (errorDescription === 'ERR_INTERNET_DISCONNECTED' || errorDescription === 'ERR_PROXY_CONNECTION_FAILED') {
|
||||
console.log('Error Description:' + errorDescription);
|
||||
const hasConnectivityErr = (connectivityERR.indexOf(errorDescription) >= 0);
|
||||
if (hasConnectivityErr) {
|
||||
console.error('error', errorDescription);
|
||||
checkConnectivity();
|
||||
}
|
||||
});
|
||||
@@ -158,6 +172,7 @@ function updateDockBadge(title) {
|
||||
if (process.platform === 'darwin') {
|
||||
app.setBadgeCount(messageCount);
|
||||
}
|
||||
mainWindow.webContents.send('tray', messageCount);
|
||||
}
|
||||
|
||||
function createMainWindow() {
|
||||
@@ -170,7 +185,7 @@ function createMainWindow() {
|
||||
minWidth: 600,
|
||||
minHeight: 400,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
preload: path.join(__dirname, '../renderer/js/preload.js'),
|
||||
plugins: true,
|
||||
allowDisplayingInsecureContent: true,
|
||||
nodeIntegration: false
|
||||
@@ -217,7 +232,7 @@ function createMainWindow() {
|
||||
});
|
||||
});
|
||||
|
||||
// on osx it's 'moved'
|
||||
// On osx it's 'moved'
|
||||
win.on('move', function () {
|
||||
const pos = this.getPosition();
|
||||
conf.set({
|
||||
@@ -226,20 +241,54 @@ function createMainWindow() {
|
||||
});
|
||||
});
|
||||
|
||||
// stop page to update it's title
|
||||
// Stop page to update it's title
|
||||
win.on('page-title-updated', (e, title) => {
|
||||
e.preventDefault();
|
||||
updateDockBadge(title);
|
||||
});
|
||||
|
||||
// To destroy tray icon when navigate to a new URL
|
||||
win.webContents.on('will-navigate', e => {
|
||||
if (e) {
|
||||
win.webContents.send('destroytray');
|
||||
}
|
||||
});
|
||||
|
||||
return win;
|
||||
}
|
||||
|
||||
// TODO - fix certificate errors
|
||||
app.commandLine.appendSwitch('ignore-certificate-errors', 'true');
|
||||
|
||||
// app.commandLine.appendSwitch('ignore-certificate-errors', 'true');
|
||||
|
||||
// For self-signed certificate
|
||||
ipc.on('certificate-err', (e, domain) => {
|
||||
const detail = `URL: ${domain} \n Error: Self-Signed Certificate`;
|
||||
dialog.showMessageBox(mainWindow, {
|
||||
title: 'Certificate error',
|
||||
message: `Do you trust certificate from ${domain}?`,
|
||||
// eslint-disable-next-line object-shorthand
|
||||
detail: detail,
|
||||
type: 'warning',
|
||||
buttons: ['Yes', 'No'],
|
||||
cancelId: 1
|
||||
// eslint-disable-next-line object-shorthand
|
||||
}, response => {
|
||||
if (response === 0) {
|
||||
// eslint-disable-next-line object-shorthand
|
||||
db.push('/domain', domain);
|
||||
mainWindow.loadURL(domain);
|
||||
}
|
||||
});
|
||||
});
|
||||
// eslint-disable-next-line max-params
|
||||
app.on('certificate-error', (event, webContents, url, error, certificate, callback) => {
|
||||
event.preventDefault();
|
||||
callback(true);
|
||||
});
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
// unregister all the shortcuts so that they don't interfare with other apps
|
||||
// Unregister all the shortcuts so that they don't interfare with other apps
|
||||
electronLocalshortcut.unregisterAll(mainWindow);
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
@@ -255,13 +304,14 @@ app.on('activate', () => {
|
||||
app.on('ready', () => {
|
||||
electron.Menu.setApplicationMenu(appMenu);
|
||||
mainWindow = createMainWindow();
|
||||
tray.create();
|
||||
// Not using for now // tray.create();
|
||||
|
||||
const page = mainWindow.webContents;
|
||||
|
||||
// TODO - use global shortcut instead
|
||||
electronLocalshortcut.register(mainWindow, 'CommandOrControl+R', () => {
|
||||
mainWindow.reload();
|
||||
mainWindow.webContents.send('destroytray');
|
||||
});
|
||||
|
||||
electronLocalshortcut.register(mainWindow, 'CommandOrControl+[', () => {
|
||||
@@ -277,7 +327,7 @@ app.on('ready', () => {
|
||||
});
|
||||
|
||||
page.on('dom-ready', () => {
|
||||
page.insertCSS(fs.readFileSync(path.join(__dirname, 'preload.css'), 'utf8'));
|
||||
page.insertCSS(fs.readFileSync(path.join(__dirname, '../renderer/css/preload.css'), 'utf8'));
|
||||
mainWindow.show();
|
||||
});
|
||||
|
||||
@@ -301,19 +351,22 @@ app.on('ready', () => {
|
||||
});
|
||||
|
||||
app.on('will-quit', () => {
|
||||
// unregister all the shortcuts so that they don't interfare with other apps
|
||||
// Unregister all the shortcuts so that they don't interfare with other apps
|
||||
electronLocalshortcut.unregisterAll(mainWindow);
|
||||
});
|
||||
|
||||
ipc.on('new-domain', (e, domain) => {
|
||||
// mainWindow.loadURL(domain);
|
||||
// MainWindow.loadURL(domain);
|
||||
if (!mainWindow) {
|
||||
mainWindow = createMainWindow();
|
||||
mainWindow.loadURL(domain);
|
||||
mainWindow.webContents.send('destroytray');
|
||||
} else if (mainWindow.isMinimized()) {
|
||||
mainWindow.webContents.send('destroytray');
|
||||
mainWindow.loadURL(domain);
|
||||
mainWindow.show();
|
||||
} else {
|
||||
mainWindow.webContents.send('destroytray');
|
||||
mainWindow.loadURL(domain);
|
||||
serverError(domain);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ function linkIsInternal(currentUrl, newUrl) {
|
||||
// We'll be needing this to open images in default browser
|
||||
const skipImages = '.jpg|.gif|.png|.jpeg|.JPG|.PNG';
|
||||
|
||||
exports = module.exports = {
|
||||
linkIsInternal, skipImages
|
||||
module.exports = {
|
||||
linkIsInternal,
|
||||
skipImages
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ const app = electron.app;
|
||||
const BrowserWindow = electron.BrowserWindow;
|
||||
const shell = electron.shell;
|
||||
const appName = app.getName();
|
||||
const tray = require('./tray');
|
||||
// Const tray = require('./tray');
|
||||
|
||||
const {addDomain, about} = require('./windowmanager');
|
||||
|
||||
@@ -36,6 +36,7 @@ const viewSubmenu = [
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
focusedWindow.reload();
|
||||
focusedWindow.webContents.send('destroytray');
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -79,7 +80,7 @@ const viewSubmenu = [
|
||||
label: 'Toggle Tray Icon',
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
tray.toggle();
|
||||
focusedWindow.webContents.send('toggletray');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
'use strict';
|
||||
const path = require('path');
|
||||
const electron = require('electron');
|
||||
const app = require('electron').app;
|
||||
const {addDomain, about} = require('./windowmanager');
|
||||
|
||||
let tray = null;
|
||||
|
||||
const APP_ICON = path.join(__dirname, '../resources/tray', 'tray');
|
||||
|
||||
const iconPath = () => {
|
||||
if (process.platform === 'linux') {
|
||||
return APP_ICON + 'linux.png';
|
||||
}
|
||||
return APP_ICON + (process.platform === 'win32' ? 'win.ico' : 'osx.png');
|
||||
};
|
||||
|
||||
const createHandler = () => {
|
||||
const contextMenu = electron.Menu.buildFromTemplate([
|
||||
{
|
||||
label: 'About',
|
||||
click() {
|
||||
about();
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Change Zulip server',
|
||||
click() {
|
||||
addDomain();
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Reload',
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
focusedWindow.reload();
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Quit',
|
||||
click() {
|
||||
app.quit();
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
tray = new electron.Tray(iconPath());
|
||||
tray.setToolTip(`${app.getName()}`);
|
||||
tray.setContextMenu(contextMenu);
|
||||
};
|
||||
|
||||
const destroyHandler = () => {
|
||||
tray.destroy();
|
||||
if (tray.isDestroyed()) {
|
||||
tray = null;
|
||||
} else {
|
||||
throw new Error('Tray icon not properly destroyed.');
|
||||
}
|
||||
};
|
||||
|
||||
const toggleHandler = () => {
|
||||
if (tray) {
|
||||
destroyHandler();
|
||||
} else {
|
||||
createHandler();
|
||||
}
|
||||
};
|
||||
|
||||
exports.create = createHandler;
|
||||
exports.destroy = destroyHandler;
|
||||
exports.toggle = toggleHandler;
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
const path = require('path');
|
||||
const electron = require('electron');
|
||||
const ipc = require('electron').ipcMain;
|
||||
|
||||
const APP_ICON = path.join(__dirname, '../resources', 'Icon');
|
||||
|
||||
@@ -11,7 +12,7 @@ let domainWindow;
|
||||
let aboutWindow;
|
||||
|
||||
function onClosed() {
|
||||
// dereference the window
|
||||
// Dereference the window
|
||||
domainWindow = null;
|
||||
aboutWindow = null;
|
||||
}
|
||||
@@ -64,7 +65,7 @@ function createAboutWindow() {
|
||||
aboutwin.loadURL(aboutURL);
|
||||
aboutwin.on('closed', onClosed);
|
||||
|
||||
// stop page to update it's title
|
||||
// Stop page to update it's title
|
||||
aboutwin.on('page-title-updated', e => {
|
||||
e.preventDefault();
|
||||
});
|
||||
@@ -82,4 +83,18 @@ function about() {
|
||||
});
|
||||
}
|
||||
|
||||
exports = module.exports = {addDomain, about};
|
||||
ipc.on('trayabout', event => {
|
||||
if (event) {
|
||||
about();
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('traychangeserver', event => {
|
||||
if (event) {
|
||||
addDomain();
|
||||
}
|
||||
});
|
||||
module.exports = {
|
||||
addDomain,
|
||||
about
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "zulip",
|
||||
"productName": "Zulip",
|
||||
"version": "0.5.9",
|
||||
"version": "0.5.10",
|
||||
"description": "Zulip Desktop App",
|
||||
"license": "Apache-2.0",
|
||||
"email": "<svnitakash@gmail.com>",
|
||||
|
||||
@@ -6,17 +6,19 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class="about">
|
||||
<center><img src="../resources/zulip.png"></center>
|
||||
<center><p class="detail" id="version"> Version : ?.?.? </p>
|
||||
<center><p class="detail"> License : Apache </p>
|
||||
<center><p class="detail"> Maintainer : Zulip </p>
|
||||
<p class="left"><a class="bug" onclick="linkInBrowser()" href="#">Found bug?</a></p>
|
||||
<img class="logo" src="../resources/zulip.png" />
|
||||
<p class="detail" id="version">version ?.?.?</p>
|
||||
<div class="maintenance-info">
|
||||
<p class="detail maintainer">Maintained by Zulip</p>
|
||||
<p class="detail license">Available under the Apache License</p>
|
||||
<a class="bug" onclick="linkInBrowser()" href="#">Found bug?</a>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
|
||||
const app = require('electron').remote.app;
|
||||
const version_tag = document.getElementById('version');
|
||||
version_tag.innerHTML = ' Version : ' + app.getVersion() + ' ';
|
||||
version_tag.innerHTML = 'version ' + app.getVersion();
|
||||
|
||||
function linkInBrowser(event) {
|
||||
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
body {
|
||||
background: #6BB6C7;
|
||||
background: #fafafa;
|
||||
font-family: menu, "Helvetica Neue", sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#version {
|
||||
color: #aaa;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.about {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.left {
|
||||
position: absolute;
|
||||
top:89%;
|
||||
left:76%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.about p {
|
||||
@@ -18,10 +25,49 @@ body {
|
||||
}
|
||||
|
||||
.about img {
|
||||
width:160px;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.detail {
|
||||
text-align: left;
|
||||
margin-left: 35%;
|
||||
}
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.detail.maintainer {
|
||||
font-size: 1.2em;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.detail.license {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.maintenance-info {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: 20px;
|
||||
left: 0px;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.maintenance-info p {
|
||||
margin: 0;
|
||||
font-size: 1em;
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.maintenance-info .bug {
|
||||
display: inline-block;
|
||||
padding: 8px 15px;
|
||||
margin-top: 30px;
|
||||
text-decoration: none;
|
||||
background-color: #52c2af;
|
||||
color: #fff;
|
||||
border-radius: 4px;
|
||||
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.maintenance-info .bug:hover {
|
||||
background-color: #32a692;
|
||||
}
|
||||
|
||||
@@ -1,375 +1,143 @@
|
||||
@charset "UTF-8";
|
||||
header,
|
||||
section {
|
||||
display: block
|
||||
* {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||
Helvetica, Arial, sans-serif, "Apple Color Emoji",
|
||||
"Segoe UI Emoji", "Segoe UI Symbol";
|
||||
-webkit-font-smoothing: antialiased;
|
||||
color: #444;
|
||||
|
||||
vertical-align: top;
|
||||
}
|
||||
html {
|
||||
font-size: 100%;
|
||||
overflow-y: scroll;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
html,
|
||||
button,
|
||||
input {
|
||||
font-family: "Helvetica Neue", Arial, sans-serif
|
||||
}
|
||||
html,
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0
|
||||
}
|
||||
img {
|
||||
border: 0;
|
||||
-ms-interpolation-mode: bicubic
|
||||
}
|
||||
img {
|
||||
vertical-align: middle
|
||||
}
|
||||
form {
|
||||
margin: 0
|
||||
}
|
||||
fieldset {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0
|
||||
}
|
||||
button,
|
||||
input {
|
||||
font-size: 100%;
|
||||
margin: 0;
|
||||
vertical-align: baseline;
|
||||
box-sizing: content-box;
|
||||
-webkit-box-sizing: content-box
|
||||
}
|
||||
button,
|
||||
input {
|
||||
line-height: normal
|
||||
background-image: url(../img/topography.png);
|
||||
}
|
||||
|
||||
input,
|
||||
button {
|
||||
cursor: pointer;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
button::-moz-focus-inner,
|
||||
input::-moz-focus-inner {
|
||||
border: 0;
|
||||
padding: 0
|
||||
}
|
||||
hr {
|
||||
display: none
|
||||
}
|
||||
img {
|
||||
max-width: 100%
|
||||
}
|
||||
h1 {
|
||||
color: #111;
|
||||
line-height: 1em;
|
||||
font-weight: 400;
|
||||
font-family: "Helvetica Neue", Arial, sans-serif;
|
||||
text-rendering: optimizelegibility;
|
||||
-webkit-text-stroke: none
|
||||
}
|
||||
h1 {
|
||||
margin: 0 0 35px
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
body.container-layout header #logo {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: top
|
||||
}
|
||||
body.container-layout header #logo {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background-position: -790px 0
|
||||
}
|
||||
button[type=submit] {
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
font-family: "Helvetica Neue", Arial, sans-serif;
|
||||
font-size-adjust: auto;
|
||||
vertical-align: bottom;
|
||||
background-color: #e6eaef;
|
||||
border: 2px solid #e6eaef;
|
||||
color: #96a0ac;
|
||||
font-size: 20px;
|
||||
line-height: 26px;
|
||||
padding: 6px 17px
|
||||
}
|
||||
.desktop button[type=submit] {
|
||||
-webkit-transition: all .2s ease-in-out;
|
||||
transition: all .2s ease-in-out
|
||||
}
|
||||
.desktop button[type=submit]:hover {
|
||||
background-color: #eff2f5;
|
||||
border: 2px solid #eff2f5;
|
||||
color: #96a0ac
|
||||
}
|
||||
button[type=submit]:focus {
|
||||
outline: 0
|
||||
}
|
||||
button[type=submit].btn-primary {
|
||||
background-color: #20b36c;
|
||||
border: 2px solid #20b36c;
|
||||
color: #fff
|
||||
}
|
||||
.desktop button[type=submit].btn-primary:hover {
|
||||
background-color: #39ca83;
|
||||
border: 2px solid #39ca83;
|
||||
color: #fff
|
||||
}
|
||||
button[type=submit].btn-primary:focus {
|
||||
outline: 0
|
||||
}
|
||||
button[type=submit].btn-large {
|
||||
font-family: "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 20px;
|
||||
line-height: 26px;
|
||||
padding: 12px 30px
|
||||
}
|
||||
input[type=text] {
|
||||
font-family: "Helvetica Neue", Arial, sans-serif;
|
||||
font-weight: 200;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
-webkit-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
border: 1px solid #cad0d7;
|
||||
color: #000;
|
||||
font-size: 18px;
|
||||
line-height: 26px;
|
||||
height: 42px;
|
||||
padding: 10px 10px
|
||||
}
|
||||
.desktop input[type=text]:hover {
|
||||
background-color: #fff;
|
||||
border-color: #bbc3cc;
|
||||
color: #111
|
||||
}
|
||||
input[type=text]:focus {
|
||||
background-color: #fff;
|
||||
border-color: #20b36c!important;
|
||||
color: #000;
|
||||
outline: 0
|
||||
}
|
||||
input[type=text]::-webkit-input-placeholder {
|
||||
color: #8e959e
|
||||
}
|
||||
input[type=text]::-moz-placeholder {
|
||||
color: #8e959e
|
||||
}
|
||||
input[type=text]:-ms-input-placeholder {
|
||||
color: #8e959e
|
||||
}
|
||||
.form-large input[type=text] {
|
||||
font-size: 18px;
|
||||
line-height: 26px;
|
||||
height: 54px;
|
||||
padding: 12px 15px
|
||||
}
|
||||
.control-group {
|
||||
margin-top: 40px
|
||||
}
|
||||
h1 {
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
word-break: break-word;
|
||||
-webkit-hyphens: auto;
|
||||
-moz-hyphens: auto;
|
||||
hyphens: auto
|
||||
}
|
||||
.section-main {
|
||||
position: relative
|
||||
}
|
||||
body {
|
||||
color: #111;
|
||||
font-size: 18px;
|
||||
line-height: 24px;
|
||||
font-family: "Helvetica Neue", Arial, sans-serif;
|
||||
background: #edf1f3;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box
|
||||
}
|
||||
.desktop body {
|
||||
padding-top: 116px
|
||||
}
|
||||
body.container-layout {
|
||||
padding: 0!important;
|
||||
background-color: #E6EAEF
|
||||
}
|
||||
header {
|
||||
z-index: 800;
|
||||
border-bottom: 1px solid #dae0e7;
|
||||
background: rgba(255, 255, 255, .97);
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%
|
||||
}
|
||||
header .container {
|
||||
position: relative
|
||||
}
|
||||
.desktop header {
|
||||
position: fixed
|
||||
}
|
||||
header .container {
|
||||
position: relative;
|
||||
height: 75px
|
||||
}
|
||||
.container-layout header {
|
||||
position: relative;
|
||||
border: 0;
|
||||
background: 0 0;
|
||||
padding: 30px 0
|
||||
}
|
||||
.container-layout header .container {
|
||||
height: auto
|
||||
}
|
||||
header #logo {
|
||||
display: block;
|
||||
text-indent: -9999px
|
||||
text-align: center;
|
||||
margin: 0px 0px 30px 0px;
|
||||
}
|
||||
|
||||
header #logo {
|
||||
position: absolute!important;
|
||||
top: 50%;
|
||||
left: 10px;
|
||||
-webkit-transform: translate(0, -50%);
|
||||
transform: translate(0, -50%)
|
||||
header img {
|
||||
width: 50px;
|
||||
}
|
||||
body.container-layout header #logo {
|
||||
position: relative!important;
|
||||
top: 0;
|
||||
left: 0;
|
||||
-webkit-transform: translate(0, 0);
|
||||
transform: translate(0, 0);
|
||||
margin: 0 auto;
|
||||
display: block
|
||||
|
||||
header h1 {
|
||||
display: inline-block;
|
||||
margin: 10px 0px 10px 10px;
|
||||
|
||||
font-weight: 500;
|
||||
}
|
||||
.content {
|
||||
padding-bottom: 40px;
|
||||
overflow: hidden
|
||||
|
||||
|
||||
section.server {
|
||||
display: inline-block;
|
||||
padding: 50px;
|
||||
margin-top: calc(50vh - 135px);
|
||||
|
||||
text-align: left;
|
||||
|
||||
box-shadow: 0px 0px 100px rgba(0,0,0,0.15);
|
||||
|
||||
background-color: #fff;
|
||||
|
||||
animation-name: pulse;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
.container-layout .content {
|
||||
max-width: 660px;
|
||||
margin: 0 auto
|
||||
|
||||
section.shake {
|
||||
animation-name: shake;
|
||||
animation-duration: 0.5s;
|
||||
animation-iteration-count: 1;
|
||||
}
|
||||
.content .server {
|
||||
position: relative;
|
||||
margin: 0 10px
|
||||
|
||||
form label {
|
||||
display: block;
|
||||
|
||||
font-weight: 600;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.content .server .container {
|
||||
background: #fff;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: 50px 0 0;
|
||||
-webkit-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
max-width: 580px
|
||||
|
||||
form input[type=text] {
|
||||
width: 300px;
|
||||
border: 2px solid #ccc;
|
||||
padding: 10px 10px;
|
||||
|
||||
outline: none;
|
||||
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.content .server h1 {
|
||||
|
||||
form input[type=text]:hover,
|
||||
form input[type=text]:focus {
|
||||
border-color: #aaa;
|
||||
}
|
||||
|
||||
form button {
|
||||
padding: 10px 10px;
|
||||
|
||||
background-color: #52c2af;
|
||||
color: #fff;
|
||||
|
||||
border: 2px solid #52c2af;
|
||||
outline: none;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
form button:hover {
|
||||
background-color: #30b09a;
|
||||
border-color: #30b09a;
|
||||
}
|
||||
|
||||
form button:active {
|
||||
background-color: #14957f;
|
||||
border-color: #14957f;
|
||||
}
|
||||
|
||||
form #error {
|
||||
margin: 5px 0px 0px;
|
||||
|
||||
font-size: 0.8em;
|
||||
font-weight: 600;
|
||||
|
||||
color: rgb(201, 107, 107);
|
||||
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
|
||||
height: 15px;
|
||||
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
form #error.show {
|
||||
opacity: 1;
|
||||
transform: translateY(0px);
|
||||
}
|
||||
|
||||
/* -- generic components -- */
|
||||
.center {
|
||||
text-align: center;
|
||||
padding: 0 10%
|
||||
}
|
||||
.content .server h1 {
|
||||
font-size: 2.4em;
|
||||
line-height: 1.2em;
|
||||
margin-bottom: 10px
|
||||
|
||||
@keyframes pulse {
|
||||
0% { box-shadow: 0px 0px 100px rgba(0,0,0,0.15); }
|
||||
50% { box-shadow: 0px 0px 100px rgba(0,0,0,0.30); }
|
||||
100% { box-shadow: 0px 0px 100px rgba(0,0,0,0.15); }
|
||||
}
|
||||
.content .server fieldset {
|
||||
padding: 25px 10% 80px 39px;
|
||||
position: relative
|
||||
}
|
||||
.content .server fieldset .control-group .control-field input {
|
||||
width: 100%
|
||||
}
|
||||
.content .server button {
|
||||
width: 100%
|
||||
}
|
||||
@media screen and (min-width: 749px) {
|
||||
input[type=text] {
|
||||
width: 60%
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1071px) {
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.3em
|
||||
}
|
||||
}
|
||||
.container {
|
||||
width: 1070px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.container:before,
|
||||
.container:after {
|
||||
content: "";
|
||||
display: table
|
||||
}
|
||||
.container:after {
|
||||
clear: both
|
||||
}
|
||||
.responsive .container {
|
||||
max-width: 1070px;
|
||||
width: auto;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.responsive .container:before,
|
||||
.responsive .container:after {
|
||||
content: "";
|
||||
display: table
|
||||
}
|
||||
.responsive .container:after {
|
||||
clear: both
|
||||
}
|
||||
@media screen and (max-width: 480px) {
|
||||
.responsive h1 {
|
||||
font-size: 1.7em;
|
||||
line-height: 1.3em
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 481px) and (max-width: 640px) {
|
||||
.responsive h1 {
|
||||
font-size: 1.9em;
|
||||
line-height: 1.3em
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 641px) and (max-width: 748px) {
|
||||
.responsive h1 {
|
||||
font-size: 2.2em;
|
||||
line-height: 1.3em
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 748px) {
|
||||
.responsive input[type=text] {
|
||||
width: 100%
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 749px) and (max-width: 1070px) {
|
||||
.responsive h1 {
|
||||
font-size: 2.6em;
|
||||
line-height: 1.3em
|
||||
}
|
||||
}
|
||||
#server-status {
|
||||
text-align: center;
|
||||
color: #c71212;
|
||||
|
||||
@keyframes shake {
|
||||
10%, 90% { transform: translate3d(-1px, 0, 0); }
|
||||
20%, 80% { transform: translate3d(2px, 0, 0); }
|
||||
30%, 50%, 70% { transform: translate3d(-4px, 0, 0); }
|
||||
40%, 60% { transform: translate3d(4px, 0, 0); }
|
||||
}
|
||||
|
||||
BIN
app/renderer/img/topography.png
Normal file
BIN
app/renderer/img/topography.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
@@ -7,36 +7,24 @@
|
||||
<title>Login - Zulip</title>
|
||||
<link rel="stylesheet" href="css/main.css" type="text/css" media="screen">
|
||||
</head>
|
||||
<body class="container-layout">
|
||||
<div class="section-main">
|
||||
<header>
|
||||
<div class="container">
|
||||
<img src="../resources/zulip.png" id="logo"/>
|
||||
<body>
|
||||
<div class="center">
|
||||
<section class="server">
|
||||
<header>
|
||||
<img src="../resources/zulip.png" id="logo"/>
|
||||
<h1>Zulip Login</h1>
|
||||
</header>
|
||||
<div class="container">
|
||||
<form id="frm-signInForm" class="form-large" onsubmit="addDomain(); return false">
|
||||
<label for="url">
|
||||
Zulip Server URL
|
||||
</label>
|
||||
<input type="text" id="url" autofocus="autofocus" spellcheck="false" placeholder="Server URL">
|
||||
<button type="submit" id="main" class="btn-primary btn-large" value="Submit">Connect</button>
|
||||
<p id="error"></p>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</header>
|
||||
<hr>
|
||||
<section class="content">
|
||||
<section class="server">
|
||||
<div class="container">
|
||||
<h1>Enter your Zulip Server URL</h1>
|
||||
<form id="frm-signInForm" class="form-large" onsubmit="addDomain(); return false">
|
||||
<fieldset>
|
||||
<div class="control-group control-required">
|
||||
<div class="control-field">
|
||||
<input type="text" id="url" autofocus="autofocus" spellcheck="false" placeholder="Server URL">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="control-submit">
|
||||
<button type="submit" id="main" class="btn-primary btn-large" value="Submit" onclick="addDomain();">Connect</button>
|
||||
</div>
|
||||
</div>
|
||||
<p id="server-status"><p>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -4,17 +4,38 @@ const JsonDB = require('node-json-db');
|
||||
const request = require('request');
|
||||
|
||||
const db = new JsonDB(app.getPath('userData') + '/domain.json', true, true);
|
||||
const data = db.getData('/');
|
||||
|
||||
console.log(data.domain);
|
||||
window.addDomain = function () {
|
||||
const el = sel => {
|
||||
return document.querySelector(sel);
|
||||
};
|
||||
|
||||
const $el = {
|
||||
error: el('#error'),
|
||||
main: el('#main'),
|
||||
section: el('section')
|
||||
};
|
||||
|
||||
const event = sel => {
|
||||
return {
|
||||
on: (event, callback) => {
|
||||
document.querySelector(sel).addEventListener(event, callback);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const displayError = msg => {
|
||||
$el.error.innerText = msg;
|
||||
$el.error.classList.add('show');
|
||||
$el.section.classList.add('shake');
|
||||
};
|
||||
|
||||
let newDomain = document.getElementById('url').value;
|
||||
newDomain = newDomain.replace(/^https?:\/\//, '');
|
||||
newDomain = newDomain.replace(/^http?:\/\//, '');
|
||||
if (newDomain === '') {
|
||||
document.getElementById('server-status').innerHTML = 'Please input a value';
|
||||
displayError('Please input a valid URL.');
|
||||
} else {
|
||||
document.getElementById('main').innerHTML = 'Checking...';
|
||||
el('#main').innerHTML = 'Checking...';
|
||||
if (newDomain.indexOf('localhost:') >= 0) {
|
||||
const domain = 'http://' + newDomain;
|
||||
const checkDomain = domain + '/static/audio/zulip.ogg';
|
||||
@@ -24,8 +45,8 @@ window.addDomain = function () {
|
||||
db.push('/domain', domain);
|
||||
ipcRenderer.send('new-domain', domain);
|
||||
} else {
|
||||
document.getElementById('main').innerHTML = 'Connect';
|
||||
document.getElementById('server-status').innerHTML = 'Not a valid Zulip Local Server.';
|
||||
$el.main.innerHTML = 'Connect';
|
||||
displayError('Not a valid Zulip local server');
|
||||
}
|
||||
});
|
||||
// });
|
||||
@@ -35,14 +56,25 @@ window.addDomain = function () {
|
||||
|
||||
request(checkDomain, (error, response) => {
|
||||
if (!error && response.statusCode !== 404) {
|
||||
document.getElementById('main').innerHTML = 'Connect';
|
||||
$el.main.innerHTML = 'Connect';
|
||||
db.push('/domain', domain);
|
||||
ipcRenderer.send('new-domain', domain);
|
||||
} else if (error.toString().indexOf('Error: self signed certificate') >= 0) {
|
||||
$el.main.innerHTML = 'Connect';
|
||||
ipcRenderer.send('certificate-err', domain);
|
||||
} else {
|
||||
document.getElementById('main').innerHTML = 'Connect';
|
||||
document.getElementById('server-status').innerHTML = 'Not a valid Zulip Server.';
|
||||
$el.main.innerHTML = 'Connect';
|
||||
displayError('Not a valid Zulip server');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
event('#url').on('input', () => {
|
||||
el('#error').classList.remove('show');
|
||||
});
|
||||
|
||||
event('section').on('animationend', function () {
|
||||
this.classList.remove('shake');
|
||||
});
|
||||
};
|
||||
@@ -20,9 +20,7 @@ window.prefDomain = function () {
|
||||
const ipcRenderer = require('electron').ipcRenderer;
|
||||
const JsonDB = require('node-json-db');
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
const {
|
||||
app
|
||||
} = require('electron').remote;
|
||||
const {app} = require('electron').remote;
|
||||
|
||||
const db = new JsonDB(app.getPath('userData') + '/domain.json', true, true);
|
||||
|
||||
@@ -57,6 +55,10 @@ window.prefDomain = function () {
|
||||
document.getElementById('urladded').innerHTML = 'Switched to ' + newDomain;
|
||||
db.push('/domain', domain);
|
||||
ipcRenderer.send('new-domain', domain);
|
||||
} else if (error.toString().indexOf('Error: self signed certificate') >= 0) {
|
||||
document.getElementById('main').innerHTML = 'Switch';
|
||||
ipcRenderer.send('certificate-err', domain);
|
||||
document.getElementById('urladded').innerHTML = 'Switched to ' + newDomain;
|
||||
} else {
|
||||
document.getElementById('main').innerHTML = 'Switch';
|
||||
document.getElementById('urladded').innerHTML = 'Not a valid Zulip Server.';
|
||||
|
||||
@@ -12,8 +12,11 @@ process.once('loaded', () => {
|
||||
|
||||
// eslint-disable-next-line import/no-unassigned-import
|
||||
require('./domain');
|
||||
// eslint-disable-next-line import/no-unassigned-import
|
||||
require('./tray.js');
|
||||
// Calling Tray.js in renderer process everytime app window loads
|
||||
|
||||
// handle zooming functionality
|
||||
// Handle zooming functionality
|
||||
const zoomIn = () => {
|
||||
webFrame.setZoomFactor(webFrame.getZoomFactor() + 0.1);
|
||||
};
|
||||
@@ -26,7 +29,7 @@ const zoomActualSize = () => {
|
||||
webFrame.setZoomFactor(1);
|
||||
};
|
||||
|
||||
// get zooming actions from main process
|
||||
// Get zooming actions from main process
|
||||
ipcRenderer.on('zoomIn', () => {
|
||||
zoomIn();
|
||||
});
|
||||
@@ -40,7 +43,7 @@ ipcRenderer.on('zoomActualSize', () => {
|
||||
});
|
||||
|
||||
ipcRenderer.on('log-out', () => {
|
||||
// create the menu for the below
|
||||
// Create the menu for the below
|
||||
document.querySelector('.dropdown-toggle').click();
|
||||
|
||||
const nodes = document.querySelectorAll('.dropdown-menu li:last-child a');
|
||||
@@ -48,19 +51,19 @@ ipcRenderer.on('log-out', () => {
|
||||
});
|
||||
|
||||
ipcRenderer.on('shortcut', () => {
|
||||
// create the menu for the below
|
||||
// Create the menu for the below
|
||||
const node = document.querySelector('a[data-overlay-trigger=keyboard-shortcuts]');
|
||||
// additional check
|
||||
// Additional check
|
||||
if (node.text.trim().toLowerCase() === 'keyboard shortcuts') {
|
||||
node.click();
|
||||
} else {
|
||||
// atleast click the dropdown
|
||||
// Atleast click the dropdown
|
||||
document.querySelector('.dropdown-toggle').click();
|
||||
}
|
||||
});
|
||||
|
||||
// To prevent failing this script on linux we need to load it after the document loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// init spellchecker
|
||||
// Init spellchecker
|
||||
spellChecker();
|
||||
});
|
||||
@@ -22,6 +22,6 @@ function spellChecker() {
|
||||
});
|
||||
}
|
||||
|
||||
exports = module.exports = {
|
||||
module.exports = {
|
||||
spellChecker
|
||||
};
|
||||
188
app/renderer/js/tray.js
Normal file
188
app/renderer/js/tray.js
Normal file
@@ -0,0 +1,188 @@
|
||||
'use strict';
|
||||
const path = require('path');
|
||||
|
||||
const electron = require('electron');
|
||||
|
||||
const {ipcRenderer, remote} = electron;
|
||||
|
||||
const {Tray, Menu, nativeImage} = remote;
|
||||
|
||||
const APP_ICON = path.join(__dirname, '../../resources/tray', 'tray');
|
||||
|
||||
const iconPath = () => {
|
||||
if (process.platform === 'linux') {
|
||||
return APP_ICON + 'linux.png';
|
||||
}
|
||||
return APP_ICON + (process.platform === 'win32' ? 'win.ico' : 'osx.png');
|
||||
};
|
||||
|
||||
let unread = 0;
|
||||
|
||||
const trayIconSize = () => {
|
||||
switch (process.platform) {
|
||||
case 'darwin':
|
||||
return 20;
|
||||
case 'win32':
|
||||
return 100;
|
||||
case 'linux':
|
||||
return 100;
|
||||
default: return 80;
|
||||
}
|
||||
};
|
||||
|
||||
// Default config for Icon we might make it OS specific if needed like the size
|
||||
const config = {
|
||||
pixelRatio: window.devicePixelRatio,
|
||||
unreadCount: 0,
|
||||
showUnreadCount: true,
|
||||
unreadColor: '#000000',
|
||||
readColor: '#000000',
|
||||
unreadBackgroundColor: '#B9FEEA',
|
||||
readBackgroundColor: '#B9FEEA',
|
||||
size: trayIconSize(),
|
||||
thick: process.platform === 'win32'
|
||||
};
|
||||
|
||||
const renderCanvas = function (arg) {
|
||||
config.unreadCount = arg;
|
||||
return new Promise((resolve, reject) => {
|
||||
const SIZE = config.size * config.pixelRatio;
|
||||
const PADDING = SIZE * 0.05;
|
||||
const CENTER = SIZE / 2;
|
||||
const HAS_COUNT = config.showUnreadCount && config.unreadCount;
|
||||
const color = config.unreadCount ? config.unreadColor : config.readColor;
|
||||
const backgroundColor = config.unreadCount ? config.unreadBackgroundColor : config.readBackgroundColor;
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = SIZE;
|
||||
canvas.height = SIZE;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Circle
|
||||
// If (!config.thick || config.thick && HAS_COUNT) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(CENTER, CENTER, (SIZE / 2) - PADDING, 0, 2 * Math.PI, false);
|
||||
ctx.fillStyle = backgroundColor;
|
||||
ctx.fill();
|
||||
ctx.lineWidth = SIZE / (config.thick ? 10 : 20);
|
||||
ctx.strokeStyle = backgroundColor;
|
||||
ctx.stroke();
|
||||
// Count or Icon
|
||||
if (HAS_COUNT) {
|
||||
ctx.fillStyle = color;
|
||||
ctx.textAlign = 'center';
|
||||
if (config.unreadCount > 99) {
|
||||
ctx.font = `${config.thick ? 'bold ' : ''}${SIZE * 0.4}px Helvetica`;
|
||||
ctx.fillText('99+', CENTER, CENTER + (SIZE * 0.15));
|
||||
} else if (config.unreadCount < 10) {
|
||||
ctx.font = `${config.thick ? 'bold ' : ''}${SIZE * 0.5}px Helvetica`;
|
||||
ctx.fillText(config.unreadCount, CENTER, CENTER + (SIZE * 0.20));
|
||||
} else {
|
||||
ctx.font = `${config.thick ? 'bold ' : ''}${SIZE * 0.5}px Helvetica`;
|
||||
ctx.fillText(config.unreadCount, CENTER, CENTER + (SIZE * 0.15));
|
||||
}
|
||||
|
||||
resolve(canvas);
|
||||
} else {
|
||||
reject(canvas);
|
||||
}
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Renders the tray icon as a native image
|
||||
* @param arg: Unread count
|
||||
* @return the native image
|
||||
*/
|
||||
const renderNativeImage = function (arg) {
|
||||
return Promise.resolve()
|
||||
.then(() => renderCanvas(arg))
|
||||
.then(canvas => {
|
||||
const pngData = nativeImage.createFromDataURL(canvas.toDataURL('image/png')).toPng();
|
||||
return Promise.resolve(nativeImage.createFromBuffer(pngData, config.pixelRatio));
|
||||
});
|
||||
};
|
||||
|
||||
const createTray = function () {
|
||||
window.tray = new Tray(iconPath());
|
||||
const contextMenu = Menu.buildFromTemplate([{
|
||||
label: 'About',
|
||||
click() {
|
||||
ipcRenderer.send('trayabout');
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Change Zulip server',
|
||||
click() {
|
||||
ipcRenderer.send('traychangeserver');
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Reload',
|
||||
click() {
|
||||
remote.getCurrentWindow().reload();
|
||||
window.tray.destroy();
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Quit',
|
||||
click() {
|
||||
remote.getCurrentWindow().close();
|
||||
}
|
||||
}
|
||||
]);
|
||||
window.tray.setContextMenu(contextMenu);
|
||||
};
|
||||
|
||||
ipcRenderer.on('destroytray', event => {
|
||||
window.tray.destroy();
|
||||
if (window.tray.isDestroyed()) {
|
||||
window.tray = null;
|
||||
} else {
|
||||
throw new Error('Tray icon not properly destroyed.');
|
||||
}
|
||||
|
||||
return event;
|
||||
});
|
||||
|
||||
ipcRenderer.on('tray', (event, arg) => {
|
||||
if (arg === 0) {
|
||||
unread = arg;
|
||||
// Message Count // console.log("message count is zero.");
|
||||
window.tray.setImage(iconPath());
|
||||
window.tray.setToolTip('No unread messages');
|
||||
} else {
|
||||
unread = arg;
|
||||
renderNativeImage(arg).then(image => {
|
||||
window.tray.setImage(image);
|
||||
window.tray.setToolTip(arg + ' unread messages');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
ipcRenderer.on('toggletray', event => {
|
||||
if (event) {
|
||||
if (window.tray) {
|
||||
window.tray.destroy();
|
||||
if (window.tray.isDestroyed()) {
|
||||
window.tray = null;
|
||||
}
|
||||
} else {
|
||||
createTray();
|
||||
renderNativeImage(unread).then(image => {
|
||||
window.tray.setImage(image);
|
||||
window.tray.setToolTip(unread + ' unread messages');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
createTray();
|
||||
20
package.json
20
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "zulip",
|
||||
"productName": "Zulip",
|
||||
"version": "0.5.9",
|
||||
"version": "0.5.10",
|
||||
"main": "./app/main",
|
||||
"description": "Zulip Desktop App",
|
||||
"license": "Apache-2.0",
|
||||
@@ -88,15 +88,15 @@
|
||||
"InstantMessaging"
|
||||
],
|
||||
"devDependencies": {
|
||||
"assert": "^1.4.1",
|
||||
"devtron": "^1.1.0",
|
||||
"assert": "1.4.1",
|
||||
"devtron": "1.4.0",
|
||||
"electron-builder": "16.6.0",
|
||||
"electron": "1.4.15",
|
||||
"electron-connect": "^0.4.6",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-mocha": "^3.0.1",
|
||||
"spectron": "^3.3.0",
|
||||
"xo": "*"
|
||||
"electron-connect": "0.4.8",
|
||||
"gulp": "3.9.1",
|
||||
"gulp-mocha": "3.0.1",
|
||||
"spectron": "3.6.1",
|
||||
"xo": "0.18.1"
|
||||
},
|
||||
"xo": {
|
||||
"esnext": true,
|
||||
@@ -109,6 +109,7 @@
|
||||
500
|
||||
],
|
||||
"no-warning-comments": 0,
|
||||
"capitalized-comments": 0,
|
||||
"no-else-return": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"import/no-extraneous-dependencies": 0
|
||||
@@ -116,8 +117,7 @@
|
||||
}
|
||||
],
|
||||
"ignore": [
|
||||
"tests/*.js",
|
||||
"app/main/macos-swipe-navigation.js"
|
||||
"tests/*.js"
|
||||
],
|
||||
"envs": [
|
||||
"node",
|
||||
|
||||
Reference in New Issue
Block a user