messageListView.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /*
  2. * Licensed Materials - Property of IBM
  3. *
  4. * IBM Cognos Products: SHARE
  5. *
  6. * (C) Copyright IBM Corp. 2015, 2016
  7. *
  8. * US Government Users Restricted Rights - Use, duplication or disclosure
  9. * restricted by GSA ADP Schedule Contract with IBM Corp.
  10. */
  11. define([ "bi/commons/ui/View",
  12. 'underscore',
  13. 'jquery',
  14. 'q',
  15. 'bi/sharecommon/utils/simpledoT',
  16. 'text!bi/notifications/templates/messagesList.html',
  17. 'bi/sharecommon/utils/translator',
  18. 'bi/commons/utils/Utils',
  19. "bi/notifications/app/n10nController",
  20. 'bi/commons/utils/ContentFormatter',
  21. 'moment-timezone'
  22. ],
  23. function(View, _, $, Q, dot, msgTemplate, t, Utils, controller, ContentFormatter, moment) {
  24. var DEFAULT_DELAY = 500;
  25. var __STANDARD_DATE_FORMAT = 'L';
  26. var messageListView = View.extend({
  27. /** constructor
  28. * expects:
  29. * { data: <msgList>, context: <context>}
  30. */
  31. init: function(options){
  32. messageListView.inherited('init', this, arguments);
  33. $.extend(this, options);
  34. this.timezone = this.glassContext.services.userProfile.preferences.timeZoneID;
  35. this.defaultLocale = this.glassContext.services.userProfile.preferences.contentLocale || "en-us";
  36. this.KEYS = {
  37. 'TAB': 9,
  38. 'ENTER': 13,
  39. 'SPACE': 32,
  40. 'LEFT_ARROW': 37,
  41. 'UP_ARROW': 38,
  42. 'RIGHT_ARROW': 39,
  43. 'DOWN_ARROW': 40
  44. };
  45. },
  46. setData: function(newData) {
  47. this.data = newData;
  48. },
  49. _setEvents: function() {
  50. // Listen for a keydown event on the full message div
  51. this.$el.find('.n10n_message').off('.n10n').on('keydown.n10n', this._handleKeyDown.bind(this));
  52. // Listen for a "primaryaction" even on the message content div
  53. this.$el.find(".n10n_message_inner:not(.clickBound)").addClass('clickBound').on('primaryaction', function(event) {
  54. var messageId = event.currentTarget.parentNode.id;
  55. var $notification = this.$el.find(event.currentTarget.parentNode);
  56. // If the target of the event isn't selected (and thus shown)
  57. // then show it and mark it as selected
  58. if(!$notification.hasClass('selected')) {
  59. var messageContext = {
  60. messageId: messageId,
  61. messageListView: this,
  62. glassContext: this.glassContext,
  63. onHideCallback: this.clear.bind(this)
  64. };
  65. controller.showNotificationContent(messageContext, this.slideout)
  66. // visually display the current message as read
  67. this._displayAsRead($notification);
  68. }
  69. }.bind(this));
  70. // Listen for a "primaryaction" event on the delete button
  71. this.$el.find(".n10n_delete_icon:not(.clickBound)").addClass('clickBound').on('primaryaction', function(event) {
  72. var messageId = event.currentTarget.parentNode.id;
  73. var deletionRequest = {
  74. messageId: messageId,
  75. pending: true
  76. };
  77. if (this.glassContext.services.userProfile.preferences.accessibilityFeatures) {
  78. this.glassContext.appController.showMessage(t.translate('delete_notification_confirm_message'),
  79. t.translate('delete_confirm'), 'info', ['ok','cancel'], undefined, function(evt) {
  80. if (evt.btn==='ok') {
  81. this._completeNotificationDelete(deletionRequest);
  82. }
  83. }.bind(this));
  84. } else {
  85. var $message = $(event.currentTarget.parentNode);
  86. $message.hide(DEFAULT_DELAY);
  87. this.glassContext.appController.showToast(t.translate('delete_notification_message'), {
  88. 'type': 'info',
  89. 'btnLabel': t.translate('delete_undo'),
  90. 'callback': function() {
  91. deletionRequest.pending = false;
  92. this.$el.find('#' + deletionRequest.messageId).show(DEFAULT_DELAY);
  93. }.bind(this),
  94. 'newestOnTop': true,
  95. 'preventDuplicates': false,
  96. 'timeOut': 6000,
  97. 'extendedTimeOut': 1000,
  98. 'onHidden': function() {
  99. this._completeNotificationDelete(deletionRequest);
  100. }.bind(this)
  101. });
  102. }
  103. }.bind(this));
  104. },
  105. /**
  106. * Handles moving the focus to the proper previous or next message in the notification list
  107. * depending on keys pressed as well as other factors
  108. */
  109. _handleKeyDown: function(event) {
  110. var key = event.keyCode || event.charCode || event.which || 0;
  111. if(key === this.KEYS.UP_ARROW || key === this.KEYS.DOWN_ARROW ||
  112. key === this.KEYS.LEFT_ARROW || key === this.KEYS.RIGHT_ARROW) {
  113. var $fromTarget = $(event.currentTarget);
  114. var $toTarget;
  115. switch(key) {
  116. case this.KEYS.DOWN_ARROW:
  117. case this.KEYS.RIGHT_ARROW:
  118. $toTarget = $fromTarget.next();
  119. if($toTarget.css('display') == 'none') {
  120. $toTarget = $toTarget.next();
  121. }
  122. break;
  123. case this.KEYS.UP_ARROW:
  124. case this.KEYS.LEFT_ARROW:
  125. $toTarget = $fromTarget.prev();
  126. if($toTarget.css('display') == 'none') {
  127. $toTarget = $toTarget.prev();
  128. }
  129. break;
  130. }
  131. if($toTarget && $toTarget.length > 0) {
  132. $toTarget.find('div.n10n_message_inner').focus();
  133. }
  134. event.preventDefault();
  135. }
  136. // Check to see the number of siblings to the parent of the
  137. // activeElement (the focused one) and load more messages if one or less.
  138. if($(event.target.parentNode).nextAll().length <= 1 &&
  139. (key === this.KEYS.DOWN_ARROW || key === this.KEYS.RIGHT_ARROW)) {
  140. if(this.loadMessagesCallback && typeof this.loadMessagesCallback === 'function'){
  141. this.loadMessagesCallback(this.$el.parent());
  142. }
  143. }
  144. },
  145. _completeNotificationDelete: function(deleteRequest) {
  146. var $pendingDeleteMessage = this.$el.find('#'+deleteRequest.messageId);
  147. var context = {
  148. glassContext: this.glassContext
  149. };
  150. if(deleteRequest.pending) {
  151. controller.deleteNotification(deleteRequest.messageId, context).done(function(){
  152. $pendingDeleteMessage.remove();
  153. }.bind(this));
  154. }
  155. },
  156. clear: function() {
  157. // deactivate any selected messages
  158. this.$el.find(".n10n_message").removeClass("selected");
  159. },
  160. _displayAsRead: function($el) {
  161. $el.attr('aria-label', t.translate('read_notification'));
  162. $el.find("strong").contents().unwrap();
  163. $el.find('.wft_message_unread_32').addClass('wft_message_read_32').removeClass('wft_message_unread_32');
  164. },
  165. _formatContent: function(){
  166. var subjects = this.$el.find('.n10n_message_subject');
  167. var dates = this.$el.find('.n10n_message_date');
  168. var senders = this.$el.find('.n10n_message_sender');
  169. for(var i=0; i<subjects.length; i++){
  170. if(subjects[i]) {
  171. ContentFormatter.middleShortenString(subjects[i]);
  172. }
  173. ContentFormatter.middleShortenString(dates[i]);
  174. ContentFormatter.middleShortenString(senders[i]);
  175. }
  176. },
  177. _setIcon: function(){
  178. var deleteIcon = this.$el.find(".n10n_delete_icon:not(:has(svg))");
  179. Utils.setIcon(deleteIcon, 'common-remove-delete', t.translate('delete_notification'));
  180. },
  181. render: function() {
  182. var svcData = [];
  183. var data = this.data || [];
  184. var unreadNotification = t.translate('unread_notification');
  185. var readNotification = t.translate('read_notification');
  186. var deleteLabel = t.translate('delete_message_label');
  187. for(var i=0; i<data.length;i++) {
  188. svcData.push({ icon: (data[i].unread)? "wft_message_unread_32" :"wft_message_read_32",
  189. subject: _.escape(data[i].subject) || "",
  190. sender: _.escape(data[i].sender) || "",
  191. date: moment(data[i].created).locale(this.defaultLocale).tz(this.timezone).format(__STANDARD_DATE_FORMAT),
  192. read: (data[i].unread)? "unread" :"read",
  193. id: data[i].id || "",
  194. label: (data[i].unread) ? unreadNotification : readNotification,
  195. deleteLabel: deleteLabel
  196. });
  197. }
  198. var listAttrs = {
  199. msgs: svcData
  200. };
  201. var list = dot.simpleTemplate(msgTemplate);
  202. this.$el.append(list(listAttrs));
  203. this._formatContent();
  204. this._setEvents();
  205. this._setIcon();
  206. return Q(this);
  207. },
  208. appendNewMessages: function(newMessages) {
  209. var svcData = [];
  210. var data = newMessages || [];
  211. var unreadNotification = t.translate('unread_notification');
  212. var readNotification = t.translate('read_notification');
  213. var deleteLabel = t.translate('delete_message_label');
  214. for(var i=0; i<data.length;i++) {
  215. svcData.push({ icon: (data[i].unread)? "wft_message_unread_32" :"wft_message_read_32",
  216. subject: _.escape(data[i].subject) || "",
  217. sender: _.escape(data[i].sender) || "",
  218. date: moment(data[i].created).locale(this.defaultLocale).tz(this.timezone).format(__STANDARD_DATE_FORMAT),
  219. read: (data[i].unread)? "unread" :"read",
  220. id: data[i].id || "",
  221. label: (data[i].unread) ? unreadNotification : readNotification,
  222. deleteLabel: deleteLabel
  223. });
  224. }
  225. var listAttrs = {
  226. msgs: svcData
  227. };
  228. var list = dot.simpleTemplate(msgTemplate);
  229. this.$el.append(list(listAttrs));
  230. this._formatContent();
  231. this._setEvents();
  232. this._setIcon();
  233. },
  234. prependNewMessages: function(newMessages) {
  235. var svcData = [];
  236. var data = newMessages || [];
  237. var unreadNotification = t.translate('unread_notification');
  238. var readNotification = t.translate('read_notification');
  239. var deleteLabel = t.translate('delete_message_label');
  240. for(var i=0; i<data.length;i++) {
  241. svcData.push({ icon: (data[i].unread)? "wft_message_unread_32" :"wft_message_read_32",
  242. subject: _.escape(data[i].subject) || "",
  243. sender: _.escape(data[i].sender) || "",
  244. date: moment(data[i].created).locale(this.defaultLocale).tz(this.timezone).format(__STANDARD_DATE_FORMAT),
  245. read: (data[i].unread)? "unread" :"read",
  246. id: data[i].id || "",
  247. label: (data[i].unread) ? unreadNotification : readNotification,
  248. deleteLabel: deleteLabel
  249. });
  250. }
  251. var listAttrs = {
  252. msgs: svcData
  253. };
  254. var list = dot.simpleTemplate(msgTemplate);
  255. this.$el.prepend(list(listAttrs));
  256. this._formatContent();
  257. this._setEvents();
  258. this._setIcon();
  259. },
  260. focusNextMessage: function(currentMessage) {
  261. var messageCount = this.$el.children().length - 1;
  262. if(messageCount > 0) {
  263. var $nextMessage = currentMessage.next('.n10n_message');
  264. if($nextMessage.length == 1) {
  265. $nextMessage.find('.n10n_message_inner').focus();
  266. currentMessage = $nextMessage;
  267. }
  268. else {
  269. var $prevMessage = currentMessage.prev('.n10n_message');
  270. if($prevMessage.length == 1) {
  271. $prevMessage.find('.n10n_message_inner').focus();
  272. currentMessage = $prevMessage;
  273. }
  274. }
  275. }
  276. return currentMessage;
  277. }
  278. });
  279. return messageListView;
  280. });