feat(ui): add CoverArtAvatar component and integrate it into artist and playlist lists

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan
2026-03-16 06:39:48 -04:00
parent ab8a58157a
commit cefa6e9619
5 changed files with 51 additions and 28 deletions
+6
View File
@@ -22,6 +22,7 @@ import { useDrag } from 'react-dnd'
import clsx from 'clsx'
import {
ArtistContextMenu,
CoverArtAvatar,
List,
QuickFilter,
useGetHandleArtistClick,
@@ -43,6 +44,10 @@ const useStyles = makeStyles({
verticalAlign: 'text-top',
},
row: {
'& td': {
paddingTop: '4px !important',
paddingBottom: '4px !important',
},
'&:hover': {
'& $contextMenu': {
visibility: 'visible',
@@ -170,6 +175,7 @@ const ArtistListView = ({ hasShow, hasEdit, hasList, width, ...rest }) => {
/>
) : (
<ArtistDatagrid rowClick={handleArtistLink} classes={{ row: classes.row }}>
<CoverArtAvatar source="id" />
<TextField source="name" />
<FunctionField
source="albumCount"
+6 -1
View File
@@ -2,12 +2,13 @@ import React from 'react'
import PropTypes from 'prop-types'
import List from '@material-ui/core/List'
import ListItem from '@material-ui/core/ListItem'
import ListItemAvatar from '@material-ui/core/ListItemAvatar'
import ListItemIcon from '@material-ui/core/ListItemIcon'
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'
import ListItemText from '@material-ui/core/ListItemText'
import { makeStyles } from '@material-ui/core/styles'
import { sanitizeListRestProps } from 'react-admin'
import { ArtistContextMenu, RatingField } from '../common'
import { ArtistContextMenu, CoverArtAvatar, RatingField } from '../common'
import config from '../config'
const useStyles = makeStyles(
@@ -47,7 +48,11 @@ const ArtistSimpleList = ({
data[id] && (
<span key={id} onClick={() => linkType(id)}>
<ListItem className={classes.listItem} button={true}>
<ListItemAvatar>
<CoverArtAvatar record={data[id]} />
</ListItemAvatar>
<ListItemText
style={{ marginLeft: '8px' }}
primary={
<>
<div className={classes.title}>{data[id].name}</div>
+36
View File
@@ -0,0 +1,36 @@
import { useRecordContext } from 'react-admin'
import { Avatar } from '@material-ui/core'
import { makeStyles } from '@material-ui/core/styles'
import clsx from 'clsx'
import subsonic from '../subsonic'
const useStyles = makeStyles({
avatar: {
width: '55px',
height: '55px',
},
square: {
borderRadius: '4px',
},
})
export const CoverArtAvatar = ({
record: recordProp,
variant = 'circular',
}) => {
const classes = useStyles()
const recordContext = useRecordContext()
const record = recordProp || recordContext
if (!record) return null
const square = variant !== 'circular'
return (
<Avatar
src={subsonic.getCoverArtUrl(record, 80, square)}
variant={variant}
className={clsx(classes.avatar, square && classes.square)}
alt={record.name}
/>
)
}
CoverArtAvatar.defaultProps = { label: '', sortable: false }
+1
View File
@@ -44,3 +44,4 @@ export * from './ParticipantsInfo'
export * from './OverflowTooltip'
export * from './useSearchRefocus'
export * from './ImageUploadOverlay'
export * from './CoverArtAvatar'
+2 -27
View File
@@ -16,10 +16,10 @@ import {
usePermissions,
} from 'react-admin'
import Switch from '@material-ui/core/Switch'
import { Avatar } from '@material-ui/core'
import { makeStyles } from '@material-ui/core/styles'
import { useMediaQuery } from '@material-ui/core'
import {
CoverArtAvatar,
DurationField,
List,
Writable,
@@ -29,17 +29,11 @@ import {
} from '../common'
import PlaylistListActions from './PlaylistListActions'
import ChangePublicStatusButton from './ChangePublicStatusButton'
import subsonic from '../subsonic'
const useStyles = makeStyles((theme) => ({
button: {
color: theme.palette.type === 'dark' ? 'white' : undefined,
},
coverArt: {
width: '40px',
height: '40px',
borderRadius: '4px',
},
}))
const PlaylistFilter = (props) => {
@@ -126,25 +120,6 @@ const ToggleAutoImport = ({ resource, source }) => {
) : null
}
const CoverArtField = () => {
const classes = useStyles()
const record = useRecordContext()
if (!record) return null
return (
<Avatar
src={subsonic.getCoverArtUrl(record, 80, true)}
variant="square"
className={classes.coverArt}
alt={record.name}
/>
)
}
CoverArtField.defaultProps = {
label: '',
sortable: false,
}
const PlaylistListBulkActions = (props) => {
const classes = useStyles()
return (
@@ -204,7 +179,7 @@ const PlaylistList = (props) => {
bulkActionButtons={!isXsmall && <PlaylistListBulkActions />}
>
<Datagrid rowClick="show" isRowSelectable={(r) => isWritable(r?.ownerId)}>
<CoverArtField source="id" />
<CoverArtAvatar source="id" variant="square" />
<TextField source="name" />
{columns}
<Writable>