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);
+  }
+}