fix: restore old date display/sort behaviour (#3862)

* fix(server): bring back legacy date mappings

Signed-off-by: Deluan <deluan@navidrome.org>

* reuse the mapDates logic in the legacyReleaseDate function

Signed-off-by: Deluan <deluan@navidrome.org>

* fix mappings

Signed-off-by: Deluan <deluan@navidrome.org>

* show original and release dates in album grid

Signed-off-by: Deluan <deluan@navidrome.org>

* fix tests based on new year mapping

Signed-off-by: Deluan <deluan@navidrome.org>

* fix(subsonic): prefer returning original_year over (recording) year
when sorting albums

Signed-off-by: Deluan <deluan@navidrome.org>

* fix case when we don't have originalYear

Signed-off-by: Deluan <deluan@navidrome.org>

* show all dates in album's info, and remove the recording date from the album page

Signed-off-by: Deluan <deluan@navidrome.org>

* better?

Signed-off-by: Deluan <deluan@navidrome.org>

* add snapshot tests for Album Details

Signed-off-by: Deluan <deluan@navidrome.org>

* fix(subsonic): sort order for getAlbumList?type=byYear

Signed-off-by: Deluan <deluan@navidrome.org>

---------

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan Quintão 2025-03-30 17:06:58 -04:00 committed by GitHub
parent 88f87e6c4f
commit 2b84c574ba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 929 additions and 155 deletions

View file

@ -51,20 +51,6 @@ func legacyMapAlbumName(md Metadata) string {
// Keep the TaggedLikePicard logic for backwards compatibility
func legacyReleaseDate(md Metadata) string {
// Start with defaults
date := md.Date(model.TagRecordingDate)
year := date.Year()
originalDate := md.Date(model.TagOriginalDate)
originalYear := originalDate.Year()
releaseDate := md.Date(model.TagReleaseDate)
releaseYear := releaseDate.Year()
// MusicBrainz Picard writes the Release Date of an album to the Date tag, and leaves the Release Date tag empty
taggedLikePicard := (originalYear != 0) &&
(releaseYear == 0) &&
(year >= originalYear)
if taggedLikePicard {
return string(date)
}
_, _, releaseDate := md.mapDates()
return string(releaseDate)
}

View file

@ -0,0 +1,30 @@
package metadata
import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("legacyReleaseDate", func() {
DescribeTable("legacyReleaseDate",
func(recordingDate, originalDate, releaseDate, expected string) {
md := New("", Info{
Tags: map[string][]string{
"DATE": {recordingDate},
"ORIGINALDATE": {originalDate},
"RELEASEDATE": {releaseDate},
},
})
result := legacyReleaseDate(md)
Expect(result).To(Equal(expected))
},
Entry("regular mapping", "2020-05-15", "2019-02-10", "2021-01-01", "2021-01-01"),
Entry("legacy mapping", "2020-05-15", "2019-02-10", "", "2020-05-15"),
Entry("legacy mapping, originalYear < year", "2018-05-15", "2019-02-10", "2021-01-01", "2021-01-01"),
Entry("legacy mapping, originalYear empty", "2020-05-15", "", "2021-01-01", "2021-01-01"),
Entry("legacy mapping, releaseYear", "2020-05-15", "2019-02-10", "2021-01-01", "2021-01-01"),
Entry("legacy mapping, same dates", "2020-05-15", "2020-05-15", "", "2020-05-15"),
)
})

View file

@ -1,6 +1,7 @@
package metadata
import (
"cmp"
"encoding/json"
"maps"
"math"
@ -39,11 +40,9 @@ func (md Metadata) ToMediaFile(libID int, folderID string) model.MediaFile {
mf.ExplicitStatus = md.mapExplicitStatusTag()
// Dates
origDate := md.Date(model.TagOriginalDate)
date, origDate, relDate := md.mapDates()
mf.OriginalYear, mf.OriginalDate = origDate.Year(), string(origDate)
relDate := md.Date(model.TagReleaseDate)
mf.ReleaseYear, mf.ReleaseDate = relDate.Year(), string(relDate)
date := md.Date(model.TagRecordingDate)
mf.Year, mf.Date = date.Year(), string(date)
// MBIDs
@ -164,3 +163,22 @@ func (md Metadata) mapExplicitStatusTag() string {
return ""
}
}
func (md Metadata) mapDates() (date Date, originalDate Date, releaseDate Date) {
// Start with defaults
date = md.Date(model.TagRecordingDate)
originalDate = md.Date(model.TagOriginalDate)
releaseDate = md.Date(model.TagReleaseDate)
// For some historic reason, taggers have been writing the Release Date of an album to the Date tag,
// and leave the Release Date tag empty.
legacyMappings := (originalDate != "") &&
(releaseDate == "") &&
(date >= originalDate)
if legacyMappings {
return originalDate, originalDate, date
}
// when there's no Date, first fall back to Original Date, then to Release Date.
date = cmp.Or(date, originalDate, releaseDate)
return date, originalDate, releaseDate
}

View file

@ -35,7 +35,7 @@ var _ = Describe("ToMediaFile", func() {
}
Describe("Dates", func() {
It("should parse the dates like Picard", func() {
It("should parse properly tagged dates ", func() {
mf = toMediaFile(model.RawTags{
"ORIGINALDATE": {"1978-09-10"},
"DATE": {"1977-03-04"},
@ -49,6 +49,32 @@ var _ = Describe("ToMediaFile", func() {
Expect(mf.ReleaseYear).To(Equal(2002))
Expect(mf.ReleaseDate).To(Equal("2002-01-02"))
})
It("should parse dates with only year", func() {
mf = toMediaFile(model.RawTags{
"ORIGINALYEAR": {"1978"},
"DATE": {"1977"},
"RELEASEDATE": {"2002"},
})
Expect(mf.Year).To(Equal(1977))
Expect(mf.Date).To(Equal("1977"))
Expect(mf.OriginalYear).To(Equal(1978))
Expect(mf.OriginalDate).To(Equal("1978"))
Expect(mf.ReleaseYear).To(Equal(2002))
Expect(mf.ReleaseDate).To(Equal("2002"))
})
It("should parse dates tagged the legacy way (no release date)", func() {
mf = toMediaFile(model.RawTags{
"DATE": {"2014"},
"ORIGINALDATE": {"1966"},
})
Expect(mf.Year).To(Equal(1966))
Expect(mf.OriginalYear).To(Equal(1966))
Expect(mf.ReleaseYear).To(Equal(2014))
})
})
Describe("Lyrics", func() {

View file

@ -90,13 +90,14 @@ var _ = Describe("Metadata", func() {
md = metadata.New(filePath, props)
Expect(md.All()).To(SatisfyAll(
HaveLen(5),
Not(HaveKey(unknownTag)),
HaveKeyWithValue(model.TagTrackArtist, []string{"Artist Name", "Second Artist"}),
HaveKeyWithValue(model.TagAlbum, []string{"Album Name"}),
HaveKeyWithValue(model.TagRecordingDate, []string{"2022-10-02", "2022"}),
HaveKeyWithValue(model.TagRecordingDate, []string{"2022-10-02"}),
HaveKeyWithValue(model.TagReleaseDate, []string{"2022"}),
HaveKeyWithValue(model.TagGenre, []string{"Pop", "Rock"}),
HaveKeyWithValue(model.TagTrackNumber, []string{"1/10"}),
HaveLen(6),
))
})

View file

@ -97,9 +97,10 @@ func NewAlbumRepository(ctx context.Context, db dbx.Builder) model.AlbumReposito
r.tableName = "album"
r.registerModel(&model.Album{}, albumFilters())
r.setSortMappings(map[string]string{
"name": "order_album_name, order_album_artist_name",
"artist": "compilation, order_album_artist_name, order_album_name",
"album_artist": "compilation, order_album_artist_name, order_album_name",
"name": "order_album_name, order_album_artist_name",
"artist": "compilation, order_album_artist_name, order_album_name",
"album_artist": "compilation, order_album_artist_name, order_album_name",
// TODO Rename this to just year (or date)
"max_year": "coalesce(nullif(original_date,''), cast(max_year as text)), release_date, name",
"random": "random",
"recently_added": recentlyAddedSort(),

View file

@ -57,6 +57,7 @@
"genre": "Gênero",
"compilation": "Coletânea",
"year": "Ano",
"date": "Data de Lançamento",
"updatedAt": "Últ. Atualização",
"comment": "Comentário",
"rating": "Classificação",

View file

@ -118,10 +118,10 @@ main:
aliases: [ tdor, originaldate, ----:com.apple.itunes:originaldate, wm/originalreleasetime, tory, originalyear, ----:com.apple.itunes:originalyear, wm/originalreleaseyear ]
type: date
recordingdate:
aliases: [ tdrc, date, icrd, ©day, wm/year, year ]
aliases: [ tdrc, date, recordingdate, icrd, record date ]
type: date
releasedate:
aliases: [ tdrl, releasedate ]
aliases: [ tdrl, releasedate, ©day, wm/year, year ]
type: date
catalognumber:
aliases: [ txxx:catalognumber, catalognumber, ----:com.apple.itunes:catalognumber, wm/catalogno ]

View file

@ -62,13 +62,14 @@ func AlbumsByArtistID(artistId string) Options {
}
func AlbumsByYear(fromYear, toYear int) Options {
sortOption := "max_year, name"
orderOption := ""
if fromYear > toYear {
fromYear, toYear = toYear, fromYear
sortOption = "max_year desc, name"
orderOption = "desc"
}
return addDefaultFilters(Options{
Sort: sortOption,
Sort: "max_year",
Order: orderOption,
Filters: Or{
And{
GtOrEq{"min_year": fromYear},
@ -118,7 +119,7 @@ func SongWithLyrics(artist, title string) Options {
func ByGenre(genre string) Options {
return addDefaultFilters(Options{
Sort: "name asc",
Sort: "name",
Filters: filterByGenre(genre),
})
}

View file

@ -296,7 +296,7 @@ func childFromAlbum(ctx context.Context, al model.Album) responses.Child {
child.Name = al.Name
child.Album = al.Name
child.Artist = al.AlbumArtist
child.Year = int32(al.MaxYear)
child.Year = int32(cmp.Or(al.MaxOriginalYear, al.MaxYear))
child.Genre = al.Genre
child.CoverArt = al.CoverArtID().String()
child.Created = &al.CreatedAt
@ -380,7 +380,7 @@ func buildAlbumID3(ctx context.Context, album model.Album) responses.AlbumID3 {
dir.SongCount = int32(album.SongCount)
dir.Duration = int32(album.Duration)
dir.PlayCount = album.PlayCount
dir.Year = int32(album.MaxYear)
dir.Year = int32(cmp.Or(album.MaxOriginalYear, album.MaxYear))
dir.Genre = album.Genre
if !album.CreatedAt.IsZero() {
dir.Created = &album.CreatedAt

View file

@ -0,0 +1,19 @@
import { useRecordContext } from 'react-admin'
import { formatRange } from '../common/index.js'
const originalYearSymbol = '♫'
const releaseYearSymbol = '○'
export const AlbumDatesField = ({ className, ...rest }) => {
const record = useRecordContext(rest)
const releaseDate = record.releaseDate
const releaseYear = releaseDate?.toString().substring(0, 4)
const yearRange =
formatRange(record, 'originalYear') || record['maxYear']?.toString()
let label = yearRange
if (releaseYear !== undefined && yearRange !== releaseYear) {
label = `${originalYearSymbol} ${yearRange} · ${releaseYearSymbol} ${releaseYear}`
}
return <span className={className}>{label}</span>
}

View file

@ -1,4 +1,4 @@
import { useState, useEffect, useCallback } from 'react'
import { useCallback, useEffect, useState } from 'react'
import {
Card,
CardContent,
@ -10,25 +10,25 @@ import {
withWidth,
} from '@material-ui/core'
import {
useRecordContext,
useTranslate,
ArrayField,
SingleFieldList,
ChipField,
Link,
SingleFieldList,
useRecordContext,
useTranslate,
} from 'react-admin'
import Lightbox from 'react-image-lightbox'
import 'react-image-lightbox/style.css'
import subsonic from '../subsonic'
import {
ArtistLinkField,
CollapsibleComment,
DurationField,
formatRange,
SizeField,
LoveButton,
RatingField,
SizeField,
useAlbumsPerPage,
CollapsibleComment,
} from '../common'
import config from '../config'
import { formatFullDate, intersperse } from '../utils'
@ -140,69 +140,55 @@ const GenreList = () => {
)
}
const Details = (props) => {
export const Details = (props) => {
const isXsmall = useMediaQuery((theme) => theme.breakpoints.down('xs'))
const translate = useTranslate()
const record = useRecordContext(props)
// Create an array of detail elements
let details = []
const addDetail = (obj) => {
const id = details.length
details.push(<span key={`detail-${record.id}-${id}`}>{obj}</span>)
}
const originalYearRange = formatRange(record, 'originalYear')
const originalDate = record.originalDate
? formatFullDate(record.originalDate)
: originalYearRange
// Calculate date related fields
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
const originalDate = record.originalDate
? formatFullDate(record.originalDate)
: formatRange(record, 'originalYear')
const releaseDate = record?.releaseDate && formatFullDate(record.releaseDate)
showOriginalDate &&
!isXsmall &&
const dateToUse = originalDate || date
const isOriginalDate = originalDate && dateToUse !== date
const showDate = dateToUse && dateToUse !== releaseDate
// Get label for the main date display
const getDateLabel = () => {
if (isXsmall) return '♫'
if (isOriginalDate) return translate('resources.album.fields.originalDate')
return null
}
// Get label for release date display
const getReleaseDateLabel = () => {
if (!isXsmall) return translate('resources.album.fields.releaseDate')
if (showDate) return '○'
return null
}
// Display dates with appropriate labels
if (showDate) {
addDetail(<>{[getDateLabel(), dateToUse].filter(Boolean).join(' ')}</>)
}
if (releaseDate) {
addDetail(
<>
{[translate('resources.album.fields.originalDate'), originalDate].join(
' ',
)}
</>,
<>{[getReleaseDateLabel(), releaseDate].filter(Boolean).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 +
@ -215,6 +201,7 @@ const Details = (props) => {
!isXsmall && addDetail(<DurationField source={'duration'} />)
!isXsmall && addDetail(<SizeField source="size" />)
// Return the details rendered with separators
return <>{intersperse(details, ' · ')}</>
}

View file

@ -0,0 +1,327 @@
// ui/src/album/__tests__/AlbumDetails.test.jsx
import { describe, test, expect, beforeEach, afterEach } from 'vitest'
import { render } from '@testing-library/react'
import { RecordContextProvider } from 'react-admin'
import { useMediaQuery } from '@material-ui/core'
import { Details } from './AlbumDetails'
// Mock useMediaQuery
vi.mock('@material-ui/core', async () => {
const actual = await import('@material-ui/core')
return {
...actual,
useMediaQuery: vi.fn(),
}
})
describe('Details component', () => {
describe('Desktop view', () => {
beforeEach(() => {
// Set desktop view (isXsmall = false)
vi.mocked(useMediaQuery).mockReturnValue(false)
})
test('renders correctly with just year range', () => {
const record = {
id: '123',
name: 'Test Album',
songCount: 12,
duration: 3600,
size: 102400,
year: 2020,
}
const { container } = render(
<RecordContextProvider value={record}>
<Details />
</RecordContextProvider>,
)
expect(container).toMatchSnapshot()
})
test('renders correctly with date', () => {
const record = {
id: '123',
name: 'Test Album',
songCount: 12,
duration: 3600,
size: 102400,
date: '2020-05-01',
}
const { container } = render(
<RecordContextProvider value={record}>
<Details />
</RecordContextProvider>,
)
expect(container).toMatchSnapshot()
})
test('renders correctly with originalDate', () => {
const record = {
id: '123',
name: 'Test Album',
songCount: 12,
duration: 3600,
size: 102400,
originalDate: '2018-03-15',
}
const { container } = render(
<RecordContextProvider value={record}>
<Details />
</RecordContextProvider>,
)
expect(container).toMatchSnapshot()
})
test('renders correctly with date and originalDate', () => {
const record = {
id: '123',
name: 'Test Album',
songCount: 12,
duration: 3600,
size: 102400,
date: '2020-05-01',
originalDate: '2018-03-15',
}
const { container } = render(
<RecordContextProvider value={record}>
<Details />
</RecordContextProvider>,
)
expect(container).toMatchSnapshot()
})
test('renders correctly with releaseDate', () => {
const record = {
id: '123',
name: 'Test Album',
songCount: 12,
duration: 3600,
size: 102400,
releaseDate: '2020-06-15',
}
const { container } = render(
<RecordContextProvider value={record}>
<Details />
</RecordContextProvider>,
)
expect(container).toMatchSnapshot()
})
test('renders correctly with all date fields', () => {
const record = {
id: '123',
name: 'Test Album',
songCount: 12,
duration: 3600,
size: 102400,
date: '2020-05-01',
originalDate: '2018-03-15',
releaseDate: '2020-06-15',
}
const { container } = render(
<RecordContextProvider value={record}>
<Details />
</RecordContextProvider>,
)
expect(container).toMatchSnapshot()
})
})
describe('Mobile view', () => {
beforeEach(() => {
// Set mobile view (isXsmall = true)
vi.mocked(useMediaQuery).mockReturnValue(true)
})
afterEach(() => {
vi.clearAllMocks()
})
test('renders correctly with just year range', () => {
const record = {
id: '123',
name: 'Test Album',
songCount: 12,
duration: 3600,
size: 102400,
year: 2020,
}
const { container } = render(
<RecordContextProvider value={record}>
<Details />
</RecordContextProvider>,
)
expect(container).toMatchSnapshot()
})
test('renders correctly with date', () => {
const record = {
id: '123',
name: 'Test Album',
songCount: 12,
duration: 3600,
size: 102400,
date: '2020-05-01',
}
const { container } = render(
<RecordContextProvider value={record}>
<Details />
</RecordContextProvider>,
)
expect(container).toMatchSnapshot()
})
test('renders correctly with originalDate', () => {
const record = {
id: '123',
name: 'Test Album',
songCount: 12,
duration: 3600,
size: 102400,
originalDate: '2018-03-15',
}
const { container } = render(
<RecordContextProvider value={record}>
<Details />
</RecordContextProvider>,
)
expect(container).toMatchSnapshot()
})
test('renders correctly with date and originalDate', () => {
const record = {
id: '123',
name: 'Test Album',
songCount: 12,
duration: 3600,
size: 102400,
date: '2020-05-01',
originalDate: '2018-03-15',
}
const { container } = render(
<RecordContextProvider value={record}>
<Details />
</RecordContextProvider>,
)
expect(container).toMatchSnapshot()
})
test('renders correctly with releaseDate', () => {
const record = {
id: '123',
name: 'Test Album',
songCount: 12,
duration: 3600,
size: 102400,
releaseDate: '2020-06-15',
}
const { container } = render(
<RecordContextProvider value={record}>
<Details />
</RecordContextProvider>,
)
expect(container).toMatchSnapshot()
})
test('renders correctly with all date fields', () => {
const record = {
id: '123',
name: 'Test Album',
songCount: 12,
duration: 3600,
size: 102400,
date: '2020-05-01',
originalDate: '2018-03-15',
releaseDate: '2020-06-15',
}
const { container } = render(
<RecordContextProvider value={record}>
<Details />
</RecordContextProvider>,
)
expect(container).toMatchSnapshot()
})
test('renders correctly with no date fields', () => {
const record = {
id: '123',
name: 'Test Album',
songCount: 12,
duration: 3600,
size: 102400,
}
const { container } = render(
<RecordContextProvider value={record}>
<Details />
</RecordContextProvider>,
)
expect(container).toMatchSnapshot()
})
test('renders correctly with year range (start and end years)', () => {
const record = {
id: '123',
name: 'Test Album',
songCount: 12,
duration: 3600,
size: 102400,
year: 2018,
yearEnd: 2020,
}
const { container } = render(
<RecordContextProvider value={record}>
<Details />
</RecordContextProvider>,
)
expect(container).toMatchSnapshot()
})
test('renders correctly with originalYear range', () => {
const record = {
id: '123',
name: 'Test Album',
songCount: 12,
duration: 3600,
size: 102400,
originalYear: 2015,
originalYearEnd: 2016,
}
const { container } = render(
<RecordContextProvider value={record}>
<Details />
</RecordContextProvider>,
)
expect(container).toMatchSnapshot()
})
})
})

View file

@ -13,14 +13,10 @@ import { linkToRecord, useListContext, Loading } from 'react-admin'
import { withContentRect } from 'react-measure'
import { useDrag } from 'react-dnd'
import subsonic from '../subsonic'
import {
AlbumContextMenu,
PlayButton,
ArtistLinkField,
RangeDoubleField,
} from '../common'
import { AlbumContextMenu, PlayButton, ArtistLinkField } from '../common'
import { DraggableTypes } from '../consts'
import clsx from 'clsx'
import { AlbumDatesField } from './AlbumDatesField.jsx'
const useStyles = makeStyles(
(theme) => ({
@ -187,16 +183,7 @@ const AlbumGridTile = ({ showArtist, record, basePath, ...props }) => {
{showArtist ? (
<ArtistLinkField record={record} className={classes.albumSubtitle} />
) : (
<RangeDoubleField
record={record}
source={'year'}
symbol1={'♫'}
symbol2={'○'}
separator={' · '}
sortBy={'max_year'}
sortByOrder={'DESC'}
className={classes.albumSubtitle}
/>
<AlbumDatesField record={record} className={classes.albumSubtitle} />
)}
</div>
)

View file

@ -20,6 +20,7 @@ import {
ArtistLinkField,
MultiLineTextField,
ParticipantsInfo,
RangeField,
} from '../common'
const useStyles = makeStyles({
@ -47,6 +48,20 @@ const AlbumInfo = (props) => {
</SingleFieldList>
</ArrayField>
),
date:
record?.maxYear && record.maxYear === record.minYear ? (
<TextField source={'date'} />
) : (
<RangeField source={'year'} />
),
originalDate:
record?.maxOriginalYear &&
record.maxOriginalYear === record.minOriginalYear ? (
<TextField source={'originalDate'} />
) : (
<RangeField source={'originalYear'} />
),
releaseDate: <TextField source={'releaseDate'} />,
recordLabel: (
<FunctionField
source={'recordLabel'}

View file

@ -0,0 +1,425 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Details component > Desktop view > renders correctly with all date fields 1`] = `
<div>
<span>
resources.album.fields.originalDate Mar 15, 2018
</span>
·
<span>
resources.album.fields.releaseDate Jun 15, 2020
</span>
·
<span>
12 resources.song.name
</span>
·
<span>
<span>
01:00:00
</span>
</span>
·
<span>
<span
class="makeStyles-root-6"
>
100 KB
</span>
</span>
</div>
`;
exports[`Details component > Desktop view > renders correctly with date 1`] = `
<div>
<span>
May 1, 2020
</span>
·
<span>
12 resources.song.name
</span>
·
<span>
<span>
01:00:00
</span>
</span>
·
<span>
<span
class="makeStyles-root-2"
>
100 KB
</span>
</span>
</div>
`;
exports[`Details component > Desktop view > renders correctly with date and originalDate 1`] = `
<div>
<span>
resources.album.fields.originalDate Mar 15, 2018
</span>
·
<span>
12 resources.song.name
</span>
·
<span>
<span>
01:00:00
</span>
</span>
·
<span>
<span
class="makeStyles-root-4"
>
100 KB
</span>
</span>
</div>
`;
exports[`Details component > Desktop view > renders correctly with just year range 1`] = `
<div>
<span>
12 resources.song.name
</span>
·
<span>
<span>
01:00:00
</span>
</span>
·
<span>
<span
class="makeStyles-root-1"
>
100 KB
</span>
</span>
</div>
`;
exports[`Details component > Desktop view > renders correctly with originalDate 1`] = `
<div>
<span>
resources.album.fields.originalDate Mar 15, 2018
</span>
·
<span>
12 resources.song.name
</span>
·
<span>
<span>
01:00:00
</span>
</span>
·
<span>
<span
class="makeStyles-root-3"
>
100 KB
</span>
</span>
</div>
`;
exports[`Details component > Desktop view > renders correctly with releaseDate 1`] = `
<div>
<span>
resources.album.fields.releaseDate Jun 15, 2020
</span>
·
<span>
12 resources.song.name
</span>
·
<span>
<span>
01:00:00
</span>
</span>
·
<span>
<span
class="makeStyles-root-5"
>
100 KB
</span>
</span>
</div>
`;
exports[`Details component > Mobile view > renders correctly with all date fields 1`] = `
<div>
<span>
♫ Mar 15, 2018
</span>
·
<span>
○ Jun 15, 2020
</span>
·
<span>
12 resources.song.name
</span>
</div>
`;
exports[`Details component > Mobile view > renders correctly with date 1`] = `
<div>
<span>
♫ May 1, 2020
</span>
·
<span>
12 resources.song.name
</span>
</div>
`;
exports[`Details component > Mobile view > renders correctly with date and originalDate 1`] = `
<div>
<span>
♫ Mar 15, 2018
</span>
·
<span>
12 resources.song.name
</span>
</div>
`;
exports[`Details component > Mobile view > renders correctly with just year range 1`] = `
<div>
<span>
12 resources.song.name
</span>
</div>
`;
exports[`Details component > Mobile view > renders correctly with no date fields 1`] = `
<div>
<span>
12 resources.song.name
</span>
</div>
`;
exports[`Details component > Mobile view > renders correctly with originalDate 1`] = `
<div>
<span>
♫ Mar 15, 2018
</span>
·
<span>
12 resources.song.name
</span>
</div>
`;
exports[`Details component > Mobile view > renders correctly with originalYear range 1`] = `
<div>
<span>
12 resources.song.name
</span>
</div>
`;
exports[`Details component > Mobile view > renders correctly with releaseDate 1`] = `
<div>
<span>
Jun 15, 2020
</span>
·
<span>
12 resources.song.name
</span>
</div>
`;
exports[`Details component > Mobile view > renders correctly with year range (start and end years) 1`] = `
<div>
<span>
12 resources.song.name
</span>
</div>
`;
exports[`Details component > renders correctly in mobile view 1`] = `
<div>
<span>
♫ Mar 15, 2018
</span>
·
<span>
○ Jun 15, 2020
</span>
·
<span>
12 resources.song.name
</span>
</div>
`;
exports[`Details component > renders correctly with all date fields 1`] = `
<div>
<span>
resources.album.fields.originalDate Mar 15, 2018
</span>
·
<span>
resources.album.fields.releaseDate Jun 15, 2020
</span>
·
<span>
12 resources.song.name
</span>
·
<span>
<span>
01:00:00
</span>
</span>
·
<span>
<span
class="makeStyles-root-6"
>
100 KB
</span>
</span>
</div>
`;
exports[`Details component > renders correctly with date 1`] = `
<div>
<span>
May 1, 2020
</span>
·
<span>
12 resources.song.name
</span>
·
<span>
<span>
01:00:00
</span>
</span>
·
<span>
<span
class="makeStyles-root-2"
>
100 KB
</span>
</span>
</div>
`;
exports[`Details component > renders correctly with date and originalDate 1`] = `
<div>
<span>
resources.album.fields.originalDate Mar 15, 2018
</span>
·
<span>
12 resources.song.name
</span>
·
<span>
<span>
01:00:00
</span>
</span>
·
<span>
<span
class="makeStyles-root-4"
>
100 KB
</span>
</span>
</div>
`;
exports[`Details component > renders correctly with just year range 1`] = `
<div>
<span>
12 resources.song.name
</span>
·
<span>
<span>
01:00:00
</span>
</span>
·
<span>
<span
class="makeStyles-root-1"
>
100 KB
</span>
</span>
</div>
`;
exports[`Details component > renders correctly with originalDate 1`] = `
<div>
<span>
resources.album.fields.originalDate Mar 15, 2018
</span>
·
<span>
12 resources.song.name
</span>
·
<span>
<span>
01:00:00
</span>
</span>
·
<span>
<span
class="makeStyles-root-3"
>
100 KB
</span>
</span>
</div>
`;
exports[`Details component > renders correctly with releaseDate 1`] = `
<div>
<span>
resources.album.fields.releaseDate Jun 15, 2020
</span>
·
<span>
12 resources.song.name
</span>
·
<span>
<span>
01:00:00
</span>
</span>
·
<span>
<span
class="makeStyles-root-5"
>
100 KB
</span>
</span>
</div>
`;

View file

@ -50,7 +50,7 @@ const ArtistDetails = (props) => {
)
}
const AlbumShowLayout = (props) => {
const ArtistShowLayout = (props) => {
const showContext = useShowContext(props)
const record = useRecordContext()
const { width } = props
@ -98,7 +98,7 @@ const ArtistShow = withWidth()((props) => {
const controllerProps = useShowController(props)
return (
<ShowContextProvider value={controllerProps}>
<AlbumShowLayout {...controllerProps} />
<ArtistShowLayout {...controllerProps} />
</ShowContextProvider>
)
})

View file

@ -1,50 +0,0 @@
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,
}

View file

@ -13,7 +13,6 @@ export * from './Pagination'
export * from './PlayButton'
export * from './QuickFilter'
export * from './RangeField'
export * from './RangeDoubleField'
export * from './ShuffleAllButton'
export * from './SimpleList'
export * from './SizeField'

View file

@ -58,6 +58,7 @@
"genre": "Genre",
"compilation": "Compilation",
"year": "Year",
"date": "Recording Date",
"originalDate": "Original",
"releaseDate": "Released",
"releases": "Release |||| Releases",