add on this day to discover

This commit is contained in:
Violet Caulfield
2025-12-16 21:54:06 -06:00
parent 621f7c38fb
commit c00185f513
8 changed files with 170 additions and 8 deletions
+4 -1
View File
@@ -1,5 +1,5 @@
import { useMutation } from '@tanstack/react-query'
import { useRecentlyAddedAlbums } from '../../queries/album'
import { useAlbumsOnThisDay, useRecentlyAddedAlbums } from '../../queries/album'
import { usePublicPlaylists } from '../../queries/playlist'
import { useDiscoverArtists } from '../../queries/suggestions'
@@ -10,12 +10,15 @@ const useDiscoverQueries = () => {
const { refetch: refetchArtistSuggestions } = useDiscoverArtists()
const { refetch: refetchOnThisDay } = useAlbumsOnThisDay()
return useMutation({
mutationFn: async () =>
await Promise.allSettled([
refetchRecentlyAdded(),
refetchPublicPlaylists(),
refetchArtistSuggestions(),
refetchOnThisDay(),
]),
networkMode: 'online',
})
+31 -4
View File
@@ -2,15 +2,18 @@ import { QueryKeys } from '../../../enums/query-keys'
import { InfiniteData, useInfiniteQuery, UseInfiniteQueryResult } from '@tanstack/react-query'
import { ItemSortBy } from '@jellyfin/sdk/lib/generated-client/models/item-sort-by'
import { SortOrder } from '@jellyfin/sdk/lib/generated-client/models/sort-order'
import { fetchAlbums } from './utils/album'
import { fetchAlbums, fetchAlbumsOnThisDay } from './utils/album'
import { RefObject, useCallback, useRef } from 'react'
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client'
import { BaseItemDto, BaseItemKind } from '@jellyfin/sdk/lib/generated-client'
import flattenInfiniteQueryPages from '../../../utils/query-selectors'
import { ApiLimits, MaxPages } from '../../../configs/query.config'
import { fetchRecentlyAdded } from '../recents/utils'
import { queryClient } from '../../../constants/query-client'
import { useApi, useJellifyLibrary, useJellifyUser } from '../../../stores'
import useLibraryStore from '../../../stores/library'
import { AlbumsOnthisDayQueryKey, RecentlyAddedAlbumsQueryKey } from './keys'
import { fetchItems } from '../item'
import { getItemsApi } from '@jellyfin/sdk/lib/utils/api'
const useAlbums: () => [
RefObject<Set<string>>,
@@ -64,7 +67,7 @@ export const useRecentlyAddedAlbums = () => {
const [library] = useJellifyLibrary()
return useInfiniteQuery({
queryKey: [QueryKeys.RecentlyAddedAlbums, library?.musicLibraryId],
queryKey: RecentlyAddedAlbumsQueryKey(library),
queryFn: ({ pageParam }) => fetchRecentlyAdded(api, library, pageParam),
select: (data) => data.pages.flatMap((page) => page),
getNextPageParam: (lastPage, allPages, lastPageParam) =>
@@ -78,6 +81,30 @@ export const useRefetchRecentlyAdded: () => () => void = () => {
return () =>
queryClient.invalidateQueries({
queryKey: [QueryKeys.RecentlyAddedAlbums, library?.musicLibraryId],
queryKey: RecentlyAddedAlbumsQueryKey(library),
})
}
export const useAlbumsOnThisDay = () => {
const api = useApi()
const [library] = useJellifyLibrary()
return useInfiniteQuery({
queryKey: AlbumsOnthisDayQueryKey(library),
queryFn: ({ queryKey, pageParam }) =>
fetchAlbumsOnThisDay(
api,
library,
queryKey[2] as number,
queryKey[3] as number,
pageParam,
),
select: (data) => data.pages.flatMap((page) => page),
enabled: !!api,
maxPages: MaxPages.Discover,
initialPageParam: 0,
getNextPageParam: (lastPage, allPages, lastPageParam) =>
lastPage.length > 0 ? lastPageParam + 1 : undefined,
})
}
+42
View File
@@ -0,0 +1,42 @@
import { JellifyLibrary } from '@/src/types/JellifyLibrary'
enum AlbumQueryKeys {
RecentlyAdded = 'RECENTLY_ADDED',
OnThisDay = 'ON_THIS_DAY',
InfiniteAlbums = 'INFINITE_ALBUMS',
}
/**
* A query key for an infinite query of albums
*
* @param isFavorites Whether the albums are filtered to favorites
* @param library The {@link JellifyLibrary} set in the store
* @returns
*/
export const InfiniteAlbumsQueryKey = (
isFavorites: boolean | undefined,
library: JellifyLibrary | undefined,
) => [AlbumQueryKeys.InfiniteAlbums, isFavorites, library?.musicLibraryId]
/**
* A query key for an infinite query of recent albums
*
* @param library The {@link JellifyLibrary} set in the store
* @returns
*/
export const RecentlyAddedAlbumsQueryKey = (library: JellifyLibrary | undefined) => [
AlbumQueryKeys.RecentlyAdded,
library?.musicLibraryId,
]
/**
* A query key for an infinite query of albums released on this day
*
* @param library The {@link JellifyLibrary} set in the store
* @returns
*/
export const AlbumsOnthisDayQueryKey = (library: JellifyLibrary | undefined) => {
const date = new Date()
return [AlbumQueryKeys.OnThisDay, library?.musicLibraryId, date.getMonth(), date.getDay()]
}
+24
View File
@@ -55,3 +55,27 @@ export function fetchAlbumById(api: Api | undefined, albumId: string): Promise<B
})
})
}
export function fetchAlbumsOnThisDay(
api: Api | undefined,
library: JellifyLibrary | undefined,
month: number,
day: number,
page: number,
): Promise<BaseItemDto[]> {
return new Promise((resolve, reject) => {
if (!api || !library) return reject('Api or Library instance not set')
else
nitroFetch<{ Items: BaseItemDto[] }>(api, '/Items', {
ParentId: library.musicLibraryId,
IncludeItemTypes: [BaseItemKind.MusicAlbum],
EnableUserData: true, // This will populate the user data query later down the line
SortBy: [ItemSortBy.ProductionYear],
SortOrder: [SortOrder.Descending],
StartIndex: page * ApiLimits.Library,
Limit: ApiLimits.Library,
Fields: [ItemFields.SortName],
Recursive: true,
}).then(({ Items }) => resolve(Items))
})
}
+3
View File
@@ -7,6 +7,7 @@ import useDiscoverQueries from '../../api/mutations/discover'
import { useIsRestoring } from '@tanstack/react-query'
import { useRecentlyAddedAlbums } from '../../api/queries/album'
import { RefreshControl } from 'react-native'
import OnThisDay from './helpers/on-this-day'
export default function Index(): React.JSX.Element {
const { mutateAsync: refreshAsync, isPending: refreshing } = useDiscoverQueries()
@@ -44,6 +45,8 @@ function DiscoverContent() {
<YStack gap={'$3'}>
<RecentlyAdded />
<OnThisDay />
<PublicPlaylists />
<SuggestedArtists />
@@ -0,0 +1,62 @@
import { useNavigation } from '@react-navigation/native'
import { useAlbumsOnThisDay } from '../../../api/queries/album'
import Animated, { FadeIn, FadeOut, LinearTransition } from 'react-native-reanimated'
import { H5, XStack } from 'tamagui'
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
import DiscoverStackParamList from '@/src/screens/Discover/types'
import Icon from '../../Global/components/icon'
import HorizontalCardList from '../../Global/components/horizontal-list'
import { ItemCard } from '../../Global/components/item-card'
import navigationRef from '../../../../navigation'
export default function OnThisDay(): React.JSX.Element | null {
const { data: albumsOnThisDay } = useAlbumsOnThisDay()
const albumsOnThisDayExist = albumsOnThisDay && albumsOnThisDay.length > 0
const navigation = useNavigation<NativeStackNavigationProp<DiscoverStackParamList>>()
return albumsOnThisDayExist ? (
<Animated.View
entering={FadeIn.springify()}
exiting={FadeOut.springify()}
layout={LinearTransition.springify()}
testID='discover-public-playlists'
style={{
flex: 1,
}}
>
<XStack alignItems='center'>
<H5 marginLeft={'$2'} lineBreakStrategyIOS='standard'>
On this day
</H5>
<Icon name='arrow-right' />
</XStack>
<HorizontalCardList
data={albumsOnThisDay.slice(0, 10) ?? []}
renderItem={({ item }) => (
<ItemCard
caption={item.Name}
subCaption={`${item.Artists?.join(', ')}`}
squared
size={'$11'}
item={item}
onPress={() => {
navigation.navigate('Album', {
album: item,
})
}}
onLongPress={() => {
navigationRef.navigate('Context', {
item,
navigation,
})
}}
marginHorizontal={'$1'}
captionAlign='left'
/>
)}
/>
</Animated.View>
) : null
}
@@ -9,7 +9,7 @@ import DiscoverStackParamList from '../../../screens/Discover/types'
import navigationRef from '../../../../navigation'
import { useJellifyServer } from '../../../stores'
import { usePublicPlaylists } from '../../../api/queries/playlist'
import Animated, { FadeIn, LinearTransition } from 'react-native-reanimated'
import Animated, { FadeIn, FadeOut, LinearTransition } from 'react-native-reanimated'
export default function PublicPlaylists(): React.JSX.Element | null {
const {
@@ -31,7 +31,7 @@ export default function PublicPlaylists(): React.JSX.Element | null {
return publicPlaylistsExist ? (
<Animated.View
entering={FadeIn.springify()}
exiting={FadeIn.springify()}
exiting={FadeOut.springify()}
layout={LinearTransition.springify()}
testID='discover-public-playlists'
style={{
+2 -1
View File
@@ -1,8 +1,9 @@
import { ImageFormat } from '@jellyfin/sdk/lib/generated-client/models'
export enum MaxPages {
Home = 2,
Home = 3,
Library = 5,
Discover = 2,
}
export enum ApiLimits {