Split MusicDetails in three and created a first version of ArtistDetails, ReleaseDetails and ReleaseGroupDetails
parent
2cd4d4fb56
commit
ad86646daf
@ -0,0 +1,287 @@
|
||||
import Button from '@app/components/Common/Button';
|
||||
import CachedImage from '@app/components/Common/CachedImage';
|
||||
import ListView from '@app/components/Common/ListView';
|
||||
import PageTitle from '@app/components/Common/PageTitle';
|
||||
import type { PlayButtonLink } from '@app/components/Common/PlayButton';
|
||||
import PlayButton from '@app/components/Common/PlayButton';
|
||||
import Tooltip from '@app/components/Common/Tooltip';
|
||||
import IssueModal from '@app/components/IssueModal';
|
||||
import RequestButton from '@app/components/RequestButton';
|
||||
import StatusBadge from '@app/components/StatusBadge';
|
||||
import FetchedDataTitleCard from '@app/components/TitleCard/FetchedDataTitleCard';
|
||||
import useDeepLinks from '@app/hooks/useDeepLinks';
|
||||
import { Permission, useUser } from '@app/hooks/useUser';
|
||||
import Error from '@app/pages/_error';
|
||||
import { ExclamationTriangleIcon, PlayIcon } from '@heroicons/react/24/outline';
|
||||
import { MediaStatus, SecondaryType } from '@server/constants/media';
|
||||
import type { ArtistResult, ReleaseGroupResult } from '@server/models/Search';
|
||||
import 'country-flag-icons/3x2/flags.css';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { defineMessage, defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
overview: 'Overview',
|
||||
playonplex: 'Play on Plex',
|
||||
reportissue: 'Report an Issue',
|
||||
releases: 'Releases',
|
||||
});
|
||||
|
||||
const categoriesMessages = {
|
||||
Album: defineMessage({ id: 'Albums', defaultMessage: 'Albums' }),
|
||||
Single: defineMessage({ id: 'Singles', defaultMessage: 'Singles' }),
|
||||
EP: defineMessage({ id: 'EPs', defaultMessage: 'EPs' }),
|
||||
Other: defineMessage({ id: 'Other', defaultMessage: 'Other' }),
|
||||
};
|
||||
|
||||
interface ArtistDetailsProp {
|
||||
artist: ArtistResult;
|
||||
}
|
||||
|
||||
const ArtistDetails = ({ artist }: ArtistDetailsProp) => {
|
||||
const { hasPermission } = useUser();
|
||||
const router = useRouter();
|
||||
const intl = useIntl();
|
||||
const [showIssueModal, setShowIssueModal] = useState(false);
|
||||
|
||||
const categorize = (res: ReleaseGroupResult[]) =>
|
||||
res.reduce((group: { [key: string]: ReleaseGroupResult[] }, item) => {
|
||||
if (!group[item.type]) {
|
||||
group[item.type] = [];
|
||||
}
|
||||
group[item.type].push(item);
|
||||
return group;
|
||||
}, {});
|
||||
|
||||
const data = artist;
|
||||
|
||||
const { plexUrl } = useDeepLinks({
|
||||
plexUrl: data?.mediaInfo?.plexUrl,
|
||||
iOSPlexUrl: data?.mediaInfo?.iOSPlexUrl,
|
||||
});
|
||||
|
||||
const mediaLinks: PlayButtonLink[] = [];
|
||||
|
||||
if (
|
||||
plexUrl &&
|
||||
hasPermission([Permission.REQUEST, Permission.REQUEST_MUSIC], {
|
||||
type: 'or',
|
||||
})
|
||||
) {
|
||||
mediaLinks.push({
|
||||
text: intl.formatMessage(messages.playonplex),
|
||||
url: plexUrl,
|
||||
svg: <PlayIcon />,
|
||||
});
|
||||
}
|
||||
|
||||
const cleanDate = (date: Date | string | undefined) => {
|
||||
date = date ?? '';
|
||||
return new Date(date).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
});
|
||||
};
|
||||
|
||||
const mainDateDisplay: string =
|
||||
data.beginDate && data.endDate
|
||||
? `${cleanDate(data.beginDate)} - ${cleanDate(data.endDate)}`
|
||||
: cleanDate(data.beginDate);
|
||||
|
||||
const releaseGroups: ReleaseGroupResult[] = data.releaseGroups;
|
||||
|
||||
const categorizedReleaseGroupsType = categorize(releaseGroups);
|
||||
|
||||
const [categories, setCategories] = useState(
|
||||
categorizedReleaseGroupsType ?? {}
|
||||
);
|
||||
|
||||
const customMerge = (
|
||||
oldData: { [key: string]: ReleaseGroupResult[] },
|
||||
newData: { [key: string]: ReleaseGroupResult[] }
|
||||
) => {
|
||||
for (const key in newData) {
|
||||
if (oldData[key]) {
|
||||
oldData[key].push(...newData[key]);
|
||||
} else {
|
||||
oldData[key] = newData[key];
|
||||
}
|
||||
}
|
||||
return oldData;
|
||||
};
|
||||
|
||||
const [currentOffset, setCurrentOffset] = useState(0);
|
||||
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
const getMore = useCallback(() => {
|
||||
if (isLoading) {
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
fetch(
|
||||
`/api/v1/music/artist/${router.query.mbId}?full=true&offset=${
|
||||
currentOffset + 25
|
||||
}`
|
||||
)
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
res = categorize(res.releaseGroups ?? []);
|
||||
setCategories((prev) => customMerge(prev, res));
|
||||
setCurrentOffset(currentOffset + 25);
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
}, [currentOffset, isLoading, router.query.mbId]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
const bottom =
|
||||
document.body.scrollHeight - window.scrollY - window.outerHeight <= 1;
|
||||
if (bottom) {
|
||||
getMore();
|
||||
}
|
||||
};
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
};
|
||||
}, [getMore]);
|
||||
|
||||
if (!data) {
|
||||
return <Error statusCode={404} />;
|
||||
}
|
||||
|
||||
const title = data.name;
|
||||
|
||||
const tags: string[] = data.tags ?? [];
|
||||
|
||||
return (
|
||||
<div
|
||||
className="media-page"
|
||||
style={{
|
||||
height: 493,
|
||||
}}
|
||||
>
|
||||
<div className="media-page-bg-image">
|
||||
<CachedImage
|
||||
alt=""
|
||||
src={data.fanartPath ?? ''}
|
||||
layout="fill"
|
||||
objectFit="cover"
|
||||
priority
|
||||
/>
|
||||
<div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
backgroundImage:
|
||||
'linear-gradient(180deg, rgba(17, 24, 39, 0.47) 0%, rgba(17, 24, 39, 1) 100%)',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<PageTitle title={title} />
|
||||
<IssueModal
|
||||
onCancel={() => setShowIssueModal(false)}
|
||||
show={showIssueModal}
|
||||
mediaType="music"
|
||||
mbId={data.id}
|
||||
secondaryType={SecondaryType.ARTIST}
|
||||
/>
|
||||
<div className="media-header">
|
||||
<div className="media-poster">
|
||||
<CachedImage
|
||||
src={data.posterPath ?? ''}
|
||||
alt={title + ' poster'}
|
||||
layout="responsive"
|
||||
width={600}
|
||||
height={600}
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
<div className="media-title">
|
||||
<div className="media-status">
|
||||
<StatusBadge
|
||||
status={data.mediaInfo?.status}
|
||||
downloadItem={data.mediaInfo?.downloadStatus}
|
||||
title={title}
|
||||
inProgress={(data.mediaInfo?.downloadStatus ?? []).length > 0}
|
||||
tmdbId={data.mediaInfo?.tmdbId}
|
||||
mediaType="music"
|
||||
plexUrl={plexUrl}
|
||||
serviceUrl={data.mediaInfo?.serviceUrl}
|
||||
secondaryType={SecondaryType.ARTIST}
|
||||
/>
|
||||
</div>
|
||||
<h1 data-testid="media-title">
|
||||
{title}{' '}
|
||||
{mainDateDisplay !== '' && (
|
||||
<span className="media-year">({mainDateDisplay})</span>
|
||||
)}
|
||||
</h1>
|
||||
<span className="media-attributes">
|
||||
{tags.map((t, k) => (
|
||||
<span key={k}>{t}</span>
|
||||
))}
|
||||
</span>
|
||||
</div>
|
||||
<div className="media-actions">
|
||||
<PlayButton links={mediaLinks} />
|
||||
<RequestButton
|
||||
mediaType="music"
|
||||
media={data.mediaInfo}
|
||||
mbId={data.id}
|
||||
secondaryType={SecondaryType.ARTIST}
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
onUpdate={() => {}}
|
||||
/>
|
||||
{data.mediaInfo?.status === MediaStatus.AVAILABLE &&
|
||||
hasPermission(
|
||||
[Permission.CREATE_ISSUES, Permission.MANAGE_ISSUES],
|
||||
{
|
||||
type: 'or',
|
||||
}
|
||||
) && (
|
||||
<Tooltip content={intl.formatMessage(messages.reportissue)}>
|
||||
<Button
|
||||
buttonType="warning"
|
||||
onClick={() => setShowIssueModal(true)}
|
||||
className="ml-2 first:ml-0"
|
||||
>
|
||||
<ExclamationTriangleIcon />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{Object.entries(categories).map(([type, category]) => (
|
||||
<>
|
||||
<div className="slider-header">
|
||||
<div className="slider-title">
|
||||
<span>
|
||||
{intl.formatMessage(
|
||||
categoriesMessages[type as keyof typeof categoriesMessages]
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<ListView
|
||||
isLoading={false}
|
||||
jsxItems={category.map((item) => (
|
||||
<FetchedDataTitleCard
|
||||
key={`media-slider-item-${item.id}`}
|
||||
data={item}
|
||||
/>
|
||||
))}
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
onScrollBottom={() => {}}
|
||||
/>
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ArtistDetails;
|
@ -0,0 +1,228 @@
|
||||
import Button from '@app/components/Common/Button';
|
||||
import CachedImage from '@app/components/Common/CachedImage';
|
||||
import List from '@app/components/Common/List';
|
||||
import PageTitle from '@app/components/Common/PageTitle';
|
||||
import type { PlayButtonLink } from '@app/components/Common/PlayButton';
|
||||
import PlayButton from '@app/components/Common/PlayButton';
|
||||
import Tooltip from '@app/components/Common/Tooltip';
|
||||
import IssueModal from '@app/components/IssueModal';
|
||||
import RequestButton from '@app/components/RequestButton';
|
||||
import StatusBadge from '@app/components/StatusBadge';
|
||||
import useDeepLinks from '@app/hooks/useDeepLinks';
|
||||
import { Permission, useUser } from '@app/hooks/useUser';
|
||||
import Error from '@app/pages/_error';
|
||||
import { ExclamationTriangleIcon, PlayIcon } from '@heroicons/react/24/outline';
|
||||
import { MediaStatus, SecondaryType } from '@server/constants/media';
|
||||
import type { RecordingResult, ReleaseResult } from '@server/models/Search';
|
||||
import 'country-flag-icons/3x2/flags.css';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
|
||||
const messages = defineMessages({
|
||||
originaltitle: 'Original Title',
|
||||
overview: 'Overview',
|
||||
recommendations: 'Recommendations',
|
||||
playonplex: 'Play on Plex',
|
||||
markavailable: 'Mark as Available',
|
||||
showmore: 'Show More',
|
||||
showless: 'Show Less',
|
||||
digitalrelease: 'Digital Release',
|
||||
physicalrelease: 'Physical Release',
|
||||
reportissue: 'Report an Issue',
|
||||
managemusic: 'Manage Music',
|
||||
releases: 'Releases',
|
||||
albums: 'Albums',
|
||||
singles: 'Singles',
|
||||
eps: 'EPs',
|
||||
broadcasts: 'Broadcasts',
|
||||
others: 'Others',
|
||||
feats: 'Featured In',
|
||||
tracks: 'Tracks',
|
||||
});
|
||||
|
||||
interface ReleaseDetailsProp {
|
||||
release: ReleaseResult;
|
||||
}
|
||||
|
||||
const ReleaseDetails = ({ release }: ReleaseDetailsProp) => {
|
||||
const { hasPermission } = useUser();
|
||||
const router = useRouter();
|
||||
const intl = useIntl();
|
||||
const [showIssueModal, setShowIssueModal] = useState(false);
|
||||
|
||||
const { data: fetched } = useSWR<ReleaseResult>(
|
||||
`/api/v1/music/release/${router.query.mbId}?full=true`
|
||||
);
|
||||
|
||||
const data = fetched ?? release;
|
||||
|
||||
const { plexUrl } = useDeepLinks({
|
||||
plexUrl: data?.mediaInfo?.plexUrl,
|
||||
iOSPlexUrl: data?.mediaInfo?.iOSPlexUrl,
|
||||
});
|
||||
|
||||
const mediaLinks: PlayButtonLink[] = [];
|
||||
|
||||
if (
|
||||
plexUrl &&
|
||||
hasPermission([Permission.REQUEST, Permission.REQUEST_MOVIE], {
|
||||
type: 'or',
|
||||
})
|
||||
) {
|
||||
mediaLinks.push({
|
||||
text: intl.formatMessage(messages.playonplex),
|
||||
url: plexUrl,
|
||||
svg: <PlayIcon />,
|
||||
});
|
||||
}
|
||||
|
||||
const cleanDate = (date: Date | string | undefined) => {
|
||||
date = date ?? '';
|
||||
return new Date(date).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
});
|
||||
};
|
||||
|
||||
const mainDateDisplay: string = cleanDate(data.date);
|
||||
|
||||
const tracks: RecordingResult[] = data.tracks ?? [];
|
||||
|
||||
const lengthToTime = (length: number) => {
|
||||
length /= 1000;
|
||||
const minutes = Math.floor(length / 60);
|
||||
const seconds = length - minutes * 60;
|
||||
return `${minutes}:${seconds < 10 ? '0' : ''}${seconds.toFixed(0)}`;
|
||||
};
|
||||
|
||||
if (!data) {
|
||||
return <Error statusCode={404} />;
|
||||
}
|
||||
|
||||
const title = data.title;
|
||||
|
||||
const tags: string[] = data.tags ?? [];
|
||||
|
||||
return (
|
||||
<div
|
||||
className="media-page"
|
||||
style={{
|
||||
height: 493,
|
||||
}}
|
||||
>
|
||||
<div className="media-page-bg-image">
|
||||
<div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
backgroundImage:
|
||||
'linear-gradient(180deg, rgba(17, 24, 39, 0.47) 0%, rgba(17, 24, 39, 1) 100%)',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<PageTitle title={title} />
|
||||
<IssueModal
|
||||
onCancel={() => setShowIssueModal(false)}
|
||||
show={showIssueModal}
|
||||
mediaType="music"
|
||||
mbId={data.id}
|
||||
secondaryType={SecondaryType.RELEASE}
|
||||
/>
|
||||
<div className="media-header">
|
||||
<div className="media-poster">
|
||||
<CachedImage
|
||||
src={data.posterPath ?? ''}
|
||||
alt=""
|
||||
layout="responsive"
|
||||
width={600}
|
||||
height={600}
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
<div className="media-title">
|
||||
<div className="media-status">
|
||||
<StatusBadge
|
||||
status={data.mediaInfo?.status}
|
||||
downloadItem={data.mediaInfo?.downloadStatus}
|
||||
title={title}
|
||||
inProgress={(data.mediaInfo?.downloadStatus ?? []).length > 0}
|
||||
tmdbId={data.mediaInfo?.tmdbId}
|
||||
mediaType="music"
|
||||
plexUrl={plexUrl}
|
||||
serviceUrl={data.mediaInfo?.serviceUrl}
|
||||
secondaryType={SecondaryType.RELEASE}
|
||||
/>
|
||||
</div>
|
||||
<h1 data-testid="media-title">
|
||||
{title}{' '}
|
||||
{mainDateDisplay !== '' && (
|
||||
<span className="media-year">({mainDateDisplay})</span>
|
||||
)}
|
||||
</h1>
|
||||
<span className="media-attributes">
|
||||
{tags.map((t, k) => (
|
||||
<span key={k}>{t}</span>
|
||||
))}
|
||||
</span>
|
||||
</div>
|
||||
<div className="media-actions">
|
||||
<PlayButton links={mediaLinks} />
|
||||
<RequestButton
|
||||
mediaType="music"
|
||||
media={data.mediaInfo}
|
||||
mbId={data.id}
|
||||
secondaryType={SecondaryType.RELEASE}
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
onUpdate={() => {}}
|
||||
/>
|
||||
{data.mediaInfo?.status === MediaStatus.AVAILABLE &&
|
||||
hasPermission(
|
||||
[Permission.CREATE_ISSUES, Permission.MANAGE_ISSUES],
|
||||
{
|
||||
type: 'or',
|
||||
}
|
||||
) && (
|
||||
<Tooltip content={intl.formatMessage(messages.reportissue)}>
|
||||
<Button
|
||||
buttonType="warning"
|
||||
onClick={() => setShowIssueModal(true)}
|
||||
className="ml-2 first:ml-0"
|
||||
>
|
||||
<ExclamationTriangleIcon />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<List title={intl.formatMessage(messages.tracks)}>
|
||||
{tracks.map((track, index) => (
|
||||
<div key={index}>
|
||||
<div className="max-w-6xl py-4 sm:grid sm:grid-cols-3 sm:gap-4">
|
||||
<dt className="block text-sm font-bold text-gray-400">
|
||||
{track.title}
|
||||
</dt>
|
||||
<dd className="flex text-sm text-white sm:col-span-2 sm:mt-0">
|
||||
<span className="flex-grow">
|
||||
{lengthToTime(track.length)}
|
||||
</span>
|
||||
</dd>
|
||||
<dd>
|
||||
<span className="flex-grow">
|
||||
{track.artist.map((artist, index) => (
|
||||
<span key={index}>{artist.name}</span>
|
||||
))}
|
||||
</span>
|
||||
</dd>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</List>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReleaseDetails;
|
@ -0,0 +1,203 @@
|
||||
import Button from '@app/components/Common/Button';
|
||||
import CachedImage from '@app/components/Common/CachedImage';
|
||||
import ListView from '@app/components/Common/ListView';
|
||||
import PageTitle from '@app/components/Common/PageTitle';
|
||||
import type { PlayButtonLink } from '@app/components/Common/PlayButton';
|
||||
import PlayButton from '@app/components/Common/PlayButton';
|
||||
import Tooltip from '@app/components/Common/Tooltip';
|
||||
import IssueModal from '@app/components/IssueModal';
|
||||
import RequestButton from '@app/components/RequestButton';
|
||||
import StatusBadge from '@app/components/StatusBadge';
|
||||
import FetchedDataTitleCard from '@app/components/TitleCard/FetchedDataTitleCard';
|
||||
import useDeepLinks from '@app/hooks/useDeepLinks';
|
||||
import { Permission, useUser } from '@app/hooks/useUser';
|
||||
import Error from '@app/pages/_error';
|
||||
import { ExclamationTriangleIcon, PlayIcon } from '@heroicons/react/24/outline';
|
||||
import { MediaStatus, SecondaryType } from '@server/constants/media';
|
||||
import type { ReleaseGroupResult } from '@server/models/Search';
|
||||
import 'country-flag-icons/3x2/flags.css';
|
||||
import { useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
overview: 'Overview',
|
||||
recommendations: 'Recommendations',
|
||||
playonplex: 'Play on Plex',
|
||||
markavailable: 'Mark as Available',
|
||||
showmore: 'Show More',
|
||||
showless: 'Show Less',
|
||||
reportissue: 'Report an Issue',
|
||||
releases: 'Releases',
|
||||
albums: 'Albums',
|
||||
singles: 'Singles',
|
||||
eps: 'EPs',
|
||||
broadcasts: 'Broadcasts',
|
||||
others: 'Others',
|
||||
feats: 'Featured In',
|
||||
});
|
||||
|
||||
interface ReleaseGroupDetailsProp {
|
||||
releaseGroup: ReleaseGroupResult;
|
||||
}
|
||||
|
||||
const ReleaseGroupDetails = ({ releaseGroup }: ReleaseGroupDetailsProp) => {
|
||||
const { hasPermission } = useUser();
|
||||
const intl = useIntl();
|
||||
const [showIssueModal, setShowIssueModal] = useState(false);
|
||||
|
||||
const data = releaseGroup;
|
||||
|
||||
const { plexUrl } = useDeepLinks({
|
||||
plexUrl: data?.mediaInfo?.plexUrl,
|
||||
iOSPlexUrl: data?.mediaInfo?.iOSPlexUrl,
|
||||
});
|
||||
|
||||
const mediaLinks: PlayButtonLink[] = [];
|
||||
|
||||
if (
|
||||
plexUrl &&
|
||||
hasPermission([Permission.REQUEST, Permission.REQUEST_MOVIE], {
|
||||
type: 'or',
|
||||
})
|
||||
) {
|
||||
mediaLinks.push({
|
||||
text: intl.formatMessage(messages.playonplex),
|
||||
url: plexUrl,
|
||||
svg: <PlayIcon />,
|
||||
});
|
||||
}
|
||||
|
||||
const releases = data.releases;
|
||||
|
||||
if (!data) {
|
||||
return <Error statusCode={404} />;
|
||||
}
|
||||
|
||||
/*
|
||||
const cleanDate = (date: Date | string | undefined) => {
|
||||
date = date ?? '';
|
||||
return new Date(date).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
});
|
||||
};
|
||||
|
||||
const formatedDate = cleanDate(data.firstReleaseDate ?? '');
|
||||
*/
|
||||
|
||||
const title = data.title;
|
||||
|
||||
const tags: string[] = data.tags ?? [];
|
||||
|
||||
return (
|
||||
<div
|
||||
className="media-page"
|
||||
style={{
|
||||
height: 493,
|
||||
}}
|
||||
>
|
||||
<div className="media-page-bg-image">
|
||||
<div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
backgroundImage:
|
||||
'linear-gradient(180deg, rgba(17, 24, 39, 0.47) 0%, rgba(17, 24, 39, 1) 100%)',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<PageTitle title={title} />
|
||||
<IssueModal
|
||||
onCancel={() => setShowIssueModal(false)}
|
||||
show={showIssueModal}
|
||||
mediaType="music"
|
||||
mbId={data.id}
|
||||
secondaryType={SecondaryType.RELEASE_GROUP}
|
||||
/>
|
||||
<div className="media-header">
|
||||
<div className="media-poster">
|
||||
<CachedImage
|
||||
src={data.posterPath ?? ''}
|
||||
alt={title + ' album cover'}
|
||||
layout="responsive"
|
||||
width={600}
|
||||
height={600}
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
<div className="media-title">
|
||||
<div className="media-status">
|
||||
<StatusBadge
|
||||
status={data.mediaInfo?.status}
|
||||
downloadItem={data.mediaInfo?.downloadStatus}
|
||||
title={title}
|
||||
inProgress={(data.mediaInfo?.downloadStatus ?? []).length > 0}
|
||||
tmdbId={data.mediaInfo?.tmdbId}
|
||||
mediaType="music"
|
||||
plexUrl={plexUrl}
|
||||
serviceUrl={data.mediaInfo?.serviceUrl}
|
||||
secondaryType={SecondaryType.RELEASE_GROUP}
|
||||
/>
|
||||
</div>
|
||||
<h1 data-testid="media-title">{title}</h1>
|
||||
<h2 data-testid="media-subtitle">{data.type}</h2>
|
||||
<span className="media-attributes">
|
||||
{tags.map((t, k) => (
|
||||
<span key={k}>{t}</span>
|
||||
))}
|
||||
</span>
|
||||
</div>
|
||||
<div className="media-actions">
|
||||
<PlayButton links={mediaLinks} />
|
||||
<RequestButton
|
||||
mediaType="music"
|
||||
media={data.mediaInfo}
|
||||
mbId={data.id}
|
||||
secondaryType={SecondaryType.RELEASE_GROUP}
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
onUpdate={() => {}}
|
||||
/>
|
||||
{data.mediaInfo?.status === MediaStatus.AVAILABLE &&
|
||||
hasPermission(
|
||||
[Permission.CREATE_ISSUES, Permission.MANAGE_ISSUES],
|
||||
{
|
||||
type: 'or',
|
||||
}
|
||||
) && (
|
||||
<Tooltip content={intl.formatMessage(messages.reportissue)}>
|
||||
<Button
|
||||
buttonType="warning"
|
||||
onClick={() => setShowIssueModal(true)}
|
||||
className="ml-2 first:ml-0"
|
||||
>
|
||||
<ExclamationTriangleIcon />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{releases?.length > 0 && (
|
||||
<>
|
||||
<div className="slider-header">
|
||||
<div className="slider-title">
|
||||
<span>{intl.formatMessage(messages.releases)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ListView
|
||||
isLoading={false}
|
||||
jsxItems={releases.map((item) => (
|
||||
<FetchedDataTitleCard
|
||||
key={`media-slider-item-${item.id}`}
|
||||
data={item}
|
||||
/>
|
||||
))}
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
onScrollBottom={() => {}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReleaseGroupDetails;
|
Loading…
Reference in new issue