ExecutionDialog.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import { ActionStatusDisplay } from './ActionStatusDisplay.js'
  2. // This ExecutionDialog is NOT a custom HTML element, but rather just picks up
  3. // the <dialog /> element out of index.html and just re-uses that - as only
  4. // one dialog can be shown at a time.
  5. export class ExecutionDialog {
  6. constructor () {
  7. this.dlg = document.querySelector('dialog#execution-results-popup')
  8. this.domIcon = document.getElementById('execution-dialog-icon')
  9. this.domTitle = document.getElementById('execution-dialog-title')
  10. this.domOutput = document.getElementById('execution-dialog-xterm')
  11. this.domOutputToggleBig = document.getElementById('execution-dialog-toggle-size')
  12. this.domOutputToggleBig.onclick = () => {
  13. this.toggleSize()
  14. }
  15. this.domBtnKill = document.getElementById('execution-dialog-kill-action')
  16. this.domDuration = document.getElementById('execution-dialog-duration')
  17. this.domStatus = new ActionStatusDisplay(document.getElementById('execution-dialog-status'))
  18. this.domExecutionBasics = document.getElementById('execution-dialog-basics')
  19. this.domExecutionDetails = document.getElementById('execution-dialog-details')
  20. window.terminal.open(this.domOutput)
  21. }
  22. showOutput () {
  23. this.domOutput.hidden = false
  24. this.domOutput.hidden = false
  25. }
  26. toggleSize () {
  27. if (this.dlg.classList.contains('big')) {
  28. this.dlg.classList.remove('big')
  29. } else {
  30. this.dlg.classList.add('big')
  31. }
  32. window.terminal.fit.fit()
  33. }
  34. reset () {
  35. this.executionSeconds = 0
  36. this.executionTrackingId = 'notset'
  37. this.dlg.classList.remove('big')
  38. this.domOutputToggleBig.hidden = false
  39. this.domIcon.innerText = ''
  40. this.domTitle.innerText = 'Waiting for result... '
  41. this.domDuration.innerText = ''
  42. // window.terminal.close()
  43. this.domBtnKill.disabled = true
  44. this.domBtnKill.onclick = () => {}
  45. this.hideDetailsOnResult = false
  46. this.domExecutionBasics.hidden = false
  47. this.domExecutionDetails.hidden = true
  48. window.terminal.reset()
  49. window.terminal.fit.fit()
  50. }
  51. show (actionButton) {
  52. if (typeof actionButton !== 'undefined' && actionButton != null) {
  53. this.domIcon.innerText = actionButton.domIcon.innerText
  54. }
  55. this.domBtnKill.disabled = false
  56. this.domBtnKill.onclick = () => {
  57. this.killAction()
  58. }
  59. clearInterval(window.executionDialogTicker)
  60. this.executionSeconds = 0
  61. this.executionTick()
  62. window.executionDialogTicker = setInterval(() => {
  63. this.executionTick()
  64. }, 1000)
  65. if (this.dlg.open) {
  66. this.dlg.close()
  67. }
  68. this.dlg.showModal()
  69. }
  70. killAction () {
  71. const killActionArgs = {
  72. executionTrackingId: this.executionTrackingId
  73. }
  74. window.fetch(window.restBaseUrl + 'KillAction', {
  75. cors: 'cors',
  76. method: 'POST',
  77. headers: {
  78. 'Content-Type': 'application/json'
  79. },
  80. body: JSON.stringify(killActionArgs)
  81. }).then((res) => {
  82. return res.json() // This isn't used by anything. UI is updated by OnExecutionFinished like normal.
  83. }).catch(err => {
  84. throw err
  85. })
  86. }
  87. executionTick () {
  88. this.executionSeconds++
  89. this.updateDuration(this.executionSeconds + ' seconds ago', '')
  90. }
  91. hideEverythingApartFromOutput () {
  92. this.hideDetailsOnResult = true
  93. this.domExecutionBasics.hidden = true
  94. }
  95. fetchExecutionResult (executionTrackingId) {
  96. this.executionTrackingId = executionTrackingId
  97. const executionStatusArgs = {
  98. executionTrackingId: this.executionTrackingId
  99. }
  100. window.fetch(window.restBaseUrl + 'ExecutionStatus', {
  101. cors: 'cors',
  102. method: 'POST',
  103. headers: {
  104. 'Content-Type': 'application/json'
  105. },
  106. body: JSON.stringify(executionStatusArgs)
  107. }).then((res) => {
  108. if (res.ok) {
  109. return res.json()
  110. } else {
  111. throw new Error(res.statusText)
  112. }
  113. }
  114. ).then((json) => {
  115. this.renderExecutionResult(json)
  116. }).catch(err => {
  117. this.renderError(err)
  118. })
  119. }
  120. updateDuration (started, finished) {
  121. if (finished === '') {
  122. this.domDuration.innerHTML = started
  123. } else {
  124. let delta = ''
  125. try {
  126. delta = (new Date(finished) - new Date(started)) / 1000
  127. delta = new Intl.RelativeTimeFormat().format(delta, 'seconds').replace('in ', '').replace('ago', '')
  128. } catch (e) {
  129. console.warn('Failed to calculate delta', e)
  130. }
  131. this.domDuration.innerHTML = started + ' &rarr; ' + finished
  132. if (delta !== '') {
  133. this.domDuration.innerHTML += ' (' + delta + ')'
  134. }
  135. }
  136. }
  137. renderExecutionResult (res) {
  138. this.res = res
  139. clearInterval(window.executionDialogTicker)
  140. this.domOutput.hidden = false
  141. if (!this.hideDetailsOnResult) {
  142. this.domExecutionDetails.hidden = false
  143. }
  144. this.executionTrackingId = res.logEntry.executionTrackingId
  145. this.domBtnKill.disabled = res.logEntry.executionFinished
  146. this.domStatus.update(res.logEntry)
  147. this.domIcon.innerHTML = res.logEntry.actionIcon
  148. this.domTitle.innerText = res.logEntry.actionTitle
  149. if (res.logEntry.executionFinished) {
  150. this.updateDuration(res.logEntry.datetimeStarted, res.logEntry.datetimeFinished)
  151. } else {
  152. this.updateDuration(res.logEntry.datetimeStarted, 'Still running...')
  153. }
  154. window.terminal.reset()
  155. window.terminal.write(res.logEntry.output, () => {
  156. window.terminal.fit.fit()
  157. })
  158. }
  159. renderError (err) {
  160. this.dlg.querySelector('pre').innerText = JSON.stringify(err)
  161. }
  162. }