diff --git a/ui/src/album/AlbumDetails.js b/ui/src/album/AlbumDetails.js new file mode 100644 index 000000000..93311e0f0 --- /dev/null +++ b/ui/src/album/AlbumDetails.js @@ -0,0 +1,46 @@ +import { Loading, useGetOne } from 'react-admin' +import { Card, CardContent, CardMedia, Typography } from '@material-ui/core' +import { subsonicUrl } from '../subsonic' +import React from 'react' + +export const AlbumDetails = ({ id, children, classes }) => { + const { data, loading, error } = useGetOne('album', id) + + if (loading) { + return + } + + if (error) { + return

ERROR: {error}

+ } + + const genreYear = (data) => { + let genreDateLine = [] + if (data.genre) { + genreDateLine.push(data.genre) + } + if (data.year) { + genreDateLine.push(data.year) + } + return genreDateLine.join(' - ') + } + + return ( + + + + + {data.name} + + + {data.albumArtist || data.artist} + + {genreYear(data)} + + {children} + + ) +} diff --git a/ui/src/album/AlbumList.js b/ui/src/album/AlbumList.js index 434f68261..7c0b35339 100644 --- a/ui/src/album/AlbumList.js +++ b/ui/src/album/AlbumList.js @@ -6,6 +6,7 @@ import { Filter, List, NumberField, + FunctionField, SearchInput, TextInput, Show, @@ -13,6 +14,7 @@ import { TextField } from 'react-admin' import { DurationField, Pagination, Title } from '../common' +import { useMediaQuery } from '@material-ui/core' const AlbumFilter = (props) => ( @@ -34,33 +36,30 @@ const AlbumDetails = (props) => { ) } -// const albumRowClick = (id, basePath, record) => { -// const filter = { album: record.name, album_id: id } -// if (!record.compilation) { -// filter.artist = record.artist -// } -// return `/song?filter=${JSON.stringify(filter)}&order=ASC&sort=trackNumber` -// } - -const AlbumList = (props) => ( - } - sort={{ field: 'name', order: 'ASC' }} - exporter={false} - bulkActionButtons={false} - filters={} - perPage={15} - pagination={} - > - } rowClick={'show'}> - - - - - - - -) - +const AlbumList = (props) => { + const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md')) + return ( + } + sort={{ field: 'name', order: 'ASC' }} + exporter={false} + bulkActionButtons={false} + filters={} + perPage={15} + pagination={} + > + } rowClick={'show'}> + + (r.albumArtist ? r.albumArtist : r.artist)} + /> + {isDesktop && } + + {isDesktop && } + + + ) +} export default AlbumList diff --git a/ui/src/album/AlbumShow.js b/ui/src/album/AlbumShow.js index 112f8b3a8..f2a49de94 100644 --- a/ui/src/album/AlbumShow.js +++ b/ui/src/album/AlbumShow.js @@ -1,91 +1,42 @@ import React from 'react' -import { Show, SimpleList, useGetList, useGetOne, Loading } from 'react-admin' -import { PlayButton, Title } from '../common' -import { addTrack } from '../player' -import { DurationField } from '../common' -import AddIcon from '@material-ui/icons/Add' -import { Typography, Paper } from '@material-ui/core' +import { Show } from 'react-admin' +import { Title } from '../common' import { makeStyles } from '@material-ui/core/styles' +import { AlbumSongList } from './AlbumSongList' +import { AlbumDetails } from './AlbumDetails' const AlbumTitle = ({ record }) => { - return + return <Title subTitle={record ? record.name : ''} /> } const useStyles = makeStyles({ - container: { minWidth: '35em', padding: '1em' }, + container: { minWidth: '24em', padding: '1em' }, rightAlignedCell: { textAlign: 'right' }, - boldCell: { fontWeight: 'bold' } + boldCell: { fontWeight: 'bold' }, + albumCover: { + display: 'inline-block', + height: '8em', + width: '8em' + }, + albumDetails: { + display: 'inline-block', + verticalAlign: 'top', + width: '14em' + }, + albumTitle: { + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis' + } }) -const AlbumDetail = (props) => { - const classes = useStyles() - const { data, loading, error } = useGetOne('album', props.id) - - if (loading) { - return <Loading /> - } - - if (error) { - return <p>ERROR: {error}</p> - } - - let genreDate = [] - if (data.genre) { - genreDate.push(data.genre) - } - if (data.year) { - genreDate.push(data.year) - } - - return ( - <Paper className={classes.container} elevation={2}> - <Typography variant="h5">{data.name}</Typography> - <Typography variant="h6">{data.albumArtist || data.artist}</Typography> - <Typography variant="h7">{genreDate.join(' - ')}</Typography> - <Typography variant="body2"></Typography> - </Paper> - ) -} - -const AlbumSongs = (props) => { - 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 <p>ERROR: {error}</p> - } - return ( - <SimpleList - data={data} - ids={Object.keys(data)} - loading={loading} - total={total} - primaryText={(r) => ( - <> - <PlayButton record={r} /> - <PlayButton record={r} action={addTrack} icon={<AddIcon />} /> - {r.trackNumber + '. ' + r.title} - </> - )} - secondaryText={(r) => - r.albumArtist && r.artist !== r.albumArtist ? r.artist : '' - } - tertiaryText={(r) => <DurationField record={r} source={'duration'} />} - linkType={false} - /> - ) -} - const AlbumShow = (props) => { + const classes = useStyles() return ( <> - <AlbumDetail {...props} /> + <AlbumDetails classes={classes} {...props} /> <Show title={<AlbumTitle />} {...props}> - <AlbumSongs {...props} /> + <AlbumSongList {...props} /> </Show> </> ) diff --git a/ui/src/album/AlbumSongList.js b/ui/src/album/AlbumSongList.js new file mode 100644 index 000000000..2df948a1d --- /dev/null +++ b/ui/src/album/AlbumSongList.js @@ -0,0 +1,45 @@ +import React from 'react' +import { SimpleList, useGetList } from 'react-admin' +import { DurationField, PlayButton } from '../common' +import { addTrack } from '../player' +import AddIcon from '@material-ui/icons/Add' + +export const AlbumSongList = (props) => { + 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 <p>ERROR: {error}</p> + } + const trackName = (r) => { + const name = r.title + if (r.trackNumber) { + return r.trackNumber + '. ' + name + } + return name + } + return ( + <SimpleList + data={data} + ids={Object.keys(data)} + loading={loading} + total={total} + primaryText={(r) => ( + <> + <PlayButton record={r} /> + <PlayButton record={r} action={addTrack} icon={<AddIcon />} /> + {trackName(r)} + </> + )} + secondaryText={(r) => + r.albumArtist && r.artist !== r.albumArtist ? r.artist : '' + } + tertiaryText={(r) => <DurationField record={r} source={'duration'} />} + linkType={false} + /> + ) +} diff --git a/ui/src/common/Title.js b/ui/src/common/Title.js index 2cf9a83d9..07c9f7645 100644 --- a/ui/src/common/Title.js +++ b/ui/src/common/Title.js @@ -1,7 +1,13 @@ import React from 'react' +import { useMediaQuery } from '@material-ui/core' const Title = ({ subTitle }) => { - return <span>Navidrome {subTitle ? ` - ${subTitle}` : ''}</span> + const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md')) + + if (isDesktop) { + return <span>Navidrome {subTitle ? ` - ${subTitle}` : ''}</span> + } + return <span>{subTitle ? subTitle : 'Navidrome'}</span> } export default Title diff --git a/ui/src/player/queue.js b/ui/src/player/queue.js index d4dc882d7..f12f00bf2 100644 --- a/ui/src/player/queue.js +++ b/ui/src/player/queue.js @@ -1,22 +1,11 @@ import 'react-jinke-music-player/assets/index.css' +import { subsonicUrl } from '../subsonic' const PLAYER_ADD_TRACK = 'PLAYER_ADD_TRACK' const PLAYER_SET_TRACK = 'PLAYER_SET_TRACK' const PLAYER_SYNC_QUEUE = 'PLAYER_SYNC_QUEUE' const PLAYER_SCROBBLE = 'PLAYER_SCROBBLE' -const subsonicUrl = (command, id, options) => { - const username = localStorage.getItem('username') - const token = localStorage.getItem('subsonic-token') - const salt = localStorage.getItem('subsonic-salt') - const timeStamp = new Date().getTime() - const url = `rest/${command}?u=${username}&f=json&v=1.8.0&c=NavidromeUI&t=${token}&s=${salt}&id=${id}&_=${timeStamp}` - if (options) { - return url + '&' + options - } - return url -} - const mapToAudioLists = (item) => ({ id: item.id, name: item.title, diff --git a/ui/src/subsonic/index.js b/ui/src/subsonic/index.js new file mode 100644 index 000000000..3c5a4248c --- /dev/null +++ b/ui/src/subsonic/index.js @@ -0,0 +1,13 @@ +const subsonicUrl = (command, id, options) => { + const username = localStorage.getItem('username') + const token = localStorage.getItem('subsonic-token') + const salt = localStorage.getItem('subsonic-salt') + const timeStamp = new Date().getTime() + const url = `rest/${command}?u=${username}&f=json&v=1.8.0&c=NavidromeUI&t=${token}&s=${salt}&id=${id}&_=${timeStamp}` + if (options) { + return url + '&' + options + } + return url +} + +export { subsonicUrl }