201 lines
6.2 KiB
TypeScript
201 lines
6.2 KiB
TypeScript
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<string>) => 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<string>) => ({ 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<string>) => 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<string>) => 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<string>) => 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<string>();
|
|
(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
|
|
);
|