"use client"; import { useEffect, useState, useMemo } from "react"; import { useStickersStore } from "@/stores/stickers-store"; import { useMediaStore } from "@/stores/media-store"; import { useProjectStore } from "@/stores/project-store"; import { useTimelineStore } from "@/stores/timeline-store"; import { usePlaybackStore } from "@/stores/playback-store"; import { Loader2, Grid3X3, Hash, Smile, Clock, X, Sparkles, ArrowRight, StickerIcon, } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; import { Separator } from "@/components/ui/separator"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; import { getIconSvgUrl, buildIconSvgUrl, ICONIFY_HOSTS, POPULAR_COLLECTIONS, } from "@/lib/iconify-api"; import { cn, generateUUID } from "@/lib/utils"; import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants"; import Image from "next/image"; import { DraggableMediaItem } from "@/components/ui/draggable-item"; import { InputWithBack } from "@/components/ui/input-with-back"; import { StickerCategory } from "@/stores/stickers-store"; import { useInfiniteScroll } from "@/hooks/use-infinite-scroll"; export function StickersView() { const { selectedCategory, setSelectedCategory } = useStickersStore(); return (
{ if (["all", "general", "brands", "emoji"].includes(v)) { setSelectedCategory(v as StickerCategory); } }} className="flex flex-col h-full" >
All Icons Brands Emoji
); } function StickerGrid({ icons, onAdd, addingSticker, }: { icons: string[]; onAdd: (iconName: string) => void; addingSticker: string | null; }) { return (
{icons.map((iconName) => ( ))}
); } function CollectionGrid({ collections, onSelectCollection, }: { collections: Array<{ prefix: string; name: string; total: number; category?: string; }>; onSelectCollection: (prefix: string) => void; }) { return (
{collections.map((collection) => ( onSelectCollection(collection.prefix)} /> ))}
); } function EmptyView({ message }: { message: string }) { return (

No stickers found

{message}

); } function StickersContentView({ category }: { category: StickerCategory }) { const { activeProject } = useProjectStore(); const { addMediaAtTime } = useTimelineStore(); const { currentTime } = usePlaybackStore(); const { addMediaItem } = useMediaStore(); const { searchQuery, selectedCollection, viewMode, collections, currentCollection, searchResults, recentStickers, isLoadingCollections, isLoadingCollection, isSearching, setSearchQuery, setSelectedCollection, loadCollections, searchStickers, downloadSticker, clearRecentStickers, } = useStickersStore(); const [addingSticker, setAddingSticker] = useState(null); const [localSearchQuery, setLocalSearchQuery] = useState(searchQuery); const [collectionsToShow, setCollectionsToShow] = useState(20); const [showCollectionItems, setShowCollectionItems] = useState(false); const filteredCollections = useMemo(() => { if (category === "all") { return Object.entries(collections).map(([prefix, collection]) => ({ prefix, name: collection.name, total: collection.total, category: collection.category, })); } const collectionList = POPULAR_COLLECTIONS[category as keyof typeof POPULAR_COLLECTIONS]; if (!collectionList) return []; return collectionList .map((c) => { const collection = collections[c.prefix]; return collection ? { prefix: c.prefix, name: c.name, total: collection.total, } : null; }) .filter(Boolean) as Array<{ prefix: string; name: string; total: number; }>; }, [collections, category]); const { scrollAreaRef, handleScroll } = useInfiniteScroll({ onLoadMore: () => setCollectionsToShow((prev) => prev + 20), hasMore: filteredCollections.length > collectionsToShow, isLoading: isLoadingCollections, enabled: viewMode === "browse" && !selectedCollection && category === "all", }); useEffect(() => { if (Object.keys(collections).length === 0) { loadCollections(); } }, []); useEffect(() => { const timer = setTimeout(() => { if (localSearchQuery !== searchQuery) { setSearchQuery(localSearchQuery); if (localSearchQuery.trim()) { searchStickers(localSearchQuery); } } }, 500); return () => clearTimeout(timer); }, [localSearchQuery]); const handleAddSticker = async (iconName: string) => { if (!activeProject) { toast.error("No active project"); return; } setAddingSticker(iconName); try { const file = await downloadSticker(iconName); if (!file) { throw new Error("Failed to download sticker"); } const mediaItem = { name: iconName.replace(":", "-"), type: "image" as const, file, url: URL.createObjectURL(file), width: 200, height: 200, duration: TIMELINE_CONSTANTS.DEFAULT_IMAGE_DURATION, ephemeral: false, }; await addMediaItem(activeProject.id, mediaItem); const added = useMediaStore .getState() .mediaItems.find( (m) => m.url === mediaItem.url && m.name === mediaItem.name ); if (!added) throw new Error("Sticker not in media store"); addMediaAtTime(added, currentTime); toast.success(`Added "${iconName}" to timeline`); } catch (error) { console.error("Failed to add sticker:", error); toast.error("Failed to add sticker to timeline"); } finally { setAddingSticker(null); } }; const iconsToDisplay = useMemo(() => { if (viewMode === "search" && searchResults) { return searchResults.icons; } if (viewMode === "collection" && currentCollection) { const icons: string[] = []; if (currentCollection.uncategorized) { icons.push( ...currentCollection.uncategorized.map( (name) => `${currentCollection.prefix}:${name}` ) ); } if (currentCollection.categories) { Object.values(currentCollection.categories).forEach((categoryIcons) => { icons.push( ...categoryIcons.map( (name) => `${currentCollection.prefix}:${name}` ) ); }); } return icons.slice(0, 100); } return []; }, [viewMode, searchResults, currentCollection]); const isInCollection = viewMode === "collection" && !!selectedCollection; useEffect(() => { if (isInCollection) { setShowCollectionItems(false); const timer = setTimeout(() => setShowCollectionItems(true), 350); return () => clearTimeout(timer); } else { setShowCollectionItems(false); } }, [isInCollection]); return (
{ if (!expanded && isInCollection) { setSelectedCollection(null); } }} placeholder="Search icons..." value={localSearchQuery} onChange={setLocalSearchQuery} />
{recentStickers.length > 0 && viewMode === "browse" && (
Recent

Clear recent stickers

)} {viewMode === "collection" && selectedCollection && (
{isLoadingCollection ? (
) : showCollectionItems ? ( ) : (
)}
)} {viewMode === "search" && (
{isSearching ? (
) : searchResults?.icons.length ? ( <>
{searchResults.total} results
) : searchQuery ? ( ) : null}
)} {viewMode === "browse" && !selectedCollection && (
{isLoadingCollections ? (
) : ( <> {category !== "all" && (

Popular{" "} {category === "general" ? "Icon Sets" : category === "brands" ? "Brand Icons" : "Emoji Sets"}

)} {category === "all" && filteredCollections.length > 0 && (
)} )}
)}
); } interface CollectionItemProps { title: string; subtitle: string; onClick: () => void; } function CollectionItem({ title, subtitle, onClick }: CollectionItemProps) { return ( ); } interface StickerItemProps { iconName: string; onAdd: (iconName: string) => void; isAdding?: boolean; } function StickerItem({ iconName, onAdd, isAdding }: StickerItemProps) { const [imageError, setImageError] = useState(false); const [hostIndex, setHostIndex] = useState(0); useEffect(() => { setImageError(false); setHostIndex(0); }, [iconName]); const displayName = iconName.split(":")[1] || iconName; const collectionPrefix = iconName.split(":")[0]; const preview = imageError ? (
{displayName}
) : (
{displayName} { const next = hostIndex + 1; if (next < ICONIFY_HOSTS.length) { setHostIndex(next); } else { setImageError(true); } }} loading="lazy" unoptimized />
); return (
onAdd(iconName)} aspectRatio={1} showLabel={false} rounded={true} variant="card" className="" isDraggable={false} /> {isAdding && (
)}

{displayName}

{collectionPrefix}

); }