Update to layer 145: Introduce Emoji Statuses (#2062)

This commit is contained in:
Alexander Zinchuk 2022-10-17 17:35:03 +02:00
parent e433149da7
commit e01a878bba
58 changed files with 1006 additions and 372 deletions

View File

@ -33,6 +33,7 @@ interface GramJsAppConfig extends LimitsConfig {
premium_bot_username: string;
premium_invoice_slug: string;
premium_promo_order: string[];
default_emoji_statuses_stickerset_id: string;
}
function buildEmojiSounds(appConfig: GramJsAppConfig) {
@ -64,8 +65,8 @@ export function buildAppConfig(json: GramJs.TypeJSONValue): ApiAppConfig {
const appConfig = buildJson(json) as GramJsAppConfig;
return {
emojiSounds: buildEmojiSounds(appConfig),
defaultReaction: appConfig.reactions_default,
emojiSounds: buildEmojiSounds(appConfig),
seenByMaxChatMembers: appConfig.chat_read_mark_size_threshold,
seenByExpiresAt: appConfig.chat_read_mark_expire_period,
autologinDomains: appConfig.autologin_domains || [],
@ -75,6 +76,7 @@ export function buildAppConfig(json: GramJs.TypeJSONValue): ApiAppConfig {
premiumInvoiceSlug: appConfig.premium_invoice_slug,
premiumPromoOrder: appConfig.premium_promo_order,
isPremiumPurchaseBlocked: appConfig.premium_purchase_blocked,
defaultEmojiStatusesStickerSetId: appConfig.default_emoji_statuses_stickerset_id,
limits: {
uploadMaxFileparts: getLimit(appConfig, 'upload_max_fileparts', 'uploadMaxFileparts'),
stickersFaved: getLimit(appConfig, 'stickers_faved_limit', 'stickersFaved'),

View File

@ -18,6 +18,7 @@ import {
} from './peers';
import { omitVirtualClassFields } from './helpers';
import { getServerTime } from '../../../util/serverTime';
import { buildApiReaction } from './messages';
type PeerEntityApiChatFields = Omit<ApiChat, (
'id' | 'type' | 'title' |
@ -453,3 +454,16 @@ export function buildApiChatSettings({
canBlockContact: Boolean(blockContact),
};
}
export function buildApiChatReactions(availableReactions?: GramJs.TypeChatReactions): string[] | undefined {
if (availableReactions instanceof GramJs.ChatReactionsAll) {
// TODO Hack before custom reactions are implemented
// eslint-disable-next-line max-len
return ['👍', '👎', '❤', '🔥', '🥰', '👏', '😁', '🤔', '🤯', '😱', '🤬', '😢', '🎉', '🤩', '🤮', '💩', '🙏', '👌', '🕊', '🤡', '🥱', '🥴', '😍', '🐳', '❤‍🔥', '🌚', '🌭', '💯', '🤣', '⚡', '🍌', '🏆', '💔', '🤨', '😐', '🍓', '🍾', '💋', '🖕', '😈'];
}
if (availableReactions instanceof GramJs.ChatReactionsSome) {
return availableReactions.reactions.map(buildApiReaction).filter(Boolean);
}
return undefined;
}

View File

@ -213,34 +213,50 @@ export function buildMessageReactions(reactions: GramJs.MessageReactions): ApiRe
return {
canSeeList,
results: results.map(buildReactionCount),
recentReactions: recentReactions?.map(buildMessagePeerReaction),
results: results.map(buildReactionCount).filter(Boolean),
recentReactions: recentReactions?.map(buildMessagePeerReaction).filter(Boolean),
};
}
function buildReactionCount(reactionCount: GramJs.ReactionCount): ApiReactionCount {
const { chosen, count, reaction } = reactionCount;
function buildReactionCount(reactionCount: GramJs.ReactionCount): ApiReactionCount | undefined {
const { chosenOrder, count, reaction } = reactionCount;
// TODO: Add custom reactions support
const apiReaction = buildApiReaction(reaction);
if (!apiReaction) return undefined;
return {
isChosen: chosen,
isChosen: Boolean(chosenOrder), // TODO: Add custom reactions support
count,
reaction,
reaction: apiReaction,
};
}
export function buildMessagePeerReaction(userReaction: GramJs.MessagePeerReaction): ApiUserReaction {
export function buildMessagePeerReaction(userReaction: GramJs.MessagePeerReaction): ApiUserReaction | undefined {
const {
peerId, reaction, big, unread,
} = userReaction;
// TODO: Add custom reactions support
const apiReaction = buildApiReaction(reaction);
if (!apiReaction) return undefined;
return {
userId: getApiChatIdFromMtpPeer(peerId),
reaction,
reaction: apiReaction,
isUnread: unread,
isBig: big,
};
}
export function buildApiReaction(reaction: GramJs.TypeReaction): string | undefined {
if (reaction instanceof GramJs.ReactionEmoji) {
return reaction.emoticon;
}
// TODO: Add custom reactions support
return undefined;
}
export function buildApiAvailableReaction(availableReaction: GramJs.AvailableReaction): ApiAvailableReaction {
const {
selectAnimation, staticIcon, reaction, title,

View File

@ -1,6 +1,8 @@
import type { Api as GramJs } from '../../../lib/gramjs';
import type { ApiInvoice, ApiPaymentSavedInfo, ApiPremiumPromo } from '../../types';
import type {
ApiInvoice, ApiPaymentSavedInfo, ApiPremiumPromo, ApiPremiumSubscriptionOption,
} from '../../types';
import { buildApiDocument, buildApiMessageEntity, buildApiWebDocument } from './messages';
import { omitVirtualClassFields } from './helpers';
@ -156,15 +158,29 @@ export function buildApiInvoiceFromForm(form: GramJs.payments.PaymentForm): ApiI
export function buildApiPremiumPromo(promo: GramJs.help.PremiumPromo): ApiPremiumPromo {
const {
statusText, statusEntities, videos, videoSections, currency, monthlyAmount,
statusText, statusEntities, videos, videoSections, periodOptions,
} = promo;
return {
statusText,
statusEntities: statusEntities.map((l) => buildApiMessageEntity(l)),
videoSections,
currency,
videos: videos.map(buildApiDocument).filter(Boolean),
monthlyAmount: monthlyAmount.toString(),
options: periodOptions.map(buildApiPremiumSubscriptionOption),
};
}
function buildApiPremiumSubscriptionOption(option: GramJs.PremiumSubscriptionOption): ApiPremiumSubscriptionOption {
const {
current, canPurchaseUpgrade, currency, amount, botUrl, months,
} = option;
return {
isCurrent: current,
canPurchaseUpgrade,
currency,
amount: amount.toString(),
botUrl,
months,
};
}

View File

@ -1,5 +1,6 @@
import { Api as GramJs } from '../../../lib/gramjs';
import type {
ApiEmojiStatus,
ApiPremiumGiftOption,
ApiUser, ApiUserStatus, ApiUserType,
} from '../../types';
@ -66,6 +67,7 @@ export function buildApiUser(mtpUser: GramJs.TypeUser): ApiUser | undefined {
noStatus: !mtpUser.status,
...(mtpUser.accessHash && { accessHash: String(mtpUser.accessHash) }),
...(avatarHash && { avatarHash }),
...(mtpUser.emojiStatus && { emojiStatus: buildApiUserEmojiStatus(mtpUser.emojiStatus) }),
hasVideoAvatar,
...(mtpUser.bot && mtpUser.botInlinePlaceholder && { botPlaceholder: mtpUser.botInlinePlaceholder }),
...(mtpUser.bot && mtpUser.botAttachMenu && { isAttachBot: mtpUser.botAttachMenu }),
@ -99,6 +101,18 @@ export function buildApiUserStatus(mtpStatus?: GramJs.TypeUserStatus): ApiUserSt
}
}
export function buildApiUserEmojiStatus(mtpEmojiStatus: GramJs.TypeEmojiStatus): ApiEmojiStatus | undefined {
if (mtpEmojiStatus instanceof GramJs.EmojiStatus) {
return { documentId: mtpEmojiStatus.documentId.toString() };
}
if (mtpEmojiStatus instanceof GramJs.EmojiStatusUntil) {
return { documentId: mtpEmojiStatus.documentId.toString(), until: mtpEmojiStatus.until };
}
return undefined;
}
export function buildApiUsersAndStatuses(mtpUsers: GramJs.TypeUser[]) {
const userStatusesById: Record<string, ApiUserStatus> = {};
const users: ApiUser[] = [];

View File

@ -546,3 +546,16 @@ export function buildInputInvoice(invoice: ApiRequestInputInvoice) {
});
}
}
export function buildInputReaction(reaction?: string) {
if (!reaction) return new GramJs.ReactionEmpty();
return new GramJs.ReactionEmoji({
emoticon: reaction,
});
}
export function buildInputChatReactions(chatReactions: string[]) {
return new GramJs.ChatReactionsSome({
reactions: chatReactions.map(buildInputReaction),
});
}

View File

@ -28,6 +28,7 @@ import {
buildApiChatFolderFromSuggested,
buildApiChatBotCommands,
buildApiChatSettings,
buildApiChatReactions,
} from '../apiBuilders/chats';
import { buildApiMessage, buildMessageDraft } from '../apiBuilders/messages';
import { buildApiUser, buildApiUsersAndStatuses } from '../apiBuilders/users';
@ -40,6 +41,7 @@ import {
isMessageWithMedia,
buildChatBannedRights,
buildChatAdminRights,
buildInputChatReactions,
} from '../gramjsBuilders';
import { addEntitiesWithPhotosToLocalDb, addMessageToLocalDb, addPhotoToLocalDb } from '../helpers';
import { buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
@ -391,7 +393,7 @@ async function getFullChatInfo(chatId: string): Promise<FullChatData | undefined
inviteLink: exportedInvite.link,
}),
groupCallId: call?.id.toString(),
enabledReactions: availableReactions,
enabledReactions: buildApiChatReactions(availableReactions),
requestsPending,
recentRequesterIds: recentRequesters?.map((userId) => buildApiPeerId(userId, 'user')),
},
@ -508,7 +510,7 @@ async function getFullChannelInfo(
groupCallId: call ? String(call.id) : undefined,
linkedChatId: linkedChatId ? buildApiPeerId(linkedChatId, 'chat') : undefined,
botCommands,
enabledReactions: availableReactions,
enabledReactions: buildApiChatReactions(availableReactions),
sendAsId: defaultSendAs ? getApiChatIdFromMtpPeer(defaultSendAs) : undefined,
requestsPending,
recentRequesterIds: recentRequesters?.map((userId) => buildApiPeerId(userId, 'user')),
@ -1259,7 +1261,7 @@ export function setChatEnabledReactions({
}) {
return invokeRequest(new GramJs.messages.SetChatAvailableReactions({
peer: buildInputPeer(chat.id, chat.accessHash),
availableReactions: enabledReactions,
availableReactions: buildInputChatReactions(enabledReactions),
}), true);
}

View File

@ -1317,7 +1317,7 @@ export async function fetchSendAs({
return {
users,
chats,
ids: result.peers.map(getApiChatIdFromMtpPeer),
ids: result.peers.map((sendAsPeer) => getApiChatIdFromMtpPeer(sendAsPeer.peer)),
};
}

View File

@ -1,7 +1,7 @@
import type { ApiChat, ApiUser } from '../../types';
import type { ApiChat } from '../../types';
import { invokeRequest } from './client';
import { Api as GramJs } from '../../../lib/gramjs';
import { buildInputPeer } from '../gramjsBuilders';
import { buildInputPeer, buildInputReaction } from '../gramjsBuilders';
import localDb from '../localDb';
import { buildApiAvailableReaction, buildMessagePeerReaction } from '../apiBuilders/messages';
import { REACTION_LIST_LIMIT } from '../../../config';
@ -79,7 +79,7 @@ export function sendReaction({
chat: ApiChat; messageId: number; reaction?: string;
}) {
return invokeRequest(new GramJs.messages.SendReaction({
...(reaction && { reaction }),
...(reaction && { reaction: [buildInputReaction(reaction)] }),
peer: buildInputPeer(chat.id, chat.accessHash),
msgId: messageId,
}), true);
@ -104,7 +104,7 @@ export async function fetchMessageReactionsList({
const result = await invokeRequest(new GramJs.messages.GetMessageReactionsList({
peer: buildInputPeer(chat.id, chat.accessHash),
id: messageId,
...(reaction && { reaction }),
...(reaction && { reaction: buildInputReaction(reaction) }),
limit: REACTION_LIST_LIMIT,
...(offset && { offset }),
}));
@ -118,9 +118,9 @@ export async function fetchMessageReactionsList({
const { nextOffset, reactions, count } = result;
return {
users: result.users.map(buildApiUser).filter<ApiUser>(Boolean as any),
users: result.users.map(buildApiUser).filter(Boolean),
nextOffset,
reactions: reactions.map(buildMessagePeerReaction),
reactions: reactions.map(buildMessagePeerReaction).filter(Boolean),
count,
};
}
@ -131,6 +131,6 @@ export function setDefaultReaction({
reaction: string;
}) {
return invokeRequest(new GramJs.messages.SetDefaultReaction({
reaction,
reaction: buildInputReaction(reaction),
}));
}

View File

@ -24,7 +24,7 @@ import {
buildApiChatFolder,
buildApiChatSettings,
} from './apiBuilders/chats';
import { buildApiUser, buildApiUserStatus } from './apiBuilders/users';
import { buildApiUser, buildApiUserEmojiStatus, buildApiUserStatus } from './apiBuilders/users';
import {
buildMessageFromUpdate,
isMessageWithMedia,
@ -738,6 +738,14 @@ export function updater(update: Update, originRequest?: GramJs.AnyRequest) {
userId: buildApiPeerId(update.userId, 'user'),
status: buildApiUserStatus(update.status),
});
} else if (update instanceof GramJs.UpdateUserEmojiStatus) {
const emojiStatus = buildApiUserEmojiStatus(update.emojiStatus);
if (!emojiStatus) return;
onUpdate({
'@type': 'updateUserEmojiStatus',
userId: buildApiPeerId(update.userId, 'user'),
emojiStatus,
});
} else if (update instanceof GramJs.UpdateUserName) {
const apiUserId = buildApiPeerId(update.userId, 'user');
const updatedUser = localDb.users[apiUserId];

View File

@ -160,8 +160,8 @@ export interface ApiCountryCode extends ApiCountry {
}
export interface ApiAppConfig {
emojiSounds: Record<string, string>;
defaultReaction: string;
emojiSounds: Record<string, string>;
seenByMaxChatMembers: number;
seenByExpiresAt: number;
autologinDomains: string[];
@ -171,6 +171,7 @@ export interface ApiAppConfig {
premiumBotUsername: string;
isPremiumPurchaseBlocked: boolean;
premiumPromoOrder: string[];
defaultEmojiStatusesStickerSetId: string;
limits: Record<ApiLimitType, readonly [number, number]>;
}

View File

@ -65,10 +65,18 @@ export interface ApiReceipt {
}
export interface ApiPremiumPromo {
currency: string;
monthlyAmount: string;
videoSections: string[];
videos: ApiDocument[];
statusText: string;
statusEntities: ApiMessageEntity[];
options: ApiPremiumSubscriptionOption[];
}
export interface ApiPremiumSubscriptionOption {
isCurrent?: boolean;
canPurchaseUpgrade?: boolean;
months: number;
currency: string;
amount: string;
botUrl: string;
}

View File

@ -15,7 +15,9 @@ import type {
import type {
ApiFormattedText, ApiMessage, ApiPhoto, ApiPoll, ApiReactions, ApiStickerSet, ApiThreadInfo,
} from './messages';
import type { ApiUser, ApiUserFullInfo, ApiUserStatus } from './users';
import type {
ApiEmojiStatus, ApiUser, ApiUserFullInfo, ApiUserStatus,
} from './users';
import type {
ApiEmojiInteraction, ApiError, ApiInviteInfo, ApiNotifyException, ApiSessionData,
} from './misc';
@ -324,6 +326,12 @@ export type ApiUpdateUserStatus = {
status: ApiUserStatus;
};
export type ApiUpdateUserEmojiStatus = {
'@type': 'updateUserEmojiStatus';
userId: string;
emojiStatus: ApiEmojiStatus;
};
export type ApiUpdateUserFullInfo = {
'@type': 'updateUserFullInfo';
id: string;
@ -548,7 +556,7 @@ export type ApiUpdate = (
ApiUpdateGroupCallConnectionState | ApiUpdateGroupCallLeavePresentation | ApiUpdateGroupCallChatId |
ApiUpdatePendingJoinRequests | ApiUpdatePaymentVerificationNeeded | ApiUpdatePaymentStateCompleted |
ApiUpdatePhoneCall | ApiUpdatePhoneCallSignalingData | ApiUpdatePhoneCallMediaState |
ApiUpdatePhoneCallConnectionState | ApiUpdateBotMenuButton | ApiUpdateTranscribedAudio
ApiUpdatePhoneCallConnectionState | ApiUpdateBotMenuButton | ApiUpdateTranscribedAudio | ApiUpdateUserEmojiStatus
);
export type OnApiUpdate = (update: ApiUpdate) => void;

View File

@ -28,6 +28,7 @@ export interface ApiUser {
};
fakeType?: ApiFakeType;
isAttachBot?: boolean;
emojiStatus?: ApiEmojiStatus;
// Obtained from GetFullUser / UserFullInfo
fullInfo?: ApiUserFullInfo;
@ -79,3 +80,8 @@ export interface ApiPremiumGiftOption {
amount: number;
botUrl: string;
}
export interface ApiEmojiStatus {
documentId: string;
until?: number;
}

View File

@ -8,8 +8,10 @@ import React, {
import { fastRaf } from '../../util/schedulers';
import buildClassName from '../../util/buildClassName';
import buildStyle from '../../util/buildStyle';
import useHeavyAnimationCheck from '../../hooks/useHeavyAnimationCheck';
import useBackgroundMode from '../../hooks/useBackgroundMode';
import useOnChange from '../../hooks/useOnChange';
export type OwnProps = {
ref?: RefObject<HTMLDivElement>;
@ -28,6 +30,7 @@ export type OwnProps = {
onClick?: NoneToVoidFunction;
onLoad?: NoneToVoidFunction;
onEnded?: NoneToVoidFunction;
onLoop?: NoneToVoidFunction;
};
type RLottieClass = typeof import('../../lib/rlottie/RLottie').default;
@ -66,6 +69,7 @@ const AnimatedSticker: FC<OwnProps> = ({
onClick,
onLoad,
onEnded,
onLoop,
}) => {
// eslint-disable-next-line no-null/no-null
let containerRef = useRef<HTMLDivElement>(null);
@ -102,9 +106,10 @@ const AnimatedSticker: FC<OwnProps> = ({
quality,
isLowPriority,
},
onLoad,
color,
onLoad,
onEnded,
onLoop,
);
if (speed) {
@ -125,7 +130,7 @@ const AnimatedSticker: FC<OwnProps> = ({
});
});
}
}, [color, animation, tgsUrl, isLowPriority, noLoop, onLoad, quality, size, speed, onEnded]);
}, [color, animation, tgsUrl, isLowPriority, noLoop, onLoad, quality, size, speed, onEnded, onLoop]);
useEffect(() => {
if (!animation) return;
@ -186,6 +191,12 @@ const AnimatedSticker: FC<OwnProps> = ({
fastRaf(unfreezeAnimation);
}, [unfreezeAnimation]);
useOnChange(([prevNoLoop]) => {
if (noLoop !== undefined && noLoop !== prevNoLoop) {
animation?.setNoLoop(noLoop);
}
}, [noLoop, animation]);
useEffect(() => {
if (!animation) {
return;

View File

@ -0,0 +1,36 @@
.root {
display: inline-block;
vertical-align: text-bottom;
width: var(--custom-emoji-size);
height: var(--custom-emoji-size);
&.with-grid-fix .media {
width: calc(100% + 1px) !important;
height: calc(100% + 1px) !important;
vertical-align: baseline;
}
:global(.emoji-small) {
vertical-align: baseline !important; // Fix for fallback on Windows, when custom emoji not ready
}
&:global(.custom-color) {
--emoji-status-color: var(--color-primary);
}
}
.media {
width: 100%;
height: 100%;
}
.sticker {
width: var(--custom-emoji-size) !important;
height: var(--custom-emoji-size) !important;
display: flex !important;
:global(canvas) {
width: var(--custom-emoji-size) !important;
height: var(--custom-emoji-size) !important;
}
}

View File

@ -1,6 +1,7 @@
import React, {
memo, useEffect, useMemo, useRef,
memo, useCallback, useEffect, useMemo, useRef, useState,
} from '../../lib/teact/teact';
import { getGlobal } from '../../global';
import type { FC, TeactNode } from '../../lib/teact/teact';
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
@ -8,6 +9,10 @@ import type { ObserveFn } from '../../hooks/useIntersectionObserver';
import { IS_WEBM_SUPPORTED } from '../../util/environment';
import renderText from './helpers/renderText';
import safePlay from '../../util/safePlay';
import { getPropertyHexColor } from '../../util/themeStyle';
import { hexToRgb } from '../../util/switchTheme';
import buildClassName from '../../util/buildClassName';
import { selectIsAlwaysHighPriorityEmoji, selectIsDefaultEmojiStatusPack } from '../../global/selectors';
import useMedia from '../../hooks/useMedia';
import useEnsureCustomEmoji from '../../hooks/useEnsureCustomEmoji';
@ -17,26 +22,56 @@ import useCustomEmoji from './hooks/useCustomEmoji';
import AnimatedSticker from './AnimatedSticker';
import styles from './CustomEmoji.module.scss';
type OwnProps = {
documentId: string;
children?: TeactNode;
className?: string;
loopLimit?: number;
withGridFix?: boolean;
observeIntersection?: ObserveFn;
onClick?: NoneToVoidFunction;
};
const STICKER_SIZE = 24;
const CustomEmojiInner: FC<OwnProps> = ({
const CustomEmoji: FC<OwnProps> = ({
documentId,
children,
className,
loopLimit,
withGridFix,
observeIntersection,
onClick,
}) => {
// eslint-disable-next-line no-null/no-null
const ref = useRef<HTMLDivElement>(null);
// An alternative to `withGlobal` to avoid adding numerous global containers
const customEmoji = useCustomEmoji(documentId);
const mediaHash = customEmoji && `sticker${customEmoji.id}`;
const isUnsupportedVideo = customEmoji?.isVideo && !IS_WEBM_SUPPORTED;
const mediaHash = customEmoji && `sticker${customEmoji.id}${isUnsupportedVideo ? '?size=m' : ''}`;
const mediaData = useMedia(mediaHash);
const thumbDataUri = useThumbnail(customEmoji);
const loopCountRef = useRef(0);
const [shouldLoop, setShouldLoop] = useState(true);
const [customColor, setCustomColor] = useState<[number, number, number] | undefined>();
const hasCustomColor = customEmoji && selectIsDefaultEmojiStatusPack(getGlobal(), customEmoji.stickerSetInfo);
useEffect(() => {
if (!hasCustomColor || !ref.current) {
setCustomColor(undefined);
return;
}
const hexColor = getPropertyHexColor(getComputedStyle(ref.current), '--emoji-status-color');
if (!hexColor) {
setCustomColor(undefined);
return;
}
const customColorRgb = hexToRgb(hexColor);
setCustomColor([customColorRgb.r, customColorRgb.g, customColorRgb.b]);
}, [hasCustomColor]);
const isIntersecting = useIsIntersecting(ref, observeIntersection);
@ -54,46 +89,99 @@ const CustomEmojiInner: FC<OwnProps> = ({
}
}, [customEmoji, isIntersecting]);
useEffect(() => {
if (!loopLimit) return undefined;
const video = ref.current?.querySelector('video');
if (!mediaData || !video) return undefined;
const returnToStart = () => {
loopCountRef.current += 1;
if (loopCountRef.current >= loopLimit) {
setShouldLoop(false);
video.currentTime = 0;
} else {
video.play();
}
};
video.addEventListener('ended', returnToStart);
return () => video.removeEventListener('ended', returnToStart);
}, [loopLimit, mediaData]);
const handleStickerLoop = useCallback(() => {
if (!loopLimit) return;
loopCountRef.current += 1;
// Sticker plays 1 more time after disabling loop
if (loopCountRef.current >= loopLimit - 1) {
setShouldLoop(false);
}
}, [loopLimit]);
const content = useMemo(() => {
if (!customEmoji || (!thumbDataUri && !mediaData)) {
return (children && renderText(children, ['emoji']));
}
if (!mediaData || (customEmoji.isVideo && !IS_WEBM_SUPPORTED)) {
if (!mediaData) {
return (
<img src={thumbDataUri} alt={customEmoji.emoji} />
<img className={styles.media} src={thumbDataUri} alt={customEmoji.emoji} />
);
}
if (!customEmoji.isVideo && !customEmoji.isLottie) {
if (isUnsupportedVideo || (!customEmoji.isVideo && !customEmoji.isLottie)) {
return (
<img src={mediaData} alt={customEmoji.emoji} />
<img className={styles.media} src={mediaData} alt={customEmoji.emoji} />
);
}
if (customEmoji.isVideo) {
return (
<video
className={styles.media}
playsInline
muted
loop={!loopLimit}
autoPlay={isIntersecting}
loop
src={mediaData}
disablePictureInPicture
/>
);
}
return (
<AnimatedSticker
key={mediaData}
className={styles.sticker}
size={STICKER_SIZE}
tgsUrl={mediaData}
play={isIntersecting}
isLowPriority
color={customColor}
noLoop={!shouldLoop}
isLowPriority={!selectIsAlwaysHighPriorityEmoji(getGlobal(), customEmoji.stickerSetInfo)}
onLoop={handleStickerLoop}
/>
);
}, [children, customEmoji, isIntersecting, mediaData, thumbDataUri]);
}, [
children, customColor, customEmoji, handleStickerLoop, isIntersecting, loopLimit, mediaData, shouldLoop,
thumbDataUri,
]);
return (
<div ref={ref} className="text-entity-custom-emoji emoji">
<div
ref={ref}
className={buildClassName(
styles.root,
className,
'custom-emoji',
'emoji',
hasCustomColor && 'custom-color',
withGridFix && styles.withGridFix,
)}
onClick={onClick}
>
{content}
</div>
);
};
export default memo(CustomEmojiInner);
export default memo(CustomEmoji);

View File

@ -104,7 +104,7 @@
vertical-align: text-bottom !important;
}
.text-entity-custom-emoji {
.custom-emoji {
// Custom emoji needs to be slightly bigger than normal emoji
--custom-emoji-size: calc(1.125 * var(--message-text-size, 1rem) + 1px);
margin-inline-end: 1px;

View File

@ -16,7 +16,11 @@ const FakeIcon: FC<OwnProps> = ({
}) => {
const lang = useLang();
return <span className="FakeIcon">{lang(fakeType === 'fake' ? 'FakeMessage' : 'ScamMessage')}</span>;
return (
<span className="FakeIcon">
{lang(fakeType === 'fake' ? 'FakeMessage' : 'ScamMessage')}
</span>
);
};
export default memo(FakeIcon);

View File

@ -0,0 +1,10 @@
.root {
display: flex;
align-items: center;
gap: 0.25rem;
--custom-emoji-size: 1.25rem;
> h3 {
margin-bottom: 0;
}
}

View File

@ -0,0 +1,76 @@
import React, { memo } from '../../lib/teact/teact';
import type { ApiChat, ApiUser } from '../../api/types';
import type { FC } from '../../lib/teact/teact';
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
import { EMOJI_STATUS_LOOP_LIMIT } from '../../config';
import renderText from './helpers/renderText';
import { getChatTitle, getUserFullName, isUserId } from '../../global/helpers';
import buildClassName from '../../util/buildClassName';
import useLang from '../../hooks/useLang';
import VerifiedIcon from './VerifiedIcon';
import FakeIcon from './FakeIcon';
import CustomEmoji from './CustomEmoji';
import PremiumIcon from './PremiumIcon';
import styles from './FullNameTitle.module.scss';
type OwnProps = {
peer: ApiChat | ApiUser;
className?: string;
noVerified?: boolean;
noFake?: boolean;
withEmojiStatus?: boolean;
isSavedMessages?: boolean;
noLoopLimit?: boolean;
onEmojiStatusClick?: NoneToVoidFunction;
observeIntersection?: ObserveFn;
};
const FullNameTitle: FC<OwnProps> = ({
className,
peer,
noVerified,
noFake,
withEmojiStatus,
isSavedMessages,
noLoopLimit,
onEmojiStatusClick,
observeIntersection,
}) => {
const lang = useLang();
const isUser = isUserId(peer.id);
const title = isUser ? getUserFullName(peer as ApiUser) : getChatTitle(lang, peer as ApiChat);
const emojiStatus = isUser && (peer as ApiUser).emojiStatus;
const isPremium = isUser && (peer as ApiUser).isPremium;
if (isSavedMessages) {
return (
<div className={buildClassName('title', styles.root, className)}>
<h3>{lang('SavedMessages')}</h3>
</div>
);
}
return (
<div className={buildClassName('title', styles.root, className)}>
<h3 dir="auto" className="fullName">{renderText(title)}</h3>
{!noVerified && peer.isVerified && <VerifiedIcon />}
{!noFake && peer.fakeType && <FakeIcon fakeType={peer.fakeType} />}
{withEmojiStatus && emojiStatus && (
<CustomEmoji
documentId={emojiStatus.documentId}
loopLimit={!noLoopLimit ? EMOJI_STATUS_LOOP_LIMIT : undefined}
observeIntersection={observeIntersection}
onClick={onEmojiStatusClick}
/>
)}
{withEmojiStatus && !emojiStatus && isPremium && <PremiumIcon />}
</div>
);
};
export default memo(FullNameTitle);

View File

@ -10,19 +10,16 @@ import { MediaViewerOrigin } from '../../types';
import {
getChatTypeString,
getChatTitle,
isChatSuperGroup,
} from '../../global/helpers';
import { selectChat, selectChatMessages, selectChatOnlineCount } from '../../global/selectors';
import renderText from './helpers/renderText';
import type { LangFn } from '../../hooks/useLang';
import useLang from '../../hooks/useLang';
import Avatar from './Avatar';
import VerifiedIcon from './VerifiedIcon';
import TypingStatus from './TypingStatus';
import DotAnimation from './DotAnimation';
import FakeIcon from './FakeIcon';
import FullNameTitle from './FullNameTitle';
type OwnProps = {
chatId: string;
@ -152,11 +149,7 @@ const GroupChatInfo: FC<OwnProps & StateProps> = ({
animationLevel={animationLevel}
/>
<div className="info">
<div className="title">
<h3 dir="auto">{renderText(getChatTitle(lang, chat))}</h3>
{chat.isVerified && <VerifiedIcon />}
{chat.fakeType && <FakeIcon fakeType={chat.fakeType} />}
</div>
<FullNameTitle peer={chat} />
{renderStatusOrTyping()}
</div>
</div>

View File

@ -9,16 +9,13 @@ import type { AnimationLevel } from '../../types';
import { MediaViewerOrigin } from '../../types';
import { selectChatMessages, selectUser, selectUserStatus } from '../../global/selectors';
import { getUserFullName, getUserStatus, isUserOnline } from '../../global/helpers';
import renderText from './helpers/renderText';
import { getUserStatus, isUserOnline } from '../../global/helpers';
import useLang from '../../hooks/useLang';
import Avatar from './Avatar';
import VerifiedIcon from './VerifiedIcon';
import TypingStatus from './TypingStatus';
import DotAnimation from './DotAnimation';
import FakeIcon from './FakeIcon';
import PremiumIcon from './PremiumIcon';
import FullNameTitle from './FullNameTitle';
type OwnProps = {
userId: string;
@ -74,7 +71,6 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
} = getActions();
const { id: userId } = user || {};
const fullName = getUserFullName(user);
useEffect(() => {
if (userId && lastSyncTime) {
@ -143,18 +139,7 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
animationLevel={animationLevel}
/>
<div className="info">
{isSavedMessages ? (
<div className="title">
<h3>{lang('SavedMessages')}</h3>
</div>
) : (
<div className="title">
<h3 dir="auto">{fullName && renderText(fullName)}</h3>
{user?.isVerified && <VerifiedIcon />}
{user.isPremium && <PremiumIcon />}
{user.fakeType && <FakeIcon fakeType={user.fakeType} />}
</div>
)}
<FullNameTitle peer={user} withEmojiStatus isSavedMessages={isSavedMessages} />
{(status || (!isSavedMessages && !noStatusOrTyping)) && renderStatusOrTyping()}
</div>
</div>

View File

@ -119,9 +119,6 @@
}
.title {
display: flex;
align-items: center;
.fullName {
font-weight: 500;
font-size: 1.25rem;
@ -132,18 +129,28 @@
}
.VerifiedIcon, .PremiumIcon {
margin-left: 0.25rem;
z-index: 2;
--color-fill: var(--color-white);
--color-checkmark: var(--color-primary);
opacity: 0.8;
}
.emoji {
.emoji:not(.custom-emoji) {
width: 1.5rem;
height: 1.5rem;
background-size: 1.5rem;
}
.custom-emoji {
pointer-events: auto;
cursor: pointer;
--custom-emoji-size: 1.5rem;
&.custom-color {
--emoji-status-color: var(--color-white);
}
}
}
.status {

View File

@ -13,20 +13,18 @@ import { IS_TOUCH_ENV } from '../../util/environment';
import { MEMO_EMPTY_ARRAY } from '../../util/memo';
import { selectChat, selectUser, selectUserStatus } from '../../global/selectors';
import {
getUserFullName, getUserStatus, isChatChannel, isUserOnline,
getUserStatus, isChatChannel, isUserOnline,
} from '../../global/helpers';
import renderText from './helpers/renderText';
import { captureEvents, SwipeDirection } from '../../util/captureEvents';
import buildClassName from '../../util/buildClassName';
import usePhotosPreload from './hooks/usePhotosPreload';
import useLang from '../../hooks/useLang';
import usePrevious from '../../hooks/usePrevious';
import VerifiedIcon from './VerifiedIcon';
import FullNameTitle from './FullNameTitle';
import ProfilePhoto from './ProfilePhoto';
import Transition from '../ui/Transition';
import FakeIcon from './FakeIcon';
import PremiumIcon from './PremiumIcon';
import './ProfileInfo.scss';
@ -70,7 +68,6 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
const { id: userId } = user || {};
const { id: chatId } = chat || {};
const fullName = user ? getUserFullName(user) : (chat ? chat.title : '');
const photos = user?.photos || chat?.photos || MEMO_EMPTY_ARRAY;
const prevMediaId = usePrevious(mediaId);
const prevAvatarOwnerId = usePrevious(avatarOwnerId);
@ -196,7 +193,7 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
function renderStatus() {
if (user) {
return (
<div className={`status ${isUserOnline(user, userStatus) ? 'online' : ''}`}>
<div className={buildClassName('status', isUserOnline(user, userStatus) && 'online')}>
<span className="user-status" dir="auto">{getUserStatus(lang, user, userStatus, serverTimeOffset)}</span>
</div>
);
@ -212,10 +209,6 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
);
}
const isVerifiedIconShown = (user || chat)?.isVerified;
const isPremiumIconShown = user?.isPremium;
const fakeType = (user || chat)?.fakeType;
return (
<div className={buildClassName('ProfileInfo', forceShowSelf && 'self')} dir={lang.isRtl ? 'rtl' : undefined}>
<div className="photo-wrapper">
@ -243,17 +236,14 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
</div>
<div className="info" dir={lang.isRtl ? 'rtl' : 'auto'}>
{isSavedMessages ? (
<div className="title">
<div className="fullName" dir="auto">{lang('SavedMessages')}</div>
</div>
) : (
<div className="title">
<div className="fullName" dir="auto">{fullName && renderText(fullName)}</div>
{isVerifiedIconShown && <VerifiedIcon />}
{isPremiumIconShown && <PremiumIcon onClick={handleClickPremium} />}
{fakeType && <FakeIcon fakeType={fakeType} />}
</div>
{(user || chat) && (
<FullNameTitle
peer={(user || chat)!}
withEmojiStatus
isSavedMessages={isSavedMessages}
onEmojiStatusClick={handleClickPremium}
noLoopLimit
/>
)}
{!isSavedMessages && renderStatus()}
</div>

View File

@ -309,7 +309,7 @@ function processEntity(
if (entity.type === ApiMessageEntityTypes.CustomEmoji) {
return (
<CustomEmoji documentId={entity.documentId} observeIntersection={observeIntersection}>
<CustomEmoji documentId={entity.documentId} observeIntersection={observeIntersection} withGridFix>
{renderNestedMessagePart()}
</CustomEmoji>
);
@ -419,7 +419,7 @@ function processEntity(
return <Spoiler messageId={messageId}>{renderNestedMessagePart()}</Spoiler>;
case ApiMessageEntityTypes.CustomEmoji:
return (
<CustomEmoji documentId={entity.documentId} observeIntersection={observeIntersection}>
<CustomEmoji documentId={entity.documentId} observeIntersection={observeIntersection} withGridFix>
{renderNestedMessagePart()}
</CustomEmoji>
);

View File

@ -48,6 +48,10 @@
&.selected:hover {
--background-color: var(--color-chat-active) !important;
.custom-emoji.custom-color {
--emoji-status-color: var(--color-white);
}
.VerifiedIcon, .PremiumIcon {
--color-fill: #fff;
--color-checkmark: var(--color-primary)
@ -95,15 +99,10 @@
margin-top: -0.125rem;
}
h3 {
width: auto;
max-width: 80%;
}
.icon-muted {
font-size: 1.25rem;
margin-left: 0.25rem;
margin-top: -0.0625rem;
margin-left: 0.25rem;
color: #c6c8ca;
body.is-ios & {
@ -113,8 +112,6 @@
}
.LastMessageMeta {
margin-left: auto;
body.is-ios & {
font-size: 0.875rem;
margin-right: 0;
@ -177,7 +174,7 @@
vertical-align: -0.125rem;
}
.text-entity-custom-emoji {
.custom-emoji {
--custom-emoji-size: 1.25rem;
}

View File

@ -15,7 +15,6 @@ import { MAIN_THREAD_ID } from '../../../api/types';
import { ANIMATION_END_DELAY } from '../../../config';
import { IS_SINGLE_COLUMN_LAYOUT } from '../../../util/environment';
import {
getChatTitle,
isUserId,
isActionMessage,
getPrivateChatUserId,
@ -31,7 +30,7 @@ import {
} from '../../../global/helpers';
import {
selectChat, selectUser, selectChatMessage, selectOutgoingStatus, selectDraft, selectCurrentMessageList,
selectNotifySettings, selectNotifyExceptions, selectUserStatus,
selectNotifySettings, selectNotifyExceptions, selectUserStatus, selectIsDefaultEmojiStatusPack,
} from '../../../global/selectors';
import { renderActionMessageText } from '../../common/helpers/renderActionMessageText';
import renderText from '../../common/helpers/renderText';
@ -47,7 +46,6 @@ import { ChatAnimationTypes } from './hooks';
import useLang from '../../../hooks/useLang';
import Avatar from '../../common/Avatar';
import VerifiedIcon from '../../common/VerifiedIcon';
import TypingStatus from '../../common/TypingStatus';
import LastMessageMeta from '../../common/LastMessageMeta';
import DeleteChatModal from '../../common/DeleteChatModal';
@ -56,8 +54,7 @@ import Badge from './Badge';
import ChatFolderModal from '../ChatFolderModal.async';
import ChatCallStatus from './ChatCallStatus';
import ReportModal from '../../common/ReportModal';
import FakeIcon from '../../common/FakeIcon';
import PremiumIcon from '../../common/PremiumIcon';
import FullNameTitle from '../../common/FullNameTitle';
import './Chat.scss';
@ -77,6 +74,7 @@ type StateProps = {
isMuted?: boolean;
user?: ApiUser;
userStatus?: ApiUserStatus;
isEmojiStatusColored?: boolean;
actionTargetUserIds?: string[];
actionTargetMessage?: ApiMessage;
actionTargetChatId?: string;
@ -104,6 +102,7 @@ const Chat: FC<OwnProps & StateProps> = ({
isMuted,
user,
userStatus,
isEmojiStatusColored,
actionTargetUserIds,
lastMessageSender,
lastMessageOutgoingStatus,
@ -325,12 +324,16 @@ const Chat: FC<OwnProps & StateProps> = ({
)}
</div>
<div className="info">
<div className="title">
<h3>{renderText(getChatTitle(lang, chat, user))}</h3>
{chat.isVerified && <VerifiedIcon />}
{user?.isPremium && !user.isSelf && <PremiumIcon />}
{chat.fakeType && <FakeIcon fakeType={chat.fakeType} />}
<div className="info-row">
<FullNameTitle
peer={user || chat}
withEmojiStatus
isSavedMessages={chatId === user?.id && user?.isSelf}
observeIntersection={observeIntersection}
key={!IS_SINGLE_COLUMN_LAYOUT && isEmojiStatusColored ? `${isSelected}` : undefined}
/>
{isMuted && <i className="icon-muted" />}
<div className="separator" />
{chat.lastMessage && (
<LastMessageMeta
message={chat.lastMessage}
@ -410,6 +413,11 @@ export default memo(withGlobal<OwnProps>(
} = selectCurrentMessageList(global) || {};
const isSelected = chatId === currentChatId && currentThreadId === MAIN_THREAD_ID;
const user = privateChatUserId ? selectUser(global, privateChatUserId) : undefined;
const userStatus = privateChatUserId ? selectUserStatus(global, privateChatUserId) : undefined;
const statusEmoji = user?.emojiStatus && global.customEmojis.byId[user.emojiStatus.documentId];
const isEmojiStatusColored = statusEmoji && selectIsDefaultEmojiStatusPack(global, statusEmoji.stickerSetInfo);
return {
chat,
isMuted: selectIsChatMuted(chat, selectNotifySettings(global), selectNotifyExceptions(global)),
@ -426,10 +434,9 @@ export default memo(withGlobal<OwnProps>(
...(isOutgoing && chat.lastMessage && {
lastMessageOutgoingStatus: selectOutgoingStatus(global, chat.lastMessage),
}),
...(privateChatUserId && {
user: selectUser(global, privateChatUserId),
userStatus: selectUserStatus(global, privateChatUserId),
}),
user,
userStatus,
isEmojiStatusColored,
};
},
)(Chat));

View File

@ -9,7 +9,6 @@ import type { AnimationLevel } from '../../../types';
import { IS_SINGLE_COLUMN_LAYOUT } from '../../../util/environment';
import {
getChatTitle,
getPrivateChatUserId,
getMessageMediaHash,
getMessageMediaThumbDataUri,
@ -19,7 +18,6 @@ import {
} from '../../../global/helpers';
import { selectChat, selectUser } from '../../../global/selectors';
import buildClassName from '../../../util/buildClassName';
import renderText from '../../common/helpers/renderText';
import { formatPastTimeShort } from '../../../util/dateFormat';
import { renderMessageSummary } from '../../common/helpers/renderMessageText';
@ -29,11 +27,9 @@ import useLang from '../../../hooks/useLang';
import useSelectWithEnter from '../../../hooks/useSelectWithEnter';
import Avatar from '../../common/Avatar';
import VerifiedIcon from '../../common/VerifiedIcon';
import ListItem from '../../ui/ListItem';
import Link from '../../ui/Link';
import FakeIcon from '../../common/FakeIcon';
import PremiumIcon from '../../common/PremiumIcon';
import FullNameTitle from '../../common/FullNameTitle';
import './ChatMessage.scss';
@ -95,12 +91,11 @@ const ChatMessage: FC<OwnProps & StateProps> = ({
/>
<div className="info">
<div className="info-row">
<div className="title">
<h3 dir="auto">{renderText(getChatTitle(lang, chat, privateChatUser))}</h3>
{chat.isVerified && <VerifiedIcon />}
{privateChatUser?.isPremium && <PremiumIcon />}
{chat.fakeType && <FakeIcon fakeType={chat.fakeType} />}
</div>
<FullNameTitle
peer={privateChatUser || chat}
withEmojiStatus
isSavedMessages={chatId === privateChatUser?.id && privateChatUser?.isSelf}
/>
<div className="message-date">
<Link className="date">
{formatPastTimeShort(lang, message.date * 1000)}

View File

@ -17,12 +17,10 @@
}
.title {
text-align: center;
margin-bottom: 0.25rem;
justify-content: center;
}
.note,
.date {
.note {
color: var(--color-text-secondary);
font-size: 0.875rem;
text-align: center;

View File

@ -5,7 +5,6 @@ import type { FC } from '../../../lib/teact/teact';
import type { ApiUser, ApiWebSession } from '../../../api/types';
import type { AnimationLevel } from '../../../types';
import { getUserFullName } from '../../../global/helpers';
import buildClassName from '../../../util/buildClassName';
import useLang from '../../../hooks/useLang';
@ -14,6 +13,7 @@ import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
import Modal from '../../ui/Modal';
import Button from '../../ui/Button';
import Avatar from '../../common/Avatar';
import FullNameTitle from '../../common/FullNameTitle';
import styles from './SettingsActiveWebsite.module.scss';
@ -77,8 +77,8 @@ const SettingsActiveWebsite: FC<OwnProps & StateProps> = ({
className={styles.root}
>
<Avatar className={styles.avatar} user={renderingBot} size="large" animationLevel={animationLevel} withVideo />
<h3 className={styles.title} dir="auto">{getUserFullName(renderingBot)}</h3>
<div className={styles.date} aria-label={lang('PrivacySettings.LastSeen')}>
{renderingBot && <FullNameTitle className={styles.title} peer={renderingBot} />}
<div className={styles.note}>
{renderingSession?.domain}
</div>

View File

@ -8,7 +8,6 @@ import type { ApiWebSession } from '../../../api/types';
import type { AnimationLevel } from '../../../types';
import { formatPastTimeShort } from '../../../util/dateFormat';
import { getUserFullName } from '../../../global/helpers';
import buildClassName from '../../../util/buildClassName';
import useFlag from '../../../hooks/useFlag';
@ -19,6 +18,7 @@ import ListItem from '../../ui/ListItem';
import ConfirmDialog from '../../ui/ConfirmDialog';
import SettingsActiveWebsite from './SettingsActiveWebsite';
import Avatar from '../../common/Avatar';
import FullNameTitle from '../../common/FullNameTitle';
import styles from './SettingsActiveWebsites.module.scss';
@ -116,7 +116,7 @@ const SettingsActiveWebsites: FC<OwnProps & StateProps> = ({
<Avatar className={styles.avatar} user={bot} size="tiny" animationLevel={animationLevel} withVideo />
<div className="multiline-menu-item full-size" dir="auto">
<span className="date">{formatPastTimeShort(lang, session.dateActive * 1000)}</span>
<span className="title">{getUserFullName(bot)}</span>
{bot && <FullNameTitle className={styles.title} peer={bot} />}
<span className={buildClassName('subtitle', 'black', 'tight', styles.platform)}>
{session.domain}, {session.browser}, {session.platform}
</span>

View File

@ -7,9 +7,8 @@ import type { ApiChat, ApiCountryCode, ApiUser } from '../../../api/types';
import { CHAT_HEIGHT_PX } from '../../../config';
import { formatPhoneNumberWithCode } from '../../../util/phoneNumber';
import {
getChatTitle, getUserFullName, isUserId,
isUserId,
} from '../../../global/helpers';
import renderText from '../../common/helpers/renderText';
import buildClassName from '../../../util/buildClassName';
import useLang from '../../../hooks/useLang';
import useHistoryBack from '../../../hooks/useHistoryBack';
@ -20,6 +19,7 @@ import FloatingActionButton from '../../ui/FloatingActionButton';
import Avatar from '../../common/Avatar';
import Loading from '../../ui/Loading';
import BlockUserModal from './BlockUserModal';
import FullNameTitle from '../../common/FullNameTitle';
type OwnProps = {
isActive?: boolean;
@ -58,6 +58,7 @@ const SettingsPrivacyBlockedUsers: FC<OwnProps & StateProps> = ({
const isPrivate = isUserId(contactId);
const user = isPrivate ? usersByIds[contactId] : undefined;
const chat = !isPrivate ? chatsByIds[contactId] : undefined;
const userOrChat = user || chat;
const className = buildClassName(
'Chat chat-item-clickable blocked-list-item small-icon',
@ -81,7 +82,7 @@ const SettingsPrivacyBlockedUsers: FC<OwnProps & StateProps> = ({
>
<Avatar size="medium" user={user} chat={chat} />
<div className="contact-info" dir="auto">
<h3 dir="auto">{renderText((isPrivate ? getUserFullName(user) : getChatTitle(lang, chat!)) || '')}</h3>
{userOrChat && <FullNameTitle peer={userOrChat} />}
{user?.phoneNumber && (
<div className="contact-phone" dir="auto">{formatPhoneNumberWithCode(phoneCodeList, user.phoneNumber)}</div>
)}

View File

@ -197,6 +197,9 @@ const PremiumFeatureModal: FC<OwnProps> = ({
stopScrolling();
}, [startScrolling, stopScrolling]);
// TODO Support all subscription options
const month = promo.options.find((option) => option.months === 1)!;
return (
<div className={styles.root}>
<Button
@ -316,7 +319,7 @@ const PremiumFeatureModal: FC<OwnProps> = ({
>
{isPremium
? lang('OK')
: lang('SubscribeToPremium', formatCurrency(Number(promo.monthlyAmount), promo.currency, lang.code))}
: lang('SubscribeToPremium', formatCurrency(Number(month.amount), month.currency, lang.code))}
</Button>
</div>
</div>

View File

@ -178,6 +178,9 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
if (!promo) return undefined;
// TODO Support all subscription options
const month = promo.options.find((option) => option.months === 1)!;
function getHeaderText() {
if (isGift) {
return fromUser?.id === currentUserId
@ -284,7 +287,7 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
<div className={styles.footer}>
{/* eslint-disable-next-line react/jsx-no-bind */}
<Button className={styles.button} isShiny withPremiumGradient onClick={handleClick}>
{lang('SubscribeToPremium', formatCurrency(Number(promo.monthlyAmount), promo.currency, lang.code))}
{lang('SubscribeToPremium', formatCurrency(Number(month.amount), month.currency, lang.code))}
</Button>
</div>
)}

View File

@ -67,13 +67,13 @@
max-height: 2.75rem;
}
.emoji:not(.text-entity-custom-emoji) {
.emoji:not(.custom-emoji) {
width: 0.9375rem;
height: 0.9375rem;
vertical-align: -2px;
}
.text-entity-custom-emoji {
.custom-emoji {
--custom-emoji-size: 1.25rem;
}

View File

@ -293,9 +293,12 @@
}
.VerifiedIcon, .PremiumIcon {
margin-left: 0.25rem;
margin-top: 0.0625rem;
}
.custom-emoji {
--custom-emoji-size: 1.375rem;
}
}
.status,
@ -521,7 +524,7 @@
font-size: 0.9375rem;
}
.text-entity-custom-emoji {
.custom-emoji {
--custom-emoji-size: 1.125rem;
}
}

View File

@ -11,8 +11,6 @@ import { LoadMoreDirection } from '../../types';
import useLang from '../../hooks/useLang';
import { selectChatMessage } from '../../global/selectors';
import useInfiniteScroll from '../../hooks/useInfiniteScroll';
import { getUserFullName } from '../../global/helpers';
import renderText from '../common/helpers/renderText';
import useFlag from '../../hooks/useFlag';
import buildClassName from '../../util/buildClassName';
import { formatIntegerCompact } from '../../util/textFormat';
@ -25,7 +23,7 @@ import Avatar from '../common/Avatar';
import ListItem from '../ui/ListItem';
import ReactionStaticEmoji from '../common/ReactionStaticEmoji';
import Loading from '../ui/Loading';
import PremiumIcon from '../common/PremiumIcon';
import FullNameTitle from '../common/FullNameTitle';
import './ReactorListModal.scss';
@ -163,7 +161,6 @@ const ReactorListModal: FC<OwnProps & StateProps> = ({
{viewportIds?.map(
(userId) => {
const user = usersById[userId];
const fullName = getUserFullName(user);
const reaction = reactors?.reactions.find((l) => l.userId === userId)?.reaction;
return (
<ListItem
@ -173,10 +170,7 @@ const ReactorListModal: FC<OwnProps & StateProps> = ({
onClick={() => handleClick(userId)}
>
<Avatar user={user} size="small" animationLevel={animationLevel} withVideo />
<div className="title">
<h3 dir="auto">{fullName && renderText(fullName)}</h3>
{user.isPremium && <PremiumIcon />}
</div>
<FullNameTitle peer={user} withEmojiStatus />
{reaction && <ReactionStaticEmoji className="reactors-list-emoji" reaction={reaction} />}
</ListItem>
);

View File

@ -6,8 +6,7 @@ import React, {
import setTooltipItemVisible from '../../../util/setTooltipItemVisible';
import { useKeyboardNavigation } from './hooks/useKeyboardNavigation';
import { IS_TOUCH_ENV } from '../../../util/environment';
import renderText from '../../common/helpers/renderText';
import { getUserFullName, isUserId } from '../../../global/helpers';
import { isUserId } from '../../../global/helpers';
import useMouseInside from '../../../hooks/useMouseInside';
import useLang from '../../../hooks/useLang';
import buildClassName from '../../../util/buildClassName';
@ -16,6 +15,7 @@ import { getActions, getGlobal } from '../../../global';
import ListItem from '../../ui/ListItem';
import Avatar from '../../common/Avatar';
import Menu from '../../ui/Menu';
import FullNameTitle from '../../common/FullNameTitle';
import './SendAsMenu.scss';
@ -93,7 +93,7 @@ const SendAsMenu: FC<OwnProps> = ({
{usersById && chatsById && sendAsIds?.map((id, index) => {
const user = isUserId(id) ? usersById[id] : undefined;
const chat = !user ? chatsById[id] : undefined;
const fullName = user ? getUserFullName(user) : chat?.title;
const userOrChat = user || chat;
return (
<ListItem
@ -110,9 +110,7 @@ const SendAsMenu: FC<OwnProps> = ({
className={buildClassName(selectedSendAsId === id && 'selected')}
/>
<div className="info">
<div className="title">
<h3 dir="auto">{fullName && renderText(fullName)}</h3>
</div>
{userOrChat && <FullNameTitle peer={userOrChat} noFake />}
<span className="subtitle">{user
? lang('VoipGroupPersonalAccount')
: lang('Subscribers', chat?.membersCount, 'i')}

View File

@ -47,8 +47,7 @@
}
> .Avatar,
> .message-content-wrapper,
> .chat-avatar-premium {
> .message-content-wrapper {
opacity: 1;
transform: scale(1) translateX(0);
transition: transform var(--select-transition);
@ -71,24 +70,6 @@
}
}
& > .chat-avatar-premium {
position: absolute;
bottom: -0.1875rem;
left: 1.25rem;
height: 0.75rem;
z-index: 1;
& > svg {
position: absolute;
}
--color-fill: white;
@media (max-width: 600px) {
left: 1.5rem;
}
}
.quick-reaction {
cursor: pointer;
position: absolute;
@ -298,8 +279,7 @@
&:not(.own) {
> .Avatar,
> .message-content-wrapper,
> .chat-avatar-premium {
> .message-content-wrapper {
transform: translateX(2.5rem);
}
}

View File

@ -26,6 +26,7 @@ import {
} from '../../../types';
import { IS_ANDROID, IS_TOUCH_ENV } from '../../../util/environment';
import { EMOJI_STATUS_LOOP_LIMIT } from '../../../config';
import {
selectChat,
selectChatMessage,
@ -118,8 +119,10 @@ import CommentButton from './CommentButton';
import Reactions from './Reactions';
import ReactionStaticEmoji from '../../common/ReactionStaticEmoji';
import MessagePhoneCall from './MessagePhoneCall';
import PremiumIcon from '../../common/PremiumIcon';
import DotAnimation from '../../common/DotAnimation';
import CustomEmoji from '../../common/CustomEmoji';
import PremiumIcon from '../../common/PremiumIcon';
import FakeIcon from '../../common/FakeIcon';
import './Message.scss';
@ -609,23 +612,19 @@ const Message: FC<OwnProps & StateProps> = ({
const avatarUser = (avatarPeer && isAvatarPeerUser) ? avatarPeer as ApiUser : undefined;
const avatarChat = (avatarPeer && !isAvatarPeerUser) ? avatarPeer as ApiChat : undefined;
const hiddenName = (!avatarPeer && forwardInfo) ? forwardInfo.hiddenUserName : undefined;
const isAvatarPremium = avatarUser?.isPremium;
return (
<>
<Avatar
size="small"
user={avatarUser}
chat={avatarChat}
text={hiddenName}
lastSyncTime={lastSyncTime}
onClick={(avatarUser || avatarChat) ? handleAvatarClick : undefined}
observeIntersection={observeIntersectionForMedia}
animationLevel={animationLevel}
withVideo
/>
{isAvatarPremium && <PremiumIcon className="chat-avatar-premium" />}
</>
<Avatar
size="small"
user={avatarUser}
chat={avatarChat}
text={hiddenName}
lastSyncTime={lastSyncTime}
onClick={(avatarUser || avatarChat) ? handleAvatarClick : undefined}
observeIntersection={observeIntersectionForMedia}
animationLevel={animationLevel}
withVideo
/>
);
}
@ -904,16 +903,27 @@ const Message: FC<OwnProps & StateProps> = ({
} else if (forwardInfo?.hiddenUserName) {
senderTitle = forwardInfo.hiddenUserName;
}
const senderEmojiStatus = senderPeer && 'emojiStatus' in senderPeer && senderPeer.emojiStatus;
const senderIsPremium = senderPeer && 'isPremium' in senderPeer && senderPeer.isPremium;
return (
<div className="message-title" dir="ltr">
{senderTitle ? (
<span
className={buildClassName('interactive', senderColor)}
className={buildClassName('message-title-name interactive', senderColor)}
onClick={handleSenderClick}
dir="auto"
>
{renderText(senderTitle)}
{!asForwarded && senderEmojiStatus && (
<CustomEmoji
documentId={senderEmojiStatus.documentId}
loopLimit={EMOJI_STATUS_LOOP_LIMIT}
observeIntersection={observeIntersectionForAnimatedStickers}
/>
)}
{!asForwarded && !senderEmojiStatus && senderIsPremium && <PremiumIcon />}
{senderPeer?.fakeType && <FakeIcon fakeType={senderPeer.fakeType} />}
</span>
) : !botSender ? (
NBSP

View File

@ -218,6 +218,10 @@
unicode-bidi: plaintext;
display: flex;
.message-title-name {
display: flex;
}
& > .interactive {
overflow: hidden;
text-overflow: ellipsis;
@ -235,6 +239,14 @@
@for $i from 1 through 8 {
& > .color-#{$i} {
color: var(--color-user-#{$i});
.custom-emoji.custom-color {
--emoji-status-color: var(--color-user-#{$i});
}
.PremiumIcon {
--color-fill: var(--color-user-#{$i});
}
}
}
@ -255,6 +267,21 @@
padding-left: 0.25rem;
}
.custom-emoji {
--custom-emoji-size: 1.25rem;
margin-left: 0.25rem;
&.custom-color {
opacity: 0.5;
}
}
.PremiumIcon {
vertical-align: middle;
opacity: 0.5;
margin-left: 0.25rem;
}
.admin-title {
flex: 1;
margin-left: 1rem;
@ -392,7 +419,7 @@
}
}
&:not(.custom-shape) .emoji {
&:not(.custom-shape) .emoji:not(.custom-emoji) {
display: inline-block;
width: 1.25rem;
background-size: 1.25rem;
@ -413,7 +440,7 @@
background-size: calc(1.25 * var(--message-text-size, 1rem));
}
.text-entity-custom-emoji {
.custom-emoji {
--custom-emoji-size: calc(1.25 * var(--message-text-size, 1rem));
}
}
@ -833,31 +860,6 @@
}
}
.text-entity-custom-emoji {
display: inline-block;
vertical-align: text-bottom;
--custom-emoji-size: 1.5rem;
width: var(--custom-emoji-size);
height: var(--custom-emoji-size);
& > video, & > img {
width: calc(100% + 1px) !important;
height: calc(100% + 1px) !important;
vertical-align: baseline;
}
& > .AnimatedSticker {
width: var(--custom-emoji-size) !important;
height: var(--custom-emoji-size) !important;
display: flex !important;
& > canvas {
width: var(--custom-emoji-size) !important;
height: var(--custom-emoji-size) !important;
}
}
}
.text-entity-code {
color: var(--color-code);
background: var(--color-code-bg);

View File

@ -62,10 +62,6 @@
word-break: break-word;
}
.VerifiedIcon, .PremiumIcon {
margin-left: 0.25rem;
}
.emoji {
width: 1.5rem;
height: 1.5rem;

View File

@ -13,8 +13,6 @@ import {
selectCurrentTextSearch,
} from '../../global/selectors';
import {
getChatTitle,
getUserFullName,
isChatChannel,
} from '../../global/helpers';
import useLang from '../../hooks/useLang';
@ -22,12 +20,12 @@ import useKeyboardListNavigation from '../../hooks/useKeyboardListNavigation';
import useHistoryBack from '../../hooks/useHistoryBack';
import useInfiniteScroll from '../../hooks/useInfiniteScroll';
import { renderMessageSummary } from '../common/helpers/renderMessageText';
import renderText from '../common/helpers/renderText';
import InfiniteScroll from '../ui/InfiniteScroll';
import ListItem from '../ui/ListItem';
import LastMessageMeta from '../common/LastMessageMeta';
import Avatar from '../common/Avatar';
import FullNameTitle from '../common/FullNameTitle';
import './RightSearch.scss';
@ -122,7 +120,6 @@ const RightSearch: FC<OwnProps & StateProps> = ({
senderChat?: ApiChat;
onClick: NoneToVoidFunction;
}) => {
const title = senderChat ? getChatTitle(lang, senderChat) : getUserFullName(senderUser);
const text = renderMessageSummary(lang, message, undefined, query);
return (
@ -134,8 +131,8 @@ const RightSearch: FC<OwnProps & StateProps> = ({
>
<Avatar chat={senderChat} user={senderUser} animationLevel={animationLevel} withVideo />
<div className="info">
<div className="title">
<h3 dir="auto">{title && renderText(title)}</h3>
<div className="search-result-message-top">
<FullNameTitle peer={(senderUser || senderChat)!} />
<LastMessageMeta message={message} />
</div>
<div className="subtitle" dir="auto">

View File

@ -173,6 +173,7 @@
overflow: hidden;
}
.info-row,
.title,
.subtitle {
overflow: hidden;
@ -181,6 +182,10 @@
align-items: center;
}
.separator {
flex-grow: 1;
}
h3,
.last-message,
.status,
@ -199,15 +204,6 @@
font-weight: 500;
}
.VerifiedIcon {
margin-left: 0.5rem;
}
.PremiumIcon,
.VerifiedIcon {
margin-left: 0.25rem;
}
.emoji {
vertical-align: text-bottom;
background-position: 0 0;
@ -289,9 +285,14 @@
&.search-result-message {
.title {
flex-grow: 1;
padding-right: 0.125rem;
}
.search-result-message-top {
display: flex;
}
h3 {
max-width: 80%;
}
@ -307,11 +308,6 @@
display: block;
}
.LastMessageMeta {
margin-left: auto;
margin-right: 0;
}
.subtitle {
color: var(--color-text-secondary);

View File

@ -150,6 +150,7 @@ export const STICKER_SIZE_INLINE_BOT_RESULT = 100;
export const STICKER_SIZE_JOIN_REQUESTS = 140;
export const STICKER_SIZE_INVITES = 140;
export const RECENT_STICKERS_LIMIT = 20;
export const EMOJI_STATUS_LOOP_LIMIT = 2;
export const RECENT_SYMBOL_SET_ID = 'recent';
export const FAVORITE_SYMBOL_SET_ID = 'favorite';
export const CHAT_STICKER_SET_ID = 'chatStickers';
@ -206,6 +207,7 @@ export const API_CHAT_TYPES = ['bots', 'channels', 'chats', 'users'] as const;
// MTProto constants
export const SERVICE_NOTIFICATIONS_USER_ID = '777000';
export const REPLIES_USER_ID = '1271266957'; // TODO For Test connection ID must be equal to 708513
export const RESTRICTED_EMOJI_SET_ID = '7173162320003080';
export const ALL_FOLDER_ID = 0;
export const ARCHIVED_FOLDER_ID = 1;
export const DELETED_COMMENTS_CHANNEL_ID = '-777';

View File

@ -411,13 +411,16 @@ addActionHandler('openGiftPremiumModal', async (global, actions, payload) => {
global = getGlobal();
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
// TODO Support all subscription options
const month = result.promo.options.find((option) => option.months === 1)!;
setGlobal({
...global,
giftPremiumModal: {
isOpen: true,
forUserId,
monthlyCurrency: result.promo.currency,
monthlyAmount: result.promo.monthlyAmount,
monthlyCurrency: month.currency,
monthlyAmount: month.amount,
},
});
});

View File

@ -42,6 +42,10 @@ addActionHandler('apiUpdate', (global, actions, update) => {
return updateUser(global, update.id, update.user);
}
case 'updateUserEmojiStatus': {
return updateUser(global, update.userId, { emojiStatus: update.emojiStatus });
}
case 'updateUserStatus': {
// Status updates come very often so we throttle them
scheduleStatusUpdate(update.userId, update.status);

View File

@ -1,4 +1,6 @@
import type { ApiMessage, ApiReactions } from '../../api/types';
import type {
ApiMessage, ApiReactions,
} from '../../api/types';
import type { GlobalState } from '../types';
export function getMessageRecentReaction(message: Partial<ApiMessage>) {

View File

@ -1,6 +1,7 @@
import type { GlobalState } from '../types';
import type { ApiStickerSetInfo, ApiSticker, ApiStickerSet } from '../../api/types';
import { RESTRICTED_EMOJI_SET_ID } from '../../config';
import { selectIsCurrentUserPremium } from './users';
export function selectIsStickerFavorite(global: GlobalState, sticker: ApiSticker) {
@ -117,3 +118,11 @@ export function selectLocalAnimatedEmojiEffect(emoji: string) {
export function selectLocalAnimatedEmojiEffectByName(name: string) {
return name === 'Cumshot' ? '🍆' : undefined;
}
export function selectIsDefaultEmojiStatusPack(global: GlobalState, pack: ApiStickerSetInfo) {
return 'id' in pack && pack.id === global.appConfig?.defaultEmojiStatusesStickerSetId;
}
export function selectIsAlwaysHighPriorityEmoji(global: GlobalState, pack: ApiStickerSetInfo) {
return selectIsDefaultEmojiStatusPack(global, pack) || ('id' in pack && pack.id === RESTRICTED_EMOJI_SET_ID);
}

View File

@ -1,6 +1,6 @@
const api = require('./api');
const LAYER = 144;
const LAYER = 145;
const tlobjects = {};
for (const tl of Object.values(api)) {

File diff suppressed because one or more lines are too long

View File

@ -64,7 +64,7 @@ storage.fileMov#4b09ebbc = storage.FileType;
storage.fileMp4#b3cea0e4 = storage.FileType;
storage.fileWebp#1081464c = storage.FileType;
userEmpty#d3bc4b7a id:long = User;
user#3ff6ecb0 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
user#5d99adee flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus = User;
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
userProfilePhoto#82d1f706 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto;
userStatusEmpty#9d05049 = UserStatus;
@ -78,8 +78,8 @@ chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5
chatForbidden#6592a1a7 id:long title:string = Chat;
channel#8261ac61 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#d18ee226 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?Vector<string> = ChatFull;
channelFull#ea68a619 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags.31?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?Vector<string> = ChatFull;
chatFull#c9d31138 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions = ChatFull;
channelFull#f2355507 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags.31?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions = ChatFull;
chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant;
chatParticipantAdmin#a0933f5b user_id:long inviter_id:long date:int = ChatParticipant;
@ -241,7 +241,7 @@ updateChannelMessageViews#f226ac08 channel_id:long id:int views:int = Update;
updateChatParticipantAdmin#d7ca61a2 chat_id:long user_id:long is_admin:Bool version:int = Update;
updateNewStickerSet#688a30aa stickerset:messages.StickerSet = Update;
updateStickerSetsOrder#bb2d201 flags:# masks:flags.0?true emojis:flags.1?true order:Vector<long> = Update;
updateStickerSets#43ae3dec = Update;
updateStickerSets#31c24808 flags:# masks:flags.0?true emojis:flags.1?true = Update;
updateSavedGifs#9375341e = Update;
updateBotInlineQuery#496f379c flags:# query_id:long user_id:long query:string geo:flags.0?GeoPoint peer_type:flags.1?InlineQueryPeerType offset:string = Update;
updateBotInlineSend#12f12a07 flags:# user_id:long query:string geo:flags.0?GeoPoint id:string msg_id:flags.1?InputBotInlineMessageID = Update;
@ -310,6 +310,10 @@ updateBotMenuButton#14b85813 bot_id:long button:BotMenuButton = Update;
updateSavedRingtones#74d8be99 = Update;
updateTranscribedAudio#84cd5a flags:# pending:flags.0?true peer:Peer msg_id:int transcription_id:long text:string = Update;
updateReadFeaturedEmojiStickers#fb4c496c = Update;
updateUserEmojiStatus#28373599 user_id:long emoji_status:EmojiStatus = Update;
updateRecentEmojiStatuses#30f443db = Update;
updateRecentReactions#6f7863f4 = Update;
updateMoveStickerSetToTop#86fccf85 flags:# masks:flags.0?true emojis:flags.1?true stickerset:long = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
updates.differenceEmpty#5d75a138 date:int seq:int = updates.Difference;
updates.difference#f49ca0 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> state:updates.State = updates.Difference;
@ -328,7 +332,7 @@ photos.photo#20212ca8 photo:Photo users:Vector<User> = photos.Photo;
upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File;
upload.fileCdnRedirect#f18cda44 dc_id:int file_token:bytes encryption_key:bytes encryption_iv:bytes file_hashes:Vector<FileHash> = upload.File;
dcOption#18b7a10d flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true this_port_only:flags.5?true id:int ip_address:string port:int secret:flags.10?bytes = DcOption;
config#330b4067 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true pfs_enabled:flags.13?true force_try_ipv6:flags.14?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int pinned_infolder_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int = Config;
config#232566ac flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true pfs_enabled:flags.13?true force_try_ipv6:flags.14?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int pinned_infolder_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int reactions_default:flags.15?Reaction = Config;
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
help.appUpdate#ccbbce30 flags:# can_not_skip:flags.0?true id:int version:string text:string entities:Vector<MessageEntity> document:flags.1?Document url:flags.2?string sticker:flags.3?Document = help.AppUpdate;
help.noAppUpdate#c45a6536 = help.AppUpdate;
@ -435,7 +439,7 @@ webPage#e89c45b2 flags:# id:long url:string display_url:string hash:int type:fla
webPageNotModified#7311ca11 flags:# cached_page_views:flags.0?int = WebPage;
authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true encrypted_requests_disabled:flags.3?true call_requests_disabled:flags.4?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization;
account.authorizations#4bff8ea0 authorization_ttl_days:int authorizations:Vector<Authorization> = account.Authorizations;
account.password#185b184f flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes pending_reset_date:flags.5?int = account.Password;
account.password#957b50fb flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes pending_reset_date:flags.5?int login_email_pattern:flags.6?string = account.Password;
account.passwordSettings#9a5c33e5 flags:# email:flags.0?string secure_settings:flags.1?SecureSecretSettings = account.PasswordSettings;
account.passwordInputSettings#c23727c9 flags:# new_algo:flags.0?PasswordKdfAlgo new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_settings:flags.2?SecureSecretSettings = account.PasswordInputSettings;
auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery;
@ -452,6 +456,8 @@ inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet;
inputStickerSetDice#e67f520e emoticon:string = InputStickerSet;
inputStickerSetAnimatedEmojiAnimations#cde3739 = InputStickerSet;
inputStickerSetPremiumGifts#c88b3b02 = InputStickerSet;
inputStickerSetEmojiGenericAnimations#4c4d4ce = InputStickerSet;
inputStickerSetEmojiDefaultStatuses#29d0f5ee = InputStickerSet;
stickerSet#2dd14edc flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true videos:flags.6?true emojis:flags.7?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int thumb_document_id:flags.8?long count:int hash:int = StickerSet;
messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
messages.stickerSetNotModified#d3f924eb = messages.StickerSet;
@ -559,6 +565,8 @@ auth.sentCodeTypeSms#c000bba2 length:int = auth.SentCodeType;
auth.sentCodeTypeCall#5353e5a7 length:int = auth.SentCodeType;
auth.sentCodeTypeFlashCall#ab03c6d9 pattern:string = auth.SentCodeType;
auth.sentCodeTypeMissedCall#82006484 prefix:string length:int = auth.SentCodeType;
auth.sentCodeTypeEmailCode#5a159841 flags:# apple_signin_allowed:flags.0?true google_signin_allowed:flags.1?true email_pattern:string length:int next_phone_login_date:flags.2?int = auth.SentCodeType;
auth.sentCodeTypeSetUpEmailRequired#a5491dea flags:# apple_signin_allowed:flags.0?true google_signin_allowed:flags.1?true = auth.SentCodeType;
messages.botCallbackAnswer#36585ea4 flags:# alert:flags.1?true has_url:flags.3?true native_ui:flags.4?true message:flags.0?string url:flags.2?string cache_time:int = messages.BotCallbackAnswer;
messages.messageEditData#26b5dde6 flags:# caption:flags.0?true = messages.MessageEditData;
inputBotInlineMessageID#890c3d89 dc_id:int id:long access_hash:long = InputBotInlineMessageID;
@ -659,7 +667,7 @@ webDocumentNoProxy#f9c8bcc6 url:string size:int mime_type:string attributes:Vect
inputWebDocument#9bed434d url:string size:int mime_type:string attributes:Vector<DocumentAttribute> = InputWebDocument;
inputWebFileLocation#c239d686 url:string access_hash:long = InputWebFileLocation;
inputWebFileGeoPointLocation#9f2221c9 geo_point:InputGeoPoint access_hash:long w:int h:int zoom:int scale:int = InputWebFileLocation;
inputWebFileAudioAlbumThumbLocation#f46fe924 flags:# document:flags.0?InputDocument title:flags.1?string performer:flags.1?string = InputWebFileLocation;
inputWebFileAudioAlbumThumbLocation#f46fe924 flags:# small:flags.2?true document:flags.0?InputDocument title:flags.1?string performer:flags.1?string = InputWebFileLocation;
upload.webFile#21e753bc size:int mime_type:string file_type:storage.FileType mtime:int bytes:bytes = upload.WebFile;
payments.paymentForm#a0058751 flags:# can_save_credentials:flags.2?true password_missing:flags.3?true form_id:long bot_id:long title:string description:string photo:flags.5?WebDocument invoice:Invoice provider_id:long url:string native_provider:flags.4?string native_params:flags.4?DataJSON additional_methods:flags.6?Vector<PaymentFormMethod> saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?Vector<PaymentSavedCredentials> users:Vector<User> = payments.PaymentForm;
payments.validatedRequestedInfo#d1451883 flags:# id:flags.0?string shipping_options:flags.1?Vector<ShippingOption> = payments.ValidatedRequestedInfo;
@ -729,7 +737,7 @@ channelAdminLogEventActionChangeHistoryTTL#6e941a38 prev_value:int new_value:int
channelAdminLogEventActionParticipantJoinByRequest#afb6144a invite:ExportedChatInvite approved_by:long = ChannelAdminLogEventAction;
channelAdminLogEventActionToggleNoForwards#cb2ac766 new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionSendMessage#278f2868 message:Message = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeAvailableReactions#9cf7f76a prev_value:Vector<string> new_value:Vector<string> = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeAvailableReactions#be4e0ef8 prev_value:ChatReactions new_value:ChatReactions = ChannelAdminLogEventAction;
channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
channels.adminLogResults#ed8af74d events:Vector<ChannelAdminLogEvent> chats:Vector<Chat> users:Vector<User> = channels.AdminLogResults;
channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true = ChannelAdminLogEventsFilter;
@ -961,11 +969,11 @@ searchResultsCalendarPeriod#c9b0539f date:int min_msg_id:int max_msg_id:int coun
messages.searchResultsCalendar#147ee23c flags:# inexact:flags.0?true count:int min_date:int min_msg_id:int offset_id_offset:flags.1?int periods:Vector<SearchResultsCalendarPeriod> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SearchResultsCalendar;
searchResultPosition#7f648b67 msg_id:int date:int offset:int = SearchResultsPosition;
messages.searchResultsPositions#53b22baf count:int positions:Vector<SearchResultsPosition> = messages.SearchResultsPositions;
channels.sendAsPeers#8356cda9 peers:Vector<Peer> chats:Vector<Chat> users:Vector<User> = channels.SendAsPeers;
channels.sendAsPeers#f496b0c6 peers:Vector<SendAsPeer> chats:Vector<Chat> users:Vector<User> = channels.SendAsPeers;
users.userFull#3b6d152e full_user:UserFull chats:Vector<Chat> users:Vector<User> = users.UserFull;
messages.peerSettings#6880b94d settings:PeerSettings chats:Vector<Chat> users:Vector<User> = messages.PeerSettings;
auth.loggedOut#c3a2835f flags:# future_auth_token:flags.0?bytes = auth.LoggedOut;
reactionCount#6fb250d1 flags:# chosen:flags.0?true reaction:string count:int = ReactionCount;
reactionCount#a3d1cb80 flags:# chosen_order:flags.0?int reaction:Reaction count:int = ReactionCount;
messageReactions#4f2b9479 flags:# min:flags.0?true can_see_list:flags.2?true results:Vector<ReactionCount> recent_reactions:flags.1?Vector<MessagePeerReaction> = MessageReactions;
messages.messageReactionsList#31bd492d flags:# count:int reactions:Vector<MessagePeerReaction> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = messages.MessageReactionsList;
availableReaction#c077ec01 flags:# inactive:flags.0?true premium:flags.2?true reaction:string title:string static_icon:Document appear_animation:Document select_animation:Document activate_animation:Document effect_animation:Document around_animation:flags.1?Document center_icon:flags.1?Document = AvailableReaction;
@ -973,7 +981,7 @@ messages.availableReactionsNotModified#9f071957 = messages.AvailableReactions;
messages.availableReactions#768e3aad hash:int reactions:Vector<AvailableReaction> = messages.AvailableReactions;
messages.translateNoResult#67ca4737 = messages.TranslatedText;
messages.translateResultText#a214f7d0 text:string = messages.TranslatedText;
messagePeerReaction#51b67eff flags:# big:flags.0?true unread:flags.1?true peer_id:Peer reaction:string = MessagePeerReaction;
messagePeerReaction#b156fe9c flags:# big:flags.0?true unread:flags.1?true peer_id:Peer reaction:Reaction = MessagePeerReaction;
groupCallStreamChannel#80eb48af channel:int scale:int last_timestamp_ms:long = GroupCallStreamChannel;
phone.groupCallStreamChannels#d0e482b2 channels:Vector<GroupCallStreamChannel> = phone.GroupCallStreamChannels;
phone.groupCallStreamRtmpUrl#2dbf3432 url:string key:string = phone.GroupCallStreamRtmpUrl;
@ -1006,17 +1014,40 @@ inputInvoiceMessage#c5b56859 peer:InputPeer msg_id:int = InputInvoice;
inputInvoiceSlug#c326caef slug:string = InputInvoice;
payments.exportedInvoice#aed0cbd9 url:string = payments.ExportedInvoice;
messages.transcribedAudio#93752c52 flags:# pending:flags.0?true transcription_id:long text:string = messages.TranscribedAudio;
help.premiumPromo#8a4f3c29 status_text:string status_entities:Vector<MessageEntity> video_sections:Vector<string> videos:Vector<Document> currency:string monthly_amount:long users:Vector<User> = help.PremiumPromo;
help.premiumPromo#5334759c status_text:string status_entities:Vector<MessageEntity> video_sections:Vector<string> videos:Vector<Document> period_options:Vector<PremiumSubscriptionOption> users:Vector<User> = help.PremiumPromo;
inputStorePaymentPremiumSubscription#a6751e66 flags:# restore:flags.0?true = InputStorePaymentPurpose;
inputStorePaymentGiftPremium#616f7fe8 user_id:InputUser currency:string amount:long = InputStorePaymentPurpose;
premiumGiftOption#74c34319 flags:# months:int currency:string amount:long bot_url:string store_product:flags.0?string = PremiumGiftOption;
paymentFormMethod#88f8f21b url:string title:string = PaymentFormMethod;
emojiStatusEmpty#2de11aae = EmojiStatus;
emojiStatus#929b619d document_id:long = EmojiStatus;
emojiStatusUntil#fa30a8c7 document_id:long until:int = EmojiStatus;
account.emojiStatusesNotModified#d08ce645 = account.EmojiStatuses;
account.emojiStatuses#90c467d1 hash:long statuses:Vector<EmojiStatus> = account.EmojiStatuses;
reactionEmpty#79f5d419 = Reaction;
reactionEmoji#1b2286b8 emoticon:string = Reaction;
reactionCustomEmoji#8935fc73 document_id:long = Reaction;
chatReactionsNone#eafc32bc = ChatReactions;
chatReactionsAll#52928bca flags:# allow_custom:flags.0?true = ChatReactions;
chatReactionsSome#661d4037 reactions:Vector<Reaction> = ChatReactions;
messages.reactionsNotModified#b06fdbdf = messages.Reactions;
messages.reactions#eafdf716 hash:long reactions:Vector<Reaction> = messages.Reactions;
emailVerifyPurposeLoginSetup#4345be73 phone_number:string phone_code_hash:string = EmailVerifyPurpose;
emailVerifyPurposeLoginChange#527d22eb = EmailVerifyPurpose;
emailVerifyPurposePassport#bbf51685 = EmailVerifyPurpose;
emailVerificationCode#922e55a9 code:string = EmailVerification;
emailVerificationGoogle#db909ec2 token:string = EmailVerification;
emailVerificationApple#96d074fd token:string = EmailVerification;
account.emailVerified#2b96cd1b email:string = account.EmailVerified;
account.emailVerifiedLogin#e1bb0d61 email:string sent_code:auth.SentCode = account.EmailVerified;
premiumSubscriptionOption#b6f11ebe flags:# current:flags.1?true can_purchase_upgrade:flags.2?true months:int currency:string amount:long bot_url:string store_product:flags.0?string = PremiumSubscriptionOption;
sendAsPeer#b81c7034 flags:# premium_required:flags.0?true peer:Peer = SendAsPeer;
---functions---
initConnection#c1cd5ea9 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy params:flags.1?JSONValue query:!X = X;
invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;
auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode;
auth.signUp#80eee427 phone_number:string phone_code_hash:string first_name:string last_name:string = auth.Authorization;
auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization;
auth.signIn#8d52a951 flags:# phone_number:string phone_code_hash:string phone_code:flags.0?string email_verification:flags.1?EmailVerification = auth.Authorization;
auth.logOut#3e72ba19 = auth.LoggedOut;
auth.resetAuthorizations#9fab0d1a = Bool;
auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization;
@ -1087,8 +1118,8 @@ messages.deleteHistory#b08f922a flags:# just_clear:flags.0?true revoke:flags.1?t
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
messages.sendMessage#d9d75a4 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMedia#e25ff8e0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMessage#d9d75a4 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMedia#e25ff8e0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.forwardMessages#cc30290b flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.reportSpam#cf1592db peer:InputPeer = Bool;
messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings;
@ -1137,7 +1168,7 @@ messages.getFavedStickers#4f1aaa9 hash:long = messages.FavedStickers;
messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool;
messages.getUnreadMentions#46578472 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readMentions#f0189d3 peer:InputPeer = messages.AffectedHistory;
messages.sendMultiMedia#f803138f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMultiMedia#f803138f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.searchStickerSets#35705b8a flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets;
messages.markDialogUnread#c286d98f flags:# unread:flags.0?true peer:InputDialogPeer = Bool;
messages.updatePinnedMessage#d2aaf7ec flags:# silent:flags.0?true unpin:flags.1?true pm_oneside:flags.2?true peer:InputPeer id:int = Updates;
@ -1172,20 +1203,20 @@ messages.hideChatJoinRequest#7fe7e815 flags:# approved:flags.0?true peer:InputPe
messages.hideAllChatJoinRequests#e085f4ea flags:# approved:flags.0?true peer:InputPeer link:flags.1?string = Updates;
messages.toggleNoForwards#b11eafa2 peer:InputPeer enabled:Bool = Updates;
messages.saveDefaultSendAs#ccfddf96 peer:InputPeer send_as:InputPeer = Bool;
messages.sendReaction#25690ce4 flags:# big:flags.1?true peer:InputPeer msg_id:int reaction:flags.0?string = Updates;
messages.sendReaction#d30d78d4 flags:# big:flags.1?true add_to_recent:flags.2?true peer:InputPeer msg_id:int reaction:flags.0?Vector<Reaction> = Updates;
messages.getMessagesReactions#8bba90e6 peer:InputPeer id:Vector<int> = Updates;
messages.getMessageReactionsList#e0ee6b77 flags:# peer:InputPeer id:int reaction:flags.0?string offset:flags.1?string limit:int = messages.MessageReactionsList;
messages.setChatAvailableReactions#14050ea6 peer:InputPeer available_reactions:Vector<string> = Updates;
messages.getMessageReactionsList#461b3f48 flags:# peer:InputPeer id:int reaction:flags.0?Reaction offset:flags.1?string limit:int = messages.MessageReactionsList;
messages.setChatAvailableReactions#feb16771 peer:InputPeer available_reactions:ChatReactions = Updates;
messages.getAvailableReactions#18dea0ac hash:int = messages.AvailableReactions;
messages.setDefaultReaction#d960c4d4 reaction:string = Bool;
messages.setDefaultReaction#4f47a016 reaction:Reaction = Bool;
messages.getUnreadReactions#e85bae1a peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readReactions#82e251d7 peer:InputPeer = messages.AffectedHistory;
messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots;
messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot;
messages.toggleBotInAttachMenu#1aee33af bot:InputUser enabled:Bool = Bool;
messages.requestWebView#91b15831 flags:# from_bot_menu:flags.4?true silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON reply_to_msg_id:flags.0?int send_as:flags.13?InputPeer = WebViewResult;
messages.requestWebView#fc87a53c flags:# from_bot_menu:flags.4?true silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON platform:string reply_to_msg_id:flags.0?int send_as:flags.13?InputPeer = WebViewResult;
messages.prolongWebView#ea5fbcce flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to_msg_id:flags.0?int send_as:flags.13?InputPeer = Bool;
messages.requestSimpleWebView#6abb2f73 flags:# bot:InputUser url:string theme_params:flags.0?DataJSON = SimpleWebViewResult;
messages.requestSimpleWebView#299bec8e flags:# bot:InputUser url:string theme_params:flags.0?DataJSON platform:string = SimpleWebViewResult;
messages.sendWebViewResultMessage#a4314f5 bot_query_id:string result:InputBotInlineResult = WebViewMessageSent;
messages.sendWebViewData#dc0242c8 bot:InputUser random_id:long button_text:string data:string = Updates;
messages.transcribeAudio#269e9a49 peer:InputPeer msg_id:int = messages.TranscribedAudio;

View File

@ -80,7 +80,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType;
storage.fileWebp#1081464c = storage.FileType;
userEmpty#d3bc4b7a id:long = User;
user#3ff6ecb0 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
user#5d99adee flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus = User;
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
userProfilePhoto#82d1f706 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto;
@ -98,8 +98,8 @@ chatForbidden#6592a1a7 id:long title:string = Chat;
channel#8261ac61 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#d18ee226 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?Vector<string> = ChatFull;
channelFull#ea68a619 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags.31?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?Vector<string> = ChatFull;
chatFull#c9d31138 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions = ChatFull;
channelFull#f2355507 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags.31?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions = ChatFull;
chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant;
@ -294,7 +294,7 @@ updateChannelMessageViews#f226ac08 channel_id:long id:int views:int = Update;
updateChatParticipantAdmin#d7ca61a2 chat_id:long user_id:long is_admin:Bool version:int = Update;
updateNewStickerSet#688a30aa stickerset:messages.StickerSet = Update;
updateStickerSetsOrder#bb2d201 flags:# masks:flags.0?true emojis:flags.1?true order:Vector<long> = Update;
updateStickerSets#43ae3dec = Update;
updateStickerSets#31c24808 flags:# masks:flags.0?true emojis:flags.1?true = Update;
updateSavedGifs#9375341e = Update;
updateBotInlineQuery#496f379c flags:# query_id:long user_id:long query:string geo:flags.0?GeoPoint peer_type:flags.1?InlineQueryPeerType offset:string = Update;
updateBotInlineSend#12f12a07 flags:# user_id:long query:string geo:flags.0?GeoPoint id:string msg_id:flags.1?InputBotInlineMessageID = Update;
@ -363,6 +363,10 @@ updateBotMenuButton#14b85813 bot_id:long button:BotMenuButton = Update;
updateSavedRingtones#74d8be99 = Update;
updateTranscribedAudio#84cd5a flags:# pending:flags.0?true peer:Peer msg_id:int transcription_id:long text:string = Update;
updateReadFeaturedEmojiStickers#fb4c496c = Update;
updateUserEmojiStatus#28373599 user_id:long emoji_status:EmojiStatus = Update;
updateRecentEmojiStatuses#30f443db = Update;
updateRecentReactions#6f7863f4 = Update;
updateMoveStickerSetToTop#86fccf85 flags:# masks:flags.0?true emojis:flags.1?true stickerset:long = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -389,7 +393,7 @@ upload.fileCdnRedirect#f18cda44 dc_id:int file_token:bytes encryption_key:bytes
dcOption#18b7a10d flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true this_port_only:flags.5?true id:int ip_address:string port:int secret:flags.10?bytes = DcOption;
config#330b4067 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true pfs_enabled:flags.13?true force_try_ipv6:flags.14?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int pinned_infolder_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int = Config;
config#232566ac flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true pfs_enabled:flags.13?true force_try_ipv6:flags.14?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int pinned_infolder_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int reactions_default:flags.15?Reaction = Config;
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
@ -527,7 +531,7 @@ authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true pa
account.authorizations#4bff8ea0 authorization_ttl_days:int authorizations:Vector<Authorization> = account.Authorizations;
account.password#185b184f flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes pending_reset_date:flags.5?int = account.Password;
account.password#957b50fb flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes pending_reset_date:flags.5?int login_email_pattern:flags.6?string = account.Password;
account.passwordSettings#9a5c33e5 flags:# email:flags.0?string secure_settings:flags.1?SecureSecretSettings = account.PasswordSettings;
@ -551,6 +555,8 @@ inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet;
inputStickerSetDice#e67f520e emoticon:string = InputStickerSet;
inputStickerSetAnimatedEmojiAnimations#cde3739 = InputStickerSet;
inputStickerSetPremiumGifts#c88b3b02 = InputStickerSet;
inputStickerSetEmojiGenericAnimations#4c4d4ce = InputStickerSet;
inputStickerSetEmojiDefaultStatuses#29d0f5ee = InputStickerSet;
stickerSet#2dd14edc flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true videos:flags.6?true emojis:flags.7?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int thumb_document_id:flags.8?long count:int hash:int = StickerSet;
@ -686,6 +692,8 @@ auth.sentCodeTypeSms#c000bba2 length:int = auth.SentCodeType;
auth.sentCodeTypeCall#5353e5a7 length:int = auth.SentCodeType;
auth.sentCodeTypeFlashCall#ab03c6d9 pattern:string = auth.SentCodeType;
auth.sentCodeTypeMissedCall#82006484 prefix:string length:int = auth.SentCodeType;
auth.sentCodeTypeEmailCode#5a159841 flags:# apple_signin_allowed:flags.0?true google_signin_allowed:flags.1?true email_pattern:string length:int next_phone_login_date:flags.2?int = auth.SentCodeType;
auth.sentCodeTypeSetUpEmailRequired#a5491dea flags:# apple_signin_allowed:flags.0?true google_signin_allowed:flags.1?true = auth.SentCodeType;
messages.botCallbackAnswer#36585ea4 flags:# alert:flags.1?true has_url:flags.3?true native_ui:flags.4?true message:flags.0?string url:flags.2?string cache_time:int = messages.BotCallbackAnswer;
@ -820,7 +828,7 @@ inputWebDocument#9bed434d url:string size:int mime_type:string attributes:Vector
inputWebFileLocation#c239d686 url:string access_hash:long = InputWebFileLocation;
inputWebFileGeoPointLocation#9f2221c9 geo_point:InputGeoPoint access_hash:long w:int h:int zoom:int scale:int = InputWebFileLocation;
inputWebFileAudioAlbumThumbLocation#f46fe924 flags:# document:flags.0?InputDocument title:flags.1?string performer:flags.1?string = InputWebFileLocation;
inputWebFileAudioAlbumThumbLocation#f46fe924 flags:# small:flags.2?true document:flags.0?InputDocument title:flags.1?string performer:flags.1?string = InputWebFileLocation;
upload.webFile#21e753bc size:int mime_type:string file_type:storage.FileType mtime:int bytes:bytes = upload.WebFile;
@ -912,7 +920,7 @@ channelAdminLogEventActionChangeHistoryTTL#6e941a38 prev_value:int new_value:int
channelAdminLogEventActionParticipantJoinByRequest#afb6144a invite:ExportedChatInvite approved_by:long = ChannelAdminLogEventAction;
channelAdminLogEventActionToggleNoForwards#cb2ac766 new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionSendMessage#278f2868 message:Message = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeAvailableReactions#9cf7f76a prev_value:Vector<string> new_value:Vector<string> = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeAvailableReactions#be4e0ef8 prev_value:ChatReactions new_value:ChatReactions = ChannelAdminLogEventAction;
channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
@ -1289,7 +1297,7 @@ searchResultPosition#7f648b67 msg_id:int date:int offset:int = SearchResultsPosi
messages.searchResultsPositions#53b22baf count:int positions:Vector<SearchResultsPosition> = messages.SearchResultsPositions;
channels.sendAsPeers#8356cda9 peers:Vector<Peer> chats:Vector<Chat> users:Vector<User> = channels.SendAsPeers;
channels.sendAsPeers#f496b0c6 peers:Vector<SendAsPeer> chats:Vector<Chat> users:Vector<User> = channels.SendAsPeers;
users.userFull#3b6d152e full_user:UserFull chats:Vector<Chat> users:Vector<User> = users.UserFull;
@ -1297,7 +1305,7 @@ messages.peerSettings#6880b94d settings:PeerSettings chats:Vector<Chat> users:Ve
auth.loggedOut#c3a2835f flags:# future_auth_token:flags.0?bytes = auth.LoggedOut;
reactionCount#6fb250d1 flags:# chosen:flags.0?true reaction:string count:int = ReactionCount;
reactionCount#a3d1cb80 flags:# chosen_order:flags.0?int reaction:Reaction count:int = ReactionCount;
messageReactions#4f2b9479 flags:# min:flags.0?true can_see_list:flags.2?true results:Vector<ReactionCount> recent_reactions:flags.1?Vector<MessagePeerReaction> = MessageReactions;
@ -1311,7 +1319,7 @@ messages.availableReactions#768e3aad hash:int reactions:Vector<AvailableReaction
messages.translateNoResult#67ca4737 = messages.TranslatedText;
messages.translateResultText#a214f7d0 text:string = messages.TranslatedText;
messagePeerReaction#51b67eff flags:# big:flags.0?true unread:flags.1?true peer_id:Peer reaction:string = MessagePeerReaction;
messagePeerReaction#b156fe9c flags:# big:flags.0?true unread:flags.1?true peer_id:Peer reaction:Reaction = MessagePeerReaction;
groupCallStreamChannel#80eb48af channel:int scale:int last_timestamp_ms:long = GroupCallStreamChannel;
@ -1364,7 +1372,7 @@ payments.exportedInvoice#aed0cbd9 url:string = payments.ExportedInvoice;
messages.transcribedAudio#93752c52 flags:# pending:flags.0?true transcription_id:long text:string = messages.TranscribedAudio;
help.premiumPromo#8a4f3c29 status_text:string status_entities:Vector<MessageEntity> video_sections:Vector<string> videos:Vector<Document> currency:string monthly_amount:long users:Vector<User> = help.PremiumPromo;
help.premiumPromo#5334759c status_text:string status_entities:Vector<MessageEntity> video_sections:Vector<string> videos:Vector<Document> period_options:Vector<PremiumSubscriptionOption> users:Vector<User> = help.PremiumPromo;
inputStorePaymentPremiumSubscription#a6751e66 flags:# restore:flags.0?true = InputStorePaymentPurpose;
inputStorePaymentGiftPremium#616f7fe8 user_id:InputUser currency:string amount:long = InputStorePaymentPurpose;
@ -1373,6 +1381,39 @@ premiumGiftOption#74c34319 flags:# months:int currency:string amount:long bot_ur
paymentFormMethod#88f8f21b url:string title:string = PaymentFormMethod;
emojiStatusEmpty#2de11aae = EmojiStatus;
emojiStatus#929b619d document_id:long = EmojiStatus;
emojiStatusUntil#fa30a8c7 document_id:long until:int = EmojiStatus;
account.emojiStatusesNotModified#d08ce645 = account.EmojiStatuses;
account.emojiStatuses#90c467d1 hash:long statuses:Vector<EmojiStatus> = account.EmojiStatuses;
reactionEmpty#79f5d419 = Reaction;
reactionEmoji#1b2286b8 emoticon:string = Reaction;
reactionCustomEmoji#8935fc73 document_id:long = Reaction;
chatReactionsNone#eafc32bc = ChatReactions;
chatReactionsAll#52928bca flags:# allow_custom:flags.0?true = ChatReactions;
chatReactionsSome#661d4037 reactions:Vector<Reaction> = ChatReactions;
messages.reactionsNotModified#b06fdbdf = messages.Reactions;
messages.reactions#eafdf716 hash:long reactions:Vector<Reaction> = messages.Reactions;
emailVerifyPurposeLoginSetup#4345be73 phone_number:string phone_code_hash:string = EmailVerifyPurpose;
emailVerifyPurposeLoginChange#527d22eb = EmailVerifyPurpose;
emailVerifyPurposePassport#bbf51685 = EmailVerifyPurpose;
emailVerificationCode#922e55a9 code:string = EmailVerification;
emailVerificationGoogle#db909ec2 token:string = EmailVerification;
emailVerificationApple#96d074fd token:string = EmailVerification;
account.emailVerified#2b96cd1b email:string = account.EmailVerified;
account.emailVerifiedLogin#e1bb0d61 email:string sent_code:auth.SentCode = account.EmailVerified;
premiumSubscriptionOption#b6f11ebe flags:# current:flags.1?true can_purchase_upgrade:flags.2?true months:int currency:string amount:long bot_url:string store_product:flags.0?string = PremiumSubscriptionOption;
sendAsPeer#b81c7034 flags:# premium_required:flags.0?true peer:Peer = SendAsPeer;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1385,7 +1426,7 @@ invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X;
auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode;
auth.signUp#80eee427 phone_number:string phone_code_hash:string first_name:string last_name:string = auth.Authorization;
auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization;
auth.signIn#8d52a951 flags:# phone_number:string phone_code_hash:string phone_code:flags.0?string email_verification:flags.1?EmailVerification = auth.Authorization;
auth.logOut#3e72ba19 = auth.LoggedOut;
auth.resetAuthorizations#9fab0d1a = Bool;
auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization;
@ -1441,8 +1482,8 @@ account.getAuthorizationForm#a929597a bot_id:long scope:string public_key:string
account.acceptAuthorization#f3ed4c73 bot_id:long scope:string public_key:string value_hashes:Vector<SecureValueHash> credentials:SecureCredentialsEncrypted = Bool;
account.sendVerifyPhoneCode#a5a356f9 phone_number:string settings:CodeSettings = auth.SentCode;
account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_code:string = Bool;
account.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode;
account.verifyEmail#ecba39db email:string code:string = Bool;
account.sendVerifyEmailCode#98e037bb purpose:EmailVerifyPurpose email:string = account.SentEmailCode;
account.verifyEmail#32da4cf purpose:EmailVerifyPurpose verification:EmailVerification = account.EmailVerified;
account.initTakeoutSession#8ef3eab0 flags:# contacts:flags.0?true message_users:flags.1?true message_chats:flags.2?true message_megagroups:flags.3?true message_channels:flags.4?true files:flags.5?true file_max_size:flags.5?long = account.Takeout;
account.finishTakeoutSession#1d2652ee flags:# success:flags.0?true = Bool;
account.confirmPasswordEmail#8fdf1920 code:string = Bool;
@ -1479,6 +1520,10 @@ account.changeAuthorizationSettings#40f48462 flags:# hash:long encrypted_request
account.getSavedRingtones#e1902288 hash:long = account.SavedRingtones;
account.saveRingtone#3dea5b03 id:InputDocument unsave:Bool = account.SavedRingtone;
account.uploadRingtone#831a83a2 file:InputFile file_name:string mime_type:string = Document;
account.updateEmojiStatus#fbd3de6b emoji_status:EmojiStatus = Bool;
account.getDefaultEmojiStatuses#d6753386 hash:long = account.EmojiStatuses;
account.getRecentEmojiStatuses#f578105 hash:long = account.EmojiStatuses;
account.clearRecentEmojiStatuses#18201aae = Bool;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
@ -1515,8 +1560,8 @@ messages.deleteHistory#b08f922a flags:# just_clear:flags.0?true revoke:flags.1?t
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
messages.sendMessage#d9d75a4 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMedia#e25ff8e0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMessage#d9d75a4 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMedia#e25ff8e0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.forwardMessages#cc30290b flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.reportSpam#cf1592db peer:InputPeer = Bool;
messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings;
@ -1596,7 +1641,7 @@ messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool;
messages.getUnreadMentions#46578472 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readMentions#f0189d3 peer:InputPeer = messages.AffectedHistory;
messages.getRecentLocations#702a40e0 peer:InputPeer limit:int hash:long = messages.Messages;
messages.sendMultiMedia#f803138f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.sendMultiMedia#f803138f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
messages.searchStickerSets#35705b8a flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets;
messages.getSplitRanges#1cff7e08 = Vector<MessageRange>;
@ -1655,12 +1700,12 @@ messages.hideChatJoinRequest#7fe7e815 flags:# approved:flags.0?true peer:InputPe
messages.hideAllChatJoinRequests#e085f4ea flags:# approved:flags.0?true peer:InputPeer link:flags.1?string = Updates;
messages.toggleNoForwards#b11eafa2 peer:InputPeer enabled:Bool = Updates;
messages.saveDefaultSendAs#ccfddf96 peer:InputPeer send_as:InputPeer = Bool;
messages.sendReaction#25690ce4 flags:# big:flags.1?true peer:InputPeer msg_id:int reaction:flags.0?string = Updates;
messages.sendReaction#d30d78d4 flags:# big:flags.1?true add_to_recent:flags.2?true peer:InputPeer msg_id:int reaction:flags.0?Vector<Reaction> = Updates;
messages.getMessagesReactions#8bba90e6 peer:InputPeer id:Vector<int> = Updates;
messages.getMessageReactionsList#e0ee6b77 flags:# peer:InputPeer id:int reaction:flags.0?string offset:flags.1?string limit:int = messages.MessageReactionsList;
messages.setChatAvailableReactions#14050ea6 peer:InputPeer available_reactions:Vector<string> = Updates;
messages.getMessageReactionsList#461b3f48 flags:# peer:InputPeer id:int reaction:flags.0?Reaction offset:flags.1?string limit:int = messages.MessageReactionsList;
messages.setChatAvailableReactions#feb16771 peer:InputPeer available_reactions:ChatReactions = Updates;
messages.getAvailableReactions#18dea0ac hash:int = messages.AvailableReactions;
messages.setDefaultReaction#d960c4d4 reaction:string = Bool;
messages.setDefaultReaction#4f47a016 reaction:Reaction = Bool;
messages.translateText#24ce6dee flags:# peer:flags.0?InputPeer msg_id:flags.0?int text:flags.1?string from_lang:flags.2?string to_lang:string = messages.TranslatedText;
messages.getUnreadReactions#e85bae1a peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readReactions#82e251d7 peer:InputPeer = messages.AffectedHistory;
@ -1668,9 +1713,9 @@ messages.searchSentMedia#107e31a0 q:string filter:MessagesFilter limit:int = mes
messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots;
messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot;
messages.toggleBotInAttachMenu#1aee33af bot:InputUser enabled:Bool = Bool;
messages.requestWebView#91b15831 flags:# from_bot_menu:flags.4?true silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON reply_to_msg_id:flags.0?int send_as:flags.13?InputPeer = WebViewResult;
messages.requestWebView#fc87a53c flags:# from_bot_menu:flags.4?true silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON platform:string reply_to_msg_id:flags.0?int send_as:flags.13?InputPeer = WebViewResult;
messages.prolongWebView#ea5fbcce flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to_msg_id:flags.0?int send_as:flags.13?InputPeer = Bool;
messages.requestSimpleWebView#6abb2f73 flags:# bot:InputUser url:string theme_params:flags.0?DataJSON = SimpleWebViewResult;
messages.requestSimpleWebView#299bec8e flags:# bot:InputUser url:string theme_params:flags.0?DataJSON platform:string = SimpleWebViewResult;
messages.sendWebViewResultMessage#a4314f5 bot_query_id:string result:InputBotInlineResult = WebViewMessageSent;
messages.sendWebViewData#dc0242c8 bot:InputUser random_id:long button_text:string data:string = Updates;
messages.transcribeAudio#269e9a49 peer:InputPeer msg_id:int = messages.TranscribedAudio;
@ -1678,6 +1723,10 @@ messages.rateTranscribedAudio#7f1d072f peer:InputPeer msg_id:int transcription_i
messages.getCustomEmojiDocuments#d9ab0f54 document_id:Vector<long> = Vector<Document>;
messages.getEmojiStickers#fbfca18f hash:long = messages.AllStickers;
messages.getFeaturedEmojiStickers#ecf6736 hash:long = messages.FeaturedStickers;
messages.reportReaction#3f64c076 peer:InputPeer id:int reaction_peer:InputPeer = Bool;
messages.getTopReactions#bb8125ba limit:int hash:long = messages.Reactions;
messages.getRecentReactions#39461db2 limit:int hash:long = messages.Reactions;
messages.clearRecentReactions#9dfeefb4 = Bool;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@ -1784,7 +1833,6 @@ payments.exportInvoice#f91b065 invoice_media:InputMedia = payments.ExportedInvoi
payments.assignAppStoreTransaction#80ed747d receipt:bytes purpose:InputStorePaymentPurpose = Updates;
payments.assignPlayMarketTransaction#dffd50d3 receipt:DataJSON purpose:InputStorePaymentPurpose = Updates;
payments.canPurchasePremium#9fc19eb6 purpose:InputStorePaymentPurpose = Bool;
payments.requestRecurringPayment#146e958d user_id:InputUser recurring_init_charge:string invoice_media:InputMedia = Updates;
stickers.createStickerSet#9021ab67 flags:# masks:flags.0?true animated:flags.1?true videos:flags.4?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector<InputStickerSetItem> software:flags.3?string = messages.StickerSet;
stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet;
@ -1840,3 +1888,5 @@ stats.loadAsyncGraph#621d5fa0 flags:# token:string x:flags.0?long = StatsGraph;
stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel = stats.MegagroupStats;
stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
// LAYER 145

View File

@ -90,9 +90,10 @@ class RLottie {
private container: HTMLDivElement,
private tgsUrl: string,
private params: Params = {},
private onLoad?: () => void,
private customColor?: [number, number, number],
private onLoad?: () => void,
private onEnded?: (isDestroyed?: boolean) => void,
private onLoop?: () => void,
) {
this.initContainer();
this.initConfig();
@ -135,6 +136,10 @@ class RLottie {
this.speed = speed;
}
setNoLoop(noLoop: boolean) {
this.params.noLoop = noLoop;
}
destroy() {
this.isDestroyed = true;
this.pause();
@ -355,6 +360,7 @@ class RLottie {
this.onEnded?.();
return false;
}
this.onLoop?.();
this.approxFrameIndex = 0;
@ -366,6 +372,7 @@ class RLottie {
this.onEnded?.();
return false;
}
this.onLoop?.();
this.approxFrameIndex = this.framesCount! - 1;

View File

@ -183,6 +183,7 @@ $color-message-reaction-own-hover: #b5e0a4;
--messages-container-width: 45.5rem;
--right-column-width: 26.5rem;
--header-height: 3.5rem;
--custom-emoji-size: 1.5rem;
--symbol-menu-width: 26.25rem;
--symbol-menu-height: 23.25rem;

View File

@ -74,7 +74,7 @@ function transition(t: number) {
return 1 - ((1 - t) ** 3.5);
}
function hexToRgb(hex: string): RGBAColor {
export function hexToRgb(hex: string): RGBAColor {
const result = HEX_COLOR_REGEX.exec(hex)!;
return {

View File

@ -2,13 +2,13 @@ import type { ApiThemeParameters } from '../api/types';
export function extractCurrentThemeParams(): ApiThemeParameters {
const style = getComputedStyle(document.documentElement);
const backgroundColor = getPropertyWrapped(style, '--color-background');
const textColor = getPropertyWrapped(style, '--color-text');
const buttonColor = getPropertyWrapped(style, '--color-primary');
const buttonTextColor = getPropertyWrapped(style, '--color-white');
const linkColor = getPropertyWrapped(style, '--color-links');
const hintColor = getPropertyWrapped(style, '--color-text-secondary');
const secondaryBgColor = getPropertyWrapped(style, '--color-background-secondary');
const backgroundColor = getPropertyHexColor(style, '--color-background')!;
const textColor = getPropertyHexColor(style, '--color-text')!;
const buttonColor = getPropertyHexColor(style, '--color-primary')!;
const buttonTextColor = getPropertyHexColor(style, '--color-white')!;
const linkColor = getPropertyHexColor(style, '--color-links')!;
const hintColor = getPropertyHexColor(style, '--color-text-secondary')!;
const secondaryBgColor = getPropertyHexColor(style, '--color-background-secondary')!;
return {
bg_color: backgroundColor,
text_color: textColor,
@ -24,12 +24,13 @@ export function validateHexColor(color: string) {
return /^#[0-9A-F]{6}$/i.test(color);
}
function getPropertyWrapped(style: CSSStyleDeclaration, property: string) {
export function getPropertyHexColor(style: CSSStyleDeclaration, property: string) {
const value = style.getPropertyValue(property);
return wrapColor(value.trim());
if (!value) return undefined;
return prepareHexColor(value.trim());
}
function wrapColor(color: string) {
function prepareHexColor(color: string) {
if (validateHexColor(color)) return color;
return `#${color.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+\.{0,1}\d*))?\)$/)!
.slice(1)