* fix(ui): ensure album tracks are always ordered by disc and track number (fixes #3720) * refactor(ui): remove obsolete release date grouping logic from SongDatagrid and AlbumSongs * fix(ui): ensure correct album track ordering in context menu and play button * fix: Update album sort to use album_id instead of release_date * refactor: Adjust filters in PlayButton and AlbumContextMenu * fix: correct typo in comment regarding participants in GetMissingAndMatching function * fix: prevent visual separation of tracks on same disc Removes the leftover `releaseDate` check from the `firstTracksOfDiscs` calculation in `SongDatagridBody`. This check caused unnecessary `DiscSubtitleRow` insertions when tracks on the same disc had different release dates, leading to an incorrect visual grouping that resembled a multi-disc layout. This change ensures disc subtitles are only shown when the actual `discNumber` changes, correcting the UI presentation issue reported in issue #3720 after PR #3975. * fix: remove remaining releaseDate references in SongDatagrid Cleaned up leftover `releaseDate` references in `SongDatagrid.jsx`: - Removed `releaseDate` parameter and usage from `handlePlaySubset` in `DiscSubtitleRow`. - Removed `releaseDate` prop passed to `AlbumContextMenu` in `DiscSubtitleRow`. - Removed `releaseDate` from the drag item data in `SongDatagridRow`. - Removed `releaseDate` parameter and the corresponding `else` block from the `playSubset` function in `SongDatagridBody`. This ensures the component consistently uses `discNumber` for grouping and playback actions initiated from the disc subtitle, fully resolving the inconsistencies related to issue #3720.
This commit is contained in:
@@ -77,7 +77,7 @@ func NewMediaFileRepository(ctx context.Context, db dbx.Builder) model.MediaFile
|
|||||||
"title": "order_title",
|
"title": "order_title",
|
||||||
"artist": "order_artist_name, order_album_name, release_date, disc_number, track_number",
|
"artist": "order_artist_name, order_album_name, release_date, disc_number, track_number",
|
||||||
"album_artist": "order_album_artist_name, order_album_name, release_date, disc_number, track_number",
|
"album_artist": "order_album_artist_name, order_album_name, release_date, disc_number, track_number",
|
||||||
"album": "order_album_name, release_date, disc_number, track_number, order_artist_name, title",
|
"album": "order_album_name, album_id, disc_number, track_number, order_artist_name, title",
|
||||||
"random": "random",
|
"random": "random",
|
||||||
"created_at": "media_file.created_at",
|
"created_at": "media_file.created_at",
|
||||||
"starred_at": "starred, starred_at",
|
"starred_at": "starred, starred_at",
|
||||||
@@ -242,7 +242,7 @@ func (r *mediaFileRepository) MarkMissingByFolder(missing bool, folderIDs ...str
|
|||||||
|
|
||||||
// GetMissingAndMatching returns all mediafiles that are missing and their potential matches (comparing PIDs)
|
// GetMissingAndMatching returns all mediafiles that are missing and their potential matches (comparing PIDs)
|
||||||
// that were added/updated after the last scan started. The result is ordered by PID.
|
// that were added/updated after the last scan started. The result is ordered by PID.
|
||||||
// It does not need to load bookmarks, annotations and participnts, as they are not used by the scanner.
|
// It does not need to load bookmarks, annotations and participants, as they are not used by the scanner.
|
||||||
func (r *mediaFileRepository) GetMissingAndMatching(libId int) (model.MediaFileCursor, error) {
|
func (r *mediaFileRepository) GetMissingAndMatching(libId int) (model.MediaFileCursor, error) {
|
||||||
subQ := r.newSelect().Columns("pid").
|
subQ := r.newSelect().Columns("pid").
|
||||||
Where(And{
|
Where(And{
|
||||||
|
|||||||
@@ -185,7 +185,6 @@ const AlbumSongs = (props) => {
|
|||||||
{...props}
|
{...props}
|
||||||
hasBulkActions={true}
|
hasBulkActions={true}
|
||||||
showDiscSubtitles={true}
|
showDiscSubtitles={true}
|
||||||
showReleaseDivider={true}
|
|
||||||
contextAlwaysVisible={!isDesktop}
|
contextAlwaysVisible={!isDesktop}
|
||||||
classes={{ row: classes.row }}
|
classes={{ row: classes.row }}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -231,7 +231,6 @@ export const AlbumContextMenu = (props) =>
|
|||||||
sort: { field: 'album', order: 'ASC' },
|
sort: { field: 'album', order: 'ASC' },
|
||||||
filter: {
|
filter: {
|
||||||
album_id: props.record.id,
|
album_id: props.record.id,
|
||||||
release_date: props.releaseDate,
|
|
||||||
disc_number: props.discNumber,
|
disc_number: props.discNumber,
|
||||||
missing: false,
|
missing: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ export const PlayButton = ({ record, size, className }) => {
|
|||||||
sort: { field: 'album', order: 'ASC' },
|
sort: { field: 'album', order: 'ASC' },
|
||||||
filter: {
|
filter: {
|
||||||
album_id: record.id,
|
album_id: record.id,
|
||||||
release_date: record.releaseDate,
|
|
||||||
disc_number: record.discNumber,
|
disc_number: record.discNumber,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -59,59 +59,12 @@ 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>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
ReleaseRow.displayName = 'ReleaseRow'
|
|
||||||
|
|
||||||
const DiscSubtitleRow = forwardRef(
|
const DiscSubtitleRow = forwardRef(
|
||||||
({ record, onClick, colSpan, contextAlwaysVisible }, ref) => {
|
({ record, onClick, colSpan, contextAlwaysVisible }, ref) => {
|
||||||
const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md'))
|
const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md'))
|
||||||
const classes = useStyles({ isDesktop })
|
const classes = useStyles({ isDesktop })
|
||||||
const handlePlaySubset = (releaseDate, discNumber) => () => {
|
const handlePlaySubset = (discNumber) => () => {
|
||||||
onClick(releaseDate, discNumber)
|
onClick(discNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
let subtitle = []
|
let subtitle = []
|
||||||
@@ -126,7 +79,7 @@ const DiscSubtitleRow = forwardRef(
|
|||||||
<TableRow
|
<TableRow
|
||||||
hover
|
hover
|
||||||
ref={ref}
|
ref={ref}
|
||||||
onClick={handlePlaySubset(record.releaseDate, record.discNumber)}
|
onClick={handlePlaySubset(record.discNumber)}
|
||||||
className={classes.row}
|
className={classes.row}
|
||||||
>
|
>
|
||||||
<TableCell colSpan={colSpan}>
|
<TableCell colSpan={colSpan}>
|
||||||
@@ -139,7 +92,6 @@ const DiscSubtitleRow = forwardRef(
|
|||||||
<AlbumContextMenu
|
<AlbumContextMenu
|
||||||
record={{ id: record.albumId }}
|
record={{ id: record.albumId }}
|
||||||
discNumber={record.discNumber}
|
discNumber={record.discNumber}
|
||||||
releaseDate={record.releaseDate}
|
|
||||||
showLove={false}
|
showLove={false}
|
||||||
className={classes.contextMenu}
|
className={classes.contextMenu}
|
||||||
hideShare={true}
|
hideShare={true}
|
||||||
@@ -158,7 +110,6 @@ export const SongDatagridRow = ({
|
|||||||
record,
|
record,
|
||||||
children,
|
children,
|
||||||
firstTracksOfDiscs,
|
firstTracksOfDiscs,
|
||||||
firstTracksOfReleases,
|
|
||||||
contextAlwaysVisible,
|
contextAlwaysVisible,
|
||||||
onClickSubset,
|
onClickSubset,
|
||||||
className,
|
className,
|
||||||
@@ -176,7 +127,6 @@ export const SongDatagridRow = ({
|
|||||||
discs: [
|
discs: [
|
||||||
{
|
{
|
||||||
albumId: record?.albumId,
|
albumId: record?.albumId,
|
||||||
releaseDate: record?.releaseDate,
|
|
||||||
discNumber: record?.discNumber,
|
discNumber: record?.discNumber,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -209,15 +159,6 @@ export const SongDatagridRow = ({
|
|||||||
const childCount = fields.length
|
const childCount = fields.length
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{firstTracksOfReleases.has(record.id) && (
|
|
||||||
<ReleaseRow
|
|
||||||
ref={dragDiscRef}
|
|
||||||
record={record}
|
|
||||||
onClick={onClickSubset}
|
|
||||||
contextAlwaysVisible={contextAlwaysVisible}
|
|
||||||
colSpan={childCount + (rest.expand ? 1 : 0)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{firstTracksOfDiscs.has(record.id) && (
|
{firstTracksOfDiscs.has(record.id) && (
|
||||||
<DiscSubtitleRow
|
<DiscSubtitleRow
|
||||||
ref={dragDiscRef}
|
ref={dragDiscRef}
|
||||||
@@ -244,7 +185,6 @@ SongDatagridRow.propTypes = {
|
|||||||
record: PropTypes.object,
|
record: PropTypes.object,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
firstTracksOfDiscs: PropTypes.instanceOf(Set),
|
firstTracksOfDiscs: PropTypes.instanceOf(Set),
|
||||||
firstTracksOfReleases: PropTypes.instanceOf(Set),
|
|
||||||
contextAlwaysVisible: PropTypes.bool,
|
contextAlwaysVisible: PropTypes.bool,
|
||||||
onClickSubset: PropTypes.func,
|
onClickSubset: PropTypes.func,
|
||||||
}
|
}
|
||||||
@@ -256,23 +196,16 @@ SongDatagridRow.defaultProps = {
|
|||||||
const SongDatagridBody = ({
|
const SongDatagridBody = ({
|
||||||
contextAlwaysVisible,
|
contextAlwaysVisible,
|
||||||
showDiscSubtitles,
|
showDiscSubtitles,
|
||||||
showReleaseDivider,
|
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const { ids, data } = rest
|
const { ids, data } = rest
|
||||||
|
|
||||||
const playSubset = useCallback(
|
const playSubset = useCallback(
|
||||||
(releaseDate, discNumber) => {
|
(discNumber) => {
|
||||||
let idsToPlay = []
|
let idsToPlay = []
|
||||||
if (discNumber !== undefined) {
|
if (discNumber !== undefined) {
|
||||||
idsToPlay = ids.filter(
|
idsToPlay = ids.filter((id) => data[id].discNumber === discNumber)
|
||||||
(id) =>
|
|
||||||
data[id].releaseDate === releaseDate &&
|
|
||||||
data[id].discNumber === discNumber,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
idsToPlay = ids.filter((id) => data[id].releaseDate === releaseDate)
|
|
||||||
}
|
}
|
||||||
dispatch(
|
dispatch(
|
||||||
playTracks(
|
playTracks(
|
||||||
@@ -297,8 +230,7 @@ const SongDatagridBody = ({
|
|||||||
foundSubtitle = foundSubtitle || data[id].discSubtitle
|
foundSubtitle = foundSubtitle || data[id].discSubtitle
|
||||||
if (
|
if (
|
||||||
acc.length === 0 ||
|
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)
|
acc.push(id)
|
||||||
}
|
}
|
||||||
@@ -311,37 +243,12 @@ const SongDatagridBody = ({
|
|||||||
return set
|
return set
|
||||||
}, [ids, data, showDiscSubtitles])
|
}, [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 (
|
return (
|
||||||
<PureDatagridBody
|
<PureDatagridBody
|
||||||
{...rest}
|
{...rest}
|
||||||
row={
|
row={
|
||||||
<SongDatagridRow
|
<SongDatagridRow
|
||||||
firstTracksOfDiscs={firstTracksOfDiscs}
|
firstTracksOfDiscs={firstTracksOfDiscs}
|
||||||
firstTracksOfReleases={firstTracksOfReleases}
|
|
||||||
contextAlwaysVisible={contextAlwaysVisible}
|
contextAlwaysVisible={contextAlwaysVisible}
|
||||||
onClickSubset={playSubset}
|
onClickSubset={playSubset}
|
||||||
/>
|
/>
|
||||||
@@ -353,7 +260,6 @@ const SongDatagridBody = ({
|
|||||||
export const SongDatagrid = ({
|
export const SongDatagrid = ({
|
||||||
contextAlwaysVisible,
|
contextAlwaysVisible,
|
||||||
showDiscSubtitles,
|
showDiscSubtitles,
|
||||||
showReleaseDivider,
|
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
@@ -366,7 +272,6 @@ export const SongDatagrid = ({
|
|||||||
<SongDatagridBody
|
<SongDatagridBody
|
||||||
contextAlwaysVisible={contextAlwaysVisible}
|
contextAlwaysVisible={contextAlwaysVisible}
|
||||||
showDiscSubtitles={showDiscSubtitles}
|
showDiscSubtitles={showDiscSubtitles}
|
||||||
showReleaseDivider={showReleaseDivider}
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -376,6 +281,5 @@ export const SongDatagrid = ({
|
|||||||
SongDatagrid.propTypes = {
|
SongDatagrid.propTypes = {
|
||||||
contextAlwaysVisible: PropTypes.bool,
|
contextAlwaysVisible: PropTypes.bool,
|
||||||
showDiscSubtitles: PropTypes.bool,
|
showDiscSubtitles: PropTypes.bool,
|
||||||
showReleaseDivider: PropTypes.bool,
|
|
||||||
classes: PropTypes.object,
|
classes: PropTypes.object,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user