diff --git a/src/components/App.tsx b/src/components/App.tsx index 5b8c66bb7..e61da5681 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -47,6 +47,7 @@ enum AppScreens { inactive, } +const TRANSITION_RENDER_COUNT = Object.keys(AppScreens).length / 2; const INACTIVE_PAGE_TITLE = `${PAGE_TITLE} ${INACTIVE_MARKER}`; const App: FC = ({ @@ -204,7 +205,7 @@ const App: FC = ({ }, [theme]); return ( - + = ({ 'full-height', (activeKey === AppScreens.auth || prevActiveKey === AppScreens.auth) && 'is-auth', )} + renderCount={TRANSITION_RENDER_COUNT} > {renderContent} diff --git a/src/components/left/LeftColumn.tsx b/src/components/left/LeftColumn.tsx index 81104f538..99295373e 100644 --- a/src/components/left/LeftColumn.tsx +++ b/src/components/left/LeftColumn.tsx @@ -484,19 +484,19 @@ const LeftColumn: FC = ({ activeKey={contentType} shouldCleanup cleanupExceptionKey={ContentType.Main} + shouldWrap + wrapExceptionKey={ContentType.Main} id="LeftColumn" - > - {(isActive) => ( - <> - {renderContent(isActive)} -
- + afterChildren={( +
)} + > + {renderContent} ); }; diff --git a/src/components/left/main/LeftMain.scss b/src/components/left/main/LeftMain.scss index 28bc67ab4..bd38722d3 100644 --- a/src/components/left/main/LeftMain.scss +++ b/src/components/left/main/LeftMain.scss @@ -4,7 +4,6 @@ display: flex; flex-direction: column; overflow: hidden; - z-index: 1; > .Transition { flex: 1; diff --git a/src/components/left/main/LeftMain.tsx b/src/components/left/main/LeftMain.tsx index aa5cdd984..fa3db0375 100644 --- a/src/components/left/main/LeftMain.tsx +++ b/src/components/left/main/LeftMain.tsx @@ -172,6 +172,8 @@ const LeftMain: FC = ({ activeKey={content} shouldCleanup cleanupExceptionKey={LeftColumnContent.ChatList} + shouldWrap + wrapExceptionKey={LeftColumnContent.ChatList} > {(isActive) => { switch (content) { diff --git a/src/components/left/settings/Settings.tsx b/src/components/left/settings/Settings.tsx index fc25c8a90..c4b11b780 100644 --- a/src/components/left/settings/Settings.tsx +++ b/src/components/left/settings/Settings.tsx @@ -451,6 +451,7 @@ const Settings: FC = ({ name={shouldSkipTransition ? 'none' : LAYERS_ANIMATION_NAME} activeKey={currentScreen} renderCount={TRANSITION_RENDER_COUNT} + shouldWrap > {renderCurrentSection} diff --git a/src/components/main/Main.tsx b/src/components/main/Main.tsx index aadb5808f..14d90a28e 100644 --- a/src/components/main/Main.tsx +++ b/src/components/main/Main.tsx @@ -1,7 +1,8 @@ import type { FC } from '../../lib/teact/teact'; import React, { - useEffect, memo, useCallback, useState, useRef, + useEffect, memo, useCallback, useState, useRef, useLayoutEffect, } from '../../lib/teact/teact'; +import { addExtraClass } from '../../lib/teact/teact-dom'; import { getActions, getGlobal, withGlobal } from '../../global'; import type { AnimationLevel, LangCode } from '../../types'; @@ -231,6 +232,9 @@ const Main: FC = ({ void loadBundle(Bundles.Calls); }, CALL_BUNDLE_LOADING_DELAY_MS); + // eslint-disable-next-line no-null/no-null + const containerRef = useRef(null); + const { isDesktop } = useAppLayout(); useEffect(() => { if (!isLeftColumnOpen && !isMiddleColumnOpen && !isDesktop) { @@ -348,6 +352,14 @@ const Main: FC = ({ } }, [lastSyncTime, openChat]); + // Restore Transition slide class after async rendering + useLayoutEffect(() => { + const container = containerRef.current!; + if (container.parentNode!.childElementCount === 1) { + addExtraClass(container, 'Transition__slide--active'); + } + }, []); + const leftColumnTransition = useShowTransition( isLeftColumnOpen, undefined, true, undefined, shouldSkipHistoryAnimations, undefined, true, ); @@ -445,7 +457,7 @@ const Main: FC = ({ usePreventPinchZoomGesture(isMediaViewerOpen); return ( -
+
diff --git a/src/components/right/Profile.tsx b/src/components/right/Profile.tsx index 5940388c9..1db64ce1c 100644 --- a/src/components/right/Profile.tsx +++ b/src/components/right/Profile.tsx @@ -454,7 +454,7 @@ const Profile: FC = ({ .Transition__slide--active.${resultType}-list > .scroll-item`} items={canRenderContent ? viewportIds : undefined} cacheBuster={cacheBuster} sensitiveArea={PROFILE_SENSITIVE_AREA} @@ -515,15 +515,6 @@ function renderProfileInfo(chatId: string, resolvedUserId: string | undefined, i ); } -function buildInfiniteScrollItemSelector(resultType: string) { - return [ - // Used on first render - `.shared-media-transition > div:only-child > .${resultType}-list > .scroll-item`, - // Used after transition - `.shared-media-transition > .Transition__slide--active > .${resultType}-list > .scroll-item`, - ].join(', '); -} - export default memo(withGlobal( (global, { chatId, topicId, isMobile }): StateProps => { const chat = selectChat(global, chatId); diff --git a/src/components/right/hooks/useTransitionFixes.ts b/src/components/right/hooks/useTransitionFixes.ts index d2f2eb791..47c79bd00 100644 --- a/src/components/right/hooks/useTransitionFixes.ts +++ b/src/components/right/hooks/useTransitionFixes.ts @@ -30,14 +30,14 @@ export default function useTransitionFixes( if (container.style.overflowY !== 'hidden') { const scrollBarWidth = container.offsetWidth - container.clientWidth; container.style.overflowY = 'hidden'; - container.style.marginRight = `${scrollBarWidth}px`; + container.style.paddingRight = `${scrollBarWidth}px`; } }, [containerRef]); const releaseTransitionFix = useCallback(() => { const container = containerRef.current!; container.style.overflowY = 'scroll'; - container.style.marginRight = '0'; + container.style.paddingRight = '0'; }, [containerRef]); return { applyTransitionFix, releaseTransitionFix }; diff --git a/src/components/ui/Transition.scss b/src/components/ui/Transition.scss index 2e5b48ed1..8e941fc9d 100644 --- a/src/components/ui/Transition.scss +++ b/src/components/ui/Transition.scss @@ -12,8 +12,10 @@ top: 0; left: 0; } + } - &:not(.Transition__slide--active):not(.from):not(.to) { + &:not(.slide-optimized):not(.slide-optimized-rtl) { + > .Transition__slide:not(.Transition__slide--active):not(.from):not(.to) { display: none !important; // Best performance when animating container //transform: scale(0); // Shortest initial delay } @@ -36,7 +38,6 @@ #root & > .Transition__slide { position: absolute; - display: block !important; top: 0; left: 0; transition: transform var(--slide-transition); @@ -348,7 +349,7 @@ &.slide-layers { --background-color: var(--color-background); - background: black; + background: black !important; > .Transition__slide { background: var(--background-color); @@ -427,6 +428,7 @@ &.push-slide.backwards { > .to { transform: scale(0.7); + opacity: 0; } &.animating { diff --git a/src/components/ui/Transition.tsx b/src/components/ui/Transition.tsx index e8da4a84d..7f65d9b90 100644 --- a/src/components/ui/Transition.tsx +++ b/src/components/ui/Transition.tsx @@ -29,18 +29,23 @@ export type TransitionProps = { shouldRestoreHeight?: boolean; shouldCleanup?: boolean; cleanupExceptionKey?: number; + // Used by async components which are usually remounted during first animation + shouldWrap?: boolean; + wrapExceptionKey?: number; isDisabled?: boolean; id?: string; className?: string; onStart?: NoneToVoidFunction; onStop?: NoneToVoidFunction; children: React.ReactNode | ChildrenFn; + afterChildren?: React.ReactNode; }; const FALLBACK_ANIMATION_END = 1000; const CLASSES = { slide: 'Transition__slide', active: 'Transition__slide--active', + afterSlides: 'Transition__after-slides', }; const Transition: FC = ({ @@ -52,11 +57,14 @@ const Transition: FC = ({ shouldRestoreHeight, shouldCleanup, cleanupExceptionKey, + shouldWrap, + wrapExceptionKey, id, className, onStart, onStop, children, + afterChildren, }) => { // No need for a container to update on change const { animationLevel } = getGlobal().settings.byKey; @@ -94,7 +102,8 @@ const Transition: FC = ({ } const container = containerRef.current!; - const childElements = Array.from(container.children) as HTMLElement[]; + const childElements = (Array.from(container.children) as HTMLElement[]) + .filter((el) => !el.classList.contains(CLASSES.afterSlides)); childElements.forEach((el) => { addExtraClass(el, CLASSES.slide); @@ -113,7 +122,9 @@ const Transition: FC = ({ return; } - const childNodes = Array.from(container.childNodes); + const childNodes = Array.from(container.childNodes) + .filter((el) => !(el instanceof HTMLElement && el.classList.contains(CLASSES.afterSlides))); + if (!activeKeyChanged || !childNodes.length) { return; } @@ -271,16 +282,21 @@ const Transition: FC = ({ return undefined; } - return ( -
{ - typeof render === 'function' - ? render(key === activeKey, key === prevActiveKey, activeKey) - : render - } -
- ); + const rendered = typeof render === 'function' + ? render(key === activeKey, key === prevActiveKey, activeKey) + : render; + + return (shouldWrap && key !== wrapExceptionKey) || asFastList + ?
{rendered}
+ : rendered; }); + if (afterChildren) { + contents.push(( +
{afterChildren}
+ )); + } + return (