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

This commit is contained in:
Norbert Maciaszek 2025-08-25 22:05:59 +02:00
parent cb0962f184
commit 9079a52778
1 changed files with 126 additions and 49 deletions

View File

@ -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<Props> = ({
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,10 +30,67 @@ export const MovieRow: FC<Props> = ({
)
);
// 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 (
<div className="relative overflow-hidden">
{/* Background actions */}
<div className="absolute inset-0 flex">
<div className="absolute right-0 h-full w-24 bg-green-500/20 flex items-center justify-center cursor-pointer">
<FaEye className="w-5 h-5 transition-colors text-green-500" />
</div>
<div className="absolute left-0 h-full w-24 bg-red-500/20 flex items-center justify-center cursor-pointer">
<FaHeart className="w-5 h-5 transition-colors text-red-500" />
</div>
</div>
<motion.div
drag="x"
style={{ x }}
animate={dragControls}
dragConstraints={{ left: -80, right: 80 }}
dragElastic={0.01}
dragMomentum={false}
whileDrag={{ cursor: "grabbing" }}
onDragEnd={handleDragAction}
className="relative z-10"
>
<Link
href={`/film/${movie.id}`}
className="flex items-center gap-4 p-3 rounded-lg bg-gray-800/30 hover:bg-gray-800/50 transition-colors group"
draggable={false}
className="flex items-center gap-4 p-3 rounded-lg bg-gray-800 hover:bg-gray-800 transition-colors group"
>
<div className="relative w-12 h-16 rounded overflow-hidden flex-shrink-0">
<img
@ -57,8 +122,18 @@ export const MovieRow: FC<Props> = ({
</div>
)}
{movie.favorite && (
<div className="w-2 h-2 bg-red-500 rounded-full" title="Ulubione" />
{(isFavorite || movie.favorite) && (
<div
className="w-2 h-2 bg-red-500 rounded-full"
title="Ulubione"
/>
)}
{(isWatched || movie.seen) && (
<div
className="w-2 h-2 bg-green-500 rounded-full"
title="Obejrzane"
/>
)}
</div>
</div>
@ -77,5 +152,7 @@ export const MovieRow: FC<Props> = ({
</div>
)}
</Link>
</motion.div>
</div>
);
};