4
0

ActionButton.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import './ExecutionButton.js'
  2. import './ArgumentForm.js'
  3. import { ExecutionFeedbackButton } from './ExecutionFeedbackButton.js'
  4. class ActionButton extends ExecutionFeedbackButton {
  5. constructDomFromTemplate () {
  6. const tpl = document.getElementById('tplActionButton')
  7. const content = tpl.content.cloneNode(true)
  8. /*
  9. * FIXME: Should probably be using a shadowdom here, but seem to
  10. * get an error when combined with custom elements.
  11. */
  12. this.appendChild(content)
  13. this.btn = this.querySelector('button')
  14. this.domTitle = this.btn.querySelector('.title')
  15. this.domIcon = this.btn.querySelector('.icon')
  16. }
  17. constructFromJson (json) {
  18. this.updateIterationTimestamp = 0
  19. this.constructDomFromTemplate()
  20. // Class attributes
  21. this.updateFromJson(json)
  22. this.actionId = json.id
  23. // DOM Attributes
  24. this.setAttribute('role', 'none')
  25. this.setAttribute('id', 'actionButton-' + this.actionId)
  26. if (!json.canExec) {
  27. this.btn.disabled = true
  28. }
  29. this.btn.setAttribute('id', 'actionButtonInner-' + this.actionId)
  30. this.btn.title = json.title
  31. this.btn.onclick = () => {
  32. if (json.arguments.length > 0) {
  33. for (const oldArgumentForm of document.querySelectorAll('argument-form')) {
  34. oldArgumentForm.remove()
  35. }
  36. this.updateUrlWithAction()
  37. const frm = document.createElement('argument-form')
  38. frm.setup(json, (args) => {
  39. this.startAction(args)
  40. })
  41. document.body.appendChild(frm)
  42. frm.querySelector('dialog').showModal()
  43. } else {
  44. this.startAction()
  45. }
  46. }
  47. this.popupOnStart = json.popupOnStart
  48. this.updateFromJson(json)
  49. this.domTitle.innerText = this.btn.title
  50. this.domIcon.innerHTML = this.unicodeIcon
  51. }
  52. updateFromJson (json) {
  53. // Fields that should not be updated
  54. //
  55. // title - as the callback URL relies on it
  56. if (json.icon === '') {
  57. this.unicodeIcon = '&#x1f4a9'
  58. } else {
  59. this.unicodeIcon = unescape(json.icon)
  60. }
  61. this.domIcon.innerHTML = this.unicodeIcon
  62. }
  63. onExecStatusChanged () {
  64. this.btn.disabled = false
  65. setTimeout(() => {
  66. this.updateDom(null, this.btn.title)
  67. }, 2000)
  68. }
  69. getUniqueId () {
  70. if (window.isSecureContext) {
  71. return window.crypto.randomUUID()
  72. } else {
  73. return Date.now().toString()
  74. }
  75. }
  76. startAction (actionArgs) {
  77. this.btn.classList = [] // Removes old animation classes
  78. if (actionArgs === undefined) {
  79. actionArgs = []
  80. }
  81. // UUIDs are create client side, so that we can setup a "execution-button"
  82. // to track the execution before we send the request to the server.
  83. const startActionArgs = {
  84. actionId: this.actionId,
  85. arguments: actionArgs,
  86. uniqueTrackingId: this.getUniqueId()
  87. }
  88. this.onActionStarted(startActionArgs.uniqueTrackingId)
  89. window.fetch(window.restBaseUrl + 'StartAction', {
  90. method: 'POST',
  91. headers: {
  92. 'Content-Type': 'application/json'
  93. },
  94. body: JSON.stringify(startActionArgs)
  95. }).then((res) => {
  96. if (res.ok) {
  97. return res.json()
  98. } else {
  99. throw new Error(res.statusText)
  100. }
  101. }
  102. ).then((json) => {
  103. // The button used to wait for the action to finish, but now it is fire & forget
  104. }).catch(err => {
  105. throw err // We used to flash buttons red, but now hand to the global error handler
  106. })
  107. }
  108. updateUrlWithAction () {
  109. // Get the current URL and create a new URL object
  110. const url = new URL(window.location.href)
  111. // Set the action parameter
  112. url.searchParams.set('action', this.btn.title)
  113. // Update the URL without reloading the page
  114. window.history.replaceState({}, '', url.toString())
  115. }
  116. onActionStarted (executionTrackingId) {
  117. if (this.popupOnStart === 'execution-button') {
  118. const btnExecution = document.createElement('execution-button')
  119. btnExecution.constructFromJson(executionTrackingId)
  120. this.querySelector('.action-button-footer').hidden = false
  121. this.querySelector('.action-button-footer').style.display = 'flex'
  122. this.querySelector('.action-button-footer').prepend(btnExecution)
  123. return
  124. }
  125. if (this.popupOnStart.includes('execution-dialog')) {
  126. window.executionDialog.reset()
  127. if (this.popupOnStart === 'execution-dialog-stdout-only') {
  128. window.executionDialog.hideEverythingApartFromOutput()
  129. }
  130. window.executionDialog.executionTrackingId = executionTrackingId
  131. window.executionDialog.show(this)
  132. }
  133. this.btn.disabled = true
  134. }
  135. }
  136. window.customElements.define('action-button', ActionButton)