Skip to content

Commit 8a191b9

Browse files
context sheet prefetching
unit tests update rn screens
1 parent 24d3aa4 commit 8a191b9

File tree

24 files changed

+481
-384
lines changed

24 files changed

+481
-384
lines changed

App.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import OTAUpdateScreen from './src/components/OtaUpdates'
2424
import { usePerformanceMonitor } from './src/hooks/use-performance-monitor'
2525
import { SettingsProvider, useThemeSettingContext } from './src/providers/Settings'
2626
import navigationRef from './navigation'
27+
import { PROGRESS_UPDATE_EVENT_INTERVAL } from './src/player/config'
2728

2829
export default function App(): React.JSX.Element {
2930
// Add performance monitoring to track app-level re-renders
@@ -59,7 +60,7 @@ export default function App(): React.JSX.Element {
5960
capabilities: CAPABILITIES,
6061
notificationCapabilities: CAPABILITIES,
6162
// Reduced interval for smoother progress tracking and earlier prefetch detection
62-
progressUpdateEventInterval: 5,
63+
progressUpdateEventInterval: PROGRESS_UPDATE_EVENT_INTERVAL,
6364
}),
6465
)
6566
.finally(() => {

ios/Podfile.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2719,7 +2719,7 @@ PODS:
27192719
- RNWorklets
27202720
- SocketRocket
27212721
- Yoga
2722-
- RNScreens (4.14.1):
2722+
- RNScreens (4.15.0):
27232723
- boost
27242724
- DoubleConversion
27252725
- fast_float
@@ -2746,10 +2746,10 @@ PODS:
27462746
- ReactCodegen
27472747
- ReactCommon/turbomodule/bridging
27482748
- ReactCommon/turbomodule/core
2749-
- RNScreens/common (= 4.14.1)
2749+
- RNScreens/common (= 4.15.0)
27502750
- SocketRocket
27512751
- Yoga
2752-
- RNScreens/common (4.14.1):
2752+
- RNScreens/common (4.15.0):
27532753
- boost
27542754
- DoubleConversion
27552755
- fast_float
@@ -3306,7 +3306,7 @@ SPEC CHECKSUMS:
33063306
RNGestureHandler: 3a73f098d74712952870e948b3d9cf7b6cae9961
33073307
RNReactNativeHapticFeedback: be4f1b4bf0398c30b59b76ed92ecb0a2ff3a69c6
33083308
RNReanimated: ee96d03fe3713993a30cc205522792b4cb08e4f9
3309-
RNScreens: 6ced6ae8a526512a6eef6e28c2286e1fc2d378c3
3309+
RNScreens: 48bbaca97a5f9aedc3e52bd48673efd2b6aac4f6
33103310
RNSentry: 95e1ed0ede28a4af58aaafedeac9fcfaba0e89ce
33113311
RNWorklets: e8335dff9d27004709f58316985769040cd1e8f2
33123312
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Progress } from 'react-native-track-player'
2+
import { shouldMarkPlaybackFinished } from '../../src/providers/Player/utils/handlers'
3+
4+
describe('Playback Event Handlers', () => {
5+
it('should determine that the track has finished', () => {
6+
const progress: Progress = {
7+
position: 95.23423453,
8+
duration: 98.23557854,
9+
buffered: 98.2345568679345,
10+
}
11+
12+
const playbackFinished = shouldMarkPlaybackFinished(progress)
13+
14+
expect(playbackFinished).toBeTruthy()
15+
})
16+
17+
it('should determine the track is still playing', () => {
18+
const progress: Progress = {
19+
position: 85.23423453,
20+
duration: 98.23557854,
21+
buffered: 98.2345568679345,
22+
}
23+
24+
const playbackFinished = shouldMarkPlaybackFinished(progress)
25+
26+
expect(playbackFinished).toBeFalsy()
27+
})
28+
})

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
"react-native-pager-view": "^7.0.0",
8383
"react-native-reanimated": "4.0.2",
8484
"react-native-safe-area-context": "^5.6.0",
85-
"react-native-screens": "4.14.1",
85+
"react-native-screens": "4.15.0",
8686
"react-native-swipeable-item": "^2.0.9",
8787
"react-native-text-ticker": "^1.15.0",
8888
"react-native-toast-message": "^2.3.3",

src/api/queries/media.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Api } from '@jellyfin/sdk'
2-
import { BaseItemDto, PlaybackInfoResponse } from '@jellyfin/sdk/lib/generated-client/models'
3-
import { getAudioApi, getMediaInfoApi } from '@jellyfin/sdk/lib/utils/api'
2+
import { PlaybackInfoResponse } from '@jellyfin/sdk/lib/generated-client/models'
3+
import { getMediaInfoApi } from '@jellyfin/sdk/lib/utils/api'
44
import { isUndefined } from 'lodash'
55
import { JellifyUser } from '../../types/JellifyUser'
66
import { AudioQuality } from '../../types/AudioQuality'
@@ -9,7 +9,7 @@ export async function fetchMediaInfo(
99
api: Api | undefined,
1010
user: JellifyUser | undefined,
1111
bitrate: AudioQuality | undefined,
12-
item: BaseItemDto,
12+
itemId: string,
1313
): Promise<PlaybackInfoResponse> {
1414
console.debug(`Fetching media info of quality ${JSON.stringify(bitrate)}`)
1515

@@ -19,7 +19,7 @@ export async function fetchMediaInfo(
1919

2020
getMediaInfoApi(api)
2121
.getPostedPlaybackInfo({
22-
itemId: item.Id!,
22+
itemId: itemId!,
2323
userId: user.id,
2424
playbackInfoDto: {
2525
MaxAudioChannels: bitrate?.MaxAudioBitDepth

src/api/queries/suggestions.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getItemsApi } from '@jellyfin/sdk/lib/utils/api'
1+
import { getArtistsApi, getItemsApi } from '@jellyfin/sdk/lib/utils/api'
22
import { BaseItemDto, BaseItemKind, ItemFields } from '@jellyfin/sdk/lib/generated-client/models'
33
import { Api } from '@jellyfin/sdk'
44
import { isUndefined } from 'lodash'
@@ -54,14 +54,12 @@ export async function fetchArtistSuggestions(
5454
if (isUndefined(user)) return reject('User has not been set')
5555
if (isUndefined(libraryId)) return reject('Library has not been set')
5656

57-
getItemsApi(api)
58-
.getItems({
57+
getArtistsApi(api)
58+
.getAlbumArtists({
5959
parentId: libraryId,
6060
userId: user.id,
61-
recursive: true,
6261
limit: 50,
6362
startIndex: page * 50,
64-
includeItemTypes: [BaseItemKind.MusicArtist],
6563
fields: [ItemFields.ChildCount, ItemFields.SortName],
6664
sortBy: ['Random'],
6765
})

src/components/AddToPlaylist/index.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,13 @@ export default function AddToPlaylist({ track }: { track: BaseItemDto }): React.
120120
return (
121121
<ScrollView>
122122
<XStack gap={'$2'} margin={'$4'}>
123-
<ItemImage item={track} />
123+
<ItemImage item={track} width={'$12'} height={'$12'} />
124124

125125
<YStack gap={'$2'} margin={'$2'}>
126126
<TextTicker {...TextTickerConfig}>
127-
<Text bold>{getItemName(track)}</Text>
127+
<Text bold fontSize={'$6'}>
128+
{getItemName(track)}
129+
</Text>
128130
</TextTicker>
129131

130132
<TextTicker {...TextTickerConfig}>

src/components/Context/index.tsx

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ export default function ItemContext({ item, stackNavigation }: ContextProps): Re
7979

8080
const renderViewAlbumRow = useMemo(() => isAlbum || (isTrack && album), [album, item])
8181

82+
const artistIds = !isPlaylist
83+
? isArtist
84+
? [item.Id]
85+
: item.ArtistItems
86+
? item.ArtistItems.map((item) => item.Id)
87+
: []
88+
: []
89+
8290
return (
8391
<ScrollView>
8492
<YGroup unstyled marginBottom={bottom}>
@@ -108,10 +116,7 @@ export default function ItemContext({ item, stackNavigation }: ContextProps): Re
108116
)}
109117

110118
{!isPlaylist && (
111-
<ViewArtistMenuRow
112-
artists={isArtist ? [item] : itemArtists}
113-
stackNavigation={stackNavigation}
114-
/>
119+
<ArtistMenuRows artistIds={artistIds} stackNavigation={stackNavigation} />
115120
)}
116121
</YGroup>
117122
</ScrollView>
@@ -210,13 +215,37 @@ function ViewAlbumMenuRow({ album: album, stackNavigation }: MenuRowProps): Reac
210215
)
211216
}
212217

218+
function ArtistMenuRows({
219+
artistIds,
220+
stackNavigation,
221+
}: {
222+
artistIds: (string | null | undefined)[]
223+
stackNavigation: StackNavigation | undefined
224+
}): React.JSX.Element {
225+
return (
226+
<View>
227+
{artistIds.map((id) => (
228+
<ViewArtistMenuRow artistId={id} key={id} stackNavigation={stackNavigation} />
229+
))}
230+
</View>
231+
)
232+
}
233+
213234
function ViewArtistMenuRow({
214-
artists,
235+
artistId,
215236
stackNavigation,
216237
}: {
217-
artists: BaseItemDto[]
238+
artistId: string | null | undefined
218239
stackNavigation: StackNavigation | undefined
219240
}): React.JSX.Element {
241+
const { api } = useJellifyContext()
242+
243+
const { data: artist } = useQuery({
244+
queryKey: [QueryKeys.ArtistById, artistId],
245+
queryFn: () => fetchItem(api, artistId!),
246+
enabled: !!artistId,
247+
})
248+
220249
const goToArtist = useCallback(
221250
(artist: BaseItemDto) => {
222251
if (stackNavigation) stackNavigation.navigate('Artist', { artist })
@@ -225,23 +254,20 @@ function ViewArtistMenuRow({
225254
[stackNavigation, navigationRef],
226255
)
227256

228-
return (
229-
<View>
230-
{artists.map((artist, index) => (
231-
<ListItem
232-
animation={'quick'}
233-
backgroundColor={'transparent'}
234-
gap={'$3'}
235-
justifyContent='flex-start'
236-
key={index}
237-
onPress={() => goToArtist(artist)}
238-
pressStyle={{ opacity: 0.5 }}
239-
>
240-
<ItemImage circular item={artist} height={'$10'} width={'$10'} />
241-
242-
<Text bold>{`Go to ${getItemName(artist)}`}</Text>
243-
</ListItem>
244-
))}
245-
</View>
257+
return artist ? (
258+
<ListItem
259+
animation={'quick'}
260+
backgroundColor={'transparent'}
261+
gap={'$3'}
262+
justifyContent='flex-start'
263+
onPress={() => goToArtist(artist)}
264+
pressStyle={{ opacity: 0.5 }}
265+
>
266+
<ItemImage circular item={artist} height={'$10'} width={'$10'} />
267+
268+
<Text bold>{`Go to ${getItemName(artist)}`}</Text>
269+
</ListItem>
270+
) : (
271+
<></>
246272
)
247273
}

src/components/Context/utils/navigation.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,17 @@ import { InteractionManager } from 'react-native'
66
export function goToAlbumFromContextSheet(album: BaseItemDto | undefined) {
77
if (!navigationRef.isReady() || !album) return
88

9-
// Pop Context Sheet and Player Modal
10-
navigationRef.dispatch(StackActions.popTo('Tabs'))
9+
// Pop Context Sheet
10+
navigationRef.dispatch(StackActions.pop())
1111

12-
const route = navigationRef.current?.getCurrentRoute()
12+
let route = navigationRef.current?.getCurrentRoute()
13+
14+
// If we've popped into the player, pop that as well
15+
if (route?.name.includes('Player')) {
16+
navigationRef.dispatch(StackActions.pop())
17+
18+
route = navigationRef.current?.getCurrentRoute()
19+
}
1320

1421
if (route?.name.includes('Settings')) {
1522
navigationRef.dispatch(TabActions.jumpTo('LibraryTab'))
@@ -22,10 +29,17 @@ export function goToAlbumFromContextSheet(album: BaseItemDto | undefined) {
2229
export function goToArtistFromContextSheet(artist: BaseItemDto | undefined) {
2330
if (!navigationRef.isReady() || !artist) return
2431

25-
// Pop Context Sheet and Player Modal
26-
navigationRef.dispatch(StackActions.popTo('Tabs'))
32+
// Pop Context Sheet
33+
navigationRef.dispatch(StackActions.pop())
34+
35+
let route = navigationRef.current?.getCurrentRoute()
36+
37+
// If we've popped into the player, pop that as well
38+
if (route?.name.includes('Player')) {
39+
navigationRef.dispatch(StackActions.pop())
2740

28-
const route = navigationRef.current?.getCurrentRoute()
41+
route = navigationRef.current?.getCurrentRoute()
42+
}
2943

3044
if (route?.name.includes('Settings')) {
3145
navigationRef.dispatch(TabActions.jumpTo('LibraryTab'))

src/components/Global/components/image.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,7 @@ function getBorderRadius(circular: boolean | undefined, width: Token | number |
8383
if (circular) {
8484
borderRadius = width ? (typeof width === 'number' ? width : getTokenValue(width)) : '100%'
8585
} else if (!isUndefined(width)) {
86-
borderRadius =
87-
typeof width === 'number'
88-
? width / 25
89-
: getTokenValue(width) / (getTokenValue(width) / 6)
86+
borderRadius = typeof width === 'number' ? width / 25 : getTokenValue(width) / 15
9087
} else borderRadius = '5%'
9188

9289
return borderRadius

0 commit comments

Comments
 (0)