mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-04 21:17:37 +03:00
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:
parent
88f87e6c4f
commit
2b84c574ba
20 changed files with 929 additions and 155 deletions
|
@ -51,20 +51,6 @@ func legacyMapAlbumName(md Metadata) string {
|
||||||
|
|
||||||
// Keep the TaggedLikePicard logic for backwards compatibility
|
// Keep the TaggedLikePicard logic for backwards compatibility
|
||||||
func legacyReleaseDate(md Metadata) string {
|
func legacyReleaseDate(md Metadata) string {
|
||||||
// Start with defaults
|
_, _, releaseDate := md.mapDates()
|
||||||
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)
|
|
||||||
}
|
|
||||||
return string(releaseDate)
|
return string(releaseDate)
|
||||||
}
|
}
|
||||||
|
|
30
model/metadata/legacy_ids_test.go
Normal file
30
model/metadata/legacy_ids_test.go
Normal 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"),
|
||||||
|
)
|
||||||
|
})
|
|
@ -1,6 +1,7 @@
|
||||||
package metadata
|
package metadata
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cmp"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"maps"
|
"maps"
|
||||||
"math"
|
"math"
|
||||||
|
@ -39,11 +40,9 @@ func (md Metadata) ToMediaFile(libID int, folderID string) model.MediaFile {
|
||||||
mf.ExplicitStatus = md.mapExplicitStatusTag()
|
mf.ExplicitStatus = md.mapExplicitStatusTag()
|
||||||
|
|
||||||
// Dates
|
// Dates
|
||||||
origDate := md.Date(model.TagOriginalDate)
|
date, origDate, relDate := md.mapDates()
|
||||||
mf.OriginalYear, mf.OriginalDate = origDate.Year(), string(origDate)
|
mf.OriginalYear, mf.OriginalDate = origDate.Year(), string(origDate)
|
||||||
relDate := md.Date(model.TagReleaseDate)
|
|
||||||
mf.ReleaseYear, mf.ReleaseDate = relDate.Year(), string(relDate)
|
mf.ReleaseYear, mf.ReleaseDate = relDate.Year(), string(relDate)
|
||||||
date := md.Date(model.TagRecordingDate)
|
|
||||||
mf.Year, mf.Date = date.Year(), string(date)
|
mf.Year, mf.Date = date.Year(), string(date)
|
||||||
|
|
||||||
// MBIDs
|
// MBIDs
|
||||||
|
@ -164,3 +163,22 @@ func (md Metadata) mapExplicitStatusTag() string {
|
||||||
return ""
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ var _ = Describe("ToMediaFile", func() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Describe("Dates", func() {
|
Describe("Dates", func() {
|
||||||
It("should parse the dates like Picard", func() {
|
It("should parse properly tagged dates ", func() {
|
||||||
mf = toMediaFile(model.RawTags{
|
mf = toMediaFile(model.RawTags{
|
||||||
"ORIGINALDATE": {"1978-09-10"},
|
"ORIGINALDATE": {"1978-09-10"},
|
||||||
"DATE": {"1977-03-04"},
|
"DATE": {"1977-03-04"},
|
||||||
|
@ -49,6 +49,32 @@ var _ = Describe("ToMediaFile", func() {
|
||||||
Expect(mf.ReleaseYear).To(Equal(2002))
|
Expect(mf.ReleaseYear).To(Equal(2002))
|
||||||
Expect(mf.ReleaseDate).To(Equal("2002-01-02"))
|
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() {
|
Describe("Lyrics", func() {
|
||||||
|
|
|
@ -90,13 +90,14 @@ var _ = Describe("Metadata", func() {
|
||||||
md = metadata.New(filePath, props)
|
md = metadata.New(filePath, props)
|
||||||
|
|
||||||
Expect(md.All()).To(SatisfyAll(
|
Expect(md.All()).To(SatisfyAll(
|
||||||
HaveLen(5),
|
|
||||||
Not(HaveKey(unknownTag)),
|
Not(HaveKey(unknownTag)),
|
||||||
HaveKeyWithValue(model.TagTrackArtist, []string{"Artist Name", "Second Artist"}),
|
HaveKeyWithValue(model.TagTrackArtist, []string{"Artist Name", "Second Artist"}),
|
||||||
HaveKeyWithValue(model.TagAlbum, []string{"Album Name"}),
|
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.TagGenre, []string{"Pop", "Rock"}),
|
||||||
HaveKeyWithValue(model.TagTrackNumber, []string{"1/10"}),
|
HaveKeyWithValue(model.TagTrackNumber, []string{"1/10"}),
|
||||||
|
HaveLen(6),
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -97,9 +97,10 @@ func NewAlbumRepository(ctx context.Context, db dbx.Builder) model.AlbumReposito
|
||||||
r.tableName = "album"
|
r.tableName = "album"
|
||||||
r.registerModel(&model.Album{}, albumFilters())
|
r.registerModel(&model.Album{}, albumFilters())
|
||||||
r.setSortMappings(map[string]string{
|
r.setSortMappings(map[string]string{
|
||||||
"name": "order_album_name, order_album_artist_name",
|
"name": "order_album_name, order_album_artist_name",
|
||||||
"artist": "compilation, order_album_artist_name, order_album_name",
|
"artist": "compilation, order_album_artist_name, order_album_name",
|
||||||
"album_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",
|
"max_year": "coalesce(nullif(original_date,''), cast(max_year as text)), release_date, name",
|
||||||
"random": "random",
|
"random": "random",
|
||||||
"recently_added": recentlyAddedSort(),
|
"recently_added": recentlyAddedSort(),
|
||||||
|
|
|
@ -57,6 +57,7 @@
|
||||||
"genre": "Gênero",
|
"genre": "Gênero",
|
||||||
"compilation": "Coletânea",
|
"compilation": "Coletânea",
|
||||||
"year": "Ano",
|
"year": "Ano",
|
||||||
|
"date": "Data de Lançamento",
|
||||||
"updatedAt": "Últ. Atualização",
|
"updatedAt": "Últ. Atualização",
|
||||||
"comment": "Comentário",
|
"comment": "Comentário",
|
||||||
"rating": "Classificação",
|
"rating": "Classificação",
|
||||||
|
|
|
@ -118,10 +118,10 @@ main:
|
||||||
aliases: [ tdor, originaldate, ----:com.apple.itunes:originaldate, wm/originalreleasetime, tory, originalyear, ----:com.apple.itunes:originalyear, wm/originalreleaseyear ]
|
aliases: [ tdor, originaldate, ----:com.apple.itunes:originaldate, wm/originalreleasetime, tory, originalyear, ----:com.apple.itunes:originalyear, wm/originalreleaseyear ]
|
||||||
type: date
|
type: date
|
||||||
recordingdate:
|
recordingdate:
|
||||||
aliases: [ tdrc, date, icrd, ©day, wm/year, year ]
|
aliases: [ tdrc, date, recordingdate, icrd, record date ]
|
||||||
type: date
|
type: date
|
||||||
releasedate:
|
releasedate:
|
||||||
aliases: [ tdrl, releasedate ]
|
aliases: [ tdrl, releasedate, ©day, wm/year, year ]
|
||||||
type: date
|
type: date
|
||||||
catalognumber:
|
catalognumber:
|
||||||
aliases: [ txxx:catalognumber, catalognumber, ----:com.apple.itunes:catalognumber, wm/catalogno ]
|
aliases: [ txxx:catalognumber, catalognumber, ----:com.apple.itunes:catalognumber, wm/catalogno ]
|
||||||
|
|
|
@ -62,13 +62,14 @@ func AlbumsByArtistID(artistId string) Options {
|
||||||
}
|
}
|
||||||
|
|
||||||
func AlbumsByYear(fromYear, toYear int) Options {
|
func AlbumsByYear(fromYear, toYear int) Options {
|
||||||
sortOption := "max_year, name"
|
orderOption := ""
|
||||||
if fromYear > toYear {
|
if fromYear > toYear {
|
||||||
fromYear, toYear = toYear, fromYear
|
fromYear, toYear = toYear, fromYear
|
||||||
sortOption = "max_year desc, name"
|
orderOption = "desc"
|
||||||
}
|
}
|
||||||
return addDefaultFilters(Options{
|
return addDefaultFilters(Options{
|
||||||
Sort: sortOption,
|
Sort: "max_year",
|
||||||
|
Order: orderOption,
|
||||||
Filters: Or{
|
Filters: Or{
|
||||||
And{
|
And{
|
||||||
GtOrEq{"min_year": fromYear},
|
GtOrEq{"min_year": fromYear},
|
||||||
|
@ -118,7 +119,7 @@ func SongWithLyrics(artist, title string) Options {
|
||||||
|
|
||||||
func ByGenre(genre string) Options {
|
func ByGenre(genre string) Options {
|
||||||
return addDefaultFilters(Options{
|
return addDefaultFilters(Options{
|
||||||
Sort: "name asc",
|
Sort: "name",
|
||||||
Filters: filterByGenre(genre),
|
Filters: filterByGenre(genre),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -296,7 +296,7 @@ func childFromAlbum(ctx context.Context, al model.Album) responses.Child {
|
||||||
child.Name = al.Name
|
child.Name = al.Name
|
||||||
child.Album = al.Name
|
child.Album = al.Name
|
||||||
child.Artist = al.AlbumArtist
|
child.Artist = al.AlbumArtist
|
||||||
child.Year = int32(al.MaxYear)
|
child.Year = int32(cmp.Or(al.MaxOriginalYear, al.MaxYear))
|
||||||
child.Genre = al.Genre
|
child.Genre = al.Genre
|
||||||
child.CoverArt = al.CoverArtID().String()
|
child.CoverArt = al.CoverArtID().String()
|
||||||
child.Created = &al.CreatedAt
|
child.Created = &al.CreatedAt
|
||||||
|
@ -380,7 +380,7 @@ func buildAlbumID3(ctx context.Context, album model.Album) responses.AlbumID3 {
|
||||||
dir.SongCount = int32(album.SongCount)
|
dir.SongCount = int32(album.SongCount)
|
||||||
dir.Duration = int32(album.Duration)
|
dir.Duration = int32(album.Duration)
|
||||||
dir.PlayCount = album.PlayCount
|
dir.PlayCount = album.PlayCount
|
||||||
dir.Year = int32(album.MaxYear)
|
dir.Year = int32(cmp.Or(album.MaxOriginalYear, album.MaxYear))
|
||||||
dir.Genre = album.Genre
|
dir.Genre = album.Genre
|
||||||
if !album.CreatedAt.IsZero() {
|
if !album.CreatedAt.IsZero() {
|
||||||
dir.Created = &album.CreatedAt
|
dir.Created = &album.CreatedAt
|
||||||
|
|
19
ui/src/album/AlbumDatesField.jsx
Normal file
19
ui/src/album/AlbumDatesField.jsx
Normal 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>
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import { useState, useEffect, useCallback } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
|
@ -10,25 +10,25 @@ import {
|
||||||
withWidth,
|
withWidth,
|
||||||
} from '@material-ui/core'
|
} from '@material-ui/core'
|
||||||
import {
|
import {
|
||||||
useRecordContext,
|
|
||||||
useTranslate,
|
|
||||||
ArrayField,
|
ArrayField,
|
||||||
SingleFieldList,
|
|
||||||
ChipField,
|
ChipField,
|
||||||
Link,
|
Link,
|
||||||
|
SingleFieldList,
|
||||||
|
useRecordContext,
|
||||||
|
useTranslate,
|
||||||
} from 'react-admin'
|
} from 'react-admin'
|
||||||
import Lightbox from 'react-image-lightbox'
|
import Lightbox from 'react-image-lightbox'
|
||||||
import 'react-image-lightbox/style.css'
|
import 'react-image-lightbox/style.css'
|
||||||
import subsonic from '../subsonic'
|
import subsonic from '../subsonic'
|
||||||
import {
|
import {
|
||||||
ArtistLinkField,
|
ArtistLinkField,
|
||||||
|
CollapsibleComment,
|
||||||
DurationField,
|
DurationField,
|
||||||
formatRange,
|
formatRange,
|
||||||
SizeField,
|
|
||||||
LoveButton,
|
LoveButton,
|
||||||
RatingField,
|
RatingField,
|
||||||
|
SizeField,
|
||||||
useAlbumsPerPage,
|
useAlbumsPerPage,
|
||||||
CollapsibleComment,
|
|
||||||
} from '../common'
|
} from '../common'
|
||||||
import config from '../config'
|
import config from '../config'
|
||||||
import { formatFullDate, intersperse } from '../utils'
|
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 isXsmall = useMediaQuery((theme) => theme.breakpoints.down('xs'))
|
||||||
const translate = useTranslate()
|
const translate = useTranslate()
|
||||||
const record = useRecordContext(props)
|
const record = useRecordContext(props)
|
||||||
|
|
||||||
|
// Create an array of detail elements
|
||||||
let details = []
|
let details = []
|
||||||
const addDetail = (obj) => {
|
const addDetail = (obj) => {
|
||||||
const id = details.length
|
const id = details.length
|
||||||
details.push(<span key={`detail-${record.id}-${id}`}>{obj}</span>)
|
details.push(<span key={`detail-${record.id}-${id}`}>{obj}</span>)
|
||||||
}
|
}
|
||||||
|
|
||||||
const originalYearRange = formatRange(record, 'originalYear')
|
// Calculate date related fields
|
||||||
const originalDate = record.originalDate
|
|
||||||
? formatFullDate(record.originalDate)
|
|
||||||
: originalYearRange
|
|
||||||
const yearRange = formatRange(record, 'year')
|
const yearRange = formatRange(record, 'year')
|
||||||
const date = record.date ? formatFullDate(record.date) : yearRange
|
const date = record.date ? formatFullDate(record.date) : yearRange
|
||||||
const releaseDate = record.releaseDate
|
|
||||||
? formatFullDate(record.releaseDate)
|
|
||||||
: date
|
|
||||||
|
|
||||||
const showReleaseDate = date !== releaseDate && releaseDate.length > 3
|
const originalDate = record.originalDate
|
||||||
const showOriginalDate =
|
? formatFullDate(record.originalDate)
|
||||||
date !== originalDate &&
|
: formatRange(record, 'originalYear')
|
||||||
originalDate !== releaseDate &&
|
const releaseDate = record?.releaseDate && formatFullDate(record.releaseDate)
|
||||||
originalDate.length > 3
|
|
||||||
|
|
||||||
showOriginalDate &&
|
const dateToUse = originalDate || date
|
||||||
!isXsmall &&
|
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(
|
addDetail(
|
||||||
<>
|
<>{[getReleaseDateLabel(), releaseDate].filter(Boolean).join(' ')}</>,
|
||||||
{[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(
|
addDetail(
|
||||||
<>
|
<>
|
||||||
{record.songCount +
|
{record.songCount +
|
||||||
|
@ -215,6 +201,7 @@ const Details = (props) => {
|
||||||
!isXsmall && addDetail(<DurationField source={'duration'} />)
|
!isXsmall && addDetail(<DurationField source={'duration'} />)
|
||||||
!isXsmall && addDetail(<SizeField source="size" />)
|
!isXsmall && addDetail(<SizeField source="size" />)
|
||||||
|
|
||||||
|
// Return the details rendered with separators
|
||||||
return <>{intersperse(details, ' · ')}</>
|
return <>{intersperse(details, ' · ')}</>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
327
ui/src/album/AlbumDetails.test.jsx
Normal file
327
ui/src/album/AlbumDetails.test.jsx
Normal 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()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -13,14 +13,10 @@ import { linkToRecord, useListContext, Loading } from 'react-admin'
|
||||||
import { withContentRect } from 'react-measure'
|
import { withContentRect } from 'react-measure'
|
||||||
import { useDrag } from 'react-dnd'
|
import { useDrag } from 'react-dnd'
|
||||||
import subsonic from '../subsonic'
|
import subsonic from '../subsonic'
|
||||||
import {
|
import { AlbumContextMenu, PlayButton, ArtistLinkField } from '../common'
|
||||||
AlbumContextMenu,
|
|
||||||
PlayButton,
|
|
||||||
ArtistLinkField,
|
|
||||||
RangeDoubleField,
|
|
||||||
} from '../common'
|
|
||||||
import { DraggableTypes } from '../consts'
|
import { DraggableTypes } from '../consts'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
|
import { AlbumDatesField } from './AlbumDatesField.jsx'
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
(theme) => ({
|
(theme) => ({
|
||||||
|
@ -187,16 +183,7 @@ const AlbumGridTile = ({ showArtist, record, basePath, ...props }) => {
|
||||||
{showArtist ? (
|
{showArtist ? (
|
||||||
<ArtistLinkField record={record} className={classes.albumSubtitle} />
|
<ArtistLinkField record={record} className={classes.albumSubtitle} />
|
||||||
) : (
|
) : (
|
||||||
<RangeDoubleField
|
<AlbumDatesField record={record} className={classes.albumSubtitle} />
|
||||||
record={record}
|
|
||||||
source={'year'}
|
|
||||||
symbol1={'♫'}
|
|
||||||
symbol2={'○'}
|
|
||||||
separator={' · '}
|
|
||||||
sortBy={'max_year'}
|
|
||||||
sortByOrder={'DESC'}
|
|
||||||
className={classes.albumSubtitle}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -20,6 +20,7 @@ import {
|
||||||
ArtistLinkField,
|
ArtistLinkField,
|
||||||
MultiLineTextField,
|
MultiLineTextField,
|
||||||
ParticipantsInfo,
|
ParticipantsInfo,
|
||||||
|
RangeField,
|
||||||
} from '../common'
|
} from '../common'
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
|
@ -47,6 +48,20 @@ const AlbumInfo = (props) => {
|
||||||
</SingleFieldList>
|
</SingleFieldList>
|
||||||
</ArrayField>
|
</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: (
|
recordLabel: (
|
||||||
<FunctionField
|
<FunctionField
|
||||||
source={'recordLabel'}
|
source={'recordLabel'}
|
||||||
|
|
425
ui/src/album/__snapshots__/AlbumDetails.test.jsx.snap
Normal file
425
ui/src/album/__snapshots__/AlbumDetails.test.jsx.snap
Normal 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>
|
||||||
|
`;
|
|
@ -50,7 +50,7 @@ const ArtistDetails = (props) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const AlbumShowLayout = (props) => {
|
const ArtistShowLayout = (props) => {
|
||||||
const showContext = useShowContext(props)
|
const showContext = useShowContext(props)
|
||||||
const record = useRecordContext()
|
const record = useRecordContext()
|
||||||
const { width } = props
|
const { width } = props
|
||||||
|
@ -98,7 +98,7 @@ const ArtistShow = withWidth()((props) => {
|
||||||
const controllerProps = useShowController(props)
|
const controllerProps = useShowController(props)
|
||||||
return (
|
return (
|
||||||
<ShowContextProvider value={controllerProps}>
|
<ShowContextProvider value={controllerProps}>
|
||||||
<AlbumShowLayout {...controllerProps} />
|
<ArtistShowLayout {...controllerProps} />
|
||||||
</ShowContextProvider>
|
</ShowContextProvider>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -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,
|
|
||||||
}
|
|
|
@ -13,7 +13,6 @@ export * from './Pagination'
|
||||||
export * from './PlayButton'
|
export * from './PlayButton'
|
||||||
export * from './QuickFilter'
|
export * from './QuickFilter'
|
||||||
export * from './RangeField'
|
export * from './RangeField'
|
||||||
export * from './RangeDoubleField'
|
|
||||||
export * from './ShuffleAllButton'
|
export * from './ShuffleAllButton'
|
||||||
export * from './SimpleList'
|
export * from './SimpleList'
|
||||||
export * from './SizeField'
|
export * from './SizeField'
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
"genre": "Genre",
|
"genre": "Genre",
|
||||||
"compilation": "Compilation",
|
"compilation": "Compilation",
|
||||||
"year": "Year",
|
"year": "Year",
|
||||||
|
"date": "Recording Date",
|
||||||
"originalDate": "Original",
|
"originalDate": "Original",
|
||||||
"releaseDate": "Released",
|
"releaseDate": "Released",
|
||||||
"releases": "Release |||| Releases",
|
"releases": "Release |||| Releases",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue