| 21 | } |
| 22 | |
| 23 | export function useEasterEggs({ |
| 24 | onSpecialTranslation, |
| 25 | }: UseEasterEggsOptions = {}): UseEasterEggsReturn { |
| 26 | const [isChaos, setIsChaos] = useState(false); |
| 27 | const [activeGif, setActiveGif] = useState<string | null>(null); |
| 28 | |
| 29 | const konamiSequence = useRef<string[]>([]); |
| 30 | const specialSequence = useRef<string>(""); |
| 31 | const gifTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null); |
| 32 | |
| 33 | const showGif = useCallback((gif: string) => { |
| 34 | if (gifTimeoutRef.current) { |
| 35 | clearTimeout(gifTimeoutRef.current); |
| 36 | } |
| 37 | setActiveGif(gif); |
| 38 | gifTimeoutRef.current = setTimeout(() => { |
| 39 | setActiveGif(null); |
| 40 | gifTimeoutRef.current = null; |
| 41 | }, 3000); |
| 42 | }, []); |
| 43 | |
| 44 | const toggleChaos = useCallback(() => setIsChaos((p) => !p), []); |
| 45 | |
| 46 | const clearGif = useCallback(() => { |
| 47 | if (gifTimeoutRef.current) { |
| 48 | clearTimeout(gifTimeoutRef.current); |
| 49 | gifTimeoutRef.current = null; |
| 50 | } |
| 51 | setActiveGif(null); |
| 52 | }, []); |
| 53 | |
| 54 | useEffect(() => { |
| 55 | return () => { |
| 56 | if (gifTimeoutRef.current) { |
| 57 | clearTimeout(gifTimeoutRef.current); |
| 58 | } |
| 59 | }; |
| 60 | }, []); |
| 61 | |
| 62 | useEffect(() => { |
| 63 | const isInEditor = () => { |
| 64 | const el = document.activeElement; |
| 65 | return ( |
| 66 | el?.tagName === "INPUT" || |
| 67 | el?.tagName === "TEXTAREA" || |
| 68 | el?.getAttribute("contenteditable") === "true" || |
| 69 | el?.closest('[contenteditable="true"]') !== null |
| 70 | ); |
| 71 | }; |
| 72 | |
| 73 | const handleKeyDown = (e: KeyboardEvent) => { |
| 74 | if (isInEditor()) return; |
| 75 | |
| 76 | const key = e.key.toLowerCase(); |
| 77 | |
| 78 | const expectedKey = |
| 79 | KONAMI_CODE[konamiSequence.current.length]?.toLowerCase(); |
| 80 | |