[Perf] Reduce amount of global containers (DeleteChatModal, Transition, SafeLink, Audio)

This commit is contained in:
Alexander Zinchuk 2021-06-24 14:46:12 +03:00
parent 094a235373
commit 3f5c8edceb
11 changed files with 80 additions and 60 deletions

View File

@ -1,7 +1,6 @@
import React, {
FC, memo, useCallback, useEffect, useMemo, useRef, useState,
} from '../../lib/teact/teact';
import { withGlobal } from '../../lib/teact/teactn';
import {
ApiAudio, ApiMessage, ApiVoice,
@ -24,7 +23,6 @@ import { renderWaveformToDataUri } from './helpers/waveform';
import buildClassName from '../../util/buildClassName';
import renderText from './helpers/renderText';
import { decodeWaveform, interpolateArray } from '../../util/waveform';
import { selectTheme } from '../../modules/selectors';
import useMediaWithDownloadProgress from '../../hooks/useMediaWithDownloadProgress';
import useShowTransition from '../../hooks/useShowTransition';
import useBuffering from '../../hooks/useBuffering';
@ -39,10 +37,11 @@ import Link from '../ui/Link';
import './Audio.scss';
type OwnProps = {
theme: ISettings['theme'];
message: ApiMessage;
senderTitle?: string;
uploadProgress?: number;
renderingFor?: 'searchResult' | 'sharedMedia';
target?: 'searchResult' | 'sharedMedia';
date?: number;
lastSyncTime?: number;
className?: string;
@ -54,10 +53,6 @@ type OwnProps = {
onDateClick?: (messageId: number, chatId: number) => void;
};
type StateProps = {
theme: ISettings['theme'];
};
interface ISeekMethods {
handleStartSeek: (e: React.MouseEvent<HTMLElement>) => void;
handleSeek: (e: React.MouseEvent<HTMLElement>) => void;
@ -70,12 +65,12 @@ const MAX_SPIKES = IS_SINGLE_COLUMN_LAYOUT ? 50 : 75;
// This is needed for browsers requiring user interaction before playing.
const PRELOAD = true;
const Audio: FC<OwnProps & StateProps> = ({
const Audio: FC<OwnProps> = ({
theme,
message,
senderTitle,
uploadProgress,
renderingFor,
target,
date,
lastSyncTime,
className,
@ -229,8 +224,8 @@ const Audio: FC<OwnProps & StateProps> = ({
const fullClassName = buildClassName(
'Audio media-inner',
className,
isOwn && !renderingFor && 'own',
renderingFor && 'bigger',
isOwn && !target && 'own',
target && 'bigger',
isSelected && 'audio-is-selected',
);
@ -287,7 +282,7 @@ const Audio: FC<OwnProps & StateProps> = ({
<Button
round
ripple={!IS_SINGLE_COLUMN_LAYOUT}
size={renderingFor ? 'smaller' : 'tiny'}
size={target ? 'smaller' : 'tiny'}
className={buttonClassNames.join(' ')}
ariaLabel={isPlaying ? 'Pause audio' : 'Play audio'}
onClick={handleButtonClick}
@ -301,7 +296,7 @@ const Audio: FC<OwnProps & StateProps> = ({
<ProgressSpinner
progress={transferProgress}
transparent
size={renderingFor ? 'm' : 's'}
size={target ? 'm' : 's'}
onClick={isLoadingForPlaying ? handleButtonClick : undefined}
noCross={!isLoadingForPlaying}
/>
@ -318,12 +313,12 @@ const Audio: FC<OwnProps & StateProps> = ({
<i className={isDownloadStarted ? 'icon-close' : 'icon-arrow-down'} />
</Button>
)}
{renderingFor === 'searchResult' && renderSearchResult()}
{renderingFor !== 'searchResult' && audio && renderAudio(
{target === 'searchResult' && renderSearchResult()}
{target !== 'searchResult' && audio && renderAudio(
lang, audio, isPlaying, playProgress, bufferedProgress, seekHandlers, date,
onDateClick ? handleDateClick : undefined,
)}
{renderingFor !== 'searchResult' && voice && renderVoice(voice, renderedWaveform, isMediaUnread)}
{target !== 'searchResult' && voice && renderVoice(voice, renderedWaveform, isMediaUnread)}
</div>
);
};
@ -457,6 +452,4 @@ function renderSeekline(
);
}
export default memo(withGlobal<OwnProps>((global) => ({
theme: selectTheme(global),
}))(Audio));
export default memo(Audio);

View File

@ -28,6 +28,7 @@ export type OwnProps = {
isOpen: boolean;
chat: ApiChat;
onClose: () => void;
onCloseAnimationEnd?: () => void;
};
type StateProps = {
@ -53,6 +54,7 @@ const DeleteChatModal: FC<OwnProps & StateProps & DispatchProps> = ({
canDeleteForAll,
contactName,
onClose,
onCloseAnimationEnd,
leaveChannel,
deleteHistory,
deleteChannel,
@ -147,9 +149,10 @@ const DeleteChatModal: FC<OwnProps & StateProps & DispatchProps> = ({
return (
<Modal
isOpen={isOpen}
onClose={onClose}
className="DeleteChatModal"
header={renderHeader()}
onClose={onClose}
onCloseAnimationEnd={onCloseAnimationEnd}
>
{renderMessage()}
{canDeleteForAll && (

View File

@ -1,10 +1,8 @@
import React, { FC, memo, useCallback } from '../../lib/teact/teact';
import { withGlobal } from '../../lib/teact/teactn';
import { getDispatch } from '../../lib/teact/teactn';
import convertPunycode from '../../lib/punycode';
import { GlobalActions } from '../../global/types';
import { DEBUG, RE_TME_INVITE_LINK, RE_TME_LINK } from '../../config';
import { pick } from '../../util/iteratees';
import buildClassName from '../../util/buildClassName';
type OwnProps = {
@ -15,17 +13,15 @@ type OwnProps = {
isRtl?: boolean;
};
type DispatchProps = Pick<GlobalActions, 'toggleSafeLinkModal' | 'openTelegramLink'>;
const SafeLink: FC<OwnProps & DispatchProps> = ({
const SafeLink: FC<OwnProps> = ({
url,
text,
className,
children,
isRtl,
toggleSafeLinkModal,
openTelegramLink,
}) => {
const { toggleSafeLinkModal, openTelegramLink } = getDispatch();
const content = children || text;
const isNotSafe = url !== content;
@ -113,9 +109,4 @@ function getDomain(url?: string) {
return undefined;
}
export default memo(withGlobal<OwnProps>(
undefined,
(setGlobal, actions): DispatchProps => pick(actions, [
'toggleSafeLinkModal', 'openTelegramLink',
]),
)(SafeLink));
export default memo(SafeLink);

View File

@ -109,6 +109,7 @@ const Chat: FC<OwnProps & StateProps & DispatchProps> = ({
const ref = useRef<HTMLDivElement>(null);
const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useFlag();
const [shouldRenderDeleteModal, markRenderDeleteModal, unmarkRenderDeleteModal] = useFlag();
const { lastMessage, typingStatus } = chat || {};
const isAction = lastMessage && isActionMessage(lastMessage);
@ -171,10 +172,15 @@ const Chat: FC<OwnProps & StateProps & DispatchProps> = ({
focusLastMessage,
]);
function handleDelete() {
markRenderDeleteModal();
openDeleteModal();
}
const contextActions = useChatContextActions({
chat,
privateChatUser,
handleDelete: openDeleteModal,
handleDelete,
folderId,
isPinned,
isMuted,
@ -277,11 +283,14 @@ const Chat: FC<OwnProps & StateProps & DispatchProps> = ({
<Badge chat={chat} isPinned={isPinned} isMuted={isMuted} />
</div>
</div>
<DeleteChatModal
isOpen={isDeleteModalOpen}
onClose={closeDeleteModal}
chat={chat}
/>
{shouldRenderDeleteModal && (
<DeleteChatModal
isOpen={isDeleteModalOpen}
onClose={closeDeleteModal}
onCloseAnimationEnd={unmarkRenderDeleteModal}
chat={chat}
/>
)}
</ListItem>
);
};

View File

@ -31,6 +31,7 @@ type DispatchProps = Pick<GlobalActions, ('searchMessagesGlobal' | 'focusMessage
const runThrottled = throttle((cb) => cb(), 500, true);
const AudioResults: FC<OwnProps & StateProps & DispatchProps> = ({
theme,
isVoice,
searchQuery,
searchChatId,
@ -94,8 +95,9 @@ const AudioResults: FC<OwnProps & StateProps & DispatchProps> = ({
)}
<Audio
key={message.id}
theme={theme}
message={message}
renderingFor="searchResult"
target="searchResult"
senderTitle={getSenderName(lang, message, chatsById, usersById)}
date={message.date}
lastSyncTime={lastSyncTime}

View File

@ -2,8 +2,12 @@ import { GlobalState } from '../../../../global/types';
import {
ApiChat, ApiGlobalMessageSearchType, ApiMessage, ApiUser,
} from '../../../../api/types';
import { ISettings } from '../../../../types';
import { selectTheme } from '../../../../modules/selectors';
export type StateProps = {
theme: ISettings['theme'];
isLoading?: boolean;
chatsById: Record<number, ApiChat>;
usersById: Record<number, ApiUser>;
@ -30,6 +34,7 @@ export function createMapStateToProps(type: ApiGlobalMessageSearchType) {
const { foundIds } = (resultsByType && resultsByType[currentType]) || {};
return {
theme: selectTheme(global),
isLoading: foundIds === undefined
|| (fetchingStatus ? Boolean(fetchingStatus.chats || fetchingStatus.messages) : false),
chatsById,

View File

@ -19,7 +19,9 @@ import {
ApiSticker,
MAIN_THREAD_ID,
} from '../../../api/types';
import { FocusDirection, IAlbum, MediaViewerOrigin } from '../../../types';
import {
FocusDirection, IAlbum, ISettings, MediaViewerOrigin,
} from '../../../types';
import { IS_ANDROID } from '../../../util/environment';
import { pick } from '../../../util/iteratees';
@ -40,7 +42,9 @@ import {
selectForwardedSender,
selectThreadTopMessageId,
selectShouldAutoLoadMedia,
selectShouldAutoPlayMedia, selectShouldLoopStickers,
selectShouldAutoPlayMedia,
selectShouldLoopStickers,
selectTheme,
} from '../../../modules/selectors';
import {
getMessageContent,
@ -117,6 +121,7 @@ type OwnProps = {
} & MessagePositionProperties;
type StateProps = {
theme: ISettings['theme'];
forceSenderName?: boolean;
sender?: ApiUser | ApiChat;
originSender?: ApiUser | ApiChat;
@ -179,6 +184,7 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
isFirstInDocumentGroup,
isLastInDocumentGroup,
isLastInList,
theme,
forceSenderName,
sender,
originSender,
@ -601,6 +607,7 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
)}
{(audio || voice) && (
<Audio
theme={theme}
message={message}
uploadProgress={uploadProgress}
lastSyncTime={lastSyncTime}
@ -907,6 +914,7 @@ export default memo(withGlobal<OwnProps>(
}
return {
theme: selectTheme(global),
forceSenderName,
sender,
originSender,

View File

@ -28,8 +28,8 @@ type StateProps = {
type DispatchProps = Pick<GlobalActions, ('toggleMessageSelection')>;
export default function withSelectControl(WrapedComponent: FC) {
const Component: FC<OwnProps & StateProps & DispatchProps> = (props) => {
export default function withSelectControl(WrappedComponent: FC) {
const ComponentWithSelectControl: FC<OwnProps & StateProps & DispatchProps> = (props) => {
const {
isInSelectMode,
isSelected,
@ -77,7 +77,7 @@ export default function withSelectControl(WrapedComponent: FC) {
</div>
)}
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
<WrapedComponent {...newProps} />
<WrappedComponent {...newProps} />
</div>
);
};
@ -93,5 +93,5 @@ export default function withSelectControl(WrapedComponent: FC) {
(setGlobal, actions) => pick(actions, [
'toggleMessageSelection',
]),
)(Component));
)(ComponentWithSelectControl));
}

View File

@ -11,6 +11,7 @@ import {
} from '../../api/types';
import { GlobalActions } from '../../global/types';
import {
ISettings,
MediaViewerOrigin, ProfileState, ProfileTabType, SharedMediaType,
} from '../../types';
@ -29,6 +30,7 @@ import {
selectChat,
selectCurrentMediaSearch,
selectIsRightColumnShown,
selectTheme,
} from '../../modules/selectors';
import { pick } from '../../util/iteratees';
import { captureEvents, SwipeDirection } from '../../util/captureEvents';
@ -63,6 +65,7 @@ type OwnProps = {
};
type StateProps = {
theme: ISettings['theme'];
isChannel?: boolean;
resolvedUserId?: number;
chatMessages?: Record<number, ApiMessage>;
@ -96,6 +99,7 @@ const Profile: FC<OwnProps & StateProps & DispatchProps> = ({
chatId,
profileState,
onProfileStateChange,
theme,
isChannel,
resolvedUserId,
chatMessages,
@ -287,8 +291,9 @@ const Profile: FC<OwnProps & StateProps & DispatchProps> = ({
viewportIds!.map((id) => chatMessages[id] && (
<Audio
key={id}
renderingFor="sharedMedia"
theme={theme}
message={chatMessages[id]}
target="sharedMedia"
date={chatMessages[id].date}
lastSyncTime={lastSyncTime}
className="scroll-item"
@ -394,6 +399,7 @@ export default memo(withGlobal<OwnProps>(
}
return {
theme: selectTheme(global),
isChannel,
resolvedUserId,
chatMessages,

View File

@ -2,7 +2,7 @@ import { RefObject } from 'react';
import React, {
FC, useLayoutEffect, useRef,
} from '../../lib/teact/teact';
import { withGlobal } from '../../lib/teact/teactn';
import { getGlobal } from '../../lib/teact/teactn';
import { ANIMATION_END_DELAY } from '../../config';
import { IS_SINGLE_COLUMN_LAYOUT } from '../../util/environment';
@ -32,10 +32,6 @@ type OwnProps = {
children: ChildrenFn;
};
type StateProps = {
animationLevel: number;
};
const ANIMATION_DURATION = {
slide: 450,
'slide-reversed': 450,
@ -51,7 +47,7 @@ const ANIMATION_DURATION = {
const CLEANED_UP = Symbol('CLEANED_UP');
const Transition: FC<OwnProps & StateProps> = ({
const Transition: FC<OwnProps> = ({
ref,
activeKey,
name,
@ -64,8 +60,10 @@ const Transition: FC<OwnProps & StateProps> = ({
onStart,
onStop,
children,
animationLevel,
}) => {
// No need for a container to update on change
const { animationLevel } = getGlobal().settings.byKey;
// eslint-disable-next-line no-null/no-null
let containerRef = useRef<HTMLDivElement>(null);
if (ref) {
@ -255,7 +253,4 @@ const Transition: FC<OwnProps & StateProps> = ({
);
};
export default withGlobal<OwnProps>((global) => {
const { animationLevel } = global.settings.byKey;
return { animationLevel };
})(Transition);
export default Transition;

View File

@ -218,6 +218,14 @@ if (DEBUG) {
document.addEventListener('dblclick', () => {
// eslint-disable-next-line no-console
console.log('GLOBAL CONTAINERS', orderBy(Object.values(containers), 'DEBUG_updates', 'desc'));
console.log(
'GLOBAL CONTAINERS',
orderBy(
Array.from(containers.values())
.map(({ DEBUG_componentName, DEBUG_updates }) => ({ DEBUG_componentName, DEBUG_updates })),
'DEBUG_updates',
'desc',
),
);
});
}