context sheet prefetching

player style fixes
This commit is contained in:
Violet Caulfield
2025-08-20 07:08:44 -05:00
parent 0112da90e1
commit 24d3aa475c
8 changed files with 82 additions and 66 deletions

View File

@@ -12,7 +12,7 @@ import Icon from '../Global/components/icon'
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
import { useQuery } from '@tanstack/react-query'
import { QueryKeys } from '../../enums/query-keys'
import { fetchItem, fetchItems } from '../../api/queries/item'
import { fetchAlbumDiscs, fetchItem, fetchItems } from '../../api/queries/item'
import { useJellifyContext } from '../../providers'
import { getItemsApi } from '@jellyfin/sdk/lib/utils/api'
import { useAddToQueueContext } from '../../providers/Player/queue'
@@ -38,20 +38,10 @@ interface ContextProps {
}
export default function ItemContext({ item, stackNavigation }: ContextProps): React.JSX.Element {
const { api, user, library } = useJellifyContext()
const { api } = useJellifyContext()
const { bottom } = useSafeAreaInsets()
const bottomMargin = useMemo(() => {
const isAndroid = Platform.OS === 'android'
let finalMargin = bottom
if (isAndroid) finalMargin += getTokenValue('$12')
return finalMargin
}, [bottom])
const isArtist = item.Type === BaseItemKind.MusicArtist
const isAlbum = item.Type === BaseItemKind.MusicAlbum
const isTrack = item.Type === BaseItemKind.Audio
@@ -74,6 +64,13 @@ export default function ItemContext({ item, stackNavigation }: ContextProps): Re
if (data.Items) return data.Items
else return []
}),
enabled: isPlaylist,
})
const { data: discs } = useQuery({
queryKey: [QueryKeys.ItemTracks, item.Id],
queryFn: () => fetchAlbumDiscs(api, item),
enabled: isAlbum,
})
const renderAddToQueueRow = isTrack || (isAlbum && tracks) || (isPlaylist && tracks)
@@ -84,10 +81,22 @@ export default function ItemContext({ item, stackNavigation }: ContextProps): Re
return (
<ScrollView>
<YGroup unstyled marginBottom={bottomMargin}>
<YGroup unstyled marginBottom={bottom}>
<FavoriteContextMenuRow item={item} />
{renderAddToQueueRow && <AddToQueueMenuRow tracks={isTrack ? [item] : tracks!} />}
{renderAddToQueueRow && (
<AddToQueueMenuRow
tracks={
isTrack
? [item]
: isAlbum && discs
? discs.flatMap((data) => data.data)
: isPlaylist && tracks
? tracks
: []
}
/>
)}
{renderAddToPlaylistRow && <AddToPlaylistRow track={item} />}
@@ -100,7 +109,7 @@ export default function ItemContext({ item, stackNavigation }: ContextProps): Re
{!isPlaylist && (
<ViewArtistMenuRow
artists={isArtist ? [item] : item.ArtistItems ? item.ArtistItems : []}
artists={isArtist ? [item] : itemArtists}
stackNavigation={stackNavigation}
/>
)}
@@ -109,29 +118,6 @@ export default function ItemContext({ item, stackNavigation }: ContextProps): Re
)
}
function ItemContextBackground({ item }: { item: BaseItemDto }): React.JSX.Element {
return (
<ZStack flex={1}>
<BackgroundBlur item={item} />
<BackgroundGradient />
</ZStack>
)
}
function BackgroundBlur({ item }: { item: BaseItemDto }): React.JSX.Element {
const blurhash = getPrimaryBlurhashFromDto(item)
return (
<Blurhash
blurhash={blurhash!}
style={{
flex: 1,
}}
/>
)
}
function AddToPlaylistRow({ track }: { track: BaseItemDto }): React.JSX.Element {
return (
<ListItem

View File

@@ -85,7 +85,7 @@ function getBorderRadius(circular: boolean | undefined, width: Token | number |
} else if (!isUndefined(width)) {
borderRadius =
typeof width === 'number'
? width / 6
? width / 25
: getTokenValue(width) / (getTokenValue(width) / 6)
} else borderRadius = '5%'

View File

@@ -11,7 +11,7 @@ import { QueryKeys } from '../../../enums/query-keys'
import { getQualityParams } from '../../../utils/mappings'
import { useStreamingQualityContext } from '../../../providers/Settings'
import { useQuery } from '@tanstack/react-query'
import { fetchItem } from '../../../api/queries/item'
import { fetchAlbumDiscs, fetchItem } from '../../../api/queries/item'
interface CardProps extends TamaguiCardProps {
caption?: string | null | undefined
@@ -36,15 +36,36 @@ export function ItemCard(props: CardProps) {
queryKey: [QueryKeys.MediaSources, streamingQuality, props.item.Id],
queryFn: () => fetchMediaInfo(api, user, getQualityParams(streamingQuality), props.item),
staleTime: Infinity, // Don't refetch media info unless the user changes the quality
enabled: props.item.Type === 'Audio',
enabled: props.item.Type === BaseItemKind.Audio,
})
/**
* Fire query for a track's album
*
* Referenced later in the context sheet
*/
useQuery({
queryKey: [QueryKeys.Album, props.item.AlbumId],
queryFn: () => fetchItem(api, props.item.AlbumId!),
enabled: props.item.Type === BaseItemKind.Audio && !!props.item.AlbumId,
})
/**
* Fire query for an album's tracks
*
* Referenced later in the context sheet
*/
useQuery({
queryKey: [QueryKeys.ItemTracks, props.item.Id],
queryFn: () => fetchAlbumDiscs(api, props.item),
enabled: !!props.item.Id && props.item.Type === BaseItemKind.MusicAlbum,
})
/**
* Fire query for an playlist's tracks
*
* Referenced later in the context sheet
*/
useQuery({
queryKey: [QueryKeys.ItemTracks, props.item.Id],
queryFn: () =>
@@ -54,7 +75,7 @@ export function ItemCard(props: CardProps) {
if (data.Items) return data.Items
else return []
}),
enabled: !!props.item.Id && props.item.Type === BaseItemKind.MusicAlbum,
enabled: !!props.item.Id && props.item.Type === BaseItemKind.Playlist,
})
return (

View File

@@ -18,7 +18,7 @@ import { useStreamingQualityContext } from '../../../providers/Settings'
import navigationRef from '../../../../navigation'
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
import { BaseStackParamList } from '../../../screens/types'
import { fetchItem } from '../../../api/queries/item'
import { fetchAlbumDiscs, fetchItem } from '../../../api/queries/item'
import { getItemsApi } from '@jellyfin/sdk/lib/utils/api'
interface ItemRowProps {
@@ -79,6 +79,17 @@ export default function ItemRow({
*
* Referenced later in the context sheet
*/
useQuery({
queryKey: [QueryKeys.ItemTracks, item.Id],
queryFn: () => fetchAlbumDiscs(api, item),
enabled: !!item.Id && item.Type === BaseItemKind.MusicAlbum,
})
/**
* Fire query for an playlist's tracks
*
* Referenced later in the context sheet
*/
useQuery({
queryKey: [QueryKeys.ItemTracks, item.Id],
queryFn: () =>
@@ -88,7 +99,7 @@ export default function ItemRow({
if (data.Items) return data.Items
else return []
}),
enabled: !!item.Id && item.Type === BaseItemKind.MusicAlbum,
enabled: !!item.Id && item.Type === BaseItemKind.Playlist,
})
const gestureCallback = () => {

View File

@@ -129,7 +129,7 @@ export default function Track({
item: track,
})
}
}, [onLongPress, navigation, track, isNested])
}, [onLongPress, track, isNested])
const handleIconPress = useCallback(() => {
if (showRemove) {
@@ -139,7 +139,7 @@ export default function Track({
item: track,
})
}
}, [showRemove, onRemove, navigation, track, isNested])
}, [showRemove, onRemove, track, isNested])
// Only fetch media info if needed (for streaming)
useQuery({

View File

@@ -1,19 +1,18 @@
import { InstantMixProps } from '../../screens/types'
import { FlatList } from 'react-native'
import Track from '../Global/components/track'
import { Separator } from 'tamagui'
import { FlashList } from '@shopify/flash-list'
export default function InstantMix({ route, navigation }: InstantMixProps): React.JSX.Element {
const { mix } = route.params
return (
<FlatList
<FlashList
contentInsetAdjustmentBehavior='automatic'
data={mix}
ItemSeparatorComponent={() => <Separator />}
renderItem={({ item, index }) => (
<Track
navigation={navigation}
showArtwork
track={item}
index={index}

View File

@@ -1,13 +1,13 @@
import { useNowPlayingContext } from '../../../providers/Player'
import { useQueueRefContext } from '../../../providers/Player/queue'
import { XStack, YStack, Spacer, useTheme, getTokenValue, TamaguiElement } from 'tamagui'
import { XStack, YStack, Spacer, useTheme, getTokenValue } from 'tamagui'
import { Text } from '../../Global/helpers/text'
import React, { useMemo, useRef } from 'react'
import React, { useMemo } from 'react'
import ItemImage from '../../Global/components/image'
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'
import { Platform } from 'react-native'
import { useSafeAreaFrame } from 'react-native-safe-area-context'
import MaterialDesignIcons from '@react-native-vector-icons/material-design-icons'
import navigationRef from '../../../../navigation'
export default function PlayerHeader(): React.JSX.Element {
const imageBounds = getTokenValue('$20') * 2
@@ -26,7 +26,12 @@ export default function PlayerHeader(): React.JSX.Element {
return (
<YStack flexGrow={1} justifyContent='flex-start' maxHeight={'80%'}>
<XStack flexShrink={1} alignContent='flex-start' justifyContent='center'>
<XStack
alignContent='flex-start'
flexShrink={1}
justifyContent='center'
onPress={() => navigationRef.goBack()}
>
<MaterialDesignIcons
color={theme.color.val}
name={Platform.OS === 'android' ? 'chevron-left' : 'chevron-down'}
@@ -44,23 +49,18 @@ export default function PlayerHeader(): React.JSX.Element {
<Spacer flex={1} />
</XStack>
<YStack
flexGrow={1}
marginVertical={'auto'}
maxHeight={'65%'}
paddingVertical={Platform.OS === 'android' ? '$2' : undefined}
paddingHorizontal={Platform.OS === 'ios' ? '$3' : '$2'}
maxWidth={'100%'}
>
<YStack flexGrow={1} justifyContent='center'>
<Animated.View
entering={FadeIn}
exiting={FadeOut}
style={{
flex: 1,
}}
key={`${nowPlaying!.item.AlbumId}-item-image`}
>
<ItemImage item={nowPlaying!.item} testID='player-image-test-id' />
<ItemImage
item={nowPlaying!.item}
testID='player-image-test-id'
width={360}
height={360}
/>
</Animated.View>
</YStack>
</YStack>

View File

@@ -1,7 +1,6 @@
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
import { NativeStackScreenProps } from '@react-navigation/native-stack'
import { BaseStackParamList } from '../types'
import { Queue } from '../../player/types/queue-item'
import { NavigatorScreenParams } from '@react-navigation/native'
type LibraryStackParamList = BaseStackParamList & {