Browse Source

New modal for adding contacts.

Remove the xhr_user_search and xhr_user_search_url options
Lazily create modals
JC Brand 7 years ago
parent
commit
e344bf769a

+ 11 - 0
CHANGES.md

@@ -2,10 +2,21 @@
 
 ## 4.0.0 (Unreleased)
 
+## Removed configuration settings
+
+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.
+
+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.
+
 * 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.
 
 
 ## 3.3.4 (Unreleased)

+ 0 - 45
docs/source/configuration.rst

@@ -1524,48 +1524,3 @@ An example from `the embedded room demo <https://conversejs.org/demo/embedded.ht
             whitelisted_plugins: ['converse-muc-embedded']
         });
     });
-
-
-xhr_user_search
----------------
-
-* Default:  ``false``
-
-.. note::
-    XHR stands for XMLHTTPRequest, and is meant here in the AJAX sense (Asynchronous JavaScript and XML).
-
-There are two ways to add users.
-
-* The user inputs a valid JID (Jabber ID), 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.
-
-This setting enables the second mechanism, otherwise by default the first will be used.
-
-*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 ``id`` and ``fullname``.
-
-.. code-block:: javascript
-
-    [{"id": "foo", "fullname": "Foo McFoo"}, {"id": "bar", "fullname": "Bar McBar"}]
-
-.. note::
-    Make sure your server script sets the header `Content-Type: application/json`.
-
-xhr_user_search_url
--------------------
-
-.. note::
-    XHR stands for XMLHTTPRequest, and is meant here in the AJAX sense (Asynchronous JavaScript and XML).
-
-* Default:  Empty string
-
-Used only in conjunction with ``xhr_user_search``.
-
-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.

+ 1 - 1
mockup/modals.html

@@ -124,7 +124,7 @@
                 </button>
             </div>
             <div class="modal-body">
-                <form class="pure-form add-xmpp-contact">
+                <form class="converse-form add-xmpp-contact">
                     <div class="form-group">
                         <input type="text" name="identifier" class="form-control" placeholder="Contact username">
                     </div>

+ 3 - 121
src/converse-controlbox.js

@@ -9,13 +9,10 @@
 (function (root, factory) {
     define(["converse-core",
             "lodash.fp",
-            "tpl!add_contact_dropdown",
-            "tpl!add_contact_form",
             "tpl!converse_brand_heading",
             "tpl!controlbox",
             "tpl!controlbox_toggle",
             "tpl!login_panel",
-            "tpl!search_contact",
             "converse-chatview",
             "converse-rosterview",
             "converse-profile"
@@ -23,13 +20,10 @@
 }(this, function (
             converse,
             fp,
-            tpl_add_contact_dropdown,
-            tpl_add_contact_form,
             tpl_brand_heading,
             tpl_controlbox,
             tpl_controlbox_toggle,
-            tpl_login_panel,
-            tpl_search_contact
+            tpl_login_panel
         ) {
     "use strict";
 
@@ -86,7 +80,7 @@
          *
          * NB: These plugins need to have already been loaded via require.js.
          */
-        dependencies: ["converse-chatboxes", "converse-rosterview", "converse-chatview"],
+        dependencies: ["converse-modal", "converse-chatboxes", "converse-rosterview", "converse-chatview"],
 
         overrides: {
             // Overrides mentioned here will be picked up by converse.js's
@@ -212,9 +206,7 @@
                 default_domain: undefined,
                 locked_domain: undefined,
                 show_controlbox_by_default: false,
-                sticky_controlbox: false,
-                xhr_user_search: false,
-                xhr_user_search_url: ''
+                sticky_controlbox: false
             });
 
             _converse.api.promises.add('controlboxInitialized');
@@ -230,13 +222,6 @@
                 })
 
 
-            _converse.AddContactModal = Backbone.VDOMView.extend({
-                events: {
-                    'submit form': 'addContact'
-                },
-            });
-
-
             _converse.ControlBoxView = _converse.ChatBoxView.extend({
                 tagName: 'div',
                 className: 'chatbox',
@@ -528,12 +513,6 @@
             _converse.ControlBoxPane = Backbone.NativeView.extend({
                 tagName: 'div',
                 className: 'controlbox-pane',
-                events: {
-                    'click a.toggle-xmpp-contact-form': 'toggleContactForm',
-                    'submit form.add-xmpp-contact': 'addContactFromForm',
-                    'submit form.search-xmpp-contact': 'searchContacts',
-                    'click a.subscribe-to-user': 'addContactFromList'
-                },
 
                 initialize () {
                     _converse.xmppstatusview = new _converse.XMPPStatusView({
@@ -543,103 +522,6 @@
                         'afterBegin',
                         _converse.xmppstatusview.render().el
                     );
-                },
-
-                generateAddContactHTML (settings={}) {
-                    if (_converse.xhr_user_search) {
-                        return tpl_search_contact({
-                            label_contact_name: __('Contact name'),
-                            label_search: __('Search')
-                        });
-                    } else {
-                        return tpl_add_contact_form(_.assign({
-                            error_message: null,
-                            label_contact_username: __('e.g. user@example.org'),
-                            label_add: __('Add'),
-                            value: ''
-                        }, settings));
-                    }
-                },
-
-                toggleContactForm (ev) {
-                    ev.preventDefault();
-                    this.el.querySelector('.search-xmpp div').innerHTML = this.generateAddContactHTML();
-                    var dropdown = this.el.querySelector('.contact-form-container');
-                    u.slideToggleElement(dropdown).then(() => {
-                        if (u.isVisible(dropdown)) {
-                            dropdown.querySelector('input.username').focus();
-                        }
-                    });
-                },
-
-                searchContacts (ev) {
-                    ev.preventDefault();
-                    const search_query= ev.target.querySelector('input.username').value,
-                          url = _converse.xhr_user_search_url+ "?q=" + search_query,
-                          xhr = new XMLHttpRequest();
-
-                    xhr.open('GET', url, true);
-                    xhr.setRequestHeader('Accept', "application/json, text/javascript");
-
-                    xhr.onload = function () {
-                        if (xhr.status >= 200 && xhr.status < 400) {
-                            const data = JSON.parse(xhr.responseText),
-                                  ul = _converse.root.querySelector('.search-xmpp ul');
-                            u.removeElement(ul.querySelector('li.found-user'));
-                            u.removeElement(ul.querySelector('li.chat-info'));
-                            if (!data.length) {
-                                const no_users_text = __('No users found');
-                                ul.insertAdjacentHTML('beforeEnd', `<li class="chat-info">${no_users_text}</li>`);
-                            }
-                            else {
-                                const title_subscribe = __('Click to add as a chat contact');
-                                _.each(data, function (obj) {
-                                    const li = u.stringToElement('<li class="found-user"></li>'),
-                                          a = u.stringToElement(`<a class="subscribe-to-user" href="#" title="${title_subscribe}"></a>`),
-                                          jid = Strophe.getNodeFromJid(obj.id)+"@"+Strophe.getDomainFromJid(obj.id);
-
-                                    a.setAttribute('data-recipient', jid);
-                                    a.textContent = obj.fullname;
-                                    li.appendChild(a);
-                                    u.appendChild(li)
-                                });
-                            }
-                        } else {
-                            xhr.onerror();
-                        }
-                    };
-                    xhr.onerror = function () {
-                        _converse.log('Could not fetch contacts via XHR', Strophe.LogLevel.ERROR);
-                    };
-                    xhr.send();
-                },
-
-                addContactFromForm (ev) {
-                    ev.preventDefault();
-                    const input = ev.target.querySelector('input');
-                    const jid = input.value;
-                    if (!jid || _.compact(jid.split('@')).length < 2) {
-                        this.el.querySelector('.search-xmpp div').innerHTML =
-                            this.generateAddContactHTML({
-                                error_message: __('Please enter a valid XMPP address'),
-                                label_contact_username: __('e.g. user@example.org'),
-                                label_add: __('Add'),
-                                value: jid
-                            });
-                        return;
-                    }
-                    _converse.roster.addAndSubscribe(jid);
-                    u.slideIn(this.el.querySelector('.contact-form-container'));
-                },
-
-                addContactFromList (ev) {
-                    ev.preventDefault();
-                    const jid = ev.target.getAttribute('data-recipient'),
-                        name = ev.target.textContent;
-                    _converse.roster.addAndSubscribe(jid, name);
-                    const parent = ev.target.parentNode;
-                    parent.parentNode.removeChild(parent);
-                    u.slideIn(this.el.querySelector('.contact-form-container'));
                 }
             });
 

+ 1 - 1
src/converse-modal.js

@@ -38,9 +38,9 @@
                 },
 
                 show (ev) {
+                    ev.preventDefault();
                     this.trigger_el = ev.target;
                     this.trigger_el.classList.add('selected');
-                    this.render();
                     this.modal.show();
                 }
             });

+ 6 - 7
src/converse-muc-views.js

@@ -2184,11 +2184,6 @@
                     'click a.room-info': 'toggleRoomInfo'
                 },
 
-                initialize (cfg) {
-                    this.add_room_modal = new _converse.AddChatRoomModal({'model': this.model});
-                    this.list_rooms_modal = new _converse.ListChatRoomsModal({'model': this.model});
-                },
-
                 render () {
                     this.el.innerHTML = tpl_room_panel({
                         'heading_chatrooms': __('Chatrooms'),
@@ -2204,12 +2199,16 @@
                 },
 
                 showAddRoomModal (ev) {
-                    ev.preventDefault();
+                    if (_.isUndefined(this.add_room_modal)) {
+                        this.add_room_modal = new _converse.AddChatRoomModal({'model': this.model});
+                    }
                     this.add_room_modal.show(ev);
                 },
 
                 showListRoomsModal(ev) {
-                    ev.preventDefault();
+                    if (_.isUndefined(this.list_rooms_modal)) {
+                        this.list_rooms_modal = new _converse.ListChatRoomsModal({'model': this.model});
+                    }
                     this.list_rooms_modal.show(ev);
                 }
             });

+ 9 - 23
src/converse-profile.js

@@ -12,7 +12,8 @@
             "tpl!chat_status_modal",
             "tpl!profile_view",
             "tpl!status_option",
-            "converse-vcard"
+            "converse-vcard",
+            "converse-modal"
     ], factory);
 }(this, function (
             converse,
@@ -29,6 +30,8 @@
 
     converse.plugins.add('converse-profile', {
 
+        dependencies: ["converse-modal"],
+
         initialize () {
             /* The initialize function gets called as soon as the plugin is
              * loaded by converse.js's plugin machinery.
@@ -37,20 +40,12 @@
                   { __ } = _converse;
 
 
-            _converse.ChatStatusModal = Backbone.VDOMView.extend({
+            _converse.ChatStatusModal = _converse.BootstrapModal.extend({
                 events: {
                     "submit form#set-xmpp-status": "onFormSubmitted",
                     "click .clear-input": "clearStatusMessage"
                 },
 
-                initialize () {
-                    this.render().insertIntoDOM();
-                    this.modal = new bootstrap.Modal(this.el, {
-                        backdrop: 'static',
-                        keyboard: true
-                    });
-                },
-
                 toHTML () {
                     return tpl_chat_status_modal(_.extend(this.model.toJSON(), {
                         'label_away': __('Away'),
@@ -67,16 +62,6 @@
                     }));
                 },
 
-                insertIntoDOM () {
-                    const container_el = _converse.chatboxviews.el.querySelector('#converse-modals');
-                    container_el.insertAdjacentElement('beforeEnd', this.el);
-                },
-
-                show () {
-                    this.render();
-                    this.modal.show();
-                },
-
                 clearStatusMessage (ev) {
                     if (ev && ev.preventDefault) {
                         ev.preventDefault();
@@ -107,7 +92,6 @@
 
                 initialize () {
                     this.model.on("change", this.render, this);
-                    this.status_modal = new _converse.ChatStatusModal({model: this.model});
                 },
 
                 toHTML () {
@@ -125,8 +109,10 @@
                 },
 
                showStatusChangeModal (ev) {
-                    ev.preventDefault();
-                    this.status_modal.show();
+                    if (_.isUndefined(this.status_modal)) {
+                        this.status_modal = new _converse.ChatStatusModal({model: this.model});
+                    }
+                    this.status_modal.show(ev);
                 },
 
                 logOut (ev) {

+ 63 - 2
src/converse-rosterview.js

@@ -8,22 +8,28 @@
 
 (function (root, factory) {
     define(["converse-core",
+            "tpl!add_contact_modal",
             "tpl!group_header",
             "tpl!pending_contact",
             "tpl!requesting_contact",
             "tpl!roster",
             "tpl!roster_filter",
             "tpl!roster_item",
-            "converse-chatboxes"
+            "tpl!search_contact",
+            "converse-chatboxes",
+            "converse-modal"
     ], factory);
 }(this, function (
             converse, 
+            tpl_add_contact_modal,
             tpl_group_header,
             tpl_pending_contact,
             tpl_requesting_contact,
             tpl_roster,
             tpl_roster_filter,
-            tpl_roster_item) {
+            tpl_roster_item,
+            tpl_search_contact
+    ) {
     "use strict";
     const { Backbone, Strophe, $iq, b64_sha1, sizzle, _ } = converse.env;
     const u = converse.env.utils;
@@ -31,6 +37,8 @@
 
     converse.plugins.add('converse-rosterview', {
 
+        dependencies: ["converse-modal"],
+
         overrides: {
             // Overrides mentioned here will be picked up by converse.js's
             // plugin architecture they will replace existing methods on the
@@ -118,6 +126,47 @@
             };
 
 
+            _converse.AddContactModal = _converse.BootstrapModal.extend({
+                events: {
+                    'click a.subscribe-to-user': 'addContactFromList',
+                    'submit form': 'addContactFromForm',
+                    'submit form.search-xmpp-contact': 'searchContacts'
+                },
+
+                initialize () {
+                    _converse.BootstrapModal.prototype.initialize.apply(this, arguments);
+                    this.model.on('change', this.render, this);
+                },
+
+                toHTML () {
+                    return tpl_add_contact_modal(_.extend(this.model.toJSON(), {
+                        'heading_new_contact': __('Add a Contact'),
+                        'label_xmpp_address': __('XMPP Address'),
+                        'label_nickname': __('Optional nickname'),
+                        'contact_placeholder': __('name@example.org'),
+                        'label_add': __('Add'),
+                    }));
+                },
+
+                addContactFromForm (ev) {
+                    ev.preventDefault();
+                    const data = new FormData(ev.target),
+                          jid = data.get('jid');
+
+                    if (!jid || _.compact(jid.split('@')).length < 2) {
+                        this.model.set({
+                            'error_message': __('Please enter a valid XMPP address'),
+                            'jid': jid
+                        })
+                    } else {
+                        _converse.roster.addAndSubscribe(jid);
+                        this.model.clear();
+                        this.modal.hide();
+                    }
+                }
+            });
+
+
             _converse.RosterFilter = Backbone.Model.extend({
                 initialize () {
                     this.set({
@@ -620,6 +669,11 @@
                 sortEvent: null, // Groups are immutable, so they don't get re-sorted
                 subviewIndex: 'name',
 
+                events: {
+                    'click a.chatbox-btn.add-contact': 'showAddContactModal',
+
+                },
+
                 initialize () {
                     Backbone.OrderedListView.prototype.initialize.apply(this, arguments);
 
@@ -666,6 +720,13 @@
                     return this;
                 },
 
+                showAddContactModal (ev) {
+                    if (_.isUndefined(this.add_contact_modal)) {
+                        this.add_contact_modal = new _converse.AddContactModal({'model': new Backbone.Model()});
+                    }
+                    this.add_contact_modal.show(ev);
+                },
+
                 createRosterFilter () {
                     // Create a model on which we can store filter properties
                     const model = new _converse.RosterFilter();

+ 5 - 5
src/templates/add_chatroom_modal.html

@@ -1,9 +1,9 @@
-<div class="modal fade" id="chatroomsModal" tabindex="-1" role="dialog" aria-labelledby="chatroomsModalLabel" aria-hidden="true">
+<div class="modal fade" id="add-chatroom-modal" tabindex="-1" role="dialog" aria-labelledby="add-chatroom-modal-label" aria-hidden="true">
     <div class="modal-dialog" role="document">
         <div class="modal-content">
             <div class="modal-header">
                 <h5 class="modal-title"
-                    id="chatroomsModalLabel">{{{o.heading_new_chatroom}}}</h5>
+                    id="add-chatroom-modal-label">{{{o.heading_new_chatroom}}}</h5>
                 <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                     <span aria-hidden="true">&times;</span>
                 </button>
@@ -11,11 +11,11 @@
             <div class="modal-body">
                 <form class="converse-form add-chatroom">
                     <div class="form-group">
-                        <label for="server">{{{o.label_room_address}}}:</label>
-                        <input type="text" value="{{{o.muc_domain}}}" required="required" name="chatroom" class="form-control" placeholder="{{{o.chatroom_placeholder}}}">
+                        <label for="chatroom">{{{o.label_room_address}}}:</label>
+                        <input type="text" required="required" name="chatroom" class="form-control" placeholder="{{{o.chatroom_placeholder}}}">
                     </div>
                     <div class="form-group">
-                        <label for="chatroom">{{{o.label_nickname}}}:</label>
+                        <label for="nickname">{{{o.label_nickname}}}:</label>
                         <input type="text" name="nickname" value="{{{o.nick}}}" class="form-control">
                     </div>
                     <input type="submit" class="btn btn-primary" name="join" value="{{{o.label_join}}}">

+ 0 - 9
src/templates/add_contact_dropdown.html

@@ -1,9 +0,0 @@
-<dl class="add-converse-contact dropdown">
-    <dt id="xmpp-contact-search" class="fancy-dropdown">
-        <a class="toggle-xmpp-contact-form icon-plus" href="#" title="{{{o.label_click_to_chat}}}"> {{{o.label_add_contact}}}</a>
-    </dt>
-    <dd class="search-xmpp">
-        <div class="contact-form-container collapsed"></div>
-        <ul></ul>
-    </dd>
-</dl>

+ 27 - 0
src/templates/add_contact_modal.html

@@ -0,0 +1,27 @@
+<!-- Add contact Modal -->
+<div class="modal fade" id="add-contact-modal" tabindex="-1" role="dialog" aria-labelledby="addContactModalLabel" aria-hidden="true">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title" id="addContactModalLabel">{{{o.heading_new_contact}}}</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+            </div>
+            <form class="converse-form add-xmpp-contact">
+                <div class="modal-body">
+                    <div class="form-group">
+                        <label for="jid">{{{o.label_xmpp_address}}}:</label>
+                        <input type="text" name="jid" required="required" value="{{{o.jid}}}"
+                               class="form-control {[ if (o.error_message) { ]} is-invalid {[ } ]}"
+                               placeholder="{{{o.contact_placeholder}}}">
+                        {[ if (o.error_message) { ]}
+                            <div class="invalid-feedback">{{{o.error_message}}}</div>
+                        {[ } ]}
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <button type="submit" class="btn btn-primary">{{{o.label_add}}}</button>
+                </div>
+            </form>
+        </div>
+    </div>
+</div>

+ 3 - 3
src/templates/list_chatrooms_modal.html

@@ -1,15 +1,15 @@
-<div class="modal fade" id="list-chatrooms-modal" tabindex="-1" role="dialog" aria-labelledby="chatroomsModalLabel" aria-hidden="true">
+<div class="modal fade" id="list-chatrooms-modal" tabindex="-1" role="dialog" aria-labelledby="list-chatrooms-modal-label" aria-hidden="true">
     <div class="modal-dialog" role="document">
         <div class="modal-content">
             <div class="modal-header">
                 <h5 class="modal-title"
-                    id="chatroomsModalLabel">{{{o.heading_list_chatrooms}}}</h5>
+                    id="list-chatrooms-modal-label">{{{o.heading_list_chatrooms}}}</h5>
                 <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                     <span aria-hidden="true">&times;</span>
                 </button>
             </div>
             <div class="modal-body">
-                <form class="converse-form add-chatroom">
+                <form class="converse-form list-chatrooms">
                     <div class="form-group">
                         <label for="chatroom">{{{o.label_server_address}}}:</label>
                         <input type="text" value="{{{o.muc_domain}}}" required="required" name="server" class="form-control" placeholder="{{{o.server_placeholder}}}">

+ 1 - 1
src/templates/room_panel.html

@@ -2,7 +2,7 @@
 <div class="d-flex">
     <span class="w-100">{{{o.heading_chatrooms}}}</span>
     <a class="chatbox-btn fa fa-list-ul" title="{{{o.title_list_rooms}}}" data-toggle="modal" data-target="#list-chatrooms-modal"></a>
-    <a class="chatbox-btn fa fa-users" title="{{{o.title_new_room}}}" data-toggle="modal" data-target="#chatroomsModal"></a>
+    <a class="chatbox-btn fa fa-users" title="{{{o.title_new_room}}}" data-toggle="modal" data-target="#add-chatrooms-modal"></a>
 </div>
 <div class="list-container open-rooms-list rooms-list-container"></div>
 <div class="list-container bookmarks-list rooms-list-container"></div>

+ 2 - 2
src/templates/roster.html

@@ -1,7 +1,7 @@
 <div class="d-flex">
     <span class="w-100">{{{o.heading_contacts}}}</span>
-    <a class="chatbox-btn fa fa-user-plus" title="{{{o.title_add_contact}}}"
-       data-toggle="modal" data-target="#addContactModal"></a>
+    <a class="chatbox-btn add-contact fa fa-user-plus" title="{{{o.title_add_contact}}}"
+       data-toggle="modal" data-target="#add-contact-modal"></a>
 </div>
 
 <form class="roster-filter-form"></form>