diff --git a/ui/src/album/AlbumActions.js b/ui/src/album/AlbumActions.js
new file mode 100644
index 000000000..518ff19f2
--- /dev/null
+++ b/ui/src/album/AlbumActions.js
@@ -0,0 +1,64 @@
+import {
+ Button,
+ sanitizeListRestProps,
+ TopToolbar,
+ useTranslate
+} from 'react-admin'
+import PlayArrowIcon from '@material-ui/icons/PlayArrow'
+import ShuffleIcon from '@material-ui/icons/Shuffle'
+import React from 'react'
+import { useDispatch } from 'react-redux'
+import { playAlbum } from '../player'
+
+export const AlbumActions = ({
+ className,
+ ids,
+ data,
+ exporter,
+ permanentFilter,
+ ...rest
+}) => {
+ const dispatch = useDispatch()
+ const translation = useTranslate()
+
+ const shuffle = (data) => {
+ const ids = Object.keys(data)
+ for (let i = ids.length - 1; i > 0; i--) {
+ let j = Math.floor(Math.random() * (i + 1))
+ ;[ids[i], ids[j]] = [ids[j], ids[i]]
+ }
+ const shuffled = {}
+ ids.forEach((id) => (shuffled[id] = data[id]))
+ return shuffled
+ }
+
+ return (
+
+
+
+
+ )
+}
+
+AlbumActions.defaultProps = {
+ selectedIds: [],
+ onUnselectItems: () => null
+}
diff --git a/ui/src/album/AlbumDetails.js b/ui/src/album/AlbumDetails.js
index 7b6b00164..7473f07a6 100644
--- a/ui/src/album/AlbumDetails.js
+++ b/ui/src/album/AlbumDetails.js
@@ -1,26 +1,15 @@
import React from 'react'
-import { Loading, useGetOne } from 'react-admin'
import { Card, CardContent, CardMedia, Typography } from '@material-ui/core'
import { subsonicUrl } from '../subsonic'
-const AlbumDetails = ({ id, classes }) => {
- const { data, loading, error } = useGetOne('album', id)
-
- if (loading) {
- return
- }
-
- if (error) {
- return
ERROR: {error}
- }
-
- const genreYear = (data) => {
+const AlbumDetails = ({ classes, record }) => {
+ const genreYear = (record) => {
let genreDateLine = []
- if (data.genre) {
- genreDateLine.push(data.genre)
+ if (record.genre) {
+ genreDateLine.push(record.genre)
}
- if (data.year) {
- genreDateLine.push(data.year)
+ if (record.year) {
+ genreDateLine.push(record.year)
}
return genreDateLine.join(' - ')
}
@@ -30,19 +19,19 @@ const AlbumDetails = ({ id, classes }) => {
- {data.name}
+ {record.name}
- {data.albumArtist || data.artist}
+ {record.albumArtist || record.artist}
- {genreYear(data)}
+ {genreYear(record)}
)
diff --git a/ui/src/album/AlbumShow.js b/ui/src/album/AlbumShow.js
index 5485a76af..095158e80 100644
--- a/ui/src/album/AlbumShow.js
+++ b/ui/src/album/AlbumShow.js
@@ -1,68 +1,75 @@
import React from 'react'
-import { Show } from 'react-admin'
-import { Title } from '../common'
-import { makeStyles } from '@material-ui/core/styles'
-import AlbumSongList from './AlbumSongList'
+import {
+ Datagrid,
+ FunctionField,
+ List,
+ Loading,
+ TextField,
+ useGetOne
+} from 'react-admin'
import AlbumDetails from './AlbumDetails'
-
-const AlbumTitle = ({ record }) => {
- return
-}
-
-const useStyles = makeStyles((theme) => ({
- container: {
- [theme.breakpoints.down('xs')]: {
- padding: '0.7em',
- minWidth: '24em'
- },
- [theme.breakpoints.up('sm')]: {
- padding: '1em',
- minWidth: '32em'
- }
- },
- albumCover: {
- display: 'inline-block',
- [theme.breakpoints.down('xs')]: {
- height: '8em',
- width: '8em'
- },
- [theme.breakpoints.up('sm')]: {
- height: '15em',
- width: '15em'
- },
- [theme.breakpoints.up('lg')]: {
- height: '20em',
- width: '20em'
- }
- },
- albumDetails: {
- display: 'inline-block',
- verticalAlign: 'top',
- [theme.breakpoints.down('xs')]: {
- width: '14em'
- },
- [theme.breakpoints.up('sm')]: {
- width: '26em'
- },
- [theme.breakpoints.up('lg')]: {
- width: '38em'
- }
- },
- albumTitle: {
- whiteSpace: 'nowrap',
- overflow: 'hidden',
- textOverflow: 'ellipsis'
- }
-}))
+import { DurationField, Title } from '../common'
+import { useStyles } from './styles'
+import { SongBulkActions } from '../song/SongBulkActions'
+import { AlbumActions } from './AlbumActions'
+import { useMediaQuery } from '@material-ui/core'
+import { setTrack } from '../player'
+import { useDispatch } from 'react-redux'
const AlbumShow = (props) => {
+ const dispatch = useDispatch()
+ const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md'))
const classes = useStyles()
+ const { data: record, loading, error } = useGetOne('album', props.id)
+
+ if (loading) {
+ return
+ }
+
+ if (error) {
+ return ERROR: {error}
+ }
+
+ const trackName = (r) => {
+ const name = r.title
+ if (r.trackNumber) {
+ return r.trackNumber.toString().padStart(2, '0') + ' ' + name
+ }
+ return name
+ }
+
return (
<>
-
- } {...props}>
-
-
+
+
}
+ actions={}
+ filter={{ album_id: props.id }}
+ resource={'song'}
+ exporter={false}
+ basePath={'/song'}
+ perPage={1000}
+ pagination={null}
+ sort={{ field: 'discNumber asc, trackNumber asc', order: 'ASC' }}
+ bulkActionButtons={}
+ >
+ dispatch(setTrack(record))}
+ >
+ {isDesktop && (
+
+ )}
+ {isDesktop && }
+ {!isDesktop && }
+ {record.compilation && }
+
+
+
>
)
}
diff --git a/ui/src/album/AlbumSongList.js b/ui/src/album/AlbumSongList.js
deleted file mode 100644
index f227f5351..000000000
--- a/ui/src/album/AlbumSongList.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import React from 'react'
-import { useGetList } from 'react-admin'
-import { DurationField, PlayButton, SimpleList } from '../common'
-import { addTrack } from '../player'
-import AddIcon from '@material-ui/icons/Add'
-import { useDispatch } from 'react-redux'
-import { playAlbum } from '../player/queue'
-
-const AlbumSongList = (props) => {
- const dispatch = useDispatch()
- const { record } = props
-
- const { data, total, loading, error } = useGetList(
- 'song',
- { page: 0, perPage: 100 },
- { field: 'album', order: 'ASC' },
- { album_id: record.id }
- )
-
- if (error) {
- return ERROR: {error}
- }
-
- const trackName = (r) => {
- const name = r.title
- if (r.trackNumber) {
- return r.trackNumber.toString().padStart(2, '0') + ' ' + name
- }
- return name
- }
-
- return (
- (
- <>
-
- } />
- {trackName(r)}
- >
- )}
- secondaryText={(r) =>
- r.albumArtist && r.artist !== r.albumArtist ? r.artist : ''
- }
- tertiaryText={(r) => }
- linkType={(id) => dispatch(playAlbum(id, data))}
- />
- )
-}
-
-export default AlbumSongList
diff --git a/ui/src/album/styles.js b/ui/src/album/styles.js
new file mode 100644
index 000000000..3046968a4
--- /dev/null
+++ b/ui/src/album/styles.js
@@ -0,0 +1,47 @@
+import { makeStyles } from '@material-ui/core/styles'
+
+export const useStyles = makeStyles((theme) => ({
+ container: {
+ [theme.breakpoints.down('xs')]: {
+ padding: '0.7em',
+ minWidth: '24em'
+ },
+ [theme.breakpoints.up('sm')]: {
+ padding: '1em',
+ minWidth: '32em'
+ }
+ },
+ albumCover: {
+ display: 'inline-block',
+ [theme.breakpoints.down('xs')]: {
+ height: '8em',
+ width: '8em'
+ },
+ [theme.breakpoints.up('sm')]: {
+ height: '10em',
+ width: '10em'
+ },
+ [theme.breakpoints.up('lg')]: {
+ height: '15em',
+ width: '15em'
+ }
+ },
+ albumDetails: {
+ display: 'inline-block',
+ verticalAlign: 'top',
+ [theme.breakpoints.down('xs')]: {
+ width: '14em'
+ },
+ [theme.breakpoints.up('sm')]: {
+ width: '26em'
+ },
+ [theme.breakpoints.up('lg')]: {
+ width: '38em'
+ }
+ },
+ albumTitle: {
+ whiteSpace: 'nowrap',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis'
+ }
+}))
diff --git a/ui/src/i18n/en.js b/ui/src/i18n/en.js
index e67b12e34..4d85b9a35 100644
--- a/ui/src/i18n/en.js
+++ b/ui/src/i18n/en.js
@@ -17,6 +17,12 @@ export default deepmerge(englishMessages, {
fields: {
albumArtist: 'Album Artist',
duration: 'Time'
+ },
+ actions: {
+ playAll: 'Play',
+ playNext: 'Play Next',
+ addToQueue: 'Play Later',
+ shuffle: 'Shuffle'
}
}
},
diff --git a/ui/src/player/index.js b/ui/src/player/index.js
index 38bef76d5..754843c37 100644
--- a/ui/src/player/index.js
+++ b/ui/src/player/index.js
@@ -1,4 +1,4 @@
import Player from './Player'
-import { addTrack, setTrack, playQueueReducer } from './queue'
+import { addTrack, setTrack, playQueueReducer, playAlbum } from './queue'
-export { Player, addTrack, setTrack, playQueueReducer }
+export { Player, addTrack, setTrack, playAlbum, playQueueReducer }
diff --git a/ui/src/song/AddToQueueButton.js b/ui/src/song/AddToQueueButton.js
index 6574b75e9..2ee88cb7c 100644
--- a/ui/src/song/AddToQueueButton.js
+++ b/ui/src/song/AddToQueueButton.js
@@ -2,15 +2,13 @@ import React from 'react'
import {
Button,
useDataProvider,
- useUnselectAll,
- useTranslate
+ useTranslate,
+ useUnselectAll
} from 'react-admin'
import { useDispatch } from 'react-redux'
import { addTrack } from '../player'
import AddToQueueIcon from '@material-ui/icons/AddToQueue'
-import Tooltip from '@material-ui/core/Tooltip'
-
const AddToQueueButton = ({ selectedIds }) => {
const dispatch = useDispatch()
const translate = useTranslate()
@@ -26,13 +24,12 @@ const AddToQueueButton = ({ selectedIds }) => {
}
return (
-