MDL-66477 message: Use the generic drawer for the message drawer
[moodle.git] / message / amd / src / message_drawer.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 message drawer.
18 *
19 * @module core_message/message_drawer
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/pubsub',
28 'core_message/message_drawer_view_contact',
29 'core_message/message_drawer_view_contacts',
30 'core_message/message_drawer_view_conversation',
31 'core_message/message_drawer_view_group_info',
32 'core_message/message_drawer_view_overview',
33 'core_message/message_drawer_view_search',
34 'core_message/message_drawer_view_settings',
35 'core_message/message_drawer_router',
36 'core_message/message_drawer_routes',
11970d0a
AN
37 'core_message/message_drawer_events',
38 'core/pending',
8aca1807 39 'core/drawer',
5005d8cf
RW
40],
41function(
42 $,
43 CustomEvents,
44 PubSub,
45 ViewContact,
46 ViewContacts,
47 ViewConversation,
48 ViewGroupInfo,
49 ViewOverview,
50 ViewSearch,
51 ViewSettings,
52 Router,
53 Routes,
11970d0a 54 Events,
8aca1807
JP
55 Pending,
56 Drawer
5005d8cf
RW
57) {
58
59 var SELECTORS = {
7cbea160
BB
60 PANEL_BODY_CONTAINER: '[data-region="panel-body-container"]',
61 PANEL_HEADER_CONTAINER: '[data-region="panel-header-container"]',
5005d8cf
RW
62 VIEW_CONTACT: '[data-region="view-contact"]',
63 VIEW_CONTACTS: '[data-region="view-contacts"]',
64 VIEW_CONVERSATION: '[data-region="view-conversation"]',
65 VIEW_GROUP_INFO: '[data-region="view-group-info"]',
66 VIEW_OVERVIEW: '[data-region="view-overview"]',
67 VIEW_SEARCH: '[data-region="view-search"]',
68 VIEW_SETTINGS: '[data-region="view-settings"]',
69 ROUTES: '[data-route]',
70 ROUTES_BACK: '[data-route-back]',
71 HEADER_CONTAINER: '[data-region="header-container"]',
72 BODY_CONTAINER: '[data-region="body-container"]',
73 FOOTER_CONTAINER: '[data-region="footer-container"]',
74 };
75
76 /**
77 * Get elements for route.
78 *
cb01c45c 79 * @param {String} namespace Unique identifier for the Routes
5005d8cf
RW
80 * @param {Object} root The message drawer container.
81 * @param {string} selector The route container.
82 *
83 * @return {array} elements Found route container objects.
84 */
cb01c45c 85 var getParametersForRoute = function(namespace, root, selector) {
7cbea160
BB
86
87 var header = root.find(SELECTORS.HEADER_CONTAINER).find(selector);
88 if (!header.length) {
89 header = root.find(SELECTORS.PANEL_HEADER_CONTAINER).find(selector);
90 }
91 var body = root.find(SELECTORS.BODY_CONTAINER).find(selector);
92 if (!body.length) {
93 body = root.find(SELECTORS.PANEL_BODY_CONTAINER).find(selector);
94 }
95 var footer = root.find(SELECTORS.FOOTER_CONTAINER).find(selector);
5005d8cf 96
cb01c45c
MN
97 return [
98 namespace,
99 header.length ? header : null,
100 body.length ? body : null,
101 footer.length ? footer : null
102 ];
5005d8cf
RW
103 };
104
105 var routes = [
106 [Routes.VIEW_CONTACT, SELECTORS.VIEW_CONTACT, ViewContact.show, ViewContact.description],
107 [Routes.VIEW_CONTACTS, SELECTORS.VIEW_CONTACTS, ViewContacts.show, ViewContacts.description],
108 [Routes.VIEW_CONVERSATION, SELECTORS.VIEW_CONVERSATION, ViewConversation.show, ViewConversation.description],
109 [Routes.VIEW_GROUP_INFO, SELECTORS.VIEW_GROUP_INFO, ViewGroupInfo.show, ViewGroupInfo.description],
110 [Routes.VIEW_OVERVIEW, SELECTORS.VIEW_OVERVIEW, ViewOverview.show, ViewOverview.description],
111 [Routes.VIEW_SEARCH, SELECTORS.VIEW_SEARCH, ViewSearch.show, ViewSearch.description],
cb01c45c 112 [Routes.VIEW_SETTINGS, SELECTORS.VIEW_SETTINGS, ViewSettings.show, ViewSettings.description]
5005d8cf
RW
113 ];
114
115 /**
116 * Create routes.
117 *
cb01c45c 118 * @param {String} namespace Unique identifier for the Routes
5005d8cf
RW
119 * @param {Object} root The message drawer container.
120 */
cb01c45c 121 var createRoutes = function(namespace, root) {
5005d8cf 122 routes.forEach(function(route) {
cb01c45c 123 Router.add(namespace, route[0], getParametersForRoute(namespace, root, route[1]), route[2], route[3]);
5005d8cf
RW
124 });
125 };
126
127 /**
128 * Show the message drawer.
129 *
cb01c45c 130 * @param {string} namespace The route namespace.
5005d8cf
RW
131 * @param {Object} root The message drawer container.
132 */
cb01c45c 133 var show = function(namespace, root) {
5005d8cf 134 if (!root.attr('data-shown')) {
cb01c45c 135 Router.go(namespace, Routes.VIEW_OVERVIEW);
5005d8cf
RW
136 root.attr('data-shown', true);
137 }
138
8aca1807
JP
139 var drawerRoot = Drawer.getDrawerRoot(root);
140 if (drawerRoot) {
141 Drawer.show(drawerRoot);
142 }
5005d8cf
RW
143 };
144
145 /**
146 * Hide the message drawer.
147 *
148 * @param {Object} root The message drawer container.
149 */
150 var hide = function(root) {
8aca1807
JP
151 var drawerRoot = Drawer.getDrawerRoot(root);
152 if (drawerRoot) {
153 Drawer.hide(drawerRoot);
154 }
5005d8cf
RW
155 };
156
157 /**
158 * Check if the drawer is visible.
159 *
160 * @param {Object} root The message drawer container.
8aca1807 161 * @return {boolean}
5005d8cf
RW
162 */
163 var isVisible = function(root) {
8aca1807
JP
164 var drawerRoot = Drawer.getDrawerRoot(root);
165 if (drawerRoot) {
166 return Drawer.isVisible(drawerRoot);
167 }
168 return true;
5005d8cf
RW
169 };
170
171 /**
172 * Listen to and handle events for routing, showing and hiding the message drawer.
173 *
cb01c45c 174 * @param {string} namespace The route namespace.
5005d8cf 175 * @param {Object} root The message drawer container.
fd998fc6 176 * @param {bool} alwaysVisible Is this messaging app always shown?
5005d8cf 177 */
fd998fc6 178 var registerEventListeners = function(namespace, root, alwaysVisible) {
5005d8cf
RW
179 CustomEvents.define(root, [CustomEvents.events.activate]);
180 var paramRegex = /^data-route-param-?(\d*)$/;
181
182 root.on(CustomEvents.events.activate, SELECTORS.ROUTES, function(e, data) {
183 var element = $(e.target).closest(SELECTORS.ROUTES);
184 var route = element.attr('data-route');
185 var attributes = [];
186
187 for (var i = 0; i < element[0].attributes.length; i++) {
188 attributes.push(element[0].attributes[i]);
189 }
190
191 var paramAttributes = attributes.filter(function(attribute) {
192 var name = attribute.nodeName;
193 var match = paramRegex.test(name);
194 return match;
195 });
196 paramAttributes.sort(function(a, b) {
197 var aParts = paramRegex.exec(a.nodeName);
198 var bParts = paramRegex.exec(b.nodeName);
199 var aIndex = aParts.length > 1 ? aParts[1] : 0;
200 var bIndex = bParts.length > 1 ? bParts[1] : 0;
201
202 if (aIndex < bIndex) {
203 return -1;
204 } else if (bIndex < aIndex) {
205 return 1;
206 } else {
207 return 0;
208 }
209 });
210
211 var params = paramAttributes.map(function(attribute) {
212 return attribute.nodeValue;
213 });
7cbea160 214
cb01c45c 215 var routeParams = [namespace, route].concat(params);
5005d8cf
RW
216
217 Router.go.apply(null, routeParams);
218
219 data.originalEvent.preventDefault();
220 });
221
222 root.on(CustomEvents.events.activate, SELECTORS.ROUTES_BACK, function(e, data) {
cb01c45c 223 Router.back(namespace);
5005d8cf
RW
224
225 data.originalEvent.preventDefault();
226 });
227
11970d0a
AN
228 // These are theme-specific to help us fix random behat fails.
229 // These events target those events defined in BS3 and BS4 onwards.
230 root.on('hide.bs.collapse', '.collapse', function(e) {
231 var pendingPromise = new Pending();
232 $(e.target).one('hidden.bs.collapse', function() {
233 pendingPromise.resolve();
234 });
235 });
236
237 root.on('show.bs.collapse', '.collapse', function(e) {
238 var pendingPromise = new Pending();
239 $(e.target).one('shown.bs.collapse', function() {
240 pendingPromise.resolve();
241 });
242 });
243
fd998fc6
MN
244 if (!alwaysVisible) {
245 PubSub.subscribe(Events.SHOW, function() {
246 show(namespace, root);
247 });
5005d8cf 248
fd998fc6 249 PubSub.subscribe(Events.HIDE, function() {
5005d8cf 250 hide(root);
fd998fc6
MN
251 });
252
253 PubSub.subscribe(Events.TOGGLE_VISIBILITY, function() {
254 if (isVisible(root)) {
255 hide(root);
256 } else {
257 show(namespace, root);
258 }
259 });
260 }
5005d8cf
RW
261
262 PubSub.subscribe(Events.SHOW_CONVERSATION, function(conversationId) {
cb01c45c
MN
263 show(namespace, root);
264 Router.go(namespace, Routes.VIEW_CONVERSATION, conversationId);
5005d8cf
RW
265 });
266
1d3535f9 267 PubSub.subscribe(Events.CREATE_CONVERSATION_WITH_USER, function(userId) {
cb01c45c
MN
268 show(namespace, root);
269 Router.go(namespace, Routes.VIEW_CONVERSATION, null, 'create', userId);
1d3535f9
RW
270 });
271
5005d8cf 272 PubSub.subscribe(Events.SHOW_SETTINGS, function() {
cb01c45c
MN
273 show(namespace, root);
274 Router.go(namespace, Routes.VIEW_SETTINGS);
5005d8cf 275 });
663ccd58
RW
276
277 PubSub.subscribe(Events.PREFERENCES_UPDATED, function(preferences) {
278 var filteredPreferences = preferences.filter(function(preference) {
279 return preference.type == 'message_entertosend';
280 });
281 var enterToSendPreference = filteredPreferences.length ? filteredPreferences[0] : null;
282
283 if (enterToSendPreference) {
284 var viewConversationFooter = root.find(SELECTORS.FOOTER_CONTAINER).find(SELECTORS.VIEW_CONVERSATION);
285 viewConversationFooter.attr('data-enter-to-send', enterToSendPreference.value);
286 }
287 });
5005d8cf
RW
288 };
289
290 /**
291 * Initialise the message drawer.
292 *
293 * @param {Object} root The message drawer container.
cb01c45c 294 * @param {String} uniqueId Unique identifier for the Routes
fd998fc6 295 * @param {bool} alwaysVisible Should we show the app now, or wait for the user?
11970d0a 296 * @param {Object} route
5005d8cf 297 */
6cb33ce7 298 var init = function(root, uniqueId, alwaysVisible, route) {
5005d8cf 299 root = $(root);
cb01c45c 300 createRoutes(uniqueId, root);
fd998fc6 301 registerEventListeners(uniqueId, root, alwaysVisible);
6cb33ce7 302
fd998fc6
MN
303 if (alwaysVisible) {
304 show(uniqueId, root);
6cb33ce7
RW
305
306 if (route) {
307 var routeParams = route.params || [];
308 routeParams = [uniqueId, route.path].concat(routeParams);
309 Router.go.apply(null, routeParams);
fd998fc6
MN
310 }
311 }
5005d8cf
RW
312 };
313
314 return {
315 init: init,
316 };
317});