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

View File

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

View File

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

View File

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

View File

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

View File

@ -19,7 +19,7 @@ import ChatlistNew from './ChatlistNew';
import styles from './ChatlistModal.module.scss';
export type OwnProps = {
info?: TabState['chatlistModal'];
modal?: TabState['chatlistModal'];
};
type StateProps = {
@ -27,15 +27,15 @@ type StateProps = {
};
const ChatlistInviteModal: FC<OwnProps & StateProps> = ({
info,
modal,
folder,
}) => {
const { closeChatlistModal } = getActions();
const lang = useLang();
const isOpen = Boolean(info);
const renderingInfo = usePrevious(info) || info;
const isOpen = Boolean(modal);
const renderingInfo = usePrevious(modal) || modal;
const renderingFolder = usePrevious(folder) || folder;
const title = useMemo(() => {
@ -111,8 +111,8 @@ const ChatlistInviteModal: FC<OwnProps & StateProps> = ({
};
export default memo(withGlobal<OwnProps>(
(global, { info }): StateProps => {
const { invite, removal } = info || {};
(global, { modal }): StateProps => {
const { invite, removal } = modal || {};
const folderId = removal?.folderId || (invite && 'folderId' in invite ? invite.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';
const InviteViaLinkModalAsync: FC<OwnProps> = (props) => {
const { chatId } = props;
const InviteViaLinkModal = useModuleLoader(Bundles.Extra, 'InviteViaLinkModal', !chatId);
const { modal } = props;
const InviteViaLinkModal = useModuleLoader(Bundles.Extra, 'InviteViaLinkModal', !modal);
// eslint-disable-next-line react/jsx-props-no-spreading
return InviteViaLinkModal ? <InviteViaLinkModal {...props} /> : undefined;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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