diff --git a/packages/renderer/src/features/albums/components/album-list-header.tsx b/packages/renderer/src/features/albums/components/album-list-header.tsx new file mode 100644 index 000000000..03b40b605 --- /dev/null +++ b/packages/renderer/src/features/albums/components/album-list-header.tsx @@ -0,0 +1,266 @@ +import type { MouseEvent } from 'react'; +import { useCallback } from 'react'; +import { Group, Slider } from '@mantine/core'; +import throttle from 'lodash/throttle'; +import { RiArrowDownSLine } from 'react-icons/ri'; +import { JFAlbumListSort } from '/@/api/jellyfin.types'; +import { NDAlbumListSort } from '/@/api/navidrome.types'; +import type { AlbumListSort } from '/@/api/types'; +import { SortOrder } from '/@/api/types'; +import { Button, DropdownMenu } from '/@/components'; +import { useCurrentServer, useAppStoreActions, useAlbumRouteStore } from '/@/store'; +import { CardDisplayType } from '/@/types'; +import styled from 'styled-components'; + +const FILTERS = { + jellyfin: [ + { name: 'Album Artist', value: JFAlbumListSort.NAME }, + { name: 'Community Rating', value: JFAlbumListSort.RATING }, + { name: 'Critic Rating', value: JFAlbumListSort.CRITIC_RATING }, + { name: 'Name', value: JFAlbumListSort.NAME }, + { name: 'Random', value: JFAlbumListSort.RANDOM }, + { name: 'Recently Added', value: JFAlbumListSort.RECENTLY_ADDED }, + { name: 'Release Date', value: JFAlbumListSort.RELEASE_DATE }, + ], + navidrome: [ + { name: 'Album Artist', value: NDAlbumListSort.ALBUM_ARTIST }, + { name: 'Artist', value: NDAlbumListSort.ARTIST }, + { name: 'Duration', value: NDAlbumListSort.DURATION }, + { name: 'Name', value: NDAlbumListSort.NAME }, + { name: 'Play Count', value: NDAlbumListSort.PLAY_COUNT }, + { name: 'Random', value: NDAlbumListSort.RANDOM }, + { name: 'Rating', value: NDAlbumListSort.RATING }, + { name: 'Recently Added', value: NDAlbumListSort.RECENTLY_ADDED }, + { name: 'Song Count', value: NDAlbumListSort.SONG_COUNT }, + { name: 'Starred', value: NDAlbumListSort.STARRED }, + { name: 'Year', value: NDAlbumListSort.YEAR }, + ], +}; + +const ORDER = [ + { name: 'Ascending', value: SortOrder.ASC }, + { name: 'Descending', value: SortOrder.DESC }, +]; + +const AlbumListHeaderContainer = styled.div` + padding: 1rem 170px 1rem 1rem; + + button { + -webkit-app-region: no-drag; + } +`; + +export const AlbumListHeader = () => { + const server = useCurrentServer(); + const { setPage } = useAppStoreActions(); + const page = useAlbumRouteStore(); + const filters = page.list.filter; + const sortByLabel = server?.type + ? (FILTERS[server.type as keyof typeof FILTERS] as { name: string; value: string }[]).find( + (f) => f.value === filters.sortBy, + )?.name + : 'Unknown'; + + const sortOrderLabel = ORDER.find((s) => s.value === filters.sortOrder)?.name; + + const setSize = throttle( + (e: number) => + setPage('albums', { + ...page, + list: { ...page.list, size: e }, + }), + 200, + ); + + const handleSetFilter = useCallback( + (e: MouseEvent) => { + if (!e.currentTarget?.value) return; + setPage('albums', { + list: { + ...page.list, + filter: { + ...page.list.filter, + sortBy: e.currentTarget.value as AlbumListSort, + }, + }, + }); + }, + [page.list, setPage], + ); + + const handleSetOrder = useCallback( + (e: MouseEvent) => { + if (!e.currentTarget?.value) return; + setPage('albums', { + list: { + ...page.list, + filter: { + ...page.list.filter, + sortOrder: e.currentTarget.value as SortOrder, + }, + }, + }); + }, + [page.list, setPage], + ); + + const handleSetViewType = useCallback( + (e: MouseEvent) => { + if (!e.currentTarget?.value) return; + const type = e.currentTarget.value; + if (type === CardDisplayType.CARD) { + setPage('albums', { + ...page, + list: { + ...page.list, + display: CardDisplayType.CARD, + type: 'grid', + }, + }); + } else if (type === CardDisplayType.POSTER) { + setPage('albums', { + ...page, + list: { + ...page.list, + display: CardDisplayType.POSTER, + type: 'grid', + }, + }); + } else { + setPage('albums', { + ...page, + list: { + ...page.list, + type: 'list', + }, + }); + } + }, + [page, setPage], + ); + + return ( + + + + + + + + + + + + + Card + + + Poster + + + List + + + + + + + + + {FILTERS[server?.type as keyof typeof FILTERS].map((filter) => ( + + {filter.name} + + ))} + + + + + + + + {ORDER.map((sort) => ( + + {sort.name} + + ))} + + + + + + + {/* + {serverFolders?.map((folder) => ( + + {folder.name} + + ))} + */} + + + + ); +}; diff --git a/packages/renderer/src/features/albums/routes/album-list-route.tsx b/packages/renderer/src/features/albums/routes/album-list-route.tsx index 67bfda727..d79a5f3bc 100644 --- a/packages/renderer/src/features/albums/routes/album-list-route.tsx +++ b/packages/renderer/src/features/albums/routes/album-list-route.tsx @@ -1,36 +1,24 @@ /* eslint-disable no-plusplus */ -import type { MouseEvent } from 'react'; import { useCallback } from 'react'; -import { Group } from '@mantine/core'; import { useQueryClient } from '@tanstack/react-query'; -import { AnimatePresence } from 'framer-motion'; -import debounce from 'lodash/debounce'; -import throttle from 'lodash/throttle'; -import { RiArrowDownSLine, RiSettings2Fill } from 'react-icons/ri'; import AutoSizer from 'react-virtualized-auto-sizer'; import type { ListOnScrollProps } from 'react-window'; import { queryKeys } from '/@/api/query-keys'; -import type { Album, AlbumListResponse, AlbumListSort } from '/@/api/types'; -import { SortOrder } from '/@/api/types'; +import type { Album, AlbumListResponse } from '/@/api/types'; import { - Button, - DropdownMenu, - Slider, - Text, VirtualGridAutoSizerContainer, VirtualGridContainer, VirtualInfiniteGrid, } from '/@/components'; -import { AnimatedPage } from '/@/features/shared'; import { AppRoute } from '/@/router/routes'; import { useAlbumRouteStore, useAppStoreActions, useCurrentServer } from '/@/store'; import { LibraryItem, CardDisplayType } from '/@/types'; import { useAlbumList } from '../queries/album-list-query'; -import { JFAlbumListSort } from '/@/api/jellyfin.types'; import type { NDAlbum } from '/@/api/navidrome.types'; -import { NDAlbumListSort } from '/@/api/navidrome.types'; import { controller } from '/@/api/controller'; import { ndNormalize } from '/@/api/navidrome.api'; +import { AnimatedPage } from '/@/features/shared'; +import { AlbumListHeader } from '/@/features/albums/components/album-list-header'; const AlbumListRoute = () => { const queryClient = useQueryClient(); @@ -89,304 +77,23 @@ const AlbumListRoute = () => { [filters, queryClient, server], ); - const setSize = throttle( - (e: number) => + const handleGridScroll = useCallback( + (e: ListOnScrollProps) => { setPage('albums', { ...page, - list: { ...page.list, size: e }, - }), - 200, + list: { + ...page.list, + gridScrollOffset: e.scrollOffset, + }, + }); + }, + [page, setPage], ); - const handleSetFilter = (e: MouseEvent) => { - if (!e.currentTarget?.value) return; - setPage('albums', { - list: { - ...page.list, - filter: { - ...page.list.filter, - sortBy: e.currentTarget.value as AlbumListSort, - }, - }, - }); - }; - - const handleSetOrder = (e: MouseEvent) => { - if (!e.currentTarget?.value) return; - setPage('albums', { - list: { - ...page.list, - filter: { - ...page.list.filter, - sortOrder: e.currentTarget.value as SortOrder, - }, - }, - }); - }; - - const handleSetViewType = (e: MouseEvent) => { - if (!e.currentTarget?.value) return; - const type = e.currentTarget.value; - if (type === CardDisplayType.CARD) { - setPage('albums', { - ...page, - list: { - ...page.list, - display: CardDisplayType.CARD, - type: 'grid', - }, - }); - } else if (type === CardDisplayType.POSTER) { - setPage('albums', { - ...page, - list: { - ...page.list, - display: CardDisplayType.POSTER, - type: 'grid', - }, - }); - } else { - setPage('albums', { - ...page, - list: { - ...page.list, - type: 'list', - }, - }); - } - }; - - const handleGridScroll = debounce((e: ListOnScrollProps) => { - setPage('albums', { - ...page, - list: { - ...page.list, - gridScrollOffset: e.scrollOffset, - }, - }); - }, 300); - - const sortByLabel = server?.type - ? (FILTERS[server.type as keyof typeof FILTERS] as { name: string; value: string }[]).find( - (f) => f.value === filters.sortBy, - )?.name - : 'Unknown'; - - const sortOrderLabel = ORDER.find((s) => s.value === filters.sortOrder)?.name; - return ( - - - - Albums - - - - - - - {FILTERS[server?.type as keyof typeof FILTERS].map((filter) => ( - - {filter.name} - - ))} - - - - - - - - {ORDER.map((sort) => ( - - {sort.name} - - ))} - - - - - - - {/* - {serverFolders?.map((folder) => ( - - {folder.name} - - ))} - */} - - - - - - - - - - - - - - Card - - - Poster - - - List - - - - - - - {/* {isAdvFilter && ( - - - - - - Advanced Filters - - - - - - - - - - - - )} */} - + {({ height, width }) => (