Layer 181: Telegram Stars, Collapsible quotes, Fact Check (#4637)
This commit is contained in:
parent
9034a66c9c
commit
46c85ebb88
@ -260,6 +260,15 @@ export function buildApiMessageEntity(entity: GramJs.TypeMessageEntity): ApiMess
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (entity instanceof GramJs.MessageEntityBlockquote) {
|
||||||
|
return {
|
||||||
|
type: ApiMessageEntityTypes.Blockquote,
|
||||||
|
canCollapse: entity.collapsed,
|
||||||
|
offset,
|
||||||
|
length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: type as `${ApiMessageEntityDefault['type']}`,
|
type: type as `${ApiMessageEntityDefault['type']}`,
|
||||||
offset,
|
offset,
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import type {
|
|||||||
ApiAttachment,
|
ApiAttachment,
|
||||||
ApiChat,
|
ApiChat,
|
||||||
ApiContact,
|
ApiContact,
|
||||||
|
ApiFactCheck,
|
||||||
ApiGroupCall,
|
ApiGroupCall,
|
||||||
ApiInputMessageReplyInfo,
|
ApiInputMessageReplyInfo,
|
||||||
ApiInputReplyInfo,
|
ApiInputReplyInfo,
|
||||||
@ -52,6 +53,7 @@ import {
|
|||||||
} from '../helpers';
|
} from '../helpers';
|
||||||
import { buildApiCallDiscardReason } from './calls';
|
import { buildApiCallDiscardReason } from './calls';
|
||||||
import {
|
import {
|
||||||
|
buildApiFormattedText,
|
||||||
buildApiPhoto,
|
buildApiPhoto,
|
||||||
} from './common';
|
} from './common';
|
||||||
import { buildMessageContent, buildMessageMediaContent, buildMessageTextContent } from './messageContent';
|
import { buildMessageContent, buildMessageMediaContent, buildMessageTextContent } from './messageContent';
|
||||||
@ -149,12 +151,7 @@ export function buildApiMessageFromNotification(
|
|||||||
|
|
||||||
export type UniversalMessage = (
|
export type UniversalMessage = (
|
||||||
Pick<GramJs.Message & GramJs.MessageService, ('id' | 'date')>
|
Pick<GramJs.Message & GramJs.MessageService, ('id' | 'date')>
|
||||||
& Pick<Partial<GramJs.Message & GramJs.MessageService>, (
|
& Partial<GramJs.Message & GramJs.MessageService>
|
||||||
'out' | 'message' | 'entities' | 'fromId' | 'peerId' | 'fwdFrom' | 'replyTo' | 'replyMarkup' | 'post' |
|
|
||||||
'media' | 'action' | 'views' | 'editDate' | 'editHide' | 'mediaUnread' | 'groupedId' | 'mentioned' | 'viaBotId' |
|
|
||||||
'replies' | 'fromScheduled' | 'postAuthor' | 'noforwards' | 'reactions' | 'forwards' | 'silent' | 'pinned' |
|
|
||||||
'savedPeerId' | 'fromBoostsApplied' | 'quickReplyShortcutId' | 'viaBusinessBotId'
|
|
||||||
)>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export function buildApiMessageWithChatId(
|
export function buildApiMessageWithChatId(
|
||||||
@ -192,6 +189,7 @@ export function buildApiMessageWithChatId(
|
|||||||
const emojiOnlyCount = getEmojiOnlyCountForMessage(content, groupedId);
|
const emojiOnlyCount = getEmojiOnlyCountForMessage(content, groupedId);
|
||||||
const hasComments = mtpMessage.replies?.comments;
|
const hasComments = mtpMessage.replies?.comments;
|
||||||
const senderBoosts = mtpMessage.fromBoostsApplied;
|
const senderBoosts = mtpMessage.fromBoostsApplied;
|
||||||
|
const factCheck = mtpMessage.factcheck && buildApiFactCheck(mtpMessage.factcheck);
|
||||||
|
|
||||||
const savedPeerId = mtpMessage.savedPeerId && getApiChatIdFromMtpPeer(mtpMessage.savedPeerId);
|
const savedPeerId = mtpMessage.savedPeerId && getApiChatIdFromMtpPeer(mtpMessage.savedPeerId);
|
||||||
|
|
||||||
@ -234,6 +232,7 @@ export function buildApiMessageWithChatId(
|
|||||||
savedPeerId,
|
savedPeerId,
|
||||||
senderBoosts,
|
senderBoosts,
|
||||||
viaBusinessBotId: mtpMessage.viaBusinessBotId?.toString(),
|
viaBusinessBotId: mtpMessage.viaBusinessBotId?.toString(),
|
||||||
|
factCheck,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,6 +318,15 @@ function buildApiReplyInfo(replyHeader: GramJs.TypeMessageReplyHeader): ApiReply
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildApiFactCheck(factCheck: GramJs.FactCheck): ApiFactCheck {
|
||||||
|
return {
|
||||||
|
shouldFetch: factCheck.needCheck,
|
||||||
|
hash: factCheck.hash.toString(),
|
||||||
|
text: factCheck.text && buildApiFormattedText(factCheck.text),
|
||||||
|
countryCode: factCheck.country,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function buildAction(
|
function buildAction(
|
||||||
action: GramJs.TypeMessageAction,
|
action: GramJs.TypeMessageAction,
|
||||||
senderId: string | undefined,
|
senderId: string | undefined,
|
||||||
@ -450,6 +458,7 @@ function buildAction(
|
|||||||
amount = Number(action.totalAmount);
|
amount = Number(action.totalAmount);
|
||||||
currency = action.currency;
|
currency = action.currency;
|
||||||
text = 'PaymentSuccessfullyPaid';
|
text = 'PaymentSuccessfullyPaid';
|
||||||
|
type = 'receipt';
|
||||||
if (targetPeerId) {
|
if (targetPeerId) {
|
||||||
targetUserIds.push(targetPeerId);
|
targetUserIds.push(targetPeerId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,8 +9,12 @@ import type {
|
|||||||
ApiInvoice, ApiLabeledPrice, ApiMyBoost, ApiPaymentCredentials,
|
ApiInvoice, ApiLabeledPrice, ApiMyBoost, ApiPaymentCredentials,
|
||||||
ApiPaymentForm, ApiPaymentSavedInfo, ApiPremiumGiftCodeOption, ApiPremiumPromo, ApiPremiumSubscriptionOption,
|
ApiPaymentForm, ApiPaymentSavedInfo, ApiPremiumGiftCodeOption, ApiPremiumPromo, ApiPremiumSubscriptionOption,
|
||||||
ApiReceipt,
|
ApiReceipt,
|
||||||
|
ApiStarsTransaction,
|
||||||
|
ApiStarsTransactionPeer,
|
||||||
|
ApiStarTopupOption,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
|
import { addWebDocumentToLocalDb } from '../helpers';
|
||||||
import { buildApiMessageEntity } from './common';
|
import { buildApiMessageEntity } from './common';
|
||||||
import { omitVirtualClassFields } from './helpers';
|
import { omitVirtualClassFields } from './helpers';
|
||||||
import { buildApiDocument, buildApiWebDocument } from './messageContent';
|
import { buildApiDocument, buildApiWebDocument } from './messageContent';
|
||||||
@ -37,7 +41,35 @@ export function buildShippingOptions(shippingOptions: GramJs.ShippingOption[] |
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildApiReceipt(receipt: GramJs.payments.PaymentReceipt): ApiReceipt {
|
export function buildApiReceipt(receipt: GramJs.payments.TypePaymentReceipt): ApiReceipt {
|
||||||
|
const { photo } = receipt;
|
||||||
|
|
||||||
|
if (photo) {
|
||||||
|
addWebDocumentToLocalDb(photo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receipt instanceof GramJs.payments.PaymentReceiptStars) {
|
||||||
|
const {
|
||||||
|
botId, currency, date, description: text, title, totalAmount, transactionId,
|
||||||
|
} = receipt;
|
||||||
|
|
||||||
|
if (photo) {
|
||||||
|
addWebDocumentToLocalDb(photo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'stars',
|
||||||
|
currency,
|
||||||
|
botId: buildApiPeerId(botId, 'user'),
|
||||||
|
date,
|
||||||
|
text,
|
||||||
|
title,
|
||||||
|
totalAmount: -totalAmount.toJSNumber(),
|
||||||
|
transactionId,
|
||||||
|
photo: photo && buildApiWebDocument(photo),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
invoice,
|
invoice,
|
||||||
info,
|
info,
|
||||||
@ -46,6 +78,8 @@ export function buildApiReceipt(receipt: GramJs.payments.PaymentReceipt): ApiRec
|
|||||||
totalAmount,
|
totalAmount,
|
||||||
credentialsTitle,
|
credentialsTitle,
|
||||||
tipAmount,
|
tipAmount,
|
||||||
|
title,
|
||||||
|
description: text,
|
||||||
} = receipt;
|
} = receipt;
|
||||||
|
|
||||||
const { shippingAddress, phone, name } = (info || {});
|
const { shippingAddress, phone, name } = (info || {});
|
||||||
@ -70,6 +104,7 @@ export function buildApiReceipt(receipt: GramJs.payments.PaymentReceipt): ApiRec
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
type: 'regular',
|
||||||
currency,
|
currency,
|
||||||
prices: mappedPrices,
|
prices: mappedPrices,
|
||||||
info: { shippingAddress, phone, name },
|
info: { shippingAddress, phone, name },
|
||||||
@ -78,10 +113,23 @@ export function buildApiReceipt(receipt: GramJs.payments.PaymentReceipt): ApiRec
|
|||||||
shippingPrices,
|
shippingPrices,
|
||||||
shippingMethod,
|
shippingMethod,
|
||||||
tipAmount: tipAmount ? tipAmount.toJSNumber() : 0,
|
tipAmount: tipAmount ? tipAmount.toJSNumber() : 0,
|
||||||
|
title,
|
||||||
|
text,
|
||||||
|
photo: photo && buildApiWebDocument(photo),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildApiPaymentForm(form: GramJs.payments.PaymentForm): ApiPaymentForm {
|
export function buildApiPaymentForm(form: GramJs.payments.TypePaymentForm): ApiPaymentForm {
|
||||||
|
if (form instanceof GramJs.payments.PaymentFormStars) {
|
||||||
|
const { botId, formId } = form;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'stars',
|
||||||
|
botId: buildApiPeerId(botId, 'user'),
|
||||||
|
formId: String(formId),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
formId,
|
formId,
|
||||||
canSaveCredentials,
|
canSaveCredentials,
|
||||||
@ -93,6 +141,7 @@ export function buildApiPaymentForm(form: GramJs.payments.PaymentForm): ApiPayme
|
|||||||
invoice,
|
invoice,
|
||||||
savedCredentials,
|
savedCredentials,
|
||||||
url,
|
url,
|
||||||
|
botId,
|
||||||
} = form;
|
} = form;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -121,7 +170,9 @@ export function buildApiPaymentForm(form: GramJs.payments.PaymentForm): ApiPayme
|
|||||||
const nativeData = nativeParams ? JSON.parse(nativeParams.data) : {};
|
const nativeData = nativeParams ? JSON.parse(nativeParams.data) : {};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
type: 'regular',
|
||||||
url,
|
url,
|
||||||
|
botId: buildApiPeerId(botId, 'user'),
|
||||||
canSaveCredentials,
|
canSaveCredentials,
|
||||||
isPasswordMissing,
|
isPasswordMissing,
|
||||||
formId: String(formId),
|
formId: String(formId),
|
||||||
@ -146,12 +197,13 @@ export function buildApiPaymentForm(form: GramJs.payments.PaymentForm): ApiPayme
|
|||||||
needZip: Boolean(nativeData?.need_zip),
|
needZip: Boolean(nativeData?.need_zip),
|
||||||
publishableKey: nativeData?.publishable_key,
|
publishableKey: nativeData?.publishable_key,
|
||||||
publicToken: nativeData?.public_token,
|
publicToken: nativeData?.public_token,
|
||||||
|
tokenizeUrl: nativeData?.tokenize_url,
|
||||||
},
|
},
|
||||||
...(savedCredentials && { savedCredentials: buildApiPaymentCredentials(savedCredentials) }),
|
savedCredentials: savedCredentials && buildApiPaymentCredentials(savedCredentials),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildApiInvoiceFromForm(form: GramJs.payments.PaymentForm): ApiInvoice {
|
export function buildApiInvoiceFromForm(form: GramJs.payments.TypePaymentForm): ApiInvoice {
|
||||||
const {
|
const {
|
||||||
invoice, description: text, title, photo,
|
invoice, description: text, title, photo,
|
||||||
} = form;
|
} = form;
|
||||||
@ -328,3 +380,61 @@ export function buildApiPremiumGiftCodeOption(option: GramJs.PremiumGiftCodeOpti
|
|||||||
users,
|
users,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildApiStarsTransactionPeer(peer: GramJs.TypeStarsTransactionPeer): ApiStarsTransactionPeer {
|
||||||
|
if (peer instanceof GramJs.StarsTransactionPeerAppStore) {
|
||||||
|
return { type: 'appStore' };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peer instanceof GramJs.StarsTransactionPeerPlayMarket) {
|
||||||
|
return { type: 'playMarket' };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peer instanceof GramJs.StarsTransactionPeerPremiumBot) {
|
||||||
|
return { type: 'premiumBot' };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peer instanceof GramJs.StarsTransactionPeerFragment) {
|
||||||
|
return { type: 'fragment' };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peer instanceof GramJs.StarsTransactionPeer) {
|
||||||
|
return { type: 'peer', id: getApiChatIdFromMtpPeer(peer.peer) };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { type: 'unsupported' };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildApiStarsTransaction(transaction: GramJs.StarsTransaction): ApiStarsTransaction {
|
||||||
|
const {
|
||||||
|
date, id, peer, stars, description, photo, title, refund,
|
||||||
|
} = transaction;
|
||||||
|
|
||||||
|
if (photo) {
|
||||||
|
addWebDocumentToLocalDb(photo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
date,
|
||||||
|
peer: buildApiStarsTransactionPeer(peer),
|
||||||
|
stars: stars.toJSNumber(),
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
photo: photo && buildApiWebDocument(photo),
|
||||||
|
isRefund: refund,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildApiStarTopupOption(option: GramJs.TypeStarsTopupOption): ApiStarTopupOption {
|
||||||
|
const {
|
||||||
|
amount, currency, stars, extended,
|
||||||
|
} = option;
|
||||||
|
|
||||||
|
return {
|
||||||
|
amount: amount.toJSNumber(),
|
||||||
|
currency,
|
||||||
|
stars: stars.toJSNumber(),
|
||||||
|
isExtended: extended,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import type {
|
|||||||
ApiReportReason,
|
ApiReportReason,
|
||||||
ApiRequestInputInvoice,
|
ApiRequestInputInvoice,
|
||||||
ApiSendMessageAction,
|
ApiSendMessageAction,
|
||||||
|
ApiStarTopupOption,
|
||||||
ApiSticker,
|
ApiSticker,
|
||||||
ApiStory,
|
ApiStory,
|
||||||
ApiStorySkipped,
|
ApiStorySkipped,
|
||||||
@ -604,6 +605,15 @@ function buildPremiumGiftCodeOption(optionData: ApiPremiumGiftCodeOption) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildInputStarsTopupOption(option: ApiStarTopupOption) {
|
||||||
|
return new GramJs.StarsTopupOption({
|
||||||
|
stars: BigInt(option.stars),
|
||||||
|
amount: BigInt(option.amount),
|
||||||
|
currency: option.currency,
|
||||||
|
extended: option.isExtended,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function buildInputInvoice(invoice: ApiRequestInputInvoice) {
|
export function buildInputInvoice(invoice: ApiRequestInputInvoice) {
|
||||||
switch (invoice.type) {
|
switch (invoice.type) {
|
||||||
case 'message': {
|
case 'message': {
|
||||||
@ -619,6 +629,12 @@ export function buildInputInvoice(invoice: ApiRequestInputInvoice) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'stars': {
|
||||||
|
return new GramJs.InputInvoiceStars({
|
||||||
|
option: buildInputStarsTopupOption(invoice.option),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
case 'giveaway':
|
case 'giveaway':
|
||||||
default: {
|
default: {
|
||||||
const purpose = buildInputStorePaymentPurpose(invoice.purpose);
|
const purpose = buildInputStorePaymentPurpose(invoice.purpose);
|
||||||
|
|||||||
@ -83,9 +83,8 @@ function addMediaToLocalDb(media: GramJs.TypeMessageMedia) {
|
|||||||
addPhotoToLocalDb(media.game.photo);
|
addPhotoToLocalDb(media.game.photo);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (media instanceof GramJs.MessageMediaInvoice
|
if (media instanceof GramJs.MessageMediaInvoice && media.photo) {
|
||||||
&& media.photo) {
|
addWebDocumentToLocalDb(media.photo);
|
||||||
localDb.webDocuments[String(media.photo.url)] = media.photo;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,6 +153,10 @@ export function addEntitiesToLocalDb(entities: (GramJs.TypeUser | GramJs.TypeCha
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function addWebDocumentToLocalDb(webDocument: GramJs.TypeWebDocument) {
|
||||||
|
localDb.webDocuments[webDocument.url] = webDocument;
|
||||||
|
}
|
||||||
|
|
||||||
export function swapLocalInvoiceMedia(
|
export function swapLocalInvoiceMedia(
|
||||||
chatId: string, messageId: number, extendedMedia: GramJs.TypeMessageExtendedMedia,
|
chatId: string, messageId: number, extendedMedia: GramJs.TypeMessageExtendedMedia,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -33,7 +33,9 @@ import {
|
|||||||
buildInputThemeParams,
|
buildInputThemeParams,
|
||||||
generateRandomBigInt,
|
generateRandomBigInt,
|
||||||
} from '../gramjsBuilders';
|
} from '../gramjsBuilders';
|
||||||
import { addEntitiesToLocalDb, addUserToLocalDb, deserializeBytes } from '../helpers';
|
import {
|
||||||
|
addEntitiesToLocalDb, addUserToLocalDb, addWebDocumentToLocalDb, deserializeBytes,
|
||||||
|
} from '../helpers';
|
||||||
import localDb from '../localDb';
|
import localDb from '../localDb';
|
||||||
import { invokeRequest } from './client';
|
import { invokeRequest } from './client';
|
||||||
|
|
||||||
@ -561,10 +563,6 @@ function addPhotoToLocalDb(photo: GramJs.Photo) {
|
|||||||
localDb.photos[String(photo.id)] = photo;
|
localDb.photos[String(photo.id)] = photo;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addWebDocumentToLocalDb(webDocument: GramJs.TypeWebDocument) {
|
|
||||||
localDb.webDocuments[webDocument.url] = webDocument;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setBotInfo({
|
export function setBotInfo({
|
||||||
bot,
|
bot,
|
||||||
langCode,
|
langCode,
|
||||||
|
|||||||
@ -33,7 +33,7 @@ export {
|
|||||||
reportMessages, sendMessageAction, fetchSeenBy, fetchSponsoredMessages, viewSponsoredMessage, fetchSendAs,
|
reportMessages, sendMessageAction, fetchSeenBy, fetchSponsoredMessages, viewSponsoredMessage, fetchSendAs,
|
||||||
saveDefaultSendAs, fetchUnreadReactions, readAllReactions, fetchUnreadMentions, readAllMentions, transcribeAudio,
|
saveDefaultSendAs, fetchUnreadReactions, readAllReactions, fetchUnreadMentions, readAllMentions, transcribeAudio,
|
||||||
closePoll, fetchExtendedMedia, translateText, fetchMessageViews, fetchDiscussionMessage, clickSponsoredMessage,
|
closePoll, fetchExtendedMedia, translateText, fetchMessageViews, fetchDiscussionMessage, clickSponsoredMessage,
|
||||||
fetchOutboxReadDate, exportMessageLink, fetchQuickReplies, sendQuickReply,
|
fetchOutboxReadDate, exportMessageLink, fetchQuickReplies, sendQuickReply, fetchFactChecks,
|
||||||
deleteSavedHistory,
|
deleteSavedHistory,
|
||||||
} from './messages';
|
} from './messages';
|
||||||
|
|
||||||
@ -100,7 +100,8 @@ export * from './stories';
|
|||||||
export {
|
export {
|
||||||
validateRequestedInfo, sendPaymentForm, getPaymentForm, getReceipt, fetchPremiumPromo, fetchTemporaryPaymentPassword,
|
validateRequestedInfo, sendPaymentForm, getPaymentForm, getReceipt, fetchPremiumPromo, fetchTemporaryPaymentPassword,
|
||||||
applyBoost, fetchBoostList, fetchBoostStatus, fetchGiveawayInfo, fetchMyBoosts, applyGiftCode, checkGiftCode,
|
applyBoost, fetchBoostList, fetchBoostStatus, fetchGiveawayInfo, fetchMyBoosts, applyGiftCode, checkGiftCode,
|
||||||
getPremiumGiftCodeOptions, launchPrepaidGiveaway,
|
getPremiumGiftCodeOptions, launchPrepaidGiveaway, fetchStarsStatus, fetchStarsTopupOptions, fetchStarsTransactions,
|
||||||
|
sendStarPaymentForm,
|
||||||
} from './payments';
|
} from './payments';
|
||||||
|
|
||||||
export * from './fragment';
|
export * from './fragment';
|
||||||
|
|||||||
@ -49,6 +49,7 @@ import { buildApiChatFromPreview, buildApiSendAsPeerId } from '../apiBuilders/ch
|
|||||||
import { buildApiFormattedText } from '../apiBuilders/common';
|
import { buildApiFormattedText } from '../apiBuilders/common';
|
||||||
import { buildMessageMediaContent, buildMessageTextContent, buildWebPage } from '../apiBuilders/messageContent';
|
import { buildMessageMediaContent, buildMessageTextContent, buildWebPage } from '../apiBuilders/messageContent';
|
||||||
import {
|
import {
|
||||||
|
buildApiFactCheck,
|
||||||
buildApiMessage,
|
buildApiMessage,
|
||||||
buildApiQuickReply,
|
buildApiQuickReply,
|
||||||
buildApiSponsoredMessage,
|
buildApiSponsoredMessage,
|
||||||
@ -1047,6 +1048,25 @@ export async function fetchMessageViews({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchFactChecks({
|
||||||
|
chat, ids,
|
||||||
|
}: {
|
||||||
|
chat: ApiChat;
|
||||||
|
ids: number[];
|
||||||
|
}) {
|
||||||
|
const chunks = split(ids, API_GENERAL_ID_LIMIT);
|
||||||
|
const results = await Promise.all(chunks.map((chunkIds) => (
|
||||||
|
invokeRequest(new GramJs.messages.GetFactCheck({
|
||||||
|
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||||
|
msgId: chunkIds,
|
||||||
|
}))
|
||||||
|
)));
|
||||||
|
|
||||||
|
if (!results || results.some((result) => !result)) return undefined;
|
||||||
|
|
||||||
|
return results.flatMap((result) => result!).map(buildApiFactCheck);
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchDiscussionMessage({
|
export async function fetchDiscussionMessage({
|
||||||
chat, messageId,
|
chat, messageId,
|
||||||
}: {
|
}: {
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import type {
|
|||||||
OnApiUpdate,
|
OnApiUpdate,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
|
import { DEBUG } from '../../../config';
|
||||||
import { buildApiChatFromPreview } from '../apiBuilders/chats';
|
import { buildApiChatFromPreview } from '../apiBuilders/chats';
|
||||||
import {
|
import {
|
||||||
buildApiBoost,
|
buildApiBoost,
|
||||||
@ -18,6 +19,8 @@ import {
|
|||||||
buildApiPremiumGiftCodeOption,
|
buildApiPremiumGiftCodeOption,
|
||||||
buildApiPremiumPromo,
|
buildApiPremiumPromo,
|
||||||
buildApiReceipt,
|
buildApiReceipt,
|
||||||
|
buildApiStarsTransaction,
|
||||||
|
buildApiStarTopupOption,
|
||||||
buildShippingOptions,
|
buildShippingOptions,
|
||||||
} from '../apiBuilders/payments';
|
} from '../apiBuilders/payments';
|
||||||
import { buildApiUser } from '../apiBuilders/users';
|
import { buildApiUser } from '../apiBuilders/users';
|
||||||
@ -26,6 +29,7 @@ import {
|
|||||||
} from '../gramjsBuilders';
|
} from '../gramjsBuilders';
|
||||||
import {
|
import {
|
||||||
addEntitiesToLocalDb,
|
addEntitiesToLocalDb,
|
||||||
|
addWebDocumentToLocalDb,
|
||||||
deserializeBytes,
|
deserializeBytes,
|
||||||
serializeBytes,
|
serializeBytes,
|
||||||
} from '../helpers';
|
} from '../helpers';
|
||||||
@ -124,6 +128,34 @@ export async function sendPaymentForm({
|
|||||||
return Boolean(result);
|
return Boolean(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function sendStarPaymentForm({
|
||||||
|
formId,
|
||||||
|
inputInvoice,
|
||||||
|
}: {
|
||||||
|
formId: string;
|
||||||
|
inputInvoice: ApiRequestInputInvoice;
|
||||||
|
}) {
|
||||||
|
const result = await invokeRequest(new GramJs.payments.SendStarsForm({
|
||||||
|
formId: BigInt(formId),
|
||||||
|
invoice: buildInputInvoice(inputInvoice),
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!result) return false;
|
||||||
|
|
||||||
|
if (result instanceof GramJs.payments.PaymentVerificationNeeded) {
|
||||||
|
if (DEBUG) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.warn('Unexpected PaymentVerificationNeeded in sendStarsForm');
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
} else {
|
||||||
|
handleGramJsUpdate(result.updates);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Boolean(result);
|
||||||
|
}
|
||||||
|
|
||||||
export async function getPaymentForm(inputInvoice: ApiRequestInputInvoice) {
|
export async function getPaymentForm(inputInvoice: ApiRequestInputInvoice) {
|
||||||
const result = await invokeRequest(new GramJs.payments.GetPaymentForm({
|
const result = await invokeRequest(new GramJs.payments.GetPaymentForm({
|
||||||
invoice: buildInputInvoice(inputInvoice),
|
invoice: buildInputInvoice(inputInvoice),
|
||||||
@ -134,7 +166,7 @@ export async function getPaymentForm(inputInvoice: ApiRequestInputInvoice) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (result.photo) {
|
if (result.photo) {
|
||||||
localDb.webDocuments[result.photo.url] = result.photo;
|
addWebDocumentToLocalDb(result.photo);
|
||||||
}
|
}
|
||||||
|
|
||||||
addEntitiesToLocalDb(result.users);
|
addEntitiesToLocalDb(result.users);
|
||||||
@ -143,7 +175,6 @@ export async function getPaymentForm(inputInvoice: ApiRequestInputInvoice) {
|
|||||||
form: buildApiPaymentForm(result),
|
form: buildApiPaymentForm(result),
|
||||||
invoice: buildApiInvoiceFromForm(result),
|
invoice: buildApiInvoiceFromForm(result),
|
||||||
users: result.users.map(buildApiUser).filter(Boolean),
|
users: result.users.map(buildApiUser).filter(Boolean),
|
||||||
botId: result.botId.toString(),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,3 +418,66 @@ export function launchPrepaidGiveaway({
|
|||||||
shouldReturnTrue: true,
|
shouldReturnTrue: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchStarsStatus() {
|
||||||
|
const result = await invokeRequest(new GramJs.payments.GetStarsStatus({
|
||||||
|
peer: new GramJs.InputPeerSelf(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const users = result.users.map(buildApiUser).filter(Boolean);
|
||||||
|
const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean);
|
||||||
|
|
||||||
|
return {
|
||||||
|
users,
|
||||||
|
chats,
|
||||||
|
nextOffset: result.nextOffset,
|
||||||
|
history: result.history.map(buildApiStarsTransaction),
|
||||||
|
balance: result.balance.toJSNumber(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchStarsTransactions({
|
||||||
|
offset,
|
||||||
|
isInbound,
|
||||||
|
isOutbound,
|
||||||
|
}: {
|
||||||
|
offset?: string;
|
||||||
|
isInbound?: true;
|
||||||
|
isOutbound?: true;
|
||||||
|
}) {
|
||||||
|
const result = await invokeRequest(new GramJs.payments.GetStarsTransactions({
|
||||||
|
peer: new GramJs.InputPeerSelf(),
|
||||||
|
offset,
|
||||||
|
inbound: isInbound,
|
||||||
|
outbound: isOutbound,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const users = result.users.map(buildApiUser).filter(Boolean);
|
||||||
|
const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean);
|
||||||
|
|
||||||
|
return {
|
||||||
|
users,
|
||||||
|
chats,
|
||||||
|
nextOffset: result.nextOffset,
|
||||||
|
history: result.history.map(buildApiStarsTransaction),
|
||||||
|
balance: result.balance.toJSNumber(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchStarsTopupOptions() {
|
||||||
|
const result = await invokeRequest(new GramJs.payments.GetStarsTopupOptions());
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.map(buildApiStarTopupOption);
|
||||||
|
}
|
||||||
|
|||||||
@ -244,12 +244,7 @@ export function updater(update: Update) {
|
|||||||
if (update.message instanceof GramJs.MessageService) {
|
if (update.message instanceof GramJs.MessageService) {
|
||||||
const { action } = update.message;
|
const { action } = update.message;
|
||||||
|
|
||||||
if (action instanceof GramJs.MessageActionPaymentSent) {
|
if (action instanceof GramJs.MessageActionChatEditTitle) {
|
||||||
onUpdate({
|
|
||||||
'@type': 'updatePaymentStateCompleted',
|
|
||||||
slug: action.invoiceSlug,
|
|
||||||
});
|
|
||||||
} else if (action instanceof GramJs.MessageActionChatEditTitle) {
|
|
||||||
onUpdate({
|
onUpdate({
|
||||||
'@type': 'updateChat',
|
'@type': 'updateChat',
|
||||||
id: message.chatId,
|
id: message.chatId,
|
||||||
@ -1194,6 +1189,11 @@ export function updater(update: Update) {
|
|||||||
chatId: buildApiPeerId(update.channelId, 'channel'),
|
chatId: buildApiPeerId(update.channelId, 'channel'),
|
||||||
isEnabled: update.enabled ? true : undefined,
|
isEnabled: update.enabled ? true : undefined,
|
||||||
});
|
});
|
||||||
|
} else if (update instanceof GramJs.UpdateStarsBalance) {
|
||||||
|
onUpdate({
|
||||||
|
'@type': 'updateStarsBalance',
|
||||||
|
balance: update.balance.toJSNumber(),
|
||||||
|
});
|
||||||
} else if (update instanceof LocalUpdatePremiumFloodWait) {
|
} else if (update instanceof LocalUpdatePremiumFloodWait) {
|
||||||
onUpdate({
|
onUpdate({
|
||||||
'@type': 'updatePremiumFloodWait',
|
'@type': 'updatePremiumFloodWait',
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import type { ThreadId } from '../../types';
|
|||||||
import type { ApiWebDocument } from './bots';
|
import type { ApiWebDocument } from './bots';
|
||||||
import type { ApiGroupCall, PhoneCallAction } from './calls';
|
import type { ApiGroupCall, PhoneCallAction } from './calls';
|
||||||
import type { ApiChat, ApiPeerColor } from './chats';
|
import type { ApiChat, ApiPeerColor } from './chats';
|
||||||
import type { ApiInputStorePaymentPurpose, ApiPremiumGiftCodeOption } from './payments';
|
import type { ApiInputStorePaymentPurpose, ApiPremiumGiftCodeOption, ApiStarTopupOption } from './payments';
|
||||||
import type { ApiMessageStoryData, ApiWebPageStickerData, ApiWebPageStoryData } from './stories';
|
import type { ApiMessageStoryData, ApiWebPageStickerData, ApiWebPageStoryData } from './stories';
|
||||||
|
|
||||||
export interface ApiDimensions {
|
export interface ApiDimensions {
|
||||||
@ -208,8 +208,13 @@ export type ApiInputInvoiceGiftCode = {
|
|||||||
option: ApiPremiumGiftCodeOption;
|
option: ApiPremiumGiftCodeOption;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ApiInputInvoiceStars = {
|
||||||
|
type: 'stars';
|
||||||
|
option: ApiStarTopupOption;
|
||||||
|
};
|
||||||
|
|
||||||
export type ApiInputInvoice = ApiInputInvoiceMessage | ApiInputInvoiceSlug | ApiInputInvoiceGiveaway
|
export type ApiInputInvoice = ApiInputInvoiceMessage | ApiInputInvoiceSlug | ApiInputInvoiceGiveaway
|
||||||
| ApiInputInvoiceGiftCode;
|
| ApiInputInvoiceGiftCode | ApiInputInvoiceStars;
|
||||||
|
|
||||||
/* Used for Invoice request */
|
/* Used for Invoice request */
|
||||||
export type ApiRequestInputInvoiceMessage = {
|
export type ApiRequestInputInvoiceMessage = {
|
||||||
@ -229,8 +234,13 @@ export type ApiRequestInputInvoiceGiveaway = {
|
|||||||
option: ApiPremiumGiftCodeOption;
|
option: ApiPremiumGiftCodeOption;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ApiRequestInputInvoiceStars = {
|
||||||
|
type: 'stars';
|
||||||
|
option: ApiStarTopupOption;
|
||||||
|
};
|
||||||
|
|
||||||
export type ApiRequestInputInvoice = ApiRequestInputInvoiceMessage | ApiRequestInputInvoiceSlug
|
export type ApiRequestInputInvoice = ApiRequestInputInvoiceMessage | ApiRequestInputInvoiceSlug
|
||||||
| ApiRequestInputInvoiceGiveaway;
|
| ApiRequestInputInvoiceGiveaway | ApiRequestInputInvoiceStars;
|
||||||
|
|
||||||
export interface ApiInvoice {
|
export interface ApiInvoice {
|
||||||
text: string;
|
text: string;
|
||||||
@ -345,6 +355,7 @@ export interface ApiAction {
|
|||||||
| 'suggestProfilePhoto'
|
| 'suggestProfilePhoto'
|
||||||
| 'joinedChannel'
|
| 'joinedChannel'
|
||||||
| 'chatBoost'
|
| 'chatBoost'
|
||||||
|
| 'receipt'
|
||||||
| 'other';
|
| 'other';
|
||||||
photo?: ApiPhoto;
|
photo?: ApiPhoto;
|
||||||
amount?: number;
|
amount?: number;
|
||||||
@ -445,7 +456,7 @@ export type ApiMessageEntityDefault = {
|
|||||||
type: Exclude<
|
type: Exclude<
|
||||||
`${ApiMessageEntityTypes}`,
|
`${ApiMessageEntityTypes}`,
|
||||||
`${ApiMessageEntityTypes.Pre}` | `${ApiMessageEntityTypes.TextUrl}` | `${ApiMessageEntityTypes.MentionName}` |
|
`${ApiMessageEntityTypes.Pre}` | `${ApiMessageEntityTypes.TextUrl}` | `${ApiMessageEntityTypes.MentionName}` |
|
||||||
`${ApiMessageEntityTypes.CustomEmoji}`
|
`${ApiMessageEntityTypes.CustomEmoji}` | `${ApiMessageEntityTypes.Blockquote}`
|
||||||
>;
|
>;
|
||||||
offset: number;
|
offset: number;
|
||||||
length: number;
|
length: number;
|
||||||
@ -472,6 +483,13 @@ export type ApiMessageEntityMentionName = {
|
|||||||
userId: string;
|
userId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ApiMessageEntityBlockquote = {
|
||||||
|
type: ApiMessageEntityTypes.Blockquote;
|
||||||
|
offset: number;
|
||||||
|
length: number;
|
||||||
|
canCollapse?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export type ApiMessageEntityCustomEmoji = {
|
export type ApiMessageEntityCustomEmoji = {
|
||||||
type: ApiMessageEntityTypes.CustomEmoji;
|
type: ApiMessageEntityTypes.CustomEmoji;
|
||||||
offset: number;
|
offset: number;
|
||||||
@ -480,7 +498,7 @@ export type ApiMessageEntityCustomEmoji = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type ApiMessageEntity = ApiMessageEntityDefault | ApiMessageEntityPre | ApiMessageEntityTextUrl |
|
export type ApiMessageEntity = ApiMessageEntityDefault | ApiMessageEntityPre | ApiMessageEntityTextUrl |
|
||||||
ApiMessageEntityMentionName | ApiMessageEntityCustomEmoji;
|
ApiMessageEntityMentionName | ApiMessageEntityCustomEmoji | ApiMessageEntityBlockquote;
|
||||||
|
|
||||||
export enum ApiMessageEntityTypes {
|
export enum ApiMessageEntityTypes {
|
||||||
Bold = 'MessageEntityBold',
|
Bold = 'MessageEntityBold',
|
||||||
@ -587,6 +605,7 @@ export interface ApiMessage {
|
|||||||
readDate?: number;
|
readDate?: number;
|
||||||
savedPeerId?: string;
|
savedPeerId?: string;
|
||||||
senderBoosts?: number;
|
senderBoosts?: number;
|
||||||
|
factCheck?: ApiFactCheck;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiReactions {
|
export interface ApiReactions {
|
||||||
@ -835,6 +854,13 @@ export type ApiQuickReply = {
|
|||||||
topMessageId: number;
|
topMessageId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ApiFactCheck = {
|
||||||
|
shouldFetch?: true;
|
||||||
|
hash: string;
|
||||||
|
countryCode?: string;
|
||||||
|
text?: ApiFormattedText;
|
||||||
|
};
|
||||||
|
|
||||||
export type ApiSponsoredMessageReportResult = {
|
export type ApiSponsoredMessageReportResult = {
|
||||||
type: 'reported' | 'hidden' | 'premiumRequired';
|
type: 'reported' | 'hidden' | 'premiumRequired';
|
||||||
} | {
|
} | {
|
||||||
|
|||||||
@ -22,8 +22,10 @@ export interface ApiPaymentSavedInfo {
|
|||||||
shippingAddress?: ApiShippingAddress;
|
shippingAddress?: ApiShippingAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiPaymentForm {
|
export interface ApiPaymentFormRegular {
|
||||||
|
type: 'regular';
|
||||||
url: string;
|
url: string;
|
||||||
|
botId: string;
|
||||||
canSaveCredentials?: boolean;
|
canSaveCredentials?: boolean;
|
||||||
isPasswordMissing?: boolean;
|
isPasswordMissing?: boolean;
|
||||||
formId: string;
|
formId: string;
|
||||||
@ -35,12 +37,21 @@ export interface ApiPaymentForm {
|
|||||||
nativeParams: ApiPaymentFormNativeParams;
|
nativeParams: ApiPaymentFormNativeParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ApiPaymentFormStars {
|
||||||
|
type: 'stars';
|
||||||
|
formId: string;
|
||||||
|
botId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ApiPaymentForm = ApiPaymentFormRegular | ApiPaymentFormStars;
|
||||||
|
|
||||||
export interface ApiPaymentFormNativeParams {
|
export interface ApiPaymentFormNativeParams {
|
||||||
needCardholderName?: boolean;
|
needCardholderName?: boolean;
|
||||||
needCountry?: boolean;
|
needCountry?: boolean;
|
||||||
needZip?: boolean;
|
needZip?: boolean;
|
||||||
publishableKey?: string;
|
publishableKey?: string;
|
||||||
publicToken?: string;
|
publicToken?: string;
|
||||||
|
tokenizeUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiLabeledPrice {
|
export interface ApiLabeledPrice {
|
||||||
@ -48,7 +59,21 @@ export interface ApiLabeledPrice {
|
|||||||
amount: number;
|
amount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiReceipt {
|
export interface ApiReceiptStars {
|
||||||
|
type: 'stars';
|
||||||
|
botId?: string;
|
||||||
|
peer?: ApiStarsTransactionPeer;
|
||||||
|
date: number;
|
||||||
|
title?: string;
|
||||||
|
text?: string;
|
||||||
|
photo?: ApiWebDocument;
|
||||||
|
currency: string;
|
||||||
|
totalAmount: number;
|
||||||
|
transactionId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiReceiptRegular {
|
||||||
|
type: 'regular';
|
||||||
photo?: ApiWebDocument;
|
photo?: ApiWebDocument;
|
||||||
text?: string;
|
text?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
@ -66,6 +91,8 @@ export interface ApiReceipt {
|
|||||||
shippingMethod?: string;
|
shippingMethod?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ApiReceipt = ApiReceiptRegular | ApiReceiptStars;
|
||||||
|
|
||||||
export interface ApiPremiumPromo {
|
export interface ApiPremiumPromo {
|
||||||
videoSections: ApiPremiumSection[];
|
videoSections: ApiPremiumSection[];
|
||||||
videos: ApiDocument[];
|
videos: ApiDocument[];
|
||||||
@ -179,3 +206,54 @@ export interface ApiPrepaidGiveaway {
|
|||||||
quantity: number;
|
quantity: number;
|
||||||
date: number;
|
date: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ApiStarsTransactionPeerUnsupported {
|
||||||
|
type: 'unsupported';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiStarsTransactionPeerAppStore {
|
||||||
|
type: 'appStore';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiStarsTransactionPeerPlayMarket {
|
||||||
|
type: 'playMarket';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiStarsTransactionPeerPremiumBot {
|
||||||
|
type: 'premiumBot';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiStarsTransactionPeerFragment {
|
||||||
|
type: 'fragment';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiStarsTransactionPeerPeer {
|
||||||
|
type: 'peer';
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ApiStarsTransactionPeer =
|
||||||
|
| ApiStarsTransactionPeerUnsupported
|
||||||
|
| ApiStarsTransactionPeerAppStore
|
||||||
|
| ApiStarsTransactionPeerPlayMarket
|
||||||
|
| ApiStarsTransactionPeerPremiumBot
|
||||||
|
| ApiStarsTransactionPeerFragment
|
||||||
|
| ApiStarsTransactionPeerPeer;
|
||||||
|
|
||||||
|
export interface ApiStarsTransaction {
|
||||||
|
id: string;
|
||||||
|
peer: ApiStarsTransactionPeer;
|
||||||
|
stars: number;
|
||||||
|
isRefund?: true;
|
||||||
|
date: number;
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
photo?: ApiWebDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiStarTopupOption {
|
||||||
|
isExtended?: true;
|
||||||
|
stars: number;
|
||||||
|
currency: string;
|
||||||
|
amount: number;
|
||||||
|
}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import type {
|
|||||||
} from './chats';
|
} from './chats';
|
||||||
import type {
|
import type {
|
||||||
ApiFormattedText,
|
ApiFormattedText,
|
||||||
|
ApiInputInvoice,
|
||||||
ApiMessage,
|
ApiMessage,
|
||||||
ApiMessageExtendedMediaPreview,
|
ApiMessageExtendedMediaPreview,
|
||||||
ApiPhoto,
|
ApiPhoto,
|
||||||
@ -518,7 +519,7 @@ export type ApiUpdatePaymentVerificationNeeded = {
|
|||||||
|
|
||||||
export type ApiUpdatePaymentStateCompleted = {
|
export type ApiUpdatePaymentStateCompleted = {
|
||||||
'@type': 'updatePaymentStateCompleted';
|
'@type': 'updatePaymentStateCompleted';
|
||||||
slug?: string;
|
inputInvoice: ApiInputInvoice;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ApiUpdatePrivacy = {
|
export type ApiUpdatePrivacy = {
|
||||||
@ -738,6 +739,11 @@ export type ApiUpdatePremiumFloodWait = {
|
|||||||
isUpload?: boolean;
|
isUpload?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ApiUpdateStarsBalance = {
|
||||||
|
'@type': 'updateStarsBalance';
|
||||||
|
balance: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type ApiUpdate = (
|
export type ApiUpdate = (
|
||||||
ApiUpdateReady | ApiUpdateSession | ApiUpdateWebAuthTokenFailed | ApiUpdateRequestUserUpdate |
|
ApiUpdateReady | ApiUpdateSession | ApiUpdateWebAuthTokenFailed | ApiUpdateRequestUserUpdate |
|
||||||
ApiUpdateAuthorizationState | ApiUpdateAuthorizationError | ApiUpdateConnectionState | ApiUpdateCurrentUser |
|
ApiUpdateAuthorizationState | ApiUpdateAuthorizationError | ApiUpdateConnectionState | ApiUpdateCurrentUser |
|
||||||
@ -768,7 +774,7 @@ export type ApiUpdate = (
|
|||||||
ApiRequestReconnectApi | ApiRequestSync | ApiUpdateFetchingDifference | ApiUpdateChannelMessages |
|
ApiRequestReconnectApi | ApiRequestSync | ApiUpdateFetchingDifference | ApiUpdateChannelMessages |
|
||||||
ApiUpdateStealthMode | ApiUpdateAttachMenuBots | ApiUpdateNewAuthorization | ApiUpdateGroupInvitePrivacyForbidden |
|
ApiUpdateStealthMode | ApiUpdateAttachMenuBots | ApiUpdateNewAuthorization | ApiUpdateGroupInvitePrivacyForbidden |
|
||||||
ApiUpdateViewForumAsMessages | ApiUpdateSavedDialogPinned | ApiUpdatePinnedSavedDialogIds | ApiUpdateChatLastMessage |
|
ApiUpdateViewForumAsMessages | ApiUpdateSavedDialogPinned | ApiUpdatePinnedSavedDialogIds | ApiUpdateChatLastMessage |
|
||||||
ApiUpdateDeleteSavedHistory | ApiUpdatePremiumFloodWait |
|
ApiUpdateDeleteSavedHistory | ApiUpdatePremiumFloodWait | ApiUpdateStarsBalance |
|
||||||
ApiUpdateQuickReplyMessage | ApiUpdateQuickReplies | ApiDeleteQuickReply | ApiUpdateDeleteQuickReplyMessages
|
ApiUpdateQuickReplyMessage | ApiUpdateQuickReplies | ApiDeleteQuickReply | ApiUpdateDeleteQuickReplyMessages
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 567 B After Width: | Height: | Size: 567 B |
1
src/assets/icons/StarLogo.svg
Normal file
1
src/assets/icons/StarLogo.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="88" height="84" fill="none"><path fill="#fff" d="M41.307 70.48 21.729 82.472a3.49 3.49 0 0 1-5.205-3.835l3.03-11.929a14.17 14.17 0 0 1 7.6-9.282l21.358-10.255a2.834 2.834 0 0 0-1.71-5.346l-23.774 4.116c-4.592.795-9.3-.473-12.871-3.466l-7.511-6.294a3.49 3.49 0 0 1 1.969-6.153l22.947-1.796a5.16 5.16 0 0 0 4.362-3.167l8.852-21.37a3.49 3.49 0 0 1 6.448 0l8.852 21.37a5.16 5.16 0 0 0 4.362 3.167l23.073 1.806a3.49 3.49 0 0 1 1.992 6.134L67.906 51.175a5.16 5.16 0 0 0-1.668 5.13l5.41 22.474a3.49 3.49 0 0 1-5.216 3.792L46.693 70.48a5.16 5.16 0 0 0-5.386 0"/><path fill="url(#a)" d="M41.307 70.48 21.729 82.472a3.49 3.49 0 0 1-5.205-3.835l3.03-11.929a14.17 14.17 0 0 1 7.6-9.282l21.358-10.255a2.834 2.834 0 0 0-1.71-5.346l-23.774 4.116c-4.592.795-9.3-.473-12.871-3.466l-7.511-6.294a3.49 3.49 0 0 1 1.969-6.153l22.947-1.796a5.16 5.16 0 0 0 4.362-3.167l8.852-21.37a3.49 3.49 0 0 1 6.448 0l8.852 21.37a5.16 5.16 0 0 0 4.362 3.167l23.073 1.806a3.49 3.49 0 0 1 1.992 6.134L67.906 51.175a5.16 5.16 0 0 0-1.668 5.13l5.41 22.474a3.49 3.49 0 0 1-5.216 3.792L46.693 70.48a5.16 5.16 0 0 0-5.386 0"/><path fill="url(#b)" d="M41.307 70.48 21.729 82.472a3.49 3.49 0 0 1-5.205-3.835l3.03-11.929a14.17 14.17 0 0 1 7.6-9.282l21.358-10.255a2.834 2.834 0 0 0-1.71-5.346l-23.774 4.116c-4.592.795-9.3-.473-12.871-3.466l-7.511-6.294a3.49 3.49 0 0 1 1.969-6.153l22.947-1.796a5.16 5.16 0 0 0 4.362-3.167l8.852-21.37a3.49 3.49 0 0 1 6.448 0l8.852 21.37a5.16 5.16 0 0 0 4.362 3.167l23.073 1.806a3.49 3.49 0 0 1 1.992 6.134L67.906 51.175a5.16 5.16 0 0 0-1.668 5.13l5.41 22.474a3.49 3.49 0 0 1-5.216 3.792L46.693 70.48a5.16 5.16 0 0 0-5.386 0"/><path stroke="url(#c)" stroke-width="1.667" d="M41.307 70.48 21.729 82.472a3.49 3.49 0 0 1-5.205-3.835l3.03-11.929a14.17 14.17 0 0 1 7.6-9.282l21.358-10.255a2.834 2.834 0 0 0-1.71-5.346l-23.774 4.116c-4.592.795-9.3-.473-12.871-3.466l-7.511-6.294a3.49 3.49 0 0 1 1.969-6.153l22.947-1.796a5.16 5.16 0 0 0 4.362-3.167l8.852-21.37a3.49 3.49 0 0 1 6.448 0l8.852 21.37a5.16 5.16 0 0 0 4.362 3.167l23.073 1.806a3.49 3.49 0 0 1 1.992 6.134L67.906 51.175a5.16 5.16 0 0 0-1.668 5.13l5.41 22.474a3.49 3.49 0 0 1-5.216 3.792L46.693 70.48a5.16 5.16 0 0 0-5.386 0Z" style="mix-blend-mode:soft-light"/><defs><linearGradient id="a" x1="3" x2="84.148" y1="63.5" y2="-1.323" gradientUnits="userSpaceOnUse"><stop stop-color="#6B93FF"/><stop offset=".439" stop-color="#976FFF"/><stop offset="1" stop-color="#E46ACE"/></linearGradient><linearGradient id="b" x1="-7" x2="80.569" y1="102" y2="-4.497" gradientUnits="userSpaceOnUse"><stop stop-color="#FDEB32"/><stop offset=".439" stop-color="#FEBD04"/><stop offset="1" stop-color="#D75902"/></linearGradient><linearGradient id="c" x1="94.417" x2="44.063" y1="26.667" y2="42.313" gradientUnits="userSpaceOnUse"><stop stop-color="#fff" stop-opacity="0"/><stop offset=".396" stop-color="#fff" stop-opacity=".85"/><stop offset=".521" stop-color="#fff"/><stop offset=".646" stop-color="#fff" stop-opacity=".85"/><stop offset="1" stop-color="#fff" stop-opacity="0"/></linearGradient></defs></svg>
|
||||||
|
After Width: | Height: | Size: 3.0 KiB |
BIN
src/assets/stars-bg.png
Normal file
BIN
src/assets/stars-bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
@ -25,6 +25,8 @@ export { default as StatusPickerMenu } from '../components/left/main/StatusPicke
|
|||||||
export { default as BoostModal } from '../components/modals/boost/BoostModal';
|
export { default as BoostModal } from '../components/modals/boost/BoostModal';
|
||||||
export { default as GiftCodeModal } from '../components/modals/giftcode/GiftCodeModal';
|
export { default as GiftCodeModal } from '../components/modals/giftcode/GiftCodeModal';
|
||||||
export { default as ChatlistModal } from '../components/modals/chatlist/ChatlistModal';
|
export { default as ChatlistModal } from '../components/modals/chatlist/ChatlistModal';
|
||||||
|
export { default as StarsBalanceModal } from '../components/modals/stars/StarsBalanceModal';
|
||||||
|
export { default as StarPaymentModal } from '../components/modals/stars/StarsPaymentModal';
|
||||||
|
|
||||||
export { default as AboutAdsModal } from '../components/common/AboutAdsModal';
|
export { default as AboutAdsModal } from '../components/common/AboutAdsModal';
|
||||||
export { default as ReportAdModal } from '../components/modals/reportAd/ReportAdModal';
|
export { default as ReportAdModal } from '../components/modals/reportAd/ReportAdModal';
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import Button from '../ui/Button';
|
|||||||
import ListItem from '../ui/ListItem';
|
import ListItem from '../ui/ListItem';
|
||||||
import Modal from '../ui/Modal';
|
import Modal from '../ui/Modal';
|
||||||
import Separator from '../ui/Separator';
|
import Separator from '../ui/Separator';
|
||||||
import Icon from './Icon';
|
import Icon from './icons/Icon';
|
||||||
import SafeLink from './SafeLink';
|
import SafeLink from './SafeLink';
|
||||||
|
|
||||||
import styles from './AboutAdsModal.module.scss';
|
import styles from './AboutAdsModal.module.scss';
|
||||||
|
|||||||
@ -44,7 +44,7 @@ import Button from '../ui/Button';
|
|||||||
import Link from '../ui/Link';
|
import Link from '../ui/Link';
|
||||||
import ProgressSpinner from '../ui/ProgressSpinner';
|
import ProgressSpinner from '../ui/ProgressSpinner';
|
||||||
import AnimatedIcon from './AnimatedIcon';
|
import AnimatedIcon from './AnimatedIcon';
|
||||||
import Icon from './Icon';
|
import Icon from './icons/Icon';
|
||||||
|
|
||||||
import './Audio.scss';
|
import './Audio.scss';
|
||||||
|
|
||||||
|
|||||||
@ -36,6 +36,10 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.force-fit &__media {
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
.emoji {
|
.emoji {
|
||||||
width: 1rem;
|
width: 1rem;
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { getActions } from '../../global';
|
|||||||
|
|
||||||
import type {
|
import type {
|
||||||
ApiChat, ApiPeer, ApiPhoto, ApiUser,
|
ApiChat, ApiPeer, ApiPhoto, ApiUser,
|
||||||
|
ApiWebDocument,
|
||||||
} from '../../api/types';
|
} from '../../api/types';
|
||||||
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
|
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
|
||||||
import type { CustomPeer, StoryViewerOrigin } from '../../types';
|
import type { CustomPeer, StoryViewerOrigin } from '../../types';
|
||||||
@ -16,6 +17,7 @@ import {
|
|||||||
getChatTitle,
|
getChatTitle,
|
||||||
getPeerStoryHtmlId,
|
getPeerStoryHtmlId,
|
||||||
getUserFullName,
|
getUserFullName,
|
||||||
|
getWebDocumentHash,
|
||||||
isAnonymousForwardsChat,
|
isAnonymousForwardsChat,
|
||||||
isChatWithRepliesBot,
|
isChatWithRepliesBot,
|
||||||
isDeletedUser,
|
isDeletedUser,
|
||||||
@ -34,7 +36,7 @@ import useMediaTransition from '../../hooks/useMediaTransition';
|
|||||||
|
|
||||||
import OptimizedVideo from '../ui/OptimizedVideo';
|
import OptimizedVideo from '../ui/OptimizedVideo';
|
||||||
import AvatarStoryCircle from './AvatarStoryCircle';
|
import AvatarStoryCircle from './AvatarStoryCircle';
|
||||||
import Icon from './Icon';
|
import Icon from './icons/Icon';
|
||||||
|
|
||||||
import './Avatar.scss';
|
import './Avatar.scss';
|
||||||
|
|
||||||
@ -51,6 +53,7 @@ type OwnProps = {
|
|||||||
size?: AvatarSize;
|
size?: AvatarSize;
|
||||||
peer?: ApiPeer | CustomPeer;
|
peer?: ApiPeer | CustomPeer;
|
||||||
photo?: ApiPhoto;
|
photo?: ApiPhoto;
|
||||||
|
webPhoto?: ApiWebDocument;
|
||||||
text?: string;
|
text?: string;
|
||||||
isSavedMessages?: boolean;
|
isSavedMessages?: boolean;
|
||||||
isSavedDialog?: boolean;
|
isSavedDialog?: boolean;
|
||||||
@ -74,6 +77,7 @@ const Avatar: FC<OwnProps> = ({
|
|||||||
size = 'large',
|
size = 'large',
|
||||||
peer,
|
peer,
|
||||||
photo,
|
photo,
|
||||||
|
webPhoto,
|
||||||
text,
|
text,
|
||||||
isSavedMessages,
|
isSavedMessages,
|
||||||
isSavedDialog,
|
isSavedDialog,
|
||||||
@ -118,6 +122,8 @@ const Avatar: FC<OwnProps> = ({
|
|||||||
if (photo.isVideo && withVideo) {
|
if (photo.isVideo && withVideo) {
|
||||||
videoHash = `videoAvatar${photo.id}?size=u`;
|
videoHash = `videoAvatar${photo.id}?size=u`;
|
||||||
}
|
}
|
||||||
|
} else if (webPhoto) {
|
||||||
|
imageHash = getWebDocumentHash(webPhoto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,6 +236,7 @@ const Avatar: FC<OwnProps> = ({
|
|||||||
isReplies && 'replies-bot-account',
|
isReplies && 'replies-bot-account',
|
||||||
isPremiumGradient && 'premium-gradient-bg',
|
isPremiumGradient && 'premium-gradient-bg',
|
||||||
isRoundedRect && 'forum',
|
isRoundedRect && 'forum',
|
||||||
|
(photo || webPhoto) && 'force-fit',
|
||||||
((withStory && realPeer?.hasStories) || forPremiumPromo) && 'with-story-circle',
|
((withStory && realPeer?.hasStories) || forPremiumPromo) && 'with-story-circle',
|
||||||
withStorySolid && realPeer?.hasStories && 'with-story-solid',
|
withStorySolid && realPeer?.hasStories && 'with-story-solid',
|
||||||
withStorySolid && forceFriendStorySolid && 'close-friend',
|
withStorySolid && forceFriendStorySolid && 'close-friend',
|
||||||
|
|||||||
31
src/components/common/Blockquote.module.scss
Normal file
31
src/components/common/Blockquote.module.scss
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
@use '../../styles/mixins';
|
||||||
|
|
||||||
|
.root {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsed {
|
||||||
|
@include mixins.gradient-border-bottom(1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gradientContainer {
|
||||||
|
max-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapseIcon {
|
||||||
|
position: absolute;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clickable {
|
||||||
|
cursor: var(--custom-cursor, pointer);
|
||||||
|
}
|
||||||
65
src/components/common/Blockquote.tsx
Normal file
65
src/components/common/Blockquote.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import React, {
|
||||||
|
type TeactNode,
|
||||||
|
useRef,
|
||||||
|
} from '../../lib/teact/teact';
|
||||||
|
|
||||||
|
import { ApiMessageEntityTypes } from '../../api/types';
|
||||||
|
|
||||||
|
import buildClassName from '../../util/buildClassName';
|
||||||
|
|
||||||
|
import useCollapsibleLines from '../../hooks/element/useCollapsibleLines';
|
||||||
|
import useLastCallback from '../../hooks/useLastCallback';
|
||||||
|
|
||||||
|
import Icon from './icons/Icon';
|
||||||
|
|
||||||
|
import styles from './Blockquote.module.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
canBeCollapsible?: boolean;
|
||||||
|
isToggleDisabled?: boolean;
|
||||||
|
children: TeactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const MAX_LINES = 4;
|
||||||
|
|
||||||
|
const Blockquote = ({ canBeCollapsible, isToggleDisabled, children }: OwnProps) => {
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
const ref = useRef<HTMLQuoteElement>(null);
|
||||||
|
const {
|
||||||
|
isCollapsed, isCollapsible, setIsCollapsed,
|
||||||
|
} = useCollapsibleLines(ref, MAX_LINES, undefined, !canBeCollapsible);
|
||||||
|
|
||||||
|
const canExpand = !isToggleDisabled && isCollapsed;
|
||||||
|
|
||||||
|
const handleExpand = useLastCallback(() => {
|
||||||
|
setIsCollapsed(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleToggle = useLastCallback(() => {
|
||||||
|
setIsCollapsed((prev) => !prev);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className={styles.root} onClick={canExpand ? handleExpand : undefined}>
|
||||||
|
<blockquote
|
||||||
|
ref={ref}
|
||||||
|
data-entity-type={ApiMessageEntityTypes.Blockquote}
|
||||||
|
>
|
||||||
|
<div className={buildClassName(styles.gradientContainer, isCollapsed && styles.collapsed)}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
{isCollapsible && (
|
||||||
|
<div
|
||||||
|
className={buildClassName(styles.collapseIcon, !isToggleDisabled && styles.clickable)}
|
||||||
|
onClick={!isToggleDisabled ? handleToggle : undefined}
|
||||||
|
aria-hidden
|
||||||
|
>
|
||||||
|
<Icon name={isCollapsed ? 'down' : 'up'} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</blockquote>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Blockquote;
|
||||||
@ -158,7 +158,7 @@ import ResponsiveHoverButton from '../ui/ResponsiveHoverButton';
|
|||||||
import Spinner from '../ui/Spinner';
|
import Spinner from '../ui/Spinner';
|
||||||
import Avatar from './Avatar';
|
import Avatar from './Avatar';
|
||||||
import DeleteMessageModal from './DeleteMessageModal.async';
|
import DeleteMessageModal from './DeleteMessageModal.async';
|
||||||
import Icon from './Icon';
|
import Icon from './icons/Icon';
|
||||||
import ReactionAnimatedEmoji from './reactions/ReactionAnimatedEmoji';
|
import ReactionAnimatedEmoji from './reactions/ReactionAnimatedEmoji';
|
||||||
|
|
||||||
import './Composer.scss';
|
import './Composer.scss';
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import usePrevious from '../../hooks/usePrevious';
|
|||||||
|
|
||||||
import Button from '../ui/Button';
|
import Button from '../ui/Button';
|
||||||
import Modal from '../ui/Modal';
|
import Modal from '../ui/Modal';
|
||||||
import Icon from './Icon';
|
import Icon from './icons/Icon';
|
||||||
import Picker from './Picker';
|
import Picker from './Picker';
|
||||||
|
|
||||||
import styles from './CountryPickerModal.module.scss';
|
import styles from './CountryPickerModal.module.scss';
|
||||||
|
|||||||
@ -43,7 +43,7 @@ import { useStickerPickerObservers } from './hooks/useStickerPickerObservers';
|
|||||||
import StickerSetCover from '../middle/composer/StickerSetCover';
|
import StickerSetCover from '../middle/composer/StickerSetCover';
|
||||||
import Button from '../ui/Button';
|
import Button from '../ui/Button';
|
||||||
import Loading from '../ui/Loading';
|
import Loading from '../ui/Loading';
|
||||||
import Icon from './Icon';
|
import Icon from './icons/Icon';
|
||||||
import StickerButton from './StickerButton';
|
import StickerButton from './StickerButton';
|
||||||
import StickerSet from './StickerSet';
|
import StickerSet from './StickerSet';
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import useLastCallback from '../../hooks/useLastCallback';
|
|||||||
|
|
||||||
import CustomEmoji from './CustomEmoji';
|
import CustomEmoji from './CustomEmoji';
|
||||||
import FakeIcon from './FakeIcon';
|
import FakeIcon from './FakeIcon';
|
||||||
import PremiumIcon from './PremiumIcon';
|
import StarIcon from './icons/StarIcon';
|
||||||
import VerifiedIcon from './VerifiedIcon';
|
import VerifiedIcon from './VerifiedIcon';
|
||||||
|
|
||||||
import styles from './FullNameTitle.module.scss';
|
import styles from './FullNameTitle.module.scss';
|
||||||
@ -127,7 +127,7 @@ const FullNameTitle: FC<OwnProps> = ({
|
|||||||
onClick={onEmojiStatusClick}
|
onClick={onEmojiStatusClick}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{withEmojiStatus && !realPeer?.emojiStatus && isPremium && <PremiumIcon />}
|
{withEmojiStatus && !realPeer?.emojiStatus && isPremium && <StarIcon />}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{iconElement}
|
{iconElement}
|
||||||
|
|||||||
@ -33,7 +33,7 @@ import Transition from '../ui/Transition';
|
|||||||
import Avatar from './Avatar';
|
import Avatar from './Avatar';
|
||||||
import DotAnimation from './DotAnimation';
|
import DotAnimation from './DotAnimation';
|
||||||
import FullNameTitle from './FullNameTitle';
|
import FullNameTitle from './FullNameTitle';
|
||||||
import Icon from './Icon';
|
import Icon from './icons/Icon';
|
||||||
import TopicIcon from './TopicIcon';
|
import TopicIcon from './TopicIcon';
|
||||||
import TypingStatus from './TypingStatus';
|
import TypingStatus from './TypingStatus';
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import useLastCallback from '../../hooks/useLastCallback';
|
|||||||
import Button from '../ui/Button';
|
import Button from '../ui/Button';
|
||||||
import DropdownMenu from '../ui/DropdownMenu';
|
import DropdownMenu from '../ui/DropdownMenu';
|
||||||
import MenuItem from '../ui/MenuItem';
|
import MenuItem from '../ui/MenuItem';
|
||||||
import Icon from './Icon';
|
import Icon from './icons/Icon';
|
||||||
|
|
||||||
import styles from './LinkField.module.scss';
|
import styles from './LinkField.module.scss';
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import React, { memo } from '../../lib/teact/teact';
|
|||||||
import type { ApiMessageOutgoingStatus } from '../../api/types';
|
import type { ApiMessageOutgoingStatus } from '../../api/types';
|
||||||
|
|
||||||
import Transition from '../ui/Transition';
|
import Transition from '../ui/Transition';
|
||||||
import Icon from './Icon';
|
import Icon from './icons/Icon';
|
||||||
|
|
||||||
import './MessageOutgoingStatus.scss';
|
import './MessageOutgoingStatus.scss';
|
||||||
|
|
||||||
|
|||||||
@ -29,6 +29,7 @@ interface OwnProps {
|
|||||||
inChatList?: boolean;
|
inChatList?: boolean;
|
||||||
forcePlayback?: boolean;
|
forcePlayback?: boolean;
|
||||||
focusedQuote?: string;
|
focusedQuote?: string;
|
||||||
|
isInSelectMode?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MIN_CUSTOM_EMOJIS_FOR_SHARED_CANVAS = 3;
|
const MIN_CUSTOM_EMOJIS_FOR_SHARED_CANVAS = 3;
|
||||||
@ -49,6 +50,7 @@ function MessageText({
|
|||||||
inChatList,
|
inChatList,
|
||||||
forcePlayback,
|
forcePlayback,
|
||||||
focusedQuote,
|
focusedQuote,
|
||||||
|
isInSelectMode,
|
||||||
}: OwnProps) {
|
}: OwnProps) {
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const sharedCanvasRef = useRef<HTMLCanvasElement>(null);
|
const sharedCanvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
@ -104,6 +106,7 @@ function MessageText({
|
|||||||
cacheBuster: textCacheBusterRef.current.toString(),
|
cacheBuster: textCacheBusterRef.current.toString(),
|
||||||
forcePlayback,
|
forcePlayback,
|
||||||
focusedQuote,
|
focusedQuote,
|
||||||
|
isInSelectMode,
|
||||||
}),
|
}),
|
||||||
].flat().filter(Boolean)}
|
].flat().filter(Boolean)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -1,3 +1,22 @@
|
|||||||
.root {
|
.root {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
padding-inline-start: 0.5rem;
|
||||||
|
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
|
||||||
|
background-color: var(--accent-background-color);
|
||||||
|
color: var(--accent-color);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
inset-inline-start: 0;
|
||||||
|
width: 0.1875rem;
|
||||||
|
background: var(--bar-gradient, var(--accent-color));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import React, {
|
|||||||
} from '../../lib/teact/teact';
|
} from '../../lib/teact/teact';
|
||||||
|
|
||||||
import type { ApiCountry } from '../../api/types';
|
import type { ApiCountry } from '../../api/types';
|
||||||
import type { CustomPeer, CustomPeerType } from '../../types';
|
import type { CustomPeer, CustomPeerType, UniqueCustomPeer } from '../../types';
|
||||||
|
|
||||||
import { requestMeasure } from '../../lib/fasterdom/fasterdom';
|
import { requestMeasure } from '../../lib/fasterdom/fasterdom';
|
||||||
import { isUserId } from '../../global/helpers';
|
import { isUserId } from '../../global/helpers';
|
||||||
@ -28,7 +28,7 @@ import './Picker.scss';
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
categories?: CustomPeer[];
|
categories?: UniqueCustomPeer[];
|
||||||
itemIds: string[];
|
itemIds: string[];
|
||||||
selectedCategories?: CustomPeerType[];
|
selectedCategories?: CustomPeerType[];
|
||||||
selectedIds: string[];
|
selectedIds: string[];
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import renderText from './helpers/renderText';
|
|||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
|
|
||||||
import Avatar from './Avatar';
|
import Avatar from './Avatar';
|
||||||
import Icon from './Icon';
|
import Icon from './icons/Icon';
|
||||||
|
|
||||||
import './PickerSelectedItem.scss';
|
import './PickerSelectedItem.scss';
|
||||||
|
|
||||||
|
|||||||
@ -1,23 +0,0 @@
|
|||||||
.PremiumIcon {
|
|
||||||
flex-shrink: 0;
|
|
||||||
display: flex;
|
|
||||||
width: 1rem;
|
|
||||||
height: 1rem;
|
|
||||||
|
|
||||||
&.big {
|
|
||||||
width: 1.5rem;
|
|
||||||
height: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
--color-fill: var(--color-primary);
|
|
||||||
|
|
||||||
& > svg {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.clickable {
|
|
||||||
cursor: var(--custom-cursor, pointer);
|
|
||||||
pointer-events: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
import type { FC } from '../../lib/teact/teact';
|
|
||||||
import React, { memo } from '../../lib/teact/teact';
|
|
||||||
|
|
||||||
import buildClassName from '../../util/buildClassName';
|
|
||||||
|
|
||||||
import useUniqueId from '../../hooks/useUniqueId';
|
|
||||||
|
|
||||||
import './PremiumIcon.scss';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
withGradient?: boolean;
|
|
||||||
big?: boolean;
|
|
||||||
className?: string;
|
|
||||||
onClick?: VoidFunction;
|
|
||||||
};
|
|
||||||
|
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
const STAR_PATH = 'M6.63869 12.1902L3.50621 14.1092C3.18049 14.3087 2.75468 14.2064 2.55515 13.8807C2.45769 13.7216 2.42864 13.5299 2.47457 13.3491L2.95948 11.4405C3.13452 10.7515 3.60599 10.1756 4.24682 9.86791L7.6642 8.22716C7.82352 8.15067 7.89067 7.95951 7.81418 7.80019C7.75223 7.67116 7.61214 7.59896 7.47111 7.62338L3.66713 8.28194C2.89387 8.41581 2.1009 8.20228 1.49941 7.69823L0.297703 6.69116C0.00493565 6.44581 -0.0335059 6.00958 0.211842 5.71682C0.33117 5.57442 0.502766 5.48602 0.687982 5.47153L4.35956 5.18419C4.61895 5.16389 4.845 4.99974 4.94458 4.75937L6.36101 1.3402C6.5072 0.987302 6.91179 0.819734 7.26469 0.965925C7.43413 1.03612 7.56876 1.17075 7.63896 1.3402L9.05539 4.75937C9.15496 4.99974 9.38101 5.16389 9.6404 5.18419L13.3322 5.47311C13.713 5.50291 13.9975 5.83578 13.9677 6.2166C13.9534 6.39979 13.8667 6.56975 13.7269 6.68896L10.9114 9.08928C10.7131 9.25826 10.6267 9.52425 10.6876 9.77748L11.5532 13.3733C11.6426 13.7447 11.414 14.1182 11.0427 14.2076C10.8642 14.2506 10.676 14.2208 10.5195 14.1249L7.36128 12.1902C7.13956 12.0544 6.8604 12.0544 6.63869 12.1902Z';
|
|
||||||
|
|
||||||
const PremiumIcon: FC<OwnProps> = ({
|
|
||||||
withGradient,
|
|
||||||
big,
|
|
||||||
className,
|
|
||||||
onClick,
|
|
||||||
}) => {
|
|
||||||
const randomId = useUniqueId();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<i
|
|
||||||
onClick={onClick}
|
|
||||||
className={buildClassName(
|
|
||||||
'PremiumIcon', className, withGradient && 'gradient', onClick && 'clickable', big && 'big',
|
|
||||||
)}
|
|
||||||
title="Premium"
|
|
||||||
>
|
|
||||||
{withGradient ? (
|
|
||||||
<svg width="14" height="15" viewBox="0 0 14 15" fill="none">
|
|
||||||
<defs>
|
|
||||||
<linearGradient id={randomId} x1="3" y1="63.5001" x2="84.1475" y2="-1.32262" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#6B93FF" />
|
|
||||||
<stop offset="0.439058" stop-color="#976FFF" />
|
|
||||||
<stop offset="1" stop-color="#E46ACE" />
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d={STAR_PATH} fill={`url(#${randomId})`} />
|
|
||||||
</svg>
|
|
||||||
) : (
|
|
||||||
<svg width="14" height="15" viewBox="0 0 14 15" fill="none">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d={STAR_PATH} fill="var(--color-fill)" />
|
|
||||||
</svg>
|
|
||||||
)}
|
|
||||||
</i>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(PremiumIcon);
|
|
||||||
@ -11,7 +11,7 @@ import buildStyle from '../../util/buildStyle';
|
|||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
import useResizeObserver from '../../hooks/useResizeObserver';
|
import useResizeObserver from '../../hooks/useResizeObserver';
|
||||||
|
|
||||||
import Icon from './Icon';
|
import Icon from './icons/Icon';
|
||||||
|
|
||||||
import styles from './PremiumProgress.module.scss';
|
import styles from './PremiumProgress.module.scss';
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import Button from '../ui/Button';
|
|||||||
import Modal, { ANIMATION_DURATION } from '../ui/Modal';
|
import Modal, { ANIMATION_DURATION } from '../ui/Modal';
|
||||||
import Separator from '../ui/Separator';
|
import Separator from '../ui/Separator';
|
||||||
import AnimatedIconWithPreview from './AnimatedIconWithPreview';
|
import AnimatedIconWithPreview from './AnimatedIconWithPreview';
|
||||||
import Icon from './Icon';
|
import Icon from './icons/Icon';
|
||||||
|
|
||||||
import styles from './PrivacySettingsNoticeModal.module.scss';
|
import styles from './PrivacySettingsNoticeModal.module.scss';
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import RippleEffect from '../ui/RippleEffect';
|
|||||||
import Avatar from './Avatar';
|
import Avatar from './Avatar';
|
||||||
import DotAnimation from './DotAnimation';
|
import DotAnimation from './DotAnimation';
|
||||||
import FullNameTitle from './FullNameTitle';
|
import FullNameTitle from './FullNameTitle';
|
||||||
import Icon from './Icon';
|
import Icon from './icons/Icon';
|
||||||
import TypingStatus from './TypingStatus';
|
import TypingStatus from './TypingStatus';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
@ -227,7 +227,7 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Avatar
|
<Avatar
|
||||||
key={customPeer?.type || user?.id}
|
key={user?.id}
|
||||||
size={avatarSize}
|
size={avatarSize}
|
||||||
peer={customPeer || user}
|
peer={customPeer || user}
|
||||||
className={buildClassName(isSavedDialog && 'overlay-avatar')}
|
className={buildClassName(isSavedDialog && 'overlay-avatar')}
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.VerifiedIcon,
|
.VerifiedIcon,
|
||||||
.PremiumIcon {
|
.StarIcon {
|
||||||
--color-fill: var(--color-white);
|
--color-fill: var(--color-white);
|
||||||
--color-checkmark: var(--color-primary);
|
--color-checkmark: var(--color-primary);
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,7 @@ import useMediaTransition from '../../hooks/useMediaTransition';
|
|||||||
|
|
||||||
import OptimizedVideo from '../ui/OptimizedVideo';
|
import OptimizedVideo from '../ui/OptimizedVideo';
|
||||||
import Spinner from '../ui/Spinner';
|
import Spinner from '../ui/Spinner';
|
||||||
import Icon from './Icon';
|
import Icon from './icons/Icon';
|
||||||
|
|
||||||
import './ProfilePhoto.scss';
|
import './ProfilePhoto.scss';
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import type { FC } from '../../lib/teact/teact';
|
import type { TeactNode } from '../../lib/teact/teact';
|
||||||
import React, { memo } from '../../lib/teact/teact';
|
import React from '../../lib/teact/teact';
|
||||||
import { getActions } from '../../global';
|
import { getActions } from '../../global';
|
||||||
|
|
||||||
import { ApiMessageEntityTypes } from '../../api/types';
|
import { ApiMessageEntityTypes } from '../../api/types';
|
||||||
@ -17,17 +17,17 @@ type OwnProps = {
|
|||||||
url?: string;
|
url?: string;
|
||||||
text: string;
|
text: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
children?: React.ReactNode;
|
children?: TeactNode;
|
||||||
isRtl?: boolean;
|
isRtl?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SafeLink: FC<OwnProps> = ({
|
const SafeLink = ({
|
||||||
url,
|
url,
|
||||||
text,
|
text,
|
||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
isRtl,
|
isRtl,
|
||||||
}) => {
|
}: OwnProps) => {
|
||||||
const { openUrl } = getActions();
|
const { openUrl } = getActions();
|
||||||
|
|
||||||
const content = children || text;
|
const content = children || text;
|
||||||
@ -96,4 +96,4 @@ function getUnicodeUrl(url?: string) {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(SafeLink);
|
export default SafeLink;
|
||||||
|
|||||||
@ -37,7 +37,7 @@ import useThumbnail from '../../../hooks/useThumbnail';
|
|||||||
import useMessageTranslation from '../../middle/message/hooks/useMessageTranslation';
|
import useMessageTranslation from '../../middle/message/hooks/useMessageTranslation';
|
||||||
|
|
||||||
import ActionMessage from '../../middle/ActionMessage';
|
import ActionMessage from '../../middle/ActionMessage';
|
||||||
import Icon from '../Icon';
|
import Icon from '../icons/Icon';
|
||||||
import MediaSpoiler from '../MediaSpoiler';
|
import MediaSpoiler from '../MediaSpoiler';
|
||||||
import MessageSummary from '../MessageSummary';
|
import MessageSummary from '../MessageSummary';
|
||||||
import EmojiIconBackground from './EmojiIconBackground';
|
import EmojiIconBackground from './EmojiIconBackground';
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import useLang from '../../../hooks/useLang';
|
|||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useMedia from '../../../hooks/useMedia';
|
import useMedia from '../../../hooks/useMedia';
|
||||||
|
|
||||||
import Icon from '../Icon';
|
import Icon from '../icons/Icon';
|
||||||
|
|
||||||
import './EmbeddedMessage.scss';
|
import './EmbeddedMessage.scss';
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import { useFastClick } from '../../../hooks/useFastClick';
|
|||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
import Icon from '../Icon';
|
import Icon from '../icons/Icon';
|
||||||
import EmojiIconBackground from './EmojiIconBackground';
|
import EmojiIconBackground from './EmojiIconBackground';
|
||||||
|
|
||||||
import './EmbeddedMessage.scss';
|
import './EmbeddedMessage.scss';
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import { buildCustomEmojiHtmlFromEntity } from '../../middle/composer/helpers/cu
|
|||||||
import renderText from './renderText';
|
import renderText from './renderText';
|
||||||
|
|
||||||
import MentionLink from '../../middle/message/MentionLink';
|
import MentionLink from '../../middle/message/MentionLink';
|
||||||
|
import Blockquote from '../Blockquote';
|
||||||
import CodeBlock from '../code/CodeBlock';
|
import CodeBlock from '../code/CodeBlock';
|
||||||
import CustomEmoji from '../CustomEmoji';
|
import CustomEmoji from '../CustomEmoji';
|
||||||
import SafeLink from '../SafeLink';
|
import SafeLink from '../SafeLink';
|
||||||
@ -46,6 +47,7 @@ export function renderTextWithEntities({
|
|||||||
cacheBuster,
|
cacheBuster,
|
||||||
forcePlayback,
|
forcePlayback,
|
||||||
focusedQuote,
|
focusedQuote,
|
||||||
|
isInSelectMode,
|
||||||
}: {
|
}: {
|
||||||
text: string;
|
text: string;
|
||||||
entities?: ApiMessageEntity[];
|
entities?: ApiMessageEntity[];
|
||||||
@ -64,6 +66,7 @@ export function renderTextWithEntities({
|
|||||||
cacheBuster?: string;
|
cacheBuster?: string;
|
||||||
forcePlayback?: boolean;
|
forcePlayback?: boolean;
|
||||||
focusedQuote?: string;
|
focusedQuote?: string;
|
||||||
|
isInSelectMode?: boolean;
|
||||||
}) {
|
}) {
|
||||||
if (!entities?.length) {
|
if (!entities?.length) {
|
||||||
return renderMessagePart({
|
return renderMessagePart({
|
||||||
@ -170,6 +173,7 @@ export function renderTextWithEntities({
|
|||||||
sharedCanvasHqRef,
|
sharedCanvasHqRef,
|
||||||
cacheBuster,
|
cacheBuster,
|
||||||
forcePlayback,
|
forcePlayback,
|
||||||
|
isInSelectMode,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Array.isArray(newEntity)) {
|
if (Array.isArray(newEntity)) {
|
||||||
@ -384,6 +388,7 @@ function processEntity({
|
|||||||
sharedCanvasHqRef,
|
sharedCanvasHqRef,
|
||||||
cacheBuster,
|
cacheBuster,
|
||||||
forcePlayback,
|
forcePlayback,
|
||||||
|
isInSelectMode,
|
||||||
} : {
|
} : {
|
||||||
entity: ApiMessageEntity;
|
entity: ApiMessageEntity;
|
||||||
entityContent: TextPart;
|
entityContent: TextPart;
|
||||||
@ -402,6 +407,7 @@ function processEntity({
|
|||||||
sharedCanvasHqRef?: React.RefObject<HTMLCanvasElement>;
|
sharedCanvasHqRef?: React.RefObject<HTMLCanvasElement>;
|
||||||
cacheBuster?: string;
|
cacheBuster?: string;
|
||||||
forcePlayback?: boolean;
|
forcePlayback?: boolean;
|
||||||
|
isInSelectMode?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const entityText = typeof entityContent === 'string' && entityContent;
|
const entityText = typeof entityContent === 'string' && entityContent;
|
||||||
const renderedContent = nestedEntityContent.length ? nestedEntityContent : entityContent;
|
const renderedContent = nestedEntityContent.length ? nestedEntityContent : entityContent;
|
||||||
@ -451,11 +457,9 @@ function processEntity({
|
|||||||
return <strong data-entity-type={entity.type}>{renderNestedMessagePart()}</strong>;
|
return <strong data-entity-type={entity.type}>{renderNestedMessagePart()}</strong>;
|
||||||
case ApiMessageEntityTypes.Blockquote:
|
case ApiMessageEntityTypes.Blockquote:
|
||||||
return (
|
return (
|
||||||
<span className="text-entity-blockquote-wrapper">
|
<Blockquote canBeCollapsible={entity.canCollapse} isToggleDisabled={isInSelectMode}>
|
||||||
<blockquote data-entity-type={entity.type}>
|
{renderNestedMessagePart()}
|
||||||
{renderNestedMessagePart()}
|
</Blockquote>
|
||||||
</blockquote>
|
|
||||||
</span>
|
|
||||||
);
|
);
|
||||||
case ApiMessageEntityTypes.BotCommand:
|
case ApiMessageEntityTypes.BotCommand:
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import type { AriaRole } from 'react';
|
import type { AriaRole } from 'react';
|
||||||
import React from '../../lib/teact/teact';
|
import React from '../../../lib/teact/teact';
|
||||||
|
|
||||||
import type { IconName } from '../../types/icons';
|
import type { IconName } from '../../../types/icons';
|
||||||
|
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
name: IconName;
|
name: IconName;
|
||||||
28
src/components/common/icons/StarIcon.module.scss
Normal file
28
src/components/common/icons/StarIcon.module.scss
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
.root {
|
||||||
|
--color-fill: var(--color-primary);
|
||||||
|
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.middle {
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.big {
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.svg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clickable {
|
||||||
|
cursor: var(--custom-cursor, pointer);
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
139
src/components/common/icons/StarIcon.tsx
Normal file
139
src/components/common/icons/StarIcon.tsx
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
import type { FC } from '../../../lib/teact/teact';
|
||||||
|
import React, { memo } from '../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import buildClassName from '../../../util/buildClassName';
|
||||||
|
|
||||||
|
import useUniqueId from '../../../hooks/useUniqueId';
|
||||||
|
|
||||||
|
import styles from './StarIcon.module.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
type?: 'gold' | 'premium' | 'regular';
|
||||||
|
size?: 'small' | 'middle' | 'big';
|
||||||
|
className?: string;
|
||||||
|
onClick?: VoidFunction;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* eslint-disable max-len */
|
||||||
|
const STAR_PATH = 'M6.63869 12.1902L3.50621 14.1092C3.18049 14.3087 2.75468 14.2064 2.55515 13.8807C2.45769 13.7216 2.42864 13.5299 2.47457 13.3491L2.95948 11.4405C3.13452 10.7515 3.60599 10.1756 4.24682 9.86791L7.6642 8.22716C7.82352 8.15067 7.89067 7.95951 7.81418 7.80019C7.75223 7.67116 7.61214 7.59896 7.47111 7.62338L3.66713 8.28194C2.89387 8.41581 2.1009 8.20228 1.49941 7.69823L0.297703 6.69116C0.00493565 6.44581 -0.0335059 6.00958 0.211842 5.71682C0.33117 5.57442 0.502766 5.48602 0.687982 5.47153L4.35956 5.18419C4.61895 5.16389 4.845 4.99974 4.94458 4.75937L6.36101 1.3402C6.5072 0.987302 6.91179 0.819734 7.26469 0.965925C7.43413 1.03612 7.56876 1.17075 7.63896 1.3402L9.05539 4.75937C9.15496 4.99974 9.38101 5.16389 9.6404 5.18419L13.3322 5.47311C13.713 5.50291 13.9975 5.83578 13.9677 6.2166C13.9534 6.39979 13.8667 6.56975 13.7269 6.68896L10.9114 9.08928C10.7131 9.25826 10.6267 9.52425 10.6876 9.77748L11.5532 13.3733C11.6426 13.7447 11.414 14.1182 11.0427 14.2076C10.8642 14.2506 10.676 14.2208 10.5195 14.1249L7.36128 12.1902C7.13956 12.0544 6.8604 12.0544 6.63869 12.1902Z';
|
||||||
|
const GOLD_STAR_PATH = 'M10.5197 16.2049L6.46899 18.6864C6.04779 18.9444 5.49716 18.8121 5.23913 18.3909C5.11311 18.1852 5.07554 17.9373 5.13494 17.7035L5.762 15.2354C5.98835 14.3444 6.59803 13.5997 7.42671 13.2018L11.8459 11.0801C12.0519 10.9812 12.1387 10.734 12.0398 10.528C11.9597 10.3611 11.7786 10.2677 11.5962 10.2993L6.67709 11.1509C5.67715 11.324 4.65172 11.0479 3.87392 10.3961L2.31994 9.09382C1.94135 8.77655 1.89164 8.21245 2.20891 7.83386C2.36321 7.64972 2.58511 7.53541 2.82462 7.51667L7.5725 7.1451C7.90793 7.11885 8.20025 6.90658 8.32901 6.59574L10.1607 2.17427C10.3497 1.71792 10.8729 1.50123 11.3292 1.69028C11.5484 1.78105 11.7225 1.95514 11.8132 2.17427L13.6449 6.59574C13.7736 6.90658 14.066 7.11885 14.4014 7.1451L19.1754 7.51871C19.6678 7.55725 20.0358 7.9877 19.9972 8.48015C19.9787 8.71704 19.8666 8.93682 19.6858 9.09098L16.0449 12.1949C15.7886 12.4134 15.6768 12.7574 15.7556 13.0849L16.8749 17.7348C16.9905 18.215 16.6949 18.698 16.2147 18.8137C15.9839 18.8692 15.7406 18.8307 15.5382 18.7068L11.4541 16.2049C11.1674 16.0292 10.8064 16.0292 10.5197 16.2049Z';
|
||||||
|
/* eslint-enable max-len */
|
||||||
|
|
||||||
|
const StarIcon: FC<OwnProps> = ({
|
||||||
|
type = 'regular',
|
||||||
|
size = 'small',
|
||||||
|
className,
|
||||||
|
onClick,
|
||||||
|
}) => {
|
||||||
|
const randomId = useUniqueId();
|
||||||
|
const validSvgRandomId = `svg-${randomId}`; // ID must start with a letter
|
||||||
|
|
||||||
|
return (
|
||||||
|
<i
|
||||||
|
onClick={onClick}
|
||||||
|
className={buildClassName(
|
||||||
|
'StarIcon',
|
||||||
|
styles.root,
|
||||||
|
className,
|
||||||
|
onClick && styles.clickable,
|
||||||
|
styles[size],
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{type === 'gold'
|
||||||
|
? <GoldStarIcon randomId={validSvgRandomId} />
|
||||||
|
: type === 'premium'
|
||||||
|
? <PremiumStarIcon randomId={validSvgRandomId} />
|
||||||
|
: <RegularStarIcon />}
|
||||||
|
</i>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
function GoldStarIcon({ randomId }: { randomId: string }) {
|
||||||
|
const fillId = `${randomId}-fill`;
|
||||||
|
const stroke1Id = `${randomId}-stroke1`;
|
||||||
|
const stroke2Id = `${randomId}-stroke2`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg className={styles.svg} width="21" height="20" viewBox="0 0 21 20" fill="none">
|
||||||
|
<defs>
|
||||||
|
<linearGradient
|
||||||
|
id={fillId}
|
||||||
|
x1="0.434893"
|
||||||
|
y1="22.5796"
|
||||||
|
x2="34.2364"
|
||||||
|
y2="-15.5089"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop stop-color="#FDEB32" />
|
||||||
|
<stop offset="0.439058" stop-color="#FEBD04" />
|
||||||
|
<stop offset="1" stop-color="#D75902" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id={stroke1Id}
|
||||||
|
x1="22.5"
|
||||||
|
y1="2.5"
|
||||||
|
x2="8"
|
||||||
|
y2="12.5"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop stop-color="#DB5A00" />
|
||||||
|
<stop offset="1" stop-color="#FF9145" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id={stroke2Id}
|
||||||
|
x1="24.5"
|
||||||
|
y1="2"
|
||||||
|
x2="11"
|
||||||
|
y2="10.2302"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop stop-color="white" stop-opacity="0" />
|
||||||
|
<stop offset="0.395833" stop-color="white" stop-opacity="0.85" />
|
||||||
|
<stop offset="0.520833" stop-color="white" />
|
||||||
|
<stop offset="0.645833" stop-color="white" stop-opacity="0.85" />
|
||||||
|
<stop offset="1" stop-color="white" stop-opacity="0" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d={GOLD_STAR_PATH}
|
||||||
|
fill={`url(#${fillId})`}
|
||||||
|
stroke={`url(#${stroke1Id})`}
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d={GOLD_STAR_PATH}
|
||||||
|
stroke={`url(#${stroke2Id})`}
|
||||||
|
stroke-width="2"
|
||||||
|
style="mix-blend-mode:soft-light"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function PremiumStarIcon({ randomId }: { randomId: string }) {
|
||||||
|
return (
|
||||||
|
<svg className={styles.svg} width="14" height="15" viewBox="0 0 14 15" fill="none">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id={randomId} x1="3" y1="63.5001" x2="84.1475" y2="-1.32262" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#6B93FF" />
|
||||||
|
<stop offset="0.439058" stop-color="#976FFF" />
|
||||||
|
<stop offset="1" stop-color="#E46ACE" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d={STAR_PATH} fill={`url(#${randomId})`} />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RegularStarIcon() {
|
||||||
|
return (
|
||||||
|
<svg className={styles.svg} width="14" height="15" viewBox="0 0 14 15" fill="none">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d={STAR_PATH} fill="var(--color-fill)" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(StarIcon);
|
||||||
@ -22,7 +22,7 @@ import useSelectorSignal from '../../../hooks/useSelectorSignal';
|
|||||||
|
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import Transition, { ACTIVE_SLIDE_CLASS_NAME, TO_SLIDE_CLASS_NAME } from '../../ui/Transition';
|
import Transition, { ACTIVE_SLIDE_CLASS_NAME, TO_SLIDE_CLASS_NAME } from '../../ui/Transition';
|
||||||
import Icon from '../Icon';
|
import Icon from '../icons/Icon';
|
||||||
|
|
||||||
import styles from './BusinessHours.module.scss';
|
import styles from './BusinessHours.module.scss';
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { FC } from '../../../lib/teact/teact';
|
import type { TeactNode } from '../../../lib/teact/teact';
|
||||||
import React, { memo, useEffect, useRef } from '../../../lib/teact/teact';
|
import React, { memo, useEffect, useRef } from '../../../lib/teact/teact';
|
||||||
|
|
||||||
import { ApiMessageEntityTypes } from '../../../api/types';
|
import { ApiMessageEntityTypes } from '../../../api/types';
|
||||||
@ -11,7 +11,7 @@ import useLastCallback from '../../../hooks/useLastCallback';
|
|||||||
import './Spoiler.scss';
|
import './Spoiler.scss';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
children?: React.ReactNode;
|
children?: TeactNode;
|
||||||
containerId?: string;
|
containerId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -19,10 +19,10 @@ const revealByContainerId: Map<string, VoidFunction[]> = new Map();
|
|||||||
|
|
||||||
const buildClassName = createClassNameBuilder('Spoiler');
|
const buildClassName = createClassNameBuilder('Spoiler');
|
||||||
|
|
||||||
const Spoiler: FC<OwnProps> = ({
|
const Spoiler = ({
|
||||||
children,
|
children,
|
||||||
containerId,
|
containerId,
|
||||||
}) => {
|
}: OwnProps) => {
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const contentRef = useRef<HTMLDivElement>(null);
|
const contentRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
|||||||
@ -89,7 +89,7 @@
|
|||||||
color: var(--color-white);
|
color: var(--color-white);
|
||||||
}
|
}
|
||||||
|
|
||||||
.VerifiedIcon, .PremiumIcon {
|
.VerifiedIcon, .StarIcon {
|
||||||
--color-fill: #fff;
|
--color-fill: #fff;
|
||||||
--color-checkmark: var(--color-primary);
|
--color-checkmark: var(--color-primary);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -121,7 +121,7 @@
|
|||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.PremiumIcon {
|
.StarIcon {
|
||||||
width: 1.5rem;
|
width: 1.5rem;
|
||||||
height: 1.5rem;
|
height: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import useEffectWithPrevDeps from '../../../hooks/useEffectWithPrevDeps';
|
|||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
|
|
||||||
import CustomEmoji from '../../common/CustomEmoji';
|
import CustomEmoji from '../../common/CustomEmoji';
|
||||||
import PremiumIcon from '../../common/PremiumIcon';
|
import StarIcon from '../../common/icons/StarIcon';
|
||||||
import CustomEmojiEffect from '../../common/reactions/CustomEmojiEffect';
|
import CustomEmojiEffect from '../../common/reactions/CustomEmojiEffect';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import StatusPickerMenu from './StatusPickerMenu.async';
|
import StatusPickerMenu from './StatusPickerMenu.async';
|
||||||
@ -82,7 +82,7 @@ const StatusButton: FC<StateProps> = ({ emojiStatus }) => {
|
|||||||
size={EMOJI_STATUS_SIZE}
|
size={EMOJI_STATUS_SIZE}
|
||||||
loopLimit={EMOJI_STATUS_LOOP_LIMIT}
|
loopLimit={EMOJI_STATUS_LOOP_LIMIT}
|
||||||
/>
|
/>
|
||||||
) : <PremiumIcon />}
|
) : <StarIcon />}
|
||||||
</Button>
|
</Button>
|
||||||
<StatusPickerMenu
|
<StatusPickerMenu
|
||||||
statusButtonRef={buttonRef}
|
statusButtonRef={buttonRef}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import type { ApiPremiumSection } from '../../../global/types';
|
|||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
import PremiumIcon from '../../common/PremiumIcon';
|
import StarIcon from '../../common/icons/StarIcon';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
@ -21,7 +21,7 @@ function PremiumStatusItem({ premiumSection }: OwnProps) {
|
|||||||
return (
|
return (
|
||||||
<div className="settings-item">
|
<div className="settings-item">
|
||||||
<ListItem
|
<ListItem
|
||||||
leftElement={<PremiumIcon className="icon" withGradient big />}
|
leftElement={<StarIcon className="icon" type="premium" size="big" />}
|
||||||
onClick={handleOpenPremiumModal}
|
onClick={handleOpenPremiumModal}
|
||||||
>
|
>
|
||||||
{lang('PrivacyLastSeenPremium')}
|
{lang('PrivacyLastSeenPremium')}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { getActions } from '../../../global';
|
|||||||
|
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
|
||||||
import Icon from '../../common/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
|
|
||||||
import styles from './PrivacyLockedOption.module.scss';
|
import styles from './PrivacyLockedOption.module.scss';
|
||||||
|
|
||||||
|
|||||||
@ -103,7 +103,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-main-menu-premium .PremiumIcon {
|
.settings-main-menu-star .StarIcon {
|
||||||
margin-right: 1.25rem;
|
margin-right: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -78,7 +78,7 @@ const SettingsExperimental: FC<OwnProps & StateProps> = ({
|
|||||||
<div className="settings-item">
|
<div className="settings-item">
|
||||||
<ListItem
|
<ListItem
|
||||||
// eslint-disable-next-line react/jsx-no-bind
|
// eslint-disable-next-line react/jsx-no-bind
|
||||||
onClick={() => requestConfetti({})}
|
onClick={() => requestConfetti({ withStars: true })}
|
||||||
icon="animations"
|
icon="animations"
|
||||||
>
|
>
|
||||||
<div className="title">Launch some confetti!</div>
|
<div className="title">Launch some confetti!</div>
|
||||||
|
|||||||
@ -9,13 +9,14 @@ import {
|
|||||||
selectIsGiveawayGiftsPurchaseAvailable,
|
selectIsGiveawayGiftsPurchaseAvailable,
|
||||||
selectIsPremiumPurchaseBlocked,
|
selectIsPremiumPurchaseBlocked,
|
||||||
} from '../../../global/selectors';
|
} from '../../../global/selectors';
|
||||||
|
import { formatInteger } from '../../../util/textFormat';
|
||||||
|
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
import PremiumIcon from '../../common/PremiumIcon';
|
import StarIcon from '../../common/icons/StarIcon';
|
||||||
import ChatExtra from '../../common/profile/ChatExtra';
|
import ChatExtra from '../../common/profile/ChatExtra';
|
||||||
import ProfileInfo from '../../common/ProfileInfo';
|
import ProfileInfo from '../../common/ProfileInfo';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
@ -32,16 +33,20 @@ type StateProps = {
|
|||||||
currentUserId?: string;
|
currentUserId?: string;
|
||||||
canBuyPremium?: boolean;
|
canBuyPremium?: boolean;
|
||||||
isGiveawayAvailable?: boolean;
|
isGiveawayAvailable?: boolean;
|
||||||
|
starsBalance?: number;
|
||||||
|
shouldDisplayStars?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SettingsMain: FC<OwnProps & StateProps> = ({
|
const SettingsMain: FC<OwnProps & StateProps> = ({
|
||||||
isActive,
|
isActive,
|
||||||
onScreenSelect,
|
|
||||||
onReset,
|
|
||||||
currentUserId,
|
currentUserId,
|
||||||
sessionCount,
|
sessionCount,
|
||||||
canBuyPremium,
|
canBuyPremium,
|
||||||
isGiveawayAvailable,
|
isGiveawayAvailable,
|
||||||
|
starsBalance,
|
||||||
|
shouldDisplayStars,
|
||||||
|
onScreenSelect,
|
||||||
|
onReset,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
loadProfilePhotos,
|
loadProfilePhotos,
|
||||||
@ -49,6 +54,7 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
|
|||||||
openSupportChat,
|
openSupportChat,
|
||||||
openUrl,
|
openUrl,
|
||||||
openPremiumGiftingModal,
|
openPremiumGiftingModal,
|
||||||
|
openStarsBalanceModal,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const [isSupportDialogOpen, openSupportDialog, closeSupportDialog] = useFlag(false);
|
const [isSupportDialogOpen, openSupportDialog, closeSupportDialog] = useFlag(false);
|
||||||
@ -156,18 +162,31 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
|
|||||||
<div className="settings-main-menu">
|
<div className="settings-main-menu">
|
||||||
{canBuyPremium && (
|
{canBuyPremium && (
|
||||||
<ListItem
|
<ListItem
|
||||||
leftElement={<PremiumIcon className="icon" withGradient big />}
|
leftElement={<StarIcon className="icon" type="premium" size="big" />}
|
||||||
className="settings-main-menu-premium"
|
className="settings-main-menu-star"
|
||||||
// eslint-disable-next-line react/jsx-no-bind
|
// eslint-disable-next-line react/jsx-no-bind
|
||||||
onClick={() => openPremiumModal()}
|
onClick={() => openPremiumModal()}
|
||||||
>
|
>
|
||||||
{lang('TelegramPremium')}
|
{lang('TelegramPremium')}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
)}
|
)}
|
||||||
|
{shouldDisplayStars && (
|
||||||
|
<ListItem
|
||||||
|
leftElement={<StarIcon className="icon" type="gold" size="big" />}
|
||||||
|
className="settings-main-menu-star"
|
||||||
|
// eslint-disable-next-line react/jsx-no-bind
|
||||||
|
onClick={() => openStarsBalanceModal({})}
|
||||||
|
>
|
||||||
|
{lang('MenuTelegramStars')}
|
||||||
|
{Boolean(starsBalance) && (
|
||||||
|
<span className="settings-item__current-value">{formatInteger(starsBalance)}</span>
|
||||||
|
)}
|
||||||
|
</ListItem>
|
||||||
|
)}
|
||||||
{isGiveawayAvailable && (
|
{isGiveawayAvailable && (
|
||||||
<ListItem
|
<ListItem
|
||||||
icon="gift"
|
icon="gift"
|
||||||
className="settings-main-menu-premium"
|
className="settings-main-menu-star"
|
||||||
// eslint-disable-next-line react/jsx-no-bind
|
// eslint-disable-next-line react/jsx-no-bind
|
||||||
onClick={() => openPremiumGiftingModal()}
|
onClick={() => openPremiumGiftingModal()}
|
||||||
>
|
>
|
||||||
@ -213,12 +232,16 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
(global): StateProps => {
|
(global): StateProps => {
|
||||||
const { currentUserId } = global;
|
const { currentUserId } = global;
|
||||||
const isGiveawayAvailable = selectIsGiveawayGiftsPurchaseAvailable(global);
|
const isGiveawayAvailable = selectIsGiveawayGiftsPurchaseAvailable(global);
|
||||||
|
const starsBalance = global.stars?.balance;
|
||||||
|
const shouldDisplayStars = Boolean(global.stars?.history?.all?.transactions.length);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sessionCount: global.activeSessions.orderedHashes.length,
|
sessionCount: global.activeSessions.orderedHashes.length,
|
||||||
currentUserId,
|
currentUserId,
|
||||||
canBuyPremium: !selectIsPremiumPurchaseBlocked(global),
|
canBuyPremium: !selectIsPremiumPurchaseBlocked(global),
|
||||||
isGiveawayAvailable,
|
isGiveawayAvailable,
|
||||||
|
starsBalance,
|
||||||
|
shouldDisplayStars,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(SettingsMain));
|
)(SettingsMain));
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import { selectCanSetPasscode, selectIsCurrentUserPremium } from '../../../globa
|
|||||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
|
||||||
import PremiumIcon from '../../common/PremiumIcon';
|
import StarIcon from '../../common/icons/StarIcon';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
|
|
||||||
@ -282,7 +282,7 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
|||||||
<ListItem
|
<ListItem
|
||||||
narrow
|
narrow
|
||||||
allowDisabledClick
|
allowDisabledClick
|
||||||
rightElement={isCurrentUserPremium && <PremiumIcon big withGradient />}
|
rightElement={isCurrentUserPremium && <StarIcon size="big" type="premium" />}
|
||||||
className="no-icon"
|
className="no-icon"
|
||||||
// eslint-disable-next-line react/jsx-no-bind
|
// eslint-disable-next-line react/jsx-no-bind
|
||||||
onClick={() => onScreenSelect(SettingsScreens.PrivacyVoiceMessages)}
|
onClick={() => onScreenSelect(SettingsScreens.PrivacyVoiceMessages)}
|
||||||
@ -296,7 +296,7 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
|||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem
|
<ListItem
|
||||||
narrow
|
narrow
|
||||||
rightElement={isCurrentUserPremium && <PremiumIcon big withGradient />}
|
rightElement={isCurrentUserPremium && <StarIcon size="big" type="premium" />}
|
||||||
className="no-icon"
|
className="no-icon"
|
||||||
// eslint-disable-next-line react/jsx-no-bind
|
// eslint-disable-next-line react/jsx-no-bind
|
||||||
onClick={() => onScreenSelect(SettingsScreens.PrivacyMessages)}
|
onClick={() => onScreenSelect(SettingsScreens.PrivacyMessages)}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import renderText from '../../common/helpers/renderText';
|
|||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
import PremiumIcon from '../../common/PremiumIcon';
|
import StarIcon from '../../common/icons/StarIcon';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ const SettingsPrivacyLastSeen = ({
|
|||||||
)}
|
)}
|
||||||
<div className="settings-item">
|
<div className="settings-item">
|
||||||
<ListItem
|
<ListItem
|
||||||
leftElement={<PremiumIcon className="icon" withGradient big />}
|
leftElement={<StarIcon className="icon" type="premium" size="big" />}
|
||||||
onClick={handleOpenPremiumModal}
|
onClick={handleOpenPremiumModal}
|
||||||
>
|
>
|
||||||
{isCurrentUserPremium ? lang('PrivacyLastSeenPremiumForPremium') : lang('PrivacyLastSeenPremium')}
|
{isCurrentUserPremium ? lang('PrivacyLastSeenPremiumForPremium') : lang('PrivacyLastSeenPremium')}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import useFlag from '../../hooks/useFlag';
|
|||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
import useLastCallback from '../../hooks/useLastCallback';
|
import useLastCallback from '../../hooks/useLastCallback';
|
||||||
|
|
||||||
import Icon from '../common/Icon';
|
import Icon from '../common/icons/Icon';
|
||||||
import Picker from '../common/Picker';
|
import Picker from '../common/Picker';
|
||||||
import Button from '../ui/Button';
|
import Button from '../ui/Button';
|
||||||
import ConfirmDialog from '../ui/ConfirmDialog';
|
import ConfirmDialog from '../ui/ConfirmDialog';
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import type { FC } from '../../lib/teact/teact';
|
|
||||||
import React, { memo, useRef } from '../../lib/teact/teact';
|
import React, { memo, useRef } from '../../lib/teact/teact';
|
||||||
import { withGlobal } from '../../global';
|
import { withGlobal } from '../../global';
|
||||||
|
|
||||||
@ -32,6 +31,7 @@ interface Confetti {
|
|||||||
};
|
};
|
||||||
size: number;
|
size: number;
|
||||||
color: string;
|
color: string;
|
||||||
|
isStar?: boolean;
|
||||||
flicker: number;
|
flicker: number;
|
||||||
flickerFrequency: number;
|
flickerFrequency: number;
|
||||||
rotation: number;
|
rotation: number;
|
||||||
@ -42,8 +42,11 @@ interface Confetti {
|
|||||||
const CONFETTI_FADEOUT_TIMEOUT = 10000;
|
const CONFETTI_FADEOUT_TIMEOUT = 10000;
|
||||||
const DEFAULT_CONFETTI_SIZE = 10;
|
const DEFAULT_CONFETTI_SIZE = 10;
|
||||||
const CONFETTI_COLORS = ['#E8BC2C', '#D0049E', '#02CBFE', '#5723FD', '#FE8C27', '#6CB859'];
|
const CONFETTI_COLORS = ['#E8BC2C', '#D0049E', '#02CBFE', '#5723FD', '#FE8C27', '#6CB859'];
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
const STAR_PATH = new Path2D('M6.63869 12.1902L3.50621 14.1092C3.18049 14.3087 2.75468 14.2064 2.55515 13.8807C2.45769 13.7216 2.42864 13.5299 2.47457 13.3491L2.95948 11.4405C3.13452 10.7515 3.60599 10.1756 4.24682 9.86791L7.6642 8.22716C7.82352 8.15067 7.89067 7.95951 7.81418 7.80019C7.75223 7.67116 7.61214 7.59896 7.47111 7.62338L3.66713 8.28194C2.89387 8.41581 2.1009 8.20228 1.49941 7.69823L0.297703 6.69116C0.00493565 6.44581 -0.0335059 6.00958 0.211842 5.71682C0.33117 5.57442 0.502766 5.48602 0.687982 5.47153L4.35956 5.18419C4.61895 5.16389 4.845 4.99974 4.94458 4.75937L6.36101 1.3402C6.5072 0.987302 6.91179 0.819734 7.26469 0.965925C7.43413 1.03612 7.56876 1.17075 7.63896 1.3402L9.05539 4.75937C9.15496 4.99974 9.38101 5.16389 9.6404 5.18419L13.3322 5.47311C13.713 5.50291 13.9975 5.83578 13.9677 6.2166C13.9534 6.39979 13.8667 6.56975 13.7269 6.68896L10.9114 9.08928C10.7131 9.25826 10.6267 9.52425 10.6876 9.77748L11.5532 13.3733C11.6426 13.7447 11.414 14.1182 11.0427 14.2076C10.8642 14.2506 10.676 14.2208 10.5195 14.1249L7.36128 12.1902C7.13956 12.0544 6.8604 12.0544 6.63869 12.1902Z');
|
||||||
|
const STAR_SIZE_MULTIPLIER = 1.5;
|
||||||
|
|
||||||
const ConfettiContainer: FC<StateProps> = ({ confetti }) => {
|
const ConfettiContainer = ({ confetti }: StateProps) => {
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
const confettiRef = useRef<Confetti[]>([]);
|
const confettiRef = useRef<Confetti[]>([]);
|
||||||
@ -76,6 +79,7 @@ const ConfettiContainer: FC<StateProps> = ({ confetti }) => {
|
|||||||
rotation: 0,
|
rotation: 0,
|
||||||
lastDrawnAt: Date.now(),
|
lastDrawnAt: Date.now(),
|
||||||
frameCount: 0,
|
frameCount: 0,
|
||||||
|
isStar: confetti?.withStars && Math.random() > 0.8,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -143,17 +147,29 @@ const ConfettiContainer: FC<StateProps> = ({ confetti }) => {
|
|||||||
|
|
||||||
confettiRef.current[i] = newConfetti;
|
confettiRef.current[i] = newConfetti;
|
||||||
ctx.fillStyle = color;
|
ctx.fillStyle = color;
|
||||||
ctx.beginPath();
|
if (c.isStar) {
|
||||||
ctx.ellipse(
|
ctx.save();
|
||||||
pos.x,
|
ctx.translate(pos.x, pos.y);
|
||||||
pos.y,
|
ctx.scale(
|
||||||
size,
|
(size / DEFAULT_CONFETTI_SIZE) * STAR_SIZE_MULTIPLIER,
|
||||||
flicker,
|
(size / DEFAULT_CONFETTI_SIZE) * STAR_SIZE_MULTIPLIER,
|
||||||
rotation,
|
);
|
||||||
0,
|
ctx.rotate(rotation);
|
||||||
2 * Math.PI,
|
ctx.fill(STAR_PATH);
|
||||||
);
|
ctx.restore();
|
||||||
ctx.fill();
|
} else {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.ellipse(
|
||||||
|
pos.x,
|
||||||
|
pos.y,
|
||||||
|
size,
|
||||||
|
flicker,
|
||||||
|
rotation,
|
||||||
|
0,
|
||||||
|
2 * Math.PI,
|
||||||
|
);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
confettiRef.current = confettiRef.current.filter((c) => !confettiToRemove.includes(c));
|
confettiRef.current = confettiRef.current.filter((c) => !confettiToRemove.includes(c));
|
||||||
if (confettiRef.current.length) {
|
if (confettiRef.current.length) {
|
||||||
|
|||||||
@ -248,6 +248,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
|||||||
loadSavedReactionTags,
|
loadSavedReactionTags,
|
||||||
loadTimezones,
|
loadTimezones,
|
||||||
loadQuickReplies,
|
loadQuickReplies,
|
||||||
|
loadStarStatus,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
if (DEBUG && !DEBUG_isLogged) {
|
if (DEBUG && !DEBUG_isLogged) {
|
||||||
@ -328,6 +329,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
|||||||
loadSavedReactionTags();
|
loadSavedReactionTags();
|
||||||
loadTimezones();
|
loadTimezones();
|
||||||
loadQuickReplies();
|
loadQuickReplies();
|
||||||
|
loadStarStatus();
|
||||||
}
|
}
|
||||||
}, [isMasterTab, isSynced]);
|
}, [isMasterTab, isSynced]);
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import useLang from '../../../hooks/useLang';
|
|||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
import AvatarList from '../../common/AvatarList';
|
import AvatarList from '../../common/AvatarList';
|
||||||
import Icon from '../../common/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import Link from '../../ui/Link';
|
import Link from '../../ui/Link';
|
||||||
import Modal from '../../ui/Modal';
|
import Modal from '../../ui/Modal';
|
||||||
@ -122,6 +122,7 @@ const GiftPremiumModal: FC<OwnProps & StateProps> = ({
|
|||||||
left,
|
left,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
withStars: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -31,7 +31,7 @@ import useLastCallback from '../../../hooks/useLastCallback';
|
|||||||
import CalendarModal from '../../common/CalendarModal';
|
import CalendarModal from '../../common/CalendarModal';
|
||||||
import CountryPickerModal from '../../common/CountryPickerModal';
|
import CountryPickerModal from '../../common/CountryPickerModal';
|
||||||
import GroupChatInfo from '../../common/GroupChatInfo';
|
import GroupChatInfo from '../../common/GroupChatInfo';
|
||||||
import Icon from '../../common/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
import InputText from '../../ui/InputText';
|
import InputText from '../../ui/InputText';
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import buildClassName from '../../../util/buildClassName';
|
|||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
import Icon from '../../common/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
|
|
||||||
import styles from './GiveawayTypeOption.module.scss';
|
import styles from './GiveawayTypeOption.module.scss';
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import sortChatIds from '../../common/helpers/sortChatIds';
|
|||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
import Icon from '../../common/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
import Picker from '../../common/Picker';
|
import Picker from '../../common/Picker';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import Modal from '../../ui/Modal';
|
import Modal from '../../ui/Modal';
|
||||||
|
|||||||
@ -202,6 +202,7 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
|||||||
left,
|
left,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
withStars: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import renderText from '../../../common/helpers/renderText';
|
|||||||
import useFlag from '../../../../hooks/useFlag';
|
import useFlag from '../../../../hooks/useFlag';
|
||||||
import useLang from '../../../../hooks/useLang';
|
import useLang from '../../../../hooks/useLang';
|
||||||
|
|
||||||
import Icon from '../../../common/Icon';
|
import Icon from '../../../common/icons/Icon';
|
||||||
import Button from '../../../ui/Button';
|
import Button from '../../../ui/Button';
|
||||||
import Modal from '../../../ui/Modal';
|
import Modal from '../../../ui/Modal';
|
||||||
import PremiumLimitsCompare from './PremiumLimitsCompare';
|
import PremiumLimitsCompare from './PremiumLimitsCompare';
|
||||||
|
|||||||
@ -102,7 +102,9 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
|
|||||||
observeIntersectionForPlaying,
|
observeIntersectionForPlaying,
|
||||||
onPinnedIntersectionChange,
|
onPinnedIntersectionChange,
|
||||||
}) => {
|
}) => {
|
||||||
const { openPremiumModal, requestConfetti, checkGiftCode } = getActions();
|
const {
|
||||||
|
openPremiumModal, requestConfetti, checkGiftCode, getReceipt,
|
||||||
|
} = getActions();
|
||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
@ -150,7 +152,7 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isVisible && shouldShowConfettiRef.current) {
|
if (isVisible && shouldShowConfettiRef.current) {
|
||||||
shouldShowConfettiRef.current = false;
|
shouldShowConfettiRef.current = false;
|
||||||
requestConfetti({});
|
requestConfetti({ withStars: true });
|
||||||
}
|
}
|
||||||
}, [isVisible, requestConfetti]);
|
}, [isVisible, requestConfetti]);
|
||||||
|
|
||||||
@ -210,6 +212,15 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
|
|||||||
checkGiftCode({ slug, message: { chatId: message.chatId, messageId: message.id } });
|
checkGiftCode({ slug, message: { chatId: message.chatId, messageId: message.id } });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
if (message.content.action?.type === 'receipt') {
|
||||||
|
getReceipt({
|
||||||
|
chatId: message.chatId,
|
||||||
|
messageId: message.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// TODO Refactoring for action rendering
|
// TODO Refactoring for action rendering
|
||||||
const shouldSkipRender = isInsideTopic && message.content.action?.text === 'TopicWasCreatedAction';
|
const shouldSkipRender = isInsideTopic && message.content.action?.text === 'TopicWasCreatedAction';
|
||||||
if (shouldSkipRender) {
|
if (shouldSkipRender) {
|
||||||
@ -294,7 +305,7 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
|
|||||||
onContextMenu={handleContextMenu}
|
onContextMenu={handleContextMenu}
|
||||||
>
|
>
|
||||||
{!isSuggestedAvatar && !isGiftCode && !isJoinedMessage && (
|
{!isSuggestedAvatar && !isGiftCode && !isJoinedMessage && (
|
||||||
<span className="action-message-content">{renderContent()}</span>
|
<span className="action-message-content" onClick={handleClick}>{renderContent()}</span>
|
||||||
)}
|
)}
|
||||||
{isGift && renderGift()}
|
{isGift && renderGift()}
|
||||||
{isGiftCode && renderGiftCode()}
|
{isGiftCode && renderGiftCode()}
|
||||||
|
|||||||
@ -132,6 +132,7 @@ type StateProps = {
|
|||||||
|
|
||||||
const MESSAGE_REACTIONS_POLLING_INTERVAL = 20 * 1000;
|
const MESSAGE_REACTIONS_POLLING_INTERVAL = 20 * 1000;
|
||||||
const MESSAGE_COMMENTS_POLLING_INTERVAL = 20 * 1000;
|
const MESSAGE_COMMENTS_POLLING_INTERVAL = 20 * 1000;
|
||||||
|
const MESSAGE_FACT_CHECK_UPDATE_INTERVAL = 5 * 1000;
|
||||||
const MESSAGE_STORY_POLLING_INTERVAL = 5 * 60 * 1000;
|
const MESSAGE_STORY_POLLING_INTERVAL = 5 * 60 * 1000;
|
||||||
const BOTTOM_THRESHOLD = 50;
|
const BOTTOM_THRESHOLD = 50;
|
||||||
const UNREAD_DIVIDER_TOP = 10;
|
const UNREAD_DIVIDER_TOP = 10;
|
||||||
@ -188,7 +189,7 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
loadViewportMessages, setScrollOffset, loadSponsoredMessages, loadMessageReactions, copyMessagesByIds,
|
loadViewportMessages, setScrollOffset, loadSponsoredMessages, loadMessageReactions, copyMessagesByIds,
|
||||||
loadMessageViews, loadPeerStoriesByIds,
|
loadMessageViews, loadPeerStoriesByIds, loadFactChecks,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
@ -320,6 +321,17 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
|||||||
loadMessageViews({ chatId, ids });
|
loadMessageViews({ chatId, ids });
|
||||||
}, MESSAGE_COMMENTS_POLLING_INTERVAL, true);
|
}, MESSAGE_COMMENTS_POLLING_INTERVAL, true);
|
||||||
|
|
||||||
|
useInterval(() => {
|
||||||
|
if (!messageIds || !messagesById || threadId !== MAIN_THREAD_ID || type === 'scheduled') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ids = messageIds.filter((id) => messagesById[id].factCheck?.shouldFetch);
|
||||||
|
|
||||||
|
if (!ids.length) return;
|
||||||
|
|
||||||
|
loadFactChecks({ chatId, ids });
|
||||||
|
}, MESSAGE_FACT_CHECK_UPDATE_INTERVAL);
|
||||||
|
|
||||||
const loadMoreAround = useMemo(() => {
|
const loadMoreAround = useMemo(() => {
|
||||||
if (type !== 'thread') {
|
if (type !== 'thread') {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import useLang from '../../hooks/useLang';
|
|||||||
import useLastCallback from '../../hooks/useLastCallback';
|
import useLastCallback from '../../hooks/useLastCallback';
|
||||||
|
|
||||||
import AnimatedIconWithPreview from '../common/AnimatedIconWithPreview';
|
import AnimatedIconWithPreview from '../common/AnimatedIconWithPreview';
|
||||||
import Icon from '../common/Icon';
|
import Icon from '../common/icons/Icon';
|
||||||
import Button from '../ui/Button';
|
import Button from '../ui/Button';
|
||||||
|
|
||||||
import styles from './PremiumRequiredMessage.module.scss';
|
import styles from './PremiumRequiredMessage.module.scss';
|
||||||
|
|||||||
@ -30,7 +30,7 @@ import useLang from '../../../hooks/useLang';
|
|||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useMouseInside from '../../../hooks/useMouseInside';
|
import useMouseInside from '../../../hooks/useMouseInside';
|
||||||
|
|
||||||
import Icon from '../../common/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
import Menu from '../../ui/Menu';
|
import Menu from '../../ui/Menu';
|
||||||
import MenuItem from '../../ui/MenuItem';
|
import MenuItem from '../../ui/MenuItem';
|
||||||
import ResponsiveHoverButton from '../../ui/ResponsiveHoverButton';
|
import ResponsiveHoverButton from '../../ui/ResponsiveHoverButton';
|
||||||
|
|||||||
@ -38,7 +38,7 @@ import useMenuPosition from '../../../hooks/useMenuPosition';
|
|||||||
import useShowTransition from '../../../hooks/useShowTransition';
|
import useShowTransition from '../../../hooks/useShowTransition';
|
||||||
|
|
||||||
import { ClosableEmbeddedMessage } from '../../common/embedded/EmbeddedMessage';
|
import { ClosableEmbeddedMessage } from '../../common/embedded/EmbeddedMessage';
|
||||||
import Icon from '../../common/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import Menu from '../../ui/Menu';
|
import Menu from '../../ui/Menu';
|
||||||
import MenuItem from '../../ui/MenuItem';
|
import MenuItem from '../../ui/MenuItem';
|
||||||
|
|||||||
@ -1,21 +1,6 @@
|
|||||||
.root {
|
.root {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 0.25rem 0.25rem 0.875rem 0.25rem;
|
margin: 0.25rem 0.25rem 0.875rem 0.25rem;
|
||||||
border-radius: 0.25rem;
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: var(--accent-background-color);
|
|
||||||
color: var(--accent-color);
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: "";
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
inset-inline-start: 0;
|
|
||||||
width: 0.1875rem;
|
|
||||||
background: var(--bar-gradient, var(--accent-color));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-container {
|
.info-container {
|
||||||
|
|||||||
52
src/components/middle/message/FactCheck.module.scss
Normal file
52
src/components/middle/message/FactCheck.module.scss
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
@use '../../../styles/mixins';
|
||||||
|
|
||||||
|
.root {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
padding-block: 0.25rem;
|
||||||
|
padding-inline-end: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
--color-dividers: var(--accent-color);
|
||||||
|
|
||||||
|
margin-block: 0.25rem;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footnote {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsed {
|
||||||
|
@include mixins.gradient-border-bottom(1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cutoutWrapper {
|
||||||
|
max-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapseIcon {
|
||||||
|
position: absolute;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clickable {
|
||||||
|
cursor: var(--custom-cursor, pointer);
|
||||||
|
}
|
||||||
89
src/components/middle/message/FactCheck.tsx
Normal file
89
src/components/middle/message/FactCheck.tsx
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import React, { memo, useMemo, useRef } from '../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import type { ApiFactCheck } from '../../../api/types';
|
||||||
|
|
||||||
|
import buildClassName from '../../../util/buildClassName';
|
||||||
|
import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities';
|
||||||
|
|
||||||
|
import useCollapsibleLines from '../../../hooks/element/useCollapsibleLines';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
|
import Icon from '../../common/icons/Icon';
|
||||||
|
import PeerColorWrapper from '../../common/PeerColorWrapper';
|
||||||
|
import Separator from '../../ui/Separator';
|
||||||
|
|
||||||
|
import styles from './FactCheck.module.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
factCheck: ApiFactCheck;
|
||||||
|
isToggleDisabled?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const COLOR = {
|
||||||
|
color: 0,
|
||||||
|
};
|
||||||
|
const MAX_LINES = 4;
|
||||||
|
|
||||||
|
const FactCheck = ({ factCheck, isToggleDisabled }: OwnProps) => {
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
const cutoutRef = useRef<HTMLDivElement>(null);
|
||||||
|
const {
|
||||||
|
isCollapsed, isCollapsible, setIsCollapsed,
|
||||||
|
} = useCollapsibleLines(ref, MAX_LINES, cutoutRef);
|
||||||
|
|
||||||
|
const countryLocalized = useMemo(() => {
|
||||||
|
if (!factCheck.countryCode || !lang.code) return undefined;
|
||||||
|
|
||||||
|
const displayNames = new Intl.DisplayNames([lang.code], { type: 'region' });
|
||||||
|
return displayNames.of(factCheck.countryCode);
|
||||||
|
}, [factCheck.countryCode, lang.code]);
|
||||||
|
|
||||||
|
const canExpand = !isToggleDisabled && isCollapsed;
|
||||||
|
|
||||||
|
const handleExpand = useLastCallback(() => {
|
||||||
|
setIsCollapsed(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleToggle = useLastCallback(() => {
|
||||||
|
setIsCollapsed((prev) => !prev);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!factCheck.text) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PeerColorWrapper peerColor={COLOR} className={styles.root} onClick={canExpand ? handleExpand : undefined}>
|
||||||
|
<div
|
||||||
|
ref={cutoutRef}
|
||||||
|
className={buildClassName(styles.cutoutWrapper, isCollapsed && styles.collapsed)}
|
||||||
|
>
|
||||||
|
<div className={styles.title}>{lang('FactCheck')}</div>
|
||||||
|
<div ref={ref} className={styles.content}>
|
||||||
|
{renderTextWithEntities({
|
||||||
|
text: factCheck.text.text,
|
||||||
|
entities: factCheck.text.entities,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<Separator className={styles.separator} />
|
||||||
|
<div className={styles.footnote}>{lang('FactCheckFooter', countryLocalized)}</div>
|
||||||
|
</div>
|
||||||
|
{isCollapsible && (
|
||||||
|
<div
|
||||||
|
className={buildClassName(styles.collapseIcon, !isToggleDisabled && styles.clickable)}
|
||||||
|
onClick={!isToggleDisabled ? handleToggle : undefined}
|
||||||
|
aria-hidden
|
||||||
|
>
|
||||||
|
<Icon name={isCollapsed ? 'down' : 'up'} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</PeerColorWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(FactCheck);
|
||||||
@ -38,16 +38,16 @@
|
|||||||
top: 0;
|
top: 0;
|
||||||
padding: 0.25rem 0.5rem;
|
padding: 0.25rem 0.5rem;
|
||||||
margin: 0.25rem;
|
margin: 0.25rem;
|
||||||
background-color: rgba(0, 0, 0, 0.2);
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
border-radius: var(--border-radius-messages-small);
|
border-radius: var(--border-radius-messages-small);
|
||||||
color: var(--color-white);
|
color: var(--color-white);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|
||||||
span {
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.test-invoice {
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.invoice-image-container {
|
.invoice-image-container {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import type { ApiMessage } from '../../../api/types';
|
|||||||
import type { ISettings } from '../../../types';
|
import type { ISettings } from '../../../types';
|
||||||
|
|
||||||
import { CUSTOM_APPENDIX_ATTRIBUTE, MESSAGE_CONTENT_SELECTOR } from '../../../config';
|
import { CUSTOM_APPENDIX_ATTRIBUTE, MESSAGE_CONTENT_SELECTOR } from '../../../config';
|
||||||
|
import { requestMutation } from '../../../lib/fasterdom/fasterdom';
|
||||||
import { getMessageInvoice, getWebDocumentHash } from '../../../global/helpers';
|
import { getMessageInvoice, getWebDocumentHash } from '../../../global/helpers';
|
||||||
import buildStyle from '../../../util/buildStyle';
|
import buildStyle from '../../../util/buildStyle';
|
||||||
import { formatCurrency } from '../../../util/formatCurrency';
|
import { formatCurrency } from '../../../util/formatCurrency';
|
||||||
@ -67,8 +68,10 @@ const Invoice: FC<OwnProps> = ({
|
|||||||
if (photoUrl) {
|
if (photoUrl) {
|
||||||
const contentEl = ref.current!.closest<HTMLDivElement>(MESSAGE_CONTENT_SELECTOR)!;
|
const contentEl = ref.current!.closest<HTMLDivElement>(MESSAGE_CONTENT_SELECTOR)!;
|
||||||
getCustomAppendixBg(photoUrl, false, isSelected, theme).then((appendixBg) => {
|
getCustomAppendixBg(photoUrl, false, isSelected, theme).then((appendixBg) => {
|
||||||
contentEl.style.setProperty('--appendix-bg', appendixBg);
|
requestMutation(() => {
|
||||||
contentEl.setAttribute(CUSTOM_APPENDIX_ATTRIBUTE, '');
|
contentEl.style.setProperty('--appendix-bg', appendixBg);
|
||||||
|
contentEl.setAttribute(CUSTOM_APPENDIX_ATTRIBUTE, '');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [shouldAffectAppendix, photoUrl, isInSelectMode, isSelected, theme]);
|
}, [shouldAffectAppendix, photoUrl, isInSelectMode, isSelected, theme]);
|
||||||
@ -117,7 +120,7 @@ const Invoice: FC<OwnProps> = ({
|
|||||||
)}
|
)}
|
||||||
<p className="description-text">
|
<p className="description-text">
|
||||||
{formatCurrency(amount, currency, lang.code)}
|
{formatCurrency(amount, currency, lang.code)}
|
||||||
{isTest && <span>{lang('PaymentTestInvoice')}</span>}
|
{isTest && <span className="test-invoice">{lang('PaymentTestInvoice')}</span>}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { FC } from '../../../lib/teact/teact';
|
import type { TeactNode } from '../../../lib/teact/teact';
|
||||||
import React from '../../../lib/teact/teact';
|
import React from '../../../lib/teact/teact';
|
||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
@ -12,19 +12,19 @@ import useAppLayout from '../../../hooks/useAppLayout';
|
|||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
userId?: string;
|
userId?: string;
|
||||||
username?: string;
|
username?: string;
|
||||||
children: React.ReactNode;
|
children: TeactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
userOrChat?: ApiPeer;
|
userOrChat?: ApiPeer;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MentionLink: FC<OwnProps & StateProps> = ({
|
const MentionLink = ({
|
||||||
userId,
|
userId,
|
||||||
username,
|
username,
|
||||||
userOrChat,
|
userOrChat,
|
||||||
children,
|
children,
|
||||||
}) => {
|
}: OwnProps & StateProps) => {
|
||||||
const {
|
const {
|
||||||
openChat,
|
openChat,
|
||||||
openChatByUsername,
|
openChatByUsername,
|
||||||
|
|||||||
@ -145,9 +145,9 @@ import DotAnimation from '../../common/DotAnimation';
|
|||||||
import EmbeddedMessage from '../../common/embedded/EmbeddedMessage';
|
import EmbeddedMessage from '../../common/embedded/EmbeddedMessage';
|
||||||
import EmbeddedStory from '../../common/embedded/EmbeddedStory';
|
import EmbeddedStory from '../../common/embedded/EmbeddedStory';
|
||||||
import FakeIcon from '../../common/FakeIcon';
|
import FakeIcon from '../../common/FakeIcon';
|
||||||
import Icon from '../../common/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
|
import StarIcon from '../../common/icons/StarIcon';
|
||||||
import MessageText from '../../common/MessageText';
|
import MessageText from '../../common/MessageText';
|
||||||
import PremiumIcon from '../../common/PremiumIcon';
|
|
||||||
import ReactionStaticEmoji from '../../common/ReactionStaticEmoji';
|
import ReactionStaticEmoji from '../../common/ReactionStaticEmoji';
|
||||||
import TopicChip from '../../common/TopicChip';
|
import TopicChip from '../../common/TopicChip';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
@ -157,6 +157,7 @@ import AnimatedEmoji from './AnimatedEmoji';
|
|||||||
import CommentButton from './CommentButton';
|
import CommentButton from './CommentButton';
|
||||||
import Contact from './Contact';
|
import Contact from './Contact';
|
||||||
import ContextMenuContainer from './ContextMenuContainer.async';
|
import ContextMenuContainer from './ContextMenuContainer.async';
|
||||||
|
import FactCheck from './FactCheck';
|
||||||
import Game from './Game';
|
import Game from './Game';
|
||||||
import Giveaway from './Giveaway';
|
import Giveaway from './Giveaway';
|
||||||
import InlineButtons from './InlineButtons';
|
import InlineButtons from './InlineButtons';
|
||||||
@ -470,7 +471,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
id: messageId, chatId, forwardInfo, viaBotId, isTranscriptionError,
|
id: messageId, chatId, forwardInfo, viaBotId, isTranscriptionError, factCheck,
|
||||||
} = message;
|
} = message;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -524,6 +525,8 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const noUserColors = isOwn && !isCustomShape;
|
const noUserColors = isOwn && !isCustomShape;
|
||||||
|
|
||||||
|
const hasFactCheck = Boolean(factCheck?.text);
|
||||||
|
|
||||||
const hasSubheader = hasTopicChip || hasMessageReply || hasStoryReply;
|
const hasSubheader = hasTopicChip || hasMessageReply || hasStoryReply;
|
||||||
|
|
||||||
const selectMessage = useLastCallback((e?: React.MouseEvent<HTMLDivElement, MouseEvent>, groupedId?: string) => {
|
const selectMessage = useLastCallback((e?: React.MouseEvent<HTMLDivElement, MouseEvent>, groupedId?: string) => {
|
||||||
@ -627,12 +630,13 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
}, [focusLastMessage, isLastInList, transcribedText, withVoiceTranscription]);
|
}, [focusLastMessage, isLastInList, transcribedText, withVoiceTranscription]);
|
||||||
|
|
||||||
const textMessage = album?.hasMultipleCaptions ? undefined : (album?.captionMessage || message);
|
const textMessage = album?.hasMultipleCaptions ? undefined : (album?.captionMessage || message);
|
||||||
const hasText = textMessage && hasMessageText(textMessage);
|
const hasTextContent = textMessage && hasMessageText(textMessage);
|
||||||
|
const hasText = hasTextContent || hasFactCheck;
|
||||||
|
|
||||||
const containerClassName = buildClassName(
|
const containerClassName = buildClassName(
|
||||||
'Message message-list-item',
|
'Message message-list-item',
|
||||||
isFirstInGroup && 'first-in-group',
|
isFirstInGroup && 'first-in-group',
|
||||||
isProtected && !hasText ? 'is-protected' : 'allow-selection',
|
isProtected && !hasTextContent ? 'is-protected' : 'allow-selection',
|
||||||
isLastInGroup && 'last-in-group',
|
isLastInGroup && 'last-in-group',
|
||||||
isFirstInDocumentGroup && 'first-in-document-group',
|
isFirstInDocumentGroup && 'first-in-document-group',
|
||||||
isLastInDocumentGroup && 'last-in-document-group',
|
isLastInDocumentGroup && 'last-in-document-group',
|
||||||
@ -915,6 +919,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
observeIntersectionForLoading={observeIntersectionForLoading}
|
observeIntersectionForLoading={observeIntersectionForLoading}
|
||||||
observeIntersectionForPlaying={observeIntersectionForPlaying}
|
observeIntersectionForPlaying={observeIntersectionForPlaying}
|
||||||
withTranslucentThumbs={isCustomShape}
|
withTranslucentThumbs={isCustomShape}
|
||||||
|
isInSelectMode={isInSelectMode}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1227,6 +1232,9 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{hasFactCheck && (
|
||||||
|
<FactCheck factCheck={factCheck} isToggleDisabled={isInSelectMode} />
|
||||||
|
)}
|
||||||
{metaPosition === 'in-text' && renderReactionsAndMeta()}
|
{metaPosition === 'in-text' && renderReactionsAndMeta()}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -1331,7 +1339,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
observeIntersectionForPlaying={observeIntersectionForPlaying}
|
observeIntersectionForPlaying={observeIntersectionForPlaying}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!asForwarded && !senderEmojiStatus && senderIsPremium && <PremiumIcon />}
|
{!asForwarded && !senderEmojiStatus && senderIsPremium && <StarIcon />}
|
||||||
{senderPeer?.fakeType && <FakeIcon fakeType={senderPeer.fakeType} />}
|
{senderPeer?.fakeType && <FakeIcon fakeType={senderPeer.fakeType} />}
|
||||||
</span>
|
</span>
|
||||||
) : !botSender ? (
|
) : !botSender ? (
|
||||||
|
|||||||
@ -31,7 +31,7 @@ import useShowTransition from '../../../hooks/useShowTransition';
|
|||||||
import useSignal from '../../../hooks/useSignal';
|
import useSignal from '../../../hooks/useSignal';
|
||||||
import useBlurredMediaThumbRef from './hooks/useBlurredMediaThumbRef';
|
import useBlurredMediaThumbRef from './hooks/useBlurredMediaThumbRef';
|
||||||
|
|
||||||
import Icon from '../../common/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
import MediaSpoiler from '../../common/MediaSpoiler';
|
import MediaSpoiler from '../../common/MediaSpoiler';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import OptimizedVideo from '../../ui/OptimizedVideo';
|
import OptimizedVideo from '../../ui/OptimizedVideo';
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import useLang from '../../../hooks/useLang';
|
|||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
import Avatar from '../../common/Avatar';
|
import Avatar from '../../common/Avatar';
|
||||||
import Icon from '../../common/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import Skeleton from '../../ui/placeholder/Skeleton';
|
import Skeleton from '../../ui/placeholder/Skeleton';
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import useLastCallback from '../../../hooks/useLastCallback';
|
|||||||
|
|
||||||
import AboutAdsModal from '../../common/AboutAdsModal.async';
|
import AboutAdsModal from '../../common/AboutAdsModal.async';
|
||||||
import Avatar from '../../common/Avatar';
|
import Avatar from '../../common/Avatar';
|
||||||
import Icon from '../../common/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
import PeerColorWrapper from '../../common/PeerColorWrapper';
|
import PeerColorWrapper from '../../common/PeerColorWrapper';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import MessageAppendix from './MessageAppendix';
|
import MessageAppendix from './MessageAppendix';
|
||||||
|
|||||||
@ -322,7 +322,7 @@
|
|||||||
color: var(--accent-color);
|
color: var(--accent-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.PremiumIcon {
|
.StarIcon {
|
||||||
--color-fill: var(--accent-color);
|
--color-fill: var(--accent-color);
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
@ -963,12 +963,6 @@
|
|||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove extra bottom padding from `blockquote`
|
|
||||||
.text-entity-blockquote-wrapper {
|
|
||||||
display: inline-block;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote, .blockquote {
|
blockquote, .blockquote {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@ -40,10 +40,11 @@ export function buildContentClassName(
|
|||||||
giveaway, giveawayResults,
|
giveaway, giveawayResults,
|
||||||
} = getMessageContent(message);
|
} = getMessageContent(message);
|
||||||
const text = album?.hasMultipleCaptions ? undefined : getMessageContent(album?.captionMessage || message).text;
|
const text = album?.hasMultipleCaptions ? undefined : getMessageContent(album?.captionMessage || message).text;
|
||||||
|
const hasFactCheck = Boolean(message.factCheck?.text);
|
||||||
|
|
||||||
const classNames = [MESSAGE_CONTENT_CLASS_NAME];
|
const classNames = [MESSAGE_CONTENT_CLASS_NAME];
|
||||||
const isMedia = storyData || photo || video || location || invoice?.extendedMedia;
|
const isMedia = storyData || photo || video || location || invoice?.extendedMedia;
|
||||||
const hasText = text || location?.type === 'venue' || isGeoLiveActive;
|
const hasText = text || location?.type === 'venue' || isGeoLiveActive || hasFactCheck;
|
||||||
const isMediaWithNoText = isMedia && !hasText;
|
const isMediaWithNoText = isMedia && !hasText;
|
||||||
const isViaBot = Boolean(message.viaBotId);
|
const isViaBot = Boolean(message.viaBotId);
|
||||||
|
|
||||||
@ -144,7 +145,7 @@ export function buildContentClassName(
|
|||||||
classNames.push('has-background');
|
classNames.push('has-background');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasSubheader || asForwarded || isViaBot || !isMediaWithNoText || forceSenderName) {
|
if (hasSubheader || asForwarded || isViaBot || !isMediaWithNoText || forceSenderName || hasFactCheck) {
|
||||||
classNames.push('has-solid-background');
|
classNames.push('has-solid-background');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import buildClassName from '../../../../util/buildClassName';
|
|||||||
import { REM } from '../../../common/helpers/mediaDimensions';
|
import { REM } from '../../../common/helpers/mediaDimensions';
|
||||||
|
|
||||||
import CustomEmoji from '../../../common/CustomEmoji';
|
import CustomEmoji from '../../../common/CustomEmoji';
|
||||||
import Icon from '../../../common/Icon';
|
import Icon from '../../../common/icons/Icon';
|
||||||
|
|
||||||
import styles from './ReactionSelectorReaction.module.scss';
|
import styles from './ReactionSelectorReaction.module.scss';
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import useFlag from '../../../../hooks/useFlag';
|
|||||||
import useMedia from '../../../../hooks/useMedia';
|
import useMedia from '../../../../hooks/useMedia';
|
||||||
|
|
||||||
import AnimatedSticker from '../../../common/AnimatedSticker';
|
import AnimatedSticker from '../../../common/AnimatedSticker';
|
||||||
import Icon from '../../../common/Icon';
|
import Icon from '../../../common/icons/Icon';
|
||||||
|
|
||||||
import styles from './ReactionSelectorReaction.module.scss';
|
import styles from './ReactionSelectorReaction.module.scss';
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,8 @@ import InviteViaLinkModal from './inviteViaLink/InviteViaLinkModal.async';
|
|||||||
import MapModal from './map/MapModal.async';
|
import MapModal from './map/MapModal.async';
|
||||||
import OneTimeMediaModal from './oneTimeMedia/OneTimeMediaModal.async';
|
import OneTimeMediaModal from './oneTimeMedia/OneTimeMediaModal.async';
|
||||||
import ReportAdModal from './reportAd/ReportAdModal.async';
|
import ReportAdModal from './reportAd/ReportAdModal.async';
|
||||||
|
import StarsBalanceModal from './stars/StarsBalanceModal.async';
|
||||||
|
import StarsPaymentModal from './stars/StarsPaymentModal.async';
|
||||||
import UrlAuthModal from './urlAuth/UrlAuthModal.async';
|
import UrlAuthModal from './urlAuth/UrlAuthModal.async';
|
||||||
import WebAppModal from './webApp/WebAppModal.async';
|
import WebAppModal from './webApp/WebAppModal.async';
|
||||||
|
|
||||||
@ -30,6 +32,8 @@ type ModalKey = keyof Pick<TabState,
|
|||||||
'requestedAttachBotInstall' |
|
'requestedAttachBotInstall' |
|
||||||
'collectibleInfoModal' |
|
'collectibleInfoModal' |
|
||||||
'reportAdModal' |
|
'reportAdModal' |
|
||||||
|
'starsBalanceModal' |
|
||||||
|
'isStarPaymentModalOpen' |
|
||||||
'webApp'
|
'webApp'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
@ -57,6 +61,8 @@ const MODALS: ModalRegistry = {
|
|||||||
webApp: WebAppModal,
|
webApp: WebAppModal,
|
||||||
collectibleInfoModal: CollectibleInfoModal,
|
collectibleInfoModal: CollectibleInfoModal,
|
||||||
mapModal: MapModal,
|
mapModal: MapModal,
|
||||||
|
isStarPaymentModalOpen: StarsPaymentModal,
|
||||||
|
starsBalanceModal: StarsBalanceModal,
|
||||||
};
|
};
|
||||||
const MODAL_KEYS = Object.keys(MODALS) as ModalKey[];
|
const MODAL_KEYS = Object.keys(MODALS) as ModalKey[];
|
||||||
const MODAL_ENTRIES = Object.entries(MODALS) as Entries<ModalRegistry>;
|
const MODAL_ENTRIES = Object.entries(MODALS) as Entries<ModalRegistry>;
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import useLang from '../../../hooks/useLang';
|
|||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
import Avatar from '../../common/Avatar';
|
import Avatar from '../../common/Avatar';
|
||||||
import Icon from '../../common/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
import PremiumProgress from '../../common/PremiumProgress';
|
import PremiumProgress from '../../common/PremiumProgress';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import useLang from '../../../hooks/useLang';
|
|||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
||||||
import Icon from '../../common/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
import PickerSelectedItem from '../../common/PickerSelectedItem';
|
import PickerSelectedItem from '../../common/PickerSelectedItem';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import Modal from '../../ui/Modal';
|
import Modal from '../../ui/Modal';
|
||||||
|
|||||||
37
src/components/modals/common/TableInfoModal.module.scss
Normal file
37
src/components/modals/common/TableInfoModal.module.scss
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
max-height: min(92vh, 40rem) !important;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
background-color: var(--color-background-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table .cell {
|
||||||
|
border: 1px solid var(--color-borders);
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 6.25rem;
|
||||||
|
height: 6.25rem;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chatItem {
|
||||||
|
margin: 0;
|
||||||
|
width: fit-content;
|
||||||
|
background-color: var(--color-background);
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
100
src/components/modals/common/TableInfoModal.tsx
Normal file
100
src/components/modals/common/TableInfoModal.tsx
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
import React, { memo, type TeactNode } from '../../../lib/teact/teact';
|
||||||
|
import { getActions } from '../../../global';
|
||||||
|
|
||||||
|
import type { ApiPeer, ApiWebDocument } from '../../../api/types';
|
||||||
|
import type { CustomPeer } from '../../../types';
|
||||||
|
|
||||||
|
import buildClassName from '../../../util/buildClassName';
|
||||||
|
|
||||||
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
|
import Avatar from '../../common/Avatar';
|
||||||
|
import PickerSelectedItem from '../../common/PickerSelectedItem';
|
||||||
|
import Button from '../../ui/Button';
|
||||||
|
import Modal from '../../ui/Modal';
|
||||||
|
|
||||||
|
import styles from './TableInfoModal.module.scss';
|
||||||
|
|
||||||
|
type ChatItem = { chatId: string };
|
||||||
|
|
||||||
|
export type TableData = [TeactNode, TeactNode | ChatItem][];
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
isOpen?: boolean;
|
||||||
|
title?: string;
|
||||||
|
tableData?: TableData;
|
||||||
|
headerImageUrl?: string;
|
||||||
|
headerAvatarPeer?: ApiPeer | CustomPeer;
|
||||||
|
headerAvatarWebPhoto?: ApiWebDocument;
|
||||||
|
header?: TeactNode;
|
||||||
|
footer?: TeactNode;
|
||||||
|
buttonText?: string;
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
onButtonClick?: NoneToVoidFunction;
|
||||||
|
};
|
||||||
|
|
||||||
|
const TableInfoModal = ({
|
||||||
|
isOpen,
|
||||||
|
title,
|
||||||
|
tableData,
|
||||||
|
headerImageUrl,
|
||||||
|
headerAvatarPeer,
|
||||||
|
headerAvatarWebPhoto,
|
||||||
|
header,
|
||||||
|
footer,
|
||||||
|
buttonText,
|
||||||
|
onClose,
|
||||||
|
onButtonClick,
|
||||||
|
}: OwnProps) => {
|
||||||
|
const { openChat } = getActions();
|
||||||
|
const handleOpenChat = useLastCallback((peerId: string) => {
|
||||||
|
openChat({ id: peerId });
|
||||||
|
onClose();
|
||||||
|
});
|
||||||
|
|
||||||
|
const withAvatar = Boolean(headerAvatarPeer || headerAvatarWebPhoto);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
hasCloseButton={Boolean(title)}
|
||||||
|
hasAbsoluteCloseButton={!title}
|
||||||
|
isSlim
|
||||||
|
title={title}
|
||||||
|
contentClassName={styles.content}
|
||||||
|
onClose={onClose}
|
||||||
|
>
|
||||||
|
{withAvatar ? (
|
||||||
|
<Avatar peer={headerAvatarPeer} webPhoto={headerAvatarWebPhoto} size="jumbo" className={styles.avatar} />
|
||||||
|
) : (
|
||||||
|
<img className={styles.logo} src={headerImageUrl} alt="" draggable={false} />
|
||||||
|
)}
|
||||||
|
{header}
|
||||||
|
<table className={styles.table}>
|
||||||
|
{tableData?.map(([label, value]) => (
|
||||||
|
<tr className={styles.row}>
|
||||||
|
<td className={buildClassName(styles.cell, styles.title)}>{label}</td>
|
||||||
|
<td className={buildClassName(styles.cell, styles.value)}>
|
||||||
|
{typeof value === 'object' && 'chatId' in value ? (
|
||||||
|
<PickerSelectedItem
|
||||||
|
peerId={value.chatId}
|
||||||
|
className={styles.chatItem}
|
||||||
|
forceShowSelf
|
||||||
|
fluid
|
||||||
|
clickArg={value.chatId}
|
||||||
|
onClick={handleOpenChat}
|
||||||
|
/>
|
||||||
|
) : value}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</table>
|
||||||
|
{footer}
|
||||||
|
{buttonText && (
|
||||||
|
<Button onClick={onButtonClick || onClose}>{buttonText}</Button>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(TableInfoModal);
|
||||||
@ -1,38 +1,8 @@
|
|||||||
.content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.5rem;
|
|
||||||
max-height: min(92vh, 40rem) !important;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clickable {
|
.clickable {
|
||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
|
||||||
background-color: var(--color-background-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.table td {
|
|
||||||
border: 1px solid var(--color-borders);
|
|
||||||
padding: 0.25rem 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-item {
|
|
||||||
margin: 0;
|
|
||||||
width: fit-content;
|
|
||||||
background-color: var(--color-background);
|
|
||||||
color: var(--color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
width: 6.25rem;
|
|
||||||
height: 6.25rem;
|
|
||||||
align-self: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.centered {
|
.centered {
|
||||||
text-align: center !important;
|
text-align: center !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { memo } from '../../../lib/teact/teact';
|
import React, { memo, useMemo } from '../../../lib/teact/teact';
|
||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import type { ApiPeer } from '../../../api/types';
|
import type { ApiPeer } from '../../../api/types';
|
||||||
@ -14,9 +14,7 @@ import useLang from '../../../hooks/useLang';
|
|||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
import LinkField from '../../common/LinkField';
|
import LinkField from '../../common/LinkField';
|
||||||
import PickerSelectedItem from '../../common/PickerSelectedItem';
|
import TableInfoModal, { type TableData } from '../common/TableInfoModal';
|
||||||
import Button from '../../ui/Button';
|
|
||||||
import Modal from '../../ui/Modal';
|
|
||||||
|
|
||||||
import styles from './GiftCodeModal.module.scss';
|
import styles from './GiftCodeModal.module.scss';
|
||||||
|
|
||||||
@ -39,18 +37,13 @@ const GiftCodeModal = ({
|
|||||||
messageSender,
|
messageSender,
|
||||||
}: OwnProps & StateProps) => {
|
}: OwnProps & StateProps) => {
|
||||||
const {
|
const {
|
||||||
closeGiftCodeModal, openChat, applyGiftCode, focusMessage,
|
closeGiftCodeModal, applyGiftCode, focusMessage,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
const isOpen = Boolean(modal);
|
const isOpen = Boolean(modal);
|
||||||
|
|
||||||
const canUse = (!modal?.info.toId || modal?.info.toId === currentUserId) && !modal?.info.usedAt;
|
const canUse = (!modal?.info.toId || modal?.info.toId === currentUserId) && !modal?.info.usedAt;
|
||||||
|
|
||||||
const handleOpenChat = useLastCallback((peerId: string) => {
|
|
||||||
openChat({ id: peerId });
|
|
||||||
closeGiftCodeModal();
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleOpenGiveaway = useLastCallback(() => {
|
const handleOpenGiveaway = useLastCallback(() => {
|
||||||
if (!modal || !modal.info.giveawayMessageId) return;
|
if (!modal || !modal.info.giveawayMessageId) return;
|
||||||
focusMessage({
|
focusMessage({
|
||||||
@ -68,101 +61,63 @@ const GiftCodeModal = ({
|
|||||||
closeGiftCodeModal();
|
closeGiftCodeModal();
|
||||||
});
|
});
|
||||||
|
|
||||||
function renderContent() {
|
const modalData = useMemo(() => {
|
||||||
if (!modal) return undefined;
|
if (!modal) return undefined;
|
||||||
const { slug, info } = modal;
|
const { slug, info } = modal;
|
||||||
|
|
||||||
const fromId = info.fromId || messageSender?.id;
|
const fromId = info.fromId || messageSender?.id;
|
||||||
|
|
||||||
return (
|
const header = (
|
||||||
<>
|
<>
|
||||||
<img className={styles.logo} src={PremiumLogo} alt="" draggable={false} />
|
|
||||||
<p className={styles.centered}>{renderText(lang('lng_gift_link_about'), ['simple_markdown'])}</p>
|
<p className={styles.centered}>{renderText(lang('lng_gift_link_about'), ['simple_markdown'])}</p>
|
||||||
<LinkField title="BoostingGiftLink" link={`${TME_LINK_PREFIX}/${GIFTCODE_PATH}/${slug}`} />
|
<LinkField title="BoostingGiftLink" link={`${TME_LINK_PREFIX}/${GIFTCODE_PATH}/${slug}`} />
|
||||||
<table className={styles.table}>
|
|
||||||
<tr>
|
|
||||||
<td className={styles.title}>{lang('BoostingFrom')}</td>
|
|
||||||
<td>
|
|
||||||
{fromId ? (
|
|
||||||
<PickerSelectedItem
|
|
||||||
peerId={fromId}
|
|
||||||
className={styles.chatItem}
|
|
||||||
forceShowSelf
|
|
||||||
fluid
|
|
||||||
clickArg={fromId}
|
|
||||||
onClick={handleOpenChat}
|
|
||||||
/>
|
|
||||||
) : lang('BoostingNoRecipient')}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td className={styles.title}>
|
|
||||||
{lang('BoostingTo')}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{info.toId ? (
|
|
||||||
<PickerSelectedItem
|
|
||||||
peerId={info.toId}
|
|
||||||
className={styles.chatItem}
|
|
||||||
forceShowSelf
|
|
||||||
fluid
|
|
||||||
clickArg={info.toId}
|
|
||||||
onClick={handleOpenChat}
|
|
||||||
/>
|
|
||||||
) : lang('BoostingNoRecipient')}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td className={styles.title}>
|
|
||||||
{lang('BoostingGift')}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{lang('BoostingTelegramPremiumFor', lang('Months', info.months, 'i'))}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td className={styles.title}>
|
|
||||||
{lang('BoostingReason')}
|
|
||||||
</td>
|
|
||||||
<td className={buildClassName(info.giveawayMessageId && styles.clickable)} onClick={handleOpenGiveaway}>
|
|
||||||
{info.isFromGiveaway && !info.toId ? lang('BoostingIncompleteGiveaway')
|
|
||||||
: lang(info.isFromGiveaway ? 'BoostingGiveaway' : 'BoostingYouWereSelected')}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td className={styles.title}>
|
|
||||||
{lang('BoostingDate')}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{formatDateTimeToString(info.date * 1000, lang.code, true)}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<span className={styles.centered}>
|
|
||||||
{renderText(
|
|
||||||
info.usedAt ? lang('BoostingUsedLinkDate', formatDateTimeToString(info.usedAt * 1000, lang.code, true))
|
|
||||||
: lang('BoostingSendLinkToAnyone'),
|
|
||||||
['simple_markdown'],
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
<Button onClick={handleButtonClick}>
|
|
||||||
{canUse ? lang('BoostingUseLink') : lang('Close')}
|
|
||||||
</Button>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
const tableData = [
|
||||||
|
[lang('BoostingFrom'), fromId ? { chatId: fromId } : lang('BoostingNoRecipient')],
|
||||||
|
[lang('BoostingTo'), info.toId ? { chatId: info.toId } : lang('BoostingNoRecipient')],
|
||||||
|
[lang('BoostingGift'), lang('BoostingTelegramPremiumFor', lang('Months', info.months, 'i'))],
|
||||||
|
[lang('BoostingReason'), (
|
||||||
|
<span className={buildClassName(info.giveawayMessageId && styles.clickable)} onClick={handleOpenGiveaway}>
|
||||||
|
{info.isFromGiveaway && !info.toId ? lang('BoostingIncompleteGiveaway')
|
||||||
|
: lang(info.isFromGiveaway ? 'BoostingGiveaway' : 'BoostingYouWereSelected')}
|
||||||
|
</span>
|
||||||
|
)],
|
||||||
|
[lang('BoostingDate'), formatDateTimeToString(info.date * 1000, lang.code, true)],
|
||||||
|
] satisfies TableData;
|
||||||
|
|
||||||
|
const footer = (
|
||||||
|
<span className={styles.centered}>
|
||||||
|
{renderText(
|
||||||
|
info.usedAt ? lang('BoostingUsedLinkDate', formatDateTimeToString(info.usedAt * 1000, lang.code, true))
|
||||||
|
: lang('BoostingSendLinkToAnyone'),
|
||||||
|
['simple_markdown'],
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
header,
|
||||||
|
tableData,
|
||||||
|
footer,
|
||||||
|
};
|
||||||
|
}, [lang, messageSender?.id, modal]);
|
||||||
|
|
||||||
|
if (!modalData) return undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<TableInfoModal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
hasCloseButton
|
|
||||||
isSlim
|
|
||||||
title={lang('lng_gift_link_title')}
|
title={lang('lng_gift_link_title')}
|
||||||
contentClassName={styles.content}
|
headerImageUrl={PremiumLogo}
|
||||||
|
tableData={modalData.tableData}
|
||||||
|
header={modalData.header}
|
||||||
|
footer={modalData.footer}
|
||||||
|
buttonText={canUse ? lang('BoostingUseLink') : lang('Close')}
|
||||||
|
onButtonClick={handleButtonClick}
|
||||||
onClose={closeGiftCodeModal}
|
onClose={closeGiftCodeModal}
|
||||||
>
|
/>
|
||||||
{renderContent()}
|
|
||||||
</Modal>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import buildClassName from '../../../util/buildClassName';
|
|||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
import Icon from '../../common/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
import SafeLink from '../../common/SafeLink';
|
import SafeLink from '../../common/SafeLink';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
|
|||||||
31
src/components/modals/stars/BalanceBlock.tsx
Normal file
31
src/components/modals/stars/BalanceBlock.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import React, { memo } from '../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import buildClassName from '../../../util/buildClassName';
|
||||||
|
import { formatInteger } from '../../../util/textFormat';
|
||||||
|
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
|
|
||||||
|
import StarIcon from '../../common/icons/StarIcon';
|
||||||
|
|
||||||
|
import styles from './StarsBalanceModal.module.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
balance: number;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const BalanceBlock = ({ balance, className }: OwnProps) => {
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={buildClassName(styles.balance, className)}>
|
||||||
|
<span className={styles.smallerText}>{lang('StarsBalance')}</span>
|
||||||
|
<div className={styles.balanceBottom}>
|
||||||
|
<StarIcon type="gold" size="middle" />
|
||||||
|
{formatInteger(balance)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(BalanceBlock);
|
||||||
18
src/components/modals/stars/StarsBalanceModal.async.tsx
Normal file
18
src/components/modals/stars/StarsBalanceModal.async.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import type { FC } from '../../../lib/teact/teact';
|
||||||
|
import React from '../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import type { OwnProps } from './StarsBalanceModal';
|
||||||
|
|
||||||
|
import { Bundles } from '../../../util/moduleLoader';
|
||||||
|
|
||||||
|
import useModuleLoader from '../../../hooks/useModuleLoader';
|
||||||
|
|
||||||
|
const StarsBalanceModalAsync: FC<OwnProps> = (props) => {
|
||||||
|
const { modal } = props;
|
||||||
|
const StarsBalanceModal = useModuleLoader(Bundles.Extra, 'StarsBalanceModal', !modal);
|
||||||
|
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
return StarsBalanceModal ? <StarsBalanceModal {...props} /> : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StarsBalanceModalAsync;
|
||||||
239
src/components/modals/stars/StarsBalanceModal.module.scss
Normal file
239
src/components/modals/stars/StarsBalanceModal.module.scss
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
@use '../../../styles/mixins';
|
||||||
|
|
||||||
|
.root :global(.modal-content) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root :global(.modal-dialog) {
|
||||||
|
height: min(calc(55vh + 41px + 193px), 90vh);
|
||||||
|
max-width: 26.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root :global(.modal-dialog),
|
||||||
|
.root :global(.modal-content),
|
||||||
|
.transition {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
padding: 0.5rem;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
@include mixins.adapt-padding-to-scrollbar(0.5rem);
|
||||||
|
|
||||||
|
@include mixins.side-panel-section;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondaryInfo {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
background-color: var(--color-background-secondary);
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
margin: 1rem;
|
||||||
|
width: 6.25rem;
|
||||||
|
height: 6.25rem;
|
||||||
|
min-height: 6.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logoBackground {
|
||||||
|
position: absolute;
|
||||||
|
top: 0.75rem;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
height: 8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerHext {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: center;
|
||||||
|
margin-inline: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
text-align: center;
|
||||||
|
margin-inline: 0.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 0.0625rem solid var(--color-borders);
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 3.5rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
background: var(--color-background);
|
||||||
|
transition: 0.25s ease-out transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
.starHeaderText {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 500;
|
||||||
|
margin: 0 0 0 3rem;
|
||||||
|
unicode-bidi: plaintext;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hiddenHeader {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.closeButton {
|
||||||
|
position: absolute;
|
||||||
|
top: 0.5rem;
|
||||||
|
left: 0.5rem;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.balance {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smallerText {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.balanceBottom {
|
||||||
|
font-weight: 500;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modalBalance {
|
||||||
|
position: absolute;
|
||||||
|
top: 0.75rem;
|
||||||
|
right: 1.25rem;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 0.5rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option {
|
||||||
|
--_background-color: var(--color-background-secondary);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.125rem;
|
||||||
|
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 0.625rem;
|
||||||
|
|
||||||
|
background-color: var(--_background-color);
|
||||||
|
transition: background-color 0.25s ease-out;
|
||||||
|
|
||||||
|
cursor: var(--custom-cursor, pointer);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
--_background-color: var(--color-background-secondary-accent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.optionTop {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stackedStars {
|
||||||
|
display: grid;
|
||||||
|
grid-auto-columns: 0.4375rem;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
justify-items: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stackedStar {
|
||||||
|
@include mixins.filter-outline(0.0625rem, var(--_background-color));
|
||||||
|
}
|
||||||
|
|
||||||
|
.optionBottom {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.moreOptions {
|
||||||
|
grid-column: 1/-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconDown {
|
||||||
|
margin-inline-start: 0.25rem;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paymentContent {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paymentImages {
|
||||||
|
display: grid;
|
||||||
|
grid-auto-columns: 3.5rem;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
place-items: center;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paymentPhoto {
|
||||||
|
outline: 0.25rem solid var(--color-background);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paymentImageBackground {
|
||||||
|
height: 7rem;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.paymentAmount {
|
||||||
|
display: flex;
|
||||||
|
line-height: 1.125;
|
||||||
|
gap: 0.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paymentButton {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paymentButtonStar {
|
||||||
|
--color-fill: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transactions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user