MDL-65254 core_message: Fix going back to message view drawer search
[moodle.git] / message / amd / src / message_drawer_view_search.js
CommitLineData
5005d8cf
RW
1// This file is part of Moodle - http://moodle.org/
2//
3// Moodle is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// (at your option) any later version.
7//
8// Moodle is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
15
16/**
17 * Controls the search page of the message drawer.
18 *
19 * @module core_message/message_drawer_view_search
20 * @copyright 2018 Ryan Wyllie <ryan@moodle.com>
21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22 */
23define(
24[
25 'jquery',
26 'core/custom_interaction_events',
27 'core/notification',
28 'core/pubsub',
29 'core/str',
30 'core/templates',
31 'core_message/message_repository',
32 'core_message/message_drawer_events',
33],
34function(
35 $,
36 CustomEvents,
37 Notification,
38 PubSub,
39 Str,
40 Templates,
41 Repository,
42 Events
43) {
44
45 var MESSAGE_SEARCH_LIMIT = 50;
46 var USERS_SEARCH_LIMIT = 50;
47 var USERS_INITIAL_SEARCH_LIMIT = 3;
48
49 var SELECTORS = {
50 BLOCK_ICON_CONTAINER: '[data-region="block-icon-container"]',
51 CANCEL_SEARCH_BUTTON: '[data-action="cancel-search"]',
52 CONTACTS_CONTAINER: '[data-region="contacts-container"]',
53 CONTACTS_LIST: '[data-region="contacts-container"] [data-region="list"]',
54 EMPTY_MESSAGE_CONTAINER: '[data-region="empty-message-container"]',
55 LIST: '[data-region="list"]',
56 LOADING_ICON_CONTAINER: '[data-region="loading-icon-container"]',
57 LOADING_PLACEHOLDER: '[data-region="loading-placeholder"]',
58 MESSAGES_LIST: '[data-region="messages-container"] [data-region="list"]',
59 MESSAGES_CONTAINER: '[data-region="messages-container"]',
60 NON_CONTACTS_CONTAINER: '[data-region="non-contacts-container"]',
61 NON_CONTACTS_LIST: '[data-region="non-contacts-container"] [data-region="list"]',
62 SEARCH_ICON_CONTAINER: '[data-region="search-icon-container"]',
63 SEARCH_ACTION: '[data-action="search"]',
64 SEARCH_INPUT: '[data-region="search-input"]',
65 SEARCH_RESULTS_CONTAINER: '[data-region="search-results-container"]',
66 LOAD_MORE_USERS: '[data-action="load-more-users"]',
67 LOAD_MORE_MESSAGES: '[data-action="load-more-messages"]',
68 BUTTON_TEXT: '[data-region="button-text"]',
69 NO_RESULTS_CONTAINTER: '[data-region="no-results-container"]',
9acc0bc6 70 ALL_CONTACTS_CONTAINER: '[data-region="all-contacts-container"]'
5005d8cf
RW
71 };
72
73 var TEMPLATES = {
74 CONTACTS_LIST: 'core_message/message_drawer_contacts_list',
75 NON_CONTACTS_LIST: 'core_message/message_drawer_non_contacts_list',
76 MESSAGES_LIST: 'core_message/message_drawer_messages_list'
77 };
78
79 /**
80 * Get the logged in user id.
81 *
82 * @param {Object} body Search body container element.
83 * @return {Number} User id.
84 */
85 var getLoggedInUserId = function(body) {
86 return body.attr('data-user-id');
87 };
88
89 /**
90 * Show the no messages container element.
91 *
92 * @param {Object} body Search body container element.
93 * @return {Object} No messages container element.
94 */
95 var getEmptyMessageContainer = function(body) {
96 return body.find(SELECTORS.EMPTY_MESSAGE_CONTAINER);
97 };
98
99 /**
100 * Get the search loading icon.
101 *
102 * @param {Object} header Search header container element.
103 * @return {Object} Loading icon element.
104 */
105 var getLoadingIconContainer = function(header) {
106 return header.find(SELECTORS.LOADING_ICON_CONTAINER);
107 };
108
109 /**
110 * Get the loading container element.
111 *
112 * @param {Object} body Search body container element.
113 * @return {Object} Loading container element.
114 */
115 var getLoadingPlaceholder = function(body) {
116 return body.find(SELECTORS.LOADING_PLACEHOLDER);
117 };
118
119 /**
120 * Get the search icon container.
121 *
122 * @param {Object} header Search header container element.
123 * @return {Object} Search icon container.
124 */
125 var getSearchIconContainer = function(header) {
126 return header.find(SELECTORS.SEARCH_ICON_CONTAINER);
127 };
128
129 /**
130 * Get the search input container.
131 *
132 * @param {Object} header Search header container element.
133 * @return {Object} Search input container.
134 */
135 var getSearchInput = function(header) {
136 return header.find(SELECTORS.SEARCH_INPUT);
137 };
138
139 /**
140 * Get the search results container.
141 *
142 * @param {Object} body Search body container element.
143 * @return {Object} Search results container.
144 */
145 var getSearchResultsContainer = function(body) {
146 return body.find(SELECTORS.SEARCH_RESULTS_CONTAINER);
147 };
148
149 /**
150 * Get the search contacts container.
151 *
152 * @param {Object} body Search body container element.
153 * @return {Object} Search contacts container.
154 */
155 var getContactsContainer = function(body) {
156 return body.find(SELECTORS.CONTACTS_CONTAINER);
157 };
158
159 /**
160 * Get the search non contacts container.
161 *
162 * @param {Object} body Search body container element.
163 * @return {Object} Search non contacts container.
164 */
165 var getNonContactsContainer = function(body) {
166 return body.find(SELECTORS.NON_CONTACTS_CONTAINER);
167 };
168
169 /**
170 * Get the search messages container.
171 *
172 * @param {Object} body Search body container element.
173 * @return {Object} Search messages container.
174 */
175 var getMessagesContainer = function(body) {
176 return body.find(SELECTORS.MESSAGES_CONTAINER);
177 };
178
179
180 /**
181 * Show the messages empty container.
182 *
183 * @param {Object} body Search body container element.
184 */
185 var showEmptyMessage = function(body) {
186 getEmptyMessageContainer(body).removeClass('hidden');
187 };
188
189 /**
190 * Hide the messages empty container.
191 *
192 * @param {Object} body Search body container element.
193 */
194 var hideEmptyMessage = function(body) {
195 getEmptyMessageContainer(body).addClass('hidden');
196 };
197
198
199 /**
200 * Show the loading icon.
201 *
202 * @param {Object} header Search header container element.
203 */
204 var showLoadingIcon = function(header) {
205 getLoadingIconContainer(header).removeClass('hidden');
206 };
207
208 /**
209 * Hide the loading icon.
210 *
211 * @param {Object} header Search header container element.
212 */
213 var hideLoadingIcon = function(header) {
214 getLoadingIconContainer(header).addClass('hidden');
215 };
216
217 /**
218 * Show loading placeholder.
219 *
220 * @param {Object} body Search body container element.
221 */
222 var showLoadingPlaceholder = function(body) {
223 getLoadingPlaceholder(body).removeClass('hidden');
224 };
225
226 /**
227 * Hide loading placeholder.
228 *
229 * @param {Object} body Search body container element.
230 */
231 var hideLoadingPlaceholder = function(body) {
232 getLoadingPlaceholder(body).addClass('hidden');
233 };
234
235 /**
236 * Show search icon.
237 *
238 * @param {Object} header Search header container element.
239 */
240 var showSearchIcon = function(header) {
241 getSearchIconContainer(header).removeClass('hidden');
242 };
243
244 /**
245 * Hide search icon.
246 *
247 * @param {Object} header Search header container element.
248 */
249 var hideSearchIcon = function(header) {
250 getSearchIconContainer(header).addClass('hidden');
251 };
252
253 /**
254 * Show search results.
255 *
256 * @param {Object} body Search body container element.
257 */
258 var showSearchResults = function(body) {
259 getSearchResultsContainer(body).removeClass('hidden');
260 };
261
262 /**
263 * Hide search results.
264 *
265 * @param {Object} body Search body container element.
266 */
267 var hideSearchResults = function(body) {
268 getSearchResultsContainer(body).addClass('hidden');
269 };
270
9acc0bc6
RW
271 /**
272 * Show the no search results message.
273 *
274 * @param {Object} body Search body container element.
275 */
276 var showNoSearchResults = function(body) {
277 var container = getSearchResultsContainer(body);
278 container.find(SELECTORS.ALL_CONTACTS_CONTAINER).addClass('hidden');
279 container.find(SELECTORS.MESSAGES_CONTAINER).addClass('hidden');
280 container.find(SELECTORS.NO_RESULTS_CONTAINTER).removeClass('hidden');
281 };
282
283 /**
284 * Hide the no search results message.
285 *
286 * @param {Object} body Search body container element.
287 */
288 var hideNoSearchResults = function(body) {
289 var container = getSearchResultsContainer(body);
290 container.find(SELECTORS.ALL_CONTACTS_CONTAINER).removeClass('hidden');
291 container.find(SELECTORS.MESSAGES_CONTAINER).removeClass('hidden');
292 container.find(SELECTORS.NO_RESULTS_CONTAINTER).addClass('hidden');
293 };
294
295 /**
296 * Show the whole contacts results area.
297 *
298 * @param {Object} body Search body container element.
299 */
300 var showAllContactsSearchResults = function(body) {
301 var container = getSearchResultsContainer(body);
302 container.find(SELECTORS.ALL_CONTACTS_CONTAINER).removeClass('hidden');
303 };
304
305 /**
306 * Hide the whole contacts results area.
307 *
308 * @param {Object} body Search body container element.
309 */
310 var hideAllContactsSearchResults = function(body) {
311 var container = getSearchResultsContainer(body);
312 container.find(SELECTORS.ALL_CONTACTS_CONTAINER).addClass('hidden');
313 };
314
315 /**
316 * Show the contacts results.
317 *
318 * @param {Object} body Search body container element.
319 */
320 var showContactsSearchResults = function(body) {
321 var container = getSearchResultsContainer(body);
322 container.find(SELECTORS.CONTACTS_CONTAINER).removeClass('hidden');
323 };
324
325 /**
326 * Hide the contacts results.
327 *
328 * @param {Object} body Search body container element.
329 */
330 var hideContactsSearchResults = function(body) {
331 var container = getSearchResultsContainer(body);
332 container.find(SELECTORS.CONTACTS_CONTAINER).addClass('hidden');
333 };
334
335 /**
336 * Show the non contacts results.
337 *
338 * @param {Object} body Search body container element.
339 */
340 var showNonContactsSearchResults = function(body) {
341 var container = getSearchResultsContainer(body);
342 container.find(SELECTORS.NON_CONTACTS_CONTAINER).removeClass('hidden');
343 };
344
345 /**
346 * Hide the non contacts results.
347 *
348 * @param {Object} body Search body container element.
349 */
350 var hideNonContactsSearchResults = function(body) {
351 var container = getSearchResultsContainer(body);
352 container.find(SELECTORS.NON_CONTACTS_CONTAINER).addClass('hidden');
353 };
354
355 /**
356 * Show the messages results.
357 *
358 * @param {Object} body Search body container element.
359 */
360 var showMessagesSearchResults = function(body) {
361 var container = getSearchResultsContainer(body);
362 container.find(SELECTORS.MESSAGES_CONTAINER).removeClass('hidden');
363 };
364
365 /**
366 * Hide the messages results.
367 *
368 * @param {Object} body Search body container element.
369 */
370 var hideMessagesSearchResults = function(body) {
371 var container = getSearchResultsContainer(body);
372 container.find(SELECTORS.MESSAGES_CONTAINER).addClass('hidden');
373 };
374
5005d8cf
RW
375 /**
376 * Disable the search input.
377 *
378 * @param {Object} header Search header container element.
379 */
380 var disableSearchInput = function(header) {
381 getSearchInput(header).prop('disabled', true);
382 };
383
384 /**
385 * Enable the search input.
386 *
387 * @param {Object} header Search header container element.
388 */
389 var enableSearchInput = function(header) {
390 getSearchInput(header).prop('disabled', false);
391 };
392
393 /**
394 * Clear the search input.
395 *
396 * @param {Object} header Search header container element.
397 */
398 var clearSearchInput = function(header) {
399 getSearchInput(header).val('');
400 };
401
402 /**
403 * Clear all search results
404 *
405 * @param {Object} body Search body container element.
406 */
407 var clearAllSearchResults = function(body) {
408 body.find(SELECTORS.CONTACTS_LIST).empty();
409 body.find(SELECTORS.NON_CONTACTS_LIST).empty();
410 body.find(SELECTORS.MESSAGES_LIST).empty();
9acc0bc6
RW
411 hideNoSearchResults(body);
412 showAllContactsSearchResults(body);
413 showContactsSearchResults(body);
414 showNonContactsSearchResults(body);
415 showMessagesSearchResults(body);
5005d8cf
RW
416 showLoadMoreUsersButton(body);
417 showLoadMoreMessagesButton(body);
418 };
419
420 /**
421 * Update the body and header to indicate the search is loading.
422 *
423 * @param {Object} header Search header container element.
424 * @param {Object} body Search body container element.
425 */
426 var startLoading = function(header, body) {
427 hideSearchIcon(header);
428 hideEmptyMessage(body);
429 hideSearchResults(body);
430 showLoadingIcon(header);
431 showLoadingPlaceholder(body);
432 disableSearchInput(header);
433 };
434
435 /**
436 * Update the body and header to indicate the search has stopped loading.
437 *
438 * @param {Object} header Search header container element.
439 * @param {Object} body Search body container element.
440 */
441 var stopLoading = function(header, body) {
442 showSearchIcon(header);
443 hideEmptyMessage(body);
444 showSearchResults(body);
445 hideLoadingIcon(header);
446 hideLoadingPlaceholder(body);
447 enableSearchInput(header);
448 };
449
450 /**
451 * Show the more users loading icon.
452 *
453 * @param {Object} root The more users container element.
454 */
455 var showUsersLoadingIcon = function(root) {
456 var button = root.find(SELECTORS.LOAD_MORE_USERS);
457 button.prop('disabled', true);
458 button.find(SELECTORS.BUTTON_TEXT).addClass('hidden');
459 button.find(SELECTORS.LOADING_ICON_CONTAINER).removeClass('hidden');
460 };
461
462 /**
463 * Hide the more users loading icon.
464 *
465 * @param {Object} root The more users container element.
466 */
467 var hideUsersLoadingIcon = function(root) {
468 var button = root.find(SELECTORS.LOAD_MORE_USERS);
469 button.prop('disabled', false);
470 button.find(SELECTORS.BUTTON_TEXT).removeClass('hidden');
471 button.find(SELECTORS.LOADING_ICON_CONTAINER).addClass('hidden');
472 };
473
474 /**
475 * Show the load more users button.
476 *
477 * @param {Object} root The users container element.
478 */
479 var showLoadMoreUsersButton = function(root) {
480 root.find(SELECTORS.LOAD_MORE_USERS).removeClass('hidden');
481 };
482
483 /**
484 * Hide the load more users button.
485 *
486 * @param {Object} root The users container element.
487 */
488 var hideLoadMoreUsersButton = function(root) {
489 root.find(SELECTORS.LOAD_MORE_USERS).addClass('hidden');
490 };
491
492 /**
493 * Show the messages are loading icon.
494 *
495 * @param {Object} root Messages root element.
496 */
497 var showMessagesLoadingIcon = function(root) {
498 var button = root.find(SELECTORS.LOAD_MORE_MESSAGES);
499 button.prop('disabled', true);
500 button.find(SELECTORS.BUTTON_TEXT).addClass('hidden');
501 button.find(SELECTORS.LOADING_ICON_CONTAINER).removeClass('hidden');
502 };
503
504 /**
505 * Hide the messages are loading icon.
506 *
507 * @param {Object} root Messages root element.
508 */
509 var hideMessagesLoadingIcon = function(root) {
510 var button = root.find(SELECTORS.LOAD_MORE_MESSAGES);
511 button.prop('disabled', false);
512 button.find(SELECTORS.BUTTON_TEXT).removeClass('hidden');
513 button.find(SELECTORS.LOADING_ICON_CONTAINER).addClass('hidden');
514 };
515
516 /**
517 * Show the load more messages button.
518 *
519 * @param {Object} root The messages container element.
520 */
521 var showLoadMoreMessagesButton = function(root) {
522 root.find(SELECTORS.LOAD_MORE_MESSAGES).removeClass('hidden');
523 };
524
525 /**
526 * Hide the load more messages button.
527 *
528 * @param {Object} root The messages container element.
529 */
530 var hideLoadMoreMessagesButton = function(root) {
531 root.find(SELECTORS.LOAD_MORE_MESSAGES).addClass('hidden');
532 };
533
534 /**
535 * Find a contact in the search results.
536 *
537 * @param {Object} root Search results container element.
538 * @param {Number} userId User id.
539 * @return {Object} User container element.
540 */
541 var findContact = function(root, userId) {
542 return root.find('[data-contact-user-id="' + userId + '"]');
543 };
544
545 /**
546 * Add a contact to the search results.
547 *
548 * @param {Object} root Search results container.
549 * @param {Object} contact User in contacts list.
550 */
551 var addContact = function(root, contact) {
552 var nonContactsContainer = getNonContactsContainer(root);
553 var nonContact = findContact(nonContactsContainer, contact.userid);
554
555 if (nonContact.length) {
556 nonContact.remove();
557 var contactsContainer = getContactsContainer(root);
558 contactsContainer.removeClass('hidden');
559 contactsContainer.find(SELECTORS.LIST).append(nonContact);
560 }
561
562 if (!nonContactsContainer.find(SELECTORS.LIST).children().length) {
563 nonContactsContainer.addClass('hidden');
564 }
565 };
566
567 /**
568 * Remove a contact from the contacts results.
569 *
570 * @param {Object} root Search results container.
571 * @param {Object} userId Contact user id.
572 */
573 var removeContact = function(root, userId) {
574 var contactsContainer = getContactsContainer(root);
575 var contact = findContact(contactsContainer, userId);
576
577 if (contact.length) {
578 contact.remove();
579 var nonContactsContainer = getNonContactsContainer(root);
580 nonContactsContainer.removeClass('hidden');
581 nonContactsContainer.find(SELECTORS.LIST).append(contact);
582 }
583
584 if (!contactsContainer.find(SELECTORS.LIST).children().length) {
585 contactsContainer.addClass('hidden');
586 }
587 };
588
589 /**
590 * Show the contact is blocked icon.
591 *
592 * @param {Object} root Search results container.
593 * @param {Object} userId Contact user id.
594 */
595 var blockContact = function(root, userId) {
596 var contact = findContact(root, userId);
597 if (contact.length) {
598 contact.find(SELECTORS.BLOCK_ICON_CONTAINER).removeClass('hidden');
599 }
600 };
601
602 /**
603 * Hide the contact is blocked icon.
604 *
605 * @param {Object} root Search results container.
606 * @param {Object} userId Contact user id.
607 */
608 var unblockContact = function(root, userId) {
609 var contact = findContact(root, userId);
610 if (contact.length) {
611 contact.find(SELECTORS.BLOCK_ICON_CONTAINER).addClass('hidden');
612 }
613 };
614
615 /**
616 * Render contacts in the contacts search results.
617 *
618 * @param {Object} root Search results container.
619 * @param {Array} contacts List of contacts.
620 * @return {Promise} Renderer promise.
621 */
622 var renderContacts = function(root, contacts) {
623 var container = getContactsContainer(root);
624 var list = container.find(SELECTORS.LIST);
625
9acc0bc6
RW
626 return Templates.render(TEMPLATES.CONTACTS_LIST, {contacts: contacts})
627 .then(function(html) {
628 list.append(html);
629 return html;
630 });
5005d8cf
RW
631 };
632
633 /**
634 * Render non contacts in the contacts search results.
635 *
636 * @param {Object} root Search results container.
637 * @param {Array} nonContacts List of non contacts.
638 * @return {Promise} Renderer promise.
639 */
640 var renderNonContacts = function(root, nonContacts) {
641 var container = getNonContactsContainer(root);
642 var list = container.find(SELECTORS.LIST);
643
9acc0bc6
RW
644 return Templates.render(TEMPLATES.NON_CONTACTS_LIST, {noncontacts: nonContacts})
645 .then(function(html) {
646 list.append(html);
647 return html;
648 });
5005d8cf
RW
649 };
650
651 /**
652 * Render messages in the messages search results.
653 *
654 * @param {Object} root Search results container.
655 * @param {Array} messages List of messages.
656 * @return {Promise} Renderer promise.
657 */
658 var renderMessages = function(root, messages) {
659 var container = getMessagesContainer(root);
660 var list = container.find(SELECTORS.LIST);
661
9acc0bc6
RW
662 return Templates.render(TEMPLATES.MESSAGES_LIST, {messages: messages})
663 .then(function(html) {
664 list.append(html);
665 return html;
666 });
5005d8cf
RW
667 };
668
669 /**
670 * Load more users from the repository and render the results into the users search results.
671 *
672 * @param {Object} root Search results container.
673 * @param {Number} loggedInUserId Current logged in user.
674 * @param {String} text Search text.
675 * @param {Number} limit Number of users to get.
676 * @param {Number} offset Load users from
677 * @return {Object} jQuery promise
678 */
679 var loadMoreUsers = function(root, loggedInUserId, text, limit, offset) {
680 var loadedAll = false;
681 showUsersLoadingIcon(root);
9acc0bc6 682
5005d8cf
RW
683 return Repository.searchUsers(loggedInUserId, text, limit + 1, offset)
684 .then(function(results) {
685 var contacts = results.contacts;
686 var noncontacts = results.noncontacts;
687
688 if (contacts.length <= limit && noncontacts.length <= limit) {
689 loadedAll = true;
690 return {
691 contacts: contacts,
692 noncontacts: noncontacts
693 };
694 } else {
695 return {
696 contacts: contacts.slice(0, limit),
697 noncontacts: noncontacts.slice(0, limit)
698 };
699 }
700 })
701 .then(function(results) {
9acc0bc6
RW
702 var contactsCount = results.contacts.length;
703 var nonContactsCount = results.noncontacts.length;
704
5005d8cf 705 return $.when(
9acc0bc6
RW
706 contactsCount ? renderContacts(root, results.contacts) : true,
707 nonContactsCount ? renderNonContacts(root, results.noncontacts) : true
708 )
709 .then(function() {
710 return {
711 contactsCount: contactsCount,
712 nonContactsCount: nonContactsCount
713 };
714 });
5005d8cf 715 })
9acc0bc6 716 .then(function(counts) {
5005d8cf
RW
717 hideUsersLoadingIcon(root);
718
719 if (loadedAll) {
720 hideLoadMoreUsersButton(root);
721 }
722
9acc0bc6 723 return counts;
5005d8cf
RW
724 })
725 .catch(function(error) {
726 hideUsersLoadingIcon(root);
727 // Rethrow error for other handlers.
728 throw error;
729 });
730 };
731
732 /**
733 * Load more messages from the repository and render the results into the messages search results.
734 *
735 * @param {Object} root Search results container.
736 * @param {Number} loggedInUserId Current logged in user.
737 * @param {String} text Search text.
738 * @param {Number} limit Number of messages to get.
739 * @param {Number} offset Load messages from
740 * @return {Object} jQuery promise
741 */
742 var loadMoreMessages = function(root, loggedInUserId, text, limit, offset) {
743 var loadedAll = false;
744 showMessagesLoadingIcon(root);
9acc0bc6 745
5005d8cf
RW
746 return Repository.searchMessages(loggedInUserId, text, limit + 1, offset)
747 .then(function(results) {
748 var messages = results.contacts;
749
750 if (messages.length <= limit) {
751 loadedAll = true;
752 return messages;
753 } else {
754 return messages.slice(0, limit);
755 }
756 })
757 .then(function(messages) {
9acc0bc6
RW
758 if (messages.length) {
759 return renderMessages(root, messages)
760 .then(function() {
761 return messages.length;
762 });
763 } else {
764 return messages.length;
765 }
5005d8cf 766 })
9acc0bc6 767 .then(function(count) {
5005d8cf
RW
768 hideMessagesLoadingIcon(root);
769
770 if (loadedAll) {
771 hideLoadMoreMessagesButton(root);
772 }
773
9acc0bc6 774 return count;
5005d8cf
RW
775 })
776 .catch(function(error) {
777 hideMessagesLoadingIcon(root);
778 // Rethrow error for other handlers.
779 throw error;
780 });
781 };
782
783 /**
784 * Search for users and messages.
785 *
786 * @param {Object} header Search header container element.
787 * @param {Object} body Search body container element.
788 * @param {String} searchText Search text.
789 * @param {Number} usersLimit The users limit.
790 * @param {Number} usersOffset The users offset.
791 * @param {Number} messagesLimit The message limit.
792 * @param {Number} messagesOffset The message offset.
793 * @return {Object} jQuery promise
794 */
795 var search = function(header, body, searchText, usersLimit, usersOffset, messagesLimit, messagesOffset) {
796 var loggedInUserId = getLoggedInUserId(body);
797 startLoading(header, body);
798 clearAllSearchResults(body);
799
800 return $.when(
801 loadMoreUsers(body, loggedInUserId, searchText, usersLimit, usersOffset),
802 loadMoreMessages(body, loggedInUserId, searchText, messagesLimit, messagesOffset)
803 )
9acc0bc6
RW
804 .then(function(userCounts, messagesCount) {
805 var contactsCount = userCounts.contactsCount;
806 var nonContactsCount = userCounts.nonContactsCount;
807
5005d8cf 808 stopLoading(header, body);
9acc0bc6
RW
809
810 if (!contactsCount && !nonContactsCount && !messagesCount) {
811 showNoSearchResults(body);
812 } else {
813 if (!contactsCount && !nonContactsCount) {
814 hideAllContactsSearchResults(body);
815 } else {
816 if (!contactsCount) {
817 hideContactsSearchResults(body);
818 }
819
820 if (!nonContactsCount) {
821 hideNonContactsSearchResults(body);
822 }
823 }
824
825 if (!messagesCount) {
826 hideMessagesSearchResults(body);
827 }
828 }
829
5005d8cf
RW
830 return;
831 });
832 };
833
834
835 /**
836 * Listen to and handle events for searching.
837 *
838 * @param {Object} header Search header container element.
839 * @param {Object} body Search body container element.
840 */
841 var registerEventListeners = function(header, body) {
842 var loggedInUserId = getLoggedInUserId(body);
843 var searchInput = getSearchInput(header);
844 var searchText = '';
845 var messagesOffset = 0;
846 var usersOffset = 0;
847
848 var searchEventHandler = function(e, data) {
849 searchText = searchInput.val().trim();
850
851 if (searchText !== '') {
852 messagesOffset = 0;
853 usersOffset = 0;
854 search(
855 header,
856 body,
857 searchText,
858 USERS_INITIAL_SEARCH_LIMIT,
859 usersOffset,
860 MESSAGE_SEARCH_LIMIT,
861 messagesOffset
862 )
863 .then(function() {
864 searchInput.focus();
865 usersOffset = usersOffset + USERS_INITIAL_SEARCH_LIMIT;
866 messagesOffset = messagesOffset + MESSAGE_SEARCH_LIMIT;
867 return;
868 })
869 .catch(Notification.exception);
870 }
871
872 data.originalEvent.preventDefault();
873 };
874
875 CustomEvents.define(searchInput, [CustomEvents.events.enter]);
876 CustomEvents.define(header, [CustomEvents.events.activate]);
877 CustomEvents.define(body, [CustomEvents.events.activate]);
878
879 searchInput.on(CustomEvents.events.enter, searchEventHandler);
880
881 header.on(CustomEvents.events.activate, SELECTORS.SEARCH_ACTION, searchEventHandler);
882
883 body.on(CustomEvents.events.activate, SELECTORS.LOAD_MORE_MESSAGES, function(e, data) {
884 if (searchText !== '') {
885 loadMoreMessages(body, loggedInUserId, searchText, MESSAGE_SEARCH_LIMIT, messagesOffset)
886 .then(function() {
887 messagesOffset = messagesOffset + MESSAGE_SEARCH_LIMIT;
888 return;
889 })
890 .catch(Notification.exception);
891 }
892 data.originalEvent.preventDefault();
893 });
894
895 body.on(CustomEvents.events.activate, SELECTORS.LOAD_MORE_USERS, function(e, data) {
896 if (searchText !== '') {
897 loadMoreUsers(body, loggedInUserId, searchText, USERS_SEARCH_LIMIT, usersOffset)
898 .then(function() {
899 usersOffset = usersOffset + USERS_SEARCH_LIMIT;
900 return;
901 })
902 .catch(Notification.exception);
903 }
904 data.originalEvent.preventDefault();
905 });
906
907 header.on(CustomEvents.events.activate, SELECTORS.CANCEL_SEARCH_BUTTON, function() {
908 clearSearchInput(header);
909 showEmptyMessage(body);
910 showSearchIcon(header);
911 hideSearchResults(body);
912 hideLoadingIcon(header);
913 hideLoadingPlaceholder(body);
914 usersOffset = 0;
915 messagesOffset = 0;
916 });
917
918 PubSub.subscribe(Events.CONTACT_ADDED, function(userId) {
919 addContact(body, userId);
920 });
921
922 PubSub.subscribe(Events.CONTACT_REMOVED, function(userId) {
923 removeContact(body, userId);
924 });
925
926 PubSub.subscribe(Events.CONTACT_BLOCKED, function(userId) {
927 blockContact(body, userId);
928 });
929
930 PubSub.subscribe(Events.CONTACT_UNBLOCKED, function(userId) {
931 unblockContact(body, userId);
932 });
933 };
934
935 /**
936 * Setup the search page.
937 *
cb01c45c 938 * @param {string} namespace The route namespace.
5005d8cf
RW
939 * @param {Object} header Contacts header container element.
940 * @param {Object} body Contacts body container element.
941 * @return {Object} jQuery promise
942 */
cb01c45c 943 var show = function(namespace, header, body) {
5005d8cf
RW
944 if (!body.attr('data-init')) {
945 registerEventListeners(header, body);
946 body.attr('data-init', true);
947 }
948
949 var searchInput = getSearchInput(header);
950 searchInput.focus();
951
952 return $.Deferred().resolve().promise();
953 };
954
955 /**
956 * String describing this page used for aria-labels.
957 *
3118db5d 958 * @param {string} namespace The route namespace.
5005d8cf
RW
959 * @param {Object} header Contacts header container element.
960 * @return {Object} jQuery promise
961 */
3118db5d 962 var description = function(namespace, header) {
5005d8cf
RW
963 var searchInput = getSearchInput(header);
964 var searchText = searchInput.val().trim();
965 return Str.get_string('messagedrawerviewsearch', 'core_message', searchText);
966 };
967
968 return {
969 show: show,
970 description: description
971 };
972});