Tightening up Favorite button functionality

This commit is contained in:
Violet Caulfield
2025-04-22 06:57:07 -05:00
parent 20152fe072
commit 21a260d20c
3 changed files with 124 additions and 62 deletions

View File

@@ -1,18 +1,13 @@
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
import React, { useEffect, useState } from 'react'
import Icon from '../helpers/icon'
import { getUserLibraryApi } from '@jellyfin/sdk/lib/utils/api'
import { useMutation, useQuery } from '@tanstack/react-query'
import { useQuery } from '@tanstack/react-query'
import { isUndefined } from 'lodash'
import { getTokens, Spinner } from 'tamagui'
import Client from '../../../api/client'
import { usePlayerContext } from '../../..//player/provider'
import { queryClient } from '../../../constants/query-client'
import { QueryKeys } from '../../../enums/query-keys'
import { trigger } from 'react-native-haptic-feedback'
import { fetchUserData } from '../../../api/queries/functions/favorites'
import * as Burnt from 'burnt'
import { useJellifyUserDataContext } from '../../../components/user-data-provider'
interface SetFavoriteMutation {
item: BaseItemDto
@@ -25,73 +20,38 @@ export default function FavoriteButton({
item: BaseItemDto
onToggle?: () => void
}): React.JSX.Element {
const { nowPlaying, nowPlayingIsFavorite } = usePlayerContext()
usePlayerContext()
const [isFavorite, setIsFavorite] = useState<boolean>(isFavoriteItem(item))
const [isFavorite, setFavorite] = useState<boolean>(isFavoriteItem(item))
const { data, isFetching, isFetched, refetch } = useQuery({
const { toggleFavorite } = useJellifyUserDataContext()
const { data, isFetching, refetch } = useQuery({
queryKey: [QueryKeys.UserData, item.Id!],
queryFn: () => fetchUserData(item.Id!),
})
const useSetFavorite = useMutation({
mutationFn: async (mutation: SetFavoriteMutation) => {
return getUserLibraryApi(Client.api!).markFavoriteItem({
itemId: mutation.item.Id!,
})
},
onSuccess: () => {
Burnt.alert({
title: `Added favorite`,
duration: 1,
preset: 'heart',
})
trigger('notificationSuccess')
setIsFavorite(true)
onToggle ? onToggle() : {}
// Force refresh of track user data
queryClient.invalidateQueries({ queryKey: [QueryKeys.UserData, item.Id] })
},
})
const useRemoveFavorite = useMutation({
mutationFn: async (mutation: SetFavoriteMutation) => {
return getUserLibraryApi(Client.api!).unmarkFavoriteItem({
itemId: mutation.item.Id!,
})
},
onSuccess: () => {
Burnt.alert({
title: `Removed favorite`,
duration: 1,
preset: 'done',
})
trigger('notificationSuccess')
setIsFavorite(false)
onToggle ? onToggle() : {}
},
})
const toggleFavorite = () => {
if (isFavorite) useRemoveFavorite.mutate({ item })
else useSetFavorite.mutate({ item })
}
useEffect(() => {
refetch()
}, [item])
useEffect(() => {
if (data) setFavorite(data.IsFavorite ?? false)
}, [data])
return isFetching && isUndefined(item.UserData) ? (
<Spinner />
) : (
<Icon
name={data?.IsFavorite ?? isFavorite ? 'heart' : 'heart-outline'}
name={isFavorite ? 'heart' : 'heart-outline'}
color={getTokens().color.telemagenta.val}
onPress={toggleFavorite}
onPress={() =>
toggleFavorite(isFavorite, {
item,
setFavorite,
onToggle,
})
}
/>
)
}

View File

@@ -8,6 +8,7 @@ import { useColorScheme } from 'react-native'
import { PortalProvider } from '@tamagui/portal'
import { JellifyProvider, useJellifyContext } from './provider'
import { ToastProvider } from '@tamagui/toast'
import { JellifyUserDataProvider } from './user-data-provider'
export default function Jellify(): React.JSX.Element {
return (
@@ -26,9 +27,11 @@ function App(): React.JSX.Element {
const { loggedIn } = useJellifyContext()
return loggedIn ? (
<PlayerProvider>
<Navigation />
</PlayerProvider>
<JellifyUserDataProvider>
<PlayerProvider>
<Navigation />
</PlayerProvider>
</JellifyUserDataProvider>
) : (
<JellyfinAuthenticationProvider>
<Login />

View File

@@ -0,0 +1,99 @@
import Client from '../api/client'
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
import { getUserLibraryApi } from '@jellyfin/sdk/lib/utils/api'
import { useMutation } from '@tanstack/react-query'
import { createContext, ReactNode, SetStateAction, useContext } from 'react'
import * as Burnt from 'burnt'
import { trigger } from 'react-native-haptic-feedback'
import { queryClient } from '../constants/query-client'
import { QueryKeys } from '../enums/query-keys'
interface SetFavoriteMutation {
item: BaseItemDto
setFavorite: React.Dispatch<SetStateAction<boolean>>
onToggle?: () => void
}
interface JellifyUserDataContext {
toggleFavorite: (isFavorite: boolean, mutation: SetFavoriteMutation) => void
}
const JellifyUserDataContextInitializer = () => {
const useSetFavorite = useMutation({
mutationFn: async (mutation: SetFavoriteMutation) => {
return getUserLibraryApi(Client.api!).markFavoriteItem({
itemId: mutation.item.Id!,
})
},
onSuccess: ({ data }, { item, setFavorite, onToggle }) => {
Burnt.alert({
title: `Added favorite`,
duration: 1,
preset: 'heart',
})
trigger('notificationSuccess')
setFavorite(true)
if (onToggle) onToggle()
// Force refresh of track user data
queryClient.invalidateQueries({ queryKey: [QueryKeys.UserData, item.Id] })
},
})
const useRemoveFavorite = useMutation({
mutationFn: async (mutation: SetFavoriteMutation) => {
return getUserLibraryApi(Client.api!).unmarkFavoriteItem({
itemId: mutation.item.Id!,
})
},
onSuccess: ({ data }, { item, setFavorite, onToggle }) => {
Burnt.alert({
title: `Removed favorite`,
duration: 1,
preset: 'done',
})
trigger('notificationSuccess')
setFavorite(false)
if (onToggle) onToggle()
// Force refresh of track user data
queryClient.invalidateQueries({ queryKey: [QueryKeys.UserData, item.Id] })
},
})
const toggleFavorite = (isFavorite: boolean, mutation: SetFavoriteMutation) =>
(isFavorite ? useRemoveFavorite : useSetFavorite).mutate(mutation)
return {
toggleFavorite,
}
}
const JellifyUserDataContext = createContext<JellifyUserDataContext>({
toggleFavorite: () => {},
})
export const JellifyUserDataProvider: ({
children,
}: {
children: ReactNode
}) => React.JSX.Element = ({ children }: { children: ReactNode }) => {
const { toggleFavorite } = JellifyUserDataContextInitializer()
return (
<JellifyUserDataContext.Provider
value={{
toggleFavorite,
}}
>
{children}
</JellifyUserDataContext.Provider>
)
}
export const useJellifyUserDataContext = () => useContext(JellifyUserDataContext)