mirror of
https://github.com/jeffvli/feishin.git
synced 2026-06-19 18:04:22 +02:00
add more metadata to album header / side
This commit is contained in:
@@ -22,18 +22,12 @@ import { useContainerQuery } from '/@/renderer/hooks';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { useCurrentServer, usePlayerSong } from '/@/renderer/store';
|
||||
import { useGeneralSettings, useSettingsStore } from '/@/renderer/store/settings.store';
|
||||
import {
|
||||
formatDateAbsoluteUTC,
|
||||
formatDurationString,
|
||||
formatSizeString,
|
||||
titleCase,
|
||||
} from '/@/renderer/utils';
|
||||
import { titleCase } from '/@/renderer/utils';
|
||||
import { replaceURLWithHTMLLinks } from '/@/renderer/utils/linkify';
|
||||
import { normalizeReleaseTypes } from '/@/renderer/utils/normalize-release-types';
|
||||
import { sortSongList } from '/@/shared/api/utils';
|
||||
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
||||
import { Checkbox } from '/@/shared/components/checkbox/checkbox';
|
||||
import { Flex } from '/@/shared/components/flex/flex';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { Icon } from '/@/shared/components/icon/icon';
|
||||
import { Pill, PillLink } from '/@/shared/components/pill/pill';
|
||||
@@ -55,89 +49,85 @@ import {
|
||||
} from '/@/shared/types/domain-types';
|
||||
import { ItemListKey, ListDisplayType, Play } from '/@/shared/types/types';
|
||||
|
||||
const MetadataPillGroup = ({
|
||||
items,
|
||||
title,
|
||||
}: {
|
||||
items: undefined | { id: string; value: ReactNode | string | undefined }[];
|
||||
title: string;
|
||||
}) => {
|
||||
if (!items || items.length === 0) return null;
|
||||
|
||||
return (
|
||||
<Stack align="center" className={styles.metadataPillGroup} gap="xs">
|
||||
<Text fw={600} isNoSelect size="sm" tt="uppercase">
|
||||
{title}
|
||||
</Text>
|
||||
<div className={styles['pill-group-wrapper']}>
|
||||
<Pill.Group>
|
||||
{items.map((tag, index) => (
|
||||
<Pill key={`item-${tag.id}-${index}`} size="md">
|
||||
{tag.value}
|
||||
</Pill>
|
||||
))}
|
||||
</Pill.Group>
|
||||
</div>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
interface AlbumMetadataTagsProps {
|
||||
album: Album | undefined;
|
||||
}
|
||||
|
||||
const MOOD_TAG = 'mood';
|
||||
const RELEASE_COUNTRY_TAG = 'releasecountry';
|
||||
const RELEASE_STATUS_TAG = 'releasestatus';
|
||||
|
||||
const AlbumMetadataTags = ({ album }: AlbumMetadataTagsProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const metadataItems = useMemo(() => {
|
||||
const defaultTagItems = useMemo(() => {
|
||||
if (!album) return [];
|
||||
|
||||
const originalDifferentFromRelease =
|
||||
album.originalDate && album.originalDate !== album.releaseDate;
|
||||
|
||||
const releasePrefix = originalDifferentFromRelease
|
||||
? t('page.albumDetail.released', { postProcess: 'sentenceCase' })
|
||||
: '♫';
|
||||
|
||||
const releaseTypes = normalizeReleaseTypes(album.releaseTypes ?? [], t).map((type) => ({
|
||||
id: type,
|
||||
value: titleCase(type),
|
||||
}));
|
||||
|
||||
const releaseCountries =
|
||||
album.tags?.[RELEASE_COUNTRY_TAG]?.map((country) => ({
|
||||
id: country,
|
||||
value: country,
|
||||
})) || [];
|
||||
|
||||
const releaseStatuses =
|
||||
album.tags?.[RELEASE_STATUS_TAG]?.map((status) => ({
|
||||
id: status,
|
||||
value: status,
|
||||
})) || [];
|
||||
|
||||
const recordLabels =
|
||||
album.recordLabels?.map((label) => ({
|
||||
id: label,
|
||||
value: label,
|
||||
})) || [];
|
||||
|
||||
console.log('album', album);
|
||||
|
||||
const items: Array<{ id: string; value: ReactNode | string | undefined }> = [];
|
||||
|
||||
if (originalDifferentFromRelease && album.originalDate) {
|
||||
items.push({
|
||||
id: 'originalDate',
|
||||
value: `♫ ${formatDateAbsoluteUTC(album.originalDate)}`,
|
||||
});
|
||||
}
|
||||
|
||||
items.push(...releaseTypes);
|
||||
|
||||
items.push(
|
||||
...releaseTypes,
|
||||
{
|
||||
id: 'isCompilation',
|
||||
value: album?.isCompilation
|
||||
? t('filter.isCompilation', { postProcess: 'sentenceCase' })
|
||||
: undefined,
|
||||
},
|
||||
{
|
||||
id: 'releaseDate',
|
||||
value: album.releaseDate
|
||||
? `${releasePrefix} ${formatDateAbsoluteUTC(album.releaseDate)}`
|
||||
: undefined,
|
||||
},
|
||||
{
|
||||
id: 'releaseYear',
|
||||
value: album.releaseDate
|
||||
? undefined
|
||||
: album.releaseYear
|
||||
? album.releaseYear.toString()
|
||||
: undefined,
|
||||
},
|
||||
{
|
||||
id: 'songCount',
|
||||
value: album.songCount
|
||||
? t('entity.trackWithCount', {
|
||||
count: album.songCount,
|
||||
})
|
||||
: undefined,
|
||||
},
|
||||
{
|
||||
id: 'duration',
|
||||
value: album.duration ? (
|
||||
<Flex align="center" gap="xs">
|
||||
<Icon icon="duration" size="md" /> {formatDurationString(album.duration)}
|
||||
</Flex>
|
||||
) : undefined,
|
||||
},
|
||||
{
|
||||
id: 'size',
|
||||
value: album.size ? formatSizeString(album.size) : undefined,
|
||||
},
|
||||
{
|
||||
id: 'playCount',
|
||||
value:
|
||||
typeof album.playCount === 'number'
|
||||
? t('entity.play', {
|
||||
count: album.playCount,
|
||||
})
|
||||
: undefined,
|
||||
},
|
||||
...releaseCountries,
|
||||
...releaseStatuses,
|
||||
...recordLabels,
|
||||
{
|
||||
id: 'explicitStatus',
|
||||
value:
|
||||
@@ -147,31 +137,32 @@ const AlbumMetadataTags = ({ album }: AlbumMetadataTagsProps) => {
|
||||
? t('common.clean', { postProcess: 'sentenceCase' })
|
||||
: undefined,
|
||||
},
|
||||
|
||||
{
|
||||
id: 'version',
|
||||
value: album.version || undefined,
|
||||
},
|
||||
);
|
||||
|
||||
return items.filter((item) => item.value);
|
||||
}, [album, t]);
|
||||
|
||||
if (metadataItems.length === 0) return null;
|
||||
const moodTagItems = useMemo(() => {
|
||||
if (!album) return [];
|
||||
|
||||
return album.tags?.[MOOD_TAG]?.map((tag) => ({
|
||||
id: tag,
|
||||
value: tag,
|
||||
}));
|
||||
}, [album]);
|
||||
|
||||
return (
|
||||
<Stack gap="xs">
|
||||
<Text fw={600} isNoSelect size="sm" tt="uppercase">
|
||||
{t('common.tags', { postProcess: 'sentenceCase' })}
|
||||
</Text>
|
||||
<Pill.Group>
|
||||
{metadataItems.map((item, index) => (
|
||||
<Pill key={`item-${item.id}-${index}`} size="md">
|
||||
{item.value}
|
||||
</Pill>
|
||||
))}
|
||||
</Pill.Group>
|
||||
</Stack>
|
||||
<>
|
||||
<MetadataPillGroup
|
||||
items={defaultTagItems}
|
||||
title={t('common.tags', { postProcess: 'sentenceCase' })}
|
||||
/>
|
||||
|
||||
<MetadataPillGroup
|
||||
items={moodTagItems}
|
||||
title={t('common.mood', { postProcess: 'sentenceCase' })}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -208,38 +199,38 @@ const AlbumMetadataGenres = ({ genres }: AlbumMetadataGenresProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
interface AlbumMetadataArtistsProps {
|
||||
artists?: Array<{ id: string; name: string }>;
|
||||
}
|
||||
// interface AlbumMetadataArtistsProps {
|
||||
// artists?: Array<{ id: string; name: string }>;
|
||||
// }
|
||||
|
||||
const AlbumMetadataArtists = ({ artists }: AlbumMetadataArtistsProps) => {
|
||||
const { t } = useTranslation();
|
||||
// const AlbumMetadataArtists = ({ artists }: AlbumMetadataArtistsProps) => {
|
||||
// const { t } = useTranslation();
|
||||
|
||||
if (!artists || artists.length === 0) return null;
|
||||
// if (!artists || artists.length === 0) return null;
|
||||
|
||||
return (
|
||||
<Stack gap="xs">
|
||||
<Text fw={600} isNoSelect size="sm" tt="uppercase">
|
||||
{t('entity.albumArtist', {
|
||||
count: artists.length,
|
||||
})}
|
||||
</Text>
|
||||
<Pill.Group>
|
||||
{artists.map((artist) => (
|
||||
<PillLink
|
||||
key={`artist-${artist.id}`}
|
||||
size="md"
|
||||
to={generatePath(AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL, {
|
||||
albumArtistId: artist.id,
|
||||
})}
|
||||
>
|
||||
{artist.name}
|
||||
</PillLink>
|
||||
))}
|
||||
</Pill.Group>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
// return (
|
||||
// <Stack gap="xs">
|
||||
// <Text fw={600} isNoSelect size="sm" tt="uppercase">
|
||||
// {t('entity.albumArtist', {
|
||||
// count: artists.length,
|
||||
// })}
|
||||
// </Text>
|
||||
// <Pill.Group>
|
||||
// {artists.map((artist) => (
|
||||
// <PillLink
|
||||
// key={`artist-${artist.id}`}
|
||||
// size="md"
|
||||
// to={generatePath(AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL, {
|
||||
// albumArtistId: artist.id,
|
||||
// })}
|
||||
// >
|
||||
// {artist.name}
|
||||
// </PillLink>
|
||||
// ))}
|
||||
// </Pill.Group>
|
||||
// </Stack>
|
||||
// );
|
||||
// };
|
||||
|
||||
interface AlbumMetadataExternalLinksProps {
|
||||
albumArtist?: string;
|
||||
@@ -269,7 +260,7 @@ const AlbumMetadataExternalLinks = ({
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
</Text>
|
||||
<Group gap="sm">
|
||||
<Group className={styles.externalLinksGroup} gap="sm">
|
||||
{lastFM && (
|
||||
<ActionIcon
|
||||
component="a"
|
||||
@@ -382,19 +373,17 @@ export const AlbumDetailContent = () => {
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.metadataColumn}>
|
||||
<Stack gap="2xl">
|
||||
<AlbumMetadataArtists artists={detailQuery?.data?.albumArtists} />
|
||||
<AlbumMetadataGenres genres={detailQuery?.data?.genres} />
|
||||
<AlbumMetadataTags album={detailQuery?.data} />
|
||||
<AlbumMetadataExternalLinks
|
||||
albumArtist={detailQuery?.data?.albumArtist}
|
||||
albumName={detailQuery?.data?.name}
|
||||
externalLinks={externalLinks}
|
||||
lastFM={lastFM}
|
||||
mbzId={mbzId || undefined}
|
||||
musicBrainz={musicBrainz}
|
||||
/>
|
||||
</Stack>
|
||||
{/* <AlbumMetadataArtists artists={detailQuery?.data?.albumArtists} /> */}
|
||||
<AlbumMetadataGenres genres={detailQuery?.data?.genres} />
|
||||
<AlbumMetadataTags album={detailQuery?.data} />
|
||||
<AlbumMetadataExternalLinks
|
||||
albumArtist={detailQuery?.data?.albumArtist}
|
||||
albumName={detailQuery?.data?.name}
|
||||
externalLinks={externalLinks}
|
||||
lastFM={lastFM}
|
||||
mbzId={mbzId || undefined}
|
||||
musicBrainz={musicBrainz}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{labels && (
|
||||
|
||||
Reference in New Issue
Block a user