diff --git a/.eslintrc.json b/.eslintrc.json index 34585d5b3..ebee35649 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -149,7 +149,7 @@ "no-negated-condition": "off", "no-negated-in-lhs": "error", "no-nested-ternary": "off", - "no-new": "error", + "no-new": "off", "no-new-func": "error", "no-new-object": "error", "no-new-require": "error", diff --git a/.travis.yml b/.travis.yml index 7d19c2ef7..c7e463858 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ addons: chrome: stable node_js: - 6 -install: make node_modules +install: make stamp-npm before_script: make serve_bg script: make check sudo: false diff --git a/CHANGES.md b/CHANGES.md index de5901acc..eeee15632 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,29 +2,19 @@ ## 4.0.0 (Unreleased) -## Removed configuration settings +## UI changes -Due to rewriting parts of the code, we regrettably had to remove certain -lesser-used configuration settings because the cost of adding them to the -new code was too high. +The UI is now based on Bootstrap4 and Flexbox is used extensively. -If you relied on any of these settings, you can reproduce their -functionality in your own 3rd party plugins, or you can [contact us](http://opkode.com/contact.html) -with regards to sponsoring development on reintroducing them. +## Configuration changes * Removed the `xhr_custom_status` and `xhr_custom_status_url` configuration settings. If you relied on these settings, you can instead listen for the [statusMessageChanged](https://conversejs.org/docs/html/events.html#contactstatusmessagechanged) event and make the XMLHttpRequest yourself. -* Removed the `xhr_user_search` and `xhr_user_search_url` configuration options. - -## Updated UI - -The UI is now rewritten with Bootstrap4 and Flexbox is used pretty much -everywhere. Unfortunately this means that in the overlayed view_mode, chat -boxes can no longer be resized horizontally (or diagonally). Perhaps a solution -for this can again be found, but time constraints meant that this feature had -to be removed. +* Removed `xhr_user_search` in favor of only accepting `xhr_user_search_url` as configuration option. +* The data returned from the `xhr_user_search_url` must now include the user's + `jid` instead of just an `id`. ### Bugfixes diff --git a/css/converse.css b/css/converse.css index 86790c259..2be604b4f 100644 --- a/css/converse.css +++ b/css/converse.css @@ -8586,20 +8586,23 @@ body.reset { #conversejs:not(.fullscreen) #minimized-chats .chat-head-message-count-hidden { display: none; } -#converse-embedded-chat, -#conversejs { - /* Pointer */ } - #converse-embedded-chat [hidden], - #conversejs [hidden] { - display: none; } - #converse-embedded-chat .visually-hidden, - #conversejs .visually-hidden { - position: absolute; - clip: rect(0, 0, 0, 0); } - #converse-embedded-chat div.awesomplete, - #conversejs div.awesomplete { - display: inline-block; - position: relative; } +#converse-embedded-chat [hidden], +#conversejs [hidden] { + display: none; } +#converse-embedded-chat .visually-hidden, +#conversejs .visually-hidden { + position: absolute; + clip: rect(0, 0, 0, 0); } +#converse-embedded-chat .form-group .awesomplete, +#conversejs .form-group .awesomplete { + width: 100%; } +#converse-embedded-chat div.awesomplete, +#conversejs div.awesomplete { + display: inline-block; + position: relative; } + #converse-embedded-chat div.awesomplete mark, + #conversejs div.awesomplete mark { + background: #FFB9A7; } #converse-embedded-chat div.awesomplete > input, #conversejs div.awesomplete > input { display: block; } @@ -8620,62 +8623,60 @@ body.reset { border: 1px solid rgba(0, 0, 0, 0.3); box-shadow: 0.05em 0.2em 0.6em rgba(0, 0, 0, 0.2); text-shadow: none; } + #converse-embedded-chat div.awesomplete > ul:before, + #conversejs div.awesomplete > ul:before { + content: ""; + position: absolute; + top: -.43em; + left: 1em; + width: 0; + height: 0; + background: white; + border: inherit; + border-right: 0; + border-bottom: 0; + -webkit-transform: rotate(45deg); + transform: rotate(45deg); } + #converse-embedded-chat div.awesomplete > ul > li, + #conversejs div.awesomplete > ul > li { + text-overflow: ellipsis; + overflow-x: hidden; + position: relative; + cursor: pointer; + padding: 1em; } +#converse-embedded-chat div.awesomplete > ul[hidden], +#converse-embedded-chat div.awesomplete > ul:empty, +#conversejs div.awesomplete > ul[hidden], +#conversejs div.awesomplete > ul:empty { + display: none; } +@supports (transform: scale(0)) { + #converse-embedded-chat div.awesomplete > ul, + #conversejs div.awesomplete > ul { + transition: 0.3s cubic-bezier(0.4, 0.2, 0.5, 1.4); + transform-origin: 1.43em -.43em; } #converse-embedded-chat div.awesomplete > ul[hidden], #converse-embedded-chat div.awesomplete > ul:empty, #conversejs div.awesomplete > ul[hidden], #conversejs div.awesomplete > ul:empty { - display: none; } - @supports (transform: scale(0)) { - #converse-embedded-chat div.awesomplete > ul, - #conversejs div.awesomplete > ul { - transition: 0.3s cubic-bezier(0.4, 0.2, 0.5, 1.4); - transform-origin: 1.43em -.43em; } - #converse-embedded-chat div.awesomplete > ul[hidden], - #converse-embedded-chat div.awesomplete > ul:empty, - #conversejs div.awesomplete > ul[hidden], - #conversejs div.awesomplete > ul:empty { - opacity: 0; - transform: scale(0); - display: block; - transition-timing-function: ease; } } - #converse-embedded-chat div.awesomplete > ul:before, - #conversejs div.awesomplete > ul:before { - content: ""; - position: absolute; - top: -.43em; - left: 1em; - width: 0; - height: 0; - background: white; - border: inherit; - border-right: 0; - border-bottom: 0; - -webkit-transform: rotate(45deg); - transform: rotate(45deg); } - #converse-embedded-chat div.awesomplete > ul > li, - #conversejs div.awesomplete > ul > li { - text-overflow: ellipsis; - overflow-x: hidden; - position: relative; - cursor: pointer; } - #converse-embedded-chat div.awesomplete > ul > li:hover, - #conversejs div.awesomplete > ul > li:hover { - background: #E77051; - color: white; } - #converse-embedded-chat div.awesomplete > ul > li[aria-selected="true"], - #conversejs div.awesomplete > ul > li[aria-selected="true"] { - background: #3d6d8f; - color: white; } - #converse-embedded-chat div.awesomplete mark, - #conversejs div.awesomplete mark { - background: #FFB9A7; } - #converse-embedded-chat div.awesomplete li:hover mark, - #conversejs div.awesomplete li:hover mark { - background: #A53214; - color: white; } - #converse-embedded-chat div.awesomplete li[aria-selected="true"] mark, - #conversejs div.awesomplete li[aria-selected="true"] mark { - background: #3d6b00; - color: inherit; } + opacity: 0; + transform: scale(0); + display: block; + transition-timing-function: ease; } } +#converse-embedded-chat div.awesomplete > ul > li:hover, +#conversejs div.awesomplete > ul > li:hover { + background: #E77051; + color: white; } +#converse-embedded-chat div.awesomplete > ul > li[aria-selected="true"], +#conversejs div.awesomplete > ul > li[aria-selected="true"] { + background: #3d6d8f; + color: white; } +#converse-embedded-chat div.awesomplete li:hover mark, +#conversejs div.awesomplete li:hover mark { + background: #A53214; + color: white; } +#converse-embedded-chat div.awesomplete li[aria-selected="true"] mark, +#conversejs div.awesomplete li[aria-selected="true"] mark { + background: #3d6b00; + color: inherit; } /*# sourceMappingURL=converse.css.map */ diff --git a/css/inverse.css b/css/inverse.css index 23df2a29b..d98b1d755 100644 --- a/css/inverse.css +++ b/css/inverse.css @@ -8698,20 +8698,23 @@ body { border: 1.2em solid #E7A151; border-top: 0.8em solid #E7A151; } -#converse-embedded-chat, -#conversejs { - /* Pointer */ } - #converse-embedded-chat [hidden], - #conversejs [hidden] { - display: none; } - #converse-embedded-chat .visually-hidden, - #conversejs .visually-hidden { - position: absolute; - clip: rect(0, 0, 0, 0); } - #converse-embedded-chat div.awesomplete, - #conversejs div.awesomplete { - display: inline-block; - position: relative; } +#converse-embedded-chat [hidden], +#conversejs [hidden] { + display: none; } +#converse-embedded-chat .visually-hidden, +#conversejs .visually-hidden { + position: absolute; + clip: rect(0, 0, 0, 0); } +#converse-embedded-chat .form-group .awesomplete, +#conversejs .form-group .awesomplete { + width: 100%; } +#converse-embedded-chat div.awesomplete, +#conversejs div.awesomplete { + display: inline-block; + position: relative; } + #converse-embedded-chat div.awesomplete mark, + #conversejs div.awesomplete mark { + background: #FFB9A7; } #converse-embedded-chat div.awesomplete > input, #conversejs div.awesomplete > input { display: block; } @@ -8732,62 +8735,60 @@ body { border: 1px solid rgba(0, 0, 0, 0.3); box-shadow: 0.05em 0.2em 0.6em rgba(0, 0, 0, 0.2); text-shadow: none; } + #converse-embedded-chat div.awesomplete > ul:before, + #conversejs div.awesomplete > ul:before { + content: ""; + position: absolute; + top: -.43em; + left: 1em; + width: 0; + height: 0; + background: white; + border: inherit; + border-right: 0; + border-bottom: 0; + -webkit-transform: rotate(45deg); + transform: rotate(45deg); } + #converse-embedded-chat div.awesomplete > ul > li, + #conversejs div.awesomplete > ul > li { + text-overflow: ellipsis; + overflow-x: hidden; + position: relative; + cursor: pointer; + padding: 1em; } +#converse-embedded-chat div.awesomplete > ul[hidden], +#converse-embedded-chat div.awesomplete > ul:empty, +#conversejs div.awesomplete > ul[hidden], +#conversejs div.awesomplete > ul:empty { + display: none; } +@supports (transform: scale(0)) { + #converse-embedded-chat div.awesomplete > ul, + #conversejs div.awesomplete > ul { + transition: 0.3s cubic-bezier(0.4, 0.2, 0.5, 1.4); + transform-origin: 1.43em -.43em; } #converse-embedded-chat div.awesomplete > ul[hidden], #converse-embedded-chat div.awesomplete > ul:empty, #conversejs div.awesomplete > ul[hidden], #conversejs div.awesomplete > ul:empty { - display: none; } - @supports (transform: scale(0)) { - #converse-embedded-chat div.awesomplete > ul, - #conversejs div.awesomplete > ul { - transition: 0.3s cubic-bezier(0.4, 0.2, 0.5, 1.4); - transform-origin: 1.43em -.43em; } - #converse-embedded-chat div.awesomplete > ul[hidden], - #converse-embedded-chat div.awesomplete > ul:empty, - #conversejs div.awesomplete > ul[hidden], - #conversejs div.awesomplete > ul:empty { - opacity: 0; - transform: scale(0); - display: block; - transition-timing-function: ease; } } - #converse-embedded-chat div.awesomplete > ul:before, - #conversejs div.awesomplete > ul:before { - content: ""; - position: absolute; - top: -.43em; - left: 1em; - width: 0; - height: 0; - background: white; - border: inherit; - border-right: 0; - border-bottom: 0; - -webkit-transform: rotate(45deg); - transform: rotate(45deg); } - #converse-embedded-chat div.awesomplete > ul > li, - #conversejs div.awesomplete > ul > li { - text-overflow: ellipsis; - overflow-x: hidden; - position: relative; - cursor: pointer; } - #converse-embedded-chat div.awesomplete > ul > li:hover, - #conversejs div.awesomplete > ul > li:hover { - background: #E77051; - color: white; } - #converse-embedded-chat div.awesomplete > ul > li[aria-selected="true"], - #conversejs div.awesomplete > ul > li[aria-selected="true"] { - background: #3d6d8f; - color: white; } - #converse-embedded-chat div.awesomplete mark, - #conversejs div.awesomplete mark { - background: #FFB9A7; } - #converse-embedded-chat div.awesomplete li:hover mark, - #conversejs div.awesomplete li:hover mark { - background: #A53214; - color: white; } - #converse-embedded-chat div.awesomplete li[aria-selected="true"] mark, - #conversejs div.awesomplete li[aria-selected="true"] mark { - background: #3d6b00; - color: inherit; } + opacity: 0; + transform: scale(0); + display: block; + transition-timing-function: ease; } } +#converse-embedded-chat div.awesomplete > ul > li:hover, +#conversejs div.awesomplete > ul > li:hover { + background: #E77051; + color: white; } +#converse-embedded-chat div.awesomplete > ul > li[aria-selected="true"], +#conversejs div.awesomplete > ul > li[aria-selected="true"] { + background: #3d6d8f; + color: white; } +#converse-embedded-chat div.awesomplete li:hover mark, +#conversejs div.awesomplete li:hover mark { + background: #A53214; + color: white; } +#converse-embedded-chat div.awesomplete li[aria-selected="true"] mark, +#conversejs div.awesomplete li[aria-selected="true"] mark { + background: #3d6b00; + color: inherit; } /*# sourceMappingURL=inverse.css.map */ diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 36628fbe9..3bafb6b5f 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -1535,3 +1535,38 @@ Example: whitelisted_plugins: ['myplugin'] }); }); + + +xhr_user_search_url +------------------- + +.. note:: + XHR stands for XMLHTTPRequest, and is meant here in the AJAX sense (Asynchronous JavaScript and XML). + +* Default: ``null`` + +There are two ways to add users. + +* The user inputs a valid JID (Jabber ID, aka XMPP address), and the user is added as a pending contact. +* The user inputs some text (for example part of a first name or last name), + an XHR (Ajax Request) will be made to a remote server, and a list of matches are returned. + The user can then choose one of the matches to add as a contact. + +By providing an XHR search URL, you're enabling the second mechanism. + +*What is expected from the remote server?* + +A default JSON encoded list of objects must be returned. Each object +corresponds to a matched user and needs the keys ``jid`` and ``fullname``. + +.. code-block:: javascript + + [{"jid": "marty@mcfly.net", "fullname": "Marty McFly"}, {"jid": "doc@brown.com", "fullname": "Doc Brown"}] + +.. note:: + Make sure your server script sets the header `Content-Type: application/json`. + +This is the URL to which an XHR GET request will be made to fetch user data from your remote server. +The query string will be included in the request with ``q`` as its key. + +The data returned must be a JSON encoded list of user JIDs. diff --git a/sass/_awesomplete.scss b/sass/_awesomplete.scss index d65d9df31..14d338d3c 100644 --- a/sass/_awesomplete.scss +++ b/sass/_awesomplete.scss @@ -7,31 +7,62 @@ clip: rect(0, 0, 0, 0); } + .form-group { + .awesomplete { + width: 100%; + } + } + div.awesomplete { display: inline-block; position: relative; - } + mark { + background: $lightest-red; + } - div.awesomplete > input { - display: block; - } + > input { + display: block; + } - div.awesomplete > ul { - position: absolute; - left: 0; - right: 0; - z-index: 1; - min-width: 100%; - box-sizing: border-box; - list-style: none; - padding: 0; - border-radius: .3em; - margin: .2em 0 0; - background: hsla(0,0%,100%,.9); - background: linear-gradient(to bottom right, white, hsla(0,0%,100%,.8)); - border: 1px solid rgba(0,0,0,.3); - box-shadow: .05em .2em .6em rgba(0,0,0,.2); - text-shadow: none; + > ul { + &:before { + content: ""; + position: absolute; + top: -.43em; + left: 1em; + width: 0; height: 0; + background: white; + border: inherit; + border-right: 0; + border-bottom: 0; + -webkit-transform: rotate(45deg); + transform: rotate(45deg); + } + + position: absolute; + left: 0; + right: 0; + z-index: 1; + min-width: 100%; + box-sizing: border-box; + list-style: none; + padding: 0; + border-radius: .3em; + margin: .2em 0 0; + background: hsla(0,0%,100%,.9); + background: linear-gradient(to bottom right, white, hsla(0,0%,100%,.8)); + border: 1px solid rgba(0,0,0,.3); + box-shadow: .05em .2em .6em rgba(0,0,0,.2); + text-shadow: none; + + > li { + text-overflow: ellipsis; + overflow-x: hidden; + position: relative; + cursor: pointer; + padding: 1em; + } + } } div.awesomplete > ul[hidden], @@ -53,28 +84,6 @@ transition-timing-function: ease; } } - - /* Pointer */ - div.awesomplete > ul:before { - content: ""; - position: absolute; - top: -.43em; - left: 1em; - width: 0; height: 0; - background: white; - border: inherit; - border-right: 0; - border-bottom: 0; - -webkit-transform: rotate(45deg); - transform: rotate(45deg); - } - - div.awesomplete > ul > li { - text-overflow: ellipsis; - overflow-x: hidden; - position: relative; - cursor: pointer; - } div.awesomplete > ul > li:hover { background: $red; @@ -86,10 +95,6 @@ color: white; } - div.awesomplete mark { - background: $lightest-red; - } - div.awesomplete li:hover mark { background: $darkest-red; color: $inverse-link-color; diff --git a/spec/chatroom.js b/spec/chatroom.js index 0704f6efe..57ae97ff8 100644 --- a/spec/chatroom.js +++ b/spec/chatroom.js @@ -1353,20 +1353,12 @@ $(view.el).find('.chat-area').remove(); test_utils.waitUntil(function () { - return $(view.el).find('input.invited-contact').length; + return $(view.el).find('input.invited-contact').length; }, 300).then(function () { var $input = $(view.el).find('input.invited-contact'); expect($input.attr('placeholder')).toBe('Invite'); $input.val("Felix"); - var evt; - // check if Event() is a constructor function - // usage as per the spec, if true - if (typeof(Event) === 'function') { - evt = new Event('input'); - } else { // the deprecated way for PhantomJS - evt = document.createEvent('CustomEvent'); - evt.initCustomEvent('input', false, false, null); - } + var evt = new Event('input'); $input[0].dispatchEvent(evt); var sent_stanza; diff --git a/spec/controlbox.js b/spec/controlbox.js index ff41a1fc1..db10ba3ae 100644 --- a/spec/controlbox.js +++ b/spec/controlbox.js @@ -33,6 +33,37 @@ describe("The \"Contacts\" section", function () { + it("can be used to add contact and it checks for case-sensivity", + mock.initConverseWithPromises( + null, ['rosterGroupsFetched'], {}, + function (done, _converse) { + + spyOn(_converse, 'emit'); + spyOn(_converse.rosterview, 'update').and.callThrough(); + test_utils.openControlBox(); + // Adding two contacts one with Capital initials and one with small initials of same JID (Case sensitive check) + _converse.roster.create({ + jid: mock.pend_names[0].replace(/ /g,'.').toLowerCase() + '@localhost', + subscription: 'none', + ask: 'subscribe', + fullname: mock.pend_names[0] + }); + _converse.roster.create({ + jid: mock.pend_names[0].replace(/ /g,'.') + '@localhost', + subscription: 'none', + ask: 'subscribe', + fullname: mock.pend_names[0] + }); + test_utils.waitUntil(function () { + return $(_converse.rosterview.el).find('.roster-group li:visible').length; + }, 700).then(function () { + // Checking that only one entry is created because both JID is same (Case sensitive check) + expect($(_converse.rosterview.el).find('li:visible').length).toBe(1); + expect(_converse.rosterview.update).toHaveBeenCalled(); + done(); + }); + })); + it("shows the number of unread mentions received", mock.initConverseWithPromises( null, ['rosterGroupsFetched'], {}, @@ -157,6 +188,8 @@ null, ['rosterGroupsFetched'], {}, function (done, _converse) { + test_utils.createContacts(_converse, 'all').openControlBox(); + var panel = _converse.chatboxviews.get('controlbox').contactspanel; var cbview = _converse.chatboxviews.get('controlbox'); cbview.el.querySelector('.add-contact').click() @@ -165,37 +198,55 @@ return u.isVisible(modal.el); }, 1000).then(function () { expect(!_.isNull(modal.el.querySelector('form.add-xmpp-contact'))).toBeTruthy(); + var input_el = modal.el.querySelector('input[name="jid"]'); + input_el.value = 'someone@'; + var evt = new Event('input'); + input_el.dispatchEvent(evt); + expect(modal.el.querySelector('.awesomplete li').textContent).toBe('someone@localhost'); done(); }); })); - it("can be used to add contact and it checks for case-sensivity", + + it("integrates with xhr_user_search_url to search for contacts", mock.initConverseWithPromises( - null, ['rosterGroupsFetched'], {}, + null, ['rosterGroupsFetched'], + { 'xhr_user_search': true, + 'xhr_user_search_url': 'http://example.org/' + }, function (done, _converse) { - spyOn(_converse, 'emit'); - spyOn(_converse.rosterview, 'update').and.callThrough(); - test_utils.openControlBox(); - // Adding two contacts one with Capital initials and one with small initials of same JID (Case sensitive check) - _converse.roster.create({ - jid: mock.pend_names[0].replace(/ /g,'.').toLowerCase() + '@localhost', - subscription: 'none', - ask: 'subscribe', - fullname: mock.pend_names[0] + var xhr = { + 'open': _.noop, + 'send': function () { + xhr.responseText = JSON.stringify([ + {"jid": "marty@mcfly.net", "fullname": "Marty McFly"}, + {"jid": "doc@brown.com", "fullname": "Doc Brown"} + ]); + xhr.onload(); + } + }; + window.XMLHttpRequest = jasmine.createSpy('XMLHttpRequest'); + XMLHttpRequest.and.callFake(function () { + return xhr; }); - _converse.roster.create({ - jid: mock.pend_names[0].replace(/ /g,'.') + '@localhost', - subscription: 'none', - ask: 'subscribe', - fullname: mock.pend_names[0] - }); - test_utils.waitUntil(function () { - return $(_converse.rosterview.el).find('.roster-group li:visible').length; - }, 700).then(function () { - // Checking that only one entry is created because both JID is same (Case sensitive check) - expect($(_converse.rosterview.el).find('li:visible').length).toBe(1); - expect(_converse.rosterview.update).toHaveBeenCalled(); + + var panel = _converse.chatboxviews.get('controlbox').contactspanel; + var cbview = _converse.chatboxviews.get('controlbox'); + cbview.el.querySelector('.add-contact').click() + var modal = _converse.rosterview.add_contact_modal; + return test_utils.waitUntil(function () { + return u.isVisible(modal.el); + }, 1000).then(function () { + var input_el = modal.el.querySelector('input[name="jid"]'); + input_el.value = 'marty@'; + var evt = new Event('input'); + input_el.dispatchEvent(evt); + return test_utils.waitUntil(function () { + return modal.el.querySelector('.awesomplete li'); + }); + }).then(function () { + expect(modal.el.querySelector('.awesomplete li').textContent).toBe('marty@mcfly.net'); done(); }); })); diff --git a/src/converse-rosterview.js b/src/converse-rosterview.js index 05fad0e7d..dc1b6e038 100644 --- a/src/converse-rosterview.js +++ b/src/converse-rosterview.js @@ -16,6 +16,7 @@ "tpl!roster_filter", "tpl!roster_item", "tpl!search_contact", + "awesomplete", "converse-chatboxes", "converse-modal" ], factory); @@ -28,7 +29,8 @@ tpl_roster, tpl_roster_filter, tpl_roster_item, - tpl_search_contact + tpl_search_contact, + Awesomplete ) { "use strict"; const { Backbone, Strophe, $iq, b64_sha1, sizzle, _ } = converse.env; @@ -78,9 +80,10 @@ { __ } = _converse; _converse.api.settings.update({ - allow_chat_pending_contacts: true, - allow_contact_removal: true, - show_toolbar: true, + 'allow_chat_pending_contacts': true, + 'allow_contact_removal': true, + 'show_toolbar': true, + 'xhr_user_search_url': null }); _converse.api.promises.add('rosterViewInitialized'); @@ -147,6 +150,31 @@ })); }, + afterRender () { + const input_el = this.el.querySelector('input[name="jid"]'); + if (_converse.xhr_user_search_url && _.isString(_converse.xhr_user_search_url)) { + const awesomplete = new Awesomplete(input_el, {'list': [], 'minChars': 2}); + const xhr = new window.XMLHttpRequest(); + // `open` must be called after `onload` for + // mock/testing purposes. + xhr.onload = function () { + awesomplete.list = JSON.parse(xhr.responseText).map((i) => i.jid); + awesomplete.evaluate(); + }; + xhr.open("GET", _converse.xhr_user_search_url, true); + input_el.addEventListener('input', _.debounce(() => xhr.send()), 100, {'leading': true}); + } else { + 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 + }); + } + }, + addContactFromForm (ev) { ev.preventDefault(); const data = new FormData(ev.target), diff --git a/src/templates/add_contact_form.html b/src/templates/add_contact_form.html deleted file mode 100644 index a831dbf08..000000000 --- a/src/templates/add_contact_form.html +++ /dev/null @@ -1,11 +0,0 @@ -
- {[ if (o.error_message) { ]} - {{{o.error_message}}} - {[ } ]} - - -
diff --git a/src/templates/add_contact_modal.html b/src/templates/add_contact_modal.html index b131702c1..bac3b7dfb 100644 --- a/src/templates/add_contact_modal.html +++ b/src/templates/add_contact_modal.html @@ -9,7 +9,7 @@