Allow setting a nickname when adding a roster contact

This commit is contained in:
JC Brand 2018-03-26 17:55:12 +02:00
parent 108bd03e3a
commit 4f2b040f22
3 changed files with 101 additions and 38 deletions

View File

@ -197,12 +197,27 @@
return test_utils.waitUntil(function () { return test_utils.waitUntil(function () {
return u.isVisible(modal.el); return u.isVisible(modal.el);
}, 1000).then(function () { }, 1000).then(function () {
var sendIQ = _converse.connection.sendIQ;
var sent_stanza, IQ_id;
spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
sent_stanza = iq;
IQ_id = sendIQ.bind(this)(iq, callback, errback);
});
expect(!_.isNull(modal.el.querySelector('form.add-xmpp-contact'))).toBeTruthy(); expect(!_.isNull(modal.el.querySelector('form.add-xmpp-contact'))).toBeTruthy();
var input_el = modal.el.querySelector('input[name="jid"]'); var input_jid = modal.el.querySelector('input[name="jid"]');
input_el.value = 'someone@'; var input_name = modal.el.querySelector('input[name="name"]');
input_jid.value = 'someone@';
var evt = new Event('input'); var evt = new Event('input');
input_el.dispatchEvent(evt); input_jid.dispatchEvent(evt);
expect(modal.el.querySelector('.awesomplete li').textContent).toBe('someone@localhost'); expect(modal.el.querySelector('.awesomplete li').textContent).toBe('someone@localhost');
input_jid.value = 'someone@localhost';
input_name.value = 'Someone';
modal.el.querySelector('button[type="submit"]').click();
expect(sent_stanza.toLocaleString()).toEqual(
"<iq type='set' xmlns='jabber:client' id='"+IQ_id+"'>"+
"<query xmlns='jabber:iq:roster'><item jid='someone@localhost' name='Someone'/></query>"+
"</iq>");
done(); done();
}); });
})); }));
@ -231,6 +246,7 @@
return xhr; return xhr;
}); });
var input_el;
var panel = _converse.chatboxviews.get('controlbox').contactspanel; var panel = _converse.chatboxviews.get('controlbox').contactspanel;
var cbview = _converse.chatboxviews.get('controlbox'); var cbview = _converse.chatboxviews.get('controlbox');
cbview.el.querySelector('.add-contact').click() cbview.el.querySelector('.add-contact').click()
@ -238,15 +254,40 @@
return test_utils.waitUntil(function () { return test_utils.waitUntil(function () {
return u.isVisible(modal.el); return u.isVisible(modal.el);
}, 1000).then(function () { }, 1000).then(function () {
var input_el = modal.el.querySelector('input[name="jid"]'); input_el = modal.el.querySelector('input[name="name"]');
input_el.value = 'marty@'; input_el.value = 'marty';
var evt = new Event('input'); var evt = new Event('input');
input_el.dispatchEvent(evt); input_el.dispatchEvent(evt);
return test_utils.waitUntil(function () { return test_utils.waitUntil(function () {
return modal.el.querySelector('.awesomplete li'); return modal.el.querySelector('.awesomplete li');
}); }, 1000);
}).then(function () { }).then(function () {
expect(modal.el.querySelector('.awesomplete li').textContent).toBe('marty@mcfly.net'); var sendIQ = _converse.connection.sendIQ;
var sent_stanza, IQ_id;
spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
sent_stanza = iq;
IQ_id = sendIQ.bind(this)(iq, callback, errback);
});
expect(modal.el.querySelectorAll('.awesomplete li').length).toBe(1);
const suggestion = modal.el.querySelector('.awesomplete li');
expect(suggestion.textContent).toBe('Marty McFly');
// Can't trigger "mousedown" event so trigger the Awesomplete
// custom event which would have been triggered upon mousedown.
var evt = document.createEvent("HTMLEvents");
evt.initEvent('awesomplete-selectcomplete', true, true );
evt.text = {
'label': 'Marty McFly',
'value': 'marty@mcfly.net'
}
modal.el.dispatchEvent(evt);
expect(input_el.value).toBe('Marty McFly');
expect(modal.el.querySelector('input[name="jid"]').value).toBe('marty@mcfly.net');
modal.el.querySelector('button[type="submit"]').click();
expect(sent_stanza.toLocaleString()).toEqual(
"<iq type='set' xmlns='jabber:client' id='"+IQ_id+"'>"+
"<query xmlns='jabber:iq:roster'><item jid='marty@mcfly.net' name='Marty McFly'/></query>"+
"</iq>");
done(); done();
}); });
})); }));

View File

@ -131,8 +131,7 @@
_converse.AddContactModal = _converse.BootstrapModal.extend({ _converse.AddContactModal = _converse.BootstrapModal.extend({
events: { events: {
'submit form': 'addContactFromForm', 'submit form': 'addContactFromForm'
'submit form.search-xmpp-contact': 'searchContacts'
}, },
initialize () { initialize () {
@ -141,49 +140,66 @@
}, },
toHTML () { toHTML () {
const label_nickname = _converse.xhr_user_search_url ? __('Contact name') : __('Optional nickname');
return tpl_add_contact_modal(_.extend(this.model.toJSON(), { return tpl_add_contact_modal(_.extend(this.model.toJSON(), {
'_converse': _converse,
'heading_new_contact': __('Add a Contact'), 'heading_new_contact': __('Add a Contact'),
'label_xmpp_address': __('XMPP Address'), 'label_xmpp_address': __('XMPP Address'),
'label_nickname': __('Optional nickname'), 'label_nickname': label_nickname,
'contact_placeholder': __('name@example.org'), 'contact_placeholder': __('name@example.org'),
'label_add': __('Add'), 'label_add': __('Add'),
})); }));
}, },
afterRender () { afterRender () {
const input_el = this.el.querySelector('input[name="jid"]');
if (_converse.xhr_user_search_url && _.isString(_converse.xhr_user_search_url)) { if (_converse.xhr_user_search_url && _.isString(_converse.xhr_user_search_url)) {
const awesomplete = new Awesomplete(input_el, {'list': []}); this.initXHRAutoComplete();
const xhr = new window.XMLHttpRequest();
// `open` must be called after `onload` for mock/testing purposes.
xhr.onload = function () {
if (xhr.responseText) {
awesomplete.list = JSON.parse(xhr.responseText).map((i) => {
return {'label': i.fullname, 'value': i.jid};
});
awesomplete.evaluate();
}
};
input_el.addEventListener('input', _.debounce(() => {
xhr.open("GET", `${_converse.xhr_user_search_url}?q=${input_el.value}`, true);
xhr.send()
} , 500));
} else { } else {
const list = _.uniq(_converse.roster.map((item) => Strophe.getDomainFromJid(item.get('jid')))); this.initJIDAutoComplete();
new Awesomplete(input_el, {
'list': list,
'data': function (text, input) {
return input.slice(0, input.indexOf("@")) + "@" + text;
},
'filter': Awesomplete.FILTER_STARTSWITH
});
} }
}, },
initJIDAutoComplete () {
const input_el = this.el.querySelector('input[name="jid"]');
const list = _.uniq(_converse.roster.map((item) => Strophe.getDomainFromJid(item.get('jid'))));
new Awesomplete(input_el, {
'list': list,
'data': function (text, input) {
return input.slice(0, input.indexOf("@")) + "@" + text;
},
'filter': Awesomplete.FILTER_STARTSWITH
});
},
initXHRAutoComplete () {
const name_input = this.el.querySelector('input[name="name"]');
const jid_input = this.el.querySelector('input[name="jid"]');
const awesomplete = new Awesomplete(name_input, {'list': []});
const xhr = new window.XMLHttpRequest();
// `open` must be called after `onload` for mock/testing purposes.
xhr.onload = function () {
if (xhr.responseText) {
awesomplete.list = JSON.parse(xhr.responseText).map((i) => { //eslint-disable-line arrow-body-style
return {'label': i.fullname, 'value': i.jid};
});
awesomplete.evaluate();
}
};
name_input.addEventListener('input', _.debounce(() => {
xhr.open("GET", `${_converse.xhr_user_search_url}?q=${name_input.value}`, true);
xhr.send()
} , 300));
this.el.addEventListener('awesomplete-selectcomplete', (ev) => {
jid_input.value = ev.text.value;
name_input.value = ev.text.label;
});
},
addContactFromForm (ev) { addContactFromForm (ev) {
ev.preventDefault(); ev.preventDefault();
const data = new FormData(ev.target), const data = new FormData(ev.target),
jid = data.get('jid'); jid = data.get('jid'),
name = data.get('name');
if (!jid || _.compact(jid.split('@')).length < 2) { if (!jid || _.compact(jid.split('@')).length < 2) {
this.model.set({ this.model.set({
@ -191,7 +207,7 @@
'jid': jid 'jid': jid
}) })
} else { } else {
_converse.roster.addAndSubscribe(jid); _converse.roster.addAndSubscribe(jid, name);
this.model.clear(); this.model.clear();
this.modal.hide(); this.modal.hide();
} }

View File

@ -8,15 +8,21 @@
</div> </div>
<form class="converse-form add-xmpp-contact"> <form class="converse-form add-xmpp-contact">
<div class="modal-body"> <div class="modal-body">
<div class="form-group"> <div class="form-group {[ if (o._converse.xhr_user_search_url) { ]} hidden {[ } ]}">
<label class="clearfix" for="jid">{{{o.label_xmpp_address}}}:</label> <label class="clearfix" for="jid">{{{o.label_xmpp_address}}}:</label>
<input type="text" name="jid" required="required" value="{{{o.jid}}}" <input type="text" name="jid" required="required" value="{{{o.jid}}}"
class="form-control {[ if (o.error_message) { ]} is-invalid {[ } ]}" class="form-control {[ if (o.error_message) { ]} is-invalid {[ } ]}"
placeholder="{{{o.contact_placeholder}}}"> placeholder="{{{o.contact_placeholder}}}"/>
{[ if (o.error_message) { ]} {[ if (o.error_message) { ]}
<div class="invalid-feedback">{{{o.error_message}}}</div> <div class="invalid-feedback">{{{o.error_message}}}</div>
{[ } ]} {[ } ]}
</div> </div>
<div class="form-group">
<label class="clearfix" for="name">{{{o.label_nickname}}}:</label>
<input type="text" name="name" value="{{{o.nickname}}}"
class="form-control {[ if (o.error_message) { ]} is-invalid {[ } ]}"
placeholder="{{{o.nickname_placeholder}}}"/>
</div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="submit" class="btn btn-primary">{{{o.label_add}}}</button> <button type="submit" class="btn btn-primary">{{{o.label_add}}}</button>