mirror of
https://github.com/navidrome/navidrome.git
synced 2026-04-28 03:19:38 +00:00
feat(ui): abort all in-flight image fetches on pagination change
Pagination component now watches page/perPage via useListContext and calls abortAllInFlight() when either changes, freeing the browser connection pool immediately for the next page's data request. Also adds empty placeholder style to CoverArtAvatar so it renders as a clean transparent area while loading instead of the default person icon.
This commit is contained in:
parent
cafcb0bb59
commit
3bc09f9d03
2 changed files with 33 additions and 5 deletions
|
|
@ -1,6 +1,18 @@
|
|||
import React from 'react'
|
||||
import { Pagination as RAPagination } from 'react-admin'
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { Pagination as RAPagination, useListContext } from 'react-admin'
|
||||
import { abortAllInFlight } from './useImageUrl'
|
||||
|
||||
export const Pagination = (props) => (
|
||||
<RAPagination rowsPerPageOptions={[15, 25, 50]} {...props} />
|
||||
)
|
||||
export const Pagination = (props) => {
|
||||
const { page, perPage } = useListContext()
|
||||
const prevRef = useRef({ page, perPage })
|
||||
|
||||
useEffect(() => {
|
||||
const prev = prevRef.current
|
||||
if (prev.page !== page || prev.perPage !== perPage) {
|
||||
abortAllInFlight()
|
||||
prevRef.current = { page, perPage }
|
||||
}
|
||||
}, [page, perPage])
|
||||
|
||||
return <RAPagination rowsPerPageOptions={[15, 25, 50]} {...props} />
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,18 @@ import { useEffect, useState, useRef } from 'react'
|
|||
// React Admin refreshes (which remount list items) don't re-fetch images.
|
||||
const cache = new Map()
|
||||
const MAX_CACHE_SIZE = 300
|
||||
const activeControllers = new Set()
|
||||
|
||||
/**
|
||||
* Aborts all in-flight image fetches. Call this before navigation/pagination
|
||||
* so that pending image requests don't block the browser connection pool.
|
||||
*/
|
||||
export const abortAllInFlight = () => {
|
||||
for (const controller of activeControllers) {
|
||||
controller.abort()
|
||||
}
|
||||
activeControllers.clear()
|
||||
}
|
||||
|
||||
// Evicts oldest unused entries (Map iterates in insertion order).
|
||||
const evictIfNeeded = () => {
|
||||
|
|
@ -53,6 +65,7 @@ export const useImageUrl = (url) => {
|
|||
}
|
||||
|
||||
const controller = new AbortController()
|
||||
activeControllers.add(controller)
|
||||
setImgUrl(null)
|
||||
setLoading(true)
|
||||
setError(false)
|
||||
|
|
@ -83,8 +96,10 @@ export const useImageUrl = (url) => {
|
|||
setImgUrl(objectUrl)
|
||||
}
|
||||
setLoading(false)
|
||||
activeControllers.delete(controller)
|
||||
})
|
||||
.catch((err) => {
|
||||
activeControllers.delete(controller)
|
||||
if (err.name === 'AbortError') {
|
||||
return // Expected on unmount or URL change
|
||||
}
|
||||
|
|
@ -97,6 +112,7 @@ export const useImageUrl = (url) => {
|
|||
return () => {
|
||||
abortedRef.current = true
|
||||
controller.abort()
|
||||
activeControllers.delete(controller)
|
||||
const entry = cache.get(url)
|
||||
if (entry) {
|
||||
entry.refCount--
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue