diff --git a/CHANGES.md b/CHANGES.md
index 245974f54..2c7bb340e 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -10,6 +10,7 @@
- #1823: New config options [muc_roomid_policy](https://conversejs.org/docs/html/configuration.html#muc-roomid-policy)
and [muc_roomid_policy_hint](https://conversejs.org/docs/html/configuration.html#muc-roomid-policy-hint)
- #1826: A user can now add himself as a contact
+- #1839: Headline messages are shown in controlbox
## 6.0.0 (2020-01-09)
diff --git a/sass/_controlbox.scss b/sass/_controlbox.scss
index 9572b1575..67e8f1855 100644
--- a/sass/_controlbox.scss
+++ b/sass/_controlbox.scss
@@ -230,6 +230,10 @@
color: var(--chat-head-color-dark);
}
+ .controlbox-heading--headline {
+ color: var(--headline-head-color);
+ }
+
.controlbox-heading__btn {
cursor: pointer;
font-size: 1em;
diff --git a/spec/headline.js b/spec/headline.js
index 7d93f49aa..78242449a 100644
--- a/spec/headline.js
+++ b/spec/headline.js
@@ -87,6 +87,79 @@
done();
}));
+ it("will show headline messages in the controlbox", mock.initConverse(
+ ['rosterGroupsFetched'], {}, async function (done, _converse) {
+
+ /*
+ * SIEVE
+ * <juliet@example.com> You got mail.
+ *
+ *
+ * imap://romeo@example.com/INBOX;UIDVALIDITY=385759043/;UID=18
+ *
+ *
+ *
+ */
+ const stanza = $msg({
+ 'type': 'headline',
+ 'from': 'notify.example.com',
+ 'to': 'romeo@montague.lit',
+ 'xml:lang': 'en'
+ })
+ .c('subject').t('SIEVE').up()
+ .c('body').t('<juliet@example.com> You got mail.').up()
+ .c('x', {'xmlns': 'jabber:x:oob'})
+ .c('url').t('imap://romeo@example.com/INBOX;UIDVALIDITY=385759043/;UID=18');
+
+ _converse.connection._dataRecv(test_utils.createRequest(stanza));
+ const view = _converse.chatboxviews.get('controlbox');
+ await u.waitUntil(() => view.el.querySelectorAll(".open-headline").length);
+ expect(view.el.querySelectorAll('.open-headline').length).toBe(1);
+ expect(view.el.querySelector('.open-headline').text).toBe('notify.example.com');
+ done();
+ }));
+
+ it("will remove headline messages from the controlbox if closed", mock.initConverse(
+ ['rosterGroupsFetched'], {}, async function (done, _converse) {
+
+ /*
+ * SIEVE
+ * <juliet@example.com> You got mail.
+ *
+ *
+ * imap://romeo@example.com/INBOX;UIDVALIDITY=385759043/;UID=18
+ *
+ *
+ *
+ */
+ const stanza = $msg({
+ 'type': 'headline',
+ 'from': 'notify.example.com',
+ 'to': 'romeo@montague.lit',
+ 'xml:lang': 'en'
+ })
+ .c('subject').t('SIEVE').up()
+ .c('body').t('<juliet@example.com> You got mail.').up()
+ .c('x', {'xmlns': 'jabber:x:oob'})
+ .c('url').t('imap://romeo@example.com/INBOX;UIDVALIDITY=385759043/;UID=18');
+
+ _converse.connection._dataRecv(test_utils.createRequest(stanza));
+ const cbview = _converse.chatboxviews.get('controlbox');
+ await u.waitUntil(() => cbview.el.querySelectorAll(".open-headline").length);
+ const hlview = _converse.chatboxviews.get('notify.example.com');
+ const close_el = hlview.el.querySelector('.close-chatbox-button');
+ close_el.click();
+ await u.waitUntil(() => cbview.el.querySelectorAll(".open-headline").length === 0);
+ expect(cbview.el.querySelectorAll('.open-headline').length).toBe(0);
+ done();
+ }));
+
it("will not show a headline messages from a full JID if allow_non_roster_messaging is false",
mock.initConverse(
['rosterGroupsFetched', 'chatBoxesFetched'], {}, function (done, _converse) {
diff --git a/src/headless/converse-headlines.js b/src/headless/converse-headlines.js
index 2ff059326..06aabf5a6 100644
--- a/src/headless/converse-headlines.js
+++ b/src/headless/converse-headlines.js
@@ -9,8 +9,10 @@
import "converse-chatview";
import converse from "@converse/headless/converse-core";
import { isString } from "lodash";
+import tpl_headline_list from "templates/headline_list.html";
+import tpl_headline_panel from "templates/headline_panel.html";
-const { utils } = converse.env;
+const { Backbone, utils } = converse.env;
converse.plugins.add('converse-headlines', {
@@ -42,7 +44,13 @@ converse.plugins.add('converse-headlines', {
return this.__super__.model.apply(this, arguments);
}
},
- }
+ },
+ ControlBoxView: {
+ renderControlBoxPane () {
+ this.__super__.renderControlBoxPane.apply(this, arguments);
+ this.renderHeadlinePanel();
+ },
+ },
},
@@ -50,7 +58,33 @@ converse.plugins.add('converse-headlines', {
/* The initialize function gets called as soon as the plugin is
* loaded by converse.js's plugin machinery.
*/
- const { _converse } = this;
+ const { _converse } = this,
+ { __ } = _converse;
+
+ const viewWithHeadlinePanel = {
+ renderHeadlinePanel () {
+ if (this.headlinepanel && utils.isInDOM(this.headlinepanel.el)) {
+ return this.headlinepanel;
+ }
+ this.headlinepanel = new _converse.HeadlinePanel();
+ this.el.querySelector('.controlbox-pane').insertAdjacentElement(
+ 'beforeEnd', this.headlinepanel.render().el);
+
+ return this.headlinepanel;
+ },
+
+ getHeadlinePanel () {
+ if (this.headlinepanel && utils.isInDOM(this.headlinepanel.el)) {
+ return this.headlinepanel;
+ } else {
+ return this.renderHeadlinePanel();
+ }
+ }
+ }
+
+ if (_converse.ControlBoxView) {
+ Object.assign(_converse.ControlBoxView.prototype, viewWithHeadlinePanel);
+ }
/**
* Shows headline messages
@@ -83,6 +117,26 @@ converse.plugins.add('converse-headlines', {
}
});
+ function getAllHeadlineBoxes (removedBox = null) {
+ const chatboxviews = _converse.chatboxviews.getAll();
+ return Object.keys(chatboxviews)
+ .filter(
+ id => chatboxviews[id].el.classList.contains('headlines') &&
+ id !== removedBox
+ );
+ }
+
+ function renderHeadlineList (removedBox = null) {
+ const controlboxview = _converse.chatboxviews.get('controlbox');
+ if (controlboxview !== undefined) {
+ const el = controlboxview.el.querySelector('.list-container--headline');
+ const headline_list = tpl_headline_list({
+ 'headlineboxes': getAllHeadlineBoxes(removedBox),
+ 'open_title': __('Click to open this server message'),
+ });
+ el && (el.outerHTML = headline_list);
+ }
+ }
async function onHeadlineMessage (message) {
// Handler method for all incoming messages of type "headline".
@@ -106,9 +160,45 @@ converse.plugins.add('converse-headlines', {
const attrs = await chatbox.getMessageAttributesFromStanza(message, message);
await chatbox.messages.create(attrs);
_converse.api.trigger('message', {'chatbox': chatbox, 'stanza': message});
+ renderHeadlineList();
+ _converse.chatboxviews.get(from_jid).el
+ .querySelector('.close-chatbox-button')
+ .addEventListener("click",
+ () => renderHeadlineList(from_jid)
+ );
}
}
+ /**
+ * Backbone.NativeView which renders headlines section of the control box.
+ * @class
+ * @namespace _converse.HeadlinePanel
+ * @memberOf _converse
+ */
+ _converse.HeadlinePanel = Backbone.NativeView.extend({
+ tagName: 'div',
+ className: 'controlbox-section',
+ id: 'headline',
+
+ events: {
+ 'click .open-headline': 'openHeadline'
+ },
+
+ openHeadline (ev) {
+ ev.preventDefault();
+ const jid = ev.target.getAttribute('data-headline-jid');
+ const chat = _converse.chatboxes.get(jid);
+ chat.maybeShow(true);
+ },
+
+ render () {
+ this.el.innerHTML = tpl_headline_panel({
+ 'heading_headline': __('Server Messages')
+ });
+ return this;
+ }
+ });
+
/************************ BEGIN Event Handlers ************************/
function registerHeadlineHandler () {
diff --git a/src/templates/headline_list.html b/src/templates/headline_list.html
new file mode 100644
index 000000000..a7b81a5f4
--- /dev/null
+++ b/src/templates/headline_list.html
@@ -0,0 +1,12 @@
+
+
+ {[o.headlineboxes.forEach(function (headline) { ]}
+
+ {[ }) ]}
+
+
diff --git a/src/templates/headline_panel.html b/src/templates/headline_panel.html
new file mode 100644
index 000000000..9dc7f4e3f
--- /dev/null
+++ b/src/templates/headline_panel.html
@@ -0,0 +1,6 @@
+
+
+ {{{o.heading_headline}}}
+
+
+