import { useTranslation } from 'react-i18next';
import { Play, ListPlus, Radio, Heart, ChevronRight, ChevronsRight, User, Disc3, ListMusic, Info, Sparkles, Star, Trash2, HeartCrack, Share2, Orbit as OrbitIcon } from 'lucide-react';
import { useNavigateToAlbum } from '../../hooks/useNavigateToAlbum';
import { useNavigateToArtist } from '../../hooks/useNavigateToArtist';
import { resolveAlbum, resolveMediaServerId, resolvePlaylist } from '../../utils/offline/offlineMediaResolve';
import { queueSongStar } from '../../store/pendingStarSync';
import { lastfmLoveTrack, lastfmUnloveTrack } from '../../api/lastfm';
import type { Track } from '../../store/playerStoreTypes';
import { useAuthStore } from '../../store/authStore';
import { usePlaylistStore } from '../../store/playlistStore';
import { songToTrack } from '../../utils/playback/songToTrack';
import { showToast } from '../../utils/ui/toast';
import { suggestOrbitTrack, hostEnqueueToOrbit, evaluateOrbitSuggestGate, OrbitSuggestBlockedError } from '../../utils/orbit';
import LastfmIcon from '../LastfmIcon';
import StarRating from '../StarRating';
import { AddToPlaylistSubmenu } from './AddToPlaylistSubmenu';
import type { ContextMenuItemsProps } from './contextMenuItemTypes';
export default function SongContextItems(props: ContextMenuItemsProps) {
const {
type, item, queueIndex, playlistId, playlistSongIndex, shareKindOverride,
playTrack, playNext, enqueue, removeTrack, queue, currentTrack, closeContextMenu,
starredOverrides, lastfmLovedCache, setLastfmLovedForSong,
openSongInfo, userRatingOverrides, setKeyboardRating, keyboardRating,
playlistSubmenuOpen, setPlaylistSubmenuOpen, cancelPlaylistSubmenuCloseTimer, onPlaylistSubmenuTriggerMouseLeave,
playlistSongIds, setPlaylistSongIds,
orbitRole, entityRatingSupport, audiomuseNavidromeEnabled,
applySongRating, applyAlbumRating, applyArtistRating,
handleAction, startRadio, startInstantMix, downloadAlbum, copyShareLink, isStarred,
offlinePolicy,
} = props;
const { t } = useTranslation();
const auth = useAuthStore();
const navigateToAlbum = useNavigateToAlbum();
const navigateToArtist = useNavigateToArtist();
return (
<>
{(type === 'song' || type === 'album-song') && (() => {
const song = item as Track;
return (
<>
handleAction(() => playTrack(song, [song]))}>
{t('contextMenu.playNow')}
handleAction(() => playNext([song]))}>
{t('contextMenu.playNext')}
handleAction(() => enqueue([song]))}>
{t('contextMenu.addToQueue')}
{orbitRole === 'guest' && (() => {
const muted = evaluateOrbitSuggestGate().reason === 'muted';
return (
handleAction(() => {
if (muted) { showToast(t('orbit.suggestBlockedMuted'), 3500, 'error'); return; }
suggestOrbitTrack(song.id)
.then(() => showToast(t('orbit.ctxSuggestedToast'), 2200, 'info'))
.catch(err => {
if (err instanceof OrbitSuggestBlockedError && err.reason === 'muted') {
showToast(t('orbit.suggestBlockedMuted'), 3500, 'error');
} else {
showToast(t('orbit.ctxSuggestFailed'), 3000, 'error');
}
});
})}
>
{t('orbit.ctxAddToSession')}
);
})()}
{orbitRole === 'host' && (
handleAction(() => {
hostEnqueueToOrbit(song.id)
.then(() => showToast(t('orbit.ctxAddedHostToast'), 2200, 'info'))
.catch(() => showToast(t('orbit.ctxAddHostFailed'), 3000, 'error'));
})}>
{t('orbit.ctxAddToSessionHost')}
)}
{offlinePolicy.canAddToPlaylist && (
{ cancelPlaylistSubmenuCloseTimer(); setPlaylistSongIds([song.id]); setPlaylistSubmenuOpen(true); }}
onMouseLeave={onPlaylistSubmenuTriggerMouseLeave}
>
{t('contextMenu.addToPlaylist')}
{playlistSubmenuOpen && playlistSongIds[0] === song.id && (
{ setPlaylistSubmenuOpen(false); closeContextMenu(); }} />
)}
)}
{type === 'album-song' && (
handleAction(async () => {
const serverId = resolveMediaServerId(song.serverId);
if (!serverId || !song.albumId) return;
const albumData = await resolveAlbum(serverId, song.albumId);
if (!albumData) return;
const tracks = albumData.songs.map(songToTrack);
enqueue(tracks);
})}>
{t('contextMenu.enqueueAlbum')}
)}
{song.albumId && (
handleAction(() => navigateToAlbum(song.albumId!))}>
{t('contextMenu.openAlbum')}
)}
{song.artistId && (
handleAction(() => navigateToArtist(song.artistId!))}>
{t('contextMenu.goToArtist')}
)}
handleAction(() => startRadio(song.artistId ?? song.artist, song.artist, song))}>
{t('contextMenu.startRadio')}
{audiomuseNavidromeEnabled && (
handleAction(() => startInstantMix(song))}>
{t('contextMenu.instantMix')}
)}
{offlinePolicy.canFavorite && (
handleAction(() => {
queueSongStar(song.id, !isStarred(song.id, song.starred), song.serverId);
})}>
{isStarred(song.id, song.starred) ? t('contextMenu.unfavorite') : t('contextMenu.favorite')}
)}
{auth.lastfmSessionKey && (() => {
const loveKey = `${song.title}::${song.artist}`;
const loved = lastfmLovedCache[loveKey] ?? false;
return (
handleAction(() => {
const newLoved = !loved;
setLastfmLovedForSong(song.title, song.artist, newLoved);
if (newLoved) lastfmLoveTrack(song, auth.lastfmSessionKey);
else lastfmUnloveTrack(song, auth.lastfmSessionKey);
})}>
{loved ? t('contextMenu.lfmUnlove') : t('contextMenu.lfmLove')}
);
})()}
{offlinePolicy.canRate && (
e.stopPropagation()}
>
{ setKeyboardRating({ kind: 'song', id: song.id, value: r }); applySongRating(song.id, r); }}
ariaLabel={t('albumDetail.ratingLabel')}
/>
)}
handleAction(() => copyShareLink('track', song.id))}>
{t('contextMenu.shareLink')}
handleAction(() => openSongInfo(song.id))}>
{t('contextMenu.songInfo')}
{offlinePolicy.canEditPlaylist && playlistId && playlistSongIndex !== undefined && (
handleAction(async () => {
const { updatePlaylist } = await import('../../api/subsonicPlaylists');
const { showToast } = await import('../../utils/ui/toast');
const touchPlaylist = usePlaylistStore.getState().touchPlaylist;
try {
const serverId = resolveMediaServerId();
if (!serverId) return;
const resolved = await resolvePlaylist(serverId, playlistId);
if (!resolved) return;
const { songs } = resolved;
const prevCount = songs.length;
const updatedIds = songs.filter((_, i) => i !== playlistSongIndex).map(s => s.id);
await updatePlaylist(playlistId, updatedIds, prevCount);
touchPlaylist(playlistId);
showToast(t('playlists.removeSuccess'), 3000, 'info');
} catch {
showToast(t('playlists.removeError'), 4000, 'error');
}
})}>
{t('contextMenu.removeFromPlaylist')}
)}
>
);
})()}
{type === 'favorite-song' && (() => {
const song = item as Track;
return (
<>
handleAction(() => playTrack(song, [song]))}>
{t('contextMenu.playNow')}
handleAction(() => playNext([song]))}>
{t('contextMenu.playNext')}
handleAction(() => enqueue([song]))}>
{t('contextMenu.addToQueue')}
{orbitRole === 'guest' && (() => {
const muted = evaluateOrbitSuggestGate().reason === 'muted';
return (
handleAction(() => {
if (muted) { showToast(t('orbit.suggestBlockedMuted'), 3500, 'error'); return; }
suggestOrbitTrack(song.id)
.then(() => showToast(t('orbit.ctxSuggestedToast'), 2200, 'info'))
.catch(err => {
if (err instanceof OrbitSuggestBlockedError && err.reason === 'muted') {
showToast(t('orbit.suggestBlockedMuted'), 3500, 'error');
} else {
showToast(t('orbit.ctxSuggestFailed'), 3000, 'error');
}
});
})}
>
{t('orbit.ctxAddToSession')}
);
})()}
{orbitRole === 'host' && (
handleAction(() => {
hostEnqueueToOrbit(song.id)
.then(() => showToast(t('orbit.ctxAddedHostToast'), 2200, 'info'))
.catch(() => showToast(t('orbit.ctxAddHostFailed'), 3000, 'error'));
})}>
{t('orbit.ctxAddToSessionHost')}
)}
{offlinePolicy.canAddToPlaylist && (
{ cancelPlaylistSubmenuCloseTimer(); setPlaylistSongIds([song.id]); setPlaylistSubmenuOpen(true); }}
onMouseLeave={onPlaylistSubmenuTriggerMouseLeave}
>
{t('contextMenu.addToPlaylist')}
{playlistSubmenuOpen && playlistSongIds[0] === song.id && (
{ setPlaylistSubmenuOpen(false); closeContextMenu(); }} />
)}
)}
{song.albumId && (
handleAction(() => navigateToAlbum(song.albumId!))}>
{t('contextMenu.openAlbum')}
)}
{song.artistId && (
handleAction(() => navigateToArtist(song.artistId!))}>
{t('contextMenu.goToArtist')}
)}
handleAction(() => startRadio(song.artistId ?? song.artist, song.artist, song))}>
{t('contextMenu.startRadio')}
{audiomuseNavidromeEnabled && (
handleAction(() => startInstantMix(song))}>
{t('contextMenu.instantMix')}
)}
{auth.lastfmSessionKey && (() => {
const loveKey = `${song.title}::${song.artist}`;
const loved = lastfmLovedCache[loveKey] ?? false;
return (
handleAction(() => {
const newLoved = !loved;
setLastfmLovedForSong(song.title, song.artist, newLoved);
if (newLoved) lastfmLoveTrack(song, auth.lastfmSessionKey);
else lastfmUnloveTrack(song, auth.lastfmSessionKey);
})}>
{loved ? t('contextMenu.lfmUnlove') : t('contextMenu.lfmLove')}
);
})()}
{offlinePolicy.canRate && (
e.stopPropagation()}
>
{ setKeyboardRating({ kind: 'song', id: song.id, value: r }); applySongRating(song.id, r); }}
ariaLabel={t('albumDetail.ratingLabel')}
/>
)}
handleAction(() => copyShareLink('track', song.id))}>
{t('contextMenu.shareLink')}
handleAction(() => openSongInfo(song.id))}>
{t('contextMenu.songInfo')}
{offlinePolicy.canFavorite && (
<>
handleAction(() => {
queueSongStar(song.id, false, song.serverId);
})}>
{t('contextMenu.unfavorite')}
>
)}
>
);
})()}
>
);
}