chatWindowView.coffee 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. define [
  2. "libs/underscore"
  3. "libs/backbone"
  4. "views/userMessageBlockView"
  5. "views/timeMessageBlockView"
  6. ], (_, Backbone, UserMessageBlockView, TimeMessageBlockView) ->
  7. FIVE_MINS = 5 * 60 * 1000
  8. ONE_HOUR = 60 * 60 * 1000
  9. TWELVE_HOURS = ONE_HOUR * 12
  10. ONE_DAY = ONE_HOUR * 24
  11. ChatWindowView = Backbone.View.extend
  12. events:
  13. "keydown textarea" : "_onTextAreaKeyDown"
  14. "click .js-load-older-messages" : "_loadOlderMessages"
  15. "click .js-minimize-toggle" : "_toggleMinimizeState"
  16. "click h3" : "_toggleMinimizeState"
  17. "click .js-new-message-alert" : "_toggleMinimizeState"
  18. "click" : "_removeNotification"
  19. initialize: () ->
  20. @template = $("#chatWindowTemplate").html()
  21. @chat = @options.chat
  22. @room = @options.room
  23. @listenTo @room.get("messages"), "add", (model, collection) -> @_onNewMessage(model, collection)
  24. @listenTo @room.get("messages"), "noMoreMessages", () -> @$(".js-loading").hide()
  25. @listenTo @room.get("connectedUsers"), "add", (user, collection) -> @_renderConnectedUsers()
  26. @listenTo @room.get("connectedUsers"), "remove", (user, collection) -> @_renderConnectedUsers()
  27. @listenTo @room.get("connectedUsers"), "change", (user, collection) -> @_renderConnectedUsers()
  28. @listenTo @room, "joined", -> @_onJoin()
  29. @listenTo @room, "disconnected", -> @_onDisconnect()
  30. @listenTo @room, "afterMessagesPreloaded", -> @_scrollToBottomOfMessages()
  31. render: () ->
  32. @setElement($(@template))
  33. $(document.body).append(@$el)
  34. @_renderConnectedUsers()
  35. @_initializeMinimizedState()
  36. _onJoin: () ->
  37. if !@rendered?
  38. @render()
  39. @rendered = true
  40. @$el.removeClass("disconnected")
  41. @$("textarea").removeAttr("disabled")
  42. _onDisconnect: () ->
  43. @$el.addClass("disconnected")
  44. @$("textarea").attr("disabled", "disabled")
  45. _onNewMessage: (message, collection) ->
  46. @_renderMessage(message, collection)
  47. @_notifyAboutNewMessage(message)
  48. _renderMessage: (message, collection) ->
  49. @messageBlocks ||= []
  50. scrollEl = @$(".sent-message-area")
  51. isOldestMessage = (message, collection)->
  52. collection.indexOf(message) == 0
  53. ismessageFromNewUser = (messageView, message)->
  54. !messageView? or messageView.user != message.get("user")
  55. isTimeForNewBlockBackwards = (message, previousUserMessageBlockView)->
  56. if !message? or !previousUserMessageBlockView?
  57. return true
  58. timeSinceMessageWasSent = new Date().getTime() - message.get("timestamp")
  59. if timeSinceMessageWasSent < ONE_HOUR
  60. timeBlockSize = FIVE_MINS
  61. else if timeSinceMessageWasSent > ONE_HOUR and timeSinceMessageWasSent < (ONE_DAY + TWELVE_HOURS)
  62. timeBlockSize = ONE_HOUR
  63. else
  64. timeBlockSize = ONE_DAY
  65. timeSinceLastPrinted = previousUserMessageBlockView.getTime() - message.get("timestamp")
  66. if timeSinceLastPrinted > timeBlockSize
  67. return true
  68. else
  69. return false
  70. isTimeForNewBlock = (message, previousUserMessageBlockView)->
  71. (message.get("timestamp") - previousUserMessageBlockView.getTime()) > FIVE_MINS
  72. if isOldestMessage(message, collection)
  73. oldScrollTopFromBottom = scrollEl[0].scrollHeight - scrollEl.scrollTop()
  74. userMessageBlockView = @messageBlocks[0]
  75. if ismessageFromNewUser(userMessageBlockView, message) or isTimeForNewBlockBackwards(message, userMessageBlockView)
  76. userMessageBlockView = new UserMessageBlockView(user: message.get("user"))
  77. @$(".sent-messages").prepend(userMessageBlockView.$el)
  78. @messageBlocks.unshift userMessageBlockView
  79. userMessageBlockView.prependMessage(message)
  80. scrollEl.scrollTop(scrollEl[0].scrollHeight - oldScrollTopFromBottom)
  81. else
  82. oldScrollBottom = @_getScrollBottom()
  83. userMessageBlockView = @messageBlocks[@messageBlocks.length - 1]
  84. if ismessageFromNewUser(userMessageBlockView, message) or isTimeForNewBlock(message, userMessageBlockView)
  85. userMessageBlockView = new UserMessageBlockView(user: message.get("user"))
  86. @$(".sent-messages").append(userMessageBlockView.$el)
  87. @messageBlocks.push userMessageBlockView
  88. userMessageBlockView.appendMessage(message)
  89. if oldScrollBottom <= 0
  90. @_scrollToBottomOfMessages()
  91. _renderConnectedUsers: () ->
  92. users = @room.get("connectedUsers")
  93. names = users
  94. .reject((user) => user == @chat.user)
  95. .map((user) -> "#{user.get("first_name")} #{user.get("last_name")}")
  96. if names.length == 0
  97. text = "No one else is online :("
  98. else if names.length == 1
  99. text = "#{names[0]} is also online"
  100. else
  101. text = "#{names.slice(0, -1).join(", ")} and #{names[names.length - 1]} are also online"
  102. @$(".js-connected-users").text(text)
  103. @_resizeSentMessageArea()
  104. _resizeSentMessageArea: () ->
  105. marginTop = @$(".js-header").outerHeight() + @$(".js-connected-users").outerHeight()
  106. @$(".js-sent-message-area").css({
  107. top: marginTop + "px"
  108. })
  109. _getScrollBottom: () ->
  110. scrollEl = @$(".sent-message-area")
  111. return scrollEl[0].scrollHeight - scrollEl.scrollTop() - scrollEl.innerHeight()
  112. _scrollToBottomOfMessages: () ->
  113. scrollEl = @$(".sent-message-area")
  114. doScroll = ->
  115. return scrollEl.scrollTop(scrollEl[0].scrollHeight - scrollEl.innerHeight())
  116. MathJax.Hub.Queue(["Typeset", doScroll])
  117. _notifyAboutNewMessage: (message) ->
  118. isMessageNewToUser = message.get("user") != @chat.user and !message.get("preloaded")
  119. isTextAreaFocused = @$("textarea").is(":focus")
  120. if !isTextAreaFocused and isMessageNewToUser
  121. @unseenMessages ||= 0
  122. @unseenMessages += 1
  123. @$el.addClass("new-messages")
  124. @$(".new-message-alert").text(@unseenMessages)
  125. _removeNotification: () ->
  126. @unseenMessages = 0
  127. @$el.removeClass("new-messages")
  128. @$(".new-message-alert").text("")
  129. _onTextAreaKeyDown: (e) ->
  130. if e.keyCode == 13 # Enter
  131. e.preventDefault()
  132. message = @$("textarea").val()
  133. @$("textarea").val("")
  134. @_sendMessage(message)
  135. _loadOlderMessages: (e) ->
  136. e.preventDefault()
  137. @room.get("messages").fetchMoreMessages()
  138. _sendMessage: (content) ->
  139. @room.sendMessage(content)
  140. isMinimized: () ->
  141. minimized = $.localStorage "chat.rooms.project-chat.minimized"
  142. if !minimized?
  143. minimized = false
  144. return minimized
  145. _setMinimizedState: (state) ->
  146. $.localStorage "chat.rooms.project-chat.minimized", state
  147. _initializeMinimizedState: () ->
  148. minimized = @isMinimized()
  149. if minimized
  150. @_minimize(false)
  151. _toggleMinimizeState: (e) ->
  152. e.preventDefault()
  153. minimized = @isMinimized()
  154. if !minimized
  155. @_setMinimizedState(true)
  156. @_minimize()
  157. else
  158. @_setMinimizedState(false)
  159. @_unminimize()
  160. _minimize: (animate = true) ->
  161. @$(".new-message-area").hide()
  162. @$(".js-connected-users").hide()
  163. @$el.addClass("minimized")
  164. if animate
  165. @$el.animate height: 20, width: 80
  166. else
  167. @$el.css height: 20, width: 80
  168. _unminimize: () ->
  169. @$(".new-message-area").show()
  170. @$(".js-connected-users").show()
  171. @$el.removeClass("minimized")
  172. @$el.animate height: 260, width: 220, () =>
  173. @_resizeSentMessageArea()
  174. @_scrollToBottomOfMessages()