235 lines
7.7 KiB
TypeScript
235 lines
7.7 KiB
TypeScript
import React, { memo, useEffect, useMemo } from '../../../lib/teact/teact';
|
|
import { getActions, withGlobal } from '../../../global';
|
|
|
|
import type {
|
|
ApiChat, ApiMediaExtendedPreview, ApiMessage, ApiUser,
|
|
} from '../../../api/types';
|
|
import type { GlobalState, TabState } from '../../../global/types';
|
|
|
|
import { getChatTitle, getCustomPeerFromInvite, getUserFullName } from '../../../global/helpers';
|
|
import {
|
|
selectChat, selectChatMessage, selectUser,
|
|
} from '../../../global/selectors';
|
|
import buildClassName from '../../../util/buildClassName';
|
|
import { formatStarsAsIcon } from '../../../util/localization/format';
|
|
import { formatInteger } from '../../../util/textFormat';
|
|
import renderText from '../../common/helpers/renderText';
|
|
|
|
import useFlag from '../../../hooks/useFlag';
|
|
import useLang from '../../../hooks/useLang';
|
|
import useLastCallback from '../../../hooks/useLastCallback';
|
|
import useOldLang from '../../../hooks/useOldLang';
|
|
import usePrevious from '../../../hooks/usePrevious';
|
|
|
|
import Avatar from '../../common/Avatar';
|
|
import StarIcon from '../../common/icons/StarIcon';
|
|
import PeerBadge from '../../common/PeerBadge';
|
|
import PeerChip from '../../common/PeerChip';
|
|
import SafeLink from '../../common/SafeLink';
|
|
import Button from '../../ui/Button';
|
|
import Modal from '../../ui/Modal';
|
|
import BalanceBlock from './BalanceBlock';
|
|
import PaidMediaThumb from './transaction/PaidMediaThumb';
|
|
|
|
import styles from './StarsBalanceModal.module.scss';
|
|
|
|
import StarsBackground from '../../../assets/stars-bg.png';
|
|
|
|
export type OwnProps = {
|
|
modal: TabState['starsPayment'];
|
|
};
|
|
|
|
type StateProps = {
|
|
starsBalanceState?: GlobalState['stars'];
|
|
bot?: ApiUser;
|
|
paidMediaMessage?: ApiMessage;
|
|
paidMediaChat?: ApiChat;
|
|
};
|
|
|
|
const StarPaymentModal = ({
|
|
modal,
|
|
bot,
|
|
starsBalanceState,
|
|
paidMediaMessage,
|
|
paidMediaChat,
|
|
}: OwnProps & StateProps) => {
|
|
const { closeStarsPaymentModal, openStarsBalanceModal, sendStarPaymentForm } = getActions();
|
|
const [isLoading, markLoading, unmarkLoading] = useFlag();
|
|
const isOpen = Boolean(modal?.inputInvoice && starsBalanceState);
|
|
|
|
const prevModal = usePrevious(modal);
|
|
const renderingModal = modal || prevModal;
|
|
|
|
const { form, subscriptionInfo } = renderingModal || {};
|
|
const amount = form?.invoice?.totalAmount || subscriptionInfo?.subscriptionPricing?.amount;
|
|
const isBotSubscription = Boolean(form?.invoice.subscriptionPeriod);
|
|
const canShowPeerItem = !subscriptionInfo?.subscriptionPricing;
|
|
|
|
const photo = form?.photo;
|
|
|
|
const oldLang = useOldLang();
|
|
const lang = useLang();
|
|
|
|
useEffect(() => {
|
|
if (!isOpen) {
|
|
unmarkLoading();
|
|
}
|
|
}, [isOpen]);
|
|
|
|
const descriptionText = useMemo(() => {
|
|
if (!renderingModal?.inputInvoice) {
|
|
return '';
|
|
}
|
|
|
|
const botName = getUserFullName(bot);
|
|
const starsText = oldLang('Stars.Intro.PurchasedText.Stars', amount);
|
|
|
|
if (paidMediaMessage) {
|
|
const extendedMedia = paidMediaMessage.content.paidMedia!.extendedMedia as ApiMediaExtendedPreview[];
|
|
const areAllPhotos = extendedMedia.every((media) => !media.duration);
|
|
const areAllVideos = extendedMedia.every((media) => !!media.duration);
|
|
|
|
const mediaText = areAllPhotos ? oldLang('Stars.Transfer.Photos', extendedMedia.length)
|
|
: areAllVideos ? oldLang('Stars.Transfer.Videos', extendedMedia.length)
|
|
: oldLang('Media', extendedMedia.length);
|
|
|
|
const channelTitle = getChatTitle(oldLang, paidMediaChat!);
|
|
return oldLang('Stars.Transfer.UnlockInfo', [mediaText, channelTitle, starsText]);
|
|
}
|
|
|
|
if (subscriptionInfo) {
|
|
return lang('StarsSubscribeText', {
|
|
chat: subscriptionInfo.title,
|
|
amount,
|
|
}, {
|
|
withNodes: true,
|
|
withMarkdown: true,
|
|
pluralValue: amount!,
|
|
});
|
|
}
|
|
|
|
if (isBotSubscription) {
|
|
return lang('StarsSubscribeBotText', {
|
|
name: form.title,
|
|
amount,
|
|
bot: botName,
|
|
}, {
|
|
pluralValue: amount!,
|
|
});
|
|
}
|
|
|
|
return oldLang('Stars.Transfer.Info', [form!.title, botName, starsText]);
|
|
}, [
|
|
renderingModal?.inputInvoice, bot, oldLang, amount, paidMediaMessage, subscriptionInfo, isBotSubscription, form,
|
|
paidMediaChat, lang,
|
|
]);
|
|
|
|
const disclaimerText = useMemo(() => {
|
|
if (subscriptionInfo) {
|
|
return lang('StarsSubscribeInfo', {
|
|
link: <SafeLink url={lang('StarsSubscribeInfoLink')} text={lang('StarsSubscribeInfoLinkText')} />,
|
|
}, {
|
|
withNodes: true,
|
|
});
|
|
}
|
|
|
|
return undefined;
|
|
}, [subscriptionInfo, lang]);
|
|
|
|
const inviteCustomPeer = useMemo(() => {
|
|
if (!subscriptionInfo) {
|
|
return undefined;
|
|
}
|
|
|
|
return getCustomPeerFromInvite(subscriptionInfo);
|
|
}, [subscriptionInfo]);
|
|
|
|
const handlePayment = useLastCallback(() => {
|
|
const balance = starsBalanceState?.balance;
|
|
if (amount === undefined || balance === undefined) {
|
|
return;
|
|
}
|
|
|
|
if (amount > balance.amount) {
|
|
openStarsBalanceModal({
|
|
originStarsPayment: modal,
|
|
});
|
|
return;
|
|
}
|
|
|
|
sendStarPaymentForm({});
|
|
markLoading();
|
|
});
|
|
|
|
return (
|
|
<Modal
|
|
contentClassName={styles.paymentContent}
|
|
isOpen={isOpen}
|
|
hasAbsoluteCloseButton
|
|
isSlim
|
|
onClose={closeStarsPaymentModal}
|
|
>
|
|
<BalanceBlock balance={starsBalanceState?.balance} className={styles.modalBalance} />
|
|
<div className={styles.paymentImages} dir={oldLang.isRtl ? 'ltr' : 'rtl'}>
|
|
{paidMediaMessage ? (
|
|
<PaidMediaThumb media={paidMediaMessage.content.paidMedia!.extendedMedia} />
|
|
) : inviteCustomPeer ? (
|
|
<>
|
|
<Avatar className={styles.paymentPhoto} peer={inviteCustomPeer} size="giant" />
|
|
<StarIcon type="gold" size="adaptive" className={styles.avatarStar} />
|
|
</>
|
|
) : (
|
|
<PeerBadge
|
|
peer={!photo ? bot : undefined}
|
|
avatarWebPhoto={photo}
|
|
avatarSize="giant"
|
|
badgeIcon="star"
|
|
badgeText={formatInteger(amount!)}
|
|
badgeClassName={styles.amountBadge}
|
|
className={styles.paymentPhoto}
|
|
/>
|
|
)}
|
|
<img className={styles.paymentImageBackground} src={StarsBackground} alt="" draggable={false} />
|
|
</div>
|
|
<h2 className={styles.headerText}>
|
|
{inviteCustomPeer ? oldLang('StarsSubscribeTitle') : oldLang('StarsConfirmPurchaseTitle')}
|
|
</h2>
|
|
{canShowPeerItem && <PeerChip className={styles.botItem} peerId={form?.botId} />}
|
|
<div className={styles.description}>
|
|
{renderText(descriptionText, ['simple_markdown', 'emoji'])}
|
|
</div>
|
|
<Button className={styles.paymentButton} size="smaller" onClick={handlePayment} isLoading={isLoading}>
|
|
{lang(isBotSubscription ? 'StarsSubscribeBotButtonMonth' : 'StarsPay', {
|
|
amount: formatStarsAsIcon(lang, amount!, true),
|
|
}, {
|
|
withNodes: true,
|
|
})}
|
|
</Button>
|
|
{disclaimerText && (
|
|
<div className={buildClassName(styles.disclaimer, styles.smallerText)}>
|
|
{disclaimerText}
|
|
</div>
|
|
)}
|
|
</Modal>
|
|
);
|
|
};
|
|
|
|
export default memo(withGlobal<OwnProps>(
|
|
(global, { modal }): StateProps => {
|
|
const bot = modal?.form?.botId ? selectUser(global, modal.form.botId) : undefined;
|
|
|
|
const messageInputInvoice = modal?.inputInvoice?.type === 'message' ? modal.inputInvoice : undefined;
|
|
const message = messageInputInvoice
|
|
? selectChatMessage(global, messageInputInvoice.chatId, messageInputInvoice.messageId) : undefined;
|
|
const chat = messageInputInvoice ? selectChat(global, messageInputInvoice.chatId) : undefined;
|
|
const isPaidMedia = message?.content.paidMedia;
|
|
|
|
return {
|
|
bot,
|
|
starsBalanceState: global.stars,
|
|
paidMediaMessage: isPaidMedia ? message : undefined,
|
|
paidMediaChat: isPaidMedia ? chat : undefined,
|
|
};
|
|
},
|
|
)(StarPaymentModal));
|