mirror of
https://github.com/anultravioletaurora/Jellify.git
synced 2025-12-16 18:55:44 -06:00
* fix: update sort icon name and label in ArtistTabBar * fix: optimize image URL generation and improve performance in Playlists and Tracks components * homescreen improvements * deduplicate tracks in FrequentlyPlayedTracks and RecentlyPlayed components * enhance storage management with versioning and slimmed track persistence * refactor HorizontalCardList to allow customizable estimatedItemSize * update sort button label in ArtistTabBar for clarity * refactor media info fetching and improve search debounce logic * refactor navigation parameters in artist and track components for simplicity * refactor PlayPauseButton to manage optimistic UI state and improve playback handling * Cut queue remap churn, speed lyric lookup, clean cast listener cleanup, and avoid redundant HEADs on downloads * Revert "Cut queue remap churn, speed lyric lookup, clean cast listener cleanup, and avoid redundant HEADs on downloads" This reverts commit1c63b748b6. * Cut queue remap churn, speed lyric lookup, clean cast listener cleanup, and avoid redundant HEADs on downloads * Revert "Cut queue remap churn, speed lyric lookup, clean cast listener cleanup, and avoid redundant HEADs on downloads" This reverts commitf9e0e82e57. * Reapply "Cut queue remap churn, speed lyric lookup, clean cast listener cleanup, and avoid redundant HEADs on downloads" This reverts commit6710d3404c. * Update project configuration: refine build phases, adjust code signing identity, and format flags * Fix TypeScript errors in Search component and improve playback state handler in Player queries * Refactor ItemRow component to accept queueName prop and update queue handling * lot's o fixes to item cards and item rows * memoize tracks component * fix jest * simplify navigation in FrequentArtists, FrequentlyPlayedTracks, RecentArtists, and RecentlyPlayed components * Update axios version and enhance image handling options in components * Enhance ItemImage component with imageOptions for better image handling in PlayerHeader and Miniplayer * moves buffers to player config --------- Co-authored-by: Violet Caulfield <42452695+anultravioletaurora@users.noreply.github.com> Co-authored-by: Violet Caulfield <violet@cosmonautical.cloud>
135 lines
4.3 KiB
TypeScript
135 lines
4.3 KiB
TypeScript
import './gesture-handler'
|
|
import React, { useEffect, useRef, useState } from 'react'
|
|
import 'react-native-url-polyfill/auto'
|
|
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'
|
|
import Jellify from './src/components/jellify'
|
|
import { TamaguiProvider } from 'tamagui'
|
|
import { LogBox, Platform, useColorScheme } from 'react-native'
|
|
import jellifyConfig from './tamagui.config'
|
|
import { queryClientPersister } from './src/constants/storage'
|
|
import { ONE_DAY, queryClient } from './src/constants/query-client'
|
|
import { GestureHandlerRootView } from 'react-native-gesture-handler'
|
|
import TrackPlayer, {
|
|
AndroidAudioContentType,
|
|
AppKilledPlaybackBehavior,
|
|
IOSCategory,
|
|
IOSCategoryOptions,
|
|
} from 'react-native-track-player'
|
|
import { CAPABILITIES } from './src/player/constants'
|
|
import { SafeAreaProvider } from 'react-native-safe-area-context'
|
|
import { NavigationContainer } from '@react-navigation/native'
|
|
import { JellifyDarkTheme, JellifyLightTheme, JellifyOLEDTheme } from './src/components/theme'
|
|
import { requestStoragePermission } from './src/utils/permisson-helpers'
|
|
import ErrorBoundary from './src/components/ErrorBoundary'
|
|
import OTAUpdateScreen from './src/components/OtaUpdates'
|
|
import { usePerformanceMonitor } from './src/hooks/use-performance-monitor'
|
|
import navigationRef from './navigation'
|
|
import { BUFFERS, PROGRESS_UPDATE_EVENT_INTERVAL } from './src/player/config'
|
|
import { useThemeSetting } from './src/stores/settings/app'
|
|
|
|
LogBox.ignoreAllLogs()
|
|
|
|
export default function App(): React.JSX.Element {
|
|
// Add performance monitoring to track app-level re-renders
|
|
const performanceMetrics = usePerformanceMonitor('App', 3)
|
|
|
|
const [playerIsReady, setPlayerIsReady] = useState<boolean>(false)
|
|
const playerInitializedRef = useRef<boolean>(false)
|
|
|
|
useEffect(() => {
|
|
// Guard against double initialization (React StrictMode, hot reload)
|
|
if (playerInitializedRef.current) return
|
|
playerInitializedRef.current = true
|
|
|
|
TrackPlayer.setupPlayer({
|
|
autoHandleInterruptions: true,
|
|
iosCategory: IOSCategory.Playback,
|
|
iosCategoryOptions: [
|
|
IOSCategoryOptions.AllowAirPlay,
|
|
IOSCategoryOptions.AllowBluetooth,
|
|
],
|
|
androidAudioContentType: AndroidAudioContentType.Music,
|
|
minBuffer: 30, // 30 seconds minimum buffer
|
|
...BUFFERS,
|
|
})
|
|
.then(() =>
|
|
TrackPlayer.updateOptions({
|
|
capabilities: CAPABILITIES,
|
|
notificationCapabilities: CAPABILITIES,
|
|
// Reduced interval for smoother progress tracking and earlier prefetch detection
|
|
progressUpdateEventInterval: PROGRESS_UPDATE_EVENT_INTERVAL,
|
|
// Stop playback and remove notification when app is killed to prevent battery drain
|
|
android: {
|
|
appKilledPlaybackBehavior:
|
|
AppKilledPlaybackBehavior.StopPlaybackAndRemoveNotification,
|
|
},
|
|
}),
|
|
)
|
|
.catch((error) => {
|
|
// Player may already be initialized (e.g., after hot reload)
|
|
// This is expected and not a fatal error
|
|
console.log('[TrackPlayer] Setup caught:', error?.message ?? error)
|
|
})
|
|
.finally(() => {
|
|
setPlayerIsReady(true)
|
|
requestStoragePermission()
|
|
})
|
|
}, []) // Empty deps - only run once on mount
|
|
|
|
const [reloader, setReloader] = useState(0)
|
|
|
|
const handleRetry = () => setReloader((r) => r + 1)
|
|
|
|
return (
|
|
<React.StrictMode>
|
|
<SafeAreaProvider>
|
|
<OTAUpdateScreen />
|
|
<ErrorBoundary reloader={reloader} onRetry={handleRetry}>
|
|
<PersistQueryClientProvider
|
|
client={queryClient}
|
|
persistOptions={{
|
|
persister: queryClientPersister,
|
|
|
|
/**
|
|
* Maximum query data age of one day
|
|
*/
|
|
maxAge: ONE_DAY,
|
|
}}
|
|
>
|
|
<Container playerIsReady={playerIsReady} />
|
|
</PersistQueryClientProvider>
|
|
</ErrorBoundary>
|
|
</SafeAreaProvider>
|
|
</React.StrictMode>
|
|
)
|
|
}
|
|
|
|
function Container({ playerIsReady }: { playerIsReady: boolean }): React.JSX.Element {
|
|
const [theme] = useThemeSetting()
|
|
|
|
const isDarkMode = useColorScheme() === 'dark'
|
|
|
|
return (
|
|
<NavigationContainer
|
|
ref={navigationRef}
|
|
theme={
|
|
theme === 'system'
|
|
? isDarkMode
|
|
? JellifyDarkTheme
|
|
: JellifyLightTheme
|
|
: theme === 'dark'
|
|
? JellifyDarkTheme
|
|
: theme === 'oled'
|
|
? JellifyOLEDTheme
|
|
: JellifyLightTheme
|
|
}
|
|
>
|
|
<GestureHandlerRootView>
|
|
<TamaguiProvider config={jellifyConfig}>
|
|
{playerIsReady && <Jellify />}
|
|
</TamaguiProvider>
|
|
</GestureHandlerRootView>
|
|
</NavigationContainer>
|
|
)
|
|
}
|