[Refactoring] Simplify modal registration (#4501)

This commit is contained in:
Alexander Zinchuk 2024-05-03 14:38:17 +02:00
parent 209564ca6f
commit 19e90c8fbb
18 changed files with 158 additions and 139 deletions

View File

@ -9,10 +9,8 @@ import { addExtraClass } from '../../lib/teact/teact-dom';
import { getActions, getGlobal, withGlobal } from '../../global'; import { getActions, getGlobal, withGlobal } from '../../global';
import type { import type {
ApiAttachBot,
ApiChat, ApiChat,
ApiChatFolder, ApiChatFolder,
ApiGeoPoint,
ApiMessage, ApiMessage,
ApiUser, ApiUser,
} from '../../api/types'; } from '../../api/types';
@ -24,7 +22,6 @@ import {
BASE_EMOJI_KEYWORD_LANG, DEBUG, INACTIVE_MARKER, BASE_EMOJI_KEYWORD_LANG, DEBUG, INACTIVE_MARKER,
} from '../../config'; } from '../../config';
import { requestNextMutation } from '../../lib/fasterdom/fasterdom'; import { requestNextMutation } from '../../lib/fasterdom/fasterdom';
import { getUserFullName } from '../../global/helpers';
import { import {
selectCanAnimateInterface, selectCanAnimateInterface,
selectChatFolder, selectChatFolder,
@ -75,15 +72,7 @@ import AudioPlayer from '../middle/AudioPlayer';
import ReactionPicker from '../middle/message/reactions/ReactionPicker.async'; import ReactionPicker from '../middle/message/reactions/ReactionPicker.async';
import MessageListHistoryHandler from '../middle/MessageListHistoryHandler'; import MessageListHistoryHandler from '../middle/MessageListHistoryHandler';
import MiddleColumn from '../middle/MiddleColumn'; import MiddleColumn from '../middle/MiddleColumn';
import AttachBotInstallModal from '../modals/attachBotInstall/AttachBotInstallModal.async'; import ModalContainer from '../modals/ModalContainer';
import BoostModal from '../modals/boost/BoostModal.async';
import ChatlistModal from '../modals/chatlist/ChatlistModal.async';
import GiftCodeModal from '../modals/giftcode/GiftCodeModal.async';
import InviteViaLinkModal from '../modals/inviteViaLink/InviteViaLinkModal.async';
import MapModal from '../modals/map/MapModal.async';
import OneTimeMediaModal from '../modals/oneTimeMedia/OneTimeMediaModal.async';
import UrlAuthModal from '../modals/urlAuth/UrlAuthModal.async';
import WebAppModal from '../modals/webApp/WebAppModal.async';
import PaymentModal from '../payment/PaymentModal.async'; import PaymentModal from '../payment/PaymentModal.async';
import ReceiptModal from '../payment/ReceiptModal.async'; import ReceiptModal from '../payment/ReceiptModal.async';
import RightColumn from '../right/RightColumn'; import RightColumn from '../right/RightColumn';
@ -126,8 +115,6 @@ type StateProps = {
hasDialogs: boolean; hasDialogs: boolean;
audioMessage?: ApiMessage; audioMessage?: ApiMessage;
safeLinkModalUrl?: string; safeLinkModalUrl?: string;
mapModalGeoPoint?: ApiGeoPoint;
mapModalZoom?: number;
isHistoryCalendarOpen: boolean; isHistoryCalendarOpen: boolean;
shouldSkipHistoryAnimations?: boolean; shouldSkipHistoryAnimations?: boolean;
openedStickerSetShortName?: string; openedStickerSetShortName?: string;
@ -144,15 +131,11 @@ type StateProps = {
openedGame?: TabState['openedGame']; openedGame?: TabState['openedGame'];
gameTitle?: string; gameTitle?: string;
isRatePhoneCallModalOpen?: boolean; isRatePhoneCallModalOpen?: boolean;
webApp?: TabState['webApp'];
isPremiumModalOpen?: boolean; isPremiumModalOpen?: boolean;
botTrustRequest?: TabState['botTrustRequest']; botTrustRequest?: TabState['botTrustRequest'];
botTrustRequestBot?: ApiUser; botTrustRequestBot?: ApiUser;
attachBotToInstall?: ApiAttachBot;
requestedAttachBotInChat?: TabState['requestedAttachBotInChat']; requestedAttachBotInChat?: TabState['requestedAttachBotInChat'];
requestedDraft?: TabState['requestedDraft']; requestedDraft?: TabState['requestedDraft'];
currentUserName?: string;
urlAuth?: TabState['urlAuth'];
limitReached?: ApiLimitTypeWithModal; limitReached?: ApiLimitTypeWithModal;
deleteFolderDialog?: ApiChatFolder; deleteFolderDialog?: ApiChatFolder;
isPaymentModalOpen?: boolean; isPaymentModalOpen?: boolean;
@ -162,14 +145,9 @@ type StateProps = {
isGiveawayModalOpen?: boolean; isGiveawayModalOpen?: boolean;
isPremiumGiftingModalOpen?: boolean; isPremiumGiftingModalOpen?: boolean;
isCurrentUserPremium?: boolean; isCurrentUserPremium?: boolean;
chatlistModal?: TabState['chatlistModal'];
boostModal?: TabState['boostModal'];
giftCodeModal?: TabState['giftCodeModal'];
noRightColumnAnimation?: boolean; noRightColumnAnimation?: boolean;
withInterfaceAnimations?: boolean; withInterfaceAnimations?: boolean;
isSynced?: boolean; isSynced?: boolean;
inviteViaLinkModal?: TabState['inviteViaLinkModal'];
oneTimeMediaModal?: TabState['oneTimeMediaModal'];
}; };
const APP_OUTDATED_TIMEOUT_MS = 5 * 60 * 1000; // 5 min const APP_OUTDATED_TIMEOUT_MS = 5 * 60 * 1000; // 5 min
@ -191,8 +169,6 @@ const Main: FC<OwnProps & StateProps> = ({
audioMessage, audioMessage,
activeGroupCallId, activeGroupCallId,
safeLinkModalUrl, safeLinkModalUrl,
mapModalGeoPoint,
mapModalZoom,
isHistoryCalendarOpen, isHistoryCalendarOpen,
shouldSkipHistoryAnimations, shouldSkipHistoryAnimations,
limitReached, limitReached,
@ -212,12 +188,8 @@ const Main: FC<OwnProps & StateProps> = ({
isRatePhoneCallModalOpen, isRatePhoneCallModalOpen,
botTrustRequest, botTrustRequest,
botTrustRequestBot, botTrustRequestBot,
attachBotToInstall,
requestedAttachBotInChat, requestedAttachBotInChat,
requestedDraft, requestedDraft,
webApp,
currentUserName,
urlAuth,
isPremiumModalOpen, isPremiumModalOpen,
isGiveawayModalOpen, isGiveawayModalOpen,
isPremiumGiftingModalOpen, isPremiumGiftingModalOpen,
@ -227,13 +199,8 @@ const Main: FC<OwnProps & StateProps> = ({
isCurrentUserPremium, isCurrentUserPremium,
deleteFolderDialog, deleteFolderDialog,
isMasterTab, isMasterTab,
chatlistModal,
giftCodeModal,
boostModal,
noRightColumnAnimation, noRightColumnAnimation,
isSynced, isSynced,
inviteViaLinkModal,
oneTimeMediaModal,
currentUserId, currentUserId,
}) => { }) => {
const { const {
@ -565,9 +532,8 @@ const Main: FC<OwnProps & StateProps> = ({
<Notifications isOpen={hasNotifications} /> <Notifications isOpen={hasNotifications} />
<Dialogs isOpen={hasDialogs} /> <Dialogs isOpen={hasDialogs} />
{audioMessage && <AudioPlayer key={audioMessage.id} message={audioMessage} noUi />} {audioMessage && <AudioPlayer key={audioMessage.id} message={audioMessage} noUi />}
<ModalContainer />
<SafeLinkModal url={safeLinkModalUrl} /> <SafeLinkModal url={safeLinkModalUrl} />
<MapModal geoPoint={mapModalGeoPoint} zoom={mapModalZoom} />
<UrlAuthModal urlAuth={urlAuth} currentUserName={currentUserName} />
<HistoryCalendar isOpen={isHistoryCalendarOpen} /> <HistoryCalendar isOpen={isHistoryCalendarOpen} />
<StickerSetModal <StickerSetModal
isOpen={Boolean(openedStickerSetShortName)} isOpen={Boolean(openedStickerSetShortName)}
@ -585,12 +551,7 @@ const Main: FC<OwnProps & StateProps> = ({
userId={newContactUserId} userId={newContactUserId}
isByPhoneNumber={newContactByPhoneNumber} isByPhoneNumber={newContactByPhoneNumber}
/> />
<BoostModal info={boostModal} />
<GiftCodeModal modal={giftCodeModal} />
<OneTimeMediaModal info={oneTimeMediaModal} />
<ChatlistModal info={chatlistModal} />
<GameModal openedGame={openedGame} gameTitle={gameTitle} /> <GameModal openedGame={openedGame} gameTitle={gameTitle} />
<WebAppModal webApp={webApp} />
<DownloadManager /> <DownloadManager />
<ConfettiContainer /> <ConfettiContainer />
<PhoneCall isActive={isPhoneCallActive} /> <PhoneCall isActive={isPhoneCallActive} />
@ -601,7 +562,6 @@ const Main: FC<OwnProps & StateProps> = ({
type={botTrustRequest?.type} type={botTrustRequest?.type}
shouldRequestWriteAccess={botTrustRequest?.shouldRequestWriteAccess} shouldRequestWriteAccess={botTrustRequest?.shouldRequestWriteAccess}
/> />
<AttachBotInstallModal bot={attachBotToInstall} />
<AttachBotRecipientPicker requestedAttachBotInChat={requestedAttachBotInChat} /> <AttachBotRecipientPicker requestedAttachBotInChat={requestedAttachBotInChat} />
<MessageListHistoryHandler /> <MessageListHistoryHandler />
{isPremiumModalOpen && <PremiumMainModal isOpen={isPremiumModalOpen} />} {isPremiumModalOpen && <PremiumMainModal isOpen={isPremiumModalOpen} />}
@ -612,7 +572,6 @@ const Main: FC<OwnProps & StateProps> = ({
<ReceiptModal isOpen={isReceiptModalOpen} onClose={clearReceipt} /> <ReceiptModal isOpen={isReceiptModalOpen} onClose={clearReceipt} />
<DeleteFolderDialog folder={deleteFolderDialog} /> <DeleteFolderDialog folder={deleteFolderDialog} />
<ReactionPicker isOpen={isReactionPickerOpen} /> <ReactionPicker isOpen={isReactionPickerOpen} />
<InviteViaLinkModal missingUsers={inviteViaLinkModal?.missingUsers} chatId={inviteViaLinkModal?.chatId} />
</div> </div>
); );
}; };
@ -630,13 +589,9 @@ export default memo(withGlobal<OwnProps>(
const { const {
botTrustRequest, botTrustRequest,
requestedAttachBotInstall,
requestedAttachBotInChat, requestedAttachBotInChat,
requestedDraft, requestedDraft,
urlAuth,
webApp,
safeLinkModalUrl, safeLinkModalUrl,
mapModal,
openedStickerSetShortName, openedStickerSetShortName,
openedCustomEmojiSetIds, openedCustomEmojiSetIds,
shouldSkipHistoryAnimations, shouldSkipHistoryAnimations,
@ -655,11 +610,6 @@ export default memo(withGlobal<OwnProps>(
payment, payment,
limitReachedModal, limitReachedModal,
deleteFolderDialogModal, deleteFolderDialogModal,
chatlistModal,
boostModal,
giftCodeModal,
inviteViaLinkModal,
oneTimeMediaModal,
} = selectTabState(global); } = selectTabState(global);
const { chatId: audioChatId, messageId: audioMessageId } = audioPlayer; const { chatId: audioChatId, messageId: audioMessageId } = audioPlayer;
@ -668,7 +618,6 @@ export default memo(withGlobal<OwnProps>(
: undefined; : undefined;
const gameMessage = openedGame && selectChatMessage(global, openedGame.chatId, openedGame.messageId); const gameMessage = openedGame && selectChatMessage(global, openedGame.chatId, openedGame.messageId);
const gameTitle = gameMessage?.content.game?.title; const gameTitle = gameMessage?.content.game?.title;
const currentUser = global.currentUserId ? selectUser(global, global.currentUserId) : undefined;
const { chatId } = selectCurrentMessageList(global) || {}; const { chatId } = selectCurrentMessageList(global) || {};
const noRightColumnAnimation = !selectPerformanceSettingsValue(global, 'rightColumnAnimations') const noRightColumnAnimation = !selectPerformanceSettingsValue(global, 'rightColumnAnimations')
|| !selectCanAnimateInterface(global); || !selectCanAnimateInterface(global);
@ -688,8 +637,6 @@ export default memo(withGlobal<OwnProps>(
hasDialogs: Boolean(dialogs.length), hasDialogs: Boolean(dialogs.length),
audioMessage, audioMessage,
safeLinkModalUrl, safeLinkModalUrl,
mapModalGeoPoint: mapModal?.point,
mapModalZoom: mapModal?.zoom,
isHistoryCalendarOpen: Boolean(historyCalendarSelectedAt), isHistoryCalendarOpen: Boolean(historyCalendarSelectedAt),
shouldSkipHistoryAnimations, shouldSkipHistoryAnimations,
openedStickerSetShortName, openedStickerSetShortName,
@ -709,11 +656,7 @@ export default memo(withGlobal<OwnProps>(
isRatePhoneCallModalOpen: Boolean(ratingPhoneCall), isRatePhoneCallModalOpen: Boolean(ratingPhoneCall),
botTrustRequest, botTrustRequest,
botTrustRequestBot: botTrustRequest && selectUser(global, botTrustRequest.botId), botTrustRequestBot: botTrustRequest && selectUser(global, botTrustRequest.botId),
attachBotToInstall: requestedAttachBotInstall?.bot,
requestedAttachBotInChat, requestedAttachBotInChat,
webApp,
currentUserName: getUserFullName(currentUser),
urlAuth,
isCurrentUserPremium: selectIsCurrentUserPremium(global), isCurrentUserPremium: selectIsCurrentUserPremium(global),
isPremiumModalOpen: premiumModal?.isOpen, isPremiumModalOpen: premiumModal?.isOpen,
isGiveawayModalOpen: giveawayModal?.isOpen, isGiveawayModalOpen: giveawayModal?.isOpen,
@ -724,13 +667,8 @@ export default memo(withGlobal<OwnProps>(
deleteFolderDialog, deleteFolderDialog,
isMasterTab, isMasterTab,
requestedDraft, requestedDraft,
chatlistModal,
boostModal,
giftCodeModal,
noRightColumnAnimation, noRightColumnAnimation,
isSynced: global.isSynced, isSynced: global.isSynced,
inviteViaLinkModal,
oneTimeMediaModal,
}; };
}, },
)(Main)); )(Main));

View File

@ -0,0 +1,66 @@
import React, { memo } from '../../lib/teact/teact';
import { withGlobal } from '../../global';
import type { TabState } from '../../global/types';
import { selectTabState } from '../../global/selectors';
import { pick } from '../../util/iteratees';
import AttachBotInstallModal from './attachBotInstall/AttachBotInstallModal.async';
import BoostModal from './boost/BoostModal.async';
import ChatlistModal from './chatlist/ChatlistModal.async';
import GiftCodeModal from './giftcode/GiftCodeModal.async';
import InviteViaLinkModal from './inviteViaLink/InviteViaLinkModal.async';
import OneTimeMediaModal from './oneTimeMedia/OneTimeMediaModal.async';
import UrlAuthModal from './urlAuth/UrlAuthModal.async';
import WebAppModal from './webApp/WebAppModal.async';
// `Pick` used only to provide tab completion
type ModalKey = keyof Pick<TabState,
'giftCodeModal' |
'boostModal' |
'chatlistModal' |
'urlAuth' |
'oneTimeMediaModal' |
'inviteViaLinkModal' |
'requestedAttachBotInstall' |
'webApp'
>;
type StateProps = {
[K in ModalKey]?: TabState[K];
};
type ModalRegistry = {
[K in ModalKey]: React.FC<{
modal: TabState[K];
}>;
};
type Entries<T> = {
[K in keyof T]: [K, T[K]];
}[keyof T][];
const MODALS: ModalRegistry = {
giftCodeModal: GiftCodeModal,
boostModal: BoostModal,
chatlistModal: ChatlistModal,
urlAuth: UrlAuthModal,
oneTimeMediaModal: OneTimeMediaModal,
inviteViaLinkModal: InviteViaLinkModal,
requestedAttachBotInstall: AttachBotInstallModal,
webApp: WebAppModal,
};
const MODAL_KEYS = Object.keys(MODALS) as ModalKey[];
const MODAL_ENTRIES = Object.entries(MODALS) as Entries<ModalRegistry>;
const ModalContainer = (modalProps: StateProps) => {
return MODAL_ENTRIES.map(([key, ModalComponent]) => (
// @ts-ignore -- TS does not preserve tuple types in `map` callbacks
<ModalComponent key={key} modal={modalProps[key]} />
));
};
export default memo(withGlobal(
(global): StateProps => (
pick(selectTabState(global), MODAL_KEYS)
),
)(ModalContainer));

View File

@ -8,8 +8,8 @@ import { Bundles } from '../../../util/moduleLoader';
import useModuleLoader from '../../../hooks/useModuleLoader'; import useModuleLoader from '../../../hooks/useModuleLoader';
const AttachBotInstallModalAsync: FC<OwnProps> = (props) => { const AttachBotInstallModalAsync: FC<OwnProps> = (props) => {
const { bot } = props; const { modal } = props;
const AttachBotInstallModal = useModuleLoader(Bundles.Extra, 'AttachBotInstallModal', !bot); const AttachBotInstallModal = useModuleLoader(Bundles.Extra, 'AttachBotInstallModal', !modal);
// eslint-disable-next-line react/jsx-props-no-spreading // eslint-disable-next-line react/jsx-props-no-spreading
return AttachBotInstallModal ? <AttachBotInstallModal {...props} /> : undefined; return AttachBotInstallModal ? <AttachBotInstallModal {...props} /> : undefined;

View File

@ -4,7 +4,7 @@ import React, {
} from '../../../lib/teact/teact'; } from '../../../lib/teact/teact';
import { getActions } from '../../../global'; import { getActions } from '../../../global';
import type { ApiAttachBot } from '../../../api/types'; import type { TabState } from '../../../global/types';
import { MINI_APP_TOS_URL } from '../../../config'; import { MINI_APP_TOS_URL } from '../../../config';
import renderText from '../../common/helpers/renderText'; import renderText from '../../common/helpers/renderText';
@ -15,13 +15,15 @@ import Checkbox from '../../ui/Checkbox';
import ConfirmDialog from '../../ui/ConfirmDialog'; import ConfirmDialog from '../../ui/ConfirmDialog';
export type OwnProps = { export type OwnProps = {
bot?: ApiAttachBot; modal: TabState['requestedAttachBotInstall'];
}; };
const AttachBotInstallModal: FC<OwnProps> = ({ const AttachBotInstallModal: FC<OwnProps> = ({
bot, modal,
}) => { }) => {
const { confirmAttachBotInstall, cancelAttachBotInstall } = getActions(); const { confirmAttachBotInstall, cancelAttachBotInstall } = getActions();
const bot = modal?.bot;
const [isTosAccepted, setIsTosAccepted] = useState(false); const [isTosAccepted, setIsTosAccepted] = useState(false);
const lang = useLang(); const lang = useLang();

View File

@ -8,8 +8,8 @@ import { Bundles } from '../../../util/moduleLoader';
import useModuleLoader from '../../../hooks/useModuleLoader'; import useModuleLoader from '../../../hooks/useModuleLoader';
const BoostModalAsync: FC<OwnProps> = (props) => { const BoostModalAsync: FC<OwnProps> = (props) => {
const { info } = props; const { modal } = props;
const BoostModal = useModuleLoader(Bundles.Extra, 'BoostModal', !info); const BoostModal = useModuleLoader(Bundles.Extra, 'BoostModal', !modal);
// eslint-disable-next-line react/jsx-props-no-spreading // eslint-disable-next-line react/jsx-props-no-spreading
return BoostModal ? <BoostModal {...props} /> : undefined; return BoostModal ? <BoostModal {...props} /> : undefined;

View File

@ -46,7 +46,7 @@ type BoostInfo = ({
} & LoadedParams); } & LoadedParams);
export type OwnProps = { export type OwnProps = {
info: TabState['boostModal']; modal: TabState['boostModal'];
}; };
type StateProps = { type StateProps = {
@ -57,7 +57,7 @@ type StateProps = {
}; };
const BoostModal = ({ const BoostModal = ({
info, modal,
chat, chat,
chatFullInfo, chatFullInfo,
prevBoostedChat, prevBoostedChat,
@ -77,7 +77,7 @@ const BoostModal = ({
const isChannel = chat && isChatChannel(chat); const isChannel = chat && isChatChannel(chat);
const isOpen = Boolean(info); const isOpen = Boolean(modal);
const lang = useLang(); const lang = useLang();
@ -115,7 +115,7 @@ const BoostModal = ({
descriptionText, descriptionText,
canBoostMore, canBoostMore,
}: BoostInfo = useMemo(() => { }: BoostInfo = useMemo(() => {
if (!info?.boostStatus || !chat) { if (!modal?.boostStatus || !chat) {
return { return {
isStatusLoaded: false, isStatusLoaded: false,
title: lang('Loading'), title: lang('Loading'),
@ -124,10 +124,10 @@ const BoostModal = ({
const { const {
hasMyBoost, hasMyBoost,
} = info.boostStatus; } = modal.boostStatus;
const firstBoost = info?.myBoosts && getFirstAvailableBoost(info.myBoosts, chat.id); const firstBoost = modal?.myBoosts && getFirstAvailableBoost(modal.myBoosts, chat.id);
const areBoostsInDifferentChannels = info?.myBoosts && !areAllBoostsInChannel(info.myBoosts, chat.id); const areBoostsInDifferentChannels = modal?.myBoosts && !areAllBoostsInChannel(modal.myBoosts, chat.id);
const { const {
boosts, boosts,
@ -136,7 +136,7 @@ const BoostModal = ({
levelProgress, levelProgress,
remainingBoosts, remainingBoosts,
isMaxLevel, isMaxLevel,
} = getBoostProgressInfo(info.boostStatus, true); } = getBoostProgressInfo(modal.boostStatus, true);
const hasBoost = hasMyBoost; const hasBoost = hasMyBoost;
@ -172,10 +172,10 @@ const BoostModal = ({
isBoosted: hasBoost, isBoosted: hasBoost,
canBoostMore: areBoostsInDifferentChannels && !isMaxLevel, canBoostMore: areBoostsInDifferentChannels && !isMaxLevel,
}; };
}, [chat, chatTitle, info, lang, chatFullInfo, isChannel]); }, [chat, chatTitle, modal, lang, chatFullInfo, isChannel]);
const isBoostDisabled = !info?.myBoosts?.length && isCurrentUserPremium; const isBoostDisabled = !modal?.myBoosts?.length && isCurrentUserPremium;
const isReplacingBoost = boost?.chatId && boost.chatId !== info?.chatId; const isReplacingBoost = boost?.chatId && boost.chatId !== modal?.chatId;
const handleApplyBoost = useLastCallback(() => { const handleApplyBoost = useLastCallback(() => {
closeReplaceModal(); closeReplaceModal();
@ -342,10 +342,10 @@ function areAllBoostsInChannel(myBoosts: ApiMyBoost[], chatId: string) {
} }
export default memo(withGlobal<OwnProps>( export default memo(withGlobal<OwnProps>(
(global, { info }): StateProps => { (global, { modal }): StateProps => {
const chat = info && selectChat(global, info?.chatId); const chat = modal && selectChat(global, modal?.chatId);
const chatFullInfo = chat && selectChatFullInfo(global, chat.id); const chatFullInfo = chat && selectChatFullInfo(global, chat.id);
const firstBoost = info?.myBoosts && getFirstAvailableBoost(info.myBoosts, info.chatId); const firstBoost = modal?.myBoosts && getFirstAvailableBoost(modal.myBoosts, modal.chatId);
const boostedChat = firstBoost?.chatId ? selectChat(global, firstBoost?.chatId) : undefined; const boostedChat = firstBoost?.chatId ? selectChat(global, firstBoost?.chatId) : undefined;
return { return {

View File

@ -8,8 +8,8 @@ import { Bundles } from '../../../util/moduleLoader';
import useModuleLoader from '../../../hooks/useModuleLoader'; import useModuleLoader from '../../../hooks/useModuleLoader';
const ChatlistModalAsync: FC<OwnProps> = (props) => { const ChatlistModalAsync: FC<OwnProps> = (props) => {
const { info } = props; const { modal } = props;
const ChatlistModal = useModuleLoader(Bundles.Extra, 'ChatlistModal', !info); const ChatlistModal = useModuleLoader(Bundles.Extra, 'ChatlistModal', !modal);
// eslint-disable-next-line react/jsx-props-no-spreading // eslint-disable-next-line react/jsx-props-no-spreading
return ChatlistModal ? <ChatlistModal {...props} /> : undefined; return ChatlistModal ? <ChatlistModal {...props} /> : undefined;

View File

@ -19,7 +19,7 @@ import ChatlistNew from './ChatlistNew';
import styles from './ChatlistModal.module.scss'; import styles from './ChatlistModal.module.scss';
export type OwnProps = { export type OwnProps = {
info?: TabState['chatlistModal']; modal?: TabState['chatlistModal'];
}; };
type StateProps = { type StateProps = {
@ -27,15 +27,15 @@ type StateProps = {
}; };
const ChatlistInviteModal: FC<OwnProps & StateProps> = ({ const ChatlistInviteModal: FC<OwnProps & StateProps> = ({
info, modal,
folder, folder,
}) => { }) => {
const { closeChatlistModal } = getActions(); const { closeChatlistModal } = getActions();
const lang = useLang(); const lang = useLang();
const isOpen = Boolean(info); const isOpen = Boolean(modal);
const renderingInfo = usePrevious(info) || info; const renderingInfo = usePrevious(modal) || modal;
const renderingFolder = usePrevious(folder) || folder; const renderingFolder = usePrevious(folder) || folder;
const title = useMemo(() => { const title = useMemo(() => {
@ -111,8 +111,8 @@ const ChatlistInviteModal: FC<OwnProps & StateProps> = ({
}; };
export default memo(withGlobal<OwnProps>( export default memo(withGlobal<OwnProps>(
(global, { info }): StateProps => { (global, { modal }): StateProps => {
const { invite, removal } = info || {}; const { invite, removal } = modal || {};
const folderId = removal?.folderId || (invite && 'folderId' in invite ? invite.folderId : undefined); const folderId = removal?.folderId || (invite && 'folderId' in invite ? invite.folderId : undefined);
const folder = folderId ? selectChatFolder(global, folderId) : undefined; const folder = folderId ? selectChatFolder(global, folderId) : undefined;

View File

@ -8,8 +8,8 @@ import { Bundles } from '../../../util/moduleLoader';
import useModuleLoader from '../../../hooks/useModuleLoader'; import useModuleLoader from '../../../hooks/useModuleLoader';
const InviteViaLinkModalAsync: FC<OwnProps> = (props) => { const InviteViaLinkModalAsync: FC<OwnProps> = (props) => {
const { chatId } = props; const { modal } = props;
const InviteViaLinkModal = useModuleLoader(Bundles.Extra, 'InviteViaLinkModal', !chatId); const InviteViaLinkModal = useModuleLoader(Bundles.Extra, 'InviteViaLinkModal', !modal);
// eslint-disable-next-line react/jsx-props-no-spreading // eslint-disable-next-line react/jsx-props-no-spreading
return InviteViaLinkModal ? <InviteViaLinkModal {...props} /> : undefined; return InviteViaLinkModal ? <InviteViaLinkModal {...props} /> : undefined;

View File

@ -6,7 +6,8 @@ import React, {
} from '../../../lib/teact/teact'; } from '../../../lib/teact/teact';
import { getActions, getGlobal, withGlobal } from '../../../global'; import { getActions, getGlobal, withGlobal } from '../../../global';
import type { ApiChat, ApiMissingInvitedUser } from '../../../api/types'; import type { ApiChat } from '../../../api/types';
import type { TabState } from '../../../global/types';
import { getUserFullName } from '../../../global/helpers'; import { getUserFullName } from '../../../global/helpers';
import { selectChat } from '../../../global/selectors'; import { selectChat } from '../../../global/selectors';
@ -25,8 +26,7 @@ import Separator from '../../ui/Separator';
import styles from './InviteViaLinkModal.module.scss'; import styles from './InviteViaLinkModal.module.scss';
export type OwnProps = { export type OwnProps = {
chatId?: string; modal: TabState['inviteViaLinkModal'];
missingUsers?: ApiMissingInvitedUser[];
}; };
type StateProps = { type StateProps = {
@ -34,10 +34,11 @@ type StateProps = {
}; };
const InviteViaLinkModal: FC<OwnProps & StateProps> = ({ const InviteViaLinkModal: FC<OwnProps & StateProps> = ({
missingUsers, modal,
chat, chat,
}) => { }) => {
const { sendInviteMessages, closeInviteViaLinkModal, openPremiumModal } = getActions(); const { sendInviteMessages, closeInviteViaLinkModal, openPremiumModal } = getActions();
const { missingUsers } = modal || {};
const lang = useLang(); const lang = useLang();
const [selectedMemberIds, setSelectedMemberIds] = useState<string[]>([]); const [selectedMemberIds, setSelectedMemberIds] = useState<string[]>([]);
@ -196,8 +197,8 @@ const InviteViaLinkModal: FC<OwnProps & StateProps> = ({
}; };
export default memo(withGlobal<OwnProps>( export default memo(withGlobal<OwnProps>(
(global, { chatId }): StateProps => { (global, { modal }): StateProps => {
const chat = chatId ? selectChat(global, chatId) : undefined; const chat = modal?.chatId ? selectChat(global, modal.chatId) : undefined;
return { return {
chat, chat,

View File

@ -8,8 +8,8 @@ import { Bundles } from '../../../util/moduleLoader';
import useModuleLoader from '../../../hooks/useModuleLoader'; import useModuleLoader from '../../../hooks/useModuleLoader';
const MapModalAsync: FC<OwnProps> = (props) => { const MapModalAsync: FC<OwnProps> = (props) => {
const { geoPoint } = props; const { modal } = props;
const MapModal = useModuleLoader(Bundles.Extra, 'MapModal', !geoPoint); const MapModal = useModuleLoader(Bundles.Extra, 'MapModal', !modal);
// eslint-disable-next-line react/jsx-props-no-spreading // eslint-disable-next-line react/jsx-props-no-spreading
return MapModal ? <MapModal {...props} /> : undefined; return MapModal ? <MapModal {...props} /> : undefined;

View File

@ -1,7 +1,7 @@
import React, { memo, useMemo } from '../../../lib/teact/teact'; import React, { memo, useMemo } from '../../../lib/teact/teact';
import { getActions } from '../../../global'; import { getActions } from '../../../global';
import type { ApiGeoPoint } from '../../../api/types'; import type { TabState } from '../../../global/types';
import { prepareMapUrl } from '../../../util/map'; import { prepareMapUrl } from '../../../util/map';
import { IS_IOS, IS_MAC_OS } from '../../../util/windowEnvironment'; import { IS_IOS, IS_MAC_OS } from '../../../util/windowEnvironment';
@ -15,13 +15,14 @@ import Modal from '../../ui/Modal';
import styles from './MapModal.module.scss'; import styles from './MapModal.module.scss';
export type OwnProps = { export type OwnProps = {
geoPoint?: ApiGeoPoint; modal: TabState['mapModal'];
zoom?: number;
}; };
const OpenMapModal = ({ geoPoint, zoom }: OwnProps) => { const OpenMapModal = ({ modal }: OwnProps) => {
const { closeMapModal } = getActions(); const { closeMapModal } = getActions();
const { point: geoPoint, zoom } = modal || {};
const lang = useLang(); const lang = useLang();
const isOpen = Boolean(geoPoint); const isOpen = Boolean(geoPoint);

View File

@ -8,8 +8,8 @@ import { Bundles } from '../../../util/moduleLoader';
import useModuleLoader from '../../../hooks/useModuleLoader'; import useModuleLoader from '../../../hooks/useModuleLoader';
const OneTimeMediaModalAsync: FC<OwnProps> = (props) => { const OneTimeMediaModalAsync: FC<OwnProps> = (props) => {
const { info } = props; const { modal } = props;
const OneTimeMediaModal = useModuleLoader(Bundles.Extra, 'OneTimeMediaModal', !info); const OneTimeMediaModal = useModuleLoader(Bundles.Extra, 'OneTimeMediaModal', !modal);
// eslint-disable-next-line react/jsx-props-no-spreading // eslint-disable-next-line react/jsx-props-no-spreading
return OneTimeMediaModal ? <OneTimeMediaModal {...props} /> : undefined; return OneTimeMediaModal ? <OneTimeMediaModal {...props} /> : undefined;

View File

@ -20,23 +20,23 @@ import Button from '../../ui/Button';
import styles from './OneTimeMediaModal.module.scss'; import styles from './OneTimeMediaModal.module.scss';
export type OwnProps = { export type OwnProps = {
info: TabState['oneTimeMediaModal']; modal: TabState['oneTimeMediaModal'];
}; };
const OneTimeMediaModal = ({ const OneTimeMediaModal = ({
info, modal,
}: OwnProps) => { }: OwnProps) => {
const { const {
closeOneTimeMediaModal, closeOneTimeMediaModal,
} = getActions(); } = getActions();
const lang = useLang(); const lang = useLang();
const message = useCurrentOrPrev(info?.message, true); const message = useCurrentOrPrev(modal?.message, true);
const { const {
shouldRender, shouldRender,
transitionClassNames, transitionClassNames,
} = useShowTransition(Boolean(info)); } = useShowTransition(Boolean(modal));
const handlePlayVoice = useLastCallback(() => { const handlePlayVoice = useLastCallback(() => {
return undefined; return undefined;

View File

@ -8,8 +8,8 @@ import { Bundles } from '../../../util/moduleLoader';
import useModuleLoader from '../../../hooks/useModuleLoader'; import useModuleLoader from '../../../hooks/useModuleLoader';
const UrlAuthModalAsync: FC<OwnProps> = (props) => { const UrlAuthModalAsync: FC<OwnProps> = (props) => {
const { urlAuth } = props; const { modal } = props;
const UrlAuthModal = useModuleLoader(Bundles.Extra, 'UrlAuthModal', !urlAuth); const UrlAuthModal = useModuleLoader(Bundles.Extra, 'UrlAuthModal', !modal);
// eslint-disable-next-line react/jsx-props-no-spreading // eslint-disable-next-line react/jsx-props-no-spreading
return UrlAuthModal ? <UrlAuthModal {...props} /> : undefined; return UrlAuthModal ? <UrlAuthModal {...props} /> : undefined;

View File

@ -2,11 +2,13 @@ import type { FC } from '../../../lib/teact/teact';
import React, { import React, {
memo, useCallback, useEffect, useState, memo, useCallback, useEffect, useState,
} from '../../../lib/teact/teact'; } from '../../../lib/teact/teact';
import { getActions, getGlobal } from '../../../global'; import { getActions, getGlobal, withGlobal } from '../../../global';
import type { ApiUser } from '../../../api/types';
import type { TabState } from '../../../global/types'; import type { TabState } from '../../../global/types';
import { getUserFullName } from '../../../global/helpers'; import { getUserFullName } from '../../../global/helpers';
import { selectUser } from '../../../global/selectors';
import { ensureProtocol } from '../../../util/ensureProtocol'; import { ensureProtocol } from '../../../util/ensureProtocol';
import renderText from '../../common/helpers/renderText'; import renderText from '../../common/helpers/renderText';
@ -19,25 +21,28 @@ import ConfirmDialog from '../../ui/ConfirmDialog';
import styles from './UrlAuthModal.module.scss'; import styles from './UrlAuthModal.module.scss';
export type OwnProps = { export type OwnProps = {
urlAuth?: TabState['urlAuth']; modal?: TabState['urlAuth'];
currentUserName?: string;
}; };
const UrlAuthModal: FC<OwnProps> = ({ type StateProps = {
urlAuth, currentUserName, currentUser?: ApiUser;
};
const UrlAuthModal: FC<OwnProps & StateProps> = ({
modal, currentUser,
}) => { }) => {
const { closeUrlAuthModal, acceptBotUrlAuth, acceptLinkUrlAuth } = getActions(); const { closeUrlAuthModal, acceptBotUrlAuth, acceptLinkUrlAuth } = getActions();
const [isLoginChecked, setLoginChecked] = useState(true); const [isLoginChecked, setLoginChecked] = useState(true);
const [isWriteAccessChecked, setWriteAccessChecked] = useState(true); const [isWriteAccessChecked, setWriteAccessChecked] = useState(true);
const currentAuth = useCurrentOrPrev(urlAuth, false); const currentAuth = useCurrentOrPrev(modal, false);
const { domain, botId, shouldRequestWriteAccess } = currentAuth?.request || {}; const { domain, botId, shouldRequestWriteAccess } = currentAuth?.request || {};
const bot = botId ? getGlobal().users.byId[botId] : undefined; const bot = botId ? getGlobal().users.byId[botId] : undefined;
const lang = useLang(); const lang = useLang();
const handleOpen = useCallback(() => { const handleOpen = useCallback(() => {
if (urlAuth?.url && isLoginChecked) { if (modal?.url && isLoginChecked) {
const acceptAction = urlAuth.button ? acceptBotUrlAuth : acceptLinkUrlAuth; const acceptAction = modal.button ? acceptBotUrlAuth : acceptLinkUrlAuth;
acceptAction({ acceptAction({
isWriteAllowed: isWriteAccessChecked, isWriteAllowed: isWriteAccessChecked,
}); });
@ -46,7 +51,7 @@ const UrlAuthModal: FC<OwnProps> = ({
} }
closeUrlAuthModal(); closeUrlAuthModal();
}, [ }, [
urlAuth, isLoginChecked, closeUrlAuthModal, acceptBotUrlAuth, acceptLinkUrlAuth, isWriteAccessChecked, currentAuth, modal, isLoginChecked, closeUrlAuthModal, acceptBotUrlAuth, acceptLinkUrlAuth, isWriteAccessChecked, currentAuth,
]); ]);
const handleDismiss = useCallback(() => { const handleDismiss = useCallback(() => {
@ -68,7 +73,7 @@ const UrlAuthModal: FC<OwnProps> = ({
return ( return (
<ConfirmDialog <ConfirmDialog
isOpen={Boolean(urlAuth?.url)} isOpen={Boolean(modal?.url)}
onClose={handleDismiss} onClose={handleDismiss}
title={lang('OpenUrlTitle')} title={lang('OpenUrlTitle')}
confirmLabel={lang('OpenUrlTitle')} confirmLabel={lang('OpenUrlTitle')}
@ -81,7 +86,7 @@ const UrlAuthModal: FC<OwnProps> = ({
label={( label={(
<> <>
{renderText( {renderText(
lang('Conversation.OpenBotLinkLogin', [domain, currentUserName]), lang('Conversation.OpenBotLinkLogin', [domain, getUserFullName(currentUser)]),
['simple_markdown'], ['simple_markdown'],
)} )}
</> </>
@ -109,5 +114,11 @@ const UrlAuthModal: FC<OwnProps> = ({
</ConfirmDialog> </ConfirmDialog>
); );
}; };
export default memo(withGlobal<OwnProps>(
export default memo(UrlAuthModal); (global): StateProps => {
const currentUser = selectUser(global, global.currentUserId!);
return {
currentUser,
};
},
)(UrlAuthModal));

View File

@ -8,8 +8,8 @@ import { Bundles } from '../../../util/moduleLoader';
import useModuleLoader from '../../../hooks/useModuleLoader'; import useModuleLoader from '../../../hooks/useModuleLoader';
const WebAppModalAsync: FC<OwnProps> = (props) => { const WebAppModalAsync: FC<OwnProps> = (props) => {
const { webApp } = props; const { modal } = props;
const WebAppModal = useModuleLoader(Bundles.Extra, 'WebAppModal', !webApp); const WebAppModal = useModuleLoader(Bundles.Extra, 'WebAppModal', !modal);
// eslint-disable-next-line react/jsx-props-no-spreading // eslint-disable-next-line react/jsx-props-no-spreading
return WebAppModal ? <WebAppModal {...props} /> : undefined; return WebAppModal ? <WebAppModal {...props} /> : undefined;

View File

@ -52,7 +52,7 @@ type WebAppButton = {
}; };
export type OwnProps = { export type OwnProps = {
webApp?: TabState['webApp']; modal?: TabState['webApp'];
}; };
type StateProps = { type StateProps = {
@ -88,7 +88,7 @@ const DEFAULT_BUTTON_TEXT: Record<string, string> = {
}; };
const WebAppModal: FC<OwnProps & StateProps> = ({ const WebAppModal: FC<OwnProps & StateProps> = ({
webApp, modal,
chat, chat,
bot, bot,
attachBot, attachBot,
@ -141,7 +141,7 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
const lang = useLang(); const lang = useLang();
const { const {
url, buttonText, queryId, replyInfo, url, buttonText, queryId, replyInfo,
} = webApp || {}; } = modal || {};
const isOpen = Boolean(url); const isOpen = Boolean(url);
const isSimple = Boolean(buttonText); const isSimple = Boolean(buttonText);
@ -173,7 +173,7 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
}); });
const handleRefreshClick = useLastCallback(() => { const handleRefreshClick = useLastCallback(() => {
reloadFrame(webApp!.url); reloadFrame(modal!.url);
}); });
const handleClose = useLastCallback(() => { const handleClose = useLastCallback(() => {
@ -215,11 +215,11 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
useSyncEffect(([prevIsPaymentModalOpen]) => { useSyncEffect(([prevIsPaymentModalOpen]) => {
if (isPaymentModalOpen === prevIsPaymentModalOpen) return; if (isPaymentModalOpen === prevIsPaymentModalOpen) return;
if (webApp?.slug && !isPaymentModalOpen && paymentStatus) { if (modal?.slug && !isPaymentModalOpen && paymentStatus) {
sendEvent({ sendEvent({
eventType: 'invoice_closed', eventType: 'invoice_closed',
eventData: { eventData: {
slug: webApp.slug, slug: modal.slug,
status: paymentStatus, status: paymentStatus,
}, },
}); });
@ -227,7 +227,7 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
slug: undefined, slug: undefined,
}); });
} }
}, [isPaymentModalOpen, paymentStatus, sendEvent, setWebAppPaymentSlug, webApp]); }, [isPaymentModalOpen, paymentStatus, sendEvent, modal?.slug]);
const handleRemoveAttachBot = useLastCallback(() => { const handleRemoveAttachBot = useLastCallback(() => {
toggleAttachBot({ toggleAttachBot({
@ -693,8 +693,8 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
}; };
export default memo(withGlobal<OwnProps>( export default memo(withGlobal<OwnProps>(
(global, { webApp }): StateProps => { (global, { modal }): StateProps => {
const { botId } = webApp || {}; const { botId } = modal || {};
const attachBot = botId ? global.attachMenu.bots[botId] : undefined; const attachBot = botId ? global.attachMenu.bots[botId] : undefined;
const bot = botId ? selectUser(global, botId) : undefined; const bot = botId ? selectUser(global, botId) : undefined;
const chat = selectCurrentChat(global); const chat = selectCurrentChat(global);