Add filter functionality for infinite album list

This commit is contained in:
jeffvli
2022-07-30 07:03:10 -07:00
parent fa9cf2efda
commit b9a171b096
6 changed files with 347 additions and 207 deletions
@@ -0,0 +1,57 @@
import { Dispatch } from 'react';
import { ActionIcon, Menu, MenuProps } from '@mantine/core';
import { LayoutGrid, LayoutList, Table } from 'tabler-icons-react';
export enum ViewType {
Detail = 'detail',
Grid = 'grid',
Table = 'table',
}
interface ViewTypeButtonProps {
handler: Dispatch<ViewType>;
menuProps: MenuProps;
type: ViewType;
}
export const ViewTypeButton = ({
type,
menuProps,
handler,
}: ViewTypeButtonProps) => {
return (
<Menu {...menuProps}>
<Menu.Target>
<ActionIcon variant="transparent">
{type === ViewType.Grid ? (
<LayoutGrid />
) : type === ViewType.Detail ? (
<LayoutList />
) : (
<Table />
)}
</ActionIcon>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item
icon={<LayoutGrid size={14} />}
onClick={() => handler(ViewType.Grid)}
>
Grid
</Menu.Item>
<Menu.Item
icon={<LayoutList size={14} />}
onClick={() => handler(ViewType.Detail)}
>
Detail
</Menu.Item>
<Menu.Item
icon={<Table size={14} />}
onClick={() => handler(ViewType.Table)}
>
Table
</Menu.Item>
</Menu.Dropdown>
</Menu>
);
};
@@ -1,20 +1,21 @@
import { useInfiniteQuery, useQuery } from 'react-query';
import { queryKeys } from 'renderer/api/queryKeys';
import { AlbumsResponse } from 'renderer/api/types';
import { albumsApi, AlbumsRequest } from '../../../api/albumsApi';
export const useAlbums = (params: AlbumsRequest) => {
return useQuery({
queryFn: () => albumsApi.getAlbums(params),
queryKey: queryKeys.albums(),
queryKey: queryKeys.albums(params),
});
};
export const useAlbumsInfinite = (params: AlbumsRequest) => {
return useInfiniteQuery({
getNextPageParam: (lastPage) => {
getNextPageParam: (lastPage: AlbumsResponse) => {
return !!lastPage.pagination.nextPage;
},
getPreviousPageParam: (firstPage) => {
getPreviousPageParam: (firstPage: AlbumsResponse) => {
return !!firstPage.pagination.prevPage;
},
queryFn: ({ pageParam }) =>
@@ -1,58 +1,155 @@
/* eslint-disable no-plusplus */
import { useRef } from 'react';
import InfiniteLoader from 'react-window-infinite-loader';
import { useState } from 'react';
import { Button, Group, Menu } from '@mantine/core';
import { useSetState } from '@mantine/hooks';
import AutoSizer from 'react-virtualized-auto-sizer';
import { CaretDown } from 'tabler-icons-react';
import i18n from 'i18n/i18n';
import { albumsApi } from 'renderer/api/albumsApi';
import { VirtualInfiniteGrid } from 'renderer/components/virtual-grid/VirtualInfiniteGrid';
import { AnimatedPage } from 'renderer/features/shared/components/AnimatedPage';
import { AppRoute } from 'renderer/router/utils/routes';
import { Item } from 'types';
import { ViewType, ViewTypeButton } from '../components/ViewTypeButton';
import { useAlbums } from '../queries/getAlbums';
export const LibraryAlbumsRoute = () => {
const infiniteLoaderRef = useRef<InfiniteLoader>(null);
export enum AlbumSort {
DATE_ADDED = 'date_added',
DATE_ADDED_REMOTE = 'date_added_remote',
DATE_PLAYED = 'date_played',
DATE_RELEASED = 'date_released',
RANDOM = 'random',
RATING = 'rating',
TITLE = 'title',
YEAR = 'year',
}
const params = {
const FILTERS = [
{ name: i18n.t('filters.dateAdded'), value: AlbumSort.DATE_ADDED },
{
name: i18n.t('filters.dateAddedRemote'),
value: AlbumSort.DATE_ADDED_REMOTE,
},
{ name: i18n.t('filters.datePlayed'), value: AlbumSort.DATE_PLAYED },
{ name: i18n.t('filters.dateReleased'), value: AlbumSort.DATE_RELEASED },
{ name: i18n.t('filters.random'), value: AlbumSort.RANDOM },
{ name: i18n.t('filters.rating'), value: AlbumSort.RATING },
{ name: i18n.t('filters.title'), value: AlbumSort.TITLE },
{ name: i18n.t('filters.year'), value: AlbumSort.YEAR },
];
export const LibraryAlbumsRoute = () => {
const [viewType, setViewType] = useState(ViewType.Grid);
const [filters, setFilters] = useSetState({
orderBy: 'asc',
sortBy: 'title',
};
sortBy: AlbumSort.TITLE,
});
const { data: albums } = useAlbums({
limit: 0,
page: 0,
...params,
...filters,
});
return (
<AnimatedPage>
{albums && (
<VirtualInfiniteGrid
ref={infiniteLoaderRef}
cardControls={{
endpoint: albumsApi.getAlbum,
idProperty: 'id',
type: Item.Album,
}}
cardRows={[
{
align: 'center',
prop: 'name',
route: {
prop: 'id',
route: AppRoute.LIBRARY_ALBUMS_DETAIL,
},
},
{
align: 'center',
prop: 'year',
},
]}
itemCount={albums.pagination.totalEntries}
itemGap={20}
itemSize={180}
query={albumsApi.getAlbums}
queryParams={params}
/>
)}
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Group mb={10} position="apart">
<Menu position="bottom-start">
<Menu.Target>
<Button variant="subtle">
{
FILTERS.find((filter) => filter.value === filters.sortBy)
?.name
}
</Button>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item
rightSection={<CaretDown size={12} />}
onClick={() => setFilters({ sortBy: AlbumSort.TITLE })}
>
Title
</Menu.Item>
<Menu.Item
rightSection={<CaretDown size={12} />}
onClick={() => setFilters({ sortBy: AlbumSort.YEAR })}
>
Year
</Menu.Item>
<Menu.Item
rightSection={<CaretDown size={12} />}
onClick={() => setFilters({ sortBy: AlbumSort.RATING })}
>
Rating
</Menu.Item>
<Menu.Item
rightSection={<CaretDown size={12} />}
onClick={() => setFilters({ sortBy: AlbumSort.DATE_RELEASED })}
>
Date Released
</Menu.Item>
<Menu.Item
rightSection={<CaretDown size={12} />}
onClick={() => setFilters({ sortBy: AlbumSort.DATE_ADDED })}
>
Date Added
</Menu.Item>
<Menu.Item
rightSection={<CaretDown size={12} />}
onClick={() =>
setFilters({ sortBy: AlbumSort.DATE_ADDED_REMOTE })
}
>
Date Added (Remote)
</Menu.Item>
</Menu.Dropdown>
</Menu>
<ViewTypeButton
handler={setViewType}
menuProps={{ position: 'bottom-end' }}
type={viewType}
/>
</Group>
<div style={{ flex: 1 }}>
{albums && (
<AutoSizer>
{({ height, width }) => (
<VirtualInfiniteGrid
cardControls={{
endpoint: albumsApi.getAlbum,
idProperty: 'id',
type: Item.Album,
}}
cardRows={[
{
align: 'center',
prop: 'name',
route: {
prop: 'id',
route: AppRoute.LIBRARY_ALBUMS_DETAIL,
},
},
{
align: 'center',
prop: 'year',
},
]}
height={height}
itemCount={albums.pagination.totalEntries}
itemGap={20}
itemSize={180}
minimumBatchSize={100}
query={albumsApi.getAlbums}
queryParams={filters}
width={width}
/>
)}
</AutoSizer>
)}
</div>
</div>
</AnimatedPage>
);
};