fix: avoid loading indicator issues when using browser history for navigation

This commit is contained in:
Björn Fromme
2025-12-09 15:13:04 +01:00
parent 420efb25ad
commit 32c73ab7b1
9 changed files with 87 additions and 106 deletions
-99
View File
@@ -1,99 +0,0 @@
import {Controller} from '@hotwired/stimulus'
export default class extends Controller {
static targets = ['indicator']
static classes = ['hidden']
static values = {
visible: {
type: Boolean,
default: false
}
}
connect() {
this.debounceTimeout = null
// Bind event handlers to preserve context
this.boundHandleBeforeRequest = this.handleBeforeRequest.bind(this)
this.boundHandleAfterRequest = this.handleAfterRequest.bind(this)
this.boundHandleTimeout = this.handleTimeout.bind(this)
this.boundHandlePageShow = this.handlePageShow.bind(this)
this.boundHandleHistoryRestore = this.handleHistoryRestore.bind(this)
// Listen to HTMX events
document.body.addEventListener('htmx:beforeRequest', this.boundHandleBeforeRequest)
document.body.addEventListener('htmx:afterRequest', this.boundHandleAfterRequest)
document.body.addEventListener('htmx:timeout', this.boundHandleTimeout)
document.body.addEventListener('htmx:historyRestore', this.boundHandleHistoryRestore)
// Listen for browser back/forward navigation
window.addEventListener('pageshow', this.boundHandlePageShow)
}
disconnect() {
// Clean up event listeners
document.body.removeEventListener('htmx:beforeRequest', this.boundHandleBeforeRequest)
document.body.removeEventListener('htmx:afterRequest', this.boundHandleAfterRequest)
document.body.removeEventListener('htmx:timeout', this.boundHandleTimeout)
document.body.removeEventListener('htmx:historyRestore', this.boundHandleHistoryRestore)
window.removeEventListener('pageshow', this.boundHandlePageShow)
// Clear any pending timeout
if (this.debounceTimeout) {
clearTimeout(this.debounceTimeout)
}
}
handleBeforeRequest() {
// Don't start a new debounce if already visible
if (true === this.visibleValue) {
return
}
// Show indicator after 200ms delay for field refreshes
this.debounceTimeout = setTimeout(() => {
this.show()
}, 200)
}
handleAfterRequest() {
// Clear debounce timer if request completes before 200ms
if (this.debounceTimeout) {
clearTimeout(this.debounceTimeout)
this.debounceTimeout = null
}
this.hide()
}
handleTimeout() {
// Hide loading indicator
this.hide()
// Show user-friendly error message
alert('Die Anfrage hat zu lange gedauert. Bitte versuchen Sie es erneut. Falls das Problem weiterhin besteht, kontaktieren Sie bitte unseren Support.')
}
handlePageShow(event) {
// event.persisted is true when page is restored from bfcache (back/forward navigation)
if (true === event.persisted) {
this.hide()
}
}
handleHistoryRestore() {
// HTMX history restore - hide loading indicator
this.hide()
}
show() {
this.visibleValue = true
this.indicatorTarget.classList.remove(this.hiddenClass)
}
hide() {
this.visibleValue = false
this.indicatorTarget.classList.add(this.hiddenClass)
}
}