diff --git a/src/components/left/main/ChatBadge.module.scss b/src/components/left/main/ChatBadge.module.scss index 6d970f888..04f26f99c 100644 --- a/src/components/left/main/ChatBadge.module.scss +++ b/src/components/left/main/ChatBadge.module.scss @@ -20,6 +20,8 @@ } .transition { + transform-origin: center; + display: flex; align-items: center; justify-content: center; @@ -28,7 +30,7 @@ opacity: 1; - transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); + transition: transform 0.15s ease-out, opacity 0.15s ease-out; &:not(:global(.open)) { transform: scale(0); @@ -38,12 +40,6 @@ &:not(:global(.shown)) { display: none; } - - &:global(.closing) { - transition: - transform 0.2s ease-out, - opacity 0.2s ease-out; - } } .wrapper { diff --git a/src/components/left/main/ChatBadge.tsx b/src/components/left/main/ChatBadge.tsx index d1add24d1..1b9b18a9a 100644 --- a/src/components/left/main/ChatBadge.tsx +++ b/src/components/left/main/ChatBadge.tsx @@ -18,6 +18,7 @@ import useSelector, { useShallowSelector } from '../../../hooks/data/useSelector import useDerivedState from '../../../hooks/useDerivedState'; import useLang from '../../../hooks/useLang'; import useLastCallback from '../../../hooks/useLastCallback'; +import useShowTransitionSwap from '../../../hooks/useShowTransitionSwap'; import AnimatedCounter from '../../common/AnimatedCounter'; import Icon from '../../common/icons/Icon'; @@ -153,6 +154,12 @@ const ChatBadge = ({ || isTopicUnopened || hasMiniApp, ); + const contentCategory = (isUnread || unreadMentionsCount || unreadReactionsCount + || unreadPollVotesCount || isTopicUnopened) + ? 'active' + : (hasMiniApp ? 'miniapp' : (isPinned ? 'pinned' : 'none')); + const effectiveIsOpen = useShowTransitionSwap(isShown, contentCategory); + const handleOpenApp = useLastCallback((e: React.MouseEvent) => { e.stopPropagation(); @@ -263,7 +270,7 @@ const ChatBadge = ({ isOnAvatar && styles.onAvatar, transitionClassName, )} - isOpen={isShown} + isOpen={effectiveIsOpen} > {renderContent()} diff --git a/src/hooks/useShowTransitionSwap.ts b/src/hooks/useShowTransitionSwap.ts new file mode 100644 index 000000000..f79482f6e --- /dev/null +++ b/src/hooks/useShowTransitionSwap.ts @@ -0,0 +1,31 @@ +import { useRef } from '../lib/teact/teact'; + +import useForceUpdate from './useForceUpdate'; +import usePreviousDeprecated from './usePreviousDeprecated'; +import useSyncEffect from './useSyncEffect'; + +const SWAP_CLOSE_DURATION = 150; + +export default function useShowTransitionSwap(isOpen: boolean, contentKey?: string): boolean { + const prevIsOpen = usePreviousDeprecated(isOpen); + const prevContentKey = usePreviousDeprecated(contentKey); + const isSwappingRef = useRef(false); + const forceUpdate = useForceUpdate(); + + if (isOpen && prevIsOpen + && contentKey !== undefined && prevContentKey !== undefined + && prevContentKey !== contentKey && !isSwappingRef.current) { + isSwappingRef.current = true; + } + + useSyncEffect(() => { + if (!isSwappingRef.current) return undefined; + const timer = window.setTimeout(() => { + isSwappingRef.current = false; + forceUpdate(); + }, SWAP_CLOSE_DURATION); + return () => clearTimeout(timer); + }, [contentKey]); + + return isOpen && !isSwappingRef.current; +}