198 lines
5.6 KiB
TypeScript
198 lines
5.6 KiB
TypeScript
import type { FC } from '../../lib/teact/teact';
|
|
import React, { useEffect } from '../../lib/teact/teact';
|
|
import { getActions, getGlobal, withGlobal } from '../../global';
|
|
|
|
import { ApiMediaFormat } from '../../api/types';
|
|
import type { GlobalState } from '../../global/types';
|
|
import type { ThemeKey } from '../../types';
|
|
|
|
import { DARK_THEME_BG_COLOR, LIGHT_THEME_BG_COLOR } from '../../config';
|
|
import { getChatAvatarHash } from '../../global/helpers/chats'; // Direct import for better module splitting
|
|
import { selectIsRightColumnShown, selectTheme } from '../../global/selectors';
|
|
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 buildStyle from '../../util/buildStyle';
|
|
import useCustomBackground from '../../hooks/useCustomBackground';
|
|
|
|
import './UiLoader.scss';
|
|
|
|
import telegramLogoPath from '../../assets/telegram-logo.svg';
|
|
import reactionThumbsPath from '../../assets/reaction-thumbs.png';
|
|
import monkeyPath from '../../assets/monkey.svg';
|
|
|
|
type OwnProps = {
|
|
page: 'main' | 'authCode' | 'authPassword' | 'authPhoneNumber' | 'authQrCode';
|
|
children: React.ReactNode;
|
|
};
|
|
|
|
type StateProps =
|
|
Pick<GlobalState, 'uiReadyState' | 'shouldSkipHistoryAnimations'>
|
|
& {
|
|
isRightColumnShown?: boolean;
|
|
leftColumnWidth?: number;
|
|
isBackgroundBlurred?: boolean;
|
|
theme: ThemeKey;
|
|
customBackground?: string;
|
|
backgroundColor?: string;
|
|
};
|
|
|
|
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<OwnProps & StateProps> = ({
|
|
page,
|
|
children,
|
|
isRightColumnShown,
|
|
shouldSkipHistoryAnimations,
|
|
leftColumnWidth,
|
|
theme,
|
|
backgroundColor,
|
|
customBackground,
|
|
isBackgroundBlurred,
|
|
}) => {
|
|
const { setIsUiReady } = getActions();
|
|
|
|
const [isReady, markReady] = useFlag();
|
|
const {
|
|
shouldRender: shouldRenderMask, transitionClassNames,
|
|
} = useShowTransition(!isReady, undefined, true);
|
|
|
|
const customBackgroundValue = useCustomBackground(theme, customBackground);
|
|
|
|
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
|
|
}, []);
|
|
|
|
const middleClassName = buildClassName(
|
|
'middle bg-layers',
|
|
transitionClassNames,
|
|
customBackground && 'custom-bg-image',
|
|
backgroundColor && 'custom-bg-color',
|
|
customBackground && isBackgroundBlurred && 'blurred',
|
|
isRightColumnShown && 'with-right-column',
|
|
);
|
|
const inlineStyles = [
|
|
`--theme-background-color: ${backgroundColor || (theme === 'dark' ? DARK_THEME_BG_COLOR : LIGHT_THEME_BG_COLOR)}`,
|
|
customBackgroundValue && `--custom-background: ${customBackgroundValue}`,
|
|
];
|
|
|
|
return (
|
|
<div id="UiLoader">
|
|
{children}
|
|
{shouldRenderMask && !shouldSkipHistoryAnimations && (
|
|
<div className={buildClassName('mask', transitionClassNames)}>
|
|
{page === 'main' ? (
|
|
<>
|
|
<div
|
|
className="left"
|
|
style={leftColumnWidth ? `width: ${leftColumnWidth}px` : undefined}
|
|
/>
|
|
<div
|
|
className={middleClassName}
|
|
style={buildStyle(...inlineStyles)}
|
|
/>
|
|
{isRightColumnShown && <div className="right" />}
|
|
</>
|
|
) : (
|
|
<div className="blank" />
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default withGlobal<OwnProps>(
|
|
(global): StateProps => {
|
|
const theme = selectTheme(global);
|
|
const {
|
|
isBlurred: isBackgroundBlurred, background: customBackground, backgroundColor,
|
|
} = global.settings.themes[theme] || {};
|
|
|
|
return {
|
|
shouldSkipHistoryAnimations: global.shouldSkipHistoryAnimations,
|
|
uiReadyState: global.uiReadyState,
|
|
isRightColumnShown: selectIsRightColumnShown(global),
|
|
leftColumnWidth: global.leftColumnWidth,
|
|
theme,
|
|
customBackground,
|
|
isBackgroundBlurred,
|
|
backgroundColor,
|
|
};
|
|
},
|
|
)(UiLoader);
|