"use client" import { useState, useEffect, useRef, useCallback } from "react" import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Search, Image, Sticker, X, Loader2, TrendingUp } from "lucide-react" import data from "@emoji-mart/data" import Picker from "@emoji-mart/react" import { getStickerPacks, type StickerPack } from "@/data/stickers" type Tab = "emoji" | "gif" | "sticker" interface MediaPickerProps { onEmojiSelect: (emoji: string) => void onMediaSelect: (content: string) => void onClose: () => void theme?: string } const RECENT_GIFS_KEY = "recent-gifs" const RECENT_STICKERS_KEY = "recent-stickers" const MAX_RECENT = 12 function getRecent(key: string): string[] { if (typeof window === "undefined") return [] try { return JSON.parse(localStorage.getItem(key) || "[]") } catch { return [] } } function addRecent(key: string, id: string) { if (typeof window === "undefined") return const items = [id, ...getRecent(key).filter((x) => x !== id)].slice(0, MAX_RECENT) localStorage.setItem(key, JSON.stringify(items)) } export default function MediaPicker({ onEmojiSelect, onMediaSelect, onClose, theme }: MediaPickerProps) { const [tab, setTab] = useState("emoji") const [gifQuery, setGifQuery] = useState("") const [gifResults, setGifResults] = useState([]) const [gifLoading, setGifLoading] = useState(false) const [gifError, setGifError] = useState("") const [gifUnavailable, setGifUnavailable] = useState(false) const [gifNext, setGifNext] = useState("") const gifCache = useRef>(new Map()) const [recentGifs, setRecentGifs] = useState([]) const [recentStickers, setRecentStickers] = useState([]) const [stickerPacks] = useState(getStickerPacks) const [activePack, setActivePack] = useState(stickerPacks[0]?.id || "") const searchTimer = useRef | null>(null) const pickerRef = useRef(null) useEffect(() => { const handleClick = (e: MouseEvent) => { if (pickerRef.current && !pickerRef.current.contains(e.target as Node)) onClose() } document.addEventListener("mousedown", handleClick) return () => document.removeEventListener("mousedown", handleClick) }, [onClose]) useEffect(() => { setRecentGifs(getRecent(RECENT_GIFS_KEY)) }, []) useEffect(() => { setRecentStickers(getRecent(RECENT_STICKERS_KEY)) }, []) const fetchGifs = useCallback(async (query: string, pos = "") => { const cacheKey = `${query}::${pos}` const cached = gifCache.current.get(cacheKey) if (cached && !pos) { setGifResults(cached.results) setGifNext(cached.next) return } setGifLoading(true) setGifError("") try { const params = new URLSearchParams({ limit: "20", pos }) if (query) params.set("q", query) const res = await fetch(`/api/gifs?${params}`) const data = await res.json() if (data.noKey) { setGifUnavailable(true) setGifResults([]) } else if (data.results) { setGifResults((prev) => (pos ? [...prev, ...data.results] : data.results)) setGifNext(data.next || "") setGifUnavailable(false) if (!pos) gifCache.current.set(cacheKey, { results: data.results, next: data.next || "" }) } else { setGifError("No results found") } } catch { setGifError("Could not load GIFs. Check your connection.") } setGifLoading(false) }, []) useEffect(() => { if (tab !== "gif") return if (searchTimer.current) clearTimeout(searchTimer.current) if (gifQuery.trim()) { searchTimer.current = setTimeout(() => fetchGifs(gifQuery.trim()), 400) } else { fetchGifs("") } return () => { if (searchTimer.current) clearTimeout(searchTimer.current) } }, [tab, gifQuery, fetchGifs]) const handleGifSelect = (gif: any) => { const content = JSON.stringify({ gif: gif.url, w: gif.width, h: gif.height }) addRecent(RECENT_GIFS_KEY, gif.id) setRecentGifs(getRecent(RECENT_GIFS_KEY)) onMediaSelect(content) } const handleStickerSelect = (sticker: any) => { const content = sticker.svg ? JSON.stringify({ sticker: sticker.svg, w: 200, h: 200, id: sticker.id }) : JSON.stringify({ sticker: sticker.emoji, w: 120, h: 120, id: sticker.id }) addRecent(RECENT_STICKERS_KEY, sticker.id) setRecentStickers(getRecent(RECENT_STICKERS_KEY)) onMediaSelect(content) } const activeStickers = stickerPacks.find((p) => p.id === activePack)?.stickers || [] const renderGifGrid = (items: any[], emptyMsg: string) => { if (items.length === 0) { return (
{emptyMsg}
) } return (
{items.map((gif: any) => ( ))}
) } return (
{/* Tabs */}
{/* Emoji Tab */} {tab === "emoji" && (
{ onEmojiSelect(emoji.native); onClose() }} theme={theme === "dark" ? "dark" : "light"} previewPosition="none" skinTonePosition="none" set="native" emojiSize={32} maxFrequentRows={2} perLine={8} />
)} {/* GIF Tab */} {tab === "gif" && (
setGifQuery(e.target.value)} placeholder="Search GIFs..." className="h-8 pl-8 text-sm" /> {gifQuery && ( )}
{gifLoading && gifResults.length === 0 ? (
) : gifUnavailable ? (
GIFs are temporarily unavailable.
) : gifError ? (
{gifError}
) : gifQuery.trim() ? ( renderGifGrid(gifResults, "No GIFs found") ) : recentGifs.length > 0 && gifResults.length === 0 ? ( <>
Recent GIFs
{renderGifGrid( recentGifs.map((id) => gifResults.find((g: any) => g.id === id)).filter(Boolean), "No recent GIFs" )} ) : ( <>
Trending
{renderGifGrid(gifResults, "No trending GIFs available")} )} {gifNext && !gifLoading && (
)}
)} {/* Stickers Tab */} {tab === "sticker" && (
{stickerPacks.map((pack) => ( ))}
{activeStickers.map((sticker) => ( ))}
{recentStickers.length > 0 && ( <>
Recently Used
{recentStickers.map((id) => { const s = stickerPacks.flatMap((p) => p.stickers).find((x) => x.id === id) if (!s) return null return ( ) })}
)}
)}
) }