mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-09 20:29:36 +02:00
add client side sort to album song list
This commit is contained in:
@@ -15,6 +15,8 @@ import { albumQueries } from '/@/renderer/features/albums/api/album-api';
|
|||||||
import { AlbumInfiniteCarousel } from '/@/renderer/features/albums/components/album-infinite-carousel';
|
import { AlbumInfiniteCarousel } from '/@/renderer/features/albums/components/album-infinite-carousel';
|
||||||
import { usePlayer } from '/@/renderer/features/player/context/player-context';
|
import { usePlayer } from '/@/renderer/features/player/context/player-context';
|
||||||
import { ListConfigMenu } from '/@/renderer/features/shared/components/list-config-menu';
|
import { ListConfigMenu } from '/@/renderer/features/shared/components/list-config-menu';
|
||||||
|
import { ListSortByDropdownControlled } from '/@/renderer/features/shared/components/list-sort-by-dropdown';
|
||||||
|
import { ListSortOrderToggleButtonControlled } from '/@/renderer/features/shared/components/list-sort-order-toggle-button';
|
||||||
import { searchLibraryItems } from '/@/renderer/features/shared/utils';
|
import { searchLibraryItems } from '/@/renderer/features/shared/utils';
|
||||||
import { useContainerQuery } from '/@/renderer/hooks';
|
import { useContainerQuery } from '/@/renderer/hooks';
|
||||||
import { AppRoute } from '/@/renderer/router/routes';
|
import { AppRoute } from '/@/renderer/router/routes';
|
||||||
@@ -28,6 +30,7 @@ import {
|
|||||||
} from '/@/renderer/utils';
|
} from '/@/renderer/utils';
|
||||||
import { replaceURLWithHTMLLinks } from '/@/renderer/utils/linkify';
|
import { replaceURLWithHTMLLinks } from '/@/renderer/utils/linkify';
|
||||||
import { normalizeReleaseTypes } from '/@/renderer/utils/normalize-release-types';
|
import { normalizeReleaseTypes } from '/@/renderer/utils/normalize-release-types';
|
||||||
|
import { sortSongList } from '/@/shared/api/utils';
|
||||||
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
||||||
import { Checkbox } from '/@/shared/components/checkbox/checkbox';
|
import { Checkbox } from '/@/shared/components/checkbox/checkbox';
|
||||||
import { Flex } from '/@/shared/components/flex/flex';
|
import { Flex } from '/@/shared/components/flex/flex';
|
||||||
@@ -46,6 +49,7 @@ import {
|
|||||||
ExplicitStatus,
|
ExplicitStatus,
|
||||||
LibraryItem,
|
LibraryItem,
|
||||||
Song,
|
Song,
|
||||||
|
SongListSort,
|
||||||
SortOrder,
|
SortOrder,
|
||||||
} from '/@/shared/types/domain-types';
|
} from '/@/shared/types/domain-types';
|
||||||
import { ItemListKey, ListDisplayType, Play } from '/@/shared/types/types';
|
import { ItemListKey, ListDisplayType, Play } from '/@/shared/types/types';
|
||||||
@@ -426,13 +430,20 @@ const AlbumDetailSongsTable = ({ songs }: AlbumDetailSongsTableProps) => {
|
|||||||
|
|
||||||
const currentSong = usePlayerSong();
|
const currentSong = usePlayerSong();
|
||||||
|
|
||||||
|
const [sortBy, setSortBy] = useState<SongListSort>(SongListSort.ID);
|
||||||
|
const [sortOrder, setSortOrder] = useState<SortOrder>(SortOrder.ASC);
|
||||||
|
|
||||||
const columns = useMemo(() => {
|
const columns = useMemo(() => {
|
||||||
return tableConfig?.columns || [];
|
return tableConfig?.columns || [];
|
||||||
}, [tableConfig?.columns]);
|
}, [tableConfig?.columns]);
|
||||||
|
|
||||||
const filteredSongs = useMemo(() => {
|
const filteredSongs = useMemo(() => {
|
||||||
return searchLibraryItems(songs, searchTerm, LibraryItem.SONG);
|
return sortSongList(
|
||||||
}, [songs, searchTerm]);
|
searchLibraryItems(songs, searchTerm, LibraryItem.SONG),
|
||||||
|
sortBy,
|
||||||
|
sortOrder,
|
||||||
|
);
|
||||||
|
}, [songs, searchTerm, sortBy, sortOrder]);
|
||||||
|
|
||||||
const { handleColumnReordered } = useItemListColumnReorder({
|
const { handleColumnReordered } = useItemListColumnReorder({
|
||||||
itemListKey: ItemListKey.ALBUM_DETAIL,
|
itemListKey: ItemListKey.ALBUM_DETAIL,
|
||||||
@@ -484,6 +495,11 @@ const AlbumDetailSongsTable = ({ songs }: AlbumDetailSongsTableProps) => {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove groups when sorting
|
||||||
|
if (sortBy !== SongListSort.ID) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
if (discGroups.length <= 1) {
|
if (discGroups.length <= 1) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -561,7 +577,7 @@ const AlbumDetailSongsTable = ({ songs }: AlbumDetailSongsTableProps) => {
|
|||||||
},
|
},
|
||||||
rowHeight: 40,
|
rowHeight: 40,
|
||||||
}));
|
}));
|
||||||
}, [discGroups, t, searchTerm]);
|
}, [searchTerm, sortBy, discGroups, t]);
|
||||||
|
|
||||||
const player = usePlayer();
|
const player = usePlayer();
|
||||||
|
|
||||||
@@ -630,6 +646,15 @@ const AlbumDetailSongsTable = ({ songs }: AlbumDetailSongsTableProps) => {
|
|||||||
}}
|
}}
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
/>
|
/>
|
||||||
|
<ListSortByDropdownControlled
|
||||||
|
itemType={LibraryItem.PLAYLIST_SONG}
|
||||||
|
setSortBy={(value) => setSortBy(value as SongListSort)}
|
||||||
|
sortBy={sortBy}
|
||||||
|
/>
|
||||||
|
<ListSortOrderToggleButtonControlled
|
||||||
|
setSortOrder={(value) => setSortOrder(value as SortOrder)}
|
||||||
|
sortOrder={sortOrder}
|
||||||
|
/>
|
||||||
<ListConfigMenu
|
<ListConfigMenu
|
||||||
displayTypes={[{ hidden: true, value: ListDisplayType.GRID }]}
|
displayTypes={[{ hidden: true, value: ListDisplayType.GRID }]}
|
||||||
listKey={ItemListKey.ALBUM_DETAIL}
|
listKey={ItemListKey.ALBUM_DETAIL}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
|
|
||||||
import i18n from '/@/i18n/i18n';
|
import i18n from '/@/i18n/i18n';
|
||||||
import { useSortByFilter } from '/@/renderer/features/shared/hooks/use-sort-by-filter';
|
import { useSortByFilter } from '/@/renderer/features/shared/hooks/use-sort-by-filter';
|
||||||
import { useCurrentServer } from '/@/renderer/store';
|
import { useCurrentServer } from '/@/renderer/store';
|
||||||
@@ -19,6 +21,7 @@ import { ItemListKey } from '/@/shared/types/types';
|
|||||||
interface ListSortByDropdownProps {
|
interface ListSortByDropdownProps {
|
||||||
defaultSortByValue: string;
|
defaultSortByValue: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
includeId?: boolean;
|
||||||
itemType: LibraryItem;
|
itemType: LibraryItem;
|
||||||
listKey: ItemListKey;
|
listKey: ItemListKey;
|
||||||
onChange?: (value: string) => void;
|
onChange?: (value: string) => void;
|
||||||
@@ -72,6 +75,57 @@ export const ListSortByDropdown = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface ListSortByDropdownControlledProps {
|
||||||
|
disabled?: boolean;
|
||||||
|
itemType: LibraryItem;
|
||||||
|
setSortBy: Dispatch<SetStateAction<string>>;
|
||||||
|
sortBy: string;
|
||||||
|
target?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ListSortByDropdownControlled = ({
|
||||||
|
disabled,
|
||||||
|
itemType,
|
||||||
|
setSortBy,
|
||||||
|
sortBy,
|
||||||
|
target,
|
||||||
|
}: ListSortByDropdownControlledProps) => {
|
||||||
|
const server = useCurrentServer();
|
||||||
|
|
||||||
|
const sortByLabel =
|
||||||
|
(itemType && FILTERS[itemType][server.type].find((f) => f.value === sortBy)?.name) || '—';
|
||||||
|
|
||||||
|
const handleSortByChange = (sortBy: string) => {
|
||||||
|
setSortBy(sortBy);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu disabled={disabled} position="bottom-start">
|
||||||
|
<DropdownMenu.Target>
|
||||||
|
{target ? (
|
||||||
|
target
|
||||||
|
) : (
|
||||||
|
<Button disabled={disabled} variant="subtle">
|
||||||
|
{sortByLabel}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</DropdownMenu.Target>
|
||||||
|
<DropdownMenu.Dropdown>
|
||||||
|
{FILTERS[itemType][server.type].map((f) => (
|
||||||
|
<DropdownMenu.Item
|
||||||
|
isSelected={f.value === sortBy}
|
||||||
|
key={`filter-${f.name}`}
|
||||||
|
onClick={() => handleSortByChange(f.value)}
|
||||||
|
value={f.value}
|
||||||
|
>
|
||||||
|
{f.name}
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
))}
|
||||||
|
</DropdownMenu.Dropdown>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const CLIENT_SIDE_SONG_FILTERS = [
|
const CLIENT_SIDE_SONG_FILTERS = [
|
||||||
{
|
{
|
||||||
defaultOrder: SortOrder.ASC,
|
defaultOrder: SortOrder.ASC,
|
||||||
|
|||||||
@@ -29,3 +29,25 @@ export const ListSortOrderToggleButton = ({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface ListSortOrderToggleButtonControlledProps {
|
||||||
|
disabled?: boolean;
|
||||||
|
setSortOrder: (sortOrder: SortOrder) => void;
|
||||||
|
sortOrder: SortOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ListSortOrderToggleButtonControlled = ({
|
||||||
|
disabled,
|
||||||
|
setSortOrder,
|
||||||
|
sortOrder,
|
||||||
|
}: ListSortOrderToggleButtonControlledProps) => {
|
||||||
|
return (
|
||||||
|
<OrderToggleButton
|
||||||
|
disabled={disabled}
|
||||||
|
onToggle={() =>
|
||||||
|
setSortOrder(sortOrder === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC)
|
||||||
|
}
|
||||||
|
sortOrder={sortOrder as SortOrder}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user