[Perf] Get rid of redundant wrappers: #root, #UiLoader and #LeftColumn

This commit is contained in:
Alexander Zinchuk 2023-04-15 13:50:23 +02:00
parent ac1033b673
commit 982ba8c3a7
13 changed files with 186 additions and 198 deletions

View File

@ -0,0 +1,40 @@
.bg {
height: 100%;
background-color: var(--theme-background-color);
background-position: center;
background-repeat: no-repeat;
background-size: 100% 100%;
@media (max-width: 600px) {
height: calc(var(--vh, 1vh) * 100);
}
:global(html.theme-light) & {
background-image: url('../assets/chat-bg-br.png');
}
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-image: url('../assets/chat-bg-pattern-light.png');
background-position: top right;
background-size: 510px auto;
background-repeat: repeat;
mix-blend-mode: overlay;
:global(html.theme-dark) & {
background-image: url('../assets/chat-bg-pattern-dark.png');
mix-blend-mode: unset;
}
@media (max-width: 600px) {
bottom: auto;
height: calc(var(--vh, 1vh) * 100);
}
}
}

View File

@ -1,37 +1,43 @@
import type { FC } from './lib/teact/teact';
import React, { useEffect } from './lib/teact/teact';
import { getActions, withGlobal } from './global';
import type { FC } from '../lib/teact/teact';
import React, { useEffect, useLayoutEffect } from '../lib/teact/teact';
import { getActions, withGlobal } from '../global';
import type { GlobalState } from './global/types';
import type { UiLoaderPage } from './components/common/UiLoader';
import type { GlobalState } from '../global/types';
import type { UiLoaderPage } from './common/UiLoader';
import type { ThemeKey } from '../types';
import { IS_INSTALL_PROMPT_SUPPORTED, IS_MULTITAB_SUPPORTED, PLATFORM_ENV } from './util/windowEnvironment';
import { INACTIVE_MARKER, PAGE_TITLE } from './config';
import { selectTabState } from './global/selectors';
import { updateSizes } from './util/windowSize';
import { addActiveTabChangeListener } from './util/activeTabMonitor';
import { hasStoredSession } from './util/sessions';
import { setupBeforeInstallPrompt } from './util/installPrompt';
import buildClassName from './util/buildClassName';
import { parseInitialLocationHash } from './util/routing';
import useFlag from './hooks/useFlag';
import usePrevious from './hooks/usePrevious';
import useAppLayout from './hooks/useAppLayout';
import { IS_INSTALL_PROMPT_SUPPORTED, IS_MULTITAB_SUPPORTED, PLATFORM_ENV } from '../util/windowEnvironment';
import {
DARK_THEME_BG_COLOR, INACTIVE_MARKER, LIGHT_THEME_BG_COLOR, PAGE_TITLE,
} from '../config';
import { selectTabState, selectTheme } from '../global/selectors';
import { updateSizes } from '../util/windowSize';
import { addActiveTabChangeListener } from '../util/activeTabMonitor';
import { hasStoredSession } from '../util/sessions';
import buildClassName from '../util/buildClassName';
import { parseInitialLocationHash } from '../util/routing';
import useFlag from '../hooks/useFlag';
import usePrevious from '../hooks/usePrevious';
import useAppLayout from '../hooks/useAppLayout';
import Auth from './components/auth/Auth';
import Main from './components/main/Main.async';
import LockScreen from './components/main/LockScreen.async';
import AppInactive from './components/main/AppInactive';
import Transition from './components/ui/Transition';
import UiLoader from './components/common/UiLoader';
import Auth from './auth/Auth';
import Main from './main/Main.async';
import LockScreen from './main/LockScreen.async';
import AppInactive from './main/AppInactive';
import Transition from './ui/Transition';
import UiLoader from './common/UiLoader';
// import Test from './components/test/TestNoRedundancy';
import styles from './App.module.scss';
import { setupBeforeInstallPrompt } from '../util/installPrompt';
type StateProps = {
authState: GlobalState['authState'];
isScreenLocked?: boolean;
hasPasscode?: boolean;
isInactiveAuth?: boolean;
hasWebAuthTokenFailed?: boolean;
theme: ThemeKey;
};
enum AppScreens {
@ -47,8 +53,9 @@ const App: FC<StateProps> = ({
authState,
isScreenLocked,
hasPasscode,
hasWebAuthTokenFailed,
isInactiveAuth,
hasWebAuthTokenFailed,
theme,
}) => {
const { disconnect } = getActions();
@ -185,6 +192,17 @@ const App: FC<StateProps> = ({
}
}
useLayoutEffect(() => {
document.body.classList.add(styles.bg);
}, []);
useLayoutEffect(() => {
document.body.style.setProperty(
'--theme-background-color',
theme === 'dark' ? DARK_THEME_BG_COLOR : LIGHT_THEME_BG_COLOR,
);
}, [theme]);
return (
<UiLoader key="Loader" page={page} isMobile={isMobile}>
<Transition
@ -210,6 +228,7 @@ export default withGlobal(
hasPasscode: global.passcode?.hasPasscode,
isInactiveAuth: selectTabState(global).isInactive,
hasWebAuthTokenFailed: global.hasWebAuthTokenFailed || global.hasWebAuthTokenPasswordRequired,
theme: selectTheme(global),
};
},
)(App);

View File

@ -1,44 +1,3 @@
.bg {
height: 100%;
background-color: var(--theme-background-color);
background-position: center;
background-repeat: no-repeat;
background-size: 100% 100%;
@media (max-width: 600px) {
height: calc(var(--vh, 1vh) * 100);
}
:global(html.theme-light) & {
background-image: url('../../assets/chat-bg-br.png');
}
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-image: url('../../assets/chat-bg-pattern-light.png');
background-position: top right;
background-size: 510px auto;
background-repeat: repeat;
mix-blend-mode: overlay;
:global(html.theme-dark) & {
background-image: url('../../assets/chat-bg-pattern-dark.png');
mix-blend-mode: unset;
}
@media (max-width: 600px) {
bottom: auto;
height: calc(var(--vh, 1vh) * 100);
}
}
}
.mask {
position: fixed;
top: 0;

View File

@ -3,16 +3,10 @@ import { getActions, getGlobal, withGlobal } from '../../global';
import { ApiMediaFormat } from '../../api/types';
import type { TabState } from '../../global/types';
import type { ThemeKey } from '../../types';
import type { FC } from '../../lib/teact/teact';
import { getChatAvatarHash } from '../../global/helpers/chats'; // Direct import for better module splitting
import {
selectIsRightColumnShown,
selectTheme,
selectTabState,
} from '../../global/selectors';
import { DARK_THEME_BG_COLOR, LIGHT_THEME_BG_COLOR } from '../../config';
import { selectIsRightColumnShown, selectTabState } from '../../global/selectors';
import { pause } from '../../util/schedulers';
import { preloadImage } from '../../util/files';
import preloadFonts from '../../util/fonts';
@ -25,6 +19,7 @@ import useShowTransition from '../../hooks/useShowTransition';
import useEffectOnce from '../../hooks/useEffectOnce';
import styles from './UiLoader.module.scss';
import appStyles from '../App.module.scss';
// Workaround for incorrect bundling by Webpack: force including in the main chunk
import '../ui/Modal.scss';
@ -54,7 +49,6 @@ type OwnProps = {
type StateProps = Pick<TabState, 'uiReadyState' | 'shouldSkipHistoryAnimations'> & {
isRightColumnShown?: boolean;
leftColumnWidth?: number;
theme: ThemeKey;
};
const MAX_PRELOAD_DELAY = 700;
@ -111,7 +105,6 @@ const UiLoader: FC<OwnProps & StateProps> = ({
isRightColumnShown,
shouldSkipHistoryAnimations,
leftColumnWidth,
theme,
}) => {
const { setIsUiReady } = getActions();
@ -154,11 +147,7 @@ const UiLoader: FC<OwnProps & StateProps> = ({
});
return (
<div
id="UiLoader"
className={styles.bg}
style={`--theme-background-color: ${theme === 'dark' ? DARK_THEME_BG_COLOR : LIGHT_THEME_BG_COLOR}`}
>
<>
{children}
{shouldRenderMask && !shouldSkipHistoryAnimations && Boolean(page) && (
<div className={buildClassName(styles.mask, transitionClassNames)}>
@ -168,23 +157,22 @@ const UiLoader: FC<OwnProps & StateProps> = ({
className={styles.left}
style={leftColumnWidth ? `width: ${leftColumnWidth}px` : undefined}
/>
<div className={buildClassName(styles.middle, styles.bg)} />
<div className={buildClassName(styles.middle, appStyles.bg)} />
{isRightColumnShown && <div className={styles.right} />}
</div>
) : (page === 'inactive' || page === 'lock') ? (
<div className={buildClassName(styles.blank, styles.bg)} />
<div className={buildClassName(styles.blank, appStyles.bg)} />
) : (
<div className={styles.blank} />
)}
</div>
)}
</div>
</>
);
};
export default withGlobal<OwnProps>(
(global, { isMobile }): StateProps => {
const theme = selectTheme(global);
const tabState = selectTabState(global);
return {
@ -192,7 +180,6 @@ export default withGlobal<OwnProps>(
uiReadyState: tabState.uiReadyState,
isRightColumnShown: selectIsRightColumnShown(global, isMobile),
leftColumnWidth: global.leftColumnWidth,
theme,
};
},
)(UiLoader);

View File

@ -406,95 +406,98 @@ const LeftColumn: FC<StateProps> = ({
setSettingsScreen(screen);
}, []);
function renderContent(isActive: boolean) {
switch (contentType) {
case ContentType.Archived:
return (
<ArchivedChats
isActive={isActive}
onReset={handleReset}
onTopicSearch={handleTopicSearch}
foldersDispatch={foldersDispatch}
onSettingsScreenSelect={handleSettingsScreenSelect}
onLeftColumnContentChange={setContent}
isForumPanelOpen={isForumPanelOpen}
archiveSettings={archiveSettings}
/>
);
case ContentType.Settings:
return (
<Settings
isActive={isActive}
currentScreen={settingsScreen}
foldersState={foldersState}
foldersDispatch={foldersDispatch}
onScreenSelect={handleSettingsScreenSelect}
onReset={handleReset}
shouldSkipTransition={shouldSkipHistoryAnimations}
/>
);
case ContentType.NewChannel:
return (
<NewChat
key={lastResetTime}
isActive={isActive}
isChannel
content={content}
onContentChange={setContent}
onReset={handleReset}
/>
);
case ContentType.NewGroup:
return (
<NewChat
key={lastResetTime}
isActive={isActive}
content={content}
onContentChange={setContent}
onReset={handleReset}
/>
);
default:
return (
<LeftMain
content={content}
isClosingSearch={isClosingSearch}
searchQuery={searchQuery}
searchDate={searchDate}
contactsFilter={contactsFilter}
foldersDispatch={foldersDispatch}
onContentChange={setContent}
onSearchQuery={handleSearchQuery}
onSettingsScreenSelect={handleSettingsScreenSelect}
onReset={handleReset}
shouldSkipTransition={shouldSkipHistoryAnimations}
isUpdateAvailable={isUpdateAvailable}
isForumPanelOpen={isForumPanelOpen}
onTopicSearch={handleTopicSearch}
/>
);
}
}
return (
<div
id="LeftColumn"
<Transition
ref={resizeRef}
name={shouldSkipHistoryAnimations ? 'none' : LAYERS_ANIMATION_NAME}
renderCount={RENDER_COUNT}
activeKey={contentType}
shouldCleanup
cleanupExceptionKey={ContentType.Main}
id="LeftColumn"
>
<Transition
name={shouldSkipHistoryAnimations ? 'none' : LAYERS_ANIMATION_NAME}
renderCount={RENDER_COUNT}
activeKey={contentType}
shouldCleanup
cleanupExceptionKey={ContentType.Main}
>
{(isActive) => {
switch (contentType) {
case ContentType.Archived:
return (
<ArchivedChats
isActive={isActive}
onReset={handleReset}
onTopicSearch={handleTopicSearch}
foldersDispatch={foldersDispatch}
onSettingsScreenSelect={handleSettingsScreenSelect}
onLeftColumnContentChange={setContent}
isForumPanelOpen={isForumPanelOpen}
archiveSettings={archiveSettings}
/>
);
case ContentType.Settings:
return (
<Settings
isActive={isActive}
currentScreen={settingsScreen}
foldersState={foldersState}
foldersDispatch={foldersDispatch}
onScreenSelect={handleSettingsScreenSelect}
onReset={handleReset}
shouldSkipTransition={shouldSkipHistoryAnimations}
/>
);
case ContentType.NewChannel:
return (
<NewChat
key={lastResetTime}
isActive={isActive}
isChannel
content={content}
onContentChange={setContent}
onReset={handleReset}
/>
);
case ContentType.NewGroup:
return (
<NewChat
key={lastResetTime}
isActive={isActive}
content={content}
onContentChange={setContent}
onReset={handleReset}
/>
);
default:
return (
<LeftMain
content={content}
isClosingSearch={isClosingSearch}
searchQuery={searchQuery}
searchDate={searchDate}
contactsFilter={contactsFilter}
foldersDispatch={foldersDispatch}
onContentChange={setContent}
onSearchQuery={handleSearchQuery}
onSettingsScreenSelect={handleSettingsScreenSelect}
onReset={handleReset}
shouldSkipTransition={shouldSkipHistoryAnimations}
isUpdateAvailable={isUpdateAvailable}
isForumPanelOpen={isForumPanelOpen}
onTopicSearch={handleTopicSearch}
/>
);
}
}}
</Transition>
<div
className="resize-handle"
onMouseDown={initResize}
onMouseUp={handleMouseUp}
onDoubleClick={resetResize}
/>
</div>
{(isActive) => (
<>
{renderContent(isActive)}
<div
className="resize-handle"
onMouseDown={initResize}
onMouseUp={handleMouseUp}
onDoubleClick={resetResize}
/>
</>
)}
</Transition>
);
};

View File

@ -1,8 +0,0 @@
#Dialogs {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: var(--z-modal);
}

View File

@ -18,8 +18,6 @@ import Modal from '../ui/Modal';
import Button from '../ui/Button';
import Avatar from '../common/Avatar';
import './Dialogs.scss';
type StateProps = {
dialogs: (ApiError | ApiInviteInfo | ApiContact)[];
animationLevel: AnimationLevel;
@ -179,11 +177,7 @@ const Dialogs: FC<StateProps> = ({ dialogs, animationLevel }) => {
return renderError(dialog);
};
return (
<div id="Dialogs">
{Boolean(dialogs.length) && renderDialog(dialogs[dialogs.length - 1])}
</div>
);
return Boolean(dialogs.length) && renderDialog(dialogs[dialogs.length - 1]);
};
function getErrorHeader(error: ApiError) {

View File

@ -28,22 +28,17 @@
}
#LeftColumn {
width: 33vw;
--left-column-min-width: 16rem;
--left-column-max-width: 26.5rem;
width: 33vw;
min-width: var(--left-column-min-width);
max-width: var(--left-column-max-width);
height: 100%;
position: relative;
background-color: var(--color-background);
overflow: hidden;
& > div {
height: 100%;
overflow: hidden;
}
background-color: var(--color-background);
@media (max-width: 600px) {
height: calc(var(--vh, 1vh) * 100);

View File

@ -18,7 +18,7 @@
right: 0;
bottom: 0;
background: var(--color-background);
z-index: 1;
z-index: var(--z-symbol-menu-mobile);
transition: transform var(--layer-transition);
padding-right: env(safe-area-inset-right);

View File

@ -43,13 +43,12 @@
<link rel="manifest" id="the-manifest-placeholder" href="./<%= htmlWebpackPlugin.options.manifest %>">
</head>
<body>
<body id="root">
<noscript>
<video src="./nojs.mp4" class="nojs-video" muted loop autoplay playsinline disablePictureInPicture></video>
<h1>Telegram Web</h1>
<p>Please, enable JavaScript to open the app.</p>
</noscript>
<div id="root"></div>
<div id="portals"></div>
</body>
</html>

View File

@ -17,7 +17,7 @@ import { requestGlobal, subscribeToMultitabBroadcastChannel } from './util/multi
import { onBeforeUnload } from './util/schedulers';
import { selectTabState } from './global/selectors';
import App from './App';
import App from './components/App';
import './styles/index.scss';

View File

@ -242,6 +242,7 @@ $color-message-reaction-own-hover: #b5e0a4;
--z-register-add-avatar: 5;
--z-forum-panel: 5;
--z-media-viewer-head: 3;
--z-symbol-menu-mobile: 2;
--z-resize-handle: 2;
--z-below: -1;

View File

@ -26,7 +26,6 @@
html,
body,
#root,
#UiLoader,
#Main,
#MiddleColumn,
.MessageList,