Stars Giveaway: Create giveaway in groups and channels (#4980)

This commit is contained in:
Alexander Zinchuk 2024-09-24 14:48:44 +02:00
parent 9fa6077fc7
commit 10629eb69a
47 changed files with 1286 additions and 457 deletions

View File

@ -82,6 +82,7 @@ export interface GramJsAppConfig extends LimitsConfig {
upload_premium_speedup_notify_period?: number;
upload_premium_speedup_download?: number;
upload_premium_speedup_upload?: number;
stars_gifts_enabled?: boolean;
}
function buildEmojiSounds(appConfig: GramJsAppConfig) {
@ -163,5 +164,6 @@ export function buildAppConfig(json: GramJs.TypeJSONValue, hash: number): ApiApp
bandwidthPremiumDownloadSpeedup: appConfig.upload_premium_speedup_download,
channelRestrictAdsLevelMin: appConfig.channel_restrict_sponsored_level_min,
isChannelRevenueWithdrawalEnabled: appConfig.channel_revenue_withdrawal_enabled,
isStarsGiftsEnabled: appConfig.stars_gifts_enabled,
};
}

View File

@ -576,7 +576,7 @@ function buildGiweawayFromMedia(media: GramJs.TypeMessageMedia): ApiGiveaway | u
function buildGiveaway(media: GramJs.MessageMediaGiveaway): ApiGiveaway | undefined {
const {
channels, months, quantity, untilDate, countriesIso2, onlyNewSubscribers, prizeDescription,
channels, months, stars, quantity, untilDate, countriesIso2, onlyNewSubscribers, prizeDescription,
} = media;
const channelIds = channels.map((channel) => buildApiPeerId(channel, 'channel'));
@ -585,6 +585,7 @@ function buildGiveaway(media: GramJs.MessageMediaGiveaway): ApiGiveaway | undefi
mediaType: 'giveaway',
channelIds,
months,
stars: stars?.toJSNumber(),
quantity,
untilDate,
countries: countriesIso2,

View File

@ -608,6 +608,15 @@ function buildAction(
amount = action.winnersCount;
pluralValue = action.winnersCount;
}
} else if (action instanceof GramJs.MessageActionPrizeStars) {
type = 'prizeStars';
isUnclaimed = Boolean(action.unclaimed);
if (action.boostPeer) {
targetChatId = getApiChatIdFromMtpPeer(action.boostPeer);
}
text = 'Notification.StarsPrize';
stars = action.stars.toJSNumber();
transactionId = action.transactionId;
} else if (action instanceof GramJs.MessageActionBoostApply) {
type = 'chatBoost';
if (action.boosts === 1) {

View File

@ -6,9 +6,20 @@ import type {
ApiBoostsStatus,
ApiCheckedGiftCode,
ApiGiveawayInfo,
ApiInvoice, ApiLabeledPrice, ApiMyBoost, ApiPaymentCredentials,
ApiPaymentForm, ApiPaymentSavedInfo, ApiPremiumGiftCodeOption, ApiPremiumPromo, ApiPremiumSubscriptionOption,
ApiInvoice,
ApiLabeledPrice,
ApiMyBoost,
ApiPaymentCredentials,
ApiPaymentForm,
ApiPaymentSavedInfo,
ApiPremiumGiftCodeOption,
ApiPremiumPromo,
ApiPremiumSubscriptionOption,
ApiPrepaidGiveaway,
ApiPrepaidStarsGiveaway,
ApiReceipt,
ApiStarGiveawayOption,
ApiStarsGiveawayWinnerOption,
ApiStarsTransaction,
ApiStarsTransactionPeer,
ApiStarTopupOption,
@ -20,7 +31,7 @@ import { buildApiMessageEntity } from './common';
import { omitVirtualClassFields } from './helpers';
import { buildApiDocument, buildApiWebDocument, buildMessageMediaContent } from './messageContent';
import { buildApiPeerId, getApiChatIdFromMtpPeer } from './peers';
import { buildPrepaidGiveaway, buildStatisticsPercentage } from './statistics';
import { buildStatisticsPercentage } from './statistics';
export function buildShippingOptions(shippingOptions: GramJs.ShippingOption[] | undefined) {
if (!shippingOptions) {
@ -265,19 +276,46 @@ export function buildApiPaymentCredentials(credentials: GramJs.PaymentSavedCrede
return credentials.map(({ id, title }) => ({ id, title }));
}
export function buildPrepaidGiveaway(
interaction: GramJs.TypePrepaidGiveaway,
): ApiPrepaidGiveaway | ApiPrepaidStarsGiveaway {
if (interaction instanceof GramJs.PrepaidGiveaway) {
return {
type: 'giveaway',
id: interaction.id.toString(),
date: interaction.date,
months: interaction.months,
quantity: interaction.quantity,
};
}
return {
type: 'starsGiveaway',
id: interaction.id.toString(),
stars: interaction.stars.toJSNumber(),
quantity: interaction.quantity,
boosts: interaction.boosts,
date: interaction.date,
};
}
export function buildApiBoostsStatus(boostStatus: GramJs.premium.BoostsStatus): ApiBoostsStatus {
const {
level, boostUrl, boosts, myBoost, currentLevelBoosts, nextLevelBoosts, premiumAudience, prepaidGiveaways,
level, boostUrl, boosts,
giftBoosts, myBoost, currentLevelBoosts, nextLevelBoosts,
premiumAudience, prepaidGiveaways,
} = boostStatus;
return {
level,
currentLevelBoosts,
boosts,
hasMyBoost: Boolean(myBoost),
boostUrl,
giftBoosts,
nextLevelBoosts,
...(premiumAudience && { premiumSubscribers: buildStatisticsPercentage(premiumAudience) }),
...(prepaidGiveaways && { prepaidGiveaways: prepaidGiveaways.map(buildPrepaidGiveaway) }),
...(prepaidGiveaways && { prepaidGiveaways: prepaidGiveaways.map((m) => buildPrepaidGiveaway(m)) }),
};
}
@ -288,6 +326,7 @@ export function buildApiBoost(boost: GramJs.Boost): ApiBoost {
expires,
giveaway,
gift,
stars,
} = boost;
return {
@ -296,6 +335,7 @@ export function buildApiBoost(boost: GramJs.Boost): ApiBoost {
expires,
isFromGiveaway: giveaway,
isGift: gift,
stars: stars?.toJSNumber(),
};
}
@ -342,6 +382,7 @@ export function buildApiGiveawayInfo(info: GramJs.payments.TypeGiveawayInfo): Ap
refunded,
startDate,
winnersCount,
starsPrize,
} = info;
return {
@ -353,6 +394,7 @@ export function buildApiGiveawayInfo(info: GramJs.payments.TypeGiveawayInfo): Ap
giftCodeSlug,
isRefunded: refunded,
isWinner: winner,
starsPrize: starsPrize?.toJSNumber(),
};
}
}
@ -399,6 +441,38 @@ export function buildApiStarsGiftOptions(option: GramJs.StarsGiftOption): ApiSta
};
}
export function buildApiStarsGiveawayWinnersOption(
option: GramJs.StarsGiveawayWinnersOption,
): ApiStarsGiveawayWinnerOption {
const {
default: isDefault, users, perUserStars,
} = option;
return {
isDefault,
users,
perUserStars: perUserStars.toJSNumber(),
};
}
export function buildApiStarsGiveawayOptions(option: GramJs.StarsGiveawayOption): ApiStarGiveawayOption {
const {
extended, default: isDefault, stars, yearlyBoosts, amount, winners, currency,
} = option;
const winnerList = winners?.map((m) => buildApiStarsGiveawayWinnersOption(m)).filter(Boolean);
return {
isExtended: extended,
isDefault,
yearlyBoosts,
stars: stars.toJSNumber(),
amount: amount.toJSNumber(),
currency,
winners: winnerList,
};
}
export function buildApiStarsTransactionPeer(peer: GramJs.TypeStarsTransactionPeer): ApiStarsTransactionPeer {
if (peer instanceof GramJs.StarsTransactionPeerAppStore) {
return { type: 'appStore' };

View File

@ -8,7 +8,7 @@ import type {
ApiPostStatistics,
ApiStoryPublicForward,
ChannelMonetizationBalances,
PrepaidGiveaway, StatisticsGraph,
StatisticsGraph,
StatisticsMessageInteractionCounter,
StatisticsOverviewItem,
StatisticsOverviewPercentage,
@ -233,15 +233,6 @@ export function buildStatisticsPercentage(data: GramJs.StatsPercentValue): Stati
};
}
export function buildPrepaidGiveaway(prepaidGiveaway: GramJs.PrepaidGiveaway): PrepaidGiveaway {
return {
id: prepaidGiveaway.id.toString(),
date: prepaidGiveaway.date,
months: prepaidGiveaway.months,
quantity: prepaidGiveaway.quantity,
};
}
function getOverviewPeriod(data: GramJs.StatsDateRangeDays): StatisticsOverviewPeriod {
return {
maxDate: data.maxDate,

View File

@ -568,6 +568,23 @@ GramJs.TypeInputStorePaymentPurpose {
const randomId = generateRandomBigInt();
if (purpose.type === 'starsgiveaway') {
return new GramJs.InputStorePaymentStarsGiveaway({
boostPeer: buildInputPeer(purpose.chat.id, purpose.chat.accessHash),
additionalPeers: purpose.additionalChannels?.map((chat) => buildInputPeer(chat.id, chat.accessHash)),
stars: BigInt(purpose.stars!),
countriesIso2: purpose.countries,
prizeDescription: purpose.prizeDescription,
onlyNewSubscribers: purpose.isOnlyForNewSubscribers || undefined,
winnersAreVisible: purpose.areWinnersVisible || undefined,
untilDate: purpose.untilDate,
currency: purpose.currency,
amount: BigInt(purpose.amount),
users: purpose.users,
randomId,
});
}
return new GramJs.InputStorePaymentPremiumGiveaway({
boostPeer: buildInputPeer(purpose.chat.id, purpose.chat.accessHash),
additionalPeers: purpose.additionalChannels?.map((chat) => buildInputPeer(chat.id, chat.accessHash)),
@ -613,6 +630,13 @@ export function buildInputInvoice(invoice: ApiRequestInputInvoice) {
});
}
case 'starsgiveaway': {
const purpose = buildInputStorePaymentPurpose(invoice.purpose);
return new GramJs.InputInvoiceStars({
purpose,
});
}
case 'giveaway':
default: {
const purpose = buildInputStorePaymentPurpose(invoice.purpose);

View File

@ -17,7 +17,9 @@ import {
buildApiPaymentForm,
buildApiPremiumGiftCodeOption,
buildApiPremiumPromo,
buildApiReceipt, buildApiStarsGiftOptions,
buildApiReceipt,
buildApiStarsGiftOptions,
buildApiStarsGiveawayOptions,
buildApiStarsTransaction,
buildApiStarTopupOption,
buildShippingOptions,
@ -376,6 +378,16 @@ export async function getStarsGiftOptions({
return result.map(buildApiStarsGiftOptions);
}
export async function fetchStarsGiveawayOptions() {
const result = await invokeRequest(new GramJs.payments.GetStarsGiveawayOptions());
if (!result) {
return undefined;
}
return result.map(buildApiStarsGiveawayOptions);
}
export function launchPrepaidGiveaway({
chat,
giveawayId,

View File

@ -248,8 +248,23 @@ export type ApiInputInvoiceStarsGift = {
amount: number;
};
export type ApiInputInvoiceStarsGiveaway = {
type: 'starsgiveaway';
chatId: string;
additionalChannelIds?: string[];
isOnlyForNewSubscribers?: boolean;
areWinnersVisible?: boolean;
prizeDescription?: string;
countries?: string[];
untilDate: number;
currency: string;
amount: number;
stars: number;
users: number;
};
export type ApiInputInvoice = ApiInputInvoiceMessage | ApiInputInvoiceSlug | ApiInputInvoiceGiveaway
| ApiInputInvoiceGiftCode | ApiInputInvoiceStarsGift | ApiInputInvoiceStars;
| ApiInputInvoiceGiftCode | ApiInputInvoiceStarsGift | ApiInputInvoiceStars | ApiInputInvoiceStarsGiveaway;
/* Used for Invoice request */
export type ApiRequestInputInvoiceMessage = {
@ -274,8 +289,13 @@ export type ApiRequestInputInvoiceStars = {
purpose: ApiInputStorePaymentPurpose;
};
export type ApiRequestInputInvoiceStarsGiveaway = {
type: 'starsgiveaway';
purpose: ApiInputStorePaymentPurpose;
};
export type ApiRequestInputInvoice = ApiRequestInputInvoiceMessage | ApiRequestInputInvoiceSlug
| ApiRequestInputInvoiceGiveaway | ApiRequestInputInvoiceStars;
| ApiRequestInputInvoiceGiveaway | ApiRequestInputInvoiceStars | ApiRequestInputInvoiceStarsGiveaway;
export interface ApiInvoice {
mediaType: 'invoice';
@ -351,7 +371,8 @@ export type ApiGame = {
export type ApiGiveaway = {
mediaType: 'giveaway';
quantity: number;
months: number;
months?: number;
stars?: number;
untilDate: number;
isOnlyForNewSubscribers?: true;
countries?: string[];
@ -361,7 +382,8 @@ export type ApiGiveaway = {
export type ApiGiveawayResults = {
mediaType: 'giveawayResults';
months: number;
months?: number;
stars?: number;
untilDate: number;
isRefunded?: true;
isOnlyForNewSubscribers?: true;
@ -400,6 +422,7 @@ export interface ApiAction {
| 'giftStars'
| 'giftPremium'
| 'giftCode'
| 'prizeStars'
| 'other';
photo?: ApiPhoto;
amount?: number;

View File

@ -211,6 +211,7 @@ export interface ApiAppConfig {
bandwidthPremiumDownloadSpeedup?: number;
channelRestrictAdsLevelMin?: number;
isChannelRevenueWithdrawalEnabled?: boolean;
isStarsGiftsEnabled?: boolean;
}
export interface ApiConfig {

View File

@ -5,7 +5,7 @@ import type { ApiChat } from './chats';
import type {
ApiDocument, ApiMessageEntity, ApiPaymentCredentials, BoughtPaidMedia,
} from './messages';
import type { PrepaidGiveaway, StatisticsOverviewPercentage } from './statistics';
import type { StatisticsOverviewPercentage } from './statistics';
import type { ApiUser } from './users';
export interface ApiShippingAddress {
@ -149,8 +149,23 @@ export type ApiInputStorePaymentStarsGift = {
amount: number;
};
export type ApiInputStorePaymentStarsGiveaway = {
type: 'starsgiveaway';
isOnlyForNewSubscribers?: boolean;
areWinnersVisible?: boolean;
chat: ApiChat;
additionalChannels?: ApiChat[];
stars?: number;
countries?: string[];
prizeDescription?: string;
untilDate: number;
currency: string;
amount: number;
users: number;
};
export type ApiInputStorePaymentPurpose = ApiInputStorePaymentGiveaway | ApiInputStorePaymentGiftcode |
ApiInputStorePaymentStarsTopup | ApiInputStorePaymentStarsGift;
ApiInputStorePaymentStarsTopup | ApiInputStorePaymentStarsGift | ApiInputStorePaymentStarsGiveaway;
export interface ApiPremiumGiftCodeOption {
users: number;
@ -159,6 +174,25 @@ export interface ApiPremiumGiftCodeOption {
amount: number;
}
export interface ApiPrepaidGiveaway {
type: 'giveaway';
id: string;
months: number;
quantity: number;
date: number;
}
export type ApiPrepaidStarsGiveaway = {
type: 'starsGiveaway';
id: string;
stars: number;
quantity: number;
boosts: number;
date: number;
};
export type ApiTypePrepaidGiveaway = ApiPrepaidGiveaway | ApiPrepaidStarsGiveaway;
export type ApiBoostsStatus = {
level: number;
currentLevelBoosts: number;
@ -166,8 +200,9 @@ export type ApiBoostsStatus = {
nextLevelBoosts?: number;
hasMyBoost?: boolean;
boostUrl: string;
giftBoosts?: number;
premiumSubscribers?: StatisticsOverviewPercentage;
prepaidGiveaways?: PrepaidGiveaway[];
prepaidGiveaways?: ApiTypePrepaidGiveaway[];
};
export type ApiMyBoost = {
@ -184,6 +219,7 @@ export type ApiBoost = {
expires: number;
isFromGiveaway?: boolean;
isGift?: boolean;
stars?: number;
};
export type ApiGiveawayInfoActive = {
@ -201,10 +237,11 @@ export type ApiGiveawayInfoResults = {
isWinner?: true;
isRefunded?: true;
startDate: number;
starsPrize?: number;
finishDate: number;
giftCodeSlug?: string;
winnersCount: number;
activatedCount: number;
activatedCount?: number;
};
export type ApiGiveawayInfo = ApiGiveawayInfoActive | ApiGiveawayInfoResults;
@ -219,13 +256,6 @@ export type ApiCheckedGiftCode = {
usedAt?: number;
};
export interface ApiPrepaidGiveaway {
id: string;
months: number;
quantity: number;
date: number;
}
export interface ApiStarsTransactionPeerUnsupported {
type: 'unsupported';
}
@ -271,6 +301,7 @@ export interface ApiStarsTransaction {
stars: number;
isRefund?: true;
isGift?: true;
isPrizeStars?: true;
isMyGift?: true; // Used only for outgoing star gift messages
hasFailed?: true;
isPending?: true;
@ -287,3 +318,19 @@ export interface ApiStarTopupOption {
currency: string;
amount: number;
}
export interface ApiStarsGiveawayWinnerOption {
isDefault?: true;
users: number;
perUserStars: number;
}
export interface ApiStarGiveawayOption {
isExtended?: true;
isDefault?: true;
stars: number;
yearlyBoosts: number;
currency: string;
amount: number;
winners: ApiStarsGiveawayWinnerOption[];
}

View File

@ -1,4 +1,5 @@
import type { ApiChat } from './chats';
import type { ApiTypePrepaidGiveaway } from './payments';
export interface ApiChannelStatistics {
growthGraph?: StatisticsGraph | string;
@ -61,7 +62,7 @@ export interface ApiBoostStatistics {
boosts: number;
premiumSubscribers: StatisticsOverviewPercentage;
remainingBoosts: number;
prepaidGiveaways: PrepaidGiveaway[];
prepaidGiveaways: ApiTypePrepaidGiveaway[];
}
export interface ApiMessagePublicForward {
@ -115,13 +116,6 @@ export interface StatisticsOverviewPercentage {
percentage: string;
}
export interface PrepaidGiveaway {
id: string;
months: number;
quantity: number;
date: number;
}
export interface StatisticsOverviewPeriod {
maxDate: number;
minDate: number;

View File

@ -1283,3 +1283,4 @@
"CreditsBoxHistoryEntryGiftOutAbout" = "With Stars, {user} will be able to unlock content and services on Telegram. {link}"
"CreditsBoxOutAbout" = "Review the {link} for Stars."
"GiftStarsOutgoing" = "With Stars, {user} will be able to unlock content and services on Telegram."
"PrizeCredits" = "Your prize is {count} Stars."

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 504 KiB

View File

@ -80,7 +80,7 @@ const FullNameTitle: FC<OwnProps> = ({
const specialTitle = useMemo(() => {
if (customPeer) {
return lang(customPeer.titleKey);
return lang(customPeer.titleKey, customPeer.titleValue, 'i');
}
if (isSavedMessages) {

View File

@ -91,6 +91,11 @@ export function renderActionMessageText(
.replace('un2', '%action_origin%')
.replace(/\*\*/g, '');
}
if (translationKey === 'BoostingReceivedPrizeFrom') {
unprocessed = unprocessed
.replace('**%s**', '%target_chat%')
.replace(/\*\*/g, '');
}
let processed: TextPart[];
if (unprocessed.includes('%star_target_user%')) {

View File

@ -569,10 +569,10 @@ const Main = ({
/>
<AttachBotRecipientPicker requestedAttachBotInChat={requestedAttachBotInChat} />
<MessageListHistoryHandler />
{isPremiumModalOpen && <PremiumMainModal isOpen={isPremiumModalOpen} />}
{isGiveawayModalOpen && <GiveawayModal isOpen={isGiveawayModalOpen} />}
{isPremiumGiftingPickerModal && <PremiumGiftingPickerModal isOpen={isPremiumGiftingPickerModal} />}
{isStarsGiftingPickerModal && <StarsGiftingPickerModal isOpen={isStarsGiftingPickerModal} />}
<PremiumMainModal isOpen={isPremiumModalOpen} />
<GiveawayModal isOpen={isGiveawayModalOpen} />
<PremiumGiftingPickerModal isOpen={isPremiumGiftingPickerModal} />
<StarsGiftingPickerModal isOpen={isStarsGiftingPickerModal} />
<PremiumLimitReachedModal limit={limitReached} />
<PaymentModal isOpen={isPaymentModalOpen} onClose={closePaymentModal} />
<ReceiptModal isOpen={isReceiptModalOpen} onClose={clearReceipt} />

View File

@ -114,6 +114,7 @@
.options {
width: 100%;
padding: 0 0.25rem;
}
.giveawayTitle {
@ -133,6 +134,10 @@
@include mixins.adapt-margin-to-scrollbar(1rem);
}
.starSubscription {
margin-top: 0;
}
.subscriptionOption {
margin-bottom: 0;
padding-inline: 3.5rem 1rem;
@ -142,10 +147,10 @@
.status {
display: flex;
align-items: center;
gap: 1rem;
gap: 0.8125rem;
justify-content: space-between;
width: 100%;
padding: 0 0.375rem 0 1.125rem;
padding: 0 0.375rem 0 1rem;
margin-bottom: 1.3125rem;
}
@ -263,7 +268,8 @@
}
.addChannel {
margin-inline-start: initial !important;
margin-inline-end: 1.375rem !important;
margin-inline-start: 0.3125rem !important;
}
.removeChannel {
@ -272,6 +278,10 @@
padding: 0.5rem;
}
.starOptions {
padding: 0.8125rem;
}
@media (max-width: 600px) {
.root :global(.modal-dialog) {
width: 100%;

View File

@ -6,7 +6,13 @@ import React, {
import { getActions, getGlobal, withGlobal } from '../../../global';
import type {
ApiCountry, ApiPremiumGiftCodeOption, ApiPrepaidGiveaway, ApiUser,
ApiCountry,
ApiPremiumGiftCodeOption,
ApiPrepaidGiveaway,
ApiPrepaidStarsGiveaway,
ApiStarGiveawayOption,
ApiTypePrepaidGiveaway,
ApiUser,
} from '../../../api/types';
import {
@ -22,6 +28,7 @@ import {
} from '../../../global/selectors';
import buildClassName from '../../../util/buildClassName';
import { formatDateTimeToString } from '../../../util/dates/dateFormat';
import { unique } from '../../../util/iteratees';
import renderText from '../../common/helpers/renderText';
import useFlag from '../../../hooks/useFlag';
@ -32,6 +39,7 @@ import CalendarModal from '../../common/CalendarModal';
import CountryPickerModal from '../../common/CountryPickerModal';
import GroupChatInfo from '../../common/GroupChatInfo';
import Icon from '../../common/icons/Icon';
import StarTopupOptionList from '../../modals/stars/StarTopupOptionList';
import Button from '../../ui/Button';
import ConfirmDialog from '../../ui/ConfirmDialog';
import InputText from '../../ui/InputText';
@ -51,7 +59,7 @@ import styles from './GiveawayModal.module.scss';
import GiftBlueRound from '../../../assets/premium/GiftBlueRound.svg';
import GiftGreenRound from '../../../assets/premium/GiftGreenRound.svg';
import GiftRedRound from '../../../assets/premium/GiftRedRound.svg';
import GiveawayUsersRound from '../../../assets/premium/GiveawayUsersRound.svg';
import GiftStar from '../../../assets/premium/GiftStar.svg';
import PremiumLogo from '../../../assets/premium/PremiumLogo.svg';
export type OwnProps = {
@ -69,13 +77,15 @@ type StateProps = {
giveawayBoostPerPremiumLimit?: number;
userSelectionLimit?: number;
countryList: ApiCountry[];
prepaidGiveaway?: ApiPrepaidGiveaway;
prepaidGiveaway?: ApiTypePrepaidGiveaway;
countrySelectionLimit: number | undefined;
isChannel?: boolean;
isStarsGiftsEnabled?: boolean;
starsGiftOptions?: ApiStarGiveawayOption[] | undefined;
};
type GiveawayAction = 'createRandomlyUsers' | 'createSpecificUsers';
type ApiGiveawayType = 'random_users' | 'specific_users';
type GiveawayAction = 'createPremiumGiveaway' | 'createStarsGiveaway';
type ApiGiveawayType = 'premium_giveaway' | 'stars_giveaway';
type SubscribersType = 'all' | 'new';
interface TypeOption {
@ -110,12 +120,14 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
prepaidGiveaway,
countrySelectionLimit = GIVEAWAY_MAX_ADDITIONAL_COUNTRIES,
userSelectionLimit = GIVEAWAY_MAX_ADDITIONAL_USERS,
isStarsGiftsEnabled,
starsGiftOptions,
}) => {
// eslint-disable-next-line no-null/no-null
const dialogRef = useRef<HTMLDivElement>(null);
const {
closeGiveawayModal, openInvoice, openPremiumModal,
launchPrepaidGiveaway,
launchPrepaidGiveaway, launchPrepaidStarsGiveaway,
} = getActions();
const lang = useOldLang();
@ -126,28 +138,33 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
const [isChannelPickerModalOpen, openChannelPickerModal, closeChannelPickerModal] = useFlag();
const TYPE_OPTIONS: TypeOption[] = [{
name: 'BoostingCreateGiveaway',
name: 'Premium.Title',
text: 'BoostingWinnersRandomly',
value: 'random_users',
value: 'premium_giveaway',
img: GiftBlueRound,
actions: 'createRandomlyUsers',
isLink: false,
}, {
name: 'BoostingAwardSpecificUsers',
text: 'BoostingSelectRecipients',
value: 'specific_users',
img: GiveawayUsersRound,
actions: 'createSpecificUsers',
actions: 'createPremiumGiveaway',
isLink: true,
onClickAction: () => {
openUserPickerModal();
},
}];
if (isStarsGiftsEnabled) {
TYPE_OPTIONS.push({
name: 'TelegramStars',
text: 'BoostingWinnersRandomly',
value: 'stars_giveaway',
img: GiftStar,
actions: 'createStarsGiveaway',
isLink: false,
});
}
const [customExpireDate, setCustomExpireDate] = useState<number>(Date.now() + DEFAULT_CUSTOM_EXPIRE_DATE);
const [isHeaderHidden, setHeaderHidden] = useState(true);
const [selectedRandomUserCount, setSelectedRandomUserCount] = useState<number>(DEFAULT_BOOST_COUNT);
const [selectedGiveawayOption, setGiveawayOption] = useState<ApiGiveawayType>(TYPE_OPTIONS[0].value);
const [selectedStarOption, setSelectedStarOption] = useState<ApiStarGiveawayOption | undefined>();
const [selectedSubscriberOption, setSelectedSubscriberOption] = useState<SubscribersType>('all');
const [selectedMonthOption, setSelectedMonthOption] = useState<number | undefined>();
const [selectedUserIds, setSelectedUserIds] = useState<string[]>([]);
@ -157,10 +174,16 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
const [shouldShowPrizes, setShouldShowPrizes] = useState<boolean>(false);
const [prizeDescription, setPrizeDescription] = useState<string | undefined>(undefined);
const [dataPrepaidGiveaway, setDataPrepaidGiveaway] = useState<ApiPrepaidGiveaway | undefined>(undefined);
const [
dataStarsPrepaidGiveaway, setDataStarsPrepaidGiveaway,
] = useState<ApiPrepaidStarsGiveaway | undefined>(undefined);
const isRandomUsers = selectedGiveawayOption === 'random_users';
const selectedUserCount = isRandomUsers ? selectedRandomUserCount : selectedUserIds.length;
const isPremiumGiveaway = selectedGiveawayOption === 'premium_giveaway';
const isStarsGiveaway = selectedGiveawayOption === 'stars_giveaway';
const selectedUserCount = isPremiumGiveaway
&& !selectedUserIds.length ? selectedRandomUserCount : selectedUserIds.length;
const boostQuantity = selectedUserCount * giveawayBoostPerPremiumLimit;
const boostStarsQuantity = selectedStarOption?.yearlyBoosts;
const SUBSCRIBER_OPTIONS = useMemo(() => [
{
@ -180,44 +203,65 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
], [isChannel, lang, selectedCountryIds]);
const monthQuantity = lang('Months', selectedMonthOption);
const isStarsPrepaidGiveaway = prepaidGiveaway?.type === 'starsGiveaway';
const isPremiumPrepaidGiveaway = prepaidGiveaway?.type === 'giveaway';
const selectedGift = useMemo(() => {
return gifts!.find((gift) => gift.months === selectedMonthOption && gift.users === selectedUserCount);
return gifts?.find((gift) => gift.months === selectedMonthOption && gift.users === selectedUserCount);
}, [gifts, selectedMonthOption, selectedUserCount]);
const selectedStarsGift = useMemo(() => {
return starsGiftOptions?.find((gift) => {
return isStarsPrepaidGiveaway && gift.stars === (dataStarsPrepaidGiveaway?.stars);
});
}, [dataStarsPrepaidGiveaway, starsGiftOptions, isStarsPrepaidGiveaway]);
const filteredGifts = useMemo(() => {
return gifts?.filter((gift) => gift.users === selectedUserCount);
}, [gifts, selectedUserCount]);
const fullMonthlyAmount = useMemo(() => {
if (!filteredGifts?.length) {
return undefined;
}
const basicGift = filteredGifts.reduce((acc, gift) => {
const basicGift = filteredGifts?.reduce((acc, gift) => {
return gift.amount < acc.amount ? gift : acc;
});
}, filteredGifts[0]);
return Math.floor(basicGift.amount / basicGift.months);
return basicGift && Math.floor(basicGift.amount / basicGift.months);
}, [filteredGifts]);
const userCountOptions = useMemo(() => {
const uniqueUserCounts = new Set(gifts?.map((gift) => gift.users));
return Array.from(uniqueUserCounts).sort((a, b) => a - b);
return unique((gifts?.map((winner) => winner.users) || [])).sort((a, b) => a - b);
}, [gifts]);
useEffect(() => {
if (isOpen) {
setSelectedMonthOption(prepaidGiveaway ? prepaidGiveaway.months : gifts?.[0].months);
}
}, [gifts, isOpen, prepaidGiveaway]);
const winnerCountOptions = useMemo(() => {
return unique((selectedStarOption?.winners?.map((winner) => winner.users) || [])).sort((a, b) => a - b);
}, [selectedStarOption]);
useEffect(() => {
if (prepaidGiveaway) {
if (isOpen && gifts?.length && !isStarsPrepaidGiveaway) {
setSelectedMonthOption(gifts?.[0].months);
}
}, [isOpen, gifts, isStarsPrepaidGiveaway]);
useEffect(() => {
if (isOpen && starsGiftOptions?.length && !isPremiumPrepaidGiveaway) {
setSelectedStarOption(starsGiftOptions?.[0]);
}
}, [isOpen, starsGiftOptions, isPremiumPrepaidGiveaway]);
useEffect(() => {
if (isOpen && isStarsPrepaidGiveaway) {
setSelectedRandomUserCount(prepaidGiveaway.quantity);
setDataStarsPrepaidGiveaway(prepaidGiveaway);
}
}, [isOpen, isStarsPrepaidGiveaway, prepaidGiveaway]);
useEffect(() => {
if (isOpen && isPremiumPrepaidGiveaway) {
setSelectedRandomUserCount(prepaidGiveaway.quantity);
setDataPrepaidGiveaway(prepaidGiveaway);
setSelectedMonthOption(prepaidGiveaway.months);
}
}, [prepaidGiveaway]);
}, [isOpen, isPremiumPrepaidGiveaway, prepaidGiveaway]);
useEffect(() => {
if (selectedMemberList) {
@ -235,10 +279,44 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
openPremiumModal();
});
const handleClose = useLastCallback(() => {
setDataStarsPrepaidGiveaway(undefined);
setDataPrepaidGiveaway(undefined);
setSelectedStarOption(undefined);
setSelectedMonthOption(undefined);
setSelectedRandomUserCount(DEFAULT_BOOST_COUNT);
closeGiveawayModal();
});
const handleClick = useLastCallback(() => {
if (isRandomUsers) {
if (isPremiumGiveaway) {
if (selectedUserIds?.length) {
openInvoice({
type: 'giftcode',
boostChannelId: chatId!,
userIds: selectedUserIds,
currency: selectedGift!.currency,
amount: selectedGift!.amount,
option: selectedGift!,
});
} else {
openInvoice({
type: 'giveaway',
chatId: chatId!,
additionalChannelIds: selectedChannelIds,
isOnlyForNewSubscribers: selectedSubscriberOption === 'new',
countries: selectedCountryIds,
areWinnersVisible: shouldShowWinners,
prizeDescription,
untilDate: customExpireDate / 1000,
currency: selectedGift!.currency,
amount: selectedGift!.amount,
option: selectedGift!,
});
}
} else {
openInvoice({
type: 'giveaway',
type: 'starsgiveaway',
chatId: chatId!,
additionalChannelIds: selectedChannelIds,
isOnlyForNewSubscribers: selectedSubscriberOption === 'new',
@ -246,47 +324,61 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
areWinnersVisible: shouldShowWinners,
prizeDescription,
untilDate: customExpireDate / 1000,
currency: selectedGift!.currency,
amount: selectedGift!.amount,
option: selectedGift!,
});
} else {
openInvoice({
type: 'giftcode',
boostChannelId: chatId!,
userIds: selectedUserIds,
currency: selectedGift!.currency,
amount: selectedGift!.amount,
option: selectedGift!,
currency: selectedStarOption!.currency,
amount: selectedStarOption!.amount,
stars: selectedStarOption!.stars,
users: selectedRandomUserCount,
});
}
closeGiveawayModal();
handleClose();
});
const confirmLaunchPrepaidGiveaway = useLastCallback(() => {
launchPrepaidGiveaway({
chatId: chatId!,
giveawayId: dataPrepaidGiveaway!.id,
paymentPurpose: {
additionalChannelIds: selectedChannelIds,
countries: selectedCountryIds,
prizeDescription,
areWinnersVisible: shouldShowWinners,
untilDate: customExpireDate / 1000,
currency: selectedGift!.currency,
amount: selectedGift!.amount,
},
});
if (isStarsPrepaidGiveaway) {
launchPrepaidStarsGiveaway({
chatId: chatId!,
giveawayId: dataStarsPrepaidGiveaway!.id,
paymentPurpose: {
additionalChannelIds: selectedChannelIds,
countries: selectedCountryIds,
prizeDescription,
areWinnersVisible: shouldShowWinners,
untilDate: customExpireDate / 1000,
stars: dataStarsPrepaidGiveaway!.stars,
currency: selectedStarsGift!.currency,
amount: selectedStarsGift!.amount,
users: dataStarsPrepaidGiveaway!.quantity,
},
});
} else {
launchPrepaidGiveaway({
chatId: chatId!,
giveawayId: dataPrepaidGiveaway!.id,
paymentPurpose: {
additionalChannelIds: selectedChannelIds,
countries: selectedCountryIds,
prizeDescription,
areWinnersVisible: shouldShowWinners,
untilDate: customExpireDate / 1000,
currency: selectedGift!.currency,
amount: selectedGift!.amount,
},
});
}
closeConfirmModal();
closeGiveawayModal();
handleClose();
});
const handleRandomUserCountChange = useLastCallback((newValue) => {
setSelectedRandomUserCount(newValue);
});
const handleWinnerCountChange = useLastCallback((newValue) => {
setSelectedRandomUserCount(newValue);
});
const handlePrizeDescriptionChange = useLastCallback((e: ChangeEvent<HTMLInputElement>) => {
setPrizeDescription(e.target.value);
});
@ -309,6 +401,7 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
const handleChangeTypeOption = useLastCallback((value: ApiGiveawayType) => {
setGiveawayOption(value);
setSelectedUserIds([]);
setSelectedRandomUserCount(DEFAULT_BOOST_COUNT);
});
const handleExpireDateChange = useLastCallback((date: Date) => {
@ -323,7 +416,7 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
const handleSelectedUserIdsChange = useLastCallback((newSelectedIds: string[]) => {
setSelectedUserIds(newSelectedIds);
if (!newSelectedIds.length) {
setGiveawayOption('random_users');
setGiveawayOption('premium_giveaway');
}
});
@ -331,10 +424,6 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
setSelectedChannelIds(newSelectedIds);
});
const handleClose = useLastCallback(() => {
closeGiveawayModal();
});
const handleShouldShowWinnersChange = useLastCallback((e: ChangeEvent<HTMLInputElement>) => {
setShouldShowWinners(e.target.checked);
});
@ -347,7 +436,9 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
openCountryPickerModal();
});
if (!gifts) return undefined;
const handleStarClick = useLastCallback((option) => {
setSelectedStarOption(option);
});
function renderTypeOptions() {
return (
@ -428,12 +519,177 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
setSelectedChannelIds(filteredChannelIds);
}
function renderStarOptionList() {
return (
<StarTopupOptionList
className={styles.starOptions}
options={starsGiftOptions}
selectedStarCount={selectedRandomUserCount}
selectedStarOption={selectedStarOption}
onClick={handleStarClick}
/>
);
}
function renderGiveawayOptionList() {
return (
<>
<div className={styles.section}>
<h2 className={styles.giveawayTitle}>
{lang('BoostingChannelsGroupsIncludedGiveaway')}
</h2>
<ListItem
inactive
className="chat-item-clickable contact-list-item"
>
<GroupChatInfo
chatId={chatId!}
status={lang(isChannel ? 'BoostingChannelWillReceiveBoost'
: 'BoostingGroupWillReceiveBoost', boostQuantity || boostStarsQuantity, 'i')}
/>
</ListItem>
{selectedChannelIds?.map((channelId) => {
return (
<ListItem
ripple
key={channelId}
className="chat-item-clickable contact-list-item"
/* eslint-disable-next-line react/jsx-no-bind */
onClick={() => deleteParticipantsHandler(channelId)}
rightElement={(<Icon name="close" className={styles.removeChannel} />)}
>
<GroupChatInfo
chatId={channelId.toString()}
/>
</ListItem>
);
})}
{selectedChannelIds.length < MAX_ADDITIONAL_CHANNELS && (
<ListItem
icon="add"
ripple
onClick={openChannelPickerModal}
className={styles.addButton}
iconClassName={styles.addChannel}
>
{lang('BoostingAddChannelOrGroup')}
</ListItem>
)}
</div>
<div className={styles.section}>
<h2 className={styles.giveawayTitle}>
{lang('BoostingEligibleUsers')}
</h2>
{renderSubscribersOptions()}
</div>
<div className={styles.subscription}>
{renderText(lang(isChannel ? 'BoostGift.LimitSubscribersInfo' : 'lng_giveaway_users_about_group'))}
</div>
<div className={styles.section}>
<div className={styles.checkboxSection}>
<h2 className={styles.title}>
{lang('BoostingGiveawayAdditionalPrizes')}
</h2>
<Switcher
label={lang('BoostingGiveawayAdditionalPrizes')}
checked={shouldShowPrizes}
onChange={handleShouldShowPrizesChange}
/>
</div>
{shouldShowPrizes && (
<div className={styles.prizesSection}>
<h2 className={styles.title}>
{selectedRandomUserCount}
</h2>
<InputText
className={styles.prizesInput}
value={prizeDescription}
onChange={handlePrizeDescriptionChange}
label={lang('BoostingGiveawayEnterYourPrize')}
/>
</div>
)}
</div>
{shouldShowPrizes ? (
!isStarsGiveaway && !isStarsPrepaidGiveaway ? (
<div className={styles.subscription}>
{prizeDescription?.length ? renderText(lang('BoostingGiveawayAdditionPrizeCountNameHint',
dataPrepaidGiveaway
? [dataPrepaidGiveaway.quantity, prizeDescription, monthQuantity]
: [selectedUserCount, prizeDescription, monthQuantity],
undefined,
selectedMonthOption), ['simple_markdown']) : renderText(lang('BoostingGiveawayAdditionPrizeCountHint',
dataPrepaidGiveaway
? [dataPrepaidGiveaway.quantity, monthQuantity]
: [selectedUserCount, monthQuantity],
undefined,
selectedMonthOption), ['simple_markdown'])}
</div>
) : undefined
) : (
<div className={styles.subscription}>
{renderText(lang('BoostingGiveawayAdditionPrizeHint'))}
</div>
)}
<div className={styles.section}>
<div className={styles.checkboxSection}>
<h2 className={styles.title}>
{lang('BoostingGiveawayShowWinners')}
</h2>
<Switcher
label={lang('BoostingGiveawayAdditionalPrizes')}
checked={shouldShowWinners}
onChange={handleShouldShowWinnersChange}
/>
</div>
</div>
<div className={styles.subscription}>
{renderText(lang('BoostingGiveawayShowWinnersHint'))}
</div>
<div className={buildClassName(styles.section,
(dataPrepaidGiveaway || dataStarsPrepaidGiveaway || isStarsGiveaway) && styles.subscriptionFooter)}
>
<h2 className={styles.giveawayTitle}>
{lang('BoostingDateWhenGiveawayEnds')}
</h2>
<Button
ariaLabel={lang('BoostGift.DateEnds')}
className={buildClassName(styles.dateButton, 'expire-limit')}
isText
onClick={openCalendar}
>
<h3 className={styles.title}>
{lang('BoostGift.DateEnds')}
</h3>
{formatDateTimeToString(customExpireDate, lang.code)}
</Button>
</div>
</>
);
}
return (
<Modal
className={styles.root}
onClose={handleClose}
isOpen={isOpen}
dialogRef={dialogRef}
onEnter={(dataPrepaidGiveaway || dataStarsPrepaidGiveaway) ? openConfirmModal : handleClick}
>
<div className={styles.main} onScroll={handleScroll}>
<Button
@ -458,22 +714,31 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
{lang('BoostingBoostsViaGifts')}
</h2>
</div>
{dataPrepaidGiveaway ? (
{(dataPrepaidGiveaway || dataStarsPrepaidGiveaway) ? (
<div className={styles.status}>
<div>
<img className={styles.prepaidImg} src={GIVEAWAY_IMG_LIST[dataPrepaidGiveaway.months]} alt="" />
{dataStarsPrepaidGiveaway ? (
<img className={styles.prepaidImg} src={GiftStar} alt="" />
) : (
<img className={styles.prepaidImg} src={GIVEAWAY_IMG_LIST[dataPrepaidGiveaway!.months]} alt="" />
)}
</div>
<div className={styles.info}>
<h3 className={styles.title}>
{lang('BoostingTelegramPremiumCountPlural', dataPrepaidGiveaway.quantity)}
{dataStarsPrepaidGiveaway ? lang('Giveaway.Stars.Prepaid.Title', dataStarsPrepaidGiveaway?.stars)
: lang('BoostingTelegramPremiumCountPlural', dataPrepaidGiveaway!.quantity)}
</h3>
<p className={styles.month}>{lang('PrepaidGiveawayMonths', dataPrepaidGiveaway.months)}</p>
<p className={styles.month}>
{dataStarsPrepaidGiveaway ? lang('Giveaway.Stars.Prepaid.Desc', dataStarsPrepaidGiveaway?.quantity)
: lang('PrepaidGiveawayMonths', dataPrepaidGiveaway?.months)}
</p>
</div>
<div className={styles.quantity}>
<div className={buildClassName(styles.floatingBadge, styles.floatingBadgeColor)}>
<Icon name="boost" className={styles.floatingBadgeIcon} />
<div className={styles.floatingBadgeValue} dir={lang.isRtl ? 'rtl' : undefined}>
{dataPrepaidGiveaway.quantity * (giveawayBoostPerPremiumLimit ?? GIVEAWAY_BOOST_PER_PREMIUM)}
{dataStarsPrepaidGiveaway ? dataStarsPrepaidGiveaway?.boosts
: dataPrepaidGiveaway!.quantity * (giveawayBoostPerPremiumLimit ?? GIVEAWAY_BOOST_PER_PREMIUM)}
</div>
</div>
</div>
@ -484,9 +749,9 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
</div>
)}
{isRandomUsers && (
{isPremiumGiveaway && !selectedUserIds?.length && (
<>
{!dataPrepaidGiveaway && (
{!dataPrepaidGiveaway && !dataStarsPrepaidGiveaway && (
<>
<div className={styles.section}>
<div className={styles.quantity}>
@ -514,151 +779,57 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
</>
)}
<div className={styles.section}>
<h2 className={styles.giveawayTitle}>
{lang('BoostingChannelsIncludedGiveaway')}
</h2>
<ListItem
inactive
className="chat-item-clickable contact-list-item"
>
<GroupChatInfo
chatId={chatId!}
status={lang(isChannel ? 'BoostingChannelWillReceiveBoost'
: 'BoostingGroupWillReceiveBoost', boostQuantity, 'i')}
/>
</ListItem>
{selectedChannelIds?.map((channelId) => {
return (
<ListItem
ripple
key={channelId}
className="chat-item-clickable contact-list-item"
/* eslint-disable-next-line react/jsx-no-bind */
onClick={() => deleteParticipantsHandler(channelId)}
rightElement={(<Icon name="close" className={styles.removeChannel} />)}
>
<GroupChatInfo
chatId={channelId.toString()}
/>
</ListItem>
);
})}
{selectedChannelIds.length < MAX_ADDITIONAL_CHANNELS && (
<ListItem
icon="add"
ripple
onClick={openChannelPickerModal}
className={styles.addButton}
iconClassName={styles.addChannel}
>
{lang('BoostingAddChannelOrGroup')}
</ListItem>
)}
</div>
<div className={styles.section}>
<h2 className={styles.giveawayTitle}>
{lang('BoostingEligibleUsers')}
</h2>
{renderSubscribersOptions()}
</div>
<div className={styles.subscription}>
{renderText(lang(isChannel ? 'BoostGift.LimitSubscribersInfo' : 'lng_giveaway_users_about_group'))}
</div>
<div className={styles.section}>
<div className={styles.checkboxSection}>
<h2 className={styles.title}>
{lang('BoostingGiveawayAdditionalPrizes')}
</h2>
<Switcher
label={lang('BoostingGiveawayAdditionalPrizes')}
checked={shouldShowPrizes}
onChange={handleShouldShowPrizesChange}
/>
</div>
{shouldShowPrizes && (
<div className={styles.prizesSection}>
<h2 className={styles.title}>
{dataPrepaidGiveaway ? dataPrepaidGiveaway.quantity : selectedUserCount}
</h2>
<InputText
className={styles.prizesInput}
value={prizeDescription}
onChange={handlePrizeDescriptionChange}
label={lang('BoostingGiveawayEnterYourPrize')}
/>
</div>
)}
</div>
{shouldShowPrizes ? (
<div className={styles.subscription}>
{prizeDescription?.length ? renderText(lang('BoostingGiveawayAdditionPrizeCountNameHint',
dataPrepaidGiveaway
? [dataPrepaidGiveaway.quantity, prizeDescription, monthQuantity]
: [selectedUserCount, prizeDescription, monthQuantity],
undefined,
selectedMonthOption), ['simple_markdown']) : renderText(lang('BoostingGiveawayAdditionPrizeCountHint',
dataPrepaidGiveaway
? [dataPrepaidGiveaway.quantity, monthQuantity]
: [selectedUserCount, monthQuantity],
undefined,
selectedMonthOption), ['simple_markdown'])}
</div>
) : (
<div className={styles.subscription}>
{renderText(lang('BoostingGiveawayAdditionPrizeHint'))}
</div>
)}
<div className={styles.section}>
<div className={styles.checkboxSection}>
<h2 className={styles.title}>
{lang('BoostingGiveawayShowWinners')}
</h2>
<Switcher
label={lang('BoostingGiveawayAdditionalPrizes')}
checked={shouldShowWinners}
onChange={handleShouldShowWinnersChange}
/>
</div>
</div>
<div className={styles.subscription}>
{renderText(lang('BoostingGiveawayShowWinnersHint'))}
</div>
<div className={buildClassName(styles.section, dataPrepaidGiveaway && styles.subscriptionFooter)}>
<h2 className={styles.giveawayTitle}>
{lang('BoostingDateWhenGiveawayEnds')}
</h2>
<Button
ariaLabel={lang('BoostGift.DateEnds')}
className={buildClassName(styles.dateButton, 'expire-limit')}
isText
onClick={openCalendar}
>
<h3 className={styles.title}>
{lang('BoostGift.DateEnds')}
</h3>
{formatDateTimeToString(customExpireDate, lang.code)}
</Button>
</div>
{renderGiveawayOptionList()}
</>
)}
{!dataPrepaidGiveaway && (
{isStarsGiveaway && (
<>
{!dataStarsPrepaidGiveaway && !dataPrepaidGiveaway && (
<>
<div className={styles.section}>
<div className={styles.quantity}>
<h2 className={styles.giveawayTitle}>
{lang('BoostingStarsOptions')}
</h2>
<div className={buildClassName(styles.floatingBadge, styles.floatingBadgeColor)}>
<Icon name="boost" className={styles.floatingBadgeIcon} />
<div className={styles.floatingBadgeValue} dir={lang.isRtl ? 'rtl' : undefined}>
{boostStarsQuantity}
</div>
</div>
</div>
{renderStarOptionList()}
</div>
<div className={buildClassName(styles.subscription, styles.starSubscription)}>
{renderText(lang('BoostGift.Stars.Info'))}
</div>
<div className={styles.section}>
<h2 className={styles.giveawayTitle}>
{lang('BoostingStarsQuantityPrizes')}
</h2>
<RangeSliderWithMarks
rangeCount={selectedRandomUserCount}
marks={winnerCountOptions}
onChange={handleWinnerCountChange}
/>
<div className={styles.subscription}>
{renderText(lang('BoostingStarsQuantityPrizesInfo'))}
</div>
</div>
</>
)}
{renderGiveawayOptionList()}
</>
)}
{!dataPrepaidGiveaway && !dataStarsPrepaidGiveaway && isPremiumGiveaway && (
<>
<div className={styles.section}>
<h2 className={styles.giveawayTitle}>
@ -676,16 +847,11 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
{selectedGiveawayOption && (
<div className={styles.footer}>
<Button className={styles.button} onClick={dataPrepaidGiveaway ? openConfirmModal : handleClick}>
<Button
className={styles.button}
onClick={(dataPrepaidGiveaway || dataStarsPrepaidGiveaway) ? openConfirmModal : handleClick}
>
{lang('BoostingStartGiveaway')}
<div className={styles.quantity}>
<div className={buildClassName(styles.floatingBadge, styles.floatingBadgeButtonColor)}>
<Icon name="boost" className={styles.floatingBadgeIcon} />
<div className={styles.floatingBadgeValue} dir={lang.isRtl ? 'rtl' : undefined}>
{dataPrepaidGiveaway ? dataPrepaidGiveaway.quantity : boostQuantity}
</div>
</div>
</div>
</Button>
</div>
)}
@ -748,10 +914,12 @@ export default memo(withGlobal<OwnProps>((global): StateProps => {
selectedMemberList: giveawayModal?.selectedMemberIds,
selectedChannelList: giveawayModal?.selectedChannelIds,
giveawayBoostPerPremiumLimit: global.appConfig?.giveawayBoostsPerPremium,
isStarsGiftsEnabled: global.appConfig?.isStarsGiftsEnabled,
userSelectionLimit: global.appConfig?.giveawayAddPeersMax,
countrySelectionLimit: global.appConfig?.giveawayCountriesMax,
countryList: global.countryList.general,
prepaidGiveaway: giveawayModal?.prepaidGiveaway,
isChannel,
starsGiftOptions: giveawayModal?.starOptions,
};
})(GiveawayModal));

View File

@ -1,7 +1,7 @@
.wrapper {
position: relative;
display: block;
padding-inline: 3.8125rem 1rem;
padding-inline: 3.5rem 1rem;
border: none;
margin-bottom: 0;

View File

@ -11,7 +11,7 @@ import Icon from '../../common/icons/Icon';
import styles from './GiveawayTypeOption.module.scss';
type ApiGiveawayType = 'random_users' | 'specific_users';
type ApiGiveawayType = 'premium_giveaway' | 'stars_giveaway';
type OwnProps = {
option: ApiGiveawayType;
@ -54,7 +54,6 @@ const GiveawayTypeOption: FC<OwnProps> = ({
<label
className={buildClassName(styles.wrapper, className)}
dir={lang.isRtl ? 'rtl' : undefined}
onClick={handleClick}
role="button"
tabIndex={0}
>
@ -74,7 +73,7 @@ const GiveawayTypeOption: FC<OwnProps> = ({
{lang(`${name}`)}
</h3>
{isLink ? (
<div className={styles.link}>
<div className={styles.link} onClick={handleClick}>
<span>{displayText}</span>
<Icon name="next" />
</div>

View File

@ -15,7 +15,6 @@
}
.root :global(.modal-dialog) {
height: min(calc(55vh + 41px + 193px), 90vh);
max-width: 26.25rem;
}
@ -119,5 +118,5 @@
}
.footer {
margin: 0 1.5rem;
margin: 0 1.5rem 1rem;
}

View File

@ -15,7 +15,7 @@
.giveawayWrapper {
position: relative;
display: block;
padding-inline: 3.8125rem 1rem;
padding-inline: 3.5rem 1rem;
cursor: var(--custom-cursor, pointer);

View File

@ -4,10 +4,6 @@
}
}
.root {
z-index: calc(var(--z-modal-low-priority) + 1);
}
.root :global(.modal-content) {
padding: 0;
}
@ -15,11 +11,6 @@
.root :global(.modal-dialog) {
height: min(calc(55vh + 41px + 193px), 90vh);
max-width: 26.25rem;
}
.root :global(.modal-dialog),
.root :global(.modal-content),
.transition {
overflow: hidden;
}
@ -28,6 +19,7 @@
overflow-y: scroll;
display: flex;
flex-direction: column;
align-items: center;
}
.headerInfo {

View File

@ -116,6 +116,10 @@ const StarsGiftModal: FC<OwnProps & StateProps> = ({
setHeaderHidden(scrollTop <= 150);
}
const handleClose = useLastCallback(() => {
closeStarsGiftModal();
});
function renderGiftTitle() {
if (isCompleted) {
return user ? renderText(oldLang('Notification.StarsGift.SentYou',
@ -149,12 +153,12 @@ const StarsGiftModal: FC<OwnProps & StateProps> = ({
return (
<Modal
dialogRef={dialogRef}
onClose={closeStarsGiftModal}
isOpen={isOpen}
className={buildClassName(styles.modalDialog, styles.root)}
dialogRef={dialogRef}
onClose={handleClose}
isOpen={isOpen}
>
<div className={buildClassName(styles.main, 'custom-scroll')} onScroll={handleScroll}>
<div className={styles.main} onScroll={handleScroll}>
<Button
round
size="smaller"

View File

@ -35,6 +35,7 @@ import useContextMenuHandlers from '../../hooks/useContextMenuHandlers';
import useEnsureMessage from '../../hooks/useEnsureMessage';
import useFlag from '../../hooks/useFlag';
import { useIsIntersecting, useOnIntersect } from '../../hooks/useIntersectionObserver';
import useLang from '../../hooks/useLang';
import useOldLang from '../../hooks/useOldLang';
import useShowTransitionDeprecated from '../../hooks/useShowTransitionDeprecated';
import useFocusMessage from './message/hooks/useFocusMessage';
@ -105,10 +106,16 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
onIntersectPinnedMessage,
}) => {
const {
openPremiumModal, requestConfetti, checkGiftCode, getReceipt, openStarsTransactionFromGift,
openPremiumModal,
requestConfetti,
checkGiftCode,
getReceipt,
openStarsTransactionFromGift,
openPrizeStarsTransactionFromGiveaway,
} = getActions();
const oldLang = useOldLang();
const lang = useLang();
// eslint-disable-next-line no-null/no-null
const ref = useRef<HTMLDivElement>(null);
@ -134,6 +141,7 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
const isSuggestedAvatar = message.content.action?.type === 'suggestProfilePhoto' && message.content.action!.photo;
const isJoinedMessage = isJoinedChannelMessage(message);
const isStarsGift = message.content.action?.type === 'giftStars';
const isPrizeStars = message.content.action?.type === 'prizeStars';
useEffect(() => {
if (noAppearanceAnimation) {
@ -214,6 +222,13 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
});
};
const handlePrizeStarsClick = () => {
openPrizeStarsTransactionFromGiveaway({
chatId: message.chatId,
messageId: message.id,
});
};
const handleGiftCodeClick = () => {
const slug = message.content.action?.slug;
if (!slug) return;
@ -343,6 +358,50 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
);
}
function renderPrizeStars() {
const isUnclaimed = message.content.action?.isUnclaimed;
return (
<span
className="action-message-gift action-message-gift-code"
tabIndex={0}
role="button"
onClick={handlePrizeStarsClick}
>
<AnimatedIconFromSticker
key={message.id}
sticker={starGiftSticker}
play={canPlayAnimatedEmojis}
noLoop
nonInteractive
/>
<strong>
{oldLang(isUnclaimed ? 'BoostingUnclaimedPrize' : 'BoostingCongratulations')}
</strong>
<span className="action-message-subtitle">
{targetChat && renderText(oldLang(isUnclaimed
? 'BoostingReceivedPrizeFrom' : 'BoostingYouHaveUnclaimedPrize', getChatTitle(oldLang, targetChat)),
['simple_markdown'])}
</span>
<span className="action-message-subtitle">
{renderText(lang(
'PrizeCredits', {
count: (
<b>{formatInteger(message.content.action?.stars!)}</b>
),
}, {
withNodes: true,
},
), ['simple_markdown'])}
</span>
<span className="action-message-button">{
oldLang('ActionGiftPremiumView')
}
</span>
</span>
);
}
const className = buildClassName(
'ActionMessage message-list-item',
isFocused && !noFocusHighlight && 'focused',
@ -368,6 +427,7 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
{isPremiumGift && renderGift()}
{isGiftCode && renderGiftCode()}
{isStarsGift && renderStarsGift()}
{isPrizeStars && renderPrizeStars()}
{isSuggestedAvatar && (
<ActionMessageSuggestedAvatar message={message} renderContent={renderContent} />
)}

View File

@ -68,7 +68,7 @@ const Giveaway = ({
const { giveaway, giveawayResults } = message.content;
const isResults = Boolean(giveawayResults);
const {
months, untilDate, prizeDescription,
months, untilDate, prizeDescription, stars,
} = (giveaway || giveawayResults)!;
const isOwn = isOwnMessage(message);
@ -130,12 +130,25 @@ const Giveaway = ({
</>
)}
<p className={styles.description}>
{renderText(lang('Chat.Giveaway.Info.Subscriptions', quantity), ['simple_markdown'])}
<br />
{renderText(lang(
'ActionGiftPremiumSubtitle',
lang('Chat.Giveaway.Info.Months', months),
), ['simple_markdown'])}
{message?.content?.giveaway?.stars ? (
<>
{renderText(
lang('Chat.Giveaway.Message.Stars.PrizeText', lang('Stars', message?.content?.giveaway?.stars)),
['simple_markdown'],
)}
<br />
{renderText(lang('AmongWinners', quantity), ['simple_markdown'])}
</>
) : (
<>
{renderText(lang('Chat.Giveaway.Info.Subscriptions', quantity), ['simple_markdown'])}
<br />
{renderText(lang(
'ActionGiftPremiumSubtitle',
lang('Chat.Giveaway.Info.Months', months),
), ['simple_markdown'])}
</>
)}
</p>
</div>
<div className={styles.section}>
@ -216,14 +229,14 @@ const Giveaway = ({
const isResultsInfo = giveawayInfo.type === 'results';
const chatTitle = isApiPeerChat(sender) ? getChatTitle(lang, sender) : getUserFullName(sender);
const duration = lang('Chat.Giveaway.Info.Months', months);
const endDate = formatDateAtTime(lang, untilDate * 1000);
const otherChannelsCount = giveaway?.channelIds ? giveaway.channelIds.length - 1 : 0;
const otherChannelsString = lang('Chat.Giveaway.Info.OtherChannels', otherChannelsCount);
const isSeveral = otherChannelsCount > 0;
const firstKey = isResultsInfo ? 'BoostingGiveawayHowItWorksTextEnd' : 'BoostingGiveawayHowItWorksText';
const firstParagraph = lang(firstKey, [chatTitle, quantity, duration], undefined, quantity);
const giveawayDuration = isResultsInfo ? lang('Chat.Giveaway.Info.Months', months) : lang('Stars', stars, 'i');
const firstParagraph = lang(firstKey, [chatTitle, quantity, giveawayDuration], undefined, quantity);
const additionalPrizes = prizeDescription
? lang('BoostingGiveawayHowItWorksIncludeText', [chatTitle, quantity, prizeDescription], undefined, quantity)

View File

@ -731,6 +731,10 @@
width: 1px;
visibility: hidden;
}
.giveaway-result-content {
min-width: 17rem;
}
}
// Border-radius styles

View File

@ -1576,7 +1576,9 @@ const Message: FC<OwnProps & StateProps> = ({
)}
{withAvatar && renderAvatar()}
<div
className={buildClassName('message-content-wrapper', contentClassName.includes('text') && 'can-select-text')}
className={buildClassName('message-content-wrapper',
contentClassName.includes('text') && 'giveaway-result-content',
contentClassName.includes('giveaway') && 'can-select-text')}
>
<div
className={contentClassName}

View File

@ -16,11 +16,29 @@
word-break: break-word;
}
.table .cell {
border: 1px solid var(--color-borders);
.table {
border-collapse: separate;
border-spacing: 0;
}
.cell {
border: solid 0.0625rem var(--color-borders);
border-style: none solid solid none;
padding: 0.25rem 0.5rem;
}
.row:first-child .cell:first-child { border-top-left-radius: 0.3125rem; }
.row:first-child .cell:last-child { border-top-right-radius: 0.3125rem; }
.row:last-child .cell:first-child { border-bottom-left-radius: 0.3125rem; }
.row:last-child .cell:last-child { border-bottom-right-radius: 0.3125rem; }
.row:first-child .cell { border-top-style: solid; }
.row .cell:first-child { border-left-style: solid; }
.section {
display: flex;
flex-direction: column;

View File

@ -31,6 +31,7 @@ type OwnProps = {
headerAvatarWebPhoto?: ApiWebDocument;
noHeaderImage?: boolean;
isGift?: boolean;
isPrizeStars?: boolean;
header?: TeactNode;
footer?: TeactNode;
buttonText?: string;
@ -49,6 +50,7 @@ const TableInfoModal = ({
headerAvatarWebPhoto,
noHeaderImage,
isGift,
isPrizeStars,
header,
footer,
buttonText,
@ -75,7 +77,7 @@ const TableInfoModal = ({
contentClassName={styles.content}
onClose={onClose}
>
{!isGift && !noHeaderImage && (
{!isGift && !isPrizeStars && !noHeaderImage && (
withAvatar ? (
<Avatar peer={headerAvatarPeer} webPhoto={headerAvatarWebPhoto} size="jumbo" className={styles.avatar} />
) : (

View File

@ -28,6 +28,10 @@
}
}
.active {
--_background-color: var(--color-background-secondary-accent);
}
.wideOption {
grid-column: 1 / -1;
}

View File

@ -1,13 +1,17 @@
import type { FC } from '../../../lib/teact/teact';
import React, { memo, useEffect, useMemo } from '../../../lib/teact/teact';
import React, {
memo, useEffect, useMemo,
} from '../../../lib/teact/teact';
import type { ApiStarTopupOption } from '../../../api/types';
import type { ApiStarGiveawayOption, ApiStarTopupOption } from '../../../api/types';
import buildClassName from '../../../util/buildClassName';
import { formatCurrency } from '../../../util/formatCurrency';
import { formatInteger } from '../../../util/textFormat';
import renderText from '../../common/helpers/renderText';
import useFlag from '../../../hooks/useFlag';
import useLang from '../../../hooks/useLang';
import useOldLang from '../../../hooks/useOldLang';
import Icon from '../../common/icons/Icon';
@ -20,18 +24,25 @@ const MAX_STARS_COUNT = 6;
type OwnProps = {
isActive?: boolean;
options?: ApiStarTopupOption[];
options?: ApiStarTopupOption[] | ApiStarGiveawayOption[];
selectedStarOption?: ApiStarTopupOption | ApiStarGiveawayOption;
selectedStarCount?: number;
starsNeeded?: number;
onClick: (option: ApiStarTopupOption) => void;
className?: string;
onClick: (option: ApiStarTopupOption | ApiStarGiveawayOption) => void;
};
const StarTopupOptionList: FC<OwnProps> = ({
isActive,
className,
options,
selectedStarOption,
selectedStarCount,
starsNeeded,
onClick,
}) => {
const lang = useOldLang();
const oldLang = useOldLang();
const lang = useLang();
const [areOptionsExtended, markOptionsExtended, unmarkOptionsExtended] = useFlag();
@ -51,7 +62,7 @@ const StarTopupOptionList: FC<OwnProps> = ({
));
const forceShowAll = starsNeeded && maxOption.stars < starsNeeded;
const result: { option: ApiStarTopupOption; starsCount: number; isWide: boolean }[] = [];
const result: { option: ApiStarTopupOption | ApiStarGiveawayOption; starsCount: number; isWide: boolean }[] = [];
let currentStackedStarsCount = 0;
let canExtendOptions = false;
options.forEach((option, index) => {
@ -73,13 +84,24 @@ const StarTopupOptionList: FC<OwnProps> = ({
}, [areOptionsExtended, options, starsNeeded]);
return (
<div className={styles.options}>
<div className={buildClassName(styles.options, className)}>
{renderingOptions?.map(({ option, starsCount, isWide }) => {
const length = renderingOptions?.length;
const isOdd = length % 2 === 0;
const isActiveOption = option === selectedStarOption;
let perUserStarCount;
if (option && 'winners' in option) {
const winner = option.winners.find((opt) => opt.users === selectedStarCount)
|| option.winners.reduce((max, opt) => (opt.users > max.users ? opt : max), option.winners[0]);
perUserStarCount = winner?.perUserStars;
}
return (
<div
className={buildClassName(styles.option, (!isOdd && isWide) && styles.wideOption)}
className={buildClassName(
styles.option, (!isOdd && isWide) && styles.wideOption, isActiveOption && styles.active,
)}
key={option.stars}
onClick={() => onClick?.(option)}
>
@ -92,14 +114,21 @@ const StarTopupOptionList: FC<OwnProps> = ({
</div>
</div>
<div className={styles.optionBottom}>
{formatCurrency(option.amount, option.currency, lang.code)}
{formatCurrency(option.amount, option.currency, oldLang.code)}
</div>
{(isActiveOption || (selectedStarOption && 'winners' in selectedStarOption)) && perUserStarCount && (
<div className={styles.optionBottom}>
<div className={styles.perUserStars}>
{renderText(oldLang('BoostGift.Stars.PerUser', formatInteger(perUserStarCount)))}
</div>
</div>
)}
</div>
);
})}
{!areOptionsExtended && canExtend && (
<Button className={styles.moreOptions} isText noForcedUpperCase onClick={markOptionsExtended}>
{lang('Stars.Purchase.ShowMore')}
{oldLang('Stars.Purchase.ShowMore')}
<Icon className={styles.iconDown} name="down" />
</Button>
)}

View File

@ -56,6 +56,7 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
const lang = useLang();
const { transaction } = modal || {};
const isGift = transaction?.isGift;
const isPrizeStars = transaction?.isPrizeStars;
const handleOpenMedia = useLastCallback(() => {
const media = transaction?.extendedMedia;
@ -158,7 +159,7 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
onClick={handleOpenMedia}
/>
)}
{isGift ? animatedStickerData : (
{(isGift || isPrizeStars) ? animatedStickerData : (
<img
className={buildClassName(styles.starsBackground, media && styles.mediaShift)}
src={StarsBackground}
@ -167,9 +168,10 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
/>
)}
{title && <h1 className={styles.title}>{title}</h1>}
{isGift && (
{(isGift || isPrizeStars) && (
<h1 className={buildClassName(styles.title, styles.starTitle)}>
{transaction?.isMyGift ? oldLang('StarsGiftSent') : oldLang('StarsGiftReceived')}
{isPrizeStars ? oldLang('StarsGiveawayPrizeReceived')
: transaction?.isMyGift ? oldLang('StarsGiftSent') : oldLang('StarsGiftReceived')}
</h1>
)}
<p className={styles.description}>{description}</p>
@ -199,7 +201,14 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
tableData.push([oldLang('Stars.Transaction.Media'), <SafeLink url={messageLink} text={messageLink} />]);
}
if (transaction.id) {
if (isPrizeStars) {
tableData.push(
[oldLang('BoostReason'), oldLang('Giveaway')],
[oldLang('Gift'), oldLang('Stars', transaction.stars, 'i')],
);
}
if (transaction.id && !isPrizeStars) {
tableData.push([
oldLang('Stars.Transaction.Id'),
(
@ -241,7 +250,9 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
footer,
avatarPeer: !transaction.photo ? (peer || customPeer) : undefined,
};
}, [transaction, oldLang, peer, isGift, animatedStickerData, giftOutAboutText, giftEntryAboutText]);
}, [
transaction, oldLang, peer, isGift, isPrizeStars, animatedStickerData, giftOutAboutText, giftEntryAboutText,
]);
const prevModalData = usePreviousDeprecated(starModalData);
const renderingModalData = prevModalData || starModalData;
@ -252,6 +263,7 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
className={styles.modal}
header={renderingModalData?.header}
isGift={isGift}
isPrizeStars={isPrizeStars}
tableData={renderingModalData?.tableData}
footer={renderingModalData?.footer}
noHeaderImage={Boolean(transaction?.extendedMedia)}

View File

@ -3,7 +3,7 @@ import React, {
} from '../../../lib/teact/teact';
import { getActions, withGlobal } from '../../../global';
import type { ApiBoost, ApiBoostStatistics, ApiPrepaidGiveaway } from '../../../api/types';
import type { ApiBoost, ApiBoostStatistics, ApiTypePrepaidGiveaway } from '../../../api/types';
import type { TabState } from '../../../global/types';
import {
@ -17,7 +17,7 @@ import {
} from '../../../global/selectors';
import buildClassName from '../../../util/buildClassName';
import { formatDateAtTime } from '../../../util/dates/dateFormat';
import { CUSTOM_PEER_TO_BE_DISTRIBUTED } from '../../../util/objects/customPeer';
import { CUSTOM_PEER_STAR, CUSTOM_PEER_TO_BE_DISTRIBUTED } from '../../../util/objects/customPeer';
import { formatInteger } from '../../../util/textFormat';
import { getBoostProgressInfo } from '../../common/helpers/boostInfo';
@ -40,6 +40,7 @@ import styles from './BoostStatistics.module.scss';
import GiftBlueRound from '../../../assets/premium/GiftBlueRound.svg';
import GiftGreenRound from '../../../assets/premium/GiftGreenRound.svg';
import GiftRedRound from '../../../assets/premium/GiftRedRound.svg';
import GiftStar from '../../../assets/premium/GiftStar.svg';
type StateProps = {
boostStatistics: TabState['boostStatistics'];
@ -196,6 +197,8 @@ const BoostStatistics = ({
});
const renderBoostList = useLastCallback((boost) => {
const hasStars = Boolean(boost?.stars);
return (
<ListItem
className="chat-item-clickable"
@ -205,7 +208,8 @@ const BoostStatistics = ({
<PrivateChatInfo
className={styles.user}
userId={boost.userId}
customPeer={!boost.userId ? CUSTOM_PEER_TO_BE_DISTRIBUTED : undefined}
customPeer={hasStars ? { ...CUSTOM_PEER_STAR, titleValue: boost.stars }
: (!boost.userId ? CUSTOM_PEER_TO_BE_DISTRIBUTED : undefined)}
status={lang('BoostExpireOn', formatDateAtTime(lang, boost.expires * 1000))}
noEmojiStatus
forceShowSelf
@ -218,8 +222,7 @@ const BoostStatistics = ({
);
});
const handleGiveawayClick = useLastCallback((e) => {
e.preventDefault();
const handleGiveawayClick = useLastCallback(() => {
openGiveawayModal({ chatId });
});
@ -228,7 +231,7 @@ const BoostStatistics = ({
loadMoreBoosters({ isGifts: tabType === 'giftedBoostList' });
});
const launchPrepaidGiveawayHandler = useLastCallback((prepaidGiveaway: ApiPrepaidGiveaway) => {
const launchPrepaidGiveawayHandler = useLastCallback((prepaidGiveaway: ApiTypePrepaidGiveaway) => {
openGiveawayModal({ chatId, prepaidGiveaway });
});
@ -271,45 +274,65 @@ const BoostStatistics = ({
<h4 className={styles.sectionHeader} dir={lang.isRtl ? 'rtl' : undefined}>
{lang('BoostingPreparedGiveaways')}
</h4>
{statsOverview?.prepaidGiveaways?.map((prepaidGiveaway) => (
<ListItem
key={prepaidGiveaway.id}
className="chat-item-clickable"
// eslint-disable-next-line react/jsx-no-bind
onClick={() => launchPrepaidGiveawayHandler(prepaidGiveaway)}
>
<div className={buildClassName(styles.status, 'status-clickable')}>
<div>
<img
src={GIVEAWAY_IMG_LIST[prepaidGiveaway.months]}
className={styles.giveawayIcon}
alt={lang('Giveaway')}
/>
</div>
<div className={styles.info}>
<h3>
{lang('BoostingTelegramPremiumCountPlural', prepaidGiveaway.quantity)}
</h3>
<p className={styles.month}>{lang('PrepaidGiveawayMonths', prepaidGiveaway.months)}</p>
</div>
<div className={styles.quantity}>
<div className={buildClassName(styles.floatingBadge,
styles.floatingBadgeButtonColor,
styles.floatingBadgeButton)}
>
<Icon name="boost" className={styles.floatingBadgeIcon} />
<div className={styles.floatingBadgeValue} dir={lang.isRtl ? 'rtl' : undefined}>
{prepaidGiveaway.quantity * (giveawayBoostsPerPremium ?? GIVEAWAY_BOOST_PER_PREMIUM)}
{statsOverview?.prepaidGiveaways?.map((prepaidGiveaway) => {
const isStarsGiveaway = 'stars' in prepaidGiveaway;
return (
<ListItem
key={prepaidGiveaway.id}
className="chat-item-clickable"
// eslint-disable-next-line react/jsx-no-bind
onClick={() => launchPrepaidGiveawayHandler(prepaidGiveaway)}
>
<div className={buildClassName(styles.status, 'status-clickable')}>
<div>
{isStarsGiveaway
? (
<img
src={GiftStar}
className={styles.giveawayIcon}
alt={lang('GiftStar')}
/>
) : (
<img
src={GIVEAWAY_IMG_LIST[prepaidGiveaway.months]}
className={styles.giveawayIcon}
alt={lang('Giveaway')}
/>
)}
</div>
<div className={styles.info}>
<h3>
{isStarsGiveaway
? lang('Giveaway.Stars.Prepaid.Title', prepaidGiveaway.stars)
: lang('BoostingTelegramPremiumCountPlural', prepaidGiveaway.quantity)}
</h3>
<p className={styles.month}>{
isStarsGiveaway ? lang('Giveaway.Stars.Prepaid.Desc', prepaidGiveaway.quantity)
: lang('PrepaidGiveawayMonths', prepaidGiveaway.months)
}
</p>
</div>
<div className={styles.quantity}>
<div className={buildClassName(styles.floatingBadge,
styles.floatingBadgeButtonColor,
styles.floatingBadgeButton)}
>
<Icon name="boost" className={styles.floatingBadgeIcon} />
<div className={styles.floatingBadgeValue} dir={lang.isRtl ? 'rtl' : undefined}>
{isStarsGiveaway ? prepaidGiveaway.boosts
: prepaidGiveaway.quantity * (giveawayBoostsPerPremium ?? GIVEAWAY_BOOST_PER_PREMIUM)}
</div>
</div>
</div>
</div>
</div>
</ListItem>
))}
</ListItem>
);
})}
<p className="text-muted hint" key="links-hint">{lang('BoostingSelectPaidGiveaway')}</p>
</div>
)}
<div>
<div className={styles.section}>
{shouldDisplayGiftList ? (
<div
className={buildClassName(styles.boostSection, styles.content)}
@ -326,7 +349,7 @@ const BoostStatistics = ({
<TabList activeTab={renderingActiveTab} tabs={tabs} onSwitchTab={setActiveTab} />
</div>
) : (
<div className={styles.section}>
<>
<h4 className={styles.sectionHeader} dir={lang.isRtl ? 'rtl' : undefined}>
{lang('BoostingBoostsCount', boostStatistics?.boosts?.count)}
</h4>
@ -335,25 +358,23 @@ const BoostStatistics = ({
</div>
)}
{boostStatistics?.boosts?.list?.map((boost) => renderBoostList(boost))}
</div>
</>
)}
{Boolean(boostersToLoadCount) && (
<ListItem
key="load-more"
className={styles.showMore}
disabled={boostStatistics?.isLoadingBoosters}
onClick={handleLoadMore}
>
{boostStatistics?.isLoadingBoosters ? (
<Spinner className={styles.loadMoreSpinner} />
) : (
<Icon name="down" className={styles.down} />
)}
{lang('ShowVotes', boostersToLoadCount, 'i')}
</ListItem>
)}
<div className={styles.section}>
{Boolean(boostersToLoadCount) && (
<ListItem
key="load-more"
className={styles.showMore}
disabled={boostStatistics?.isLoadingBoosters}
onClick={handleLoadMore}
>
{boostStatistics?.isLoadingBoosters ? (
<Spinner className={styles.loadMoreSpinner} />
) : (
<Icon name="down" className={styles.down} />
)}
{lang('ShowVotes', boostersToLoadCount, 'i')}
</ListItem>
)}
</div>
</div>
<LinkField className={styles.section} link={status!.boostUrl} withShare title={lang('LinkForBoosting')} />
{isGiveawayAvailable && (

View File

@ -7,14 +7,16 @@
background: var(--color-links);
border-radius: 50%;
cursor: var(--custom-cursor, pointer);
transform: scale(1);
position: absolute;
left: var(--fill-percentage);
transform: scale(1) translate(-45%, -50%);
transition: transform 0.3s ease-in-out;
z-index: 2;
-webkit-appearance: none;
appearance: none;
&:hover {
transform: scale(1.5);
transform: scale(1.5) translate(-30%, -30%);
}
}
@ -70,30 +72,6 @@
);
}
.slider::before,
.slider::after {
content: '';
position: absolute;
top: 0;
height: 100%;
transition: transform 0.2s ease;
z-index: -1;
transform-origin: left;
}
.slider::before {
left: 0;
background-color: var(--color-links);
transform: scaleX(var(--fill-percentage-before));
}
.slider::after {
right: 0;
background: var(--color-text-secondary);
transform: scaleX(var(--fill-percentage-after));
transform-origin: right;
}
.slider::-webkit-slider-thumb {
@include thumb-styles();
}

View File

@ -51,7 +51,7 @@ export const MEDIA_PROGRESSIVE_CACHE_DISABLED = false;
export const MEDIA_PROGRESSIVE_CACHE_NAME = 'tt-media-progressive';
export const MEDIA_CACHE_MAX_BYTES = 512 * 1024; // 512 KB
export const CUSTOM_BG_CACHE_NAME = 'tt-custom-bg';
export const LANG_CACHE_NAME = 'tt-lang-packs-v40';
export const LANG_CACHE_NAME = 'tt-lang-packs-v42';
export const ASSET_CACHE_NAME = 'tt-assets';
export const AUTODOWNLOAD_FILESIZE_MB_LIMITS = [1, 5, 10, 50, 100, 500];
export const DATA_BROADCAST_CHANNEL_NAME = 'tt-global';

View File

@ -11,7 +11,11 @@ import { buildQueryString } from '../../../util/requestQuery';
import { extractCurrentThemeParams } from '../../../util/themeStyle';
import { callApi } from '../../../api/gramjs';
import { isChatChannel, isChatSuperGroup } from '../../helpers';
import { getRequestInputInvoice, getStarsTransactionFromGift } from '../../helpers/payments';
import {
getPrizeStarsTransactionFromGiveaway,
getRequestInputInvoice,
getStarsTransactionFromGift,
} from '../../helpers/payments';
import { addActionHandler, getGlobal, setGlobal } from '../../index';
import {
appendStarsTransactions, closeInvoice,
@ -457,12 +461,12 @@ addActionHandler('openGiveawayModal', async (global, actions, payload): Promise<
chat,
});
if (!result) {
const starOptions = await callApi('fetchStarsGiveawayOptions');
if (!result || !starOptions) {
return;
}
global = getGlobal();
const isOpen = Boolean(chatId);
global = updateTabState(global, {
@ -471,6 +475,7 @@ addActionHandler('openGiveawayModal', async (global, actions, payload): Promise<
gifts: result,
isOpen,
prepaidGiveaway,
starOptions,
},
}, tabId);
setGlobal(global);
@ -546,6 +551,22 @@ addActionHandler('openStarsTransactionFromGift', (global, actions, payload): Act
return openStarsTransactionModal(global, transaction, tabId);
});
addActionHandler('openPrizeStarsTransactionFromGiveaway', (global, actions, payload): ActionReturnType => {
const {
chatId,
messageId,
tabId = getCurrentTabId(),
} = payload || {};
const message = selectChatMessage(global, chatId, messageId);
if (!message) return undefined;
const transaction = getPrizeStarsTransactionFromGiveaway(message);
if (!transaction) return undefined;
return openStarsTransactionModal(global, transaction, tabId);
});
addActionHandler('openPremiumGiftModal', async (global, actions, payload): Promise<void> => {
const {
forUserIds, tabId = getCurrentTabId(),
@ -974,6 +995,41 @@ addActionHandler('launchPrepaidGiveaway', async (global, actions, payload): Prom
actions.openBoostStatistics({ chatId, tabId });
});
addActionHandler('launchPrepaidStarsGiveaway', async (global, actions, payload): Promise<void> => {
const {
chatId, giveawayId, paymentPurpose, tabId = getCurrentTabId(),
} = payload;
const chat = selectChat(global, chatId);
if (!chat) return;
const additionalChannels = paymentPurpose?.additionalChannelIds?.map((id) => selectChat(global, id)).filter(Boolean);
const result = await callApi('launchPrepaidGiveaway', {
chat,
giveawayId,
paymentPurpose: {
type: 'starsgiveaway',
chat,
areWinnersVisible: paymentPurpose?.areWinnersVisible,
additionalChannels,
countries: paymentPurpose?.countries,
prizeDescription: paymentPurpose.prizeDescription,
untilDate: paymentPurpose.untilDate,
currency: paymentPurpose.currency,
amount: paymentPurpose.amount,
stars: paymentPurpose.stars,
users: paymentPurpose.users,
},
});
if (!result) {
return;
}
actions.openBoostStatistics({ chatId, tabId });
});
addActionHandler('loadStarStatus', async (global): Promise<void> => {
const currentStatus = global.stars;
const needsTopupOptions = !currentStatus?.topupOptions;

View File

@ -85,6 +85,36 @@ export function getRequestInputInvoice<T extends GlobalState>(
};
}
if (inputInvoice.type === 'starsgiveaway') {
const {
chatId, additionalChannelIds, amount, currency, untilDate, areWinnersVisible, countries,
isOnlyForNewSubscribers, prizeDescription, stars, users,
} = inputInvoice;
const chat = selectChat(global, chatId);
if (!chat) {
return undefined;
}
const additionalChannels = additionalChannelIds?.map((id) => selectChat(global, id)).filter(Boolean);
return {
type: 'starsgiveaway',
purpose: {
type: 'starsgiveaway',
amount,
currency,
chat,
additionalChannels,
untilDate,
areWinnersVisible,
countries,
isOnlyForNewSubscribers,
prizeDescription,
stars,
users,
},
};
}
if (inputInvoice.type === 'giveaway') {
const {
chatId, additionalChannelIds, amount, currency, option, untilDate, areWinnersVisible, countries,
@ -207,3 +237,22 @@ export function getStarsTransactionFromGift(message: ApiMessage): ApiStarsTransa
isMyGift: message.isOutgoing || undefined,
};
}
export function getPrizeStarsTransactionFromGiveaway(message: ApiMessage): ApiStarsTransaction | undefined {
const { action } = message.content;
if (action?.type !== 'prizeStars') return undefined;
const { transactionId, stars, targetChatId } = action;
return {
id: transactionId!,
stars: stars!,
peer: {
type: 'peer',
id: targetChatId!,
},
date: message.date,
isPrizeStars: true,
};
}

View File

@ -54,7 +54,6 @@ import type {
ApiPostStatistics,
ApiPremiumGiftCodeOption,
ApiPremiumPromo,
ApiPrepaidGiveaway,
ApiQuickReply,
ApiReaction,
ApiReactionKey,
@ -64,7 +63,7 @@ import type {
ApiSendMessageAction,
ApiSession,
ApiSessionData,
ApiSponsoredMessage,
ApiSponsoredMessage, ApiStarGiveawayOption,
ApiStarsTransaction,
ApiStarTopupOption,
ApiStealthMode,
@ -76,6 +75,7 @@ import type {
ApiTimezone,
ApiTopic,
ApiTranscription,
ApiTypePrepaidGiveaway,
ApiTypeStoryView,
ApiTypingStatus,
ApiUpdate,
@ -716,7 +716,8 @@ export type TabState = {
gifts?: ApiPremiumGiftCodeOption[];
selectedMemberIds?: string[];
selectedChannelIds?: string[];
prepaidGiveaway?: ApiPrepaidGiveaway;
prepaidGiveaway?: ApiTypePrepaidGiveaway;
starOptions?: ApiStarGiveawayOption[];
};
deleteMessageModal?: {
@ -1798,6 +1799,11 @@ export interface ActionPayloads {
messageId: number;
} & WithTabId;
closeStarsTransactionModal: WithTabId | undefined;
openPrizeStarsTransactionFromGiveaway: {
chatId: string;
messageId: number;
} & WithTabId;
closePrizeStarsTransactionFromGiveaway: WithTabId | undefined;
sendCredentialsInfo: {
credentials: ApiCredentials;
} & WithTabId;
@ -2239,6 +2245,22 @@ export interface ActionPayloads {
};
} & WithTabId;
launchPrepaidStarsGiveaway: {
chatId: string;
giveawayId: string;
paymentPurpose: {
additionalChannelIds?: string[];
areWinnersVisible?: boolean;
countries?: string[];
prizeDescription?: string;
untilDate: number;
currency: string;
stars: number;
users: number;
amount: number;
};
} & WithTabId;
loadStarStatus: undefined;
loadStarsTransactions: {
type: StarsTransactionType;
@ -3248,7 +3270,7 @@ export interface ActionPayloads {
openGiveawayModal: ({
chatId: string;
gifts?: number[] | undefined;
prepaidGiveaway?: ApiPrepaidGiveaway | undefined;
prepaidGiveaway?: ApiTypePrepaidGiveaway | undefined;
} & WithTabId);
closeGiveawayModal: WithTabId | undefined;

View File

@ -1,6 +1,6 @@
const api = require('./api');
const LAYER = 186;
const LAYER = 187;
const tlobjects = {};
for (const tl of Object.values(api)) {

File diff suppressed because one or more lines are too long

View File

@ -36,7 +36,7 @@ inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> s
inputMediaDice#e66fbf7b emoticon:string = InputMedia;
inputMediaStory#89fdd778 peer:InputPeer id:int = InputMedia;
inputMediaWebPage#c21b8849 flags:# force_large_media:flags.0?true force_small_media:flags.1?true optional:flags.2?true url:string = InputMedia;
inputMediaPaidMedia#aa661fc3 stars_amount:long extended_media:Vector<InputMedia> = InputMedia;
inputMediaPaidMedia#c4103386 flags:# stars_amount:long extended_media:Vector<InputMedia> payload:flags.0?string = InputMedia;
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
inputChatUploadedPhoto#bdcdaec0 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.3?VideoSize = InputChatPhoto;
inputChatPhoto#8953ad37 id:InputPhoto = InputChatPhoto;
@ -108,8 +108,8 @@ messageMediaGeoLive#b940c666 flags:# geo:GeoPoint heading:flags.0?int period:int
messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia;
messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia;
messageMediaStory#68cb6283 flags:# via_mention:flags.1?true peer:Peer id:int story:flags.0?StoryItem = MessageMedia;
messageMediaGiveaway#daad85b0 flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.2?true channels:Vector<long> countries_iso2:flags.1?Vector<string> prize_description:flags.3?string quantity:int months:int until_date:int = MessageMedia;
messageMediaGiveawayResults#c6991068 flags:# only_new_subscribers:flags.0?true refunded:flags.2?true channel_id:long additional_peers_count:flags.3?int launch_msg_id:int winners_count:int unclaimed_count:int winners:Vector<long> months:int prize_description:flags.1?string until_date:int = MessageMedia;
messageMediaGiveaway#aa073beb flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.2?true channels:Vector<long> countries_iso2:flags.1?Vector<string> prize_description:flags.3?string quantity:int months:flags.4?int stars:flags.5?long until_date:int = MessageMedia;
messageMediaGiveawayResults#ceaa3ea1 flags:# only_new_subscribers:flags.0?true refunded:flags.2?true channel_id:long additional_peers_count:flags.3?int launch_msg_id:int winners_count:int unclaimed_count:int winners:Vector<long> months:flags.4?int stars:flags.5?long prize_description:flags.1?string until_date:int = MessageMedia;
messageMediaPaidMedia#a8852491 stars_amount:long extended_media:Vector<MessageExtendedMedia> = MessageMedia;
messageActionEmpty#b6aef7b0 = MessageAction;
messageActionChatCreate#bd47cbad title:string users:Vector<long> = MessageAction;
@ -150,12 +150,13 @@ messageActionSuggestProfilePhoto#57de635e photo:Photo = MessageAction;
messageActionRequestedPeer#31518e9b button_id:int peers:Vector<Peer> = MessageAction;
messageActionSetChatWallPaper#5060a3f4 flags:# same:flags.0?true for_both:flags.1?true wallpaper:WallPaper = MessageAction;
messageActionGiftCode#678c2e09 flags:# via_giveaway:flags.0?true unclaimed:flags.2?true boost_peer:flags.1?Peer months:int slug:string currency:flags.2?string amount:flags.2?long crypto_currency:flags.3?string crypto_amount:flags.3?long = MessageAction;
messageActionGiveawayLaunch#332ba9ed = MessageAction;
messageActionGiveawayResults#2a9fadc5 winners_count:int unclaimed_count:int = MessageAction;
messageActionGiveawayLaunch#a80f51e4 flags:# stars:flags.0?long = MessageAction;
messageActionGiveawayResults#87e2f155 flags:# stars:flags.0?true winners_count:int unclaimed_count:int = MessageAction;
messageActionBoostApply#cc02aa6d boosts:int = MessageAction;
messageActionRequestedPeerSentMe#93b31848 button_id:int peers:Vector<RequestedPeer> = MessageAction;
messageActionPaymentRefunded#41b3e202 flags:# peer:Peer currency:string total_amount:long payload:flags.0?bytes charge:PaymentCharge = MessageAction;
messageActionGiftStars#45d5b021 flags:# currency:string amount:long stars:long crypto_currency:flags.0?string crypto_amount:flags.0?long transaction_id:flags.1?string = MessageAction;
messageActionPrizeStars#b00c47a2 flags:# unclaimed:flags.0?true stars:long transaction_id:string boost_peer:Peer giveaway_msg_id:int = MessageAction;
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
photoEmpty#2331b22d id:long = Photo;
@ -369,6 +370,8 @@ updateBroadcastRevenueTransactions#dfd961f5 peer:Peer balances:BroadcastRevenueB
updateStarsBalance#fb85198 balance:long = Update;
updateBusinessBotCallbackQuery#1ea2fda7 flags:# query_id:long user_id:long connection_id:string message:Message reply_to_message:flags.2?Message chat_instance:long data:flags.0?bytes = Update;
updateStarsRevenueStatus#a584b019 peer:Peer status:StarsRevenueStatus = Update;
updateBotPurchasedPaidMedia#283bd312 user_id:long payload:string qts:int = Update;
updatePaidReactionPrivacy#51ca7aec private:Bool = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
updates.differenceEmpty#5d75a138 date:int seq:int = updates.Difference;
updates.difference#f49ca0 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> state:updates.State = updates.Difference;
@ -830,9 +833,10 @@ channelAdminLogEventActionChangeWallpaper#31bb5d52 prev_value:WallPaper new_valu
channelAdminLogEventActionChangeEmojiStatus#3ea9feb1 prev_value:EmojiStatus new_value:EmojiStatus = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeEmojiStickerSet#46d840ab prev_stickerset:InputStickerSet new_stickerset:InputStickerSet = ChannelAdminLogEventAction;
channelAdminLogEventActionToggleSignatureProfiles#60a79c79 new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantSubExtend#64642db3 prev_participant:ChannelParticipant new_participant:ChannelParticipant = ChannelAdminLogEventAction;
channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
channels.adminLogResults#ed8af74d events:Vector<ChannelAdminLogEvent> chats:Vector<Chat> users:Vector<User> = channels.AdminLogResults;
channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true forums:flags.17?true = ChannelAdminLogEventsFilter;
channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true forums:flags.17?true sub_extend:flags.18?true = ChannelAdminLogEventsFilter;
popularContact#5ce14175 client_id:long importers:int = PopularContact;
messages.favedStickersNotModified#9e8fa6d3 = messages.FavedStickers;
messages.favedStickers#2cb51097 hash:long packs:Vector<StickerPack> stickers:Vector<Document> = messages.FavedStickers;
@ -1117,6 +1121,7 @@ inputStorePaymentPremiumGiftCode#a3805f3f flags:# users:Vector<InputUser> boost_
inputStorePaymentPremiumGiveaway#160544ca flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.3?true boost_peer:InputPeer additional_peers:flags.1?Vector<InputPeer> countries_iso2:flags.2?Vector<string> prize_description:flags.4?string random_id:long until_date:int currency:string amount:long = InputStorePaymentPurpose;
inputStorePaymentStarsTopup#dddd0f56 stars:long currency:string amount:long = InputStorePaymentPurpose;
inputStorePaymentStarsGift#1d741ef7 user_id:InputUser stars:long currency:string amount:long = InputStorePaymentPurpose;
inputStorePaymentStarsGiveaway#751f08fa flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.3?true stars:long boost_peer:InputPeer additional_peers:flags.1?Vector<InputPeer> countries_iso2:flags.2?Vector<string> prize_description:flags.4?string random_id:long until_date:int currency:string amount:long users:int = InputStorePaymentPurpose;
premiumGiftOption#74c34319 flags:# months:int currency:string amount:long bot_url:string store_product:flags.0?string = PremiumGiftOption;
paymentFormMethod#88f8f21b url:string title:string = PaymentFormMethod;
emojiStatusEmpty#2de11aae = EmojiStatus;
@ -1218,9 +1223,10 @@ messages.webPage#fd5e12bd webpage:WebPage chats:Vector<Chat> users:Vector<User>
premiumGiftCodeOption#257e962b flags:# users:int months:int store_product:flags.0?string store_quantity:flags.1?int currency:string amount:long = PremiumGiftCodeOption;
payments.checkedGiftCode#284a1096 flags:# via_giveaway:flags.2?true from_id:flags.4?Peer giveaway_msg_id:flags.3?int to_id:flags.0?long date:int months:int used_date:flags.1?int chats:Vector<Chat> users:Vector<User> = payments.CheckedGiftCode;
payments.giveawayInfo#4367daa0 flags:# participating:flags.0?true preparing_results:flags.3?true start_date:int joined_too_early_date:flags.1?int admin_disallowed_chat_id:flags.2?long disallowed_country:flags.4?string = payments.GiveawayInfo;
payments.giveawayInfoResults#cd5570 flags:# winner:flags.0?true refunded:flags.1?true start_date:int gift_code_slug:flags.0?string finish_date:int winners_count:int activated_count:int = payments.GiveawayInfo;
payments.giveawayInfoResults#e175e66f flags:# winner:flags.0?true refunded:flags.1?true start_date:int gift_code_slug:flags.3?string stars_prize:flags.4?long finish_date:int winners_count:int activated_count:flags.2?int = payments.GiveawayInfo;
prepaidGiveaway#b2539d54 id:long months:int quantity:int date:int = PrepaidGiveaway;
boost#2a1c8c71 flags:# gift:flags.1?true giveaway:flags.2?true unclaimed:flags.3?true id:string user_id:flags.0?long giveaway_msg_id:flags.2?int date:int expires:int used_gift_slug:flags.4?string multiplier:flags.5?int = Boost;
prepaidStarsGiveaway#9a9d77e0 id:long stars:long quantity:int boosts:int date:int = PrepaidGiveaway;
boost#4b3e14d6 flags:# gift:flags.1?true giveaway:flags.2?true unclaimed:flags.3?true id:string user_id:flags.0?long giveaway_msg_id:flags.2?int date:int expires:int used_gift_slug:flags.4?string multiplier:flags.5?int stars:flags.6?long = Boost;
premium.boostsList#86f8613c flags:# count:int boosts:Vector<Boost> next_offset:flags.0?string users:Vector<User> = premium.BoostsList;
myBoost#c448415c flags:# slot:int peer:flags.0?Peer date:int expires:int cooldown_until_date:flags.1?int = MyBoost;
premium.myBoosts#9ae228e2 my_boosts:Vector<MyBoost> chats:Vector<Chat> users:Vector<User> = premium.MyBoosts;
@ -1310,7 +1316,7 @@ stats.broadcastRevenueTransactions#87158466 count:int transactions:Vector<Broadc
reactionNotificationsFromContacts#bac3a61a = ReactionNotificationsFrom;
reactionNotificationsFromAll#4b9e22a0 = ReactionNotificationsFrom;
reactionsNotifySettings#56e34970 flags:# messages_notify_from:flags.0?ReactionNotificationsFrom stories_notify_from:flags.1?ReactionNotificationsFrom sound:NotificationSound show_previews:Bool = ReactionsNotifySettings;
broadcastRevenueBalances#8438f1c6 current_balance:long available_balance:long overall_revenue:long = BroadcastRevenueBalances;
broadcastRevenueBalances#c3ff71e7 flags:# withdrawal_enabled:flags.0?true current_balance:long available_balance:long overall_revenue:long = BroadcastRevenueBalances;
availableEffect#93c3e27e flags:# premium_required:flags.2?true id:long emoticon:string static_icon_id:flags.0?long effect_sticker_id:long effect_animation_id:flags.1?long = AvailableEffect;
messages.availableEffectsNotModified#d1ed9a5b = messages.AvailableEffects;
messages.availableEffects#bddb616e hash:int effects:Vector<AvailableEffect> documents:Vector<Document> = messages.AvailableEffects;
@ -1323,7 +1329,7 @@ starsTransactionPeerFragment#e92fd902 = StarsTransactionPeer;
starsTransactionPeer#d80da15d peer:Peer = StarsTransactionPeer;
starsTransactionPeerAds#60682812 = StarsTransactionPeer;
starsTopupOption#bd915c0 flags:# extended:flags.1?true stars:long store_product:flags.0?string currency:string amount:long = StarsTopupOption;
starsTransaction#433aeb2b flags:# refund:flags.3?true pending:flags.4?true failed:flags.6?true gift:flags.10?true reaction:flags.11?true id:string stars:long date:int peer:StarsTransactionPeer title:flags.0?string description:flags.1?string photo:flags.2?WebDocument transaction_date:flags.5?int transaction_url:flags.5?string bot_payload:flags.7?bytes msg_id:flags.8?int extended_media:flags.9?Vector<MessageMedia> subscription_period:flags.12?int = StarsTransaction;
starsTransaction#ee7522d5 flags:# refund:flags.3?true pending:flags.4?true failed:flags.6?true gift:flags.10?true reaction:flags.11?true id:string stars:long date:int peer:StarsTransactionPeer title:flags.0?string description:flags.1?string photo:flags.2?WebDocument transaction_date:flags.5?int transaction_url:flags.5?string bot_payload:flags.7?bytes msg_id:flags.8?int extended_media:flags.9?Vector<MessageMedia> subscription_period:flags.12?int giveaway_post_id:flags.13?int = StarsTransaction;
payments.starsStatus#bbfa316c flags:# balance:long subscriptions:flags.1?Vector<StarsSubscription> subscriptions_next_offset:flags.2?string subscriptions_missing_balance:flags.4?long history:flags.3?Vector<StarsTransaction> next_offset:flags.0?string chats:Vector<Chat> users:Vector<User> = payments.StarsStatus;
foundStory#e87acbc0 peer:Peer story:StoryItem = FoundStory;
stories.foundStories#e2de7737 flags:# count:int stories:Vector<FoundStory> next_offset:flags.0?string chats:Vector<Chat> users:Vector<User> = stories.FoundStories;
@ -1340,6 +1346,8 @@ bots.previewInfo#ca71d64 media:Vector<BotPreviewMedia> lang_codes:Vector<string>
starsSubscriptionPricing#5416d58 period:int amount:long = StarsSubscriptionPricing;
starsSubscription#538ecf18 flags:# canceled:flags.0?true can_refulfill:flags.1?true missing_balance:flags.2?true id:string peer:Peer until_date:int pricing:StarsSubscriptionPricing chat_invite_hash:flags.3?string = StarsSubscription;
messageReactor#4ba3a95a flags:# top:flags.0?true my:flags.1?true anonymous:flags.2?true peer_id:flags.3?Peer count:int = MessageReactor;
starsGiveawayOption#94ce852a flags:# extended:flags.0?true default:flags.1?true stars:long yearly_boosts:int store_product:flags.2?string currency:string amount:long winners:Vector<StarsGiveawayWinnersOption> = StarsGiveawayOption;
starsGiveawayWinnersOption#54236209 flags:# default:flags.0?true users:int per_user_stars:long = StarsGiveawayWinnersOption;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
initConnection#c1cd5ea9 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy params:flags.1?JSONValue query:!X = X;
@ -1643,6 +1651,7 @@ payments.sendStarsForm#2bb731d flags:# form_id:long invoice:InputInvoice = payme
payments.refundStarsCharge#25ae8f4a user_id:InputUser charge_id:string = Updates;
payments.getStarsTransactionsByID#27842d2e peer:InputPeer id:Vector<InputStarsTransaction> = payments.StarsStatus;
payments.getStarsGiftOptions#d3c96bc8 flags:# user_id:flags.0?InputUser = Vector<StarsGiftOption>;
payments.getStarsGiveawayOptions#bd1efd3e = Vector<StarsGiveawayOption>;
phone.requestCall#42ff96ed flags:# video:flags.0?true user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
phone.acceptCall#3bd2b4a0 peer:InputPhoneCall g_b:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
phone.confirmCall#2efe1722 peer:InputPhoneCall g_a:bytes key_fingerprint:long protocol:PhoneCallProtocol = phone.PhoneCall;

View File

@ -368,6 +368,7 @@
"payments.getStarsTransactionsByID",
"payments.sendStarsForm",
"payments.getStarsGiftOptions",
"payments.getStarsGiveawayOptions",
"payments.refundStarsCharge",
"payments.getStarsGiftOptions",
"fragment.getCollectibleInfo"

View File

@ -45,7 +45,7 @@ inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> s
inputMediaDice#e66fbf7b emoticon:string = InputMedia;
inputMediaStory#89fdd778 peer:InputPeer id:int = InputMedia;
inputMediaWebPage#c21b8849 flags:# force_large_media:flags.0?true force_small_media:flags.1?true optional:flags.2?true url:string = InputMedia;
inputMediaPaidMedia#aa661fc3 stars_amount:long extended_media:Vector<InputMedia> = InputMedia;
inputMediaPaidMedia#c4103386 flags:# stars_amount:long extended_media:Vector<InputMedia> payload:flags.0?string = InputMedia;
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
inputChatUploadedPhoto#bdcdaec0 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.3?VideoSize = InputChatPhoto;
@ -133,8 +133,8 @@ messageMediaGeoLive#b940c666 flags:# geo:GeoPoint heading:flags.0?int period:int
messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia;
messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia;
messageMediaStory#68cb6283 flags:# via_mention:flags.1?true peer:Peer id:int story:flags.0?StoryItem = MessageMedia;
messageMediaGiveaway#daad85b0 flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.2?true channels:Vector<long> countries_iso2:flags.1?Vector<string> prize_description:flags.3?string quantity:int months:int until_date:int = MessageMedia;
messageMediaGiveawayResults#c6991068 flags:# only_new_subscribers:flags.0?true refunded:flags.2?true channel_id:long additional_peers_count:flags.3?int launch_msg_id:int winners_count:int unclaimed_count:int winners:Vector<long> months:int prize_description:flags.1?string until_date:int = MessageMedia;
messageMediaGiveaway#aa073beb flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.2?true channels:Vector<long> countries_iso2:flags.1?Vector<string> prize_description:flags.3?string quantity:int months:flags.4?int stars:flags.5?long until_date:int = MessageMedia;
messageMediaGiveawayResults#ceaa3ea1 flags:# only_new_subscribers:flags.0?true refunded:flags.2?true channel_id:long additional_peers_count:flags.3?int launch_msg_id:int winners_count:int unclaimed_count:int winners:Vector<long> months:flags.4?int stars:flags.5?long prize_description:flags.1?string until_date:int = MessageMedia;
messageMediaPaidMedia#a8852491 stars_amount:long extended_media:Vector<MessageExtendedMedia> = MessageMedia;
messageActionEmpty#b6aef7b0 = MessageAction;
@ -176,12 +176,13 @@ messageActionSuggestProfilePhoto#57de635e photo:Photo = MessageAction;
messageActionRequestedPeer#31518e9b button_id:int peers:Vector<Peer> = MessageAction;
messageActionSetChatWallPaper#5060a3f4 flags:# same:flags.0?true for_both:flags.1?true wallpaper:WallPaper = MessageAction;
messageActionGiftCode#678c2e09 flags:# via_giveaway:flags.0?true unclaimed:flags.2?true boost_peer:flags.1?Peer months:int slug:string currency:flags.2?string amount:flags.2?long crypto_currency:flags.3?string crypto_amount:flags.3?long = MessageAction;
messageActionGiveawayLaunch#332ba9ed = MessageAction;
messageActionGiveawayResults#2a9fadc5 winners_count:int unclaimed_count:int = MessageAction;
messageActionGiveawayLaunch#a80f51e4 flags:# stars:flags.0?long = MessageAction;
messageActionGiveawayResults#87e2f155 flags:# stars:flags.0?true winners_count:int unclaimed_count:int = MessageAction;
messageActionBoostApply#cc02aa6d boosts:int = MessageAction;
messageActionRequestedPeerSentMe#93b31848 button_id:int peers:Vector<RequestedPeer> = MessageAction;
messageActionPaymentRefunded#41b3e202 flags:# peer:Peer currency:string total_amount:long payload:flags.0?bytes charge:PaymentCharge = MessageAction;
messageActionGiftStars#45d5b021 flags:# currency:string amount:long stars:long crypto_currency:flags.0?string crypto_amount:flags.0?long transaction_id:flags.1?string = MessageAction;
messageActionPrizeStars#b00c47a2 flags:# unclaimed:flags.0?true stars:long transaction_id:string boost_peer:Peer giveaway_msg_id:int = MessageAction;
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
@ -422,6 +423,8 @@ updateBroadcastRevenueTransactions#dfd961f5 peer:Peer balances:BroadcastRevenueB
updateStarsBalance#fb85198 balance:long = Update;
updateBusinessBotCallbackQuery#1ea2fda7 flags:# query_id:long user_id:long connection_id:string message:Message reply_to_message:flags.2?Message chat_instance:long data:flags.0?bytes = Update;
updateStarsRevenueStatus#a584b019 peer:Peer status:StarsRevenueStatus = Update;
updateBotPurchasedPaidMedia#283bd312 user_id:long payload:string qts:int = Update;
updatePaidReactionPrivacy#51ca7aec private:Bool = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -1013,12 +1016,13 @@ channelAdminLogEventActionChangeWallpaper#31bb5d52 prev_value:WallPaper new_valu
channelAdminLogEventActionChangeEmojiStatus#3ea9feb1 prev_value:EmojiStatus new_value:EmojiStatus = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeEmojiStickerSet#46d840ab prev_stickerset:InputStickerSet new_stickerset:InputStickerSet = ChannelAdminLogEventAction;
channelAdminLogEventActionToggleSignatureProfiles#60a79c79 new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantSubExtend#64642db3 prev_participant:ChannelParticipant new_participant:ChannelParticipant = ChannelAdminLogEventAction;
channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
channels.adminLogResults#ed8af74d events:Vector<ChannelAdminLogEvent> chats:Vector<Chat> users:Vector<User> = channels.AdminLogResults;
channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true forums:flags.17?true = ChannelAdminLogEventsFilter;
channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true forums:flags.17?true sub_extend:flags.18?true = ChannelAdminLogEventsFilter;
popularContact#5ce14175 client_id:long importers:int = PopularContact;
@ -1472,6 +1476,7 @@ inputStorePaymentPremiumGiftCode#a3805f3f flags:# users:Vector<InputUser> boost_
inputStorePaymentPremiumGiveaway#160544ca flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.3?true boost_peer:InputPeer additional_peers:flags.1?Vector<InputPeer> countries_iso2:flags.2?Vector<string> prize_description:flags.4?string random_id:long until_date:int currency:string amount:long = InputStorePaymentPurpose;
inputStorePaymentStarsTopup#dddd0f56 stars:long currency:string amount:long = InputStorePaymentPurpose;
inputStorePaymentStarsGift#1d741ef7 user_id:InputUser stars:long currency:string amount:long = InputStorePaymentPurpose;
inputStorePaymentStarsGiveaway#751f08fa flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.3?true stars:long boost_peer:InputPeer additional_peers:flags.1?Vector<InputPeer> countries_iso2:flags.2?Vector<string> prize_description:flags.4?string random_id:long until_date:int currency:string amount:long users:int = InputStorePaymentPurpose;
premiumGiftOption#74c34319 flags:# months:int currency:string amount:long bot_url:string store_product:flags.0?string = PremiumGiftOption;
@ -1633,11 +1638,12 @@ premiumGiftCodeOption#257e962b flags:# users:int months:int store_product:flags.
payments.checkedGiftCode#284a1096 flags:# via_giveaway:flags.2?true from_id:flags.4?Peer giveaway_msg_id:flags.3?int to_id:flags.0?long date:int months:int used_date:flags.1?int chats:Vector<Chat> users:Vector<User> = payments.CheckedGiftCode;
payments.giveawayInfo#4367daa0 flags:# participating:flags.0?true preparing_results:flags.3?true start_date:int joined_too_early_date:flags.1?int admin_disallowed_chat_id:flags.2?long disallowed_country:flags.4?string = payments.GiveawayInfo;
payments.giveawayInfoResults#cd5570 flags:# winner:flags.0?true refunded:flags.1?true start_date:int gift_code_slug:flags.0?string finish_date:int winners_count:int activated_count:int = payments.GiveawayInfo;
payments.giveawayInfoResults#e175e66f flags:# winner:flags.0?true refunded:flags.1?true start_date:int gift_code_slug:flags.3?string stars_prize:flags.4?long finish_date:int winners_count:int activated_count:flags.2?int = payments.GiveawayInfo;
prepaidGiveaway#b2539d54 id:long months:int quantity:int date:int = PrepaidGiveaway;
prepaidStarsGiveaway#9a9d77e0 id:long stars:long quantity:int boosts:int date:int = PrepaidGiveaway;
boost#2a1c8c71 flags:# gift:flags.1?true giveaway:flags.2?true unclaimed:flags.3?true id:string user_id:flags.0?long giveaway_msg_id:flags.2?int date:int expires:int used_gift_slug:flags.4?string multiplier:flags.5?int = Boost;
boost#4b3e14d6 flags:# gift:flags.1?true giveaway:flags.2?true unclaimed:flags.3?true id:string user_id:flags.0?long giveaway_msg_id:flags.2?int date:int expires:int used_gift_slug:flags.4?string multiplier:flags.5?int stars:flags.6?long = Boost;
premium.boostsList#86f8613c flags:# count:int boosts:Vector<Boost> next_offset:flags.0?string users:Vector<User> = premium.BoostsList;
@ -1795,7 +1801,7 @@ reactionNotificationsFromAll#4b9e22a0 = ReactionNotificationsFrom;
reactionsNotifySettings#56e34970 flags:# messages_notify_from:flags.0?ReactionNotificationsFrom stories_notify_from:flags.1?ReactionNotificationsFrom sound:NotificationSound show_previews:Bool = ReactionsNotifySettings;
broadcastRevenueBalances#8438f1c6 current_balance:long available_balance:long overall_revenue:long = BroadcastRevenueBalances;
broadcastRevenueBalances#c3ff71e7 flags:# withdrawal_enabled:flags.0?true current_balance:long available_balance:long overall_revenue:long = BroadcastRevenueBalances;
availableEffect#93c3e27e flags:# premium_required:flags.2?true id:long emoticon:string static_icon_id:flags.0?long effect_sticker_id:long effect_animation_id:flags.1?long = AvailableEffect;
@ -1814,7 +1820,7 @@ starsTransactionPeerAds#60682812 = StarsTransactionPeer;
starsTopupOption#bd915c0 flags:# extended:flags.1?true stars:long store_product:flags.0?string currency:string amount:long = StarsTopupOption;
starsTransaction#433aeb2b flags:# refund:flags.3?true pending:flags.4?true failed:flags.6?true gift:flags.10?true reaction:flags.11?true id:string stars:long date:int peer:StarsTransactionPeer title:flags.0?string description:flags.1?string photo:flags.2?WebDocument transaction_date:flags.5?int transaction_url:flags.5?string bot_payload:flags.7?bytes msg_id:flags.8?int extended_media:flags.9?Vector<MessageMedia> subscription_period:flags.12?int = StarsTransaction;
starsTransaction#ee7522d5 flags:# refund:flags.3?true pending:flags.4?true failed:flags.6?true gift:flags.10?true reaction:flags.11?true id:string stars:long date:int peer:StarsTransactionPeer title:flags.0?string description:flags.1?string photo:flags.2?WebDocument transaction_date:flags.5?int transaction_url:flags.5?string bot_payload:flags.7?bytes msg_id:flags.8?int extended_media:flags.9?Vector<MessageMedia> subscription_period:flags.12?int giveaway_post_id:flags.13?int = StarsTransaction;
payments.starsStatus#bbfa316c flags:# balance:long subscriptions:flags.1?Vector<StarsSubscription> subscriptions_next_offset:flags.2?string subscriptions_missing_balance:flags.4?long history:flags.3?Vector<StarsTransaction> next_offset:flags.0?string chats:Vector<Chat> users:Vector<User> = payments.StarsStatus;
@ -1848,6 +1854,10 @@ starsSubscription#538ecf18 flags:# canceled:flags.0?true can_refulfill:flags.1?t
messageReactor#4ba3a95a flags:# top:flags.0?true my:flags.1?true anonymous:flags.2?true peer_id:flags.3?Peer count:int = MessageReactor;
starsGiveawayOption#94ce852a flags:# extended:flags.0?true default:flags.1?true stars:long yearly_boosts:int store_product:flags.2?string currency:string amount:long winners:Vector<StarsGiveawayWinnersOption> = StarsGiveawayOption;
starsGiveawayWinnersOption#54236209 flags:# default:flags.0?true users:int per_user_stars:long = StarsGiveawayWinnersOption;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -2244,8 +2254,9 @@ messages.editFactCheck#589ee75 peer:InputPeer msg_id:int text:TextWithEntities =
messages.deleteFactCheck#d1da940c peer:InputPeer msg_id:int = Updates;
messages.getFactCheck#b9cdc5ee peer:InputPeer msg_id:Vector<int> = Vector<FactCheck>;
messages.requestMainWebView#c9e01e7b flags:# compact:flags.7?true peer:InputPeer bot:InputUser start_param:flags.1?string theme_params:flags.0?DataJSON platform:string = WebViewResult;
messages.sendPaidReaction#25c8fe3e flags:# private:flags.0?true peer:InputPeer msg_id:int count:int random_id:long = Updates;
messages.sendPaidReaction#9dd6a67b flags:# peer:InputPeer msg_id:int count:int random_id:long private:flags.0?Bool = Updates;
messages.togglePaidReactionPrivacy#849ad397 peer:InputPeer msg_id:int private:Bool = Bool;
messages.getPaidReactionPrivacy#472455aa = Updates;
updates.getState#edd4882a = updates.State;
updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference;
@ -2411,6 +2422,7 @@ payments.getStarsGiftOptions#d3c96bc8 flags:# user_id:flags.0?InputUser = Vector
payments.getStarsSubscriptions#32512c5 flags:# missing_balance:flags.0?true peer:InputPeer offset:string = payments.StarsStatus;
payments.changeStarsSubscription#c7770878 flags:# peer:InputPeer subscription_id:string canceled:flags.0?Bool = Bool;
payments.fulfillStarsSubscription#cc5bebb3 peer:InputPeer subscription_id:string = Bool;
payments.getStarsGiveawayOptions#bd1efd3e = Vector<StarsGiveawayOption>;
stickers.createStickerSet#9021ab67 flags:# masks:flags.0?true emojis:flags.5?true text_color:flags.6?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector<InputStickerSetItem> software:flags.3?string = messages.StickerSet;
stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet;

View File

@ -514,14 +514,16 @@ export type InlineBotSettings = {
};
export type CustomPeerType = 'premium' | 'toBeDistributed' | 'contacts' | 'nonContacts'
| 'groups' | 'channels' | 'bots' | 'excludeMuted' | 'excludeArchived' | 'excludeRead';
| 'groups' | 'channels' | 'bots' | 'excludeMuted' | 'excludeArchived' | 'excludeRead' | 'stars';
export interface CustomPeer {
isCustomPeer: true;
key?: string | number;
titleKey: string;
subtitleKey?: string;
avatarIcon: IconName;
isAvatarSquare?: boolean;
titleValue?: number;
peerColorId?: number;
customPeerAvatarColor?: string;
withPremiumGradient?: boolean;

View File

@ -1539,6 +1539,9 @@ export interface LangPair {
'GiftStarsOutgoing': {
'user': string | number;
};
'PrizeCredits': {
'count': string | number;
};
}
export type LangKey = keyof LangPair;

View File

@ -18,6 +18,14 @@ export const CUSTOM_PEER_TO_BE_DISTRIBUTED: UniqueCustomPeer = {
withPremiumGradient: true,
};
export const CUSTOM_PEER_STAR: UniqueCustomPeer = {
isCustomPeer: true,
type: 'stars',
titleKey: 'Stars',
avatarIcon: 'star',
peerColorId: 1,
};
export const CUSTOM_PEER_INCLUDED_CHAT_TYPES: UniqueCustomPeer[] = [
{
isCustomPeer: true,