This commit is contained in:
zubiden 2024-12-20 11:37:16 +01:00 committed by Alexander Zinchuk
parent 99c3c62f6c
commit 50621e91d3
36 changed files with 428 additions and 133 deletions

View File

@ -86,6 +86,7 @@ export interface GramJsAppConfig extends LimitsConfig {
stars_gifts_enabled?: boolean;
stargifts_message_length_max?: number;
stargifts_convert_period_max?: number;
starref_start_param_prefixes?: string[];
}
function buildEmojiSounds(appConfig: GramJsAppConfig) {
@ -171,5 +172,6 @@ export function buildAppConfig(json: GramJs.TypeJSONValue, hash: number): ApiApp
isStarsGiftEnabled: appConfig.stars_gifts_enabled,
starGiftMaxMessageLength: appConfig.stargifts_message_length_max,
starGiftMaxConvertPeriod: appConfig.stargifts_convert_period_max,
starRefStartPrefixes: appConfig.starref_start_param_prefixes,
};
}

View File

@ -21,6 +21,7 @@ import type {
ApiReceipt,
ApiStarGift,
ApiStarGiveawayOption,
ApiStarsAmount,
ApiStarsGiveawayWinnerOption,
ApiStarsSubscription,
ApiStarsTransaction,
@ -461,6 +462,13 @@ export function buildApiStarsGiftOptions(option: GramJs.StarsGiftOption): ApiSta
};
}
export function buildApiStarsAmount(amount: GramJs.StarsAmount): ApiStarsAmount {
return {
amount: amount.amount.toJSNumber(),
nanos: amount.nanos,
};
}
export function buildApiStarsGiveawayWinnersOption(
option: GramJs.StarsGiveawayWinnersOption,
): ApiStarsGiveawayWinnerOption {
@ -528,7 +536,7 @@ export function buildApiStarsTransactionPeer(peer: GramJs.TypeStarsTransactionPe
export function buildApiStarsTransaction(transaction: GramJs.StarsTransaction): ApiStarsTransaction {
const {
date, id, peer, stars, description, photo, title, refund, extendedMedia, failed, msgId, pending, gift, reaction,
subscriptionPeriod, stargift, giveawayPostId,
subscriptionPeriod, stargift, giveawayPostId, starrefCommissionPermille,
} = transaction;
if (photo) {
@ -538,11 +546,13 @@ export function buildApiStarsTransaction(transaction: GramJs.StarsTransaction):
const boughtExtendedMedia = extendedMedia?.map((m) => buildMessageMediaContent(m))
.filter(Boolean) as BoughtPaidMedia[];
const starRefCommision = starrefCommissionPermille ? starrefCommissionPermille / 10 : undefined;
return {
id,
date,
peer: buildApiStarsTransactionPeer(peer),
stars: stars.toJSNumber(),
stars: buildApiStarsAmount(stars),
title,
description,
photo: photo && buildApiWebDocument(photo),
@ -556,6 +566,7 @@ export function buildApiStarsTransaction(transaction: GramJs.StarsTransaction):
isReaction: reaction,
starGift: stargift && buildApiStarGift(stargift),
giveawayPostId,
starRefCommision,
};
}

View File

@ -1131,9 +1131,10 @@ export async function getChatByPhoneNumber(phoneNumber: string) {
return processResolvedPeer(result);
}
export async function getChatByUsername(username: string) {
export async function getChatByUsername(username: string, referrer?: string) {
const result = await invokeRequest(new GramJs.contacts.ResolveUsername({
username,
referer: referrer,
}));
return processResolvedPeer(result);

View File

@ -19,6 +19,7 @@ import {
buildApiPremiumPromo,
buildApiReceipt,
buildApiStarGift,
buildApiStarsAmount,
buildApiStarsGiftOptions,
buildApiStarsGiveawayOptions,
buildApiStarsSubscription,
@ -534,7 +535,7 @@ export async function fetchStarsStatus() {
history: result.history?.map(buildApiStarsTransaction),
nextSubscriptionOffset: result.subscriptionsNextOffset,
subscriptions: result.subscriptions?.map(buildApiStarsSubscription),
balance: result.balance.toJSNumber(),
balance: buildApiStarsAmount(result.balance),
};
}
@ -564,7 +565,7 @@ export async function fetchStarsTransactions({
return {
nextOffset: result.nextOffset,
history: result.history?.map(buildApiStarsTransaction),
balance: result.balance.toJSNumber(),
balance: buildApiStarsAmount(result.balance),
};
}
@ -610,7 +611,7 @@ export async function fetchStarsSubscriptions({
return {
nextOffset: result.subscriptionsNextOffset,
subscriptions: result.subscriptions.map(buildApiStarsSubscription),
balance: result.balance.toJSNumber(),
balance: buildApiStarsAmount(result.balance),
};
}

View File

@ -51,6 +51,7 @@ import {
buildLangStrings,
buildPrivacyKey,
} from '../apiBuilders/misc';
import { buildApiStarsAmount } from '../apiBuilders/payments';
import { buildApiEmojiStatus, buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
import {
buildApiReaction,
@ -1059,7 +1060,7 @@ export function updater(update: Update) {
} else if (update instanceof GramJs.UpdateStarsBalance) {
sendApiUpdate({
'@type': 'updateStarsBalance',
balance: update.balance.toJSNumber(),
balance: buildApiStarsAmount(update.balance),
});
} else if (update instanceof GramJs.UpdatePaidReactionPrivacy) {
sendApiUpdate({

View File

@ -243,6 +243,7 @@ export interface ApiAppConfig {
isStarsGiftEnabled?: boolean;
starGiftMaxMessageLength?: number;
starGiftMaxConvertPeriod?: number;
starRefStartPrefixes?: string[];
}
export interface ApiConfig {

View File

@ -302,6 +302,11 @@ export type ApiCheckedGiftCode = {
usedAt?: number;
};
export interface ApiStarsAmount {
amount: number;
nanos: number;
}
export interface ApiStarsTransactionPeerUnsupported {
type: 'unsupported';
}
@ -349,7 +354,7 @@ export interface ApiStarsTransaction {
id?: string;
peer: ApiStarsTransactionPeer;
messageId?: number;
stars: number;
stars: ApiStarsAmount;
isRefund?: true;
isGift?: true;
starGift?: ApiStarGift;
@ -364,6 +369,7 @@ export interface ApiStarsTransaction {
photo?: ApiWebDocument;
extendedMedia?: BoughtPaidMedia[];
subscriptionPeriod?: number;
starRefCommision?: number;
}
export interface ApiStarsSubscription {

View File

@ -34,6 +34,7 @@ import type {
import type {
ApiEmojiInteraction, ApiError, ApiNotifyException, ApiSessionData,
} from './misc';
import type { ApiStarsAmount } from './payments';
import type { LangPackStringValue } from './settings';
import type { ApiStealthMode, ApiStory, ApiStorySkipped } from './stories';
import type {
@ -760,7 +761,7 @@ export type ApiUpdatePremiumFloodWait = {
export type ApiUpdateStarsBalance = {
'@type': 'updateStarsBalance';
balance: number;
balance: ApiStarsAmount;
};
export type ApiUpdateDeleteProfilePhoto = {

View File

@ -1377,6 +1377,7 @@
"StarsSubscribeInfoLinkText" = "Terms of Service";
"StarsSubscribeInfoLink" = "https://telegram.org/tos/stars";
"StarsPerMonth" = "⭐️{amount}/month";
"StarsBalance" = "Balance";
"OpenApp" = "Open App";
"PopularApps" = "Popular Apps";
"SearchApps" = "Search Apps";

View File

@ -95,7 +95,7 @@
width: 1.125rem;
height: 1.125rem;
background-size: 1.125rem;
vertical-align: -2px;
vertical-align: -4px;
}
}

View File

@ -2,17 +2,19 @@ import type { FC } from '../../../lib/teact/teact';
import React, { memo, useEffect } from '../../../lib/teact/teact';
import { getActions, withGlobal } from '../../../global';
import type { ApiStarsAmount } from '../../../api/types';
import { SettingsScreens } from '../../../types';
import { FAQ_URL, PRIVACY_URL } from '../../../config';
import { formatStarsAmount } from '../../../global/helpers/payments';
import {
selectIsGiveawayGiftsPurchaseAvailable,
selectIsPremiumPurchaseBlocked,
} from '../../../global/selectors';
import { formatInteger } from '../../../util/textFormat';
import useFlag from '../../../hooks/useFlag';
import useHistoryBack from '../../../hooks/useHistoryBack';
import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback';
import useOldLang from '../../../hooks/useOldLang';
@ -33,7 +35,7 @@ type StateProps = {
currentUserId?: string;
canBuyPremium?: boolean;
isGiveawayAvailable?: boolean;
starsBalance?: number;
starsBalance?: ApiStarsAmount;
shouldDisplayStars?: boolean;
};
@ -59,6 +61,7 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
const [isSupportDialogOpen, openSupportDialog, closeSupportDialog] = useFlag(false);
const lang = useLang();
const oldLang = useOldLang();
useEffect(() => {
@ -188,7 +191,9 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
>
{oldLang('MenuTelegramStars')}
{Boolean(starsBalance) && (
<span className="settings-item__current-value">{formatInteger(starsBalance)}</span>
<span className="settings-item__current-value">
{formatStarsAmount(lang, starsBalance)}
</span>
)}
</ListItem>
)}

View File

@ -296,7 +296,6 @@ type StateProps = {
canTranscribeVoice?: boolean;
viaBusinessBot?: ApiUser;
effect?: ApiAvailableEffect;
availableStars?: number;
poll?: ApiPoll;
};
@ -418,7 +417,6 @@ const Message: FC<OwnProps & StateProps> = ({
canTranscribeVoice,
viaBusinessBot,
effect,
availableStars,
poll,
onIntersectPinnedMessage,
}) => {
@ -1062,7 +1060,6 @@ const Message: FC<OwnProps & StateProps> = ({
noRecentReactors={isChannel}
tags={tags}
isCurrentUserPremium={isPremium}
availableStars={availableStars}
/>
);
}
@ -1701,7 +1698,6 @@ const Message: FC<OwnProps & StateProps> = ({
observeIntersection={observeIntersectionForPlaying}
noRecentReactors={isChannel}
tags={tags}
availableStars={availableStars}
/>
)}
</div>
@ -1852,7 +1848,6 @@ export default memo(withGlobal<OwnProps>(
const effect = effectId ? global.availableEffectById[effectId] : undefined;
const { balance: availableStars } = global.stars || {};
const poll = selectPollFromMessage(global, message);
return {
@ -1941,7 +1936,6 @@ export default memo(withGlobal<OwnProps>(
canTranscribeVoice,
viaBusinessBot,
effect,
availableStars,
poll,
};
},

View File

@ -40,14 +40,13 @@ type OwnProps = {
recentReactors?: ApiPeer[];
className?: string;
chosenClassName?: string;
availableStars?: number;
observeIntersection?: ObserveFn;
onClick?: (reaction: ApiReaction) => void;
onPaidClick?: (count: number) => void;
};
function selectAreStarsLoaded(global: GlobalState) {
return Boolean(global.stars);
function selectStarsState(global: GlobalState) {
return global.stars;
}
const ReactionButton = ({
@ -57,7 +56,6 @@ const ReactionButton = ({
recentReactors,
className,
chosenClassName,
availableStars,
chatId,
messageId,
observeIntersection,
@ -78,7 +76,8 @@ const ReactionButton = ({
const isPaid = reaction.reaction.type === 'paid';
const areStarsLoaded = useSelector(selectAreStarsLoaded);
const starsState = useSelector(selectStarsState);
const areStarsLoaded = Boolean(starsState);
const handlePaidClick = useLastCallback((count = 1) => {
onPaidClick?.(count);
@ -120,7 +119,7 @@ const ReactionButton = ({
const button = ref.current;
if (!amount || !button || amount === prevReaction?.localAmount) return;
if (areStarsLoaded && (!availableStars || amount > availableStars)) {
if (areStarsLoaded && amount > starsState.balance.amount) {
openStarsBalanceModal({
originReaction: {
chatId,
@ -153,7 +152,7 @@ const ReactionButton = ({
duration: 500 * currentScale,
easing: 'ease-out',
});
}, [reaction, availableStars, areStarsLoaded, chatId, messageId]);
}, [reaction, starsState?.balance, areStarsLoaded, chatId, messageId]);
const prevAmount = usePrevious(reaction.localAmount);

View File

@ -36,7 +36,6 @@ type OwnProps = {
isCurrentUserPremium?: boolean;
observeIntersection?: ObserveFn;
noRecentReactors?: boolean;
availableStars?: number;
};
const MAX_RECENT_AVATARS = 3;
@ -52,7 +51,6 @@ const Reactions: FC<OwnProps> = ({
noRecentReactors,
isCurrentUserPremium,
tags,
availableStars,
}) => {
const {
toggleReaction,
@ -216,7 +214,6 @@ const Reactions: FC<OwnProps> = ({
onClick={handleClick}
onPaidClick={handlePaidClick}
observeIntersection={observeIntersection}
availableStars={availableStars}
/>
)
))}

View File

@ -7,6 +7,7 @@ import { getActions, withGlobal } from '../../../global';
import type {
ApiPremiumGiftCodeOption,
ApiStarGift,
ApiStarsAmount,
ApiUser,
} from '../../../api/types';
import type { StarGiftCategory, TabState } from '../../../global/types';
@ -45,7 +46,7 @@ type StateProps = {
boostPerSentGift?: number;
starGiftsById?: Record<string, ApiStarGift>;
starGiftCategoriesByName: Record<StarGiftCategory, string[]>;
starBalance?: number;
starBalance?: ApiStarsAmount;
user?: ApiUser;
};

View File

@ -3,7 +3,9 @@ import React, {
} from '../../../lib/teact/teact';
import { getActions, getGlobal, withGlobal } from '../../../global';
import type { ApiChat, ApiMessage, ApiUser } from '../../../api/types';
import type {
ApiChat, ApiMessage, ApiStarsAmount, ApiUser,
} from '../../../api/types';
import type { TabState } from '../../../global/types';
import type { CustomPeer } from '../../../types';
@ -38,7 +40,7 @@ type StateProps = {
message?: ApiMessage;
chat?: ApiChat;
maxAmount: number;
starBalance?: number;
starBalance?: ApiStarsAmount;
defaultPrivacy?: boolean;
};

View File

@ -1,28 +1,30 @@
import React, { memo } from '../../../lib/teact/teact';
import buildClassName from '../../../util/buildClassName';
import { formatInteger } from '../../../util/textFormat';
import type { ApiStarsAmount } from '../../../api/types';
import useOldLang from '../../../hooks/useOldLang';
import { formatStarsAmount } from '../../../global/helpers/payments';
import buildClassName from '../../../util/buildClassName';
import useLang from '../../../hooks/useLang';
import StarIcon from '../../common/icons/StarIcon';
import styles from './StarsBalanceModal.module.scss';
type OwnProps = {
balance?: number;
balance?: ApiStarsAmount;
className?: string;
};
const BalanceBlock = ({ balance, className }: OwnProps) => {
const lang = useOldLang();
const lang = useLang();
return (
<div className={buildClassName(styles.balance, className)}>
<span className={styles.smallerText}>{lang('StarsBalance')}</span>
<div className={styles.balanceBottom}>
<StarIcon type="gold" size="middle" />
{balance !== undefined ? formatInteger(balance) : '…'}
{balance !== undefined ? formatStarsAmount(lang, balance) : '…'}
</div>
</div>
);

View File

@ -81,7 +81,7 @@ const StarsBalanceModal = ({
|| originReaction?.amount
|| originGift?.gift.stars
|| topup?.balanceNeeded;
const starsNeeded = ongoingTransactionAmount ? ongoingTransactionAmount - (balance || 0) : undefined;
const starsNeeded = ongoingTransactionAmount ? ongoingTransactionAmount - (balance?.amount || 0) : undefined;
const starsNeededText = useMemo(() => {
const global = getGlobal();

View File

@ -150,7 +150,7 @@ const StarPaymentModal = ({
return;
}
if (amount > balance) {
if (amount > balance.amount) {
openStarsBalanceModal({
originStarsPayment: modal,
});

View File

@ -1,9 +1,13 @@
import type { ApiStarsTransaction } from '../../../../api/types';
import type { ApiStarsAmount, ApiStarsTransaction } from '../../../../api/types';
import type { OldLangFn } from '../../../../hooks/useOldLang';
import { buildStarsTransactionCustomPeer } from '../../../../global/helpers/payments';
import { formatPercent } from '../../../../util/textFormat';
export function getTransactionTitle(lang: OldLangFn, transaction: ApiStarsTransaction) {
if (transaction.starRefCommision) {
return lang('StarTransactionCommission', formatPercent(transaction.starRefCommision));
}
if (transaction.extendedMedia) return lang('StarMediaPurchase');
if (transaction.subscriptionPeriod) return transaction.title || lang('StarSubscriptionPurchase');
if (transaction.isReaction) return lang('StarsReactionsSent');
@ -11,7 +15,7 @@ export function getTransactionTitle(lang: OldLangFn, transaction: ApiStarsTransa
if (transaction.isMyGift) return lang('StarsGiftSent');
if (transaction.isGift) return lang('StarsGiftReceived');
if (transaction.starGift) {
return transaction.stars < 0 ? lang('Gift2TransactionSent') : lang('Gift2ConvertedTitle');
return isNegativeStarsAmount(transaction.stars) ? lang('Gift2TransactionSent') : lang('Gift2ConvertedTitle');
}
const customPeer = (transaction.peer && transaction.peer.type !== 'peer'
@ -21,3 +25,8 @@ export function getTransactionTitle(lang: OldLangFn, transaction: ApiStarsTransa
return transaction.title;
}
export function isNegativeStarsAmount(starsAmount: ApiStarsAmount) {
if (starsAmount.amount) return starsAmount.amount < 0;
return starsAmount.nanos < 0;
}

View File

@ -14,9 +14,11 @@ import { selectPeer } from '../../../../global/selectors';
import buildClassName from '../../../../util/buildClassName';
import { formatDateTimeToString } from '../../../../util/dates/dateFormat';
import { CUSTOM_PEER_PREMIUM } from '../../../../util/objects/customPeer';
import { getTransactionTitle } from '../helpers/transaction';
import renderText from '../../../common/helpers/renderText';
import { getTransactionTitle, isNegativeStarsAmount } from '../helpers/transaction';
import useSelector from '../../../../hooks/data/useSelector';
import useLang from '../../../../hooks/useLang';
import useLastCallback from '../../../../hooks/useLastCallback';
import useOldLang from '../../../../hooks/useOldLang';
@ -47,24 +49,25 @@ const StarsTransactionItem = ({ transaction, className }: OwnProps) => {
extendedMedia,
subscriptionPeriod,
} = transaction;
const lang = useOldLang();
const lang = useLang();
const oldLang = useOldLang();
const peerId = transactionPeer.type === 'peer' ? transactionPeer.id : undefined;
const peer = useSelector(selectOptionalPeer(peerId));
const data = useMemo(() => {
let title = getTransactionTitle(lang, transaction);
let title = getTransactionTitle(oldLang, transaction);
let description;
let status: string | undefined;
let avatarPeer: ApiPeer | CustomPeer | undefined;
if (transaction.peer.type === 'peer') {
description = peer && getSenderTitle(lang, peer);
description = peer && getSenderTitle(oldLang, peer);
avatarPeer = peer || CUSTOM_PEER_PREMIUM;
} else {
const customPeer = buildStarsTransactionCustomPeer(transaction.peer);
title = customPeer.title || lang(customPeer.titleKey!);
description = lang(customPeer.subtitleKey!);
title = customPeer.title || oldLang(customPeer.titleKey!);
description = oldLang(customPeer.subtitleKey!);
avatarPeer = customPeer;
}
@ -73,15 +76,15 @@ const StarsTransactionItem = ({ transaction, className }: OwnProps) => {
}
if (transaction.isRefund) {
status = lang('StarsRefunded');
status = oldLang('StarsRefunded');
}
if (transaction.hasFailed) {
status = lang('StarsFailed');
status = oldLang('StarsFailed');
}
if (transaction.isPending) {
status = lang('StarsPending');
status = oldLang('StarsPending');
}
return {
@ -90,7 +93,7 @@ const StarsTransactionItem = ({ transaction, className }: OwnProps) => {
avatarPeer,
status,
};
}, [lang, peer, transaction]);
}, [oldLang, peer, transaction]);
const handleClick = useLastCallback(() => {
openStarsTransactionModal({ transaction });
@ -107,15 +110,19 @@ const StarsTransactionItem = ({ transaction, className }: OwnProps) => {
</div>
<div className={styles.info}>
<h3 className={styles.title}>{data.title}</h3>
<p className={styles.description}>{data.description}</p>
{data.description && (
<p className={styles.description}>{renderText(data.description)}</p>
)}
<p className={styles.date}>
{formatDateTimeToString(date * 1000, lang.code, true)}
{formatDateTimeToString(date * 1000, oldLang.code, true)}
{data.status && ` — (${data.status})`}
</p>
</div>
<div className={styles.stars}>
<span className={buildClassName(styles.amount, stars < 0 ? styles.negative : styles.positive)}>
{formatStarsTransactionAmount(stars)}
<span
className={buildClassName(styles.amount, isNegativeStarsAmount(stars) ? styles.negative : styles.positive)}
>
{formatStarsTransactionAmount(lang, stars)}
</span>
<StarIcon className={styles.star} type="gold" size="adaptive" />
</div>

View File

@ -19,8 +19,9 @@ import {
import buildClassName from '../../../../util/buildClassName';
import { copyTextToClipboard } from '../../../../util/clipboard';
import { formatDateTimeToString } from '../../../../util/dates/dateFormat';
import { getTransactionTitle } from '../helpers/transaction';
import { getTransactionTitle, isNegativeStarsAmount } from '../helpers/transaction';
import useLang from '../../../../hooks/useLang';
import useLastCallback from '../../../../hooks/useLastCallback';
import useOldLang from '../../../../hooks/useOldLang';
import usePrevious from '../../../../hooks/usePrevious';
@ -51,6 +52,8 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
modal, peer, canPlayAnimatedEmojis, topSticker,
}) => {
const { showNotification, openMediaViewer, closeStarsTransactionModal } = getActions();
const lang = useLang();
const oldLang = useOldLang();
const { transaction } = modal || {};
@ -70,7 +73,7 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
}
const {
giveawayPostId, photo,
giveawayPostId, photo, stars,
} = transaction;
const customPeer = (transaction.peer && transaction.peer.type !== 'peer'
@ -130,8 +133,10 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
{title && <h1 className={styles.title}>{title}</h1>}
<p className={styles.description}>{description}</p>
<p className={styles.amount}>
<span className={buildClassName(styles.amount, transaction.stars < 0 ? styles.negative : styles.positive)}>
{formatStarsTransactionAmount(transaction.stars)}
<span
className={buildClassName(styles.amount, isNegativeStarsAmount(stars) ? styles.negative : styles.positive)}
>
{formatStarsTransactionAmount(lang, stars)}
</span>
<StarIcon type="gold" size="middle" />
</p>
@ -140,9 +145,26 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
const tableData: TableData = [];
if (transaction.starRefCommision) {
tableData.push([
oldLang('StarsTransaction.StarRefReason.Title'),
oldLang('StarsTransaction.StarRefReason.Program'),
]);
}
let peerLabel;
if (isNegativeStarsAmount(stars) || transaction.isMyGift) {
peerLabel = oldLang('Stars.Transaction.To');
} else if (transaction.starRefCommision) {
peerLabel = oldLang('StarsTransaction.StarRefReason.Miniapp');
} else if (peerId) {
peerLabel = oldLang('Star.Transaction.From');
} else {
peerLabel = oldLang('Stars.Transaction.Via');
}
tableData.push([
oldLang(transaction.stars < 0 || transaction.isMyGift ? 'Stars.Transaction.To'
: peerId ? 'Star.Transaction.From' : 'Stars.Transaction.Via'),
peerLabel,
peerId ? { chatId: peerId } : toName || '',
]);
@ -198,7 +220,7 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
tableData,
footer,
};
}, [transaction, oldLang, peer, topSticker, canPlayAnimatedEmojis]);
}, [transaction, oldLang, lang, peer, topSticker, canPlayAnimatedEmojis]);
const prevModalData = usePrevious(starModalData);
const renderingModalData = prevModalData || starModalData;
@ -222,7 +244,7 @@ export default memo(withGlobal<OwnProps>(
const peer = peerId ? selectPeer(global, peerId) : undefined;
const starCount = modal?.transaction.stars;
const starsGiftSticker = modal?.transaction.isGift && selectGiftStickerForStars(global, starCount);
const starsGiftSticker = modal?.transaction.isGift && selectGiftStickerForStars(global, starCount?.amount);
const starGiftStickerId = modal?.transaction.starGift?.stickerId;
const starGiftSticker = starGiftStickerId && selectStarGiftSticker(global, starGiftStickerId);

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-v45';
export const LANG_CACHE_NAME = 'tt-lang-packs-v46';
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

@ -2912,14 +2912,15 @@ export async function migrateChat<T extends GlobalState>(
export async function fetchChatByUsername<T extends GlobalState>(
global: T,
username: string,
referrer?: string,
) {
global = getGlobal();
const localChat = selectChatByUsername(global, username);
const localChat = !referrer ? selectChatByUsername(global, username) : undefined;
if (localChat && !localChat.isMin) {
return localChat;
}
const { chat, user } = await callApi('getChatByUsername', username) || {};
const { chat, user } = await callApi('getChatByUsername', username, referrer) || {};
if (!chat) {
return undefined;
}
@ -3047,7 +3048,16 @@ async function openChatByUsername<T extends GlobalState>(
actions.openChat({ id: TMP_CHAT_ID, tabId });
}
const chat = await fetchChatByUsername(global, username);
const starRefStartPrefixes = global.appConfig?.starRefStartPrefixes;
let referrer = '';
if (startParam && starRefStartPrefixes?.length) {
const prefix = starRefStartPrefixes.find((p) => startParam.startsWith(p));
if (prefix) {
referrer = startParam.slice(prefix.length);
}
}
const chat = await fetchChatByUsername(global, username, referrer);
if (!chat) {
if (!isCurrentChat) {
actions.openPreviousChat({ tabId });

View File

@ -126,7 +126,7 @@ addActionHandler('sendStarGift', async (global, actions, payload): Promise<void>
if (balance === undefined) return;
if (balance < gift.stars) {
if (balance.amount < gift.stars) {
actions.openStarsBalanceModal({ tabId });
return;
}

View File

@ -119,7 +119,7 @@ addActionHandler('openStarsBalanceModal', (global, actions, payload): ActionRetu
const starBalance = global.stars?.balance;
if (!shouldIgnoreBalance && starBalance && topup && topup.balanceNeeded <= starBalance) {
if (!shouldIgnoreBalance && starBalance && topup && topup.balanceNeeded <= starBalance.amount) {
actions.showNotification({
message: langProvider.oldTranslate('StarsTopupLinkEnough'),
actionText: langProvider.oldTranslate('StarsTopupLinkTopupAnyway'),

View File

@ -2,14 +2,15 @@ import type {
ApiInputInvoice,
ApiMessage,
ApiRequestInputInvoice,
ApiStarsAmount,
ApiStarsTransaction,
ApiStarsTransactionPeer,
ApiStarsTransactionPeerPeer,
} from '../../api/types';
import type { CustomPeer } from '../../types';
import type { LangFn } from '../../util/localization';
import type { GlobalState } from '../types';
import { formatInteger } from '../../util/textFormat';
import { selectChat, selectUser } from '../selectors';
export function getRequestInputInvoice<T extends GlobalState>(
@ -247,12 +248,17 @@ export function buildStarsTransactionCustomPeer(
};
}
export function formatStarsTransactionAmount(amount: number) {
export function formatStarsTransactionAmount(lang: LangFn, starsAmount: ApiStarsAmount) {
const amount = starsAmount.amount + starsAmount.nanos / 1e9;
if (amount < 0) {
return `- ${formatInteger(Math.abs(amount))}`;
return `- ${lang.number(Math.abs(amount))}`;
}
return `+ ${formatInteger(amount)}`;
return `+ ${lang.number(amount)}`;
}
export function formatStarsAmount(lang: LangFn, starsAmount: ApiStarsAmount) {
return lang.number(starsAmount.amount + starsAmount.nanos / 1e9);
}
export function getStarsTransactionFromGift(message: ApiMessage): ApiStarsTransaction | undefined {
@ -264,7 +270,10 @@ export function getStarsTransactionFromGift(message: ApiMessage): ApiStarsTransa
return {
id: transactionId!,
stars: stars!,
stars: {
amount: stars!,
nanos: 0,
},
peer: {
type: 'peer',
id: message.isOutgoing ? message.chatId : (message.senderId || message.chatId),
@ -284,7 +293,10 @@ export function getPrizeStarsTransactionFromGiveaway(message: ApiMessage): ApiSt
return {
id: transactionId!,
stars: stars!,
stars: {
amount: stars!,
nanos: 0,
},
peer: {
type: 'peer',
id: targetChatId!,

View File

@ -1,6 +1,7 @@
import type {
ApiReceiptRegular,
ApiReceiptStars,
ApiStarsAmount,
ApiStarsSubscription,
ApiStarsTransaction,
} from '../../api/types';
@ -131,7 +132,7 @@ export function closeInvoice<T extends GlobalState>(
}
export function updateStarsBalance<T extends GlobalState>(
global: T, balance: number,
global: T, balance: ApiStarsAmount,
): T {
return {
...global,
@ -232,7 +233,10 @@ export function openStarsTransactionFromReceipt<T extends GlobalState>(
type: 'peer',
id: receipt.botId,
},
stars: receipt.totalAmount,
stars: {
amount: receipt.totalAmount,
nanos: 0,
},
date: receipt.date,
title: receipt.title,
description: receipt.description,

View File

@ -68,6 +68,7 @@ import type {
ApiSponsoredMessage,
ApiStarGift,
ApiStarGiveawayOption,
ApiStarsAmount,
ApiStarsSubscription,
ApiStarsTransaction,
ApiStarTopupOption,
@ -1290,7 +1291,7 @@ export type GlobalState = {
stars?: {
topupOptions: ApiStarTopupOption[];
balance: number;
balance: ApiStarsAmount;
history: StarsTransactionHistory;
subscriptions?: StarsSubscriptions;
};

View File

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

File diff suppressed because one or more lines are too long

View File

@ -125,8 +125,8 @@ messageActionChannelMigrateFrom#ea3948e9 title:string chat_id:long = MessageActi
messageActionPinMessage#94bd38ed = MessageAction;
messageActionHistoryClear#9fbab604 = MessageAction;
messageActionGameScore#92a72876 game_id:long score:int = MessageAction;
messageActionPaymentSentMe#8f31b327 flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string charge:PaymentCharge = MessageAction;
messageActionPaymentSent#96163f56 flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long invoice_slug:flags.0?string = MessageAction;
messageActionPaymentSentMe#ffa00ccc flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string charge:PaymentCharge subscription_until_date:flags.4?int = MessageAction;
messageActionPaymentSent#c624b16e flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long invoice_slug:flags.0?string subscription_until_date:flags.4?int = MessageAction;
messageActionPhoneCall#80e11a7f flags:# video:flags.2?true call_id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = MessageAction;
messageActionScreenshotTaken#4792929b = MessageAction;
messageActionCustomAction#fae69f56 message:string = MessageAction;
@ -195,7 +195,7 @@ inputReportReasonGeoIrrelevant#dbd4feed = ReportReason;
inputReportReasonFake#f5ddd6e7 = ReportReason;
inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
inputReportReasonPersonalDetails#9ec7863d = ReportReason;
userFull#1f58e369 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true can_view_revenue:flags2.9?true bot_can_manage_emoji_status:flags2.10?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int stargifts_count:flags2.8?int = UserFull;
userFull#979d2376 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true can_view_revenue:flags2.9?true bot_can_manage_emoji_status:flags2.10?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int stargifts_count:flags2.8?int starref_program:flags2.11?StarRefProgram = UserFull;
contact#145ade0b user_id:long mutual:Bool = Contact;
importedContact#c13e3c50 user_id:long client_id:long = ImportedContact;
contactStatus#16d9703b user_id:long status:UserStatus = ContactStatus;
@ -368,12 +368,11 @@ updateBotEditBusinessMessage#7df587c flags:# connection_id:string message:Messag
updateBotDeleteBusinessMessage#a02a982e connection_id:string peer:Peer messages:Vector<int> qts:int = Update;
updateNewStoryReaction#1824e40b story_id:int peer:Peer reaction:Reaction = Update;
updateBroadcastRevenueTransactions#dfd961f5 peer:Peer balances:BroadcastRevenueBalances = Update;
updateStarsBalance#fb85198 balance:long = Update;
updateStarsBalance#4e80a379 balance:StarsAmount = 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;
updateBotSubscriptionExpire#2d13c6ee user_id:long payload:string invoice_slug:string until_date:int qts:int = 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;
@ -1341,12 +1340,12 @@ starsTransactionPeer#d80da15d peer:Peer = StarsTransactionPeer;
starsTransactionPeerAds#60682812 = StarsTransactionPeer;
starsTransactionPeerAPI#f9677aad = StarsTransactionPeer;
starsTopupOption#bd915c0 flags:# extended:flags.1?true stars:long store_product:flags.0?string currency:string amount:long = StarsTopupOption;
starsTransaction#35d4f276 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 stargift:flags.14?StarGift floodskip_number:flags.15?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;
starsTransaction#64dfc926 flags:# refund:flags.3?true pending:flags.4?true failed:flags.6?true gift:flags.10?true reaction:flags.11?true id:string stars:StarsAmount 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 stargift:flags.14?StarGift floodskip_number:flags.15?int starref_commission_permille:flags.16?int starref_peer:flags.17?Peer starref_amount:flags.17?StarsAmount = StarsTransaction;
payments.starsStatus#6c9ce8ed flags:# balance:StarsAmount 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;
geoPointAddress#de4c5d93 flags:# country_iso2:string state:flags.0?string city:flags.1?string street:flags.2?string = GeoPointAddress;
starsRevenueStatus#79342946 flags:# withdrawal_enabled:flags.0?true current_balance:long available_balance:long overall_revenue:long next_withdrawal_at:flags.1?int = StarsRevenueStatus;
starsRevenueStatus#febe5491 flags:# withdrawal_enabled:flags.0?true current_balance:StarsAmount available_balance:StarsAmount overall_revenue:StarsAmount next_withdrawal_at:flags.1?int = StarsRevenueStatus;
payments.starsRevenueStats#c92bb73b revenue_graph:StatsGraph status:StarsRevenueStatus usd_rate:double = payments.StarsRevenueStats;
payments.starsRevenueWithdrawalUrl#1dab80b7 url:string = payments.StarsRevenueWithdrawalUrl;
payments.starsRevenueAdsAccountUrl#394e7f21 url:string = payments.StarsRevenueAdsAccountUrl;
@ -1372,6 +1371,13 @@ reportResultReported#8db33c4b = ReportResult;
messages.botPreparedInlineMessage#8ecf0511 id:string expire_date:int = messages.BotPreparedInlineMessage;
messages.preparedInlineMessage#ff57708d query_id:long result:BotInlineResult peer_types:Vector<InlineQueryPeerType> cache_time:int users:Vector<User> = messages.PreparedInlineMessage;
botAppSettings#c99b1950 flags:# placeholder_path:flags.0?bytes background_color:flags.1?int background_dark_color:flags.2?int header_color:flags.3?int header_dark_color:flags.4?int = BotAppSettings;
starRefProgram#dd0c66f2 flags:# bot_id:long commission_permille:int duration_months:flags.0?int end_date:flags.1?int daily_revenue_per_user:flags.2?StarsAmount = StarRefProgram;
connectedBotStarRef#19a13f71 flags:# revoked:flags.1?true url:string date:int bot_id:long commission_permille:int duration_months:flags.0?int participants:long revenue:long = ConnectedBotStarRef;
payments.connectedStarRefBots#98d5ea1d count:int connected_bots:Vector<ConnectedBotStarRef> users:Vector<User> = payments.ConnectedStarRefBots;
payments.suggestedStarRefBots#b4d5d859 flags:# count:int suggested_bots:Vector<StarRefProgram> users:Vector<User> next_offset:flags.0?string = payments.SuggestedStarRefBots;
starsAmount#bbb6b4a3 amount:long nanos:int = StarsAmount;
messages.foundStickersNotModified#6010c534 flags:# next_offset:flags.0?int = messages.FoundStickers;
messages.foundStickers#82c9e290 flags:# next_offset:flags.0?int hash:long stickers:Vector<Document> = messages.FoundStickers;
---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;
@ -1443,7 +1449,7 @@ contacts.block#2e2e8734 flags:# my_stories_from:flags.0?true id:InputPeer = Bool
contacts.unblock#b550d328 flags:# my_stories_from:flags.0?true id:InputPeer = Bool;
contacts.getBlocked#9a868f80 flags:# my_stories_from:flags.0?true offset:int limit:int = contacts.Blocked;
contacts.search#11f812d8 q:string limit:int = contacts.Found;
contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer;
contacts.resolveUsername#725afbbc flags:# username:string referer:flags.0?string = contacts.ResolvedPeer;
contacts.getTopPeers#973478b6 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true forward_users:flags.4?true forward_chats:flags.5?true groups:flags.10?true channels:flags.15?true bots_app:flags.16?true offset:int limit:int hash:long = contacts.TopPeers;
contacts.addContact#e8f463d0 flags:# add_phone_privacy_exception:flags.0?true id:InputUser first_name:string last_name:string phone:string = Updates;
contacts.resolvePhone#8af94344 phone:string = contacts.ResolvedPeer;

View File

@ -151,8 +151,8 @@ messageActionChannelMigrateFrom#ea3948e9 title:string chat_id:long = MessageActi
messageActionPinMessage#94bd38ed = MessageAction;
messageActionHistoryClear#9fbab604 = MessageAction;
messageActionGameScore#92a72876 game_id:long score:int = MessageAction;
messageActionPaymentSentMe#8f31b327 flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string charge:PaymentCharge = MessageAction;
messageActionPaymentSent#96163f56 flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long invoice_slug:flags.0?string = MessageAction;
messageActionPaymentSentMe#ffa00ccc flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string charge:PaymentCharge subscription_until_date:flags.4?int = MessageAction;
messageActionPaymentSent#c624b16e flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long invoice_slug:flags.0?string subscription_until_date:flags.4?int = MessageAction;
messageActionPhoneCall#80e11a7f flags:# video:flags.2?true call_id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = MessageAction;
messageActionScreenshotTaken#4792929b = MessageAction;
messageActionCustomAction#fae69f56 message:string = MessageAction;
@ -235,7 +235,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason;
inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
inputReportReasonPersonalDetails#9ec7863d = ReportReason;
userFull#1f58e369 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true can_view_revenue:flags2.9?true bot_can_manage_emoji_status:flags2.10?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int stargifts_count:flags2.8?int = UserFull;
userFull#979d2376 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true can_view_revenue:flags2.9?true bot_can_manage_emoji_status:flags2.10?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int stargifts_count:flags2.8?int starref_program:flags2.11?StarRefProgram = UserFull;
contact#145ade0b user_id:long mutual:Bool = Contact;
@ -421,12 +421,11 @@ updateBotEditBusinessMessage#7df587c flags:# connection_id:string message:Messag
updateBotDeleteBusinessMessage#a02a982e connection_id:string peer:Peer messages:Vector<int> qts:int = Update;
updateNewStoryReaction#1824e40b story_id:int peer:Peer reaction:Reaction = Update;
updateBroadcastRevenueTransactions#dfd961f5 peer:Peer balances:BroadcastRevenueBalances = Update;
updateStarsBalance#fb85198 balance:long = Update;
updateStarsBalance#4e80a379 balance:StarsAmount = 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;
updateBotSubscriptionExpire#2d13c6ee user_id:long payload:string invoice_slug:string until_date:int qts:int = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -1832,9 +1831,9 @@ starsTransactionPeerAPI#f9677aad = StarsTransactionPeer;
starsTopupOption#bd915c0 flags:# extended:flags.1?true stars:long store_product:flags.0?string currency:string amount:long = StarsTopupOption;
starsTransaction#35d4f276 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 stargift:flags.14?StarGift floodskip_number:flags.15?int = StarsTransaction;
starsTransaction#64dfc926 flags:# refund:flags.3?true pending:flags.4?true failed:flags.6?true gift:flags.10?true reaction:flags.11?true id:string stars:StarsAmount 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 stargift:flags.14?StarGift floodskip_number:flags.15?int starref_commission_permille:flags.16?int starref_peer:flags.17?Peer starref_amount:flags.17?StarsAmount = 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;
payments.starsStatus#6c9ce8ed flags:# balance:StarsAmount 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;
@ -1842,7 +1841,7 @@ stories.foundStories#e2de7737 flags:# count:int stories:Vector<FoundStory> next_
geoPointAddress#de4c5d93 flags:# country_iso2:string state:flags.0?string city:flags.1?string street:flags.2?string = GeoPointAddress;
starsRevenueStatus#79342946 flags:# withdrawal_enabled:flags.0?true current_balance:long available_balance:long overall_revenue:long next_withdrawal_at:flags.1?int = StarsRevenueStatus;
starsRevenueStatus#febe5491 flags:# withdrawal_enabled:flags.0?true current_balance:StarsAmount available_balance:StarsAmount overall_revenue:StarsAmount next_withdrawal_at:flags.1?int = StarsRevenueStatus;
payments.starsRevenueStats#c92bb73b revenue_graph:StatsGraph status:StarsRevenueStatus usd_rate:double = payments.StarsRevenueStats;
@ -1891,6 +1890,19 @@ messages.preparedInlineMessage#ff57708d query_id:long result:BotInlineResult pee
botAppSettings#c99b1950 flags:# placeholder_path:flags.0?bytes background_color:flags.1?int background_dark_color:flags.2?int header_color:flags.3?int header_dark_color:flags.4?int = BotAppSettings;
starRefProgram#dd0c66f2 flags:# bot_id:long commission_permille:int duration_months:flags.0?int end_date:flags.1?int daily_revenue_per_user:flags.2?StarsAmount = StarRefProgram;
connectedBotStarRef#19a13f71 flags:# revoked:flags.1?true url:string date:int bot_id:long commission_permille:int duration_months:flags.0?int participants:long revenue:long = ConnectedBotStarRef;
payments.connectedStarRefBots#98d5ea1d count:int connected_bots:Vector<ConnectedBotStarRef> users:Vector<User> = payments.ConnectedStarRefBots;
payments.suggestedStarRefBots#b4d5d859 flags:# count:int suggested_bots:Vector<StarRefProgram> users:Vector<User> next_offset:flags.0?string = payments.SuggestedStarRefBots;
starsAmount#bbb6b4a3 amount:long nanos:int = StarsAmount;
messages.foundStickersNotModified#6010c534 flags:# next_offset:flags.0?int = messages.FoundStickers;
messages.foundStickers#82c9e290 flags:# next_offset:flags.0?int hash:long stickers:Vector<Document> = messages.FoundStickers;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -2056,7 +2068,7 @@ contacts.block#2e2e8734 flags:# my_stories_from:flags.0?true id:InputPeer = Bool
contacts.unblock#b550d328 flags:# my_stories_from:flags.0?true id:InputPeer = Bool;
contacts.getBlocked#9a868f80 flags:# my_stories_from:flags.0?true offset:int limit:int = contacts.Blocked;
contacts.search#11f812d8 q:string limit:int = contacts.Found;
contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer;
contacts.resolveUsername#725afbbc flags:# username:string referer:flags.0?string = contacts.ResolvedPeer;
contacts.getTopPeers#973478b6 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true forward_users:flags.4?true forward_chats:flags.5?true groups:flags.10?true channels:flags.15?true bots_app:flags.16?true offset:int limit:int hash:long = contacts.TopPeers;
contacts.resetTopPeerRating#1ae373ac category:TopPeerCategory peer:InputPeer = Bool;
contacts.resetSaved#879537f1 = Bool;
@ -2296,6 +2308,7 @@ messages.reportSponsoredMessage#1af3dbb8 peer:InputPeer random_id:bytes option:b
messages.getSponsoredMessages#9bd2f439 peer:InputPeer = messages.SponsoredMessages;
messages.savePreparedInlineMessage#f21f7f2f flags:# result:InputBotInlineResult user_id:InputUser peer_types:flags.0?Vector<InlineQueryPeerType> = messages.BotPreparedInlineMessage;
messages.getPreparedInlineMessage#857ebdb8 bot:InputUser id:string = messages.PreparedInlineMessage;
messages.searchStickers#29b1c66a flags:# emojis:flags.0?true q:string emoticon:string lang_code:Vector<string> offset:int limit:int hash:long = messages.FoundStickers;
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;
@ -2430,6 +2443,8 @@ bots.getPreviewMedias#a2a5594d bot:InputUser = Vector<BotPreviewMedia>;
bots.updateUserEmojiStatus#ed9f30c5 user_id:InputUser emoji_status:EmojiStatus = Bool;
bots.toggleUserEmojiStatusPermission#6de6392 bot:InputUser enabled:Bool = Bool;
bots.checkDownloadFileParams#50077589 bot:InputUser file_name:string url:string = Bool;
bots.getAdminedBots#b0711d83 = Vector<User>;
bots.updateStarRefProgram#778b5ab3 flags:# bot:InputUser commission_permille:int duration_months:flags.0?int = StarRefProgram;
payments.getPaymentForm#37148dbb flags:# invoice:InputInvoice theme_params:flags.0?DataJSON = payments.PaymentForm;
payments.getPaymentReceipt#2478d1cc peer:InputPeer msg_id:int = payments.PaymentReceipt;
@ -2465,7 +2480,12 @@ payments.getStarGifts#c4563590 hash:int = payments.StarGifts;
payments.getUserStarGifts#5e72c7e1 user_id:InputUser offset:string limit:int = payments.UserStarGifts;
payments.saveStarGift#87acf08e flags:# unsave:flags.0?true user_id:InputUser msg_id:int = Bool;
payments.convertStarGift#421e027 user_id:InputUser msg_id:int = Bool;
payments.botCancelStarsSubscription#57f9ece6 flags:# restore:flags.0?true user_id:InputUser invoice_slug:flags.1?string charge_id:flags.2?string = Bool;
payments.botCancelStarsSubscription#6dfa0622 flags:# restore:flags.0?true user_id:InputUser charge_id:string = Bool;
payments.getConnectedStarRefBots#5869a553 flags:# peer:InputPeer offset_date:flags.2?int offset_link:flags.2?string limit:int = payments.ConnectedStarRefBots;
payments.getConnectedStarRefBot#b7d998f0 peer:InputPeer bot:InputUser = payments.ConnectedStarRefBots;
payments.getSuggestedStarRefBots#d6b48f7 flags:# order_by_revenue:flags.0?true order_by_date:flags.1?true peer:InputPeer offset:string limit:int = payments.SuggestedStarRefBots;
payments.connectStarRefBot#7ed5348a peer:InputPeer bot:InputUser = payments.ConnectedStarRefBots;
payments.editConnectedStarRefBot#e4fca4a3 flags:# revoked:flags.0?true peer:InputPeer link:string = payments.ConnectedStarRefBots;
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

@ -1145,6 +1145,7 @@ export interface LangPair {
'StarGiftAvailability': undefined;
'StarsSubscribeInfoLinkText': undefined;
'StarsSubscribeInfoLink': undefined;
'StarsBalance': undefined;
'OpenApp': undefined;
'PopularApps': undefined;
'SearchApps': undefined;

View File

@ -32,7 +32,7 @@ export function formatCurrencyAsString(
) {
const price = totalPrice / 10 ** getCurrencyExp(currency);
if ((options?.shouldOmitFractions || currency === STARS_CURRENCY_CODE) && price % 1 === 0) {
if ((options?.shouldOmitFractions || currency === STARS_CURRENCY_CODE) && Number.isInteger(price)) {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency,

View File

@ -29,6 +29,10 @@ export function formatIntegerCompact(views: number) {
return `${formatFixedNumber(views / 1e6)}M`;
}
export function formatPercent(value: number, fractionDigits = 1) {
return `${Number.isInteger(value) ? value : value.toFixed(fractionDigits)}%`;
}
export const getFirstLetters = withCache((phrase: string, count = 2) => {
return phrase
.replace(/[.,!@#$%^&*()_+=\-`~[\]/\\{}:"|<>?]+/gi, '')