Files
feishin/src/renderer/components/item-list/item-table-list/album-group-controls.tsx
T
Marc Plano-Lesay 07ef323ccc Make ratings/favourites a bit more customizable (#2262)
* Replace show ratings toggle with favorite/rating controls setting

Swap `genera.showRatings` (previously a simple bool) for a
`favoriteRatingControls` enum, exposed via derived `useShowRatings` /
`useShowFavorites` hooks. Settings switch becomes a select, with a
migration to a new v32 schema mapping the old boolean forward with no
user change.

* Gate favorite controls on `favoriteRatingControls` setting

Hide the playerbar and mobile fullscreen favorite buttons and the
context-menu favorite action when the setting excludes favorites,
mirroring how ratings are already gated.

* Gate favorite controls on item cards and detail list

Drill a `showFavorite` flag through the item-card tree (mirroring
`showRating`), and gate the card/detail-list favorite badges and hover
buttons on the `favoriteRatingControls` setting.

* Gate favorite controls in detail headers and album group controls

* Keep the original `showRatings` switch and add a `showFavorites`

* Gate album header favorite hander at definition
2026-07-26 12:59:17 +00:00

112 lines
4.2 KiB
TypeScript

import { useQuery } from '@tanstack/react-query';
import { useCallback } from 'react';
import styles from './album-group-controls.module.css';
import { albumQueries } from '/@/renderer/features/albums/api/album-api';
import { useSetFavorite } from '/@/renderer/features/shared/hooks/use-set-favorite';
import { useSetRating } from '/@/renderer/features/shared/hooks/use-set-rating';
import { useIsMutatingCreateFavorite } from '/@/renderer/features/shared/mutations/create-favorite-mutation';
import { useIsMutatingDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-favorite-mutation';
import { useIsMutatingRating } from '/@/renderer/features/shared/mutations/set-rating-mutation';
import { useShowFavorites, useShowRatings } from '/@/renderer/store';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Rating } from '/@/shared/components/rating/rating';
import { LibraryItem, ServerType } from '/@/shared/types/domain-types';
const isRatingSupported = (serverType: ServerType | undefined) =>
serverType === ServerType.NAVIDROME || serverType === ServerType.SUBSONIC;
const useAlbumGroupAlbum = (albumId: string | undefined, serverId: string | undefined) => {
const enabled = !!albumId && !!serverId && !albumId.startsWith('dummy/');
return useQuery({
...albumQueries.detail({ query: { id: albumId! }, serverId: serverId! }),
enabled,
staleTime: 5 * 60 * 1000,
});
};
interface AlbumGroupControlsProps {
albumId: string | undefined;
serverId: string | undefined;
serverType: ServerType | undefined;
}
export const AlbumGroupControls = ({ albumId, serverId, serverType }: AlbumGroupControlsProps) => {
const showRatingsSetting = useShowRatings();
const showFavorites = useShowFavorites();
const detailQuery = useAlbumGroupAlbum(albumId, serverId);
const setFavorite = useSetFavorite();
const setRating = useSetRating();
const isMutatingCreateFavorite = useIsMutatingCreateFavorite();
const isMutatingDeleteFavorite = useIsMutatingDeleteFavorite();
const isMutatingFavorite = isMutatingCreateFavorite || isMutatingDeleteFavorite;
const isMutatingRating = useIsMutatingRating();
const album = detailQuery.data;
const showRating = showRatingsSetting && isRatingSupported(serverType ?? album?._serverType);
const handleFavorite = useCallback(
(event: React.MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
event.preventDefault();
if (!album) return;
setFavorite(album._serverId, [album.id], LibraryItem.ALBUM, !album.userFavorite);
},
[album, setFavorite],
);
const handleRating = useCallback(
(rating: number) => {
if (!album) return;
if (album.userRating === rating) {
return setRating(album._serverId, [album.id], LibraryItem.ALBUM, 0);
}
return setRating(album._serverId, [album.id], LibraryItem.ALBUM, rating);
},
[album, setRating],
);
if (!album) {
return null;
}
return (
<div className={styles.controls}>
{showFavorites && (
<ActionIcon
className={styles.favorite}
disabled={isMutatingFavorite}
icon="favorite"
iconProps={{
color: album.userFavorite ? 'primary' : 'muted',
fill: album.userFavorite ? 'primary' : undefined,
size: 'xs',
}}
onClick={handleFavorite}
onDoubleClick={(event) => {
event.stopPropagation();
event.preventDefault();
}}
size="xs"
variant="transparent"
/>
)}
{showRating && (
<Rating
className={styles.rating}
onChange={handleRating}
readOnly={isMutatingRating}
size="xs"
value={album.userRating || 0}
/>
)}
</div>
);
};