Refactoring

- Move headless one-on-one chat functionality into converse-chat
- Split converse-headline into converse-headlines and converse-headlines-views
- Add api in `_converse.api.chatboxes` for creating chatboxes
- Add `_converse.api.controlbox.get` method
This commit is contained in:
JC Brand 2019-11-01 16:04:55 +01:00
parent 93d56898b7
commit 879e165ae5
30 changed files with 1861 additions and 1743 deletions

View File

@ -14,6 +14,7 @@
instances. Still working out a wire protocol for compatibility with other clients.
To add custom emojis, edit the `emojis.json` file.
- Refactor some presence and status handling code from `converse-core` into `@converse/headless/converse-status`.
- New API [\_converse.api.headlines](https://conversejs.org/docs/html/api/-_converse.api.headlines.html#.get)
### Breaking changes
@ -34,10 +35,12 @@
* `_converse.api.rooms.create`
* `_converse.api.roomviews.close`
- `_converse.api.chats.get()` now only returns one-on-one chats, not the control box or headline notifications.
- The `show_only_online_users` setting has been removed.
- The order of certain events have now changed: `statusInitialized` is now triggered after `initialized` and `connected` and `reconnected`.
- `_converse.api.alert.show` is now `_converse.api.show` and instead of taking
an integer for the `type`, "info", "warn" or "error" should be passed in.
- The `converse-headline` plugin has been split up into `converse-headlines` and `converse-headlines-view`.
## 5.0.4 (2019-10-08)
- New config option [allow_message_corrections](https://conversejs.org/docs/html/configuration.html#allow-message-corrections)

View File

@ -168,7 +168,7 @@
'name': 'The Play',
'nick': ' Othello'
});
await u.waitUntil(() => _converse.api.rooms.get().length);
await new Promise(resolve => _converse.api.listen.once('chatBoxInitialized', resolve));
expect(_.isUndefined(_converse.chatboxviews.get(jid))).toBeFalsy();
// Check that we don't auto-join if muc_respect_autojoin is false

View File

@ -38,7 +38,7 @@
type: 'chat',
id: (new Date()).getTime()
}).c('body').t('hello world').tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await u.waitUntil(() => view.content.querySelectorAll('.chat-msg').length);
expect(view.content.lastElementChild.textContent.trim().indexOf('hello world')).not.toBe(-1);
done();
@ -61,7 +61,7 @@
}).c('body').t(message).up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
const view = _converse.chatboxviews.get(sender_jid);
await new Promise(resolve => view.once('messageInserted', resolve));
expect(view.el.querySelectorAll('.chat-msg--action').length).toBe(1);
@ -135,7 +135,7 @@
const message_promise = new Promise(resolve => _converse.api.listen.on('message', resolve));
_converse.connection._dataRecv(test_utils.createRequest(stanza));
await u.waitUntil(() => _converse.api.chats.get().length === 2);
await new Promise(resolve => _converse.api.listen.once('chatBoxInitialized', resolve));
await u.waitUntil(() => message_promise);
expect(_converse.chatboxviews.keys().length).toBe(2);
done();
@ -190,7 +190,7 @@
el.click();
}
await u.waitUntil(() => _converse.chatboxes.length == 16);
expect(_converse.chatboxviews.trimChats.calls.count()).toBe(16);
expect(_converse.chatboxviews.trimChats.calls.count()).toBe(17);
_converse.api.chatviews.get().forEach(v => spyOn(v, 'onMinimized').and.callThrough());
for (i=0; i<online_contacts.length; i++) {
@ -212,7 +212,7 @@
expect(trimmedview.restore).toHaveBeenCalled();
expect(chatbox.maximize).toHaveBeenCalled();
expect(_converse.chatboxviews.trimChats.calls.count()).toBe(17);
expect(_converse.chatboxviews.trimChats.calls.count()).toBe(18);
done();
}));
@ -520,58 +520,28 @@
describe("A Chat Status Notification", function () {
it("is ignored when it's a carbon copy of one of my own",
mock.initConverse(
['rosterGroupsFetched'], {},
async function (done, _converse) {
await test_utils.waitForRoster(_converse, 'current');
await test_utils.openControlBox(_converse);
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
await test_utils.openChatBoxFor(_converse, sender_jid);
let stanza = u.toStanza(
`<message from="${sender_jid}"
type="chat"
to="romeo@montague.lit/orchard">
<composing xmlns="http://jabber.org/protocol/chatstates"/>
<no-store xmlns="urn:xmpp:hints"/>
<no-permanent-store xmlns="urn:xmpp:hints"/>
</message>`);
_converse.connection._dataRecv(test_utils.createRequest(stanza));
stanza = u.toStanza(
`<message from="${sender_jid}"
type="chat"
to="romeo@montague.lit/orchard">
<paused xmlns="http://jabber.org/protocol/chatstates"/>
<no-store xmlns="urn:xmpp:hints"/>
<no-permanent-store xmlns="urn:xmpp:hints"/>
</message>`);
_converse.connection._dataRecv(test_utils.createRequest(stanza));
done();
}));
it("does not open a new chatbox",
mock.initConverse(
['rosterGroupsFetched'], {},
['rosterGroupsFetched', 'emojisInitialized'], {},
async function (done, _converse) {
await test_utils.waitForRoster(_converse, 'current');
await test_utils.openControlBox(_converse);
spyOn(_converse.api, "trigger").and.callThrough();
const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@montague.lit';
// <composing> state
const msg = $msg({
const stanza = $msg({
'from': sender_jid,
'to': _converse.connection.jid,
'type': 'chat',
'id': (new Date()).getTime()
}).c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
await _converse.chatboxes.onMessage(msg);
spyOn(_converse.api, "trigger").and.callThrough();
_converse.connection._dataRecv(test_utils.createRequest(stanza));
await u.waitUntil(() => _converse.api.trigger.calls.count());
expect(_converse.api.trigger).toHaveBeenCalledWith('message', jasmine.any(Object));
expect(_converse.api.chats.get().length).toBe(1);
expect(_converse.chatboxviews.keys().length).toBe(1);
done();
}));
@ -705,7 +675,6 @@
await test_utils.openControlBox(_converse);
// See XEP-0085 https://xmpp.org/extensions/xep-0085.html#definitions
spyOn(_converse.api, "trigger").and.callThrough();
const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@montague.lit';
await u.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length);
await test_utils.openChatBoxFor(_converse, sender_jid);
@ -716,10 +685,13 @@
to: _converse.connection.jid,
type: 'chat',
id: (new Date()).getTime()
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
await _converse.chatboxes.onMessage(msg);
}).c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
spyOn(_converse.api, "trigger").and.callThrough();
_converse.connection._dataRecv(test_utils.createRequest(msg));
await u.waitUntil(() => _converse.api.trigger.calls.count());
expect(_converse.api.trigger).toHaveBeenCalledWith('message', jasmine.any(Object));
var view = _converse.chatboxviews.get(sender_jid);
const view = _converse.chatboxviews.get(sender_jid);
expect(view).toBeDefined();
const event = await u.waitUntil(() => view.el.querySelector('.chat-state-notification'));
@ -732,7 +704,7 @@
type: 'chat',
id: (new Date()).getTime()
}).c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
const events = view.el.querySelectorAll('.chat-state-notification');
expect(events.length).toBe(1);
expect(events[0].textContent).toEqual(mock.cur_names[1] + ' is typing');
@ -765,7 +737,7 @@
'to': recipient_jid,
'type': 'chat'
}).c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await u.waitUntil(() => view.model.messages.length);
// Check that the chatbox and its view now exist
const chatbox = _converse.chatboxes.get(recipient_jid);
@ -859,7 +831,7 @@
type: 'chat',
id: (new Date()).getTime()
}).c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
expect(_converse.api.trigger).toHaveBeenCalledWith('message', jasmine.any(Object));
await u.waitUntil(() => view.model.vcard.get('fullname') === mock.cur_names[1])
const event = await u.waitUntil(() => view.el.querySelector('.chat-state-notification'));
@ -893,7 +865,7 @@
'to': recipient_jid,
'type': 'chat'
}).c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await u.waitUntil(() => view.model.messages.length);
// Check that the chatbox and its view now exist
const chatbox = _converse.chatboxes.get(recipient_jid);
@ -1042,7 +1014,8 @@
'type': 'chat'})
.c('composing', {'xmlns': Strophe.NS.CHATSTATES}).up()
.tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await u.waitUntil(() => view.model.messages.length);
await u.waitUntil(() => view.el.querySelector('.chat-state-notification'));
expect(view.el.querySelectorAll('.chat-state-notification').length).toBe(1);
msg = $msg({
@ -1050,8 +1023,8 @@
to: _converse.connection.jid,
type: 'chat',
id: (new Date()).getTime()
}).c('body').c('inactive', {'xmlns': Strophe.NS.CHATSTATES}).tree();
await _converse.chatboxes.onMessage(msg);
}).c('inactive', {'xmlns': Strophe.NS.CHATSTATES}).tree();
await _converse.handleMessageStanza(msg);
await u.waitUntil(() => (view.model.messages.length > 1));
expect(_converse.api.trigger).toHaveBeenCalledWith('message', jasmine.any(Object));
await u.waitUntil(() => view.el.querySelectorAll('.chat-state-notification').length === 0);
@ -1078,7 +1051,7 @@
type: 'chat',
id: (new Date()).getTime()
}).c('body').c('gone', {'xmlns': Strophe.NS.CHATSTATES}).tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
expect(_converse.api.trigger).toHaveBeenCalledWith('message', jasmine.any(Object));
const view = _converse.chatboxviews.get(sender_jid);
await u.waitUntil(() => view.model.vcard.get('fullname') === mock.cur_names[1]);
@ -1165,7 +1138,7 @@
spyOn(_converse, 'incrementMsgCounter').and.callThrough();
spyOn(_converse, 'clearMsgCounter').and.callThrough();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.once('messageInserted', resolve));
expect(_converse.incrementMsgCounter).toHaveBeenCalled();
expect(_converse.clearMsgCounter).not.toHaveBeenCalled();
@ -1210,7 +1183,7 @@
id: (new Date()).getTime()
}).c('body').t(message).up()
.c('active', {'xmlns': Strophe.NS.CHATSTATES}).tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
expect(_converse.incrementMsgCounter).not.toHaveBeenCalled();
expect(document.title).toBe('Converse Tests');
done();
@ -1240,14 +1213,13 @@
// leave converse-chat page
_converse.windowState = 'hidden';
_converse.chatboxes.onMessage(msgFactory());
await u.waitUntil(() => _converse.api.chats.get().length === 2)
await _converse.handleMessageStanza(msgFactory());
let view = _converse.chatboxviews.get(sender_jid);
expect(document.title).toBe('Messages (1) Converse Tests');
// come back to converse-chat page
_converse.saveWindowState(null, 'focus');
expect(u.isVisible(view.el)).toBeTruthy();
await u.waitUntil(() => u.isVisible(view.el));
expect(document.title).toBe('Converse Tests');
// close chatbox and leave converse-chat page again
@ -1255,8 +1227,7 @@
_converse.windowState = 'hidden';
// check that msg_counter is incremented from zero again
_converse.chatboxes.onMessage(msgFactory());
await u.waitUntil(() => _converse.api.chats.get().length === 2)
await _converse.handleMessageStanza(msgFactory());
view = _converse.chatboxviews.get(sender_jid);
expect(u.isVisible(view.el)).toBeTruthy();
expect(document.title).toBe('Messages (1) Converse Tests');
@ -1277,7 +1248,7 @@
const view = await test_utils.openChatBoxFor(_converse, sender_jid)
view.model.save('scrolled', true);
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await u.waitUntil(() => view.model.messages.length);
expect(view.model.get('num_unread')).toBe(1);
done();
@ -1295,7 +1266,7 @@
await test_utils.openChatBoxFor(_converse, sender_jid);
const chatbox = _converse.chatboxes.get(sender_jid);
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
expect(chatbox.get('num_unread')).toBe(0);
done();
}));
@ -1313,7 +1284,7 @@
await test_utils.openChatBoxFor(_converse, sender_jid);
const chatbox = _converse.chatboxes.get(sender_jid);
_converse.windowState = 'hidden';
_converse.chatboxes.onMessage(msgFactory());
_converse.handleMessageStanza(msgFactory());
await u.waitUntil(() => chatbox.messages.length);
expect(chatbox.get('num_unread')).toBe(1);
done();
@ -1331,7 +1302,7 @@
const chatbox = _converse.chatboxes.get(sender_jid);
chatbox.save('scrolled', true);
_converse.windowState = 'hidden';
_converse.chatboxes.onMessage(msgFactory());
_converse.handleMessageStanza(msgFactory());
await u.waitUntil(() => chatbox.messages.length);
expect(chatbox.get('num_unread')).toBe(1);
done();
@ -1348,7 +1319,7 @@
await test_utils.openChatBoxFor(_converse, sender_jid);
const chatbox = _converse.chatboxes.get(sender_jid);
_converse.windowState = 'hidden';
_converse.chatboxes.onMessage(msgFactory());
_converse.handleMessageStanza(msgFactory());
await u.waitUntil(() => chatbox.messages.length);
expect(chatbox.get('num_unread')).toBe(1);
_converse.saveWindowState(null, 'focus');
@ -1368,7 +1339,7 @@
const chatbox = _converse.chatboxes.get(sender_jid);
chatbox.save('scrolled', true);
_converse.windowState = 'hidden';
_converse.chatboxes.onMessage(msgFactory());
_converse.handleMessageStanza(msgFactory());
await u.waitUntil(() => chatbox.messages.length);
expect(chatbox.get('num_unread')).toBe(1);
_converse.saveWindowState(null, 'focus');
@ -1392,13 +1363,13 @@
const chatbox = _converse.chatboxes.get(sender_jid);
chatbox.save('scrolled', true);
msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await u.waitUntil(() => chatbox.messages.length);
const selector = 'a.open-chat:contains("' + chatbox.get('nickname') + '") .msgs-indicator';
indicator_el = sizzle(selector, _converse.rosterview.el).pop();
expect(indicator_el.textContent).toBe('1');
msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too');
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await u.waitUntil(() => chatbox.messages.length > 1);
indicator_el = sizzle(selector, _converse.rosterview.el).pop();
expect(indicator_el.textContent).toBe('2');
@ -1421,14 +1392,14 @@
chatboxview.minimize();
msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await u.waitUntil(() => chatbox.messages.length);
const selector = 'a.open-chat:contains("' + chatbox.get('nickname') + '") .msgs-indicator';
indicator_el = sizzle(selector, _converse.rosterview.el).pop();
expect(indicator_el.textContent).toBe('1');
msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too');
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await u.waitUntil(() => chatbox.messages.length === 2);
indicator_el = sizzle(selector, _converse.rosterview.el).pop();
expect(indicator_el.textContent).toBe('2');
@ -1450,10 +1421,10 @@
const selector = 'a.open-chat:contains("' + chatbox.get('nickname') + '") .msgs-indicator';
const select_msgs_indicator = () => sizzle(selector, _converse.rosterview.el).pop();
view.minimize();
_converse.chatboxes.onMessage(msgFactory());
_converse.handleMessageStanza(msgFactory());
await u.waitUntil(() => chatbox.messages.length);
expect(select_msgs_indicator().textContent).toBe('1');
_converse.chatboxes.onMessage(msgFactory());
_converse.handleMessageStanza(msgFactory());
await u.waitUntil(() => chatbox.messages.length > 1);
expect(select_msgs_indicator().textContent).toBe('2');
view.model.maximize();
@ -1476,7 +1447,7 @@
const selector = `a.open-chat:contains("${chatbox.get('nickname')}") .msgs-indicator`;
const select_msgs_indicator = () => sizzle(selector, _converse.rosterview.el).pop();
chatbox.save('scrolled', true);
_converse.chatboxes.onMessage(msgFactory());
_converse.handleMessageStanza(msgFactory());
const view = _converse.chatboxviews.get(sender_jid);
await u.waitUntil(() => view.model.messages.length);
expect(select_msgs_indicator().textContent).toBe('1');
@ -1502,7 +1473,7 @@
const selector = 'a.open-chat:contains("' + chatbox.get('nickname') + '") .msgs-indicator';
const select_msgs_indicator = () => sizzle(selector, _converse.rosterview.el).pop();
chatbox.save('scrolled', true);
_converse.chatboxes.onMessage(msgFactory());
_converse.handleMessageStanza(msgFactory());
await u.waitUntil(() => view.model.messages.length);
expect(select_msgs_indicator().textContent).toBe('1');
await test_utils.openChatBoxFor(_converse, sender_jid);
@ -1530,7 +1501,7 @@
};
const chatbox = _converse.chatboxes.get(sender_jid);
chatbox.save('scrolled', true);
_converse.chatboxes.onMessage(msgFactory());
_converse.handleMessageStanza(msgFactory());
await u.waitUntil(() => chatbox.messages.length);
const chatboxview = _converse.chatboxviews.get(sender_jid);
chatboxview.minimize();
@ -1558,7 +1529,7 @@
return minimizedChatBoxView.el.querySelector('.message-count');
};
view.minimize();
_converse.chatboxes.onMessage(msgFactory());
_converse.handleMessageStanza(msgFactory());
await u.waitUntil(() => view.model.messages.length);
const unread_count = selectUnreadMsgCount();
expect(u.isVisible(unread_count)).toBeTruthy();

View File

@ -88,7 +88,7 @@
id: (new Date()).getTime()
}).c('body').t('hello').up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
_converse.chatboxes.onMessage(msg);
_converse.handleMessageStanza(msg);
await u.waitUntil(() => _converse.rosterview.el.querySelectorAll(".msgs-indicator").length);
spyOn(chatview.model, 'incrementUnreadMsgCounter').and.callThrough();
expect(_converse.chatboxviews.el.querySelector('.restore-chat .message-count').textContent).toBe('1');
@ -101,7 +101,7 @@
id: (new Date()).getTime()
}).c('body').t('hello again').up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
_converse.chatboxes.onMessage(msg);
_converse.handleMessageStanza(msg);
await u.waitUntil(() => chatview.model.incrementUnreadMsgCounter.calls.count());
expect(_converse.chatboxviews.el.querySelector('.restore-chat .message-count').textContent).toBe('2');
expect(_converse.rosterview.el.querySelector('.msgs-indicator').textContent).toBe('2');

View File

@ -269,7 +269,7 @@
// Test on chat that's not open
chat = await _converse.api.chats.get(jid);
expect(typeof chat === 'undefined').toBeTruthy();
expect(chat === null).toBeTruthy();
expect(_converse.chatboxes.length).toBe(1);
// Test for one JID
@ -281,7 +281,7 @@
await u.waitUntil(() => u.isVisible(view.el));
// Test for multiple JIDs
test_utils.openChatBoxFor(_converse, jid2);
await u.waitUntil(() => _converse.chatboxes.length == 2);
await u.waitUntil(() => _converse.chatboxes.length == 3);
const list = await _converse.api.chats.get([jid, jid2]);
expect(Array.isArray(list)).toBeTruthy();
expect(list[0].get('box_id')).toBe(`box-${btoa(jid)}`);

View File

@ -163,7 +163,7 @@
await test_utils.waitForRoster(_converse, 'current');
const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@montague.lit';
_converse.chatboxes.onMessage($msg({
_converse.handleMessageStanza($msg({
'from': sender_jid,
'to': _converse.connection.jid,
'type': 'chat',
@ -177,7 +177,7 @@
let message = chat_content.querySelector('.chat-msg__text');
expect(u.hasClass('chat-msg__text--larger', message)).toBe(true);
_converse.chatboxes.onMessage($msg({
_converse.handleMessageStanza($msg({
'from': sender_jid,
'to': _converse.connection.jid,
'type': 'chat',

View File

@ -14,7 +14,7 @@
it("will not open nor display non-headline messages",
mock.initConverse(
['rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) {
['rosterGroupsFetched', 'chatBoxesFetched'], {}, function (done, _converse) {
/* XMPP spam message:
*
@ -36,9 +36,9 @@
.c('nick', {'xmlns': "http://jabber.org/protocol/nick"}).t("-wwdmz").up()
.c('body').t('SORRY FOR THIS ADVERT');
_converse.connection._dataRecv(test_utils.createRequest(stanza));
await u.waitUntil(() => _converse.api.chats.get().length);
expect(u.isHeadlineMessage.called).toBeTruthy();
expect(u.isHeadlineMessage.returned(false)).toBeTruthy();
expect(_converse.api.headlines.get().length === 0);
u.isHeadlineMessage.restore();
done();
}));

View File

@ -205,7 +205,7 @@
describe("An archived message", function () {
describe("when recieved", function () {
describe("when received", function () {
it("is discarded if it doesn't come from the right sender",
mock.initConverse(

View File

@ -21,7 +21,8 @@
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
const forwarded_contact_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@montague.lit';
await test_utils.openChatBoxFor(_converse, contact_jid);
expect(_converse.api.chats.get().length).toBe(2);
let models = await _converse.api.chats.get();
expect(models.length).toBe(1);
const received_stanza = u.toStanza(`
<message to='${_converse.jid}' from='${contact_jid}' type='chat' id='${_converse.connection.getUniqueId()}'>
<body>A most courteous exposition!</body>
@ -51,7 +52,8 @@
'Forwarded messages not part of an encapsulating protocol are not supported</text>'+
'</error>'+
'</message>');
expect(_converse.api.chats.get().length).toBe(2);
models = await _converse.api.chats.get();
expect(models.length).toBe(1);
done();
}));
@ -148,7 +150,7 @@
await u.waitUntil(() => (u.hasClass('correcting', view.el.querySelector('.chat-msg')) === false), 500);
// Test that messages from other users don't have the pencil icon
_converse.chatboxes.onMessage(
_converse.handleMessageStanza(
$msg({
'from': contact_jid,
'to': _converse.connection.jid,
@ -352,7 +354,6 @@
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
await u.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length)
spyOn(_converse.chatboxes, 'getChatBox').and.callThrough();
_converse.filter_by_resource = true;
let msg = $msg({
@ -364,8 +365,7 @@
.c('body').t("message").up()
.c('delay', {'xmlns': 'urn:xmpp:delay', 'stamp':'2018-01-02T13:08:25Z'})
.tree();
await _converse.chatboxes.onMessage(msg);
await u.waitUntil(() => _converse.api.chats.get().length);
await _converse.handleMessageStanza(msg);
const view = _converse.api.chatviews.get(sender_jid);
msg = $msg({
@ -377,7 +377,7 @@
.c('body').t("Older message").up()
.c('delay', {'xmlns': 'urn:xmpp:delay', 'stamp':'2017-12-31T22:08:25Z'})
.tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.once('messageInserted', resolve));
msg = $msg({
@ -389,7 +389,7 @@
.c('body').t("Inbetween message").up()
.c('delay', {'xmlns': 'urn:xmpp:delay', 'stamp':'2018-01-01T13:18:23Z'})
.tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.once('messageInserted', resolve));
msg = $msg({
@ -401,7 +401,7 @@
.c('body').t("another inbetween message").up()
.c('delay', {'xmlns': 'urn:xmpp:delay', 'stamp':'2018-01-01T13:18:23Z'})
.tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.once('messageInserted', resolve));
msg = $msg({
@ -413,7 +413,7 @@
.c('body').t("An earlier message on the next day").up()
.c('delay', {'xmlns': 'urn:xmpp:delay', 'stamp':'2018-01-02T12:18:23Z'})
.tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.once('messageInserted', resolve));
msg = $msg({
@ -425,7 +425,7 @@
.c('body').t("newer message from the next day").up()
.c('delay', {'xmlns': 'urn:xmpp:delay', 'stamp':'2018-01-02T22:28:23Z'})
.tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.once('messageInserted', resolve));
// Insert <composing> message, to also check that
@ -439,7 +439,7 @@
'type': 'chat'})
.c('composing', {'xmlns': Strophe.NS.CHATSTATES}).up()
.tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
msg = $msg({
'id': _converse.connection.getUniqueId(),
@ -450,7 +450,7 @@
.c('composing', {'xmlns': Strophe.NS.CHATSTATES}).up()
.c('body').t("latest message")
.tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.once('messageInserted', resolve));
const chat_content = view.el.querySelector('.chat-content');
@ -519,7 +519,7 @@
// Ideally we wouldn't have to filter out headline
// messages, but Prosody gives them the wrong 'type' :(
sinon.spy(_converse, 'log');
sinon.spy(_converse.chatboxes, 'getChatBox');
sinon.spy(_converse.api.chatboxes, 'get');
sinon.spy(u, 'isHeadlineMessage');
const msg = $msg({
from: 'montague.lit',
@ -527,17 +527,17 @@
type: 'chat',
id: (new Date()).getTime()
}).c('body').t("This headline message will not be shown").tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
expect(_converse.log.calledWith(
"onMessage: Ignoring incoming headline message from JID: montague.lit",
Strophe.LogLevel.INFO
)).toBeTruthy();
expect(u.isHeadlineMessage.called).toBeTruthy();
expect(u.isHeadlineMessage.returned(true)).toBeTruthy();
expect(_converse.chatboxes.getChatBox.called).toBeFalsy();
expect(_converse.api.chatboxes.get.called).toBeFalsy();
// Remove sinon spies
_converse.log.restore();
_converse.chatboxes.getChatBox.restore();
_converse.api.chatboxes.get.restore();
u.isHeadlineMessage.restore();
done();
}));
@ -570,8 +570,7 @@
'type': 'chat'
}).c('body').t(msgtext).tree();
await _converse.chatboxes.onMessage(msg);
await u.waitUntil(() => (_converse.api.chats.get().length > 1))
await _converse.handleMessageStanza(msg);
const chatbox = _converse.chatboxes.get(sender_jid);
const view = _converse.chatboxviews.get(sender_jid);
@ -622,7 +621,7 @@
'type': 'chat'
}).c('body').t(msgtext).tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
// Check that the chatbox and its view now exist
const chatbox = await _converse.api.chats.get(recipient_jid);
const view = _converse.api.chatviews.get(recipient_jid);
@ -677,15 +676,15 @@
'to': _converse.connection.jid,
'type': 'chat'
}).c('body').t(msgtext).tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
// Check that chatbox for impersonated user is not created.
let chatbox = await _converse.api.chats.get(impersonated_jid);
expect(chatbox).not.toBeDefined();
expect(chatbox).toBe(null);
// Check that the chatbox for the malicous user is not created
chatbox = await _converse.api.chats.get(sender_jid);
expect(chatbox).not.toBeDefined();
expect(chatbox).toBe(null);
done();
}));
@ -719,7 +718,7 @@
id: (new Date()).getTime()
}).c('body').t(message).up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await u.waitUntil(() => chatview.model.messages.length);
expect(_converse.api.trigger).toHaveBeenCalledWith('message', jasmine.any(Object));
@ -730,7 +729,7 @@
expect(trimmedview.model.get('minimized')).toBeTruthy();
expect(u.isVisible(count)).toBeTruthy();
expect(count.textContent).toBe('1');
_converse.chatboxes.onMessage(
_converse.handleMessageStanza(
$msg({
from: mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit',
to: _converse.connection.jid,
@ -779,7 +778,7 @@
}).c('body').t(message).up()
.c('delay', { xmlns:'urn:xmpp:delay', from: 'montague.lit', stamp: one_day_ago.toISOString() })
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.once('messageInserted', resolve));
expect(_converse.api.trigger).toHaveBeenCalledWith('message', jasmine.any(Object));
@ -812,7 +811,7 @@
id: new Date().getTime()
}).c('body').t(message).up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await new Promise(resolve => view.once('messageInserted', resolve));
expect(_converse.api.trigger).toHaveBeenCalledWith('message', jasmine.any(Object));
@ -1077,7 +1076,7 @@
jasmine.clock().install();
jasmine.clock().mockDate(base_time);
_converse.chatboxes.onMessage($msg({
_converse.handleMessageStanza($msg({
'from': sender_jid,
'to': _converse.connection.jid,
'type': 'chat',
@ -1089,7 +1088,7 @@
await new Promise(resolve => view.once('messageInserted', resolve));
jasmine.clock().tick(3*ONE_MINUTE_LATER);
_converse.chatboxes.onMessage($msg({
_converse.handleMessageStanza($msg({
'from': sender_jid,
'to': _converse.connection.jid,
'type': 'chat',
@ -1099,7 +1098,7 @@
await new Promise(resolve => view.once('messageInserted', resolve));
jasmine.clock().tick(11*ONE_MINUTE_LATER);
_converse.chatboxes.onMessage($msg({
_converse.handleMessageStanza($msg({
'from': sender_jid,
'to': _converse.connection.jid,
'type': 'chat',
@ -1112,7 +1111,7 @@
// Insert <composing> message, to also check that
// text messages are inserted correctly with
// temporary chat events in the chat contents.
_converse.chatboxes.onMessage($msg({
_converse.handleMessageStanza($msg({
'id': 'aeb219',
'to': _converse.bare_jid,
'xmlns': 'jabber:client',
@ -1123,7 +1122,7 @@
await new Promise(resolve => view.once('messageInserted', resolve));
jasmine.clock().tick(1*ONE_MINUTE_LATER);
_converse.chatboxes.onMessage($msg({
_converse.handleMessageStanza($msg({
'from': sender_jid,
'to': _converse.connection.jid,
'type': 'chat',
@ -1154,7 +1153,7 @@
"Another message within 10 minutes, but from a different person");
// Let's add a delayed, inbetween message
_converse.chatboxes.onMessage(
_converse.handleMessageStanza(
$msg({
'xmlns': 'jabber:client',
'id': _converse.connection.getUniqueId(),
@ -1184,7 +1183,7 @@
"Another message 1 minute and 1 second since the previous one");
expect(u.hasClass('chat-msg--followup', chat_content.querySelector('.message:nth-child(7)'))).toBe(false);
_converse.chatboxes.onMessage(
_converse.handleMessageStanza(
$msg({
'xmlns': 'jabber:client',
'id': _converse.connection.getUniqueId(),
@ -1242,7 +1241,7 @@
'id': msg_id,
}).c('body').t('Message!').up()
.c('request', {'xmlns': Strophe.NS.RECEIPTS}).tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
const sent_messages = sent_stanzas.map(s => _.isElement(s) ? s : s.nodeTree).filter(s => s.nodeName === 'message');
expect(sent_messages.length).toBe(1);
const receipt = sizzle(`received[xmlns="${Strophe.NS.RECEIPTS}"]`, sent_messages[0]).pop();
@ -1274,8 +1273,7 @@
'id': msg_id
}).c('body').t('Message!').up()
.c('request', {'xmlns': Strophe.NS.RECEIPTS}).tree();
await _converse.chatboxes.onMessage(msg);
await u.waitUntil(() => _converse.api.chats.get().length);
await _converse.handleMessageStanza(msg);
expect(view.model.sendReceiptStanza).not.toHaveBeenCalled();
done();
}));
@ -1298,7 +1296,6 @@
preventDefault: function preventDefault () {},
keyCode: 13 // Enter
});
await u.waitUntil(() => _converse.api.chats.get().length);
const chatbox = _converse.chatboxes.get(contact_jid);
expect(chatbox).toBeDefined();
await new Promise(resolve => view.once('messageInserted', resolve));
@ -1314,7 +1311,7 @@
expect(view.el.querySelectorAll('.chat-msg__receipt').length).toBe(1);
// Also handle receipts with type 'chat'. See #1353
spyOn(_converse.chatboxes, 'onMessage').and.callThrough();
spyOn(_converse, 'handleMessageStanza').and.callThrough();
textarea.value = 'Another message';
view.onKeyDown({
target: textarea,
@ -1334,7 +1331,7 @@
_converse.connection._dataRecv(test_utils.createRequest(msg));
await new Promise(resolve => view.model.messages.once('rendered', resolve));
expect(view.el.querySelectorAll('.chat-msg__receipt').length).toBe(2);
expect(_converse.chatboxes.onMessage.calls.count()).toBe(1);
expect(_converse.handleMessageStanza.calls.count()).toBe(1);
done();
}));
@ -1396,7 +1393,7 @@
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
// We don't already have an open chatbox for this user
expect(_converse.chatboxes.get(sender_jid)).not.toBeDefined();
_converse.chatboxes.onMessage(
await _converse.handleMessageStanza(
$msg({
'from': sender_jid,
'to': _converse.connection.jid,
@ -1405,8 +1402,7 @@
}).c('body').t(message).up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree()
);
await u.waitUntil(() => (_converse.api.chats.get().length === 2));
const chatbox = _converse.chatboxes.get(sender_jid);
const chatbox = await _converse.chatboxes.get(sender_jid);
expect(chatbox).toBeDefined();
const view = _converse.api.chatviews.get(sender_jid);
expect(view).toBeDefined();
@ -1421,7 +1417,8 @@
expect(msg_obj.get('is_delayed')).toEqual(false);
// Now check that the message appears inside the chatbox in the DOM
const chat_content = view.el.querySelector('.chat-content');
expect(chat_content.querySelector('.chat-msg .chat-msg__text').textContent).toEqual(message);
const mel = await u.waitUntil(() => chat_content.querySelector('.chat-msg .chat-msg__text'));
expect(mel.textContent).toEqual(message);
expect(chat_content.querySelector('.chat-msg__time').textContent.match(/^[0-9][0-9]:[0-9][0-9]/)).toBeTruthy();
await u.waitUntil(() => chatbox.vcard.get('fullname') === mock.cur_names[0]);
expect(chat_content.querySelector('span.chat-msg__author').textContent.trim()).toBe('Mercutio');
@ -1437,7 +1434,7 @@
await u.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length, 300);
const message = '\n\n This is a received message \n\n';
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
_converse.chatboxes.onMessage(
await _converse.handleMessageStanza(
$msg({
'from': sender_jid,
'to': _converse.connection.jid,
@ -1446,13 +1443,13 @@
}).c('body').t(message).up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree()
);
await u.waitUntil(() => (_converse.api.chats.get().length === 2));
const view = _converse.api.chatviews.get(sender_jid);
expect(view.model.messages.length).toEqual(1);
const msg_obj = view.model.messages.at(0);
expect(msg_obj.get('message')).toEqual(message.trim());
const chat_content = view.el.querySelector('.chat-content');
expect(chat_content.querySelector('.chat-msg .chat-msg__text').textContent).toEqual(message.trim());
const mel = await u.waitUntil(() => chat_content.querySelector('.chat-msg .chat-msg__text'));
expect(mel.textContent).toEqual(message.trim());
done();
}));
@ -1467,7 +1464,7 @@
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
const msg_id = u.getUniqueId();
const view = await test_utils.openChatBoxFor(_converse, sender_jid);
_converse.chatboxes.onMessage($msg({
_converse.handleMessageStanza($msg({
'from': sender_jid,
'to': _converse.connection.jid,
'type': 'chat',
@ -1478,7 +1475,7 @@
expect(view.el.querySelector('.chat-msg__text').textContent)
.toBe('But soft, what light through yonder airlock breaks?');
_converse.chatboxes.onMessage($msg({
_converse.handleMessageStanza($msg({
'from': sender_jid,
'to': _converse.connection.jid,
'type': 'chat',
@ -1493,7 +1490,7 @@
expect(view.el.querySelectorAll('.chat-msg__content .fa-edit').length).toBe(1);
expect(view.model.messages.models.length).toBe(1);
_converse.chatboxes.onMessage($msg({
_converse.handleMessageStanza($msg({
'from': sender_jid,
'to': _converse.connection.jid,
'type': 'chat',
@ -1550,8 +1547,7 @@
// We don't already have an open chatbox for this user
expect(_converse.chatboxes.get(sender_jid)).not.toBeDefined();
await _converse.chatboxes.onMessage(msg);
await u.waitUntil(() => _converse.api.chats.get().length);
await _converse.handleMessageStanza(msg);
expect(_converse.api.trigger).toHaveBeenCalledWith('message', jasmine.any(Object));
// Check that the chatbox and its view now exist
@ -1600,15 +1596,15 @@
expect(_converse.chatboxes.get(sender_jid)).not.toBeDefined();
let chatbox = await _converse.api.chats.get(sender_jid);
expect(chatbox).not.toBeDefined();
expect(chatbox).toBe(null);
// onMessage is a handler for received XMPP messages
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
let view = _converse.chatboxviews.get(sender_jid);
expect(view).not.toBeDefined();
// onMessage is a handler for received XMPP messages
_converse.allow_non_roster_messaging = true;
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
view = _converse.chatboxviews.get(sender_jid);
await new Promise(resolve => view.once('messageInserted', resolve));
expect(_converse.api.trigger).toHaveBeenCalledWith('message', jasmine.any(Object));
@ -1807,7 +1803,7 @@
// Create enough messages so that there's a scrollbar.
const promises = [];
for (let i=0; i<20; i++) {
_converse.chatboxes.onMessage($msg({
_converse.handleMessageStanza($msg({
from: sender_jid,
to: _converse.connection.jid,
type: 'chat',
@ -1826,7 +1822,7 @@
view.model.set('scrolled', true);
const message = 'This message is received while the chat area is scrolled up';
_converse.chatboxes.onMessage($msg({
_converse.handleMessageStanza($msg({
from: sender_jid,
to: _converse.connection.jid,
type: 'chat',
@ -1858,7 +1854,7 @@
await u.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length)
// Send a message from a different resource
spyOn(_converse, 'log');
spyOn(_converse.chatboxes, 'getChatBox').and.callThrough();
spyOn(_converse.api.chatboxes, 'create').and.callThrough();
_converse.filter_by_resource = true;
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
let msg = $msg({
@ -1868,12 +1864,12 @@
id: (new Date()).getTime()
}).c('body').t("This message will not be shown").up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
await _converse.chatboxes.onMessage(msg);
await u.waitUntil(() => _converse.api.chats.get().length);
await _converse.handleMessageStanza(msg);
expect(_converse.log).toHaveBeenCalledWith(
"onMessage: Ignoring incoming message intended for a different resource: romeo@montague.lit/some-other-resource",
Strophe.LogLevel.INFO);
expect(_converse.chatboxes.getChatBox).not.toHaveBeenCalled();
expect(_converse.api.chatboxes.create).not.toHaveBeenCalled();
_converse.filter_by_resource = false;
const message = "This message sent to a different resource will be shown";
@ -1884,11 +1880,11 @@
id: '134234623462346'
}).c('body').t(message).up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
await _converse.chatboxes.onMessage(msg);
await _converse.handleMessageStanza(msg);
await u.waitUntil(() => _converse.chatboxviews.keys().length > 1, 1000);
const view = _converse.chatboxviews.get(sender_jid);
await u.waitUntil(() => view.model.messages.length);
expect(_converse.chatboxes.getChatBox).toHaveBeenCalled();
expect(_converse.api.chatboxes.create).toHaveBeenCalled();
const last_message = await u.waitUntil(() => sizzle('.chat-content:last .chat-msg__text', view.el).pop());
const msg_txt = last_message.textContent;
expect(msg_txt).toEqual(message);
@ -2121,20 +2117,11 @@
const sent_stanzas = [];
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s));
_converse.connection._dataRecv(test_utils.createRequest(stanza));
await u.waitUntil(() => _converse.api.chats.get().length == 2);
await _converse.handleMessageStanza(stanza);
const sent_messages = sent_stanzas
.map(s => _.isElement(s) ? s : s.nodeTree)
.filter(e => e.nodeName === 'message');
// Only one message is sent out, and it's not a chat marker
expect(sent_messages.length).toBe(1);
expect(Strophe.serialize(sent_messages[0])).toBe(
`<message id="${sent_messages[0].getAttribute('id')}" to="someone@montague.lit" type="chat" xmlns="jabber:client">`+
`<active xmlns="http://jabber.org/protocol/chatstates"/>`+
`<no-store xmlns="urn:xmpp:hints"/>`+
`<no-permanent-store xmlns="urn:xmpp:hints"/>`+
`</message>`);
expect(sent_messages.length).toBe(0);
done();
}));

View File

@ -95,7 +95,7 @@
id: (new Date()).getTime()
}).c('body').t('This message is sent to a minimized chatbox').up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
_converse.chatboxes.onMessage(msg);
_converse.handleMessageStanza(msg);
}
return u.waitUntil(() => chatview.model.messages.length);
}).then(() => {
@ -103,7 +103,7 @@
expect(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count').textContent).toBe((3).toString());
// Chat state notifications don't increment the unread messages counter
// <composing> state
_converse.chatboxes.onMessage($msg({
_converse.handleMessageStanza($msg({
from: contact_jid,
to: _converse.connection.jid,
type: 'chat',
@ -112,7 +112,7 @@
expect(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count').textContent).toBe((i).toString());
// <paused> state
_converse.chatboxes.onMessage($msg({
_converse.handleMessageStanza($msg({
from: contact_jid,
to: _converse.connection.jid,
type: 'chat',
@ -121,7 +121,7 @@
expect(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count').textContent).toBe((i).toString());
// <gone> state
_converse.chatboxes.onMessage($msg({
_converse.handleMessageStanza($msg({
from: contact_jid,
to: _converse.connection.jid,
type: 'chat',
@ -130,7 +130,7 @@
expect(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count').textContent).toBe((i).toString());
// <inactive> state
_converse.chatboxes.onMessage($msg({
_converse.handleMessageStanza($msg({
from: contact_jid,
to: _converse.connection.jid,
type: 'chat',

View File

@ -89,7 +89,7 @@
// Non-existing room
muc_jid = 'chillout2@montague.lit';
room = await _converse.api.rooms.get(muc_jid);
expect(typeof room === 'undefined').toBeTruthy();
expect(room).toBe(null);
done();
}));

View File

@ -120,7 +120,6 @@
by="room@muc.example.com"/>
</message>`);
_converse.connection._dataRecv(test_utils.createRequest(stanza));
await u.waitUntil(() => _converse.api.chats.get().length);
await u.waitUntil(() => view.model.messages.length === 1);
await u.waitUntil(() => view.model.findDuplicateFromStanzaID.calls.count() === 1);
let result = await view.model.findDuplicateFromStanzaID.calls.all()[0].returnValue;
@ -572,7 +571,6 @@
const view = _converse.api.chatviews.get(muc_jid);
view.model.sendMessage('hello world');
await u.waitUntil(() => _converse.api.chats.get().length);
await u.waitUntil(() => view.model.messages.length === 1);
const msg = view.model.messages.at(0);
expect(msg.get('stanza_id')).toBeUndefined();

View File

@ -30,7 +30,7 @@
id: (new Date()).getTime()
}).c('body').t(message).up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
await _converse.chatboxes.onMessage(msg); // This will emit 'message'
await _converse.handleMessageStanza(msg); // This will emit 'message'
await u.waitUntil(() => _converse.api.chatviews.get(sender_jid));
expect(_converse.areDesktopNotificationsEnabled).toHaveBeenCalled();
expect(_converse.showMessageNotification).toHaveBeenCalled();

View File

@ -303,7 +303,6 @@
spyOn(registerview, 'onRegistrationFields').and.callThrough();
spyOn(registerview, 'renderRegistrationForm').and.callThrough();
registerview.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
spyOn(_converse.connection, 'connect').and.callThrough();
registerview.el.querySelector('input[name=domain]').value = 'conversejs.org';
registerview.el.querySelector('input[type=submit]').click();

View File

@ -263,7 +263,7 @@
expect(window.confirm).toHaveBeenCalledWith(
'Are you sure you want to leave the groupchat lounge@conference.shakespeare.lit?');
await u.waitUntil(() => !_converse.api.rooms.get().length);
await new Promise(resolve => _converse.api.listen.once('chatBoxClosed', resolve));
room_els = _converse.rooms_list_view.el.querySelectorAll(".open-room");
expect(room_els.length).toBe(0);
expect(_converse.chatboxes.length).toBe(1);

View File

@ -34,8 +34,8 @@
'xmlns': 'urn:xmpp:spoiler:0',
}).t(spoiler_hint)
.tree();
await _converse.chatboxes.onMessage(msg);
await u.waitUntil(() => _converse.api.chats.get().length === 2);
_converse.connection._dataRecv(test_utils.createRequest(msg));
await new Promise(resolve => _converse.api.listen.once('chatBoxInitialized', resolve));
const view = _converse.chatboxviews.get(sender_jid);
await new Promise(resolve => view.once('messageInserted', resolve));
await u.waitUntil(() => view.model.vcard.get('fullname') === 'Mercutio')
@ -69,10 +69,10 @@
.c('spoiler', {
'xmlns': 'urn:xmpp:spoiler:0',
}).tree();
await _converse.chatboxes.onMessage(msg);
await u.waitUntil(() => _converse.api.chats.get().length === 2);
_converse.connection._dataRecv(test_utils.createRequest(msg));
await new Promise(resolve => _converse.api.listen.once('chatBoxInitialized', resolve));
const view = _converse.chatboxviews.get(sender_jid);
await new Promise(resolve => view.once('messageInserted', resolve));
await u.waitUntil(() => u.isVisible(view.el));
await u.waitUntil(() => view.model.vcard.get('fullname') === 'Mercutio')
expect(_.includes(view.el.querySelector('.chat-msg__author').textContent, 'Mercutio')).toBeTruthy();
const message_content = view.el.querySelector('.chat-msg__text');

View File

@ -47,6 +47,7 @@ converse.plugins.add('converse-chatview', {
*/
dependencies: [
"converse-chatboxviews",
"converse-chat",
"converse-disco",
"converse-message-view",
"converse-modal"
@ -72,17 +73,6 @@ converse.plugins.add('converse-chatview', {
},
});
function onWindowStateChanged (data) {
if (_converse.chatboxviews) {
_converse.chatboxviews.forEach(view => {
if (view.model.get('id') !== 'controlbox') {
view.onWindowStateChanged(data.state);
}
});
}
}
_converse.api.listen.on('windowStateChanged', onWindowStateChanged);
_converse.ChatBoxHeading = _converse.ViewWithAvatar.extend({
initialize () {
@ -92,6 +82,9 @@ converse.plugins.add('converse-chatview', {
if (this.model.vcard) {
this.listenTo(this.model.vcard, 'change', this.debouncedRender);
}
if (this.model.contact) {
this.listenTo(this.model.contact, 'destroy', this.debouncedRender);
}
if (this.model.rosterContactAdded) {
this.model.rosterContactAdded.then(() => {
this.listenTo(this.model.contact, 'change:nickname', this.debouncedRender);
@ -101,8 +94,8 @@ converse.plugins.add('converse-chatview', {
},
render () {
const vcard = get(this.model, 'vcard'),
vcard_json = vcard ? vcard.toJSON() : {};
const vcard = get(this.model, 'vcard');
const vcard_json = vcard ? vcard.toJSON() : {};
this.el.innerHTML = tpl_chatbox_head(
Object.assign(
vcard_json,
@ -409,10 +402,6 @@ converse.plugins.add('converse-chatview', {
this.heading = new _converse.ChatBoxHeading({'model': this.model});
this.heading.render();
this.heading.chatview = this;
if (this.model.contact !== undefined) {
this.listenTo(this.model.contact, 'destroy', this.heading.render);
}
const flyout = this.el.querySelector('.flyout');
flyout.insertBefore(this.heading.el, flyout.querySelector('.chat-body'));
return this;
@ -1299,15 +1288,29 @@ converse.plugins.add('converse-chatview', {
_converse.api.listen.on('chatBoxViewsInitialized', () => {
const views = _converse.chatboxviews;
_converse.chatboxes.on('add', item => {
_converse.chatboxes.on('add', async item => {
if (!views.get(item.get('id')) && item.get('type') === _converse.PRIVATE_CHAT_TYPE) {
await item.initialized;
views.add(item.get('id'), new _converse.ChatBoxView({model: item}));
}
});
});
// Advertise that we support XEP-0382 Message Spoilers
/************************ BEGIN Event Handlers ************************/
function onWindowStateChanged (data) {
if (_converse.chatboxviews) {
_converse.chatboxviews.forEach(view => {
if (view.model.get('id') !== 'controlbox') {
view.onWindowStateChanged(data.state);
}
});
}
}
_converse.api.listen.on('windowStateChanged', onWindowStateChanged);
_converse.api.listen.on('connected', () => _converse.api.disco.own.features.add(Strophe.NS.SPOILER));
/************************ END Event Handlers ************************/
/************************ BEGIN API ************************/
Object.assign(_converse.api, {

View File

@ -69,7 +69,7 @@ converse.plugins.add('converse-controlbox', {
*
* NB: These plugins need to have already been loaded via require.js.
*/
dependencies: ["converse-modal", "converse-chatboxes", "converse-rosterview", "converse-chatview"],
dependencies: ["converse-modal", "converse-chatboxes", "converse-chat", "converse-rosterview", "converse-chatview"],
enabled (_converse) {
return !_converse.singleton;
@ -626,16 +626,25 @@ converse.plugins.add('converse-controlbox', {
* @namespace _converse.api.controlbox
* @memberOf _converse.api
*/
'controlbox': {
controlbox: {
/**
* Retrieves the controlbox view.
*
* Opens the controlbox
* @method _converse.api.controlbox.open
* @returns { Promise<_converse.ControlBox> }
*/
async open () {
await _converse.api.waitUntil('chatBoxesFetched');
const model = await _converse.api.chatboxes.get('controlbox') ||
_converse.api.chatboxes.create('controlbox', {}, _converse.Controlbox);
model.trigger('show');
return model;
},
/**
* Returns the controlbox view.
* @method _converse.api.controlbox.get
*
* @example
* const view = _converse.api.controlbox.get();
*
* @returns {Backbone.View} View representing the controlbox
* @returns { Backbone.View } View representing the controlbox
* @example const view = _converse.api.controlbox.get();
*/
get () {
return _converse.chatboxviews.get('controlbox');

View File

@ -0,0 +1,89 @@
// Converse.js (A browser based XMPP chat client)
// https://conversejs.org
//
// Copyright (c) 2019, Jan-Carel Brand <jc@opkode.com>
// Licensed under the Mozilla Public License (MPLv2)
/**
* @module converse-headline
*/
import "converse-chatview";
import converse from "@converse/headless/converse-core";
import tpl_chatbox from "templates/chatbox.html";
converse.plugins.add('converse-headlines-view', {
/* Plugin dependencies are other plugins which might be
* overridden or relied upon, and therefore need to be loaded before
* this plugin.
*
* If the setting "strict_plugin_dependencies" is set to true,
* an error will be raised if the plugin is not found. By default it's
* false, which means these plugins are only loaded opportunistically.
*
* NB: These plugins need to have already been loaded via require.js.
*/
dependencies: ["converse-headlines", "converse-chatview"],
initialize () {
/* The initialize function gets called as soon as the plugin is
* loaded by converse.js's plugin machinery.
*/
const { _converse } = this;
_converse.HeadlinesBoxView = _converse.ChatBoxView.extend({
className: 'chatbox headlines',
events: {
'click .close-chatbox-button': 'close',
'click .toggle-chatbox-button': 'minimize',
'keypress textarea.chat-textarea': 'onKeyDown'
},
initialize () {
this.initDebounced();
this.model.disable_mam = true; // Don't do MAM queries for this box
this.listenTo(this.model.messages, 'add', this.onMessageAdded);
this.listenTo(this.model, 'show', this.show);
this.listenTo(this.model, 'destroy', this.hide);
this.listenTo(this.model, 'change:minimized', this.onMinimizedChanged);
this.render().insertHeading()
this.updateAfterMessagesFetched();
this.insertIntoDOM().hide();
_converse.api.trigger('chatBoxInitialized', this);
},
render () {
this.el.setAttribute('id', this.model.get('box_id'))
this.el.innerHTML = tpl_chatbox(
Object.assign(this.model.toJSON(), {
info_close: '',
label_personal_message: '',
show_send_button: false,
show_toolbar: false,
unread_msgs: ''
}
));
this.content = this.el.querySelector('.chat-content');
return this;
},
// Override to avoid the methods in converse-chatview.js
'renderMessageForm': function renderMessageForm () {},
'afterShown': function afterShown () {}
});
_converse.api.listen.on('chatBoxViewsInitialized', () => {
const views = _converse.chatboxviews;
_converse.chatboxes.on('add', item => {
if (!views.get(item.get('id')) && item.get('type') === _converse.HEADLINES_TYPE) {
views.add(item.get('id'), new _converse.HeadlinesBoxView({model: item}));
}
});
});
}
});

View File

@ -112,8 +112,9 @@ converse.plugins.add('converse-minimize', {
ChatBoxHeading: {
render () {
const { _converse } = this.__super__,
{ __ } = _converse;
const { _converse } = this.__super__;
const { __ } = _converse;
this.__super__.render.apply(this, arguments);
const new_html = tpl_chatbox_minimize({
'info_minimize': __('Minimize this chat box')

View File

@ -91,7 +91,7 @@ converse.plugins.add('converse-roomslist', {
toHTML () {
return tpl_rooms_list({
'rooms': _converse.api.rooms.get(),
'rooms': this.model.filter(m => m.get('type') === _converse.CHATROOMS_TYPE),
'allow_bookmarks': _converse.allow_bookmarks && _converse.bookmarks,
'collapsed': this.list_model.get('toggle-state') !== _converse.OPENED,
'desc_rooms': __('Click to toggle the list of open groupchats'),

View File

@ -17,10 +17,10 @@ import "converse-controlbox"; // The control box
import "converse-dragresize"; // Allows chat boxes to be resized by dragging them
import "converse-emoji-views";
import "converse-fullscreen";
import "converse-headline"; // Support for headline messages
import "converse-mam-views";
import "converse-minimize"; // Allows chat boxes to be minimized
import "converse-muc-views"; // Views related to MUC
import "converse-headlines-view";
import "converse-notification"; // HTML5 Notifications
import "converse-omemo";
import "converse-profile";
@ -51,6 +51,7 @@ const WHITELISTED_PLUGINS = [
'converse-minimize',
'converse-modal',
'converse-muc-views',
'converse-headlines-view',
'converse-notification',
'converse-omemo',
'converse-profile',

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -78,10 +78,12 @@ const CORE_PLUGINS = [
'converse-bosh',
'converse-caps',
'converse-chatboxes',
'converse-chat',
'converse-disco',
'converse-emoji',
'converse-mam',
'converse-muc',
'converse-headlines',
'converse-ping',
'converse-pubsub',
'converse-roster',

View File

@ -8,12 +8,12 @@
*/
import "converse-chatview";
import converse from "@converse/headless/converse-core";
import tpl_chatbox from "templates/chatbox.html";
import { isString } from "lodash";
const { utils } = converse.env;
converse.plugins.add('converse-headline', {
converse.plugins.add('converse-headlines', {
/* Plugin dependencies are other plugins which might be
* overridden or relied upon, and therefore need to be loaded before
* this plugin.
@ -24,7 +24,7 @@ converse.plugins.add('converse-headline', {
*
* NB: These plugins need to have already been loaded via require.js.
*/
dependencies: ["converse-chatview"],
dependencies: ["converse-chat"],
overrides: {
// Overrides mentioned here will be picked up by converse.js's
@ -71,52 +71,8 @@ converse.plugins.add('converse-headline', {
});
_converse.HeadlinesBoxView = _converse.ChatBoxView.extend({
className: 'chatbox headlines',
events: {
'click .close-chatbox-button': 'close',
'click .toggle-chatbox-button': 'minimize',
'keypress textarea.chat-textarea': 'onKeyDown'
},
initialize () {
this.initDebounced();
this.model.disable_mam = true; // Don't do MAM queries for this box
this.listenTo(this.model.messages, 'add', this.onMessageAdded);
this.listenTo(this.model, 'show', this.show);
this.listenTo(this.model, 'destroy', this.hide);
this.listenTo(this.model, 'change:minimized', this.onMinimizedChanged);
this.render().insertHeading()
this.updateAfterMessagesFetched();
this.insertIntoDOM().hide();
_converse.api.trigger('chatBoxInitialized', this);
},
render () {
this.el.setAttribute('id', this.model.get('box_id'))
this.el.innerHTML = tpl_chatbox(
Object.assign(this.model.toJSON(), {
info_close: '',
label_personal_message: '',
show_send_button: false,
show_toolbar: false,
unread_msgs: ''
}
));
this.content = this.el.querySelector('.chat-content');
return this;
},
// Override to avoid the methods in converse-chatview.js
'renderMessageForm': function renderMessageForm () {},
'afterShown': function afterShown () {}
});
async function onHeadlineMessage (message) {
/* Handler method for all incoming messages of type "headline". */
// Handler method for all incoming messages of type "headline".
if (utils.isHeadlineMessage(_converse, message)) {
const from_jid = message.getAttribute('from');
if (from_jid.includes('@') &&
@ -140,6 +96,8 @@ converse.plugins.add('converse-headline', {
}
}
/************************ BEGIN Event Handlers ************************/
function registerHeadlineHandler () {
_converse.connection.addHandler(message => {
onHeadlineMessage(message);
@ -148,15 +106,52 @@ converse.plugins.add('converse-headline', {
}
_converse.api.listen.on('connected', registerHeadlineHandler);
_converse.api.listen.on('reconnected', registerHeadlineHandler);
/************************ END Event Handlers ************************/
_converse.api.listen.on('chatBoxViewsInitialized', () => {
const views = _converse.chatboxviews;
_converse.chatboxes.on('add', item => {
if (!views.get(item.get('id')) && item.get('type') === _converse.HEADLINES_TYPE) {
views.add(item.get('id'), new _converse.HeadlinesBoxView({model: item}));
/************************ BEGIN API ************************/
Object.assign(_converse.api, {
/**
* The "headlines" namespace, which is used for headline-channels
* which are read-only channels containing messages of type
* "headline".
*
* @namespace _converse.api.headlines
* @memberOf _converse.api
*/
headlines: {
/**
* Retrieves a headline-channel or all headline-channels.
*
* @method _converse.api.headlines.get
* @param {String|String[]} jids - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com']
* @param {Object} [attrs] - Attributes to be set on the _converse.ChatBox model.
* @param {Boolean} [create=false] - Whether the chat should be created if it's not found.
* @returns { Promise<_converse.HeadlinesBox> }
*/
async get (jids, attrs={}, create=false) {
async function _get (jid) {
let model = await _converse.api.chatboxes.get(jid);
if (!model && create) {
model = await _converse.api.chatboxes.create(jid, attrs, _converse.HeadlinesBox);
} else {
model = (model && model.get('type') === _converse.HEADLINES_TYPE) ? model : null;
if (model && Object.keys(attrs).length) {
model.save(attrs);
}
}
return model;
}
if (jids === undefined) {
const chats = await _converse.api.chatboxes.get();
return chats.filter(c => (c.get('type') === _converse.HEADLINES_TYPE));
} else if (isString(jids)) {
return _get(jids);
}
return Promise.all(jids.map(jid => _get(jid)));
}
});
}
});
/************************ END API ************************/
}
});

View File

@ -117,7 +117,7 @@ converse.plugins.add('converse-mam', {
}
const message_handler = is_groupchat ?
this.onMessage.bind(this) :
_converse.chatboxes.onMessage.bind(_converse.chatboxes);
_converse.handleMessageStanza.bind(_converse.chatboxes);
const query = Object.assign({
'groupchat': is_groupchat,

View File

@ -12,6 +12,7 @@
import "./converse-disco";
import "./converse-emoji";
import "./utils/muc";
import { clone, get, intersection, invoke, isElement, isObject, isString, uniq, zipObject } from "lodash";
import converse from "./converse-core";
import u from "./utils/form";
@ -22,7 +23,7 @@ const MUC_ROLE_WEIGHTS = {
'none': 2,
};
const { Strophe, Backbone, $iq, $build, $msg, $pres, sizzle, _ } = converse.env;
const { Strophe, Backbone, $iq, $build, $msg, $pres, sizzle } = converse.env;
// Add Strophe Namespaces
Strophe.addNamespace('MUC_ADMIN', Strophe.NS.MUC + "#admin");
@ -81,13 +82,13 @@ converse.plugins.add('converse-muc', {
*
* NB: These plugins need to have already been loaded via require.js.
*/
dependencies: ["converse-chatboxes", "converse-disco", "converse-controlbox"],
dependencies: ["converse-chatboxes", "converse-chat", "converse-disco", "converse-controlbox"],
overrides: {
tearDown () {
const { _converse } = this.__super__;
const groupchats = this.chatboxes.where({'type': _converse.CHATROOMS_TYPE});
_.each(groupchats, gc => u.safeSave(gc, {'connection_status': converse.ROOMSTATUS.DISCONNECTED}));
groupchats.forEach(gc => u.safeSave(gc, {'connection_status': converse.ROOMSTATUS.DISCONNECTED}));
this.__super__.tearDown.call(this, arguments);
},
@ -129,7 +130,7 @@ converse.plugins.add('converse-muc', {
});
_converse.api.promises.add(['roomsAutoJoined']);
if (_converse.locked_muc_domain && !_.isString(_converse.muc_domain)) {
if (_converse.locked_muc_domain && !isString(_converse.muc_domain)) {
throw new Error("Config Error: it makes no sense to set locked_muc_domain "+
"to true when muc_domain is not set");
}
@ -234,7 +235,7 @@ converse.plugins.add('converse-muc', {
*/
settings.type = _converse.CHATROOMS_TYPE;
settings.id = jid;
const chatbox = await _converse.chatboxes.getChatBox(jid, settings, true);
const chatbox = await _converse.api.rooms.get(jid, settings, true);
chatbox.maybeShow(true);
return chatbox;
}
@ -263,7 +264,7 @@ converse.plugins.add('converse-muc', {
onOccupantRemoved () {
this.stopListening(this.occupant);
delete this.occupant;
const chatbox = _.get(this, 'collection.chatbox');
const chatbox = get(this, 'collection.chatbox');
if (!chatbox) {
return _converse.log(
`Could not get collection.chatbox for message: ${JSON.stringify(this.toJSON())}`,
@ -277,7 +278,7 @@ converse.plugins.add('converse-muc', {
if (occupant.get('nick') === Strophe.getResourceFromJid(this.get('from'))) {
this.occupant = occupant;
this.listenTo(this.occupant, 'destroy', this.onOccupantRemoved);
const chatbox = _.get(this, 'collection.chatbox');
const chatbox = get(this, 'collection.chatbox');
if (!chatbox) {
return _converse.log(
`Could not get collection.chatbox for message: ${JSON.stringify(this.toJSON())}`,
@ -290,7 +291,7 @@ converse.plugins.add('converse-muc', {
setOccupant () {
if (this.get('type') !== 'groupchat') { return; }
const chatbox = _.get(this, 'collection.chatbox');
const chatbox = get(this, 'collection.chatbox');
if (!chatbox) {
return _converse.log(
`Could not get collection.chatbox for message: ${JSON.stringify(this.toJSON())}`,
@ -308,7 +309,7 @@ converse.plugins.add('converse-muc', {
},
getVCardForChatroomOccupant () {
const chatbox = _.get(this, 'collection.chatbox');
const chatbox = get(this, 'collection.chatbox');
const nick = Strophe.getResourceFromJid(this.get('from'));
if (chatbox && chatbox.get('nick') === nick) {
@ -488,7 +489,7 @@ converse.plugins.add('converse-muc', {
initFeatures () {
const id = `converse.muc-features-${_converse.bare_jid}-${this.get('jid')}`;
this.features = new Backbone.Model(
_.assign({id}, _.zipObject(converse.ROOM_FEATURES, converse.ROOM_FEATURES.map(_.stubFalse)))
Object.assign({id}, zipObject(converse.ROOM_FEATURES, converse.ROOM_FEATURES.map(() => false)))
);
this.features.browserStorage = _converse.createStore(id, "session");
},
@ -855,13 +856,13 @@ converse.plugins.add('converse-muc', {
const fields = await _converse.api.disco.getFields(this.get('jid'));
this.save({
'name': identity && identity.get('name'),
'description': _.get(fields.findWhere({'var': "muc#roominfo_description"}), 'attributes.value')
'description': get(fields.findWhere({'var': "muc#roominfo_description"}), 'attributes.value')
}
);
const features = await _converse.api.disco.getFeatures(this.get('jid'));
const attrs = Object.assign(
_.zipObject(converse.ROOM_FEATURES, converse.ROOM_FEATURES.map(_.stubFalse)),
zipObject(converse.ROOM_FEATURES, converse.ROOM_FEATURES.map(() => false)),
{'fetched': (new Date()).toISOString()}
);
features.each(feature => {
@ -874,7 +875,7 @@ converse.plugins.add('converse-muc', {
}
attrs[fieldname.replace('muc_', '')] = true;
});
attrs.description = _.get(fields.findWhere({'var': "muc#roominfo_description"}), 'attributes.value');
attrs.description = get(fields.findWhere({'var': "muc#roominfo_description"}), 'attributes.value');
this.features.save(attrs);
},
@ -1006,7 +1007,7 @@ converse.plugins.add('converse-muc', {
* @returns { ('none'|'visitor'|'participant'|'moderator') }
*/
getOwnRole () {
return _.get(this.getOwnOccupant(), 'attributes.role');
return get(this.getOwnOccupant(), 'attributes.role');
},
/**
@ -1016,7 +1017,7 @@ converse.plugins.add('converse-muc', {
* @returns { ('none'|'outcast'|'member'|'admin'|'owner') }
*/
getOwnAffiliation () {
return _.get(this.getOwnOccupant(), 'attributes.affiliation');
return get(this.getOwnOccupant(), 'attributes.affiliation');
},
/**
@ -1066,7 +1067,7 @@ converse.plugins.add('converse-muc', {
* @returns { Promise }
*/
setAffiliations (members) {
const affiliations = _.uniq(members.map(m => m.affiliation));
const affiliations = uniq(members.map(m => m.affiliation));
return Promise.all(affiliations.map(a => this.setAffiliation(a, members)));
},
@ -1280,8 +1281,8 @@ converse.plugins.add('converse-muc', {
}
const jid = data.jid || '';
const attributes = Object.assign(data, {
'jid': Strophe.getBareJidFromJid(jid) || _.get(occupant, 'attributes.jid'),
'resource': Strophe.getResourceFromJid(jid) || _.get(occupant, 'attributes.resource')
'jid': Strophe.getBareJidFromJid(jid) || get(occupant, 'attributes.jid'),
'resource': Strophe.getResourceFromJid(jid) || get(occupant, 'attributes.resource')
});
if (occupant) {
occupant.save(attributes);
@ -1326,7 +1327,7 @@ converse.plugins.add('converse-muc', {
}
});
} else if (child.getAttribute("xmlns") === Strophe.NS.VCARDUPDATE) {
data.image_hash = _.get(child.querySelector('photo'), 'textContent');
data.image_hash = get(child.querySelector('photo'), 'textContent');
}
}
});
@ -1415,7 +1416,7 @@ converse.plugins.add('converse-muc', {
*/
isOwnMessage (msg) {
let from;
if (_.isElement(msg)) {
if (isElement(msg)) {
from = msg.getAttribute('from');
} else if (msg instanceof _converse.Message) {
from = msg.get('from');
@ -1460,7 +1461,7 @@ converse.plugins.add('converse-muc', {
await _converse.api.sendIQ(ping);
} catch (e) {
const sel = `error not-acceptable[xmlns="${Strophe.NS.STANZAS}"]`;
if (_.isElement(e) && sizzle(sel, e).length) {
if (isElement(e) && sizzle(sel, e).length) {
return false;
}
}
@ -1576,7 +1577,7 @@ converse.plugins.add('converse-muc', {
handleModifyError(pres) {
const text = _.get(pres.querySelector('error text'), 'textContent');
const text = get(pres.querySelector('error text'), 'textContent');
if (text) {
if (this.get('connection_status') === converse.ROOMSTATUS.CONNECTING) {
this.setDisconnectionMessage(text);
@ -1598,7 +1599,7 @@ converse.plugins.add('converse-muc', {
return;
}
const codes = sizzle('status', x).map(s => s.getAttribute('code'));
const disconnection_codes = _.intersection(codes, Object.keys(_converse.muc.disconnect_messages));
const disconnection_codes = intersection(codes, Object.keys(_converse.muc.disconnect_messages));
const disconnected = is_self && disconnection_codes.length > 0;
if (!disconnected) {
return;
@ -1608,8 +1609,8 @@ converse.plugins.add('converse-muc', {
// element. This appears to be a safe assumption, since
// each <x/> element pertains to a single user.
const item = x.querySelector('item');
const reason = item ? _.get(item.querySelector('reason'), 'textContent') : undefined;
const actor = item ? _.invoke(item.querySelector('actor'), 'getAttribute', 'nick') : undefined;
const reason = item ? get(item.querySelector('reason'), 'textContent') : undefined;
const actor = item ? invoke(item.querySelector('actor'), 'getAttribute', 'nick') : undefined;
const message = _converse.muc.disconnect_messages[disconnection_codes[0]];
this.setDisconnectionMessage(message, reason, actor);
},
@ -1639,8 +1640,8 @@ converse.plugins.add('converse-muc', {
const nick = Strophe.getResourceFromJid(stanza.getAttribute('from'));
message = __(_converse.muc.action_info_messages[code], nick);
const item = x.querySelector('item');
const reason = item ? _.get(item.querySelector('reason'), 'textContent') : undefined;
const actor = item ? _.invoke(item.querySelector('actor'), 'getAttribute', 'nick') : undefined;
const reason = item ? get(item.querySelector('reason'), 'textContent') : undefined;
const actor = item ? invoke(item.querySelector('actor'), 'getAttribute', 'nick') : undefined;
if (actor) {
message += '\n' + __('This action was done by %1$s.', actor);
}
@ -1705,7 +1706,7 @@ converse.plugins.add('converse-muc', {
onErrorPresence (stanza) {
const error = stanza.querySelector('error');
const error_type = error.getAttribute('type');
const reason = _.get(sizzle(`text[xmlns="${Strophe.NS.STANZAS}"]`, error).pop(), 'textContent');
const reason = get(sizzle(`text[xmlns="${Strophe.NS.STANZAS}"]`, error).pop(), 'textContent');
if (error_type === 'modify') {
this.handleModifyError(stanza);
@ -1731,7 +1732,7 @@ converse.plugins.add('converse-muc', {
const message = __("Your nickname doesn't conform to this groupchat's policies.");
this.setDisconnectionMessage(message, reason);
} else if (sizzle(`gone[xmlns="${Strophe.NS.STANZAS}"]`, error).length) {
const moved_jid = _.get(sizzle(`gone[xmlns="${Strophe.NS.STANZAS}"]`, error).pop(), 'textContent')
const moved_jid = get(sizzle(`gone[xmlns="${Strophe.NS.STANZAS}"]`, error).pop(), 'textContent')
.replace(/^xmpp:/, '')
.replace(/\?join$/, '');
this.save({
@ -2073,18 +2074,11 @@ converse.plugins.add('converse-muc', {
_converse.api.listen.on('reconnected', registerDirectInvitationHandler);
}
const getChatRoom = function (jid, attrs, create) {
jid = jid.toLowerCase();
attrs.type = _converse.CHATROOMS_TYPE;
attrs.id = jid;
return _converse.chatboxes.getChatBox(jid, attrs, create);
};
const createChatRoom = function (jid, attrs) {
if (jid.startsWith('xmpp:') && jid.endsWith('?join')) {
jid = jid.replace(/^xmpp:/, '').replace(/\?join$/, '');
}
return getChatRoom(jid, attrs, true);
return _converse.api.rooms.get(jid, attrs, true);
};
/**
@ -2095,13 +2089,13 @@ converse.plugins.add('converse-muc', {
*/
function autoJoinRooms () {
_converse.auto_join_rooms.forEach(groupchat => {
if (_.isString(groupchat)) {
if (isString(groupchat)) {
if (_converse.chatboxes.where({'jid': groupchat}).length) {
return;
}
_converse.api.rooms.open(groupchat);
} else if (_.isObject(groupchat)) {
_converse.api.rooms.open(groupchat.jid, _.clone(groupchat));
} else if (isObject(groupchat)) {
_converse.api.rooms.open(groupchat.jid, clone(groupchat));
} else {
_converse.log(
'Invalid groupchat criteria specified for "auto_join_rooms"',
@ -2187,13 +2181,13 @@ converse.plugins.add('converse-muc', {
* @returns {Promise} Promise which resolves with the Backbone.Model representing the chat.
*/
create (jids, attrs={}) {
attrs = _.isString(attrs) ? {'nick': attrs} : (attrs || {});
attrs = isString(attrs) ? {'nick': attrs} : (attrs || {});
if (!attrs.nick && _converse.muc_nickname_from_jid) {
attrs.nick = Strophe.getNodeFromJid(_converse.bare_jid);
}
if (jids === undefined) {
throw new TypeError('rooms.create: You need to provide at least one JID');
} else if (_.isString(jids)) {
} else if (isString(jids)) {
return createChatRoom(jids, attrs);
}
return jids.map(jid => createChatRoom(jid, attrs));
@ -2264,7 +2258,7 @@ converse.plugins.add('converse-muc', {
const err_msg = 'rooms.open: You need to provide at least one JID';
_converse.log(err_msg, Strophe.LogLevel.ERROR);
throw(new TypeError(err_msg));
} else if (_.isString(jids)) {
} else if (isString(jids)) {
const room = await _converse.api.rooms.create(jids, attrs);
room && room.maybeShow(force);
return room;
@ -2288,7 +2282,7 @@ converse.plugins.add('converse-muc', {
* the user's JID will be used.
* @param {boolean} create A boolean indicating whether the room should be created
* if not found (default: `false`)
* @returns {Promise} Promise which resolves with the Backbone.Model representing the chat.
* @returns { Promise<_converse.ChatRoom> }
* @example
* _converse.api.waitUntil('roomsAutoJoined').then(() => {
* const create_if_not_found = true;
@ -2299,28 +2293,26 @@ converse.plugins.add('converse-muc', {
* )
* });
*/
get (jids, attrs, create) {
if (_.isString(attrs)) {
attrs = {'nick': attrs};
} else if (attrs === undefined) {
attrs = {};
async get (jids, attrs={}, create=false) {
async function _get (jid) {
let model = await _converse.api.chatboxes.get(jid);
if (!model && create) {
model = await _converse.api.chatboxes.create(jid, attrs, _converse.ChatRoom);
} else {
model = (model && model.get('type') === _converse.CHATROOMS_TYPE) ? model : null;
if (model && Object.keys(attrs).length) {
model.save(attrs);
}
}
return model;
}
if (jids === undefined) {
const result = [];
_converse.chatboxes.each(function (chatbox) {
if (chatbox.get('type') === _converse.CHATROOMS_TYPE) {
result.push(chatbox);
}
});
return result;
const chats = await _converse.api.chatboxes.get();
return chats.filter(c => (c.get('type') === _converse.CHATROOMS_TYPE));
} else if (isString(jids)) {
return _get(jids);
}
if (!attrs.nick) {
attrs.nick = Strophe.getNodeFromJid(_converse.bare_jid);
}
if (_.isString(jids)) {
return getChatRoom(jids, attrs, create);
}
return jids.map(jid => getChatRoom(jid, attrs, create));
return Promise.all(jids.map(jid => _get(jid)));
}
}
});

View File

@ -6,15 +6,17 @@ import "./converse-bookmarks"; // XEP-0199 XMPP Ping
import "./converse-bosh"; // XEP-0206 BOSH
import "./converse-caps"; // XEP-0115 Entity Capabilities
import "./converse-chatboxes"; // Backbone Collection and Models for chat boxes
import "./converse-chat"; // Support for one-on-one chats
import "./converse-disco"; // XEP-0030 Service discovery
import "./converse-mam"; // XEP-0313 Message Archive Management
import "./converse-muc"; // XEP-0045 Multi-user chat
import "./converse-headlines"; // Support for headline messages
import "./converse-ping"; // XEP-0199 XMPP Ping
import "./converse-pubsub"; // XEP-0060 Pubsub
import "./converse-roster"; // Contacts Roster
import "./converse-rsm"; // XEP-0059 Result Set management
import "./converse-smacks"; // XEP-0198 Stream Management
import "./converse-status"; // XEP-0199 XMPP Ping
import "./converse-status"; // XEP-0199 XMPP Ping
import "./converse-vcard"; // XEP-0054 VCard-temp
/* END: Removable components */

View File

@ -52,7 +52,7 @@
};
utils.openControlBox = async function (_converse) {
const model = await _converse.api.chats.open('controlbox');
const model = await _converse.api.controlbox.open();
await u.waitUntil(() => model.get('connected'));
var toggle = document.querySelector(".toggle-controlbox");
if (!u.isVisible(document.querySelector("#controlbox"))) {
@ -121,7 +121,7 @@
utils.openChatRoomViaModal = async function (_converse, jid, nick='') {
// Opens a new chatroom
const model = await _converse.api.chats.open('controlbox');
const model = await _converse.api.controlbox.open('controlbox');
await u.waitUntil(() => model.get('connected'));
utils.openControlBox();
const view = await _converse.chatboxviews.get('controlbox');