move genre buttons to album detail header

This commit is contained in:
jeffvli
2025-11-19 02:30:01 -08:00
parent 1785e5c3a6
commit 1735b64d76
2 changed files with 74 additions and 62 deletions
@@ -1,7 +1,7 @@
import { useQuery } from '@tanstack/react-query';
import { Suspense, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { generatePath, Link, useParams } from 'react-router';
import { useParams } from 'react-router';
import styles from './album-detail-content.module.css';
@@ -19,7 +19,6 @@ import { ListConfigMenu } from '/@/renderer/features/shared/components/list-conf
import { PlayButton } from '/@/renderer/features/shared/components/play-button';
import { searchLibraryItems } from '/@/renderer/features/shared/utils';
import { useContainerQuery } from '/@/renderer/hooks';
import { useGenreRoute } from '/@/renderer/hooks/use-genre-route';
import { useCurrentServer } from '/@/renderer/store';
import {
useGeneralSettings,
@@ -28,7 +27,6 @@ import {
} from '/@/renderer/store/settings.store';
import { replaceURLWithHTMLLinks } from '/@/renderer/utils/linkify';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Button } from '/@/shared/components/button/button';
import { Checkbox } from '/@/shared/components/checkbox/checkbox';
import { Group } from '/@/shared/components/group/group';
import { Icon } from '/@/shared/components/icon/icon';
@@ -54,7 +52,6 @@ export const AlbumDetailContent = ({ background }: AlbumDetailContentProps) => {
const { ref, ...cq } = useContainerQuery();
const { externalLinks, lastFM, musicBrainz } = useGeneralSettings();
const genreRoute = useGenreRoute();
const carousels = [
{
@@ -94,23 +91,13 @@ export const AlbumDetailContent = ({ background }: AlbumDetailContentProps) => {
];
const playButtonBehavior = usePlayButtonBehavior();
const { addToQueueByFetch, setFavorite } = usePlayer();
const { addToQueueByFetch } = usePlayer();
const handlePlay = () => {
if (!server?.id) return;
addToQueueByFetch(server.id, [albumId], LibraryItem.ALBUM, playButtonBehavior);
};
const handleFavorite = () => {
if (!detailQuery?.data) return;
setFavorite(
detailQuery.data._serverId,
[detailQuery.data.id],
LibraryItem.ALBUM,
!detailQuery.data.userFavorite,
);
};
const handleMoreOptions = (e: React.MouseEvent<HTMLButtonElement>) => {
if (!detailQuery?.data) return;
ContextMenuController.call({
@@ -119,7 +106,6 @@ export const AlbumDetailContent = ({ background }: AlbumDetailContentProps) => {
});
};
const showGenres = detailQuery?.data?.genres ? detailQuery?.data?.genres.length !== 0 : false;
const comment = detailQuery?.data?.comment;
const mbzId = detailQuery?.data?.mbzId;
@@ -132,48 +118,15 @@ export const AlbumDetailContent = ({ background }: AlbumDetailContentProps) => {
<Group gap="sm" justify="space-between">
<Group>
<PlayButton onClick={handlePlay} />
<Group gap="xs">
<ActionIcon
icon="favorite"
iconProps={{
fill: detailQuery?.data?.userFavorite
? 'primary'
: undefined,
}}
onClick={handleFavorite}
size="lg"
variant="transparent"
/>
<ActionIcon
icon="ellipsisHorizontal"
onClick={handleMoreOptions}
size="lg"
variant="transparent"
/>
</Group>
<ActionIcon
icon="ellipsisHorizontal"
onClick={handleMoreOptions}
size="lg"
variant="transparent"
/>
</Group>
</Group>
</section>
{showGenres && (
<section>
<Group gap="sm">
{detailQuery?.data?.genres?.map((genre) => (
<Button
component={Link}
key={`genre-${genre.id}`}
radius="md"
size="compact-md"
to={generatePath(genreRoute, {
genreId: genre.id,
})}
variant="outline"
>
{genre.name}
</Button>
))}
</Group>
</section>
)}
{externalLinks && (lastFM || musicBrainz) ? (
<section>
<Group gap="sm">
@@ -435,6 +388,12 @@ const AlbumDetailSongsTable = ({ songs }: AlbumDetailSongsTableProps) => {
/>
) : null
}
styles={{
input: {
background: 'transparent',
border: '1px solid rgba(255, 255, 255, 0.05)',
},
}}
value={searchTerm}
/>
<ListConfigMenu
@@ -6,10 +6,13 @@ import { generatePath, Link, useParams } from 'react-router';
import { albumQueries } from '/@/renderer/features/albums/api/album-api';
import { usePlayer } from '/@/renderer/features/player/context/player-context';
import { LibraryHeader } from '/@/renderer/features/shared/components/library-header';
import { useGenreRoute } from '/@/renderer/hooks/use-genre-route';
import { AppRoute } from '/@/renderer/router/routes';
import { useCurrentServer } from '/@/renderer/store';
import { formatDateAbsoluteUTC, formatDurationString, titleCase } from '/@/renderer/utils';
import { normalizeReleaseTypes } from '/@/renderer/utils/normalize-release-types';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Button } from '/@/shared/components/button/button';
import { Group } from '/@/shared/components/group/group';
import { Pill } from '/@/shared/components/pill/pill';
import { Rating } from '/@/shared/components/rating/rating';
@@ -33,11 +36,16 @@ export const AlbumDetailHeader = forwardRef<HTMLDivElement, AlbumDetailHeaderPro
albumQueries.detail({ query: { id: albumId }, serverId: server?.id }),
);
const { t } = useTranslation();
const genreRoute = useGenreRoute();
const showRating =
detailQuery?.data?._serverType === ServerType.NAVIDROME ||
detailQuery?.data?._serverType === ServerType.SUBSONIC;
const showGenres = detailQuery?.data?.genres
? detailQuery?.data?.genres.length !== 0
: false;
const originalDifferentFromRelease =
detailQuery.data?.originalDate &&
detailQuery.data.originalDate !== detailQuery.data.releaseDate;
@@ -95,7 +103,17 @@ export const AlbumDetailHeader = forwardRef<HTMLDivElement, AlbumDetailHeaderPro
});
}
const { setRating } = usePlayer();
const { setFavorite, setRating } = usePlayer();
const handleFavorite = () => {
if (!detailQuery?.data) return;
setFavorite(
detailQuery.data._serverId,
[detailQuery.data.id],
LibraryItem.ALBUM,
!detailQuery.data.userFavorite,
);
};
const handleUpdateRating = (rating: number) => {
if (!detailQuery?.data) return;
@@ -133,13 +151,48 @@ export const AlbumDetailHeader = forwardRef<HTMLDivElement, AlbumDetailHeaderPro
),
)}
</Pill.Group>
{showRating && (
<Rating
onChange={handleUpdateRating}
readOnly={detailQuery?.isFetching}
value={detailQuery?.data?.userRating || 0}
/>
{showGenres && (
<Group gap="sm">
{detailQuery?.data?.genres?.map((genre) => (
<Button
component={Link}
key={`genre-${genre.id}`}
radius="md"
size="compact-md"
to={generatePath(genreRoute, {
genreId: genre.id,
})}
variant="outline"
>
{genre.name}
</Button>
))}
</Group>
)}
<Group align="center" gap="sm">
<ActionIcon
icon="favorite"
iconProps={{
fill: detailQuery?.data?.userFavorite ? 'primary' : undefined,
size: 'lg',
}}
onClick={handleFavorite}
size="xs"
variant="transparent"
/>
{showRating && (
<Rating
onChange={handleUpdateRating}
readOnly={detailQuery?.isFetching}
styles={{
input: {
background: 'transparent',
},
}}
value={detailQuery?.data?.userRating || 0}
/>
)}
</Group>
<Group
gap="md"
mah="4rem"