Commit 584f293d authored by JC Brand's avatar JC Brand

Updated and refactored the work from @worlword

* Use Promises instead of callbacks
* Update to latest (Last Call) version of XEP-0363
* Move non-view specific methods to models instead
* Add more tests

updates #161
parent 9c2a5bd3
......@@ -91,7 +91,7 @@
"max-depth": "error",
"max-len": "off",
"max-lines": "off",
"max-nested-callbacks": "error",
"max-nested-callbacks": "off",
"max-params": "off",
"max-statements": "off",
"max-statements-per-line": "off",
......
......@@ -173,6 +173,7 @@
describe("When supported", function () {
describe("A file upload toolbar button", function () {
it("appears in private chats", mock.initConverseWithAsync(function (done, _converse) {
......@@ -201,6 +202,92 @@
it("appears in MUC chats", mock.initConverseWithAsync(function (done, _converse) {
done();
}));
describe("when clicked", function () {
it("a file upload slot is requested", mock.initConverseWithAsync(function (done, _converse) {
test_utils.waitUntilDiscoConfirmed(
_converse, _converse.domain,
[{'category': 'server', 'type':'IM'}],
['http://jabber.org/protocol/disco#items'], [], 'info').then(function () {
var IQ_stanzas = _converse.connection.IQ_stanzas;
test_utils.waitUntilDiscoConfirmed(_converse, _converse.domain, [], [], ['upload.montague.tld'], 'items').then(function () {
test_utils.waitUntilDiscoConfirmed(_converse, 'upload.montague.tld', [], [Strophe.NS.HTTPUPLOAD], []).then(function () {
test_utils.createContacts(_converse, 'current');
var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(_converse, contact_jid);
var view = _converse.chatboxviews.get(contact_jid);
var file = {
'type': 'image/jpeg',
'size': '23456' ,
'lastModifiedDate': "",
'name': "my-juliet.jpg"
};
view.model.sendFile(file);
return test_utils.waitUntil(function () {
return _.filter(IQ_stanzas, function (iq) {
return iq.nodeTree.querySelector('iq[to="upload.montague.tld"] request');
});
}).then(function () {
var iq = IQ_stanzas.pop();
expect(iq.toLocaleString()).toBe(
"<iq from='dummy@localhost/resource' "+
"to='upload.montague.tld' "+
"type='get' "+
"xmlns='jabber:client' "+
"id='"+iq.nodeTree.getAttribute('id')+"'>"+
"<request xmlns='urn:xmpp:http:upload:0' "+
"filename='my-juliet.jpg' "+
"size='23456' "+
"content-type='image/jpeg'/>"+
"</iq>");
var stanza = Strophe.xmlHtmlNode(
"<iq from='upload.montague.tld'"+
" id='"+iq.nodeTree.getAttribute('id')+"'"+
" to='dummy@localhost/resource'"+
" type='result'>"+
"<slot xmlns='urn:xmpp:http:upload:0'>"+
" <put url='https://upload.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my-juliet.jpg'>"+
" <header name='Authorization'>Basic Base64String==</header>"+
" <header name='Cookie'>foo=bar; user=romeo</header>"+
" </put>"+
" <get url='https://download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my-juliet.jpg' />"+
"</slot>"+
"</iq>").firstElementChild;
spyOn(view.model, 'uploadFile').and.callFake(function () {
return new window.Promise((resolve, reject) => { resolve(); });
});
var sent_stanza;
spyOn(_converse.connection, 'send').and.callFake(function (stanza) {
sent_stanza = stanza;
});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
return test_utils.waitUntil(function () {
return sent_stanza;
}).then(function () {
expect(view.model.uploadFile).toHaveBeenCalled();
expect(sent_stanza.toLocaleString()).toBe(
"<message from='dummy@localhost/resource' "+
"to='irini.vlastuin@localhost' "+
"type='chat' "+
"id='"+sent_stanza.nodeTree.getAttribute('id')+"' xmlns='jabber:client'>"+
"<body>https://download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my-juliet.jpg</body>"+
"<active xmlns='http://jabber.org/protocol/chatstates'/>"+
"<x xmlns='jabber:x:oob'>"+
"<url>https://download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my-juliet.jpg</url>"+
"</x>"+
"</message>");
done();
});
});
});
});
});
}));
});
});
});
});
......
......@@ -37,6 +37,7 @@ require.config({
"lodash.converter": "3rdparty/lodash.fp",
"lodash.fp": "src/lodash.fp",
"lodash.noconflict": "src/lodash.noconflict",
"message-utils": "src/utils/message",
"muc-utils": "src/utils/muc",
"pluggable": "node_modules/pluggable.js/dist/pluggable",
"polyfill": "src/polyfill",
......
This diff is collapsed.
......@@ -268,7 +268,6 @@
this.model.on('change:chat_state', this.sendChatState, this);
this.model.on('change:chat_status', this.onChatStatusChanged, this);
this.model.on('showHelpMessages', this.showHelpMessages, this);
this.model.on('sendMessage', this.sendMessage, this);
this.render();
this.fetchMessages();
_converse.emit('chatBoxOpened', this);
......@@ -372,8 +371,8 @@
}
return _.extend(options || {}, {
'label_clear': __('Clear all messages'),
'label_insert_smiley': __('Insert a smiley'),
'label_start_call': __('Start a call'),
'tooltip_insert_smiley': __('Insert emojis'),
'tooltip_start_call': __('Start a call'),
'label_toggle_spoiler': label_toggle_spoiler,
'show_call_button': _converse.visible_toolbar_buttons.call,
'show_spoiler_button': _converse.visible_toolbar_buttons.spoiler,
......@@ -666,7 +665,7 @@
'beforeend',
tpl_help_message({
'isodate': moment().format(),
'type': type||'info',
'type': type,
'message': xss.filterXSS(msg, {'whiteList': {'strong': []}})
})
);
......@@ -796,55 +795,6 @@
});
},
createMessageStanza (message) {
const stanza = $msg({
'from': _converse.connection.jid,
'to': this.model.get('jid'),
'type': 'chat',
'id': message.get('msgid')
}).c('body').t(message.get('message')).up()
.c(_converse.ACTIVE, {'xmlns': Strophe.NS.CHATSTATES}).up();
if (message.get('is_spoiler')) {
if (message.get('spoiler_hint')) {
stanza.c('spoiler', {'xmlns': Strophe.NS.SPOILER }, message.get('spoiler_hint'));
} else {
stanza.c('spoiler', {'xmlns': Strophe.NS.SPOILER });
}
}
return stanza;
},
sendMessage (message, file = null) {
/* Responsible for sending off a text message.
*
* Parameters:
* (Message) message - The chat message
*/
// TODO: We might want to send to specfic resources.
// Especially in the OTR case.
var messageStanza;
if (file !== null) {
messageStanza = this.model.createFileMessageStanza(message, this.model.get('jid'));
}
else {
messageStanza = this.createMessageStanza(message);
}
_converse.connection.send(messageStanza);
if (_converse.forward_messages) {
// Forward the message, so that other connected resources are also aware of it.
_converse.connection.send(
$msg({ to: _converse.bare_jid, type: 'chat', id: message.get('msgid') })
.c('forwarded', {'xmlns': Strophe.NS.FORWARD})
.c('delay', {
'xmns': Strophe.NS.DELAY,
'stamp': moment().format()
}).up()
.cnode(messageStanza.tree())
);
}
},
parseMessageForCommands (text) {
const match = text.replace(/^\s*/, "").match(/^\/(.*)\s*$/);
if (match) {
......@@ -864,7 +814,7 @@
}
},
onMessageSubmitted (text, spoiler_hint, file = null) {
onMessageSubmitted (text, spoiler_hint, file=null) {
/* This method gets called once the user has typed a message
* and then pressed enter in a chat box.
*
......@@ -884,8 +834,7 @@
return;
}
const attrs = this.getOutgoingMessageAttributes(text, spoiler_hint);
const message = this.model.messages.create(attrs);
this.sendMessage(message, file);
this.model.sendMessage(attrs, file);
},
getOutgoingMessageAttributes (text, spoiler_hint) {
......
......@@ -32,7 +32,16 @@
ChatBoxView: {
events: {
'click .upload-file': 'toggleFileUpload',
'change input.fileupload': 'handleFileSelect'
'change input.fileupload': 'onFileSelection'
},
toggleFileUpload (ev) {
this.el.querySelector('input.fileupload').click();
},
onFileSelection (evt) {
this.model.sendFiles(evt.target.files);
},
addFileUploadButton (options) {
......@@ -45,30 +54,19 @@
renderToolbar (toolbar, options) {
const { _converse } = this.__super__;
const result = this.__super__.renderToolbar.apply(this, arguments);
_converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain)
.then((result) => {
if (result.length) {
this.addFileUploadButton();
}
});
_converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain).then((result) => {
if (result.length) {
this.addFileUploadButton();
}
});
return result;
},
toggleFileUpload (ev) {
this.el.querySelector('.input.fileupload').click();
},
handleFileSelect (evt) {
var files = evt.target.files;
var file = files[0];
this.model.sendFile(file, this);
}
},
ChatRoomView: {
events: {
'click .upload-file': 'toggleFileUpload',
'change .input.fileupload': 'handleFileSelect'
'change .input.fileupload': 'onFileSelection'
}
}
}
......
......@@ -224,7 +224,7 @@
this.trigger('showReceivedOTRMessage', msg);
});
this.otr.on('io', (msg) => {
this.trigger('sendMessage', new _converse.Message({ message: msg }));
this.sendMessage(new _converse.Message({'message':msg}));
});
this.otr.on('error', (msg) => {
this.trigger('showOTRError', msg);
......
<div class="message chat-{{{o.type}}}" data-isodate="{{{o.isodate}}}">{{o.message}}</div>
<div class="message chat-info {[ if (o.type !== 'info') { ]} chat-{{{o.type}}} {[ } ]}" data-isodate="{{{o.isodate}}}">{{o.message}}</div>
{[ if (o.use_emoji) { ]}
<li class="toggle-toolbar-menu toggle-smiley fa fa-smile-o dropup">
<li class="toggle-toolbar-menu toggle-smiley dropup">
<a class="btn toggle-smiley fa fa-smile-o" title="{{{o.tooltip_insert_smiley}}}" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></a>
<div class="emoji-picker dropdown-menu toolbar-menu"></div>
</li>
{[ } ]}
......
<input type="file" class="fileupload" style="display:none"/>
<input type="file" class="fileupload" multiple style="display:none"/>
<li class="upload-file">
<a class="fa fa-paperclip" title="{{{o.tooltip_upload_file}}}"></a>
</li>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment