/* 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 { 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'; 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 }, ]; export const AlbumListRoute = () => { const queryClient = useQueryClient(); const server = useCurrentServer(); const { setPage } = useAppStoreActions(); const page = useAlbumRouteStore(); const filters = page.list.filter; const albumListQuery = useAlbumList({ limit: 1, sortBy: filters.sortBy, sortOrder: filters.sortOrder, startIndex: 0, }); const fetch = useCallback( async ({ skip, take }: { skip: number; take: number }) => { const queryKey = queryKeys.albums.list(server?.id || '', { limit: take, startIndex: skip, ...filters, }); const albums = await queryClient.fetchQuery(queryKey, async ({ signal }) => controller.getAlbumList({ query: { limit: take, sortBy: filters.sortBy, sortOrder: filters.sortOrder, startIndex: skip, }, server, signal, }), ); let items: Album[] = []; switch (server?.type) { case 'jellyfin': break; case 'navidrome': items = (albums?.items || []).map((a) => { return ndNormalize.album(a as NDAlbum, server); }); break; case 'subsonic': break; } return { items, startIndex: skip, totalRecordCount: albums?.totalRecordCount || 0, } as AlbumListResponse; }, [filters, queryClient, server], ); const setSize = throttle( (e: number) => setPage('albums', { ...page, list: { ...page.list, size: e }, }), 200, ); 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 }) => ( )} ); };