Signed-off-by: mbrucedogs <mbrucedogs@gmail.com>
This commit is contained in:
parent
551b180f54
commit
d7250bde10
@ -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,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -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';
|
||||||
@ -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,
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
88
src/hooks/usePagination.ts
Normal file
88
src/hooks/usePagination.ts
Normal 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,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -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,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -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,
|
||||||
|
|||||||
@ -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,
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user