Commit 9528d81c authored by JC Brand's avatar JC Brand

Move various MUC methods onto the Backbone.Model

To more cleanly separate views and models and to make MUC in headless
mode more viable.

Refs #1032
parent b0c22d98
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
## UI changes ## UI changes
* The UI is now based on Bootstrap4 and Flexbox is used extensively. - The UI is now based on Bootstrap4 and Flexbox is used extensively.
* #956 Conversation pane should show my own identity in pane header - #956 Conversation pane should show my own identity in pane header
## New Features ## New Features
...@@ -13,15 +13,21 @@ ...@@ -13,15 +13,21 @@
## Configuration changes ## Configuration changes
* Removed the `xhr_custom_status` and `xhr_custom_status_url` configuration - Removed the `xhr_custom_status` and `xhr_custom_status_url` configuration
settings. If you relied on these settings, you can instead listen for the settings. If you relied on these settings, you can instead listen for the
[statusMessageChanged](https://conversejs.org/docs/html/events.html#contactstatusmessagechanged) [statusMessageChanged](https://conversejs.org/docs/html/events.html#contactstatusmessagechanged)
event and make the XMLHttpRequest yourself. event and make the XMLHttpRequest yourself.
* Removed `xhr_user_search` in favor of only accepting `xhr_user_search_url` as configuration option. - Removed `xhr_user_search` in favor of only accepting `xhr_user_search_url` as configuration option.
* The data returned from the `xhr_user_search_url` must now include the user's - The data returned from the `xhr_user_search_url` must now include the user's
`jid` instead of just an `id`. `jid` instead of just an `id`.
- New configuration setting [nickname](https://conversejs.org/docs/html/configurations.html#nickname) - New configuration setting [nickname](https://conversejs.org/docs/html/configurations.html#nickname)
## Architectural changes
- Extracted the views from `converse-muc.js` into `converse-muc-views.js` and
where appropriate moved methods from the views into the models/collections.
This makes MUC possible in headless mode.
### Bugfixes ### Bugfixes
- Spoiler messages didn't include the message author's name. - Spoiler messages didn't include the message author's name.
......
...@@ -7594,6 +7594,9 @@ body.reset { ...@@ -7594,6 +7594,9 @@ body.reset {
#converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li .toolbar-menu ul li.insert-emoji a:hover, #converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li .toolbar-menu ul li.insert-emoji a:hover,
#conversejs .chatbox .sendXMPPMessage .chat-toolbar li .toolbar-menu ul li.insert-emoji a:hover { #conversejs .chatbox .sendXMPPMessage .chat-toolbar li .toolbar-menu ul li.insert-emoji a:hover {
color: #8f2831; } color: #8f2831; }
#converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley a.toggle-smiley,
#conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley a.toggle-smiley {
padding: 0; }
#converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar, #converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar,
#conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar { #conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar {
box-shadow: 0 -1px 1px 0 rgba(0, 0, 0, 0.4); } box-shadow: 0 -1px 1px 0 rgba(0, 0, 0, 0.4); }
......
...@@ -7647,28 +7647,31 @@ body { ...@@ -7647,28 +7647,31 @@ body {
#converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li .toolbar-menu ul li.insert-emoji a:hover, #converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li .toolbar-menu ul li.insert-emoji a:hover,
#conversejs .chatbox .sendXMPPMessage .chat-toolbar li .toolbar-menu ul li.insert-emoji a:hover { #conversejs .chatbox .sendXMPPMessage .chat-toolbar li .toolbar-menu ul li.insert-emoji a:hover {
color: #8f2831; } color: #8f2831; }
#converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar, #converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley,
#conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar { #conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley {
box-shadow: 0 -1px 1px 0 rgba(0, 0, 0, 0.4); } padding: 0 0 0 0.5em; }
#converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker, #converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar,
#conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker { #conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar {
padding-top: 0.5em; } box-shadow: 0 -1px 1px 0 rgba(0, 0, 0, 0.4); }
#converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker ul, #converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker,
#conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker ul { #conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker {
display: flex; padding-top: 0.5em; }
flex-direction: row; #converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker ul,
justify-content: space-between; } #conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker ul {
#converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker li, display: flex;
#converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-skintone-picker li, flex-direction: row;
#conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker li, justify-content: space-between; }
#conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-skintone-picker li { #converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker li,
padding: 0.2em; #converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-skintone-picker li,
font-size: 26px; } #conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker li,
#converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker li:hover, #conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-skintone-picker li {
#converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-skintone-picker li:hover, padding: 0.2em;
#conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker li:hover, font-size: 26px; }
#conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-skintone-picker li:hover { #converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker li:hover,
background-color: #DCF9F6; } #converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-skintone-picker li:hover,
#conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-category-picker li:hover,
#conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-smiley .emoji-toolbar .emoji-skintone-picker li:hover {
background-color: #DCF9F6; }
#converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-otr ul, #converse-embedded-chat .chatbox .sendXMPPMessage .chat-toolbar li.toggle-otr ul,
#conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-otr ul { #conversejs .chatbox .sendXMPPMessage .chat-toolbar li.toggle-otr ul {
z-index: 99; } z-index: 99; }
...@@ -7788,13 +7791,11 @@ body { ...@@ -7788,13 +7791,11 @@ body {
line-height: 26px; } line-height: 26px; }
#conversejs.fullscreen .chatbox .sendXMPPMessage ul { #conversejs.fullscreen .chatbox .sendXMPPMessage ul {
width: 100%; } width: 100%; }
#conversejs.fullscreen .chatbox .sendXMPPMessage .toggle-smiley { #conversejs.fullscreen .chatbox .sendXMPPMessage .toggle-smiley ul.emoji-toolbar .emoji-category-picker {
padding-left: 0.5em; } margin-right: 5em; }
#conversejs.fullscreen .chatbox .sendXMPPMessage .toggle-smiley ul.emoji-toolbar .emoji-category-picker { #conversejs.fullscreen .chatbox .sendXMPPMessage .toggle-smiley ul.emoji-toolbar .emoji-category {
margin-right: 5em; } padding-left: 10px;
#conversejs.fullscreen .chatbox .sendXMPPMessage .toggle-smiley ul.emoji-toolbar .emoji-category { padding-right: 10px; }
padding-left: 10px;
padding-right: 10px; }
@media screen and (max-width: 767px) { @media screen and (max-width: 767px) {
#conversejs.fullscreen .chatbox { #conversejs.fullscreen .chatbox {
......
...@@ -34,8 +34,8 @@ For more info on how to use (or add promises), you can read the ...@@ -34,8 +34,8 @@ For more info on how to use (or add promises), you can read the
Below we will now list all events and also specify whether they are available Below we will now list all events and also specify whether they are available
as promises. as promises.
List of Events (and promises) List of global events (and promises)
----------------------------- ------------------------------------
Hooking into events that Converse.js emits is a great way to extend or Hooking into events that Converse.js emits is a great way to extend or
customize its functionality. customize its functionality.
...@@ -478,3 +478,16 @@ windowStateChanged ...@@ -478,3 +478,16 @@ windowStateChanged
When window state has changed. Used to determine when a user left the page and when came back. When window state has changed. Used to determine when a user left the page and when came back.
``_converse.on('windowStateChanged', function (data) { ... });`` ``_converse.on('windowStateChanged', function (data) { ... });``
List of events on the ChatRoom Backbone.Model
---------------------------------------------
configurationNeeded
~~~~~~~~~~~~~~~~~~~
Triggered when a new room has been created which first needs to be configured
and when `auto_configure` is set to `false`.
Used by the core `ChatRoomView` view in order to know when to render the
configuration form for a new room.
...@@ -433,6 +433,9 @@ ...@@ -433,6 +433,9 @@
} }
} }
&.toggle-smiley { &.toggle-smiley {
a.toggle-smiley {
padding: 0;
}
.emoji-toolbar { .emoji-toolbar {
box-shadow: 0 -1px 1px 0 rgba(0, 0, 0, 0.4); box-shadow: 0 -1px 1px 0 rgba(0, 0, 0, 0.4);
......
...@@ -78,7 +78,6 @@ ...@@ -78,7 +78,6 @@
width: 100%; width: 100%;
} }
.toggle-smiley { .toggle-smiley {
padding-left: 0.5em;
ul { ul {
&.emoji-toolbar { &.emoji-toolbar {
.emoji-category-picker { .emoji-category-picker {
......
This diff is collapsed.
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
</forwarded> </forwarded>
</result> </result>
</message>`).firstElementChild; </message>`).firstElementChild;
chatroomview.onChatRoomMessage(stanza); chatroomview.model.onMessage(stanza);
expect(chatroomview.content.querySelectorAll('.chat-message').length).toBe(1); expect(chatroomview.content.querySelectorAll('.chat-message').length).toBe(1);
done(); done();
}); });
......
...@@ -158,7 +158,7 @@ ...@@ -158,7 +158,7 @@
to: 'dummy@localhost', to: 'dummy@localhost',
type: 'groupchat' type: 'groupchat'
}).c('body').t(message).tree(); }).c('body').t(message).tree();
view.handleMUCMessage(msg); view.model.onMessage(msg);
expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).is(':visible')).toBeTruthy(); expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).is(':visible')).toBeTruthy();
expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).text()).toBe('1'); expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).text()).toBe('1');
......
...@@ -173,7 +173,7 @@ ...@@ -173,7 +173,7 @@
to: 'dummy@localhost', to: 'dummy@localhost',
type: 'groupchat' type: 'groupchat'
}).c('body').t(text); }).c('body').t(text);
view.onChatRoomMessage(message.nodeTree); view.model.onMessage(message.nodeTree);
expect(_converse.playSoundNotification).toHaveBeenCalled(); expect(_converse.playSoundNotification).toHaveBeenCalled();
text = "This message won't play a sound"; text = "This message won't play a sound";
...@@ -183,7 +183,7 @@ ...@@ -183,7 +183,7 @@
to: 'dummy@localhost', to: 'dummy@localhost',
type: 'groupchat' type: 'groupchat'
}).c('body').t(text); }).c('body').t(text);
view.onChatRoomMessage(message.nodeTree); view.model.onMessage(message.nodeTree);
expect(_converse.playSoundNotification, 1); expect(_converse.playSoundNotification, 1);
_converse.play_sounds = false; _converse.play_sounds = false;
...@@ -194,7 +194,7 @@ ...@@ -194,7 +194,7 @@
to: 'dummy@localhost', to: 'dummy@localhost',
type: 'groupchat' type: 'groupchat'
}).c('body').t(text); }).c('body').t(text);
view.onChatRoomMessage(message.nodeTree); view.model.onMessage(message.nodeTree);
expect(_converse.playSoundNotification, 1); expect(_converse.playSoundNotification, 1);
_converse.play_sounds = false; _converse.play_sounds = false;
done(); done();
......
...@@ -97,7 +97,7 @@ ...@@ -97,7 +97,7 @@
view.model.set({'minimized': true}); view.model.set({'minimized': true});
var contact_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost'; var contact_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
var nick = mock.chatroom_names[0]; var nick = mock.chatroom_names[0];
view.handleMUCMessage( view.model.onMessage(
$msg({ $msg({
from: room_jid+'/'+nick, from: room_jid+'/'+nick,
id: (new Date()).getTime(), id: (new Date()).getTime(),
...@@ -112,7 +112,7 @@ ...@@ -112,7 +112,7 @@
expect(_.includes(room_el.classList, 'unread-msgs')); expect(_.includes(room_el.classList, 'unread-msgs'));
// If the user is mentioned, the counter also gets updated // If the user is mentioned, the counter also gets updated
view.handleMUCMessage( view.model.onMessage(
$msg({ $msg({
from: room_jid+'/'+nick, from: room_jid+'/'+nick,
id: (new Date()).getTime(), id: (new Date()).getTime(),
...@@ -123,7 +123,7 @@ ...@@ -123,7 +123,7 @@
var indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator"); var indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
expect(indicator_el.textContent).toBe('1'); expect(indicator_el.textContent).toBe('1');
view.handleMUCMessage( view.model.onMessage(
$msg({ $msg({
from: room_jid+'/'+nick, from: room_jid+'/'+nick,
id: (new Date()).getTime(), id: (new Date()).getTime(),
......
...@@ -416,6 +416,7 @@ ...@@ -416,6 +416,7 @@
'isodate': isodate, 'isodate': isodate,
'data': data 'data': data
})); }));
this.insertDayIndicator(this.content.lastElementChild);
this.scrollDown(); this.scrollDown();
return isodate; return isodate;
}, },
......
...@@ -269,13 +269,16 @@ ...@@ -269,13 +269,16 @@
}, },
}, },
ChatRoomView: { ChatRoom: {
initialize () { onMessage (stanza) {
const { _converse } = this.__super__; /* MAM (message archive management XEP-0313) messages are
this.__super__.initialize.apply(this, arguments); * ignored, since they're handled separately.
this.model.on('change:mam_enabled', this.fetchArchivedMessagesIfNecessary, this); */
this.model.on('change:connection_status', this.fetchArchivedMessagesIfNecessary, this); if (sizzle(`[xmlns="${Strophe.NS.MAM}"]`, stanza).length > 0) {
return true;
}
return this.__super__.onMessage.apply(this, arguments);
}, },
isDuplicate (message, original_stanza) { isDuplicate (message, original_stanza) {
...@@ -285,8 +288,18 @@ ...@@ -285,8 +288,18 @@
} }
const archive_id = getMessageArchiveID(original_stanza); const archive_id = getMessageArchiveID(original_stanza);
if (archive_id) { if (archive_id) {
return this.model.messages.filter({'archive_id': archive_id}).length > 0; return this.messages.filter({'archive_id': archive_id}).length > 0;
} }
}
},
ChatRoomView: {
initialize () {
const { _converse } = this.__super__;
this.__super__.initialize.apply(this, arguments);
this.model.on('change:mam_enabled', this.fetchArchivedMessagesIfNecessary, this);
this.model.on('change:connection_status', this.fetchArchivedMessagesIfNecessary, this);
}, },
renderChatArea () { renderChatArea () {
...@@ -297,16 +310,6 @@ ...@@ -297,16 +310,6 @@
return result; return result;
}, },
handleMUCMessage (stanza) {
/* MAM (message archive management XEP-0313) messages are
* ignored, since they're handled separately.
*/
if (sizzle(`[xmlns="${Strophe.NS.MAM}"]`, stanza).length > 0) {
return true;
}
return this.__super__.handleMUCMessage.apply(this, arguments);
},
fetchArchivedMessagesIfNecessary () { fetchArchivedMessagesIfNecessary () {
if (this.model.get('connection_status') !== converse.ROOMSTATUS.ENTERED || if (this.model.get('connection_status') !== converse.ROOMSTATUS.ENTERED ||
!this.model.get('mam_enabled') || !this.model.get('mam_enabled') ||
...@@ -321,7 +324,7 @@ ...@@ -321,7 +324,7 @@
fetchArchivedMessages (options) { fetchArchivedMessages (options) {
/* Fetch archived chat messages for this Chat Room /* Fetch archived chat messages for this Chat Room
* *
* Then, upon receiving them, call onChatRoomMessage * Then, upon receiving them, call onMessage
* so that they are displayed inside it. * so that they are displayed inside it.
*/ */
const that = this; const that = this;
...@@ -337,7 +340,7 @@ ...@@ -337,7 +340,7 @@
function (messages) { function (messages) {
that.clearSpinner(); that.clearSpinner();
if (messages.length) { if (messages.length) {
_.each(messages, that.onChatRoomMessage.bind(that)); _.each(messages, that.model.onMessage.bind(that));
} }
}, },
function () { function () {
...@@ -363,7 +366,6 @@ ...@@ -363,7 +366,6 @@
message_archiving_timeout: 8000, // Time (in milliseconds) to wait before aborting MAM request message_archiving_timeout: 8000, // Time (in milliseconds) to wait before aborting MAM request
}); });
_converse.onMAMError = function (iq) { _converse.onMAMError = function (iq) {
if (iq.querySelectorAll('feature-not-implemented').length) { if (iq.querySelectorAll('feature-not-implemented').length) {
_converse.log( _converse.log(
......
This diff is collapsed.
This diff is collapsed.
...@@ -213,7 +213,8 @@ ...@@ -213,7 +213,8 @@
const name = ev.target.getAttribute('data-room-name'); const name = ev.target.getAttribute('data-room-name');
const jid = ev.target.getAttribute('data-room-jid'); const jid = ev.target.getAttribute('data-room-jid');
if (confirm(__("Are you sure you want to leave the room %1$s?", name))) { if (confirm(__("Are you sure you want to leave the room %1$s?", name))) {
_converse.chatboxviews.get(jid).leave(); // TODO: replace with API call
_converse.chatboxviews.get(jid).close();
} }
}, },
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment