mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-14 04:20:07 +02:00
Add filter functionality for infinite album list
This commit is contained in:
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user