mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-03 20:47:35 +03:00
Support for Original Date, Release Date & splitting/grouping of album editions (#2162)
* Update AlbumGridView.js * Update AlbumDetails.js * Update AlbumDetails.js * Create DoubleRangeField.js * Update and rename DoubleRangeField.js to RangeFieldDouble.js * Update RangeFieldDouble.js * Update AlbumGridView.js * Update AlbumDetails.js * Update RangeFieldDouble.js * Update index.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update AlbumDetails.js * Update RangeFieldDouble.js * Update AlbumDetails.js * Update RangeFieldDouble.js * Update AlbumDetails.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update AlbumDetails.js * Update AlbumDetails.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update AlbumDetails.js * Update RangeFieldDouble.js * Update AlbumDetails.js * Update en.json * Update en.json * Update AlbumDetails.js * Update RangeFieldDouble.js * Update AlbumGridView.js * Update AlbumDetails.js * Update AlbumSongs.js * Update ContextMenus.js * Update SongDatagrid.js * Update AlbumSongs.js * Update SongDatagrid.js * Update SongDatagrid.js * Update SongDatagrid.js * Update AlbumSongs.js * Update SongList.js * Update playlist_track_repository.go * Update 20230113000000_release_year.go * Update PlayButton.js * Update mediafile_repository.go * Update album.go * Update playlist_track_repository.go * Update playlist_track_repository.go * Update SongDatagrid.js * Update 20230113000000_release_year.go * Update SongDatagrid.js * Update AlbumSongs.js * Update SongDatagrid.js * Update SongDatagrid.js * Update SongDatagrid.js * Update SongDatagrid.js * Update AlbumDetails.js * Update AlbumSongs.js * Update AlbumSongs.js * Update RangeFieldDouble.js * Update SongDatagrid.js * Update 20230113000000_release_year.go * Update 20230113000000_release_year.go * Update 20230113000000_release_year.go * Update 20230113000000_release_year.go * Update AlbumSongs.js * Update AlbumSongs.js * Update mapping.go * Update RangeFieldDouble.js * Update AlbumGridView.js * Update AlbumSongs.js * Update en.json * Update SongDatagrid.js * Update SongDatagrid.js * Update metadata.go * Update mapping.go * Update AlbumDetails.js * Update AlbumGridView.js * Update RangeFieldDouble.js * Update mapping.go * Update metadata.go * Update mapping.go * Update AlbumDetails.js * Update 20230113000000_release_year.go * Update AlbumDetails.js * Update en.json * Update configuration.go * Update mapping.go * Update configuration.go * Update mediafile.go * Update metadata.go * Update RangeFieldDouble.js * Update 20230113000000_release_year.go * Update configuration.go * Update mapping.go * Update mediafile.go * Update mapping.go * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update 20230113000000_release_year.go * Update AlbumDetails.js * Update RangeFieldDouble.js * Update mapping.go * Update metadata.go * Update album.go * Update mediafile.go * Update mediafile.go * Update album.go * Update fields.go * Update mediafile_repository.go * Update playlist_track_repository.go * Update AlbumSongs.js * Update SongDatagrid.js * Update PlayButton.js * Update SongList.js * Update ContextMenus.js * Update SongDatagrid.js * Update metadata.go * Update ArtistShow.js * Update mapping.go * Update configuration.go * Update mapping.go * Update metadata.go * Update metadata.go * Update mapping.go * Update metadata.go * Update metadata.go * Update mapping.go * Update 20230113000000_release_year.go * Update 20230113000000_release_year.go * Update mapping.go * Update metadata.go * Update metadata.go * Update album.go * Update mediafile.go * Update AlbumDetails.js * Update AlbumSongs.js * Update album.go * Update mediafile.go * Update metadata.go * Update mediafile.go * Update 20230113000000_release_year.go * Update 20230113000000_release_year.go * Update album.go * Update mediafile.go * Update RangeFieldDouble.js * Update AlbumDetails.js * Update AlbumGridView.js * Update en.json * Update AlbumGridView.js * Update RangeFieldDouble.js * Update and rename 20230113000000_release_year.go to 20230113000000_release_date.go * Update album.go * Update mediafile.go * Update fields.go * Update playlist_track_repository.go * Update mediafile_repository.go * Update mapping.go * Update metadata.go * Update mapping.go * Update SongDatagrid.js * Update RangeFieldDouble.js * Update index.js * Update ContextMenus.js * Update PlayButton.js * Create FormatDate.js * Update SongList.js * Update AlbumDetails.js * Update AlbumSongs.js * Update AlbumSongs.js * Update en.json * Update AlbumDetails.js * Update album.go fixed conflict I think? * Update mediafile.go fixed conflict * Format with goimports * Update SongDatagrid.js only show Cat # in desktop view * Update metadata_internal_test.go * Update metadata_test.go * Delete test.mp3 * Add files via upload mp3 test file with Date, Original Date and Release Date * Update metadata_test.go * Update metadata_test.go * Update metadata_test.go * Update metadata_test.go * Update taglib_test.go * Delete test.mp3 * Add files via upload file with replaygain & dates * Update AlbumGridView.js * Update AlbumDetails.js * Update AlbumSongs.js * Update ContextMenus.js * Update FormatDate.js * Update PlayButton.js * Update RangeFieldDouble.js * Update SongDatagrid.js * Update AlbumSongs.js * Update SongDatagrid.js * Update AlbumSongs.js * Fix formatting * Update mapping.go * Update AlbumSongs.js * Update SongDatagrid.js * Update SongDatagrid.js prettier * Create RangeDoubleField.js rename of RangeFieldDouble.js * Update AlbumGridView.js RangeFieldDouble -> RangeDoubleField * Update mediafile.go AllOrNothing() -> allOrNothing() * Update metadata_internal_test.go getYear -> getDate * Update AlbumDetails.js wrote suggested changes * Update en.json Editions -> Releases & fixed the field name * Update configuration.go Rename Editions -> Releases * Update 20230113000000_release_date.go Editions -> Releases * Update album.go Editions -> Releases * Update mediafile.go Editions -> Releases * Update AlbumDetails.js Editions -> Releases * Update AlbumSongs.js Editions -> Releases * Update RangeDoubleField.js Editions -> Releases * Update SongDatagrid.js Editions -> Releases * Update index.js FormatFullDate and RangeDoubleField * Rename FormatDate.js to FormatFullDate.js * Delete RangeFieldDouble.js * Update mediafile.go AllOrNothing -> allOrNothing * Update mapping.go Editions -> Releases * Update AlbumDetails.js prettier * Update SongDatagrid.js showReleaseRow -> showReleaseDivider * Update AlbumSongs.js showReleaseRow -> showReleaseDivider for clarity * Update and rename 20230113000000_release_date.go to 20230515184510_add_release_date.go - rename the migration file - fixed the import to goose/v3 - additional db fields for original date & year * Update 20230515184510_add_release_date.go * Update fields.go * Update album.go * Update mediafile.go * Update mapping.go * Update AlbumDetails.js * Update en.json * Update AlbumDetails.js * Update AlbumDetails.js now hopefully prettier * Update mapping.go --------- Co-authored-by: Deluan <deluan@navidrome.org>
This commit is contained in:
parent
010ba0d15c
commit
52b77e4194
24 changed files with 511 additions and 78 deletions
|
@ -25,6 +25,7 @@ import {
|
|||
ArtistLinkField,
|
||||
DurationField,
|
||||
formatRange,
|
||||
FormatFullDate,
|
||||
SizeField,
|
||||
LoveButton,
|
||||
RatingField,
|
||||
|
@ -195,8 +196,59 @@ const Details = (props) => {
|
|||
details.push(<span key={`detail-${record.id}-${id}`}>{obj}</span>)
|
||||
}
|
||||
|
||||
const year = formatRange(record, 'year')
|
||||
year && addDetail(<>{year}</>)
|
||||
const originalYearRange = formatRange(record, 'originalYear')
|
||||
const originalDate = record.originalDate
|
||||
? FormatFullDate(record.originalDate)
|
||||
: originalYearRange
|
||||
const yearRange = formatRange(record, 'year')
|
||||
const date = record.date ? FormatFullDate(record.date) : yearRange
|
||||
const releaseDate = record.releaseDate
|
||||
? FormatFullDate(record.releaseDate)
|
||||
: date
|
||||
|
||||
const showReleaseDate = date !== releaseDate && releaseDate.length > 3
|
||||
const showOriginalDate =
|
||||
date !== originalDate &&
|
||||
originalDate !== releaseDate &&
|
||||
originalDate.length > 3
|
||||
|
||||
showOriginalDate &&
|
||||
!isXsmall &&
|
||||
addDetail(
|
||||
<>
|
||||
{[translate('resources.album.fields.originalDate'), originalDate].join(
|
||||
' '
|
||||
)}
|
||||
</>
|
||||
)
|
||||
|
||||
yearRange && addDetail(<>{['♫', !isXsmall ? date : yearRange].join(' ')}</>)
|
||||
|
||||
showReleaseDate &&
|
||||
addDetail(
|
||||
<>
|
||||
{(!isXsmall
|
||||
? [translate('resources.album.fields.releaseDate'), releaseDate]
|
||||
: ['○', record.releaseDate.substring(0, 4)]
|
||||
).join(' ')}
|
||||
</>
|
||||
)
|
||||
|
||||
const showReleases = record.releases > 1
|
||||
showReleases &&
|
||||
addDetail(
|
||||
<>
|
||||
{!isXsmall
|
||||
? [
|
||||
record.releases,
|
||||
translate('resources.album.fields.releases', {
|
||||
smart_count: record.releases,
|
||||
}),
|
||||
].join(' ')
|
||||
: ['(', record.releases, ')))'].join(' ')}
|
||||
</>
|
||||
)
|
||||
|
||||
addDetail(
|
||||
<>
|
||||
{record.songCount +
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
AlbumContextMenu,
|
||||
PlayButton,
|
||||
ArtistLinkField,
|
||||
RangeField,
|
||||
RangeDoubleField,
|
||||
} from '../common'
|
||||
import { DraggableTypes } from '../consts'
|
||||
|
||||
|
@ -161,9 +161,12 @@ const AlbumGridTile = ({ showArtist, record, basePath, ...props }) => {
|
|||
{showArtist ? (
|
||||
<ArtistLinkField record={record} className={classes.albumSubtitle} />
|
||||
) : (
|
||||
<RangeField
|
||||
<RangeDoubleField
|
||||
record={record}
|
||||
source={'year'}
|
||||
symbol1={'♫'}
|
||||
symbol2={'○'}
|
||||
separator={' · '}
|
||||
sortBy={'max_year'}
|
||||
sortByOrder={'DESC'}
|
||||
className={classes.albumSubtitle}
|
||||
|
|
|
@ -99,7 +99,7 @@ const AlbumSongs = (props) => {
|
|||
trackNumber: isDesktop && (
|
||||
<TextField
|
||||
source="trackNumber"
|
||||
sortBy="discNumber asc, trackNumber asc"
|
||||
sortBy="releaseDate asc, discNumber asc, trackNumber asc"
|
||||
label="#"
|
||||
sortable={false}
|
||||
/>
|
||||
|
@ -172,6 +172,7 @@ const AlbumSongs = (props) => {
|
|||
{...props}
|
||||
hasBulkActions={true}
|
||||
showDiscSubtitles={true}
|
||||
showReleaseDivider={true}
|
||||
contextAlwaysVisible={!isDesktop}
|
||||
classes={{ row: classes.row }}
|
||||
>
|
||||
|
@ -207,7 +208,6 @@ export const removeAlbumCommentsFromSongs = ({ album, data }) => {
|
|||
|
||||
const SanitizedAlbumSongs = (props) => {
|
||||
removeAlbumCommentsFromSongs(props)
|
||||
|
||||
const { loaded, loading, total, ...rest } = useListContext(props)
|
||||
return <>{loaded && <AlbumSongs {...rest} actions={props.actions} />}</>
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ const AlbumShowLayout = (props) => {
|
|||
addLabel={false}
|
||||
reference="album"
|
||||
target="artist_id"
|
||||
sort={{ field: 'max_year', order: 'ASC' }}
|
||||
sort={{ field: 'max_year asc,date asc', order: 'ASC' }}
|
||||
filter={{ artist_id: record?.id }}
|
||||
perPage={0}
|
||||
pagination={null}
|
||||
|
|
|
@ -200,8 +200,12 @@ export const AlbumContextMenu = (props) =>
|
|||
resource={'album'}
|
||||
songQueryParams={{
|
||||
pagination: { page: 1, perPage: -1 },
|
||||
sort: { field: 'discNumber, trackNumber', order: 'ASC' },
|
||||
filter: { album_id: props.record.id, disc_number: props.discNumber },
|
||||
sort: { field: 'releaseDate, discNumber, trackNumber', order: 'ASC' },
|
||||
filter: {
|
||||
album_id: props.record.id,
|
||||
release_date: props.releaseDate,
|
||||
disc_number: props.discNumber,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
) : null
|
||||
|
@ -226,7 +230,10 @@ export const ArtistContextMenu = (props) =>
|
|||
resource={'artist'}
|
||||
songQueryParams={{
|
||||
pagination: { page: 1, perPage: 200 },
|
||||
sort: { field: 'album, discNumber, trackNumber', order: 'ASC' },
|
||||
sort: {
|
||||
field: 'album, releaseDate, discNumber, trackNumber',
|
||||
order: 'ASC',
|
||||
},
|
||||
filter: { album_artist_id: props.record.id },
|
||||
}}
|
||||
/>
|
||||
|
|
29
ui/src/common/FormatFullDate.js
Normal file
29
ui/src/common/FormatFullDate.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
export const FormatFullDate = (date) => {
|
||||
const dashes = date.split('-').length - 1
|
||||
let options = {
|
||||
year: 'numeric',
|
||||
}
|
||||
switch (dashes) {
|
||||
case 2:
|
||||
options = {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
}
|
||||
return new Date(date).toLocaleDateString(undefined, options)
|
||||
case 1:
|
||||
options = {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
}
|
||||
return new Date(date).toLocaleDateString(undefined, options)
|
||||
case 0:
|
||||
if (date.length === 4) {
|
||||
return new Date(date).toLocaleDateString(undefined, options)
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
}
|
|
@ -21,8 +21,12 @@ export const PlayButton = ({ record, size, className }) => {
|
|||
dataProvider
|
||||
.getList('song', {
|
||||
pagination: { page: 1, perPage: -1 },
|
||||
sort: { field: 'discNumber, trackNumber', order: 'ASC' },
|
||||
filter: { album_id: record.id, disc_number: record.discNumber },
|
||||
sort: { field: 'releaseDate, discNumber, trackNumber', order: 'ASC' },
|
||||
filter: {
|
||||
album_id: record.id,
|
||||
release_date: record.releaseDate,
|
||||
disc_number: record.discNumber,
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
let { data, ids } = extractSongsData(response)
|
||||
|
|
50
ui/src/common/RangeDoubleField.js
Normal file
50
ui/src/common/RangeDoubleField.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useRecordContext } from 'react-admin'
|
||||
import { formatRange } from '../common'
|
||||
|
||||
export const RangeDoubleField = ({
|
||||
className,
|
||||
source,
|
||||
symbol1,
|
||||
symbol2,
|
||||
separator,
|
||||
...rest
|
||||
}) => {
|
||||
const record = useRecordContext(rest)
|
||||
const yearRange = formatRange(record, source).toString()
|
||||
const releases = [record.releases]
|
||||
const releaseDate = [record.releaseDate]
|
||||
const releaseYear = releaseDate.toString().substring(0, 4)
|
||||
let subtitle = yearRange
|
||||
|
||||
if (releases > 1) {
|
||||
subtitle = [
|
||||
[yearRange && symbol1, yearRange].join(' '),
|
||||
['(', releases, ')))'].join(' '),
|
||||
].join(separator)
|
||||
}
|
||||
|
||||
if (
|
||||
yearRange !== releaseYear &&
|
||||
yearRange.length > 0 &&
|
||||
releaseYear.length > 0
|
||||
) {
|
||||
subtitle = [
|
||||
[yearRange && symbol1, yearRange].join(' '),
|
||||
[symbol2, releaseYear].join(' '),
|
||||
].join(separator)
|
||||
}
|
||||
|
||||
return <span className={className}>{subtitle}</span>
|
||||
}
|
||||
|
||||
RangeDoubleField.propTypes = {
|
||||
label: PropTypes.string,
|
||||
record: PropTypes.object,
|
||||
source: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
RangeDoubleField.defaultProps = {
|
||||
addLabel: true,
|
||||
}
|
|
@ -1,6 +1,11 @@
|
|||
import React, { isValidElement, useMemo, useCallback, forwardRef } from 'react'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { Datagrid, PureDatagridBody, PureDatagridRow } from 'react-admin'
|
||||
import {
|
||||
Datagrid,
|
||||
PureDatagridBody,
|
||||
PureDatagridRow,
|
||||
useTranslate,
|
||||
} from 'react-admin'
|
||||
import {
|
||||
TableCell,
|
||||
TableRow,
|
||||
|
@ -13,7 +18,7 @@ import AlbumIcon from '@material-ui/icons/Album'
|
|||
import clsx from 'clsx'
|
||||
import { useDrag } from 'react-dnd'
|
||||
import { playTracks } from '../actions'
|
||||
import { AlbumContextMenu } from '../common'
|
||||
import { AlbumContextMenu, FormatFullDate } from '../common'
|
||||
import { DraggableTypes } from '../consts'
|
||||
|
||||
const useStyles = makeStyles({
|
||||
|
@ -49,12 +54,57 @@ const useStyles = makeStyles({
|
|||
},
|
||||
})
|
||||
|
||||
const ReleaseRow = forwardRef(
|
||||
({ record, onClick, colSpan, contextAlwaysVisible }, ref) => {
|
||||
const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md'))
|
||||
const classes = useStyles({ isDesktop })
|
||||
const translate = useTranslate()
|
||||
const handlePlaySubset = (releaseDate) => () => {
|
||||
onClick(releaseDate)
|
||||
}
|
||||
|
||||
let releaseTitle = []
|
||||
if (record.releaseDate) {
|
||||
releaseTitle.push(translate('resources.album.fields.released'))
|
||||
releaseTitle.push(FormatFullDate(record.releaseDate))
|
||||
if (record.catalogNum && isDesktop) {
|
||||
releaseTitle.push('· Cat #')
|
||||
releaseTitle.push(record.catalogNum)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
hover
|
||||
ref={ref}
|
||||
onClick={handlePlaySubset(record.releaseDate)}
|
||||
className={classes.row}
|
||||
>
|
||||
<TableCell colSpan={colSpan}>
|
||||
<Typography variant="h6" className={classes.subtitle}>
|
||||
{releaseTitle.join(' ')}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<AlbumContextMenu
|
||||
record={{ id: record.albumId }}
|
||||
releaseDate={record.releaseDate}
|
||||
showLove={false}
|
||||
className={classes.contextMenu}
|
||||
visible={contextAlwaysVisible}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
const DiscSubtitleRow = forwardRef(
|
||||
({ record, onClick, colSpan, contextAlwaysVisible }, ref) => {
|
||||
const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md'))
|
||||
const classes = useStyles({ isDesktop })
|
||||
const handlePlayDisc = (discNumber) => () => {
|
||||
onClick(discNumber)
|
||||
const handlePlaySubset = (releaseDate, discNumber) => () => {
|
||||
onClick(releaseDate, discNumber)
|
||||
}
|
||||
|
||||
let subtitle = []
|
||||
|
@ -69,7 +119,7 @@ const DiscSubtitleRow = forwardRef(
|
|||
<TableRow
|
||||
hover
|
||||
ref={ref}
|
||||
onClick={handlePlayDisc(record.discNumber)}
|
||||
onClick={handlePlaySubset(record.releaseDate, record.discNumber)}
|
||||
className={classes.row}
|
||||
>
|
||||
<TableCell colSpan={colSpan}>
|
||||
|
@ -82,6 +132,7 @@ const DiscSubtitleRow = forwardRef(
|
|||
<AlbumContextMenu
|
||||
record={{ id: record.albumId }}
|
||||
discNumber={record.discNumber}
|
||||
releaseDate={record.releaseDate}
|
||||
showLove={false}
|
||||
className={classes.contextMenu}
|
||||
visible={contextAlwaysVisible}
|
||||
|
@ -95,9 +146,10 @@ const DiscSubtitleRow = forwardRef(
|
|||
export const SongDatagridRow = ({
|
||||
record,
|
||||
children,
|
||||
firstTracks,
|
||||
firstTracksOfDiscs,
|
||||
firstTracksOfReleases,
|
||||
contextAlwaysVisible,
|
||||
onClickDiscSubtitle,
|
||||
onClickSubset,
|
||||
className,
|
||||
...rest
|
||||
}) => {
|
||||
|
@ -110,7 +162,13 @@ export const SongDatagridRow = ({
|
|||
() => ({
|
||||
type: DraggableTypes.DISC,
|
||||
item: {
|
||||
discs: [{ albumId: record?.albumId, discNumber: record?.discNumber }],
|
||||
discs: [
|
||||
{
|
||||
albumId: record?.albumId,
|
||||
releaseDate: record?.releaseDate,
|
||||
discNumber: record?.discNumber,
|
||||
},
|
||||
],
|
||||
},
|
||||
options: { dropEffect: 'copy' },
|
||||
}),
|
||||
|
@ -133,11 +191,20 @@ export const SongDatagridRow = ({
|
|||
const childCount = fields.length
|
||||
return (
|
||||
<>
|
||||
{firstTracks.has(record.id) && (
|
||||
{firstTracksOfReleases.has(record.id) && (
|
||||
<ReleaseRow
|
||||
ref={dragDiscRef}
|
||||
record={record}
|
||||
onClick={onClickSubset}
|
||||
contextAlwaysVisible={contextAlwaysVisible}
|
||||
colSpan={childCount + (rest.expand ? 1 : 0)}
|
||||
/>
|
||||
)}
|
||||
{firstTracksOfDiscs.has(record.id) && (
|
||||
<DiscSubtitleRow
|
||||
ref={dragDiscRef}
|
||||
record={record}
|
||||
onClick={onClickDiscSubtitle}
|
||||
onClick={onClickSubset}
|
||||
contextAlwaysVisible={contextAlwaysVisible}
|
||||
colSpan={childCount + (rest.expand ? 1 : 0)}
|
||||
/>
|
||||
|
@ -157,32 +224,43 @@ export const SongDatagridRow = ({
|
|||
SongDatagridRow.propTypes = {
|
||||
record: PropTypes.object,
|
||||
children: PropTypes.node,
|
||||
firstTracks: PropTypes.instanceOf(Set),
|
||||
firstTracksOfDiscs: PropTypes.instanceOf(Set),
|
||||
firstTracksOfReleases: PropTypes.instanceOf(Set),
|
||||
contextAlwaysVisible: PropTypes.bool,
|
||||
onClickDiscSubtitle: PropTypes.func,
|
||||
onClickSubset: PropTypes.func,
|
||||
}
|
||||
|
||||
SongDatagridRow.defaultProps = {
|
||||
onClickDiscSubtitle: () => {},
|
||||
onClickSubset: () => {},
|
||||
}
|
||||
|
||||
const SongDatagridBody = ({
|
||||
contextAlwaysVisible,
|
||||
showDiscSubtitles,
|
||||
showReleaseDivider,
|
||||
...rest
|
||||
}) => {
|
||||
const dispatch = useDispatch()
|
||||
const { ids, data } = rest
|
||||
|
||||
const playDisc = useCallback(
|
||||
(discNumber) => {
|
||||
const idsToPlay = ids.filter((id) => data[id].discNumber === discNumber)
|
||||
const playSubset = useCallback(
|
||||
(releaseDate, discNumber) => {
|
||||
let idsToPlay = []
|
||||
if (discNumber !== undefined) {
|
||||
idsToPlay = ids.filter(
|
||||
(id) =>
|
||||
data[id].releaseDate === releaseDate &&
|
||||
data[id].discNumber === discNumber
|
||||
)
|
||||
} else {
|
||||
idsToPlay = ids.filter((id) => data[id].releaseDate === releaseDate)
|
||||
}
|
||||
dispatch(playTracks(data, idsToPlay))
|
||||
},
|
||||
[dispatch, data, ids]
|
||||
)
|
||||
|
||||
const firstTracks = useMemo(() => {
|
||||
const firstTracksOfDiscs = useMemo(() => {
|
||||
if (!ids) {
|
||||
return new Set()
|
||||
}
|
||||
|
@ -195,7 +273,8 @@ const SongDatagridBody = ({
|
|||
foundSubtitle = foundSubtitle || data[id].discSubtitle
|
||||
if (
|
||||
acc.length === 0 ||
|
||||
(last && data[id].discNumber !== data[last].discNumber)
|
||||
(last && data[id].discNumber !== data[last].discNumber) ||
|
||||
(last && data[id].releaseDate !== data[last].releaseDate)
|
||||
) {
|
||||
acc.push(id)
|
||||
}
|
||||
|
@ -208,14 +287,39 @@ const SongDatagridBody = ({
|
|||
return set
|
||||
}, [ids, data, showDiscSubtitles])
|
||||
|
||||
const firstTracksOfReleases = useMemo(() => {
|
||||
if (!ids) {
|
||||
return new Set()
|
||||
}
|
||||
const set = new Set(
|
||||
ids
|
||||
.filter((i) => data[i])
|
||||
.reduce((acc, id) => {
|
||||
const last = acc && acc[acc.length - 1]
|
||||
if (
|
||||
acc.length === 0 ||
|
||||
(last && data[id].releaseDate !== data[last].releaseDate)
|
||||
) {
|
||||
acc.push(id)
|
||||
}
|
||||
return acc
|
||||
}, [])
|
||||
)
|
||||
if (!showReleaseDivider || set.size < 2) {
|
||||
set.clear()
|
||||
}
|
||||
return set
|
||||
}, [ids, data, showReleaseDivider])
|
||||
|
||||
return (
|
||||
<PureDatagridBody
|
||||
{...rest}
|
||||
row={
|
||||
<SongDatagridRow
|
||||
firstTracks={firstTracks}
|
||||
firstTracksOfDiscs={firstTracksOfDiscs}
|
||||
firstTracksOfReleases={firstTracksOfReleases}
|
||||
contextAlwaysVisible={contextAlwaysVisible}
|
||||
onClickDiscSubtitle={playDisc}
|
||||
onClickSubset={playSubset}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
@ -225,6 +329,7 @@ const SongDatagridBody = ({
|
|||
export const SongDatagrid = ({
|
||||
contextAlwaysVisible,
|
||||
showDiscSubtitles,
|
||||
showReleaseDivider,
|
||||
...rest
|
||||
}) => {
|
||||
const classes = useStyles()
|
||||
|
@ -236,6 +341,7 @@ export const SongDatagrid = ({
|
|||
<SongDatagridBody
|
||||
contextAlwaysVisible={contextAlwaysVisible}
|
||||
showDiscSubtitles={showDiscSubtitles}
|
||||
showReleaseDivider={showReleaseDivider}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
@ -245,5 +351,6 @@ export const SongDatagrid = ({
|
|||
SongDatagrid.propTypes = {
|
||||
contextAlwaysVisible: PropTypes.bool,
|
||||
showDiscSubtitles: PropTypes.bool,
|
||||
showReleaseDivider: PropTypes.bool,
|
||||
classes: PropTypes.object,
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ export * from './BatchPlayButton'
|
|||
export * from './BitrateField'
|
||||
export * from './ContextMenus'
|
||||
export * from './DateField'
|
||||
export * from './FormatFullDate'
|
||||
export * from './DocLink'
|
||||
export * from './DurationField'
|
||||
export * from './List'
|
||||
|
@ -12,6 +13,7 @@ export * from './Pagination'
|
|||
export * from './PlayButton'
|
||||
export * from './QuickFilter'
|
||||
export * from './RangeField'
|
||||
export * from './RangeDoubleField'
|
||||
export * from './ShuffleAllButton'
|
||||
export * from './SimpleList'
|
||||
export * from './SizeField'
|
||||
|
|
|
@ -51,7 +51,9 @@
|
|||
"name": "Name",
|
||||
"genre": "Genre",
|
||||
"compilation": "Compilation",
|
||||
"year": "Year",
|
||||
"originalDate": "Original",
|
||||
"releaseDate": "Released",
|
||||
"releases": "Release |||| Releases",
|
||||
"updatedAt": "Updated at",
|
||||
"comment": "Comment",
|
||||
"rating": "Rating",
|
||||
|
|
|
@ -102,7 +102,7 @@ const SongList = (props) => {
|
|||
<AlbumLinkField
|
||||
source="album"
|
||||
sortBy={
|
||||
'album, order_album_artist_name, disc_number, track_number, title'
|
||||
'album, order_album_artist_name, release_date, disc_number, track_number, title'
|
||||
}
|
||||
sortByOrder={'ASC'}
|
||||
/>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue