Discover Refactor (#752)

* Discover Refactor

tear down discover context provider

add animations to rows in discover screen

tidy up home screen refresh

* handle case where teh discover screen is empty on initial load
This commit is contained in:
Violet Caulfield
2025-12-05 04:49:32 -06:00
committed by GitHub
parent 67ed515723
commit 5f59567d70
14 changed files with 339 additions and 384 deletions

View File

@@ -1,5 +1,6 @@
{ {
"lockfileVersion": 1, "lockfileVersion": 1,
"configVersion": 0,
"workspaces": { "workspaces": {
"": { "": {
"name": "jellify", "name": "jellify",

View File

@@ -0,0 +1,23 @@
import { useMutation } from '@tanstack/react-query'
import { useRecentlyAddedAlbums } from '../../queries/album'
import { usePublicPlaylists } from '../../queries/playlist'
import { useDiscoverArtists } from '../../queries/suggestions'
const useDiscoverQueries = () => {
const { refetch: refetchRecentlyAdded } = useRecentlyAddedAlbums()
const { refetch: refetchPublicPlaylists } = usePublicPlaylists()
const { refetch: refetchArtistSuggestions } = useDiscoverArtists()
return useMutation({
mutationFn: async () =>
await Promise.allSettled([
refetchRecentlyAdded(),
refetchPublicPlaylists(),
refetchArtistSuggestions(),
]),
})
}
export default useDiscoverQueries

View File

@@ -16,12 +16,12 @@ const useHomeQueries = () => {
return useMutation({ return useMutation({
mutationFn: async () => { mutationFn: async () => {
await Promise.all([ await Promise.allSettled([
refetchRecentlyPlayed(), refetchRecentlyPlayed(),
refetchFrequentlyPlayed(), refetchFrequentlyPlayed(),
refetchUserPlaylists(), refetchUserPlaylists(),
]) ])
await Promise.all([refetchFrequentArtists(), refetchRecentArtists()]) await Promise.allSettled([refetchFrequentArtists(), refetchRecentArtists()])
return true return true
}, },
}) })

View File

@@ -39,3 +39,17 @@ export const usePlaylistTracks = (playlist: BaseItemDto) => {
}, },
}) })
} }
export const usePublicPlaylists = () => {
const api = useApi()
const [library] = useJellifyLibrary()
return useInfiniteQuery({
queryKey: [QueryKeys.PublicPlaylists, library?.playlistLibraryId],
queryFn: ({ pageParam }) => fetchPublicPlaylists(api, library, pageParam),
select: (data) => data.pages.flatMap((page) => page),
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) =>
lastPage.length > 0 ? lastPageParam + 1 : undefined,
initialPageParam: 0,
})
}

View File

@@ -0,0 +1,42 @@
import { useInfiniteQuery, useQuery } from '@tanstack/react-query'
import { SuggestionQueryKeys } from './keys'
import { fetchArtistSuggestions, fetchSearchSuggestions } from './utils/suggestions'
import { useApi, useJellifyLibrary, useJellifyUser } from '../../../stores'
import { isUndefined } from 'lodash'
export const useSearchSuggestions = () => {
const api = useApi()
const [library] = useJellifyLibrary()
const [user] = useJellifyUser()
return useQuery({
queryKey: [SuggestionQueryKeys.SearchSuggestions, library?.musicLibraryId],
queryFn: () => fetchSearchSuggestions(api, user, library?.musicLibraryId),
enabled: !isUndefined(library),
})
}
export const useDiscoverArtists = () => {
const api = useApi()
const [library] = useJellifyLibrary()
const [user] = useJellifyUser()
return useInfiniteQuery({
queryKey: [
SuggestionQueryKeys.InfiniteArtistSuggestions,
user?.id,
library?.musicLibraryId,
],
queryFn: ({ pageParam }) =>
fetchArtistSuggestions(api, user, library?.musicLibraryId, pageParam),
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) =>
lastPage.length > 0 ? lastPageParam + 1 : undefined,
select: (data) => data.pages.flatMap((page) => page),
initialPageParam: 0,
maxPages: 2,
})
}

View File

@@ -0,0 +1,4 @@
export enum SuggestionQueryKeys {
InfiniteArtistSuggestions,
SearchSuggestions,
}

View File

@@ -2,7 +2,7 @@ import { getArtistsApi, getItemsApi } from '@jellyfin/sdk/lib/utils/api'
import { BaseItemDto, BaseItemKind, ItemFields } from '@jellyfin/sdk/lib/generated-client/models' import { BaseItemDto, BaseItemKind, ItemFields } from '@jellyfin/sdk/lib/generated-client/models'
import { Api } from '@jellyfin/sdk' import { Api } from '@jellyfin/sdk'
import { isUndefined } from 'lodash' import { isUndefined } from 'lodash'
import { JellifyUser } from '../../types/JellifyUser' import { JellifyUser } from '../../../../types/JellifyUser'
/** /**
* Fetches search suggestions from the Jellyfin server * Fetches search suggestions from the Jellyfin server

View File

@@ -1,18 +1,21 @@
import React from 'react' import React from 'react'
import { getToken, ScrollView, useTheme, View, YStack } from 'tamagui' import { getToken, ScrollView, useTheme, YStack } from 'tamagui'
import RecentlyAdded from './helpers/just-added' import RecentlyAdded from './helpers/just-added'
import { useDiscoverContext } from '../../providers/Discover'
import { RefreshControl } from 'react-native' import { RefreshControl } from 'react-native'
import PublicPlaylists from './helpers/public-playlists' import PublicPlaylists from './helpers/public-playlists'
import SuggestedArtists from './helpers/suggested-artists' import SuggestedArtists from './helpers/suggested-artists'
import useDiscoverQueries from '../../api/mutations/discover'
import { useIsRestoring } from '@tanstack/react-query'
import { useRecentlyAddedAlbums } from '../../api/queries/album'
export default function Index(): React.JSX.Element { export default function Index(): React.JSX.Element {
const theme = useTheme() const theme = useTheme()
const { refreshing, refresh, publicPlaylists, suggestedArtistsInfiniteQuery } = const { mutateAsync: refreshAsync, isPending: refreshing } = useDiscoverQueries()
useDiscoverContext()
const publicPlaylistsLength = (publicPlaylists ?? []).length const isRestoring = useIsRestoring()
const { isPending: loadingInitialData } = useRecentlyAddedAlbums()
return ( return (
<ScrollView <ScrollView
@@ -25,29 +28,25 @@ export default function Index(): React.JSX.Element {
removeClippedSubviews removeClippedSubviews
refreshControl={ refreshControl={
<RefreshControl <RefreshControl
refreshing={refreshing} refreshing={refreshing || isRestoring || loadingInitialData}
onRefresh={refresh} onRefresh={refreshAsync}
tintColor={theme.primary.val} tintColor={theme.primary.val}
/> />
} }
> >
<YStack gap={'$3'}> <DiscoverContent />
<View testID='discover-recently-added'>
<RecentlyAdded />
</View>
{publicPlaylistsLength > 0 && (
<View testID='discover-public-playlists'>
<PublicPlaylists />
</View>
)}
{suggestedArtistsInfiniteQuery.data && (
<View testID='discover-suggested-artists'>
<SuggestedArtists />
</View>
)}
</YStack>
</ScrollView> </ScrollView>
) )
} }
function DiscoverContent() {
return (
<YStack gap={'$3'}>
<RecentlyAdded />
<PublicPlaylists />
<SuggestedArtists />
</YStack>
)
}

View File

@@ -1,59 +1,65 @@
import { NativeStackNavigationProp } from '@react-navigation/native-stack' import { NativeStackNavigationProp } from '@react-navigation/native-stack'
import HorizontalCardList from '../../../components/Global/components/horizontal-list' import HorizontalCardList from '../../../components/Global/components/horizontal-list'
import { ItemCard } from '../../../components/Global/components/item-card' import { ItemCard } from '../../../components/Global/components/item-card'
import { useDiscoverContext } from '../../../providers/Discover'
import { H5, View, XStack } from 'tamagui' import { H5, View, XStack } from 'tamagui'
import { H4 } from '../../../components/Global/helpers/text'
import Icon from '../../Global/components/icon' import Icon from '../../Global/components/icon'
import { useNavigation } from '@react-navigation/native' import { useNavigation } from '@react-navigation/native'
import DiscoverStackParamList from '../../../screens/Discover/types' import DiscoverStackParamList from '../../../screens/Discover/types'
import navigationRef from '../../../../navigation' import navigationRef from '../../../../navigation'
import { useRecentlyAddedAlbums } from '../../../api/queries/album' import { useRecentlyAddedAlbums } from '../../../api/queries/album'
import Animated, { FadeIn, FadeOut, LinearTransition } from 'react-native-reanimated'
export default function RecentlyAdded(): React.JSX.Element { export default function RecentlyAdded(): React.JSX.Element | undefined {
const recentlyAddedAlbumsInfinityQuery = useRecentlyAddedAlbums() const recentlyAddedAlbumsInfinityQuery = useRecentlyAddedAlbums()
const navigation = useNavigation<NativeStackNavigationProp<DiscoverStackParamList>>() const navigation = useNavigation<NativeStackNavigationProp<DiscoverStackParamList>>()
return ( return (
<View> recentlyAddedAlbumsInfinityQuery.data && (
<XStack <Animated.View
alignItems='center' entering={FadeIn.springify()}
onPress={() => { exiting={FadeOut.springify()}
navigation.navigate('RecentlyAdded', { layout={LinearTransition.springify()}
albumsInfiniteQuery: recentlyAddedAlbumsInfinityQuery, testID='discover-recently-added'
})
}}
> >
<H5 marginLeft={'$2'}>Recently Added</H5> <XStack
<Icon name='arrow-right' /> alignItems='center'
</XStack> onPress={() => {
navigation.navigate('RecentlyAdded', {
albumsInfiniteQuery: recentlyAddedAlbumsInfinityQuery,
})
}}
>
<H5 marginLeft={'$2'}>Recently Added</H5>
<Icon name='arrow-right' />
</XStack>
<HorizontalCardList <HorizontalCardList
data={recentlyAddedAlbumsInfinityQuery.data?.slice(0, 10) ?? []} data={recentlyAddedAlbumsInfinityQuery.data?.slice(0, 10) ?? []}
renderItem={({ item }) => ( renderItem={({ item }) => (
<ItemCard <ItemCard
caption={item.Name} caption={item.Name}
subCaption={`${item.Artists?.join(', ')}`} subCaption={`${item.Artists?.join(', ')}`}
squared squared
size={'$11'} size={'$11'}
item={item} item={item}
onPress={() => { onPress={() => {
navigation.navigate('Album', { navigation.navigate('Album', {
album: item, album: item,
}) })
}} }}
onLongPress={() => { onLongPress={() => {
navigationRef.navigate('Context', { navigationRef.navigate('Context', {
item, item,
navigation, navigation,
}) })
}} }}
gap={'$1'} gap={'$1'}
captionAlign='left' captionAlign='left'
/> />
)} )}
/> />
</View> </Animated.View>
)
) )
} }

View File

@@ -1,5 +1,4 @@
import { H5, View, XStack } from 'tamagui' import { H5, XStack } from 'tamagui'
import { useDiscoverContext } from '../../../providers/Discover'
import { NativeStackNavigationProp } from '@react-navigation/native-stack' import { NativeStackNavigationProp } from '@react-navigation/native-stack'
import Icon from '../../Global/components/icon' import Icon from '../../Global/components/icon'
import HorizontalCardList from '../../Global/components/horizontal-list' import HorizontalCardList from '../../Global/components/horizontal-list'
@@ -9,65 +8,74 @@ import { useNavigation } from '@react-navigation/native'
import DiscoverStackParamList from '../../../screens/Discover/types' import DiscoverStackParamList from '../../../screens/Discover/types'
import navigationRef from '../../../../navigation' import navigationRef from '../../../../navigation'
import { useJellifyServer } from '../../../stores' import { useJellifyServer } from '../../../stores'
import { usePublicPlaylists } from '../../../api/queries/playlist'
import Animated, { FadeIn, LinearTransition } from 'react-native-reanimated'
export default function PublicPlaylists() { export default function PublicPlaylists() {
const { const {
publicPlaylists, data: playlists,
fetchNextPublicPlaylists, fetchNextPage,
hasNextPublicPlaylists, hasNextPage,
isFetchingNextPublicPlaylists, isPending,
isPendingPublicPlaylists, isFetchingNextPage,
refetchPublicPlaylists, refetch,
} = useDiscoverContext() } = usePublicPlaylists()
const navigation = useNavigation<NativeStackNavigationProp<DiscoverStackParamList>>() const navigation = useNavigation<NativeStackNavigationProp<DiscoverStackParamList>>()
const [server] = useJellifyServer() const [server] = useJellifyServer()
const { width } = useSafeAreaFrame() const { width } = useSafeAreaFrame()
return ( return (
<View> playlists && (
<XStack <Animated.View
alignItems='center' entering={FadeIn.springify()}
onPress={() => { exiting={FadeIn.springify()}
navigation.navigate('PublicPlaylists', { layout={LinearTransition.springify()}
playlists: publicPlaylists, testID='discover-public-playlists'
navigation: navigation,
fetchNextPage: fetchNextPublicPlaylists,
hasNextPage: hasNextPublicPlaylists,
isPending: isPendingPublicPlaylists,
isFetchingNextPage: isFetchingNextPublicPlaylists,
refetch: refetchPublicPlaylists,
})
}}
> >
<H5 marginLeft={'$2'} lineBreakStrategyIOS='standard' maxWidth={width * 0.8}> <XStack
Playlists on {server?.name ?? 'Jellyfin'} alignItems='center'
</H5> onPress={() => {
<Icon name='arrow-right' /> navigation.navigate('PublicPlaylists', {
</XStack> playlists,
<HorizontalCardList navigation: navigation,
data={publicPlaylists?.slice(0, 10) ?? []} fetchNextPage,
renderItem={({ item }) => ( hasNextPage,
<ItemCard isPending,
caption={item.Name} isFetchingNextPage,
subCaption={`${item.Genres?.join(', ')}`} refetch,
squared })
size={'$10'} }}
item={item} >
onPress={() => { <H5 marginLeft={'$2'} lineBreakStrategyIOS='standard' maxWidth={width * 0.8}>
navigation.navigate('Playlist', { playlist: item, canEdit: false }) Playlists on {server?.name ?? 'Jellyfin'}
}} </H5>
onLongPress={() => <Icon name='arrow-right' />
navigationRef.navigate('Context', { </XStack>
item, <HorizontalCardList
navigation, data={playlists?.slice(0, 10) ?? []}
}) renderItem={({ item }) => (
} <ItemCard
marginHorizontal={'$1'} caption={item.Name}
captionAlign='left' subCaption={`${item.Genres?.join(', ')}`}
/> squared
)} size={'$10'}
/> item={item}
</View> onPress={() => {
navigation.navigate('Playlist', { playlist: item, canEdit: false })
}}
onLongPress={() =>
navigationRef.navigate('Context', {
item,
navigation,
})
}
marginHorizontal={'$1'}
captionAlign='left'
/>
)}
/>
</Animated.View>
)
) )
} }

View File

@@ -3,54 +3,62 @@ import Icon from '../../Global/components/icon'
import HorizontalCardList from '../../Global/components/horizontal-list' import HorizontalCardList from '../../Global/components/horizontal-list'
import { ItemCard } from '../../Global/components/item-card' import { ItemCard } from '../../Global/components/item-card'
import { NativeStackNavigationProp } from '@react-navigation/native-stack' import { NativeStackNavigationProp } from '@react-navigation/native-stack'
import { useDiscoverContext } from '../../../providers/Discover'
import { useNavigation } from '@react-navigation/native' import { useNavigation } from '@react-navigation/native'
import DiscoverStackParamList from '../../../screens/Discover/types' import DiscoverStackParamList from '../../../screens/Discover/types'
import navigationRef from '../../../../navigation' import navigationRef from '../../../../navigation'
import { pickFirstGenre } from '../../../utils/genre-formatting' import { pickFirstGenre } from '../../../utils/genre-formatting'
import Animated, { FadeIn, FadeOut, LinearTransition } from 'react-native-reanimated'
import { useDiscoverArtists } from '../../../api/queries/suggestions'
export default function SuggestedArtists(): React.JSX.Element { export default function SuggestedArtists(): React.JSX.Element | undefined {
const { suggestedArtistsInfiniteQuery } = useDiscoverContext() const suggestedArtistsInfiniteQuery = useDiscoverArtists()
const navigation = useNavigation<NativeStackNavigationProp<DiscoverStackParamList>>() const navigation = useNavigation<NativeStackNavigationProp<DiscoverStackParamList>>()
return ( return (
<View> suggestedArtistsInfiniteQuery.data && (
<XStack <Animated.View
alignItems='center' entering={FadeIn.springify()}
onPress={() => { exiting={FadeOut.springify()}
navigation.navigate('SuggestedArtists', { layout={LinearTransition.springify()}
artistsInfiniteQuery: suggestedArtistsInfiniteQuery, testID='discover-suggested-artists'
navigation: navigation,
})
}}
marginLeft={'$2'}
> >
<H5>Suggested Artists</H5> <XStack
<Icon name='arrow-right' /> alignItems='center'
</XStack> onPress={() => {
<HorizontalCardList navigation.navigate('SuggestedArtists', {
data={suggestedArtistsInfiniteQuery.data?.slice(0, 10) ?? []} artistsInfiniteQuery: suggestedArtistsInfiniteQuery,
renderItem={({ item }) => ( navigation: navigation,
<ItemCard })
caption={item.Name} }}
subCaption={pickFirstGenre(item.Genres)} marginLeft={'$2'}
size={'$10'} >
item={item} <H5>Suggested Artists</H5>
onPress={() => { <Icon name='arrow-right' />
navigation.navigate('Artist', { </XStack>
artist: item, <HorizontalCardList
}) data={suggestedArtistsInfiniteQuery.data?.slice(0, 10) ?? []}
}} renderItem={({ item }) => (
onLongPress={() => <ItemCard
navigationRef.navigate('Context', { caption={item.Name}
item, subCaption={pickFirstGenre(item.Genres)}
navigation, size={'$10'}
}) item={item}
} onPress={() => {
/> navigation.navigate('Artist', {
)} artist: item,
/> })
</View> }}
onLongPress={() =>
navigationRef.navigate('Context', {
item,
navigation,
})
}
/>
)}
/>
</Animated.View>
)
) )
} }

View File

@@ -6,7 +6,7 @@ import { QueryKeys } from '../../enums/query-keys'
import { fetchSearchResults } from '../../api/queries/search' import { fetchSearchResults } from '../../api/queries/search'
import { useQuery } from '@tanstack/react-query' import { useQuery } from '@tanstack/react-query'
import { FlatList } from 'react-native' import { FlatList } from 'react-native'
import { fetchSearchSuggestions } from '../../api/queries/suggestions' import { fetchSearchSuggestions } from '../../api/queries/suggestions/utils/suggestions'
import { getToken, H3, Separator, Spinner, YStack } from 'tamagui' import { getToken, H3, Separator, Spinner, YStack } from 'tamagui'
import Suggestions from './suggestions' import Suggestions from './suggestions'
import { isEmpty } from 'lodash' import { isEmpty } from 'lodash'
@@ -15,6 +15,7 @@ import { ItemCard } from '../Global/components/item-card'
import SearchParamList from '../../screens/Search/types' import SearchParamList from '../../screens/Search/types'
import { closeAllSwipeableRows } from '../Global/components/swipeable-row-registry' import { closeAllSwipeableRows } from '../Global/components/swipeable-row-registry'
import { useApi, useJellifyLibrary, useJellifyUser } from '../../stores' import { useApi, useJellifyLibrary, useJellifyUser } from '../../stores'
import { useSearchSuggestions } from '../../api/queries/suggestions'
export default function Search({ export default function Search({
navigation, navigation,
@@ -40,10 +41,7 @@ export default function Search({
data: suggestions, data: suggestions,
isFetching: fetchingSuggestions, isFetching: fetchingSuggestions,
refetch: refetchSuggestions, refetch: refetchSuggestions,
} = useQuery({ } = useSearchSuggestions()
queryKey: [QueryKeys.SearchSuggestions, library?.musicLibraryId],
queryFn: () => fetchSearchSuggestions(api, user, library?.musicLibraryId),
})
const search = () => { const search = () => {
let timeout: ReturnType<typeof setTimeout> let timeout: ReturnType<typeof setTimeout>

View File

@@ -1,145 +0,0 @@
import {
InfiniteQueryObserverResult,
useInfiniteQuery,
UseInfiniteQueryResult,
} from '@tanstack/react-query'
import { QueryKeys } from '../../enums/query-keys'
import { createContext, ReactNode, useContext, useState } from 'react'
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
import { fetchPublicPlaylists } from '../../api/queries/playlist/utils'
import { fetchArtistSuggestions } from '../../api/queries/suggestions'
import { useRefetchRecentlyAdded } from '../../api/queries/album'
import { useApi, useJellifyUser, useJellifyLibrary } from '../../stores'
interface DiscoverContext {
refreshing: boolean
refresh: () => void
publicPlaylists: BaseItemDto[] | undefined
fetchNextPublicPlaylists: () => void
hasNextPublicPlaylists: boolean
isPendingPublicPlaylists: boolean
isFetchingNextPublicPlaylists: boolean
refetchPublicPlaylists: () => void
suggestedArtistsInfiniteQuery: UseInfiniteQueryResult<BaseItemDto[], Error>
}
const DiscoverContextInitializer = () => {
const api = useApi()
const [user] = useJellifyUser()
const [library] = useJellifyLibrary()
const [refreshing, setRefreshing] = useState<boolean>(false)
const refetchRecentlyAdded = useRefetchRecentlyAdded()
const {
data: publicPlaylists,
refetch: refetchPublicPlaylists,
fetchNextPage: fetchNextPublicPlaylists,
hasNextPage: hasNextPublicPlaylists,
isPending: isPendingPublicPlaylists,
isFetchingNextPage: isFetchingNextPublicPlaylists,
} = useInfiniteQuery({
queryKey: [QueryKeys.PublicPlaylists, library?.playlistLibraryId],
queryFn: ({ pageParam }) => fetchPublicPlaylists(api, library, pageParam),
select: (data) => data.pages.flatMap((page) => page),
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) =>
lastPage.length > 0 ? lastPageParam + 1 : undefined,
initialPageParam: 0,
})
const suggestedArtistsInfiniteQuery = useInfiniteQuery({
queryKey: [QueryKeys.InfiniteSuggestedArtists, user?.id, library?.musicLibraryId],
queryFn: ({ pageParam }) =>
fetchArtistSuggestions(api, user, library?.musicLibraryId, pageParam),
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) =>
lastPage.length > 0 ? lastPageParam + 1 : undefined,
select: (data) => data.pages.flatMap((page) => page),
initialPageParam: 0,
maxPages: 2,
})
const refresh = async () => {
setRefreshing(true)
await Promise.all([
refetchRecentlyAdded(),
refetchPublicPlaylists(),
suggestedArtistsInfiniteQuery.refetch(),
])
setRefreshing(false)
}
return {
refreshing,
refresh,
publicPlaylists,
fetchNextPublicPlaylists,
hasNextPublicPlaylists,
isPendingPublicPlaylists,
isFetchingNextPublicPlaylists,
refetchPublicPlaylists,
suggestedArtistsInfiniteQuery,
}
}
const DiscoverContext = createContext<DiscoverContext>({
refreshing: false,
refresh: () => {},
publicPlaylists: undefined,
fetchNextPublicPlaylists: () => {},
hasNextPublicPlaylists: false,
isPendingPublicPlaylists: false,
isFetchingNextPublicPlaylists: false,
refetchPublicPlaylists: () => {},
suggestedArtistsInfiniteQuery: {
data: undefined,
error: null,
isEnabled: true,
isStale: false,
isRefetching: false,
isError: false,
isLoading: true,
isPending: true,
isFetching: true,
isSuccess: false,
isFetched: false,
hasPreviousPage: false,
refetch: async () =>
Promise.resolve({} as InfiniteQueryObserverResult<BaseItemDto[], Error>),
fetchNextPage: async () =>
Promise.resolve({} as InfiniteQueryObserverResult<BaseItemDto[], Error>),
hasNextPage: false,
isFetchingNextPage: false,
isFetchPreviousPageError: false,
isFetchNextPageError: false,
isFetchingPreviousPage: false,
isLoadingError: false,
isRefetchError: false,
isPlaceholderData: false,
status: 'pending',
fetchStatus: 'idle',
dataUpdatedAt: 0,
errorUpdatedAt: 0,
failureCount: 0,
failureReason: null,
errorUpdateCount: 0,
isFetchedAfterMount: false,
isInitialLoading: false,
isPaused: false,
fetchPreviousPage: async () =>
Promise.resolve({} as InfiniteQueryObserverResult<BaseItemDto[], Error>),
promise: Promise.resolve([]),
},
})
export const DiscoverProvider: ({ children }: { children: ReactNode }) => React.JSX.Element = ({
children,
}: {
children: ReactNode
}) => {
const context = DiscoverContextInitializer()
return <DiscoverContext.Provider value={context}>{children}</DiscoverContext.Provider>
}
export const useDiscoverContext = () => useContext(DiscoverContext)

View File

@@ -2,7 +2,6 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack'
import Index from '../../components/Discover/component' import Index from '../../components/Discover/component'
import AlbumScreen from '../Album' import AlbumScreen from '../Album'
import { ArtistScreen } from '../Artist' import { ArtistScreen } from '../Artist'
import { DiscoverProvider } from '../../providers/Discover'
import { useTheme } from 'tamagui' import { useTheme } from 'tamagui'
import RecentlyAdded from './albums' import RecentlyAdded from './albums'
import PublicPlaylists from './playlists' import PublicPlaylists from './playlists'
@@ -18,87 +17,85 @@ export function Discover(): React.JSX.Element {
const theme = useTheme() const theme = useTheme()
return ( return (
<DiscoverProvider> <DiscoverStack.Navigator initialRouteName='Discover'>
<DiscoverStack.Navigator initialRouteName='Discover'> <DiscoverStack.Screen
<DiscoverStack.Screen name='Discover'
name='Discover' component={Index}
component={Index} options={{
options={{ headerTitleStyle: {
headerTitleStyle: { fontFamily: 'Figtree-Bold',
fontFamily: 'Figtree-Bold', },
}, }}
}} />
/>
<DiscoverStack.Screen <DiscoverStack.Screen
name='Artist' name='Artist'
component={ArtistScreen} component={ArtistScreen}
options={({ route }) => ({ options={({ route }) => ({
title: route.params.artist.Name ?? 'Unknown Artist', title: route.params.artist.Name ?? 'Unknown Artist',
headerTitleStyle: { headerTitleStyle: {
color: theme.background.val, color: theme.background.val,
}, },
})} })}
/> />
<DiscoverStack.Screen <DiscoverStack.Screen
name='Album' name='Album'
component={AlbumScreen} component={AlbumScreen}
options={({ route }) => ({ options={({ route }) => ({
title: route.params.album.Name ?? 'Untitled Album', title: route.params.album.Name ?? 'Untitled Album',
headerTitleStyle: { headerTitleStyle: {
color: theme.background.val, color: theme.background.val,
}, },
})} })}
/> />
<DiscoverStack.Screen <DiscoverStack.Screen
name='Playlist' name='Playlist'
component={PlaylistScreen} component={PlaylistScreen}
options={({ route }) => ({ options={({ route }) => ({
title: route.params.playlist.Name ?? 'Untitled Playlist', title: route.params.playlist.Name ?? 'Untitled Playlist',
})} })}
/> />
<DiscoverStack.Screen <DiscoverStack.Screen
name='RecentlyAdded' name='RecentlyAdded'
component={RecentlyAdded} component={RecentlyAdded}
options={{ options={{
title: 'Recently Added', title: 'Recently Added',
headerTitleStyle: { headerTitleStyle: {
fontFamily: 'Figtree-Bold', fontFamily: 'Figtree-Bold',
}, },
}} }}
/> />
<DiscoverStack.Screen <DiscoverStack.Screen
name='PublicPlaylists' name='PublicPlaylists'
component={PublicPlaylists} component={PublicPlaylists}
options={{ options={{
title: 'Public Playlists', title: 'Public Playlists',
headerTitleStyle: { headerTitleStyle: {
fontFamily: 'Figtree-Bold', fontFamily: 'Figtree-Bold',
color: theme.background.val, color: theme.background.val,
}, },
}} }}
/> />
<DiscoverStack.Screen <DiscoverStack.Screen
name='SuggestedArtists' name='SuggestedArtists'
component={SuggestedArtists} component={SuggestedArtists}
options={{ options={{
title: 'Suggested Artists', title: 'Suggested Artists',
}} }}
/> />
<DiscoverStack.Screen <DiscoverStack.Screen
name='InstantMix' name='InstantMix'
component={InstantMix} component={InstantMix}
options={({ route }) => ({ options={({ route }) => ({
headerTitle: `${getItemName(route.params.item)} Mix`, headerTitle: `${getItemName(route.params.item)} Mix`,
})} })}
/> />
</DiscoverStack.Navigator> </DiscoverStack.Navigator>
</DiscoverProvider>
) )
} }