diff --git a/src/elements/emby-playstatebutton/PlayedButton.tsx b/src/elements/emby-playstatebutton/PlayedButton.tsx new file mode 100644 index 000000000..89f4052b1 --- /dev/null +++ b/src/elements/emby-playstatebutton/PlayedButton.tsx @@ -0,0 +1,72 @@ +import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client'; +import React, { FC, useCallback } from 'react'; +import CheckIcon from '@mui/icons-material/Check'; +import { IconButton } from '@mui/material'; +import classNames from 'classnames'; +import globalize from 'scripts/globalize'; +import { useTogglePlayedMutation } from 'hooks/useFetchItems'; + +interface PlayedButtonProps { + className?: string; + isPlayed : boolean | undefined; + itemId: string | null | undefined; + itemType: string | null | undefined +} + +const PlayedButton: FC = ({ + className, + isPlayed = false, + itemId, + itemType +}) => { + const { mutateAsync: togglePlayedMutation } = useTogglePlayedMutation(); + const [playedState, setPlayedState] = React.useState(isPlayed); + + const getTitle = useCallback(() => { + let buttonTitle; + if (itemType !== BaseItemKind.AudioBook) { + buttonTitle = playedState ? globalize.translate('Watched') : globalize.translate('MarkPlayed'); + } else { + buttonTitle = playedState ? globalize.translate('Played') : globalize.translate('MarkPlayed'); + } + + return buttonTitle; + }, [playedState, itemType]); + + const onClick = useCallback(async () => { + try { + if (!itemId) { + throw new Error('Item has no Id'); + } + + const _isPlayed = await togglePlayedMutation({ + itemId, + playedState + }); + setPlayedState(!!_isPlayed); + } catch (e) { + console.error(e); + } + }, [playedState, itemId, togglePlayedMutation]); + + const btnClass = classNames( + className, + { 'playstatebutton-played': playedState } + ); + + const iconClass = classNames( + { 'playstatebutton-icon-played': playedState } + ); + return ( + + + + ); +}; + +export default PlayedButton; diff --git a/src/elements/emby-ratingbutton/FavoriteButton.tsx b/src/elements/emby-ratingbutton/FavoriteButton.tsx new file mode 100644 index 000000000..ebb662d37 --- /dev/null +++ b/src/elements/emby-ratingbutton/FavoriteButton.tsx @@ -0,0 +1,59 @@ +import React, { FC, useCallback } from 'react'; +import FavoriteIcon from '@mui/icons-material/Favorite'; +import { IconButton } from '@mui/material'; +import classNames from 'classnames'; +import { useToggleFavoriteMutation } from 'hooks/useFetchItems'; +import globalize from 'scripts/globalize'; + +interface FavoriteButtonProps { + className?: string; + isFavorite: boolean | undefined; + itemId: string | null | undefined +} + +const FavoriteButton: FC = ({ + className, + isFavorite = false, + itemId +}) => { + const { mutateAsync: toggleFavoriteMutation } = useToggleFavoriteMutation(); + const [favoriteState, setFavoriteState] = React.useState(isFavorite); + + const onClick = useCallback(async () => { + try { + if (!itemId) { + throw new Error('Item has no Id'); + } + + const _isFavorite = await toggleFavoriteMutation({ + itemId, + favoriteState + }); + setFavoriteState(!!_isFavorite); + } catch (e) { + console.error(e); + } + }, [favoriteState, itemId, toggleFavoriteMutation]); + + const btnClass = classNames( + className, + { 'ratingbutton-withrating': favoriteState } + ); + + const iconClass = classNames( + { 'ratingbutton-icon-withrating': favoriteState } + ); + + return ( + + + + ); +}; + +export default FavoriteButton; diff --git a/src/hooks/useFetchItems.ts b/src/hooks/useFetchItems.ts index 07eeca1e3..b3d40a287 100644 --- a/src/hooks/useFetchItems.ts +++ b/src/hooks/useFetchItems.ts @@ -15,6 +15,7 @@ import { getStudiosApi } from '@jellyfin/sdk/lib/utils/api/studios-api'; import { getTvShowsApi } from '@jellyfin/sdk/lib/utils/api/tv-shows-api'; import { getUserLibraryApi } from '@jellyfin/sdk/lib/utils/api/user-library-api'; import { getPlaylistsApi } from '@jellyfin/sdk/lib/utils/api/playlists-api'; +import { getPlaystateApi } from '@jellyfin/sdk/lib/utils/api/playstate-api'; import { useMutation, useQuery } from '@tanstack/react-query'; import datetime from 'scripts/datetime'; import globalize from 'scripts/globalize'; @@ -166,7 +167,7 @@ const fetchGetItemsBySuggestionsType = async ( response = ( await getItemsApi(api).getResumeItems( { - userId: user?.Id, + userId: user.Id, parentId: parentId ?? undefined, fields: [ ItemFields.PrimaryImageAspectRatio, @@ -617,3 +618,75 @@ export const useGetGroupsUpcomingEpisodes = (parentId: ParentId) => { enabled: !!parentId }); }; + +interface ToggleFavoriteMutationProp { + itemId: string; + favoriteState: boolean +} + +const fetchUpdateFavoriteStatus = async ( + currentApi: JellyfinApiContext, + itemId: string, + favoriteState: boolean +) => { + const { api, user } = currentApi; + if (api && user?.Id) { + if (favoriteState) { + const response = await getUserLibraryApi(api).unmarkFavoriteItem({ + userId: user.Id, + itemId: itemId + }); + return response.data.IsFavorite; + } else { + const response = await getUserLibraryApi(api).markFavoriteItem({ + userId: user.Id, + itemId: itemId + }); + return response.data.IsFavorite; + } + } +}; + +export const useToggleFavoriteMutation = () => { + const currentApi = useApi(); + return useMutation({ + mutationFn: ({ itemId, favoriteState }: ToggleFavoriteMutationProp) => + fetchUpdateFavoriteStatus(currentApi, itemId, favoriteState ) + }); +}; + +interface TogglePlayedMutationProp { + itemId: string; + playedState: boolean +} + +const fetchUpdatePlayedState = async ( + currentApi: JellyfinApiContext, + itemId: string, + playedState: boolean +) => { + const { api, user } = currentApi; + if (api && user?.Id) { + if (playedState) { + const response = await getPlaystateApi(api).markUnplayedItem({ + userId: user.Id, + itemId: itemId + }); + return response.data.Played; + } else { + const response = await getPlaystateApi(api).markPlayedItem({ + userId: user.Id, + itemId: itemId + }); + return response.data.Played; + } + } +}; + +export const useTogglePlayedMutation = () => { + const currentApi = useApi(); + return useMutation({ + mutationFn: ({ itemId, playedState }: TogglePlayedMutationProp) => + fetchUpdatePlayedState(currentApi, itemId, playedState ) + }); +};