dashboard.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. /*
  2. * Copyright (C) 2011 - present Instructure, Inc.
  3. *
  4. * This file is part of Canvas.
  5. *
  6. * Canvas is free software: you can redistribute it and/or modify it under
  7. * the terms of the GNU Affero General Public License as published by the Free
  8. * Software Foundation, version 3 of the License.
  9. *
  10. * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
  11. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  13. * details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License along
  16. * with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. import {View} from 'Backbone'
  19. import $ from 'jquery'
  20. import React from 'react'
  21. import ReactDOM from 'react-dom'
  22. import I18n from 'i18n!dashboard'
  23. import newCourseForm from 'compiled/util/newCourseForm'
  24. import DashboardHeader from 'jsx/dashboard/DashboardHeader'
  25. import showMoreTemplate from 'jst/dashboard/show_more_link'
  26. import 'jquery.disableWhileLoading'
  27. if (ENV.DASHBOARD_SIDEBAR_URL) {
  28. const rightSide = $('#right-side')
  29. rightSide.disableWhileLoading($.get(ENV.DASHBOARD_SIDEBAR_URL, (html) => {
  30. rightSide.html(html)
  31. return newCourseForm()
  32. }))
  33. }
  34. const dashboardHeaderContainer = document.getElementById('dashboard_header_container');
  35. if (dashboardHeaderContainer) {
  36. ReactDOM.render(
  37. <DashboardHeader
  38. recent_activity_dashboard={ENV.PREFERENCES.recent_activity_dashboard}
  39. hide_dashcard_color_overlays={ENV.PREFERENCES.hide_dashcard_color_overlays}
  40. planner_enabled={ENV.STUDENT_PLANNER_ENABLED}
  41. planner_selected={ENV.PREFERENCES.show_planner}
  42. />,
  43. dashboardHeaderContainer
  44. )
  45. }
  46. class DashboardView extends View {
  47. static initClass () {
  48. this.prototype.el = document.body
  49. this.prototype.events = {
  50. 'click .stream_header': 'toggleDetails',
  51. 'click .stream_header .links a': 'stopPropagation',
  52. 'click .stream-details': 'handleDetailsClick',
  53. 'click .close_conference_link': 'closeConference',
  54. 'focus .todo-tooltip': 'handleTooltipFocus',
  55. beforeremove: 'updateCategoryCounts', // ujsLinks event
  56. }
  57. }
  58. initialize () {
  59. super.initialize(...arguments)
  60. // setup all 'Show More' links to reflect currently being collapsed.
  61. $('.stream-category').each((idx, elm) => this.setShowMoreLink($(elm)))
  62. }
  63. toggleDetails (event) {
  64. const header = $(event.currentTarget)
  65. // since toggling, isExpanded is the opposite of the current DOM state
  66. const isExpanded = !(header.attr('aria-expanded') === 'true')
  67. header.attr('aria-expanded', isExpanded)
  68. const details = header.next('.details_container')
  69. details.toggle(isExpanded)
  70. // if expanded, focus first link in detail area
  71. if (isExpanded) {
  72. details.find('a:first').focus()
  73. }
  74. // Set the link contents. Second param for being currently expanded or collapsed
  75. this.setShowMoreLink(header.closest('.stream-category'), isExpanded)
  76. }
  77. setShowMoreLink ($category) {
  78. if (!$category) return
  79. // determine if currently expanded
  80. const isExpanded = $category.find('.details_container').is(':visible')
  81. // go up to stream-category to build the text to display
  82. const categoryName = $category.data('category')
  83. const count = parseInt($category.find('.count:first').text())
  84. const assistiveText = this.getCategoryText(categoryName, count, !isExpanded)
  85. const $link = $category.find('.toggle-details')
  86. $link.html(showMoreTemplate({expanded: isExpanded, assistiveText}))
  87. }
  88. getCategoryText (category, count, forExpand) {
  89. if (category === 'Announcement') {
  90. if (forExpand) {
  91. return I18n.t('announcements_expand', {
  92. one: 'Expand %{count} announcement',
  93. other: 'Expand %{count} announcements'
  94. }, {count})
  95. } else {
  96. return I18n.t('announcements_collapse', {
  97. one: 'Collapse %{count} announcement',
  98. other: 'Collapse %{count} announcements'
  99. }, {count})
  100. }
  101. } else if (category === 'Conversation') {
  102. if (forExpand) {
  103. return I18n.t('conversations_expand', {
  104. one: 'Expand %{count} conversation message',
  105. other: 'Expand %{count} conversation messages'
  106. }, {count})
  107. } else {
  108. return I18n.t('conversations_collapse', {
  109. one: 'Collapse %{count} conversation message',
  110. other: 'Collapse %{count} conversation messages'
  111. }, {count})
  112. }
  113. } else if (category === 'Assignment') {
  114. if (forExpand) {
  115. return I18n.t('assignments_expand', {
  116. one: 'Expand %{count} assignment notification',
  117. other: 'Expand %{count} assignment notifications'
  118. }, {count})
  119. } else {
  120. return I18n.t('assignments_collapse', {
  121. one: 'Collapse %{count} assignment notification',
  122. other: 'Collapse %{count} assignment notifications'
  123. }, {count})
  124. }
  125. } else if (category === 'DiscussionTopic') {
  126. if (forExpand) {
  127. return I18n.t('discussions_expand', {
  128. one: 'Expand %{count} discussion',
  129. other: 'Expand %{count} discussions'
  130. }, {count})
  131. } else {
  132. return I18n.t('discussions_collapse', {
  133. one: 'Collapse %{count} discussion',
  134. other: 'Collapse %{count} discussions'
  135. }, {count})
  136. }
  137. } else {
  138. return ''
  139. }
  140. }
  141. handleDetailsClick (event) {
  142. let link
  143. const row = $(event.target).closest('tr')
  144. return link = row.find('a')
  145. }
  146. // TODO: switch recent items to client rendering and skip all this
  147. // ridiculous dom manip that is likely to just get worse
  148. updateCategoryCounts (event) {
  149. const parent = $(event.target).closest('li[class^=stream-]')
  150. const items = parent.find('tbody tr').filter(':visible')
  151. if (items.length) {
  152. parent.find('.count').text(items.length)
  153. } else {
  154. parent.remove()
  155. }
  156. return this.setShowMoreLink($(event.target).closest('.stream-category'))
  157. }
  158. handleTooltipFocus (event) {
  159. // needed so that the screenreader will read target element before points possible on focus
  160. setTimeout(
  161. () => $.screenReaderFlashMessage($(event.target).find('.screenreader_points_possible').text())
  162. , 6000
  163. )
  164. }
  165. closeConference (e) {
  166. e.preventDefault()
  167. if (!confirm(I18n.t('confirm.close', 'Are you sure you want to end this conference?\n\nYou will not be able to reopen it.'))) return
  168. const link = $(e.currentTarget)
  169. return $.ajaxJSON(link.attr('href'), 'POST', {}, data =>
  170. link.parents('.ic-notification.conference').hide()
  171. )
  172. }
  173. }
  174. DashboardView.initClass()
  175. new DashboardView()