mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-10 04:30:25 +02:00
improve album artist favorite performance and search (#1709)
* improve album artist favorite performance and search * adjust top songs / favorite songs sections --------- Co-authored-by: jeffvli <jeffvictorli@gmail.com>
This commit is contained in:
@@ -228,6 +228,39 @@ const AlbumArtistMetadataBiography = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const TABLE_ROW_HEIGHT = {
|
||||||
|
compact: 40,
|
||||||
|
default: 64,
|
||||||
|
large: 88,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const TABLE_HEADER_HEIGHT = 40;
|
||||||
|
|
||||||
|
interface SongTableListContainerProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
enableHeader?: boolean;
|
||||||
|
itemCount: number;
|
||||||
|
maxRows?: number;
|
||||||
|
tableSize?: 'compact' | 'default' | 'large';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTableRowHeight(size: 'compact' | 'default' | 'large' | undefined): number {
|
||||||
|
return size ? TABLE_ROW_HEIGHT[size] : TABLE_ROW_HEIGHT.default;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SongTableListContainer = ({
|
||||||
|
children,
|
||||||
|
enableHeader = true,
|
||||||
|
itemCount,
|
||||||
|
maxRows = 5,
|
||||||
|
tableSize = 'default',
|
||||||
|
}: SongTableListContainerProps) => {
|
||||||
|
const rowHeight = getTableRowHeight(tableSize);
|
||||||
|
const headerOffset = enableHeader ? TABLE_HEADER_HEIGHT : 0;
|
||||||
|
const height = headerOffset + rowHeight * Math.min(itemCount, maxRows);
|
||||||
|
return <div style={{ height }}>{children}</div>;
|
||||||
|
};
|
||||||
|
|
||||||
interface AlbumArtistMetadataTopSongsProps {
|
interface AlbumArtistMetadataTopSongsProps {
|
||||||
detailQuery: ReturnType<typeof useSuspenseQuery<AlbumArtistDetailResponse>>;
|
detailQuery: ReturnType<typeof useSuspenseQuery<AlbumArtistDetailResponse>>;
|
||||||
routeId: string;
|
routeId: string;
|
||||||
@@ -240,7 +273,6 @@ const AlbumArtistMetadataTopSongsContent = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [debouncedSearchTerm] = useDebouncedValue(searchTerm, 300);
|
const [debouncedSearchTerm] = useDebouncedValue(searchTerm, 300);
|
||||||
const [showAll, setShowAll] = useState(false);
|
|
||||||
const [topSongsQueryType, setTopSongsQueryType] = useLocalStorage<'community' | 'personal'>({
|
const [topSongsQueryType, setTopSongsQueryType] = useLocalStorage<'community' | 'personal'>({
|
||||||
defaultValue: 'community',
|
defaultValue: 'community',
|
||||||
key: 'album-artist-top-songs-query-type',
|
key: 'album-artist-top-songs-query-type',
|
||||||
@@ -272,13 +304,8 @@ const AlbumArtistMetadataTopSongsContent = ({
|
|||||||
}, [tableConfig?.columns]);
|
}, [tableConfig?.columns]);
|
||||||
|
|
||||||
const filteredSongs = useMemo(() => {
|
const filteredSongs = useMemo(() => {
|
||||||
const filtered = searchLibraryItems(songs, debouncedSearchTerm, LibraryItem.SONG);
|
return searchLibraryItems(songs, debouncedSearchTerm, LibraryItem.SONG);
|
||||||
// When searching, show all results. Otherwise, limit to 5 if not showing all
|
}, [songs, debouncedSearchTerm]);
|
||||||
if (debouncedSearchTerm?.trim() || showAll) {
|
|
||||||
return filtered;
|
|
||||||
}
|
|
||||||
return filtered.slice(0, 5);
|
|
||||||
}, [songs, debouncedSearchTerm, showAll]);
|
|
||||||
|
|
||||||
const { handleColumnReordered } = useItemListColumnReorder({
|
const { handleColumnReordered } = useItemListColumnReorder({
|
||||||
itemListKey: ItemListKey.SONG,
|
itemListKey: ItemListKey.SONG,
|
||||||
@@ -465,6 +492,12 @@ const AlbumArtistMetadataTopSongsContent = ({
|
|||||||
tableColumnsData={SONG_TABLE_COLUMNS}
|
tableColumnsData={SONG_TABLE_COLUMNS}
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
|
<SongTableListContainer
|
||||||
|
enableHeader={tableConfig.enableHeader}
|
||||||
|
itemCount={filteredSongs.length}
|
||||||
|
maxRows={5}
|
||||||
|
tableSize={tableConfig.size}
|
||||||
|
>
|
||||||
<ItemTableList
|
<ItemTableList
|
||||||
activeRowId={currentSongId}
|
activeRowId={currentSongId}
|
||||||
autoFitColumns={tableConfig.autoFitColumns}
|
autoFitColumns={tableConfig.autoFitColumns}
|
||||||
@@ -487,13 +520,7 @@ const AlbumArtistMetadataTopSongsContent = ({
|
|||||||
overrideControls={overrideControls}
|
overrideControls={overrideControls}
|
||||||
size={tableConfig.size}
|
size={tableConfig.size}
|
||||||
/>
|
/>
|
||||||
{!searchTerm.trim() && songs.length > 5 && !showAll && (
|
</SongTableListContainer>
|
||||||
<Group justify="center" w="100%">
|
|
||||||
<Button onClick={() => setShowAll(true)} variant="subtle">
|
|
||||||
{t('action.viewMore', { postProcess: 'sentenceCase' })}
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -529,7 +556,6 @@ const AlbumArtistMetadataFavoriteSongs = ({ routeId }: AlbumArtistMetadataFavori
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [debouncedSearchTerm] = useDebouncedValue(searchTerm, 300);
|
const [debouncedSearchTerm] = useDebouncedValue(searchTerm, 300);
|
||||||
const [showAll, setShowAll] = useState(false);
|
|
||||||
const tableConfig = useSettingsStore((state) => state.lists[ItemListKey.SONG]?.table);
|
const tableConfig = useSettingsStore((state) => state.lists[ItemListKey.SONG]?.table);
|
||||||
const currentSong = usePlayerSong();
|
const currentSong = usePlayerSong();
|
||||||
const player = usePlayer();
|
const player = usePlayer();
|
||||||
@@ -554,13 +580,8 @@ const AlbumArtistMetadataFavoriteSongs = ({ routeId }: AlbumArtistMetadataFavori
|
|||||||
}, [tableConfig?.columns]);
|
}, [tableConfig?.columns]);
|
||||||
|
|
||||||
const filteredSongs = useMemo(() => {
|
const filteredSongs = useMemo(() => {
|
||||||
const filtered = searchLibraryItems(songs, debouncedSearchTerm, LibraryItem.SONG);
|
return searchLibraryItems(songs, debouncedSearchTerm, LibraryItem.SONG);
|
||||||
// When searching, show all results. Otherwise, limit to 5 if not showing all
|
}, [songs, debouncedSearchTerm]);
|
||||||
if (debouncedSearchTerm?.trim() || showAll) {
|
|
||||||
return filtered;
|
|
||||||
}
|
|
||||||
return filtered.slice(0, 5);
|
|
||||||
}, [songs, debouncedSearchTerm, showAll]);
|
|
||||||
|
|
||||||
const { handleColumnReordered } = useItemListColumnReorder({
|
const { handleColumnReordered } = useItemListColumnReorder({
|
||||||
itemListKey: ItemListKey.SONG,
|
itemListKey: ItemListKey.SONG,
|
||||||
@@ -726,6 +747,12 @@ const AlbumArtistMetadataFavoriteSongs = ({ routeId }: AlbumArtistMetadataFavori
|
|||||||
tableColumnsData={SONG_TABLE_COLUMNS}
|
tableColumnsData={SONG_TABLE_COLUMNS}
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
|
<SongTableListContainer
|
||||||
|
enableHeader={tableConfig.enableHeader}
|
||||||
|
itemCount={filteredSongs.length}
|
||||||
|
maxRows={5}
|
||||||
|
tableSize={tableConfig.size}
|
||||||
|
>
|
||||||
<ItemTableList
|
<ItemTableList
|
||||||
activeRowId={currentSongId}
|
activeRowId={currentSongId}
|
||||||
autoFitColumns={tableConfig.autoFitColumns}
|
autoFitColumns={tableConfig.autoFitColumns}
|
||||||
@@ -748,13 +775,7 @@ const AlbumArtistMetadataFavoriteSongs = ({ routeId }: AlbumArtistMetadataFavori
|
|||||||
overrideControls={overrideControls}
|
overrideControls={overrideControls}
|
||||||
size={tableConfig.size}
|
size={tableConfig.size}
|
||||||
/>
|
/>
|
||||||
{!searchTerm.trim() && songs.length > 5 && !showAll && (
|
</SongTableListContainer>
|
||||||
<Group justify="center" w="100%">
|
|
||||||
<Button onClick={() => setShowAll(true)} variant="subtle">
|
|
||||||
{t('action.viewMore', { postProcess: 'sentenceCase' })}
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -128,23 +128,12 @@ export const createFuseForLibraryItem = <T extends FuseSearchableItem>(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const sampleItem = items[0];
|
const stringKeys: string[] = [];
|
||||||
|
|
||||||
const stringKeys = Object.keys(sampleItem).filter(
|
|
||||||
(key) =>
|
|
||||||
typeof sampleItem[key as keyof T] === 'string' &&
|
|
||||||
!key.startsWith('_') &&
|
|
||||||
key !== 'id' &&
|
|
||||||
key !== 'albumId' &&
|
|
||||||
key !== 'streamUrl' &&
|
|
||||||
key !== 'serverId' &&
|
|
||||||
key !== 'ownerId',
|
|
||||||
) as string[];
|
|
||||||
|
|
||||||
const nestedKeys: Array<{ getFn: (item: T) => string; name: string }> = [];
|
const nestedKeys: Array<{ getFn: (item: T) => string; name: string }> = [];
|
||||||
|
|
||||||
switch (itemType) {
|
switch (itemType) {
|
||||||
case LibraryItem.ALBUM: {
|
case LibraryItem.ALBUM: {
|
||||||
|
stringKeys.push('name', 'releaseType');
|
||||||
nestedKeys.push(
|
nestedKeys.push(
|
||||||
{
|
{
|
||||||
getFn: (item) => {
|
getFn: (item) => {
|
||||||
@@ -172,6 +161,7 @@ export const createFuseForLibraryItem = <T extends FuseSearchableItem>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
case LibraryItem.ALBUM_ARTIST: {
|
case LibraryItem.ALBUM_ARTIST: {
|
||||||
|
stringKeys.push('name');
|
||||||
nestedKeys.push({
|
nestedKeys.push({
|
||||||
getFn: (item) => {
|
getFn: (item) => {
|
||||||
const aa = item as AlbumArtist;
|
const aa = item as AlbumArtist;
|
||||||
@@ -185,9 +175,10 @@ export const createFuseForLibraryItem = <T extends FuseSearchableItem>(
|
|||||||
case LibraryItem.ARTIST:
|
case LibraryItem.ARTIST:
|
||||||
case LibraryItem.GENRE:
|
case LibraryItem.GENRE:
|
||||||
case LibraryItem.RADIO_STATION:
|
case LibraryItem.RADIO_STATION:
|
||||||
|
stringKeys.push('name');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LibraryItem.PLAYLIST: {
|
case LibraryItem.PLAYLIST: {
|
||||||
|
stringKeys.push('name');
|
||||||
nestedKeys.push({
|
nestedKeys.push({
|
||||||
getFn: (item) => {
|
getFn: (item) => {
|
||||||
const p = item as Playlist;
|
const p = item as Playlist;
|
||||||
@@ -200,7 +191,8 @@ export const createFuseForLibraryItem = <T extends FuseSearchableItem>(
|
|||||||
|
|
||||||
case LibraryItem.PLAYLIST_SONG:
|
case LibraryItem.PLAYLIST_SONG:
|
||||||
case LibraryItem.QUEUE_SONG:
|
case LibraryItem.QUEUE_SONG:
|
||||||
case LibraryItem.SONG: {
|
case LibraryItem.SONG:
|
||||||
|
stringKeys.push('album', 'name');
|
||||||
nestedKeys.push(
|
nestedKeys.push(
|
||||||
{
|
{
|
||||||
getFn: (item) => {
|
getFn: (item) => {
|
||||||
@@ -219,7 +211,6 @@ export const createFuseForLibraryItem = <T extends FuseSearchableItem>(
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return new Fuse(items, {
|
return new Fuse(items, {
|
||||||
fieldNormWeight,
|
fieldNormWeight,
|
||||||
|
|||||||
Reference in New Issue
Block a user