TelegramPWA/src/components/modals/stars/subscription/StarsSubscriptionModal.tsx
2024-12-06 19:44:04 +04:00

253 lines
7.4 KiB
TypeScript

import type { FC } from '../../../../lib/teact/teact';
import React, { memo, useMemo } from '../../../../lib/teact/teact';
import { getActions, withGlobal } from '../../../../global';
import type {
ApiPeer,
} from '../../../../api/types';
import type { TabState } from '../../../../global/types';
import { STARS_ICON_PLACEHOLDER } from '../../../../config';
import { isApiPeerUser } from '../../../../global/helpers/peers';
import {
selectPeer,
} from '../../../../global/selectors';
import buildClassName from '../../../../util/buildClassName';
import { formatDateTimeToString } from '../../../../util/dates/dateFormat';
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 SafeLink from '../../../common/SafeLink';
import Button from '../../../ui/Button';
import TableInfoModal, { type TableData } from '../../common/TableInfoModal';
import styles from './StarsSubscriptionModal.module.scss';
import StarsBackground from '../../../../assets/stars-bg.png';
export type OwnProps = {
modal: TabState['starsSubscriptionModal'];
};
type StateProps = {
peer?: ApiPeer;
};
const StarsSubscriptionModal: FC<OwnProps & StateProps> = ({
modal, peer,
}) => {
const {
closeStarsSubscriptionModal,
fulfillStarsSubscription,
changeStarsSubscription,
checkChatInvite,
loadStarStatus,
openInvoice,
} = getActions();
const oldLang = useOldLang();
const lang = useLang();
const { subscription } = modal || {};
const buttonState = useMemo(() => {
if (!subscription) {
return 'hidden';
}
if (subscription.canRefulfill) {
return 'refulfill';
}
const isActive = subscription.until > Date.now() / 1000;
if (isActive && !subscription.isCancelled) {
return 'cancel';
}
if (isActive && subscription.isCancelled) {
return 'renew';
}
const canRestart = subscription.chatInviteHash || subscription.invoiceSlug;
if (!isActive && canRestart) {
return 'restart';
}
return 'ok';
}, [subscription]);
const handleButtonClick = useLastCallback(() => {
if (!subscription) {
return;
}
switch (buttonState) {
case 'refulfill': {
fulfillStarsSubscription({ id: subscription.id });
break;
}
case 'restart': {
if (subscription.chatInviteHash) {
checkChatInvite({ hash: subscription.chatInviteHash });
} else if (subscription.invoiceSlug) {
openInvoice({
type: 'slug',
slug: subscription.invoiceSlug,
});
}
loadStarStatus();
break;
}
case 'renew': {
changeStarsSubscription({ id: subscription.id, isCancelled: false });
break;
}
case 'cancel': {
changeStarsSubscription({ id: subscription.id, isCancelled: true });
break;
}
}
closeStarsSubscriptionModal();
});
const starModalData = useMemo(() => {
if (!subscription || !peer) {
return undefined;
}
const {
pricing, until, isCancelled, canRefulfill, photo, title, hasBotCancelled,
} = subscription;
const isBotSubscription = isApiPeerUser(peer);
const header = (
<div className={buildClassName(styles.header, styles.starsHeader)}>
<div className={styles.avatarWrapper}>
<Avatar peer={!photo ? peer : undefined} webPhoto={photo} size="jumbo" />
<StarIcon className={styles.subscriptionStar} type="gold" size="adaptive" />
</div>
<img
className={buildClassName(styles.starsBackground)}
src={StarsBackground}
alt=""
draggable={false}
/>
<h1 className={styles.title}>{title || oldLang('StarsSubscriptionTitle')}</h1>
<p className={styles.amount}>
{lang('StarsPerMonth', {
amount: pricing.amount,
}, {
withNodes: true,
specialReplacement: {
[STARS_ICON_PLACEHOLDER]: <StarIcon className={styles.amountStar} size="adaptive" type="gold" />,
},
})}
</p>
</div>
);
const tableData: TableData = [];
tableData.push([
oldLang(isBotSubscription ? 'StarsSubscriptionBot' : 'StarsSubscriptionChannel'),
{ chatId: peer.id },
]);
if (title) {
tableData.push([
oldLang('StarsSubscriptionBotProduct'),
title,
]);
}
const hasExpired = until < Date.now() / 1000;
tableData.push([
oldLang(hasExpired ? 'StarsSubscriptionUntilExpired'
: isCancelled ? 'StarsSubscriptionUntilExpires' : 'StarsSubscriptionUntilRenews'),
formatDateTimeToString(until * 1000, oldLang.code, true),
]);
const footerTos = lang('StarsTransactionTOS', {
link: <SafeLink url={lang('StarsTransactionTOSLink')} text={lang('StarsTransactionTOSLinkText')} />,
}, {
withNodes: true,
});
const footer = (
<span className={styles.footer}>
<p className={styles.secondary}>{footerTos}</p>
{isCancelled && (
<p className={styles.danger}>
{oldLang(hasBotCancelled ? 'StarsSubscriptionBotCancelledText' : 'StarsSubscriptionCancelledText')}
</p>
)}
{canRefulfill && (
<p className={styles.secondary}>
{oldLang('StarsSubscriptionRefulfillInfo', formatDateTimeToString(until * 1000, oldLang.code, true))}
</p>
)}
{!isCancelled && !canRefulfill && hasExpired && (
<p className={styles.secondary}>
{oldLang('StarsSubscriptionExpiredInfo', formatDateTimeToString(until * 1000, oldLang.code, true))}
</p>
)}
{!isCancelled && !canRefulfill && !hasExpired && (
<p className={styles.secondary}>
{oldLang('StarsSubscriptionCancelInfo', formatDateTimeToString(until * 1000, oldLang.code, true))}
</p>
)}
{buttonState !== 'hidden' && (
<Button
size="smaller"
color={buttonState === 'cancel' ? 'danger' : 'primary'}
isText={buttonState === 'cancel'}
onClick={handleButtonClick}
>
{oldLang(
buttonState === 'cancel' ? 'StarsSubscriptionCancel'
: buttonState === 'refulfill' ? 'StarsSubscriptionRefulfill'
: buttonState === 'restart' ? 'StarsSubscriptionAgain'
: buttonState === 'renew' ? 'StarsSubscriptionRenew' : 'OK',
)}
</Button>
)}
</span>
);
return {
header,
tableData,
footer,
};
}, [buttonState, lang, oldLang, peer, subscription]);
const prevModalData = usePrevious(starModalData);
const renderingModalData = prevModalData || starModalData;
return (
<TableInfoModal
isOpen={Boolean(subscription)}
className={styles.modal}
header={renderingModalData?.header}
tableData={renderingModalData?.tableData}
footer={renderingModalData?.footer}
onClose={closeStarsSubscriptionModal}
/>
);
};
export default memo(withGlobal<OwnProps>(
(global, { modal }): StateProps => {
const peerId = modal?.subscription.peerId;
const peer = peerId ? selectPeer(global, peerId) : undefined;
return {
peer,
};
},
)(StarsSubscriptionModal));