TrackChangesManager.coffee 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. define [
  2. "ide/track-changes/controllers/TrackChangesListController"
  3. "ide/track-changes/controllers/TrackChangesDiffController"
  4. "ide/track-changes/directives/infiniteScroll"
  5. ], () ->
  6. class TrackChangesManager
  7. constructor: (@ide, @$scope) ->
  8. @reset()
  9. @$scope.toggleTrackChanges = () =>
  10. if @$scope.ui.view == "track-changes"
  11. @hide()
  12. else
  13. @show()
  14. @$scope.$watch "trackChanges.selection.updates", (updates) =>
  15. if updates? and updates.length > 0
  16. @_selectDocFromUpdates()
  17. @reloadDiff()
  18. @$scope.$on "entity:selected", (event, entity) =>
  19. if (@$scope.ui.view == "track-changes") and (entity.type == "doc")
  20. @$scope.trackChanges.selection.doc = entity
  21. @reloadDiff()
  22. show: () ->
  23. @$scope.ui.view = "track-changes"
  24. @reset()
  25. hide: () ->
  26. @$scope.ui.view = "editor"
  27. # Make sure we run the 'open' logic for whatever is currently selected
  28. @$scope.$emit "entity:selected", @ide.fileTreeManager.findSelectedEntity()
  29. reset: () ->
  30. @$scope.trackChanges = {
  31. updates: []
  32. nextBeforeTimestamp: null
  33. atEnd: false
  34. selection: {
  35. updates: []
  36. doc: null
  37. range: {
  38. fromV: null
  39. toV: null
  40. start_ts: null
  41. end_ts: null
  42. }
  43. }
  44. diff: null
  45. }
  46. autoSelectRecentUpdates: () ->
  47. return if @$scope.trackChanges.updates.length == 0
  48. @$scope.trackChanges.updates[0].selectedTo = true
  49. indexOfLastUpdateNotByMe = 0
  50. for update, i in @$scope.trackChanges.updates
  51. if @_updateContainsUserId(update, @$scope.user.id)
  52. break
  53. indexOfLastUpdateNotByMe = i
  54. @$scope.trackChanges.updates[indexOfLastUpdateNotByMe].selectedFrom = true
  55. BATCH_SIZE: 10
  56. fetchNextBatchOfUpdates: () ->
  57. url = "/project/#{@ide.project_id}/updates?min_count=#{@BATCH_SIZE}"
  58. if @$scope.trackChanges.nextBeforeTimestamp?
  59. url += "&before=#{@$scope.trackChanges.nextBeforeTimestamp}"
  60. @$scope.trackChanges.loading = true
  61. @ide.$http
  62. .get(url)
  63. .success (data) =>
  64. @_loadUpdates(data.updates)
  65. @$scope.trackChanges.nextBeforeTimestamp = data.nextBeforeTimestamp
  66. if !data.nextBeforeTimestamp?
  67. @$scope.trackChanges.atEnd = true
  68. @$scope.trackChanges.loading = false
  69. reloadDiff: () ->
  70. diff = @$scope.trackChanges.diff
  71. {updates, doc} = @$scope.trackChanges.selection
  72. {fromV, toV, start_ts, end_ts} = @_calculateRangeFromSelection()
  73. return if !doc?
  74. return if diff? and
  75. diff.doc == doc and
  76. diff.fromV == fromV and
  77. diff.toV == toV
  78. @$scope.trackChanges.diff = diff = {
  79. fromV: fromV
  80. toV: toV
  81. start_ts: start_ts
  82. end_ts: end_ts
  83. doc: doc
  84. error: false
  85. }
  86. if !doc.deleted
  87. diff.loading = true
  88. url = "/project/#{@$scope.project_id}/doc/#{diff.doc.id}/diff"
  89. if diff.fromV? and diff.toV?
  90. url += "?from=#{diff.fromV}&to=#{diff.toV}"
  91. @ide.$http
  92. .get(url)
  93. .success (data) =>
  94. diff.loading = false
  95. {text, highlights} = @_parseDiff(data)
  96. diff.text = text
  97. diff.highlights = highlights
  98. .error () ->
  99. diff.loading = false
  100. diff.error = true
  101. else
  102. diff.deleted = true
  103. restoreDeletedDoc: (doc) ->
  104. url = "/project/#{@$scope.project_id}/doc/#{doc.id}/restore"
  105. @ide.$http.post(url, name: doc.name, _csrf: window.csrfToken)
  106. restoreDiff: (diff) ->
  107. url = "/project/#{@$scope.project_id}/doc/#{diff.doc.id}/version/#{diff.fromV}/restore"
  108. @ide.$http.post(url, _csrf: window.csrfToken)
  109. _parseDiff: (diff) ->
  110. row = 0
  111. column = 0
  112. highlights = []
  113. text = ""
  114. for entry, i in diff.diff or []
  115. content = entry.u or entry.i or entry.d
  116. content ||= ""
  117. text += content
  118. lines = content.split("\n")
  119. startRow = row
  120. startColumn = column
  121. if lines.length > 1
  122. endRow = startRow + lines.length - 1
  123. endColumn = lines[lines.length - 1].length
  124. else
  125. endRow = startRow
  126. endColumn = startColumn + lines[0].length
  127. row = endRow
  128. column = endColumn
  129. range = {
  130. start:
  131. row: startRow
  132. column: startColumn
  133. end:
  134. row: endRow
  135. column: endColumn
  136. }
  137. if entry.i? or entry.d?
  138. if entry.meta.user?
  139. name = "#{entry.meta.user.first_name} #{entry.meta.user.last_name}"
  140. else
  141. name = "Anonymous"
  142. if entry.meta.user?.id == @$scope.user.id
  143. name = "you"
  144. date = moment(entry.meta.end_ts).format("Do MMM YYYY, h:mm a")
  145. if entry.i?
  146. highlights.push {
  147. label: "Added by #{name} on #{date}"
  148. highlight: range
  149. hue: @ide.onlineUsersManager.getHueForUserId(entry.meta.user?.id)
  150. }
  151. else if entry.d?
  152. highlights.push {
  153. label: "Deleted by #{name} on #{date}"
  154. strikeThrough: range
  155. hue: @ide.onlineUsersManager.getHueForUserId(entry.meta.user?.id)
  156. }
  157. return {text, highlights}
  158. _loadUpdates: (updates = []) ->
  159. previousUpdate = @$scope.trackChanges.updates[@$scope.trackChanges.updates.length - 1]
  160. for update in updates
  161. for doc_id, doc of update.docs or {}
  162. doc.entity = @ide.fileTreeManager.findEntityById(doc_id, includeDeleted: true)
  163. for user in update.meta.users or []
  164. user.hue = @ide.onlineUsersManager.getHueForUserId(user.id)
  165. if !previousUpdate? or !moment(previousUpdate.meta.end_ts).isSame(update.meta.end_ts, "day")
  166. update.meta.first_in_day = true
  167. update.selectedFrom = false
  168. update.selectedTo = false
  169. update.inSelection = false
  170. previousUpdate = update
  171. firstLoad = @$scope.trackChanges.updates.length == 0
  172. @$scope.trackChanges.updates =
  173. @$scope.trackChanges.updates.concat(updates)
  174. @autoSelectRecentUpdates() if firstLoad
  175. _calculateRangeFromSelection: () ->
  176. fromV = toV = start_ts = end_ts = null
  177. selected_doc_id = @$scope.trackChanges.selection.doc?.id
  178. for update in @$scope.trackChanges.selection.updates or []
  179. for doc_id, doc of update.docs
  180. if doc_id == selected_doc_id
  181. if fromV? and toV?
  182. fromV = Math.min(fromV, doc.fromV)
  183. toV = Math.max(toV, doc.toV)
  184. start_ts = Math.min(start_ts, update.meta.start_ts)
  185. end_ts = Math.max(end_ts, update.meta.end_ts)
  186. else
  187. fromV = doc.fromV
  188. toV = doc.toV
  189. start_ts = update.meta.start_ts
  190. end_ts = update.meta.end_ts
  191. break
  192. return {fromV, toV, start_ts, end_ts}
  193. # Set the track changes selected doc to one of the docs in the range
  194. # of currently selected updates. If we already have a selected doc
  195. # then prefer this one if present.
  196. _selectDocFromUpdates: () ->
  197. affected_docs = {}
  198. for update in @$scope.trackChanges.selection.updates
  199. for doc_id, doc of update.docs
  200. affected_docs[doc_id] = doc.entity
  201. selected_doc = @$scope.trackChanges.selection.doc
  202. if selected_doc? and affected_docs[selected_doc.id]?
  203. # Selected doc is already open
  204. else
  205. for doc_id, doc of affected_docs
  206. selected_doc = doc
  207. break
  208. @$scope.trackChanges.selection.doc = selected_doc
  209. @ide.fileTreeManager.selectEntity(selected_doc)
  210. _updateContainsUserId: (update, user_id) ->
  211. for user in update.meta.users
  212. return true if user.id == user_id
  213. return false