From e0337957225e2f562083ca3e106fcf9fcc42395d Mon Sep 17 00:00:00 2001 From: Ally Descoteaux <128718372+adescoteaux1@users.noreply.github.com> Date: Tue, 3 Dec 2024 20:41:38 -0500 Subject: [PATCH] Fix review media page (#218) Co-authored-by: adescoteaux1 --- .../handler/reviews/get_media_reviews.go | 46 +++++++++++++++ backend/internal/service/server.go | 6 ++ frontend/app/MediaPage.tsx | 42 ++++++++++--- frontend/app/MediaReviewsPage.tsx | 59 ++++++++++++++----- frontend/app/ReviewPage.tsx | 22 +++---- frontend/assets/images/Ratings/Property0.svg | 29 +++++++++ frontend/assets/images/Ratings/Property1.svg | 29 +++++++++ frontend/assets/images/Ratings/Property10.svg | 29 +++++++++ frontend/assets/images/Ratings/Property2.svg | 29 +++++++++ frontend/assets/images/Ratings/Property3.svg | 29 +++++++++ frontend/assets/images/Ratings/Property4.svg | 29 +++++++++ frontend/assets/images/Ratings/Property5.svg | 29 +++++++++ frontend/assets/images/Ratings/Property6.svg | 29 +++++++++ frontend/assets/images/Ratings/Property7.svg | 29 +++++++++ frontend/assets/images/Ratings/Property8.svg | 29 +++++++++ frontend/assets/images/Ratings/Property9.svg | 29 +++++++++ frontend/components/ReviewPreview.tsx | 52 ++++++++-------- frontend/components/media/RatingSvg.tsx | 22 +++---- frontend/components/media/ReviewStats.tsx | 6 +- frontend/components/search/Filter.tsx | 12 +++- 20 files changed, 512 insertions(+), 74 deletions(-) create mode 100644 frontend/assets/images/Ratings/Property0.svg create mode 100644 frontend/assets/images/Ratings/Property1.svg create mode 100644 frontend/assets/images/Ratings/Property10.svg create mode 100644 frontend/assets/images/Ratings/Property2.svg create mode 100644 frontend/assets/images/Ratings/Property3.svg create mode 100644 frontend/assets/images/Ratings/Property4.svg create mode 100644 frontend/assets/images/Ratings/Property5.svg create mode 100644 frontend/assets/images/Ratings/Property6.svg create mode 100644 frontend/assets/images/Ratings/Property7.svg create mode 100644 frontend/assets/images/Ratings/Property8.svg create mode 100644 frontend/assets/images/Ratings/Property9.svg diff --git a/backend/internal/service/handler/reviews/get_media_reviews.go b/backend/internal/service/handler/reviews/get_media_reviews.go index 1fe05525..a8931739 100644 --- a/backend/internal/service/handler/reviews/get_media_reviews.go +++ b/backend/internal/service/handler/reviews/get_media_reviews.go @@ -3,6 +3,7 @@ package reviews import ( "fmt" "platnm/internal/models" + "sort" "github.com/gofiber/fiber/v2" ) @@ -43,6 +44,51 @@ func (h *Handler) GetReviewsByMediaId(c *fiber.Ctx, mediaType string) error { return c.Status(fiber.StatusOK).JSON(response) } +func (h *Handler) GetTopReviewsByMediaId(c *fiber.Ctx, mediaType string) error { + var id = c.Params("id") + + // Even though we are paginating the reviews we need to get all the reviews in order to calculate average rating + // Fetch the review based on ID and media type + reviews, err := h.reviewRepository.GetReviewsByMediaID(c.Context(), id, mediaType) + if err != nil { + // If error, log it and return 500 + fmt.Println(err.Error(), "from transactions err ") + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Unable to retrieve reviews", + }) + } + + var scores []float64 + + for _, r := range reviews { + rating := float64(r.Rating) + scores = append(scores, rating) + } + + var rating = getAve(scores) + + // Calculate upvote-downvote scores for sorting + sort.Slice(reviews, func(i, j int) bool { + scoreI := reviews[i].ReviewStat.Upvotes + scoreJ := reviews[j].ReviewStat.Upvotes + return scoreI > scoreJ // Sort descending by score + }) + + // Take the top 10 reviews + topReviews := reviews + // if len(reviews) > 5 { + // topReviews = reviews[:5] + // } + + response := Response{ + AvgRating: rating, + TotalCount: len(reviews), + Reviews: topReviews, + } + + return c.Status(fiber.StatusOK).JSON(response) +} + func paginate(reviews []*models.Preview, limit int, offset int) []*models.Preview { var start = offset * limit var end = (offset * limit) + limit diff --git a/backend/internal/service/server.go b/backend/internal/service/server.go index b8297e61..4e0fa789 100644 --- a/backend/internal/service/server.go +++ b/backend/internal/service/server.go @@ -110,6 +110,12 @@ func setupRoutes(app *fiber.App, repo *storage.Repository, config config.Config) r.Get("/track/:id", func(c *fiber.Ctx) error { return reviewHandler.GetReviewsByMediaId(c, "track") }) + r.Get("/album/top/:id", func(c *fiber.Ctx) error { + return reviewHandler.GetTopReviewsByMediaId(c, "album") + }) + r.Get("/track/top/:id", func(c *fiber.Ctx) error { + return reviewHandler.GetTopReviewsByMediaId(c, "track") + }) r.Get("/track/:userId/:mediaId", func(c *fiber.Ctx) error { return reviewHandler.GetUserReviewOfTrack(c) }) diff --git a/frontend/app/MediaPage.tsx b/frontend/app/MediaPage.tsx index efa6f22b..bd1cb1d5 100644 --- a/frontend/app/MediaPage.tsx +++ b/frontend/app/MediaPage.tsx @@ -1,6 +1,12 @@ import React, { useState, useEffect, useCallback } from "react"; -import { StyleSheet, ScrollView, View, Text } from "react-native"; -import { useFocusEffect, useLocalSearchParams } from "expo-router"; +import { + StyleSheet, + ScrollView, + View, + Text, + TouchableOpacity, +} from "react-native"; +import { router, useFocusEffect, useLocalSearchParams } from "expo-router"; import axios from "axios"; import Histogram from "@/components/media/Histogram"; import YourRatings from "@/components/media/YourRatings"; @@ -10,15 +16,18 @@ import ReviewStats from "@/components/media/ReviewStats"; import ReviewPreview from "@/components/ReviewPreview"; import SkeletonLoader from "expo-skeleton-loader"; +import { useAuthContext } from "@/components/AuthProvider"; export default function MediaPage() { const [media, setMedia] = useState(); const [reviews, setReviews] = useState([]); const [reviewsLoading, setReviewsLoading] = useState(true); const [avgRating, setAvgRating] = useState(null); + const [totalCount, setTotalCount] = useState(0); const [ratingDistributions, setRatingDistributions] = useState< RatingDistribution[] >([]); + const { userId } = useAuthContext(); const BASE_URL = process.env.EXPO_PUBLIC_BASE_URL; const { mediaId, mediaType } = useLocalSearchParams<{ @@ -68,11 +77,12 @@ export default function MediaPage() { useCallback(() => { if (media) { axios - .get(`${BASE_URL}/reviews/${mediaType}/${mediaId}`) + .get(`${BASE_URL}/reviews/${mediaType}/top/${mediaId}`) .then((response) => { setReviews(response.data.reviews); setReviewsLoading(false); setAvgRating(Math.round(response.data.avgRating) || null); + setTotalCount(response.data.totalCount); }) .catch((error) => console.error(error)); } @@ -146,10 +156,26 @@ export default function MediaPage() { ) : ( - {avgRating && ( - - )} + + router.push({ + pathname: "/MediaReviewsPage", + params: { + media_id: mediaId, + user_id: userId, + media_type: mediaType, + filter: "all", + }, + }) + } + > + {avgRating && ( + + )} + + {ratingDistributions && ratingDistributions.length > 0 && ( )} @@ -158,7 +184,7 @@ export default function MediaPage() { - {reviews?.map((review) => ( + {reviews?.slice(0, 5).map((review) => ( { const [allReviews, setAllReviews] = useState([]); const [mediaStats, setMediaStats] = useState<{ userScore: number; + userRatings: number; friendScore: number; + friendRatings: number; avgScore: Number; totalRatings: number; }>({ userScore: 0, + userRatings: 0, friendScore: 0, + friendRatings: 0, avgScore: 0, totalRatings: 0, }); @@ -40,17 +44,16 @@ const MediaReviewsPage = () => { useEffect(() => { const fetchAll = async () => { try { - console.log(`${BASE_URL}/reviews/${media_type}/${media_id}`); const response = await axios.get( `${BASE_URL}/reviews/${media_type}/${media_id}`, ); setAllReviews(response.data.reviews); - setMediaStats({ - userScore: 4.2, - friendScore: mediaStats.friendScore, + + setMediaStats((prev) => ({ + ...prev, avgScore: response.data.avgRating || 0, totalRatings: response.data.totalCount || 0, - }); + })); } catch (error) { console.error(error); } @@ -81,17 +84,17 @@ const MediaReviewsPage = () => { setUserReviews(reviews); // Calculate the average score - const totalScore = reviews.reduce( + const totalScore = response.data.reduce( (sum: any, review: { rating: any }) => sum + review.rating, 0, ); // Sum of all ratings const averageScore = reviews.length > 0 ? totalScore / reviews.length : 0; // Avoid division by 0 - // Update userScore in mediaStats - setMediaStats((prevStats) => ({ - ...prevStats, + setMediaStats((prev) => ({ + ...prev, userScore: averageScore, + userRatings: reviews.length, })); } catch (error) { console.error(error); @@ -156,10 +159,30 @@ const MediaReviewsPage = () => { Avg Rating )} - - {formatLargeNumber(mediaStats.totalRatings)} - - Total Ratings + {selectedFilter === "you" && ( + <> + + {formatLargeNumber(mediaStats.userRatings)} + + Your Ratings + + )} + {selectedFilter === "friend" && ( + <> + + {formatLargeNumber(mediaStats.friendRatings)} + + Friends Ratings + + )} + {selectedFilter === "all" && ( + <> + + {formatLargeNumber(mediaStats.totalRatings)} + + Total Ratings + + )} { /> {selectedFilter === "you" && ( - + {userReviews.map((review, index) => { return ; })} )} {selectedFilter === "friend" && ( - // TODO ALEX: Map each fetched review to a ReviewPreview component which will take care of the rest + // TODO ALEX: Map each fetched review to a ReviewPreview component which will take care of the rest )} {selectedFilter === "all" && ( - + {allReviews.map((review, index) => { return ; })} @@ -262,6 +285,10 @@ const styles = StyleSheet.create({ reviewsContainer: { backgroundColor: "#fff", }, + reviews: { + width: "90%", + alignSelf: "center", + }, }); export default MediaReviewsPage; diff --git a/frontend/app/ReviewPage.tsx b/frontend/app/ReviewPage.tsx index 2c0b80da..40f2f3b8 100644 --- a/frontend/app/ReviewPage.tsx +++ b/frontend/app/ReviewPage.tsx @@ -14,17 +14,17 @@ import { Modal, KeyboardAvoidingView, } from "react-native"; -import Rating0 from "@/assets/images/Ratings/Radial-0.svg"; -import Rating1 from "@/assets/images/Ratings/Radial-1.svg"; -import Rating2 from "@/assets/images/Ratings/Radial-2.svg"; -import Rating3 from "@/assets/images/Ratings/Radial-3.svg"; -import Rating4 from "@/assets/images/Ratings/Radial-4.svg"; -import Rating5 from "@/assets/images/Ratings/Radial-5.svg"; -import Rating6 from "@/assets/images/Ratings/Radial-6.svg"; -import Rating7 from "@/assets/images/Ratings/Radial-7.svg"; -import Rating8 from "@/assets/images/Ratings/Radial-8.svg"; -import Rating9 from "@/assets/images/Ratings/Radial-9.svg"; -import Rating10 from "@/assets/images/Ratings/Radial-10.svg"; +import Rating0 from "@/assets/images/Ratings/Property0.svg"; +import Rating1 from "@/assets/images/Ratings/Property1.svg"; +import Rating2 from "@/assets/images/Ratings/Property2.svg"; +import Rating3 from "@/assets/images/Ratings/Property3.svg"; +import Rating4 from "@/assets/images/Ratings/Property4.svg"; +import Rating5 from "@/assets/images/Ratings/Property5.svg"; +import Rating6 from "@/assets/images/Ratings/Property6.svg"; +import Rating7 from "@/assets/images/Ratings/Property7.svg"; +import Rating8 from "@/assets/images/Ratings/Property8.svg"; +import Rating9 from "@/assets/images/Ratings/Property9.svg"; +import Rating10 from "@/assets/images/Ratings/Property10.svg"; import Downvote from "@/assets/images/ReviewPreview/downvote.svg"; import Upvote from "@/assets/images/ReviewPreview/upvote.svg"; import Comment from "@/assets/images/ReviewPreview/comment.svg"; diff --git a/frontend/assets/images/Ratings/Property0.svg b/frontend/assets/images/Ratings/Property0.svg new file mode 100644 index 00000000..734d30fc --- /dev/null +++ b/frontend/assets/images/Ratings/Property0.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/assets/images/Ratings/Property1.svg b/frontend/assets/images/Ratings/Property1.svg new file mode 100644 index 00000000..c4a3b7ff --- /dev/null +++ b/frontend/assets/images/Ratings/Property1.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/assets/images/Ratings/Property10.svg b/frontend/assets/images/Ratings/Property10.svg new file mode 100644 index 00000000..d485a746 --- /dev/null +++ b/frontend/assets/images/Ratings/Property10.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/assets/images/Ratings/Property2.svg b/frontend/assets/images/Ratings/Property2.svg new file mode 100644 index 00000000..e466c44a --- /dev/null +++ b/frontend/assets/images/Ratings/Property2.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/assets/images/Ratings/Property3.svg b/frontend/assets/images/Ratings/Property3.svg new file mode 100644 index 00000000..ec12323a --- /dev/null +++ b/frontend/assets/images/Ratings/Property3.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/assets/images/Ratings/Property4.svg b/frontend/assets/images/Ratings/Property4.svg new file mode 100644 index 00000000..c8cd42ee --- /dev/null +++ b/frontend/assets/images/Ratings/Property4.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/assets/images/Ratings/Property5.svg b/frontend/assets/images/Ratings/Property5.svg new file mode 100644 index 00000000..83bbc285 --- /dev/null +++ b/frontend/assets/images/Ratings/Property5.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/assets/images/Ratings/Property6.svg b/frontend/assets/images/Ratings/Property6.svg new file mode 100644 index 00000000..82744be4 --- /dev/null +++ b/frontend/assets/images/Ratings/Property6.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/assets/images/Ratings/Property7.svg b/frontend/assets/images/Ratings/Property7.svg new file mode 100644 index 00000000..f1971b2f --- /dev/null +++ b/frontend/assets/images/Ratings/Property7.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/assets/images/Ratings/Property8.svg b/frontend/assets/images/Ratings/Property8.svg new file mode 100644 index 00000000..fd515dc4 --- /dev/null +++ b/frontend/assets/images/Ratings/Property8.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/assets/images/Ratings/Property9.svg b/frontend/assets/images/Ratings/Property9.svg new file mode 100644 index 00000000..919acd77 --- /dev/null +++ b/frontend/assets/images/Ratings/Property9.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/components/ReviewPreview.tsx b/frontend/components/ReviewPreview.tsx index a1ad74b5..85b62530 100644 --- a/frontend/components/ReviewPreview.tsx +++ b/frontend/components/ReviewPreview.tsx @@ -12,17 +12,17 @@ import { TextInput, } from "react-native"; -import Rating0 from "@/assets/images/Ratings/Radial-0.svg"; -import Rating1 from "@/assets/images/Ratings/Radial-1.svg"; -import Rating2 from "@/assets/images/Ratings/Radial-2.svg"; -import Rating3 from "@/assets/images/Ratings/Radial-3.svg"; -import Rating4 from "@/assets/images/Ratings/Radial-4.svg"; -import Rating5 from "@/assets/images/Ratings/Radial-5.svg"; -import Rating6 from "@/assets/images/Ratings/Radial-6.svg"; -import Rating7 from "@/assets/images/Ratings/Radial-7.svg"; -import Rating8 from "@/assets/images/Ratings/Radial-8.svg"; -import Rating9 from "@/assets/images/Ratings/Radial-9.svg"; -import Rating10 from "@/assets/images/Ratings/Radial-10.svg"; +import Rating0 from "@/assets/images/Ratings/Property0.svg"; +import Rating1 from "@/assets/images/Ratings/Property1.svg"; +import Rating2 from "@/assets/images/Ratings/Property2.svg"; +import Rating3 from "@/assets/images/Ratings/Property3.svg"; +import Rating4 from "@/assets/images/Ratings/Property4.svg"; +import Rating5 from "@/assets/images/Ratings/Property5.svg"; +import Rating6 from "@/assets/images/Ratings/Property6.svg"; +import Rating7 from "@/assets/images/Ratings/Property7.svg"; +import Rating8 from "@/assets/images/Ratings/Property8.svg"; +import Rating9 from "@/assets/images/Ratings/Property9.svg"; +import Rating10 from "@/assets/images/Ratings/Property10.svg"; import Downvote from "@/assets/images/ReviewPreview/downvote.svg"; import Upvote from "@/assets/images/ReviewPreview/upvote.svg"; @@ -293,21 +293,13 @@ const ReviewPreview: React.FC = ({ preview }) => { getRatingImage(preview.rating as keyof typeof ratingImages), { style: styles.ratingImage, + width: 70, // Adjust size as needed + height: 70, }, )} - {preview.tags && preview.tags.length > 0 && ( - - {preview.tags.map((tag, index) => ( - - {tag} - - ))} - - )} - {preview.title} {isEditable ? ( @@ -342,6 +334,16 @@ const ReviewPreview: React.FC = ({ preview }) => { )} + + {preview.tags && preview.tags.length > 0 && ( + + {preview.tags.map((tag, index) => ( + + {tag} + + ))} + + )} @@ -462,6 +464,8 @@ const styles = StyleSheet.create({ alignItems: "flex-start", overflow: "scroll", boxShadow: "0px 4px 4px rgba(0, 0, 0, 0.2)", + borderColor: "#ddd", + borderWidth: 0.5, }, voteButton: { flexDirection: "row", @@ -544,6 +548,7 @@ const styles = StyleSheet.create({ ratingContainer: { justifyContent: "flex-start", alignItems: "flex-start", + marginBottom: -15, }, mediaContainer: { flexDirection: "row", @@ -600,9 +605,10 @@ const styles = StyleSheet.create({ marginHorizontal: 10, }, ratingImage: { - width: 30, // Smaller size - height: 30, // Match smaller size + width: 50, + height: 50, marginRight: 30, // Adjust horizontal placement + marginBottom: -20, }, modalOverlay: { flex: 1, diff --git a/frontend/components/media/RatingSvg.tsx b/frontend/components/media/RatingSvg.tsx index 599b0b1b..772408fa 100644 --- a/frontend/components/media/RatingSvg.tsx +++ b/frontend/components/media/RatingSvg.tsx @@ -1,16 +1,16 @@ import React from "react"; import { StyleSheet, View } from "react-native"; -import Rating0 from "@/assets/images/Ratings/Radial-0.svg"; -import Rating1 from "@/assets/images/Ratings/Radial-1.svg"; -import Rating2 from "@/assets/images/Ratings/Radial-2.svg"; -import Rating3 from "@/assets/images/Ratings/Radial-3.svg"; -import Rating4 from "@/assets/images/Ratings/Radial-4.svg"; -import Rating5 from "@/assets/images/Ratings/Radial-5.svg"; -import Rating6 from "@/assets/images/Ratings/Radial-6.svg"; -import Rating7 from "@/assets/images/Ratings/Radial-7.svg"; -import Rating8 from "@/assets/images/Ratings/Radial-8.svg"; -import Rating9 from "@/assets/images/Ratings/Radial-9.svg"; -import Rating10 from "@/assets/images/Ratings/Radial-10.svg"; +import Rating0 from "@/assets/images/Ratings/Property0.svg"; +import Rating1 from "@/assets/images/Ratings/Property1.svg"; +import Rating2 from "@/assets/images/Ratings/Property2.svg"; +import Rating3 from "@/assets/images/Ratings/Property3.svg"; +import Rating4 from "@/assets/images/Ratings/Property4.svg"; +import Rating5 from "@/assets/images/Ratings/Property5.svg"; +import Rating6 from "@/assets/images/Ratings/Property6.svg"; +import Rating7 from "@/assets/images/Ratings/Property7.svg"; +import Rating8 from "@/assets/images/Ratings/Property8.svg"; +import Rating9 from "@/assets/images/Ratings/Property9.svg"; +import Rating10 from "@/assets/images/Ratings/Property10.svg"; type RatingSvgProps = { rating: number; diff --git a/frontend/components/media/ReviewStats.tsx b/frontend/components/media/ReviewStats.tsx index 800cfbd4..06072f0b 100644 --- a/frontend/components/media/ReviewStats.tsx +++ b/frontend/components/media/ReviewStats.tsx @@ -4,14 +4,14 @@ import RatingSvg from "./RatingSvg"; type ReviewStatsProps = { rating: number; - reviews: Preview[]; + count: number; }; -const ReviewStats = ({ rating, reviews }: ReviewStatsProps) => { +const ReviewStats = ({ rating, count }: ReviewStatsProps) => { return ( - {reviews.length} + {count} Total ratings diff --git a/frontend/components/search/Filter.tsx b/frontend/components/search/Filter.tsx index 1e23d69a..f67779ba 100644 --- a/frontend/components/search/Filter.tsx +++ b/frontend/components/search/Filter.tsx @@ -19,7 +19,10 @@ const Filter: React.FC = ({ onFilterChange(filter)} - style={[styles.button]} + style={[ + styles.button, + currentFilter === filter && styles.selectedButton, + ]} >