PremiumMainModal: Render all subscription options (#4129)

This commit is contained in:
Alexander Zinchuk 2023-12-28 14:38:10 +01:00
parent d2b834d1a6
commit 3e96262038
8 changed files with 87 additions and 25 deletions

View File

@ -194,7 +194,7 @@ function buildApiPremiumSubscriptionOption(option: GramJs.PremiumSubscriptionOpt
isCurrent: current,
canPurchaseUpgrade,
currency,
amount: amount.toString(),
amount: amount.toJSNumber(),
botUrl,
months,
};

View File

@ -75,7 +75,7 @@ export interface ApiPremiumSubscriptionOption {
canPurchaseUpgrade?: boolean;
months: number;
currency: string;
amount: string;
amount: number;
botUrl: string;
}

View File

@ -22,7 +22,7 @@ import Avatar from '../../common/Avatar';
import Button from '../../ui/Button';
import Link from '../../ui/Link';
import Modal from '../../ui/Modal';
import GiftOption from './GiftOption';
import PremiumSubscriptionOption from './PremiumSubscriptionOption';
import styles from './GiftPremiumModal.module.scss';
@ -141,7 +141,7 @@ const GiftPremiumModal: FC<OwnProps & StateProps> = ({
<div className={styles.options}>
{renderedGifts?.map((gift) => (
<GiftOption
<PremiumSubscriptionOption
key={gift.amount}
option={gift}
fullMonthlyAmount={fullMonthlyAmount}

View File

@ -168,3 +168,11 @@
height: 100%;
}
}
.subscriptionOptions {
width: 100%;
}
.subscriptionOption {
margin: 0.8125rem;
}

View File

@ -5,7 +5,7 @@ import React, {
import { getActions, withGlobal } from '../../../global';
import type {
ApiPremiumPromo, ApiSticker, ApiStickerSet, ApiUser,
ApiPremiumPromo, ApiPremiumSubscriptionOption, ApiSticker, ApiStickerSet, ApiUser,
} from '../../../api/types';
import type { GlobalState } from '../../../global/types';
@ -36,6 +36,7 @@ import PremiumFeatureModal, {
PREMIUM_FEATURE_SECTIONS,
PREMIUM_FEATURE_TITLES,
} from './PremiumFeatureModal';
import PremiumSubscriptionOption from './PremiumSubscriptionOption';
import styles from './PremiumMainModal.module.scss';
@ -133,6 +134,7 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
const lang = useLang();
const [isHeaderHidden, setHeaderHidden] = useState(true);
const [currentSection, setCurrentSection] = useState<string | undefined>(initialSection);
const [selectedSubscriptionOption, setSubscriptionOption] = useState<ApiPremiumSubscriptionOption>();
const handleOpen = useCallback((section: string | undefined) => {
return () => {
@ -146,7 +148,7 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
setHeaderHidden(scrollTop <= 150);
}
function handleClickWithStartParam(startParam?: string) {
const handleClickWithStartParam = useLastCallback((startParam?: string) => {
const dialog = dialogRef.current;
if (!dialog) return;
@ -160,12 +162,21 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
});
closePremiumModal();
}
}
const handleClick = useLastCallback(() => {
handleClickWithStartParam();
});
const handleClick = useCallback(() => {
if (selectedSubscriptionOption) {
handleClickWithStartParam(String(selectedSubscriptionOption.months));
} else {
handleClickWithStartParam();
}
}, [selectedSubscriptionOption, handleClickWithStartParam]);
const handleChangeSubscriptionOption = useCallback((months: number) => {
const foundOption = promo?.options.find((option) => option.months === months);
setSubscriptionOption(foundOption);
}, [promo]);
const showConfetti = useCallback(() => {
const dialog = dialogRef.current;
if (!dialog) return;
@ -206,6 +217,11 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
});
}, [loadStickers, fromUserStatusEmoji, fromUserStatusSet]);
useEffect(() => {
const [defaultOption] = promo?.options ?? [];
setSubscriptionOption(defaultOption);
}, [promo]);
const handleOpenStatusSet = useLastCallback(() => {
if (!fromUserStatusSet) return;
@ -231,10 +247,15 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
return [renderText(first), link, renderText(second)];
}, [fromUser, fromUserStatusSet, lang]);
if (!promo || (fromUserStatusEmoji && !fromUserStatusSet)) return undefined;
const fullMonthlyAmount = useMemo(() => {
const monthOption = promo?.options.find((option) => option.months === 1);
if (!monthOption) {
return undefined;
}
return Number(monthOption.amount);
}, [promo]);
// TODO Support all subscription options
const month = promo.options.find((option) => option.months === 1)!;
if (!promo || (fromUserStatusEmoji && !fromUserStatusSet)) return undefined;
function getHeaderText() {
if (isGift) {
@ -279,6 +300,24 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
);
}
function renderSubscriptionOptions() {
return (
<div className={styles.subscriptionOptions}>
{promo?.options
.map((option) => (
<PremiumSubscriptionOption
className={styles.subscriptionOption}
key={option.amount}
option={option}
onChange={handleChangeSubscriptionOption}
fullMonthlyAmount={fullMonthlyAmount}
checked={selectedSubscriptionOption?.months === option.months}
/>
))}
</div>
);
}
return (
<Modal
className={styles.root}
@ -319,12 +358,12 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
<div className={styles.description}>
{renderText(getHeaderDescription(), ['simple_markdown', 'emoji'])}
</div>
{!isPremium && renderSubscriptionOptions()}
<div className={buildClassName(styles.header, isHeaderHidden && styles.hiddenHeader)}>
<h2 className={styles.premiumHeaderText}>
{lang('TelegramPremium')}
</h2>
</div>
<div className={buildClassName(styles.list, isPremium && styles.noButton)}>
{filteredSections.map((section, index) => {
return (
@ -355,10 +394,15 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
</div>
{renderFooterText()}
</div>
{!isPremium && (
{!isPremium && selectedSubscriptionOption && (
<div className={styles.footer}>
<Button className={styles.button} isShiny withPremiumGradient onClick={handleClick}>
{lang('SubscribeToPremium', formatCurrency(Number(month.amount), month.currency, lang.code))}
{lang('SubscribeToPremium',
formatCurrency(
selectedSubscriptionOption.amount,
selectedSubscriptionOption.currency,
lang.code,
))}
</Button>
</div>
)}

View File

@ -2,24 +2,27 @@ import type { ChangeEvent } from 'react';
import type { FC } from '../../../lib/teact/teact';
import React, { memo, useCallback, useMemo } from '../../../lib/teact/teact';
import type { ApiPremiumGiftOption } from '../../../api/types';
import buildClassName from '../../../util/buildClassName';
import { formatCurrency } from '../../../util/formatCurrency';
import useLang from '../../../hooks/useLang';
import styles from './GiftOption.module.scss';
import styles from './PremiumSubscriptionOption.module.scss';
type OwnProps = {
option: ApiPremiumGiftOption;
option: {
months: number;
currency: string;
amount: number;
};
checked?: boolean;
fullMonthlyAmount?: number;
className?: string;
onChange: (month: number) => void;
};
const GiftOption: FC<OwnProps> = ({
option, checked, fullMonthlyAmount, onChange,
const PremiumSubscriptionOption: FC<OwnProps> = ({
option, checked, fullMonthlyAmount, onChange, className,
}) => {
const lang = useLang();
@ -39,7 +42,14 @@ const GiftOption: FC<OwnProps> = ({
}, [months, onChange]);
return (
<label className={buildClassName(styles.wrapper, checked && styles.active)} dir={lang.isRtl ? 'rtl' : undefined}>
<label
className={buildClassName(
styles.wrapper,
checked && styles.active,
className,
)}
dir={lang.isRtl ? 'rtl' : undefined}
>
<input
className={styles.input}
type="radio"
@ -62,4 +72,4 @@ const GiftOption: FC<OwnProps> = ({
);
};
export default memo(GiftOption);
export default memo(PremiumSubscriptionOption);

View File

@ -407,7 +407,7 @@ addActionHandler('openGiftPremiumModal', async (global, actions, payload): Promi
isOpen: true,
forUserId,
monthlyCurrency: month.currency,
monthlyAmount: month.amount,
monthlyAmount: String(month.amount),
},
}, tabId);
setGlobal(global);