feat(ui): Show performer subrole(s) where possible (#3747)

* feat(ui): Show performer subrole(s) where possible

* nit: simplify subrole formatting

Signed-off-by: Deluan <deluan@navidrome.org>

---------

Signed-off-by: Deluan <deluan@navidrome.org>
Co-authored-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Kendall Garner
2025-02-22 17:05:19 +00:00
committed by GitHub
parent aee19e747c
commit f6eee65955
3 changed files with 71 additions and 42 deletions
+6 -1
View File
@@ -26,6 +26,9 @@ const useStyles = makeStyles({
tableCell: { tableCell: {
width: '17.5%', width: '17.5%',
}, },
value: {
whiteSpace: 'pre-line',
},
}) })
const AlbumInfo = (props) => { const AlbumInfo = (props) => {
@@ -113,7 +116,9 @@ const AlbumInfo = (props) => {
})} })}
: :
</TableCell> </TableCell>
<TableCell align="left">{data[key]}</TableCell> <TableCell align="left" className={classes.value}>
{data[key]}
</TableCell>
</TableRow> </TableRow>
) )
})} })}
+16 -5
View File
@@ -23,6 +23,7 @@ const ALink = withWidth()((props) => {
{...rest} {...rest}
> >
{artist.name} {artist.name}
{artist.subroles?.length > 0 ? ` (${artist.subroles.join(', ')})` : ''}
</Link> </Link>
) )
}) })
@@ -89,19 +90,29 @@ export const ArtistLinkField = ({ record, className, limit, source }) => {
} }
// Dedupe artists, only shows the first 3 // Dedupe artists, only shows the first 3
const seen = new Set() const seen = new Map()
const dedupedArtists = [] const dedupedArtists = []
let limitedShow = false let limitedShow = false
for (const artist of artists ?? []) { for (const artist of artists ?? []) {
if (!seen.has(artist.id)) { if (!seen.has(artist.id)) {
seen.add(artist.id)
if (dedupedArtists.length < limit) { if (dedupedArtists.length < limit) {
dedupedArtists.push(artist) seen.set(artist.id, dedupedArtists.length)
dedupedArtists.push({
...artist,
subroles: artist.subRole ? [artist.subRole] : [],
})
} else { } else {
limitedShow = true limitedShow = true
break }
} else {
const position = seen.get(artist.id)
if (position !== -1) {
const existing = dedupedArtists[position]
if (artist.subRole && !existing.subroles.includes(artist.subRole)) {
existing.subroles.push(artist.subRole)
}
} }
} }
} }
+22 -9
View File
@@ -36,6 +36,9 @@ const useStyles = makeStyles({
tableCell: { tableCell: {
width: '17.5%', width: '17.5%',
}, },
value: {
whiteSpace: 'pre-line',
},
}) })
export const SongInfo = (props) => { export const SongInfo = (props) => {
@@ -111,8 +114,6 @@ export const SongInfo = (props) => {
return ( return (
<TableContainer> <TableContainer>
<Table aria-label="song details" size="small">
<TableBody>
{record.rawTags && ( {record.rawTags && (
<Tabs value={tab} onChange={(_, value) => setTab(value)}> <Tabs value={tab} onChange={(_, value) => setTab(value)}>
<Tab <Tab
@@ -128,10 +129,12 @@ export const SongInfo = (props) => {
</Tabs> </Tabs>
)} )}
<div <div
hidden={tab === 1} hidden={tab == 1}
id="mapped-tags-body" id="mapped-tags-body"
aria-labelledby={record.rawTags ? 'mapped-tags-tab' : undefined} aria-labelledby={record.rawTags ? 'mapped-tags-tab' : undefined}
> >
<Table aria-label="song details" size="small">
<TableBody>
{Object.keys(data).map((key) => { {Object.keys(data).map((key) => {
return ( return (
<TableRow key={`${record.id}-${key}`}> <TableRow key={`${record.id}-${key}`}>
@@ -141,7 +144,9 @@ export const SongInfo = (props) => {
})} })}
: :
</TableCell> </TableCell>
<TableCell align="left">{data[key]}</TableCell> <TableCell align="left" className={classes.value}>
{data[key]}
</TableCell>
</TableRow> </TableRow>
) )
})} })}
@@ -152,7 +157,7 @@ export const SongInfo = (props) => {
scope="row" scope="row"
className={classes.tableCell} className={classes.tableCell}
></TableCell> ></TableCell>
<TableCell align="left"> <TableCell align="left" className={classes.value}>
<h4>{translate(`resources.song.fields.tags`)}</h4> <h4>{translate(`resources.song.fields.tags`)}</h4>
</TableCell> </TableCell>
</TableRow> </TableRow>
@@ -162,9 +167,13 @@ export const SongInfo = (props) => {
<TableCell scope="row" className={classes.tableCell}> <TableCell scope="row" className={classes.tableCell}>
{name}: {name}:
</TableCell> </TableCell>
<TableCell align="left">{values.join(' • ')}</TableCell> <TableCell align="left" className={classes.value}>
{values.join(' • ')}
</TableCell>
</TableRow> </TableRow>
))} ))}
</TableBody>
</Table>
</div> </div>
{record.rawTags && ( {record.rawTags && (
<div <div
@@ -172,6 +181,8 @@ export const SongInfo = (props) => {
id="raw-tags-body" id="raw-tags-body"
aria-labelledby="raw-tags-tab" aria-labelledby="raw-tags-tab"
> >
<Table size="small" aria-label="song raw tags">
<TableBody>
<TableRow key={`${record.id}-raw-path`}> <TableRow key={`${record.id}-raw-path`}>
<TableCell scope="row" className={classes.tableCell}> <TableCell scope="row" className={classes.tableCell}>
{translate(`resources.song.fields.path`)}: {translate(`resources.song.fields.path`)}:
@@ -183,13 +194,15 @@ export const SongInfo = (props) => {
<TableCell scope="row" className={classes.tableCell}> <TableCell scope="row" className={classes.tableCell}>
{key}: {key}:
</TableCell> </TableCell>
<TableCell align="left">{value.join(' • ')}</TableCell> <TableCell align="left" className={classes.value}>
{value.join(' • ')}
</TableCell>
</TableRow> </TableRow>
))} ))}
</div>
)}
</TableBody> </TableBody>
</Table> </Table>
</div>
)}
</TableContainer> </TableContainer>
) )
} }