Support subscription invites (#5024)
This commit is contained in:
parent
24a129c3e1
commit
28cecbfe3c
@ -8,6 +8,7 @@ import type {
|
|||||||
ApiChatBannedRights,
|
ApiChatBannedRights,
|
||||||
ApiChatFolder,
|
ApiChatFolder,
|
||||||
ApiChatInviteImporter,
|
ApiChatInviteImporter,
|
||||||
|
ApiChatInviteInfo,
|
||||||
ApiChatlistExportedInvite,
|
ApiChatlistExportedInvite,
|
||||||
ApiChatlistInvite,
|
ApiChatlistInvite,
|
||||||
ApiChatMember,
|
ApiChatMember,
|
||||||
@ -18,13 +19,14 @@ import type {
|
|||||||
ApiRestrictionReason,
|
ApiRestrictionReason,
|
||||||
ApiSendAsPeerId,
|
ApiSendAsPeerId,
|
||||||
ApiSponsoredMessageReportResult,
|
ApiSponsoredMessageReportResult,
|
||||||
|
ApiStarsSubscriptionPricing,
|
||||||
ApiTopic,
|
ApiTopic,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
import { omitUndefined, pick, pickTruthy } from '../../../util/iteratees';
|
import { omitUndefined, pick, pickTruthy } from '../../../util/iteratees';
|
||||||
import { getServerTime, getServerTimeOffset } from '../../../util/serverTime';
|
import { getServerTime, getServerTimeOffset } from '../../../util/serverTime';
|
||||||
import { serializeBytes } from '../helpers';
|
import { addPhotoToLocalDb, addUserToLocalDb, serializeBytes } from '../helpers';
|
||||||
import { buildApiUsernames, buildAvatarPhotoId } from './common';
|
import { buildApiPhoto, buildApiUsernames, buildAvatarPhotoId } from './common';
|
||||||
import { omitVirtualClassFields } from './helpers';
|
import { omitVirtualClassFields } from './helpers';
|
||||||
import {
|
import {
|
||||||
buildApiEmojiStatus,
|
buildApiEmojiStatus,
|
||||||
@ -67,6 +69,7 @@ function buildApiChatFieldsFromPeerEntity(
|
|||||||
? buildApiEmojiStatus(peerEntity.emojiStatus) : undefined;
|
? buildApiEmojiStatus(peerEntity.emojiStatus) : undefined;
|
||||||
const boostLevel = ('level' in peerEntity) ? peerEntity.level : undefined;
|
const boostLevel = ('level' in peerEntity) ? peerEntity.level : undefined;
|
||||||
const areProfilesShown = Boolean('signatureProfiles' in peerEntity && peerEntity.signatureProfiles);
|
const areProfilesShown = Boolean('signatureProfiles' in peerEntity && peerEntity.signatureProfiles);
|
||||||
|
const subscriptionUntil = 'subscriptionUntilDate' in peerEntity ? peerEntity.subscriptionUntilDate : undefined;
|
||||||
|
|
||||||
return omitUndefined<PeerEntityApiChatFields>({
|
return omitUndefined<PeerEntityApiChatFields>({
|
||||||
isMin,
|
isMin,
|
||||||
@ -100,6 +103,7 @@ function buildApiChatFieldsFromPeerEntity(
|
|||||||
hasStories: Boolean(maxStoryId) && !storiesUnavailable,
|
hasStories: Boolean(maxStoryId) && !storiesUnavailable,
|
||||||
emojiStatus,
|
emojiStatus,
|
||||||
boostLevel,
|
boostLevel,
|
||||||
|
subscriptionUntil,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -670,3 +674,47 @@ export function buildApiSponsoredMessageReportResult(
|
|||||||
options,
|
options,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildApiChatInviteInfo(invite: GramJs.ChatInvite): ApiChatInviteInfo {
|
||||||
|
const {
|
||||||
|
color, participants, participantsCount, photo, title, about, scam, fake, verified, megagroup, channel, broadcast,
|
||||||
|
requestNeeded, subscriptionFormId, subscriptionPricing, canRefulfillSubscription,
|
||||||
|
} = invite;
|
||||||
|
|
||||||
|
let apiPhoto;
|
||||||
|
if (photo instanceof GramJs.Photo) {
|
||||||
|
addPhotoToLocalDb(photo);
|
||||||
|
apiPhoto = buildApiPhoto(photo);
|
||||||
|
}
|
||||||
|
|
||||||
|
participants?.forEach(addUserToLocalDb);
|
||||||
|
|
||||||
|
return {
|
||||||
|
title,
|
||||||
|
about,
|
||||||
|
isFake: fake,
|
||||||
|
isScam: scam,
|
||||||
|
isVerified: verified,
|
||||||
|
isSuperGroup: megagroup,
|
||||||
|
isPublic: invite.public,
|
||||||
|
participantsCount,
|
||||||
|
color,
|
||||||
|
isChannel: channel,
|
||||||
|
isBroadcast: broadcast,
|
||||||
|
isRequestNeeded: requestNeeded,
|
||||||
|
photo: apiPhoto,
|
||||||
|
subscriptionFormId: subscriptionFormId?.toString(),
|
||||||
|
subscriptionPricing: subscriptionPricing && buildApiStarsSubscriptionPricing(subscriptionPricing),
|
||||||
|
canRefulfillSubscription,
|
||||||
|
participantIds: participants?.map((participant) => buildApiPeerId(participant.id, 'user')).filter(Boolean),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildApiStarsSubscriptionPricing(
|
||||||
|
pricing: GramJs.StarsSubscriptionPricing,
|
||||||
|
): ApiStarsSubscriptionPricing {
|
||||||
|
return {
|
||||||
|
period: pricing.period,
|
||||||
|
amount: pricing.amount.toJSNumber(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import type {
|
|||||||
ApiReceipt,
|
ApiReceipt,
|
||||||
ApiStarGiveawayOption,
|
ApiStarGiveawayOption,
|
||||||
ApiStarsGiveawayWinnerOption,
|
ApiStarsGiveawayWinnerOption,
|
||||||
|
ApiStarsSubscription,
|
||||||
ApiStarsTransaction,
|
ApiStarsTransaction,
|
||||||
ApiStarsTransactionPeer,
|
ApiStarsTransactionPeer,
|
||||||
ApiStarTopupOption,
|
ApiStarTopupOption,
|
||||||
@ -27,6 +28,7 @@ import type {
|
|||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
import { addWebDocumentToLocalDb } from '../helpers';
|
import { addWebDocumentToLocalDb } from '../helpers';
|
||||||
|
import { buildApiStarsSubscriptionPricing } from './chats';
|
||||||
import { buildApiMessageEntity } from './common';
|
import { buildApiMessageEntity } from './common';
|
||||||
import { omitVirtualClassFields } from './helpers';
|
import { omitVirtualClassFields } from './helpers';
|
||||||
import { buildApiDocument, buildApiWebDocument, buildMessageMediaContent } from './messageContent';
|
import { buildApiDocument, buildApiWebDocument, buildMessageMediaContent } from './messageContent';
|
||||||
@ -504,6 +506,7 @@ export function buildApiStarsTransactionPeer(peer: GramJs.TypeStarsTransactionPe
|
|||||||
export function buildApiStarsTransaction(transaction: GramJs.StarsTransaction): ApiStarsTransaction {
|
export function buildApiStarsTransaction(transaction: GramJs.StarsTransaction): ApiStarsTransaction {
|
||||||
const {
|
const {
|
||||||
date, id, peer, stars, description, photo, title, refund, extendedMedia, failed, msgId, pending, gift, reaction,
|
date, id, peer, stars, description, photo, title, refund, extendedMedia, failed, msgId, pending, gift, reaction,
|
||||||
|
subscriptionPeriod,
|
||||||
} = transaction;
|
} = transaction;
|
||||||
|
|
||||||
if (photo) {
|
if (photo) {
|
||||||
@ -527,10 +530,28 @@ export function buildApiStarsTransaction(transaction: GramJs.StarsTransaction):
|
|||||||
messageId: msgId,
|
messageId: msgId,
|
||||||
isGift: gift,
|
isGift: gift,
|
||||||
extendedMedia: boughtExtendedMedia,
|
extendedMedia: boughtExtendedMedia,
|
||||||
|
subscriptionPeriod,
|
||||||
isReaction: reaction,
|
isReaction: reaction,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildApiStarsSubscription(subscription: GramJs.StarsSubscription): ApiStarsSubscription {
|
||||||
|
const {
|
||||||
|
id, peer, pricing, untilDate, canRefulfill, canceled, chatInviteHash, missingBalance,
|
||||||
|
} = subscription;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
peerId: getApiChatIdFromMtpPeer(peer),
|
||||||
|
until: untilDate,
|
||||||
|
pricing: buildApiStarsSubscriptionPricing(pricing),
|
||||||
|
isCancelled: canceled,
|
||||||
|
canRefulfill,
|
||||||
|
hasMissingBalance: missingBalance,
|
||||||
|
chatInviteHash,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function buildApiStarTopupOption(option: GramJs.TypeStarsTopupOption): ApiStarTopupOption {
|
export function buildApiStarTopupOption(option: GramJs.TypeStarsTopupOption): ApiStarTopupOption {
|
||||||
const {
|
const {
|
||||||
amount, currency, stars, extended,
|
amount, currency, stars, extended,
|
||||||
|
|||||||
@ -647,6 +647,12 @@ export function buildInputInvoice(invoice: ApiRequestInputInvoice) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'chatInviteSubscription': {
|
||||||
|
return new GramJs.InputInvoiceChatInviteSubscription({
|
||||||
|
hash: invoice.hash,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
case 'giveaway':
|
case 'giveaway':
|
||||||
default: {
|
default: {
|
||||||
const purpose = buildInputStorePaymentPurpose(invoice.purpose);
|
const purpose = buildInputStorePaymentPurpose(invoice.purpose);
|
||||||
|
|||||||
@ -38,6 +38,7 @@ import {
|
|||||||
buildApiChatFromDialog,
|
buildApiChatFromDialog,
|
||||||
buildApiChatFromPreview,
|
buildApiChatFromPreview,
|
||||||
buildApiChatFromSavedDialog,
|
buildApiChatFromSavedDialog,
|
||||||
|
buildApiChatInviteInfo,
|
||||||
buildApiChatlistExportedInvite,
|
buildApiChatlistExportedInvite,
|
||||||
buildApiChatlistInvite,
|
buildApiChatlistInvite,
|
||||||
buildApiChatReactions,
|
buildApiChatReactions,
|
||||||
@ -1404,53 +1405,27 @@ export async function migrateChat(chat: ApiChat) {
|
|||||||
return buildApiChatFromPreview(newChannel);
|
return buildApiChatFromPreview(newChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function openChatByInvite(hash: string) {
|
export async function checkChatInvite(hash: string) {
|
||||||
const result = await invokeRequest(new GramJs.messages.CheckChatInvite({ hash }));
|
const result = await invokeRequest(new GramJs.messages.CheckChatInvite({ hash }));
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
let chat: ApiChat | undefined;
|
|
||||||
|
|
||||||
if (result instanceof GramJs.ChatInvite) {
|
if (result instanceof GramJs.ChatInvite) {
|
||||||
const {
|
return {
|
||||||
photo, participantsCount, title, channel, requestNeeded, about, megagroup,
|
chat: undefined,
|
||||||
} = result;
|
invite: buildApiChatInviteInfo(result),
|
||||||
|
users: result.participants?.map(buildApiUser).filter(Boolean),
|
||||||
if (photo instanceof GramJs.Photo) {
|
};
|
||||||
addPhotoToLocalDb(result.photo);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendApiUpdate({
|
|
||||||
'@type': 'showInvite',
|
|
||||||
data: {
|
|
||||||
title,
|
|
||||||
about,
|
|
||||||
hash,
|
|
||||||
participantsCount,
|
|
||||||
isChannel: channel && !megagroup,
|
|
||||||
isRequestNeeded: requestNeeded,
|
|
||||||
...(photo instanceof GramJs.Photo && { photo: buildApiPhoto(photo) }),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
chat = buildApiChatFromPreview(result.chat);
|
|
||||||
|
|
||||||
if (chat) {
|
|
||||||
sendApiUpdate({
|
|
||||||
'@type': 'updateChat',
|
|
||||||
id: chat.id,
|
|
||||||
chat,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const chat = buildApiChatFromPreview(result.chat);
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { chatId: chat.id };
|
return { chat, invite: undefined, users: undefined };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addChatMembers(chat: ApiChat, users: ApiUser[]) {
|
export async function addChatMembers(chat: ApiChat, users: ApiUser[]) {
|
||||||
|
|||||||
@ -20,10 +20,12 @@ import {
|
|||||||
buildApiReceipt,
|
buildApiReceipt,
|
||||||
buildApiStarsGiftOptions,
|
buildApiStarsGiftOptions,
|
||||||
buildApiStarsGiveawayOptions,
|
buildApiStarsGiveawayOptions,
|
||||||
|
buildApiStarsSubscription,
|
||||||
buildApiStarsTransaction,
|
buildApiStarsTransaction,
|
||||||
buildApiStarTopupOption,
|
buildApiStarTopupOption,
|
||||||
buildShippingOptions,
|
buildShippingOptions,
|
||||||
} from '../apiBuilders/payments';
|
} from '../apiBuilders/payments';
|
||||||
|
import { buildApiPeerId } from '../apiBuilders/peers';
|
||||||
import {
|
import {
|
||||||
buildInputInvoice, buildInputPeer, buildInputStorePaymentPurpose, buildInputThemeParams, buildShippingInfo,
|
buildInputInvoice, buildInputPeer, buildInputStorePaymentPurpose, buildInputThemeParams, buildShippingInfo,
|
||||||
} from '../gramjsBuilders';
|
} from '../gramjsBuilders';
|
||||||
@ -134,7 +136,7 @@ export async function sendStarPaymentForm({
|
|||||||
invoice: buildInputInvoice(inputInvoice),
|
invoice: buildInputInvoice(inputInvoice),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (!result) return false;
|
if (!result) return undefined;
|
||||||
|
|
||||||
if (result instanceof GramJs.payments.PaymentVerificationNeeded) {
|
if (result instanceof GramJs.payments.PaymentVerificationNeeded) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@ -143,11 +145,29 @@ export async function sendStarPaymentForm({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
} else {
|
|
||||||
handleGramJsUpdate(result.updates);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Boolean(result);
|
handleGramJsUpdate(result.updates);
|
||||||
|
|
||||||
|
if (inputInvoice.type === 'chatInviteSubscription') {
|
||||||
|
const updates = 'updates' in result.updates ? result.updates.updates : undefined;
|
||||||
|
|
||||||
|
const mtpChannelId = updates?.find((update): update is GramJs.UpdateChannel => (
|
||||||
|
update instanceof GramJs.UpdateChannel
|
||||||
|
))?.channelId;
|
||||||
|
|
||||||
|
if (!mtpChannelId) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
channelId: buildApiPeerId(mtpChannelId, 'channel'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
completed: true,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPaymentForm(inputInvoice: ApiRequestInputInvoice, theme?: ApiThemeParameters) {
|
export async function getPaymentForm(inputInvoice: ApiRequestInputInvoice, theme?: ApiThemeParameters) {
|
||||||
@ -416,8 +436,10 @@ export async function fetchStarsStatus() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
nextOffset: result.nextOffset,
|
nextHistoryOffset: result.nextOffset,
|
||||||
history: result.history?.map(buildApiStarsTransaction),
|
history: result.history?.map(buildApiStarsTransaction),
|
||||||
|
nextSubscriptionOffset: result.subscriptionsNextOffset,
|
||||||
|
subscriptions: result.subscriptions?.map(buildApiStarsSubscription),
|
||||||
balance: result.balance.toJSNumber(),
|
balance: result.balance.toJSNumber(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -475,6 +497,59 @@ export async function fetchStarsTransactionById({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchStarsSubscriptions({
|
||||||
|
offset, peer,
|
||||||
|
}: {
|
||||||
|
offset?: string;
|
||||||
|
peer?: ApiPeer;
|
||||||
|
}) {
|
||||||
|
const inputPeer = peer ? buildInputPeer(peer.id, peer.accessHash) : new GramJs.InputPeerSelf();
|
||||||
|
const result = await invokeRequest(new GramJs.payments.GetStarsSubscriptions({
|
||||||
|
peer: inputPeer,
|
||||||
|
offset,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!result?.subscriptions) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
nextOffset: result.subscriptionsNextOffset,
|
||||||
|
subscriptions: result.subscriptions.map(buildApiStarsSubscription),
|
||||||
|
balance: result.balance.toJSNumber(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function changeStarsSubscription({
|
||||||
|
peer, subscriptionId, isCancelled,
|
||||||
|
}: {
|
||||||
|
peer?: ApiPeer;
|
||||||
|
subscriptionId: string;
|
||||||
|
isCancelled: boolean;
|
||||||
|
}) {
|
||||||
|
const result = await invokeRequest(new GramJs.payments.ChangeStarsSubscription({
|
||||||
|
peer: peer ? buildInputPeer(peer.id, peer.accessHash) : new GramJs.InputPeerSelf(),
|
||||||
|
subscriptionId,
|
||||||
|
canceled: isCancelled,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fulfillStarsSubscription({
|
||||||
|
peer, subscriptionId,
|
||||||
|
}: {
|
||||||
|
peer?: ApiPeer;
|
||||||
|
subscriptionId: string;
|
||||||
|
}) {
|
||||||
|
const result = await invokeRequest(new GramJs.payments.FulfillStarsSubscription({
|
||||||
|
peer: peer ? buildInputPeer(peer.id, peer.accessHash) : new GramJs.InputPeerSelf(),
|
||||||
|
subscriptionId,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchStarsTopupOptions() {
|
export async function fetchStarsTopupOptions() {
|
||||||
const result = await invokeRequest(new GramJs.payments.GetStarsTopupOptions());
|
const result = await invokeRequest(new GramJs.payments.GetStarsTopupOptions());
|
||||||
|
|
||||||
|
|||||||
@ -86,6 +86,8 @@ export interface ApiChat {
|
|||||||
hasUnreadStories?: boolean;
|
hasUnreadStories?: boolean;
|
||||||
maxStoryId?: number;
|
maxStoryId?: number;
|
||||||
|
|
||||||
|
subscriptionUntil?: number;
|
||||||
|
|
||||||
// Locally determined field
|
// Locally determined field
|
||||||
detectedLanguage?: string;
|
detectedLanguage?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +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 { ApiChatInviteInfo } from './misc';
|
||||||
import type {
|
import type {
|
||||||
ApiInputStorePaymentPurpose,
|
ApiInputStorePaymentPurpose,
|
||||||
ApiPremiumGiftCodeOption,
|
ApiPremiumGiftCodeOption,
|
||||||
@ -263,8 +264,15 @@ export type ApiInputInvoiceStarsGiveaway = {
|
|||||||
users: number;
|
users: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ApiInputInvoiceChatInviteSubscription = {
|
||||||
|
type: 'chatInviteSubscription';
|
||||||
|
hash: string;
|
||||||
|
inviteInfo: ApiChatInviteInfo;
|
||||||
|
};
|
||||||
|
|
||||||
export type ApiInputInvoice = ApiInputInvoiceMessage | ApiInputInvoiceSlug | ApiInputInvoiceGiveaway
|
export type ApiInputInvoice = ApiInputInvoiceMessage | ApiInputInvoiceSlug | ApiInputInvoiceGiveaway
|
||||||
| ApiInputInvoiceGiftCode | ApiInputInvoiceStarsGift | ApiInputInvoiceStars | ApiInputInvoiceStarsGiveaway;
|
| ApiInputInvoiceGiftCode | ApiInputInvoiceStarsGift | ApiInputInvoiceStars | ApiInputInvoiceStarsGiveaway
|
||||||
|
| ApiInputInvoiceChatInviteSubscription;
|
||||||
|
|
||||||
/* Used for Invoice request */
|
/* Used for Invoice request */
|
||||||
export type ApiRequestInputInvoiceMessage = {
|
export type ApiRequestInputInvoiceMessage = {
|
||||||
@ -294,8 +302,14 @@ export type ApiRequestInputInvoiceStarsGiveaway = {
|
|||||||
purpose: ApiInputStorePaymentPurpose;
|
purpose: ApiInputStorePaymentPurpose;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ApiRequestInputInvoiceChatInviteSubscription = {
|
||||||
|
type: 'chatInviteSubscription';
|
||||||
|
hash: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type ApiRequestInputInvoice = ApiRequestInputInvoiceMessage | ApiRequestInputInvoiceSlug
|
export type ApiRequestInputInvoice = ApiRequestInputInvoiceMessage | ApiRequestInputInvoiceSlug
|
||||||
| ApiRequestInputInvoiceGiveaway | ApiRequestInputInvoiceStars | ApiRequestInputInvoiceStarsGiveaway;
|
| ApiRequestInputInvoiceGiveaway | ApiRequestInputInvoiceStars | ApiRequestInputInvoiceStarsGiveaway
|
||||||
|
| ApiRequestInputInvoiceChatInviteSubscription;
|
||||||
|
|
||||||
export interface ApiInvoice {
|
export interface ApiInvoice {
|
||||||
mediaType: 'invoice';
|
mediaType: 'invoice';
|
||||||
|
|||||||
@ -134,16 +134,6 @@ export type ApiFieldError = {
|
|||||||
message: string;
|
message: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ApiInviteInfo = {
|
|
||||||
title: string;
|
|
||||||
about?: string;
|
|
||||||
hash: string;
|
|
||||||
isChannel?: boolean;
|
|
||||||
participantsCount?: number;
|
|
||||||
isRequestNeeded?: true;
|
|
||||||
photo?: ApiPhoto;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ApiExportedInvite = {
|
export type ApiExportedInvite = {
|
||||||
isRevoked?: boolean;
|
isRevoked?: boolean;
|
||||||
isPermanent?: boolean;
|
isPermanent?: boolean;
|
||||||
@ -159,6 +149,31 @@ export type ApiExportedInvite = {
|
|||||||
adminId: string;
|
adminId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ApiChatInviteInfo = {
|
||||||
|
title: string;
|
||||||
|
about?: string;
|
||||||
|
photo?: ApiPhoto;
|
||||||
|
isScam?: boolean;
|
||||||
|
isFake?: boolean;
|
||||||
|
isChannel?: boolean;
|
||||||
|
isVerified?: boolean;
|
||||||
|
isSuperGroup?: boolean;
|
||||||
|
isPublic?: boolean;
|
||||||
|
participantsCount?: number;
|
||||||
|
participantIds?: string[];
|
||||||
|
color: number;
|
||||||
|
isBroadcast?: boolean;
|
||||||
|
isRequestNeeded?: boolean;
|
||||||
|
subscriptionFormId?: string;
|
||||||
|
canRefulfillSubscription?: boolean;
|
||||||
|
subscriptionPricing?: ApiStarsSubscriptionPricing;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ApiStarsSubscriptionPricing = {
|
||||||
|
period: number;
|
||||||
|
amount: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type ApiChatInviteImporter = {
|
export type ApiChatInviteImporter = {
|
||||||
userId: string;
|
userId: string;
|
||||||
date: number;
|
date: number;
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import type { ApiChat } from './chats';
|
|||||||
import type {
|
import type {
|
||||||
ApiDocument, ApiMessageEntity, ApiPaymentCredentials, BoughtPaidMedia,
|
ApiDocument, ApiMessageEntity, ApiPaymentCredentials, BoughtPaidMedia,
|
||||||
} from './messages';
|
} from './messages';
|
||||||
|
import type { ApiStarsSubscriptionPricing } from './misc';
|
||||||
import type { StatisticsOverviewPercentage } from './statistics';
|
import type { StatisticsOverviewPercentage } from './statistics';
|
||||||
import type { ApiUser } from './users';
|
import type { ApiUser } from './users';
|
||||||
|
|
||||||
@ -311,6 +312,18 @@ export interface ApiStarsTransaction {
|
|||||||
description?: string;
|
description?: string;
|
||||||
photo?: ApiWebDocument;
|
photo?: ApiWebDocument;
|
||||||
extendedMedia?: BoughtPaidMedia[];
|
extendedMedia?: BoughtPaidMedia[];
|
||||||
|
subscriptionPeriod?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiStarsSubscription {
|
||||||
|
id: string;
|
||||||
|
peerId: string;
|
||||||
|
until: number;
|
||||||
|
pricing: ApiStarsSubscriptionPricing;
|
||||||
|
isCancelled?: true;
|
||||||
|
canRefulfill?: true;
|
||||||
|
hasMissingBalance?: true;
|
||||||
|
chatInviteHash?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiStarTopupOption {
|
export interface ApiStarTopupOption {
|
||||||
|
|||||||
@ -33,7 +33,7 @@ import type {
|
|||||||
BoughtPaidMedia,
|
BoughtPaidMedia,
|
||||||
} from './messages';
|
} from './messages';
|
||||||
import type {
|
import type {
|
||||||
ApiEmojiInteraction, ApiError, ApiInviteInfo, ApiNotifyException, ApiSessionData,
|
ApiEmojiInteraction, ApiError, ApiNotifyException, ApiSessionData,
|
||||||
} from './misc';
|
} from './misc';
|
||||||
import type { ApiStealthMode, ApiStory, ApiStorySkipped } from './stories';
|
import type { ApiStealthMode, ApiStory, ApiStorySkipped } from './stories';
|
||||||
import type {
|
import type {
|
||||||
@ -114,11 +114,6 @@ export type ApiUpdateChatJoin = {
|
|||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ApiUpdateShowInvite = {
|
|
||||||
'@type': 'showInvite';
|
|
||||||
data: ApiInviteInfo;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ApiUpdateChatLeave = {
|
export type ApiUpdateChatLeave = {
|
||||||
'@type': 'updateChatLeave';
|
'@type': 'updateChatLeave';
|
||||||
id: string;
|
id: string;
|
||||||
@ -788,7 +783,7 @@ export type ApiUpdate = (
|
|||||||
ApiUpdateDeleteScheduledMessages | ApiUpdateResetMessages | ApiUpdateMessageTranslations |
|
ApiUpdateDeleteScheduledMessages | ApiUpdateResetMessages | ApiUpdateMessageTranslations |
|
||||||
ApiUpdateTwoFaError | ApiUpdatePasswordError | ApiUpdateTwoFaStateWaitCode | ApiUpdateWebViewResultSent |
|
ApiUpdateTwoFaError | ApiUpdatePasswordError | ApiUpdateTwoFaStateWaitCode | ApiUpdateWebViewResultSent |
|
||||||
ApiUpdateNotifySettings | ApiUpdateNotifyExceptions | ApiUpdatePeerBlocked | ApiUpdatePrivacy |
|
ApiUpdateNotifySettings | ApiUpdateNotifyExceptions | ApiUpdatePeerBlocked | ApiUpdatePrivacy |
|
||||||
ApiUpdateServerTimeOffset | ApiUpdateShowInvite | ApiUpdateMessageReactions | ApiUpdateSavedReactionTags |
|
ApiUpdateServerTimeOffset | ApiUpdateMessageReactions | ApiUpdateSavedReactionTags |
|
||||||
ApiUpdateGroupCallParticipants | ApiUpdateGroupCallConnection | ApiUpdateGroupCall | ApiUpdateGroupCallStreams |
|
ApiUpdateGroupCallParticipants | ApiUpdateGroupCallConnection | ApiUpdateGroupCall | ApiUpdateGroupCallStreams |
|
||||||
ApiUpdateGroupCallConnectionState | ApiUpdateGroupCallLeavePresentation | ApiUpdateGroupCallChatId |
|
ApiUpdateGroupCallConnectionState | ApiUpdateGroupCallLeavePresentation | ApiUpdateGroupCallChatId |
|
||||||
ApiUpdatePendingJoinRequests | ApiUpdatePaymentVerificationNeeded | ApiUpdatePaymentStateCompleted |
|
ApiUpdatePendingJoinRequests | ApiUpdatePaymentVerificationNeeded | ApiUpdatePaymentStateCompleted |
|
||||||
|
|||||||
@ -1281,7 +1281,9 @@
|
|||||||
"AriaSearchOlderResult" = "Focus next result";
|
"AriaSearchOlderResult" = "Focus next result";
|
||||||
"AriaSearchNewerResult" = "Focus previous result";
|
"AriaSearchNewerResult" = "Focus previous result";
|
||||||
"CreditsBoxHistoryEntryGiftOutAbout" = "With Stars, {user} will be able to unlock content and services on Telegram. {link}"
|
"CreditsBoxHistoryEntryGiftOutAbout" = "With Stars, {user} will be able to unlock content and services on Telegram. {link}"
|
||||||
"CreditsBoxOutAbout" = "Review the {link} for Stars."
|
"StarsTransactionTOS" = "Review the {link} for Stars."
|
||||||
|
"StarsTransactionTOSLinkText" = "Terms of Service"
|
||||||
|
"StarsTransactionTOSLink" = "https://telegram.org/tos/stars"
|
||||||
"GiftStarsOutgoing" = "With Stars, {user} will be able to unlock content and services on Telegram."
|
"GiftStarsOutgoing" = "With Stars, {user} will be able to unlock content and services on Telegram."
|
||||||
"SendPaidReaction" = "Send ⭐️{amount}"
|
"SendPaidReaction" = "Send ⭐️{amount}"
|
||||||
"StarsReactionTerms" = "By sending Stars you agree to the {link}"
|
"StarsReactionTerms" = "By sending Stars you agree to the {link}"
|
||||||
@ -1290,3 +1292,9 @@
|
|||||||
"MiniAppsMoreTabs_one" = "{botName} & {count} Other";
|
"MiniAppsMoreTabs_one" = "{botName} & {count} Other";
|
||||||
"MiniAppsMoreTabs_other" = "{botName} & {count} Others";
|
"MiniAppsMoreTabs_other" = "{botName} & {count} Others";
|
||||||
"PrizeCredits" = "Your prize is {count} Stars."
|
"PrizeCredits" = "Your prize is {count} Stars."
|
||||||
|
"StarsSubscribeText_one" = "Do you want to subscribe to **{chat}** for **{amount} Star** per month?"
|
||||||
|
"StarsSubscribeText_other" = "Do you want to subscribe to **{chat}** for **{amount} Stars** per month?"
|
||||||
|
"StarsSubscribeInfo" = "By subscribing you agree to the {link}"
|
||||||
|
"StarsSubscribeInfoLinkText" = "Terms of Service"
|
||||||
|
"StarsSubscribeInfoLink" = "https://telegram.org/tos/stars"
|
||||||
|
"StarsPerMonth" = "⭐️{amount}/month"
|
||||||
|
|||||||
@ -18,19 +18,14 @@ export { default as AttachBotInstallModal } from '../components/modals/attachBot
|
|||||||
export { default as DeleteFolderDialog } from '../components/main/DeleteFolderDialog';
|
export { default as DeleteFolderDialog } from '../components/main/DeleteFolderDialog';
|
||||||
export { default as PremiumMainModal } from '../components/main/premium/PremiumMainModal';
|
export { default as PremiumMainModal } from '../components/main/premium/PremiumMainModal';
|
||||||
export { default as PremiumGiftModal } from '../components/main/premium/PremiumGiftModal';
|
export { default as PremiumGiftModal } from '../components/main/premium/PremiumGiftModal';
|
||||||
export { default as StarsGiftModal } from '../components/main/premium/StarsGiftModal';
|
|
||||||
export { default as GiveawayModal } from '../components/main/premium/GiveawayModal';
|
export { default as GiveawayModal } from '../components/main/premium/GiveawayModal';
|
||||||
export { default as PremiumGiftingPickerModal } from '../components/main/premium/PremiumGiftingPickerModal';
|
export { default as PremiumGiftingPickerModal } from '../components/main/premium/PremiumGiftingPickerModal';
|
||||||
export { default as StarsGiftingPickerModal } from '../components/main/premium/StarsGiftingPickerModal';
|
|
||||||
export { default as PremiumLimitReachedModal } from '../components/main/premium/common/PremiumLimitReachedModal';
|
export { default as PremiumLimitReachedModal } from '../components/main/premium/common/PremiumLimitReachedModal';
|
||||||
export { default as StatusPickerMenu } from '../components/left/main/StatusPickerMenu';
|
export { default as StatusPickerMenu } from '../components/left/main/StatusPickerMenu';
|
||||||
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 ChatInviteModal } from '../components/modals/chatInvite/ChatInviteModal';
|
||||||
export { default as StarPaymentModal } from '../components/modals/stars/StarsPaymentModal';
|
|
||||||
export { default as StarsTransactionInfoModal } from '../components/modals/stars/transaction/StarsTransactionModal';
|
|
||||||
export { default as PaidReactionModal } from '../components/modals/paidReaction/PaidReactionModal';
|
|
||||||
|
|
||||||
export { default as AboutAdsModal } from '../components/common/AboutAdsModal';
|
export { default as AboutAdsModal } from '../components/common/AboutAdsModal';
|
||||||
export { default as AboutMonetizationModal } from '../components/common/AboutMonetizationModal';
|
export { default as AboutMonetizationModal } from '../components/common/AboutMonetizationModal';
|
||||||
|
|||||||
7
src/bundles/stars.ts
Normal file
7
src/bundles/stars.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export { default as StarsGiftModal } from '../components/main/premium/StarsGiftModal';
|
||||||
|
export { default as StarsGiftingPickerModal } from '../components/main/premium/StarsGiftingPickerModal';
|
||||||
|
export { default as StarsBalanceModal } from '../components/modals/stars/StarsBalanceModal';
|
||||||
|
export { default as StarPaymentModal } from '../components/modals/stars/StarsPaymentModal';
|
||||||
|
export { default as StarsTransactionInfoModal } from '../components/modals/stars/transaction/StarsTransactionModal';
|
||||||
|
export { default as StarsSubscriptionModal } from '../components/modals/stars/subscription/StarsSubscriptionModal';
|
||||||
|
export { default as PaidReactionModal } from '../components/modals/paidReaction/PaidReactionModal';
|
||||||
@ -220,6 +220,9 @@ const Avatar: FC<OwnProps> = ({
|
|||||||
} else if (chat) {
|
} else if (chat) {
|
||||||
const title = getChatTitle(lang, chat);
|
const title = getChatTitle(lang, chat);
|
||||||
content = title && getFirstLetters(title, isUserId(chat.id) ? 2 : 1);
|
content = title && getFirstLetters(title, isUserId(chat.id) ? 2 : 1);
|
||||||
|
} else if (isCustomPeer) {
|
||||||
|
const title = peer.title || lang(peer.titleKey!);
|
||||||
|
content = title && getFirstLetters(title, 1);
|
||||||
} else if (text) {
|
} else if (text) {
|
||||||
content = getFirstLetters(text, 2);
|
content = getFirstLetters(text, 2);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.fullName {
|
.fullName {
|
||||||
font-size: 1rem;
|
font-size: 1em;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|
||||||
&.canCopy {
|
&.canCopy {
|
||||||
|
|||||||
@ -80,7 +80,7 @@ const FullNameTitle: FC<OwnProps> = ({
|
|||||||
|
|
||||||
const specialTitle = useMemo(() => {
|
const specialTitle = useMemo(() => {
|
||||||
if (customPeer) {
|
if (customPeer) {
|
||||||
return lang(customPeer.titleKey, customPeer.titleValue, 'i');
|
return customPeer.title || lang(customPeer.titleKey!);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSavedMessages) {
|
if (isSavedMessages) {
|
||||||
@ -125,8 +125,8 @@ const FullNameTitle: FC<OwnProps> = ({
|
|||||||
</h3>
|
</h3>
|
||||||
{!iconElement && peer && (
|
{!iconElement && peer && (
|
||||||
<>
|
<>
|
||||||
{!noVerified && realPeer?.isVerified && <VerifiedIcon />}
|
{!noVerified && peer?.isVerified && <VerifiedIcon />}
|
||||||
{!noFake && realPeer?.fakeType && <FakeIcon fakeType={realPeer.fakeType} />}
|
{!noFake && peer?.fakeType && <FakeIcon fakeType={peer.fakeType} />}
|
||||||
{withEmojiStatus && realPeer?.emojiStatus && (
|
{withEmojiStatus && realPeer?.emojiStatus && (
|
||||||
<CustomEmoji
|
<CustomEmoji
|
||||||
documentId={realPeer.emojiStatus.documentId}
|
documentId={realPeer.emojiStatus.documentId}
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import styles from './PeerBadge.module.scss';
|
|||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
peer: ApiPeer | CustomPeer;
|
peer: ApiPeer | CustomPeer;
|
||||||
text?: string;
|
text?: string;
|
||||||
badgeText: string;
|
badgeText?: string;
|
||||||
badgeIcon?: IconName;
|
badgeIcon?: IconName;
|
||||||
className?: string;
|
className?: string;
|
||||||
badgeClassName?: string;
|
badgeClassName?: string;
|
||||||
@ -37,10 +37,12 @@ const PeerBadge = ({
|
|||||||
>
|
>
|
||||||
<div className={styles.top}>
|
<div className={styles.top}>
|
||||||
<Avatar size="large" peer={peer} />
|
<Avatar size="large" peer={peer} />
|
||||||
<div className={buildClassName(styles.badge, badgeClassName)}>
|
{badgeText && (
|
||||||
{badgeIcon && <Icon name={badgeIcon} />}
|
<div className={buildClassName(styles.badge, badgeClassName)}>
|
||||||
{badgeText}
|
{badgeIcon && <Icon name={badgeIcon} />}
|
||||||
</div>
|
{badgeText}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{text && <p className={styles.text}>{text}</p>}
|
{text && <p className={styles.text}>{text}</p>}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -10,7 +10,7 @@ export function getPeerColorClass(peer?: ApiPeer | CustomPeer, noUserColors?: bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ('isCustomPeer' in peer) {
|
if ('isCustomPeer' in peer) {
|
||||||
if (!peer.peerColorId) return undefined;
|
if (peer.peerColorId === undefined) return undefined;
|
||||||
return `peer-color-${peer.peerColorId}`;
|
return `peer-color-${peer.peerColorId}`;
|
||||||
}
|
}
|
||||||
return noUserColors ? `peer-color-count-${getPeerColorCount(peer)}` : `peer-color-${getPeerColorKey(peer)}`;
|
return noUserColors ? `peer-color-count-${getPeerColorCount(peer)}` : `peer-color-${getPeerColorKey(peer)}`;
|
||||||
|
|||||||
@ -80,7 +80,7 @@ const PickerSelectedItem = <T,>({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const name = (customPeer && lang(customPeer.titleKey))
|
const name = (customPeer && (customPeer.title || lang(customPeer.titleKey!)))
|
||||||
|| (!chat || (user && !isSavedMessages)
|
|| (!chat || (user && !isSavedMessages)
|
||||||
? getUserFirstOrLastName(user)
|
? getUserFirstOrLastName(user)
|
||||||
: getChatTitle(lang, chat, isSavedMessages));
|
: getChatTitle(lang, chat, isSavedMessages));
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
@use "../../../styles/mixins";
|
||||||
|
|
||||||
.Chat {
|
.Chat {
|
||||||
--background-color: var(--color-background);
|
--background-color: var(--color-background);
|
||||||
--thumbs-background: var(--background-color);
|
--thumbs-background: var(--background-color);
|
||||||
@ -39,8 +41,8 @@
|
|||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&.ListItem.has-menu-open {
|
&.ListItem.has-menu-open {
|
||||||
.avatar-online {
|
.avatar-badge {
|
||||||
border-color: var(--color-chat-hover);
|
--_color-outline: var(--color-chat-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-badge-wrapper {
|
.avatar-badge-wrapper {
|
||||||
@ -70,8 +72,8 @@
|
|||||||
&.selected {
|
&.selected {
|
||||||
--background-color: var(--color-chat-hover) !important;
|
--background-color: var(--color-chat-hover) !important;
|
||||||
|
|
||||||
.avatar-online {
|
.avatar-badge {
|
||||||
border-color: var(--color-chat-hover);
|
--_color-outline: var(--color-chat-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ChatCallStatus {
|
.ChatCallStatus {
|
||||||
@ -94,8 +96,11 @@
|
|||||||
--color-checkmark: var(--color-primary);
|
--color-checkmark: var(--color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.avatar-badge {
|
||||||
|
--_color-outline: var(--color-chat-active) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.avatar-online {
|
.avatar-online {
|
||||||
border-color: var(--color-chat-active) !important;
|
|
||||||
background-color: var(--color-white);
|
background-color: var(--color-white);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,16 +246,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-online {
|
.avatar-badge {
|
||||||
|
--_color-outline: var(--color-background);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0.0625rem;
|
bottom: 0.0625rem;
|
||||||
right: 0.0625rem;
|
right: 0.0625rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-subscription {
|
||||||
|
@include mixins.filter-outline(1px, var(--_color-outline));
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-online {
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid var(--_color-outline);
|
||||||
|
background-color: #0ac630;
|
||||||
|
|
||||||
width: 0.875rem;
|
width: 0.875rem;
|
||||||
height: 0.875rem;
|
height: 0.875rem;
|
||||||
border-radius: 50%;
|
|
||||||
border: 2px solid var(--color-background);
|
|
||||||
background-color: #0ac630;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
transform: scale(0);
|
transform: scale(0);
|
||||||
|
|||||||
@ -64,6 +64,7 @@ import useChatListEntry from './hooks/useChatListEntry';
|
|||||||
import Avatar from '../../common/Avatar';
|
import Avatar from '../../common/Avatar';
|
||||||
import DeleteChatModal from '../../common/DeleteChatModal';
|
import DeleteChatModal from '../../common/DeleteChatModal';
|
||||||
import FullNameTitle from '../../common/FullNameTitle';
|
import FullNameTitle from '../../common/FullNameTitle';
|
||||||
|
import StarIcon from '../../common/icons/StarIcon';
|
||||||
import LastMessageMeta from '../../common/LastMessageMeta';
|
import LastMessageMeta from '../../common/LastMessageMeta';
|
||||||
import ReportModal from '../../common/ReportModal';
|
import ReportModal from '../../common/ReportModal';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
@ -345,12 +346,17 @@ const Chat: FC<OwnProps & StateProps> = ({
|
|||||||
isSavedDialog={isSavedDialog}
|
isSavedDialog={isSavedDialog}
|
||||||
size={isPreview ? 'medium' : 'large'}
|
size={isPreview ? 'medium' : 'large'}
|
||||||
withStory={!user?.isSelf}
|
withStory={!user?.isSelf}
|
||||||
withStoryGap={isAvatarOnlineShown}
|
withStoryGap={isAvatarOnlineShown || Boolean(chat.subscriptionUntil)}
|
||||||
storyViewerOrigin={StoryViewerOrigin.ChatList}
|
storyViewerOrigin={StoryViewerOrigin.ChatList}
|
||||||
storyViewerMode="single-peer"
|
storyViewerMode="single-peer"
|
||||||
/>
|
/>
|
||||||
<div className="avatar-badge-wrapper">
|
<div className="avatar-badge-wrapper">
|
||||||
<div className={buildClassName('avatar-online', isAvatarOnlineShown && 'avatar-online-shown')} />
|
<div
|
||||||
|
className={buildClassName('avatar-online', 'avatar-badge', isAvatarOnlineShown && 'avatar-online-shown')}
|
||||||
|
/>
|
||||||
|
{!isAvatarOnlineShown && Boolean(chat.subscriptionUntil) && (
|
||||||
|
<StarIcon type="gold" className="avatar-badge avatar-subscription" size="adaptive" />
|
||||||
|
)}
|
||||||
<ChatBadge
|
<ChatBadge
|
||||||
chat={chat}
|
chat={chat}
|
||||||
isMuted={isMuted}
|
isMuted={isMuted}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import React, { memo, useEffect } from '../../lib/teact/teact';
|
|||||||
import { getActions, withGlobal } from '../../global';
|
import { getActions, withGlobal } from '../../global';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ApiContact, ApiError, ApiInviteInfo, ApiPhoto,
|
ApiContact, ApiError,
|
||||||
} from '../../api/types';
|
} from '../../api/types';
|
||||||
import type { MessageList } from '../../global/types';
|
import type { MessageList } from '../../global/types';
|
||||||
|
|
||||||
@ -15,21 +15,18 @@ import renderText from '../common/helpers/renderText';
|
|||||||
import useFlag from '../../hooks/useFlag';
|
import useFlag from '../../hooks/useFlag';
|
||||||
import useOldLang from '../../hooks/useOldLang';
|
import useOldLang from '../../hooks/useOldLang';
|
||||||
|
|
||||||
import Avatar from '../common/Avatar';
|
|
||||||
import Button from '../ui/Button';
|
import Button from '../ui/Button';
|
||||||
import Modal from '../ui/Modal';
|
import Modal from '../ui/Modal';
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
currentMessageList?: MessageList;
|
currentMessageList?: MessageList;
|
||||||
dialogs: (ApiError | ApiInviteInfo | ApiContact)[];
|
dialogs: (ApiError | ApiContact)[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const Dialogs: FC<StateProps> = ({ dialogs, currentMessageList }) => {
|
const Dialogs: FC<StateProps> = ({ dialogs, currentMessageList }) => {
|
||||||
const {
|
const {
|
||||||
dismissDialog,
|
dismissDialog,
|
||||||
acceptInviteConfirmation,
|
|
||||||
sendMessage,
|
sendMessage,
|
||||||
showNotification,
|
|
||||||
} = getActions();
|
} = getActions();
|
||||||
const [isModalOpen, openModal, closeModal] = useFlag();
|
const [isModalOpen, openModal, closeModal] = useFlag();
|
||||||
|
|
||||||
@ -45,77 +42,6 @@ const Dialogs: FC<StateProps> = ({ dialogs, currentMessageList }) => {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderInviteHeader(title: string, photo?: ApiPhoto) {
|
|
||||||
return (
|
|
||||||
<div className="modal-header">
|
|
||||||
{photo && <Avatar size="small" photo={photo} withVideo />}
|
|
||||||
<div className="modal-title">
|
|
||||||
{renderText(title)}
|
|
||||||
</div>
|
|
||||||
<Button round color="translucent" size="smaller" ariaLabel={lang('Close')} onClick={closeModal}>
|
|
||||||
<i className="icon icon-close" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const renderInvite = (invite: ApiInviteInfo) => {
|
|
||||||
const {
|
|
||||||
hash, title, about, participantsCount, isChannel, photo, isRequestNeeded,
|
|
||||||
} = invite;
|
|
||||||
|
|
||||||
const handleJoinClick = () => {
|
|
||||||
acceptInviteConfirmation({
|
|
||||||
hash,
|
|
||||||
});
|
|
||||||
if (isRequestNeeded) {
|
|
||||||
showNotification({
|
|
||||||
message: isChannel ? lang('RequestToJoinChannelSentDescription') : lang('RequestToJoinGroupSentDescription'),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
closeModal();
|
|
||||||
};
|
|
||||||
|
|
||||||
const participantsText = isChannel
|
|
||||||
? lang('Subscribers', participantsCount, 'i')
|
|
||||||
: lang('Members', participantsCount, 'i');
|
|
||||||
|
|
||||||
const joinText = isChannel ? lang('ChannelJoin') : lang('JoinGroup');
|
|
||||||
const requestToJoinText = isChannel
|
|
||||||
? lang('MemberRequests.RequestToJoinChannel') : lang('MemberRequests.RequestToJoinGroup');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
isOpen={isModalOpen}
|
|
||||||
onClose={closeModal}
|
|
||||||
className="error"
|
|
||||||
header={renderInviteHeader(title, photo)}
|
|
||||||
onCloseAnimationEnd={dismissDialog}
|
|
||||||
>
|
|
||||||
{participantsCount !== undefined && <p className="modal-help">{participantsText}</p>}
|
|
||||||
{about && <p className="modal-about">{renderText(about, ['br'])}</p>}
|
|
||||||
{isRequestNeeded && (
|
|
||||||
<p className="modal-help">
|
|
||||||
{isChannel
|
|
||||||
? lang('MemberRequests.RequestToJoinDescriptionChannel')
|
|
||||||
: lang('MemberRequests.RequestToJoinDescriptionGroup')}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
<div className="dialog-buttons mt-2">
|
|
||||||
<Button
|
|
||||||
isText
|
|
||||||
className="confirm-dialog-button"
|
|
||||||
// eslint-disable-next-line react/jsx-no-bind
|
|
||||||
onClick={handleJoinClick}
|
|
||||||
>
|
|
||||||
{isRequestNeeded ? requestToJoinText : joinText}
|
|
||||||
</Button>
|
|
||||||
<Button isText className="confirm-dialog-button" onClick={closeModal}>{lang('Cancel')}</Button>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderContactRequest = (contactRequest: ApiContact) => {
|
const renderContactRequest = (contactRequest: ApiContact) => {
|
||||||
const handleConfirm = () => {
|
const handleConfirm = () => {
|
||||||
if (!currentMessageList) {
|
if (!currentMessageList) {
|
||||||
@ -171,11 +97,7 @@ const Dialogs: FC<StateProps> = ({ dialogs, currentMessageList }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderDialog = (dialog: ApiError | ApiInviteInfo | ApiContact) => {
|
const renderDialog = (dialog: ApiError | ApiContact) => {
|
||||||
if ('hash' in dialog) {
|
|
||||||
return renderInvite(dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('phoneNumber' in dialog) {
|
if ('phoneNumber' in dialog) {
|
||||||
return renderContactRequest(dialog);
|
return renderContactRequest(dialog);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import useModuleLoader from '../../../hooks/useModuleLoader';
|
|||||||
|
|
||||||
const StarsGiftModalAsync: FC<OwnProps> = (props) => {
|
const StarsGiftModalAsync: FC<OwnProps> = (props) => {
|
||||||
const { isOpen } = props;
|
const { isOpen } = props;
|
||||||
const StarsGiftModal = useModuleLoader(Bundles.Extra, 'StarsGiftModal', !isOpen);
|
const StarsGiftModal = useModuleLoader(Bundles.Stars, 'StarsGiftModal', !isOpen);
|
||||||
|
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
return StarsGiftModal ? <StarsGiftModal {...props} /> : undefined;
|
return StarsGiftModal ? <StarsGiftModal {...props} /> : undefined;
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import useModuleLoader from '../../../hooks/useModuleLoader';
|
|||||||
|
|
||||||
const StarsGiftingPickerModalAsync: FC<OwnProps> = (props) => {
|
const StarsGiftingPickerModalAsync: FC<OwnProps> = (props) => {
|
||||||
const { isOpen } = props;
|
const { isOpen } = props;
|
||||||
const StarsGiftingPickerModal = useModuleLoader(Bundles.Extra, 'StarsGiftingPickerModal', !isOpen);
|
const StarsGiftingPickerModal = useModuleLoader(Bundles.Stars, 'StarsGiftingPickerModal', !isOpen);
|
||||||
|
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
return StarsGiftingPickerModal ? <StarsGiftingPickerModal {...props} /> : undefined;
|
return StarsGiftingPickerModal ? <StarsGiftingPickerModal {...props} /> : undefined;
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { pick } from '../../util/iteratees';
|
|||||||
|
|
||||||
import AttachBotInstallModal from './attachBotInstall/AttachBotInstallModal.async';
|
import AttachBotInstallModal from './attachBotInstall/AttachBotInstallModal.async';
|
||||||
import BoostModal from './boost/BoostModal.async';
|
import BoostModal from './boost/BoostModal.async';
|
||||||
|
import ChatInviteModal from './chatInvite/ChatInviteModal.async';
|
||||||
import ChatlistModal from './chatlist/ChatlistModal.async';
|
import ChatlistModal from './chatlist/ChatlistModal.async';
|
||||||
import CollectibleInfoModal from './collectible/CollectibleInfoModal.async';
|
import CollectibleInfoModal from './collectible/CollectibleInfoModal.async';
|
||||||
import GiftCodeModal from './giftcode/GiftCodeModal.async';
|
import GiftCodeModal from './giftcode/GiftCodeModal.async';
|
||||||
@ -18,6 +19,7 @@ import PaidReactionModal from './paidReaction/PaidReactionModal.async';
|
|||||||
import ReportAdModal from './reportAd/ReportAdModal.async';
|
import ReportAdModal from './reportAd/ReportAdModal.async';
|
||||||
import StarsBalanceModal from './stars/StarsBalanceModal.async';
|
import StarsBalanceModal from './stars/StarsBalanceModal.async';
|
||||||
import StarsPaymentModal from './stars/StarsPaymentModal.async';
|
import StarsPaymentModal from './stars/StarsPaymentModal.async';
|
||||||
|
import StarsSubscriptionModal from './stars/subscription/StarsSubscriptionModal.async';
|
||||||
import StarsTransactionInfoModal from './stars/transaction/StarsTransactionModal.async';
|
import StarsTransactionInfoModal from './stars/transaction/StarsTransactionModal.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';
|
||||||
@ -39,7 +41,9 @@ type ModalKey = keyof Pick<TabState,
|
|||||||
'starsTransactionModal' |
|
'starsTransactionModal' |
|
||||||
'paidReactionModal' |
|
'paidReactionModal' |
|
||||||
'webApps' |
|
'webApps' |
|
||||||
'starsTransactionModal'
|
'starsTransactionModal' |
|
||||||
|
'chatInviteModal' |
|
||||||
|
'starsSubscriptionModal'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -69,7 +73,9 @@ const MODALS: ModalRegistry = {
|
|||||||
isStarPaymentModalOpen: StarsPaymentModal,
|
isStarPaymentModalOpen: StarsPaymentModal,
|
||||||
starsBalanceModal: StarsBalanceModal,
|
starsBalanceModal: StarsBalanceModal,
|
||||||
starsTransactionModal: StarsTransactionInfoModal,
|
starsTransactionModal: StarsTransactionInfoModal,
|
||||||
|
chatInviteModal: ChatInviteModal,
|
||||||
paidReactionModal: PaidReactionModal,
|
paidReactionModal: PaidReactionModal,
|
||||||
|
starsSubscriptionModal: StarsSubscriptionModal,
|
||||||
};
|
};
|
||||||
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>;
|
||||||
|
|||||||
18
src/components/modals/chatInvite/ChatInviteModal.async.tsx
Normal file
18
src/components/modals/chatInvite/ChatInviteModal.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 './ChatInviteModal';
|
||||||
|
|
||||||
|
import { Bundles } from '../../../util/moduleLoader';
|
||||||
|
|
||||||
|
import useModuleLoader from '../../../hooks/useModuleLoader';
|
||||||
|
|
||||||
|
const ChatInviteModalAsync: FC<OwnProps> = (props) => {
|
||||||
|
const { modal } = props;
|
||||||
|
const ChatInviteModal = useModuleLoader(Bundles.Extra, 'ChatInviteModal', !modal);
|
||||||
|
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
return ChatInviteModal ? <ChatInviteModal {...props} /> : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChatInviteModalAsync;
|
||||||
31
src/components/modals/chatInvite/ChatInviteModal.module.scss
Normal file
31
src/components/modals/chatInvite/ChatInviteModal.module.scss
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.participantCount {
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.participants {
|
||||||
|
display: flex;
|
||||||
|
overflow-x: scroll;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-self: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.participant {
|
||||||
|
min-width: 4.5rem;
|
||||||
|
width: 4.5rem;
|
||||||
|
margin-inline: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
align-self: flex-end;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
108
src/components/modals/chatInvite/ChatInviteModal.tsx
Normal file
108
src/components/modals/chatInvite/ChatInviteModal.tsx
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import React, { memo, useMemo, useRef } from '../../../lib/teact/teact';
|
||||||
|
import { getActions, getGlobal } from '../../../global';
|
||||||
|
|
||||||
|
import type { TabState } from '../../../global/types';
|
||||||
|
|
||||||
|
import { getCustomPeerFromInvite, getUserFullName } from '../../../global/helpers';
|
||||||
|
import { selectUser } from '../../../global/selectors';
|
||||||
|
import buildClassName from '../../../util/buildClassName';
|
||||||
|
|
||||||
|
import useHorizontalScroll from '../../../hooks/useHorizontalScroll';
|
||||||
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
import usePrevious from '../../../hooks/usePrevious';
|
||||||
|
|
||||||
|
import Avatar from '../../common/Avatar';
|
||||||
|
import FullNameTitle from '../../common/FullNameTitle';
|
||||||
|
import PeerBadge from '../../common/PeerBadge';
|
||||||
|
import Button from '../../ui/Button';
|
||||||
|
import Modal from '../../ui/Modal';
|
||||||
|
|
||||||
|
import styles from './ChatInviteModal.module.scss';
|
||||||
|
|
||||||
|
export type OwnProps = {
|
||||||
|
modal: TabState['chatInviteModal'];
|
||||||
|
};
|
||||||
|
|
||||||
|
const ChatInviteModal = ({ modal }: OwnProps) => {
|
||||||
|
const { acceptChatInvite, closeChatInviteModal, showNotification } = getActions();
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
const participantsRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const lang = useOldLang();
|
||||||
|
|
||||||
|
const prevModal = usePrevious(modal);
|
||||||
|
const { hash, inviteInfo } = modal || prevModal || {};
|
||||||
|
const {
|
||||||
|
about, isBroadcast, participantIds, participantsCount, photo, isRequestNeeded,
|
||||||
|
} = inviteInfo || {};
|
||||||
|
|
||||||
|
const handleClose = useLastCallback(() => {
|
||||||
|
closeChatInviteModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleAccept = useLastCallback(() => {
|
||||||
|
acceptChatInvite({ hash: hash! });
|
||||||
|
|
||||||
|
showNotification({
|
||||||
|
message: isBroadcast ? lang('RequestToJoinChannelSentDescription') : lang('RequestToJoinGroupSentDescription'),
|
||||||
|
});
|
||||||
|
|
||||||
|
handleClose();
|
||||||
|
});
|
||||||
|
|
||||||
|
const acceptLangKey = isBroadcast ? 'ProfileJoinChannel' : 'JoinGroup';
|
||||||
|
const requestToJoinLangKey = isBroadcast ? 'MemberRequests.RequestToJoinChannel'
|
||||||
|
: 'MemberRequests.RequestToJoinGroup';
|
||||||
|
|
||||||
|
const customPeer = useMemo(() => {
|
||||||
|
if (!inviteInfo) return undefined;
|
||||||
|
|
||||||
|
return getCustomPeerFromInvite(inviteInfo);
|
||||||
|
}, [inviteInfo]);
|
||||||
|
|
||||||
|
const participants = useMemo(() => {
|
||||||
|
if (!participantIds) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const global = getGlobal();
|
||||||
|
return participantIds.map((id) => selectUser(global, id)).filter(Boolean);
|
||||||
|
}, [participantIds]);
|
||||||
|
|
||||||
|
useHorizontalScroll(participantsRef, !modal || !participants);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={Boolean(modal)}
|
||||||
|
contentClassName={styles.content}
|
||||||
|
isSlim
|
||||||
|
onClose={handleClose}
|
||||||
|
onEnter={handleAccept}
|
||||||
|
>
|
||||||
|
{customPeer && <Avatar size="jumbo" photo={photo} peer={customPeer} withVideo />}
|
||||||
|
{customPeer && <FullNameTitle className={styles.title} peer={customPeer} />}
|
||||||
|
{about && <p className={styles.about}>{about}</p>}
|
||||||
|
<p className={styles.participantCount}>
|
||||||
|
{lang(isBroadcast ? 'Subscribers' : 'Members', participantsCount, 'i')}
|
||||||
|
</p>
|
||||||
|
{participants && (
|
||||||
|
<div ref={participantsRef} className={buildClassName(styles.participants, 'no-scrollbar')}>
|
||||||
|
{participants.map((participant) => (
|
||||||
|
<PeerBadge className={styles.participant} peer={participant} text={getUserFullName(participant)} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className={buildClassName('dialog-buttons', styles.buttons)}>
|
||||||
|
<Button isText className="confirm-dialog-button" onClick={handleAccept}>
|
||||||
|
{lang(isRequestNeeded ? requestToJoinLangKey : acceptLangKey)}
|
||||||
|
</Button>
|
||||||
|
<Button isText className="confirm-dialog-button" onClick={handleClose}>
|
||||||
|
{lang('Cancel')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(ChatInviteModal);
|
||||||
@ -13,56 +13,28 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.value {
|
.value {
|
||||||
|
background-color: var(--color-background);
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
|
min-width: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table {
|
.table {
|
||||||
border-collapse: separate;
|
display: grid;
|
||||||
border-spacing: 0;
|
grid-template-columns: max-content 1fr;
|
||||||
|
border-radius: 0.3125rem;
|
||||||
|
border: 1px solid var(--color-borders);
|
||||||
|
background-color: var(--color-borders);
|
||||||
|
gap: 1px;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cell {
|
.cell {
|
||||||
border: solid 0.0625rem var(--color-borders);
|
|
||||||
border-style: none solid solid none;
|
|
||||||
padding: 0.25rem 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row:first-child .cell:first-child { border-top-left-radius: 0.3125rem; }
|
|
||||||
|
|
||||||
.row:first-child .cell:last-child { border-top-right-radius: 0.3125rem; }
|
|
||||||
|
|
||||||
.row:last-child .cell:first-child { border-bottom-left-radius: 0.3125rem; }
|
|
||||||
|
|
||||||
.row:last-child .cell:last-child { border-bottom-right-radius: 0.3125rem; }
|
|
||||||
|
|
||||||
.row:first-child .cell { border-top-style: solid; }
|
|
||||||
|
|
||||||
.row .cell:first-child { border-left-style: solid; }
|
|
||||||
|
|
||||||
.section {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
padding: 0.5rem;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
min-height: 2.5rem;
|
||||||
@include mixins.adapt-padding-to-scrollbar(0.5rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { memo, type TeactNode } from '../../../lib/teact/teact';
|
import React, { memo, type TeactNode } from '../../../lib/teact/teact';
|
||||||
import { getActions } from '../../../global';
|
import { getActions } from '../../../global';
|
||||||
|
|
||||||
import type { ApiPeer, ApiWebDocument } from '../../../api/types';
|
import type { ApiPeer } from '../../../api/types';
|
||||||
import type { CustomPeer } from '../../../types';
|
import type { CustomPeer } from '../../../types';
|
||||||
|
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
@ -15,8 +15,6 @@ import Modal from '../../ui/Modal';
|
|||||||
|
|
||||||
import styles from './TableInfoModal.module.scss';
|
import styles from './TableInfoModal.module.scss';
|
||||||
|
|
||||||
import StarsBackground from '../../../assets/stars-bg.png';
|
|
||||||
|
|
||||||
type ChatItem = { chatId: string };
|
type ChatItem = { chatId: string };
|
||||||
|
|
||||||
export type TableData = [TeactNode, TeactNode | ChatItem][];
|
export type TableData = [TeactNode, TeactNode | ChatItem][];
|
||||||
@ -25,13 +23,7 @@ type OwnProps = {
|
|||||||
isOpen?: boolean;
|
isOpen?: boolean;
|
||||||
title?: string;
|
title?: string;
|
||||||
tableData?: TableData;
|
tableData?: TableData;
|
||||||
headerImageUrl?: string;
|
|
||||||
logoBackground?: string;
|
|
||||||
headerAvatarPeer?: ApiPeer | CustomPeer;
|
headerAvatarPeer?: ApiPeer | CustomPeer;
|
||||||
headerAvatarWebPhoto?: ApiWebDocument;
|
|
||||||
noHeaderImage?: boolean;
|
|
||||||
isGift?: boolean;
|
|
||||||
isPrizeStars?: boolean;
|
|
||||||
header?: TeactNode;
|
header?: TeactNode;
|
||||||
footer?: TeactNode;
|
footer?: TeactNode;
|
||||||
buttonText?: string;
|
buttonText?: string;
|
||||||
@ -44,13 +36,7 @@ const TableInfoModal = ({
|
|||||||
isOpen,
|
isOpen,
|
||||||
title,
|
title,
|
||||||
tableData,
|
tableData,
|
||||||
headerImageUrl,
|
|
||||||
logoBackground,
|
|
||||||
headerAvatarPeer,
|
headerAvatarPeer,
|
||||||
headerAvatarWebPhoto,
|
|
||||||
noHeaderImage,
|
|
||||||
isGift,
|
|
||||||
isPrizeStars,
|
|
||||||
header,
|
header,
|
||||||
footer,
|
footer,
|
||||||
buttonText,
|
buttonText,
|
||||||
@ -64,8 +50,6 @@ const TableInfoModal = ({
|
|||||||
onClose();
|
onClose();
|
||||||
});
|
});
|
||||||
|
|
||||||
const withAvatar = Boolean(headerAvatarPeer || headerAvatarWebPhoto);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
@ -77,23 +61,15 @@ const TableInfoModal = ({
|
|||||||
contentClassName={styles.content}
|
contentClassName={styles.content}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
>
|
>
|
||||||
{!isGift && !isPrizeStars && !noHeaderImage && (
|
{headerAvatarPeer && (
|
||||||
withAvatar ? (
|
<Avatar peer={headerAvatarPeer} size="jumbo" className={styles.avatar} />
|
||||||
<Avatar peer={headerAvatarPeer} webPhoto={headerAvatarWebPhoto} size="jumbo" className={styles.avatar} />
|
|
||||||
) : (
|
|
||||||
<div className={styles.section}>
|
|
||||||
<img className={styles.logo} src={headerImageUrl} alt="" draggable={false} />
|
|
||||||
{Boolean(logoBackground)
|
|
||||||
&& <img className={styles.logoBackground} src={StarsBackground} alt="" draggable={false} />}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
)}
|
)}
|
||||||
{header}
|
{header}
|
||||||
<table className={styles.table}>
|
<div className={styles.table}>
|
||||||
{tableData?.map(([label, value]) => (
|
{tableData?.map(([label, value]) => (
|
||||||
<tr className={styles.row}>
|
<>
|
||||||
<td className={buildClassName(styles.cell, styles.title)}>{label}</td>
|
<div className={buildClassName(styles.cell, styles.title)}>{label}</div>
|
||||||
<td className={buildClassName(styles.cell, styles.value)}>
|
<div className={buildClassName(styles.cell, styles.value)}>
|
||||||
{typeof value === 'object' && 'chatId' in value ? (
|
{typeof value === 'object' && 'chatId' in value ? (
|
||||||
<PickerSelectedItem
|
<PickerSelectedItem
|
||||||
peerId={value.chatId}
|
peerId={value.chatId}
|
||||||
@ -104,10 +80,10 @@ const TableInfoModal = ({
|
|||||||
onClick={handleOpenChat}
|
onClick={handleOpenChat}
|
||||||
/>
|
/>
|
||||||
) : value}
|
) : value}
|
||||||
</td>
|
</div>
|
||||||
</tr>
|
</>
|
||||||
))}
|
))}
|
||||||
</table>
|
</div>
|
||||||
{footer}
|
{footer}
|
||||||
{buttonText && (
|
{buttonText && (
|
||||||
<Button size="smaller" onClick={onButtonClick || onClose}>{buttonText}</Button>
|
<Button size="smaller" onClick={onButtonClick || onClose}>{buttonText}</Button>
|
||||||
|
|||||||
@ -6,3 +6,10 @@
|
|||||||
.centered {
|
.centered {
|
||||||
text-align: center !important;
|
text-align: center !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 7.5rem;
|
||||||
|
height: 7.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|||||||
@ -69,6 +69,7 @@ const GiftCodeModal = ({
|
|||||||
|
|
||||||
const header = (
|
const header = (
|
||||||
<>
|
<>
|
||||||
|
<img src={PremiumLogo} alt="" className={styles.logo} />
|
||||||
<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}`} />
|
||||||
</>
|
</>
|
||||||
@ -110,7 +111,6 @@ const GiftCodeModal = ({
|
|||||||
<TableInfoModal
|
<TableInfoModal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
title={lang('lng_gift_link_title')}
|
title={lang('lng_gift_link_title')}
|
||||||
headerImageUrl={PremiumLogo}
|
|
||||||
tableData={modalData.tableData}
|
tableData={modalData.tableData}
|
||||||
header={modalData.header}
|
header={modalData.header}
|
||||||
footer={modalData.footer}
|
footer={modalData.footer}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import useModuleLoader from '../../../hooks/useModuleLoader';
|
|||||||
|
|
||||||
const PaidReactionModalAsync: FC<OwnProps> = (props) => {
|
const PaidReactionModalAsync: FC<OwnProps> = (props) => {
|
||||||
const { modal } = props;
|
const { modal } = props;
|
||||||
const PaidReactionModal = useModuleLoader(Bundles.Extra, 'PaidReactionModal', !modal);
|
const PaidReactionModal = useModuleLoader(Bundles.Stars, 'PaidReactionModal', !modal);
|
||||||
|
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
return PaidReactionModal ? <PaidReactionModal {...props} /> : undefined;
|
return PaidReactionModal ? <PaidReactionModal {...props} /> : undefined;
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import useModuleLoader from '../../../hooks/useModuleLoader';
|
|||||||
|
|
||||||
const StarsBalanceModalAsync: FC<OwnProps> = (props) => {
|
const StarsBalanceModalAsync: FC<OwnProps> = (props) => {
|
||||||
const { modal } = props;
|
const { modal } = props;
|
||||||
const StarsBalanceModal = useModuleLoader(Bundles.Extra, 'StarsBalanceModal', !modal);
|
const StarsBalanceModal = useModuleLoader(Bundles.Stars, 'StarsBalanceModal', !modal);
|
||||||
|
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
return StarsBalanceModal ? <StarsBalanceModal {...props} /> : undefined;
|
return StarsBalanceModal ? <StarsBalanceModal {...props} /> : undefined;
|
||||||
|
|||||||
@ -42,6 +42,14 @@
|
|||||||
@include mixins.side-panel-section;
|
@include mixins.side-panel-section;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sectionTitle {
|
||||||
|
color: var(--color-primary);
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 1rem;
|
||||||
|
align-self: flex-start;
|
||||||
|
padding: 0.25rem 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
.secondaryInfo {
|
.secondaryInfo {
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
@ -76,6 +84,7 @@
|
|||||||
margin-inline: 0.5rem;
|
margin-inline: 0.5rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
text-wrap: balance;
|
text-wrap: balance;
|
||||||
|
line-height: 1.375;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
@ -123,10 +132,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.balanceBottom {
|
.balanceBottom {
|
||||||
|
line-height: 1.5;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
line-height: 1.5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.modalBalance {
|
.modalBalance {
|
||||||
@ -162,6 +172,17 @@
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.avatarStar {
|
||||||
|
font-size: 2rem;
|
||||||
|
|
||||||
|
@include mixins.filter-outline(1px, var(--color-background));
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
right: -1rem;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.paymentImageBackground {
|
.paymentImageBackground {
|
||||||
height: 7rem;
|
height: 7rem;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -179,13 +200,27 @@
|
|||||||
.paymentButton {
|
.paymentButton {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.125rem;
|
gap: 0.125rem;
|
||||||
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.paymentButtonStar {
|
.paymentButtonStar {
|
||||||
--color-fill: white !important;
|
--color-fill: white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.transactions {
|
.transactions, .subscriptions {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs {
|
||||||
|
// Disable tabs rounded corners
|
||||||
|
--border-radius-messages-small: 0;
|
||||||
|
|
||||||
|
top: 3.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disclaimer {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,8 @@ import TabList, { type TabWithProperties } from '../../ui/TabList';
|
|||||||
import Transition from '../../ui/Transition';
|
import Transition from '../../ui/Transition';
|
||||||
import BalanceBlock from './BalanceBlock';
|
import BalanceBlock from './BalanceBlock';
|
||||||
import StarTopupOptionList from './StarTopupOptionList';
|
import StarTopupOptionList from './StarTopupOptionList';
|
||||||
import TransactionItem from './transaction/StarsTransactionItem';
|
import StarsSubscriptionItem from './subscription/StarsSubscriptionItem';
|
||||||
|
import StarsTransactionItem from './transaction/StarsTransactionItem';
|
||||||
|
|
||||||
import styles from './StarsBalanceModal.module.scss';
|
import styles from './StarsBalanceModal.module.scss';
|
||||||
|
|
||||||
@ -39,6 +40,7 @@ const TRANSACTION_TABS: TabWithProperties[] = [
|
|||||||
{ title: 'StarsTransactionsIncoming' },
|
{ title: 'StarsTransactionsIncoming' },
|
||||||
{ title: 'StarsTransactionsOutgoing' },
|
{ title: 'StarsTransactionsOutgoing' },
|
||||||
];
|
];
|
||||||
|
const TRANSACTION_ITEM_CLASS = 'StarsTransactionItem';
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
modal: TabState['starsBalanceModal'];
|
modal: TabState['starsBalanceModal'];
|
||||||
@ -56,7 +58,7 @@ const StarsBalanceModal = ({
|
|||||||
closeStarsBalanceModal, loadStarsTransactions, openStarsGiftingModal, openInvoice,
|
closeStarsBalanceModal, loadStarsTransactions, openStarsGiftingModal, openInvoice,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const { balance, history } = starsBalanceState || {};
|
const { balance, history, subscriptions } = starsBalanceState || {};
|
||||||
|
|
||||||
const oldLang = useOldLang();
|
const oldLang = useOldLang();
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
@ -90,13 +92,14 @@ const StarsBalanceModal = ({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}, [oldLang, originPayment, originReaction, starsNeeded]);
|
}, [oldLang, originPayment, originReaction, starsNeeded]);
|
||||||
|
|
||||||
const shouldShowTransactions = Boolean(history?.all?.transactions.length && !originPayment && !originReaction);
|
const shouldShowItems = Boolean(history?.all?.transactions.length && !originPayment && !originReaction);
|
||||||
const shouldSuggestGifting = !originPayment && !originReaction;
|
const shouldSuggestGifting = !originPayment && !originReaction;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
setHeaderHidden(true);
|
setHeaderHidden(true);
|
||||||
setSelectedTabIndex(0);
|
setSelectedTabIndex(0);
|
||||||
|
hideBuyOptions();
|
||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
@ -127,7 +130,7 @@ const StarsBalanceModal = ({
|
|||||||
setHeaderHidden(scrollTop <= 150);
|
setHeaderHidden(scrollTop <= 150);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleLoadMore = useLastCallback(() => {
|
const handleLoadMoreTransactions = useLastCallback(() => {
|
||||||
loadStarsTransactions({
|
loadStarsTransactions({
|
||||||
type: TRANSACTION_TYPES[selectedTabIndex],
|
type: TRANSACTION_TYPES[selectedTabIndex],
|
||||||
});
|
});
|
||||||
@ -170,7 +173,7 @@ const StarsBalanceModal = ({
|
|||||||
<img className={styles.logo} src={StarLogo} alt="" draggable={false} />
|
<img className={styles.logo} src={StarLogo} alt="" draggable={false} />
|
||||||
<img className={styles.logoBackground} src={StarsBackground} alt="" draggable={false} />
|
<img className={styles.logoBackground} src={StarsBackground} alt="" draggable={false} />
|
||||||
<h2 className={styles.headerText}>
|
<h2 className={styles.headerText}>
|
||||||
{starsNeeded ? oldLang('StarsNeededTitle', starsNeeded) : oldLang('TelegramStars')}
|
{starsNeeded ? oldLang('StarsNeededTitle', ongoingTransactionAmount) : oldLang('TelegramStars')}
|
||||||
</h2>
|
</h2>
|
||||||
<div className={styles.description}>
|
<div className={styles.description}>
|
||||||
{renderText(
|
{renderText(
|
||||||
@ -207,7 +210,20 @@ const StarsBalanceModal = ({
|
|||||||
<div className={styles.secondaryInfo}>
|
<div className={styles.secondaryInfo}>
|
||||||
{tosText}
|
{tosText}
|
||||||
</div>
|
</div>
|
||||||
{shouldShowTransactions && (
|
{shouldShowItems && Boolean(subscriptions?.list.length) && (
|
||||||
|
<div className={styles.section}>
|
||||||
|
<h3 className={styles.sectionTitle}>{oldLang('StarMySubscriptions')}</h3>
|
||||||
|
<div className={styles.subscriptions}>
|
||||||
|
{subscriptions?.list.map((subscription) => (
|
||||||
|
<StarsSubscriptionItem
|
||||||
|
key={subscription.id}
|
||||||
|
subscription={subscription}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{shouldShowItems && (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.section}>
|
<div className={styles.section}>
|
||||||
<Transition
|
<Transition
|
||||||
@ -218,21 +234,25 @@ const StarsBalanceModal = ({
|
|||||||
className={styles.transition}
|
className={styles.transition}
|
||||||
>
|
>
|
||||||
<InfiniteScroll
|
<InfiniteScroll
|
||||||
onLoadMore={handleLoadMore}
|
onLoadMore={handleLoadMoreTransactions}
|
||||||
items={history?.[TRANSACTION_TYPES[selectedTabIndex]]?.transactions}
|
items={history?.[TRANSACTION_TYPES[selectedTabIndex]]?.transactions}
|
||||||
|
scrollContainerClosest={`.${styles.main}`}
|
||||||
|
itemSelector={`.${TRANSACTION_ITEM_CLASS}`}
|
||||||
className={styles.transactions}
|
className={styles.transactions}
|
||||||
noFastList
|
noFastList
|
||||||
>
|
>
|
||||||
{history?.[TRANSACTION_TYPES[selectedTabIndex]]?.transactions.map((transaction) => (
|
{history?.[TRANSACTION_TYPES[selectedTabIndex]]?.transactions.map((transaction) => (
|
||||||
<TransactionItem
|
<StarsTransactionItem
|
||||||
key={`${transaction.id}-${transaction.isRefund}`}
|
key={`${transaction.id}-${transaction.isRefund}`}
|
||||||
transaction={transaction}
|
transaction={transaction}
|
||||||
|
className={TRANSACTION_ITEM_CLASS}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
<TabList
|
<TabList
|
||||||
|
className={styles.tabs}
|
||||||
activeTab={selectedTabIndex}
|
activeTab={selectedTabIndex}
|
||||||
tabs={TRANSACTION_TABS}
|
tabs={TRANSACTION_TABS}
|
||||||
onSwitchTab={setSelectedTabIndex}
|
onSwitchTab={setSelectedTabIndex}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import useModuleLoader from '../../../hooks/useModuleLoader';
|
|||||||
|
|
||||||
const StarPaymentModalAsync: FC<OwnProps> = (props) => {
|
const StarPaymentModalAsync: FC<OwnProps> = (props) => {
|
||||||
const { modal } = props;
|
const { modal } = props;
|
||||||
const StarPaymentModal = useModuleLoader(Bundles.Extra, 'StarPaymentModal', !modal);
|
const StarPaymentModal = useModuleLoader(Bundles.Stars, 'StarPaymentModal', !modal);
|
||||||
|
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
return StarPaymentModal ? <StarPaymentModal {...props} /> : undefined;
|
return StarPaymentModal ? <StarPaymentModal {...props} /> : undefined;
|
||||||
|
|||||||
@ -2,11 +2,11 @@ import React, { memo, useEffect, useMemo } from '../../../lib/teact/teact';
|
|||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ApiChat, ApiMediaExtendedPreview, ApiMessage, ApiUser,
|
ApiChat, ApiChatInviteInfo, ApiMediaExtendedPreview, ApiMessage, ApiUser,
|
||||||
} from '../../../api/types';
|
} from '../../../api/types';
|
||||||
import type { GlobalState, TabState } from '../../../global/types';
|
import type { GlobalState, TabState } from '../../../global/types';
|
||||||
|
|
||||||
import { getChatTitle, getUserFullName } from '../../../global/helpers';
|
import { getChatTitle, getCustomPeerFromInvite, getUserFullName } from '../../../global/helpers';
|
||||||
import {
|
import {
|
||||||
selectChat, selectChatMessage, selectTabState, selectUser,
|
selectChat, selectChatMessage, selectTabState, selectUser,
|
||||||
} from '../../../global/selectors';
|
} from '../../../global/selectors';
|
||||||
@ -14,11 +14,13 @@ import buildClassName from '../../../util/buildClassName';
|
|||||||
import renderText from '../../common/helpers/renderText';
|
import renderText from '../../common/helpers/renderText';
|
||||||
|
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
import Avatar from '../../common/Avatar';
|
import Avatar from '../../common/Avatar';
|
||||||
import StarIcon from '../../common/icons/StarIcon';
|
import StarIcon from '../../common/icons/StarIcon';
|
||||||
|
import SafeLink from '../../common/SafeLink';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import Modal from '../../ui/Modal';
|
import Modal from '../../ui/Modal';
|
||||||
import BalanceBlock from './BalanceBlock';
|
import BalanceBlock from './BalanceBlock';
|
||||||
@ -38,6 +40,7 @@ type StateProps = {
|
|||||||
bot?: ApiUser;
|
bot?: ApiUser;
|
||||||
paidMediaMessage?: ApiMessage;
|
paidMediaMessage?: ApiMessage;
|
||||||
paidMediaChat?: ApiChat;
|
paidMediaChat?: ApiChat;
|
||||||
|
inviteInfo?: ApiChatInviteInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StarPaymentModal = ({
|
const StarPaymentModal = ({
|
||||||
@ -47,6 +50,7 @@ const StarPaymentModal = ({
|
|||||||
payment,
|
payment,
|
||||||
paidMediaMessage,
|
paidMediaMessage,
|
||||||
paidMediaChat,
|
paidMediaChat,
|
||||||
|
inviteInfo,
|
||||||
}: OwnProps & StateProps) => {
|
}: OwnProps & StateProps) => {
|
||||||
const { closePaymentModal, openStarsBalanceModal, sendStarPaymentForm } = getActions();
|
const { closePaymentModal, openStarsBalanceModal, sendStarPaymentForm } = getActions();
|
||||||
const [isLoading, markLoading, unmarkLoading] = useFlag();
|
const [isLoading, markLoading, unmarkLoading] = useFlag();
|
||||||
@ -54,7 +58,8 @@ const StarPaymentModal = ({
|
|||||||
|
|
||||||
const photo = payment?.invoice?.photo;
|
const photo = payment?.invoice?.photo;
|
||||||
|
|
||||||
const lang = useOldLang();
|
const oldLang = useOldLang();
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
@ -68,23 +73,54 @@ const StarPaymentModal = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const botName = getUserFullName(bot);
|
const botName = getUserFullName(bot);
|
||||||
const starsText = lang('Stars.Intro.PurchasedText.Stars', payment.invoice.amount);
|
const starsText = oldLang('Stars.Intro.PurchasedText.Stars', payment.invoice.amount);
|
||||||
|
|
||||||
if (paidMediaMessage) {
|
if (paidMediaMessage) {
|
||||||
const extendedMedia = paidMediaMessage.content.paidMedia!.extendedMedia as ApiMediaExtendedPreview[];
|
const extendedMedia = paidMediaMessage.content.paidMedia!.extendedMedia as ApiMediaExtendedPreview[];
|
||||||
const areAllPhotos = extendedMedia.every((media) => !media.duration);
|
const areAllPhotos = extendedMedia.every((media) => !media.duration);
|
||||||
const areAllVideos = extendedMedia.every((media) => !!media.duration);
|
const areAllVideos = extendedMedia.every((media) => !!media.duration);
|
||||||
|
|
||||||
const mediaText = areAllPhotos ? lang('Stars.Transfer.Photos', extendedMedia.length)
|
const mediaText = areAllPhotos ? oldLang('Stars.Transfer.Photos', extendedMedia.length)
|
||||||
: areAllVideos ? lang('Stars.Transfer.Videos', extendedMedia.length)
|
: areAllVideos ? oldLang('Stars.Transfer.Videos', extendedMedia.length)
|
||||||
: lang('Media', extendedMedia.length);
|
: oldLang('Media', extendedMedia.length);
|
||||||
|
|
||||||
const channelTitle = getChatTitle(lang, paidMediaChat!);
|
const channelTitle = getChatTitle(oldLang, paidMediaChat!);
|
||||||
return lang('Stars.Transfer.UnlockInfo', [mediaText, channelTitle, starsText]);
|
return oldLang('Stars.Transfer.UnlockInfo', [mediaText, channelTitle, starsText]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return lang('Stars.Transfer.Info', [payment.invoice.title, botName, starsText]);
|
if (inviteInfo) {
|
||||||
}, [payment?.invoice, bot, lang, paidMediaMessage, paidMediaChat]);
|
return lang('StarsSubscribeText', {
|
||||||
|
chat: inviteInfo.title,
|
||||||
|
amount: payment.invoice.amount,
|
||||||
|
}, {
|
||||||
|
withNodes: true,
|
||||||
|
withMarkdown: true,
|
||||||
|
pluralValue: payment.invoice.amount,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldLang('Stars.Transfer.Info', [payment.invoice.title, botName, starsText]);
|
||||||
|
}, [payment?.invoice, bot, oldLang, lang, paidMediaMessage, paidMediaChat, inviteInfo]);
|
||||||
|
|
||||||
|
const disclaimerText = useMemo(() => {
|
||||||
|
if (inviteInfo) {
|
||||||
|
return lang('StarsSubscribeInfo', {
|
||||||
|
link: <SafeLink url={lang('StarsSubscribeInfoLink')} text={lang('StarsSubscribeInfoLinkText')} />,
|
||||||
|
}, {
|
||||||
|
withNodes: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}, [inviteInfo, lang]);
|
||||||
|
|
||||||
|
const inviteCustomPeer = useMemo(() => {
|
||||||
|
if (!inviteInfo) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getCustomPeerFromInvite(inviteInfo);
|
||||||
|
}, [inviteInfo]);
|
||||||
|
|
||||||
const handlePayment = useLastCallback(() => {
|
const handlePayment = useLastCallback(() => {
|
||||||
const price = payment?.invoice?.amount;
|
const price = payment?.invoice?.amount;
|
||||||
@ -113,9 +149,14 @@ const StarPaymentModal = ({
|
|||||||
onClose={closePaymentModal}
|
onClose={closePaymentModal}
|
||||||
>
|
>
|
||||||
<BalanceBlock balance={starsBalanceState?.balance || 0} className={styles.modalBalance} />
|
<BalanceBlock balance={starsBalanceState?.balance || 0} className={styles.modalBalance} />
|
||||||
<div className={styles.paymentImages} dir={lang.isRtl ? 'ltr' : 'rtl'}>
|
<div className={styles.paymentImages} dir={oldLang.isRtl ? 'ltr' : 'rtl'}>
|
||||||
{paidMediaMessage ? (
|
{paidMediaMessage ? (
|
||||||
<PaidMediaThumb media={paidMediaMessage.content.paidMedia!.extendedMedia} />
|
<PaidMediaThumb media={paidMediaMessage.content.paidMedia!.extendedMedia} />
|
||||||
|
) : inviteCustomPeer ? (
|
||||||
|
<>
|
||||||
|
<Avatar className={styles.paymentPhoto} peer={inviteCustomPeer} size="giant" />
|
||||||
|
<StarIcon type="gold" size="adaptive" className={styles.avatarStar} />
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Avatar peer={bot} size="giant" />
|
<Avatar peer={bot} size="giant" />
|
||||||
@ -125,18 +166,23 @@ const StarPaymentModal = ({
|
|||||||
<img className={styles.paymentImageBackground} src={StarsBackground} alt="" draggable={false} />
|
<img className={styles.paymentImageBackground} src={StarsBackground} alt="" draggable={false} />
|
||||||
</div>
|
</div>
|
||||||
<h2 className={styles.headerText}>
|
<h2 className={styles.headerText}>
|
||||||
{lang('StarsConfirmPurchaseTitle')}
|
{inviteCustomPeer ? oldLang('StarsSubscribeTitle') : oldLang('StarsConfirmPurchaseTitle')}
|
||||||
</h2>
|
</h2>
|
||||||
<div className={buildClassName(styles.description, styles.smallerText)}>
|
<div className={styles.description}>
|
||||||
{renderText(descriptionText, ['simple_markdown', 'emoji'])}
|
{renderText(descriptionText, ['simple_markdown', 'emoji'])}
|
||||||
</div>
|
</div>
|
||||||
<Button className={styles.paymentButton} size="smaller" onClick={handlePayment} isLoading={isLoading}>
|
<Button className={styles.paymentButton} size="smaller" onClick={handlePayment} isLoading={isLoading}>
|
||||||
{lang('Stars.Transfer.Pay')}
|
{oldLang('Stars.Transfer.Pay')}
|
||||||
<div className={styles.paymentAmount}>
|
<div className={styles.paymentAmount}>
|
||||||
{payment?.invoice?.amount}
|
{payment?.invoice?.amount}
|
||||||
<StarIcon className={styles.paymentButtonStar} size="small" />
|
<StarIcon className={styles.paymentButtonStar} size="small" />
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
|
{disclaimerText && (
|
||||||
|
<div className={buildClassName(styles.disclaimer, styles.smallerText)}>
|
||||||
|
{disclaimerText}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -152,12 +198,17 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const chat = messageInputInvoice ? selectChat(global, messageInputInvoice.chatId) : undefined;
|
const chat = messageInputInvoice ? selectChat(global, messageInputInvoice.chatId) : undefined;
|
||||||
const isPaidMedia = message?.content.paidMedia;
|
const isPaidMedia = message?.content.paidMedia;
|
||||||
|
|
||||||
|
const inviteInputInvoice = payment.inputInvoice?.type === 'chatInviteSubscription'
|
||||||
|
? payment.inputInvoice : undefined;
|
||||||
|
const inviteInfo = inviteInputInvoice?.inviteInfo;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
bot,
|
bot,
|
||||||
starsBalanceState: global.stars,
|
starsBalanceState: global.stars,
|
||||||
payment,
|
payment,
|
||||||
paidMediaMessage: isPaidMedia ? message : undefined,
|
paidMediaMessage: isPaidMedia ? message : undefined,
|
||||||
paidMediaChat: isPaidMedia ? chat : undefined,
|
paidMediaChat: isPaidMedia ? chat : undefined,
|
||||||
|
inviteInfo,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(StarPaymentModal));
|
)(StarPaymentModal));
|
||||||
|
|||||||
@ -0,0 +1,69 @@
|
|||||||
|
@use '../../../../styles/mixins';
|
||||||
|
|
||||||
|
.root {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.25rem 0.75rem 0.25rem 0.25rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
cursor: var(--custom-cursor, pointer);
|
||||||
|
transition: background-color 0.25s ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-chat-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statusPricing {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title, .description {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description, .statusPeriod, .statusEnded {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.statusEnded {
|
||||||
|
color: var(--color-error);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
position: relative;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subscriptionStar {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
@include mixins.filter-outline(1px, var(--color-background));
|
||||||
|
}
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
import React, { memo } from '../../../../lib/teact/teact';
|
||||||
|
import { getActions } from '../../../../global';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ApiStarsSubscription,
|
||||||
|
} from '../../../../api/types';
|
||||||
|
import type { GlobalState } from '../../../../global/types';
|
||||||
|
|
||||||
|
import { getSenderTitle } from '../../../../global/helpers';
|
||||||
|
import { selectPeer } from '../../../../global/selectors';
|
||||||
|
import { formatDateToString } from '../../../../util/dates/dateFormat';
|
||||||
|
import { formatInteger } from '../../../../util/textFormat';
|
||||||
|
|
||||||
|
import useSelector from '../../../../hooks/data/useSelector';
|
||||||
|
import useLastCallback from '../../../../hooks/useLastCallback';
|
||||||
|
import useOldLang from '../../../../hooks/useOldLang';
|
||||||
|
|
||||||
|
import Avatar from '../../../common/Avatar';
|
||||||
|
import StarIcon from '../../../common/icons/StarIcon';
|
||||||
|
|
||||||
|
import styles from './StarsSubscriptionItem.module.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
subscription: ApiStarsSubscription;
|
||||||
|
};
|
||||||
|
|
||||||
|
function selectProvidedPeer(peerId: string) {
|
||||||
|
return (global: GlobalState) => (
|
||||||
|
selectPeer(global, peerId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const StarsSubscriptionItem = ({ subscription }: OwnProps) => {
|
||||||
|
const { openStarsSubscriptionModal } = getActions();
|
||||||
|
const {
|
||||||
|
peerId, pricing, until, isCancelled,
|
||||||
|
} = subscription;
|
||||||
|
const lang = useOldLang();
|
||||||
|
|
||||||
|
const peer = useSelector(selectProvidedPeer(peerId))!;
|
||||||
|
|
||||||
|
const handleClick = useLastCallback(() => {
|
||||||
|
openStarsSubscriptionModal({ subscription });
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!peer) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const hasExpired = until < Date.now() / 1000;
|
||||||
|
const formattedDate = formatDateToString(until * 1000, lang.code, true, 'long');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.root} onClick={handleClick}>
|
||||||
|
<div className={styles.preview}>
|
||||||
|
<Avatar size="medium" peer={peer} />
|
||||||
|
<StarIcon className={styles.subscriptionStar} type="gold" size="small" />
|
||||||
|
</div>
|
||||||
|
<div className={styles.info}>
|
||||||
|
<h3 className={styles.title}>{getSenderTitle(lang, peer)}</h3>
|
||||||
|
<p className={styles.description}>
|
||||||
|
{lang(
|
||||||
|
hasExpired ? 'StarsSubscriptionExpired'
|
||||||
|
: isCancelled ? 'StarsSubscriptionExpires' : 'StarsSubscriptionRenews',
|
||||||
|
formattedDate,
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className={styles.status}>
|
||||||
|
{(isCancelled || hasExpired) ? (
|
||||||
|
<div className={styles.statusEnded}>
|
||||||
|
{lang(hasExpired ? 'StarsSubscriptionStatusExpired' : 'StarsSubscriptionStatusCancelled')}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className={styles.statusPricing}>
|
||||||
|
<StarIcon className={styles.star} type="gold" size="adaptive" />
|
||||||
|
<span className={styles.amount}>
|
||||||
|
{formatInteger(pricing.amount)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.statusPeriod}>{lang('StarsParticipantSubscriptionPerMonth')}</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(StarsSubscriptionItem);
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import type { FC } from '../../../../lib/teact/teact';
|
||||||
|
import React from '../../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import type { OwnProps } from './StarsSubscriptionModal';
|
||||||
|
|
||||||
|
import { Bundles } from '../../../../util/moduleLoader';
|
||||||
|
|
||||||
|
import useModuleLoader from '../../../../hooks/useModuleLoader';
|
||||||
|
|
||||||
|
const StarsSubscriptionModalAsync: FC<OwnProps> = (props) => {
|
||||||
|
const { modal } = props;
|
||||||
|
const StarsSubscriptionModal = useModuleLoader(Bundles.Stars, 'StarsSubscriptionModal', !modal);
|
||||||
|
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
return StarsSubscriptionModal ? <StarsSubscriptionModal {...props} /> : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StarsSubscriptionModalAsync;
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
@use '../../../../styles/mixins';
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
z-index: calc(var(--z-modal-low-priority) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.starsHeader {
|
||||||
|
gap: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title, .amount {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
text-align: center;
|
||||||
|
margin-block: 0.5rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.starsBackground {
|
||||||
|
position: absolute;
|
||||||
|
height: 8rem;
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatarWrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subscriptionStar {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
font-size: 2rem;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
@include mixins.filter-outline(2px, var(--color-background));
|
||||||
|
}
|
||||||
|
|
||||||
|
.amountStar {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary {
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.danger {
|
||||||
|
color: var(--color-error);
|
||||||
|
}
|
||||||
@ -0,0 +1,230 @@
|
|||||||
|
import type { FC } from '../../../../lib/teact/teact';
|
||||||
|
import React, { memo, useMemo } from '../../../../lib/teact/teact';
|
||||||
|
import { getActions, withGlobal } from '../../../../global';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ApiPeer,
|
||||||
|
} from '../../../../api/types';
|
||||||
|
import type { TabState } from '../../../../global/types';
|
||||||
|
|
||||||
|
import {
|
||||||
|
selectPeer,
|
||||||
|
} from '../../../../global/selectors';
|
||||||
|
import buildClassName from '../../../../util/buildClassName';
|
||||||
|
import { formatDateTimeToString } from '../../../../util/dates/dateFormat';
|
||||||
|
|
||||||
|
import useLang from '../../../../hooks/useLang';
|
||||||
|
import useLastCallback from '../../../../hooks/useLastCallback';
|
||||||
|
import useOldLang from '../../../../hooks/useOldLang';
|
||||||
|
import usePrevious from '../../../../hooks/usePrevious';
|
||||||
|
|
||||||
|
import Avatar from '../../../common/Avatar';
|
||||||
|
import StarIcon from '../../../common/icons/StarIcon';
|
||||||
|
import SafeLink from '../../../common/SafeLink';
|
||||||
|
import Button from '../../../ui/Button';
|
||||||
|
import TableInfoModal, { type TableData } from '../../common/TableInfoModal';
|
||||||
|
|
||||||
|
import styles from './StarsSubscriptionModal.module.scss';
|
||||||
|
|
||||||
|
import StarsBackground from '../../../../assets/stars-bg.png';
|
||||||
|
|
||||||
|
export type OwnProps = {
|
||||||
|
modal: TabState['starsSubscriptionModal'];
|
||||||
|
};
|
||||||
|
|
||||||
|
type StateProps = {
|
||||||
|
peer?: ApiPeer;
|
||||||
|
};
|
||||||
|
|
||||||
|
const StarsSubscriptionModal: FC<OwnProps & StateProps> = ({
|
||||||
|
modal, peer,
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
closeStarsSubscriptionModal,
|
||||||
|
fulfillStarsSubscription,
|
||||||
|
changeStarsSubscription,
|
||||||
|
checkChatInvite,
|
||||||
|
loadStarStatus,
|
||||||
|
} = getActions();
|
||||||
|
const oldLang = useOldLang();
|
||||||
|
const lang = useLang();
|
||||||
|
const { subscription } = modal || {};
|
||||||
|
|
||||||
|
const buttonState = useMemo(() => {
|
||||||
|
if (!subscription) {
|
||||||
|
return 'hidden';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subscription.canRefulfill) {
|
||||||
|
return 'refulfill';
|
||||||
|
}
|
||||||
|
|
||||||
|
const isActive = subscription.until > Date.now() / 1000;
|
||||||
|
if (isActive && !subscription.isCancelled) {
|
||||||
|
return 'cancel';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isActive && subscription.isCancelled) {
|
||||||
|
return 'renew';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isActive) {
|
||||||
|
return 'restart';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'ok';
|
||||||
|
}, [subscription]);
|
||||||
|
|
||||||
|
const handleButtonClick = useLastCallback(() => {
|
||||||
|
if (!subscription) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (buttonState) {
|
||||||
|
case 'refulfill': {
|
||||||
|
fulfillStarsSubscription({ id: subscription.id });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'restart': {
|
||||||
|
checkChatInvite({ hash: subscription.chatInviteHash! });
|
||||||
|
loadStarStatus();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'renew': {
|
||||||
|
changeStarsSubscription({ id: subscription.id, isCancelled: false });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'cancel': {
|
||||||
|
changeStarsSubscription({ id: subscription.id, isCancelled: true });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closeStarsSubscriptionModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
const starModalData = useMemo(() => {
|
||||||
|
if (!subscription || !peer) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
pricing, until, isCancelled, canRefulfill,
|
||||||
|
} = subscription;
|
||||||
|
|
||||||
|
const header = (
|
||||||
|
<div className={buildClassName(styles.header, styles.starsHeader)}>
|
||||||
|
<div className={styles.avatarWrapper}>
|
||||||
|
<Avatar peer={peer} size="jumbo" />
|
||||||
|
<StarIcon className={styles.subscriptionStar} type="gold" size="adaptive" />
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
className={buildClassName(styles.starsBackground)}
|
||||||
|
src={StarsBackground}
|
||||||
|
alt=""
|
||||||
|
draggable={false}
|
||||||
|
/>
|
||||||
|
<h1 className={styles.title}>{oldLang('StarsSubscriptionTitle')}</h1>
|
||||||
|
<p className={styles.amount}>
|
||||||
|
{lang('StarsPerMonth', {
|
||||||
|
amount: pricing.amount,
|
||||||
|
}, {
|
||||||
|
withNodes: true,
|
||||||
|
specialReplacement: {
|
||||||
|
'⭐️': <StarIcon className={styles.amountStar} size="adaptive" type="gold" />,
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const tableData: TableData = [];
|
||||||
|
|
||||||
|
tableData.push([
|
||||||
|
oldLang('StarsSubscriptionChannel'),
|
||||||
|
{ chatId: peer.id },
|
||||||
|
]);
|
||||||
|
|
||||||
|
const hasExpired = until < Date.now() / 1000;
|
||||||
|
tableData.push([
|
||||||
|
oldLang(hasExpired ? 'StarsSubscriptionUntilExpired'
|
||||||
|
: isCancelled ? 'StarsSubscriptionUntilExpires' : 'StarsSubscriptionUntilRenews'),
|
||||||
|
formatDateTimeToString(until * 1000, oldLang.code, true),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const footerTos = lang('StarsTransactionTOS', {
|
||||||
|
link: <SafeLink url={lang('StarsTransactionTOSLink')} text={lang('StarsTransactionTOSLinkText')} />,
|
||||||
|
}, {
|
||||||
|
withNodes: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const footer = (
|
||||||
|
<span className={styles.footer}>
|
||||||
|
<p className={styles.secondary}>{footerTos}</p>
|
||||||
|
{isCancelled && (
|
||||||
|
<p className={styles.danger}>{oldLang('StarsSubscriptionCancelledText')}</p>
|
||||||
|
)}
|
||||||
|
{canRefulfill && (
|
||||||
|
<p className={styles.secondary}>
|
||||||
|
{oldLang('StarsSubscriptionRefulfillInfo', formatDateTimeToString(until * 1000, oldLang.code, true))}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{!isCancelled && !canRefulfill && hasExpired && (
|
||||||
|
<p className={styles.secondary}>
|
||||||
|
{oldLang('StarsSubscriptionExpiredInfo', formatDateTimeToString(until * 1000, oldLang.code, true))}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{!isCancelled && !canRefulfill && !hasExpired && (
|
||||||
|
<p className={styles.secondary}>
|
||||||
|
{oldLang('StarsSubscriptionCancelInfo', formatDateTimeToString(until * 1000, oldLang.code, true))}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{buttonState !== 'hidden' && (
|
||||||
|
<Button
|
||||||
|
size="smaller"
|
||||||
|
color={buttonState === 'cancel' ? 'danger' : 'primary'}
|
||||||
|
isText={buttonState === 'cancel'}
|
||||||
|
onClick={handleButtonClick}
|
||||||
|
>
|
||||||
|
{oldLang(
|
||||||
|
buttonState === 'cancel' ? 'StarsSubscriptionCancel'
|
||||||
|
: buttonState === 'refulfill' ? 'StarsSubscriptionRefulfill'
|
||||||
|
: buttonState === 'restart' ? 'StarsSubscriptionAgain'
|
||||||
|
: buttonState === 'renew' ? 'StarsSubscriptionRenew' : 'OK',
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
header,
|
||||||
|
tableData,
|
||||||
|
footer,
|
||||||
|
};
|
||||||
|
}, [buttonState, lang, oldLang, peer, subscription]);
|
||||||
|
|
||||||
|
const prevModalData = usePrevious(starModalData);
|
||||||
|
const renderingModalData = prevModalData || starModalData;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableInfoModal
|
||||||
|
isOpen={Boolean(subscription)}
|
||||||
|
className={styles.modal}
|
||||||
|
header={renderingModalData?.header}
|
||||||
|
tableData={renderingModalData?.tableData}
|
||||||
|
footer={renderingModalData?.footer}
|
||||||
|
onClose={closeStarsSubscriptionModal}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(withGlobal<OwnProps>(
|
||||||
|
(global, { modal }): StateProps => {
|
||||||
|
const peerId = modal?.subscription.peerId;
|
||||||
|
const peer = peerId ? selectPeer(global, peerId) : undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
peer,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
)(StarsSubscriptionModal));
|
||||||
@ -1,7 +1,9 @@
|
|||||||
|
@use '../../../../styles/mixins';
|
||||||
|
|
||||||
.root {
|
.root {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
padding: 0.25rem;
|
padding: 0.25rem 0.75rem 0.25rem 0.25rem;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
cursor: var(--custom-cursor, pointer);
|
cursor: var(--custom-cursor, pointer);
|
||||||
transition: background-color 0.25s ease-out;
|
transition: background-color 0.25s ease-out;
|
||||||
@ -28,10 +30,6 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.star {
|
|
||||||
margin-top: -0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title, .description, .date {
|
.title, .description, .date {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
@ -56,3 +54,17 @@
|
|||||||
.negative {
|
.negative {
|
||||||
color: var(--color-error);
|
color: var(--color-error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
position: relative;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subscriptionStar {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
@include mixins.filter-outline(1px, var(--color-background));
|
||||||
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import styles from './StarsTransactionItem.module.scss';
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
transaction: ApiStarsTransaction;
|
transaction: ApiStarsTransaction;
|
||||||
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
function selectOptionalPeer(peerId?: string) {
|
function selectOptionalPeer(peerId?: string) {
|
||||||
@ -35,7 +36,7 @@ function selectOptionalPeer(peerId?: string) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const StarsTransactionItem = ({ transaction }: OwnProps) => {
|
const StarsTransactionItem = ({ transaction, className }: OwnProps) => {
|
||||||
const { openStarsTransactionModal } = getActions();
|
const { openStarsTransactionModal } = getActions();
|
||||||
const {
|
const {
|
||||||
date,
|
date,
|
||||||
@ -43,6 +44,7 @@ const StarsTransactionItem = ({ transaction }: OwnProps) => {
|
|||||||
photo,
|
photo,
|
||||||
peer: transactionPeer,
|
peer: transactionPeer,
|
||||||
extendedMedia,
|
extendedMedia,
|
||||||
|
subscriptionPeriod,
|
||||||
} = transaction;
|
} = transaction;
|
||||||
const lang = useOldLang();
|
const lang = useOldLang();
|
||||||
|
|
||||||
@ -50,15 +52,13 @@ const StarsTransactionItem = ({ transaction }: OwnProps) => {
|
|||||||
const peer = useSelector(selectOptionalPeer(peerId));
|
const peer = useSelector(selectOptionalPeer(peerId));
|
||||||
|
|
||||||
const data = useMemo(() => {
|
const data = useMemo(() => {
|
||||||
let title = transaction.title;
|
let title = (() => {
|
||||||
if (transaction.extendedMedia) {
|
if (transaction.extendedMedia) return lang('StarMediaPurchase');
|
||||||
title = lang('StarMediaPurchase');
|
if (transaction.subscriptionPeriod) return lang('StarSubscriptionPurchase');
|
||||||
}
|
if (transaction.isReaction) return lang('StarsReactionsSent');
|
||||||
|
|
||||||
if (transaction.isReaction) {
|
|
||||||
title = lang('StarsReactionsSent');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return transaction.title;
|
||||||
|
})();
|
||||||
let description;
|
let description;
|
||||||
let status: string | undefined;
|
let status: string | undefined;
|
||||||
let avatarPeer: ApiPeer | CustomPeer | undefined;
|
let avatarPeer: ApiPeer | CustomPeer | undefined;
|
||||||
@ -68,7 +68,7 @@ const StarsTransactionItem = ({ transaction }: OwnProps) => {
|
|||||||
avatarPeer = peer || CUSTOM_PEER_PREMIUM;
|
avatarPeer = peer || CUSTOM_PEER_PREMIUM;
|
||||||
} else {
|
} else {
|
||||||
const customPeer = buildStarsTransactionCustomPeer(transaction.peer);
|
const customPeer = buildStarsTransactionCustomPeer(transaction.peer);
|
||||||
title = lang(customPeer.titleKey);
|
title = customPeer.title || lang(customPeer.titleKey!);
|
||||||
description = lang(customPeer.subtitleKey!);
|
description = lang(customPeer.subtitleKey!);
|
||||||
avatarPeer = customPeer;
|
avatarPeer = customPeer;
|
||||||
}
|
}
|
||||||
@ -102,9 +102,14 @@ const StarsTransactionItem = ({ transaction }: OwnProps) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.root} onClick={handleClick}>
|
<div className={buildClassName(styles.root, className)} onClick={handleClick}>
|
||||||
{extendedMedia ? <PaidMediaThumb media={extendedMedia} isTransactionPreview />
|
<div className={styles.preview}>
|
||||||
: <Avatar size="medium" webPhoto={photo} peer={data.avatarPeer} />}
|
{extendedMedia ? <PaidMediaThumb media={extendedMedia} isTransactionPreview />
|
||||||
|
: <Avatar size="medium" webPhoto={photo} peer={data.avatarPeer} />}
|
||||||
|
{Boolean(subscriptionPeriod) && (
|
||||||
|
<StarIcon className={styles.subscriptionStar} type="gold" size="small" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className={styles.info}>
|
<div className={styles.info}>
|
||||||
<h3 className={styles.title}>{data.title}</h3>
|
<h3 className={styles.title}>{data.title}</h3>
|
||||||
<p className={styles.description}>{data.description}</p>
|
<p className={styles.description}>{data.description}</p>
|
||||||
@ -117,7 +122,7 @@ const StarsTransactionItem = ({ transaction }: OwnProps) => {
|
|||||||
<span className={buildClassName(styles.amount, stars < 0 ? styles.negative : styles.positive)}>
|
<span className={buildClassName(styles.amount, stars < 0 ? styles.negative : styles.positive)}>
|
||||||
{formatStarsTransactionAmount(stars)}
|
{formatStarsTransactionAmount(stars)}
|
||||||
</span>
|
</span>
|
||||||
<StarIcon className={styles.star} type="gold" size="big" />
|
<StarIcon className={styles.star} type="gold" size="adaptive" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import useModuleLoader from '../../../../hooks/useModuleLoader';
|
|||||||
|
|
||||||
const StarsTransactionModalAsync: FC<OwnProps> = (props) => {
|
const StarsTransactionModalAsync: FC<OwnProps> = (props) => {
|
||||||
const { modal } = props;
|
const { modal } = props;
|
||||||
const StarsTransactionModal = useModuleLoader(Bundles.Extra, 'StarsTransactionInfoModal', !modal);
|
const StarsTransactionModal = useModuleLoader(Bundles.Stars, 'StarsTransactionInfoModal', !modal);
|
||||||
|
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
return StarsTransactionModal ? <StarsTransactionModal {...props} /> : undefined;
|
return StarsTransactionModal ? <StarsTransactionModal {...props} /> : undefined;
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
|
@use "../../../../styles/mixins";
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
z-index: calc(var(--z-modal-low-priority) + 1);
|
z-index: calc(var(--z-modal-low-priority) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal :global(.modal-dialog) {
|
|
||||||
max-width: 26rem !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.positive {
|
.positive {
|
||||||
color: var(--color-success);
|
color: var(--color-success);
|
||||||
}
|
}
|
||||||
@ -43,6 +41,11 @@
|
|||||||
font-family: var(--font-family-monospace);
|
font-family: var(--font-family-monospace);
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
@include mixins.gradient-border-right(3rem, 1rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
@ -53,27 +56,31 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
margin-block: 0.5rem;
|
margin-block: 0.5rem;
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.starsBackground {
|
.starsBackground {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 8rem;
|
height: 8rem;
|
||||||
top: -8.5rem;
|
top: 0;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
}
|
z-index: -1;
|
||||||
|
|
||||||
.mediaShift {
|
|
||||||
top: -1.5rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.copyIcon {
|
.copyIcon {
|
||||||
|
position: absolute;
|
||||||
|
right: 0.25rem;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
|
||||||
margin-inline-start: 0.25rem;
|
margin-inline-start: 0.25rem;
|
||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mediaPreview {
|
.mediaPreview {
|
||||||
margin-bottom: 2rem;
|
margin-block: 1.5rem 1rem;
|
||||||
cursor: var(--custom-cursor, pointer);
|
cursor: var(--custom-cursor, pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,9 +24,10 @@ 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 useOldLang from '../../../../hooks/useOldLang';
|
import useOldLang from '../../../../hooks/useOldLang';
|
||||||
import usePreviousDeprecated from '../../../../hooks/usePreviousDeprecated';
|
import usePrevious from '../../../../hooks/usePrevious';
|
||||||
|
|
||||||
import AnimatedIconFromSticker from '../../../common/AnimatedIconFromSticker';
|
import AnimatedIconFromSticker from '../../../common/AnimatedIconFromSticker';
|
||||||
|
import Avatar from '../../../common/Avatar';
|
||||||
import Icon from '../../../common/icons/Icon';
|
import Icon from '../../../common/icons/Icon';
|
||||||
import StarIcon from '../../../common/icons/StarIcon';
|
import StarIcon from '../../../common/icons/StarIcon';
|
||||||
import SafeLink from '../../../common/SafeLink';
|
import SafeLink from '../../../common/SafeLink';
|
||||||
@ -55,8 +56,6 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
const oldLang = useOldLang();
|
const oldLang = useOldLang();
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
const { transaction } = modal || {};
|
const { transaction } = modal || {};
|
||||||
const isGift = transaction?.isGift;
|
|
||||||
const isPrizeStars = transaction?.isPrizeStars;
|
|
||||||
|
|
||||||
const handleOpenMedia = useLastCallback(() => {
|
const handleOpenMedia = useLastCallback(() => {
|
||||||
const media = transaction?.extendedMedia;
|
const media = transaction?.extendedMedia;
|
||||||
@ -68,22 +67,6 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const animatedStickerData = useMemo(() => {
|
|
||||||
if (!transaction) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AnimatedIconFromSticker
|
|
||||||
key={transaction.id}
|
|
||||||
sticker={starGiftSticker}
|
|
||||||
play={canPlayAnimatedEmojis}
|
|
||||||
noLoop
|
|
||||||
nonInteractive
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}, [canPlayAnimatedEmojis, starGiftSticker, transaction]);
|
|
||||||
|
|
||||||
const giftEntryAboutText = useMemo(() => {
|
const giftEntryAboutText = useMemo(() => {
|
||||||
const subtitleText = oldLang('lng_credits_box_history_entry_gift_in_about');
|
const subtitleText = oldLang('lng_credits_box_history_entry_gift_in_about');
|
||||||
const subtitleTextParts = subtitleText.split('{link}');
|
const subtitleTextParts = subtitleText.split('{link}');
|
||||||
@ -127,24 +110,23 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { isGift, isPrizeStars, photo } = transaction;
|
||||||
|
|
||||||
const customPeer = (transaction.peer && transaction.peer.type !== 'peer'
|
const customPeer = (transaction.peer && transaction.peer.type !== 'peer'
|
||||||
&& buildStarsTransactionCustomPeer(transaction.peer)) || undefined;
|
&& buildStarsTransactionCustomPeer(transaction.peer)) || undefined;
|
||||||
|
|
||||||
const peerId = transaction.peer?.type === 'peer' ? transaction.peer.id : undefined;
|
const peerId = transaction.peer?.type === 'peer' ? transaction.peer.id : undefined;
|
||||||
const toName = transaction.peer && oldLang(getStarsPeerTitleKey(transaction.peer));
|
const toName = transaction.peer && oldLang(getStarsPeerTitleKey(transaction.peer));
|
||||||
|
|
||||||
let title = transaction.title;
|
const title = (() => {
|
||||||
if (!title && customPeer) {
|
if (transaction.extendedMedia) return oldLang('StarMediaPurchase');
|
||||||
title = oldLang(customPeer.titleKey);
|
if (transaction.subscriptionPeriod) return oldLang('StarSubscriptionPurchase');
|
||||||
}
|
if (transaction.isReaction) return oldLang('StarsReactionsSent');
|
||||||
|
|
||||||
if (!title && transaction.extendedMedia) {
|
if (customPeer) return customPeer.title || oldLang(customPeer.titleKey!);
|
||||||
title = oldLang('StarMediaPurchase');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!title && transaction.isReaction) {
|
return transaction.title;
|
||||||
title = oldLang('StarsReactionsSent');
|
})();
|
||||||
}
|
|
||||||
|
|
||||||
const messageLink = peer && transaction.messageId
|
const messageLink = peer && transaction.messageId
|
||||||
? getMessageLink(peer, undefined, transaction.messageId) : undefined;
|
? getMessageLink(peer, undefined, transaction.messageId) : undefined;
|
||||||
@ -161,6 +143,9 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const description = transaction.description || (media ? mediaText : undefined);
|
const description = transaction.description || (media ? mediaText : undefined);
|
||||||
|
|
||||||
|
const shouldDisplayAvatar = !media && !isGift && !isPrizeStars;
|
||||||
|
const avatarPeer = !photo ? (peer || customPeer) : undefined;
|
||||||
|
|
||||||
const header = (
|
const header = (
|
||||||
<div className={buildClassName(styles.header, styles.starsHeader)}>
|
<div className={buildClassName(styles.header, styles.starsHeader)}>
|
||||||
{media && (
|
{media && (
|
||||||
@ -170,14 +155,24 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
onClick={handleOpenMedia}
|
onClick={handleOpenMedia}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{(isGift || isPrizeStars) ? animatedStickerData : (
|
{(isGift || isPrizeStars) && starGiftSticker && (
|
||||||
<img
|
<AnimatedIconFromSticker
|
||||||
className={buildClassName(styles.starsBackground, media && styles.mediaShift)}
|
key={transaction.id}
|
||||||
src={StarsBackground}
|
sticker={starGiftSticker}
|
||||||
alt=""
|
play={canPlayAnimatedEmojis}
|
||||||
draggable={false}
|
noLoop
|
||||||
|
nonInteractive
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{shouldDisplayAvatar && (
|
||||||
|
<Avatar peer={avatarPeer} webPhoto={photo} size="jumbo" />
|
||||||
|
)}
|
||||||
|
<img
|
||||||
|
className={buildClassName(styles.starsBackground)}
|
||||||
|
src={StarsBackground}
|
||||||
|
alt=""
|
||||||
|
draggable={false}
|
||||||
|
/>
|
||||||
{title && <h1 className={styles.title}>{title}</h1>}
|
{title && <h1 className={styles.title}>{title}</h1>}
|
||||||
{(isGift || isPrizeStars) && (
|
{(isGift || isPrizeStars) && (
|
||||||
<h1 className={buildClassName(styles.title, styles.starTitle)}>
|
<h1 className={buildClassName(styles.title, styles.starTitle)}>
|
||||||
@ -223,18 +218,20 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
tableData.push([
|
tableData.push([
|
||||||
oldLang('Stars.Transaction.Id'),
|
oldLang('Stars.Transaction.Id'),
|
||||||
(
|
(
|
||||||
<span
|
<>
|
||||||
className={styles.tid}
|
<div
|
||||||
onClick={() => {
|
className={styles.tid}
|
||||||
copyTextToClipboard(transaction.id!);
|
onClick={() => {
|
||||||
showNotification({
|
copyTextToClipboard(transaction.id!);
|
||||||
message: oldLang('StarsTransactionIDCopied'),
|
showNotification({
|
||||||
});
|
message: oldLang('StarsTransactionIDCopied'),
|
||||||
}}
|
});
|
||||||
>
|
}}
|
||||||
{transaction.id}
|
>
|
||||||
|
{transaction.id}
|
||||||
|
</div>
|
||||||
<Icon className={styles.copyIcon} name="copy" />
|
<Icon className={styles.copyIcon} name="copy" />
|
||||||
</span>
|
</>
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -259,13 +256,12 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
header,
|
header,
|
||||||
tableData,
|
tableData,
|
||||||
footer,
|
footer,
|
||||||
avatarPeer: !transaction.photo ? (peer || customPeer) : undefined,
|
|
||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
transaction, oldLang, peer, isGift, isPrizeStars, animatedStickerData, giftOutAboutText, giftEntryAboutText,
|
transaction, oldLang, peer, giftOutAboutText, giftEntryAboutText, canPlayAnimatedEmojis, starGiftSticker,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const prevModalData = usePreviousDeprecated(starModalData);
|
const prevModalData = usePrevious(starModalData);
|
||||||
const renderingModalData = prevModalData || starModalData;
|
const renderingModalData = prevModalData || starModalData;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -273,13 +269,8 @@ const StarsTransactionModal: FC<OwnProps & StateProps> = ({
|
|||||||
isOpen={Boolean(transaction)}
|
isOpen={Boolean(transaction)}
|
||||||
className={styles.modal}
|
className={styles.modal}
|
||||||
header={renderingModalData?.header}
|
header={renderingModalData?.header}
|
||||||
isGift={isGift}
|
|
||||||
isPrizeStars={isPrizeStars}
|
|
||||||
tableData={renderingModalData?.tableData}
|
tableData={renderingModalData?.tableData}
|
||||||
footer={renderingModalData?.footer}
|
footer={renderingModalData?.footer}
|
||||||
noHeaderImage={Boolean(transaction?.extendedMedia)}
|
|
||||||
headerAvatarWebPhoto={transaction?.photo}
|
|
||||||
headerAvatarPeer={renderingModalData?.avatarPeer}
|
|
||||||
buttonText={oldLang('OK')}
|
buttonText={oldLang('OK')}
|
||||||
onClose={closeStarsTransactionModal}
|
onClose={closeStarsTransactionModal}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { getActions, withGlobal } from '../../../global';
|
|||||||
|
|
||||||
import type { ApiBoost, ApiBoostStatistics, ApiTypePrepaidGiveaway } from '../../../api/types';
|
import type { ApiBoost, ApiBoostStatistics, ApiTypePrepaidGiveaway } from '../../../api/types';
|
||||||
import type { TabState } from '../../../global/types';
|
import type { TabState } from '../../../global/types';
|
||||||
|
import type { CustomPeer } from '../../../types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
GIVEAWAY_BOOST_PER_PREMIUM,
|
GIVEAWAY_BOOST_PER_PREMIUM,
|
||||||
@ -17,7 +18,6 @@ import {
|
|||||||
} from '../../../global/selectors';
|
} from '../../../global/selectors';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import { formatDateAtTime } from '../../../util/dates/dateFormat';
|
import { formatDateAtTime } from '../../../util/dates/dateFormat';
|
||||||
import { CUSTOM_PEER_STAR, CUSTOM_PEER_TO_BE_DISTRIBUTED } from '../../../util/objects/customPeer';
|
|
||||||
import { formatInteger } from '../../../util/textFormat';
|
import { formatInteger } from '../../../util/textFormat';
|
||||||
import { getBoostProgressInfo } from '../../common/helpers/boostInfo';
|
import { getBoostProgressInfo } from '../../common/helpers/boostInfo';
|
||||||
|
|
||||||
@ -56,6 +56,19 @@ const GIVEAWAY_IMG_LIST: { [key: number]: string } = {
|
|||||||
12: GiftRedRound,
|
12: GiftRedRound,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CUSTOM_PEER_STAR_TEMPLATE: Omit<CustomPeer, 'title' | 'titleKey'> = {
|
||||||
|
isCustomPeer: true,
|
||||||
|
avatarIcon: 'star',
|
||||||
|
peerColorId: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
const CUSTOM_PEER_TO_BE_DISTRIBUTED: CustomPeer = {
|
||||||
|
isCustomPeer: true,
|
||||||
|
titleKey: 'BoostingToBeDistributed',
|
||||||
|
avatarIcon: 'user',
|
||||||
|
withPremiumGradient: true,
|
||||||
|
};
|
||||||
|
|
||||||
const BoostStatistics = ({
|
const BoostStatistics = ({
|
||||||
boostStatistics,
|
boostStatistics,
|
||||||
isGiveawayAvailable,
|
isGiveawayAvailable,
|
||||||
@ -199,6 +212,18 @@ const BoostStatistics = ({
|
|||||||
const renderBoostList = useLastCallback((boost) => {
|
const renderBoostList = useLastCallback((boost) => {
|
||||||
const hasStars = Boolean(boost?.stars);
|
const hasStars = Boolean(boost?.stars);
|
||||||
|
|
||||||
|
let customPeer: CustomPeer | undefined;
|
||||||
|
if (hasStars) {
|
||||||
|
customPeer = {
|
||||||
|
...CUSTOM_PEER_STAR_TEMPLATE,
|
||||||
|
title: lang('Stars', boost.stars),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!boost.userId) {
|
||||||
|
customPeer = CUSTOM_PEER_TO_BE_DISTRIBUTED;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem
|
<ListItem
|
||||||
className="chat-item-clickable"
|
className="chat-item-clickable"
|
||||||
@ -208,8 +233,7 @@ const BoostStatistics = ({
|
|||||||
<PrivateChatInfo
|
<PrivateChatInfo
|
||||||
className={styles.user}
|
className={styles.user}
|
||||||
userId={boost.userId}
|
userId={boost.userId}
|
||||||
customPeer={hasStars ? { ...CUSTOM_PEER_STAR, titleValue: boost.stars }
|
customPeer={customPeer}
|
||||||
: (!boost.userId ? CUSTOM_PEER_TO_BE_DISTRIBUTED : undefined)}
|
|
||||||
status={lang('BoostExpireOn', formatDateAtTime(lang, boost.expires * 1000))}
|
status={lang('BoostExpireOn', formatDateAtTime(lang, boost.expires * 1000))}
|
||||||
noEmojiStatus
|
noEmojiStatus
|
||||||
forceShowSelf
|
forceShowSelf
|
||||||
|
|||||||
@ -29,6 +29,7 @@ type OwnProps = {
|
|||||||
noFastList?: boolean;
|
noFastList?: boolean;
|
||||||
cacheBuster?: any;
|
cacheBuster?: any;
|
||||||
beforeChildren?: React.ReactNode;
|
beforeChildren?: React.ReactNode;
|
||||||
|
scrollContainerClosest?: string;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
onLoadMore?: ({ direction }: { direction: LoadMoreDirection; noScroll?: boolean }) => void;
|
onLoadMore?: ({ direction }: { direction: LoadMoreDirection; noScroll?: boolean }) => void;
|
||||||
onScroll?: (e: UIEvent<HTMLDivElement>) => void;
|
onScroll?: (e: UIEvent<HTMLDivElement>) => void;
|
||||||
@ -61,6 +62,7 @@ const InfiniteScroll: FC<OwnProps> = ({
|
|||||||
cacheBuster,
|
cacheBuster,
|
||||||
beforeChildren,
|
beforeChildren,
|
||||||
children,
|
children,
|
||||||
|
scrollContainerClosest,
|
||||||
onLoadMore,
|
onLoadMore,
|
||||||
onScroll,
|
onScroll,
|
||||||
onWheel,
|
onWheel,
|
||||||
@ -100,8 +102,10 @@ const InfiniteScroll: FC<OwnProps> = ({
|
|||||||
|
|
||||||
// Initial preload
|
// Initial preload
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const container = containerRef.current;
|
const scrollContainer = scrollContainerClosest
|
||||||
if (!loadMoreBackwards || !container) {
|
? containerRef.current!.closest<HTMLDivElement>(scrollContainerClosest)!
|
||||||
|
: containerRef.current!;
|
||||||
|
if (!loadMoreBackwards || !scrollContainer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,16 +114,21 @@ const InfiniteScroll: FC<OwnProps> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { scrollHeight, clientHeight } = container;
|
const { scrollHeight, clientHeight } = scrollContainer;
|
||||||
if (clientHeight && scrollHeight < clientHeight) {
|
if (clientHeight && scrollHeight < clientHeight) {
|
||||||
loadMoreBackwards();
|
loadMoreBackwards();
|
||||||
}
|
}
|
||||||
}, [items, loadMoreBackwards, preloadBackwards]);
|
}, [items, loadMoreBackwards, preloadBackwards, scrollContainerClosest]);
|
||||||
|
|
||||||
// Restore `scrollTop` after adding items
|
// Restore `scrollTop` after adding items
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
|
const scrollContainer = scrollContainerClosest
|
||||||
|
? containerRef.current!.closest<HTMLDivElement>(scrollContainerClosest)!
|
||||||
|
: containerRef.current!;
|
||||||
|
|
||||||
|
const container = containerRef.current!;
|
||||||
|
|
||||||
requestForcedReflow(() => {
|
requestForcedReflow(() => {
|
||||||
const container = containerRef.current!;
|
|
||||||
const state = stateRef.current;
|
const state = stateRef.current;
|
||||||
|
|
||||||
state.listItemElements = container.querySelectorAll<HTMLDivElement>(itemSelector);
|
state.listItemElements = container.querySelectorAll<HTMLDivElement>(itemSelector);
|
||||||
@ -127,7 +136,7 @@ const InfiniteScroll: FC<OwnProps> = ({
|
|||||||
let newScrollTop: number;
|
let newScrollTop: number;
|
||||||
|
|
||||||
if (state.currentAnchor && Array.from(state.listItemElements).includes(state.currentAnchor)) {
|
if (state.currentAnchor && Array.from(state.listItemElements).includes(state.currentAnchor)) {
|
||||||
const { scrollTop } = container;
|
const { scrollTop } = scrollContainer;
|
||||||
const newAnchorTop = state.currentAnchor!.getBoundingClientRect().top;
|
const newAnchorTop = state.currentAnchor!.getBoundingClientRect().top;
|
||||||
newScrollTop = scrollTop + (newAnchorTop - state.currentAnchorTop!);
|
newScrollTop = scrollTop + (newAnchorTop - state.currentAnchorTop!);
|
||||||
} else {
|
} else {
|
||||||
@ -142,18 +151,21 @@ const InfiniteScroll: FC<OwnProps> = ({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { scrollTop } = container;
|
const { scrollTop } = scrollContainer;
|
||||||
if (noScrollRestoreOnTop && scrollTop === 0) {
|
if (noScrollRestoreOnTop && scrollTop === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
resetScroll(container, newScrollTop);
|
resetScroll(scrollContainer, newScrollTop);
|
||||||
|
|
||||||
state.isScrollTopJustUpdated = true;
|
state.isScrollTopJustUpdated = true;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}, [items, itemSelector, noScrollRestore, noScrollRestoreOnTop, cacheBuster, withAbsolutePositioning]);
|
}, [
|
||||||
|
items, itemSelector, noScrollRestore, noScrollRestoreOnTop, cacheBuster, withAbsolutePositioning,
|
||||||
|
scrollContainerClosest,
|
||||||
|
]);
|
||||||
|
|
||||||
const handleScroll = useLastCallback((e: UIEvent<HTMLDivElement>) => {
|
const handleScroll = useLastCallback((e: UIEvent<HTMLDivElement>) => {
|
||||||
if (loadMoreForwards && loadMoreBackwards) {
|
if (loadMoreForwards && loadMoreBackwards) {
|
||||||
@ -168,8 +180,10 @@ const InfiniteScroll: FC<OwnProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listLength = listItemElements.length;
|
const listLength = listItemElements.length;
|
||||||
const container = containerRef.current!;
|
const scrollContainer = scrollContainerClosest
|
||||||
const { scrollTop, scrollHeight, offsetHeight } = container;
|
? containerRef.current!.closest<HTMLDivElement>(scrollContainerClosest)!
|
||||||
|
: containerRef.current!;
|
||||||
|
const { scrollTop, scrollHeight, offsetHeight } = scrollContainer;
|
||||||
const top = listLength ? listItemElements[0].offsetTop : 0;
|
const top = listLength ? listItemElements[0].offsetTop : 0;
|
||||||
const isNearTop = scrollTop <= top + sensitiveArea;
|
const isNearTop = scrollTop <= top + sensitiveArea;
|
||||||
const bottom = listLength
|
const bottom = listLength
|
||||||
@ -237,11 +251,25 @@ const InfiniteScroll: FC<OwnProps> = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
const scrollContainer = scrollContainerClosest
|
||||||
|
? containerRef.current!.closest<HTMLDivElement>(scrollContainerClosest)!
|
||||||
|
: containerRef.current!;
|
||||||
|
if (!scrollContainer) return undefined;
|
||||||
|
|
||||||
|
const handleNativeScroll = (e: Event) => handleScroll(e as unknown as UIEvent<HTMLDivElement>);
|
||||||
|
|
||||||
|
scrollContainer.addEventListener('scroll', handleNativeScroll);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
scrollContainer.removeEventListener('scroll', handleNativeScroll);
|
||||||
|
};
|
||||||
|
}, [handleScroll, scrollContainerClosest]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
className={className}
|
className={className}
|
||||||
onScroll={handleScroll}
|
|
||||||
onWheel={onWheel}
|
onWheel={onWheel}
|
||||||
teactFastList={!noFastList && !withAbsolutePositioning}
|
teactFastList={!noFastList && !withAbsolutePositioning}
|
||||||
onKeyDown={onKeyDown}
|
onKeyDown={onKeyDown}
|
||||||
|
|||||||
@ -51,7 +51,7 @@ export const MEDIA_PROGRESSIVE_CACHE_DISABLED = false;
|
|||||||
export const MEDIA_PROGRESSIVE_CACHE_NAME = 'tt-media-progressive';
|
export const MEDIA_PROGRESSIVE_CACHE_NAME = 'tt-media-progressive';
|
||||||
export const MEDIA_CACHE_MAX_BYTES = 512 * 1024; // 512 KB
|
export const MEDIA_CACHE_MAX_BYTES = 512 * 1024; // 512 KB
|
||||||
export const CUSTOM_BG_CACHE_NAME = 'tt-custom-bg';
|
export const CUSTOM_BG_CACHE_NAME = 'tt-custom-bg';
|
||||||
export const LANG_CACHE_NAME = 'tt-lang-packs-v42';
|
export const LANG_CACHE_NAME = 'tt-lang-packs-v43';
|
||||||
export const ASSET_CACHE_NAME = 'tt-assets';
|
export const ASSET_CACHE_NAME = 'tt-assets';
|
||||||
export const AUTODOWNLOAD_FILESIZE_MB_LIMITS = [1, 5, 10, 50, 100, 500];
|
export const AUTODOWNLOAD_FILESIZE_MB_LIMITS = [1, 5, 10, 50, 100, 500];
|
||||||
export const DATA_BROADCAST_CHANNEL_NAME = 'tt-global';
|
export const DATA_BROADCAST_CHANNEL_NAME = 'tt-global';
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import {
|
|||||||
RE_TG_LINK,
|
RE_TG_LINK,
|
||||||
SAVED_FOLDER_ID,
|
SAVED_FOLDER_ID,
|
||||||
SERVICE_NOTIFICATIONS_USER_ID,
|
SERVICE_NOTIFICATIONS_USER_ID,
|
||||||
|
STARS_CURRENCY_CODE,
|
||||||
TME_WEB_DOMAINS,
|
TME_WEB_DOMAINS,
|
||||||
TMP_CHAT_ID,
|
TMP_CHAT_ID,
|
||||||
TOP_CHAT_MESSAGES_PRELOAD_LIMIT,
|
TOP_CHAT_MESSAGES_PRELOAD_LIMIT,
|
||||||
@ -56,8 +57,10 @@ import {
|
|||||||
} from '../../index';
|
} from '../../index';
|
||||||
import {
|
import {
|
||||||
addChatMembers,
|
addChatMembers,
|
||||||
|
addChats,
|
||||||
addMessages,
|
addMessages,
|
||||||
addSimilarChannels,
|
addSimilarChannels,
|
||||||
|
addUsers,
|
||||||
addUserStatuses,
|
addUserStatuses,
|
||||||
deleteChatMessages,
|
deleteChatMessages,
|
||||||
deletePeerPhoto,
|
deletePeerPhoto,
|
||||||
@ -466,7 +469,7 @@ addActionHandler('openThread', async (global, actions, payload): Promise<void> =
|
|||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('openLinkedChat', async (global, actions, payload): Promise<void> => {
|
addActionHandler('openLinkedChat', async (global, actions, payload): Promise<void> => {
|
||||||
const { id, tabId = getCurrentTabId() } = payload!;
|
const { id, tabId = getCurrentTabId() } = payload;
|
||||||
const chat = selectChat(global, id);
|
const chat = selectChat(global, id);
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
return;
|
return;
|
||||||
@ -534,7 +537,7 @@ addActionHandler('loadAllChats', async (global, actions, payload): Promise<void>
|
|||||||
addActionHandler('loadFullChat', (global, actions, payload): ActionReturnType => {
|
addActionHandler('loadFullChat', (global, actions, payload): ActionReturnType => {
|
||||||
const {
|
const {
|
||||||
chatId, force, withPhotos,
|
chatId, force, withPhotos,
|
||||||
} = payload!;
|
} = payload;
|
||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
return;
|
return;
|
||||||
@ -716,7 +719,7 @@ addActionHandler('createChannel', async (global, actions, payload): Promise<void
|
|||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('joinChannel', async (global, actions, payload): Promise<void> => {
|
addActionHandler('joinChannel', async (global, actions, payload): Promise<void> => {
|
||||||
const { chatId, tabId = getCurrentTabId() } = payload!;
|
const { chatId, tabId = getCurrentTabId() } = payload;
|
||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
return;
|
return;
|
||||||
@ -758,7 +761,7 @@ addActionHandler('deleteChatUser', (global, actions, payload): ActionReturnType
|
|||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('deleteChat', (global, actions, payload): ActionReturnType => {
|
addActionHandler('deleteChat', (global, actions, payload): ActionReturnType => {
|
||||||
const { chatId, tabId = getCurrentTabId() } = payload!;
|
const { chatId, tabId = getCurrentTabId() } = payload;
|
||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
return;
|
return;
|
||||||
@ -775,7 +778,7 @@ addActionHandler('deleteChat', (global, actions, payload): ActionReturnType => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('leaveChannel', async (global, actions, payload): Promise<void> => {
|
addActionHandler('leaveChannel', async (global, actions, payload): Promise<void> => {
|
||||||
const { chatId, tabId = getCurrentTabId() } = payload!;
|
const { chatId, tabId = getCurrentTabId() } = payload;
|
||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
return;
|
return;
|
||||||
@ -797,8 +800,6 @@ addActionHandler('leaveChannel', async (global, actions, payload): Promise<void>
|
|||||||
global = deleteChatMessages(global, chatId, localMessageIds);
|
global = deleteChatMessages(global, chatId, localMessageIds);
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
}
|
}
|
||||||
|
|
||||||
actions.loadFullChat({ chatId, force: true });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('deleteChannel', (global, actions, payload): ActionReturnType => {
|
addActionHandler('deleteChannel', (global, actions, payload): ActionReturnType => {
|
||||||
@ -891,7 +892,7 @@ addActionHandler('createGroupChat', async (global, actions, payload): Promise<vo
|
|||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('toggleChatPinned', (global, actions, payload): ActionReturnType => {
|
addActionHandler('toggleChatPinned', (global, actions, payload): ActionReturnType => {
|
||||||
const { id, folderId, tabId = getCurrentTabId() } = payload!;
|
const { id, folderId, tabId = getCurrentTabId() } = payload;
|
||||||
const chat = selectChat(global, id);
|
const chat = selectChat(global, id);
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
return;
|
return;
|
||||||
@ -938,7 +939,7 @@ addActionHandler('toggleChatPinned', (global, actions, payload): ActionReturnTyp
|
|||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('toggleChatArchived', (global, actions, payload): ActionReturnType => {
|
addActionHandler('toggleChatArchived', (global, actions, payload): ActionReturnType => {
|
||||||
const { id } = payload!;
|
const { id } = payload;
|
||||||
const chat = selectChat(global, id);
|
const chat = selectChat(global, id);
|
||||||
if (chat) {
|
if (chat) {
|
||||||
void callApi('toggleChatArchived', {
|
void callApi('toggleChatArchived', {
|
||||||
@ -949,7 +950,7 @@ addActionHandler('toggleChatArchived', (global, actions, payload): ActionReturnT
|
|||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('toggleSavedDialogPinned', (global, actions, payload): ActionReturnType => {
|
addActionHandler('toggleSavedDialogPinned', (global, actions, payload): ActionReturnType => {
|
||||||
const { id, tabId = getCurrentTabId() } = payload!;
|
const { id, tabId = getCurrentTabId() } = payload;
|
||||||
const chat = selectChat(global, id);
|
const chat = selectChat(global, id);
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
return;
|
return;
|
||||||
@ -1046,7 +1047,7 @@ addActionHandler('editChatFolders', (global, actions, payload): ActionReturnType
|
|||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('editChatFolder', (global, actions, payload): ActionReturnType => {
|
addActionHandler('editChatFolder', (global, actions, payload): ActionReturnType => {
|
||||||
const { id, folderUpdate } = payload!;
|
const { id, folderUpdate } = payload;
|
||||||
const folder = selectChatFolder(global, id);
|
const folder = selectChatFolder(global, id);
|
||||||
|
|
||||||
if (folder) {
|
if (folder) {
|
||||||
@ -1063,7 +1064,7 @@ addActionHandler('editChatFolder', (global, actions, payload): ActionReturnType
|
|||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('addChatFolder', async (global, actions, payload): Promise<void> => {
|
addActionHandler('addChatFolder', async (global, actions, payload): Promise<void> => {
|
||||||
const { folder, tabId = getCurrentTabId() } = payload!;
|
const { folder, tabId = getCurrentTabId() } = payload;
|
||||||
const { orderedIds, byId } = global.chatFolders;
|
const { orderedIds, byId } = global.chatFolders;
|
||||||
|
|
||||||
const limit = selectCurrentLimit(global, 'dialogFilters');
|
const limit = selectCurrentLimit(global, 'dialogFilters');
|
||||||
@ -1125,7 +1126,7 @@ addActionHandler('addChatFolder', async (global, actions, payload): Promise<void
|
|||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('sortChatFolders', async (global, actions, payload): Promise<void> => {
|
addActionHandler('sortChatFolders', async (global, actions, payload): Promise<void> => {
|
||||||
const { folderIds } = payload!;
|
const { folderIds } = payload;
|
||||||
|
|
||||||
const result = await callApi('sortChatFolders', folderIds);
|
const result = await callApi('sortChatFolders', folderIds);
|
||||||
if (result) {
|
if (result) {
|
||||||
@ -1191,21 +1192,65 @@ addActionHandler('markTopicRead', (global, actions, payload): ActionReturnType =
|
|||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('openChatByInvite', async (global, actions, payload): Promise<void> => {
|
addActionHandler('checkChatInvite', async (global, actions, payload): Promise<void> => {
|
||||||
const { hash, tabId = getCurrentTabId() } = payload!;
|
const { hash, tabId = getCurrentTabId() } = payload;
|
||||||
|
|
||||||
const result = await callApi('openChatByInvite', hash);
|
const result = await callApi('checkChatInvite', hash);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
actions.openChat({ id: result.chatId, tabId });
|
global = getGlobal();
|
||||||
|
|
||||||
|
if (result.users) {
|
||||||
|
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.chat) {
|
||||||
|
global = addChats(global, buildCollectionByKey([result.chat], 'id'));
|
||||||
|
setGlobal(global);
|
||||||
|
actions.openChat({ id: result.chat.id, tabId });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.invite.subscriptionFormId) {
|
||||||
|
global = updateTabState(global, {
|
||||||
|
payment: {
|
||||||
|
formId: result.invite.subscriptionFormId,
|
||||||
|
inputInvoice: {
|
||||||
|
type: 'chatInviteSubscription',
|
||||||
|
hash,
|
||||||
|
inviteInfo: result.invite,
|
||||||
|
},
|
||||||
|
invoice: {
|
||||||
|
amount: result.invite.subscriptionPricing!.amount,
|
||||||
|
currency: STARS_CURRENCY_CODE,
|
||||||
|
isRecurring: true,
|
||||||
|
mediaType: 'invoice',
|
||||||
|
// Placeholder values
|
||||||
|
title: 'Subscription',
|
||||||
|
text: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isStarPaymentModalOpen: true,
|
||||||
|
}, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
global = updateTabState(global, {
|
||||||
|
chatInviteModal: {
|
||||||
|
hash,
|
||||||
|
inviteInfo: result.invite,
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
setGlobal(global);
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('openChatByPhoneNumber', async (global, actions, payload): Promise<void> => {
|
addActionHandler('openChatByPhoneNumber', async (global, actions, payload): Promise<void> => {
|
||||||
const {
|
const {
|
||||||
phoneNumber, startAttach, attach, text, tabId = getCurrentTabId(),
|
phoneNumber, startAttach, attach, text, tabId = getCurrentTabId(),
|
||||||
} = payload!;
|
} = payload;
|
||||||
|
|
||||||
// Open temporary empty chat to make the click response feel faster
|
// Open temporary empty chat to make the click response feel faster
|
||||||
actions.openChat({ id: TMP_CHAT_ID, tabId });
|
actions.openChat({ id: TMP_CHAT_ID, tabId });
|
||||||
@ -1240,7 +1285,7 @@ addActionHandler('openTelegramLink', async (global, actions, payload): Promise<v
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
openChatByPhoneNumber,
|
openChatByPhoneNumber,
|
||||||
openChatByInvite,
|
checkChatInvite,
|
||||||
openStickerSet,
|
openStickerSet,
|
||||||
openChatWithDraft,
|
openChatWithDraft,
|
||||||
joinVoiceChatByLink,
|
joinVoiceChatByLink,
|
||||||
@ -1313,7 +1358,7 @@ addActionHandler('openTelegramLink', async (global, actions, payload): Promise<v
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hash) {
|
if (hash) {
|
||||||
openChatByInvite({ hash, tabId });
|
checkChatInvite({ hash, tabId });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1453,8 +1498,8 @@ addActionHandler('processBoostParameters', async (global, actions, payload): Pro
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('acceptInviteConfirmation', async (global, actions, payload): Promise<void> => {
|
addActionHandler('acceptChatInvite', async (global, actions, payload): Promise<void> => {
|
||||||
const { hash, tabId = getCurrentTabId() } = payload!;
|
const { hash, tabId = getCurrentTabId() } = payload;
|
||||||
const result = await callApi('importChatInvite', { hash });
|
const result = await callApi('importChatInvite', { hash });
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return;
|
return;
|
||||||
@ -1467,7 +1512,7 @@ addActionHandler('openChatByUsername', async (global, actions, payload): Promise
|
|||||||
const {
|
const {
|
||||||
username, messageId, commentId, startParam, startAttach, attach, threadId, originalParts, startApp, text,
|
username, messageId, commentId, startParam, startAttach, attach, threadId, originalParts, startApp, text,
|
||||||
tabId = getCurrentTabId(),
|
tabId = getCurrentTabId(),
|
||||||
} = payload!;
|
} = payload;
|
||||||
|
|
||||||
const chat = selectCurrentChat(global, tabId);
|
const chat = selectCurrentChat(global, tabId);
|
||||||
const webAppName = originalParts?.[1];
|
const webAppName = originalParts?.[1];
|
||||||
@ -1557,7 +1602,7 @@ addActionHandler('togglePreHistoryHidden', async (global, actions, payload): Pro
|
|||||||
const {
|
const {
|
||||||
chatId, isEnabled,
|
chatId, isEnabled,
|
||||||
tabId = getCurrentTabId(),
|
tabId = getCurrentTabId(),
|
||||||
} = payload!;
|
} = payload;
|
||||||
|
|
||||||
const chat = await ensureIsSuperGroup(global, actions, chatId, tabId);
|
const chat = await ensureIsSuperGroup(global, actions, chatId, tabId);
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
@ -1572,7 +1617,7 @@ addActionHandler('togglePreHistoryHidden', async (global, actions, payload): Pro
|
|||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('updateChatDefaultBannedRights', (global, actions, payload): ActionReturnType => {
|
addActionHandler('updateChatDefaultBannedRights', (global, actions, payload): ActionReturnType => {
|
||||||
const { chatId, bannedRights } = payload!;
|
const { chatId, bannedRights } = payload;
|
||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
|
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
@ -1586,7 +1631,7 @@ addActionHandler('updateChatMemberBannedRights', async (global, actions, payload
|
|||||||
const {
|
const {
|
||||||
chatId, userId, bannedRights,
|
chatId, userId, bannedRights,
|
||||||
tabId = getCurrentTabId(),
|
tabId = getCurrentTabId(),
|
||||||
} = payload!;
|
} = payload;
|
||||||
|
|
||||||
const user = selectUser(global, userId);
|
const user = selectUser(global, userId);
|
||||||
|
|
||||||
@ -1634,7 +1679,7 @@ addActionHandler('updateChatAdmin', async (global, actions, payload): Promise<vo
|
|||||||
const {
|
const {
|
||||||
chatId, userId, adminRights, customTitle,
|
chatId, userId, adminRights, customTitle,
|
||||||
tabId = getCurrentTabId(),
|
tabId = getCurrentTabId(),
|
||||||
} = payload!;
|
} = payload;
|
||||||
|
|
||||||
const user = selectUser(global, userId);
|
const user = selectUser(global, userId);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
|||||||
@ -14,10 +14,10 @@ import { isChatChannel, isChatSuperGroup } from '../../helpers';
|
|||||||
import {
|
import {
|
||||||
getPrizeStarsTransactionFromGiveaway,
|
getPrizeStarsTransactionFromGiveaway,
|
||||||
getRequestInputInvoice,
|
getRequestInputInvoice,
|
||||||
getStarsTransactionFromGift,
|
|
||||||
} from '../../helpers/payments';
|
} from '../../helpers/payments';
|
||||||
import { addActionHandler, getGlobal, setGlobal } from '../../index';
|
import { addActionHandler, getGlobal, setGlobal } from '../../index';
|
||||||
import {
|
import {
|
||||||
|
appendStarsSubscriptions,
|
||||||
appendStarsTransactions, closeInvoice,
|
appendStarsTransactions, closeInvoice,
|
||||||
openStarsTransactionFromReceipt,
|
openStarsTransactionFromReceipt,
|
||||||
openStarsTransactionModal,
|
openStarsTransactionModal,
|
||||||
@ -38,6 +38,7 @@ import {
|
|||||||
selectChatMessage,
|
selectChatMessage,
|
||||||
selectPaymentFormId,
|
selectPaymentFormId,
|
||||||
selectPaymentInputInvoice, selectPaymentRequestId,
|
selectPaymentInputInvoice, selectPaymentRequestId,
|
||||||
|
selectPeer,
|
||||||
selectProviderPublicToken,
|
selectProviderPublicToken,
|
||||||
selectProviderPublishableKey,
|
selectProviderPublishableKey,
|
||||||
selectSmartGlocalCredentials,
|
selectSmartGlocalCredentials,
|
||||||
@ -271,6 +272,10 @@ addActionHandler('sendStarPaymentForm', async (global, actions, payload): Promis
|
|||||||
global = closeInvoice(global, tabId);
|
global = closeInvoice(global, tabId);
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
|
|
||||||
|
if ('channelId' in result) {
|
||||||
|
actions.openChat({ id: result.channelId, tabId });
|
||||||
|
}
|
||||||
|
|
||||||
actions.apiUpdate({
|
actions.apiUpdate({
|
||||||
'@type': 'updatePaymentStateCompleted',
|
'@type': 'updatePaymentStateCompleted',
|
||||||
inputInvoice,
|
inputInvoice,
|
||||||
@ -535,22 +540,6 @@ addActionHandler('closeStarsGiftingModal', (global, actions, payload): ActionRet
|
|||||||
}, tabId);
|
}, tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('openStarsTransactionFromGift', (global, actions, payload): ActionReturnType => {
|
|
||||||
const {
|
|
||||||
chatId,
|
|
||||||
messageId,
|
|
||||||
tabId = getCurrentTabId(),
|
|
||||||
} = payload || {};
|
|
||||||
|
|
||||||
const message = selectChatMessage(global, chatId, messageId);
|
|
||||||
if (!message) return undefined;
|
|
||||||
|
|
||||||
const transaction = getStarsTransactionFromGift(message);
|
|
||||||
if (!transaction) return undefined;
|
|
||||||
|
|
||||||
return openStarsTransactionModal(global, transaction, tabId);
|
|
||||||
});
|
|
||||||
|
|
||||||
addActionHandler('openPrizeStarsTransactionFromGiveaway', (global, actions, payload): ActionReturnType => {
|
addActionHandler('openPrizeStarsTransactionFromGiveaway', (global, actions, payload): ActionReturnType => {
|
||||||
const {
|
const {
|
||||||
chatId,
|
chatId,
|
||||||
@ -1056,11 +1045,18 @@ addActionHandler('loadStarStatus', async (global): Promise<void> => {
|
|||||||
inbound: undefined,
|
inbound: undefined,
|
||||||
outbound: undefined,
|
outbound: undefined,
|
||||||
},
|
},
|
||||||
|
subscriptions: undefined,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (status.history) {
|
if (status.history) {
|
||||||
global = appendStarsTransactions(global, 'all', status.history, status.nextOffset);
|
global = appendStarsTransactions(global, 'all', status.history, status.nextHistoryOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (status.subscriptions) {
|
||||||
|
global = appendStarsSubscriptions(global, status.subscriptions, status.nextSubscriptionOffset);
|
||||||
|
}
|
||||||
|
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1089,3 +1085,54 @@ addActionHandler('loadStarsTransactions', async (global, actions, payload): Prom
|
|||||||
}
|
}
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('loadStarsSubscriptions', async (global): Promise<void> => {
|
||||||
|
const subscriptions = global.stars?.subscriptions;
|
||||||
|
const offset = subscriptions?.nextOffset;
|
||||||
|
if (subscriptions && !offset) return; // Already loaded all
|
||||||
|
|
||||||
|
const result = await callApi('fetchStarsSubscriptions', {
|
||||||
|
offset: offset || '',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
global = getGlobal();
|
||||||
|
|
||||||
|
global = updateStarsBalance(global, result.balance);
|
||||||
|
global = appendStarsSubscriptions(global, result.subscriptions, result.nextOffset);
|
||||||
|
setGlobal(global);
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('changeStarsSubscription', async (global, actions, payload): Promise<void> => {
|
||||||
|
const { peerId, id, isCancelled } = payload;
|
||||||
|
|
||||||
|
const peer = peerId ? selectPeer(global, peerId) : undefined;
|
||||||
|
|
||||||
|
if (peerId && !peer) return;
|
||||||
|
|
||||||
|
await callApi('changeStarsSubscription', {
|
||||||
|
peer,
|
||||||
|
subscriptionId: id,
|
||||||
|
isCancelled,
|
||||||
|
});
|
||||||
|
|
||||||
|
actions.loadStarStatus();
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('fulfillStarsSubscription', async (global, actions, payload): Promise<void> => {
|
||||||
|
const { peerId, id } = payload;
|
||||||
|
|
||||||
|
const peer = peerId ? selectPeer(global, peerId) : undefined;
|
||||||
|
|
||||||
|
if (peerId && !peer) return;
|
||||||
|
|
||||||
|
await callApi('fulfillStarsSubscription', {
|
||||||
|
peer,
|
||||||
|
subscriptionId: id,
|
||||||
|
});
|
||||||
|
|
||||||
|
actions.loadStarStatus();
|
||||||
|
});
|
||||||
|
|||||||
@ -125,8 +125,10 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
|||||||
const chat = selectChat(global, update.id);
|
const chat = selectChat(global, update.id);
|
||||||
if (chat && isChatChannel(chat)) {
|
if (chat && isChatChannel(chat)) {
|
||||||
const chatMessages = selectChatMessages(global, update.id);
|
const chatMessages = selectChatMessages(global, update.id);
|
||||||
const localMessageIds = Object.keys(chatMessages).map(Number).filter(isLocalMessageId);
|
if (chatMessages) {
|
||||||
global = deleteChatMessages(global, chat.id, localMessageIds);
|
const localMessageIds = Object.keys(chatMessages).map(Number).filter(isLocalMessageId);
|
||||||
|
global = deleteChatMessages(global, chat.id, localMessageIds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return global;
|
return global;
|
||||||
@ -434,16 +436,6 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
|||||||
return global;
|
return global;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'showInvite': {
|
|
||||||
const { data } = update;
|
|
||||||
|
|
||||||
Object.values(global.byTabId).forEach(({ id: tabId }) => {
|
|
||||||
actions.showDialog({ data, tabId });
|
|
||||||
});
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'updatePendingJoinRequests': {
|
case 'updatePendingJoinRequests': {
|
||||||
const { chatId, requestsPending, recentRequesterIds } = update;
|
const { chatId, requestsPending, recentRequesterIds } = update;
|
||||||
const chat = global.chats.byId[chatId];
|
const chat = global.chats.byId[chatId];
|
||||||
|
|||||||
@ -176,7 +176,7 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'updatePaidReactionPrivacy': {
|
case 'updatePaidReactionPrivacy': {
|
||||||
return {
|
global = {
|
||||||
...global,
|
...global,
|
||||||
settings: {
|
settings: {
|
||||||
...global.settings,
|
...global.settings,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import type { ActionReturnType } from '../../types';
|
import type { ActionReturnType } from '../../types';
|
||||||
|
|
||||||
|
import { STARS_CURRENCY_CODE } from '../../../config';
|
||||||
import { areDeepEqual } from '../../../util/areDeepEqual';
|
import { areDeepEqual } from '../../../util/areDeepEqual';
|
||||||
import { formatCurrencyAsString } from '../../../util/formatCurrency';
|
import { formatCurrencyAsString } from '../../../util/formatCurrency';
|
||||||
import * as langProvider from '../../../util/oldLangProvider';
|
import * as langProvider from '../../../util/oldLangProvider';
|
||||||
@ -19,13 +20,15 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
|||||||
if (invoice) {
|
if (invoice) {
|
||||||
const { amount, currency, title } = invoice;
|
const { amount, currency, title } = invoice;
|
||||||
|
|
||||||
actions.showNotification({
|
if (currency !== STARS_CURRENCY_CODE) {
|
||||||
tabId,
|
actions.showNotification({
|
||||||
message: langProvider.oldTranslate('PaymentInfoHint', [
|
tabId,
|
||||||
formatCurrencyAsString(amount, currency, langProvider.getTranslationFn().code),
|
message: langProvider.oldTranslate('PaymentInfoHint', [
|
||||||
title,
|
formatCurrencyAsString(amount, currency, langProvider.getTranslationFn().code),
|
||||||
]),
|
title,
|
||||||
});
|
]),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputInvoice?.type === 'giftcode') {
|
if (inputInvoice?.type === 'giftcode') {
|
||||||
@ -42,7 +45,6 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
|||||||
isCompleted: true,
|
isCompleted: true,
|
||||||
},
|
},
|
||||||
}, tabId);
|
}, tabId);
|
||||||
global = closeInvoice(global, tabId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,14 +62,10 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
|||||||
isCompleted: true,
|
isCompleted: true,
|
||||||
},
|
},
|
||||||
}, tabId);
|
}, tabId);
|
||||||
global = closeInvoice(global, tabId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputInvoice?.type === 'stars') {
|
if (inputInvoice?.type === 'stars') {
|
||||||
if (!inputInvoice.stars) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const starsModalState = selectTabState(global, tabId).starsGiftModal;
|
const starsModalState = selectTabState(global, tabId).starsGiftModal;
|
||||||
|
|
||||||
if (starsModalState && starsModalState.isOpen) {
|
if (starsModalState && starsModalState.isOpen) {
|
||||||
@ -77,10 +75,27 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
|||||||
isCompleted: true,
|
isCompleted: true,
|
||||||
},
|
},
|
||||||
}, tabId);
|
}, tabId);
|
||||||
global = closeInvoice(global, tabId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actions.loadStarStatus(); // Manually reload. Server update takes ~10 seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inputInvoice?.type === 'chatInviteSubscription') {
|
||||||
|
const { amount } = invoice!;
|
||||||
|
actions.showNotification({
|
||||||
|
tabId,
|
||||||
|
title: langProvider.oldTranslate('StarsSubscriptionCompleted'),
|
||||||
|
message: langProvider.oldTranslate('StarsSubscriptionCompletedText', [
|
||||||
|
amount,
|
||||||
|
inputInvoice.inviteInfo.title,
|
||||||
|
], undefined, amount),
|
||||||
|
icon: 'star',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invoice?.currency === STARS_CURRENCY_CODE) {
|
||||||
|
global = closeInvoice(global, tabId);
|
||||||
|
}
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -204,3 +204,10 @@ addActionHandler('requestChatTranslation', (global, actions, payload): ActionRet
|
|||||||
const { chatId, toLanguageCode, tabId = getCurrentTabId() } = payload;
|
const { chatId, toLanguageCode, tabId = getCurrentTabId() } = payload;
|
||||||
return updateRequestedChatTranslation(global, chatId, toLanguageCode, tabId);
|
return updateRequestedChatTranslation(global, chatId, toLanguageCode, tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('closeChatInviteModal', (global, actions, payload): ActionReturnType => {
|
||||||
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
|
return updateTabState(global, {
|
||||||
|
chatInviteModal: undefined,
|
||||||
|
}, tabId);
|
||||||
|
});
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
import type { ActionReturnType } from '../../types';
|
import type { ActionReturnType } from '../../types';
|
||||||
|
|
||||||
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
||||||
|
import { getStarsTransactionFromGift } from '../../helpers/payments';
|
||||||
import { addActionHandler } from '../../index';
|
import { addActionHandler } from '../../index';
|
||||||
import {
|
import {
|
||||||
clearPayment, closeInvoice, openStarsTransactionModal, updatePayment,
|
clearPayment, closeInvoice, openStarsTransactionModal, updatePayment,
|
||||||
} from '../../reducers';
|
} from '../../reducers';
|
||||||
import { updateTabState } from '../../reducers/tabs';
|
import { updateTabState } from '../../reducers/tabs';
|
||||||
import { selectTabState } from '../../selectors';
|
import { selectChatMessage, selectTabState } from '../../selectors';
|
||||||
|
|
||||||
addActionHandler('closePaymentModal', (global, actions, payload): ActionReturnType => {
|
addActionHandler('closePaymentModal', (global, actions, payload): ActionReturnType => {
|
||||||
const { tabId = getCurrentTabId() } = payload || {};
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
@ -97,6 +98,22 @@ addActionHandler('openStarsTransactionModal', (global, actions, payload): Action
|
|||||||
return openStarsTransactionModal(global, transaction, tabId);
|
return openStarsTransactionModal(global, transaction, tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('openStarsTransactionFromGift', (global, actions, payload): ActionReturnType => {
|
||||||
|
const {
|
||||||
|
chatId,
|
||||||
|
messageId,
|
||||||
|
tabId = getCurrentTabId(),
|
||||||
|
} = payload || {};
|
||||||
|
|
||||||
|
const message = selectChatMessage(global, chatId, messageId);
|
||||||
|
if (!message) return undefined;
|
||||||
|
|
||||||
|
const transaction = getStarsTransactionFromGift(message);
|
||||||
|
if (!transaction) return undefined;
|
||||||
|
|
||||||
|
return openStarsTransactionModal(global, transaction, tabId);
|
||||||
|
});
|
||||||
|
|
||||||
addActionHandler('closeStarsTransactionModal', (global, actions, payload): ActionReturnType => {
|
addActionHandler('closeStarsTransactionModal', (global, actions, payload): ActionReturnType => {
|
||||||
const { tabId = getCurrentTabId() } = payload || {};
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
|
|
||||||
@ -104,3 +121,21 @@ addActionHandler('closeStarsTransactionModal', (global, actions, payload): Actio
|
|||||||
starsTransactionModal: undefined,
|
starsTransactionModal: undefined,
|
||||||
}, tabId);
|
}, tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('openStarsSubscriptionModal', (global, actions, payload): ActionReturnType => {
|
||||||
|
const { subscription, tabId = getCurrentTabId() } = payload;
|
||||||
|
|
||||||
|
return updateTabState(global, {
|
||||||
|
starsSubscriptionModal: {
|
||||||
|
subscription,
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('closeStarsSubscriptionModal', (global, actions, payload): ActionReturnType => {
|
||||||
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
|
|
||||||
|
return updateTabState(global, {
|
||||||
|
starsSubscriptionModal: undefined,
|
||||||
|
}, tabId);
|
||||||
|
});
|
||||||
|
|||||||
@ -4,12 +4,15 @@ import type {
|
|||||||
ApiChatBannedRights,
|
ApiChatBannedRights,
|
||||||
ApiChatFolder,
|
ApiChatFolder,
|
||||||
ApiChatFullInfo,
|
ApiChatFullInfo,
|
||||||
|
ApiChatInviteInfo,
|
||||||
ApiPeer,
|
ApiPeer,
|
||||||
ApiTopic,
|
ApiTopic,
|
||||||
ApiUser,
|
ApiUser,
|
||||||
} from '../../api/types';
|
} from '../../api/types';
|
||||||
import type { LangFn } from '../../hooks/useOldLang';
|
import type { LangFn } from '../../hooks/useOldLang';
|
||||||
import type { NotifyException, NotifySettings, ThreadId } from '../../types';
|
import type {
|
||||||
|
CustomPeer, NotifyException, NotifySettings, ThreadId,
|
||||||
|
} from '../../types';
|
||||||
import { MAIN_THREAD_ID } from '../../api/types';
|
import { MAIN_THREAD_ID } from '../../api/types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -482,3 +485,16 @@ export function getGroupStatus(lang: LangFn, chat: ApiChat) {
|
|||||||
? lang('Subscribers', membersCount, 'i')
|
? lang('Subscribers', membersCount, 'i')
|
||||||
: lang('Members', membersCount, 'i');
|
: lang('Members', membersCount, 'i');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getCustomPeerFromInvite(invite: ApiChatInviteInfo): CustomPeer {
|
||||||
|
const {
|
||||||
|
title, color, isVerified, isFake, isScam,
|
||||||
|
} = invite;
|
||||||
|
return {
|
||||||
|
isCustomPeer: true,
|
||||||
|
title,
|
||||||
|
peerColorId: color,
|
||||||
|
isVerified,
|
||||||
|
fakeType: isFake ? 'fake' : isScam ? 'scam' : undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@ -53,6 +53,15 @@ export function getRequestInputInvoice<T extends GlobalState>(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inputInvoice.type === 'chatInviteSubscription') {
|
||||||
|
const { hash } = inputInvoice;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'chatInviteSubscription',
|
||||||
|
hash,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (inputInvoice.type === 'message') {
|
if (inputInvoice.type === 'message') {
|
||||||
const chat = selectChat(global, inputInvoice.chatId);
|
const chat = selectChat(global, inputInvoice.chatId);
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
|
|||||||
@ -385,6 +385,7 @@ export function leaveChat<T extends GlobalState>(global: T, leftChatId: string):
|
|||||||
global = removeChatFromChatLists(global, leftChatId);
|
global = removeChatFromChatLists(global, leftChatId);
|
||||||
|
|
||||||
global = updateChat(global, leftChatId, { isNotJoined: true });
|
global = updateChat(global, leftChatId, { isNotJoined: true });
|
||||||
|
global = updateChatFullInfo(global, leftChatId, { joinInfo: undefined });
|
||||||
|
|
||||||
return global;
|
return global;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import type {
|
|||||||
ApiInvoice, ApiPaymentForm,
|
ApiInvoice, ApiPaymentForm,
|
||||||
ApiReceiptRegular,
|
ApiReceiptRegular,
|
||||||
ApiReceiptStars,
|
ApiReceiptStars,
|
||||||
|
ApiStarsSubscription,
|
||||||
ApiStarsTransaction,
|
ApiStarsTransaction,
|
||||||
} from '../../api/types';
|
} from '../../api/types';
|
||||||
import type { PaymentStep, ShippingOption } from '../../types';
|
import type { PaymentStep, ShippingOption } from '../../types';
|
||||||
@ -187,6 +188,29 @@ export function appendStarsTransactions<T extends GlobalState>(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function appendStarsSubscriptions<T extends GlobalState>(
|
||||||
|
global: T,
|
||||||
|
subscriptions: ApiStarsSubscription[],
|
||||||
|
nextOffset?: string,
|
||||||
|
): T {
|
||||||
|
if (!global.stars) {
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newObject = {
|
||||||
|
list: (global.stars.subscriptions?.list || []).concat(subscriptions),
|
||||||
|
nextOffset,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...global,
|
||||||
|
stars: {
|
||||||
|
...global.stars,
|
||||||
|
subscriptions: newObject,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function openStarsTransactionModal<T extends GlobalState>(
|
export function openStarsTransactionModal<T extends GlobalState>(
|
||||||
global: T, transaction: ApiStarsTransaction, ...[tabId = getCurrentTabId()]: TabArgs<T>
|
global: T, transaction: ApiStarsTransaction, ...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||||
): T {
|
): T {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import type {
|
|||||||
ApiChatBannedRights,
|
ApiChatBannedRights,
|
||||||
ApiChatFolder,
|
ApiChatFolder,
|
||||||
ApiChatFullInfo,
|
ApiChatFullInfo,
|
||||||
|
ApiChatInviteInfo,
|
||||||
ApiChatlistExportedInvite,
|
ApiChatlistExportedInvite,
|
||||||
ApiChatlistInvite,
|
ApiChatlistInvite,
|
||||||
ApiChatReactions,
|
ApiChatReactions,
|
||||||
@ -33,7 +34,6 @@ import type {
|
|||||||
ApiGroupStatistics,
|
ApiGroupStatistics,
|
||||||
ApiInputInvoice,
|
ApiInputInvoice,
|
||||||
ApiInputMessageReplyInfo,
|
ApiInputMessageReplyInfo,
|
||||||
ApiInviteInfo,
|
|
||||||
ApiInvoice,
|
ApiInvoice,
|
||||||
ApiKeyboardButton,
|
ApiKeyboardButton,
|
||||||
ApiMediaFormat,
|
ApiMediaFormat,
|
||||||
@ -65,6 +65,7 @@ import type {
|
|||||||
ApiSession,
|
ApiSession,
|
||||||
ApiSessionData,
|
ApiSessionData,
|
||||||
ApiSponsoredMessage, ApiStarGiveawayOption,
|
ApiSponsoredMessage, ApiStarGiveawayOption,
|
||||||
|
ApiStarsSubscription,
|
||||||
ApiStarsTransaction,
|
ApiStarsTransaction,
|
||||||
ApiStarTopupOption,
|
ApiStarTopupOption,
|
||||||
ApiStealthMode,
|
ApiStealthMode,
|
||||||
@ -183,6 +184,10 @@ export type StarsTransactionHistory = Record<StarsTransactionType, {
|
|||||||
transactions: ApiStarsTransaction[];
|
transactions: ApiStarsTransaction[];
|
||||||
nextOffset?: string;
|
nextOffset?: string;
|
||||||
} | undefined>;
|
} | undefined>;
|
||||||
|
export type StarsSubscriptions = {
|
||||||
|
list: ApiStarsSubscription[];
|
||||||
|
nextOffset?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type ConfettiStyle = 'poppers' | 'top-down';
|
export type ConfettiStyle = 'poppers' | 'top-down';
|
||||||
|
|
||||||
@ -351,6 +356,11 @@ export type TabState = {
|
|||||||
messageIds: number[];
|
messageIds: number[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
chatInviteModal?: {
|
||||||
|
hash: string;
|
||||||
|
inviteInfo: ApiChatInviteInfo;
|
||||||
|
};
|
||||||
|
|
||||||
seenByModal?: {
|
seenByModal?: {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
messageId: number;
|
messageId: number;
|
||||||
@ -595,7 +605,7 @@ export type TabState = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
notifications: ApiNotification[];
|
notifications: ApiNotification[];
|
||||||
dialogs: (ApiError | ApiInviteInfo | ApiContact)[];
|
dialogs: (ApiError | ApiContact)[];
|
||||||
|
|
||||||
safeLinkModalUrl?: string;
|
safeLinkModalUrl?: string;
|
||||||
mapModal?: {
|
mapModal?: {
|
||||||
@ -748,6 +758,9 @@ export type TabState = {
|
|||||||
starsTransactionModal?: {
|
starsTransactionModal?: {
|
||||||
transaction: ApiStarsTransaction;
|
transaction: ApiStarsTransaction;
|
||||||
};
|
};
|
||||||
|
starsSubscriptionModal?: {
|
||||||
|
subscription: ApiStarsSubscription;
|
||||||
|
};
|
||||||
|
|
||||||
giftModal?: {
|
giftModal?: {
|
||||||
isCompleted?: boolean;
|
isCompleted?: boolean;
|
||||||
@ -1216,6 +1229,7 @@ export type GlobalState = {
|
|||||||
topupOptions: ApiStarTopupOption[];
|
topupOptions: ApiStarTopupOption[];
|
||||||
balance: number;
|
balance: number;
|
||||||
history: StarsTransactionHistory;
|
history: StarsTransactionHistory;
|
||||||
|
subscriptions?: StarsSubscriptions;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1337,7 +1351,12 @@ export interface ActionPayloads {
|
|||||||
adminRights: ApiChatAdminRights;
|
adminRights: ApiChatAdminRights;
|
||||||
customTitle?: string;
|
customTitle?: string;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
acceptInviteConfirmation: { hash: string } & WithTabId;
|
|
||||||
|
checkChatInvite: {
|
||||||
|
hash: string;
|
||||||
|
} & WithTabId;
|
||||||
|
acceptChatInvite: { hash: string } & WithTabId;
|
||||||
|
closeChatInviteModal: WithTabId | undefined;
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
setSettingOption: Partial<ISettings> | undefined;
|
setSettingOption: Partial<ISettings> | undefined;
|
||||||
@ -1546,9 +1565,6 @@ export interface ActionPayloads {
|
|||||||
attach?: string;
|
attach?: string;
|
||||||
text?: string;
|
text?: string;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
openChatByInvite: {
|
|
||||||
hash: string;
|
|
||||||
} & WithTabId;
|
|
||||||
toggleSavedDialogPinned: {
|
toggleSavedDialogPinned: {
|
||||||
id: string;
|
id: string;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
@ -1839,6 +1855,10 @@ export interface ActionPayloads {
|
|||||||
messageId: number;
|
messageId: number;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
closeStarsTransactionModal: WithTabId | undefined;
|
closeStarsTransactionModal: WithTabId | undefined;
|
||||||
|
openStarsSubscriptionModal: {
|
||||||
|
subscription: ApiStarsSubscription;
|
||||||
|
} & WithTabId;
|
||||||
|
closeStarsSubscriptionModal: WithTabId | undefined;
|
||||||
openPrizeStarsTransactionFromGiveaway: {
|
openPrizeStarsTransactionFromGiveaway: {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
messageId: number;
|
messageId: number;
|
||||||
@ -2304,6 +2324,16 @@ export interface ActionPayloads {
|
|||||||
loadStarsTransactions: {
|
loadStarsTransactions: {
|
||||||
type: StarsTransactionType;
|
type: StarsTransactionType;
|
||||||
};
|
};
|
||||||
|
loadStarsSubscriptions: undefined;
|
||||||
|
changeStarsSubscription: {
|
||||||
|
peerId?: string;
|
||||||
|
id: string;
|
||||||
|
isCancelled: boolean;
|
||||||
|
};
|
||||||
|
fulfillStarsSubscription: {
|
||||||
|
peerId?: string;
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
openStarsBalanceModal: {
|
openStarsBalanceModal: {
|
||||||
originPayment?: TabState['payment'];
|
originPayment?: TabState['payment'];
|
||||||
originReaction?: {
|
originReaction?: {
|
||||||
|
|||||||
@ -1000,8 +1000,11 @@ class TelegramClient {
|
|||||||
if (isExported) this.releaseExportedSender(sender);
|
if (isExported) this.releaseExportedSender(sender);
|
||||||
return result;
|
return result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof errors.ServerError || e.message === 'RPC_CALL_FAIL'
|
if (e instanceof errors.ServerError
|
||||||
|| e.message === 'RPC_MCGET_FAIL') {
|
|| e.message === 'RPC_CALL_FAIL'
|
||||||
|
|| e.message === 'RPC_MCGET_FAIL'
|
||||||
|
|| e.message.match(/INTERDC_\d_CALL(_RICH)?_ERROR/)
|
||||||
|
) {
|
||||||
this._log.warn(`Telegram is having internal issues ${e.constructor.name}`);
|
this._log.warn(`Telegram is having internal issues ${e.constructor.name}`);
|
||||||
await sleep(2000);
|
await sleep(2000);
|
||||||
} else if (e instanceof errors.FloodWaitError || e instanceof errors.FloodTestPhoneWaitError) {
|
} else if (e instanceof errors.FloodWaitError || e instanceof errors.FloodTestPhoneWaitError) {
|
||||||
|
|||||||
@ -1654,6 +1654,9 @@ payments.sendStarsForm#2bb731d flags:# form_id:long invoice:InputInvoice = payme
|
|||||||
payments.refundStarsCharge#25ae8f4a user_id:InputUser charge_id:string = Updates;
|
payments.refundStarsCharge#25ae8f4a user_id:InputUser charge_id:string = Updates;
|
||||||
payments.getStarsTransactionsByID#27842d2e peer:InputPeer id:Vector<InputStarsTransaction> = payments.StarsStatus;
|
payments.getStarsTransactionsByID#27842d2e peer:InputPeer id:Vector<InputStarsTransaction> = payments.StarsStatus;
|
||||||
payments.getStarsGiftOptions#d3c96bc8 flags:# user_id:flags.0?InputUser = Vector<StarsGiftOption>;
|
payments.getStarsGiftOptions#d3c96bc8 flags:# user_id:flags.0?InputUser = Vector<StarsGiftOption>;
|
||||||
|
payments.getStarsSubscriptions#32512c5 flags:# missing_balance:flags.0?true peer:InputPeer offset:string = payments.StarsStatus;
|
||||||
|
payments.changeStarsSubscription#c7770878 flags:# peer:InputPeer subscription_id:string canceled:flags.0?Bool = Bool;
|
||||||
|
payments.fulfillStarsSubscription#cc5bebb3 peer:InputPeer subscription_id:string = Bool;
|
||||||
payments.getStarsGiveawayOptions#bd1efd3e = Vector<StarsGiveawayOption>;
|
payments.getStarsGiveawayOptions#bd1efd3e = Vector<StarsGiveawayOption>;
|
||||||
phone.requestCall#42ff96ed flags:# video:flags.0?true user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
|
phone.requestCall#42ff96ed flags:# video:flags.0?true user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
|
||||||
phone.acceptCall#3bd2b4a0 peer:InputPhoneCall g_b:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
|
phone.acceptCall#3bd2b4a0 peer:InputPhoneCall g_b:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
|
||||||
|
|||||||
@ -368,6 +368,9 @@
|
|||||||
"payments.getStarsStatus",
|
"payments.getStarsStatus",
|
||||||
"payments.getStarsTransactions",
|
"payments.getStarsTransactions",
|
||||||
"payments.getStarsTransactionsByID",
|
"payments.getStarsTransactionsByID",
|
||||||
|
"payments.getStarsSubscriptions",
|
||||||
|
"payments.changeStarsSubscription",
|
||||||
|
"payments.fulfillStarsSubscription",
|
||||||
"payments.sendStarsForm",
|
"payments.sendStarsForm",
|
||||||
"payments.getStarsGiftOptions",
|
"payments.getStarsGiftOptions",
|
||||||
"payments.getStarsGiveawayOptions",
|
"payments.getStarsGiveawayOptions",
|
||||||
|
|||||||
@ -33,8 +33,12 @@
|
|||||||
mask-image: linear-gradient(to right, transparent, black $borderStart, black $borderEnd, transparent);
|
mask-image: linear-gradient(to right, transparent, black $borderStart, black $borderEnd, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin gradient-border-left($indent) {
|
@mixin gradient-border-left($indent, $cutout: 0px) {
|
||||||
mask-image: linear-gradient(to right, transparent, black $indent);
|
mask-image: linear-gradient(to right, transparent $cutout, black $indent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin gradient-border-right($indent, $cutout: 0px) {
|
||||||
|
mask-image: linear-gradient(to left, transparent $cutout, black $indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin gradient-border-top-bottom($top, $bottom) {
|
@mixin gradient-border-top-bottom($top, $bottom) {
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import type {
|
|||||||
ApiChatInviteImporter,
|
ApiChatInviteImporter,
|
||||||
ApiDocument,
|
ApiDocument,
|
||||||
ApiExportedInvite,
|
ApiExportedInvite,
|
||||||
|
ApiFakeType,
|
||||||
ApiLanguage,
|
ApiLanguage,
|
||||||
ApiMessage,
|
ApiMessage,
|
||||||
ApiPhoto,
|
ApiPhoto,
|
||||||
@ -517,19 +518,25 @@ export type InlineBotSettings = {
|
|||||||
export type CustomPeerType = 'premium' | 'toBeDistributed' | 'contacts' | 'nonContacts'
|
export type CustomPeerType = 'premium' | 'toBeDistributed' | 'contacts' | 'nonContacts'
|
||||||
| 'groups' | 'channels' | 'bots' | 'excludeMuted' | 'excludeArchived' | 'excludeRead' | 'stars';
|
| 'groups' | 'channels' | 'bots' | 'excludeMuted' | 'excludeArchived' | 'excludeRead' | 'stars';
|
||||||
|
|
||||||
export interface CustomPeer {
|
export type CustomPeer = {
|
||||||
isCustomPeer: true;
|
isCustomPeer: true;
|
||||||
key?: string | number;
|
key?: string | number;
|
||||||
titleKey: string;
|
|
||||||
subtitleKey?: string;
|
subtitleKey?: string;
|
||||||
avatarIcon: IconName;
|
avatarIcon?: IconName;
|
||||||
isAvatarSquare?: boolean;
|
isAvatarSquare?: boolean;
|
||||||
titleValue?: number;
|
|
||||||
peerColorId?: number;
|
peerColorId?: number;
|
||||||
|
isVerified?: boolean;
|
||||||
|
fakeType?: ApiFakeType;
|
||||||
customPeerAvatarColor?: string;
|
customPeerAvatarColor?: string;
|
||||||
withPremiumGradient?: boolean;
|
withPremiumGradient?: boolean;
|
||||||
}
|
} & ({
|
||||||
|
titleKey: string;
|
||||||
|
title?: undefined;
|
||||||
|
} | {
|
||||||
|
title: string;
|
||||||
|
titleKey?: undefined;
|
||||||
|
});
|
||||||
|
|
||||||
export interface UniqueCustomPeer extends CustomPeer {
|
export type UniqueCustomPeer<T = CustomPeerType> = CustomPeer & {
|
||||||
type: CustomPeerType;
|
type: T;
|
||||||
}
|
};
|
||||||
|
|||||||
17
src/types/language.d.ts
vendored
17
src/types/language.d.ts
vendored
@ -1533,9 +1533,11 @@ export interface LangPair {
|
|||||||
'user': string | number;
|
'user': string | number;
|
||||||
'link': string | number;
|
'link': string | number;
|
||||||
};
|
};
|
||||||
'CreditsBoxOutAbout': {
|
'StarsTransactionTOS': {
|
||||||
'link': string | number;
|
'link': string | number;
|
||||||
};
|
};
|
||||||
|
'StarsTransactionTOSLinkText': undefined;
|
||||||
|
'StarsTransactionTOSLink': undefined;
|
||||||
'GiftStarsOutgoing': {
|
'GiftStarsOutgoing': {
|
||||||
'user': string | number;
|
'user': string | number;
|
||||||
};
|
};
|
||||||
@ -1549,10 +1551,23 @@ export interface LangPair {
|
|||||||
'StarsReactionLink': undefined;
|
'StarsReactionLink': undefined;
|
||||||
'MiniAppsMoreTabs': {
|
'MiniAppsMoreTabs': {
|
||||||
'botName': string | number;
|
'botName': string | number;
|
||||||
|
'count': string | number;
|
||||||
};
|
};
|
||||||
'PrizeCredits': {
|
'PrizeCredits': {
|
||||||
'count': string | number;
|
'count': string | number;
|
||||||
};
|
};
|
||||||
|
'StarsSubscribeText': {
|
||||||
|
'chat': string | number;
|
||||||
|
'amount': string | number;
|
||||||
|
};
|
||||||
|
'StarsSubscribeInfo': {
|
||||||
|
'link': string | number;
|
||||||
|
};
|
||||||
|
'StarsSubscribeInfoLinkText': undefined;
|
||||||
|
'StarsSubscribeInfoLink': undefined;
|
||||||
|
'StarsPerMonth': {
|
||||||
|
'amount': string | number;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -59,7 +59,7 @@ export const processDeepLink = (url: string): boolean => {
|
|||||||
const params = Object.fromEntries(searchParams);
|
const params = Object.fromEntries(searchParams);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
openChatByInvite,
|
checkChatInvite,
|
||||||
openChatByUsername,
|
openChatByUsername,
|
||||||
openChatByPhoneNumber,
|
openChatByPhoneNumber,
|
||||||
openStickerSet,
|
openStickerSet,
|
||||||
@ -139,7 +139,7 @@ export const processDeepLink = (url: string): boolean => {
|
|||||||
case 'join': {
|
case 'join': {
|
||||||
const { invite } = params;
|
const { invite } = params;
|
||||||
|
|
||||||
openChatByInvite({ hash: invite });
|
checkChatInvite({ hash: invite });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'addemoji':
|
case 'addemoji':
|
||||||
|
|||||||
@ -6,6 +6,7 @@ export enum Bundles {
|
|||||||
Main,
|
Main,
|
||||||
Extra,
|
Extra,
|
||||||
Calls,
|
Calls,
|
||||||
|
Stars,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImportedBundles {
|
interface ImportedBundles {
|
||||||
@ -13,6 +14,7 @@ interface ImportedBundles {
|
|||||||
[Bundles.Main]: typeof import('../bundles/main');
|
[Bundles.Main]: typeof import('../bundles/main');
|
||||||
[Bundles.Extra]: typeof import('../bundles/extra');
|
[Bundles.Extra]: typeof import('../bundles/extra');
|
||||||
[Bundles.Calls]: typeof import('../bundles/calls');
|
[Bundles.Calls]: typeof import('../bundles/calls');
|
||||||
|
[Bundles.Stars]: typeof import('../bundles/stars');
|
||||||
}
|
}
|
||||||
|
|
||||||
type BundlePromises = {
|
type BundlePromises = {
|
||||||
@ -46,6 +48,9 @@ export async function loadBundle<B extends Bundles>(bundleName: B) {
|
|||||||
case Bundles.Calls:
|
case Bundles.Calls:
|
||||||
LOAD_PROMISES[Bundles.Calls] = import(/* webpackChunkName: "BundleCalls" */ '../bundles/calls');
|
LOAD_PROMISES[Bundles.Calls] = import(/* webpackChunkName: "BundleCalls" */ '../bundles/calls');
|
||||||
break;
|
break;
|
||||||
|
case Bundles.Stars:
|
||||||
|
LOAD_PROMISES[Bundles.Stars] = import(/* webpackChunkName: "BundleStars" */ '../bundles/stars');
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
(LOAD_PROMISES[bundleName] as Promise<ImportedBundles[B]>).then(runCallbacks);
|
(LOAD_PROMISES[bundleName] as Promise<ImportedBundles[B]>).then(runCallbacks);
|
||||||
|
|||||||
@ -10,22 +10,6 @@ export const CUSTOM_PEER_PREMIUM: UniqueCustomPeer = {
|
|||||||
withPremiumGradient: true,
|
withPremiumGradient: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CUSTOM_PEER_TO_BE_DISTRIBUTED: UniqueCustomPeer = {
|
|
||||||
isCustomPeer: true,
|
|
||||||
type: 'toBeDistributed',
|
|
||||||
titleKey: 'BoostingToBeDistributed',
|
|
||||||
avatarIcon: 'user',
|
|
||||||
withPremiumGradient: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const CUSTOM_PEER_STAR: UniqueCustomPeer = {
|
|
||||||
isCustomPeer: true,
|
|
||||||
type: 'stars',
|
|
||||||
titleKey: 'Stars',
|
|
||||||
avatarIcon: 'star',
|
|
||||||
peerColorId: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const CUSTOM_PEER_INCLUDED_CHAT_TYPES: UniqueCustomPeer[] = [
|
export const CUSTOM_PEER_INCLUDED_CHAT_TYPES: UniqueCustomPeer[] = [
|
||||||
{
|
{
|
||||||
isCustomPeer: true,
|
isCustomPeer: true,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user