import React, { FC, useEffect } from '../../lib/teact/teact'; import { getActions, getGlobal, withGlobal } from '../../global'; import { ApiMediaFormat } from '../../api/types'; import { GlobalState } from '../../global/types'; import { getChatAvatarHash } from '../../global/helpers/chats'; // Direct import for better module splitting import useFlag from '../../hooks/useFlag'; import useShowTransition from '../../hooks/useShowTransition'; import { pause } from '../../util/schedulers'; import { preloadImage } from '../../util/files'; import preloadFonts from '../../util/fonts'; import * as mediaLoader from '../../util/mediaLoader'; import { Bundles, loadModule } from '../../util/moduleLoader'; import buildClassName from '../../util/buildClassName'; import './UiLoader.scss'; import telegramLogoPath from '../../assets/telegram-logo.svg'; import reactionThumbsPath from '../../assets/reaction-thumbs.png'; import monkeyPath from '../../assets/monkey.svg'; import { selectIsRightColumnShown, selectTheme } from '../../global/selectors'; type OwnProps = { page: 'main' | 'authCode' | 'authPassword' | 'authPhoneNumber' | 'authQrCode'; children: React.ReactNode; }; type StateProps = Pick & { hasCustomBackground?: boolean; hasCustomBackgroundColor: boolean; isRightColumnShown?: boolean; leftColumnWidth?: number; }; const MAX_PRELOAD_DELAY = 700; const SECOND_STATE_DELAY = 1000; const AVATARS_TO_PRELOAD = 10; function preloadAvatars() { const { listIds, byId } = getGlobal().chats; if (!listIds.active) { return undefined; } return Promise.all(listIds.active.slice(0, AVATARS_TO_PRELOAD).map((chatId) => { const chat = byId[chatId]; if (!chat) { return undefined; } const avatarHash = getChatAvatarHash(chat); if (!avatarHash) { return undefined; } return mediaLoader.fetch(avatarHash, ApiMediaFormat.BlobUrl); })); } const preloadTasks = { main: () => Promise.all([ loadModule(Bundles.Main, 'Main') .then(preloadFonts), preloadAvatars(), preloadImage(reactionThumbsPath), ]), authPhoneNumber: () => Promise.all([ preloadFonts(), preloadImage(telegramLogoPath), ]), authCode: () => preloadImage(monkeyPath), authPassword: () => preloadImage(monkeyPath), authQrCode: preloadFonts, }; const UiLoader: FC = ({ page, children, hasCustomBackground, hasCustomBackgroundColor, isRightColumnShown, shouldSkipHistoryAnimations, leftColumnWidth, }) => { const { setIsUiReady } = getActions(); const [isReady, markReady] = useFlag(); const { shouldRender: shouldRenderMask, transitionClassNames, } = useShowTransition(!isReady, undefined, true); useEffect(() => { let timeout: number | undefined; const safePreload = async () => { try { await preloadTasks[page](); } catch (err) { // Do nothing } }; Promise.race([ pause(MAX_PRELOAD_DELAY), safePreload(), ]).then(() => { markReady(); setIsUiReady({ uiReadyState: 1 }); timeout = window.setTimeout(() => { setIsUiReady({ uiReadyState: 2 }); }, SECOND_STATE_DELAY); }); return () => { if (timeout) { clearTimeout(timeout); timeout = undefined; } setIsUiReady({ uiReadyState: 0 }); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return (
{children} {shouldRenderMask && !shouldSkipHistoryAnimations && (
{page === 'main' ? ( <>
{isRightColumnShown &&
} ) : (
)}
)}
); }; export default withGlobal( (global): StateProps => { const theme = selectTheme(global); const { background, backgroundColor } = global.settings.themes[theme] || {}; return { shouldSkipHistoryAnimations: global.shouldSkipHistoryAnimations, uiReadyState: global.uiReadyState, hasCustomBackground: Boolean(background), hasCustomBackgroundColor: Boolean(backgroundColor), isRightColumnShown: selectIsRightColumnShown(global), leftColumnWidth: global.leftColumnWidth, }; }, )(UiLoader);