mirror of
https://github.com/anultravioletaurora/Jellify.git
synced 2026-02-12 17:18:35 -06:00
Player Render Optimizations, Better DeviceProfile Handling (#520)
removes hooks from player provider in lieu of nonreactive functions and data fetching. In testing this fixed the issue where the player provider was rerendering when the queue or the currently active track changed Use a UUID to distinguish between DeviceProfiles, not the name. This means that media info from Jellyfin will be properly refetched if the user selects a different audio quality mini player rendering optimization fixes, since we don't need that rerendering 4 times a second when updates are only distinguishable every whole second. Also brings some style fixes that reduces vertical height usage update sentry sdk while we're in there doing debugging
This commit is contained in:
@@ -1893,7 +1893,7 @@ PODS:
|
||||
- SocketRocket
|
||||
- SSZipArchive (~> 2.4.3)
|
||||
- Yoga
|
||||
- react-native-pager-view (7.0.0):
|
||||
- react-native-pager-view (6.9.1):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
- fast_float
|
||||
@@ -2816,7 +2816,7 @@ PODS:
|
||||
- ReactCommon/turbomodule/core
|
||||
- SocketRocket
|
||||
- Yoga
|
||||
- RNSentry (6.17.0):
|
||||
- RNSentry (7.0.1):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
- fast_float
|
||||
@@ -2843,7 +2843,7 @@ PODS:
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Sentry/HybridSDK (= 8.53.1)
|
||||
- Sentry/HybridSDK (= 8.53.2)
|
||||
- SocketRocket
|
||||
- Yoga
|
||||
- RNWorklets (0.4.1):
|
||||
@@ -2938,7 +2938,7 @@ PODS:
|
||||
- SDWebImageWebPCoder (0.8.5):
|
||||
- libwebp (~> 1.0)
|
||||
- SDWebImage/Core (~> 5.10)
|
||||
- Sentry/HybridSDK (8.53.1)
|
||||
- Sentry/HybridSDK (8.53.2)
|
||||
- SocketRocket (0.7.1)
|
||||
- SSZipArchive (2.4.3)
|
||||
- SwiftAudioEx (1.1.0)
|
||||
@@ -3311,7 +3311,7 @@ SPEC CHECKSUMS:
|
||||
react-native-mmkv: 560d39188cf4d817fb34b0df79426a298934ee7d
|
||||
react-native-netinfo: cec9c4e86083cb5b6aba0e0711f563e2fbbff187
|
||||
react-native-ota-hot-update: 5c8fe703c7a789f6de651030e4740923c77fc610
|
||||
react-native-pager-view: 0b0b445d3cb9f8e9972842edf6ddf892b46bdc55
|
||||
react-native-pager-view: a0516effb17ca5120ac2113bfd21b91130ad5748
|
||||
react-native-safe-area-context: c6e2edd1c1da07bdce287fa9d9e60c5f7b514616
|
||||
react-native-track-player: 89d8e641c83a89bea5dee43c381be743282553e9
|
||||
react-native-vector-icons-material-design-icons: c502df5b988ce85d6c7d2b7ee909818315760b82
|
||||
@@ -3355,11 +3355,11 @@ SPEC CHECKSUMS:
|
||||
RNReactNativeHapticFeedback: be4f1b4bf0398c30b59b76ed92ecb0a2ff3a69c6
|
||||
RNReanimated: ee96d03fe3713993a30cc205522792b4cb08e4f9
|
||||
RNScreens: 0bbf16c074ae6bb1058a7bf2d1ae017f4306797c
|
||||
RNSentry: 95e1ed0ede28a4af58aaafedeac9fcfaba0e89ce
|
||||
RNSentry: 6c63debc7b22a00cbf7d1c9ed8de43e336216545
|
||||
RNWorklets: e8335dff9d27004709f58316985769040cd1e8f2
|
||||
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
|
||||
SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d
|
||||
Sentry: 1e4e974d45f09d153af4b30b42acfb1c79e957d3
|
||||
Sentry: 59993bffde4a1ac297ba6d268dc4bbce068d7c1b
|
||||
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
|
||||
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
|
||||
SwiftAudioEx: f6aa653770f3a0d3851edaf8d834a30aee4a7646
|
||||
|
||||
@@ -65,8 +65,6 @@ appId: com.jellify
|
||||
# Test About (Info) Tab
|
||||
- tapOn:
|
||||
text: "About"
|
||||
- assertVisible:
|
||||
text: "Made with love"
|
||||
- assertVisible:
|
||||
text: "View Source"
|
||||
- assertVisible:
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"@react-navigation/material-top-tabs": "^7.3.7",
|
||||
"@react-navigation/native": "^7.1.17",
|
||||
"@react-navigation/native-stack": "^7.3.26",
|
||||
"@sentry/react-native": "6.17.0",
|
||||
"@sentry/react-native": "7.0.1",
|
||||
"@shopify/flash-list": "^2.0.3",
|
||||
"@tamagui/config": "^1.132.23",
|
||||
"@tanstack/query-async-storage-persister": "^5.87.1",
|
||||
@@ -79,7 +79,7 @@
|
||||
"react-native-linear-gradient": "^2.8.3",
|
||||
"react-native-mmkv": "3.3.0",
|
||||
"react-native-ota-hot-update": "2.3.1",
|
||||
"react-native-pager-view": "^7.0.0",
|
||||
"react-native-pager-view": "^6.9.1",
|
||||
"react-native-reanimated": "4.0.2",
|
||||
"react-native-safe-area-context": "^5.6.1",
|
||||
"react-native-screens": "4.16.0",
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
diff --git a/node_modules/@sentry/react-native/ios/RNSentry.mm b/node_modules/@sentry/react-native/ios/RNSentry.mm
|
||||
index 267c41c..b731bad 100644
|
||||
--- a/node_modules/@sentry/react-native/ios/RNSentry.mm
|
||||
+++ b/node_modules/@sentry/react-native/ios/RNSentry.mm
|
||||
@@ -819,7 +819,7 @@ + (SentryUser *_Nullable)userFrom:(NSDictionary *)userKeys
|
||||
{
|
||||
#if SENTRY_PROFILING_ENABLED
|
||||
try {
|
||||
- facebook::hermes::HermesRuntime::enableSamplingProfiler();
|
||||
+// facebook::hermes::HermesRuntime::enableSamplingProfiler();
|
||||
if (nativeProfileTraceId == nil && nativeProfileStartTime == 0 && platformProfilers) {
|
||||
# if SENTRY_TARGET_PROFILING_SUPPORTED
|
||||
nativeProfileTraceId = [RNSentryId newId];
|
||||
@@ -879,10 +879,10 @@ + (SentryUser *_Nullable)userFrom:(NSDictionary *)userKeys
|
||||
nativeProfileTraceId = nil;
|
||||
nativeProfileStartTime = 0;
|
||||
|
||||
- facebook::hermes::HermesRuntime::disableSamplingProfiler();
|
||||
+// facebook::hermes::HermesRuntime::;
|
||||
std::stringstream ss;
|
||||
// Before RN 0.69 Hermes used llvh::raw_ostream (profiling is supported for 0.69 and newer)
|
||||
- facebook::hermes::HermesRuntime::dumpSampledTraceToStream(ss);
|
||||
+// facebook::hermes::HermesRuntime::dumpSampledTraceToStream(ss);
|
||||
|
||||
std::string s = ss.str();
|
||||
NSString *data = [NSString stringWithCString:s.c_str()
|
||||
@@ -8,9 +8,8 @@ import {
|
||||
JellifyDownloadProgress,
|
||||
JellifyDownloadProgressState,
|
||||
} from '../../../types/JellifyDownload'
|
||||
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||
import { queryClient } from '../../../constants/query-client'
|
||||
import { QueryKeys } from '../../../enums/query-keys'
|
||||
import { AUDIO_CACHE_QUERY } from '../../queries/download/constants'
|
||||
|
||||
export async function downloadJellyfinFile(
|
||||
url: string,
|
||||
@@ -158,7 +157,7 @@ export const saveAudio = async (
|
||||
return false
|
||||
}
|
||||
mmkv.set(MMKV_OFFLINE_MODE_KEYS.AUDIO_CACHE, JSON.stringify(existingArray))
|
||||
queryClient.invalidateQueries({ queryKey: [QueryKeys.AudioCache] })
|
||||
queryClient.invalidateQueries(AUDIO_CACHE_QUERY)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
24
src/api/mutations/download/utils/index.ts
Normal file
24
src/api/mutations/download/utils/index.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Api } from '@jellyfin/sdk/lib/api'
|
||||
import { BaseItemDto, DeviceProfile } from '@jellyfin/sdk/lib/generated-client'
|
||||
import { getAudioCache, saveAudio } from '../offlineModeUtils'
|
||||
import { mapDtoToTrack } from '../../../../utils/mappings'
|
||||
|
||||
export default async function saveAudioItem(
|
||||
api: Api | undefined,
|
||||
item: BaseItemDto,
|
||||
deviceProfile: DeviceProfile,
|
||||
autoCached: boolean = false,
|
||||
) {
|
||||
if (!api) return Promise.reject('API Instance not set')
|
||||
|
||||
const downloadedTracks = getAudioCache()
|
||||
|
||||
// If we already have this track downloaded, resolve the promise
|
||||
if (downloadedTracks?.filter((download) => download.item.Id === item.Id).length ?? 0 > 0)
|
||||
return Promise.resolve(false)
|
||||
|
||||
const track = mapDtoToTrack(api, item, downloadedTracks ?? [], deviceProfile)
|
||||
|
||||
// TODO: fix download progresses
|
||||
return saveAudio(track, () => {}, autoCached)
|
||||
}
|
||||
8
src/api/queries/download/constants/index.ts
Normal file
8
src/api/queries/download/constants/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { getAudioCache } from '../../../mutations/download/offlineModeUtils'
|
||||
import DownloadQueryKeys from '../keys'
|
||||
|
||||
export const AUDIO_CACHE_QUERY = {
|
||||
queryKey: [DownloadQueryKeys.DownloadedTracks],
|
||||
queryFn: getAudioCache,
|
||||
staleTime: Infinity, // Never stale, we will manually refetch when downloads are completed
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { useQuery } from '@tanstack/react-query'
|
||||
import fetchStorageInUse from './utils/storage-in-use'
|
||||
import { getAudioCache } from '../../mutations/download/offlineModeUtils'
|
||||
import DownloadQueryKeys from './keys'
|
||||
import { AUDIO_CACHE_QUERY } from './constants'
|
||||
|
||||
export const useStorageInUse = () =>
|
||||
useQuery({
|
||||
@@ -10,12 +11,7 @@ export const useStorageInUse = () =>
|
||||
queryFn: fetchStorageInUse,
|
||||
})
|
||||
|
||||
export const useAllDownloadedTracks = () =>
|
||||
useQuery({
|
||||
queryKey: [DownloadQueryKeys.DownloadedTracks],
|
||||
queryFn: getAudioCache,
|
||||
staleTime: Infinity, // Never stale, we will manually refetch when downloads are completed
|
||||
})
|
||||
export const useAllDownloadedTracks = () => useQuery(AUDIO_CACHE_QUERY)
|
||||
|
||||
export const useDownloadedTracks = (itemIds: (string | null | undefined)[]) =>
|
||||
useAllDownloadedTracks().data?.filter((download) => itemIds.includes(download.item.Id))
|
||||
|
||||
@@ -9,7 +9,7 @@ interface MediaInfoQueryProps {
|
||||
const MediaInfoQueryKey = ({ api, deviceProfile, itemId }: MediaInfoQueryProps) => [
|
||||
'MEDIA_INFO',
|
||||
api,
|
||||
deviceProfile?.Name,
|
||||
deviceProfile?.Id,
|
||||
itemId,
|
||||
]
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ export function HorizontalSlider({ value, max, width, props }: SliderProps): Rea
|
||||
{...props}
|
||||
>
|
||||
<JellifySliderTrack size='$2'>
|
||||
<JellifyActiveSliderTrack size={'$'} />
|
||||
<JellifyActiveSliderTrack />
|
||||
</JellifySliderTrack>
|
||||
<JellifySliderThumb
|
||||
circular
|
||||
|
||||
@@ -13,10 +13,10 @@ import { useDisplayAudioQualityBadge } from '../../../stores/settings/player'
|
||||
import useHapticFeedback from '../../../hooks/use-haptic-feedback'
|
||||
|
||||
// Create a simple pan gesture
|
||||
const scrubGesture = Gesture.Pan().runOnJS(true)
|
||||
const scrubGesture = Gesture.Pan()
|
||||
|
||||
export default function Scrubber(): React.JSX.Element {
|
||||
const { mutate: seekTo, isPending: seekPending, mutateAsync: seekToAsync } = useSeekTo()
|
||||
const { isPending: seekPending, mutateAsync: seekToAsync } = useSeekTo()
|
||||
const { data: nowPlaying } = useNowPlaying()
|
||||
const { width } = useSafeAreaFrame()
|
||||
|
||||
@@ -90,7 +90,7 @@ export default function Scrubber(): React.JSX.Element {
|
||||
}, 100)
|
||||
})
|
||||
},
|
||||
[useSeekTo],
|
||||
[seekToAsync],
|
||||
)
|
||||
|
||||
// Memoize time calculations to prevent unnecessary re-renders
|
||||
|
||||
@@ -4,10 +4,10 @@ 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 { ProgressMultiplier, TextTickerConfig } from './component.config'
|
||||
import { TextTickerConfig } from './component.config'
|
||||
import { useJellifyContext } from '../../providers'
|
||||
import { RunTimeSeconds } from '../Global/helpers/time-codes'
|
||||
import { UPDATE_INTERVAL } from '../../player/config'
|
||||
import { MINIPLAYER_UPDATE_INTERVAL } from '../../player/config'
|
||||
import { Progress as TrackPlayerProgress } from 'react-native-track-player'
|
||||
import { useProgress } from '../../providers/Player/hooks/queries'
|
||||
|
||||
@@ -80,93 +80,82 @@ export const Miniplayer = React.memo(function Miniplayer(): React.JSX.Element {
|
||||
)
|
||||
|
||||
return (
|
||||
<ZStack height={'$7'} testID='miniplayer-test-id'>
|
||||
<View testID='miniplayer-test-id' borderTopWidth={'$0.75'} borderColor={'$borderColor'}>
|
||||
{nowPlaying && (
|
||||
<>
|
||||
<GestureDetector gesture={gesture}>
|
||||
<YStack>
|
||||
<MiniPlayerProgress />
|
||||
|
||||
<XStack
|
||||
alignItems='center'
|
||||
margin={0}
|
||||
padding={0}
|
||||
height={'$6'}
|
||||
onPress={() =>
|
||||
navigation.navigate('PlayerRoot', { screen: 'PlayerScreen' })
|
||||
}
|
||||
>
|
||||
<YStack
|
||||
justify='center'
|
||||
alignItems='center'
|
||||
marginVertical={'auto'}
|
||||
marginLeft={'$2'}
|
||||
>
|
||||
{api && (
|
||||
<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'}
|
||||
marginVertical={'auto'}
|
||||
flex={6}
|
||||
>
|
||||
<MiniPlayerRuntime />
|
||||
|
||||
<GestureDetector gesture={gesture}>
|
||||
<YStack>
|
||||
<XStack
|
||||
paddingVertical={'$1'}
|
||||
alignItems='center'
|
||||
onPress={() =>
|
||||
navigation.navigate('PlayerRoot', { screen: 'PlayerScreen' })
|
||||
}
|
||||
>
|
||||
<YStack justify='center' alignItems='center' marginLeft={'$2'}>
|
||||
{api && (
|
||||
<Animated.View
|
||||
entering={FadeIn}
|
||||
exiting={FadeOut}
|
||||
key={`${nowPlaying!.item.AlbumId}-mini-player-song-info`}
|
||||
key={`${nowPlaying!.item.AlbumId}-album-image`}
|
||||
>
|
||||
<View width={'100%'}>
|
||||
<TextTicker {...TextTickerConfig}>
|
||||
<Text bold width={'100%'}>
|
||||
{nowPlaying?.title ?? 'Nothing Playing'}
|
||||
</Text>
|
||||
</TextTicker>
|
||||
|
||||
<TextTicker {...TextTickerConfig}>
|
||||
<Text height={'$0.5'} width={'100%'}>
|
||||
{nowPlaying?.artist ?? ''}
|
||||
</Text>
|
||||
</TextTicker>
|
||||
</View>
|
||||
<ItemImage
|
||||
item={nowPlaying!.item}
|
||||
width={'$12'}
|
||||
height={'$12'}
|
||||
/>
|
||||
</Animated.View>
|
||||
</YStack>
|
||||
)}
|
||||
</YStack>
|
||||
|
||||
<XStack
|
||||
justifyContent='flex-end'
|
||||
alignItems='center'
|
||||
flex={2}
|
||||
marginRight={'$2'}
|
||||
height={'$6'}
|
||||
<YStack
|
||||
alignContent='flex-start'
|
||||
justifyContent='center'
|
||||
marginLeft={'$2'}
|
||||
flex={6}
|
||||
>
|
||||
<MiniPlayerRuntime />
|
||||
|
||||
<Animated.View
|
||||
entering={FadeIn}
|
||||
exiting={FadeOut}
|
||||
key={`${nowPlaying!.item.AlbumId}-mini-player-song-info`}
|
||||
>
|
||||
<PlayPauseButton size={getToken('$12')} />
|
||||
</XStack>
|
||||
<View width={'100%'}>
|
||||
<TextTicker {...TextTickerConfig}>
|
||||
<Text bold width={'100%'}>
|
||||
{nowPlaying?.title ?? 'Nothing Playing'}
|
||||
</Text>
|
||||
</TextTicker>
|
||||
|
||||
<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'}
|
||||
>
|
||||
<PlayPauseButton size={getToken('$12')} />
|
||||
</XStack>
|
||||
</YStack>
|
||||
</GestureDetector>
|
||||
</>
|
||||
</XStack>
|
||||
|
||||
<MiniPlayerProgress />
|
||||
</YStack>
|
||||
</GestureDetector>
|
||||
)}
|
||||
</ZStack>
|
||||
</View>
|
||||
)
|
||||
})
|
||||
|
||||
function MiniPlayerRuntime(): React.JSX.Element {
|
||||
const { position } = useProgress(UPDATE_INTERVAL)
|
||||
const { position } = useProgress(MINIPLAYER_UPDATE_INTERVAL)
|
||||
const { data: nowPlaying } = useNowPlaying()
|
||||
const { duration } = nowPlaying!
|
||||
|
||||
@@ -198,11 +187,11 @@ function MiniPlayerRuntime(): React.JSX.Element {
|
||||
}
|
||||
|
||||
function MiniPlayerProgress(): React.JSX.Element {
|
||||
const progress = useProgress(UPDATE_INTERVAL)
|
||||
const progress = useProgress(MINIPLAYER_UPDATE_INTERVAL)
|
||||
|
||||
return (
|
||||
<Progress
|
||||
size={'$1'}
|
||||
size={'$0.75'}
|
||||
value={calculateProgressPercentage(progress)}
|
||||
backgroundColor={'$borderColor'}
|
||||
borderRadius={0}
|
||||
@@ -213,8 +202,5 @@ function MiniPlayerProgress(): React.JSX.Element {
|
||||
}
|
||||
|
||||
function calculateProgressPercentage(progress: TrackPlayerProgress | undefined): number {
|
||||
return Math.round(
|
||||
((progress!.position * ProgressMultiplier) / (progress!.duration * ProgressMultiplier)) *
|
||||
100,
|
||||
)
|
||||
return Math.round((progress!.position / progress!.duration) * 100)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,16 @@
|
||||
*/
|
||||
export const UPDATE_INTERVAL: number = 250
|
||||
|
||||
/**
|
||||
* Interval in milliseconds for the miniplayer progress updates from the track player
|
||||
*
|
||||
* Lower value provides smoother progress movement, but because of the math involved to
|
||||
* determine playback progress, updates are only visible every full second.
|
||||
*
|
||||
* This is therefore set to 1000ms
|
||||
*/
|
||||
export const MINIPLAYER_UPDATE_INTERVAL: number = 1000
|
||||
|
||||
/**
|
||||
* Indicates the seconds the progress position must be
|
||||
* less than in order to do a skip to the previous
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
SHUFFLED_QUERY_KEY,
|
||||
UNSHUFFLED_QUEUE_QUERY_KEY,
|
||||
} from '../constants/query-keys'
|
||||
import { CURRENT_INDEX_QUERY, QUEUE_QUERY } from '../constants/queries'
|
||||
import { CURRENT_INDEX_QUERY, NOW_PLAYING_QUERY, QUEUE_QUERY } from '../constants/queries'
|
||||
|
||||
export function getActiveIndex(): number | undefined {
|
||||
return queryClient.getQueryData(ACTIVE_INDEX_QUERY_KEY) as number | undefined
|
||||
@@ -52,6 +52,7 @@ export function setShuffled(shuffled: boolean): void {
|
||||
}
|
||||
|
||||
export function handleActiveTrackChanged(): void {
|
||||
queryClient.invalidateQueries(NOW_PLAYING_QUERY)
|
||||
queryClient.ensureQueryData(QUEUE_QUERY)
|
||||
queryClient.ensureQueryData(CURRENT_INDEX_QUERY)
|
||||
queryClient.invalidateQueries(CURRENT_INDEX_QUERY)
|
||||
}
|
||||
|
||||
22
src/providers/Player/functions/initialization.ts
Normal file
22
src/providers/Player/functions/initialization.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { isUndefined } from 'lodash'
|
||||
import { getActiveIndex, getPlayQueue } from '.'
|
||||
import TrackPlayer from 'react-native-track-player'
|
||||
|
||||
export default async function Initialize() {
|
||||
const storedPlayQueue = getPlayQueue()
|
||||
const storedIndex = getActiveIndex()
|
||||
|
||||
console.debug(
|
||||
`StoredIndex: ${storedIndex}, storedPlayQueue: ${storedPlayQueue?.map((track, index) => index)}`,
|
||||
)
|
||||
|
||||
if (!isUndefined(storedPlayQueue) && !isUndefined(storedIndex)) {
|
||||
console.debug('Initializing play queue from storage')
|
||||
|
||||
await TrackPlayer.reset()
|
||||
await TrackPlayer.add(storedPlayQueue)
|
||||
await TrackPlayer.skip(storedIndex)
|
||||
|
||||
console.debug('Initialized play queue from storage')
|
||||
}
|
||||
}
|
||||
@@ -30,33 +30,6 @@ const PLAYER_MUTATION_OPTIONS = {
|
||||
retry: false,
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns a mutation hook
|
||||
*/
|
||||
export const useInitialization = () =>
|
||||
useMutation({
|
||||
mutationFn: async () => {
|
||||
const storedPlayQueue = getPlayQueue()
|
||||
const storedIndex = getActiveIndex()
|
||||
|
||||
console.debug(
|
||||
`StoredIndex: ${storedIndex}, storedPlayQueue: ${storedPlayQueue?.map((track, index) => index)}`,
|
||||
)
|
||||
|
||||
if (!isUndefined(storedPlayQueue) && !isUndefined(storedIndex)) {
|
||||
console.debug('Initializing play queue from storage')
|
||||
|
||||
await TrackPlayer.reset()
|
||||
await TrackPlayer.add(storedPlayQueue)
|
||||
await TrackPlayer.skip(storedIndex)
|
||||
|
||||
console.debug('Initialized play queue from storage')
|
||||
}
|
||||
},
|
||||
onSuccess: async () => console.debug('Play Queue initialized from queryables'),
|
||||
})
|
||||
|
||||
/**
|
||||
* A mutation to handle starting playback
|
||||
*/
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
import { usePerformanceMonitor } from '../../hooks/use-performance-monitor'
|
||||
import { Event, State, useTrackPlayerEvents } from 'react-native-track-player'
|
||||
import { refetchNowPlaying } from './functions/queries'
|
||||
import { createContext, useEffect } from 'react'
|
||||
import { useAudioNormalization, useInitialization } from './hooks/mutations'
|
||||
import { useCurrentIndex, useNowPlaying, useQueue } from './hooks/queries'
|
||||
import TrackPlayer, { Event, State, useTrackPlayerEvents } from 'react-native-track-player'
|
||||
import { createContext, useCallback, useEffect } from 'react'
|
||||
import { handleActiveTrackChanged } from './functions'
|
||||
import JellifyTrack from '../../types/JellifyTrack'
|
||||
import { useIsRestoring } from '@tanstack/react-query'
|
||||
import {
|
||||
useReportPlaybackProgress,
|
||||
useReportPlaybackStarted,
|
||||
useReportPlaybackStopped,
|
||||
} from '../../api/mutations/playback'
|
||||
import { useDownloadAudioItem } from '../../api/mutations/download'
|
||||
import { useAutoDownload } from '../../stores/settings/usage'
|
||||
import { queryClient } from '../../constants/query-client'
|
||||
import { NOW_PLAYING_QUERY_KEY } from './constants/query-keys'
|
||||
import reportPlaybackStopped from '../../api/mutations/playback/functions/playback-stopped'
|
||||
import reportPlaybackCompleted from '../../api/mutations/playback/functions/playback-completed'
|
||||
import isPlaybackFinished from '../../api/mutations/playback/utils'
|
||||
import { useJellifyContext } from '..'
|
||||
import reportPlaybackProgress from '../../api/mutations/playback/functions/playback-progress'
|
||||
import reportPlaybackStarted from '../../api/mutations/playback/functions/playback-started'
|
||||
import calculateTrackVolume from './utils/normalization'
|
||||
import saveAudioItem from '../../api/mutations/download/utils'
|
||||
import { useDownloadingDeviceProfile } from '../../stores/device-profile'
|
||||
import { NOW_PLAYING_QUERY } from './constants/queries'
|
||||
import Initialize from './functions/initialization'
|
||||
|
||||
const PLAYER_EVENTS: Event[] = [
|
||||
Event.PlaybackActiveTrackChanged,
|
||||
@@ -26,78 +30,75 @@ interface PlayerContext {}
|
||||
export const PlayerContext = createContext<PlayerContext>({})
|
||||
|
||||
export const PlayerProvider: () => React.JSX.Element = () => {
|
||||
const { api } = useJellifyContext()
|
||||
|
||||
const [autoDownload] = useAutoDownload()
|
||||
|
||||
const downloadingDeviceProfile = useDownloadingDeviceProfile()
|
||||
|
||||
usePerformanceMonitor('PlayerProvider', 3)
|
||||
|
||||
const { mutate: initializePlayQueue } = useInitialization()
|
||||
|
||||
const { data: currentIndex } = useCurrentIndex()
|
||||
|
||||
const { data: playQueue } = useQueue()
|
||||
|
||||
const { data: nowPlaying } = useNowPlaying()
|
||||
|
||||
const { mutate: normalizeAudioVolume } = useAudioNormalization()
|
||||
|
||||
const { mutate: reportPlaybackStarted } = useReportPlaybackStarted()
|
||||
const { mutate: reportPlaybackProgress } = useReportPlaybackProgress()
|
||||
const { mutate: reportPlaybackStopped } = useReportPlaybackStopped()
|
||||
|
||||
const [downloadProgress, downloadAudioItem] = useDownloadAudioItem()
|
||||
|
||||
const isRestoring = useIsRestoring()
|
||||
|
||||
useTrackPlayerEvents(PLAYER_EVENTS, (event) => {
|
||||
switch (event.type) {
|
||||
case Event.PlaybackActiveTrackChanged:
|
||||
if (event.track) normalizeAudioVolume(event.track as JellifyTrack)
|
||||
const eventHandler = useCallback(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
async (event: any) => {
|
||||
let nowPlaying: JellifyTrack | undefined
|
||||
|
||||
handleActiveTrackChanged()
|
||||
refetchNowPlaying()
|
||||
switch (event.type) {
|
||||
case Event.PlaybackActiveTrackChanged:
|
||||
await handleActiveTrackChanged()
|
||||
|
||||
if (event.lastTrack)
|
||||
reportPlaybackStopped({
|
||||
track: event.lastTrack as JellifyTrack,
|
||||
lastPosition: event.lastPosition,
|
||||
duration: (event.lastTrack as JellifyTrack).duration,
|
||||
})
|
||||
break
|
||||
case Event.PlaybackProgressUpdated:
|
||||
console.debug(`Completion percentage: ${event.position / event.duration}`)
|
||||
if (nowPlaying)
|
||||
reportPlaybackProgress({
|
||||
track: nowPlaying,
|
||||
position: event.position,
|
||||
})
|
||||
if (event.track) {
|
||||
nowPlaying = event.track as JellifyTrack
|
||||
|
||||
if (event.position / event.duration > 0.3 && autoDownload && nowPlaying)
|
||||
downloadAudioItem({ item: nowPlaying.item, autoCached: true })
|
||||
break
|
||||
case Event.PlaybackState:
|
||||
switch (event.state) {
|
||||
case State.Playing:
|
||||
if (nowPlaying)
|
||||
reportPlaybackStarted({
|
||||
track: nowPlaying,
|
||||
})
|
||||
break
|
||||
case State.Paused:
|
||||
case State.Stopped:
|
||||
case State.Ended:
|
||||
if (nowPlaying)
|
||||
reportPlaybackStopped({
|
||||
track: nowPlaying,
|
||||
lastPosition: 0,
|
||||
duration: nowPlaying.duration,
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
})
|
||||
const volume = calculateTrackVolume(nowPlaying)
|
||||
await TrackPlayer.setVolume(volume)
|
||||
}
|
||||
|
||||
if (event.lastTrack)
|
||||
if (isPlaybackFinished(event.lastPosition, event.lastTrack.duration ?? 1))
|
||||
await reportPlaybackCompleted(api, event.lastTrack as JellifyTrack)
|
||||
else await reportPlaybackStopped(api, event.lastTrack as JellifyTrack)
|
||||
|
||||
break
|
||||
|
||||
case Event.PlaybackProgressUpdated:
|
||||
console.debug(`Completion percentage: ${event.position / event.duration}`)
|
||||
|
||||
nowPlaying = queryClient.getQueryData<JellifyTrack>(NOW_PLAYING_QUERY_KEY)
|
||||
|
||||
if (nowPlaying) {
|
||||
reportPlaybackProgress(api, nowPlaying, event.position)
|
||||
}
|
||||
|
||||
if (event.position / event.duration > 0.3 && autoDownload && nowPlaying)
|
||||
saveAudioItem(api, nowPlaying.item, downloadingDeviceProfile, true)
|
||||
break
|
||||
|
||||
case Event.PlaybackState:
|
||||
nowPlaying = queryClient.getQueryData<JellifyTrack>(NOW_PLAYING_QUERY_KEY)
|
||||
|
||||
switch (event.state) {
|
||||
case State.Playing:
|
||||
if (nowPlaying) reportPlaybackStarted(api, nowPlaying)
|
||||
queryClient.ensureQueryData(NOW_PLAYING_QUERY)
|
||||
break
|
||||
case State.Paused:
|
||||
case State.Stopped:
|
||||
case State.Ended:
|
||||
if (nowPlaying) reportPlaybackStopped(api, nowPlaying)
|
||||
}
|
||||
break
|
||||
}
|
||||
},
|
||||
[api, autoDownload],
|
||||
)
|
||||
|
||||
useTrackPlayerEvents(PLAYER_EVENTS, eventHandler)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isRestoring) initializePlayQueue()
|
||||
if (!isRestoring) Initialize()
|
||||
}, [isRestoring])
|
||||
|
||||
return (
|
||||
|
||||
@@ -19,11 +19,11 @@ import {
|
||||
EncodingContext,
|
||||
MediaStreamProtocol,
|
||||
} from '@jellyfin/sdk/lib/generated-client'
|
||||
import { Platform } from 'react-native'
|
||||
import { getQualityParams } from './mappings'
|
||||
import { capitalize } from 'lodash'
|
||||
import { SourceType } from '../types/JellifyTrack'
|
||||
import StreamingQuality from '../enums/audio-quality'
|
||||
import uuid from 'react-native-uuid'
|
||||
|
||||
/**
|
||||
* A constant that defines the options for the {@link useDeviceProfile} hook - building the
|
||||
@@ -39,6 +39,7 @@ export function getDeviceProfile(
|
||||
type: SourceType,
|
||||
): DeviceProfile {
|
||||
return {
|
||||
Id: uuid.v4(),
|
||||
Name: `${capitalize(streamingQuality)} Quality Audio ${capitalize(type)}`,
|
||||
MaxStaticBitrate:
|
||||
streamingQuality === 'original'
|
||||
|
||||
224
yarn.lock
224
yarn.lock
@@ -2203,96 +2203,96 @@
|
||||
resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8"
|
||||
integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==
|
||||
|
||||
"@sentry-internal/browser-utils@8.54.0":
|
||||
version "8.54.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.54.0.tgz#2d68c7fa843db867ed98059faf1a750be3eca95a"
|
||||
integrity sha512-DKWCqb4YQosKn6aD45fhKyzhkdG7N6goGFDeyTaJFREJDFVDXiNDsYZu30nJ6BxMM7uQIaARhPAC5BXfoED3pQ==
|
||||
"@sentry-internal/browser-utils@10.8.0":
|
||||
version "10.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-10.8.0.tgz#a028e8067566cb80026c112cd62b5c119415e337"
|
||||
integrity sha512-FaQX9eefc8sh3h3ZQy16U73KiH0xgDldXnrFiWK6OeWg8X4bJpnYbLqEi96LgHiQhjnnz+UQP1GDzH5oFuu5fA==
|
||||
dependencies:
|
||||
"@sentry/core" "8.54.0"
|
||||
"@sentry/core" "10.8.0"
|
||||
|
||||
"@sentry-internal/feedback@8.54.0":
|
||||
version "8.54.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.54.0.tgz#52c3a63aa5b520eca7acfa1376621e8441984126"
|
||||
integrity sha512-nQqRacOXoElpE0L0ADxUUII0I3A94niqG9Z4Fmsw6057QvyrV/LvTiMQBop6r5qLjwMqK+T33iR4/NQI5RhsXQ==
|
||||
"@sentry-internal/feedback@10.8.0":
|
||||
version "10.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-10.8.0.tgz#fa562fe64c806d429f4c33acd8e7ca0f929053f3"
|
||||
integrity sha512-n7SqgFQItq4QSPG7bCjcZcIwK6AatKnnmSDJ/i6e8jXNIyLwkEuY2NyvTXACxVdO/kafGD5VmrwnTo3Ekc1AMg==
|
||||
dependencies:
|
||||
"@sentry/core" "8.54.0"
|
||||
"@sentry/core" "10.8.0"
|
||||
|
||||
"@sentry-internal/replay-canvas@8.54.0":
|
||||
version "8.54.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.54.0.tgz#e57a3893db2bb0ea7ad9dc2a804bb035142fe3ba"
|
||||
integrity sha512-K/On3OAUBeq/TV2n+1EvObKC+WMV9npVXpVyJqCCyn8HYMm8FUGzuxeajzm0mlW4wDTPCQor6mK9/IgOquUzCw==
|
||||
"@sentry-internal/replay-canvas@10.8.0":
|
||||
version "10.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-10.8.0.tgz#5adc2f3aa7036fed46415fcb6eabd4d8a6d335e3"
|
||||
integrity sha512-jC4OOwiNgrlIPeXIPMLkaW53BSS1do+toYHoWzzO5AXGpN6jRhanoSj36FpVuH2N3kFnxKVfVxrwh8L+/3vFWg==
|
||||
dependencies:
|
||||
"@sentry-internal/replay" "8.54.0"
|
||||
"@sentry/core" "8.54.0"
|
||||
"@sentry-internal/replay" "10.8.0"
|
||||
"@sentry/core" "10.8.0"
|
||||
|
||||
"@sentry-internal/replay@8.54.0":
|
||||
version "8.54.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.54.0.tgz#b92990a51ffbe8d92998ff8188db9e3a6f9d1e18"
|
||||
integrity sha512-8xuBe06IaYIGJec53wUC12tY2q4z2Z0RPS2s1sLtbA00EvK1YDGuXp96IDD+HB9mnDMrQ/jW5f97g9TvPsPQUg==
|
||||
"@sentry-internal/replay@10.8.0":
|
||||
version "10.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-10.8.0.tgz#3f14329924ff744611296ce4e9588530e5f168e4"
|
||||
integrity sha512-9+qDEoEjv4VopLuOzK1zM4LcvcUsvB5N0iJ+FRCM3XzzOCbebJOniXTQbt5HflJc3XLnQNKFdKfTfgj8M/0RKQ==
|
||||
dependencies:
|
||||
"@sentry-internal/browser-utils" "8.54.0"
|
||||
"@sentry/core" "8.54.0"
|
||||
"@sentry-internal/browser-utils" "10.8.0"
|
||||
"@sentry/core" "10.8.0"
|
||||
|
||||
"@sentry/babel-plugin-component-annotate@3.5.0":
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-3.5.0.tgz#1b0d01f903b725da876117d551610085c3dd21c7"
|
||||
integrity sha512-s2go8w03CDHbF9luFGtBHKJp4cSpsQzNVqgIa9Pfa4wnjipvrK6CxVT4icpLA3YO6kg5u622Yoa5GF3cJdippw==
|
||||
"@sentry/babel-plugin-component-annotate@4.3.0":
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-4.3.0.tgz#c5b6cbb986952596d3ad233540a90a1fd18bad80"
|
||||
integrity sha512-OuxqBprXRyhe8Pkfyz/4yHQJc5c3lm+TmYWSSx8u48g5yKewSQDOxkiLU5pAk3WnbLPy8XwU/PN+2BG0YFU9Nw==
|
||||
|
||||
"@sentry/browser@8.54.0":
|
||||
version "8.54.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.54.0.tgz#5487075908aac564892e689e1b6d233fdb314f5b"
|
||||
integrity sha512-BgUtvxFHin0fS0CmJVKTLXXZcke0Av729IVfi+2fJ4COX8HO7/HAP02RKaSQGmL2HmvWYTfNZ7529AnUtrM4Rg==
|
||||
"@sentry/browser@10.8.0":
|
||||
version "10.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-10.8.0.tgz#f33b95f48e3967db661db1666ad3e1dc0a6922fb"
|
||||
integrity sha512-2J7HST8/ixCaboq17yFn/j/OEokXSXoCBMXRrFx4FKJggKWZ90e2Iau5mP/IPPhrW+W9zCptCgNMY0167wS4qA==
|
||||
dependencies:
|
||||
"@sentry-internal/browser-utils" "8.54.0"
|
||||
"@sentry-internal/feedback" "8.54.0"
|
||||
"@sentry-internal/replay" "8.54.0"
|
||||
"@sentry-internal/replay-canvas" "8.54.0"
|
||||
"@sentry/core" "8.54.0"
|
||||
"@sentry-internal/browser-utils" "10.8.0"
|
||||
"@sentry-internal/feedback" "10.8.0"
|
||||
"@sentry-internal/replay" "10.8.0"
|
||||
"@sentry-internal/replay-canvas" "10.8.0"
|
||||
"@sentry/core" "10.8.0"
|
||||
|
||||
"@sentry/cli-darwin@2.47.0":
|
||||
version "2.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.47.0.tgz#171d4ed94a035b35e7cba21e133351fa1d8a5664"
|
||||
integrity sha512-xEFppdMQogV1A85A/s+Al1VH0NHXk7syy+5BL/jYd168FPeVB3iERP0AwP4h9UhR3/wTe1lTb+tfOKpXrECLCw==
|
||||
"@sentry/cli-darwin@2.53.0":
|
||||
version "2.53.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.53.0.tgz#0584f5a4a376c9373f91ad5e1d9194278be2aed6"
|
||||
integrity sha512-NNPfpILMwKgpHiyJubHHuauMKltkrgLQ5tvMdxNpxY60jBNdo5VJtpESp4XmXlnidzV4j1z61V4ozU6ttDgt5Q==
|
||||
|
||||
"@sentry/cli-linux-arm64@2.47.0":
|
||||
version "2.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.47.0.tgz#9802fe08164217483926ce8a1becf45f740fea20"
|
||||
integrity sha512-qjF87W0Vo5vITbm4GXjtX8uQCDRg2gVT0yP1Uz12IuBri80iJj66IANX1wbae2mG2Io1Ibc4AKN5FWd2HpPiKw==
|
||||
"@sentry/cli-linux-arm64@2.53.0":
|
||||
version "2.53.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.53.0.tgz#04a73b2592edf10d6e06957905becc98692605b1"
|
||||
integrity sha512-xY/CZ1dVazsSCvTXzKpAgXaRqfljVfdrFaYZRUaRPf1ZJRGa3dcrivoOhSIeG/p5NdYtMvslMPY9Gm2MT0M83A==
|
||||
|
||||
"@sentry/cli-linux-arm@2.47.0":
|
||||
version "2.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.47.0.tgz#32221dcfe46024babf43d846a1e2d05a57b153a8"
|
||||
integrity sha512-tE8gDcp2qFCTtndz1ViLAo+JNQEEjniBFJAWIFh1utJKwBxBStB0JDporOZHvWUcnSCP5F+W59iuir2YAAQh/w==
|
||||
"@sentry/cli-linux-arm@2.53.0":
|
||||
version "2.53.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.53.0.tgz#caa1dceb23ee40e9d0c82a7c6156c3f010eebc0e"
|
||||
integrity sha512-NdRzQ15Ht83qG0/Lyu11ciy/Hu/oXbbtJUgwzACc7bWvHQA8xEwTsehWexqn1529Kfc5EjuZ0Wmj3MHmp+jOWw==
|
||||
|
||||
"@sentry/cli-linux-i686@2.47.0":
|
||||
version "2.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.47.0.tgz#e551e813e8060de161d6058664e252c952f22291"
|
||||
integrity sha512-5FYe1dth06xThbr41AOvX67oKZr4xqtDwHJvpFdyCdf+Yh5E5/rtPX35K1beMERgVyT+whRetrNBFAcHnp6LaA==
|
||||
"@sentry/cli-linux-i686@2.53.0":
|
||||
version "2.53.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.53.0.tgz#989dc766b098e94c6751bad3efcd4ca0fe1a2565"
|
||||
integrity sha512-0REmBibGAB4jtqt9S6JEsFF4QybzcXHPcHtJjgMi5T0ueh952uG9wLzjSxQErCsxTKF+fL8oG0Oz5yKBuCwCCQ==
|
||||
|
||||
"@sentry/cli-linux-x64@2.47.0":
|
||||
version "2.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.47.0.tgz#56c257e4df8466709fb80ec21e1b5350ee713464"
|
||||
integrity sha512-wq67T2UpbTst//1lZGDTeFa7nKsnOpP8rS34TQ3GxsGU1LOjinl9zYl0mUPsoVXIHbWxTHlU6YDNf0q0eB7ddA==
|
||||
"@sentry/cli-linux-x64@2.53.0":
|
||||
version "2.53.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.53.0.tgz#2a94361233ed24e4a32f08919011a591aea4cb6b"
|
||||
integrity sha512-9UGJL+Vy5N/YL1EWPZ/dyXLkShlNaDNrzxx4G7mTS9ywjg+BIuemo6rnN7w43K1NOjObTVO6zY0FwumJ1pCyLg==
|
||||
|
||||
"@sentry/cli-win32-arm64@2.47.0":
|
||||
version "2.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.47.0.tgz#ce7914253f15160373e856e48738932f03adef41"
|
||||
integrity sha512-a1sv44bMe35V9eW9Zk/kYymXswzJ/RHXNRjkFnW1m1iXx6NauQD3sjEgkryu3UmuvKO9g3pBkMMT1u6xB/08QA==
|
||||
"@sentry/cli-win32-arm64@2.53.0":
|
||||
version "2.53.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.53.0.tgz#946609eabd318657521c4b3ef15a420cc00f1c60"
|
||||
integrity sha512-G1kjOjrjMBY20rQcJV2GA8KQE74ufmROCDb2GXYRfjvb1fKAsm4Oh8N5+Tqi7xEHdjQoLPkE4CNW0aH68JSUDQ==
|
||||
|
||||
"@sentry/cli-win32-i686@2.47.0":
|
||||
version "2.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.47.0.tgz#c6414696b7031a590520b01e8a062e0fe71874be"
|
||||
integrity sha512-QKLSCED00jNHC4cu9GutLWaFAy5vdVGDrIPvVdztSFLS2fRMhRSSPE8tJwlSYh2OfdHhUHQbMOo58cDVfEklBg==
|
||||
"@sentry/cli-win32-i686@2.53.0":
|
||||
version "2.53.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.53.0.tgz#f51937d73cefad16b9d2e89acc4c9f178da36cc6"
|
||||
integrity sha512-qbGTZUzesuUaPtY9rPXdNfwLqOZKXrJRC1zUFn52hdo6B+Dmv0m/AHwRVFHZP53Tg1NCa8bDei2K/uzRN0dUZw==
|
||||
|
||||
"@sentry/cli-win32-x64@2.47.0":
|
||||
version "2.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.47.0.tgz#96ceaf75a9ffb39fd255f11fa2b85f4e7e26b591"
|
||||
integrity sha512-XcM+I7eWpSp8khy44djunVvQKSMsBP698j0swA41Pd1JL0mxLFV/4P9wfWZw1RRB8R71jks74kZvM45AAh2FZw==
|
||||
"@sentry/cli-win32-x64@2.53.0":
|
||||
version "2.53.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.53.0.tgz#d89cde8354b4eb8e89f2c11dc6a6fb5e7392e2ae"
|
||||
integrity sha512-1TXYxYHtwgUq5KAJt3erRzzUtPqg7BlH9T7MdSPHjJatkrr/kwZqnVe2H6Arr/5NH891vOlIeSPHBdgJUAD69g==
|
||||
|
||||
"@sentry/cli@2.47.0":
|
||||
version "2.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.47.0.tgz#d9a3309d281d03e23651a68e81fbd04c60f52bfe"
|
||||
integrity sha512-M1zAbc74rGqcWXPi4vowNY7plADjsvKVEZhyUcSq+K3JtZOQ1m1QJgSno31hLbK9V4sx4qyDesNEcBtUjof07w==
|
||||
"@sentry/cli@2.53.0":
|
||||
version "2.53.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.53.0.tgz#fd5b65b9f6f06f0ed16345acf3ecf0720bd7bcf8"
|
||||
integrity sha512-n2ZNb+5Z6AZKQSI0SusQ7ZzFL637mfw3Xh4C3PEyVSn9LiF683fX0TTq8OeGmNZQS4maYfS95IFD+XpydU0dEA==
|
||||
dependencies:
|
||||
https-proxy-agent "^5.0.0"
|
||||
node-fetch "^2.6.7"
|
||||
@@ -2300,55 +2300,47 @@
|
||||
proxy-from-env "^1.1.0"
|
||||
which "^2.0.2"
|
||||
optionalDependencies:
|
||||
"@sentry/cli-darwin" "2.47.0"
|
||||
"@sentry/cli-linux-arm" "2.47.0"
|
||||
"@sentry/cli-linux-arm64" "2.47.0"
|
||||
"@sentry/cli-linux-i686" "2.47.0"
|
||||
"@sentry/cli-linux-x64" "2.47.0"
|
||||
"@sentry/cli-win32-arm64" "2.47.0"
|
||||
"@sentry/cli-win32-i686" "2.47.0"
|
||||
"@sentry/cli-win32-x64" "2.47.0"
|
||||
"@sentry/cli-darwin" "2.53.0"
|
||||
"@sentry/cli-linux-arm" "2.53.0"
|
||||
"@sentry/cli-linux-arm64" "2.53.0"
|
||||
"@sentry/cli-linux-i686" "2.53.0"
|
||||
"@sentry/cli-linux-x64" "2.53.0"
|
||||
"@sentry/cli-win32-arm64" "2.53.0"
|
||||
"@sentry/cli-win32-i686" "2.53.0"
|
||||
"@sentry/cli-win32-x64" "2.53.0"
|
||||
|
||||
"@sentry/core@8.54.0":
|
||||
version "8.54.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.54.0.tgz#a2ebec965cadcb6de89e116689feeef79d5862a6"
|
||||
integrity sha512-03bWf+D1j28unOocY/5FDB6bUHtYlm6m6ollVejhg45ZmK9iPjdtxNWbrLsjT1WRym0Tjzowu+A3p+eebYEv0Q==
|
||||
"@sentry/core@10.8.0":
|
||||
version "10.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-10.8.0.tgz#71f71f8ecb5a06c41a426f5ccee2d9767b7d7cfd"
|
||||
integrity sha512-scYzM/UOItu4PjEq6CpHLdArpXjIS0laHYxE4YjkIbYIH6VMcXGQbD/FSBClsnCr1wXRnlXfXBzj0hrQAFyw+Q==
|
||||
|
||||
"@sentry/react-native@6.17.0":
|
||||
version "6.17.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/react-native/-/react-native-6.17.0.tgz#d897af3861251ced6ca0f60d015e1806b4ec575d"
|
||||
integrity sha512-R8cQHE5wtespva5tYtBc620xI1+w5dI53fotdzX+ONxHs5G31Da9O1OwYLH9hh2WouIf4dDXfBSmnKoVXL79+Q==
|
||||
"@sentry/react-native@7.0.1":
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/react-native/-/react-native-7.0.1.tgz#688a68bb1ec12a9e18577c72477593989ad1e6c0"
|
||||
integrity sha512-xz8ON51qSDvcHVFkdLo0b7rlrQVXpRVXqzm7e1+nHEZ07TX0o+utxx04akxD1Z4hmGPTWPmsHeMlm7diV9NtTQ==
|
||||
dependencies:
|
||||
"@sentry/babel-plugin-component-annotate" "3.5.0"
|
||||
"@sentry/browser" "8.54.0"
|
||||
"@sentry/cli" "2.47.0"
|
||||
"@sentry/core" "8.54.0"
|
||||
"@sentry/react" "8.54.0"
|
||||
"@sentry/types" "8.54.0"
|
||||
"@sentry/utils" "8.54.0"
|
||||
"@sentry/babel-plugin-component-annotate" "4.3.0"
|
||||
"@sentry/browser" "10.8.0"
|
||||
"@sentry/cli" "2.53.0"
|
||||
"@sentry/core" "10.8.0"
|
||||
"@sentry/react" "10.8.0"
|
||||
"@sentry/types" "10.8.0"
|
||||
|
||||
"@sentry/react@8.54.0":
|
||||
version "8.54.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-8.54.0.tgz#16cec103b5d5697bdfebacf6e2d35f19699b3ab3"
|
||||
integrity sha512-42T/fp8snYN19Fy/2P0Mwotu4gcdy+1Lx+uYCNcYP1o7wNGigJ7qb27sW7W34GyCCHjoCCfQgeOqDQsyY8LC9w==
|
||||
"@sentry/react@10.8.0":
|
||||
version "10.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-10.8.0.tgz#51020c04d9d6ce2a9edc3aa1bd961823f1a27561"
|
||||
integrity sha512-w/dGLMCLJG2lp8gKVKX1jjeg2inXewKfPb73+PS1CDi9/ihvqZU2DAXxnaNsBA7YYtGwlWVJe1bLAqguwTEpqw==
|
||||
dependencies:
|
||||
"@sentry/browser" "8.54.0"
|
||||
"@sentry/core" "8.54.0"
|
||||
"@sentry/browser" "10.8.0"
|
||||
"@sentry/core" "10.8.0"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
|
||||
"@sentry/types@8.54.0":
|
||||
version "8.54.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.54.0.tgz#1d57bb094443081de4e0d8b638e6ebc40f5ddd36"
|
||||
integrity sha512-wztdtr7dOXQKi0iRvKc8XJhJ7HaAfOv8lGu0yqFOFwBZucO/SHnu87GOPi8mvrTiy1bentQO5l+zXWAaMvG4uw==
|
||||
"@sentry/types@10.8.0":
|
||||
version "10.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-10.8.0.tgz#3c87e7d48bf2755841e75e078ff0fb17ff36a37f"
|
||||
integrity sha512-xRe41/KvnNt4o6t5YeB+yBRTWvLUu6FJpft/VBOs4Bfh1/6rz+l78oxSCtpXo3MsfTd5185I0uuggAjEdD4Y6g==
|
||||
dependencies:
|
||||
"@sentry/core" "8.54.0"
|
||||
|
||||
"@sentry/utils@8.54.0":
|
||||
version "8.54.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.54.0.tgz#5e28e03a249451b4a55200a0787f4e2c59bab2c5"
|
||||
integrity sha512-JL8UDjrsKxKclTdLXfuHfE7B3KbrAPEYP7tMyN/xiO2vsF6D84fjwYyalO0ZMtuFZE6vpSze8ZOLEh6hLnPYsw==
|
||||
dependencies:
|
||||
"@sentry/core" "8.54.0"
|
||||
"@sentry/core" "10.8.0"
|
||||
|
||||
"@shopify/flash-list@^2.0.3":
|
||||
version "2.0.3"
|
||||
@@ -8554,10 +8546,10 @@ react-native-ota-hot-update@2.3.1:
|
||||
buffer "^6.0.3"
|
||||
isomorphic-git "1.27.3"
|
||||
|
||||
react-native-pager-view@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-pager-view/-/react-native-pager-view-7.0.0.tgz#09e72960776d2a95d7b0bc5c71aa2b808a7ca4a4"
|
||||
integrity sha512-RgpGiTqE7UoSvCMG//AkiMrReF3NxtCfeCiRSvqxhYlTwHq3jMFWqAyZsyEepvCHYCYnMSmRGTMxv9koziat7g==
|
||||
react-native-pager-view@^6.9.1:
|
||||
version "6.9.1"
|
||||
resolved "https://registry.yarnpkg.com/react-native-pager-view/-/react-native-pager-view-6.9.1.tgz#a9e6d9323935cc2ae1d46d7816b66f76dc3eff8e"
|
||||
integrity sha512-uUT0MMMbNtoSbxe9pRvdJJKEi9snjuJ3fXlZhG8F2vVMOBJVt/AFtqMPUHu9yMflmqOr08PewKzj9EPl/Yj+Gw==
|
||||
|
||||
react-native-reanimated@4.0.2:
|
||||
version "4.0.2"
|
||||
|
||||
Reference in New Issue
Block a user