mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
feat(genres): local index genre browse with Subsonic fallback (#937)
* feat(genres): genre detail browse via local index with aligned counts Move genre detail albums/play/shuffle onto the local library index with Albums-style in-page scroll, session restore, and genre-scoped stash. Unify genre album totals between the cloud and detail pages via library_get_genre_album_counts, and fix grouped browse totals to count distinct albums rather than matching tracks. * perf(genres): local genre browse with scoped counts cache Add dedicated Rust genre album pagination and indexes, slice-mode grid loading, library-filter-aware counts, and a long-lived in-memory catalog cache invalidated on sync so genre pages avoid repeated full-library SQL. * fix(genres): restore scroll after album back; play hold-to-shuffle Pin restore display count in refs so clearing the return stash no longer reloads the genre grid mid-restore. Load the first SQL page only (60 rows), use long-press on Play for shuffle, and add genre play tooltips. * fix(genres): fall back to Subsonic byGenre when local index unavailable Genre detail album grid now matches All Albums: try library_list_albums_by_genre first, then getAlbumsByGenre when the index is off, not ready, or errors. * docs: add CHANGELOG and credits for PR #937
This commit is contained in:
@@ -5,8 +5,14 @@ import {
|
||||
DEFAULT_ALBUM_BROWSE_SORT,
|
||||
albumBrowseSortForServer,
|
||||
albumBrowseSurfaceForPath,
|
||||
clearGenreDetailReturnStash,
|
||||
isAlbumDetailPath,
|
||||
isGenreDetailPath,
|
||||
genreDetailGenreFromPath,
|
||||
peekAlbumBrowseScrollRestore,
|
||||
peekGenreDetailReturnStash,
|
||||
peekGenreDetailScrollRestore,
|
||||
stashGenreDetailReturnFilters,
|
||||
useAlbumBrowseSessionStore,
|
||||
} from './albumBrowseSessionStore';
|
||||
|
||||
@@ -97,6 +103,22 @@ describe('albumBrowseSessionStore', () => {
|
||||
const { sortByServer } = useAlbumBrowseSessionStore.getState();
|
||||
expect(albumBrowseSortForServer(sortByServer, 'unknown')).toBe(DEFAULT_ALBUM_BROWSE_SORT);
|
||||
});
|
||||
|
||||
it('stashes genre detail leave snapshot separately from album grid surfaces', () => {
|
||||
stashGenreDetailReturnFilters('srv-a', 'Rock', {
|
||||
...DEFAULT_ALBUM_BROWSE_RETURN_FILTERS,
|
||||
selectedGenres: ['Rock'],
|
||||
scrollTop: 640,
|
||||
displayCount: 90,
|
||||
});
|
||||
expect(peekGenreDetailReturnStash('srv-a', 'Rock')?.scrollTop).toBe(640);
|
||||
expect(peekGenreDetailScrollRestore('srv-a', 'Rock')).toEqual({
|
||||
scrollTop: 640,
|
||||
displayCount: 90,
|
||||
});
|
||||
clearGenreDetailReturnStash('srv-a', 'Rock');
|
||||
expect(peekGenreDetailReturnStash('srv-a', 'Rock')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAlbumDetailPath', () => {
|
||||
@@ -109,6 +131,16 @@ describe('isAlbumDetailPath', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('isGenreDetailPath', () => {
|
||||
it('matches single genre detail routes only', () => {
|
||||
expect(isGenreDetailPath('/genres/Rock')).toBe(true);
|
||||
expect(isGenreDetailPath('/genres/Rock%20%26%20Roll')).toBe(true);
|
||||
expect(isGenreDetailPath('/genres')).toBe(false);
|
||||
expect(isGenreDetailPath('/genres/Rock/albums')).toBe(false);
|
||||
expect(genreDetailGenreFromPath('/genres/Rock%20%26%20Roll')).toBe('Rock & Roll');
|
||||
});
|
||||
});
|
||||
|
||||
describe('albumBrowseSurfaceForPath', () => {
|
||||
it('maps album grid browse routes', () => {
|
||||
expect(albumBrowseSurfaceForPath('/albums')).toBe('albums');
|
||||
|
||||
Reference in New Issue
Block a user