mirror of
https://github.com/anultravioletaurora/Jellify.git
synced 2025-12-17 19:25:34 -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 OTAUpdateScreen from './src/components/OtaUpdates'
|
||||||
import { usePerformanceMonitor } from './src/hooks/use-performance-monitor'
|
import { usePerformanceMonitor } from './src/hooks/use-performance-monitor'
|
||||||
import { SettingsProvider, useThemeSettingContext } from './src/providers/Settings'
|
import { SettingsProvider, useThemeSettingContext } from './src/providers/Settings'
|
||||||
import { navigationRef } from './navigation'
|
import navigationRef from './navigation'
|
||||||
|
|
||||||
export default function App(): React.JSX.Element {
|
export default function App(): React.JSX.Element {
|
||||||
// Add performance monitoring to track app-level re-renders
|
// 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 { name as appName } from './app.json'
|
||||||
import { PlaybackService } from './src/player/service'
|
import { PlaybackService } from './src/player/service'
|
||||||
import TrackPlayer from 'react-native-track-player'
|
import TrackPlayer from 'react-native-track-player'
|
||||||
import { enableFreeze } from 'react-native-screens'
|
import { enableFreeze, enableScreens } from 'react-native-screens'
|
||||||
|
|
||||||
|
enableScreens(true)
|
||||||
enableFreeze(true)
|
enableFreeze(true)
|
||||||
|
|
||||||
AppRegistry.registerComponent(appName, () => App)
|
AppRegistry.registerComponent(appName, () => App)
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { createNavigationContainerRef } from '@react-navigation/native'
|
import { createNavigationContainerRef } from '@react-navigation/native'
|
||||||
import { RootStackParamList } from './src/screens/types'
|
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'
|
contentInsetAdjustmentBehavior='automatic'
|
||||||
sections={!isUndefined(discs) ? discs : []}
|
sections={!isUndefined(discs) ? discs : []}
|
||||||
keyExtractor={(item, index) => item.Id! + index}
|
keyExtractor={(item, index) => item.Id! + index}
|
||||||
ItemSeparatorComponent={() => <Separator />}
|
ItemSeparatorComponent={Separator}
|
||||||
renderSectionHeader={({ section }) => {
|
renderSectionHeader={({ section }) => {
|
||||||
return (
|
return (
|
||||||
<XStack
|
<XStack
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react'
|
import React, { useMemo } from 'react'
|
||||||
import Albums from './albums'
|
import Albums from './albums'
|
||||||
import SimilarArtists from './similar'
|
import SimilarArtists from './similar'
|
||||||
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs'
|
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs'
|
||||||
@@ -11,9 +11,11 @@ const ArtistTabs = createMaterialTopTabNavigator<ArtistTabList>()
|
|||||||
export default function ArtistNavigation(): React.JSX.Element {
|
export default function ArtistNavigation(): React.JSX.Element {
|
||||||
const { featuredOn, artist } = useArtistContext()
|
const { featuredOn, artist } = useArtistContext()
|
||||||
|
|
||||||
|
const hasFeaturedOn = useMemo(() => featuredOn && featuredOn.length > 0, [artist])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ArtistTabs.Navigator
|
<ArtistTabs.Navigator
|
||||||
tabBar={(props) => ArtistTabBar(props)}
|
tabBar={ArtistTabBar}
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
tabBarLabelStyle: {
|
tabBarLabelStyle: {
|
||||||
fontFamily: 'Figtree-Bold',
|
fontFamily: 'Figtree-Bold',
|
||||||
@@ -36,7 +38,7 @@ export default function ArtistNavigation(): React.JSX.Element {
|
|||||||
component={Albums}
|
component={Albums}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{featuredOn && featuredOn.length > 0 && (
|
{hasFeaturedOn && (
|
||||||
<ArtistTabs.Screen
|
<ArtistTabs.Screen
|
||||||
name='ArtistFeaturedOn'
|
name='ArtistFeaturedOn'
|
||||||
options={{
|
options={{
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import { RouteProp, useNavigation } from '@react-navigation/native'
|
|||||||
import { RootStackParamList } from '../../../screens/types'
|
import { RootStackParamList } from '../../../screens/types'
|
||||||
|
|
||||||
interface MultipleArtistsProps {
|
interface MultipleArtistsProps {
|
||||||
navigation: NativeStackNavigationProp<PlayerParamList, 'MultipleArtists'>
|
navigation: NativeStackNavigationProp<PlayerParamList, 'MultipleArtistsSheet'>
|
||||||
route: RouteProp<PlayerParamList, 'MultipleArtists'>
|
route: RouteProp<PlayerParamList, 'MultipleArtistsSheet'>
|
||||||
}
|
}
|
||||||
export default function MultipleArtists({
|
export default function MultipleArtists({
|
||||||
navigation,
|
navigation,
|
||||||
@@ -27,7 +27,7 @@ export default function MultipleArtists({
|
|||||||
navigation.popToTop()
|
navigation.popToTop()
|
||||||
|
|
||||||
rootNavigation.popTo('Tabs', {
|
rootNavigation.popTo('Tabs', {
|
||||||
screen: 'Library',
|
screen: 'LibraryTab',
|
||||||
params: {
|
params: {
|
||||||
screen: 'Artist',
|
screen: 'Artist',
|
||||||
params: {
|
params: {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Text } from '../Global/helpers/text'
|
|||||||
import FavoriteContextMenuRow from '../Global/components/favorite-context-menu-row'
|
import FavoriteContextMenuRow from '../Global/components/favorite-context-menu-row'
|
||||||
import { Blurhash } from 'react-native-blurhash'
|
import { Blurhash } from 'react-native-blurhash'
|
||||||
import { getPrimaryBlurhashFromDto } from '../../utils/blurhash'
|
import { getPrimaryBlurhashFromDto } from '../../utils/blurhash'
|
||||||
import { useColorScheme } from 'react-native'
|
import { InteractionManager, useColorScheme } from 'react-native'
|
||||||
import { useThemeSettingContext } from '../../providers/Settings'
|
import { useThemeSettingContext } from '../../providers/Settings'
|
||||||
import LinearGradient from 'react-native-linear-gradient'
|
import LinearGradient from 'react-native-linear-gradient'
|
||||||
import Icon from '../Global/components/icon'
|
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 { useAddToQueueContext } from '../../providers/Player/queue'
|
||||||
import { AddToQueueMutation } from '../../providers/Player/interfaces'
|
import { AddToQueueMutation } from '../../providers/Player/interfaces'
|
||||||
import { QueuingType } from '../../enums/queuing-type'
|
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 DiscoverStackParamList from '../../screens/Discover/types'
|
||||||
import HomeStackParamList from '../../screens/Home/types'
|
import HomeStackParamList from '../../screens/Home/types'
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
|
import { StackActions, TabActions } from '@react-navigation/native'
|
||||||
|
import navigationRef from '../../../navigation'
|
||||||
|
import { goToAlbumFromContextSheet, goToArtistFromContextSheet } from './utils/navigation'
|
||||||
|
|
||||||
interface ContextProps {
|
interface ContextProps {
|
||||||
item: BaseItemDto
|
item: BaseItemDto
|
||||||
@@ -29,14 +32,11 @@ interface ContextProps {
|
|||||||
HomeStackParamList | LibraryStackParamList | DiscoverStackParamList
|
HomeStackParamList | LibraryStackParamList | DiscoverStackParamList
|
||||||
>
|
>
|
||||||
navigation: NativeStackNavigationProp<RootStackParamList>
|
navigation: NativeStackNavigationProp<RootStackParamList>
|
||||||
|
navigationCallback?: (screen: 'Album' | 'Artist', item: BaseItemDto) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ItemContext({
|
export default function ItemContext({ item, stackNavigation }: ContextProps): React.JSX.Element {
|
||||||
item,
|
const { api } = useJellifyContext()
|
||||||
stackNavigation,
|
|
||||||
navigation,
|
|
||||||
}: ContextProps): React.JSX.Element {
|
|
||||||
const { api, user, library } = useJellifyContext()
|
|
||||||
|
|
||||||
const isArtist = item.Type === BaseItemKind.MusicArtist
|
const isArtist = item.Type === BaseItemKind.MusicArtist
|
||||||
const isAlbum = item.Type === BaseItemKind.MusicAlbum
|
const isAlbum = item.Type === BaseItemKind.MusicAlbum
|
||||||
@@ -45,19 +45,19 @@ export default function ItemContext({
|
|||||||
|
|
||||||
const albumArtists = item.AlbumArtists ?? []
|
const albumArtists = item.AlbumArtists ?? []
|
||||||
|
|
||||||
const { data: album, isSuccess: albumFetchSuccess } = useQuery({
|
const { data: album } = useQuery({
|
||||||
queryKey: [QueryKeys.Item, item.AlbumId],
|
queryKey: [QueryKeys.Item, item.AlbumId],
|
||||||
queryFn: () => fetchItem(api, item.AlbumId!),
|
queryFn: () => fetchItem(api, item.AlbumId!),
|
||||||
enabled: isTrack,
|
enabled: isTrack,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: artist, isSuccess: artistFetchSuccess } = useQuery({
|
const { data: artist } = useQuery({
|
||||||
queryKey: [QueryKeys.ArtistById, albumArtists.length > 0 ? albumArtists[0].Id : item.Id],
|
queryKey: [QueryKeys.ArtistById, albumArtists.length > 0 ? albumArtists[0].Id : item.Id],
|
||||||
queryFn: () => fetchItem(api, albumArtists[0].Id!),
|
queryFn: () => fetchItem(api, albumArtists[0].Id!),
|
||||||
enabled: (isTrack || isAlbum) && albumArtists.length > 0,
|
enabled: (isTrack || isAlbum) && albumArtists.length > 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: tracks, isSuccess: tracksFetchSuccess } = useQuery({
|
const { data: tracks } = useQuery({
|
||||||
queryKey: [QueryKeys.ItemTracks, item.Id],
|
queryKey: [QueryKeys.ItemTracks, item.Id],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
getItemsApi(api!)
|
getItemsApi(api!)
|
||||||
@@ -85,7 +85,6 @@ export default function ItemContext({
|
|||||||
<ViewAlbumMenuRow
|
<ViewAlbumMenuRow
|
||||||
item={isAlbum ? item : album!}
|
item={isAlbum ? item : album!}
|
||||||
stackNavigation={stackNavigation}
|
stackNavigation={stackNavigation}
|
||||||
rootNavigation={navigation}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -93,7 +92,6 @@ export default function ItemContext({
|
|||||||
<ViewArtistMenuRow
|
<ViewArtistMenuRow
|
||||||
item={isArtist ? item : artist}
|
item={isArtist ? item : artist}
|
||||||
stackNavigation={stackNavigation}
|
stackNavigation={stackNavigation}
|
||||||
rootNavigation={navigation}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</YGroup>
|
</YGroup>
|
||||||
@@ -171,25 +169,13 @@ interface MenuRowProps {
|
|||||||
stackNavigation?: NativeStackNavigationProp<
|
stackNavigation?: NativeStackNavigationProp<
|
||||||
HomeStackParamList | LibraryStackParamList | DiscoverStackParamList
|
HomeStackParamList | LibraryStackParamList | DiscoverStackParamList
|
||||||
>
|
>
|
||||||
rootNavigation: NativeStackNavigationProp<RootStackParamList>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function ViewAlbumMenuRow({
|
function ViewAlbumMenuRow({ item: album, stackNavigation }: MenuRowProps): React.JSX.Element {
|
||||||
item: album,
|
|
||||||
stackNavigation,
|
|
||||||
rootNavigation,
|
|
||||||
}: MenuRowProps): React.JSX.Element {
|
|
||||||
const goToAlbum = useCallback(() => {
|
const goToAlbum = useCallback(() => {
|
||||||
if (stackNavigation && album) stackNavigation.navigate('Album', { album })
|
if (stackNavigation && album) stackNavigation.navigate('Album', { album })
|
||||||
else if (album) {
|
else goToAlbumFromContextSheet(album)
|
||||||
rootNavigation.popTo('Tabs', {
|
}, [album, stackNavigation, navigationRef])
|
||||||
screen: 'Library',
|
|
||||||
merge: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
LibraryNavigation.album = album
|
|
||||||
}
|
|
||||||
}, [album, stackNavigation, rootNavigation])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem
|
<ListItem
|
||||||
@@ -207,22 +193,11 @@ function ViewAlbumMenuRow({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ViewArtistMenuRow({
|
function ViewArtistMenuRow({ item: artist, stackNavigation }: MenuRowProps): React.JSX.Element {
|
||||||
item: artist,
|
|
||||||
stackNavigation,
|
|
||||||
rootNavigation,
|
|
||||||
}: MenuRowProps): React.JSX.Element {
|
|
||||||
const goToArtist = useCallback(() => {
|
const goToArtist = useCallback(() => {
|
||||||
if (stackNavigation && artist) stackNavigation.navigate('Artist', { artist })
|
if (stackNavigation && artist) stackNavigation.navigate('Artist', { artist })
|
||||||
else if (artist) {
|
else goToArtistFromContextSheet(artist)
|
||||||
rootNavigation.popTo('Tabs', {
|
}, [artist, stackNavigation, navigationRef])
|
||||||
screen: 'Library',
|
|
||||||
merge: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
LibraryNavigation.artist = artist
|
|
||||||
}
|
|
||||||
}, [artist, stackNavigation, rootNavigation])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem
|
<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 FrequentArtists from './helpers/frequent-artists'
|
||||||
import FrequentlyPlayedTracks from './helpers/frequent-tracks'
|
import FrequentlyPlayedTracks from './helpers/frequent-tracks'
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||||
import { useJellifyContext } from '../../providers'
|
|
||||||
import { usePreventRemove } from '@react-navigation/native'
|
import { usePreventRemove } from '@react-navigation/native'
|
||||||
|
|
||||||
export function ProvidedHome(): React.JSX.Element {
|
export function ProvidedHome(): React.JSX.Element {
|
||||||
usePreventRemove(true, () => {})
|
usePreventRemove(true, () => {})
|
||||||
const { user } = useJellifyContext()
|
|
||||||
const { refreshing: refetching, onRefresh } = useHomeContext()
|
const { refreshing: refetching, onRefresh } = useHomeContext()
|
||||||
const insets = useSafeAreaInsets()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView
|
<ScrollView
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ import React from 'react'
|
|||||||
|
|
||||||
const LibraryTabsNavigator = createMaterialTopTabNavigator()
|
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()
|
const theme = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import Icon from '../../Global/components/icon'
|
|||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { QueryKeys } from '../../../enums/query-keys'
|
import { QueryKeys } from '../../../enums/query-keys'
|
||||||
import { BaseItemDto, BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models'
|
import { BaseItemDto, BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models'
|
||||||
|
import { PlayerParamList } from '../../../screens/Player/types'
|
||||||
|
|
||||||
export default function SongInfo(): React.JSX.Element {
|
export default function SongInfo(): React.JSX.Element {
|
||||||
const { api, user, library } = useJellifyContext()
|
const { api, user, library } = useJellifyContext()
|
||||||
@@ -28,11 +29,7 @@ export default function SongInfo(): React.JSX.Element {
|
|||||||
queryFn: () => fetchItem(api, nowPlaying!.item.AlbumId!),
|
queryFn: () => fetchItem(api, nowPlaying!.item.AlbumId!),
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: artists } = useQuery<
|
useQuery({
|
||||||
{ title: string | number; data: BaseItemDto[] },
|
|
||||||
Error,
|
|
||||||
void
|
|
||||||
>({
|
|
||||||
queryKey: [QueryKeys.TrackArtists, nowPlaying!.item.ArtistItems],
|
queryKey: [QueryKeys.TrackArtists, nowPlaying!.item.ArtistItems],
|
||||||
queryFn: () => fetchItems(api, user, library, [BaseItemKind.MusicArtist]),
|
queryFn: () => fetchItems(api, user, library, [BaseItemKind.MusicArtist]),
|
||||||
select: (data: { title: string | number; data: BaseItemDto[] }) => data.data,
|
select: (data: { title: string | number; data: BaseItemDto[] }) => data.data,
|
||||||
@@ -46,7 +43,7 @@ export default function SongInfo(): React.JSX.Element {
|
|||||||
onPress={() => {
|
onPress={() => {
|
||||||
if (album) {
|
if (album) {
|
||||||
navigation.popTo('Tabs', {
|
navigation.popTo('Tabs', {
|
||||||
screen: 'Library',
|
screen: 'LibraryTab',
|
||||||
params: {
|
params: {
|
||||||
screen: 'Album',
|
screen: 'Album',
|
||||||
params: {
|
params: {
|
||||||
@@ -86,12 +83,15 @@ export default function SongInfo(): React.JSX.Element {
|
|||||||
onPress={() => {
|
onPress={() => {
|
||||||
if (nowPlaying!.item.ArtistItems) {
|
if (nowPlaying!.item.ArtistItems) {
|
||||||
if (nowPlaying!.item.ArtistItems!.length > 1) {
|
if (nowPlaying!.item.ArtistItems!.length > 1) {
|
||||||
navigation.navigate('MultipleArtists', {
|
navigation.navigate('PlayerRoot', {
|
||||||
artists: nowPlaying!.item.ArtistItems!,
|
screen: 'MultipleArtistsSheet',
|
||||||
|
params: {
|
||||||
|
artists: nowPlaying!.item.ArtistItems!,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
navigation.popTo('Tabs', {
|
navigation.popTo('Tabs', {
|
||||||
screen: 'Library',
|
screen: 'LibraryTab',
|
||||||
params: {
|
params: {
|
||||||
screen: 'Artist',
|
screen: 'Artist',
|
||||||
params: {
|
params: {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
} from 'tamagui'
|
} from 'tamagui'
|
||||||
import { useNowPlayingContext } from '../../providers/Player'
|
import { useNowPlayingContext } from '../../providers/Player'
|
||||||
import { BottomTabNavigationEventMap } from '@react-navigation/bottom-tabs'
|
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 { Text } from '../Global/helpers/text'
|
||||||
import TextTicker from 'react-native-text-ticker'
|
import TextTicker from 'react-native-text-ticker'
|
||||||
import PlayPauseButton from './components/buttons'
|
import PlayPauseButton from './components/buttons'
|
||||||
@@ -32,17 +32,17 @@ import Animated, {
|
|||||||
withSpring,
|
withSpring,
|
||||||
} from 'react-native-reanimated'
|
} from 'react-native-reanimated'
|
||||||
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models'
|
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({
|
export const Miniplayer = React.memo(function Miniplayer(): React.JSX.Element {
|
||||||
navigation,
|
|
||||||
}: {
|
|
||||||
navigation: NavigationHelpers<ParamListBase, BottomTabNavigationEventMap>
|
|
||||||
}): React.JSX.Element {
|
|
||||||
const { api } = useJellifyContext()
|
const { api } = useJellifyContext()
|
||||||
const nowPlaying = useNowPlayingContext()
|
const nowPlaying = useNowPlayingContext()
|
||||||
const useSkip = useSkipContext()
|
const useSkip = useSkipContext()
|
||||||
const usePrevious = usePreviousContext()
|
const usePrevious = usePreviousContext()
|
||||||
|
|
||||||
|
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList>>()
|
||||||
|
|
||||||
const translateX = useSharedValue(0)
|
const translateX = useSharedValue(0)
|
||||||
const translateY = useSharedValue(0)
|
const translateY = useSharedValue(0)
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ export const Miniplayer = React.memo(function Miniplayer({
|
|||||||
useSkip()
|
useSkip()
|
||||||
} else if (direction === 'Swiped Up') {
|
} else if (direction === 'Swiped Up') {
|
||||||
// Navigate to the big player
|
// Navigate to the big player
|
||||||
navigation.navigate('Player')
|
navigation.navigate('PlayerRoot', { screen: 'PlayerScreen' })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[useSkip, usePrevious, navigation],
|
[useSkip, usePrevious, navigation],
|
||||||
@@ -102,7 +102,9 @@ export const Miniplayer = React.memo(function Miniplayer({
|
|||||||
margin={0}
|
margin={0}
|
||||||
padding={0}
|
padding={0}
|
||||||
height={'$7'}
|
height={'$7'}
|
||||||
onPress={() => navigation.navigate('Player')}
|
onPress={() =>
|
||||||
|
navigation.navigate('PlayerRoot', { screen: 'PlayerScreen' })
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<YStack
|
<YStack
|
||||||
justify='center'
|
justify='center'
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ import Toast from 'react-native-toast-message'
|
|||||||
import JellifyToastConfig from '../constants/toast.config'
|
import JellifyToastConfig from '../constants/toast.config'
|
||||||
import { useColorScheme } from 'react-native'
|
import { useColorScheme } from 'react-native'
|
||||||
import { CarPlayProvider } from '../providers/CarPlay'
|
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}
|
* The main component for the Jellify app. Children are wrapped in the {@link JellifyProvider}
|
||||||
* @returns The {@link Jellify} component
|
* @returns The {@link Jellify} component
|
||||||
@@ -83,8 +86,14 @@ function App(): React.JSX.Element {
|
|||||||
<NetworkContextProvider>
|
<NetworkContextProvider>
|
||||||
<QueueProvider>
|
<QueueProvider>
|
||||||
<PlayerProvider>
|
<PlayerProvider>
|
||||||
<CarPlayProvider />
|
<HomeProvider>
|
||||||
<Root />
|
<LibrarySortAndFilterProvider>
|
||||||
|
<LibraryProvider>
|
||||||
|
<CarPlayProvider />
|
||||||
|
<Root />
|
||||||
|
</LibraryProvider>
|
||||||
|
</LibrarySortAndFilterProvider>
|
||||||
|
</HomeProvider>
|
||||||
</PlayerProvider>
|
</PlayerProvider>
|
||||||
</QueueProvider>
|
</QueueProvider>
|
||||||
</NetworkContextProvider>
|
</NetworkContextProvider>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ function AlbumContextInitializer(album: BaseItemDto): AlbumContext {
|
|||||||
const { data: discs, isPending } = useQuery({
|
const { data: discs, isPending } = useQuery({
|
||||||
queryKey: [QueryKeys.ItemTracks, album.Id!],
|
queryKey: [QueryKeys.ItemTracks, album.Id!],
|
||||||
queryFn: () => fetchAlbumDiscs(api, album),
|
queryFn: () => fetchAlbumDiscs(api, album),
|
||||||
|
enabled: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -41,7 +42,7 @@ export const AlbumProvider: ({
|
|||||||
}) => React.JSX.Element = ({ album, children }) => {
|
}) => React.JSX.Element = ({ album, children }) => {
|
||||||
const context = AlbumContextInitializer(album)
|
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)
|
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 { SharedValue, useSharedValue } from 'react-native-reanimated'
|
||||||
import { useJellifyContext } from '..'
|
import { useJellifyContext } from '..'
|
||||||
import { fetchArtistAlbums, fetchArtistFeaturedOn } from '../../api/queries/artist'
|
import { fetchArtistAlbums, fetchArtistFeaturedOn } from '../../api/queries/artist'
|
||||||
|
import { isUndefined } from 'lodash'
|
||||||
|
import { Spinner } from 'tamagui'
|
||||||
|
|
||||||
interface ArtistContext {
|
interface ArtistContext {
|
||||||
fetchingAlbums: boolean
|
fetchingAlbums: boolean
|
||||||
@@ -45,8 +47,9 @@ export const ArtistProvider = ({
|
|||||||
refetch: refetchAlbums,
|
refetch: refetchAlbums,
|
||||||
isPending: fetchingAlbums,
|
isPending: fetchingAlbums,
|
||||||
} = useQuery({
|
} = useQuery({
|
||||||
queryKey: [QueryKeys.ArtistAlbums, library?.musicLibraryId, artist.Id!],
|
queryKey: [QueryKeys.ArtistAlbums, library?.musicLibraryId, artist.Id],
|
||||||
queryFn: () => fetchArtistAlbums(api, library?.musicLibraryId, artist),
|
queryFn: () => fetchArtistAlbums(api, library?.musicLibraryId, artist),
|
||||||
|
enabled: !isUndefined(artist.Id),
|
||||||
})
|
})
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -54,8 +57,9 @@ export const ArtistProvider = ({
|
|||||||
refetch: refetchFeaturedOn,
|
refetch: refetchFeaturedOn,
|
||||||
isPending: fetchingFeaturedOn,
|
isPending: fetchingFeaturedOn,
|
||||||
} = useQuery({
|
} = useQuery({
|
||||||
queryKey: [QueryKeys.ArtistFeaturedOn, library?.musicLibraryId, artist.Id!],
|
queryKey: [QueryKeys.ArtistFeaturedOn, library?.musicLibraryId, artist.Id],
|
||||||
queryFn: () => fetchArtistFeaturedOn(api, library?.musicLibraryId, artist),
|
queryFn: () => fetchArtistFeaturedOn(api, library?.musicLibraryId, artist),
|
||||||
|
enabled: !isUndefined(artist.Id),
|
||||||
})
|
})
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -63,8 +67,9 @@ export const ArtistProvider = ({
|
|||||||
refetch: refetchSimilar,
|
refetch: refetchSimilar,
|
||||||
isPending: fetchingSimilarArtists,
|
isPending: fetchingSimilarArtists,
|
||||||
} = useQuery({
|
} = useQuery({
|
||||||
queryKey: [QueryKeys.SimilarItems, library?.musicLibraryId, artist.Id!],
|
queryKey: [QueryKeys.SimilarItems, library?.musicLibraryId, artist.Id],
|
||||||
queryFn: () => fetchSimilar(api, user, library?.musicLibraryId, artist.Id!),
|
queryFn: () => fetchSimilar(api, user, library?.musicLibraryId, artist.Id!),
|
||||||
|
enabled: !isUndefined(artist.Id),
|
||||||
})
|
})
|
||||||
|
|
||||||
const refresh = useCallback(() => {
|
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)
|
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 { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||||
import {
|
import {
|
||||||
InfiniteData,
|
InfiniteData,
|
||||||
@@ -12,6 +19,7 @@ import { queryClient } from '../../constants/query-client'
|
|||||||
import QueryConfig from '../../api/queries/query.config'
|
import QueryConfig from '../../api/queries/query.config'
|
||||||
import { fetchFrequentlyPlayed, fetchFrequentlyPlayedArtists } from '../../api/queries/frequents'
|
import { fetchFrequentlyPlayed, fetchFrequentlyPlayedArtists } from '../../api/queries/frequents'
|
||||||
import { useJellifyContext } from '..'
|
import { useJellifyContext } from '..'
|
||||||
|
import { useIsFocused } from '@react-navigation/native'
|
||||||
interface HomeContext {
|
interface HomeContext {
|
||||||
refreshing: boolean
|
refreshing: boolean
|
||||||
onRefresh: () => void
|
onRefresh: () => void
|
||||||
@@ -36,6 +44,12 @@ const HomeContextInitializer = () => {
|
|||||||
const { api, library, user } = useJellifyContext()
|
const { api, library, user } = useJellifyContext()
|
||||||
const [refreshing, setRefreshing] = useState<boolean>(false)
|
const [refreshing, setRefreshing] = useState<boolean>(false)
|
||||||
|
|
||||||
|
const isFocused = useIsFocused()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.debug(`Home focused: ${isFocused}`)
|
||||||
|
}, [isFocused])
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: recentTracks,
|
data: recentTracks,
|
||||||
isFetching: isFetchingRecentTracks,
|
isFetching: isFetchingRecentTracks,
|
||||||
@@ -98,7 +112,7 @@ const HomeContextInitializer = () => {
|
|||||||
enabled: !!frequentlyPlayed && frequentlyPlayed.length > 0 && !isStaleFrequentlyPlayed,
|
enabled: !!frequentlyPlayed && frequentlyPlayed.length > 0 && !isStaleFrequentlyPlayed,
|
||||||
})
|
})
|
||||||
|
|
||||||
const onRefresh = async () => {
|
const onRefresh = useCallback(async () => {
|
||||||
setRefreshing(true)
|
setRefreshing(true)
|
||||||
|
|
||||||
queryClient.invalidateQueries({ queryKey: [QueryKeys.RecentlyPlayedArtists] })
|
queryClient.invalidateQueries({ queryKey: [QueryKeys.RecentlyPlayedArtists] })
|
||||||
@@ -114,7 +128,12 @@ const HomeContextInitializer = () => {
|
|||||||
])
|
])
|
||||||
|
|
||||||
setRefreshing(false)
|
setRefreshing(false)
|
||||||
}
|
}, [
|
||||||
|
refetchRecentTracks,
|
||||||
|
refetchFrequentlyPlayed,
|
||||||
|
recentArtistsInfiniteQuery.refetch,
|
||||||
|
frequentArtistsInfiniteQuery.refetch,
|
||||||
|
])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
refreshing,
|
refreshing,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export default function ItemContextScreen({ route, navigation }: ContextProps):
|
|||||||
item={route.params.item}
|
item={route.params.item}
|
||||||
stackNavigation={route.params.navigation}
|
stackNavigation={route.params.navigation}
|
||||||
navigation={navigation}
|
navigation={navigation}
|
||||||
|
navigationCallback={route.params.navigationCallback}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,77 +21,75 @@ export default function Home(): React.JSX.Element {
|
|||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HomeProvider>
|
<HomeStack.Navigator initialRouteName='HomeScreen' screenOptions={{ headerShown: true }}>
|
||||||
<HomeStack.Navigator initialRouteName='Home' screenOptions={{ headerShown: true }}>
|
<HomeStack.Group>
|
||||||
<HomeStack.Group>
|
<HomeStack.Screen
|
||||||
<HomeStack.Screen
|
name='HomeScreen'
|
||||||
name='Home'
|
component={ProvidedHome}
|
||||||
component={ProvidedHome}
|
options={{
|
||||||
options={{
|
title: 'Home',
|
||||||
title: 'Home',
|
headerTitleStyle: {
|
||||||
headerTitleStyle: {
|
fontFamily: 'Figtree-Bold',
|
||||||
fontFamily: 'Figtree-Bold',
|
},
|
||||||
},
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
<HomeStack.Screen
|
||||||
<HomeStack.Screen
|
name='Artist'
|
||||||
name='Artist'
|
component={ArtistScreen}
|
||||||
component={ArtistScreen}
|
options={({ route }) => ({
|
||||||
options={({ route }) => ({
|
title: route.params.artist.Name ?? 'Unknown Artist',
|
||||||
title: route.params.artist.Name ?? 'Unknown Artist',
|
headerTitleStyle: {
|
||||||
headerTitleStyle: {
|
color: theme.background.val,
|
||||||
color: theme.background.val,
|
fontFamily: 'Figtree-Bold',
|
||||||
fontFamily: 'Figtree-Bold',
|
},
|
||||||
},
|
})}
|
||||||
})}
|
/>
|
||||||
/>
|
|
||||||
|
|
||||||
<HomeStack.Screen
|
<HomeStack.Screen
|
||||||
name='RecentArtists'
|
name='RecentArtists'
|
||||||
component={HomeArtistsScreen}
|
component={HomeArtistsScreen}
|
||||||
options={{ title: 'Recent Artists' }}
|
options={{ title: 'Recent Artists' }}
|
||||||
/>
|
/>
|
||||||
<HomeStack.Screen
|
<HomeStack.Screen
|
||||||
name='MostPlayedArtists'
|
name='MostPlayedArtists'
|
||||||
component={HomeArtistsScreen}
|
component={HomeArtistsScreen}
|
||||||
options={{ title: 'Most Played' }}
|
options={{ title: 'Most Played' }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<HomeStack.Screen
|
<HomeStack.Screen
|
||||||
name='RecentTracks'
|
name='RecentTracks'
|
||||||
component={HomeTracksScreen}
|
component={HomeTracksScreen}
|
||||||
options={{ title: 'Recently Played' }}
|
options={{ title: 'Recently Played' }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<HomeStack.Screen
|
<HomeStack.Screen
|
||||||
name='MostPlayedTracks'
|
name='MostPlayedTracks'
|
||||||
component={HomeTracksScreen}
|
component={HomeTracksScreen}
|
||||||
options={{ title: 'On Repeat' }}
|
options={{ title: 'On Repeat' }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<HomeStack.Screen
|
<HomeStack.Screen
|
||||||
name='Album'
|
name='Album'
|
||||||
component={AlbumScreen}
|
component={AlbumScreen}
|
||||||
options={({ route }) => ({
|
options={({ route }) => ({
|
||||||
title: route.params.album.Name ?? 'Untitled Album',
|
title: route.params.album.Name ?? 'Untitled Album',
|
||||||
headerTitleStyle: {
|
headerTitleStyle: {
|
||||||
color: theme.background.val,
|
color: theme.background.val,
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<HomeStack.Screen
|
<HomeStack.Screen
|
||||||
name='Playlist'
|
name='Playlist'
|
||||||
component={PlaylistScreen}
|
component={PlaylistScreen}
|
||||||
options={({ route }) => ({
|
options={({ route }) => ({
|
||||||
headerShown: true,
|
headerShown: true,
|
||||||
headerTitleStyle: {
|
headerTitleStyle: {
|
||||||
color: theme.background.val,
|
color: theme.background.val,
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</HomeStack.Group>
|
</HomeStack.Group>
|
||||||
</HomeStack.Navigator>
|
</HomeStack.Navigator>
|
||||||
</HomeProvider>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
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 { NativeStackScreenProps } from '@react-navigation/native-stack'
|
||||||
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||||
import { UseInfiniteQueryResult } from '@tanstack/react-query'
|
import { UseInfiniteQueryResult } from '@tanstack/react-query'
|
||||||
|
import { NavigatorScreenParams } from '@react-navigation/native'
|
||||||
|
|
||||||
type HomeStackParamList = BaseStackParamList & {
|
type HomeStackParamList = BaseStackParamList & {
|
||||||
|
HomeScreen: undefined
|
||||||
|
|
||||||
RecentArtists: {
|
RecentArtists: {
|
||||||
artistsInfiniteQuery: UseInfiniteQueryResult<BaseItemDto[], Error>
|
artistsInfiniteQuery: UseInfiniteQueryResult<BaseItemDto[], Error>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect } from 'react'
|
import React from 'react'
|
||||||
import Library from '../../components/Library/component'
|
import LibraryScreen from '../../components/Library/component'
|
||||||
import { PlaylistScreen } from '../Playlist'
|
import { PlaylistScreen } from '../Playlist'
|
||||||
import AddPlaylist from './add-playlist'
|
import AddPlaylist from './add-playlist'
|
||||||
import DeletePlaylist from './delete-playlist'
|
import DeletePlaylist from './delete-playlist'
|
||||||
@@ -9,121 +9,87 @@ import { LibraryProvider } from '../../providers/Library'
|
|||||||
import { LibrarySortAndFilterProvider } from '../../providers/Library/sorting-filtering'
|
import { LibrarySortAndFilterProvider } from '../../providers/Library/sorting-filtering'
|
||||||
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
||||||
import AlbumScreen from '../Album'
|
import AlbumScreen from '../Album'
|
||||||
import LibraryStackParamList, { LibraryNavigation } from './types'
|
import LibraryStackParamList from './types'
|
||||||
import { LibraryTabProps } from '../Tabs/types'
|
import { LibraryTabProps } from '../Tabs/types'
|
||||||
import { useIsFocused } from '@react-navigation/native'
|
import { LibraryNavigationContext } from './navigation'
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<LibraryStackParamList>()
|
const Stack = createNativeStackNavigator<LibraryStackParamList>()
|
||||||
|
|
||||||
export default function LibraryStack({ route, navigation }: LibraryTabProps): React.JSX.Element {
|
export default function LibraryStack({ route, navigation }: LibraryTabProps): React.JSX.Element {
|
||||||
const theme = useTheme()
|
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 (
|
return (
|
||||||
<LibrarySortAndFilterProvider>
|
<Stack.Navigator initialRouteName='LibraryScreen'>
|
||||||
<LibraryProvider>
|
<Stack.Screen
|
||||||
<Stack.Navigator initialRouteName='Library'>
|
name='LibraryScreen'
|
||||||
<Stack.Screen
|
component={LibraryScreen}
|
||||||
name='Library'
|
options={{
|
||||||
component={Library}
|
title: 'Library',
|
||||||
options={{
|
|
||||||
title: 'Library',
|
|
||||||
|
|
||||||
// I honestly don't think we need a header for this screen, given that there are
|
// 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,
|
// tabs on the top of the screen for navigating the library, but if we want one,
|
||||||
// we can use the title above
|
// we can use the title above
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='Artist'
|
name='Artist'
|
||||||
component={ArtistScreen}
|
component={ArtistScreen}
|
||||||
options={({ route }) => ({
|
options={({ route }) => ({
|
||||||
title: route.params.artist.Name ?? 'Unknown Artist',
|
title: route.params.artist.Name ?? 'Unknown Artist',
|
||||||
headerTitleStyle: {
|
headerTitleStyle: {
|
||||||
color: theme.background.val,
|
color: theme.background.val,
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='Album'
|
name='Album'
|
||||||
component={AlbumScreen}
|
component={AlbumScreen}
|
||||||
options={({ route }) => ({
|
options={({ route }) => ({
|
||||||
headerShown: true,
|
headerShown: true,
|
||||||
title: route.params.album.Name ?? 'Untitled Album',
|
title: route.params.album.Name ?? 'Untitled Album',
|
||||||
headerTitleStyle: {
|
headerTitleStyle: {
|
||||||
color: theme.background.val,
|
color: theme.background.val,
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='Playlist'
|
name='Playlist'
|
||||||
component={PlaylistScreen}
|
component={PlaylistScreen}
|
||||||
options={({ route }) => ({
|
options={({ route }) => ({
|
||||||
headerShown: true,
|
headerShown: true,
|
||||||
title: route.params.playlist.Name ?? 'Untitled Playlist',
|
title: route.params.playlist.Name ?? 'Untitled Playlist',
|
||||||
headerTitleStyle: {
|
headerTitleStyle: {
|
||||||
color: theme.background.val,
|
color: theme.background.val,
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Stack.Group
|
<Stack.Group
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
presentation: 'formSheet',
|
presentation: 'formSheet',
|
||||||
sheetAllowedDetents: 'fitToContents',
|
sheetAllowedDetents: 'fitToContents',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='AddPlaylist'
|
name='AddPlaylist'
|
||||||
component={AddPlaylist}
|
component={AddPlaylist}
|
||||||
options={{
|
options={{
|
||||||
title: 'Add Playlist',
|
title: 'Add Playlist',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='DeletePlaylist'
|
name='DeletePlaylist'
|
||||||
component={DeletePlaylist}
|
component={DeletePlaylist}
|
||||||
options={{
|
options={{
|
||||||
title: 'Delete Playlist',
|
title: 'Delete Playlist',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Stack.Group>
|
</Stack.Group>
|
||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
</LibraryProvider>
|
|
||||||
</LibrarySortAndFilterProvider>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
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 { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||||
import { NativeStackScreenProps } from '@react-navigation/native-stack'
|
import { NativeStackScreenProps } from '@react-navigation/native-stack'
|
||||||
import { BaseStackParamList } from '../types'
|
import { BaseStackParamList } from '../types'
|
||||||
|
import { Queue } from '../../player/types/queue-item'
|
||||||
|
import { NavigatorScreenParams } from '@react-navigation/native'
|
||||||
|
|
||||||
type LibraryStackParamList = BaseStackParamList & {
|
type LibraryStackParamList = BaseStackParamList & {
|
||||||
|
LibraryScreen: NavigatorScreenParams<BaseStackParamList>
|
||||||
AddPlaylist: undefined
|
AddPlaylist: undefined
|
||||||
|
|
||||||
DeletePlaylist: {
|
DeletePlaylist: {
|
||||||
@@ -12,7 +15,7 @@ type LibraryStackParamList = BaseStackParamList & {
|
|||||||
|
|
||||||
export default LibraryStackParamList
|
export default LibraryStackParamList
|
||||||
|
|
||||||
export type LibraryScreenProps = NativeStackScreenProps<LibraryStackParamList, 'Library'>
|
export type LibraryScreenProps = NativeStackScreenProps<LibraryScreenParamList, 'LibraryScreen'>
|
||||||
export type LibraryArtistProps = NativeStackScreenProps<LibraryStackParamList, 'Artist'>
|
export type LibraryArtistProps = NativeStackScreenProps<LibraryStackParamList, 'Artist'>
|
||||||
export type LibraryAlbumProps = NativeStackScreenProps<LibraryStackParamList, 'Album'>
|
export type LibraryAlbumProps = NativeStackScreenProps<LibraryStackParamList, 'Album'>
|
||||||
|
|
||||||
@@ -22,10 +25,6 @@ export type LibraryDeletePlaylistProps = NativeStackScreenProps<
|
|||||||
'DeletePlaylist'
|
'DeletePlaylist'
|
||||||
>
|
>
|
||||||
|
|
||||||
type LibraryNavigation = {
|
type LibraryScreenParamList = {
|
||||||
album?: BaseItemDto
|
LibraryScreen: NavigatorScreenParams<LibraryStackParamList>
|
||||||
artist?: BaseItemDto
|
|
||||||
playlist?: BaseItemDto
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LibraryNavigation: LibraryNavigation = {}
|
|
||||||
|
|||||||
@@ -16,11 +16,12 @@ import Toast from 'react-native-toast-message'
|
|||||||
import { IS_MAESTRO_BUILD } from '../../configs/config'
|
import { IS_MAESTRO_BUILD } from '../../configs/config'
|
||||||
import { AxiosResponse } from 'axios'
|
import { AxiosResponse } from 'axios'
|
||||||
import { AuthenticationResult } from '@jellyfin/sdk/lib/generated-client/models'
|
import { AuthenticationResult } from '@jellyfin/sdk/lib/generated-client/models'
|
||||||
|
import LoginStackParamList from './types'
|
||||||
|
|
||||||
export default function ServerAuthentication({
|
export default function ServerAuthentication({
|
||||||
navigation,
|
navigation,
|
||||||
}: {
|
}: {
|
||||||
navigation: NativeStackNavigationProp<RootStackParamList>
|
navigation: NativeStackNavigationProp<LoginStackParamList>
|
||||||
}): React.JSX.Element {
|
}): React.JSX.Element {
|
||||||
const { api } = useJellifyContext()
|
const { api } = useJellifyContext()
|
||||||
const [username, setUsername] = useState<string | undefined>(undefined)
|
const [username, setUsername] = useState<string | undefined>(undefined)
|
||||||
@@ -116,10 +117,7 @@ export default function ServerAuthentication({
|
|||||||
bordered={0}
|
bordered={0}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
if (navigation.canGoBack()) navigation.goBack()
|
if (navigation.canGoBack()) navigation.goBack()
|
||||||
else
|
else navigation.navigate('ServerAddress', undefined, { pop: true })
|
||||||
navigation.navigate('ServerAddress', undefined, {
|
|
||||||
pop: true,
|
|
||||||
})
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Switch Server
|
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 {
|
export default function Player(): React.JSX.Element {
|
||||||
return (
|
return (
|
||||||
<PlayerStack.Navigator initialRouteName='Player'>
|
<PlayerStack.Navigator initialRouteName='PlayerScreen'>
|
||||||
<PlayerStack.Screen
|
<PlayerStack.Screen
|
||||||
name='Player'
|
name='PlayerScreen'
|
||||||
component={PlayerScreen}
|
component={PlayerScreen}
|
||||||
options={{
|
options={{
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
@@ -20,7 +20,7 @@ export default function Player(): React.JSX.Element {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<PlayerStack.Screen
|
<PlayerStack.Screen
|
||||||
name='Queue'
|
name='QueueScreen'
|
||||||
component={Queue}
|
component={Queue}
|
||||||
options={{
|
options={{
|
||||||
headerTitle: '',
|
headerTitle: '',
|
||||||
@@ -28,7 +28,7 @@ export default function Player(): React.JSX.Element {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<PlayerStack.Screen
|
<PlayerStack.Screen
|
||||||
name='MultipleArtists'
|
name='MultipleArtistsSheet'
|
||||||
component={MultipleArtistsSheet}
|
component={MultipleArtistsSheet}
|
||||||
options={{
|
options={{
|
||||||
presentation: 'formSheet',
|
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 { NativeStackScreenProps } from '@react-navigation/native-stack'
|
||||||
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||||
|
|
||||||
type PlayerParamList = RootStackParamList & {
|
type PlayerParamList = {
|
||||||
Player: undefined
|
PlayerScreen: undefined
|
||||||
Queue: undefined
|
QueueScreen: undefined
|
||||||
|
|
||||||
MultipleArtists: {
|
MultipleArtistsSheet: {
|
||||||
artists: BaseItemDto[]
|
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 LibraryStack from '../Library'
|
||||||
import InternetConnectionWatcher from '../../components/Network/internetConnectionWatcher'
|
import InternetConnectionWatcher from '../../components/Network/internetConnectionWatcher'
|
||||||
import TabParamList from './types'
|
import TabParamList from './types'
|
||||||
|
import { TabProps } from '../types'
|
||||||
|
|
||||||
const Tab = createBottomTabNavigator<TabParamList>()
|
const Tab = createBottomTabNavigator<TabParamList>()
|
||||||
|
|
||||||
export function Tabs(): React.JSX.Element {
|
export default function Tabs({ route, navigation }: TabProps): React.JSX.Element {
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
const nowPlaying = useNowPlayingContext()
|
const nowPlaying = useNowPlayingContext()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tab.Navigator
|
<Tab.Navigator
|
||||||
initialRouteName='Home'
|
initialRouteName={route.params?.screen ?? 'HomeTab'}
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
animation: 'shift',
|
animation: 'shift',
|
||||||
tabBarActiveTintColor: theme.primary.val,
|
tabBarActiveTintColor: theme.primary.val,
|
||||||
@@ -30,7 +31,7 @@ export function Tabs(): React.JSX.Element {
|
|||||||
<>
|
<>
|
||||||
{nowPlaying && (
|
{nowPlaying && (
|
||||||
/* Hide miniplayer if the queue is empty */
|
/* Hide miniplayer if the queue is empty */
|
||||||
<Miniplayer navigation={props.navigation} />
|
<Miniplayer />
|
||||||
)}
|
)}
|
||||||
<InternetConnectionWatcher />
|
<InternetConnectionWatcher />
|
||||||
|
|
||||||
@@ -39,9 +40,10 @@ export function Tabs(): React.JSX.Element {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
name='Home'
|
name='HomeTab'
|
||||||
component={Home}
|
component={Home}
|
||||||
options={{
|
options={{
|
||||||
|
title: 'Home',
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
tabBarIcon: ({ color, size }) => (
|
tabBarIcon: ({ color, size }) => (
|
||||||
<MaterialDesignIcons name='jellyfish-outline' color={color} size={size} />
|
<MaterialDesignIcons name='jellyfish-outline' color={color} size={size} />
|
||||||
@@ -51,21 +53,24 @@ export function Tabs(): React.JSX.Element {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
name='Library'
|
name='LibraryTab'
|
||||||
component={LibraryStack}
|
component={LibraryStack}
|
||||||
options={{
|
options={{
|
||||||
|
title: 'Library',
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
tabBarIcon: ({ color, size }) => (
|
tabBarIcon: ({ color, size }) => (
|
||||||
<MaterialDesignIcons name='music-box-multiple' color={color} size={size} />
|
<MaterialDesignIcons name='music-box-multiple' color={color} size={size} />
|
||||||
),
|
),
|
||||||
tabBarButtonTestID: 'library-tab-button',
|
tabBarButtonTestID: 'library-tab-button',
|
||||||
|
lazy: false, // Load on mount since we need to be able to navigate here from the player
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
name='Search'
|
name='SearchTab'
|
||||||
component={SearchStack}
|
component={SearchStack}
|
||||||
options={{
|
options={{
|
||||||
|
title: 'Search',
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
tabBarIcon: ({ color, size }) => (
|
tabBarIcon: ({ color, size }) => (
|
||||||
<MaterialDesignIcons name='magnify' color={color} size={size} />
|
<MaterialDesignIcons name='magnify' color={color} size={size} />
|
||||||
@@ -75,9 +80,10 @@ export function Tabs(): React.JSX.Element {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
name='Discover'
|
name='DiscoverTab'
|
||||||
component={Discover}
|
component={Discover}
|
||||||
options={{
|
options={{
|
||||||
|
title: 'Discover',
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
tabBarIcon: ({ color, size }) => (
|
tabBarIcon: ({ color, size }) => (
|
||||||
<MaterialDesignIcons name='earth' color={color} size={size} />
|
<MaterialDesignIcons name='earth' color={color} size={size} />
|
||||||
@@ -87,9 +93,10 @@ export function Tabs(): React.JSX.Element {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
name='Settings'
|
name='SettingsTab'
|
||||||
component={SettingsScreen}
|
component={SettingsScreen}
|
||||||
options={{
|
options={{
|
||||||
|
title: 'Settings',
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
tabBarIcon: ({ color, size }) => (
|
tabBarIcon: ({ color, size }) => (
|
||||||
<MaterialDesignIcons name='dip-switch' color={color} size={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'
|
import LibraryStackParamList from '../Library/types'
|
||||||
|
|
||||||
type TabParamList = {
|
type TabParamList = {
|
||||||
Home: undefined
|
HomeTab: undefined
|
||||||
Library: NavigatorScreenParams<LibraryStackParamList>
|
LibraryTab: NavigatorScreenParams<LibraryStackParamList>
|
||||||
Search: undefined
|
SearchTab: undefined
|
||||||
Discover: undefined
|
DiscoverTab: undefined
|
||||||
Settings: undefined
|
SettingsTab: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export type HomeTabProps = BottomTabScreenProps<TabParamList, 'Home'>
|
export type HomeTabProps = BottomTabScreenProps<TabParamList, 'HomeTab'>
|
||||||
export type LibraryTabProps = BottomTabScreenProps<TabParamList, 'Library'>
|
export type LibraryTabProps = BottomTabScreenProps<TabParamList, 'LibraryTab'>
|
||||||
|
|
||||||
export default TabParamList
|
export default TabParamList
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Player from './Player'
|
import Player from './Player'
|
||||||
import { Tabs } from './Tabs'
|
import Tabs from './Tabs'
|
||||||
import { RootStackParamList } from './types'
|
import { RootStackParamList } from './types'
|
||||||
import { getToken, useTheme } from 'tamagui'
|
import { getToken, useTheme } from 'tamagui'
|
||||||
import { useJellifyContext } from '../providers'
|
import { useJellifyContext } from '../providers'
|
||||||
@@ -20,7 +20,7 @@ export default function Root(): React.JSX.Element {
|
|||||||
initialRouteName={api && library ? 'Tabs' : 'Login'}
|
initialRouteName={api && library ? 'Tabs' : 'Login'}
|
||||||
screenOptions={({ route }) => ({
|
screenOptions={({ route }) => ({
|
||||||
navigationBarColor:
|
navigationBarColor:
|
||||||
route.name === 'Player' ? getToken('$black') : theme.background.val,
|
route.name === 'PlayerRoot' ? getToken('$black') : theme.background.val,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<RootStack.Screen
|
<RootStack.Screen
|
||||||
@@ -33,7 +33,7 @@ export default function Root(): React.JSX.Element {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<RootStack.Screen
|
<RootStack.Screen
|
||||||
name='Player'
|
name='PlayerRoot'
|
||||||
component={Player}
|
component={Player}
|
||||||
options={{
|
options={{
|
||||||
presentation: 'modal',
|
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'
|
import { PlayerParamList } from './Player/types'
|
||||||
|
|
||||||
export type BaseStackParamList = {
|
export type BaseStackParamList = {
|
||||||
Home: undefined
|
|
||||||
Library: undefined
|
|
||||||
Search: undefined
|
|
||||||
Discover: undefined
|
|
||||||
Settings: undefined
|
|
||||||
|
|
||||||
Artist: {
|
Artist: {
|
||||||
artist: BaseItemDto
|
artist: BaseItemDto
|
||||||
}
|
}
|
||||||
@@ -52,6 +46,7 @@ export type BaseStackParamList = {
|
|||||||
|
|
||||||
export type ArtistProps = NativeStackScreenProps<BaseStackParamList, 'Artist'>
|
export type ArtistProps = NativeStackScreenProps<BaseStackParamList, 'Artist'>
|
||||||
export type AlbumProps = NativeStackScreenProps<BaseStackParamList, 'Album'>
|
export type AlbumProps = NativeStackScreenProps<BaseStackParamList, 'Album'>
|
||||||
|
export type PlaylistProps = NativeStackNavigationProp<BaseStackParamList, 'Playlist'>
|
||||||
export type TracksProps = NativeStackScreenProps<BaseStackParamList, 'Tracks'>
|
export type TracksProps = NativeStackScreenProps<BaseStackParamList, 'Tracks'>
|
||||||
export type InstantMixProps = NativeStackScreenProps<BaseStackParamList, 'InstantMix'>
|
export type InstantMixProps = NativeStackScreenProps<BaseStackParamList, 'InstantMix'>
|
||||||
|
|
||||||
@@ -59,19 +54,20 @@ export type RootStackParamList = {
|
|||||||
Login: undefined
|
Login: undefined
|
||||||
Tabs: NavigatorScreenParams<TabParamList>
|
Tabs: NavigatorScreenParams<TabParamList>
|
||||||
|
|
||||||
Player: NavigatorScreenParams<PlayerParamList>
|
PlayerRoot: NavigatorScreenParams<PlayerParamList>
|
||||||
|
|
||||||
Context: {
|
Context: {
|
||||||
item: BaseItemDto
|
item: BaseItemDto
|
||||||
navigation?: NativeStackNavigationProp<
|
navigation?: NativeStackNavigationProp<
|
||||||
HomeStackParamList | LibraryStackParamList | DiscoverStackParamList
|
HomeStackParamList | LibraryStackParamList | DiscoverStackParamList
|
||||||
>
|
>
|
||||||
|
navigationCallback?: (screen: 'Album' | 'Artist', item: BaseItemDto) => void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LoginProps = NativeStackNavigationProp<RootStackParamList, 'Login'>
|
export type LoginProps = NativeStackNavigationProp<RootStackParamList, 'Login'>
|
||||||
export type TabProps = NativeStackScreenProps<RootStackParamList, 'Tabs'>
|
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 ContextProps = NativeStackScreenProps<RootStackParamList, 'Context'>
|
||||||
|
|
||||||
export type ArtistsProps = {
|
export type ArtistsProps = {
|
||||||
|
|||||||
Reference in New Issue
Block a user