mirror of
https://github.com/antebudimir/feishin.git
synced 2025-12-31 18:13:31 +00:00
client-side only sort for all playlists (#1125)
* initial client-side only sort for all playlists * allow reordering jellyfin (assume it works properly) and navidrome * on playlist page, add to queue by sort order
This commit is contained in:
parent
d68165dab5
commit
1d46cd5ff9
16 changed files with 135 additions and 247 deletions
|
|
@ -542,10 +542,6 @@ export const JellyfinController: ControllerEndpoint = {
|
||||||
query: {
|
query: {
|
||||||
Fields: 'Genres, DateCreated, MediaSources, UserData, ParentId, People, Tags',
|
Fields: 'Genres, DateCreated, MediaSources, UserData, ParentId, People, Tags',
|
||||||
IncludeItemTypes: 'Audio',
|
IncludeItemTypes: 'Audio',
|
||||||
Limit: query.limit,
|
|
||||||
SortBy: query.sortBy ? songListSortMap.jellyfin[query.sortBy] : undefined,
|
|
||||||
SortOrder: query.sortOrder ? sortOrderMap.jellyfin[query.sortOrder] : undefined,
|
|
||||||
StartIndex: query.startIndex,
|
|
||||||
UserId: apiClientProps.server?.userId,
|
UserId: apiClientProps.server?.userId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -556,7 +552,7 @@ export const JellyfinController: ControllerEndpoint = {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: res.body.Items.map((item) => jfNormalize.song(item, apiClientProps.server, '')),
|
items: res.body.Items.map((item) => jfNormalize.song(item, apiClientProps.server, '')),
|
||||||
startIndex: query.startIndex,
|
startIndex: 0,
|
||||||
totalRecordCount: res.body.TotalRecordCount,
|
totalRecordCount: res.body.TotalRecordCount,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import { ssApiClient } from '/@/renderer/api/subsonic/subsonic-api';
|
||||||
import { SubsonicController } from '/@/renderer/api/subsonic/subsonic-controller';
|
import { SubsonicController } from '/@/renderer/api/subsonic/subsonic-controller';
|
||||||
import { NDSongListSort } from '/@/shared/api/navidrome.types';
|
import { NDSongListSort } from '/@/shared/api/navidrome.types';
|
||||||
import { ndNormalize } from '/@/shared/api/navidrome/navidrome-normalize';
|
import { ndNormalize } from '/@/shared/api/navidrome/navidrome-normalize';
|
||||||
import { ndType } from '/@/shared/api/navidrome/navidrome-types';
|
|
||||||
import { ssNormalize } from '/@/shared/api/subsonic/subsonic-normalize';
|
import { ssNormalize } from '/@/shared/api/subsonic/subsonic-normalize';
|
||||||
import { SubsonicExtensions } from '/@/shared/api/subsonic/subsonic-types';
|
import { SubsonicExtensions } from '/@/shared/api/subsonic/subsonic-types';
|
||||||
import { getFeatures, hasFeature, VersionInfo } from '/@/shared/api/utils';
|
import { getFeatures, hasFeature, VersionInfo } from '/@/shared/api/utils';
|
||||||
|
|
@ -430,12 +429,9 @@ export const NavidromeController: ControllerEndpoint = {
|
||||||
id: query.id,
|
id: query.id,
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
_end: query.startIndex + (query.limit || -1),
|
_end: -1,
|
||||||
_order: query.sortOrder ? sortOrderMap.navidrome[query.sortOrder] : 'ASC',
|
_order: 'ASC',
|
||||||
_sort: query.sortBy
|
_start: 0,
|
||||||
? songListSortMap.navidrome[query.sortBy]
|
|
||||||
: ndType._enum.songList.ID,
|
|
||||||
_start: query.startIndex,
|
|
||||||
...excludeMissing(apiClientProps.server),
|
...excludeMissing(apiClientProps.server),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -446,7 +442,7 @@ export const NavidromeController: ControllerEndpoint = {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: res.body.data.map((item) => ndNormalize.song(item, apiClientProps.server)),
|
items: res.body.data.map((item) => ndNormalize.song(item, apiClientProps.server)),
|
||||||
startIndex: query?.startIndex || 0,
|
startIndex: 0,
|
||||||
totalRecordCount: Number(res.body.headers.get('x-total-count') || 0),
|
totalRecordCount: Number(res.body.headers.get('x-total-count') || 0),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import type {
|
||||||
LyricsQuery,
|
LyricsQuery,
|
||||||
PlaylistDetailQuery,
|
PlaylistDetailQuery,
|
||||||
PlaylistListQuery,
|
PlaylistListQuery,
|
||||||
PlaylistSongListQuery,
|
|
||||||
RandomSongListQuery,
|
RandomSongListQuery,
|
||||||
SearchQuery,
|
SearchQuery,
|
||||||
SimilarSongsQuery,
|
SimilarSongsQuery,
|
||||||
|
|
@ -191,21 +190,6 @@ export const queryKeys: Record<
|
||||||
if (id) return [serverId, 'playlists', id, 'detail'] as const;
|
if (id) return [serverId, 'playlists', id, 'detail'] as const;
|
||||||
return [serverId, 'playlists', 'detail'] as const;
|
return [serverId, 'playlists', 'detail'] as const;
|
||||||
},
|
},
|
||||||
detailSongList: (serverId: string, id: string, query?: PlaylistSongListQuery) => {
|
|
||||||
const { filter, pagination } = splitPaginatedQuery(query);
|
|
||||||
|
|
||||||
if (query && id && pagination) {
|
|
||||||
return [serverId, 'playlists', id, 'detailSongList', filter, pagination] as const;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (query && id) {
|
|
||||||
return [serverId, 'playlists', id, 'detailSongList', filter] as const;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id) return [serverId, 'playlists', id, 'detailSongList'] as const;
|
|
||||||
|
|
||||||
return [serverId, 'playlists', 'detailSongList'] as const;
|
|
||||||
},
|
|
||||||
list: (serverId: string, query?: PlaylistListQuery) => {
|
list: (serverId: string, query?: PlaylistListQuery) => {
|
||||||
const { filter, pagination } = splitPaginatedQuery(query);
|
const { filter, pagination } = splitPaginatedQuery(query);
|
||||||
if (query && pagination) {
|
if (query && pagination) {
|
||||||
|
|
@ -219,16 +203,7 @@ export const queryKeys: Record<
|
||||||
return [serverId, 'playlists', 'list'] as const;
|
return [serverId, 'playlists', 'list'] as const;
|
||||||
},
|
},
|
||||||
root: (serverId: string) => [serverId, 'playlists'] as const,
|
root: (serverId: string) => [serverId, 'playlists'] as const,
|
||||||
songList: (serverId: string, id?: string, query?: PlaylistSongListQuery) => {
|
songList: (serverId: string, id?: string) => {
|
||||||
const { filter, pagination } = splitPaginatedQuery(query);
|
|
||||||
if (query && id && pagination) {
|
|
||||||
return [serverId, 'playlists', id, 'songList', filter, pagination] as const;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (query && id) {
|
|
||||||
return [serverId, 'playlists', id, 'songList', filter] as const;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id) return [serverId, 'playlists', id, 'songList'] as const;
|
if (id) return [serverId, 'playlists', id, 'songList'] as const;
|
||||||
return [serverId, 'playlists', 'songList'] as const;
|
return [serverId, 'playlists', 'songList'] as const;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -759,18 +759,14 @@ export const SubsonicController: ControllerEndpoint = {
|
||||||
throw new Error('Failed to get playlist song list');
|
throw new Error('Failed to get playlist song list');
|
||||||
}
|
}
|
||||||
|
|
||||||
let results =
|
const items =
|
||||||
res.body.playlist.entry?.map((song) => ssNormalize.song(song, apiClientProps.server)) ||
|
res.body.playlist.entry?.map((song) => ssNormalize.song(song, apiClientProps.server)) ||
|
||||||
[];
|
[];
|
||||||
|
|
||||||
if (query.sortBy && query.sortOrder) {
|
|
||||||
results = sortSongList(results, query.sortBy, query.sortOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: results,
|
items,
|
||||||
startIndex: 0,
|
startIndex: 0,
|
||||||
totalRecordCount: results?.length || 0,
|
totalRecordCount: items.length,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
getRandomSongList: async (args) => {
|
getRandomSongList: async (args) => {
|
||||||
|
|
|
||||||
|
|
@ -541,7 +541,6 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
ctx.context?.tableRef?.current?.api?.refreshInfiniteCache();
|
|
||||||
closeAllModals();
|
closeAllModals();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,8 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,19 @@ import { api } from '/@/renderer/api';
|
||||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||||
import {
|
import {
|
||||||
PlaylistSongListQuery,
|
PlaylistSongListQuery,
|
||||||
|
PlaylistSongListQueryClientSide,
|
||||||
ServerListItem,
|
ServerListItem,
|
||||||
SongDetailQuery,
|
SongDetailQuery,
|
||||||
SongListQuery,
|
SongListQuery,
|
||||||
SongListResponse,
|
SongListResponse,
|
||||||
SongListSort,
|
SongListSort,
|
||||||
SortOrder,
|
SortOrder,
|
||||||
|
sortSongList,
|
||||||
} from '/@/shared/types/domain-types';
|
} from '/@/shared/types/domain-types';
|
||||||
|
|
||||||
export const getPlaylistSongsById = async (args: {
|
export const getPlaylistSongsById = async (args: {
|
||||||
id: string;
|
id: string;
|
||||||
query?: Partial<PlaylistSongListQuery>;
|
query?: Partial<PlaylistSongListQueryClientSide>;
|
||||||
queryClient: QueryClient;
|
queryClient: QueryClient;
|
||||||
server: ServerListItem;
|
server: ServerListItem;
|
||||||
}) => {
|
}) => {
|
||||||
|
|
@ -22,13 +24,9 @@ export const getPlaylistSongsById = async (args: {
|
||||||
|
|
||||||
const queryFilter: PlaylistSongListQuery = {
|
const queryFilter: PlaylistSongListQuery = {
|
||||||
id,
|
id,
|
||||||
sortBy: SongListSort.ID,
|
|
||||||
sortOrder: SortOrder.ASC,
|
|
||||||
startIndex: 0,
|
|
||||||
...query,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const queryKey = queryKeys.playlists.songList(server?.id, id, queryFilter);
|
const queryKey = queryKeys.playlists.songList(server?.id, id);
|
||||||
|
|
||||||
const res = await queryClient.fetchQuery(
|
const res = await queryClient.fetchQuery(
|
||||||
queryKey,
|
queryKey,
|
||||||
|
|
@ -46,6 +44,14 @@ export const getPlaylistSongsById = async (args: {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
res.items = sortSongList(
|
||||||
|
res.items,
|
||||||
|
query?.sortBy || SongListSort.ID,
|
||||||
|
query?.sortOrder || SortOrder.ASC,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -145,12 +145,7 @@ export const AddToPlaylistContextModal = ({
|
||||||
const uniqueSongIds: string[] = [];
|
const uniqueSongIds: string[] = [];
|
||||||
|
|
||||||
if (values.skipDuplicates) {
|
if (values.skipDuplicates) {
|
||||||
const query = {
|
const queryKey = queryKeys.playlists.songList(server?.id || '', playlistId);
|
||||||
id: playlistId,
|
|
||||||
startIndex: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
const queryKey = queryKeys.playlists.songList(server?.id || '', playlistId, query);
|
|
||||||
|
|
||||||
const playlistSongsRes = await queryClient.fetchQuery(queryKey, ({ signal }) => {
|
const playlistSongsRes = await queryClient.fetchQuery(queryKey, ({ signal }) => {
|
||||||
if (!server)
|
if (!server)
|
||||||
|
|
@ -164,9 +159,6 @@ export const AddToPlaylistContextModal = ({
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
id: playlistId,
|
id: playlistId,
|
||||||
sortBy: SongListSort.ID,
|
|
||||||
sortOrder: SortOrder.ASC,
|
|
||||||
startIndex: 0,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import type {
|
||||||
BodyScrollEvent,
|
BodyScrollEvent,
|
||||||
ColDef,
|
ColDef,
|
||||||
GridReadyEvent,
|
GridReadyEvent,
|
||||||
IDatasource,
|
|
||||||
PaginationChangedEvent,
|
PaginationChangedEvent,
|
||||||
RowDoubleClickedEvent,
|
RowDoubleClickedEvent,
|
||||||
RowDragEvent,
|
RowDragEvent,
|
||||||
|
|
@ -27,7 +26,6 @@ import {
|
||||||
} from '/@/renderer/features/context-menu/context-menu-items';
|
} from '/@/renderer/features/context-menu/context-menu-items';
|
||||||
import { usePlayQueueAdd } from '/@/renderer/features/player';
|
import { usePlayQueueAdd } from '/@/renderer/features/player';
|
||||||
import { usePlaylistDetail } from '/@/renderer/features/playlists/queries/playlist-detail-query';
|
import { usePlaylistDetail } from '/@/renderer/features/playlists/queries/playlist-detail-query';
|
||||||
import { usePlaylistSongList } from '/@/renderer/features/playlists/queries/playlist-song-list-query';
|
|
||||||
import { useAppFocus } from '/@/renderer/hooks';
|
import { useAppFocus } from '/@/renderer/hooks';
|
||||||
import {
|
import {
|
||||||
useCurrentServer,
|
useCurrentServer,
|
||||||
|
|
@ -42,13 +40,15 @@ import { PersistedTableColumn, usePlayButtonBehavior } from '/@/renderer/store/s
|
||||||
import { toast } from '/@/shared/components/toast/toast';
|
import { toast } from '/@/shared/components/toast/toast';
|
||||||
import {
|
import {
|
||||||
LibraryItem,
|
LibraryItem,
|
||||||
PlaylistSongListQuery,
|
PlaylistSongListQueryClientSide,
|
||||||
QueueSong,
|
QueueSong,
|
||||||
|
ServerType,
|
||||||
Song,
|
Song,
|
||||||
|
SongListResponse,
|
||||||
SongListSort,
|
SongListSort,
|
||||||
SortOrder,
|
SortOrder,
|
||||||
} from '/@/shared/types/domain-types';
|
} from '/@/shared/types/domain-types';
|
||||||
import { ListDisplayType, ServerType } from '/@/shared/types/types';
|
import { ListDisplayType } from '/@/shared/types/types';
|
||||||
|
|
||||||
interface PlaylistDetailContentProps {
|
interface PlaylistDetailContentProps {
|
||||||
songs?: Song[];
|
songs?: Song[];
|
||||||
|
|
@ -63,7 +63,7 @@ export const PlaylistDetailSongListContent = ({ songs, tableRef }: PlaylistDetai
|
||||||
const currentSong = useCurrentSong();
|
const currentSong = useCurrentSong();
|
||||||
const server = useCurrentServer();
|
const server = useCurrentServer();
|
||||||
const page = usePlaylistDetailStore();
|
const page = usePlaylistDetailStore();
|
||||||
const filters: Partial<PlaylistSongListQuery> = useMemo(() => {
|
const filters: PlaylistSongListQueryClientSide = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
sortBy: page?.table.id[playlistId]?.filter?.sortBy || SongListSort.ID,
|
sortBy: page?.table.id[playlistId]?.filter?.sortBy || SongListSort.ID,
|
||||||
sortOrder: page?.table.id[playlistId]?.filter?.sortOrder || SortOrder.ASC,
|
sortOrder: page?.table.id[playlistId]?.filter?.sortOrder || SortOrder.ASC,
|
||||||
|
|
@ -88,20 +88,6 @@ export const PlaylistDetailSongListContent = ({ songs, tableRef }: PlaylistDetai
|
||||||
|
|
||||||
const isPaginationEnabled = page.display === ListDisplayType.TABLE_PAGINATED;
|
const isPaginationEnabled = page.display === ListDisplayType.TABLE_PAGINATED;
|
||||||
|
|
||||||
const iSClientSide = server?.type === ServerType.SUBSONIC;
|
|
||||||
|
|
||||||
const checkPlaylistList = usePlaylistSongList({
|
|
||||||
options: {
|
|
||||||
enabled: !iSClientSide,
|
|
||||||
},
|
|
||||||
query: {
|
|
||||||
id: playlistId,
|
|
||||||
limit: 1,
|
|
||||||
startIndex: 0,
|
|
||||||
},
|
|
||||||
serverId: server?.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
const columnDefs: ColDef[] = useMemo(
|
const columnDefs: ColDef[] = useMemo(
|
||||||
() => getColumnDefs(page.table.columns, false, 'generic'),
|
() => getColumnDefs(page.table.columns, false, 'generic'),
|
||||||
[page.table.columns],
|
[page.table.columns],
|
||||||
|
|
@ -109,51 +95,9 @@ export const PlaylistDetailSongListContent = ({ songs, tableRef }: PlaylistDetai
|
||||||
|
|
||||||
const onGridReady = useCallback(
|
const onGridReady = useCallback(
|
||||||
(params: GridReadyEvent) => {
|
(params: GridReadyEvent) => {
|
||||||
if (!iSClientSide) {
|
|
||||||
const dataSource: IDatasource = {
|
|
||||||
getRows: async (params) => {
|
|
||||||
const limit = params.endRow - params.startRow;
|
|
||||||
const startIndex = params.startRow;
|
|
||||||
|
|
||||||
const query: PlaylistSongListQuery = {
|
|
||||||
id: playlistId,
|
|
||||||
limit,
|
|
||||||
startIndex,
|
|
||||||
...filters,
|
|
||||||
};
|
|
||||||
|
|
||||||
const queryKey = queryKeys.playlists.songList(
|
|
||||||
server?.id || '',
|
|
||||||
playlistId,
|
|
||||||
query,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!server) return;
|
|
||||||
|
|
||||||
const songsRes = await queryClient.fetchQuery(
|
|
||||||
queryKey,
|
|
||||||
async ({ signal }) =>
|
|
||||||
api.controller.getPlaylistSongList({
|
|
||||||
apiClientProps: {
|
|
||||||
server,
|
|
||||||
signal,
|
|
||||||
},
|
|
||||||
query,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
params.successCallback(
|
|
||||||
songsRes?.items || [],
|
|
||||||
songsRes?.totalRecordCount || 0,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
rowCount: undefined,
|
|
||||||
};
|
|
||||||
params.api.setDatasource(dataSource);
|
|
||||||
}
|
|
||||||
params.api?.ensureIndexVisible(pagination.scrollOffset, 'top');
|
params.api?.ensureIndexVisible(pagination.scrollOffset, 'top');
|
||||||
},
|
},
|
||||||
[filters, iSClientSide, pagination.scrollOffset, playlistId, queryClient, server],
|
[pagination.scrollOffset],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDragEnd = useCallback(
|
const handleDragEnd = useCallback(
|
||||||
|
|
@ -175,12 +119,32 @@ export const PlaylistDetailSongListContent = ({ songs, tableRef }: PlaylistDetai
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
queryClient.setQueryData<SongListResponse>(
|
||||||
queryClient.invalidateQueries({
|
queryKeys.playlists.songList(server?.id || '', playlistId),
|
||||||
queryKey: queryKeys.playlists.songList(server?.id || '', playlistId),
|
(previous) => {
|
||||||
});
|
if (previous?.items) {
|
||||||
e.api.refreshInfiniteCache();
|
const from = e.node.rowIndex!;
|
||||||
}, 200);
|
const to = e.overIndex;
|
||||||
|
|
||||||
|
const item = previous.items[from];
|
||||||
|
const remaining = previous.items.toSpliced(from, 1);
|
||||||
|
remaining.splice(to, 0, item);
|
||||||
|
|
||||||
|
return {
|
||||||
|
error: previous.error,
|
||||||
|
items: remaining,
|
||||||
|
startIndex: previous.startIndex,
|
||||||
|
totalRecordCount: previous.totalRecordCount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return previous;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Nodes have to be redrawn, otherwise the row indexes will be wrong
|
||||||
|
// Maybe it's possible to only redraw necessary rows to not be as expensive?
|
||||||
|
tableRef.current?.api.redrawRows();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error({
|
toast.error({
|
||||||
message: (error as Error).message,
|
message: (error as Error).message,
|
||||||
|
|
@ -189,7 +153,7 @@ export const PlaylistDetailSongListContent = ({ songs, tableRef }: PlaylistDetai
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[playlistId, queryClient, server],
|
[playlistId, queryClient, server, tableRef],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleGridSizeChange = () => {
|
const handleGridSizeChange = () => {
|
||||||
|
|
@ -286,7 +250,9 @@ export const PlaylistDetailSongListContent = ({ songs, tableRef }: PlaylistDetai
|
||||||
const { rowClassRules } = useCurrentSongRowStyles({ tableRef });
|
const { rowClassRules } = useCurrentSongRowStyles({ tableRef });
|
||||||
|
|
||||||
const canDrag =
|
const canDrag =
|
||||||
filters.sortBy === SongListSort.ID && !detailQuery?.data?.rules && !iSClientSide;
|
filters.sortBy === SongListSort.ID &&
|
||||||
|
!detailQuery?.data?.rules &&
|
||||||
|
server?.type !== ServerType.SUBSONIC;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -303,9 +269,6 @@ export const PlaylistDetailSongListContent = ({ songs, tableRef }: PlaylistDetai
|
||||||
status,
|
status,
|
||||||
}}
|
}}
|
||||||
getRowId={(data) => data.data.uniqueId}
|
getRowId={(data) => data.data.uniqueId}
|
||||||
infiniteInitialRowCount={
|
|
||||||
iSClientSide ? undefined : checkPlaylistList.data?.totalRecordCount || 100
|
|
||||||
}
|
|
||||||
// https://github.com/ag-grid/ag-grid/issues/5284
|
// https://github.com/ag-grid/ag-grid/issues/5284
|
||||||
// Key is used to force remount of table when display, rowHeight, or server changes
|
// Key is used to force remount of table when display, rowHeight, or server changes
|
||||||
key={`table-${page.display}-${page.table.rowHeight}-${server?.id}`}
|
key={`table-${page.display}-${page.table.rowHeight}-${server?.id}`}
|
||||||
|
|
@ -326,7 +289,7 @@ export const PlaylistDetailSongListContent = ({ songs, tableRef }: PlaylistDetai
|
||||||
rowData={songs}
|
rowData={songs}
|
||||||
rowDragEntireRow={canDrag}
|
rowDragEntireRow={canDrag}
|
||||||
rowHeight={page.table.rowHeight || 40}
|
rowHeight={page.table.rowHeight || 40}
|
||||||
rowModelType={iSClientSide ? 'clientSide' : 'infinite'}
|
rowModelType="clientSide"
|
||||||
shouldUpdateSong
|
shouldUpdateSong
|
||||||
/>
|
/>
|
||||||
</VirtualGridAutoSizerContainer>
|
</VirtualGridAutoSizerContainer>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||||
|
|
||||||
import { IDatasource } from '@ag-grid-community/core';
|
|
||||||
import { closeAllModals, openModal } from '@mantine/modals';
|
import { closeAllModals, openModal } from '@mantine/modals';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
|
|
@ -9,7 +8,6 @@ import { useTranslation } from 'react-i18next';
|
||||||
import { useNavigate, useParams } from 'react-router';
|
import { useNavigate, useParams } from 'react-router';
|
||||||
|
|
||||||
import i18n from '/@/i18n/i18n';
|
import i18n from '/@/i18n/i18n';
|
||||||
import { api } from '/@/renderer/api';
|
|
||||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||||
import { SONG_TABLE_COLUMNS } from '/@/renderer/components/virtual-table';
|
import { SONG_TABLE_COLUMNS } from '/@/renderer/components/virtual-table';
|
||||||
import { usePlayQueueAdd } from '/@/renderer/features/player';
|
import { usePlayQueueAdd } from '/@/renderer/features/player';
|
||||||
|
|
@ -23,7 +21,6 @@ import { useContainerQuery } from '/@/renderer/hooks';
|
||||||
import { AppRoute } from '/@/renderer/router/routes';
|
import { AppRoute } from '/@/renderer/router/routes';
|
||||||
import {
|
import {
|
||||||
PersistedTableColumn,
|
PersistedTableColumn,
|
||||||
SongListFilter,
|
|
||||||
useCurrentServer,
|
useCurrentServer,
|
||||||
usePlaylistDetailStore,
|
usePlaylistDetailStore,
|
||||||
useSetPlaylistDetailFilters,
|
useSetPlaylistDetailFilters,
|
||||||
|
|
@ -42,7 +39,7 @@ import { Text } from '/@/shared/components/text/text';
|
||||||
import { toast } from '/@/shared/components/toast/toast';
|
import { toast } from '/@/shared/components/toast/toast';
|
||||||
import {
|
import {
|
||||||
LibraryItem,
|
LibraryItem,
|
||||||
PlaylistSongListQuery,
|
PlaylistSongListQueryClientSide,
|
||||||
ServerType,
|
ServerType,
|
||||||
SongListSort,
|
SongListSort,
|
||||||
SortOrder,
|
SortOrder,
|
||||||
|
|
@ -155,7 +152,7 @@ const FILTERS = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
defaultOrder: SortOrder.ASC,
|
defaultOrder: SortOrder.ASC,
|
||||||
name: i18n.t('filter.playCount', { postProcess: 'titleCase' }),
|
name: i18n.t('filter.genre', { postProcess: 'titleCase' }),
|
||||||
value: SongListSort.GENRE,
|
value: SongListSort.GENRE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -240,11 +237,6 @@ const FILTERS = {
|
||||||
name: i18n.t('filter.recentlyAdded', { postProcess: 'titleCase' }),
|
name: i18n.t('filter.recentlyAdded', { postProcess: 'titleCase' }),
|
||||||
value: SongListSort.RECENTLY_ADDED,
|
value: SongListSort.RECENTLY_ADDED,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
defaultOrder: SortOrder.DESC,
|
|
||||||
name: i18n.t('filter.recentlyPlayed', { postProcess: 'titleCase' }),
|
|
||||||
value: SongListSort.RECENTLY_PLAYED,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
defaultOrder: SortOrder.DESC,
|
defaultOrder: SortOrder.DESC,
|
||||||
name: i18n.t('filter.releaseYear', { postProcess: 'titleCase' }),
|
name: i18n.t('filter.releaseYear', { postProcess: 'titleCase' }),
|
||||||
|
|
@ -270,7 +262,7 @@ export const PlaylistDetailSongListHeaderFilters = ({
|
||||||
const setPage = useSetPlaylistStore();
|
const setPage = useSetPlaylistStore();
|
||||||
const setFilter = useSetPlaylistDetailFilters();
|
const setFilter = useSetPlaylistDetailFilters();
|
||||||
const page = usePlaylistDetailStore();
|
const page = usePlaylistDetailStore();
|
||||||
const filters: Partial<PlaylistSongListQuery> = {
|
const filters: Partial<PlaylistSongListQueryClientSide> = {
|
||||||
sortBy: page?.table.id[playlistId]?.filter?.sortBy || SongListSort.ID,
|
sortBy: page?.table.id[playlistId]?.filter?.sortBy || SongListSort.ID,
|
||||||
sortOrder: page?.table.id[playlistId]?.filter?.sortOrder || SortOrder.ASC,
|
sortOrder: page?.table.id[playlistId]?.filter?.sortOrder || SortOrder.ASC,
|
||||||
};
|
};
|
||||||
|
|
@ -297,68 +289,18 @@ export const PlaylistDetailSongListHeaderFilters = ({
|
||||||
|
|
||||||
const debouncedHandleItemSize = debounce(handleItemSize, 20);
|
const debouncedHandleItemSize = debounce(handleItemSize, 20);
|
||||||
|
|
||||||
const handleFilterChange = useCallback(
|
const handleFilterChange = useCallback(async () => {
|
||||||
async (filters: SongListFilter) => {
|
tableRef.current?.api.redrawRows();
|
||||||
if (server?.type !== ServerType.SUBSONIC) {
|
tableRef.current?.api.ensureIndexVisible(0, 'top');
|
||||||
const dataSource: IDatasource = {
|
|
||||||
getRows: async (params) => {
|
|
||||||
const limit = params.endRow - params.startRow;
|
|
||||||
const startIndex = params.startRow;
|
|
||||||
|
|
||||||
const queryKey = queryKeys.playlists.songList(
|
if (page.display === ListDisplayType.TABLE_PAGINATED) {
|
||||||
server?.id || '',
|
setPagination({ data: { currentPage: 0 } });
|
||||||
playlistId,
|
}
|
||||||
{
|
}, [tableRef, page.display, setPagination]);
|
||||||
id: playlistId,
|
|
||||||
limit,
|
|
||||||
startIndex,
|
|
||||||
...filters,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const songsRes = await queryClient.fetchQuery(
|
|
||||||
queryKey,
|
|
||||||
async ({ signal }) =>
|
|
||||||
api.controller.getPlaylistSongList({
|
|
||||||
apiClientProps: {
|
|
||||||
server,
|
|
||||||
signal,
|
|
||||||
},
|
|
||||||
query: {
|
|
||||||
id: playlistId,
|
|
||||||
limit,
|
|
||||||
startIndex,
|
|
||||||
...filters,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
{ 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');
|
|
||||||
} else {
|
|
||||||
tableRef.current?.api.redrawRows();
|
|
||||||
tableRef.current?.api.ensureIndexVisible(0, 'top');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page.display === ListDisplayType.TABLE_PAGINATED) {
|
|
||||||
setPagination({ data: { currentPage: 0 } });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[tableRef, page.display, server, playlistId, queryClient, setPagination],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleRefresh = () => {
|
const handleRefresh = () => {
|
||||||
queryClient.invalidateQueries(queryKeys.albums.list(server?.id || ''));
|
queryClient.invalidateQueries(queryKeys.playlists.songList(server?.id || '', playlistId));
|
||||||
handleFilterChange({ ...page?.table.id[playlistId].filter, ...filters });
|
handleFilterChange();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSetSortBy = useCallback(
|
const handleSetSortBy = useCallback(
|
||||||
|
|
@ -369,20 +311,20 @@ export const PlaylistDetailSongListHeaderFilters = ({
|
||||||
(f) => f.value === e.currentTarget.value,
|
(f) => f.value === e.currentTarget.value,
|
||||||
)?.defaultOrder;
|
)?.defaultOrder;
|
||||||
|
|
||||||
const updatedFilters = setFilter(playlistId, {
|
setFilter(playlistId, {
|
||||||
sortBy: e.currentTarget.value as SongListSort,
|
sortBy: e.currentTarget.value as SongListSort,
|
||||||
sortOrder: sortOrder || SortOrder.ASC,
|
sortOrder: sortOrder || SortOrder.ASC,
|
||||||
});
|
});
|
||||||
|
|
||||||
handleFilterChange(updatedFilters);
|
handleFilterChange();
|
||||||
},
|
},
|
||||||
[handleFilterChange, playlistId, server?.type, setFilter],
|
[handleFilterChange, playlistId, server?.type, setFilter],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleToggleSortOrder = useCallback(() => {
|
const handleToggleSortOrder = useCallback(() => {
|
||||||
const newSortOrder = filters.sortOrder === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC;
|
const newSortOrder = filters.sortOrder === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC;
|
||||||
const updatedFilters = setFilter(playlistId, { sortOrder: newSortOrder });
|
setFilter(playlistId, { sortOrder: newSortOrder });
|
||||||
handleFilterChange(updatedFilters);
|
handleFilterChange();
|
||||||
}, [filters.sortOrder, handleFilterChange, playlistId, setFilter]);
|
}, [filters.sortOrder, handleFilterChange, playlistId, setFilter]);
|
||||||
|
|
||||||
const handleSetViewType = useCallback(
|
const handleSetViewType = useCallback(
|
||||||
|
|
@ -432,6 +374,7 @@ export const PlaylistDetailSongListHeaderFilters = ({
|
||||||
handlePlayQueueAdd?.({
|
handlePlayQueueAdd?.({
|
||||||
byItemType: { id: [playlistId], type: LibraryItem.PLAYLIST },
|
byItemType: { id: [playlistId], type: LibraryItem.PLAYLIST },
|
||||||
playType,
|
playType,
|
||||||
|
query: filters,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,17 @@ import { usePlayQueueAdd } from '/@/renderer/features/player';
|
||||||
import { PlaylistDetailSongListHeaderFilters } from '/@/renderer/features/playlists/components/playlist-detail-song-list-header-filters';
|
import { PlaylistDetailSongListHeaderFilters } from '/@/renderer/features/playlists/components/playlist-detail-song-list-header-filters';
|
||||||
import { usePlaylistDetail } from '/@/renderer/features/playlists/queries/playlist-detail-query';
|
import { usePlaylistDetail } from '/@/renderer/features/playlists/queries/playlist-detail-query';
|
||||||
import { FilterBar, LibraryHeaderBar } from '/@/renderer/features/shared';
|
import { FilterBar, LibraryHeaderBar } from '/@/renderer/features/shared';
|
||||||
import { useCurrentServer } from '/@/renderer/store';
|
import { useCurrentServer, usePlaylistDetailStore } from '/@/renderer/store';
|
||||||
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
|
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
|
||||||
import { Badge } from '/@/shared/components/badge/badge';
|
import { Badge } from '/@/shared/components/badge/badge';
|
||||||
import { SpinnerIcon } from '/@/shared/components/spinner/spinner';
|
import { SpinnerIcon } from '/@/shared/components/spinner/spinner';
|
||||||
import { Stack } from '/@/shared/components/stack/stack';
|
import { Stack } from '/@/shared/components/stack/stack';
|
||||||
import { LibraryItem } from '/@/shared/types/domain-types';
|
import {
|
||||||
|
LibraryItem,
|
||||||
|
PlaylistSongListQueryClientSide,
|
||||||
|
SongListSort,
|
||||||
|
SortOrder,
|
||||||
|
} from '/@/shared/types/domain-types';
|
||||||
import { Play } from '/@/shared/types/types';
|
import { Play } from '/@/shared/types/types';
|
||||||
|
|
||||||
interface PlaylistDetailHeaderProps {
|
interface PlaylistDetailHeaderProps {
|
||||||
|
|
@ -33,11 +38,17 @@ export const PlaylistDetailSongListHeader = ({
|
||||||
const server = useCurrentServer();
|
const server = useCurrentServer();
|
||||||
const detailQuery = usePlaylistDetail({ query: { id: playlistId }, serverId: server?.id });
|
const detailQuery = usePlaylistDetail({ query: { id: playlistId }, serverId: server?.id });
|
||||||
const handlePlayQueueAdd = usePlayQueueAdd();
|
const handlePlayQueueAdd = usePlayQueueAdd();
|
||||||
|
const page = usePlaylistDetailStore();
|
||||||
|
const filters: Partial<PlaylistSongListQueryClientSide> = {
|
||||||
|
sortBy: page?.table.id[playlistId]?.filter?.sortBy || SongListSort.ID,
|
||||||
|
sortOrder: page?.table.id[playlistId]?.filter?.sortOrder || SortOrder.ASC,
|
||||||
|
};
|
||||||
|
|
||||||
const handlePlay = async (playType: Play) => {
|
const handlePlay = async (playType: Play) => {
|
||||||
handlePlayQueueAdd?.({
|
handlePlayQueueAdd?.({
|
||||||
byItemType: { id: [playlistId], type: LibraryItem.PLAYLIST },
|
byItemType: { id: [playlistId], type: LibraryItem.PLAYLIST },
|
||||||
playType,
|
playType,
|
||||||
|
query: filters,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,6 @@ export const useAddToPlaylist = (args: MutationHookArgs) => {
|
||||||
|
|
||||||
queryClient.invalidateQueries(queryKeys.playlists.list(serverId), { exact: false });
|
queryClient.invalidateQueries(queryKeys.playlists.list(serverId), { exact: false });
|
||||||
queryClient.invalidateQueries(queryKeys.playlists.detail(serverId, variables.query.id));
|
queryClient.invalidateQueries(queryKeys.playlists.detail(serverId, variables.query.id));
|
||||||
queryClient.invalidateQueries(
|
|
||||||
queryKeys.playlists.detailSongList(serverId, variables.query.id),
|
|
||||||
);
|
|
||||||
queryClient.invalidateQueries(
|
queryClient.invalidateQueries(
|
||||||
queryKeys.playlists.songList(serverId, variables.query.id),
|
queryKeys.playlists.songList(serverId, variables.query.id),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ export const useRemoveFromPlaylist = (options?: MutationOptions) => {
|
||||||
queryClient.invalidateQueries(queryKeys.playlists.list(serverId), { exact: false });
|
queryClient.invalidateQueries(queryKeys.playlists.list(serverId), { exact: false });
|
||||||
queryClient.invalidateQueries(queryKeys.playlists.detail(serverId, variables.query.id));
|
queryClient.invalidateQueries(queryKeys.playlists.detail(serverId, variables.query.id));
|
||||||
queryClient.invalidateQueries(
|
queryClient.invalidateQueries(
|
||||||
queryKeys.playlists.detailSongList(serverId, variables.query.id),
|
queryKeys.playlists.songList(serverId, variables.query.id),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
...options,
|
...options,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export const usePlaylistSongList = (args: QueryHookArgs<PlaylistSongListQuery>)
|
||||||
query,
|
query,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
queryKey: queryKeys.playlists.songList(server?.id || '', query.id, query),
|
queryKey: queryKeys.playlists.songList(server?.id || '', query.id),
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/li
|
||||||
|
|
||||||
import { closeAllModals, openModal } from '@mantine/modals';
|
import { closeAllModals, openModal } from '@mantine/modals';
|
||||||
import { motion } from 'motion/react';
|
import { motion } from 'motion/react';
|
||||||
import { useRef, useState } from 'react';
|
import { useMemo, useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { generatePath, useNavigate, useParams } from 'react-router';
|
import { generatePath, useNavigate, useParams } from 'react-router';
|
||||||
|
|
||||||
|
|
@ -22,12 +22,7 @@ import { Box } from '/@/shared/components/box/box';
|
||||||
import { Group } from '/@/shared/components/group/group';
|
import { Group } from '/@/shared/components/group/group';
|
||||||
import { Text } from '/@/shared/components/text/text';
|
import { Text } from '/@/shared/components/text/text';
|
||||||
import { toast } from '/@/shared/components/toast/toast';
|
import { toast } from '/@/shared/components/toast/toast';
|
||||||
import {
|
import { ServerType, SongListSort, SortOrder, sortSongList } from '/@/shared/types/domain-types';
|
||||||
PlaylistSongListQuery,
|
|
||||||
ServerType,
|
|
||||||
SongListSort,
|
|
||||||
SortOrder,
|
|
||||||
} from '/@/shared/types/domain-types';
|
|
||||||
|
|
||||||
const PlaylistDetailSongListRoute = () => {
|
const PlaylistDetailSongListRoute = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
@ -148,22 +143,25 @@ const PlaylistDetailSongListRoute = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const page = usePlaylistDetailStore();
|
const page = usePlaylistDetailStore();
|
||||||
const filters: Partial<PlaylistSongListQuery> = {
|
|
||||||
sortBy: page?.table.id[playlistId]?.filter?.sortBy || SongListSort.ID,
|
|
||||||
sortOrder: page?.table.id[playlistId]?.filter?.sortOrder || SortOrder.ASC,
|
|
||||||
};
|
|
||||||
|
|
||||||
const itemCountCheck = usePlaylistSongList({
|
const playlistSongs = usePlaylistSongList({
|
||||||
query: {
|
query: {
|
||||||
id: playlistId,
|
id: playlistId,
|
||||||
limit: 1,
|
|
||||||
startIndex: 0,
|
|
||||||
...filters,
|
|
||||||
},
|
},
|
||||||
serverId: server?.id,
|
serverId: server?.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
const itemCount = itemCountCheck.data?.totalRecordCount || itemCountCheck.data?.items.length;
|
const itemCount = playlistSongs.data?.totalRecordCount ?? undefined;
|
||||||
|
|
||||||
|
const filterSortedSongs = useMemo(() => {
|
||||||
|
if (playlistSongs.data?.items) {
|
||||||
|
const sortBy = page?.table.id[playlistId]?.filter?.sortBy || SongListSort.ID;
|
||||||
|
const sortOrder = page?.table.id[playlistId]?.filter?.sortOrder || SortOrder.ASC;
|
||||||
|
return sortSongList(playlistSongs.data?.items, sortBy, sortOrder);
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}, [playlistSongs.data?.items, page?.table.id, playlistId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatedPage key={`playlist-detail-songList-${playlistId}`}>
|
<AnimatedPage key={`playlist-detail-songList-${playlistId}`}>
|
||||||
|
|
@ -203,12 +201,7 @@ const PlaylistDetailSongListRoute = () => {
|
||||||
</Box>
|
</Box>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
<PlaylistDetailSongListContent
|
<PlaylistDetailSongListContent songs={filterSortedSongs} tableRef={tableRef} />
|
||||||
songs={
|
|
||||||
server?.type === ServerType.SUBSONIC ? itemCountCheck.data?.items : undefined
|
|
||||||
}
|
|
||||||
tableRef={tableRef}
|
|
||||||
/>
|
|
||||||
</AnimatedPage>
|
</AnimatedPage>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -988,10 +988,11 @@ export type PlaylistSongListArgs = BaseEndpointArgs & { query: PlaylistSongListQ
|
||||||
|
|
||||||
export type PlaylistSongListQuery = {
|
export type PlaylistSongListQuery = {
|
||||||
id: string;
|
id: string;
|
||||||
limit?: number;
|
};
|
||||||
|
|
||||||
|
export type PlaylistSongListQueryClientSide = {
|
||||||
sortBy?: SongListSort;
|
sortBy?: SongListSort;
|
||||||
sortOrder?: SortOrder;
|
sortOrder?: SortOrder;
|
||||||
startIndex: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Playlist Songs
|
// Playlist Songs
|
||||||
|
|
@ -1400,7 +1401,7 @@ export const sortSongList = (songs: QueueSong[], sortBy: SongListSort, sortOrder
|
||||||
case SongListSort.ALBUM_ARTIST:
|
case SongListSort.ALBUM_ARTIST:
|
||||||
results = orderBy(
|
results = orderBy(
|
||||||
results,
|
results,
|
||||||
['albumArtist', (v) => v.album?.toLowerCase(), 'discNumber', 'trackNumber'],
|
[(v) => v.albumArtists[0]?.name.toLowerCase(), 'discNumber', 'trackNumber'],
|
||||||
[order, order, 'asc', 'asc'],
|
[order, order, 'asc', 'asc'],
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
@ -1408,11 +1409,23 @@ export const sortSongList = (songs: QueueSong[], sortBy: SongListSort, sortOrder
|
||||||
case SongListSort.ARTIST:
|
case SongListSort.ARTIST:
|
||||||
results = orderBy(
|
results = orderBy(
|
||||||
results,
|
results,
|
||||||
['artist', (v) => v.album?.toLowerCase(), 'discNumber', 'trackNumber'],
|
[(v) => v.artistName?.toLowerCase(), 'discNumber', 'trackNumber'],
|
||||||
[order, order, 'asc', 'asc'],
|
[order, order, 'asc', 'asc'],
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SongListSort.BPM:
|
||||||
|
results = orderBy(results, ['bpm'], [order]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SongListSort.CHANNELS:
|
||||||
|
results = orderBy(results, ['channels'], [order]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SongListSort.COMMENT:
|
||||||
|
results = orderBy(results, ['comment'], [order]);
|
||||||
|
break;
|
||||||
|
|
||||||
case SongListSort.DURATION:
|
case SongListSort.DURATION:
|
||||||
results = orderBy(results, ['duration'], [order]);
|
results = orderBy(results, ['duration'], [order]);
|
||||||
break;
|
break;
|
||||||
|
|
@ -1425,7 +1438,7 @@ export const sortSongList = (songs: QueueSong[], sortBy: SongListSort, sortOrder
|
||||||
results = orderBy(
|
results = orderBy(
|
||||||
results,
|
results,
|
||||||
[
|
[
|
||||||
(v) => v.genres?.[0].name.toLowerCase(),
|
(v) => v.genres?.[0]?.name.toLowerCase(),
|
||||||
(v) => v.album?.toLowerCase(),
|
(v) => v.album?.toLowerCase(),
|
||||||
'discNumber',
|
'discNumber',
|
||||||
'trackNumber',
|
'trackNumber',
|
||||||
|
|
@ -1457,13 +1470,21 @@ export const sortSongList = (songs: QueueSong[], sortBy: SongListSort, sortOrder
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SongListSort.RECENTLY_ADDED:
|
case SongListSort.RECENTLY_ADDED:
|
||||||
results = orderBy(results, ['created'], [order]);
|
results = orderBy(results, ['createdAt'], [order]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SongListSort.RECENTLY_PLAYED:
|
||||||
|
results = orderBy(results, ['lastPlayedAt'], [order]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SongListSort.RELEASE_DATE:
|
||||||
|
results = orderBy(results, ['releaseDate'], [order]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SongListSort.YEAR:
|
case SongListSort.YEAR:
|
||||||
results = orderBy(
|
results = orderBy(
|
||||||
results,
|
results,
|
||||||
['year', (v) => v.album?.toLowerCase(), 'discNumber', 'track'],
|
['releaseYear', (v) => v.album?.toLowerCase(), 'discNumber', 'track'],
|
||||||
[order, 'asc', 'asc', 'asc'],
|
[order, 'asc', 'asc', 'asc'],
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue