Signed-off-by: mbrucedogs <mbrucedogs@gmail.com>

This commit is contained in:
mbrucedogs 2025-07-20 20:39:33 -05:00
parent 551b180f54
commit d7250bde10
10 changed files with 190 additions and 351 deletions

View File

@ -26,6 +26,9 @@ export const UI_CONSTANTS = {
DEBOUNCE_DELAY: 300, DEBOUNCE_DELAY: 300,
MIN_SEARCH_LENGTH: 2, MIN_SEARCH_LENGTH: 2,
}, },
PAGINATION: {
ITEMS_PER_PAGE: 20,
},
QUEUE: { QUEUE: {
MAX_ITEMS: 100, MAX_ITEMS: 100,
}, },

View File

@ -12,5 +12,6 @@ export { useSingers } from './useSingers';
export { useSongLists } from './useSongLists'; export { useSongLists } from './useSongLists';
export { useDisabledSongs } from './useDisabledSongs'; export { useDisabledSongs } from './useDisabledSongs';
export { useActions } from './useActions'; export { useActions } from './useActions';
export { usePagination } from './usePagination';
export { useSongInfo } from './useSongInfo'; export { useSongInfo } from './useSongInfo';

View File

@ -1,20 +1,15 @@
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { useAppSelector, selectArtistsArray, selectSongsArray } from '../redux'; import { useAppSelector, selectArtistsArray, selectSongsArray } from '../redux';
import { debugLog } from '../utils/logger'; import { useActions } from './useActions';
import { useSongOperations } from './useSongOperations'; import { usePagination } from './usePagination';
import { useToast } from './useToast';
import type { Song } from '../types'; import type { Song } from '../types';
const ITEMS_PER_PAGE = 20;
export const useArtists = () => { export const useArtists = () => {
const allArtists = useAppSelector(selectArtistsArray); const allArtists = useAppSelector(selectArtistsArray);
const allSongs = useAppSelector(selectSongsArray); const allSongs = useAppSelector(selectSongsArray);
const { addToQueue, toggleFavorite } = useSongOperations(); const { handleAddToQueue, handleToggleFavorite } = useActions();
const { showSuccess, showError } = useToast();
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState('');
const [currentPage, setCurrentPage] = useState(1);
// Pre-compute songs by artist and song counts for performance // Pre-compute songs by artist and song counts for performance
const songsByArtist = useMemo(() => { const songsByArtist = useMemo(() => {
@ -44,31 +39,8 @@ export const useArtists = () => {
); );
}, [allArtists, searchTerm]); }, [allArtists, searchTerm]);
// Paginate the filtered artists - show all items up to current page // Use unified pagination hook
const artists = useMemo(() => { const pagination = usePagination(filteredArtists);
const endIndex = currentPage * ITEMS_PER_PAGE;
return filteredArtists.slice(0, endIndex);
}, [filteredArtists, currentPage]);
const hasMore = useMemo(() => {
// Show "hasMore" if there are more items than currently loaded
return filteredArtists.length > ITEMS_PER_PAGE && artists.length < filteredArtists.length;
}, [artists.length, filteredArtists.length]);
const loadMore = useCallback(() => {
debugLog('useArtists - loadMore called:', {
hasMore,
currentPage,
filteredArtistsLength: filteredArtists.length,
artistsLength: artists.length
});
if (hasMore) {
debugLog('useArtists - Incrementing page from', currentPage, 'to', currentPage + 1);
setCurrentPage(prev => prev + 1);
} else {
debugLog('useArtists - Not loading more because hasMore is false');
}
}, [hasMore, currentPage, filteredArtists.length, artists.length]);
// Get songs by artist (now using cached data) // Get songs by artist (now using cached data)
const getSongsByArtist = useCallback((artistName: string) => { const getSongsByArtist = useCallback((artistName: string) => {
@ -82,35 +54,17 @@ export const useArtists = () => {
const handleSearchChange = useCallback((value: string) => { const handleSearchChange = useCallback((value: string) => {
setSearchTerm(value); setSearchTerm(value);
setCurrentPage(1); // Reset to first page when searching pagination.resetPage(); // Reset to first page when searching
}, []); }, [pagination]);
const handleAddToQueue = useCallback(async (song: Song) => {
try {
await addToQueue(song);
showSuccess('Song added to queue');
} catch {
showError('Failed to add song to queue');
}
}, [addToQueue, showSuccess, showError]);
const handleToggleFavorite = useCallback(async (song: Song) => {
try {
await toggleFavorite(song);
showSuccess(song.favorite ? 'Removed from favorites' : 'Added to favorites');
} catch {
showError('Failed to update favorites');
}
}, [toggleFavorite, showSuccess, showError]);
return { return {
artists, artists: pagination.items,
allArtists: filteredArtists, allArtists: filteredArtists,
searchTerm, searchTerm,
hasMore, hasMore: pagination.hasMore,
loadMore, loadMore: pagination.loadMore,
currentPage, currentPage: pagination.currentPage,
totalPages: Math.ceil(filteredArtists.length / ITEMS_PER_PAGE), totalPages: pagination.totalPages,
handleSearchChange, handleSearchChange,
getSongsByArtist, getSongsByArtist,
getSongCountByArtist, getSongCountByArtist,

View File

@ -1,20 +1,17 @@
import { useCallback, useMemo, useState } from 'react'; import { useMemo } from 'react';
import { useAppSelector, selectFavoritesArray } from '../redux'; import { useAppSelector, selectFavoritesArray } from '../redux';
import { debugLog } from '../utils/logger'; import { debugLog } from '../utils/logger';
import { useActions } from './useActions'; import { useActions } from './useActions';
import { usePagination } from './usePagination';
import { useDisabledSongs } from './useDisabledSongs'; import { useDisabledSongs } from './useDisabledSongs';
const ITEMS_PER_PAGE = 20;
export const useFavorites = () => { export const useFavorites = () => {
const allFavoritesItems = useAppSelector(selectFavoritesArray); const allFavoritesItems = useAppSelector(selectFavoritesArray);
const { handleAddToQueue, handleToggleFavorite, handleToggleDisabled, isSongDisabled } = useActions(); const { handleAddToQueue, handleToggleFavorite, handleToggleDisabled, isSongDisabled } = useActions();
const { disabledSongPaths, loading: disabledSongsLoading } = useDisabledSongs(); const { disabledSongPaths, loading: disabledSongsLoading } = useDisabledSongs();
const [currentPage, setCurrentPage] = useState(1);
// Filter out disabled songs and paginate // Filter out disabled songs
const favoritesItems = useMemo(() => { const filteredItems = useMemo(() => {
// Don't return any results if disabled songs are still loading // Don't return any results if disabled songs are still loading
if (disabledSongsLoading) { if (disabledSongsLoading) {
debugLog('useFavorites - disabled songs still loading, returning empty array'); debugLog('useFavorites - disabled songs still loading, returning empty array');
@ -22,37 +19,23 @@ export const useFavorites = () => {
} }
// Filter out disabled songs first // Filter out disabled songs first
const filteredItems = allFavoritesItems.filter(song => !disabledSongPaths.has(song.path)); const filtered = allFavoritesItems.filter(song => !disabledSongPaths.has(song.path));
const endIndex = currentPage * ITEMS_PER_PAGE;
debugLog('useFavorites - filtering favorites:', { debugLog('useFavorites - filtering favorites:', {
totalFavorites: allFavoritesItems.length, totalFavorites: allFavoritesItems.length,
afterDisabledFilter: filteredItems.length, afterDisabledFilter: filtered.length,
currentPage,
endIndex
}); });
return filteredItems.slice(0, endIndex); return filtered;
}, [allFavoritesItems, currentPage, disabledSongPaths, disabledSongsLoading]); }, [allFavoritesItems, disabledSongPaths, disabledSongsLoading]);
const hasMore = useMemo(() => { // Use unified pagination hook
if (disabledSongsLoading) return false; const pagination = usePagination(filteredItems);
const filteredItems = allFavoritesItems.filter(song => !disabledSongPaths.has(song.path));
return filteredItems.length > ITEMS_PER_PAGE && favoritesItems.length < filteredItems.length;
}, [favoritesItems.length, allFavoritesItems, disabledSongPaths, disabledSongsLoading]);
const loadMore = useCallback(() => {
debugLog('useFavorites - loadMore called:', { hasMore, currentPage, allFavoritesItemsLength: allFavoritesItems.length });
if (hasMore) {
setCurrentPage(prev => prev + 1);
}
}, [hasMore, currentPage, allFavoritesItems.length]);
return { return {
favoritesItems, favoritesItems: pagination.items,
hasMore, hasMore: pagination.hasMore,
loadMore, loadMore: pagination.loadMore,
handleAddToQueue, handleAddToQueue,
handleToggleFavorite, handleToggleFavorite,
handleToggleDisabled, handleToggleDisabled,

View File

@ -1,20 +1,17 @@
import { useCallback, useMemo, useState } from 'react'; import { useMemo } from 'react';
import { useAppSelector, selectHistoryArray } from '../redux'; import { useAppSelector, selectHistoryArray } from '../redux';
import { debugLog } from '../utils/logger'; import { debugLog } from '../utils/logger';
import { useActions } from './useActions'; import { useActions } from './useActions';
import { usePagination } from './usePagination';
import { useDisabledSongs } from './useDisabledSongs'; import { useDisabledSongs } from './useDisabledSongs';
const ITEMS_PER_PAGE = 20;
export const useHistory = () => { export const useHistory = () => {
const allHistoryItems = useAppSelector(selectHistoryArray); const allHistoryItems = useAppSelector(selectHistoryArray);
const { handleAddToQueue, handleToggleFavorite, handleToggleDisabled, handleDeleteFromHistory, isSongDisabled } = useActions(); const { handleAddToQueue, handleToggleFavorite, handleToggleDisabled, handleDeleteFromHistory, isSongDisabled } = useActions();
const { disabledSongPaths, loading: disabledSongsLoading } = useDisabledSongs(); const { disabledSongPaths, loading: disabledSongsLoading } = useDisabledSongs();
const [currentPage, setCurrentPage] = useState(1);
// Filter out disabled songs and paginate // Filter out disabled songs
const historyItems = useMemo(() => { const filteredItems = useMemo(() => {
// Don't return any results if disabled songs are still loading // Don't return any results if disabled songs are still loading
if (disabledSongsLoading) { if (disabledSongsLoading) {
debugLog('useHistory - disabled songs still loading, returning empty array'); debugLog('useHistory - disabled songs still loading, returning empty array');
@ -22,37 +19,23 @@ export const useHistory = () => {
} }
// Filter out disabled songs first // Filter out disabled songs first
const filteredItems = allHistoryItems.filter(song => !disabledSongPaths.has(song.path)); const filtered = allHistoryItems.filter(song => !disabledSongPaths.has(song.path));
const endIndex = currentPage * ITEMS_PER_PAGE;
debugLog('useHistory - filtering history:', { debugLog('useHistory - filtering history:', {
totalHistory: allHistoryItems.length, totalHistory: allHistoryItems.length,
afterDisabledFilter: filteredItems.length, afterDisabledFilter: filtered.length,
currentPage,
endIndex
}); });
return filteredItems.slice(0, endIndex); return filtered;
}, [allHistoryItems, currentPage, disabledSongPaths, disabledSongsLoading]); }, [allHistoryItems, disabledSongPaths, disabledSongsLoading]);
const hasMore = useMemo(() => { // Use unified pagination hook
if (disabledSongsLoading) return false; const pagination = usePagination(filteredItems);
const filteredItems = allHistoryItems.filter(song => !disabledSongPaths.has(song.path));
return filteredItems.length > ITEMS_PER_PAGE && historyItems.length < filteredItems.length;
}, [historyItems.length, allHistoryItems, disabledSongPaths, disabledSongsLoading]);
const loadMore = useCallback(() => {
debugLog('useHistory - loadMore called:', { hasMore, currentPage, allHistoryItemsLength: allHistoryItems.length });
if (hasMore) {
setCurrentPage(prev => prev + 1);
}
}, [hasMore, currentPage, allHistoryItems.length]);
return { return {
historyItems, historyItems: pagination.items,
hasMore, hasMore: pagination.hasMore,
loadMore, loadMore: pagination.loadMore,
handleAddToQueue, handleAddToQueue,
handleToggleFavorite, handleToggleFavorite,
handleToggleDisabled, handleToggleDisabled,

View File

@ -1,23 +1,17 @@
import { useCallback, useMemo, useState } from 'react'; import { useMemo } from 'react';
import { useAppSelector, selectNewSongsArray } from '../redux'; import { useAppSelector, selectNewSongsArray } from '../redux';
import { debugLog } from '../utils/logger'; import { debugLog } from '../utils/logger';
import { useSongOperations } from './useSongOperations'; import { useActions } from './useActions';
import { useToast } from './useToast'; import { usePagination } from './usePagination';
import { useDisabledSongs } from './useDisabledSongs'; import { useDisabledSongs } from './useDisabledSongs';
import type { Song } from '../types';
const ITEMS_PER_PAGE = 20;
export const useNewSongs = () => { export const useNewSongs = () => {
const allNewSongsItems = useAppSelector(selectNewSongsArray); const allNewSongsItems = useAppSelector(selectNewSongsArray);
const { addToQueue, toggleFavorite } = useSongOperations(); const { handleAddToQueue, handleToggleFavorite, handleToggleDisabled, isSongDisabled } = useActions();
const { showSuccess, showError } = useToast(); const { disabledSongPaths, loading: disabledSongsLoading } = useDisabledSongs();
const { disabledSongPaths, isSongDisabled, addDisabledSong, removeDisabledSong, loading: disabledSongsLoading } = useDisabledSongs();
const [currentPage, setCurrentPage] = useState(1);
// Filter out disabled songs and paginate // Filter out disabled songs
const newSongsItems = useMemo(() => { const filteredItems = useMemo(() => {
// Don't return any results if disabled songs are still loading // Don't return any results if disabled songs are still loading
if (disabledSongsLoading) { if (disabledSongsLoading) {
debugLog('useNewSongs - disabled songs still loading, returning empty array'); debugLog('useNewSongs - disabled songs still loading, returning empty array');
@ -25,67 +19,23 @@ export const useNewSongs = () => {
} }
// Filter out disabled songs first // Filter out disabled songs first
const filteredItems = allNewSongsItems.filter(song => !disabledSongPaths.has(song.path)); const filtered = allNewSongsItems.filter(song => !disabledSongPaths.has(song.path));
const endIndex = currentPage * ITEMS_PER_PAGE;
debugLog('useNewSongs - filtering new songs:', { debugLog('useNewSongs - filtering new songs:', {
totalNewSongs: allNewSongsItems.length, totalNewSongs: allNewSongsItems.length,
afterDisabledFilter: filteredItems.length, afterDisabledFilter: filtered.length,
currentPage,
endIndex
}); });
return filteredItems.slice(0, endIndex); return filtered;
}, [allNewSongsItems, currentPage, disabledSongPaths, disabledSongsLoading]); }, [allNewSongsItems, disabledSongPaths, disabledSongsLoading]);
const hasMore = useMemo(() => { // Use unified pagination hook
if (disabledSongsLoading) return false; const pagination = usePagination(filteredItems);
const filteredItems = allNewSongsItems.filter(song => !disabledSongPaths.has(song.path));
return filteredItems.length > ITEMS_PER_PAGE && newSongsItems.length < filteredItems.length;
}, [newSongsItems.length, allNewSongsItems, disabledSongPaths, disabledSongsLoading]);
const loadMore = useCallback(() => {
debugLog('useNewSongs - loadMore called:', { hasMore, currentPage, allNewSongsItemsLength: allNewSongsItems.length });
if (hasMore) {
setCurrentPage(prev => prev + 1);
}
}, [hasMore, currentPage, allNewSongsItems.length]);
const handleAddToQueue = useCallback(async (song: Song) => {
try {
await addToQueue(song);
showSuccess('Song added to queue');
} catch {
showError('Failed to add song to queue');
}
}, [addToQueue, showSuccess, showError]);
const handleToggleFavorite = useCallback(async (song: Song) => {
try {
await toggleFavorite(song);
showSuccess(song.favorite ? 'Removed from favorites' : 'Added to favorites');
} catch {
showError('Failed to update favorites');
}
}, [toggleFavorite, showSuccess, showError]);
const handleToggleDisabled = useCallback(async (song: Song) => {
try {
if (isSongDisabled(song)) {
await removeDisabledSong(song);
} else {
await addDisabledSong(song);
}
} catch {
showError('Failed to update song disabled status');
}
}, [isSongDisabled, addDisabledSong, removeDisabledSong, showError]);
return { return {
newSongsItems, newSongsItems: pagination.items,
hasMore, hasMore: pagination.hasMore,
loadMore, loadMore: pagination.loadMore,
handleAddToQueue, handleAddToQueue,
handleToggleFavorite, handleToggleFavorite,
handleToggleDisabled, handleToggleDisabled,

View File

@ -0,0 +1,88 @@
import { useState, useCallback, useMemo } from 'react';
import { UI_CONSTANTS } from '../constants';
export interface PaginationConfig {
itemsPerPage?: number;
initialPage?: number;
}
export interface PaginationResult<T> {
// Current state
currentPage: number;
items: T[];
hasMore: boolean;
// Actions
loadMore: () => void;
resetPage: () => void;
setPage: (page: number) => void;
// Computed values
totalItems: number;
totalPages: number;
startIndex: number;
endIndex: number;
}
export const usePagination = <T>(
allItems: T[],
config: PaginationConfig = {}
): PaginationResult<T> => {
const {
itemsPerPage = UI_CONSTANTS.PAGINATION.ITEMS_PER_PAGE,
initialPage = 1
} = config;
const [currentPage, setCurrentPage] = useState(initialPage);
// Calculate pagination values
const totalItems = allItems.length;
const totalPages = Math.ceil(totalItems / itemsPerPage);
const startIndex = 0;
const endIndex = currentPage * itemsPerPage;
// Get paginated items
const items = useMemo(() => {
return allItems.slice(startIndex, endIndex);
}, [allItems, endIndex]);
// Check if there are more items to load
const hasMore = useMemo(() => {
return totalItems > itemsPerPage && items.length < totalItems;
}, [totalItems, itemsPerPage, items.length]);
// Load more items
const loadMore = useCallback(() => {
if (hasMore) {
setCurrentPage(prev => prev + 1);
}
}, [hasMore]);
// Reset to first page
const resetPage = useCallback(() => {
setCurrentPage(initialPage);
}, [initialPage]);
// Set specific page
const setPage = useCallback((page: number) => {
setCurrentPage(page);
}, []);
return {
// Current state
currentPage,
items,
hasMore,
// Actions
loadMore,
resetPage,
setPage,
// Computed values
totalItems,
totalPages,
startIndex,
endIndex,
};
};

View File

@ -1,16 +1,14 @@
import { useState, useCallback, useMemo } from 'react'; import { useState, useCallback, useMemo } from 'react';
import { useAppSelector, selectSongsArray } from '../redux'; import { useAppSelector, selectSongsArray } from '../redux';
import { useActions } from './useActions'; import { useActions } from './useActions';
import { usePagination } from './usePagination';
import { useDisabledSongs } from './useDisabledSongs'; import { useDisabledSongs } from './useDisabledSongs';
import { UI_CONSTANTS } from '../constants'; import { UI_CONSTANTS } from '../constants';
import { filterSongs } from '../utils/dataProcessing'; import { filterSongs } from '../utils/dataProcessing';
import { debugLog } from '../utils/logger'; import { debugLog } from '../utils/logger';
const ITEMS_PER_PAGE = 20;
export const useSearch = () => { export const useSearch = () => {
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState('');
const [currentPage, setCurrentPage] = useState(1);
const { handleAddToQueue, handleToggleFavorite, handleToggleDisabled, isSongDisabled } = useActions(); const { handleAddToQueue, handleToggleFavorite, handleToggleDisabled, isSongDisabled } = useActions();
const { disabledSongPaths, loading: disabledSongsLoading } = useDisabledSongs(); const { disabledSongPaths, loading: disabledSongsLoading } = useDisabledSongs();
@ -58,30 +56,22 @@ export const useSearch = () => {
return filtered; return filtered;
}, [allSongs, searchTerm, disabledSongPaths, disabledSongsLoading]); }, [allSongs, searchTerm, disabledSongPaths, disabledSongsLoading]);
// Paginate the filtered results - show all items up to current page // Use unified pagination hook
const searchResults = useMemo(() => { const pagination = usePagination(filteredSongs);
const endIndex = currentPage * ITEMS_PER_PAGE;
const paginatedSongs = filteredSongs.slice(0, endIndex);
return {
songs: paginatedSongs,
count: filteredSongs.length,
hasMore: endIndex < filteredSongs.length,
currentPage,
totalPages: Math.ceil(filteredSongs.length / ITEMS_PER_PAGE),
};
}, [filteredSongs, currentPage]);
const handleSearchChange = useCallback((value: string) => { const handleSearchChange = useCallback((value: string) => {
setSearchTerm(value); setSearchTerm(value);
setCurrentPage(1); // Reset to first page when searching pagination.resetPage(); // Reset to first page when searching
}, []); }, [pagination]);
const loadMore = useCallback(() => { // Create search results object for backward compatibility
if (searchResults.hasMore) { const searchResults = useMemo(() => ({
setCurrentPage(prev => prev + 1); songs: pagination.items,
} count: pagination.totalItems,
}, [searchResults.hasMore]); hasMore: pagination.hasMore,
currentPage: pagination.currentPage,
totalPages: pagination.totalPages,
}), [pagination]);
return { return {
searchTerm, searchTerm,
@ -90,7 +80,7 @@ export const useSearch = () => {
handleAddToQueue, handleAddToQueue,
handleToggleFavorite, handleToggleFavorite,
handleToggleDisabled, handleToggleDisabled,
loadMore, loadMore: pagination.loadMore,
isSongDisabled, isSongDisabled,
}; };
}; };

View File

@ -1,56 +1,16 @@
import { useCallback, useMemo, useState } from 'react'; import { useCallback } from 'react';
import { useAppSelector, selectSongListArray, selectSongsArray } from '../redux'; import { useAppSelector, selectSongListArray, selectSongsArray } from '../redux';
import { debugLog } from '../utils/logger'; import { useActions } from './useActions';
import { useSongOperations } from './useSongOperations'; import { usePagination } from './usePagination';
import { useToast } from './useToast'; import type { SongListSong } from '../types';
import type { SongListSong, Song } from '../types';
const ITEMS_PER_PAGE = 20;
export const useSongLists = () => { export const useSongLists = () => {
const allSongLists = useAppSelector(selectSongListArray); const allSongLists = useAppSelector(selectSongListArray);
const allSongs = useAppSelector(selectSongsArray); const allSongs = useAppSelector(selectSongsArray);
const { addToQueue, toggleFavorite } = useSongOperations(); const { handleAddToQueue, handleToggleFavorite } = useActions();
const { showSuccess, showError } = useToast();
const [currentPage, setCurrentPage] = useState(1);
// Paginate the song lists - show all items up to current page // Use unified pagination hook
const songLists = useMemo(() => { const pagination = usePagination(allSongLists);
const endIndex = currentPage * ITEMS_PER_PAGE;
return allSongLists.slice(0, endIndex);
}, [allSongLists, currentPage]);
const hasMore = useMemo(() => {
// Show "hasMore" if there are more items than currently loaded
const hasMoreItems = songLists.length < allSongLists.length;
debugLog('useSongLists - hasMore calculation:', {
songListsLength: songLists.length,
allSongListsLength: allSongLists.length,
hasMore: hasMoreItems,
currentPage
});
return hasMoreItems;
}, [songLists.length, allSongLists.length, currentPage]);
const loadMore = useCallback(() => {
const endIndex = currentPage * ITEMS_PER_PAGE;
const hasMoreItems = endIndex < allSongLists.length;
debugLog('useSongLists - loadMore called:', {
hasMoreItems,
currentPage,
allSongListsLength: allSongLists.length,
endIndex
});
if (hasMoreItems) {
debugLog('useSongLists - Incrementing page from', currentPage, 'to', currentPage + 1);
setCurrentPage(prev => prev + 1);
} else {
debugLog('useSongLists - Not loading more because hasMore is false');
}
}, [currentPage, allSongLists.length]);
// Check if a song exists in the catalog // Check if a song exists in the catalog
const checkSongAvailability = useCallback((songListSong: SongListSong) => { const checkSongAvailability = useCallback((songListSong: SongListSong) => {
@ -67,31 +27,13 @@ export const useSongLists = () => {
return matchingSongs; return matchingSongs;
}, [allSongs]); }, [allSongs]);
const handleAddToQueue = useCallback(async (song: Song) => {
try {
await addToQueue(song);
showSuccess('Song added to queue');
} catch {
showError('Failed to add song to queue');
}
}, [addToQueue, showSuccess, showError]);
const handleToggleFavorite = useCallback(async (song: Song) => {
try {
await toggleFavorite(song);
showSuccess(song.favorite ? 'Removed from favorites' : 'Added to favorites');
} catch {
showError('Failed to update favorites');
}
}, [toggleFavorite, showSuccess, showError]);
return { return {
songLists, songLists: pagination.items,
allSongLists, allSongLists,
hasMore, hasMore: pagination.hasMore,
loadMore, loadMore: pagination.loadMore,
currentPage, currentPage: pagination.currentPage,
totalPages: Math.ceil(allSongLists.length / ITEMS_PER_PAGE), totalPages: pagination.totalPages,
checkSongAvailability, checkSongAvailability,
handleAddToQueue, handleAddToQueue,
handleToggleFavorite, handleToggleFavorite,

View File

@ -1,97 +1,42 @@
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useState } from 'react';
import { useAppSelector, selectTopPlayedArray } from '../redux'; import { useAppSelector, selectTopPlayedArray } from '../redux';
import { debugLog } from '../utils/logger'; import { debugLog } from '../utils/logger';
import { useSongOperations } from './useSongOperations'; import { useActions } from './useActions';
import { useToast } from './useToast'; import { usePagination } from './usePagination';
import type { TopPlayed } from '../types';
const ITEMS_PER_PAGE = 20;
export const useTopPlayed = () => { export const useTopPlayed = () => {
const allTopPlayedItems = useAppSelector(selectTopPlayedArray); const allTopPlayedItems = useAppSelector(selectTopPlayedArray);
const { addToQueue, toggleFavorite } = useSongOperations(); const { handleAddToQueue, handleToggleFavorite } = useActions();
const { showSuccess, showError } = useToast();
const [currentPage, setCurrentPage] = useState(1);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
// Paginate the top played items - show all items up to current page // Use unified pagination hook
const topPlayedItems = useMemo(() => { const pagination = usePagination(allTopPlayedItems);
const endIndex = currentPage * ITEMS_PER_PAGE;
const result = allTopPlayedItems.slice(0, endIndex);
debugLog('useTopPlayed - pagination:', {
currentPage,
ITEMS_PER_PAGE,
endIndex,
allTopPlayedItemsLength: allTopPlayedItems.length,
resultLength: result.length
});
return result;
}, [allTopPlayedItems, currentPage]);
const hasMore = useMemo(() => {
// Show "hasMore" if there are more items than currently loaded
const result = topPlayedItems.length < allTopPlayedItems.length;
debugLog('useTopPlayed - hasMore calculation:', {
topPlayedItemsLength: topPlayedItems.length,
allTopPlayedItemsLength: allTopPlayedItems.length,
result
});
return result;
}, [topPlayedItems.length, allTopPlayedItems.length]);
const loadMore = useCallback(() => { const loadMore = useCallback(() => {
debugLog('useTopPlayed - loadMore called:', { hasMore, currentPage, allTopPlayedItemsLength: allTopPlayedItems.length }); debugLog('useTopPlayed - loadMore called:', {
if (hasMore && !isLoading) { hasMore: pagination.hasMore,
currentPage: pagination.currentPage,
allTopPlayedItemsLength: allTopPlayedItems.length
});
if (pagination.hasMore && !isLoading) {
setIsLoading(true); setIsLoading(true);
// Simulate a small delay to show loading state // Simulate a small delay to show loading state
setTimeout(() => { setTimeout(() => {
setCurrentPage(prev => prev + 1); pagination.loadMore();
setIsLoading(false); setIsLoading(false);
}, 100); }, 100);
} }
}, [hasMore, currentPage, allTopPlayedItems.length, isLoading]); }, [pagination, allTopPlayedItems.length, isLoading]);
const handleAddToQueue = useCallback(async (song: TopPlayed) => {
try {
// Convert TopPlayed to Song format for queue
const songForQueue = {
...song,
path: '', // TopPlayed doesn't have path
disabled: false,
favorite: false,
};
await addToQueue(songForQueue);
showSuccess('Song added to queue');
} catch {
showError('Failed to add song to queue');
}
}, [addToQueue, showSuccess, showError]);
const handleToggleFavorite = useCallback(async (song: TopPlayed) => {
try {
// Convert TopPlayed to Song format for favorites
const songForFavorites = {
...song,
path: '', // TopPlayed doesn't have path
disabled: false,
favorite: false,
};
await toggleFavorite(songForFavorites);
showSuccess(songForFavorites.favorite ? 'Removed from favorites' : 'Added to favorites');
} catch {
showError('Failed to update favorites');
}
}, [toggleFavorite, showSuccess, showError]);
return { return {
topPlayedItems, topPlayedItems: pagination.items,
allTopPlayedItems, allTopPlayedItems,
hasMore, hasMore: pagination.hasMore,
loadMore, loadMore,
isLoading, isLoading,
currentPage, currentPage: pagination.currentPage,
totalPages: Math.ceil(allTopPlayedItems.length / ITEMS_PER_PAGE), totalPages: pagination.totalPages,
handleAddToQueue, handleAddToQueue,
handleToggleFavorite, handleToggleFavorite,
}; };