import { useState, useEffect, useRef } from "react"; import { Box, Avatar as MantineAvatar } from "@mantine/core"; interface GlitchAvatarProps { name: string; src?: string; glitchSrc?: string; size?: number; radius?: string | number; withBorder?: boolean; children?: React.ReactNode; } const FRAME_PADDING = 8; const toCssRadius = (radius: string | number) => typeof radius === "number" ? `${radius}px` : `var(--mantine-radius-${radius})`; const GlitchAvatar = ({ name, src, glitchSrc, size = 35, radius = "md", withBorder = true, children, }: GlitchAvatarProps) => { const [showGlitch, setShowGlitch] = useState(false); const [isPlaying, setIsPlaying] = useState(false); const videoRef = useRef(null); useEffect(() => { if (!glitchSrc) return; if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return; let timeoutId: ReturnType; const scheduleNextGlitch = () => { const delay = Math.random() * 10000 + 5000; timeoutId = setTimeout(() => { setShowGlitch(true); setIsPlaying(true); timeoutId = setTimeout(() => { setShowGlitch(false); setIsPlaying(false); scheduleNextGlitch(); }, 4000); }, delay); }; scheduleNextGlitch(); return () => clearTimeout(timeoutId); }, [glitchSrc]); useEffect(() => { const video = videoRef.current; if (!video) return; const handleEnded = () => { setShowGlitch(false); setIsPlaying(false); }; video.addEventListener("ended", handleEnded); return () => video.removeEventListener("ended", handleEnded); }, []); useEffect(() => { const video = videoRef.current; if (!video) return; video.load(); }, [glitchSrc]); useEffect(() => { const video = videoRef.current; if (!video || !showGlitch || !isPlaying) return; video.currentTime = 0; video.play().catch((err) => { console.error("Failed to play glitch", err); }); }, [showGlitch, isPlaying]); const innerRadius = toCssRadius(radius); return ( {src ? ( {name} {glitchSrc && ( ) : ( {children} )} ); }; export default GlitchAvatar;