ActionButton.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import { marshalLogsJsonToHtml } from './marshaller.js';
  2. class ActionButton extends window.HTMLButtonElement {
  3. constructFromJson (json) {
  4. this.updateIterationTimestamp = 0;
  5. this.title = json.title
  6. this.temporaryStatusMessage = null
  7. this.isWaiting = false
  8. this.actionCallUrl = window.restBaseUrl + 'StartAction?actionName=' + this.title
  9. this.updateFromJson(json)
  10. this.onclick = () => { this.startAction() }
  11. this.constructTemplate()
  12. this.updateHtml()
  13. this.setAttribute("id", "actionButton_" + json.id)
  14. }
  15. updateFromJson (json) {
  16. // Fields that should not be updated
  17. //
  18. // title - as the callback URL relies on it
  19. // actionCallbackUrl - as it's based on the title
  20. // temporaryStatusMessage - as the button might be "waiting" on execution to finish while it's being updated.
  21. if (json.icon === '') {
  22. this.unicodeIcon = '&#x1f4a9'
  23. } else {
  24. this.unicodeIcon = unescape(json.icon)
  25. }
  26. }
  27. startAction () {
  28. this.disabled = true
  29. this.isWaiting = true
  30. this.updateHtml()
  31. this.classList = [] // Removes old animation classes
  32. window.fetch(this.actionCallUrl).then(res => res.json()
  33. ).then((json) => {
  34. marshalLogsJsonToHtml({"logs": [json.logEntry]})
  35. if (json.logEntry.timedOut) {
  36. this.onActionResult('actionTimeout', 'Timed out')
  37. } else if (json.logEntry.exitCode !== 0) {
  38. this.onActionResult('actionNonZeroExit', 'Exit code ' + json.logEntry.exitCode)
  39. } else {
  40. this.onActionResult('actionSuccess', 'Success!')
  41. }
  42. }).catch(err => {
  43. this.onActionError(err)
  44. })
  45. }
  46. onActionResult (cssClass, temporaryStatusMessage) {
  47. this.temporaryStatusMessage = '[ ' + temporaryStatusMessage + ' ]'
  48. this.updateHtml()
  49. this.classList.add(cssClass)
  50. setTimeout(() => {
  51. this.classList.remove(cssClass)
  52. }, 1000);
  53. }
  54. onActionError (err) {
  55. console.log('callback error', err)
  56. this.disabled = false
  57. this.isWaiting = false
  58. this.updateHtml()
  59. this.classList.add('actionFailed')
  60. setTimeout(() => {
  61. this.classList.remove('actionFailed')
  62. }, 1000);
  63. }
  64. constructTemplate () {
  65. const tpl = document.getElementById('tplActionButton')
  66. const content = tpl.content.cloneNode(true)
  67. /*
  68. * FIXME: Should probably be using a shadowdom here, but seem to
  69. * get an error when combined with custom elements.
  70. */
  71. this.appendChild(content)
  72. this.domTitle = this.querySelector('.title')
  73. this.domIcon = this.querySelector('.icon')
  74. }
  75. updateHtml () {
  76. if (this.temporaryStatusMessage != null) {
  77. this.domTitle.innerText = this.temporaryStatusMessage
  78. this.domTitle.classList.add('temporaryStatusMessage')
  79. this.isWaiting = false
  80. this.disabled = false
  81. setTimeout(() => {
  82. this.temporaryStatusMessage = null
  83. this.domTitle.classList.remove('temporaryStatusMessage')
  84. this.updateHtml()
  85. }, 2000)
  86. } else if (this.isWaiting) {
  87. this.domTitle.innerText = 'Waiting...'
  88. } else {
  89. this.domTitle.innerText = this.title
  90. }
  91. this.domIcon.innerHTML = this.unicodeIcon
  92. }
  93. }
  94. window.customElements.define('action-button', ActionButton, { extends: 'button' })