diff --git a/spec/messages.js b/spec/messages.js index 3d7a94444..df1dd1920 100644 --- a/spec/messages.js +++ b/spec/messages.js @@ -2047,7 +2047,7 @@ describe("A XEP-0333 Chat Marker", function () { .map(s => _.isElement(s) ? s : s.nodeTree) .filter(e => e.nodeName === 'message'); - expect(sent_messages.length).toBe(2); + await u.waitUntil(() => sent_messages.length === 2); expect(Strophe.serialize(sent_messages[0])).toBe( ``+ ``+ diff --git a/spec/retractions.js b/spec/retractions.js index ac654c073..34d01047b 100644 --- a/spec/retractions.js +++ b/spec/retractions.js @@ -616,9 +616,12 @@ describe("Message Retractions", function () { expect(occupant.get('role')).toBe('moderator'); occupant.save('role', 'member'); const retraction_stanza = await sendAndThenRetractMessage(_converse, view); - await u.waitUntil(() => view.el.querySelectorAll('.chat-msg--retracted').length === 1); + await u.waitUntil(() => view.el.querySelectorAll('.chat-msg--retracted').length === 1, 1000); + console.log('XXX: First message retracted by author'); const msg_obj = view.model.messages.last(); + expect(msg_obj.get('retracted')).toBeTruthy(); + expect(Strophe.serialize(retraction_stanza)).toBe( ``+ ``+ @@ -627,7 +630,6 @@ describe("Message Retractions", function () { ``+ ``); - await u.waitUntil(() => view.model.messages.last().get('retracted')); const message = view.model.messages.last(); expect(message.get('is_ephemeral')).toBe(false); expect(message.get('editable')).toBeFalsy(); @@ -645,9 +647,11 @@ describe("Message Retractions", function () { spyOn(view.model, 'handleRetraction').and.callThrough(); _converse.connection._dataRecv(mock.createRequest(reflection)); - await u.waitUntil(() => view.model.handleRetraction.calls.count() === 1); + await u.waitUntil(() => view.model.handleRetraction.calls.count() === 1, 1000); + console.log('XXX: Handle retraction was called on reflection'); - await u.waitUntil(() => view.model.messages.length === 1); + await u.waitUntil(() => view.model.messages.length === 1, 1000); + console.log('XXX: We have one message'); expect(view.model.messages.last().get('retracted')).toBeTruthy(); expect(view.model.messages.last().get('is_ephemeral')).toBe(false); expect(view.model.messages.last().get('editable')).toBe(false); diff --git a/src/converse-chatview.js b/src/converse-chatview.js index 6386be6a5..f187c9dff 100644 --- a/src/converse-chatview.js +++ b/src/converse-chatview.js @@ -98,7 +98,7 @@ converse.plugins.add('converse-chatview', { this.model.toJSON(), vcard_json, { '_converse': _converse, - 'allow_contact_removal': _converse.allow_contact_removal, + 'allow_contact_removal': api.settings.get('allow_contact_removal'), 'display_name': this.model.getDisplayName(), 'is_roster_contact': this.model.contact !== undefined, 'removeContact': ev => this.removeContact(ev), @@ -133,7 +133,7 @@ converse.plugins.add('converse-chatview', { removeContact (ev) { if (ev && ev.preventDefault) { ev.preventDefault(); } - if (!_converse.allow_contact_removal) { return; } + if (!api.settings.get('allow_contact_removal')) { return; } const result = confirm(__("Are you sure you want to remove this contact?")); if (result === true) { this.modal.hide(); diff --git a/src/converse-controlbox.js b/src/converse-controlbox.js index e8b8a177d..98ce49c36 100644 --- a/src/converse-controlbox.js +++ b/src/converse-controlbox.js @@ -251,7 +251,7 @@ converse.plugins.add('converse-controlbox', { this.insertBrandHeading(); } this.loginpanel.initPopovers(); - if (_converse.auto_focus) { + if (api.settings.get('auto_focus')) { this.loginpanel.el.querySelector('#converse-login-jid').focus(); } return this; @@ -384,7 +384,7 @@ converse.plugins.add('converse-controlbox', { 'EXTERNAL': _converse.EXTERNAL, 'LOGIN': _converse.LOGIN, 'PREBIND': _converse.PREBIND, - 'auto_login': _converse.auto_login, + 'auto_login': api.settings.get('auto_login'), 'authentication': api.settings.get("authentication"), 'connection_status': connection_status, 'conn_feedback_class': feedback_class, diff --git a/src/converse-muc-views.js b/src/converse-muc-views.js index 72a47958c..40980ed39 100644 --- a/src/converse-muc-views.js +++ b/src/converse-muc-views.js @@ -74,9 +74,8 @@ converse.plugins.add('converse-muc-views', { overrides: { ControlBoxView: { renderControlBoxPane () { - const { _converse } = this.__super__; this.__super__.renderControlBoxPane.apply(this, arguments); - if (_converse.allow_muc) { + if (api.settings.get('allow_muc')) { this.renderRoomsPanel(); } } @@ -224,17 +223,17 @@ converse.plugins.add('converse-muc-views', { this.loading_items = false; BootstrapModal.prototype.initialize.apply(this, arguments); - if (_converse.muc_domain && !this.model.get('muc_domain')) { - this.model.save('muc_domain', _converse.muc_domain); + if (api.settings.get('muc_domain') && !this.model.get('muc_domain')) { + this.model.save('muc_domain', api.settings.get('muc_domain')); } this.listenTo(this.model, 'change:muc_domain', this.onDomainChange); }, toHTML () { - const muc_domain = this.model.get('muc_domain') || _converse.muc_domain; + const muc_domain = this.model.get('muc_domain') || api.settings.get('muc_domain'); return tpl_list_chatrooms_modal( Object.assign(this.model.toJSON(), { - 'show_form': !_converse.locked_muc_domain, + 'show_form': !api.settings.get('locked_muc_domain'), 'server_placeholder': muc_domain ? muc_domain : __('conference.example.org'), 'items': this.items, 'loading_items': this.loading_items, @@ -246,7 +245,7 @@ converse.plugins.add('converse-muc-views', { }, afterRender () { - if (_converse.locked_muc_domain) { + if (api.settings.get('locked_muc_domain')) { this.updateRoomsList(); } else { this.el.addEventListener('shown.bs.modal', @@ -270,7 +269,7 @@ converse.plugins.add('converse-muc-views', { }, onDomainChange () { - _converse.auto_list_rooms && this.updateRoomsList(); + api.settings.get('auto_list_rooms') && this.updateRoomsList(); }, /** @@ -347,16 +346,16 @@ converse.plugins.add('converse-muc-views', { toHTML () { let placeholder = ''; - if (!_converse.locked_muc_domain) { - const muc_domain = this.model.get('muc_domain') || _converse.muc_domain; + if (!api.settings.get('locked_muc_domain')) { + const muc_domain = this.model.get('muc_domain') || api.settings.get('muc_domain'); placeholder = muc_domain ? `name@${muc_domain}` : __('name@conference.example.org'); } return tpl_add_chatroom_modal(Object.assign(this.model.toJSON(), { '_converse': _converse, - 'label_room_address': _converse.muc_domain ? __('Groupchat name') : __('Groupchat address'), + 'label_room_address': api.settings.get('muc_domain') ? __('Groupchat name') : __('Groupchat address'), 'chatroom_placeholder': placeholder, 'muc_roomid_policy_error_msg': this.muc_roomid_policy_error_msg, - 'muc_roomid_policy_hint': _converse.muc_roomid_policy_hint + 'muc_roomid_policy_hint': api.settings.get('muc_roomid_policy_hint') })); }, @@ -370,7 +369,7 @@ converse.plugins.add('converse-muc-views', { const data = new FormData(form); const jid = data.get('chatroom'); let nick; - if (_converse.locked_muc_nickname) { + if (api.settings.get('locked_muc_nickname')) { nick = _converse.getDefaultMUCNickname(); if (!nick) { throw new Error("Using locked_muc_nickname but no nickname found!"); @@ -392,8 +391,8 @@ converse.plugins.add('converse-muc-views', { data.nick = undefined; } let jid; - if (_converse.locked_muc_domain || (_converse.muc_domain && !u.isValidJID(data.jid))) { - jid = `${Strophe.escapeNode(data.jid)}@${_converse.muc_domain}`; + if (api.settings.get('locked_muc_domain') || (api.settings.get('muc_domain') && !u.isValidJID(data.jid))) { + jid = `${Strophe.escapeNode(data.jid)}@${api.settings.get('muc_domain')}`; } else { jid = data.jid this.model.setDomain(jid); @@ -404,15 +403,15 @@ converse.plugins.add('converse-muc-views', { }, checkRoomidPolicy () { - if (_converse.muc_roomid_policy && _converse.muc_domain) { + if (api.settings.get('muc_roomid_policy') && api.settings.get('muc_domain')) { let jid = this.el.querySelector('.roomjid-input').value; if (converse.locked_muc_domain || !u.isValidJID(jid)) { - jid = `${Strophe.escapeNode(jid)}@${_converse.muc_domain}`; + jid = `${Strophe.escapeNode(jid)}@${api.settings.get('muc_domain')}`; } const roomid = Strophe.getNodeFromJid(jid); const roomdomain = Strophe.getDomainFromJid(jid); - if (_converse.muc_domain !== roomdomain || - _converse.muc_roomid_policy.test(roomid)) { + if (api.settings.get('muc_domain') !== roomdomain || + api.settings.get('muc_roomid_policy').test(roomid)) { this.muc_roomid_policy_error_msg = null; } else { this.muc_roomid_policy_error_msg = __('Groupchat id is invalid.'); @@ -513,7 +512,7 @@ converse.plugins.add('converse-muc-views', { async render () { this.el.setAttribute('id', this.model.get('box_id')); render(tpl_chatroom({ - 'muc_show_logs_before_join': _converse.muc_show_logs_before_join, + 'muc_show_logs_before_join': api.settings.get('muc_show_logs_before_join'), 'show_send_button': _converse.show_send_button }), this.el); @@ -523,7 +522,7 @@ converse.plugins.add('converse-muc-views', { this.help_container = this.el.querySelector('.chat-content__help'); this.renderBottomPanel(); - if (!_converse.muc_show_logs_before_join && + if (!api.settings.get('muc_show_logs_before_join') && this.model.session.get('connection_status') !== converse.ROOMSTATUS.ENTERED) { this.showSpinner(); } @@ -737,7 +736,7 @@ converse.plugins.add('converse-muc-views', { const element = document.createElement("li"); element.setAttribute("aria-selected", "false"); - if (_converse.muc_mention_autocomplete_show_avatar) { + if (api.settings.get('muc_mention_autocomplete_show_avatar')) { const img = document.createElement("img"); let dataUri = "data:" + _converse.DEFAULT_IMAGE_TYPE + ";base64," + _converse.DEFAULT_IMAGE; @@ -772,10 +771,12 @@ converse.plugins.add('converse-muc-views', { this.mention_auto_complete = new _converse.AutoComplete(this.el, { 'auto_first': true, 'auto_evaluate': false, - 'min_chars': _converse.muc_mention_autocomplete_min_chars, + 'min_chars': api.settings.get('muc_mention_autocomplete_min_chars'), 'match_current_word': true, 'list': () => this.getAutoCompleteList(), - 'filter': _converse.muc_mention_autocomplete_filter == 'contains' ? _converse.FILTER_CONTAINS : _converse.FILTER_STARTSWITH, + 'filter': api.settings.get('muc_mention_autocomplete_filter') == 'contains' ? + _converse.FILTER_CONTAINS : + _converse.FILTER_STARTSWITH, 'ac_triggers': ["Tab", "@"], 'include_triggers': [], 'item': this.getAutoCompleteListItem @@ -815,14 +816,14 @@ converse.plugins.add('converse-muc-views', { if (message.mayBeRetracted()) { const messages = [__('Are you sure you want to retract this message?')]; - if (_converse.show_retraction_warning) { + if (api.settings.get('show_retraction_warning')) { messages[1] = retraction_warning; } !!(await api.confirm(__('Confirm'), messages)) && this.model.retractOwnMessage(message); } else if (await message.mayBeModerated()) { if (message.get('sender') === 'me') { let messages = [__('Are you sure you want to retract this message?')]; - if (_converse.show_retraction_warning) { + if (api.settings.get('show_retraction_warning')) { messages = [messages[0], retraction_warning, messages[1]] } !!(await api.confirm(__('Confirm'), messages)) && this.retractOtherMessage(message); @@ -831,7 +832,7 @@ converse.plugins.add('converse-muc-views', { __('You are about to retract this message.'), __('You may optionally include a message, explaining the reason for the retraction.') ]; - if (_converse.show_retraction_warning) { + if (api.settings.get('show_retraction_warning')) { messages = [messages[0], retraction_warning, messages[1]] } const reason = await api.prompt( @@ -1257,7 +1258,7 @@ converse.plugins.add('converse-muc-views', { } } const attrs = { jid, reason }; - if (occupant && _converse.auto_register_muc_nickname) { + if (occupant && api.settings.get('auto_register_muc_nickname')) { attrs['nick'] = occupant.get('nick'); } this.model.setAffiliation(affiliation, [attrs]) @@ -1320,8 +1321,8 @@ converse.plugins.add('converse-muc-views', { } allowed_commands.sort(); - if (Array.isArray(_converse.muc_disable_slash_commands)) { - return allowed_commands.filter(c => !_converse.muc_disable_slash_commands.includes(c)); + if (Array.isArray(api.settings.get('muc_disable_slash_commands'))) { + return allowed_commands.filter(c => !api.settings.get('muc_disable_slash_commands').includes(c)); } else { return allowed_commands; } @@ -1355,7 +1356,8 @@ converse.plugins.add('converse-muc-views', { }, parseMessageForCommands (text) { - if (_converse.muc_disable_slash_commands && !Array.isArray(_converse.muc_disable_slash_commands)) { + if (api.settings.get('muc_disable_slash_commands') && + !Array.isArray(api.settings.get('muc_disable_slash_commands'))) { return _converse.ChatBoxView.prototype.parseMessageForCommands.apply(this, arguments); } text = text.replace(/^\s*/, ""); @@ -1499,7 +1501,7 @@ converse.plugins.add('converse-muc-views', { * @method _converse.ChatRoomView#renderNicknameForm */ renderNicknameForm () { - const heading = _converse.muc_show_logs_before_join ? + const heading = api.settings.get('muc_show_logs_before_join') ? __('Choose a nickname to enter') : __('Please choose your nickname'); @@ -1509,7 +1511,7 @@ converse.plugins.add('converse-muc-views', { 'label_join': __('Enter groupchat'), }, this.model.toJSON())); - if (_converse.muc_show_logs_before_join) { + if (api.settings.get('muc_show_logs_before_join')) { const container = this.el.querySelector('.muc-bottom-panel'); container.innerHTML = html; u.addClass('muc-bottom-panel--nickname', container); @@ -1783,7 +1785,7 @@ converse.plugins.add('converse-muc-views', { toHTML () { const stanza = u.toStanza(this.model.get('config_stanza')); - const whitelist = _converse.roomconfig_whitelist; + const whitelist = api.settings.get('roomconfig_whitelist'); let fields = sizzle('field', stanza); if (whitelist.length) { fields = fields.filter(f => whitelist.includes(f.getAttribute('var'))); @@ -1978,10 +1980,10 @@ converse.plugins.add('converse-muc-views', { function fetchAndSetMUCDomain (controlboxview) { if (controlboxview.model.get('connected')) { if (!controlboxview.getRoomsPanel().model.get('muc_domain')) { - if (_converse.muc_domain === undefined) { + if (api.settings.get('muc_domain') === undefined) { setMUCDomainFromDisco(controlboxview); } else { - setMUCDomain(_converse.muc_domain, controlboxview); + setMUCDomain(api.settings.get('muc_domain'), controlboxview); } } } @@ -2020,7 +2022,7 @@ converse.plugins.add('converse-muc-views', { }); api.listen.on('controlBoxInitialized', (view) => { - if (!_converse.allow_muc) { + if (!api.settings.get('allow_muc')) { return; } fetchAndSetMUCDomain(view); diff --git a/src/converse-notification.js b/src/converse-notification.js index f962aa7c1..5787f076a 100644 --- a/src/converse-notification.js +++ b/src/converse-notification.js @@ -11,6 +11,8 @@ import { _converse, api, converse } from "@converse/headless/converse-core"; const { Strophe, sizzle } = converse.env; const u = converse.env.utils; +const supports_html5_notification = "Notification" in window; + converse.plugins.add('converse-notification', { @@ -20,7 +22,6 @@ converse.plugins.add('converse-notification', { /* The initialize function gets called as soon as the plugin is * loaded by converse.js's plugin machinery. */ - _converse.supports_html5_notification = "Notification" in window; api.settings.extend({ notify_all_room_messages: false, @@ -37,7 +38,7 @@ converse.plugins.add('converse-notification', { _converse.shouldNotifyOfGroupMessage = function (message) { /* Is this a group message worthy of notification? */ - let notify_all = _converse.notify_all_room_messages; + let notify_all = api.settings.get('notify_all_room_messages'); const jid = message.getAttribute('from'), resource = Strophe.getResourceFromJid(jid), room_jid = Strophe.getBareJidFromJid(jid), @@ -85,7 +86,7 @@ converse.plugins.add('converse-notification', { return !u.isOnlyChatStateNotification(message) && !u.isOnlyMessageDeliveryReceipt(message) && !is_me && - (_converse.show_desktop_notifications === 'all' || _converse.isMessageToHiddenChat(message)); + (api.settings.get('show_desktop_notifications') === 'all' || _converse.isMessageToHiddenChat(message)); }; @@ -95,13 +96,13 @@ converse.plugins.add('converse-notification', { * @method _converse#playSoundNotification */ _converse.playSoundNotification = function () { - if (_converse.play_sounds && window.Audio !== undefined) { - const audioOgg = new Audio(_converse.sounds_path+"msg_received.ogg"); + if (api.settings.get('play_sounds') && window.Audio !== undefined) { + const audioOgg = new Audio(api.settings.get('sounds_path')+"msg_received.ogg"); const canPlayOgg = audioOgg.canPlayType('audio/ogg'); if (canPlayOgg === 'probably') { return audioOgg.play(); } - const audioMp3 = new Audio(_converse.sounds_path+"msg_received.mp3"); + const audioMp3 = new Audio(api.settings.get('sounds_path')+"msg_received.mp3"); const canPlayMp3 = audioMp3.canPlayType('audio/mp3'); if (canPlayMp3 === 'probably') { audioMp3.play(); @@ -114,9 +115,8 @@ converse.plugins.add('converse-notification', { }; _converse.areDesktopNotificationsEnabled = function () { - return _converse.supports_html5_notification && - - _converse.show_desktop_notifications && + return supports_html5_notification && + api.settings.get('show_desktop_notifications') && Notification.permission === "granted"; }; @@ -171,11 +171,11 @@ converse.plugins.add('converse-notification', { const n = new Notification(title, { 'body': body, 'lang': _converse.locale, - 'icon': _converse.notification_icon, + 'icon': api.settings.get('notification_icon'), 'requireInteraction': !_converse.notification_delay }); - if (_converse.notification_delay) { - setTimeout(n.close.bind(n), _converse.notification_delay); + if (api.settings.get('notification_delay')) { + setTimeout(n.close.bind(n), api.settings.get('notification_delay')); } n.onclick = function (event) { event.preventDefault(); @@ -241,7 +241,7 @@ converse.plugins.add('converse-notification', { * Will show an HTML5 notification to indicate that the chat * status has changed. */ - if (_converse.areDesktopNotificationsEnabled() && _converse.show_chat_state_notifications) { + if (_converse.areDesktopNotificationsEnabled() && api.settings.get('show_chat_state_notifications')) { _converse.showChatStateNotification(contact); } }; @@ -279,7 +279,7 @@ converse.plugins.add('converse-notification', { }; _converse.requestPermission = function () { - if (_converse.supports_html5_notification && !['denied', 'granted'].includes(Notification.permission)) { + if (supports_html5_notification && !['denied', 'granted'].includes(Notification.permission)) { // Ask user to enable HTML5 notifications Notification.requestPermission(); } diff --git a/src/converse-singleton.js b/src/converse-singleton.js index a1ddcb72e..6ce16463e 100644 --- a/src/converse-singleton.js +++ b/src/converse-singleton.js @@ -4,7 +4,7 @@ * @license Mozilla Public License (MPLv2) * @description A plugin which restricts Converse to only one chat. */ -import { converse } from "@converse/headless/converse-core"; +import { api, converse } from "@converse/headless/converse-core"; converse.plugins.add('converse-singleton', { @@ -17,17 +17,17 @@ converse.plugins.add('converse-singleton', { /* The initialize function gets called as soon as the plugin is * loaded by converse.js's plugin machinery. */ - this._converse.api.settings.extend({ + api.settings.extend({ 'allow_logout': false, // No point in logging out when we have auto_login as true. 'allow_muc_invitations': false, // Doesn't make sense to allow because only // roster contacts can be invited 'hide_muc_server': true }); - const { _converse } = this; - if (!Array.isArray(_converse.auto_join_rooms) && !Array.isArray(_converse.auto_join_private_chats)) { + if (!Array.isArray(api.settings.get('auto_join_rooms')) && + !Array.isArray(api.settings.get('auto_join_private_chats'))) { throw new Error("converse-singleton: auto_join_rooms must be an Array"); } - if (_converse.auto_join_rooms.length > 1 || _converse.auto_join_private_chats.length > 1) { + if (api.settings.get('auto_join_rooms').length > 1 || api.settings.get('auto_join_private_chats').length > 1) { throw new Error("It doesn't make sense to have singleton set to true and " + "auto_join_rooms or auto_join_private_chats set to more then one, " + "since only one chat room may be open at any time."); diff --git a/src/headless/converse-chat.js b/src/headless/converse-chat.js index 4341e986b..8f4208832 100644 --- a/src/headless/converse-chat.js +++ b/src/headless/converse-chat.js @@ -180,7 +180,7 @@ converse.plugins.add('converse-chat', { getMessageText () { if (this.get('is_encrypted')) { return this.get('plaintext') || - (_converse.loglevel === 'debug' ? __('Unencryptable OMEMO message') : null); + (api.settings.get('loglevel') === 'debug' ? __('Unencryptable OMEMO message') : null); } return this.get('message'); }, @@ -521,7 +521,7 @@ converse.plugins.add('converse-chat', { } const room_jids = _converse.auto_join_rooms.map(s => isObject(s) ? s.jid : s); const auto_join = api.settings.get('auto_join_private_chats').concat(room_jids); - if (api.settings.get("singleton") && !auto_join.includes(attrs.jid) && !_converse.auto_join_on_invite) { + if (api.settings.get("singleton") && !auto_join.includes(attrs.jid) && !api.settings.get('auto_join_on_invite')) { const msg = `${attrs.jid} is not allowed because singleton is true and it's not being auto_joined`; log.warn(msg); return msg; diff --git a/src/headless/converse-muc.js b/src/headless/converse-muc.js index 872cea990..1e99ec1d3 100644 --- a/src/headless/converse-muc.js +++ b/src/headless/converse-muc.js @@ -197,7 +197,7 @@ converse.plugins.add('converse-muc', { return log.warn(`invalid jid "${jid}" provided in url fragment`); } await api.waitUntil('roomsAutoJoined'); - if (_converse.allow_bookmarks) { + if (api.settings.get('allow_bookmarks')) { await api.waitUntil('bookmarksInitialized'); } api.rooms.open(jid); @@ -483,7 +483,7 @@ converse.plugins.add('converse-muc', { // Looks like we haven't restored occupants from cache, so we clear it. this.occupants.clearStore(); } - if (_converse.clear_messages_on_reconnection) { + if (api.settings.get('clear_messages_on_reconnection')) { await this.clearMessages(); } }, @@ -1049,13 +1049,13 @@ converse.plugins.add('converse-muc', { * @method _converse.ChatRoom#sendChatState */ sendChatState () { - if (!_converse.send_chat_state_notifications || + if (!api.settings.get('send_chat_state_notifications') || !this.get('chat_state') || this.session.get('connection_status') !== converse.ROOMSTATUS.ENTERED || this.features.get('moderated') && this.getOwnRole() === 'visitor') { return; } - const allowed = _converse.send_chat_state_notifications; + const allowed = api.settings.get('send_chat_state_notifications'); if (Array.isArray(allowed) && !allowed.includes(this.get('chat_state'))) { return; } @@ -2739,7 +2739,7 @@ converse.plugins.add('converse-muc', { window.addEventListener(_converse.unloadevent, () => { const using_websocket = api.connection.isType('websocket'); if (using_websocket && - (!_converse.enable_smacks || !_converse.session.get('smacks_stream_id'))) { + (!api.settings.get('enable_smacks') || !_converse.session.get('smacks_stream_id'))) { // For non-SMACKS websocket connections, or non-resumeable // connections, we disconnect all chatrooms when the page unloads. // See issue #1111