diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..a746ad4 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,22 @@ +module.exports = { + "env": { + "browser": true, + "es2021": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": 13, + "sourceType": "module" + }, + "globals": { + "__dirname": true, + "converse": true, + "exports": true, + "module": true, + "process": true, + "require": true, + }, + "rules": { + "prefer-const": "error", + } +}; diff --git a/libs/converse.js/3rdparty/LICENSE b/3rdparty/LICENSE similarity index 100% rename from libs/converse.js/3rdparty/LICENSE rename to 3rdparty/LICENSE diff --git a/libs/converse.js/3rdparty/libsignal-protocol.js b/3rdparty/libsignal-protocol.js similarity index 100% rename from libs/converse.js/3rdparty/libsignal-protocol.js rename to 3rdparty/libsignal-protocol.js diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8533e98 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +BIN ?= ./node_modules/.bin/ +ESLINT ?= ./node_modules/.bin/eslint + +node_modules: package.json package-lock.json + npm i + +build: node_modules + $(BIN)/electron-rebuild + +serve: build + npm start + +.PHONY: eslint +eslint: node_modules + $(ESLINT) *.js + $(ESLINT) app/**/*.js diff --git a/app/controllers/about-controller.js b/app/controllers/about-controller.js deleted file mode 100644 index 72366bb..0000000 --- a/app/controllers/about-controller.js +++ /dev/null @@ -1,10 +0,0 @@ -let angApp = require(__dirname+'/../init') - -angApp.controller('AboutController', function($scope, AppStateService, AppInfo) { - - $scope.appInfo = AppInfo - - $scope.closeAbout = () => { - AppStateService.set(AppStateService.APP_STATE_DEFAULT) - } -}) \ No newline at end of file diff --git a/app/controllers/default-controller.js b/app/controllers/default-controller.js deleted file mode 100644 index f2ebe99..0000000 --- a/app/controllers/default-controller.js +++ /dev/null @@ -1,47 +0,0 @@ -let angApp = require(__dirname+'/../init') - -angApp.controller('DefaultController', function($scope, $timeout, $http, AppInfo) { - - $scope.appInfo = AppInfo - - let getUpdateInfo = () => { - $http({ - url: $scope.appInfo.APP_RELEASES_CHECK_URL, - method: 'GET' - }).then((response) => { - let releaseTag = response.data[0].tag_name - let releaseVersion = parseInt(releaseTag.replace(/v|\./g, '')) - let appVersion = parseInt($scope.appInfo.APP_VERSION.replace(/v|\./g, '')) - if (appVersion < releaseVersion ) { - $scope.checkingForUpdate = 'updateAvailable' - } - else { - $scope.checkingForUpdate = 'latest' - } - }).catch((error) => { - $scope.checkingForUpdate = 'checkErr' - }) - } - - let checkForUpdate = (timeout = 5000) => { - $scope.checkingForUpdate = 'inProgress' - $timeout(() => { - getUpdateInfo() - }, timeout) - } - - let checkForUpdateDelayed = (timeout = 5000) => { - $timeout(() => { - checkForUpdate() - }, timeout) - } - - checkForUpdateDelayed() - - $scope.checkRetry = ($event) => { - $event.preventDefault() - checkForUpdate() - } - - -}) \ No newline at end of file diff --git a/app/controllers/footer-controller.js b/app/controllers/footer-controller.js deleted file mode 100644 index 6747c82..0000000 --- a/app/controllers/footer-controller.js +++ /dev/null @@ -1,7 +0,0 @@ -let angApp = require(__dirname+'/../init') - -angApp.controller('FooterController', function($scope, AppInfo) { - - $scope.appInfo = AppInfo - -}) \ No newline at end of file diff --git a/app/controllers/login-controller.js b/app/controllers/login-controller.js deleted file mode 100644 index c364351..0000000 --- a/app/controllers/login-controller.js +++ /dev/null @@ -1,21 +0,0 @@ -let angApp = require(__dirname+'/../init') - -angApp.controller('LoginController', function($scope, DesktopService, CredentialsService) { - - $scope.help = {} - - $scope.showHelp = (item) => { - $scope.help[item] = typeof $scope.help[item] === 'undefined' ? true : !$scope.help[item]; - } - - $scope.addAccountAndLoginAction = () => { - CredentialsService.addCredentials($scope.credentials.connectionManager, - $scope.credentials.login, - $scope.credentials.password - ) - DesktopService.getCredentialsAndLogin() - $scope.accountForm.$setPristine() - $scope.accountForm.$setUntouched() - $scope.credentials = {} - } -}) \ No newline at end of file diff --git a/app/controllers/settings-controller.js b/app/controllers/settings-controller.js deleted file mode 100644 index be5154f..0000000 --- a/app/controllers/settings-controller.js +++ /dev/null @@ -1,40 +0,0 @@ -let angApp = require(__dirname+'/../init') - -angApp.controller('SettingsController', function ($scope, $rootScope, AppStateService, SettingsService) { - - let formInitialized = false - $scope.settingsChanged = false - $scope.settingsSaved = false - - const settingsSetPristine = () => { - $scope.settingsChanged = false - formInitialized = false - } - - $scope.closeSettings = () => { - $scope.settings = SettingsService.loadAll() - settingsSetPristine() - AppStateService.set(AppStateService.APP_STATE_DEFAULT) - } - - $scope.saveSettings = () => { - $scope.settingsSaved = true - SettingsService.saveAll($scope.settings) - settingsSetPristine() - } - - $scope.restartApp = () => { - $rootScope.$broadcast('app:restart') - } - - $scope.settings = SettingsService.loadAll() - - $scope.$watch("settings", () => { - if (!formInitialized) { - formInitialized = true - } else { - $scope.settingsChanged = true - } - }, true) - -}) \ No newline at end of file diff --git a/app/converse-plugins/desktop-credentials.js b/app/converse-plugins/desktop-credentials.js new file mode 100644 index 0000000..08b0a24 --- /dev/null +++ b/app/converse-plugins/desktop-credentials.js @@ -0,0 +1,24 @@ +const { addCredentials } = require('../credentials.js'); +const { getCredentials, removeCredentials } = require('../credentials.js'); + +converse.plugins.add('converse-desktop-credentials', { + + initialize () { + const { _converse } = this; + const { api } = _converse; + + api.listen.on('afterResourceBinding', () => { + if (_converse.connection.pass) { + addCredentials( + converse.connectionManager, + _converse.bare_jid, + _converse.connection.pass + ); + } + }); + + api.listen.on('logout', () => { + getCredentials().then((result) => removeCredentials(result.login)) + }); + } +}); diff --git a/app/credentials.js b/app/credentials.js new file mode 100644 index 0000000..ce0e9dd --- /dev/null +++ b/app/credentials.js @@ -0,0 +1,49 @@ +/* global require, module */ + +const settings = require('electron-settings'); +const keytar = require('keytar') + +function addCredentials (connectionManager, login, password) { + const xmppService = login.split('@').pop() + settings.setSync('connectionManager', connectionManager) + settings.setSync('login', login) + keytar.setPassword(xmppService, login, password) +} + +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({}); + } + }); +} + +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) + }) + }) +} + +module.exports = { + addCredentials, + getCredentials, + removeCredentials +} diff --git a/app/init.js b/app/init.js deleted file mode 100644 index 266e9d5..0000000 --- a/app/init.js +++ /dev/null @@ -1,14 +0,0 @@ -const angular = require('angular') -let angApp = angular.module('app', []) - -angApp.constant('AppInfo', { - APP_NAME: 'Converse Desktop', - APP_VERSION: 'v0.1.0', - APP_HOME: 'https://github.com/conversejs/converse-desktop', - APP_RELEASES_CHECK_URL: 'https://api.github.com/repos/conversejs/converse-desktop/releases', - APP_RELEASES_URL: 'https://github.com/conversejs/converse-desktop/releases', - CONVERSE_VERSION: 'v7.0.4', - ELECTRON_VERSION: '11.2.3' -}); - -module.exports = angApp diff --git a/app/services/app-state-service.js b/app/services/app-state-service.js deleted file mode 100644 index 9c4ba66..0000000 --- a/app/services/app-state-service.js +++ /dev/null @@ -1,22 +0,0 @@ -let angApp = require(__dirname + '/../init') - -angApp.factory('AppStateService', [ '$rootScope', ($rootScope) => { - - let stateService = {} - - stateService.APP_STATE_LOGIN = 'login' - stateService.APP_STATE_DEFAULT = 'default' - stateService.APP_STATE_SETTINGS = 'settings' - stateService.APP_STATE_ABOUT = 'about' - - stateService.set = (state) => { - stateService.previousState = typeof stateService.state !== 'undefined' ? - stateService.state : stateService.APP_STATE_DEFAULT - stateService.state = state - $rootScope.$broadcast('app:state:changed', stateService.state) - } - - stateService.set(stateService.APP_STATE_DEFAULT) - - return stateService -}]) \ No newline at end of file diff --git a/app/services/credentials-service.js b/app/services/credentials-service.js deleted file mode 100644 index cd6831f..0000000 --- a/app/services/credentials-service.js +++ /dev/null @@ -1,54 +0,0 @@ -let angApp = require(__dirname+'/../init') - -angApp.factory('CredentialsService', () => { - - const keytar = require('keytar') - const settings = require('electron-settings') - - let credentialsService = {} - - credentialsService.getCredentials = () => { - let credentials = {} - credentials.login = settings.getSync('login') - let promise = new Promise((resolve, reject) => { - 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 { - reject(Error('No login stored')) - } - }) - return promise - } - - credentialsService.addCredentials = (connectionManager, login, password) => { - let xmppService = login.split('@').pop() - settings.setSync('connectionManager', connectionManager) - settings.setSync('login', login) - keytar.setPassword(xmppService, login, password) - } - - credentialsService.removeCredentials = (login) => { - let xmppService = login.split('@').pop() - passwordDelete = keytar.deletePassword(xmppService, login) - let promise = new Promise((resolve, reject) => { - passwordDelete.then((result) => { - settings.unsetSync('login') - settings.unsetSync('connectionManager') - resolve() - }, (error) => { - reject(error) - }) - }) - return promise - } - - return credentialsService -}) - diff --git a/app/services/desktop-service.js b/app/services/desktop-service.js deleted file mode 100644 index ebaec6a..0000000 --- a/app/services/desktop-service.js +++ /dev/null @@ -1,96 +0,0 @@ -let angApp = require(__dirname + '/../init') - -const desktopPlugin = require(__dirname +'/../../libs/converse.js/converse-desktop/desktop-plugin') - -angApp.factory('DesktopService', ( - $window, $timeout, CredentialsService, SystemService, AppStateService, - SettingsService, XmppHelperService - ) => { - - let desktopService = {} - - desktopService._notifyMessage = () => { - SystemService.playAudio() - SystemService.showEnvelope() - } - - desktopService._hideNotifyMessage = () => { - SystemService.hideEnvelope() - } - - desktopService.logout = () => { - let credentials = CredentialsService.getCredentials() - credentials.then((result) => { - let remove = CredentialsService.removeCredentials(result.login) - console.log('Remove credentials on logout') - remove.then(() => { - AppStateService.set(AppStateService.APP_STATE_LOGIN) - }) - }) - } - - desktopService.initConverse = (connectionManager, login, password) => { - AppStateService.set(AppStateService.APP_STATE_DEFAULT) // Always set to default state before init - desktopPlugin.register(login) - let lang = navigator.language - let allowBookmarks = SettingsService.get('allowBookmarks') - let omemoDefault = SettingsService.get('omemoDefault') - let xmppResource = XmppHelperService.getResourceFromJid(login) - if (!xmppResource) { - xmppResource = '.' + (Math.random().toString(36)+'00000000000000000').slice(2, 7) // Generate 5 char unique str - login = login + '/converseDesktop'+xmppResource - } - let conversejsParams = { - assets_path: './node_modules/converse.js/dist/', - allow_bookmarks: allowBookmarks, - auto_login: true, - auto_reconnect: true, - // debug: true, - i18n: lang, - jid: login, - omemo_default: omemoDefault, - password: password, - play_sounds: false, - priority: 50, - view_mode: 'embedded', - whitelisted_plugins: ['converseDesktop'], - } - if (connectionManager.startsWith('ws')) { - conversejsParams.websocket_url = connectionManager - } else { - conversejsParams.bosh_service_url = connectionManager - } - $timeout(() => { - converse.initialize(conversejsParams) - }, 50) - } - - desktopService.getCredentialsAndLogin = () => { - let credentials = CredentialsService.getCredentials() - credentials.then((result) => { - desktopService.initConverse(result.connectionManager, result.login, result.password) - }, (error) => { - AppStateService.set(AppStateService.APP_STATE_LOGIN) - }) - } - - - desktopService.chatToOpen = null - - $window.document.addEventListener('conversejs-logout', function (e) { - desktopService.logout() - }) - - $window.document.addEventListener('conversejs-unread', function (e) { - let sender = e.detail - desktopService.chatToOpen = sender - desktopService._notifyMessage() - }) - - $window.document.addEventListener('conversejs-no-unread', function (e) { - desktopService._hideNotifyMessage() - }) - - return desktopService - -}) \ No newline at end of file diff --git a/app/services/settings-service.js b/app/services/settings-service.js deleted file mode 100644 index 0d19ac6..0000000 --- a/app/services/settings-service.js +++ /dev/null @@ -1,109 +0,0 @@ -let angApp = require(__dirname+'/../init') - -angApp.factory('SettingsService', () => { - - let settingsService = {} - - const electronSettings = require('electron-settings') - - const settings = { - converseDesktop: { - runMinimized: { - default: false, - title: 'Run minimized', - hint: 'Default: false. Whether run Converse Desktop minimized to tray or not.' - }, - minimizeOnClose: { - default: false, - title: 'Minimize on close', - hint: 'Default: false. Minimize or close Converse Desktop window.' - }, - preserveWindowSize: { - default: true, - title: 'Preserve window size', - hint: 'Default: true, 800x600 otherwise.' - }, - preserveWindowPosition: { - default: true, - title: 'Preserve window position', - hint: 'Default: true, screen center otherwise.' - } - }, - conversejs: { - allowBookmarks: { - default: false, - title: 'Allow server bookmarks', - hint: 'Default: false. Enables/disables chatroom bookmarks functionality.' - }, - omemoDefault: { - default: false, - title: 'Use OMEMO encryption by default', - hint: 'Default: false. Use OMEMO encryption by default when the chat supports it.' - } - } - } - - const iterateSettings = (callback, settingsObj) => { - if(typeof settingsObj === "undefined") { - settingsObj = settings - } - angular.forEach(settingsObj, (value, key) => { - let settingsList = settingsObj[key] - angular.forEach(settingsList, (value, key) => { - let itemDefault = settingsList[key]['default'] - callback(key, itemDefault, settingsList) - }) - }) - } - - // Callback - // TODO: replace with promise? - const saveDefault = (key, value) => { - if (!electronSettings.hasSync(key)) { - electronSettings.setSync(key, value) - } - } - - // Callback - const save = (key, defaultValue, settingsList) => { - let value = settingsList[key]['value'] - electronSettings.setSync(key, value) - } - - // Callback - const loadAll = (key, defaultValue, settingsList) => { - if (!electronSettings.hasSync(key)) { - settingsList[key]['value'] = defaultValue - } - settingsList[key]['value'] = electronSettings.getSync(key) - } - - /** - * SettingsService - */ - settingsService.initDefaults = () => { - iterateSettings(saveDefault) - // Logout for versions with BOSH only - if (electronSettings.hasSync('bosh')) { - electronSettings.unsetSync('bosh') - electronSettings.unsetSync('login') - } - } - - settingsService.get = (key) => { - return electronSettings.getSync(key) - } - - settingsService.loadAll = () => { - let currentSettings = angular.copy(settings) - iterateSettings(loadAll, currentSettings) - return currentSettings - } - - settingsService.saveAll = (currentSettings) => { - iterateSettings(save, currentSettings) - } - - return settingsService -}) - diff --git a/app/services/system-service.js b/app/services/system-service.js deleted file mode 100644 index 98f2af1..0000000 --- a/app/services/system-service.js +++ /dev/null @@ -1,28 +0,0 @@ -let angApp = require(__dirname + '/../init') - -angApp.factory('SystemService', () => { - - const remote = require('electron').remote - - let systemService = {} - - systemService.playAudio = () => { - var audio = new Audio(__dirname + '/../../resources/sounds/graceful.ogg') - audio.play() - } - - systemService.showEnvelope = () => { - remote.require('./main').trayService.showEnvelope() - } - - systemService.hideEnvelope = () => { - remote.require('./main').trayService.hideEnvelope() - } - - systemService.reloadWindow = () => { - remote.getCurrentWindow().reload() - } - - return systemService - -}) \ No newline at end of file diff --git a/app/services/xmpp-helper-service.js b/app/services/xmpp-helper-service.js deleted file mode 100644 index 74408ab..0000000 --- a/app/services/xmpp-helper-service.js +++ /dev/null @@ -1,25 +0,0 @@ -let angApp = require(__dirname + '/../init') - -angApp.factory('XmppHelperService', [() => { - - let xmppHelperService = {} - - /** - * Use function copy from Strophe js lib because converse.js Strophe library - * is under private _api and unavailable before converse.js is initialized. - * This function is used _before_ converse.js is initialized. - * - * Get the resource portion of a JID String. - * @param {string} jid A JID. - * @return {string | null} A String containing the resource. - */ - xmppHelperService.getResourceFromJid = (jid) => { - if (!jid) { return null } - const s = jid.split("/") - if (s.length < 2) { return null } - s.splice(0, 1) - return s.join('/') - } - - return xmppHelperService -}]) \ No newline at end of file diff --git a/app/views/about/page.html b/app/views/about/page.html deleted file mode 100644 index a959743..0000000 --- a/app/views/about/page.html +++ /dev/null @@ -1,35 +0,0 @@ -