feat: optimized for small screens (only)

This commit is contained in:
Deluan 2020-02-07 12:36:26 -05:00
parent 81e1a7088f
commit 2ca98d8e81
7 changed files with 165 additions and 116 deletions

View File

@ -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 <Loading />
}
if (error) {
return <p>ERROR: {error}</p>
}
const genreYear = (data) => {
let genreDateLine = []
if (data.genre) {
genreDateLine.push(data.genre)
}
if (data.year) {
genreDateLine.push(data.year)
}
return genreDateLine.join(' - ')
}
return (
<Card className={classes.container}>
<CardMedia
image={subsonicUrl('getCoverArt', data.coverArtId || 'not_found')}
className={classes.albumCover}
/>
<CardContent className={classes.albumDetails}>
<Typography variant="h5" className={classes.albumTitle}>
{data.name}
</Typography>
<Typography component="h6">
{data.albumArtist || data.artist}
</Typography>
<Typography variant="h7">{genreYear(data)}</Typography>
</CardContent>
{children}
</Card>
)
}

View File

@ -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) => (
<Filter {...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) => (
<List
{...props}
title={<Title subTitle={'Albums'} />}
sort={{ field: 'name', order: 'ASC' }}
exporter={false}
bulkActionButtons={false}
filters={<AlbumFilter />}
perPage={15}
pagination={<Pagination />}
>
<Datagrid expand={<AlbumDetails />} rowClick={'show'}>
<TextField source="name" />
<TextField source="artist" />
<NumberField source="songCount" />
<TextField source="year" />
<DurationField source="duration" />
</Datagrid>
</List>
)
const AlbumList = (props) => {
const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md'))
return (
<List
{...props}
title={<Title subTitle={'Albums'} />}
sort={{ field: 'name', order: 'ASC' }}
exporter={false}
bulkActionButtons={false}
filters={<AlbumFilter />}
perPage={15}
pagination={<Pagination />}
>
<Datagrid expand={<AlbumDetails />} rowClick={'show'}>
<TextField source="name" />
<FunctionField
source="artist"
render={(r) => (r.albumArtist ? r.albumArtist : r.artist)}
/>
{isDesktop && <NumberField source="songCount" />}
<TextField source="year" />
{isDesktop && <DurationField source="duration" />}
</Datagrid>
</List>
)
}
export default AlbumList

View File

@ -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 <Title subTitle={`Album: ${record ? record.name : ''}`} />
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>
</>
)

View File

@ -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}
/>
)
}

View File

@ -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

View File

@ -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,

13
ui/src/subsonic/index.js Normal file
View File

@ -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 }