import React, { useState, useEffect } from "react"; import { Button, HStack, Text, Box, IconButton, Image, useBreakpointValue, } from "@chakra-ui/react"; import AiBigIcon from "~/assets/icons/ai-big.svg"; import CloseIcon from "~/assets/icons/close.svg"; import { useChatContext } from "@/providers/chat/chat-context"; import { shouldTriggerFabAnimation, saveFabAnimationTimestamp, } from "~/utils/session-storage"; import { getPositionConfig } from "~/utils/positioning"; import { SupportChannelMenu } from "../support-channel-menu"; interface FloatingActionButtonProps { onClick?: () => void; } const FloatingActionButton: React.FC = ({ onClick, }) => { const { botData, dynamicWelcomeMessage, isLoadingWelcomeMessage } = useChatContext(); const [buttonVariant, setButtonVariant] = useState<"expanded" | "minimized">( "expanded" ); const [isVisible, setIsVisible] = useState(false); const [isTextBubbleVisible, setIsTextBubbleVisible] = useState(false); const [shouldAnimate, setShouldAnimate] = useState(false); const isMobile = useBreakpointValue({ base: true, md: false }) ?? false; const positionConfig = getPositionConfig(botData, isMobile); const askFromMoriText = botData?.agent_name ? `از ${botData?.agent_name} بپرس!` : "از موری بپرس!"; const [isMenuOpen, setIsMenuOpen] = useState(false); const [isHovering, setIsHovering] = useState(false); const menuCloseTimer = React.useRef(null); const clearCloseTimer = () => { if (menuCloseTimer.current) { clearTimeout(menuCloseTimer.current); menuCloseTimer.current = null; } }; const openMenu = () => { clearCloseTimer(); if (!isMobile) { setIsMenuOpen(true); setIsHovering(true); } }; const closeMenu = () => { if (!isMobile) { clearCloseTimer(); menuCloseTimer.current = setTimeout(() => { setIsMenuOpen(false); setIsHovering(false); }, 100); } }; useEffect(() => { return () => clearCloseTimer(); }, []); // Check if widget_test mode is enabled const isWidgetTestMode = botData?.widget_test === true; // Static welcome message (original behavior) const hasStaticWelcomeMessage = botData?.wellcomeMessage && botData.wellcomeMessage.trim().length > 0; const staticWelcomeMessageText = botData?.wellcomeMessage; // Determine which welcome message to use based on mode const welcomeMessageText = isWidgetTestMode ? dynamicWelcomeMessage : staticWelcomeMessageText; // ============================================ // ORIGINAL BEHAVIOR (widget_test = false) // ============================================ const triggerAnimationCycle = () => { saveFabAnimationTimestamp(); setButtonVariant("expanded"); setIsTextBubbleVisible(false); setIsVisible(true); if (hasStaticWelcomeMessage) { const showDelay = 100; const stayDuration = 5000; const bubbleTimer = setTimeout(() => { setIsTextBubbleVisible(true); const minimizeTimer = setTimeout(() => { setIsTextBubbleVisible(false); setTimeout(() => { setButtonVariant("minimized"); }, 100); }, stayDuration); return () => clearTimeout(minimizeTimer); }, showDelay); return () => clearTimeout(bubbleTimer); } else { const minimizeTimer = setTimeout(() => { setButtonVariant("minimized"); }, 3000); return () => clearTimeout(minimizeTimer); } }; const startMinimized = () => { setButtonVariant("minimized"); setIsTextBubbleVisible(false); setIsVisible(true); }; // Original behavior effect - only runs when widget_test is false useEffect(() => { if (isWidgetTestMode) { return; // Skip original behavior when in test mode } if (botData === null) { return; } const shouldTrigger = shouldTriggerFabAnimation(); setShouldAnimate(true); if (shouldTrigger) { return triggerAnimationCycle(); } else { return startMinimized(); } }, [botData, isWidgetTestMode]); // Original periodic check - only runs when widget_test is false useEffect(() => { if (isWidgetTestMode) { return; // Skip when in test mode } const timer = setInterval(() => { if (shouldTriggerFabAnimation()) { triggerAnimationCycle(); } }, 30000); return () => clearInterval(timer); }, [shouldAnimate, isWidgetTestMode]); // ============================================ // NEW BEHAVIOR (widget_test = true) // ============================================ // Show widget when botData is loaded (widget_test mode) // Behavior should be same as non-test mode: show expanded, then minimize after 3s useEffect(() => { if (!isWidgetTestMode) { return; // Skip when not in test mode } if (botData) { setButtonVariant("expanded"); setIsVisible(true); // Minimize after 3 seconds (same as non-test mode without welcome message) const minimizeTimer = setTimeout(() => { setButtonVariant("minimized"); }, 3000); return () => clearTimeout(minimizeTimer); } }, [botData, isWidgetTestMode]); // Show welcome message bubble when dynamic message is available (widget_test mode) // This runs independently - shows bubble when message arrives, hides after 3.5s useEffect(() => { if (!isWidgetTestMode) { return; // Skip when not in test mode } if (!isLoadingWelcomeMessage && dynamicWelcomeMessage) { // Expand button and show bubble when message arrives setButtonVariant("expanded"); const showTimer = setTimeout(() => { setIsTextBubbleVisible(true); }, 300); const hideTimer = setTimeout(() => { setIsTextBubbleVisible(false); setTimeout(() => { setButtonVariant("minimized"); }, 300); }, 3500); return () => { clearTimeout(showTimer); clearTimeout(hideTimer); }; } }, [isLoadingWelcomeMessage, dynamicWelcomeMessage, isWidgetTestMode]); // ============================================ // SHARED HANDLERS // ============================================ const resetToExpanded = () => { if (buttonVariant === "minimized") { setIsTextBubbleVisible(false); setButtonVariant("expanded"); setTimeout(() => { setIsTextBubbleVisible(true); }, 300); } }; const handleClick = () => { if (onClick) { onClick(); if (!isWidgetTestMode) { resetToExpanded(); } } }; const handleCloseBubble = () => { setIsTextBubbleVisible(false); setTimeout(() => { setButtonVariant("minimized"); }, 300); }; // ============================================ // RENDER // ============================================ // Determine bubble render condition based on mode const shouldRenderBubble = isWidgetTestMode ? dynamicWelcomeMessage // New mode: render if dynamic message exists : buttonVariant === "expanded" && hasStaticWelcomeMessage; // Original: expanded + static message // Dynamic bubble for widget_test mode - slightly more polished const renderDynamicBubble = () => ( { e.stopPropagation(); handleCloseBubble(); }} color="#666" _hover={{ bg: "rgba(0,0,0,0.08)" }} h="auto" p={1} borderRadius="full" flexShrink={0} > Close {welcomeMessageText} ); // Original bubble for non-test mode const renderStaticBubble = () => ( Close {welcomeMessageText} ); return ( setIsMenuOpen(!isMenuOpen)} isMobile={isMobile} onMouseEnter={openMenu} onChatClick={onClick} /> {/* Welcome message bubble */} {shouldRenderBubble && ( isWidgetTestMode ? renderDynamicBubble() : renderStaticBubble() )} ); }; export default FloatingActionButton;