defer non-critical startup operations

- delay StorageProvider queries until 2s after initial render
- defer player queue restoration with InteractionManager
- add enabled option to download query hooks
This commit is contained in:
skalthoff
2026-02-03 19:58:47 -08:00
parent c3be61c8e6
commit cc0befeb52
3 changed files with 73 additions and 42 deletions

View File

@@ -3,13 +3,22 @@ import { useQuery } from '@tanstack/react-query'
import fetchStorageInUse from './utils/storage-in-use'
import { AUDIO_CACHE_QUERY } from './constants'
export const useStorageInUse = () =>
type QueryOptions = {
enabled?: boolean
}
export const useStorageInUse = (options?: QueryOptions) =>
useQuery({
queryKey: [QueryKeys.StorageInUse],
queryFn: fetchStorageInUse,
enabled: options?.enabled,
})
export const useAllDownloadedTracks = () => useQuery(AUDIO_CACHE_QUERY)
export const useAllDownloadedTracks = (options?: QueryOptions) =>
useQuery({
...AUDIO_CACHE_QUERY,
enabled: options?.enabled,
})
export const useDownloadedTracks = (itemIds: (string | null | undefined)[]) =>
useAllDownloadedTracks().data?.filter((download) => itemIds.includes(download.item.Id))

View File

@@ -1,48 +1,55 @@
import { isUndefined } from 'lodash'
import { InteractionManager } from 'react-native'
import TrackPlayer, { RepeatMode } from 'react-native-track-player'
import { usePlayerQueueStore } from '../../../stores/player/queue'
import { createMMKV } from 'react-native-mmkv'
export default async function Initialize() {
const {
queue: persistedQueue,
currentIndex: persistedIndex,
repeatMode,
} = usePlayerQueueStore.getState()
export default function Initialize() {
// Defer queue restoration until after UI interactions complete
// This allows the home screen to render faster on cold start
InteractionManager.runAfterInteractions(async () => {
const {
queue: persistedQueue,
currentIndex: persistedIndex,
repeatMode,
} = usePlayerQueueStore.getState()
// Read saved position BEFORE reset() to prevent it from being cleared
const progressStorage = createMMKV({ id: 'progress_storage' })
const savedPosition = progressStorage.getNumber('player-key') ?? 0
console.log('savedPosition before reset', savedPosition)
// Read saved position BEFORE reset() to prevent it from being cleared
const progressStorage = createMMKV({ id: 'progress_storage' })
const savedPosition = progressStorage.getNumber('player-key') ?? 0
console.log('savedPosition before reset', savedPosition)
const storedPlayQueue = persistedQueue.length > 0 ? persistedQueue : undefined
const storedIndex = persistedIndex
const storedPlayQueue = persistedQueue.length > 0 ? persistedQueue : undefined
const storedIndex = persistedIndex
if (
Array.isArray(storedPlayQueue) &&
storedPlayQueue.length > 0 &&
!isUndefined(storedIndex) &&
storedIndex !== null
) {
await TrackPlayer.reset()
await TrackPlayer.add(storedPlayQueue)
await TrackPlayer.skip(storedIndex)
if (
Array.isArray(storedPlayQueue) &&
storedPlayQueue.length > 0 &&
!isUndefined(storedIndex) &&
storedIndex !== null
) {
await TrackPlayer.reset()
await TrackPlayer.add(storedPlayQueue)
await TrackPlayer.skip(storedIndex)
usePlayerQueueStore.getState().setQueue(storedPlayQueue)
usePlayerQueueStore.getState().setCurrentIndex(storedIndex)
usePlayerQueueStore.getState().setCurrentTrack(storedPlayQueue[storedIndex] ?? undefined)
}
const restoredRepeatMode = repeatMode ?? RepeatMode.Off
await TrackPlayer.setRepeatMode(restoredRepeatMode)
// Restore saved playback position after queue is loaded
if (savedPosition > 0) {
try {
await TrackPlayer.seekTo(savedPosition)
console.log('Restored playback position:', savedPosition)
} catch (error) {
console.warn('Failed to restore playback position:', error)
usePlayerQueueStore.getState().setQueue(storedPlayQueue)
usePlayerQueueStore.getState().setCurrentIndex(storedIndex)
usePlayerQueueStore
.getState()
.setCurrentTrack(storedPlayQueue[storedIndex] ?? undefined)
}
}
const restoredRepeatMode = repeatMode ?? RepeatMode.Off
await TrackPlayer.setRepeatMode(restoredRepeatMode)
// Restore saved playback position after queue is loaded
if (savedPosition > 0) {
try {
await TrackPlayer.seekTo(savedPosition)
console.log('Restored playback position:', savedPosition)
} catch (error) {
console.warn('Failed to restore playback position:', error)
}
}
})
}

View File

@@ -1,4 +1,11 @@
import React, { PropsWithChildren, createContext, use, useContext, useState } from 'react'
import React, {
PropsWithChildren,
createContext,
use,
useContext,
useState,
useEffect,
} from 'react'
import { useAllDownloadedTracks, useStorageInUse } from '../../api/queries/download'
import { JellifyDownload, JellifyDownloadProgress } from '../../types/JellifyDownload'
import {
@@ -57,16 +64,24 @@ const sumDownloadBytes = (download: JellifyDownload | undefined) => {
}
export function StorageProvider({ children }: PropsWithChildren): React.JSX.Element {
// Defer storage queries until after initial app render to improve cold start time
const [shouldFetch, setShouldFetch] = useState(false)
useEffect(() => {
const timer = setTimeout(() => setShouldFetch(true), 2000)
return () => clearTimeout(timer)
}, [])
const {
data: downloads,
refetch: refetchDownloads,
isFetching: isFetchingDownloads,
} = useAllDownloadedTracks()
} = useAllDownloadedTracks({ enabled: shouldFetch })
const {
data: storageInfo,
refetch: refetchStorageInfo,
isFetching: isFetchingStorage,
} = useStorageInUse()
} = useStorageInUse({ enabled: shouldFetch })
const activeDownloads = useDownloadProgress()
const [selection, setSelection] = useState<StorageSelectionState>({})