add show album / album artist context menu items (#1105)

This commit is contained in:
jeffvli 2025-09-06 00:45:59 -07:00
parent 2cf0027419
commit 40fb5ba916
6 changed files with 61 additions and 8 deletions

View file

@ -364,6 +364,8 @@
"setRating": "$t(action.setRating)", "setRating": "$t(action.setRating)",
"playShuffled": "$t(player.shuffle)", "playShuffled": "$t(player.shuffle)",
"shareItem": "share item", "shareItem": "share item",
"goToAlbum": "go to $t(entity.album_one)",
"goToAlbumArtist": "go to $t(entity.albumArtist_one)",
"showDetails": "get info" "showDetails": "get info"
}, },
"fullscreenPlayer": { "fullscreenPlayer": {

View file

@ -16,7 +16,6 @@ import 'overlayscrollbars/overlayscrollbars.css';
import '/styles/overlayscrollbars.css'; import '/styles/overlayscrollbars.css';
import i18n from '/@/i18n/i18n'; import i18n from '/@/i18n/i18n';
import { ContextMenuProvider } from '/@/renderer/features/context-menu';
import { useDiscordRpc } from '/@/renderer/features/discord-rpc/use-discord-rpc'; import { useDiscordRpc } from '/@/renderer/features/discord-rpc/use-discord-rpc';
import { PlayQueueHandlerContext } from '/@/renderer/features/player'; import { PlayQueueHandlerContext } from '/@/renderer/features/player';
import { WebAudioContext } from '/@/renderer/features/player/context/webaudio-context'; import { WebAudioContext } from '/@/renderer/features/player/context/webaudio-context';
@ -193,11 +192,9 @@ export const App = () => {
<MantineProvider defaultColorScheme={mode as 'dark' | 'light'} theme={theme}> <MantineProvider defaultColorScheme={mode as 'dark' | 'light'} theme={theme}>
<Notifications containerWidth="300px" position="bottom-center" zIndex={50000} /> <Notifications containerWidth="300px" position="bottom-center" zIndex={50000} />
<PlayQueueHandlerContext.Provider value={providerValue}> <PlayQueueHandlerContext.Provider value={providerValue}>
<ContextMenuProvider> <WebAudioContext.Provider value={webAudioProvider}>
<WebAudioContext.Provider value={webAudioProvider}> <AppRouter />
<AppRouter /> </WebAudioContext.Provider>
</WebAudioContext.Provider>{' '}
</ContextMenuProvider>
</PlayQueueHandlerContext.Provider> </PlayQueueHandlerContext.Provider>
<IsUpdatedDialog /> <IsUpdatedDialog />
</MantineProvider> </MantineProvider>

View file

@ -12,6 +12,8 @@ export const QUEUE_CONTEXT_MENU_ITEMS: SetContextMenuItems = [
{ disabled: false, divider: true, id: 'deselectAll' }, { disabled: false, divider: true, id: 'deselectAll' },
{ id: 'download' }, { id: 'download' },
{ divider: true, id: 'shareItem' }, { divider: true, id: 'shareItem' },
{ id: 'goToAlbum' },
{ id: 'goToAlbumArtist' },
{ divider: true, id: 'showDetails' }, { divider: true, id: 'showDetails' },
]; ];
@ -27,6 +29,8 @@ export const SONG_CONTEXT_MENU_ITEMS: SetContextMenuItems = [
{ children: true, disabled: false, divider: true, id: 'setRating' }, { children: true, disabled: false, divider: true, id: 'setRating' },
{ id: 'download' }, { id: 'download' },
{ divider: true, id: 'shareItem' }, { divider: true, id: 'shareItem' },
{ id: 'goToAlbum' },
{ id: 'goToAlbumArtist' },
{ divider: true, id: 'showDetails' }, { divider: true, id: 'showDetails' },
]; ];
@ -51,6 +55,8 @@ export const PLAYLIST_SONG_CONTEXT_MENU_ITEMS: SetContextMenuItems = [
{ children: true, disabled: false, id: 'setRating' }, { children: true, disabled: false, id: 'setRating' },
{ id: 'download' }, { id: 'download' },
{ divider: true, id: 'shareItem' }, { divider: true, id: 'shareItem' },
{ id: 'goToAlbum' },
{ id: 'goToAlbumArtist' },
{ divider: true, id: 'showDetails' }, { divider: true, id: 'showDetails' },
]; ];
@ -66,6 +72,8 @@ export const SMART_PLAYLIST_SONG_CONTEXT_MENU_ITEMS: SetContextMenuItems = [
{ children: true, disabled: false, id: 'setRating' }, { children: true, disabled: false, id: 'setRating' },
{ id: 'download' }, { id: 'download' },
{ divider: true, id: 'shareItem' }, { divider: true, id: 'shareItem' },
{ id: 'goToAlbum' },
{ id: 'goToAlbumArtist' },
{ divider: true, id: 'showDetails' }, { divider: true, id: 'showDetails' },
]; ];
@ -79,6 +87,7 @@ export const ALBUM_CONTEXT_MENU_ITEMS: SetContextMenuItems = [
{ id: 'removeFromFavorites' }, { id: 'removeFromFavorites' },
{ children: true, disabled: false, divider: true, id: 'setRating' }, { children: true, disabled: false, divider: true, id: 'setRating' },
{ divider: true, id: 'shareItem' }, { divider: true, id: 'shareItem' },
{ id: 'goToAlbumArtist' },
{ divider: true, id: 'showDetails' }, { divider: true, id: 'showDetails' },
]; ];

View file

@ -19,6 +19,7 @@ import {
useState, useState,
} from 'react'; } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { generatePath, useNavigate } from 'react-router-dom';
import { api } from '/@/renderer/api'; import { api } from '/@/renderer/api';
import { controller } from '/@/renderer/api/controller'; import { controller } from '/@/renderer/api/controller';
@ -34,6 +35,7 @@ import { updateSong } from '/@/renderer/features/player/update-remote-song';
import { useDeletePlaylist } from '/@/renderer/features/playlists'; import { useDeletePlaylist } from '/@/renderer/features/playlists';
import { useRemoveFromPlaylist } from '/@/renderer/features/playlists/mutations/remove-from-playlist-mutation'; import { useRemoveFromPlaylist } from '/@/renderer/features/playlists/mutations/remove-from-playlist-mutation';
import { useCreateFavorite, useDeleteFavorite, useSetRating } from '/@/renderer/features/shared'; import { useCreateFavorite, useDeleteFavorite, useSetRating } from '/@/renderer/features/shared';
import { AppRoute } from '/@/renderer/router/routes';
import { import {
getServerById, getServerById,
useAuthStore, useAuthStore,
@ -131,6 +133,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
}); });
const handlePlayQueueAdd = usePlayQueueAdd(); const handlePlayQueueAdd = usePlayQueueAdd();
const navigate = useNavigate();
const openContextMenu = useCallback( const openContextMenu = useCallback(
(args: OpenContextMenuProps) => { (args: OpenContextMenuProps) => {
@ -734,6 +737,24 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
} }
}, [ctx.data, server]); }, [ctx.data, server]);
const handleGoToAlbum = useCallback(() => {
const item = ctx.data[0];
if (item.albumId) {
navigate(generatePath(AppRoute.LIBRARY_ALBUMS_DETAIL, { albumId: item.albumId }));
}
}, [ctx.data, navigate]);
const handleGoToAlbumArtist = useCallback(() => {
const item = ctx.data[0];
if (item.albumArtists && item.albumArtists.length > 0) {
navigate(
generatePath(AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL, {
albumArtistId: item.albumArtists[0].id,
}),
);
}
}, [ctx.data, navigate]);
const contextMenuItems: Record<ContextMenuItemType, ContextMenuItem> = useMemo(() => { const contextMenuItems: Record<ContextMenuItemType, ContextMenuItem> = useMemo(() => {
return { return {
addToFavorites: { addToFavorites: {
@ -772,6 +793,23 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
leftIcon: <Icon icon="download" />, leftIcon: <Icon icon="download" />,
onClick: handleDownload, onClick: handleDownload,
}, },
goToAlbum: {
disabled: ctx.data?.length !== 1 || !ctx.data[0]?.albumId,
id: 'goToAlbum',
label: t('page.contextMenu.goToAlbum', { postProcess: 'sentenceCase' }),
leftIcon: <Icon icon="album" />,
onClick: handleGoToAlbum,
},
goToAlbumArtist: {
disabled:
ctx.data?.length !== 1 ||
!ctx.data[0]?.albumArtists ||
ctx.data[0]?.albumArtists?.length === 0,
id: 'goToAlbumArtist',
label: t('page.contextMenu.goToAlbumArtist', { postProcess: 'sentenceCase' }),
leftIcon: <Icon icon="artist" />,
onClick: handleGoToAlbumArtist,
},
moveToBottomOfQueue: { moveToBottomOfQueue: {
id: 'moveToBottomOfQueue', id: 'moveToBottomOfQueue',
label: t('page.contextMenu.moveToBottom', { postProcess: 'sentenceCase' }), label: t('page.contextMenu.moveToBottom', { postProcess: 'sentenceCase' }),
@ -888,6 +926,8 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
handleRemoveSelected, handleRemoveSelected,
server, server,
handleShareItem, handleShareItem,
handleGoToAlbum,
handleGoToAlbumArtist,
handleOpenItemDetails, handleOpenItemDetails,
handlePlay, handlePlay,
handleUpdateRating, handleUpdateRating,

View file

@ -15,6 +15,8 @@ export type ContextMenuItemType =
| 'deletePlaylist' | 'deletePlaylist'
| 'deselectAll' | 'deselectAll'
| 'download' | 'download'
| 'goToAlbum'
| 'goToAlbumArtist'
| 'moveToBottomOfQueue' | 'moveToBottomOfQueue'
| 'moveToNextOfQueue' | 'moveToNextOfQueue'
| 'moveToTopOfQueue' | 'moveToTopOfQueue'
@ -57,6 +59,8 @@ export const CONFIGURABLE_CONTEXT_MENU_ITEMS: ContextMenuItemType[] = [
'setRating', 'setRating',
'download', 'download',
'shareItem', 'shareItem',
'goToAlbum',
'goToAlbumArtist',
'showDetails', 'showDetails',
]; ];

View file

@ -6,6 +6,7 @@ import { useNavigate } from 'react-router';
import styles from './default-layout.module.css'; import styles from './default-layout.module.css';
import { ContextMenuProvider } from '/@/renderer/features/context-menu';
import { CommandPalette } from '/@/renderer/features/search/components/command-palette'; import { CommandPalette } from '/@/renderer/features/search/components/command-palette';
import { MainContent } from '/@/renderer/layouts/default-layout/main-content'; import { MainContent } from '/@/renderer/layouts/default-layout/main-content';
import { PlayerBar } from '/@/renderer/layouts/default-layout/player-bar'; import { PlayerBar } from '/@/renderer/layouts/default-layout/player-bar';
@ -73,7 +74,7 @@ export const DefaultLayout = ({ shell }: DefaultLayoutProps) => {
]); ]);
return ( return (
<> <ContextMenuProvider>
<div <div
className={clsx(styles.layout, { className={clsx(styles.layout, {
[styles.macos]: windowBarStyle === Platform.MACOS, [styles.macos]: windowBarStyle === Platform.MACOS,
@ -86,6 +87,6 @@ export const DefaultLayout = ({ shell }: DefaultLayoutProps) => {
<PlayerBar /> <PlayerBar />
</div> </div>
<CommandPalette modalProps={{ handlers, opened }} /> <CommandPalette modalProps={{ handlers, opened }} />
</> </ContextMenuProvider>
); );
}; };