From afb1c2d7b832d5eff0385584037c501d1b49e0ad Mon Sep 17 00:00:00 2001 From: Royce Chua Date: Mon, 21 Jun 2021 21:22:45 +0800 Subject: [PATCH] Add movie rating to movie screens --- src/features/home/HomeScreen.tsx | 16 ++++- src/features/movies/MovieDetailScreen.tsx | 81 ++++++++++++++++++++--- 2 files changed, 87 insertions(+), 10 deletions(-) diff --git a/src/features/home/HomeScreen.tsx b/src/features/home/HomeScreen.tsx index ab2510e..fe1c2d2 100644 --- a/src/features/home/HomeScreen.tsx +++ b/src/features/home/HomeScreen.tsx @@ -29,6 +29,7 @@ import theme from '../../themes/themes'; import moment from 'moment'; import Res from '../../themes/Res'; import { AirbnbRating } from 'react-native-ratings'; +import { setUserMovieRatings } from '../movies/moviesSlice'; // Props type HomeScreenNavigationProp = StackNavigationProp< @@ -47,10 +48,21 @@ const HomeScreen: React.FC = (props: Props) => { const dispatch = useAppDispatch(); const { data } = useAppSelector((state) => state.user); - // search feature + // search feature states const [searchText, setSearchText] = useState(''); const handleClearSearchText = () => setSearchText(''); + // retrieve data + const getUserRatingsQuery = useQuery( + 'getUserRatings', + () => API.getRatings({ account_id: data.id, session_id: data.sessionId }), + { keepPreviousData: true, staleTime: 5000 } + ); + + useEffect(() => { + dispatch(setUserMovieRatings(getUserRatingsQuery.data?.data.results)) + }, [getUserRatingsQuery.isLoading]) + // retrieve data const trendingMoviesQuery = useQuery( 'getTrendingMovies', @@ -173,7 +185,7 @@ const HomeScreen: React.FC = (props: Props) => { count={5} // showRating defaultRating={ - item.vote_average - 5 + item.vote_average/2 } size={20} isDisabled diff --git a/src/features/movies/MovieDetailScreen.tsx b/src/features/movies/MovieDetailScreen.tsx index 38299d6..f74a993 100644 --- a/src/features/movies/MovieDetailScreen.tsx +++ b/src/features/movies/MovieDetailScreen.tsx @@ -1,6 +1,6 @@ import { StackNavigationProp } from '@react-navigation/stack'; import moment from 'moment'; -import React, { useLayoutEffect } from 'react'; +import React, { useState, useEffect, useLayoutEffect } from 'react'; import { StyleSheet, Text, @@ -14,10 +14,11 @@ import { AirbnbRating, Rating } from 'react-native-ratings'; import { useMutation, useQueryClient } from 'react-query'; import Spacer from '../../common/components/Spacer'; import { RootStackParamsList } from '../../navigation/MainNavigationContainer'; -import { useAppSelector } from '../../redux/hooks'; +import { useAppDispatch, useAppSelector } from '../../redux/hooks'; import API from '../../services/API'; import Res from '../../themes/Res'; import theme from '../../themes/themes'; +import { getMovieRatings } from './moviesSlice'; // Props type MovieDetailScreenNavigationProp = StackNavigationProp< @@ -37,12 +38,26 @@ const MovieDetailScreen: React.FC = (props: Props) => { const { navigation, route } = props; const { movie, type } = route.params; + // Access the react query client + const queryClient = useQueryClient(); + + const dispatch = useAppDispatch(); // retrieve user data const { data } = useAppSelector((state) => state.user); + const { ratings } = useAppSelector((state) => state.movies); - // Access the react query client - const queryClient = useQueryClient(); + // rating state + const [rating, setRating] = useState(0); + + // assign this movie an existing rating from the user if any record was found + useEffect(() => { + const existingRating = ratings.find((entry) => entry.id == movie.id); + if (existingRating) { + setRating(existingRating.rating); + } + }, [ratings]); + // invoke a dynamic header title depending on the movie title useLayoutEffect(() => { navigation.setOptions({ headerTitle: movie.original_title, @@ -72,8 +87,37 @@ const MovieDetailScreen: React.FC = (props: Props) => { }, }); + const addRatingMutation = useMutation(API.addRating, { + onSuccess: () => { + alert('Rating saved'); + // Invalidate and refetch + queryClient.invalidateQueries('getUserRatings'); + dispatch(getMovieRatings()); + }, + onError: (error: Error) => { + alert( + `Error encountered while adding movie rating. '${error.message}'` + ); + }, + }); + + const deleteRatingMutation = useMutation(API.deleteRating, { + onSuccess: () => { + alert('Movie rating deleted'); + setRating(0); + // Invalidate and refetch + queryClient.invalidateQueries('getUserRatings'); + dispatch(getMovieRatings()); + }, + onError: (error: Error) => { + alert( + `Error encountered while deleting movie rating. '${error.message}'` + ); + }, + }); + const handleAddToWatchList = () => { - addToWatchlistMutation.mutate({ + addToWatchlistMutation.mutateAsync({ account_id: data.id, session_id: data.sessionId, media_type: 'movie', @@ -82,6 +126,22 @@ const MovieDetailScreen: React.FC = (props: Props) => { }); }; + const handleOnFinishRating = (rating : number) => { + setRating(rating); + addRatingMutation.mutateAsync({ + media_id: movie.id, + session_id: data.sessionId, + value: rating, + }) + } + + const handleRemoveRating = () => { + deleteRatingMutation.mutateAsync({ + media_id: movie.id, + session_id: data.sessionId, + }); + } + return ( @@ -113,7 +173,7 @@ const MovieDetailScreen: React.FC = (props: Props) => { showRating={false} count={5} // showRating - defaultRating={movie.vote_average - 5} + defaultRating={movie.vote_average / 2} size={20} isDisabled /> @@ -141,10 +201,15 @@ const MovieDetailScreen: React.FC = (props: Props) => { showRating={false} count={5} // showRating - defaultRating={0} + defaultRating={rating} size={20} - onFinishRating={(rating) => {}} + onFinishRating={handleOnFinishRating} /> + { + rating + ? + : null + }