import { CreateLinkEvent, Dispose, DropBounce, EaseOut, GetSessionDataManager, JumpBy, Motions, NitroToolbarAnimateIconEvent, PerkAllowancesMessageEvent, PerkEnum, Queue, Wait, YouTubeRoomSettingsEvent } from '@nitrots/nitro-renderer'; import { AnimatePresence, motion } from 'framer-motion'; import { FC, useEffect, useState } from 'react'; import { GetConfigurationValue, MessengerIconState, OpenMessengerChat, setYoutubeRoomEnabled, VisitDesktop } from '../../api'; import { Flex, LayoutAvatarImageView, LayoutItemCountView } from '../../common'; import { useAchievements, useFriends, useInventoryUnseenTracker, useMessageEvent, useMessenger, useNitroEvent, useSessionInfo, useWiredTools } from '../../hooks'; import { ToolbarItemView } from './ToolbarItemView'; import { ToolbarMeView } from './ToolbarMeView'; import { YouTubePlayerView } from './YouTubePlayerView'; const containerVariants = { hidden: {}, visible: { transition: { staggerChildren: 0.05 } }, exit: { transition: { staggerChildren: 0.03, staggerDirection: -1 as const } } }; const itemVariants = { hidden: { opacity: 0, y: 10, scale: 0.8 }, visible: { opacity: 1, y: 0, scale: 1, transition: { type: 'spring', stiffness: 400, damping: 22 } }, exit: { opacity: 0, y: 6, scale: 0.85, transition: { duration: 0.1 } } }; export const ToolbarView: FC<{ isInRoom: boolean }> = props => { const { isInRoom } = props; const [ isMeExpanded, setMeExpanded ] = useState(false); const [ isToolbarOpen, setIsToolbarOpen ] = useState(false); const [ isTouchLayout, setIsTouchLayout ] = useState(false); const [ useGuideTool, setUseGuideTool ] = useState(false); const [ youtubeEnabled, setYoutubeEnabled ] = useState(false); const { userFigure = null } = useSessionInfo(); const { getFullCount = 0 } = useInventoryUnseenTracker(); const { getTotalUnseen = 0 } = useAchievements(); const { requests = [] } = useFriends(); const { iconState = MessengerIconState.HIDDEN } = useMessenger(); const { openMonitor, showToolbarButton } = useWiredTools(); const isMod = GetSessionDataManager().isModerator; const hasDesktopUnifiedShell = (isInRoom && isToolbarOpen); const showDesktopShell = (isToolbarOpen || !isInRoom); const desktopToolbarFrameClasses = isTouchLayout ? '' : 'md:left-1/2 md:right-auto md:h-[52px] md:w-[420px] md:-translate-x-1/2 md:items-center md:px-[6px] md:py-[4px] lg:w-[460px]'; const desktopToolbarOpenClasses = isTouchLayout ? '' : 'md:rounded-none md:border-0 md:bg-transparent md:shadow-none'; const desktopToggleClasses = isTouchLayout ? '' : 'md:mb-0'; const desktopToggleIconClasses = isTouchLayout ? '' : (isToolbarOpen ? 'md:-rotate-90' : 'md:rotate-90'); const desktopChatInputClasses = isTouchLayout ? '' : 'md:px-0'; const mobileOnlyClasses = isTouchLayout ? '' : 'md:hidden'; const desktopBlockClasses = isTouchLayout ? 'hidden' : 'hidden md:block'; const desktopFlexClasses = isTouchLayout ? 'hidden' : 'hidden md:flex'; useMessageEvent(YouTubeRoomSettingsEvent, event => { const enabled = event.getParser().youtubeEnabled; setYoutubeEnabled(enabled); setYoutubeRoomEnabled(enabled); }); useEffect(() => { if(!isInRoom) { setYoutubeEnabled(false); setYoutubeRoomEnabled(false); } }, [ isInRoom ]); useEffect(() => { const query = window.matchMedia('(pointer: coarse), (hover: none)'); const updateTouchLayout = () => setIsTouchLayout(query.matches); updateTouchLayout(); query.addEventListener('change', updateTouchLayout); return () => query.removeEventListener('change', updateTouchLayout); }, []); const openYouTubePlayer = () => window.dispatchEvent(new CustomEvent('youtube:toggle')); useMessageEvent(PerkAllowancesMessageEvent, event => { setUseGuideTool(event.getParser().isAllowed(PerkEnum.USE_GUIDE_TOOL)); }); useNitroEvent(NitroToolbarAnimateIconEvent.ANIMATE_ICON, event => { const animationIconToToolbar = (iconName: string, image: HTMLImageElement, x: number, y: number) => { const target = (document.body.getElementsByClassName(iconName)[0] as HTMLElement); if(!target) return; image.className = 'toolbar-icon-animation'; image.style.visibility = 'visible'; image.style.left = (x + 'px'); image.style.top = (y + 'px'); document.body.append(image); const targetBounds = target.getBoundingClientRect(); const imageBounds = image.getBoundingClientRect(); const left = (imageBounds.x - targetBounds.x); const top = (imageBounds.y - targetBounds.y); const squared = Math.sqrt(((left * left) + (top * top))); const wait = (500 - Math.abs(((((1 / squared) * 100) * 500) * 0.5))); const height = 20; const motionName = (`ToolbarBouncing[${ iconName }]`); if(!Motions.getMotionByTag(motionName)) { Motions.runMotion(new Queue(new Wait((wait + 8)), new DropBounce(target, 400, 12))).tag = motionName; } const motion = new Queue(new EaseOut(new JumpBy(image, wait, ((targetBounds.x - imageBounds.x) + height), (targetBounds.y - imageBounds.y), 100, 1), 1), new Dispose(image)); Motions.runMotion(motion); }; animationIconToToolbar('icon-inventory', event.image, event.x, event.y); }); return ( <> { youtubeEnabled && } { isInRoom &&
setIsToolbarOpen(value => !value) } whileTap={ { scale: 0.9 } }>
CreateLinkEvent('friends/toggle') } className="tb-icon" /> { (requests.length > 0) && }
} { (isToolbarOpen || !isInRoom) && <> { showDesktopShell && } { isInRoom ? VisitDesktop() } className="tb-icon" /> : CreateLinkEvent('navigator/goto/home') } className="tb-icon" /> } CreateLinkEvent('navigator/toggle') } className="tb-icon" /> { GetConfigurationValue('game.center.enabled') && CreateLinkEvent('games/toggle') } className="tb-icon" /> } CreateLinkEvent('catalog/toggle/normal') } className="tb-icon" /> CreateLinkEvent('catalog/toggle/builder') } className="tb-icon" /> CreateLinkEvent('inventory/toggle') } className="tb-icon" /> { (getFullCount > 0) && } { isMeExpanded && } { setMeExpanded(value => !value); event.stopPropagation(); } }> { (getTotalUnseen > 0) && } { (isInRoom && showToolbarButton) && } { isInRoom && CreateLinkEvent('camera/toggle') } className="tb-icon" /> } { (isInRoom && youtubeEnabled) && } { isMod && CreateLinkEvent('mod-tools/toggle') } className="tb-icon" /> } { isMod && CreateLinkEvent('furni-editor/toggle') } className="tb-icon" /> } CreateLinkEvent('friends/toggle') } className="tb-icon" /> { (requests.length > 0) && } { ((iconState === MessengerIconState.SHOW) || (iconState === MessengerIconState.UNREAD)) && OpenMessengerChat() } /> }
{ isInRoom ? VisitDesktop() } className="tb-icon" /> : CreateLinkEvent('navigator/goto/home') } className="tb-icon" /> } CreateLinkEvent('navigator/toggle') } className="tb-icon" /> { GetConfigurationValue('game.center.enabled') && CreateLinkEvent('games/toggle') } className="tb-icon" /> } CreateLinkEvent('catalog/toggle/normal') } className="tb-icon" /> CreateLinkEvent('catalog/toggle/builder') } className="tb-icon" /> CreateLinkEvent('inventory/toggle') } className="tb-icon" /> { (getFullCount > 0) && } { isMeExpanded && } { setMeExpanded(value => !value); event.stopPropagation(); } }> { (getTotalUnseen > 0) && } { (isInRoom && showToolbarButton) && } { isInRoom && CreateLinkEvent('camera/toggle') } className="tb-icon" /> } { (isInRoom && youtubeEnabled) && } { isMod && CreateLinkEvent('mod-tools/toggle') } className="tb-icon" /> } { isMod && CreateLinkEvent('furni-editor/toggle') } className="tb-icon" /> } { !isInRoom && CreateLinkEvent('friends/toggle') } className="tb-icon" /> { (requests.length > 0) && } } } ); }; const TOOLBAR_STYLES = ` .tb-icon { opacity: 1; transition: transform 0.15s ease; cursor: pointer; } .tb-icon:hover { transform: translateY(-2px); } .tb-icon:active { transform: translateY(0); } .tb-toggle { width: 32px; height: 32px; flex-shrink: 0; border-radius: 9px; background: rgba(18, 16, 14, 0.80); backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); border: 1px solid rgba(255, 255, 255, 0.08); display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 3px 12px rgba(0, 0, 0, 0.5); transition: background 0.15s, border-color 0.15s; } .tb-toggle:hover { background: rgba(30, 26, 20, 0.88); border-color: rgba(255, 255, 255, 0.13); } .tb-bar-scroll { overflow-x: auto; overflow-y: visible; scrollbar-width: none; -ms-overflow-style: none; flex-wrap: nowrap; } .tb-bar-scroll::-webkit-scrollbar { display: none; } .tb-open-shell { scrollbar-width: none; -ms-overflow-style: none; } .tb-open-shell::-webkit-scrollbar { display: none; } `;