mirror of
https://github.com/antebudimir/feishin.git
synced 2026-01-01 18:33:33 +00:00
[enhancement]: Support toggling Album/Track view for gneres (#591)
* [enhancement]: Support toggling Album/Track view for gneres The _primary_ purpose of this PR is to enable viewing tracks OR albums for genres. This has a few requirements: 1. Ability to set default route for genres, **except** when already on song/album page 2. Ability to toggle between album and genre view 3. Fixed refresh for genre ID Additionally, there was some refactoring: - Since the *-list-headers had very similar functions for search, export that as a hook instead * also use hook for album artist * support switching albumartist tracks/albums * remove toggle on song/album list, simplify logic
This commit is contained in:
parent
595eba152a
commit
ba531505af
20 changed files with 336 additions and 158 deletions
|
|
@ -13,9 +13,9 @@ import {
|
|||
} from '/@/renderer/components/virtual-grid';
|
||||
import { useListContext } from '/@/renderer/context/list-context';
|
||||
import { usePlayQueueAdd } from '/@/renderer/features/player';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { useCurrentServer, useListStoreActions, useListStoreByKey } from '/@/renderer/store';
|
||||
import { CardRow, ListDisplayType } from '/@/renderer/types';
|
||||
import { useGenreRoute } from '/@/renderer/hooks/use-genre-route';
|
||||
|
||||
export const GenreListGridView = ({ gridRef, itemCount }: any) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
|
@ -24,6 +24,7 @@ export const GenreListGridView = ({ gridRef, itemCount }: any) => {
|
|||
const { pageKey, id } = useListContext();
|
||||
const { grid, display, filter } = useListStoreByKey({ key: pageKey });
|
||||
const { setGrid } = useListStoreActions();
|
||||
const genrePath = useGenreRoute();
|
||||
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const scrollOffset = searchParams.get('scrollOffset');
|
||||
|
|
@ -137,7 +138,7 @@ export const GenreListGridView = ({ gridRef, itemCount }: any) => {
|
|||
loading={itemCount === undefined || itemCount === null}
|
||||
minimumBatchSize={40}
|
||||
route={{
|
||||
route: AppRoute.LIBRARY_GENRES_SONGS,
|
||||
route: genrePath,
|
||||
slugs: [{ idProperty: 'id', slugProperty: 'genreId' }],
|
||||
}}
|
||||
width={width}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,14 @@ import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/li
|
|||
import { Divider, Flex, Group, Stack } from '@mantine/core';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { RiFolder2Fill, RiMoreFill, RiRefreshLine, RiSettings3Fill } from 'react-icons/ri';
|
||||
import {
|
||||
RiAlbumLine,
|
||||
RiFolder2Fill,
|
||||
RiMoreFill,
|
||||
RiMusic2Line,
|
||||
RiRefreshLine,
|
||||
RiSettings3Fill,
|
||||
} from 'react-icons/ri';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { GenreListSort, LibraryItem, ServerType, SortOrder } from '/@/renderer/api/types';
|
||||
import { Button, DropdownMenu, MultiSelect, Slider, Switch, Text } from '/@/renderer/components';
|
||||
|
|
@ -15,9 +22,12 @@ import { useContainerQuery } from '/@/renderer/hooks';
|
|||
import { useListFilterRefresh } from '/@/renderer/hooks/use-list-filter-refresh';
|
||||
import {
|
||||
GenreListFilter,
|
||||
GenreTarget,
|
||||
useCurrentServer,
|
||||
useGeneralSettings,
|
||||
useListStoreActions,
|
||||
useListStoreByKey,
|
||||
useSettingsStoreActions,
|
||||
} from '/@/renderer/store';
|
||||
import { ListDisplayType, TableColumn } from '/@/renderer/types';
|
||||
import i18n from '/@/i18n/i18n';
|
||||
|
|
@ -52,6 +62,8 @@ export const GenreListHeaderFilters = ({ gridRef, tableRef }: GenreListHeaderFil
|
|||
const { setFilter, setTable, setGrid, setDisplayType } = useListStoreActions();
|
||||
const { display, filter, table, grid } = useListStoreByKey({ key: pageKey });
|
||||
const cq = useContainerQuery();
|
||||
const { genreTarget } = useGeneralSettings();
|
||||
const { setGenreBehavior } = useSettingsStoreActions();
|
||||
|
||||
const { handleRefreshTable, handleRefreshGrid } = useListFilterRefresh({
|
||||
itemType: LibraryItem.GENRE,
|
||||
|
|
@ -208,6 +220,11 @@ export const GenreListHeaderFilters = ({ gridRef, tableRef }: GenreListHeaderFil
|
|||
return filter.musicFolderId !== undefined;
|
||||
}, [filter.musicFolderId]);
|
||||
|
||||
const handleGenreToggle = useCallback(() => {
|
||||
const newState = genreTarget === GenreTarget.ALBUM ? GenreTarget.TRACK : GenreTarget.ALBUM;
|
||||
setGenreBehavior(newState);
|
||||
}, [genreTarget, setGenreBehavior]);
|
||||
|
||||
return (
|
||||
<Flex justify="space-between">
|
||||
<Group
|
||||
|
|
@ -309,6 +326,23 @@ export const GenreListHeaderFilters = ({ gridRef, tableRef }: GenreListHeaderFil
|
|||
{t('common.refresh', { postProcess: 'titleCase' })}
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Dropdown>
|
||||
<Divider orientation="vertical" />
|
||||
<Button
|
||||
compact
|
||||
size="md"
|
||||
tooltip={{
|
||||
label: t(
|
||||
genreTarget === GenreTarget.ALBUM
|
||||
? 'page.genreList.showAlbums'
|
||||
: 'page.genreList.showTracks',
|
||||
{ postProcess: 'sentenceCase' },
|
||||
),
|
||||
}}
|
||||
variant="subtle"
|
||||
onClick={handleGenreToggle}
|
||||
>
|
||||
{genreTarget === GenreTarget.ALBUM ? <RiAlbumLine /> : <RiMusic2Line />}
|
||||
</Button>
|
||||
</DropdownMenu>
|
||||
</Group>
|
||||
<Group
|
||||
|
|
|
|||
|
|
@ -5,19 +5,12 @@ import debounce from 'lodash/debounce';
|
|||
import { LibraryItem } from '/@/renderer/api/types';
|
||||
import { PageHeader, SearchInput } from '/@/renderer/components';
|
||||
import { VirtualInfiniteGridRef } from '/@/renderer/components/virtual-grid';
|
||||
import { useListContext } from '/@/renderer/context/list-context';
|
||||
import { GenreListHeaderFilters } from '/@/renderer/features/genres/components/genre-list-header-filters';
|
||||
import { FilterBar, LibraryHeaderBar } from '/@/renderer/features/shared';
|
||||
import { useContainerQuery } from '/@/renderer/hooks';
|
||||
import { useListFilterRefresh } from '/@/renderer/hooks/use-list-filter-refresh';
|
||||
import {
|
||||
GenreListFilter,
|
||||
useCurrentServer,
|
||||
useListStoreActions,
|
||||
useListStoreByKey,
|
||||
} from '/@/renderer/store';
|
||||
import { ListDisplayType } from '/@/renderer/types';
|
||||
import { GenreListFilter, useCurrentServer } from '/@/renderer/store';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDisplayRefresh } from '/@/renderer/hooks/use-display-refresh';
|
||||
|
||||
interface GenreListHeaderProps {
|
||||
gridRef: MutableRefObject<VirtualInfiniteGridRef | null>;
|
||||
|
|
@ -29,34 +22,18 @@ export const GenreListHeader = ({ itemCount, gridRef, tableRef }: GenreListHeade
|
|||
const { t } = useTranslation();
|
||||
const cq = useContainerQuery();
|
||||
const server = useCurrentServer();
|
||||
const { pageKey } = useListContext();
|
||||
const { display, filter } = useListStoreByKey({ key: pageKey });
|
||||
const { setFilter, setTablePagination } = useListStoreActions();
|
||||
|
||||
const { handleRefreshGrid, handleRefreshTable } = useListFilterRefresh({
|
||||
const { filter, refresh, search } = useDisplayRefresh({
|
||||
gridRef,
|
||||
itemType: LibraryItem.GENRE,
|
||||
server,
|
||||
tableRef,
|
||||
});
|
||||
|
||||
const handleSearch = debounce((e: ChangeEvent<HTMLInputElement>) => {
|
||||
const searchTerm = e.target.value === '' ? undefined : e.target.value;
|
||||
const updatedFilters = setFilter({
|
||||
data: { searchTerm },
|
||||
itemType: LibraryItem.GENRE,
|
||||
key: pageKey,
|
||||
}) as GenreListFilter;
|
||||
|
||||
const filterWithCustom = {
|
||||
...updatedFilters,
|
||||
};
|
||||
|
||||
if (display === ListDisplayType.TABLE || display === ListDisplayType.TABLE_PAGINATED) {
|
||||
handleRefreshTable(tableRef, filterWithCustom);
|
||||
setTablePagination({ data: { currentPage: 0 }, key: pageKey });
|
||||
} else {
|
||||
handleRefreshGrid(gridRef, filterWithCustom);
|
||||
}
|
||||
const updatedFilters = search(e) as GenreListFilter;
|
||||
refresh(updatedFilters);
|
||||
}, 500);
|
||||
|
||||
return (
|
||||
<Stack
|
||||
ref={cq.ref}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { useCurrentServer } from '/@/renderer/store';
|
|||
import { MutableRefObject, useCallback } from 'react';
|
||||
import { RowDoubleClickedEvent } from '@ag-grid-community/core';
|
||||
import { generatePath, useNavigate } from 'react-router';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { useGenreRoute } from '/@/renderer/hooks/use-genre-route';
|
||||
|
||||
interface GenreListTableViewProps {
|
||||
itemCount?: number;
|
||||
|
|
@ -20,6 +20,7 @@ export const GenreListTableView = ({ tableRef, itemCount }: GenreListTableViewPr
|
|||
const server = useCurrentServer();
|
||||
const { pageKey, customFilters } = useListContext();
|
||||
const navigate = useNavigate();
|
||||
const genrePath = useGenreRoute();
|
||||
|
||||
const tableProps = useVirtualTable({
|
||||
contextMenu: GENRE_CONTEXT_MENU_ITEMS,
|
||||
|
|
@ -36,9 +37,9 @@ export const GenreListTableView = ({ tableRef, itemCount }: GenreListTableViewPr
|
|||
const { data } = e;
|
||||
if (!data) return;
|
||||
|
||||
navigate(generatePath(AppRoute.LIBRARY_GENRES_SONGS, { genreId: data.id }));
|
||||
navigate(generatePath(genrePath, { genreId: data.id }));
|
||||
},
|
||||
[navigate],
|
||||
[genrePath, navigate],
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue