mirror of
https://github.com/anultravioletaurora/Jellify.git
synced 2025-12-16 18:55:44 -06:00
getting context sheet navigation to work
This commit is contained in:
2
App.tsx
2
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
|
||||
|
||||
3
index.js
3
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)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { createNavigationContainerRef } from '@react-navigation/native'
|
||||
import { RootStackParamList } from './src/screens/types'
|
||||
|
||||
export const navigationRef = createNavigationContainerRef<RootStackParamList>()
|
||||
const navigationRef = createNavigationContainerRef<RootStackParamList>()
|
||||
|
||||
export default navigationRef
|
||||
|
||||
@@ -73,7 +73,7 @@ export function Album(): React.JSX.Element {
|
||||
contentInsetAdjustmentBehavior='automatic'
|
||||
sections={!isUndefined(discs) ? discs : []}
|
||||
keyExtractor={(item, index) => item.Id! + index}
|
||||
ItemSeparatorComponent={() => <Separator />}
|
||||
ItemSeparatorComponent={Separator}
|
||||
renderSectionHeader={({ section }) => {
|
||||
return (
|
||||
<XStack
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React, { useMemo } from 'react'
|
||||
import Albums from './albums'
|
||||
import SimilarArtists from './similar'
|
||||
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs'
|
||||
@@ -11,9 +11,11 @@ const ArtistTabs = createMaterialTopTabNavigator<ArtistTabList>()
|
||||
export default function ArtistNavigation(): React.JSX.Element {
|
||||
const { featuredOn, artist } = useArtistContext()
|
||||
|
||||
const hasFeaturedOn = useMemo(() => featuredOn && featuredOn.length > 0, [artist])
|
||||
|
||||
return (
|
||||
<ArtistTabs.Navigator
|
||||
tabBar={(props) => 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 && (
|
||||
<ArtistTabs.Screen
|
||||
name='ArtistFeaturedOn'
|
||||
options={{
|
||||
|
||||
@@ -6,8 +6,8 @@ import { RouteProp, useNavigation } from '@react-navigation/native'
|
||||
import { RootStackParamList } from '../../../screens/types'
|
||||
|
||||
interface MultipleArtistsProps {
|
||||
navigation: NativeStackNavigationProp<PlayerParamList, 'MultipleArtists'>
|
||||
route: RouteProp<PlayerParamList, 'MultipleArtists'>
|
||||
navigation: NativeStackNavigationProp<PlayerParamList, 'MultipleArtistsSheet'>
|
||||
route: RouteProp<PlayerParamList, 'MultipleArtistsSheet'>
|
||||
}
|
||||
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: {
|
||||
|
||||
@@ -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<RootStackParamList>
|
||||
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({
|
||||
<ViewAlbumMenuRow
|
||||
item={isAlbum ? item : album!}
|
||||
stackNavigation={stackNavigation}
|
||||
rootNavigation={navigation}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -93,7 +92,6 @@ export default function ItemContext({
|
||||
<ViewArtistMenuRow
|
||||
item={isArtist ? item : artist}
|
||||
stackNavigation={stackNavigation}
|
||||
rootNavigation={navigation}
|
||||
/>
|
||||
)}
|
||||
</YGroup>
|
||||
@@ -171,25 +169,13 @@ interface MenuRowProps {
|
||||
stackNavigation?: NativeStackNavigationProp<
|
||||
HomeStackParamList | LibraryStackParamList | DiscoverStackParamList
|
||||
>
|
||||
rootNavigation: NativeStackNavigationProp<RootStackParamList>
|
||||
}
|
||||
|
||||
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 (
|
||||
<ListItem
|
||||
@@ -207,22 +193,11 @@ function ViewAlbumMenuRow({
|
||||
)
|
||||
}
|
||||
|
||||
function ViewArtistMenuRow({
|
||||
item: artist,
|
||||
stackNavigation,
|
||||
rootNavigation,
|
||||
}: MenuRowProps): React.JSX.Element {
|
||||
function ViewArtistMenuRow({ item: artist, stackNavigation }: MenuRowProps): React.JSX.Element {
|
||||
const goToArtist = useCallback(() => {
|
||||
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 (
|
||||
<ListItem
|
||||
|
||||
36
src/components/Context/utils/navigation.ts
Normal file
36
src/components/Context/utils/navigation.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { CommonActions, StackActions, TabActions } from '@react-navigation/native'
|
||||
import navigationRef from '../../../../navigation'
|
||||
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||
import { InteractionManager } from 'react-native'
|
||||
|
||||
export function goToAlbumFromContextSheet(album: BaseItemDto | undefined) {
|
||||
if (!navigationRef.isReady() || !album) 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('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 }))
|
||||
}
|
||||
@@ -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 (
|
||||
<ScrollView
|
||||
|
||||
@@ -11,7 +11,10 @@ import React from 'react'
|
||||
|
||||
const LibraryTabsNavigator = createMaterialTopTabNavigator()
|
||||
|
||||
export default function Library({ route, navigation }: LibraryScreenProps): React.JSX.Element {
|
||||
export default function LibraryScreen({
|
||||
route,
|
||||
navigation,
|
||||
}: LibraryScreenProps): React.JSX.Element {
|
||||
const theme = useTheme()
|
||||
|
||||
return (
|
||||
|
||||
@@ -16,6 +16,7 @@ import Icon from '../../Global/components/icon'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { QueryKeys } from '../../../enums/query-keys'
|
||||
import { BaseItemDto, BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models'
|
||||
import { PlayerParamList } from '../../../screens/Player/types'
|
||||
|
||||
export default function SongInfo(): React.JSX.Element {
|
||||
const { api, user, library } = useJellifyContext()
|
||||
@@ -28,11 +29,7 @@ export default function SongInfo(): React.JSX.Element {
|
||||
queryFn: () => 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: {
|
||||
|
||||
@@ -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<ParamListBase, BottomTabNavigationEventMap>
|
||||
}): 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<NativeStackNavigationProp<RootStackParamList>>()
|
||||
|
||||
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' })
|
||||
}
|
||||
>
|
||||
<YStack
|
||||
justify='center'
|
||||
|
||||
@@ -21,6 +21,9 @@ import Toast from 'react-native-toast-message'
|
||||
import JellifyToastConfig from '../constants/toast.config'
|
||||
import { useColorScheme } from 'react-native'
|
||||
import { CarPlayProvider } from '../providers/CarPlay'
|
||||
import { LibrarySortAndFilterProvider } from '../providers/Library/sorting-filtering'
|
||||
import { LibraryProvider } from '../providers/Library'
|
||||
import { HomeProvider } from '../providers/Home'
|
||||
/**
|
||||
* The main component for the Jellify app. Children are wrapped in the {@link JellifyProvider}
|
||||
* @returns The {@link Jellify} component
|
||||
@@ -83,8 +86,14 @@ function App(): React.JSX.Element {
|
||||
<NetworkContextProvider>
|
||||
<QueueProvider>
|
||||
<PlayerProvider>
|
||||
<CarPlayProvider />
|
||||
<Root />
|
||||
<HomeProvider>
|
||||
<LibrarySortAndFilterProvider>
|
||||
<LibraryProvider>
|
||||
<CarPlayProvider />
|
||||
<Root />
|
||||
</LibraryProvider>
|
||||
</LibrarySortAndFilterProvider>
|
||||
</HomeProvider>
|
||||
</PlayerProvider>
|
||||
</QueueProvider>
|
||||
</NetworkContextProvider>
|
||||
|
||||
@@ -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 <AlbumContext.Provider value={{ ...context }}>{children}</AlbumContext.Provider>
|
||||
return <AlbumContext.Provider value={context}>{children}</AlbumContext.Provider>
|
||||
}
|
||||
|
||||
export const useAlbumContext = () => useContext(AlbumContext)
|
||||
|
||||
@@ -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 <ArtistContext.Provider value={value}>{children}</ArtistContext.Provider>
|
||||
return (
|
||||
<ArtistContext.Provider value={value}>
|
||||
{fetchingAlbums || fetchingFeaturedOn || fetchingSimilarArtists ? (
|
||||
<Spinner color={'$primary'} flex={1} />
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</ArtistContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useArtistContext = () => useContext(ArtistContext)
|
||||
|
||||
@@ -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<boolean>(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,
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -21,77 +21,75 @@ export default function Home(): React.JSX.Element {
|
||||
const theme = useTheme()
|
||||
|
||||
return (
|
||||
<HomeProvider>
|
||||
<HomeStack.Navigator initialRouteName='Home' screenOptions={{ headerShown: true }}>
|
||||
<HomeStack.Group>
|
||||
<HomeStack.Screen
|
||||
name='Home'
|
||||
component={ProvidedHome}
|
||||
options={{
|
||||
title: 'Home',
|
||||
headerTitleStyle: {
|
||||
fontFamily: 'Figtree-Bold',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<HomeStack.Screen
|
||||
name='Artist'
|
||||
component={ArtistScreen}
|
||||
options={({ route }) => ({
|
||||
title: route.params.artist.Name ?? 'Unknown Artist',
|
||||
headerTitleStyle: {
|
||||
color: theme.background.val,
|
||||
fontFamily: 'Figtree-Bold',
|
||||
},
|
||||
})}
|
||||
/>
|
||||
<HomeStack.Navigator initialRouteName='HomeScreen' screenOptions={{ headerShown: true }}>
|
||||
<HomeStack.Group>
|
||||
<HomeStack.Screen
|
||||
name='HomeScreen'
|
||||
component={ProvidedHome}
|
||||
options={{
|
||||
title: 'Home',
|
||||
headerTitleStyle: {
|
||||
fontFamily: 'Figtree-Bold',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<HomeStack.Screen
|
||||
name='Artist'
|
||||
component={ArtistScreen}
|
||||
options={({ route }) => ({
|
||||
title: route.params.artist.Name ?? 'Unknown Artist',
|
||||
headerTitleStyle: {
|
||||
color: theme.background.val,
|
||||
fontFamily: 'Figtree-Bold',
|
||||
},
|
||||
})}
|
||||
/>
|
||||
|
||||
<HomeStack.Screen
|
||||
name='RecentArtists'
|
||||
component={HomeArtistsScreen}
|
||||
options={{ title: 'Recent Artists' }}
|
||||
/>
|
||||
<HomeStack.Screen
|
||||
name='MostPlayedArtists'
|
||||
component={HomeArtistsScreen}
|
||||
options={{ title: 'Most Played' }}
|
||||
/>
|
||||
<HomeStack.Screen
|
||||
name='RecentArtists'
|
||||
component={HomeArtistsScreen}
|
||||
options={{ title: 'Recent Artists' }}
|
||||
/>
|
||||
<HomeStack.Screen
|
||||
name='MostPlayedArtists'
|
||||
component={HomeArtistsScreen}
|
||||
options={{ title: 'Most Played' }}
|
||||
/>
|
||||
|
||||
<HomeStack.Screen
|
||||
name='RecentTracks'
|
||||
component={HomeTracksScreen}
|
||||
options={{ title: 'Recently Played' }}
|
||||
/>
|
||||
<HomeStack.Screen
|
||||
name='RecentTracks'
|
||||
component={HomeTracksScreen}
|
||||
options={{ title: 'Recently Played' }}
|
||||
/>
|
||||
|
||||
<HomeStack.Screen
|
||||
name='MostPlayedTracks'
|
||||
component={HomeTracksScreen}
|
||||
options={{ title: 'On Repeat' }}
|
||||
/>
|
||||
<HomeStack.Screen
|
||||
name='MostPlayedTracks'
|
||||
component={HomeTracksScreen}
|
||||
options={{ title: 'On Repeat' }}
|
||||
/>
|
||||
|
||||
<HomeStack.Screen
|
||||
name='Album'
|
||||
component={AlbumScreen}
|
||||
options={({ route }) => ({
|
||||
title: route.params.album.Name ?? 'Untitled Album',
|
||||
headerTitleStyle: {
|
||||
color: theme.background.val,
|
||||
},
|
||||
})}
|
||||
/>
|
||||
<HomeStack.Screen
|
||||
name='Album'
|
||||
component={AlbumScreen}
|
||||
options={({ route }) => ({
|
||||
title: route.params.album.Name ?? 'Untitled Album',
|
||||
headerTitleStyle: {
|
||||
color: theme.background.val,
|
||||
},
|
||||
})}
|
||||
/>
|
||||
|
||||
<HomeStack.Screen
|
||||
name='Playlist'
|
||||
component={PlaylistScreen}
|
||||
options={({ route }) => ({
|
||||
headerShown: true,
|
||||
headerTitleStyle: {
|
||||
color: theme.background.val,
|
||||
},
|
||||
})}
|
||||
/>
|
||||
</HomeStack.Group>
|
||||
</HomeStack.Navigator>
|
||||
</HomeProvider>
|
||||
<HomeStack.Screen
|
||||
name='Playlist'
|
||||
component={PlaylistScreen}
|
||||
options={({ route }) => ({
|
||||
headerShown: true,
|
||||
headerTitleStyle: {
|
||||
color: theme.background.val,
|
||||
},
|
||||
})}
|
||||
/>
|
||||
</HomeStack.Group>
|
||||
</HomeStack.Navigator>
|
||||
)
|
||||
}
|
||||
|
||||
3
src/screens/Home/types.d.ts
vendored
3
src/screens/Home/types.d.ts
vendored
@@ -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<BaseItemDto[], Error>
|
||||
}
|
||||
|
||||
@@ -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<LibraryStackParamList>()
|
||||
|
||||
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 (
|
||||
<LibrarySortAndFilterProvider>
|
||||
<LibraryProvider>
|
||||
<Stack.Navigator initialRouteName='Library'>
|
||||
<Stack.Screen
|
||||
name='Library'
|
||||
component={Library}
|
||||
options={{
|
||||
title: 'Library',
|
||||
<Stack.Navigator initialRouteName='LibraryScreen'>
|
||||
<Stack.Screen
|
||||
name='LibraryScreen'
|
||||
component={LibraryScreen}
|
||||
options={{
|
||||
title: 'Library',
|
||||
|
||||
// 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,
|
||||
}}
|
||||
/>
|
||||
// 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,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name='Artist'
|
||||
component={ArtistScreen}
|
||||
options={({ route }) => ({
|
||||
title: route.params.artist.Name ?? 'Unknown Artist',
|
||||
headerTitleStyle: {
|
||||
color: theme.background.val,
|
||||
},
|
||||
})}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name='Artist'
|
||||
component={ArtistScreen}
|
||||
options={({ route }) => ({
|
||||
title: route.params.artist.Name ?? 'Unknown Artist',
|
||||
headerTitleStyle: {
|
||||
color: theme.background.val,
|
||||
},
|
||||
})}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name='Album'
|
||||
component={AlbumScreen}
|
||||
options={({ route }) => ({
|
||||
headerShown: true,
|
||||
title: route.params.album.Name ?? 'Untitled Album',
|
||||
headerTitleStyle: {
|
||||
color: theme.background.val,
|
||||
},
|
||||
})}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name='Album'
|
||||
component={AlbumScreen}
|
||||
options={({ route }) => ({
|
||||
headerShown: true,
|
||||
title: route.params.album.Name ?? 'Untitled Album',
|
||||
headerTitleStyle: {
|
||||
color: theme.background.val,
|
||||
},
|
||||
})}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name='Playlist'
|
||||
component={PlaylistScreen}
|
||||
options={({ route }) => ({
|
||||
headerShown: true,
|
||||
title: route.params.playlist.Name ?? 'Untitled Playlist',
|
||||
headerTitleStyle: {
|
||||
color: theme.background.val,
|
||||
},
|
||||
})}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name='Playlist'
|
||||
component={PlaylistScreen}
|
||||
options={({ route }) => ({
|
||||
headerShown: true,
|
||||
title: route.params.playlist.Name ?? 'Untitled Playlist',
|
||||
headerTitleStyle: {
|
||||
color: theme.background.val,
|
||||
},
|
||||
})}
|
||||
/>
|
||||
|
||||
<Stack.Group
|
||||
screenOptions={{
|
||||
presentation: 'formSheet',
|
||||
sheetAllowedDetents: 'fitToContents',
|
||||
}}
|
||||
>
|
||||
<Stack.Screen
|
||||
name='AddPlaylist'
|
||||
component={AddPlaylist}
|
||||
options={{
|
||||
title: 'Add Playlist',
|
||||
}}
|
||||
/>
|
||||
<Stack.Group
|
||||
screenOptions={{
|
||||
presentation: 'formSheet',
|
||||
sheetAllowedDetents: 'fitToContents',
|
||||
}}
|
||||
>
|
||||
<Stack.Screen
|
||||
name='AddPlaylist'
|
||||
component={AddPlaylist}
|
||||
options={{
|
||||
title: 'Add Playlist',
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name='DeletePlaylist'
|
||||
component={DeletePlaylist}
|
||||
options={{
|
||||
title: 'Delete Playlist',
|
||||
}}
|
||||
/>
|
||||
</Stack.Group>
|
||||
</Stack.Navigator>
|
||||
</LibraryProvider>
|
||||
</LibrarySortAndFilterProvider>
|
||||
<Stack.Screen
|
||||
name='DeletePlaylist'
|
||||
component={DeletePlaylist}
|
||||
options={{
|
||||
title: 'Delete Playlist',
|
||||
}}
|
||||
/>
|
||||
</Stack.Group>
|
||||
</Stack.Navigator>
|
||||
)
|
||||
}
|
||||
|
||||
16
src/screens/Library/navigation.tsx
Normal file
16
src/screens/Library/navigation.tsx
Normal file
@@ -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<NavigationProp<TabParamList> | 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
|
||||
@@ -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<BaseStackParamList>
|
||||
AddPlaylist: undefined
|
||||
|
||||
DeletePlaylist: {
|
||||
@@ -12,7 +15,7 @@ type LibraryStackParamList = BaseStackParamList & {
|
||||
|
||||
export default LibraryStackParamList
|
||||
|
||||
export type LibraryScreenProps = NativeStackScreenProps<LibraryStackParamList, 'Library'>
|
||||
export type LibraryScreenProps = NativeStackScreenProps<LibraryScreenParamList, 'LibraryScreen'>
|
||||
export type LibraryArtistProps = NativeStackScreenProps<LibraryStackParamList, 'Artist'>
|
||||
export type LibraryAlbumProps = NativeStackScreenProps<LibraryStackParamList, 'Album'>
|
||||
|
||||
@@ -22,10 +25,6 @@ export type LibraryDeletePlaylistProps = NativeStackScreenProps<
|
||||
'DeletePlaylist'
|
||||
>
|
||||
|
||||
type LibraryNavigation = {
|
||||
album?: BaseItemDto
|
||||
artist?: BaseItemDto
|
||||
playlist?: BaseItemDto
|
||||
type LibraryScreenParamList = {
|
||||
LibraryScreen: NavigatorScreenParams<LibraryStackParamList>
|
||||
}
|
||||
|
||||
export const LibraryNavigation: LibraryNavigation = {}
|
||||
|
||||
@@ -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<RootStackParamList>
|
||||
navigation: NativeStackNavigationProp<LoginStackParamList>
|
||||
}): React.JSX.Element {
|
||||
const { api } = useJellifyContext()
|
||||
const [username, setUsername] = useState<string | undefined>(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
|
||||
|
||||
7
src/screens/Login/types.ts
Normal file
7
src/screens/Login/types.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
type LoginStackParamList = {
|
||||
ServerAddress: undefined
|
||||
ServerAuthentication: undefined
|
||||
LibrarySelection: undefined
|
||||
}
|
||||
|
||||
export default LoginStackParamList
|
||||
@@ -9,9 +9,9 @@ export const PlayerStack = createNativeStackNavigator<PlayerParamList>()
|
||||
|
||||
export default function Player(): React.JSX.Element {
|
||||
return (
|
||||
<PlayerStack.Navigator initialRouteName='Player'>
|
||||
<PlayerStack.Navigator initialRouteName='PlayerScreen'>
|
||||
<PlayerStack.Screen
|
||||
name='Player'
|
||||
name='PlayerScreen'
|
||||
component={PlayerScreen}
|
||||
options={{
|
||||
headerShown: false,
|
||||
@@ -20,7 +20,7 @@ export default function Player(): React.JSX.Element {
|
||||
/>
|
||||
|
||||
<PlayerStack.Screen
|
||||
name='Queue'
|
||||
name='QueueScreen'
|
||||
component={Queue}
|
||||
options={{
|
||||
headerTitle: '',
|
||||
@@ -28,7 +28,7 @@ export default function Player(): React.JSX.Element {
|
||||
/>
|
||||
|
||||
<PlayerStack.Screen
|
||||
name='MultipleArtists'
|
||||
name='MultipleArtistsSheet'
|
||||
component={MultipleArtistsSheet}
|
||||
options={{
|
||||
presentation: 'formSheet',
|
||||
|
||||
11
src/screens/Player/types.d.ts
vendored
11
src/screens/Player/types.d.ts
vendored
@@ -2,13 +2,14 @@ import { RootStackParamList } from '../types'
|
||||
import { NativeStackScreenProps } from '@react-navigation/native-stack'
|
||||
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||
|
||||
type PlayerParamList = RootStackParamList & {
|
||||
Player: undefined
|
||||
Queue: undefined
|
||||
type PlayerParamList = {
|
||||
PlayerScreen: undefined
|
||||
QueueScreen: undefined
|
||||
|
||||
MultipleArtists: {
|
||||
MultipleArtistsSheet: {
|
||||
artists: BaseItemDto[]
|
||||
}
|
||||
}
|
||||
|
||||
export type MultipleArtistsProps = NativeStackScreenProps<PlayerParamList, 'MultipleArtists'>
|
||||
export type PlayerProps = NativeStackScreenProps<PlayerParamList, 'PlayerScreen'>
|
||||
export type MultipleArtistsProps = NativeStackScreenProps<PlayerParamList, 'MultipleArtistsSheet'>
|
||||
|
||||
@@ -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<TabParamList>()
|
||||
|
||||
export function Tabs(): React.JSX.Element {
|
||||
export default function Tabs({ route, navigation }: TabProps): React.JSX.Element {
|
||||
const theme = useTheme()
|
||||
const nowPlaying = useNowPlayingContext()
|
||||
|
||||
return (
|
||||
<Tab.Navigator
|
||||
initialRouteName='Home'
|
||||
initialRouteName={route.params?.screen ?? 'HomeTab'}
|
||||
screenOptions={{
|
||||
animation: 'shift',
|
||||
tabBarActiveTintColor: theme.primary.val,
|
||||
@@ -30,7 +31,7 @@ export function Tabs(): React.JSX.Element {
|
||||
<>
|
||||
{nowPlaying && (
|
||||
/* Hide miniplayer if the queue is empty */
|
||||
<Miniplayer navigation={props.navigation} />
|
||||
<Miniplayer />
|
||||
)}
|
||||
<InternetConnectionWatcher />
|
||||
|
||||
@@ -39,9 +40,10 @@ export function Tabs(): React.JSX.Element {
|
||||
)}
|
||||
>
|
||||
<Tab.Screen
|
||||
name='Home'
|
||||
name='HomeTab'
|
||||
component={Home}
|
||||
options={{
|
||||
title: 'Home',
|
||||
headerShown: false,
|
||||
tabBarIcon: ({ color, size }) => (
|
||||
<MaterialDesignIcons name='jellyfish-outline' color={color} size={size} />
|
||||
@@ -51,21 +53,24 @@ export function Tabs(): React.JSX.Element {
|
||||
/>
|
||||
|
||||
<Tab.Screen
|
||||
name='Library'
|
||||
name='LibraryTab'
|
||||
component={LibraryStack}
|
||||
options={{
|
||||
title: 'Library',
|
||||
headerShown: false,
|
||||
tabBarIcon: ({ color, size }) => (
|
||||
<MaterialDesignIcons name='music-box-multiple' color={color} size={size} />
|
||||
),
|
||||
tabBarButtonTestID: 'library-tab-button',
|
||||
lazy: false, // Load on mount since we need to be able to navigate here from the player
|
||||
}}
|
||||
/>
|
||||
|
||||
<Tab.Screen
|
||||
name='Search'
|
||||
name='SearchTab'
|
||||
component={SearchStack}
|
||||
options={{
|
||||
title: 'Search',
|
||||
headerShown: false,
|
||||
tabBarIcon: ({ color, size }) => (
|
||||
<MaterialDesignIcons name='magnify' color={color} size={size} />
|
||||
@@ -75,9 +80,10 @@ export function Tabs(): React.JSX.Element {
|
||||
/>
|
||||
|
||||
<Tab.Screen
|
||||
name='Discover'
|
||||
name='DiscoverTab'
|
||||
component={Discover}
|
||||
options={{
|
||||
title: 'Discover',
|
||||
headerShown: false,
|
||||
tabBarIcon: ({ color, size }) => (
|
||||
<MaterialDesignIcons name='earth' color={color} size={size} />
|
||||
@@ -87,9 +93,10 @@ export function Tabs(): React.JSX.Element {
|
||||
/>
|
||||
|
||||
<Tab.Screen
|
||||
name='Settings'
|
||||
name='SettingsTab'
|
||||
component={SettingsScreen}
|
||||
options={{
|
||||
title: 'Settings',
|
||||
headerShown: false,
|
||||
tabBarIcon: ({ color, size }) => (
|
||||
<MaterialDesignIcons name='dip-switch' color={color} size={size} />
|
||||
|
||||
14
src/screens/Tabs/types.d.ts
vendored
14
src/screens/Tabs/types.d.ts
vendored
@@ -3,14 +3,14 @@ import { NavigatorScreenParams } from '@react-navigation/native'
|
||||
import LibraryStackParamList from '../Library/types'
|
||||
|
||||
type TabParamList = {
|
||||
Home: undefined
|
||||
Library: NavigatorScreenParams<LibraryStackParamList>
|
||||
Search: undefined
|
||||
Discover: undefined
|
||||
Settings: undefined
|
||||
HomeTab: undefined
|
||||
LibraryTab: NavigatorScreenParams<LibraryStackParamList>
|
||||
SearchTab: undefined
|
||||
DiscoverTab: undefined
|
||||
SettingsTab: undefined
|
||||
}
|
||||
|
||||
export type HomeTabProps = BottomTabScreenProps<TabParamList, 'Home'>
|
||||
export type LibraryTabProps = BottomTabScreenProps<TabParamList, 'Library'>
|
||||
export type HomeTabProps = BottomTabScreenProps<TabParamList, 'HomeTab'>
|
||||
export type LibraryTabProps = BottomTabScreenProps<TabParamList, 'LibraryTab'>
|
||||
|
||||
export default TabParamList
|
||||
|
||||
@@ -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,
|
||||
})}
|
||||
>
|
||||
<RootStack.Screen
|
||||
@@ -33,7 +33,7 @@ export default function Root(): React.JSX.Element {
|
||||
}}
|
||||
/>
|
||||
<RootStack.Screen
|
||||
name='Player'
|
||||
name='PlayerRoot'
|
||||
component={Player}
|
||||
options={{
|
||||
presentation: 'modal',
|
||||
|
||||
12
src/screens/types.d.ts
vendored
12
src/screens/types.d.ts
vendored
@@ -17,12 +17,6 @@ import TabParamList from './Tabs/types'
|
||||
import { PlayerParamList } from './Player/types'
|
||||
|
||||
export type BaseStackParamList = {
|
||||
Home: undefined
|
||||
Library: undefined
|
||||
Search: undefined
|
||||
Discover: undefined
|
||||
Settings: undefined
|
||||
|
||||
Artist: {
|
||||
artist: BaseItemDto
|
||||
}
|
||||
@@ -52,6 +46,7 @@ export type BaseStackParamList = {
|
||||
|
||||
export type ArtistProps = NativeStackScreenProps<BaseStackParamList, 'Artist'>
|
||||
export type AlbumProps = NativeStackScreenProps<BaseStackParamList, 'Album'>
|
||||
export type PlaylistProps = NativeStackNavigationProp<BaseStackParamList, 'Playlist'>
|
||||
export type TracksProps = NativeStackScreenProps<BaseStackParamList, 'Tracks'>
|
||||
export type InstantMixProps = NativeStackScreenProps<BaseStackParamList, 'InstantMix'>
|
||||
|
||||
@@ -59,19 +54,20 @@ export type RootStackParamList = {
|
||||
Login: undefined
|
||||
Tabs: NavigatorScreenParams<TabParamList>
|
||||
|
||||
Player: NavigatorScreenParams<PlayerParamList>
|
||||
PlayerRoot: NavigatorScreenParams<PlayerParamList>
|
||||
|
||||
Context: {
|
||||
item: BaseItemDto
|
||||
navigation?: NativeStackNavigationProp<
|
||||
HomeStackParamList | LibraryStackParamList | DiscoverStackParamList
|
||||
>
|
||||
navigationCallback?: (screen: 'Album' | 'Artist', item: BaseItemDto) => void
|
||||
}
|
||||
}
|
||||
|
||||
export type LoginProps = NativeStackNavigationProp<RootStackParamList, 'Login'>
|
||||
export type TabProps = NativeStackScreenProps<RootStackParamList, 'Tabs'>
|
||||
export type PlayerProps = NativeStackScreenProps<RootStackParamList, 'Player'>
|
||||
export type PlayerProps = NativeStackScreenProps<RootStackParamList, 'PlayerRoot'>
|
||||
export type ContextProps = NativeStackScreenProps<RootStackParamList, 'Context'>
|
||||
|
||||
export type ArtistsProps = {
|
||||
|
||||
Reference in New Issue
Block a user