diff --git a/src/components/common/ProfileInfo.tsx b/src/components/common/ProfileInfo.tsx index d3f6f4ca2..099ddd1d8 100644 --- a/src/components/common/ProfileInfo.tsx +++ b/src/components/common/ProfileInfo.tsx @@ -5,6 +5,7 @@ import { getActions, withGlobal } from '../../global'; import type { ApiChat, ApiPeerPhotos, ApiSticker, ApiTopic, ApiUser, ApiUserStatus, } from '../../api/types'; +import type { AnimationLevel } from '../../types'; import { MediaViewerOrigin } from '../../types'; import { @@ -20,10 +21,12 @@ import { selectUser, selectUserStatus, } from '../../global/selectors'; +import { selectSharedSettings } from '../../global/selectors/sharedState.ts'; import { IS_TOUCH_ENV } from '../../util/browser/windowEnvironment'; import buildClassName from '../../util/buildClassName'; import { captureEvents, SwipeDirection } from '../../util/captureEvents'; import { MEMO_EMPTY_ARRAY } from '../../util/memo'; +import { resolveTransitionName } from '../../util/resolveTransitionName.ts'; import renderText from './helpers/renderText'; import useIntervalForceUpdate from '../../hooks/schedulers/useIntervalForceUpdate'; @@ -58,6 +61,7 @@ type StateProps = avatarOwnerId?: string; topic?: ApiTopic; messagesCount?: number; + animationLevel: AnimationLevel; emojiStatusSticker?: ApiSticker; emojiStatusSlug?: string; profilePhotos?: ApiPeerPhotos; @@ -79,6 +83,7 @@ const ProfileInfo: FC = ({ avatarOwnerId, topic, messagesCount, + animationLevel, emojiStatusSticker, emojiStatusSlug, profilePhotos, @@ -103,8 +108,6 @@ const ProfileInfo: FC = ({ const prevMediaIndex = usePreviousDeprecated(mediaIndex); const prevAvatarOwnerId = usePreviousDeprecated(avatarOwnerId); const [hasSlideAnimation, setHasSlideAnimation] = useState(true); - // slideOptimized doesn't work well when animation is dynamically disabled - const slideAnimation = hasSlideAnimation ? (oldLang.isRtl ? 'slideRtl' : 'slide') : 'none'; const [currentPhotoIndex, setCurrentPhotoIndex] = useState(0); const isFirst = photos.length <= 1 || currentPhotoIndex === 0; @@ -349,7 +352,10 @@ const ProfileInfo: FC = ({ )} - + {renderPhoto} @@ -400,6 +406,7 @@ export default memo(withGlobal( const isForum = chat?.isForum; const { threadId: currentTopicId } = selectCurrentMessageList(global) || {}; const topic = isForum && currentTopicId ? selectTopic(global, peerId, currentTopicId) : undefined; + const { animationLevel } = selectSharedSettings(global); const emojiStatus = (user || chat)?.emojiStatus; const emojiStatusSticker = emojiStatus ? global.customEmojis.byId[emojiStatus.documentId] : undefined; @@ -411,6 +418,7 @@ export default memo(withGlobal( chat, mediaIndex, avatarOwnerId, + animationLevel, emojiStatusSticker, emojiStatusSlug, profilePhotos, diff --git a/src/components/left/LeftColumn.tsx b/src/components/left/LeftColumn.tsx index e41f58027..4244d1fb9 100644 --- a/src/components/left/LeftColumn.tsx +++ b/src/components/left/LeftColumn.tsx @@ -1,22 +1,24 @@ import type { - ElementRef } from '../../lib/teact/teact'; + ElementRef } from '@teact'; import { memo, useEffect, useMemo, useState, -} from '../../lib/teact/teact'; +} from '@teact'; import { getActions, withGlobal } from '../../global'; import type { GlobalState } from '../../global/types'; import type { FoldersActions } from '../../hooks/reducers/useFoldersReducer'; import type { ReducerAction } from '../../hooks/useReducer'; -import { LeftColumnContent, SettingsScreens } from '../../types'; +import { type AnimationLevel, LeftColumnContent, SettingsScreens } from '../../types'; import { selectCurrentChat, selectIsCurrentUserFrozen, selectIsForumPanelOpen, selectTabState, } from '../../global/selectors'; +import { selectSharedSettings } from '../../global/selectors/sharedState.ts'; import { - IS_APP, IS_FIREFOX, IS_MAC_OS, IS_TOUCH_ENV, LAYERS_ANIMATION_NAME, + IS_APP, IS_FIREFOX, IS_MAC_OS, IS_TOUCH_ENV, } from '../../util/browser/windowEnvironment'; import captureEscKeyListener from '../../util/captureEscKeyListener'; +import { resolveTransitionName } from '../../util/resolveTransitionName.ts'; import { debounce } from '../../util/schedulers'; import { captureControlledSwipe } from '../../util/swipeController'; @@ -45,6 +47,7 @@ type StateProps = { searchQuery?: string; searchDate?: number; isFirstChatFolderActive: boolean; + animationLevel: AnimationLevel; shouldSkipHistoryAnimations?: boolean; currentUserId?: string; hasPasscode?: boolean; @@ -81,6 +84,7 @@ function LeftColumn({ searchQuery, searchDate, isFirstChatFolderActive, + animationLevel, shouldSkipHistoryAnimations, currentUserId, hasPasscode, @@ -501,6 +505,7 @@ function LeftColumn({ currentScreen={settingsScreen} foldersState={foldersState} foldersDispatch={foldersDispatch} + animationLevel={animationLevel} shouldSkipTransition={shouldSkipHistoryAnimations} onReset={handleReset} /> @@ -512,6 +517,7 @@ function LeftColumn({ isActive={isActive} isChannel content={contentKey} + animationLevel={animationLevel} onReset={handleReset} /> ); @@ -521,6 +527,7 @@ function LeftColumn({ key={lastResetTime} isActive={isActive} content={contentKey} + animationLevel={animationLevel} onReset={handleReset} /> ); @@ -549,7 +556,7 @@ function LeftColumn({ return ( ( archiveSettings, } = global; + const { animationLevel } = selectSharedSettings(global); const currentChat = selectCurrentChat(global); const isChatOpen = Boolean(currentChat?.id); const isForumPanelOpen = selectIsForumPanelOpen(global); @@ -600,6 +608,7 @@ export default memo(withGlobal( searchQuery: query, searchDate: minDate, isFirstChatFolderActive: activeChatFolder === 0, + animationLevel, shouldSkipHistoryAnimations, currentUserId, hasPasscode, diff --git a/src/components/left/main/ChatFolders.tsx b/src/components/left/main/ChatFolders.tsx index 4d1e86489..3c5f3b8c7 100644 --- a/src/components/left/main/ChatFolders.tsx +++ b/src/components/left/main/ChatFolders.tsx @@ -1,12 +1,11 @@ -import type { FC } from '../../../lib/teact/teact'; -import { - memo, useEffect, useMemo, useRef, -} from '../../../lib/teact/teact'; +import type { FC } from '@teact'; +import { memo, useEffect, useMemo, useRef } from '@teact'; import { getActions, getGlobal, withGlobal } from '../../../global'; import type { ApiChatFolder, ApiChatlistExportedInvite, ApiSession } from '../../../api/types'; import type { GlobalState } from '../../../global/types'; import type { FolderEditDispatch } from '../../../hooks/reducers/useFoldersReducer'; +import type { AnimationLevel } from '../../../types'; import type { MenuItemContextAction } from '../../ui/ListItem'; import type { TabWithProperties } from '../../ui/TabList'; import { SettingsScreens } from '../../../types'; @@ -14,11 +13,13 @@ import { SettingsScreens } from '../../../types'; import { ALL_FOLDER_ID } from '../../../config'; import { selectCanShareFolder, selectIsCurrentUserFrozen, selectTabState } from '../../../global/selectors'; import { selectCurrentLimit } from '../../../global/selectors/limits'; +import { selectSharedSettings } from '../../../global/selectors/sharedState.ts'; import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment'; import buildClassName from '../../../util/buildClassName'; import captureEscKeyListener from '../../../util/captureEscKeyListener'; import { captureEvents, SwipeDirection } from '../../../util/captureEvents'; import { MEMO_EMPTY_ARRAY } from '../../../util/memo'; +import { resolveTransitionName } from '../../../util/resolveTransitionName.ts'; import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities'; import useDerivedState from '../../../hooks/useDerivedState'; @@ -48,6 +49,7 @@ type StateProps = { orderedFolderIds?: number[]; activeChatFolder: number; currentUserId?: string; + animationLevel: AnimationLevel; shouldSkipHistoryAnimations?: boolean; maxFolders: number; maxChatLists: number; @@ -70,6 +72,7 @@ const ChatFolders: FC = ({ activeChatFolder, currentUserId, isForumPanelOpen, + animationLevel, shouldSkipHistoryAnimations, maxFolders, maxChatLists, @@ -393,7 +396,7 @@ const ChatFolders: FC = ({ ) : undefined} @@ -427,6 +430,7 @@ export default memo(withGlobal( currentUserId, archiveSettings, } = global; + const { animationLevel } = selectSharedSettings(global); const { shouldSkipHistoryAnimations, activeChatFolder } = selectTabState(global); const { storyViewer: { isRibbonShown: isStoryRibbonShown } } = selectTabState(global); const isAccountFrozen = selectIsCurrentUserFrozen(global); @@ -437,6 +441,7 @@ export default memo(withGlobal( orderedFolderIds, activeChatFolder, currentUserId, + animationLevel, shouldSkipHistoryAnimations, hasArchivedChats: Boolean(archived?.length), hasArchivedStories: Boolean(archivedStories?.length), diff --git a/src/components/left/main/LeftSideMenuItems.tsx b/src/components/left/main/LeftSideMenuItems.tsx index 9b911b2b9..b341ada52 100644 --- a/src/components/left/main/LeftSideMenuItems.tsx +++ b/src/components/left/main/LeftSideMenuItems.tsx @@ -19,7 +19,7 @@ import { } from '../../../config'; import { INITIAL_PERFORMANCE_STATE_MAX, - INITIAL_PERFORMANCE_STATE_MID, + INITIAL_PERFORMANCE_STATE_MED, INITIAL_PERFORMANCE_STATE_MIN, } from '../../../global/initialState'; import { selectTabState, selectTheme, selectUser } from '../../../global/selectors'; @@ -119,7 +119,7 @@ const LeftSideMenuItems = ({ } const performanceSettings = newLevel === ANIMATION_LEVEL_MIN ? INITIAL_PERFORMANCE_STATE_MIN - : (newLevel === ANIMATION_LEVEL_MAX ? INITIAL_PERFORMANCE_STATE_MAX : INITIAL_PERFORMANCE_STATE_MID); + : (newLevel === ANIMATION_LEVEL_MAX ? INITIAL_PERFORMANCE_STATE_MAX : INITIAL_PERFORMANCE_STATE_MED); setSharedSettingOption({ animationLevel: newLevel as AnimationLevel }); updatePerformanceSettings(performanceSettings); diff --git a/src/components/left/newChat/NewChat.tsx b/src/components/left/newChat/NewChat.tsx index 0ce4abafb..01cf181f8 100644 --- a/src/components/left/newChat/NewChat.tsx +++ b/src/components/left/newChat/NewChat.tsx @@ -1,10 +1,10 @@ -import type { FC } from '../../../lib/teact/teact'; -import { memo, useCallback, useState } from '../../../lib/teact/teact'; +import type { FC } from '@teact'; +import { memo, useCallback, useState } from '@teact'; import { getActions } from '../../../global'; -import { LeftColumnContent } from '../../../types'; +import { type AnimationLevel, LeftColumnContent } from '../../../types'; -import { LAYERS_ANIMATION_NAME } from '../../../util/browser/windowEnvironment'; +import { resolveTransitionName } from '../../../util/resolveTransitionName.ts'; import useLastCallback from '../../../hooks/useLastCallback.ts'; @@ -18,6 +18,7 @@ export type OwnProps = { isActive: boolean; isChannel?: boolean; content: LeftColumnContent; + animationLevel: AnimationLevel; onReset: () => void; }; @@ -27,6 +28,7 @@ const NewChat: FC = ({ isActive, isChannel = false, content, + animationLevel, onReset, }) => { const { openLeftColumnContent, setGlobalSearchQuery } = getActions(); @@ -50,7 +52,7 @@ const NewChat: FC = ({ return ( diff --git a/src/components/left/search/LeftSearch.tsx b/src/components/left/search/LeftSearch.tsx index e2fef4d01..62c717318 100644 --- a/src/components/left/search/LeftSearch.tsx +++ b/src/components/left/search/LeftSearch.tsx @@ -9,10 +9,12 @@ import { import { getActions, withGlobal } from '../../../global'; import type { RegularLangKey } from '../../../types/language'; -import { GlobalSearchContent } from '../../../types'; +import { type AnimationLevel, GlobalSearchContent } from '../../../types'; import { selectTabState } from '../../../global/selectors'; +import { selectSharedSettings } from '../../../global/selectors/sharedState.ts'; import { parseDateString } from '../../../util/dates/dateFormat'; +import { resolveTransitionName } from '../../../util/resolveTransitionName.ts'; import useHistoryBack from '../../../hooks/useHistoryBack'; import useKeyboardListNavigation from '../../../hooks/useKeyboardListNavigation'; @@ -42,6 +44,7 @@ export type OwnProps = { type StateProps = { currentContent?: GlobalSearchContent; chatId?: string; + animationLevel: AnimationLevel; }; type TabInfo = { @@ -72,6 +75,7 @@ const LeftSearch: FC = ({ isActive, currentContent = GlobalSearchContent.ChatList, chatId, + animationLevel, onReset, }) => { const { @@ -120,7 +124,7 @@ const LeftSearch: FC = ({
@@ -195,7 +199,8 @@ const LeftSearch: FC = ({ export default memo(withGlobal( (global): StateProps => { const { currentContent, chatId } = selectTabState(global).globalSearch; + const { animationLevel } = selectSharedSettings(global); - return { currentContent, chatId }; + return { currentContent, chatId, animationLevel }; }, )(LeftSearch)); diff --git a/src/components/left/search/PublicPostsResults.tsx b/src/components/left/search/PublicPostsResults.tsx index e8a9ab751..d5891d3d3 100644 --- a/src/components/left/search/PublicPostsResults.tsx +++ b/src/components/left/search/PublicPostsResults.tsx @@ -3,11 +3,14 @@ import { getActions, withGlobal } from '../../../global'; import { getGlobal } from '../../../global'; import type { ApiMessage, ApiSearchPostsFlood } from '../../../api/types'; +import type { AnimationLevel } from '../../../types'; import { LoadMoreDirection } from '../../../types'; import { selectTabState } from '../../../global/selectors'; +import { selectSharedSettings } from '../../../global/selectors/sharedState.ts'; import { parseSearchResultKey, type SearchResultKey } from '../../../util/keys/searchResultKey'; import { MEMO_EMPTY_ARRAY } from '../../../util/memo'; +import { resolveTransitionName } from '../../../util/resolveTransitionName.ts'; import { throttle } from '../../../util/schedulers'; import { renderMessageSummary } from '../../common/helpers/renderMessageText'; @@ -32,6 +35,7 @@ type StateProps = { shouldShowSearchLauncher?: boolean; isNothingFound?: boolean; isLoading?: boolean; + animationLevel: AnimationLevel; }; const runThrottled = throttle((cb) => cb(), 500, true); @@ -44,6 +48,7 @@ const PublicPostsResults = ({ shouldShowSearchLauncher, isNothingFound, isLoading, + animationLevel, }: OwnProps & StateProps) => { const { searchMessagesGlobal } = getActions(); @@ -104,7 +109,7 @@ const PublicPostsResults = ({ return ( {shouldShowSearchLauncher || isLoading ? ( @@ -149,12 +154,12 @@ export default memo(withGlobal( (global): StateProps => { const { messages: { byChatId: globalMessagesByChatId } } = global; const { resultsByType, searchFlood, fetchingStatus } = selectTabState(global).globalSearch; - const publicPostsResult = resultsByType?.publicPosts; const { foundIds } = publicPostsResult || {}; const isLoading = Boolean(fetchingStatus?.publicPosts && !publicPostsResult); const shouldShowSearchLauncher = !publicPostsResult && !isLoading; const isNothingFound = publicPostsResult && !foundIds?.length; + const { animationLevel } = selectSharedSettings(global); return { foundIds, @@ -163,6 +168,7 @@ export default memo(withGlobal( shouldShowSearchLauncher, isNothingFound, isLoading, + animationLevel, }; }, )(PublicPostsResults)); diff --git a/src/components/left/settings/Settings.tsx b/src/components/left/settings/Settings.tsx index a317bae6b..31b29faf2 100644 --- a/src/components/left/settings/Settings.tsx +++ b/src/components/left/settings/Settings.tsx @@ -1,12 +1,13 @@ -import type { FC } from '../../../lib/teact/teact'; -import { memo, useRef, useState } from '../../../lib/teact/teact'; +import type { FC } from '@teact'; +import { memo, useRef, useState } from '@teact'; import { getActions, getGlobal } from '../../../global'; import type { FolderEditDispatch, FoldersState } from '../../../hooks/reducers/useFoldersReducer'; +import type { AnimationLevel } from '../../../types'; import { SettingsScreens } from '../../../types'; import { selectTabState } from '../../../global/selectors'; -import { LAYERS_ANIMATION_NAME } from '../../../util/browser/windowEnvironment'; +import { resolveTransitionName } from '../../../util/resolveTransitionName.ts'; import useTwoFaReducer from '../../../hooks/reducers/useTwoFaReducer'; import useLastCallback from '../../../hooks/useLastCallback'; @@ -150,6 +151,7 @@ export type OwnProps = { currentScreen: SettingsScreens; foldersState: FoldersState; foldersDispatch: FolderEditDispatch; + animationLevel: AnimationLevel; shouldSkipTransition?: boolean; onReset: (forceReturnToChatList?: true | Event) => void; }; @@ -160,6 +162,7 @@ const Settings: FC = ({ foldersState, foldersDispatch, onReset, + animationLevel, shouldSkipTransition, }) => { const { closeShareChatFolderModal, openSettingsScreen } = getActions(); @@ -510,7 +513,7 @@ const Settings: FC = ({ { const performance = newLevel === ANIMATION_LEVEL_MIN ? INITIAL_PERFORMANCE_STATE_MIN - : (newLevel === ANIMATION_LEVEL_MED ? INITIAL_PERFORMANCE_STATE_MID : INITIAL_PERFORMANCE_STATE_MAX); + : (newLevel === ANIMATION_LEVEL_MED ? INITIAL_PERFORMANCE_STATE_MED : INITIAL_PERFORMANCE_STATE_MAX); setSharedSettingOption({ animationLevel: newLevel as AnimationLevel }); updatePerformanceSettings(performance); diff --git a/src/components/middle/MiddleColumn.tsx b/src/components/middle/MiddleColumn.tsx index 43cd5b727..92865281f 100644 --- a/src/components/middle/MiddleColumn.tsx +++ b/src/components/middle/MiddleColumn.tsx @@ -1,21 +1,10 @@ -import type { - ElementRef } from '../../lib/teact/teact'; -import type React from '../../lib/teact/teact'; -import { - memo, useEffect, useMemo, - useState, -} from '../../lib/teact/teact'; +import type React from '@teact'; +import type { ElementRef } from '@teact'; +import { memo, useEffect, useMemo, useState } from '@teact'; import { getActions, withGlobal } from '../../global'; -import type { - ApiChat, ApiChatBannedRights, ApiInputMessageReplyInfo, ApiTopic, -} from '../../api/types'; -import type { - ActiveEmojiInteraction, - MessageListType, - ThemeKey, - ThreadId, -} from '../../types'; +import type { ApiChat, ApiChatBannedRights, ApiInputMessageReplyInfo, ApiTopic } from '../../api/types'; +import type { ActiveEmojiInteraction, AnimationLevel, MessageListType, ThemeKey, ThreadId } from '../../types'; import { MAIN_THREAD_ID } from '../../api/types'; import { @@ -42,7 +31,7 @@ import { } from '../../global/helpers'; import { selectBot, - selectCanAnimateInterface, + selectCanAnimateInterface, selectCanAnimateRightColumn, selectChat, selectChatFullInfo, selectCurrentMessageList, @@ -65,13 +54,20 @@ import { selectTopics, selectUserFullInfo, } from '../../global/selectors'; +import { selectSharedSettings } from '../../global/selectors/sharedState.ts'; import { - IS_ANDROID, IS_ELECTRON, IS_IOS, IS_SAFARI, IS_TRANSLATION_SUPPORTED, MASK_IMAGE_DISABLED, + IS_ANDROID, + IS_ELECTRON, + IS_IOS, + IS_SAFARI, + IS_TRANSLATION_SUPPORTED, + MASK_IMAGE_DISABLED, } from '../../util/browser/windowEnvironment'; import buildClassName from '../../util/buildClassName'; import buildStyle from '../../util/buildStyle'; import captureEscKeyListener from '../../util/captureEscKeyListener'; import { isUserId } from '../../util/entities/ids'; +import { resolveTransitionName } from '../../util/resolveTransitionName.ts'; import calculateMiddleFooterTransforms from './helpers/calculateMiddleFooterTransforms'; import useAppLayout from '../../hooks/useAppLayout'; @@ -144,7 +140,9 @@ type StateProps = { isPrivacySettingsNoticeModalOpen: boolean; isReactorListModalOpen: boolean; isChatLanguageModalOpen?: boolean; + animationLevel: AnimationLevel; withInterfaceAnimations?: boolean; + withRightColumnAnimation?: boolean; shouldSkipHistoryAnimations?: boolean; currentTransitionKey: number; isChannel?: boolean; @@ -208,7 +206,9 @@ function MiddleColumn({ isPrivacySettingsNoticeModalOpen, isReactorListModalOpen, isChatLanguageModalOpen, + animationLevel, withInterfaceAnimations, + withRightColumnAnimation, shouldSkipHistoryAnimations, currentTransitionKey, isChannel, @@ -444,7 +444,7 @@ function MiddleColumn({ const bgClassName = buildClassName( styles.background, - styles.withTransition, + withRightColumnAnimation && styles.withTransition, customBackground && styles.customBgImage, backgroundColor && styles.customBgColor, customBackground && isBackgroundBlurred && styles.blurred, @@ -557,7 +557,11 @@ function MiddleColumn({ onFocusPinnedMessage={handleFocusPinnedMessage} /> ( isPrivacySettingsNoticeModalOpen: Boolean(privacySettingsNoticeModal), isReactorListModalOpen: Boolean(reactorModal), isChatLanguageModalOpen: Boolean(chatLanguageModal), + animationLevel: selectSharedSettings(global).animationLevel, withInterfaceAnimations: selectCanAnimateInterface(global), + withRightColumnAnimation: selectCanAnimateRightColumn(global), currentTransitionKey: Math.max(0, messageLists.length - 1), activeEmojiInteractions, leftColumnWidth, diff --git a/src/components/middle/MiddleHeaderPanes.module.scss b/src/components/middle/MiddleHeaderPanes.module.scss index 9fb4f91b8..e24e456f7 100644 --- a/src/components/middle/MiddleHeaderPanes.module.scss +++ b/src/components/middle/MiddleHeaderPanes.module.scss @@ -6,9 +6,6 @@ width: 100%; - /* stylelint-disable-next-line plugin/no-low-performance-animation-properties */ - transition: width var(--layer-transition); - // In case if there are no children, we need to have a shadow &::before { pointer-events: none; @@ -25,6 +22,11 @@ box-shadow: 0 2px 2px var(--color-light-shadow); } + &_withRightColumnAnimation { + /* stylelint-disable-next-line plugin/no-low-performance-animation-properties */ + transition: width var(--layer-transition); + } + /* stylelint-disable-next-line plugin/selector-tag-no-without-class */ > div:last-child { box-shadow: 0 2px 2px var(--color-light-shadow); diff --git a/src/components/middle/MiddleHeaderPanes.tsx b/src/components/middle/MiddleHeaderPanes.tsx index 238fef893..79597dc04 100644 --- a/src/components/middle/MiddleHeaderPanes.tsx +++ b/src/components/middle/MiddleHeaderPanes.tsx @@ -1,7 +1,5 @@ -import { - memo, useRef, useSignal, -} from '../../lib/teact/teact'; -import { setExtraStyles } from '../../lib/teact/teact-dom'; +import { memo, useRef, useSignal } from '@teact'; +import { setExtraStyles } from '@teact/teact-dom'; import { withGlobal } from '../../global'; import type { ApiChat, ApiUserFullInfo } from '../../api/types'; @@ -10,7 +8,12 @@ import type { Signal } from '../../util/signals'; import { MAIN_THREAD_ID } from '../../api/types'; import { - selectChat, selectChatMessage, selectCurrentMiddleSearch, selectTabState, selectUserFullInfo, + selectCanAnimateRightColumn, + selectChat, + selectChatMessage, + selectCurrentMiddleSearch, + selectTabState, + selectUserFullInfo, } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; @@ -45,6 +48,7 @@ type StateProps = { userFullInfo?: ApiUserFullInfo; isAudioPlayerRendered?: boolean; isMiddleSearchOpen?: boolean; + withRightColumnAnimation?: boolean; }; const FALLBACK_PANE_STATE = { height: 0 }; @@ -60,6 +64,7 @@ const MiddleHeaderPanes = ({ getLoadingPinnedId, isAudioPlayerRendered, isMiddleSearchOpen, + withRightColumnAnimation, onFocusPinnedMessage, }: OwnProps & StateProps) => { const { settings } = userFullInfo || {}; @@ -119,7 +124,16 @@ const MiddleHeaderPanes = ({ if (!shouldRender) return undefined; return ( -
+
( userFullInfo, isAudioPlayerRendered: Boolean(audioMessage), isMiddleSearchOpen, + withRightColumnAnimation: selectCanAnimateRightColumn(global), }; }, )(MiddleHeaderPanes)); diff --git a/src/components/middle/composer/SymbolMenu.tsx b/src/components/middle/composer/SymbolMenu.tsx index 32d60e21b..62ed54a20 100644 --- a/src/components/middle/composer/SymbolMenu.tsx +++ b/src/components/middle/composer/SymbolMenu.tsx @@ -1,18 +1,20 @@ -import type { FC } from '../../../lib/teact/teact'; +import type { FC } from '@teact'; import { memo, useEffect, useLayoutEffect, useRef, useState, -} from '../../../lib/teact/teact'; +} from '@teact'; import { withGlobal } from '../../../global'; import type { ApiSticker, ApiVideo } from '../../../api/types'; import type { GlobalActions } from '../../../global'; -import type { ThreadId } from '../../../types'; +import type { AnimationLevel, ThreadId } from '../../../types'; import type { MenuPositionOptions } from '../../ui/Menu'; import { requestMutation } from '../../../lib/fasterdom/fasterdom'; import { selectIsContextMenuTranslucent, selectTabState } from '../../../global/selectors'; +import { selectSharedSettings } from '../../../global/selectors/sharedState.ts'; import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment'; import buildClassName from '../../../util/buildClassName'; +import { resolveTransitionName } from '../../../util/resolveTransitionName.ts'; import useAppLayout from '../../../hooks/useAppLayout'; import useLastCallback from '../../../hooks/useLastCallback'; @@ -69,6 +71,7 @@ export type OwnProps = { type StateProps = { isLeftColumnShown: boolean; isBackgroundTranslucent?: boolean; + animationLevel: AnimationLevel; }; let isActivated = false; @@ -80,12 +83,10 @@ const SymbolMenu: FC = ({ canSendStickers, canSendGifs, isMessageComposer, - isLeftColumnShown, idPrefix, isAttachmentModal, canSendPlainText, className, - isBackgroundTranslucent, onLoad, onClose, onEmojiSelect, @@ -96,6 +97,9 @@ const SymbolMenu: FC = ({ onSearchOpen, addRecentEmoji, addRecentCustomEmoji, + isLeftColumnShown, + isBackgroundTranslucent, + animationLevel, ...menuPositionOptions }) => { const [activeTab, setActiveTab] = useState(SymbolMenuTabs.Emoji); @@ -253,7 +257,7 @@ const SymbolMenu: FC = ({
{isActivated && ( @@ -343,6 +347,7 @@ export default memo(withGlobal( return { isLeftColumnShown: selectTabState(global).isLeftColumnShown, isBackgroundTranslucent: selectIsContextMenuTranslucent(global), + animationLevel: selectSharedSettings(global).animationLevel, }; }, )(SymbolMenu)); diff --git a/src/components/modals/stars/StarsBalanceModal.tsx b/src/components/modals/stars/StarsBalanceModal.tsx index 63db577f0..b5ce9b043 100644 --- a/src/components/modals/stars/StarsBalanceModal.tsx +++ b/src/components/modals/stars/StarsBalanceModal.tsx @@ -3,6 +3,7 @@ import { getActions, getGlobal, withGlobal } from '../../../global'; import type { ApiStarTopupOption } from '../../../api/types'; import type { GlobalState, TabState } from '../../../global/types'; +import type { AnimationLevel } from '../../../types'; import type { RegularLangKey } from '../../../types/language'; import { @@ -15,8 +16,10 @@ import { import { getChatTitle, getUserFullName } from '../../../global/helpers'; import { getPeerTitle } from '../../../global/helpers/peers'; import { selectChat, selectIsPremiumPurchaseBlocked, selectUser } from '../../../global/selectors'; +import { selectSharedSettings } from '../../../global/selectors/sharedState.ts'; import buildClassName from '../../../util/buildClassName'; import { convertCurrencyFromBaseUnit, convertTonToUsd, formatCurrencyAsString } from '../../../util/formatCurrency'; +import { resolveTransitionName } from '../../../util/resolveTransitionName.ts'; import renderText from '../../common/helpers/renderText'; import useFlag from '../../../hooks/useFlag'; @@ -59,10 +62,11 @@ type StateProps = { shouldForceHeight?: boolean; tonUsdRate?: number; tonTopupUrl: string; + animationLevel: AnimationLevel; }; const StarsBalanceModal = ({ - modal, starsBalanceState, tonBalanceState, canBuyPremium, shouldForceHeight, tonUsdRate, tonTopupUrl, + modal, starsBalanceState, tonBalanceState, canBuyPremium, shouldForceHeight, tonUsdRate, tonTopupUrl, animationLevel, }: OwnProps & StateProps) => { const { closeStarsBalanceModal, loadStarsTransactions, loadStarsSubscriptions, openStarsGiftingPickerModal, openInvoice, @@ -365,7 +369,7 @@ const StarsBalanceModal = ({
( canBuyPremium: !selectIsPremiumPurchaseBlocked(global), tonUsdRate: global.appConfig?.tonUsdRate || TON_USD_RATE_DEFAULT, tonTopupUrl: global.appConfig?.tonTopupUrl || TON_TOPUP_URL_DEFAULT, + animationLevel: selectSharedSettings(global).animationLevel, }; }, )(StarsBalanceModal)); diff --git a/src/components/right/Profile.tsx b/src/components/right/Profile.tsx index db15c3939..445eac8e5 100644 --- a/src/components/right/Profile.tsx +++ b/src/components/right/Profile.tsx @@ -17,6 +17,7 @@ import type { } from '../../api/types'; import type { TabState } from '../../global/types'; import type { + AnimationLevel, ProfileState, ProfileTabType, SharedMediaType, ThemeKey, ThreadId, } from '../../types'; import type { RegularLangKey } from '../../types/language'; @@ -67,6 +68,7 @@ import { IS_TOUCH_ENV } from '../../util/browser/windowEnvironment'; import buildClassName from '../../util/buildClassName'; import { captureEvents, SwipeDirection } from '../../util/captureEvents'; import { isUserId } from '../../util/entities/ids'; +import { resolveTransitionName } from '../../util/resolveTransitionName.ts'; import { LOCAL_TGS_URLS } from '../common/helpers/animatedAssets'; import renderText from '../common/helpers/renderText'; import { getSenderName } from '../left/search/helpers/getSenderName'; @@ -154,6 +156,7 @@ type StateProps = { activeDownloads: TabState['activeDownloads']; isChatProtected?: boolean; nextProfileTab?: ProfileTabType; + animationLevel: AnimationLevel; shouldWarnAboutSvg?: boolean; similarChannels?: string[]; similarBots?: string[]; @@ -219,6 +222,7 @@ const Profile: FC = ({ activeDownloads, isChatProtected, nextProfileTab, + animationLevel, shouldWarnAboutSvg, similarChannels, similarBots, @@ -892,7 +896,7 @@ const Profile: FC = ({ > ( const userFullInfo = selectUserFullInfo(global, chatId); const messagesById = selectChatMessages(global, chatId); - const { shouldWarnAboutSvg } = selectSharedSettings(global); + const { animationLevel, shouldWarnAboutSvg } = selectSharedSettings(global); const { currentType: mediaSearchType, resultsByType } = selectCurrentSharedMediaSearch(global) || {}; const { foundIds } = (resultsByType && mediaSearchType && resultsByType[mediaSearchType]) || {}; @@ -1030,6 +1034,7 @@ export default memo(withGlobal( isChatProtected: chat?.isProtected, nextProfileTab: selectTabState(global).nextProfileTab, forceScrollProfileTab: selectTabState(global).forceScrollProfileTab, + animationLevel, shouldWarnAboutSvg, similarChannels: similarChannelIds, similarBots: similarBotsIds, diff --git a/src/components/right/RightColumn.tsx b/src/components/right/RightColumn.tsx index 6476ee33c..e55ddc140 100644 --- a/src/components/right/RightColumn.tsx +++ b/src/components/right/RightColumn.tsx @@ -1,13 +1,9 @@ -import type { FC } from '../../lib/teact/teact'; -import { - memo, useEffect, useRef, useState, -} from '../../lib/teact/teact'; +import type { FC } from '@teact'; +import { memo, useEffect, useRef, useState } from '@teact'; import { getActions, withGlobal } from '../../global'; -import type { ProfileTabType, ThreadId } from '../../types'; -import { - ManagementScreens, NewChatMembersProgress, ProfileState, RightColumnContent, -} from '../../types'; +import type { AnimationLevel, ProfileTabType, ThreadId } from '../../types'; +import { ManagementScreens, NewChatMembersProgress, ProfileState, RightColumnContent } from '../../types'; import { ANIMATION_END_DELAY, MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN } from '../../config'; import { getIsSavedDialog } from '../../global/helpers'; @@ -18,7 +14,9 @@ import { selectRightColumnContentKey, selectTabState, } from '../../global/selectors'; +import { selectSharedSettings } from '../../global/selectors/sharedState.ts'; import captureEscKeyListener from '../../util/captureEscKeyListener'; +import { resolveTransitionName } from '../../util/resolveTransitionName.ts'; import useCurrentOrPrev from '../../hooks/useCurrentOrPrev'; import useHistoryBack from '../../hooks/useHistoryBack'; @@ -55,6 +53,7 @@ type StateProps = { threadId?: ThreadId; isInsideTopic?: boolean; isChatSelected: boolean; + animationLevel: AnimationLevel; shouldSkipHistoryAnimations?: boolean; nextManagementScreen?: ManagementScreens; nextProfileTab?: ProfileTabType; @@ -80,6 +79,7 @@ const RightColumn: FC = ({ threadId, isMobile, isChatSelected, + animationLevel, shouldSkipHistoryAnimations, nextManagementScreen, nextProfileTab, @@ -403,7 +403,7 @@ const RightColumn: FC = ({ /> ( const { chatId, threadId } = selectCurrentMessageList(global) || {}; const areActiveChatsLoaded = selectAreActiveChatsLoaded(global); + const { animationLevel } = selectSharedSettings(global); const { management, shouldSkipHistoryAnimations, nextProfileTab, shouldCloseRightColumn, } = selectTabState(global); @@ -438,6 +439,7 @@ export default memo(withGlobal( chatId, threadId, isChatSelected: Boolean(chatId && areActiveChatsLoaded), + animationLevel, shouldSkipHistoryAnimations, nextManagementScreen, nextProfileTab, diff --git a/src/components/right/statistics/BoostStatistics.tsx b/src/components/right/statistics/BoostStatistics.tsx index 030988cba..49cecb6a6 100644 --- a/src/components/right/statistics/BoostStatistics.tsx +++ b/src/components/right/statistics/BoostStatistics.tsx @@ -1,23 +1,17 @@ -import { - memo, useMemo, useRef, useState, -} from '../../../lib/teact/teact'; +import { memo, useMemo, useRef, useState } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; import type { ApiBoost, ApiBoostStatistics, ApiTypePrepaidGiveaway } from '../../../api/types'; import type { TabState } from '../../../global/types'; -import type { CustomPeer } from '../../../types'; +import type { AnimationLevel, CustomPeer } from '../../../types'; -import { - GIVEAWAY_BOOST_PER_PREMIUM, -} from '../../../config'; +import { GIVEAWAY_BOOST_PER_PREMIUM } from '../../../config'; import { isChatChannel } from '../../../global/helpers'; -import { - selectChat, - selectIsGiveawayGiftsPurchaseAvailable, - selectTabState, -} from '../../../global/selectors'; +import { selectChat, selectIsGiveawayGiftsPurchaseAvailable, selectTabState } from '../../../global/selectors'; +import { selectSharedSettings } from '../../../global/selectors/sharedState.ts'; import buildClassName from '../../../util/buildClassName'; import { formatDateAtTime } from '../../../util/dates/dateFormat'; +import { resolveTransitionName } from '../../../util/resolveTransitionName.ts'; import { formatInteger } from '../../../util/textFormat'; import { getBoostProgressInfo } from '../../common/helpers/boostInfo'; @@ -48,6 +42,7 @@ type StateProps = { chatId: string; giveawayBoostsPerPremium?: number; isChannel?: boolean; + animationLevel: AnimationLevel; }; const GIVEAWAY_IMG_LIST: Partial> = { @@ -75,6 +70,7 @@ const BoostStatistics = ({ chatId, giveawayBoostsPerPremium, isChannel, + animationLevel, }: StateProps) => { const { openChat, loadMoreBoosters, closeBoostStatistics, openGiveawayModal, showNotification, @@ -363,7 +359,7 @@ const BoostStatistics = ({ > (global: T) { return selectPerformanceSettingsValue(global, 'pageTransitions'); } +export function selectCanAnimateRightColumn(global: T) { + return selectPerformanceSettingsValue(global, 'rightColumnAnimations'); +} + +export function selectCanAnimateSnapEffect(global: T) { + return IS_SNAP_EFFECT_SUPPORTED && selectPerformanceSettingsValue(global, 'snapEffect'); +} + export function selectIsContextMenuTranslucent(global: T) { return selectPerformanceSettingsValue(global, 'contextMenuBlur'); } @@ -152,10 +160,6 @@ export function selectIsSynced(global: T) { return global.isSynced; } -export function selectCanAnimateSnapEffect(global: T) { - return IS_SNAP_EFFECT_SUPPORTED && selectPerformanceSettingsValue(global, 'snapEffect'); -} - export function selectWebApp( global: T, key: string, ...[tabId = getCurrentTabId()]: TabArgs ) { diff --git a/src/util/browser/windowEnvironment.ts b/src/util/browser/windowEnvironment.ts index 55ddbf49c..464a4014c 100644 --- a/src/util/browser/windowEnvironment.ts +++ b/src/util/browser/windowEnvironment.ts @@ -76,7 +76,6 @@ export const IS_CANVAS_FILTER_SUPPORTED = ( ); export const IS_REQUEST_FULLSCREEN_SUPPORTED = 'requestFullscreen' in document.createElement('div'); export const ARE_CALLS_SUPPORTED = true; -export const LAYERS_ANIMATION_NAME = IS_ANDROID ? 'slideFade' : IS_IOS ? 'slideLayers' : 'pushSlide'; export const IS_WAVE_TRANSFORM_SUPPORTED = !IS_MOBILE && !IS_FIREFOX // https://bugzilla.mozilla.org/show_bug.cgi?id=1961378 diff --git a/src/util/resolveTransitionName.ts b/src/util/resolveTransitionName.ts new file mode 100644 index 000000000..5431b60cb --- /dev/null +++ b/src/util/resolveTransitionName.ts @@ -0,0 +1,23 @@ +import type { AnimationLevel } from '../types'; + +import { ANIMATION_LEVEL_MED, ANIMATION_LEVEL_MIN } from '../config.ts'; +import { IS_ANDROID, IS_IOS } from './browser/windowEnvironment.ts'; + +export function resolveTransitionName( + name: 'slideOptimized' | 'slide' | 'layers', + animationLevel: AnimationLevel, + isDisabled = false, + isRtl = false, +) { + if (isDisabled || animationLevel === ANIMATION_LEVEL_MIN) return 'none'; + + if (animationLevel === ANIMATION_LEVEL_MED) return 'slideFade'; + + return name === 'slideOptimized' ? ( + isRtl ? 'slideOptimizedRtl' : 'slideOptimized' + ) : name === 'slide' ? ( + isRtl ? 'slideRtl' : 'slide' + ) : ( + IS_ANDROID ? 'slideFade' : IS_IOS ? 'slideLayers' : 'pushSlide' + ); +}