From 287f1dc0e1a86e5a5e3caf3875084c1e44d1f2d3 Mon Sep 17 00:00:00 2001 From: jeffvli Date: Thu, 20 Jul 2023 00:37:05 -0700 Subject: [PATCH] Update search list implementation --- .../search/components/search-content.tsx | 134 +++++--------- .../search/components/search-header.tsx | 21 ++- .../features/search/routes/search-route.tsx | 163 +----------------- 3 files changed, 54 insertions(+), 264 deletions(-) diff --git a/src/renderer/features/search/components/search-content.tsx b/src/renderer/features/search/components/search-content.tsx index c7985b97..2ee955aa 100644 --- a/src/renderer/features/search/components/search-content.tsx +++ b/src/renderer/features/search/components/search-content.tsx @@ -1,86 +1,36 @@ -import { MutableRefObject, useMemo, useCallback } from 'react'; -import { - ColDef, - GridReadyEvent, - RowDoubleClickedEvent, - IDatasource, -} from '@ag-grid-community/core'; +import { RowDoubleClickedEvent } from '@ag-grid-community/core'; import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact'; -import { Stack } from '@mantine/core'; +import { MutableRefObject } from 'react'; import { generatePath, useNavigate } from 'react-router'; import { useParams, useSearchParams } from 'react-router-dom'; +import { AppRoute } from '../../../router/routes'; import { LibraryItem, QueueSong } from '/@/renderer/api/types'; import { VirtualGridAutoSizerContainer } from '/@/renderer/components/virtual-grid'; -import { VirtualTable, getColumnDefs } from '/@/renderer/components/virtual-table'; -import { useHandleTableContextMenu } from '/@/renderer/features/context-menu'; +import { VirtualTable } from '/@/renderer/components/virtual-table'; +import { useCurrentSongRowStyles } from '/@/renderer/components/virtual-table/hooks/use-current-song-row-styles'; +import { useVirtualTable } from '/@/renderer/components/virtual-table/hooks/use-virtual-table'; import { ALBUM_CONTEXT_MENU_ITEMS, ARTIST_CONTEXT_MENU_ITEMS, SONG_CONTEXT_MENU_ITEMS, } from '/@/renderer/features/context-menu/context-menu-items'; import { usePlayQueueAdd } from '/@/renderer/features/player'; -import { AppRoute } from '../../../router/routes'; -import { - useCurrentServer, - useSongListStore, - usePlayButtonBehavior, - useAlbumListStore, - useAlbumArtistListStore, -} from '/@/renderer/store'; +import { useCurrentServer, usePlayButtonBehavior } from '/@/renderer/store'; interface SearchContentProps { - getDatasource: (searchQuery: string, itemType: LibraryItem) => IDatasource | undefined; tableRef: MutableRefObject; } -export const SearchContent = ({ tableRef, getDatasource }: SearchContentProps) => { +export const SearchContent = ({ tableRef }: SearchContentProps) => { const navigate = useNavigate(); const server = useCurrentServer(); const { itemType } = useParams() as { itemType: LibraryItem }; const [searchParams] = useSearchParams(); - const songListStore = useSongListStore(); - const albumListStore = useAlbumListStore(); - const albumArtistListStore = useAlbumArtistListStore(); + const pageKey = itemType; + const handlePlayQueueAdd = usePlayQueueAdd(); const playButtonBehavior = usePlayButtonBehavior(); - const getTable = useCallback( - (itemType: string) => { - switch (itemType) { - case LibraryItem.SONG: - return songListStore.table; - case LibraryItem.ALBUM: - return albumListStore.table; - case LibraryItem.ALBUM_ARTIST: - return albumArtistListStore.table; - default: - return undefined; - } - }, - [albumArtistListStore.table, albumListStore.table, songListStore.table], - ); - - const table = getTable(itemType)!; - - const columnDefs: ColDef[] = useMemo(() => getColumnDefs(table.columns), [table.columns]); - - const onGridReady = useCallback( - (params: GridReadyEvent) => { - const datasource = getDatasource(searchParams.get('query') || '', itemType); - if (!datasource) return; - - params.api.setDatasource(datasource); - params.api.ensureIndexVisible(table.scrollOffset, 'top'); - }, - [getDatasource, itemType, searchParams, table.scrollOffset], - ); - - const handleGridSizeChange = () => { - if (table.autoFit) { - tableRef?.current?.api.sizeColumnsToFit(); - } - }; - const contextMenuItems = () => { switch (itemType) { case LibraryItem.ALBUM: @@ -94,8 +44,6 @@ export const SearchContent = ({ tableRef, getDatasource }: SearchContentProps) = } }; - const handleContextMenu = useHandleTableContextMenu(itemType, contextMenuItems()); - const handleRowDoubleClick = (e: RowDoubleClickedEvent) => { if (!e.data) return; switch (itemType) { @@ -118,39 +66,35 @@ export const SearchContent = ({ tableRef, getDatasource }: SearchContentProps) = } }; + const customFilters = { + searchTerm: searchParams.get('query') || '', + }; + + const { rowClassRules } = useCurrentSongRowStyles({ tableRef }); + + const tableProps = useVirtualTable({ + contextMenu: contextMenuItems(), + customFilters, + itemType, + pageKey, + server, + tableRef, + }); + return ( - - - data.data.id} - infiniteInitialRowCount={25} - rowBuffer={20} - rowHeight={table.rowHeight || 40} - rowModelType="infinite" - rowSelection="multiple" - onCellContextMenu={handleContextMenu} - onGridReady={onGridReady} - onGridSizeChanged={handleGridSizeChange} - onRowDoubleClicked={handleRowDoubleClick} - /> - - + + data.data.id} + infiniteInitialRowCount={25} + rowClassRules={rowClassRules} + onRowDoubleClicked={handleRowDoubleClick} + /> + ); }; diff --git a/src/renderer/features/search/components/search-header.tsx b/src/renderer/features/search/components/search-header.tsx index b8124d66..d86d775f 100644 --- a/src/renderer/features/search/components/search-header.tsx +++ b/src/renderer/features/search/components/search-header.tsx @@ -1,32 +1,36 @@ -import { ChangeEvent, MutableRefObject } from 'react'; -import { IDatasource } from '@ag-grid-community/core'; import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact'; -import { Stack, Flex, Group } from '@mantine/core'; +import { Flex, Group, Stack } from '@mantine/core'; import debounce from 'lodash/debounce'; +import { ChangeEvent, MutableRefObject } from 'react'; import { generatePath, Link, useParams, useSearchParams } from 'react-router-dom'; +import { useCurrentServer } from '../../../store/auth.store'; import { LibraryItem } from '/@/renderer/api/types'; import { Button, PageHeader, SearchInput } from '/@/renderer/components'; import { FilterBar, LibraryHeaderBar } from '/@/renderer/features/shared'; import { useContainerQuery } from '/@/renderer/hooks'; +import { useListFilterRefresh } from '/@/renderer/hooks/use-list-filter-refresh'; import { AppRoute } from '/@/renderer/router/routes'; interface SearchHeaderProps { - getDatasource: (searchQuery: string, itemType: LibraryItem) => IDatasource | undefined; navigationId: string; tableRef: MutableRefObject; } -export const SearchHeader = ({ tableRef, getDatasource, navigationId }: SearchHeaderProps) => { +export const SearchHeader = ({ tableRef, navigationId }: SearchHeaderProps) => { const { itemType } = useParams() as { itemType: LibraryItem }; const [searchParams, setSearchParams] = useSearchParams(); const cq = useContainerQuery(); + const server = useCurrentServer(); + + const { handleRefreshTable } = useListFilterRefresh({ + itemType, + server, + }); const handleSearch = debounce((e: ChangeEvent) => { if (!e.target.value) return; setSearchParams({ query: e.target.value }, { replace: true, state: { navigationId } }); - const datasource = getDatasource(e.target.value, itemType); - if (!datasource) return; - tableRef.current?.api.setDatasource(datasource); + handleRefreshTable(tableRef, { searchTerm: e.target.value }); }, 200); return ( @@ -44,7 +48,6 @@ export const SearchHeader = ({ tableRef, getDatasource, navigationId }: SearchHe { const { state: locationState } = useLocation(); @@ -17,166 +11,15 @@ const SearchRoute = () => { const navigationId = locationState?.navigationId || localNavigationId; const { itemType } = useParams() as { itemType: string }; const tableRef = useRef(null); - const server = useCurrentServer(); - const queryClient = useQueryClient(); - - const getDatasource = useCallback( - (searchQuery: string, itemType: LibraryItem) => { - let dataSource: IDatasource | undefined; - - switch (itemType) { - case LibraryItem.ALBUM: - dataSource = { - getRows: async (params) => { - const limit = params.endRow - params.startRow; - const startIndex = params.startRow; - - const query: SearchQuery = { - albumArtistLimit: 0, - albumArtistStartIndex: 0, - albumLimit: limit, - albumStartIndex: startIndex, - query: searchQuery || ' ', - songLimit: 0, - songStartIndex: 0, - }; - - const queryKey = queryKeys.search.list(server?.id || '', query); - - const res = await queryClient.fetchQuery( - queryKey, - async ({ signal }) => - api.controller.search({ - apiClientProps: { - server, - signal, - }, - query, - }), - { cacheTime: 1000 * 60 }, - ); - - if (!res) return; - - const items = res.albums || []; - const numOfItems = items.length; - - let lastRow = -1; - if (numOfItems < limit) { - lastRow = startIndex + numOfItems; - } - - params.successCallback(items, lastRow); - }, - }; - break; - case LibraryItem.ALBUM_ARTIST: - dataSource = { - getRows: async (params) => { - const limit = params.endRow - params.startRow; - const startIndex = params.startRow; - - const query: SearchQuery = { - albumArtistLimit: limit, - albumArtistStartIndex: startIndex, - albumLimit: 0, - albumStartIndex: 0, - query: searchQuery || ' ', - songLimit: 0, - songStartIndex: 0, - }; - - const queryKey = queryKeys.search.list(server?.id || '', query); - - const res = await queryClient.fetchQuery( - queryKey, - async ({ signal }) => - api.controller.search({ - apiClientProps: { - server, - signal, - }, - query, - }), - { cacheTime: 1000 * 60 }, - ); - - if (!res) return; - - const items = res.albumArtists || []; - const numOfItems = items.length; - - let lastRow = -1; - if (numOfItems < limit) { - lastRow = startIndex + numOfItems; - } - - params.successCallback(items, lastRow); - }, - }; - break; - case LibraryItem.SONG: - dataSource = { - getRows: async (params) => { - const limit = params.endRow - params.startRow; - const startIndex = params.startRow; - - const query: SearchQuery = { - albumArtistLimit: 0, - albumArtistStartIndex: 0, - albumLimit: 0, - albumStartIndex: 0, - query: searchQuery || ' ', - songLimit: limit, - songStartIndex: startIndex, - }; - - const queryKey = queryKeys.search.list(server?.id || '', query); - - const res = await queryClient.fetchQuery( - queryKey, - async ({ signal }) => - api.controller.search({ - apiClientProps: { - server, - signal, - }, - query, - }), - { cacheTime: 1000 * 60 }, - ); - - if (!res) return; - - const items = res.songs || []; - const numOfItems = items.length; - - let lastRow = -1; - if (numOfItems < limit) { - lastRow = startIndex + numOfItems; - } - - params.successCallback(items, lastRow); - }, - }; - break; - } - - return dataSource; - }, - [queryClient, server], - ); return (