quintodrome/ui/src/artist/ArtistActions.jsx
Deluan Quintão a65140b965 feat(ui): add Play Artist's Top Songs button (#4204)
* ui: add Play button to artist toolbar

* refactor

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

* test(ui): add tests for Play button functionality in ArtistActions

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

* ui: update Play button label to Top Songs in ArtistActions

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

---------

Signed-off-by: Deluan <deluan@navidrome.org>
2025-06-09 19:09:04 -04:00

128 lines
3.4 KiB
JavaScript

import React from 'react'
import PropTypes from 'prop-types'
import { useDispatch } from 'react-redux'
import { useMediaQuery } from '@material-ui/core'
import { makeStyles } from '@material-ui/core/styles'
import {
Button,
TopToolbar,
sanitizeListRestProps,
useDataProvider,
useNotify,
useTranslate,
} from 'react-admin'
import ShuffleIcon from '@material-ui/icons/Shuffle'
import PlayArrowIcon from '@material-ui/icons/PlayArrow'
import { IoIosRadio } from 'react-icons/io'
import { playShuffle, playSimilar, playTopSongs } from './actions.js'
const useStyles = makeStyles((theme) => ({
toolbar: {
minHeight: 'auto',
padding: '0 !important',
background: 'transparent',
boxShadow: 'none',
'& .MuiToolbar-root': {
minHeight: 'auto',
padding: '0 !important',
background: 'transparent',
},
},
button: {
[theme.breakpoints.down('xs')]: {
minWidth: 'auto',
padding: '8px 12px',
fontSize: '0.75rem',
'& .MuiButton-startIcon': {
marginRight: '4px',
},
},
},
radioIcon: {
[theme.breakpoints.down('xs')]: {
fontSize: '1.5rem',
},
},
}))
const ArtistActions = ({ className, record, ...rest }) => {
const dispatch = useDispatch()
const translate = useTranslate()
const dataProvider = useDataProvider()
const notify = useNotify()
const classes = useStyles()
const isMobile = useMediaQuery((theme) => theme.breakpoints.down('xs'))
const handlePlay = React.useCallback(async () => {
try {
await playTopSongs(dispatch, notify, record.name)
} catch (e) {
// eslint-disable-next-line no-console
console.error('Error fetching top songs for artist:', e)
notify('ra.page.error', 'warning')
}
}, [dispatch, notify, record])
const handleShuffle = React.useCallback(async () => {
try {
await playShuffle(dataProvider, dispatch, record.id)
} catch (e) {
// eslint-disable-next-line no-console
console.error('Error fetching songs for shuffle:', e)
notify('ra.page.error', 'warning')
}
}, [dataProvider, dispatch, record, notify])
const handleRadio = React.useCallback(async () => {
try {
await playSimilar(dispatch, notify, record.id)
} catch (e) {
// eslint-disable-next-line no-console
console.error('Error starting radio for artist:', e)
notify('ra.page.error', 'warning')
}
}, [dispatch, notify, record])
return (
<TopToolbar
className={`${className} ${classes.toolbar}`}
{...sanitizeListRestProps(rest)}
>
<Button
onClick={handlePlay}
label={translate('resources.artist.actions.topSongs')}
className={classes.button}
size={isMobile ? 'small' : 'medium'}
>
<PlayArrowIcon />
</Button>
<Button
onClick={handleShuffle}
label={translate('resources.artist.actions.shuffle')}
className={classes.button}
size={isMobile ? 'small' : 'medium'}
>
<ShuffleIcon />
</Button>
<Button
onClick={handleRadio}
label={translate('resources.artist.actions.radio')}
className={classes.button}
size={isMobile ? 'small' : 'medium'}
>
<IoIosRadio className={classes.radioIcon} />
</Button>
</TopToolbar>
)
}
ArtistActions.propTypes = {
className: PropTypes.string,
record: PropTypes.object.isRequired,
}
ArtistActions.defaultProps = {
className: '',
}
export default ArtistActions