import { createSelector } from '@reduxjs/toolkit'; import type { RootState, QueueItem, Singer, Song } from '../types'; import { selectSongs, selectQueue, selectFavorites, selectHistory, selectTopPlayed, selectNewSongs, selectSongList, selectSingers, selectIsAdmin, selectCurrentSinger, selectPlayerState } from './index'; import { objectToArray, filterSongs, sortQueueByOrder, sortHistoryByDate, sortTopPlayedByCount, sortSongsByArtistAndTitle, limitArray } from '../utils/dataProcessing'; import { UI_CONSTANTS } from '../constants'; // Enhanced selectors with data processing export const selectSongsArray = createSelector( [selectSongs], (songs) => sortSongsByArtistAndTitle(objectToArray(songs)) ); // Selector that filters songs and excludes disabled ones export const selectSongsArrayWithoutDisabled = createSelector( [selectSongsArray, (_state: RootState, disabledSongPaths: Set) => disabledSongPaths], (songs, disabledSongPaths) => songs.filter(song => !disabledSongPaths.has(song.path)) ); export const selectFilteredSongs = createSelector( [selectSongsArray, (_state: RootState, searchTerm: string) => searchTerm], (songs, searchTerm) => filterSongs(songs, searchTerm) ); // Enhanced filtered songs that also excludes disabled songs export const selectFilteredSongsWithoutDisabled = createSelector( [selectSongsArray, (_state: RootState, searchTerm: string, disabledSongPaths: Set) => ({ searchTerm, disabledSongPaths })], (songs, { searchTerm, disabledSongPaths }) => filterSongs(songs, searchTerm, disabledSongPaths) ); export const selectQueueArray = createSelector( [selectQueue], (queue) => sortQueueByOrder(objectToArray(queue)) ); export const selectQueueStats = createSelector( [selectQueue], (queue) => { const queueArray = Object.values(queue) as QueueItem[]; const totalSongs = queueArray.length; const singers = [...new Set(queueArray.map(item => item.singer.name))]; const estimatedDuration = totalSongs * 3; // Rough estimate: 3 minutes per song return { totalSongs, singers, estimatedDuration, }; } ); export const selectHistoryArray = createSelector( [selectHistory], (history) => limitArray(sortHistoryByDate(objectToArray(history)), UI_CONSTANTS.HISTORY.MAX_ITEMS) ); // History array without disabled songs export const selectHistoryArrayWithoutDisabled = createSelector( [selectHistoryArray, (_state: RootState, disabledSongPaths: Set) => disabledSongPaths], (history, disabledSongPaths) => history.filter(song => !disabledSongPaths.has(song.path)) ); export const selectFavoritesArray = createSelector( [selectFavorites], (favorites) => sortSongsByArtistAndTitle(objectToArray(favorites)) ); // Favorites array without disabled songs export const selectFavoritesArrayWithoutDisabled = createSelector( [selectFavoritesArray, (_state: RootState, disabledSongPaths: Set) => disabledSongPaths], (favorites, disabledSongPaths) => favorites.filter(song => !disabledSongPaths.has(song.path)) ); export const selectNewSongsArray = createSelector( [selectNewSongs], (newSongs) => sortSongsByArtistAndTitle(objectToArray(newSongs)) ); // New songs array without disabled songs export const selectNewSongsArrayWithoutDisabled = createSelector( [selectNewSongsArray, (_state: RootState, disabledSongPaths: Set) => disabledSongPaths], (newSongs, disabledSongPaths) => newSongs.filter(song => !disabledSongPaths.has(song.path)) ); export const selectSingersArray = createSelector( [selectSingers], (singers) => (objectToArray(singers) as Singer[]).sort((a, b) => a.name.localeCompare(b.name)) ); export const selectSongListArray = createSelector( [selectSongList], (songList) => objectToArray(songList) ); export const selectArtistsArray = createSelector( [selectSongs], (songs) => { const artists = new Set(); (Object.values(songs) as Song[]).forEach((song) => { if (song.artist) { artists.add(song.artist); } }); return Array.from(artists).sort((a, b) => a.localeCompare(b)); } ); export const selectTopPlayedArray = createSelector( [selectTopPlayed], (topPlayed) => sortTopPlayedByCount(objectToArray(topPlayed)) ); // User-specific selectors export const selectUserQueueItems = createSelector( [selectQueueArray, selectCurrentSinger], (queueArray, currentSinger) => queueArray.filter((item: QueueItem) => item.singer.name === currentSinger) ); export const selectCanReorderQueue = createSelector( [selectIsAdmin], (isAdmin) => Boolean(isAdmin) ); // Search-specific selectors export const selectSearchResults = createSelector( [selectFilteredSongs], (filteredSongs) => ({ songs: filteredSongs, count: filteredSongs.length, }) ); // Enhanced search results that exclude disabled songs export const selectSearchResultsWithoutDisabled = createSelector( [selectFilteredSongsWithoutDisabled], (filteredSongs) => ({ songs: filteredSongs, count: filteredSongs.length, }) ); // Queue-specific selectors export const selectQueueWithUserInfo = createSelector( [selectQueueArray, selectCurrentSinger], (queueArray, currentSinger) => { // If no items, return empty array if (queueArray.length === 0) return []; // If no current singer, return items without isCurrentUser flag if (!currentSinger) { return queueArray.map(item => ({ ...item, isCurrentUser: false, })); } // Map items and add isCurrentUser flag return queueArray.map(item => ({ ...item, isCurrentUser: item.singer.name === currentSinger, })); } ); // Memoized selector for queue length to prevent unnecessary re-renders export const selectQueueLength = createSelector( [selectQueue], (queue) => Object.keys(queue).length ); // Memoized selector for queue object to prevent unnecessary re-renders export const selectQueueObject = createSelector( [selectQueue], (queue) => Object.keys(queue).length > 0 ? { ...queue } : {} ); // Memoized selector for player state to prevent unnecessary re-renders export const selectPlayerStateMemoized = createSelector( [selectPlayerState], (playerState) => playerState ? { ...playerState } : null );