fix: implement fetch generation counter to handle concurrent refreshes in ScrollingList
Some checks failed
Chore App Build, Test, and Push Docker Images / build-and-push (push) Has been cancelled

- Added a fetch generation counter to the fetchItems method to discard stale results from concurrent fetches.
- Updated the loading state management to ensure it only updates if the fetch is the latest.
- Enhanced the refresh method to prevent stale data from being rendered when multiple refreshes occur.
- Added a test case to verify that stale fetches are discarded correctly when concurrent refreshes happen.
- Fixed CSS for mobile banners to prevent overflow and adjusted font sizes for better visibility.
- Resolved issues with task icons disappearing when extending time on overdue chores by ensuring only one fetch is active at a time.
This commit is contained in:
2026-03-25 15:30:00 -04:00
parent f64311689b
commit 359c170b27
15 changed files with 166 additions and 35 deletions

View File

@@ -39,14 +39,19 @@ const scrollWrapper = ref<HTMLDivElement | null>(null)
const itemRefs = ref<Record<string, HTMLElement | Element | null>>({})
const lastCenteredItemId = ref<string | null>(null)
let fetchGeneration = 0
const fetchItems = async () => {
const thisGeneration = ++fetchGeneration
loading.value = true
error.value = null
try {
const resp = await fetch(fetchUrl.value)
if (thisGeneration !== fetchGeneration) return
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
const data = await resp.json()
if (thisGeneration !== fetchGeneration) return
// Try to use 'tasks', 'reward_status', or 'items' as fallback
let itemList = data[props.itemKey || 'items'] || []
if (props.filterFn) itemList = itemList.filter(props.filterFn)
@@ -75,18 +80,23 @@ const fetchItems = async () => {
}
}),
)
if (thisGeneration !== fetchGeneration) return
// Re-assign to trigger Vue reactivity for image URLs set on raw objects
items.value = [...itemList]
} catch (err) {
if (thisGeneration !== fetchGeneration) return
error.value = err instanceof Error ? err.message : 'Failed to fetch items'
items.value = []
console.error('Error fetching items:', err)
} finally {
loading.value = false
if (thisGeneration === fetchGeneration) {
loading.value = false
}
}
}
async function refresh() {
await fetchItems()
loading.value = false
}
const scrollToItem = async (itemId: string) => {