diff --git a/src/components/cardbuilder/Card/CardText.tsx b/src/components/cardbuilder/Card/CardText.tsx index b1230c5ec8..ef528939ef 100644 --- a/src/components/cardbuilder/Card/CardText.tsx +++ b/src/components/cardbuilder/Card/CardText.tsx @@ -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 = ({ className, textLine }) => { const { title, titleAction } = textLine; - // eslint-disable-next-line sonarjs/function-return-type - const renderCardText = () => { - if (titleAction) { - return ( - - {titleAction.title} - - ); - } else { - return title; - } - }; - return {renderCardText()}; + return ( + + {titleAction ? ( + ensureArray(titleAction).map((action, i, arr) => ( + <> + + {action.title} + + {/* If there are more items, add the separator */} + {(i < arr.length - 1) && SEPARATOR} + + )) + ) : ( + ensureArray(title).join(SEPARATOR) + )} + + ); }; export default CardText; diff --git a/src/components/cardbuilder/Card/cardHelper.ts b/src/components/cardbuilder/Card/cardHelper.ts index 070c8085df..3044105a5f 100644 --- a/src/components/cardbuilder/Card/cardHelper.ts +++ b/src/components/cardbuilder/Card/cardHelper.ts @@ -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) ? diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js index c48cb82ca8..43298704db 100644 --- a/src/components/cardbuilder/cardBuilder.js +++ b/src/components/cardbuilder/cardBuilder.js @@ -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 || ''))); } diff --git a/src/components/itemHelper.js b/src/components/itemHelper.js index 965b33da48..f21ce6e063 100644 --- a/src/components/itemHelper.js +++ b/src/components/itemHelper.js @@ -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; } diff --git a/src/utils/array.ts b/src/utils/array.ts new file mode 100644 index 0000000000..ca648e3514 --- /dev/null +++ b/src/utils/array.ts @@ -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(val: T | T[] | null | undefined): T[] { + if (val == null) return []; + if (Array.isArray(val)) return val; + return [ val ]; +}