TrackChangesManager.coffee 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. define [
  2. "track-changes/models/ChangeList"
  3. "track-changes/models/Diff"
  4. "track-changes/ChangeListView"
  5. "track-changes/DiffView"
  6. "account/AccountManager"
  7. "utils/Modal"
  8. "models/Doc"
  9. "moment"
  10. ], (ChangeList, Diff, ChangeListView, DiffView, AccountManager, Modal, Doc, moment) ->
  11. class TrackChangesManager
  12. template: $("#trackChangesPanelTemplate").html()
  13. constructor: (@ide) ->
  14. @project_id = window.userSettings.project_id
  15. @$el = $(@template)
  16. @ide.mainAreaManager.addArea
  17. identifier: "trackChanges"
  18. element: @$el
  19. @ide.tabManager.addTab
  20. id: "history"
  21. name: "History"
  22. show: "code"
  23. after: "code"
  24. contract: true
  25. onShown: () => @show()
  26. onHidden: () => @hide()
  27. @ide.editor.on "resize", () =>
  28. @diffView?.resize()
  29. @$el.find(".track-changes-close").on "click", (e) =>
  30. e.preventDefault
  31. @hide()
  32. @bindToFileTreeEvents()
  33. @disable()
  34. bindToFileTreeEvents: () ->
  35. @ide.fileTreeManager.on "open:doc", (doc_id) =>
  36. @doc_id = doc_id
  37. if @enabled
  38. @updateDiff()
  39. AB_BUCKETS: ["control", "one-week", "pop-up"]
  40. show: () ->
  41. @changes = new ChangeList([], project_id: @project_id, ide: @ide)
  42. if @changeListView?
  43. @changeListView.remove()
  44. @changeListView = new ChangeListView(
  45. el: @$el.find(".change-list-area")
  46. collection: @changes
  47. )
  48. @changeListView.render()
  49. @changeListView.loadUntilFull (error) =>
  50. @autoSelectDiff()
  51. @changeListView.on "change_diff", (fromIndex, toIndex) =>
  52. @findDocsInChange(fromIndex, toIndex)
  53. @updateLabels()
  54. @updateDiff()
  55. @showUpgradeView()
  56. if @diffView?
  57. @diffView.remove()
  58. @ide.mainAreaManager.change "trackChanges"
  59. @ide.editor.disable()
  60. @ide.fileViewManager.disable()
  61. @ide.fileTreeManager.showDeletedDocs()
  62. @enable()
  63. showUpgradeView: () ->
  64. @$el.find("button.start-free-trial").off "click.track-changes"
  65. @$el.find("button.start-free-trial").on "click.track-changes", () => @gotoFreeTrial()
  66. if !@ide.project.get("features").versioning
  67. ga('send', 'event', 'subscription-funnel', 'askToUgrade', "trackchanges")
  68. @$el.find(".track-changes-upgrade-popup").show()
  69. if @ide.project.get("owner") == @ide.user
  70. @$el.find(".show-when-not-owner").hide()
  71. else
  72. @$el.find(".show-when-owner").hide()
  73. hide: () ->
  74. @ide.editor.enable()
  75. @ide.fileViewManager.enable()
  76. @disable()
  77. @ide.fileTreeManager.openDoc(@doc_id)
  78. @ide.tabManager.show "code"
  79. @resetLabels()
  80. @ide.fileTreeManager.hideDeletedDocs()
  81. autoSelectDiff: () ->
  82. if @changes.models.length == 0
  83. return
  84. # Find all change until the last one we made
  85. fromIndex = null
  86. for change, i in @changes.models
  87. if ide.user in change.get("users")
  88. if i > 0
  89. fromIndex = i - 1
  90. else
  91. fromIndex = 0
  92. break
  93. fromIndex = 0 if !fromIndex
  94. toChange = @changes.models[0]
  95. fromChange = @changes.models[fromIndex]
  96. @changeListView.setSelectionRange(fromIndex, 0)
  97. @updateDiff()
  98. findDocsInChange: (fromIndex, toIndex) ->
  99. @changed_doc_ids = []
  100. for change in @changes.models.slice(toIndex, fromIndex + 1)
  101. for doc in change.get("docs") or []
  102. @changed_doc_ids.push doc.id if doc.id not in @changed_doc_ids
  103. if !@doc_id? or @doc_id not in @changed_doc_ids
  104. @doc_id = @changed_doc_ids[0]
  105. @updateDiff()
  106. updateLabels: () ->
  107. labels = {}
  108. for doc_id in @changed_doc_ids
  109. labels[doc_id] = true
  110. @ide.fileTreeManager.setLabels(labels)
  111. resetLabels: () ->
  112. @ide.fileTreeManager.setLabels({})
  113. updateDiff: () ->
  114. fromIndex = @changeListView.selectedFromIndex
  115. toIndex = @changeListView.selectedToIndex
  116. if !toIndex? or !fromIndex?
  117. console.log "No selection - what should we do!?"
  118. return
  119. {from, to, start_ts, end_ts} = @_findDocVersionsRangeInSelection(@doc_id, fromIndex, toIndex)
  120. @diff = new Diff({
  121. project_id: @project_id
  122. doc_id: @doc_id
  123. from: from
  124. to: to
  125. start_ts: start_ts
  126. end_ts: end_ts
  127. }, {
  128. ide: @ide
  129. })
  130. if @diffView?
  131. @diffView.remove()
  132. if !@diff.get("doc")?
  133. console.log "This document does not exist. What should we do?"
  134. return
  135. @diffView = new DiffView(
  136. model: @diff
  137. el: @$el.find(".track-changes-diff")
  138. )
  139. @diffView.on "restore", () =>
  140. @restoreDiff(@diff)
  141. @diffView.on "restore-deleted", () =>
  142. @restoreDeletedDoc @diff.get("doc"), (error, doc_id) =>
  143. return if error? or !doc_id?
  144. setTimeout () =>
  145. # Give doc a chance to appear in file tree via socket.io
  146. @hide()
  147. @ide.fileTreeManager.openDoc(doc_id)
  148. , 1000
  149. @ide.fileTreeManager.selectEntity(@doc_id)
  150. _findDocVersionsRangeInSelection: (doc_id, fromIndex, toIndex) ->
  151. from = to = start_ts = end_ts = null
  152. for change in @changes.models.slice(toIndex, fromIndex + 1)
  153. for doc in change.get("docs")
  154. if doc.id == doc_id
  155. if from? and to?
  156. from = Math.min(from, doc.fromV)
  157. to = Math.max(to, doc.toV)
  158. start_ts = Math.min(start_ts, change.get("start_ts"))
  159. end_ts = Math.max(end_ts, change.get("end_ts"))
  160. else
  161. from = doc.fromV
  162. to = doc.toV
  163. start_ts = change.get("start_ts")
  164. end_ts = change.get("end_ts")
  165. break
  166. return {from, to, start_ts, end_ts}
  167. restoreDiff: (diff) ->
  168. name = diff.get("doc")?.get("name")
  169. date = moment(diff.get("start_ts")).format("Do MMM YYYY, h:mm:ss a")
  170. modal = new Modal({
  171. title: "Restore document"
  172. message: "Are you sure you want to restore <strong>#{name}</strong> to before the changes on #{date}?"
  173. buttons: [{
  174. text: "Cancel"
  175. }, {
  176. text: "Restore"
  177. class: "btn-success"
  178. close: false
  179. callback: ($button) =>
  180. $button.text("Restoring...")
  181. $button.prop("disabled", true)
  182. diff.restore (error) =>
  183. modal.remove()
  184. @hide()
  185. }]
  186. })
  187. restoreDeletedDoc: (doc, callback) ->
  188. $.ajax {
  189. url: "/project/#{@project_id}/doc/#{doc.get("id")}/restore"
  190. type: "POST"
  191. dataType: "json"
  192. data:
  193. name: doc.get("name")
  194. headers:
  195. "X-CSRF-Token": window.csrfToken
  196. success: (body, status, response) ->
  197. callback(null, body?.doc_id)
  198. error: (error) ->
  199. callback(error)
  200. }
  201. enable: () ->
  202. @enabled = true
  203. disable: () ->
  204. @enabled = false
  205. gotoFreeTrial: () ->
  206. AccountManager.gotoSubscriptionsPage()
  207. ga('send', 'event', 'subscription-funnel', 'upgraded-free-trial', "trackchanges")
  208. return TrackChangesManager