Merge pull request #7248 from thornbill/card-album-artists
Fix multiple album artists in card footer
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
import React, { type FC } from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
import { ensureArray } from 'utils/array';
|
||||
|
||||
import type { TextLine } from './cardHelper';
|
||||
|
||||
interface CardTextProps {
|
||||
@@ -7,27 +10,33 @@ interface CardTextProps {
|
||||
textLine: TextLine;
|
||||
}
|
||||
|
||||
const SEPARATOR = ' / ';
|
||||
|
||||
const CardText: FC<CardTextProps> = ({ className, textLine }) => {
|
||||
const { title, titleAction } = textLine;
|
||||
// eslint-disable-next-line sonarjs/function-return-type
|
||||
const renderCardText = () => {
|
||||
if (titleAction) {
|
||||
return (
|
||||
<a
|
||||
className='itemAction textActionButton'
|
||||
href={titleAction.url}
|
||||
title={titleAction.title}
|
||||
{...titleAction.dataAttributes}
|
||||
>
|
||||
{titleAction.title}
|
||||
</a>
|
||||
);
|
||||
} else {
|
||||
return title;
|
||||
}
|
||||
};
|
||||
|
||||
return <Box className={className}>{renderCardText()}</Box>;
|
||||
return (
|
||||
<Box className={className}>
|
||||
{titleAction ? (
|
||||
ensureArray(titleAction).map((action, i, arr) => (
|
||||
<>
|
||||
<a
|
||||
className='itemAction textActionButton'
|
||||
href={action.url}
|
||||
title={action.title}
|
||||
{...action.dataAttributes}
|
||||
>
|
||||
{action.title}
|
||||
</a>
|
||||
{/* If there are more items, add the separator */}
|
||||
{(i < arr.length - 1) && SEPARATOR}
|
||||
</>
|
||||
))
|
||||
) : (
|
||||
ensureArray(title).join(SEPARATOR)
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default CardText;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Api } from '@jellyfin/sdk';
|
||||
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
|
||||
import type { BaseItemPerson } from '@jellyfin/sdk/lib/generated-client/models/base-item-person';
|
||||
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type';
|
||||
import { getImageApi } from '@jellyfin/sdk/lib/utils/api/image-api';
|
||||
@@ -12,6 +13,7 @@ import { isUsingLiveTvNaming } from '../cardBuilderUtils';
|
||||
import { getDataAttributes } from 'utils/items';
|
||||
import { ItemKind } from 'types/base/models/item-kind';
|
||||
import { ItemMediaKind } from 'types/base/models/item-media-kind';
|
||||
import { ensureArray } from 'utils/array';
|
||||
|
||||
import type { NullableNumber, NullableString } from 'types/base/common/shared/types';
|
||||
import type { ItemDto } from 'types/base/models/item-dto';
|
||||
@@ -65,8 +67,8 @@ interface TextAction {
|
||||
}
|
||||
|
||||
export interface TextLine {
|
||||
title?: NullableString;
|
||||
titleAction?: TextAction;
|
||||
title?: NullableString | string[];
|
||||
titleAction?: TextAction | TextAction[];
|
||||
}
|
||||
|
||||
export function getTextActionButton(
|
||||
@@ -210,9 +212,25 @@ function getParentTitle(
|
||||
item: ItemDto
|
||||
) {
|
||||
if (isOuterFooter && item.AlbumArtists?.length) {
|
||||
(item.AlbumArtists[0] as ItemDto).Type = ItemKind.MusicArtist;
|
||||
(item.AlbumArtists[0] as ItemDto).IsFolder = true;
|
||||
return getTextActionButton(item.AlbumArtists[0], null, serverId);
|
||||
return item.AlbumArtists
|
||||
.map(artist => {
|
||||
const artistItem: ItemDto = {
|
||||
...artist,
|
||||
Type: BaseItemKind.MusicArtist,
|
||||
IsFolder: true
|
||||
};
|
||||
return getTextActionButton(artistItem, null, serverId);
|
||||
})
|
||||
.reduce((acc, line) => ({
|
||||
title: [
|
||||
...ensureArray(acc.title),
|
||||
...ensureArray(line.title)
|
||||
],
|
||||
titleAction: [
|
||||
...ensureArray(acc.titleAction),
|
||||
...ensureArray(line.titleAction)
|
||||
]
|
||||
}), {});
|
||||
} else {
|
||||
return {
|
||||
title: isUsingLiveTvNaming(item.Type) ?
|
||||
|
||||
@@ -575,9 +575,14 @@ function getCardFooterText(item, apiClient, options, footerClass, progressHtml,
|
||||
if (showOtherText) {
|
||||
if (options.showParentTitle && parentTitleUnderneath) {
|
||||
if (flags.isOuterFooter && item.AlbumArtists?.length) {
|
||||
item.AlbumArtists[0].Type = 'MusicArtist';
|
||||
item.AlbumArtists[0].IsFolder = true;
|
||||
lines.push(getTextActionButton(item.AlbumArtists, null, serverId));
|
||||
const artistText = item.AlbumArtists
|
||||
.map(artist => {
|
||||
artist.Type = BaseItemKind.MusicArtist;
|
||||
artist.IsFolder = true;
|
||||
return getTextActionButton(artist, null, serverId);
|
||||
})
|
||||
.join(' / ');
|
||||
lines.push(artistText);
|
||||
} else {
|
||||
lines.push(escapeHtml(isUsingLiveTvNaming(item.Type) ? item.Name : (item.SeriesName || item.Series || item.Album || item.AlbumArtist || '')));
|
||||
}
|
||||
|
||||
@@ -52,14 +52,6 @@ export function getDisplayName(item, options = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(item)) {
|
||||
if (item.length > 1) {
|
||||
return item.map(i => getDisplayName(i, options)).join(' / ');
|
||||
} else if (item.length === 1) {
|
||||
return item[0].Name;
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
8
src/utils/array.ts
Normal file
8
src/utils/array.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Utility function that converts a value that can be a single item, array of items, null, or undefined to an array.
|
||||
*/
|
||||
export function ensureArray<T>(val: T | T[] | null | undefined): T[] {
|
||||
if (val == null) return [];
|
||||
if (Array.isArray(val)) return val;
|
||||
return [ val ];
|
||||
}
|
||||
Reference in New Issue
Block a user