diff --git a/.gitignore b/.gitignore
index 781dce0..3772294 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@ node_modules
.DS_*
package-lock.json
dist/
+.idea/
diff --git a/app/converse-plugins/desktop-credentials.js b/app/converse-plugins/desktop-credentials.js
index 08b0a24..b3ac722 100644
--- a/app/converse-plugins/desktop-credentials.js
+++ b/app/converse-plugins/desktop-credentials.js
@@ -1,5 +1,4 @@
-const { addCredentials } = require('../credentials.js');
-const { getCredentials, removeCredentials } = require('../credentials.js');
+const credentials = await import('../credentials.js');
converse.plugins.add('converse-desktop-credentials', {
@@ -9,7 +8,7 @@ converse.plugins.add('converse-desktop-credentials', {
api.listen.on('afterResourceBinding', () => {
if (_converse.connection.pass) {
- addCredentials(
+ credentials.addCredentials(
converse.connectionManager,
_converse.bare_jid,
_converse.connection.pass
@@ -18,7 +17,7 @@ converse.plugins.add('converse-desktop-credentials', {
});
api.listen.on('logout', () => {
- getCredentials().then((result) => removeCredentials(result.login))
+ credentials.getCredentials().then((result) => credentials.removeCredentials(result.login))
});
}
});
diff --git a/app/converse-plugins/desktop-trayicon.js b/app/converse-plugins/desktop-trayicon.js
new file mode 100644
index 0000000..c63c627
--- /dev/null
+++ b/app/converse-plugins/desktop-trayicon.js
@@ -0,0 +1,24 @@
+/* global api */
+
+converse.plugins.add('converse-desktop-trayicon', {
+
+ initialize() {
+ const {_converse} = this;
+ let envelopeIsShowing = false;
+
+ function hideEnvelope() {
+ if (envelopeIsShowing) {
+ api.trayService.hideEnvelope();
+ envelopeIsShowing = false;
+ }
+ }
+
+ window.addEventListener('focus', hideEnvelope);
+ _converse.api.listen.on('chatBoxInitialized', hideEnvelope);
+ _converse.api.listen.on('chatBoxFocused', hideEnvelope);
+ _converse.api.listen.on('messageNotification', () => {
+ api.trayService.showEnvelope();
+ envelopeIsShowing = true;
+ });
+ }
+});
diff --git a/app/credentials.js b/app/credentials.js
index ce0e9dd..fb2f17b 100644
--- a/app/credentials.js
+++ b/app/credentials.js
@@ -1,48 +1,32 @@
-/* global require, module */
+/* global api */
-const settings = require('electron-settings');
-const keytar = require('keytar')
-
-function addCredentials (connectionManager, login, password) {
+async function addCredentials(connectionManager, login, password) {
const xmppService = login.split('@').pop()
- settings.setSync('connectionManager', connectionManager)
- settings.setSync('login', login)
- keytar.setPassword(xmppService, login, password)
+ await api.settings.set('connectionManager', connectionManager)
+ await api.settings.set('login', login)
+ await api.keytar.setPassword(xmppService, login, password)
}
-function getCredentials () {
+async function getCredentials() {
const credentials = {}
- credentials.login = settings.getSync('login')
- return new Promise((resolve) => {
- if (credentials.login) {
- credentials.connectionManager = settings.getSync('connectionManager')
- credentials.xmppService = credentials.login.split('@').pop()
- let password = keytar.getPassword(credentials.xmppService, credentials.login)
- password.then((result) => {
- credentials.password = result
- resolve(credentials)
- })
- } else {
- resolve({});
- }
- });
+ credentials.login = await api.settings.get('login')
+ if (credentials.login) {
+ credentials.connectionManager = await api.settings.get('connectionManager') || null
+ credentials.xmppService = credentials.login.split('@').pop()
+ credentials.password = await api.keytar.getPassword(credentials.xmppService, credentials.login)
+ }
+
+ return credentials;
}
-function removeCredentials (login) {
- const xmppService = login.split('@').pop()
- const passwordDelete = keytar.deletePassword(xmppService, login)
- return new Promise((resolve, reject) => {
- passwordDelete.then(() => {
- settings.unsetSync('login')
- settings.unsetSync('connectionManager')
- resolve()
- }, (error) => {
- reject(error)
- })
- })
+async function removeCredentials(login) {
+ const xmppService = login.split('@').pop();
+ await api.keytar.deletePassword(xmppService, login);
+ await api.settings.unset('login');
+ await api.settings.unset('connectionManager');
}
-module.exports = {
+export {
addCredentials,
getCredentials,
removeCredentials
diff --git a/index.html b/index.html
index 44f8030..5e880c2 100644
--- a/index.html
+++ b/index.html
@@ -11,30 +11,15 @@
-
+
-
-
-
-
-
+
+
diff --git a/main.js b/main.js
index b5470ae..9db4339 100644
--- a/main.js
+++ b/main.js
@@ -1,5 +1,6 @@
// Modules to control application life and create native browser window
const { app, BrowserWindow, ipcMain, shell } = require('electron')
+const path = require('path');
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
@@ -26,20 +27,15 @@ function createWindow () {
const mainWindowOptions = {
zoomToPageWidth: true,
webPreferences: {
- nodeIntegration: true,
- contextIsolation: false,
- enableRemoteModule: true
+ preload: path.join(__dirname, 'preload.js')
},
icon: './resources/images/logo.png',
}
// Create the browser window.
- mainWindow = new BrowserWindow(mainWindowOptions)
+ app.mainWindow = mainWindow = new BrowserWindow(mainWindowOptions)
mainWindow.maximize();
- // and load the index.html of the app.
- mainWindow.loadFile('index.html')
-
// Init tray
trayService.initTray(mainWindow)
@@ -88,6 +84,17 @@ function createWindow () {
e.preventDefault()
shell.openExternal(url)
})
+
+ ipcMain.handle('settings', (e, method, ...args) => {
+ return settingsService[method].apply(settingsService, args);
+ });
+
+ ipcMain.handle('trayService', (e, method, ...args) => {
+ return trayService[method].apply(trayService, args);
+ });
+
+ // and load the index.html of the app.
+ mainWindow.loadFile('index.html')
}
// This method will be called when Electron has finished
diff --git a/modules/menu-service.js b/modules/menu-service.js
index 2a9fd23..ae51dd5 100644
--- a/modules/menu-service.js
+++ b/modules/menu-service.js
@@ -1,16 +1,19 @@
/**
* Module for Menu functions.
*/
-const {app, Menu, BrowserWindow} = require('electron')
+const {app, Menu, MenuItem, BrowserWindow} = require('electron')
const settingsService = require(__dirname + '/../modules/settings-service')
+const prompt = require('electron-prompt');
const menuService = {}
menuService.createMenu = () => {
- const application = {
- label: 'Converse Desktop',
- submenu: [
+ let converse;
+ const application = new Menu();
+ application.append(new MenuItem({
+ label: 'Converse Desktop'
+ , submenu: converse = Menu.buildFromTemplate([
{
label: 'Reconnect',
accelerator: 'CmdOrCtrl+R',
@@ -23,10 +26,30 @@ menuService.createMenu = () => {
{
label: 'Minimize on close',
type: 'checkbox',
+ id: 'minimize-on-close',
checked: settingsService.get('minimizeOnClose'),
click: () => {
- this.checked = !this.checked;
- settingsService.set('minimizeOnClose', this.checked);
+ settingsService.set('minimizeOnClose', converse.getMenuItemById('minimize-on-close').checked);
+ }
+ },
+ {
+ label: 'Connection Manager...',
+ click: () => {
+ let currentValue = settingsService.get('connectionManager') || '';
+ prompt({
+ title: 'Connection manager'
+ , label: 'Connection manager URL:'
+ , value: currentValue
+ , resizable: true
+ , width: 620
+ , height: 180
+ }, app.mainWindow).then(function (newValue) {
+ if (newValue !== null && newValue !== currentValue) {
+ settingsService.set('connectionManager', newValue === '' ? null : newValue);
+ app.mainWindow.reload()
+ }
+ }).catch(function (ex) {
+ });
}
},
{
@@ -40,12 +63,11 @@ menuService.createMenu = () => {
app.quit()
},
},
- ],
- }
-
- const edit = {
- label: 'Edit',
- submenu: [
+ ])
+ }));
+ application.append(new MenuItem({
+ label: 'Edit'
+ , submenu: Menu.buildFromTemplate([
{
label: 'Undo',
accelerator: 'CmdOrCtrl+Z',
@@ -77,12 +99,11 @@ menuService.createMenu = () => {
accelerator: 'CmdOrCtrl+A',
role: 'selectAll',
},
- ],
- }
-
- const help = {
- label: 'Help',
- submenu: [
+ ])
+ }));
+ application.append(new MenuItem({
+ label: 'Help'
+ , submenu: Menu.buildFromTemplate([
{
label: 'Debug info',
accelerator: 'F12',
@@ -91,12 +112,10 @@ menuService.createMenu = () => {
activeWindow.webContents.openDevTools()
}
}
- ]
- }
+ ])
+ }));
- const template = [application, edit, help]
-
- Menu.setApplicationMenu(Menu.buildFromTemplate(template))
+ Menu.setApplicationMenu(application);
}
module.exports = menuService
diff --git a/modules/settings-service.js b/modules/settings-service.js
index fd503b8..b2ed7f9 100644
--- a/modules/settings-service.js
+++ b/modules/settings-service.js
@@ -1,5 +1,5 @@
/**
- * Module for getting settigns in Main process.
+ * Module for getting settings in Main process.
*/
const electronSettings = require('electron-settings')
@@ -18,4 +18,8 @@ settingsService.set = (itemKey, settingValue) => {
electronSettings.setSync(itemKey, settingValue)
}
+settingsService.has = (itemKey) => electronSettings.hasSync(itemKey);
+
+settingsService.unset = (itemKey) => electronSettings.unsetSync(itemKey);
+
module.exports = settingsService
diff --git a/modules/tray-service.js b/modules/tray-service.js
index e6facae..ad65f86 100644
--- a/modules/tray-service.js
+++ b/modules/tray-service.js
@@ -2,38 +2,32 @@
* Module for Tray functions.
*/
-const { BrowserWindow, Tray } = require('electron')
+const { Tray } = require('electron')
const path = require('path')
-let trayServiceWindow = null
let tray = null
const trayService = {}
const getTrayServiceIcon = (iconName = 'icon') => {
- let iconImage = ''
- if (process.platform === 'darwin') {
- iconImage = iconName+'Template'
- } else if (process.platform === 'win32') {
+ let iconImage;
+ if (process.platform === 'darwin' || process.platform === 'win32') {
iconImage = iconName+'-16x16'
} else {
iconImage = iconName+'-48x48'
}
- return path.join(__dirname, '/../resources/images/' + iconImage + '.png')
+ return path.join(__dirname, '..','resources','images', iconImage + '.png')
}
trayService.initTray = (window) => {
- trayServiceWindow = window
const iconPath = getTrayServiceIcon()
tray = new Tray(iconPath)
tray.setToolTip('Converse Desktop')
tray.on('click', function() {
- // Sent open-related-chat event only on click
- const activeWindow = BrowserWindow.getAllWindows()[0]
- activeWindow.webContents.send('open-unread-chat')
+ window.webContents.send('open-unread-chat')
trayService.hideEnvelope()
- trayServiceWindow.show()
+ window.show()
})
}
diff --git a/package.json b/package.json
index edfeb56..00292c7 100644
--- a/package.json
+++ b/package.json
@@ -22,18 +22,19 @@
],
"license": "MPL-2.0",
"devDependencies": {
- "electron": "11.2.3",
+ "electron": "^16.0.0",
"electron-builder": "^22.9.1",
"electron-packager": "^15.2.0",
"electron-rebuild": "^3.2.5",
"eslint": "^8.4.1"
},
"dependencies": {
- "converse.js": "conversejs/converse.js#ba6da97416b912a35060a4c5667bc77f76852780",
+ "converse.js": "conversejs/converse.js#cb0b176",
"electron-settings": "^4.0.2",
"github-buttons": "^2.8.0",
"keytar": "^7.3.0",
- "open-iconic": "^1.1.1"
+ "open-iconic": "^1.1.1",
+ "electron-prompt": "^1.7.0"
},
"build": {
"appId": "com.denry.converse-desktop",
@@ -67,7 +68,7 @@
},
"win": {
"target": "nsis",
- "icon": "resources/images/logo.ico"
+ "icon": "resources/images/logo.png"
}
}
}
diff --git a/preload.js b/preload.js
new file mode 100644
index 0000000..db32f9c
--- /dev/null
+++ b/preload.js
@@ -0,0 +1,42 @@
+const {ipcRenderer, contextBridge} = require('electron');
+const keytar = require('keytar');
+
+contextBridge.exposeInMainWorld('api', {
+ reload() {
+ ipcRenderer.send('reload')
+ },
+
+ settings: {
+ has(setting) {
+ return ipcRenderer.invoke('settings', 'has', setting);
+ },
+ set(setting, value) {
+ ipcRenderer.invoke('settings', 'set', setting, value);
+ },
+ unset(setting) {
+ ipcRenderer.invoke('settings', 'unset', setting);
+ },
+ get(setting) {
+ return ipcRenderer.invoke('settings', 'get', setting);
+ }
+ },
+ trayService: {
+ showEnvelope() {
+ ipcRenderer.invoke('trayService', 'showEnvelope');
+ },
+ hideEnvelope() {
+ ipcRenderer.invoke('trayService', 'hideEnvelope');
+ }
+ },
+ keytar: {
+ getPassword(service, login) {
+ return keytar.getPassword(service, login);
+ },
+ setPassword(service, login, password) {
+ return keytar.setPassword(service, login, password);
+ },
+ deletePassword(service, login) {
+ return keytar.deletePassword(service, login);
+ }
+ }
+});
diff --git a/resources/images/envelope-16x16.png b/resources/images/envelope-16x16.png
new file mode 100644
index 0000000..19a8969
Binary files /dev/null and b/resources/images/envelope-16x16.png differ
diff --git a/resources/images/envelope-48x48.png b/resources/images/envelope-48x48.png
new file mode 100644
index 0000000..90a558d
Binary files /dev/null and b/resources/images/envelope-48x48.png differ
diff --git a/resources/images/envelope.png b/resources/images/envelope.png
new file mode 100644
index 0000000..2dbffb4
Binary files /dev/null and b/resources/images/envelope.png differ
diff --git a/resources/images/logo.png b/resources/images/logo.png
index f85f008..e88a110 100644
Binary files a/resources/images/logo.png and b/resources/images/logo.png differ
diff --git a/setup.js b/setup.js
index a677813..7337cde 100644
--- a/setup.js
+++ b/setup.js
@@ -1,5 +1,6 @@
-require('./app/converse-plugins/desktop-credentials.js');
-const{ getCredentials } = require('./app/credentials.js')
+await import('./app/converse-plugins/desktop-credentials.js')
+await import('./app/converse-plugins/desktop-trayicon.js')
+const getCredentials = (await import('./app/credentials.js')).getCredentials;
async function initialize () {
@@ -39,7 +40,7 @@ async function initialize () {
theme: 'concord',
view_mode: 'fullscreen',
websocket_url,
- whitelisted_plugins: ['converse-debug', 'converse-desktop-credentials'],
+ whitelisted_plugins: ['converse-debug', 'converse-desktop-credentials', 'converse-desktop-trayicon'],
});
}