mirror of
https://github.com/jeffvli/feishin.git
synced 2024-11-20 14:37:06 +01:00
Update song list implementation
This commit is contained in:
parent
85964bfded
commit
6cd27c3e88
@ -1,26 +1,27 @@
|
||||
import { ChangeEvent, useMemo } from 'react';
|
||||
import { Divider, Group, Stack } from '@mantine/core';
|
||||
import { MultiSelect, NumberInput, Switch, Text } from '/@/renderer/components';
|
||||
import { SongListFilter, useListStoreActions, useSongListFilter } from '/@/renderer/store';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useGenreList } from '/@/renderer/features/genres';
|
||||
import { ChangeEvent, useMemo } from 'react';
|
||||
import { useListFilterByKey } from '../../../store/list.store';
|
||||
import { LibraryItem } from '/@/renderer/api/types';
|
||||
import { MultiSelect, NumberInput, Switch, Text } from '/@/renderer/components';
|
||||
import { useGenreList } from '/@/renderer/features/genres';
|
||||
import { SongListFilter, useListStoreActions } from '/@/renderer/store';
|
||||
|
||||
interface JellyfinSongFiltersProps {
|
||||
handleFilterChange: (filters: SongListFilter) => void;
|
||||
id?: string;
|
||||
customFilters?: Partial<SongListFilter>;
|
||||
onFilterChange: (filters: SongListFilter) => void;
|
||||
pageKey: string;
|
||||
serverId?: string;
|
||||
}
|
||||
|
||||
export const JellyfinSongFilters = ({
|
||||
id,
|
||||
customFilters,
|
||||
pageKey,
|
||||
handleFilterChange,
|
||||
onFilterChange,
|
||||
serverId,
|
||||
}: JellyfinSongFiltersProps) => {
|
||||
const { setFilter } = useListStoreActions();
|
||||
const filter = useSongListFilter({ id, key: pageKey });
|
||||
const { filter } = useListFilterByKey({ key: pageKey });
|
||||
|
||||
// TODO - eventually replace with /items/filters endpoint to fetch genres and tags specific to the selected library
|
||||
const genreListQuery = useGenreList({ query: null, serverId });
|
||||
@ -42,6 +43,7 @@ export const JellyfinSongFilters = ({
|
||||
label: 'Is favorited',
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const updatedFilters = setFilter({
|
||||
customFilters,
|
||||
data: {
|
||||
_custom: {
|
||||
...filter._custom,
|
||||
@ -55,7 +57,7 @@ export const JellyfinSongFilters = ({
|
||||
itemType: LibraryItem.SONG,
|
||||
key: pageKey,
|
||||
}) as SongListFilter;
|
||||
handleFilterChange(updatedFilters);
|
||||
onFilterChange(updatedFilters);
|
||||
},
|
||||
value: filter._custom?.jellyfin?.IsFavorite,
|
||||
},
|
||||
@ -64,6 +66,7 @@ export const JellyfinSongFilters = ({
|
||||
const handleMinYearFilter = debounce((e: number | string) => {
|
||||
if (typeof e === 'number' && (e < 1700 || e > 2300)) return;
|
||||
const updatedFilters = setFilter({
|
||||
customFilters,
|
||||
data: {
|
||||
_custom: {
|
||||
...filter._custom,
|
||||
@ -77,12 +80,13 @@ export const JellyfinSongFilters = ({
|
||||
itemType: LibraryItem.SONG,
|
||||
key: pageKey,
|
||||
}) as SongListFilter;
|
||||
handleFilterChange(updatedFilters);
|
||||
onFilterChange(updatedFilters);
|
||||
}, 500);
|
||||
|
||||
const handleMaxYearFilter = debounce((e: number | string) => {
|
||||
if (typeof e === 'number' && (e < 1700 || e > 2300)) return;
|
||||
const updatedFilters = setFilter({
|
||||
customFilters,
|
||||
data: {
|
||||
_custom: {
|
||||
...filter._custom,
|
||||
@ -96,12 +100,13 @@ export const JellyfinSongFilters = ({
|
||||
itemType: LibraryItem.SONG,
|
||||
key: pageKey,
|
||||
}) as SongListFilter;
|
||||
handleFilterChange(updatedFilters);
|
||||
onFilterChange(updatedFilters);
|
||||
}, 500);
|
||||
|
||||
const handleGenresFilter = debounce((e: string[] | undefined) => {
|
||||
const genreFilterString = e?.length ? e.join(',') : undefined;
|
||||
const updatedFilters = setFilter({
|
||||
customFilters,
|
||||
data: {
|
||||
_custom: {
|
||||
...filter._custom,
|
||||
@ -115,7 +120,7 @@ export const JellyfinSongFilters = ({
|
||||
itemType: LibraryItem.SONG,
|
||||
key: pageKey,
|
||||
}) as SongListFilter;
|
||||
handleFilterChange(updatedFilters);
|
||||
onFilterChange(updatedFilters);
|
||||
}, 250);
|
||||
|
||||
return (
|
||||
|
@ -1,26 +1,26 @@
|
||||
import { ChangeEvent, useMemo } from 'react';
|
||||
import { Divider, Group, Stack } from '@mantine/core';
|
||||
import { NumberInput, Select, Switch, Text } from '/@/renderer/components';
|
||||
import { SongListFilter, useListStoreActions, useSongListFilter } from '/@/renderer/store';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useGenreList } from '/@/renderer/features/genres';
|
||||
import { ChangeEvent, useMemo } from 'react';
|
||||
import { LibraryItem } from '/@/renderer/api/types';
|
||||
import { NumberInput, Select, Switch, Text } from '/@/renderer/components';
|
||||
import { useGenreList } from '/@/renderer/features/genres';
|
||||
import { SongListFilter, useListFilterByKey, useListStoreActions } from '/@/renderer/store';
|
||||
|
||||
interface NavidromeSongFiltersProps {
|
||||
handleFilterChange: (filters: SongListFilter) => void;
|
||||
id?: string;
|
||||
customFilters?: Partial<SongListFilter>;
|
||||
onFilterChange: (filters: SongListFilter) => void;
|
||||
pageKey: string;
|
||||
serverId?: string;
|
||||
}
|
||||
|
||||
export const NavidromeSongFilters = ({
|
||||
handleFilterChange,
|
||||
customFilters,
|
||||
onFilterChange,
|
||||
pageKey,
|
||||
id,
|
||||
serverId,
|
||||
}: NavidromeSongFiltersProps) => {
|
||||
const { setFilter } = useListStoreActions();
|
||||
const filter = useSongListFilter({ id, key: pageKey });
|
||||
const filter = useListFilterByKey({ key: pageKey });
|
||||
|
||||
const genreListQuery = useGenreList({ query: null, serverId });
|
||||
|
||||
@ -34,6 +34,7 @@ export const NavidromeSongFilters = ({
|
||||
|
||||
const handleGenresFilter = debounce((e: string | null) => {
|
||||
const updatedFilters = setFilter({
|
||||
customFilters,
|
||||
data: {
|
||||
_custom: {
|
||||
...filter._custom,
|
||||
@ -45,7 +46,8 @@ export const NavidromeSongFilters = ({
|
||||
itemType: LibraryItem.SONG,
|
||||
key: pageKey,
|
||||
}) as SongListFilter;
|
||||
handleFilterChange(updatedFilters);
|
||||
|
||||
onFilterChange(updatedFilters);
|
||||
}, 250);
|
||||
|
||||
const toggleFilters = [
|
||||
@ -53,6 +55,7 @@ export const NavidromeSongFilters = ({
|
||||
label: 'Is favorited',
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const updatedFilters = setFilter({
|
||||
customFilters,
|
||||
data: {
|
||||
_custom: {
|
||||
...filter._custom,
|
||||
@ -65,7 +68,7 @@ export const NavidromeSongFilters = ({
|
||||
key: pageKey,
|
||||
}) as SongListFilter;
|
||||
|
||||
handleFilterChange(updatedFilters);
|
||||
onFilterChange(updatedFilters);
|
||||
},
|
||||
value: filter._custom?.navidrome?.starred,
|
||||
},
|
||||
@ -73,6 +76,7 @@ export const NavidromeSongFilters = ({
|
||||
|
||||
const handleYearFilter = debounce((e: number | string) => {
|
||||
const updatedFilters = setFilter({
|
||||
customFilters,
|
||||
data: {
|
||||
_custom: {
|
||||
...filter._custom,
|
||||
@ -85,7 +89,7 @@ export const NavidromeSongFilters = ({
|
||||
key: pageKey,
|
||||
}) as SongListFilter;
|
||||
|
||||
handleFilterChange(updatedFilters);
|
||||
onFilterChange(updatedFilters);
|
||||
}, 500);
|
||||
|
||||
return (
|
||||
|
@ -1,8 +1,8 @@
|
||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||
import { lazy, MutableRefObject, Suspense } from 'react';
|
||||
import { Spinner } from '/@/renderer/components';
|
||||
import { useSongListContext } from '/@/renderer/features/songs/context/song-list-context';
|
||||
import { useSongListStore } from '/@/renderer/store';
|
||||
import { useListContext } from '/@/renderer/context/list-context';
|
||||
import { useListStoreByKey } from '/@/renderer/store';
|
||||
import { ListDisplayType } from '/@/renderer/types';
|
||||
|
||||
const SongListTableView = lazy(() =>
|
||||
@ -17,8 +17,8 @@ interface SongListContentProps {
|
||||
}
|
||||
|
||||
export const SongListContent = ({ itemCount, tableRef }: SongListContentProps) => {
|
||||
const { id, pageKey } = useSongListContext();
|
||||
const { display } = useSongListStore({ id, key: pageKey });
|
||||
const { pageKey } = useListContext();
|
||||
const { display } = useListStoreByKey({ key: pageKey });
|
||||
|
||||
const isGrid = display === ListDisplayType.CARD || display === ListDisplayType.POSTER;
|
||||
|
||||
|
@ -1,37 +1,31 @@
|
||||
import { useCallback, useMemo, ChangeEvent, MutableRefObject, MouseEvent } from 'react';
|
||||
import { IDatasource } from '@ag-grid-community/core';
|
||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||
import { Divider, Flex, Group, Stack } from '@mantine/core';
|
||||
import { openModal } from '@mantine/modals';
|
||||
import { ChangeEvent, MouseEvent, MutableRefObject, useCallback, useMemo } from 'react';
|
||||
import {
|
||||
RiFolder2Line,
|
||||
RiMoreFill,
|
||||
RiSettings3Fill,
|
||||
RiPlayFill,
|
||||
RiAddBoxFill,
|
||||
RiAddCircleFill,
|
||||
RiRefreshLine,
|
||||
RiFilterFill,
|
||||
RiFolder2Line,
|
||||
RiMoreFill,
|
||||
RiPlayFill,
|
||||
RiRefreshLine,
|
||||
RiSettings3Fill,
|
||||
} from 'react-icons/ri';
|
||||
import { api } from '/@/renderer/api';
|
||||
import { useListStoreByKey } from '../../../store/list.store';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { LibraryItem, SongListQuery, SongListSort, SortOrder } from '/@/renderer/api/types';
|
||||
import { DropdownMenu, Button, Slider, MultiSelect, Switch, Text } from '/@/renderer/components';
|
||||
import { LibraryItem, SongListSort, SortOrder } from '/@/renderer/api/types';
|
||||
import { Button, DropdownMenu, MultiSelect, Slider, Switch, Text } from '/@/renderer/components';
|
||||
import { SONG_TABLE_COLUMNS } from '/@/renderer/components/virtual-table';
|
||||
import { useListContext } from '/@/renderer/context/list-context';
|
||||
import { OrderToggleButton, useMusicFolders } from '/@/renderer/features/shared';
|
||||
import { JellyfinSongFilters } from '/@/renderer/features/songs/components/jellyfin-song-filters';
|
||||
import { NavidromeSongFilters } from '/@/renderer/features/songs/components/navidrome-song-filters';
|
||||
import { useContainerQuery } from '/@/renderer/hooks';
|
||||
import { useListFilterRefresh } from '/@/renderer/hooks/use-list-filter-refresh';
|
||||
import { queryClient } from '/@/renderer/lib/react-query';
|
||||
import {
|
||||
SongListFilter,
|
||||
useCurrentServer,
|
||||
useListStoreActions,
|
||||
useSongListFilter,
|
||||
useSongListStore,
|
||||
} from '/@/renderer/store';
|
||||
import { ListDisplayType, ServerType, Play, TableColumn } from '/@/renderer/types';
|
||||
import { useSongListContext } from '/@/renderer/features/songs/context/song-list-context';
|
||||
import { SONG_TABLE_COLUMNS } from '/@/renderer/components/virtual-table';
|
||||
import { SongListFilter, useCurrentServer, useListStoreActions } from '/@/renderer/store';
|
||||
import { ListDisplayType, Play, ServerType, TableColumn } from '/@/renderer/types';
|
||||
|
||||
const FILTERS = {
|
||||
jellyfin: [
|
||||
@ -83,10 +77,14 @@ interface SongListHeaderFiltersProps {
|
||||
|
||||
export const SongListHeaderFilters = ({ tableRef }: SongListHeaderFiltersProps) => {
|
||||
const server = useCurrentServer();
|
||||
const { id, pageKey, handlePlay } = useSongListContext();
|
||||
const { display, table } = useSongListStore({ id, key: pageKey });
|
||||
const { pageKey, handlePlay, customFilters } = useListContext();
|
||||
const { display, table, filter } = useListStoreByKey({ key: pageKey });
|
||||
const { setFilter, setTable, setTablePagination, setDisplayType } = useListStoreActions();
|
||||
const filter = useSongListFilter({ id, key: pageKey });
|
||||
|
||||
const { handleRefreshTable } = useListFilterRefresh({
|
||||
itemType: LibraryItem.SONG,
|
||||
server,
|
||||
});
|
||||
|
||||
const cq = useContainerQuery();
|
||||
|
||||
@ -99,48 +97,6 @@ export const SongListHeaderFilters = ({ tableRef }: SongListHeaderFiltersProps)
|
||||
).find((f) => f.value === filter.sortBy)?.name) ||
|
||||
'Unknown';
|
||||
|
||||
const handleFilterChange = useCallback(
|
||||
async (filters?: SongListFilter) => {
|
||||
const dataSource: IDatasource = {
|
||||
getRows: async (params) => {
|
||||
const limit = params.endRow - params.startRow;
|
||||
const startIndex = params.startRow;
|
||||
|
||||
const pageFilters = filters || filter;
|
||||
|
||||
const query: SongListQuery = {
|
||||
limit,
|
||||
startIndex,
|
||||
...pageFilters,
|
||||
};
|
||||
|
||||
const queryKey = queryKeys.songs.list(server?.id || '', query);
|
||||
|
||||
const songsRes = await queryClient.fetchQuery(
|
||||
queryKey,
|
||||
async ({ signal }) =>
|
||||
api.controller.getSongList({
|
||||
apiClientProps: {
|
||||
server,
|
||||
signal,
|
||||
},
|
||||
query,
|
||||
}),
|
||||
{ cacheTime: 1000 * 60 * 1 },
|
||||
);
|
||||
|
||||
params.successCallback(songsRes?.items || [], songsRes?.totalRecordCount || 0);
|
||||
},
|
||||
rowCount: undefined,
|
||||
};
|
||||
tableRef.current?.api.setDatasource(dataSource);
|
||||
tableRef.current?.api.purgeInfiniteCache();
|
||||
tableRef.current?.api.ensureIndexVisible(0, 'top');
|
||||
setTablePagination({ data: { currentPage: 0 }, key: pageKey });
|
||||
},
|
||||
[filter, pageKey, server, setTablePagination, tableRef],
|
||||
);
|
||||
|
||||
const handleSetSortBy = useCallback(
|
||||
(e: MouseEvent<HTMLButtonElement>) => {
|
||||
if (!e.currentTarget?.value || !server?.type) return;
|
||||
@ -150,6 +106,7 @@ export const SongListHeaderFilters = ({ tableRef }: SongListHeaderFiltersProps)
|
||||
)?.defaultOrder;
|
||||
|
||||
const updatedFilters = setFilter({
|
||||
customFilters,
|
||||
data: {
|
||||
sortBy: e.currentTarget.value as SongListSort,
|
||||
sortOrder: sortOrder || SortOrder.ASC,
|
||||
@ -158,9 +115,9 @@ export const SongListHeaderFilters = ({ tableRef }: SongListHeaderFiltersProps)
|
||||
key: pageKey,
|
||||
}) as SongListFilter;
|
||||
|
||||
handleFilterChange(updatedFilters);
|
||||
handleRefreshTable(tableRef, updatedFilters);
|
||||
},
|
||||
[handleFilterChange, pageKey, server?.type, setFilter],
|
||||
[customFilters, handleRefreshTable, pageKey, server?.type, setFilter, tableRef],
|
||||
);
|
||||
|
||||
const handleSetMusicFolder = useCallback(
|
||||
@ -170,32 +127,35 @@ export const SongListHeaderFilters = ({ tableRef }: SongListHeaderFiltersProps)
|
||||
let updatedFilters = null;
|
||||
if (e.currentTarget.value === String(filter.musicFolderId)) {
|
||||
updatedFilters = setFilter({
|
||||
customFilters,
|
||||
data: { musicFolderId: undefined },
|
||||
itemType: LibraryItem.SONG,
|
||||
key: pageKey,
|
||||
}) as SongListFilter;
|
||||
} else {
|
||||
updatedFilters = setFilter({
|
||||
customFilters,
|
||||
data: { musicFolderId: e.currentTarget.value },
|
||||
itemType: LibraryItem.SONG,
|
||||
key: pageKey,
|
||||
}) as SongListFilter;
|
||||
}
|
||||
|
||||
handleFilterChange(updatedFilters);
|
||||
handleRefreshTable(tableRef, updatedFilters);
|
||||
},
|
||||
[filter.musicFolderId, handleFilterChange, setFilter, pageKey],
|
||||
[filter.musicFolderId, handleRefreshTable, tableRef, setFilter, customFilters, pageKey],
|
||||
);
|
||||
|
||||
const handleToggleSortOrder = useCallback(() => {
|
||||
const newSortOrder = filter.sortOrder === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC;
|
||||
const updatedFilters = setFilter({
|
||||
customFilters,
|
||||
data: { sortOrder: newSortOrder },
|
||||
itemType: LibraryItem.SONG,
|
||||
key: pageKey,
|
||||
}) as SongListFilter;
|
||||
handleFilterChange(updatedFilters);
|
||||
}, [filter.sortOrder, handleFilterChange, pageKey, setFilter]);
|
||||
handleRefreshTable(tableRef, updatedFilters);
|
||||
}, [customFilters, filter.sortOrder, handleRefreshTable, pageKey, setFilter, tableRef]);
|
||||
|
||||
const handleSetViewType = useCallback(
|
||||
(e: MouseEvent<HTMLButtonElement>) => {
|
||||
@ -258,7 +218,14 @@ export const SongListHeaderFilters = ({ tableRef }: SongListHeaderFiltersProps)
|
||||
|
||||
const handleRefresh = () => {
|
||||
queryClient.invalidateQueries(queryKeys.songs.list(server?.id || ''));
|
||||
handleFilterChange(filter);
|
||||
handleRefreshTable(tableRef, filter);
|
||||
};
|
||||
|
||||
const onFilterChange = (filter: SongListFilter) => {
|
||||
handleRefreshTable(tableRef, {
|
||||
...filter,
|
||||
...customFilters,
|
||||
});
|
||||
};
|
||||
|
||||
const handleOpenFiltersModal = () => {
|
||||
@ -267,15 +234,15 @@ export const SongListHeaderFilters = ({ tableRef }: SongListHeaderFiltersProps)
|
||||
<>
|
||||
{server?.type === ServerType.NAVIDROME ? (
|
||||
<NavidromeSongFilters
|
||||
handleFilterChange={handleFilterChange}
|
||||
id={id}
|
||||
customFilters={customFilters}
|
||||
pageKey={pageKey}
|
||||
onFilterChange={onFilterChange}
|
||||
/>
|
||||
) : (
|
||||
<JellyfinSongFilters
|
||||
handleFilterChange={handleFilterChange}
|
||||
id={id}
|
||||
customFilters={customFilters}
|
||||
pageKey={pageKey}
|
||||
onFilterChange={onFilterChange}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
@ -1,24 +1,18 @@
|
||||
import type { IDatasource } from '@ag-grid-community/core';
|
||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||
import { Flex, Group, Stack } from '@mantine/core';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { ChangeEvent, MutableRefObject, useCallback } from 'react';
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { LibraryItem, SongListQuery } from '/@/renderer/api/types';
|
||||
import { ChangeEvent, MutableRefObject } from 'react';
|
||||
import { useListStoreByKey } from '../../../store/list.store';
|
||||
import { LibraryItem } from '/@/renderer/api/types';
|
||||
import { PageHeader, SearchInput } from '/@/renderer/components';
|
||||
import { useListContext } from '/@/renderer/context/list-context';
|
||||
import { FilterBar, LibraryHeaderBar } from '/@/renderer/features/shared';
|
||||
import { SongListHeaderFilters } from '/@/renderer/features/songs/components/song-list-header-filters';
|
||||
import { useSongListContext } from '/@/renderer/features/songs/context/song-list-context';
|
||||
import { useContainerQuery } from '/@/renderer/hooks';
|
||||
import { queryClient } from '/@/renderer/lib/react-query';
|
||||
import {
|
||||
SongListFilter,
|
||||
useCurrentServer,
|
||||
useListStoreActions,
|
||||
useSongListFilter,
|
||||
} from '/@/renderer/store';
|
||||
import { useListFilterRefresh } from '/@/renderer/hooks/use-list-filter-refresh';
|
||||
import { SongListFilter, useCurrentServer, useListStoreActions } from '/@/renderer/store';
|
||||
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
|
||||
import { ListDisplayType } from '/@/renderer/types';
|
||||
|
||||
interface SongListHeaderProps {
|
||||
itemCount?: number;
|
||||
@ -28,62 +22,35 @@ interface SongListHeaderProps {
|
||||
|
||||
export const SongListHeader = ({ title, itemCount, tableRef }: SongListHeaderProps) => {
|
||||
const server = useCurrentServer();
|
||||
const { id, pageKey, handlePlay } = useSongListContext();
|
||||
const filter = useSongListFilter({ id, key: pageKey });
|
||||
const { pageKey, handlePlay, customFilters } = useListContext();
|
||||
const { setFilter, setTablePagination } = useListStoreActions();
|
||||
const { display, filter } = useListStoreByKey({ key: pageKey });
|
||||
const cq = useContainerQuery();
|
||||
|
||||
const handleFilterChange = useCallback(
|
||||
async (filters?: SongListFilter) => {
|
||||
const dataSource: IDatasource = {
|
||||
getRows: async (params) => {
|
||||
const limit = params.endRow - params.startRow;
|
||||
const startIndex = params.startRow;
|
||||
|
||||
const pageFilters = filters || filter;
|
||||
|
||||
const query: SongListQuery = {
|
||||
limit,
|
||||
startIndex,
|
||||
...pageFilters,
|
||||
};
|
||||
|
||||
const queryKey = queryKeys.songs.list(server?.id || '', query);
|
||||
|
||||
const songsRes = await queryClient.fetchQuery(
|
||||
queryKey,
|
||||
async ({ signal }) =>
|
||||
api.controller.getSongList({
|
||||
apiClientProps: {
|
||||
server,
|
||||
signal,
|
||||
},
|
||||
query,
|
||||
}),
|
||||
{ cacheTime: 1000 * 60 * 1 },
|
||||
);
|
||||
|
||||
params.successCallback(songsRes?.items || [], songsRes?.totalRecordCount || 0);
|
||||
},
|
||||
rowCount: undefined,
|
||||
};
|
||||
tableRef.current?.api.setDatasource(dataSource);
|
||||
tableRef.current?.api.purgeInfiniteCache();
|
||||
tableRef.current?.api.ensureIndexVisible(0, 'top');
|
||||
setTablePagination({ data: { currentPage: 0 }, key: pageKey });
|
||||
},
|
||||
[filter, pageKey, server, setTablePagination, tableRef],
|
||||
);
|
||||
const { handleRefreshTable } = useListFilterRefresh({
|
||||
itemType: LibraryItem.SONG,
|
||||
server,
|
||||
});
|
||||
|
||||
const handleSearch = debounce((e: ChangeEvent<HTMLInputElement>) => {
|
||||
const previousSearchTerm = filter.searchTerm;
|
||||
const searchTerm = e.target.value === '' ? undefined : e.target.value;
|
||||
const updatedFilters = setFilter({
|
||||
data: { searchTerm },
|
||||
itemType: LibraryItem.SONG,
|
||||
key: pageKey,
|
||||
}) as SongListFilter;
|
||||
if (previousSearchTerm !== searchTerm) handleFilterChange(updatedFilters);
|
||||
|
||||
const filterWithCustom = {
|
||||
...updatedFilters,
|
||||
...customFilters,
|
||||
};
|
||||
|
||||
if (display === ListDisplayType.TABLE || display === ListDisplayType.TABLE_PAGINATED) {
|
||||
handleRefreshTable(tableRef, filterWithCustom);
|
||||
setTablePagination({ data: { currentPage: 0 }, key: pageKey });
|
||||
} else {
|
||||
// handleRefreshGrid(gridRef, filterWithCustom);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
const playButtonBehavior = usePlayButtonBehavior();
|
||||
|
@ -1,25 +1,14 @@
|
||||
import { RowDoubleClickedEvent } from '@ag-grid-community/core';
|
||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||
import { MutableRefObject, useCallback } from 'react';
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { LibraryItem, QueueSong, SongListQuery, SongListResponse } from '/@/renderer/api/types';
|
||||
import { MutableRefObject } from 'react';
|
||||
import { LibraryItem, QueueSong, SongListQuery } from '/@/renderer/api/types';
|
||||
import { VirtualGridAutoSizerContainer } from '/@/renderer/components/virtual-grid';
|
||||
import { VirtualTable } from '/@/renderer/components/virtual-table';
|
||||
import { useCurrentSongRowStyles } from '/@/renderer/components/virtual-table/hooks/use-current-song-row-styles';
|
||||
import {
|
||||
AgGridFetchFn,
|
||||
useVirtualTable,
|
||||
} from '/@/renderer/components/virtual-table/hooks/use-virtual-table';
|
||||
import { useVirtualTable } from '/@/renderer/components/virtual-table/hooks/use-virtual-table';
|
||||
import { useListContext } from '/@/renderer/context/list-context';
|
||||
import { SONG_CONTEXT_MENU_ITEMS } from '/@/renderer/features/context-menu/context-menu-items';
|
||||
import { useSongListContext } from '/@/renderer/features/songs/context/song-list-context';
|
||||
import {
|
||||
useCurrentServer,
|
||||
useListStoreActions,
|
||||
usePlayButtonBehavior,
|
||||
useSongListFilter,
|
||||
useSongListStore,
|
||||
} from '/@/renderer/store';
|
||||
import { useCurrentServer, usePlayButtonBehavior } from '/@/renderer/store';
|
||||
|
||||
interface SongListTableViewProps {
|
||||
itemCount?: number;
|
||||
@ -28,50 +17,17 @@ interface SongListTableViewProps {
|
||||
|
||||
export const SongListTableView = ({ tableRef, itemCount }: SongListTableViewProps) => {
|
||||
const server = useCurrentServer();
|
||||
const { id, pageKey, handlePlay } = useSongListContext();
|
||||
const filter = useSongListFilter({ id, key: pageKey });
|
||||
const listProperties = useSongListStore({ id, key: pageKey });
|
||||
|
||||
const { setTable, setTablePagination } = useListStoreActions();
|
||||
|
||||
const fetchFn: AgGridFetchFn<SongListResponse, Omit<SongListQuery, 'startIndex'>> = useCallback(
|
||||
async ({ filter, limit, startIndex }, signal) => {
|
||||
const res = api.controller.getSongList({
|
||||
apiClientProps: {
|
||||
server,
|
||||
signal,
|
||||
},
|
||||
query: {
|
||||
...filter,
|
||||
limit,
|
||||
sortBy: filter.sortBy,
|
||||
sortOrder: filter.sortOrder,
|
||||
startIndex,
|
||||
},
|
||||
});
|
||||
|
||||
return res;
|
||||
},
|
||||
[server],
|
||||
);
|
||||
const { pageKey, handlePlay, customFilters } = useListContext();
|
||||
|
||||
const { rowClassRules } = useCurrentSongRowStyles({ tableRef });
|
||||
|
||||
const tableProps = useVirtualTable<SongListResponse, Omit<SongListQuery, 'startIndex'>>({
|
||||
const tableProps = useVirtualTable<SongListQuery>({
|
||||
contextMenu: SONG_CONTEXT_MENU_ITEMS,
|
||||
fetch: {
|
||||
filter,
|
||||
fn: fetchFn,
|
||||
itemCount,
|
||||
queryKey: queryKeys.albums.list,
|
||||
server,
|
||||
},
|
||||
customFilters,
|
||||
itemCount,
|
||||
itemType: LibraryItem.SONG,
|
||||
pageKey,
|
||||
properties: listProperties,
|
||||
setTable,
|
||||
setTablePagination,
|
||||
server,
|
||||
tableRef,
|
||||
});
|
||||
|
||||
@ -86,7 +42,7 @@ export const SongListTableView = ({ tableRef, itemCount }: SongListTableViewProp
|
||||
<VirtualTable
|
||||
// https://github.com/ag-grid/ag-grid/issues/5284
|
||||
// Key is used to force remount of table when display, rowHeight, or server changes
|
||||
key={`table-${listProperties.display}-${listProperties.table.rowHeight}-${server?.id}`}
|
||||
key={`table-${tableProps.rowHeight}-${server?.id}`}
|
||||
ref={tableRef}
|
||||
{...tableProps}
|
||||
rowClassRules={rowClassRules}
|
||||
|
@ -1,18 +0,0 @@
|
||||
import { createContext, useContext } from 'react';
|
||||
import { ListKey } from '/@/renderer/store';
|
||||
import { Play } from '/@/renderer/types';
|
||||
|
||||
interface SongListContextProps {
|
||||
handlePlay?: (args: { initialSongId?: string; playType: Play }) => void;
|
||||
id?: string;
|
||||
pageKey: ListKey;
|
||||
}
|
||||
|
||||
export const SongListContext = createContext<SongListContextProps>({
|
||||
pageKey: 'song',
|
||||
});
|
||||
|
||||
export const useSongListContext = () => {
|
||||
const ctxValue = useContext(SongListContext);
|
||||
return ctxValue;
|
||||
};
|
@ -1,14 +1,14 @@
|
||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { useParams, useSearchParams } from 'react-router-dom';
|
||||
import { SongListQuery, LibraryItem } from '/@/renderer/api/types';
|
||||
import { LibraryItem, SongListQuery } from '/@/renderer/api/types';
|
||||
import { ListContext } from '/@/renderer/context/list-context';
|
||||
import { usePlayQueueAdd } from '/@/renderer/features/player';
|
||||
import { AnimatedPage } from '/@/renderer/features/shared';
|
||||
import { SongListContent } from '/@/renderer/features/songs/components/song-list-content';
|
||||
import { SongListHeader } from '/@/renderer/features/songs/components/song-list-header';
|
||||
import { SongListContext } from '/@/renderer/features/songs/context/song-list-context';
|
||||
import { useSongList } from '/@/renderer/features/songs/queries/song-list-query';
|
||||
import { generatePageKey, useCurrentServer, useSongListFilter } from '/@/renderer/store';
|
||||
import { useCurrentServer, useListFilterByKey } from '/@/renderer/store';
|
||||
import { Play } from '/@/renderer/types';
|
||||
|
||||
const TrackListRoute = () => {
|
||||
@ -16,13 +16,18 @@ const TrackListRoute = () => {
|
||||
const server = useCurrentServer();
|
||||
const [searchParams] = useSearchParams();
|
||||
const { albumArtistId } = useParams();
|
||||
const pageKey = generatePageKey(
|
||||
'song',
|
||||
albumArtistId ? `${albumArtistId}_${server?.id}` : undefined,
|
||||
);
|
||||
const pageKey = albumArtistId ? `albumArtistSong` : 'song';
|
||||
|
||||
const customFilters = {
|
||||
...(albumArtistId && { artistIds: [albumArtistId] }),
|
||||
};
|
||||
|
||||
const handlePlayQueueAdd = usePlayQueueAdd();
|
||||
const songListFilter = useSongListFilter({ id: albumArtistId, key: pageKey });
|
||||
const songListFilter = useListFilterByKey<SongListQuery>({
|
||||
filter: customFilters,
|
||||
key: pageKey,
|
||||
});
|
||||
|
||||
const itemCountCheck = useSongList({
|
||||
options: {
|
||||
cacheTime: 1000 * 60,
|
||||
@ -74,7 +79,7 @@ const TrackListRoute = () => {
|
||||
|
||||
return (
|
||||
<AnimatedPage>
|
||||
<SongListContext.Provider value={{ handlePlay, id: albumArtistId, pageKey }}>
|
||||
<ListContext.Provider value={{ customFilters, handlePlay, id: albumArtistId, pageKey }}>
|
||||
<SongListHeader
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
@ -84,7 +89,7 @@ const TrackListRoute = () => {
|
||||
itemCount={itemCount}
|
||||
tableRef={tableRef}
|
||||
/>
|
||||
</SongListContext.Provider>
|
||||
</ListContext.Provider>
|
||||
</AnimatedPage>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user