diff --git a/src/plugins/converse-rai.js b/src/plugins/converse-rai.js
index c11090646e0a5ae692b4128828ddab8832e8d841..fb72df1e5b2adb9de11c01901941379d87e29b6f 100644
--- a/src/plugins/converse-rai.js
+++ b/src/plugins/converse-rai.js
@@ -1,195 +1,120 @@
-(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);
-        }
-      });
-
-      _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);
+/**
+ * Add RAI support to MUCs.
+ * @see https://xmpp.org/extensions/inbox/room-activity-indicators.html
+ */
+converse.plugins.add('converse-rai', {
+  dependencies: [
+    'converse-chatboxes',
+  ],
+  overrides: {
+    ChatRoom: {
+      async initialize() {
+        await this.__super__.initialize.apply(this, arguments);
+        this.listenTo(this, 'change:hidden', this.onHiddenChange);
+      },
+
+      /**
+       * Handler that gets called when the 'hidden' flag is toggled.
+       * @private
+       * @method _converse.ChatRoom#onHiddenChange
+       */
+      async onHiddenChange() {
+        const conn_status = this.session.get('connection_status');
+        const { api } = this.__super__._converse;
+
+        if (this.get('hidden') && conn_status === converse.ROOMSTATUS.ENTERED) {
+          if (api.settings.get('muc_subscribe_to_rai') && this.getOwnAffiliation() !== 'none') {
+            if (conn_status !== converse.ROOMSTATUS.DISCONNECTED) {
+              this.sendMarkerForLastMessage('received', true);
+              await this.leave();
+              await this.close();
             }
-            break;
           }
+        } else if (conn_status === converse.ROOMSTATUS.DISCONNECTED) {
+          await this.rejoin();
         }
-      });
-
-      _converse.api.listen.on('chatBoxInsertedIntoDOM', function(view) {
-        const jid = view.model.get('jid');
+      },
+    },
+    ChatBox: {
+      /**
+       * Finds the last eligible message and then sends a XEP-0333 chat marker for it.
+       * @param { ('received'|'displayed'|'acknowledged') } [type='displayed']
+       * @param { Boolean } force - Whether a marker should be sent for the
+       *  message, even if it didn't include a `markable` element.
+       */
+      sendMarkerForLastMessage(type = 'displayed', force = false) {
+        const msgs = Array.from(this.messages.models);
+        msgs.reverse();
+        const msg = msgs.find(m => m.get('sender') === 'them' && (force || m.get('is_markable')));
+        msg && this.sendMarkerForMessage(msg, type, force);
+      },
+    },
+  },
+  initialize() {
+    const { Strophe, _, $pres, u, sizzle, log } = converse.env;
+    const _converse = this._converse;
+    const { api } = _converse;
+
+    // Register namespace
+    Strophe.addNamespace('RAI', 'urn:xmpp:rai:0');
+
+    // Register settings
+    api.settings.extend({
+      muc_subscribe_to_rai: false,
+    });
 
-        if (view.model.get('num_unread') > 0) {
-          emitNotification(jid);
+    Object.assign(api.rooms, {
+      /**
+       * Send an RAI stanza for the given jids.
+       * The presence stanza is sent for a whole muc domain.
+       *
+       * @param {string|Array} jids
+       * @returns {void}
+       */
+      subscribe(jids) {
+        if (!api.settings.get('muc_subscribe_to_rai')) {
+          console.error('Can\'t subscribe to RAI, this feature is not enabled');
         }
 
-      });
-
-      _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);
-          }
+        if (typeof jids === 'string') {
+          jids = [jids];
         }
-      });
-    },
-  });
 
+        const muc_domains = jids.map(jid => Strophe.getDomainFromJid(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',
-    }));
-  }
-
-  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);
-    }
-  }
+        _.uniq(muc_domains).forEach(muc_domain => {
+          const rai = $pres({ to: muc_domain, id: u.getUniqueId() }).c('rai', {
+            'xmlns': Strophe.NS.RAI,
+          });
+          api.send(rai);
+          console.log('Sent RAI stanza for muc_domain', muc_domain, rai.toString());
+        });
+      },
+    });
 
-  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);
+    /**
+     * Triggers an event if a jid has activity.
+     *
+     * @param message
+     * @returns {boolean}
+     */
+    function mucActivityHandler(message) {
+      const rai = sizzle(`rai[xmlns="${Strophe.NS.RAI}"]`, message).pop();
+
+      if (rai) {
+        rai.querySelectorAll('activity').forEach(activity => {
+          const jid = activity.textContent;
+          log.debug(`Received activity for the room: ${jid}`);
+          api.trigger('chatRoomHasActivity', jid);
+        });
       }
-    }
 
-    // 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');
-  }
-
-  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,
+    // Register our RAI handler
+    api.listen.on('connected', () => {
+      _converse.connection.addHandler(mucActivityHandler, null, 'message');
     });
-
-    _converse.api.send(stanza);
-  }
-})();
+  },
+});
diff --git a/src/plugins/sib-subscribe-to-rai.js b/src/plugins/sib-subscribe-to-rai.js
index 51dbb91174b448768f923173549ac9555694063b..e18baf76676125b85121b46258fc82adb062335f 100644
--- a/src/plugins/sib-subscribe-to-rai.js
+++ b/src/plugins/sib-subscribe-to-rai.js
@@ -51,15 +51,17 @@ converse.plugins.add('sib-subscribe-to-rai', {
       }, 250);
     });
 
+    // @MattJ Here userRooms is an array of each jabberID the user is on.
     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('connected', () => {
+      api.rooms.subscribe(userRooms);
+    });
 
-    api.listen.on('chatRoomActivityIndicators', jid => {
+    api.listen.on('chatRoomHasActivity', jid => {
       window.dispatchEvent(new CustomEvent('newMessage', {
         detail: {
           jid,
diff --git a/src/solid-xmpp-chat.js b/src/solid-xmpp-chat.js
index e6c792da921f140a734b19e4bb29759d77cfa955..8887e7ab131e173794aba07d10221da35c634299 100644
--- a/src/solid-xmpp-chat.js
+++ b/src/solid-xmpp-chat.js
@@ -205,6 +205,7 @@ export const SolidXMPPChat = {
         'muc_nickname_from_jid': false,
         'muc_fetch_members': true,
         'muc_show_info_messages': [],
+        'muc_subscribe_to_rai': true,
         'play_sounds': false,
         'root': this.element.shadowRoot,
         'show_client_info': false,