diff --git a/ui/src/actions/dialogs.js b/ui/src/actions/dialogs.js index dea4d6203..d637f7bff 100644 --- a/ui/src/actions/dialogs.js +++ b/ui/src/actions/dialogs.js @@ -29,9 +29,10 @@ export const closeShareMenu = () => ({ type: SHARE_MENU_CLOSE, }) -export const openAddToPlaylist = ({ selectedIds, onSuccess }) => ({ +export const openAddToPlaylist = ({ selectedIds, albumIds, onSuccess }) => ({ type: ADD_TO_PLAYLIST_OPEN, selectedIds, + albumIds, onSuccess, }) diff --git a/ui/src/album/AlbumActions.jsx b/ui/src/album/AlbumActions.jsx index 96cfab09a..b62fa12fc 100644 --- a/ui/src/album/AlbumActions.jsx +++ b/ui/src/album/AlbumActions.jsx @@ -5,6 +5,8 @@ import { Button, sanitizeListRestProps, TopToolbar, + useDataProvider, + useNotify, useRecordContext, useTranslate, } from 'react-admin' @@ -19,7 +21,6 @@ import { playNext, addTracks, playTracks, - shuffleTracks, openAddToPlaylist, openDownloadMenu, DOWNLOAD_MENU_ALBUM, @@ -52,30 +53,76 @@ const AlbumActions = ({ }) => { const dispatch = useDispatch() const translate = useTranslate() + const dataProvider = useDataProvider() + const notify = useNotify() const classes = useStyles() const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md')) const isNotSmall = useMediaQuery((theme) => theme.breakpoints.up('sm')) + const getAllSongsAndDispatch = React.useCallback( + (action) => { + if (ids && ids.length === record.songCount) { + return dispatch(action(data, ids)) + } + dataProvider + .getList('song', { + pagination: { page: 1, perPage: 0 }, + sort: { field: 'album', order: 'ASC' }, + filter: { album_id: record.id }, + }) + .then((res) => { + const allData = res.data.reduce( + (acc, curr) => ({ ...acc, [curr.id]: curr }), + {}, + ) + const allIds = res.data.map((s) => s.id) + dispatch(action(allData, allIds)) + }) + .catch(() => { + notify('ra.page.error', 'warning') + }) + }, + [dataProvider, dispatch, record, data, ids, notify], + ) + const handlePlay = React.useCallback(() => { - dispatch(playTracks(data, ids)) - }, [dispatch, data, ids]) + getAllSongsAndDispatch(playTracks) + }, [getAllSongsAndDispatch]) const handlePlayNext = React.useCallback(() => { - dispatch(playNext(data, ids)) - }, [dispatch, data, ids]) + getAllSongsAndDispatch(playNext) + }, [getAllSongsAndDispatch]) const handlePlayLater = React.useCallback(() => { - dispatch(addTracks(data, ids)) - }, [dispatch, data, ids]) + getAllSongsAndDispatch(addTracks) + }, [getAllSongsAndDispatch]) const handleShuffle = React.useCallback(() => { - dispatch(shuffleTracks(data, ids)) - }, [dispatch, data, ids]) + const filter = { album_id: record.id } + if (config.skipLowRatingInShuffle) { + filter.not_disliked = true + } + dataProvider + .getList('song', { + pagination: { page: 1, perPage: 500 }, + sort: { field: 'random', order: 'ASC' }, + filter, + }) + .then((res) => { + const allData = res.data.reduce( + (acc, curr) => ({ ...acc, [curr.id]: curr }), + {}, + ) + dispatch(playTracks(allData)) + }) + .catch(() => { + notify('ra.page.error', 'warning') + }) + }, [dataProvider, dispatch, record, notify]) const handleAddToPlaylist = React.useCallback(() => { - const selectedIds = ids.filter((id) => !data[id].missing) - dispatch(openAddToPlaylist({ selectedIds })) - }, [dispatch, data, ids]) + dispatch(openAddToPlaylist({ albumIds: [record.id] })) + }, [dataProvider, dispatch, record, data, ids, notify]) const handleShare = React.useCallback(() => { dispatch(openShareMenu([record.id], 'album', record.name)) diff --git a/ui/src/album/AlbumShow.jsx b/ui/src/album/AlbumShow.jsx index c9e944999..68dee861c 100644 --- a/ui/src/album/AlbumShow.jsx +++ b/ui/src/album/AlbumShow.jsx @@ -4,6 +4,7 @@ import { ShowContextProvider, useShowContext, useShowController, + Pagination, Title as RaTitle, } from 'react-admin' import { makeStyles } from '@material-ui/core/styles' @@ -40,8 +41,7 @@ const AlbumShowLayout = (props) => { reference="song" target="album_id" sort={{ field: 'album', order: 'ASC' }} - perPage={0} - pagination={null} + perPage={50} > { actions={ } + pagination={} /> )} diff --git a/ui/src/album/AlbumSongs.jsx b/ui/src/album/AlbumSongs.jsx index 8a7fd2ae4..4c942035a 100644 --- a/ui/src/album/AlbumSongs.jsx +++ b/ui/src/album/AlbumSongs.jsx @@ -89,6 +89,7 @@ const useStyles = makeStyles( const AlbumSongs = (props) => { const { data, ids } = props + const listContext = useListContext(props) const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md')) const classes = useStyles({ isDesktop }) const dispatch = useDispatch() @@ -210,6 +211,8 @@ const AlbumSongs = (props) => { } /> + {props.pagination && + React.cloneElement(props.pagination, listContext)} ) } @@ -217,7 +220,17 @@ const AlbumSongs = (props) => { const SanitizedAlbumSongs = (props) => { removeAlbumCommentsFromSongs(props) const { loaded, loading, total, ...rest } = useListContext(props) - return <>{loaded && } + return ( + <> + {loaded && ( + + )} + + ) } export default SanitizedAlbumSongs diff --git a/ui/src/dialogs/AddToPlaylistDialog.jsx b/ui/src/dialogs/AddToPlaylistDialog.jsx index 91521d1ce..c9c603107 100644 --- a/ui/src/dialogs/AddToPlaylistDialog.jsx +++ b/ui/src/dialogs/AddToPlaylistDialog.jsx @@ -39,7 +39,7 @@ const useStyles = makeStyles({ export const AddToPlaylistDialog = () => { const classes = useStyles() - const { open, selectedIds, onSuccess, duplicateSong, duplicateIds } = + const { open, selectedIds, albumIds, onSuccess, duplicateSong, duplicateIds } = useSelector((state) => state.addToPlaylistDialog) const dispatch = useDispatch() const translate = useTranslate() @@ -61,10 +61,11 @@ export const AddToPlaylistDialog = () => { const addToPlaylist = (playlistId, distinctIds) => { const trackIds = Array.isArray(distinctIds) ? distinctIds : selectedIds - if (trackIds.length) { + const data = albumIds?.length ? { albumIds } : { ids: trackIds } + if (albumIds?.length || trackIds.length) { dataProvider .create('playlistTrack', { - data: { ids: trackIds }, + data, filter: { playlist_id: playlistId }, }) .then(() => { diff --git a/ui/src/reducers/dialogReducer.js b/ui/src/reducers/dialogReducer.js index e1a77f100..a9924dfca 100644 --- a/ui/src/reducers/dialogReducer.js +++ b/ui/src/reducers/dialogReducer.js @@ -63,6 +63,7 @@ export const addToPlaylistDialogReducer = ( ...previousState, open: true, selectedIds: payload.selectedIds, + albumIds: payload.albumIds, onSuccess: payload.onSuccess, } case ADD_TO_PLAYLIST_CLOSE: