refactor grid columns internally into album artist sections to handle null cases

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