TrackChangesManager.coffee 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  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. diff.restoreInProgress = false
  104. diff.restoreDeletedSuccess = false
  105. diff.restoredDocNewId = null
  106. restoreDeletedDoc: (doc) ->
  107. url = "/project/#{@$scope.project_id}/doc/#{doc.id}/restore"
  108. @ide.$http.post(url, name: doc.name, _csrf: window.csrfToken)
  109. restoreDiff: (diff) ->
  110. url = "/project/#{@$scope.project_id}/doc/#{diff.doc.id}/version/#{diff.fromV}/restore"
  111. @ide.$http.post(url, _csrf: window.csrfToken)
  112. _parseDiff: (diff) ->
  113. row = 0
  114. column = 0
  115. highlights = []
  116. text = ""
  117. for entry, i in diff.diff or []
  118. content = entry.u or entry.i or entry.d
  119. content ||= ""
  120. text += content
  121. lines = content.split("\n")
  122. startRow = row
  123. startColumn = column
  124. if lines.length > 1
  125. endRow = startRow + lines.length - 1
  126. endColumn = lines[lines.length - 1].length
  127. else
  128. endRow = startRow
  129. endColumn = startColumn + lines[0].length
  130. row = endRow
  131. column = endColumn
  132. range = {
  133. start:
  134. row: startRow
  135. column: startColumn
  136. end:
  137. row: endRow
  138. column: endColumn
  139. }
  140. if entry.i? or entry.d?
  141. if entry.meta.user?
  142. name = "#{entry.meta.user.first_name} #{entry.meta.user.last_name}"
  143. else
  144. name = "Anonymous"
  145. if entry.meta.user?.id == @$scope.user.id
  146. name = "you"
  147. date = moment(entry.meta.end_ts).format("Do MMM YYYY, h:mm a")
  148. if entry.i?
  149. highlights.push {
  150. label: "Added by #{name} on #{date}"
  151. highlight: range
  152. hue: @ide.onlineUsersManager.getHueForUserId(entry.meta.user?.id)
  153. }
  154. else if entry.d?
  155. highlights.push {
  156. label: "Deleted by #{name} on #{date}"
  157. strikeThrough: range
  158. hue: @ide.onlineUsersManager.getHueForUserId(entry.meta.user?.id)
  159. }
  160. return {text, highlights}
  161. _loadUpdates: (updates = []) ->
  162. previousUpdate = @$scope.trackChanges.updates[@$scope.trackChanges.updates.length - 1]
  163. for update in updates
  164. for doc_id, doc of update.docs or {}
  165. doc.entity = @ide.fileTreeManager.findEntityById(doc_id, includeDeleted: true)
  166. for user in update.meta.users or []
  167. if user?
  168. user.hue = @ide.onlineUsersManager.getHueForUserId(user.id)
  169. if !previousUpdate? or !moment(previousUpdate.meta.end_ts).isSame(update.meta.end_ts, "day")
  170. update.meta.first_in_day = true
  171. update.selectedFrom = false
  172. update.selectedTo = false
  173. update.inSelection = false
  174. previousUpdate = update
  175. firstLoad = @$scope.trackChanges.updates.length == 0
  176. @$scope.trackChanges.updates =
  177. @$scope.trackChanges.updates.concat(updates)
  178. @autoSelectRecentUpdates() if firstLoad
  179. _calculateRangeFromSelection: () ->
  180. fromV = toV = start_ts = end_ts = null
  181. selected_doc_id = @$scope.trackChanges.selection.doc?.id
  182. for update in @$scope.trackChanges.selection.updates or []
  183. for doc_id, doc of update.docs
  184. if doc_id == selected_doc_id
  185. if fromV? and toV?
  186. fromV = Math.min(fromV, doc.fromV)
  187. toV = Math.max(toV, doc.toV)
  188. start_ts = Math.min(start_ts, update.meta.start_ts)
  189. end_ts = Math.max(end_ts, update.meta.end_ts)
  190. else
  191. fromV = doc.fromV
  192. toV = doc.toV
  193. start_ts = update.meta.start_ts
  194. end_ts = update.meta.end_ts
  195. break
  196. return {fromV, toV, start_ts, end_ts}
  197. # Set the track changes selected doc to one of the docs in the range
  198. # of currently selected updates. If we already have a selected doc
  199. # then prefer this one if present.
  200. _selectDocFromUpdates: () ->
  201. affected_docs = {}
  202. for update in @$scope.trackChanges.selection.updates
  203. for doc_id, doc of update.docs
  204. affected_docs[doc_id] = doc.entity
  205. selected_doc = @$scope.trackChanges.selection.doc
  206. if selected_doc? and affected_docs[selected_doc.id]?
  207. # Selected doc is already open
  208. else
  209. for doc_id, doc of affected_docs
  210. selected_doc = doc
  211. break
  212. @$scope.trackChanges.selection.doc = selected_doc
  213. @ide.fileTreeManager.selectEntity(selected_doc)
  214. _updateContainsUserId: (update, user_id) ->
  215. for user in update.meta.users
  216. return true if user?.id == user_id
  217. return false