ActionButton.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import { marshalLogsJsonToHtml } from './marshaller.js'
  2. import './ArgumentForm.js'
  3. class ActionButton extends window.HTMLElement {
  4. constructFromJson (json) {
  5. this.updateIterationTimestamp = 0
  6. this.constructDomFromTemplate()
  7. // Class attributes
  8. this.temporaryStatusMessage = null
  9. this.isWaiting = false
  10. this.actionCallUrl = window.restBaseUrl + 'StartAction'
  11. this.updateFromJson(json)
  12. // DOM Attributes
  13. this.setAttribute('role', 'none')
  14. this.btn.title = json.title
  15. this.btn.onclick = () => {
  16. console.log(json.arguments)
  17. if (json.arguments.length > 0) {
  18. const frm = document.createElement('argument-form')
  19. frm.setup(json, (args) => {
  20. this.startAction(args)
  21. })
  22. document.body.appendChild(frm)
  23. } else {
  24. this.startAction()
  25. }
  26. }
  27. this.updateFromJson(json)
  28. this.updateDom()
  29. this.setAttribute('id', 'actionButton_' + json.id)
  30. }
  31. updateFromJson (json) {
  32. // Fields that should not be updated
  33. //
  34. // title - as the callback URL relies on it
  35. // actionCallbackUrl - as it's based on the title
  36. // temporaryStatusMessage - as the button might be "waiting" on execution to finish while it's being updated.
  37. if (json.icon === '') {
  38. this.unicodeIcon = '&#x1f4a9'
  39. } else {
  40. this.unicodeIcon = unescape(json.icon)
  41. }
  42. }
  43. startAction (actionArgs) {
  44. this.btn.disabled = true
  45. this.isWaiting = true
  46. this.updateDom()
  47. this.btn.classList = [] // Removes old animation classes
  48. if (actionArgs === undefined) {
  49. actionArgs = []
  50. }
  51. const startActionArgs = {
  52. actionName: this.btn.title,
  53. arguments: actionArgs
  54. }
  55. window.fetch(this.actionCallUrl, {
  56. method: 'POST',
  57. headers: {
  58. 'Content-Type': 'application/json'
  59. },
  60. body: JSON.stringify(startActionArgs)
  61. }).then((res) => {
  62. if (res.ok) {
  63. return res.json()
  64. } else {
  65. throw new Error(res.statusText)
  66. }
  67. }
  68. ).then((json) => {
  69. marshalLogsJsonToHtml({ logs: [json.logEntry] })
  70. if (json.logEntry.timedOut) {
  71. this.onActionResult('actionTimeout', 'Timed out')
  72. } else if (json.logEntry.exitCode === -1337) {
  73. this.onActionError('Error')
  74. } else if (json.logEntry.exitCode !== 0) {
  75. this.onActionResult('actionNonZeroExit', 'Exit code ' + json.logEntry.exitCode)
  76. } else {
  77. this.onActionResult('actionSuccess', 'Success!')
  78. }
  79. }).catch(err => {
  80. this.onActionError(err)
  81. })
  82. }
  83. onActionResult (cssClass, temporaryStatusMessage) {
  84. this.btn.disabled = false
  85. this.temporaryStatusMessage = '[ ' + temporaryStatusMessage + ' ]'
  86. this.updateDom()
  87. this.btn.classList.add(cssClass)
  88. setTimeout(() => {
  89. this.btn.classList.remove(cssClass)
  90. }, 1000)
  91. }
  92. onActionError (err) {
  93. console.error('callback error', err)
  94. this.btn.disabled = false
  95. this.isWaiting = false
  96. this.updateDom()
  97. this.btn.classList.add('actionFailed')
  98. setTimeout(() => {
  99. this.btn.classList.remove('actionFailed')
  100. }, 1000)
  101. }
  102. constructDomFromTemplate () {
  103. const tpl = document.getElementById('tplActionButton')
  104. const content = tpl.content.cloneNode(true)
  105. /*
  106. * FIXME: Should probably be using a shadowdom here, but seem to
  107. * get an error when combined with custom elements.
  108. */
  109. this.appendChild(content)
  110. this.btn = this.querySelector('button')
  111. this.domTitle = this.btn.querySelector('.title')
  112. this.domIcon = this.btn.querySelector('.icon')
  113. }
  114. updateDom () {
  115. if (this.temporaryStatusMessage != null) {
  116. this.domTitle.innerText = this.temporaryStatusMessage
  117. this.domTitle.classList.add('temporaryStatusMessage')
  118. this.isWaiting = false
  119. this.disabled = false
  120. setTimeout(() => {
  121. this.temporaryStatusMessage = null
  122. this.domTitle.classList.remove('temporaryStatusMessage')
  123. this.updateDom()
  124. }, 2000)
  125. } else if (this.isWaiting) {
  126. this.domTitle.innerText = 'Waiting...'
  127. } else {
  128. this.domTitle.innerText = this.btn.title
  129. }
  130. this.domIcon.innerHTML = this.unicodeIcon
  131. }
  132. }
  133. window.customElements.define('action-button', ActionButton)