diff --git a/ui/src/album/AlbumDetails.js b/ui/src/album/AlbumDetails.js index ffce4e7bd..64775e7a6 100644 --- a/ui/src/album/AlbumDetails.js +++ b/ui/src/album/AlbumDetails.js @@ -8,7 +8,13 @@ import { Typography, useMediaQuery, } from '@material-ui/core' -import { useTranslate } from 'react-admin' +import { + useRecordContext, + useTranslate, + ArrayField, + SingleFieldList, + ChipField, +} from 'react-admin' import clsx from 'clsx' import Lightbox from 'react-image-lightbox' import 'react-image-lightbox/style.css' @@ -22,6 +28,7 @@ import { RatingField, } from '../common' import config from '../config' +import { intersperse } from '../utils' const useStyles = makeStyles( (theme) => ({ @@ -85,6 +92,9 @@ const useStyles = makeStyles( recordName: {}, recordArtist: {}, recordMeta: {}, + genreList: { + marginTop: theme.spacing(1), + }, }), { name: 'NDAlbumDetails', @@ -126,23 +136,49 @@ const AlbumComment = ({ record }) => { ) } -const AlbumDetails = ({ record }) => { +const GenreList = () => { + const classes = useStyles() + return ( + + + + + + ) +} + +const Details = (props) => { + const isXsmall = useMediaQuery((theme) => theme.breakpoints.down('xs')) + const translate = useTranslate() + const record = useRecordContext(props) + let details = [] + const addDetail = (obj) => { + const id = details.length + details.push({obj}) + } + + const year = formatRange(record, 'year') + year && addDetail(<>{year}) + addDetail( + <> + {record.songCount + + ' ' + + translate('resources.song.name', { + smart_count: record.songCount, + })} + + ) + !isXsmall && addDetail() + !isXsmall && addDetail() + + return <>{intersperse(details, ' · ')} +} + +const AlbumDetails = (props) => { + const record = useRecordContext(props) const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('lg')) const classes = useStyles() const [isLightboxOpen, setLightboxOpen] = React.useState(false) - const translate = useTranslate() - - const genreYear = (record) => { - let genreDateLine = [] - if (record.genre) { - genreDateLine.push(record.genre) - } - const year = formatRange(record, 'year') - if (year) { - genreDateLine.push(year) - } - return genreDateLine.join(' · ') - } const imageUrl = subsonic.getCoverArtUrl(record, 300) const fullImageUrl = subsonic.getCoverArtUrl(record) @@ -188,16 +224,7 @@ const AlbumDetails = ({ record }) => { - {genreYear(record)} - - - {record.songCount}{' '} - {translate('resources.song.name', { - smart_count: record.songCount, - })} - {' · '} {' '} - {' · '} - +
{config.enableStarRating && (
@@ -208,6 +235,7 @@ const AlbumDetails = ({ record }) => { />
)} + {isDesktop && record['comment'] && } diff --git a/ui/src/album/AlbumTableView.js b/ui/src/album/AlbumTableView.js index f7446f615..7e346f0ec 100644 --- a/ui/src/album/AlbumTableView.js +++ b/ui/src/album/AlbumTableView.js @@ -1,5 +1,4 @@ import React, { useMemo } from 'react' -import Paper from '@material-ui/core/Paper' import Table from '@material-ui/core/Table' import TableBody from '@material-ui/core/TableBody' import inflection from 'inflection' @@ -7,12 +6,15 @@ import TableCell from '@material-ui/core/TableCell' import TableContainer from '@material-ui/core/TableContainer' import TableRow from '@material-ui/core/TableRow' import { + ArrayField, BooleanField, + ChipField, Datagrid, DateField, NumberField, - Show, + SingleFieldList, TextField, + useRecordContext, useTranslate, } from 'react-admin' import { useMediaQuery } from '@material-ui/core' @@ -60,43 +62,50 @@ const useStyles = makeStyles({ const AlbumDetails = (props) => { const classes = useStyles() const translate = useTranslate() - const { record } = props + const record = useRecordContext(props) const data = { - albumArtist: , - genre: , - compilation: , - updatedAt: , - comment: , - } - if (!record.comment) { - delete data.comment + albumArtist: , + genre: ( + + + + + + ), + compilation: , + updatedAt: , + comment: , } + + const optionalFields = ['comment', 'genre'] + optionalFields.forEach((field) => { + !record[field] && delete data[field] + }) + return ( - - - - - {Object.keys(data).map((key) => { - return ( - - - {translate(`resources.album.fields.${key}`, { - _: inflection.humanize(inflection.underscore(key)), - })} - : - - {data[key]} - - ) - })} - -
-
-
+ + + + {Object.keys(data).map((key) => { + return ( + + + {translate(`resources.album.fields.${key}`, { + _: inflection.humanize(inflection.underscore(key)), + })} + : + + {data[key]} + + ) + })} + +
+
) } diff --git a/ui/src/common/BitrateField.js b/ui/src/common/BitrateField.js index d6ac2fd03..8b9552ef1 100644 --- a/ui/src/common/BitrateField.js +++ b/ui/src/common/BitrateField.js @@ -1,7 +1,9 @@ import React from 'react' import PropTypes from 'prop-types' +import { useRecordContext } from 'react-admin' -export const BitrateField = ({ record = {}, source }) => { +export const BitrateField = ({ source, ...rest }) => { + const record = useRecordContext(rest) return {`${record[source]} kbps`} } diff --git a/ui/src/common/DurationField.js b/ui/src/common/DurationField.js index 0222faa88..730acdbee 100644 --- a/ui/src/common/DurationField.js +++ b/ui/src/common/DurationField.js @@ -1,8 +1,10 @@ import React from 'react' import PropTypes from 'prop-types' import { formatDuration } from '../utils' +import { useRecordContext } from 'react-admin' -export const DurationField = ({ record = {}, source }) => { +export const DurationField = ({ source, ...rest }) => { + const record = useRecordContext(rest) try { return {formatDuration(record[source])} } catch (e) { diff --git a/ui/src/common/LoveButton.js b/ui/src/common/LoveButton.js index 7e38f7bef..7a1fa0207 100644 --- a/ui/src/common/LoveButton.js +++ b/ui/src/common/LoveButton.js @@ -5,6 +5,7 @@ import FavoriteBorderIcon from '@material-ui/icons/FavoriteBorder' import IconButton from '@material-ui/core/IconButton' import { makeStyles } from '@material-ui/core/styles' import { useToggleLove } from './useToggleLove' +import { useRecordContext } from 'react-admin' const useStyles = makeStyles({ love: { @@ -16,7 +17,6 @@ const useStyles = makeStyles({ export const LoveButton = ({ resource, - record, color, visible, size, @@ -25,6 +25,7 @@ export const LoveButton = ({ disabled, ...rest }) => { + const record = useRecordContext(rest) || {} const classes = useStyles({ color, visible, loved: record.starred }) const [toggleLove, loading] = useToggleLove(resource, record) @@ -56,7 +57,7 @@ export const LoveButton = ({ LoveButton.propTypes = { resource: PropTypes.string.isRequired, - record: PropTypes.object.isRequired, + record: PropTypes.object, visible: PropTypes.bool, color: PropTypes.string, size: PropTypes.string, @@ -66,7 +67,6 @@ LoveButton.propTypes = { LoveButton.defaultProps = { addLabel: true, - record: {}, visible: true, size: 'small', color: 'inherit', diff --git a/ui/src/common/MultiLineTextField.js b/ui/src/common/MultiLineTextField.js index 1053e7f4a..5148482dd 100644 --- a/ui/src/common/MultiLineTextField.js +++ b/ui/src/common/MultiLineTextField.js @@ -2,18 +2,19 @@ import React, { memo } from 'react' import Typography from '@material-ui/core/Typography' import sanitizeFieldRestProps from './sanitizeFieldRestProps' import md5 from 'blueimp-md5' +import { useRecordContext } from 'react-admin' export const MultiLineTextField = memo( ({ className, emptyText, source, - record, firstLine, maxLines, addLabel, ...rest }) => { + const record = useRecordContext(rest) const value = record && record[source] let lines = value ? value.split('\n') : [] if (maxLines || firstLine) { @@ -46,7 +47,6 @@ export const MultiLineTextField = memo( ) MultiLineTextField.defaultProps = { - record: {}, addLabel: true, firstLine: 0, } diff --git a/ui/src/common/RangeField.js b/ui/src/common/RangeField.js index 7e9bbc930..7ea83330a 100644 --- a/ui/src/common/RangeField.js +++ b/ui/src/common/RangeField.js @@ -1,5 +1,6 @@ import React from 'react' import PropTypes from 'prop-types' +import { useRecordContext } from 'react-admin' export const formatRange = (record, source) => { const nameCapitalized = source.charAt(0).toUpperCase() + source.slice(1) @@ -15,7 +16,8 @@ export const formatRange = (record, source) => { return range.join('-') } -export const RangeField = ({ className, record = {}, source }) => { +export const RangeField = ({ className, source, ...rest }) => { + const record = useRecordContext(rest) return {formatRange(record, source)} } diff --git a/ui/src/common/RatingField.js b/ui/src/common/RatingField.js index b22c535b8..fe9171527 100644 --- a/ui/src/common/RatingField.js +++ b/ui/src/common/RatingField.js @@ -5,6 +5,7 @@ import { makeStyles } from '@material-ui/core/styles' import StarBorderIcon from '@material-ui/icons/StarBorder' import clsx from 'clsx' import { useRating } from './useRating' +import { useRecordContext } from 'react-admin' const useStyles = makeStyles({ rating: { @@ -21,14 +22,15 @@ const useStyles = makeStyles({ export const RatingField = ({ resource, - record, visible, className, size, color, + ...rest }) => { + const record = useRecordContext(rest) || {} const [rate, rating] = useRating(resource, record) - const classes = useStyles({ visible, rating: record.rating, color }) + const classes = useStyles({ color, visible }) const stopPropagation = (e) => { e.stopPropagation() @@ -60,13 +62,12 @@ export const RatingField = ({ } RatingField.propTypes = { resource: PropTypes.string.isRequired, - record: PropTypes.object.isRequired, + record: PropTypes.object, visible: PropTypes.bool, size: PropTypes.string, } RatingField.defaultProps = { - record: {}, visible: true, size: 'small', color: 'inherit', diff --git a/ui/src/common/SizeField.js b/ui/src/common/SizeField.js index c5c02c5e6..64eaaf888 100644 --- a/ui/src/common/SizeField.js +++ b/ui/src/common/SizeField.js @@ -1,8 +1,10 @@ import React from 'react' import PropTypes from 'prop-types' import { formatBytes } from '../utils' +import { useRecordContext } from 'react-admin' -export const SizeField = ({ record = {}, source }) => { +export const SizeField = ({ source, ...rest }) => { + const record = useRecordContext(rest) return {formatBytes(record[source])} } diff --git a/ui/src/common/SongDetails.js b/ui/src/common/SongDetails.js index e95cf0264..05ad06883 100644 --- a/ui/src/common/SongDetails.js +++ b/ui/src/common/SongDetails.js @@ -1,5 +1,4 @@ import React from 'react' -import Paper from '@material-ui/core/Paper' import Table from '@material-ui/core/Table' import TableBody from '@material-ui/core/TableBody' import TableCell from '@material-ui/core/TableCell' @@ -11,6 +10,10 @@ import { TextField, NumberField, useTranslate, + ArrayField, + SingleFieldList, + ChipField, + useRecordContext, } from 'react-admin' import inflection from 'inflection' import { BitrateField, SizeField } from './index' @@ -26,35 +29,38 @@ const useStyles = makeStyles({ export const SongDetails = (props) => { const classes = useStyles() const translate = useTranslate() - const { record } = props + const record = useRecordContext(props) const data = { - path: , - album: , - discSubtitle: , - albumArtist: , - genre: , - compilation: , - bitRate: , - size: , - updatedAt: , - playCount: , - bpm: , - comment: , - } - if (!record.discSubtitle) { - delete data.discSubtitle - } - if (!record.comment) { - delete data.comment - } - if (!record.bpm) { - delete data.bpm + path: , + album: , + discSubtitle: , + albumArtist: , + genre: ( + + + + + + ), + compilation: , + bitRate: , + size: , + updatedAt: , + playCount: , + bpm: , + comment: , } + + const optionalFields = ['discSubtitle', 'comment', 'bpm', 'genre'] + optionalFields.forEach((field) => { + !record[field] && delete data[field] + }) if (record.playCount > 0) { data.playDate = } + return ( - + {Object.keys(data).map((key) => { diff --git a/ui/src/utils/index.js b/ui/src/utils/index.js index da366f14e..ee1093300 100644 --- a/ui/src/utils/index.js +++ b/ui/src/utils/index.js @@ -1,5 +1,6 @@ export * from './baseUrl' export * from './docsUrl' export * from './formatters' +export * from './intersperse' export * from './notifications' export * from './openInNewTab' diff --git a/ui/src/utils/intersperse.js b/ui/src/utils/intersperse.js new file mode 100644 index 000000000..01ff7f87c --- /dev/null +++ b/ui/src/utils/intersperse.js @@ -0,0 +1,20 @@ +/* intersperse: Return an array with the separator interspersed between + * each element of the input array. + * + * > _([1,2,3]).intersperse(0) + * [1,0,2,0,3] + * + * From: https://stackoverflow.com/a/23619085 + */ +export const intersperse = (arr, sep) => { + if (arr.length === 0) { + return [] + } + + return arr.slice(1).reduce( + function (xs, x, i) { + return xs.concat([sep, x]) + }, + [arr[0]] + ) +}