import { useState } from 'react'; import { usePathname } from 'expo-router'; import { useReturnToTabs } from '@/navigation/returnToTabs'; import { AppSheet, AppSheetItem, AppSheetSection, AppSheetTitle, type AppSheetItemProps, } from '@/components/sheets/AppSheet'; import { PlaylistPickerSheet } from '@/components/sheets/PlaylistPickerSheet'; import { enqueueEnd, enqueueTop } from '@/audio/playbackController'; import { dbTrackToTrack } from '@/library/trackAdapter'; import { resolveNavigationArtist } from '@/library/artistGrouping'; import { usePlaylistStore } from '@/stores/playlistStore'; import { useSettingsStore } from '@/stores/settingsStore'; import type { DbTrack } from '@/types/library'; export interface TrackActionSheetItem extends AppSheetItemProps { key: string; } interface TrackActionsSheetProps { /** null = hidden. */ track: DbTrack | null; onClose: () => void; /** Allows callers with their own menu to jump straight to playlist picking. */ initialStep?: 'menu' | 'pickPlaylist'; /** Screen-specific extras (e.g. playlist detail: remove / move). Handlers should close. */ extraItems?: TrackActionSheetItem[]; } /** Track menu: queue, playlist, navigation, favorites, and optional screen extras. */ export function TrackActionsSheet(props: TrackActionsSheetProps) { // Mount fresh per track so the step state resets. if (!props.track) return null; return ; } function TrackActionsSheetInner({ track, onClose, initialStep = 'menu', extraItems = [], }: TrackActionsSheetProps & { track: DbTrack }) { // This sheet also renders from `recently-played` and from inside the // now-playing overlay, i.e. while a root-stack sibling of `(tabs)` is focused, // where a bare push would mint a second copy of the whole tab tree. const returnToTabs = useReturnToTabs(); const pathname = usePathname(); const [step, setStep] = useState<'menu' | 'pickPlaylist'>(initialStep); const groupingMode = useSettingsStore((s) => s.artistGroupingMode); const isFavorite = usePlaylistStore((s) => s.favoritePaths.has(track.path)); const toggleFavorite = usePlaylistStore((s) => s.toggleFavorite); const artistName = resolveNavigationArtist(track, groupingMode); const albumHref = `/library/album/${encodeURIComponent(track.album_identity_key)}`; const artistHref = `/library/artist/${encodeURIComponent(artistName)}`; const closeAndRun = (run: () => void) => { onClose(); run(); }; const menuItems: TrackActionSheetItem[] = [ { key: 'play-next', label: 'Play next', icon: 'play-skip-forward', onPress: () => closeAndRun(() => void enqueueTop(dbTrackToTrack(track))), }, { key: 'add-to-queue', label: 'Add to queue', icon: 'list-outline', onPress: () => closeAndRun(() => void enqueueEnd(dbTrackToTrack(track))), }, { key: 'add-to-playlist', label: 'Add to playlist...', icon: 'add-circle-outline', onPress: () => setStep('pickPlaylist'), }, { key: 'view-album', label: 'View album', icon: 'albums-outline', onPress: () => closeAndRun(() => { // Already here (this sheet also opens from the album screen itself): // pushing would stack a duplicate of the current screen. if (pathname === albumHref) return; returnToTabs( { pathname: '/library/album/[key]', params: { key: track.album_identity_key } }, 'push' ); }), }, { key: 'view-artist', label: 'View artist', icon: 'person-outline', onPress: () => closeAndRun(() => { if (pathname === artistHref) return; returnToTabs( { pathname: '/library/artist/[name]', params: { name: artistName } }, 'push' ); }), }, { key: 'favorite', label: isFavorite ? 'Remove from favorites' : 'Add to favorites', icon: isFavorite ? 'heart-dislike-outline' : 'heart-outline', selected: isFavorite, onPress: () => closeAndRun(() => void toggleFavorite(track)), }, ]; if (step === 'pickPlaylist') { return ( setStep('menu') : undefined} /> ); } return ( {menuItems.map(({ key, ...item }) => ( ))} {extraItems.length > 0 ? ( <> {extraItems.map(({ key, ...item }) => ( ))} ) : null} ); }