Support member tags (#6749)
This commit is contained in:
parent
cdebd3b479
commit
cbce577dab
@ -345,6 +345,7 @@ export function buildChatMember(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
userId,
|
userId,
|
||||||
|
rank: 'rank' in member ? member.rank : undefined,
|
||||||
inviterId: 'inviterId' in member && member.inviterId
|
inviterId: 'inviterId' in member && member.inviterId
|
||||||
? buildApiPeerId(member.inviterId, 'user')
|
? buildApiPeerId(member.inviterId, 'user')
|
||||||
: undefined,
|
: undefined,
|
||||||
@ -355,7 +356,6 @@ export function buildChatMember(
|
|||||||
: undefined,
|
: undefined,
|
||||||
bannedRights: 'bannedRights' in member ? omitVirtualClassFields(member.bannedRights) : undefined,
|
bannedRights: 'bannedRights' in member ? omitVirtualClassFields(member.bannedRights) : undefined,
|
||||||
adminRights: 'adminRights' in member ? omitVirtualClassFields(member.adminRights) : undefined,
|
adminRights: 'adminRights' in member ? omitVirtualClassFields(member.adminRights) : undefined,
|
||||||
customTitle: 'rank' in member ? member.rank : undefined,
|
|
||||||
isViaRequest: 'viaRequest' in member ? member.viaRequest : undefined,
|
isViaRequest: 'viaRequest' in member ? member.viaRequest : undefined,
|
||||||
...((member instanceof GramJs.ChannelParticipantAdmin || member instanceof GramJs.ChatParticipantAdmin) && {
|
...((member instanceof GramJs.ChannelParticipantAdmin || member instanceof GramJs.ChatParticipantAdmin) && {
|
||||||
isAdmin: true,
|
isAdmin: true,
|
||||||
|
|||||||
@ -297,6 +297,7 @@ export function buildApiMessageWithChatId(
|
|||||||
paidMessageStars: toJSNumber(mtpMessage.paidMessageStars),
|
paidMessageStars: toJSNumber(mtpMessage.paidMessageStars),
|
||||||
restrictionReasons,
|
restrictionReasons,
|
||||||
summaryLanguageCode: mtpMessage.summaryFromLanguage,
|
summaryLanguageCode: mtpMessage.summaryFromLanguage,
|
||||||
|
fromRank: mtpMessage.fromRank,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1411,8 +1411,8 @@ export function updateChatMemberBannedRights({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function updateChatAdmin({
|
export function updateChatAdmin({
|
||||||
chat, user, adminRights, customTitle = DEFAULT_PRIMITIVES.STRING,
|
chat, user, adminRights, rank,
|
||||||
}: { chat: ApiChat; user: ApiUser; adminRights: ApiChatAdminRights; customTitle?: string }) {
|
}: { chat: ApiChat; user: ApiUser; adminRights: ApiChatAdminRights; rank?: string }) {
|
||||||
const channel = buildInputChannel(chat.id, chat.accessHash);
|
const channel = buildInputChannel(chat.id, chat.accessHash);
|
||||||
const userId = buildInputUser(user.id, user.accessHash);
|
const userId = buildInputUser(user.id, user.accessHash);
|
||||||
|
|
||||||
@ -1420,7 +1420,7 @@ export function updateChatAdmin({
|
|||||||
channel,
|
channel,
|
||||||
userId,
|
userId,
|
||||||
adminRights: buildChatAdminRights(adminRights),
|
adminRights: buildChatAdminRights(adminRights),
|
||||||
rank: customTitle,
|
rank,
|
||||||
}), {
|
}), {
|
||||||
shouldReturnTrue: true,
|
shouldReturnTrue: true,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1112,6 +1112,24 @@ export async function deleteParticipantHistory({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function editChatParticipantRank({
|
||||||
|
chat, peer, rank,
|
||||||
|
}: {
|
||||||
|
chat: ApiChat;
|
||||||
|
peer: ApiPeer;
|
||||||
|
rank: string;
|
||||||
|
}) {
|
||||||
|
const participant = buildInputPeer(peer.id, peer.accessHash);
|
||||||
|
|
||||||
|
return invokeRequest(new GramJs.messages.EditChatParticipantRank({
|
||||||
|
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||||
|
participant,
|
||||||
|
rank,
|
||||||
|
}), {
|
||||||
|
shouldReturnTrue: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function deleteScheduledMessages({
|
export function deleteScheduledMessages({
|
||||||
chat, messageIds,
|
chat, messageIds,
|
||||||
}: {
|
}: {
|
||||||
|
|||||||
@ -654,6 +654,13 @@ export function updater(update: Update) {
|
|||||||
id: buildApiPeerId(update.chatId, 'chat'),
|
id: buildApiPeerId(update.chatId, 'chat'),
|
||||||
deletedMemberId: buildApiPeerId(update.userId, 'user'),
|
deletedMemberId: buildApiPeerId(update.userId, 'user'),
|
||||||
});
|
});
|
||||||
|
} else if (update instanceof GramJs.UpdateChatParticipantRank) {
|
||||||
|
sendApiUpdate({
|
||||||
|
'@type': 'updateChatParticipantRank',
|
||||||
|
id: buildApiPeerId(update.chatId, 'chat'),
|
||||||
|
userId: buildApiPeerId(update.userId, 'user'),
|
||||||
|
rank: update.rank,
|
||||||
|
});
|
||||||
} else if (
|
} else if (
|
||||||
update instanceof GramJs.UpdatePinnedMessages
|
update instanceof GramJs.UpdatePinnedMessages
|
||||||
|| update instanceof GramJs.UpdatePinnedChannelMessages
|
|| update instanceof GramJs.UpdatePinnedChannelMessages
|
||||||
|
|||||||
@ -160,13 +160,13 @@ export interface ApiChatFullInfo {
|
|||||||
|
|
||||||
export interface ApiChatMember {
|
export interface ApiChatMember {
|
||||||
userId: string;
|
userId: string;
|
||||||
|
rank?: string;
|
||||||
inviterId?: string;
|
inviterId?: string;
|
||||||
joinedDate?: number;
|
joinedDate?: number;
|
||||||
kickedByUserId?: string;
|
kickedByUserId?: string;
|
||||||
promotedByUserId?: string;
|
promotedByUserId?: string;
|
||||||
bannedRights?: ApiChatBannedRights;
|
bannedRights?: ApiChatBannedRights;
|
||||||
adminRights?: ApiChatAdminRights;
|
adminRights?: ApiChatAdminRights;
|
||||||
customTitle?: string;
|
|
||||||
isAdmin?: true;
|
isAdmin?: true;
|
||||||
isOwner?: true;
|
isOwner?: true;
|
||||||
isViaRequest?: true;
|
isViaRequest?: true;
|
||||||
@ -188,6 +188,7 @@ export interface ApiChatAdminRights {
|
|||||||
editStories?: true;
|
editStories?: true;
|
||||||
deleteStories?: true;
|
deleteStories?: true;
|
||||||
manageDirectMessages?: true;
|
manageDirectMessages?: true;
|
||||||
|
manageRanks?: true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiChatBannedRights {
|
export interface ApiChatBannedRights {
|
||||||
@ -211,6 +212,7 @@ export interface ApiChatBannedRights {
|
|||||||
sendVoices?: true;
|
sendVoices?: true;
|
||||||
sendDocs?: true;
|
sendDocs?: true;
|
||||||
sendPlain?: true;
|
sendPlain?: true;
|
||||||
|
editRank?: true;
|
||||||
untilDate?: number;
|
untilDate?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -698,6 +698,7 @@ export interface ApiMessage {
|
|||||||
paidMessageStars?: number;
|
paidMessageStars?: number;
|
||||||
restrictionReasons?: ApiRestrictionReason[];
|
restrictionReasons?: ApiRestrictionReason[];
|
||||||
summaryLanguageCode?: string;
|
summaryLanguageCode?: string;
|
||||||
|
fromRank?: string;
|
||||||
|
|
||||||
isTypingDraft?: boolean; // Local field
|
isTypingDraft?: boolean; // Local field
|
||||||
}
|
}
|
||||||
|
|||||||
@ -179,6 +179,13 @@ export type ApiUpdateChatMembers = {
|
|||||||
deletedMemberId?: string;
|
deletedMemberId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ApiUpdateChatParticipantRank = {
|
||||||
|
'@type': 'updateChatParticipantRank';
|
||||||
|
id: string;
|
||||||
|
userId: string;
|
||||||
|
rank: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type ApiUpdatePinnedChatIds = {
|
export type ApiUpdatePinnedChatIds = {
|
||||||
'@type': 'updatePinnedChatIds';
|
'@type': 'updatePinnedChatIds';
|
||||||
ids?: string[];
|
ids?: string[];
|
||||||
@ -924,7 +931,8 @@ export type ApiUpdate = (
|
|||||||
ApiUpdateReady | ApiUpdateSession | ApiUpdateWebAuthTokenFailed | ApiUpdateRequestUserUpdate |
|
ApiUpdateReady | ApiUpdateSession | ApiUpdateWebAuthTokenFailed | ApiUpdateRequestUserUpdate |
|
||||||
ApiUpdateAuthorizationState | ApiUpdateAuthorizationError | ApiUpdateConnectionState | ApiUpdateCurrentUser |
|
ApiUpdateAuthorizationState | ApiUpdateAuthorizationError | ApiUpdateConnectionState | ApiUpdateCurrentUser |
|
||||||
ApiUpdateChat | ApiUpdateChatTypingStatus | ApiUpdateChatFullInfo | ApiUpdatePinnedChatIds |
|
ApiUpdateChat | ApiUpdateChatTypingStatus | ApiUpdateChatFullInfo | ApiUpdatePinnedChatIds |
|
||||||
ApiUpdateChatMembers | ApiUpdateChatJoin | ApiUpdateChatLeave | ApiUpdateChatPinned | ApiUpdatePinnedMessageIds |
|
ApiUpdateChatMembers | ApiUpdateChatParticipantRank | ApiUpdateChatJoin | ApiUpdateChatLeave
|
||||||
|
| ApiUpdateChatPinned | ApiUpdatePinnedMessageIds |
|
||||||
ApiUpdateChatListType | ApiUpdateChatFolder | ApiUpdateChatFoldersOrder | ApiUpdateRecommendedChatFolders |
|
ApiUpdateChatListType | ApiUpdateChatFolder | ApiUpdateChatFoldersOrder | ApiUpdateRecommendedChatFolders |
|
||||||
ApiUpdateNewMessage | ApiUpdateMessage | ApiUpdateThreadInfo | ApiUpdateCommonBoxMessages | ApiUpdatePasskeyOption |
|
ApiUpdateNewMessage | ApiUpdateMessage | ApiUpdateThreadInfo | ApiUpdateCommonBoxMessages | ApiUpdatePasskeyOption |
|
||||||
ApiUpdateDeleteMessages | ApiUpdateMessagePoll | ApiUpdateMessagePollVote | ApiUpdateDeleteHistory |
|
ApiUpdateDeleteMessages | ApiUpdateMessagePoll | ApiUpdateMessagePollVote | ApiUpdateDeleteHistory |
|
||||||
|
|||||||
1
src/assets/font-icons/user-tag.svg
Normal file
1
src/assets/font-icons/user-tag.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 32 32"><path d="M14.98 14.686a6.31 6.31 0 1 0 .003-12.62h-.002a6.31 6.31 0 0 0 0 12.62m-.224 11.128a4.4 4.4 0 0 1-.555-.708l-.153-.228-.1-.28a3.7 3.7 0 0 1-.206-1.16v-4.972c0-.202.023-.4.06-.594-6.067.298-9.243 2.86-10.606 4.377-.613.683-.837 1.595-.837 2.513a4.123 4.123 0 0 0 4.123 4.123h11.344z"/><path d="M29.64 24.56c0 .16-.053.321-.106.535-.054.214-.16.32-.321.48l-4.971 4.972c-.16.16-.268.214-.481.32-.214.108-.321.054-.481.054s-.321-.053-.535-.107c-.214-.053-.32-.16-.481-.32L16.17 24.4a3 3 0 0 1-.32-.428 2 2 0 0 1-.107-.534v-4.971c0-.374.16-.695.427-.962s.588-.428.963-.428h4.97c.161 0 .375.053.535.107.16.053.32.16.428.32l6.147 6.094q.24.24.32.481c.054.16.107.321.107.481m-9.728-4.383c0-.267-.107-.534-.32-.748-.214-.16-.428-.267-.749-.267s-.534.107-.748.32-.321.428-.321.749c0 .267.107.534.32.748s.428.32.75.32.534-.106.747-.32c.214-.267.321-.481.321-.802"/></svg>
|
||||||
|
After Width: | Height: | Size: 950 B |
@ -130,6 +130,7 @@
|
|||||||
"UserRestrictionsNoChangeInfo" = "can't change Info";
|
"UserRestrictionsNoChangeInfo" = "can't change Info";
|
||||||
"UserRestrictionsInviteUsers" = "Add Users";
|
"UserRestrictionsInviteUsers" = "Add Users";
|
||||||
"UserRestrictionsPinMessages" = "Pin Messages";
|
"UserRestrictionsPinMessages" = "Pin Messages";
|
||||||
|
"UserRestrictionsEditRank" = "Edit Own Tags";
|
||||||
"ChatPermissionNotAvailable" = "This permission is not available in public groups.";
|
"ChatPermissionNotAvailable" = "This permission is not available in public groups.";
|
||||||
"StatsMessageInteractionsTitle" = "INTERACTIONS";
|
"StatsMessageInteractionsTitle" = "INTERACTIONS";
|
||||||
"StatsGroupGrowthTitle" = "GROWTH";
|
"StatsGroupGrowthTitle" = "GROWTH";
|
||||||
@ -541,6 +542,7 @@
|
|||||||
"PrivacyExceptions" = "Exceptions";
|
"PrivacyExceptions" = "Exceptions";
|
||||||
"AlwaysAllow" = "Always Allow";
|
"AlwaysAllow" = "Always Allow";
|
||||||
"EditAdminAddUsers" = "Add Users";
|
"EditAdminAddUsers" = "Add Users";
|
||||||
|
"EditAdminEditRank" = "Edit Member Tags";
|
||||||
"NeverAllow" = "Never Allow";
|
"NeverAllow" = "Never Allow";
|
||||||
"AlwaysAllowPlaceholder" = "Always allow...";
|
"AlwaysAllowPlaceholder" = "Always allow...";
|
||||||
"NeverAllowPlaceholder" = "Never allow...";
|
"NeverAllowPlaceholder" = "Never allow...";
|
||||||
@ -966,7 +968,7 @@
|
|||||||
"StartVoipChatPermission" = "Manage Video Chats";
|
"StartVoipChatPermission" = "Manage Video Chats";
|
||||||
"EditAdminSendAnonymously" = "Remain Anonymous";
|
"EditAdminSendAnonymously" = "Remain Anonymous";
|
||||||
"ChannelEditAdminCannotEdit" = "You can't edit the rights of this admin.";
|
"ChannelEditAdminCannotEdit" = "You can't edit the rights of this admin.";
|
||||||
"EditAdminRank" = "Custom title";
|
"EditAdminRank" = "Member tag";
|
||||||
"EditAdminRemoveAdmin" = "Dismiss Admin";
|
"EditAdminRemoveAdmin" = "Dismiss Admin";
|
||||||
"EditAdminTransferChannelOwnership" = "Transfer Channel Ownership";
|
"EditAdminTransferChannelOwnership" = "Transfer Channel Ownership";
|
||||||
"EditAdminTransferGroupOwnership" = "Transfer Group Ownership";
|
"EditAdminTransferGroupOwnership" = "Transfer Group Ownership";
|
||||||
@ -2732,4 +2734,19 @@
|
|||||||
"GiftPreviewToggleRegularModels" = "View Primary Models >";
|
"GiftPreviewToggleRegularModels" = "View Primary Models >";
|
||||||
"AriaGiftPreviewPlay" = "Play random previews";
|
"AriaGiftPreviewPlay" = "Play random previews";
|
||||||
"AriaGiftPreviewStop" = "Pause random previews";
|
"AriaGiftPreviewStop" = "Pause random previews";
|
||||||
|
"RankModalEdit" = "Edit Tag";
|
||||||
|
"RankModalEditMy" = "Edit My Tag";
|
||||||
|
"MemberContextEditRank" = "Edit Tag";
|
||||||
|
"RankMemberTag" = "Member Tag";
|
||||||
|
"RankAdminTag" = "Admin Tag";
|
||||||
|
"RankOwnerTag" = "Owner Tag";
|
||||||
|
"RankModalMemberTagTitle" = "Member Tag";
|
||||||
|
"RankModalAdminTagTitle" = "Admin Tag";
|
||||||
|
"RankModalOwnerTagTitle" = "Owner Tag";
|
||||||
|
"RankModalMemberText" = "This gray tag {tag} is **{author}**'s member tag in **{group}**.";
|
||||||
|
"RankModalAdminText" = "This green tag {tag} is **{author}**'s admin tag in **{group}**.";
|
||||||
|
"RankModalOwnerText" = "This purple tag {tag} is **{author}**'s owner tag in **{group}**.";
|
||||||
|
"RankEditSave" = "Edit Tag";
|
||||||
|
"RankEditTextOwn" = "Share your role, title or how you're known in this group. Your tag is visible to all members.";
|
||||||
|
"RankEditText" = "Add a short tag next to {user}'s name.";
|
||||||
"MenuAddCaption" = "Add Caption";
|
"MenuAddCaption" = "Add Caption";
|
||||||
|
|||||||
@ -105,6 +105,8 @@ export { default as OneTimeMediaModal } from '../components/modals/oneTimeMedia/
|
|||||||
export { default as WebAppsCloseConfirmationModal } from '../components/main/WebAppsCloseConfirmationModal';
|
export { default as WebAppsCloseConfirmationModal } from '../components/main/WebAppsCloseConfirmationModal';
|
||||||
export { default as FrozenAccountModal } from '../components/modals/frozenAccount/FrozenAccountModal';
|
export { default as FrozenAccountModal } from '../components/modals/frozenAccount/FrozenAccountModal';
|
||||||
export { default as ProfileRatingModal } from '../components/modals/profileRating/ProfileRatingModal';
|
export { default as ProfileRatingModal } from '../components/modals/profileRating/ProfileRatingModal';
|
||||||
|
export { default as EditRankModal } from '../components/modals/rank/EditRankModal';
|
||||||
|
export { default as RankModal } from '../components/modals/rank/RankModal';
|
||||||
export { default as QuickPreviewModal } from '../components/modals/quickPreview/QuickPreviewModal';
|
export { default as QuickPreviewModal } from '../components/modals/quickPreview/QuickPreviewModal';
|
||||||
export { default as StealthModeModal } from '../components/modals/storyStealthMode/StealthModeModal';
|
export { default as StealthModeModal } from '../components/modals/storyStealthMode/StealthModeModal';
|
||||||
export { default as LeaveGroupModal } from '../components/modals/leaveGroup/LeaveGroupModal';
|
export { default as LeaveGroupModal } from '../components/modals/leaveGroup/LeaveGroupModal';
|
||||||
|
|||||||
@ -254,7 +254,7 @@ const GroupCallParticipantVideo: FC<OwnProps & StateProps> = ({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<Skeleton className={buildClassName(styles.video, styles.loader)} />
|
<Skeleton className={buildClassName(styles.video, styles.loader)} animation="wave" />
|
||||||
)}
|
)}
|
||||||
{stream && (
|
{stream && (
|
||||||
<video
|
<video
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
.root {
|
.root {
|
||||||
padding: 0.25em 0.5em;
|
padding: 0.25rem 0.5rem;
|
||||||
border-radius: 1em;
|
border-radius: 1em;
|
||||||
|
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
@ -12,6 +12,11 @@
|
|||||||
transition: 150ms filter ease-in;
|
transition: 150ms filter ease-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.plain {
|
||||||
|
color: rgba(var(--color-text-meta-rgb), 0.75);
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.clickable {
|
.clickable {
|
||||||
cursor: var(--custom-cursor, pointer);
|
cursor: var(--custom-cursor, pointer);
|
||||||
|
|
||||||
@ -19,3 +24,7 @@
|
|||||||
filter: brightness(1.1);
|
filter: brightness(1.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inline {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,12 +1,14 @@
|
|||||||
import type React from '../../lib/teact/teact';
|
import { type TeactNode } from '../../lib/teact/teact';
|
||||||
|
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
|
|
||||||
import styles from './BadgeButton.module.scss';
|
import styles from './BadgeButton.module.scss';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
children: React.ReactNode;
|
children: TeactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
isPlain?: boolean;
|
||||||
|
inline?: boolean;
|
||||||
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
||||||
onMouseDown?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
onMouseDown?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
||||||
};
|
};
|
||||||
@ -14,12 +16,20 @@ type OwnProps = {
|
|||||||
const BadgeButton = ({
|
const BadgeButton = ({
|
||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
|
isPlain,
|
||||||
|
inline,
|
||||||
onClick,
|
onClick,
|
||||||
onMouseDown,
|
onMouseDown,
|
||||||
}: OwnProps) => {
|
}: OwnProps) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={buildClassName(styles.root, onClick && styles.clickable, className)}
|
className={buildClassName(
|
||||||
|
styles.root,
|
||||||
|
isPlain && styles.plain,
|
||||||
|
onClick && styles.clickable,
|
||||||
|
inline && styles.inline,
|
||||||
|
className,
|
||||||
|
)}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
onMouseDown={onMouseDown}
|
onMouseDown={onMouseDown}
|
||||||
>
|
>
|
||||||
|
|||||||
132
src/components/common/PreviewBlock.module.scss
Normal file
132
src/components/common/PreviewBlock.module.scss
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
.root {
|
||||||
|
--preview-background-overscan: 50%;
|
||||||
|
|
||||||
|
isolation: isolate;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
border-radius: 1.5rem;
|
||||||
|
|
||||||
|
background-color: var(--theme-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.background {
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
inset: calc(var(--preview-background-overscan) * -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
|
||||||
|
min-height: 100%;
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message,
|
||||||
|
.bubble {
|
||||||
|
--preview-message-background: var(--color-background);
|
||||||
|
--preview-message-sender-color: var(--accent-color);
|
||||||
|
--preview-message-shadow: 0 1px 2px var(--color-default-shadow);
|
||||||
|
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
padding: 0.375rem 0.5rem 0.4375rem 0.625rem;
|
||||||
|
border-radius: 1.125rem;
|
||||||
|
|
||||||
|
background: var(--preview-message-background);
|
||||||
|
box-shadow: var(--preview-message-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageWithAvatar {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: flex-end;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubble {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
min-width: 0;
|
||||||
|
height: 1.25rem;
|
||||||
|
margin-bottom: 0.375rem;
|
||||||
|
|
||||||
|
font-size: calc(var(--message-text-size, 1rem) - 0.125rem);
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sender {
|
||||||
|
overflow: hidden;
|
||||||
|
flex: 0 1 auto;
|
||||||
|
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
color: var(--preview-message-sender-color);
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
display: flex;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
align-items: center;
|
||||||
|
margin-inline-start: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.375rem;
|
||||||
|
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
font-size: var(--message-text-size, 1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
user-select: none;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
display: inline-flex;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
align-items: flex-end;
|
||||||
|
|
||||||
|
font-size: 0.75rem;
|
||||||
|
line-height: 1;
|
||||||
|
color: rgba(var(--color-text-meta-rgb), 0.75);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
241
src/components/common/PreviewBlock.tsx
Normal file
241
src/components/common/PreviewBlock.tsx
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
import type { FC, TeactNode } from '../../lib/teact/teact';
|
||||||
|
import { memo } from '../../lib/teact/teact';
|
||||||
|
import { withGlobal } from '../../global';
|
||||||
|
|
||||||
|
import type { ThemeKey } from '../../types';
|
||||||
|
|
||||||
|
import { selectTheme, selectThemeValues } from '../../global/selectors';
|
||||||
|
import buildClassName from '../../util/buildClassName';
|
||||||
|
import buildStyle from '../../util/buildStyle';
|
||||||
|
|
||||||
|
import useCustomBackground from '../../hooks/useCustomBackground';
|
||||||
|
|
||||||
|
import backgroundStyles from '../../styles/_patternBackground.module.scss';
|
||||||
|
import styles from './PreviewBlock.module.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
children: TeactNode;
|
||||||
|
className?: string;
|
||||||
|
style?: string;
|
||||||
|
contentClassName?: string;
|
||||||
|
backgroundClassName?: string;
|
||||||
|
backgroundStyle?: string;
|
||||||
|
backgroundColor?: string;
|
||||||
|
patternColor?: string;
|
||||||
|
customBackground?: string;
|
||||||
|
isBackgroundBlurred?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type StateProps = {
|
||||||
|
theme: ThemeKey;
|
||||||
|
themeBackgroundColor?: string;
|
||||||
|
themePatternColor?: string;
|
||||||
|
themeCustomBackground?: string;
|
||||||
|
themeIsBackgroundBlurred?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type MessageProps = {
|
||||||
|
children?: TeactNode;
|
||||||
|
className?: string;
|
||||||
|
style?: string;
|
||||||
|
bubbleClassName?: string;
|
||||||
|
bubbleStyle?: string;
|
||||||
|
headerClassName?: string;
|
||||||
|
bodyClassName?: string;
|
||||||
|
footerClassName?: string;
|
||||||
|
avatar?: TeactNode;
|
||||||
|
sender?: TeactNode;
|
||||||
|
badge?: TeactNode;
|
||||||
|
footer?: TeactNode;
|
||||||
|
time?: TeactNode;
|
||||||
|
senderColor?: string;
|
||||||
|
backgroundColor?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type MessageTimeProps = {
|
||||||
|
children?: TeactNode;
|
||||||
|
className?: string;
|
||||||
|
style?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type PreviewBlockMessageComponent = FC<MessageProps> & {
|
||||||
|
Time: FC<MessageTimeProps>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type PreviewBlockComponent = FC<OwnProps> & {
|
||||||
|
Message: PreviewBlockMessageComponent;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PreviewBlockBase = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
style,
|
||||||
|
contentClassName,
|
||||||
|
backgroundClassName,
|
||||||
|
backgroundStyle,
|
||||||
|
backgroundColor,
|
||||||
|
patternColor,
|
||||||
|
customBackground,
|
||||||
|
isBackgroundBlurred,
|
||||||
|
theme,
|
||||||
|
themeBackgroundColor,
|
||||||
|
themePatternColor,
|
||||||
|
themeCustomBackground,
|
||||||
|
themeIsBackgroundBlurred,
|
||||||
|
}: OwnProps & StateProps) => {
|
||||||
|
const resolvedBackgroundColor = backgroundColor ?? themeBackgroundColor;
|
||||||
|
const resolvedPatternColor = patternColor ?? themePatternColor;
|
||||||
|
const resolvedCustomBackground = customBackground ?? themeCustomBackground;
|
||||||
|
const resolvedIsBackgroundBlurred = isBackgroundBlurred ?? themeIsBackgroundBlurred;
|
||||||
|
const customBackgroundValue = useCustomBackground(theme, resolvedCustomBackground);
|
||||||
|
|
||||||
|
const backgroundClassNames = buildClassName(
|
||||||
|
styles.background,
|
||||||
|
backgroundStyles.background,
|
||||||
|
resolvedCustomBackground && backgroundStyles.customBgImage,
|
||||||
|
resolvedBackgroundColor && backgroundStyles.customBgColor,
|
||||||
|
resolvedCustomBackground && resolvedIsBackgroundBlurred && backgroundStyles.blurred,
|
||||||
|
backgroundClassName,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={buildClassName(styles.root, className)}
|
||||||
|
style={buildStyle(
|
||||||
|
resolvedPatternColor && `--pattern-color: ${resolvedPatternColor}`,
|
||||||
|
resolvedBackgroundColor && `--theme-background-color: ${resolvedBackgroundColor}`,
|
||||||
|
style,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={backgroundClassNames}
|
||||||
|
style={buildStyle(
|
||||||
|
customBackgroundValue && `--custom-background: ${customBackgroundValue}`,
|
||||||
|
backgroundStyle,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<div className={buildClassName(styles.content, contentClassName)}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const PreviewBlockMessage: FC<MessageProps> = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
style,
|
||||||
|
bubbleClassName,
|
||||||
|
bubbleStyle,
|
||||||
|
headerClassName,
|
||||||
|
bodyClassName,
|
||||||
|
footerClassName,
|
||||||
|
avatar,
|
||||||
|
sender,
|
||||||
|
badge,
|
||||||
|
footer,
|
||||||
|
time,
|
||||||
|
senderColor,
|
||||||
|
backgroundColor,
|
||||||
|
}) => {
|
||||||
|
const hasAvatar = avatar !== undefined;
|
||||||
|
const hasSender = sender !== undefined;
|
||||||
|
const hasBadge = badge !== undefined;
|
||||||
|
const hasChildren = children !== undefined;
|
||||||
|
const hasFooterContent = footer !== undefined;
|
||||||
|
const hasTime = time !== undefined;
|
||||||
|
const hasHeader = hasSender || hasBadge;
|
||||||
|
const hasFooter = hasFooterContent || hasTime;
|
||||||
|
const bubbleStyles = buildStyle(
|
||||||
|
senderColor && `--preview-message-sender-color: ${senderColor}`,
|
||||||
|
backgroundColor && `--preview-message-background: ${backgroundColor}`,
|
||||||
|
bubbleStyle,
|
||||||
|
);
|
||||||
|
const content = (
|
||||||
|
<>
|
||||||
|
{hasHeader ? (
|
||||||
|
<div className={buildClassName(styles.header, headerClassName)}>
|
||||||
|
{hasSender ? <span className={styles.sender}>{sender}</span> : undefined}
|
||||||
|
<span className={styles.spacer} />
|
||||||
|
{hasBadge ? <span className={styles.badge}>{badge}</span> : undefined}
|
||||||
|
</div>
|
||||||
|
) : undefined}
|
||||||
|
{hasChildren ? (
|
||||||
|
<div className={buildClassName(styles.body, bodyClassName)}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
) : undefined}
|
||||||
|
{hasFooter ? (
|
||||||
|
<div className={buildClassName(styles.footer, footerClassName)}>
|
||||||
|
{hasFooterContent ? footer : <PreviewBlockMessageTime>{time}</PreviewBlockMessageTime>}
|
||||||
|
</div>
|
||||||
|
) : undefined}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hasAvatar) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={buildClassName(styles.messageWithAvatar, className)}
|
||||||
|
style={style}
|
||||||
|
>
|
||||||
|
<div className={styles.avatar}>{avatar}</div>
|
||||||
|
<div
|
||||||
|
className={buildClassName(styles.bubble, bubbleClassName)}
|
||||||
|
style={bubbleStyles}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={buildClassName(styles.message, className, bubbleClassName)}
|
||||||
|
style={buildStyle(
|
||||||
|
senderColor && `--preview-message-sender-color: ${senderColor}`,
|
||||||
|
backgroundColor && `--preview-message-background: ${backgroundColor}`,
|
||||||
|
bubbleStyle,
|
||||||
|
style,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const PreviewBlockMessageTime: FC<MessageTimeProps> = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
style,
|
||||||
|
}) => (
|
||||||
|
<span className={buildClassName(styles.time, className)} style={style}>
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
const PreviewBlockMessageMemo = memo(PreviewBlockMessage) as PreviewBlockMessageComponent;
|
||||||
|
PreviewBlockMessageMemo.Time = memo(PreviewBlockMessageTime);
|
||||||
|
|
||||||
|
const PreviewBlock = memo(withGlobal<OwnProps>((global) => {
|
||||||
|
const theme = selectTheme(global);
|
||||||
|
const {
|
||||||
|
isBlurred: themeIsBackgroundBlurred,
|
||||||
|
background: themeCustomBackground,
|
||||||
|
backgroundColor: themeBackgroundColor,
|
||||||
|
patternColor: themePatternColor,
|
||||||
|
} = selectThemeValues(global, theme) || {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
theme,
|
||||||
|
themeBackgroundColor,
|
||||||
|
themePatternColor,
|
||||||
|
themeCustomBackground,
|
||||||
|
themeIsBackgroundBlurred,
|
||||||
|
};
|
||||||
|
})(PreviewBlockBase)) as PreviewBlockComponent;
|
||||||
|
|
||||||
|
PreviewBlock.Message = PreviewBlockMessageMemo;
|
||||||
|
|
||||||
|
export default PreviewBlock;
|
||||||
@ -19,6 +19,7 @@ import {
|
|||||||
} from '../../global/selectors';
|
} from '../../global/selectors';
|
||||||
import { selectThreadMessagesCount } from '../../global/selectors/threads';
|
import { selectThreadMessagesCount } from '../../global/selectors/threads';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
|
import { hasRank } from './helpers/chatMember';
|
||||||
import { REM } from './helpers/mediaDimensions';
|
import { REM } from './helpers/mediaDimensions';
|
||||||
import renderText from './helpers/renderText';
|
import renderText from './helpers/renderText';
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ import Avatar from './Avatar';
|
|||||||
import DotAnimation from './DotAnimation';
|
import DotAnimation from './DotAnimation';
|
||||||
import FullNameTitle from './FullNameTitle';
|
import FullNameTitle from './FullNameTitle';
|
||||||
import Icon from './icons/Icon';
|
import Icon from './icons/Icon';
|
||||||
|
import RankBadge from './RankBadge';
|
||||||
import TopicIcon from './TopicIcon';
|
import TopicIcon from './TopicIcon';
|
||||||
import TypingStatus from './TypingStatus';
|
import TypingStatus from './TypingStatus';
|
||||||
|
|
||||||
@ -58,7 +60,8 @@ type BaseOwnProps = {
|
|||||||
emojiStatusSize?: number;
|
emojiStatusSize?: number;
|
||||||
noStatusOrTyping?: boolean;
|
noStatusOrTyping?: boolean;
|
||||||
noRtl?: boolean;
|
noRtl?: boolean;
|
||||||
adminMember?: ApiChatMember;
|
chatMemberOriginId?: string;
|
||||||
|
chatMember?: ApiChatMember;
|
||||||
isSavedDialog?: boolean;
|
isSavedDialog?: boolean;
|
||||||
noAvatar?: boolean;
|
noAvatar?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -118,7 +121,8 @@ const PrivateChatInfo = ({
|
|||||||
isSavedMessages,
|
isSavedMessages,
|
||||||
isSavedDialog,
|
isSavedDialog,
|
||||||
areMessagesLoaded,
|
areMessagesLoaded,
|
||||||
adminMember,
|
chatMember,
|
||||||
|
chatMemberOriginId,
|
||||||
ripple,
|
ripple,
|
||||||
className,
|
className,
|
||||||
storyViewerOrigin,
|
storyViewerOrigin,
|
||||||
@ -237,10 +241,6 @@ const PrivateChatInfo = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const customTitle = adminMember
|
|
||||||
? adminMember.customTitle || oldLang(adminMember.isOwner ? 'GroupInfo.LabelOwner' : 'GroupInfo.LabelAdmin')
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
function renderNameTitle() {
|
function renderNameTitle() {
|
||||||
if (isTopic) {
|
if (isTopic) {
|
||||||
return (
|
return (
|
||||||
@ -248,18 +248,27 @@ const PrivateChatInfo = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (customTitle) {
|
if (chatMember && hasRank(chatMember)) {
|
||||||
return (
|
return (
|
||||||
<div className="info-name-title">
|
<div className="info-name-title">
|
||||||
<FullNameTitle
|
<FullNameTitle
|
||||||
peer={user!}
|
peer={customPeer || user!}
|
||||||
|
noFake={noFake}
|
||||||
|
noVerified={noVerified}
|
||||||
withEmojiStatus={!noEmojiStatus}
|
withEmojiStatus={!noEmojiStatus}
|
||||||
emojiStatusSize={emojiStatusSize}
|
emojiStatusSize={emojiStatusSize}
|
||||||
isSavedMessages={isSavedMessages}
|
isSavedMessages={isSavedMessages}
|
||||||
isSavedDialog={isSavedDialog}
|
isSavedDialog={isSavedDialog}
|
||||||
|
iconElement={iconElement}
|
||||||
onEmojiStatusClick={onEmojiStatusClick}
|
onEmojiStatusClick={onEmojiStatusClick}
|
||||||
/>
|
/>
|
||||||
{customTitle && <span className="custom-title">{customTitle}</span>}
|
<RankBadge
|
||||||
|
chatId={chatMemberOriginId!}
|
||||||
|
userId={chatMember.userId}
|
||||||
|
isAdmin={chatMember.isAdmin}
|
||||||
|
isOwner={chatMember.isOwner}
|
||||||
|
rank={chatMember.rank}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
58
src/components/common/RankBadge.tsx
Normal file
58
src/components/common/RankBadge.tsx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { memo } from '@teact';
|
||||||
|
import { getActions } from '../../global';
|
||||||
|
|
||||||
|
import buildClassName from '../../util/buildClassName';
|
||||||
|
|
||||||
|
import useLang from '../../hooks/useLang';
|
||||||
|
import useLastCallback from '../../hooks/useLastCallback';
|
||||||
|
import { getPeerColorClass } from '../../hooks/usePeerColor';
|
||||||
|
|
||||||
|
import BadgeButton from './BadgeButton';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
chatId: string;
|
||||||
|
userId: string;
|
||||||
|
isAdmin?: boolean;
|
||||||
|
isOwner?: boolean;
|
||||||
|
className?: string;
|
||||||
|
rank?: string;
|
||||||
|
isClickable?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const OWNER_PEER_COLOR = 2;
|
||||||
|
const ADMIN_PEER_COLOR = 3;
|
||||||
|
|
||||||
|
const RankBadge = ({
|
||||||
|
chatId, className, userId, isAdmin, isOwner, rank, isClickable,
|
||||||
|
}: OwnProps) => {
|
||||||
|
const { openRankModal } = getActions();
|
||||||
|
const lang = useLang();
|
||||||
|
const hasCustomColor = isOwner || isAdmin;
|
||||||
|
|
||||||
|
const rankText = rank || (isOwner && lang('ChannelCreator')) || (isAdmin && lang('ChannelAdmin'));
|
||||||
|
|
||||||
|
const handleClick = useLastCallback(() => {
|
||||||
|
if (!chatId) return;
|
||||||
|
openRankModal({ chatId, userId, isAdmin, isOwner, rank });
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!rankText) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BadgeButton
|
||||||
|
className={buildClassName(
|
||||||
|
hasCustomColor && getPeerColorClass(isOwner ? OWNER_PEER_COLOR : ADMIN_PEER_COLOR),
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
isPlain={!hasCustomColor}
|
||||||
|
inline
|
||||||
|
onClick={isClickable ? handleClick : undefined}
|
||||||
|
>
|
||||||
|
{rankText}
|
||||||
|
</BadgeButton>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(RankBadge);
|
||||||
5
src/components/common/helpers/chatMember.ts
Normal file
5
src/components/common/helpers/chatMember.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import type { ApiChatMember } from '../../../api/types';
|
||||||
|
|
||||||
|
export function hasRank(member?: ApiChatMember): boolean {
|
||||||
|
return Boolean(member && (member.rank || member.isOwner || member.isAdmin));
|
||||||
|
}
|
||||||
@ -201,7 +201,7 @@ const ChatExtra = ({
|
|||||||
return <img src={locationBlobUrl} alt="" className={styles.businessLocation} />;
|
return <img src={locationBlobUrl} alt="" className={styles.businessLocation} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Skeleton className={styles.businessLocation} />;
|
return <Skeleton className={styles.businessLocation} animation="wave" />;
|
||||||
}, [businessLocation, locationBlobUrl]);
|
}, [businessLocation, locationBlobUrl]);
|
||||||
|
|
||||||
const isTopicInfo = Boolean(topicId && topicId !== MAIN_THREAD_ID);
|
const isTopicInfo = Boolean(topicId && topicId !== MAIN_THREAD_ID);
|
||||||
|
|||||||
@ -249,6 +249,17 @@ const PermissionCheckboxList = ({
|
|||||||
onChange={handlePermissionChange}
|
onChange={handlePermissionChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className={buildClassName('ListItem', withCheckbox && 'with-checkbox')}>
|
||||||
|
<Checkbox
|
||||||
|
name="editRank"
|
||||||
|
checked={!permissions.editRank}
|
||||||
|
label={lang('UserRestrictionsEditRank')}
|
||||||
|
blocking
|
||||||
|
permissionGroup={permissionGroup}
|
||||||
|
onChange={handlePermissionChange}
|
||||||
|
disabled={getControlIsDisabled && getControlIsDisabled('editRank')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
className={buildClassName('ListItem', withCheckbox && 'with-checkbox')}
|
className={buildClassName('ListItem', withCheckbox && 'with-checkbox')}
|
||||||
onClick={shouldDisablePermissionForPublicGroup ? handleDisabledClick : undefined}
|
onClick={shouldDisablePermissionForPublicGroup ? handleDisabledClick : undefined}
|
||||||
|
|||||||
@ -208,6 +208,7 @@ const MessageListAccountInfo: FC<OwnProps & StateProps> = ({
|
|||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
forceAspectRatio
|
forceAspectRatio
|
||||||
|
animation="pulse"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isVerifyCodes && (
|
{isVerifyCodes && (
|
||||||
|
|||||||
@ -57,7 +57,7 @@ const Game: FC<OwnProps> = ({
|
|||||||
onClick={handleGameClick}
|
onClick={handleGameClick}
|
||||||
>
|
>
|
||||||
{!photoBlobUrl && !videoBlobUrl && (
|
{!photoBlobUrl && !videoBlobUrl && (
|
||||||
<Skeleton className="skeleton preview-content" />
|
<Skeleton className="skeleton preview-content" animation="pulse" />
|
||||||
)}
|
)}
|
||||||
{photoBlobUrl && (
|
{photoBlobUrl && (
|
||||||
<img
|
<img
|
||||||
|
|||||||
@ -116,6 +116,7 @@ const Invoice: FC<OwnProps> = ({
|
|||||||
width={width}
|
width={width}
|
||||||
height={photo.dimensions?.height}
|
height={photo.dimensions?.height}
|
||||||
forceAspectRatio
|
forceAspectRatio
|
||||||
|
animation="pulse"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -24,7 +24,7 @@ function LastEditTimeMenuItem({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuItem icon="clock-edit" className={styles.item}>
|
<MenuItem icon="clock-edit" className={styles.item}>
|
||||||
{shouldRenderSkeleton ? <Skeleton className={styles.skeleton} /> : Boolean(editDate)
|
{shouldRenderSkeleton ? <Skeleton className={styles.skeleton} animation="wave" /> : Boolean(editDate)
|
||||||
&& lang('Chat.PrivateMessageEditTimestamp.Date', formatDateAtTime(lang, editDate * 1000))}
|
&& lang('Chat.PrivateMessageEditTimestamp.Date', formatDateAtTime(lang, editDate * 1000))}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -184,7 +184,7 @@ const Location: FC<OwnProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderMap() {
|
function renderMap() {
|
||||||
if (!mapBlobUrl) return <Skeleton width={width} height={height} />;
|
if (!mapBlobUrl) return <Skeleton width={width} height={height} animation="pulse" />;
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
className="full-media map"
|
className="full-media map"
|
||||||
|
|||||||
@ -174,6 +174,7 @@ import Icon from '../../common/icons/Icon';
|
|||||||
import StarIcon from '../../common/icons/StarIcon';
|
import StarIcon from '../../common/icons/StarIcon';
|
||||||
import MessageText from '../../common/MessageText';
|
import MessageText from '../../common/MessageText';
|
||||||
import PeerColorWrapper from '../../common/PeerColorWrapper';
|
import PeerColorWrapper from '../../common/PeerColorWrapper';
|
||||||
|
import RankBadge from '../../common/RankBadge';
|
||||||
import ReactionStaticEmoji from '../../common/reactions/ReactionStaticEmoji';
|
import ReactionStaticEmoji from '../../common/reactions/ReactionStaticEmoji';
|
||||||
import Sparkles from '../../common/Sparkles';
|
import Sparkles from '../../common/Sparkles';
|
||||||
import TopicChip from '../../common/TopicChip';
|
import TopicChip from '../../common/TopicChip';
|
||||||
@ -302,7 +303,7 @@ type StateProps = {
|
|||||||
isTranscribing?: boolean;
|
isTranscribing?: boolean;
|
||||||
transcribedText?: string;
|
transcribedText?: string;
|
||||||
isPremium: boolean;
|
isPremium: boolean;
|
||||||
senderAdminMember?: ApiChatMember;
|
senderChatMember?: ApiChatMember;
|
||||||
messageTopic?: ApiTopic;
|
messageTopic?: ApiTopic;
|
||||||
hasTopicChip?: boolean;
|
hasTopicChip?: boolean;
|
||||||
chatTranslations?: ChatTranslatedMessages;
|
chatTranslations?: ChatTranslatedMessages;
|
||||||
@ -429,7 +430,7 @@ const Message = ({
|
|||||||
repliesThreadInfo,
|
repliesThreadInfo,
|
||||||
hasUnreadReaction,
|
hasUnreadReaction,
|
||||||
memoFirstUnreadIdRef,
|
memoFirstUnreadIdRef,
|
||||||
senderAdminMember,
|
senderChatMember,
|
||||||
messageTopic,
|
messageTopic,
|
||||||
hasTopicChip,
|
hasTopicChip,
|
||||||
chatTranslations,
|
chatTranslations,
|
||||||
@ -542,7 +543,7 @@ const Message = ({
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
id: messageId, chatId, forwardInfo, viaBotId, isTranscriptionError, factCheck,
|
id: messageId, chatId, forwardInfo, viaBotId, isTranscriptionError, factCheck,
|
||||||
isTypingDraft,
|
isTypingDraft, fromRank,
|
||||||
} = message;
|
} = message;
|
||||||
const hasSummary = Boolean(message.summaryLanguageCode);
|
const hasSummary = Boolean(message.summaryLanguageCode);
|
||||||
|
|
||||||
@ -1677,7 +1678,6 @@ const Message = ({
|
|||||||
const senderIsPremium = senderPeer && 'isPremium' in senderPeer && senderPeer.isPremium;
|
const senderIsPremium = senderPeer && 'isPremium' in senderPeer && senderPeer.isPremium;
|
||||||
|
|
||||||
const shouldRenderForwardAvatar = asForwarded && senderPeer;
|
const shouldRenderForwardAvatar = asForwarded && senderPeer;
|
||||||
const hasBotSenderUsername = botSender?.hasUsername;
|
|
||||||
return (
|
return (
|
||||||
<div className="message-title" dir="ltr">
|
<div className="message-title" dir="ltr">
|
||||||
{(senderTitle || asForwarded) ? (
|
{(senderTitle || asForwarded) ? (
|
||||||
@ -1733,16 +1733,20 @@ const Message = ({
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<div className="title-spacer" />
|
<div className="title-spacer" />
|
||||||
{!shouldSkipRenderAdminTitle && !hasBotSenderUsername ? (forwardInfo?.isLinkedChannelPost ? (
|
{(!shouldSkipRenderAdminTitle && !signature) ? (forwardInfo?.isLinkedChannelPost ? (
|
||||||
<span className="admin-title" dir="auto">{oldLang('DiscussChannel')}</span>
|
<span className="admin-title" dir="auto">{oldLang('DiscussChannel')}</span>
|
||||||
) : message.postAuthorTitle && isGroup && !asForwarded ? (
|
) : message.postAuthorTitle && isGroup && !asForwarded ? (
|
||||||
<span className="admin-title" dir="auto">{message.postAuthorTitle}</span>
|
<span className="admin-title" dir="auto">{message.postAuthorTitle}</span>
|
||||||
) : senderAdminMember && !asForwarded && !viaBotId ? (
|
) : (senderChatMember || fromRank) && !asForwarded ? (
|
||||||
<span className="admin-title" dir="auto">
|
<RankBadge
|
||||||
{senderAdminMember.customTitle || oldLang(
|
chatId={chatId}
|
||||||
senderAdminMember.isOwner ? 'GroupInfo.LabelOwner' : 'GroupInfo.LabelAdmin',
|
userId={(senderChatMember?.userId || sender?.id)!}
|
||||||
)}
|
isAdmin={senderChatMember?.isAdmin}
|
||||||
</span>
|
isOwner={senderChatMember?.isOwner}
|
||||||
|
rank={senderChatMember?.rank || fromRank}
|
||||||
|
className="admin-title-badge"
|
||||||
|
isClickable
|
||||||
|
/>
|
||||||
) : undefined) : undefined}
|
) : undefined) : undefined}
|
||||||
{canShowSenderBoosts && (
|
{canShowSenderBoosts && (
|
||||||
<span className="sender-boosts" aria-hidden>
|
<span className="sender-boosts" aria-hidden>
|
||||||
@ -2020,6 +2024,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const isChannel = chat && isChatChannel(chat);
|
const isChannel = chat && isChatChannel(chat);
|
||||||
const isGroup = chat && isChatGroup(chat);
|
const isGroup = chat && isChatGroup(chat);
|
||||||
const chatFullInfo = !isChatWithUser ? selectChatFullInfo(global, chatId) : undefined;
|
const chatFullInfo = !isChatWithUser ? selectChatFullInfo(global, chatId) : undefined;
|
||||||
|
const { adminMembersById, members, boostsApplied } = chatFullInfo || {};
|
||||||
const webPageStoryData = webPage?.story;
|
const webPageStoryData = webPage?.story;
|
||||||
const webPageStory = webPageStoryData
|
const webPageStory = webPageStoryData
|
||||||
? selectPeerStory(global, webPageStoryData.peerId, webPageStoryData.id)
|
? selectPeerStory(global, webPageStoryData.peerId, webPageStoryData.id)
|
||||||
@ -2031,8 +2036,8 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const sender = selectSender(global, message);
|
const sender = selectSender(global, message);
|
||||||
const originSender = selectForwardedSender(global, message);
|
const originSender = selectForwardedSender(global, message);
|
||||||
const botSender = viaBotId ? selectUser(global, viaBotId) : undefined;
|
const botSender = viaBotId ? selectUser(global, viaBotId) : undefined;
|
||||||
const senderAdminMember = sender?.id && isGroup
|
const senderChatMember = sender?.id
|
||||||
? chatFullInfo?.adminMembersById?.[sender?.id]
|
? (adminMembersById?.[sender?.id] || members?.find((member) => member.userId === sender?.id))
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const isThreadTop = message.id === threadId;
|
const isThreadTop = message.id === threadId;
|
||||||
@ -2119,7 +2124,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
|
|
||||||
const isPremium = selectIsCurrentUserPremium(global);
|
const isPremium = selectIsCurrentUserPremium(global);
|
||||||
const senderBoosts = sender && selectIsChatWithSelf(global, sender.id)
|
const senderBoosts = sender && selectIsChatWithSelf(global, sender.id)
|
||||||
? (chatFullInfo?.boostsApplied ?? message.senderBoosts) : message.senderBoosts;
|
? (boostsApplied ?? message.senderBoosts) : message.senderBoosts;
|
||||||
|
|
||||||
const chatLevel = chat?.boostLevel || 0;
|
const chatLevel = chat?.boostLevel || 0;
|
||||||
const transcribeMinLevel = global.appConfig.groupTranscribeLevelMin;
|
const transcribeMinLevel = global.appConfig.groupTranscribeLevelMin;
|
||||||
@ -2202,7 +2207,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
isTranscribing: transcriptionId !== undefined && global.transcriptions[transcriptionId]?.isPending,
|
isTranscribing: transcriptionId !== undefined && global.transcriptions[transcriptionId]?.isPending,
|
||||||
transcribedText: transcriptionId !== undefined ? global.transcriptions[transcriptionId]?.text : undefined,
|
transcribedText: transcriptionId !== undefined ? global.transcriptions[transcriptionId]?.text : undefined,
|
||||||
isPremium,
|
isPremium,
|
||||||
senderAdminMember,
|
senderChatMember,
|
||||||
messageTopic,
|
messageTopic,
|
||||||
hasTopicChip,
|
hasTopicChip,
|
||||||
chatTranslations,
|
chatTranslations,
|
||||||
|
|||||||
@ -462,8 +462,8 @@ const MessageContextMenu: FC<OwnProps> = ({
|
|||||||
<MenuSeparator size="thick" />
|
<MenuSeparator size="thick" />
|
||||||
{!customEmojiSets && (
|
{!customEmojiSets && (
|
||||||
<>
|
<>
|
||||||
<Skeleton inline className="menu-loading-row" />
|
<Skeleton inline className="menu-loading-row" animation="wave" />
|
||||||
<Skeleton inline className="menu-loading-row" />
|
<Skeleton inline className="menu-loading-row" animation="wave" />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{customEmojiSets && customEmojiSets.length === 1 && (
|
{customEmojiSets && customEmojiSets.length === 1 && (
|
||||||
|
|||||||
@ -36,7 +36,7 @@ function ReadTimeMenuItem({
|
|||||||
return (
|
return (
|
||||||
<MenuItem icon="message-read" className={styles.item}>
|
<MenuItem icon="message-read" className={styles.item}>
|
||||||
<Transition name="fade" activeKey={shouldRenderSkeleton ? 1 : 2} className={styles.transition}>
|
<Transition name="fade" activeKey={shouldRenderSkeleton ? 1 : 2} className={styles.transition}>
|
||||||
{shouldRenderSkeleton ? <Skeleton className={styles.skeleton} /> : (
|
{shouldRenderSkeleton ? <Skeleton className={styles.skeleton} animation="wave" /> : (
|
||||||
<>
|
<>
|
||||||
{Boolean(readDate) && lang('PmReadAt', formatDateAtTime(lang, readDate * 1000))}
|
{Boolean(readDate) && lang('PmReadAt', formatDateAtTime(lang, readDate * 1000))}
|
||||||
{!readDate && shouldRenderShowWhen && (
|
{!readDate && shouldRenderShowWhen && (
|
||||||
|
|||||||
@ -154,7 +154,7 @@ const SimilarChannels = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={buildClassName(styles.root)}>
|
<div className={buildClassName(styles.root)}>
|
||||||
{shouldRenderSkeleton && <Skeleton className={styles.skeleton} />}
|
{shouldRenderSkeleton && <Skeleton className={styles.skeleton} animation="wave" />}
|
||||||
{shouldRenderChannels && (
|
{shouldRenderChannels && (
|
||||||
<div
|
<div
|
||||||
className={buildClassName(
|
className={buildClassName(
|
||||||
|
|||||||
@ -303,7 +303,6 @@
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
unicode-bidi: plaintext;
|
unicode-bidi: plaintext;
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
font-size: calc(var(--message-text-size, 1rem) - 0.125rem);
|
font-size: calc(var(--message-text-size, 1rem) - 0.125rem);
|
||||||
@ -399,11 +398,12 @@
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-title {
|
.admin-title-badge, .admin-title {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
margin-left: 1rem;
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-title {
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
font-weight: var(--font-weight-normal);
|
font-weight: var(--font-weight-normal);
|
||||||
color: rgba(var(--color-text-meta-rgb), 0.75);
|
color: rgba(var(--color-text-meta-rgb), 0.75);
|
||||||
@ -414,6 +414,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.admin-title-badge {
|
||||||
|
margin-right: -0.1875rem;
|
||||||
|
}
|
||||||
|
|
||||||
.sender-boosts {
|
.sender-boosts {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
@ -540,23 +544,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.voice,
|
|
||||||
&.audio,
|
|
||||||
&.document {
|
|
||||||
.text-content {
|
|
||||||
margin-top: 0.25rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.voice,
|
|
||||||
&.audio {
|
|
||||||
.message-title,
|
|
||||||
.Embedded {
|
|
||||||
margin-top: -0.1875rem;
|
|
||||||
margin-bottom: 0.1875rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.audio {
|
&.audio {
|
||||||
min-width: 20rem;
|
min-width: 20rem;
|
||||||
|
|
||||||
|
|||||||
@ -58,6 +58,8 @@ import PriceConfirmModal from './priceConfirm/PriceConfirmModal.async';
|
|||||||
import ProfileRatingModal from './profileRating/ProfileRatingModal.async';
|
import ProfileRatingModal from './profileRating/ProfileRatingModal.async';
|
||||||
import QuickChatPickerModal from './quickChatPicker/QuickChatPickerModal.async';
|
import QuickChatPickerModal from './quickChatPicker/QuickChatPickerModal.async';
|
||||||
import QuickPreviewModal from './quickPreview/QuickPreviewModal.async';
|
import QuickPreviewModal from './quickPreview/QuickPreviewModal.async';
|
||||||
|
import EditRankModal from './rank/EditRankModal.async';
|
||||||
|
import RankModal from './rank/RankModal.async';
|
||||||
import ReportAdModal from './reportAd/ReportAdModal.async';
|
import ReportAdModal from './reportAd/ReportAdModal.async';
|
||||||
import ReportModal from './reportModal/ReportModal.async';
|
import ReportModal from './reportModal/ReportModal.async';
|
||||||
import SharePreparedMessageModal from './sharePreparedMessage/SharePreparedMessageModal.async';
|
import SharePreparedMessageModal from './sharePreparedMessage/SharePreparedMessageModal.async';
|
||||||
@ -144,7 +146,9 @@ type ModalKey = keyof Pick<TabState,
|
|||||||
'leaveGroupModal' |
|
'leaveGroupModal' |
|
||||||
'isTwoFaCheckModalOpen' |
|
'isTwoFaCheckModalOpen' |
|
||||||
'isQuickChatPickerOpen' |
|
'isQuickChatPickerOpen' |
|
||||||
'isCocoonModalOpen'
|
'isCocoonModalOpen' |
|
||||||
|
'editRankModal' |
|
||||||
|
'rankModal'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -228,6 +232,8 @@ const MODALS: ModalRegistry = {
|
|||||||
isTwoFaCheckModalOpen: TwoFaCheckModal,
|
isTwoFaCheckModalOpen: TwoFaCheckModal,
|
||||||
isQuickChatPickerOpen: QuickChatPickerModal,
|
isQuickChatPickerOpen: QuickChatPickerModal,
|
||||||
isCocoonModalOpen: CocoonModal,
|
isCocoonModalOpen: CocoonModal,
|
||||||
|
editRankModal: EditRankModal,
|
||||||
|
rankModal: RankModal,
|
||||||
};
|
};
|
||||||
const MODAL_KEYS = Object.keys(MODALS) as ModalKey[];
|
const MODAL_KEYS = Object.keys(MODALS) as ModalKey[];
|
||||||
const MODAL_ENTRIES = Object.entries(MODALS) as Entries<ModalRegistry>;
|
const MODAL_ENTRIES = Object.entries(MODALS) as Entries<ModalRegistry>;
|
||||||
|
|||||||
14
src/components/modals/rank/EditRankModal.async.tsx
Normal file
14
src/components/modals/rank/EditRankModal.async.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import type { OwnProps } from './EditRankModal';
|
||||||
|
|
||||||
|
import { Bundles } from '../../../util/moduleLoader';
|
||||||
|
|
||||||
|
import useModuleLoader from '../../../hooks/useModuleLoader';
|
||||||
|
|
||||||
|
const EditRankModalAsync = (props: OwnProps) => {
|
||||||
|
const { modal } = props;
|
||||||
|
const EditRankModal = useModuleLoader(Bundles.Extra, 'EditRankModal', !modal);
|
||||||
|
|
||||||
|
return EditRankModal ? <EditRankModal {...props} /> : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditRankModalAsync;
|
||||||
31
src/components/modals/rank/EditRankModal.module.scss
Normal file
31
src/components/modals/rank/EditRankModal.module.scss
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.previewMessage {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.previewBlock {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.previewRankBadge {
|
||||||
|
max-width: 10rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.previewLine {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
177
src/components/modals/rank/EditRankModal.tsx
Normal file
177
src/components/modals/rank/EditRankModal.tsx
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
import { memo, useEffect, useState } from '../../../lib/teact/teact';
|
||||||
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
|
import type { ApiChat, ApiUser } from '../../../api/types';
|
||||||
|
import type { TabState } from '../../../global/types';
|
||||||
|
|
||||||
|
import { getUserFirstOrLastName } from '../../../global/helpers';
|
||||||
|
import { getPeerTitle } from '../../../global/helpers/peers';
|
||||||
|
import { selectCanEditRank, selectChat, selectUser } from '../../../global/selectors';
|
||||||
|
|
||||||
|
import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
|
import Avatar from '../../common/Avatar';
|
||||||
|
import PreviewBlock from '../../common/PreviewBlock';
|
||||||
|
import RankBadge from '../../common/RankBadge';
|
||||||
|
import Button from '../../ui/Button';
|
||||||
|
import InputText from '../../ui/InputText';
|
||||||
|
import Modal from '../../ui/Modal';
|
||||||
|
import Skeleton from '../../ui/placeholder/Skeleton';
|
||||||
|
|
||||||
|
import styles from './EditRankModal.module.scss';
|
||||||
|
|
||||||
|
export type OwnProps = {
|
||||||
|
modal: TabState['editRankModal'];
|
||||||
|
};
|
||||||
|
|
||||||
|
type StateProps = {
|
||||||
|
user?: ApiUser;
|
||||||
|
chat?: ApiChat;
|
||||||
|
canEditRank?: boolean;
|
||||||
|
isOwn?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const MAX_RANK_LENGTH = 16;
|
||||||
|
const PREVIEW_TIME = '9:37';
|
||||||
|
|
||||||
|
const EditRankModal = ({
|
||||||
|
modal, user, chat, canEditRank, isOwn,
|
||||||
|
}: OwnProps & StateProps) => {
|
||||||
|
const { closeEditRankModal, editChatParticipantRank } = getActions();
|
||||||
|
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
const isOpen = Boolean(modal);
|
||||||
|
const renderingModal = useCurrentOrPrev(modal);
|
||||||
|
const [rank, setRank] = useState('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isOpen) return;
|
||||||
|
setRank((renderingModal?.rank || '').slice(0, MAX_RANK_LENGTH));
|
||||||
|
}, [renderingModal?.chatId, renderingModal?.rank, renderingModal?.userId, isOpen]);
|
||||||
|
|
||||||
|
const handleClose = useLastCallback(() => {
|
||||||
|
closeEditRankModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleInputChange = useLastCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setRank(e.target.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
userId, chatId, isAdmin, isOwner,
|
||||||
|
} = renderingModal || {};
|
||||||
|
const initialRank = (renderingModal?.rank || '').slice(0, MAX_RANK_LENGTH);
|
||||||
|
const shouldShowBadge = Boolean(rank || isAdmin || isOwner);
|
||||||
|
const isSubmitDisabled = !canEditRank || rank === initialRank;
|
||||||
|
|
||||||
|
const handleSubmit = useLastCallback(() => {
|
||||||
|
if (isSubmitDisabled) return;
|
||||||
|
|
||||||
|
if (!chatId || !userId) return;
|
||||||
|
|
||||||
|
editChatParticipantRank({
|
||||||
|
chatId,
|
||||||
|
userId,
|
||||||
|
rank,
|
||||||
|
});
|
||||||
|
|
||||||
|
handleClose();
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleKeyDown = useLastCallback((e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (e.key !== 'Enter') return;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
handleSubmit();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!chatId || !userId) return undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
title={lang(isOwn ? 'RankModalEditMy' : 'RankModalEdit')}
|
||||||
|
contentClassName={styles.content}
|
||||||
|
isSlim
|
||||||
|
hasCloseButton
|
||||||
|
onClose={handleClose}
|
||||||
|
>
|
||||||
|
<PreviewBlock className={styles.previewBlock} contentClassName={styles.previewBlockContent}>
|
||||||
|
<PreviewBlock.Message
|
||||||
|
className={styles.previewMessage}
|
||||||
|
avatar={user ? <Avatar peer={user} size="small" /> : undefined}
|
||||||
|
sender={user && getPeerTitle(lang, user)}
|
||||||
|
badge={shouldShowBadge ? (
|
||||||
|
<RankBadge
|
||||||
|
chatId={chatId}
|
||||||
|
userId={userId}
|
||||||
|
className={styles.previewRankBadge}
|
||||||
|
isAdmin={isAdmin}
|
||||||
|
isOwner={isOwner}
|
||||||
|
rank={rank || undefined}
|
||||||
|
/>
|
||||||
|
) : undefined}
|
||||||
|
time={PREVIEW_TIME}
|
||||||
|
>
|
||||||
|
{renderPreviewContent()}
|
||||||
|
</PreviewBlock.Message>
|
||||||
|
</PreviewBlock>
|
||||||
|
|
||||||
|
<InputText
|
||||||
|
id="edit-rank"
|
||||||
|
label={lang('EditAdminRank')}
|
||||||
|
className={styles.input}
|
||||||
|
value={rank}
|
||||||
|
maxLength={MAX_RANK_LENGTH}
|
||||||
|
disabled={!chat || !user || !canEditRank}
|
||||||
|
teactExperimentControlled
|
||||||
|
onChange={handleInputChange}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
/>
|
||||||
|
<p className={styles.description}>
|
||||||
|
{isOwn ? lang('RankEditTextOwn') : lang('RankEditText', {
|
||||||
|
user: user && getUserFirstOrLastName(user),
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
disabled={isSubmitDisabled}
|
||||||
|
onClick={handleSubmit}
|
||||||
|
>
|
||||||
|
{lang('RankEditSave')}
|
||||||
|
</Button>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
function renderPreviewContent() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span className={styles.previewLine}>
|
||||||
|
<Skeleton variant="rounded-rect" height={10} inline />
|
||||||
|
</span>
|
||||||
|
<span className={styles.previewLine}>
|
||||||
|
<Skeleton variant="rounded-rect" width={128} height={10} inline />
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(withGlobal<OwnProps>(
|
||||||
|
(global, { modal }): StateProps => {
|
||||||
|
return {
|
||||||
|
user: modal?.userId ? selectUser(global, modal.userId) : undefined,
|
||||||
|
chat: modal?.chatId ? selectChat(global, modal.chatId) : undefined,
|
||||||
|
canEditRank: modal && selectCanEditRank(global, {
|
||||||
|
chatId: modal.chatId,
|
||||||
|
userId: modal.userId,
|
||||||
|
isAdmin: modal.isAdmin,
|
||||||
|
isOwner: modal.isOwner,
|
||||||
|
}),
|
||||||
|
isOwn: modal && modal.userId === global.currentUserId,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
)(EditRankModal));
|
||||||
14
src/components/modals/rank/RankModal.async.tsx
Normal file
14
src/components/modals/rank/RankModal.async.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import type { OwnProps } from './RankModal';
|
||||||
|
|
||||||
|
import { Bundles } from '../../../util/moduleLoader';
|
||||||
|
|
||||||
|
import useModuleLoader from '../../../hooks/useModuleLoader';
|
||||||
|
|
||||||
|
const RankModalAsync = (props: OwnProps) => {
|
||||||
|
const { modal } = props;
|
||||||
|
const RankModal = useModuleLoader(Bundles.Extra, 'RankModal', !modal);
|
||||||
|
|
||||||
|
return RankModal ? <RankModal {...props} /> : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RankModalAsync;
|
||||||
102
src/components/modals/rank/RankModal.module.scss
Normal file
102
src/components/modals/rank/RankModal.module.scss
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
@use "../../../styles/mixins";
|
||||||
|
|
||||||
|
.root {
|
||||||
|
:global(.modal-dialog) {
|
||||||
|
overflow: hidden;
|
||||||
|
width: 26.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0 1rem 1rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
padding-top: 2rem;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.previewGrid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.previewBlockContent {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.previewMessage {
|
||||||
|
--preview-crop-left: 1.5rem;
|
||||||
|
--preview-crop-fade-width: 1.5rem;
|
||||||
|
|
||||||
|
@include mixins.gradient-border-left(
|
||||||
|
calc(var(--preview-crop-left) + var(--preview-crop-fade-width)),
|
||||||
|
0.75rem
|
||||||
|
);
|
||||||
|
|
||||||
|
width: calc(100% + var(--preview-crop-left));
|
||||||
|
max-width: none;
|
||||||
|
margin-top: auto;
|
||||||
|
margin-left: calc(var(--preview-crop-left) * -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.previewLine,
|
||||||
|
.previewMetaLine {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.previewMetaLine {
|
||||||
|
gap: 0.375rem;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.memberTag {
|
||||||
|
padding: 0.25rem 0.625rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
|
||||||
|
font-size: 0.75rem;
|
||||||
|
line-height: 1;
|
||||||
|
color: rgba(var(--color-text-meta-rgb), 0.8);
|
||||||
|
|
||||||
|
background: rgb(var(--color-text-meta-rgb) / 12%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.previewRankBadge {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
align-self: stretch;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.understoodIcon {
|
||||||
|
font-size: 1.1875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topIcon {
|
||||||
|
display: grid;
|
||||||
|
flex-shrink: 0;
|
||||||
|
place-items: center;
|
||||||
|
|
||||||
|
width: 6rem;
|
||||||
|
height: 6rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
font-size: 4rem;
|
||||||
|
color: var(--color-white);
|
||||||
|
|
||||||
|
background: var(--color-primary);
|
||||||
|
}
|
||||||
190
src/components/modals/rank/RankModal.tsx
Normal file
190
src/components/modals/rank/RankModal.tsx
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
import { memo, useMemo } from '@teact';
|
||||||
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
|
import type { ApiChat, ApiChatMember, ApiUser } from '../../../api/types';
|
||||||
|
import type { TabState } from '../../../global/types';
|
||||||
|
|
||||||
|
import { getPeerTitle } from '../../../global/helpers/peers';
|
||||||
|
import {
|
||||||
|
selectCanEditOwnRank, selectCanEditRank, selectChat, selectChatFullInfo, selectUser,
|
||||||
|
} from '../../../global/selectors';
|
||||||
|
|
||||||
|
import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
|
import Icon from '../../common/icons/Icon';
|
||||||
|
import PreviewBlock from '../../common/PreviewBlock';
|
||||||
|
import RankBadge from '../../common/RankBadge';
|
||||||
|
import Button from '../../ui/Button';
|
||||||
|
import Modal from '../../ui/Modal';
|
||||||
|
import Skeleton from '../../ui/placeholder/Skeleton';
|
||||||
|
|
||||||
|
import styles from './RankModal.module.scss';
|
||||||
|
|
||||||
|
export type OwnProps = {
|
||||||
|
modal: TabState['rankModal'];
|
||||||
|
};
|
||||||
|
|
||||||
|
type StateProps = {
|
||||||
|
user?: ApiUser;
|
||||||
|
chat?: ApiChat;
|
||||||
|
canEditRank?: boolean;
|
||||||
|
canEditOwnRank?: boolean;
|
||||||
|
currentUserMember?: ApiChatMember;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PLACEHOLDER_ID = 'placeholder';
|
||||||
|
|
||||||
|
const RankModal = ({
|
||||||
|
modal, user, chat, canEditRank, canEditOwnRank, currentUserMember,
|
||||||
|
}: OwnProps & StateProps) => {
|
||||||
|
const { closeRankModal, openEditRankModal } = getActions();
|
||||||
|
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
const isOpen = Boolean(modal);
|
||||||
|
const renderingModal = useCurrentOrPrev(modal);
|
||||||
|
const isOwn = renderingModal?.userId === currentUserMember?.userId;
|
||||||
|
|
||||||
|
const handleClose = useLastCallback(() => {
|
||||||
|
closeRankModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleActionClick = useLastCallback(() => {
|
||||||
|
if (!renderingModal) return;
|
||||||
|
if (canEditRank) {
|
||||||
|
openEditRankModal(renderingModal);
|
||||||
|
} else if (canEditOwnRank && currentUserMember) {
|
||||||
|
openEditRankModal({
|
||||||
|
chatId: renderingModal.chatId,
|
||||||
|
userId: currentUserMember.userId,
|
||||||
|
isAdmin: currentUserMember.isAdmin,
|
||||||
|
isOwner: currentUserMember.isOwner,
|
||||||
|
rank: currentUserMember.rank,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
closeRankModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
const mainButtonText = useMemo(() => {
|
||||||
|
if (canEditRank) return isOwn ? lang('RankModalEditMy') : lang('RankModalEdit');
|
||||||
|
if (canEditOwnRank) return lang('RankModalEditMy');
|
||||||
|
return lang('ButtonUnderstood');
|
||||||
|
}, [canEditRank, canEditOwnRank, isOwn, lang]);
|
||||||
|
|
||||||
|
if (!renderingModal) return undefined;
|
||||||
|
|
||||||
|
const { userId, chatId, isAdmin, isOwner, rank } = renderingModal;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
className={styles.root}
|
||||||
|
contentClassName={styles.content}
|
||||||
|
hasAbsoluteCloseButton
|
||||||
|
onClose={handleClose}
|
||||||
|
>
|
||||||
|
<div className={styles.body}>
|
||||||
|
<div className={styles.topIcon}>
|
||||||
|
<Icon name="user-tag" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className={styles.previewTitle}>
|
||||||
|
{lang(
|
||||||
|
isOwner ? 'RankModalOwnerTagTitle' : isAdmin ? 'RankModalAdminTagTitle' : 'RankModalMemberTagTitle',
|
||||||
|
)}
|
||||||
|
</h3>
|
||||||
|
<div className={styles.previewText}>
|
||||||
|
{lang(
|
||||||
|
isOwner ? 'RankModalOwnerText' : isAdmin ? 'RankModalAdminText' : 'RankModalMemberText',
|
||||||
|
{
|
||||||
|
tag: <RankBadge chatId={chatId} userId={userId} isAdmin={isAdmin} isOwner={isOwner} rank={rank} />,
|
||||||
|
author: user && getPeerTitle(lang, user),
|
||||||
|
group: chat && getPeerTitle(lang, chat),
|
||||||
|
},
|
||||||
|
{ withNodes: true, withMarkdown: true },
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.previewGrid}>
|
||||||
|
<PreviewBlock contentClassName={styles.previewBlockContent}>
|
||||||
|
<PreviewBlock.Message
|
||||||
|
className={styles.previewMessage}
|
||||||
|
badge={(
|
||||||
|
<RankBadge
|
||||||
|
chatId={PLACEHOLDER_ID}
|
||||||
|
userId={PLACEHOLDER_ID}
|
||||||
|
className={styles.previewRankBadge}
|
||||||
|
rank={lang('RankMemberTag')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{renderPreviewContent()}
|
||||||
|
</PreviewBlock.Message>
|
||||||
|
</PreviewBlock>
|
||||||
|
<PreviewBlock contentClassName={styles.previewBlockContent}>
|
||||||
|
<PreviewBlock.Message
|
||||||
|
className={styles.previewMessage}
|
||||||
|
badge={(
|
||||||
|
<RankBadge
|
||||||
|
className={styles.previewRankBadge}
|
||||||
|
chatId={PLACEHOLDER_ID}
|
||||||
|
userId={PLACEHOLDER_ID}
|
||||||
|
isAdmin
|
||||||
|
rank={lang('RankAdminTag')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{renderPreviewContent()}
|
||||||
|
</PreviewBlock.Message>
|
||||||
|
</PreviewBlock>
|
||||||
|
</div>
|
||||||
|
<div className={styles.footer}>
|
||||||
|
<Button
|
||||||
|
iconName={!canEditRank ? 'understood' : undefined}
|
||||||
|
iconClassName={styles.understoodIcon}
|
||||||
|
onClick={handleActionClick}
|
||||||
|
>
|
||||||
|
{mainButtonText}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
function renderPreviewContent() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span className={styles.previewLine}>
|
||||||
|
<Skeleton variant="rounded-rect" width={168} height={10} inline />
|
||||||
|
</span>
|
||||||
|
<span className={styles.previewMetaLine}>
|
||||||
|
<Skeleton variant="rounded-rect" width={104} height={10} inline />
|
||||||
|
<PreviewBlock.Message.Time>9:37</PreviewBlock.Message.Time>
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(withGlobal<OwnProps>(
|
||||||
|
(global, { modal }): StateProps => {
|
||||||
|
const chatFullInfo = modal?.chatId ? selectChatFullInfo(global, modal.chatId) : undefined;
|
||||||
|
const currentUserMember = chatFullInfo?.adminMembersById?.[global.currentUserId!]
|
||||||
|
|| chatFullInfo?.members?.find((member) => member.userId === global.currentUserId);
|
||||||
|
return {
|
||||||
|
user: modal?.userId ? selectUser(global, modal.userId) : undefined,
|
||||||
|
chat: modal?.chatId ? selectChat(global, modal.chatId) : undefined,
|
||||||
|
canEditRank: modal && selectCanEditRank(global, {
|
||||||
|
chatId: modal.chatId,
|
||||||
|
userId: modal.userId,
|
||||||
|
isAdmin: modal.isAdmin,
|
||||||
|
isOwner: modal.isOwner,
|
||||||
|
}),
|
||||||
|
canEditOwnRank: modal && selectCanEditOwnRank(global, modal.chatId),
|
||||||
|
currentUserMember,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
)(RankModal));
|
||||||
@ -188,6 +188,7 @@ const Checkout: FC<OwnProps> = ({
|
|||||||
width={photo.dimensions?.width}
|
width={photo.dimensions?.width}
|
||||||
height={photo.dimensions?.height}
|
height={photo.dimensions?.height}
|
||||||
className={styles.checkoutPicture}
|
className={styles.checkoutPicture}
|
||||||
|
animation="pulse"
|
||||||
forceAspectRatio
|
forceAspectRatio
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -41,6 +41,7 @@ import {
|
|||||||
import { getSavedGiftKey } from '../../global/helpers/stars';
|
import { getSavedGiftKey } from '../../global/helpers/stars';
|
||||||
import {
|
import {
|
||||||
selectActiveDownloads,
|
selectActiveDownloads,
|
||||||
|
selectCanEditRank,
|
||||||
selectCanUpdateMainTab,
|
selectCanUpdateMainTab,
|
||||||
selectChat,
|
selectChat,
|
||||||
selectChatFullInfo,
|
selectChatFullInfo,
|
||||||
@ -74,6 +75,7 @@ import { IS_TOUCH_ENV } from '../../util/browser/windowEnvironment';
|
|||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import { captureEvents, SwipeDirection } from '../../util/captureEvents';
|
import { captureEvents, SwipeDirection } from '../../util/captureEvents';
|
||||||
import { isUserId } from '../../util/entities/ids';
|
import { isUserId } from '../../util/entities/ids';
|
||||||
|
import { buildCollectionByKey } from '../../util/iteratees.ts';
|
||||||
import { resolveTransitionName } from '../../util/resolveTransitionName.ts';
|
import { resolveTransitionName } from '../../util/resolveTransitionName.ts';
|
||||||
import { LOCAL_TGS_URLS } from '../common/helpers/animatedAssets';
|
import { LOCAL_TGS_URLS } from '../common/helpers/animatedAssets';
|
||||||
import renderText from '../common/helpers/renderText';
|
import renderText from '../common/helpers/renderText';
|
||||||
@ -301,6 +303,7 @@ const Profile = ({
|
|||||||
resetSelectedStoryAlbum,
|
resetSelectedStoryAlbum,
|
||||||
changeProfileTab,
|
changeProfileTab,
|
||||||
setMainProfileTab,
|
setMainProfileTab,
|
||||||
|
openEditRankModal,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const containerRef = useRef<HTMLDivElement>();
|
const containerRef = useRef<HTMLDivElement>();
|
||||||
@ -481,6 +484,10 @@ const Profile = ({
|
|||||||
const { startViewTransition } = useViewTransition();
|
const { startViewTransition } = useViewTransition();
|
||||||
const { createVtnStyle } = useVtn();
|
const { createVtnStyle } = useVtn();
|
||||||
|
|
||||||
|
const membersById = useMemo(() => {
|
||||||
|
return members && buildCollectionByKey(members, 'userId');
|
||||||
|
}, [members]);
|
||||||
|
|
||||||
const giftIds = useMemo(() => renderingGifts?.map((gift) => getSavedGiftKey(gift)), [renderingGifts]);
|
const giftIds = useMemo(() => renderingGifts?.map((gift) => getSavedGiftKey(gift)), [renderingGifts]);
|
||||||
|
|
||||||
const activeTabIndex = useMemo(() => {
|
const activeTabIndex = useMemo(() => {
|
||||||
@ -724,13 +731,48 @@ const Profile = ({
|
|||||||
activeTabIndex, activeCollectionId, selectedStoryAlbumId], renderingDelay);
|
activeTabIndex, activeCollectionId, selectedStoryAlbumId], renderingDelay);
|
||||||
|
|
||||||
function getMemberContextAction(memberId: string): MenuItemContextAction[] | undefined {
|
function getMemberContextAction(memberId: string): MenuItemContextAction[] | undefined {
|
||||||
return memberId === currentUserId || !canDeleteMembers ? undefined : [{
|
const global = getGlobal();
|
||||||
title: oldLang('lng_context_remove_from_group'),
|
const member = adminMembersById?.[memberId] || membersById?.[memberId];
|
||||||
icon: 'stop',
|
const canEditRank = member && selectCanEditRank(global, {
|
||||||
handler: () => {
|
chatId,
|
||||||
setDeletingUserId(memberId);
|
userId: memberId,
|
||||||
},
|
isAdmin: member?.isAdmin,
|
||||||
}];
|
isOwner: member?.isOwner,
|
||||||
|
});
|
||||||
|
|
||||||
|
const actions: MenuItemContextAction[] = [];
|
||||||
|
|
||||||
|
if (memberId !== currentUserId && canDeleteMembers) {
|
||||||
|
actions.push({
|
||||||
|
title: oldLang('lng_context_remove_from_group'),
|
||||||
|
icon: 'stop',
|
||||||
|
handler: () => {
|
||||||
|
setDeletingUserId(memberId);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canEditRank) {
|
||||||
|
actions.push({
|
||||||
|
title: lang('MemberContextEditRank'),
|
||||||
|
icon: 'tag',
|
||||||
|
handler: () => {
|
||||||
|
openEditRankModal({
|
||||||
|
chatId,
|
||||||
|
userId: memberId,
|
||||||
|
isAdmin: member?.isAdmin,
|
||||||
|
isOwner: member?.isOwner,
|
||||||
|
rank: member?.rank,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actions.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderNothingFoundGiftsWithFilter() {
|
function renderNothingFoundGiftsWithFilter() {
|
||||||
@ -978,8 +1020,15 @@ const Profile = ({
|
|||||||
|
|
||||||
onClick={() => handleMemberClick(id)}
|
onClick={() => handleMemberClick(id)}
|
||||||
contextActions={getMemberContextAction(id)}
|
contextActions={getMemberContextAction(id)}
|
||||||
|
withPortalForMenu
|
||||||
>
|
>
|
||||||
<PrivateChatInfo userId={id} adminMember={adminMembersById?.[id]} forceShowSelf withStory />
|
<PrivateChatInfo
|
||||||
|
userId={id}
|
||||||
|
chatMemberOriginId={chatId}
|
||||||
|
chatMember={adminMembersById?.[id] || membersById?.[id]}
|
||||||
|
forceShowSelf
|
||||||
|
withStory
|
||||||
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
))
|
))
|
||||||
) : resultType === 'commonChats' ? (
|
) : resultType === 'commonChats' ? (
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
memo, useCallback, useEffect, useMemo, useState,
|
memo, useEffect, useMemo, useState,
|
||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
import { getActions, getGlobal, withGlobal } from '../../../global';
|
import { getActions, getGlobal, withGlobal } from '../../../global';
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ import type {
|
|||||||
import { ManagementScreens } from '../../../types';
|
import { ManagementScreens } from '../../../types';
|
||||||
|
|
||||||
import { getUserFullName, isChatBasicGroup, isChatChannel, isUserBot } from '../../../global/helpers';
|
import { getUserFullName, isChatBasicGroup, isChatChannel, isUserBot } from '../../../global/helpers';
|
||||||
import { selectChat, selectChatFullInfo } from '../../../global/selectors';
|
import { selectCanEditRank, selectChat, selectChatFullInfo } from '../../../global/selectors';
|
||||||
|
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
@ -37,11 +37,12 @@ type OwnProps = {
|
|||||||
type StateProps = {
|
type StateProps = {
|
||||||
chat: ApiChat;
|
chat: ApiChat;
|
||||||
usersById: Record<string, ApiUser>;
|
usersById: Record<string, ApiUser>;
|
||||||
adminMembersById?: Record<string, ApiChatMember>;
|
selectedAdminMember?: ApiChatMember;
|
||||||
hasFullInfo: boolean;
|
hasFullInfo: boolean;
|
||||||
currentUserId?: string;
|
currentUserId?: string;
|
||||||
isFormFullyDisabled: boolean;
|
isFormFullyDisabled: boolean;
|
||||||
defaultRights?: ApiChatAdminRights;
|
defaultRights?: ApiChatAdminRights;
|
||||||
|
canEditRank?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CUSTOM_TITLE_MAX_LENGTH = 16;
|
const CUSTOM_TITLE_MAX_LENGTH = 16;
|
||||||
@ -54,9 +55,10 @@ const ManageGroupAdminRights = ({
|
|||||||
chat,
|
chat,
|
||||||
usersById,
|
usersById,
|
||||||
currentUserId,
|
currentUserId,
|
||||||
adminMembersById,
|
selectedAdminMember,
|
||||||
hasFullInfo,
|
hasFullInfo,
|
||||||
isFormFullyDisabled,
|
isFormFullyDisabled,
|
||||||
|
canEditRank,
|
||||||
onClose,
|
onClose,
|
||||||
onScreenSelect,
|
onScreenSelect,
|
||||||
}: OwnProps & StateProps) => {
|
}: OwnProps & StateProps) => {
|
||||||
@ -71,7 +73,7 @@ const ManageGroupAdminRights = ({
|
|||||||
const [isDismissConfirmationDialogOpen, openDismissConfirmationDialog, closeDismissConfirmationDialog] = useFlag();
|
const [isDismissConfirmationDialogOpen, openDismissConfirmationDialog, closeDismissConfirmationDialog] = useFlag();
|
||||||
const [isTransferDialogOpen, openTransferDialog, closeTransferDialog] = useFlag();
|
const [isTransferDialogOpen, openTransferDialog, closeTransferDialog] = useFlag();
|
||||||
const [isPasswordModalOpen, openPasswordModal, closePasswordModal] = useFlag();
|
const [isPasswordModalOpen, openPasswordModal, closePasswordModal] = useFlag();
|
||||||
const [customTitle, setCustomTitle] = useState('');
|
const [rank, setRank] = useState('');
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
const isChannel = isChatChannel(chat);
|
const isChannel = isChatChannel(chat);
|
||||||
@ -83,9 +85,7 @@ const ManageGroupAdminRights = ({
|
|||||||
onBack: onClose,
|
onBack: onClose,
|
||||||
});
|
});
|
||||||
|
|
||||||
const selectedChatMember = useMemo(() => {
|
const selectedChatMember: ApiChatMember | undefined = useMemo(() => {
|
||||||
const selectedAdminMember = selectedUserId ? adminMembersById?.[selectedUserId] : undefined;
|
|
||||||
|
|
||||||
// If `selectedAdminMember` variable is filled with a value, then we have already saved the administrator,
|
// If `selectedAdminMember` variable is filled with a value, then we have already saved the administrator,
|
||||||
// so now we need to return to the list of administrators
|
// so now we need to return to the list of administrators
|
||||||
if (isNewAdmin && (selectedAdminMember || !selectedUserId)) {
|
if (isNewAdmin && (selectedAdminMember || !selectedUserId)) {
|
||||||
@ -98,14 +98,14 @@ const ManageGroupAdminRights = ({
|
|||||||
return user ? {
|
return user ? {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
adminRights: defaultRights,
|
adminRights: defaultRights,
|
||||||
customTitle: lang('ChannelAdmin'),
|
rank: lang('ChannelAdmin'),
|
||||||
isOwner: false,
|
isOwner: undefined,
|
||||||
promotedByUserId: undefined,
|
promotedByUserId: undefined,
|
||||||
} : undefined;
|
} : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return selectedAdminMember;
|
return selectedAdminMember;
|
||||||
}, [adminMembersById, defaultRights, isNewAdmin, lang, selectedUserId]);
|
}, [selectedAdminMember, defaultRights, isNewAdmin, lang, selectedUserId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (hasFullInfo && selectedUserId && !selectedChatMember) {
|
if (hasFullInfo && selectedUserId && !selectedChatMember) {
|
||||||
@ -115,12 +115,12 @@ const ManageGroupAdminRights = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setPermissions(selectedChatMember?.adminRights || {});
|
setPermissions(selectedChatMember?.adminRights || {});
|
||||||
setCustomTitle((selectedChatMember?.customTitle || '').substr(0, CUSTOM_TITLE_MAX_LENGTH));
|
setRank((selectedChatMember?.rank || '').slice(0, CUSTOM_TITLE_MAX_LENGTH));
|
||||||
setIsTouched(Boolean(isNewAdmin));
|
setIsTouched(Boolean(isNewAdmin));
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}, [defaultRights, isNewAdmin, selectedChatMember]);
|
}, [defaultRights, isNewAdmin, selectedChatMember]);
|
||||||
|
|
||||||
const handlePermissionChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
const handlePermissionChange = useLastCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const { name } = e.target;
|
const { name } = e.target;
|
||||||
|
|
||||||
function getUpdatedPermissionValue(value: true | undefined) {
|
function getUpdatedPermissionValue(value: true | undefined) {
|
||||||
@ -132,23 +132,24 @@ const ManageGroupAdminRights = ({
|
|||||||
[name]: getUpdatedPermissionValue(p[name as keyof ApiChatAdminRights]),
|
[name]: getUpdatedPermissionValue(p[name as keyof ApiChatAdminRights]),
|
||||||
}));
|
}));
|
||||||
setIsTouched(true);
|
setIsTouched(true);
|
||||||
}, []);
|
});
|
||||||
|
|
||||||
const handleSavePermissions = useCallback(() => {
|
const handleSavePermissions = useLastCallback(() => {
|
||||||
if (!selectedUserId) {
|
if (!selectedUserId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const hasRankChanged = rank !== selectedAdminMember?.rank;
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
updateChatAdmin({
|
updateChatAdmin({
|
||||||
chatId: chat.id,
|
chatId: chat.id,
|
||||||
userId: selectedUserId,
|
userId: selectedUserId,
|
||||||
adminRights: permissions,
|
adminRights: permissions,
|
||||||
customTitle,
|
rank: hasRankChanged ? rank : undefined,
|
||||||
});
|
});
|
||||||
}, [selectedUserId, updateChatAdmin, chat.id, permissions, customTitle]);
|
});
|
||||||
|
|
||||||
const handleDismissAdmin = useCallback(() => {
|
const handleDismissAdmin = useLastCallback(() => {
|
||||||
if (!selectedUserId) {
|
if (!selectedUserId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -159,9 +160,9 @@ const ManageGroupAdminRights = ({
|
|||||||
adminRights: {},
|
adminRights: {},
|
||||||
});
|
});
|
||||||
closeDismissConfirmationDialog();
|
closeDismissConfirmationDialog();
|
||||||
}, [chat.id, closeDismissConfirmationDialog, selectedUserId, updateChatAdmin]);
|
});
|
||||||
|
|
||||||
const getControlIsDisabled = useCallback((key: keyof ApiChatAdminRights) => {
|
const getControlIsDisabled = useLastCallback((key: keyof ApiChatAdminRights) => {
|
||||||
if (isChatBasicGroup(chat)) {
|
if (isChatBasicGroup(chat)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -175,7 +176,7 @@ const ManageGroupAdminRights = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return !chat.adminRights[key];
|
return !chat.adminRights[key];
|
||||||
}, [chat, isFormFullyDisabled]);
|
});
|
||||||
|
|
||||||
const memberStatus = useMemo(() => {
|
const memberStatus = useMemo(() => {
|
||||||
if (isNewAdmin || !selectedChatMember) {
|
if (isNewAdmin || !selectedChatMember) {
|
||||||
@ -197,11 +198,11 @@ const ManageGroupAdminRights = ({
|
|||||||
return lang('ChannelAdmin');
|
return lang('ChannelAdmin');
|
||||||
}, [isNewAdmin, selectedChatMember, usersById, lang]);
|
}, [isNewAdmin, selectedChatMember, usersById, lang]);
|
||||||
|
|
||||||
const handleCustomTitleChange = useCallback((e) => {
|
const handleRankChange = useLastCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const { value } = e.target;
|
const { value } = e.target;
|
||||||
setCustomTitle(value);
|
setRank(value);
|
||||||
setIsTouched(true);
|
setIsTouched(true);
|
||||||
}, []);
|
});
|
||||||
|
|
||||||
const handleStartTransfer = useLastCallback(() => {
|
const handleStartTransfer = useLastCallback(() => {
|
||||||
if (!selectedUserId) return;
|
if (!selectedUserId) return;
|
||||||
@ -373,6 +374,16 @@ const ManageGroupAdminRights = ({
|
|||||||
onChange={handlePermissionChange}
|
onChange={handlePermissionChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="ListItem">
|
||||||
|
<Checkbox
|
||||||
|
name="editRank"
|
||||||
|
checked={Boolean(permissions.manageRanks)}
|
||||||
|
label={lang('EditAdminEditRank')}
|
||||||
|
blocking
|
||||||
|
disabled={getControlIsDisabled('manageRanks')}
|
||||||
|
onChange={handlePermissionChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
{!isChannel && (
|
{!isChannel && (
|
||||||
<div className="ListItem">
|
<div className="ListItem">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@ -441,9 +452,9 @@ const ManageGroupAdminRights = ({
|
|||||||
id="admin-title"
|
id="admin-title"
|
||||||
label={lang('EditAdminRank')}
|
label={lang('EditAdminRank')}
|
||||||
className="input-admin-title"
|
className="input-admin-title"
|
||||||
onChange={handleCustomTitleChange}
|
onChange={handleRankChange}
|
||||||
value={customTitle}
|
value={rank}
|
||||||
disabled={isFormFullyDisabled}
|
disabled={isFormFullyDisabled || !canEditRank}
|
||||||
maxLength={CUSTOM_TITLE_MAX_LENGTH}
|
maxLength={CUSTOM_TITLE_MAX_LENGTH}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -503,12 +514,21 @@ const ManageGroupAdminRights = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default memo(withGlobal<OwnProps>(
|
export default memo(withGlobal<OwnProps>(
|
||||||
(global, { chatId, isPromotedByCurrentUser }): Complete<StateProps> => {
|
(global, { chatId, isPromotedByCurrentUser, selectedUserId }): Complete<StateProps> => {
|
||||||
const chat = selectChat(global, chatId)!;
|
const chat = selectChat(global, chatId)!;
|
||||||
const fullInfo = selectChatFullInfo(global, chatId);
|
const fullInfo = selectChatFullInfo(global, chatId);
|
||||||
const { byId: usersById } = global.users;
|
const { byId: usersById } = global.users;
|
||||||
const { currentUserId } = global;
|
const { currentUserId } = global;
|
||||||
const isFormFullyDisabled = !(chat.isCreator || isPromotedByCurrentUser);
|
const isFormFullyDisabled = !(chat.isCreator || isPromotedByCurrentUser);
|
||||||
|
const adminMembersById = fullInfo?.adminMembersById;
|
||||||
|
|
||||||
|
const selectedAdminMember = selectedUserId ? adminMembersById?.[selectedUserId] : undefined;
|
||||||
|
const canEditRank = selectedAdminMember && selectCanEditRank(global, {
|
||||||
|
chatId,
|
||||||
|
userId: selectedAdminMember.userId,
|
||||||
|
isAdmin: selectedAdminMember.isAdmin,
|
||||||
|
isOwner: selectedAdminMember.isOwner,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
chat,
|
chat,
|
||||||
@ -517,7 +537,8 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
isFormFullyDisabled,
|
isFormFullyDisabled,
|
||||||
defaultRights: chat.adminRights,
|
defaultRights: chat.adminRights,
|
||||||
hasFullInfo: Boolean(fullInfo),
|
hasFullInfo: Boolean(fullInfo),
|
||||||
adminMembersById: fullInfo?.adminMembersById,
|
selectedAdminMember,
|
||||||
|
canEditRank,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
(global, { chatId }) => {
|
(global, { chatId }) => {
|
||||||
|
|||||||
@ -808,7 +808,7 @@ function Story({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{shouldRenderSkeleton && (
|
{shouldRenderSkeleton && (
|
||||||
<Skeleton className={buildClassName(skeletonTransitionClassNames, styles.fullSize)} />
|
<Skeleton className={buildClassName(skeletonTransitionClassNames, styles.fullSize)} animation="pulse" />
|
||||||
)}
|
)}
|
||||||
{!isVideo && fullMediaData && (
|
{!isVideo && fullMediaData && (
|
||||||
<img
|
<img
|
||||||
|
|||||||
@ -45,7 +45,7 @@ const InputText = ({
|
|||||||
disabled,
|
disabled,
|
||||||
readOnly,
|
readOnly,
|
||||||
placeholder,
|
placeholder,
|
||||||
autoComplete,
|
autoComplete = 'off',
|
||||||
inputMode,
|
inputMode,
|
||||||
maxLength,
|
maxLength,
|
||||||
tabIndex,
|
tabIndex,
|
||||||
|
|||||||
@ -245,6 +245,7 @@
|
|||||||
.info-name-title {
|
.info-name-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-row,
|
.info-row,
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import './Skeleton.scss';
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
variant?: 'rectangular' | 'rounded-rect' | 'round';
|
variant?: 'rectangular' | 'rounded-rect' | 'round';
|
||||||
animation?: 'wave' | 'pulse';
|
animation?: 'wave' | 'pulse' | 'none';
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
forceAspectRatio?: boolean;
|
forceAspectRatio?: boolean;
|
||||||
@ -18,7 +18,7 @@ type OwnProps = {
|
|||||||
|
|
||||||
const Skeleton: FC<OwnProps> = ({
|
const Skeleton: FC<OwnProps> = ({
|
||||||
variant = 'rectangular',
|
variant = 'rectangular',
|
||||||
animation = 'wave',
|
animation = 'none',
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
forceAspectRatio,
|
forceAspectRatio,
|
||||||
|
|||||||
@ -85,6 +85,7 @@ import {
|
|||||||
updateChatFullInfo,
|
updateChatFullInfo,
|
||||||
updateChatLastMessageId,
|
updateChatLastMessageId,
|
||||||
updateChatListSecondaryInfo,
|
updateChatListSecondaryInfo,
|
||||||
|
updateChatParticipantRank,
|
||||||
updateChats,
|
updateChats,
|
||||||
updateChatsLastMessageId,
|
updateChatsLastMessageId,
|
||||||
updateListedTopicIds,
|
updateListedTopicIds,
|
||||||
@ -2081,7 +2082,7 @@ addActionHandler('updateChatAdmin', async (global, actions, payload): Promise<vo
|
|||||||
if (selectIsCurrentUserFrozen(global)) return;
|
if (selectIsCurrentUserFrozen(global)) return;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
chatId, userId, adminRights, customTitle,
|
chatId, userId, adminRights, rank,
|
||||||
tabId = getCurrentTabId(),
|
tabId = getCurrentTabId(),
|
||||||
} = payload;
|
} = payload;
|
||||||
|
|
||||||
@ -2095,7 +2096,7 @@ addActionHandler('updateChatAdmin', async (global, actions, payload): Promise<vo
|
|||||||
if (!chat) return;
|
if (!chat) return;
|
||||||
|
|
||||||
await callApi('updateChatAdmin', {
|
await callApi('updateChatAdmin', {
|
||||||
chat, user, adminRights, customTitle,
|
chat, user, adminRights, rank,
|
||||||
});
|
});
|
||||||
|
|
||||||
const chatAfterUpdate = await callApi('fetchFullChat', chat);
|
const chatAfterUpdate = await callApi('fetchFullChat', chat);
|
||||||
@ -2116,7 +2117,7 @@ addActionHandler('updateChatAdmin', async (global, actions, payload): Promise<vo
|
|||||||
[userId]: {
|
[userId]: {
|
||||||
...adminMembersById[userId],
|
...adminMembersById[userId],
|
||||||
adminRights,
|
adminRights,
|
||||||
customTitle,
|
rank,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -2129,6 +2130,31 @@ addActionHandler('updateChatAdmin', async (global, actions, payload): Promise<vo
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('editChatParticipantRank', async (global, actions, payload): Promise<void> => {
|
||||||
|
if (selectIsCurrentUserFrozen(global)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
chatId, userId, rank,
|
||||||
|
} = payload;
|
||||||
|
|
||||||
|
const chat = selectChat(global, chatId);
|
||||||
|
const peer = selectPeer(global, userId);
|
||||||
|
if (!chat || !peer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isUpdated = await callApi('editChatParticipantRank', { chat, peer, rank });
|
||||||
|
if (!isUpdated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
global = getGlobal();
|
||||||
|
global = updateChatParticipantRank(global, chat.id, userId, rank);
|
||||||
|
setGlobal(global);
|
||||||
|
});
|
||||||
|
|
||||||
addActionHandler('updateChat', async (global, actions, payload): Promise<void> => {
|
addActionHandler('updateChat', async (global, actions, payload): Promise<void> => {
|
||||||
const {
|
const {
|
||||||
chatId, title, about, photo, tabId = getCurrentTabId(),
|
chatId, title, about, photo, tabId = getCurrentTabId(),
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import {
|
|||||||
updateChat,
|
updateChat,
|
||||||
updateChatFullInfo,
|
updateChatFullInfo,
|
||||||
updateChatListType,
|
updateChatListType,
|
||||||
|
updateChatParticipantRank,
|
||||||
updatePeerStoriesHidden,
|
updatePeerStoriesHidden,
|
||||||
updateTopic,
|
updateTopic,
|
||||||
} from '../../reducers';
|
} from '../../reducers';
|
||||||
@ -444,6 +445,10 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'updateChatParticipantRank': {
|
||||||
|
return updateChatParticipantRank(global, update.id, update.userId, update.rank);
|
||||||
|
}
|
||||||
|
|
||||||
case 'draftMessage': {
|
case 'draftMessage': {
|
||||||
const {
|
const {
|
||||||
chatId, threadId, draft,
|
chatId, threadId, draft,
|
||||||
|
|||||||
@ -77,3 +77,23 @@ addActionHandler('openProfileRatingModal', (global, actions, payload): ActionRet
|
|||||||
});
|
});
|
||||||
|
|
||||||
addTabStateResetterAction('closeProfileRatingModal', 'profileRatingModal');
|
addTabStateResetterAction('closeProfileRatingModal', 'profileRatingModal');
|
||||||
|
|
||||||
|
addActionHandler('openRankModal', (global, actions, payload): ActionReturnType => {
|
||||||
|
const { tabId = getCurrentTabId(), ...rest } = payload;
|
||||||
|
|
||||||
|
return updateTabState(global, {
|
||||||
|
rankModal: rest,
|
||||||
|
}, tabId);
|
||||||
|
});
|
||||||
|
|
||||||
|
addTabStateResetterAction('closeRankModal', 'rankModal');
|
||||||
|
|
||||||
|
addActionHandler('openEditRankModal', (global, actions, payload): ActionReturnType => {
|
||||||
|
const { tabId = getCurrentTabId(), ...rest } = payload;
|
||||||
|
|
||||||
|
return updateTabState(global, {
|
||||||
|
editRankModal: rest,
|
||||||
|
}, tabId);
|
||||||
|
});
|
||||||
|
|
||||||
|
addTabStateResetterAction('closeEditRankModal', 'editRankModal');
|
||||||
|
|||||||
@ -442,6 +442,55 @@ export function addChatMembers<T extends GlobalState>(global: T, chat: ApiChat,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function updateChatParticipantRank<T extends GlobalState>(
|
||||||
|
global: T, chatId: string, userId: string, rank: string,
|
||||||
|
): T {
|
||||||
|
const fullInfo = selectChatFullInfo(global, chatId);
|
||||||
|
if (!fullInfo) {
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { adminMembersById, members } = fullInfo;
|
||||||
|
let fullInfoUpdate: Partial<ApiChatFullInfo> | undefined;
|
||||||
|
|
||||||
|
const currentAdminMember = adminMembersById?.[userId];
|
||||||
|
if (currentAdminMember && currentAdminMember.rank !== rank) {
|
||||||
|
fullInfoUpdate = {
|
||||||
|
...fullInfoUpdate,
|
||||||
|
adminMembersById: {
|
||||||
|
...adminMembersById,
|
||||||
|
[userId]: {
|
||||||
|
...currentAdminMember,
|
||||||
|
rank,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let shouldUpdateMembers = false;
|
||||||
|
const updatedMembers = members?.map((member) => {
|
||||||
|
if (member.userId !== userId || member.rank === rank) {
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldUpdateMembers = true;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...member,
|
||||||
|
rank,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (shouldUpdateMembers) {
|
||||||
|
fullInfoUpdate = {
|
||||||
|
...fullInfoUpdate,
|
||||||
|
members: updatedMembers,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullInfoUpdate ? updateChatFullInfo(global, chatId, fullInfoUpdate) : global;
|
||||||
|
}
|
||||||
|
|
||||||
export function replaceSimilarChannels<T extends GlobalState>(
|
export function replaceSimilarChannels<T extends GlobalState>(
|
||||||
global: T,
|
global: T,
|
||||||
chatId: string,
|
chatId: string,
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
|
import type {
|
||||||
|
ApiChat, ApiChatFullInfo, ApiChatType,
|
||||||
|
} from '../../api/types';
|
||||||
import type { ChatListType } from '../../types';
|
import type { ChatListType } from '../../types';
|
||||||
import type { GlobalState, TabArgs } from '../types';
|
import type { GlobalState, TabArgs } from '../types';
|
||||||
import {
|
|
||||||
type ApiChat, type ApiChatFullInfo, type ApiChatType,
|
|
||||||
} from '../../api/types';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ALL_FOLDER_ID, ARCHIVED_FOLDER_ID, MEMBERS_LOAD_SLICE, SAVED_FOLDER_ID, SERVICE_NOTIFICATIONS_USER_ID,
|
ALL_FOLDER_ID, ARCHIVED_FOLDER_ID, MEMBERS_LOAD_SLICE, SAVED_FOLDER_ID, SERVICE_NOTIFICATIONS_USER_ID,
|
||||||
@ -12,12 +12,14 @@ import { isUserId } from '../../util/entities/ids';
|
|||||||
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
||||||
import {
|
import {
|
||||||
getHasAdminRight,
|
getHasAdminRight,
|
||||||
|
isChatAdmin,
|
||||||
isChatChannel,
|
isChatChannel,
|
||||||
isChatPublic,
|
isChatPublic,
|
||||||
isChatSuperGroup,
|
isChatSuperGroup,
|
||||||
isHistoryClearMessage,
|
isHistoryClearMessage,
|
||||||
isUserBot,
|
isUserBot,
|
||||||
isUserOnline,
|
isUserOnline,
|
||||||
|
isUserRightBanned,
|
||||||
} from '../helpers';
|
} from '../helpers';
|
||||||
import { selectActiveRestrictionReasons } from './messages';
|
import { selectActiveRestrictionReasons } from './messages';
|
||||||
import { selectTabState } from './tabs';
|
import { selectTabState } from './tabs';
|
||||||
@ -378,3 +380,35 @@ export function selectAreFoldersPresent<T extends GlobalState>(global: T) {
|
|||||||
const ids = global.chatFolders.orderedIds;
|
const ids = global.chatFolders.orderedIds;
|
||||||
return Boolean(ids && ids.length > 1);
|
return Boolean(ids && ids.length > 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function selectCanEditRank<T extends GlobalState>(global: T, {
|
||||||
|
chatId, userId, isAdmin, isOwner,
|
||||||
|
}: {
|
||||||
|
chatId: string;
|
||||||
|
userId: string;
|
||||||
|
isAdmin?: boolean;
|
||||||
|
isOwner?: boolean;
|
||||||
|
}) {
|
||||||
|
const chat = selectChat(global, chatId);
|
||||||
|
if (!chat || chat.isNotJoined || chat.isRestricted) return false;
|
||||||
|
|
||||||
|
const isCurrentUserAdmin = isChatAdmin(chat);
|
||||||
|
const hasAdminRight = getHasAdminRight(chat, 'manageRanks');
|
||||||
|
if (!isCurrentUserAdmin) return userId === global.currentUserId && !isUserRightBanned(chat, 'editRank'); // Users can edit only their own rank
|
||||||
|
if (!hasAdminRight) return false;
|
||||||
|
|
||||||
|
if (userId === global.currentUserId) return true; // Admin can edit own rank with permission
|
||||||
|
|
||||||
|
if (!chat.isCreator && (isOwner || isAdmin)) return false; // Admin can't edit rank of owner or another admin
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectCanEditOwnRank<T extends GlobalState>(global: T, chatId: string) {
|
||||||
|
const chat = selectChat(global, chatId);
|
||||||
|
if (!chat || chat.isNotJoined || chat.isRestricted) return false;
|
||||||
|
|
||||||
|
const isCurrentUserAdmin = isChatAdmin(chat);
|
||||||
|
if (!isCurrentUserAdmin) return !isUserRightBanned(chat, 'editRank'); // Users can edit only their own rank
|
||||||
|
return getHasAdminRight(chat, 'manageRanks');
|
||||||
|
}
|
||||||
|
|||||||
@ -182,8 +182,13 @@ export interface ActionPayloads {
|
|||||||
chatId: string;
|
chatId: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
adminRights: ApiChatAdminRights;
|
adminRights: ApiChatAdminRights;
|
||||||
customTitle?: string;
|
rank?: string;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
|
editChatParticipantRank: {
|
||||||
|
chatId: string;
|
||||||
|
userId: string;
|
||||||
|
rank: string;
|
||||||
|
};
|
||||||
|
|
||||||
checkChatInvite: {
|
checkChatInvite: {
|
||||||
hash: string;
|
hash: string;
|
||||||
@ -1938,6 +1943,22 @@ export interface ActionPayloads {
|
|||||||
level: number;
|
level: number;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
closeProfileRatingModal: WithTabId | undefined;
|
closeProfileRatingModal: WithTabId | undefined;
|
||||||
|
openRankModal: {
|
||||||
|
chatId: string;
|
||||||
|
userId: string;
|
||||||
|
isAdmin?: boolean;
|
||||||
|
isOwner?: boolean;
|
||||||
|
rank?: string;
|
||||||
|
} & WithTabId;
|
||||||
|
closeRankModal: WithTabId | undefined;
|
||||||
|
openEditRankModal: {
|
||||||
|
chatId: string;
|
||||||
|
userId: string;
|
||||||
|
isAdmin?: boolean;
|
||||||
|
isOwner?: boolean;
|
||||||
|
rank?: string;
|
||||||
|
} & WithTabId;
|
||||||
|
closeEditRankModal: WithTabId | undefined;
|
||||||
loadMoreProfilePhotos: {
|
loadMoreProfilePhotos: {
|
||||||
peerId: string;
|
peerId: string;
|
||||||
isPreload?: boolean;
|
isPreload?: boolean;
|
||||||
|
|||||||
@ -1035,5 +1035,19 @@ export type TabState = {
|
|||||||
|
|
||||||
isCocoonModalOpen?: boolean;
|
isCocoonModalOpen?: boolean;
|
||||||
|
|
||||||
|
rankModal?: {
|
||||||
|
chatId: string;
|
||||||
|
userId: string;
|
||||||
|
isAdmin?: boolean;
|
||||||
|
isOwner?: boolean;
|
||||||
|
rank?: string;
|
||||||
|
};
|
||||||
|
editRankModal?: {
|
||||||
|
chatId: string;
|
||||||
|
userId: string;
|
||||||
|
isAdmin?: boolean;
|
||||||
|
isOwner?: boolean;
|
||||||
|
rank?: string;
|
||||||
|
};
|
||||||
shouldOpenMessageMediaEditor?: boolean;
|
shouldOpenMessageMediaEditor?: boolean;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1794,6 +1794,7 @@ messages.deleteTopicHistory#d2816f10 peer:InputPeer top_msg_id:int = messages.Af
|
|||||||
messages.summarizeText#9d4104e2 flags:# peer:InputPeer id:int to_lang:flags.0?string = TextWithEntities;
|
messages.summarizeText#9d4104e2 flags:# peer:InputPeer id:int to_lang:flags.0?string = TextWithEntities;
|
||||||
messages.editChatCreator#f743b857 peer:InputPeer user_id:InputUser password:InputCheckPasswordSRP = Updates;
|
messages.editChatCreator#f743b857 peer:InputPeer user_id:InputUser password:InputCheckPasswordSRP = Updates;
|
||||||
messages.getFutureChatCreatorAfterLeave#3b7d0ea6 peer:InputPeer = User;
|
messages.getFutureChatCreatorAfterLeave#3b7d0ea6 peer:InputPeer = User;
|
||||||
|
messages.editChatParticipantRank#a00f32b0 peer:InputPeer participant:InputPeer rank:string = Updates;
|
||||||
updates.getState#edd4882a = updates.State;
|
updates.getState#edd4882a = updates.State;
|
||||||
updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference;
|
updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference;
|
||||||
updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
|
updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
|
||||||
|
|||||||
@ -248,6 +248,7 @@
|
|||||||
"messages.summarizeText",
|
"messages.summarizeText",
|
||||||
"messages.editChatCreator",
|
"messages.editChatCreator",
|
||||||
"messages.getFutureChatCreatorAfterLeave",
|
"messages.getFutureChatCreatorAfterLeave",
|
||||||
|
"messages.editChatParticipantRank",
|
||||||
"updates.getState",
|
"updates.getState",
|
||||||
"updates.getDifference",
|
"updates.getDifference",
|
||||||
"updates.getChannelDifference",
|
"updates.getChannelDifference",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -16,326 +16,327 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$icons-map: (
|
$icons-map: (
|
||||||
"zoom-out": "\f101",
|
"active-sessions": "\f101",
|
||||||
"zoom-in": "\f102",
|
"add-caption": "\f102",
|
||||||
"word-wrap": "\f103",
|
"add-filled": "\f103",
|
||||||
"webapp": "\f104",
|
"add-one-badge": "\f104",
|
||||||
"web": "\f105",
|
"add-user-filled": "\f105",
|
||||||
"warning": "\f106",
|
"add-user": "\f106",
|
||||||
"volume-3": "\f107",
|
"add": "\f107",
|
||||||
"volume-2": "\f108",
|
"admin": "\f108",
|
||||||
"volume-1": "\f109",
|
"allow-speak": "\f109",
|
||||||
"voice-chat": "\f10a",
|
"animals": "\f10a",
|
||||||
"view-once": "\f10b",
|
"animations": "\f10b",
|
||||||
"video": "\f10c",
|
"archive-filled": "\f10c",
|
||||||
"video-stop": "\f10d",
|
"archive-from-main": "\f10d",
|
||||||
"video-outlined": "\f10e",
|
"archive-to-main": "\f10e",
|
||||||
"user": "\f10f",
|
"archive": "\f10f",
|
||||||
"user-stars": "\f110",
|
"arrow-down-circle": "\f110",
|
||||||
"user-online": "\f111",
|
"arrow-down": "\f111",
|
||||||
"user-filled": "\f112",
|
"arrow-left": "\f112",
|
||||||
"up": "\f113",
|
"arrow-right": "\f113",
|
||||||
"unread": "\f114",
|
"ask-support": "\f114",
|
||||||
"unpin": "\f115",
|
"attach": "\f115",
|
||||||
"unmute": "\f116",
|
"auction-drop": "\f116",
|
||||||
"unlock": "\f117",
|
"auction-filled": "\f117",
|
||||||
"unlock-badge": "\f118",
|
"auction-next-round": "\f118",
|
||||||
"unlist": "\f119",
|
"auction": "\f119",
|
||||||
"unlist-outline": "\f11a",
|
"author-hidden": "\f11a",
|
||||||
"unique-profile": "\f11b",
|
"avatar-archived-chats": "\f11b",
|
||||||
"undo": "\f11c",
|
"avatar-deleted-account": "\f11c",
|
||||||
"understood": "\f11d",
|
"avatar-saved-messages": "\f11d",
|
||||||
"underlined": "\f11e",
|
"bold": "\f11e",
|
||||||
"unarchive": "\f11f",
|
"boost-craft-chance": "\f11f",
|
||||||
"truck": "\f120",
|
"boost-outline": "\f120",
|
||||||
"transcribe": "\f121",
|
"boost": "\f121",
|
||||||
"trade": "\f122",
|
"boostcircle": "\f122",
|
||||||
"topic-new": "\f123",
|
"boosts": "\f123",
|
||||||
"tools": "\f124",
|
"bot-command": "\f124",
|
||||||
"toncoin": "\f125",
|
"bot-commands-filled": "\f125",
|
||||||
"timer": "\f126",
|
"bots": "\f126",
|
||||||
"tag": "\f127",
|
"brush": "\f127",
|
||||||
"tag-name": "\f128",
|
"bug": "\f128",
|
||||||
"tag-filter": "\f129",
|
"calendar-filter": "\f129",
|
||||||
"tag-crossed": "\f12a",
|
"calendar": "\f12a",
|
||||||
"tag-add": "\f12b",
|
"camera-add": "\f12b",
|
||||||
"strikethrough": "\f12c",
|
"camera": "\f12c",
|
||||||
"story-reply": "\f12d",
|
"car": "\f12d",
|
||||||
"story-priority": "\f12e",
|
"card": "\f12e",
|
||||||
"story-expired": "\f12f",
|
"cash-circle": "\f12f",
|
||||||
"story-caption": "\f130",
|
"channel-filled": "\f130",
|
||||||
"stop": "\f131",
|
"channel": "\f131",
|
||||||
"stop-raising-hand": "\f132",
|
"channelviews": "\f132",
|
||||||
"stickers": "\f133",
|
"chat-badge": "\f133",
|
||||||
"stealth-past": "\f134",
|
"chats-badge": "\f134",
|
||||||
"stealth-future": "\f135",
|
"check": "\f135",
|
||||||
"stats": "\f136",
|
"clock-edit": "\f136",
|
||||||
"stars-refund": "\f137",
|
"clock": "\f137",
|
||||||
"stars-lock": "\f138",
|
"close-circle": "\f138",
|
||||||
"star": "\f139",
|
"close-topic": "\f139",
|
||||||
"sport": "\f13a",
|
"close": "\f13a",
|
||||||
"spoiler": "\f13b",
|
"closed-gift": "\f13b",
|
||||||
"spoiler-disable": "\f13c",
|
"cloud-download": "\f13c",
|
||||||
"speaker": "\f13d",
|
"collapse-modal": "\f13d",
|
||||||
"speaker-story": "\f13e",
|
"collapse": "\f13e",
|
||||||
"speaker-outline": "\f13f",
|
"colorize": "\f13f",
|
||||||
"speaker-muted-story": "\f140",
|
"combine-craft": "\f140",
|
||||||
"sort": "\f141",
|
"comments-sticker": "\f141",
|
||||||
"sort-by-price": "\f142",
|
"comments": "\f142",
|
||||||
"sort-by-number": "\f143",
|
"copy-media": "\f143",
|
||||||
"sort-by-date": "\f144",
|
"copy": "\f144",
|
||||||
"smile": "\f145",
|
"craft": "\f145",
|
||||||
"smallscreen": "\f146",
|
"crop": "\f146",
|
||||||
"skip-previous": "\f147",
|
"crown-take-off-outline": "\f147",
|
||||||
"skip-next": "\f148",
|
"crown-take-off": "\f148",
|
||||||
"sidebar": "\f149",
|
"crown-wear-outline": "\f149",
|
||||||
"show-message": "\f14a",
|
"crown-wear": "\f14a",
|
||||||
"share-screen": "\f14b",
|
"darkmode": "\f14b",
|
||||||
"share-screen-stop": "\f14c",
|
"data": "\f14c",
|
||||||
"share-screen-outlined": "\f14d",
|
"delete-filled": "\f14d",
|
||||||
"share-filled": "\f14e",
|
"delete-left": "\f14e",
|
||||||
"settings": "\f14f",
|
"delete-user": "\f14f",
|
||||||
"settings-filled": "\f150",
|
"delete": "\f150",
|
||||||
"send": "\f151",
|
"diamond": "\f151",
|
||||||
"send-outline": "\f152",
|
"document": "\f152",
|
||||||
"sell": "\f153",
|
"double-badge": "\f153",
|
||||||
"sell-outline": "\f154",
|
"down": "\f154",
|
||||||
"select": "\f155",
|
"download": "\f155",
|
||||||
"search": "\f156",
|
"dropdown-arrows": "\f156",
|
||||||
"sd-photo": "\f157",
|
"eats": "\f157",
|
||||||
"scheduled": "\f158",
|
"edit": "\f158",
|
||||||
"schedule": "\f159",
|
"email": "\f159",
|
||||||
"saved-messages": "\f15a",
|
"enter": "\f15a",
|
||||||
"save-story": "\f15b",
|
"expand-modal": "\f15b",
|
||||||
"rotate": "\f15c",
|
"expand": "\f15c",
|
||||||
"revote": "\f15d",
|
"eye-crossed-outline": "\f15d",
|
||||||
"revenue-split": "\f15e",
|
"eye-crossed": "\f15e",
|
||||||
"reply": "\f15f",
|
"eye-outline": "\f15f",
|
||||||
"reply-filled": "\f160",
|
"eye": "\f160",
|
||||||
"replies": "\f161",
|
"favorite-filled": "\f161",
|
||||||
"replace": "\f162",
|
"favorite": "\f162",
|
||||||
"reorder-tabs": "\f163",
|
"file-badge": "\f163",
|
||||||
"reopen-topic": "\f164",
|
"flag": "\f164",
|
||||||
"remove": "\f165",
|
"flip": "\f165",
|
||||||
"remove-quote": "\f166",
|
"folder-badge": "\f166",
|
||||||
"reload": "\f167",
|
"folder-tabs-bot": "\f167",
|
||||||
"refund": "\f168",
|
"folder-tabs-channel": "\f168",
|
||||||
"redo": "\f169",
|
"folder-tabs-chat": "\f169",
|
||||||
"recent": "\f16a",
|
"folder-tabs-chats": "\f16a",
|
||||||
"readchats": "\f16b",
|
"folder-tabs-folder": "\f16b",
|
||||||
"radial-badge": "\f16c",
|
"folder-tabs-group": "\f16c",
|
||||||
"quote": "\f16d",
|
"folder-tabs-star": "\f16d",
|
||||||
"quote-text": "\f16e",
|
"folder-tabs-user": "\f16e",
|
||||||
"proof-of-ownership": "\f16f",
|
"folder": "\f16f",
|
||||||
"privacy-policy": "\f170",
|
"fontsize": "\f170",
|
||||||
"previous": "\f171",
|
"forums": "\f171",
|
||||||
"poll": "\f172",
|
"forward": "\f172",
|
||||||
"play": "\f173",
|
"fragment": "\f173",
|
||||||
"play-story": "\f174",
|
"frozen-time": "\f174",
|
||||||
"pip": "\f175",
|
"fullscreen": "\f175",
|
||||||
"pinned-message": "\f176",
|
"gifs": "\f176",
|
||||||
"pinned-chat": "\f177",
|
"gift-transfer-inline": "\f177",
|
||||||
"pin": "\f178",
|
"gift": "\f178",
|
||||||
"pin-list": "\f179",
|
"group-filled": "\f179",
|
||||||
"pin-badge": "\f17a",
|
"group": "\f17a",
|
||||||
"photo": "\f17b",
|
"grouped-disable": "\f17b",
|
||||||
"phone": "\f17c",
|
"grouped": "\f17c",
|
||||||
"phone-discard": "\f17d",
|
"hand-stop": "\f17d",
|
||||||
"phone-discard-outline": "\f17e",
|
"hashtag": "\f17e",
|
||||||
"permissions": "\f17f",
|
"hd-photo": "\f17f",
|
||||||
"pause": "\f180",
|
"heart-outline": "\f180",
|
||||||
"password-off": "\f181",
|
"heart": "\f181",
|
||||||
"open-in-new-tab": "\f182",
|
"help": "\f182",
|
||||||
"one-filled": "\f183",
|
"info-filled": "\f183",
|
||||||
"note": "\f184",
|
"info": "\f184",
|
||||||
"non-contacts": "\f185",
|
"install": "\f185",
|
||||||
"noise-suppression": "\f186",
|
"italic": "\f186",
|
||||||
"nochannel": "\f187",
|
"key": "\f187",
|
||||||
"next": "\f188",
|
"keyboard": "\f188",
|
||||||
"next-link": "\f189",
|
"lamp": "\f189",
|
||||||
"new-chat-filled": "\f18a",
|
"language": "\f18a",
|
||||||
"my-notes": "\f18b",
|
"large-pause": "\f18b",
|
||||||
"muted": "\f18c",
|
"large-play": "\f18c",
|
||||||
"mute": "\f18d",
|
"link-badge": "\f18d",
|
||||||
"move-caption-up": "\f18e",
|
"link-broken": "\f18e",
|
||||||
"move-caption-down": "\f18f",
|
"link": "\f18f",
|
||||||
"more": "\f190",
|
"location": "\f190",
|
||||||
"more-circle": "\f191",
|
"lock-badge": "\f191",
|
||||||
"monospace": "\f192",
|
"lock": "\f192",
|
||||||
"microphone": "\f193",
|
"logout": "\f193",
|
||||||
"microphone-alt": "\f194",
|
"loop": "\f194",
|
||||||
"message": "\f195",
|
"mention": "\f195",
|
||||||
"message-succeeded": "\f196",
|
"menu": "\f196",
|
||||||
"message-read": "\f197",
|
"message-failed": "\f197",
|
||||||
"message-pending": "\f198",
|
"message-pending": "\f198",
|
||||||
"message-failed": "\f199",
|
"message-read": "\f199",
|
||||||
"menu": "\f19a",
|
"message-succeeded": "\f19a",
|
||||||
"mention": "\f19b",
|
"message": "\f19b",
|
||||||
"loop": "\f19c",
|
"microphone-alt": "\f19c",
|
||||||
"logout": "\f19d",
|
"microphone": "\f19d",
|
||||||
"lock": "\f19e",
|
"monospace": "\f19e",
|
||||||
"lock-badge": "\f19f",
|
"more-circle": "\f19f",
|
||||||
"location": "\f1a0",
|
"more": "\f1a0",
|
||||||
"link": "\f1a1",
|
"move-caption-down": "\f1a1",
|
||||||
"link-broken": "\f1a2",
|
"move-caption-up": "\f1a2",
|
||||||
"link-badge": "\f1a3",
|
"mute": "\f1a3",
|
||||||
"large-play": "\f1a4",
|
"muted": "\f1a4",
|
||||||
"large-pause": "\f1a5",
|
"my-notes": "\f1a5",
|
||||||
"language": "\f1a6",
|
"new-chat-filled": "\f1a6",
|
||||||
"lamp": "\f1a7",
|
"next-link": "\f1a7",
|
||||||
"keyboard": "\f1a8",
|
"next": "\f1a8",
|
||||||
"key": "\f1a9",
|
"nochannel": "\f1a9",
|
||||||
"italic": "\f1aa",
|
"noise-suppression": "\f1aa",
|
||||||
"install": "\f1ab",
|
"non-contacts": "\f1ab",
|
||||||
"info": "\f1ac",
|
"note": "\f1ac",
|
||||||
"info-filled": "\f1ad",
|
"one-filled": "\f1ad",
|
||||||
"help": "\f1ae",
|
"open-in-new-tab": "\f1ae",
|
||||||
"heart": "\f1af",
|
"password-off": "\f1af",
|
||||||
"heart-outline": "\f1b0",
|
"pause": "\f1b0",
|
||||||
"hd-photo": "\f1b1",
|
"permissions": "\f1b1",
|
||||||
"hashtag": "\f1b2",
|
"phone-discard-outline": "\f1b2",
|
||||||
"hand-stop": "\f1b3",
|
"phone-discard": "\f1b3",
|
||||||
"grouped": "\f1b4",
|
"phone": "\f1b4",
|
||||||
"grouped-disable": "\f1b5",
|
"photo": "\f1b5",
|
||||||
"group": "\f1b6",
|
"pin-badge": "\f1b6",
|
||||||
"group-filled": "\f1b7",
|
"pin-list": "\f1b7",
|
||||||
"gift": "\f1b8",
|
"pin": "\f1b8",
|
||||||
"gift-transfer-inline": "\f1b9",
|
"pinned-chat": "\f1b9",
|
||||||
"gifs": "\f1ba",
|
"pinned-message": "\f1ba",
|
||||||
"fullscreen": "\f1bb",
|
"pip": "\f1bb",
|
||||||
"frozen-time": "\f1bc",
|
"play-story": "\f1bc",
|
||||||
"fragment": "\f1bd",
|
"play": "\f1bd",
|
||||||
"forward": "\f1be",
|
"poll": "\f1be",
|
||||||
"forums": "\f1bf",
|
"previous": "\f1bf",
|
||||||
"fontsize": "\f1c0",
|
"privacy-policy": "\f1c0",
|
||||||
"folder": "\f1c1",
|
"proof-of-ownership": "\f1c1",
|
||||||
"folder-badge": "\f1c2",
|
"quote-text": "\f1c2",
|
||||||
"flip": "\f1c3",
|
"quote": "\f1c3",
|
||||||
"flag": "\f1c4",
|
"radial-badge": "\f1c4",
|
||||||
"file-badge": "\f1c5",
|
"rating-icons-level1": "\f1c5",
|
||||||
"favorite": "\f1c6",
|
"rating-icons-level10": "\f1c6",
|
||||||
"favorite-filled": "\f1c7",
|
"rating-icons-level2": "\f1c7",
|
||||||
"eye": "\f1c8",
|
"rating-icons-level20": "\f1c8",
|
||||||
"eye-outline": "\f1c9",
|
"rating-icons-level3": "\f1c9",
|
||||||
"eye-crossed": "\f1ca",
|
"rating-icons-level30": "\f1ca",
|
||||||
"eye-crossed-outline": "\f1cb",
|
"rating-icons-level4": "\f1cb",
|
||||||
"expand": "\f1cc",
|
"rating-icons-level40": "\f1cc",
|
||||||
"expand-modal": "\f1cd",
|
"rating-icons-level5": "\f1cd",
|
||||||
"enter": "\f1ce",
|
"rating-icons-level50": "\f1ce",
|
||||||
"email": "\f1cf",
|
"rating-icons-level6": "\f1cf",
|
||||||
"edit": "\f1d0",
|
"rating-icons-level60": "\f1d0",
|
||||||
"eats": "\f1d1",
|
"rating-icons-level7": "\f1d1",
|
||||||
"dropdown-arrows": "\f1d2",
|
"rating-icons-level70": "\f1d2",
|
||||||
"download": "\f1d3",
|
"rating-icons-level8": "\f1d3",
|
||||||
"down": "\f1d4",
|
"rating-icons-level80": "\f1d4",
|
||||||
"double-badge": "\f1d5",
|
"rating-icons-level9": "\f1d5",
|
||||||
"document": "\f1d6",
|
"rating-icons-level90": "\f1d6",
|
||||||
"diamond": "\f1d7",
|
"rating-icons-negative": "\f1d7",
|
||||||
"delete": "\f1d8",
|
"readchats": "\f1d8",
|
||||||
"delete-user": "\f1d9",
|
"recent": "\f1d9",
|
||||||
"delete-left": "\f1da",
|
"redo": "\f1da",
|
||||||
"delete-filled": "\f1db",
|
"refund": "\f1db",
|
||||||
"data": "\f1dc",
|
"reload": "\f1dc",
|
||||||
"darkmode": "\f1dd",
|
"remove-quote": "\f1dd",
|
||||||
"crown-wear": "\f1de",
|
"remove": "\f1de",
|
||||||
"crown-wear-outline": "\f1df",
|
"reopen-topic": "\f1df",
|
||||||
"crown-take-off": "\f1e0",
|
"reorder-tabs": "\f1e0",
|
||||||
"crown-take-off-outline": "\f1e1",
|
"replace": "\f1e1",
|
||||||
"crop": "\f1e2",
|
"replies": "\f1e2",
|
||||||
"craft": "\f1e3",
|
"reply-filled": "\f1e3",
|
||||||
"copy": "\f1e4",
|
"reply": "\f1e4",
|
||||||
"copy-media": "\f1e5",
|
"revenue-split": "\f1e5",
|
||||||
"comments": "\f1e6",
|
"revote": "\f1e6",
|
||||||
"comments-sticker": "\f1e7",
|
"rotate": "\f1e7",
|
||||||
"combine-craft": "\f1e8",
|
"save-story": "\f1e8",
|
||||||
"colorize": "\f1e9",
|
"saved-messages": "\f1e9",
|
||||||
"collapse": "\f1ea",
|
"schedule": "\f1ea",
|
||||||
"collapse-modal": "\f1eb",
|
"scheduled": "\f1eb",
|
||||||
"cloud-download": "\f1ec",
|
"sd-photo": "\f1ec",
|
||||||
"closed-gift": "\f1ed",
|
"search": "\f1ed",
|
||||||
"close": "\f1ee",
|
"select": "\f1ee",
|
||||||
"close-topic": "\f1ef",
|
"sell-outline": "\f1ef",
|
||||||
"close-circle": "\f1f0",
|
"sell": "\f1f0",
|
||||||
"clock": "\f1f1",
|
"send-outline": "\f1f1",
|
||||||
"clock-edit": "\f1f2",
|
"send": "\f1f2",
|
||||||
"check": "\f1f3",
|
"settings-filled": "\f1f3",
|
||||||
"chats-badge": "\f1f4",
|
"settings": "\f1f4",
|
||||||
"chat-badge": "\f1f5",
|
"share-filled": "\f1f5",
|
||||||
"channelviews": "\f1f6",
|
"share-screen-outlined": "\f1f6",
|
||||||
"channel": "\f1f7",
|
"share-screen-stop": "\f1f7",
|
||||||
"channel-filled": "\f1f8",
|
"share-screen": "\f1f8",
|
||||||
"cash-circle": "\f1f9",
|
"show-message": "\f1f9",
|
||||||
"card": "\f1fa",
|
"sidebar": "\f1fa",
|
||||||
"car": "\f1fb",
|
"skip-next": "\f1fb",
|
||||||
"camera": "\f1fc",
|
"skip-previous": "\f1fc",
|
||||||
"camera-add": "\f1fd",
|
"smallscreen": "\f1fd",
|
||||||
"calendar": "\f1fe",
|
"smile": "\f1fe",
|
||||||
"calendar-filter": "\f1ff",
|
"sort-by-date": "\f1ff",
|
||||||
"bug": "\f200",
|
"sort-by-number": "\f200",
|
||||||
"brush": "\f201",
|
"sort-by-price": "\f201",
|
||||||
"bots": "\f202",
|
"sort": "\f202",
|
||||||
"bot-commands-filled": "\f203",
|
"speaker-muted-story": "\f203",
|
||||||
"bot-command": "\f204",
|
"speaker-outline": "\f204",
|
||||||
"boosts": "\f205",
|
"speaker-story": "\f205",
|
||||||
"boostcircle": "\f206",
|
"speaker": "\f206",
|
||||||
"boost": "\f207",
|
"spoiler-disable": "\f207",
|
||||||
"boost-outline": "\f208",
|
"spoiler": "\f208",
|
||||||
"boost-craft-chance": "\f209",
|
"sport": "\f209",
|
||||||
"bold": "\f20a",
|
"star": "\f20a",
|
||||||
"avatar-saved-messages": "\f20b",
|
"stars-lock": "\f20b",
|
||||||
"avatar-deleted-account": "\f20c",
|
"stars-refund": "\f20c",
|
||||||
"avatar-archived-chats": "\f20d",
|
"stats": "\f20d",
|
||||||
"author-hidden": "\f20e",
|
"stealth-future": "\f20e",
|
||||||
"auction": "\f20f",
|
"stealth-past": "\f20f",
|
||||||
"auction-next-round": "\f210",
|
"stickers": "\f210",
|
||||||
"auction-filled": "\f211",
|
"stop-raising-hand": "\f211",
|
||||||
"auction-drop": "\f212",
|
"stop": "\f212",
|
||||||
"attach": "\f213",
|
"story-caption": "\f213",
|
||||||
"ask-support": "\f214",
|
"story-expired": "\f214",
|
||||||
"arrow-right": "\f215",
|
"story-priority": "\f215",
|
||||||
"arrow-left": "\f216",
|
"story-reply": "\f216",
|
||||||
"arrow-down": "\f217",
|
"strikethrough": "\f217",
|
||||||
"arrow-down-circle": "\f218",
|
"tag-add": "\f218",
|
||||||
"archive": "\f219",
|
"tag-crossed": "\f219",
|
||||||
"archive-to-main": "\f21a",
|
"tag-filter": "\f21a",
|
||||||
"archive-from-main": "\f21b",
|
"tag-name": "\f21b",
|
||||||
"archive-filled": "\f21c",
|
"tag": "\f21c",
|
||||||
"animations": "\f21d",
|
"timer": "\f21d",
|
||||||
"animals": "\f21e",
|
"toncoin": "\f21e",
|
||||||
"allow-speak": "\f21f",
|
"tools": "\f21f",
|
||||||
"admin": "\f220",
|
"topic-new": "\f220",
|
||||||
"add": "\f221",
|
"trade": "\f221",
|
||||||
"add-user": "\f222",
|
"transcribe": "\f222",
|
||||||
"add-user-filled": "\f223",
|
"truck": "\f223",
|
||||||
"add-one-badge": "\f224",
|
"unarchive": "\f224",
|
||||||
"add-filled": "\f225",
|
"underlined": "\f225",
|
||||||
"add-caption": "\f226",
|
"understood": "\f226",
|
||||||
"active-sessions": "\f227",
|
"undo": "\f227",
|
||||||
"rating-icons-negative": "\f228",
|
"unique-profile": "\f228",
|
||||||
"rating-icons-level90": "\f229",
|
"unlist-outline": "\f229",
|
||||||
"rating-icons-level9": "\f22a",
|
"unlist": "\f22a",
|
||||||
"rating-icons-level80": "\f22b",
|
"unlock-badge": "\f22b",
|
||||||
"rating-icons-level8": "\f22c",
|
"unlock": "\f22c",
|
||||||
"rating-icons-level70": "\f22d",
|
"unmute": "\f22d",
|
||||||
"rating-icons-level7": "\f22e",
|
"unpin": "\f22e",
|
||||||
"rating-icons-level60": "\f22f",
|
"unread": "\f22f",
|
||||||
"rating-icons-level6": "\f230",
|
"up": "\f230",
|
||||||
"rating-icons-level50": "\f231",
|
"user-filled": "\f231",
|
||||||
"rating-icons-level5": "\f232",
|
"user-online": "\f232",
|
||||||
"rating-icons-level40": "\f233",
|
"user-stars": "\f233",
|
||||||
"rating-icons-level4": "\f234",
|
"user-tag": "\f234",
|
||||||
"rating-icons-level30": "\f235",
|
"user": "\f235",
|
||||||
"rating-icons-level3": "\f236",
|
"video-outlined": "\f236",
|
||||||
"rating-icons-level20": "\f237",
|
"video-stop": "\f237",
|
||||||
"rating-icons-level2": "\f238",
|
"video": "\f238",
|
||||||
"rating-icons-level10": "\f239",
|
"view-once": "\f239",
|
||||||
"rating-icons-level1": "\f23a",
|
"voice-chat": "\f23a",
|
||||||
"folder-tabs-user": "\f23b",
|
"volume-1": "\f23b",
|
||||||
"folder-tabs-star": "\f23c",
|
"volume-2": "\f23c",
|
||||||
"folder-tabs-group": "\f23d",
|
"volume-3": "\f23d",
|
||||||
"folder-tabs-folder": "\f23e",
|
"warning": "\f23e",
|
||||||
"folder-tabs-chats": "\f23f",
|
"web": "\f23f",
|
||||||
"folder-tabs-chat": "\f240",
|
"webapp": "\f240",
|
||||||
"folder-tabs-channel": "\f241",
|
"word-wrap": "\f241",
|
||||||
"folder-tabs-bot": "\f242",
|
"zoom-in": "\f242",
|
||||||
|
"zoom-out": "\f243",
|
||||||
);
|
);
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -1,323 +1,324 @@
|
|||||||
export type FontIconName =
|
export type FontIconName =
|
||||||
| 'zoom-out'
|
|
||||||
| 'zoom-in'
|
|
||||||
| 'word-wrap'
|
|
||||||
| 'webapp'
|
|
||||||
| 'web'
|
|
||||||
| 'warning'
|
|
||||||
| 'volume-3'
|
|
||||||
| 'volume-2'
|
|
||||||
| 'volume-1'
|
|
||||||
| 'voice-chat'
|
|
||||||
| 'view-once'
|
|
||||||
| 'video'
|
|
||||||
| 'video-stop'
|
|
||||||
| 'video-outlined'
|
|
||||||
| 'user'
|
|
||||||
| 'user-stars'
|
|
||||||
| 'user-online'
|
|
||||||
| 'user-filled'
|
|
||||||
| 'up'
|
|
||||||
| 'unread'
|
|
||||||
| 'unpin'
|
|
||||||
| 'unmute'
|
|
||||||
| 'unlock'
|
|
||||||
| 'unlock-badge'
|
|
||||||
| 'unlist'
|
|
||||||
| 'unlist-outline'
|
|
||||||
| 'unique-profile'
|
|
||||||
| 'undo'
|
|
||||||
| 'understood'
|
|
||||||
| 'underlined'
|
|
||||||
| 'unarchive'
|
|
||||||
| 'truck'
|
|
||||||
| 'transcribe'
|
|
||||||
| 'trade'
|
|
||||||
| 'topic-new'
|
|
||||||
| 'tools'
|
|
||||||
| 'toncoin'
|
|
||||||
| 'timer'
|
|
||||||
| 'tag'
|
|
||||||
| 'tag-name'
|
|
||||||
| 'tag-filter'
|
|
||||||
| 'tag-crossed'
|
|
||||||
| 'tag-add'
|
|
||||||
| 'strikethrough'
|
|
||||||
| 'story-reply'
|
|
||||||
| 'story-priority'
|
|
||||||
| 'story-expired'
|
|
||||||
| 'story-caption'
|
|
||||||
| 'stop'
|
|
||||||
| 'stop-raising-hand'
|
|
||||||
| 'stickers'
|
|
||||||
| 'stealth-past'
|
|
||||||
| 'stealth-future'
|
|
||||||
| 'stats'
|
|
||||||
| 'stars-refund'
|
|
||||||
| 'stars-lock'
|
|
||||||
| 'star'
|
|
||||||
| 'sport'
|
|
||||||
| 'spoiler'
|
|
||||||
| 'spoiler-disable'
|
|
||||||
| 'speaker'
|
|
||||||
| 'speaker-story'
|
|
||||||
| 'speaker-outline'
|
|
||||||
| 'speaker-muted-story'
|
|
||||||
| 'sort'
|
|
||||||
| 'sort-by-price'
|
|
||||||
| 'sort-by-number'
|
|
||||||
| 'sort-by-date'
|
|
||||||
| 'smile'
|
|
||||||
| 'smallscreen'
|
|
||||||
| 'skip-previous'
|
|
||||||
| 'skip-next'
|
|
||||||
| 'sidebar'
|
|
||||||
| 'show-message'
|
|
||||||
| 'share-screen'
|
|
||||||
| 'share-screen-stop'
|
|
||||||
| 'share-screen-outlined'
|
|
||||||
| 'share-filled'
|
|
||||||
| 'settings'
|
|
||||||
| 'settings-filled'
|
|
||||||
| 'send'
|
|
||||||
| 'send-outline'
|
|
||||||
| 'sell'
|
|
||||||
| 'sell-outline'
|
|
||||||
| 'select'
|
|
||||||
| 'search'
|
|
||||||
| 'sd-photo'
|
|
||||||
| 'scheduled'
|
|
||||||
| 'schedule'
|
|
||||||
| 'saved-messages'
|
|
||||||
| 'save-story'
|
|
||||||
| 'rotate'
|
|
||||||
| 'revote'
|
|
||||||
| 'revenue-split'
|
|
||||||
| 'reply'
|
|
||||||
| 'reply-filled'
|
|
||||||
| 'replies'
|
|
||||||
| 'replace'
|
|
||||||
| 'reorder-tabs'
|
|
||||||
| 'reopen-topic'
|
|
||||||
| 'remove'
|
|
||||||
| 'remove-quote'
|
|
||||||
| 'reload'
|
|
||||||
| 'refund'
|
|
||||||
| 'redo'
|
|
||||||
| 'recent'
|
|
||||||
| 'readchats'
|
|
||||||
| 'radial-badge'
|
|
||||||
| 'quote'
|
|
||||||
| 'quote-text'
|
|
||||||
| 'proof-of-ownership'
|
|
||||||
| 'privacy-policy'
|
|
||||||
| 'previous'
|
|
||||||
| 'poll'
|
|
||||||
| 'play'
|
|
||||||
| 'play-story'
|
|
||||||
| 'pip'
|
|
||||||
| 'pinned-message'
|
|
||||||
| 'pinned-chat'
|
|
||||||
| 'pin'
|
|
||||||
| 'pin-list'
|
|
||||||
| 'pin-badge'
|
|
||||||
| 'photo'
|
|
||||||
| 'phone'
|
|
||||||
| 'phone-discard'
|
|
||||||
| 'phone-discard-outline'
|
|
||||||
| 'permissions'
|
|
||||||
| 'pause'
|
|
||||||
| 'password-off'
|
|
||||||
| 'open-in-new-tab'
|
|
||||||
| 'one-filled'
|
|
||||||
| 'note'
|
|
||||||
| 'non-contacts'
|
|
||||||
| 'noise-suppression'
|
|
||||||
| 'nochannel'
|
|
||||||
| 'next'
|
|
||||||
| 'next-link'
|
|
||||||
| 'new-chat-filled'
|
|
||||||
| 'my-notes'
|
|
||||||
| 'muted'
|
|
||||||
| 'mute'
|
|
||||||
| 'move-caption-up'
|
|
||||||
| 'move-caption-down'
|
|
||||||
| 'more'
|
|
||||||
| 'more-circle'
|
|
||||||
| 'monospace'
|
|
||||||
| 'microphone'
|
|
||||||
| 'microphone-alt'
|
|
||||||
| 'message'
|
|
||||||
| 'message-succeeded'
|
|
||||||
| 'message-read'
|
|
||||||
| 'message-pending'
|
|
||||||
| 'message-failed'
|
|
||||||
| 'menu'
|
|
||||||
| 'mention'
|
|
||||||
| 'loop'
|
|
||||||
| 'logout'
|
|
||||||
| 'lock'
|
|
||||||
| 'lock-badge'
|
|
||||||
| 'location'
|
|
||||||
| 'link'
|
|
||||||
| 'link-broken'
|
|
||||||
| 'link-badge'
|
|
||||||
| 'large-play'
|
|
||||||
| 'large-pause'
|
|
||||||
| 'language'
|
|
||||||
| 'lamp'
|
|
||||||
| 'keyboard'
|
|
||||||
| 'key'
|
|
||||||
| 'italic'
|
|
||||||
| 'install'
|
|
||||||
| 'info'
|
|
||||||
| 'info-filled'
|
|
||||||
| 'help'
|
|
||||||
| 'heart'
|
|
||||||
| 'heart-outline'
|
|
||||||
| 'hd-photo'
|
|
||||||
| 'hashtag'
|
|
||||||
| 'hand-stop'
|
|
||||||
| 'grouped'
|
|
||||||
| 'grouped-disable'
|
|
||||||
| 'group'
|
|
||||||
| 'group-filled'
|
|
||||||
| 'gift'
|
|
||||||
| 'gift-transfer-inline'
|
|
||||||
| 'gifs'
|
|
||||||
| 'fullscreen'
|
|
||||||
| 'frozen-time'
|
|
||||||
| 'fragment'
|
|
||||||
| 'forward'
|
|
||||||
| 'forums'
|
|
||||||
| 'fontsize'
|
|
||||||
| 'folder'
|
|
||||||
| 'folder-badge'
|
|
||||||
| 'flip'
|
|
||||||
| 'flag'
|
|
||||||
| 'file-badge'
|
|
||||||
| 'favorite'
|
|
||||||
| 'favorite-filled'
|
|
||||||
| 'eye'
|
|
||||||
| 'eye-outline'
|
|
||||||
| 'eye-crossed'
|
|
||||||
| 'eye-crossed-outline'
|
|
||||||
| 'expand'
|
|
||||||
| 'expand-modal'
|
|
||||||
| 'enter'
|
|
||||||
| 'email'
|
|
||||||
| 'edit'
|
|
||||||
| 'eats'
|
|
||||||
| 'dropdown-arrows'
|
|
||||||
| 'download'
|
|
||||||
| 'down'
|
|
||||||
| 'double-badge'
|
|
||||||
| 'document'
|
|
||||||
| 'diamond'
|
|
||||||
| 'delete'
|
|
||||||
| 'delete-user'
|
|
||||||
| 'delete-left'
|
|
||||||
| 'delete-filled'
|
|
||||||
| 'data'
|
|
||||||
| 'darkmode'
|
|
||||||
| 'crown-wear'
|
|
||||||
| 'crown-wear-outline'
|
|
||||||
| 'crown-take-off'
|
|
||||||
| 'crown-take-off-outline'
|
|
||||||
| 'crop'
|
|
||||||
| 'craft'
|
|
||||||
| 'copy'
|
|
||||||
| 'copy-media'
|
|
||||||
| 'comments'
|
|
||||||
| 'comments-sticker'
|
|
||||||
| 'combine-craft'
|
|
||||||
| 'colorize'
|
|
||||||
| 'collapse'
|
|
||||||
| 'collapse-modal'
|
|
||||||
| 'cloud-download'
|
|
||||||
| 'closed-gift'
|
|
||||||
| 'close'
|
|
||||||
| 'close-topic'
|
|
||||||
| 'close-circle'
|
|
||||||
| 'clock'
|
|
||||||
| 'clock-edit'
|
|
||||||
| 'check'
|
|
||||||
| 'chats-badge'
|
|
||||||
| 'chat-badge'
|
|
||||||
| 'channelviews'
|
|
||||||
| 'channel'
|
|
||||||
| 'channel-filled'
|
|
||||||
| 'cash-circle'
|
|
||||||
| 'card'
|
|
||||||
| 'car'
|
|
||||||
| 'camera'
|
|
||||||
| 'camera-add'
|
|
||||||
| 'calendar'
|
|
||||||
| 'calendar-filter'
|
|
||||||
| 'bug'
|
|
||||||
| 'brush'
|
|
||||||
| 'bots'
|
|
||||||
| 'bot-commands-filled'
|
|
||||||
| 'bot-command'
|
|
||||||
| 'boosts'
|
|
||||||
| 'boostcircle'
|
|
||||||
| 'boost'
|
|
||||||
| 'boost-outline'
|
|
||||||
| 'boost-craft-chance'
|
|
||||||
| 'bold'
|
|
||||||
| 'avatar-saved-messages'
|
|
||||||
| 'avatar-deleted-account'
|
|
||||||
| 'avatar-archived-chats'
|
|
||||||
| 'author-hidden'
|
|
||||||
| 'auction'
|
|
||||||
| 'auction-next-round'
|
|
||||||
| 'auction-filled'
|
|
||||||
| 'auction-drop'
|
|
||||||
| 'attach'
|
|
||||||
| 'ask-support'
|
|
||||||
| 'arrow-right'
|
|
||||||
| 'arrow-left'
|
|
||||||
| 'arrow-down'
|
|
||||||
| 'arrow-down-circle'
|
|
||||||
| 'archive'
|
|
||||||
| 'archive-to-main'
|
|
||||||
| 'archive-from-main'
|
|
||||||
| 'archive-filled'
|
|
||||||
| 'animations'
|
|
||||||
| 'animals'
|
|
||||||
| 'allow-speak'
|
|
||||||
| 'admin'
|
|
||||||
| 'add'
|
|
||||||
| 'add-user'
|
|
||||||
| 'add-user-filled'
|
|
||||||
| 'add-one-badge'
|
|
||||||
| 'add-filled'
|
|
||||||
| 'add-caption'
|
|
||||||
| 'active-sessions'
|
| 'active-sessions'
|
||||||
| 'rating-icons-negative'
|
| 'add-caption'
|
||||||
| 'rating-icons-level90'
|
| 'add-filled'
|
||||||
| 'rating-icons-level9'
|
| 'add-one-badge'
|
||||||
| 'rating-icons-level80'
|
| 'add-user-filled'
|
||||||
| 'rating-icons-level8'
|
| 'add-user'
|
||||||
| 'rating-icons-level70'
|
| 'add'
|
||||||
| 'rating-icons-level7'
|
| 'admin'
|
||||||
| 'rating-icons-level60'
|
| 'allow-speak'
|
||||||
| 'rating-icons-level6'
|
| 'animals'
|
||||||
| 'rating-icons-level50'
|
| 'animations'
|
||||||
| 'rating-icons-level5'
|
| 'archive-filled'
|
||||||
| 'rating-icons-level40'
|
| 'archive-from-main'
|
||||||
| 'rating-icons-level4'
|
| 'archive-to-main'
|
||||||
| 'rating-icons-level30'
|
| 'archive'
|
||||||
| 'rating-icons-level3'
|
| 'arrow-down-circle'
|
||||||
| 'rating-icons-level20'
|
| 'arrow-down'
|
||||||
| 'rating-icons-level2'
|
| 'arrow-left'
|
||||||
| 'rating-icons-level10'
|
| 'arrow-right'
|
||||||
| 'rating-icons-level1'
|
| 'ask-support'
|
||||||
| 'folder-tabs-user'
|
| 'attach'
|
||||||
| 'folder-tabs-star'
|
| 'auction-drop'
|
||||||
| 'folder-tabs-group'
|
| 'auction-filled'
|
||||||
| 'folder-tabs-folder'
|
| 'auction-next-round'
|
||||||
| 'folder-tabs-chats'
|
| 'auction'
|
||||||
| 'folder-tabs-chat'
|
| 'author-hidden'
|
||||||
|
| 'avatar-archived-chats'
|
||||||
|
| 'avatar-deleted-account'
|
||||||
|
| 'avatar-saved-messages'
|
||||||
|
| 'bold'
|
||||||
|
| 'boost-craft-chance'
|
||||||
|
| 'boost-outline'
|
||||||
|
| 'boost'
|
||||||
|
| 'boostcircle'
|
||||||
|
| 'boosts'
|
||||||
|
| 'bot-command'
|
||||||
|
| 'bot-commands-filled'
|
||||||
|
| 'bots'
|
||||||
|
| 'brush'
|
||||||
|
| 'bug'
|
||||||
|
| 'calendar-filter'
|
||||||
|
| 'calendar'
|
||||||
|
| 'camera-add'
|
||||||
|
| 'camera'
|
||||||
|
| 'car'
|
||||||
|
| 'card'
|
||||||
|
| 'cash-circle'
|
||||||
|
| 'channel-filled'
|
||||||
|
| 'channel'
|
||||||
|
| 'channelviews'
|
||||||
|
| 'chat-badge'
|
||||||
|
| 'chats-badge'
|
||||||
|
| 'check'
|
||||||
|
| 'clock-edit'
|
||||||
|
| 'clock'
|
||||||
|
| 'close-circle'
|
||||||
|
| 'close-topic'
|
||||||
|
| 'close'
|
||||||
|
| 'closed-gift'
|
||||||
|
| 'cloud-download'
|
||||||
|
| 'collapse-modal'
|
||||||
|
| 'collapse'
|
||||||
|
| 'colorize'
|
||||||
|
| 'combine-craft'
|
||||||
|
| 'comments-sticker'
|
||||||
|
| 'comments'
|
||||||
|
| 'copy-media'
|
||||||
|
| 'copy'
|
||||||
|
| 'craft'
|
||||||
|
| 'crop'
|
||||||
|
| 'crown-take-off-outline'
|
||||||
|
| 'crown-take-off'
|
||||||
|
| 'crown-wear-outline'
|
||||||
|
| 'crown-wear'
|
||||||
|
| 'darkmode'
|
||||||
|
| 'data'
|
||||||
|
| 'delete-filled'
|
||||||
|
| 'delete-left'
|
||||||
|
| 'delete-user'
|
||||||
|
| 'delete'
|
||||||
|
| 'diamond'
|
||||||
|
| 'document'
|
||||||
|
| 'double-badge'
|
||||||
|
| 'down'
|
||||||
|
| 'download'
|
||||||
|
| 'dropdown-arrows'
|
||||||
|
| 'eats'
|
||||||
|
| 'edit'
|
||||||
|
| 'email'
|
||||||
|
| 'enter'
|
||||||
|
| 'expand-modal'
|
||||||
|
| 'expand'
|
||||||
|
| 'eye-crossed-outline'
|
||||||
|
| 'eye-crossed'
|
||||||
|
| 'eye-outline'
|
||||||
|
| 'eye'
|
||||||
|
| 'favorite-filled'
|
||||||
|
| 'favorite'
|
||||||
|
| 'file-badge'
|
||||||
|
| 'flag'
|
||||||
|
| 'flip'
|
||||||
|
| 'folder-badge'
|
||||||
|
| 'folder-tabs-bot'
|
||||||
| 'folder-tabs-channel'
|
| 'folder-tabs-channel'
|
||||||
| 'folder-tabs-bot';
|
| 'folder-tabs-chat'
|
||||||
|
| 'folder-tabs-chats'
|
||||||
|
| 'folder-tabs-folder'
|
||||||
|
| 'folder-tabs-group'
|
||||||
|
| 'folder-tabs-star'
|
||||||
|
| 'folder-tabs-user'
|
||||||
|
| 'folder'
|
||||||
|
| 'fontsize'
|
||||||
|
| 'forums'
|
||||||
|
| 'forward'
|
||||||
|
| 'fragment'
|
||||||
|
| 'frozen-time'
|
||||||
|
| 'fullscreen'
|
||||||
|
| 'gifs'
|
||||||
|
| 'gift-transfer-inline'
|
||||||
|
| 'gift'
|
||||||
|
| 'group-filled'
|
||||||
|
| 'group'
|
||||||
|
| 'grouped-disable'
|
||||||
|
| 'grouped'
|
||||||
|
| 'hand-stop'
|
||||||
|
| 'hashtag'
|
||||||
|
| 'hd-photo'
|
||||||
|
| 'heart-outline'
|
||||||
|
| 'heart'
|
||||||
|
| 'help'
|
||||||
|
| 'info-filled'
|
||||||
|
| 'info'
|
||||||
|
| 'install'
|
||||||
|
| 'italic'
|
||||||
|
| 'key'
|
||||||
|
| 'keyboard'
|
||||||
|
| 'lamp'
|
||||||
|
| 'language'
|
||||||
|
| 'large-pause'
|
||||||
|
| 'large-play'
|
||||||
|
| 'link-badge'
|
||||||
|
| 'link-broken'
|
||||||
|
| 'link'
|
||||||
|
| 'location'
|
||||||
|
| 'lock-badge'
|
||||||
|
| 'lock'
|
||||||
|
| 'logout'
|
||||||
|
| 'loop'
|
||||||
|
| 'mention'
|
||||||
|
| 'menu'
|
||||||
|
| 'message-failed'
|
||||||
|
| 'message-pending'
|
||||||
|
| 'message-read'
|
||||||
|
| 'message-succeeded'
|
||||||
|
| 'message'
|
||||||
|
| 'microphone-alt'
|
||||||
|
| 'microphone'
|
||||||
|
| 'monospace'
|
||||||
|
| 'more-circle'
|
||||||
|
| 'more'
|
||||||
|
| 'move-caption-down'
|
||||||
|
| 'move-caption-up'
|
||||||
|
| 'mute'
|
||||||
|
| 'muted'
|
||||||
|
| 'my-notes'
|
||||||
|
| 'new-chat-filled'
|
||||||
|
| 'next-link'
|
||||||
|
| 'next'
|
||||||
|
| 'nochannel'
|
||||||
|
| 'noise-suppression'
|
||||||
|
| 'non-contacts'
|
||||||
|
| 'note'
|
||||||
|
| 'one-filled'
|
||||||
|
| 'open-in-new-tab'
|
||||||
|
| 'password-off'
|
||||||
|
| 'pause'
|
||||||
|
| 'permissions'
|
||||||
|
| 'phone-discard-outline'
|
||||||
|
| 'phone-discard'
|
||||||
|
| 'phone'
|
||||||
|
| 'photo'
|
||||||
|
| 'pin-badge'
|
||||||
|
| 'pin-list'
|
||||||
|
| 'pin'
|
||||||
|
| 'pinned-chat'
|
||||||
|
| 'pinned-message'
|
||||||
|
| 'pip'
|
||||||
|
| 'play-story'
|
||||||
|
| 'play'
|
||||||
|
| 'poll'
|
||||||
|
| 'previous'
|
||||||
|
| 'privacy-policy'
|
||||||
|
| 'proof-of-ownership'
|
||||||
|
| 'quote-text'
|
||||||
|
| 'quote'
|
||||||
|
| 'radial-badge'
|
||||||
|
| 'rating-icons-level1'
|
||||||
|
| 'rating-icons-level10'
|
||||||
|
| 'rating-icons-level2'
|
||||||
|
| 'rating-icons-level20'
|
||||||
|
| 'rating-icons-level3'
|
||||||
|
| 'rating-icons-level30'
|
||||||
|
| 'rating-icons-level4'
|
||||||
|
| 'rating-icons-level40'
|
||||||
|
| 'rating-icons-level5'
|
||||||
|
| 'rating-icons-level50'
|
||||||
|
| 'rating-icons-level6'
|
||||||
|
| 'rating-icons-level60'
|
||||||
|
| 'rating-icons-level7'
|
||||||
|
| 'rating-icons-level70'
|
||||||
|
| 'rating-icons-level8'
|
||||||
|
| 'rating-icons-level80'
|
||||||
|
| 'rating-icons-level9'
|
||||||
|
| 'rating-icons-level90'
|
||||||
|
| 'rating-icons-negative'
|
||||||
|
| 'readchats'
|
||||||
|
| 'recent'
|
||||||
|
| 'redo'
|
||||||
|
| 'refund'
|
||||||
|
| 'reload'
|
||||||
|
| 'remove-quote'
|
||||||
|
| 'remove'
|
||||||
|
| 'reopen-topic'
|
||||||
|
| 'reorder-tabs'
|
||||||
|
| 'replace'
|
||||||
|
| 'replies'
|
||||||
|
| 'reply-filled'
|
||||||
|
| 'reply'
|
||||||
|
| 'revenue-split'
|
||||||
|
| 'revote'
|
||||||
|
| 'rotate'
|
||||||
|
| 'save-story'
|
||||||
|
| 'saved-messages'
|
||||||
|
| 'schedule'
|
||||||
|
| 'scheduled'
|
||||||
|
| 'sd-photo'
|
||||||
|
| 'search'
|
||||||
|
| 'select'
|
||||||
|
| 'sell-outline'
|
||||||
|
| 'sell'
|
||||||
|
| 'send-outline'
|
||||||
|
| 'send'
|
||||||
|
| 'settings-filled'
|
||||||
|
| 'settings'
|
||||||
|
| 'share-filled'
|
||||||
|
| 'share-screen-outlined'
|
||||||
|
| 'share-screen-stop'
|
||||||
|
| 'share-screen'
|
||||||
|
| 'show-message'
|
||||||
|
| 'sidebar'
|
||||||
|
| 'skip-next'
|
||||||
|
| 'skip-previous'
|
||||||
|
| 'smallscreen'
|
||||||
|
| 'smile'
|
||||||
|
| 'sort-by-date'
|
||||||
|
| 'sort-by-number'
|
||||||
|
| 'sort-by-price'
|
||||||
|
| 'sort'
|
||||||
|
| 'speaker-muted-story'
|
||||||
|
| 'speaker-outline'
|
||||||
|
| 'speaker-story'
|
||||||
|
| 'speaker'
|
||||||
|
| 'spoiler-disable'
|
||||||
|
| 'spoiler'
|
||||||
|
| 'sport'
|
||||||
|
| 'star'
|
||||||
|
| 'stars-lock'
|
||||||
|
| 'stars-refund'
|
||||||
|
| 'stats'
|
||||||
|
| 'stealth-future'
|
||||||
|
| 'stealth-past'
|
||||||
|
| 'stickers'
|
||||||
|
| 'stop-raising-hand'
|
||||||
|
| 'stop'
|
||||||
|
| 'story-caption'
|
||||||
|
| 'story-expired'
|
||||||
|
| 'story-priority'
|
||||||
|
| 'story-reply'
|
||||||
|
| 'strikethrough'
|
||||||
|
| 'tag-add'
|
||||||
|
| 'tag-crossed'
|
||||||
|
| 'tag-filter'
|
||||||
|
| 'tag-name'
|
||||||
|
| 'tag'
|
||||||
|
| 'timer'
|
||||||
|
| 'toncoin'
|
||||||
|
| 'tools'
|
||||||
|
| 'topic-new'
|
||||||
|
| 'trade'
|
||||||
|
| 'transcribe'
|
||||||
|
| 'truck'
|
||||||
|
| 'unarchive'
|
||||||
|
| 'underlined'
|
||||||
|
| 'understood'
|
||||||
|
| 'undo'
|
||||||
|
| 'unique-profile'
|
||||||
|
| 'unlist-outline'
|
||||||
|
| 'unlist'
|
||||||
|
| 'unlock-badge'
|
||||||
|
| 'unlock'
|
||||||
|
| 'unmute'
|
||||||
|
| 'unpin'
|
||||||
|
| 'unread'
|
||||||
|
| 'up'
|
||||||
|
| 'user-filled'
|
||||||
|
| 'user-online'
|
||||||
|
| 'user-stars'
|
||||||
|
| 'user-tag'
|
||||||
|
| 'user'
|
||||||
|
| 'video-outlined'
|
||||||
|
| 'video-stop'
|
||||||
|
| 'video'
|
||||||
|
| 'view-once'
|
||||||
|
| 'voice-chat'
|
||||||
|
| 'volume-1'
|
||||||
|
| 'volume-2'
|
||||||
|
| 'volume-3'
|
||||||
|
| 'warning'
|
||||||
|
| 'web'
|
||||||
|
| 'webapp'
|
||||||
|
| 'word-wrap'
|
||||||
|
| 'zoom-in'
|
||||||
|
| 'zoom-out';
|
||||||
|
|||||||
31
src/types/language.d.ts
vendored
31
src/types/language.d.ts
vendored
@ -112,6 +112,7 @@ export interface LangPair {
|
|||||||
'UserRestrictionsNoChangeInfo': undefined;
|
'UserRestrictionsNoChangeInfo': undefined;
|
||||||
'UserRestrictionsInviteUsers': undefined;
|
'UserRestrictionsInviteUsers': undefined;
|
||||||
'UserRestrictionsPinMessages': undefined;
|
'UserRestrictionsPinMessages': undefined;
|
||||||
|
'UserRestrictionsEditRank': undefined;
|
||||||
'ChatPermissionNotAvailable': undefined;
|
'ChatPermissionNotAvailable': undefined;
|
||||||
'StatsMessageInteractionsTitle': undefined;
|
'StatsMessageInteractionsTitle': undefined;
|
||||||
'StatsGroupGrowthTitle': undefined;
|
'StatsGroupGrowthTitle': undefined;
|
||||||
@ -490,6 +491,7 @@ export interface LangPair {
|
|||||||
'PrivacyExceptions': undefined;
|
'PrivacyExceptions': undefined;
|
||||||
'AlwaysAllow': undefined;
|
'AlwaysAllow': undefined;
|
||||||
'EditAdminAddUsers': undefined;
|
'EditAdminAddUsers': undefined;
|
||||||
|
'EditAdminEditRank': undefined;
|
||||||
'NeverAllow': undefined;
|
'NeverAllow': undefined;
|
||||||
'AlwaysAllowPlaceholder': undefined;
|
'AlwaysAllowPlaceholder': undefined;
|
||||||
'NeverAllowPlaceholder': undefined;
|
'NeverAllowPlaceholder': undefined;
|
||||||
@ -2004,6 +2006,17 @@ export interface LangPair {
|
|||||||
'GiftPreviewToggleRegularModels': undefined;
|
'GiftPreviewToggleRegularModels': undefined;
|
||||||
'AriaGiftPreviewPlay': undefined;
|
'AriaGiftPreviewPlay': undefined;
|
||||||
'AriaGiftPreviewStop': undefined;
|
'AriaGiftPreviewStop': undefined;
|
||||||
|
'RankModalEdit': undefined;
|
||||||
|
'RankModalEditMy': undefined;
|
||||||
|
'MemberContextEditRank': undefined;
|
||||||
|
'RankMemberTag': undefined;
|
||||||
|
'RankAdminTag': undefined;
|
||||||
|
'RankOwnerTag': undefined;
|
||||||
|
'RankModalMemberTagTitle': undefined;
|
||||||
|
'RankModalAdminTagTitle': undefined;
|
||||||
|
'RankModalOwnerTagTitle': undefined;
|
||||||
|
'RankEditSave': undefined;
|
||||||
|
'RankEditTextOwn': undefined;
|
||||||
'MenuAddCaption': undefined;
|
'MenuAddCaption': undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3519,6 +3532,24 @@ export interface LangPairWithVariables<V = LangVariable> {
|
|||||||
'BotAuthSuccessText': {
|
'BotAuthSuccessText': {
|
||||||
'url': V;
|
'url': V;
|
||||||
};
|
};
|
||||||
|
'RankModalMemberText': {
|
||||||
|
'tag': V;
|
||||||
|
'author': V;
|
||||||
|
'group': V;
|
||||||
|
};
|
||||||
|
'RankModalAdminText': {
|
||||||
|
'tag': V;
|
||||||
|
'author': V;
|
||||||
|
'group': V;
|
||||||
|
};
|
||||||
|
'RankModalOwnerText': {
|
||||||
|
'tag': V;
|
||||||
|
'author': V;
|
||||||
|
'group': V;
|
||||||
|
};
|
||||||
|
'RankEditText': {
|
||||||
|
'user': V;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LangPairPlural {
|
export interface LangPairPlural {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user