"use client"; import Image from "next/image"; import type { CSSProperties } from "react"; import { useEffect, useMemo, useState } from "react"; import { toast } from "sonner"; import { DraggableItem } from "@/components/editor/panels/assets/draggable-item"; import { PanelBaseView as BaseView } from "@/components/editor/panels/panel-base-view"; import { Button } from "@/components/ui/button"; import { InputWithBack } from "@/components/ui/input-with-back"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; import { STICKER_CATEGORIES } from "@/constants/stickers-constants"; import { useInfiniteScroll } from "@/hooks/use-infinite-scroll"; import { buildIconSvgUrl, getIconSvgUrl, ICONIFY_HOSTS, POPULAR_COLLECTIONS, } from "@/lib/iconify-api"; import { useStickersStore } from "@/stores/stickers-store"; import type { StickerCategory } from "@/types/stickers"; import { cn } from "@/utils/ui"; import { ArrowRightIcon, HappyIcon, ClockIcon, LayoutGridIcon, MultiplicationSignIcon, SparklesIcon, HashtagIcon, } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/react"; import { Spinner } from "@/components/ui/spinner"; function isStickerCategory(value: string): value is StickerCategory { return STICKER_CATEGORIES.includes(value as StickerCategory); } export function StickersView() { const { selectedCategory, setSelectedCategory } = useStickersStore(); return ( { if (isStickerCategory(v)) { setSelectedCategory({ category: v }); } }} tabs={[ { value: "all", label: "All", icon: , content: , }, { value: "general", label: "Icons", icon: , content: , }, { value: "brands", label: "Brands", icon: , content: , }, { value: "emoji", label: "Emoji", icon: , content: , }, ]} className="flex h-full flex-col overflow-hidden p-0" /> ); } function StickerGrid({ icons, onAdd, addingSticker, capSize = false, }: { icons: string[]; onAdd: (iconName: string) => void; addingSticker: string | null; capSize?: boolean; }) { const gridStyle: CSSProperties & { "--sticker-min": string; "--sticker-max"?: string; } = { gridTemplateColumns: capSize ? "repeat(auto-fill, minmax(var(--sticker-min, 96px), var(--sticker-max, 160px)))" : "repeat(auto-fit, minmax(var(--sticker-min, 96px), 1fr))", "--sticker-min": "96px", ...(capSize ? { "--sticker-max": "160px" } : {}), }; return (
{icons.map((iconName) => ( ))}
); } function CollectionGrid({ collections, onSelectCollection, }: { collections: Array<{ prefix: string; name: string; total: number; category?: string; }>; onSelectCollection: ({ prefix }: { prefix: string }) => void; }) { return (
{collections.map((collection) => ( onSelectCollection({ prefix: collection.prefix })} /> ))}
); } function EmptyView({ message }: { message: string }) { return (

No stickers found

{message}

); } function StickersContentView({ category }: { category: StickerCategory }) { const { searchQuery, selectedCollection, viewMode, collections, currentCollection, searchResults, recentStickers, isLoadingCollections, isLoadingCollection, isSearching, setSearchQuery, setSelectedCollection, loadCollections, searchStickers, addStickerToTimeline, clearRecentStickers, setSelectedCategory, addingSticker, } = useStickersStore(); 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(); } }, [collections, loadCollections]); useEffect(() => { const timer = setTimeout(() => { if (localSearchQuery !== searchQuery) { setSearchQuery({ query: localSearchQuery }); if (localSearchQuery.trim()) { searchStickers({ query: localSearchQuery }); } } }, 500); return () => clearTimeout(timer); }, [localSearchQuery, searchQuery, searchStickers, setSearchQuery]); const handleAddSticker = async (iconName: string) => { try { await addStickerToTimeline({ iconName }); } catch (error) { console.error("Failed to add sticker:", error); toast.error("Failed to add sticker to timeline"); } }; 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({ collection: null }); } }} placeholder={ category === "all" ? "Search all stickers" : category === "general" ? "Search icons" : category === "brands" ? "Search brands" : "Search Emojis" } value={localSearchQuery} onChange={setLocalSearchQuery} disableAnimation={true} />
{recentStickers.length > 0 && viewMode === "browse" && (
Recent

Clear recent stickers

)} {viewMode === "collection" && selectedCollection && (
{isLoadingCollection ? (
) : showCollectionItems ? ( ) : (
)}
)} {viewMode === "search" && (
{isSearching ? (
) : searchResults?.icons.length ? ( <>
{searchResults.total} results
) : searchQuery ? (
{category !== "all" && ( )}
) : null}
)} {viewMode === "browse" && !selectedCollection && (
{isLoadingCollections ? (
) : ( <> {category !== "all" && (
setSelectedCollection({ collection: prefix }) } />
)} {category === "all" && filteredCollections.length > 0 && (
setSelectedCollection({ collection: prefix }) } />
)} )}
)}
); } 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; capSize?: boolean; } function StickerItem({ iconName, onAdd, isAdding, capSize = false, }: StickerItemProps) { const [imageError, setImageError] = useState(false); const [hostIndex, setHostIndex] = useState(0); useEffect(() => { if (!iconName) { return; } 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} shouldShowLabel={false} isRounded={true} variant="card" className="" containerClassName="w-full" /> {isAdding && (
)}

{displayName}

{collectionPrefix}

); }