[Perf] Reduce unneeded renders in various components
This commit is contained in:
parent
a9e699e82f
commit
07ac02b201
@ -1,7 +1,7 @@
|
||||
import React, {
|
||||
FC, memo, useCallback, useLayoutEffect, useMemo, useRef,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getDispatch, withGlobal } from '../../../lib/teact/teactn';
|
||||
import { getDispatch, getGlobal, withGlobal } from '../../../lib/teact/teactn';
|
||||
|
||||
import useLang, { LangFn } from '../../../hooks/useLang';
|
||||
|
||||
@ -68,7 +68,6 @@ type StateProps = {
|
||||
user?: ApiUser;
|
||||
userStatus?: ApiUserStatus;
|
||||
actionTargetUserIds?: string[];
|
||||
usersById?: Record<string, ApiUser>;
|
||||
actionTargetMessage?: ApiMessage;
|
||||
actionTargetChatId?: string;
|
||||
lastMessageSender?: ApiUser;
|
||||
@ -95,7 +94,6 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
user,
|
||||
userStatus,
|
||||
actionTargetUserIds,
|
||||
usersById,
|
||||
lastMessageSender,
|
||||
lastMessageOutgoingStatus,
|
||||
actionTargetMessage,
|
||||
@ -132,10 +130,14 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
const isRoundVideo = Boolean(lastMessage && getMessageRoundVideo(lastMessage));
|
||||
|
||||
const actionTargetUsers = useMemo(() => {
|
||||
return actionTargetUserIds
|
||||
? actionTargetUserIds.map((userId) => usersById?.[userId]).filter<ApiUser>(Boolean as any)
|
||||
: undefined;
|
||||
}, [actionTargetUserIds, usersById]);
|
||||
if (!actionTargetUserIds) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// No need for expensive global updates on users, so we avoid them
|
||||
const usersById = getGlobal().users.byId;
|
||||
return actionTargetUserIds.map((userId) => usersById[userId]).filter<ApiUser>(Boolean as any);
|
||||
}, [actionTargetUserIds]);
|
||||
|
||||
// Sets animation excess values when `orderDiff` changes and then resets excess values to animate.
|
||||
useLayoutEffect(() => {
|
||||
@ -360,7 +362,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
: undefined;
|
||||
const { targetUserIds: actionTargetUserIds, targetChatId: actionTargetChatId } = lastMessageAction || {};
|
||||
const privateChatUserId = getPrivateChatUserId(chat);
|
||||
const { byId: usersById } = global.users;
|
||||
const {
|
||||
chatId: currentChatId,
|
||||
threadId: currentThreadId,
|
||||
@ -386,7 +387,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
user: selectUser(global, privateChatUserId),
|
||||
userStatus: selectUserStatus(global, privateChatUserId),
|
||||
}),
|
||||
...(actionTargetUserIds && { usersById }),
|
||||
};
|
||||
},
|
||||
)(Chat));
|
||||
|
||||
@ -85,7 +85,6 @@ type StateProps = {
|
||||
isLeftColumnShown?: boolean;
|
||||
isRightColumnShown?: boolean;
|
||||
audioMessage?: ApiMessage;
|
||||
chatsById?: Record<string, ApiChat>;
|
||||
messagesCount?: number;
|
||||
isChatWithSelf?: boolean;
|
||||
isChatWithBot?: boolean;
|
||||
@ -110,7 +109,6 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
isRightColumnShown,
|
||||
audioMessage,
|
||||
chat,
|
||||
chatsById,
|
||||
messagesCount,
|
||||
isChatWithSelf,
|
||||
isChatWithBot,
|
||||
@ -229,12 +227,12 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
]);
|
||||
|
||||
const unreadCount = useMemo(() => {
|
||||
if (!isLeftColumnHideable || !chatsById) {
|
||||
if (!isLeftColumnHideable) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return selectCountNotMutedUnread(getGlobal()) || undefined;
|
||||
}, [isLeftColumnHideable, chatsById]);
|
||||
}, [isLeftColumnHideable]);
|
||||
|
||||
const canToolsCollideWithChatInfo = (
|
||||
windowWidth >= MIN_SCREEN_WIDTH_FOR_STATIC_LEFT_COLUMN
|
||||
@ -445,7 +443,6 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global, { chatId, threadId, messageListType }): StateProps => {
|
||||
const { isLeftColumnShown, lastSyncTime, shouldSkipHistoryAnimations } = global;
|
||||
const { byId: chatsById } = global.chats;
|
||||
const chat = selectChat(global, chatId);
|
||||
|
||||
const { typingStatus } = chat || {};
|
||||
@ -474,7 +471,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
isSelectModeActive: selectIsInSelectMode(global),
|
||||
audioMessage,
|
||||
chat,
|
||||
chatsById,
|
||||
messagesCount,
|
||||
isChatWithSelf: selectIsChatWithSelf(global, chatId),
|
||||
isChatWithBot: chat && selectIsChatWithBot(global, chat),
|
||||
|
||||
@ -3,7 +3,6 @@ import React, { FC, memo, useCallback } from '../../../lib/teact/teact';
|
||||
import { CONTENT_TYPES_WITH_PREVIEW } from '../../../config';
|
||||
import { IS_TOUCH_ENV } from '../../../util/environment';
|
||||
import { openSystemFilesDialog } from '../../../util/systemFilesDialog';
|
||||
import { IAllowedAttachmentOptions } from '../../../modules/helpers';
|
||||
import useMouseInside from '../../../hooks/useMouseInside';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
|
||||
@ -14,14 +13,15 @@ import './AttachMenu.scss';
|
||||
|
||||
export type OwnProps = {
|
||||
isOpen: boolean;
|
||||
allowedAttachmentOptions: IAllowedAttachmentOptions;
|
||||
canAttachMedia: boolean;
|
||||
canAttachPolls: boolean;
|
||||
onFileSelect: (files: File[], isQuick: boolean) => void;
|
||||
onPollCreate: () => void;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
const AttachMenu: FC<OwnProps> = ({
|
||||
isOpen, allowedAttachmentOptions, onFileSelect, onPollCreate, onClose,
|
||||
isOpen, canAttachMedia, canAttachPolls, onFileSelect, onPollCreate, onClose,
|
||||
}) => {
|
||||
const [handleMouseEnter, handleMouseLeave] = useMouseInside(isOpen, onClose);
|
||||
|
||||
@ -46,8 +46,6 @@ const AttachMenu: FC<OwnProps> = ({
|
||||
|
||||
const lang = useLang();
|
||||
|
||||
const { canAttachMedia, canAttachPolls } = allowedAttachmentOptions;
|
||||
|
||||
return (
|
||||
<Menu
|
||||
isOpen={isOpen}
|
||||
|
||||
@ -2,7 +2,7 @@ import React, {
|
||||
FC, memo, useCallback, useEffect, useRef,
|
||||
} from '../../../lib/teact/teact';
|
||||
|
||||
import { ApiAttachment, ApiChatMember, ApiUser } from '../../../api/types';
|
||||
import { ApiAttachment, ApiChatMember } from '../../../api/types';
|
||||
|
||||
import {
|
||||
CONTENT_TYPES_WITH_PREVIEW,
|
||||
@ -17,6 +17,7 @@ import useMentionTooltip from './hooks/useMentionTooltip';
|
||||
import useEmojiTooltip from './hooks/useEmojiTooltip';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useFlag from '../../../hooks/useFlag';
|
||||
import { useStateRef } from '../../../hooks/useStateRef';
|
||||
|
||||
import Button from '../../ui/Button';
|
||||
import Modal from '../../ui/Modal';
|
||||
@ -35,7 +36,6 @@ export type OwnProps = {
|
||||
isReady?: boolean;
|
||||
currentUserId?: string;
|
||||
groupChatMembers?: ApiChatMember[];
|
||||
usersById?: Record<string, ApiUser>;
|
||||
recentEmojis: string[];
|
||||
baseEmojiKeywords?: Record<string, string[]>;
|
||||
emojiKeywords?: Record<string, string[]>;
|
||||
@ -56,7 +56,6 @@ const AttachmentModal: FC<OwnProps> = ({
|
||||
isReady,
|
||||
currentUserId,
|
||||
groupChatMembers,
|
||||
usersById,
|
||||
recentEmojis,
|
||||
baseEmojiKeywords,
|
||||
emojiKeywords,
|
||||
@ -66,8 +65,8 @@ const AttachmentModal: FC<OwnProps> = ({
|
||||
onFileAppend,
|
||||
onClear,
|
||||
}) => {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const hideTimeoutRef = useRef<number>(null);
|
||||
const captionRef = useStateRef(caption);
|
||||
const hideTimeoutRef = useRef<number>();
|
||||
const prevAttachments = usePrevious(attachments);
|
||||
const renderingAttachments = attachments.length ? attachments : prevAttachments;
|
||||
const isOpen = Boolean(attachments.length);
|
||||
@ -79,7 +78,7 @@ const AttachmentModal: FC<OwnProps> = ({
|
||||
isMentionTooltipOpen, closeMentionTooltip, insertMention, mentionFilteredUsers,
|
||||
} = useMentionTooltip(
|
||||
isOpen,
|
||||
caption,
|
||||
captionRef,
|
||||
onCaptionUpdate,
|
||||
EDITABLE_INPUT_MODAL_ID,
|
||||
groupChatMembers,
|
||||
@ -90,7 +89,7 @@ const AttachmentModal: FC<OwnProps> = ({
|
||||
isEmojiTooltipOpen, closeEmojiTooltip, filteredEmojis, insertEmoji,
|
||||
} = useEmojiTooltip(
|
||||
isOpen,
|
||||
caption,
|
||||
captionRef,
|
||||
recentEmojis,
|
||||
EDITABLE_INPUT_MODAL_ID,
|
||||
onCaptionUpdate,
|
||||
@ -150,6 +149,7 @@ const AttachmentModal: FC<OwnProps> = ({
|
||||
|
||||
if (hideTimeoutRef.current) {
|
||||
window.clearTimeout(hideTimeoutRef.current);
|
||||
hideTimeoutRef.current = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,7 +238,6 @@ const AttachmentModal: FC<OwnProps> = ({
|
||||
onClose={closeMentionTooltip}
|
||||
onInsertUserName={insertMention}
|
||||
filteredUsers={mentionFilteredUsers}
|
||||
usersById={usersById}
|
||||
/>
|
||||
<EmojiTooltip
|
||||
isOpen={isEmojiTooltipOpen}
|
||||
|
||||
@ -25,6 +25,7 @@ import {
|
||||
BASE_EMOJI_KEYWORD_LANG, EDITABLE_INPUT_ID, REPLIES_USER_ID, SCHEDULED_WHEN_ONLINE, SEND_MESSAGE_ACTION_INTERVAL,
|
||||
} from '../../../config';
|
||||
import { IS_VOICE_RECORDING_SUPPORTED, IS_SINGLE_COLUMN_LAYOUT, IS_IOS } from '../../../util/environment';
|
||||
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
||||
import {
|
||||
selectChat,
|
||||
selectIsRightColumnShown,
|
||||
@ -37,6 +38,7 @@ import {
|
||||
selectChatBot,
|
||||
selectChatUser,
|
||||
selectChatMessage,
|
||||
selectUser,
|
||||
selectUserStatus,
|
||||
} from '../../../modules/selectors';
|
||||
import {
|
||||
@ -67,6 +69,7 @@ import useLang from '../../../hooks/useLang';
|
||||
import useSendMessageAction from '../../../hooks/useSendMessageAction';
|
||||
import useInterval from '../../../hooks/useInterval';
|
||||
import useOnChange from '../../../hooks/useOnChange';
|
||||
import { useStateRef } from '../../../hooks/useStateRef';
|
||||
import useVoiceRecording from './hooks/useVoiceRecording';
|
||||
import useClipboardPaste from './hooks/useClipboardPaste';
|
||||
import useDraft from './hooks/useDraft';
|
||||
@ -80,6 +83,8 @@ import DeleteMessageModal from '../../common/DeleteMessageModal.async';
|
||||
import Button from '../../ui/Button';
|
||||
import ResponsiveHoverButton from '../../ui/ResponsiveHoverButton';
|
||||
import Spinner from '../../ui/Spinner';
|
||||
import CalendarModal from '../../common/CalendarModal.async';
|
||||
import Avatar from '../../common/Avatar';
|
||||
import AttachMenu from './AttachMenu.async';
|
||||
import SymbolMenu from './SymbolMenu.async';
|
||||
import InlineBotTooltip from './InlineBotTooltip.async';
|
||||
@ -96,10 +101,7 @@ import BotCommandMenu from './BotCommandMenu.async';
|
||||
import PollModal from './PollModal.async';
|
||||
import DropArea, { DropAreaState } from './DropArea.async';
|
||||
import WebPagePreview from './WebPagePreview';
|
||||
import Portal from '../../ui/Portal';
|
||||
import CalendarModal from '../../common/CalendarModal.async';
|
||||
import SendAsMenu from './SendAsMenu.async';
|
||||
import Avatar from '../../common/Avatar';
|
||||
|
||||
import './Composer.scss';
|
||||
|
||||
@ -131,7 +133,6 @@ type StateProps =
|
||||
stickersForEmoji?: ApiSticker[];
|
||||
groupChatMembers?: ApiChatMember[];
|
||||
currentUserId?: string;
|
||||
usersById?: Record<string, ApiUser>;
|
||||
recentEmojis: string[];
|
||||
lastSyncTime?: number;
|
||||
contentToBeScheduled?: GlobalState['messages']['contentToBeScheduled'];
|
||||
@ -195,7 +196,6 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
groupChatMembers,
|
||||
topInlineBotIds,
|
||||
currentUserId,
|
||||
usersById,
|
||||
lastSyncTime,
|
||||
contentToBeScheduled,
|
||||
shouldSuggestStickers,
|
||||
@ -231,6 +231,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const appendixRef = useRef<HTMLDivElement>(null);
|
||||
const [html, setHtml] = useState<string>('');
|
||||
const htmlRef = useStateRef(html);
|
||||
const lastMessageSendTimeSeconds = useRef<number>();
|
||||
const prevDropAreaState = usePrevious(dropAreaState);
|
||||
const [isCalendarOpen, openCalendar, closeCalendar] = useFlag();
|
||||
@ -241,12 +242,6 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
const sendAsIds = chat?.sendAsIds;
|
||||
const sendMessageAction = useSendMessageAction(chatId, threadId);
|
||||
|
||||
// Cache for frequently updated state
|
||||
const htmlRef = useRef<string>(html);
|
||||
useEffect(() => {
|
||||
htmlRef.current = html;
|
||||
}, [html]);
|
||||
|
||||
useEffect(() => {
|
||||
lastMessageSendTimeSeconds.current = undefined;
|
||||
}, [chatId]);
|
||||
@ -323,7 +318,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
isMentionTooltipOpen, closeMentionTooltip, insertMention, mentionFilteredUsers,
|
||||
} = useMentionTooltip(
|
||||
!attachments.length,
|
||||
html,
|
||||
htmlRef,
|
||||
setHtml,
|
||||
undefined,
|
||||
groupChatMembers,
|
||||
@ -365,15 +360,15 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
handleContextMenuHide,
|
||||
} = useContextMenuHandlers(mainButtonRef, !(mainButtonState === MainButtonState.Send && canShowCustomSendMenu));
|
||||
|
||||
const allowedAttachmentOptions = useMemo(() => {
|
||||
return getAllowedAttachmentOptions(chat, isChatWithBot);
|
||||
}, [chat, isChatWithBot]);
|
||||
const {
|
||||
canSendStickers, canSendGifs, canAttachMedia, canAttachPolls, canAttachEmbedLinks,
|
||||
} = useMemo(() => getAllowedAttachmentOptions(chat, isChatWithBot), [chat, isChatWithBot]);
|
||||
|
||||
const isAdmin = chat && isChatAdmin(chat);
|
||||
const slowMode = getChatSlowModeOptions(chat);
|
||||
|
||||
const { isStickerTooltipOpen, closeStickerTooltip } = useStickerTooltip(
|
||||
Boolean(shouldSuggestStickers && allowedAttachmentOptions.canSendStickers && !attachments.length),
|
||||
Boolean(shouldSuggestStickers && canSendStickers && !attachments.length),
|
||||
html,
|
||||
stickersForEmoji,
|
||||
!isReady,
|
||||
@ -381,8 +376,8 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
const {
|
||||
isEmojiTooltipOpen, closeEmojiTooltip, filteredEmojis, insertEmoji,
|
||||
} = useEmojiTooltip(
|
||||
Boolean(shouldSuggestStickers && allowedAttachmentOptions.canSendStickers && !attachments.length),
|
||||
html,
|
||||
Boolean(shouldSuggestStickers && canSendStickers && !attachments.length),
|
||||
htmlRef,
|
||||
recentEmojis,
|
||||
undefined,
|
||||
setHtml,
|
||||
@ -413,7 +408,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
requestAnimationFrame(() => {
|
||||
focusEditableElement(messageInput);
|
||||
});
|
||||
}, []);
|
||||
}, [htmlRef]);
|
||||
|
||||
const removeSymbol = useCallback(() => {
|
||||
const selection = window.getSelection()!;
|
||||
@ -427,13 +422,13 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
|
||||
setHtml(deleteLastCharacterOutsideSelection(htmlRef.current!));
|
||||
}, []);
|
||||
}, [htmlRef]);
|
||||
|
||||
const resetComposer = useCallback((shouldPreserveInput = false) => {
|
||||
if (!shouldPreserveInput) {
|
||||
setHtml('');
|
||||
}
|
||||
setAttachments([]);
|
||||
setAttachments(MEMO_EMPTY_ARRAY);
|
||||
closeStickerTooltip();
|
||||
closeCalendar();
|
||||
setScheduledMessageArgs(undefined);
|
||||
@ -459,7 +454,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
}, [chatId, resetComposer, stopRecordingVoiceRef]);
|
||||
|
||||
const handleEditComplete = useEditing(htmlRef, setHtml, editingMessage, resetComposer, openDeleteModal);
|
||||
useDraft(draft, chatId, threadId, html, htmlRef, setHtml, editingMessage);
|
||||
useDraft(draft, chatId, threadId, htmlRef, setHtml, editingMessage);
|
||||
useClipboardPaste(insertTextAndUpdateCursor, setAttachments, editingMessage);
|
||||
|
||||
const handleFileSelect = useCallback(async (files: File[], isQuick: boolean) => {
|
||||
@ -474,7 +469,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
}, [attachments]);
|
||||
|
||||
const handleClearAttachment = useCallback(() => {
|
||||
setAttachments([]);
|
||||
setAttachments(MEMO_EMPTY_ARRAY);
|
||||
}, []);
|
||||
|
||||
const handleSend = useCallback(async (isSilent = false, scheduledAt?: number) => {
|
||||
@ -580,7 +575,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
}, [
|
||||
connectionState, attachments, activeVoiceRecording, isForwarding, clearDraft, chatId, serverTimeOffset,
|
||||
resetComposer, stopRecordingVoice, showDialog, slowMode, isAdmin, sendMessage, forwardMessages, lang,
|
||||
resetComposer, stopRecordingVoice, showDialog, slowMode, isAdmin, sendMessage, forwardMessages, lang, htmlRef,
|
||||
]);
|
||||
|
||||
const handleActivateBotCommandMenu = useCallback(() => {
|
||||
@ -791,8 +786,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
activeVoiceRecording, openCalendar, pauseRecordingVoice,
|
||||
]);
|
||||
|
||||
const areVoiceMessagesNotAllowed = mainButtonState === MainButtonState.Record
|
||||
&& !allowedAttachmentOptions.canAttachMedia;
|
||||
const areVoiceMessagesNotAllowed = mainButtonState === MainButtonState.Record && !canAttachMedia;
|
||||
|
||||
const prevEditedMessage = usePrevious(editingMessage, true);
|
||||
const renderedEditedMessage = editingMessage || prevEditedMessage;
|
||||
@ -836,15 +830,13 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
{allowedAttachmentOptions.canAttachMedia && isReady && (
|
||||
<Portal containerId="#middle-column-portals">
|
||||
<DropArea
|
||||
isOpen={dropAreaState !== DropAreaState.None}
|
||||
withQuick={[dropAreaState, prevDropAreaState].includes(DropAreaState.QuickFile)}
|
||||
onHide={onDropHide}
|
||||
onFileSelect={handleFileSelect}
|
||||
/>
|
||||
</Portal>
|
||||
{canAttachMedia && isReady && (
|
||||
<DropArea
|
||||
isOpen={dropAreaState !== DropAreaState.None}
|
||||
withQuick={dropAreaState === DropAreaState.QuickFile || prevDropAreaState === DropAreaState.QuickFile}
|
||||
onHide={onDropHide}
|
||||
onFileSelect={handleFileSelect}
|
||||
/>
|
||||
)}
|
||||
<AttachmentModal
|
||||
chatId={chatId}
|
||||
@ -853,7 +845,6 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
caption={attachments.length ? html : ''}
|
||||
groupChatMembers={groupChatMembers}
|
||||
currentUserId={currentUserId}
|
||||
usersById={usersById}
|
||||
recentEmojis={recentEmojis}
|
||||
isReady={isReady}
|
||||
onCaptionUpdate={setHtml}
|
||||
@ -889,12 +880,10 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
onClose={closeMentionTooltip}
|
||||
onInsertUserName={insertMention}
|
||||
filteredUsers={mentionFilteredUsers}
|
||||
usersById={usersById}
|
||||
/>
|
||||
<InlineBotTooltip
|
||||
isOpen={isInlineBotTooltipOpen}
|
||||
botId={inlineBotId}
|
||||
allowedAttachmentOptions={allowedAttachmentOptions}
|
||||
isGallery={isInlineBotTooltipGallery}
|
||||
inlineBotResults={inlineBotResults}
|
||||
switchPm={inlineBotSwitchPm}
|
||||
@ -916,7 +905,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
chatId={chatId}
|
||||
threadId={threadId}
|
||||
messageText={!attachments.length ? html : ''}
|
||||
disabled={!allowedAttachmentOptions.canAttachEmbedLinks}
|
||||
disabled={!canAttachEmbedLinks}
|
||||
/>
|
||||
<div className="message-input-wrapper">
|
||||
{isChatWithBot && botCommands !== false && !activeVoiceRecording && !editingMessage && (
|
||||
@ -1044,7 +1033,8 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
/>
|
||||
<AttachMenu
|
||||
isOpen={isAttachMenuOpen}
|
||||
allowedAttachmentOptions={allowedAttachmentOptions}
|
||||
canAttachMedia={canAttachMedia}
|
||||
canAttachPolls={canAttachPolls}
|
||||
onFileSelect={handleFileSelect}
|
||||
onPollCreate={openPollModal}
|
||||
onClose={closeAttachMenu}
|
||||
@ -1067,7 +1057,8 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
chatId={chatId}
|
||||
threadId={threadId}
|
||||
isOpen={isSymbolMenuOpen}
|
||||
allowedAttachmentOptions={allowedAttachmentOptions}
|
||||
canSendGifs={canSendGifs}
|
||||
canSendStickers={canSendStickers}
|
||||
onLoad={onSymbolMenuLoadingComplete}
|
||||
onClose={closeSymbolMenu}
|
||||
onEmojiSelect={insertTextAndUpdateCursor}
|
||||
@ -1145,12 +1136,10 @@ export default memo(withGlobal<OwnProps>(
|
||||
const emojiKeywords = language !== BASE_EMOJI_KEYWORD_LANG ? global.emojiKeywords[language] : undefined;
|
||||
const botKeyboardMessageId = messageWithActualBotKeyboard ? messageWithActualBotKeyboard.id : undefined;
|
||||
const keyboardMessage = botKeyboardMessageId ? selectChatMessage(global, chatId, botKeyboardMessageId) : undefined;
|
||||
const usersById = global.users.byId;
|
||||
const chatsById = global.chats.byId;
|
||||
const { currentUserId } = global;
|
||||
const sendAsId = chat?.fullInfo ? chat?.fullInfo?.sendAsId || currentUserId : undefined;
|
||||
const sendAsUser = sendAsId ? usersById?.[sendAsId] : undefined;
|
||||
const sendAsChat = !sendAsUser && sendAsId ? chatsById?.[sendAsId] : undefined;
|
||||
const sendAsUser = sendAsId ? selectUser(global, sendAsId) : undefined;
|
||||
const sendAsChat = !sendAsUser && sendAsId ? selectChat(global, sendAsId) : undefined;
|
||||
|
||||
return {
|
||||
editingMessage: selectEditingMessage(global, chatId, threadId, messageListType),
|
||||
@ -1179,7 +1168,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
groupChatMembers: chat?.fullInfo?.members,
|
||||
topInlineBotIds: global.topInlineBots?.userIds,
|
||||
currentUserId,
|
||||
usersById,
|
||||
lastSyncTime: global.lastSyncTime,
|
||||
contentToBeScheduled: global.messages.contentToBeScheduled,
|
||||
shouldSuggestStickers,
|
||||
|
||||
@ -8,6 +8,7 @@ import buildClassName from '../../../util/buildClassName';
|
||||
import captureEscKeyListener from '../../../util/captureEscKeyListener';
|
||||
import usePrevious from '../../../hooks/usePrevious';
|
||||
|
||||
import Portal from '../../ui/Portal';
|
||||
import DropTarget from './DropTarget';
|
||||
|
||||
import './DropArea.scss';
|
||||
@ -84,10 +85,12 @@ const DropArea: FC<OwnProps> = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={className} onDragLeave={handleDragLeave} onDragOver={handleDragOver} onDrop={onHide}>
|
||||
<DropTarget onFileSelect={handleFilesDrop} />
|
||||
{(withQuick || prevWithQuick) && <DropTarget onFileSelect={handleQuickFilesDrop} isQuick />}
|
||||
</div>
|
||||
<Portal containerId="#middle-column-portals">
|
||||
<div className={className} onDragLeave={handleDragLeave} onDragOver={handleDragOver} onDrop={onHide}>
|
||||
<DropTarget onFileSelect={handleFilesDrop} />
|
||||
{(withQuick || prevWithQuick) && <DropTarget onFileSelect={handleQuickFilesDrop} isQuick />}
|
||||
</div>
|
||||
</Portal>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -3,7 +3,6 @@ import React, {
|
||||
} from '../../../lib/teact/teact';
|
||||
|
||||
import { ApiBotInlineMediaResult, ApiBotInlineResult, ApiBotInlineSwitchPm } from '../../../api/types';
|
||||
import { IAllowedAttachmentOptions } from '../../../modules/helpers';
|
||||
import { LoadMoreDirection } from '../../../types';
|
||||
|
||||
import { IS_TOUCH_ENV } from '../../../util/environment';
|
||||
@ -32,7 +31,6 @@ export type OwnProps = {
|
||||
isOpen: boolean;
|
||||
botId?: string;
|
||||
isGallery?: boolean;
|
||||
allowedAttachmentOptions: IAllowedAttachmentOptions;
|
||||
inlineBotResults?: (ApiBotInlineResult | ApiBotInlineMediaResult)[];
|
||||
switchPm?: ApiBotInlineSwitchPm;
|
||||
onSelectResult: (inlineResult: ApiBotInlineMediaResult | ApiBotInlineResult) => void;
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import React, {
|
||||
FC, useCallback, useEffect, useRef, memo,
|
||||
} from '../../../lib/teact/teact';
|
||||
import usePrevious from '../../../hooks/usePrevious';
|
||||
import { getGlobal } from '../../../lib/teact/teactn';
|
||||
|
||||
import { ApiUser } from '../../../api/types';
|
||||
|
||||
import useShowTransition from '../../../hooks/useShowTransition';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import setTooltipItemVisible from '../../../util/setTooltipItemVisible';
|
||||
import usePrevious from '../../../hooks/usePrevious';
|
||||
import useShowTransition from '../../../hooks/useShowTransition';
|
||||
import { useKeyboardNavigation } from './hooks/useKeyboardNavigation';
|
||||
|
||||
import ListItem from '../../ui/ListItem';
|
||||
@ -20,14 +21,12 @@ export type OwnProps = {
|
||||
onClose: () => void;
|
||||
onInsertUserName: (user: ApiUser, forceFocus?: boolean) => void;
|
||||
filteredUsers?: ApiUser[];
|
||||
usersById?: Record<string, ApiUser>;
|
||||
};
|
||||
|
||||
const MentionTooltip: FC<OwnProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
onInsertUserName,
|
||||
usersById,
|
||||
filteredUsers,
|
||||
}) => {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
@ -35,13 +34,15 @@ const MentionTooltip: FC<OwnProps> = ({
|
||||
const { shouldRender, transitionClassNames } = useShowTransition(isOpen, undefined, undefined, false);
|
||||
|
||||
const handleUserSelect = useCallback((userId: string, forceFocus = false) => {
|
||||
const user = usersById?.[userId];
|
||||
// No need for expensive global updates on users, so we avoid them
|
||||
const usersById = getGlobal().users.byId;
|
||||
const user = usersById[userId];
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
|
||||
onInsertUserName(user, forceFocus);
|
||||
}, [usersById, onInsertUserName]);
|
||||
}, [onInsertUserName]);
|
||||
|
||||
const handleSelectMention = useCallback((member: ApiUser) => {
|
||||
handleUserSelect(member.id, true);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { FC } from '../../../lib/teact/teact';
|
||||
import React, { FC, memo } from '../../../lib/teact/teact';
|
||||
import { OwnProps } from './StickerTooltip';
|
||||
import { Bundles } from '../../../util/moduleLoader';
|
||||
|
||||
@ -12,4 +12,4 @@ const StickerTooltipAsync: FC<OwnProps> = (props) => {
|
||||
return StickerTooltip ? <StickerTooltip {...props} /> : undefined;
|
||||
};
|
||||
|
||||
export default StickerTooltipAsync;
|
||||
export default memo(StickerTooltipAsync);
|
||||
|
||||
@ -5,7 +5,6 @@ import { withGlobal } from '../../../lib/teact/teactn';
|
||||
|
||||
import { ApiSticker, ApiVideo } from '../../../api/types';
|
||||
|
||||
import { IAllowedAttachmentOptions } from '../../../modules/helpers';
|
||||
import { IS_SINGLE_COLUMN_LAYOUT, IS_TOUCH_ENV } from '../../../util/environment';
|
||||
import { fastRaf } from '../../../util/schedulers';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
@ -30,7 +29,8 @@ export type OwnProps = {
|
||||
chatId: string;
|
||||
threadId?: number;
|
||||
isOpen: boolean;
|
||||
allowedAttachmentOptions: IAllowedAttachmentOptions;
|
||||
canSendStickers: boolean;
|
||||
canSendGifs: boolean;
|
||||
onLoad: () => void;
|
||||
onClose: () => void;
|
||||
onEmojiSelect: (emoji: string) => void;
|
||||
@ -51,7 +51,8 @@ const SymbolMenu: FC<OwnProps & StateProps> = ({
|
||||
chatId,
|
||||
threadId,
|
||||
isOpen,
|
||||
allowedAttachmentOptions,
|
||||
canSendStickers,
|
||||
canSendGifs,
|
||||
isLeftColumnShown,
|
||||
onLoad,
|
||||
onClose,
|
||||
@ -131,8 +132,6 @@ const SymbolMenu: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const lang = useLang();
|
||||
|
||||
const { canSendStickers, canSendGifs } = allowedAttachmentOptions;
|
||||
|
||||
function renderContent(isActive: boolean, isFrom: boolean) {
|
||||
switch (activeTab) {
|
||||
case SymbolMenuTabs.Emoji:
|
||||
|
||||
@ -21,7 +21,6 @@ export default (
|
||||
draft: ApiFormattedText | undefined,
|
||||
chatId: string,
|
||||
threadId: number,
|
||||
html: string,
|
||||
htmlRef: { current: string },
|
||||
setHtml: (html: string) => void,
|
||||
editedMessage: ApiMessage | undefined,
|
||||
@ -29,8 +28,9 @@ export default (
|
||||
const { saveDraft, clearDraft } = getDispatch();
|
||||
|
||||
const updateDraft = useCallback((draftChatId: string, draftThreadId: number) => {
|
||||
if (htmlRef.current.length && !editedMessage) {
|
||||
saveDraft({ chatId: draftChatId, threadId: draftThreadId, draft: parseMessageInput(htmlRef.current!) });
|
||||
const currentHtml = htmlRef.current;
|
||||
if (currentHtml.length && !editedMessage) {
|
||||
saveDraft({ chatId: draftChatId, threadId: draftThreadId, draft: parseMessageInput(currentHtml!) });
|
||||
} else {
|
||||
clearDraft({ chatId: draftChatId, threadId: draftThreadId });
|
||||
}
|
||||
@ -75,6 +75,7 @@ export default (
|
||||
}
|
||||
}, [chatId, threadId, draft, setHtml, updateDraft, prevChatId, prevThreadId]);
|
||||
|
||||
const html = htmlRef.current;
|
||||
// Update draft when input changes
|
||||
const prevHtml = usePrevious(html);
|
||||
useEffect(() => {
|
||||
|
||||
@ -43,7 +43,7 @@ try {
|
||||
|
||||
export default function useEmojiTooltip(
|
||||
isAllowed: boolean,
|
||||
html: string,
|
||||
htmlRef: { current: string },
|
||||
recentEmojiIds: string[],
|
||||
inputId = EDITABLE_INPUT_ID,
|
||||
onUpdateHtml: (html: string) => void,
|
||||
@ -71,6 +71,7 @@ export default function useEmojiTooltip(
|
||||
}
|
||||
}, [isDisabled]);
|
||||
|
||||
const html = htmlRef.current;
|
||||
useEffect(() => {
|
||||
if (!isAllowed || !html || !byId || isDisabled) {
|
||||
unmarkIsOpen();
|
||||
@ -111,9 +112,10 @@ export default function useEmojiTooltip(
|
||||
]);
|
||||
|
||||
const insertEmoji = useCallback((textEmoji: string, isForce?: boolean) => {
|
||||
const atIndex = html.lastIndexOf(':', isForce ? html.lastIndexOf(':') - 1 : undefined);
|
||||
const currentHtml = htmlRef.current;
|
||||
const atIndex = currentHtml.lastIndexOf(':', isForce ? currentHtml.lastIndexOf(':') - 1 : undefined);
|
||||
if (atIndex !== -1) {
|
||||
onUpdateHtml(`${html.substr(0, atIndex)}${textEmoji}`);
|
||||
onUpdateHtml(`${currentHtml.substr(0, atIndex)}${textEmoji}`);
|
||||
const messageInput = document.getElementById(inputId)!;
|
||||
requestAnimationFrame(() => {
|
||||
focusEditableElement(messageInput, true);
|
||||
@ -121,7 +123,7 @@ export default function useEmojiTooltip(
|
||||
}
|
||||
|
||||
unmarkIsOpen();
|
||||
}, [html, inputId, onUpdateHtml, unmarkIsOpen]);
|
||||
}, [htmlRef, inputId, onUpdateHtml, unmarkIsOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen && shouldForceInsertEmoji && filteredEmojis.length) {
|
||||
|
||||
@ -8,6 +8,12 @@ import useDebouncedMemo from '../../../../hooks/useDebouncedMemo';
|
||||
const DEBOUNCE_MS = 300;
|
||||
const INLINE_BOT_QUERY_REGEXP = /^@([a-z0-9_]{1,32})[\u00A0\u0020]+(.*)/i;
|
||||
const HAS_NEW_LINE = /^@([a-z0-9_]{1,32})[\u00A0\u0020]+\n{2,}/i;
|
||||
const MEMO_NO_RESULT = {
|
||||
username: '',
|
||||
query: '',
|
||||
canShowHelp: false,
|
||||
usernameLowered: '',
|
||||
};
|
||||
|
||||
const tempEl = document.createElement('div');
|
||||
|
||||
@ -81,12 +87,7 @@ function parseBotQuery(html: string) {
|
||||
const text = getPlainText(html);
|
||||
const result = text.match(INLINE_BOT_QUERY_REGEXP);
|
||||
if (!result) {
|
||||
return {
|
||||
username: '',
|
||||
query: '',
|
||||
canShowHelp: false,
|
||||
usernameLowered: '',
|
||||
};
|
||||
return MEMO_NO_RESULT;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@ -24,7 +24,7 @@ try {
|
||||
|
||||
export default function useMentionTooltip(
|
||||
canSuggestMembers: boolean | undefined,
|
||||
html: string,
|
||||
htmlRef: { current: string },
|
||||
onUpdateHtml: (html: string) => void,
|
||||
inputId: string = EDITABLE_INPUT_ID,
|
||||
groupChatMembers?: ApiChatMember[],
|
||||
@ -62,6 +62,7 @@ export default function useMentionTooltip(
|
||||
});
|
||||
}, [currentUserId, groupChatMembers, topInlineBotIds]);
|
||||
|
||||
const html = htmlRef.current;
|
||||
useEffect(() => {
|
||||
if (!canSuggestMembers || !html.length) {
|
||||
unmarkIsOpen();
|
||||
@ -76,7 +77,7 @@ export default function useMentionTooltip(
|
||||
} else {
|
||||
unmarkIsOpen();
|
||||
}
|
||||
}, [canSuggestMembers, html, updateFilteredUsers, markIsOpen, unmarkIsOpen]);
|
||||
}, [canSuggestMembers, updateFilteredUsers, markIsOpen, unmarkIsOpen, html]);
|
||||
|
||||
useEffect(() => {
|
||||
if (usersToMention?.length) {
|
||||
@ -101,9 +102,10 @@ export default function useMentionTooltip(
|
||||
dir="auto"
|
||||
>${getUserFirstOrLastName(user)}</a>`;
|
||||
|
||||
const atIndex = html.lastIndexOf('@');
|
||||
const currentHtml = htmlRef.current;
|
||||
const atIndex = currentHtml.lastIndexOf('@');
|
||||
if (atIndex !== -1) {
|
||||
onUpdateHtml(`${html.substr(0, atIndex)}${insertedHtml} `);
|
||||
onUpdateHtml(`${currentHtml.substr(0, atIndex)}${insertedHtml} `);
|
||||
const messageInput = document.getElementById(inputId)!;
|
||||
requestAnimationFrame(() => {
|
||||
focusEditableElement(messageInput, forceFocus);
|
||||
@ -111,7 +113,7 @@ export default function useMentionTooltip(
|
||||
}
|
||||
|
||||
unmarkIsOpen();
|
||||
}, [html, inputId, onUpdateHtml, unmarkIsOpen]);
|
||||
}, [htmlRef, inputId, onUpdateHtml, unmarkIsOpen]);
|
||||
|
||||
return {
|
||||
isMentionTooltipOpen: isOpen,
|
||||
|
||||
@ -45,29 +45,27 @@ interface OwnProps {
|
||||
onSecondaryIconClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
}
|
||||
|
||||
const ListItem: FC<OwnProps> = (props) => {
|
||||
const {
|
||||
ref,
|
||||
buttonRef,
|
||||
icon,
|
||||
secondaryIcon,
|
||||
className,
|
||||
style,
|
||||
children,
|
||||
disabled,
|
||||
ripple,
|
||||
narrow,
|
||||
inactive,
|
||||
focus,
|
||||
destructive,
|
||||
multiline,
|
||||
isStatic,
|
||||
contextActions,
|
||||
onMouseDown,
|
||||
onClick,
|
||||
onSecondaryIconClick,
|
||||
} = props;
|
||||
|
||||
const ListItem: FC<OwnProps> = ({
|
||||
ref,
|
||||
buttonRef,
|
||||
icon,
|
||||
secondaryIcon,
|
||||
className,
|
||||
style,
|
||||
children,
|
||||
disabled,
|
||||
ripple,
|
||||
narrow,
|
||||
inactive,
|
||||
focus,
|
||||
destructive,
|
||||
multiline,
|
||||
isStatic,
|
||||
contextActions,
|
||||
onMouseDown,
|
||||
onClick,
|
||||
onSecondaryIconClick,
|
||||
}) => {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
let containerRef = useRef<HTMLDivElement>(null);
|
||||
if (ref) {
|
||||
|
||||
@ -12,10 +12,13 @@ export default <B extends Bundles, M extends BundleModules<B>>(
|
||||
const module = getModuleFromMemory(bundleName, moduleName);
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
if (autoUpdate) {
|
||||
// Use effect and cleanup for listener removal
|
||||
addLoadListener(forceUpdate);
|
||||
}
|
||||
useEffect(() => {
|
||||
if (!autoUpdate) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return addLoadListener(forceUpdate);
|
||||
}, [autoUpdate, forceUpdate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!noLoad && !module) {
|
||||
|
||||
13
src/hooks/useStateRef.ts
Normal file
13
src/hooks/useStateRef.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { useEffect, useRef } from '../lib/teact/teact';
|
||||
|
||||
// Allows to use state value as "silent" dependency in hooks (not causing updates).
|
||||
// Useful for state values that update frequently (such as controlled input value).
|
||||
export function useStateRef<T>(value: T) {
|
||||
const ref = useRef<T>(value);
|
||||
|
||||
useEffect(() => {
|
||||
ref.current = value;
|
||||
}, [value]);
|
||||
|
||||
return ref;
|
||||
}
|
||||
@ -100,6 +100,7 @@ const Fragment = Symbol('Fragment');
|
||||
|
||||
const DEBUG_RENDER_THRESHOLD = 7;
|
||||
const DEBUG_EFFECT_THRESHOLD = 7;
|
||||
const DEBUG_SILENT_RENDERS_FOR = new Set(['TeactMemoWrapper', 'TeactNContainer', 'Button', 'ListItem', 'MenuItem']);
|
||||
|
||||
let renderingInstance: ComponentInstance;
|
||||
|
||||
@ -276,7 +277,7 @@ export function renderComponent(componentInstance: ComponentInstance) {
|
||||
}
|
||||
|
||||
if (DEBUG_MORE) {
|
||||
if (componentName !== 'TeactMemoWrapper' && componentName !== 'TeactNContainer') {
|
||||
if (!DEBUG_SILENT_RENDERS_FOR.has(componentName)) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`[Teact] Render ${componentName}`);
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import {
|
||||
} from '../../../api/types';
|
||||
|
||||
import { unique } from '../../../util/iteratees';
|
||||
import { areDeepEqual } from '../../../util/areDeepEqual';
|
||||
import {
|
||||
updateChat,
|
||||
deleteChatMessages,
|
||||
@ -468,14 +469,17 @@ addReducer('apiUpdate', (global, actions, update: ApiUpdate) => {
|
||||
}
|
||||
|
||||
case 'updateMessageReactions': {
|
||||
setGlobal(updateChatMessage(
|
||||
global,
|
||||
update.chatId,
|
||||
update.id,
|
||||
{
|
||||
reactions: update.reactions,
|
||||
},
|
||||
));
|
||||
const { chatId, id, reactions } = update;
|
||||
const message = selectChatMessage(global, chatId, id);
|
||||
const currentReactions = message?.reactions;
|
||||
|
||||
// `updateMessageReactions` happens with an interval so we try to avoid redundant global state updates
|
||||
if (currentReactions && areDeepEqual(reactions, currentReactions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setGlobal(updateChatMessage(global, chatId, id, { reactions: update.reactions }));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
35
src/util/areDeepEqual.ts
Normal file
35
src/util/areDeepEqual.ts
Normal file
@ -0,0 +1,35 @@
|
||||
export function areDeepEqual<T extends any>(value1: T, value2: T): boolean {
|
||||
const type1 = typeof value1;
|
||||
const type2 = typeof value2;
|
||||
if (type1 !== type2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type1 !== 'object') {
|
||||
return value1 === value2;
|
||||
}
|
||||
|
||||
const isArray1 = Array.isArray(value1);
|
||||
const isArray2 = Array.isArray(value2);
|
||||
|
||||
if (isArray1 !== isArray2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isArray1) {
|
||||
const array1 = value1 as any[];
|
||||
const array2 = value2 as any[];
|
||||
|
||||
if (array1.length !== array2.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return array1.every((member1, i) => areDeepEqual(member1, array2[i]));
|
||||
}
|
||||
|
||||
const object1 = value1 as AnyLiteral;
|
||||
const object2 = value1 as AnyLiteral;
|
||||
const keys1 = Object.keys(object1);
|
||||
|
||||
return keys1.every((key1) => areDeepEqual(object1[key1], object2[key1]));
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import { DEBUG } from '../config';
|
||||
import { createCallbackManager } from './callbacks';
|
||||
|
||||
export enum Bundles {
|
||||
Auth,
|
||||
@ -23,6 +24,8 @@ export type BundleModules<B extends keyof ImportedBundles> = keyof ImportedBundl
|
||||
const LOAD_PROMISES: Partial<BundlePromises> = {};
|
||||
const MEMORY_CACHE: Partial<ImportedBundles> = {};
|
||||
|
||||
const { addCallback, runCallbacks } = createCallbackManager();
|
||||
|
||||
export async function loadModule<B extends Bundles, M extends BundleModules<B>>(bundleName: B, moduleName: M) {
|
||||
if (!LOAD_PROMISES[bundleName]) {
|
||||
switch (bundleName) {
|
||||
@ -45,7 +48,7 @@ export async function loadModule<B extends Bundles, M extends BundleModules<B>>(
|
||||
break;
|
||||
}
|
||||
|
||||
(LOAD_PROMISES[bundleName] as Promise<ImportedBundles[B]>).then(handleBundleLoad);
|
||||
(LOAD_PROMISES[bundleName] as Promise<ImportedBundles[B]>).then(runCallbacks);
|
||||
}
|
||||
|
||||
const bundle = (await LOAD_PROMISES[bundleName]) as unknown as ImportedBundles[B];
|
||||
@ -67,16 +70,4 @@ export function getModuleFromMemory<B extends Bundles, M extends BundleModules<B
|
||||
return bundle[moduleName];
|
||||
}
|
||||
|
||||
const listeners: NoneToVoidFunction[] = [];
|
||||
|
||||
export function addLoadListener(listener: NoneToVoidFunction) {
|
||||
if (!listeners.includes(listener)) {
|
||||
listeners.push(listener);
|
||||
}
|
||||
}
|
||||
|
||||
function handleBundleLoad() {
|
||||
listeners.forEach((listener) => {
|
||||
listener();
|
||||
});
|
||||
}
|
||||
export const addLoadListener = addCallback;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user