Profile: Support multiple usernames (#2202)
This commit is contained in:
parent
88234a64dc
commit
1b37ccc533
@ -20,6 +20,7 @@ import {
|
||||
import { omitVirtualClassFields } from './helpers';
|
||||
import { getServerTime } from '../../../util/serverTime';
|
||||
import { buildApiReaction } from './messages';
|
||||
import { buildApiUsernames } from './common';
|
||||
|
||||
type PeerEntityApiChatFields = Omit<ApiChat, (
|
||||
'id' | 'type' | 'title' |
|
||||
@ -42,15 +43,16 @@ function buildApiChatFieldsFromPeerEntity(
|
||||
const isFake = Boolean('fake' in peerEntity && peerEntity.fake);
|
||||
const isJoinToSend = Boolean('joinToSend' in peerEntity && peerEntity.joinToSend);
|
||||
const isJoinRequest = Boolean('joinRequest' in peerEntity && peerEntity.joinRequest);
|
||||
const usernames = buildApiUsernames(peerEntity);
|
||||
|
||||
return {
|
||||
isMin,
|
||||
hasPrivateLink,
|
||||
isSignaturesShown,
|
||||
usernames,
|
||||
...(accessHash && { accessHash }),
|
||||
hasVideoAvatar,
|
||||
...(avatarHash && { avatarHash }),
|
||||
...('username' in peerEntity && { username: peerEntity.username }),
|
||||
...('verified' in peerEntity && { isVerified: peerEntity.verified }),
|
||||
...('callActive' in peerEntity && { isCallActive: peerEntity.callActive }),
|
||||
...('callNotEmpty' in peerEntity && { isCallNotEmpty: peerEntity.callNotEmpty }),
|
||||
|
||||
@ -2,7 +2,7 @@ import { Api as GramJs } from '../../../lib/gramjs';
|
||||
import { strippedPhotoToJpg } from '../../../lib/gramjs/Utils';
|
||||
|
||||
import type {
|
||||
ApiPhoto, ApiPhotoSize, ApiThumbnail, ApiVideoSize,
|
||||
ApiPhoto, ApiPhotoSize, ApiThumbnail, ApiVideoSize, ApiUsername,
|
||||
} from '../../types';
|
||||
import { bytesToDataUri } from './helpers';
|
||||
import { pathBytesToSvg } from './pathBytesToSvg';
|
||||
@ -100,3 +100,31 @@ export function buildApiPhotoSize(photoSize: GramJs.PhotoSize): ApiPhotoSize {
|
||||
type: type as ('m' | 'x' | 'y'),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildApiUsernames(mtpPeer: GramJs.User | GramJs.Channel | GramJs.UpdateUserName) {
|
||||
if (!mtpPeer.usernames && !('username' in mtpPeer && mtpPeer.username)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const usernames: ApiUsername[] = [];
|
||||
|
||||
if ('username' in mtpPeer && mtpPeer.username) {
|
||||
usernames.push({
|
||||
username: mtpPeer.username,
|
||||
isActive: true,
|
||||
isEditable: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (mtpPeer.usernames) {
|
||||
mtpPeer.usernames.forEach(({ username, active, editable }) => {
|
||||
usernames.push({
|
||||
username,
|
||||
...(active && { isActive: true }),
|
||||
...(editable && { isEditable: true }),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return usernames;
|
||||
}
|
||||
|
||||
@ -2,11 +2,13 @@ import { Api as GramJs } from '../../../lib/gramjs';
|
||||
import type {
|
||||
ApiEmojiStatus,
|
||||
ApiPremiumGiftOption,
|
||||
ApiUser, ApiUserStatus, ApiUserType,
|
||||
ApiUser,
|
||||
ApiUserStatus,
|
||||
ApiUserType,
|
||||
} from '../../types';
|
||||
import { buildApiPeerId } from './peers';
|
||||
import { buildApiBotInfo } from './bots';
|
||||
import { buildApiPhoto } from './common';
|
||||
import { buildApiPhoto, buildApiUsernames } from './common';
|
||||
|
||||
export function buildApiUserFromFull(mtpUserFull: GramJs.users.UserFull): ApiUser {
|
||||
const {
|
||||
@ -50,6 +52,8 @@ export function buildApiUser(mtpUser: GramJs.TypeUser): ApiUser | undefined {
|
||||
: undefined;
|
||||
const userType = buildApiUserType(mtpUser);
|
||||
|
||||
const usernames = buildApiUsernames(mtpUser);
|
||||
|
||||
return {
|
||||
id: buildApiPeerId(id, 'user'),
|
||||
isMin: Boolean(mtpUser.min),
|
||||
@ -62,7 +66,7 @@ export function buildApiUser(mtpUser: GramJs.TypeUser): ApiUser | undefined {
|
||||
...(firstName && { firstName }),
|
||||
...(userType === 'userTypeBot' && { canBeInvitedToGroup: !mtpUser.botNochats }),
|
||||
...(lastName && { lastName }),
|
||||
username: mtpUser.username || '',
|
||||
...(usernames && { usernames }),
|
||||
phoneNumber: mtpUser.phone || '',
|
||||
noStatus: !mtpUser.status,
|
||||
...(mtpUser.accessHash && { accessHash: String(mtpUser.accessHash) }),
|
||||
|
||||
@ -100,7 +100,7 @@ export async function fetchInlineBotResults({
|
||||
return {
|
||||
isGallery: Boolean(result.gallery),
|
||||
help: bot.botPlaceholder,
|
||||
nextOffset: getInlineBotResultsNextOffset(bot.username, result.nextOffset),
|
||||
nextOffset: getInlineBotResultsNextOffset(bot.usernames![0].username, result.nextOffset),
|
||||
switchPm: buildBotSwitchPm(result.switchPm),
|
||||
users: result.users.map(buildApiUser).filter(Boolean),
|
||||
results: processInlineBotResult(String(result.queryId), result.results),
|
||||
|
||||
@ -60,7 +60,7 @@ export {
|
||||
fetchNotificationExceptions, fetchNotificationSettings, updateContactSignUpNotification, updateNotificationSettings,
|
||||
fetchLanguages, fetchLangPack, fetchPrivacySettings, setPrivacySettings, registerDevice, unregisterDevice,
|
||||
updateIsOnline, fetchContentSettings, updateContentSettings, fetchLangStrings, fetchCountryList, fetchAppConfig,
|
||||
fetchGlobalPrivacySettings, updateGlobalPrivacySettings,
|
||||
fetchGlobalPrivacySettings, updateGlobalPrivacySettings, toggleUsername, reorderUsernames,
|
||||
} from './settings';
|
||||
|
||||
export {
|
||||
|
||||
@ -5,6 +5,8 @@ import { buildInputEntity, buildInputPeer } from '../gramjsBuilders';
|
||||
import type {
|
||||
ApiChat, ApiError, ApiUser, OnApiUpdate,
|
||||
} from '../../types';
|
||||
|
||||
import { USERNAME_PURCHASE_ERROR } from '../../../config';
|
||||
import { addEntitiesWithPhotosToLocalDb } from '../helpers';
|
||||
import { buildApiExportedInvite, buildChatInviteImporter } from '../apiBuilders/chats';
|
||||
import { buildApiUser } from '../apiBuilders/users';
|
||||
@ -12,20 +14,32 @@ import { buildCollectionByKey } from '../../../util/iteratees';
|
||||
|
||||
let onUpdate: OnApiUpdate;
|
||||
|
||||
export const ACCEPTABLE_USERNAME_ERRORS = new Set([USERNAME_PURCHASE_ERROR, 'USERNAME_INVALID']);
|
||||
|
||||
export function init(_onUpdate: OnApiUpdate) {
|
||||
onUpdate = _onUpdate;
|
||||
}
|
||||
|
||||
export function checkChatUsername({ username }: { username: string }) {
|
||||
return invokeRequest(new GramJs.channels.CheckUsername({
|
||||
channel: new GramJs.InputChannelEmpty(),
|
||||
username,
|
||||
}), undefined, true).catch((error) => {
|
||||
if ((error as ApiError).message === 'USERNAME_INVALID') {
|
||||
return false;
|
||||
export async function checkChatUsername({ username }: { username: string }) {
|
||||
try {
|
||||
const result = await invokeRequest(new GramJs.channels.CheckUsername({
|
||||
channel: new GramJs.InputChannelEmpty(),
|
||||
username,
|
||||
}), undefined, true);
|
||||
|
||||
return { result, error: undefined };
|
||||
} catch (error) {
|
||||
const errorMessage = (error as ApiError).message;
|
||||
|
||||
if (ACCEPTABLE_USERNAME_ERRORS.has(errorMessage)) {
|
||||
return {
|
||||
result: false,
|
||||
error: errorMessage,
|
||||
};
|
||||
}
|
||||
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function setChatUsername(
|
||||
@ -36,11 +50,13 @@ export async function setChatUsername(
|
||||
username,
|
||||
}));
|
||||
|
||||
const usernames = chat.usernames!.map((u) => (u.isEditable ? { ...u, username } : u));
|
||||
|
||||
if (result) {
|
||||
onUpdate({
|
||||
'@type': 'updateChat',
|
||||
id: chat.id,
|
||||
chat: { username },
|
||||
chat: { usernames },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1358,7 +1358,7 @@ export async function fetchSponsoredMessages({ chat }: { chat: ApiChat }) {
|
||||
channel: buildInputPeer(chat.id, chat.accessHash),
|
||||
}));
|
||||
|
||||
if (!result || !result.messages.length) {
|
||||
if (!result || result instanceof GramJs.messages.SponsoredMessagesEmpty || !result.messages.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@ import type { ApiPrivacyKey, InputPrivacyRules, LangCode } from '../../../types'
|
||||
|
||||
import type { LANG_PACKS } from '../../../config';
|
||||
import { BLOCKED_LIST_LIMIT, DEFAULT_LANG_PACK } from '../../../config';
|
||||
import { ACCEPTABLE_USERNAME_ERRORS } from './management';
|
||||
import {
|
||||
buildApiCountryList,
|
||||
buildApiNotifyException,
|
||||
@ -55,13 +56,25 @@ export function updateProfile({
|
||||
}), true);
|
||||
}
|
||||
|
||||
export function checkUsername(username: string) {
|
||||
return invokeRequest(new GramJs.account.CheckUsername({ username }), undefined, true).catch((error) => {
|
||||
if ((error as ApiError).message === 'USERNAME_INVALID') {
|
||||
return false;
|
||||
export async function checkUsername(username: string) {
|
||||
try {
|
||||
const result = await invokeRequest(new GramJs.account.CheckUsername({
|
||||
username,
|
||||
}), undefined, true);
|
||||
|
||||
return { result, error: undefined };
|
||||
} catch (error) {
|
||||
const errorMessage = (error as ApiError).message;
|
||||
|
||||
if (ACCEPTABLE_USERNAME_ERRORS.has(errorMessage)) {
|
||||
return {
|
||||
result: false,
|
||||
error: errorMessage,
|
||||
};
|
||||
}
|
||||
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function updateUsername(username: string) {
|
||||
@ -544,3 +557,42 @@ export async function updateGlobalPrivacySettings({ shouldArchiveAndMuteNewNonCo
|
||||
shouldArchiveAndMuteNewNonContact: Boolean(result.archiveAndMuteNewNoncontactPeers),
|
||||
};
|
||||
}
|
||||
|
||||
export function toggleUsername({
|
||||
chatId, accessHash, username, isActive,
|
||||
}: {
|
||||
username: string;
|
||||
isActive: boolean;
|
||||
chatId?: string;
|
||||
accessHash?: string;
|
||||
}) {
|
||||
if (chatId) {
|
||||
return invokeRequest(new GramJs.channels.ToggleUsername({
|
||||
channel: buildInputEntity(chatId, accessHash) as GramJs.InputChannel,
|
||||
username,
|
||||
active: isActive,
|
||||
}));
|
||||
}
|
||||
|
||||
return invokeRequest(new GramJs.account.ToggleUsername({
|
||||
username,
|
||||
active: isActive,
|
||||
}));
|
||||
}
|
||||
|
||||
export function reorderUsernames({ chatId, accessHash, usernames }: {
|
||||
usernames: string[];
|
||||
chatId?: string;
|
||||
accessHash?: string;
|
||||
}) {
|
||||
if (chatId) {
|
||||
return invokeRequest(new GramJs.channels.ReorderUsernames({
|
||||
channel: buildInputEntity(chatId, accessHash) as GramJs.InputChannel,
|
||||
order: usernames,
|
||||
}));
|
||||
}
|
||||
|
||||
return invokeRequest(new GramJs.account.ReorderUsernames({
|
||||
order: usernames,
|
||||
}));
|
||||
}
|
||||
|
||||
@ -27,7 +27,11 @@ import {
|
||||
buildApiChatFolder,
|
||||
buildApiChatSettings,
|
||||
} from './apiBuilders/chats';
|
||||
import { buildApiUser, buildApiUserEmojiStatus, buildApiUserStatus } from './apiBuilders/users';
|
||||
import {
|
||||
buildApiUser,
|
||||
buildApiUserEmojiStatus,
|
||||
buildApiUserStatus,
|
||||
} from './apiBuilders/users';
|
||||
import {
|
||||
buildMessageFromUpdate,
|
||||
isMessageWithMedia,
|
||||
@ -46,7 +50,7 @@ import {
|
||||
swapLocalInvoiceMedia,
|
||||
} from './helpers';
|
||||
import { buildApiNotifyException, buildPrivacyKey, buildPrivacyRules } from './apiBuilders/misc';
|
||||
import { buildApiPhoto } from './apiBuilders/common';
|
||||
import { buildApiPhoto, buildApiUsernames } from './apiBuilders/common';
|
||||
import {
|
||||
buildApiGroupCall,
|
||||
buildApiGroupCallParticipant,
|
||||
@ -779,14 +783,20 @@ export function updater(update: Update, originRequest?: GramJs.AnyRequest) {
|
||||
} else if (update instanceof GramJs.UpdateUserName) {
|
||||
const apiUserId = buildApiPeerId(update.userId, 'user');
|
||||
const updatedUser = localDb.users[apiUserId];
|
||||
|
||||
const user = updatedUser?.mutualContact && !updatedUser.self
|
||||
? pick(update, ['username'])
|
||||
: pick(update, ['firstName', 'lastName', 'username']);
|
||||
? pick(update, [])
|
||||
: pick(update, ['firstName', 'lastName']);
|
||||
|
||||
const usernames = buildApiUsernames(update);
|
||||
|
||||
onUpdate({
|
||||
'@type': 'updateUser',
|
||||
id: apiUserId,
|
||||
user,
|
||||
user: {
|
||||
...user,
|
||||
usernames,
|
||||
},
|
||||
});
|
||||
} else if (update instanceof GramJs.UpdateUserPhoto) {
|
||||
const { userId, photo } = update;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { ApiMessage, ApiPhoto, ApiStickerSet } from './messages';
|
||||
import type { ApiBotCommand } from './bots';
|
||||
import type { ApiChatInviteImporter } from './misc';
|
||||
import type { ApiFakeType } from './users';
|
||||
import type { ApiFakeType, ApiUsername } from './users';
|
||||
|
||||
type ApiChatType = (
|
||||
'chatTypePrivate' | 'chatTypeSecret' |
|
||||
@ -29,7 +29,7 @@ export interface ApiChat {
|
||||
isMin?: boolean;
|
||||
hasVideoAvatar?: boolean;
|
||||
avatarHash?: string;
|
||||
username?: string;
|
||||
usernames?: ApiUsername[];
|
||||
membersCount?: number;
|
||||
joinDate?: number;
|
||||
isSupport?: boolean;
|
||||
|
||||
@ -13,7 +13,7 @@ export interface ApiUser {
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
noStatus?: boolean;
|
||||
username: string;
|
||||
usernames?: ApiUsername[];
|
||||
phoneNumber: string;
|
||||
accessHash?: string;
|
||||
hasVideoAvatar?: boolean;
|
||||
@ -58,6 +58,12 @@ export interface ApiUserStatus {
|
||||
expires?: number;
|
||||
}
|
||||
|
||||
export interface ApiUsername {
|
||||
username: string;
|
||||
isActive?: true;
|
||||
isEditable?: true;
|
||||
}
|
||||
|
||||
export type ApiChatType = typeof API_CHAT_TYPES[number];
|
||||
export type ApiAttachMenuPeerType = 'self' | ApiChatType;
|
||||
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useCallback, useEffect, useState,
|
||||
memo, useCallback, useEffect, useMemo, useState,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import type { GlobalState } from '../../global/types';
|
||||
import type { ApiChat, ApiCountryCode, ApiUser } from '../../api/types';
|
||||
import type {
|
||||
ApiChat, ApiCountryCode, ApiUser, ApiUsername,
|
||||
} from '../../api/types';
|
||||
|
||||
import { TME_LINK_PREFIX } from '../../config';
|
||||
import {
|
||||
selectChat, selectNotifyExceptions, selectNotifySettings, selectUser,
|
||||
} from '../../global/selectors';
|
||||
@ -17,6 +20,7 @@ import renderText from './helpers/renderText';
|
||||
import { copyTextToClipboard } from '../../util/clipboard';
|
||||
import { formatPhoneNumberWithCode } from '../../util/phoneNumber';
|
||||
import { debounce } from '../../util/schedulers';
|
||||
import stopEvent from '../../util/stopEvent';
|
||||
import useLang from '../../hooks/useLang';
|
||||
|
||||
import ListItem from '../ui/ListItem';
|
||||
@ -57,11 +61,11 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
const {
|
||||
id: userId,
|
||||
fullInfo,
|
||||
username,
|
||||
usernames,
|
||||
phoneNumber,
|
||||
isSelf,
|
||||
} = user || {};
|
||||
const { id: chatId } = chat || {};
|
||||
const { id: chatId, usernames: chatUsernames } = chat || {};
|
||||
const lang = useLang();
|
||||
|
||||
const [areNotificationsEnabled, setAreNotificationsEnabled] = useState(!isMuted);
|
||||
@ -70,6 +74,17 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
loadFullUser({ userId });
|
||||
}
|
||||
}, [loadFullUser, userId, lastSyncTime]);
|
||||
const activeUsernames = useMemo(() => {
|
||||
const result = usernames?.filter((u) => u.isActive);
|
||||
|
||||
return result?.length ? result : undefined;
|
||||
}, [usernames]);
|
||||
const activeChatUsernames = useMemo(() => {
|
||||
const result = chatUsernames?.filter((u) => u.isActive);
|
||||
|
||||
return result?.length ? result : undefined;
|
||||
}, [chatUsernames]);
|
||||
const link = useMemo(() => (chat ? getChatLink(chat) : undefined), [chat]);
|
||||
|
||||
const handleNotificationChange = useCallback(() => {
|
||||
setAreNotificationsEnabled((current) => {
|
||||
@ -93,9 +108,55 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
|
||||
const formattedNumber = phoneNumber && formatPhoneNumberWithCode(phoneCodeList, phoneNumber);
|
||||
const link = getChatLink(chat);
|
||||
const description = (fullInfo?.bio) || getChatDescription(chat);
|
||||
|
||||
function renderUsernames(usernameList: ApiUsername[], isChat?: boolean) {
|
||||
const [mainUsername, ...otherUsernames] = usernameList;
|
||||
const usernameLinks = otherUsernames.length
|
||||
? (lang('UsernameAlso', '%USERNAMES%') as string)
|
||||
.split('%')
|
||||
.map((s) => {
|
||||
return (s === 'USERNAMES' ? (
|
||||
<>
|
||||
{otherUsernames.map(({ username: nick }, idx) => (
|
||||
<>
|
||||
{idx > 0 ? ', ' : ''}
|
||||
<a
|
||||
key={nick}
|
||||
href={`${TME_LINK_PREFIX}${nick}`}
|
||||
onClick={(e) => {
|
||||
stopEvent(e);
|
||||
copy(`@${nick}`, lang(isChat ? 'Link' : 'Username'));
|
||||
}}
|
||||
className="username-link"
|
||||
>
|
||||
{`@${nick}`}
|
||||
</a>
|
||||
</>
|
||||
))}
|
||||
</>
|
||||
) : s);
|
||||
})
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
icon="mention"
|
||||
multiline
|
||||
narrow
|
||||
ripple
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => copy(`@${mainUsername.username}`, lang(isChat ? 'Link' : 'Username'))}
|
||||
>
|
||||
<span className="title" dir="auto">{renderText(mainUsername.username)}</span>
|
||||
<span className="subtitle">
|
||||
{usernameLinks && <span className="other-usernames">{usernameLinks}</span>}
|
||||
{lang(isChat ? 'Link' : 'Username')}
|
||||
</span>
|
||||
</ListItem>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ChatExtra">
|
||||
{formattedNumber && Boolean(formattedNumber.length) && (
|
||||
@ -105,19 +166,7 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
<span className="subtitle">{lang('Phone')}</span>
|
||||
</ListItem>
|
||||
)}
|
||||
{username && (
|
||||
<ListItem
|
||||
icon="mention"
|
||||
multiline
|
||||
narrow
|
||||
ripple
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => copy(`@${username}`, lang('Username'))}
|
||||
>
|
||||
<span className="title" dir="auto">{renderText(username)}</span>
|
||||
<span className="subtitle">{lang('Username')}</span>
|
||||
</ListItem>
|
||||
)}
|
||||
{activeUsernames && renderUsernames(activeUsernames)}
|
||||
{description && Boolean(description.length) && (
|
||||
<ListItem
|
||||
icon="info"
|
||||
@ -131,9 +180,10 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
<span className="subtitle">{lang(userId ? 'UserBio' : 'Info')}</span>
|
||||
</ListItem>
|
||||
)}
|
||||
{(canInviteUsers || !username) && link && (
|
||||
{activeChatUsernames && renderUsernames(activeChatUsernames, true)}
|
||||
{!activeChatUsernames && canInviteUsers && link && (
|
||||
<ListItem
|
||||
icon={chat.username ? 'mention' : 'link'}
|
||||
icon="link"
|
||||
multiline
|
||||
narrow
|
||||
ripple
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import type { MouseEvent as ReactMouseEvent } from 'react';
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React, { useEffect, useCallback, memo } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
useEffect, useCallback, memo, useMemo,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import type { ApiChat, ApiTypingStatus } from '../../api/types';
|
||||
@ -10,6 +12,7 @@ import { MediaViewerOrigin } from '../../types';
|
||||
|
||||
import {
|
||||
getChatTypeString,
|
||||
getMainUsername,
|
||||
isChatSuperGroup,
|
||||
} from '../../global/helpers';
|
||||
import { selectChat, selectChatMessages, selectChatOnlineCount } from '../../global/selectors';
|
||||
@ -91,6 +94,7 @@ const GroupChatInfo: FC<OwnProps & StateProps> = ({
|
||||
}, [chat, avatarSize, openMediaViewer]);
|
||||
|
||||
const lang = useLang();
|
||||
const mainUsername = useMemo(() => chat && withUsername && getMainUsername(chat), [chat, withUsername]);
|
||||
|
||||
if (!chat) {
|
||||
return undefined;
|
||||
@ -125,13 +129,12 @@ const GroupChatInfo: FC<OwnProps & StateProps> = ({
|
||||
);
|
||||
}
|
||||
|
||||
const handle = withUsername ? chat.username : undefined;
|
||||
const groupStatus = getGroupStatus(lang, chat);
|
||||
const onlineStatus = onlineCount ? `, ${lang('OnlineCount', onlineCount, 'i')}` : undefined;
|
||||
|
||||
return (
|
||||
<span className="status">
|
||||
{handle && <span className="handle">{handle}</span>}
|
||||
{mainUsername && <span className="handle">{mainUsername}</span>}
|
||||
<span className="group-status">{groupStatus}</span>
|
||||
{onlineStatus && <span className="online-status">{onlineStatus}</span>}
|
||||
</span>
|
||||
|
||||
41
src/components/common/ManageUsernames.module.scss
Normal file
41
src/components/common/ManageUsernames.module.scss
Normal file
@ -0,0 +1,41 @@
|
||||
.container {
|
||||
background-color: var(--color-background);
|
||||
padding: 1.5rem 1.5rem 0;
|
||||
box-shadow: inset 0 -0.0625rem 0 0 var(--color-background-secondary-accent);
|
||||
margin-bottom: 0.625rem;
|
||||
}
|
||||
|
||||
.header {
|
||||
font-size: 1rem;
|
||||
color: var(--color-text-secondary);
|
||||
margin-bottom: 2rem;
|
||||
position: relative;
|
||||
|
||||
&[dir="rtl"] {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-secondary);
|
||||
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 0;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.sortableContainer {
|
||||
position: relative;
|
||||
|
||||
:global(.draggable-knob) {
|
||||
margin-top: -0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
margin-bottom: 0;
|
||||
margin-left: -1rem;
|
||||
margin-right: -1rem;
|
||||
}
|
||||
213
src/components/common/ManageUsernames.tsx
Normal file
213
src/components/common/ManageUsernames.tsx
Normal file
@ -0,0 +1,213 @@
|
||||
import React, {
|
||||
memo, useCallback, useEffect, useMemo, useState,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions } from '../../global';
|
||||
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import type { ApiUsername } from '../../api/types';
|
||||
|
||||
import { copyTextToClipboard } from '../../util/clipboard';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { isBetween } from '../../util/math';
|
||||
import usePrevious from '../../hooks/usePrevious';
|
||||
import useLang from '../../hooks/useLang';
|
||||
|
||||
import Draggable from '../ui/Draggable';
|
||||
import ListItem from '../ui/ListItem';
|
||||
import ConfirmDialog from '../ui/ConfirmDialog';
|
||||
|
||||
import styles from './ManageUsernames.module.scss';
|
||||
|
||||
type SortState = {
|
||||
orderedUsernames?: string[];
|
||||
dragOrderUsernames?: string[];
|
||||
draggedIndex?: number;
|
||||
};
|
||||
|
||||
type OwnProps = {
|
||||
chatId?: string;
|
||||
usernames: ApiUsername[];
|
||||
onEditUsername: (username: string) => void;
|
||||
};
|
||||
|
||||
const USERNAME_HEIGHT_PX = 60;
|
||||
|
||||
const ManageUsernames: FC<OwnProps> = ({
|
||||
chatId,
|
||||
usernames,
|
||||
onEditUsername,
|
||||
}) => {
|
||||
const {
|
||||
showNotification,
|
||||
toggleUsername,
|
||||
toggleChatUsername,
|
||||
sortUsernames,
|
||||
sortChatUsernames,
|
||||
} = getActions();
|
||||
const lang = useLang();
|
||||
const [usernameForConfirm, setUsernameForConfirm] = useState<ApiUsername | undefined>();
|
||||
|
||||
const usernameList = useMemo(() => usernames.map(({ username }) => username), [usernames]);
|
||||
const prevUsernameList = usePrevious(usernameList);
|
||||
|
||||
const [state, setState] = useState<SortState>({
|
||||
orderedUsernames: usernameList,
|
||||
dragOrderUsernames: usernameList,
|
||||
draggedIndex: undefined,
|
||||
});
|
||||
|
||||
// Sync folders state after changing folders in other clients
|
||||
useEffect(() => {
|
||||
if (prevUsernameList !== usernameList) {
|
||||
setState({
|
||||
orderedUsernames: usernameList,
|
||||
dragOrderUsernames: usernameList,
|
||||
draggedIndex: undefined,
|
||||
});
|
||||
}
|
||||
}, [prevUsernameList, usernameList]);
|
||||
|
||||
const handleCopyUsername = useCallback((value: string) => {
|
||||
copyTextToClipboard(`@${value}`);
|
||||
showNotification({
|
||||
message: lang('UsernameCopied'),
|
||||
});
|
||||
}, [lang, showNotification]);
|
||||
|
||||
const handleUsernameClick = useCallback((data: ApiUsername) => {
|
||||
if (data.isEditable) {
|
||||
onEditUsername(data.username);
|
||||
} else {
|
||||
setUsernameForConfirm(data);
|
||||
}
|
||||
}, [onEditUsername]);
|
||||
|
||||
const closeConfirmUsernameDialog = useCallback(() => {
|
||||
setUsernameForConfirm(undefined);
|
||||
}, []);
|
||||
|
||||
const handleUsernameToggle = useCallback(() => {
|
||||
if (chatId) {
|
||||
toggleChatUsername({
|
||||
chatId,
|
||||
username: usernameForConfirm!.username,
|
||||
isActive: !usernameForConfirm!.isActive,
|
||||
});
|
||||
} else {
|
||||
toggleUsername({
|
||||
username: usernameForConfirm!.username,
|
||||
isActive: !usernameForConfirm!.isActive,
|
||||
});
|
||||
}
|
||||
closeConfirmUsernameDialog();
|
||||
}, [chatId, closeConfirmUsernameDialog, toggleChatUsername, toggleUsername, usernameForConfirm]);
|
||||
|
||||
const handleDrag = useCallback((translation: { x: number; y: number }, id: string | number) => {
|
||||
const delta = Math.round(translation.y / USERNAME_HEIGHT_PX);
|
||||
const index = state.orderedUsernames?.indexOf(id as string) || 0;
|
||||
const dragOrderUsernames = state.orderedUsernames?.filter((username) => username !== id);
|
||||
|
||||
if (!dragOrderUsernames || !isBetween(index + delta, 0, usernameList.length)) {
|
||||
return;
|
||||
}
|
||||
|
||||
dragOrderUsernames.splice(index + delta, 0, id as string);
|
||||
setState((current) => ({
|
||||
...current,
|
||||
draggedIndex: index,
|
||||
dragOrderUsernames,
|
||||
}));
|
||||
}, [state.orderedUsernames, usernameList.length]);
|
||||
|
||||
const handleDragEnd = useCallback(() => {
|
||||
setState((current) => {
|
||||
if (chatId) {
|
||||
sortChatUsernames({
|
||||
chatId,
|
||||
usernames: current.dragOrderUsernames!,
|
||||
});
|
||||
} else {
|
||||
sortUsernames({ usernames: current.dragOrderUsernames! });
|
||||
}
|
||||
|
||||
return {
|
||||
...current,
|
||||
orderedUsernames: current.dragOrderUsernames,
|
||||
draggedIndex: undefined,
|
||||
};
|
||||
});
|
||||
}, [chatId, sortChatUsernames, sortUsernames]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.container}>
|
||||
<h4 className={styles.header} dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
{lang('lng_usernames_subtitle')}
|
||||
</h4>
|
||||
<div className={styles.sortableContainer} style={`height: ${(usernames.length) * USERNAME_HEIGHT_PX}px`}>
|
||||
{usernames.map((usernameData, i) => {
|
||||
const isDragged = state.draggedIndex === i;
|
||||
const draggedTop = (state.orderedUsernames?.indexOf(usernameData.username) ?? 0) * USERNAME_HEIGHT_PX;
|
||||
const top = (state.dragOrderUsernames?.indexOf(usernameData.username) ?? 0) * USERNAME_HEIGHT_PX;
|
||||
const subtitle = usernameData.isEditable
|
||||
? 'lng_usernames_edit'
|
||||
: (usernameData.isActive ? 'lng_usernames_active' : 'lng_usernames_non_active');
|
||||
|
||||
return (
|
||||
<Draggable
|
||||
key={usernameData.username}
|
||||
id={usernameData.username}
|
||||
onDrag={handleDrag}
|
||||
onDragEnd={handleDragEnd}
|
||||
style={`top: ${isDragged ? draggedTop : top}px;`}
|
||||
knobStyle={`${lang.isRtl ? 'left' : 'right'}: 3rem;`}
|
||||
isDisabled={!usernameData.isActive}
|
||||
>
|
||||
<ListItem
|
||||
key={usernameData.username}
|
||||
className={buildClassName('mb-2 no-icon', styles.item)}
|
||||
narrow
|
||||
secondaryIcon="more"
|
||||
icon={usernameData.isActive ? 'link' : 'link-broken'}
|
||||
multiline
|
||||
contextActions={[
|
||||
{
|
||||
handler: () => {
|
||||
handleCopyUsername(usernameData.username);
|
||||
},
|
||||
title: lang('Copy'),
|
||||
icon: 'copy',
|
||||
},
|
||||
]}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => {
|
||||
handleUsernameClick(usernameData);
|
||||
}}
|
||||
>
|
||||
<span className="title">@{usernameData.username}</span>
|
||||
<span className="subtitle">{lang(subtitle)}</span>
|
||||
</ListItem>
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<p className={styles.description} dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
{lang('lng_usernames_description')}
|
||||
</p>
|
||||
</div>
|
||||
<ConfirmDialog
|
||||
isOpen={Boolean(usernameForConfirm)}
|
||||
onClose={closeConfirmUsernameDialog}
|
||||
title={lang(usernameForConfirm?.isActive ? 'Username.DeactivateAlertTitle' : 'Username.ActivateAlertTitle')}
|
||||
text={lang(usernameForConfirm?.isActive ? 'Username.DeactivateAlertText' : 'Username.ActivateAlertText')}
|
||||
confirmLabel={lang(usernameForConfirm?.isActive
|
||||
? 'Username.DeactivateAlertHide'
|
||||
: 'Username.ActivateAlertShow')}
|
||||
confirmHandler={handleUsernameToggle}
|
||||
confirmIsDestructive={!usernameForConfirm?.isActive}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ManageUsernames);
|
||||
@ -1,4 +1,6 @@
|
||||
import React, { useEffect, useCallback, memo } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
useEffect, useCallback, memo, useMemo,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
@ -10,7 +12,7 @@ import type { AnimationLevel } from '../../types';
|
||||
import { MediaViewerOrigin } from '../../types';
|
||||
|
||||
import { selectChatMessages, selectUser, selectUserStatus } from '../../global/selectors';
|
||||
import { getUserStatus, isUserOnline } from '../../global/helpers';
|
||||
import { getMainUsername, getUserStatus, isUserOnline } from '../../global/helpers';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import renderText from './helpers/renderText';
|
||||
|
||||
@ -99,6 +101,7 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
}, [user, avatarSize, openMediaViewer]);
|
||||
|
||||
const lang = useLang();
|
||||
const mainUsername = useMemo(() => user && withUsername && getMainUsername(user), [user, withUsername]);
|
||||
|
||||
if (!user) {
|
||||
return undefined;
|
||||
@ -129,7 +132,7 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<span className={buildClassName('status', isUserOnline(user, userStatus) && 'online')}>
|
||||
{withUsername && user.username && <span className="handle">{user.username}</span>}
|
||||
{mainUsername && <span className="handle">{mainUsername}</span>}
|
||||
<span className="user-status" dir="auto">{getUserStatus(lang, user, userStatus, serverTimeOffset)}</span>
|
||||
</span>
|
||||
);
|
||||
|
||||
@ -25,7 +25,7 @@ type OwnProps = {
|
||||
const MIN_USERNAME_LENGTH = 5;
|
||||
const MAX_USERNAME_LENGTH = 32;
|
||||
const LINK_PREFIX_REGEX = /https:\/\/t\.me\/?/i;
|
||||
const USERNAME_REGEX = /^[^\d]([a-zA-Z0-9_]+)$/;
|
||||
const USERNAME_REGEX = /^\D([a-zA-Z0-9_]+)$/;
|
||||
|
||||
const runDebouncedForCheckUsername = debounce((cb) => cb(), 250, false);
|
||||
|
||||
|
||||
@ -1,21 +1,22 @@
|
||||
import type { ChangeEvent } from 'react';
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, {
|
||||
useState, useCallback, memo, useEffect, useMemo,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import type { ApiUsername } from '../../../api/types';
|
||||
import { ApiMediaFormat } from '../../../api/types';
|
||||
import { ProfileEditProgress } from '../../../types';
|
||||
|
||||
import { TME_LINK_PREFIX } from '../../../config';
|
||||
import { PURCHASE_USERNAME, TME_LINK_PREFIX, USERNAME_PURCHASE_ERROR } from '../../../config';
|
||||
import { throttle } from '../../../util/schedulers';
|
||||
import { selectUser } from '../../../global/selectors';
|
||||
import { getChatAvatarHash } from '../../../global/helpers';
|
||||
import useMedia from '../../../hooks/useMedia';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import { selectCurrentLimit } from '../../../global/selectors/limits';
|
||||
import renderText from '../../common/helpers/renderText';
|
||||
import useMedia from '../../../hooks/useMedia';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
import usePrevious from '../../../hooks/usePrevious';
|
||||
|
||||
@ -25,6 +26,8 @@ import Spinner from '../../ui/Spinner';
|
||||
import InputText from '../../ui/InputText';
|
||||
import UsernameInput from '../../common/UsernameInput';
|
||||
import TextArea from '../../ui/TextArea';
|
||||
import ManageUsernames from '../../common/ManageUsernames';
|
||||
import SafeLink from '../../common/SafeLink';
|
||||
|
||||
type OwnProps = {
|
||||
isActive: boolean;
|
||||
@ -36,11 +39,12 @@ type StateProps = {
|
||||
currentFirstName?: string;
|
||||
currentLastName?: string;
|
||||
currentBio?: string;
|
||||
currentUsername?: string;
|
||||
progress?: ProfileEditProgress;
|
||||
checkedUsername?: string;
|
||||
editUsernameError?: string;
|
||||
isUsernameAvailable?: boolean;
|
||||
maxBioLength: number;
|
||||
usernames?: ApiUsername[];
|
||||
};
|
||||
|
||||
const runThrottled = throttle((cb) => cb(), 60000, true);
|
||||
@ -53,11 +57,12 @@ const SettingsEditProfile: FC<OwnProps & StateProps> = ({
|
||||
currentFirstName,
|
||||
currentLastName,
|
||||
currentBio,
|
||||
currentUsername,
|
||||
progress,
|
||||
checkedUsername,
|
||||
editUsernameError,
|
||||
isUsernameAvailable,
|
||||
maxBioLength,
|
||||
usernames,
|
||||
onReset,
|
||||
}) => {
|
||||
const {
|
||||
@ -67,6 +72,8 @@ const SettingsEditProfile: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const lang = useLang();
|
||||
|
||||
const firstEditableUsername = useMemo(() => usernames?.find(({ isEditable }) => isEditable), [usernames]);
|
||||
const currentUsername = firstEditableUsername?.username || '';
|
||||
const [isUsernameTouched, setIsUsernameTouched] = useState(false);
|
||||
const [isProfileFieldsTouched, setIsProfileFieldsTouched] = useState(false);
|
||||
const [error, setError] = useState<string | undefined>();
|
||||
@ -75,15 +82,16 @@ const SettingsEditProfile: FC<OwnProps & StateProps> = ({
|
||||
const [firstName, setFirstName] = useState(currentFirstName || '');
|
||||
const [lastName, setLastName] = useState(currentLastName || '');
|
||||
const [bio, setBio] = useState(currentBio || '');
|
||||
const [username, setUsername] = useState<string | false>(currentUsername || '');
|
||||
const [editableUsername, setEditableUsername] = useState<string | false>(currentUsername);
|
||||
|
||||
const currentAvatarBlobUrl = useMedia(currentAvatarHash, false, ApiMediaFormat.BlobUrl);
|
||||
|
||||
const isLoading = progress === ProfileEditProgress.InProgress;
|
||||
const isUsernameError = username === false;
|
||||
const isUsernameError = editableUsername === false;
|
||||
|
||||
const previousIsUsernameAvailable = usePrevious(isUsernameAvailable);
|
||||
const renderingIsUsernameAvailable = isUsernameAvailable ?? previousIsUsernameAvailable;
|
||||
const shouldRenderUsernamesManage = usernames && usernames.length > 1;
|
||||
|
||||
const isSaveButtonShown = useMemo(() => {
|
||||
if (isUsernameError) {
|
||||
@ -117,7 +125,7 @@ const SettingsEditProfile: FC<OwnProps & StateProps> = ({
|
||||
}, [currentFirstName, currentLastName, currentBio]);
|
||||
|
||||
useEffect(() => {
|
||||
setUsername(currentUsername || '');
|
||||
setEditableUsername(currentUsername || '');
|
||||
}, [currentUsername]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -148,7 +156,7 @@ const SettingsEditProfile: FC<OwnProps & StateProps> = ({
|
||||
}, []);
|
||||
|
||||
const handleUsernameChange = useCallback((value: string | false) => {
|
||||
setUsername(value);
|
||||
setEditableUsername(value);
|
||||
setIsUsernameTouched(currentUsername !== value);
|
||||
}, [currentUsername]);
|
||||
|
||||
@ -170,16 +178,31 @@ const SettingsEditProfile: FC<OwnProps & StateProps> = ({
|
||||
bio: trimmedBio,
|
||||
}),
|
||||
...(isUsernameTouched && {
|
||||
username,
|
||||
username: editableUsername,
|
||||
}),
|
||||
});
|
||||
}, [
|
||||
photo,
|
||||
firstName, lastName, bio, isProfileFieldsTouched,
|
||||
username, isUsernameTouched,
|
||||
editableUsername, isUsernameTouched,
|
||||
updateProfile,
|
||||
]);
|
||||
|
||||
function renderPurchaseLink() {
|
||||
const purchaseInfoLink = `${TME_LINK_PREFIX}${PURCHASE_USERNAME}`;
|
||||
|
||||
return (
|
||||
<p className="settings-item-description" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
{(lang('lng_username_purchase_available') as string)
|
||||
.replace('{link}', '%PURCHASE_LINK%')
|
||||
.split('%')
|
||||
.map((s) => {
|
||||
return (s === 'PURCHASE_LINK' ? <SafeLink url={purchaseInfoLink} text={`@${PURCHASE_USERNAME}`} /> : s);
|
||||
})}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="settings-fab-wrapper">
|
||||
<div className="settings-content no-border custom-scroll">
|
||||
@ -228,16 +251,24 @@ const SettingsEditProfile: FC<OwnProps & StateProps> = ({
|
||||
onChange={handleUsernameChange}
|
||||
/>
|
||||
|
||||
{editUsernameError === USERNAME_PURCHASE_ERROR && renderPurchaseLink()}
|
||||
<p className="settings-item-description" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
{renderText(lang('UsernameHelp'), ['br', 'simple_markdown'])}
|
||||
</p>
|
||||
{username && (
|
||||
{editableUsername && (
|
||||
<p className="settings-item-description" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
{lang('lng_username_link')}<br />
|
||||
<span className="username-link">{TME_LINK_PREFIX}{username}</span>
|
||||
<span className="username-link">{TME_LINK_PREFIX}{editableUsername}</span>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{shouldRenderUsernamesManage && (
|
||||
<ManageUsernames
|
||||
usernames={usernames}
|
||||
onEditUsername={setEditableUsername}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<FloatingActionButton
|
||||
@ -259,7 +290,9 @@ const SettingsEditProfile: FC<OwnProps & StateProps> = ({
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global): StateProps => {
|
||||
const { currentUserId } = global;
|
||||
const { progress, isUsernameAvailable, checkedUsername } = global.profileEdit || {};
|
||||
const {
|
||||
progress, isUsernameAvailable, checkedUsername, error: editUsernameError,
|
||||
} = global.profileEdit || {};
|
||||
const currentUser = currentUserId ? selectUser(global, currentUserId) : undefined;
|
||||
|
||||
const maxBioLength = selectCurrentLimit(global, 'aboutLength');
|
||||
@ -269,6 +302,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
progress,
|
||||
checkedUsername,
|
||||
isUsernameAvailable,
|
||||
editUsernameError,
|
||||
maxBioLength,
|
||||
};
|
||||
}
|
||||
@ -276,7 +310,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const {
|
||||
firstName: currentFirstName,
|
||||
lastName: currentLastName,
|
||||
username: currentUsername,
|
||||
usernames,
|
||||
fullInfo,
|
||||
} = currentUser;
|
||||
const { bio: currentBio } = fullInfo || {};
|
||||
@ -287,11 +321,12 @@ export default memo(withGlobal<OwnProps>(
|
||||
currentFirstName,
|
||||
currentLastName,
|
||||
currentBio,
|
||||
currentUsername,
|
||||
progress,
|
||||
isUsernameAvailable,
|
||||
checkedUsername,
|
||||
editUsernameError,
|
||||
maxBioLength,
|
||||
usernames,
|
||||
};
|
||||
},
|
||||
)(SettingsEditProfile));
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, { memo, useCallback } from '../../../lib/teact/teact';
|
||||
import React, { memo, useCallback, useMemo } from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiChat, ApiCountryCode, ApiUser } from '../../../api/types';
|
||||
|
||||
import { CHAT_HEIGHT_PX } from '../../../config';
|
||||
import { formatPhoneNumberWithCode } from '../../../util/phoneNumber';
|
||||
import {
|
||||
isUserId,
|
||||
} from '../../../global/helpers';
|
||||
import { getMainUsername, isUserId } from '../../../global/helpers';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
@ -54,6 +52,20 @@ const SettingsPrivacyBlockedUsers: FC<OwnProps & StateProps> = ({
|
||||
onBack: onReset,
|
||||
});
|
||||
|
||||
const blockedUsernamesById = useMemo(() => {
|
||||
return blockedIds.reduce((acc, contactId) => {
|
||||
const isPrivate = isUserId(contactId);
|
||||
const user = isPrivate ? usersByIds[contactId] : undefined;
|
||||
const mainUsername = user && !user.phoneNumber && getMainUsername(user);
|
||||
|
||||
if (mainUsername) {
|
||||
acc[contactId] = mainUsername;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {} as Record<string, string>);
|
||||
}, [blockedIds, usersByIds]);
|
||||
|
||||
function renderContact(contactId: string, i: number, viewportOffset: number) {
|
||||
const isPrivate = isUserId(contactId);
|
||||
const user = isPrivate ? usersByIds[contactId] : undefined;
|
||||
@ -65,6 +77,8 @@ const SettingsPrivacyBlockedUsers: FC<OwnProps & StateProps> = ({
|
||||
isPrivate ? 'private' : 'group',
|
||||
);
|
||||
|
||||
const userMainUsername = blockedUsernamesById[contactId];
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
key={contactId}
|
||||
@ -86,9 +100,7 @@ const SettingsPrivacyBlockedUsers: FC<OwnProps & StateProps> = ({
|
||||
{user?.phoneNumber && (
|
||||
<div className="contact-phone" dir="auto">{formatPhoneNumberWithCode(phoneCodeList, user.phoneNumber)}</div>
|
||||
)}
|
||||
{user && !user.phoneNumber && user.username && (
|
||||
<div className="contact-username" dir="auto">@{user.username}</div>
|
||||
)}
|
||||
{userMainUsername && (<div className="contact-username" dir="auto">@{userMainUsername}</div>)}
|
||||
</div>
|
||||
</ListItem>
|
||||
);
|
||||
|
||||
@ -10,6 +10,7 @@ import { ALL_FOLDER_ID, STICKER_SIZE_FOLDER_SETTINGS } from '../../../../config'
|
||||
import { LOCAL_TGS_URLS } from '../../../common/helpers/animatedAssets';
|
||||
import { MEMO_EMPTY_ARRAY } from '../../../../util/memo';
|
||||
import { throttle } from '../../../../util/schedulers';
|
||||
import { isBetween } from '../../../../util/math';
|
||||
import { getFolderDescriptionText } from '../../../../global/helpers';
|
||||
import { selectCurrentLimit } from '../../../../global/selectors/limits';
|
||||
import { selectIsCurrentUserPremium } from '../../../../global/selectors';
|
||||
@ -154,16 +155,16 @@ const SettingsFoldersMain: FC<OwnProps & StateProps> = ({
|
||||
addChatFolder({ folder });
|
||||
}, [foldersById, maxFolders, addChatFolder, openLimitReachedModal]);
|
||||
|
||||
const handleDrag = useCallback((translation: { x: number; y: number }, id: number) => {
|
||||
const handleDrag = useCallback((translation: { x: number; y: number }, id: string | number) => {
|
||||
const delta = Math.round(translation.y / FOLDER_HEIGHT_PX);
|
||||
const index = state.orderedFolderIds?.indexOf(id) || 0;
|
||||
const index = state.orderedFolderIds?.indexOf(id as number) || 0;
|
||||
const dragOrderIds = state.orderedFolderIds?.filter((folderId) => folderId !== id);
|
||||
|
||||
if (!dragOrderIds || !inRange(index + delta, 0, folderIds?.length || 0)) {
|
||||
if (!dragOrderIds || !isBetween(index + delta, 0, folderIds?.length || 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
dragOrderIds.splice(index + delta + (isPremium ? 0 : 1), 0, id);
|
||||
dragOrderIds.splice(index + delta + (isPremium ? 0 : 1), 0, id as number);
|
||||
setState((current) => ({
|
||||
...current,
|
||||
draggedIndex: index,
|
||||
@ -362,7 +363,3 @@ export default memo(withGlobal<OwnProps>(
|
||||
};
|
||||
},
|
||||
)(SettingsFoldersMain));
|
||||
|
||||
function inRange(x: number, min: number, max: number) {
|
||||
return x >= min && x <= max;
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ const BotCommandTooltip: FC<OwnProps & StateProps> = ({
|
||||
const handleSendCommand = useCallback(({ botId, command }: ApiBotCommand) => {
|
||||
const bot = usersById[botId];
|
||||
sendBotCommand({
|
||||
command: `/${command}${withUsername && bot ? `@${bot.username}` : ''}`,
|
||||
command: `/${command}${withUsername && bot ? `@${bot.usernames![0].username}` : ''}`,
|
||||
botId,
|
||||
});
|
||||
onClick();
|
||||
|
||||
@ -6,7 +6,7 @@ import { getGlobal } from '../../../../global';
|
||||
import type { ApiChatMember, ApiUser } from '../../../../api/types';
|
||||
import { ApiMessageEntityTypes } from '../../../../api/types';
|
||||
|
||||
import { filterUsersByName, getUserFirstOrLastName } from '../../../../global/helpers';
|
||||
import { filterUsersByName, getMainUsername, getUserFirstOrLastName } from '../../../../global/helpers';
|
||||
import { prepareForRegExp } from '../helpers/prepareForRegExp';
|
||||
import focusEditableElement from '../../../../util/focusEditableElement';
|
||||
import { pickTruthy, unique } from '../../../../util/iteratees';
|
||||
@ -106,12 +106,13 @@ export default function useMentionTooltip(
|
||||
}, [markIsOpen, unmarkIsOpen, usersToMention]);
|
||||
|
||||
const insertMention = useCallback((user: ApiUser, forceFocus = false) => {
|
||||
if (!user.username && !getUserFirstOrLastName(user)) {
|
||||
if (!user.usernames && !getUserFirstOrLastName(user)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const insertedHtml = user.username
|
||||
? `@${user.username}`
|
||||
const mainUsername = getMainUsername(user);
|
||||
const insertedHtml = mainUsername
|
||||
? `@${mainUsername}`
|
||||
: `<a
|
||||
class="text-entity-link"
|
||||
data-entity-type="${ApiMessageEntityTypes.MentionName}"
|
||||
|
||||
@ -18,6 +18,7 @@ import type {
|
||||
ApiThreadInfo,
|
||||
ApiAvailableReaction,
|
||||
ApiChatMember,
|
||||
ApiUsername,
|
||||
} from '../../../api/types';
|
||||
import type {
|
||||
AnimationLevel, FocusDirection, IAlbum, ISettings,
|
||||
@ -164,7 +165,7 @@ type OwnProps =
|
||||
type StateProps = {
|
||||
theme: ISettings['theme'];
|
||||
forceSenderName?: boolean;
|
||||
chatUsername?: string;
|
||||
chatUsernames?: ApiUsername[];
|
||||
sender?: ApiUser | ApiChat;
|
||||
canShowSender: boolean;
|
||||
originSender?: ApiUser | ApiChat;
|
||||
@ -238,7 +239,7 @@ const NO_MEDIA_CORNERS_THRESHOLD = 18;
|
||||
|
||||
const Message: FC<OwnProps & StateProps> = ({
|
||||
message,
|
||||
chatUsername,
|
||||
chatUsernames,
|
||||
observeIntersectionForBottom,
|
||||
observeIntersectionForLoading,
|
||||
observeIntersectionForPlaying,
|
||||
@ -990,7 +991,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
className="interactive"
|
||||
onClick={handleViaBotClick}
|
||||
>
|
||||
{renderText(`@${botSender.username}`)}
|
||||
{renderText(`@${botSender.usernames![0].username}`)}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
@ -1012,6 +1013,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
|
||||
const forwardAuthor = isGroup && asForwarded ? message.postAuthorTitle : undefined;
|
||||
const chatUsername = useMemo(() => chatUsernames?.find((c) => c.isActive), [chatUsernames]);
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -1123,7 +1125,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
anchor={contextMenuPosition}
|
||||
message={message}
|
||||
album={album}
|
||||
chatUsername={chatUsername}
|
||||
chatUsername={chatUsername?.username}
|
||||
messageListType={messageListType}
|
||||
onClose={handleContextMenuClose}
|
||||
onCloseAnimationEnd={handleContextMenuHide}
|
||||
@ -1150,7 +1152,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const isRepliesChat = isChatWithRepliesBot(chatId);
|
||||
const isChannel = chat && isChatChannel(chat);
|
||||
const isGroup = chat && isChatGroup(chat);
|
||||
const chatUsername = chat?.username;
|
||||
const chatUsernames = chat?.usernames;
|
||||
|
||||
const isForwarding = forwardMessages.messageIds && forwardMessages.messageIds.includes(id);
|
||||
const forceSenderName = !isChatWithSelf && isAnonymousOwnMessage(message);
|
||||
@ -1215,7 +1217,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
return {
|
||||
theme: selectTheme(global),
|
||||
chatUsername,
|
||||
chatUsernames,
|
||||
forceSenderName,
|
||||
sender,
|
||||
canShowSender,
|
||||
|
||||
@ -391,7 +391,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
default:
|
||||
return (
|
||||
<>
|
||||
<h3>Profile</h3>
|
||||
<h3>{lang(isChannel ? 'Channel.TitleInfo' : (userId ? 'UserInfo.Title' : 'GroupInfo.Title'))}</h3>
|
||||
<section className="tools">
|
||||
{canAddContact && (
|
||||
<Button
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { ChangeEvent } from 'react';
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useCallback, useEffect, useState,
|
||||
memo, useCallback, useEffect, useMemo, useState,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
@ -9,7 +9,7 @@ import { ManagementScreens, ManagementProgress } from '../../../types';
|
||||
import type { ApiChat, ApiExportedInvite } from '../../../api/types';
|
||||
import { ApiMediaFormat } from '../../../api/types';
|
||||
|
||||
import { getChatAvatarHash, getHasAdminRight } from '../../../global/helpers';
|
||||
import { getChatAvatarHash, getHasAdminRight, isChatPublic } from '../../../global/helpers';
|
||||
import useMedia from '../../../hooks/useMedia';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import { selectChat } from '../../../global/selectors';
|
||||
@ -192,6 +192,7 @@ const ManageChannel: FC<OwnProps & StateProps> = ({
|
||||
}, [chat.isCreator, chat.id, closeDeleteDialog, closeManagement, leaveChannel, deleteChannel, openChat]);
|
||||
|
||||
const enabledReactionsCount = chat.fullInfo?.enabledReactions?.length || 0;
|
||||
const isChannelPublic = useMemo(() => isChatPublic(chat), [chat]);
|
||||
|
||||
if (chat.isRestricted || chat.isForbidden) {
|
||||
return undefined;
|
||||
@ -229,7 +230,7 @@ const ManageChannel: FC<OwnProps & StateProps> = ({
|
||||
{chat.isCreator && (
|
||||
<ListItem icon="lock" multiline onClick={handleClickEditType}>
|
||||
<span className="title">{lang('ChannelType')}</span>
|
||||
<span className="subtitle">{chat.username ? lang('TypePublic') : lang('TypePrivate')}</span>
|
||||
<span className="subtitle">{isChannelPublic ? lang('TypePublic') : lang('TypePrivate')}</span>
|
||||
</ListItem>
|
||||
)}
|
||||
<ListItem
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
import type { ChangeEvent } from 'react';
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useCallback, useEffect, useState,
|
||||
memo, useCallback, useEffect, useMemo, useState,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, getGlobal, withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiChat } from '../../../api/types';
|
||||
import { ManagementProgress } from '../../../types';
|
||||
|
||||
import { PURCHASE_USERNAME, TME_LINK_PREFIX, USERNAME_PURCHASE_ERROR } from '../../../config';
|
||||
import { selectChat, selectManagement } from '../../../global/selectors';
|
||||
import { isChatChannel } from '../../../global/helpers';
|
||||
import { isChatChannel, isChatPublic } from '../../../global/helpers';
|
||||
import { selectCurrentLimit } from '../../../global/selectors/limits';
|
||||
|
||||
import useFlag from '../../../hooks/useFlag';
|
||||
@ -25,6 +26,7 @@ import Spinner from '../../ui/Spinner';
|
||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||
import UsernameInput from '../../common/UsernameInput';
|
||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||
import ManageUsernames from '../../common/ManageUsernames';
|
||||
|
||||
type PrivacyType = 'private' | 'public';
|
||||
|
||||
@ -40,6 +42,7 @@ type StateProps = {
|
||||
progress?: ManagementProgress;
|
||||
isUsernameAvailable?: boolean;
|
||||
checkedUsername?: string;
|
||||
error?: string;
|
||||
isProtected?: boolean;
|
||||
maxPublicLinks: number;
|
||||
};
|
||||
@ -51,6 +54,7 @@ const ManageChatPrivacyType: FC<OwnProps & StateProps> = ({
|
||||
progress,
|
||||
isUsernameAvailable,
|
||||
checkedUsername,
|
||||
error,
|
||||
isProtected,
|
||||
maxPublicLinks,
|
||||
onClose,
|
||||
@ -62,18 +66,21 @@ const ManageChatPrivacyType: FC<OwnProps & StateProps> = ({
|
||||
openLimitReachedModal,
|
||||
} = getActions();
|
||||
|
||||
const isPublic = Boolean(chat.username);
|
||||
const firstEditableUsername = useMemo(() => chat.usernames?.find(({ isEditable }) => isEditable), [chat.usernames]);
|
||||
const currentUsername = firstEditableUsername?.username || '';
|
||||
const isPublic = useMemo(() => isChatPublic(chat), [chat]);
|
||||
const privateLink = chat.fullInfo?.inviteLink;
|
||||
|
||||
const [privacyType, setPrivacyType] = useState<PrivacyType>(isPublic ? 'public' : 'private');
|
||||
const [username, setUsername] = useState();
|
||||
const [editableUsername, setEditableUsername] = useState();
|
||||
const [isRevokeConfirmDialogOpen, openRevokeConfirmDialog, closeRevokeConfirmDialog] = useFlag();
|
||||
const [isUsernameLostDialogOpen, openUsernameLostDialog, closeUsernameLostDialog] = useFlag();
|
||||
|
||||
const previousIsUsernameAvailable = usePrevious(isUsernameAvailable);
|
||||
const renderingIsUsernameAvailable = isUsernameAvailable ?? previousIsUsernameAvailable;
|
||||
|
||||
const canUpdate = Boolean(
|
||||
(privacyType === 'public' && username && renderingIsUsernameAvailable)
|
||||
(privacyType === 'public' && editableUsername && renderingIsUsernameAvailable)
|
||||
|| (privacyType === 'private' && isPublic),
|
||||
);
|
||||
|
||||
@ -89,7 +96,9 @@ const ManageChatPrivacyType: FC<OwnProps & StateProps> = ({
|
||||
}, [privacyType, privateLink, updatePrivateLink]);
|
||||
|
||||
const handleOptionChange = useCallback((value: string, e: ChangeEvent<HTMLInputElement>) => {
|
||||
const myChats = Object.values(getGlobal().chats.byId).filter((l) => l.isCreator && l.username);
|
||||
const myChats = Object.values(getGlobal().chats.byId)
|
||||
.filter((l) => l.isCreator && l.usernames?.some((c) => c.isActive));
|
||||
|
||||
if (myChats.length >= maxPublicLinks && value === 'public') {
|
||||
openLimitReachedModal({ limit: 'channelsPublic' });
|
||||
const radioGroup = e.currentTarget.closest('.radio-group') as HTMLDivElement;
|
||||
@ -110,8 +119,17 @@ const ManageChatPrivacyType: FC<OwnProps & StateProps> = ({
|
||||
}, [chat.id, toggleIsProtected]);
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
updatePublicLink({ username: privacyType === 'public' ? username : '' });
|
||||
}, [privacyType, updatePublicLink, username]);
|
||||
if (isPublic && privacyType === 'private') {
|
||||
openUsernameLostDialog();
|
||||
} else {
|
||||
updatePublicLink({ username: privacyType === 'public' ? editableUsername : '' });
|
||||
}
|
||||
}, [isPublic, openUsernameLostDialog, privacyType, updatePublicLink, editableUsername]);
|
||||
|
||||
const handleMakeChannelPrivateConfirm = useCallback(() => {
|
||||
updatePublicLink({ username: '' });
|
||||
closeUsernameLostDialog();
|
||||
}, [closeUsernameLostDialog, updatePublicLink]);
|
||||
|
||||
const handleRevokePrivateLink = useCallback(() => {
|
||||
closeRevokeConfirmDialog();
|
||||
@ -136,6 +154,22 @@ const ManageChatPrivacyType: FC<OwnProps & StateProps> = ({
|
||||
}];
|
||||
|
||||
const isLoading = progress === ManagementProgress.InProgress;
|
||||
const shouldRenderUsernamesManage = privacyType === 'public' && chat.usernames && chat.usernames.length > 1;
|
||||
|
||||
function renderPurchaseLink() {
|
||||
const purchaseInfoLink = `${TME_LINK_PREFIX}${PURCHASE_USERNAME}`;
|
||||
|
||||
return (
|
||||
<p className="section-info" dir="auto">
|
||||
{(lang('lng_username_purchase_available') as string)
|
||||
.replace('{link}', '%PURCHASE_LINK%')
|
||||
.split('%')
|
||||
.map((s) => {
|
||||
return (s === 'PURCHASE_LINK' ? <SafeLink url={purchaseInfoLink} text={`@${PURCHASE_USERNAME}`} /> : s);
|
||||
})}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="Management">
|
||||
@ -178,17 +212,25 @@ const ManageChatPrivacyType: FC<OwnProps & StateProps> = ({
|
||||
<div className="section no-border">
|
||||
<UsernameInput
|
||||
asLink
|
||||
currentUsername={chat.username}
|
||||
currentUsername={currentUsername}
|
||||
isLoading={isLoading}
|
||||
isUsernameAvailable={isUsernameAvailable}
|
||||
checkedUsername={checkedUsername}
|
||||
onChange={setUsername}
|
||||
onChange={setEditableUsername}
|
||||
/>
|
||||
{error === USERNAME_PURCHASE_ERROR && renderPurchaseLink()}
|
||||
<p className="section-info" dir="auto">
|
||||
{lang(`${langPrefix2}.Username.CreatePublicLinkHelp`)}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{shouldRenderUsernamesManage && (
|
||||
<ManageUsernames
|
||||
chatId={chat.id}
|
||||
usernames={chat.usernames!}
|
||||
onEditUsername={setEditableUsername}
|
||||
/>
|
||||
)}
|
||||
<div className="section" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
<h3 className="section-heading">
|
||||
{lang(isChannel ? 'ChannelVisibility.Forwarding.ChannelTitle' : 'ChannelVisibility.Forwarding.GroupTitle')}
|
||||
@ -218,6 +260,13 @@ const ManageChatPrivacyType: FC<OwnProps & StateProps> = ({
|
||||
<i className="icon-check" />
|
||||
)}
|
||||
</FloatingActionButton>
|
||||
<ConfirmDialog
|
||||
isOpen={isUsernameLostDialogOpen}
|
||||
onClose={closeUsernameLostDialog}
|
||||
text={lang('ChannelVisibility.Confirm.MakePrivate.Channel', currentUsername)}
|
||||
confirmHandler={handleMakeChannelPrivateConfirm}
|
||||
confirmIsDestructive
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -225,12 +274,13 @@ const ManageChatPrivacyType: FC<OwnProps & StateProps> = ({
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global, { chatId }): StateProps => {
|
||||
const chat = selectChat(global, chatId)!;
|
||||
const { isUsernameAvailable, checkedUsername } = selectManagement(global, chatId)!;
|
||||
const { isUsernameAvailable, checkedUsername, error } = selectManagement(global, chatId)!;
|
||||
|
||||
return {
|
||||
chat,
|
||||
isChannel: isChatChannel(chat),
|
||||
progress: global.management.progress,
|
||||
error,
|
||||
isUsernameAvailable,
|
||||
checkedUsername,
|
||||
isProtected: chat?.isProtected,
|
||||
|
||||
@ -9,7 +9,12 @@ import { ManagementScreens, ManagementProgress } from '../../../types';
|
||||
import type { ApiChat, ApiChatBannedRights, ApiExportedInvite } from '../../../api/types';
|
||||
import { ApiMediaFormat } from '../../../api/types';
|
||||
|
||||
import { getChatAvatarHash, getHasAdminRight, isChatBasicGroup } from '../../../global/helpers';
|
||||
import {
|
||||
getChatAvatarHash,
|
||||
getHasAdminRight,
|
||||
isChatBasicGroup,
|
||||
isChatPublic,
|
||||
} from '../../../global/helpers';
|
||||
import useMedia from '../../../hooks/useMedia';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useFlag from '../../../hooks/useFlag';
|
||||
@ -97,7 +102,7 @@ const ManageGroup: FC<OwnProps & StateProps> = ({
|
||||
const [error, setError] = useState<string | undefined>();
|
||||
const imageHash = getChatAvatarHash(chat);
|
||||
const currentAvatarBlobUrl = useMedia(imageHash, false, ApiMediaFormat.BlobUrl);
|
||||
const isPublicGroup = chat.username || hasLinkedChannel;
|
||||
const isPublicGroup = useMemo(() => hasLinkedChannel || isChatPublic(chat), [chat, hasLinkedChannel]);
|
||||
const lang = useLang();
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const isPreHistoryHiddenCheckboxRef = useRef<HTMLDivElement>(null);
|
||||
@ -292,7 +297,7 @@ const ManageGroup: FC<OwnProps & StateProps> = ({
|
||||
{chat.isCreator && (
|
||||
<ListItem icon="lock" multiline onClick={handleClickEditType}>
|
||||
<span className="title">{lang('GroupType')}</span>
|
||||
<span className="subtitle">{chat.username ? lang('TypePublic') : lang('TypePrivate')}</span>
|
||||
<span className="subtitle">{isPublicGroup ? lang('TypePublic') : lang('TypePrivate')}</span>
|
||||
</ListItem>
|
||||
)}
|
||||
{hasLinkedChannel && (
|
||||
|
||||
@ -19,7 +19,7 @@ import { copyTextToClipboard } from '../../../util/clipboard';
|
||||
import { IS_SINGLE_COLUMN_LAYOUT } from '../../../util/environment';
|
||||
import { getServerTime } from '../../../util/serverTime';
|
||||
import useFlag from '../../../hooks/useFlag';
|
||||
import { isChatChannel } from '../../../global/helpers';
|
||||
import { getMainUsername, isChatChannel } from '../../../global/helpers';
|
||||
|
||||
import ListItem from '../../ui/ListItem';
|
||||
import NothingFound from '../../common/NothingFound';
|
||||
@ -99,12 +99,13 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
||||
forceUpdate();
|
||||
}, hasDetailedCountdown ? 1000 : undefined);
|
||||
|
||||
const chatMainUsername = useMemo(() => chat && getMainUsername(chat), [chat]);
|
||||
const primaryInvite = exportedInvites?.find(({ isPermanent }) => isPermanent);
|
||||
const primaryInviteLink = chat?.username ? `${TME_LINK_PREFIX}${chat.username}` : primaryInvite?.link;
|
||||
const primaryInviteLink = chatMainUsername ? `${TME_LINK_PREFIX}${chatMainUsername}` : primaryInvite?.link;
|
||||
const temporalInvites = useMemo(() => {
|
||||
const invites = chat?.username ? exportedInvites : exportedInvites?.filter(({ isPermanent }) => !isPermanent);
|
||||
const invites = chat?.usernames ? exportedInvites : exportedInvites?.filter(({ isPermanent }) => !isPermanent);
|
||||
return invites?.sort(inviteComparator);
|
||||
}, [chat?.username, exportedInvites]);
|
||||
}, [chat?.usernames, exportedInvites]);
|
||||
|
||||
const editInvite = (invite: ApiExportedInvite) => {
|
||||
setEditingExportedInvite({ chatId, invite });
|
||||
@ -307,7 +308,7 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
||||
{primaryInviteLink && (
|
||||
<div className="section">
|
||||
<p className="text-muted">
|
||||
{chat?.username ? lang('PublicLink') : lang('lng_create_permanent_link_title')}
|
||||
{chat?.usernames ? lang('PublicLink') : lang('lng_create_permanent_link_title')}
|
||||
</p>
|
||||
<div className="primary-link">
|
||||
<input
|
||||
@ -322,7 +323,7 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
||||
positionX="right"
|
||||
>
|
||||
<MenuItem icon="copy" onClick={handleCopyPrimaryClicked}>{lang('Copy')}</MenuItem>
|
||||
{!chat?.username && (
|
||||
{!chat?.usernames && (
|
||||
<MenuItem icon="delete" onClick={handlePrimaryRevoke} destructive>{lang('RevokeButton')}</MenuItem>
|
||||
)}
|
||||
</DropdownMenu>
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, { memo, useCallback } from '../../../lib/teact/teact';
|
||||
import React, { memo, useCallback, useMemo } from '../../../lib/teact/teact';
|
||||
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import { getActions } from '../../../global';
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import type { ApiMessagePublicForward } from '../../../api/types';
|
||||
|
||||
import { getActions } from '../../../global';
|
||||
|
||||
import { getMainUsername } from '../../../global/helpers';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
|
||||
import Avatar from '../../common/Avatar';
|
||||
|
||||
import './StatisticsPublicForward.scss';
|
||||
@ -17,9 +20,10 @@ const StatisticsPublicForward: FC<OwnProps> = ({ data }) => {
|
||||
const lang = useLang();
|
||||
const { openChatByUsername } = getActions();
|
||||
|
||||
const username = useMemo(() => getMainUsername(data.chat), [data.chat]);
|
||||
const handleClick = useCallback(() => {
|
||||
openChatByUsername({ username: data.chat.username, messageId: data.messageId });
|
||||
}, [data, openChatByUsername]);
|
||||
openChatByUsername({ username, messageId: data.messageId });
|
||||
}, [data.messageId, openChatByUsername, username]);
|
||||
|
||||
return (
|
||||
<div className="StatisticsPublicForward" onClick={handleClick}>
|
||||
|
||||
@ -25,9 +25,9 @@ type DraggableState = {
|
||||
|
||||
type OwnProps = {
|
||||
children: React.ReactNode;
|
||||
onDrag: (translation: TPoint, id: number) => void;
|
||||
onDrag: (translation: TPoint, id: number | string) => void;
|
||||
onDragEnd: NoneToVoidFunction;
|
||||
id: number;
|
||||
id: number | string;
|
||||
style?: string;
|
||||
knobStyle?: string;
|
||||
isDisabled?: boolean;
|
||||
@ -143,12 +143,12 @@ const Draggable: FC<OwnProps> = ({
|
||||
|
||||
const cssStyles = useMemo(() => {
|
||||
return buildStyle(
|
||||
`transform: translate(${state.translation.x}px, ${state.translation.y}px)`,
|
||||
state.isDragging && `transform: translate(${state.translation.x}px, ${state.translation.y}px)`,
|
||||
state.width ? `width: ${state.width}px` : undefined,
|
||||
state.height ? `height: ${state.height}px` : undefined,
|
||||
externalStyle,
|
||||
);
|
||||
}, [externalStyle, state.height, state.translation.x, state.translation.y, state.width]);
|
||||
}, [externalStyle, state.height, state.isDragging, state.translation.x, state.translation.y, state.width]);
|
||||
|
||||
return (
|
||||
<div style={cssStyles} className={fullClassName} ref={ref}>
|
||||
|
||||
@ -49,11 +49,21 @@
|
||||
.user-status,
|
||||
.group-status,
|
||||
.title,
|
||||
.other-usernames,
|
||||
.subtitle {
|
||||
text-align: initial;
|
||||
unicode-bidi: plaintext;
|
||||
}
|
||||
|
||||
.other-usernames {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.username-link {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&.multiline {
|
||||
.ListItem-button > i {
|
||||
position: relative;
|
||||
|
||||
@ -44,7 +44,7 @@ export const CUSTOM_EMOJI_PREVIEW_CACHE_DISABLED = false;
|
||||
export const CUSTOM_EMOJI_PREVIEW_CACHE_NAME = 'tt-custom-emoji-preview';
|
||||
export const MEDIA_CACHE_MAX_BYTES = 512 * 1024; // 512 KB
|
||||
export const CUSTOM_BG_CACHE_NAME = 'tt-custom-bg';
|
||||
export const LANG_CACHE_NAME = 'tt-lang-packs-v14';
|
||||
export const LANG_CACHE_NAME = 'tt-lang-packs-v15';
|
||||
export const ASSET_CACHE_NAME = 'tt-assets';
|
||||
export const AUTODOWNLOAD_FILESIZE_MB_LIMITS = [1, 5, 10, 50, 100, 500];
|
||||
|
||||
@ -206,6 +206,8 @@ export const RE_TG_LINK = /^tg:(\/\/)?/;
|
||||
export const RE_TME_LINK = /^(https?:\/\/)?([-a-zA-Z0-9@:%_+~#=]{1,32}\.)?t\.me/;
|
||||
export const RE_TELEGRAM_LINK = /^(https?:\/\/)?telegram\.org\//;
|
||||
export const TME_LINK_PREFIX = 'https://t.me/';
|
||||
export const USERNAME_PURCHASE_ERROR = 'USERNAME_PURCHASE_AVAILABLE';
|
||||
export const PURCHASE_USERNAME = 'auction';
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
export const COUNTRIES_WITH_12H_TIME_FORMAT = new Set(['AU', 'BD', 'CA', 'CO', 'EG', 'HN', 'IE', 'IN', 'JO', 'MX', 'MY', 'NI', 'NZ', 'PH', 'PK', 'SA', 'SV', 'US']);
|
||||
|
||||
@ -288,7 +288,7 @@ addActionHandler('switchBotInline', (global, actions, payload) => {
|
||||
}
|
||||
|
||||
actions.openChatWithDraft({
|
||||
text: `@${botSender.username} ${query}`,
|
||||
text: `@${botSender.usernames![0].username} ${query}`,
|
||||
chatId: isSamePeer ? chat.id : undefined,
|
||||
});
|
||||
return undefined;
|
||||
|
||||
@ -57,7 +57,6 @@ const SERVICE_NOTIFICATIONS_USER_MOCK: ApiUser = {
|
||||
accessHash: '0',
|
||||
type: 'userTypeRegular',
|
||||
isMin: true,
|
||||
username: '',
|
||||
phoneNumber: '',
|
||||
};
|
||||
|
||||
@ -724,7 +723,7 @@ addActionHandler('openChatByUsername', async (global, actions, payload) => {
|
||||
const chat = selectCurrentChat(global);
|
||||
|
||||
if (!commentId) {
|
||||
if (chat && chat.username === username && !startAttach && !startParam) {
|
||||
if (!startAttach && !startParam && chat?.usernames?.some((c) => c.username === username)) {
|
||||
actions.focusMessage({ chatId: chat.id, messageId });
|
||||
return;
|
||||
}
|
||||
@ -1683,7 +1682,7 @@ async function openChatByUsername(
|
||||
return;
|
||||
}
|
||||
|
||||
const isCurrentChat = currentChat?.username === username;
|
||||
const isCurrentChat = currentChat?.usernames?.some((c) => c.username === username);
|
||||
|
||||
if (!isCurrentChat) {
|
||||
// Open temporary empty chat to make the click response feel faster
|
||||
|
||||
@ -26,16 +26,20 @@ addActionHandler('checkPublicLink', async (global, actions, payload) => {
|
||||
global = updateManagement(global, chatId, { isUsernameAvailable: undefined, checkedUsername: undefined });
|
||||
setGlobal(global);
|
||||
|
||||
const isUsernameAvailable = (await callApi('checkChatUsername', { username }))!;
|
||||
const { result, error } = (await callApi('checkChatUsername', { username }))!;
|
||||
|
||||
global = getGlobal();
|
||||
global = updateManagementProgress(
|
||||
global, isUsernameAvailable ? ManagementProgress.Complete : ManagementProgress.Error,
|
||||
global, result === true ? ManagementProgress.Complete : ManagementProgress.Error,
|
||||
);
|
||||
global = updateManagement(global, chatId, { isUsernameAvailable, checkedUsername: username });
|
||||
global = updateManagement(global, chatId, {
|
||||
isUsernameAvailable: result === true,
|
||||
checkedUsername: username,
|
||||
error,
|
||||
});
|
||||
setGlobal(global);
|
||||
|
||||
if (isUsernameAvailable === undefined) {
|
||||
if (result === undefined) {
|
||||
actions.openLimitReachedModal({ limit: 'channelsPublic' });
|
||||
}
|
||||
});
|
||||
@ -66,7 +70,11 @@ addActionHandler('updatePublicLink', async (global, actions, payload) => {
|
||||
|
||||
global = getGlobal();
|
||||
global = updateManagementProgress(global, result ? ManagementProgress.Complete : ManagementProgress.Error);
|
||||
global = updateManagement(global, chatId, { isUsernameAvailable: undefined, checkedUsername: undefined });
|
||||
global = updateManagement(global, chatId, {
|
||||
isUsernameAvailable: undefined,
|
||||
checkedUsername: undefined,
|
||||
error: undefined,
|
||||
});
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import type { GlobalState } from '../../types';
|
||||
import type {
|
||||
ApiPrivacyKey, PrivacyVisibility, InputPrivacyRules, InputPrivacyContact,
|
||||
} from '../../../types';
|
||||
import type { ApiUsername } from '../../../api/types';
|
||||
import {
|
||||
ProfileEditProgress,
|
||||
UPLOADING_WALLPAPER_SLUG,
|
||||
@ -14,10 +15,10 @@ import { callApi } from '../../../api/gramjs';
|
||||
import { buildCollectionByKey } from '../../../util/iteratees';
|
||||
import { subscribe, unsubscribe } from '../../../util/notifications';
|
||||
import { setTimeFormat } from '../../../util/langProvider';
|
||||
import { selectUser, selectChat } from '../../selectors';
|
||||
import { selectChat, selectUser } from '../../selectors';
|
||||
import {
|
||||
addUsers, addBlockedContact, updateChats, updateUser, removeBlockedContact, replaceSettings, updateNotifySettings,
|
||||
addNotifyExceptions,
|
||||
addNotifyExceptions, updateChat,
|
||||
} from '../../reducers';
|
||||
import { isUserId } from '../../helpers';
|
||||
|
||||
@ -70,8 +71,15 @@ addActionHandler('updateProfile', async (global, actions, payload) => {
|
||||
|
||||
if (username) {
|
||||
const result = await callApi('updateUsername', username);
|
||||
if (result && currentUserId) {
|
||||
setGlobal(updateUser(getGlobal(), currentUserId, { username }));
|
||||
global = getGlobal();
|
||||
const currentUser = currentUserId && selectUser(global, currentUserId);
|
||||
|
||||
if (result && currentUser) {
|
||||
const shouldUsernameUpdate = currentUser.usernames?.find((u) => u.isEditable);
|
||||
const usernames = shouldUsernameUpdate
|
||||
? currentUser.usernames?.map((u) => (u.isEditable ? { ...u, username } : u))
|
||||
: [{ username, isEditable: true, isActive: true } as ApiUsername, ...currentUser.usernames || []];
|
||||
setGlobal(updateUser(global, currentUserId, { usernames }));
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,10 +153,11 @@ addActionHandler('checkUsername', async (global, actions, payload) => {
|
||||
progress: global.profileEdit ? global.profileEdit.progress : ProfileEditProgress.Idle,
|
||||
checkedUsername: undefined,
|
||||
isUsernameAvailable: undefined,
|
||||
error: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const isUsernameAvailable = await callApi('checkUsername', username);
|
||||
const { result, error } = (await callApi('checkUsername', username))!;
|
||||
|
||||
global = getGlobal();
|
||||
setGlobal({
|
||||
@ -156,7 +165,8 @@ addActionHandler('checkUsername', async (global, actions, payload) => {
|
||||
profileEdit: {
|
||||
...global.profileEdit!,
|
||||
checkedUsername: username,
|
||||
isUsernameAvailable,
|
||||
isUsernameAvailable: result === true,
|
||||
error,
|
||||
},
|
||||
});
|
||||
});
|
||||
@ -640,3 +650,104 @@ addActionHandler('updateGlobalPrivacySettings', async (global, actions, payload)
|
||||
: result.shouldArchiveAndMuteNewNonContact,
|
||||
}));
|
||||
});
|
||||
|
||||
addActionHandler('toggleUsername', async (global, actions, { username, isActive }) => {
|
||||
const { currentUserId } = global;
|
||||
if (!currentUserId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentUser = selectUser(global, currentUserId);
|
||||
if (!currentUser?.usernames) {
|
||||
return;
|
||||
}
|
||||
|
||||
const usernames = currentUser.usernames.map((item) => {
|
||||
if (item.username !== username) {
|
||||
return item;
|
||||
}
|
||||
|
||||
return { ...item, isActive: isActive || undefined };
|
||||
});
|
||||
|
||||
setGlobal(updateUser(global, currentUserId, { usernames }));
|
||||
|
||||
const result = await callApi('toggleUsername', { username, isActive });
|
||||
|
||||
if (!result) {
|
||||
actions.loadFullUser({ userId: currentUserId });
|
||||
}
|
||||
});
|
||||
|
||||
addActionHandler('toggleChatUsername', async (global, actions, { chatId, username, isActive }) => {
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat?.usernames) {
|
||||
return;
|
||||
}
|
||||
|
||||
const usernames = chat.usernames.map((item) => {
|
||||
if (item.username !== username) {
|
||||
return item;
|
||||
}
|
||||
|
||||
return { ...item, isActive: isActive || undefined };
|
||||
});
|
||||
|
||||
setGlobal(updateChat(global, chatId, { usernames }));
|
||||
|
||||
const result = await callApi('toggleUsername', {
|
||||
chatId: chat.id,
|
||||
accessHash: chat.accessHash,
|
||||
username,
|
||||
isActive,
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
actions.loadFullChat({ chatId });
|
||||
}
|
||||
});
|
||||
|
||||
addActionHandler('sortUsernames', async (global, actions, { usernames }) => {
|
||||
const { currentUserId } = global;
|
||||
if (!currentUserId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await callApi('reorderUsernames', { usernames });
|
||||
|
||||
// After saving the order of usernames, server sends an update with the necessary data,
|
||||
// so there is no need to update the state in this place
|
||||
if (!result) {
|
||||
actions.loadUser({ userId: currentUserId });
|
||||
}
|
||||
});
|
||||
|
||||
addActionHandler('sortChatUsernames', async (global, actions, { chatId, usernames }) => {
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat) {
|
||||
return;
|
||||
}
|
||||
|
||||
const prevUsernames = [...chat.usernames!];
|
||||
const sortedUsernames = chat.usernames!.reduce((res, currentUsername) => {
|
||||
const idx = usernames.findIndex((username) => username === currentUsername.username);
|
||||
res[idx] = currentUsername;
|
||||
|
||||
return res;
|
||||
}, [] as ApiUsername[]);
|
||||
|
||||
global = updateChat(global, chatId, { usernames: sortedUsernames });
|
||||
setGlobal(global);
|
||||
|
||||
const result = await callApi('reorderUsernames', {
|
||||
chatId: chat.id,
|
||||
accessHash: chat.accessHash,
|
||||
usernames,
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
global = getGlobal();
|
||||
global = updateChat(global, chatId, { usernames: prevUsernames });
|
||||
setGlobal(global);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,19 +1,22 @@
|
||||
import {
|
||||
addActionHandler, getActions, getGlobal, setGlobal,
|
||||
} from '../../index';
|
||||
import { selectActiveGroupCall, selectChatGroupCall, selectGroupCall } from '../../selectors/calls';
|
||||
import { callApi } from '../../../api/gramjs';
|
||||
import { selectChat, selectUser } from '../../selectors';
|
||||
import { copyTextToClipboard } from '../../../util/clipboard';
|
||||
import type { ApiGroupCall } from '../../../api/types';
|
||||
import { updateGroupCall } from '../../reducers/calls';
|
||||
import { buildCollectionByKey, omit } from '../../../util/iteratees';
|
||||
import { addChats, addUsers } from '../../reducers';
|
||||
import { fetchChatByUsername, loadFullChat } from '../api/chats';
|
||||
|
||||
import type { ApiGroupCall } from '../../../api/types';
|
||||
import type { CallSound } from '../../types';
|
||||
|
||||
import { addChats, addUsers } from '../../reducers';
|
||||
import { updateGroupCall } from '../../reducers/calls';
|
||||
import { selectActiveGroupCall, selectChatGroupCall, selectGroupCall } from '../../selectors/calls';
|
||||
import { selectChat, selectUser } from '../../selectors';
|
||||
import { getMainUsername } from '../../helpers';
|
||||
import { copyTextToClipboard } from '../../../util/clipboard';
|
||||
import { buildCollectionByKey, omit } from '../../../util/iteratees';
|
||||
import safePlay from '../../../util/safePlay';
|
||||
import { ARE_CALLS_SUPPORTED } from '../../../util/environment';
|
||||
import * as langProvider from '../../../util/langProvider';
|
||||
import type { CallSound } from '../../types';
|
||||
|
||||
// Workaround for Safari not playing audio without user interaction
|
||||
let audioElement: HTMLAudioElement | undefined;
|
||||
@ -167,10 +170,10 @@ addActionHandler('createGroupCallInviteLink', async (global, actions) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const canInvite = Boolean(chat.username);
|
||||
const hasPublicUsername = Boolean(getMainUsername(chat));
|
||||
|
||||
let { inviteLink } = chat.fullInfo!;
|
||||
if (canInvite) {
|
||||
if (hasPublicUsername) {
|
||||
inviteLink = await callApi('exportGroupCallInvite', {
|
||||
call: groupCall,
|
||||
canSelfUnmute: false,
|
||||
|
||||
@ -3,6 +3,7 @@ import { addCallback, removeCallback } from '../lib/teact/teactn';
|
||||
import { addActionHandler, getGlobal } from './index';
|
||||
|
||||
import type { GlobalState } from './types';
|
||||
import type { ApiChat, ApiUser } from '../api/types';
|
||||
import { MAIN_THREAD_ID } from '../api/types';
|
||||
|
||||
import { onBeforeUnload, onIdle, throttle } from '../util/schedulers';
|
||||
@ -326,6 +327,30 @@ function unsafeMigrateCache(cached: GlobalState, initialState: GlobalState) {
|
||||
notification.isDeleted = isHidden;
|
||||
}
|
||||
});
|
||||
|
||||
// TODO Remove in Mar 2023 (this was re-designed but can be hardcoded in cache)
|
||||
if (cached.users.byId && Object.values(cached.users.byId).some((u) => 'username' in u)) {
|
||||
cached.users.byId = Object.entries(cached.users.byId).reduce((acc, [id, user]) => {
|
||||
if ('username' in user) {
|
||||
delete user.username;
|
||||
}
|
||||
acc[id] = user;
|
||||
|
||||
return acc;
|
||||
}, {} as Record<string, ApiUser>);
|
||||
}
|
||||
|
||||
// TODO Remove in Mar 2023 (this was re-designed but can be hardcoded in cache)
|
||||
if (cached.chats.byId && Object.values(cached.chats.byId).some((c) => 'username' in c)) {
|
||||
cached.chats.byId = Object.entries(cached.chats.byId).reduce((acc, [id, user]) => {
|
||||
if ('username' in user) {
|
||||
delete user.username;
|
||||
}
|
||||
acc[id] = user;
|
||||
|
||||
return acc;
|
||||
}, {} as Record<string, ApiChat>);
|
||||
}
|
||||
}
|
||||
|
||||
function updateCache() {
|
||||
|
||||
@ -94,9 +94,12 @@ export function getChatDescription(chat: ApiChat) {
|
||||
}
|
||||
|
||||
export function getChatLink(chat: ApiChat) {
|
||||
const { username } = chat;
|
||||
if (username) {
|
||||
return `${TME_LINK_PREFIX}${username}`;
|
||||
const { usernames } = chat;
|
||||
if (usernames) {
|
||||
const activeUsername = usernames.find((u) => u.isActive);
|
||||
if (activeUsername) {
|
||||
return `${TME_LINK_PREFIX}${activeUsername.username}`;
|
||||
}
|
||||
}
|
||||
|
||||
const { inviteLink } = chat.fullInfo || {};
|
||||
@ -376,3 +379,7 @@ export function filterChatsByName(
|
||||
return searchWords(getChatTitle(lang, chat, undefined, id === currentUserId));
|
||||
});
|
||||
}
|
||||
|
||||
export function isChatPublic(chat: ApiChat) {
|
||||
return chat.usernames?.some(({ isActive }) => isActive);
|
||||
}
|
||||
|
||||
@ -248,7 +248,8 @@ export function filterUsersByName(
|
||||
}
|
||||
|
||||
const name = id === currentUserId ? savedMessagesLang : getUserFullName(user);
|
||||
return (name && searchWords(name)) || searchWords(user.username);
|
||||
|
||||
return (name && searchWords(name)) || Boolean(user.usernames?.find(({ username }) => searchWords(username)));
|
||||
});
|
||||
}
|
||||
|
||||
@ -268,3 +269,7 @@ export function getUserColorKey(peer: ApiUser | ApiChat | undefined) {
|
||||
|
||||
return USER_COLOR_KEYS[index];
|
||||
}
|
||||
|
||||
export function getMainUsername(userOrChat: ApiUser | ApiChat) {
|
||||
return userOrChat.usernames?.find((u) => u.isActive)?.username;
|
||||
}
|
||||
|
||||
@ -175,7 +175,7 @@ export function selectIsChatPinned(global: GlobalState, chatId: string, folderId
|
||||
export function selectChatByUsername(global: GlobalState, username: string) {
|
||||
const usernameLowered = username.toLowerCase();
|
||||
return Object.values(global.chats.byId).find(
|
||||
(chat) => chat.username && chat.username.toLowerCase() === usernameLowered,
|
||||
(chat) => chat.usernames?.some((c) => c.username.toLowerCase() === usernameLowered),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ export function selectIsPremiumPurchaseBlocked(global: GlobalState) {
|
||||
export function selectUserByUsername(global: GlobalState, username: string) {
|
||||
const usernameLowered = username.toLowerCase();
|
||||
return Object.values(global.users.byId).find(
|
||||
(user) => user.username.toLowerCase() === usernameLowered,
|
||||
(user) => user.usernames?.some((u) => u.username.toLowerCase() === usernameLowered),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -523,6 +523,7 @@ export type GlobalState = {
|
||||
progress: ProfileEditProgress;
|
||||
checkedUsername?: string;
|
||||
isUsernameAvailable?: boolean;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
notifications: ApiNotification[];
|
||||
@ -1119,6 +1120,22 @@ export interface ActionPayloads {
|
||||
hash: string;
|
||||
};
|
||||
terminateAllWebAuthorizations: never;
|
||||
toggleUsername: {
|
||||
username: string;
|
||||
isActive: boolean;
|
||||
};
|
||||
sortUsernames: {
|
||||
usernames: string[];
|
||||
};
|
||||
toggleChatUsername: {
|
||||
chatId: string;
|
||||
username: string;
|
||||
isActive: boolean;
|
||||
};
|
||||
sortChatUsernames: {
|
||||
chatId: string;
|
||||
usernames: string[];
|
||||
};
|
||||
|
||||
// Misc
|
||||
openPollModal: {
|
||||
|
||||
@ -240,6 +240,7 @@ class TelegramClient {
|
||||
|
||||
return new Api.messages.ChannelMessages({
|
||||
messages: peer.TEST_messages,
|
||||
topics: [],
|
||||
pts: 0,
|
||||
count: peer.TEST_messages.length,
|
||||
chats: [],
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
const api = require('./api');
|
||||
|
||||
const LAYER = 147;
|
||||
const LAYER = 149;
|
||||
const tlobjects = {};
|
||||
|
||||
for (const tl of Object.values(api)) {
|
||||
|
||||
398
src/lib/gramjs/tl/api.d.ts
vendored
398
src/lib/gramjs/tl/api.d.ts
vendored
File diff suppressed because one or more lines are too long
@ -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#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;
|
||||
user#8f97c628 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 flags2:# 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 usernames:flags2.0?Vector<Username> = 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;
|
||||
@ -76,7 +76,7 @@ userStatusLastMonth#77ebc742 = UserStatus;
|
||||
chatEmpty#29562865 id:long = Chat;
|
||||
chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
|
||||
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;
|
||||
channel#83259464 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 forum:flags.30?true flags2:# 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 usernames:flags2.0?Vector<Username> = 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#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:flags2.0?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;
|
||||
@ -136,6 +136,8 @@ messageActionChatJoinedByRequest#ebbca3cb = MessageAction;
|
||||
messageActionWebViewDataSentMe#47dd8079 text:string data:string = MessageAction;
|
||||
messageActionWebViewDataSent#b4c38cb5 text:string = MessageAction;
|
||||
messageActionGiftPremium#aba0f5c6 currency:string amount:long months:int = MessageAction;
|
||||
messageActionTopicCreate#d999256 flags:# title:string icon_color:int icon_emoji_id:flags.0?long = MessageAction;
|
||||
messageActionTopicEdit#b18a431c flags:# title:flags.0?string icon_emoji_id:flags.1?long closed:flags.2?Bool = MessageAction;
|
||||
dialog#a8edd0f5 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog;
|
||||
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
|
||||
photoEmpty#2331b22d id:long = Photo;
|
||||
@ -156,6 +158,7 @@ inputNotifyPeer#b8bc5b0c peer:InputPeer = InputNotifyPeer;
|
||||
inputNotifyUsers#193b4417 = InputNotifyPeer;
|
||||
inputNotifyChats#4a95e84e = InputNotifyPeer;
|
||||
inputNotifyBroadcasts#b1db7c7e = InputNotifyPeer;
|
||||
inputNotifyForumTopic#5c467992 peer:InputPeer top_msg_id:int = InputNotifyPeer;
|
||||
inputPeerNotifySettings#df1f002b flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?NotificationSound = InputPeerNotifySettings;
|
||||
peerNotifySettings#a83b0426 flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int ios_sound:flags.3?NotificationSound android_sound:flags.4?NotificationSound other_sound:flags.5?NotificationSound = PeerNotifySettings;
|
||||
peerSettings#a518110d flags:# report_spam:flags.0?true add_contact:flags.1?true block_contact:flags.2?true share_contact:flags.3?true need_contacts_exception:flags.4?true report_geo:flags.5?true autoarchived:flags.7?true invite_members:flags.8?true request_chat_broadcast:flags.10?true geo_distance:flags.6?int request_chat_title:flags.9?string request_chat_date:flags.9?int = PeerSettings;
|
||||
@ -185,7 +188,7 @@ messages.dialogsSlice#71e094f3 count:int dialogs:Vector<Dialog> messages:Vector<
|
||||
messages.dialogsNotModified#f0e3e596 count:int = messages.Dialogs;
|
||||
messages.messages#8c718e87 messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
|
||||
messages.messagesSlice#3a54685e flags:# inexact:flags.1?true count:int next_rate:flags.0?int offset_id_offset:flags.2?int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
|
||||
messages.channelMessages#64479808 flags:# inexact:flags.1?true pts:int count:int offset_id_offset:flags.2?int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
|
||||
messages.channelMessages#c776ba4e flags:# inexact:flags.1?true pts:int count:int offset_id_offset:flags.2?int messages:Vector<Message> topics:Vector<ForumTopic> chats:Vector<Chat> users:Vector<User> = messages.Messages;
|
||||
messages.messagesNotModified#74535f21 count:int = messages.Messages;
|
||||
messages.chats#64ff9fd5 chats:Vector<Chat> = messages.Chats;
|
||||
messages.chatsSlice#9cd81144 count:int chats:Vector<Chat> = messages.Chats;
|
||||
@ -215,7 +218,7 @@ updateUserTyping#c01e857f user_id:long action:SendMessageAction = Update;
|
||||
updateChatUserTyping#83487af0 chat_id:long from_id:Peer action:SendMessageAction = Update;
|
||||
updateChatParticipants#7761198 participants:ChatParticipants = Update;
|
||||
updateUserStatus#e5bdf8de user_id:long status:UserStatus = Update;
|
||||
updateUserName#c3f202e0 user_id:long first_name:string last_name:string username:string = Update;
|
||||
updateUserName#a7848924 user_id:long first_name:string last_name:string usernames:Vector<Username> = Update;
|
||||
updateUserPhoto#f227868c user_id:long date:int photo:UserProfilePhoto previous:Bool = Update;
|
||||
updateNewEncryptedMessage#12bcbd9a message:EncryptedMessage qts:int = Update;
|
||||
updateEncryptedChatTyping#1710f156 chat_id:int = Update;
|
||||
@ -250,7 +253,7 @@ updateBotCallbackQuery#b9cfc48d flags:# query_id:long user_id:long peer:Peer msg
|
||||
updateEditMessage#e40370a3 message:Message pts:int pts_count:int = Update;
|
||||
updateInlineBotCallbackQuery#691e9052 flags:# query_id:long user_id:long msg_id:InputBotInlineMessageID chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update;
|
||||
updateReadChannelOutbox#b75f99a9 channel_id:long max_id:int = Update;
|
||||
updateDraftMessage#ee2bb969 peer:Peer draft:DraftMessage = Update;
|
||||
updateDraftMessage#1b49ec6d flags:# peer:Peer top_msg_id:flags.0?int draft:DraftMessage = Update;
|
||||
updateReadFeaturedStickers#571d2742 = Update;
|
||||
updateRecentStickers#9a422c20 = Update;
|
||||
updateConfig#a229dd06 = Update;
|
||||
@ -266,7 +269,7 @@ updatePhoneCall#ab0f6b1e phone_call:PhoneCall = Update;
|
||||
updateLangPackTooLong#46560264 lang_code:string = Update;
|
||||
updateLangPack#56022f4d difference:LangPackDifference = Update;
|
||||
updateFavedStickers#e511996d = Update;
|
||||
updateChannelReadMessagesContents#44bdd535 channel_id:long messages:Vector<int> = Update;
|
||||
updateChannelReadMessagesContents#ea29055d flags:# channel_id:long top_msg_id:flags.0?int messages:Vector<int> = Update;
|
||||
updateContactsReset#7084a7be = Update;
|
||||
updateChannelAvailableMessages#b23fc698 channel_id:long available_min_id:int = Update;
|
||||
updateDialogUnreadMark#e16459c3 flags:# unread:flags.0?true peer:DialogPeer = Update;
|
||||
@ -303,7 +306,7 @@ updateGroupCallConnection#b783982 flags:# presentation:flags.0?true params:DataJ
|
||||
updateBotCommands#4d712f2e peer:Peer bot_id:long commands:Vector<BotCommand> = Update;
|
||||
updatePendingJoinRequests#7063c3db peer:Peer requests_pending:int recent_requesters:Vector<long> = Update;
|
||||
updateBotChatInviteRequester#11dfa986 peer:Peer date:int user_id:long about:string invite:ExportedChatInvite qts:int = Update;
|
||||
updateMessageReactions#154798c3 peer:Peer msg_id:int reactions:MessageReactions = Update;
|
||||
updateMessageReactions#5e1b3cb8 flags:# peer:Peer msg_id:int top_msg_id:flags.0?int reactions:MessageReactions = Update;
|
||||
updateAttachMenuBots#17b7a20b = Update;
|
||||
updateWebViewResultSent#1592b79d query_id:long = Update;
|
||||
updateBotMenuButton#14b85813 bot_id:long button:BotMenuButton = Update;
|
||||
@ -315,6 +318,8 @@ updateRecentEmojiStatuses#30f443db = Update;
|
||||
updateRecentReactions#6f7863f4 = Update;
|
||||
updateMoveStickerSetToTop#86fccf85 flags:# masks:flags.0?true emojis:flags.1?true stickerset:long = Update;
|
||||
updateMessageExtendedMedia#5a73a98c peer:Peer msg_id:int extended_media:MessageExtendedMedia = Update;
|
||||
updateChannelPinnedTopic#192efbe3 flags:# pinned:flags.0?true channel_id:long topic_id:int = Update;
|
||||
updateChannelPinnedTopics#fe198602 flags:# channel_id:long order:flags.0?Vector<int> = 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;
|
||||
@ -365,6 +370,7 @@ notifyPeer#9fd40bd8 peer:Peer = NotifyPeer;
|
||||
notifyUsers#b4c83b4c = NotifyPeer;
|
||||
notifyChats#c007cec3 = NotifyPeer;
|
||||
notifyBroadcasts#d612e8ef = NotifyPeer;
|
||||
notifyForumTopic#226e6308 peer:Peer top_msg_id:int = NotifyPeer;
|
||||
sendMessageTypingAction#16bf744e = SendMessageAction;
|
||||
sendMessageCancelAction#fd5ec8f5 = SendMessageAction;
|
||||
sendMessageRecordVideoAction#a187d66f = SendMessageAction;
|
||||
@ -459,6 +465,7 @@ inputStickerSetAnimatedEmojiAnimations#cde3739 = InputStickerSet;
|
||||
inputStickerSetPremiumGifts#c88b3b02 = InputStickerSet;
|
||||
inputStickerSetEmojiGenericAnimations#4c4d4ce = InputStickerSet;
|
||||
inputStickerSetEmojiDefaultStatuses#29d0f5ee = InputStickerSet;
|
||||
inputStickerSetEmojiDefaultTopicIcons#44c1f8e9 = 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#6e153f16 set:StickerSet packs:Vector<StickerPack> keywords:Vector<StickerKeyword> documents:Vector<Document> = messages.StickerSet;
|
||||
messages.stickerSetNotModified#d3f924eb = messages.StickerSet;
|
||||
@ -739,6 +746,12 @@ channelAdminLogEventActionParticipantJoinByRequest#afb6144a invite:ExportedChatI
|
||||
channelAdminLogEventActionToggleNoForwards#cb2ac766 new_value:Bool = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionSendMessage#278f2868 message:Message = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionChangeAvailableReactions#be4e0ef8 prev_value:ChatReactions new_value:ChatReactions = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionChangeUsernames#f04fb3a9 prev_value:Vector<string> new_value:Vector<string> = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionToggleForum#2cc6383 new_value:Bool = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionCreateTopic#58707d28 topic:ForumTopic = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionEditTopic#f06fe208 prev_topic:ForumTopic new_topic:ForumTopic = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionDeleteTopic#ae168909 topic:ForumTopic = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionPinTopic#5d8d353b flags:# prev_topic:flags.0?ForumTopic new_topic:flags.1?ForumTopic = 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 forums:flags.17?true = ChannelAdminLogEventsFilter;
|
||||
@ -845,8 +858,8 @@ pollAnswerVoters#3b6ddad2 flags:# chosen:flags.0?true correct:flags.1?true optio
|
||||
pollResults#dcb82ea3 flags:# min:flags.0?true results:flags.1?Vector<PollAnswerVoters> total_voters:flags.2?int recent_voters:flags.3?Vector<long> solution:flags.4?string solution_entities:flags.4?Vector<MessageEntity> = PollResults;
|
||||
chatOnlines#f041e250 onlines:int = ChatOnlines;
|
||||
statsURL#47a971e0 url:string = StatsURL;
|
||||
chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true other:flags.12?true = ChatAdminRights;
|
||||
chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true until_date:int = ChatBannedRights;
|
||||
chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true other:flags.12?true manage_topics:flags.13?true = ChatAdminRights;
|
||||
chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true manage_topics:flags.18?true until_date:int = ChatBannedRights;
|
||||
inputWallPaper#e630b979 id:long access_hash:long = InputWallPaper;
|
||||
inputWallPaperSlug#72091c80 slug:string = InputWallPaper;
|
||||
inputWallPaperNoFile#967a462e id:long = InputWallPaper;
|
||||
@ -923,7 +936,7 @@ help.countriesList#87d0759e countries:Vector<help.Country> hash:int = help.Count
|
||||
messageViews#455b853d flags:# views:flags.0?int forwards:flags.1?int replies:flags.2?MessageReplies = MessageViews;
|
||||
messages.messageViews#b6c4f543 views:Vector<MessageViews> chats:Vector<Chat> users:Vector<User> = messages.MessageViews;
|
||||
messages.discussionMessage#a6341782 flags:# messages:Vector<Message> max_id:flags.0?int read_inbox_max_id:flags.1?int read_outbox_max_id:flags.2?int unread_count:int chats:Vector<Chat> users:Vector<User> = messages.DiscussionMessage;
|
||||
messageReplyHeader#a6d57763 flags:# reply_to_scheduled:flags.2?true reply_to_msg_id:int reply_to_peer_id:flags.0?Peer reply_to_top_id:flags.1?int = MessageReplyHeader;
|
||||
messageReplyHeader#a6d57763 flags:# reply_to_scheduled:flags.2?true forum_topic:flags.3?true reply_to_msg_id:int reply_to_peer_id:flags.0?Peer reply_to_top_id:flags.1?int = MessageReplyHeader;
|
||||
messageReplies#83d60fc2 flags:# comments:flags.0?true replies:int replies_pts:int recent_repliers:flags.1?Vector<Peer> channel_id:flags.0?long max_id:flags.2?int read_max_id:flags.3?int = MessageReplies;
|
||||
peerBlocked#e8fd8014 peer_id:Peer date:int = PeerBlocked;
|
||||
stats.messageStats#8999f295 views_graph:StatsGraph = stats.MessageStats;
|
||||
@ -964,8 +977,9 @@ botCommandScopePeerUser#a1321f3 peer:InputPeer user_id:InputUser = BotCommandSco
|
||||
account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordResult;
|
||||
account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
|
||||
account.resetPasswordOk#e926d63e = account.ResetPasswordResult;
|
||||
sponsoredMessage#3a836df8 flags:# recommended:flags.5?true random_id:bytes from_id:flags.3?Peer chat_invite:flags.4?ChatInvite chat_invite_hash:flags.4?string channel_post:flags.2?int start_param:flags.0?string message:string entities:flags.1?Vector<MessageEntity> = SponsoredMessage;
|
||||
messages.sponsoredMessages#65a4c7d5 messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
|
||||
sponsoredMessage#3a836df8 flags:# recommended:flags.5?true show_peer_photo:flags.6?true random_id:bytes from_id:flags.3?Peer chat_invite:flags.4?ChatInvite chat_invite_hash:flags.4?string channel_post:flags.2?int start_param:flags.0?string message:string entities:flags.1?Vector<MessageEntity> = SponsoredMessage;
|
||||
messages.sponsoredMessages#c9ee1d87 flags:# posts_between:flags.0?int messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
|
||||
messages.sponsoredMessagesEmpty#1839490f = messages.SponsoredMessages;
|
||||
searchResultsCalendarPeriod#c9b0539f date:int min_msg_id:int max_msg_id:int count:int = SearchResultsCalendarPeriod;
|
||||
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;
|
||||
@ -1046,6 +1060,10 @@ sendAsPeer#b81c7034 flags:# premium_required:flags.0?true peer:Peer = SendAsPeer
|
||||
messageExtendedMediaPreview#ad628cc8 flags:# w:flags.0?int h:flags.0?int thumb:flags.1?PhotoSize video_duration:flags.2?int = MessageExtendedMedia;
|
||||
messageExtendedMedia#ee479c64 media:MessageMedia = MessageExtendedMedia;
|
||||
stickerKeyword#fcfeb29c document_id:long keyword:Vector<string> = StickerKeyword;
|
||||
username#b4073647 flags:# editable:flags.0?true active:flags.1?true username:string = Username;
|
||||
forumTopicDeleted#23f109b id:int = ForumTopic;
|
||||
forumTopic#71701da9 flags:# my:flags.1?true closed:flags.2?true pinned:flags.3?true short:flags.5?true id:int date:int title:string icon_color:int icon_emoji_id:flags.0?long top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int from_id:Peer notify_settings:PeerNotifySettings draft:flags.4?DraftMessage = ForumTopic;
|
||||
messages.forumTopics#367617d3 flags:# order_by_create_date:flags.0?true count:int topics:Vector<ForumTopic> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> pts:int = messages.ForumTopics;
|
||||
---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;
|
||||
@ -1101,6 +1119,8 @@ account.setGlobalPrivacySettings#1edaaac2 settings:GlobalPrivacySettings = Globa
|
||||
account.reportProfilePhoto#fa8cc6f5 peer:InputPeer photo_id:InputPhoto reason:ReportReason message:string = Bool;
|
||||
account.setAuthorizationTTL#bf899aa0 authorization_ttl_days:int = Bool;
|
||||
account.changeAuthorizationSettings#40f48462 flags:# hash:long encrypted_requests_disabled:flags.0?Bool call_requests_disabled:flags.1?Bool = Bool;
|
||||
account.reorderUsernames#ef500eab order:Vector<string> = Bool;
|
||||
account.toggleUsername#58d6b376 username:string active:Bool = Bool;
|
||||
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
||||
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
|
||||
contacts.getContacts#5dd69e12 hash:long = contacts.Contacts;
|
||||
@ -1123,9 +1143,9 @@ 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 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.sendMessage#1cc20387 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 top_msg_id:flags.9?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#7547c966 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 top_msg_id:flags.9?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#c661bbc4 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 top_msg_id:flags.9?int schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.reportSpam#cf1592db peer:InputPeer = Bool;
|
||||
messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings;
|
||||
messages.report#8953ab4e peer:InputPeer id:Vector<int> reason:ReportReason message:string = Bool;
|
||||
@ -1154,11 +1174,11 @@ messages.getDocumentByHash#b1f2061f sha256:bytes size:long mime_type:string = Do
|
||||
messages.getSavedGifs#5cf09635 hash:long = messages.SavedGifs;
|
||||
messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
|
||||
messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
|
||||
messages.sendInlineBotResult#7aa11297 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.sendInlineBotResult#d3fbdccb flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int top_msg_id:flags.9?int random_id:long query_id:long id:string schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.editMessage#48f71778 flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int = Updates;
|
||||
messages.getBotCallbackAnswer#9342ca07 flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes password:flags.2?InputCheckPasswordSRP = messages.BotCallbackAnswer;
|
||||
messages.getPeerDialogs#e470bcfd peers:Vector<InputDialogPeer> = messages.PeerDialogs;
|
||||
messages.saveDraft#bc39e14b flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> = Bool;
|
||||
messages.saveDraft#b4331e3f flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int top_msg_id:flags.2?int peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> = Bool;
|
||||
messages.getFeaturedStickers#64780b14 hash:long = messages.FeaturedStickers;
|
||||
messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool;
|
||||
messages.getRecentStickers#9da9403b flags:# attached:flags.0?true hash:long = messages.RecentStickers;
|
||||
@ -1171,9 +1191,9 @@ messages.getPinnedDialogs#d6b94df2 folder_id:int = messages.PeerDialogs;
|
||||
messages.uploadMedia#519bc2b1 peer:InputPeer media:InputMedia = MessageMedia;
|
||||
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 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.getUnreadMentions#f107e790 flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
|
||||
messages.readMentions#36e5bf4d flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory;
|
||||
messages.sendMultiMedia#b6f11a1c 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 top_msg_id:flags.9?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;
|
||||
@ -1196,7 +1216,7 @@ messages.updateDialogFiltersOrder#c563c1e4 order:Vector<int> = Bool;
|
||||
messages.getReplies#22ddd30c peer:InputPeer msg_id:int offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;
|
||||
messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage;
|
||||
messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool;
|
||||
messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory;
|
||||
messages.unpinAllMessages#ee22b9a8 flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory;
|
||||
messages.deleteChat#5bd0ee50 chat_id:long = Bool;
|
||||
messages.getExportedChatInvites#a2b5a3f6 flags:# revoked:flags.3?true peer:InputPeer admin_id:InputUser offset_date:flags.2?int offset_link:flags.2?string limit:int = messages.ExportedChatInvites;
|
||||
messages.editExportedChatInvite#bdca2f75 flags:# revoked:flags.2?true peer:InputPeer link:string expire_date:flags.0?int usage_limit:flags.1?int request_needed:flags.3?Bool title:flags.4?string = messages.ExportedChatInvite;
|
||||
@ -1214,13 +1234,13 @@ messages.getMessageReactionsList#461b3f48 flags:# peer:InputPeer id:int reaction
|
||||
messages.setChatAvailableReactions#feb16771 peer:InputPeer available_reactions:ChatReactions = Updates;
|
||||
messages.getAvailableReactions#18dea0ac hash:int = messages.AvailableReactions;
|
||||
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.getUnreadReactions#3223495b flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
|
||||
messages.readReactions#54aa7f8e flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory;
|
||||
messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots;
|
||||
messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot;
|
||||
messages.toggleBotInAttachMenu#1aee33af bot:InputUser enabled:Bool = Bool;
|
||||
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.requestWebView#178b480b 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 top_msg_id:flags.9?int send_as:flags.13?InputPeer = WebViewResult;
|
||||
messages.prolongWebView#7ff34309 flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to_msg_id:flags.0?int top_msg_id:flags.9?int send_as:flags.13?InputPeer = Bool;
|
||||
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;
|
||||
@ -1275,6 +1295,8 @@ channels.getSponsoredMessages#ec210fbf channel:InputChannel = messages.Sponsored
|
||||
channels.getSendAs#dc770ee peer:InputPeer = channels.SendAsPeers;
|
||||
channels.toggleJoinToSend#e4cb9580 channel:InputChannel enabled:Bool = Updates;
|
||||
channels.toggleJoinRequest#4c2985b6 channel:InputChannel enabled:Bool = Updates;
|
||||
channels.reorderUsernames#b45ced1d channel:InputChannel order:Vector<string> = Bool;
|
||||
channels.toggleUsername#50f24105 channel:InputChannel username:string active:Bool = Bool;
|
||||
payments.getPaymentForm#37148dbb flags:# invoice:InputInvoice theme_params:flags.0?DataJSON = payments.PaymentForm;
|
||||
payments.getPaymentReceipt#2478d1cc peer:InputPeer msg_id:int = payments.PaymentReceipt;
|
||||
payments.validateRequestedInfo#b6c8f12b flags:# save:flags.0?true invoice:InputInvoice info:PaymentRequestedInfo = payments.ValidatedRequestedInfo;
|
||||
|
||||
@ -53,6 +53,8 @@
|
||||
"account.reportProfilePhoto",
|
||||
"account.changeAuthorizationSettings",
|
||||
"account.setAuthorizationTTL",
|
||||
"account.reorderUsernames",
|
||||
"account.toggleUsername",
|
||||
"users.getUsers",
|
||||
"users.getFullUser",
|
||||
"contacts.getContacts",
|
||||
@ -201,6 +203,8 @@
|
||||
"channels.getSendAs",
|
||||
"channels.toggleJoinToSend",
|
||||
"channels.toggleJoinRequest",
|
||||
"channels.reorderUsernames",
|
||||
"channels.toggleUsername",
|
||||
"channels.viewSponsoredMessage",
|
||||
"channels.getSponsoredMessages",
|
||||
"payments.getPaymentForm",
|
||||
|
||||
@ -80,7 +80,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType;
|
||||
storage.fileWebp#1081464c = storage.FileType;
|
||||
|
||||
userEmpty#d3bc4b7a id:long = 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;
|
||||
user#8f97c628 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 flags2:# 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 usernames:flags2.0?Vector<Username> = User;
|
||||
|
||||
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
|
||||
userProfilePhoto#82d1f706 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto;
|
||||
@ -95,7 +95,7 @@ userStatusLastMonth#77ebc742 = UserStatus;
|
||||
chatEmpty#29562865 id:long = Chat;
|
||||
chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
|
||||
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;
|
||||
channel#83259464 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 forum:flags.30?true flags2:# 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 usernames:flags2.0?Vector<Username> = 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#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;
|
||||
@ -162,6 +162,8 @@ messageActionChatJoinedByRequest#ebbca3cb = MessageAction;
|
||||
messageActionWebViewDataSentMe#47dd8079 text:string data:string = MessageAction;
|
||||
messageActionWebViewDataSent#b4c38cb5 text:string = MessageAction;
|
||||
messageActionGiftPremium#aba0f5c6 currency:string amount:long months:int = MessageAction;
|
||||
messageActionTopicCreate#d999256 flags:# title:string icon_color:int icon_emoji_id:flags.0?long = MessageAction;
|
||||
messageActionTopicEdit#b18a431c flags:# title:flags.0?string icon_emoji_id:flags.1?long closed:flags.2?Bool = MessageAction;
|
||||
|
||||
dialog#a8edd0f5 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog;
|
||||
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
|
||||
@ -190,6 +192,7 @@ inputNotifyPeer#b8bc5b0c peer:InputPeer = InputNotifyPeer;
|
||||
inputNotifyUsers#193b4417 = InputNotifyPeer;
|
||||
inputNotifyChats#4a95e84e = InputNotifyPeer;
|
||||
inputNotifyBroadcasts#b1db7c7e = InputNotifyPeer;
|
||||
inputNotifyForumTopic#5c467992 peer:InputPeer top_msg_id:int = InputNotifyPeer;
|
||||
|
||||
inputPeerNotifySettings#df1f002b flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?NotificationSound = InputPeerNotifySettings;
|
||||
|
||||
@ -233,7 +236,7 @@ messages.dialogsNotModified#f0e3e596 count:int = messages.Dialogs;
|
||||
|
||||
messages.messages#8c718e87 messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
|
||||
messages.messagesSlice#3a54685e flags:# inexact:flags.1?true count:int next_rate:flags.0?int offset_id_offset:flags.2?int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
|
||||
messages.channelMessages#64479808 flags:# inexact:flags.1?true pts:int count:int offset_id_offset:flags.2?int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
|
||||
messages.channelMessages#c776ba4e flags:# inexact:flags.1?true pts:int count:int offset_id_offset:flags.2?int messages:Vector<Message> topics:Vector<ForumTopic> chats:Vector<Chat> users:Vector<User> = messages.Messages;
|
||||
messages.messagesNotModified#74535f21 count:int = messages.Messages;
|
||||
|
||||
messages.chats#64ff9fd5 chats:Vector<Chat> = messages.Chats;
|
||||
@ -268,7 +271,7 @@ updateUserTyping#c01e857f user_id:long action:SendMessageAction = Update;
|
||||
updateChatUserTyping#83487af0 chat_id:long from_id:Peer action:SendMessageAction = Update;
|
||||
updateChatParticipants#7761198 participants:ChatParticipants = Update;
|
||||
updateUserStatus#e5bdf8de user_id:long status:UserStatus = Update;
|
||||
updateUserName#c3f202e0 user_id:long first_name:string last_name:string username:string = Update;
|
||||
updateUserName#a7848924 user_id:long first_name:string last_name:string usernames:Vector<Username> = Update;
|
||||
updateUserPhoto#f227868c user_id:long date:int photo:UserProfilePhoto previous:Bool = Update;
|
||||
updateNewEncryptedMessage#12bcbd9a message:EncryptedMessage qts:int = Update;
|
||||
updateEncryptedChatTyping#1710f156 chat_id:int = Update;
|
||||
@ -303,7 +306,7 @@ updateBotCallbackQuery#b9cfc48d flags:# query_id:long user_id:long peer:Peer msg
|
||||
updateEditMessage#e40370a3 message:Message pts:int pts_count:int = Update;
|
||||
updateInlineBotCallbackQuery#691e9052 flags:# query_id:long user_id:long msg_id:InputBotInlineMessageID chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update;
|
||||
updateReadChannelOutbox#b75f99a9 channel_id:long max_id:int = Update;
|
||||
updateDraftMessage#ee2bb969 peer:Peer draft:DraftMessage = Update;
|
||||
updateDraftMessage#1b49ec6d flags:# peer:Peer top_msg_id:flags.0?int draft:DraftMessage = Update;
|
||||
updateReadFeaturedStickers#571d2742 = Update;
|
||||
updateRecentStickers#9a422c20 = Update;
|
||||
updateConfig#a229dd06 = Update;
|
||||
@ -319,7 +322,7 @@ updatePhoneCall#ab0f6b1e phone_call:PhoneCall = Update;
|
||||
updateLangPackTooLong#46560264 lang_code:string = Update;
|
||||
updateLangPack#56022f4d difference:LangPackDifference = Update;
|
||||
updateFavedStickers#e511996d = Update;
|
||||
updateChannelReadMessagesContents#44bdd535 channel_id:long messages:Vector<int> = Update;
|
||||
updateChannelReadMessagesContents#ea29055d flags:# channel_id:long top_msg_id:flags.0?int messages:Vector<int> = Update;
|
||||
updateContactsReset#7084a7be = Update;
|
||||
updateChannelAvailableMessages#b23fc698 channel_id:long available_min_id:int = Update;
|
||||
updateDialogUnreadMark#e16459c3 flags:# unread:flags.0?true peer:DialogPeer = Update;
|
||||
@ -356,7 +359,7 @@ updateGroupCallConnection#b783982 flags:# presentation:flags.0?true params:DataJ
|
||||
updateBotCommands#4d712f2e peer:Peer bot_id:long commands:Vector<BotCommand> = Update;
|
||||
updatePendingJoinRequests#7063c3db peer:Peer requests_pending:int recent_requesters:Vector<long> = Update;
|
||||
updateBotChatInviteRequester#11dfa986 peer:Peer date:int user_id:long about:string invite:ExportedChatInvite qts:int = Update;
|
||||
updateMessageReactions#154798c3 peer:Peer msg_id:int reactions:MessageReactions = Update;
|
||||
updateMessageReactions#5e1b3cb8 flags:# peer:Peer msg_id:int top_msg_id:flags.0?int reactions:MessageReactions = Update;
|
||||
updateAttachMenuBots#17b7a20b = Update;
|
||||
updateWebViewResultSent#1592b79d query_id:long = Update;
|
||||
updateBotMenuButton#14b85813 bot_id:long button:BotMenuButton = Update;
|
||||
@ -368,6 +371,8 @@ updateRecentEmojiStatuses#30f443db = Update;
|
||||
updateRecentReactions#6f7863f4 = Update;
|
||||
updateMoveStickerSetToTop#86fccf85 flags:# masks:flags.0?true emojis:flags.1?true stickerset:long = Update;
|
||||
updateMessageExtendedMedia#5a73a98c peer:Peer msg_id:int extended_media:MessageExtendedMedia = Update;
|
||||
updateChannelPinnedTopic#192efbe3 flags:# pinned:flags.0?true channel_id:long topic_id:int = Update;
|
||||
updateChannelPinnedTopics#fe198602 flags:# channel_id:long order:flags.0?Vector<int> = Update;
|
||||
|
||||
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
|
||||
|
||||
@ -440,6 +445,7 @@ notifyPeer#9fd40bd8 peer:Peer = NotifyPeer;
|
||||
notifyUsers#b4c83b4c = NotifyPeer;
|
||||
notifyChats#c007cec3 = NotifyPeer;
|
||||
notifyBroadcasts#d612e8ef = NotifyPeer;
|
||||
notifyForumTopic#226e6308 peer:Peer top_msg_id:int = NotifyPeer;
|
||||
|
||||
sendMessageTypingAction#16bf744e = SendMessageAction;
|
||||
sendMessageCancelAction#fd5ec8f5 = SendMessageAction;
|
||||
@ -558,6 +564,7 @@ inputStickerSetAnimatedEmojiAnimations#cde3739 = InputStickerSet;
|
||||
inputStickerSetPremiumGifts#c88b3b02 = InputStickerSet;
|
||||
inputStickerSetEmojiGenericAnimations#4c4d4ce = InputStickerSet;
|
||||
inputStickerSetEmojiDefaultStatuses#29d0f5ee = InputStickerSet;
|
||||
inputStickerSetEmojiDefaultTopicIcons#44c1f8e9 = 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;
|
||||
|
||||
@ -922,6 +929,12 @@ channelAdminLogEventActionParticipantJoinByRequest#afb6144a invite:ExportedChatI
|
||||
channelAdminLogEventActionToggleNoForwards#cb2ac766 new_value:Bool = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionSendMessage#278f2868 message:Message = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionChangeAvailableReactions#be4e0ef8 prev_value:ChatReactions new_value:ChatReactions = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionChangeUsernames#f04fb3a9 prev_value:Vector<string> new_value:Vector<string> = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionToggleForum#2cc6383 new_value:Bool = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionCreateTopic#58707d28 topic:ForumTopic = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionEditTopic#f06fe208 prev_topic:ForumTopic new_topic:ForumTopic = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionDeleteTopic#ae168909 topic:ForumTopic = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionPinTopic#5d8d353b flags:# prev_topic:flags.0?ForumTopic new_topic:flags.1?ForumTopic = ChannelAdminLogEventAction;
|
||||
|
||||
channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
|
||||
|
||||
@ -1085,9 +1098,9 @@ chatOnlines#f041e250 onlines:int = ChatOnlines;
|
||||
|
||||
statsURL#47a971e0 url:string = StatsURL;
|
||||
|
||||
chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true other:flags.12?true = ChatAdminRights;
|
||||
chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true other:flags.12?true manage_topics:flags.13?true = ChatAdminRights;
|
||||
|
||||
chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true until_date:int = ChatBannedRights;
|
||||
chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true manage_topics:flags.18?true until_date:int = ChatBannedRights;
|
||||
|
||||
inputWallPaper#e630b979 id:long access_hash:long = InputWallPaper;
|
||||
inputWallPaperSlug#72091c80 slug:string = InputWallPaper;
|
||||
@ -1218,7 +1231,7 @@ messages.messageViews#b6c4f543 views:Vector<MessageViews> chats:Vector<Chat> use
|
||||
|
||||
messages.discussionMessage#a6341782 flags:# messages:Vector<Message> max_id:flags.0?int read_inbox_max_id:flags.1?int read_outbox_max_id:flags.2?int unread_count:int chats:Vector<Chat> users:Vector<User> = messages.DiscussionMessage;
|
||||
|
||||
messageReplyHeader#a6d57763 flags:# reply_to_scheduled:flags.2?true reply_to_msg_id:int reply_to_peer_id:flags.0?Peer reply_to_top_id:flags.1?int = MessageReplyHeader;
|
||||
messageReplyHeader#a6d57763 flags:# reply_to_scheduled:flags.2?true forum_topic:flags.3?true reply_to_msg_id:int reply_to_peer_id:flags.0?Peer reply_to_top_id:flags.1?int = MessageReplyHeader;
|
||||
|
||||
messageReplies#83d60fc2 flags:# comments:flags.0?true replies:int replies_pts:int recent_repliers:flags.1?Vector<Peer> channel_id:flags.0?long max_id:flags.2?int read_max_id:flags.3?int = MessageReplies;
|
||||
|
||||
@ -1286,9 +1299,10 @@ account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordR
|
||||
account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
|
||||
account.resetPasswordOk#e926d63e = account.ResetPasswordResult;
|
||||
|
||||
sponsoredMessage#3a836df8 flags:# recommended:flags.5?true random_id:bytes from_id:flags.3?Peer chat_invite:flags.4?ChatInvite chat_invite_hash:flags.4?string channel_post:flags.2?int start_param:flags.0?string message:string entities:flags.1?Vector<MessageEntity> = SponsoredMessage;
|
||||
sponsoredMessage#3a836df8 flags:# recommended:flags.5?true show_peer_photo:flags.6?true random_id:bytes from_id:flags.3?Peer chat_invite:flags.4?ChatInvite chat_invite_hash:flags.4?string channel_post:flags.2?int start_param:flags.0?string message:string entities:flags.1?Vector<MessageEntity> = SponsoredMessage;
|
||||
|
||||
messages.sponsoredMessages#65a4c7d5 messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
|
||||
messages.sponsoredMessages#c9ee1d87 flags:# posts_between:flags.0?int messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
|
||||
messages.sponsoredMessagesEmpty#1839490f = messages.SponsoredMessages;
|
||||
|
||||
searchResultsCalendarPeriod#c9b0539f date:int min_msg_id:int max_msg_id:int count:int = SearchResultsCalendarPeriod;
|
||||
|
||||
@ -1420,6 +1434,13 @@ messageExtendedMedia#ee479c64 media:MessageMedia = MessageExtendedMedia;
|
||||
|
||||
stickerKeyword#fcfeb29c document_id:long keyword:Vector<string> = StickerKeyword;
|
||||
|
||||
username#b4073647 flags:# editable:flags.0?true active:flags.1?true username:string = Username;
|
||||
|
||||
forumTopicDeleted#23f109b id:int = ForumTopic;
|
||||
forumTopic#71701da9 flags:# my:flags.1?true closed:flags.2?true pinned:flags.3?true short:flags.5?true id:int date:int title:string icon_color:int icon_emoji_id:flags.0?long top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int from_id:Peer notify_settings:PeerNotifySettings draft:flags.4?DraftMessage = ForumTopic;
|
||||
|
||||
messages.forumTopics#367617d3 flags:# order_by_create_date:flags.0?true count:int topics:Vector<ForumTopic> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> pts:int = messages.ForumTopics;
|
||||
|
||||
---functions---
|
||||
|
||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||
@ -1511,7 +1532,7 @@ account.createTheme#652e4400 flags:# slug:string title:string document:flags.2?I
|
||||
account.updateTheme#2bf40ccc flags:# format:string theme:InputTheme slug:flags.0?string title:flags.1?string document:flags.2?InputDocument settings:flags.3?Vector<InputThemeSettings> = Theme;
|
||||
account.saveTheme#f257106c theme:InputTheme unsave:Bool = Bool;
|
||||
account.installTheme#c727bb3b flags:# dark:flags.0?true theme:flags.1?InputTheme format:flags.2?string base_theme:flags.3?BaseTheme = Bool;
|
||||
account.getTheme#8d9d742b format:string theme:InputTheme document_id:long = Theme;
|
||||
account.getTheme#3a5869ec format:string theme:InputTheme = Theme;
|
||||
account.getThemes#7206e458 format:string hash:long = account.Themes;
|
||||
account.setContentSettings#b574b16b flags:# sensitive_enabled:flags.0?true = Bool;
|
||||
account.getContentSettings#8b9b4dae = account.ContentSettings;
|
||||
@ -1531,6 +1552,8 @@ 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;
|
||||
account.reorderUsernames#ef500eab order:Vector<string> = Bool;
|
||||
account.toggleUsername#58d6b376 username:string active:Bool = Bool;
|
||||
|
||||
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
||||
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
|
||||
@ -1567,9 +1590,9 @@ 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 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.sendMessage#1cc20387 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 top_msg_id:flags.9?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#7547c966 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 top_msg_id:flags.9?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#c661bbc4 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 top_msg_id:flags.9?int schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.reportSpam#cf1592db peer:InputPeer = Bool;
|
||||
messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings;
|
||||
messages.report#8953ab4e peer:InputPeer id:Vector<int> reason:ReportReason message:string = Bool;
|
||||
@ -1612,14 +1635,14 @@ messages.getSavedGifs#5cf09635 hash:long = messages.SavedGifs;
|
||||
messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
|
||||
messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
|
||||
messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool;
|
||||
messages.sendInlineBotResult#7aa11297 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.sendInlineBotResult#d3fbdccb flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int top_msg_id:flags.9?int random_id:long query_id:long id:string schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
|
||||
messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
|
||||
messages.editMessage#48f71778 flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int = Updates;
|
||||
messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Bool;
|
||||
messages.getBotCallbackAnswer#9342ca07 flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes password:flags.2?InputCheckPasswordSRP = messages.BotCallbackAnswer;
|
||||
messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
|
||||
messages.getPeerDialogs#e470bcfd peers:Vector<InputDialogPeer> = messages.PeerDialogs;
|
||||
messages.saveDraft#bc39e14b flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> = Bool;
|
||||
messages.saveDraft#b4331e3f flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int top_msg_id:flags.2?int peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> = Bool;
|
||||
messages.getAllDrafts#6a3f8d65 = Updates;
|
||||
messages.getFeaturedStickers#64780b14 hash:long = messages.FeaturedStickers;
|
||||
messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool;
|
||||
@ -1645,10 +1668,10 @@ messages.uploadMedia#519bc2b1 peer:InputPeer media:InputMedia = MessageMedia;
|
||||
messages.sendScreenshotNotification#c97df020 peer:InputPeer reply_to_msg_id:int random_id:long = Updates;
|
||||
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.getUnreadMentions#f107e790 flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
|
||||
messages.readMentions#36e5bf4d flags:# peer:InputPeer top_msg_id:flags.0?int = 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 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.sendMultiMedia#b6f11a1c 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 top_msg_id:flags.9?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>;
|
||||
@ -1665,7 +1688,7 @@ messages.getEmojiKeywords#35a0e062 lang_code:string = EmojiKeywordsDifference;
|
||||
messages.getEmojiKeywordsDifference#1508b6af lang_code:string from_version:int = EmojiKeywordsDifference;
|
||||
messages.getEmojiKeywordsLanguages#4e9963b2 lang_codes:Vector<string> = Vector<EmojiLanguage>;
|
||||
messages.getEmojiURL#d5b10c26 lang_code:string = EmojiURL;
|
||||
messages.getSearchCounters#732eef00 peer:InputPeer filters:Vector<MessagesFilter> = Vector<messages.SearchCounter>;
|
||||
messages.getSearchCounters#ae7cc1 flags:# peer:InputPeer top_msg_id:flags.0?int filters:Vector<MessagesFilter> = Vector<messages.SearchCounter>;
|
||||
messages.requestUrlAuth#198fb446 flags:# peer:flags.1?InputPeer msg_id:flags.1?int button_id:flags.1?int url:flags.2?string = UrlAuthResult;
|
||||
messages.acceptUrlAuth#b12c7125 flags:# write_allowed:flags.0?true peer:flags.1?InputPeer msg_id:flags.1?int button_id:flags.1?int url:flags.2?string = UrlAuthResult;
|
||||
messages.hidePeerSettingsBar#4facb138 peer:InputPeer = Bool;
|
||||
@ -1683,7 +1706,7 @@ messages.getOldFeaturedStickers#7ed094a1 offset:int limit:int hash:long = messag
|
||||
messages.getReplies#22ddd30c peer:InputPeer msg_id:int offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;
|
||||
messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage;
|
||||
messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool;
|
||||
messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory;
|
||||
messages.unpinAllMessages#ee22b9a8 flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory;
|
||||
messages.deleteChat#5bd0ee50 chat_id:long = Bool;
|
||||
messages.deletePhoneCallHistory#f9cbe409 flags:# revoke:flags.0?true = messages.AffectedFoundMessages;
|
||||
messages.checkHistoryImport#43fe19f3 import_head:string = messages.HistoryImportParsed;
|
||||
@ -1714,14 +1737,14 @@ messages.setChatAvailableReactions#feb16771 peer:InputPeer available_reactions:C
|
||||
messages.getAvailableReactions#18dea0ac hash:int = messages.AvailableReactions;
|
||||
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;
|
||||
messages.getUnreadReactions#3223495b flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
|
||||
messages.readReactions#54aa7f8e flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory;
|
||||
messages.searchSentMedia#107e31a0 q:string filter:MessagesFilter limit:int = messages.Messages;
|
||||
messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots;
|
||||
messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot;
|
||||
messages.toggleBotInAttachMenu#1aee33af bot:InputUser enabled:Bool = Bool;
|
||||
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.requestWebView#178b480b 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 top_msg_id:flags.9?int send_as:flags.13?InputPeer = WebViewResult;
|
||||
messages.prolongWebView#7ff34309 flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to_msg_id:flags.0?int top_msg_id:flags.9?int send_as:flags.13?InputPeer = Bool;
|
||||
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;
|
||||
@ -1819,6 +1842,17 @@ channels.getSendAs#dc770ee peer:InputPeer = channels.SendAsPeers;
|
||||
channels.deleteParticipantHistory#367544db channel:InputChannel participant:InputPeer = messages.AffectedHistory;
|
||||
channels.toggleJoinToSend#e4cb9580 channel:InputChannel enabled:Bool = Updates;
|
||||
channels.toggleJoinRequest#4c2985b6 channel:InputChannel enabled:Bool = Updates;
|
||||
channels.reorderUsernames#b45ced1d channel:InputChannel order:Vector<string> = Bool;
|
||||
channels.toggleUsername#50f24105 channel:InputChannel username:string active:Bool = Bool;
|
||||
channels.deactivateAllUsernames#a245dd3 channel:InputChannel = Bool;
|
||||
channels.toggleForum#a4298b29 channel:InputChannel enabled:Bool = Updates;
|
||||
channels.createForumTopic#f40c0224 flags:# channel:InputChannel title:string icon_color:flags.0?int icon_emoji_id:flags.3?long random_id:long send_as:flags.2?InputPeer = Updates;
|
||||
channels.getForumTopics#de560d1 flags:# channel:InputChannel q:flags.0?string offset_date:int offset_id:int offset_topic:int limit:int = messages.ForumTopics;
|
||||
channels.getForumTopicsByID#b0831eb9 channel:InputChannel topics:Vector<int> = messages.ForumTopics;
|
||||
channels.editForumTopic#6c883e2d flags:# channel:InputChannel topic_id:int title:flags.0?string icon_emoji_id:flags.1?long closed:flags.2?Bool = Updates;
|
||||
channels.updatePinnedForumTopic#6c2d9026 channel:InputChannel topic_id:int pinned:Bool = Updates;
|
||||
channels.deleteTopicHistory#34435f2d channel:InputChannel top_msg_id:int = messages.AffectedHistory;
|
||||
channels.reorderPinnedForumTopics#2950a18f flags:# force:flags.0?true channel:InputChannel order:Vector<int> = Updates;
|
||||
|
||||
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
|
||||
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
|
||||
@ -1897,4 +1931,4 @@ stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel
|
||||
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 147
|
||||
// LAYER 149
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user