mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Update favorite and played state to use Query Invalidation
This commit is contained in:
parent
97472ac8bb
commit
31a77c25f3
5 changed files with 79 additions and 59 deletions
|
@ -18,7 +18,7 @@ const ProgramsSectionView: FC<ProgramsSectionViewProps> = ({
|
|||
sectionType,
|
||||
isUpcomingRecordingsEnabled = false
|
||||
}) => {
|
||||
const { isLoading, data: sectionsWithItems } = useGetProgramsSectionsWithItems(parentId, sectionType);
|
||||
const { isLoading, data: sectionsWithItems, refetch } = useGetProgramsSectionsWithItems(parentId, sectionType);
|
||||
const {
|
||||
isLoading: isUpcomingRecordingsLoading,
|
||||
data: upcomingRecordings
|
||||
|
@ -60,8 +60,10 @@ const ProgramsSectionView: FC<ProgramsSectionViewProps> = ({
|
|||
sectionTitle={globalize.translate(section.name)}
|
||||
items={items ?? []}
|
||||
url={getRouteUrl(section)}
|
||||
reloadItems={refetch}
|
||||
cardOptions={{
|
||||
...section.cardOptions
|
||||
...section.cardOptions,
|
||||
queryKey: ['ProgramSectionWithItems']
|
||||
}}
|
||||
/>
|
||||
|
||||
|
@ -73,6 +75,7 @@ const ProgramsSectionView: FC<ProgramsSectionViewProps> = ({
|
|||
sectionTitle={group.name}
|
||||
items={group.timerInfo ?? []}
|
||||
cardOptions={{
|
||||
queryKey: ['Timers'],
|
||||
shape: 'overflowBackdrop',
|
||||
showTitle: true,
|
||||
showParentTitleOrTitle: true,
|
||||
|
|
|
@ -102,6 +102,7 @@ const SuggestionsSectionView: FC<SuggestionsSectionViewProps> = ({
|
|||
url={getRouteUrl(section)}
|
||||
cardOptions={{
|
||||
...section.cardOptions,
|
||||
queryKey: ['SuggestionSectionWithItems'],
|
||||
showTitle: true,
|
||||
centerText: true,
|
||||
cardLayout: false,
|
||||
|
@ -117,6 +118,7 @@ const SuggestionsSectionView: FC<SuggestionsSectionViewProps> = ({
|
|||
sectionTitle={getRecommendationTittle(recommendation)}
|
||||
items={recommendation.Items ?? []}
|
||||
cardOptions={{
|
||||
queryKey: ['MovieRecommendations'],
|
||||
shape: 'overflowPortrait',
|
||||
showYear: true,
|
||||
scalable: true,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import React, { FC, useCallback } from 'react';
|
||||
import CheckIcon from '@mui/icons-material/Check';
|
||||
import { IconButton } from '@mui/material';
|
||||
|
@ -10,28 +11,30 @@ interface PlayedButtonProps {
|
|||
className?: string;
|
||||
isPlayed : boolean | undefined;
|
||||
itemId: string | null | undefined;
|
||||
itemType: string | null | undefined
|
||||
itemType: string | null | undefined,
|
||||
queryKey?: string[]
|
||||
}
|
||||
|
||||
const PlayedButton: FC<PlayedButtonProps> = ({
|
||||
className,
|
||||
isPlayed = false,
|
||||
itemId,
|
||||
itemType
|
||||
itemType,
|
||||
queryKey
|
||||
}) => {
|
||||
const queryClient = useQueryClient();
|
||||
const { mutateAsync: togglePlayedMutation } = useTogglePlayedMutation();
|
||||
const [playedState, setPlayedState] = React.useState<boolean>(isPlayed);
|
||||
|
||||
const getTitle = useCallback(() => {
|
||||
let buttonTitle;
|
||||
if (itemType !== BaseItemKind.AudioBook) {
|
||||
buttonTitle = playedState ? globalize.translate('Watched') : globalize.translate('MarkPlayed');
|
||||
buttonTitle = isPlayed ? globalize.translate('Watched') : globalize.translate('MarkPlayed');
|
||||
} else {
|
||||
buttonTitle = playedState ? globalize.translate('Played') : globalize.translate('MarkPlayed');
|
||||
buttonTitle = isPlayed ? globalize.translate('Played') : globalize.translate('MarkPlayed');
|
||||
}
|
||||
|
||||
return buttonTitle;
|
||||
}, [playedState, itemType]);
|
||||
}, [itemType, isPlayed]);
|
||||
|
||||
const onClick = useCallback(async () => {
|
||||
try {
|
||||
|
@ -39,23 +42,29 @@ const PlayedButton: FC<PlayedButtonProps> = ({
|
|||
throw new Error('Item has no Id');
|
||||
}
|
||||
|
||||
const _isPlayed = await togglePlayedMutation({
|
||||
await togglePlayedMutation({
|
||||
itemId,
|
||||
playedState
|
||||
isPlayed
|
||||
},
|
||||
{ onSuccess: async() => {
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: queryKey,
|
||||
type: 'all',
|
||||
refetchType: 'active'
|
||||
});
|
||||
setPlayedState(!!_isPlayed);
|
||||
} });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}, [playedState, itemId, togglePlayedMutation]);
|
||||
}, [itemId, togglePlayedMutation, isPlayed, queryClient, queryKey]);
|
||||
|
||||
const btnClass = classNames(
|
||||
className,
|
||||
{ 'playstatebutton-played': playedState }
|
||||
{ 'playstatebutton-played': isPlayed }
|
||||
);
|
||||
|
||||
const iconClass = classNames(
|
||||
{ 'playstatebutton-icon-played': playedState }
|
||||
{ 'playstatebutton-icon-played': isPlayed }
|
||||
);
|
||||
return (
|
||||
<IconButton
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import FavoriteIcon from '@mui/icons-material/Favorite';
|
||||
import { IconButton } from '@mui/material';
|
||||
import classNames from 'classnames';
|
||||
|
@ -8,16 +9,18 @@ import globalize from 'scripts/globalize';
|
|||
interface FavoriteButtonProps {
|
||||
className?: string;
|
||||
isFavorite: boolean | undefined;
|
||||
itemId: string | null | undefined
|
||||
itemId: string | null | undefined;
|
||||
queryKey?: string[]
|
||||
}
|
||||
|
||||
const FavoriteButton: FC<FavoriteButtonProps> = ({
|
||||
className,
|
||||
isFavorite = false,
|
||||
itemId
|
||||
itemId,
|
||||
queryKey
|
||||
}) => {
|
||||
const queryClient = useQueryClient();
|
||||
const { mutateAsync: toggleFavoriteMutation } = useToggleFavoriteMutation();
|
||||
const [favoriteState, setFavoriteState] = React.useState<boolean>(isFavorite);
|
||||
|
||||
const onClick = useCallback(async () => {
|
||||
try {
|
||||
|
@ -25,28 +28,34 @@ const FavoriteButton: FC<FavoriteButtonProps> = ({
|
|||
throw new Error('Item has no Id');
|
||||
}
|
||||
|
||||
const _isFavorite = await toggleFavoriteMutation({
|
||||
await toggleFavoriteMutation({
|
||||
itemId,
|
||||
favoriteState
|
||||
isFavorite
|
||||
},
|
||||
{ onSuccess: async() => {
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: queryKey,
|
||||
type: 'all',
|
||||
refetchType: 'active'
|
||||
});
|
||||
setFavoriteState(!!_isFavorite);
|
||||
} });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}, [favoriteState, itemId, toggleFavoriteMutation]);
|
||||
}, [isFavorite, itemId, queryClient, queryKey, toggleFavoriteMutation]);
|
||||
|
||||
const btnClass = classNames(
|
||||
className,
|
||||
{ 'ratingbutton-withrating': favoriteState }
|
||||
{ 'ratingbutton-withrating': isFavorite }
|
||||
);
|
||||
|
||||
const iconClass = classNames(
|
||||
{ 'ratingbutton-icon-withrating': favoriteState }
|
||||
{ 'ratingbutton-icon-withrating': isFavorite }
|
||||
);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
title={favoriteState ? globalize.translate('Favorite') : globalize.translate('AddToFavorites')}
|
||||
title={isFavorite ? globalize.translate('Favorite') : globalize.translate('AddToFavorites')}
|
||||
className={btnClass}
|
||||
size='small'
|
||||
onClick={onClick}
|
||||
|
|
|
@ -376,10 +376,12 @@ export const useGetItemsViewByType = (
|
|||
return useQuery({
|
||||
queryKey: [
|
||||
'ItemsViewByType',
|
||||
{
|
||||
viewType,
|
||||
parentId,
|
||||
itemType,
|
||||
libraryViewSettings
|
||||
}
|
||||
],
|
||||
queryFn: ({ signal }) =>
|
||||
fetchGetItemsViewByType(
|
||||
|
@ -526,17 +528,17 @@ export const useGetGroupsUpcomingEpisodes = (parentId: ParentId) => {
|
|||
|
||||
interface ToggleFavoriteMutationProp {
|
||||
itemId: string;
|
||||
favoriteState: boolean
|
||||
isFavorite: boolean
|
||||
}
|
||||
|
||||
const fetchUpdateFavoriteStatus = async (
|
||||
currentApi: JellyfinApiContext,
|
||||
itemId: string,
|
||||
favoriteState: boolean
|
||||
isFavorite: boolean
|
||||
) => {
|
||||
const { api, user } = currentApi;
|
||||
if (api && user?.Id) {
|
||||
if (favoriteState) {
|
||||
if (isFavorite) {
|
||||
const response = await getUserLibraryApi(api).unmarkFavoriteItem({
|
||||
userId: user.Id,
|
||||
itemId: itemId
|
||||
|
@ -555,24 +557,24 @@ const fetchUpdateFavoriteStatus = async (
|
|||
export const useToggleFavoriteMutation = () => {
|
||||
const currentApi = useApi();
|
||||
return useMutation({
|
||||
mutationFn: ({ itemId, favoriteState }: ToggleFavoriteMutationProp) =>
|
||||
fetchUpdateFavoriteStatus(currentApi, itemId, favoriteState )
|
||||
mutationFn: ({ itemId, isFavorite }: ToggleFavoriteMutationProp) =>
|
||||
fetchUpdateFavoriteStatus(currentApi, itemId, isFavorite )
|
||||
});
|
||||
};
|
||||
|
||||
interface TogglePlayedMutationProp {
|
||||
itemId: string;
|
||||
playedState: boolean
|
||||
isPlayed: boolean
|
||||
}
|
||||
|
||||
const fetchUpdatePlayedState = async (
|
||||
currentApi: JellyfinApiContext,
|
||||
itemId: string,
|
||||
playedState: boolean
|
||||
isPlayed: boolean
|
||||
) => {
|
||||
const { api, user } = currentApi;
|
||||
if (api && user?.Id) {
|
||||
if (playedState) {
|
||||
if (isPlayed) {
|
||||
const response = await getPlaystateApi(api).markUnplayedItem({
|
||||
userId: user.Id,
|
||||
itemId: itemId
|
||||
|
@ -591,8 +593,8 @@ const fetchUpdatePlayedState = async (
|
|||
export const useTogglePlayedMutation = () => {
|
||||
const currentApi = useApi();
|
||||
return useMutation({
|
||||
mutationFn: ({ itemId, playedState }: TogglePlayedMutationProp) =>
|
||||
fetchUpdatePlayedState(currentApi, itemId, playedState )
|
||||
mutationFn: ({ itemId, isPlayed }: TogglePlayedMutationProp) =>
|
||||
fetchUpdatePlayedState(currentApi, itemId, isPlayed )
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -676,7 +678,7 @@ const fetchGetTimers = async (
|
|||
export const useGetTimers = (isUpcomingRecordingsEnabled: boolean, indexByDate?: boolean) => {
|
||||
const currentApi = useApi();
|
||||
return useQuery({
|
||||
queryKey: ['Timers', isUpcomingRecordingsEnabled, indexByDate],
|
||||
queryKey: ['Timers', { isUpcomingRecordingsEnabled, indexByDate }],
|
||||
queryFn: ({ signal }) =>
|
||||
isUpcomingRecordingsEnabled ? fetchGetTimers(currentApi, indexByDate, { signal }) : []
|
||||
});
|
||||
|
@ -830,7 +832,7 @@ const fetchGetSectionItems = async (
|
|||
],
|
||||
parentId: parentId ?? undefined,
|
||||
imageTypeLimit: 1,
|
||||
enableImageTypes: [ImageType.Primary],
|
||||
enableImageTypes: [ImageType.Primary, ImageType.Thumb],
|
||||
...section.parametersOptions
|
||||
},
|
||||
{
|
||||
|
@ -882,7 +884,6 @@ const getSectionsWithItems = async (
|
|||
const updatedSectionWithItems: SectionWithItems[] = [];
|
||||
|
||||
for (const section of sections) {
|
||||
try {
|
||||
const items = await fetchGetSectionItems(
|
||||
currentApi, parentId, section, options
|
||||
);
|
||||
|
@ -893,9 +894,6 @@ const getSectionsWithItems = async (
|
|||
items
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error occurred for section ${section.type}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
return updatedSectionWithItems;
|
||||
|
@ -908,7 +906,7 @@ export const useGetSuggestionSectionsWithItems = (
|
|||
const currentApi = useApi();
|
||||
const sections = getSuggestionSections();
|
||||
return useQuery({
|
||||
queryKey: ['SuggestionSectionWithItems', suggestionSectionType],
|
||||
queryKey: ['SuggestionSectionWithItems', { suggestionSectionType }],
|
||||
queryFn: ({ signal }) =>
|
||||
getSectionsWithItems(currentApi, parentId, sections, suggestionSectionType, { signal }),
|
||||
enabled: !!parentId
|
||||
|
@ -922,9 +920,8 @@ export const useGetProgramsSectionsWithItems = (
|
|||
const currentApi = useApi();
|
||||
const sections = getProgramSections();
|
||||
return useQuery({
|
||||
queryKey: ['ProgramSectionWithItems', programSectionType],
|
||||
queryFn: ({ signal }) =>
|
||||
getSectionsWithItems(currentApi, parentId, sections, programSectionType, { signal })
|
||||
queryKey: ['ProgramSectionWithItems', { programSectionType }],
|
||||
queryFn: ({ signal }) => getSectionsWithItems(currentApi, parentId, sections, programSectionType, { signal })
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue