Refactor converse-desktop and bump to Converse 9
This removes all AngularJS code Angular version 1 is unmaintained and largely obsolete since years. Additionally, I'd rather re-use the conventions and libraries from Converse instead of having different ones in converse-desktop. This means we're losing some functionality, such as the app settings and the about modal. This is unfortunate, but ideally (non-Electron) app settings should be implemented in Converse itself and not in this repo.
|
@ -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",
|
||||||
|
}
|
||||||
|
};
|
|
@ -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
|
|
@ -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)
|
|
||||||
}
|
|
||||||
})
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
})
|
|
|
@ -1,7 +0,0 @@
|
||||||
let angApp = require(__dirname+'/../init')
|
|
||||||
|
|
||||||
angApp.controller('FooterController', function($scope, AppInfo) {
|
|
||||||
|
|
||||||
$scope.appInfo = AppInfo
|
|
||||||
|
|
||||||
})
|
|
|
@ -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 = {}
|
|
||||||
}
|
|
||||||
})
|
|
|
@ -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)
|
|
||||||
|
|
||||||
})
|
|
|
@ -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))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
|
@ -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
|
||||||
|
}
|
14
app/init.js
|
@ -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
|
|
|
@ -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
|
|
||||||
}])
|
|
|
@ -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
|
|
||||||
})
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
||||||
})
|
|
|
@ -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
|
|
||||||
})
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
||||||
})
|
|
|
@ -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
|
|
||||||
}])
|
|
|
@ -1,35 +0,0 @@
|
||||||
<div class="page-about" ng-controller="AboutController">
|
|
||||||
<div class="about-card">
|
|
||||||
<div class="about-card__content">
|
|
||||||
<div class="about-card__logo">
|
|
||||||
<img src="./resources/images/logo.png" srcset="./resources/images/logo@2x.png 2x" alt=""
|
|
||||||
class="chimeverse-branding__img" />
|
|
||||||
</div>
|
|
||||||
<div class="about-card__description">
|
|
||||||
<h3 class="about__title">About {{appInfo.APP_NAME}} {{appInfo.APP_VERSION}}</h3>
|
|
||||||
<div class="about__description">Jabber/XMPP client based on Converse.js and Electron</div>
|
|
||||||
<div> </div>
|
|
||||||
<div class="about__converse-version">
|
|
||||||
Version of <a href="https://conversejs.org/" target="_blank">converse.js</a> is {{appInfo.CONVERSE_VERSION}}
|
|
||||||
</div>
|
|
||||||
<div class="about__electron-version">
|
|
||||||
Version of <a href="https://www.electronjs.org/" target="_blank">electron</a> is {{appInfo.ELECTRON_VERSION}}
|
|
||||||
</div>
|
|
||||||
<div> </div>
|
|
||||||
<div class="about__thanks">
|
|
||||||
Thanks to
|
|
||||||
<a href="https://github.com/nick-denry/Chimeverse/graphs/contributors" target="_blank">all contributors</a>
|
|
||||||
</div>
|
|
||||||
<div> </div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="about__action">
|
|
||||||
<button class="about__close-button" ng-click="closeAbout()">OK</button>
|
|
||||||
</div>
|
|
||||||
<div> </div>
|
|
||||||
<div class="about__copyright">Copyright © 2019-2020 Nick Denry</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ng-include src="'./app/views/shared/_footer.html'"></ng-include>
|
|
|
@ -1,24 +0,0 @@
|
||||||
<div class="page-default" ng-controller="DefaultController">
|
|
||||||
<div class="chimeverse-branding noselect">
|
|
||||||
<img src="./resources/images/logo.png" srcset="./resources/images/logo@2x.png 2x" alt=""
|
|
||||||
class="chimeverse-branding__img" />
|
|
||||||
<h3 class="chimeverse-branding__header">{{appInfo.APP_NAME}}</h3>
|
|
||||||
<div class="chimeverse-branding__version">
|
|
||||||
<span>{{appInfo.APP_VERSION}} </span>
|
|
||||||
<span class="chimeverse-branding__update">
|
|
||||||
<span class="update__checking" ng-show="checkingForUpdate == 'inProgress'">checking for update...</span>
|
|
||||||
<span class="update__error" ng-show="checkingForUpdate == 'checkErr'">
|
|
||||||
>_< check for update failed <a href="#" ng-click="checkRetry($event)">retry</a>
|
|
||||||
</span>
|
|
||||||
<span class="update__latest" ng-show="checkingForUpdate == 'latest'">latest version</span>
|
|
||||||
<a class="update__available" href="{{appInfo.APP_RELEASES_URL}}" target="_blank"
|
|
||||||
ng-show="checkingForUpdate == 'updateAvailable'">
|
|
||||||
Update available
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="conversejs-adoption">
|
|
||||||
<div id="conversejs"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,43 +0,0 @@
|
||||||
<div class="login-form">
|
|
||||||
<h3 class="login-form__title">Welcome to Converse Desktop</h3>
|
|
||||||
<div class="login-form__description">Jabber/XMPP client based on Converse.js and Electron</div>
|
|
||||||
<div class="login-form__card">
|
|
||||||
<form name="accountForm" ng-controller="LoginController">
|
|
||||||
<div class="login-form__wrapper">
|
|
||||||
<div class="login-form__input-group">
|
|
||||||
<span class="group-prepend oi" data-glyph="link-intact"></span>
|
|
||||||
<input ng-model="credentials.connectionManager" class="login-form__input login-form__input--with-append" type="text" placeholder="Connection manager url" required>
|
|
||||||
<span class="group-append--backgrounded oi" data-glyph="info" ng-click="showHelp('connectionManager')"></span>
|
|
||||||
</div>
|
|
||||||
<div class="login-form__input-help" ng-show="help.connectionManager == true"">
|
|
||||||
<div>
|
|
||||||
Should be a
|
|
||||||
<a href="https://m.conversejs.org/docs/html/setup.html#bosh-section" target="_blank">BOSH service</a>
|
|
||||||
or a
|
|
||||||
<a href="https://m.conversejs.org/docs/html/setup.html#websocket-section" target="_blank">Websocket</a>
|
|
||||||
URL
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="login-form__wrapper">
|
|
||||||
<div class="login-form__input-group">
|
|
||||||
<span class="group-prepend oi" data-glyph="person"></span>
|
|
||||||
<input ng-model="credentials.login" class="login-form__input" type="text" placeholder="jid@jabber.org" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="login-form__wrapper">
|
|
||||||
<div class="login-form__input-group">
|
|
||||||
<span class="group-prepend oi" data-glyph="lock-locked"></span>
|
|
||||||
<input ng-model="credentials.password" class="login-form__input" type="password" placeholder="Password" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="login-form__wrapper">
|
|
||||||
<button class="login-form__button" ng-click="addAccountAndLoginAction()" ng-disabled="accountForm.$invalid" disabled>Add account</button>
|
|
||||||
</div>
|
|
||||||
<div class="login-form__credentials-message">
|
|
||||||
Credentials stored via <a href="https://atom.github.io/node-keytar/" target="_blank">Keychain</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ng-include src="'./app/views/shared/_footer.html'"></ng-include>
|
|
|
@ -1,28 +0,0 @@
|
||||||
<div class="settings-page" ng-controller="SettingsController">
|
|
||||||
<form name="settignsForm">
|
|
||||||
<h3 class="settings-page__title">Chimeverse settings</h3>
|
|
||||||
<div class="form-item" ng-repeat="(key, item) in settings.converseDesktop">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="{{key}}" ng-model="item.value" /> {{item.title}}
|
|
||||||
</label>
|
|
||||||
<div class="form-item__hint">{{item.hint}}</div>
|
|
||||||
</div>
|
|
||||||
<h3 class="settings-page__title">Converse.js settings</h3>
|
|
||||||
<div class="form-item" ng-repeat="(key, item) in settings.conversejs">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="{{key}}" ng-model="item.value" /> {{item.title}}
|
|
||||||
</label>
|
|
||||||
<div class="form-item__hint">{{item.hint}}</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-item" ng-show="settingsSaved">
|
|
||||||
<a href="#" ng-click="restartApp()" class="form-item__restart-app">
|
|
||||||
Click here to restart the app and apply your changes
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="form-item__save-button" ng-class="{'active': settingsChanged == true}" ng-click="saveSettings()">Save</button>
|
|
||||||
<button class="form-item__cancel-button" ng-click="closeSettings()">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<ng-include src="'./app/views/shared/_footer.html'"></ng-include>
|
|
|
@ -1,4 +0,0 @@
|
||||||
<div class="page__footer" ng-controller="FooterController">
|
|
||||||
<span class="footer__version">{{appInfo.APP_NAME}} {{appInfo.APP_VERSION}}.</span>
|
|
||||||
<a class="github-button" href="{{appInfo.APP_HOME}}" data-icon="octicon-star" aria-label="Star nick-denry/Chimeverse on GitHub">Star</a>
|
|
||||||
</div>
|
|
37
index.html
|
@ -7,28 +7,17 @@
|
||||||
<link rel="stylesheet" type="text/css" media="screen" href="node_modules/converse.js/dist/converse.min.css">
|
<link rel="stylesheet" type="text/css" media="screen" href="node_modules/converse.js/dist/converse.min.css">
|
||||||
<link rel="stylesheet" type="text/css" href="resources/css/app.css">
|
<link rel="stylesheet" type="text/css" href="resources/css/app.css">
|
||||||
<link rel="stylesheet" type="text/css" href="resources/css/_footer.css">
|
<link rel="stylesheet" type="text/css" href="resources/css/_footer.css">
|
||||||
<link rel="stylesheet" type="text/css" href="resources/css/page-about.css">
|
|
||||||
<link rel="stylesheet" type="text/css" href="resources/css/page-default.css">
|
<link rel="stylesheet" type="text/css" href="resources/css/page-default.css">
|
||||||
<link rel="stylesheet" type="text/css" href="resources/css/page-login.css">
|
|
||||||
<link rel="stylesheet" type="text/css" href="resources/css/page-settings.css">
|
<link rel="stylesheet" type="text/css" href="resources/css/page-settings.css">
|
||||||
</head>
|
</head>
|
||||||
<base href="./">
|
<base href="./">
|
||||||
<body ng-app="app">
|
<body class="converse-fullscreen" ng-app="app">
|
||||||
<div class="main-background"></div>
|
<div class="main-window">
|
||||||
<div class="main-window" ng-controller="AppController" ng-cloak>
|
<div class="page-default">
|
||||||
<div ng-show="state == 'default'">
|
<div id="conversejs-bg"></div>
|
||||||
<ng-include src="'./app/views/default/page.html'"></ng-include>
|
|
||||||
</div>
|
|
||||||
<div ng-show="state == 'login'">
|
|
||||||
<ng-include src="'./app/views/login/page.html'"></ng-include>
|
|
||||||
</div>
|
|
||||||
<div ng-show="state == 'about'">
|
|
||||||
<ng-include src="'./app/views/about/page.html'"></ng-include>
|
|
||||||
</div>
|
|
||||||
<div ng-show="state == 'settings'">
|
|
||||||
<ng-include src="'./app/views/settings/page.html'"></ng-include>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
@see comment https://github.com/signalapp/libsignal-protocol-javascript/issues/6#issuecomment-247208665
|
@see comment https://github.com/signalapp/libsignal-protocol-javascript/issues/6#issuecomment-247208665
|
||||||
-->
|
-->
|
||||||
|
@ -37,27 +26,15 @@
|
||||||
delete window.require
|
delete window.require
|
||||||
</script>
|
</script>
|
||||||
<!-- Place libsignal at libs dir as it's no more distributed with converse.js -->
|
<!-- Place libsignal at libs dir as it's no more distributed with converse.js -->
|
||||||
<script src="libs/converse.js/3rdparty/libsignal-protocol.js"></script>
|
<script src="./3rdparty/libsignal-protocol.js"></script>
|
||||||
<script>
|
<script>
|
||||||
window.require = window.nodeRequire
|
window.require = window.nodeRequire
|
||||||
delete window.nodeRequire
|
delete window.nodeRequire
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
// You can also require other files to run in this process
|
// You can also require other files to run in this process
|
||||||
require('./node_modules/converse.js/dist/emojis.js')
|
|
||||||
require('./node_modules/converse.js/dist/converse.js')
|
require('./node_modules/converse.js/dist/converse.js')
|
||||||
require('./renderer.js')
|
require('./setup.js')
|
||||||
</script>
|
|
||||||
<script>
|
|
||||||
function loadJs(url) {
|
|
||||||
var script = document.createElement('script')
|
|
||||||
script.src = url
|
|
||||||
script.setAttribute('async', 'true')
|
|
||||||
document.documentElement.firstChild.appendChild(script)
|
|
||||||
}
|
|
||||||
setTimeout(function () {
|
|
||||||
loadJs("./node_modules/github-buttons/dist/buttons.min.js")
|
|
||||||
}, 500)
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -1,67 +0,0 @@
|
||||||
let desktopPlugin = {}
|
|
||||||
|
|
||||||
desktopPlugin.register = (login) => {
|
|
||||||
converse.plugins.add('converseDesktop', {
|
|
||||||
initialize: (event) => {
|
|
||||||
let _converse = event.properties._converse
|
|
||||||
let Strophe = converse.env.Strophe
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if message stanza has some body payload
|
|
||||||
* @param {*} stanzaNodes
|
|
||||||
*/
|
|
||||||
let isBodyMessage = (stanzaNodes) => {
|
|
||||||
let result = false
|
|
||||||
Object.keys(stanzaNodes).some((key) => {
|
|
||||||
if (stanzaNodes[key].nodeName == 'body') {
|
|
||||||
result = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
Promise.all([
|
|
||||||
_converse.api.waitUntil('rosterContactsFetched'),
|
|
||||||
_converse.api.waitUntil('chatBoxesFetched')
|
|
||||||
]).then(() => {
|
|
||||||
_converse.api.listen.on('logout', () => {
|
|
||||||
let event = new CustomEvent('conversejs-logout')
|
|
||||||
document.dispatchEvent(event)
|
|
||||||
})
|
|
||||||
_converse.api.listen.on('message', (data) => {
|
|
||||||
// Display notifications only for "payloaded" messages
|
|
||||||
if (isBodyMessage(data.stanza.childNodes)) {
|
|
||||||
let sender = data.stanza.attributes.from.nodeValue
|
|
||||||
let senderJid = Strophe.getBareJidFromJid(sender)
|
|
||||||
let loginJid = Strophe.getBareJidFromJid(login)
|
|
||||||
if (senderJid != loginJid) {
|
|
||||||
console.log(senderJid)
|
|
||||||
let event = new CustomEvent('conversejs-unread', {detail: senderJid})
|
|
||||||
document.dispatchEvent(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
_converse.api.listen.on('chatBoxFocused', () => {
|
|
||||||
let event = new CustomEvent('conversejs-no-unread')
|
|
||||||
document.dispatchEvent(event)
|
|
||||||
//chimeverseService._hideNotifyMessage()
|
|
||||||
})
|
|
||||||
window.document.addEventListener('converse-force-logout', function (e) {
|
|
||||||
console.log('Get converse-force-logout event')
|
|
||||||
console.log('Logout form plugin')
|
|
||||||
_converse.api.user.logout()
|
|
||||||
//chimeverseService.logout()
|
|
||||||
})
|
|
||||||
window.document.addEventListener('conversejs-open-chat', function (e) {
|
|
||||||
let chatToOpen = e.detail
|
|
||||||
console.log('Get open-unread-chat event: '+chatToOpen)
|
|
||||||
if (chatToOpen !== null) {
|
|
||||||
_converse.api.chats.open(chatToOpen)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = desktopPlugin
|
|
62
main.js
|
@ -8,7 +8,7 @@ let mainWindow
|
||||||
// Require other app modules
|
// Require other app modules
|
||||||
const trayService = require(__dirname+'/modules/tray-service')
|
const trayService = require(__dirname+'/modules/tray-service')
|
||||||
const menuService = require(__dirname+'/modules/menu-service')
|
const menuService = require(__dirname+'/modules/menu-service')
|
||||||
const settingsService = require(__dirname+'/modules/settings-service')
|
// const settingsService = require(__dirname+'/modules/settings-service')
|
||||||
|
|
||||||
const isMac = process.platform === 'darwin'
|
const isMac = process.platform === 'darwin'
|
||||||
const isWin = process.platform === 'win32'
|
const isWin = process.platform === 'win32'
|
||||||
|
@ -23,41 +23,19 @@ function initApp() {
|
||||||
|
|
||||||
function createWindow () {
|
function createWindow () {
|
||||||
// Main window options
|
// Main window options
|
||||||
let mainWindowOptions = {
|
const mainWindowOptions = {
|
||||||
width: 800,
|
zoomToPageWidth: true,
|
||||||
height: 600,
|
|
||||||
minWidth: 780,
|
|
||||||
minHeight: 560,
|
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
contextIsolation: false,
|
contextIsolation: false,
|
||||||
enableRemoteModule: true
|
enableRemoteModule: true
|
||||||
}
|
},
|
||||||
}
|
icon: './resources/images/logo.png',
|
||||||
|
|
||||||
// Load app settings
|
|
||||||
let runMinimized = settingsService.get('runMinimized')
|
|
||||||
if (runMinimized) {
|
|
||||||
mainWindowOptions.show = !runMinimized
|
|
||||||
}
|
|
||||||
let preserveWindowSize = settingsService.get('preserveWindowSize')
|
|
||||||
if (preserveWindowSize) {
|
|
||||||
let width = settingsService.get('windowWidth')
|
|
||||||
let height = settingsService.get('windowHeight')
|
|
||||||
if (width) mainWindowOptions.width = width
|
|
||||||
if (height) mainWindowOptions.height = height
|
|
||||||
}
|
|
||||||
|
|
||||||
let preserveWindowPosition = settingsService.get('preserveWindowPosition')
|
|
||||||
if (preserveWindowPosition) {
|
|
||||||
let windowX = settingsService.get('windowX')
|
|
||||||
let windowY = settingsService.get('windowY')
|
|
||||||
if (windowX) mainWindowOptions.x = windowX
|
|
||||||
if (windowY) mainWindowOptions.y = windowY
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the browser window.
|
// Create the browser window.
|
||||||
mainWindow = new BrowserWindow(mainWindowOptions)
|
mainWindow = new BrowserWindow(mainWindowOptions)
|
||||||
|
mainWindow.maximize();
|
||||||
|
|
||||||
// and load the index.html of the app.
|
// and load the index.html of the app.
|
||||||
mainWindow.loadFile('index.html')
|
mainWindow.loadFile('index.html')
|
||||||
|
@ -72,35 +50,15 @@ function createWindow () {
|
||||||
// mainWindow.webContents.openDevTools()
|
// mainWindow.webContents.openDevTools()
|
||||||
|
|
||||||
// Before close
|
// Before close
|
||||||
let minimizeOnClose = settingsService.get('minimizeOnClose')
|
// const minimizeOnClose = settingsService.get('minimizeOnClose');
|
||||||
|
const minimizeOnClose = false; // XXX: this doesn't seem to work
|
||||||
if (minimizeOnClose) {
|
if (minimizeOnClose) {
|
||||||
mainWindow.on('close', (e) => {
|
mainWindow.on('close', (e) => {
|
||||||
if (!app.isQuitting) {
|
if (!app.isQuitting) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
mainWindow.hide()
|
mainWindow.hide()
|
||||||
}
|
}
|
||||||
})
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
// Save window size
|
|
||||||
if (preserveWindowSize) {
|
|
||||||
mainWindow.on('resize', (e) => {
|
|
||||||
let newSize = mainWindow.getSize()
|
|
||||||
let width = newSize[0]
|
|
||||||
let height = newSize[1]
|
|
||||||
settingsService.set('windowWidth', width)
|
|
||||||
settingsService.set('windowHeight', height)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save window position
|
|
||||||
if (preserveWindowPosition !== 'undefined') {
|
|
||||||
mainWindow.on('move', (e) => {
|
|
||||||
let newPosition = mainWindow.getPosition()
|
|
||||||
let windowX = newPosition[0]
|
|
||||||
let windowY = newPosition[1]
|
|
||||||
settingsService.set('windowX', windowX)
|
|
||||||
settingsService.set('windowY', windowY)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +73,7 @@ function createWindow () {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle restart
|
// Handle restart
|
||||||
ipcMain.on('app-restart', (evt, arg) => {
|
ipcMain.on('app-restart', () => {
|
||||||
app.isQuitting = true
|
app.isQuitting = true
|
||||||
app.relaunch()
|
app.relaunch()
|
||||||
app.exit()
|
app.exit()
|
||||||
|
|
|
@ -1,61 +1,24 @@
|
||||||
/**
|
/**
|
||||||
* Module for Menu functions.
|
* Module for Menu functions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const {app, Menu, BrowserWindow} = require('electron')
|
const {app, Menu, BrowserWindow} = require('electron')
|
||||||
|
|
||||||
let menuService = {}
|
const menuService = {}
|
||||||
|
|
||||||
|
|
||||||
menuService.createMenu = () => {
|
menuService.createMenu = () => {
|
||||||
|
|
||||||
const isMac = process.platform === 'darwin'
|
|
||||||
|
|
||||||
const about = {
|
|
||||||
label: 'About Converse Desktop',
|
|
||||||
click: () => {
|
|
||||||
// @see https://github.com/electron/electron/issues/16558#issuecomment-484460276
|
|
||||||
// let activeWindow = BrowserWindow.getFocusedWindow()
|
|
||||||
let activeWindow = BrowserWindow.getAllWindows()[0]
|
|
||||||
activeWindow.show()
|
|
||||||
activeWindow.webContents.send('about-page-event')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const application = {
|
const application = {
|
||||||
label: 'Converse Desktop',
|
label: 'Converse Desktop',
|
||||||
submenu: [
|
submenu: [
|
||||||
... isMac ? [about] : [],
|
|
||||||
{
|
{
|
||||||
label: 'Reconnect',
|
label: 'Reconnect',
|
||||||
accelerator: 'CmdOrCtrl+R',
|
accelerator: 'CmdOrCtrl+R',
|
||||||
click: () => {
|
click: () => {
|
||||||
let activeWindow = BrowserWindow.getAllWindows()[0]
|
const activeWindow = BrowserWindow.getAllWindows()[0]
|
||||||
activeWindow.show()
|
activeWindow.show()
|
||||||
activeWindow.reload()
|
activeWindow.reload()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: 'Force logout',
|
|
||||||
accelerator: 'CmdOrCtrl+D',
|
|
||||||
click: () => {
|
|
||||||
let activeWindow = BrowserWindow.getAllWindows()[0]
|
|
||||||
activeWindow.show()
|
|
||||||
activeWindow.webContents.send('force-logout-event')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'separator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Preferences',
|
|
||||||
accelerator: 'CmdOrCtrl+,',
|
|
||||||
click: () => {
|
|
||||||
let activeWindow = BrowserWindow.getAllWindows()[0]
|
|
||||||
activeWindow.show()
|
|
||||||
activeWindow.webContents.send('preferences-event')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: 'separator',
|
type: 'separator',
|
||||||
},
|
},
|
||||||
|
@ -110,12 +73,11 @@ menuService.createMenu = () => {
|
||||||
const help = {
|
const help = {
|
||||||
label: 'Help',
|
label: 'Help',
|
||||||
submenu: [
|
submenu: [
|
||||||
... !isMac ? [about] : [],
|
|
||||||
{
|
{
|
||||||
label: 'Debug info',
|
label: 'Debug info',
|
||||||
accelerator: 'F12',
|
accelerator: 'F12',
|
||||||
click: () => {
|
click: () => {
|
||||||
let activeWindow = BrowserWindow.getAllWindows()[0]
|
const activeWindow = BrowserWindow.getAllWindows()[0]
|
||||||
activeWindow.webContents.openDevTools()
|
activeWindow.webContents.openDevTools()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ const electronSettings = require('electron-settings')
|
||||||
let settingsService = {}
|
let settingsService = {}
|
||||||
|
|
||||||
settingsService.get = (itemKey) => {
|
settingsService.get = (itemKey) => {
|
||||||
settingValue = electronSettings.getSync(itemKey)
|
const settingValue = electronSettings.getSync(itemKey)
|
||||||
if (typeof settingValue === 'undefined' || settingValue === null) {
|
if (typeof settingValue === 'undefined' || settingValue === null) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,17 +9,15 @@ const path = require('path')
|
||||||
let trayServiceWindow = null
|
let trayServiceWindow = null
|
||||||
let tray = null
|
let tray = null
|
||||||
|
|
||||||
let trayService = {}
|
const trayService = {}
|
||||||
|
|
||||||
let getTrayServiceIcon = (iconName = 'icon') => {
|
const getTrayServiceIcon = (iconName = 'icon') => {
|
||||||
let iconImage = ''
|
let iconImage = ''
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
iconImage = iconName+'Template'
|
iconImage = iconName+'Template'
|
||||||
}
|
} else if (process.platform === 'win32') {
|
||||||
else if (process.platform === 'win32') {
|
|
||||||
iconImage = iconName+'-16x16'
|
iconImage = iconName+'-16x16'
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
iconImage = iconName+'-48x48'
|
iconImage = iconName+'-48x48'
|
||||||
}
|
}
|
||||||
return path.join(__dirname, '/../resources/images/' + iconImage + '.png')
|
return path.join(__dirname, '/../resources/images/' + iconImage + '.png')
|
||||||
|
@ -27,12 +25,12 @@ let getTrayServiceIcon = (iconName = 'icon') => {
|
||||||
|
|
||||||
trayService.initTray = (window) => {
|
trayService.initTray = (window) => {
|
||||||
trayServiceWindow = window
|
trayServiceWindow = window
|
||||||
let iconPath = getTrayServiceIcon()
|
const iconPath = getTrayServiceIcon()
|
||||||
tray = new Tray(iconPath)
|
tray = new Tray(iconPath)
|
||||||
tray.setToolTip('Converse Desktop')
|
tray.setToolTip('Converse Desktop')
|
||||||
tray.on('click', function() {
|
tray.on('click', function() {
|
||||||
// Sent open-related-chat event only on click
|
// Sent open-related-chat event only on click
|
||||||
let activeWindow = BrowserWindow.getAllWindows()[0]
|
const activeWindow = BrowserWindow.getAllWindows()[0]
|
||||||
activeWindow.webContents.send('open-unread-chat')
|
activeWindow.webContents.send('open-unread-chat')
|
||||||
trayService.hideEnvelope()
|
trayService.hideEnvelope()
|
||||||
trayServiceWindow.show()
|
trayServiceWindow.show()
|
||||||
|
@ -40,12 +38,12 @@ trayService.initTray = (window) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
trayService.showEnvelope = () => {
|
trayService.showEnvelope = () => {
|
||||||
let iconPath = getTrayServiceIcon('envelope')
|
const iconPath = getTrayServiceIcon('envelope')
|
||||||
tray.setImage(iconPath)
|
tray.setImage(iconPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
trayService.hideEnvelope = () => {
|
trayService.hideEnvelope = () => {
|
||||||
let iconPath = getTrayServiceIcon()
|
const iconPath = getTrayServiceIcon()
|
||||||
tray.setImage(iconPath)
|
tray.setImage(iconPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
package.json
|
@ -20,16 +20,16 @@
|
||||||
"Electron",
|
"Electron",
|
||||||
"OMEMO"
|
"OMEMO"
|
||||||
],
|
],
|
||||||
"author": "Nick Denry <nick@denry.ru>",
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "11.2.3",
|
"electron": "11.2.3",
|
||||||
"electron-builder": "^22.9.1",
|
"electron-builder": "^22.9.1",
|
||||||
"electron-packager": "^15.2.0"
|
"electron-packager": "^15.2.0",
|
||||||
|
"electron-rebuild": "^3.2.5",
|
||||||
|
"eslint": "^8.4.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"angular": "^1.7.9",
|
"converse.js": "conversejs/converse.js#6ee3ddbc8c41608cf56d86566d6460bb423892b0",
|
||||||
"converse.js": "7.0.4",
|
|
||||||
"electron-settings": "^4.0.2",
|
"electron-settings": "^4.0.2",
|
||||||
"github-buttons": "^2.8.0",
|
"github-buttons": "^2.8.0",
|
||||||
"keytar": "^7.3.0",
|
"keytar": "^7.3.0",
|
||||||
|
@ -59,7 +59,7 @@
|
||||||
},
|
},
|
||||||
"linux": {
|
"linux": {
|
||||||
"artifactName": "converse_desktop-${version}_${arch}.${ext}",
|
"artifactName": "converse_desktop-${version}_${arch}.${ext}",
|
||||||
"icon": "resources/images/logo@2x.png",
|
"icon": "resources/images/logo.png",
|
||||||
"target": [
|
"target": [
|
||||||
"deb",
|
"deb",
|
||||||
"tar.gz"
|
"tar.gz"
|
||||||
|
|
64
renderer.js
|
@ -1,64 +0,0 @@
|
||||||
// This file is required by the index.html file and will
|
|
||||||
// be executed in the renderer process for that window.
|
|
||||||
// All of the Node.js APIs are available in this process.
|
|
||||||
|
|
||||||
var angApp = require('./app/init')
|
|
||||||
|
|
||||||
require('./app/services/credentials-service')
|
|
||||||
require('./app/services/settings-service')
|
|
||||||
require('./app/services/system-service')
|
|
||||||
require('./app/services/app-state-service')
|
|
||||||
require('./app/services/xmpp-helper-service')
|
|
||||||
require('./app/services/desktop-service')
|
|
||||||
require('./app/controllers/settings-controller')
|
|
||||||
require('./app/controllers/login-controller')
|
|
||||||
require('./app/controllers/default-controller')
|
|
||||||
require('./app/controllers/about-controller')
|
|
||||||
require('./app/controllers/footer-controller')
|
|
||||||
|
|
||||||
angApp.controller('AppController', function ($scope, $timeout, DesktopService, SettingsService, AppStateService) {
|
|
||||||
|
|
||||||
const { ipcRenderer } = require('electron')
|
|
||||||
|
|
||||||
// Menu force logout event
|
|
||||||
ipcRenderer.on('force-logout-event', () => {
|
|
||||||
DesktopService.logout()
|
|
||||||
let event = new CustomEvent("converse-force-logout") // Dispatch to the plugin
|
|
||||||
document.dispatchEvent(event)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Menu settings event
|
|
||||||
ipcRenderer.on('preferences-event', () => {
|
|
||||||
AppStateService.set(AppStateService.APP_STATE_SETTINGS)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Menu about event
|
|
||||||
ipcRenderer.on('about-page-event', () => {
|
|
||||||
AppStateService.set(AppStateService.APP_STATE_ABOUT)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Menu about event
|
|
||||||
ipcRenderer.on('open-unread-chat', () => {
|
|
||||||
let event = new CustomEvent('conversejs-open-chat', {detail: DesktopService.chatToOpen})
|
|
||||||
document.dispatchEvent(event)
|
|
||||||
})
|
|
||||||
|
|
||||||
AppStateService.set(AppStateService.APP_STATE_DEFAULT)
|
|
||||||
|
|
||||||
$scope.$on('app:state:changed', (event, data) => {
|
|
||||||
// @see https://docs.angularjs.org/error/$rootScope/inprog
|
|
||||||
$timeout(() => {
|
|
||||||
$scope.state = data
|
|
||||||
console.log('Switch to the "' + $scope.state +'" state')
|
|
||||||
}, 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
$scope.$on('app:restart', (event, data) => {
|
|
||||||
ipcRenderer.send('app-restart')
|
|
||||||
})
|
|
||||||
|
|
||||||
SettingsService.initDefaults()
|
|
||||||
|
|
||||||
DesktopService.getCredentialsAndLogin()
|
|
||||||
|
|
||||||
})
|
|
|
@ -20,16 +20,6 @@ a {
|
||||||
color: #777;
|
color: #777;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-background {
|
|
||||||
background: #f8f8f8;
|
|
||||||
height: 100%;
|
|
||||||
left: 0px;
|
|
||||||
position: fixed;
|
|
||||||
top: 0px;
|
|
||||||
width: 100%;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.conversejs-adoption {
|
.conversejs-adoption {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
.page-about {
|
|
||||||
color: #777;
|
|
||||||
left: 50%;
|
|
||||||
position: absolute;
|
|
||||||
top: 47%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-card {
|
|
||||||
background: #fff;
|
|
||||||
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.1);
|
|
||||||
margin-top: 30px;
|
|
||||||
min-width: 400px;
|
|
||||||
padding: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-card__content {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about__action {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about__close-button {
|
|
||||||
border: 0px solid #ddd;
|
|
||||||
background: rgb(5,93,228);
|
|
||||||
background: linear-gradient(0deg, rgba(5,93,228,1) 0%, rgba(76,145,255,1) 100%);
|
|
||||||
color: #fff;
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 18px;
|
|
||||||
outline: none;
|
|
||||||
padding: 10px 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about__copyright {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
|
@ -1,100 +0,0 @@
|
||||||
.login-form {
|
|
||||||
color: #777;
|
|
||||||
left: 50%;
|
|
||||||
position: absolute;
|
|
||||||
top: 47%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form__title {
|
|
||||||
font-size: 28px;
|
|
||||||
text-align: center;
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 0px 0px 15px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form__description {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form__card {
|
|
||||||
background: #fff;
|
|
||||||
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.1);
|
|
||||||
margin-top: 30px;
|
|
||||||
padding: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form__wrapper {
|
|
||||||
margin-bottom: 15px;;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form__input-group {
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 0px;
|
|
||||||
padding: 0px 0px 0px 30px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form__input-group:active,
|
|
||||||
.login-form__input-group:focus,
|
|
||||||
.login-form__input-group:hover {
|
|
||||||
border: 1px solid #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form__input-group .group-prepend {
|
|
||||||
position: absolute;
|
|
||||||
left: 15px;
|
|
||||||
top: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form__input-group .group-append--backgrounded {
|
|
||||||
background: #f2f2f2;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 12px;
|
|
||||||
position: absolute;
|
|
||||||
right: 0px;
|
|
||||||
top: 0 px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form__input {
|
|
||||||
border: 0px;
|
|
||||||
color: #222;
|
|
||||||
font-size: 18px;
|
|
||||||
outline: none;
|
|
||||||
padding: 10px;
|
|
||||||
width: 290px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form__input::placeholder { /* Most modern browsers support this now. */
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form__input--with-append {
|
|
||||||
width: 260px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form__input-help {
|
|
||||||
font-size: 13px;
|
|
||||||
margin: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form__button {
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
background: rgb(5,93,228);
|
|
||||||
background: linear-gradient(0deg, rgba(5,93,228,1) 0%, rgba(76,145,255,1) 100%);
|
|
||||||
color: #fff;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 18px;
|
|
||||||
outline: none;
|
|
||||||
padding: 10px 30px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form__button:disabled {
|
|
||||||
background: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form__credentials-message {
|
|
||||||
font-size: 14px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
Before Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 362 B |
Before Width: | Height: | Size: 719 B |
Before Width: | Height: | Size: 701 B After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 399 B |
Before Width: | Height: | Size: 895 B |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.1 KiB |
|
@ -0,0 +1,46 @@
|
||||||
|
require('./app/converse-plugins/desktop-credentials.js');
|
||||||
|
const{ getCredentials } = require('./app/credentials.js')
|
||||||
|
|
||||||
|
|
||||||
|
async function initialize () {
|
||||||
|
let websocket_url, bosh_service_url;
|
||||||
|
const { connectionManager, login, password } = await getCredentials()
|
||||||
|
|
||||||
|
converse.connectionManager = connectionManager;
|
||||||
|
|
||||||
|
if (connectionManager?.startsWith('ws')) {
|
||||||
|
websocket_url = connectionManager
|
||||||
|
} else if (connectionManager?.startsWith('http')) {
|
||||||
|
bosh_service_url = connectionManager
|
||||||
|
} else {
|
||||||
|
bosh_service_url = 'https://conversejs.org/http-bind/';
|
||||||
|
}
|
||||||
|
|
||||||
|
converse.plugins.add('converse-debug', {
|
||||||
|
initialize () {
|
||||||
|
const { _converse } = this;
|
||||||
|
window._converse = _converse;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
converse.initialize({
|
||||||
|
assets_path: './node_modules/converse.js/dist/',
|
||||||
|
auto_login: login && password,
|
||||||
|
bosh_service_url,
|
||||||
|
i18n: navigator.language,
|
||||||
|
jid: login,
|
||||||
|
loglevel: 'debug',
|
||||||
|
muc_respect_autojoin: true,
|
||||||
|
muc_show_logs_before_join: true,
|
||||||
|
password: password,
|
||||||
|
play_sounds: false,
|
||||||
|
priority: 50,
|
||||||
|
prune_messages_above: 250,
|
||||||
|
theme: 'concord',
|
||||||
|
view_mode: 'fullscreen',
|
||||||
|
websocket_url,
|
||||||
|
whitelisted_plugins: ['converse-debug', 'converse-desktop-credentials'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize();
|