diff --git a/App.tsx b/App.tsx index 97cc904b..8694c096 100644 --- a/App.tsx +++ b/App.tsx @@ -23,7 +23,7 @@ import ErrorBoundary from './src/components/ErrorBoundary' import OTAUpdateScreen from './src/components/OtaUpdates' import { usePerformanceMonitor } from './src/hooks/use-performance-monitor' import { SettingsProvider, useThemeSettingContext } from './src/providers/Settings' -import { navigationRef } from './navigation' +import navigationRef from './navigation' export default function App(): React.JSX.Element { // Add performance monitoring to track app-level re-renders diff --git a/index.js b/index.js index 000fe650..fe6f9074 100644 --- a/index.js +++ b/index.js @@ -4,8 +4,9 @@ import App from './App' import { name as appName } from './app.json' import { PlaybackService } from './src/player/service' import TrackPlayer from 'react-native-track-player' -import { enableFreeze } from 'react-native-screens' +import { enableFreeze, enableScreens } from 'react-native-screens' +enableScreens(true) enableFreeze(true) AppRegistry.registerComponent(appName, () => App) diff --git a/navigation.ts b/navigation.ts index ae60d33b..9d36feaa 100644 --- a/navigation.ts +++ b/navigation.ts @@ -1,4 +1,6 @@ import { createNavigationContainerRef } from '@react-navigation/native' import { RootStackParamList } from './src/screens/types' -export const navigationRef = createNavigationContainerRef() +const navigationRef = createNavigationContainerRef() + +export default navigationRef diff --git a/src/components/Album/index.tsx b/src/components/Album/index.tsx index 69dcff22..02779407 100644 --- a/src/components/Album/index.tsx +++ b/src/components/Album/index.tsx @@ -73,7 +73,7 @@ export function Album(): React.JSX.Element { contentInsetAdjustmentBehavior='automatic' sections={!isUndefined(discs) ? discs : []} keyExtractor={(item, index) => item.Id! + index} - ItemSeparatorComponent={() => } + ItemSeparatorComponent={Separator} renderSectionHeader={({ section }) => { return ( () export default function ArtistNavigation(): React.JSX.Element { const { featuredOn, artist } = useArtistContext() + const hasFeaturedOn = useMemo(() => featuredOn && featuredOn.length > 0, [artist]) + return ( ArtistTabBar(props)} + tabBar={ArtistTabBar} screenOptions={{ tabBarLabelStyle: { fontFamily: 'Figtree-Bold', @@ -36,7 +38,7 @@ export default function ArtistNavigation(): React.JSX.Element { component={Albums} /> - {featuredOn && featuredOn.length > 0 && ( + {hasFeaturedOn && ( - route: RouteProp + navigation: NativeStackNavigationProp + route: RouteProp } export default function MultipleArtists({ navigation, @@ -27,7 +27,7 @@ export default function MultipleArtists({ navigation.popToTop() rootNavigation.popTo('Tabs', { - screen: 'Library', + screen: 'LibraryTab', params: { screen: 'Artist', params: { diff --git a/src/components/Context/index.tsx b/src/components/Context/index.tsx index 66251708..9891c8bb 100644 --- a/src/components/Context/index.tsx +++ b/src/components/Context/index.tsx @@ -5,7 +5,7 @@ import { Text } from '../Global/helpers/text' import FavoriteContextMenuRow from '../Global/components/favorite-context-menu-row' import { Blurhash } from 'react-native-blurhash' import { getPrimaryBlurhashFromDto } from '../../utils/blurhash' -import { useColorScheme } from 'react-native' +import { InteractionManager, useColorScheme } from 'react-native' import { useThemeSettingContext } from '../../providers/Settings' import LinearGradient from 'react-native-linear-gradient' import Icon from '../Global/components/icon' @@ -18,10 +18,13 @@ import { getItemsApi } from '@jellyfin/sdk/lib/utils/api' import { useAddToQueueContext } from '../../providers/Player/queue' import { AddToQueueMutation } from '../../providers/Player/interfaces' import { QueuingType } from '../../enums/queuing-type' -import LibraryStackParamList, { LibraryNavigation } from '../../screens/Library/types' +import LibraryStackParamList from '../../screens/Library/types' import DiscoverStackParamList from '../../screens/Discover/types' import HomeStackParamList from '../../screens/Home/types' import { useCallback } from 'react' +import { StackActions, TabActions } from '@react-navigation/native' +import navigationRef from '../../../navigation' +import { goToAlbumFromContextSheet, goToArtistFromContextSheet } from './utils/navigation' interface ContextProps { item: BaseItemDto @@ -29,14 +32,11 @@ interface ContextProps { HomeStackParamList | LibraryStackParamList | DiscoverStackParamList > navigation: NativeStackNavigationProp + navigationCallback?: (screen: 'Album' | 'Artist', item: BaseItemDto) => void } -export default function ItemContext({ - item, - stackNavigation, - navigation, -}: ContextProps): React.JSX.Element { - const { api, user, library } = useJellifyContext() +export default function ItemContext({ item, stackNavigation }: ContextProps): React.JSX.Element { + const { api } = useJellifyContext() const isArtist = item.Type === BaseItemKind.MusicArtist const isAlbum = item.Type === BaseItemKind.MusicAlbum @@ -45,19 +45,19 @@ export default function ItemContext({ const albumArtists = item.AlbumArtists ?? [] - const { data: album, isSuccess: albumFetchSuccess } = useQuery({ + const { data: album } = useQuery({ queryKey: [QueryKeys.Item, item.AlbumId], queryFn: () => fetchItem(api, item.AlbumId!), enabled: isTrack, }) - const { data: artist, isSuccess: artistFetchSuccess } = useQuery({ + const { data: artist } = useQuery({ queryKey: [QueryKeys.ArtistById, albumArtists.length > 0 ? albumArtists[0].Id : item.Id], queryFn: () => fetchItem(api, albumArtists[0].Id!), enabled: (isTrack || isAlbum) && albumArtists.length > 0, }) - const { data: tracks, isSuccess: tracksFetchSuccess } = useQuery({ + const { data: tracks } = useQuery({ queryKey: [QueryKeys.ItemTracks, item.Id], queryFn: () => getItemsApi(api!) @@ -85,7 +85,6 @@ export default function ItemContext({ )} @@ -93,7 +92,6 @@ export default function ItemContext({ )} @@ -171,25 +169,13 @@ interface MenuRowProps { stackNavigation?: NativeStackNavigationProp< HomeStackParamList | LibraryStackParamList | DiscoverStackParamList > - rootNavigation: NativeStackNavigationProp } -function ViewAlbumMenuRow({ - item: album, - stackNavigation, - rootNavigation, -}: MenuRowProps): React.JSX.Element { +function ViewAlbumMenuRow({ item: album, stackNavigation }: MenuRowProps): React.JSX.Element { const goToAlbum = useCallback(() => { if (stackNavigation && album) stackNavigation.navigate('Album', { album }) - else if (album) { - rootNavigation.popTo('Tabs', { - screen: 'Library', - merge: true, - }) - - LibraryNavigation.album = album - } - }, [album, stackNavigation, rootNavigation]) + else goToAlbumFromContextSheet(album) + }, [album, stackNavigation, navigationRef]) return ( { if (stackNavigation && artist) stackNavigation.navigate('Artist', { artist }) - else if (artist) { - rootNavigation.popTo('Tabs', { - screen: 'Library', - merge: true, - }) - - LibraryNavigation.artist = artist - } - }, [artist, stackNavigation, rootNavigation]) + else goToArtistFromContextSheet(artist) + }, [artist, stackNavigation, navigationRef]) return ( { + navigationRef.dispatch(CommonActions.navigate('Album', { album })) + }) + } else navigationRef.dispatch(CommonActions.navigate('Album', { album })) +} + +export function goToArtistFromContextSheet(artist: BaseItemDto | undefined) { + if (!navigationRef.isReady() || !artist) return + + // Pop Context Sheet and Player Modal + navigationRef.dispatch(StackActions.popTo('Tabs')) + + const route = navigationRef.current?.getCurrentRoute() + + if (route?.name.includes('Settings')) { + navigationRef.dispatch(TabActions.jumpTo('LibraryTab')) + requestAnimationFrame(() => { + navigationRef.dispatch(CommonActions.navigate('Artist', { artist })) + }) + } else navigationRef.dispatch(CommonActions.navigate('Artist', { artist })) +} diff --git a/src/components/Home/index.tsx b/src/components/Home/index.tsx index 9d2e912d..8d01f148 100644 --- a/src/components/Home/index.tsx +++ b/src/components/Home/index.tsx @@ -6,14 +6,11 @@ import { useHomeContext } from '../../providers/Home' import FrequentArtists from './helpers/frequent-artists' import FrequentlyPlayedTracks from './helpers/frequent-tracks' import { useSafeAreaInsets } from 'react-native-safe-area-context' -import { useJellifyContext } from '../../providers' import { usePreventRemove } from '@react-navigation/native' export function ProvidedHome(): React.JSX.Element { usePreventRemove(true, () => {}) - const { user } = useJellifyContext() const { refreshing: refetching, onRefresh } = useHomeContext() - const insets = useSafeAreaInsets() return ( fetchItem(api, nowPlaying!.item.AlbumId!), }) - const { data: artists } = useQuery< - { title: string | number; data: BaseItemDto[] }, - Error, - void - >({ + useQuery({ queryKey: [QueryKeys.TrackArtists, nowPlaying!.item.ArtistItems], queryFn: () => fetchItems(api, user, library, [BaseItemKind.MusicArtist]), select: (data: { title: string | number; data: BaseItemDto[] }) => data.data, @@ -46,7 +43,7 @@ export default function SongInfo(): React.JSX.Element { onPress={() => { if (album) { navigation.popTo('Tabs', { - screen: 'Library', + screen: 'LibraryTab', params: { screen: 'Album', params: { @@ -86,12 +83,15 @@ export default function SongInfo(): React.JSX.Element { onPress={() => { if (nowPlaying!.item.ArtistItems) { if (nowPlaying!.item.ArtistItems!.length > 1) { - navigation.navigate('MultipleArtists', { - artists: nowPlaying!.item.ArtistItems!, + navigation.navigate('PlayerRoot', { + screen: 'MultipleArtistsSheet', + params: { + artists: nowPlaying!.item.ArtistItems!, + }, }) } else { navigation.popTo('Tabs', { - screen: 'Library', + screen: 'LibraryTab', params: { screen: 'Artist', params: { diff --git a/src/components/Player/mini-player.tsx b/src/components/Player/mini-player.tsx index 3f821fe1..07b6122b 100644 --- a/src/components/Player/mini-player.tsx +++ b/src/components/Player/mini-player.tsx @@ -11,7 +11,7 @@ import { } from 'tamagui' import { useNowPlayingContext } from '../../providers/Player' import { BottomTabNavigationEventMap } from '@react-navigation/bottom-tabs' -import { NavigationHelpers, ParamListBase } from '@react-navigation/native' +import { NavigationHelpers, ParamListBase, useNavigation } from '@react-navigation/native' import { Text } from '../Global/helpers/text' import TextTicker from 'react-native-text-ticker' import PlayPauseButton from './components/buttons' @@ -32,17 +32,17 @@ import Animated, { withSpring, } from 'react-native-reanimated' import { ImageType } from '@jellyfin/sdk/lib/generated-client/models' +import { RootStackParamList } from '../../screens/types' +import { NativeStackNavigationProp } from '@react-navigation/native-stack' -export const Miniplayer = React.memo(function Miniplayer({ - navigation, -}: { - navigation: NavigationHelpers -}): React.JSX.Element { +export const Miniplayer = React.memo(function Miniplayer(): React.JSX.Element { const { api } = useJellifyContext() const nowPlaying = useNowPlayingContext() const useSkip = useSkipContext() const usePrevious = usePreviousContext() + const navigation = useNavigation>() + const translateX = useSharedValue(0) const translateY = useSharedValue(0) @@ -56,7 +56,7 @@ export const Miniplayer = React.memo(function Miniplayer({ useSkip() } else if (direction === 'Swiped Up') { // Navigate to the big player - navigation.navigate('Player') + navigation.navigate('PlayerRoot', { screen: 'PlayerScreen' }) } }, [useSkip, usePrevious, navigation], @@ -102,7 +102,9 @@ export const Miniplayer = React.memo(function Miniplayer({ margin={0} padding={0} height={'$7'} - onPress={() => navigation.navigate('Player')} + onPress={() => + navigation.navigate('PlayerRoot', { screen: 'PlayerScreen' }) + } > - - + + + + + + + + diff --git a/src/providers/Album/index.tsx b/src/providers/Album/index.tsx index 516fb306..98e8213b 100644 --- a/src/providers/Album/index.tsx +++ b/src/providers/Album/index.tsx @@ -17,6 +17,7 @@ function AlbumContextInitializer(album: BaseItemDto): AlbumContext { const { data: discs, isPending } = useQuery({ queryKey: [QueryKeys.ItemTracks, album.Id!], queryFn: () => fetchAlbumDiscs(api, album), + enabled: true, }) return { @@ -41,7 +42,7 @@ export const AlbumProvider: ({ }) => React.JSX.Element = ({ album, children }) => { const context = AlbumContextInitializer(album) - return {children} + return {children} } export const useAlbumContext = () => useContext(AlbumContext) diff --git a/src/providers/Artist/index.tsx b/src/providers/Artist/index.tsx index b6ea897e..d5977a5b 100644 --- a/src/providers/Artist/index.tsx +++ b/src/providers/Artist/index.tsx @@ -6,6 +6,8 @@ import { createContext, ReactNode, useCallback, useContext, useMemo } from 'reac import { SharedValue, useSharedValue } from 'react-native-reanimated' import { useJellifyContext } from '..' import { fetchArtistAlbums, fetchArtistFeaturedOn } from '../../api/queries/artist' +import { isUndefined } from 'lodash' +import { Spinner } from 'tamagui' interface ArtistContext { fetchingAlbums: boolean @@ -45,8 +47,9 @@ export const ArtistProvider = ({ refetch: refetchAlbums, isPending: fetchingAlbums, } = useQuery({ - queryKey: [QueryKeys.ArtistAlbums, library?.musicLibraryId, artist.Id!], + queryKey: [QueryKeys.ArtistAlbums, library?.musicLibraryId, artist.Id], queryFn: () => fetchArtistAlbums(api, library?.musicLibraryId, artist), + enabled: !isUndefined(artist.Id), }) const { @@ -54,8 +57,9 @@ export const ArtistProvider = ({ refetch: refetchFeaturedOn, isPending: fetchingFeaturedOn, } = useQuery({ - queryKey: [QueryKeys.ArtistFeaturedOn, library?.musicLibraryId, artist.Id!], + queryKey: [QueryKeys.ArtistFeaturedOn, library?.musicLibraryId, artist.Id], queryFn: () => fetchArtistFeaturedOn(api, library?.musicLibraryId, artist), + enabled: !isUndefined(artist.Id), }) const { @@ -63,8 +67,9 @@ export const ArtistProvider = ({ refetch: refetchSimilar, isPending: fetchingSimilarArtists, } = useQuery({ - queryKey: [QueryKeys.SimilarItems, library?.musicLibraryId, artist.Id!], + queryKey: [QueryKeys.SimilarItems, library?.musicLibraryId, artist.Id], queryFn: () => fetchSimilar(api, user, library?.musicLibraryId, artist.Id!), + enabled: !isUndefined(artist.Id), }) const refresh = useCallback(() => { @@ -100,7 +105,15 @@ export const ArtistProvider = ({ ], ) - return {children} + return ( + + {fetchingAlbums || fetchingFeaturedOn || fetchingSimilarArtists ? ( + + ) : ( + children + )} + + ) } export const useArtistContext = () => useContext(ArtistContext) diff --git a/src/providers/Home/index.tsx b/src/providers/Home/index.tsx index 3f06e523..5ebb80f5 100644 --- a/src/providers/Home/index.tsx +++ b/src/providers/Home/index.tsx @@ -1,4 +1,11 @@ -import React, { createContext, ReactNode, useContext, useState } from 'react' +import React, { + createContext, + ReactNode, + useCallback, + useContext, + useEffect, + useState, +} from 'react' import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models' import { InfiniteData, @@ -12,6 +19,7 @@ import { queryClient } from '../../constants/query-client' import QueryConfig from '../../api/queries/query.config' import { fetchFrequentlyPlayed, fetchFrequentlyPlayedArtists } from '../../api/queries/frequents' import { useJellifyContext } from '..' +import { useIsFocused } from '@react-navigation/native' interface HomeContext { refreshing: boolean onRefresh: () => void @@ -36,6 +44,12 @@ const HomeContextInitializer = () => { const { api, library, user } = useJellifyContext() const [refreshing, setRefreshing] = useState(false) + const isFocused = useIsFocused() + + useEffect(() => { + console.debug(`Home focused: ${isFocused}`) + }, [isFocused]) + const { data: recentTracks, isFetching: isFetchingRecentTracks, @@ -98,7 +112,7 @@ const HomeContextInitializer = () => { enabled: !!frequentlyPlayed && frequentlyPlayed.length > 0 && !isStaleFrequentlyPlayed, }) - const onRefresh = async () => { + const onRefresh = useCallback(async () => { setRefreshing(true) queryClient.invalidateQueries({ queryKey: [QueryKeys.RecentlyPlayedArtists] }) @@ -114,7 +128,12 @@ const HomeContextInitializer = () => { ]) setRefreshing(false) - } + }, [ + refetchRecentTracks, + refetchFrequentlyPlayed, + recentArtistsInfiniteQuery.refetch, + frequentArtistsInfiniteQuery.refetch, + ]) return { refreshing, diff --git a/src/screens/Context/index.tsx b/src/screens/Context/index.tsx index 7fabc654..f0e8351c 100644 --- a/src/screens/Context/index.tsx +++ b/src/screens/Context/index.tsx @@ -7,6 +7,7 @@ export default function ItemContextScreen({ route, navigation }: ContextProps): item={route.params.item} stackNavigation={route.params.navigation} navigation={navigation} + navigationCallback={route.params.navigationCallback} /> ) } diff --git a/src/screens/Home/index.tsx b/src/screens/Home/index.tsx index 8962adfc..8c2e05eb 100644 --- a/src/screens/Home/index.tsx +++ b/src/screens/Home/index.tsx @@ -21,77 +21,75 @@ export default function Home(): React.JSX.Element { const theme = useTheme() return ( - - - - - ({ - title: route.params.artist.Name ?? 'Unknown Artist', - headerTitleStyle: { - color: theme.background.val, - fontFamily: 'Figtree-Bold', - }, - })} - /> + + + + ({ + title: route.params.artist.Name ?? 'Unknown Artist', + headerTitleStyle: { + color: theme.background.val, + fontFamily: 'Figtree-Bold', + }, + })} + /> - - + + - + - + - ({ - title: route.params.album.Name ?? 'Untitled Album', - headerTitleStyle: { - color: theme.background.val, - }, - })} - /> + ({ + title: route.params.album.Name ?? 'Untitled Album', + headerTitleStyle: { + color: theme.background.val, + }, + })} + /> - ({ - headerShown: true, - headerTitleStyle: { - color: theme.background.val, - }, - })} - /> - - - + ({ + headerShown: true, + headerTitleStyle: { + color: theme.background.val, + }, + })} + /> + + ) } diff --git a/src/screens/Home/types.d.ts b/src/screens/Home/types.d.ts index 46fba31f..fe108a63 100644 --- a/src/screens/Home/types.d.ts +++ b/src/screens/Home/types.d.ts @@ -2,8 +2,11 @@ import { BaseStackParamList } from '../types' import { NativeStackScreenProps } from '@react-navigation/native-stack' import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models' import { UseInfiniteQueryResult } from '@tanstack/react-query' +import { NavigatorScreenParams } from '@react-navigation/native' type HomeStackParamList = BaseStackParamList & { + HomeScreen: undefined + RecentArtists: { artistsInfiniteQuery: UseInfiniteQueryResult } diff --git a/src/screens/Library/index.tsx b/src/screens/Library/index.tsx index 56f2dedc..2bfbff9d 100644 --- a/src/screens/Library/index.tsx +++ b/src/screens/Library/index.tsx @@ -1,5 +1,5 @@ -import React, { useEffect } from 'react' -import Library from '../../components/Library/component' +import React from 'react' +import LibraryScreen from '../../components/Library/component' import { PlaylistScreen } from '../Playlist' import AddPlaylist from './add-playlist' import DeletePlaylist from './delete-playlist' @@ -9,121 +9,87 @@ import { LibraryProvider } from '../../providers/Library' import { LibrarySortAndFilterProvider } from '../../providers/Library/sorting-filtering' import { createNativeStackNavigator } from '@react-navigation/native-stack' import AlbumScreen from '../Album' -import LibraryStackParamList, { LibraryNavigation } from './types' +import LibraryStackParamList from './types' import { LibraryTabProps } from '../Tabs/types' -import { useIsFocused } from '@react-navigation/native' +import { LibraryNavigationContext } from './navigation' const Stack = createNativeStackNavigator() export default function LibraryStack({ route, navigation }: LibraryTabProps): React.JSX.Element { const theme = useTheme() - const isFocused = useIsFocused() - - useEffect(() => { - if (!isFocused) return - - if (LibraryNavigation.album) { - navigation.navigate('Library', { - screen: 'Album', - params: { album: LibraryNavigation.album }, - }) - LibraryNavigation.album = undefined - } - - if (LibraryNavigation.artist) { - navigation.navigate('Library', { - screen: 'Artist', - params: { artist: LibraryNavigation.artist }, - }) - LibraryNavigation.artist = undefined - } - - if (LibraryNavigation.playlist) { - navigation.navigate('Library', { - screen: 'Playlist', - params: { playlist: LibraryNavigation.playlist }, - }) - LibraryNavigation.playlist = undefined - } - }, [isFocused]) - return ( - - - - + + // I honestly don't think we need a header for this screen, given that there are + // tabs on the top of the screen for navigating the library, but if we want one, + // we can use the title above + headerShown: false, + }} + /> - ({ - title: route.params.artist.Name ?? 'Unknown Artist', - headerTitleStyle: { - color: theme.background.val, - }, - })} - /> + ({ + title: route.params.artist.Name ?? 'Unknown Artist', + headerTitleStyle: { + color: theme.background.val, + }, + })} + /> - ({ - headerShown: true, - title: route.params.album.Name ?? 'Untitled Album', - headerTitleStyle: { - color: theme.background.val, - }, - })} - /> + ({ + headerShown: true, + title: route.params.album.Name ?? 'Untitled Album', + headerTitleStyle: { + color: theme.background.val, + }, + })} + /> - ({ - headerShown: true, - title: route.params.playlist.Name ?? 'Untitled Playlist', - headerTitleStyle: { - color: theme.background.val, - }, - })} - /> + ({ + headerShown: true, + title: route.params.playlist.Name ?? 'Untitled Playlist', + headerTitleStyle: { + color: theme.background.val, + }, + })} + /> - - + + - - - - - + + + ) } diff --git a/src/screens/Library/navigation.tsx b/src/screens/Library/navigation.tsx new file mode 100644 index 00000000..35a45e02 --- /dev/null +++ b/src/screens/Library/navigation.tsx @@ -0,0 +1,16 @@ +import { NavigationProp } from '@react-navigation/native' +import LibraryStackParamList from './types' +import { createContext, useContext } from 'use-context-selector' +import TabParamList from '../Tabs/types' + +export const LibraryNavigationContext = createContext | null>(null) + +const useLibraryNavigation = () => { + const context = useContext(LibraryNavigationContext) + + if (!context) + throw new Error('useLibraryNavigation must be used in the the LibraryNavigationProvider') + return context +} + +export default useLibraryNavigation diff --git a/src/screens/Library/types.ts b/src/screens/Library/types.ts index 615a3c53..797010d6 100644 --- a/src/screens/Library/types.ts +++ b/src/screens/Library/types.ts @@ -1,8 +1,11 @@ import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models' import { NativeStackScreenProps } from '@react-navigation/native-stack' import { BaseStackParamList } from '../types' +import { Queue } from '../../player/types/queue-item' +import { NavigatorScreenParams } from '@react-navigation/native' type LibraryStackParamList = BaseStackParamList & { + LibraryScreen: NavigatorScreenParams AddPlaylist: undefined DeletePlaylist: { @@ -12,7 +15,7 @@ type LibraryStackParamList = BaseStackParamList & { export default LibraryStackParamList -export type LibraryScreenProps = NativeStackScreenProps +export type LibraryScreenProps = NativeStackScreenProps export type LibraryArtistProps = NativeStackScreenProps export type LibraryAlbumProps = NativeStackScreenProps @@ -22,10 +25,6 @@ export type LibraryDeletePlaylistProps = NativeStackScreenProps< 'DeletePlaylist' > -type LibraryNavigation = { - album?: BaseItemDto - artist?: BaseItemDto - playlist?: BaseItemDto +type LibraryScreenParamList = { + LibraryScreen: NavigatorScreenParams } - -export const LibraryNavigation: LibraryNavigation = {} diff --git a/src/screens/Login/server-authentication.tsx b/src/screens/Login/server-authentication.tsx index c583817b..1331d28f 100644 --- a/src/screens/Login/server-authentication.tsx +++ b/src/screens/Login/server-authentication.tsx @@ -16,11 +16,12 @@ import Toast from 'react-native-toast-message' import { IS_MAESTRO_BUILD } from '../../configs/config' import { AxiosResponse } from 'axios' import { AuthenticationResult } from '@jellyfin/sdk/lib/generated-client/models' +import LoginStackParamList from './types' export default function ServerAuthentication({ navigation, }: { - navigation: NativeStackNavigationProp + navigation: NativeStackNavigationProp }): React.JSX.Element { const { api } = useJellifyContext() const [username, setUsername] = useState(undefined) @@ -116,10 +117,7 @@ export default function ServerAuthentication({ bordered={0} onPress={() => { if (navigation.canGoBack()) navigation.goBack() - else - navigation.navigate('ServerAddress', undefined, { - pop: true, - }) + else navigation.navigate('ServerAddress', undefined, { pop: true }) }} > Switch Server diff --git a/src/screens/Login/types.ts b/src/screens/Login/types.ts new file mode 100644 index 00000000..fdd198d3 --- /dev/null +++ b/src/screens/Login/types.ts @@ -0,0 +1,7 @@ +type LoginStackParamList = { + ServerAddress: undefined + ServerAuthentication: undefined + LibrarySelection: undefined +} + +export default LoginStackParamList diff --git a/src/screens/Player/index.tsx b/src/screens/Player/index.tsx index bfbc94b6..20c7dab3 100644 --- a/src/screens/Player/index.tsx +++ b/src/screens/Player/index.tsx @@ -9,9 +9,9 @@ export const PlayerStack = createNativeStackNavigator() export default function Player(): React.JSX.Element { return ( - + +export type PlayerProps = NativeStackScreenProps +export type MultipleArtistsProps = NativeStackScreenProps diff --git a/src/screens/Tabs/index.tsx b/src/screens/Tabs/index.tsx index be66f8b8..007c7b0a 100644 --- a/src/screens/Tabs/index.tsx +++ b/src/screens/Tabs/index.tsx @@ -11,16 +11,17 @@ import SearchStack from '../Search' import LibraryStack from '../Library' import InternetConnectionWatcher from '../../components/Network/internetConnectionWatcher' import TabParamList from './types' +import { TabProps } from '../types' const Tab = createBottomTabNavigator() -export function Tabs(): React.JSX.Element { +export default function Tabs({ route, navigation }: TabProps): React.JSX.Element { const theme = useTheme() const nowPlaying = useNowPlayingContext() return ( {nowPlaying && ( /* Hide miniplayer if the queue is empty */ - + )} @@ -39,9 +40,10 @@ export function Tabs(): React.JSX.Element { )} > ( @@ -51,21 +53,24 @@ export function Tabs(): React.JSX.Element { /> ( ), tabBarButtonTestID: 'library-tab-button', + lazy: false, // Load on mount since we need to be able to navigate here from the player }} /> ( @@ -75,9 +80,10 @@ export function Tabs(): React.JSX.Element { /> ( @@ -87,9 +93,10 @@ export function Tabs(): React.JSX.Element { /> ( diff --git a/src/screens/Tabs/types.d.ts b/src/screens/Tabs/types.d.ts index 705d9302..a10c10ef 100644 --- a/src/screens/Tabs/types.d.ts +++ b/src/screens/Tabs/types.d.ts @@ -3,14 +3,14 @@ import { NavigatorScreenParams } from '@react-navigation/native' import LibraryStackParamList from '../Library/types' type TabParamList = { - Home: undefined - Library: NavigatorScreenParams - Search: undefined - Discover: undefined - Settings: undefined + HomeTab: undefined + LibraryTab: NavigatorScreenParams + SearchTab: undefined + DiscoverTab: undefined + SettingsTab: undefined } -export type HomeTabProps = BottomTabScreenProps -export type LibraryTabProps = BottomTabScreenProps +export type HomeTabProps = BottomTabScreenProps +export type LibraryTabProps = BottomTabScreenProps export default TabParamList diff --git a/src/screens/index.tsx b/src/screens/index.tsx index a222cb91..0abe62dd 100644 --- a/src/screens/index.tsx +++ b/src/screens/index.tsx @@ -1,5 +1,5 @@ import Player from './Player' -import { Tabs } from './Tabs' +import Tabs from './Tabs' import { RootStackParamList } from './types' import { getToken, useTheme } from 'tamagui' import { useJellifyContext } from '../providers' @@ -20,7 +20,7 @@ export default function Root(): React.JSX.Element { initialRouteName={api && library ? 'Tabs' : 'Login'} screenOptions={({ route }) => ({ navigationBarColor: - route.name === 'Player' ? getToken('$black') : theme.background.val, + route.name === 'PlayerRoot' ? getToken('$black') : theme.background.val, })} > export type AlbumProps = NativeStackScreenProps +export type PlaylistProps = NativeStackNavigationProp export type TracksProps = NativeStackScreenProps export type InstantMixProps = NativeStackScreenProps @@ -59,19 +54,20 @@ export type RootStackParamList = { Login: undefined Tabs: NavigatorScreenParams - Player: NavigatorScreenParams + PlayerRoot: NavigatorScreenParams Context: { item: BaseItemDto navigation?: NativeStackNavigationProp< HomeStackParamList | LibraryStackParamList | DiscoverStackParamList > + navigationCallback?: (screen: 'Album' | 'Artist', item: BaseItemDto) => void } } export type LoginProps = NativeStackNavigationProp export type TabProps = NativeStackScreenProps -export type PlayerProps = NativeStackScreenProps +export type PlayerProps = NativeStackScreenProps export type ContextProps = NativeStackScreenProps export type ArtistsProps = {