diff --git a/.gitignore b/.gitignore index 44937afb8caf9c4d6bb4c548bb50c8136a157884..a187b437135fe0e056f310380a2d030b58e9aaa9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/* stamp-npm -dist \ No newline at end of file +dist +/.prettierrc \ No newline at end of file diff --git a/build-scss.js b/build-scss.js index afd914d7e638387ba27606b95ef620b062b8ec4a..9e52edc838fcd0629bf6ee6caf93e883cd3c49f0 100644 --- a/build-scss.js +++ b/build-scss.js @@ -6,12 +6,13 @@ sass.render({ file: 'src/styles/index.scss', outFile: 'dist/index.css', }, function(error, result) { - if(!error){ - if (!fs.existsSync('dist')){ + if (!error) { + if (!fs.existsSync('dist')) { fs.mkdirSync('dist', { recursive: true }); } - fs.writeFile('dist/index.css', String(result.css).replace(/\/lib\/solid-xmpp-chat/g, path), (e) =>{}); + fs.writeFile('dist/index.css', String(result.css).replace(/\/lib\/solid-xmpp-chat/g, path), (e) => { + }); } else { console.error(error); } -}); \ No newline at end of file +}); diff --git a/src/index.js b/src/index.js index 214aaca02ff69f56b18c3c03b49436e39c42bd60..52a1909885505177e57edac06693597c1d4201ad 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,3 @@ import { SolidXMPPChat } from './solid-xmpp-chat.js'; -export { SolidXMPPChat } +export { SolidXMPPChat }; diff --git a/src/path.js b/src/path.js index 92c1e6f8c5803067f51daae548e32d243fbd533a..3d432789081d7d4602b63b0270e40bb24058ae6e 100644 --- a/src/path.js +++ b/src/path.js @@ -1,3 +1,3 @@ -const path = "/lib/solid-xmpp-chat"; +const path = '/lib/solid-xmpp-chat'; module.exports = path; diff --git a/src/plugins/converse-rai.js b/src/plugins/converse-rai.js index f0db84ec7bf26e51b544bfcd4c0000b4192cb053..c11090646e0a5ae692b4128828ddab8832e8d841 100644 --- a/src/plugins/converse-rai.js +++ b/src/plugins/converse-rai.js @@ -1,216 +1,195 @@ -(function (root, factory) { - if (typeof define === 'function' && define.amd) { - define(["converse"], factory); - } else { - factory(converse); - } -}(this, function (converse) { - var Strophe, $iq, $msg, $pres, $build, b64_sha1, _ ,Backbone, dayjs, _converse; - var interestingServers = new Set(); - var subscribedServers = new Set(); - - converse.plugins.add("rai", { - 'dependencies': [], - - 'initialize': function () { - _converse = this._converse; - - Strophe = converse.env.Strophe; - $iq = converse.env.$iq; - $msg = converse.env.$msg; - $pres = converse.env.$pres; - $build = converse.env.$build; - b64_sha1 = converse.env.b64_sha1; - _ = converse.env._; - Backbone = converse.env.Backbone; - dayjs = converse.env.dayjs; - - _converse.api.settings.extend({ - rai_notification: true, - rai_notification_label: "Room Activity Indicator" - }); - - _converse.api.listen.on('connected', function () { - setupRoomActivityIndicators(); - }); - - _converse.api.listen.on('chatRoomViewInitialized', function (view) - { - const jid = view.model.get("jid"); - - if (view.model.get("num_unread") > 0 || view.model.get("num_unread_general") > 0) { - emitNotification(jid); - } - }); - - _converse.api.listen.on('raiRoomsUpdated', function (rooms) { - interestingServers = new Set(rooms.filter(room => room).map(Strophe.getDomainFromJid)); - if(_converse.api.connection.connected()) { - updateSubscriptions(); - } - }); - - _converse.api.listen.on('chatBoxScrolledDown', function (view) - { - const jid = view.chatbox.get('jid'); - const id_attr = 'stanza_id ' + jid; - const messages = view.chatbox.messages; - - if(!getUnreadStatus(jid)) { - return; - } - - for (let i = messages.length - 1; i >= 0; i--) { - const message = messages.at(i); - - if (message.has(id_attr)) { - let id = message.get(id_attr); - if(id != sessionStorage.getItem("rai_displayed." + jid)) { - sessionStorage.setItem("rai_displayed." + jid, id); - setUnreadStatus(jid, false); - setTimeout(() => sendMarker(jid, id, "displayed"), 0); - } - break; - } - } - }); - - _converse.api.listen.on('chatBoxInsertedIntoDOM', function (view) - { - const jid = view.model.get("jid"); - - if (view.model.get("num_unread") > 0) - { - emitNotification(jid); - } - - }); - - _converse.api.listen.on('message', function (data) - { - var chatbox = data.chatbox; - var history = data.attrs.is_archived; - var sender = data.attrs.sender; - var body = data.attrs.body; - - if (!history && body && chatbox && sender !== 'me') - { - const alert = chatbox.get("num_unread") > 0; - const notify = chatbox.get("num_unread_general") > 0; - - if ( alert || notify) - { - emitNotification(chatbox.get("jid"), alert); - } - } - }); +(function() { + let Strophe, $iq, $msg, $pres, $build, b64_sha1, _, Backbone, dayjs, _converse; + let interestingServers = new Set(); + let subscribedServers = new Set(); + + converse.plugins.add('converse-rai', { + 'dependencies': [], + + 'initialize': function() { + _converse = this._converse; + + Strophe = converse.env.Strophe; + $iq = converse.env.$iq; + $msg = converse.env.$msg; + $pres = converse.env.$pres; + $build = converse.env.$build; + b64_sha1 = converse.env.b64_sha1; + _ = converse.env._; + Backbone = converse.env.Backbone; + dayjs = converse.env.dayjs; + + _converse.api.settings.extend({ + rai_notification: true, + rai_notification_label: 'Room Activity Indicator', + }); + + _converse.api.listen.on('connected', function() { + setupRoomActivityIndicators(); + }); + + _converse.api.listen.on('chatRoomViewInitialized', function(view) { + const jid = view.model.get('jid'); + + if (view.model.get('num_unread') > 0 || view.model.get('num_unread_general') > 0) { + emitNotification(jid); } - }); - + }); - function subscribeServer(server_name) { - const id = Math.random().toString(36).substr(2, 9); - _converse.connection.send(converse.env.$pres({ - to: server_name, - id: id - }).c('rai', { - 'xmlns': "xmpp:prosody.im/protocol/rai" - })); - } + _converse.api.listen.on('raiRoomsUpdated', function(rooms) { + interestingServers = new Set(rooms.filter(room => room).map(Strophe.getDomainFromJid)); + if (_converse.api.connection.connected()) { + updateSubscriptions(); + } + }); - function unsubscribeServer(server_name) { - _converse.connection.send(converse.env.$pres({ - to: server_name, type: "unavailable" - }).c('rai', { - 'xmlns': "xmpp:prosody.im/protocol/rai" - })); - } + _converse.api.listen.on('chatBoxScrolledDown', function(view) { + const jid = view.chatbox.get('jid'); + const id_attr = 'stanza_id ' + jid; + const messages = view.chatbox.messages; - function updateSubscriptions() { - var new_servers = new Set([...interestingServers].filter(server => !subscribedServers.has(server))); - var obsolete_servers = new Set([...subscribedServers].filter(server => !interestingServers.has(server))); - for(let server of obsolete_servers) { - unsubscribeServer(server); + if (!getUnreadStatus(jid)) { + return; } - for(let server of new_servers) { - subscribeServer(server); - } - } - function setupRoomActivityIndicators() - { - updateSubscriptions(); - // If we already have unread notifications stored for this session, emit them now - for (var i = 0; i < sessionStorage.length; i++) - { - if (sessionStorage.key(i).indexOf("rai_notify.") == 0) - { - const jid = sessionStorage.key(i).substring(11); - emitNotification(jid); + for (let i = messages.length - 1; i >= 0; i--) { + const message = messages.at(i); + + if (message.has(id_attr)) { + let id = message.get(id_attr); + if (id != sessionStorage.getItem('rai_displayed.' + jid)) { + sessionStorage.setItem('rai_displayed.' + jid, id); + setUnreadStatus(jid, false); + setTimeout(() => sendMarker(jid, id, 'displayed'), 0); } + break; + } } + }); - // Listen for incoming RAI from the server - _converse.connection.addHandler(function (message) { - const from_jid = message.attributes.from?.nodevalue - const room_jid = from_jid?.split("/")[0] - const room = ''; - let ignore = false; - for (let i = 0; i < _converse.chatboxes.models.length; i++){ - if(_converse.chatboxes.models[i].id === room_jid){ - room = _converse.chatboxes.models[i].id; - break; - } - } - if (room && from_jid && room_jid){ - if (from_jid === room_jid+'/'+room.get('nick')){ - ignore = true; - } - } - if(message && !ignore) - message.querySelectorAll('activity').forEach(function (activity) - { - if (activity && activity.namespaceURI == "xmpp:prosody.im/protocol/rai") - { - const jid = activity.textContent; - setUnreadStatus(jid, true); - emitNotification(jid); - } - }); - return true; - }, null, 'message'); - } + _converse.api.listen.on('chatBoxInsertedIntoDOM', function(view) { + const jid = view.model.get('jid'); - function setUnreadStatus(jid, flag) - { - if(flag) { - sessionStorage.setItem("rai_notify." + jid, "true"); - } else { - sessionStorage.removeItem("rai_notify." + jid); + if (view.model.get('num_unread') > 0) { + emitNotification(jid); } - } - function getUnreadStatus(jid) { - return sessionStorage.getItem("rai_notify." + jid) == "true"; - } + }); + + _converse.api.listen.on('message', function(data) { + var chatbox = data.chatbox; + var history = data.attrs.is_archived; + var sender = data.attrs.sender; + var body = data.attrs.body; - function emitNotification(jid, alert) - { - _converse.api.trigger('chatRoomActivityIndicators', jid); + if (!history && body && chatbox && sender !== 'me') { + const alert = chatbox.get('num_unread') > 0; + const notify = chatbox.get('num_unread_general') > 0; + + if (alert || notify) { + emitNotification(chatbox.get('jid'), alert); + } + } + }); + }, + }); + + + function subscribeServer(server_name) { + const id = Math.random().toString(36).substr(2, 9); + _converse.connection.send(converse.env.$pres({ + to: server_name, + id: id, + }).c('rai', { + 'xmlns': 'xmpp:prosody.im/protocol/rai', + })); + } + + function unsubscribeServer(server_name) { + _converse.connection.send(converse.env.$pres({ + to: server_name, type: 'unavailable', + }).c('rai', { + 'xmlns': 'xmpp:prosody.im/protocol/rai', + })); + } + + function updateSubscriptions() { + var new_servers = new Set([...interestingServers].filter(server => !subscribedServers.has(server))); + var obsolete_servers = new Set([...subscribedServers].filter(server => !interestingServers.has(server))); + for (let server of obsolete_servers) { + unsubscribeServer(server); + } + for (let server of new_servers) { + subscribeServer(server); + } + } + + function setupRoomActivityIndicators() { + updateSubscriptions(); + // If we already have unread notifications stored for this session, emit them now + for (var i = 0; i < sessionStorage.length; i++) { + if (sessionStorage.key(i).indexOf('rai_notify.') == 0) { + const jid = sessionStorage.key(i).substring(11); + emitNotification(jid); + } } - function sendMarker(to_jid, id, type) - { - const stanza = converse.env.$msg({ - 'from': _converse.connection.jid, - 'id': Math.random().toString(36).substr(2,9), - 'to': to_jid, - 'type': 'groupchat' - }).c(type, { - 'xmlns': converse.env.Strophe.NS.MARKERS, - 'id': id + // Listen for incoming RAI from the server + _converse.connection.addHandler(function(message) { + const from_jid = message.attributes.from?.nodevalue; + const room_jid = from_jid?.split('/')[0]; + const room = ''; + let ignore = false; + for (let i = 0; i < _converse.chatboxes.models.length; i++) { + if (_converse.chatboxes.models[i].id === room_jid) { + room = _converse.chatboxes.models[i].id; + break; + } + } + if (room && from_jid && room_jid) { + if (from_jid === room_jid + '/' + room.get('nick')) { + ignore = true; + } + } + if (message && !ignore) + message.querySelectorAll('activity').forEach(function(activity) { + if (activity && activity.namespaceURI == 'xmpp:prosody.im/protocol/rai') { + const jid = activity.textContent; + setUnreadStatus(jid, true); + emitNotification(jid); + } }); + return true; + }, null, 'message'); + } - _converse.api.send(stanza); + function setUnreadStatus(jid, flag) { + if (flag) { + sessionStorage.setItem('rai_notify.' + jid, 'true'); + } else { + sessionStorage.removeItem('rai_notify.' + jid); } -})); + } + + function getUnreadStatus(jid) { + return sessionStorage.getItem('rai_notify.' + jid) == 'true'; + } + + function emitNotification(jid, alert) { + _converse.api.trigger('chatRoomActivityIndicators', jid); + } + + function sendMarker(to_jid, id, type) { + const stanza = converse.env.$msg({ + 'from': _converse.connection.jid, + 'id': Math.random().toString(36).substr(2, 9), + 'to': to_jid, + 'type': 'groupchat', + }).c(type, { + 'xmlns': converse.env.Strophe.NS.MARKERS, + 'id': id, + }); + + _converse.api.send(stanza); + } +})(); diff --git a/src/plugins/sib-chat-navigation.js b/src/plugins/sib-chat-navigation.js new file mode 100644 index 0000000000000000000000000000000000000000..230cfcf7160745b320ff1373cb18c50e7f5eb24d --- /dev/null +++ b/src/plugins/sib-chat-navigation.js @@ -0,0 +1,53 @@ +/** + * Register a new event to move the converse element + * into another component. + */ +converse.plugins.add('sib-chat-navigation', { + initialize() { + const _converse = this._converse; + const { api } = _converse; + + window.addEventListener('sib-change-chat', async ev => { + let { jid, is_groupchat, root } = ev.detail; + + if (!jid) { + return; + } + + jid = jid.toLowerCase(); + _converse.root = root; + + // Find the converse root + const converse_el = Array.from(document.querySelectorAll('solid-xmpp-chat')) + .map(el => el.shadowRoot.getElementById('conversejs')) + .filter(el => el) + .pop(); + + if (converse_el) { + root.appendChild(converse_el); + } + + if (is_groupchat) { + await api.rooms.open(jid, {}, true); + } else { + await api.chats.open(jid, {}, true); + } + }); + + // Get the currently used solid-xmpp-chat + const getCurrentChat = () => Array.from(document.querySelectorAll('solid-xmpp-chat')) + .filter(el => el.shadowRoot.getElementById('conversejs')) + .pop(); + + // When the a chat box is focused, send the read event + api.listen.on('chatBoxFocused', function() { + const resource = getCurrentChat()?.component.resource; + + window.dispatchEvent(new CustomEvent('read', { + detail: { + resource, + }, + })); + }); + }, +}); diff --git a/src/plugins/sib-custom-hats.js b/src/plugins/sib-custom-hats.js new file mode 100644 index 0000000000000000000000000000000000000000..f7b787137caef8f689f573c2d31b8a1e996d997b --- /dev/null +++ b/src/plugins/sib-custom-hats.js @@ -0,0 +1,23 @@ +/** + * Transform hats to custom values. + */ +converse.plugins.add('sib-custom-hats', { + overrides: { + getHats: function() { + const hat_conversions = { 'admin': 'Administrateur' }; + const hats = this.__super__.getHats.apply(this, arguments); + if (!hat_conversions) { + return hats; + } else { + const role_affiliations = Object.keys(hat_conversions); + return hats.map((hat) => { + if (role_affiliations.includes(hat.title)) { + return ({ title: hat_conversions[hat.title] }); + } else { + return hat; + } + }); + } + }, + }, +}); diff --git a/src/plugins/sib-disconnected.js b/src/plugins/sib-disconnected.js new file mode 100644 index 0000000000000000000000000000000000000000..3de3d30d9aaddb0b34a82c324675cc70192fb2eb --- /dev/null +++ b/src/plugins/sib-disconnected.js @@ -0,0 +1,24 @@ +/** + * Initialize AUTHFAIL plugin + */ +converse.plugins.add('sib-disconnected', { + initialize() { + const _converse = this._converse; + const { api } = _converse; + const { Strophe } = converse.env; + + const getChats = () => Array.from(document.querySelectorAll('solid-xmpp-chat')); + + api.listen.on('disconnected', () => { + if (_converse.connfeedback.attributes.connection_status === Strophe.Status.AUTHFAIL) { + getChats().map(el => el.shadowRoot.innerHTML = + `<div style='margin:3em;line-height:32px;'><b style='color:red;'>Erreur d'authentification.</b><br /><i style='color:grey;'>Êtes-vous sur le Hubl affilié à votre compte ?</i></div>`, + ); + } else { + getChats().map(el => el.shadowRoot.innerHTML = + `<div style='margin:3em;line-height:32px;'><b style='color:red;'>Erreur.</b><br /><i style='color:grey;'>${_converse.connfeedback.attributes.message}</i></div>`, + ); + } + }); + }, +}); diff --git a/src/plugins/sib-mam-history.js b/src/plugins/sib-mam-history.js index bffcc149dfbab10eef3c601aca1026bf6db2d490..f0704e759212129dce7970a2196b940b0443e8d6 100644 --- a/src/plugins/sib-mam-history.js +++ b/src/plugins/sib-mam-history.js @@ -3,46 +3,46 @@ * from the setting `archived_messages_page_size`. */ converse.plugins.add('sib-mam-history', { - dependencies: [ - 'converse-mam', - ], - initialize() { - const { api } = this._converse; - let counter = 0; + dependencies: [ + 'converse-mam', + ], + initialize() { + const { api } = this._converse; + let counter = 0; - api.listen.on('MAMResult', async data => { + api.listen.on('MAMResult', async data => { - const max = +api.settings.get('archived_messages_page_size'); - const messages = await Promise.all(data.messages); + const max = +api.settings.get('archived_messages_page_size'); + const messages = await Promise.all(data.messages); - // Increase counter with the messages that contains a body - counter += messages.filter(attrs => attrs.body).length; + // Increase counter with the messages that contains a body + counter += messages.filter(attrs => attrs.body).length; - // Stop if the max value is not specified - // Stop if there are less messages available than the page size - // Stop if the counter registers more messages than the max value - if (!max || messages.length < max || counter >= max) { - counter = 0; - return; - } + // Stop if the max value is not specified + // Stop if there are less messages available than the page size + // Stop if the counter registers more messages than the max value + if (!max || messages.length < max || counter >= max) { + counter = 0; + return; + } - const is_groupchat = data.chatbox.get('type') === converse.CHATROOMS_TYPE; - const oldest_message = messages.pop(); + const is_groupchat = data.chatbox.get('type') === converse.CHATROOMS_TYPE; + const oldest_message = messages.pop(); - if (oldest_message) { - const by_jid = is_groupchat ? data.chatbox.get('jid') : converse.bare_jid; - const stanza_id = oldest_message && oldest_message['stanza_id '.concat(by_jid)]; + if (oldest_message) { + const by_jid = is_groupchat ? data.chatbox.get('jid') : converse.bare_jid; + const stanza_id = oldest_message && oldest_message['stanza_id '.concat(by_jid)]; - if (stanza_id) { - await data.chatbox.fetchArchivedMessages({ - 'before': stanza_id - }); - } else { - await data.chatbox.fetchArchivedMessages({ - 'end': oldest_message['time'] - }); - } - } - }) - } + if (stanza_id) { + await data.chatbox.fetchArchivedMessages({ + 'before': stanza_id, + }); + } else { + await data.chatbox.fetchArchivedMessages({ + 'end': oldest_message['time'], + }); + } + } + }); + }, }); diff --git a/src/plugins/sib-remove-notifications.js b/src/plugins/sib-remove-notifications.js new file mode 100644 index 0000000000000000000000000000000000000000..b80d748879c83eb563d689514d0cdfdc98dce6f0 --- /dev/null +++ b/src/plugins/sib-remove-notifications.js @@ -0,0 +1,26 @@ +/** + * Override converse's request permission with an empty function + * so that permission request for notifications don't happen. + */ +converse.plugins.add('sib-remove-notifications', { + overrides: { + requestPermission: function() { + }, + showMessageNotification: function() { + }, + showChatStateNotification: function() { + }, + showContactRequestNotification: function() { + }, + showFeedbackNotification: function() { + }, + handleChatStateNotification: function() { + }, + handleMessageNotification: function() { + }, + handleContactRequestNotification: function() { + }, + handleFeedback: function() { + }, + }, +}); diff --git a/src/plugins/sib-subscribe-to-rai.js b/src/plugins/sib-subscribe-to-rai.js new file mode 100644 index 0000000000000000000000000000000000000000..51dbb91174b448768f923173549ac9555694063b --- /dev/null +++ b/src/plugins/sib-subscribe-to-rai.js @@ -0,0 +1,70 @@ +import { store } from 'https://cdn.skypack.dev/@startinblox/core@0.15'; + +/** + * Initialize rai plugin. + */ +converse.plugins.add('sib-subscribe-to-rai', { + dependencies: [ + 'converse-rai', + ], + async initialize() { + const _converse = this._converse; + const { api } = _converse; + + const getCircles = new Promise((resolve, reject) => { + const circleInterval = setInterval(async () => { + let retry = false; + let circles = []; + const user = await document.querySelector('sib-auth').getUser(); + const userProfile = await store.getData(user['@id'], this.context); + for (let circleMembership of await userProfile['circles.ldp:contains']) { + if (circleMembership) { + circles.push(await circleMembership['circle.jabberID']); + } else { + retry = true; + } + } + if (!retry) { + clearInterval(circleInterval); + resolve(circles); + } + }, 250); + }); + + const getProjects = new Promise((resolve, reject) => { + const projectInterval = setInterval(async () => { + let retry = false; + let projects = []; + const user = await document.querySelector('sib-auth').getUser(); + const userProfile = await store.getData(user['@id'], this.context); + for (let projectMembership of await userProfile['projects.ldp:contains']) { + if (projectMembership) { + projects.push(await projectMembership['project.jabberID']); + } else { + retry = true; + } + } + if (!retry) { + clearInterval(projectInterval); + resolve(projects); + } + }, 250); + }); + + let userRooms = (await Promise.all([ + getCircles, + getProjects, + ])).flat(); + + // @MattJ Here userRooms is an array of each jabberID the user is on. + api.trigger('raiRoomsUpdated', userRooms); + + api.listen.on('chatRoomActivityIndicators', jid => { + window.dispatchEvent(new CustomEvent('newMessage', { + detail: { + jid, + }, + })); + }); + }, +}); diff --git a/src/solid-xmpp-chat.js b/src/solid-xmpp-chat.js index 6be7190759de9134c45d421b9542e8b88ab7483a..613139fe497d987439a7b9bed513f6dad6621ff0 100644 --- a/src/solid-xmpp-chat.js +++ b/src/solid-xmpp-chat.js @@ -1,24 +1,21 @@ -import './conversejs/converse.min.js'; -import './conversejs/emojis.js'; -import './plugins/converse-rai.js'; -import './plugins/sib-mam-history.js' import { Sib, store, StoreMixin } from 'https://cdn.skypack.dev/@startinblox/core@0.15'; +import { Deferred } from './utils.js'; import ComponentPath from './path.js'; -class Deferred { - constructor() { - this.promise = new Promise(((resolve, reject) => { - this.resolve = resolve; - this.reject = reject; - }).bind(this)); - this.then = this.promise.then.bind(this.promise); - this.catch = this.promise.catch.bind(this.promise); - } -} +import './conversejs/converse.min.js'; +import './conversejs/emojis.js'; + +import './plugins/converse-rai.js'; +import './plugins/sib-chat-navigation.js'; +import './plugins/sib-custom-hats.js'; +import './plugins/sib-disconnected.js'; +import './plugins/sib-mam-history.js'; +import './plugins/sib-remove-notifications.js'; +import './plugins/sib-subscribe-to-rai.js'; export const SolidXMPPChat = { name: 'solid-xmpp-chat', - use: [ StoreMixin ], + use: [StoreMixin], attributes: { authentication: { type: String, @@ -39,18 +36,20 @@ export const SolidXMPPChat = { }, get extra_context() { - return { "foaf": 'http://xmlns.com/foaf/0.1/', "chatProfile": "http://happy-dev.fr/owl/#chatProfile", "jabberID": "foaf:jabberID" }; + return { + 'foaf': 'http://xmlns.com/foaf/0.1/', + 'chatProfile': 'http://happy-dev.fr/owl/#chatProfile', + 'jabberID': 'foaf:jabberID', + }; }, async created() { const check_identified = await document.querySelector('sib-auth').getUserIdToken(); - if(check_identified) { - this.element.attachShadow({ - mode: "open" - }); + if (check_identified) { + this.element.attachShadow({ mode: 'open' }); this.importCSS(`${ComponentPath}/dist/conversejs/converse.min.css?min`); this.importCSS(`${ComponentPath}/dist/index.css?min`); - if (window.converse_sib === undefined) { + if (typeof converse_sib === 'undefined') { this.initializeConverse(); } } // Else, not logged in on page load (even if not on chat) @@ -68,7 +67,7 @@ export const SolidXMPPChat = { }, async populate() { - if(typeof converse_sib !== 'undefined') { + if (typeof converse_sib !== 'undefined') { await converse_sib.loaded_deferred; if (this.resource) { if (await this.resource.jabberRoom) { @@ -78,30 +77,62 @@ export const SolidXMPPChat = { const user = await document.querySelector('sib-auth').getUser(); const userProfile = await store.getData(user['@id'], this.context); const contactsURL = await userProfile['contacts.@id']; - try { - store.post({ - "contact": this.resource['@id'], - "@context": this.context - }, contactsURL); - } catch (e) {} + const contactInterval = setInterval(async () => { + let retry = false; + let userContacts = []; + for (let contact of await userProfile['contacts.ldp:contains']) { + if (contact) { + userContacts.push(await contact['contact.@id']); + } else { + retry = true; + } + } + if (!retry) { + clearInterval(contactInterval); + if (!userContacts.includes(this.resource['@id'])) { + store.post({ + 'contact': this.resource['@id'], + '@context': this.context, + }, contactsURL); + } + } + }, 100); } await converse_sib.connected_deferred; - converse_sib.service.plugins.sibChat.changeChat( - this.jid, - await this.resource.jabberRoom, - this.element.shadowRoot, - ); + + window.dispatchEvent(new CustomEvent('sib-change-chat', { + detail: { + jid: this.jid, + is_groupchat: await this.resource.jabberRoom, + root: this.element.shadowRoot, + }, + })); + window.dispatchEvent(new CustomEvent('read', { detail: { resource: { - "@id": this.resource['@id'] - } - } + '@id': this.resource['@id'], + }, + }, })); } } // Else, not logged in, on chat change }, + removeConverseDatabase() { + for (let key in sessionStorage) { + if (sessionStorage.hasOwnProperty(key) && /converse/.test(key)) { + sessionStorage.removeItem(key); + } + } + for (let key in localStorage) { + if (localStorage.hasOwnProperty(key) && /converse/.test(key)) { + localStorage.removeItem(key); + } + } + indexedDB.deleteDatabase('converse-persistent'); + }, + initializeConverse() { window.converse_sib = {}; @@ -111,265 +142,24 @@ export const SolidXMPPChat = { // Deferred resolved after converse.initialize converse_sib.connected_deferred = new Deferred(); - // Registering window.converse_sib.service - converse_sib.service = { - 'waitUntilLoaded': converse_sib.loaded_deferred.promise, - 'initialize': function (options) { - this.waitUntilLoaded().done(this.api.initialize, options); - }, - 'waitUntilConnected': converse_sib.connected_deferred.promise, - 'plugins': {} - }; - - for(var key in sessionStorage){ - if(sessionStorage.hasOwnProperty(key) && /converse/.test(key)){ - sessionStorage.removeItem(key); - } - } - for(var key in localStorage){ - if(localStorage.hasOwnProperty(key) && /converse/.test(key)){ - localStorage.removeItem(key); - } - } - indexedDB.deleteDatabase('converse-persistent'); - - // Alias for solid-xmpp-chat use # want to un-expose converse from window later - // window.converse_sib.service.api === window.converse - converse_sib.service.api = converse; - - // Change chat plugin - converse_sib.service.plugins.sibChat = new(class { - changeChat(jid, is_groupchat, root) { - // function isEmptyMessage (attrs) { - // if (attrs && attrs.attributes) { - // attrs = attrs.attributes; - // } - // return !attrs['oob_url'] && - // !attrs['file'] && - // !(attrs['is_encrypted'] && attrs['plaintext']) && - // !attrs['message']; - // }; + this.removeConverseDatabase(); - // function removeUnnecessaryDayIndicators(view) { - // const pred = (el) => - // el.matches('.date-separator') && el.nextElementSibling.matches('.date-separator'); - // const container = view.el.querySelector('.chat-content__messages'); - // const to_remove = Array.from(container.children).filter(pred); - // to_remove.forEach((el) => el.parentElement.removeChild(el)); - // }; - // function isHidden(classList){ - // for(let i = 0; i < classList.length; i++){ - // if(classList[i] === 'hidden'){ - // return true; - // } - // } - // return false; - // } - if (!jid) { - return; - } - jid = jid.toLowerCase(); - this._converse.root = root; - - // Get the current used solid-xmpp-chat - const converse_el = Array.from(document.querySelectorAll('solid-xmpp-chat')) - .map(el => el.shadowRoot.getElementById('conversejs')) - .filter(el => el) - .pop(); - - if (converse_el) { - root.appendChild(converse_el); - // if (is_groupchat) { - // const jid_to_clear = converse_el.getElementsByClassName('converse-chatboxes'); - // let room_to_clear_view = ''; - // if (jid_to_clear.length && jid_to_clear[0].children.length){ - // for (let i = 0; i < jid_to_clear[0].children.length; i++) { - // if(!isHidden(jid_to_clear[0].children[i].classList)){ - // room_to_clear_view = this._converse.chatboxviews.views[jid_to_clear[0].children[i].id.split('-')[1]]; - // break; - // } - // } - // } - // if (room_to_clear_view) { - // if (room_to_clear_view.model.messages.length > 30) { - // const non_empty_messages = room_to_clear_view.model.messages.filter((m) => !isEmptyMessage(m)); - // if (non_empty_messages.length > 30) { - // while (non_empty_messages.length > 30) { - // non_empty_messages.shift().destroy(); - // } - // removeUnnecessaryDayIndicators(room_to_clear_view); - // } - // } - // } - // } - } - if (is_groupchat) { - this._converse.api.rooms.open(jid, {}, true); - } else { - this._converse.api.chats.open(jid, {}, true); - } - } - }); // Initialize deferred resolution plugin setTimeout(async () => { - // Initialize change change plugin - converse.plugins.add('conversejs-changechat', converse_sib.service.plugins.sibChat); - - // Initialize deferred resolution plugin - converse.plugins.add('conversejs-sib-connected', { - initialize() { - this._converse.api.listen.on('connectionInitialized', converse_sib.connected_deferred.resolve); - } - }); - - // Initialize AUTHFAIL plugin - converse.plugins.add('conversejs-sib-disconnected', { - initialize() { - this._converse.api.listen.on('disconnected', () => { - if(this._converse.connfeedback.attributes.connection_status === converse.env.Strophe.Status.AUTHFAIL) { - Array.from(document.querySelectorAll('solid-xmpp-chat')).map(el => el.shadowRoot.innerHTML = - `<div style='margin:3em;line-height:32px;'><b style='color:red;'>Erreur d'authentification.</b><br /><i style='color:grey;'>Êtes-vous sur le Hubl affilié à votre compte ?</i></div>` - ); - } else { - Array.from(document.querySelectorAll('solid-xmpp-chat')).map(el => el.shadowRoot.innerHTML = - `<div style='margin:3em;line-height:32px;'><b style='color:red;'>Erreur.</b><br /><i style='color:grey;'>${this._converse.connfeedback.attributes.message}</i></div>` - ); - } - }); - } - }); - // Initialize rai plugin - converse.plugins.add('conversejs-rai', { - async initialize() { - - let userRooms = (await Promise.all([ - new Promise((resolve, reject) => { - const circleInterval = setInterval(async () => { - let retry = false; - let circles = []; - const user = await document.querySelector('sib-auth').getUser(); - const userProfile = await store.getData(user['@id'], this.context); - for (let circleMembership of await userProfile['circles.ldp:contains']) { - if (circleMembership) { - circles.push(await circleMembership['circle.jabberID']); - } else { - retry = true; - } - } - if (!retry) { - clearInterval(circleInterval); - resolve(circles); - } - }, 250); - }), - new Promise((resolve, reject) => { - const projectInterval = setInterval(async () => { - let retry = false; - let projects = []; - const user = await document.querySelector('sib-auth').getUser(); - const userProfile = await store.getData(user['@id'], this.context); - for (let projectMembership of await userProfile['projects.ldp:contains']) { - if (projectMembership) { - projects.push(await projectMembership['project.jabberID']); - } else { - retry = true; - } - } - if (!retry) { - clearInterval(projectInterval); - resolve(projects); - } - }, 250); - }) - ])).flat(); - - // @MattJ Here userRooms is an array of each jabberID the user is on. - this._converse.api.trigger('raiRoomsUpdated', userRooms); - - this._converse.api.listen.on('chatRoomActivityIndicators', function (jid) { - window.dispatchEvent(new CustomEvent('newMessage', { - detail: { - jid: jid - } - })); - }); - } - }); - - // Transform hats to custom values - converse.plugins.add('custom-hats', { - overrides: { - getHats: function () { - const hat_conversions = {'admin': 'Administrateur'}; - const _converse = this; - const hats = _converse.__super__.getHats.apply(this, arguments); - if (!hat_conversions) { - return hats; - } else { - const role_affiliations = Object.keys(hat_conversions); - const custom_hats = hats.map((hat) => { - if (role_affiliations.includes(hat.title)){ - return({title: hat_conversions[hat.title]}); - } else { - return hat; - } - }) - return custom_hats - } - } - } - }); - //Highly experimental DO NOT USE -// converse.plugins.add('custom-storage', { -// overrides: { -// createStore: function () { -// const _converse = this; -// if (arguments.length > 1){ -// arguments[1] = "none"; -// } -// return _converse.__super__.createStore.apply(this, arguments); -// } -// } -// }); - //Override converse's request permission with an empty function - //so that permission request for notifications don't happen - converse.plugins.add('remove-notifications', { - overrides: { - requestPermission: function () {}, - showMessageNotification: function () {}, - showChatStateNotification: function () {}, - showContactRequestNotification: function () {}, - showFeedbackNotification: function () {}, - handleChatStateNotification: function () {}, - handleMessageNotification: function () {}, - handleContactRequestNotification: function () {}, - handleFeedback: function () {}, - } - }); // Initialize deferred resolution plugin - converse.plugins.add('conversejs-sib-focused', { + converse.plugins.add('sib-connected', { initialize() { - this._converse.api.listen.on('chatBoxFocused', function() { - // Get the currently used solid-xmpp-chat & send read event of - const resource = Array.from(document.querySelectorAll('solid-xmpp-chat')) - .filter(el => {return el.shadowRoot.getElementById('conversejs')}) - .pop() - .component.resource; - window.dispatchEvent(new CustomEvent('read', { - detail: { - resource: resource - } - })); - }); - } + const { api } = this._converse; + api.listen.on('connectionInitialized', converse_sib.connected_deferred.resolve); + }, }); const user = await document.querySelector('sib-auth').getUser(); const userProfile = await store.getData(user['@id'], this.context); const jabberID = await userProfile['chatProfile.jabberID']; - if(this.resource) { + if (this.resource) { if (await this.resource.jabberRoom) { this.jid = await this.resource['jabberID']; } else { @@ -391,7 +181,7 @@ export const SolidXMPPChat = { 'allow_non_roster_messaging': true, 'allow_dragresize': false, 'allow_logout': false, - 'archived_messages_page_size': 30, + 'archived_messages_page_size': '30', 'auto_list_rooms': true, 'auto_login': this.element.dataset.autoLogin === 'true', 'auto_join_on_invite': true, @@ -399,8 +189,8 @@ export const SolidXMPPChat = { 'auto_register_muc_nickname': false, 'websocket_url': this.element.dataset.websocketUrl || 'wss://jabber.happy-dev.fr/xmpp-websocket', 'enable_smacks': true, - "clear_messages_on_reconnection": false, - "discover_connection_methods": false, + 'clear_messages_on_reconnection': false, + 'discover_connection_methods': false, 'jid': jabberID.toLowerCase(), 'i18n': this.element.dataset.i18n || 'fr', 'loglevel': 'fatal', @@ -408,7 +198,7 @@ export const SolidXMPPChat = { 'message_archiving_timeout': 60000, 'muc_disable_slash_commands': true, 'muc_hats': ['hats', 'vcard_roles', 'admin'], - 'role_affiliation_hat_conversions': {'admin': 'Administrateur'}, + 'role_affiliation_hat_conversions': { 'admin': 'Administrateur' }, 'muc_nickname_from_jid': false, 'muc_fetch_members': true, 'muc_show_info_messages': [], @@ -425,24 +215,23 @@ export const SolidXMPPChat = { spoiler: false, emoji: true, fileupload: true, // Not working in current Converse - toggle_occupants: false + toggle_occupants: false, }, 'whitelisted_plugins': [ - 'rai', + 'converse-rai', + 'sib-chat-navigation', + 'sib-connected', + 'sib-custom-hats', + 'sib-disconnected', 'sib-mam-history', - 'conversejs-sib-disconnected', - 'conversejs-sib-connected', - 'conversejs-sib-focused', - 'conversejs-changechat', - 'conversejs-rai', - 'custom-hats', - 'remove-notifications', + 'sib-remove-notifications', + 'sib-subscribe-to-rai', ], }); converse_sib.loaded_deferred.resolve(); }, 0); - } + }, }; Sib.register(SolidXMPPChat); diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000000000000000000000000000000000000..01f067ac60462f6eef371eb4607c31f374113670 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,10 @@ +export class Deferred { + constructor() { + this.promise = new Promise(function(resolve, reject) { + this.resolve = resolve; + this.reject = reject; + }.bind(this)); + this.then = this.promise.then.bind(this.promise); + this.catch = this.promise.catch.bind(this.promise); + } +}