Debouncing to avoid UI flicker while typing in search.
This commit is contained in:
@@ -10,6 +10,9 @@ import SearchWorker from '@/workers/searchWorker?worker'
|
|||||||
let searchWorker: Worker | null = null
|
let searchWorker: Worker | null = null
|
||||||
let searchId = 0
|
let searchId = 0
|
||||||
let searchStore: ReturnType<typeof useMainStore> | null = null
|
let searchStore: ReturnType<typeof useMainStore> | null = null
|
||||||
|
let loadingTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
let clearOldResultsTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
let lastResultUpdate = 0
|
||||||
|
|
||||||
function getSearchWorker(): Worker {
|
function getSearchWorker(): Worker {
|
||||||
if (!searchWorker) {
|
if (!searchWorker) {
|
||||||
@@ -18,14 +21,30 @@ function getSearchWorker(): Worker {
|
|||||||
searchWorker.onmessage = (e) => {
|
searchWorker.onmessage = (e) => {
|
||||||
if (!searchStore || e.data.id !== searchId) return // Stale result
|
if (!searchStore || e.data.id !== searchId) return // Stale result
|
||||||
|
|
||||||
// Convert plain data back to Doc instances (constructor is now lightweight)
|
// Convert plain data back to Doc instances
|
||||||
const docs = []
|
const docs = e.data.docs.map((d: any) => new Doc(d))
|
||||||
for (const d of e.data.docs) {
|
|
||||||
docs.push(new Doc(d))
|
// Cancel the clear-old-results timer since we have new results
|
||||||
|
if (clearOldResultsTimer) {
|
||||||
|
clearTimeout(clearOldResultsTimer)
|
||||||
|
clearOldResultsTimer = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Throttle rapid intermediate updates to reduce UI flicker
|
||||||
|
const now = performance.now()
|
||||||
|
if (!e.data.done && now - lastResultUpdate < 50) {
|
||||||
|
return // Skip intermediate update if too recent
|
||||||
|
}
|
||||||
|
lastResultUpdate = now
|
||||||
|
|
||||||
searchStore.searchResults = docs
|
searchStore.searchResults = docs
|
||||||
|
|
||||||
if (e.data.done) {
|
if (e.data.done) {
|
||||||
|
// Clear the loading timer and hide spinner
|
||||||
|
if (loadingTimer) {
|
||||||
|
clearTimeout(loadingTimer)
|
||||||
|
loadingTimer = null
|
||||||
|
}
|
||||||
searchStore.searchLoading = false
|
searchStore.searchLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,15 +157,40 @@ export const useMainStore = defineStore('main', {
|
|||||||
// Update query immediately so watchers know we're handling this
|
// Update query immediately so watchers know we're handling this
|
||||||
this.query = query
|
this.query = query
|
||||||
|
|
||||||
// Clear old results immediately - don't show stale data
|
// Cancel pending timers
|
||||||
this.searchResults = []
|
if (loadingTimer) {
|
||||||
|
clearTimeout(loadingTimer)
|
||||||
|
loadingTimer = null
|
||||||
|
}
|
||||||
|
if (clearOldResultsTimer) {
|
||||||
|
clearTimeout(clearOldResultsTimer)
|
||||||
|
clearOldResultsTimer = null
|
||||||
|
}
|
||||||
|
|
||||||
if (!query) {
|
if (!query) {
|
||||||
|
// Clear results only when search is closed
|
||||||
|
this.searchResults = []
|
||||||
this.searchLoading = false
|
this.searchLoading = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.searchLoading = true
|
// Keep old results briefly to avoid flicker on fast cached searches
|
||||||
|
// But clear them after 50ms if no new results have arrived
|
||||||
|
clearOldResultsTimer = setTimeout(() => {
|
||||||
|
if (searchId === id) {
|
||||||
|
this.searchResults = []
|
||||||
|
}
|
||||||
|
clearOldResultsTimer = null
|
||||||
|
}, 50)
|
||||||
|
|
||||||
|
// Delay showing loading indicator to avoid flicker on fast searches
|
||||||
|
loadingTimer = setTimeout(() => {
|
||||||
|
if (searchId === id) { // Still the current search
|
||||||
|
this.searchLoading = true
|
||||||
|
}
|
||||||
|
loadingTimer = null
|
||||||
|
}, 100)
|
||||||
|
|
||||||
worker.postMessage({ type: 'search', query, loc, id })
|
worker.postMessage({ type: 'search', query, loc, id })
|
||||||
},
|
},
|
||||||
login(username: string, privileged: boolean) {
|
login(username: string, privileged: boolean) {
|
||||||
|
|||||||
Reference in New Issue
Block a user