Support TON balance (#6076)
Co-authored-by: zubiden <19638254+zubiden@users.noreply.github.com>
This commit is contained in:
parent
58d2906425
commit
a0e8eff8e3
@ -12,6 +12,10 @@ import {
|
|||||||
TODO_ITEM_LENGTH_LIMIT,
|
TODO_ITEM_LENGTH_LIMIT,
|
||||||
TODO_ITEMS_LIMIT,
|
TODO_ITEMS_LIMIT,
|
||||||
TODO_TITLE_LENGTH_LIMIT,
|
TODO_TITLE_LENGTH_LIMIT,
|
||||||
|
TON_SUGGESTED_POST_AMOUNT_MAX,
|
||||||
|
TON_SUGGESTED_POST_AMOUNT_MIN,
|
||||||
|
TON_TOPUP_URL_DEFAULT,
|
||||||
|
TON_USD_RATE_DEFAULT,
|
||||||
} from '../../../config';
|
} from '../../../config';
|
||||||
import localDb from '../localDb';
|
import localDb from '../localDb';
|
||||||
import { buildJson } from './misc';
|
import { buildJson } from './misc';
|
||||||
@ -108,6 +112,10 @@ export interface GramJsAppConfig extends LimitsConfig {
|
|||||||
stars_suggested_post_future_max?: number;
|
stars_suggested_post_future_max?: number;
|
||||||
stars_suggested_post_future_min?: number;
|
stars_suggested_post_future_min?: number;
|
||||||
ton_suggested_post_commission_permille?: number;
|
ton_suggested_post_commission_permille?: number;
|
||||||
|
ton_suggested_post_amount_max?: number;
|
||||||
|
ton_suggested_post_amount_min?: number;
|
||||||
|
ton_usd_rate?: number;
|
||||||
|
ton_topup_url?: string;
|
||||||
poll_answers_max?: number;
|
poll_answers_max?: number;
|
||||||
todo_items_max?: number;
|
todo_items_max?: number;
|
||||||
todo_title_length_max?: number;
|
todo_title_length_max?: number;
|
||||||
@ -219,6 +227,10 @@ export function buildAppConfig(json: GramJs.TypeJSONValue, hash: number): ApiApp
|
|||||||
starsSuggestedPostFutureMax: appConfig.stars_suggested_post_future_max,
|
starsSuggestedPostFutureMax: appConfig.stars_suggested_post_future_max,
|
||||||
starsSuggestedPostFutureMin: appConfig.stars_suggested_post_future_min,
|
starsSuggestedPostFutureMin: appConfig.stars_suggested_post_future_min,
|
||||||
tonSuggestedPostCommissionPermille: appConfig.ton_suggested_post_commission_permille,
|
tonSuggestedPostCommissionPermille: appConfig.ton_suggested_post_commission_permille,
|
||||||
|
tonSuggestedPostAmountMax: appConfig.ton_suggested_post_amount_max ?? TON_SUGGESTED_POST_AMOUNT_MAX,
|
||||||
|
tonSuggestedPostAmountMin: appConfig.ton_suggested_post_amount_min ?? TON_SUGGESTED_POST_AMOUNT_MIN,
|
||||||
|
tonUsdRate: appConfig.ton_usd_rate ?? TON_USD_RATE_DEFAULT,
|
||||||
|
tonTopupUrl: appConfig.ton_topup_url ?? TON_TOPUP_URL_DEFAULT,
|
||||||
pollMaxAnswers: appConfig.poll_answers_max,
|
pollMaxAnswers: appConfig.poll_answers_max,
|
||||||
todoItemsMax: appConfig.todo_items_max ?? TODO_ITEMS_LIMIT,
|
todoItemsMax: appConfig.todo_items_max ?? TODO_ITEMS_LIMIT,
|
||||||
todoTitleLengthMax: appConfig.todo_title_length_max ?? TODO_TITLE_LENGTH_LIMIT,
|
todoTitleLengthMax: appConfig.todo_title_length_max ?? TODO_TITLE_LENGTH_LIMIT,
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { buildApiBotApp } from './bots';
|
|||||||
import { buildApiFormattedText, buildApiPhoto } from './common';
|
import { buildApiFormattedText, buildApiPhoto } from './common';
|
||||||
import { buildApiStarGift } from './gifts';
|
import { buildApiStarGift } from './gifts';
|
||||||
import { buildTodoItem } from './messageContent';
|
import { buildTodoItem } from './messageContent';
|
||||||
import { buildApiStarsAmount } from './payments';
|
import { buildApiCurrencyAmount } from './payments';
|
||||||
import { buildApiPeerId, getApiChatIdFromMtpPeer } from './peers';
|
import { buildApiPeerId, getApiChatIdFromMtpPeer } from './peers';
|
||||||
|
|
||||||
const UNSUPPORTED_ACTION: ApiMessageAction = {
|
const UNSUPPORTED_ACTION: ApiMessageAction = {
|
||||||
@ -359,6 +359,20 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
|
|||||||
transactionId,
|
transactionId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (action instanceof GramJs.MessageActionGiftTon) {
|
||||||
|
const {
|
||||||
|
currency, amount, cryptoCurrency, cryptoAmount, transactionId,
|
||||||
|
} = action;
|
||||||
|
return {
|
||||||
|
mediaType: 'action',
|
||||||
|
type: 'giftTon',
|
||||||
|
currency,
|
||||||
|
amount: amount.toJSNumber(),
|
||||||
|
cryptoCurrency,
|
||||||
|
cryptoAmount: cryptoAmount.toJSNumber(),
|
||||||
|
transactionId,
|
||||||
|
};
|
||||||
|
}
|
||||||
if (action instanceof GramJs.MessageActionPrizeStars) {
|
if (action instanceof GramJs.MessageActionPrizeStars) {
|
||||||
const {
|
const {
|
||||||
unclaimed, stars, transactionId, boostPeer, giveawayMsgId,
|
unclaimed, stars, transactionId, boostPeer, giveawayMsgId,
|
||||||
@ -459,7 +473,7 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
|
|||||||
isBalanceTooLow: Boolean(balanceTooLow),
|
isBalanceTooLow: Boolean(balanceTooLow),
|
||||||
rejectComment,
|
rejectComment,
|
||||||
scheduleDate,
|
scheduleDate,
|
||||||
amount: price ? buildApiStarsAmount(price) : undefined,
|
amount: price ? buildApiCurrencyAmount(price) : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (action instanceof GramJs.MessageActionSuggestedPostSuccess) {
|
if (action instanceof GramJs.MessageActionSuggestedPostSuccess) {
|
||||||
@ -467,7 +481,7 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
|
|||||||
return {
|
return {
|
||||||
mediaType: 'action',
|
mediaType: 'action',
|
||||||
type: 'suggestedPostSuccess',
|
type: 'suggestedPostSuccess',
|
||||||
amount: buildApiStarsAmount(price),
|
amount: buildApiCurrencyAmount(price),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (action instanceof GramJs.MessageActionSuggestedPostRefund) {
|
if (action instanceof GramJs.MessageActionSuggestedPostRefund) {
|
||||||
|
|||||||
@ -47,7 +47,7 @@ import { omitUndefined, pick } from '../../../util/iteratees';
|
|||||||
import { getServerTime, getServerTimeOffset } from '../../../util/serverTime';
|
import { getServerTime, getServerTimeOffset } from '../../../util/serverTime';
|
||||||
import { interpolateArray } from '../../../util/waveform';
|
import { interpolateArray } from '../../../util/waveform';
|
||||||
import {
|
import {
|
||||||
buildApiStarsAmount,
|
buildApiCurrencyAmount,
|
||||||
} from '../apiBuilders/payments';
|
} from '../apiBuilders/payments';
|
||||||
import { buildPeer } from '../gramjsBuilders';
|
import { buildPeer } from '../gramjsBuilders';
|
||||||
import {
|
import {
|
||||||
@ -302,7 +302,7 @@ export function buildMessageDraft(draft: GramJs.TypeDraftMessage): ApiDraft | un
|
|||||||
const suggestedPostInfo = suggestedPost instanceof GramJs.SuggestedPost ? {
|
const suggestedPostInfo = suggestedPost instanceof GramJs.SuggestedPost ? {
|
||||||
isAccepted: suggestedPost.accepted,
|
isAccepted: suggestedPost.accepted,
|
||||||
isRejected: suggestedPost.rejected,
|
isRejected: suggestedPost.rejected,
|
||||||
price: suggestedPost.price ? buildApiStarsAmount(suggestedPost.price) : undefined,
|
price: suggestedPost.price ? buildApiCurrencyAmount(suggestedPost.price) : undefined,
|
||||||
scheduleDate: suggestedPost.scheduleDate,
|
scheduleDate: suggestedPost.scheduleDate,
|
||||||
} satisfies ApiInputSuggestedPostInfo : undefined;
|
} satisfies ApiInputSuggestedPostInfo : undefined;
|
||||||
|
|
||||||
@ -319,7 +319,7 @@ function buildApiSuggestedPost(suggestedPost: GramJs.SuggestedPost): ApiSuggeste
|
|||||||
return {
|
return {
|
||||||
isAccepted: suggestedPost.accepted,
|
isAccepted: suggestedPost.accepted,
|
||||||
isRejected: suggestedPost.rejected,
|
isRejected: suggestedPost.rejected,
|
||||||
price: suggestedPost.price ? buildApiStarsAmount(suggestedPost.price) : undefined,
|
price: suggestedPost.price ? buildApiCurrencyAmount(suggestedPost.price) : undefined,
|
||||||
scheduleDate: suggestedPost.scheduleDate,
|
scheduleDate: suggestedPost.scheduleDate,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,15 +20,16 @@ import type {
|
|||||||
ApiPrepaidStarsGiveaway,
|
ApiPrepaidStarsGiveaway,
|
||||||
ApiReceipt,
|
ApiReceipt,
|
||||||
ApiStarGiveawayOption,
|
ApiStarGiveawayOption,
|
||||||
ApiStarsAmount,
|
|
||||||
ApiStarsGiveawayWinnerOption,
|
ApiStarsGiveawayWinnerOption,
|
||||||
ApiStarsSubscription,
|
ApiStarsSubscription,
|
||||||
ApiStarsTransaction,
|
ApiStarsTransaction,
|
||||||
ApiStarsTransactionPeer,
|
ApiStarsTransactionPeer,
|
||||||
ApiStarTopupOption,
|
ApiStarTopupOption,
|
||||||
|
ApiTypeCurrencyAmount,
|
||||||
BoughtPaidMedia,
|
BoughtPaidMedia,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
|
import { STARS_CURRENCY_CODE, TON_CURRENCY_CODE } from '../../../config';
|
||||||
import { addWebDocumentToLocalDb } from '../helpers/localDb';
|
import { addWebDocumentToLocalDb } from '../helpers/localDb';
|
||||||
import { buildApiStarsSubscriptionPricing } from './chats';
|
import { buildApiStarsSubscriptionPricing } from './chats';
|
||||||
import { buildApiMessageEntity } from './common';
|
import { buildApiMessageEntity } from './common';
|
||||||
@ -461,28 +462,25 @@ export function buildApiStarsGiftOptions(option: GramJs.StarsGiftOption): ApiSta
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildApiStarsAmount(amount: GramJs.TypeStarsAmount): ApiStarsAmount | undefined {
|
export function buildApiCurrencyAmount(amount: GramJs.TypeStarsAmount): ApiTypeCurrencyAmount | undefined {
|
||||||
if (amount instanceof GramJs.StarsAmount) {
|
if (amount instanceof GramJs.StarsAmount) {
|
||||||
return {
|
return {
|
||||||
|
currency: STARS_CURRENCY_CODE,
|
||||||
amount: amount.amount.toJSNumber(),
|
amount: amount.amount.toJSNumber(),
|
||||||
nanos: amount.nanos,
|
nanos: amount.nanos,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (amount instanceof GramJs.StarsTonAmount) {
|
if (amount instanceof GramJs.StarsTonAmount) {
|
||||||
return undefined;
|
return {
|
||||||
|
currency: TON_CURRENCY_CODE,
|
||||||
|
amount: amount.amount.toJSNumber(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildInputStarsAmount(amount: ApiStarsAmount): GramJs.TypeStarsAmount {
|
|
||||||
return new GramJs.StarsAmount({
|
|
||||||
amount: bigInt(amount.amount),
|
|
||||||
nanos: amount.nanos,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildApiStarsGiveawayWinnersOption(
|
export function buildApiStarsGiveawayWinnersOption(
|
||||||
option: GramJs.StarsGiveawayWinnersOption,
|
option: GramJs.StarsGiveawayWinnersOption,
|
||||||
): ApiStarsGiveawayWinnerOption {
|
): ApiStarsGiveawayWinnerOption {
|
||||||
@ -563,7 +561,7 @@ export function buildApiStarsTransaction(transaction: GramJs.StarsTransaction):
|
|||||||
|
|
||||||
const starRefCommision = starrefCommissionPermille ? starrefCommissionPermille / 10 : undefined;
|
const starRefCommision = starrefCommissionPermille ? starrefCommissionPermille / 10 : undefined;
|
||||||
|
|
||||||
const starsAmount = buildApiStarsAmount(amount);
|
const starsAmount = buildApiCurrencyAmount(amount);
|
||||||
if (!starsAmount) {
|
if (!starsAmount) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -572,7 +570,7 @@ export function buildApiStarsTransaction(transaction: GramJs.StarsTransaction):
|
|||||||
id,
|
id,
|
||||||
date,
|
date,
|
||||||
peer: buildApiStarsTransactionPeer(peer),
|
peer: buildApiStarsTransactionPeer(peer),
|
||||||
stars: starsAmount,
|
amount: starsAmount,
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
photo: photo && buildApiWebDocument(photo),
|
photo: photo && buildApiWebDocument(photo),
|
||||||
|
|||||||
@ -33,15 +33,15 @@ import type {
|
|||||||
ApiStory,
|
ApiStory,
|
||||||
ApiStorySkipped,
|
ApiStorySkipped,
|
||||||
ApiThemeParameters,
|
ApiThemeParameters,
|
||||||
|
ApiTypeCurrencyAmount,
|
||||||
ApiVideo,
|
ApiVideo,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
import {
|
import {
|
||||||
ApiMessageEntityTypes,
|
ApiMessageEntityTypes,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
import { CHANNEL_ID_BASE, DEFAULT_STATUS_ICON_ID } from '../../../config';
|
import { CHANNEL_ID_BASE, DEFAULT_STATUS_ICON_ID, STARS_CURRENCY_CODE } from '../../../config';
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
import { buildInputStarsAmount } from '../apiBuilders/payments';
|
|
||||||
import { deserializeBytes } from '../helpers/misc';
|
import { deserializeBytes } from '../helpers/misc';
|
||||||
import localDb from '../localDb';
|
import localDb from '../localDb';
|
||||||
|
|
||||||
@ -890,12 +890,22 @@ export function buildInputReplyTo(replyInfo: ApiInputReplyInfo) {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildInputSuggestedPost(suggestedPostInfo: ApiInputSuggestedPostInfo): GramJs.SuggestedPost {
|
export function buildInputStarsAmount(amount: ApiTypeCurrencyAmount): GramJs.TypeStarsAmount {
|
||||||
const isPaid = Boolean(suggestedPostInfo.price)
|
if (amount.currency === STARS_CURRENCY_CODE) {
|
||||||
&& Boolean((suggestedPostInfo.price.amount || suggestedPostInfo.price.nanos));
|
return new GramJs.StarsAmount({
|
||||||
|
amount: BigInt(amount.amount),
|
||||||
|
nanos: amount.nanos,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GramJs.StarsTonAmount({
|
||||||
|
amount: BigInt(amount.amount),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildInputSuggestedPost(suggestedPostInfo: ApiInputSuggestedPostInfo): GramJs.SuggestedPost {
|
||||||
return new GramJs.SuggestedPost({
|
return new GramJs.SuggestedPost({
|
||||||
price: isPaid ? buildInputStarsAmount(suggestedPostInfo.price!) : undefined,
|
price: suggestedPostInfo.price && buildInputStarsAmount(suggestedPostInfo.price),
|
||||||
scheduleDate: suggestedPostInfo.scheduleDate,
|
scheduleDate: suggestedPostInfo.scheduleDate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import { buildApiChatFromPreview } from '../apiBuilders/chats';
|
|||||||
import { buildApiResaleGifts, buildApiSavedStarGift, buildApiStarGift,
|
import { buildApiResaleGifts, buildApiSavedStarGift, buildApiStarGift,
|
||||||
buildApiStarGiftAttribute, buildInputResaleGiftsAttributes } from '../apiBuilders/gifts';
|
buildApiStarGiftAttribute, buildInputResaleGiftsAttributes } from '../apiBuilders/gifts';
|
||||||
import {
|
import {
|
||||||
buildApiStarsAmount,
|
buildApiCurrencyAmount,
|
||||||
buildApiStarsGiftOptions,
|
buildApiStarsGiftOptions,
|
||||||
buildApiStarsGiveawayOptions,
|
buildApiStarsGiveawayOptions,
|
||||||
buildApiStarsSubscription,
|
buildApiStarsSubscription,
|
||||||
@ -182,18 +182,22 @@ export async function getStarsGiftOptions({
|
|||||||
return result.map(buildApiStarsGiftOptions);
|
return result.map(buildApiStarsGiftOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchStarsStatus() {
|
export async function fetchStarsStatus({
|
||||||
|
isTon,
|
||||||
|
}: {
|
||||||
|
isTon?: boolean;
|
||||||
|
} = {}) {
|
||||||
const result = await invokeRequest(new GramJs.payments.GetStarsStatus({
|
const result = await invokeRequest(new GramJs.payments.GetStarsStatus({
|
||||||
peer: new GramJs.InputPeerSelf(),
|
peer: new GramJs.InputPeerSelf(),
|
||||||
|
ton: isTon || undefined,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const balance = buildApiStarsAmount(result.balance);
|
const balance = buildApiCurrencyAmount(result.balance);
|
||||||
if (!balance) {
|
if (!balance) {
|
||||||
// For now, skip if balance is in TON
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,29 +216,31 @@ export async function fetchStarsTransactions({
|
|||||||
limit = DEFAULT_PRIMITIVES.INT,
|
limit = DEFAULT_PRIMITIVES.INT,
|
||||||
isInbound,
|
isInbound,
|
||||||
isOutbound,
|
isOutbound,
|
||||||
|
isTon,
|
||||||
}: {
|
}: {
|
||||||
peer?: ApiPeer;
|
peer?: ApiPeer;
|
||||||
offset?: string;
|
offset?: string;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
isInbound?: true;
|
isInbound?: boolean;
|
||||||
isOutbound?: true;
|
isOutbound?: boolean;
|
||||||
|
isTon?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const inputPeer = peer ? buildInputPeer(peer.id, peer.accessHash) : new GramJs.InputPeerSelf();
|
const inputPeer = peer ? buildInputPeer(peer.id, peer.accessHash) : new GramJs.InputPeerSelf();
|
||||||
const result = await invokeRequest(new GramJs.payments.GetStarsTransactions({
|
const result = await invokeRequest(new GramJs.payments.GetStarsTransactions({
|
||||||
peer: inputPeer,
|
peer: inputPeer,
|
||||||
offset,
|
offset,
|
||||||
limit,
|
limit,
|
||||||
inbound: isInbound,
|
inbound: isInbound || undefined,
|
||||||
outbound: isOutbound,
|
outbound: isOutbound || undefined,
|
||||||
|
ton: isTon || undefined,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const balance = buildApiStarsAmount(result.balance);
|
const balance = buildApiCurrencyAmount(result.balance);
|
||||||
if (!balance) {
|
if (!balance) {
|
||||||
// For now, skip if balance is in TON
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,14 +252,16 @@ export async function fetchStarsTransactions({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchStarsTransactionById({
|
export async function fetchStarsTransactionById({
|
||||||
id, peer,
|
id, peer, ton,
|
||||||
}: {
|
}: {
|
||||||
id: string;
|
id: string;
|
||||||
peer?: ApiPeer;
|
peer?: ApiPeer;
|
||||||
|
ton?: true;
|
||||||
}) {
|
}) {
|
||||||
const inputPeer = peer ? buildInputPeer(peer.id, peer.accessHash) : new GramJs.InputPeerSelf();
|
const inputPeer = peer ? buildInputPeer(peer.id, peer.accessHash) : new GramJs.InputPeerSelf();
|
||||||
const result = await invokeRequest(new GramJs.payments.GetStarsTransactionsByID({
|
const result = await invokeRequest(new GramJs.payments.GetStarsTransactionsByID({
|
||||||
peer: inputPeer,
|
peer: inputPeer,
|
||||||
|
ton,
|
||||||
id: [new GramJs.InputStarsTransaction({
|
id: [new GramJs.InputStarsTransaction({
|
||||||
id,
|
id,
|
||||||
})],
|
})],
|
||||||
@ -285,9 +293,8 @@ export async function fetchStarsSubscriptions({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const balance = buildApiStarsAmount(result.balance);
|
const balance = buildApiCurrencyAmount(result.balance);
|
||||||
if (!balance) {
|
if (!balance) {
|
||||||
// For now, skip if balance is in TON
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -272,6 +272,22 @@ export async function fetchPremiumGifts() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchTonGifts() {
|
||||||
|
const result = await invokeRequest(new GramJs.messages.GetStickerSet({
|
||||||
|
stickerset: new GramJs.InputStickerSetTonGifts(),
|
||||||
|
hash: DEFAULT_PRIMITIVES.INT,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!(result instanceof GramJs.messages.StickerSet)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
set: buildStickerSet(result.set),
|
||||||
|
stickers: processStickerResult(result.documents),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchDefaultTopicIcons() {
|
export async function fetchDefaultTopicIcons() {
|
||||||
const result = await invokeRequest(new GramJs.messages.GetStickerSet({
|
const result = await invokeRequest(new GramJs.messages.GetStickerSet({
|
||||||
stickerset: new GramJs.InputStickerSetEmojiDefaultTopicIcons(),
|
stickerset: new GramJs.InputStickerSetEmojiDefaultTopicIcons(),
|
||||||
|
|||||||
@ -50,7 +50,7 @@ import {
|
|||||||
buildLangStrings,
|
buildLangStrings,
|
||||||
buildPrivacyKey,
|
buildPrivacyKey,
|
||||||
} from '../apiBuilders/misc';
|
} from '../apiBuilders/misc';
|
||||||
import { buildApiStarsAmount } from '../apiBuilders/payments';
|
import { buildApiCurrencyAmount } from '../apiBuilders/payments';
|
||||||
import { buildApiEmojiStatus, buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
|
import { buildApiEmojiStatus, buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
|
||||||
import {
|
import {
|
||||||
buildApiPaidReactionPrivacy,
|
buildApiPaidReactionPrivacy,
|
||||||
@ -1042,7 +1042,7 @@ export function updater(update: Update) {
|
|||||||
isEnabled: update.enabled ? true : undefined,
|
isEnabled: update.enabled ? true : undefined,
|
||||||
});
|
});
|
||||||
} else if (update instanceof GramJs.UpdateStarsBalance) {
|
} else if (update instanceof GramJs.UpdateStarsBalance) {
|
||||||
const balance = buildApiStarsAmount(update.balance);
|
const balance = buildApiCurrencyAmount(update.balance);
|
||||||
if (!balance) {
|
if (!balance) {
|
||||||
// Skip TON balance updates for now
|
// Skip TON balance updates for now
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { ApiGroupCall, ApiPhoneCallDiscardReason } from './calls';
|
import type { ApiGroupCall, ApiPhoneCallDiscardReason } from './calls';
|
||||||
import type { ApiBotApp, ApiFormattedText, ApiPhoto } from './messages';
|
import type { ApiBotApp, ApiFormattedText, ApiPhoto } from './messages';
|
||||||
import type { ApiTodoItem } from './messages';
|
import type { ApiTodoItem } from './messages';
|
||||||
import type { ApiStarGiftRegular, ApiStarGiftUnique, ApiStarsAmount } from './stars';
|
import type { ApiStarGiftRegular, ApiStarGiftUnique, ApiTypeCurrencyAmount } from './stars';
|
||||||
|
|
||||||
interface ActionMediaType {
|
interface ActionMediaType {
|
||||||
mediaType: 'action';
|
mediaType: 'action';
|
||||||
@ -216,6 +216,15 @@ export interface ApiMessageActionGiftStars extends ActionMediaType {
|
|||||||
transactionId?: string;
|
transactionId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ApiMessageActionGiftTon extends ActionMediaType {
|
||||||
|
type: 'giftTon';
|
||||||
|
currency: string;
|
||||||
|
amount: number;
|
||||||
|
cryptoCurrency: string;
|
||||||
|
cryptoAmount: number;
|
||||||
|
transactionId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ApiMessageActionPrizeStars extends ActionMediaType {
|
export interface ApiMessageActionPrizeStars extends ActionMediaType {
|
||||||
type: 'prizeStars';
|
type: 'prizeStars';
|
||||||
isUnclaimed?: true;
|
isUnclaimed?: true;
|
||||||
@ -288,12 +297,12 @@ export interface ApiMessageActionSuggestedPostApproval extends ActionMediaType {
|
|||||||
isBalanceTooLow?: boolean;
|
isBalanceTooLow?: boolean;
|
||||||
rejectComment?: string;
|
rejectComment?: string;
|
||||||
scheduleDate?: number;
|
scheduleDate?: number;
|
||||||
amount?: ApiStarsAmount;
|
amount?: ApiTypeCurrencyAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiMessageActionSuggestedPostSuccess extends ActionMediaType {
|
export interface ApiMessageActionSuggestedPostSuccess extends ActionMediaType {
|
||||||
type: 'suggestedPostSuccess';
|
type: 'suggestedPostSuccess';
|
||||||
amount?: ApiStarsAmount;
|
amount?: ApiTypeCurrencyAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiMessageActionSuggestedPostRefund extends ActionMediaType {
|
export interface ApiMessageActionSuggestedPostRefund extends ActionMediaType {
|
||||||
@ -328,7 +337,7 @@ export type ApiMessageAction = ApiMessageActionUnsupported | ApiMessageActionCha
|
|||||||
| ApiMessageActionTopicCreate | ApiMessageActionTopicEdit | ApiMessageActionSuggestProfilePhoto
|
| ApiMessageActionTopicCreate | ApiMessageActionTopicEdit | ApiMessageActionSuggestProfilePhoto
|
||||||
| ApiMessageActionChannelJoined | ApiMessageActionGiftCode | ApiMessageActionGiveawayLaunch
|
| ApiMessageActionChannelJoined | ApiMessageActionGiftCode | ApiMessageActionGiveawayLaunch
|
||||||
| ApiMessageActionGiveawayResults | ApiMessageActionPaymentRefunded | ApiMessageActionGiftStars
|
| ApiMessageActionGiveawayResults | ApiMessageActionPaymentRefunded | ApiMessageActionGiftStars
|
||||||
| ApiMessageActionPrizeStars | ApiMessageActionStarGift | ApiMessageActionStarGiftUnique
|
| ApiMessageActionGiftTon | ApiMessageActionPrizeStars | ApiMessageActionStarGift | ApiMessageActionStarGiftUnique
|
||||||
| ApiMessageActionPaidMessagesRefunded | ApiMessageActionPaidMessagesPrice | ApiMessageActionSuggestedPostApproval
|
| ApiMessageActionPaidMessagesRefunded | ApiMessageActionPaidMessagesPrice | ApiMessageActionSuggestedPostApproval
|
||||||
| ApiMessageActionSuggestedPostSuccess | ApiMessageActionSuggestedPostRefund | ApiMessageActionTodoCompletions
|
| ApiMessageActionSuggestedPostSuccess | ApiMessageActionSuggestedPostRefund | ApiMessageActionTodoCompletions
|
||||||
| ApiMessageActionTodoAppendTasks;
|
| ApiMessageActionTodoAppendTasks;
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import type { ApiMessageAction } from './messageActions';
|
|||||||
import type {
|
import type {
|
||||||
ApiLabeledPrice,
|
ApiLabeledPrice,
|
||||||
} from './payments';
|
} from './payments';
|
||||||
import type { ApiStarGiftUnique, ApiStarsAmount } from './stars';
|
import type { ApiStarGiftUnique, ApiTypeCurrencyAmount } from './stars';
|
||||||
import type {
|
import type {
|
||||||
ApiMessageStoryData, ApiStory, ApiWebPageStickerData, ApiWebPageStoryData,
|
ApiMessageStoryData, ApiStory, ApiWebPageStickerData, ApiWebPageStoryData,
|
||||||
} from './stories';
|
} from './stories';
|
||||||
@ -415,7 +415,7 @@ export interface ApiInputMessageReplyInfo {
|
|||||||
export interface ApiSuggestedPost {
|
export interface ApiSuggestedPost {
|
||||||
isAccepted?: true;
|
isAccepted?: true;
|
||||||
isRejected?: true;
|
isRejected?: true;
|
||||||
price?: ApiStarsAmount;
|
price?: ApiTypeCurrencyAmount;
|
||||||
scheduleDate?: number;
|
scheduleDate?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,7 +426,7 @@ export interface ApiInputStoryReplyInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiInputSuggestedPostInfo {
|
export interface ApiInputSuggestedPostInfo {
|
||||||
price?: ApiStarsAmount;
|
price?: ApiTypeCurrencyAmount;
|
||||||
scheduleDate?: number;
|
scheduleDate?: number;
|
||||||
isAccepted?: true;
|
isAccepted?: true;
|
||||||
isRejected?: true;
|
isRejected?: true;
|
||||||
|
|||||||
@ -259,6 +259,10 @@ export interface ApiAppConfig {
|
|||||||
starsSuggestedPostFutureMax?: number;
|
starsSuggestedPostFutureMax?: number;
|
||||||
starsSuggestedPostFutureMin?: number;
|
starsSuggestedPostFutureMin?: number;
|
||||||
tonSuggestedPostCommissionPermille?: number;
|
tonSuggestedPostCommissionPermille?: number;
|
||||||
|
tonSuggestedPostAmountMax?: number;
|
||||||
|
tonSuggestedPostAmountMin?: number;
|
||||||
|
tonUsdRate?: number;
|
||||||
|
tonTopupUrl?: string;
|
||||||
pollMaxAnswers?: number;
|
pollMaxAnswers?: number;
|
||||||
todoItemsMax?: number;
|
todoItemsMax?: number;
|
||||||
todoTitleLengthMax?: number;
|
todoTitleLengthMax?: number;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import type { STARS_CURRENCY_CODE, TON_CURRENCY_CODE } from '../../config';
|
||||||
import type { ApiWebDocument } from './bots';
|
import type { ApiWebDocument } from './bots';
|
||||||
import type { ApiChat } from './chats';
|
import type { ApiChat } from './chats';
|
||||||
import type { ApiFormattedText, ApiSticker, BoughtPaidMedia } from './messages';
|
import type { ApiFormattedText, ApiSticker, BoughtPaidMedia } from './messages';
|
||||||
@ -153,11 +154,19 @@ export type ApiRequestInputSavedStarGiftChat = {
|
|||||||
};
|
};
|
||||||
export type ApiRequestInputSavedStarGift = ApiRequestInputSavedStarGiftUser | ApiRequestInputSavedStarGiftChat;
|
export type ApiRequestInputSavedStarGift = ApiRequestInputSavedStarGiftUser | ApiRequestInputSavedStarGiftChat;
|
||||||
|
|
||||||
|
export type ApiTypeCurrencyAmount = ApiStarsAmount | ApiTonAmount;
|
||||||
|
|
||||||
export interface ApiStarsAmount {
|
export interface ApiStarsAmount {
|
||||||
|
currency: typeof STARS_CURRENCY_CODE;
|
||||||
amount: number;
|
amount: number;
|
||||||
nanos: number;
|
nanos: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ApiTonAmount {
|
||||||
|
currency: typeof TON_CURRENCY_CODE;
|
||||||
|
amount: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ApiStarsTransactionPeerUnsupported {
|
export interface ApiStarsTransactionPeerUnsupported {
|
||||||
type: 'unsupported';
|
type: 'unsupported';
|
||||||
}
|
}
|
||||||
@ -205,7 +214,7 @@ export interface ApiStarsTransaction {
|
|||||||
id?: string;
|
id?: string;
|
||||||
peer: ApiStarsTransactionPeer;
|
peer: ApiStarsTransactionPeer;
|
||||||
messageId?: number;
|
messageId?: number;
|
||||||
stars: ApiStarsAmount;
|
amount: ApiTypeCurrencyAmount;
|
||||||
isRefund?: true;
|
isRefund?: true;
|
||||||
isGift?: true;
|
isGift?: true;
|
||||||
starGift?: ApiStarGift;
|
starGift?: ApiStarGift;
|
||||||
|
|||||||
@ -43,7 +43,7 @@ import type {
|
|||||||
ApiSessionData,
|
ApiSessionData,
|
||||||
} from './misc';
|
} from './misc';
|
||||||
import type { ApiPrivacyKey, LangPackStringValue, PrivacyVisibility } from './settings';
|
import type { ApiPrivacyKey, LangPackStringValue, PrivacyVisibility } from './settings';
|
||||||
import type { ApiStarsAmount } from './stars';
|
import type { ApiTypeCurrencyAmount } from './stars';
|
||||||
import type { ApiStealthMode, ApiStory, ApiStorySkipped } from './stories';
|
import type { ApiStealthMode, ApiStory, ApiStorySkipped } from './stories';
|
||||||
import type {
|
import type {
|
||||||
ApiEmojiStatusType, ApiUser, ApiUserFullInfo, ApiUserStatus,
|
ApiEmojiStatusType, ApiUser, ApiUserFullInfo, ApiUserStatus,
|
||||||
@ -794,7 +794,7 @@ export type ApiUpdatePremiumFloodWait = {
|
|||||||
|
|
||||||
export type ApiUpdateStarsBalance = {
|
export type ApiUpdateStarsBalance = {
|
||||||
'@type': 'updateStarsBalance';
|
'@type': 'updateStarsBalance';
|
||||||
balance: ApiStarsAmount;
|
balance: ApiTypeCurrencyAmount;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ApiUpdateDeleteProfilePhoto = {
|
export type ApiUpdateDeleteProfilePhoto = {
|
||||||
|
|||||||
@ -657,6 +657,7 @@
|
|||||||
"MenuStickers" = "Stickers and Emoji";
|
"MenuStickers" = "Stickers and Emoji";
|
||||||
"MenuAnimations" = "Animations and Performance";
|
"MenuAnimations" = "Animations and Performance";
|
||||||
"MenuStars" = "My Stars";
|
"MenuStars" = "My Stars";
|
||||||
|
"MenuTon" = "My TON";
|
||||||
"MenuSendGift" = "Send a Gift";
|
"MenuSendGift" = "Send a Gift";
|
||||||
"MenuTelegramFaq" = "Telegram FAQ";
|
"MenuTelegramFaq" = "Telegram FAQ";
|
||||||
"MenuPrivacyPolicy" = "Privacy Policy";
|
"MenuPrivacyPolicy" = "Privacy Policy";
|
||||||
@ -1880,6 +1881,10 @@
|
|||||||
"ActionGiftStarsTitle_one" = "{amount} Star";
|
"ActionGiftStarsTitle_one" = "{amount} Star";
|
||||||
"ActionGiftStarsTitle_other" = "{amount} Stars";
|
"ActionGiftStarsTitle_other" = "{amount} Stars";
|
||||||
"ActionGiftStarsText" = "Use Stars to unlock content and services on Telegram.";
|
"ActionGiftStarsText" = "Use Stars to unlock content and services on Telegram.";
|
||||||
|
"TonAmount" = "💎{amount}";
|
||||||
|
"TonAmountText_one" = "{amount} TON";
|
||||||
|
"TonAmountText_other" = "{amount} TON";
|
||||||
|
"ActionGiftCostCrypto" = "{cryptoPrice} ({price})";
|
||||||
"ActionBoostApplyYou_one" = "You boosted the group";
|
"ActionBoostApplyYou_one" = "You boosted the group";
|
||||||
"ActionBoostApplyYou_other" = "You boosted the group {count} times";
|
"ActionBoostApplyYou_other" = "You boosted the group {count} times";
|
||||||
"ActionBoostApply_one" = "{from} boosted the group";
|
"ActionBoostApply_one" = "{from} boosted the group";
|
||||||
@ -2040,11 +2045,14 @@
|
|||||||
"TitleTime" = "Time";
|
"TitleTime" = "Time";
|
||||||
"TitleSuggestMessage" = "Suggest a Message";
|
"TitleSuggestMessage" = "Suggest a Message";
|
||||||
"TitleSuggestedChanges" = "Suggest Changes";
|
"TitleSuggestedChanges" = "Suggest Changes";
|
||||||
"EnterPriceInStars" = "Enter Price In Stars";
|
"SuggestMessageNoPrice" = "Free";
|
||||||
"SuggestMessagePriceDescription" = "Choose how many {currency} to pay to publish this post.";
|
"EnterPriceInStars" = "Enter Price in Stars";
|
||||||
|
"EnterPriceInTon" = "Enter Price in TON";
|
||||||
|
"SuggestMessagePriceDescriptionStars" = "Choose how many Stars to pay to publish this post.";
|
||||||
|
"SuggestMessagePriceDescriptionTon" = "Choose how many TON to pay to publish this post.";
|
||||||
"SuggestMessageDateTimeHint" = "Select the date and time you want the post to be published.";
|
"SuggestMessageDateTimeHint" = "Select the date and time you want the post to be published.";
|
||||||
"SuggestMessageTimeDescription" = "{hint} The post will remain available for at least {duration} from this date.";
|
"SuggestMessageTimeDescription" = "{hint} The post will remain available for at least {duration} from this date.";
|
||||||
"TitleAnytime" = "Anytime";
|
"SuggestMessageAnytime" = "Anytime";
|
||||||
"ButtonOfferAmount" = "Offer {amount}";
|
"ButtonOfferAmount" = "Offer {amount}";
|
||||||
"ButtonOfferFree" = "Offer Free";
|
"ButtonOfferFree" = "Offer Free";
|
||||||
"ButtonUpdateTerms" = "Update Terms";
|
"ButtonUpdateTerms" = "Update Terms";
|
||||||
@ -2070,6 +2078,7 @@
|
|||||||
"SuggestedPostRefundedByUser" = "{channel} will not receive {amount} because {user} requested a refund.";
|
"SuggestedPostRefundedByUser" = "{channel} will not receive {amount} because {user} requested a refund.";
|
||||||
"SuggestedPostRefundedByChannel" = "{amount} was returned to {peer} because {channel} deleted the message.";
|
"SuggestedPostRefundedByChannel" = "{amount} was returned to {peer} because {channel} deleted the message.";
|
||||||
"CurrencyStars" = "Stars";
|
"CurrencyStars" = "Stars";
|
||||||
|
"CurrencyTon" = "TON";
|
||||||
"DeclineReasonPlaceholder" = "Add a reason (optional)";
|
"DeclineReasonPlaceholder" = "Add a reason (optional)";
|
||||||
"DeclinePostDialogQuestion" = "Do you want to decline this post from **{sender}**?";
|
"DeclinePostDialogQuestion" = "Do you want to decline this post from **{sender}**?";
|
||||||
"SuggestedPostRejected" = "**{peer}** rejected this message.";
|
"SuggestedPostRejected" = "**{peer}** rejected this message.";
|
||||||
@ -2127,3 +2136,8 @@
|
|||||||
"ToDoListErrorChooseTasks" = "Please enter at least one task.";
|
"ToDoListErrorChooseTasks" = "Please enter at least one task.";
|
||||||
"GiftInfoCollectibleBy" = "Collectible #{number} by **{owner}**";
|
"GiftInfoCollectibleBy" = "Collectible #{number} by **{owner}**";
|
||||||
"PremiumPreviewTodo" = "Checklists";
|
"PremiumPreviewTodo" = "Checklists";
|
||||||
|
"MenuTon" = "My TON";
|
||||||
|
"DescriptionAboutTon" = "Offer TON to submit post suggestions to channels on Telegram.";
|
||||||
|
"ButtonTopUpViaFragment" = "Top-Up Via Fragment";
|
||||||
|
"TonModalHint" = "You can top-up your TON using Fragment.";
|
||||||
|
"TonGiftReceived" = "TON Top-Up";
|
||||||
|
|||||||
BIN
src/assets/tgs/Diamond.tgs
Normal file
BIN
src/assets/tgs/Diamond.tgs
Normal file
Binary file not shown.
@ -156,11 +156,19 @@
|
|||||||
font-weight: var(--font-weight-semibold) !important;
|
font-weight: var(--font-weight-semibold) !important;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
|
||||||
animation: hide-icon 0.4s forwards ease-out;
|
|
||||||
&.visible {
|
&.visible {
|
||||||
animation: grow-icon 0.4s ease-out;
|
animation: grow-icon 0.4s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.hiding {
|
||||||
|
animation: hide-icon 0.4s forwards ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.hidden {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,6 +55,7 @@ import {
|
|||||||
SCHEDULED_WHEN_ONLINE,
|
SCHEDULED_WHEN_ONLINE,
|
||||||
SEND_MESSAGE_ACTION_INTERVAL,
|
SEND_MESSAGE_ACTION_INTERVAL,
|
||||||
SERVICE_NOTIFICATIONS_USER_ID,
|
SERVICE_NOTIFICATIONS_USER_ID,
|
||||||
|
STARS_CURRENCY_CODE,
|
||||||
} from '../../config';
|
} from '../../config';
|
||||||
import { requestMeasure, requestNextMutation } from '../../lib/fasterdom/fasterdom';
|
import { requestMeasure, requestNextMutation } from '../../lib/fasterdom/fasterdom';
|
||||||
import {
|
import {
|
||||||
@ -142,6 +143,7 @@ import useGetSelectionRange from '../../hooks/useGetSelectionRange';
|
|||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
import useLastCallback from '../../hooks/useLastCallback';
|
import useLastCallback from '../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../hooks/useOldLang';
|
import useOldLang from '../../hooks/useOldLang';
|
||||||
|
import usePrevious from '../../hooks/usePrevious';
|
||||||
import usePreviousDeprecated from '../../hooks/usePreviousDeprecated';
|
import usePreviousDeprecated from '../../hooks/usePreviousDeprecated';
|
||||||
import useSchedule from '../../hooks/useSchedule';
|
import useSchedule from '../../hooks/useSchedule';
|
||||||
import useSendMessageAction from '../../hooks/useSendMessageAction';
|
import useSendMessageAction from '../../hooks/useSendMessageAction';
|
||||||
@ -1576,7 +1578,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
});
|
});
|
||||||
const handleSuggestPostClick = useLastCallback(() => {
|
const handleSuggestPostClick = useLastCallback(() => {
|
||||||
updateDraftSuggestedPostInfo({
|
updateDraftSuggestedPostInfo({
|
||||||
price: { amount: 0, nanos: 0 },
|
price: { currency: STARS_CURRENCY_CODE, amount: 0, nanos: 0 },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1874,6 +1876,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
const effectEmoji = areEffectsSupported && effect?.emoticon;
|
const effectEmoji = areEffectsSupported && effect?.emoticon;
|
||||||
|
|
||||||
const shouldRenderPaidBadge = Boolean(paidMessagesStars && mainButtonState === MainButtonState.Send);
|
const shouldRenderPaidBadge = Boolean(paidMessagesStars && mainButtonState === MainButtonState.Send);
|
||||||
|
const prevShouldRenderPaidBadge = usePrevious(shouldRenderPaidBadge);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={fullClassName}>
|
<div className={fullClassName}>
|
||||||
@ -2358,7 +2361,12 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
{isInMessageList && <Icon name="schedule" />}
|
{isInMessageList && <Icon name="schedule" />}
|
||||||
{isInMessageList && <Icon name="check" />}
|
{isInMessageList && <Icon name="check" />}
|
||||||
<Button
|
<Button
|
||||||
className={buildClassName('paidStarsBadge', shouldRenderPaidBadge && 'visible')}
|
className={buildClassName(
|
||||||
|
'paidStarsBadge',
|
||||||
|
shouldRenderPaidBadge && 'visible',
|
||||||
|
prevShouldRenderPaidBadge && !shouldRenderPaidBadge && 'hiding',
|
||||||
|
!prevShouldRenderPaidBadge && !shouldRenderPaidBadge && 'hidden',
|
||||||
|
)}
|
||||||
nonInteractive
|
nonInteractive
|
||||||
size="tiny"
|
size="tiny"
|
||||||
color="stars"
|
color="stars"
|
||||||
|
|||||||
@ -68,6 +68,11 @@
|
|||||||
text-indent: 0rem;
|
text-indent: 0rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.suggested-price-ton-icon {
|
||||||
|
margin-left: 0rem;
|
||||||
|
text-indent: 0rem;
|
||||||
|
}
|
||||||
|
|
||||||
&--background-icons {
|
&--background-icons {
|
||||||
margin: -0.1875rem -0.375rem -0.1875rem -0.1875rem;
|
margin: -0.1875rem -0.375rem -0.1875rem -0.1875rem;
|
||||||
}
|
}
|
||||||
@ -183,6 +188,11 @@
|
|||||||
content: none;
|
content: none;
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.suggested-post-price-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.multiline {
|
.multiline {
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
|||||||
import type { ChatTranslatedMessages } from '../../../types';
|
import type { ChatTranslatedMessages } from '../../../types';
|
||||||
import type { IconName } from '../../../types/icons';
|
import type { IconName } from '../../../types/icons';
|
||||||
|
|
||||||
import { CONTENT_NOT_SUPPORTED } from '../../../config';
|
import { CONTENT_NOT_SUPPORTED, TON_CURRENCY_CODE } from '../../../config';
|
||||||
import {
|
import {
|
||||||
getMessageIsSpoiler,
|
getMessageIsSpoiler,
|
||||||
getMessageMediaHash,
|
getMessageMediaHash,
|
||||||
@ -26,7 +26,7 @@ import buildClassName from '../../../util/buildClassName';
|
|||||||
import { formatScheduledDateTime } from '../../../util/dates/dateFormat';
|
import { formatScheduledDateTime } from '../../../util/dates/dateFormat';
|
||||||
import { isUserId } from '../../../util/entities/ids';
|
import { isUserId } from '../../../util/entities/ids';
|
||||||
import freezeWhenClosed from '../../../util/hoc/freezeWhenClosed';
|
import freezeWhenClosed from '../../../util/hoc/freezeWhenClosed';
|
||||||
import { formatStarsAsIcon } from '../../../util/localization/format';
|
import { formatStarsAsIcon, formatTonAsIcon } from '../../../util/localization/format';
|
||||||
import { getPictogramDimensions } from '../helpers/mediaDimensions';
|
import { getPictogramDimensions } from '../helpers/mediaDimensions';
|
||||||
import renderText from '../helpers/renderText';
|
import renderText from '../helpers/renderText';
|
||||||
import { renderTextWithEntities } from '../helpers/renderTextWithEntities';
|
import { renderTextWithEntities } from '../helpers/renderTextWithEntities';
|
||||||
@ -150,23 +150,34 @@ const EmbeddedMessage: FC<OwnProps> = ({
|
|||||||
return lang('ComposerEmbeddedMessageSuggestedPostDescription');
|
return lang('ComposerEmbeddedMessageSuggestedPostDescription');
|
||||||
}
|
}
|
||||||
const priceText = suggestedPostInfo.price
|
const priceText = suggestedPostInfo.price
|
||||||
? formatStarsAsIcon(lang, suggestedPostInfo.price.amount, {
|
? (suggestedPostInfo.price.currency === TON_CURRENCY_CODE
|
||||||
className: 'suggested-price-star-icon',
|
? formatTonAsIcon(lang, suggestedPostInfo.price.amount, {
|
||||||
})
|
className: 'suggested-price-ton-icon',
|
||||||
|
shouldConvertFromNanos: true,
|
||||||
|
})
|
||||||
|
: formatStarsAsIcon(lang, suggestedPostInfo.price.amount, {
|
||||||
|
className: 'suggested-price-star-icon',
|
||||||
|
}))
|
||||||
: '';
|
: '';
|
||||||
const scheduleText = suggestedPostInfo.scheduleDate
|
const scheduleText = suggestedPostInfo.scheduleDate
|
||||||
? formatScheduledDateTime(suggestedPostInfo.scheduleDate, lang, oldLang)
|
? formatScheduledDateTime(suggestedPostInfo.scheduleDate, lang, oldLang)
|
||||||
: '';
|
: '';
|
||||||
if (priceText && !scheduleText) {
|
if (priceText && !scheduleText) {
|
||||||
return lang('TitleSuggestedPostAmountForAnyTime',
|
return (
|
||||||
{ amount: priceText },
|
<span className="suggested-post-price-wrapper">
|
||||||
{
|
{
|
||||||
withNodes: true,
|
lang('TitleSuggestedPostAmountForAnyTime',
|
||||||
withMarkdown: true,
|
{ amount: priceText },
|
||||||
});
|
{
|
||||||
|
withNodes: true,
|
||||||
|
withMarkdown: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<span>
|
<span className="suggested-post-price-wrapper">
|
||||||
{priceText}
|
{priceText}
|
||||||
{scheduleText ? ` • ${scheduleText}` : ''}
|
{scheduleText ? ` • ${scheduleText}` : ''}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import VoiceAllowTalk from '../../../assets/tgs/calls/VoiceAllowTalk.tgs';
|
|||||||
import VoiceMini from '../../../assets/tgs/calls/VoiceMini.tgs';
|
import VoiceMini from '../../../assets/tgs/calls/VoiceMini.tgs';
|
||||||
import VoiceMuted from '../../../assets/tgs/calls/VoiceMuted.tgs';
|
import VoiceMuted from '../../../assets/tgs/calls/VoiceMuted.tgs';
|
||||||
import VoiceOutlined from '../../../assets/tgs/calls/VoiceOutlined.tgs';
|
import VoiceOutlined from '../../../assets/tgs/calls/VoiceOutlined.tgs';
|
||||||
|
import Diamond from '../../../assets/tgs/Diamond.tgs';
|
||||||
import Flame from '../../../assets/tgs/general/Flame.tgs';
|
import Flame from '../../../assets/tgs/general/Flame.tgs';
|
||||||
import Fragment from '../../../assets/tgs/general/Fragment.tgs';
|
import Fragment from '../../../assets/tgs/general/Fragment.tgs';
|
||||||
import Mention from '../../../assets/tgs/general/Mention.tgs';
|
import Mention from '../../../assets/tgs/general/Mention.tgs';
|
||||||
@ -68,4 +69,5 @@ export const LOCAL_TGS_URLS = {
|
|||||||
Report,
|
Report,
|
||||||
SearchingDuck,
|
SearchingDuck,
|
||||||
BannedDuck,
|
BannedDuck,
|
||||||
|
Diamond,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,21 +2,23 @@ import type { FC } from '../../../lib/teact/teact';
|
|||||||
import { memo, useEffect } from '../../../lib/teact/teact';
|
import { memo, useEffect } from '../../../lib/teact/teact';
|
||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import type { ApiStarsAmount } from '../../../api/types';
|
import type { ApiStarsAmount, ApiTonAmount } from '../../../api/types';
|
||||||
import { SettingsScreens } from '../../../types';
|
import { SettingsScreens } from '../../../types';
|
||||||
|
|
||||||
import { FAQ_URL, PRIVACY_URL } from '../../../config';
|
import { FAQ_URL, PRIVACY_URL, TON_CURRENCY_CODE } from '../../../config';
|
||||||
import { formatStarsAmount } from '../../../global/helpers/payments';
|
import { formatStarsAmount } from '../../../global/helpers/payments';
|
||||||
import {
|
import {
|
||||||
selectIsGiveawayGiftsPurchaseAvailable,
|
selectIsGiveawayGiftsPurchaseAvailable,
|
||||||
selectIsPremiumPurchaseBlocked,
|
selectIsPremiumPurchaseBlocked,
|
||||||
} from '../../../global/selectors';
|
} from '../../../global/selectors';
|
||||||
|
import { convertCurrencyFromBaseUnit } from '../../../util/formatCurrency';
|
||||||
|
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
|
import Icon from '../../common/icons/Icon';
|
||||||
import StarIcon from '../../common/icons/StarIcon';
|
import StarIcon from '../../common/icons/StarIcon';
|
||||||
import ChatExtra from '../../common/profile/ChatExtra';
|
import ChatExtra from '../../common/profile/ChatExtra';
|
||||||
import ProfileInfo from '../../common/ProfileInfo';
|
import ProfileInfo from '../../common/ProfileInfo';
|
||||||
@ -34,6 +36,7 @@ type StateProps = {
|
|||||||
canBuyPremium?: boolean;
|
canBuyPremium?: boolean;
|
||||||
isGiveawayAvailable?: boolean;
|
isGiveawayAvailable?: boolean;
|
||||||
starsBalance?: ApiStarsAmount;
|
starsBalance?: ApiStarsAmount;
|
||||||
|
tonBalance?: ApiTonAmount;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SettingsMain: FC<OwnProps & StateProps> = ({
|
const SettingsMain: FC<OwnProps & StateProps> = ({
|
||||||
@ -43,6 +46,7 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
|
|||||||
canBuyPremium,
|
canBuyPremium,
|
||||||
isGiveawayAvailable,
|
isGiveawayAvailable,
|
||||||
starsBalance,
|
starsBalance,
|
||||||
|
tonBalance,
|
||||||
onReset,
|
onReset,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
@ -192,6 +196,18 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
<ListItem
|
||||||
|
leftElement={<Icon className="icon ListItem-main-icon" name="toncoin" />}
|
||||||
|
narrow
|
||||||
|
onClick={() => openStarsBalanceModal({ currency: TON_CURRENCY_CODE })}
|
||||||
|
>
|
||||||
|
{lang('MenuTon')}
|
||||||
|
{Boolean(tonBalance) && (
|
||||||
|
<span className="settings-item__current-value">
|
||||||
|
{convertCurrencyFromBaseUnit(tonBalance.amount, tonBalance.currency)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</ListItem>
|
||||||
{isGiveawayAvailable && (
|
{isGiveawayAvailable && (
|
||||||
<ListItem
|
<ListItem
|
||||||
icon="gift"
|
icon="gift"
|
||||||
@ -245,6 +261,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const { currentUserId } = global;
|
const { currentUserId } = global;
|
||||||
const isGiveawayAvailable = selectIsGiveawayGiftsPurchaseAvailable(global);
|
const isGiveawayAvailable = selectIsGiveawayGiftsPurchaseAvailable(global);
|
||||||
const starsBalance = global.stars?.balance;
|
const starsBalance = global.stars?.balance;
|
||||||
|
const tonBalance = global.ton?.balance;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sessionCount: global.activeSessions.orderedHashes.length,
|
sessionCount: global.activeSessions.orderedHashes.length,
|
||||||
@ -252,6 +269,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
canBuyPremium: !selectIsPremiumPurchaseBlocked(global),
|
canBuyPremium: !selectIsPremiumPurchaseBlocked(global),
|
||||||
isGiveawayAvailable,
|
isGiveawayAvailable,
|
||||||
starsBalance,
|
starsBalance,
|
||||||
|
tonBalance,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(SettingsMain));
|
)(SettingsMain));
|
||||||
|
|||||||
@ -214,6 +214,7 @@ const Main = ({
|
|||||||
loadAvailableReactions,
|
loadAvailableReactions,
|
||||||
loadStickerSets,
|
loadStickerSets,
|
||||||
loadPremiumGifts,
|
loadPremiumGifts,
|
||||||
|
loadTonGifts,
|
||||||
loadStarGifts,
|
loadStarGifts,
|
||||||
loadDefaultTopicIcons,
|
loadDefaultTopicIcons,
|
||||||
loadAddedStickers,
|
loadAddedStickers,
|
||||||
@ -347,6 +348,7 @@ const Main = ({
|
|||||||
loadUserCollectibleStatuses();
|
loadUserCollectibleStatuses();
|
||||||
loadGenericEmojiEffects();
|
loadGenericEmojiEffects();
|
||||||
loadPremiumGifts();
|
loadPremiumGifts();
|
||||||
|
loadTonGifts();
|
||||||
loadStarGifts();
|
loadStarGifts();
|
||||||
loadAvailableEffects();
|
loadAvailableEffects();
|
||||||
loadBirthdayNumbersStickers();
|
loadBirthdayNumbersStickers();
|
||||||
|
|||||||
@ -23,8 +23,9 @@ import { getPeerTitle } from '../../global/helpers/peers';
|
|||||||
import { selectChatMessage, selectSender } from '../../global/selectors';
|
import { selectChatMessage, selectSender } from '../../global/selectors';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import { formatHumanDate, formatScheduledDateTime } from '../../util/dates/dateFormat';
|
import { formatHumanDate, formatScheduledDateTime } from '../../util/dates/dateFormat';
|
||||||
|
import { convertTonFromNanos } from '../../util/formatCurrency';
|
||||||
import { compact } from '../../util/iteratees';
|
import { compact } from '../../util/iteratees';
|
||||||
import { formatStarsAsText } from '../../util/localization/format';
|
import { formatStarsAsText, formatTonAsText } from '../../util/localization/format';
|
||||||
import { isAlbum } from './helpers/groupMessages';
|
import { isAlbum } from './helpers/groupMessages';
|
||||||
import { preventMessageInputBlur } from './helpers/preventMessageInputBlur';
|
import { preventMessageInputBlur } from './helpers/preventMessageInputBlur';
|
||||||
import { renderPeerLink } from './message/helpers/messageActions';
|
import { renderPeerLink } from './message/helpers/messageActions';
|
||||||
@ -202,8 +203,14 @@ const MessageListContent: FC<OwnProps> = ({
|
|||||||
: lang('ActionSuggestedPostIncoming', { user: userLink }, { withNodes: true, withMarkdown: true });
|
: lang('ActionSuggestedPostIncoming', { user: userLink }, { withNodes: true, withMarkdown: true });
|
||||||
|
|
||||||
const tableData: TableEntry[] = compact([
|
const tableData: TableEntry[] = compact([
|
||||||
price && [lang('TitlePrice'), formatStarsAsText(lang, price.amount)],
|
[lang('TitlePrice'), price ? (price.currency === 'TON'
|
||||||
Boolean(scheduleDate) && [lang('TitleTime'), formatScheduledDateTime(scheduleDate, lang, oldLang)],
|
? formatTonAsText(lang, convertTonFromNanos(price.amount))
|
||||||
|
: formatStarsAsText(lang, price.amount)) : lang('SuggestMessageNoPrice')],
|
||||||
|
[lang('TitleTime'),
|
||||||
|
scheduleDate
|
||||||
|
? formatScheduledDateTime(scheduleDate, lang, oldLang)
|
||||||
|
: lang('SuggestMessageAnytime'),
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -48,6 +48,7 @@ import {
|
|||||||
selectCurrentMessageList,
|
selectCurrentMessageList,
|
||||||
selectCurrentMiddleSearch,
|
selectCurrentMiddleSearch,
|
||||||
selectDraft,
|
selectDraft,
|
||||||
|
selectEditingId,
|
||||||
selectIsChatBotNotStarted,
|
selectIsChatBotNotStarted,
|
||||||
selectIsCurrentUserFrozen,
|
selectIsCurrentUserFrozen,
|
||||||
selectIsInSelectMode,
|
selectIsInSelectMode,
|
||||||
@ -788,6 +789,8 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const chatFullInfo = chatId ? selectChatFullInfo(global, chatId) : undefined;
|
const chatFullInfo = chatId ? selectChatFullInfo(global, chatId) : undefined;
|
||||||
const userFullInfo = chatId ? selectUserFullInfo(global, chatId) : undefined;
|
const userFullInfo = chatId ? selectUserFullInfo(global, chatId) : undefined;
|
||||||
|
|
||||||
|
const editingId = selectEditingId(global, chatId, threadId);
|
||||||
|
|
||||||
const threadInfo = selectThreadInfo(global, chatId, threadId);
|
const threadInfo = selectThreadInfo(global, chatId, threadId);
|
||||||
const isMessageThread = Boolean(!threadInfo?.isCommentsInfo && threadInfo?.fromChannelId);
|
const isMessageThread = Boolean(!threadInfo?.isCommentsInfo && threadInfo?.fromChannelId);
|
||||||
const topic = selectTopic(global, chatId, threadId);
|
const topic = selectTopic(global, chatId, threadId);
|
||||||
@ -814,7 +817,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
? threadId === MAIN_THREAD_ID && !draftReplyInfo && (selectTopic(global, chatId, GENERAL_TOPIC_ID)?.isClosed)
|
? threadId === MAIN_THREAD_ID && !draftReplyInfo && (selectTopic(global, chatId, GENERAL_TOPIC_ID)?.isClosed)
|
||||||
: false;
|
: false;
|
||||||
const isMonoforumAdmin = selectIsMonoforumAdmin(global, chatId);
|
const isMonoforumAdmin = selectIsMonoforumAdmin(global, chatId);
|
||||||
const shouldBlockSendInMonoforum = Boolean(chat?.isMonoforum && !draftReplyInfo && isMonoforumAdmin);
|
const shouldBlockSendInMonoforum = Boolean(chat?.isMonoforum && !draftReplyInfo && isMonoforumAdmin && !editingId);
|
||||||
const topics = selectTopics(global, chatId);
|
const topics = selectTopics(global, chatId);
|
||||||
|
|
||||||
const isSavedDialog = getIsSavedDialog(chatId, threadId, global.currentUserId);
|
const isSavedDialog = getIsSavedDialog(chatId, threadId, global.currentUserId);
|
||||||
|
|||||||
@ -294,6 +294,7 @@ const ActionMessage = ({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'giftTon':
|
||||||
case 'giftStars': {
|
case 'giftStars': {
|
||||||
openStarsTransactionFromGift({
|
openStarsTransactionFromGift({
|
||||||
chatId: message.chatId,
|
chatId: message.chatId,
|
||||||
@ -377,6 +378,7 @@ const ActionMessage = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
case 'giftPremium':
|
case 'giftPremium':
|
||||||
|
case 'giftTon':
|
||||||
case 'giftStars':
|
case 'giftStars':
|
||||||
return (
|
return (
|
||||||
<Gift
|
<Gift
|
||||||
|
|||||||
@ -6,7 +6,9 @@ import type { ApiChat, ApiMessage, ApiPeer } from '../../../api/types';
|
|||||||
import {
|
import {
|
||||||
GENERAL_TOPIC_ID,
|
GENERAL_TOPIC_ID,
|
||||||
SERVICE_NOTIFICATIONS_USER_ID,
|
SERVICE_NOTIFICATIONS_USER_ID,
|
||||||
|
STARS_CURRENCY_CODE,
|
||||||
TME_LINK_PREFIX,
|
TME_LINK_PREFIX,
|
||||||
|
TON_CURRENCY_CODE,
|
||||||
} from '../../../config';
|
} from '../../../config';
|
||||||
import {
|
import {
|
||||||
getMainUsername,
|
getMainUsername,
|
||||||
@ -27,7 +29,8 @@ import {
|
|||||||
import { ensureProtocol } from '../../../util/browser/url';
|
import { ensureProtocol } from '../../../util/browser/url';
|
||||||
import { formatDateTimeToString, formatScheduledDateTime, formatShortDuration } from '../../../util/dates/dateFormat';
|
import { formatDateTimeToString, formatScheduledDateTime, formatShortDuration } from '../../../util/dates/dateFormat';
|
||||||
import { formatCurrency } from '../../../util/formatCurrency';
|
import { formatCurrency } from '../../../util/formatCurrency';
|
||||||
import { formatStarsAsText } from '../../../util/localization/format';
|
import { convertTonFromNanos } from '../../../util/formatCurrency';
|
||||||
|
import { formatStarsAsText, formatTonAsText } from '../../../util/localization/format';
|
||||||
import { conjuctionWithNodes } from '../../../util/localization/utils';
|
import { conjuctionWithNodes } from '../../../util/localization/utils';
|
||||||
import { getServerTime } from '../../../util/serverTime';
|
import { getServerTime } from '../../../util/serverTime';
|
||||||
import renderText from '../../common/helpers/renderText';
|
import renderText from '../../common/helpers/renderText';
|
||||||
@ -441,15 +444,17 @@ const ActionMessageText = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'giftStars':
|
case 'giftStars':
|
||||||
case 'giftPremium': {
|
case 'giftPremium':
|
||||||
|
case 'giftTon': {
|
||||||
const {
|
const {
|
||||||
amount, currency, cryptoAmount, cryptoCurrency,
|
amount, currency, cryptoAmount, cryptoCurrency, type,
|
||||||
} = action;
|
} = action;
|
||||||
|
|
||||||
const price = formatCurrency(lang, amount, currency, { asFontIcon: true });
|
const price = formatCurrency(lang, amount, currency, { asFontIcon: true });
|
||||||
const cryptoPrice = cryptoAmount ? formatCurrency(lang, cryptoAmount, cryptoCurrency!) : undefined;
|
const cryptoPrice = cryptoAmount && type !== 'giftTon'
|
||||||
|
? formatCurrency(lang, cryptoAmount, cryptoCurrency!) : undefined;
|
||||||
|
|
||||||
const cost = cryptoPrice ? lang('ActionCostCrypto', { price, cryptoPrice }, { withNodes: true }) : price;
|
const cost = cryptoPrice ? lang('ActionGiftCostCrypto', { price, cryptoPrice }, { withNodes: true }) : price;
|
||||||
|
|
||||||
if (isServiceNotificationsChat) {
|
if (isServiceNotificationsChat) {
|
||||||
return lang('ActionGiftTextCostAnonymous', { cost }, { withNodes: true });
|
return lang('ActionGiftTextCostAnonymous', { cost }, { withNodes: true });
|
||||||
@ -755,13 +760,21 @@ const ActionMessageText = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'suggestedPostSuccess': {
|
case 'suggestedPostSuccess': {
|
||||||
const { amount: stars } = action;
|
const { amount: price } = action;
|
||||||
|
const currency = price?.currency || STARS_CURRENCY_CODE;
|
||||||
|
const amount = price?.amount || 0;
|
||||||
|
|
||||||
const channel = chat?.isMonoforum ? selectMonoforumChannel(global, chatId) : chat;
|
const channel = chat?.isMonoforum ? selectMonoforumChannel(global, chatId) : chat;
|
||||||
const channelTitle = channel && getPeerTitle(lang, channel);
|
const channelTitle = channel && getPeerTitle(lang, channel);
|
||||||
const channelLink = renderPeerLink(channel?.id, channelTitle || channelFallbackText, asPreview);
|
const channelLink = renderPeerLink(channel?.id, channelTitle || channelFallbackText, asPreview);
|
||||||
|
|
||||||
|
const formattedAmount = currency === TON_CURRENCY_CODE
|
||||||
|
? formatTonAsText(lang, convertTonFromNanos(amount))
|
||||||
|
: formatStarsAsText(lang, amount);
|
||||||
|
|
||||||
return lang('ActionSuggestedPostSuccess', {
|
return lang('ActionSuggestedPostSuccess', {
|
||||||
channel: channelLink,
|
channel: channelLink,
|
||||||
amount: formatStarsAsText(lang, stars?.amount || 0),
|
amount: formattedAmount,
|
||||||
}, { withNodes: true });
|
}, { withNodes: true });
|
||||||
}
|
}
|
||||||
case 'suggestedPostRefund': {
|
case 'suggestedPostRefund': {
|
||||||
@ -775,22 +788,28 @@ const ActionMessageText = ({
|
|||||||
const postSenderTitle = postSender && getPeerTitle(lang, postSender);
|
const postSenderTitle = postSender && getPeerTitle(lang, postSender);
|
||||||
const postSenderLink = renderPeerLink(postSender?.id, postSenderTitle || userFallbackText, asPreview);
|
const postSenderLink = renderPeerLink(postSender?.id, postSenderTitle || userFallbackText, asPreview);
|
||||||
|
|
||||||
const starsAmount = replyMessage?.suggestedPostInfo?.price?.amount || 0;
|
const price = replyMessage?.suggestedPostInfo?.price;
|
||||||
|
const currency = price?.currency || STARS_CURRENCY_CODE;
|
||||||
|
const amount = price?.amount || 0;
|
||||||
|
|
||||||
const channel = chat?.isMonoforum ? selectMonoforumChannel(global, chatId) : chat;
|
const channel = chat?.isMonoforum ? selectMonoforumChannel(global, chatId) : chat;
|
||||||
const channelTitle = channel && getPeerTitle(lang, channel);
|
const channelTitle = channel && getPeerTitle(lang, channel);
|
||||||
const channelLink = renderPeerLink(channel?.id, channelTitle || channelFallbackText, asPreview);
|
const channelLink = renderPeerLink(channel?.id, channelTitle || channelFallbackText, asPreview);
|
||||||
|
|
||||||
|
const formattedAmount = currency === TON_CURRENCY_CODE
|
||||||
|
? formatTonAsText(lang, convertTonFromNanos(amount))
|
||||||
|
: formatStarsAsText(lang, amount);
|
||||||
|
|
||||||
if (payerInitiated) {
|
if (payerInitiated) {
|
||||||
return lang('SuggestedPostRefundedByUser', {
|
return lang('SuggestedPostRefundedByUser', {
|
||||||
amount: formatStarsAsText(lang, starsAmount),
|
amount: formattedAmount,
|
||||||
user: postSenderLink,
|
user: postSenderLink,
|
||||||
channel: channelLink,
|
channel: channelLink,
|
||||||
}, { withNodes: true, withMarkdown: true });
|
}, { withNodes: true, withMarkdown: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
return lang('SuggestedPostRefundedByChannel', {
|
return lang('SuggestedPostRefundedByChannel', {
|
||||||
amount: formatStarsAsText(lang, starsAmount),
|
amount: formattedAmount,
|
||||||
peer: postSenderLink,
|
peer: postSenderLink,
|
||||||
channel: channelLink,
|
channel: channelLink,
|
||||||
}, { withNodes: true, withMarkdown: true });
|
}, { withNodes: true, withMarkdown: true });
|
||||||
@ -819,9 +838,13 @@ const ActionMessageText = ({
|
|||||||
const replyMessageSender = replyMessage ? selectSender(global, replyMessage) : sender;
|
const replyMessageSender = replyMessage ? selectSender(global, replyMessage) : sender;
|
||||||
const replyPeerTitle = replyMessageSender && getPeerTitle(lang, replyMessageSender);
|
const replyPeerTitle = replyMessageSender && getPeerTitle(lang, replyMessageSender);
|
||||||
const userLink = renderPeerLink(replyMessageSender?.id, replyPeerTitle || userFallbackText, asPreview);
|
const userLink = renderPeerLink(replyMessageSender?.id, replyPeerTitle || userFallbackText, asPreview);
|
||||||
|
|
||||||
|
const currency = replyMessage?.suggestedPostInfo?.price?.currency || STARS_CURRENCY_CODE;
|
||||||
|
const currencyName = currency === TON_CURRENCY_CODE ? lang('CurrencyTon') : lang('CurrencyStars');
|
||||||
|
|
||||||
return lang('SuggestedPostBalanceTooLow', {
|
return lang('SuggestedPostBalanceTooLow', {
|
||||||
peer: userLink,
|
peer: userLink,
|
||||||
currency: lang('CurrencyStars'),
|
currency: currencyName,
|
||||||
}, { withNodes: true, withMarkdown: true });
|
}, { withNodes: true, withMarkdown: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,13 +2,18 @@ import { memo, useRef } from '../../../../lib/teact/teact';
|
|||||||
import { withGlobal } from '../../../../global';
|
import { withGlobal } from '../../../../global';
|
||||||
|
|
||||||
import type { ApiSticker } from '../../../../api/types';
|
import type { ApiSticker } from '../../../../api/types';
|
||||||
import type { ApiMessageActionGiftPremium, ApiMessageActionGiftStars } from '../../../../api/types/messageActions';
|
import type {
|
||||||
|
ApiMessageActionGiftPremium,
|
||||||
|
ApiMessageActionGiftStars,
|
||||||
|
ApiMessageActionGiftTon } from '../../../../api/types/messageActions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
selectCanPlayAnimatedEmojis,
|
selectCanPlayAnimatedEmojis,
|
||||||
selectGiftStickerForDuration,
|
selectGiftStickerForDuration,
|
||||||
selectGiftStickerForStars,
|
selectGiftStickerForStars,
|
||||||
|
selectGiftStickerForTon,
|
||||||
} from '../../../../global/selectors';
|
} from '../../../../global/selectors';
|
||||||
|
import { formatCurrency } from '../../../../util/formatCurrency';
|
||||||
import { renderTextWithEntities } from '../../../common/helpers/renderTextWithEntities';
|
import { renderTextWithEntities } from '../../../common/helpers/renderTextWithEntities';
|
||||||
|
|
||||||
import { type ObserveFn } from '../../../../hooks/useIntersectionObserver';
|
import { type ObserveFn } from '../../../../hooks/useIntersectionObserver';
|
||||||
@ -20,7 +25,7 @@ import StickerView from '../../../common/StickerView';
|
|||||||
import styles from '../ActionMessage.module.scss';
|
import styles from '../ActionMessage.module.scss';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
action: ApiMessageActionGiftPremium | ApiMessageActionGiftStars;
|
action: ApiMessageActionGiftPremium | ApiMessageActionGiftStars | ApiMessageActionGiftTon;
|
||||||
observeIntersectionForLoading?: ObserveFn;
|
observeIntersectionForLoading?: ObserveFn;
|
||||||
observeIntersectionForPlaying?: ObserveFn;
|
observeIntersectionForPlaying?: ObserveFn;
|
||||||
onClick?: NoneToVoidFunction;
|
onClick?: NoneToVoidFunction;
|
||||||
@ -45,6 +50,15 @@ const GiftAction = ({
|
|||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
const message = action.type === 'giftPremium' ? action.message : undefined;
|
const message = action.type === 'giftPremium' ? action.message : undefined;
|
||||||
|
|
||||||
|
const renderTonTitle = () => {
|
||||||
|
const { cryptoAmount, cryptoCurrency } = action;
|
||||||
|
const price = cryptoAmount
|
||||||
|
? formatCurrency(lang, cryptoAmount, cryptoCurrency!, { asFontIcon: true })
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return price;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.contentBox} tabIndex={0} role="button" onClick={onClick}>
|
<div className={styles.contentBox} tabIndex={0} role="button" onClick={onClick}>
|
||||||
<div
|
<div
|
||||||
@ -67,13 +81,16 @@ const GiftAction = ({
|
|||||||
<h3 className={styles.title}>
|
<h3 className={styles.title}>
|
||||||
{action.type === 'giftPremium' ? (
|
{action.type === 'giftPremium' ? (
|
||||||
lang('ActionGiftPremiumTitle', { months: action.months }, { pluralValue: action.months })
|
lang('ActionGiftPremiumTitle', { months: action.months }, { pluralValue: action.months })
|
||||||
) : (
|
) : action.type === 'giftStars' ? (
|
||||||
lang('ActionGiftStarsTitle', { amount: action.stars }, { pluralValue: action.stars })
|
lang('ActionGiftStarsTitle', { amount: action.stars }, { pluralValue: action.stars })
|
||||||
)}
|
) : renderTonTitle()}
|
||||||
</h3>
|
</h3>
|
||||||
<div>
|
<div>
|
||||||
{message && renderTextWithEntities(message)}
|
{message && renderTextWithEntities(message)}
|
||||||
{!message && (lang(action.type === 'giftPremium' ? 'ActionGiftPremiumText' : 'ActionGiftStarsText'))}
|
{!message
|
||||||
|
&& (lang(action.type === 'giftTon' ? 'DescriptionAboutTon'
|
||||||
|
: action.type === 'giftPremium'
|
||||||
|
? 'ActionGiftPremiumText' : 'ActionGiftStarsText'))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.actionButton}>
|
<div className={styles.actionButton}>
|
||||||
@ -88,7 +105,9 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
(global, { action }): StateProps => {
|
(global, { action }): StateProps => {
|
||||||
const sticker = action.type === 'giftPremium'
|
const sticker = action.type === 'giftPremium'
|
||||||
? selectGiftStickerForDuration(global, action.months)
|
? selectGiftStickerForDuration(global, action.months)
|
||||||
: selectGiftStickerForStars(global, action.stars);
|
: action.type === 'giftStars'
|
||||||
|
? selectGiftStickerForStars(global, action.stars)
|
||||||
|
: selectGiftStickerForTon(global, action.cryptoAmount);
|
||||||
const canPlayAnimatedEmojis = selectCanPlayAnimatedEmojis(global);
|
const canPlayAnimatedEmojis = selectCanPlayAnimatedEmojis(global);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { withGlobal } from '../../../../global';
|
|||||||
import type { ApiMessage, ApiPeer } from '../../../../api/types';
|
import type { ApiMessage, ApiPeer } from '../../../../api/types';
|
||||||
import type { ApiMessageActionSuggestedPostApproval } from '../../../../api/types/messageActions';
|
import type { ApiMessageActionSuggestedPostApproval } from '../../../../api/types/messageActions';
|
||||||
|
|
||||||
import { STARS_SUGGESTED_POST_AGE_MIN } from '../../../../config';
|
import { STARS_SUGGESTED_POST_AGE_MIN, TON_CURRENCY_CODE } from '../../../../config';
|
||||||
import { getPeerFullTitle } from '../../../../global/helpers/peers';
|
import { getPeerFullTitle } from '../../../../global/helpers/peers';
|
||||||
import { getMessageReplyInfo } from '../../../../global/helpers/replies';
|
import { getMessageReplyInfo } from '../../../../global/helpers/replies';
|
||||||
import { selectIsMonoforumAdmin, selectMonoforumChannel,
|
import { selectIsMonoforumAdmin, selectMonoforumChannel,
|
||||||
@ -12,7 +12,8 @@ import { selectIsMonoforumAdmin, selectMonoforumChannel,
|
|||||||
selectSender } from '../../../../global/selectors';
|
selectSender } from '../../../../global/selectors';
|
||||||
import buildClassName from '../../../../util/buildClassName';
|
import buildClassName from '../../../../util/buildClassName';
|
||||||
import { formatScheduledDateTime, formatShortDuration } from '../../../../util/dates/dateFormat';
|
import { formatScheduledDateTime, formatShortDuration } from '../../../../util/dates/dateFormat';
|
||||||
import { formatStarsAsText } from '../../../../util/localization/format';
|
import { convertTonFromNanos } from '../../../../util/formatCurrency';
|
||||||
|
import { formatStarsAsText, formatTonAsText } from '../../../../util/localization/format';
|
||||||
import { getServerTime } from '../../../../util/serverTime';
|
import { getServerTime } from '../../../../util/serverTime';
|
||||||
import renderText from '../../../common/helpers/renderText';
|
import renderText from '../../../common/helpers/renderText';
|
||||||
import { renderPeerLink, translateWithYou } from '../helpers/messageActions';
|
import { renderPeerLink, translateWithYou } from '../helpers/messageActions';
|
||||||
@ -58,11 +59,18 @@ const SuggestedPostApproval = ({
|
|||||||
|
|
||||||
const publishDate = scheduleDate
|
const publishDate = scheduleDate
|
||||||
? formatScheduledDateTime(scheduleDate, lang, oldLang)
|
? formatScheduledDateTime(scheduleDate, lang, oldLang)
|
||||||
: lang('TitleAnytime');
|
: lang('SuggestMessageAnytime');
|
||||||
|
|
||||||
const isPostPublished = scheduleDate ? scheduleDate <= getServerTime() : false;
|
const isPostPublished = scheduleDate ? scheduleDate <= getServerTime() : false;
|
||||||
|
|
||||||
const starsText = amount?.amount ? formatStarsAsText(lang, amount.amount) : undefined;
|
const currency = amount?.currency;
|
||||||
|
const amountValue = amount?.amount || 0;
|
||||||
|
|
||||||
|
const formattedAmount = amountValue > 0
|
||||||
|
? (currency === TON_CURRENCY_CODE
|
||||||
|
? formatTonAsText(lang, convertTonFromNanos(amountValue))
|
||||||
|
: formatStarsAsText(lang, amountValue))
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const duration = formatShortDuration(lang, ageMinSeconds, true);
|
const duration = formatShortDuration(lang, ageMinSeconds, true);
|
||||||
|
|
||||||
@ -85,31 +93,35 @@ const SuggestedPostApproval = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{starsText && (
|
{formattedAmount && (
|
||||||
<div className={styles.suggestedPostApprovalSection}>
|
<div className={styles.suggestedPostApprovalSection}>
|
||||||
{translateWithYou(lang,
|
{translateWithYou(lang,
|
||||||
'SuggestedPostCharged',
|
'SuggestedPostCharged',
|
||||||
!isAdmin,
|
!isAdmin,
|
||||||
{
|
{
|
||||||
user: originalSenderLink,
|
user: originalSenderLink,
|
||||||
amount: starsText,
|
amount: formattedAmount,
|
||||||
},
|
},
|
||||||
{ withMarkdown: true },
|
{ withMarkdown: true },
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isPostPublished && starsText && (
|
{isPostPublished && formattedAmount && (
|
||||||
<>
|
<>
|
||||||
<div className={styles.suggestedPostApprovalSection}>
|
<div className={styles.suggestedPostApprovalSection}>
|
||||||
{translateWithYou(lang, 'SuggestedPostReceiveAmount', !isAdmin, {
|
{translateWithYou(lang, 'SuggestedPostReceiveAmount', !isAdmin, {
|
||||||
peer: renderChatLink(), duration, currency: lang('CurrencyStars'),
|
peer: renderChatLink(),
|
||||||
|
duration,
|
||||||
|
currency: currency === TON_CURRENCY_CODE ? lang('CurrencyTon') : lang('CurrencyStars'),
|
||||||
}, { withMarkdown: true })}
|
}, { withMarkdown: true })}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.suggestedPostApprovalSection}>
|
<div className={styles.suggestedPostApprovalSection}>
|
||||||
{translateWithYou(lang, 'SuggestedPostRefund', !isAdmin, {
|
{translateWithYou(lang, 'SuggestedPostRefund', !isAdmin, {
|
||||||
peer: renderChatLink(), duration, currency: lang('CurrencyStars'),
|
peer: renderChatLink(),
|
||||||
|
duration,
|
||||||
|
currency: currency === TON_CURRENCY_CODE ? lang('CurrencyTon') : lang('CurrencyStars'),
|
||||||
}, { withMarkdown: true })}
|
}, { withMarkdown: true })}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { getActions, withGlobal } from '../../../../global';
|
|||||||
import type { ApiMessage, ApiPeer } from '../../../../api/types';
|
import type { ApiMessage, ApiPeer } from '../../../../api/types';
|
||||||
import type { ApiMessageActionSuggestedPostApproval } from '../../../../api/types/messageActions';
|
import type { ApiMessageActionSuggestedPostApproval } from '../../../../api/types/messageActions';
|
||||||
|
|
||||||
|
import { STARS_CURRENCY_CODE, TON_CURRENCY_CODE } from '../../../../config';
|
||||||
import { getPeerFullTitle } from '../../../../global/helpers/peers';
|
import { getPeerFullTitle } from '../../../../global/helpers/peers';
|
||||||
import { selectChatMessage, selectSender } from '../../../../global/selectors';
|
import { selectChatMessage, selectSender } from '../../../../global/selectors';
|
||||||
import buildClassName from '../../../../util/buildClassName';
|
import buildClassName from '../../../../util/buildClassName';
|
||||||
@ -25,6 +26,7 @@ type OwnProps = {
|
|||||||
type StateProps = {
|
type StateProps = {
|
||||||
sender?: ApiPeer;
|
sender?: ApiPeer;
|
||||||
replyMessageSender?: ApiPeer;
|
replyMessageSender?: ApiPeer;
|
||||||
|
replyMessage?: ApiMessage;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SuggestedPostBalanceTooLow = ({
|
const SuggestedPostBalanceTooLow = ({
|
||||||
@ -32,6 +34,7 @@ const SuggestedPostBalanceTooLow = ({
|
|||||||
message,
|
message,
|
||||||
sender,
|
sender,
|
||||||
replyMessageSender,
|
replyMessageSender,
|
||||||
|
replyMessage,
|
||||||
}: OwnProps & StateProps) => {
|
}: OwnProps & StateProps) => {
|
||||||
const { openStarsBalanceModal } = getActions();
|
const { openStarsBalanceModal } = getActions();
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
@ -46,6 +49,10 @@ const SuggestedPostBalanceTooLow = ({
|
|||||||
const peerTitle = targetPeer && getPeerFullTitle(lang, targetPeer);
|
const peerTitle = targetPeer && getPeerFullTitle(lang, targetPeer);
|
||||||
const peerLink = renderPeerLink(targetPeer?.id, peerTitle || lang('ActionFallbackUser'));
|
const peerLink = renderPeerLink(targetPeer?.id, peerTitle || lang('ActionFallbackUser'));
|
||||||
|
|
||||||
|
const currency = replyMessage?.suggestedPostInfo?.price?.currency || STARS_CURRENCY_CODE;
|
||||||
|
const currencyName = currency === TON_CURRENCY_CODE ? lang('CurrencyTon') : lang('CurrencyStars');
|
||||||
|
const buyButtonText = currency === TON_CURRENCY_CODE ? lang('ButtonTopUpViaFragment') : lang('ButtonBuyStars');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={buildClassName(styles.contentBox, styles.suggestedPostBalanceTooLowBox)}
|
className={buildClassName(styles.contentBox, styles.suggestedPostBalanceTooLowBox)}
|
||||||
@ -54,14 +61,14 @@ const SuggestedPostBalanceTooLow = ({
|
|||||||
<div className={styles.suggestedPostBalanceTooLowTitle}>
|
<div className={styles.suggestedPostBalanceTooLowTitle}>
|
||||||
{lang('SuggestedPostBalanceTooLow', {
|
{lang('SuggestedPostBalanceTooLow', {
|
||||||
peer: peerLink,
|
peer: peerLink,
|
||||||
currency: lang('CurrencyStars'),
|
currency: currencyName,
|
||||||
}, { withNodes: true, withMarkdown: true })}
|
}, { withNodes: true, withMarkdown: true })}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!message.isOutgoing && (
|
{!message.isOutgoing && (
|
||||||
<div className={styles.actionButton} onClick={handleGetMoreStars}>
|
<div className={styles.actionButton} onClick={handleGetMoreStars}>
|
||||||
<Sparkles preset="button" />
|
<Sparkles preset="button" />
|
||||||
{lang('ButtonBuyStars')}
|
{buyButtonText}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -81,6 +88,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
return {
|
return {
|
||||||
sender,
|
sender,
|
||||||
replyMessageSender,
|
replyMessageSender,
|
||||||
|
replyMessage,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(SuggestedPostBalanceTooLow));
|
)(SuggestedPostBalanceTooLow));
|
||||||
|
|||||||
@ -124,7 +124,7 @@ const GiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
const allGifts = renderingModal?.gifts;
|
const allGifts = renderingModal?.gifts;
|
||||||
const filteredGifts = useMemo(() => {
|
const filteredGifts = useMemo(() => {
|
||||||
return allGifts?.sort((prevGift, gift) => prevGift.months - gift.months)
|
return allGifts?.sort((prevGift, gift) => prevGift.months - gift.months)
|
||||||
.filter((gift) => gift.users === 1 && gift.currency !== 'XTR');
|
.filter((gift) => gift.users === 1 && gift.currency !== STARS_CURRENCY_CODE);
|
||||||
}, [allGifts]);
|
}, [allGifts]);
|
||||||
|
|
||||||
const giftsByStars = useMemo(() => {
|
const giftsByStars = useMemo(() => {
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { getActions } from '../../../global';
|
|||||||
import type {
|
import type {
|
||||||
ApiPeer,
|
ApiPeer,
|
||||||
ApiStarGiftAttributeBackdrop, ApiStarGiftAttributeModel, ApiStarGiftAttributePattern,
|
ApiStarGiftAttributeBackdrop, ApiStarGiftAttributeModel, ApiStarGiftAttributePattern,
|
||||||
ApiStarsAmount } from '../../../api/types';
|
ApiTypeCurrencyAmount } from '../../../api/types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
formatStarsTransactionAmount,
|
formatStarsTransactionAmount,
|
||||||
@ -31,7 +31,7 @@ type OwnProps = {
|
|||||||
subtitle?: TeactNode;
|
subtitle?: TeactNode;
|
||||||
subtitlePeer?: ApiPeer;
|
subtitlePeer?: ApiPeer;
|
||||||
className?: string;
|
className?: string;
|
||||||
resellPrice?: ApiStarsAmount;
|
resellPrice?: ApiTypeCurrencyAmount;
|
||||||
};
|
};
|
||||||
|
|
||||||
const STICKER_SIZE = 120;
|
const STICKER_SIZE = 120;
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import { memo } from '../../../lib/teact/teact';
|
import { memo } from '../../../lib/teact/teact';
|
||||||
import { getActions } from '../../../global';
|
import { getActions } from '../../../global';
|
||||||
|
|
||||||
import type { ApiStarsAmount } from '../../../api/types';
|
import type { ApiTypeCurrencyAmount } from '../../../api/types';
|
||||||
|
|
||||||
|
import { STARS_CURRENCY_CODE, TON_CURRENCY_CODE } from '../../../config';
|
||||||
import { formatStarsAmount } from '../../../global/helpers/payments';
|
import { formatStarsAmount } from '../../../global/helpers/payments';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
|
import { convertCurrencyFromBaseUnit } from '../../../util/formatCurrency';
|
||||||
|
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
|
||||||
@ -15,7 +17,7 @@ import StarIcon from '../../common/icons/StarIcon';
|
|||||||
import styles from './StarsBalanceModal.module.scss';
|
import styles from './StarsBalanceModal.module.scss';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
balance?: ApiStarsAmount;
|
balance?: ApiTypeCurrencyAmount;
|
||||||
withAddButton?: boolean;
|
withAddButton?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
@ -27,25 +29,42 @@ const BalanceBlock = ({ balance, className, withAddButton }: OwnProps) => {
|
|||||||
openStarsBalanceModal,
|
openStarsBalanceModal,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
|
const renderStarsAmount = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<StarIcon type="gold" size="middle" />
|
||||||
|
{balance !== undefined && balance.currency === STARS_CURRENCY_CODE
|
||||||
|
? formatStarsAmount(lang, balance) : '…'}
|
||||||
|
{withAddButton && (
|
||||||
|
<BadgeButton
|
||||||
|
className={styles.addStarsButton}
|
||||||
|
onClick={() => openStarsBalanceModal({})}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
className={styles.addStarsIcon}
|
||||||
|
name="add"
|
||||||
|
/>
|
||||||
|
</BadgeButton>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderTonAmount = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Icon name="toncoin" />
|
||||||
|
{balance !== undefined ? convertCurrencyFromBaseUnit(balance.amount, balance.currency) : '…'}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={buildClassName(styles.balanceBlock, className)}>
|
<div className={buildClassName(styles.balanceBlock, className)}>
|
||||||
<div className={styles.balanceInfo}>
|
<div className={styles.balanceInfo}>
|
||||||
<span className={styles.smallerText}>{lang('StarsBalance')}</span>
|
<span className={styles.smallerText}>{lang('StarsBalance')}</span>
|
||||||
<div className={styles.balanceBottom}>
|
<div className={styles.balanceBottom}>
|
||||||
<StarIcon type="gold" size="middle" />
|
{balance?.currency === TON_CURRENCY_CODE ? renderTonAmount() : renderStarsAmount()}
|
||||||
{balance !== undefined ? formatStarsAmount(lang, balance) : '…'}
|
|
||||||
{withAddButton && (
|
|
||||||
<BadgeButton
|
|
||||||
className={styles.addStarsButton}
|
|
||||||
|
|
||||||
onClick={() => openStarsBalanceModal({})}
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
className={styles.addStarsIcon}
|
|
||||||
name="add"
|
|
||||||
/>
|
|
||||||
</BadgeButton>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -56,6 +56,7 @@
|
|||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hint,
|
||||||
.tos {
|
.tos {
|
||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
@ -73,6 +74,37 @@
|
|||||||
margin: 1rem;
|
margin: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.topUpButton,
|
||||||
|
.tonBalanceContainer {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tonBalance {
|
||||||
|
unicode-bidi: plaintext;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
font-size: 1.75rem;
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tonIconBalance {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tonInUsd {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tonIconLogo {
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
font-size: 5rem;
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
.logoBackground {
|
.logoBackground {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0.75rem;
|
top: 0.75rem;
|
||||||
@ -196,6 +228,7 @@
|
|||||||
right: 1.25rem;
|
right: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.topUpButton,
|
||||||
.starButton {
|
.starButton {
|
||||||
grid-column: 1/-1;
|
grid-column: 1/-1;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
|
|||||||
@ -8,11 +8,19 @@ import type { ApiStarTopupOption } from '../../../api/types';
|
|||||||
import type { GlobalState, TabState } from '../../../global/types';
|
import type { GlobalState, TabState } from '../../../global/types';
|
||||||
import type { RegularLangKey } from '../../../types/language';
|
import type { RegularLangKey } from '../../../types/language';
|
||||||
|
|
||||||
import { PAID_MESSAGES_PURPOSE } from '../../../config';
|
import {
|
||||||
|
PAID_MESSAGES_PURPOSE,
|
||||||
|
STARS_CURRENCY_CODE,
|
||||||
|
TON_CURRENCY_CODE,
|
||||||
|
TON_TOPUP_URL_DEFAULT,
|
||||||
|
TON_USD_RATE_DEFAULT,
|
||||||
|
} from '../../../config';
|
||||||
import { getChatTitle, getUserFullName } from '../../../global/helpers';
|
import { getChatTitle, getUserFullName } from '../../../global/helpers';
|
||||||
import { getPeerTitle } from '../../../global/helpers/peers';
|
import { getPeerTitle } from '../../../global/helpers/peers';
|
||||||
import { selectChat, selectIsPremiumPurchaseBlocked, selectUser } from '../../../global/selectors';
|
import { selectChat, selectIsPremiumPurchaseBlocked, selectUser } from '../../../global/selectors';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
|
import { convertCurrencyFromBaseUnit, convertTonToUsd, formatCurrencyAsString } from '../../../util/formatCurrency';
|
||||||
|
import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets';
|
||||||
import renderText from '../../common/helpers/renderText';
|
import renderText from '../../common/helpers/renderText';
|
||||||
|
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
@ -20,6 +28,7 @@ import useLang from '../../../hooks/useLang';
|
|||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
|
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
||||||
import Icon from '../../common/icons/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
import SafeLink from '../../common/SafeLink';
|
import SafeLink from '../../common/SafeLink';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
@ -52,18 +61,25 @@ export type OwnProps = {
|
|||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
starsBalanceState?: GlobalState['stars'];
|
starsBalanceState?: GlobalState['stars'];
|
||||||
|
tonBalanceState?: GlobalState['ton'];
|
||||||
canBuyPremium?: boolean;
|
canBuyPremium?: boolean;
|
||||||
shouldForceHeight?: boolean;
|
shouldForceHeight?: boolean;
|
||||||
|
tonUsdRate?: number;
|
||||||
|
tonTopupUrl: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StarsBalanceModal = ({
|
const StarsBalanceModal = ({
|
||||||
modal, starsBalanceState, canBuyPremium, shouldForceHeight,
|
modal, starsBalanceState, tonBalanceState, canBuyPremium, shouldForceHeight, tonUsdRate, tonTopupUrl,
|
||||||
}: OwnProps & StateProps) => {
|
}: OwnProps & StateProps) => {
|
||||||
const {
|
const {
|
||||||
closeStarsBalanceModal, loadStarsTransactions, loadStarsSubscriptions, openStarsGiftingPickerModal, openInvoice,
|
closeStarsBalanceModal, loadStarsTransactions, loadStarsSubscriptions, openStarsGiftingPickerModal, openInvoice,
|
||||||
|
openUrl,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const { balance, history, subscriptions } = starsBalanceState || {};
|
const currency = modal?.currency || STARS_CURRENCY_CODE;
|
||||||
|
const currentState = currency === TON_CURRENCY_CODE ? tonBalanceState : starsBalanceState;
|
||||||
|
const { balance, history } = currentState || {};
|
||||||
|
const { subscriptions } = starsBalanceState || {};
|
||||||
|
|
||||||
const oldLang = useOldLang();
|
const oldLang = useOldLang();
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
@ -72,7 +88,7 @@ const StarsBalanceModal = ({
|
|||||||
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
|
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
|
||||||
const [areBuyOptionsShown, showBuyOptions, hideBuyOptions] = useFlag();
|
const [areBuyOptionsShown, showBuyOptions, hideBuyOptions] = useFlag();
|
||||||
|
|
||||||
const isOpen = Boolean(modal && starsBalanceState);
|
const isOpen = Boolean(modal && (starsBalanceState || tonBalanceState));
|
||||||
|
|
||||||
const {
|
const {
|
||||||
originStarsPayment, originReaction, originGift, topup,
|
originStarsPayment, originReaction, originGift, topup,
|
||||||
@ -159,6 +175,90 @@ const StarsBalanceModal = ({
|
|||||||
];
|
];
|
||||||
}, [isOpen, oldLang]);
|
}, [isOpen, oldLang]);
|
||||||
|
|
||||||
|
const renderStarsSection = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<img className={styles.logo} src={StarLogo} alt="" draggable={false} />
|
||||||
|
<img className={styles.logoBackground} src={StarsBackground} alt="" draggable={false} />
|
||||||
|
<h2 className={styles.headerText}>
|
||||||
|
{starsNeeded ? oldLang('StarsNeededTitle', ongoingTransactionAmount) : oldLang('TelegramStars')}
|
||||||
|
</h2>
|
||||||
|
<div className={styles.description}>
|
||||||
|
{renderText(
|
||||||
|
starsNeededText || oldLang('TelegramStarsInfo'),
|
||||||
|
['simple_markdown', 'emoji'],
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{canBuyPremium && !areBuyOptionsShown && (
|
||||||
|
<Button
|
||||||
|
className={styles.starButton}
|
||||||
|
onClick={showBuyOptions}
|
||||||
|
>
|
||||||
|
{oldLang('Star.List.BuyMoreStars')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{canBuyPremium && !areBuyOptionsShown && shouldSuggestGifting && (
|
||||||
|
<Button
|
||||||
|
isText
|
||||||
|
noForcedUpperCase
|
||||||
|
className={styles.starButton}
|
||||||
|
onClick={openStarsGiftingPickerModalHandler}
|
||||||
|
>
|
||||||
|
{oldLang('TelegramStarsGift')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{areBuyOptionsShown && starsBalanceState?.topupOptions && (
|
||||||
|
<StarTopupOptionList
|
||||||
|
starsNeeded={starsNeeded}
|
||||||
|
options={starsBalanceState.topupOptions}
|
||||||
|
onClick={handleBuyStars}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderTonSection = () => {
|
||||||
|
const tonAmount = convertCurrencyFromBaseUnit(balance?.amount || 0, TON_CURRENCY_CODE);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AnimatedIconWithPreview
|
||||||
|
size={160}
|
||||||
|
tgsUrl={LOCAL_TGS_URLS.Diamond}
|
||||||
|
nonInteractive
|
||||||
|
noLoop={false}
|
||||||
|
/>
|
||||||
|
<h2 className={styles.headerText}>
|
||||||
|
{lang('CurrencyTon')}
|
||||||
|
</h2>
|
||||||
|
<div className={styles.description}>
|
||||||
|
{lang('DescriptionAboutTon')}
|
||||||
|
</div>
|
||||||
|
<div className={styles.tonBalanceContainer}>
|
||||||
|
<div className={styles.tonBalance}>
|
||||||
|
<Icon name="toncoin" className={styles.tonIconBalance} />
|
||||||
|
{tonAmount}
|
||||||
|
</div>
|
||||||
|
{Boolean(tonUsdRate) && (
|
||||||
|
<span className={styles.tonInUsd}>
|
||||||
|
{`≈ ${formatCurrencyAsString(
|
||||||
|
convertTonToUsd(balance?.amount || 0, tonUsdRate),
|
||||||
|
'USD',
|
||||||
|
lang.code,
|
||||||
|
)}`}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
className={styles.topUpButton}
|
||||||
|
onClick={handleTonTopUp}
|
||||||
|
>
|
||||||
|
{lang('ButtonTopUpViaFragment')}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
function handleScroll(e: React.UIEvent<HTMLDivElement>) {
|
function handleScroll(e: React.UIEvent<HTMLDivElement>) {
|
||||||
const { scrollTop } = e.currentTarget;
|
const { scrollTop } = e.currentTarget;
|
||||||
|
|
||||||
@ -168,6 +268,7 @@ const StarsBalanceModal = ({
|
|||||||
const handleLoadMoreTransactions = useLastCallback(() => {
|
const handleLoadMoreTransactions = useLastCallback(() => {
|
||||||
loadStarsTransactions({
|
loadStarsTransactions({
|
||||||
type: TRANSACTION_TYPES[selectedTabIndex],
|
type: TRANSACTION_TYPES[selectedTabIndex],
|
||||||
|
isTon: currency === TON_CURRENCY_CODE,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -188,6 +289,10 @@ const StarsBalanceModal = ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleTonTopUp = useLastCallback(() => {
|
||||||
|
openUrl({ url: tonTopupUrl });
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
className={buildClassName(styles.root, !shouldForceHeight && !areBuyOptionsShown && styles.minimal)}
|
className={buildClassName(styles.root, !shouldForceHeight && !areBuyOptionsShown && styles.minimal)}
|
||||||
@ -206,55 +311,25 @@ const StarsBalanceModal = ({
|
|||||||
>
|
>
|
||||||
<Icon name="close" />
|
<Icon name="close" />
|
||||||
</Button>
|
</Button>
|
||||||
<BalanceBlock balance={balance} className={styles.modalBalance} />
|
{currency !== TON_CURRENCY_CODE && <BalanceBlock balance={balance} className={styles.modalBalance} />}
|
||||||
<div className={buildClassName(styles.header, isHeaderHidden && styles.hiddenHeader)}>
|
<div className={buildClassName(styles.header, isHeaderHidden && styles.hiddenHeader)}>
|
||||||
<h2 className={styles.starHeaderText}>
|
<h2 className={styles.starHeaderText}>
|
||||||
{oldLang('TelegramStars')}
|
{oldLang('TelegramStars')}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.section}>
|
<div className={styles.section}>
|
||||||
<img className={styles.logo} src={StarLogo} alt="" draggable={false} />
|
{currency === TON_CURRENCY_CODE ? renderTonSection() : renderStarsSection()}
|
||||||
<img className={styles.logoBackground} src={StarsBackground} alt="" draggable={false} />
|
|
||||||
<h2 className={styles.headerText}>
|
|
||||||
{starsNeeded ? oldLang('StarsNeededTitle', ongoingTransactionAmount) : oldLang('TelegramStars')}
|
|
||||||
</h2>
|
|
||||||
<div className={styles.description}>
|
|
||||||
{renderText(
|
|
||||||
starsNeededText || oldLang('TelegramStarsInfo'),
|
|
||||||
['simple_markdown', 'emoji'],
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{canBuyPremium && !areBuyOptionsShown && (
|
|
||||||
<Button
|
|
||||||
className={styles.starButton}
|
|
||||||
onClick={showBuyOptions}
|
|
||||||
>
|
|
||||||
{oldLang('Star.List.BuyMoreStars')}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{canBuyPremium && !areBuyOptionsShown && shouldSuggestGifting && (
|
|
||||||
<Button
|
|
||||||
isText
|
|
||||||
noForcedUpperCase
|
|
||||||
className={styles.starButton}
|
|
||||||
onClick={openStarsGiftingPickerModalHandler}
|
|
||||||
>
|
|
||||||
{oldLang('TelegramStarsGift')}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{areBuyOptionsShown && starsBalanceState?.topupOptions && (
|
|
||||||
<StarTopupOptionList
|
|
||||||
starsNeeded={starsNeeded}
|
|
||||||
options={starsBalanceState.topupOptions}
|
|
||||||
onClick={handleBuyStars}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
{areBuyOptionsShown && (
|
{areBuyOptionsShown && (
|
||||||
<div className={styles.tos}>
|
<div className={styles.tos}>
|
||||||
{tosText}
|
{tosText}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{currency === TON_CURRENCY_CODE && (
|
||||||
|
<div className={styles.hint}>
|
||||||
|
{lang('TonModalHint')}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{shouldShowItems && Boolean(subscriptions?.list.length) && (
|
{shouldShowItems && Boolean(subscriptions?.list.length) && (
|
||||||
<div className={styles.section}>
|
<div className={styles.section}>
|
||||||
<h3 className={styles.sectionTitle}>{oldLang('StarMySubscriptions')}</h3>
|
<h3 className={styles.sectionTitle}>{oldLang('StarMySubscriptions')}</h3>
|
||||||
@ -325,13 +400,18 @@ const StarsBalanceModal = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default memo(withGlobal<OwnProps>(
|
export default memo(withGlobal<OwnProps>(
|
||||||
(global): StateProps => {
|
(global, { modal }): StateProps => {
|
||||||
const shouldForceHeight = Boolean(global.stars?.history?.all?.transactions.length);
|
const shouldForceHeight = modal?.currency === TON_CURRENCY_CODE
|
||||||
|
? Boolean(global.ton?.history?.all?.transactions.length)
|
||||||
|
: Boolean(global.stars?.history?.all?.transactions.length);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
shouldForceHeight,
|
shouldForceHeight,
|
||||||
starsBalanceState: global.stars,
|
starsBalanceState: global.stars,
|
||||||
|
tonBalanceState: global.ton,
|
||||||
canBuyPremium: !selectIsPremiumPurchaseBlocked(global),
|
canBuyPremium: !selectIsPremiumPurchaseBlocked(global),
|
||||||
|
tonUsdRate: global.appConfig?.tonUsdRate || TON_USD_RATE_DEFAULT,
|
||||||
|
tonTopupUrl: global.appConfig?.tonTopupUrl || TON_TOPUP_URL_DEFAULT,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(StarsBalanceModal));
|
)(StarsBalanceModal));
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import type { ApiStarsAmount, ApiStarsTransaction } from '../../../../api/types';
|
import type { ApiStarsAmount, ApiStarsTransaction, ApiTypeCurrencyAmount } from '../../../../api/types';
|
||||||
import type { OldLangFn } from '../../../../hooks/useOldLang';
|
import type { OldLangFn } from '../../../../hooks/useOldLang';
|
||||||
|
|
||||||
|
import { STARS_CURRENCY_CODE, TON_CURRENCY_CODE } from '../../../../config';
|
||||||
import { buildStarsTransactionCustomPeer } from '../../../../global/helpers/payments';
|
import { buildStarsTransactionCustomPeer } from '../../../../global/helpers/payments';
|
||||||
import {
|
import {
|
||||||
type LangFn,
|
type LangFn,
|
||||||
@ -20,7 +21,7 @@ export function getTransactionTitle(oldLang: OldLangFn, lang: LangFn, transactio
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (transaction.isGiftResale) {
|
if (transaction.isGiftResale) {
|
||||||
return isNegativeStarsAmount(transaction.stars)
|
return isNegativeAmount(transaction.amount)
|
||||||
? lang('StarGiftSaleTransaction')
|
? lang('StarGiftSaleTransaction')
|
||||||
: lang('StarGiftPurchaseTransaction');
|
: lang('StarGiftPurchaseTransaction');
|
||||||
}
|
}
|
||||||
@ -34,9 +35,14 @@ export function getTransactionTitle(oldLang: OldLangFn, lang: LangFn, transactio
|
|||||||
if (transaction.isReaction) return oldLang('StarsReactionsSent');
|
if (transaction.isReaction) return oldLang('StarsReactionsSent');
|
||||||
if (transaction.giveawayPostId) return oldLang('StarsGiveawayPrizeReceived');
|
if (transaction.giveawayPostId) return oldLang('StarsGiveawayPrizeReceived');
|
||||||
if (transaction.isMyGift) return oldLang('StarsGiftSent');
|
if (transaction.isMyGift) return oldLang('StarsGiftSent');
|
||||||
if (transaction.isGift) return oldLang('StarsGiftReceived');
|
if (transaction.isGift) {
|
||||||
|
if (transaction.amount.currency === TON_CURRENCY_CODE) {
|
||||||
|
return lang('TonGiftReceived');
|
||||||
|
}
|
||||||
|
return oldLang('StarsGiftReceived');
|
||||||
|
}
|
||||||
if (transaction.starGift) {
|
if (transaction.starGift) {
|
||||||
return isNegativeStarsAmount(transaction.stars) ? oldLang('Gift2TransactionSent') : oldLang('Gift2ConvertedTitle');
|
return isNegativeAmount(transaction.amount) ? oldLang('Gift2TransactionSent') : oldLang('Gift2ConvertedTitle');
|
||||||
}
|
}
|
||||||
|
|
||||||
const customPeer = (transaction.peer && transaction.peer.type !== 'peer'
|
const customPeer = (transaction.peer && transaction.peer.type !== 'peer'
|
||||||
@ -51,3 +57,10 @@ export function isNegativeStarsAmount(starsAmount: ApiStarsAmount) {
|
|||||||
if (starsAmount.amount) return starsAmount.amount < 0;
|
if (starsAmount.amount) return starsAmount.amount < 0;
|
||||||
return starsAmount.nanos < 0;
|
return starsAmount.nanos < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isNegativeAmount(currencyAmount: ApiTypeCurrencyAmount) {
|
||||||
|
if (currencyAmount.currency === STARS_CURRENCY_CODE) {
|
||||||
|
return isNegativeStarsAmount(currencyAmount);
|
||||||
|
}
|
||||||
|
return currencyAmount.amount < 0;
|
||||||
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import type {
|
|||||||
import type { GlobalState } from '../../../../global/types';
|
import type { GlobalState } from '../../../../global/types';
|
||||||
import type { CustomPeer } from '../../../../types';
|
import type { CustomPeer } from '../../../../types';
|
||||||
|
|
||||||
|
import { STARS_CURRENCY_CODE, TON_CURRENCY_CODE } from '../../../../config';
|
||||||
import { buildStarsTransactionCustomPeer, formatStarsTransactionAmount } from '../../../../global/helpers/payments';
|
import { buildStarsTransactionCustomPeer, formatStarsTransactionAmount } from '../../../../global/helpers/payments';
|
||||||
import { getPeerTitle } from '../../../../global/helpers/peers';
|
import { getPeerTitle } from '../../../../global/helpers/peers';
|
||||||
import { selectPeer } from '../../../../global/selectors';
|
import { selectPeer } from '../../../../global/selectors';
|
||||||
@ -16,7 +17,7 @@ import { formatDateTimeToString } from '../../../../util/dates/dateFormat';
|
|||||||
import { CUSTOM_PEER_PREMIUM } from '../../../../util/objects/customPeer';
|
import { CUSTOM_PEER_PREMIUM } from '../../../../util/objects/customPeer';
|
||||||
import { getGiftAttributes, getStickerFromGift } from '../../../common/helpers/gifts';
|
import { getGiftAttributes, getStickerFromGift } from '../../../common/helpers/gifts';
|
||||||
import renderText from '../../../common/helpers/renderText';
|
import renderText from '../../../common/helpers/renderText';
|
||||||
import { getTransactionTitle, isNegativeStarsAmount } from '../helpers/transaction';
|
import { getTransactionTitle, isNegativeAmount } from '../helpers/transaction';
|
||||||
|
|
||||||
import useSelector from '../../../../hooks/data/useSelector';
|
import useSelector from '../../../../hooks/data/useSelector';
|
||||||
import useLang from '../../../../hooks/useLang';
|
import useLang from '../../../../hooks/useLang';
|
||||||
@ -48,7 +49,7 @@ const StarsTransactionItem = ({ transaction, className }: OwnProps) => {
|
|||||||
const { openStarsTransactionModal } = getActions();
|
const { openStarsTransactionModal } = getActions();
|
||||||
const {
|
const {
|
||||||
date,
|
date,
|
||||||
stars,
|
amount,
|
||||||
photo,
|
photo,
|
||||||
peer: transactionPeer,
|
peer: transactionPeer,
|
||||||
extendedMedia,
|
extendedMedia,
|
||||||
@ -73,7 +74,10 @@ const StarsTransactionItem = ({ transaction, className }: OwnProps) => {
|
|||||||
description = peer && getPeerTitle(oldLang, peer);
|
description = peer && getPeerTitle(oldLang, peer);
|
||||||
avatarPeer = peer || CUSTOM_PEER_PREMIUM;
|
avatarPeer = peer || CUSTOM_PEER_PREMIUM;
|
||||||
} else {
|
} else {
|
||||||
const customPeer = buildStarsTransactionCustomPeer(transaction.peer);
|
const customPeer = buildStarsTransactionCustomPeer(
|
||||||
|
transaction.peer,
|
||||||
|
transaction.amount.currency === TON_CURRENCY_CODE,
|
||||||
|
);
|
||||||
title = customPeer.title || oldLang(customPeer.titleKey!);
|
title = customPeer.title || oldLang(customPeer.titleKey!);
|
||||||
description = oldLang(customPeer.subtitleKey!);
|
description = oldLang(customPeer.subtitleKey!);
|
||||||
avatarPeer = customPeer;
|
avatarPeer = customPeer;
|
||||||
@ -178,11 +182,11 @@ const StarsTransactionItem = ({ transaction, className }: OwnProps) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className={styles.stars}>
|
<div className={styles.stars}>
|
||||||
<span
|
<span
|
||||||
className={buildClassName(styles.amount, isNegativeStarsAmount(stars) ? styles.negative : styles.positive)}
|
className={buildClassName(styles.amount, isNegativeAmount(amount) ? styles.negative : styles.positive)}
|
||||||
>
|
>
|
||||||
{formatStarsTransactionAmount(lang, stars)}
|
{formatStarsTransactionAmount(lang, amount)}
|
||||||
</span>
|
</span>
|
||||||
<StarIcon className={styles.star} type="gold" size="adaptive" />
|
{amount.currency === STARS_CURRENCY_CODE && <StarIcon className={styles.star} type="gold" size="adaptive" />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import type {
|
|||||||
import type { TabState } from '../../../../global/types';
|
import type { TabState } from '../../../../global/types';
|
||||||
import { MediaViewerOrigin } from '../../../../types';
|
import { MediaViewerOrigin } from '../../../../types';
|
||||||
|
|
||||||
|
import { STARS_CURRENCY_CODE } from '../../../../config';
|
||||||
import { getMessageLink } from '../../../../global/helpers';
|
import { getMessageLink } from '../../../../global/helpers';
|
||||||
import {
|
import {
|
||||||
buildStarsTransactionCustomPeer,
|
buildStarsTransactionCustomPeer,
|
||||||
@ -17,6 +18,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
selectCanPlayAnimatedEmojis,
|
selectCanPlayAnimatedEmojis,
|
||||||
selectGiftStickerForStars,
|
selectGiftStickerForStars,
|
||||||
|
selectGiftStickerForTon,
|
||||||
selectPeer,
|
selectPeer,
|
||||||
} from '../../../../global/selectors';
|
} from '../../../../global/selectors';
|
||||||
import buildClassName from '../../../../util/buildClassName';
|
import buildClassName from '../../../../util/buildClassName';
|
||||||
@ -25,7 +27,7 @@ import { formatDateTimeToString } from '../../../../util/dates/dateFormat';
|
|||||||
import { formatStarsAsIcon } from '../../../../util/localization/format';
|
import { formatStarsAsIcon } from '../../../../util/localization/format';
|
||||||
import { formatPercent } from '../../../../util/textFormat';
|
import { formatPercent } from '../../../../util/textFormat';
|
||||||
import { getGiftAttributes, getStickerFromGift } from '../../../common/helpers/gifts';
|
import { getGiftAttributes, getStickerFromGift } from '../../../common/helpers/gifts';
|
||||||
import { getTransactionTitle, isNegativeStarsAmount } from '../helpers/transaction';
|
import { getTransactionTitle, isNegativeAmount } from '../helpers/transaction';
|
||||||
|
|
||||||
import useLang from '../../../../hooks/useLang';
|
import useLang from '../../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../../hooks/useLastCallback';
|
import useLastCallback from '../../../../hooks/useLastCallback';
|
||||||
@ -85,7 +87,7 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
giveawayPostId, photo, stars, isGiftUpgrade, starGift, isGiftResale,
|
giveawayPostId, photo, amount, isGiftUpgrade, starGift, isGiftResale,
|
||||||
starRefCommision,
|
starRefCommision,
|
||||||
} = transaction;
|
} = transaction;
|
||||||
|
|
||||||
@ -132,7 +134,7 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
modelAttribute={giftAttributes!.model!}
|
modelAttribute={giftAttributes!.model!}
|
||||||
title={gift.title}
|
title={gift.title}
|
||||||
subtitle={lang('GiftInfoCollectible', { number: gift.number })}
|
subtitle={lang('GiftInfoCollectible', { number: gift.number })}
|
||||||
resellPrice={transaction.stars}
|
resellPrice={transaction.amount}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -169,11 +171,11 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
<p className={styles.description}>{description}</p>
|
<p className={styles.description}>{description}</p>
|
||||||
<p className={styles.amount}>
|
<p className={styles.amount}>
|
||||||
<span
|
<span
|
||||||
className={buildClassName(styles.amount, isNegativeStarsAmount(stars) ? styles.negative : styles.positive)}
|
className={buildClassName(styles.amount, isNegativeAmount(amount) ? styles.negative : styles.positive)}
|
||||||
>
|
>
|
||||||
{formatStarsTransactionAmount(lang, stars)}
|
{formatStarsTransactionAmount(lang, amount)}
|
||||||
</span>
|
</span>
|
||||||
<StarIcon type="gold" size="middle" />
|
{amount.currency === STARS_CURRENCY_CODE && <StarIcon type="gold" size="middle" />}
|
||||||
{transaction.isRefund && (
|
{transaction.isRefund && (
|
||||||
<p className={styles.refunded}>{lang('Refunded')}</p>
|
<p className={styles.refunded}>{lang('Refunded')}</p>
|
||||||
)}
|
)}
|
||||||
@ -212,7 +214,7 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
if (isGiftResale) {
|
if (isGiftResale) {
|
||||||
tableData.push([
|
tableData.push([
|
||||||
oldLang('StarGiftReason'),
|
oldLang('StarGiftReason'),
|
||||||
isNegativeStarsAmount(transaction.stars)
|
isNegativeAmount(transaction.amount)
|
||||||
? lang('StarGiftSaleTransaction')
|
? lang('StarGiftSaleTransaction')
|
||||||
: lang('StarGiftPurchaseTransaction'),
|
: lang('StarGiftPurchaseTransaction'),
|
||||||
]);
|
]);
|
||||||
@ -221,7 +223,7 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
let peerLabel;
|
let peerLabel;
|
||||||
if (isGiftUpgrade) {
|
if (isGiftUpgrade) {
|
||||||
peerLabel = oldLang('Stars.Transaction.GiftFrom');
|
peerLabel = oldLang('Stars.Transaction.GiftFrom');
|
||||||
} else if (isNegativeStarsAmount(stars) || transaction.isMyGift) {
|
} else if (isNegativeAmount(amount) || transaction.isMyGift) {
|
||||||
peerLabel = oldLang('Stars.Transaction.To');
|
peerLabel = oldLang('Stars.Transaction.To');
|
||||||
} else if (transaction.starRefCommision && !transaction.paidMessages && !isGiftResale) {
|
} else if (transaction.starRefCommision && !transaction.paidMessages && !isGiftResale) {
|
||||||
peerLabel = oldLang('StarsTransaction.StarRefReason.Miniapp');
|
peerLabel = oldLang('StarsTransaction.StarRefReason.Miniapp');
|
||||||
@ -240,7 +242,7 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
tableData.push([
|
tableData.push([
|
||||||
lang('PaidMessageTransactionTotal'),
|
lang('PaidMessageTransactionTotal'),
|
||||||
formatStarsAsIcon(lang,
|
formatStarsAsIcon(lang,
|
||||||
transaction.stars.amount / ((100 - transaction.starRefCommision) / 100),
|
transaction.amount.amount / ((100 - transaction.starRefCommision) / 100),
|
||||||
{ asFont: false, className: styles.starIcon, containerClassName: styles.totalStars }),
|
{ asFont: false, className: styles.starIcon, containerClassName: styles.totalStars }),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -249,9 +251,9 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
tableData.push([oldLang('Stars.Transaction.Reaction.Post'), <SafeLink url={messageLink} text={messageLink} />]);
|
tableData.push([oldLang('Stars.Transaction.Reaction.Post'), <SafeLink url={messageLink} text={messageLink} />]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (giveawayMessageLink) {
|
if (giveawayMessageLink && transaction.amount.currency === STARS_CURRENCY_CODE) {
|
||||||
tableData.push([oldLang('BoostReason'), <SafeLink url={giveawayMessageLink} text={oldLang('Giveaway')} />]);
|
tableData.push([oldLang('BoostReason'), <SafeLink url={giveawayMessageLink} text={oldLang('Giveaway')} />]);
|
||||||
tableData.push([oldLang('Gift'), oldLang('Stars', transaction.stars, 'i')]);
|
tableData.push([oldLang('Gift'), oldLang('Stars', transaction.amount, 'i')]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transaction.id) {
|
if (transaction.id) {
|
||||||
@ -322,8 +324,10 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const peer = peerId ? selectPeer(global, peerId) : undefined;
|
const peer = peerId ? selectPeer(global, peerId) : undefined;
|
||||||
const paidMessageCommission = global.appConfig?.starsPaidMessageCommissionPermille;
|
const paidMessageCommission = global.appConfig?.starsPaidMessageCommissionPermille;
|
||||||
|
|
||||||
const starCount = modal?.transaction.stars;
|
const currencyAmount = modal?.transaction.amount;
|
||||||
const starsGiftSticker = modal?.transaction.isGift && selectGiftStickerForStars(global, starCount?.amount);
|
const starsGiftSticker = modal?.transaction.isGift
|
||||||
|
&& currencyAmount?.currency === STARS_CURRENCY_CODE ? selectGiftStickerForStars(global, currencyAmount?.amount)
|
||||||
|
: selectGiftStickerForTon(global, currencyAmount?.amount);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
peer,
|
peer,
|
||||||
|
|||||||
@ -53,3 +53,14 @@
|
|||||||
.offerButton {
|
.offerButton {
|
||||||
font-weight: var(--font-weight-medium);
|
font-weight: var(--font-weight-medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.currencySelector {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.currencyIcon {
|
||||||
|
margin-inline-end: 0.25rem;
|
||||||
|
}
|
||||||
|
|||||||
@ -4,22 +4,31 @@ import {
|
|||||||
useState } from '../../../lib/teact/teact';
|
useState } from '../../../lib/teact/teact';
|
||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import type { ApiDraft, ApiStarsAmount } from '../../../api/types';
|
import type { ApiDraft, ApiStarsAmount, ApiTypeCurrencyAmount } from '../../../api/types';
|
||||||
import type { ApiPeer } from '../../../api/types';
|
import type { ApiPeer } from '../../../api/types';
|
||||||
import type { TabState } from '../../../global/types';
|
import type { TabState } from '../../../global/types';
|
||||||
import { MAIN_THREAD_ID } from '../../../api/types';
|
import { MAIN_THREAD_ID } from '../../../api/types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
STARS_CURRENCY_CODE,
|
||||||
STARS_SUGGESTED_POST_AGE_MIN,
|
STARS_SUGGESTED_POST_AGE_MIN,
|
||||||
STARS_SUGGESTED_POST_AMOUNT_MAX,
|
STARS_SUGGESTED_POST_AMOUNT_MAX,
|
||||||
STARS_SUGGESTED_POST_AMOUNT_MIN,
|
STARS_SUGGESTED_POST_AMOUNT_MIN,
|
||||||
STARS_SUGGESTED_POST_FUTURE_MAX,
|
STARS_SUGGESTED_POST_FUTURE_MAX,
|
||||||
STARS_SUGGESTED_POST_FUTURE_MIN } from '../../../config';
|
STARS_SUGGESTED_POST_FUTURE_MIN,
|
||||||
import { selectPeer } from '../../../global/selectors';
|
TON_CURRENCY_CODE,
|
||||||
|
TON_SUGGESTED_POST_AMOUNT_MAX,
|
||||||
|
TON_SUGGESTED_POST_AMOUNT_MIN } from '../../../config';
|
||||||
|
import { selectIsMonoforumAdmin, selectPeer } from '../../../global/selectors';
|
||||||
import { selectDraft } from '../../../global/selectors/messages';
|
import { selectDraft } from '../../../global/selectors/messages';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import { formatScheduledDateTime, formatShortDuration } from '../../../util/dates/dateFormat';
|
import { formatScheduledDateTime, formatShortDuration } from '../../../util/dates/dateFormat';
|
||||||
import { formatStarsAsIcon, formatStarsAsText } from '../../../util/localization/format';
|
import { convertTonFromNanos, convertTonToNanos } from '../../../util/formatCurrency';
|
||||||
|
import {
|
||||||
|
formatStarsAsIcon,
|
||||||
|
formatStarsAsText,
|
||||||
|
formatTonAsIcon,
|
||||||
|
formatTonAsText } from '../../../util/localization/format';
|
||||||
|
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
@ -41,25 +50,33 @@ import useFlag from '../../../hooks/useFlag';
|
|||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
starBalance?: ApiStarsAmount;
|
starBalance?: ApiStarsAmount;
|
||||||
|
tonBalance?: number;
|
||||||
peer?: ApiPeer;
|
peer?: ApiPeer;
|
||||||
currentDraft?: ApiDraft;
|
currentDraft?: ApiDraft;
|
||||||
maxAmount: number;
|
maxStarsAmount: number;
|
||||||
minAmount: number;
|
minStarsAmount: number;
|
||||||
|
tonMaxAmount: number;
|
||||||
|
tonMinAmount: number;
|
||||||
ageMinSeconds: number;
|
ageMinSeconds: number;
|
||||||
futureMin: number;
|
futureMin: number;
|
||||||
futureMax: number;
|
futureMax: number;
|
||||||
|
isMonoforumAdmin?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SuggestMessageModal = ({
|
const SuggestMessageModal = ({
|
||||||
modal,
|
modal,
|
||||||
starBalance,
|
starBalance,
|
||||||
|
tonBalance,
|
||||||
peer,
|
peer,
|
||||||
currentDraft,
|
currentDraft,
|
||||||
maxAmount,
|
maxStarsAmount,
|
||||||
minAmount,
|
minStarsAmount,
|
||||||
|
tonMaxAmount,
|
||||||
|
tonMinAmount,
|
||||||
ageMinSeconds,
|
ageMinSeconds,
|
||||||
futureMin,
|
futureMin,
|
||||||
futureMax,
|
futureMax,
|
||||||
|
isMonoforumAdmin,
|
||||||
}: OwnProps & StateProps) => {
|
}: OwnProps & StateProps) => {
|
||||||
const { closeSuggestMessageModal, updateDraftSuggestedPostInfo, openStarsBalanceModal } = getActions();
|
const { closeSuggestMessageModal, updateDraftSuggestedPostInfo, openStarsBalanceModal } = getActions();
|
||||||
const [isCalendarOpened, openCalendar, closeCalendar] = useFlag();
|
const [isCalendarOpened, openCalendar, closeCalendar] = useFlag();
|
||||||
@ -68,9 +85,12 @@ const SuggestMessageModal = ({
|
|||||||
const currentReplyInfo = currentDraft?.replyInfo;
|
const currentReplyInfo = currentDraft?.replyInfo;
|
||||||
const isInSuggestChangesMode = Boolean(currentReplyInfo);
|
const isInSuggestChangesMode = Boolean(currentReplyInfo);
|
||||||
|
|
||||||
const [starsAmount, setStarsAmount] = useState<number | undefined>(
|
const [currencyAmount, setCurrencyAmount] = useState<number | undefined>(
|
||||||
currentSuggestedPostInfo?.price?.amount || undefined,
|
currentSuggestedPostInfo?.price?.amount || undefined,
|
||||||
);
|
);
|
||||||
|
const [selectedCurrency, setSelectedCurrency] = useState<ApiTypeCurrencyAmount['currency']>(
|
||||||
|
currentSuggestedPostInfo?.price?.currency || STARS_CURRENCY_CODE,
|
||||||
|
);
|
||||||
const [scheduleDate, setScheduleDate] = useState<number | undefined>(
|
const [scheduleDate, setScheduleDate] = useState<number | undefined>(
|
||||||
currentSuggestedPostInfo?.scheduleDate
|
currentSuggestedPostInfo?.scheduleDate
|
||||||
? currentSuggestedPostInfo.scheduleDate * 1000
|
? currentSuggestedPostInfo.scheduleDate * 1000
|
||||||
@ -78,7 +98,10 @@ const SuggestMessageModal = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setStarsAmount(currentSuggestedPostInfo?.price?.amount || undefined);
|
const price = currentSuggestedPostInfo?.price;
|
||||||
|
const amount = price?.currency === TON_CURRENCY_CODE ? convertTonFromNanos(price.amount) : price?.amount;
|
||||||
|
setCurrencyAmount(amount);
|
||||||
|
setSelectedCurrency(currentSuggestedPostInfo?.price?.currency || STARS_CURRENCY_CODE);
|
||||||
setScheduleDate(currentSuggestedPostInfo?.scheduleDate
|
setScheduleDate(currentSuggestedPostInfo?.scheduleDate
|
||||||
? currentSuggestedPostInfo.scheduleDate * 1000
|
? currentSuggestedPostInfo.scheduleDate * 1000
|
||||||
: undefined);
|
: undefined);
|
||||||
@ -87,6 +110,7 @@ const SuggestMessageModal = ({
|
|||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
const oldLang = useOldLang();
|
const oldLang = useOldLang();
|
||||||
|
|
||||||
|
const isCurrencyStars = selectedCurrency === STARS_CURRENCY_CODE;
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const now = Math.floor(Date.now() / 1000);
|
||||||
const minAt = (now + futureMin) * 1000;
|
const minAt = (now + futureMin) * 1000;
|
||||||
const maxAt = (now + futureMax) * 1000;
|
const maxAt = (now + futureMax) * 1000;
|
||||||
@ -97,9 +121,9 @@ const SuggestMessageModal = ({
|
|||||||
const number = parseFloat(value);
|
const number = parseFloat(value);
|
||||||
|
|
||||||
const result = value === '' || Number.isNaN(number) ? undefined
|
const result = value === '' || Number.isNaN(number) ? undefined
|
||||||
: Math.min(Math.max(number, 0), maxAmount);
|
: Math.min(Math.max(number, 0), currentMaxAmount);
|
||||||
|
|
||||||
setStarsAmount(result);
|
setCurrencyAmount(result);
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleExpireDateChange = useLastCallback((date: Date) => {
|
const handleExpireDateChange = useLastCallback((date: Date) => {
|
||||||
@ -112,28 +136,44 @@ const SuggestMessageModal = ({
|
|||||||
closeCalendar();
|
closeCalendar();
|
||||||
});
|
});
|
||||||
|
|
||||||
const isDisabled = Boolean(starsAmount) && starsAmount < minAmount;
|
const currentMinAmount = isCurrencyStars ? minStarsAmount : convertTonFromNanos(tonMinAmount);
|
||||||
|
const currentMaxAmount = isCurrencyStars ? maxStarsAmount : convertTonFromNanos(tonMaxAmount);
|
||||||
|
const isDisabled = Boolean(currencyAmount) && currencyAmount < currentMinAmount;
|
||||||
|
|
||||||
const handleOffer = useLastCallback(() => {
|
const handleOffer = useLastCallback(() => {
|
||||||
const neededAmount = starsAmount || 0;
|
const neededAmount = currencyAmount
|
||||||
|
? (isCurrencyStars ? currencyAmount : convertTonToNanos(currencyAmount))
|
||||||
|
: 0;
|
||||||
|
|
||||||
if (isDisabled) {
|
if (isDisabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentBalance = starBalance?.amount || 0;
|
if (!isMonoforumAdmin) {
|
||||||
|
if (isCurrencyStars) {
|
||||||
|
const currentBalance = starBalance?.amount || 0;
|
||||||
|
|
||||||
if (neededAmount > currentBalance) {
|
if (neededAmount > currentBalance) {
|
||||||
openStarsBalanceModal({
|
openStarsBalanceModal({
|
||||||
topup: {
|
topup: {
|
||||||
balanceNeeded: neededAmount,
|
balanceNeeded: neededAmount,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const currentTonBalance = tonBalance || 0;
|
||||||
|
if (neededAmount > currentTonBalance) {
|
||||||
|
openStarsBalanceModal({
|
||||||
|
currency: TON_CURRENCY_CODE,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDraftSuggestedPostInfo({
|
updateDraftSuggestedPostInfo({
|
||||||
price: { amount: neededAmount, nanos: 0 },
|
price: { currency: selectedCurrency, amount: neededAmount, nanos: 0 },
|
||||||
scheduleDate: scheduleDate ? scheduleDate / 1000 : undefined,
|
scheduleDate: scheduleDate ? scheduleDate / 1000 : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -153,23 +193,51 @@ const SuggestMessageModal = ({
|
|||||||
>
|
>
|
||||||
<div className={styles.form}>
|
<div className={styles.form}>
|
||||||
<div className={styles.section}>
|
<div className={styles.section}>
|
||||||
|
<div className={styles.currencySelector}>
|
||||||
|
<Button
|
||||||
|
className={styles.currencyButton}
|
||||||
|
color={isCurrencyStars ? 'primary' : 'translucent'}
|
||||||
|
pill
|
||||||
|
fluid
|
||||||
|
size="tiny"
|
||||||
|
noFastClick
|
||||||
|
onClick={() => setSelectedCurrency(STARS_CURRENCY_CODE)}
|
||||||
|
>
|
||||||
|
<Icon name="star" className={styles.currencyIcon} />
|
||||||
|
{lang('CurrencyStars')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className={styles.currencyButton}
|
||||||
|
fluid
|
||||||
|
color={!isCurrencyStars ? 'primary' : 'translucent'}
|
||||||
|
pill
|
||||||
|
size="tiny"
|
||||||
|
noFastClick
|
||||||
|
onClick={() => setSelectedCurrency(TON_CURRENCY_CODE)}
|
||||||
|
>
|
||||||
|
<Icon name="toncoin" className={styles.currencyIcon} />
|
||||||
|
{lang('CurrencyTon')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
<InputText
|
<InputText
|
||||||
label={lang('InputPlaceholderPrice')}
|
label={lang('InputPlaceholderPrice')}
|
||||||
className={buildClassName(styles.input)}
|
className={buildClassName(styles.input)}
|
||||||
value={starsAmount?.toString()}
|
value={currencyAmount?.toString()}
|
||||||
onChange={handleAmountChange}
|
onChange={handleAmountChange}
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
teactExperimentControlled
|
teactExperimentControlled={isCurrencyStars}
|
||||||
/>
|
/>
|
||||||
<div className={styles.description}>
|
<div className={styles.description}>
|
||||||
{starsAmount !== undefined && starsAmount > 0 && starsAmount < minAmount
|
{currencyAmount !== undefined && currencyAmount > 0 && currencyAmount < currentMinAmount
|
||||||
? lang('DescriptionSuggestedPostMinimumOffer', {
|
? lang('DescriptionSuggestedPostMinimumOffer', {
|
||||||
amount: formatStarsAsText(lang, minAmount) },
|
amount: isCurrencyStars
|
||||||
|
? formatStarsAsText(lang, currentMinAmount)
|
||||||
|
: formatTonAsText(lang, currentMinAmount) },
|
||||||
{ withNodes: true, withMarkdown: true })
|
{ withNodes: true, withMarkdown: true })
|
||||||
: lang('SuggestMessagePriceDescription', {
|
: isCurrencyStars
|
||||||
currency: lang('CurrencyStars'),
|
? lang('SuggestMessagePriceDescriptionStars')
|
||||||
})}
|
: lang('SuggestMessagePriceDescriptionTon')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -178,7 +246,9 @@ const SuggestMessageModal = ({
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className={buildClassName('form-control', isCalendarOpened && 'focus')}
|
className={buildClassName('form-control', isCalendarOpened && 'focus')}
|
||||||
value={scheduleDate ? formatScheduledDateTime(scheduleDate / 1000, lang, oldLang) : lang('TitleAnytime')}
|
value={scheduleDate
|
||||||
|
? formatScheduledDateTime(scheduleDate / 1000, lang, oldLang)
|
||||||
|
: lang('SuggestMessageAnytime')}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
onClick={openCalendar}
|
onClick={openCalendar}
|
||||||
onFocus={openCalendar}
|
onFocus={openCalendar}
|
||||||
@ -205,7 +275,7 @@ const SuggestMessageModal = ({
|
|||||||
onSubmit={handleExpireDateChange}
|
onSubmit={handleExpireDateChange}
|
||||||
selectedAt={scheduleDate || defaultSelectedTime}
|
selectedAt={scheduleDate || defaultSelectedTime}
|
||||||
submitButtonLabel={lang('Save')}
|
submitButtonLabel={lang('Save')}
|
||||||
secondButtonLabel={lang('TitleAnytime')}
|
secondButtonLabel={lang('SuggestMessageAnytime')}
|
||||||
onSecondButtonClick={handleAnytimeClick}
|
onSecondButtonClick={handleAnytimeClick}
|
||||||
description={lang('SuggestMessageDateTimeHint')}
|
description={lang('SuggestMessageDateTimeHint')}
|
||||||
/>
|
/>
|
||||||
@ -217,8 +287,10 @@ const SuggestMessageModal = ({
|
|||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
>
|
>
|
||||||
{isInSuggestChangesMode ? lang('ButtonUpdateTerms')
|
{isInSuggestChangesMode ? lang('ButtonUpdateTerms')
|
||||||
: starsAmount ? lang('ButtonOfferAmount', {
|
: currencyAmount ? lang('ButtonOfferAmount', {
|
||||||
amount: formatStarsAsIcon(lang, starsAmount, { asFont: true }),
|
amount: isCurrencyStars
|
||||||
|
? formatStarsAsIcon(lang, currencyAmount, { asFont: true })
|
||||||
|
: formatTonAsIcon(lang, currencyAmount, { asFont: true }),
|
||||||
}, {
|
}, {
|
||||||
withNodes: true,
|
withNodes: true,
|
||||||
withMarkdown: true,
|
withMarkdown: true,
|
||||||
@ -236,21 +308,30 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const currentDraft = modal ? selectDraft(global, modal.chatId, MAIN_THREAD_ID) : undefined;
|
const currentDraft = modal ? selectDraft(global, modal.chatId, MAIN_THREAD_ID) : undefined;
|
||||||
|
|
||||||
const { appConfig } = global;
|
const { appConfig } = global;
|
||||||
const maxAmount = appConfig?.starsSuggestedPostAmountMax || STARS_SUGGESTED_POST_AMOUNT_MAX;
|
const maxStarsAmount = appConfig?.starsSuggestedPostAmountMax || STARS_SUGGESTED_POST_AMOUNT_MAX;
|
||||||
const minAmount = appConfig?.starsSuggestedPostAmountMin || STARS_SUGGESTED_POST_AMOUNT_MIN;
|
const minStarsAmount = appConfig?.starsSuggestedPostAmountMin || STARS_SUGGESTED_POST_AMOUNT_MIN;
|
||||||
const ageMinSeconds = appConfig?.starsSuggestedPostAgeMin || STARS_SUGGESTED_POST_AGE_MIN;
|
const ageMinSeconds = appConfig?.starsSuggestedPostAgeMin || STARS_SUGGESTED_POST_AGE_MIN;
|
||||||
const futureMin = appConfig?.starsSuggestedPostFutureMin || STARS_SUGGESTED_POST_FUTURE_MIN;
|
const futureMin = appConfig?.starsSuggestedPostFutureMin || STARS_SUGGESTED_POST_FUTURE_MIN;
|
||||||
const futureMax = appConfig?.starsSuggestedPostFutureMax || STARS_SUGGESTED_POST_FUTURE_MAX;
|
const futureMax = appConfig?.starsSuggestedPostFutureMax || STARS_SUGGESTED_POST_FUTURE_MAX;
|
||||||
|
|
||||||
|
const tonMaxAmount = appConfig?.tonSuggestedPostAmountMax || TON_SUGGESTED_POST_AMOUNT_MAX;
|
||||||
|
const tonMinAmount = appConfig?.tonSuggestedPostAmountMin || TON_SUGGESTED_POST_AMOUNT_MIN;
|
||||||
|
|
||||||
|
const isMonoforumAdmin = modal ? selectIsMonoforumAdmin(global, modal.chatId) : false;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
peer,
|
peer,
|
||||||
starBalance,
|
starBalance,
|
||||||
|
tonBalance: global.ton?.balance?.amount,
|
||||||
currentDraft,
|
currentDraft,
|
||||||
maxAmount,
|
maxStarsAmount,
|
||||||
minAmount,
|
minStarsAmount,
|
||||||
|
tonMaxAmount,
|
||||||
|
tonMinAmount,
|
||||||
ageMinSeconds,
|
ageMinSeconds,
|
||||||
futureMin,
|
futureMin,
|
||||||
futureMax,
|
futureMax,
|
||||||
|
isMonoforumAdmin,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(SuggestMessageModal));
|
)(SuggestMessageModal));
|
||||||
|
|||||||
@ -4,15 +4,18 @@ import { getActions, withGlobal } from '../../../global';
|
|||||||
import type { ApiMessage, ApiPeer } from '../../../api/types';
|
import type { ApiMessage, ApiPeer } from '../../../api/types';
|
||||||
import type { TabState } from '../../../global/types';
|
import type { TabState } from '../../../global/types';
|
||||||
|
|
||||||
import { STARS_SUGGESTED_POST_AGE_MIN,
|
import { STARS_CURRENCY_CODE, STARS_SUGGESTED_POST_AGE_MIN,
|
||||||
STARS_SUGGESTED_POST_COMMISSION_PERMILLE,
|
STARS_SUGGESTED_POST_COMMISSION_PERMILLE,
|
||||||
STARS_SUGGESTED_POST_FUTURE_MAX,
|
STARS_SUGGESTED_POST_FUTURE_MAX,
|
||||||
STARS_SUGGESTED_POST_FUTURE_MIN,
|
STARS_SUGGESTED_POST_FUTURE_MIN,
|
||||||
|
TON_CURRENCY_CODE,
|
||||||
|
TON_SUGGESTED_POST_COMMISSION_PERMILLE,
|
||||||
} from '../../../config';
|
} from '../../../config';
|
||||||
import { getPeerFullTitle } from '../../../global/helpers/peers';
|
import { getPeerFullTitle } from '../../../global/helpers/peers';
|
||||||
import { selectChatMessage, selectIsMonoforumAdmin, selectSender } from '../../../global/selectors';
|
import { selectChatMessage, selectIsMonoforumAdmin, selectSender } from '../../../global/selectors';
|
||||||
import { formatScheduledDateTime, formatShortDuration } from '../../../util/dates/dateFormat';
|
import { formatScheduledDateTime, formatShortDuration } from '../../../util/dates/dateFormat';
|
||||||
import { formatStarsAsText } from '../../../util/localization/format';
|
import { convertTonFromNanos } from '../../../util/formatCurrency';
|
||||||
|
import { formatStarsAsText, formatTonAsText } from '../../../util/localization/format';
|
||||||
import renderText from '../../common/helpers/renderText';
|
import renderText from '../../common/helpers/renderText';
|
||||||
|
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
@ -31,6 +34,7 @@ export type OwnProps = {
|
|||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
commissionPermille: number;
|
commissionPermille: number;
|
||||||
|
tonCommissionPermille: number;
|
||||||
minAge: number;
|
minAge: number;
|
||||||
futureMin: number;
|
futureMin: number;
|
||||||
futureMax: number;
|
futureMax: number;
|
||||||
@ -46,6 +50,7 @@ const SuggestedPostApprovalModal = ({
|
|||||||
sender,
|
sender,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
commissionPermille,
|
commissionPermille,
|
||||||
|
tonCommissionPermille,
|
||||||
minAge,
|
minAge,
|
||||||
futureMin,
|
futureMin,
|
||||||
futureMax,
|
futureMax,
|
||||||
@ -114,7 +119,10 @@ const SuggestedPostApprovalModal = ({
|
|||||||
const senderName = sender ? getPeerFullTitle(oldLang, sender) : '';
|
const senderName = sender ? getPeerFullTitle(oldLang, sender) : '';
|
||||||
|
|
||||||
const renderContent = () => {
|
const renderContent = () => {
|
||||||
const amount = message?.suggestedPostInfo?.price?.amount;
|
const price = message?.suggestedPostInfo?.price;
|
||||||
|
const amount = price?.amount;
|
||||||
|
const currency = price?.currency || STARS_CURRENCY_CODE;
|
||||||
|
|
||||||
const question = lang(
|
const question = lang(
|
||||||
'SuggestedPostConfirmMessage',
|
'SuggestedPostConfirmMessage',
|
||||||
{ peer: senderName },
|
{ peer: senderName },
|
||||||
@ -125,10 +133,13 @@ const SuggestedPostApprovalModal = ({
|
|||||||
return questionText;
|
return questionText;
|
||||||
}
|
}
|
||||||
|
|
||||||
const commission = (commissionPermille / 10);
|
const currentCommissionPermille = currency === TON_CURRENCY_CODE ? tonCommissionPermille : commissionPermille;
|
||||||
|
const commission = (currentCommissionPermille / 10);
|
||||||
const amountWithCommission = amount / 100 * commission;
|
const amountWithCommission = amount / 100 * commission;
|
||||||
|
|
||||||
const starsText = formatStarsAsText(lang, amountWithCommission);
|
const formattedAmount = currency === TON_CURRENCY_CODE
|
||||||
|
? formatTonAsText(lang, convertTonFromNanos(amountWithCommission))
|
||||||
|
: formatStarsAsText(lang, amountWithCommission);
|
||||||
|
|
||||||
const ageMinSeconds = minAge;
|
const ageMinSeconds = minAge;
|
||||||
const duration = formatShortDuration(lang, ageMinSeconds, true);
|
const duration = formatShortDuration(lang, ageMinSeconds, true);
|
||||||
@ -146,7 +157,7 @@ const SuggestedPostApprovalModal = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className={styles.details}>
|
<div className={styles.details}>
|
||||||
{renderText(lang(key, {
|
{renderText(lang(key, {
|
||||||
amount: starsText,
|
amount: formattedAmount,
|
||||||
commission,
|
commission,
|
||||||
duration,
|
duration,
|
||||||
time,
|
time,
|
||||||
@ -165,7 +176,7 @@ const SuggestedPostApprovalModal = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className={styles.details}>
|
<div className={styles.details}>
|
||||||
{renderText(lang(key, {
|
{renderText(lang(key, {
|
||||||
amount: starsText,
|
amount: formattedAmount,
|
||||||
commission,
|
commission,
|
||||||
duration,
|
duration,
|
||||||
}, { withNodes: true, withMarkdown: true }))}
|
}, { withNodes: true, withMarkdown: true }))}
|
||||||
@ -215,6 +226,8 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const { appConfig } = global;
|
const { appConfig } = global;
|
||||||
const commissionPermille = appConfig?.starsSuggestedPostCommissionPermille
|
const commissionPermille = appConfig?.starsSuggestedPostCommissionPermille
|
||||||
|| STARS_SUGGESTED_POST_COMMISSION_PERMILLE;
|
|| STARS_SUGGESTED_POST_COMMISSION_PERMILLE;
|
||||||
|
const tonCommissionPermille = appConfig?.tonSuggestedPostCommissionPermille
|
||||||
|
|| TON_SUGGESTED_POST_COMMISSION_PERMILLE;
|
||||||
const minAge = appConfig?.starsSuggestedPostAgeMin || STARS_SUGGESTED_POST_AGE_MIN;
|
const minAge = appConfig?.starsSuggestedPostAgeMin || STARS_SUGGESTED_POST_AGE_MIN;
|
||||||
const futureMin = (appConfig?.starsSuggestedPostFutureMin || STARS_SUGGESTED_POST_FUTURE_MIN) * 2;
|
const futureMin = (appConfig?.starsSuggestedPostFutureMin || STARS_SUGGESTED_POST_FUTURE_MIN) * 2;
|
||||||
const futureMax = appConfig?.starsSuggestedPostFutureMax || STARS_SUGGESTED_POST_FUTURE_MAX;
|
const futureMax = appConfig?.starsSuggestedPostFutureMax || STARS_SUGGESTED_POST_FUTURE_MAX;
|
||||||
@ -228,6 +241,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
sender,
|
sender,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
commissionPermille,
|
commissionPermille,
|
||||||
|
tonCommissionPermille,
|
||||||
scheduleDate,
|
scheduleDate,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@ -117,7 +117,12 @@ export const STARS_SUGGESTED_POST_COMMISSION_PERMILLE = 850;
|
|||||||
export const STARS_SUGGESTED_POST_AGE_MIN = 86400; // 24 hours in seconds
|
export const STARS_SUGGESTED_POST_AGE_MIN = 86400; // 24 hours in seconds
|
||||||
export const STARS_SUGGESTED_POST_FUTURE_MAX = 2678400; // 31 days in seconds
|
export const STARS_SUGGESTED_POST_FUTURE_MAX = 2678400; // 31 days in seconds
|
||||||
export const STARS_SUGGESTED_POST_FUTURE_MIN = 300; // 5 minutes in seconds
|
export const STARS_SUGGESTED_POST_FUTURE_MIN = 300; // 5 minutes in seconds
|
||||||
|
export const TON_CURRENCY_CODE = 'TON';
|
||||||
export const TON_SUGGESTED_POST_COMMISSION_PERMILLE = 850;
|
export const TON_SUGGESTED_POST_COMMISSION_PERMILLE = 850;
|
||||||
|
export const TON_USD_RATE_DEFAULT = 3;
|
||||||
|
export const TON_TOPUP_URL_DEFAULT = 'https://fragment.com/ads/topup';
|
||||||
|
export const TON_SUGGESTED_POST_AMOUNT_MIN = 10000000; // 0.01 TON in nanos
|
||||||
|
export const TON_SUGGESTED_POST_AMOUNT_MAX = 10000000000000; // 10 000 TON in nanos
|
||||||
|
|
||||||
export const STORY_VIEWS_MIN_SEARCH = 15;
|
export const STORY_VIEWS_MIN_SEARCH = 15;
|
||||||
export const STORY_MIN_REACTIONS_SORT = 10;
|
export const STORY_MIN_REACTIONS_SORT = 10;
|
||||||
|
|||||||
@ -33,10 +33,12 @@ import {
|
|||||||
MESSAGE_LIST_SLICE,
|
MESSAGE_LIST_SLICE,
|
||||||
RE_TELEGRAM_LINK,
|
RE_TELEGRAM_LINK,
|
||||||
SERVICE_NOTIFICATIONS_USER_ID,
|
SERVICE_NOTIFICATIONS_USER_ID,
|
||||||
|
STARS_CURRENCY_CODE,
|
||||||
STARS_SUGGESTED_POST_FUTURE_MIN,
|
STARS_SUGGESTED_POST_FUTURE_MIN,
|
||||||
SUPPORTED_AUDIO_CONTENT_TYPES,
|
SUPPORTED_AUDIO_CONTENT_TYPES,
|
||||||
SUPPORTED_PHOTO_CONTENT_TYPES,
|
SUPPORTED_PHOTO_CONTENT_TYPES,
|
||||||
SUPPORTED_VIDEO_CONTENT_TYPES,
|
SUPPORTED_VIDEO_CONTENT_TYPES,
|
||||||
|
TON_CURRENCY_CODE,
|
||||||
} from '../../../config';
|
} from '../../../config';
|
||||||
import { ensureProtocol, isMixedScriptUrl } from '../../../util/browser/url';
|
import { ensureProtocol, isMixedScriptUrl } from '../../../util/browser/url';
|
||||||
import { IS_IOS } from '../../../util/browser/windowEnvironment';
|
import { IS_IOS } from '../../../util/browser/windowEnvironment';
|
||||||
@ -361,18 +363,31 @@ addActionHandler('sendMessage', async (global, actions, payload): Promise<void>
|
|||||||
|
|
||||||
const messagePriceInStars = await getPeerStarsForMessage(global, chatId!);
|
const messagePriceInStars = await getPeerStarsForMessage(global, chatId!);
|
||||||
|
|
||||||
const suggestedPostPrice = draftSuggestedPostInfo?.price?.amount || 0;
|
const suggestedPostPrice = draftSuggestedPostInfo?.price;
|
||||||
if (suggestedPostPrice && !draftReplyInfo) {
|
const suggestedPostCurrency = suggestedPostPrice?.currency || STARS_CURRENCY_CODE;
|
||||||
const currentBalance = global.stars?.balance?.amount || 0;
|
const suggestedPostAmount = suggestedPostPrice?.amount || 0;
|
||||||
|
if (suggestedPostAmount && !draftReplyInfo) {
|
||||||
|
if (suggestedPostCurrency === STARS_CURRENCY_CODE) {
|
||||||
|
const currentBalance = global.stars?.balance?.amount || 0;
|
||||||
|
|
||||||
if (suggestedPostPrice > currentBalance) {
|
if (suggestedPostAmount > currentBalance) {
|
||||||
actions.openStarsBalanceModal({
|
actions.openStarsBalanceModal({
|
||||||
topup: {
|
topup: {
|
||||||
balanceNeeded: suggestedPostPrice,
|
balanceNeeded: suggestedPostAmount,
|
||||||
},
|
},
|
||||||
tabId,
|
tabId,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
} else if (suggestedPostCurrency === TON_CURRENCY_CODE) {
|
||||||
|
const currentTonBalance = global.ton?.balance?.amount || 0;
|
||||||
|
if (suggestedPostAmount > currentTonBalance) {
|
||||||
|
actions.openStarsBalanceModal({
|
||||||
|
currency: TON_CURRENCY_CODE,
|
||||||
|
tabId,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2203,16 +2218,28 @@ addActionHandler('approveSuggestedPost', async (global, actions, payload): Promi
|
|||||||
|
|
||||||
if (!isAdmin && message?.suggestedPostInfo?.price?.amount) {
|
if (!isAdmin && message?.suggestedPostInfo?.price?.amount) {
|
||||||
const neededAmount = message.suggestedPostInfo.price.amount;
|
const neededAmount = message.suggestedPostInfo.price.amount;
|
||||||
const currentBalance = global.stars?.balance?.amount || 0;
|
const isCurrencyStars = message.suggestedPostInfo.price.currency === STARS_CURRENCY_CODE;
|
||||||
|
|
||||||
if (neededAmount > currentBalance) {
|
if (isCurrencyStars) {
|
||||||
actions.openStarsBalanceModal({
|
const currentBalance = global.stars?.balance?.amount || 0;
|
||||||
topup: {
|
if (neededAmount > currentBalance) {
|
||||||
balanceNeeded: neededAmount,
|
actions.openStarsBalanceModal({
|
||||||
},
|
topup: {
|
||||||
tabId,
|
balanceNeeded: neededAmount,
|
||||||
});
|
},
|
||||||
return;
|
tabId,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const currentTonBalance = global.ton?.balance?.amount || 0;
|
||||||
|
if (neededAmount > currentTonBalance) {
|
||||||
|
actions.openStarsBalanceModal({
|
||||||
|
currency: TON_CURRENCY_CODE,
|
||||||
|
tabId,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,12 @@ import type { ApiSavedStarGift, ApiStarGiftUnique } from '../../../api/types';
|
|||||||
import type { StarGiftCategory } from '../../../types';
|
import type { StarGiftCategory } from '../../../types';
|
||||||
import type { ActionReturnType } from '../../types';
|
import type { ActionReturnType } from '../../types';
|
||||||
|
|
||||||
import { DEFAULT_RESALE_GIFTS_FILTER_OPTIONS, RESALE_GIFTS_LIMIT } from '../../../config';
|
import {
|
||||||
|
DEFAULT_RESALE_GIFTS_FILTER_OPTIONS,
|
||||||
|
RESALE_GIFTS_LIMIT,
|
||||||
|
STARS_CURRENCY_CODE,
|
||||||
|
TON_CURRENCY_CODE,
|
||||||
|
} from '../../../config';
|
||||||
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
||||||
import { buildCollectionByKey } from '../../../util/iteratees';
|
import { buildCollectionByKey } from '../../../util/iteratees';
|
||||||
import { callApi } from '../../../api/gramjs';
|
import { callApi } from '../../../api/gramjs';
|
||||||
@ -26,57 +31,82 @@ import {
|
|||||||
} from '../../selectors';
|
} from '../../selectors';
|
||||||
|
|
||||||
addActionHandler('loadStarStatus', async (global): Promise<void> => {
|
addActionHandler('loadStarStatus', async (global): Promise<void> => {
|
||||||
const currentStatus = global.stars;
|
const currentStarsStatus = global.stars;
|
||||||
const needsTopupOptions = !currentStatus?.topupOptions;
|
const needsTopupOptions = !currentStarsStatus?.topupOptions;
|
||||||
|
|
||||||
const [status, topupOptions] = await Promise.all([
|
const [starsStatus, tonStatus, topupOptions] = await Promise.all([
|
||||||
callApi('fetchStarsStatus'),
|
callApi('fetchStarsStatus'),
|
||||||
|
callApi('fetchStarsStatus', { isTon: true }),
|
||||||
needsTopupOptions ? callApi('fetchStarsTopupOptions') : undefined,
|
needsTopupOptions ? callApi('fetchStarsTopupOptions') : undefined,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!status || (needsTopupOptions && !topupOptions)) {
|
if (!(starsStatus || tonStatus) || (needsTopupOptions && !topupOptions)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
global = getGlobal();
|
global = getGlobal();
|
||||||
|
|
||||||
global = {
|
if (starsStatus && starsStatus.balance.currency === STARS_CURRENCY_CODE) {
|
||||||
...global,
|
global = {
|
||||||
stars: {
|
...global,
|
||||||
...currentStatus,
|
stars: {
|
||||||
balance: status.balance,
|
...currentStarsStatus,
|
||||||
topupOptions: topupOptions || currentStatus!.topupOptions,
|
balance: starsStatus.balance,
|
||||||
history: {
|
topupOptions: topupOptions || currentStarsStatus!.topupOptions,
|
||||||
all: undefined,
|
history: {
|
||||||
inbound: undefined,
|
all: undefined,
|
||||||
outbound: undefined,
|
inbound: undefined,
|
||||||
|
outbound: undefined,
|
||||||
|
},
|
||||||
|
subscriptions: undefined,
|
||||||
},
|
},
|
||||||
subscriptions: undefined,
|
};
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (status.history) {
|
if (starsStatus.history) {
|
||||||
global = appendStarsTransactions(global, 'all', status.history, status.nextHistoryOffset);
|
global = appendStarsTransactions(global, 'all', starsStatus.history, starsStatus.nextHistoryOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (starsStatus.subscriptions) {
|
||||||
|
global = appendStarsSubscriptions(global, starsStatus.subscriptions, starsStatus.nextSubscriptionOffset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.subscriptions) {
|
if (tonStatus?.balance.currency === TON_CURRENCY_CODE) {
|
||||||
global = appendStarsSubscriptions(global, status.subscriptions, status.nextSubscriptionOffset);
|
global = {
|
||||||
|
...global,
|
||||||
|
ton: {
|
||||||
|
...tonStatus,
|
||||||
|
balance: tonStatus.balance,
|
||||||
|
history: {
|
||||||
|
all: undefined,
|
||||||
|
inbound: undefined,
|
||||||
|
outbound: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
global = updateStarsBalance(global, tonStatus.balance);
|
||||||
|
|
||||||
|
if (tonStatus.history) {
|
||||||
|
global = appendStarsTransactions(global, 'all', tonStatus.history, tonStatus.nextHistoryOffset, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('loadStarsTransactions', async (global, actions, payload): Promise<void> => {
|
addActionHandler('loadStarsTransactions', async (global, actions, payload): Promise<void> => {
|
||||||
const { type } = payload;
|
const { type, isTon } = payload;
|
||||||
|
|
||||||
const history = global.stars?.history[type];
|
const history = isTon ? global.ton?.history[type] : global.stars?.history[type];
|
||||||
const offset = history?.nextOffset;
|
const offset = history?.nextOffset;
|
||||||
if (history && !offset) return; // Already loaded all
|
if (history && !offset) return; // Already loaded all
|
||||||
|
|
||||||
const result = await callApi('fetchStarsTransactions', {
|
const result = await callApi('fetchStarsTransactions', {
|
||||||
isInbound: type === 'inbound' || undefined,
|
isInbound: type === 'inbound',
|
||||||
isOutbound: type === 'outbound' || undefined,
|
isOutbound: type === 'outbound',
|
||||||
offset: offset || '',
|
offset: offset || '',
|
||||||
|
isTon,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
@ -87,7 +117,7 @@ addActionHandler('loadStarsTransactions', async (global, actions, payload): Prom
|
|||||||
|
|
||||||
global = updateStarsBalance(global, result.balance);
|
global = updateStarsBalance(global, result.balance);
|
||||||
if (result.history) {
|
if (result.history) {
|
||||||
global = appendStarsTransactions(global, type, result.history, result.nextOffset);
|
global = appendStarsTransactions(global, type, result.history, result.nextOffset, isTon);
|
||||||
}
|
}
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
});
|
});
|
||||||
@ -315,7 +345,7 @@ addActionHandler('loadStarsSubscriptions', async (global): Promise<void> => {
|
|||||||
offset: offset || '',
|
offset: offset || '',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result) {
|
if (!result || result.balance.currency !== STARS_CURRENCY_CODE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -215,6 +215,22 @@ addActionHandler('loadPremiumGifts', async (global): Promise<void> => {
|
|||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('loadTonGifts', async (global): Promise<void> => {
|
||||||
|
const stickerSet = await callApi('fetchTonGifts');
|
||||||
|
if (!stickerSet) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { set, stickers } = stickerSet;
|
||||||
|
|
||||||
|
global = getGlobal();
|
||||||
|
global = {
|
||||||
|
...global,
|
||||||
|
tonGifts: { ...set, stickers },
|
||||||
|
};
|
||||||
|
setGlobal(global);
|
||||||
|
});
|
||||||
|
|
||||||
addActionHandler('loadDefaultTopicIcons', async (global): Promise<void> => {
|
addActionHandler('loadDefaultTopicIcons', async (global): Promise<void> => {
|
||||||
const stickerSet = await callApi('fetchDefaultTopicIcons');
|
const stickerSet = await callApi('fetchDefaultTopicIcons');
|
||||||
if (!stickerSet) {
|
if (!stickerSet) {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import type { ApiInputSavedStarGift, ApiSavedStarGift } from '../../../api/types';
|
import type { ApiInputSavedStarGift, ApiSavedStarGift } from '../../../api/types';
|
||||||
import type { ActionReturnType } from '../../types';
|
import type { ActionReturnType } from '../../types';
|
||||||
|
|
||||||
|
import { STARS_CURRENCY_CODE } from '../../../config';
|
||||||
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
||||||
import * as langProvider from '../../../util/oldLangProvider';
|
import * as langProvider from '../../../util/oldLangProvider';
|
||||||
import { addTabStateResetterAction } from '../../helpers/meta';
|
import { addTabStateResetterAction } from '../../helpers/meta';
|
||||||
@ -110,6 +111,7 @@ addActionHandler('openStarsBalanceModal', (global, actions, payload): ActionRetu
|
|||||||
originGift,
|
originGift,
|
||||||
topup,
|
topup,
|
||||||
shouldIgnoreBalance,
|
shouldIgnoreBalance,
|
||||||
|
currency = STARS_CURRENCY_CODE,
|
||||||
tabId = getCurrentTabId(),
|
tabId = getCurrentTabId(),
|
||||||
} = payload || {};
|
} = payload || {};
|
||||||
|
|
||||||
@ -140,6 +142,7 @@ addActionHandler('openStarsBalanceModal', (global, actions, payload): ActionRetu
|
|||||||
originReaction,
|
originReaction,
|
||||||
originGift,
|
originGift,
|
||||||
topup,
|
topup,
|
||||||
|
currency,
|
||||||
},
|
},
|
||||||
}, tabId);
|
}, tabId);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -8,12 +8,15 @@ import type {
|
|||||||
ApiStarsTransaction,
|
ApiStarsTransaction,
|
||||||
ApiStarsTransactionPeer,
|
ApiStarsTransactionPeer,
|
||||||
ApiStarsTransactionPeerPeer,
|
ApiStarsTransactionPeerPeer,
|
||||||
|
ApiTypeCurrencyAmount,
|
||||||
} from '../../api/types';
|
} from '../../api/types';
|
||||||
import type { CustomPeer } from '../../types';
|
import type { CustomPeer } from '../../types';
|
||||||
import type { LangFn } from '../../util/localization';
|
import type { LangFn } from '../../util/localization';
|
||||||
import type { GlobalState } from '../types';
|
import type { GlobalState } from '../types';
|
||||||
|
|
||||||
|
import { STARS_CURRENCY_CODE, TON_CURRENCY_CODE } from '../../config';
|
||||||
import arePropsShallowEqual from '../../util/arePropsShallowEqual';
|
import arePropsShallowEqual from '../../util/arePropsShallowEqual';
|
||||||
|
import { convertCurrencyFromBaseUnit } from '../../util/formatCurrency';
|
||||||
import { selectChat, selectPeer, selectUser } from '../selectors';
|
import { selectChat, selectPeer, selectUser } from '../selectors';
|
||||||
|
|
||||||
export function getRequestInputInvoice<T extends GlobalState>(
|
export function getRequestInputInvoice<T extends GlobalState>(
|
||||||
@ -257,6 +260,7 @@ export function getRequestInputSavedStarGift<T extends GlobalState>(
|
|||||||
|
|
||||||
export function buildStarsTransactionCustomPeer(
|
export function buildStarsTransactionCustomPeer(
|
||||||
peer: Exclude<ApiStarsTransactionPeer, ApiStarsTransactionPeerPeer>,
|
peer: Exclude<ApiStarsTransactionPeer, ApiStarsTransactionPeerPeer>,
|
||||||
|
isForTon?: boolean,
|
||||||
): CustomPeer {
|
): CustomPeer {
|
||||||
if (peer.type === 'appStore') {
|
if (peer.type === 'appStore') {
|
||||||
return {
|
return {
|
||||||
@ -279,8 +283,17 @@ export function buildStarsTransactionCustomPeer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (peer.type === 'fragment') {
|
if (peer.type === 'fragment') {
|
||||||
|
if (isForTon) {
|
||||||
|
return {
|
||||||
|
avatarIcon: 'fragment',
|
||||||
|
isCustomPeer: true,
|
||||||
|
titleKey: 'Stars.Gift.Received.Title',
|
||||||
|
subtitleKey: 'Stars.Intro.Transaction.Gift.UnknownUser',
|
||||||
|
customPeerAvatarColor: '#000000',
|
||||||
|
};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
avatarIcon: 'star',
|
avatarIcon: 'fragment',
|
||||||
isCustomPeer: true,
|
isCustomPeer: true,
|
||||||
titleKey: 'Stars.Intro.Transaction.FragmentTopUp.Title',
|
titleKey: 'Stars.Intro.Transaction.FragmentTopUp.Title',
|
||||||
subtitleKey: 'Stars.Intro.Transaction.FragmentTopUp.Subtitle',
|
subtitleKey: 'Stars.Intro.Transaction.FragmentTopUp.Subtitle',
|
||||||
@ -328,13 +341,29 @@ export function buildStarsTransactionCustomPeer(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatStarsTransactionAmount(lang: LangFn, starsAmount: ApiStarsAmount) {
|
export function formatStarsTransactionAmount(lang: LangFn, currencyAmount: ApiTypeCurrencyAmount) {
|
||||||
const amount = starsAmount.amount + starsAmount.nanos / 1e9;
|
if (currencyAmount.currency === STARS_CURRENCY_CODE) {
|
||||||
if (amount < 0) {
|
const amount = currencyAmount.amount + currencyAmount.nanos / 1e9;
|
||||||
return `- ${lang.number(Math.abs(amount))}`;
|
if (amount < 0) {
|
||||||
|
return `- ${lang.number(Math.abs(amount))}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `+ ${lang.number(amount)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `+ ${lang.number(amount)}`;
|
if (currencyAmount.currency === TON_CURRENCY_CODE) {
|
||||||
|
const amount = convertCurrencyFromBaseUnit(currencyAmount.amount, currencyAmount.currency);
|
||||||
|
const absAmount = Math.abs(amount);
|
||||||
|
const tonText = lang('TonAmountText', { amount: absAmount }, { pluralValue: absAmount });
|
||||||
|
|
||||||
|
if (amount < 0) {
|
||||||
|
return `- ${tonText}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `+ ${tonText}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatStarsAmount(lang: LangFn, starsAmount: ApiStarsAmount) {
|
export function formatStarsAmount(lang: LangFn, starsAmount: ApiStarsAmount) {
|
||||||
@ -344,24 +373,45 @@ export function formatStarsAmount(lang: LangFn, starsAmount: ApiStarsAmount) {
|
|||||||
export function getStarsTransactionFromGift(message: ApiMessage): ApiStarsTransaction | undefined {
|
export function getStarsTransactionFromGift(message: ApiMessage): ApiStarsTransaction | undefined {
|
||||||
const { action } = message.content;
|
const { action } = message.content;
|
||||||
|
|
||||||
if (action?.type !== 'giftStars') return undefined;
|
if (action?.type === 'giftStars') {
|
||||||
|
const { transactionId, stars } = action;
|
||||||
|
|
||||||
const { transactionId, stars } = action;
|
return {
|
||||||
|
id: transactionId,
|
||||||
|
amount: {
|
||||||
|
currency: STARS_CURRENCY_CODE,
|
||||||
|
amount: stars,
|
||||||
|
nanos: 0,
|
||||||
|
},
|
||||||
|
peer: {
|
||||||
|
type: 'peer',
|
||||||
|
id: message.isOutgoing ? message.chatId : (message.senderId || message.chatId),
|
||||||
|
},
|
||||||
|
date: message.date,
|
||||||
|
isGift: true,
|
||||||
|
isMyGift: message.isOutgoing || undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
if (action?.type === 'giftTon') {
|
||||||
id: transactionId,
|
const { transactionId, cryptoAmount } = action;
|
||||||
stars: {
|
|
||||||
amount: stars,
|
return {
|
||||||
nanos: 0,
|
id: transactionId,
|
||||||
},
|
amount: {
|
||||||
peer: {
|
currency: TON_CURRENCY_CODE,
|
||||||
type: 'peer',
|
amount: cryptoAmount,
|
||||||
id: message.isOutgoing ? message.chatId : (message.senderId || message.chatId),
|
},
|
||||||
},
|
peer: {
|
||||||
date: message.date,
|
type: 'fragment',
|
||||||
isGift: true,
|
},
|
||||||
isMyGift: message.isOutgoing || undefined,
|
date: message.date,
|
||||||
};
|
isGift: true,
|
||||||
|
isMyGift: message.isOutgoing || undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPrizeStarsTransactionFromGiveaway(message: ApiMessage): ApiStarsTransaction | undefined {
|
export function getPrizeStarsTransactionFromGiveaway(message: ApiMessage): ApiStarsTransaction | undefined {
|
||||||
@ -373,7 +423,8 @@ export function getPrizeStarsTransactionFromGiveaway(message: ApiMessage): ApiSt
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
id: transactionId,
|
id: transactionId,
|
||||||
stars: {
|
amount: {
|
||||||
|
currency: STARS_CURRENCY_CODE,
|
||||||
amount: stars,
|
amount: stars,
|
||||||
nanos: 0,
|
nanos: 0,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import type {
|
import type {
|
||||||
ApiReceiptRegular,
|
ApiReceiptRegular,
|
||||||
ApiReceiptStars,
|
ApiReceiptStars,
|
||||||
ApiStarsAmount,
|
|
||||||
ApiStarsSubscription,
|
ApiStarsSubscription,
|
||||||
ApiStarsTransaction,
|
ApiStarsTransaction,
|
||||||
|
ApiTypeCurrencyAmount,
|
||||||
} from '../../api/types';
|
} from '../../api/types';
|
||||||
import type {
|
import type {
|
||||||
PaymentStep,
|
PaymentStep,
|
||||||
@ -15,6 +15,7 @@ import type {
|
|||||||
GlobalState, TabArgs, TabState,
|
GlobalState, TabArgs, TabState,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
|
import { STARS_CURRENCY_CODE, TON_CURRENCY_CODE } from '../../config';
|
||||||
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
||||||
import { selectStarsPayment, selectTabState } from '../selectors';
|
import { selectStarsPayment, selectTabState } from '../selectors';
|
||||||
import { updateTabState } from './tabs';
|
import { updateTabState } from './tabs';
|
||||||
@ -137,15 +138,29 @@ export function closeInvoice<T extends GlobalState>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function updateStarsBalance<T extends GlobalState>(
|
export function updateStarsBalance<T extends GlobalState>(
|
||||||
global: T, balance: ApiStarsAmount,
|
global: T, balance: ApiTypeCurrencyAmount,
|
||||||
): T {
|
): T {
|
||||||
return {
|
if (balance.currency === STARS_CURRENCY_CODE) {
|
||||||
...global,
|
return {
|
||||||
stars: {
|
...global,
|
||||||
...global.stars,
|
stars: {
|
||||||
balance,
|
...global.stars,
|
||||||
},
|
balance,
|
||||||
};
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (balance.currency === TON_CURRENCY_CODE) {
|
||||||
|
return {
|
||||||
|
...global,
|
||||||
|
ton: {
|
||||||
|
...global.ton,
|
||||||
|
balance,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return global;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function appendStarsTransactions<T extends GlobalState>(
|
export function appendStarsTransactions<T extends GlobalState>(
|
||||||
@ -153,7 +168,31 @@ export function appendStarsTransactions<T extends GlobalState>(
|
|||||||
type: StarsTransactionType,
|
type: StarsTransactionType,
|
||||||
transactions: ApiStarsTransaction[],
|
transactions: ApiStarsTransaction[],
|
||||||
nextOffset?: string,
|
nextOffset?: string,
|
||||||
|
isTon?: boolean,
|
||||||
): T {
|
): T {
|
||||||
|
if (isTon) {
|
||||||
|
const history = global.ton?.history;
|
||||||
|
if (!history) {
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newTypeObject = {
|
||||||
|
transactions: (history[type]?.transactions || []).concat(transactions),
|
||||||
|
nextOffset,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...global,
|
||||||
|
ton: {
|
||||||
|
...global.ton,
|
||||||
|
history: {
|
||||||
|
...history,
|
||||||
|
[type]: newTypeObject,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const history = global.stars?.history;
|
const history = global.stars?.history;
|
||||||
if (!history) {
|
if (!history) {
|
||||||
return global;
|
return global;
|
||||||
@ -238,7 +277,8 @@ export function openStarsTransactionFromReceipt<T extends GlobalState>(
|
|||||||
type: 'peer',
|
type: 'peer',
|
||||||
id: receipt.botId,
|
id: receipt.botId,
|
||||||
},
|
},
|
||||||
stars: {
|
amount: {
|
||||||
|
currency: STARS_CURRENCY_CODE,
|
||||||
amount: receipt.totalAmount,
|
amount: receipt.totalAmount,
|
||||||
nanos: 0,
|
nanos: 0,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import type { ApiSticker, ApiStickerSet, ApiStickerSetInfo } from '../../api/types';
|
import type { ApiSticker, ApiStickerSet, ApiStickerSetInfo } from '../../api/types';
|
||||||
import type { GlobalState, TabArgs } from '../types';
|
import type { GlobalState, TabArgs } from '../types';
|
||||||
|
|
||||||
import { RESTRICTED_EMOJI_SET_ID } from '../../config';
|
import { RESTRICTED_EMOJI_SET_ID, TON_CURRENCY_CODE } from '../../config';
|
||||||
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
||||||
|
import { convertCurrencyFromBaseUnit } from '../../util/formatCurrency';
|
||||||
import { selectTabState } from './tabs';
|
import { selectTabState } from './tabs';
|
||||||
import { selectIsCurrentUserPremium } from './users';
|
import { selectIsCurrentUserPremium } from './users';
|
||||||
|
|
||||||
@ -21,6 +22,12 @@ const STAR_EMOTICON: Record<number, string> = {
|
|||||||
5000: `${4}\u{FE0F}\u20E3`,
|
5000: `${4}\u{FE0F}\u20E3`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const TON_EMOTICON: Record<number, string> = {
|
||||||
|
1: `${1}\u{FE0F}\u20E3`,
|
||||||
|
10: `${2}\u{FE0F}\u20E3`,
|
||||||
|
50: `${3}\u{FE0F}\u20E3`,
|
||||||
|
};
|
||||||
|
|
||||||
export function selectIsStickerFavorite<T extends GlobalState>(global: T, sticker: ApiSticker) {
|
export function selectIsStickerFavorite<T extends GlobalState>(global: T, sticker: ApiSticker) {
|
||||||
const { stickers } = global.stickers.favorite;
|
const { stickers } = global.stickers.favorite;
|
||||||
return stickers && stickers.some(({ id }) => id === sticker.id);
|
return stickers && stickers.some(({ id }) => id === sticker.id);
|
||||||
@ -194,3 +201,20 @@ export function selectGiftStickerForStars<T extends GlobalState>(global: T, star
|
|||||||
|
|
||||||
return stickers.find((sticker) => sticker.emoji === emoji) || stickers[0];
|
return stickers.find((sticker) => sticker.emoji === emoji) || stickers[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function selectGiftStickerForTon<T extends GlobalState>(global: T, amount?: number) {
|
||||||
|
const stickers = global.tonGifts?.stickers;
|
||||||
|
if (!stickers || !amount) return undefined;
|
||||||
|
const convertedAmount = convertCurrencyFromBaseUnit(amount, TON_CURRENCY_CODE);
|
||||||
|
|
||||||
|
let emoji;
|
||||||
|
if (convertedAmount < 10) {
|
||||||
|
emoji = TON_EMOTICON[1];
|
||||||
|
} else if (convertedAmount < 50) {
|
||||||
|
emoji = TON_EMOTICON[10];
|
||||||
|
} else {
|
||||||
|
emoji = TON_EMOTICON[50];
|
||||||
|
}
|
||||||
|
|
||||||
|
return stickers.find((sticker) => sticker.emoji === emoji) || stickers[0];
|
||||||
|
}
|
||||||
|
|||||||
@ -49,6 +49,7 @@ import type {
|
|||||||
ApiStickerSetInfo,
|
ApiStickerSetInfo,
|
||||||
ApiThemeParameters,
|
ApiThemeParameters,
|
||||||
ApiTodoItem,
|
ApiTodoItem,
|
||||||
|
ApiTypeCurrencyAmount,
|
||||||
ApiTypePrepaidGiveaway,
|
ApiTypePrepaidGiveaway,
|
||||||
ApiUpdate,
|
ApiUpdate,
|
||||||
ApiUser,
|
ApiUser,
|
||||||
@ -1278,6 +1279,7 @@ export interface ActionPayloads {
|
|||||||
loadStarStatus: undefined;
|
loadStarStatus: undefined;
|
||||||
loadStarsTransactions: {
|
loadStarsTransactions: {
|
||||||
type: StarsTransactionType;
|
type: StarsTransactionType;
|
||||||
|
isTon?: boolean;
|
||||||
};
|
};
|
||||||
loadStarsSubscriptions: undefined;
|
loadStarsSubscriptions: undefined;
|
||||||
changeStarsSubscription: {
|
changeStarsSubscription: {
|
||||||
@ -1302,6 +1304,7 @@ export interface ActionPayloads {
|
|||||||
purpose?: string;
|
purpose?: string;
|
||||||
};
|
};
|
||||||
shouldIgnoreBalance?: boolean;
|
shouldIgnoreBalance?: boolean;
|
||||||
|
currency?: ApiTypeCurrencyAmount['currency'];
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
closeStarsBalanceModal: WithTabId | undefined;
|
closeStarsBalanceModal: WithTabId | undefined;
|
||||||
|
|
||||||
@ -2483,6 +2486,7 @@ export interface ActionPayloads {
|
|||||||
};
|
};
|
||||||
|
|
||||||
loadPremiumGifts: undefined;
|
loadPremiumGifts: undefined;
|
||||||
|
loadTonGifts: undefined;
|
||||||
loadStarGifts: undefined;
|
loadStarGifts: undefined;
|
||||||
updateResaleGiftsFilter: {
|
updateResaleGiftsFilter: {
|
||||||
filter: ResaleGiftsFilterOptions;
|
filter: ResaleGiftsFilterOptions;
|
||||||
|
|||||||
@ -37,6 +37,7 @@ import type {
|
|||||||
ApiSticker,
|
ApiSticker,
|
||||||
ApiStickerSet,
|
ApiStickerSet,
|
||||||
ApiTimezone,
|
ApiTimezone,
|
||||||
|
ApiTonAmount,
|
||||||
ApiTranscription,
|
ApiTranscription,
|
||||||
ApiUpdateAuthorizationStateType,
|
ApiUpdateAuthorizationStateType,
|
||||||
ApiUpdateConnectionStateType,
|
ApiUpdateConnectionStateType,
|
||||||
@ -367,6 +368,7 @@ export type GlobalState = {
|
|||||||
defaultTopicIconsId?: string;
|
defaultTopicIconsId?: string;
|
||||||
defaultStatusIconsId?: string;
|
defaultStatusIconsId?: string;
|
||||||
premiumGifts?: ApiStickerSet;
|
premiumGifts?: ApiStickerSet;
|
||||||
|
tonGifts?: ApiStickerSet;
|
||||||
emojiKeywords: Record<string, EmojiKeywords | undefined>;
|
emojiKeywords: Record<string, EmojiKeywords | undefined>;
|
||||||
|
|
||||||
collectibleEmojiStatuses?: {
|
collectibleEmojiStatuses?: {
|
||||||
@ -452,6 +454,10 @@ export type GlobalState = {
|
|||||||
history: StarsTransactionHistory;
|
history: StarsTransactionHistory;
|
||||||
subscriptions?: StarsSubscriptions;
|
subscriptions?: StarsSubscriptions;
|
||||||
};
|
};
|
||||||
|
ton?: {
|
||||||
|
balance: ApiTonAmount;
|
||||||
|
history: StarsTransactionHistory;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RequiredGlobalState = GlobalState & { _: never };
|
export type RequiredGlobalState = GlobalState & { _: never };
|
||||||
|
|||||||
@ -45,6 +45,7 @@ import type {
|
|||||||
ApiStarsTransaction,
|
ApiStarsTransaction,
|
||||||
ApiStarTopupOption,
|
ApiStarTopupOption,
|
||||||
ApiSticker,
|
ApiSticker,
|
||||||
|
ApiTypeCurrencyAmount,
|
||||||
ApiTypePrepaidGiveaway,
|
ApiTypePrepaidGiveaway,
|
||||||
ApiTypeStoryView,
|
ApiTypeStoryView,
|
||||||
ApiUser,
|
ApiUser,
|
||||||
@ -795,6 +796,7 @@ export type TabState = {
|
|||||||
balanceNeeded: number;
|
balanceNeeded: number;
|
||||||
purpose?: string;
|
purpose?: string;
|
||||||
};
|
};
|
||||||
|
currency?: ApiTypeCurrencyAmount['currency'];
|
||||||
};
|
};
|
||||||
|
|
||||||
giftInfoModal?: {
|
giftInfoModal?: {
|
||||||
|
|||||||
@ -318,10 +318,12 @@ body:not(.is-ios) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Increase specificity to override the default icon style
|
// Increase specificity to override the default icon style
|
||||||
|
.ton-amount-icon.ton-amount-icon,
|
||||||
.star-amount-icon.star-amount-icon {
|
.star-amount-icon.star-amount-icon {
|
||||||
margin-inline-start: 0.375em; // Prevent sticking to the text without using `white-space: pre`
|
margin-inline-start: 0.375em; // Prevent sticking to the text without using `white-space: pre`
|
||||||
margin-inline-end: 0.2em; // Prevent sticking to the text without using `white-space: pre`
|
margin-inline-end: 0.2em; // Prevent sticking to the text without using `white-space: pre`
|
||||||
line-height: inherit; // Vertical centring
|
line-height: inherit; // Vertical centring
|
||||||
|
vertical-align: text-bottom; // As regular text
|
||||||
}
|
}
|
||||||
|
|
||||||
.shared-canvas-container {
|
.shared-canvas-container {
|
||||||
|
|||||||
25
src/types/language.d.ts
vendored
25
src/types/language.d.ts
vendored
@ -576,6 +576,7 @@ export interface LangPair {
|
|||||||
'MenuStickers': undefined;
|
'MenuStickers': undefined;
|
||||||
'MenuAnimations': undefined;
|
'MenuAnimations': undefined;
|
||||||
'MenuStars': undefined;
|
'MenuStars': undefined;
|
||||||
|
'MenuTon': undefined;
|
||||||
'MenuSendGift': undefined;
|
'MenuSendGift': undefined;
|
||||||
'MenuTelegramFaq': undefined;
|
'MenuTelegramFaq': undefined;
|
||||||
'MenuPrivacyPolicy': undefined;
|
'MenuPrivacyPolicy': undefined;
|
||||||
@ -1549,9 +1550,13 @@ export interface LangPair {
|
|||||||
'TitleTime': undefined;
|
'TitleTime': undefined;
|
||||||
'TitleSuggestMessage': undefined;
|
'TitleSuggestMessage': undefined;
|
||||||
'TitleSuggestedChanges': undefined;
|
'TitleSuggestedChanges': undefined;
|
||||||
|
'SuggestMessageNoPrice': undefined;
|
||||||
'EnterPriceInStars': undefined;
|
'EnterPriceInStars': undefined;
|
||||||
|
'EnterPriceInTon': undefined;
|
||||||
|
'SuggestMessagePriceDescriptionStars': undefined;
|
||||||
|
'SuggestMessagePriceDescriptionTon': undefined;
|
||||||
'SuggestMessageDateTimeHint': undefined;
|
'SuggestMessageDateTimeHint': undefined;
|
||||||
'TitleAnytime': undefined;
|
'SuggestMessageAnytime': undefined;
|
||||||
'ButtonOfferFree': undefined;
|
'ButtonOfferFree': undefined;
|
||||||
'ButtonUpdateTerms': undefined;
|
'ButtonUpdateTerms': undefined;
|
||||||
'InputPlaceholderPrice': undefined;
|
'InputPlaceholderPrice': undefined;
|
||||||
@ -1563,6 +1568,7 @@ export interface LangPair {
|
|||||||
'SuggestedPostRejectedNotification': undefined;
|
'SuggestedPostRejectedNotification': undefined;
|
||||||
'SuggestedPostAgreementReached': undefined;
|
'SuggestedPostAgreementReached': undefined;
|
||||||
'CurrencyStars': undefined;
|
'CurrencyStars': undefined;
|
||||||
|
'CurrencyTon': undefined;
|
||||||
'DeclineReasonPlaceholder': undefined;
|
'DeclineReasonPlaceholder': undefined;
|
||||||
'SuggestedPostRejectedYou': undefined;
|
'SuggestedPostRejectedYou': undefined;
|
||||||
'SuggestedPostRejectedWithReasonYou': undefined;
|
'SuggestedPostRejectedWithReasonYou': undefined;
|
||||||
@ -1590,6 +1596,10 @@ export interface LangPair {
|
|||||||
'ToDoListErrorChooseTitle': undefined;
|
'ToDoListErrorChooseTitle': undefined;
|
||||||
'ToDoListErrorChooseTasks': undefined;
|
'ToDoListErrorChooseTasks': undefined;
|
||||||
'PremiumPreviewTodo': undefined;
|
'PremiumPreviewTodo': undefined;
|
||||||
|
'DescriptionAboutTon': undefined;
|
||||||
|
'ButtonTopUpViaFragment': undefined;
|
||||||
|
'TonModalHint': undefined;
|
||||||
|
'TonGiftReceived': undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LangPairWithVariables<V = LangVariable> {
|
export interface LangPairWithVariables<V = LangVariable> {
|
||||||
@ -2425,6 +2435,13 @@ export interface LangPairWithVariables<V = LangVariable> {
|
|||||||
'from': V;
|
'from': V;
|
||||||
'amount': V;
|
'amount': V;
|
||||||
};
|
};
|
||||||
|
'TonAmount': {
|
||||||
|
'amount': V;
|
||||||
|
};
|
||||||
|
'ActionGiftCostCrypto': {
|
||||||
|
'cryptoPrice': V;
|
||||||
|
'price': V;
|
||||||
|
};
|
||||||
'ActionPaymentRefunded': {
|
'ActionPaymentRefunded': {
|
||||||
'peer': V;
|
'peer': V;
|
||||||
'amount': V;
|
'amount': V;
|
||||||
@ -2584,9 +2601,6 @@ export interface LangPairWithVariables<V = LangVariable> {
|
|||||||
'user': V;
|
'user': V;
|
||||||
'changes': V;
|
'changes': V;
|
||||||
};
|
};
|
||||||
'SuggestMessagePriceDescription': {
|
|
||||||
'currency': V;
|
|
||||||
};
|
|
||||||
'SuggestMessageTimeDescription': {
|
'SuggestMessageTimeDescription': {
|
||||||
'hint': V;
|
'hint': V;
|
||||||
'duration': V;
|
'duration': V;
|
||||||
@ -3035,6 +3049,9 @@ export interface LangPairPluralWithVariables<V = LangVariable> {
|
|||||||
'ActionGiftStarsTitle': {
|
'ActionGiftStarsTitle': {
|
||||||
'amount': V;
|
'amount': V;
|
||||||
};
|
};
|
||||||
|
'TonAmountText': {
|
||||||
|
'amount': V;
|
||||||
|
};
|
||||||
'ActionBoostApplyYou': {
|
'ActionBoostApplyYou': {
|
||||||
'count': V;
|
'count': V;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,8 +2,24 @@ import { type TeactNode } from '../lib/teact/teact';
|
|||||||
|
|
||||||
import type { LangFn } from './localization';
|
import type { LangFn } from './localization';
|
||||||
|
|
||||||
import { STARS_CURRENCY_CODE } from '../config';
|
import { STARS_CURRENCY_CODE, TON_CURRENCY_CODE } from '../config';
|
||||||
import { formatStarsAsIcon } from './localization/format';
|
import { formatStarsAsIcon, formatTonAsIcon } from './localization/format';
|
||||||
|
|
||||||
|
export function convertCurrencyFromBaseUnit(amount: number, currency: string) {
|
||||||
|
return amount / 10 ** getCurrencyExp(currency);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertCurrencyToBaseUnit(amount: number, currency: string) {
|
||||||
|
return amount * 10 ** getCurrencyExp(currency);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertTonFromNanos(nanos: number): number {
|
||||||
|
return convertCurrencyFromBaseUnit(nanos, TON_CURRENCY_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertTonToNanos(ton: number): number {
|
||||||
|
return convertCurrencyToBaseUnit(ton, TON_CURRENCY_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
export function formatCurrency(
|
export function formatCurrency(
|
||||||
lang: LangFn,
|
lang: LangFn,
|
||||||
@ -15,15 +31,24 @@ export function formatCurrency(
|
|||||||
asFontIcon?: boolean;
|
asFontIcon?: boolean;
|
||||||
},
|
},
|
||||||
): TeactNode {
|
): TeactNode {
|
||||||
const price = totalPrice / 10 ** getCurrencyExp(currency);
|
const price = convertCurrencyFromBaseUnit(totalPrice, currency);
|
||||||
|
|
||||||
if (currency === STARS_CURRENCY_CODE) {
|
if (currency === STARS_CURRENCY_CODE) {
|
||||||
return formatStarsAsIcon(lang, price, { asFont: options?.asFontIcon, className: options?.iconClassName });
|
return formatStarsAsIcon(lang, price, { asFont: options?.asFontIcon, className: options?.iconClassName });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currency === TON_CURRENCY_CODE) {
|
||||||
|
return formatTonAsIcon(lang, price, { asFont: options?.asFontIcon, className: options?.iconClassName });
|
||||||
|
}
|
||||||
|
|
||||||
return formatCurrencyAsString(totalPrice, currency, lang.code, options);
|
return formatCurrencyAsString(totalPrice, currency, lang.code, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function convertTonToUsd(amount: number, usdRate: number): number {
|
||||||
|
const tonInRegularUnits = convertTonFromNanos(amount);
|
||||||
|
return tonInRegularUnits * usdRate * 100;
|
||||||
|
}
|
||||||
|
|
||||||
export function formatCurrencyAsString(
|
export function formatCurrencyAsString(
|
||||||
totalPrice: number,
|
totalPrice: number,
|
||||||
currency: string,
|
currency: string,
|
||||||
@ -32,7 +57,7 @@ export function formatCurrencyAsString(
|
|||||||
shouldOmitFractions?: boolean;
|
shouldOmitFractions?: boolean;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
const price = totalPrice / 10 ** getCurrencyExp(currency);
|
const price = convertCurrencyFromBaseUnit(totalPrice, currency);
|
||||||
|
|
||||||
if ((options?.shouldOmitFractions || currency === STARS_CURRENCY_CODE) && Number.isInteger(price)) {
|
if ((options?.shouldOmitFractions || currency === STARS_CURRENCY_CODE) && Number.isInteger(price)) {
|
||||||
return new Intl.NumberFormat(locale, {
|
return new Intl.NumberFormat(locale, {
|
||||||
@ -43,6 +68,15 @@ export function formatCurrencyAsString(
|
|||||||
}).format(price);
|
}).format(price);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currency === TON_CURRENCY_CODE) {
|
||||||
|
return new Intl.NumberFormat(locale, {
|
||||||
|
style: 'currency',
|
||||||
|
currency,
|
||||||
|
minimumFractionDigits: 0,
|
||||||
|
maximumFractionDigits: 10,
|
||||||
|
}).format(price);
|
||||||
|
}
|
||||||
|
|
||||||
return new Intl.NumberFormat(locale, {
|
return new Intl.NumberFormat(locale, {
|
||||||
style: 'currency',
|
style: 'currency',
|
||||||
currency,
|
currency,
|
||||||
@ -50,7 +84,7 @@ export function formatCurrencyAsString(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getCurrencyExp(currency: string) {
|
function getCurrencyExp(currency: string) {
|
||||||
if (currency === 'TON') {
|
if (currency === TON_CURRENCY_CODE) {
|
||||||
return 9;
|
return 9;
|
||||||
}
|
}
|
||||||
if (currency === 'CLF') {
|
if (currency === 'CLF') {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import type { LangFn } from './types';
|
import type { LangFn } from './types';
|
||||||
|
|
||||||
import { STARS_ICON_PLACEHOLDER } from '../../config';
|
import { STARS_ICON_PLACEHOLDER } from '../../config';
|
||||||
|
import { convertCurrencyFromBaseUnit } from '../../util/formatCurrency';
|
||||||
import buildClassName from '../buildClassName';
|
import buildClassName from '../buildClassName';
|
||||||
|
|
||||||
import Icon from '../../components/common/icons/Icon';
|
import Icon from '../../components/common/icons/Icon';
|
||||||
@ -10,6 +11,37 @@ export function formatStarsAsText(lang: LangFn, amount: number) {
|
|||||||
return lang('StarsAmountText', { amount }, { pluralValue: amount });
|
return lang('StarsAmountText', { amount }, { pluralValue: amount });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatTonAsText(lang: LangFn, amount: number) {
|
||||||
|
return lang('TonAmountText', { amount: lang.preciseNumber(amount) }, { pluralValue: amount });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatTonAsIcon(lang: LangFn, amount: number | string, options?: {
|
||||||
|
asFont?: boolean; className?: string; containerClassName?: string; shouldConvertFromNanos?: boolean; }) {
|
||||||
|
const { className, containerClassName, shouldConvertFromNanos } = options || {};
|
||||||
|
const formattedAmount = shouldConvertFromNanos ? convertCurrencyFromBaseUnit(Number(amount), 'TON') : amount;
|
||||||
|
const icon = <Icon name="toncoin" className={buildClassName('ton-amount-icon', className)} />;
|
||||||
|
|
||||||
|
if (containerClassName) {
|
||||||
|
return (
|
||||||
|
<span className={containerClassName}>
|
||||||
|
{lang('TonAmount', { amount: formattedAmount }, {
|
||||||
|
withNodes: true,
|
||||||
|
specialReplacement: {
|
||||||
|
'💎': icon,
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lang('TonAmount', { amount: formattedAmount }, {
|
||||||
|
withNodes: true,
|
||||||
|
specialReplacement: {
|
||||||
|
'💎': icon,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function formatStarsAsIcon(lang: LangFn, amount: number | string, options?: {
|
export function formatStarsAsIcon(lang: LangFn, amount: number | string, options?: {
|
||||||
asFont?: boolean; className?: string; containerClassName?: string; }) {
|
asFont?: boolean; className?: string; containerClassName?: string; }) {
|
||||||
const { asFont, className, containerClassName } = options || {};
|
const { asFont, className, containerClassName } = options || {};
|
||||||
|
|||||||
@ -169,6 +169,10 @@ function createFormatters() {
|
|||||||
conjunction: createListFormat(langCode, 'conjunction'),
|
conjunction: createListFormat(langCode, 'conjunction'),
|
||||||
disjunction: createListFormat(langCode, 'disjunction'),
|
disjunction: createListFormat(langCode, 'disjunction'),
|
||||||
number: new Intl.NumberFormat(langCode),
|
number: new Intl.NumberFormat(langCode),
|
||||||
|
preciseNumber: new Intl.NumberFormat(langCode, {
|
||||||
|
minimumFractionDigits: 0,
|
||||||
|
maximumFractionDigits: 10,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
@ -179,6 +183,10 @@ function createFormatters() {
|
|||||||
conjunction: createListFormat(FORMATTERS_FALLBACK_LANG, 'conjunction'),
|
conjunction: createListFormat(FORMATTERS_FALLBACK_LANG, 'conjunction'),
|
||||||
disjunction: createListFormat(FORMATTERS_FALLBACK_LANG, 'disjunction'),
|
disjunction: createListFormat(FORMATTERS_FALLBACK_LANG, 'disjunction'),
|
||||||
number: new Intl.NumberFormat(FORMATTERS_FALLBACK_LANG),
|
number: new Intl.NumberFormat(FORMATTERS_FALLBACK_LANG),
|
||||||
|
preciseNumber: new Intl.NumberFormat(FORMATTERS_FALLBACK_LANG, {
|
||||||
|
minimumFractionDigits: 0,
|
||||||
|
maximumFractionDigits: 10,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -325,6 +333,7 @@ function createTranslationFn(): LangFn {
|
|||||||
fn.conjunction = (list: string[]) => formatters?.conjunction.format(list) || list.join(', ');
|
fn.conjunction = (list: string[]) => formatters?.conjunction.format(list) || list.join(', ');
|
||||||
fn.disjunction = (list: string[]) => formatters?.disjunction.format(list) || list.join(', ');
|
fn.disjunction = (list: string[]) => formatters?.disjunction.format(list) || list.join(', ');
|
||||||
fn.number = (value: number) => formatters?.number.format(value) || String(value);
|
fn.number = (value: number) => formatters?.number.format(value) || String(value);
|
||||||
|
fn.preciseNumber = (value: number) => formatters?.preciseNumber.format(value) || String(value);
|
||||||
fn.internalFormatters = formatters!;
|
fn.internalFormatters = formatters!;
|
||||||
fn.languageInfo = language!;
|
fn.languageInfo = language!;
|
||||||
return fn;
|
return fn;
|
||||||
|
|||||||
@ -155,6 +155,7 @@ export type LangFn = {
|
|||||||
conjunction: (list: string[]) => string;
|
conjunction: (list: string[]) => string;
|
||||||
disjunction: (list: string[]) => string;
|
disjunction: (list: string[]) => string;
|
||||||
number: (value: number) => string;
|
number: (value: number) => string;
|
||||||
|
preciseNumber: (value: number) => string;
|
||||||
internalFormatters: LangFormatters;
|
internalFormatters: LangFormatters;
|
||||||
isRtl?: boolean;
|
isRtl?: boolean;
|
||||||
rawCode: string;
|
rawCode: string;
|
||||||
@ -171,6 +172,7 @@ export type LangFormatters = {
|
|||||||
conjunction: ListFormat;
|
conjunction: ListFormat;
|
||||||
disjunction: ListFormat;
|
disjunction: ListFormat;
|
||||||
number: Intl.NumberFormat;
|
number: Intl.NumberFormat;
|
||||||
|
preciseNumber: Intl.NumberFormat;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* GUARDS */
|
/* GUARDS */
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user