Stars: Support bot subscriptions (#5261)
This commit is contained in:
parent
c6d30e8cb8
commit
34b3df1ca6
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -342,6 +342,7 @@ export interface ApiInvoice {
|
||||
currency: string;
|
||||
isTest?: boolean;
|
||||
isRecurring?: boolean;
|
||||
subscriptionPeriod?: number;
|
||||
termsUrl?: string;
|
||||
maxTipAmount?: number;
|
||||
suggestedTipAmounts?: number[];
|
||||
|
||||
@ -375,6 +375,10 @@ export interface ApiStarsSubscription {
|
||||
canRefulfill?: true;
|
||||
hasMissingBalance?: true;
|
||||
chatInviteHash?: string;
|
||||
hasBotCancelled?: true;
|
||||
title?: string;
|
||||
photo?: ApiWebDocument;
|
||||
invoiceSlug?: string;
|
||||
}
|
||||
|
||||
export interface ApiStarTopupOption {
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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}
|
||||
>
|
||||
<div className={styles.top}>
|
||||
<Avatar size="large" peer={peer} />
|
||||
<Avatar size={avatarSize} peer={avatarPeer} webPhoto={avatarWebPhoto} />
|
||||
{badgeText && (
|
||||
<div className={buildClassName(styles.badge, badgeClassName)}>
|
||||
{badgeIcon && <Icon name={badgeIcon} className={badgeIconClassName} />}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 && (
|
||||
<Button
|
||||
isText
|
||||
disabled={subscriptions.isLoading}
|
||||
size="smaller"
|
||||
noForcedUpperCase
|
||||
className={styles.loadMore}
|
||||
onClick={handleLoadMoreSubscriptions}
|
||||
>
|
||||
<Icon name="down" className={styles.loadMoreIcon} />
|
||||
{oldLang('StarMySubscriptionsExpand')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -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 = ({
|
||||
<StarIcon type="gold" size="adaptive" className={styles.avatarStar} />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Avatar peer={bot} size="giant" />
|
||||
{photo && <Avatar className={styles.paymentPhoto} webPhoto={photo} size="giant" />}
|
||||
</>
|
||||
<PeerBadge
|
||||
peer={!photo ? bot : undefined}
|
||||
avatarWebPhoto={photo}
|
||||
avatarSize="giant"
|
||||
badgeIcon="star"
|
||||
badgeText={formatInteger(amount!)}
|
||||
badgeClassName={styles.amountBadge}
|
||||
className={styles.paymentPhoto}
|
||||
/>
|
||||
)}
|
||||
<img className={styles.paymentImageBackground} src={StarsBackground} alt="" draggable={false} />
|
||||
</div>
|
||||
<h2 className={styles.headerText}>
|
||||
{inviteCustomPeer ? oldLang('StarsSubscribeTitle') : oldLang('StarsConfirmPurchaseTitle')}
|
||||
</h2>
|
||||
{canShowPeerItem && <PickerSelectedItem className={styles.botItem} peerId={form?.botId} />}
|
||||
<div className={styles.description}>
|
||||
{renderText(descriptionText, ['simple_markdown', 'emoji'])}
|
||||
</div>
|
||||
<Button className={styles.paymentButton} size="smaller" onClick={handlePayment} isLoading={isLoading}>
|
||||
{oldLang('Stars.Transfer.Pay')}
|
||||
<div className={styles.paymentAmount}>
|
||||
{amount}
|
||||
<StarIcon className={styles.paymentButtonStar} size="small" />
|
||||
</div>
|
||||
{lang(isBotSubscription ? 'StarsSubscribeBotButtonMonth' : 'StarsPay', {
|
||||
amount: formatStarsAsIcon(lang, amount!, true),
|
||||
}, {
|
||||
withNodes: true,
|
||||
})}
|
||||
</Button>
|
||||
{disclaimerText && (
|
||||
<div className={buildClassName(styles.disclaimer, styles.smallerText)}>
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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) => {
|
||||
</div>
|
||||
<div className={styles.info}>
|
||||
<h3 className={styles.title}>{getSenderTitle(lang, peer)}</h3>
|
||||
{title && (
|
||||
<p className={styles.subtitle}>
|
||||
{photo && <Avatar webPhoto={photo} size="micro" />}
|
||||
{title}
|
||||
</p>
|
||||
)}
|
||||
<p className={styles.description}>
|
||||
{lang(
|
||||
hasExpired ? 'StarsSubscriptionExpired'
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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<OwnProps & StateProps> = ({
|
||||
changeStarsSubscription,
|
||||
checkChatInvite,
|
||||
loadStarStatus,
|
||||
openInvoice,
|
||||
} = getActions();
|
||||
const oldLang = useOldLang();
|
||||
const lang = useLang();
|
||||
@ -69,7 +71,8 @@ const StarsSubscriptionModal: FC<OwnProps & StateProps> = ({
|
||||
return 'renew';
|
||||
}
|
||||
|
||||
if (!isActive) {
|
||||
const canRestart = subscription.chatInviteHash || subscription.invoiceSlug;
|
||||
if (!isActive && canRestart) {
|
||||
return 'restart';
|
||||
}
|
||||
|
||||
@ -87,7 +90,14 @@ const StarsSubscriptionModal: FC<OwnProps & StateProps> = ({
|
||||
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<OwnProps & StateProps> = ({
|
||||
}
|
||||
|
||||
const {
|
||||
pricing, until, isCancelled, canRefulfill,
|
||||
pricing, until, isCancelled, canRefulfill, photo, title, hasBotCancelled,
|
||||
} = subscription;
|
||||
|
||||
const isBotSubscription = isApiPeerUser(peer);
|
||||
|
||||
const header = (
|
||||
<div className={buildClassName(styles.header, styles.starsHeader)}>
|
||||
<div className={styles.avatarWrapper}>
|
||||
<Avatar peer={peer} size="jumbo" />
|
||||
<Avatar peer={!photo ? peer : undefined} webPhoto={photo} size="jumbo" />
|
||||
<StarIcon className={styles.subscriptionStar} type="gold" size="adaptive" />
|
||||
</div>
|
||||
<img
|
||||
@ -124,7 +136,7 @@ const StarsSubscriptionModal: FC<OwnProps & StateProps> = ({
|
||||
alt=""
|
||||
draggable={false}
|
||||
/>
|
||||
<h1 className={styles.title}>{oldLang('StarsSubscriptionTitle')}</h1>
|
||||
<h1 className={styles.title}>{title || oldLang('StarsSubscriptionTitle')}</h1>
|
||||
<p className={styles.amount}>
|
||||
{lang('StarsPerMonth', {
|
||||
amount: pricing.amount,
|
||||
@ -141,10 +153,17 @@ const StarsSubscriptionModal: FC<OwnProps & StateProps> = ({
|
||||
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<OwnProps & StateProps> = ({
|
||||
<span className={styles.footer}>
|
||||
<p className={styles.secondary}>{footerTos}</p>
|
||||
{isCancelled && (
|
||||
<p className={styles.danger}>{oldLang('StarsSubscriptionCancelledText')}</p>
|
||||
<p className={styles.danger}>
|
||||
{oldLang(hasBotCancelled ? 'StarsSubscriptionBotCancelledText' : 'StarsSubscriptionCancelledText')}
|
||||
</p>
|
||||
)}
|
||||
{canRefulfill && (
|
||||
<p className={styles.secondary}>
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
appendStarsSubscriptions,
|
||||
appendStarsTransactions,
|
||||
updateStarsBalance,
|
||||
updateStarsSubscriptionLoading,
|
||||
} from '../../reducers';
|
||||
import {
|
||||
selectPeer,
|
||||
@ -179,6 +180,9 @@ addActionHandler('loadStarsSubscriptions', async (global): Promise<void> => {
|
||||
const offset = subscriptions?.nextOffset;
|
||||
if (subscriptions && !offset) return; // Already loaded all
|
||||
|
||||
global = updateStarsSubscriptionLoading(global, true);
|
||||
setGlobal(global);
|
||||
|
||||
const result = await callApi('fetchStarsSubscriptions', {
|
||||
offset: offset || '',
|
||||
});
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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<T extends GlobalState>(
|
||||
const newObject = {
|
||||
list: (global.stars.subscriptions?.list || []).concat(subscriptions),
|
||||
nextOffset,
|
||||
};
|
||||
} satisfies StarsSubscriptions;
|
||||
|
||||
return {
|
||||
...global,
|
||||
@ -193,6 +193,26 @@ export function appendStarsSubscriptions<T extends GlobalState>(
|
||||
};
|
||||
}
|
||||
|
||||
export function updateStarsSubscriptionLoading<T extends GlobalState>(
|
||||
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<T extends GlobalState>(
|
||||
global: T, transaction: ApiStarsTransaction, ...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||
): T {
|
||||
|
||||
@ -190,6 +190,7 @@ export type StarsTransactionHistory = Record<StarsTransactionType, {
|
||||
export type StarsSubscriptions = {
|
||||
list: ApiStarsSubscription[];
|
||||
nextOffset?: string;
|
||||
isLoading?: boolean;
|
||||
};
|
||||
|
||||
export type ConfettiStyle = 'poppers' | 'top-down';
|
||||
|
||||
@ -292,8 +292,10 @@ body:not(.is-ios) {
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
.star-amount-icon {
|
||||
margin-inline-start: 0.5em; // Prevent sticking to the text without using `white-space: pre`
|
||||
// Increase specificity to override the default icon style
|
||||
.star-amount-icon.star-amount-icon {
|
||||
line-height: inherit; // Vertical centring
|
||||
margin-inline-start: 0.375em; // Prevent sticking to the text without using `white-space: pre`
|
||||
}
|
||||
|
||||
.shared-canvas-container {
|
||||
|
||||
11
src/types/language.d.ts
vendored
11
src/types/language.d.ts
vendored
@ -1509,6 +1509,9 @@ export interface LangPairWithVariables<V extends unknown = LangVariable> {
|
||||
'SendPaidReaction': {
|
||||
'amount': V;
|
||||
};
|
||||
'StarsPay': {
|
||||
'amount': V;
|
||||
};
|
||||
'StarsReactionTerms': {
|
||||
'link': V;
|
||||
};
|
||||
@ -1555,6 +1558,9 @@ export interface LangPairWithVariables<V extends unknown = LangVariable> {
|
||||
'BotSuggestedStatus': {
|
||||
'bot': V;
|
||||
};
|
||||
'StarsSubscribeBotButtonMonth': {
|
||||
'amount': V;
|
||||
};
|
||||
}
|
||||
|
||||
export interface LangPairPlural {
|
||||
@ -1731,6 +1737,11 @@ export interface LangPairPluralWithVariables<V extends unknown = LangVariable> {
|
||||
'chat': V;
|
||||
'amount': V;
|
||||
};
|
||||
'StarsSubscribeBotText': {
|
||||
'name': V;
|
||||
'bot': V;
|
||||
'amount': V;
|
||||
};
|
||||
}
|
||||
export type RegularLangKey = keyof LangPair;
|
||||
export type RegularLangKeyWithVariables = keyof LangPairWithVariables;
|
||||
|
||||
@ -76,6 +76,7 @@ const READABLE_ERROR_MESSAGES: Record<string, string> = {
|
||||
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',
|
||||
|
||||
|
||||
@ -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
|
||||
? <Icon name="star" className="star-amount-icon" />
|
||||
: <StarIcon type="gold" className="star-amount-icon" size="adaptive" />;
|
||||
return lang('StarsAmount', { amount }, {
|
||||
withNodes: true,
|
||||
specialReplacement: {
|
||||
[STARS_ICON_PLACEHOLDER]: <StarIcon type="gold" className="star-amount-icon" size="adaptive" />,
|
||||
[STARS_ICON_PLACEHOLDER]: icon,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user