hook consolidation, fix tests

This commit is contained in:
Violet Caulfield
2026-03-12 09:15:39 -05:00
parent 972ec260c3
commit 25eefacd96
11 changed files with 46 additions and 61 deletions

View File

@@ -57,10 +57,8 @@ describe('Player Controls', () => {
await previous()
// At exactly threshold, Math.floor(4) = 4, which is NOT < 4, so seek to 0
expect(TrackPlayer.seek).toHaveBeenCalledWith(0)
expect(TrackPlayer.skipToPrevious).not.toHaveBeenCalled()
expect(TrackPlayer.play).toHaveBeenCalled()
expect(TrackPlayer.skipToPrevious).toHaveBeenCalled()
expect(TrackPlayer.play).not.toHaveBeenCalled()
})
})

View File

@@ -1,6 +1,6 @@
{
"name": "jellify",
"version": "1.1.0-alpha.5",
"version": "1.1.0-alpha.6",
"private": true,
"scripts": {
"init-android": "bun i",

View File

@@ -19,6 +19,9 @@ import useLibraryStore from '../../../stores/library'
import { fetchAlbumDiscs } from '../item'
import { Api } from '@jellyfin/sdk/lib/api'
import { AlbumDiscsQueryKey } from './keys'
import { AlbumQuery } from './queries'
export const useAlbum = (album: BaseItemDto) => useQuery(AlbumQuery(album))
const useAlbums: () => [
RefObject<Set<string>>,

View File

@@ -1,7 +1,10 @@
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client'
enum AlbumQueryKeys {
AlbumById,
AlbumDiscs,
}
export const AlbumQueryKey = (album: BaseItemDto) => [AlbumQueryKeys.AlbumById, album.Id]
export const AlbumDiscsQueryKey = (album: BaseItemDto) => [AlbumQueryKeys.AlbumDiscs, album.Id]

View File

@@ -0,0 +1,16 @@
import { ONE_DAY } from '../../../constants/query-client'
import { getApi } from '../../../stores'
import { fetchItem } from '../item'
import { AlbumQueryKey } from './keys'
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models/base-item-dto'
export const AlbumQuery = (album: BaseItemDto) => {
const api = getApi()
return {
queryKey: AlbumQueryKey(album),
queryFn: () => fetchItem(api, album.Id! as string),
enabled: !!album.Id && !!api,
staleTime: ONE_DAY,
}
}

View File

@@ -10,17 +10,12 @@ import Icon from '../Global/components/icon'
import { useNavigation } from '@react-navigation/native'
import { BaseStackParamList } from '../../screens/types'
import { closeAllSwipeableRows } from '../Global/components/swipeable-row-registry'
import { getApi } from '../../stores'
import { QueryKeys } from '../../enums/query-keys'
import { fetchAlbumDiscs } from '../../api/queries/item'
import { useQuery } from '@tanstack/react-query'
import AlbumTrackListFooter from './footer'
import AlbumTrackListHeader from './header'
import Animated, { Easing, FadeIn, FadeOut, LinearTransition } from 'react-native-reanimated'
import { useStorageContext } from '../../providers/Storage'
import { useIsDownloaded } from '../../hooks/downloads'
import useDownloadTracks, { useDeleteDownloads } from '../../hooks/downloads/mutations'
import { useDownloadProgress } from 'react-native-nitro-player'
import { useAlbumDiscs } from '../../api/queries/album'
/**
* The screen for an Album's track list
@@ -33,18 +28,9 @@ import { useDownloadProgress } from 'react-native-nitro-player'
export function Album({ album }: { album: BaseItemDto }): React.JSX.Element {
const navigation = useNavigation<NativeStackNavigationProp<BaseStackParamList>>()
const api = getApi()
const theme = useTheme()
const {
data: discs,
isPending,
refetch,
} = useQuery({
queryKey: [QueryKeys.ItemTracks, album.Id],
queryFn: () => fetchAlbumDiscs(api, album),
})
const { data: discs, isPending, refetch } = useAlbumDiscs(album)
const downloadTracks = useDownloadTracks()

View File

@@ -24,7 +24,7 @@ import { StackActions } from '@react-navigation/native'
import TextTicker from 'react-native-text-ticker'
import { TextTickerConfig } from '../Player/component.config'
import { triggerHaptic } from '../../hooks/use-haptic-feedback'
import { useApi } from '../../stores'
import { getApi } from '../../stores'
import DeletePlaylistRow from './components/delete-playlist-row'
import RemoveFromPlaylistRow from './components/remove-from-playlist-row'
import useDownloadTracks, { useDeleteDownloads } from '../../hooks/downloads/mutations'
@@ -33,6 +33,7 @@ import { useDownloadProgress } from 'react-native-nitro-player'
import CircularProgressIndicator from '../Global/components/circular-progress-indicator'
import { useArtist } from '../../api/queries/artist'
import { addToQueue } from '../../hooks/player/functions/queue'
import { useAlbum } from '../../api/queries/album'
type StackNavigation = Pick<NativeStackNavigationProp<BaseStackParamList>, 'navigate' | 'dispatch'>
@@ -53,18 +54,16 @@ export default function ItemContext({
downloadedMediaSourceInfo,
stackNavigation,
}: ContextProps): React.JSX.Element {
const api = useApi()
const api = getApi()
const isArtist = item.Type === BaseItemKind.MusicArtist
const isAlbum = item.Type === BaseItemKind.MusicAlbum
const isTrack = item.Type === BaseItemKind.Audio
const isPlaylist = item.Type === BaseItemKind.Playlist
const { data: album } = useQuery({
queryKey: [QueryKeys.Album, item.AlbumId],
queryFn: () => fetchItem(api, item.AlbumId!),
enabled: isTrack,
})
const { data: album } = useAlbum(
isTrack && item.AlbumId ? ({ Id: item.AlbumId } as BaseItemDto) : ({} as BaseItemDto),
)
const { data: tracks } = useQuery({
queryKey: [QueryKeys.ItemTracks, item.Id],
@@ -367,7 +366,7 @@ function ViewArtistMenuRow({
artistId: string | null | undefined
stackNavigation: StackNavigation | undefined
}): React.JSX.Element {
const api = useApi()
const api = getApi()
const { data: artist } = useArtist(artistId)

View File

@@ -2,39 +2,26 @@ import TextTicker from 'react-native-text-ticker'
import { Paragraph, XStack, YStack } from 'tamagui'
import { TextTickerConfig } from '../component.config'
import React from 'react'
import { useQuery } from '@tanstack/react-query'
import { fetchItem } from '../../../api/queries/item'
import FavoriteButton from '../../Global/components/favorite-button'
import { QueryKeys } from '../../../enums/query-keys'
import navigationRef from '../../../screens/navigation'
import Icon from '../../Global/components/icon'
import { CommonActions } from '@react-navigation/native'
import Animated, { Easing, FadeIn, FadeOut } from 'react-native-reanimated'
import type { SharedValue } from 'react-native-reanimated'
import { useCurrentTrack } from '../../../stores/player/queue'
import { useApi } from '../../../stores'
import { isExplicit } from '../../../utils/trackDetails'
import { MediaSourceInfo } from '@jellyfin/sdk/lib/generated-client'
import { BaseItemDto, MediaSourceInfo } from '@jellyfin/sdk/lib/generated-client'
import getTrackDto from '../../../utils/mapping/track-extra-payload'
import { ICON_PRESS_STYLES } from '../../../configs/style.config'
type SongInfoProps = {
// Shared animated value coming from Player to drive overlay icons
swipeX?: SharedValue<number>
}
import { useAlbum } from '../../../api/queries/album'
export default function SongInfo(): React.JSX.Element {
const api = useApi()
const currentTrack = useCurrentTrack()
const item = getTrackDto(currentTrack)
const { data: album } = useQuery({
queryKey: [QueryKeys.Album, item!.AlbumId!],
queryFn: () => fetchItem(api, item!.AlbumId! as string),
enabled: !!item && !!api,
})
const { data: album } = useAlbum(
item && item.AlbumId ? ({ Id: item.AlbumId } as BaseItemDto) : ({} as BaseItemDto),
)
// Memoize expensive computations
const trackTitle = currentTrack?.title ?? 'Untitled Track'

View File

@@ -41,7 +41,7 @@ export const loadNewQueue = async (variables: QueueMutation) => {
}
}
export async function loadQueue({
async function loadQueue({
index = 0,
tracklist,
queue,
@@ -83,12 +83,12 @@ export async function loadQueue({
PlayerQueue.addTracksToPlaylist(playlistId, playlist)
PlayerQueue.loadPlaylist(playlistId)
await TrackPlayer.skipToIndex(finalStartIndex)
await TrackPlayer.skipToIndex(finalStartIndex === -1 ? 0 : finalStartIndex)
setNewQueue(playlist, queue, finalStartIndex, shuffled)
setNewQueue(playlist, queue, finalStartIndex === -1 ? 0 : finalStartIndex, shuffled)
return {
finalStartIndex,
finalStartIndex: finalStartIndex === -1 ? 0 : finalStartIndex,
tracks: playlist,
}
}

View File

@@ -9,6 +9,7 @@ import fetchUserData from '../api/queries/user-data/utils'
import UserDataQueryKey from '../api/queries/user-data/keys'
import { getApi, getUser } from '../stores'
import { ArtistQueryKey } from '../api/queries/artist/keys'
import { AlbumQuery } from '../api/queries/album/queries'
// Module-level dedup guard — no hook needed, this is just a long-lived Set
const prefetchedContext = new Set<string>()
@@ -102,12 +103,7 @@ function warmArtistContext(api: Api | undefined, artistId: string): void {
function warmTrackContext(api: Api | undefined, track: BaseItemDto): void {
const { AlbumId, ArtistItems } = track
if (AlbumId)
queryClient.ensureQueryData({
queryKey: [QueryKeys.Album, AlbumId],
queryFn: () => fetchItem(api, AlbumId),
staleTime: ONE_DAY,
})
if (AlbumId) queryClient.ensureQueryData(AlbumQuery({ Id: AlbumId } as BaseItemDto))
if (ArtistItems) ArtistItems.forEach((artistItem) => warmArtistContext(api, artistItem.Id!))
}

View File

@@ -1,5 +1,4 @@
import useAppActive from './use-app-active'
import { useIsPlayerFocused } from '../stores/player/display'
import { useCurrentTrack } from '../stores/player/queue'
export default function useIsMiniPlayerActive(): boolean {
@@ -7,7 +6,5 @@ export default function useIsMiniPlayerActive(): boolean {
const currentTrack = useCurrentTrack()
const isPlayerFocused = useIsPlayerFocused()
return !!currentTrack && isAppActive && !isPlayerFocused
return !!currentTrack && isAppActive
}