|
@@ -36,7 +36,12 @@
|
|
|
<script setup>
|
|
<script setup>
|
|
|
import { buttonResults } from './stores/buttonResults'
|
|
import { buttonResults } from './stores/buttonResults'
|
|
|
import { rateLimits } from './stores/rateLimits'
|
|
import { rateLimits } from './stores/rateLimits'
|
|
|
-import { bindingExecutionState, setBindingExecutionState } from './stores/bindingExecutionState'
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ bindingExecutionState,
|
|
|
|
|
+ pendingBindingFlash,
|
|
|
|
|
+ consumePendingBindingFlash,
|
|
|
|
|
+ setBindingExecutionState
|
|
|
|
|
+} from './stores/bindingExecutionState'
|
|
|
import { connectionState } from './stores/connectionState'
|
|
import { connectionState } from './stores/connectionState'
|
|
|
import { requestReconnectNow, applyExecutionLogEntry } from '../../js/websocket.js'
|
|
import { requestReconnectNow, applyExecutionLogEntry } from '../../js/websocket.js'
|
|
|
import { useRouter } from 'vue-router'
|
|
import { useRouter } from 'vue-router'
|
|
@@ -90,6 +95,7 @@ const isComponentMounted = ref(true)
|
|
|
|
|
|
|
|
// Animation classes
|
|
// Animation classes
|
|
|
const buttonClasses = ref([])
|
|
const buttonClasses = ref([])
|
|
|
|
|
+const flashedTrackingIds = new Set()
|
|
|
|
|
|
|
|
// Show navigate on start icons - defaults to true if not set
|
|
// Show navigate on start icons - defaults to true if not set
|
|
|
const showNavigateOnStartIcons = computed(() => {
|
|
const showNavigateOnStartIcons = computed(() => {
|
|
@@ -142,6 +148,18 @@ const executionIndicatorTitle = computed(() => {
|
|
|
return ''
|
|
return ''
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+function consumeAndFlashPendingResult () {
|
|
|
|
|
+ const id = bindingId.value
|
|
|
|
|
+ if (!id) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const pending = consumePendingBindingFlash(id)
|
|
|
|
|
+ if (pending) {
|
|
|
|
|
+ onExecutionFinished(pending)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// Timestamps
|
|
// Timestamps
|
|
|
const updateIterationTimestamp = ref(0)
|
|
const updateIterationTimestamp = ref(0)
|
|
|
|
|
|
|
@@ -376,6 +394,20 @@ function onExecutionStarted(logEntry) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function onExecutionFinished(logEntry) {
|
|
function onExecutionFinished(logEntry) {
|
|
|
|
|
+ const trackingId = logEntry.executionTrackingId
|
|
|
|
|
+ if (trackingId) {
|
|
|
|
|
+ if (flashedTrackingIds.has(trackingId)) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ flashedTrackingIds.add(trackingId)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Local no-arg watches and the binding-scoped pending flash can both
|
|
|
|
|
+ // observe the same finished execution; consume so only one path flashes.
|
|
|
|
|
+ if (bindingId.value) {
|
|
|
|
|
+ consumePendingBindingFlash(bindingId.value)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if (logEntry.timedOut) {
|
|
if (logEntry.timedOut) {
|
|
|
renderExecutionResult('action-timeout', 'Timed out')
|
|
renderExecutionResult('action-timeout', 'Timed out')
|
|
|
} else if (logEntry.blocked) {
|
|
} else if (logEntry.blocked) {
|
|
@@ -383,7 +415,6 @@ function onExecutionFinished(logEntry) {
|
|
|
} else if (logEntry.exitCode !== 0) {
|
|
} else if (logEntry.exitCode !== 0) {
|
|
|
renderExecutionResult('action-nonzero-exit', 'Exit code ' + logEntry.exitCode)
|
|
renderExecutionResult('action-nonzero-exit', 'Exit code ' + logEntry.exitCode)
|
|
|
} else {
|
|
} else {
|
|
|
- const ellapsed = Math.ceil(new Date(logEntry.datetimeFinished) - new Date(logEntry.datetimeStarted)) / 1000
|
|
|
|
|
renderExecutionResult('action-success', 'Success!')
|
|
renderExecutionResult('action-success', 'Success!')
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -430,6 +461,17 @@ onMounted(() => {
|
|
|
},
|
|
},
|
|
|
{ deep: true }
|
|
{ deep: true }
|
|
|
)
|
|
)
|
|
|
|
|
+
|
|
|
|
|
+ // Binding-scoped flash survives argument-form navigation/remount (#920).
|
|
|
|
|
+ watch(
|
|
|
|
|
+ () => pendingBindingFlash[bindingId.value],
|
|
|
|
|
+ (pending) => {
|
|
|
|
|
+ if (pending) {
|
|
|
|
|
+ consumeAndFlashPendingResult()
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ { immediate: true }
|
|
|
|
|
+ )
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
onUnmounted(() => {
|