Support unique gifts (#5394)
This commit is contained in:
parent
9e6aa47013
commit
180aef57bf
@ -374,8 +374,7 @@ function buildApiMessageActionStarGift(action: GramJs.MessageActionStarGift) : A
|
|||||||
isNameHidden: Boolean(nameHidden),
|
isNameHidden: Boolean(nameHidden),
|
||||||
isSaved: Boolean(saved),
|
isSaved: Boolean(saved),
|
||||||
isConverted: Boolean(converted),
|
isConverted: Boolean(converted),
|
||||||
// ToDo: Use `!` temporarily to support layer 196
|
gift: buildApiStarGift(gift),
|
||||||
gift: buildApiStarGift(gift)!,
|
|
||||||
message: message && buildApiFormattedText(message),
|
message: message && buildApiFormattedText(message),
|
||||||
starsToConvert: convertStars?.toJSNumber(),
|
starsToConvert: convertStars?.toJSNumber(),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import type {
|
|||||||
ApiPrepaidStarsGiveaway,
|
ApiPrepaidStarsGiveaway,
|
||||||
ApiReceipt,
|
ApiReceipt,
|
||||||
ApiStarGift,
|
ApiStarGift,
|
||||||
|
ApiStarGiftAttribute,
|
||||||
ApiStarGiveawayOption,
|
ApiStarGiveawayOption,
|
||||||
ApiStarsAmount,
|
ApiStarsAmount,
|
||||||
ApiStarsGiveawayWinnerOption,
|
ApiStarsGiveawayWinnerOption,
|
||||||
@ -31,13 +32,15 @@ import type {
|
|||||||
BoughtPaidMedia,
|
BoughtPaidMedia,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
import { addWebDocumentToLocalDb } from '../helpers';
|
import { numberToHexColor } from '../../../util/colors';
|
||||||
|
import { addDocumentToLocalDb, addWebDocumentToLocalDb } from '../helpers';
|
||||||
import { buildApiStarsSubscriptionPricing } from './chats';
|
import { buildApiStarsSubscriptionPricing } from './chats';
|
||||||
import { buildApiFormattedText, buildApiMessageEntity } from './common';
|
import { buildApiFormattedText, buildApiMessageEntity } from './common';
|
||||||
import { omitVirtualClassFields } from './helpers';
|
import { omitVirtualClassFields } from './helpers';
|
||||||
import { buildApiDocument, buildApiWebDocument, buildMessageMediaContent } from './messageContent';
|
import { buildApiDocument, buildApiWebDocument, buildMessageMediaContent } from './messageContent';
|
||||||
import { buildApiPeerId, getApiChatIdFromMtpPeer } from './peers';
|
import { buildApiPeerId, getApiChatIdFromMtpPeer } from './peers';
|
||||||
import { buildStatisticsPercentage } from './statistics';
|
import { buildStatisticsPercentage } from './statistics';
|
||||||
|
import { buildStickerFromDocument } from './symbols';
|
||||||
|
|
||||||
export function buildShippingOptions(shippingOptions: GramJs.ShippingOption[] | undefined) {
|
export function buildShippingOptions(shippingOptions: GramJs.ShippingOption[] | undefined) {
|
||||||
if (!shippingOptions) {
|
if (!shippingOptions) {
|
||||||
@ -536,7 +539,7 @@ export function buildApiStarsTransactionPeer(peer: GramJs.TypeStarsTransactionPe
|
|||||||
export function buildApiStarsTransaction(transaction: GramJs.StarsTransaction): ApiStarsTransaction {
|
export function buildApiStarsTransaction(transaction: GramJs.StarsTransaction): ApiStarsTransaction {
|
||||||
const {
|
const {
|
||||||
date, id, peer, stars, description, photo, title, refund, extendedMedia, failed, msgId, pending, gift, reaction,
|
date, id, peer, stars, description, photo, title, refund, extendedMedia, failed, msgId, pending, gift, reaction,
|
||||||
subscriptionPeriod, stargift, giveawayPostId, starrefCommissionPermille,
|
subscriptionPeriod, stargift, giveawayPostId, starrefCommissionPermille, stargiftUpgrade,
|
||||||
} = transaction;
|
} = transaction;
|
||||||
|
|
||||||
if (photo) {
|
if (photo) {
|
||||||
@ -547,7 +550,6 @@ export function buildApiStarsTransaction(transaction: GramJs.StarsTransaction):
|
|||||||
.filter(Boolean) as BoughtPaidMedia[];
|
.filter(Boolean) as BoughtPaidMedia[];
|
||||||
|
|
||||||
const starRefCommision = starrefCommissionPermille ? starrefCommissionPermille / 10 : undefined;
|
const starRefCommision = starrefCommissionPermille ? starrefCommissionPermille / 10 : undefined;
|
||||||
const supportedStarGift = (stargift instanceof GramJs.StarGift) ? stargift : undefined;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
@ -565,9 +567,10 @@ export function buildApiStarsTransaction(transaction: GramJs.StarsTransaction):
|
|||||||
extendedMedia: boughtExtendedMedia,
|
extendedMedia: boughtExtendedMedia,
|
||||||
subscriptionPeriod,
|
subscriptionPeriod,
|
||||||
isReaction: reaction,
|
isReaction: reaction,
|
||||||
starGift: supportedStarGift && buildApiStarGift(supportedStarGift),
|
starGift: stargift && buildApiStarGift(stargift),
|
||||||
giveawayPostId,
|
giveawayPostId,
|
||||||
starRefCommision,
|
starRefCommision,
|
||||||
|
isGiftUpgrade: stargiftUpgrade,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -610,18 +613,38 @@ export function buildApiStarTopupOption(option: GramJs.TypeStarsTopupOption): Ap
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildApiStarGift(startGift: GramJs.TypeStarGift): ApiStarGift | undefined {
|
export function buildApiStarGift(starGift: GramJs.TypeStarGift): ApiStarGift {
|
||||||
const isTypeSupported = startGift instanceof GramJs.StarGift;
|
if (starGift instanceof GramJs.StarGiftUnique) {
|
||||||
if (!isTypeSupported) return undefined;
|
const {
|
||||||
|
id, num, ownerId, title, attributes, availabilityIssued, availabilityTotal,
|
||||||
|
} = starGift;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'starGiftUnique',
|
||||||
|
id: id.toString(),
|
||||||
|
number: num,
|
||||||
|
ownerId: buildApiPeerId(ownerId, 'user'),
|
||||||
|
attributes: attributes.map(buildApiStarGiftAttribute).filter(Boolean),
|
||||||
|
title,
|
||||||
|
totalCount: availabilityTotal,
|
||||||
|
issuedCount: availabilityIssued,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
id, limited, sticker, stars, availabilityRemains, availabilityTotal, convertStars, firstSaleDate, lastSaleDate,
|
id, limited, stars, availabilityRemains, availabilityTotal, convertStars, firstSaleDate, lastSaleDate, soldOut,
|
||||||
soldOut,
|
birthday, upgradeStars,
|
||||||
} = startGift;
|
} = starGift;
|
||||||
|
|
||||||
|
addDocumentToLocalDb(starGift.sticker);
|
||||||
|
|
||||||
|
const sticker = buildStickerFromDocument(starGift.sticker)!;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
type: 'starGift',
|
||||||
id: id.toString(),
|
id: id.toString(),
|
||||||
isLimited: limited,
|
isLimited: limited,
|
||||||
stickerId: sticker.id.toString(),
|
sticker,
|
||||||
stars: stars.toJSNumber(),
|
stars: stars.toJSNumber(),
|
||||||
availabilityRemains,
|
availabilityRemains,
|
||||||
availabilityTotal,
|
availabilityTotal,
|
||||||
@ -629,17 +652,84 @@ export function buildApiStarGift(startGift: GramJs.TypeStarGift): ApiStarGift |
|
|||||||
firstSaleDate,
|
firstSaleDate,
|
||||||
lastSaleDate,
|
lastSaleDate,
|
||||||
isSoldOut: soldOut,
|
isSoldOut: soldOut,
|
||||||
|
isBirthday: birthday,
|
||||||
|
upgradeStars: upgradeStars?.toJSNumber(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildApiStarGiftAttribute(attribute: GramJs.TypeStarGiftAttribute): ApiStarGiftAttribute | undefined {
|
||||||
|
if (attribute instanceof GramJs.StarGiftAttributeModel) {
|
||||||
|
const sticker = buildStickerFromDocument(attribute.document);
|
||||||
|
if (!sticker) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
addDocumentToLocalDb(attribute.document);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'model',
|
||||||
|
name: attribute.name,
|
||||||
|
rarityPercent: attribute.rarityPermille / 10,
|
||||||
|
sticker,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attribute instanceof GramJs.StarGiftAttributePattern) {
|
||||||
|
const sticker = buildStickerFromDocument(attribute.document);
|
||||||
|
if (!sticker) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
addDocumentToLocalDb(attribute.document);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'pattern',
|
||||||
|
name: attribute.name,
|
||||||
|
rarityPercent: attribute.rarityPermille / 10,
|
||||||
|
sticker,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attribute instanceof GramJs.StarGiftAttributeBackdrop) {
|
||||||
|
const {
|
||||||
|
name, rarityPermille, centerColor, edgeColor, patternColor, textColor,
|
||||||
|
} = attribute;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'backdrop',
|
||||||
|
name,
|
||||||
|
rarityPercent: rarityPermille / 10,
|
||||||
|
centerColor: numberToHexColor(centerColor),
|
||||||
|
edgeColor: numberToHexColor(edgeColor),
|
||||||
|
patternColor: numberToHexColor(patternColor),
|
||||||
|
textColor: numberToHexColor(textColor),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attribute instanceof GramJs.StarGiftAttributeOriginalDetails) {
|
||||||
|
const {
|
||||||
|
date, recipientId, message, senderId,
|
||||||
|
} = attribute;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'originalDetails',
|
||||||
|
date,
|
||||||
|
recipientId: recipientId && buildApiPeerId(recipientId, 'user'),
|
||||||
|
message: message && buildApiFormattedText(message),
|
||||||
|
senderId: senderId && buildApiPeerId(senderId, 'user'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export function buildApiUserStarGift(userStarGift: GramJs.UserStarGift): ApiUserStarGift {
|
export function buildApiUserStarGift(userStarGift: GramJs.UserStarGift): ApiUserStarGift {
|
||||||
const {
|
const {
|
||||||
gift, date, convertStars, fromId, message, msgId, nameHidden, unsaved,
|
gift, date, convertStars, fromId, message, msgId, nameHidden, unsaved,
|
||||||
} = userStarGift;
|
} = userStarGift;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// ToDo: Use `!` temporarily to support layer 196
|
gift: buildApiStarGift(gift),
|
||||||
gift: buildApiStarGift(gift)!,
|
|
||||||
date,
|
date,
|
||||||
starsToConvert: convertStars?.toJSNumber(),
|
starsToConvert: convertStars?.toJSNumber(),
|
||||||
fromId: fromId && buildApiPeerId(fromId, 'user'),
|
fromId: fromId && buildApiPeerId(fromId, 'user'),
|
||||||
|
|||||||
@ -2,8 +2,12 @@ import BigInt from 'big-integer';
|
|||||||
import { Api as GramJs } from '../../../lib/gramjs';
|
import { Api as GramJs } from '../../../lib/gramjs';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ApiChat, ApiInputStorePaymentPurpose, ApiPeer, ApiRequestInputInvoice,
|
ApiChat,
|
||||||
ApiSticker, ApiThemeParameters,
|
ApiInputStorePaymentPurpose,
|
||||||
|
ApiPeer,
|
||||||
|
ApiRequestInputInvoice,
|
||||||
|
ApiStarGiftRegular,
|
||||||
|
ApiThemeParameters,
|
||||||
ApiUser,
|
ApiUser,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
@ -29,7 +33,6 @@ import {
|
|||||||
buildShippingOptions,
|
buildShippingOptions,
|
||||||
} from '../apiBuilders/payments';
|
} from '../apiBuilders/payments';
|
||||||
import { buildApiPeerId } from '../apiBuilders/peers';
|
import { buildApiPeerId } from '../apiBuilders/peers';
|
||||||
import { buildStickerFromDocument } from '../apiBuilders/symbols';
|
|
||||||
import {
|
import {
|
||||||
buildInputInvoice, buildInputPeer, buildInputStorePaymentPurpose, buildInputThemeParams, buildShippingInfo,
|
buildInputInvoice, buildInputPeer, buildInputStorePaymentPurpose, buildInputThemeParams, buildShippingInfo,
|
||||||
} from '../gramjsBuilders';
|
} from '../gramjsBuilders';
|
||||||
@ -430,22 +433,8 @@ export async function fetchStarGifts() {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const gifts = result.gifts.map(buildApiStarGift).filter(Boolean);
|
// Right now, only regular star gifts can be bought, but API are not specific
|
||||||
const stickers : Record<string, ApiSticker> = {};
|
return result.gifts.map(buildApiStarGift).filter((gift): gift is ApiStarGiftRegular => gift.type === 'starGift');
|
||||||
|
|
||||||
result.gifts.forEach((gift) => {
|
|
||||||
if (!(gift instanceof GramJs.StarGift)) return;
|
|
||||||
if (gift.sticker instanceof GramJs.Document) {
|
|
||||||
localDb.documents[String(gift.sticker.id)] = gift.sticker;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sticker = buildStickerFromDocument(gift.sticker);
|
|
||||||
if (sticker) {
|
|
||||||
stickers[sticker.id] = sticker;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return { gifts, stickers };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchUserStarGifts({
|
export async function fetchUserStarGifts({
|
||||||
@ -467,12 +456,10 @@ export async function fetchUserStarGifts({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const supportedGifts = result.gifts.filter(
|
const gifts = result.gifts.map(buildApiUserStarGift);
|
||||||
((gift) => gift instanceof GramJs.StarGift),
|
|
||||||
).map(buildApiUserStarGift);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
gifts: supportedGifts,
|
gifts,
|
||||||
nextOffset: result.nextOffset,
|
nextOffset: result.nextOffset,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -528,13 +515,10 @@ export async function fetchStarsStatus() {
|
|||||||
if (!result) {
|
if (!result) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const supportedHistory = result.history?.filter(
|
|
||||||
(transaction) => !(transaction.stargift instanceof GramJs.StarGiftUnique),
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
nextHistoryOffset: result.nextOffset,
|
nextHistoryOffset: result.nextOffset,
|
||||||
history: supportedHistory?.map(buildApiStarsTransaction),
|
history: result.history?.map(buildApiStarsTransaction),
|
||||||
nextSubscriptionOffset: result.subscriptionsNextOffset,
|
nextSubscriptionOffset: result.subscriptionsNextOffset,
|
||||||
subscriptions: result.subscriptions?.map(buildApiStarsSubscription),
|
subscriptions: result.subscriptions?.map(buildApiStarsSubscription),
|
||||||
balance: buildApiStarsAmount(result.balance),
|
balance: buildApiStarsAmount(result.balance),
|
||||||
@ -564,13 +548,9 @@ export async function fetchStarsTransactions({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const supportedHistory = result.history?.filter(
|
|
||||||
(transaction) => !(transaction.stargift instanceof GramJs.StarGiftUnique),
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
nextOffset: result.nextOffset,
|
nextOffset: result.nextOffset,
|
||||||
history: supportedHistory?.map(buildApiStarsTransaction),
|
history: result.history?.map(buildApiStarsTransaction),
|
||||||
balance: buildApiStarsAmount(result.balance),
|
balance: buildApiStarsAmount(result.balance),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -589,15 +569,12 @@ export async function fetchStarsTransactionById({
|
|||||||
})],
|
})],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const supportedHistory = result?.history?.filter(
|
if (!result?.history?.[0]) {
|
||||||
(transaction) => !(transaction.stargift instanceof GramJs.StarGiftUnique),
|
|
||||||
);
|
|
||||||
if (!supportedHistory?.[0]) {
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
transaction: buildApiStarsTransaction(supportedHistory[0]),
|
transaction: buildApiStarsTransaction(result?.history[0]),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import type {
|
|||||||
ApiInvoice,
|
ApiInvoice,
|
||||||
ApiMessageEntity,
|
ApiMessageEntity,
|
||||||
ApiPaymentCredentials,
|
ApiPaymentCredentials,
|
||||||
|
ApiSticker,
|
||||||
BoughtPaidMedia,
|
BoughtPaidMedia,
|
||||||
} from './messages';
|
} from './messages';
|
||||||
import type { StatisticsOverviewPercentage } from './statistics';
|
import type { StatisticsOverviewPercentage } from './statistics';
|
||||||
@ -189,10 +190,11 @@ export type ApiInputStorePaymentStarsGiveaway = {
|
|||||||
export type ApiInputStorePaymentPurpose = ApiInputStorePaymentGiveaway | ApiInputStorePaymentGiftcode |
|
export type ApiInputStorePaymentPurpose = ApiInputStorePaymentGiveaway | ApiInputStorePaymentGiftcode |
|
||||||
ApiInputStorePaymentStarsTopup | ApiInputStorePaymentStarsGift | ApiInputStorePaymentStarsGiveaway;
|
ApiInputStorePaymentStarsTopup | ApiInputStorePaymentStarsGift | ApiInputStorePaymentStarsGiveaway;
|
||||||
|
|
||||||
export type ApiStarGift = {
|
export interface ApiStarGiftRegular {
|
||||||
|
type: 'starGift';
|
||||||
isLimited?: true;
|
isLimited?: true;
|
||||||
id: string;
|
id: string;
|
||||||
stickerId: string;
|
sticker: ApiSticker;
|
||||||
stars: number;
|
stars: number;
|
||||||
availabilityRemains?: number;
|
availabilityRemains?: number;
|
||||||
availabilityTotal?: number;
|
availabilityTotal?: number;
|
||||||
@ -200,7 +202,57 @@ export type ApiStarGift = {
|
|||||||
isSoldOut?: true;
|
isSoldOut?: true;
|
||||||
firstSaleDate?: number;
|
firstSaleDate?: number;
|
||||||
lastSaleDate?: number;
|
lastSaleDate?: number;
|
||||||
};
|
isBirthday?: true;
|
||||||
|
upgradeStars?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiStarGiftUnique {
|
||||||
|
type: 'starGiftUnique';
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
number: number;
|
||||||
|
ownerId: string;
|
||||||
|
issuedCount: number;
|
||||||
|
totalCount: number;
|
||||||
|
attributes: ApiStarGiftAttribute[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ApiStarGift = ApiStarGiftRegular | ApiStarGiftUnique;
|
||||||
|
|
||||||
|
export interface ApiStarGiftAttributeModel {
|
||||||
|
type: 'model';
|
||||||
|
name: string;
|
||||||
|
rarityPercent: number;
|
||||||
|
sticker: ApiSticker;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiStarGiftAttributePattern {
|
||||||
|
type: 'pattern';
|
||||||
|
name: string;
|
||||||
|
rarityPercent: number;
|
||||||
|
sticker: ApiSticker;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiStarGiftAttributeBackdrop {
|
||||||
|
type: 'backdrop';
|
||||||
|
name: string;
|
||||||
|
centerColor: string;
|
||||||
|
edgeColor: string;
|
||||||
|
patternColor: string;
|
||||||
|
textColor: string;
|
||||||
|
rarityPercent: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiStarGiftAttributeOriginalDetails {
|
||||||
|
type: 'originalDetails';
|
||||||
|
senderId?: string;
|
||||||
|
recipientId: string;
|
||||||
|
date: number;
|
||||||
|
message?: ApiFormattedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ApiStarGiftAttribute = ApiStarGiftAttributeModel | ApiStarGiftAttributePattern
|
||||||
|
| ApiStarGiftAttributeBackdrop | ApiStarGiftAttributeOriginalDetails;
|
||||||
|
|
||||||
export interface ApiUserStarGift {
|
export interface ApiUserStarGift {
|
||||||
isNameHidden?: boolean;
|
isNameHidden?: boolean;
|
||||||
@ -371,6 +423,7 @@ export interface ApiStarsTransaction {
|
|||||||
extendedMedia?: BoughtPaidMedia[];
|
extendedMedia?: BoughtPaidMedia[];
|
||||||
subscriptionPeriod?: number;
|
subscriptionPeriod?: number;
|
||||||
starRefCommision?: number;
|
starRefCommision?: number;
|
||||||
|
isGiftUpgrade?: true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiStarsSubscription {
|
export interface ApiStarsSubscription {
|
||||||
|
|||||||
@ -1298,7 +1298,9 @@
|
|||||||
"GiftPremiumDescriptionLinkCaption" = "See Features >";
|
"GiftPremiumDescriptionLinkCaption" = "See Features >";
|
||||||
"GiftPremiumDescriptionLink" = "https://telegram.org/faq_premium";
|
"GiftPremiumDescriptionLink" = "https://telegram.org/faq_premium";
|
||||||
"StarsGiftHeader" = "Send a Gift";
|
"StarsGiftHeader" = "Send a Gift";
|
||||||
|
"StarsGiftHeaderSelf" = "Buy a Gift";
|
||||||
"StarGiftDescription" = "Give {user} gifts that can be kept on the profile or converted to Stars.";
|
"StarGiftDescription" = "Give {user} gifts that can be kept on the profile or converted to Stars.";
|
||||||
|
"StarGiftDescriptionSelf" = "Buy yourself a gift to display on your page or reserve for later.\n\nLimited-edition gifts upgraded to collectibles can be gifted to others later.";
|
||||||
"GiftLimited" = "limited";
|
"GiftLimited" = "limited";
|
||||||
"GiftDiscount" = "-{percent}%";
|
"GiftDiscount" = "-{percent}%";
|
||||||
"GiftSoldCount" = "{count} sold";
|
"GiftSoldCount" = "{count} sold";
|
||||||
@ -1342,11 +1344,25 @@
|
|||||||
"GiftInfoSoldOutTitle" = "Unavailable";
|
"GiftInfoSoldOutTitle" = "Unavailable";
|
||||||
"GiftInfoSoldOutDescription" = "This gift has been sold out";
|
"GiftInfoSoldOutDescription" = "This gift has been sold out";
|
||||||
"GiftInfoSenderHidden" = "Only you can see the sender's name and message.";
|
"GiftInfoSenderHidden" = "Only you can see the sender's name and message.";
|
||||||
|
"GiftInfoOwner" = "Owner";
|
||||||
|
"GiftInfoAvailability" = "Availability";
|
||||||
|
"GiftInfoIssued" = "{issued}/{total} issued";
|
||||||
|
"GiftInfoCollectible" = "Collectible #{number}";
|
||||||
|
"GiftAttributeModel" = "Model";
|
||||||
|
"GiftAttributeBackdrop" = "Backdrop";
|
||||||
|
"GiftAttributeSymbol" = "Symbol";
|
||||||
|
"GiftInfoOriginalInfo" = "Gifted to {user} on {date}."
|
||||||
|
"GiftInfoOriginalInfoSender" = "Gifted by {sender} to {user} on {date}."
|
||||||
|
"GiftInfoOriginalInfoText" = "Gifted to {user} on {date} with comment \"{text}\"."
|
||||||
|
"GiftInfoOriginalInfoTextSender" = "Gifted by {sender} to {user} on {date} with comment \"{text}\"."
|
||||||
|
"GiftInfoStatus" = "Status";
|
||||||
|
"GiftInfoStatusNonUnique" = "Non-Unique";
|
||||||
"StarsAmount" = "⭐️{amount}";
|
"StarsAmount" = "⭐️{amount}";
|
||||||
"StarsAmountText_one" = "{amount} Star";
|
"StarsAmountText_one" = "{amount} Star";
|
||||||
"StarsAmountText_other" = "{amount} Stars";
|
"StarsAmountText_other" = "{amount} Stars";
|
||||||
"AllGiftsCategory" = "All gifts";
|
"AllGiftsCategory" = "All gifts";
|
||||||
"LimitedGiftsCategory" = "Limited";
|
"LimitedGiftsCategory" = "Limited";
|
||||||
|
"StockGiftsCategory" = "In Stock";
|
||||||
"PremiumGiftDescription" = "Premium";
|
"PremiumGiftDescription" = "Premium";
|
||||||
"SendPaidReaction" = "Send ⭐️{amount}";
|
"SendPaidReaction" = "Send ⭐️{amount}";
|
||||||
"StarsPay" = "Confirm and Pay {amount}";
|
"StarsPay" = "Confirm and Pay {amount}";
|
||||||
|
|||||||
@ -6,9 +6,12 @@
|
|||||||
background-color: var(--accent-background-active-color);
|
background-color: var(--accent-background-active-color);
|
||||||
color: var(--accent-color);
|
color: var(--accent-color);
|
||||||
|
|
||||||
cursor: var(--custom-cursor, pointer);
|
|
||||||
filter: brightness(1);
|
filter: brightness(1);
|
||||||
transition: 150ms filter ease-in;
|
transition: 150ms filter ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clickable {
|
||||||
|
cursor: var(--custom-cursor, pointer);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
filter: brightness(1.1);
|
filter: brightness(1.1);
|
||||||
|
|||||||
@ -16,7 +16,7 @@ const BadgeButton = ({
|
|||||||
onClick,
|
onClick,
|
||||||
}: OwnProps) => {
|
}: OwnProps) => {
|
||||||
return (
|
return (
|
||||||
<div className={buildClassName(styles.root, className)} onClick={onClick}>
|
<div className={buildClassName(styles.root, onClick && styles.clickable, className)} onClick={onClick}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -35,6 +35,7 @@ type OwnProps<T = undefined> = {
|
|||||||
className?: string;
|
className?: string;
|
||||||
fluid?: boolean;
|
fluid?: boolean;
|
||||||
withPeerColors?: boolean;
|
withPeerColors?: boolean;
|
||||||
|
withEmojiStatus?: boolean;
|
||||||
clickArg?: T;
|
clickArg?: T;
|
||||||
onClick?: (arg: T) => void;
|
onClick?: (arg: T) => void;
|
||||||
};
|
};
|
||||||
@ -59,6 +60,7 @@ const PeerChip = <T,>({
|
|||||||
fluid,
|
fluid,
|
||||||
isSavedMessages,
|
isSavedMessages,
|
||||||
withPeerColors,
|
withPeerColors,
|
||||||
|
withEmojiStatus,
|
||||||
onClick,
|
onClick,
|
||||||
}: OwnProps<T> & StateProps) => {
|
}: OwnProps<T> & StateProps) => {
|
||||||
const lang = useOldLang();
|
const lang = useOldLang();
|
||||||
@ -91,7 +93,9 @@ const PeerChip = <T,>({
|
|||||||
);
|
);
|
||||||
|
|
||||||
titleText = getPeerTitle(lang, anyPeer) || title;
|
titleText = getPeerTitle(lang, anyPeer) || title;
|
||||||
titleElement = title || <FullNameTitle peer={anyPeer} isSavedMessages={isSavedMessages} withEmojiStatus />;
|
titleElement = title || (
|
||||||
|
<FullNameTitle peer={anyPeer} isSavedMessages={isSavedMessages} withEmojiStatus={withEmojiStatus} />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const fullClassName = buildClassName(
|
const fullClassName = buildClassName(
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%) translate(6px, -6px) rotate(45deg);
|
transform: translate(-50%, -50%) translate(6px, -6px) rotate(45deg);
|
||||||
|
|
||||||
font-size: 0.6875rem;
|
font-size: 0.625rem;
|
||||||
font-weight: var(--font-weight-semibold);
|
font-weight: var(--font-weight-semibold);
|
||||||
color: var(--color-white);
|
color: var(--color-white);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
border-radius: 0.625rem;
|
border-radius: 0.625rem;
|
||||||
background-color: var(--color-hover-overlay);
|
background-color: var(--color-hover-overlay);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover::before {
|
&:hover::before {
|
||||||
@ -34,15 +35,6 @@
|
|||||||
left: 0.25rem;
|
left: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stars {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.125rem;
|
|
||||||
|
|
||||||
color: #E88011;
|
|
||||||
font-weight: var(--font-weight-medium);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hiddenGift {
|
.hiddenGift {
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
@ -60,3 +52,8 @@
|
|||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
backdrop-filter: blur(0.5rem);
|
backdrop-filter: blur(0.5rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.radialPattern {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,20 +1,22 @@
|
|||||||
import React, { memo } from '../../../lib/teact/teact';
|
import React, { memo, useMemo, useRef } from '../../../lib/teact/teact';
|
||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import type { ApiSticker, ApiUser, ApiUserStarGift } from '../../../api/types';
|
import type { ApiUser, ApiUserStarGift } from '../../../api/types';
|
||||||
|
|
||||||
import { STARS_CURRENCY_CODE } from '../../../config';
|
|
||||||
import { selectUser } from '../../../global/selectors';
|
import { selectUser } from '../../../global/selectors';
|
||||||
import { formatCurrency } from '../../../util/formatCurrency';
|
|
||||||
import { CUSTOM_PEER_HIDDEN } from '../../../util/objects/customPeer';
|
import { CUSTOM_PEER_HIDDEN } from '../../../util/objects/customPeer';
|
||||||
import { formatIntegerCompact } from '../../../util/textFormat';
|
import { formatIntegerCompact } from '../../../util/textFormat';
|
||||||
|
import { getGiftAttributes, getStickerFromGift, getTotalGiftAvailability } from '../helpers/gifts';
|
||||||
|
|
||||||
|
import useFlag from '../../../hooks/useFlag';
|
||||||
|
import { type ObserveFn, useOnIntersect } from '../../../hooks/useIntersectionObserver';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
import AnimatedIconFromSticker from '../AnimatedIconFromSticker';
|
import AnimatedIconFromSticker from '../AnimatedIconFromSticker';
|
||||||
import Avatar from '../Avatar';
|
import Avatar from '../Avatar';
|
||||||
import Icon from '../icons/Icon';
|
import Icon from '../icons/Icon';
|
||||||
|
import RadialPatternBackground from '../profile/RadialPatternBackground';
|
||||||
import GiftRibbon from './GiftRibbon';
|
import GiftRibbon from './GiftRibbon';
|
||||||
|
|
||||||
import styles from './UserGift.module.scss';
|
import styles from './UserGift.module.scss';
|
||||||
@ -22,20 +24,28 @@ import styles from './UserGift.module.scss';
|
|||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
userId: string;
|
userId: string;
|
||||||
gift: ApiUserStarGift;
|
gift: ApiUserStarGift;
|
||||||
|
observeIntersection?: ObserveFn;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
fromPeer?: ApiUser;
|
fromPeer?: ApiUser;
|
||||||
sticker?: ApiSticker;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const GIFT_STICKER_SIZE = 90;
|
const GIFT_STICKER_SIZE = 90;
|
||||||
|
|
||||||
const UserGift = ({
|
const UserGift = ({
|
||||||
userId, gift, fromPeer, sticker,
|
userId,
|
||||||
|
gift,
|
||||||
|
fromPeer,
|
||||||
|
observeIntersection,
|
||||||
}: OwnProps & StateProps) => {
|
}: OwnProps & StateProps) => {
|
||||||
const { openGiftInfoModal } = getActions();
|
const { openGiftInfoModal } = getActions();
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const [shouldPlay, play] = useFlag();
|
||||||
|
|
||||||
const oldLang = useOldLang();
|
const oldLang = useOldLang();
|
||||||
|
|
||||||
const handleClick = useLastCallback(() => {
|
const handleClick = useLastCallback(() => {
|
||||||
@ -45,16 +55,48 @@ const UserGift = ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleOnIntersect = useLastCallback((entry: IntersectionObserverEntry) => {
|
||||||
|
if (entry.isIntersecting) play();
|
||||||
|
});
|
||||||
|
|
||||||
const avatarPeer = (gift.isNameHidden || !fromPeer) ? CUSTOM_PEER_HIDDEN : fromPeer;
|
const avatarPeer = (gift.isNameHidden || !fromPeer) ? CUSTOM_PEER_HIDDEN : fromPeer;
|
||||||
|
|
||||||
|
const sticker = getStickerFromGift(gift.gift);
|
||||||
|
|
||||||
|
const radialPatternBackdrop = useMemo(() => {
|
||||||
|
const { backdrop, pattern } = getGiftAttributes(gift.gift) || {};
|
||||||
|
|
||||||
|
if (!backdrop || !pattern) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const backdropColors = [backdrop.centerColor, backdrop.edgeColor];
|
||||||
|
const patternColor = backdrop.patternColor;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RadialPatternBackground
|
||||||
|
className={styles.radialPattern}
|
||||||
|
backgroundColors={backdropColors}
|
||||||
|
patternColor={patternColor}
|
||||||
|
patternIcon={pattern.sticker}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}, [gift.gift]);
|
||||||
|
|
||||||
|
useOnIntersect(ref, observeIntersection, sticker ? handleOnIntersect : undefined);
|
||||||
|
|
||||||
if (!sticker) return undefined;
|
if (!sticker) return undefined;
|
||||||
|
|
||||||
|
const totalIssued = getTotalGiftAvailability(gift.gift);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.root} onClick={handleClick}>
|
<div ref={ref} className={styles.root} onClick={handleClick}>
|
||||||
|
{radialPatternBackdrop}
|
||||||
<Avatar className={styles.avatar} peer={avatarPeer} size="micro" />
|
<Avatar className={styles.avatar} peer={avatarPeer} size="micro" />
|
||||||
<AnimatedIconFromSticker
|
<AnimatedIconFromSticker
|
||||||
sticker={sticker}
|
sticker={sticker}
|
||||||
noLoop
|
noLoop
|
||||||
|
play={shouldPlay}
|
||||||
nonInteractive
|
nonInteractive
|
||||||
size={GIFT_STICKER_SIZE}
|
size={GIFT_STICKER_SIZE}
|
||||||
/>
|
/>
|
||||||
@ -63,13 +105,10 @@ const UserGift = ({
|
|||||||
<Icon name="eye-closed-outline" />
|
<Icon name="eye-closed-outline" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={styles.stars}>
|
{totalIssued && (
|
||||||
{formatCurrency(gift.gift.stars, STARS_CURRENCY_CODE)}
|
|
||||||
</div>
|
|
||||||
{gift.gift.availabilityTotal && (
|
|
||||||
<GiftRibbon
|
<GiftRibbon
|
||||||
color="blue"
|
color="blue"
|
||||||
text={oldLang('Gift2Limited1OfRibbon', formatIntegerCompact(gift.gift.availabilityTotal))}
|
text={oldLang('Gift2Limited1OfRibbon', formatIntegerCompact(totalIssued))}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -78,11 +117,9 @@ const UserGift = ({
|
|||||||
|
|
||||||
export default memo(withGlobal<OwnProps>(
|
export default memo(withGlobal<OwnProps>(
|
||||||
(global, { gift }): StateProps => {
|
(global, { gift }): StateProps => {
|
||||||
const sticker = global.stickers.starGifts.stickers[gift.gift.stickerId];
|
|
||||||
const fromPeer = gift.fromId ? selectUser(global, gift.fromId) : undefined;
|
const fromPeer = gift.fromId ? selectUser(global, gift.fromId) : undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sticker,
|
|
||||||
fromPeer,
|
fromPeer,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
56
src/components/common/helpers/gifts.ts
Normal file
56
src/components/common/helpers/gifts.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import type {
|
||||||
|
ApiFormattedText,
|
||||||
|
ApiStarGift,
|
||||||
|
ApiStarGiftAttributeBackdrop,
|
||||||
|
ApiStarGiftAttributeModel,
|
||||||
|
ApiStarGiftAttributeOriginalDetails,
|
||||||
|
ApiStarGiftAttributePattern,
|
||||||
|
ApiSticker,
|
||||||
|
} from '../../../api/types';
|
||||||
|
|
||||||
|
export type GiftAttributes = {
|
||||||
|
model?: ApiStarGiftAttributeModel;
|
||||||
|
originalDetails?: ApiStarGiftAttributeOriginalDetails;
|
||||||
|
pattern?: ApiStarGiftAttributePattern;
|
||||||
|
backdrop?: ApiStarGiftAttributeBackdrop;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getStickerFromGift(gift: ApiStarGift): ApiSticker | undefined {
|
||||||
|
if (gift.type === 'starGift') {
|
||||||
|
return gift.sticker;
|
||||||
|
}
|
||||||
|
|
||||||
|
return gift.attributes.find((attr): attr is ApiStarGiftAttributeModel => attr.type === 'model')?.sticker;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTotalGiftAvailability(gift: ApiStarGift): number | undefined {
|
||||||
|
if (gift.type === 'starGift') {
|
||||||
|
return gift.availabilityTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
return gift.totalCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getGiftMessage(gift: ApiStarGift): ApiFormattedText | undefined {
|
||||||
|
if (gift.type !== 'starGiftUnique') return undefined;
|
||||||
|
|
||||||
|
return gift.attributes.find((attr): attr is ApiStarGiftAttributeOriginalDetails => attr.type === 'model')?.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getGiftAttributes(gift: ApiStarGift): GiftAttributes | undefined {
|
||||||
|
if (gift.type !== 'starGiftUnique') return undefined;
|
||||||
|
|
||||||
|
const model = gift.attributes.find((attr): attr is ApiStarGiftAttributeModel => attr.type === 'model');
|
||||||
|
const backdrop = gift.attributes.find((attr): attr is ApiStarGiftAttributeBackdrop => attr.type === 'backdrop');
|
||||||
|
const pattern = gift.attributes.find((attr): attr is ApiStarGiftAttributePattern => attr.type === 'pattern');
|
||||||
|
const originalDetails = gift.attributes.find((attr): attr is ApiStarGiftAttributeOriginalDetails => (
|
||||||
|
attr.type === 'originalDetails'
|
||||||
|
));
|
||||||
|
|
||||||
|
return {
|
||||||
|
model,
|
||||||
|
originalDetails,
|
||||||
|
pattern,
|
||||||
|
backdrop,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
.root {
|
||||||
|
border-radius: inherit;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: linear-gradient(var(--_bg-1), var(--_bg-2)), radial-gradient(circle, #ffffff32, #ffffff00);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
167
src/components/common/profile/RadialPatternBackground.tsx
Normal file
167
src/components/common/profile/RadialPatternBackground.tsx
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
import React, {
|
||||||
|
memo, useEffect, useRef, useSignal, useState,
|
||||||
|
} from '../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import type { ApiSticker } from '../../../api/types';
|
||||||
|
|
||||||
|
import { requestMutation } from '../../../lib/fasterdom/fasterdom';
|
||||||
|
import { getStickerMediaHash } from '../../../global/helpers';
|
||||||
|
import buildClassName from '../../../util/buildClassName';
|
||||||
|
import buildStyle from '../../../util/buildStyle';
|
||||||
|
import { preloadImage } from '../../../util/files';
|
||||||
|
|
||||||
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
import useMedia from '../../../hooks/useMedia';
|
||||||
|
import useResizeObserver from '../../../hooks/useResizeObserver';
|
||||||
|
import { useSignalEffect } from '../../../hooks/useSignalEffect';
|
||||||
|
|
||||||
|
import styles from './RadialPatternBackground.module.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
backgroundColors: string[];
|
||||||
|
patternColor: string;
|
||||||
|
patternIcon: ApiSticker;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const RINGS = 3;
|
||||||
|
const BASE_RING_ITEM_COUNT = 8;
|
||||||
|
const RING_INCREMENT = 0.5;
|
||||||
|
const CENTER_EMPTINESS = 0.05;
|
||||||
|
const MAX_RADIUS = 0.5;
|
||||||
|
const BASE_ICON_SIZE = 20;
|
||||||
|
|
||||||
|
const MIN_SIZE = 200;
|
||||||
|
|
||||||
|
const PATTERN_POSITIONS = (() => {
|
||||||
|
const coordinates: { x: number; y: number; alpha: number; sizeFactor: number }[] = [];
|
||||||
|
for (let ring = 1; ring <= RINGS; ring++) {
|
||||||
|
const ringItemCount = Math.floor(BASE_RING_ITEM_COUNT * (1 + (ring - 1) * RING_INCREMENT));
|
||||||
|
const ringProgress = ring / RINGS;
|
||||||
|
const ringRadius = CENTER_EMPTINESS + (MAX_RADIUS - CENTER_EMPTINESS) * ringProgress;
|
||||||
|
|
||||||
|
for (let i = 0; i < ringItemCount; i++) {
|
||||||
|
const angle = (i / ringItemCount) * Math.PI * 2;
|
||||||
|
// Slightly oval
|
||||||
|
const xOffset = ringRadius * 1.71 * Math.cos(angle);
|
||||||
|
const yOffset = ringRadius * Math.sin(angle);
|
||||||
|
|
||||||
|
const x = 0.5 + xOffset;
|
||||||
|
const y = 0.5 + yOffset;
|
||||||
|
const alpha = 0.2 + Math.min((1 - ringProgress + (Math.random() / 2 - 0.5)), 0) * 0.8;
|
||||||
|
|
||||||
|
const sizeFactor = 1.4 - ringProgress * Math.random();
|
||||||
|
|
||||||
|
coordinates.push({
|
||||||
|
x, y, alpha, sizeFactor,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return coordinates;
|
||||||
|
})();
|
||||||
|
|
||||||
|
const RadialPatternBackground = ({
|
||||||
|
backgroundColors,
|
||||||
|
patternColor,
|
||||||
|
patternIcon,
|
||||||
|
className,
|
||||||
|
}: OwnProps) => {
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
|
|
||||||
|
const [getContainerSize, setContainerSize] = useSignal({ width: 0, height: 0 });
|
||||||
|
|
||||||
|
const [emojiImage, setEmojiImage] = useState<HTMLImageElement | undefined>();
|
||||||
|
|
||||||
|
const previewMediaHash = getStickerMediaHash(patternIcon, 'preview');
|
||||||
|
const previewUrl = useMedia(previewMediaHash);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!previewUrl) return;
|
||||||
|
preloadImage(previewUrl).then(setEmojiImage);
|
||||||
|
}, [previewUrl]);
|
||||||
|
|
||||||
|
useResizeObserver(containerRef, (entry) => {
|
||||||
|
setContainerSize({
|
||||||
|
width: entry.contentRect.width,
|
||||||
|
height: entry.contentRect.height,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const container = containerRef.current;
|
||||||
|
if (container) {
|
||||||
|
setContainerSize({
|
||||||
|
width: container.clientWidth,
|
||||||
|
height: container.clientHeight,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [setContainerSize]);
|
||||||
|
|
||||||
|
const draw = useLastCallback(() => {
|
||||||
|
const canvas = canvasRef.current;
|
||||||
|
if (!canvas || !emojiImage) return;
|
||||||
|
const ctx = canvas.getContext('2d')!;
|
||||||
|
const { width, height } = canvas;
|
||||||
|
if (!width || !height) return;
|
||||||
|
|
||||||
|
ctx.save();
|
||||||
|
PATTERN_POSITIONS.forEach(({
|
||||||
|
x, y, alpha, sizeFactor,
|
||||||
|
}) => {
|
||||||
|
const centerShift = (width - Math.max(width, MIN_SIZE)) / 2; // Shift coords if canvas is smaller than `MIN_SIZE`
|
||||||
|
const renderX = x * Math.max(width, MIN_SIZE) + centerShift;
|
||||||
|
const renderY = y * Math.max(height, MIN_SIZE) + centerShift;
|
||||||
|
|
||||||
|
const size = BASE_ICON_SIZE * sizeFactor * (centerShift ? 0.8 : 1);
|
||||||
|
|
||||||
|
ctx.globalAlpha = alpha;
|
||||||
|
ctx.drawImage(emojiImage, renderX - size / 2, renderY - size / 2, size, size);
|
||||||
|
});
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
ctx.save();
|
||||||
|
ctx.fillStyle = patternColor;
|
||||||
|
ctx.globalCompositeOperation = 'source-atop';
|
||||||
|
ctx.fillRect(0, 0, width, height);
|
||||||
|
ctx.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
draw();
|
||||||
|
}, [emojiImage]);
|
||||||
|
|
||||||
|
useSignalEffect(() => {
|
||||||
|
const { width, height } = getContainerSize();
|
||||||
|
const canvas = canvasRef.current!;
|
||||||
|
if (!width || !height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxSide = Math.max(width, height);
|
||||||
|
const dpr = window.devicePixelRatio;
|
||||||
|
requestMutation(() => {
|
||||||
|
canvas.width = maxSide * dpr;
|
||||||
|
canvas.height = maxSide * dpr;
|
||||||
|
|
||||||
|
draw();
|
||||||
|
});
|
||||||
|
}, [getContainerSize]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={containerRef}
|
||||||
|
className={buildClassName(styles.root, className)}
|
||||||
|
style={buildStyle(
|
||||||
|
`--_bg-1: ${backgroundColors[0]}`,
|
||||||
|
`--_bg-2: ${backgroundColors[1] || backgroundColors[0]}`,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<canvas className={styles.canvas} ref={canvasRef} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(RadialPatternBackground);
|
||||||
@ -21,7 +21,6 @@ import {
|
|||||||
selectGiftStickerForStars,
|
selectGiftStickerForStars,
|
||||||
selectIsCurrentUserPremium,
|
selectIsCurrentUserPremium,
|
||||||
selectIsMessageFocused,
|
selectIsMessageFocused,
|
||||||
selectStarGiftSticker,
|
|
||||||
selectTabState,
|
selectTabState,
|
||||||
selectTheme,
|
selectTheme,
|
||||||
selectTopicFromMessage,
|
selectTopicFromMessage,
|
||||||
@ -81,7 +80,6 @@ type StateProps = {
|
|||||||
focusDirection?: FocusDirection;
|
focusDirection?: FocusDirection;
|
||||||
noFocusHighlight?: boolean;
|
noFocusHighlight?: boolean;
|
||||||
premiumGiftSticker?: ApiSticker;
|
premiumGiftSticker?: ApiSticker;
|
||||||
starGiftSticker?: ApiSticker;
|
|
||||||
starsGiftSticker?: ApiSticker;
|
starsGiftSticker?: ApiSticker;
|
||||||
canPlayAnimatedEmojis?: boolean;
|
canPlayAnimatedEmojis?: boolean;
|
||||||
patternColor?: string;
|
patternColor?: string;
|
||||||
@ -108,7 +106,6 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
|
|||||||
focusDirection,
|
focusDirection,
|
||||||
noFocusHighlight,
|
noFocusHighlight,
|
||||||
premiumGiftSticker,
|
premiumGiftSticker,
|
||||||
starGiftSticker,
|
|
||||||
starsGiftSticker,
|
starsGiftSticker,
|
||||||
isInsideTopic,
|
isInsideTopic,
|
||||||
topic,
|
topic,
|
||||||
@ -471,7 +468,7 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
function renderStarGift() {
|
function renderStarGift() {
|
||||||
const starGift = message.content.action?.starGift;
|
const starGift = message.content.action?.starGift;
|
||||||
if (!starGift) return undefined;
|
if (!starGift || starGift.gift.type === 'starGiftUnique') return undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
@ -482,7 +479,7 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
|
|||||||
>
|
>
|
||||||
|
|
||||||
<AnimatedIconFromSticker
|
<AnimatedIconFromSticker
|
||||||
sticker={starGiftSticker}
|
sticker={starGift.gift.sticker}
|
||||||
play={canPlayAnimatedEmojis}
|
play={canPlayAnimatedEmojis}
|
||||||
noLoop
|
noLoop
|
||||||
nonInteractive
|
nonInteractive
|
||||||
@ -522,7 +519,7 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
|
|||||||
>
|
>
|
||||||
<AnimatedIconFromSticker
|
<AnimatedIconFromSticker
|
||||||
key={message.id}
|
key={message.id}
|
||||||
sticker={starGiftSticker}
|
sticker={starsGiftSticker}
|
||||||
play={canPlayAnimatedEmojis}
|
play={canPlayAnimatedEmojis}
|
||||||
noLoop
|
noLoop
|
||||||
nonInteractive
|
nonInteractive
|
||||||
@ -642,9 +639,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const giftDuration = content.action?.months;
|
const giftDuration = content.action?.months;
|
||||||
const premiumGiftSticker = selectGiftStickerForDuration(global, giftDuration);
|
const premiumGiftSticker = selectGiftStickerForDuration(global, giftDuration);
|
||||||
|
|
||||||
const starGift = content.action?.type === 'starGift' ? content.action.starGift?.gift : undefined;
|
|
||||||
const starCount = content.action?.stars;
|
const starCount = content.action?.stars;
|
||||||
const starGiftSticker = starGift?.stickerId ? selectStarGiftSticker(global, starGift.stickerId) : undefined;
|
|
||||||
const starsGiftSticker = selectGiftStickerForStars(global, starCount);
|
const starsGiftSticker = selectGiftStickerForStars(global, starCount);
|
||||||
|
|
||||||
const topic = selectTopicFromMessage(global, message);
|
const topic = selectTopicFromMessage(global, message);
|
||||||
@ -658,7 +653,6 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
targetMessage,
|
targetMessage,
|
||||||
isFocused,
|
isFocused,
|
||||||
premiumGiftSticker,
|
premiumGiftSticker,
|
||||||
starGiftSticker,
|
|
||||||
starsGiftSticker,
|
starsGiftSticker,
|
||||||
topic,
|
topic,
|
||||||
patternColor,
|
patternColor,
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import Modal from '../../ui/Modal';
|
|||||||
|
|
||||||
import styles from './TableInfoModal.module.scss';
|
import styles from './TableInfoModal.module.scss';
|
||||||
|
|
||||||
type ChatItem = { chatId: string };
|
type ChatItem = { chatId: string; withEmojiStatus?: boolean };
|
||||||
|
|
||||||
export type TableData = [TeactNode | undefined, TeactNode | ChatItem][];
|
export type TableData = [TeactNode | undefined, TeactNode | ChatItem][];
|
||||||
|
|
||||||
@ -76,6 +76,7 @@ const TableInfoModal = ({
|
|||||||
className={styles.chatItem}
|
className={styles.chatItem}
|
||||||
forceShowSelf
|
forceShowSelf
|
||||||
fluid
|
fluid
|
||||||
|
withEmojiStatus={value.withEmojiStatus}
|
||||||
clickArg={value.chatId}
|
clickArg={value.chatId}
|
||||||
onClick={handleOpenChat}
|
onClick={handleOpenChat}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
import React, { memo } from '../../../lib/teact/teact';
|
import React, { memo, useRef } from '../../../lib/teact/teact';
|
||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions } from '../../../global';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ApiStarGift,
|
ApiStarGiftRegular,
|
||||||
ApiSticker,
|
|
||||||
} from '../../../api/types';
|
} from '../../../api/types';
|
||||||
|
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
|
|
||||||
|
import useFlag from '../../../hooks/useFlag';
|
||||||
|
import { type ObserveFn, useOnIntersect } from '../../../hooks/useIntersectionObserver';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
@ -19,24 +20,27 @@ import Button from '../../ui/Button';
|
|||||||
import styles from './GiftItem.module.scss';
|
import styles from './GiftItem.module.scss';
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
gift: ApiStarGift;
|
gift: ApiStarGiftRegular;
|
||||||
onClick: (gift: ApiStarGift) => void;
|
observeIntersection?: ObserveFn;
|
||||||
};
|
onClick: (gift: ApiStarGiftRegular) => void;
|
||||||
|
|
||||||
export type StateProps = {
|
|
||||||
sticker?: ApiSticker;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const GIFT_STICKER_SIZE = 90;
|
const GIFT_STICKER_SIZE = 90;
|
||||||
|
|
||||||
function GiftItemStar({ sticker, gift, onClick }: OwnProps & StateProps) {
|
function GiftItemStar({ gift, observeIntersection, onClick }: OwnProps) {
|
||||||
const { openGiftInfoModal } = getActions();
|
const { openGiftInfoModal } = getActions();
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
const [shouldPlay, play] = useFlag();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
stars,
|
stars,
|
||||||
isLimited,
|
isLimited,
|
||||||
isSoldOut,
|
isSoldOut,
|
||||||
|
sticker,
|
||||||
} = gift;
|
} = gift;
|
||||||
|
|
||||||
const handleGiftClick = useLastCallback(() => {
|
const handleGiftClick = useLastCallback(() => {
|
||||||
@ -48,10 +52,13 @@ function GiftItemStar({ sticker, gift, onClick }: OwnProps & StateProps) {
|
|||||||
onClick(gift);
|
onClick(gift);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!sticker) return undefined;
|
useOnIntersect(ref, observeIntersection, (entry) => {
|
||||||
|
if (entry.isIntersecting) play();
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
ref={ref}
|
||||||
className={buildClassName(styles.container, styles.starGift)}
|
className={buildClassName(styles.container, styles.starGift)}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
role="button"
|
role="button"
|
||||||
@ -60,6 +67,7 @@ function GiftItemStar({ sticker, gift, onClick }: OwnProps & StateProps) {
|
|||||||
<AnimatedIconFromSticker
|
<AnimatedIconFromSticker
|
||||||
sticker={sticker}
|
sticker={sticker}
|
||||||
noLoop
|
noLoop
|
||||||
|
play={shouldPlay}
|
||||||
nonInteractive
|
nonInteractive
|
||||||
size={GIFT_STICKER_SIZE}
|
size={GIFT_STICKER_SIZE}
|
||||||
/>
|
/>
|
||||||
@ -75,12 +83,4 @@ function GiftItemStar({ sticker, gift, onClick }: OwnProps & StateProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(withGlobal<OwnProps>(
|
export default memo(GiftItemStar);
|
||||||
(global, { gift }): StateProps => {
|
|
||||||
const sticker = global.stickers.starGifts.stickers[gift.stickerId];
|
|
||||||
|
|
||||||
return {
|
|
||||||
sticker,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
)(GiftItemStar));
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { getActions, withGlobal } from '../../../global';
|
|||||||
|
|
||||||
import type {
|
import type {
|
||||||
ApiPremiumGiftCodeOption,
|
ApiPremiumGiftCodeOption,
|
||||||
ApiStarGift,
|
ApiStarGiftRegular,
|
||||||
ApiStarsAmount,
|
ApiStarsAmount,
|
||||||
ApiUser,
|
ApiUser,
|
||||||
} from '../../../api/types';
|
} from '../../../api/types';
|
||||||
@ -18,6 +18,7 @@ import { selectUser } from '../../../global/selectors';
|
|||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
|
|
||||||
import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
|
import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
|
||||||
|
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
|
||||||
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';
|
||||||
@ -41,17 +42,19 @@ export type OwnProps = {
|
|||||||
modal: TabState['giftModal'];
|
modal: TabState['giftModal'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GiftOption = ApiPremiumGiftCodeOption | ApiStarGift;
|
export type GiftOption = ApiPremiumGiftCodeOption | ApiStarGiftRegular;
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
boostPerSentGift?: number;
|
boostPerSentGift?: number;
|
||||||
starGiftsById?: Record<string, ApiStarGift>;
|
starGiftsById?: Record<string, ApiStarGiftRegular>;
|
||||||
starGiftCategoriesByName: Record<StarGiftCategory, string[]>;
|
starGiftCategoriesByName: Record<StarGiftCategory, string[]>;
|
||||||
starBalance?: ApiStarsAmount;
|
starBalance?: ApiStarsAmount;
|
||||||
user?: ApiUser;
|
user?: ApiUser;
|
||||||
|
isSelf?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AVATAR_SIZE = 100;
|
const AVATAR_SIZE = 100;
|
||||||
|
const INTERSECTION_THROTTLE = 200;
|
||||||
|
|
||||||
const PremiumGiftModal: FC<OwnProps & StateProps> = ({
|
const PremiumGiftModal: FC<OwnProps & StateProps> = ({
|
||||||
modal,
|
modal,
|
||||||
@ -59,6 +62,7 @@ const PremiumGiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
starGiftCategoriesByName,
|
starGiftCategoriesByName,
|
||||||
starBalance,
|
starBalance,
|
||||||
user,
|
user,
|
||||||
|
isSelf,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
closeGiftModal, requestConfetti,
|
closeGiftModal, requestConfetti,
|
||||||
@ -70,6 +74,9 @@ const PremiumGiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const giftHeaderRef = useRef<HTMLHeadingElement>(null);
|
const giftHeaderRef = useRef<HTMLHeadingElement>(null);
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
const scrollerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const isOpen = Boolean(modal);
|
const isOpen = Boolean(modal);
|
||||||
const renderingModal = useCurrentOrPrev(modal);
|
const renderingModal = useCurrentOrPrev(modal);
|
||||||
|
|
||||||
@ -91,6 +98,10 @@ const PremiumGiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
return filteredGifts?.reduce((prev, gift) => (prev.amount < gift.amount ? prev : gift));
|
return filteredGifts?.reduce((prev, gift) => (prev.amount < gift.amount ? prev : gift));
|
||||||
}, [filteredGifts]);
|
}, [filteredGifts]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
observe: observeIntersection,
|
||||||
|
} = useIntersectionObserver({ rootRef: scrollerRef, throttleMs: INTERSECTION_THROTTLE, isDisabled: !isOpen });
|
||||||
|
|
||||||
const showConfetti = useLastCallback(() => {
|
const showConfetti = useLastCallback(() => {
|
||||||
const dialog = dialogRef.current;
|
const dialog = dialogRef.current;
|
||||||
if (!dialog) return;
|
if (!dialog) return;
|
||||||
@ -145,9 +156,14 @@ const PremiumGiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
),
|
),
|
||||||
}, { withNodes: true });
|
}, { withNodes: true });
|
||||||
|
|
||||||
const starGiftDescription = lang('StarGiftDescription', {
|
const starGiftDescription = isSelf
|
||||||
user: getUserFullName(user)!,
|
? lang('StarGiftDescriptionSelf', undefined, {
|
||||||
}, { withNodes: true });
|
withNodes: true,
|
||||||
|
renderTextFilters: ['br'],
|
||||||
|
})
|
||||||
|
: lang('StarGiftDescription', {
|
||||||
|
user: getUserFullName(user)!,
|
||||||
|
}, { withNodes: true, withMarkdown: true });
|
||||||
|
|
||||||
function renderGiftPremiumHeader() {
|
function renderGiftPremiumHeader() {
|
||||||
return (
|
return (
|
||||||
@ -168,7 +184,7 @@ const PremiumGiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
function renderStarGiftsHeader() {
|
function renderStarGiftsHeader() {
|
||||||
return (
|
return (
|
||||||
<h2 ref={giftHeaderRef} className={buildClassName(styles.headerText, styles.center)}>
|
<h2 ref={giftHeaderRef} className={buildClassName(styles.headerText, styles.center)}>
|
||||||
{lang('StarsGiftHeader')}
|
{lang(isSelf ? 'StarsGiftHeaderSelf' : 'StarsGiftHeader')}
|
||||||
</h2>
|
</h2>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -195,6 +211,7 @@ const PremiumGiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<GiftItemStar
|
<GiftItemStar
|
||||||
gift={gift}
|
gift={gift}
|
||||||
|
observeIntersection={observeIntersection}
|
||||||
onClick={handleGiftClick}
|
onClick={handleGiftClick}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -233,7 +250,7 @@ const PremiumGiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
function renderMainScreen() {
|
function renderMainScreen() {
|
||||||
return (
|
return (
|
||||||
<div className={buildClassName(styles.main, 'custom-scroll')} onScroll={handleScroll}>
|
<div ref={scrollerRef} className={buildClassName(styles.main, 'custom-scroll')} onScroll={handleScroll}>
|
||||||
<div className={styles.avatars}>
|
<div className={styles.avatars}>
|
||||||
<Avatar
|
<Avatar
|
||||||
size={AVATAR_SIZE}
|
size={AVATAR_SIZE}
|
||||||
@ -241,10 +258,9 @@ const PremiumGiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
/>
|
/>
|
||||||
<img className={styles.logoBackground} src={StarsBackground} alt="" draggable={false} />
|
<img className={styles.logoBackground} src={StarsBackground} alt="" draggable={false} />
|
||||||
</div>
|
</div>
|
||||||
{renderGiftPremiumHeader()}
|
{!isSelf && renderGiftPremiumHeader()}
|
||||||
{renderGiftPremiumDescription()}
|
{!isSelf && renderGiftPremiumDescription()}
|
||||||
|
{!isSelf && renderPremiumGifts()}
|
||||||
{renderPremiumGifts()}
|
|
||||||
|
|
||||||
{renderStarGiftsHeader()}
|
{renderStarGiftsHeader()}
|
||||||
{renderStarGiftsDescription()}
|
{renderStarGiftsDescription()}
|
||||||
@ -294,7 +310,7 @@ const PremiumGiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
slideClassName={styles.headerSlide}
|
slideClassName={styles.headerSlide}
|
||||||
>
|
>
|
||||||
<h2 className={styles.commonHeaderText}>
|
<h2 className={styles.commonHeaderText}>
|
||||||
{lang(isHeaderForStarGifts ? 'StarsGiftHeader' : 'GiftPremiumHeader')}
|
{lang(isHeaderForStarGifts ? (isSelf ? 'StarsGiftHeaderSelf' : 'StarsGiftHeader') : 'GiftPremiumHeader')}
|
||||||
</h2>
|
</h2>
|
||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
@ -314,9 +330,15 @@ const PremiumGiftModal: FC<OwnProps & StateProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default memo(withGlobal<OwnProps>((global, { modal }): StateProps => {
|
export default memo(withGlobal<OwnProps>((global, { modal }): StateProps => {
|
||||||
const { starGiftsById, starGiftCategoriesByName, stars } = global;
|
const {
|
||||||
|
starGiftsById,
|
||||||
|
starGiftCategoriesByName,
|
||||||
|
stars,
|
||||||
|
currentUserId,
|
||||||
|
} = global;
|
||||||
|
|
||||||
const user = modal?.forUserId ? selectUser(global, modal.forUserId) : undefined;
|
const user = modal?.forUserId ? selectUser(global, modal.forUserId) : undefined;
|
||||||
|
const isSelf = Boolean(currentUserId && modal?.forUserId === currentUserId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
boostPerSentGift: global.appConfig?.boostsPerSentGift,
|
boostPerSentGift: global.appConfig?.boostsPerSentGift,
|
||||||
@ -324,15 +346,13 @@ export default memo(withGlobal<OwnProps>((global, { modal }): StateProps => {
|
|||||||
starGiftCategoriesByName,
|
starGiftCategoriesByName,
|
||||||
starBalance: stars?.balance,
|
starBalance: stars?.balance,
|
||||||
user,
|
user,
|
||||||
|
isSelf,
|
||||||
};
|
};
|
||||||
})(PremiumGiftModal));
|
})(PremiumGiftModal));
|
||||||
|
|
||||||
function getCategoryKey(category: StarGiftCategory) {
|
function getCategoryKey(category: StarGiftCategory) {
|
||||||
if (category === 'all') {
|
if (category === 'all') return -2;
|
||||||
return -1;
|
if (category === 'stock') return -1;
|
||||||
}
|
if (category === 'limited') return 0;
|
||||||
if (category === 'limited') {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return category;
|
return category;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,12 +46,9 @@ const StarGiftCategoryList = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderCategoryName(category: StarGiftCategory) {
|
function renderCategoryName(category: StarGiftCategory) {
|
||||||
if (category === 'all') {
|
if (category === 'all') return lang('AllGiftsCategory');
|
||||||
return lang('AllGiftsCategory');
|
if (category === 'stock') return lang('StockGiftsCategory');
|
||||||
}
|
if (category === 'limited') return lang('LimitedGiftsCategory');
|
||||||
if (category === 'limited') {
|
|
||||||
return lang('LimitedGiftsCategory');
|
|
||||||
}
|
|
||||||
return category;
|
return category;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +61,7 @@ const StarGiftCategoryList = ({
|
|||||||
)}
|
)}
|
||||||
onClick={() => handleItemClick(category)}
|
onClick={() => handleItemClick(category)}
|
||||||
>
|
>
|
||||||
{category !== 'all' && category !== 'limited' && (
|
{Number.isInteger(category) && (
|
||||||
<StarIcon
|
<StarIcon
|
||||||
className={styles.star}
|
className={styles.star}
|
||||||
type="gold"
|
type="gold"
|
||||||
@ -81,6 +78,7 @@ const StarGiftCategoryList = ({
|
|||||||
return (
|
return (
|
||||||
<div ref={ref} className={buildClassName(styles.list, 'no-scrollbar')}>
|
<div ref={ref} className={buildClassName(styles.list, 'no-scrollbar')}>
|
||||||
{renderCategoryItem('all')}
|
{renderCategoryItem('all')}
|
||||||
|
{renderCategoryItem('stock')}
|
||||||
{renderCategoryItem('limited')}
|
{renderCategoryItem('limited')}
|
||||||
{starCategories.map(renderCategoryItem)}
|
{starCategories.map(renderCategoryItem)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
.modal :global(.modal-dialog) {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -25,6 +29,7 @@
|
|||||||
|
|
||||||
.description {
|
.description {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
color: var(--_color-description, var(--color-text));
|
||||||
}
|
}
|
||||||
|
|
||||||
.footerDescription {
|
.footerDescription {
|
||||||
@ -44,3 +49,32 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.125rem;
|
gap: 0.125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.radialPattern {
|
||||||
|
position: absolute;
|
||||||
|
top: -3rem;
|
||||||
|
left: -1.5rem;
|
||||||
|
right: -1.5rem;
|
||||||
|
height: 16.5rem;
|
||||||
|
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uniqueAttribute {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uniqueGift {
|
||||||
|
gap: 0;
|
||||||
|
|
||||||
|
.giftSticker {
|
||||||
|
margin-block: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,17 +1,22 @@
|
|||||||
|
import type { TeactNode } from '../../../../lib/teact/teact';
|
||||||
import React, { memo, useMemo } from '../../../../lib/teact/teact';
|
import React, { memo, useMemo } from '../../../../lib/teact/teact';
|
||||||
import { getActions, withGlobal } from '../../../../global';
|
import { getActions, getGlobal, withGlobal } from '../../../../global';
|
||||||
|
|
||||||
import type { ApiSticker, ApiUser } from '../../../../api/types';
|
import type {
|
||||||
|
ApiUser,
|
||||||
|
} from '../../../../api/types';
|
||||||
import type { TabState } from '../../../../global/types';
|
import type { TabState } from '../../../../global/types';
|
||||||
|
|
||||||
import { getUserFullName } from '../../../../global/helpers';
|
import { getUserFullName } from '../../../../global/helpers';
|
||||||
import { selectStarGiftSticker, selectUser } from '../../../../global/selectors';
|
import { selectUser } from '../../../../global/selectors';
|
||||||
import buildClassName from '../../../../util/buildClassName';
|
import buildClassName from '../../../../util/buildClassName';
|
||||||
|
import buildStyle from '../../../../util/buildStyle';
|
||||||
import { formatDateTimeToString } from '../../../../util/dates/dateFormat';
|
import { formatDateTimeToString } from '../../../../util/dates/dateFormat';
|
||||||
import { formatStarsAsIcon, formatStarsAsText } from '../../../../util/localization/format';
|
import { formatStarsAsIcon, formatStarsAsText } from '../../../../util/localization/format';
|
||||||
import { CUSTOM_PEER_HIDDEN } from '../../../../util/objects/customPeer';
|
import { CUSTOM_PEER_HIDDEN } from '../../../../util/objects/customPeer';
|
||||||
import { getServerTime } from '../../../../util/serverTime';
|
import { getServerTime } from '../../../../util/serverTime';
|
||||||
import { formatInteger } from '../../../../util/textFormat';
|
import { formatInteger, formatPercent } from '../../../../util/textFormat';
|
||||||
|
import { getGiftAttributes, getStickerFromGift } from '../../../common/helpers/gifts';
|
||||||
import { renderTextWithEntities } from '../../../common/helpers/renderTextWithEntities';
|
import { renderTextWithEntities } from '../../../common/helpers/renderTextWithEntities';
|
||||||
|
|
||||||
import useCurrentOrPrev from '../../../../hooks/useCurrentOrPrev';
|
import useCurrentOrPrev from '../../../../hooks/useCurrentOrPrev';
|
||||||
@ -24,6 +29,7 @@ import AnimatedIconFromSticker from '../../../common/AnimatedIconFromSticker';
|
|||||||
import Avatar from '../../../common/Avatar';
|
import Avatar from '../../../common/Avatar';
|
||||||
import BadgeButton from '../../../common/BadgeButton';
|
import BadgeButton from '../../../common/BadgeButton';
|
||||||
import StarIcon from '../../../common/icons/StarIcon';
|
import StarIcon from '../../../common/icons/StarIcon';
|
||||||
|
import RadialPatternBackground from '../../../common/profile/RadialPatternBackground';
|
||||||
import Button from '../../../ui/Button';
|
import Button from '../../../ui/Button';
|
||||||
import ConfirmDialog from '../../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../../ui/ConfirmDialog';
|
||||||
import Link from '../../../ui/Link';
|
import Link from '../../../ui/Link';
|
||||||
@ -36,7 +42,6 @@ export type OwnProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
sticker?: ApiSticker;
|
|
||||||
userFrom?: ApiUser;
|
userFrom?: ApiUser;
|
||||||
targetUser?: ApiUser;
|
targetUser?: ApiUser;
|
||||||
currentUserId?: string;
|
currentUserId?: string;
|
||||||
@ -46,7 +51,7 @@ type StateProps = {
|
|||||||
const STICKER_SIZE = 120;
|
const STICKER_SIZE = 120;
|
||||||
|
|
||||||
const GiftInfoModal = ({
|
const GiftInfoModal = ({
|
||||||
modal, sticker, userFrom, targetUser, currentUserId, starGiftMaxConvertPeriod,
|
modal, userFrom, targetUser, currentUserId, starGiftMaxConvertPeriod,
|
||||||
}: OwnProps & StateProps) => {
|
}: OwnProps & StateProps) => {
|
||||||
const {
|
const {
|
||||||
closeGiftInfoModal,
|
closeGiftInfoModal,
|
||||||
@ -65,13 +70,16 @@ const GiftInfoModal = ({
|
|||||||
const { gift: typeGift } = renderingModal || {};
|
const { gift: typeGift } = renderingModal || {};
|
||||||
const isUserGift = typeGift && 'gift' in typeGift;
|
const isUserGift = typeGift && 'gift' in typeGift;
|
||||||
const userGift = isUserGift ? typeGift : undefined;
|
const userGift = isUserGift ? typeGift : undefined;
|
||||||
const canUpdate = Boolean(userGift?.fromId && userGift.messageId);
|
const canUpdate = Boolean(userGift?.messageId);
|
||||||
const isSender = userGift?.fromId === currentUserId;
|
const isSender = userGift?.fromId === currentUserId;
|
||||||
const canConvertDifference = (userGift && starGiftMaxConvertPeriod && (
|
const canConvertDifference = (userGift && starGiftMaxConvertPeriod && (
|
||||||
userGift.date + starGiftMaxConvertPeriod - getServerTime()
|
userGift.date + starGiftMaxConvertPeriod - getServerTime()
|
||||||
)) || 0;
|
)) || 0;
|
||||||
const conversionLeft = Math.ceil(canConvertDifference / 60 / 60 / 24);
|
const conversionLeft = Math.ceil(canConvertDifference / 60 / 60 / 24);
|
||||||
|
|
||||||
|
const gift = isUserGift ? typeGift.gift : typeGift;
|
||||||
|
const giftSticker = gift && getStickerFromGift(gift);
|
||||||
|
|
||||||
const handleClose = useLastCallback(() => {
|
const handleClose = useLastCallback(() => {
|
||||||
closeGiftInfoModal();
|
closeGiftInfoModal();
|
||||||
});
|
});
|
||||||
@ -94,15 +102,38 @@ const GiftInfoModal = ({
|
|||||||
handleClose();
|
handleClose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const giftAttributes = useMemo(() => {
|
||||||
|
return gift && getGiftAttributes(gift);
|
||||||
|
}, [gift]);
|
||||||
|
|
||||||
|
const radialPatternBackdrop = useMemo(() => {
|
||||||
|
const { backdrop, pattern } = giftAttributes || {};
|
||||||
|
|
||||||
|
if (!backdrop || !pattern || !isOpen) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const backdropColors = [backdrop.centerColor, backdrop.edgeColor];
|
||||||
|
const patternColor = backdrop.patternColor;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RadialPatternBackground
|
||||||
|
className={styles.radialPattern}
|
||||||
|
backgroundColors={backdropColors}
|
||||||
|
patternColor={patternColor}
|
||||||
|
patternIcon={pattern.sticker}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}, [giftAttributes, isOpen]);
|
||||||
|
|
||||||
const modalData = useMemo(() => {
|
const modalData = useMemo(() => {
|
||||||
if (!typeGift) {
|
if (!typeGift || !gift) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
fromId, isNameHidden, message, starsToConvert, isUnsaved, isConverted,
|
fromId, isNameHidden, starsToConvert, isUnsaved, isConverted,
|
||||||
} = userGift || {};
|
} = userGift || {};
|
||||||
const gift = isUserGift ? typeGift.gift : typeGift;
|
|
||||||
|
|
||||||
const isVisibleForMe = isNameHidden && targetUser;
|
const isVisibleForMe = isNameHidden && targetUser;
|
||||||
|
|
||||||
@ -110,6 +141,11 @@ const GiftInfoModal = ({
|
|||||||
if (!userGift) {
|
if (!userGift) {
|
||||||
return lang('GiftInfoSoldOutDescription');
|
return lang('GiftInfoSoldOutDescription');
|
||||||
}
|
}
|
||||||
|
if (gift.type === 'starGiftUnique') {
|
||||||
|
return lang('GiftInfoCollectible', {
|
||||||
|
number: gift.number,
|
||||||
|
});
|
||||||
|
}
|
||||||
if (!canUpdate && !isSender) return undefined;
|
if (!canUpdate && !isSender) return undefined;
|
||||||
if (!starsToConvert || canConvertDifference < 0) return undefined;
|
if (!starsToConvert || canConvertDifference < 0) return undefined;
|
||||||
if (isConverted) {
|
if (isConverted) {
|
||||||
@ -149,14 +185,32 @@ const GiftInfoModal = ({
|
|||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
function getTitle() {
|
||||||
|
if (!userGift) return lang('GiftInfoSoldOutTitle');
|
||||||
|
if (gift?.type === 'starGiftUnique') return gift.title;
|
||||||
|
|
||||||
|
return canUpdate ? lang('GiftInfoReceived') : lang('GiftInfoTitle');
|
||||||
|
}
|
||||||
|
|
||||||
|
const descriptionColor = giftAttributes?.backdrop?.textColor;
|
||||||
|
|
||||||
const header = (
|
const header = (
|
||||||
<div className={styles.header}>
|
<div
|
||||||
<AnimatedIconFromSticker sticker={sticker} noLoop nonInteractive size={STICKER_SIZE} />
|
className={buildClassName(styles.header, radialPatternBackdrop && styles.uniqueGift)}
|
||||||
|
style={buildStyle(descriptionColor && `--_color-description: ${descriptionColor}`)}
|
||||||
|
>
|
||||||
|
{radialPatternBackdrop}
|
||||||
|
<AnimatedIconFromSticker
|
||||||
|
className={styles.giftSticker}
|
||||||
|
sticker={giftSticker}
|
||||||
|
noLoop
|
||||||
|
nonInteractive
|
||||||
|
size={STICKER_SIZE}
|
||||||
|
/>
|
||||||
<h1 className={styles.title}>
|
<h1 className={styles.title}>
|
||||||
{!userGift && lang('GiftInfoSoldOutTitle')}
|
{getTitle()}
|
||||||
{userGift && lang(canUpdate ? 'GiftInfoReceived' : 'GiftInfoTitle')}
|
|
||||||
</h1>
|
</h1>
|
||||||
{userGift && (
|
{gift.type === 'starGift' && (
|
||||||
<p className={styles.amount}>
|
<p className={styles.amount}>
|
||||||
<span className={styles.amount}>
|
<span className={styles.amount}>
|
||||||
{formatInteger(gift.stars)}
|
{formatInteger(gift.stars)}
|
||||||
@ -173,68 +227,187 @@ const GiftInfoModal = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const tableData: TableData = [];
|
const tableData: TableData = [];
|
||||||
if (fromId || isNameHidden) {
|
if (gift.type === 'starGift') {
|
||||||
|
if ((fromId || isNameHidden)) {
|
||||||
|
tableData.push([
|
||||||
|
lang('GiftInfoFrom'),
|
||||||
|
fromId ? { chatId: fromId } : (
|
||||||
|
<>
|
||||||
|
<Avatar size="small" peer={CUSTOM_PEER_HIDDEN} />
|
||||||
|
<span className={styles.unknown}>{oldLang(CUSTOM_PEER_HIDDEN.titleKey!)}</span>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userGift?.date) {
|
||||||
|
tableData.push([
|
||||||
|
lang('GiftInfoDate'),
|
||||||
|
formatDateTimeToString(userGift.date * 1000, lang.code, true),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gift.firstSaleDate) {
|
||||||
|
tableData.push([
|
||||||
|
lang('GiftInfoFirstSale'),
|
||||||
|
formatDateTimeToString(gift.firstSaleDate * 1000, lang.code, true),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gift.lastSaleDate) {
|
||||||
|
tableData.push([
|
||||||
|
lang('GiftInfoLastSale'),
|
||||||
|
formatDateTimeToString(gift.lastSaleDate * 1000, lang.code, true),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
tableData.push([
|
tableData.push([
|
||||||
lang('GiftInfoFrom'),
|
lang('GiftInfoValue'),
|
||||||
fromId ? { chatId: fromId } : (
|
<div className={styles.giftValue}>
|
||||||
<>
|
{formatStarsAsIcon(lang, gift.stars)}
|
||||||
<Avatar size="small" peer={CUSTOM_PEER_HIDDEN} />
|
{canUpdate && canConvertDifference > 0 && Boolean(starsToConvert) && (
|
||||||
<span className={styles.unknown}>{oldLang(CUSTOM_PEER_HIDDEN.titleKey!)}</span>
|
<BadgeButton onClick={openConvertConfirm}>
|
||||||
</>
|
{lang('GiftInfoConvert', { amount: starsToConvert }, { pluralValue: starsToConvert })}
|
||||||
),
|
</BadgeButton>
|
||||||
|
)}
|
||||||
|
</div>,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if (gift.availabilityTotal) {
|
||||||
|
tableData.push([
|
||||||
|
lang('GiftInfoAvailability'),
|
||||||
|
lang('GiftInfoAvailabilityValue', {
|
||||||
|
count: gift.availabilityRemains || 0,
|
||||||
|
total: gift.availabilityTotal,
|
||||||
|
}, {
|
||||||
|
pluralValue: gift.availabilityRemains || 0,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gift.upgradeStars) {
|
||||||
|
tableData.push([
|
||||||
|
lang('GiftInfoStatus'),
|
||||||
|
lang('GiftInfoStatusNonUnique'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userGift?.message) {
|
||||||
|
tableData.push([
|
||||||
|
undefined,
|
||||||
|
renderTextWithEntities(userGift.message),
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userGift?.date) {
|
if (gift.type === 'starGiftUnique') {
|
||||||
|
const {
|
||||||
|
model, backdrop, pattern, originalDetails,
|
||||||
|
} = giftAttributes || {};
|
||||||
tableData.push([
|
tableData.push([
|
||||||
lang('GiftInfoDate'),
|
lang('GiftInfoOwner'),
|
||||||
formatDateTimeToString(userGift.date * 1000, lang.code, true),
|
{ chatId: gift.ownerId },
|
||||||
]);
|
]);
|
||||||
}
|
|
||||||
|
|
||||||
if (gift.firstSaleDate) {
|
if (model) {
|
||||||
tableData.push([
|
tableData.push([
|
||||||
lang('GiftInfoFirstSale'),
|
lang('GiftAttributeModel'),
|
||||||
formatDateTimeToString(gift.firstSaleDate * 1000, lang.code, true),
|
<span className={styles.uniqueAttribute}>
|
||||||
]);
|
{model.name}<BadgeButton>{formatPercent(model.rarityPercent)}</BadgeButton>
|
||||||
}
|
</span>,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
if (gift.lastSaleDate) {
|
if (backdrop) {
|
||||||
tableData.push([
|
tableData.push([
|
||||||
lang('GiftInfoLastSale'),
|
lang('GiftAttributeBackdrop'),
|
||||||
formatDateTimeToString(gift.lastSaleDate * 1000, lang.code, true),
|
<span className={styles.uniqueAttribute}>
|
||||||
]);
|
{backdrop.name}<BadgeButton>{formatPercent(backdrop.rarityPercent)}</BadgeButton>
|
||||||
}
|
</span>,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
tableData.push([
|
if (pattern) {
|
||||||
lang('GiftInfoValue'),
|
tableData.push([
|
||||||
<div className={styles.giftValue}>
|
lang('GiftAttributeSymbol'),
|
||||||
{formatStarsAsIcon(lang, gift.stars)}
|
<span className={styles.uniqueAttribute}>
|
||||||
{canUpdate && canConvertDifference > 0 && Boolean(starsToConvert) && (
|
{pattern.name}<BadgeButton>{formatPercent(pattern.rarityPercent)}</BadgeButton>
|
||||||
<BadgeButton onClick={openConvertConfirm}>
|
</span>,
|
||||||
{lang('GiftInfoConvert', { amount: starsToConvert }, { pluralValue: starsToConvert })}
|
]);
|
||||||
</BadgeButton>
|
}
|
||||||
)}
|
|
||||||
</div>,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (gift.availabilityTotal) {
|
|
||||||
tableData.push([
|
tableData.push([
|
||||||
lang('GiftInfoAvailability'),
|
lang('GiftInfoAvailability'),
|
||||||
lang('GiftInfoAvailabilityValue', {
|
lang('GiftInfoIssued', {
|
||||||
count: gift.availabilityRemains || 0,
|
issued: gift.issuedCount,
|
||||||
total: gift.availabilityTotal,
|
total: gift.totalCount,
|
||||||
}, {
|
|
||||||
pluralValue: gift.availabilityRemains || 0,
|
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
|
||||||
|
|
||||||
if (message) {
|
if (originalDetails) {
|
||||||
tableData.push([
|
const {
|
||||||
undefined,
|
date, recipientId, message, senderId,
|
||||||
renderTextWithEntities(message),
|
} = originalDetails;
|
||||||
]);
|
const global = getGlobal(); // User names does not need to be reactive
|
||||||
|
|
||||||
|
const openChat = (id: string) => {
|
||||||
|
openChatWithInfo({ id });
|
||||||
|
closeGiftInfoModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
const recipient = selectUser(global, recipientId)!;
|
||||||
|
const sender = senderId ? selectUser(global, senderId) : undefined;
|
||||||
|
|
||||||
|
const formattedDate = formatDateTimeToString(date * 1000, lang.code, true);
|
||||||
|
const recipientLink = (
|
||||||
|
// eslint-disable-next-line react/jsx-no-bind
|
||||||
|
<Link onClick={() => openChat(recipientId)} isPrimary>
|
||||||
|
{getUserFullName(recipient)}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
|
||||||
|
let text: TeactNode | undefined;
|
||||||
|
if (!sender || senderId === recipientId) {
|
||||||
|
text = message ? lang('GiftInfoOriginalInfoText', {
|
||||||
|
user: recipientLink,
|
||||||
|
text: renderTextWithEntities(message),
|
||||||
|
date: formattedDate,
|
||||||
|
}, {
|
||||||
|
withNodes: true,
|
||||||
|
}) : lang('GiftInfoOriginalInfo', {
|
||||||
|
user: recipientLink,
|
||||||
|
date: formattedDate,
|
||||||
|
}, {
|
||||||
|
withNodes: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const senderLink = (
|
||||||
|
// eslint-disable-next-line react/jsx-no-bind
|
||||||
|
<Link onClick={() => openChat(sender.id)} isPrimary>
|
||||||
|
{getUserFullName(sender)}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
text = message ? lang('GiftInfoOriginalInfoTextSender', {
|
||||||
|
user: recipientLink,
|
||||||
|
sender: senderLink,
|
||||||
|
text: renderTextWithEntities(message),
|
||||||
|
date: formattedDate,
|
||||||
|
}, {
|
||||||
|
withNodes: true,
|
||||||
|
}) : lang('GiftInfoOriginalInfoSender', {
|
||||||
|
user: recipientLink,
|
||||||
|
date: formattedDate,
|
||||||
|
sender: senderLink,
|
||||||
|
}, {
|
||||||
|
withNodes: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
tableData.push([
|
||||||
|
undefined,
|
||||||
|
<span>{text}</span>,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const footer = (
|
const footer = (
|
||||||
@ -274,7 +447,10 @@ const GiftInfoModal = ({
|
|||||||
tableData,
|
tableData,
|
||||||
footer,
|
footer,
|
||||||
};
|
};
|
||||||
}, [typeGift, userGift, isUserGift, targetUser, sticker, lang, canUpdate, canConvertDifference, isSender, oldLang]);
|
}, [
|
||||||
|
typeGift, userGift, targetUser, giftSticker, lang, canUpdate, canConvertDifference, isSender, oldLang, gift,
|
||||||
|
radialPatternBackdrop, giftAttributes,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -283,6 +459,7 @@ const GiftInfoModal = ({
|
|||||||
header={modalData?.header}
|
header={modalData?.header}
|
||||||
tableData={modalData?.tableData}
|
tableData={modalData?.tableData}
|
||||||
footer={modalData?.footer}
|
footer={modalData?.footer}
|
||||||
|
className={styles.modal}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
/>
|
/>
|
||||||
{userGift && (
|
{userGift && (
|
||||||
@ -323,16 +500,12 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
(global, { modal }): StateProps => {
|
(global, { modal }): StateProps => {
|
||||||
const typeGift = modal?.gift;
|
const typeGift = modal?.gift;
|
||||||
const isUserGift = typeGift && 'gift' in typeGift;
|
const isUserGift = typeGift && 'gift' in typeGift;
|
||||||
const gift = isUserGift ? typeGift.gift : typeGift;
|
|
||||||
const stickerId = gift?.stickerId;
|
|
||||||
const sticker = stickerId ? selectStarGiftSticker(global, stickerId) : undefined;
|
|
||||||
|
|
||||||
const fromId = isUserGift && typeGift.fromId;
|
const fromId = isUserGift && typeGift.fromId;
|
||||||
const userFrom = fromId ? selectUser(global, fromId) : undefined;
|
const userFrom = fromId ? selectUser(global, fromId) : undefined;
|
||||||
const targetUser = modal?.userId ? selectUser(global, modal.userId) : undefined;
|
const targetUser = modal?.userId ? selectUser(global, modal.userId) : undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sticker,
|
|
||||||
userFrom,
|
userFrom,
|
||||||
targetUser,
|
targetUser,
|
||||||
currentUserId: global.currentUserId,
|
currentUserId: global.currentUserId,
|
||||||
|
|||||||
@ -42,7 +42,8 @@ const GiftRecipientPicker: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const displayedUserIds = useMemo(() => {
|
const displayedUserIds = useMemo(() => {
|
||||||
const usersById = getGlobal().users.byId;
|
const usersById = getGlobal().users.byId;
|
||||||
const filteredContactIds = userIds ? filterUsersByName(userIds, usersById, searchQuery) : [];
|
const idsWithSelf = userIds ? userIds.concat(currentUserId!) : undefined;
|
||||||
|
const filteredContactIds = idsWithSelf ? filterUsersByName(idsWithSelf, usersById, searchQuery) : [];
|
||||||
|
|
||||||
return sortChatIds(unique(filteredContactIds).filter((userId) => {
|
return sortChatIds(unique(filteredContactIds).filter((userId) => {
|
||||||
const user = usersById[userId];
|
const user = usersById[userId];
|
||||||
@ -50,8 +51,8 @@ const GiftRecipientPicker: FC<OwnProps & StateProps> = ({
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !isUserBot(user) && userId !== currentUserId;
|
return !isUserBot(user);
|
||||||
}));
|
}), undefined, [currentUserId!]);
|
||||||
}, [currentUserId, searchQuery, userIds]);
|
}, [currentUserId, searchQuery, userIds]);
|
||||||
|
|
||||||
const handleSelectedUserIdsChange = useLastCallback((selectedId: string) => {
|
const handleSelectedUserIdsChange = useLastCallback((selectedId: string) => {
|
||||||
@ -79,6 +80,7 @@ const GiftRecipientPicker: FC<OwnProps & StateProps> = ({
|
|||||||
isSearchable
|
isSearchable
|
||||||
withDefaultPadding
|
withDefaultPadding
|
||||||
withStatus
|
withStatus
|
||||||
|
forceShowSelf
|
||||||
/>
|
/>
|
||||||
</PickerModal>
|
</PickerModal>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -8,6 +8,7 @@ export function getTransactionTitle(lang: OldLangFn, transaction: ApiStarsTransa
|
|||||||
if (transaction.starRefCommision) {
|
if (transaction.starRefCommision) {
|
||||||
return lang('StarTransactionCommission', formatPercent(transaction.starRefCommision));
|
return lang('StarTransactionCommission', formatPercent(transaction.starRefCommision));
|
||||||
}
|
}
|
||||||
|
if (transaction.isGiftUpgrade) return lang('Gift2TransactionUpgraded');
|
||||||
if (transaction.extendedMedia) return lang('StarMediaPurchase');
|
if (transaction.extendedMedia) return lang('StarMediaPurchase');
|
||||||
if (transaction.subscriptionPeriod) return transaction.title || lang('StarSubscriptionPurchase');
|
if (transaction.subscriptionPeriod) return transaction.title || lang('StarSubscriptionPurchase');
|
||||||
if (transaction.isReaction) return lang('StarsReactionsSent');
|
if (transaction.isReaction) return lang('StarsReactionsSent');
|
||||||
|
|||||||
@ -71,6 +71,10 @@ const StarsTransactionItem = ({ transaction, className }: OwnProps) => {
|
|||||||
avatarPeer = customPeer;
|
avatarPeer = customPeer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (transaction.isGiftUpgrade && transaction.starGift?.type === 'starGiftUnique') {
|
||||||
|
description = transaction.starGift.title;
|
||||||
|
}
|
||||||
|
|
||||||
if (transaction.photo) {
|
if (transaction.photo) {
|
||||||
avatarPeer = undefined;
|
avatarPeer = undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,11 +14,12 @@ import { buildStarsTransactionCustomPeer, formatStarsTransactionAmount } from '.
|
|||||||
import {
|
import {
|
||||||
selectCanPlayAnimatedEmojis,
|
selectCanPlayAnimatedEmojis,
|
||||||
selectGiftStickerForStars,
|
selectGiftStickerForStars,
|
||||||
selectPeer, selectStarGiftSticker,
|
selectPeer,
|
||||||
} from '../../../../global/selectors';
|
} from '../../../../global/selectors';
|
||||||
import buildClassName from '../../../../util/buildClassName';
|
import buildClassName from '../../../../util/buildClassName';
|
||||||
import { copyTextToClipboard } from '../../../../util/clipboard';
|
import { copyTextToClipboard } from '../../../../util/clipboard';
|
||||||
import { formatDateTimeToString } from '../../../../util/dates/dateFormat';
|
import { formatDateTimeToString } from '../../../../util/dates/dateFormat';
|
||||||
|
import { getStickerFromGift } from '../../../common/helpers/gifts';
|
||||||
import { getTransactionTitle, isNegativeStarsAmount } from '../helpers/transaction';
|
import { getTransactionTitle, isNegativeStarsAmount } from '../helpers/transaction';
|
||||||
|
|
||||||
import useLang from '../../../../hooks/useLang';
|
import useLang from '../../../../hooks/useLang';
|
||||||
@ -57,6 +58,8 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
const oldLang = useOldLang();
|
const oldLang = useOldLang();
|
||||||
const { transaction } = modal || {};
|
const { transaction } = modal || {};
|
||||||
|
|
||||||
|
const sticker = transaction?.starGift ? getStickerFromGift(transaction.starGift) : topSticker;
|
||||||
|
|
||||||
const handleOpenMedia = useLastCallback(() => {
|
const handleOpenMedia = useLastCallback(() => {
|
||||||
const media = transaction?.extendedMedia;
|
const media = transaction?.extendedMedia;
|
||||||
if (!media) return;
|
if (!media) return;
|
||||||
@ -73,7 +76,7 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
giveawayPostId, photo, stars,
|
giveawayPostId, photo, stars, isGiftUpgrade, starGift,
|
||||||
} = transaction;
|
} = transaction;
|
||||||
|
|
||||||
const customPeer = (transaction.peer && transaction.peer.type !== 'peer'
|
const customPeer = (transaction.peer && transaction.peer.type !== 'peer'
|
||||||
@ -84,7 +87,7 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const title = getTransactionTitle(oldLang, transaction);
|
const title = getTransactionTitle(oldLang, transaction);
|
||||||
|
|
||||||
const messageLink = peer && transaction.messageId
|
const messageLink = peer && transaction.messageId && !isGiftUpgrade
|
||||||
? getMessageLink(peer, undefined, transaction.messageId) : undefined;
|
? getMessageLink(peer, undefined, transaction.messageId) : undefined;
|
||||||
const giveawayMessageLink = peer && giveawayPostId && getMessageLink(peer, undefined, giveawayPostId);
|
const giveawayMessageLink = peer && giveawayPostId && getMessageLink(peer, undefined, giveawayPostId);
|
||||||
|
|
||||||
@ -98,9 +101,11 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
: areAllVideos ? oldLang('Stars.Transfer.Videos', mediaAmount)
|
: areAllVideos ? oldLang('Stars.Transfer.Videos', mediaAmount)
|
||||||
: oldLang('Media', mediaAmount);
|
: oldLang('Media', mediaAmount);
|
||||||
|
|
||||||
const description = transaction.description || (media ? mediaText : undefined);
|
const description = transaction.description
|
||||||
|
|| (isGiftUpgrade && starGift?.type === 'starGiftUnique' ? starGift.title : undefined)
|
||||||
|
|| (media ? mediaText : undefined);
|
||||||
|
|
||||||
const shouldDisplayAvatar = !media && !topSticker;
|
const shouldDisplayAvatar = !media && !sticker;
|
||||||
const avatarPeer = !photo ? (peer || customPeer) : undefined;
|
const avatarPeer = !photo ? (peer || customPeer) : undefined;
|
||||||
|
|
||||||
const header = (
|
const header = (
|
||||||
@ -112,10 +117,10 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
onClick={handleOpenMedia}
|
onClick={handleOpenMedia}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!media && topSticker && (
|
{!media && sticker && (
|
||||||
<AnimatedIconFromSticker
|
<AnimatedIconFromSticker
|
||||||
key={transaction.id}
|
key={transaction.id}
|
||||||
sticker={topSticker}
|
sticker={sticker}
|
||||||
play={canPlayAnimatedEmojis}
|
play={canPlayAnimatedEmojis}
|
||||||
noLoop
|
noLoop
|
||||||
nonInteractive
|
nonInteractive
|
||||||
@ -124,7 +129,7 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
{shouldDisplayAvatar && (
|
{shouldDisplayAvatar && (
|
||||||
<Avatar peer={avatarPeer} webPhoto={photo} size="giant" />
|
<Avatar peer={avatarPeer} webPhoto={photo} size="giant" />
|
||||||
)}
|
)}
|
||||||
{!topSticker && (
|
{!sticker && (
|
||||||
<img
|
<img
|
||||||
className={buildClassName(styles.starsBackground)}
|
className={buildClassName(styles.starsBackground)}
|
||||||
src={StarsBackground}
|
src={StarsBackground}
|
||||||
@ -154,8 +159,17 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isGiftUpgrade) {
|
||||||
|
tableData.push([
|
||||||
|
oldLang('StarGiftReason'),
|
||||||
|
oldLang('StarGiftReasonUpgrade'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
let peerLabel;
|
let peerLabel;
|
||||||
if (isNegativeStarsAmount(stars) || transaction.isMyGift) {
|
if (isGiftUpgrade) {
|
||||||
|
peerLabel = oldLang('Stars.Transaction.GiftFrom');
|
||||||
|
} else if (isNegativeStarsAmount(stars) || transaction.isMyGift) {
|
||||||
peerLabel = oldLang('Stars.Transaction.To');
|
peerLabel = oldLang('Stars.Transaction.To');
|
||||||
} else if (transaction.starRefCommision) {
|
} else if (transaction.starRefCommision) {
|
||||||
peerLabel = oldLang('StarsTransaction.StarRefReason.Miniapp');
|
peerLabel = oldLang('StarsTransaction.StarRefReason.Miniapp');
|
||||||
@ -222,7 +236,7 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
tableData,
|
tableData,
|
||||||
footer,
|
footer,
|
||||||
};
|
};
|
||||||
}, [transaction, oldLang, lang, peer, topSticker, canPlayAnimatedEmojis]);
|
}, [transaction, oldLang, lang, peer, sticker, canPlayAnimatedEmojis]);
|
||||||
|
|
||||||
const prevModalData = usePrevious(starModalData);
|
const prevModalData = usePrevious(starModalData);
|
||||||
const renderingModalData = prevModalData || starModalData;
|
const renderingModalData = prevModalData || starModalData;
|
||||||
@ -248,13 +262,10 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const starCount = modal?.transaction.stars;
|
const starCount = modal?.transaction.stars;
|
||||||
const starsGiftSticker = modal?.transaction.isGift && selectGiftStickerForStars(global, starCount?.amount);
|
const starsGiftSticker = modal?.transaction.isGift && selectGiftStickerForStars(global, starCount?.amount);
|
||||||
|
|
||||||
const starGiftStickerId = modal?.transaction.starGift?.stickerId;
|
|
||||||
const starGiftSticker = starGiftStickerId && selectStarGiftSticker(global, starGiftStickerId);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
peer,
|
peer,
|
||||||
canPlayAnimatedEmojis: selectCanPlayAnimatedEmojis(global),
|
canPlayAnimatedEmojis: selectCanPlayAnimatedEmojis(global),
|
||||||
topSticker: starGiftSticker || starsGiftSticker,
|
topSticker: starsGiftSticker,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(StarsTransactionModal));
|
)(StarsTransactionModal));
|
||||||
|
|||||||
@ -123,6 +123,7 @@ const SuggestedStatusModal = ({ modal, currentUser, bot }: OwnProps & StateProps
|
|||||||
{mockPeerWithStatus && (
|
{mockPeerWithStatus && (
|
||||||
<PeerChip
|
<PeerChip
|
||||||
mockPeer={mockPeerWithStatus}
|
mockPeer={mockPeerWithStatus}
|
||||||
|
withEmojiStatus
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Button size="smaller" onClick={handleSetStatus}>
|
<Button size="smaller" onClick={handleSetStatus}>
|
||||||
|
|||||||
@ -713,7 +713,12 @@ const Profile: FC<OwnProps & StateProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
) : resultType === 'gifts' ? (
|
) : resultType === 'gifts' ? (
|
||||||
(gifts?.map((gift) => (
|
(gifts?.map((gift) => (
|
||||||
<UserGift userId={chatId} key={`${gift.date}-${gift.fromId}-${gift.gift.id}`} gift={gift} />
|
<UserGift
|
||||||
|
userId={chatId}
|
||||||
|
key={`${gift.date}-${gift.fromId}-${gift.gift.id}`}
|
||||||
|
gift={gift}
|
||||||
|
observeIntersection={observeIntersectionForMedia}
|
||||||
|
/>
|
||||||
)))
|
)))
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -89,26 +89,27 @@ addActionHandler('loadStarGifts', async (global): Promise<void> => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { gifts, stickers } = result;
|
const starGiftsById = buildCollectionByKey(result, 'id');
|
||||||
|
|
||||||
const starGiftsById = buildCollectionByKey(gifts, 'id');
|
|
||||||
|
|
||||||
const starGiftCategoriesByName: Record<StarGiftCategory, string[]> = {
|
const starGiftCategoriesByName: Record<StarGiftCategory, string[]> = {
|
||||||
all: [],
|
all: [],
|
||||||
|
stock: [],
|
||||||
limited: [],
|
limited: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
const allStarGiftIds = Object.keys(starGiftsById);
|
const allStarGiftIds = Object.keys(starGiftsById);
|
||||||
const allStarGifts = Object.values(starGiftsById);
|
const allStarGifts = Object.values(starGiftsById);
|
||||||
|
|
||||||
const limitedStarGiftIds = allStarGifts.map(
|
const limitedStarGiftIds = allStarGifts.map((gift) => (gift.isLimited ? gift.id : undefined))
|
||||||
(gift) => {
|
.filter(Boolean) as string[];
|
||||||
return gift.isLimited ? gift.id : undefined;
|
|
||||||
},
|
const stockedStarGiftIds = allStarGifts.map((gift) => (
|
||||||
).filter(Boolean) as string[];
|
gift.availabilityRemains || !gift.availabilityTotal ? gift.id : undefined
|
||||||
|
)).filter(Boolean) as string[];
|
||||||
|
|
||||||
starGiftCategoriesByName.all = allStarGiftIds;
|
starGiftCategoriesByName.all = allStarGiftIds;
|
||||||
starGiftCategoriesByName.limited = limitedStarGiftIds;
|
starGiftCategoriesByName.limited = limitedStarGiftIds;
|
||||||
|
starGiftCategoriesByName.stock = stockedStarGiftIds;
|
||||||
|
|
||||||
allStarGifts.forEach((gift) => {
|
allStarGifts.forEach((gift) => {
|
||||||
const starsCategory = gift.stars;
|
const starsCategory = gift.stars;
|
||||||
@ -123,12 +124,6 @@ addActionHandler('loadStarGifts', async (global): Promise<void> => {
|
|||||||
...global,
|
...global,
|
||||||
starGiftsById,
|
starGiftsById,
|
||||||
starGiftCategoriesByName,
|
starGiftCategoriesByName,
|
||||||
stickers: {
|
|
||||||
...global.stickers,
|
|
||||||
starGifts: {
|
|
||||||
stickers,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -260,11 +260,6 @@ function unsafeMigrateCache(cached: GlobalState, initialState: GlobalState) {
|
|||||||
if (!cached.messages.pollById) {
|
if (!cached.messages.pollById) {
|
||||||
cached.messages.pollById = initialState.messages.pollById;
|
cached.messages.pollById = initialState.messages.pollById;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cached.stickers.starGifts) {
|
|
||||||
cached.stickers.starGifts = initialState.stickers.starGifts;
|
|
||||||
cached.users.giftsById = initialState.users.giftsById;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCache(force?: boolean) {
|
function updateCache(force?: boolean) {
|
||||||
|
|||||||
@ -183,6 +183,7 @@ export const INITIAL_GLOBAL_STATE: GlobalState = {
|
|||||||
starGiftCategoriesByName: {
|
starGiftCategoriesByName: {
|
||||||
all: [],
|
all: [],
|
||||||
limited: [],
|
limited: [],
|
||||||
|
stock: [],
|
||||||
},
|
},
|
||||||
|
|
||||||
stickers: {
|
stickers: {
|
||||||
@ -207,9 +208,6 @@ export const INITIAL_GLOBAL_STATE: GlobalState = {
|
|||||||
stickers: [],
|
stickers: [],
|
||||||
emojis: [],
|
emojis: [],
|
||||||
},
|
},
|
||||||
starGifts: {
|
|
||||||
stickers: {},
|
|
||||||
},
|
|
||||||
forEmoji: {},
|
forEmoji: {},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -171,10 +171,6 @@ export function selectIsAlwaysHighPriorityEmoji<T extends GlobalState>(
|
|||||||
|| stickerSet.id === RESTRICTED_EMOJI_SET_ID;
|
|| stickerSet.id === RESTRICTED_EMOJI_SET_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function selectStarGiftSticker<T extends GlobalState>(global: T, id: string) {
|
|
||||||
return global.stickers.starGifts.stickers[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function selectGiftStickerForDuration<T extends GlobalState>(global: T, duration = 1) {
|
export function selectGiftStickerForDuration<T extends GlobalState>(global: T, duration = 1) {
|
||||||
const stickers = global.premiumGifts?.stickers;
|
const stickers = global.premiumGifts?.stickers;
|
||||||
if (!stickers) return undefined;
|
if (!stickers) return undefined;
|
||||||
|
|||||||
@ -27,7 +27,7 @@ import type {
|
|||||||
ApiSavedReactionTag,
|
ApiSavedReactionTag,
|
||||||
ApiSession,
|
ApiSession,
|
||||||
ApiSponsoredMessage,
|
ApiSponsoredMessage,
|
||||||
ApiStarGift,
|
ApiStarGiftRegular,
|
||||||
ApiStarsAmount,
|
ApiStarsAmount,
|
||||||
ApiStarTopupOption,
|
ApiStarTopupOption,
|
||||||
ApiStealthMode,
|
ApiStealthMode,
|
||||||
@ -290,7 +290,7 @@ export type GlobalState = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
availableEffectById: Record<string, ApiAvailableEffect>;
|
availableEffectById: Record<string, ApiAvailableEffect>;
|
||||||
starGiftsById: Record<string, ApiStarGift>;
|
starGiftsById: Record<string, ApiStarGiftRegular>;
|
||||||
starGiftCategoriesByName: Record<StarGiftCategory, string[]>;
|
starGiftCategoriesByName: Record<StarGiftCategory, string[]>;
|
||||||
|
|
||||||
stickers: {
|
stickers: {
|
||||||
@ -328,9 +328,6 @@ export type GlobalState = {
|
|||||||
stickers: ApiSticker[];
|
stickers: ApiSticker[];
|
||||||
emojis: ApiSticker[];
|
emojis: ApiSticker[];
|
||||||
};
|
};
|
||||||
starGifts: {
|
|
||||||
stickers: Record<string, ApiSticker>;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
customEmojis: {
|
customEmojis: {
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import type {
|
|||||||
ApiPhoto,
|
ApiPhoto,
|
||||||
ApiReaction,
|
ApiReaction,
|
||||||
ApiReactionWithPaid,
|
ApiReactionWithPaid,
|
||||||
ApiStarGift,
|
ApiStarGiftRegular,
|
||||||
ApiStarsSubscription,
|
ApiStarsSubscription,
|
||||||
ApiStarsTransaction,
|
ApiStarsTransaction,
|
||||||
ApiStickerSet,
|
ApiStickerSet,
|
||||||
@ -561,7 +561,7 @@ export type ConfettiStyle = 'poppers' | 'top-down';
|
|||||||
|
|
||||||
export type StarGiftInfo = {
|
export type StarGiftInfo = {
|
||||||
userId: string;
|
userId: string;
|
||||||
gift: ApiStarGift;
|
gift: ApiStarGiftRegular;
|
||||||
shouldHideName?: boolean;
|
shouldHideName?: boolean;
|
||||||
message?: ApiFormattedText;
|
message?: ApiFormattedText;
|
||||||
};
|
};
|
||||||
@ -631,7 +631,7 @@ export type ConfettiParams = OptionalCombine<{
|
|||||||
|
|
||||||
export type WebPageMediaSize = 'large' | 'small';
|
export type WebPageMediaSize = 'large' | 'small';
|
||||||
|
|
||||||
export type StarGiftCategory = number | 'all' | 'limited';
|
export type StarGiftCategory = number | 'all' | 'limited' | 'stock';
|
||||||
|
|
||||||
export type CallSound = (
|
export type CallSound = (
|
||||||
'join' | 'allowTalk' | 'leave' | 'connecting' | 'incoming' | 'end' | 'connect' | 'busy' | 'ringing'
|
'join' | 'allowTalk' | 'leave' | 'connecting' | 'incoming' | 'end' | 'connect' | 'busy' | 'ringing'
|
||||||
|
|||||||
36
src/types/language.d.ts
vendored
36
src/types/language.d.ts
vendored
@ -1108,6 +1108,8 @@ export interface LangPair {
|
|||||||
'GiftPremiumDescriptionLinkCaption': undefined;
|
'GiftPremiumDescriptionLinkCaption': undefined;
|
||||||
'GiftPremiumDescriptionLink': undefined;
|
'GiftPremiumDescriptionLink': undefined;
|
||||||
'StarsGiftHeader': undefined;
|
'StarsGiftHeader': undefined;
|
||||||
|
'StarsGiftHeaderSelf': undefined;
|
||||||
|
'StarGiftDescriptionSelf': undefined;
|
||||||
'GiftLimited': undefined;
|
'GiftLimited': undefined;
|
||||||
'GiftSoldOut': undefined;
|
'GiftSoldOut': undefined;
|
||||||
'GiftMessagePlaceholder': undefined;
|
'GiftMessagePlaceholder': undefined;
|
||||||
@ -1130,8 +1132,15 @@ export interface LangPair {
|
|||||||
'GiftInfoSoldOutTitle': undefined;
|
'GiftInfoSoldOutTitle': undefined;
|
||||||
'GiftInfoSoldOutDescription': undefined;
|
'GiftInfoSoldOutDescription': undefined;
|
||||||
'GiftInfoSenderHidden': undefined;
|
'GiftInfoSenderHidden': undefined;
|
||||||
|
'GiftInfoOwner': undefined;
|
||||||
|
'GiftAttributeModel': undefined;
|
||||||
|
'GiftAttributeBackdrop': undefined;
|
||||||
|
'GiftAttributeSymbol': undefined;
|
||||||
|
'GiftInfoStatus': undefined;
|
||||||
|
'GiftInfoStatusNonUnique': undefined;
|
||||||
'AllGiftsCategory': undefined;
|
'AllGiftsCategory': undefined;
|
||||||
'LimitedGiftsCategory': undefined;
|
'LimitedGiftsCategory': undefined;
|
||||||
|
'StockGiftsCategory': undefined;
|
||||||
'PremiumGiftDescription': undefined;
|
'PremiumGiftDescription': undefined;
|
||||||
'StarsReactionLinkText': undefined;
|
'StarsReactionLinkText': undefined;
|
||||||
'StarsReactionLink': undefined;
|
'StarsReactionLink': undefined;
|
||||||
@ -1547,6 +1556,33 @@ export interface LangPairWithVariables<V extends unknown = LangVariable> {
|
|||||||
'GiftInfoSaved': {
|
'GiftInfoSaved': {
|
||||||
'link': V;
|
'link': V;
|
||||||
};
|
};
|
||||||
|
'GiftInfoIssued': {
|
||||||
|
'issued': V;
|
||||||
|
'total': V;
|
||||||
|
};
|
||||||
|
'GiftInfoCollectible': {
|
||||||
|
'number': V;
|
||||||
|
};
|
||||||
|
'GiftInfoOriginalInfo': {
|
||||||
|
'user': V;
|
||||||
|
'date': V;
|
||||||
|
};
|
||||||
|
'GiftInfoOriginalInfoSender': {
|
||||||
|
'sender': V;
|
||||||
|
'user': V;
|
||||||
|
'date': V;
|
||||||
|
};
|
||||||
|
'GiftInfoOriginalInfoText': {
|
||||||
|
'user': V;
|
||||||
|
'date': V;
|
||||||
|
'text': V;
|
||||||
|
};
|
||||||
|
'GiftInfoOriginalInfoTextSender': {
|
||||||
|
'sender': V;
|
||||||
|
'user': V;
|
||||||
|
'date': V;
|
||||||
|
'text': V;
|
||||||
|
};
|
||||||
'StarsAmount': {
|
'StarsAmount': {
|
||||||
'amount': V;
|
'amount': V;
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user