mirror of
https://github.com/anultravioletaurora/Jellify.git
synced 2025-12-21 13:30:11 -06:00
793 bug cant delete playlist inside jellify (#806)
* Ad ability to long press and delete a playlist also can delete a playlist within the playlist screen itself adds some more fun loading messages and "pull to refresh" text
This commit is contained in:
@@ -17,6 +17,7 @@ const useDiscoverQueries = () => {
|
||||
refetchPublicPlaylists(),
|
||||
refetchArtistSuggestions(),
|
||||
]),
|
||||
networkMode: 'online',
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ const useHomeQueries = () => {
|
||||
await Promise.allSettled([refetchFrequentArtists(), refetchRecentArtists()])
|
||||
return true
|
||||
},
|
||||
networkMode: 'online',
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -20,4 +20,4 @@ export const useDownloadedTrack = (itemId: string | null | undefined) =>
|
||||
useDownloadedTracks([itemId])?.at(0)
|
||||
|
||||
export const useIsDownloaded = (itemIds: (string | null | undefined)[]) =>
|
||||
useDownloadedTracks(itemIds)?.length === itemIds.length
|
||||
useDownloadedTracks(itemIds)?.length === itemIds.length && itemIds.length > 0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RefreshControl } from 'react-native'
|
||||
import RefreshControl from '../Global/components/refresh-control'
|
||||
import { Separator, useTheme, XStack, YStack } from 'tamagui'
|
||||
import React, { RefObject, useEffect, useRef } from 'react'
|
||||
import { Text } from '../Global/helpers/text'
|
||||
@@ -51,8 +51,7 @@ export default function Albums({
|
||||
const refreshControl = (
|
||||
<RefreshControl
|
||||
refreshing={albumsInfiniteQuery.isFetching && !isAlphabetSelectorPending}
|
||||
onRefresh={albumsInfiniteQuery.refetch}
|
||||
tintColor={theme.primary.val}
|
||||
refresh={albumsInfiniteQuery.refetch}
|
||||
/>
|
||||
)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { RefObject, useEffect, useRef } from 'react'
|
||||
import { Separator, useTheme, XStack, YStack } from 'tamagui'
|
||||
import { Text } from '../Global/helpers/text'
|
||||
import { RefreshControl } from 'react-native'
|
||||
import RefreshControl from '../Global/components/refresh-control'
|
||||
import ItemRow from '../Global/components/item-row'
|
||||
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models/base-item-dto'
|
||||
import { FlashList, FlashListRef } from '@shopify/flash-list'
|
||||
@@ -142,8 +142,7 @@ export default function Artists({
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={artistsInfiniteQuery.isPending && !isAlphabetSelectorPending}
|
||||
onRefresh={() => artistsInfiniteQuery.refetch()}
|
||||
tintColor={theme.primary.val}
|
||||
refresh={artistsInfiniteQuery.refetch}
|
||||
/>
|
||||
}
|
||||
renderItem={renderItem}
|
||||
|
||||
35
src/components/Context/components/delete-playlist-row.tsx
Normal file
35
src/components/Context/components/delete-playlist-row.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import navigationRef from '../../../../navigation'
|
||||
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client'
|
||||
import { StackActions, TabActions, useNavigation } from '@react-navigation/native'
|
||||
import { ListItem } from 'tamagui'
|
||||
import Icon from '../../Global/components/icon'
|
||||
import { Text } from '../../Global/helpers/text'
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
|
||||
import LibraryStackParamList from '@/src/screens/Library/types'
|
||||
|
||||
export default function DeletePlaylistRow({
|
||||
playlist,
|
||||
}: {
|
||||
playlist: BaseItemDto
|
||||
}): React.JSX.Element {
|
||||
return (
|
||||
<ListItem
|
||||
backgroundColor={'transparent'}
|
||||
gap={'$2.5'}
|
||||
justifyContent='flex-start'
|
||||
onPress={() => {
|
||||
navigationRef.dispatch(
|
||||
StackActions.push('DeletePlaylist', {
|
||||
playlist,
|
||||
onDelete: navigationRef.goBack,
|
||||
}),
|
||||
)
|
||||
}}
|
||||
pressStyle={{ opacity: 0.5 }}
|
||||
>
|
||||
<Icon small name='delete' color='$danger' />
|
||||
|
||||
<Text bold>Delete Playlist</Text>
|
||||
</ListItem>
|
||||
)
|
||||
}
|
||||
@@ -31,11 +31,8 @@ import { useDeleteDownloads } from '../../api/mutations/download'
|
||||
import useHapticFeedback from '../../hooks/use-haptic-feedback'
|
||||
import { Platform } from 'react-native'
|
||||
import { useApi } from '../../stores'
|
||||
import useAddToPendingDownloads, {
|
||||
useIsDownloading,
|
||||
usePendingDownloads,
|
||||
} from '../../stores/network/downloads'
|
||||
import { networkStatusTypes } from '../Network/internetConnectionWatcher'
|
||||
import useAddToPendingDownloads, { useIsDownloading } from '../../stores/network/downloads'
|
||||
import DeletePlaylistRow from './components/delete-playlist-row'
|
||||
|
||||
type StackNavigation = Pick<NativeStackNavigationProp<BaseStackParamList>, 'navigate' | 'dispatch'>
|
||||
|
||||
@@ -58,8 +55,6 @@ export default function ItemContext({
|
||||
|
||||
const trigger = useHapticFeedback()
|
||||
|
||||
const [networkStatus] = useNetworkStatus()
|
||||
|
||||
const isArtist = item.Type === BaseItemKind.MusicArtist
|
||||
const isAlbum = item.Type === BaseItemKind.MusicAlbum
|
||||
const isTrack = item.Type === BaseItemKind.Audio
|
||||
@@ -95,6 +90,8 @@ export default function ItemContext({
|
||||
|
||||
const renderViewAlbumRow = isAlbum || (isTrack && album)
|
||||
|
||||
const renderDeletePlaylistRow = isPlaylist && item.CanDelete
|
||||
|
||||
const artistIds = !isPlaylist
|
||||
? isArtist
|
||||
? [item.Id]
|
||||
@@ -116,6 +113,8 @@ export default function ItemContext({
|
||||
<YGroup scrollable={Platform.OS === 'android'} marginBottom={'$8'}>
|
||||
<FavoriteContextMenuRow item={item} />
|
||||
|
||||
{renderDeletePlaylistRow && <DeletePlaylistRow playlist={item} />}
|
||||
|
||||
{renderAddToQueueRow && <AddToQueueMenuRow tracks={itemTracks} />}
|
||||
|
||||
{renderAddToQueueRow && <DownloadMenuRow items={itemTracks} />}
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import React from 'react'
|
||||
import { getToken, ScrollView, useTheme, YStack } from 'tamagui'
|
||||
import { getToken, ScrollView, YStack } from 'tamagui'
|
||||
import RecentlyAdded from './helpers/just-added'
|
||||
import { RefreshControl } from 'react-native'
|
||||
import PublicPlaylists from './helpers/public-playlists'
|
||||
import SuggestedArtists from './helpers/suggested-artists'
|
||||
import useDiscoverQueries from '../../api/mutations/discover'
|
||||
import { useIsRestoring } from '@tanstack/react-query'
|
||||
import { useRecentlyAddedAlbums } from '../../api/queries/album'
|
||||
import RefreshControl from '../Global/components/refresh-control'
|
||||
|
||||
export default function Index(): React.JSX.Element {
|
||||
const theme = useTheme()
|
||||
|
||||
const { mutateAsync: refreshAsync, isPending: refreshing } = useDiscoverQueries()
|
||||
|
||||
const isRestoring = useIsRestoring()
|
||||
@@ -28,9 +26,8 @@ export default function Index(): React.JSX.Element {
|
||||
removeClippedSubviews
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refresh={refreshAsync}
|
||||
refreshing={refreshing || isRestoring || loadingInitialData}
|
||||
onRefresh={refreshAsync}
|
||||
tintColor={theme.primary.val}
|
||||
/>
|
||||
}
|
||||
>
|
||||
|
||||
30
src/components/Global/components/refresh-control.tsx
Normal file
30
src/components/Global/components/refresh-control.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useLoadingCaption } from '../../../hooks/use-caption'
|
||||
import { RefreshControl as RNRefreshControl } from 'react-native'
|
||||
import { useTheme } from 'tamagui'
|
||||
|
||||
export default function RefreshControl({
|
||||
refresh,
|
||||
refreshing,
|
||||
}: {
|
||||
refresh: () => void | Promise<unknown>
|
||||
refreshing: boolean
|
||||
}): React.JSX.Element {
|
||||
const theme = useTheme()
|
||||
|
||||
const { data: loadingCaption, refetch } = useLoadingCaption()
|
||||
|
||||
useEffect(() => {
|
||||
if (!refreshing) refetch()
|
||||
}, [refreshing])
|
||||
|
||||
return (
|
||||
<RNRefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={refresh}
|
||||
tintColor={theme.primary.val}
|
||||
title={refreshing ? loadingCaption : 'Pull to refresh'}
|
||||
titleColor={theme.primary.val}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ScrollView, RefreshControl, Platform } from 'react-native'
|
||||
import { ScrollView, Platform } from 'react-native'
|
||||
import { YStack, getToken, useTheme } from 'tamagui'
|
||||
import RecentArtists from './helpers/recent-artists'
|
||||
import RecentlyPlayed from './helpers/recently-played'
|
||||
@@ -9,6 +9,8 @@ import useHomeQueries from '../../api/mutations/home'
|
||||
import { usePerformanceMonitor } from '../../hooks/use-performance-monitor'
|
||||
import { useIsRestoring } from '@tanstack/react-query'
|
||||
import { useRecentlyPlayedTracks } from '../../api/queries/recents'
|
||||
import { useLoadingCaption } from '../../hooks/use-caption'
|
||||
import RefreshControl from '../Global/components/refresh-control'
|
||||
|
||||
const COMPONENT_NAME = 'Home'
|
||||
|
||||
@@ -25,6 +27,8 @@ export function Home(): React.JSX.Element {
|
||||
|
||||
const isRestoring = useIsRestoring()
|
||||
|
||||
const { data: loadingCaption } = useLoadingCaption()
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
contentInsetAdjustmentBehavior='automatic'
|
||||
@@ -34,8 +38,7 @@ export function Home(): React.JSX.Element {
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing || isRestoring || loadingInitialData}
|
||||
onRefresh={refresh}
|
||||
tintColor={theme.primary.val}
|
||||
refresh={refresh}
|
||||
/>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -17,7 +17,7 @@ import { QueuingType } from '../../enums/queuing-type'
|
||||
import { useApi } from '../../stores'
|
||||
import useStreamingDeviceProfile from '../../stores/device-profile'
|
||||
import { useCallback, useEffect, useLayoutEffect, useState } from 'react'
|
||||
import { RefreshControl } from 'react-native'
|
||||
import RefreshControl from '../Global/components/refresh-control'
|
||||
import { updatePlaylist } from '../../../src/api/mutations/playlists'
|
||||
import { usePlaylistTracks } from '../../../src/api/queries/playlist'
|
||||
import useHapticFeedback from '../../hooks/use-haptic-feedback'
|
||||
@@ -140,7 +140,10 @@ export default function Playlist({
|
||||
name='delete-sweep-outline' // otherwise use "delete-circle"
|
||||
onPress={() => {
|
||||
navigationRef.dispatch(
|
||||
StackActions.push('DeletePlaylist', { playlist }),
|
||||
StackActions.push('DeletePlaylist', {
|
||||
playlist,
|
||||
onDelete: navigation.goBack,
|
||||
}),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
@@ -292,13 +295,7 @@ export default function Playlist({
|
||||
return (
|
||||
<ScrollView
|
||||
flex={1}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={isPending}
|
||||
onRefresh={refetch}
|
||||
tintColor={theme.primary.val}
|
||||
/>
|
||||
}
|
||||
refreshControl={<RefreshControl refreshing={isPending} refresh={refetch} />}
|
||||
>
|
||||
<PlaylistTracklistHeader
|
||||
setNewName={setNewName}
|
||||
@@ -334,13 +331,7 @@ export default function Playlist({
|
||||
estimatedItemSize={72}
|
||||
onEndReached={handleEndReached}
|
||||
onEndReachedThreshold={0.5}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={isPending}
|
||||
onRefresh={refetch}
|
||||
tintColor={theme.primary.val}
|
||||
/>
|
||||
}
|
||||
refreshControl={<RefreshControl refreshing={isPending} refresh={refetch} />}
|
||||
ItemSeparatorComponent={() => <Separator />}
|
||||
ListHeaderComponent={
|
||||
<PlaylistTracklistHeader
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useCallback } from 'react'
|
||||
import { RefreshControl } from 'react-native'
|
||||
import { Separator, useTheme } from 'tamagui'
|
||||
import RefreshControl from '../Global/components/refresh-control'
|
||||
import { Separator } from 'tamagui'
|
||||
import { FlashList } from '@shopify/flash-list'
|
||||
import ItemRow from '../Global/components/item-row'
|
||||
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||
@@ -34,8 +34,6 @@ export default function Playlists({
|
||||
isFetchingNextPage,
|
||||
canEdit,
|
||||
}: PlaylistsProps): React.JSX.Element {
|
||||
const theme = useTheme()
|
||||
|
||||
const navigation = useNavigation<NativeStackNavigationProp<BaseStackParamList>>()
|
||||
|
||||
// Memoized key extractor to prevent recreation on each render
|
||||
@@ -62,16 +60,13 @@ export default function Playlists({
|
||||
data={playlists}
|
||||
keyExtractor={keyExtractor}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={isPending || isFetchingNextPage}
|
||||
onRefresh={refetch}
|
||||
tintColor={theme.primary.val}
|
||||
/>
|
||||
<RefreshControl refreshing={isPending || isFetchingNextPage} refresh={refetch} />
|
||||
}
|
||||
ItemSeparatorComponent={ListSeparator}
|
||||
renderItem={renderItem}
|
||||
onEndReached={handleEndReached}
|
||||
removeClippedSubviews
|
||||
onScrollBeginDrag={closeAllSwipeableRows}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,12 +5,9 @@ import { Linking } from 'react-native'
|
||||
import { ScrollView, XStack, YStack } from 'tamagui'
|
||||
import Icon from '../../Global/components/icon'
|
||||
import usePatrons from '../../../api/queries/patrons'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { INFO_CAPTIONS } from '../../../configs/info.config'
|
||||
import { ONE_HOUR } from '../../../constants/query-client'
|
||||
import { pickRandomItemFromArray } from '../../../utils/random'
|
||||
import { getStoredOtaVersion } from 'react-native-nitro-ota'
|
||||
import { downloadUpdate } from '../../OtaUpdates'
|
||||
import { useInfoCaption } from '../../../hooks/use-caption'
|
||||
|
||||
function PatronsList({ patrons }: { patrons: { fullName: string }[] | undefined }) {
|
||||
if (!patrons?.length) return null
|
||||
@@ -31,14 +28,7 @@ function PatronsList({ patrons }: { patrons: { fullName: string }[] | undefined
|
||||
export default function InfoTab() {
|
||||
const patrons = usePatrons()
|
||||
|
||||
const { data: caption } = useQuery({
|
||||
queryKey: ['Info_Caption'],
|
||||
queryFn: () => `${pickRandomItemFromArray(INFO_CAPTIONS)}`,
|
||||
staleTime: ONE_HOUR,
|
||||
initialData: 'Live and in stereo',
|
||||
refetchOnMount: 'always',
|
||||
refetchOnWindowFocus: 'always',
|
||||
})
|
||||
const { data: caption } = useInfoCaption()
|
||||
const otaVersion = getStoredOtaVersion()
|
||||
const otaVersionText = otaVersion ? `OTA Version: ${otaVersion}` : ''
|
||||
return (
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Text } from '../Global/helpers/text'
|
||||
import AZScroller, { useAlphabetSelector } from '../Global/components/alphabetical-selector'
|
||||
import { UseInfiniteQueryResult } from '@tanstack/react-query'
|
||||
import { isString } from 'lodash'
|
||||
import { RefreshControl } from 'react-native'
|
||||
import RefreshControl from '../Global/components/refresh-control'
|
||||
import { closeAllSwipeableRows } from '../Global/components/swipeable-row-registry'
|
||||
import FlashListStickyHeader from '../Global/helpers/flashlist-sticky-header'
|
||||
|
||||
@@ -146,8 +146,7 @@ export default function Tracks({
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={tracksInfiniteQuery.isFetching && !isAlphabetSelectorPending}
|
||||
onRefresh={tracksInfiniteQuery.refetch}
|
||||
tintColor={theme.primary.val}
|
||||
refresh={tracksInfiniteQuery.refetch}
|
||||
/>
|
||||
}
|
||||
onEndReached={() => {
|
||||
|
||||
30
src/hooks/use-caption.ts
Normal file
30
src/hooks/use-caption.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { INFO_CAPTIONS } from '../configs/info.config'
|
||||
import { ONE_HOUR } from '../constants/query-client'
|
||||
import { pickRandomItemFromArray } from '../utils/random'
|
||||
import { LOADING_CAPTIONS } from '../configs/loading.config'
|
||||
|
||||
enum CaptionQueryKeys {
|
||||
InfoCaption,
|
||||
LoadingCaption,
|
||||
}
|
||||
|
||||
export const useInfoCaption = () =>
|
||||
useQuery({
|
||||
queryKey: [CaptionQueryKeys.InfoCaption],
|
||||
queryFn: () => `${pickRandomItemFromArray(INFO_CAPTIONS)}`,
|
||||
staleTime: ONE_HOUR,
|
||||
initialData: 'Live and in stereo',
|
||||
refetchOnMount: 'always',
|
||||
refetchOnWindowFocus: 'always',
|
||||
})
|
||||
|
||||
export const useLoadingCaption = () =>
|
||||
useQuery({
|
||||
queryKey: [CaptionQueryKeys.LoadingCaption],
|
||||
queryFn: () => `${pickRandomItemFromArray(LOADING_CAPTIONS)}`,
|
||||
staleTime: 0,
|
||||
initialData: 'Reticulating splines',
|
||||
refetchOnMount: 'always',
|
||||
refetchOnWindowFocus: 'always',
|
||||
})
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Spinner, View, XStack } from 'tamagui'
|
||||
import { Spinner, XStack, YStack } from 'tamagui'
|
||||
import Button from '../../components/Global/helpers/button'
|
||||
import { Text } from '../../components/Global/helpers/text'
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
@@ -6,15 +6,16 @@ import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||
import { deletePlaylist } from '../../api/mutations/playlists'
|
||||
import { queryClient } from '../../constants/query-client'
|
||||
import Icon from '../../components/Global/components/icon'
|
||||
import { LibraryDeletePlaylistProps } from './types'
|
||||
import { DeletePlaylistProps } from '../types'
|
||||
import useHapticFeedback from '../../hooks/use-haptic-feedback'
|
||||
import { useApi, useJellifyLibrary } from '../../stores'
|
||||
import { UserPlaylistsQueryKey } from '../../api/queries/playlist/keys'
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||
|
||||
export default function DeletePlaylist({
|
||||
navigation,
|
||||
route,
|
||||
}: LibraryDeletePlaylistProps): React.JSX.Element {
|
||||
}: DeletePlaylistProps): React.JSX.Element {
|
||||
const api = useApi()
|
||||
|
||||
const [library] = useJellifyLibrary()
|
||||
@@ -26,8 +27,9 @@ export default function DeletePlaylist({
|
||||
onSuccess: (data: void, playlist: BaseItemDto) => {
|
||||
trigger('notificationSuccess')
|
||||
|
||||
navigation.goBack()
|
||||
navigation.goBack()
|
||||
navigation.goBack() // Dismiss modal
|
||||
|
||||
route.params.onDelete()
|
||||
|
||||
// Refresh favorite playlists component in library
|
||||
queryClient.invalidateQueries({
|
||||
@@ -39,11 +41,13 @@ export default function DeletePlaylist({
|
||||
},
|
||||
})
|
||||
|
||||
const { bottom } = useSafeAreaInsets()
|
||||
|
||||
return (
|
||||
<View margin={'$4'}>
|
||||
<Text bold textAlign='center'>{`Delete playlist ${
|
||||
route.params.playlist.Name ?? 'Untitled Playlist'
|
||||
}?`}</Text>
|
||||
<YStack margin={'$4'} gap={'$4'} justifyContent='space-between' marginBottom={bottom}>
|
||||
<Text bold textAlign='center'>
|
||||
{`Delete playlist ${route.params.playlist.Name ?? 'Untitled Playlist'}?`}
|
||||
</Text>
|
||||
<XStack justifyContent='space-evenly' gap={'$2'}>
|
||||
<Button
|
||||
onPress={() => navigation.goBack()}
|
||||
@@ -77,6 +81,6 @@ export default function DeletePlaylist({
|
||||
)}
|
||||
</Button>
|
||||
</XStack>
|
||||
</View>
|
||||
</YStack>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -86,16 +86,6 @@ export default function LibraryScreen({ route, navigation }: LibraryTabProps): R
|
||||
title: 'Add Playlist',
|
||||
}}
|
||||
/>
|
||||
|
||||
<LibraryStack.Screen
|
||||
name='DeletePlaylist'
|
||||
component={DeletePlaylist}
|
||||
options={{
|
||||
title: 'Delete Playlist',
|
||||
headerShown: false,
|
||||
sheetGrabberVisible: true,
|
||||
}}
|
||||
/>
|
||||
</LibraryStack.Group>
|
||||
</LibraryStack.Navigator>
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ import { BaseStackParamList } from '../types'
|
||||
import { NavigatorScreenParams } from '@react-navigation/native'
|
||||
|
||||
type LibraryStackParamList = BaseStackParamList & {
|
||||
LibraryScreen: NavigatorScreenParams<BaseStackParamList>
|
||||
LibraryScreen: NavigatorScreenParams<BaseStackParamList> | undefined
|
||||
AddPlaylist: undefined
|
||||
|
||||
DeletePlaylist: {
|
||||
@@ -14,7 +14,7 @@ type LibraryStackParamList = BaseStackParamList & {
|
||||
|
||||
export default LibraryStackParamList
|
||||
|
||||
export type LibraryScreenProps = NativeStackScreenProps<LibraryScreenParamList, 'LibraryScreen'>
|
||||
export type LibraryScreenProps = NativeStackScreenProps<LibraryStackParamList, 'LibraryScreen'>
|
||||
export type LibraryArtistProps = NativeStackScreenProps<LibraryStackParamList, 'Artist'>
|
||||
export type LibraryAlbumProps = NativeStackScreenProps<LibraryStackParamList, 'Album'>
|
||||
|
||||
@@ -23,7 +23,3 @@ export type LibraryDeletePlaylistProps = NativeStackScreenProps<
|
||||
LibraryStackParamList,
|
||||
'DeletePlaylist'
|
||||
>
|
||||
|
||||
type LibraryScreenParamList = {
|
||||
LibraryScreen: NavigatorScreenParams<LibraryStackParamList>
|
||||
}
|
||||
|
||||
2
src/screens/Tabs/types.d.ts
vendored
2
src/screens/Tabs/types.d.ts
vendored
@@ -4,7 +4,7 @@ import LibraryStackParamList from '../Library/types'
|
||||
|
||||
type TabParamList = {
|
||||
HomeTab: undefined
|
||||
LibraryTab: NavigatorScreenParams<LibraryStackParamList>
|
||||
LibraryTab: undefined | NavigatorScreenParams<LibraryStackParamList>
|
||||
SearchTab: undefined
|
||||
DiscoverTab: undefined
|
||||
SettingsTab: undefined
|
||||
|
||||
@@ -13,6 +13,7 @@ import { Text } from '../components/Global/helpers/text'
|
||||
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||
import AudioSpecsSheet from './Stats'
|
||||
import { useApi, useJellifyLibrary } from '../stores'
|
||||
import DeletePlaylist from './Library/delete-playlist'
|
||||
|
||||
const RootStack = createNativeStackNavigator<RootStackParamList>()
|
||||
|
||||
@@ -82,6 +83,18 @@ export default function Root(): React.JSX.Element {
|
||||
sheetGrabberVisible: true,
|
||||
})}
|
||||
/>
|
||||
|
||||
<RootStack.Screen
|
||||
name='DeletePlaylist'
|
||||
component={DeletePlaylist}
|
||||
options={{
|
||||
title: 'Delete Playlist',
|
||||
presentation: 'formSheet',
|
||||
headerShown: false,
|
||||
sheetGrabberVisible: true,
|
||||
sheetAllowedDetents: 'fitToContents',
|
||||
}}
|
||||
/>
|
||||
</RootStack.Navigator>
|
||||
)
|
||||
}
|
||||
|
||||
7
src/screens/types.d.ts
vendored
7
src/screens/types.d.ts
vendored
@@ -72,6 +72,11 @@ export type RootStackParamList = {
|
||||
streamingMediaSourceInfo?: MediaSourceInfo
|
||||
downloadedMediaSourceInfo?: MediaSourceInfo
|
||||
}
|
||||
|
||||
DeletePlaylist: {
|
||||
playlist: BaseItemDto
|
||||
onDelete: () => void
|
||||
}
|
||||
}
|
||||
|
||||
export type LoginProps = NativeStackNavigationProp<RootStackParamList, 'Login'>
|
||||
@@ -81,6 +86,8 @@ export type ContextProps = NativeStackScreenProps<RootStackParamList, 'Context'>
|
||||
export type AddToPlaylistProps = NativeStackScreenProps<RootStackParamList, 'AddToPlaylist'>
|
||||
export type AudioSpecsProps = NativeStackScreenProps<RootStackParamList, 'AudioSpecs'>
|
||||
|
||||
export type DeletePlaylistProps = NativeStackScreenProps<RootStackParamList, 'DeletePlaylist'>
|
||||
|
||||
export type GenresProps = {
|
||||
genres: InfiniteData<BaseItemDto[], unknown> | undefined
|
||||
fetchNextPage: (options?: FetchNextPageOptions | undefined) => void
|
||||
|
||||
@@ -69,7 +69,11 @@ export const useIsDownloading = (items: BaseItemDto[]) => {
|
||||
|
||||
const itemIds = items.map((item) => item.Id)
|
||||
|
||||
return itemIds.filter((id) => downloadQueue.has(id)).length === items.length
|
||||
return (
|
||||
itemIds.length !== 0 &&
|
||||
downloadQueue.values.length !== 0 &&
|
||||
itemIds.filter((id) => downloadQueue.has(id)).length === items.length
|
||||
)
|
||||
}
|
||||
|
||||
export const useAddToCompletedDownloads = () => {
|
||||
|
||||
Reference in New Issue
Block a user