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:
parent
cb0962f184
commit
9079a52778
|
|
@ -1,8 +1,11 @@
|
||||||
|
"use client";
|
||||||
import { formatter } from "@/helpers/formater";
|
import { formatter } from "@/helpers/formater";
|
||||||
import { Movie } from "@/types/global";
|
import { Movie } from "@/types/global";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { FC } from "react";
|
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 = {
|
type Props = {
|
||||||
movie: Movie;
|
movie: Movie;
|
||||||
|
|
@ -15,6 +18,11 @@ export const MovieRow: FC<Props> = ({
|
||||||
isUpcoming = false,
|
isUpcoming = false,
|
||||||
compact = false,
|
compact = false,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { movies, addMovie, updateMovie } = useGlobalStore();
|
||||||
|
|
||||||
|
const dragControls = useAnimationControls();
|
||||||
|
const x = useMotionValue(0);
|
||||||
|
|
||||||
const daysSinceRelease = Math.abs(
|
const daysSinceRelease = Math.abs(
|
||||||
Math.floor(
|
Math.floor(
|
||||||
(new Date().getTime() - new Date(movie.release_date).getTime()) /
|
(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 (
|
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
|
<Link
|
||||||
href={`/film/${movie.id}`}
|
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">
|
<div className="relative w-12 h-16 rounded overflow-hidden flex-shrink-0">
|
||||||
<img
|
<img
|
||||||
|
|
@ -57,8 +122,18 @@ export const MovieRow: FC<Props> = ({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{movie.favorite && (
|
{(isFavorite || movie.favorite) && (
|
||||||
<div className="w-2 h-2 bg-red-500 rounded-full" title="Ulubione" />
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -77,5 +152,7 @@ export const MovieRow: FC<Props> = ({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue