mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-09 20:29:36 +02:00
refactor grid columns internally into album artist sections to handle null cases
This commit is contained in:
@@ -159,15 +159,17 @@ const AlbumArtistActionButtons = ({
|
|||||||
|
|
||||||
interface AlbumArtistMetadataGenresProps {
|
interface AlbumArtistMetadataGenresProps {
|
||||||
genres?: Array<{ id: string; name: string }>;
|
genres?: Array<{ id: string; name: string }>;
|
||||||
|
order?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AlbumArtistMetadataGenres = ({ genres }: AlbumArtistMetadataGenresProps) => {
|
const AlbumArtistMetadataGenres = ({ genres, order }: AlbumArtistMetadataGenresProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const genrePath = useGenreRoute();
|
const genrePath = useGenreRoute();
|
||||||
|
|
||||||
if (!genres || genres.length === 0) return null;
|
if (!genres || genres.length === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Grid.Col order={order} span={12}>
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
<Text fw={600} isNoSelect size="sm" tt="uppercase">
|
<Text fw={600} isNoSelect size="sm" tt="uppercase">
|
||||||
{t('entity.genre', {
|
{t('entity.genre', {
|
||||||
@@ -196,16 +198,19 @@ const AlbumArtistMetadataGenres = ({ genres }: AlbumArtistMetadataGenresProps) =
|
|||||||
))}
|
))}
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
</Grid.Col>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
interface AlbumArtistMetadataBiographyProps {
|
interface AlbumArtistMetadataBiographyProps {
|
||||||
artistName?: string;
|
artistName?: string;
|
||||||
|
order?: number;
|
||||||
routeId: string;
|
routeId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AlbumArtistMetadataBiography = ({
|
const AlbumArtistMetadataBiography = ({
|
||||||
artistName,
|
artistName,
|
||||||
|
order,
|
||||||
routeId,
|
routeId,
|
||||||
}: AlbumArtistMetadataBiographyProps) => {
|
}: AlbumArtistMetadataBiographyProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -234,6 +239,7 @@ const AlbumArtistMetadataBiography = ({
|
|||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
|
<Grid.Col order={order} span={12}>
|
||||||
<section style={{ maxWidth: '1280px' }}>
|
<section style={{ maxWidth: '1280px' }}>
|
||||||
<TextTitle fw={700} order={3}>
|
<TextTitle fw={700} order={3}>
|
||||||
{t('page.albumArtistDetail.about', {
|
{t('page.albumArtistDetail.about', {
|
||||||
@@ -246,6 +252,7 @@ const AlbumArtistMetadataBiography = ({
|
|||||||
<Skeleton enableAnimation height="1rem" width="60%" />
|
<Skeleton enableAnimation height="1rem" width="60%" />
|
||||||
</Stack>
|
</Stack>
|
||||||
</section>
|
</section>
|
||||||
|
</Grid.Col>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,6 +261,7 @@ const AlbumArtistMetadataBiography = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Grid.Col order={order} span={12}>
|
||||||
<section style={{ maxWidth: '1280px' }}>
|
<section style={{ maxWidth: '1280px' }}>
|
||||||
<TextTitle fw={700} order={3}>
|
<TextTitle fw={700} order={3}>
|
||||||
{t('page.albumArtistDetail.about', {
|
{t('page.albumArtistDetail.about', {
|
||||||
@@ -264,6 +272,7 @@ const AlbumArtistMetadataBiography = ({
|
|||||||
<Text dangerouslySetInnerHTML={{ __html: sanitizedBiography }} />
|
<Text dangerouslySetInnerHTML={{ __html: sanitizedBiography }} />
|
||||||
</Spoiler>
|
</Spoiler>
|
||||||
</section>
|
</section>
|
||||||
|
</Grid.Col>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -302,11 +311,13 @@ const SongTableListContainer = ({
|
|||||||
|
|
||||||
interface AlbumArtistMetadataTopSongsProps {
|
interface AlbumArtistMetadataTopSongsProps {
|
||||||
detailQuery: ReturnType<typeof useSuspenseQuery<AlbumArtistDetailResponse>>;
|
detailQuery: ReturnType<typeof useSuspenseQuery<AlbumArtistDetailResponse>>;
|
||||||
|
order?: number;
|
||||||
routeId: string;
|
routeId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AlbumArtistMetadataTopSongsContent = ({
|
const AlbumArtistMetadataTopSongsContent = ({
|
||||||
detailQuery,
|
detailQuery,
|
||||||
|
order,
|
||||||
routeId,
|
routeId,
|
||||||
}: AlbumArtistMetadataTopSongsProps) => {
|
}: AlbumArtistMetadataTopSongsProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -395,10 +406,12 @@ const AlbumArtistMetadataTopSongsContent = ({
|
|||||||
const isLoading = topSongsQuery.isLoading || !topSongsQuery.data;
|
const isLoading = topSongsQuery.isLoading || !topSongsQuery.data;
|
||||||
|
|
||||||
if (!isLoading && !tableConfig) return null;
|
if (!isLoading && !tableConfig) return null;
|
||||||
|
if (!isLoading && songs.length === 0) return null;
|
||||||
|
|
||||||
const currentSongId = currentSong?.id;
|
const currentSongId = currentSong?.id;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Grid.Col order={order} span={12}>
|
||||||
<section>
|
<section>
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<div className={styles.albumSectionTitle}>
|
<div className={styles.albumSectionTitle}>
|
||||||
@@ -475,7 +488,9 @@ const AlbumArtistMetadataTopSongsContent = ({
|
|||||||
flex={1}
|
flex={1}
|
||||||
leftSection={<Icon icon="search" />}
|
leftSection={<Icon icon="search" />}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
placeholder={t('common.search', { postProcess: 'sentenceCase' })}
|
placeholder={t('common.search', {
|
||||||
|
postProcess: 'sentenceCase',
|
||||||
|
})}
|
||||||
radius="xl"
|
radius="xl"
|
||||||
rightSection={
|
rightSection={
|
||||||
searchTerm ? (
|
searchTerm ? (
|
||||||
@@ -564,11 +579,13 @@ const AlbumArtistMetadataTopSongsContent = ({
|
|||||||
) : null}
|
) : null}
|
||||||
</Stack>
|
</Stack>
|
||||||
</section>
|
</section>
|
||||||
|
</Grid.Col>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const AlbumArtistMetadataTopSongs = ({
|
const AlbumArtistMetadataTopSongs = ({
|
||||||
detailQuery,
|
detailQuery,
|
||||||
|
order,
|
||||||
routeId,
|
routeId,
|
||||||
}: AlbumArtistMetadataTopSongsProps) => {
|
}: AlbumArtistMetadataTopSongsProps) => {
|
||||||
const server = useCurrentServer();
|
const server = useCurrentServer();
|
||||||
@@ -581,17 +598,25 @@ const AlbumArtistMetadataTopSongs = ({
|
|||||||
return (
|
return (
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
{canStartQuery ? (
|
{canStartQuery ? (
|
||||||
<AlbumArtistMetadataTopSongsContent detailQuery={detailQuery} routeId={routeId} />
|
<AlbumArtistMetadataTopSongsContent
|
||||||
|
detailQuery={detailQuery}
|
||||||
|
order={order}
|
||||||
|
routeId={routeId}
|
||||||
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</Suspense>
|
</Suspense>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
interface AlbumArtistMetadataFavoriteSongsProps {
|
interface AlbumArtistMetadataFavoriteSongsProps {
|
||||||
|
order?: number;
|
||||||
routeId: string;
|
routeId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AlbumArtistMetadataFavoriteSongs = ({ routeId }: AlbumArtistMetadataFavoriteSongsProps) => {
|
const AlbumArtistMetadataFavoriteSongs = ({
|
||||||
|
order,
|
||||||
|
routeId,
|
||||||
|
}: AlbumArtistMetadataFavoriteSongsProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [debouncedSearchTerm] = useDebouncedValue(searchTerm, 300);
|
const [debouncedSearchTerm] = useDebouncedValue(searchTerm, 300);
|
||||||
@@ -671,10 +696,12 @@ const AlbumArtistMetadataFavoriteSongs = ({ routeId }: AlbumArtistMetadataFavori
|
|||||||
const isLoading = favoriteSongsQuery.isLoading || !favoriteSongsQuery.data;
|
const isLoading = favoriteSongsQuery.isLoading || !favoriteSongsQuery.data;
|
||||||
|
|
||||||
if (!isLoading && !tableConfig) return null;
|
if (!isLoading && !tableConfig) return null;
|
||||||
|
if (!isLoading && songs.length === 0) return null;
|
||||||
|
|
||||||
const currentSongId = currentSong?.id;
|
const currentSongId = currentSong?.id;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Grid.Col order={order} span={12}>
|
||||||
<section>
|
<section>
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<div className={styles.albumSectionTitle}>
|
<div className={styles.albumSectionTitle}>
|
||||||
@@ -691,9 +718,12 @@ const AlbumArtistMetadataFavoriteSongs = ({ routeId }: AlbumArtistMetadataFavori
|
|||||||
<Button
|
<Button
|
||||||
component={Link}
|
component={Link}
|
||||||
size="compact-md"
|
size="compact-md"
|
||||||
to={generatePath(AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL_FAVORITE_SONGS, {
|
to={generatePath(
|
||||||
|
AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL_FAVORITE_SONGS,
|
||||||
|
{
|
||||||
albumArtistId: routeId,
|
albumArtistId: routeId,
|
||||||
})}
|
},
|
||||||
|
)}
|
||||||
uppercase
|
uppercase
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
>
|
>
|
||||||
@@ -751,7 +781,9 @@ const AlbumArtistMetadataFavoriteSongs = ({ routeId }: AlbumArtistMetadataFavori
|
|||||||
flex={1}
|
flex={1}
|
||||||
leftSection={<Icon icon="search" />}
|
leftSection={<Icon icon="search" />}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
placeholder={t('common.search', { postProcess: 'sentenceCase' })}
|
placeholder={t('common.search', {
|
||||||
|
postProcess: 'sentenceCase',
|
||||||
|
})}
|
||||||
radius="xl"
|
radius="xl"
|
||||||
rightSection={
|
rightSection={
|
||||||
searchTerm ? (
|
searchTerm ? (
|
||||||
@@ -819,6 +851,7 @@ const AlbumArtistMetadataFavoriteSongs = ({ routeId }: AlbumArtistMetadataFavori
|
|||||||
) : null}
|
) : null}
|
||||||
</Stack>
|
</Stack>
|
||||||
</section>
|
</section>
|
||||||
|
</Grid.Col>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -828,6 +861,7 @@ interface AlbumArtistMetadataExternalLinksProps {
|
|||||||
lastFM: boolean;
|
lastFM: boolean;
|
||||||
mbzId?: null | string;
|
mbzId?: null | string;
|
||||||
musicBrainz: boolean;
|
musicBrainz: boolean;
|
||||||
|
order?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AlbumArtistMetadataExternalLinks = ({
|
const AlbumArtistMetadataExternalLinks = ({
|
||||||
@@ -836,12 +870,14 @@ const AlbumArtistMetadataExternalLinks = ({
|
|||||||
lastFM,
|
lastFM,
|
||||||
mbzId,
|
mbzId,
|
||||||
musicBrainz,
|
musicBrainz,
|
||||||
|
order,
|
||||||
}: AlbumArtistMetadataExternalLinksProps) => {
|
}: AlbumArtistMetadataExternalLinksProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
if (!externalLinks || (!lastFM && !musicBrainz)) return null;
|
if (!externalLinks || (!lastFM && !musicBrainz)) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Grid.Col order={order} span={12}>
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
<Text fw={600} isNoSelect size="sm" tt="uppercase">
|
<Text fw={600} isNoSelect size="sm" tt="uppercase">
|
||||||
{t('common.externalLinks', {
|
{t('common.externalLinks', {
|
||||||
@@ -885,14 +921,19 @@ const AlbumArtistMetadataExternalLinks = ({
|
|||||||
) : null}
|
) : null}
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
</Grid.Col>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
interface AlbumArtistMetadataSimilarArtistsProps {
|
interface AlbumArtistMetadataSimilarArtistsProps {
|
||||||
|
order?: number;
|
||||||
routeId: string;
|
routeId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AlbumArtistMetadataSimilarArtists = ({ routeId }: AlbumArtistMetadataSimilarArtistsProps) => {
|
const AlbumArtistMetadataSimilarArtists = ({
|
||||||
|
order,
|
||||||
|
routeId,
|
||||||
|
}: AlbumArtistMetadataSimilarArtistsProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const server = useCurrentServer();
|
const server = useCurrentServer();
|
||||||
const serverId = useCurrentServerId();
|
const serverId = useCurrentServerId();
|
||||||
@@ -957,6 +998,7 @@ const AlbumArtistMetadataSimilarArtists = ({ routeId }: AlbumArtistMetadataSimil
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Grid.Col order={order} span={12}>
|
||||||
<AlbumArtistGridCarousel
|
<AlbumArtistGridCarousel
|
||||||
data={similarArtists}
|
data={similarArtists}
|
||||||
excludeIds={[routeId]}
|
excludeIds={[routeId]}
|
||||||
@@ -964,6 +1006,7 @@ const AlbumArtistMetadataSimilarArtists = ({ routeId }: AlbumArtistMetadataSimil
|
|||||||
rowCount={1}
|
rowCount={1}
|
||||||
title={carouselTitle}
|
title={carouselTitle}
|
||||||
/>
|
/>
|
||||||
|
</Grid.Col>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1022,7 +1065,6 @@ export const AlbumArtistDetailContent = ({
|
|||||||
[routeId, detailQuery.data?.name],
|
[routeId, detailQuery.data?.name],
|
||||||
);
|
);
|
||||||
|
|
||||||
const showGenres = detailQuery.data?.genres ? detailQuery.data.genres.length !== 0 : false;
|
|
||||||
const mbzId = detailQuery.data?.mbz;
|
const mbzId = detailQuery.data?.mbz;
|
||||||
|
|
||||||
const handleArtistRadio = useCallback(async () => {
|
const handleArtistRadio = useCallback(async () => {
|
||||||
@@ -1061,50 +1103,46 @@ export const AlbumArtistDetailContent = ({
|
|||||||
onArtistRadio={handleArtistRadio}
|
onArtistRadio={handleArtistRadio}
|
||||||
/>
|
/>
|
||||||
<Grid gutter="2xl">
|
<Grid gutter="2xl">
|
||||||
{showGenres && (
|
<AlbumArtistMetadataGenres
|
||||||
<Grid.Col order={genresOrder} span={12}>
|
genres={detailQuery.data?.genres}
|
||||||
<AlbumArtistMetadataGenres genres={detailQuery.data?.genres} />
|
order={genresOrder}
|
||||||
</Grid.Col>
|
/>
|
||||||
)}
|
|
||||||
{externalLinks && (lastFM || musicBrainz) && (
|
{externalLinks && (lastFM || musicBrainz) && (
|
||||||
<Grid.Col order={externalLinksOrder} span={12}>
|
|
||||||
<AlbumArtistMetadataExternalLinks
|
<AlbumArtistMetadataExternalLinks
|
||||||
artistName={detailQuery.data?.name}
|
artistName={detailQuery.data?.name}
|
||||||
externalLinks={externalLinks}
|
externalLinks={externalLinks}
|
||||||
lastFM={lastFM}
|
lastFM={lastFM}
|
||||||
mbzId={mbzId}
|
mbzId={mbzId}
|
||||||
musicBrainz={musicBrainz}
|
musicBrainz={musicBrainz}
|
||||||
|
order={externalLinksOrder}
|
||||||
/>
|
/>
|
||||||
</Grid.Col>
|
|
||||||
)}
|
)}
|
||||||
{enabledItem.biography && (
|
{enabledItem.biography && (
|
||||||
<Grid.Col order={itemOrder.biography} span={12}>
|
|
||||||
<AlbumArtistMetadataBiography
|
<AlbumArtistMetadataBiography
|
||||||
artistName={detailQuery.data?.name}
|
artistName={detailQuery.data?.name}
|
||||||
|
order={itemOrder.biography}
|
||||||
routeId={routeId}
|
routeId={routeId}
|
||||||
/>
|
/>
|
||||||
</Grid.Col>
|
|
||||||
)}
|
)}
|
||||||
<Grid.Col order={itemOrder.recentAlbums} span={12}>
|
<ArtistAlbums albumsQuery={albumsQuery} order={itemOrder.recentAlbums} />
|
||||||
<ArtistAlbums albumsQuery={albumsQuery} />
|
|
||||||
</Grid.Col>
|
|
||||||
{enabledItem.similarArtists && (
|
{enabledItem.similarArtists && (
|
||||||
<Grid.Col order={itemOrder.similarArtists} span={12}>
|
<AlbumArtistMetadataSimilarArtists
|
||||||
<AlbumArtistMetadataSimilarArtists routeId={routeId} />
|
order={itemOrder.similarArtists}
|
||||||
</Grid.Col>
|
routeId={routeId}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{enabledItem.topSongs && (
|
{enabledItem.topSongs && (
|
||||||
<Grid.Col order={itemOrder.topSongs} span={12}>
|
|
||||||
<AlbumArtistMetadataTopSongs
|
<AlbumArtistMetadataTopSongs
|
||||||
detailQuery={detailQuery}
|
detailQuery={detailQuery}
|
||||||
|
order={itemOrder.topSongs}
|
||||||
routeId={routeId}
|
routeId={routeId}
|
||||||
/>
|
/>
|
||||||
</Grid.Col>
|
|
||||||
)}
|
)}
|
||||||
{enabledItem.favoriteSongs && (
|
{enabledItem.favoriteSongs && (
|
||||||
<Grid.Col order={itemOrder.favoriteSongs} span={12}>
|
<AlbumArtistMetadataFavoriteSongs
|
||||||
<AlbumArtistMetadataFavoriteSongs routeId={routeId} />
|
order={itemOrder.favoriteSongs}
|
||||||
</Grid.Col>
|
routeId={routeId}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
</div>
|
</div>
|
||||||
@@ -1427,9 +1465,10 @@ const releaseTypeToEnumMap: Record<string, ArtistReleaseTypeItem> = {
|
|||||||
|
|
||||||
interface ArtistAlbumsProps {
|
interface ArtistAlbumsProps {
|
||||||
albumsQuery: UseSuspenseQueryResult<AlbumListResponse, Error>;
|
albumsQuery: UseSuspenseQueryResult<AlbumListResponse, Error>;
|
||||||
|
order?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ArtistAlbums = ({ albumsQuery }: ArtistAlbumsProps) => {
|
const ArtistAlbums = ({ albumsQuery, order }: ArtistAlbumsProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const artistReleaseTypeItems = useArtistReleaseTypeItems();
|
const artistReleaseTypeItems = useArtistReleaseTypeItems();
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
@@ -1674,6 +1713,7 @@ const ArtistAlbums = ({ albumsQuery }: ArtistAlbumsProps) => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Grid.Col order={order} span={12}>
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<Group gap="sm" w="100%">
|
<Group gap="sm" w="100%">
|
||||||
<TextInput
|
<TextInput
|
||||||
@@ -1710,7 +1750,9 @@ const ArtistAlbums = ({ albumsQuery }: ArtistAlbumsProps) => {
|
|||||||
sortBy={sortBy}
|
sortBy={sortBy}
|
||||||
/>
|
/>
|
||||||
<ListSortOrderToggleButtonControlled
|
<ListSortOrderToggleButtonControlled
|
||||||
setSortOrder={(value) => setAlbumArtistDetailSort(sortBy, value as SortOrder)}
|
setSortOrder={(value) =>
|
||||||
|
setAlbumArtistDetailSort(sortBy, value as SortOrder)
|
||||||
|
}
|
||||||
sortOrder={sortOrder}
|
sortOrder={sortOrder}
|
||||||
/>
|
/>
|
||||||
<GroupingTypeSelector />
|
<GroupingTypeSelector />
|
||||||
@@ -1736,6 +1778,7 @@ const ArtistAlbums = ({ albumsQuery }: ArtistAlbumsProps) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
</Grid.Col>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user