ShareProjectModalController.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. /* eslint-disable
  2. camelcase,
  3. max-len,
  4. no-return-assign,
  5. no-undef,
  6. no-unused-vars,
  7. */
  8. // TODO: This file was created by bulk-decaffeinate.
  9. // Fix any style issues and re-enable lint.
  10. /*
  11. * decaffeinate suggestions:
  12. * DS101: Remove unnecessary use of Array.from
  13. * DS102: Remove unnecessary code created because of implicit returns
  14. * DS103: Rewrite code to no longer use __guard__
  15. * DS205: Consider reworking code to avoid use of IIFEs
  16. * DS207: Consider shorter variations of null checks
  17. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  18. */
  19. define(['base'], App =>
  20. App.controller('ShareProjectModalController', function(
  21. $scope,
  22. $modalInstance,
  23. $timeout,
  24. projectMembers,
  25. projectInvites,
  26. $modal,
  27. $http,
  28. ide,
  29. validateCaptcha,
  30. validateCaptchaV3,
  31. settings,
  32. event_tracking
  33. ) {
  34. let loadAutocompleteUsers
  35. $scope.inputs = {
  36. privileges: 'readAndWrite',
  37. contacts: []
  38. }
  39. $scope.state = {
  40. error: null,
  41. errorReason: null,
  42. inflight: false,
  43. startedFreeTrial: false,
  44. invites: []
  45. }
  46. $modalInstance.opened.then(() =>
  47. $timeout(() => $scope.$broadcast('open'), 200)
  48. )
  49. const INFINITE_COLLABORATORS = -1
  50. $scope.refreshCanAddCollaborators = function() {
  51. const allowedNoOfMembers = $scope.project.features.collaborators
  52. return ($scope.canAddCollaborators =
  53. $scope.project.members.length + $scope.project.invites.length <
  54. allowedNoOfMembers || allowedNoOfMembers === INFINITE_COLLABORATORS)
  55. }
  56. $scope.refreshCanAddCollaborators()
  57. $scope.$watch('canAddCollaborators', function() {
  58. if (!$scope.canAddCollaborators) {
  59. event_tracking.send(
  60. 'subscription-funnel',
  61. 'editor-click-feature',
  62. 'projectMembers'
  63. )
  64. }
  65. })
  66. $scope.$watch(
  67. '(project.members.length + project.invites.length)',
  68. _noOfMembers => $scope.refreshCanAddCollaborators()
  69. )
  70. $scope.autocompleteContacts = []
  71. ;(loadAutocompleteUsers = () =>
  72. $http.get('/user/contacts').then(function(response) {
  73. const { data } = response
  74. $scope.autocompleteContacts = data.contacts || []
  75. return (() => {
  76. const result = []
  77. for (let contact of Array.from($scope.autocompleteContacts)) {
  78. if (contact.type === 'user') {
  79. if (
  80. contact.first_name === contact.email.split('@')[0] &&
  81. !contact.last_name
  82. ) {
  83. // User has not set their proper name so use email as canonical display property
  84. result.push((contact.display = contact.email))
  85. } else {
  86. contact.name = `${contact.first_name} ${contact.last_name}`
  87. result.push(
  88. (contact.display = `${contact.name} <${contact.email}>`)
  89. )
  90. }
  91. } else {
  92. // Must be a group
  93. result.push((contact.display = contact.name))
  94. }
  95. }
  96. return result
  97. })()
  98. }))()
  99. const getCurrentMemberEmails = () =>
  100. ($scope.project.members || []).map(u => u.email)
  101. const getCurrentInviteEmails = () =>
  102. ($scope.project.invites || []).map(u => u.email)
  103. $scope.filterAutocompleteUsers = function($query) {
  104. const currentMemberEmails = getCurrentMemberEmails()
  105. return $scope.autocompleteContacts.filter(function(contact) {
  106. if (
  107. contact.email != null &&
  108. Array.from(currentMemberEmails).includes(contact.email)
  109. ) {
  110. return false
  111. }
  112. for (let text of [contact.name, contact.email]) {
  113. if (
  114. (text != null
  115. ? text.toLowerCase().indexOf($query.toLowerCase())
  116. : undefined) > -1
  117. ) {
  118. return true
  119. }
  120. }
  121. return false
  122. })
  123. }
  124. $scope.addMembers = function() {
  125. const addMembers = function() {
  126. let addNextMember
  127. if ($scope.inputs.contacts.length === 0) {
  128. return
  129. }
  130. const members = $scope.inputs.contacts
  131. $scope.inputs.contacts = []
  132. $scope.state.error = false
  133. $scope.state.errorReason = null
  134. $scope.state.inflight = true
  135. if ($scope.project.invites == null) {
  136. $scope.project.invites = []
  137. }
  138. const currentMemberEmails = getCurrentMemberEmails()
  139. const currentInviteEmails = getCurrentInviteEmails()
  140. return (addNextMember = function() {
  141. let email
  142. if (members.length === 0 || !$scope.canAddCollaborators) {
  143. $scope.state.inflight = false
  144. $scope.$apply()
  145. return
  146. }
  147. const member = members.shift()
  148. if (member.type === 'user') {
  149. ;({ email } = member)
  150. } else {
  151. // Not an auto-complete object, so email == display
  152. email = member.display
  153. }
  154. email = email.toLowerCase()
  155. if (Array.from(currentMemberEmails).includes(email)) {
  156. // Skip this existing member
  157. return addNextMember()
  158. }
  159. // do v3 captcha to collect data only
  160. validateCaptchaV3('invite')
  161. // do v2 captcha
  162. const ExposedSettings = window.ExposedSettings
  163. return validateCaptcha(function(response) {
  164. let inviteId, request
  165. $scope.grecaptchaResponse = response
  166. if (
  167. Array.from(currentInviteEmails).includes(email) &&
  168. (inviteId = __guard__(
  169. _.find(
  170. $scope.project.invites || [],
  171. invite => invite.email === email
  172. ),
  173. x => x._id
  174. ))
  175. ) {
  176. request = projectInvites.resendInvite(inviteId)
  177. } else {
  178. request = projectInvites.sendInvite(
  179. email,
  180. $scope.inputs.privileges,
  181. $scope.grecaptchaResponse
  182. )
  183. }
  184. return request
  185. .then(function(response) {
  186. const { data } = response
  187. if (data.error) {
  188. $scope.state.error = true
  189. $scope.state.errorReason = `${data.error}`
  190. $scope.state.inflight = false
  191. } else {
  192. if (data.invite) {
  193. const { invite } = data
  194. $scope.project.invites.push(invite)
  195. } else {
  196. let users
  197. if (data.users != null) {
  198. ;({ users } = data)
  199. } else if (data.user != null) {
  200. users = [data.user]
  201. } else {
  202. users = []
  203. }
  204. $scope.project.members.push(...Array.from(users || []))
  205. }
  206. }
  207. return setTimeout(
  208. () =>
  209. // Give $scope a chance to update $scope.canAddCollaborators
  210. // with new collaborator information.
  211. addNextMember(),
  212. 0
  213. )
  214. })
  215. .catch(function(httpResponse) {
  216. const { data, status, headers, config } = httpResponse
  217. $scope.state.inflight = false
  218. $scope.state.error = true
  219. if ((data != null ? data.errorReason : undefined) != null) {
  220. return ($scope.state.errorReason =
  221. data != null ? data.errorReason : undefined)
  222. } else {
  223. return ($scope.state.errorReason = null)
  224. }
  225. })
  226. }, ExposedSettings.recaptchaDisabled.invite)
  227. })()
  228. }
  229. return $timeout(addMembers, 50) // Give email list a chance to update
  230. }
  231. $scope.removeMember = function(member) {
  232. $scope.state.error = null
  233. $scope.state.inflight = true
  234. return projectMembers
  235. .removeMember(member)
  236. .then(function() {
  237. $scope.state.inflight = false
  238. const index = $scope.project.members.indexOf(member)
  239. if (index === -1) {
  240. return
  241. }
  242. return $scope.project.members.splice(index, 1)
  243. })
  244. .catch(function() {
  245. $scope.state.inflight = false
  246. return ($scope.state.error = 'Sorry, something went wrong :(')
  247. })
  248. }
  249. $scope.revokeInvite = function(invite) {
  250. $scope.state.error = null
  251. $scope.state.inflight = true
  252. return projectInvites
  253. .revokeInvite(invite._id)
  254. .then(function() {
  255. $scope.state.inflight = false
  256. const index = $scope.project.invites.indexOf(invite)
  257. if (index === -1) {
  258. return
  259. }
  260. return $scope.project.invites.splice(index, 1)
  261. })
  262. .catch(function() {
  263. $scope.state.inflight = false
  264. return ($scope.state.error = 'Sorry, something went wrong :(')
  265. })
  266. }
  267. $scope.resendInvite = function(invite, event) {
  268. $scope.state.error = null
  269. $scope.state.inflight = true
  270. return projectInvites
  271. .resendInvite(invite._id)
  272. .then(function() {
  273. $scope.state.inflight = false
  274. return event.target.blur()
  275. })
  276. .catch(function() {
  277. $scope.state.inflight = false
  278. $scope.state.error =
  279. 'Sorry, something went wrong resending the invite :('
  280. return event.target.blur()
  281. })
  282. }
  283. $scope.makeTokenBased = function() {
  284. $scope.project.publicAccesLevel = 'tokenBased'
  285. settings.saveProjectAdminSettings({ publicAccessLevel: 'tokenBased' })
  286. return event_tracking.sendMB('project-make-token-based')
  287. }
  288. $scope.makePrivate = function() {
  289. $scope.project.publicAccesLevel = 'private'
  290. return settings.saveProjectAdminSettings({ publicAccessLevel: 'private' })
  291. }
  292. $scope.$watch('project.tokens.readAndWrite', function(token) {
  293. if (token != null) {
  294. return ($scope.readAndWriteTokenLink = `${location.origin}/${token}`)
  295. } else {
  296. return ($scope.readAndWriteTokenLink = null)
  297. }
  298. })
  299. $scope.$watch('project.tokens.readOnly', function(token) {
  300. if (token != null) {
  301. return ($scope.readOnlyTokenLink = `${location.origin}/read/${token}`)
  302. } else {
  303. return ($scope.readOnlyTokenLink = null)
  304. }
  305. })
  306. $scope.done = () => $modalInstance.close()
  307. return ($scope.cancel = () => $modalInstance.dismiss())
  308. }))
  309. function __guard__(value, transform) {
  310. return typeof value !== 'undefined' && value !== null
  311. ? transform(value)
  312. : undefined
  313. }