From fd97126f2f68d329728eee15d372654bc505f1a5 Mon Sep 17 00:00:00 2001 From: Violet Caulfield <42452695+anultravioletaurora@users.noreply.github.com> Date: Tue, 15 Jul 2025 07:23:27 -0500 Subject: [PATCH] Multiple Artist Support in Player (#441) * Add support for navigating to multiple artists in the player fix some display issues in the artists screen, namely displaying an indicator when loading and hiding sections when we don't have artists that start with a given letter --- maestro-tests/musicplayer.yaml | 4 - package.json | 4 +- src/components/Artists/component.tsx | 59 +- .../Context/components/multiple-artists.tsx | 38 + src/components/Global/components/item-row.tsx | 11 +- .../Player/components/song-info.tsx | 22 +- src/components/types.d.ts | 5 + src/screens/Context/multiple-artists.tsx | 6 + src/screens/Player/index.tsx | 16 + src/screens/index.tsx | 1 + src/screens/tabs.tsx | 1 + yarn.lock | 1740 ++++++++--------- 12 files changed, 996 insertions(+), 911 deletions(-) create mode 100644 src/components/Context/components/multiple-artists.tsx create mode 100644 src/screens/Context/multiple-artists.tsx diff --git a/maestro-tests/musicplayer.yaml b/maestro-tests/musicplayer.yaml index 5860cea6..2cc989ea 100644 --- a/maestro-tests/musicplayer.yaml +++ b/maestro-tests/musicplayer.yaml @@ -34,10 +34,6 @@ appId: com.jellify - tapOn: id: "play-button-test-id" -# check if the pause button is visible -- assertVisible: - id: "pause-button-test-id" - # check if the queue button is visible - assertVisible: id: 'queue-button-test-id' diff --git a/package.json b/package.json index 698c4e16..eb3a41fb 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@react-navigation/native-stack": "^7.3.21", "@sentry/react-native": "^6.17.0", "@shopify/flash-list": "^2.0.0-rc.11", - "@tamagui/config": "^1.132.1", + "@tamagui/config": "^1.132.6", "@tanstack/query-sync-storage-persister": "^5.83.0", "@tanstack/react-query": "^5.83.0", "@tanstack/react-query-persist-client": "^5.83.0", @@ -87,7 +87,7 @@ "react-native-uuid": "^2.0.3", "react-native-vector-icons": "^10.2.0", "ruby": "^0.6.1", - "tamagui": "^1.132.1" + "tamagui": "^1.132.6" }, "devDependencies": { "@babel/core": "^7.28.0", diff --git a/src/components/Artists/component.tsx b/src/components/Artists/component.tsx index d58f269c..7ab7ffe7 100644 --- a/src/components/Artists/component.tsx +++ b/src/components/Artists/component.tsx @@ -11,6 +11,7 @@ import { FlashList } from '@shopify/flash-list' import { useLibraryContext } from '../../providers/Library' import { sleepify } from '../../utils/sleep' import { AZScroller } from '../Global/components/alphabetical-selector' +import { useMutation } from '@tanstack/react-query' export default function Artists({ artists, @@ -50,18 +51,19 @@ export default function Artists({ !artistPageParams.current.includes(letter)) && hasNextPage ) - - await sleepify(250) - sectionListRef.current?.scrollToIndex({ - index: - (artistsRef.current?.indexOf(letter) ?? 0) > -1 - ? artistsRef.current!.indexOf(letter) - : 0, - viewPosition: 0.1, - animated: true, - }) } + const { mutate: alphabetSelectorMutate, isPending: isAlphabetSelectorPending } = useMutation({ + mutationFn: (letter: string) => alphabeticalSelectorCallback(letter), + onSuccess: (data, letter) => { + sectionListRef.current?.scrollToIndex({ + index: artistsRef.current!.indexOf(letter), + viewPosition: 0.1, + animated: true, + }) + }, + }) + useEffect(() => { artistsRef.current = artists ?? [] }, [artists]) @@ -89,21 +91,28 @@ export default function Artists({ ItemSeparatorComponent={() => } estimatedItemSize={itemHeight} data={artists} - refreshControl={} + refreshControl={ + + } renderItem={({ index, item: artist }) => typeof artist === 'string' ? ( - - - {artist.toUpperCase()} - - + // Don't render the letter if we don't have any artists that start with it + // If the index is the last index, or the next index is not an object, then don't render the letter + index - 1 === artists!.length || + typeof artists![index + 1] !== 'object' ? null : ( + + + {artist.toUpperCase()} + + + ) ) : typeof artist === 'number' ? null : typeof artist === 'object' ? ( - {showAlphabeticalSelector && ( - - )} + {showAlphabeticalSelector && } ) } diff --git a/src/components/Context/components/multiple-artists.tsx b/src/components/Context/components/multiple-artists.tsx new file mode 100644 index 00000000..b5c4e659 --- /dev/null +++ b/src/components/Context/components/multiple-artists.tsx @@ -0,0 +1,38 @@ +import { ScrollView, View } from 'tamagui' +import { MultipleArtistsProps } from '../../types' +import ItemRow from '../../Global/components/item-row' +import { useEffect } from 'react' + +export default function MultipleArtists({ + navigation, + route, +}: MultipleArtistsProps): React.JSX.Element { + return ( + + {route.params.artists.map((artist) => { + return ( + { + navigation.goBack() + navigation.goBack() + navigation.navigate('Tabs', { + screen: 'Library', + params: { + screen: 'Artist', + params: { + artist: artist, + }, + }, + }) + }} + /> + ) + })} + + ) +} diff --git a/src/components/Global/components/item-row.tsx b/src/components/Global/components/item-row.tsx index ac82b25a..de36b10c 100644 --- a/src/components/Global/components/item-row.tsx +++ b/src/components/Global/components/item-row.tsx @@ -28,10 +28,14 @@ export default function ItemRow({ item, queueName, navigation, + onPress, + circular, }: { item: BaseItemDto queueName: string navigation: NativeStackNavigationProp + onPress?: () => void + circular?: boolean }): React.JSX.Element { const { useStartPlayback } = usePlayerContext() const { useLoadNewQueue } = useQueueContext() @@ -77,6 +81,11 @@ export default function ItemRow({ }) }} onPress={() => { + if (onPress) { + onPress() + return + } + switch (item.Type) { case 'MusicArtist': { navigation.navigate('Artist', { @@ -101,7 +110,7 @@ export default function ItemRow({ item={item} height={'$12'} width={'$12'} - circular={item.Type === 'MusicArtist'} + circular={item.Type === 'MusicArtist' || circular} /> diff --git a/src/components/Player/components/song-info.tsx b/src/components/Player/components/song-info.tsx index a355e45e..dbe3ce91 100644 --- a/src/components/Player/components/song-info.tsx +++ b/src/components/Player/components/song-info.tsx @@ -62,16 +62,22 @@ export default function SongInfo({ color={'$color'} onPress={() => { if (nowPlaying!.item.ArtistItems) { - navigation.goBack() // Dismiss player modal - navigation.navigate('Tabs', { - screen: 'Library', - params: { - screen: 'Artist', + if (nowPlaying!.item.ArtistItems!.length > 1) { + navigation.navigate('MultipleArtists', { + artists: nowPlaying!.item.ArtistItems!, + }) + } else { + navigation.goBack() // Dismiss player modal + navigation.navigate('Tabs', { + screen: 'Library', params: { - artist: nowPlaying!.item.ArtistItems![0], + screen: 'Artist', + params: { + artist: nowPlaying!.item.ArtistItems![0], + }, }, - }, - }) + }) + } } }} > diff --git a/src/components/types.d.ts b/src/components/types.d.ts index 9615ed83..4ef4dc18 100644 --- a/src/components/types.d.ts +++ b/src/components/types.d.ts @@ -87,6 +87,10 @@ export type StackParamList = { Player: undefined Queue: undefined + MultipleArtists: { + artists: BaseItemDto[] + } + Artist: { artist: BaseItemDto } @@ -126,6 +130,7 @@ export type LibrarySelectionProps = NativeStackScreenProps export type PlayerProps = NativeStackScreenProps +export type MultipleArtistsProps = NativeStackScreenProps export type ProvidedHomeProps = NativeStackScreenProps export type AddPlaylistProps = NativeStackScreenProps diff --git a/src/screens/Context/multiple-artists.tsx b/src/screens/Context/multiple-artists.tsx new file mode 100644 index 00000000..eb1c7b12 --- /dev/null +++ b/src/screens/Context/multiple-artists.tsx @@ -0,0 +1,6 @@ +import { MultipleArtistsProps } from '../../components/types' +import MultipleArtists from '../../components/Context/components/multiple-artists' + +export default function MultipleArtistsSheet(props: MultipleArtistsProps): React.JSX.Element { + return +} diff --git a/src/screens/Player/index.tsx b/src/screens/Player/index.tsx index 02e469ba..0862cafa 100644 --- a/src/screens/Player/index.tsx +++ b/src/screens/Player/index.tsx @@ -4,6 +4,7 @@ import PlayerScreen from '../../components/Player' import Queue from '../../components/Player/queue' import DetailsScreen from '../Detail' import { createNativeStackNavigator } from '@react-navigation/native-stack' +import MultipleArtistsSheet from '../Context/multiple-artists' export const PlayerStack = createNativeStackNavigator() @@ -34,6 +35,21 @@ export default function Player(): React.JSX.Element { headerTitle: '', }} /> + + + + ) } diff --git a/src/screens/index.tsx b/src/screens/index.tsx index e95fa8fa..03367002 100644 --- a/src/screens/index.tsx +++ b/src/screens/index.tsx @@ -37,6 +37,7 @@ export default function Root(): React.JSX.Element { options={{ headerShown: false, presentation: 'modal', + sheetAllowedDetents: Platform.OS === 'ios' ? 'fitToContents' : [1.0], }} /> (