From 9079a527782034d5a4854acea821935130268c5f Mon Sep 17 00:00:00 2001 From: Norbert Maciaszek Date: Mon, 25 Aug 2025 22:05:59 +0200 Subject: [PATCH] feat: enhance MovieRow component with drag-and-drop functionality for marking movies as watched or favorite; integrate framer-motion for improved animations and user interaction --- src/components/atoms/MovieRow/index.tsx | 175 +++++++++++++++++------- 1 file changed, 126 insertions(+), 49 deletions(-) diff --git a/src/components/atoms/MovieRow/index.tsx b/src/components/atoms/MovieRow/index.tsx index e6b655e..a607713 100644 --- a/src/components/atoms/MovieRow/index.tsx +++ b/src/components/atoms/MovieRow/index.tsx @@ -1,8 +1,11 @@ +"use client"; import { formatter } from "@/helpers/formater"; import { Movie } from "@/types/global"; import Link from "next/link"; import { FC } from "react"; -import { FaCalendar, FaClock, FaStar } from "react-icons/fa"; +import { FaCalendar, FaClock, FaStar, FaEye, FaHeart } from "react-icons/fa"; +import { motion, useAnimationControls, useMotionValue } from "framer-motion"; +import { useGlobalStore } from "@/app/store/globalStore"; type Props = { movie: Movie; @@ -15,6 +18,11 @@ export const MovieRow: FC = ({ isUpcoming = false, compact = false, }) => { + const { movies, addMovie, updateMovie } = useGlobalStore(); + + const dragControls = useAnimationControls(); + const x = useMotionValue(0); + const daysSinceRelease = Math.abs( Math.floor( (new Date().getTime() - new Date(movie.release_date).getTime()) / @@ -22,60 +30,129 @@ export const MovieRow: FC = ({ ) ); + // Check if movie is already in store. + const movieInStore = movies.find((m) => m.id === movie.id); + const isWatched = movieInStore?.seen || false; + const isFavorite = movieInStore?.favorite || false; + + const handleMarkAsWatched = () => { + if (movieInStore) { + updateMovie(movie.id, { seen: !isWatched }); + } else { + addMovie({ ...movie, seen: true, favorite: false }); + } + }; + + const handleAddToFavorites = () => { + if (movieInStore) { + updateMovie(movie.id, { favorite: !isFavorite, seen: true }); + } else { + addMovie({ ...movie, seen: true, favorite: true }); + } + }; + + const handleDragAction = () => { + const threshold = 70; + if (x.get() > threshold) { + handleAddToFavorites(); + } else if (x.get() < -threshold) { + handleMarkAsWatched(); + } + dragControls.start({ + x: 0, + }); + }; + return ( - -
- {movie.title} +
+ {/* Background actions */} +
+
+ +
+ +
+ +
-
-

- {movie.title} -

-
-
- {isUpcoming ? ( - - ) : ( - - )} - {formatter.formatDate(movie.release_date)} + + +
+ {movie.title}
- {!!movie.vote_average && ( -
- - {movie.vote_average.toFixed(1)} +
+

+ {movie.title} +

+
+
+ {isUpcoming ? ( + + ) : ( + + )} + {formatter.formatDate(movie.release_date)} +
+ + {!!movie.vote_average && ( +
+ + {movie.vote_average.toFixed(1)} +
+ )} + + {(isFavorite || movie.favorite) && ( +
+ )} + + {(isWatched || movie.seen) && ( +
+ )} +
+
+ + {!compact && ( +
+ {isUpcoming + ? `za ${daysSinceRelease} dni` + : `od ${daysSinceRelease} dni`}
)} - - {movie.favorite && ( -
- )} -
-
- - {!compact && ( -
- {isUpcoming - ? `za ${daysSinceRelease} dni` - : `od ${daysSinceRelease} dni`} -
- )} - + + +
); };