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..ee71c65 100644 --- a/index.html +++ b/index.html @@ -17,24 +17,9 @@
- - - - - + + diff --git a/main.js b/main.js index b5470ae..cc64f60 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,9 +27,7 @@ function createWindow () { const mainWindowOptions = { zoomToPageWidth: true, webPreferences: { - nodeIntegration: true, - contextIsolation: false, - enableRemoteModule: true + preload: path.join(__dirname, 'preload.js') }, icon: './resources/images/logo.png', } @@ -88,6 +87,14 @@ 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); + }); } // This method will be called when Electron has finished diff --git a/modules/menu-service.js b/modules/menu-service.js index 2a9fd23..4e18122 100644 --- a/modules/menu-service.js +++ b/modules/menu-service.js @@ -1,16 +1,18 @@ /** * 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 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 +25,10 @@ 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); } }, { @@ -40,12 +42,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 +78,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 +91,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..7b8f39f 100644 --- a/modules/tray-service.js +++ b/modules/tray-service.js @@ -14,13 +14,13 @@ const trayService = {} const getTrayServiceIcon = (iconName = 'icon') => { let iconImage = '' if (process.platform === 'darwin') { - iconImage = iconName+'Template' + iconImage = iconName } else if (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) => { diff --git a/package.json b/package.json index edfeb56..df26507 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ ], "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", @@ -67,7 +67,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'], }); }