2026-03-16 02:39:48 -08:00
|
|
|
import { useRecordContext } from 'react-admin'
|
|
|
|
|
import { Avatar } from '@material-ui/core'
|
|
|
|
|
import { makeStyles } from '@material-ui/core/styles'
|
|
|
|
|
import clsx from 'clsx'
|
2026-04-04 11:17:01 -08:00
|
|
|
import config from '../config'
|
2026-03-16 02:39:48 -08:00
|
|
|
import subsonic from '../subsonic'
|
fix(ui): cancel in-flight image requests on pagination, cache across remounts (#5249)
* feat(ui): cancel in-flight image requests on pagination and cache across remounts
When paginating quickly through list/grid views, image requests for previous
pages were never canceled, queuing on the server and blocking new images.
This adds a useImageUrl hook that loads images via fetch() with AbortController,
so requests are canceled when components unmount. A module-level cache (URL →
blob URL) with reference counting ensures React Admin refreshes display images
instantly without re-fetching.
* feat(ui): update AlbumListPagination to conditionally render based on albumListType
Signed-off-by: Deluan <deluan@navidrome.org>
* 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.
* Revert "feat(ui): abort all in-flight image fetches on pagination change"
This reverts commit 3bc09f9d0374aa63572a381e38a30e2f2cec4da8.
* fix(ui): limit concurrent image fetches to prevent connection pool saturation
With <img src>, the browser prioritizes API requests over image loads.
With fetch(), all requests compete equally for the HTTP/1.1 connection
pool (6 per origin), causing API requests to queue behind images and
making pagination feel unresponsive. Caps concurrent image fetches at
4 with a pending queue, leaving connections free for API requests.
Queued fetches for unmounted components are removed without ever
hitting the network.
* fix(ui): fix queued fetch not aborted on unmount
Set queued=false when doFetch executes from the pending queue, so
cleanup correctly calls controller.abort() instead of searching an
already-drained queue.
---------
Signed-off-by: Deluan <deluan@navidrome.org>
2026-03-25 17:30:40 -08:00
|
|
|
import { useImageUrl } from './useImageUrl'
|
2026-03-16 02:39:48 -08:00
|
|
|
|
|
|
|
|
const useStyles = makeStyles({
|
|
|
|
|
avatar: {
|
|
|
|
|
width: '55px',
|
|
|
|
|
height: '55px',
|
|
|
|
|
},
|
fix(ui): cancel in-flight image requests on pagination, cache across remounts (#5249)
* feat(ui): cancel in-flight image requests on pagination and cache across remounts
When paginating quickly through list/grid views, image requests for previous
pages were never canceled, queuing on the server and blocking new images.
This adds a useImageUrl hook that loads images via fetch() with AbortController,
so requests are canceled when components unmount. A module-level cache (URL →
blob URL) with reference counting ensures React Admin refreshes display images
instantly without re-fetching.
* feat(ui): update AlbumListPagination to conditionally render based on albumListType
Signed-off-by: Deluan <deluan@navidrome.org>
* 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.
* Revert "feat(ui): abort all in-flight image fetches on pagination change"
This reverts commit 3bc09f9d0374aa63572a381e38a30e2f2cec4da8.
* fix(ui): limit concurrent image fetches to prevent connection pool saturation
With <img src>, the browser prioritizes API requests over image loads.
With fetch(), all requests compete equally for the HTTP/1.1 connection
pool (6 per origin), causing API requests to queue behind images and
making pagination feel unresponsive. Caps concurrent image fetches at
4 with a pending queue, leaving connections free for API requests.
Queued fetches for unmounted components are removed without ever
hitting the network.
* fix(ui): fix queued fetch not aborted on unmount
Set queued=false when doFetch executes from the pending queue, so
cleanup correctly calls controller.abort() instead of searching an
already-drained queue.
---------
Signed-off-by: Deluan <deluan@navidrome.org>
2026-03-25 17:30:40 -08:00
|
|
|
avatarEmpty: {
|
|
|
|
|
backgroundColor: 'transparent',
|
|
|
|
|
},
|
2026-03-16 02:39:48 -08:00
|
|
|
square: {
|
|
|
|
|
borderRadius: '4px',
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export const CoverArtAvatar = ({
|
|
|
|
|
record: recordProp,
|
|
|
|
|
variant = 'circular',
|
|
|
|
|
}) => {
|
|
|
|
|
const classes = useStyles()
|
|
|
|
|
const recordContext = useRecordContext()
|
|
|
|
|
const record = recordProp || recordContext
|
|
|
|
|
const square = variant !== 'circular'
|
fix(ui): cancel in-flight image requests on pagination, cache across remounts (#5249)
* feat(ui): cancel in-flight image requests on pagination and cache across remounts
When paginating quickly through list/grid views, image requests for previous
pages were never canceled, queuing on the server and blocking new images.
This adds a useImageUrl hook that loads images via fetch() with AbortController,
so requests are canceled when components unmount. A module-level cache (URL →
blob URL) with reference counting ensures React Admin refreshes display images
instantly without re-fetching.
* feat(ui): update AlbumListPagination to conditionally render based on albumListType
Signed-off-by: Deluan <deluan@navidrome.org>
* 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.
* Revert "feat(ui): abort all in-flight image fetches on pagination change"
This reverts commit 3bc09f9d0374aa63572a381e38a30e2f2cec4da8.
* fix(ui): limit concurrent image fetches to prevent connection pool saturation
With <img src>, the browser prioritizes API requests over image loads.
With fetch(), all requests compete equally for the HTTP/1.1 connection
pool (6 per origin), causing API requests to queue behind images and
making pagination feel unresponsive. Caps concurrent image fetches at
4 with a pending queue, leaving connections free for API requests.
Queued fetches for unmounted components are removed without ever
hitting the network.
* fix(ui): fix queued fetch not aborted on unmount
Set queued=false when doFetch executes from the pending queue, so
cleanup correctly calls controller.abort() instead of searching an
already-drained queue.
---------
Signed-off-by: Deluan <deluan@navidrome.org>
2026-03-25 17:30:40 -08:00
|
|
|
const url = record
|
2026-04-04 11:17:01 -08:00
|
|
|
? subsonic.getCoverArtUrl(record, config.uiCoverArtSize, square)
|
fix(ui): cancel in-flight image requests on pagination, cache across remounts (#5249)
* feat(ui): cancel in-flight image requests on pagination and cache across remounts
When paginating quickly through list/grid views, image requests for previous
pages were never canceled, queuing on the server and blocking new images.
This adds a useImageUrl hook that loads images via fetch() with AbortController,
so requests are canceled when components unmount. A module-level cache (URL →
blob URL) with reference counting ensures React Admin refreshes display images
instantly without re-fetching.
* feat(ui): update AlbumListPagination to conditionally render based on albumListType
Signed-off-by: Deluan <deluan@navidrome.org>
* 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.
* Revert "feat(ui): abort all in-flight image fetches on pagination change"
This reverts commit 3bc09f9d0374aa63572a381e38a30e2f2cec4da8.
* fix(ui): limit concurrent image fetches to prevent connection pool saturation
With <img src>, the browser prioritizes API requests over image loads.
With fetch(), all requests compete equally for the HTTP/1.1 connection
pool (6 per origin), causing API requests to queue behind images and
making pagination feel unresponsive. Caps concurrent image fetches at
4 with a pending queue, leaving connections free for API requests.
Queued fetches for unmounted components are removed without ever
hitting the network.
* fix(ui): fix queued fetch not aborted on unmount
Set queued=false when doFetch executes from the pending queue, so
cleanup correctly calls controller.abort() instead of searching an
already-drained queue.
---------
Signed-off-by: Deluan <deluan@navidrome.org>
2026-03-25 17:30:40 -08:00
|
|
|
: null
|
|
|
|
|
const { imgUrl } = useImageUrl(url)
|
|
|
|
|
if (!record) return null
|
2026-03-16 02:39:48 -08:00
|
|
|
return (
|
|
|
|
|
<Avatar
|
fix(ui): cancel in-flight image requests on pagination, cache across remounts (#5249)
* feat(ui): cancel in-flight image requests on pagination and cache across remounts
When paginating quickly through list/grid views, image requests for previous
pages were never canceled, queuing on the server and blocking new images.
This adds a useImageUrl hook that loads images via fetch() with AbortController,
so requests are canceled when components unmount. A module-level cache (URL →
blob URL) with reference counting ensures React Admin refreshes display images
instantly without re-fetching.
* feat(ui): update AlbumListPagination to conditionally render based on albumListType
Signed-off-by: Deluan <deluan@navidrome.org>
* 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.
* Revert "feat(ui): abort all in-flight image fetches on pagination change"
This reverts commit 3bc09f9d0374aa63572a381e38a30e2f2cec4da8.
* fix(ui): limit concurrent image fetches to prevent connection pool saturation
With <img src>, the browser prioritizes API requests over image loads.
With fetch(), all requests compete equally for the HTTP/1.1 connection
pool (6 per origin), causing API requests to queue behind images and
making pagination feel unresponsive. Caps concurrent image fetches at
4 with a pending queue, leaving connections free for API requests.
Queued fetches for unmounted components are removed without ever
hitting the network.
* fix(ui): fix queued fetch not aborted on unmount
Set queued=false when doFetch executes from the pending queue, so
cleanup correctly calls controller.abort() instead of searching an
already-drained queue.
---------
Signed-off-by: Deluan <deluan@navidrome.org>
2026-03-25 17:30:40 -08:00
|
|
|
src={imgUrl || undefined}
|
2026-03-16 02:39:48 -08:00
|
|
|
variant={variant}
|
fix(ui): cancel in-flight image requests on pagination, cache across remounts (#5249)
* feat(ui): cancel in-flight image requests on pagination and cache across remounts
When paginating quickly through list/grid views, image requests for previous
pages were never canceled, queuing on the server and blocking new images.
This adds a useImageUrl hook that loads images via fetch() with AbortController,
so requests are canceled when components unmount. A module-level cache (URL →
blob URL) with reference counting ensures React Admin refreshes display images
instantly without re-fetching.
* feat(ui): update AlbumListPagination to conditionally render based on albumListType
Signed-off-by: Deluan <deluan@navidrome.org>
* 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.
* Revert "feat(ui): abort all in-flight image fetches on pagination change"
This reverts commit 3bc09f9d0374aa63572a381e38a30e2f2cec4da8.
* fix(ui): limit concurrent image fetches to prevent connection pool saturation
With <img src>, the browser prioritizes API requests over image loads.
With fetch(), all requests compete equally for the HTTP/1.1 connection
pool (6 per origin), causing API requests to queue behind images and
making pagination feel unresponsive. Caps concurrent image fetches at
4 with a pending queue, leaving connections free for API requests.
Queued fetches for unmounted components are removed without ever
hitting the network.
* fix(ui): fix queued fetch not aborted on unmount
Set queued=false when doFetch executes from the pending queue, so
cleanup correctly calls controller.abort() instead of searching an
already-drained queue.
---------
Signed-off-by: Deluan <deluan@navidrome.org>
2026-03-25 17:30:40 -08:00
|
|
|
className={clsx(
|
|
|
|
|
classes.avatar,
|
|
|
|
|
square && classes.square,
|
|
|
|
|
!imgUrl && classes.avatarEmpty,
|
|
|
|
|
)}
|
2026-03-16 02:39:48 -08:00
|
|
|
alt={record.name}
|
fix(ui): cancel in-flight image requests on pagination, cache across remounts (#5249)
* feat(ui): cancel in-flight image requests on pagination and cache across remounts
When paginating quickly through list/grid views, image requests for previous
pages were never canceled, queuing on the server and blocking new images.
This adds a useImageUrl hook that loads images via fetch() with AbortController,
so requests are canceled when components unmount. A module-level cache (URL →
blob URL) with reference counting ensures React Admin refreshes display images
instantly without re-fetching.
* feat(ui): update AlbumListPagination to conditionally render based on albumListType
Signed-off-by: Deluan <deluan@navidrome.org>
* 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.
* Revert "feat(ui): abort all in-flight image fetches on pagination change"
This reverts commit 3bc09f9d0374aa63572a381e38a30e2f2cec4da8.
* fix(ui): limit concurrent image fetches to prevent connection pool saturation
With <img src>, the browser prioritizes API requests over image loads.
With fetch(), all requests compete equally for the HTTP/1.1 connection
pool (6 per origin), causing API requests to queue behind images and
making pagination feel unresponsive. Caps concurrent image fetches at
4 with a pending queue, leaving connections free for API requests.
Queued fetches for unmounted components are removed without ever
hitting the network.
* fix(ui): fix queued fetch not aborted on unmount
Set queued=false when doFetch executes from the pending queue, so
cleanup correctly calls controller.abort() instead of searching an
already-drained queue.
---------
Signed-off-by: Deluan <deluan@navidrome.org>
2026-03-25 17:30:40 -08:00
|
|
|
>
|
|
|
|
|
{/* Empty child prevents default person icon while loading */}
|
|
|
|
|
{!imgUrl && <span />}
|
|
|
|
|
</Avatar>
|
2026-03-16 02:39:48 -08:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CoverArtAvatar.defaultProps = { label: '', sortable: false }
|