mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
temp
This commit is contained in:
+3
-1
@@ -1,5 +1,7 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
'postcss-preset-mantine': {},
|
||||
'postcss-preset-mantine': {
|
||||
mixins: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -9,9 +9,7 @@ import { getFeatures, hasFeature, VersionInfo } from '/@/shared/api/utils';
|
||||
import { albumListSortMap } from '/@/shared/types/domain/album-domain-types';
|
||||
import { ControllerEndpoint } from '/@/shared/types/domain/api-domain-types';
|
||||
import { albumArtistListSortMap } from '/@/shared/types/domain/artist-domain-types';
|
||||
import { genreListSortMap } from '/@/shared/types/domain/genre-domain-types';
|
||||
import { Played } from '/@/shared/types/domain/player-domain-types';
|
||||
import { playlistListSortMap } from '/@/shared/types/domain/playlist-domain-types';
|
||||
import { ServerFeature } from '/@/shared/types/domain/server-domain-types';
|
||||
import { LibraryItem, sortOrderMap } from '/@/shared/types/domain/shared-domain-types';
|
||||
import { Song, songListSortMap } from '/@/shared/types/domain/song-domain-types';
|
||||
|
||||
@@ -11,9 +11,7 @@ import { albumListSortMap } from '/@/shared/types/domain/album-domain-types';
|
||||
import { ControllerEndpoint } from '/@/shared/types/domain/api-domain-types';
|
||||
import { albumArtistListSortMap } from '/@/shared/types/domain/artist-domain-types';
|
||||
import { AuthenticationResponse } from '/@/shared/types/domain/auth-domain-types';
|
||||
import { genreListSortMap } from '/@/shared/types/domain/genre-domain-types';
|
||||
import {
|
||||
playlistListSortMap,
|
||||
PlaylistSongListRequest,
|
||||
PlaylistSongListResponse,
|
||||
} from '/@/shared/types/domain/playlist-domain-types';
|
||||
@@ -24,7 +22,6 @@ import {
|
||||
} from '/@/shared/types/domain/server-domain-types';
|
||||
import { sortOrderMap } from '/@/shared/types/domain/shared-domain-types';
|
||||
import { Song, songListSortMap } from '/@/shared/types/domain/song-domain-types';
|
||||
import { userListSortMap } from '/@/shared/types/domain/user-domain-types';
|
||||
|
||||
const VERSION_INFO: VersionInfo = [
|
||||
['0.55.0', { [ServerFeature.BFR]: [1] }],
|
||||
|
||||
@@ -17,8 +17,6 @@ import {
|
||||
import { AlbumListSort, sortAlbumList } from '/@/shared/types/domain/album-domain-types';
|
||||
import { ControllerEndpoint } from '/@/shared/types/domain/api-domain-types';
|
||||
import { sortAlbumArtistList } from '/@/shared/types/domain/artist-domain-types';
|
||||
import { GenreListSort } from '/@/shared/types/domain/genre-domain-types';
|
||||
import { PlaylistListSort } from '/@/shared/types/domain/playlist-domain-types';
|
||||
import { ServerFeatures } from '/@/shared/types/domain/server-domain-types';
|
||||
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||
import { Song, sortSongList } from '/@/shared/types/domain/song-domain-types';
|
||||
|
||||
@@ -24,8 +24,8 @@ import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { PersistedTableColumn, useListStoreActions } from '/@/renderer/store';
|
||||
import { ListKey, useListStoreByKey } from '/@/renderer/store/list.store';
|
||||
import {
|
||||
BasePaginatedResponse,
|
||||
BasePaginatedQuery,
|
||||
BasePaginatedResponse,
|
||||
} from '/@/shared/types/adapter/api-controller-types';
|
||||
import { ServerListItem } from '/@/shared/types/domain/server-domain-types';
|
||||
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { queryOptions, UseQueryOptions } from '@tanstack/react-query';
|
||||
|
||||
import { api } from '/@/renderer/api/api-controller';
|
||||
import { AlbumListRequest } from '/@/shared/types/domain/album-domain-types';
|
||||
|
||||
export const getAlbumListQueryKey = (serverId: string, request?: AlbumListRequest) => {
|
||||
if (!request) {
|
||||
return [serverId, 'albums'];
|
||||
}
|
||||
|
||||
return [serverId, 'albums', request];
|
||||
};
|
||||
|
||||
export const getInfiniteAlbumListQueryKey = (serverId: string, request?: AlbumListRequest) => {
|
||||
if (!request) {
|
||||
return [serverId, 'albums', 'infinite'];
|
||||
}
|
||||
|
||||
return [serverId, 'albums', 'infinite', request];
|
||||
};
|
||||
|
||||
export const getAlbumList = async (serverId: string, request: AlbumListRequest) => {
|
||||
const [error, response] = await api.controller[serverId]!.album.getList!({
|
||||
query: request.query,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
export const getAlbumListQuery = (
|
||||
serverId: string,
|
||||
request: AlbumListRequest,
|
||||
options?: UseQueryOptions,
|
||||
) => {
|
||||
return queryOptions({
|
||||
enabled: !!serverId,
|
||||
queryFn: () => getAlbumList(serverId, request),
|
||||
queryKey: getAlbumListQueryKey(serverId, request),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import type { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { AlbumDetailQuery } from '/@/shared/types/domain/album-domain-types';
|
||||
|
||||
export const useAlbumDetail = (args: QueryHookArgs<AlbumDetailQuery>) => {
|
||||
export const useAlbumDetail = (args: RQueryHookArgs<AlbumDetailQuery>) => {
|
||||
const { options, query, serverId } = args;
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import type { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { AlbumListQuery } from '/@/shared/types/domain/album-domain-types';
|
||||
|
||||
export const useAlbumListCount = (args: QueryHookArgs<AlbumListQuery>) => {
|
||||
export const useAlbumListCount = (args: RQueryHookArgs<AlbumListQuery>) => {
|
||||
const { options, query, serverId } = args;
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import type { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
|
||||
import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
|
||||
|
||||
@@ -8,7 +8,7 @@ import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { AlbumListQuery, AlbumListResponse } from '/@/shared/types/domain/album-domain-types';
|
||||
|
||||
export const useAlbumList = (args: QueryHookArgs<AlbumListQuery>) => {
|
||||
export const useAlbumList = (args: RQueryHookArgs<AlbumListQuery>) => {
|
||||
const { options, query, serverId } = args;
|
||||
const server = useServerById(serverId);
|
||||
|
||||
@@ -33,7 +33,7 @@ export const useAlbumList = (args: QueryHookArgs<AlbumListQuery>) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const useAlbumListInfinite = (args: QueryHookArgs<AlbumListQuery>) => {
|
||||
export const useAlbumListInfinite = (args: RQueryHookArgs<AlbumListQuery>) => {
|
||||
const { options, query, serverId } = args;
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@ import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { AlbumArtistDetailQuery } from '/@/shared/types/domain/artist-domain-types';
|
||||
|
||||
export const useAlbumArtistDetail = (args: QueryHookArgs<AlbumArtistDetailQuery>) => {
|
||||
export const useAlbumArtistDetail = (args: RQueryHookArgs<AlbumArtistDetailQuery>) => {
|
||||
const { options, query, serverId } = args || {};
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@ import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { AlbumArtistListQuery } from '/@/shared/types/domain/artist-domain-types';
|
||||
|
||||
export const useAlbumArtistListCount = (args: QueryHookArgs<AlbumArtistListQuery>) => {
|
||||
export const useAlbumArtistListCount = (args: RQueryHookArgs<AlbumArtistListQuery>) => {
|
||||
const { options, query, serverId } = args;
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@ import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { AlbumArtistListQuery } from '/@/shared/types/domain/artist-domain-types';
|
||||
|
||||
export const useAlbumArtistList = (args: QueryHookArgs<AlbumArtistListQuery>) => {
|
||||
export const useAlbumArtistList = (args: RQueryHookArgs<AlbumArtistListQuery>) => {
|
||||
const { options, query, serverId } = args || {};
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@ import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { AlbumArtistDetailQuery } from '/@/shared/types/domain/artist-domain-types';
|
||||
|
||||
export const useAlbumArtistInfo = (args: QueryHookArgs<AlbumArtistDetailQuery>) => {
|
||||
export const useAlbumArtistInfo = (args: RQueryHookArgs<AlbumArtistDetailQuery>) => {
|
||||
const { options, query, serverId } = args || {};
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@ import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { ArtistListQuery } from '/@/shared/types/domain/artist-domain-types';
|
||||
|
||||
export const useArtistListCount = (args: QueryHookArgs<ArtistListQuery>) => {
|
||||
export const useArtistListCount = (args: RQueryHookArgs<ArtistListQuery>) => {
|
||||
const { options, query, serverId } = args;
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@ import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
|
||||
export const useRoles = (args: QueryHookArgs<object>) => {
|
||||
export const useRoles = (args: RQueryHookArgs<object>) => {
|
||||
const { options, serverId } = args;
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import type { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { TopSongListQuery } from '/@/shared/types/domain/song-domain-types';
|
||||
|
||||
export const useTopSongsList = (args: QueryHookArgs<TopSongListQuery>) => {
|
||||
export const useTopSongsList = (args: RQueryHookArgs<TopSongListQuery>) => {
|
||||
const { options, query, serverId } = args || {};
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import type { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { GenreListQuery } from '/@/shared/types/domain/genre-domain-types';
|
||||
|
||||
export const useGenreList = (args: QueryHookArgs<GenreListQuery>) => {
|
||||
export const useGenreList = (args: RQueryHookArgs<GenreListQuery>) => {
|
||||
const { options, query, serverId } = args || {};
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { AlbumListQuery, AlbumListSort } from '/@/shared/types/domain/album-domain-types';
|
||||
import { ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||
|
||||
export const useRecentlyPlayed = (args: QueryHookArgs<Partial<AlbumListQuery>>) => {
|
||||
export const useRecentlyPlayed = (args: RQueryHookArgs<Partial<AlbumListQuery>>) => {
|
||||
const { options, query, serverId } = args;
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -4,26 +4,21 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { FeatureCarousel } from '/@/renderer/components/feature-carousel/feature-carousel';
|
||||
import { MemoizedSwiperGridCarousel } from '/@/renderer/components/grid-carousel/grid-carousel';
|
||||
import { NativeScrollArea } from '/@/renderer/components/native-scroll-area/native-scroll-area';
|
||||
import { useAlbumList } from '/@/renderer/features/albums';
|
||||
import { useRecentlyPlayed } from '/@/renderer/features/home/queries/recently-played-query';
|
||||
import { AnimatedPage, LibraryHeaderBar } from '/@/renderer/features/shared';
|
||||
import { AlbumInfiniteCarousel } from '/@/renderer/features/shared/components/infinite-album-carousel/infinite-album-carousel';
|
||||
import { useSongList } from '/@/renderer/features/songs';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import {
|
||||
HomeItem,
|
||||
useCurrentServer,
|
||||
useGeneralSettings,
|
||||
useWindowSettings,
|
||||
} from '/@/renderer/store';
|
||||
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { Icon } from '/@/shared/components/icon/icon';
|
||||
import { Spinner } from '/@/shared/components/spinner/spinner';
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
import { TextTitle } from '/@/shared/components/text-title/text-title';
|
||||
import { AlbumListSort } from '/@/shared/types/domain/album-domain-types';
|
||||
import { AlbumListSort, AlbumListSortOptions } from '/@/shared/types/domain/album-domain-types';
|
||||
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||
import { SongListSort } from '/@/shared/types/domain/song-domain-types';
|
||||
@@ -40,15 +35,15 @@ const HomeRoute = () => {
|
||||
|
||||
const feature = useAlbumList({
|
||||
options: {
|
||||
gcTime: 1000 * 60,
|
||||
enabled: homeFeature,
|
||||
gcTime: 1000 * 60,
|
||||
staleTime: 1000 * 60,
|
||||
},
|
||||
query: {
|
||||
limit: 20,
|
||||
offset: 0,
|
||||
sortBy: AlbumListSort.RANDOM,
|
||||
sortOrder: ListSortOrder.DESC,
|
||||
offset: 0,
|
||||
},
|
||||
serverId: server?.id,
|
||||
});
|
||||
@@ -63,9 +58,9 @@ const HomeRoute = () => {
|
||||
},
|
||||
query: {
|
||||
limit: itemsPerPage,
|
||||
offset: 0,
|
||||
sortBy: AlbumListSort.RANDOM,
|
||||
sortOrder: ListSortOrder.ASC,
|
||||
offset: 0,
|
||||
},
|
||||
serverId: server?.id,
|
||||
});
|
||||
@@ -76,9 +71,9 @@ const HomeRoute = () => {
|
||||
},
|
||||
query: {
|
||||
limit: itemsPerPage,
|
||||
offset: 0,
|
||||
sortBy: AlbumListSort.RECENTLY_PLAYED,
|
||||
sortOrder: ListSortOrder.DESC,
|
||||
offset: 0,
|
||||
},
|
||||
serverId: server?.id,
|
||||
});
|
||||
@@ -89,9 +84,9 @@ const HomeRoute = () => {
|
||||
},
|
||||
query: {
|
||||
limit: itemsPerPage,
|
||||
offset: 0,
|
||||
sortBy: AlbumListSort.RECENTLY_ADDED,
|
||||
sortOrder: ListSortOrder.DESC,
|
||||
offset: 0,
|
||||
},
|
||||
serverId: server?.id,
|
||||
});
|
||||
@@ -103,9 +98,9 @@ const HomeRoute = () => {
|
||||
},
|
||||
query: {
|
||||
limit: itemsPerPage,
|
||||
offset: 0,
|
||||
sortBy: AlbumListSort.PLAY_COUNT,
|
||||
sortOrder: ListSortOrder.DESC,
|
||||
offset: 0,
|
||||
},
|
||||
serverId: server?.id,
|
||||
});
|
||||
@@ -118,9 +113,9 @@ const HomeRoute = () => {
|
||||
},
|
||||
query: {
|
||||
limit: itemsPerPage,
|
||||
offset: 0,
|
||||
sortBy: SongListSort.PLAY_COUNT,
|
||||
sortOrder: ListSortOrder.DESC,
|
||||
offset: 0,
|
||||
},
|
||||
serverId: server?.id,
|
||||
},
|
||||
@@ -253,7 +248,15 @@ const HomeRoute = () => {
|
||||
px="2rem"
|
||||
>
|
||||
{homeFeature && <FeatureCarousel data={featureItemsWithImage} />}
|
||||
{sortedCarousel.map((carousel) => (
|
||||
|
||||
<AlbumInfiniteCarousel
|
||||
serverId={server?.id ?? ''}
|
||||
sortBy={AlbumListSortOptions.NAME}
|
||||
sortOrder={ListSortOrder.ASC}
|
||||
title={t('page.home.explore', { postProcess: 'sentenceCase' })}
|
||||
/>
|
||||
|
||||
{/* {sortedCarousel.map((carousel) => (
|
||||
<MemoizedSwiperGridCarousel
|
||||
cardRows={[
|
||||
{
|
||||
@@ -317,7 +320,7 @@ const HomeRoute = () => {
|
||||
}}
|
||||
uniqueId={carousel.uniqueId}
|
||||
/>
|
||||
))}
|
||||
))} */}
|
||||
</Stack>
|
||||
</NativeScrollArea>
|
||||
</AnimatedPage>
|
||||
|
||||
@@ -3,7 +3,7 @@ import isElectron from 'is-electron';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById, useLyricsSettings } from '/@/renderer/store';
|
||||
import { hasFeature } from '/@/shared/api/utils';
|
||||
import {
|
||||
@@ -61,7 +61,7 @@ const formatLyrics = (lyrics: string) => {
|
||||
};
|
||||
|
||||
export const useServerLyrics = (
|
||||
args: QueryHookArgs<LyricsQuery>,
|
||||
args: RQueryHookArgs<LyricsQuery>,
|
||||
): UseQueryResult<null | string> => {
|
||||
const { query, serverId } = args;
|
||||
const server = useServerById(serverId);
|
||||
@@ -81,7 +81,7 @@ export const useServerLyrics = (
|
||||
};
|
||||
|
||||
export const useSongLyricsBySong = (
|
||||
args: QueryHookArgs<LyricsQuery>,
|
||||
args: RQueryHookArgs<LyricsQuery>,
|
||||
song: QueueSong | undefined,
|
||||
): UseQueryResult<FullLyricsMetadata | StructuredLyric[]> => {
|
||||
const { query } = args;
|
||||
@@ -170,7 +170,7 @@ export const useSongLyricsBySong = (
|
||||
};
|
||||
|
||||
export const useSongLyricsByRemoteId = (
|
||||
args: QueryHookArgs<Partial<LyricGetQuery>>,
|
||||
args: RQueryHookArgs<Partial<LyricGetQuery>>,
|
||||
): UseQueryResult<null | string> => {
|
||||
const queryClient = useQueryClient();
|
||||
const { query, serverId } = args;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useQuery } from '@tanstack/react-query';
|
||||
import isElectron from 'is-electron';
|
||||
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import {
|
||||
InternetProviderLyricSearchResponse,
|
||||
LyricSearchQuery,
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
|
||||
const lyricsIpc = isElectron() ? window.api.lyrics : null;
|
||||
|
||||
export const useLyricSearch = (args: Omit<QueryHookArgs<LyricSearchQuery>, 'serverId'>) => {
|
||||
export const useLyricSearch = (args: Omit<RQueryHookArgs<LyricSearchQuery>, 'serverId'>) => {
|
||||
const { options, query } = args;
|
||||
|
||||
return useQuery<Record<LyricSource, InternetProviderLyricSearchResponse[]>>({
|
||||
|
||||
@@ -19,12 +19,12 @@ import { Icon } from '/@/shared/components/icon/icon';
|
||||
import { NumberInput } from '/@/shared/components/number-input/number-input';
|
||||
import { Select } from '/@/shared/components/select/select';
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
import { GenreListResponse, GenreListSort } from '/@/shared/types/domain/genre-domain-types';
|
||||
import { GenreListResponse } from '/@/shared/types/domain/genre-domain-types';
|
||||
import { Played } from '/@/shared/types/domain/player-domain-types';
|
||||
import { ServerListItem, ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||
import { ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||
import { RandomSongListQuery } from '/@/shared/types/domain/song-domain-types';
|
||||
import { Play, PlayQueueAddOptions } from '/@/shared/types/types';
|
||||
import { ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||
|
||||
interface ShuffleAllSlice extends RandomSongListQuery {
|
||||
actions: {
|
||||
@@ -256,9 +256,9 @@ export const openShuffleAllModal = async (
|
||||
signal,
|
||||
},
|
||||
query: {
|
||||
offset: 0,
|
||||
sortBy: GenreListSort.NAME,
|
||||
sortOrder: ListSortOrder.ASC,
|
||||
offset: 0,
|
||||
},
|
||||
}),
|
||||
queryKey: queryKeys.genres.list(server?.id),
|
||||
|
||||
@@ -2,12 +2,12 @@ import { useMutation } from '@tanstack/react-query';
|
||||
import { AxiosError } from 'axios';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { MutationOptions } from '/@/renderer/lib/react-query';
|
||||
import { RMutationOptions } from '/@/renderer/lib/react-query';
|
||||
import { useServerById, useIncrementQueuePlayCount } from '/@/renderer/store';
|
||||
import { usePlayEvent } from '/@/renderer/store/event.store';
|
||||
import { ScrobbleRequest, ScrobbleResponse } from '/@/shared/types/domain/user-domain-types';
|
||||
|
||||
export const useSendScrobble = (options?: MutationOptions) => {
|
||||
export const useSendScrobble = (options?: RMutationOptions) => {
|
||||
const incrementPlayCount = useIncrementQueuePlayCount();
|
||||
const sendPlayEvent = usePlayEvent();
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@ import { MultiSelect } from '/@/shared/components/multi-select/multi-select';
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
import { Switch } from '/@/shared/components/switch/switch';
|
||||
import { toast } from '/@/shared/components/toast/toast';
|
||||
import { PlaylistListSort } from '/@/shared/types/domain/playlist-domain-types';
|
||||
import { SongListQuery, SongListSort } from '/@/shared/types/domain/song-domain-types';
|
||||
import { PlaylistListSortOptions } from '/@/shared/types/domain/playlist-domain-types';
|
||||
import { ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||
import { SongListQuery, SongListSort } from '/@/shared/types/domain/song-domain-types';
|
||||
|
||||
export const AddToPlaylistContextModal = ({
|
||||
id,
|
||||
@@ -44,9 +44,9 @@ export const AddToPlaylistContextModal = ({
|
||||
smart: false,
|
||||
},
|
||||
},
|
||||
sortBy: PlaylistListSort.NAME,
|
||||
sortOrder: ListSortOrder.ASC,
|
||||
offset: 0,
|
||||
sortBy: PlaylistListSortOptions.NAME,
|
||||
sortOrder: ListSortOrder.ASC,
|
||||
},
|
||||
serverId: server?.id,
|
||||
});
|
||||
@@ -70,9 +70,9 @@ export const AddToPlaylistContextModal = ({
|
||||
const getSongsByAlbum = async (albumId: string) => {
|
||||
const query: SongListQuery = {
|
||||
albumIds: [albumId],
|
||||
offset: 0,
|
||||
sortBy: SongListSort.ALBUM,
|
||||
sortOrder: ListSortOrder.ASC,
|
||||
offset: 0,
|
||||
};
|
||||
|
||||
const queryKey = queryKeys.songs.list(server?.id || '', query);
|
||||
@@ -88,9 +88,9 @@ export const AddToPlaylistContextModal = ({
|
||||
const getSongsByArtist = async (artistId: string) => {
|
||||
const query: SongListQuery = {
|
||||
artistIds: [artistId],
|
||||
offset: 0,
|
||||
sortBy: SongListSort.ARTIST,
|
||||
sortOrder: ListSortOrder.ASC,
|
||||
offset: 0,
|
||||
};
|
||||
|
||||
const queryKey = queryKeys.songs.list(server?.id || '', query);
|
||||
|
||||
@@ -32,10 +32,9 @@ import { Icon } from '/@/shared/components/icon/icon';
|
||||
import { NumberInput } from '/@/shared/components/number-input/number-input';
|
||||
import { ScrollArea } from '/@/shared/components/scroll-area/scroll-area';
|
||||
import { Select } from '/@/shared/components/select/select';
|
||||
import { PlaylistListSort } from '/@/shared/types/domain/playlist-domain-types';
|
||||
import { ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||
import { SongListSort } from '/@/shared/types/domain/song-domain-types';
|
||||
import { QueryBuilderGroup, QueryBuilderRule } from '/@/shared/types/types';
|
||||
import { ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||
|
||||
type AddArgs = {
|
||||
groupIndex: number[];
|
||||
@@ -111,7 +110,7 @@ export const PlaylistQueryBuilder = forwardRef(
|
||||
);
|
||||
|
||||
const { data: playlists } = usePlaylistList({
|
||||
query: { sortBy: PlaylistListSort.NAME, sortOrder: ListSortOrder.ASC, offset: 0 },
|
||||
query: { offset: 0, sortBy: PlaylistListSort.NAME, sortOrder: ListSortOrder.ASC },
|
||||
serverId: server?.id,
|
||||
});
|
||||
|
||||
|
||||
@@ -3,14 +3,14 @@ import { AxiosError } from 'axios';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { MutationHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RMutationHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import {
|
||||
AddToPlaylistArgs,
|
||||
AddToPlaylistResponse,
|
||||
} from '/@/shared/types/domain/playlist-domain-types';
|
||||
|
||||
export const useAddToPlaylist = (args: MutationHookArgs) => {
|
||||
export const useAddToPlaylist = (args: RMutationHookArgs) => {
|
||||
const { options } = args || {};
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ import { AxiosError } from 'axios';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { MutationHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RMutationHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { CreatePlaylistResponse } from '/@/shared/types/domain/playlist-domain-types';
|
||||
|
||||
export const useCreatePlaylist = (args: MutationHookArgs) => {
|
||||
export const useCreatePlaylist = (args: RMutationHookArgs) => {
|
||||
const { options } = args || {};
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ import { AxiosError } from 'axios';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { MutationHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RMutationHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById, useCurrentServer } from '/@/renderer/store';
|
||||
import { DeletePlaylistResponse } from '/@/shared/types/domain/playlist-domain-types';
|
||||
|
||||
export const useDeletePlaylist = (args: MutationHookArgs) => {
|
||||
export const useDeletePlaylist = (args: RMutationHookArgs) => {
|
||||
const { options } = args || {};
|
||||
const queryClient = useQueryClient();
|
||||
const server = useCurrentServer();
|
||||
|
||||
@@ -3,11 +3,11 @@ import { AxiosError, AxiosError } from 'axios';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { MutationOptions } from '/@/renderer/lib/react-query';
|
||||
import { RMutationOptions } from '/@/renderer/lib/react-query';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { RemoveFromPlaylistResponse } from '/@/shared/types/domain/playlist-domain-types';
|
||||
|
||||
export const useRemoveFromPlaylist = (options?: MutationOptions) => {
|
||||
export const useRemoveFromPlaylist = (options?: RMutationOptions) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<
|
||||
|
||||
@@ -3,11 +3,11 @@ import { AxiosError } from 'axios';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { MutationHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RMutationHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { UpdatePlaylistResponse } from '/@/shared/types/domain/playlist-domain-types';
|
||||
|
||||
export const useUpdatePlaylist = (args: MutationHookArgs) => {
|
||||
export const useUpdatePlaylist = (args: RMutationHookArgs) => {
|
||||
const { options } = args || {};
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import type { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { PlaylistDetailQuery } from '/@/shared/types/domain/playlist-domain-types';
|
||||
|
||||
export const usePlaylistDetail = (args: QueryHookArgs<PlaylistDetailQuery>) => {
|
||||
export const usePlaylistDetail = (args: RQueryHookArgs<PlaylistDetailQuery>) => {
|
||||
const { options, query, serverId } = args || {};
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { QueryOptions } from '/@/renderer/lib/react-query';
|
||||
import type { RQueryOptions } from '/@/renderer/lib/react-query';
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useServerById } from '/@/renderer/store';
|
||||
import { PlaylistListQuery } from '/@/shared/types/domain/playlist-domain-types';
|
||||
|
||||
export const usePlaylistList = (args: {
|
||||
options?: QueryOptions;
|
||||
options?: RQueryOptions;
|
||||
query: PlaylistListQuery;
|
||||
serverId?: string;
|
||||
}) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import type { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { PlaylistSongListQuery } from '/@/shared/types/domain/playlist-domain-types';
|
||||
|
||||
export const usePlaylistSongList = (args: QueryHookArgs<PlaylistSongListQuery>) => {
|
||||
export const usePlaylistSongList = (args: RQueryHookArgs<PlaylistSongListQuery>) => {
|
||||
const { options, query, serverId } = args || {};
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@ import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { SearchQuery } from '/@/shared/types/domain/search-domain-types';
|
||||
|
||||
export const useSearch = (args: QueryHookArgs<SearchQuery>) => {
|
||||
export const useSearch = (args: RQueryHookArgs<SearchQuery>) => {
|
||||
const { options, query, serverId } = args;
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
import { useSuspenseInfiniteQuery } from '@tanstack/react-query';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
|
||||
import { PosterCard } from '/@/renderer/components/card/poster-card';
|
||||
import {
|
||||
getAlbumList,
|
||||
getInfiniteAlbumListQueryKey,
|
||||
} from '/@/renderer/features/albums/api/queries/get-album-list-query';
|
||||
import { GridCarousel } from '/@/shared/components/grid-carousel/grid-carousel';
|
||||
import { AlbumListResponse, AlbumListSortOptions } from '/@/shared/types/domain/album-domain-types';
|
||||
import { ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||
|
||||
interface AlbumCarouselProps {
|
||||
rowCount?: number;
|
||||
serverId: string;
|
||||
sortBy: AlbumListSortOptions;
|
||||
sortOrder: ListSortOrder;
|
||||
title: string;
|
||||
}
|
||||
|
||||
const MemoizedAlbumCard = memo(PosterCard);
|
||||
|
||||
export function AlbumInfiniteCarousel(props: AlbumCarouselProps) {
|
||||
const { rowCount = 1, serverId, sortBy, sortOrder, title } = props;
|
||||
const { data: albums, fetchNextPage } = useInfiniteAlbumList(serverId, sortBy, sortOrder, 20);
|
||||
|
||||
const cards = useMemo(
|
||||
() =>
|
||||
albums.pages.flatMap((page) => {
|
||||
const loadedCards = page.items.map((album) => ({
|
||||
content: <MemoizedAlbumCard controls={{}} data={album} uniqueId={album.id} />,
|
||||
id: album.id,
|
||||
}));
|
||||
|
||||
if (page.items.length === 20) {
|
||||
return loadedCards;
|
||||
}
|
||||
|
||||
return [
|
||||
...loadedCards,
|
||||
...Array.from({ length: 20 - page.items.length }).map(() => {
|
||||
const id = nanoid();
|
||||
return {
|
||||
content: <MemoizedAlbumCard controls={{}} />,
|
||||
id,
|
||||
};
|
||||
}),
|
||||
];
|
||||
}),
|
||||
[albums.pages],
|
||||
);
|
||||
|
||||
const handleNextPage = useCallback(() => {}, []);
|
||||
|
||||
const handlePrevPage = useCallback(() => {}, []);
|
||||
|
||||
if (albums.pages[0]?.items.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<GridCarousel
|
||||
cards={cards}
|
||||
loadNextPage={fetchNextPage}
|
||||
onNextPage={handleNextPage}
|
||||
onPrevPage={handlePrevPage}
|
||||
rowCount={rowCount}
|
||||
title={title}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function useInfiniteAlbumList(
|
||||
serverId: string,
|
||||
sortBy: AlbumListSortOptions,
|
||||
sortOrder: ListSortOrder,
|
||||
limit: number,
|
||||
) {
|
||||
const query = useSuspenseInfiniteQuery<AlbumListResponse>({
|
||||
getNextPageParam: (lastPage, _allPages, lastPageParam) => {
|
||||
if (lastPage.items.length < limit) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const nextPageParam = Number(lastPageParam) + limit;
|
||||
|
||||
return String(nextPageParam);
|
||||
},
|
||||
initialPageParam: 0,
|
||||
queryFn: ({ pageParam }) => {
|
||||
return getAlbumList(serverId, {
|
||||
query: { limit: limit, offset: Number(pageParam), sortBy, sortOrder },
|
||||
});
|
||||
},
|
||||
queryKey: getInfiniteAlbumListQueryKey(serverId),
|
||||
});
|
||||
|
||||
return query;
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import isElectron from 'is-electron';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { MutationHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RMutationHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById, useSetAlbumListItemDataById, useSetQueueFavorite } from '/@/renderer/store';
|
||||
import { useFavoriteEvent } from '/@/renderer/store/event.store';
|
||||
import { AlbumDetailResponse } from '/@/shared/types/domain/album-domain-types';
|
||||
@@ -14,7 +14,7 @@ import { FavoriteResponse } from '/@/shared/types/domain/user-domain-types';
|
||||
|
||||
const remote = isElectron() ? window.api.remote : null;
|
||||
|
||||
export const useCreateFavorite = (args: MutationHookArgs) => {
|
||||
export const useCreateFavorite = (args: RMutationHookArgs) => {
|
||||
const { options } = args || {};
|
||||
const queryClient = useQueryClient();
|
||||
const setAlbumListData = useSetAlbumListItemDataById();
|
||||
|
||||
@@ -4,7 +4,7 @@ import isElectron from 'is-electron';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { MutationHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RMutationHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById, useSetAlbumListItemDataById, useSetQueueFavorite } from '/@/renderer/store';
|
||||
import { useFavoriteEvent } from '/@/renderer/store/event.store';
|
||||
import { AlbumDetailResponse } from '/@/shared/types/domain/album-domain-types';
|
||||
@@ -14,7 +14,7 @@ import { FavoriteResponse } from '/@/shared/types/domain/user-domain-types';
|
||||
|
||||
const remote = isElectron() ? window.api.remote : null;
|
||||
|
||||
export const useDeleteFavorite = (args: MutationHookArgs) => {
|
||||
export const useDeleteFavorite = (args: RMutationHookArgs) => {
|
||||
const { options } = args || {};
|
||||
const queryClient = useQueryClient();
|
||||
const setAlbumListData = useSetAlbumListItemDataById();
|
||||
|
||||
@@ -4,7 +4,7 @@ import isElectron from 'is-electron';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { MutationHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RMutationHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById, useSetAlbumListItemDataById, useSetQueueRating } from '/@/renderer/store';
|
||||
import { useRatingEvent } from '/@/renderer/store/event.store';
|
||||
import { Album, AlbumDetailResponse } from '/@/shared/types/domain/album-domain-types';
|
||||
@@ -14,7 +14,7 @@ import { RatingResponse, SetRatingRequest } from '/@/shared/types/domain/user-do
|
||||
|
||||
const remote = isElectron() ? window.api.remote : null;
|
||||
|
||||
export const useSetRating = (args: MutationHookArgs) => {
|
||||
export const useSetRating = (args: RMutationHookArgs) => {
|
||||
const { options } = args || {};
|
||||
const queryClient = useQueryClient();
|
||||
const setAlbumListData = useSetAlbumListItemDataById();
|
||||
|
||||
@@ -2,11 +2,11 @@ import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { ServerMusicFolderListQuery } from '/@/shared/types/domain/server-domain-types';
|
||||
|
||||
export const useMusicFolders = (args: QueryHookArgs<ServerMusicFolderListQuery>) => {
|
||||
export const useMusicFolders = (args: RQueryHookArgs<ServerMusicFolderListQuery>) => {
|
||||
const { options, serverId } = args || {};
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ import { useMutation } from '@tanstack/react-query';
|
||||
import { AxiosError } from 'axios';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { MutationHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RMutationHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { AnyLibraryItems } from '/@/shared/types/domain/shared-domain-types';
|
||||
import { ShareItemRequest, ShareItemResponse } from '/@/shared/types/domain/user-domain-types';
|
||||
|
||||
export const useShareItem = (args: MutationHookArgs) => {
|
||||
export const useShareItem = (args: RMutationHookArgs) => {
|
||||
const { options } = args || {};
|
||||
|
||||
return useMutation<
|
||||
|
||||
@@ -17,7 +17,7 @@ import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
||||
import { ButtonProps } from '/@/shared/components/button/button';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
import { Playlist, PlaylistListSort } from '/@/shared/types/domain/playlist-domain-types';
|
||||
import { Playlist, PlaylistListSortOptions } from '/@/shared/types/domain/playlist-domain-types';
|
||||
import { ServerType } from '/@/shared/types/domain/server-domain-types';
|
||||
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||
import { Play } from '/@/shared/types/types';
|
||||
@@ -140,9 +140,9 @@ export const SidebarPlaylistList = () => {
|
||||
|
||||
const playlistsQuery = usePlaylistList({
|
||||
query: {
|
||||
sortBy: PlaylistListSort.NAME,
|
||||
sortOrder: ListSortOrder.ASC,
|
||||
offset: 0,
|
||||
sortBy: PlaylistListSortOptions.NAME,
|
||||
sortOrder: ListSortOrder.ASC,
|
||||
},
|
||||
serverId: server?.id,
|
||||
});
|
||||
@@ -256,9 +256,9 @@ export const SidebarSharedPlaylistList = () => {
|
||||
|
||||
const playlistsQuery = usePlaylistList({
|
||||
query: {
|
||||
sortBy: PlaylistListSort.NAME,
|
||||
sortOrder: ListSortOrder.ASC,
|
||||
offset: 0,
|
||||
sortBy: PlaylistListSortOptions.NAME,
|
||||
sortOrder: ListSortOrder.ASC,
|
||||
},
|
||||
serverId: server?.id,
|
||||
});
|
||||
|
||||
@@ -2,11 +2,11 @@ import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { SimilarSongsQuery } from '/@/shared/types/domain/song-domain-types';
|
||||
|
||||
export const useSimilarSongs = (args: QueryHookArgs<SimilarSongsQuery>) => {
|
||||
export const useSimilarSongs = (args: RQueryHookArgs<SimilarSongsQuery>) => {
|
||||
const { options, query, serverId } = args || {};
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import type { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { SongListQuery } from '/@/shared/types/domain/song-domain-types';
|
||||
|
||||
export const useSongListCount = (args: QueryHookArgs<SongListQuery>) => {
|
||||
export const useSongListCount = (args: RQueryHookArgs<SongListQuery>) => {
|
||||
const { options, query, serverId } = args;
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import type { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { SongListQuery } from '/@/shared/types/domain/song-domain-types';
|
||||
|
||||
export const useSongList = (args: QueryHookArgs<SongListQuery>, imageSize?: number) => {
|
||||
export const useSongList = (args: RQueryHookArgs<SongListQuery>, imageSize?: number) => {
|
||||
const { options, query, serverId } = args || {};
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@ import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { hasFeature } from '/@/shared/api/utils';
|
||||
import { ServerFeature } from '/@/shared/types/domain/server-domain-types';
|
||||
import { TagQuery } from '/@/shared/types/domain/tag-domain-types';
|
||||
|
||||
export const useTagList = (args: QueryHookArgs<TagQuery>) => {
|
||||
export const useTagList = (args: RQueryHookArgs<TagQuery>) => {
|
||||
const { options, query, serverId } = args || {};
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { QueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
import type { RQueryHookArgs } from '/@/renderer/lib/react-query';
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { useServerById } from '/@/renderer/store';
|
||||
import { UserListQuery } from '/@/shared/types/domain/user-domain-types';
|
||||
|
||||
export const useUserList = (args: QueryHookArgs<UserListQuery>) => {
|
||||
export const useUserList = (args: RQueryHookArgs<UserListQuery>) => {
|
||||
const { options, query, serverId } = args || {};
|
||||
const server = useServerById(serverId);
|
||||
|
||||
|
||||
@@ -41,8 +41,6 @@ export type InfiniteQueryOptions = {
|
||||
gcTime?: UseInfiniteQueryOptions['gcTime'];
|
||||
meta?: UseInfiniteQueryOptions['meta'];
|
||||
onError?: (err: any) => void;
|
||||
onSettled?: any;
|
||||
onSuccess?: any;
|
||||
queryKey?: UseInfiniteQueryOptions['queryKey'];
|
||||
refetchInterval?: number;
|
||||
refetchIntervalInBackground?: UseInfiniteQueryOptions['refetchIntervalInBackground'];
|
||||
@@ -53,11 +51,11 @@ export type InfiniteQueryOptions = {
|
||||
useErrorBoundary?: boolean;
|
||||
};
|
||||
|
||||
export type MutationHookArgs = {
|
||||
options?: MutationOptions;
|
||||
export type RMutationHookArgs = {
|
||||
options?: RMutationOptions;
|
||||
};
|
||||
|
||||
export type MutationOptions = {
|
||||
export type RMutationOptions = {
|
||||
mutationKey: UseMutationOptions['mutationKey'];
|
||||
onError?: (err: any) => void;
|
||||
onSettled?: any;
|
||||
@@ -67,19 +65,17 @@ export type MutationOptions = {
|
||||
useErrorBoundary?: boolean;
|
||||
};
|
||||
|
||||
export type QueryHookArgs<T> = {
|
||||
options?: QueryOptions;
|
||||
export type RQueryHookArgs<T> = {
|
||||
options?: RQueryOptions;
|
||||
query: T;
|
||||
serverId: string | undefined;
|
||||
};
|
||||
|
||||
export type QueryOptions = {
|
||||
export type RQueryOptions = {
|
||||
enabled?: UseQueryOptions['enabled'];
|
||||
gcTime?: UseQueryOptions['gcTime'];
|
||||
meta?: UseQueryOptions['meta'];
|
||||
onError?: (err: any) => void;
|
||||
onSettled?: any;
|
||||
onSuccess?: any;
|
||||
queryKey?: UseQueryOptions['queryKey'];
|
||||
refetchInterval?: number;
|
||||
refetchIntervalInBackground?: UseQueryOptions['refetchIntervalInBackground'];
|
||||
|
||||
@@ -6,28 +6,28 @@ import { createWithEqualityFn } from 'zustand/traditional';
|
||||
import { DataTableProps, PersistedTableColumn } from '/@/renderer/store/settings.store';
|
||||
import { mergeOverridingColumns } from '/@/renderer/store/utils';
|
||||
import { AlbumListRequest, AlbumListSort } from '/@/shared/types/domain/album-domain-types';
|
||||
import {
|
||||
AlbumArtistListRequest,
|
||||
AlbumArtistListSort,
|
||||
ArtistListRequest,
|
||||
} from '/@/shared/types/domain/artist-domain-types';
|
||||
import { GenreListRequest, GenreListSort } from '/@/shared/types/domain/genre-domain-types';
|
||||
import { AlbumArtistListSort, ArtistListRequest } from '/@/shared/types/domain/artist-domain-types';
|
||||
import { GenreListRequest, GenreListSortOptions } from '/@/shared/types/domain/genre-domain-types';
|
||||
import {
|
||||
PlaylistListRequest,
|
||||
PlaylistListSort,
|
||||
PlaylistListSortOptions,
|
||||
} from '/@/shared/types/domain/playlist-domain-types';
|
||||
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||
import { SongListRequest, SongListSort } from '/@/shared/types/domain/song-domain-types';
|
||||
import {
|
||||
SongListRequest,
|
||||
SongListSort,
|
||||
SongListSortOptions,
|
||||
} from '/@/shared/types/domain/song-domain-types';
|
||||
import { ListDisplayType, TableColumn, TablePagination } from '/@/shared/types/types';
|
||||
|
||||
export const generatePageKey = (page: string, id?: string) => {
|
||||
return id ? `${page}_${id}` : page;
|
||||
};
|
||||
|
||||
export type AlbumArtistListFilter = Omit<AlbumArtistListRequest['query'], 'limit' | 'startIndex'>;
|
||||
export type AlbumListFilter = Omit<AlbumListRequest['query'], 'limit' | 'startIndex'>;
|
||||
export type ArtistListFilter = Omit<ArtistListRequest['query'], 'limit' | 'startIndex'>;
|
||||
export type GenreListFilter = Omit<GenreListRequest['query'], 'limit' | 'startIndex'>;
|
||||
export type AlbumArtistListFilter = Omit<ArtistListRequest['query'], 'limit' | 'offset'>;
|
||||
export type AlbumListFilter = Omit<AlbumListRequest['query'], 'limit' | 'offset'>;
|
||||
export type ArtistListFilter = Omit<ArtistListRequest['query'], 'limit' | 'offset'>;
|
||||
export type GenreListFilter = Omit<GenreListRequest['query'], 'limit' | 'offset'>;
|
||||
export type ListDeterministicArgs = { key: ListKey };
|
||||
export type ListGridProps = {
|
||||
itemGap?: number;
|
||||
@@ -544,7 +544,7 @@ export const useListStore = createWithEqualityFn<ListSlice>()(
|
||||
genre: {
|
||||
display: ListDisplayType.TABLE,
|
||||
filter: {
|
||||
sortBy: GenreListSort.NAME,
|
||||
sortBy: GenreListSortOptions.NAME,
|
||||
sortOrder: ListSortOrder.ASC,
|
||||
},
|
||||
grid: { itemGap: 10, itemSize: 200, scrollOffset: 0 },
|
||||
@@ -573,7 +573,7 @@ export const useListStore = createWithEqualityFn<ListSlice>()(
|
||||
playlist: {
|
||||
display: ListDisplayType.GRID,
|
||||
filter: {
|
||||
sortBy: PlaylistListSort.NAME,
|
||||
sortBy: PlaylistListSortOptions.NAME,
|
||||
sortOrder: ListSortOrder.DESC,
|
||||
},
|
||||
grid: { itemGap: 10, itemSize: 200, scrollOffset: 0 },
|
||||
@@ -606,7 +606,7 @@ export const useListStore = createWithEqualityFn<ListSlice>()(
|
||||
song: {
|
||||
display: ListDisplayType.TABLE,
|
||||
filter: {
|
||||
sortBy: SongListSort.RECENTLY_ADDED,
|
||||
sortBy: SongListSortOptions.RECENTLY_ADDED,
|
||||
sortOrder: ListSortOrder.DESC,
|
||||
},
|
||||
grid: { itemGap: 10, itemSize: 200, scrollOffset: 0 },
|
||||
|
||||
@@ -5,9 +5,12 @@ import { createWithEqualityFn } from 'zustand/traditional';
|
||||
import { PlaylistListFilter, SongListFilter } from '/@/renderer/store/list.store';
|
||||
import { DataTableProps } from '/@/renderer/store/settings.store';
|
||||
import { mergeOverridingColumns } from '/@/renderer/store/utils';
|
||||
import { PlaylistListSort, PlaylistListSort } from '/@/shared/types/domain/playlist-domain-types';
|
||||
import { ListDisplayType, TableColumn, TablePagination } from '/@/shared/types/types';
|
||||
import {
|
||||
PlaylistListSortOptions,
|
||||
PlaylistListSortOptions,
|
||||
} from '/@/shared/types/domain/playlist-domain-types';
|
||||
import { ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
|
||||
import { ListDisplayType, TableColumn, TablePagination } from '/@/shared/types/types';
|
||||
|
||||
export interface PlaylistSlice extends PlaylistState {
|
||||
actions: {
|
||||
@@ -155,8 +158,8 @@ export const usePlaylistStore = createWithEqualityFn<PlaylistSlice>()(
|
||||
list: {
|
||||
display: ListDisplayType.TABLE,
|
||||
filter: {
|
||||
musicFolderId: undefined,
|
||||
sortBy: PlaylistListSort.NAME,
|
||||
offset: 0,
|
||||
sortBy: PlaylistListSortOptions.NAME,
|
||||
sortOrder: ListSortOrder.ASC,
|
||||
},
|
||||
table: {
|
||||
|
||||
@@ -55,7 +55,7 @@ export const createApiClient = (
|
||||
};
|
||||
|
||||
const authMiddleware: (server: ServerListItem) => Middleware = (server: ServerListItem) => ({
|
||||
onRequest: async ({ params }) => {
|
||||
onRequest: async ({ params, request }) => {
|
||||
const credential = deserializeCredential(server.credential);
|
||||
|
||||
if (params.query) {
|
||||
@@ -67,6 +67,18 @@ const authMiddleware: (server: ServerListItem) => Middleware = (server: ServerLi
|
||||
params.query[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
const stringifiedParams = qs.stringify(params.query, { arrayFormat: 'repeat' });
|
||||
|
||||
const url = new URL(request.url);
|
||||
url.search = stringifiedParams;
|
||||
|
||||
return new Request(url.toString(), {
|
||||
body: request.body,
|
||||
headers: request.headers,
|
||||
method: request.method,
|
||||
signal: request.signal,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import { AxiosHeaders } from 'axios';
|
||||
import dayjs from 'dayjs';
|
||||
import isElectron from 'is-electron';
|
||||
import { orderBy, shuffle } from 'lodash';
|
||||
import { stringify } from 'querystring';
|
||||
import semverCoerce from 'semver/functions/coerce';
|
||||
import semverGte from 'semver/functions/gte';
|
||||
import { z } from 'zod';
|
||||
@@ -388,7 +387,7 @@ function getListCountKey(options: {
|
||||
serverId: string;
|
||||
type: LibraryItem | string;
|
||||
}) {
|
||||
const hash = stringify(options.query as Record<string, boolean | null | number | string>);
|
||||
const hash = JSON.stringify(options.query as Record<string, boolean | null | number | string>);
|
||||
return `${options.serverId}::${options.type}::${hash}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
.grid-carousel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: base.$gap-md;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
container-name: grid-carousel;
|
||||
container-type: inline-size;
|
||||
}
|
||||
|
||||
.navigation {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(20%, 1fr));
|
||||
gap: 1rem;
|
||||
height: calc(var(--row-count) * (100cqw / 2 + 3rem));
|
||||
margin-bottom: 1rem;
|
||||
overflow: hidden;
|
||||
|
||||
/* @mixin larger-than-sm {
|
||||
grid-template-columns: repeat(4, minmax(20%, 1fr));
|
||||
height: calc(var(--row-count) * (100cqw / 4 + 3rem));
|
||||
}
|
||||
|
||||
@mixin larger-than-md {
|
||||
grid-template-columns: repeat(5, minmax(15%, 1fr));
|
||||
height: calc(var(--row-count) * (100cqw / 5 + 3rem));
|
||||
}
|
||||
|
||||
@mixin larger-than-lg {
|
||||
grid-template-columns: repeat(6, minmax(15%, 1fr));
|
||||
height: calc(var(--row-count) * (100cqw / 6 + 3rem));
|
||||
}
|
||||
|
||||
@mixin larger-than-xl {
|
||||
grid-template-columns: repeat(7, minmax(10%, 1fr));
|
||||
height: calc(var(--row-count) * (100cqw / 7 + 3rem));
|
||||
}
|
||||
|
||||
@mixin larger-than-2xl {
|
||||
grid-template-columns: repeat(8, minmax(5%, 1fr));
|
||||
height: calc(var(--row-count) * (100cqw / 8 + 3rem));
|
||||
}
|
||||
|
||||
@mixin larger-than-3xl {
|
||||
grid-template-columns: repeat(9, minmax(5%, 1fr));
|
||||
height: calc(var(--row-count) * (100cqw / 9 + 3rem));
|
||||
} */
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
import { AnimatePresence, motion, Variants } from 'motion/react';
|
||||
import { memo, ReactNode, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import styles from './grid-carousel.module.css';
|
||||
|
||||
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { TextTitle } from '/@/shared/components/text-title/text-title';
|
||||
import { useContainerBreakpoints } from '/@/shared/hooks/use-container-breakpoints';
|
||||
|
||||
interface Card {
|
||||
content: ReactNode;
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface GridCarouselProps {
|
||||
cards: Card[];
|
||||
loadNextPage?: () => void;
|
||||
onNextPage: (page: number) => void;
|
||||
onPrevPage: (page: number) => void;
|
||||
rowCount?: number;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
const MemoizedCard = memo(({ content }: { content: ReactNode }) => (
|
||||
<div className={styles.card}>{content}</div>
|
||||
));
|
||||
|
||||
MemoizedCard.displayName = 'MemoizedCard';
|
||||
|
||||
const pageVariants: Variants = {
|
||||
animate: { opacity: 1, transition: { duration: 0.3, ease: 'easeOut' }, x: 0 },
|
||||
exit: (custom: { isNext: boolean }) => ({
|
||||
opacity: 0,
|
||||
transition: { duration: 0.3, ease: 'easeIn' },
|
||||
x: custom.isNext ? -100 : 100,
|
||||
}),
|
||||
initial: (custom: { isNext: boolean }) => ({ opacity: 0, x: custom.isNext ? 100 : -100 }),
|
||||
};
|
||||
|
||||
export function GridCarousel(props: GridCarouselProps) {
|
||||
const { cards, loadNextPage, onNextPage, onPrevPage, rowCount = 1, title } = props;
|
||||
const { breakpoints, ref: containerRef } = useContainerBreakpoints();
|
||||
|
||||
const [currentPage, setCurrentPage] = useState({
|
||||
isNext: false,
|
||||
page: 0,
|
||||
});
|
||||
|
||||
const handlePrevPage = useCallback(() => {
|
||||
setCurrentPage((prev) => ({
|
||||
isNext: false,
|
||||
page: prev.page > 0 ? prev.page - 1 : 0,
|
||||
}));
|
||||
onPrevPage(currentPage.page);
|
||||
}, [currentPage, onPrevPage]);
|
||||
|
||||
const handleNextPage = useCallback(() => {
|
||||
setCurrentPage((prev) => ({
|
||||
isNext: true,
|
||||
page: prev.page + 1,
|
||||
}));
|
||||
onNextPage(currentPage.page);
|
||||
}, [currentPage, onNextPage]);
|
||||
|
||||
const cardsToShow = getCardsToShow(breakpoints);
|
||||
|
||||
const visibleCards = useMemo(() => {
|
||||
return cards.slice(
|
||||
currentPage.page * cardsToShow * rowCount,
|
||||
(currentPage.page + 1) * cardsToShow * rowCount,
|
||||
);
|
||||
}, [cards, currentPage, cardsToShow, rowCount]);
|
||||
|
||||
const shouldLoadNextPage = visibleCards.length < cardsToShow * rowCount;
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldLoadNextPage) {
|
||||
loadNextPage?.();
|
||||
}
|
||||
}, [loadNextPage, shouldLoadNextPage]);
|
||||
|
||||
const isPrevDisabled = currentPage.page === 0;
|
||||
const isNextDisabled = visibleCards.length < cardsToShow * rowCount;
|
||||
|
||||
return (
|
||||
<motion.div className={styles.gridCarousel} ref={containerRef}>
|
||||
<div className={styles.navigation}>
|
||||
<TextTitle order={1} size="lg">
|
||||
{title}
|
||||
</TextTitle>
|
||||
<Group gap="xs" justify="end">
|
||||
<ActionIcon
|
||||
disabled={isPrevDisabled}
|
||||
icon="arrowLeftS"
|
||||
onClick={handlePrevPage}
|
||||
size="lg"
|
||||
variant="default"
|
||||
/>
|
||||
<ActionIcon
|
||||
disabled={isNextDisabled}
|
||||
icon="arrowRightS"
|
||||
onClick={handleNextPage}
|
||||
size="lg"
|
||||
variant="default"
|
||||
/>
|
||||
</Group>
|
||||
</div>
|
||||
<AnimatePresence custom={currentPage} initial={false} mode="wait">
|
||||
<motion.div
|
||||
animate="animate"
|
||||
className={styles.grid}
|
||||
custom={currentPage}
|
||||
exit="exit"
|
||||
initial="initial"
|
||||
key={currentPage.page}
|
||||
style={{ '--row-count': rowCount } as React.CSSProperties}
|
||||
variants={pageVariants}
|
||||
>
|
||||
{visibleCards.map((card) => (
|
||||
<MemoizedCard content={card.content} key={card.id} />
|
||||
))}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
function getCardsToShow(breakpoints: {
|
||||
isLargerThan2xl: boolean;
|
||||
isLargerThan3xl: boolean;
|
||||
isLargerThanLg: boolean;
|
||||
isLargerThanMd: boolean;
|
||||
isLargerThanSm: boolean;
|
||||
isLargerThanXl: boolean;
|
||||
}) {
|
||||
if (breakpoints.isLargerThan3xl) {
|
||||
return 10;
|
||||
}
|
||||
|
||||
if (breakpoints.isLargerThan2xl) {
|
||||
return 8;
|
||||
}
|
||||
|
||||
if (breakpoints.isLargerThanXl) {
|
||||
return 7;
|
||||
}
|
||||
|
||||
if (breakpoints.isLargerThanLg) {
|
||||
return 6;
|
||||
}
|
||||
|
||||
if (breakpoints.isLargerThanMd) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
if (breakpoints.isLargerThanSm) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { useResizeObserver } from '@mantine/hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { Breakpoints } from '/@/shared/types/types';
|
||||
|
||||
export function useContainerBreakpoints() {
|
||||
const [ref, rect] = useResizeObserver();
|
||||
const [globalBreakpoints, setGlobalBreakpoints] = useState({
|
||||
lg: 0,
|
||||
md: 0,
|
||||
sm: 0,
|
||||
xl: 0,
|
||||
xxl: 0,
|
||||
xxxl: 0,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const root = document.documentElement;
|
||||
const computedStyle = getComputedStyle(root);
|
||||
|
||||
const getBreakpointValue = (breakpoint: string) => {
|
||||
const rootFontSize = 16;
|
||||
const value = computedStyle.getPropertyValue(`--theme-breakpoint-${breakpoint}`).trim();
|
||||
return parseInt(value, 10) * rootFontSize || 0;
|
||||
};
|
||||
|
||||
setGlobalBreakpoints({
|
||||
lg: getBreakpointValue('lg'),
|
||||
md: getBreakpointValue('md'),
|
||||
sm: getBreakpointValue('sm'),
|
||||
xl: getBreakpointValue('xl'),
|
||||
xxl: getBreakpointValue('xxl'),
|
||||
xxxl: getBreakpointValue('xxxl'),
|
||||
});
|
||||
}, []);
|
||||
|
||||
const isLargerThanSm = rect?.width >= globalBreakpoints.sm;
|
||||
const isLargerThanMd = rect?.width >= globalBreakpoints.md;
|
||||
const isLargerThanLg = rect?.width >= globalBreakpoints.lg;
|
||||
const isLargerThanXl = rect?.width >= globalBreakpoints.xl;
|
||||
const isLargerThan2xl = rect?.width >= globalBreakpoints.xxl;
|
||||
const isLargerThan3xl = rect?.width >= globalBreakpoints.xxxl;
|
||||
|
||||
const breakpoints: Breakpoints = {
|
||||
isLargerThan2xl,
|
||||
isLargerThan3xl,
|
||||
isLargerThanLg,
|
||||
isLargerThanMd,
|
||||
isLargerThanSm,
|
||||
isLargerThanXl,
|
||||
};
|
||||
|
||||
return { breakpoints, rect, ref };
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
import { orderBy, shuffle } from 'lodash';
|
||||
import { z } from 'zod';
|
||||
|
||||
import i18n from '/@/i18n/i18n';
|
||||
import { JFAlbumListSort } from '/@/shared/api/jellyfin.types';
|
||||
import { jfType } from '/@/shared/api/jellyfin/jellyfin-types';
|
||||
import { NDAlbumListSort } from '/@/shared/api/navidrome.types';
|
||||
import { ndType } from '/@/shared/api/navidrome/navidrome-types';
|
||||
import {
|
||||
BasePaginatedQuery,
|
||||
BasePaginatedResponse,
|
||||
@@ -91,7 +88,7 @@ export interface AlbumListQuery extends BasePaginatedQuery<AlbumListSortOptions>
|
||||
|
||||
export type AlbumListRequest = { query: AlbumListQuery; totalRecordCount?: number };
|
||||
|
||||
export type AlbumListResponse = BasePaginatedResponse<Album[]> | null | undefined;
|
||||
export type AlbumListResponse = BasePaginatedResponse<Album[]>;
|
||||
|
||||
type AlbumListSortMap = {
|
||||
jellyfin: Record<AlbumListSort, JFAlbumListSort | undefined>;
|
||||
@@ -199,7 +196,7 @@ export type AlbumDetailQuery = { id: string };
|
||||
|
||||
export type AlbumDetailRequest = { query: AlbumDetailQuery };
|
||||
|
||||
export type AlbumDetailResponse = Album | null | undefined;
|
||||
export type AlbumDetailResponse = Album;
|
||||
|
||||
export type AlbumInfo = {
|
||||
imageUrl: null | string;
|
||||
|
||||
@@ -133,7 +133,7 @@ export interface ArtistListQuery extends BasePaginatedQuery<ArtistListSortOption
|
||||
|
||||
export type ArtistListRequest = { query: ArtistListQuery; totalRecordCount?: number };
|
||||
|
||||
export type ArtistListResponse = BasePaginatedResponse<Artist[]> | null | undefined;
|
||||
export type ArtistListResponse = BasePaginatedResponse<Artist[]>;
|
||||
type ArtistListSortMap = {
|
||||
jellyfin: Record<ArtistListSort, JFArtistListSort | undefined>;
|
||||
navidrome: Record<ArtistListSort, undefined>;
|
||||
|
||||
@@ -36,7 +36,7 @@ export interface GenreListQuery extends BasePaginatedQuery<GenreListSortOptions>
|
||||
|
||||
export type GenreListRequest = { query: GenreListQuery; totalRecordCount?: number };
|
||||
|
||||
export type GenreListResponse = BasePaginatedResponse<Genre[]> | null | undefined;
|
||||
export type GenreListResponse = BasePaginatedResponse<Genre[]>;
|
||||
|
||||
export type RelatedGenre = {
|
||||
id: string;
|
||||
|
||||
@@ -119,7 +119,7 @@ export interface PlaylistListQuery extends BasePaginatedQuery<PlaylistListSortOp
|
||||
|
||||
export type PlaylistListRequest = { query: PlaylistListQuery; totalRecordCount?: number };
|
||||
|
||||
export type PlaylistListResponse = BasePaginatedResponse<Playlist[]> | null | undefined;
|
||||
export type PlaylistListResponse = BasePaginatedResponse<Playlist[]>;
|
||||
|
||||
export type PlaylistSong = Song & {
|
||||
playlistItemId: string;
|
||||
|
||||
@@ -153,7 +153,7 @@ export interface SongListQuery extends BasePaginatedQuery<SongListSort> {
|
||||
|
||||
export type SongListRequest = { query: SongListQuery; totalRecordCount?: number };
|
||||
|
||||
export type SongListResponse = BasePaginatedResponse<Song[]> | null | undefined;
|
||||
export type SongListResponse = BasePaginatedResponse<Song[]>;
|
||||
type SongListSortMap = {
|
||||
jellyfin: Record<SongListSort, JFSongListSort | undefined>;
|
||||
navidrome: Record<SongListSort, NDSongListSort | undefined>;
|
||||
@@ -263,7 +263,7 @@ export type TopSongListQuery = {
|
||||
|
||||
export type TopSongListRequest = { query: TopSongListQuery; totalRecordCount?: number };
|
||||
|
||||
export type TopSongListResponse = BasePaginatedResponse<Song[]> | null | undefined;
|
||||
export type TopSongListResponse = BasePaginatedResponse<Song[]>;
|
||||
|
||||
export const sortSongList = (
|
||||
songs: QueueSong[],
|
||||
|
||||
@@ -24,6 +24,15 @@ export enum Platform {
|
||||
WINDOWS = 'windows',
|
||||
}
|
||||
|
||||
export type Breakpoints = {
|
||||
isLargerThan2xl: boolean;
|
||||
isLargerThan3xl: boolean;
|
||||
isLargerThanLg: boolean;
|
||||
isLargerThanMd: boolean;
|
||||
isLargerThanSm: boolean;
|
||||
isLargerThanXl: boolean;
|
||||
};
|
||||
|
||||
export type CardRoute = {
|
||||
route: AppRoute | string;
|
||||
slugs?: RouteSlug[];
|
||||
|
||||
Reference in New Issue
Block a user