File size: 6,061 Bytes
72f0edb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import React, { useRef, useState, useEffect } from "react";
import { ContentItem } from "../lib/types";
import { useNavigate } from "react-router-dom";
import { ChevronLeft, ChevronRight, Star } from "lucide-react";
import { cn } from "@/lib/utils";
import { useIsMobile } from "@/hooks/use-mobile";
import { useMyList } from "@/hooks/use-my-list";
import { supabase } from "@/integrations/supabase/client";
import { useUrdf } from "@/hooks/useUrdf";

interface CarouselProps {
  title: string;
  items: ContentItem[];
  className?: string;
}

const Carousel: React.FC<CarouselProps> = ({ title, items, className }) => {
  const carouselRef = useRef<HTMLDivElement>(null);
  const navigate = useNavigate();
  const isMobile = useIsMobile();
  const { myList, addToMyList, removeFromMyList, isInMyList } = useMyList();
  const [imageUrls, setImageUrls] = useState<Record<string, string>>({});
  const { urdfProcessor, processUrdfFiles } = useUrdf();

  // Fetch image URLs for all items
  useEffect(() => {
    const fetchImages = async () => {
      const urls: Record<string, string> = {};

      for (const item of items) {
        if (
          item.imageUrl &&
          !item.imageUrl.startsWith("http") &&
          !item.imageUrl.startsWith("data:")
        ) {
          try {
            // Get public URL for the image
            const { data, error } = await supabase.storage
              .from("urdf-images")
              .createSignedUrl(item.imageUrl, 3600); // 1 hour expiry

            if (data && !error) {
              urls[item.id] = data.signedUrl;
            } else {
              // Fallback to placeholder
              urls[item.id] = "/placeholder.svg";
            }
          } catch (error) {
            console.error(`Error fetching image for ${item.id}:`, error);
            urls[item.id] = "/placeholder.svg";
          }
        } else {
          // For URLs that are already full URLs or data URIs
          urls[item.id] = item.imageUrl || "/placeholder.svg";
        }
      }

      setImageUrls(urls);
    };

    if (items.length > 0) {
      fetchImages();
    }
  }, [items]);

  const handleScrollLeft = () => {
    if (carouselRef.current) {
      const scrollAmount = carouselRef.current.offsetWidth / 4.1;
      carouselRef.current.scrollBy({ left: -scrollAmount, behavior: "smooth" });
    }
  };

  const handleScrollRight = () => {
    if (carouselRef.current) {
      const scrollAmount = carouselRef.current.offsetWidth / 4.1;
      carouselRef.current.scrollBy({ left: scrollAmount, behavior: "smooth" });
    }
  };

  const handleItemClick = async (item: ContentItem) => {
    // Only navigate to the content detail page, let the detail page handle loading
    navigate(`/content/${item.id}`);

    // We've removed the URDF loading here to prevent duplication with ContentDetail's loading
  };

  const handleStarClick = (e: React.MouseEvent, item: ContentItem) => {
    e.stopPropagation(); // Prevent navigation on star click

    if (isInMyList(item.id)) {
      removeFromMyList(item.id);
    } else {
      addToMyList(item);
    }
  };

  // If no items, don't render the carousel
  if (items.length === 0) return null;

  return (
    <div className={cn("my-5", className)}>
      <div className="relative group">
        <div
          ref={carouselRef}
          className="carousel-container flex items-center gap-2 overflow-x-auto py-2 px-4 scroll-smooth"
        >
          {items.map((item) => (
            <div
              key={item.id}
              className="carousel-item flex-shrink-0 cursor-pointer relative hover:z-10"
              style={{
                width: "calc(100% / 4.1)",
              }}
              onClick={() => handleItemClick(item)}
            >
              <div className="relative rounded-md w-full h-full group/item">
                {/* Image container with darker overlay on hover */}
                <div className="rounded-md overflow-hidden w-full h-full bg-black">
                  <img
                    src={imageUrls[item.id] || "/placeholder.svg"}
                    alt={item.title}
                    className="w-full h-full object-cover rounded-md transition-all duration-300 group-hover/item:brightness-90"
                    style={{
                      aspectRatio: "0.8",
                    }}
                  />
                </div>
                {/* Star button */}
                <div
                  className="absolute top-4 right-4 p-2 z-20 invisible group-hover/item:visible"
                  onClick={(e) => handleStarClick(e, item)}
                >
                  <Star
                    size={24}
                    className={cn(
                      "transition-colors duration-300",
                      isInMyList(item.id)
                        ? "fill-yellow-400 text-yellow-400"
                        : "text-white hover:text-yellow-400"
                    )}
                  />
                </div>
                {/* Title overlay - visible on hover without gradient */}
                <div className="absolute bottom-4 left-4 opacity-0 group-hover/item:opacity-100 transition-opacity duration-300">
                  <h3 className="text-gray-400 text-6xl font-bold drop-shadow-xl">
                    {item.title}
                  </h3>
                </div>
              </div>
            </div>
          ))}
        </div>

        {/* Scroll buttons - changed to always visible */}
        <button
          onClick={handleScrollLeft}
          className="absolute left-0 top-1/2 -translate-y-1/2 bg-black text-white p-1 rounded-full z-40"
          aria-label="Scroll left"
        >
          <ChevronLeft size={24} />
        </button>

        <button
          onClick={handleScrollRight}
          className="absolute right-0 top-1/2 -translate-y-1/2 bg-black text-white p-1 rounded-full z-40"
          aria-label="Scroll right"
        >
          <ChevronRight size={24} />
        </button>
      </div>
    </div>
  );
};

export default Carousel;