Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
a243e1e034
commit
4b2d1dcf77
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { InfiniteScrollList, SongItem } from '../../components/common';
|
||||
import { useNewSongs } from '../../hooks';
|
||||
import { useAppSelector } from '../../redux';
|
||||
import { selectNewSongs } from '../../redux';
|
||||
import { selectNewSongsArray } from '../../redux';
|
||||
import { debugLog } from '../../utils/logger';
|
||||
import type { Song } from '../../types';
|
||||
|
||||
@ -15,8 +15,8 @@ const NewSongs: React.FC = () => {
|
||||
handleToggleFavorite,
|
||||
} = useNewSongs();
|
||||
|
||||
const newSongs = useAppSelector(selectNewSongs);
|
||||
const newSongsCount = Object.keys(newSongs).length;
|
||||
const newSongsArray = useAppSelector(selectNewSongsArray);
|
||||
const newSongsCount = newSongsArray.length;
|
||||
|
||||
// Debug logging
|
||||
debugLog('NewSongs component - new songs count:', newSongsCount);
|
||||
|
||||
@ -4,7 +4,7 @@ import { trash, reorderThreeOutline, reorderTwoOutline, playCircle } from 'ionic
|
||||
import { ActionButton } from '../../components/common';
|
||||
import { useQueue } from '../../hooks';
|
||||
import { useAppSelector } from '../../redux';
|
||||
import { selectQueue, selectPlayerState, selectIsAdmin, selectControllerName } from '../../redux';
|
||||
import { selectQueueLength, selectPlayerStateMemoized, selectIsAdmin, selectControllerName } from '../../redux';
|
||||
import { PlayerState } from '../../types';
|
||||
import { queueService } from '../../firebase/services';
|
||||
import { debugLog } from '../../utils/logger';
|
||||
@ -23,11 +23,10 @@ const Queue: React.FC = () => {
|
||||
handleRemoveFromQueue,
|
||||
} = useQueue();
|
||||
|
||||
const queue = useAppSelector(selectQueue);
|
||||
const playerState = useAppSelector(selectPlayerState);
|
||||
const queueCount = useAppSelector(selectQueueLength);
|
||||
const playerState = useAppSelector(selectPlayerStateMemoized);
|
||||
const isAdmin = useAppSelector(selectIsAdmin);
|
||||
const controllerName = useAppSelector(selectControllerName);
|
||||
const queueCount = Object.keys(queue).length;
|
||||
|
||||
// Debug logging
|
||||
debugLog('Queue component - queue count:', queueCount);
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useAppSelector } from '../redux';
|
||||
import { selectControllerName, selectCurrentSinger } from '../redux';
|
||||
import { selectControllerName, selectCurrentSinger, selectQueueObject } from '../redux';
|
||||
import { queueService, favoritesService } from '../firebase/services';
|
||||
import type { Song, QueueItem } from '../types';
|
||||
|
||||
export const useSongOperations = () => {
|
||||
const controllerName = useAppSelector(selectControllerName);
|
||||
const currentSinger = useAppSelector(selectCurrentSinger);
|
||||
const currentQueue = useAppSelector((state) => state.controller.data?.player?.queue || {});
|
||||
const currentQueue = useAppSelector(selectQueueObject);
|
||||
|
||||
const addToQueue = useCallback(async (song: Song) => {
|
||||
if (!controllerName || !currentSinger) {
|
||||
|
||||
@ -72,8 +72,8 @@ export const selectAuth = (state: { auth: AuthState }) => state.auth.data;
|
||||
export const selectAuthLoading = (state: { auth: AuthState }) => state.auth.loading;
|
||||
export const selectAuthError = (state: { auth: AuthState }) => state.auth.error;
|
||||
export const selectIsAuthenticated = (state: { auth: AuthState }) => state.auth.data?.authenticated || false;
|
||||
export const selectCurrentSinger = (state: { auth: AuthState }) => state.auth.data?.singer || '';
|
||||
export const selectIsAdmin = (state: { auth: AuthState }) => state.auth.data?.isAdmin || false;
|
||||
export const selectControllerName = (state: { auth: AuthState }) => state.auth.data?.controller || '';
|
||||
export const selectCurrentSinger = (state: { auth: AuthState }) => state.auth.data?.singer ?? '';
|
||||
export const selectIsAdmin = (state: { auth: AuthState }) => Boolean(state.auth.data?.isAdmin);
|
||||
export const selectControllerName = (state: { auth: AuthState }) => state.auth.data?.controller ?? '';
|
||||
|
||||
export default authSlice.reducer;
|
||||
@ -151,14 +151,17 @@ export const selectControllerLoading = (state: { controller: ControllerState })
|
||||
export const selectControllerError = (state: { controller: ControllerState }) => state.controller.error;
|
||||
export const selectLastUpdated = (state: { controller: ControllerState }) => state.controller.lastUpdated;
|
||||
|
||||
// Constants for empty objects to prevent new references
|
||||
const EMPTY_OBJECT = {};
|
||||
|
||||
// Selectors for specific data
|
||||
export const selectSongs = (state: { controller: ControllerState }) => state.controller.data?.songs || {};
|
||||
export const selectQueue = (state: { controller: ControllerState }) => state.controller.data?.player?.queue || {};
|
||||
export const selectFavorites = (state: { controller: ControllerState }) => state.controller.data?.favorites || {};
|
||||
export const selectHistory = (state: { controller: ControllerState }) => state.controller.data?.history || {};
|
||||
export const selectTopPlayed = (state: { controller: ControllerState }) => state.controller.data?.topPlayed || {};
|
||||
export const selectNewSongs = (state: { controller: ControllerState }) => state.controller.data?.newSongs || {};
|
||||
export const selectSongList = (state: { controller: ControllerState }) => state.controller.data?.songList || {};
|
||||
export const selectSongs = (state: { controller: ControllerState }) => state.controller.data?.songs ?? EMPTY_OBJECT;
|
||||
export const selectQueue = (state: { controller: ControllerState }) => state.controller.data?.player?.queue ?? EMPTY_OBJECT;
|
||||
export const selectFavorites = (state: { controller: ControllerState }) => state.controller.data?.favorites ?? EMPTY_OBJECT;
|
||||
export const selectHistory = (state: { controller: ControllerState }) => state.controller.data?.history ?? EMPTY_OBJECT;
|
||||
export const selectTopPlayed = (state: { controller: ControllerState }) => state.controller.data?.topPlayed ?? EMPTY_OBJECT;
|
||||
export const selectNewSongs = (state: { controller: ControllerState }) => state.controller.data?.newSongs ?? EMPTY_OBJECT;
|
||||
export const selectSongList = (state: { controller: ControllerState }) => state.controller.data?.songList ?? EMPTY_OBJECT;
|
||||
export const selectPlayerState = (state: { controller: ControllerState }) => {
|
||||
const playerState = state.controller.data?.player?.state;
|
||||
|
||||
@ -173,6 +176,6 @@ export const selectPlayerState = (state: { controller: ControllerState }) => {
|
||||
return playerState;
|
||||
};
|
||||
export const selectSettings = (state: { controller: ControllerState }) => state.controller.data?.player?.settings;
|
||||
export const selectSingers = (state: { controller: ControllerState }) => state.controller.data?.player?.singers || {};
|
||||
export const selectSingers = (state: { controller: ControllerState }) => state.controller.data?.player?.singers ?? EMPTY_OBJECT;
|
||||
|
||||
export default controllerSlice.reducer;
|
||||
@ -1,5 +1,5 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import type { RootState } from '../types';
|
||||
import type { RootState, QueueItem, Singer, Song } from '../types';
|
||||
import {
|
||||
selectSongs,
|
||||
selectQueue,
|
||||
@ -10,7 +10,8 @@ import {
|
||||
selectSongList,
|
||||
selectSingers,
|
||||
selectIsAdmin,
|
||||
selectCurrentSinger
|
||||
selectCurrentSinger,
|
||||
selectPlayerState
|
||||
} from './index';
|
||||
import {
|
||||
objectToArray,
|
||||
@ -19,8 +20,7 @@ import {
|
||||
sortHistoryByDate,
|
||||
sortTopPlayedByCount,
|
||||
sortSongsByArtistAndTitle,
|
||||
limitArray,
|
||||
getQueueStats
|
||||
limitArray
|
||||
} from '../utils/dataProcessing';
|
||||
import { UI_CONSTANTS } from '../constants';
|
||||
|
||||
@ -54,7 +54,18 @@ export const selectQueueArray = createSelector(
|
||||
|
||||
export const selectQueueStats = createSelector(
|
||||
[selectQueue],
|
||||
(queue) => getQueueStats(queue)
|
||||
(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(
|
||||
@ -92,7 +103,7 @@ export const selectNewSongsArrayWithoutDisabled = createSelector(
|
||||
|
||||
export const selectSingersArray = createSelector(
|
||||
[selectSingers],
|
||||
(singers) => objectToArray(singers).sort((a, b) => a.name.localeCompare(b.name))
|
||||
(singers) => (objectToArray(singers) as Singer[]).sort((a, b) => a.name.localeCompare(b.name))
|
||||
);
|
||||
|
||||
export const selectSongListArray = createSelector(
|
||||
@ -104,7 +115,7 @@ export const selectArtistsArray = createSelector(
|
||||
[selectSongs],
|
||||
(songs) => {
|
||||
const artists = new Set<string>();
|
||||
Object.values(songs).forEach(song => {
|
||||
(Object.values(songs) as Song[]).forEach((song) => {
|
||||
if (song.artist) {
|
||||
artists.add(song.artist);
|
||||
}
|
||||
@ -122,12 +133,12 @@ export const selectTopPlayedArray = createSelector(
|
||||
export const selectUserQueueItems = createSelector(
|
||||
[selectQueueArray, selectCurrentSinger],
|
||||
(queueArray, currentSinger) =>
|
||||
queueArray.filter(item => item.singer.name === currentSinger)
|
||||
queueArray.filter((item: QueueItem) => item.singer.name === currentSinger)
|
||||
);
|
||||
|
||||
export const selectCanReorderQueue = createSelector(
|
||||
[selectIsAdmin],
|
||||
(isAdmin) => isAdmin
|
||||
(isAdmin) => Boolean(isAdmin)
|
||||
);
|
||||
|
||||
// Search-specific selectors
|
||||
@ -151,15 +162,40 @@ export const selectSearchResultsWithoutDisabled = createSelector(
|
||||
// Queue-specific selectors
|
||||
export const selectQueueWithUserInfo = createSelector(
|
||||
[selectQueueArray, selectCurrentSinger],
|
||||
(queueArray, currentSinger) =>
|
||||
queueArray.map(item => ({
|
||||
(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
|
||||
);
|
||||
@ -5,6 +5,8 @@ import type { Song, QueueItem, TopPlayed } from '../types';
|
||||
export const objectToArray = <T extends { key?: string }>(
|
||||
obj: Record<string, T>
|
||||
): T[] => {
|
||||
if (Object.keys(obj).length === 0) return [];
|
||||
|
||||
return Object.entries(obj).map(([key, item]) => ({
|
||||
...item,
|
||||
key,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user