mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
715 lines
19 KiB
TypeScript
715 lines
19 KiB
TypeScript
import { Api } from '@jellyfin/sdk';
|
|
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';
|
|
|
|
import { appRouter } from 'components/router/appRouter';
|
|
import layoutManager from 'components/layoutManager';
|
|
import itemHelper from 'components/itemHelper';
|
|
import globalize from 'lib/globalize';
|
|
import datetime from 'scripts/datetime';
|
|
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 type { NullableNumber, NullableString } from 'types/base/common/shared/types';
|
|
import type { ItemDto } from 'types/base/models/item-dto';
|
|
import type { CardOptions } from 'types/cardOptions';
|
|
import type { DataAttributes } from 'types/dataAttributes';
|
|
|
|
export function getCardLogoUrl(
|
|
item: ItemDto,
|
|
api: Api | undefined,
|
|
cardOptions: CardOptions
|
|
) {
|
|
let imgType;
|
|
let imgTag;
|
|
let itemId;
|
|
const logoHeight = 40;
|
|
|
|
if (cardOptions.showChannelLogo && item.ChannelPrimaryImageTag) {
|
|
imgType = ImageType.Primary;
|
|
imgTag = item.ChannelPrimaryImageTag;
|
|
itemId = item.ChannelId;
|
|
} else if (cardOptions.showLogo && item.ParentLogoImageTag) {
|
|
imgType = ImageType.Logo;
|
|
imgTag = item.ParentLogoImageTag;
|
|
itemId = item.ParentLogoItemId;
|
|
}
|
|
|
|
if (!itemId) {
|
|
itemId = item.Id;
|
|
}
|
|
|
|
if (api && imgTag && imgType && itemId) {
|
|
const response = getImageApi(api).getItemImageUrlById(itemId, imgType, {
|
|
height: logoHeight,
|
|
tag: imgTag
|
|
});
|
|
|
|
return {
|
|
logoUrl: response
|
|
};
|
|
}
|
|
|
|
return {
|
|
logoUrl: undefined
|
|
};
|
|
}
|
|
|
|
interface TextAction {
|
|
url: string;
|
|
title: string;
|
|
dataAttributes: DataAttributes
|
|
}
|
|
|
|
export interface TextLine {
|
|
title?: NullableString;
|
|
titleAction?: TextAction;
|
|
}
|
|
|
|
export function getTextActionButton(
|
|
item: ItemDto,
|
|
text?: NullableString,
|
|
serverId?: NullableString
|
|
): TextLine {
|
|
const title = text || itemHelper.getDisplayName(item);
|
|
|
|
if (layoutManager.tv) {
|
|
return {
|
|
title
|
|
};
|
|
}
|
|
|
|
const url = appRouter.getRouteUrl(item);
|
|
|
|
const dataAttributes = getDataAttributes(
|
|
{
|
|
action: 'link',
|
|
itemServerId: serverId ?? item.ServerId,
|
|
itemId: item.Id,
|
|
itemChannelId: item.ChannelId,
|
|
itemType: item.Type,
|
|
itemMediaType: item.MediaType,
|
|
itemCollectionType: item.CollectionType,
|
|
itemIsFolder: item.IsFolder
|
|
}
|
|
);
|
|
|
|
return {
|
|
titleAction: {
|
|
url,
|
|
title,
|
|
dataAttributes
|
|
}
|
|
};
|
|
}
|
|
|
|
export function getAirTimeText(
|
|
item: ItemDto,
|
|
showAirDateTime: boolean | undefined,
|
|
showAirEndTime: boolean | undefined
|
|
) {
|
|
let airTimeText = '';
|
|
|
|
if (item.StartDate) {
|
|
try {
|
|
let date = datetime.parseISO8601Date(item.StartDate);
|
|
|
|
if (showAirDateTime) {
|
|
airTimeText
|
|
+= datetime.toLocaleDateString(date, {
|
|
weekday: 'short',
|
|
month: 'short',
|
|
day: 'numeric'
|
|
}) + ' ';
|
|
}
|
|
|
|
airTimeText += datetime.getDisplayTime(date);
|
|
|
|
if (item.EndDate && showAirEndTime) {
|
|
date = datetime.parseISO8601Date(item.EndDate);
|
|
airTimeText += ' - ' + datetime.getDisplayTime(date);
|
|
}
|
|
} catch {
|
|
console.error('error parsing date: ' + item.StartDate);
|
|
}
|
|
}
|
|
return airTimeText;
|
|
}
|
|
|
|
function isGenreOrStudio(itemType: ItemKind) {
|
|
return itemType === ItemKind.Genre || itemType === ItemKind.Studio;
|
|
}
|
|
|
|
function isMusicGenreOrMusicArtist(
|
|
itemType: ItemKind,
|
|
context: NullableString
|
|
) {
|
|
return itemType === ItemKind.MusicGenre || context === 'MusicArtist';
|
|
}
|
|
|
|
function getMovieCount(itemMovieCount: NullableNumber) {
|
|
if (itemMovieCount) {
|
|
return itemMovieCount === 1 ?
|
|
globalize.translate('ValueOneMovie') :
|
|
globalize.translate('ValueMovieCount', itemMovieCount);
|
|
}
|
|
}
|
|
|
|
function getSeriesCount(itemSeriesCount: NullableNumber) {
|
|
if (itemSeriesCount) {
|
|
return itemSeriesCount === 1 ?
|
|
globalize.translate('ValueOneSeries') :
|
|
globalize.translate('ValueSeriesCount', itemSeriesCount);
|
|
}
|
|
}
|
|
|
|
function getEpisodeCount(itemEpisodeCount: NullableNumber) {
|
|
if (itemEpisodeCount) {
|
|
return itemEpisodeCount === 1 ?
|
|
globalize.translate('ValueOneEpisode') :
|
|
globalize.translate('ValueEpisodeCount', itemEpisodeCount);
|
|
}
|
|
}
|
|
|
|
function getAlbumCount(itemAlbumCount: NullableNumber) {
|
|
if (itemAlbumCount) {
|
|
return itemAlbumCount === 1 ?
|
|
globalize.translate('ValueOneAlbum') :
|
|
globalize.translate('ValueAlbumCount', itemAlbumCount);
|
|
}
|
|
}
|
|
|
|
function getSongCount(itemSongCount: NullableNumber) {
|
|
if (itemSongCount) {
|
|
return itemSongCount === 1 ?
|
|
globalize.translate('ValueOneSong') :
|
|
globalize.translate('ValueSongCount', itemSongCount);
|
|
}
|
|
}
|
|
|
|
function getMusicVideoCount(itemMusicVideoCount: NullableNumber) {
|
|
if (itemMusicVideoCount) {
|
|
return itemMusicVideoCount === 1 ?
|
|
globalize.translate('ValueOneMusicVideo') :
|
|
globalize.translate('ValueMusicVideoCount', itemMusicVideoCount);
|
|
}
|
|
}
|
|
|
|
function getRecursiveItemCount(itemRecursiveItemCount: NullableNumber) {
|
|
return itemRecursiveItemCount === 1 ?
|
|
globalize.translate('ValueOneEpisode') :
|
|
globalize.translate('ValueEpisodeCount', itemRecursiveItemCount);
|
|
}
|
|
|
|
function getParentTitle(
|
|
isOuterFooter: boolean,
|
|
serverId: NullableString,
|
|
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);
|
|
} else {
|
|
return {
|
|
title: isUsingLiveTvNaming(item.Type) ?
|
|
item.Name :
|
|
item.SeriesName
|
|
|| item.Series
|
|
|| item.Album
|
|
|| item.AlbumArtist
|
|
|| ''
|
|
};
|
|
}
|
|
}
|
|
|
|
function getRunTimeTicks(itemRunTimeTicks: NullableNumber) {
|
|
if (itemRunTimeTicks) {
|
|
let minutes = itemRunTimeTicks / 600000000;
|
|
|
|
minutes = minutes || 1;
|
|
|
|
return globalize.translate('ValueMinutes', Math.round(minutes));
|
|
} else {
|
|
return globalize.translate('ValueMinutes', 0);
|
|
}
|
|
}
|
|
|
|
export function getItemCounts(cardOptions: CardOptions, item: ItemDto) {
|
|
const counts: string[] = [];
|
|
|
|
const addCount = (text: NullableString) => {
|
|
if (text) {
|
|
counts.push(text);
|
|
}
|
|
};
|
|
|
|
if (item.Type === ItemKind.Playlist) {
|
|
const runTimeTicksText = getRunTimeTicks(item.RunTimeTicks);
|
|
addCount(runTimeTicksText);
|
|
} else if (isGenreOrStudio(item.Type)) {
|
|
const movieCountText = getMovieCount(item.MovieCount);
|
|
addCount(movieCountText);
|
|
|
|
const seriesCountText = getSeriesCount(item.SeriesCount);
|
|
addCount(seriesCountText);
|
|
|
|
const episodeCountText = getEpisodeCount(item.EpisodeCount);
|
|
addCount(episodeCountText);
|
|
} else if (isMusicGenreOrMusicArtist(item.Type, cardOptions.context)) {
|
|
const albumCountText = getAlbumCount(item.AlbumCount);
|
|
addCount(albumCountText);
|
|
|
|
const songCountText = getSongCount(item.SongCount);
|
|
addCount(songCountText);
|
|
|
|
const musicVideoCountText = getMusicVideoCount(item.MusicVideoCount);
|
|
addCount(musicVideoCountText);
|
|
} else if (item.Type === ItemKind.Series) {
|
|
const recursiveItemCountText = getRecursiveItemCount(
|
|
item.RecursiveItemCount
|
|
);
|
|
addCount(recursiveItemCountText);
|
|
}
|
|
|
|
return counts.join(', ');
|
|
}
|
|
|
|
export function shouldShowTitle(
|
|
showTitle: boolean | string | undefined,
|
|
itemType: ItemKind
|
|
) {
|
|
return (
|
|
Boolean(showTitle)
|
|
|| itemType === ItemKind.PhotoAlbum
|
|
|| itemType === ItemKind.Folder
|
|
);
|
|
}
|
|
|
|
export function shouldShowOtherText(
|
|
isOuterFooter: boolean,
|
|
overlayText: boolean | undefined
|
|
) {
|
|
return isOuterFooter ? !overlayText : overlayText;
|
|
}
|
|
|
|
export function shouldShowParentTitleUnderneath(
|
|
itemType: ItemKind
|
|
) {
|
|
return (
|
|
itemType === ItemKind.MusicAlbum
|
|
|| itemType === ItemKind.Audio
|
|
|| itemType === ItemKind.MusicVideo
|
|
);
|
|
}
|
|
|
|
function shouldShowMediaTitle(
|
|
titleAdded: boolean,
|
|
showTitle: boolean,
|
|
forceName: boolean,
|
|
cardOptions: CardOptions,
|
|
textLines: TextLine[]
|
|
) {
|
|
let showMediaTitle =
|
|
(showTitle && !titleAdded)
|
|
|| (cardOptions.showParentTitleOrTitle && !textLines.length);
|
|
if (!showMediaTitle && !titleAdded && (showTitle || forceName)) {
|
|
showMediaTitle = true;
|
|
}
|
|
return showMediaTitle;
|
|
}
|
|
|
|
function shouldShowExtraType(itemExtraType: NullableString) {
|
|
return !!(itemExtraType && itemExtraType !== 'Unknown');
|
|
}
|
|
|
|
function shouldShowSeriesYearOrYear(
|
|
showYear: string | boolean | undefined,
|
|
showSeriesYear: boolean | undefined
|
|
) {
|
|
return Boolean(showYear) || showSeriesYear;
|
|
}
|
|
|
|
function shouldShowCurrentProgram(
|
|
showCurrentProgram: boolean | undefined,
|
|
itemType: ItemKind
|
|
) {
|
|
return showCurrentProgram && itemType === ItemKind.TvChannel;
|
|
}
|
|
|
|
function shouldShowCurrentProgramTime(
|
|
showCurrentProgramTime: boolean | undefined,
|
|
itemType: ItemKind
|
|
) {
|
|
return showCurrentProgramTime && itemType === ItemKind.TvChannel;
|
|
}
|
|
|
|
function shouldShowPersonRoleOrType(
|
|
showPersonRoleOrType: boolean | undefined,
|
|
item: ItemDto
|
|
) {
|
|
return !!(showPersonRoleOrType && (item as BaseItemPerson).Role);
|
|
}
|
|
|
|
function shouldShowParentTitle(
|
|
showParentTitle: boolean | undefined,
|
|
parentTitleUnderneath: boolean
|
|
) {
|
|
return showParentTitle && parentTitleUnderneath;
|
|
}
|
|
|
|
function addOtherText(
|
|
cardOptions: CardOptions,
|
|
parentTitleUnderneath: boolean,
|
|
isOuterFooter: boolean,
|
|
item: ItemDto,
|
|
addTextLine: (val: TextLine) => void,
|
|
serverId: NullableString
|
|
) {
|
|
if (
|
|
shouldShowParentTitle(
|
|
cardOptions.showParentTitle,
|
|
parentTitleUnderneath
|
|
)
|
|
) {
|
|
addTextLine(getParentTitle(isOuterFooter, serverId, item));
|
|
}
|
|
|
|
if (shouldShowExtraType(item.ExtraType)) {
|
|
addTextLine({ title: globalize.translate(item.ExtraType) });
|
|
}
|
|
|
|
if (cardOptions.showItemCounts) {
|
|
addTextLine({ title: getItemCounts(cardOptions, item) });
|
|
}
|
|
|
|
if (cardOptions.textLines) {
|
|
addTextLine({ title: getAdditionalLines(cardOptions.textLines, item) });
|
|
}
|
|
|
|
if (cardOptions.showSongCount) {
|
|
addTextLine({ title: getSongCount(item.SongCount) });
|
|
}
|
|
|
|
if (cardOptions.showPremiereDate) {
|
|
addTextLine({ title: getPremiereDate(item.PremiereDate) });
|
|
}
|
|
|
|
if (
|
|
shouldShowSeriesYearOrYear(
|
|
cardOptions.showYear,
|
|
cardOptions.showSeriesYear
|
|
)
|
|
) {
|
|
addTextLine({ title: getProductionYear(item) });
|
|
}
|
|
|
|
if (cardOptions.showRuntime) {
|
|
addTextLine({ title: getRunTime(item.RunTimeTicks) });
|
|
}
|
|
|
|
if (cardOptions.showAirTime) {
|
|
addTextLine({
|
|
title: getAirTimeText(
|
|
item,
|
|
cardOptions.showAirDateTime,
|
|
cardOptions.showAirEndTime
|
|
)
|
|
});
|
|
}
|
|
|
|
if (cardOptions.showChannelName) {
|
|
addTextLine(getChannelName(item));
|
|
}
|
|
|
|
if (shouldShowCurrentProgram(cardOptions.showCurrentProgram, item.Type)) {
|
|
addTextLine({ title: getCurrentProgramName(item.CurrentProgram) });
|
|
}
|
|
|
|
if (
|
|
shouldShowCurrentProgramTime(
|
|
cardOptions.showCurrentProgramTime,
|
|
item.Type
|
|
)
|
|
) {
|
|
addTextLine({ title: getCurrentProgramTime(item.CurrentProgram) });
|
|
}
|
|
|
|
if (cardOptions.showSeriesTimerTime) {
|
|
addTextLine({ title: getSeriesTimerTime(item) });
|
|
}
|
|
|
|
if (cardOptions.showSeriesTimerChannel) {
|
|
addTextLine({ title: getSeriesTimerChannel(item) });
|
|
}
|
|
|
|
if (shouldShowPersonRoleOrType(cardOptions.showCurrentProgramTime, item)) {
|
|
addTextLine({
|
|
title: globalize.translate(
|
|
'PersonRole',
|
|
(item as BaseItemPerson).Role
|
|
)
|
|
});
|
|
}
|
|
}
|
|
|
|
function getSeriesTimerChannel(item: ItemDto) {
|
|
if (item.RecordAnyChannel) {
|
|
return globalize.translate('AllChannels');
|
|
} else {
|
|
return item.ChannelName || '' || globalize.translate('OneChannel');
|
|
}
|
|
}
|
|
|
|
function getSeriesTimerTime(item: ItemDto) {
|
|
if (item.RecordAnyTime) {
|
|
return globalize.translate('Anytime');
|
|
} else {
|
|
return datetime.getDisplayTime(item.StartDate);
|
|
}
|
|
}
|
|
|
|
function getCurrentProgramTime(CurrentProgram: ItemDto | undefined) {
|
|
if (CurrentProgram) {
|
|
return getAirTimeText(CurrentProgram, false, true) || '';
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
function getCurrentProgramName(CurrentProgram: ItemDto | undefined) {
|
|
if (CurrentProgram) {
|
|
return CurrentProgram.Name;
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
function getChannelName(item: ItemDto) {
|
|
if (item.ChannelId) {
|
|
return getTextActionButton(
|
|
{
|
|
Id: item.ChannelId,
|
|
ServerId: item.ServerId,
|
|
Name: item.ChannelName,
|
|
Type: ItemKind.TvChannel,
|
|
MediaType: item.MediaType,
|
|
IsFolder: false
|
|
},
|
|
item.ChannelName
|
|
);
|
|
} else {
|
|
return { title: item.ChannelName || '\u00A0' };
|
|
}
|
|
}
|
|
|
|
function getRunTime(itemRunTimeTicks: NullableNumber) {
|
|
if (itemRunTimeTicks) {
|
|
return datetime.getDisplayRunningTime(itemRunTimeTicks);
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
function getPremiereDate(PremiereDate: string | null | undefined) {
|
|
if (PremiereDate) {
|
|
try {
|
|
return datetime.toLocaleDateString(
|
|
datetime.parseISO8601Date(PremiereDate),
|
|
{ weekday: 'long', month: 'long', day: 'numeric' }
|
|
);
|
|
} catch {
|
|
return '';
|
|
}
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
function getAdditionalLines(
|
|
textLines: (item: ItemDto) => (string | undefined)[],
|
|
item: ItemDto
|
|
) {
|
|
const additionalLines = textLines(item);
|
|
for (const additionalLine of additionalLines) {
|
|
return additionalLine;
|
|
}
|
|
}
|
|
|
|
function getProductionYear(item: ItemDto) {
|
|
const productionYear =
|
|
item.ProductionYear
|
|
&& datetime.toLocaleString(item.ProductionYear, {
|
|
useGrouping: false
|
|
});
|
|
if (item.Type === ItemKind.Series) {
|
|
if (item.Status === 'Continuing') {
|
|
return globalize.translate(
|
|
'SeriesYearToPresent',
|
|
productionYear || ''
|
|
);
|
|
} else if (item.EndDate && item.ProductionYear) {
|
|
const endYear = datetime.toLocaleString(
|
|
datetime.parseISO8601Date(item.EndDate).getFullYear(),
|
|
{ useGrouping: false }
|
|
);
|
|
return (
|
|
productionYear
|
|
+ (endYear === productionYear ? '' : ' - ' + endYear)
|
|
);
|
|
} else {
|
|
return productionYear || '';
|
|
}
|
|
} else {
|
|
return productionYear || '';
|
|
}
|
|
}
|
|
|
|
function getMediaTitle(cardOptions: CardOptions, item: ItemDto): TextLine {
|
|
const name =
|
|
cardOptions.showTitle === 'auto'
|
|
&& !item.IsFolder
|
|
&& item.MediaType === ItemMediaKind.Photo ?
|
|
'' :
|
|
itemHelper.getDisplayName(item, {
|
|
includeParentInfo: cardOptions.includeParentInfoInTitle
|
|
});
|
|
|
|
return getTextActionButton({
|
|
Id: item.Id,
|
|
ServerId: item.ServerId,
|
|
Name: name,
|
|
Type: item.Type,
|
|
CollectionType: item.CollectionType,
|
|
IsFolder: item.IsFolder
|
|
});
|
|
}
|
|
|
|
function getParentTitleOrTitle(
|
|
isOuterFooter: boolean,
|
|
item: ItemDto,
|
|
setTitleAdded: (val: boolean) => void,
|
|
showTitle: boolean
|
|
): TextLine {
|
|
if (
|
|
isOuterFooter
|
|
&& item.Type === ItemKind.Episode
|
|
&& item.SeriesName
|
|
) {
|
|
if (item.SeriesId) {
|
|
return getTextActionButton({
|
|
Id: item.SeriesId,
|
|
ServerId: item.ServerId,
|
|
Name: item.SeriesName,
|
|
Type: ItemKind.Series,
|
|
IsFolder: true
|
|
});
|
|
} else {
|
|
return { title: item.SeriesName };
|
|
}
|
|
} else if (isUsingLiveTvNaming(item.Type)) {
|
|
if (!item.EpisodeTitle && !item.IndexNumber) {
|
|
setTitleAdded(true);
|
|
}
|
|
return { title: item.Name };
|
|
} else {
|
|
const parentTitle =
|
|
item.SeriesName
|
|
|| item.Series
|
|
|| item.Album
|
|
|| item.AlbumArtist
|
|
|| '';
|
|
|
|
if (parentTitle || showTitle) {
|
|
return { title: parentTitle };
|
|
}
|
|
|
|
return { title: '' };
|
|
}
|
|
}
|
|
|
|
interface TextLinesOpts {
|
|
isOuterFooter: boolean;
|
|
overlayText: boolean | undefined;
|
|
forceName: boolean;
|
|
item: ItemDto;
|
|
cardOptions: CardOptions;
|
|
imgUrl: string | undefined;
|
|
}
|
|
|
|
export function getCardTextLines({
|
|
isOuterFooter,
|
|
overlayText,
|
|
forceName,
|
|
item,
|
|
cardOptions,
|
|
imgUrl
|
|
}: TextLinesOpts) {
|
|
const showTitle = shouldShowTitle(cardOptions.showTitle, item.Type);
|
|
const showOtherText = shouldShowOtherText(isOuterFooter, overlayText);
|
|
const serverId = item.ServerId || cardOptions.serverId;
|
|
let textLines: TextLine[] = [];
|
|
const parentTitleUnderneath = shouldShowParentTitleUnderneath(item.Type);
|
|
|
|
let titleAdded = false;
|
|
const addTextLine = (val: TextLine) => {
|
|
textLines.push(val);
|
|
};
|
|
|
|
const setTitleAdded = (val: boolean) => {
|
|
titleAdded = val;
|
|
};
|
|
|
|
if (
|
|
showOtherText
|
|
&& (cardOptions.showParentTitle || cardOptions.showParentTitleOrTitle)
|
|
&& !parentTitleUnderneath
|
|
) {
|
|
addTextLine(
|
|
getParentTitleOrTitle(isOuterFooter, item, setTitleAdded, showTitle)
|
|
);
|
|
}
|
|
|
|
const showMediaTitle = shouldShowMediaTitle(
|
|
titleAdded,
|
|
showTitle,
|
|
forceName,
|
|
cardOptions,
|
|
textLines
|
|
);
|
|
|
|
if (showMediaTitle) {
|
|
addTextLine(getMediaTitle(cardOptions, item));
|
|
}
|
|
|
|
if (showOtherText) {
|
|
addOtherText(
|
|
cardOptions,
|
|
parentTitleUnderneath,
|
|
isOuterFooter,
|
|
item,
|
|
addTextLine,
|
|
serverId
|
|
);
|
|
}
|
|
|
|
if (
|
|
(showTitle || !imgUrl)
|
|
&& forceName
|
|
&& overlayText
|
|
&& textLines.length === 1
|
|
) {
|
|
textLines = [];
|
|
}
|
|
|
|
if (overlayText && showTitle) {
|
|
textLines = [{ title: item.Name }];
|
|
}
|
|
|
|
return {
|
|
textLines
|
|
};
|
|
}
|