Detect Background State, Deps Updates (#538)

detect when we are running in the background to pause UI updates, dramatically reducing CPU load and therefore conserving battery life 

update dependency packages, Pods
This commit is contained in:
Violet Caulfield
2025-09-19 09:34:04 -05:00
committed by GitHub
parent 08a28aa5ee
commit a095af85ba
11 changed files with 1069 additions and 1042 deletions

View File

@@ -2891,7 +2891,7 @@ PODS:
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- RNSentry (7.0.1):
- RNSentry (7.1.0):
- boost
- DoubleConversion
- fast_float
@@ -2918,7 +2918,7 @@ PODS:
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Sentry/HybridSDK (= 8.53.2)
- Sentry/HybridSDK (= 8.56.0)
- SocketRocket
- Yoga
- RNWorklets (0.4.1):
@@ -3010,7 +3010,7 @@ PODS:
- SDWebImage (5.11.1):
- SDWebImage/Core (= 5.11.1)
- SDWebImage/Core (5.11.1)
- Sentry/HybridSDK (8.53.2)
- Sentry/HybridSDK (8.56.0)
- SocketRocket (0.7.1)
- SSZipArchive (2.4.3)
- SwiftAudioEx (1.1.0)
@@ -3432,10 +3432,10 @@ SPEC CHECKSUMS:
RNReactNativeHapticFeedback: be4f1b4bf0398c30b59b76ed92ecb0a2ff3a69c6
RNReanimated: ee96d03fe3713993a30cc205522792b4cb08e4f9
RNScreens: 0bbf16c074ae6bb1058a7bf2d1ae017f4306797c
RNSentry: 6c63debc7b22a00cbf7d1c9ed8de43e336216545
RNSentry: 60919c9cdac7e4b35e9f5dd0149f551ec12f35cb
RNWorklets: e8335dff9d27004709f58316985769040cd1e8f2
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
Sentry: 59993bffde4a1ac297ba6d268dc4bbce068d7c1b
Sentry: 3d82977434c80381cae856c40b99c39e4be6bc11
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
SwiftAudioEx: f6aa653770f3a0d3851edaf8d834a30aee4a7646

View File

@@ -45,28 +45,28 @@
"@react-navigation/material-top-tabs": "^7.3.7",
"@react-navigation/native": "^7.1.17",
"@react-navigation/native-stack": "^7.3.26",
"@sentry/react-native": "7.0.1",
"@sentry/react-native": "7.1.0",
"@shopify/flash-list": "^2.0.3",
"@tamagui/config": "^1.132.23",
"@tanstack/query-async-storage-persister": "^5.87.4",
"@tanstack/react-query": "^5.87.4",
"@tanstack/react-query-persist-client": "^5.87.4",
"@tamagui/config": "1.132.25",
"@tanstack/query-async-storage-persister": "5.89.0",
"@tanstack/react-query": "5.89.0",
"@tanstack/react-query-persist-client": "5.89.0",
"@testing-library/react-native": "^13.2.3",
"@typedigital/telemetrydeck-react": "^0.4.1",
"axios": "^1.11.0",
"axios": "1.12.2",
"bundle": "^2.1.0",
"dlx": "^0.2.1",
"gem": "^2.4.3",
"invert-color": "^2.0.0",
"lodash": "^4.17.21",
"openai": "^5.16.0",
"openai": "5.21.0",
"react": "19.1.0",
"react-native": "0.81.4",
"react-native-background-actions": "^4.0.1",
"react-native-blob-util": "^0.22.2",
"react-native-blurhash": "2.1.1",
"react-native-carplay": "^2.4.1-beta.0",
"react-native-config": "^1.5.6",
"react-native-config": "1.5.6",
"react-native-device-info": "^14.0.4",
"react-native-dns-lookup": "^1.0.6",
"react-native-draggable-flatlist": "^4.0.3",
@@ -94,7 +94,7 @@
"react-native-worklets": "0.4.1",
"ruby": "^0.6.1",
"scheduler": "^0.26.0",
"tamagui": "^1.132.23",
"tamagui": "1.132.25",
"zustand": "^5.0.8"
},
"devDependencies": {

View File

@@ -3,9 +3,13 @@ import { FrequentlyPlayedArtistsQueryKey, FrequentlyPlayedTracksQueryKey } from
import { useJellifyContext } from '../../../providers'
import { fetchFrequentlyPlayed, fetchFrequentlyPlayedArtists } from './utils/frequents'
import { ApiLimits } from '../query.config'
import { queryClient } from '../../../constants/query-client'
import { isUndefined } from 'lodash'
const FREQUENTS_QUERY_CONFIG = {
refetchOnMount: false,
staleTime: Infinity,
} as const
export const useFrequentlyPlayedTracks = () => {
const { api, user, library } = useJellifyContext()
@@ -18,6 +22,7 @@ export const useFrequentlyPlayedTracks = () => {
console.debug('Getting next page for frequently played')
return lastPage.length === ApiLimits.Home ? lastPageParam + 1 : undefined
},
...FREQUENTS_QUERY_CONFIG,
})
}
@@ -36,5 +41,6 @@ export const useFrequentlyPlayedArtists = () => {
return lastPage.length > 0 ? lastPageParam + 1 : undefined
},
enabled: !isUndefined(frequentlyPlayedTracks),
...FREQUENTS_QUERY_CONFIG,
})
}

View File

@@ -3,9 +3,13 @@ import { RecentlyPlayedArtistsQueryKey, RecentlyPlayedTracksQueryKey } from './k
import { useInfiniteQuery } from '@tanstack/react-query'
import { fetchRecentlyPlayed, fetchRecentlyPlayedArtists } from './utils'
import { ApiLimits } from '../query.config'
import { queryClient } from '../../../constants/query-client'
import { isUndefined } from 'lodash'
const RECENTS_QUERY_CONFIG = {
refetchOnMount: false,
staleTime: Infinity,
} as const
export const useRecentlyPlayedTracks = () => {
const { api, user, library } = useJellifyContext()
@@ -18,6 +22,7 @@ export const useRecentlyPlayedTracks = () => {
console.debug('Getting next page for recent tracks')
return lastPage.length === ApiLimits.Home ? lastPageParam + 1 : undefined
},
...RECENTS_QUERY_CONFIG,
})
}
@@ -36,5 +41,6 @@ export const useRecentArtists = () => {
return lastPage.length > 0 ? lastPageParam + 1 : undefined
},
enabled: !isUndefined(recentlyPlayedTracks),
...RECENTS_QUERY_CONFIG,
})
}

View File

@@ -1,11 +1,10 @@
import React, { useMemo, useCallback } from 'react'
import { getToken, Progress, View, XStack, YStack, ZStack } from 'tamagui'
import { getToken, Progress, View, XStack, YStack } from 'tamagui'
import { useNavigation } from '@react-navigation/native'
import { Text } from '../Global/helpers/text'
import TextTicker from 'react-native-text-ticker'
import PlayPauseButton from './components/buttons'
import { TextTickerConfig } from './component.config'
import { useJellifyContext } from '../../providers'
import { RunTimeSeconds } from '../Global/helpers/time-codes'
import { MINIPLAYER_UPDATE_INTERVAL } from '../../player/config'
import { Progress as TrackPlayerProgress } from 'react-native-track-player'
@@ -85,66 +84,61 @@ export const Miniplayer = React.memo(function Miniplayer(): React.JSX.Element {
return (
<View testID='miniplayer-test-id'>
{nowPlaying && (
<GestureDetector gesture={gesture}>
<YStack>
<MiniPlayerProgress />
<XStack paddingBottom={'$1'} alignItems='center' onPress={openPlayer}>
<YStack justify='center' alignItems='center' marginLeft={'$2'}>
<Animated.View
entering={FadeIn}
exiting={FadeOut}
key={`${nowPlaying!.item.AlbumId}-album-image`}
>
<ItemImage
item={nowPlaying!.item}
width={'$12'}
height={'$12'}
/>
</Animated.View>
</YStack>
<YStack
alignContent='flex-start'
justifyContent='center'
marginLeft={'$2'}
flex={6}
<GestureDetector gesture={gesture}>
<YStack>
<MiniPlayerProgress />
<XStack paddingBottom={'$1'} alignItems='center' onPress={openPlayer}>
<YStack justify='center' alignItems='center' marginLeft={'$2'}>
<Animated.View
entering={FadeIn}
exiting={FadeOut}
key={`${nowPlaying!.item.AlbumId}-album-image`}
>
<MiniPlayerRuntime duration={nowPlaying.duration} />
<ItemImage item={nowPlaying!.item} width={'$12'} height={'$12'} />
</Animated.View>
</YStack>
<Animated.View
entering={FadeIn}
exiting={FadeOut}
key={`${nowPlaying!.item.AlbumId}-mini-player-song-info`}
>
<View width={'100%'}>
<TextTicker {...TextTickerConfig}>
<Text bold width={'100%'}>
{nowPlaying?.title ?? 'Nothing Playing'}
</Text>
</TextTicker>
<YStack
alignContent='flex-start'
justifyContent='center'
marginLeft={'$2'}
flex={6}
>
<MiniPlayerRuntime duration={nowPlaying!.duration} />
<TextTicker {...TextTickerConfig}>
<Text height={'$0.5'} width={'100%'}>
{nowPlaying?.artist ?? ''}
</Text>
</TextTicker>
</View>
</Animated.View>
</YStack>
<XStack
justifyContent='flex-end'
alignItems='center'
flex={2}
marginRight={'$2'}
<Animated.View
entering={FadeIn}
exiting={FadeOut}
key={`${nowPlaying!.item.AlbumId}-mini-player-song-info`}
style={{
width: '100%',
}}
>
<PlayPauseButton size={getToken('$12')} />
</XStack>
<TextTicker {...TextTickerConfig}>
<Text bold width={'100%'}>
{nowPlaying?.title ?? 'Nothing Playing'}
</Text>
</TextTicker>
<TextTicker {...TextTickerConfig}>
<Text height={'$0.5'} width={'100%'}>
{nowPlaying?.artist ?? ''}
</Text>
</TextTicker>
</Animated.View>
</YStack>
<XStack
justifyContent='flex-end'
alignItems='center'
flex={2}
marginRight={'$2'}
>
<PlayPauseButton size={getToken('$12')} />
</XStack>
</YStack>
</GestureDetector>
)}
</XStack>
</YStack>
</GestureDetector>
</View>
)
})

View File

@@ -7,8 +7,9 @@ const INFO_CAPTIONS = [
// Inside Jokes (that the internal Jellify team will get)
'Thank you, Pikachu',
'Now with 100% more nitro!', // since we use nitro modules
'Boosted by Nitro!', // since we use nitro modules
'git blame violet', // lol
'Made possible by Niels', // O Captain, My Captain!
// Movie Quotes
'Groovy, baby!', // Austin Powers

View File

@@ -33,6 +33,10 @@ export const queryClient = new QueryClient({
*/
staleTime: ONE_HOUR * 2,
refetchIntervalInBackground: false,
refetchOnWindowFocus: false,
retry(failureCount: number, error: Error) {
if (failureCount > 2) return false

View File

@@ -0,0 +1,16 @@
import { useEffect, useState } from 'react'
import { AppState } from 'react-native'
export default function useAppActive() {
const [isActive, setIsActive] = useState(AppState.currentState === 'active')
useEffect(() => {
const appStateListener = AppState.addEventListener('change', (state) =>
setIsActive(state === 'active'),
)
return () => appStateListener.remove()
}, [])
return isActive
}

View File

@@ -1,15 +1,11 @@
import JellifyTrack from '../../../types/JellifyTrack'
import { queryClient } from '../../../constants/query-client'
import { Queue } from '../../../player/types/queue-item'
import {
ACTIVE_INDEX_QUERY_KEY,
NOW_PLAYING_QUERY_KEY,
PLAY_QUEUE_QUERY_KEY,
QUEUE_REF_QUERY_KEY,
SHUFFLED_QUERY_KEY,
UNSHUFFLED_QUEUE_QUERY_KEY,
} from '../constants/query-keys'
import { CURRENT_INDEX_QUERY, NOW_PLAYING_QUERY, QUEUE_QUERY } from '../constants/queries'
import { CURRENT_INDEX_QUERY, NOW_PLAYING_QUERY } from '../constants/queries'
export function getActiveIndex(): number | undefined {
return queryClient.getQueryData(ACTIVE_INDEX_QUERY_KEY) as number | undefined
@@ -32,7 +28,6 @@ export function setPlayQueue(tracks: JellifyTrack[]): void {
}
export function handleActiveTrackChanged(): void {
queryClient.invalidateQueries(NOW_PLAYING_QUERY)
queryClient.ensureQueryData(QUEUE_QUERY)
queryClient.invalidateQueries(CURRENT_INDEX_QUERY)
queryClient.refetchQueries(NOW_PLAYING_QUERY)
queryClient.refetchQueries(CURRENT_INDEX_QUERY)
}

View File

@@ -2,13 +2,18 @@ import { Miniplayer } from '../../components/Player/mini-player'
import InternetConnectionWatcher from '../../components/Network/internetConnectionWatcher'
import { useNowPlaying } from '../../providers/Player/hooks/queries'
import { BottomTabBar, BottomTabBarProps } from '@react-navigation/bottom-tabs'
import useAppActive from '../../hooks/use-app-active'
export default function TabBar({ ...props }: BottomTabBarProps): React.JSX.Element {
const { data: nowPlaying } = useNowPlaying()
const appIsActive = useAppActive()
const showMiniPlayer = nowPlaying && appIsActive
return (
<>
{nowPlaying && (
{showMiniPlayer && (
/* Hide miniplayer if the queue is empty */
<Miniplayer />
)}

1918
yarn.lock

File diff suppressed because it is too large Load Diff