asyncForm.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import App from '../base'
  2. import 'libs/passfield'
  3. App.directive('asyncForm', ($http, validateCaptcha, validateCaptchaV3) => ({
  4. controller: [
  5. '$scope',
  6. '$location',
  7. function($scope, $location) {
  8. this.getEmail = () => $scope.email
  9. this.getEmailFromQuery = () =>
  10. $location.search().email || $location.search().new_email
  11. return this
  12. }
  13. ],
  14. link(scope, element, attrs, ctrl) {
  15. let response
  16. const formName = attrs.asyncForm
  17. scope[attrs.name].response = response = {}
  18. scope[attrs.name].inflight = false
  19. scope.email = scope.email || ctrl.getEmailFromQuery() || attrs.newEmail
  20. const validateCaptchaIfEnabled = function(callback) {
  21. if (attrs.captchaActionName) {
  22. validateCaptchaV3(attrs.captchaActionName)
  23. }
  24. if (attrs.captcha != null) {
  25. validateCaptcha(callback)
  26. } else {
  27. callback()
  28. }
  29. }
  30. const submitRequest = function(grecaptchaResponse) {
  31. const formData = {}
  32. for (let data of Array.from(element.serializeArray())) {
  33. formData[data.name] = data.value
  34. }
  35. if (grecaptchaResponse) {
  36. formData['g-recaptcha-response'] = grecaptchaResponse
  37. }
  38. // clear the response object which may be referenced downstream
  39. Object.keys(response).forEach(field => delete response[field])
  40. scope[attrs.name].inflight = true
  41. // for asyncForm prevent automatic redirect to /login if
  42. // authentication fails, we will handle it ourselves
  43. const httpRequestFn = _httpRequestFn(element.attr('method'))
  44. return httpRequestFn(element.attr('action'), formData, {
  45. disableAutoLoginRedirect: true
  46. })
  47. .then(function(httpResponse) {
  48. const { data, headers } = httpResponse
  49. scope[attrs.name].inflight = false
  50. response.success = true
  51. response.error = false
  52. const onSuccessHandler = scope[attrs.onSuccess]
  53. if (onSuccessHandler) {
  54. onSuccessHandler(httpResponse)
  55. return
  56. }
  57. if (data.redir) {
  58. ga('send', 'event', formName, 'success')
  59. return (window.location = data.redir)
  60. } else if (data.message) {
  61. response.message = data.message
  62. if (data.message.type === 'error') {
  63. response.success = false
  64. response.error = true
  65. return ga('send', 'event', formName, 'failure', data.message)
  66. } else {
  67. return ga('send', 'event', formName, 'success')
  68. }
  69. } else if (scope.$eval(attrs.asyncFormDownloadResponse)) {
  70. const blob = new Blob([data], {
  71. type: headers('Content-Type')
  72. })
  73. location.href = URL.createObjectURL(blob) // Trigger file save
  74. }
  75. })
  76. .catch(function(httpResponse) {
  77. const { data, status } = httpResponse
  78. scope[attrs.name].inflight = false
  79. response.success = false
  80. response.error = true
  81. response.status = status
  82. response.data = data
  83. const onErrorHandler = scope[attrs.onError]
  84. if (onErrorHandler) {
  85. onErrorHandler(httpResponse)
  86. return
  87. }
  88. let responseMessage
  89. if (data.message && data.message.text) {
  90. responseMessage = data.message.text
  91. } else {
  92. responseMessage = data.message
  93. }
  94. if (status === 400) {
  95. // Bad Request
  96. response.message = {
  97. text:
  98. responseMessage ||
  99. 'Invalid Request. Please correct the data and try again.',
  100. type: 'error'
  101. }
  102. } else if (status === 403) {
  103. // Forbidden
  104. response.message = {
  105. text:
  106. responseMessage ||
  107. 'Session error. Please check you have cookies enabled. If the problem persists, try clearing your cache and cookies.',
  108. type: 'error'
  109. }
  110. } else if (status === 429) {
  111. response.message = {
  112. text:
  113. responseMessage ||
  114. 'Too many attempts. Please wait for a while and try again.',
  115. type: 'error'
  116. }
  117. } else {
  118. response.message = {
  119. text:
  120. responseMessage ||
  121. 'Something went wrong talking to the server :(. Please try again.',
  122. type: 'error'
  123. }
  124. }
  125. ga('send', 'event', formName, 'failure', data.message)
  126. })
  127. }
  128. const submit = () =>
  129. validateCaptchaIfEnabled(response => submitRequest(response))
  130. const _httpRequestFn = (method = 'post') => {
  131. const $HTTP_FNS = {
  132. post: $http.post,
  133. get: $http.get
  134. }
  135. return $HTTP_FNS[method.toLowerCase()]
  136. }
  137. element.on('submit', function(e) {
  138. e.preventDefault()
  139. submit()
  140. })
  141. if (attrs.autoSubmit) {
  142. submit()
  143. }
  144. }
  145. }))
  146. App.directive('formMessages', () => ({
  147. restrict: 'E',
  148. template: `\
  149. <div class="alert" ng-class="{
  150. 'alert-danger': form.response.message.type == 'error',
  151. 'alert-success': form.response.message.type != 'error'
  152. }" ng-show="!!form.response.message" ng-bind-html="form.response.message.text">
  153. </div>
  154. <div ng-transclude></div>\
  155. `,
  156. transclude: true,
  157. scope: {
  158. form: '=for'
  159. }
  160. }))