From 34b3df1ca617a0d84bf1e65fb2b288ecccc6fcf1 Mon Sep 17 00:00:00 2001
From: zubiden <19638254+zubiden@users.noreply.github.com>
Date: Fri, 6 Dec 2024 19:44:04 +0400
Subject: [PATCH] Stars: Support bot subscriptions (#5261)
---
src/api/gramjs/apiBuilders/payments.ts | 13 +++++-
src/api/types/messages.ts | 1 +
src/api/types/payments.ts | 4 ++
src/assets/localization/fallback.strings | 4 ++
src/components/common/PeerBadge.tsx | 14 +++---
.../stars/StarsBalanceModal.module.scss | 29 +++++++++---
.../modals/stars/StarsBalanceModal.tsx | 19 +++++++-
.../modals/stars/StarsPaymentModal.tsx | 45 ++++++++++++++-----
.../modals/stars/helpers/transaction.ts | 2 +-
.../StarsSubscriptionItem.module.scss | 9 +++-
.../subscription/StarsSubscriptionItem.tsx | 8 +++-
.../StarsSubscriptionModal.module.scss | 7 +++
.../subscription/StarsSubscriptionModal.tsx | 35 ++++++++++++---
.../StarsTransactionModal.module.scss | 7 +++
src/global/actions/api/stars.ts | 4 ++
src/global/actions/apiUpdaters/payments.ts | 15 ++++++-
src/global/reducers/payments.ts | 24 +++++++++-
src/global/types.ts | 1 +
src/styles/index.scss | 6 ++-
src/types/language.d.ts | 11 +++++
src/util/getReadableErrorText.ts | 1 +
src/util/localization/format.tsx | 8 +++-
22 files changed, 226 insertions(+), 41 deletions(-)
diff --git a/src/api/gramjs/apiBuilders/payments.ts b/src/api/gramjs/apiBuilders/payments.ts
index 338901dea..f20fe1ef3 100644
--- a/src/api/gramjs/apiBuilders/payments.ts
+++ b/src/api/gramjs/apiBuilders/payments.ts
@@ -233,6 +233,7 @@ export function buildApiInvoice(invoice: GramJs.Invoice): ApiInvoice {
phoneToProvider,
shippingAddressRequested,
flexible,
+ subscriptionPeriod,
} = invoice;
const mappedPrices: ApiLabeledPrice[] = prices.map(({ label, amount }) => ({
@@ -258,6 +259,7 @@ export function buildApiInvoice(invoice: GramJs.Invoice): ApiInvoice {
isPhoneSentToProvider: phoneToProvider,
isShippingAddressRequested: shippingAddressRequested,
isFlexible: flexible,
+ subscriptionPeriod,
};
}
@@ -559,9 +561,14 @@ export function buildApiStarsTransaction(transaction: GramJs.StarsTransaction):
export function buildApiStarsSubscription(subscription: GramJs.StarsSubscription): ApiStarsSubscription {
const {
- id, peer, pricing, untilDate, canRefulfill, canceled, chatInviteHash, missingBalance,
+ id, peer, pricing, untilDate, canRefulfill, canceled, chatInviteHash, missingBalance, botCanceled, photo, title,
+ invoiceSlug,
} = subscription;
+ if (photo) {
+ addWebDocumentToLocalDb(photo);
+ }
+
return {
id,
peerId: getApiChatIdFromMtpPeer(peer),
@@ -571,6 +578,10 @@ export function buildApiStarsSubscription(subscription: GramJs.StarsSubscription
canRefulfill,
hasMissingBalance: missingBalance,
chatInviteHash,
+ hasBotCancelled: botCanceled,
+ title,
+ photo: photo && buildApiWebDocument(photo),
+ invoiceSlug,
};
}
diff --git a/src/api/types/messages.ts b/src/api/types/messages.ts
index 707e26f13..cfc431392 100644
--- a/src/api/types/messages.ts
+++ b/src/api/types/messages.ts
@@ -342,6 +342,7 @@ export interface ApiInvoice {
currency: string;
isTest?: boolean;
isRecurring?: boolean;
+ subscriptionPeriod?: number;
termsUrl?: string;
maxTipAmount?: number;
suggestedTipAmounts?: number[];
diff --git a/src/api/types/payments.ts b/src/api/types/payments.ts
index ae4ac8508..0e9643c0a 100644
--- a/src/api/types/payments.ts
+++ b/src/api/types/payments.ts
@@ -375,6 +375,10 @@ export interface ApiStarsSubscription {
canRefulfill?: true;
hasMissingBalance?: true;
chatInviteHash?: string;
+ hasBotCancelled?: true;
+ title?: string;
+ photo?: ApiWebDocument;
+ invoiceSlug?: string;
}
export interface ApiStarTopupOption {
diff --git a/src/assets/localization/fallback.strings b/src/assets/localization/fallback.strings
index 295633a11..da17a0c57 100644
--- a/src/assets/localization/fallback.strings
+++ b/src/assets/localization/fallback.strings
@@ -1346,6 +1346,7 @@
"LimitedGiftsCategory" = "Limited";
"PremiumGiftDescription" = "Premium";
"SendPaidReaction" = "Send ⭐️{amount}";
+"StarsPay" = "Confirm and Pay {amount}";
"StarsReactionTerms" = "By sending Stars you agree to the {link}";
"StarsReactionLinkText" = "Terms of Service";
"StarsReactionLink" = "https://telegram.org/tos/stars";
@@ -1392,3 +1393,6 @@
"BotSuggestedStatus" = "Do you want to set this emoji status suggested by **{bot}**?";
"BotSuggestedStatusTitle" = "Set Emoji Status";
"BotSuggestedStatusUpdated" = "Your emoji status is updated.";
+"StarsSubscribeBotText_one" = "Do you want to subscribe to **{name}** in **{bot}** for **{amount}** star per month?"
+"StarsSubscribeBotText_other" = "Do you want to subscribe to **{name}** in **{bot}** for **{amount}** stars per month?"
+"StarsSubscribeBotButtonMonth" = "Subscribe for {amount} / month";
diff --git a/src/components/common/PeerBadge.tsx b/src/components/common/PeerBadge.tsx
index a6868833b..8e8a261ea 100644
--- a/src/components/common/PeerBadge.tsx
+++ b/src/components/common/PeerBadge.tsx
@@ -1,18 +1,20 @@
import React, { memo } from '../../lib/teact/teact';
-import type { ApiPeer } from '../../api/types';
+import type { ApiPeer, ApiWebDocument } from '../../api/types';
import type { CustomPeer } from '../../types';
import type { IconName } from '../../types/icons';
import buildClassName from '../../util/buildClassName';
-import Avatar from './Avatar';
+import Avatar, { type AvatarSize } from './Avatar';
import Icon from './icons/Icon';
import styles from './PeerBadge.module.scss';
type OwnProps = {
- peer: ApiPeer | CustomPeer;
+ peer?: ApiPeer | CustomPeer;
+ avatarWebPhoto?: ApiWebDocument;
+ avatarSize?: AvatarSize;
text?: string;
badgeText?: string;
badgeIcon?: IconName;
@@ -24,7 +26,9 @@ type OwnProps = {
};
const PeerBadge = ({
- peer,
+ peer: avatarPeer,
+ avatarWebPhoto,
+ avatarSize,
text,
badgeText,
badgeIcon,
@@ -40,7 +44,7 @@ const PeerBadge = ({
onClick={onClick}
>
-
+
{badgeText && (
{badgeIcon && }
diff --git a/src/components/modals/stars/StarsBalanceModal.module.scss b/src/components/modals/stars/StarsBalanceModal.module.scss
index b6e7bf997..95d9050a4 100644
--- a/src/components/modals/stars/StarsBalanceModal.module.scss
+++ b/src/components/modals/stars/StarsBalanceModal.module.scss
@@ -109,6 +109,10 @@
unicode-bidi: plaintext;
}
+.botItem {
+ margin-bottom: 0.75rem;
+}
+
.hiddenHeader {
transform: translateY(-100%);
}
@@ -191,15 +195,9 @@
transform: translate(-50%, -50%);
}
-.paymentAmount {
- display: flex;
- line-height: 1.125;
- gap: 0.125rem;
-}
-
.paymentButton {
display: flex;
- gap: 0.125rem;
+ align-items: center;
margin-top: 1rem;
}
@@ -225,3 +223,20 @@
margin-top: 0.5rem;
color: var(--color-text-secondary);
}
+
+.amountBadge {
+ background-image: var(--stars-gradient);
+}
+
+.loadMore {
+ justify-content: flex-start;
+ gap: 0.75rem;
+}
+
+.loadMoreIcon {
+ display: grid;
+ place-items: center;
+ width: 2.75rem;
+ height: 2.75rem;
+ font-size: 1.5rem;
+}
diff --git a/src/components/modals/stars/StarsBalanceModal.tsx b/src/components/modals/stars/StarsBalanceModal.tsx
index f41efe911..2e74adc61 100644
--- a/src/components/modals/stars/StarsBalanceModal.tsx
+++ b/src/components/modals/stars/StarsBalanceModal.tsx
@@ -56,7 +56,7 @@ const StarsBalanceModal = ({
modal, starsBalanceState, canBuyPremium,
}: OwnProps & StateProps) => {
const {
- closeStarsBalanceModal, loadStarsTransactions, openStarsGiftingPickerModal, openInvoice,
+ closeStarsBalanceModal, loadStarsTransactions, loadStarsSubscriptions, openStarsGiftingPickerModal, openInvoice,
} = getActions();
const { balance, history, subscriptions } = starsBalanceState || {};
@@ -154,6 +154,10 @@ const StarsBalanceModal = ({
});
});
+ const handleLoadMoreSubscriptions = useLastCallback(() => {
+ loadStarsSubscriptions();
+ });
+
const openStarsGiftingPickerModalHandler = useLastCallback(() => {
openStarsGiftingPickerModal({});
});
@@ -240,6 +244,19 @@ const StarsBalanceModal = ({
subscription={subscription}
/>
))}
+ {subscriptions?.nextOffset && (
+
+ )}
)}
diff --git a/src/components/modals/stars/StarsPaymentModal.tsx b/src/components/modals/stars/StarsPaymentModal.tsx
index c6476b122..c97b2e2a2 100644
--- a/src/components/modals/stars/StarsPaymentModal.tsx
+++ b/src/components/modals/stars/StarsPaymentModal.tsx
@@ -11,6 +11,8 @@ import {
selectChat, selectChatMessage, selectUser,
} from '../../../global/selectors';
import buildClassName from '../../../util/buildClassName';
+import { formatStarsAsIcon } from '../../../util/localization/format';
+import { formatInteger } from '../../../util/textFormat';
import renderText from '../../common/helpers/renderText';
import useFlag from '../../../hooks/useFlag';
@@ -21,6 +23,8 @@ import usePrevious from '../../../hooks/usePrevious';
import Avatar from '../../common/Avatar';
import StarIcon from '../../common/icons/StarIcon';
+import PeerBadge from '../../common/PeerBadge';
+import PickerSelectedItem from '../../common/pickers/PickerSelectedItem';
import SafeLink from '../../common/SafeLink';
import Button from '../../ui/Button';
import Modal from '../../ui/Modal';
@@ -58,6 +62,8 @@ const StarPaymentModal = ({
const { form, subscriptionInfo } = renderingModal || {};
const amount = form?.invoice?.totalAmount || subscriptionInfo?.subscriptionPricing?.amount;
+ const isBotSubscription = Boolean(form?.invoice.subscriptionPeriod);
+ const canShowPeerItem = !subscriptionInfo?.subscriptionPricing;
const photo = form?.photo;
@@ -102,8 +108,21 @@ const StarPaymentModal = ({
});
}
+ if (isBotSubscription) {
+ return lang('StarsSubscribeBotText', {
+ name: form.title,
+ amount,
+ bot: botName,
+ }, {
+ pluralValue: amount!,
+ });
+ }
+
return oldLang('Stars.Transfer.Info', [form!.title, botName, starsText]);
- }, [renderingModal, bot, oldLang, amount, paidMediaMessage, subscriptionInfo, form, paidMediaChat, lang]);
+ }, [
+ renderingModal?.inputInvoice, bot, oldLang, amount, paidMediaMessage, subscriptionInfo, isBotSubscription, form,
+ paidMediaChat, lang,
+ ]);
const disclaimerText = useMemo(() => {
if (subscriptionInfo) {
@@ -160,25 +179,31 @@ const StarPaymentModal = ({
>
) : (
- <>
-
- {photo && }
- >
+
)}
{inviteCustomPeer ? oldLang('StarsSubscribeTitle') : oldLang('StarsConfirmPurchaseTitle')}
+ {canShowPeerItem && }
{renderText(descriptionText, ['simple_markdown', 'emoji'])}
{disclaimerText && (
diff --git a/src/components/modals/stars/helpers/transaction.ts b/src/components/modals/stars/helpers/transaction.ts
index c16a7862a..0dcddbffc 100644
--- a/src/components/modals/stars/helpers/transaction.ts
+++ b/src/components/modals/stars/helpers/transaction.ts
@@ -5,7 +5,7 @@ import { buildStarsTransactionCustomPeer } from '../../../../global/helpers/paym
export function getTransactionTitle(lang: OldLangFn, transaction: ApiStarsTransaction) {
if (transaction.extendedMedia) return lang('StarMediaPurchase');
- if (transaction.subscriptionPeriod) return lang('StarSubscriptionPurchase');
+ if (transaction.subscriptionPeriod) return transaction.title || lang('StarSubscriptionPurchase');
if (transaction.isReaction) return lang('StarsReactionsSent');
if (transaction.giveawayPostId) return lang('StarsGiveawayPrizeReceived');
if (transaction.isMyGift) return lang('StarsGiftSent');
diff --git a/src/components/modals/stars/subscription/StarsSubscriptionItem.module.scss b/src/components/modals/stars/subscription/StarsSubscriptionItem.module.scss
index aa39c6ec7..bd8ae094c 100644
--- a/src/components/modals/stars/subscription/StarsSubscriptionItem.module.scss
+++ b/src/components/modals/stars/subscription/StarsSubscriptionItem.module.scss
@@ -19,6 +19,13 @@
flex-grow: 1;
}
+.subtitle {
+ display: flex;
+ align-items: center;
+ gap: 0.125rem;
+ font-size: 0.875rem;
+}
+
.status {
display: flex;
flex-direction: column;
@@ -37,7 +44,7 @@
font-weight: 500;
}
-.title, .description {
+.title, .description, .subtitle {
margin-bottom: 0;
}
diff --git a/src/components/modals/stars/subscription/StarsSubscriptionItem.tsx b/src/components/modals/stars/subscription/StarsSubscriptionItem.tsx
index 3e80809c0..919e30274 100644
--- a/src/components/modals/stars/subscription/StarsSubscriptionItem.tsx
+++ b/src/components/modals/stars/subscription/StarsSubscriptionItem.tsx
@@ -33,7 +33,7 @@ function selectProvidedPeer(peerId: string) {
const StarsSubscriptionItem = ({ subscription }: OwnProps) => {
const { openStarsSubscriptionModal } = getActions();
const {
- peerId, pricing, until, isCancelled,
+ peerId, pricing, until, isCancelled, title, photo,
} = subscription;
const lang = useOldLang();
@@ -57,6 +57,12 @@ const StarsSubscriptionItem = ({ subscription }: OwnProps) => {
{getSenderTitle(lang, peer)}
+ {title && (
+
+ {photo && }
+ {title}
+
+ )}
{lang(
hasExpired ? 'StarsSubscriptionExpired'
diff --git a/src/components/modals/stars/subscription/StarsSubscriptionModal.module.scss b/src/components/modals/stars/subscription/StarsSubscriptionModal.module.scss
index c3705a040..25b0b9dd4 100644
--- a/src/components/modals/stars/subscription/StarsSubscriptionModal.module.scss
+++ b/src/components/modals/stars/subscription/StarsSubscriptionModal.module.scss
@@ -21,6 +21,13 @@
margin-bottom: 0;
}
+.title {
+ text-align: center;
+ text-wrap: balance;
+ font-size: 1.75rem;
+ line-height: 1.25;
+}
+
.amount {
display: flex;
align-items: center;
diff --git a/src/components/modals/stars/subscription/StarsSubscriptionModal.tsx b/src/components/modals/stars/subscription/StarsSubscriptionModal.tsx
index 472b8113f..82f557e1d 100644
--- a/src/components/modals/stars/subscription/StarsSubscriptionModal.tsx
+++ b/src/components/modals/stars/subscription/StarsSubscriptionModal.tsx
@@ -8,6 +8,7 @@ import type {
import type { TabState } from '../../../../global/types';
import { STARS_ICON_PLACEHOLDER } from '../../../../config';
+import { isApiPeerUser } from '../../../../global/helpers/peers';
import {
selectPeer,
} from '../../../../global/selectors';
@@ -46,6 +47,7 @@ const StarsSubscriptionModal: FC = ({
changeStarsSubscription,
checkChatInvite,
loadStarStatus,
+ openInvoice,
} = getActions();
const oldLang = useOldLang();
const lang = useLang();
@@ -69,7 +71,8 @@ const StarsSubscriptionModal: FC = ({
return 'renew';
}
- if (!isActive) {
+ const canRestart = subscription.chatInviteHash || subscription.invoiceSlug;
+ if (!isActive && canRestart) {
return 'restart';
}
@@ -87,7 +90,14 @@ const StarsSubscriptionModal: FC = ({
break;
}
case 'restart': {
- checkChatInvite({ hash: subscription.chatInviteHash! });
+ if (subscription.chatInviteHash) {
+ checkChatInvite({ hash: subscription.chatInviteHash });
+ } else if (subscription.invoiceSlug) {
+ openInvoice({
+ type: 'slug',
+ slug: subscription.invoiceSlug,
+ });
+ }
loadStarStatus();
break;
}
@@ -109,13 +119,15 @@ const StarsSubscriptionModal: FC = ({
}
const {
- pricing, until, isCancelled, canRefulfill,
+ pricing, until, isCancelled, canRefulfill, photo, title, hasBotCancelled,
} = subscription;
+ const isBotSubscription = isApiPeerUser(peer);
+
const header = (
![]()
= ({
alt=""
draggable={false}
/>
-
{oldLang('StarsSubscriptionTitle')}
+
{title || oldLang('StarsSubscriptionTitle')}
{lang('StarsPerMonth', {
amount: pricing.amount,
@@ -141,10 +153,17 @@ const StarsSubscriptionModal: FC = ({
const tableData: TableData = [];
tableData.push([
- oldLang('StarsSubscriptionChannel'),
+ oldLang(isBotSubscription ? 'StarsSubscriptionBot' : 'StarsSubscriptionChannel'),
{ chatId: peer.id },
]);
+ if (title) {
+ tableData.push([
+ oldLang('StarsSubscriptionBotProduct'),
+ title,
+ ]);
+ }
+
const hasExpired = until < Date.now() / 1000;
tableData.push([
oldLang(hasExpired ? 'StarsSubscriptionUntilExpired'
@@ -162,7 +181,9 @@ const StarsSubscriptionModal: FC = ({
{footerTos}
{isCancelled && (
- {oldLang('StarsSubscriptionCancelledText')}
+
+ {oldLang(hasBotCancelled ? 'StarsSubscriptionBotCancelledText' : 'StarsSubscriptionCancelledText')}
+
)}
{canRefulfill && (
diff --git a/src/components/modals/stars/transaction/StarsTransactionModal.module.scss b/src/components/modals/stars/transaction/StarsTransactionModal.module.scss
index a3e2906b0..5495727e1 100644
--- a/src/components/modals/stars/transaction/StarsTransactionModal.module.scss
+++ b/src/components/modals/stars/transaction/StarsTransactionModal.module.scss
@@ -37,6 +37,13 @@
margin-bottom: 0;
}
+.title {
+ text-align: center;
+ text-wrap: balance;
+ font-size: 1.75rem;
+ line-height: 1.25;
+}
+
.tid {
font-family: var(--font-family-monospace);
font-size: 0.875rem;
diff --git a/src/global/actions/api/stars.ts b/src/global/actions/api/stars.ts
index 3060eb009..b8f4c3341 100644
--- a/src/global/actions/api/stars.ts
+++ b/src/global/actions/api/stars.ts
@@ -10,6 +10,7 @@ import {
appendStarsSubscriptions,
appendStarsTransactions,
updateStarsBalance,
+ updateStarsSubscriptionLoading,
} from '../../reducers';
import {
selectPeer,
@@ -179,6 +180,9 @@ addActionHandler('loadStarsSubscriptions', async (global): Promise => {
const offset = subscriptions?.nextOffset;
if (subscriptions && !offset) return; // Already loaded all
+ global = updateStarsSubscriptionLoading(global, true);
+ setGlobal(global);
+
const result = await callApi('fetchStarsSubscriptions', {
offset: offset || '',
});
diff --git a/src/global/actions/apiUpdaters/payments.ts b/src/global/actions/apiUpdaters/payments.ts
index 6ce43be25..86d2f1813 100644
--- a/src/global/actions/apiUpdaters/payments.ts
+++ b/src/global/actions/apiUpdaters/payments.ts
@@ -42,7 +42,7 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
case 'updateStarPaymentStateCompleted': {
const { paymentState, tabId } = update;
- const { inputInvoice, subscriptionInfo } = paymentState;
+ const { inputInvoice, subscriptionInfo, form } = paymentState;
if (inputInvoice?.type === 'chatInviteSubscription' && subscriptionInfo) {
const amount = subscriptionInfo.subscriptionPricing!.amount;
@@ -57,6 +57,19 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
});
}
+ if (form?.invoice.subscriptionPeriod) {
+ const amount = form.invoice.totalAmount;
+ actions.showNotification({
+ tabId,
+ title: langProvider.oldTranslate('StarsSubscriptionCompleted'),
+ message: langProvider.oldTranslate('StarsSubscriptionCompletedText', [
+ amount,
+ form.title,
+ ], undefined, amount),
+ icon: 'star',
+ });
+ }
+
if (inputInvoice?.type === 'giftcode') {
if (!inputInvoice.userIds) {
return;
diff --git a/src/global/reducers/payments.ts b/src/global/reducers/payments.ts
index bf41dd7b4..10a36bab5 100644
--- a/src/global/reducers/payments.ts
+++ b/src/global/reducers/payments.ts
@@ -6,7 +6,7 @@ import type {
} from '../../api/types';
import type { PaymentStep, ShippingOption } from '../../types';
import type {
- GlobalState, StarsTransactionType, TabArgs, TabState,
+ GlobalState, StarsSubscriptions, StarsTransactionType, TabArgs, TabState,
} from '../types';
import { getCurrentTabId } from '../../util/establishMultitabRole';
@@ -182,7 +182,7 @@ export function appendStarsSubscriptions(
const newObject = {
list: (global.stars.subscriptions?.list || []).concat(subscriptions),
nextOffset,
- };
+ } satisfies StarsSubscriptions;
return {
...global,
@@ -193,6 +193,26 @@ export function appendStarsSubscriptions(
};
}
+export function updateStarsSubscriptionLoading(
+ global: T, isLoading: boolean,
+): T {
+ const subscriptions = global.stars?.subscriptions;
+ if (!subscriptions) {
+ return global;
+ }
+
+ return {
+ ...global,
+ stars: {
+ ...global.stars,
+ subscriptions: {
+ ...subscriptions,
+ isLoading,
+ },
+ },
+ };
+}
+
export function openStarsTransactionModal(
global: T, transaction: ApiStarsTransaction, ...[tabId = getCurrentTabId()]: TabArgs
): T {
diff --git a/src/global/types.ts b/src/global/types.ts
index 7fc230b6c..bbb539e42 100644
--- a/src/global/types.ts
+++ b/src/global/types.ts
@@ -190,6 +190,7 @@ export type StarsTransactionHistory = Record {
'SendPaidReaction': {
'amount': V;
};
+ 'StarsPay': {
+ 'amount': V;
+ };
'StarsReactionTerms': {
'link': V;
};
@@ -1555,6 +1558,9 @@ export interface LangPairWithVariables {
'BotSuggestedStatus': {
'bot': V;
};
+ 'StarsSubscribeBotButtonMonth': {
+ 'amount': V;
+ };
}
export interface LangPairPlural {
@@ -1731,6 +1737,11 @@ export interface LangPairPluralWithVariables {
'chat': V;
'amount': V;
};
+ 'StarsSubscribeBotText': {
+ 'name': V;
+ 'bot': V;
+ 'amount': V;
+ };
}
export type RegularLangKey = keyof LangPair;
export type RegularLangKeyWithVariables = keyof LangPairWithVariables;
diff --git a/src/util/getReadableErrorText.ts b/src/util/getReadableErrorText.ts
index c6288128f..6a4a0dd05 100644
--- a/src/util/getReadableErrorText.ts
+++ b/src/util/getReadableErrorText.ts
@@ -76,6 +76,7 @@ const READABLE_ERROR_MESSAGES: Record = {
PROVIDER_ACCOUNT_TIMEOUT: 'Request to the payment provider has expired',
STARGIFT_CONVERT_TOO_OLD: 'This gift no longer can be converted to Stars',
+ SUBSCRIPTION_ALREADY_ACTIVE: 'You are already subscribed',
PEERS_LIST_EMPTY: 'No chats are added to the list',
diff --git a/src/util/localization/format.tsx b/src/util/localization/format.tsx
index 7b25ff859..20bf60140 100644
--- a/src/util/localization/format.tsx
+++ b/src/util/localization/format.tsx
@@ -4,17 +4,21 @@ import type { LangFn } from './types';
import { STARS_ICON_PLACEHOLDER } from '../../config';
+import Icon from '../../components/common/icons/Icon';
import StarIcon from '../../components/common/icons/StarIcon';
export function formatStarsAsText(lang: LangFn, amount: number) {
return lang('StarsAmountText', { amount }, { pluralValue: amount });
}
-export function formatStarsAsIcon(lang: LangFn, amount: number) {
+export function formatStarsAsIcon(lang: LangFn, amount: number, asFont?: boolean) {
+ const icon = asFont
+ ?
+ : ;
return lang('StarsAmount', { amount }, {
withNodes: true,
specialReplacement: {
- [STARS_ICON_PLACEHOLDER]: ,
+ [STARS_ICON_PLACEHOLDER]: icon,
},
});
}