diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json
index b251ec10a..f232c1c7a 100644
--- a/src/i18n/locales/en.json
+++ b/src/i18n/locales/en.json
@@ -1085,10 +1085,12 @@
"preferLocalLyrics": "Prefer local lyrics",
"showLyricsInSidebar_description": "A panel will be added to the attached play queue that displays the lyrics",
"showLyricsInSidebar": "Show lyrics in player sidebar",
+ "showFavorites": "Show favorites",
+ "showFavorites_description": "Controls if the favorite (heart) buttons show up in the interface",
"showQueueInSidebar_description": "A panel will be added to the player sidebar that displays the play queue",
"showQueueInSidebar": "Show play queue in player sidebar",
- "showRatings_description": "Controls if the star ratings feature shows up in the interface",
"showRatings": "Show star ratings",
+ "showRatings_description": "Controls if the star ratings feature shows up in the interface",
"blurExplicitImages": "Blur explicit images",
"blurExplicitImages_description": "Album and song artwork tagged as explicit will be blurred",
"enableGridMultiSelect": "Enable grid multi-select",
diff --git a/src/renderer/components/item-card/item-card-controls.tsx b/src/renderer/components/item-card/item-card-controls.tsx
index 11800ee60..86471d43c 100644
--- a/src/renderer/components/item-card/item-card-controls.tsx
+++ b/src/renderer/components/item-card/item-card-controls.tsx
@@ -33,6 +33,7 @@ interface ItemCardControlsProps {
internalState?: ItemListStateActions;
item: Album | AlbumArtist | Artist | Genre | Playlist | Song | undefined;
itemType: LibraryItem;
+ showFavorite: boolean;
showRating: boolean;
type?: 'compact' | 'default' | 'poster';
}
@@ -205,6 +206,7 @@ export const ItemCardControls = ({
internalState,
item,
itemType,
+ showFavorite,
showRating,
type = 'default',
}: ItemCardControlsProps) => {
@@ -289,7 +291,7 @@ export const ItemCardControls = ({
)}
- {controls?.onFavorite && (
+ {controls?.onFavorite && showFavorite && (
)}
{controls?.onRating &&
diff --git a/src/renderer/components/item-card/item-card.tsx b/src/renderer/components/item-card/item-card.tsx
index 723907390..815e731ba 100644
--- a/src/renderer/components/item-card/item-card.tsx
+++ b/src/renderer/components/item-card/item-card.tsx
@@ -19,7 +19,7 @@ import { ItemControls } from '/@/renderer/components/item-list/types';
import { JoinedArtists } from '/@/renderer/features/albums/components/joined-artists';
import { useDragDrop } from '/@/renderer/hooks/use-drag-drop';
import { AppRoute } from '/@/renderer/router/routes';
-import { useShowRatings } from '/@/renderer/store';
+import { useShowFavorites, useShowRatings } from '/@/renderer/store';
import {
formatDateAbsolute,
formatDateRelative,
@@ -90,6 +90,7 @@ export const ItemCard = ({
withControls,
}: ItemCardProps) => {
const showRatings = useShowRatings();
+ const showFavorites = useShowFavorites();
const imageUrl = getImageUrl(data);
const rows = providedRows || [];
@@ -110,6 +111,7 @@ export const ItemCard = ({
isRound={isRound}
itemType={itemType}
rows={rows}
+ showFavorite={showFavorites}
showRating={showRatings}
withControls={withControls}
/>
@@ -130,6 +132,7 @@ export const ItemCard = ({
isRound={isRound}
itemType={itemType}
rows={rows}
+ showFavorite={showFavorites}
showRating={showRatings}
withControls={withControls}
/>
@@ -150,6 +153,7 @@ export const ItemCard = ({
isRound={isRound}
itemType={itemType}
rows={rows}
+ showFavorite={showFavorites}
showRating={showRatings}
withControls={withControls}
/>
@@ -166,6 +170,7 @@ export interface ItemCardDerivativeProps extends Omit {
imageUrl: string | undefined;
internalState?: ItemListStateActions;
rows: DataRow[];
+ showFavorite: boolean;
showRating: boolean;
}
@@ -186,6 +191,7 @@ const ItemCardStandardImageArea = memo(function ItemCardStandardImageArea({
isRound,
itemType,
navigationPath,
+ showFavorite,
showRating,
variant,
withControls,
@@ -204,6 +210,7 @@ const ItemCardStandardImageArea = memo(function ItemCardStandardImageArea({
isRound?: boolean;
itemType: LibraryItem;
navigationPath: null | string;
+ showFavorite: boolean;
showRating: boolean;
variant: 'default' | 'poster';
withControls?: boolean;
@@ -259,7 +266,7 @@ const ItemCardStandardImageArea = memo(function ItemCardStandardImageArea({
type="itemCard"
/>
)}
- {isFavorite && }
+ {showFavorite && isFavorite && }
{hasRating && {userRating}
}
{withControls && showControls && (
@@ -269,6 +276,7 @@ const ItemCardStandardImageArea = memo(function ItemCardStandardImageArea({
{...(variant === 'poster' ? { internalState } : {})}
item={data}
itemType={itemType}
+ showFavorite={showFavorite}
showRating={showRating}
type={variant}
/>
@@ -321,6 +329,7 @@ const CompactItemCardImageArea = memo(function CompactItemCardImageArea({
itemType,
navigationPath,
rows,
+ showFavorite,
showRating,
withControls,
}: {
@@ -338,6 +347,7 @@ const CompactItemCardImageArea = memo(function CompactItemCardImageArea({
itemType: LibraryItem;
navigationPath: null | string;
rows: DataRow[];
+ showFavorite: boolean;
showRating: boolean;
withControls?: boolean;
}) {
@@ -393,7 +403,7 @@ const CompactItemCardImageArea = memo(function CompactItemCardImageArea({
type="itemCard"
/>
)}
- {isFavorite && }
+ {showFavorite && isFavorite && }
{hasRating && {userRating}
}
{withControls && showControls && data && (
@@ -403,6 +413,7 @@ const CompactItemCardImageArea = memo(function CompactItemCardImageArea({
internalState={internalState}
item={data}
itemType={itemType}
+ showFavorite={showFavorite}
showRating={showRating}
type="compact"
/>
@@ -468,6 +479,7 @@ const CompactItemCard = ({
isRound,
itemType,
rows,
+ showFavorite,
showRating,
withControls,
}: ItemCardDerivativeProps) => {
@@ -634,6 +646,7 @@ const CompactItemCard = ({
itemType={itemType}
navigationPath={navigationPath}
rows={rows}
+ showFavorite={showFavorite}
showRating={showRating}
withControls={withControls}
/>
@@ -679,6 +692,7 @@ const DefaultItemCard = ({
isRound,
itemType,
rows,
+ showFavorite,
showRating,
withControls,
}: ItemCardDerivativeProps) => {
@@ -777,6 +791,7 @@ const DefaultItemCard = ({
isRound={isRound}
itemType={itemType}
navigationPath={navigationPath}
+ showFavorite={showFavorite}
showRating={showRating}
variant="default"
withControls={withControls}
@@ -840,6 +855,7 @@ const PosterItemCard = ({
isRound,
itemType,
rows,
+ showFavorite,
showRating,
withControls,
}: ItemCardDerivativeProps) => {
@@ -1005,6 +1021,7 @@ const PosterItemCard = ({
isRound={isRound}
itemType={itemType}
navigationPath={navigationPath}
+ showFavorite={showFavorite}
showRating={showRating}
variant="poster"
withControls={withControls}
diff --git a/src/renderer/components/item-list/item-detail-list/item-detail-list.tsx b/src/renderer/components/item-list/item-detail-list/item-detail-list.tsx
index 8a78a0fe7..1679b7d45 100644
--- a/src/renderer/components/item-list/item-detail-list/item-detail-list.tsx
+++ b/src/renderer/components/item-list/item-detail-list/item-detail-list.tsx
@@ -67,7 +67,7 @@ import { useIsMutatingDeleteFavorite } from '/@/renderer/features/shared/mutatio
import { songsQueries } from '/@/renderer/features/songs/api/songs-api';
import { useDragDrop } from '/@/renderer/hooks/use-drag-drop';
import { AppRoute } from '/@/renderer/router/routes';
-import { useSettingsStore, useShowRatings } from '/@/renderer/store';
+import { useSettingsStore, useShowFavorites, useShowRatings } from '/@/renderer/store';
import { formatDurationString, formatPartialIsoDateUTC } from '/@/renderer/utils';
import { SEPARATOR_STRING } from '/@/shared/api/utils';
import { ExplicitIndicator } from '/@/shared/components/explicit-indicator/explicit-indicator';
@@ -426,6 +426,7 @@ const MetadataSection = memo(
({ controls, internalState, item }: MetadataSectionProps) => {
const { t } = useTranslation();
const showRatings = useShowRatings();
+ const showFavorites = useShowFavorites();
const [isImageHovered, setIsImageHovered] = useState(false);
const [isMetadataHovered, setIsMetadataHovered] = useState(false);
@@ -570,7 +571,7 @@ const MetadataSection = memo(
serverId={item._serverId}
type="itemCard"
/>
- {isFavorite && }
+ {showFavorites && isFavorite && }
{hasRating && {userRating}
}
{controls && isImageHovered && (
@@ -580,6 +581,7 @@ const MetadataSection = memo(
internalState={internalState}
item={item}
itemType={item._itemType}
+ showFavorite={showFavorites}
showRating={true}
type="compact"
/>
diff --git a/src/renderer/components/item-list/item-table-list/album-group-controls.tsx b/src/renderer/components/item-list/item-table-list/album-group-controls.tsx
index 8df2d9da5..a13991436 100644
--- a/src/renderer/components/item-list/item-table-list/album-group-controls.tsx
+++ b/src/renderer/components/item-list/item-table-list/album-group-controls.tsx
@@ -9,7 +9,7 @@ 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 { useShowRatings } from '/@/renderer/store';
+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';
@@ -35,6 +35,7 @@ interface AlbumGroupControlsProps {
export const AlbumGroupControls = ({ albumId, serverId, serverType }: AlbumGroupControlsProps) => {
const showRatingsSetting = useShowRatings();
+ const showFavorites = useShowFavorites();
const detailQuery = useAlbumGroupAlbum(albumId, serverId);
const setFavorite = useSetFavorite();
const setRating = useSetRating();
@@ -77,23 +78,25 @@ export const AlbumGroupControls = ({ albumId, serverId, serverType }: AlbumGroup
return (
-
{
- event.stopPropagation();
- event.preventDefault();
- }}
- size="xs"
- variant="transparent"
- />
+ {showFavorites && (
+ {
+ event.stopPropagation();
+ event.preventDefault();
+ }}
+ size="xs"
+ variant="transparent"
+ />
+ )}
{showRating && (
((_props, ref) => {
const { t } = useTranslation();
const server = useCurrentServer();
const showRatings = useShowRatings();
+ const showFavorites = useShowFavorites();
const queryClient = useQueryClient();
const albumRadioCount = useArtistRadioCount();
const detailQuery = useQuery(
@@ -51,15 +52,17 @@ export const AlbumDetailHeader = forwardRef((_props, ref) => {
const setRating = useSetRating();
const setFavorite = useSetFavorite();
- const handleFavorite = () => {
- if (!detailQuery?.data) return;
- setFavorite(
- detailQuery.data._serverId,
- [detailQuery.data.id],
- LibraryItem.ALBUM,
- !detailQuery.data.userFavorite,
- );
- };
+ const handleFavorite = showFavorites
+ ? () => {
+ if (!detailQuery?.data) return;
+ setFavorite(
+ detailQuery.data._serverId,
+ [detailQuery.data.id],
+ LibraryItem.ALBUM,
+ !detailQuery.data.userFavorite,
+ );
+ }
+ : undefined;
const handleUpdateRating = showRating
? (rating: number) => {
diff --git a/src/renderer/features/albums/routes/dummy-album-detail-route.tsx b/src/renderer/features/albums/routes/dummy-album-detail-route.tsx
index dc7db3c41..cb807de05 100644
--- a/src/renderer/features/albums/routes/dummy-album-detail-route.tsx
+++ b/src/renderer/features/albums/routes/dummy-album-detail-route.tsx
@@ -19,7 +19,7 @@ import { useDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-
import { useFastAverageColor } from '/@/renderer/hooks';
import { queryClient } from '/@/renderer/lib/react-query';
import { AppRoute } from '/@/renderer/router/routes';
-import { useCurrentServer } from '/@/renderer/store';
+import { useCurrentServer, useShowFavorites } from '/@/renderer/store';
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
import { formatDurationString } from '/@/renderer/utils';
import { replaceURLWithHTMLLinks } from '/@/renderer/utils/linkify';
@@ -38,6 +38,7 @@ const DummyAlbumDetailRoute = () => {
const { albumId } = useParams() as { albumId: string };
const server = useCurrentServer();
+ const showFavorites = useShowFavorites();
const queryKey = queryKeys.songs.detail(server?.id || '', albumId);
const detailQuery = useSuspenseQuery({
queryFn: ({ signal }) => {
@@ -178,20 +179,22 @@ const DummyAlbumDetailRoute = () => {
handlePlay()} />
-
+ {showFavorites && (
+
+ )}
{
diff --git a/src/renderer/features/artists/components/album-artist-detail-header.tsx b/src/renderer/features/artists/components/album-artist-detail-header.tsx
index 924a20a92..abe165088 100644
--- a/src/renderer/features/artists/components/album-artist-detail-header.tsx
+++ b/src/renderer/features/artists/components/album-artist-detail-header.tsx
@@ -19,7 +19,7 @@ import {
import { useSetFavorite } from '/@/renderer/features/shared/hooks/use-set-favorite';
import { useSetRating } from '/@/renderer/features/shared/hooks/use-set-rating';
import { AppRoute } from '/@/renderer/router/routes';
-import { useAppStore, useCurrentServer, useShowRatings } from '/@/renderer/store';
+import { useAppStore, useCurrentServer, useShowFavorites, useShowRatings } from '/@/renderer/store';
import { useArtistReleaseTypeItems, usePlayButtonBehavior } from '/@/renderer/store/settings.store';
import { formatDurationString } from '/@/renderer/utils';
import { hasFeature, SEPARATOR_STRING, sortAlbumList } from '/@/shared/api/utils';
@@ -105,6 +105,7 @@ export const AlbumArtistDetailHeader = forwardRef
handlePlay(type)}
onRating={showRating ? handleUpdateRating : undefined}
diff --git a/src/renderer/features/context-menu/actions/set-favorite-action.tsx b/src/renderer/features/context-menu/actions/set-favorite-action.tsx
index 42cbfa178..3236ca24c 100644
--- a/src/renderer/features/context-menu/actions/set-favorite-action.tsx
+++ b/src/renderer/features/context-menu/actions/set-favorite-action.tsx
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import { useCreateFavorite } from '/@/renderer/features/shared/mutations/create-favorite-mutation';
import { useDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-favorite-mutation';
-import { useCurrentServerId } from '/@/renderer/store';
+import { useCurrentServerId, useShowFavorites } from '/@/renderer/store';
import { ContextMenu } from '/@/shared/components/context-menu/context-menu';
import { LibraryItem } from '/@/shared/types/domain-types';
@@ -15,6 +15,7 @@ interface SetFavoriteActionProps {
export const SetFavoriteAction = ({ ids, itemType }: SetFavoriteActionProps) => {
const { t } = useTranslation();
const serverId = useCurrentServerId();
+ const showFavorites = useShowFavorites();
const createFavoriteMutation = useCreateFavorite({});
const deleteFavoriteMutation = useDeleteFavorite({});
@@ -43,6 +44,10 @@ export const SetFavoriteAction = ({ ids, itemType }: SetFavoriteActionProps) =>
});
}, [deleteFavoriteMutation, ids, itemType, serverId]);
+ if (!showFavorites) {
+ return null;
+ }
+
return (
diff --git a/src/renderer/features/player/components/mobile-fullscreen-player-metadata.tsx b/src/renderer/features/player/components/mobile-fullscreen-player-metadata.tsx
index ee90fb72b..23efe6568 100644
--- a/src/renderer/features/player/components/mobile-fullscreen-player-metadata.tsx
+++ b/src/renderer/features/player/components/mobile-fullscreen-player-metadata.tsx
@@ -19,6 +19,7 @@ interface MobileFullscreenPlayerMetadataProps {
radioArtist?: string;
radioStationName?: string;
radioTitle?: string;
+ showFavorite?: boolean;
showRating?: boolean;
}
@@ -30,6 +31,7 @@ export const MobileFullscreenPlayerMetadata = memo(
radioArtist,
radioStationName,
radioTitle,
+ showFavorite,
showRating,
}: MobileFullscreenPlayerMetadataProps) => {
const isRadio = radioTitle !== undefined || radioStationName !== undefined;
@@ -77,16 +79,18 @@ export const MobileFullscreenPlayerMetadata = memo(
)}
{!isRadio && (
-
+ {showFavorite && (
+
+ )}
{showRating && (
)}
diff --git a/src/renderer/features/player/components/mobile-fullscreen-player.tsx b/src/renderer/features/player/components/mobile-fullscreen-player.tsx
index a61f5bd64..f9c8e9733 100644
--- a/src/renderer/features/player/components/mobile-fullscreen-player.tsx
+++ b/src/renderer/features/player/components/mobile-fullscreen-player.tsx
@@ -35,10 +35,11 @@ import {
useCurrentServer,
useFullScreenPlayerStore,
useFullScreenPlayerStoreActions,
- useGeneralSettings,
usePlayerData,
usePlayerSong,
useSetFullScreenPlayerStore,
+ useShowFavorites,
+ useShowRatings,
} from '/@/renderer/store';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Text } from '/@/shared/components/text/text';
@@ -387,7 +388,8 @@ export const MobileFullscreenPlayer = () => {
const isPlayingRadio = isRadioActive && isRadioPlaying;
const effectiveDynamicBackground = dynamicBackground && !isPlayingRadio;
const setFavorite = useSetFavorite();
- const { showRatings: showRatingsSetting } = useGeneralSettings();
+ const showRatingsSetting = useShowRatings();
+ const showFavorites = useShowFavorites();
const setRating = useSetRating();
const [isPageHovered, setIsPageHovered] = useState(false);
@@ -482,6 +484,7 @@ export const MobileFullscreenPlayer = () => {
radioArtist={isPlayingRadio ? (radioMetadata?.artist ?? undefined) : undefined}
radioStationName={isPlayingRadio ? (stationName ?? undefined) : undefined}
radioTitle={isPlayingRadio ? (radioMetadata?.title ?? undefined) : undefined}
+ showFavorite={showFavorites}
showRating={showRating}
/>
diff --git a/src/renderer/features/player/components/right-controls.tsx b/src/renderer/features/player/components/right-controls.tsx
index baa285c2d..166c0679f 100644
--- a/src/renderer/features/player/components/right-controls.tsx
+++ b/src/renderer/features/player/components/right-controls.tsx
@@ -24,7 +24,6 @@ import {
useAutoDJSettings,
useCurrentServer,
useFullScreenPlayerStore,
- useGeneralSettings,
useHotkeySettings,
usePlaybackSettings,
usePlaybackType,
@@ -34,6 +33,8 @@ import {
usePlayerVolume,
useSetFullScreenPlayerStore,
useSettingsStoreActions,
+ useShowFavorites,
+ useShowRatings,
useSidebarRightExpanded,
useSideQueueType,
useVolumeMax,
@@ -86,7 +87,8 @@ const calculateVolumeDown = (volume: number, volumeWheelStep: number) => {
};
export const RightControls = () => {
- const { showRatings } = useGeneralSettings();
+ const showRatings = useShowRatings();
+ const showFavorites = useShowFavorites();
return (
@@ -97,7 +99,7 @@ export const RightControls = () => {
-
+ {showFavorites && }
diff --git a/src/renderer/features/settings/components/general/application-settings.tsx b/src/renderer/features/settings/components/general/application-settings.tsx
index 1a6ecffdb..2404aa915 100644
--- a/src/renderer/features/settings/components/general/application-settings.tsx
+++ b/src/renderer/features/settings/components/general/application-settings.tsx
@@ -562,6 +562,26 @@ export const ApplicationSettings = memo(() => {
isHidden: settings.sideQueueType !== 'sideQueue',
title: t('setting.sidePlayQueueLayout'),
},
+ {
+ control: (
+ {
+ setSettings({
+ general: {
+ ...settings,
+ showFavorites: e.currentTarget.checked,
+ },
+ });
+ }}
+ />
+ ),
+ description: t('setting.showFavorites', {
+ context: 'description',
+ }),
+ isHidden: false,
+ title: t('setting.showFavorites'),
+ },
{
control: (
;
export type ItemDetailListProps = z.infer;
+
export type ItemListSettings = {
detail?: ItemDetailListProps;
display: ListDisplayType;
@@ -1005,7 +1007,6 @@ export type PlayerFilter = z.infer;
export type PlayerFilterField = z.infer;
export type PlayerFilterOperator = z.infer;
-
export interface SettingsSlice extends z.infer {
actions: {
addCollection: (collection: SavedCollection) => void;
@@ -1031,6 +1032,7 @@ export interface SettingsSlice extends z.infer {
};
}
export interface SettingsState extends z.infer {}
+
export type SidebarItemType = z.infer;
export type SideQueueLayout = z.infer;
@@ -1314,6 +1316,7 @@ const initialState: SettingsState = {
primaryShade: 6,
qobuz: true,
resume: true,
+ showFavorites: true,
showLyricsInSidebar: true,
showQueueInSidebar: true,
showRatings: true,
@@ -2937,6 +2940,9 @@ export const usePlayerbarOpenDrawer = () =>
export const useShowRatings = () => useSettingsStore((state) => state.general.showRatings, shallow);
+export const useShowFavorites = () =>
+ useSettingsStore((state) => state.general.showFavorites, shallow);
+
export const useArtistRadioCount = () =>
useSettingsStore((state) => state.general.artistRadioCount, shallow);