MDL-65134 core_message: Introduce event specific function
[moodle.git] / message / amd / src / message_drawer_view_overview_section.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 a section of the overview page in the message drawer.
18 *
19 * @module core_message/message_drawer_view_overview_section
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/user_date',
32 'core_message/message_repository',
33 'core_message/message_drawer_events',
34 'core_message/message_drawer_router',
35 'core_message/message_drawer_routes',
36 'core_message/message_drawer_lazy_load_list',
37 'core_message/message_drawer_view_conversation_constants'
38],
39function(
40 $,
41 CustomEvents,
42 Notification,
43 PubSub,
44 Str,
45 Templates,
46 UserDate,
47 MessageRepository,
48 MessageDrawerEvents,
49 MessageDrawerRouter,
50 MessageDrawerRoutes,
51 LazyLoadList,
52 MessageDrawerViewConversationContants
53) {
54
55 var SELECTORS = {
56 TOGGLE: '[data-region="toggle"]',
57 CONVERSATION: '[data-conversation-id]',
58 BLOCKED_ICON_CONTAINER: '[data-region="contact-icon-blocked"]',
59 LAST_MESSAGE: '[data-region="last-message"]',
60 LAST_MESSAGE_DATE: '[data-region="last-message-date"]',
bd038cc1 61 MUTED_ICON_CONTAINER: '[data-region="muted-icon-container"]',
5005d8cf
RW
62 UNREAD_COUNT: '[data-region="unread-count"]',
63 SECTION_TOTAL_COUNT: '[data-region="section-total-count"]',
3528c86c
RW
64 SECTION_TOTAL_COUNT_CONTAINER: '[data-region="section-total-count-container"]',
65 SECTION_UNREAD_COUNT: '[data-region="section-unread-count"]',
66 PLACEHOLDER_CONTAINER: '[data-region="placeholder-container"]'
5005d8cf
RW
67 };
68
69 var TEMPLATES = {
3528c86c
RW
70 CONVERSATIONS_LIST: 'core_message/message_drawer_conversations_list',
71 CONVERSATIONS_LIST_ITEMS_PLACEHOLDER: 'core_message/message_drawer_conversations_list_items_placeholder'
5005d8cf
RW
72 };
73
74 var LOAD_LIMIT = 50;
75 var loadedConversationsById = {};
3528c86c
RW
76 var loadedTotalCounts = false;
77 var loadedUnreadCounts = false;
5005d8cf
RW
78
79 /**
80 * Get the section visibility status.
81 *
82 * @param {Object} root The section container element.
83 * @return {Bool} Is section visible.
84 */
85 var isVisible = function(root) {
86 return LazyLoadList.getRoot(root).hasClass('show');
87 };
88
89 /**
90 * Set this section as expanded.
91 *
92 * @param {Object} root The section container element.
93 */
94 var setExpanded = function(root) {
95 root.addClass('expanded');
96 };
97
98 /**
99 * Set this section as collapsed.
100 *
101 * @param {Object} root The section container element.
102 */
103 var setCollapsed = function(root) {
104 root.removeClass('expanded');
105 };
106
3528c86c
RW
107 /**
108 * Render the total count value and show it for the user. Also update the placeholder
109 * HTML for better visuals.
110 *
111 * @param {Object} root The section container element.
112 * @param {Number} count The total count
113 */
114 var renderTotalCount = function(root, count) {
115 var container = root.find(SELECTORS.SECTION_TOTAL_COUNT_CONTAINER);
116 var countElement = container.find(SELECTORS.SECTION_TOTAL_COUNT);
117 countElement.text(count);
118 container.removeClass('hidden');
ccd47523
SA
119 Str.get_string('totalconversations', 'core_message', count).done(function(string) {
120 container.attr('aria-label', string);
121 });
3528c86c
RW
122
123 var numPlaceholders = count > 20 ? 20 : count;
124 // Array of "true" up to the number of placeholders we want.
125 var placeholders = Array.apply(null, Array(numPlaceholders)).map(function() {
126 return true;
127 });
128
129 // Replace the current placeholder (loading spinner) with some nicer placeholders that
130 // better represent the content.
131 Templates.render(TEMPLATES.CONVERSATIONS_LIST_ITEMS_PLACEHOLDER, {placeholders: placeholders})
132 .then(function(html) {
133 var placeholderContainer = root.find(SELECTORS.PLACEHOLDER_CONTAINER);
134 placeholderContainer.html(html);
135 return;
136 })
137 .catch(function() {
138 // Silently ignore. Doesn't matter if we can't render the placeholders.
139 });
140 };
141
142 /**
143 * Render the unread count value and show it for the user if it's higher than zero.
144 *
145 * @param {Object} root The section container element.
146 * @param {Number} count The unread count
147 */
148 var renderUnreadCount = function(root, count) {
149 var countElement = root.find(SELECTORS.SECTION_UNREAD_COUNT);
150 countElement.text(count);
151
ccd47523
SA
152 Str.get_string('unreadconversations', 'core_message', count).done(function(string) {
153 countElement.attr('aria-label', string);
154 });
155
3528c86c
RW
156 if (count > 0) {
157 countElement.removeClass('hidden');
158 }
159 };
160
d4b1eee6 161 /**
9be6dc2d
RW
162 * Create a formatted conversation object from the the one we get from events. The new object
163 * will be in a format that matches what we receive from the server.
164 *
165 * @param {Object} conversation
166 * @return {Object} formatted conversation.
d4b1eee6 167 */
9be6dc2d
RW
168 var formatConversationFromEvent = function(conversation) {
169 // Recursively lowercase all of the keys for an object.
170 var recursivelyLowercaseKeys = function(object) {
171 return Object.keys(object).reduce(function(carry, key) {
172 if ($.isArray(object[key])) {
173 carry[key.toLowerCase()] = object[key].map(recursivelyLowercaseKeys);
d4b1eee6 174 } else {
9be6dc2d 175 carry[key.toLowerCase()] = object[key];
d4b1eee6
P
176 }
177
178 return carry;
179 }, {});
9be6dc2d
RW
180 };
181
182 // Recursively lowercase all of the keys for the conversation.
183 var formatted = recursivelyLowercaseKeys(conversation);
184
185 // Make sure all messages have the useridfrom property set.
186 formatted.messages = formatted.messages.map(function(message) {
187 message.useridfrom = message.userfrom.id;
188 return message;
189 });
190
191 return formatted;
d4b1eee6
P
192 };
193
5005d8cf
RW
194 /**
195 * Render the messages in the overview page.
196 *
197 * @param {Object} contentContainer Conversations content container.
198 * @param {Array} conversations List of conversations to render.
199 * @param {Number} userId Logged in user id.
200 * @return {Object} jQuery promise.
201 */
d4b1eee6 202 var render = function(conversations, userId) {
5005d8cf 203 var formattedConversations = conversations.map(function(conversation) {
71b13a84 204
5005d8cf 205 var lastMessage = conversation.messages.length ? conversation.messages[conversation.messages.length - 1] : null;
71b13a84 206
5005d8cf
RW
207 var formattedConversation = {
208 id: conversation.id,
209 imageurl: conversation.imageurl,
210 name: conversation.name,
211 subname: conversation.subname,
212 unreadcount: conversation.unreadcount,
bd038cc1 213 ismuted: conversation.ismuted,
5005d8cf
RW
214 lastmessagedate: lastMessage ? lastMessage.timecreated : null,
215 sentfromcurrentuser: lastMessage ? lastMessage.useridfrom == userId : null,
216 lastmessage: lastMessage ? $(lastMessage.text).text() || lastMessage.text : null
217 };
218
734b198f
SA
219 var otherUser = null;
220 if (conversation.type == MessageDrawerViewConversationContants.CONVERSATION_TYPES.SELF) {
221 // Self-conversations have only one member.
222 otherUser = conversation.members[0];
223 } else if (conversation.type == MessageDrawerViewConversationContants.CONVERSATION_TYPES.PRIVATE) {
224 // For private conversations, remove the current userId from the members to get the other user.
225 otherUser = conversation.members.reduce(function(carry, member) {
5005d8cf
RW
226 if (!carry && member.id != userId) {
227 carry = member;
228 }
229 return carry;
230 }, null);
734b198f 231 }
5005d8cf 232
734b198f 233 if (otherUser !== null) {
a111ab44 234 formattedConversation.userid = otherUser.id;
5005d8cf 235 formattedConversation.showonlinestatus = otherUser.showonlinestatus;
663ccd58 236 formattedConversation.isonline = otherUser.isonline;
5005d8cf
RW
237 formattedConversation.isblocked = otherUser.isblocked;
238 }
239
71b13a84
BB
240 if (conversation.type == MessageDrawerViewConversationContants.CONVERSATION_TYPES.PUBLIC) {
241 formattedConversation.lastsendername = conversation.members.reduce(function(carry, member) {
9be6dc2d 242 if (!carry && lastMessage && member.id == lastMessage.useridfrom) {
71b13a84
BB
243 carry = member.fullname;
244 }
245 return carry;
12d23810 246 }, null);
71b13a84
BB
247 }
248
5005d8cf
RW
249 return formattedConversation;
250 });
251
51be3bf1
JD
252 formattedConversations.forEach(function(conversation) {
253 if (new Date().toDateString() == new Date(conversation.lastmessagedate * 1000).toDateString()) {
254 conversation.istoday = true;
255 }
256 });
257
d4b1eee6 258 return Templates.render(TEMPLATES.CONVERSATIONS_LIST, {conversations: formattedConversations});
5005d8cf
RW
259 };
260
261 /**
262 * Build the callback to load conversations.
263 *
69efd033 264 * @param {Array|null} types The conversation types for this section.
cb01c45c 265 * @param {bool} includeFavourites Include/exclude favourites.
5005d8cf
RW
266 * @param {Number} offset Result offset
267 * @return {Function}
268 */
69efd033
RW
269 var getLoadCallback = function(types, includeFavourites, offset) {
270 // Note: This function is a bit messy because we've added the concept of loading
271 // multiple conversations types (e.g. private + self) at once but haven't properly
272 // updated the web service to accept an array of types. Instead we've added a new
273 // parameter for the self type which means we can only ever load self + other type.
274 // This should be improved to make it more extensible in the future. Adding new params
275 // for each type isn't very scalable.
276 var type = null;
277 // Include self conversations in the results by default.
278 var includeSelfConversations = true;
279 if (types && types.length) {
280 // Just get the conversation types that aren't "self" for now.
281 var nonSelfConversationTypes = types.filter(function(candidate) {
282 return candidate != MessageDrawerViewConversationContants.CONVERSATION_TYPES.SELF;
283 });
284 // If we're specifically asking for a list of types that doesn't include the self
285 // conversations then we don't need to include them.
286 includeSelfConversations = types.length != nonSelfConversationTypes.length;
287 // As mentioned above the webservice is currently limited to loading one type at a
288 // time (plus self conversations) so let's hope we never change this.
289 type = nonSelfConversationTypes[0];
290 }
291
5005d8cf
RW
292 return function(root, userId) {
293 return MessageRepository.getConversations(
294 userId,
295 type,
296 LOAD_LIMIT + 1,
297 offset,
734b198f 298 includeFavourites,
69efd033 299 includeSelfConversations
5005d8cf
RW
300 )
301 .then(function(response) {
302 var conversations = response.conversations;
303
304 if (conversations.length > LOAD_LIMIT) {
305 conversations = conversations.slice(0, -1);
306 } else {
307 LazyLoadList.setLoadedAll(root, true);
308 }
309
310 offset = offset + LOAD_LIMIT;
311
312 conversations.forEach(function(conversation) {
313 loadedConversationsById[conversation.id] = conversation;
314 });
315
316 return conversations;
317 })
318 .catch(Notification.exception);
319 };
320 };
321
322 /**
323 * Get the total count container element.
324 *
325 * @param {Object} root Overview messages container element.
326 * @return {Object} Total count container element.
327 */
328 var getTotalConversationCountElement = function(root) {
329 return root.find(SELECTORS.SECTION_TOTAL_COUNT);
330 };
331
332 /**
333 * Get the unread conversations count container element.
334 *
335 * @param {Object} root Overview messages container element.
336 * @return {Object} Unread conversations count container element.
337 */
338 var getTotalUnreadConversationCountElement = function(root) {
339 return root.find(SELECTORS.SECTION_UNREAD_COUNT);
340 };
341
342 /**
343 * Increment the total conversations count.
344 *
345 * @param {Object} root Overview messages container element.
346 */
347 var incrementTotalConversationCount = function(root) {
3528c86c
RW
348 if (loadedTotalCounts) {
349 var element = getTotalConversationCountElement(root);
350 var count = parseInt(element.text());
351 count = count + 1;
352 element.text(count);
353 }
5005d8cf
RW
354 };
355
356 /**
357 * Decrement the total conversations count.
358 *
359 * @param {Object} root Overview messages container element.
360 */
361 var decrementTotalConversationCount = function(root) {
3528c86c
RW
362 if (loadedTotalCounts) {
363 var element = getTotalConversationCountElement(root);
364 var count = parseInt(element.text());
365 count = count - 1;
366 element.text(count);
367 }
5005d8cf
RW
368 };
369
370 /**
371 * Decrement the total unread conversations count.
372 *
373 * @param {Object} root Overview messages container element.
374 */
375 var decrementTotalUnreadConversationCount = function(root) {
3528c86c
RW
376 if (loadedUnreadCounts) {
377 var element = getTotalUnreadConversationCountElement(root);
378 var count = parseInt(element.text());
379 count = count - 1;
380 element.text(count);
381
382 if (count < 1) {
383 element.addClass('hidden');
384 }
5005d8cf
RW
385 }
386 };
387
388 /**
389 * Get a contact / conversation element.
390 *
391 * @param {Object} root Overview messages container element.
392 * @param {Number} conversationId The conversation id.
393 * @return {Object} Conversation element.
394 */
395 var getConversationElement = function(root, conversationId) {
396 return root.find('[data-conversation-id="' + conversationId + '"]');
397 };
398
a111ab44
RW
399 /**
400 * Get a contact / conversation element from a user id.
401 *
402 * @param {Object} root Overview messages container element.
403 * @param {Number} userId The user id.
404 * @return {Object} Conversation element.
405 */
406 var getConversationElementFromUserId = function(root, userId) {
407 return root.find('[data-user-id="' + userId + '"]');
408 };
409
bd038cc1
MN
410 /**
411 * Show the conversation is muted icon.
412 *
413 * @param {Object} conversationElement The conversation element.
414 */
415 var muteConversation = function(conversationElement) {
416 conversationElement.find(SELECTORS.MUTED_ICON_CONTAINER).removeClass('hidden');
417 };
418
419 /**
420 * Hide the conversation is muted icon.
421 *
422 * @param {Object} conversationElement The conversation element.
423 */
424 var unmuteConversation = function(conversationElement) {
425 conversationElement.find(SELECTORS.MUTED_ICON_CONTAINER).addClass('hidden');
426 };
427
5005d8cf
RW
428 /**
429 * Show the contact is blocked icon.
430 *
431 * @param {Object} conversationElement The conversation element.
432 */
433 var blockContact = function(conversationElement) {
434 conversationElement.find(SELECTORS.BLOCKED_ICON_CONTAINER).removeClass('hidden');
435 };
436
437 /**
438 * Hide the contact is blocked icon.
439 *
440 * @param {Object} conversationElement The conversation element.
441 */
442 var unblockContact = function(conversationElement) {
443 conversationElement.find(SELECTORS.BLOCKED_ICON_CONTAINER).addClass('hidden');
444 };
445
5005d8cf
RW
446 /**
447 * Create an render new conversation element in the list of conversations.
448 *
449 * @param {Object} root Overview messages container element.
450 * @param {Object} conversation The conversation.
9be6dc2d 451 * @param {Number} userId The logged in user id.
5005d8cf
RW
452 * @return {Object} jQuery promise
453 */
9be6dc2d 454 var createNewConversationFromEvent = function(root, conversation, userId) {
5005d8cf 455 var existingConversations = root.find(SELECTORS.CONVERSATION);
5005d8cf
RW
456
457 if (!existingConversations.length) {
458 // If we didn't have any conversations then we need to show
459 // the content of the list and hide the empty message.
460 var listRoot = LazyLoadList.getRoot(root);
461 LazyLoadList.showContent(listRoot);
462 LazyLoadList.hideEmptyMessage(listRoot);
463 }
464
9be6dc2d
RW
465 // Cache the conversation.
466 loadedConversationsById[conversation.id] = conversation;
5005d8cf 467
9be6dc2d 468 return render([conversation], userId)
5005d8cf
RW
469 .then(function(html) {
470 var contentContainer = LazyLoadList.getContentContainer(root);
471 return contentContainer.prepend(html);
472 })
473 .then(function() {
474 return incrementTotalConversationCount(root);
475 })
476 .catch(Notification.exception);
477 };
478
479 /**
480 * Delete a conversation from the list of conversations.
481 *
482 * @param {Object} root Overview messages container element.
483 * @param {Object} conversationElement The conversation element.
484 */
485 var deleteConversation = function(root, conversationElement) {
486 conversationElement.remove();
487 decrementTotalConversationCount(root);
488
489 var conversations = root.find(SELECTORS.CONVERSATION);
490 if (!conversations.length) {
491 // If we don't have any conversations then we need to hide
492 // the content of the list and show the empty message.
493 var listRoot = LazyLoadList.getRoot(root);
494 LazyLoadList.hideContent(listRoot);
495 LazyLoadList.showEmptyMessage(listRoot);
496 }
497 };
498
499 /**
500 * Mark a conversation as read.
501 *
502 * @param {Object} root Overview messages container element.
503 * @param {Object} conversationElement The conversation element.
504 */
505 var markConversationAsRead = function(root, conversationElement) {
506 var unreadCount = conversationElement.find(SELECTORS.UNREAD_COUNT);
507 unreadCount.text('0');
508 unreadCount.addClass('hidden');
509 decrementTotalUnreadConversationCount(root);
510 };
511
512 /**
513 * Listen to, and handle events in this section.
514 *
cb01c45c 515 * @param {String} namespace Unique identifier for the Routes
5005d8cf
RW
516 * @param {Object} root The section container element.
517 * @param {Function} loadCallback The callback to load items.
862a9b1d 518 * @param {Array|null} types The conversation types for this section
cb01c45c 519 * @param {bool} includeFavourites If this section includes favourites
7cbea160 520 * @param {String} fromPanel Routing argument to send if the section is loaded in message index left panel.
5005d8cf 521 */
862a9b1d 522 var registerEventListeners = function(namespace, root, loadCallback, types, includeFavourites, fromPanel) {
5005d8cf 523 var listRoot = LazyLoadList.getRoot(root);
69efd033
RW
524 var conversationBelongsToThisSection = function(conversation) {
525 // Make sure the type is an int so that the index of check matches correctly.
526 var conversationType = parseInt(conversation.type, 10);
527 if (
528 // If the conversation type isn't one this section cares about then we can ignore it.
529 (types && types.indexOf(conversationType) < 0) ||
530 // If this is the favourites section and the conversation isn't a favourite then ignore it.
531 (includeFavourites && !conversation.isFavourite) ||
532 // If this section doesn't include favourites and the conversation is a favourite then ignore it.
533 (!includeFavourites && conversation.isFavourite)
534 ) {
535 return false;
536 }
537
538 return true;
539 };
5005d8cf
RW
540
541 // Set the minimum height of the section to the height of the toggle. This
542 // smooths out the collapse animation.
543 var toggle = root.find(SELECTORS.TOGGLE);
544 root.css('min-height', toggle.outerHeight());
545
546 root.on('show.bs.collapse', function() {
547 setExpanded(root);
d4b1eee6 548 LazyLoadList.show(listRoot, loadCallback, function(contentContainer, conversations, userId) {
9be6dc2d
RW
549 return render(conversations, userId)
550 .then(function(html) {
551 contentContainer.append(html);
552 return html;
553 })
554 .catch(Notification.exception);
d4b1eee6 555 });
5005d8cf
RW
556 });
557
558 root.on('hidden.bs.collapse', function() {
559 setCollapsed(root);
560 });
561
a111ab44
RW
562 PubSub.subscribe(MessageDrawerEvents.CONTACT_BLOCKED, function(userId) {
563 var conversationElement = getConversationElementFromUserId(root, userId);
5005d8cf
RW
564 if (conversationElement.length) {
565 blockContact(conversationElement);
566 }
567 });
568
a111ab44
RW
569 PubSub.subscribe(MessageDrawerEvents.CONTACT_UNBLOCKED, function(userId) {
570 var conversationElement = getConversationElementFromUserId(root, userId);
bd038cc1 571
5005d8cf
RW
572 if (conversationElement.length) {
573 unblockContact(conversationElement);
574 }
575 });
576
bd038cc1
MN
577 PubSub.subscribe(MessageDrawerEvents.CONVERSATION_SET_MUTED, function(conversation) {
578 var conversationId = conversation.id;
579 var conversationElement = getConversationElement(root, conversationId);
580 if (conversationElement.length) {
581 muteConversation(conversationElement);
582 }
583 });
584
585 PubSub.subscribe(MessageDrawerEvents.CONVERSATION_UNSET_MUTED, function(conversation) {
586 var conversationId = conversation.id;
587 var conversationElement = getConversationElement(root, conversationId);
588 if (conversationElement.length) {
589 unmuteConversation(conversationElement);
590 }
591 });
592
5005d8cf 593 PubSub.subscribe(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE, function(conversation) {
69efd033 594 if (!conversationBelongsToThisSection(conversation)) {
5005d8cf
RW
595 return;
596 }
597
9be6dc2d 598 var loggedInUserId = conversation.loggedInUserId;
5005d8cf
RW
599 var conversationId = conversation.id;
600 var element = getConversationElement(root, conversationId);
9be6dc2d 601 conversation = formatConversationFromEvent(conversation);
5005d8cf 602 if (element.length) {
d4b1eee6 603 var contentContainer = LazyLoadList.getContentContainer(root);
9be6dc2d 604 render([conversation], loggedInUserId)
d4b1eee6
P
605 .then(function(html) {
606 contentContainer.prepend(html);
607 element.remove();
608 return html;
609 })
610 .catch(Notification.exception);
5005d8cf 611 } else {
9be6dc2d 612 createNewConversationFromEvent(root, conversation, loggedInUserId);
5005d8cf
RW
613 }
614 });
615
616 PubSub.subscribe(MessageDrawerEvents.CONVERSATION_DELETED, function(conversationId) {
617 var conversationElement = getConversationElement(root, conversationId);
d4b1eee6 618 delete loadedConversationsById[conversationId];
5005d8cf
RW
619 if (conversationElement.length) {
620 deleteConversation(root, conversationElement);
621 }
622 });
623
624 PubSub.subscribe(MessageDrawerEvents.CONVERSATION_READ, function(conversationId) {
625 var conversationElement = getConversationElement(root, conversationId);
626 if (conversationElement.length) {
627 markConversationAsRead(root, conversationElement);
628 }
629 });
630
631 PubSub.subscribe(MessageDrawerEvents.CONVERSATION_SET_FAVOURITE, function(conversation) {
632 var conversationElement = null;
69efd033 633 if (conversationBelongsToThisSection(conversation)) {
5005d8cf
RW
634 conversationElement = getConversationElement(root, conversation.id);
635 if (!conversationElement.length) {
9be6dc2d
RW
636 createNewConversationFromEvent(
637 root,
638 formatConversationFromEvent(conversation),
639 conversation.loggedInUserId
640 );
5005d8cf 641 }
69efd033 642 } else {
5005d8cf
RW
643 conversationElement = getConversationElement(root, conversation.id);
644 if (conversationElement.length) {
645 deleteConversation(root, conversationElement);
646 }
647 }
648 });
649
650 PubSub.subscribe(MessageDrawerEvents.CONVERSATION_UNSET_FAVOURITE, function(conversation) {
651 var conversationElement = null;
69efd033 652 if (conversationBelongsToThisSection(conversation)) {
5005d8cf
RW
653 conversationElement = getConversationElement(root, conversation.id);
654 if (!conversationElement.length) {
9be6dc2d
RW
655 createNewConversationFromEvent(
656 root,
657 formatConversationFromEvent(conversation),
658 conversation.loggedInUserId
659 );
5005d8cf 660 }
69efd033
RW
661 } else {
662 conversationElement = getConversationElement(root, conversation.id);
663 if (conversationElement.length) {
664 deleteConversation(root, conversationElement);
665 }
5005d8cf
RW
666 }
667 });
668
669 CustomEvents.define(root, [CustomEvents.events.activate]);
670 root.on(CustomEvents.events.activate, SELECTORS.CONVERSATION, function(e, data) {
671 var conversationElement = $(e.target).closest(SELECTORS.CONVERSATION);
672 var conversationId = conversationElement.attr('data-conversation-id');
673 var conversation = loadedConversationsById[conversationId];
7cbea160 674 MessageDrawerRouter.go(namespace, MessageDrawerRoutes.VIEW_CONVERSATION, conversation, fromPanel);
5005d8cf
RW
675
676 data.originalEvent.preventDefault();
677 });
678 };
679
680 /**
681 * Setup the section.
682 *
cb01c45c
MN
683 * @param {String} namespace Unique identifier for the Routes
684 * @param {Object} header The header container element.
685 * @param {Object} body The section container element.
686 * @param {Object} footer The footer container element.
69efd033 687 * @param {Array} types The conversation types that show in this section
cb01c45c 688 * @param {bool} includeFavourites If this section includes favourites
3528c86c
RW
689 * @param {Object} totalCountPromise Resolves wth the total conversations count
690 * @param {Object} unreadCountPromise Resolves wth the unread conversations count
e16ab06b 691 * @param {bool} fromPanel shown in message app panel.
5005d8cf 692 */
862a9b1d 693 var show = function(namespace, header, body, footer, types, includeFavourites, totalCountPromise, unreadCountPromise,
7cbea160 694 fromPanel) {
cb01c45c 695 var root = $(body);
5005d8cf
RW
696
697 if (!root.attr('data-init')) {
69efd033 698 var loadCallback = getLoadCallback(types, includeFavourites, 0);
862a9b1d 699 registerEventListeners(namespace, root, loadCallback, types, includeFavourites, fromPanel);
5005d8cf
RW
700
701 if (isVisible(root)) {
702 setExpanded(root);
703 var listRoot = LazyLoadList.getRoot(root);
d4b1eee6 704 LazyLoadList.show(listRoot, loadCallback, function(contentContainer, conversations, userId) {
9be6dc2d
RW
705 return render(conversations, userId)
706 .then(function(html) {
707 contentContainer.append(html);
708 return html;
709 })
710 .catch(Notification.exception);
d4b1eee6 711 });
5005d8cf
RW
712 }
713
3528c86c
RW
714 // This is given to us by the calling code because the total counts for all sections
715 // are loaded in a single ajax request rather than one request per section.
716 totalCountPromise.then(function(count) {
717 renderTotalCount(root, count);
718 loadedTotalCounts = true;
719 return;
720 })
721 .catch(function() {
722 // Silently ignore if we can't updated the counts. No need to bother the user.
723 });
724
e79a79ad
RW
725 // This is given to us by the calling code because the unread counts for all sections
726 // are loaded in a single ajax request rather than one request per section.
3528c86c
RW
727 unreadCountPromise.then(function(count) {
728 renderUnreadCount(root, count);
e79a79ad 729 loadedUnreadCounts = true;
3528c86c
RW
730 return;
731 })
732 .catch(function() {
733 // Silently ignore if we can't updated the counts. No need to bother the user.
734 });
735
5005d8cf
RW
736 root.attr('data-init', true);
737 }
738 };
739
740 return {
3528c86c
RW
741 show: show,
742 isVisible: isVisible
5005d8cf
RW
743 };
744});