Skip to content

Commit

Permalink
add message-forwarding functionality
Browse files Browse the repository at this point in the history
Adds a new action button to messages in the chat-history for message-
forwarding. The Button opens a modal dialog where the user can enter
a destination for the message. The destination can be a user in the
roster or a MUC that is currently opened.
  • Loading branch information
ibygsd committed Sep 26, 2019
1 parent 44e5b4c commit 6959b2b
Show file tree
Hide file tree
Showing 17 changed files with 491 additions and 114 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## 5.0.4

- add message-forward (XEP-0297) capabilities
- Bugfix: Don't treat every duplicate message ID as a message correction; since some
clients don't use globally unique ID's this causes false positives.
- #1712: `TypeError: plugin._features is not a function`
Expand Down
30 changes: 30 additions & 0 deletions locale/converse.pot
Original file line number Diff line number Diff line change
Expand Up @@ -1892,3 +1892,33 @@ msgstr ""
#: dist/converse-no-dependencies.js:54513
msgid "Re-sync your contacts"
msgstr ""

msgid "You can only send a message to an existing contact or an opened room."
msgstr ""

msgid "forward this message"
msgstr ""

msgid "Destination:"
msgstr ""

msgid "Additional Message:"
msgstr ""

msgid "Original-Text"
msgstr ""

msgid "Optional: Add additional message here..."
msgstr ""

msgid "forward"
msgstr ""

msgid "Forwarded Message:"
msgstr ""

msgid "original author:"
msgstr ""

msgid "time:"
msgstr ""
30 changes: 30 additions & 0 deletions locale/de/LC_MESSAGES/converse.po
Original file line number Diff line number Diff line change
Expand Up @@ -2471,3 +2471,33 @@ msgstr "Resynchronisieren Sie Ihre Kontakte"

#~ msgid "Contact username"
#~ msgstr "Benutzername"

msgid "You can only send a message to an existing contact or an opened room."
msgstr "Sie können eine Nachricht nur an einen existieren Kontakt oder offenen Chatraum senden."

msgid "forward this message"
msgstr "Nachricht weiterleiten"

msgid "Destination:"
msgstr "Empfänger:"

msgid "Additional Message:"
msgstr "Zusätzliche Nachricht"

msgid "Original-Text"
msgstr "Ursprüngliche Nachricht"

msgid "Optional: Add additional message here..."
msgstr "Optional: Geben Sie hier eine zusätzliche Nachricht ein..."

msgid "forward"
msgstr "weiterleiten"

msgid "Forwarded Message:"
msgstr "Weitergeleitete Nachricht:"

msgid "original author:"
msgstr "Ursprünglicher Autor:"

msgid "time:"
msgstr "Zeit:"
8 changes: 8 additions & 0 deletions sass/_autocomplete.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@
.suggestion-box {
width: 100%;
}
.forward-message {
overflow: visible;
border: black;
height: 100px;
border-radius: 5px;
background-color: lightgrey;
padding: 5px;
}
}

.suggestion-box {
Expand Down
28 changes: 24 additions & 4 deletions sass/_messages.scss
Original file line number Diff line number Diff line change
Expand Up @@ -218,19 +218,21 @@
width: 100%;
}
}

.chat-msg__actions {
width: 50px;
.chat-msg__action {
height: var(--message-font-size);
font-size: var(--message-font-size);
padding: 0;
border: none;
opacity: 0;
background: transparent;
width: 10px;
cursor: pointer;
&:focus {
display: block;
}
display: block;
margin: 0px 0px 0px 10px;
float: right;
}
}

Expand Down Expand Up @@ -310,6 +312,24 @@
margin-right: 0.5em;
color: var(--message-receipt-color);
}

.forwarded-message {
white-space: normal;
background-color: lightblue;
border-radius: 5px;
padding: 5px;
margin-left: 5px;
}

.forwarded-message__content {
background-color: white;
border-radius: 5px;
padding-left: 5px;
}

.forwarded-message__header {
font-size: 11px;
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions spec/controlbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@
`<presence xmlns="jabber:client">`+
`<show>dnd</show>`+
`<priority>0</priority>`+
`<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
`<c hash="sha-1" node="https://conversejs.org" ver="Js7MzULrV40dmSBGeP+rd0MNV9c=" xmlns="http://jabber.org/protocol/caps"/>`+
`</presence>`);
const first_child = view.el.querySelector('.xmpp-status span:first-child');
expect(u.hasClass('online', first_child)).toBe(false);
Expand Down Expand Up @@ -178,7 +178,7 @@
`<presence xmlns="jabber:client">`+
`<status>I am happy</status>`+
`<priority>0</priority>`+
`<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
`<c hash="sha-1" node="https://conversejs.org" ver="Js7MzULrV40dmSBGeP+rd0MNV9c=" xmlns="http://jabber.org/protocol/caps"/>`+
`</presence>`);

const first_child = view.el.querySelector('.xmpp-status span:first-child');
Expand Down
48 changes: 2 additions & 46 deletions spec/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,50 +12,6 @@

describe("A Chat Message", function () {

it("is rejected if it's an unencapsulated forwarded message",
mock.initConverse(
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
async function (done, _converse) {

await test_utils.waitForRoster(_converse, 'current', 2);
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);
const received_stanza = u.toStanza(`
<message to='${_converse.jid}' from='${contact_jid}' type='chat' id='${_converse.connection.getUniqueId()}'>
<body>A most courteous exposition!</body>
<forwarded xmlns='urn:xmpp:forward:0'>
<delay xmlns='urn:xmpp:delay' stamp='2019-07-10T23:08:25Z'/>
<message from='${forwarded_contact_jid}'
id='0202197'
to='${_converse.bare_jid}'
type='chat'
xmlns='jabber:client'>
<body>Yet I should kill thee with much cherishing.</body>
<mood xmlns='http://jabber.org/protocol/mood'>
<amorous/>
</mood>
</message>
</forwarded>
</message>
`);
_converse.connection._dataRecv(test_utils.createRequest(received_stanza));
const view = _converse.api.chatviews.get(contact_jid);
const sent_stanzas = _converse.connection.sent_stanzas;
const sent_stanza = await u.waitUntil(() => sent_stanzas.filter(s => s.querySelector('error')).pop());
expect(Strophe.serialize(sent_stanza)).toBe(
`<message id="${received_stanza.getAttribute('id')}" to="${contact_jid}" type="error" xmlns="jabber:client">`+
'<error type="cancel">'+
'<not-allowed xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>'+
'<text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">'+
'Forwarded messages not part of an encapsulating protocol are not supported</text>'+
'</error>'+
'</message>');
expect(_converse.api.chats.get().length).toBe(2);
done();
}));

it("can be sent as a correction by clicking the pencil icon",
mock.initConverse(
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
Expand All @@ -82,7 +38,7 @@
expect(textarea.value).toBe('');

const first_msg = view.model.messages.findWhere({'message': 'But soft, what light through yonder airlock breaks?'});
expect(view.el.querySelectorAll('.chat-msg .chat-msg__action').length).toBe(1);
expect(view.el.querySelectorAll('.chat-msg .chat-msg__action').length).toBe(2);
let action = view.el.querySelector('.chat-msg .chat-msg__action');
expect(action.getAttribute('title')).toBe('Edit this message');

Expand Down Expand Up @@ -159,7 +115,7 @@
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree()
);
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
expect(view.el.querySelectorAll('.chat-msg .chat-msg__action').length).toBe(1);
expect(view.el.querySelectorAll('.chat-msg .chat-msg__action').length).toBe(3);

// Test confirmation dialog
spyOn(window, 'confirm').and.returnValue(true);
Expand Down
36 changes: 0 additions & 36 deletions spec/muc_messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,6 @@

describe("A Groupchat Message", function () {

it("is rejected if it's an unencapsulated forwarded message",
mock.initConverse(
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
async function (done, _converse) {

const muc_jid = '[email protected]';
await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
const impersonated_jid = `${muc_jid}/alice`;
const received_stanza = u.toStanza(`
<message to='${_converse.jid}' from='${muc_jid}/mallory' type='groupchat' id='${_converse.connection.getUniqueId()}'>
<forwarded xmlns='urn:xmpp:forward:0'>
<delay xmlns='urn:xmpp:delay' stamp='2019-07-10T23:08:25Z'/>
<message from='${impersonated_jid}'
id='0202197'
to='${_converse.bare_jid}'
type='groupchat'
xmlns='jabber:client'>
<body>Yet I should kill thee with much cherishing.</body>
</message>
</forwarded>
</message>
`);
const view = _converse.api.chatviews.get(muc_jid);
await view.model.onMessage(received_stanza);
spyOn(_converse, 'log');
_converse.connection._dataRecv(test_utils.createRequest(received_stanza));
expect(_converse.log).toHaveBeenCalledWith(
'onMessage: Ignoring unencapsulated forwarded groupchat message',
Strophe.LogLevel.WARN
);
expect(view.el.querySelectorAll('.chat-msg').length).toBe(0);
expect(view.model.messages.length).toBe(0);
done();
}));


it("is specially marked when you are mentioned in it",
mock.initConverse(
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
Expand Down
10 changes: 5 additions & 5 deletions spec/presence.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
`<presence xmlns="jabber:client">`+
`<status>Hello world</status>`+
`<priority>0</priority>`+
`<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
`<c hash="sha-1" node="https://conversejs.org" ver="Js7MzULrV40dmSBGeP+rd0MNV9c=" xmlns="http://jabber.org/protocol/caps"/>`+
`</presence>`
);
_converse.priority = 2;
Expand All @@ -56,7 +56,7 @@
`<show>away</show>`+
`<status>Going jogging</status>`+
`<priority>2</priority>`+
`<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
`<c hash="sha-1" node="https://conversejs.org" ver="Js7MzULrV40dmSBGeP+rd0MNV9c=" xmlns="http://jabber.org/protocol/caps"/>`+
`</presence>`
);

Expand All @@ -67,7 +67,7 @@
`<show>dnd</show>`+
`<status>Doing taxes</status>`+
`<priority>0</priority>`+
`<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
`<c hash="sha-1" node="https://conversejs.org" ver="Js7MzULrV40dmSBGeP+rd0MNV9c=" xmlns="http://jabber.org/protocol/caps"/>`+
`</presence>`
);
done();
Expand Down Expand Up @@ -95,7 +95,7 @@
.toBe(`<presence xmlns="jabber:client">`+
`<status>My custom status</status>`+
`<priority>0</priority>`+
`<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
`<c hash="sha-1" node="https://conversejs.org" ver="Js7MzULrV40dmSBGeP+rd0MNV9c=" xmlns="http://jabber.org/protocol/caps"/>`+
`</presence>`)

await u.waitUntil(() => modal.el.getAttribute('aria-hidden') === "true");
Expand All @@ -105,7 +105,7 @@
modal.el.querySelector('[type="submit"]').click();
expect(_converse.connection.send.calls.mostRecent().args[0].toLocaleString())
.toBe(`<presence xmlns="jabber:client"><show>dnd</show><status>My custom status</status><priority>0</priority>`+
`<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
`<c hash="sha-1" node="https://conversejs.org" ver="Js7MzULrV40dmSBGeP+rd0MNV9c=" xmlns="http://jabber.org/protocol/caps"/>`+
`</presence>`)
done();
}));
Expand Down
Loading

0 comments on commit 6959b2b

Please sign in to comment.