Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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 overview page of the message drawer.
18
 *
19
 * @module     core_message/message_drawer_view_overview
20
 * @copyright  2018 Ryan Wyllie <ryan@moodle.com>
21
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22
 */
23
define(
24
[
25
    'jquery',
26
    'core/key_codes',
27
    'core/pubsub',
28
    'core/str',
29
    'core_message/message_drawer_router',
30
    'core_message/message_drawer_routes',
31
    'core_message/message_drawer_events',
32
    'core_message/message_drawer_view_overview_section',
33
    'core_message/message_repository',
34
    'core_message/message_drawer_view_conversation_constants'
35
],
36
function(
37
    $,
38
    KeyCodes,
39
    PubSub,
40
    Str,
41
    Router,
42
    Routes,
43
    MessageDrawerEvents,
44
    Section,
45
    MessageRepository,
46
    Constants
47
) {
48
 
49
    var SELECTORS = {
50
        CONTACT_REQUEST_COUNT: '[data-region="contact-request-count"]',
51
        FAVOURITES: '[data-region="view-overview-favourites"]',
52
        GROUP_MESSAGES: '[data-region="view-overview-group-messages"]',
53
        MESSAGES: '[data-region="view-overview-messages"]',
54
        SEARCH_INPUT: '[data-region="view-overview-search-input"]',
55
        SECTION_TOGGLE_BUTTON: '[data-toggle]'
56
    };
57
 
58
    // Categories displayed in the message drawer. Some methods (such as filterCountsByType) are expecting their value
59
    // will be the same as the defined in the CONVERSATION_TYPES, except for the favourite.
60
    var OVERVIEW_SECTION_TYPES = {
61
        PRIVATE: [Constants.CONVERSATION_TYPES.PRIVATE, Constants.CONVERSATION_TYPES.SELF],
62
        PUBLIC: [Constants.CONVERSATION_TYPES.PUBLIC],
63
        FAVOURITE: null
64
    };
65
 
66
    var loadAllCountsPromise = null;
67
 
68
    /**
69
     * Load the total and unread conversation counts from the server for this user. This function
70
     * returns a jQuery promise that will be resolved with the counts.
71
     *
72
     * The request is only sent once per page load and will be cached for subsequent
73
     * calls to this function.
74
     *
75
     * @param {Number} loggedInUserId The logged in user's id
76
     * @return {Object} jQuery promise
77
     */
78
    var loadAllCounts = function(loggedInUserId) {
79
        if (loadAllCountsPromise === null) {
80
            loadAllCountsPromise = MessageRepository.getAllConversationCounts(loggedInUserId);
81
        }
82
 
83
        return loadAllCountsPromise;
84
    };
85
 
86
    /**
87
     * Filter a set of counts to return only the count for the given type.
88
     *
89
     * This is used on the result returned by the loadAllCounts function.
90
     *
91
     * @param {Object} counts Conversation counts indexed by conversation type.
92
     * @param {Array|null} types The conversation types handlded by this section (null for all conversation types).
93
     * @param {bool} includeFavourites If this section includes favourites
94
     * @return {Number}
95
     */
96
    var filterCountsByTypes = function(counts, types, includeFavourites) {
97
        var total = 0;
98
 
99
        if (types && types.length) {
100
            total = types.reduce(function(carry, type) {
101
                return carry + counts.types[type];
102
            }, total);
103
        }
104
 
105
        if (includeFavourites) {
106
            total += counts.favourites;
107
        }
108
 
109
        return total;
110
    };
111
 
112
    /**
113
     * Opens one of the sections based on whether the section has unread conversations
114
     * or any conversations
115
     *
116
     * Default section priority is favourites, groups, then messages. A section can increase
117
     * in priority if it has conversations in it. It can increase even further if it has
118
     * unread conversations.
119
     *
120
     * @param {Array} sections List of section roots, total counts, and unread counts.
121
     */
122
    var openSection = function(sections) {
123
        var isAlreadyOpen = sections.some(function(section) {
124
            var sectionRoot = section[0];
125
            return Section.isVisible(sectionRoot);
126
        });
127
 
128
        if (isAlreadyOpen) {
129
            // The user has already opened a section so there is nothing to do.
130
            return;
131
        }
132
 
133
        // Order the sections so that sections with unread conversations are prioritised
134
        // over sections without and sections with total conversations are prioritised
135
        // over sections without.
136
        sections.sort(function(a, b) {
137
            var aTotal = a[1];
138
            var aUnread = a[2];
139
            var bTotal = b[1];
140
            var bUnread = b[2];
141
 
142
            if (aUnread > 0 && bUnread == 0) {
143
                return -1;
144
            } else if (aUnread == 0 && bUnread > 0) {
145
                return 1;
146
            } else if (aTotal > 0 && bTotal == 0) {
147
                return -1;
148
            } else if (aTotal == 0 && bTotal > 0) {
149
                return 1;
150
            } else {
151
                return 0;
152
            }
153
        });
154
 
155
        // Get the root of the first section after sorting.
156
        var sectionRoot = sections[0][0];
157
        var button = sectionRoot.find(SELECTORS.SECTION_TOGGLE_BUTTON);
158
        // Click it to expand it.
159
        button.click();
160
    };
161
 
162
    /**
163
     * Get the search input text element.
164
     *
165
     * @param  {Object} header Overview header container element.
166
     * @return {Object} The search input element.
167
     */
168
    var getSearchInput = function(header) {
169
        return header.find(SELECTORS.SEARCH_INPUT);
170
    };
171
 
172
    /**
173
     * Get the logged in user id.
174
     *
175
     * @param {Object} body Overview body container element.
176
     * @return {String} Logged in user id.
177
     */
178
    var getLoggedInUserId = function(body) {
179
        return body.attr('data-user-id');
180
    };
181
 
182
    /**
183
     * Decrement the contact request count. If the count is zero or below then
184
     * hide the count.
185
     *
186
     * @param {Object} header Conversation header container element.
187
     * @return {Function} A function to handle decrementing the count.
188
     */
189
    var decrementContactRequestCount = function(header) {
190
        return function() {
191
            var countContainer = header.find(SELECTORS.CONTACT_REQUEST_COUNT);
192
            var count = parseInt(countContainer.text(), 10);
193
            count = isNaN(count) ? 0 : count - 1;
194
 
195
            if (count <= 0) {
196
                countContainer.addClass('hidden');
197
            } else {
198
                countContainer.text(count);
199
            }
200
        };
201
    };
202
 
203
    /**
204
     * Listen to, and handle event in the overview header.
205
     *
206
     * @param {String} namespace Unique identifier for the Routes
207
     * @param {Object} header Conversation header container element.
208
     */
209
    var registerEventListeners = function(namespace, header) {
210
        var searchInput = getSearchInput(header);
211
        var ignoredKeys = [KeyCodes.tab, KeyCodes.shift, KeyCodes.ctrl, KeyCodes.alt];
212
 
213
        searchInput.on('click', function() {
214
            Router.go(namespace, Routes.VIEW_SEARCH);
215
        });
216
        searchInput.on('keydown', function(e) {
217
            if (ignoredKeys.indexOf(e.keyCode) < 0 && e.key != 'Meta') {
218
                Router.go(namespace, Routes.VIEW_SEARCH);
219
            }
220
        });
221
 
222
        PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_ACCEPTED, decrementContactRequestCount(header));
223
        PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_DECLINED, decrementContactRequestCount(header));
224
    };
225
 
226
    /**
227
     * Setup the overview page.
228
     *
229
     * @param {String} namespace Unique identifier for the Routes
230
     * @param {Object} header Overview header container element.
231
     * @param {Object} body Overview body container element.
232
     * @return {Object} jQuery promise
233
     */
234
    var show = function(namespace, header, body) {
235
        if (!header.attr('data-init')) {
236
            registerEventListeners(namespace, header);
237
            header.attr('data-init', true);
238
        }
239
        var fromPanel = header.attr('data-in-panel') ? 'frompanel' : null;
240
 
241
        getSearchInput(header).val('');
242
        var loggedInUserId = getLoggedInUserId(body);
243
        var allCounts = loadAllCounts(loggedInUserId);
244
 
245
        var sections = [
246
            // Favourite conversations section.
247
            [body.find(SELECTORS.FAVOURITES), OVERVIEW_SECTION_TYPES.FAVOURITE, true],
248
            // Group conversations section.
249
            [body.find(SELECTORS.GROUP_MESSAGES), OVERVIEW_SECTION_TYPES.PUBLIC, false],
250
            // Private conversations section.
251
            [body.find(SELECTORS.MESSAGES), OVERVIEW_SECTION_TYPES.PRIVATE, false]
252
        ];
253
 
254
        sections.forEach(function(args) {
255
            var sectionRoot = args[0];
256
            var sectionTypes = args[1];
257
            var includeFavourites = args[2];
258
            var totalCountPromise = allCounts.then(function(result) {
259
                return filterCountsByTypes(result.total, sectionTypes, includeFavourites);
260
            });
261
            var unreadCountPromise = allCounts.then(function(result) {
262
                return filterCountsByTypes(result.unread, sectionTypes, includeFavourites);
263
            });
264
 
265
            Section.show(namespace, null, sectionRoot, null, sectionTypes, includeFavourites,
266
                totalCountPromise, unreadCountPromise, fromPanel);
267
        });
268
 
269
        return allCounts.then(function(result) {
270
                var sectionParams = sections.map(function(section) {
271
                    var sectionRoot = section[0];
272
                    var sectionTypes = section[1];
273
                    var includeFavourites = section[2];
274
                    var totalCount = filterCountsByTypes(result.total, sectionTypes, includeFavourites);
275
                    var unreadCount = filterCountsByTypes(result.unread, sectionTypes, includeFavourites);
276
 
277
                    return [sectionRoot, totalCount, unreadCount];
278
                });
279
 
280
                // Open up one of the sections for the user.
281
                return openSection(sectionParams);
282
            });
283
    };
284
 
285
    /**
286
     * String describing this page used for aria-labels.
287
     *
288
     * @return {Object} jQuery promise
289
     */
290
    var description = function() {
291
        return Str.get_string('messagedrawerviewoverview', 'core_message');
292
    };
293
 
294
    return {
295
        show: show,
296
        description: description
297
    };
298
});