Gifts: Support isPremiumRequired (#6100)
This commit is contained in:
parent
fd57d088f9
commit
6d39fd28d8
@ -25,7 +25,7 @@ export function buildApiStarGift(starGift: GramJs.TypeStarGift): ApiStarGift {
|
||||
if (starGift instanceof GramJs.StarGiftUnique) {
|
||||
const {
|
||||
id, num, ownerId, ownerName, title, attributes, availabilityIssued, availabilityTotal, slug, ownerAddress,
|
||||
giftAddress, resellAmount, releasedBy, resaleTonOnly,
|
||||
giftAddress, resellAmount, releasedBy, resaleTonOnly, requirePremium,
|
||||
} = starGift;
|
||||
|
||||
return {
|
||||
@ -43,6 +43,7 @@ export function buildApiStarGift(starGift: GramJs.TypeStarGift): ApiStarGift {
|
||||
giftAddress,
|
||||
resellPrice: resellAmount && resellAmount.map((amount) => buildApiCurrencyAmount(amount)).filter(Boolean),
|
||||
releasedByPeerId: releasedBy && getApiChatIdFromMtpPeer(releasedBy),
|
||||
requirePremium,
|
||||
resaleTonOnly,
|
||||
};
|
||||
}
|
||||
@ -50,6 +51,7 @@ export function buildApiStarGift(starGift: GramJs.TypeStarGift): ApiStarGift {
|
||||
const {
|
||||
id, limited, stars, availabilityRemains, availabilityTotal, convertStars, firstSaleDate, lastSaleDate, soldOut,
|
||||
birthday, upgradeStars, resellMinStars, title, availabilityResale, releasedBy,
|
||||
requirePremium, limitedPerUser, perUserTotal, perUserRemains,
|
||||
} = starGift;
|
||||
|
||||
addDocumentToLocalDb(starGift.sticker);
|
||||
@ -74,6 +76,10 @@ export function buildApiStarGift(starGift: GramJs.TypeStarGift): ApiStarGift {
|
||||
resellMinStars: resellMinStars?.toJSNumber(),
|
||||
releasedByPeerId: releasedBy && getApiChatIdFromMtpPeer(releasedBy),
|
||||
availabilityResale: availabilityResale?.toJSNumber(),
|
||||
requirePremium,
|
||||
limitedPerUser,
|
||||
perUserTotal,
|
||||
perUserRemains,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -22,6 +22,10 @@ export interface ApiStarGiftRegular {
|
||||
resellMinStars?: number;
|
||||
releasedByPeerId?: string;
|
||||
title?: string;
|
||||
requirePremium?: true;
|
||||
limitedPerUser?: true;
|
||||
perUserTotal?: number;
|
||||
perUserRemains?: number;
|
||||
}
|
||||
|
||||
export interface ApiStarGiftUnique {
|
||||
@ -39,6 +43,7 @@ export interface ApiStarGiftUnique {
|
||||
giftAddress?: string;
|
||||
resellPrice?: ApiTypeCurrencyAmount[];
|
||||
releasedByPeerId?: string;
|
||||
requirePremium?: true;
|
||||
resaleTonOnly?: true;
|
||||
}
|
||||
|
||||
|
||||
@ -2153,6 +2153,10 @@
|
||||
"TitleAgeCheckFailed" = "Age Check Failed";
|
||||
"TitleAgeCheckSuccess" = "Age Check Success";
|
||||
"ButtonAgeVerification" = "Verify My Age";
|
||||
"GiftRibbonPremium" = "premium";
|
||||
"NotificationGiftsLimit" = "You've already sent {count} of these gifts, and it's the limit.";
|
||||
"PremiumGiftHeader" = "Premium Gift";
|
||||
"DescriptionGiftPremiumRequired" = "Subscribe to **Telegram Premium** to send up to **{count}** of these gifts and unlock access to multiple additional features.";
|
||||
"PriceInStars" = "Price in Stars";
|
||||
"PriceInTON" = "Price in TON";
|
||||
"DescriptionComposerGiftMinimumCurrencyPrice" = "Minimum price is **{amount}**.";
|
||||
@ -2164,4 +2168,5 @@
|
||||
"LabelPayInTON" = "Pay in TON";
|
||||
"PriceChanged" = "Price Changed";
|
||||
"PayNewPrice" = "Pay New Price";
|
||||
"PriceChangedText" = "The price has already changed from **{originalAmount}** to **{newAmount}**. Do you want to pay the new price?";
|
||||
"PriceChangedText" = "The price has already changed from **{originalAmount}** to **{newAmount}**. Do you want to pay the new price?";
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ const COLORS = {
|
||||
blue: [['#6ED2FF', '#34A4FC'], ['#344F5A', '#152E42']],
|
||||
purple: [['#E367D7', '#757BF6'], ['#E367D7', '#757BF6']],
|
||||
green: [['#52D553', '#4BB121'], ['#52D553', '#4BB121']],
|
||||
orange: [['#D48F23', '#BE7E15'], ['#D48F23', '#BE7E15']],
|
||||
} as const;
|
||||
type ColorKey = keyof typeof COLORS;
|
||||
|
||||
|
||||
@ -50,6 +50,11 @@
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.sticker-wrapper {
|
||||
position: relative;
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.header-text {
|
||||
margin-inline: 0.5rem;
|
||||
font-size: 1.5rem;
|
||||
@ -176,3 +181,16 @@
|
||||
.subscriptionOption {
|
||||
margin: 0.8125rem;
|
||||
}
|
||||
|
||||
.starParticlesHeader,
|
||||
.giftParticlesHeader {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
.starParticlesHeader {
|
||||
padding-top: 10rem !important;
|
||||
}
|
||||
|
||||
.giftParticlesHeader {
|
||||
padding-top: 11rem !important;
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import type {
|
||||
ApiPremiumPromo,
|
||||
ApiPremiumSection,
|
||||
ApiPremiumSubscriptionOption,
|
||||
ApiStarGift,
|
||||
ApiSticker,
|
||||
ApiStickerSet,
|
||||
ApiUser,
|
||||
@ -19,6 +20,7 @@ import { selectIsCurrentUserPremium, selectStickerSet, selectTabState, selectUse
|
||||
import { selectPremiumLimit } from '../../../global/selectors/limits';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { formatCurrency } from '../../../util/formatCurrency';
|
||||
import { getStickerFromGift } from '../../common/helpers/gifts';
|
||||
import { REM } from '../../common/helpers/mediaDimensions';
|
||||
import renderText from '../../common/helpers/renderText';
|
||||
import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities';
|
||||
@ -99,6 +101,7 @@ type StateProps = {
|
||||
isSuccess?: boolean;
|
||||
isGift?: boolean;
|
||||
monthsAmount?: number;
|
||||
gift?: ApiStarGift;
|
||||
limitChannels: number;
|
||||
limitPins: number;
|
||||
limitLinks: number;
|
||||
@ -130,6 +133,7 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
||||
toUser,
|
||||
monthsAmount,
|
||||
premiumPromoOrder,
|
||||
gift,
|
||||
}) => {
|
||||
const dialogRef = useRef<HTMLDivElement>();
|
||||
const {
|
||||
@ -273,6 +277,10 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
||||
if (!promo || (fromUserStatusEmoji && !fromUserStatusSet)) return undefined;
|
||||
|
||||
function getHeaderText() {
|
||||
if (gift) {
|
||||
return lang('PremiumGiftHeader');
|
||||
}
|
||||
|
||||
if (isGift) {
|
||||
return renderText(
|
||||
fromUser?.id === currentUserId
|
||||
@ -307,6 +315,11 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
|
||||
function getHeaderDescription() {
|
||||
if (gift) {
|
||||
const perUserTotal = gift.type !== 'starGiftUnique' ? gift.perUserTotal : 0;
|
||||
return lang('DescriptionGiftPremiumRequired', { count: perUserTotal });
|
||||
}
|
||||
|
||||
if (isGift) {
|
||||
return fromUser?.id === currentUserId
|
||||
? oldLang('TelegramPremiumUserGiftedPremiumOutboundDialogSubtitle', getUserFullName(toUser))
|
||||
@ -322,6 +335,52 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
||||
: oldLang(isPremium ? 'TelegramPremiumSubscribedSubtitle' : 'TelegramPremiumSubtitle');
|
||||
}
|
||||
|
||||
function renderHeader() {
|
||||
if (gift) {
|
||||
const giftSticker = getStickerFromGift(gift);
|
||||
return (
|
||||
<ParticlesHeader
|
||||
model="sticker"
|
||||
sticker={giftSticker}
|
||||
color="purple"
|
||||
title={getHeaderText()}
|
||||
description={renderText(getHeaderDescription(), ['simple_markdown', 'emoji'])}
|
||||
className={styles.giftParticlesHeader}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (!fromUserStatusEmoji) {
|
||||
return (
|
||||
<ParticlesHeader
|
||||
model="swaying-star"
|
||||
color="purple"
|
||||
title={getHeaderText()}
|
||||
description={renderText(getHeaderDescription(), ['simple_markdown', 'emoji'])}
|
||||
className={styles.starParticlesHeader}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<CustomEmoji
|
||||
className={styles.statusEmoji}
|
||||
onClick={handleOpenStatusSet}
|
||||
documentId={fromUserStatusEmoji.id}
|
||||
isBig
|
||||
size={STATUS_EMOJI_SIZE}
|
||||
/>
|
||||
<h2 className={buildClassName(styles.headerText, fromUserStatusSet && styles.stickerSetText)}>
|
||||
{getHeaderText()}
|
||||
</h2>
|
||||
<div className={styles.description}>
|
||||
{renderText(getHeaderDescription(), ['simple_markdown', 'emoji'])}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function renderFooterText() {
|
||||
if (!promo || (isGift && fromUser?.id === currentUserId)) {
|
||||
return undefined;
|
||||
@ -375,30 +434,7 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
||||
>
|
||||
<Icon name="close" />
|
||||
</Button>
|
||||
{!fromUserStatusEmoji ? (
|
||||
<ParticlesHeader
|
||||
model="swaying-star"
|
||||
color="purple"
|
||||
title={getHeaderText()}
|
||||
description={renderText(getHeaderDescription(), ['simple_markdown', 'emoji'])}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<CustomEmoji
|
||||
className={styles.statusEmoji}
|
||||
onClick={handleOpenStatusSet}
|
||||
documentId={fromUserStatusEmoji.id}
|
||||
isBig
|
||||
size={STATUS_EMOJI_SIZE}
|
||||
/>
|
||||
<h2 className={buildClassName(styles.headerText, fromUserStatusSet && styles.stickerSetText)}>
|
||||
{getHeaderText()}
|
||||
</h2>
|
||||
<div className={styles.description}>
|
||||
{renderText(getHeaderDescription(), ['simple_markdown', 'emoji'])}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{renderHeader()}
|
||||
{!isPremium && !isGift && renderSubscriptionOptions()}
|
||||
<div className={buildClassName(styles.header, isHeaderHidden && styles.hiddenHeader)}>
|
||||
<h2 className={styles.premiumHeaderText}>
|
||||
@ -483,6 +519,7 @@ export default memo(withGlobal<OwnProps>((global): StateProps => {
|
||||
isSuccess: premiumModal?.isSuccess,
|
||||
isGift: premiumModal?.isGift,
|
||||
monthsAmount: premiumModal?.monthsAmount,
|
||||
gift: premiumModal?.gift,
|
||||
fromUser,
|
||||
fromUserStatusEmoji,
|
||||
fromUserStatusSet,
|
||||
|
||||
@ -30,3 +30,14 @@
|
||||
text-align: center;
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
.stickerWrapper {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 2rem;
|
||||
transition: transform 0.25s ease-out;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,35 +1,47 @@
|
||||
import type { TeactNode } from '@teact';
|
||||
import { memo, useLayoutEffect, useRef } from '@teact';
|
||||
|
||||
import type { ApiSticker } from '../../../api/types';
|
||||
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { PARTICLE_BURST_PARAMS, PARTICLE_COLORS, setupParticles } from '../../../util/particles.ts';
|
||||
import { REM } from '../../common/helpers/mediaDimensions';
|
||||
|
||||
import useLastCallback from '../../../hooks/useLastCallback.ts';
|
||||
|
||||
import StickerView from '../../common/StickerView';
|
||||
import SpeedingDiamond from './SpeedingDiamond.tsx';
|
||||
import SwayingStar from './SwayingStar.tsx';
|
||||
|
||||
import styles from './ParticlesHeader.module.scss';
|
||||
|
||||
interface OwnProps {
|
||||
model: 'swaying-star' | 'speeding-diamond';
|
||||
model: 'swaying-star' | 'speeding-diamond' | 'sticker';
|
||||
sticker?: ApiSticker;
|
||||
color: 'purple' | 'gold' | 'blue';
|
||||
title: TeactNode;
|
||||
description: TeactNode;
|
||||
isDisabled?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const GIFT_STICKER_SIZE = 8 * REM;
|
||||
|
||||
const PARTICLE_PARAMS = {
|
||||
centerShift: [0, -36] as const,
|
||||
};
|
||||
|
||||
function ParticlesHeader({
|
||||
model,
|
||||
sticker,
|
||||
color,
|
||||
title,
|
||||
description,
|
||||
isDisabled,
|
||||
className,
|
||||
}: OwnProps) {
|
||||
const canvasRef = useRef<HTMLCanvasElement>();
|
||||
const stickerRef = useRef<HTMLDivElement>();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (isDisabled) return undefined;
|
||||
@ -49,7 +61,7 @@ function ParticlesHeader({
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<div className={buildClassName(styles.root, className)}>
|
||||
<canvas ref={canvasRef} className={styles.particles} />
|
||||
|
||||
{model === 'swaying-star' ? (
|
||||
@ -58,8 +70,23 @@ function ParticlesHeader({
|
||||
centerShift={PARTICLE_PARAMS.centerShift}
|
||||
onMouseMove={handleMouseMove}
|
||||
/>
|
||||
) : model === 'speeding-diamond' && (
|
||||
) : model === 'speeding-diamond' ? (
|
||||
<SpeedingDiamond onMouseMove={handleMouseMove} />
|
||||
) : model === 'sticker' && sticker && (
|
||||
<div
|
||||
ref={stickerRef}
|
||||
className={styles.stickerWrapper}
|
||||
style={`width: ${GIFT_STICKER_SIZE}px; height: ${GIFT_STICKER_SIZE}px`}
|
||||
onMouseMove={handleMouseMove}
|
||||
>
|
||||
<StickerView
|
||||
containerRef={stickerRef}
|
||||
sticker={sticker}
|
||||
size={GIFT_STICKER_SIZE}
|
||||
shouldPreloadPreview
|
||||
shouldLoop={true}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<h2 className={styles.title}>
|
||||
|
||||
@ -75,6 +75,16 @@
|
||||
font-size: 0.75rem !important;
|
||||
}
|
||||
|
||||
.premiumRequired {
|
||||
outline: 0.125rem solid #D18D21;
|
||||
outline-offset: -0.125rem;
|
||||
|
||||
&:focus-visible {
|
||||
outline: 0.125rem solid #D18D21;
|
||||
outline-offset: -0.125rem;
|
||||
}
|
||||
}
|
||||
|
||||
.amount {
|
||||
margin-top: 0.0625rem; // It just refuses to be centered
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { memo, useMemo, useRef, useState } from '../../../lib/teact/teact';
|
||||
import { getActions } from '../../../global';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type {
|
||||
ApiStarGift,
|
||||
@ -7,6 +7,7 @@ import type {
|
||||
} from '../../../api/types';
|
||||
|
||||
import { STARS_CURRENCY_CODE, TON_CURRENCY_CODE } from '../../../config';
|
||||
import { selectIsCurrentUserPremium } from '../../../global/selectors';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { formatStarsAsIcon, formatTonAsIcon } from '../../../util/localization/format';
|
||||
import { getStickerFromGift } from '../../common/helpers/gifts';
|
||||
@ -30,12 +31,16 @@ export type OwnProps = {
|
||||
isResale?: boolean;
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
isCurrentUserPremium?: boolean;
|
||||
};
|
||||
|
||||
const GIFT_STICKER_SIZE = 90;
|
||||
|
||||
function GiftItemStar({
|
||||
gift, observeIntersection, onClick, isResale,
|
||||
}: OwnProps) {
|
||||
const { openGiftInfoModal } = getActions();
|
||||
gift, observeIntersection, onClick, isResale, isCurrentUserPremium,
|
||||
}: OwnProps & StateProps) {
|
||||
const { openGiftInfoModal, openPremiumModal, showNotification } = getActions();
|
||||
|
||||
const ref = useRef<HTMLDivElement>();
|
||||
const stickerRef = useRef<HTMLDivElement>();
|
||||
@ -69,6 +74,9 @@ function GiftItemStar({
|
||||
? lang.number(resellMinStars) + '+' : priceInfo?.amount || 0;
|
||||
const isLimited = !isGiftUnique && Boolean(regularGift?.isLimited);
|
||||
const isSoldOut = !isGiftUnique && Boolean(regularGift?.isSoldOut);
|
||||
const isPremiumRequired = Boolean(gift?.requirePremium);
|
||||
const isUserLimitReached = Boolean(regularGift?.limitedPerUser && !regularGift?.perUserRemains);
|
||||
const perUserTotal = regularGift?.perUserTotal;
|
||||
|
||||
const handleGiftClick = useLastCallback(() => {
|
||||
if (isSoldOut && !isResale) {
|
||||
@ -76,6 +84,25 @@ function GiftItemStar({
|
||||
return;
|
||||
}
|
||||
|
||||
if (isUserLimitReached) {
|
||||
showNotification({
|
||||
message: lang('NotificationGiftsLimit', {
|
||||
count: perUserTotal,
|
||||
}, {
|
||||
withMarkdown: true,
|
||||
withNodes: true,
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPremiumRequired && !isCurrentUserPremium) {
|
||||
openPremiumModal({
|
||||
gift,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
onClick(gift, isResale ? 'resell' : 'original');
|
||||
});
|
||||
|
||||
@ -116,6 +143,9 @@ function GiftItemStar({
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (isPremiumRequired) {
|
||||
return <GiftRibbon color="orange" text={lang('LimitPremium')} />;
|
||||
}
|
||||
if (isResale) {
|
||||
return <GiftRibbon color="green" text={lang('GiftRibbonResale')} />;
|
||||
}
|
||||
@ -126,7 +156,7 @@ function GiftItemStar({
|
||||
return <GiftRibbon color="blue" text={lang('GiftLimited')} />;
|
||||
}
|
||||
return undefined;
|
||||
}, [isGiftUnique, isResale, gift, isSoldOut, isLimited, lang, giftNumber]);
|
||||
}, [isGiftUnique, isResale, gift, isSoldOut, isLimited, lang, giftNumber, isPremiumRequired]);
|
||||
|
||||
useOnIntersect(ref, observeIntersection, (entry) => {
|
||||
const visible = entry.isIntersecting;
|
||||
@ -136,7 +166,12 @@ function GiftItemStar({
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={buildClassName(styles.container, styles.starGift, 'starGiftItem')}
|
||||
className={buildClassName(
|
||||
styles.container,
|
||||
styles.starGift,
|
||||
'starGiftItem',
|
||||
isPremiumRequired && styles.premiumRequired,
|
||||
)}
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
onClick={handleGiftClick}
|
||||
@ -178,4 +213,12 @@ function GiftItemStar({
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(GiftItemStar);
|
||||
export default memo(
|
||||
withGlobal<OwnProps>((global): StateProps => {
|
||||
const isCurrentUserPremium = selectIsCurrentUserPremium(global);
|
||||
|
||||
return {
|
||||
isCurrentUserPremium,
|
||||
};
|
||||
})(GiftItemStar),
|
||||
);
|
||||
|
||||
@ -484,7 +484,7 @@ addActionHandler('closePremiumModal', (global, actions, payload): ActionReturnTy
|
||||
|
||||
addActionHandler('openPremiumModal', async (global, actions, payload): Promise<void> => {
|
||||
const {
|
||||
initialSection, fromUserId, isSuccess, isGift, monthsAmount, toUserId,
|
||||
initialSection, fromUserId, isSuccess, isGift, monthsAmount, toUserId, gift,
|
||||
tabId = getCurrentTabId(),
|
||||
} = payload || {};
|
||||
|
||||
@ -505,6 +505,7 @@ addActionHandler('openPremiumModal', async (global, actions, payload): Promise<v
|
||||
isGift,
|
||||
monthsAmount,
|
||||
isSuccess,
|
||||
gift,
|
||||
},
|
||||
}, tabId);
|
||||
setGlobal(global);
|
||||
|
||||
@ -2435,6 +2435,7 @@ export interface ActionPayloads {
|
||||
isSuccess?: boolean;
|
||||
isGift?: boolean;
|
||||
monthsAmount?: number;
|
||||
gift?: ApiStarGift;
|
||||
} & WithTabId) | undefined;
|
||||
closePremiumModal: WithTabId | undefined;
|
||||
|
||||
|
||||
@ -640,6 +640,7 @@ export type TabState = {
|
||||
isGift?: boolean;
|
||||
monthsAmount?: number;
|
||||
isSuccess?: boolean;
|
||||
gift?: ApiStarGift;
|
||||
};
|
||||
|
||||
giveawayModal?: {
|
||||
|
||||
8
src/types/language.d.ts
vendored
8
src/types/language.d.ts
vendored
@ -1608,6 +1608,8 @@ export interface LangPair {
|
||||
'TitleAgeCheckFailed': undefined;
|
||||
'TitleAgeCheckSuccess': undefined;
|
||||
'ButtonAgeVerification': undefined;
|
||||
'GiftRibbonPremium': undefined;
|
||||
'PremiumGiftHeader': undefined;
|
||||
'PriceInStars': undefined;
|
||||
'PriceInTON': undefined;
|
||||
'OnlyAcceptTON': undefined;
|
||||
@ -2793,6 +2795,12 @@ export interface LangPairWithVariables<V = LangVariable> {
|
||||
'ButtonSensitiveAlways': {
|
||||
'years': V;
|
||||
};
|
||||
'NotificationGiftsLimit': {
|
||||
'count': V;
|
||||
};
|
||||
'DescriptionGiftPremiumRequired': {
|
||||
'count': V;
|
||||
};
|
||||
'DescriptionComposerGiftMinimumCurrencyPrice': {
|
||||
'amount': V;
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user