fix: prevent race conditions in htmx boost response handling
This commit is contained in:
+28
-6
@@ -6,6 +6,7 @@ const DEBOUNCE_MS = 200
|
|||||||
let debounceTimeout = null
|
let debounceTimeout = null
|
||||||
let historyRestoreGracePeriod = false
|
let historyRestoreGracePeriod = false
|
||||||
let savedScrollPosition = null
|
let savedScrollPosition = null
|
||||||
|
let activeRequests = 0
|
||||||
|
|
||||||
function getIndicator() {
|
function getIndicator() {
|
||||||
return document.getElementById(INDICATOR_ID)
|
return document.getElementById(INDICATOR_ID)
|
||||||
@@ -35,6 +36,8 @@ function handleBeforeRequest(event) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
activeRequests++
|
||||||
|
|
||||||
// Save scroll position of the target container before HTMX request
|
// Save scroll position of the target container before HTMX request
|
||||||
const target = event.detail?.target || document.getElementById('main-content')
|
const target = event.detail?.target || document.getElementById('main-content')
|
||||||
if (target && typeof target.scrollTop === 'number') {
|
if (target && typeof target.scrollTop === 'number') {
|
||||||
@@ -52,6 +55,19 @@ function handleBeforeRequest(event) {
|
|||||||
}, DEBOUNCE_MS)
|
}, DEBOUNCE_MS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleRequestEnd() {
|
||||||
|
// Counter may already be 0 if a force-reset (history save, page show) fired
|
||||||
|
// before this afterRequest — ignore the stale completion in that case
|
||||||
|
if (0 === activeRequests) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
activeRequests--
|
||||||
|
if (0 === activeRequests) {
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleAfterSwap() {
|
function handleAfterSwap() {
|
||||||
// Restore scroll position after HTMX swaps content
|
// Restore scroll position after HTMX swaps content
|
||||||
if (savedScrollPosition !== null && savedScrollPosition.element) {
|
if (savedScrollPosition !== null && savedScrollPosition.element) {
|
||||||
@@ -61,6 +77,7 @@ function handleAfterSwap() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleHistoryRestore() {
|
function handleHistoryRestore() {
|
||||||
|
activeRequests = 0
|
||||||
hide()
|
hide()
|
||||||
|
|
||||||
// Set grace period to ignore change events triggered by history restore
|
// Set grace period to ignore change events triggered by history restore
|
||||||
@@ -71,24 +88,29 @@ function handleHistoryRestore() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleTimeout() {
|
function handleTimeout() {
|
||||||
hide()
|
// Don't decrement here — htmx:afterRequest always fires alongside htmx:timeout
|
||||||
|
// and handles the counter. This handler only shows the alert.
|
||||||
alert('Die Anfrage hat zu lange gedauert. Bitte versuchen Sie es erneut. Falls das Problem weiterhin besteht, kontaktieren Sie bitte unseren Support.')
|
alert('Die Anfrage hat zu lange gedauert. Bitte versuchen Sie es erneut. Falls das Problem weiterhin besteht, kontaktieren Sie bitte unseren Support.')
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBeforeHistorySave() {
|
function handleBeforeHistorySave() {
|
||||||
// Hide indicator before HTMX saves the page to history cache
|
// Hide indicator before HTMX saves the page to history cache
|
||||||
|
activeRequests = 0
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePageShow() {
|
||||||
|
activeRequests = 0
|
||||||
hide()
|
hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize event listeners
|
// Initialize event listeners
|
||||||
document.body.addEventListener('htmx:beforeRequest', handleBeforeRequest)
|
document.body.addEventListener('htmx:beforeRequest', handleBeforeRequest)
|
||||||
document.body.addEventListener('htmx:afterRequest', hide)
|
document.body.addEventListener('htmx:afterRequest', handleRequestEnd)
|
||||||
document.body.addEventListener('htmx:afterSwap', handleAfterSwap)
|
document.body.addEventListener('htmx:afterSwap', handleAfterSwap)
|
||||||
document.body.addEventListener('htmx:timeout', handleTimeout)
|
document.body.addEventListener('htmx:timeout', handleTimeout)
|
||||||
document.body.addEventListener('htmx:historyRestore', handleHistoryRestore)
|
document.body.addEventListener('htmx:historyRestore', handleHistoryRestore)
|
||||||
document.body.addEventListener('htmx:sendError', hide)
|
|
||||||
document.body.addEventListener('htmx:responseError', hide)
|
|
||||||
document.body.addEventListener('htmx:beforeHistorySave', handleBeforeHistorySave)
|
document.body.addEventListener('htmx:beforeHistorySave', handleBeforeHistorySave)
|
||||||
|
|
||||||
window.addEventListener('pageshow', hide)
|
window.addEventListener('pageshow', handlePageShow)
|
||||||
window.addEventListener('popstate', handleHistoryRestore)
|
window.addEventListener('popstate', handleHistoryRestore)
|
||||||
|
|||||||
Reference in New Issue
Block a user