Support layer 201 (#5794)

This commit is contained in:
Alexander Zinchuk 2025-04-23 18:59:21 +02:00
parent 10cfa5f22c
commit 92d9f0736c
73 changed files with 1406 additions and 331 deletions

View File

@ -92,6 +92,9 @@ export interface GramJsAppConfig extends LimitsConfig {
stars_paid_message_commission_permille?: number;
stars_paid_message_amount_max?: number;
stargifts_pinned_to_top_limit?: number;
freeze_since_date?: number;
freeze_until_date?: number;
freeze_appeal_url?: string;
}
function buildEmojiSounds(appConfig: GramJsAppConfig) {
@ -185,5 +188,8 @@ export function buildAppConfig(json: GramJs.TypeJSONValue, hash: number): ApiApp
starRefStartPrefixes: appConfig.starref_start_param_prefixes,
tonExplorerUrl: appConfig.ton_blockchain_explorer_url,
savedGiftPinLimit: appConfig.stargifts_pinned_to_top_limit,
freezeSinceDate: appConfig.freeze_since_date,
freezeUntilDate: appConfig.freeze_until_date,
freezeAppealUrl: appConfig.freeze_appeal_url,
};
}

View File

@ -413,6 +413,27 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
savedId: savedId && buildApiPeerId(savedId, 'user'),
};
}
if (action instanceof GramJs.MessageActionPaidMessagesPrice) {
const {
stars,
} = action;
return {
mediaType: 'action',
type: 'paidMessagesPrice',
stars: stars.toJSNumber(),
};
}
if (action instanceof GramJs.MessageActionPaidMessagesRefunded) {
const {
stars, count,
} = action;
return {
mediaType: 'action',
type: 'paidMessagesRefunded',
stars: stars.toJSNumber(),
count,
};
}
return UNSUPPORTED_ACTION;
}

View File

@ -307,6 +307,12 @@ export async function invokeRequest<T extends GramJs.AnyRequest>(
console.error(err);
}
const message = err instanceof RPCError ? err.errorMessage : err.message;
if (message.includes('FROZEN_METHOD_INVALID')) {
dispatchNotSupportedInFrozenAccountUpdate(err, request);
}
if (shouldThrow) {
throw err;
}
@ -441,6 +447,27 @@ export function dispatchErrorUpdate<T extends GramJs.AnyRequest>(err: Error, req
});
}
function dispatchNotSupportedInFrozenAccountUpdate<T extends GramJs.AnyRequest>(err: Error, request: T) {
if (!(err instanceof RPCError)) return;
const message = err.errorMessage;
if (
request instanceof GramJs.messages.GetPinnedDialogs
|| request instanceof GramJs.phone.GetGroupParticipants
|| request instanceof GramJs.channels.GetParticipant
|| request instanceof GramJs.channels.GetParticipants
|| request instanceof GramJs.channels.GetForumTopics) {
return;
}
sendApiUpdate({
'@type': 'notSupportedInFrozenAccount',
error: {
message,
},
});
}
async function handleTerminatedSession() {
try {
await invokeRequest(new GramJs.users.GetFullUser({

View File

@ -1775,20 +1775,17 @@ export async function fetchSponsoredMessages({ peer }: { peer: ApiPeer }) {
};
}
export async function viewSponsoredMessage({ peer, random }: { peer: ApiPeer; random: string }) {
export async function viewSponsoredMessage({ random }: { random: string }) {
await invokeRequest(new GramJs.messages.ViewSponsoredMessage({
peer: buildInputPeer(peer.id, peer.accessHash),
randomId: deserializeBytes(random),
}));
}
export function clickSponsoredMessage({
peer,
random,
isMedia,
isFullscreen,
}: {
peer: ApiPeer;
random: string;
isMedia?: boolean;
isFullscreen?: boolean;
@ -1796,23 +1793,19 @@ export function clickSponsoredMessage({
return invokeRequest(new GramJs.messages.ClickSponsoredMessage({
media: isMedia || undefined,
fullscreen: isFullscreen || undefined,
peer: buildInputPeer(peer.id, peer.accessHash),
randomId: deserializeBytes(random),
}));
}
export async function reportSponsoredMessage({
peer,
randomId,
option,
}: {
peer: ApiPeer;
randomId: string;
option: string;
}) {
try {
const result = await invokeRequest(new GramJs.messages.ReportSponsoredMessage({
peer: buildInputPeer(peer.id, peer.accessHash),
randomId: deserializeBytes(randomId),
option: deserializeBytes(option),
}), {

View File

@ -268,6 +268,17 @@ export interface ApiMessageActionExpiredContent extends ActionMediaType {
isRoundVideo?: true;
}
export interface ApiMessageActionPaidMessagesRefunded extends ActionMediaType {
type: 'paidMessagesRefunded';
count:number;
stars:number;
}
export interface ApiMessageActionPaidMessagesPrice extends ActionMediaType {
type: 'paidMessagesPrice';
stars:number;
}
export interface ApiMessageActionUnsupported extends ActionMediaType {
type: 'unsupported';
}
@ -284,4 +295,5 @@ export type ApiMessageAction = ApiMessageActionUnsupported | ApiMessageActionCha
| ApiMessageActionTopicCreate | ApiMessageActionTopicEdit | ApiMessageActionSuggestProfilePhoto
| ApiMessageActionChannelJoined | ApiMessageActionGiftCode | ApiMessageActionGiveawayLaunch
| ApiMessageActionGiveawayResults | ApiMessageActionPaymentRefunded | ApiMessageActionGiftStars
| ApiMessageActionPrizeStars | ApiMessageActionStarGift | ApiMessageActionStarGiftUnique;
| ApiMessageActionPrizeStars | ApiMessageActionStarGift | ApiMessageActionStarGiftUnique
| ApiMessageActionPaidMessagesRefunded | ApiMessageActionPaidMessagesPrice;

View File

@ -243,6 +243,9 @@ export interface ApiAppConfig {
starRefStartPrefixes?: string[];
tonExplorerUrl?: string;
savedGiftPinLimit?: number;
freezeSinceDate?: number;
freezeUntilDate?: number;
freezeAppealUrl?: string;
}
export interface ApiConfig {

View File

@ -463,6 +463,11 @@ export type ApiUpdateError = {
error: ApiError;
};
export type ApiUpdateNotSupportedInFrozenAccountError = {
'@type': 'notSupportedInFrozenAccount';
error: ApiError;
};
export type ApiUpdateConfig = {
'@type': 'updateConfig';
};
@ -855,7 +860,7 @@ export type ApiUpdate = (
ApiUpdateDeleteSavedHistory | ApiUpdatePremiumFloodWait | ApiUpdateStarsBalance |
ApiUpdateQuickReplyMessage | ApiUpdateQuickReplies | ApiDeleteQuickReply | ApiUpdateDeleteQuickReplyMessages |
ApiUpdateDeleteProfilePhoto | ApiUpdateNewProfilePhoto | ApiUpdateEntities | ApiUpdatePaidReactionPrivacy |
ApiUpdateLangPackTooLong | ApiUpdateLangPack
ApiUpdateLangPackTooLong | ApiUpdateLangPack | ApiUpdateNotSupportedInFrozenAccountError
);
export type OnApiUpdate = (update: ApiUpdate) => void;

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><path fill="#000" d="M19.58 16C23.868 14.353 27 10.153 27 5.294A3.306 3.306 0 0 0 23.703 2H7.297A3.305 3.305 0 0 0 4 5.294C4 10.153 7.05 14.353 11.42 16 7.132 17.647 4 21.847 4 26.706A3.306 3.306 0 0 0 7.297 30h16.406A3.306 3.306 0 0 0 27 26.706c0-4.859-3.133-9.059-7.42-10.706M5.65 5.294c0-.906.742-1.647 1.648-1.647h16.406c.906 0 1.648.741 1.648 1.647 0 5.435-4.451 9.8-9.81 9.8-5.358 0-9.892-4.365-9.892-9.8m18.054 23.059H7.297a1.653 1.653 0 0 1-1.648-1.647c0-5.435 4.451-9.8 9.81-9.8 5.358 0 9.892 4.365 9.892 9.8 0 .906-.742 1.647-1.648 1.647"/><path fill="#000" d="m9.688 24.235.824 1.4 4.947-3.13 5.029 3.13.824-1.4-5.853-3.623zM19.498 9l.99.741.989-1.317-.99-.742c-1.154-.906-2.802-.906-4.039 0-.577.494-1.401.494-2.06 0a3.47 3.47 0 0 0-4.123 0l-.824.742.99 1.317.906-.741a1.62 1.62 0 0 1 2.06 0c1.155.906 2.886.906 4.04 0 .66-.412 1.484-.412 2.061 0"/></svg>

After

Width:  |  Height:  |  Size: 942 B

View File

@ -1902,6 +1902,25 @@
"PaidMessageTransaction_other" = "Fee for {count} Messages";
"PaidMessageTransactionDescription" = "You receive **{percent}** of the price that you charge for each incoming message.";
"PaidMessageTransactionTotal" = "Total";
"TitleFrozenAccount" = "Your account is frozen!";
"SubtitleFrozenAccount" = "Tap to view details and submit an appeal.";
"ComposerTitleFrozenAccount" = "Your account is frozen";
"ComposerSubtitleFrozenAccount" = "Tap to view details";
"DescriptionRestrictedMedia" = "Posting media content is not allowed in this group.";
"DescriptionScheduledPaidMediaNotAllowed" = "Posting scheduled paid media content is not allowed";
"DescriptionScheduledPaidMessagesNotAllowed" = "Scheduled paid messages is not allowed";
"FrozenAccountModalTitle" = "Your Account is Frozen";
"FrozenAccountViolationTitle" = "Violation of Terms";
"FrozenAccountViolationSubtitle" = "Your account was frozen for breaking Telegram's Terms and Conditions";
"FrozenAccountReadOnlyTitle" = "Read-Only Mode";
"FrozenAccountReadOnlySubtitle" = "You can access your account but can't send messages or take actions.";
"FrozenAccountAppealTitle" = "Appeal Before Deactivation";
"FrozenAccountAppealSubtitle" = "Appeal via {botLink} before {date} or your account will be deleted.";
"ButtonAppeal" = "Submit an Appeal";
"ButtonUnderstood" = "Understood";
"ActionPaidMessageGroupPrice" = "Messages now cost **{stars}** in this group";
"ActionPaidMessageGroupPriceFree" = "Messages are now free in this group";
"ApiMessageActionPaidMessagesRefundedOutgoing" = "You refunded **{stars}** to {user}";
"ApiMessageActionPaidMessagesRefundedIncoming" = "{user} refunded **{stars}** to you";
"NotificationTitleNotSupportedInFrozenAccount" = "Your account is frozen";
"NotificationMessageNotSupportedInFrozenAccount" = "This action is not available";

Binary file not shown.

View File

@ -97,3 +97,4 @@ export { default as ReceiptModal } from '../components/payment/ReceiptModal';
export { default as InviteViaLinkModal } from '../components/modals/inviteViaLink/InviteViaLinkModal';
export { default as OneTimeMediaModal } from '../components/modals/oneTimeMedia/OneTimeMediaModal';
export { default as WebAppsCloseConfirmationModal } from '../components/main/WebAppsCloseConfirmationModal';
export { default as FrozenAccountModal } from '../components/modals/frozenAccount/FrozenAccountModal';

View File

@ -82,6 +82,7 @@ import {
selectEditingMessage,
selectEditingScheduledDraft,
selectIsChatWithSelf,
selectIsCurrentUserFrozen,
selectIsCurrentUserPremium,
selectIsInSelectMode,
selectIsPremiumPurchaseBlocked,
@ -291,6 +292,8 @@ type StateProps =
isPaymentMessageConfirmDialogOpen: boolean;
starsBalance: number;
isStarsBalanceModalOpen: boolean;
isAccountFrozen?: boolean;
isAppConfigLoaded?: boolean;
};
enum MainButtonState {
@ -411,6 +414,8 @@ const Composer: FC<OwnProps & StateProps> = ({
isPaymentMessageConfirmDialogOpen,
starsBalance,
isStarsBalanceModalOpen,
isAccountFrozen,
isAppConfigLoaded,
}) => {
const {
sendMessage,
@ -499,10 +504,10 @@ const Composer: FC<OwnProps & StateProps> = ({
}, [chatId]);
useEffect(() => {
if (chatId && isReady && !isInStoryViewer) {
if (isAppConfigLoaded && chatId && isReady && !isInStoryViewer) {
loadScheduledHistory({ chatId });
}
}, [isReady, chatId, threadId, isInStoryViewer]);
}, [isReady, chatId, threadId, isInStoryViewer, isAppConfigLoaded]);
useEffect(() => {
const isChannelWithProfiles = isChannel && chat?.areProfilesShown;
@ -1960,7 +1965,7 @@ const Composer: FC<OwnProps & StateProps> = ({
)}
</>
)}
{((!isComposerBlocked || canSendGifs || canSendStickers) && !isNeedPremium) && (
{((!isComposerBlocked || canSendGifs || canSendStickers) && !isNeedPremium && !isAccountFrozen) && (
<SymbolMenuButton
chatId={chatId}
threadId={threadId}
@ -2373,6 +2378,8 @@ export default memo(withGlobal<OwnProps>(
const isForwarding = chatId === tabState.forwardMessages.toChatId;
const starsBalance = global.stars?.balance.amount || 0;
const isStarsBalanceModalOpen = Boolean(tabState.starsBalanceModal);
const isAccountFrozen = selectIsCurrentUserFrozen(global);
const isAppConfigLoaded = global.isAppConfigLoaded;
return {
availableReactions: global.reactions.availableReactions,
@ -2457,6 +2464,8 @@ export default memo(withGlobal<OwnProps>(
isPaymentMessageConfirmDialogOpen: tabState.isPaymentMessageConfirmDialogOpen,
starsBalance,
isStarsBalanceModalOpen,
isAccountFrozen,
isAppConfigLoaded,
};
},
)(Composer));

View File

@ -1,4 +1,5 @@
import QrPlane from '../../../assets/tgs/auth/QrPlane.tgs';
import BannedDuck from '../../../assets/tgs/BannedDuck.tgs';
import CameraFlip from '../../../assets/tgs/calls/CameraFlip.tgs';
import HandFilled from '../../../assets/tgs/calls/HandFilled.tgs';
import HandOutline from '../../../assets/tgs/calls/HandOutline.tgs';
@ -66,4 +67,5 @@ export const LOCAL_TGS_URLS = {
StarReaction,
Report,
SearchingDuck,
BannedDuck,
};

View File

@ -9,7 +9,9 @@ import type { FoldersActions } from '../../hooks/reducers/useFoldersReducer';
import type { ReducerAction } from '../../hooks/useReducer';
import { LeftColumnContent, SettingsScreens } from '../../types';
import { selectCurrentChat, selectIsForumPanelOpen, selectTabState } from '../../global/selectors';
import {
selectCurrentChat, selectIsCurrentUserFrozen, selectIsForumPanelOpen, selectTabState,
} from '../../global/selectors';
import {
IS_APP, IS_FIREFOX, IS_MAC_OS, IS_TOUCH_ENV, LAYERS_ANIMATION_NAME,
} from '../../util/browser/windowEnvironment';
@ -52,6 +54,7 @@ type StateProps = {
isClosingSearch?: boolean;
archiveSettings: GlobalState['archiveSettings'];
isArchivedStoryRibbonShown?: boolean;
isAccountFrozen?: boolean;
};
enum ContentType {
@ -86,6 +89,7 @@ function LeftColumn({
isClosingSearch,
archiveSettings,
isArchivedStoryRibbonShown,
isAccountFrozen,
}: OwnProps & StateProps) {
const {
setGlobalSearchQuery,
@ -545,6 +549,7 @@ function LeftColumn({
isElectronUpdateAvailable={isElectronUpdateAvailable}
isForumPanelOpen={isForumPanelOpen}
onTopicSearch={handleTopicSearch}
isAccountFrozen={isAccountFrozen}
/>
);
}
@ -598,6 +603,7 @@ export default memo(withGlobal<OwnProps>(
const isChatOpen = Boolean(currentChat?.id);
const isForumPanelOpen = selectIsForumPanelOpen(global);
const forumPanelChatId = tabState.forumPanelChatId;
const isAccountFrozen = selectIsCurrentUserFrozen(global);
return {
searchQuery: query,
@ -616,6 +622,7 @@ export default memo(withGlobal<OwnProps>(
isClosingSearch: tabState.globalSearch.isClosing,
archiveSettings,
isArchivedStoryRibbonShown: isArchivedRibbonShown,
isAccountFrozen,
};
},
)(LeftColumn));

View File

@ -2,6 +2,7 @@ import type { FC } from '../../lib/teact/teact';
import React, {
useCallback, useEffect, useMemo, useState,
} from '../../lib/teact/teact';
import { getActions } from '../../global';
import buildClassName from '../../util/buildClassName';
@ -19,6 +20,7 @@ type OwnProps = {
onNewPrivateChat: () => void;
onNewChannel: () => void;
onNewGroup: () => void;
isAccountFrozen?: boolean;
};
const NewChatButton: FC<OwnProps> = ({
@ -26,8 +28,10 @@ const NewChatButton: FC<OwnProps> = ({
onNewPrivateChat,
onNewChannel,
onNewGroup,
isAccountFrozen,
}) => {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const { openFrozenAccountModal } = getActions();
useEffect(() => {
if (!isShown) {
@ -44,8 +48,12 @@ const NewChatButton: FC<OwnProps> = ({
);
const toggleIsMenuOpen = useCallback(() => {
if (isAccountFrozen) {
openFrozenAccountModal();
return;
}
setIsMenuOpen(!isMenuOpen);
}, [isMenuOpen]);
}, [isMenuOpen, isAccountFrozen]);
const handleClose = useCallback(() => {
setIsMenuOpen(false);

View File

@ -33,6 +33,7 @@ import {
selectChatMessage,
selectCurrentMessageList,
selectDraft,
selectIsCurrentUserFrozen,
selectIsForumPanelClosed,
selectIsForumPanelOpen,
selectNotifyDefaults,
@ -114,6 +115,7 @@ type StateProps = {
lastMessage?: ApiMessage;
currentUserId: string;
isSynced?: boolean;
isAccountFrozen?: boolean;
};
const Chat: FC<OwnProps & StateProps> = ({
@ -151,6 +153,7 @@ const Chat: FC<OwnProps & StateProps> = ({
className,
isSynced,
onDragEnter,
isAccountFrozen,
}) => {
const {
openChat,
@ -163,6 +166,7 @@ const Chat: FC<OwnProps & StateProps> = ({
closeForumPanel,
setShouldCloseRightColumn,
reportMessages,
openFrozenAccountModal,
} = getActions();
const { isMobile } = useAppLayout();
@ -248,11 +252,21 @@ const Chat: FC<OwnProps & StateProps> = ({
});
const handleDelete = useLastCallback(() => {
if (isAccountFrozen) {
openFrozenAccountModal();
return;
}
markRenderDeleteModal();
openDeleteModal();
});
const handleMute = useLastCallback(() => {
if (isAccountFrozen) {
openFrozenAccountModal();
return;
}
markRenderMuteModal();
openMuteModal();
});
@ -263,6 +277,11 @@ const Chat: FC<OwnProps & StateProps> = ({
});
const handleReport = useLastCallback(() => {
if (isAccountFrozen) {
openFrozenAccountModal();
return;
}
if (!chat) return;
reportMessages({ chatId: chat.id, messageIds: [] });
});
@ -468,6 +487,7 @@ export default memo(withGlobal<OwnProps>(
const storyData = lastMessage?.content.storyData;
const lastMessageStory = storyData && selectPeerStory(global, storyData.peerId, storyData.id);
const isAccountFrozen = selectIsCurrentUserFrozen(global);
return {
chat,
@ -494,6 +514,7 @@ export default memo(withGlobal<OwnProps>(
topics: topicsInfo?.topicsById,
isSynced: global.isSynced,
lastMessageStory,
isAccountFrozen,
};
},
)(Chat));

View File

@ -13,7 +13,7 @@ import type { TabWithProperties } from '../../ui/TabList';
import { SettingsScreens } from '../../../types';
import { ALL_FOLDER_ID } from '../../../config';
import { selectCanShareFolder, selectTabState } from '../../../global/selectors';
import { selectCanShareFolder, selectIsCurrentUserFrozen, selectTabState } from '../../../global/selectors';
import { selectCurrentLimit } from '../../../global/selectors/limits';
import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
import buildClassName from '../../../util/buildClassName';
@ -60,6 +60,7 @@ type StateProps = {
archiveSettings: GlobalState['archiveSettings'];
isStoryRibbonShown?: boolean;
sessions?: Record<string, ApiSession>;
isAccountFrozen?: boolean;
};
const SAVED_MESSAGES_HOTKEY = '0';
@ -85,6 +86,7 @@ const ChatFolders: FC<OwnProps & StateProps> = ({
archiveSettings,
isStoryRibbonShown,
sessions,
isAccountFrozen,
}) => {
const {
loadChatFolders,
@ -368,6 +370,7 @@ const ChatFolders: FC<OwnProps & StateProps> = ({
canDisplayArchive={(hasArchivedChats || hasArchivedStories) && !archiveSettings.isHidden}
archiveSettings={archiveSettings}
sessions={sessions}
isAccountFrozen={isAccountFrozen}
/>
);
}
@ -432,6 +435,7 @@ export default memo(withGlobal<OwnProps>(
} = global;
const { shouldSkipHistoryAnimations, activeChatFolder } = selectTabState(global);
const { storyViewer: { isRibbonShown: isStoryRibbonShown } } = selectTabState(global);
const isAccountFrozen = selectIsCurrentUserFrozen(global);
return {
chatFoldersById,
@ -448,6 +452,7 @@ export default memo(withGlobal<OwnProps>(
archiveSettings,
isStoryRibbonShown,
sessions,
isAccountFrozen,
};
},
)(ChatFolders));

View File

@ -39,6 +39,7 @@ import Loading from '../../ui/Loading';
import Archive from './Archive';
import Chat from './Chat';
import EmptyFolder from './EmptyFolder';
import FrozenAccountNotification from './FrozenAccountNotification';
import UnconfirmedSession from './UnconfirmedSession';
type OwnProps = {
@ -53,6 +54,7 @@ type OwnProps = {
foldersDispatch?: FolderEditDispatch;
onSettingsScreenSelect?: (screen: SettingsScreens) => void;
onLeftColumnContentChange?: (content: LeftColumnContent) => void;
isAccountFrozen?: boolean;
};
const INTERSECTION_THROTTLE = 200;
@ -71,12 +73,14 @@ const ChatList: FC<OwnProps> = ({
foldersDispatch,
onSettingsScreenSelect,
onLeftColumnContentChange,
isAccountFrozen,
}) => {
const {
openChat,
openNextChat,
closeForumPanel,
toggleStoryRibbon,
openFrozenAccountModal,
} = getActions();
// eslint-disable-next-line no-null/no-null
const containerRef = useRef<HTMLDivElement>(null);
@ -91,6 +95,7 @@ const ChatList: FC<OwnProps> = ({
);
const shouldDisplayArchive = isAllFolder && canDisplayArchive && archiveSettings;
const shouldShowFrozenAccountNotification = isAccountFrozen && isAllFolder;
const orderedIds = useFolderManagerForOrderedIds(resolvedFolderId);
usePeerStoriesPolling(orderedIds);
@ -98,6 +103,7 @@ const ChatList: FC<OwnProps> = ({
const chatsHeight = (orderedIds?.length || 0) * CHAT_HEIGHT_PX;
const archiveHeight = shouldDisplayArchive
? archiveSettings?.isMinimized ? ARCHIVE_MINIMIZED_HEIGHT : CHAT_HEIGHT_PX : 0;
const frozenNotificationHeight = shouldShowFrozenAccountNotification ? 68 : 0;
const { orderDiffById, getAnimationType } = useOrderDiff(orderedIds);
@ -108,8 +114,8 @@ const ChatList: FC<OwnProps> = ({
const current = sessionsArray.find((session) => session.isCurrent);
if (!current || getServerTime() - current.dateCreated < FRESH_AUTH_PERIOD) return false;
return isAllFolder && sessionsArray.some((session) => session.isUnconfirmed);
}, [isAllFolder, sessions]);
return !isAccountFrozen && isAllFolder && sessionsArray.some((session) => session.isUnconfirmed);
}, [isAllFolder, sessions, isAccountFrozen]);
useEffect(() => {
if (!shouldShowUnconfirmedSessions) setUnconfirmedSessionHeight(0);
@ -174,6 +180,10 @@ const ChatList: FC<OwnProps> = ({
closeForumPanel();
});
const handleFrozenAccountNotificationClick = useLastCallback(() => {
openFrozenAccountModal();
});
const handleArchivedDragEnter = useLastCallback(() => {
if (shouldIgnoreDragRef.current) {
shouldIgnoreDragRef.current = false;
@ -215,7 +225,8 @@ const ChatList: FC<OwnProps> = ({
return viewportIds!.map((id, i) => {
const isPinned = viewportOffset + i < pinnedCount;
const offsetTop = unconfirmedSessionHeight + archiveHeight + (viewportOffset + i) * CHAT_HEIGHT_PX;
const offsetTop = unconfirmedSessionHeight + archiveHeight + frozenNotificationHeight
+ (viewportOffset + i) * CHAT_HEIGHT_PX;
return (
<Chat
@ -244,7 +255,7 @@ const ChatList: FC<OwnProps> = ({
preloadBackwards={CHAT_LIST_SLICE}
withAbsolutePositioning
beforeChildren={renderedOverflowTrigger}
maxHeight={chatsHeight + archiveHeight + unconfirmedSessionHeight}
maxHeight={chatsHeight + archiveHeight + frozenNotificationHeight + unconfirmedSessionHeight}
onLoadMore={getMore}
onDragLeave={handleDragLeave}
>
@ -255,6 +266,12 @@ const ChatList: FC<OwnProps> = ({
onHeightChange={setUnconfirmedSessionHeight}
/>
)}
{shouldShowFrozenAccountNotification && (
<FrozenAccountNotification
key="frozen"
onClick={handleFrozenAccountNotificationClick}
/>
)}
{shouldDisplayArchive && (
<Archive
key="archive"

View File

@ -0,0 +1,25 @@
.root {
background-color: var(--color-background-secondary);
margin-inline: -0.5rem;
padding-block: 0.75rem;
padding-inline: 1rem;
cursor: pointer;
&:hover {
opacity: 0.85;
}
}
.title {
color: var(--color-error);
font-weight: var(--font-weight-medium);
font-size: 1rem;
line-height: 1rem;
}
.subtitle {
color: var(--color-text-secondary);
font-size: 0.875rem;
margin-top: 0.25rem;
line-height: 1rem;
}

View File

@ -0,0 +1,25 @@
import React, { memo } from '../../../lib/teact/teact';
import useLang from '../../../hooks/useLang';
import styles from './FrozenAccountNotification.module.scss';
type OwnProps = {
onClick: () => void;
};
const FrozenAccountNotification = ({ onClick }: OwnProps) => {
const lang = useLang();
return (
<div
className={styles.root}
onClick={onClick}
>
<div className={styles.title}>{lang('TitleFrozenAccount')}</div>
<div className={styles.subtitle}>{lang('SubtitleFrozenAccount')}</div>
</div>
);
};
export default memo(FrozenAccountNotification);

View File

@ -43,6 +43,7 @@ type OwnProps = {
onContentChange: (content: LeftColumnContent) => void;
onSettingsScreenSelect: (screen: SettingsScreens) => void;
onTopicSearch: NoneToVoidFunction;
isAccountFrozen?: boolean;
onReset: () => void;
};
@ -67,6 +68,7 @@ const LeftMain: FC<OwnProps> = ({
onSettingsScreenSelect,
onReset,
onTopicSearch,
isAccountFrozen,
}) => {
const { closeForumPanel } = getActions();
const [isNewChatButtonShown, setIsNewChatButtonShown] = useState(IS_TOUCH_ENV);
@ -243,6 +245,7 @@ const LeftMain: FC<OwnProps> = ({
onNewPrivateChat={handleSelectContacts}
onNewChannel={handleSelectNewChannel}
onNewGroup={handleSelectNewGroup}
isAccountFrozen={isAccountFrozen}
/>
</div>
);

View File

@ -5,7 +5,7 @@ import { getActions, withGlobal } from '../../../global';
import type { ApiEmojiStatusCollectible, ApiEmojiStatusType, ApiSticker } from '../../../api/types';
import { EMOJI_STATUS_LOOP_LIMIT } from '../../../config';
import { selectUser } from '../../../global/selectors';
import { selectIsCurrentUserFrozen, selectUser } from '../../../global/selectors';
import { getServerTime } from '../../../util/serverTime';
import useTimeout from '../../../hooks/schedulers/useTimeout';
@ -22,13 +22,14 @@ import StatusPickerMenu from './StatusPickerMenu.async';
interface StateProps {
emojiStatus?: ApiEmojiStatusType;
collectibleStatuses?: ApiEmojiStatusType[];
isAccountFrozen?: boolean;
}
const EFFECT_DURATION_MS = 1500;
const EMOJI_STATUS_SIZE = 24;
const StatusButton: FC<StateProps> = ({ emojiStatus, collectibleStatuses }) => {
const { setEmojiStatus, loadCurrentUser } = getActions();
const StatusButton: FC<StateProps> = ({ emojiStatus, collectibleStatuses, isAccountFrozen }) => {
const { setEmojiStatus, loadCurrentUser, openFrozenAccountModal } = getActions();
// eslint-disable-next-line no-null/no-null
const buttonRef = useRef<HTMLButtonElement>(null);
@ -60,8 +61,12 @@ const StatusButton: FC<StateProps> = ({ emojiStatus, collectibleStatuses }) => {
useTimeout(hideEffect, isEffectShown ? EFFECT_DURATION_MS : undefined);
const handleEmojiStatusClick = useCallback(() => {
if (isAccountFrozen) {
openFrozenAccountModal();
return;
}
openStatusPicker();
}, [openStatusPicker]);
}, [openStatusPicker, isAccountFrozen]);
return (
<div className="StatusButton extra-spacing">
@ -105,9 +110,11 @@ export default memo(withGlobal((global): StateProps => {
const { currentUserId } = global;
const currentUser = currentUserId ? selectUser(global, currentUserId) : undefined;
const collectibleStatuses = global.collectibleEmojiStatuses?.statuses;
const isAccountFrozen = selectIsCurrentUserFrozen(global);
return {
emojiStatus: currentUser?.emojiStatus,
collectibleStatuses,
isAccountFrozen,
};
})(StatusButton));

View File

@ -6,7 +6,10 @@ import type { ApiPrivacySettings } from '../../../api/types';
import type { GlobalState } from '../../../global/types';
import { SettingsScreens } from '../../../types';
import { selectCanSetPasscode, selectIsCurrentUserPremium } from '../../../global/selectors';
import {
selectCanSetPasscode, selectIsCurrentUserFrozen,
selectIsCurrentUserPremium,
} from '../../../global/selectors';
import { selectSharedSettings } from '../../../global/selectors/sharedState';
import useHistoryBack from '../../../hooks/useHistoryBack';
@ -37,6 +40,7 @@ type StateProps = {
shouldNewNonContactPeersRequirePremium?: boolean;
shouldChargeForMessages: boolean;
canDisplayChatInTitle?: boolean;
isCurrentUserFrozen?: boolean;
privacy: GlobalState['settings']['privacy'];
};
@ -58,6 +62,7 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
privacy,
onScreenSelect,
onReset,
isCurrentUserFrozen,
}) => {
const {
loadPrivacySettings,
@ -71,17 +76,19 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
} = getActions();
useEffect(() => {
loadBlockedUsers();
loadPrivacySettings();
loadContentSettings();
loadWebAuthorizations();
}, []);
if (!isCurrentUserFrozen) {
loadBlockedUsers();
loadPrivacySettings();
loadContentSettings();
loadWebAuthorizations();
}
}, [isCurrentUserFrozen]);
useEffect(() => {
if (isActive) {
if (isActive && !isCurrentUserFrozen) {
loadGlobalPrivacySettings();
}
}, [isActive, loadGlobalPrivacySettings]);
}, [isActive, isCurrentUserFrozen, loadGlobalPrivacySettings]);
const oldLang = useOldLang();
const lang = useLang();
@ -419,6 +426,7 @@ export default memo(withGlobal<OwnProps>(
const { canDisplayChatInTitle } = selectSharedSettings(global);
const shouldChargeForMessages = Boolean(nonContactPeersPaidStars);
const isCurrentUserFrozen = selectIsCurrentUserFrozen(global);
return {
isCurrentUserPremium: selectIsCurrentUserPremium(global),
@ -435,6 +443,7 @@ export default memo(withGlobal<OwnProps>(
privacy,
canDisplayChatInTitle,
canSetPasscode: selectCanSetPasscode(global),
isCurrentUserFrozen,
};
},
)(SettingsPrivacy));

View File

@ -19,6 +19,7 @@ import {
selectChatFolder,
selectChatMessage,
selectCurrentMessageList,
selectIsCurrentUserFrozen,
selectIsCurrentUserPremium,
selectIsForwardModalOpen,
selectIsMediaViewerOpen,
@ -141,6 +142,8 @@ type StateProps = {
noRightColumnAnimation?: boolean;
withInterfaceAnimations?: boolean;
isSynced?: boolean;
isAccountFrozen?: boolean;
isAppConfigLoaded?: boolean;
};
const APP_OUTDATED_TIMEOUT_MS = 5 * 60 * 1000; // 5 min
@ -194,6 +197,8 @@ const Main = ({
noRightColumnAnimation,
isSynced,
currentUserId,
isAccountFrozen,
isAppConfigLoaded,
}: OwnProps & StateProps) => {
const {
initMain,
@ -248,6 +253,10 @@ const Main = ({
loadTopBotApps,
loadPaidReactionPrivacy,
loadPasswordInfo,
loadBotFreezeAppeal,
loadAllChats,
loadAllStories,
loadAllHiddenStories,
} = getActions();
if (DEBUG && !DEBUG_isLogged) {
@ -309,45 +318,54 @@ const Main = ({
loadAppConfig();
loadPeerColors();
initMain();
loadAvailableReactions();
loadAnimatedEmojis();
loadNotificationSettings();
loadNotificationExceptions();
loadAttachBots();
loadContactList();
loadDefaultTopicIcons();
checkAppVersion();
loadTopReactions();
loadAuthorizations();
loadPasswordInfo();
}
}, [isMasterTab, isSynced]);
// Initial API calls
useEffect(() => {
if (isMasterTab && isSynced && isAppConfigLoaded && !isAccountFrozen) {
loadAllChats({ listType: 'saved' });
loadAllStories();
loadAllHiddenStories();
loadRecentReactions();
loadDefaultTagReactions();
loadFeaturedEmojiStickers();
loadAttachBots();
loadNotificationSettings();
loadNotificationExceptions();
loadTopInlineBots();
loadEmojiKeywords({ language: BASE_EMOJI_KEYWORD_LANG });
loadTimezones();
loadQuickReplies();
loadTopReactions();
loadStarStatus();
loadEmojiKeywords({ language: BASE_EMOJI_KEYWORD_LANG });
loadFeaturedEmojiStickers();
loadSavedReactionTags();
loadTopBotApps();
loadPaidReactionPrivacy();
loadDefaultTopicIcons();
loadAnimatedEmojis();
loadAvailableReactions();
loadUserCollectibleStatuses();
loadGenericEmojiEffects();
loadPremiumGifts();
loadStarGifts();
loadAvailableEffects();
loadBirthdayNumbersStickers();
loadRestrictedEmojiStickers();
loadGenericEmojiEffects();
loadSavedReactionTags();
loadAuthorizations();
loadTopBotApps();
loadPaidReactionPrivacy();
loadPasswordInfo();
loadUserCollectibleStatuses();
loadQuickReplies();
loadTimezones();
}
}, [isMasterTab, isSynced]);
}, [isMasterTab, isSynced, isAppConfigLoaded, isAccountFrozen]);
// Initial Premium API calls
useEffect(() => {
if (isMasterTab && isCurrentUserPremium) {
if (isMasterTab && isCurrentUserPremium && isAppConfigLoaded && !isAccountFrozen) {
loadDefaultStatusIcons();
loadRecentEmojiStatuses();
}
}, [isCurrentUserPremium, isMasterTab]);
}, [isCurrentUserPremium, isMasterTab, isAppConfigLoaded, isAccountFrozen]);
// Language-based API calls
useEffect(() => {
@ -357,8 +375,6 @@ const Main = ({
}
loadCountryList({ langCode: lang.code });
loadAttachBots();
}
}, [lang, isMasterTab]);
@ -374,7 +390,7 @@ const Main = ({
// Sticker sets
useEffect(() => {
if (isMasterTab && isSynced) {
if (isMasterTab && isSynced && isAppConfigLoaded && !isAccountFrozen) {
if (!addedSetIds || !addedCustomEmojiIds) {
loadStickerSets();
loadFavoriteStickers();
@ -384,7 +400,11 @@ const Main = ({
loadAddedStickers();
}
}
}, [addedSetIds, addedCustomEmojiIds, isMasterTab, isSynced]);
}, [addedSetIds, addedCustomEmojiIds, isMasterTab, isSynced, isAppConfigLoaded, isAccountFrozen]);
useEffect(() => {
loadBotFreezeAppeal();
}, [isAppConfigLoaded]);
// Check version when service chat is ready
useEffect(() => {
@ -636,6 +656,7 @@ export default memo(withGlobal<OwnProps>(
|| !selectCanAnimateInterface(global);
const deleteFolderDialog = deleteFolderDialogModal ? selectChatFolder(global, deleteFolderDialogModal) : undefined;
const isAccountFrozen = selectIsCurrentUserFrozen(global);
return {
currentUserId,
@ -681,6 +702,8 @@ export default memo(withGlobal<OwnProps>(
requestedDraft,
noRightColumnAnimation,
isSynced: global.isSynced,
isAccountFrozen,
isAppConfigLoaded: global.isAppConfigLoaded,
};
},
)(Main));

View File

@ -0,0 +1,21 @@
.root {
cursor: pointer;
&:hover {
opacity: 0.85;
}
}
.title {
color: var(--color-error);
font-weight: var(--font-weight-medium);
font-size: 1rem;
line-height: 1rem;
}
.subtitle {
color: var(--color-text-secondary);
font-size: 0.875rem;
margin-top: 0.25rem;
line-height: 1rem;
}

View File

@ -0,0 +1,29 @@
import React, { memo } from '../../lib/teact/teact';
import { getActions } from '../../global';
import useLang from '../../hooks/useLang';
import useLastCallback from '../../hooks/useLastCallback';
import styles from './FrozenAccountPlaceholder.module.scss';
function FrozenAccountPlaceholder() {
const lang = useLang();
const { openFrozenAccountModal } = getActions();
const handleClick = useLastCallback(() => {
openFrozenAccountModal();
});
return (
<div
className={styles.root}
onClick={handleClick}
>
<div className={styles.title}>{lang('ComposerTitleFrozenAccount')}</div>
<div className={styles.subtitle}>{lang('ComposerSubtitleFrozenAccount')}</div>
</div>
);
}
export default memo(FrozenAccountPlaceholder);

View File

@ -23,6 +23,7 @@ import {
selectChatFullInfo,
selectIsChatBotNotStarted,
selectIsChatWithSelf,
selectIsCurrentUserFrozen,
selectIsInSelectMode,
selectIsRightColumnShown,
selectIsUserBlocked,
@ -82,6 +83,7 @@ interface StateProps {
language: string;
detectedChatLanguage?: string;
doNotTranslate: string[];
isAccountFrozen?: boolean;
}
// Chrome breaks layout when focusing input during transition
@ -121,6 +123,7 @@ const HeaderActions: FC<OwnProps & StateProps> = ({
detectedChatLanguage,
doNotTranslate,
onTopicSearch,
isAccountFrozen,
}) => {
const {
joinChannel,
@ -137,6 +140,7 @@ const HeaderActions: FC<OwnProps & StateProps> = ({
setSettingOption,
unblockUser,
setViewForumAsMessages,
openFrozenAccountModal,
} = getActions();
// eslint-disable-next-line no-null/no-null
const menuButtonRef = useRef<HTMLButtonElement>(null);
@ -219,6 +223,10 @@ const HeaderActions: FC<OwnProps & StateProps> = ({
});
const handleRequestCall = useLastCallback(() => {
if (isAccountFrozen) {
openFrozenAccountModal();
return;
}
requestMasterAndRequestCall({ userId: chatId });
});
@ -513,6 +521,7 @@ export default memo(withGlobal<OwnProps>(
const isTranslating = Boolean(selectRequestedChatTranslationLanguage(global, chatId));
const canTranslate = selectCanTranslateChat(global, chatId) && !fullInfo?.isTranslationDisabled;
const isAccountFrozen = selectIsCurrentUserFrozen(global);
return {
noMenu: false,
@ -542,6 +551,7 @@ export default memo(withGlobal<OwnProps>(
doNotTranslate,
detectedChatLanguage: chat.detectedLanguage,
canUnblock,
isAccountFrozen,
};
},
)(HeaderActions));

View File

@ -31,6 +31,7 @@ import {
selectChatFullInfo,
selectCurrentMessageList,
selectIsChatWithSelf,
selectIsCurrentUserFrozen,
selectIsRightColumnShown,
selectNotifyDefaults,
selectNotifyException,
@ -122,6 +123,7 @@ type StateProps = {
isBot?: boolean;
isChatWithSelf?: boolean;
savedDialog?: ApiChat;
isAccountFrozen?: boolean;
};
const CLOSE_MENU_ANIMATION_DURATION = 200;
@ -176,6 +178,7 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
onAsMessagesClick,
onClose,
onCloseAnimationEnd,
isAccountFrozen,
}) => {
const {
updateChatMutedState,
@ -186,6 +189,7 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
createGroupCall,
openLinkedChat,
openAddContactDialog,
openFrozenAccountModal,
requestMasterAndRequestCall,
toggleStatistics,
openMonetizationStatistics,
@ -224,14 +228,23 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
});
const handleReport = useLastCallback(() => {
setIsMenuOpen(false);
reportMessages({ chatId, messageIds: [] });
if (isAccountFrozen) {
openFrozenAccountModal();
} else {
setIsMenuOpen(false);
reportMessages({ chatId, messageIds: [] });
}
onClose();
});
const handleDelete = useLastCallback(() => {
if (isAccountFrozen) {
openFrozenAccountModal();
onClose();
} else {
setIsDeleteModalOpen(true);
}
setIsMenuOpen(false);
setIsDeleteModalOpen(true);
});
const closeMenu = useLastCallback(() => {
@ -251,39 +264,68 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
});
const handleStartBot = useLastCallback(() => {
sendBotCommand({ command: '/start' });
if (isAccountFrozen) {
openFrozenAccountModal();
} else {
sendBotCommand({ command: '/start' });
}
});
const handleRestartBot = useLastCallback(() => {
restartBot({ chatId });
if (isAccountFrozen) {
openFrozenAccountModal();
} else {
restartBot({ chatId });
}
});
const handleUnmuteClick = useLastCallback(() => {
updateChatMutedState({ chatId, isMuted: false });
if (isAccountFrozen) {
openFrozenAccountModal();
} else {
updateChatMutedState({ chatId, isMuted: false });
}
closeMenu();
});
const handleMuteClick = useLastCallback(() => {
markRenderMuteModal();
setIsMuteModalOpen(true);
if (isAccountFrozen) {
openFrozenAccountModal();
closeMenu();
} else {
markRenderMuteModal();
setIsMuteModalOpen(true);
}
setIsMenuOpen(false);
});
const handleCreateTopicClick = useLastCallback(() => {
openCreateTopicPanel({ chatId });
setShouldCloseFast(!isRightColumnShown);
if (isAccountFrozen) {
openFrozenAccountModal();
} else {
openCreateTopicPanel({ chatId });
setShouldCloseFast(!isRightColumnShown);
}
closeMenu();
});
const handleEditClick = useLastCallback(() => {
toggleManagement({ force: true });
setShouldCloseFast(!isRightColumnShown);
if (isAccountFrozen) {
openFrozenAccountModal();
} else {
toggleManagement({ force: true });
setShouldCloseFast(!isRightColumnShown);
}
closeMenu();
});
const handleEditTopicClick = useLastCallback(() => {
openEditTopicPanel({ chatId, topicId: Number(threadId) });
setShouldCloseFast(!isRightColumnShown);
if (isAccountFrozen) {
openFrozenAccountModal();
} else {
openEditTopicPanel({ chatId, topicId: Number(threadId) });
setShouldCloseFast(!isRightColumnShown);
}
closeMenu();
});
@ -294,7 +336,9 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
});
const handleEnterVoiceChatClick = useLastCallback(() => {
if (canCreateVoiceChat) {
if (isAccountFrozen) {
openFrozenAccountModal();
} else if (canCreateVoiceChat) {
// TODO Show popup to schedule
createGroupCall({
chatId,
@ -313,27 +357,47 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
});
const handleGiftClick = useLastCallback(() => {
openGiftModal({ forUserId: chatId });
if (isAccountFrozen) {
openFrozenAccountModal();
} else {
openGiftModal({ forUserId: chatId });
}
closeMenu();
});
const handleAddContactClick = useLastCallback(() => {
openAddContactDialog({ userId: chatId });
if (isAccountFrozen) {
openFrozenAccountModal();
} else {
openAddContactDialog({ userId: chatId });
}
closeMenu();
});
const handleSubscribe = useLastCallback(() => {
onSubscribeChannel();
if (isAccountFrozen) {
openFrozenAccountModal();
} else {
onSubscribeChannel();
}
closeMenu();
});
const handleVideoCall = useLastCallback(() => {
requestMasterAndRequestCall({ userId: chatId, isVideo: true });
if (isAccountFrozen) {
openFrozenAccountModal();
} else {
requestMasterAndRequestCall({ userId: chatId, isVideo: true });
}
closeMenu();
});
const handleCall = useLastCallback(() => {
requestMasterAndRequestCall({ userId: chatId });
if (isAccountFrozen) {
openFrozenAccountModal();
} else {
requestMasterAndRequestCall({ userId: chatId });
}
closeMenu();
});
@ -355,7 +419,9 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
});
const handleBoostClick = useLastCallback(() => {
if (canViewBoosts) {
if (isAccountFrozen) {
openFrozenAccountModal();
} else if (canViewBoosts) {
openBoostStatistics({ chatId });
setShouldCloseFast(!isRightColumnShown);
} else {
@ -370,7 +436,11 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
});
const handleSelectMessages = useLastCallback(() => {
enterMessageSelectMode();
if (isAccountFrozen) {
openFrozenAccountModal();
} else {
enterMessageSelectMode();
}
closeMenu();
});
@ -380,12 +450,20 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
});
const handleBlock = useLastCallback(() => {
blockUser({ userId: chatId });
if (isAccountFrozen) {
openFrozenAccountModal();
} else {
blockUser({ userId: chatId });
}
closeMenu();
});
const handleUnblock = useLastCallback(() => {
unblockUser({ userId: chatId });
if (isAccountFrozen) {
openFrozenAccountModal();
} else {
unblockUser({ userId: chatId });
}
closeMenu();
});
@ -756,6 +834,7 @@ export default memo(withGlobal<OwnProps>(
const isSavedDialog = getIsSavedDialog(chatId, threadId, global.currentUserId);
const savedDialog = isSavedDialog ? selectChat(global, String(threadId)) : undefined;
const isAccountFrozen = selectIsCurrentUserFrozen(global);
return {
chat,
@ -782,6 +861,7 @@ export default memo(withGlobal<OwnProps>(
isBot: Boolean(chatBot),
isChatWithSelf,
savedDialog,
isAccountFrozen,
};
},
)(HeaderMenuContainer));

View File

@ -43,6 +43,7 @@ import {
selectFocusedMessageId,
selectIsChatProtected,
selectIsChatWithSelf,
selectIsCurrentUserFrozen,
selectIsCurrentUserPremium,
selectIsInSelectMode,
selectIsViewportNewest,
@ -130,10 +131,12 @@ type StateProps = {
isEmptyThread?: boolean;
isForum?: boolean;
currentUserId: string;
isAccountFrozen?: boolean;
areAdsEnabled?: boolean;
channelJoinInfo?: ApiChatFullInfo['joinInfo'];
isChatProtected?: boolean;
hasCustomGreeting?: boolean;
isAppConfigLoaded?: boolean;
};
const MESSAGE_REACTIONS_POLLING_INTERVAL = 20 * 1000;
@ -196,7 +199,9 @@ const MessageList: FC<OwnProps & StateProps> = ({
onIntersectPinnedMessage,
onScrollDownToggle,
onNotchToggle,
isAccountFrozen,
hasCustomGreeting,
isAppConfigLoaded,
}) => {
const {
loadViewportMessages, setScrollOffset, loadSponsoredMessages, loadMessageReactions, copyMessagesByIds,
@ -248,10 +253,10 @@ const MessageList: FC<OwnProps & StateProps> = ({
useEffect(() => {
const canHaveAds = isChannelChat || isBot;
if (areAdsEnabled && canHaveAds && isSynced && isReady) {
if (areAdsEnabled && canHaveAds && isSynced && isReady && isAppConfigLoaded) {
loadSponsoredMessages({ peerId: chatId });
}
}, [chatId, isSynced, isReady, isChannelChat, isBot, areAdsEnabled]);
}, [chatId, isSynced, isReady, isChannelChat, isBot, areAdsEnabled, isAppConfigLoaded]);
// Updated only once when messages are loaded (as we want the unread divider to keep its position)
useSyncEffect(() => {
@ -344,7 +349,7 @@ const MessageList: FC<OwnProps & StateProps> = ({
threadId, isChatWithSelf, channelJoinInfo]);
useInterval(() => {
if (!messageIds || !messagesById || type === 'scheduled') return;
if (!messageIds || !messagesById || type === 'scheduled' || isAccountFrozen) return;
if (!isChannelChat && !isGroupChat) return;
const ids = messageIds.filter((id) => {
@ -796,8 +801,10 @@ export default memo(withGlobal<OwnProps>(
const isCurrentUserPremium = selectIsCurrentUserPremium(global);
const areAdsEnabled = !isCurrentUserPremium || selectUserFullInfo(global, currentUserId)?.areAdsEnabled;
const isAccountFrozen = selectIsCurrentUserFrozen(global);
const hasCustomGreeting = Boolean(userFullInfo?.businessIntro);
const isAppConfigLoaded = global.isAppConfigLoaded;
return {
areAdsEnabled,
@ -832,7 +839,9 @@ export default memo(withGlobal<OwnProps>(
currentUserId,
isChatProtected: selectIsChatProtected(global, chatId),
...(withLastMessageWhenPreloading && { lastMessage }),
isAccountFrozen,
hasCustomGreeting,
isAppConfigLoaded,
};
},
)(MessageList));

View File

@ -266,14 +266,19 @@
}
.MessageSelectToolbar-inner,
.composer-button,
.messaging-disabled {
.composer-button {
.mask-image-disabled & {
box-shadow: 0 0.25rem 0.5rem 0.125rem var(--color-default-shadow);
border-radius: var(--border-radius-messages);
}
}
.messaging-disabled {
.mask-image-disabled & {
border-radius: var(--border-radius-messages);
}
}
.middle-column-footer-button-container {
width: 100%;
display: flex;

View File

@ -47,6 +47,7 @@ import {
selectCurrentMiddleSearch,
selectDraft,
selectIsChatBotNotStarted,
selectIsCurrentUserFrozen,
selectIsInSelectMode,
selectIsRightColumnShown,
selectIsUserBlocked,
@ -93,6 +94,7 @@ import ChatLanguageModal from './ChatLanguageModal.async';
import { DropAreaState } from './composer/DropArea';
import EmojiInteractionAnimation from './EmojiInteractionAnimation.async';
import FloatingActionButtons from './FloatingActionButtons';
import FrozenAccountPlaceholder from './FrozenAccountPlaceholder';
import MessageList from './MessageList';
import MessageSelectToolbar from './MessageSelectToolbar.async';
import MiddleHeader from './MiddleHeader';
@ -156,6 +158,8 @@ type StateProps = {
isContactRequirePremium?: boolean;
topics?: Record<number, ApiTopic>;
paidMessagesStars?: number;
isAccountFrozen?: boolean;
freezeAppealChat?: ApiChat;
};
function isImage(item: DataTransferItem) {
@ -217,6 +221,8 @@ function MiddleColumn({
isContactRequirePremium,
topics,
paidMessagesStars,
isAccountFrozen,
freezeAppealChat,
}: OwnProps & StateProps) {
const {
openChat,
@ -448,7 +454,8 @@ function MiddleColumn({
const composerRestrictionMessage = messageSendingRestrictionReason
?? forumComposerPlaceholder
?? (isContactRequirePremium ? <PremiumRequiredPlaceholder userId={chatId!} /> : undefined);
?? (isContactRequirePremium ? <PremiumRequiredPlaceholder userId={chatId!} /> : undefined)
?? (isAccountFrozen && freezeAppealChat?.id !== chatId ? <FrozenAccountPlaceholder /> : undefined);
// CSS Variables calculation doesn't work properly with transforms, so we calculate transform values in JS
const {
@ -477,7 +484,7 @@ function MiddleColumn({
const isMessagingDisabled = Boolean(
!isPinnedMessageList && !isSavedDialog && !renderingCanPost && !renderingCanRestartBot && !renderingCanStartBot
&& !renderingCanSubscribe && composerRestrictionMessage,
);
) || (isAccountFrozen && freezeAppealChat?.id !== chatId);
const withMessageListBottomShift = Boolean(
renderingCanRestartBot || renderingCanSubscribe || renderingShouldSendJoinRequest || renderingCanStartBot
|| (isPinnedMessageList && canUnpin) || canShowOpenChatButton || renderingCanUnblock,
@ -809,6 +816,10 @@ export default memo(withGlobal<OwnProps>(
const isContactRequirePremium = userFull?.isContactRequirePremium;
const paidMessagesStars = selectPeerPaidMessagesStars(global, chatId);
const isAccountFrozen = selectIsCurrentUserFrozen(global);
const botFreezeAppealId = global.botFreezeAppealId;
const freezeAppealChat = botFreezeAppealId
? selectChat(global, botFreezeAppealId) : undefined;
return {
...state,
@ -826,7 +837,8 @@ export default memo(withGlobal<OwnProps>(
&& !isBotNotStarted
&& !(shouldJoinToSend && chat?.isNotJoined)
&& !shouldBlockSendInForum
&& !isSavedDialog,
&& !isSavedDialog
&& (!isAccountFrozen || freezeAppealChat?.id === chatId),
isPinnedMessageList,
currentUserBannedRights: chat?.currentUserBannedRights,
defaultBannedRights: chat?.defaultBannedRights,
@ -847,6 +859,8 @@ export default memo(withGlobal<OwnProps>(
isContactRequirePremium,
topics,
paidMessagesStars,
isAccountFrozen,
freezeAppealChat,
};
},
)(MiddleColumn));

View File

@ -19,6 +19,7 @@ import { getMessageReplyInfo } from '../../../global/helpers/replies';
import {
selectChat,
selectChatMessage,
selectIsCurrentUserFrozen,
selectIsCurrentUserPremium,
selectIsInSelectMode,
selectIsMessageFocused,
@ -86,6 +87,7 @@ type StateProps = {
hasUnreadReaction?: boolean;
isResizingContainer?: boolean;
scrollTargetPosition?: ScrollTargetPosition;
isAccountFrozen?: boolean;
};
const SINGLE_LINE_ACTIONS: Set<ApiMessageAction['type']> = new Set([
@ -121,6 +123,7 @@ const ActionMessage = ({
observeIntersectionForBottom,
observeIntersectionForLoading,
observeIntersectionForPlaying,
isAccountFrozen,
}: OwnProps & StateProps) => {
const {
requestConfetti,
@ -189,7 +192,7 @@ const ActionMessage = ({
handleContextMenuClose, handleContextMenuHide,
} = useContextMenuHandlers(
ref,
isTouchScreen && isInSelectMode,
(isTouchScreen && isInSelectMode) || isAccountFrozen,
!IS_ELECTRON,
IS_ANDROID,
getIsMessageListReady,
@ -448,6 +451,7 @@ const ActionMessage = ({
threadId={threadId}
observeIntersection={observeIntersectionForPlaying}
isCurrentUserPremium={isCurrentUserPremium}
isAccountFrozen
/>
)}
</div>
@ -479,6 +483,7 @@ export default memo(withGlobal<OwnProps>(
const isCurrentUserPremium = selectIsCurrentUserPremium(global);
const hasUnreadReaction = chat?.unreadReactions?.includes(message.id);
const isAccountFrozen = selectIsCurrentUserFrozen(global);
return {
sender,
@ -494,6 +499,7 @@ export default memo(withGlobal<OwnProps>(
hasUnreadReaction,
isResizingContainer,
scrollTargetPosition,
isAccountFrozen,
};
},
)(ActionMessage));

View File

@ -699,6 +699,31 @@ const ActionMessageText = ({
case 'customAction':
return action.message;
case 'paidMessagesPrice': {
const { stars } = action;
if (stars === 0) {
return lang('ActionPaidMessageGroupPriceFree');
}
return lang('ActionPaidMessageGroupPrice', {
stars: formatStarsAsText(lang, stars),
}, { withNodes: true, withMarkdown: true });
}
case 'paidMessagesRefunded': {
const { stars } = action;
const user = selectPeer(global, chatId);
const userTitle = (user && getPeerTitle(lang, user)) || userFallbackText;
const key = isOutgoing
? 'ApiMessageActionPaidMessagesRefundedOutgoing'
: 'ApiMessageActionPaidMessagesRefundedIncoming';
return lang(key, {
stars: formatStarsAsText(lang, stars),
user: renderPeerLink(user?.id, userTitle),
}, { withNodes: true, withMarkdown: true });
}
case 'phoneCall': // Rendered as a regular message, but considered an action for the summary
return lang(getCallMessageKey(action, isOutgoing));
default:

View File

@ -4,7 +4,7 @@ import { getActions, getGlobal } from '../../../global';
import type { ApiCommentsInfo } from '../../../api/types';
import { selectPeer } from '../../../global/selectors';
import { selectIsCurrentUserFrozen, selectPeer } from '../../../global/selectors';
import buildClassName from '../../../util/buildClassName';
import { formatIntegerCompact } from '../../../util/textFormat';
@ -36,7 +36,7 @@ const CommentButton: FC<OwnProps> = ({
isLoading,
asActionButton,
}) => {
const { openThread } = getActions();
const { openThread, openFrozenAccountModal } = getActions();
const shouldRenderLoading = useAsyncRendering([isLoading], SHOW_LOADER_DELAY);
@ -46,6 +46,11 @@ const CommentButton: FC<OwnProps> = ({
} = threadInfo;
const handleClick = useLastCallback(() => {
const global = getGlobal();
if (selectIsCurrentUserFrozen(global)) {
openFrozenAccountModal();
return;
}
openThread({
isComments: true, chatId, originMessageId, originChannelId,
});

View File

@ -85,6 +85,7 @@ import {
selectForwardedSender,
selectIsChatProtected,
selectIsChatWithSelf,
selectIsCurrentUserFrozen,
selectIsCurrentUserPremium,
selectIsDocumentGroupSelected,
selectIsInSelectMode,
@ -305,6 +306,7 @@ type StateProps = {
lastPlaybackTimestamp?: number;
paidMessageStars?: number;
isChatWithUser?: boolean;
isAccountFrozen?: boolean;
};
type MetaPosition =
@ -428,6 +430,7 @@ const Message: FC<OwnProps & StateProps> = ({
onIntersectPinnedMessage,
paidMessageStars,
isChatWithUser,
isAccountFrozen,
}) => {
const {
toggleMessageSelection,
@ -465,7 +468,7 @@ const Message: FC<OwnProps & StateProps> = ({
handleContextMenuHide,
} = useContextMenuHandlers(
ref,
isTouchScreen && isInSelectMode,
(isTouchScreen && isInSelectMode) || isAccountFrozen,
!IS_ELECTRON,
IS_ANDROID,
getIsMessageListReady,
@ -570,6 +573,7 @@ const Message: FC<OwnProps & StateProps> = ({
const hasSubheader = hasTopicChip || hasMessageReply || hasStoryReply || hasForwardedCustomShape;
const selectMessage = useLastCallback((e?: React.MouseEvent<HTMLDivElement, MouseEvent>, groupedId?: string) => {
if (isAccountFrozen) return;
toggleMessageSelection({
messageId,
groupedId,
@ -768,7 +772,7 @@ const Message: FC<OwnProps & StateProps> = ({
&& !isInDocumentGroupNotLast && messageListType === 'thread'
&& !noComments;
const withQuickReactionButton = !isTouchScreen && !phoneCall && !isInSelectMode && defaultReaction
&& !isInDocumentGroupNotLast && !isStoryMention && !hasTtl;
&& !isInDocumentGroupNotLast && !isStoryMention && !hasTtl && !isAccountFrozen;
const hasOutsideReactions = !withVoiceTranscription && hasReactions
&& (isCustomShape || ((photo || video || storyData || (location?.mediaType === 'geo')) && !hasText));
@ -1048,6 +1052,7 @@ const Message: FC<OwnProps & StateProps> = ({
noRecentReactors={isChannel}
tags={tags}
isCurrentUserPremium={isPremium}
isAccountFrozen
/>
);
}
@ -1699,6 +1704,7 @@ const Message: FC<OwnProps & StateProps> = ({
observeIntersection={observeIntersectionForPlaying}
noRecentReactors={isChannel}
tags={tags}
isAccountFrozen
/>
)}
</div>
@ -1858,6 +1864,7 @@ export default memo(withGlobal<OwnProps>(
const maxTimestamp = selectMessageTimestampableDuration(global, message);
const lastPlaybackTimestamp = selectMessageLastPlaybackTimestamp(global, chatId, message.id);
const isAccountFrozen = selectIsCurrentUserFrozen(global);
return {
theme: selectTheme(global),
@ -1951,6 +1958,7 @@ export default memo(withGlobal<OwnProps>(
lastPlaybackTimestamp,
paidMessageStars,
isChatWithUser,
isAccountFrozen,
};
},
)(Message));

View File

@ -36,6 +36,7 @@ type OwnProps = {
isCurrentUserPremium?: boolean;
observeIntersection?: ObserveFn;
noRecentReactors?: boolean;
isAccountFrozen?: boolean;
};
const MAX_RECENT_AVATARS = 3;
@ -51,6 +52,7 @@ const Reactions: FC<OwnProps> = ({
noRecentReactors,
isCurrentUserPremium,
tags,
isAccountFrozen,
}) => {
const {
toggleReaction,
@ -60,6 +62,7 @@ const Reactions: FC<OwnProps> = ({
openPremiumModal,
resetLocalPaidReactions,
showNotification,
openFrozenAccountModal,
} = getActions();
const lang = useOldLang();
@ -107,6 +110,10 @@ const Reactions: FC<OwnProps> = ({
}, [message, noRecentReactors, recentReactorsByReactionKey, results, areTags, tags, totalCount]);
const handleClick = useLastCallback((reaction: ApiReaction) => {
if (isAccountFrozen) {
openFrozenAccountModal();
return;
}
if (areTags) {
if (!isCurrentUserPremium) {
openPremiumModal({
@ -130,6 +137,11 @@ const Reactions: FC<OwnProps> = ({
const paidLocalCount = useMemo(() => results.find((r) => r.reaction.type === 'paid')?.localAmount || 0, [results]);
const handlePaidClick = useLastCallback((count: number) => {
if (isAccountFrozen) {
openFrozenAccountModal();
return;
}
addLocalPaidReaction({
chatId: message.chatId,
messageId: message.id,
@ -162,6 +174,11 @@ const Reactions: FC<OwnProps> = ({
}, [lang, message, paidLocalCount]);
const handleRemoveReaction = useLastCallback((reaction: ApiReaction) => {
if (isAccountFrozen) {
openFrozenAccountModal();
return;
}
toggleReaction({
chatId: message.chatId,
messageId: message.id,

View File

@ -15,6 +15,7 @@ import ChatInviteModal from './chatInvite/ChatInviteModal.async';
import ChatlistModal from './chatlist/ChatlistModal.async';
import CollectibleInfoModal from './collectible/CollectibleInfoModal.async';
import EmojiStatusAccessModal from './emojiStatusAccess/EmojiStatusAccessModal.async';
import FrozenAccountModal from './frozenAccount/FrozenAccountModal.async';
import PremiumGiftModal from './gift/GiftModal.async';
import GiftInfoModal from './gift/info/GiftInfoModal.async';
import GiftRecipientPicker from './gift/recipient/GiftRecipientPicker.async';
@ -79,7 +80,8 @@ type ModalKey = keyof Pick<TabState,
'sharePreparedMessageModal' |
'giftStatusInfoModal' |
'giftTransferModal' |
'chatRefundModal'
'chatRefundModal' |
'isFrozenAccountModalOpen'
>;
type StateProps = {
@ -130,6 +132,7 @@ const MODALS: ModalRegistry = {
sharePreparedMessageModal: SharePreparedMessageModal,
giftTransferModal: GiftTransferModal,
chatRefundModal: ChatRefundModal,
isFrozenAccountModalOpen: FrozenAccountModal,
};
const MODAL_KEYS = Object.keys(MODALS) as ModalKey[];
const MODAL_ENTRIES = Object.entries(MODALS) as Entries<ModalRegistry>;

View File

@ -9,7 +9,9 @@ import type { ApiStickerSet, ApiUser } from '../../../api/types';
import type { TabState } from '../../../global/types';
import { getUserFullName } from '../../../global/helpers';
import { selectIsCurrentUserPremium, selectStickerSet, selectUser } from '../../../global/selectors';
import {
selectIsCurrentUserFrozen, selectIsCurrentUserPremium, selectStickerSet, selectUser,
} from '../../../global/selectors';
import buildClassName from '../../../util/buildClassName';
import useInterval from '../../../hooks/schedulers/useInterval';
@ -31,6 +33,7 @@ export type StateProps = {
currentUser?: ApiUser;
stickerSet?: ApiStickerSet;
isPremium?: boolean;
isAccountFrozen?: boolean;
};
const INTERVAL = 3200;
@ -40,6 +43,7 @@ const EmojiStatusAccessModal: FC<OwnProps & StateProps> = ({
currentUser,
stickerSet,
isPremium,
isAccountFrozen,
}) => {
const {
closeEmojiStatusAccessModal,
@ -60,10 +64,10 @@ const EmojiStatusAccessModal: FC<OwnProps & StateProps> = ({
const [currentStatusIndex, setCurrentStatusIndex] = useState<number>(0);
useEffect(() => {
if (isOpen && !stickerSet?.stickers) {
if (isOpen && !stickerSet?.stickers && !isAccountFrozen) {
loadDefaultStatusIcons();
}
}, [isOpen, stickerSet]);
}, [isOpen, stickerSet, isAccountFrozen]);
const mockPeerWithStatus = useMemo(() => {
if (!currentUser || !stickerSet?.stickers) return undefined;
@ -195,11 +199,13 @@ export default memo(withGlobal<OwnProps>(
const currentUser = selectUser(global, global.currentUserId!);
const isPremium = selectIsCurrentUserPremium(global);
const stickerSet = global.defaultStatusIconsId ? selectStickerSet(global, global.defaultStatusIconsId) : undefined;
const isAccountFrozen = selectIsCurrentUserFrozen(global);
return {
currentUser,
stickerSet,
isPremium,
isAccountFrozen,
};
},
)(EmojiStatusAccessModal));

View File

@ -0,0 +1,18 @@
import type { FC } from '../../../lib/teact/teact';
import React from '../../../lib/teact/teact';
import type { OwnProps } from './FrozenAccountModal';
import { Bundles } from '../../../util/moduleLoader';
import useModuleLoader from '../../../hooks/useModuleLoader';
const FrozenAccountModalAsync: FC<OwnProps> = (props) => {
const { modal } = props;
const FrozenAccountModal = useModuleLoader(Bundles.Extra, 'FrozenAccountModal', modal);
// eslint-disable-next-line react/jsx-props-no-spreading
return FrozenAccountModal ? <FrozenAccountModal {...props} /> : undefined;
};
export default FrozenAccountModalAsync;

View File

@ -0,0 +1,26 @@
.header {
margin-top: 0.5rem;
margin-bottom: 0.75rem;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
.title {
font-weight: var(--font-weight-medium);
font-size: 1.25rem;
text-align: center;
padding-top: 0.5rem;
}
.footer {
margin-top: 0.5rem;
display: flex;
align-self: stretch;
flex-direction: column;
}
.buttonAppeal {
margin-bottom: 0.5rem;
}

View File

@ -0,0 +1,143 @@
import React, { memo, useMemo } from '../../../lib/teact/teact';
import { getActions, withGlobal } from '../../../global';
import type { TabState } from '../../../global/types';
import { selectUser } from '../../../global/selectors';
import { formatDateToString } from '../../../util/dates/dateFormat';
import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets';
import formatUsername from '../../common/helpers/formatUsername';
import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback';
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
import Button from '../../ui/Button';
import Link from '../../ui/Link';
import TableAboutModal, { type TableAboutData } from '../common/TableAboutModal';
import styles from './FrozenAccountModal.module.scss';
export type OwnProps = {
modal: TabState['isFrozenAccountModalOpen'];
};
type StateProps = {
freezeAppealUrl?: string;
botFreezeAppealUsername?: string;
freezeUntilDate?: number;
};
const FrozenAccountModal = ({
modal,
freezeUntilDate,
freezeAppealUrl,
botFreezeAppealUsername,
}: OwnProps & StateProps) => {
const {
closeFrozenAccountModal,
openUrl,
} = getActions();
const lang = useLang();
const isOpen = Boolean(modal);
const handleClose = useLastCallback(() => {
closeFrozenAccountModal();
});
const handleAppeal = useLastCallback(() => {
closeFrozenAccountModal();
if (freezeAppealUrl) {
openUrl({ url: freezeAppealUrl });
}
});
const header = useMemo(() => {
return (
<div className={styles.header}>
<AnimatedIconWithPreview
size={160}
tgsUrl={LOCAL_TGS_URLS.BannedDuck}
noLoop
/>
<div className={styles.title}>
{lang('FrozenAccountModalTitle')}
</div>
</div>
);
}, [lang]);
const footer = useMemo(() => {
if (!isOpen) return undefined;
return (
<div className={styles.footer}>
<Button
className={styles.buttonAppeal}
size="smaller"
onClick={handleAppeal}
noForcedUpperCase
>
{lang('ButtonAppeal')}
</Button>
<Button
isText
size="smaller"
onClick={handleClose}
noForcedUpperCase
>
{lang('ButtonUnderstood')}
</Button>
</div>
);
}, [lang, isOpen]);
if (!freezeUntilDate || !botFreezeAppealUsername) return undefined;
const date = new Date(freezeUntilDate * 1000);
const botLink = (
<Link onClick={handleAppeal} isPrimary>
{formatUsername(botFreezeAppealUsername)}
</Link>
);
const listItemData = [
['hand-stop', lang('FrozenAccountViolationTitle'), lang('FrozenAccountViolationSubtitle')],
['lock', lang('FrozenAccountReadOnlyTitle'), lang('FrozenAccountReadOnlySubtitle')],
['frozen-time', lang('FrozenAccountAppealTitle'),
lang('FrozenAccountAppealSubtitle', {
botLink,
date: formatDateToString(date, lang.code),
}, {
withNodes: true,
})],
] satisfies TableAboutData;
return (
<TableAboutModal
isOpen={isOpen}
header={header}
listItemData={listItemData}
footer={footer}
hasBackdrop
onClose={handleClose}
/>
);
};
export default memo(withGlobal<OwnProps>(
(global): StateProps => {
const freezeUntilDate = global.appConfig?.freezeUntilDate;
const freezeAppealUrl = global.appConfig?.freezeAppealUrl;
const botFreezeAppealId = global.botFreezeAppealId;
const botFreezeAppealUsername = botFreezeAppealId
? selectUser(global, botFreezeAppealId)?.usernames?.[0]?.username : undefined;
return {
freezeUntilDate,
freezeAppealUrl,
botFreezeAppealUsername,
};
},
)(FrozenAccountModal));

View File

@ -19,6 +19,7 @@ import { isChatChannel, isUserId } from '../../global/helpers';
import { getPeerTitle } from '../../global/helpers/peers';
import {
selectChat,
selectIsCurrentUserFrozen,
selectIsCurrentUserPremium,
selectPeer,
selectPeerPaidMessagesStars,
@ -104,6 +105,7 @@ interface StateProps {
stealthMode: ApiStealthMode;
withHeaderAnimation?: boolean;
paidMessagesStars?: number;
isAccountFrozen?: boolean;
}
const VIDEO_MIN_READY_STATE = IS_SAFARI ? 4 : 3;
@ -137,6 +139,7 @@ function Story({
onClose,
onReport,
paidMessagesStars,
isAccountFrozen,
}: OwnProps & StateProps) {
const {
viewStory,
@ -233,7 +236,7 @@ function Story({
? story.content.video.duration
: undefined;
const shouldShowComposer = !(isOut && isUserStory) && !isChangelog && !isChannelStory;
const shouldShowComposer = !(isOut && isUserStory) && !isChangelog && !isChannelStory && !isAccountFrozen;
const shouldShowFooter = isLoadedStory && !shouldShowComposer && (isOut || isChannelStory);
const headerAnimation = isMobile && withHeaderAnimation ? 'slideFade' : 'none';
@ -965,6 +968,7 @@ export default memo(withGlobal<OwnProps>((global, {
const fromPeer = isLoadedStory && story.fromId ? selectPeer(global, story.fromId) : undefined;
const paidMessagesStars = selectPeerPaidMessagesStars(global, peerId);
const isAccountFrozen = selectIsCurrentUserFrozen(global);
return {
peer: (user || chat)!,
@ -982,5 +986,6 @@ export default memo(withGlobal<OwnProps>((global, {
stealthMode: global.stories.stealthMode,
withHeaderAnimation,
paidMessagesStars,
isAccountFrozen,
};
})(Story));

View File

@ -23,6 +23,7 @@ import './ui/messages';
import './ui/globalSearch';
import './ui/middleSearch';
import './ui/stickerSearch';
import './ui/account';
import './ui/users';
import './ui/settings';
import './ui/misc';

View File

@ -16,6 +16,7 @@ import { ManagementProgress } from '../../../types';
import { BOT_FATHER_USERNAME, GENERAL_REFETCH_INTERVAL, PAID_SEND_DELAY } from '../../../config';
import { copyTextToClipboard } from '../../../util/clipboard';
import { getUsernameFromDeepLink } from '../../../util/deepLinkParser';
import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { getTranslationFn } from '../../../util/localization';
import { formatStarsAsText } from '../../../util/localization/format';
@ -55,6 +56,7 @@ import {
selectCurrentChat,
selectCurrentMessageList,
selectDraft,
selectIsCurrentUserFrozen,
selectIsTrustedBot,
selectMessageReplyInfo,
selectPeer,
@ -681,6 +683,11 @@ addActionHandler('requestMainWebView', async (global, actions, payload): Promise
tabId = getCurrentTabId(),
} = payload;
if (selectIsCurrentUserFrozen(global)) {
actions.openFrozenAccountModal({ tabId });
return;
}
if (checkIfOpenOrActivate(global, botId, tabId)) return;
const bot = selectUser(global, botId);
@ -1423,3 +1430,17 @@ addActionHandler('startBotFatherConversation', async (global, actions, payload):
actions.openChat({ id: botFatherId, tabId });
});
addActionHandler('loadBotFreezeAppeal', async (global): Promise<void> => {
const botUrl = global.appConfig?.freezeAppealUrl;
if (!botUrl) return;
const botAppealUsername = botUrl ? getUsernameFromDeepLink(botUrl) : undefined;
if (!botAppealUsername) return;
const chat = await fetchChatByUsername(global, botAppealUsername);
global = getGlobal();
global = {
...global,
botFreezeAppealId: chat?.id,
};
setGlobal(global);
});

View File

@ -113,6 +113,7 @@ import {
selectDraft,
selectIsChatPinned,
selectIsChatWithSelf,
selectIsCurrentUserFrozen,
selectLastServiceNotification,
selectPeer,
selectSimilarChannelIds,
@ -366,10 +367,11 @@ addActionHandler('openThread', async (global, actions, payload): Promise<void> =
});
}
const result = await callApi('fetchDiscussionMessage', {
chat: selectChat(global, loadingChatId)!,
messageId: Number(loadingThreadId),
});
const result = selectIsCurrentUserFrozen(global) ? undefined
: await callApi('fetchDiscussionMessage', {
chat: selectChat(global, loadingChatId)!,
messageId: Number(loadingThreadId),
});
global = getGlobal();
loadingThread = selectTabState(global, tabId).loadingThread;
@ -630,6 +632,11 @@ addActionHandler('updateChatMutedState', (global, actions, payload): ActionRetur
const { chatId, isMuted } = payload;
let { mutedUntil } = payload;
if (selectIsCurrentUserFrozen(global)) {
actions.openFrozenAccountModal({ tabId: getCurrentTabId() });
return;
}
const chat = selectChat(global, chatId);
if (!chat) {
return;
@ -911,6 +918,11 @@ addActionHandler('createGroupChat', async (global, actions, payload): Promise<vo
addActionHandler('toggleChatPinned', (global, actions, payload): ActionReturnType => {
const { id, folderId, tabId = getCurrentTabId() } = payload;
if (selectIsCurrentUserFrozen(global)) {
actions.openFrozenAccountModal({ tabId });
return;
}
const chat = selectChat(global, id);
if (!chat) {
return;
@ -958,6 +970,12 @@ addActionHandler('toggleChatPinned', (global, actions, payload): ActionReturnTyp
addActionHandler('toggleChatArchived', (global, actions, payload): ActionReturnType => {
const { id } = payload;
if (selectIsCurrentUserFrozen(global)) {
actions.openFrozenAccountModal({ tabId: getCurrentTabId() });
return;
}
const chat = selectChat(global, id);
if (chat) {
void callApi('toggleChatArchived', {
@ -969,6 +987,12 @@ addActionHandler('toggleChatArchived', (global, actions, payload): ActionReturnT
addActionHandler('toggleSavedDialogPinned', (global, actions, payload): ActionReturnType => {
const { id, tabId = getCurrentTabId() } = payload;
if (selectIsCurrentUserFrozen(global)) {
actions.openFrozenAccountModal({ tabId });
return;
}
const chat = selectChat(global, id);
if (!chat) {
return;
@ -1171,6 +1195,11 @@ addActionHandler('deleteChatFolder', async (global, actions, payload): Promise<v
addActionHandler('markChatUnread', (global, actions, payload): ActionReturnType => {
const { id } = payload;
if (selectIsCurrentUserFrozen(global)) {
actions.openFrozenAccountModal({ tabId: getCurrentTabId() });
return;
}
const chat = selectChat(global, id);
if (!chat) return;
void callApi('toggleDialogUnread', {
@ -1180,7 +1209,14 @@ addActionHandler('markChatUnread', (global, actions, payload): ActionReturnType
});
addActionHandler('markChatMessagesRead', async (global, actions, payload): Promise<void> => {
if (selectIsCurrentUserFrozen(global)) return;
const { id } = payload;
if (selectIsCurrentUserFrozen(global)) {
actions.openFrozenAccountModal({ tabId: getCurrentTabId() });
return;
}
const chat = selectChat(global, id);
if (!chat) return;
if (!chat.isForum) {
@ -1229,6 +1265,7 @@ addActionHandler('markChatRead', (global, actions, payload): ActionReturnType =>
});
addActionHandler('markTopicRead', (global, actions, payload): ActionReturnType => {
if (selectIsCurrentUserFrozen(global)) return;
const { chatId, topicId } = payload;
const chat = selectChat(global, chatId);
if (!chat) return;
@ -1784,6 +1821,8 @@ addActionHandler('updateChatMemberBannedRights', async (global, actions, payload
});
addActionHandler('updateChatAdmin', async (global, actions, payload): Promise<void> => {
if (selectIsCurrentUserFrozen(global)) return;
const {
chatId, userId, adminRights, customTitle,
tabId = getCurrentTabId(),
@ -1942,6 +1981,7 @@ addActionHandler('loadGroupsForDiscussion', async (global): Promise<void> => {
});
addActionHandler('linkDiscussionGroup', async (global, actions, payload): Promise<void> => {
if (selectIsCurrentUserFrozen(global)) return;
const { channelId, chatId, tabId = getCurrentTabId() } = payload || {};
const channel = selectChat(global, channelId);
@ -2023,6 +2063,7 @@ addActionHandler('resetOpenChatWithDraft', (global, actions, payload): ActionRet
});
addActionHandler('loadMoreMembers', async (global, actions, payload): Promise<void> => {
if (selectIsCurrentUserFrozen(global)) return;
const { tabId = getCurrentTabId() } = payload || {};
const { chatId } = selectCurrentMessageList(global, tabId) || {};
const chat = chatId ? selectChat(global, chatId) : undefined;
@ -2211,7 +2252,11 @@ addActionHandler('processAttachBotParameters', async (global, actions, payload):
});
addActionHandler('loadTopics', async (global, actions, payload): Promise<void> => {
if (selectIsCurrentUserFrozen(global)) return;
const { chatId, force } = payload;
if (selectIsCurrentUserFrozen(global)) {
return;
}
const chat = selectChat(global, chatId);
if (!chat) return;
@ -2780,6 +2825,8 @@ addActionHandler('setViewForumAsMessages', (global, actions, payload): ActionRet
});
addActionHandler('loadChannelRecommendations', async (global, actions, payload): Promise<void> => {
if (selectIsCurrentUserFrozen(global)) return;
const { chatId } = payload;
const chat = chatId ? selectChat(global, chatId) : undefined;
@ -2810,6 +2857,8 @@ addActionHandler('loadChannelRecommendations', async (global, actions, payload):
});
addActionHandler('loadBotRecommendations', async (global, actions, payload): Promise<void> => {
if (selectIsCurrentUserFrozen(global)) return;
const { userId } = payload;
const user = selectChat(global, userId);
@ -2915,20 +2964,21 @@ async function loadChats(
const isFirstBatch = !shouldIgnorePagination && !offsetPeer && !offsetDate && !offsetId;
const shouldReplaceStaleState = listType === 'active' && isFirstBatch;
const isAccountFreeze = selectIsCurrentUserFrozen(global);
const result = listType === 'saved' ? await callApi('fetchSavedChats', {
limit: CHAT_LIST_LOAD_SLICE,
offsetDate,
offsetId,
offsetPeer,
withPinned: isFirstBatch,
withPinned: isFirstBatch && !isAccountFreeze,
}) : await callApi('fetchChats', {
limit: CHAT_LIST_LOAD_SLICE,
offsetDate,
offsetId,
offsetPeer,
archived: listType === 'archived',
withPinned: isFirstBatch,
withPinned: isFirstBatch && !isAccountFreeze,
lastLocalServiceMessageId,
});
@ -3002,6 +3052,7 @@ async function loadChats(
export async function loadFullChat<T extends GlobalState>(
global: T, actions: RequiredGlobalActions, chat: ApiChat,
) {
if (selectIsCurrentUserFrozen(global)) return undefined;
const result = await callApi('fetchFullChat', chat);
if (!result) {
return undefined;

View File

@ -10,7 +10,8 @@ import {
updateChat, updateChatFullInfo, updateManagement, updateManagementProgress, updateUserFullInfo,
} from '../../reducers';
import {
selectChat, selectCurrentMessageList, selectTabState, selectUser,
selectChat, selectCurrentMessageList, selectIsCurrentUserFrozen,
selectTabState, selectUser,
} from '../../selectors';
import { ensureIsSuperGroup } from './chats';
@ -109,6 +110,8 @@ addActionHandler('setOpenedInviteInfo', (global, actions, payload): ActionReturn
});
addActionHandler('loadExportedChatInvites', async (global, actions, payload): Promise<void> => {
if (selectIsCurrentUserFrozen(global)) return;
const {
chatId, adminId, isRevoked, limit, tabId = getCurrentTabId(),
} = payload!;

View File

@ -120,6 +120,7 @@ import {
selectForwardsContainVoiceMessages,
selectIsChatBotNotStarted,
selectIsChatWithSelf,
selectIsCurrentUserFrozen,
selectIsCurrentUserPremium,
selectLanguageCode,
selectListedIds,
@ -994,6 +995,7 @@ addActionHandler('reportChannelSpam', (global, actions, payload): ActionReturnTy
});
addActionHandler('markMessageListRead', (global, actions, payload): ActionReturnType => {
if (selectIsCurrentUserFrozen(global)) return undefined;
const { maxId, tabId = getCurrentTabId() } = payload!;
const currentMessageList = selectCurrentMessageList(global, tabId);
@ -1177,6 +1179,8 @@ addActionHandler('loadExtendedMedia', (global, actions, payload): ActionReturnTy
});
addActionHandler('loadScheduledHistory', async (global, actions, payload): Promise<void> => {
if (selectIsCurrentUserFrozen(global)) return;
const { chatId } = payload;
const chat = selectChat(global, chatId);
if (!chat) {
@ -1852,6 +1856,8 @@ addActionHandler('loadSendPaidReactionsAs', async (global, actions, payload): Pr
});
addActionHandler('loadSponsoredMessages', async (global, actions, payload): Promise<void> => {
if (selectIsCurrentUserFrozen(global)) return;
const { peerId } = payload;
const peer = selectPeer(global, peerId);
if (!peer) {
@ -1880,7 +1886,7 @@ addActionHandler('viewSponsoredMessage', (global, actions, payload): ActionRetur
return;
}
void callApi('viewSponsoredMessage', { peer, random: message.randomId });
void callApi('viewSponsoredMessage', { random: message.randomId });
});
addActionHandler('clickSponsoredMessage', (global, actions, payload): ActionReturnType => {
@ -1892,7 +1898,7 @@ addActionHandler('clickSponsoredMessage', (global, actions, payload): ActionRetu
}
void callApi('clickSponsoredMessage', {
peer, random: message.randomId, isMedia, isFullscreen,
random: message.randomId, isMedia, isFullscreen,
});
});
@ -1905,7 +1911,7 @@ addActionHandler('reportSponsoredMessage', async (global, actions, payload): Pro
return;
}
const result = await callApi('reportSponsoredMessage', { peer, randomId, option });
const result = await callApi('reportSponsoredMessage', { randomId, option });
if (!result) return;
@ -2352,6 +2358,8 @@ addActionHandler('scheduleForViewsIncrement', (global, actions, payload): Action
addActionHandler('loadMessageViews', async (global, actions, payload): Promise<void> => {
const { chatId, ids, shouldIncrement } = payload;
if (selectIsCurrentUserFrozen(global)) return;
const chat = selectChat(global, chatId);
if (!chat) return;

View File

@ -40,6 +40,7 @@ import { updateTabState } from '../../reducers/tabs';
import {
selectChat,
selectChatFullInfo,
selectIsCurrentUserFrozen,
selectPaymentInputInvoice,
selectPaymentRequestId,
selectProviderPublicToken,
@ -530,6 +531,11 @@ addActionHandler('openGiftModal', async (global, actions, payload): Promise<void
forUserId, tabId = getCurrentTabId(),
} = payload;
if (selectIsCurrentUserFrozen(global)) {
actions.openFrozenAccountModal({ tabId });
return;
}
const gifts = await callApi('getPremiumGiftCodeOptions', {});
if (!gifts) return;
@ -549,6 +555,11 @@ addActionHandler('openStarsGiftModal', async (global, actions, payload): Promise
tabId = getCurrentTabId(),
} = payload || {};
if (selectIsCurrentUserFrozen(global)) {
actions.openFrozenAccountModal({ tabId });
return;
}
const starsGiftOptions = await callApi('getStarsGiftOptions', {});
global = getGlobal();

View File

@ -31,6 +31,7 @@ import {
selectCurrentChat,
selectDefaultReaction,
selectIsChatWithSelf,
selectIsCurrentUserFrozen,
selectMaxUserReactions,
selectMessageIdsByGroupId,
selectPerformanceSettingsValue,
@ -391,6 +392,8 @@ addActionHandler('stopActiveEmojiInteraction', (global, actions, payload): Actio
});
addActionHandler('loadReactors', async (global, actions, payload): Promise<void> => {
if (selectIsCurrentUserFrozen(global)) return;
const { chatId, messageId, reaction } = payload;
const chat = selectChat(global, chatId);
const message = selectChatMessage(global, chatId, messageId);
@ -418,6 +421,8 @@ addActionHandler('loadReactors', async (global, actions, payload): Promise<void>
});
addActionHandler('loadMessageReactions', (global, actions, payload): ActionReturnType => {
if (selectIsCurrentUserFrozen(global)) return;
const { ids, chatId } = payload;
const chat = selectChat(global, chatId);

View File

@ -22,7 +22,8 @@ import {
} from '../../reducers';
import { updateTabState } from '../../reducers/tabs';
import {
selectChat, selectTabState, selectUser,
selectChat, selectIsCurrentUserFrozen,
selectTabState, selectUser,
} from '../../selectors';
import { selectSharedSettings } from '../../selectors/sharedState';
@ -390,6 +391,8 @@ addActionHandler('loadLanguages', async (global): Promise<void> => {
});
addActionHandler('loadPrivacySettings', async (global): Promise<void> => {
if (selectIsCurrentUserFrozen(global)) return;
const result = await Promise.all([
callApi('fetchPrivacySettings', 'phoneNumber'),
callApi('fetchPrivacySettings', 'addByPhone'),
@ -574,6 +577,8 @@ addActionHandler('updateIsOnline', (global, actions, payload): ActionReturnType
});
addActionHandler('loadContentSettings', async (global): Promise<void> => {
if (selectIsCurrentUserFrozen(global)) return;
const result = await callApi('fetchContentSettings');
if (!result) return;
@ -644,6 +649,7 @@ addActionHandler('loadAppConfig', async (global, actions, payload): Promise<void
global = {
...global,
appConfig,
isAppConfigLoaded: true,
};
setGlobal(global);
});

View File

@ -27,6 +27,7 @@ import {
} from '../../reducers';
import { updateTabState } from '../../reducers/tabs';
import {
selectIsCurrentUserFrozen,
selectPeer, selectPeerStories, selectPeerStory,
selectPinnedStories, selectTabState,
} from '../../selectors';
@ -278,6 +279,8 @@ addActionHandler('toggleStoryPinnedToTop', async (global, actions, payload): Pro
});
addActionHandler('loadPeerStories', async (global, actions, payload): Promise<void> => {
if (selectIsCurrentUserFrozen(global)) return;
const { peerId } = payload;
const peer = selectPeer(global, peerId);
if (!peer) return;
@ -296,6 +299,8 @@ addActionHandler('loadPeerStories', async (global, actions, payload): Promise<vo
});
addActionHandler('loadPeerProfileStories', async (global, actions, payload): Promise<void> => {
if (selectIsCurrentUserFrozen(global)) return;
const { peerId, offsetId } = payload;
const peer = selectPeer(global, peerId);
let peerStories = selectPeerStories(global, peerId);
@ -320,6 +325,8 @@ addActionHandler('loadPeerProfileStories', async (global, actions, payload): Pro
});
addActionHandler('loadStoriesArchive', async (global, actions, payload): Promise<void> => {
if (selectIsCurrentUserFrozen(global)) return;
const { peerId, offsetId } = payload;
const peer = selectPeer(global, peerId);
let peerStories = selectPeerStories(global, peerId);

View File

@ -29,7 +29,9 @@ import {
updateStickersForEmoji,
} from '../../reducers';
import { updateTabState } from '../../reducers/tabs';
import { selectIsCurrentUserPremium, selectStickerSet, selectTabState } from '../../selectors';
import {
selectIsCurrentUserFrozen, selectIsCurrentUserPremium, selectStickerSet, selectTabState,
} from '../../selectors';
import { selectCurrentLimit, selectPremiumLimit } from '../../selectors/limits';
const ADDED_SETS_THROTTLE = 200;
@ -128,6 +130,10 @@ addActionHandler('loadFavoriteStickers', async (global): Promise<void> => {
addActionHandler('loadPremiumStickers', async (global): Promise<void> => {
const { hash } = global.stickers.premium || {};
if (selectIsCurrentUserFrozen(global)) {
return;
}
const result = await callApi('fetchStickersForEmoji', { emoji: '⭐️⭐️', hash });
if (!result) {
return;
@ -151,6 +157,10 @@ addActionHandler('loadPremiumStickers', async (global): Promise<void> => {
addActionHandler('loadGreetingStickers', async (global): Promise<void> => {
const { hash } = global.stickers.greeting || {};
if (selectIsCurrentUserFrozen(global)) {
return;
}
const greeting = await callApi('fetchStickersForEmoji', { emoji: '👋⭐️', hash });
if (!greeting) {
return;

View File

@ -66,7 +66,7 @@ addActionHandler('sync', (global, actions): ActionReturnType => {
}, RELEASE_STATUS_TIMEOUT);
const {
loadAllChats, preloadTopChatMessages, loadAllStories, loadAllHiddenStories,
loadAllChats, preloadTopChatMessages,
} = actions;
initFolderManager();
@ -91,10 +91,7 @@ addActionHandler('sync', (global, actions): ActionReturnType => {
}
loadAllChats({ listType: 'archived' });
loadAllChats({ listType: 'saved' });
preloadTopChatMessages();
loadAllStories();
loadAllHiddenStories();
},
});
});

View File

@ -30,6 +30,7 @@ import { updateTabState } from '../../reducers/tabs';
import {
selectChat,
selectChatFullInfo,
selectIsCurrentUserFrozen,
selectIsCurrentUserPremium,
selectPeer,
selectPeerPhotos,
@ -159,6 +160,11 @@ addActionHandler('loadCurrentUser', (): ActionReturnType => {
addActionHandler('loadCommonChats', async (global, actions, payload): Promise<void> => {
const { userId } = payload;
if (selectIsCurrentUserFrozen(global)) {
return;
}
const user = selectUser(global, userId);
const commonChats = selectUserCommonChats(global, userId);
if (!user || isUserBot(user) || commonChats?.isFullyLoaded) {
@ -294,6 +300,8 @@ addActionHandler('deleteContact', async (global, actions, payload): Promise<void
});
addActionHandler('loadMoreProfilePhotos', async (global, actions, payload): Promise<void> => {
if (selectIsCurrentUserFrozen(global)) return;
const { peerId, shouldInvalidateCache, isPreload } = payload;
const isPrivate = isUserId(peerId);
@ -550,6 +558,8 @@ addActionHandler('openSuggestedStatusModal', async (global, actions, payload): P
addActionHandler('loadPeerSettings', async (global, actions, payload): Promise<void> => {
const { peerId } = payload;
if (selectIsCurrentUserFrozen(global)) return;
const userFullInfo = selectUserFullInfo(global, peerId);
if (!userFullInfo) {
actions.loadFullUser({ userId: peerId });

View File

@ -95,6 +95,19 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
break;
}
case 'notSupportedInFrozenAccount': {
actions.showNotification({
title: {
key: 'NotificationTitleNotSupportedInFrozenAccount',
},
message: {
key: 'NotificationMessageNotSupportedInFrozenAccount',
},
tabId: getCurrentTabId(),
});
break;
}
}
});

View File

@ -0,0 +1,21 @@
import type { ActionReturnType } from '../../types';
import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { addActionHandler } from '../..';
import { updateTabState } from '../../reducers/tabs';
addActionHandler('openFrozenAccountModal', (global, actions, payload): ActionReturnType => {
const { tabId = getCurrentTabId() } = payload || {};
return updateTabState(global, {
isFrozenAccountModalOpen: true,
}, tabId);
});
addActionHandler('closeFrozenAccountModal', (global, actions, payload): ActionReturnType => {
const { tabId = getCurrentTabId() } = payload || {};
return updateTabState(global, {
isFrozenAccountModalOpen: false,
}, tabId);
});

View File

@ -21,7 +21,8 @@ import {
import { updateGroupCall } from '../../reducers/calls';
import { updateTabState } from '../../reducers/tabs';
import {
selectChat, selectChatFullInfo, selectTabState, selectUser,
selectChat, selectChatFullInfo, selectIsCurrentUserFrozen,
selectTabState, selectUser,
} from '../../selectors';
import { selectActiveGroupCall, selectChatGroupCall, selectGroupCall } from '../../selectors/calls';
import { fetchChatByUsername, loadFullChat } from '../api/chats';
@ -90,6 +91,7 @@ export function initializeSounds() {
}
async function fetchGroupCall<T extends GlobalState>(global: T, groupCall: Partial<ApiGroupCall>) {
if (selectIsCurrentUserFrozen(global)) return undefined;
const result = await callApi('getGroupCall', {
call: groupCall,
});
@ -130,6 +132,8 @@ addActionHandler('toggleGroupCallPanel', (global, actions, payload): ActionRetur
});
addActionHandler('subscribeToGroupCallUpdates', async (global, actions, payload): Promise<void> => {
if (selectIsCurrentUserFrozen(global)) return;
const { subscribed, id } = payload!;
const groupCall = selectGroupCall(global, id);

View File

@ -10,7 +10,9 @@ import {
clearStarPayment, openStarsTransactionModal,
} from '../../reducers';
import { updateTabState } from '../../reducers/tabs';
import { selectChatMessage, selectStarsPayment, selectTabState } from '../../selectors';
import {
selectChatMessage, selectIsCurrentUserFrozen, selectStarsPayment, selectTabState,
} from '../../selectors';
addActionHandler('processOriginStarsPayment', (global, actions, payload): ActionReturnType => {
const { originData, status, tabId = getCurrentTabId() } = payload;
@ -59,6 +61,11 @@ addActionHandler('openGiftRecipientPicker', (global, actions, payload): ActionRe
tabId = getCurrentTabId(),
} = payload || {};
if (selectIsCurrentUserFrozen(global)) {
actions.openFrozenAccountModal({ tabId });
return global;
}
return updateTabState(global, {
isGiftRecipientPickerOpen: true,
}, tabId);

View File

@ -5,6 +5,7 @@ import { addTabStateResetterAction } from '../../helpers/meta';
import { addActionHandler } from '../../index';
import { closeNewContactDialog, updateUserSearch } from '../../reducers';
import { updateTabState } from '../../reducers/tabs';
import { selectIsCurrentUserFrozen } from '../../selectors';
addActionHandler('setUserSearchQuery', (global, actions, payload): ActionReturnType => {
const {
@ -23,6 +24,11 @@ addActionHandler('setUserSearchQuery', (global, actions, payload): ActionReturnT
addActionHandler('openAddContactDialog', (global, actions, payload): ActionReturnType => {
const { userId, tabId = getCurrentTabId() } = payload;
if (selectIsCurrentUserFrozen(global)) {
actions.openFrozenAccountModal({ tabId });
return global;
}
return updateTabState(global, {
newContact: { userId },
}, tabId);
@ -31,6 +37,11 @@ addActionHandler('openAddContactDialog', (global, actions, payload): ActionRetur
addActionHandler('openNewContactDialog', (global, actions, payload): ActionReturnType => {
const { tabId = getCurrentTabId() } = payload || {};
if (selectIsCurrentUserFrozen(global)) {
actions.openFrozenAccountModal({ tabId });
return global;
}
return updateTabState(global, {
newContact: {
isByPhoneNumber: true,

View File

@ -35,6 +35,10 @@ export function selectIsCurrentUserPremium<T extends GlobalState>(global: T) {
return Boolean(global.users.byId[global.currentUserId].isPremium);
}
export function selectIsCurrentUserFrozen<T extends GlobalState>(global: T) {
return Boolean(global.appConfig?.freezeUntilDate);
}
export function selectIsPremiumPurchaseBlocked<T extends GlobalState>(global: T) {
return global.appConfig?.isPremiumPurchaseBlocked ?? true;
}

View File

@ -1026,6 +1026,8 @@ export interface ActionPayloads {
changeSessionTtl: {
days: number;
};
openFrozenAccountModal: WithTabId | undefined;
closeFrozenAccountModal: WithTabId | undefined;
// Chats
loadPeerSettings: {
@ -1713,6 +1715,7 @@ export interface ActionPayloads {
startBotFatherConversation: {
param: string;
} & WithTabId;
loadBotFreezeAppeal: undefined;
checkUsername: {
username: string;
} & WithTabId;

View File

@ -84,6 +84,7 @@ export type GlobalState = {
connectionState?: ApiUpdateConnectionStateType;
currentUserId?: string;
isSyncing?: boolean;
isAppConfigLoaded?: boolean;
isAppUpdateAvailable?: boolean;
isElectronUpdateAvailable?: boolean;
isSynced?: boolean;
@ -92,6 +93,7 @@ export type GlobalState = {
lastIsChatInfoShown?: boolean;
initialUnreadNotifications?: number;
shouldShowContextMenuHint?: boolean;
botFreezeAppealId?: string;
audioPlayer: {
lastPlaybackRate: number;

View File

@ -618,6 +618,8 @@ export type TabState = {
isGiftRecipientPickerOpen?: boolean;
isFrozenAccountModalOpen?: boolean;
starsGiftingPickerModal?: {
isOpen?: boolean;
};

View File

@ -12,5 +12,5 @@ for (const tl of Object.values(Api)) {
}
}
export const LAYER = 200;
export const LAYER = 201;
export { tlobjects };

File diff suppressed because one or more lines are too long

View File

@ -159,6 +159,8 @@ messageActionGiftStars#45d5b021 flags:# currency:string amount:long stars:long c
messageActionPrizeStars#b00c47a2 flags:# unclaimed:flags.0?true stars:long transaction_id:string boost_peer:Peer giveaway_msg_id:int = MessageAction;
messageActionStarGift#4717e8a4 flags:# name_hidden:flags.0?true saved:flags.2?true converted:flags.3?true upgraded:flags.5?true refunded:flags.9?true can_upgrade:flags.10?true gift:StarGift message:flags.1?TextWithEntities convert_stars:flags.4?long upgrade_msg_id:flags.5?int upgrade_stars:flags.8?long from_id:flags.11?Peer peer:flags.12?Peer saved_id:flags.12?long = MessageAction;
messageActionStarGiftUnique#acdfcb81 flags:# upgrade:flags.0?true transferred:flags.1?true saved:flags.2?true refunded:flags.5?true gift:StarGift can_export_at:flags.3?int transfer_stars:flags.4?long from_id:flags.6?Peer peer:flags.7?Peer saved_id:flags.7?long = MessageAction;
messageActionPaidMessagesRefunded#ac1f1fcd count:int stars:long = MessageAction;
messageActionPaidMessagesPrice#bcd71419 stars:long = MessageAction;
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
photoEmpty#2331b22d id:long = Photo;
@ -173,6 +175,7 @@ geoPointEmpty#1117dd5f = GeoPoint;
geoPoint#b2a2f663 flags:# long:double lat:double access_hash:long accuracy_radius:flags.0?int = GeoPoint;
auth.sentCode#5e002502 flags:# type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int = auth.SentCode;
auth.sentCodeSuccess#2390fe44 authorization:auth.Authorization = auth.SentCode;
auth.sentCodePaymentRequired#d7cef980 store_product:string phone_code_hash:string = auth.SentCode;
auth.authorization#2ea2c0d4 flags:# setup_password_required:flags.1?true otherwise_relogin_days:flags.1?int tmp_sessions:flags.0?int future_auth_token:flags.2?bytes user:User = auth.Authorization;
auth.authorizationSignUpRequired#44747e9a flags:# terms_of_service:flags.0?help.TermsOfService = auth.Authorization;
auth.exportedAuthorization#b434e2b8 id:long bytes:bytes = auth.ExportedAuthorization;
@ -196,7 +199,7 @@ inputReportReasonGeoIrrelevant#dbd4feed = ReportReason;
inputReportReasonFake#f5ddd6e7 = ReportReason;
inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
inputReportReasonPersonalDetails#9ec7863d = ReportReason;
userFull#d2234ea0 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true can_view_revenue:flags2.9?true bot_can_manage_emoji_status:flags2.10?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int stargifts_count:flags2.8?int starref_program:flags2.11?StarRefProgram bot_verification:flags2.12?BotVerification send_paid_messages_stars:flags2.14?long = UserFull;
userFull#99e78045 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true can_view_revenue:flags2.9?true bot_can_manage_emoji_status:flags2.10?true display_gifts_button:flags2.16?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int stargifts_count:flags2.8?int starref_program:flags2.11?StarRefProgram bot_verification:flags2.12?BotVerification send_paid_messages_stars:flags2.14?long disallowed_gifts:flags2.15?DisallowedGiftsSettings = UserFull;
contact#145ade0b user_id:long mutual:Bool = Contact;
importedContact#c13e3c50 user_id:long client_id:long = ImportedContact;
contactStatus#16d9703b user_id:long status:UserStatus = ContactStatus;
@ -374,6 +377,7 @@ updateBusinessBotCallbackQuery#1ea2fda7 flags:# query_id:long user_id:long conne
updateStarsRevenueStatus#a584b019 peer:Peer status:StarsRevenueStatus = Update;
updateBotPurchasedPaidMedia#283bd312 user_id:long payload:string qts:int = Update;
updatePaidReactionPrivacy#8b725fce private:PaidReactionPrivacy = Update;
updateSentPhoneCode#504aa18f sent_code:auth.SentCode = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
updates.differenceEmpty#5d75a138 date:int seq:int = updates.Difference;
updates.difference#f49ca0 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> state:updates.State = updates.Difference;
@ -746,7 +750,6 @@ phoneCallDiscardReasonMissed#85e42301 = PhoneCallDiscardReason;
phoneCallDiscardReasonDisconnect#e095c1a0 = PhoneCallDiscardReason;
phoneCallDiscardReasonHangup#57adc690 = PhoneCallDiscardReason;
phoneCallDiscardReasonBusy#faf7e8c9 = PhoneCallDiscardReason;
phoneCallDiscardReasonAllowGroupCall#afe2b839 encrypted_key:bytes = PhoneCallDiscardReason;
dataJSON#7d748d04 data:string = DataJSON;
labeledPrice#cb296bf8 label:string amount:long = LabeledPrice;
invoice#49ee584 flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true recurring:flags.9?true currency:string prices:Vector<LabeledPrice> max_tip_amount:flags.8?long suggested_tip_amounts:flags.8?Vector<long> terms_url:flags.10?string subscription_period:flags.11?int = Invoice;
@ -1025,7 +1028,7 @@ statsGroupTopPoster#9d04af9b user_id:long messages:int avg_chars:int = StatsGrou
statsGroupTopAdmin#d7584c87 user_id:long deleted:int kicked:int banned:int = StatsGroupTopAdmin;
statsGroupTopInviter#535f779d user_id:long invitations:int = StatsGroupTopInviter;
stats.megagroupStats#ef7ff916 period:StatsDateRangeDays members:StatsAbsValueAndPrev messages:StatsAbsValueAndPrev viewers:StatsAbsValueAndPrev posters:StatsAbsValueAndPrev growth_graph:StatsGraph members_graph:StatsGraph new_members_by_source_graph:StatsGraph languages_graph:StatsGraph messages_graph:StatsGraph actions_graph:StatsGraph top_hours_graph:StatsGraph weekdays_graph:StatsGraph top_posters:Vector<StatsGroupTopPoster> top_admins:Vector<StatsGroupTopAdmin> top_inviters:Vector<StatsGroupTopInviter> users:Vector<User> = stats.MegagroupStats;
globalPrivacySettings#c9d8df1c flags:# archive_and_mute_new_noncontact_peers:flags.0?true keep_archived_unmuted:flags.1?true keep_archived_folders:flags.2?true hide_read_marks:flags.3?true new_noncontact_peers_require_premium:flags.4?true noncontact_peers_paid_stars:flags.5?long = GlobalPrivacySettings;
globalPrivacySettings#fe41b34f flags:# archive_and_mute_new_noncontact_peers:flags.0?true keep_archived_unmuted:flags.1?true keep_archived_folders:flags.2?true hide_read_marks:flags.3?true new_noncontact_peers_require_premium:flags.4?true display_gifts_button:flags.7?true noncontact_peers_paid_stars:flags.5?long disallowed_gifts:flags.6?DisallowedGiftsSettings = GlobalPrivacySettings;
help.countryCode#4203c5ef flags:# country_code:string prefixes:flags.0?Vector<string> patterns:flags.1?Vector<string> = help.CountryCode;
help.country#c3878e23 flags:# hidden:flags.0?true iso2:string default_name:string name:flags.1?string country_codes:Vector<help.CountryCode> = help.Country;
help.countriesListNotModified#93cc1f32 = help.CountriesList;
@ -1130,6 +1133,7 @@ inputInvoiceStarGift#e8625e92 flags:# hide_name:flags.0?true include_upgrade:fla
inputInvoiceStarGiftUpgrade#4d818d5d flags:# keep_original_details:flags.0?true stargift:InputSavedStarGift = InputInvoice;
inputInvoiceStarGiftTransfer#4a5f5bd9 stargift:InputSavedStarGift to_id:InputPeer = InputInvoice;
inputInvoicePremiumGiftStars#dabab2ef flags:# user_id:InputUser months:int message:flags.0?TextWithEntities = InputInvoice;
inputInvoiceBusinessBotTransferStars#f4997e42 bot:InputUser stars:long = InputInvoice;
payments.exportedInvoice#aed0cbd9 url:string = payments.ExportedInvoice;
messages.transcribedAudio#cfb9d957 flags:# pending:flags.0?true transcription_id:long text:string trial_remains_num:flags.1?int trial_remains_until_date:flags.1?int = messages.TranscribedAudio;
help.premiumPromo#5334759c status_text:string status_entities:Vector<MessageEntity> video_sections:Vector<string> videos:Vector<Document> period_options:Vector<PremiumSubscriptionOption> users:Vector<User> = help.PremiumPromo;
@ -1140,6 +1144,7 @@ inputStorePaymentPremiumGiveaway#160544ca flags:# only_new_subscribers:flags.0?t
inputStorePaymentStarsTopup#dddd0f56 stars:long currency:string amount:long = InputStorePaymentPurpose;
inputStorePaymentStarsGift#1d741ef7 user_id:InputUser stars:long currency:string amount:long = InputStorePaymentPurpose;
inputStorePaymentStarsGiveaway#751f08fa flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.3?true stars:long boost_peer:InputPeer additional_peers:flags.1?Vector<InputPeer> countries_iso2:flags.2?Vector<string> prize_description:flags.4?string random_id:long until_date:int currency:string amount:long users:int = InputStorePaymentPurpose;
inputStorePaymentAuthCode#9bb2636d flags:# restore:flags.0?true phone_number:string phone_code_hash:string currency:string amount:long = InputStorePaymentPurpose;
paymentFormMethod#88f8f21b url:string title:string = PaymentFormMethod;
emojiStatusEmpty#2de11aae = EmojiStatus;
emojiStatus#e7ff068a flags:# document_id:long until:flags.0?int = EmojiStatus;
@ -1298,11 +1303,11 @@ inputQuickReplyShortcut#24596d41 shortcut:string = InputQuickReplyShortcut;
inputQuickReplyShortcutId#1190cf1 shortcut_id:int = InputQuickReplyShortcut;
messages.quickReplies#c68d6695 quick_replies:Vector<QuickReply> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.QuickReplies;
messages.quickRepliesNotModified#5f91eb5b = messages.QuickReplies;
connectedBot#bd068601 flags:# can_reply:flags.0?true bot_id:long recipients:BusinessBotRecipients = ConnectedBot;
connectedBot#cd64636c flags:# bot_id:long recipients:BusinessBotRecipients rights:BusinessBotRights = ConnectedBot;
account.connectedBots#17d7f87b connected_bots:Vector<ConnectedBot> users:Vector<User> = account.ConnectedBots;
messages.dialogFilters#2ad93719 flags:# tags_enabled:flags.0?true filters:Vector<DialogFilter> = messages.DialogFilters;
birthday#6c8e1e06 flags:# day:int month:int year:flags.0?int = Birthday;
botBusinessConnection#896433b4 flags:# can_reply:flags.0?true disabled:flags.1?true connection_id:string user_id:long dc_id:int date:int = BotBusinessConnection;
botBusinessConnection#8f34b2f5 flags:# disabled:flags.1?true connection_id:string user_id:long dc_id:int date:int rights:flags.2?BusinessBotRights = BotBusinessConnection;
inputBusinessIntro#9c469cd flags:# title:string description:string sticker:flags.0?InputDocument = InputBusinessIntro;
businessIntro#5a0a066d flags:# title:string description:string sticker:flags.0?Document = BusinessIntro;
messages.myStickers#faff629d count:int sets:Vector<StickerSetCovered> = messages.MyStickers;
@ -1409,6 +1414,11 @@ account.paidMessagesRevenue#1e109708 stars_amount:long = account.PaidMessagesRev
requirementToContactEmpty#50a9839 = RequirementToContact;
requirementToContactPremium#e581e4e9 = RequirementToContact;
requirementToContactPaidMessages#b4f67e93 stars_amount:long = RequirementToContact;
businessBotRights#a0624cf7 flags:# reply:flags.0?true read_messages:flags.1?true delete_sent_messages:flags.2?true delete_received_messages:flags.3?true edit_name:flags.4?true edit_bio:flags.5?true edit_profile_photo:flags.6?true edit_username:flags.7?true view_gifts:flags.8?true sell_gifts:flags.9?true change_gift_settings:flags.10?true transfer_and_upgrade_gifts:flags.11?true transfer_stars:flags.12?true manage_stories:flags.13?true = BusinessBotRights;
disallowedGiftsSettings#71f276c4 flags:# disallow_unlimited_stargifts:flags.0?true disallow_limited_stargifts:flags.1?true disallow_unique_stargifts:flags.2?true disallow_premium_gifts:flags.3?true = DisallowedGiftsSettings;
sponsoredPeer#c69708d3 flags:# random_id:bytes peer:Peer sponsor_info:flags.0?string additional_info:flags.1?string = SponsoredPeer;
contacts.sponsoredPeersEmpty#ea32b4b1 = contacts.SponsoredPeers;
contacts.sponsoredPeers#eb032884 peers:Vector<SponsoredPeer> chats:Vector<Chat> users:Vector<User> = contacts.SponsoredPeers;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
initConnection#c1cd5ea9 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy params:flags.1?JSONValue query:!X = X;
@ -1476,7 +1486,6 @@ account.addNoPaidMessagesException#6f688aa7 flags:# refund_charged:flags.0?true
account.getPaidMessagesRevenue#f1266f38 user_id:InputUser = account.PaidMessagesRevenue;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
users.getRequirementsToContact#d89a83a3 id:Vector<InputUser> = Vector<RequirementToContact>;
contacts.getContacts#5dd69e12 hash:long = contacts.Contacts;
contacts.importContacts#2c800be5 contacts:Vector<InputContact> = contacts.ImportedContacts;
contacts.deleteContacts#96a0e00 id:Vector<InputUser> = Updates;
@ -1629,9 +1638,9 @@ messages.getFactCheck#b9cdc5ee peer:InputPeer msg_id:Vector<int> = Vector<FactCh
messages.requestMainWebView#c9e01e7b flags:# compact:flags.7?true fullscreen:flags.8?true peer:InputPeer bot:InputUser start_param:flags.1?string theme_params:flags.0?DataJSON platform:string = WebViewResult;
messages.sendPaidReaction#58bbcb50 flags:# peer:InputPeer msg_id:int count:int random_id:long private:flags.0?PaidReactionPrivacy = Updates;
messages.getPaidReactionPrivacy#472455aa = Updates;
messages.viewSponsoredMessage#673ad8f1 peer:InputPeer random_id:bytes = Bool;
messages.clickSponsoredMessage#f093465 flags:# media:flags.0?true fullscreen:flags.1?true peer:InputPeer random_id:bytes = Bool;
messages.reportSponsoredMessage#1af3dbb8 peer:InputPeer random_id:bytes option:bytes = channels.SponsoredMessageReportResult;
messages.viewSponsoredMessage#269e3643 random_id:bytes = Bool;
messages.clickSponsoredMessage#8235057e flags:# media:flags.0?true fullscreen:flags.1?true random_id:bytes = Bool;
messages.reportSponsoredMessage#12cbf0c4 random_id:bytes option:bytes = channels.SponsoredMessageReportResult;
messages.getSponsoredMessages#9bd2f439 peer:InputPeer = messages.SponsoredMessages;
messages.getPreparedInlineMessage#857ebdb8 bot:InputUser id:string = messages.PreparedInlineMessage;
messages.reportMessagesDelivery#5a6d7395 flags:# push:flags.0?true peer:InputPeer id:Vector<int> = Bool;

View File

@ -185,6 +185,8 @@ messageActionGiftStars#45d5b021 flags:# currency:string amount:long stars:long c
messageActionPrizeStars#b00c47a2 flags:# unclaimed:flags.0?true stars:long transaction_id:string boost_peer:Peer giveaway_msg_id:int = MessageAction;
messageActionStarGift#4717e8a4 flags:# name_hidden:flags.0?true saved:flags.2?true converted:flags.3?true upgraded:flags.5?true refunded:flags.9?true can_upgrade:flags.10?true gift:StarGift message:flags.1?TextWithEntities convert_stars:flags.4?long upgrade_msg_id:flags.5?int upgrade_stars:flags.8?long from_id:flags.11?Peer peer:flags.12?Peer saved_id:flags.12?long = MessageAction;
messageActionStarGiftUnique#acdfcb81 flags:# upgrade:flags.0?true transferred:flags.1?true saved:flags.2?true refunded:flags.5?true gift:StarGift can_export_at:flags.3?int transfer_stars:flags.4?long from_id:flags.6?Peer peer:flags.7?Peer saved_id:flags.7?long = MessageAction;
messageActionPaidMessagesRefunded#ac1f1fcd count:int stars:long = MessageAction;
messageActionPaidMessagesPrice#bcd71419 stars:long = MessageAction;
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
@ -204,6 +206,7 @@ geoPoint#b2a2f663 flags:# long:double lat:double access_hash:long accuracy_radiu
auth.sentCode#5e002502 flags:# type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int = auth.SentCode;
auth.sentCodeSuccess#2390fe44 authorization:auth.Authorization = auth.SentCode;
auth.sentCodePaymentRequired#d7cef980 store_product:string phone_code_hash:string = auth.SentCode;
auth.authorization#2ea2c0d4 flags:# setup_password_required:flags.1?true otherwise_relogin_days:flags.1?int tmp_sessions:flags.0?int future_auth_token:flags.2?bytes user:User = auth.Authorization;
auth.authorizationSignUpRequired#44747e9a flags:# terms_of_service:flags.0?help.TermsOfService = auth.Authorization;
@ -236,7 +239,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason;
inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
inputReportReasonPersonalDetails#9ec7863d = ReportReason;
userFull#d2234ea0 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true can_view_revenue:flags2.9?true bot_can_manage_emoji_status:flags2.10?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int stargifts_count:flags2.8?int starref_program:flags2.11?StarRefProgram bot_verification:flags2.12?BotVerification send_paid_messages_stars:flags2.14?long = UserFull;
userFull#99e78045 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true can_view_revenue:flags2.9?true bot_can_manage_emoji_status:flags2.10?true display_gifts_button:flags2.16?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int stargifts_count:flags2.8?int starref_program:flags2.11?StarRefProgram bot_verification:flags2.12?BotVerification send_paid_messages_stars:flags2.14?long disallowed_gifts:flags2.15?DisallowedGiftsSettings = UserFull;
contact#145ade0b user_id:long mutual:Bool = Contact;
@ -427,6 +430,7 @@ updateBusinessBotCallbackQuery#1ea2fda7 flags:# query_id:long user_id:long conne
updateStarsRevenueStatus#a584b019 peer:Peer status:StarsRevenueStatus = Update;
updateBotPurchasedPaidMedia#283bd312 user_id:long payload:string qts:int = Update;
updatePaidReactionPrivacy#8b725fce private:PaidReactionPrivacy = Update;
updateSentPhoneCode#504aa18f sent_code:auth.SentCode = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -897,7 +901,6 @@ phoneCallDiscardReasonMissed#85e42301 = PhoneCallDiscardReason;
phoneCallDiscardReasonDisconnect#e095c1a0 = PhoneCallDiscardReason;
phoneCallDiscardReasonHangup#57adc690 = PhoneCallDiscardReason;
phoneCallDiscardReasonBusy#faf7e8c9 = PhoneCallDiscardReason;
phoneCallDiscardReasonAllowGroupCall#afe2b839 encrypted_key:bytes = PhoneCallDiscardReason;
dataJSON#7d748d04 data:string = DataJSON;
@ -1311,7 +1314,7 @@ statsGroupTopInviter#535f779d user_id:long invitations:int = StatsGroupTopInvite
stats.megagroupStats#ef7ff916 period:StatsDateRangeDays members:StatsAbsValueAndPrev messages:StatsAbsValueAndPrev viewers:StatsAbsValueAndPrev posters:StatsAbsValueAndPrev growth_graph:StatsGraph members_graph:StatsGraph new_members_by_source_graph:StatsGraph languages_graph:StatsGraph messages_graph:StatsGraph actions_graph:StatsGraph top_hours_graph:StatsGraph weekdays_graph:StatsGraph top_posters:Vector<StatsGroupTopPoster> top_admins:Vector<StatsGroupTopAdmin> top_inviters:Vector<StatsGroupTopInviter> users:Vector<User> = stats.MegagroupStats;
globalPrivacySettings#c9d8df1c flags:# archive_and_mute_new_noncontact_peers:flags.0?true keep_archived_unmuted:flags.1?true keep_archived_folders:flags.2?true hide_read_marks:flags.3?true new_noncontact_peers_require_premium:flags.4?true noncontact_peers_paid_stars:flags.5?long = GlobalPrivacySettings;
globalPrivacySettings#fe41b34f flags:# archive_and_mute_new_noncontact_peers:flags.0?true keep_archived_unmuted:flags.1?true keep_archived_folders:flags.2?true hide_read_marks:flags.3?true new_noncontact_peers_require_premium:flags.4?true display_gifts_button:flags.7?true noncontact_peers_paid_stars:flags.5?long disallowed_gifts:flags.6?DisallowedGiftsSettings = GlobalPrivacySettings;
help.countryCode#4203c5ef flags:# country_code:string prefixes:flags.0?Vector<string> patterns:flags.1?Vector<string> = help.CountryCode;
@ -1481,6 +1484,7 @@ inputInvoiceStarGift#e8625e92 flags:# hide_name:flags.0?true include_upgrade:fla
inputInvoiceStarGiftUpgrade#4d818d5d flags:# keep_original_details:flags.0?true stargift:InputSavedStarGift = InputInvoice;
inputInvoiceStarGiftTransfer#4a5f5bd9 stargift:InputSavedStarGift to_id:InputPeer = InputInvoice;
inputInvoicePremiumGiftStars#dabab2ef flags:# user_id:InputUser months:int message:flags.0?TextWithEntities = InputInvoice;
inputInvoiceBusinessBotTransferStars#f4997e42 bot:InputUser stars:long = InputInvoice;
payments.exportedInvoice#aed0cbd9 url:string = payments.ExportedInvoice;
@ -1495,6 +1499,7 @@ inputStorePaymentPremiumGiveaway#160544ca flags:# only_new_subscribers:flags.0?t
inputStorePaymentStarsTopup#dddd0f56 stars:long currency:string amount:long = InputStorePaymentPurpose;
inputStorePaymentStarsGift#1d741ef7 user_id:InputUser stars:long currency:string amount:long = InputStorePaymentPurpose;
inputStorePaymentStarsGiveaway#751f08fa flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.3?true stars:long boost_peer:InputPeer additional_peers:flags.1?Vector<InputPeer> countries_iso2:flags.2?Vector<string> prize_description:flags.4?string random_id:long until_date:int currency:string amount:long users:int = InputStorePaymentPurpose;
inputStorePaymentAuthCode#9bb2636d flags:# restore:flags.0?true phone_number:string phone_code_hash:string currency:string amount:long = InputStorePaymentPurpose;
paymentFormMethod#88f8f21b url:string title:string = PaymentFormMethod;
@ -1753,7 +1758,7 @@ inputQuickReplyShortcutId#1190cf1 shortcut_id:int = InputQuickReplyShortcut;
messages.quickReplies#c68d6695 quick_replies:Vector<QuickReply> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.QuickReplies;
messages.quickRepliesNotModified#5f91eb5b = messages.QuickReplies;
connectedBot#bd068601 flags:# can_reply:flags.0?true bot_id:long recipients:BusinessBotRecipients = ConnectedBot;
connectedBot#cd64636c flags:# bot_id:long recipients:BusinessBotRecipients rights:BusinessBotRights = ConnectedBot;
account.connectedBots#17d7f87b connected_bots:Vector<ConnectedBot> users:Vector<User> = account.ConnectedBots;
@ -1761,7 +1766,7 @@ messages.dialogFilters#2ad93719 flags:# tags_enabled:flags.0?true filters:Vector
birthday#6c8e1e06 flags:# day:int month:int year:flags.0?int = Birthday;
botBusinessConnection#896433b4 flags:# can_reply:flags.0?true disabled:flags.1?true connection_id:string user_id:long dc_id:int date:int = BotBusinessConnection;
botBusinessConnection#8f34b2f5 flags:# disabled:flags.1?true connection_id:string user_id:long dc_id:int date:int rights:flags.2?BusinessBotRights = BotBusinessConnection;
inputBusinessIntro#9c469cd flags:# title:string description:string sticker:flags.0?InputDocument = InputBusinessIntro;
@ -1945,6 +1950,15 @@ requirementToContactEmpty#50a9839 = RequirementToContact;
requirementToContactPremium#e581e4e9 = RequirementToContact;
requirementToContactPaidMessages#b4f67e93 stars_amount:long = RequirementToContact;
businessBotRights#a0624cf7 flags:# reply:flags.0?true read_messages:flags.1?true delete_sent_messages:flags.2?true delete_received_messages:flags.3?true edit_name:flags.4?true edit_bio:flags.5?true edit_profile_photo:flags.6?true edit_username:flags.7?true view_gifts:flags.8?true sell_gifts:flags.9?true change_gift_settings:flags.10?true transfer_and_upgrade_gifts:flags.11?true transfer_stars:flags.12?true manage_stories:flags.13?true = BusinessBotRights;
disallowedGiftsSettings#71f276c4 flags:# disallow_unlimited_stargifts:flags.0?true disallow_limited_stargifts:flags.1?true disallow_unique_stargifts:flags.2?true disallow_premium_gifts:flags.3?true = DisallowedGiftsSettings;
sponsoredPeer#c69708d3 flags:# random_id:bytes peer:Peer sponsor_info:flags.0?string additional_info:flags.1?string = SponsoredPeer;
contacts.sponsoredPeersEmpty#ea32b4b1 = contacts.SponsoredPeers;
contacts.sponsoredPeers#eb032884 peers:Vector<SponsoredPeer> chats:Vector<Chat> users:Vector<User> = contacts.SponsoredPeers;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1956,7 +1970,7 @@ invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X;
invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X;
invokeWithBusinessConnection#dd289f8e {X:Type} connection_id:string query:!X = X;
invokeWithGooglePlayIntegrity#1df92984 {X:Type} nonce:string token:string query:!X = X;
invokeWithApnsSecret#dae54f8 {X:Type} nonce:string secret:string query:!X = X;
invokeWithApnsSecret#0dae54f8 {X:Type} nonce:string secret:string query:!X = X;
invokeWithReCaptcha#adbb0f94 {X:Type} token:string query:!X = X;
auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode;
@ -2079,7 +2093,7 @@ account.updateBusinessWorkHours#4b00e066 flags:# business_work_hours:flags.0?Bus
account.updateBusinessLocation#9e6b131a flags:# geo_point:flags.1?InputGeoPoint address:flags.0?string = Bool;
account.updateBusinessGreetingMessage#66cdafc4 flags:# message:flags.0?InputBusinessGreetingMessage = Bool;
account.updateBusinessAwayMessage#a26a7fa5 flags:# message:flags.0?InputBusinessAwayMessage = Bool;
account.updateConnectedBot#43d8521d flags:# can_reply:flags.0?true deleted:flags.1?true bot:InputUser recipients:InputBusinessBotRecipients = Updates;
account.updateConnectedBot#66a08c7e flags:# deleted:flags.1?true rights:flags.0?BusinessBotRights bot:InputUser recipients:InputBusinessBotRecipients = Updates;
account.getConnectedBots#4ea4c80f = account.ConnectedBots;
account.getBotBusinessConnection#76a86270 connection_id:string = Updates;
account.updateBusinessIntro#a614d034 flags:# intro:flags.0?InputBusinessIntro = Bool;
@ -2130,6 +2144,7 @@ contacts.importContactToken#13005788 token:string = User;
contacts.editCloseFriends#ba6705f0 id:Vector<long> = Bool;
contacts.setBlocked#94c65c76 flags:# my_stories_from:flags.0?true id:Vector<InputPeer> limit:int = Bool;
contacts.getBirthdays#daeda864 = contacts.ContactBirthdays;
contacts.getSponsoredPeers#b6c8c393 q:string = contacts.SponsoredPeers;
messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
messages.getDialogs#a0f4cb4f flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.Dialogs;
@ -2348,9 +2363,9 @@ messages.requestMainWebView#c9e01e7b flags:# compact:flags.7?true fullscreen:fla
messages.sendPaidReaction#58bbcb50 flags:# peer:InputPeer msg_id:int count:int random_id:long private:flags.0?PaidReactionPrivacy = Updates;
messages.togglePaidReactionPrivacy#435885b5 peer:InputPeer msg_id:int private:PaidReactionPrivacy = Bool;
messages.getPaidReactionPrivacy#472455aa = Updates;
messages.viewSponsoredMessage#673ad8f1 peer:InputPeer random_id:bytes = Bool;
messages.clickSponsoredMessage#f093465 flags:# media:flags.0?true fullscreen:flags.1?true peer:InputPeer random_id:bytes = Bool;
messages.reportSponsoredMessage#1af3dbb8 peer:InputPeer random_id:bytes option:bytes = channels.SponsoredMessageReportResult;
messages.viewSponsoredMessage#269e3643 random_id:bytes = Bool;
messages.clickSponsoredMessage#8235057e flags:# media:flags.0?true fullscreen:flags.1?true random_id:bytes = Bool;
messages.reportSponsoredMessage#12cbf0c4 random_id:bytes option:bytes = channels.SponsoredMessageReportResult;
messages.getSponsoredMessages#9bd2f439 peer:InputPeer = messages.SponsoredMessages;
messages.savePreparedInlineMessage#f21f7f2f flags:# result:InputBotInlineResult user_id:InputUser peer_types:flags.0?Vector<InlineQueryPeerType> = messages.BotPreparedInlineMessage;
messages.getPreparedInlineMessage#857ebdb8 bot:InputUser id:string = messages.PreparedInlineMessage;
@ -2506,7 +2521,6 @@ payments.getBankCardData#2e79d779 number:string = payments.BankCardData;
payments.exportInvoice#f91b065 invoice_media:InputMedia = payments.ExportedInvoice;
payments.assignAppStoreTransaction#80ed747d receipt:bytes purpose:InputStorePaymentPurpose = Updates;
payments.assignPlayMarketTransaction#dffd50d3 receipt:DataJSON purpose:InputStorePaymentPurpose = Updates;
payments.canPurchasePremium#9fc19eb6 purpose:InputStorePaymentPurpose = Bool;
payments.getPremiumGiftCodeOptions#2757ba54 flags:# boost_peer:flags.0?InputPeer = Vector<PremiumGiftCodeOption>;
payments.checkGiftCode#8e51b4c1 slug:string = payments.CheckedGiftCode;
payments.applyGiftCode#f6e26854 slug:string = Updates;
@ -2544,6 +2558,7 @@ payments.getSavedStarGift#b455a106 stargift:Vector<InputSavedStarGift> = payment
payments.getStarGiftWithdrawalUrl#d06e93a8 stargift:InputSavedStarGift password:InputCheckPasswordSRP = payments.StarGiftWithdrawalUrl;
payments.toggleChatStarGiftNotifications#60eaefa1 flags:# enabled:flags.0?true peer:InputPeer = Bool;
payments.toggleStarGiftsPinnedToTop#1513e7b0 peer:InputPeer stargift:Vector<InputSavedStarGift> = Bool;
payments.canPurchaseStore#4fdc5ea7 purpose:InputStorePaymentPurpose = Bool;
stickers.createStickerSet#9021ab67 flags:# masks:flags.0?true emojis:flags.5?true text_color:flags.6?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector<InputStickerSetItem> software:flags.3?string = messages.StickerSet;
stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet;

View File

@ -127,168 +127,169 @@ $icons-map: (
"forums": "\f15a",
"forward": "\f15b",
"fragment": "\f15c",
"fullscreen": "\f15d",
"gifs": "\f15e",
"gift": "\f15f",
"group-filled": "\f160",
"group": "\f161",
"grouped-disable": "\f162",
"grouped": "\f163",
"hand-stop": "\f164",
"hashtag": "\f165",
"heart-outline": "\f166",
"heart": "\f167",
"help": "\f168",
"info-filled": "\f169",
"info": "\f16a",
"install": "\f16b",
"italic": "\f16c",
"key": "\f16d",
"keyboard": "\f16e",
"lamp": "\f16f",
"language": "\f170",
"large-pause": "\f171",
"large-play": "\f172",
"link-badge": "\f173",
"link-broken": "\f174",
"link": "\f175",
"location": "\f176",
"lock-badge": "\f177",
"lock": "\f178",
"logout": "\f179",
"loop": "\f17a",
"mention": "\f17b",
"message-failed": "\f17c",
"message-pending": "\f17d",
"message-read": "\f17e",
"message-succeeded": "\f17f",
"message": "\f180",
"microphone-alt": "\f181",
"microphone": "\f182",
"monospace": "\f183",
"more-circle": "\f184",
"more": "\f185",
"move-caption-down": "\f186",
"move-caption-up": "\f187",
"mute": "\f188",
"muted": "\f189",
"my-notes": "\f18a",
"new-chat-filled": "\f18b",
"next": "\f18c",
"nochannel": "\f18d",
"noise-suppression": "\f18e",
"non-contacts": "\f18f",
"one-filled": "\f190",
"open-in-new-tab": "\f191",
"password-off": "\f192",
"pause": "\f193",
"permissions": "\f194",
"phone-discard-outline": "\f195",
"phone-discard": "\f196",
"phone": "\f197",
"photo": "\f198",
"pin-badge": "\f199",
"pin-list": "\f19a",
"pin": "\f19b",
"pinned-chat": "\f19c",
"pinned-message": "\f19d",
"pip": "\f19e",
"play-story": "\f19f",
"play": "\f1a0",
"poll": "\f1a1",
"previous": "\f1a2",
"privacy-policy": "\f1a3",
"proof-of-ownership": "\f1a4",
"quote-text": "\f1a5",
"quote": "\f1a6",
"radial-badge": "\f1a7",
"readchats": "\f1a8",
"recent": "\f1a9",
"reload": "\f1aa",
"remove-quote": "\f1ab",
"remove": "\f1ac",
"reopen-topic": "\f1ad",
"replace": "\f1ae",
"replies": "\f1af",
"reply-filled": "\f1b0",
"reply": "\f1b1",
"revenue-split": "\f1b2",
"revote": "\f1b3",
"save-story": "\f1b4",
"saved-messages": "\f1b5",
"schedule": "\f1b6",
"search": "\f1b7",
"select": "\f1b8",
"send-outline": "\f1b9",
"send": "\f1ba",
"settings-filled": "\f1bb",
"settings": "\f1bc",
"share-filled": "\f1bd",
"share-screen-outlined": "\f1be",
"share-screen-stop": "\f1bf",
"share-screen": "\f1c0",
"show-message": "\f1c1",
"sidebar": "\f1c2",
"skip-next": "\f1c3",
"skip-previous": "\f1c4",
"smallscreen": "\f1c5",
"smile": "\f1c6",
"sort": "\f1c7",
"speaker-muted-story": "\f1c8",
"speaker-outline": "\f1c9",
"speaker-story": "\f1ca",
"speaker": "\f1cb",
"spoiler-disable": "\f1cc",
"spoiler": "\f1cd",
"sport": "\f1ce",
"star": "\f1cf",
"stars-lock": "\f1d0",
"stats": "\f1d1",
"stealth-future": "\f1d2",
"stealth-past": "\f1d3",
"stickers": "\f1d4",
"stop-raising-hand": "\f1d5",
"stop": "\f1d6",
"story-caption": "\f1d7",
"story-expired": "\f1d8",
"story-priority": "\f1d9",
"story-reply": "\f1da",
"strikethrough": "\f1db",
"tag-add": "\f1dc",
"tag-crossed": "\f1dd",
"tag-filter": "\f1de",
"tag-name": "\f1df",
"tag": "\f1e0",
"timer": "\f1e1",
"toncoin": "\f1e2",
"trade": "\f1e3",
"transcribe": "\f1e4",
"truck": "\f1e5",
"unarchive": "\f1e6",
"underlined": "\f1e7",
"unique-profile": "\f1e8",
"unlock-badge": "\f1e9",
"unlock": "\f1ea",
"unmute": "\f1eb",
"unpin": "\f1ec",
"unread": "\f1ed",
"up": "\f1ee",
"user-filled": "\f1ef",
"user-online": "\f1f0",
"user": "\f1f1",
"video-outlined": "\f1f2",
"video-stop": "\f1f3",
"video": "\f1f4",
"view-once": "\f1f5",
"voice-chat": "\f1f6",
"volume-1": "\f1f7",
"volume-2": "\f1f8",
"volume-3": "\f1f9",
"web": "\f1fa",
"webapp": "\f1fb",
"word-wrap": "\f1fc",
"zoom-in": "\f1fd",
"zoom-out": "\f1fe",
"frozen-time": "\f15d",
"fullscreen": "\f15e",
"gifs": "\f15f",
"gift": "\f160",
"group-filled": "\f161",
"group": "\f162",
"grouped-disable": "\f163",
"grouped": "\f164",
"hand-stop": "\f165",
"hashtag": "\f166",
"heart-outline": "\f167",
"heart": "\f168",
"help": "\f169",
"info-filled": "\f16a",
"info": "\f16b",
"install": "\f16c",
"italic": "\f16d",
"key": "\f16e",
"keyboard": "\f16f",
"lamp": "\f170",
"language": "\f171",
"large-pause": "\f172",
"large-play": "\f173",
"link-badge": "\f174",
"link-broken": "\f175",
"link": "\f176",
"location": "\f177",
"lock-badge": "\f178",
"lock": "\f179",
"logout": "\f17a",
"loop": "\f17b",
"mention": "\f17c",
"message-failed": "\f17d",
"message-pending": "\f17e",
"message-read": "\f17f",
"message-succeeded": "\f180",
"message": "\f181",
"microphone-alt": "\f182",
"microphone": "\f183",
"monospace": "\f184",
"more-circle": "\f185",
"more": "\f186",
"move-caption-down": "\f187",
"move-caption-up": "\f188",
"mute": "\f189",
"muted": "\f18a",
"my-notes": "\f18b",
"new-chat-filled": "\f18c",
"next": "\f18d",
"nochannel": "\f18e",
"noise-suppression": "\f18f",
"non-contacts": "\f190",
"one-filled": "\f191",
"open-in-new-tab": "\f192",
"password-off": "\f193",
"pause": "\f194",
"permissions": "\f195",
"phone-discard-outline": "\f196",
"phone-discard": "\f197",
"phone": "\f198",
"photo": "\f199",
"pin-badge": "\f19a",
"pin-list": "\f19b",
"pin": "\f19c",
"pinned-chat": "\f19d",
"pinned-message": "\f19e",
"pip": "\f19f",
"play-story": "\f1a0",
"play": "\f1a1",
"poll": "\f1a2",
"previous": "\f1a3",
"privacy-policy": "\f1a4",
"proof-of-ownership": "\f1a5",
"quote-text": "\f1a6",
"quote": "\f1a7",
"radial-badge": "\f1a8",
"readchats": "\f1a9",
"recent": "\f1aa",
"reload": "\f1ab",
"remove-quote": "\f1ac",
"remove": "\f1ad",
"reopen-topic": "\f1ae",
"replace": "\f1af",
"replies": "\f1b0",
"reply-filled": "\f1b1",
"reply": "\f1b2",
"revenue-split": "\f1b3",
"revote": "\f1b4",
"save-story": "\f1b5",
"saved-messages": "\f1b6",
"schedule": "\f1b7",
"search": "\f1b8",
"select": "\f1b9",
"send-outline": "\f1ba",
"send": "\f1bb",
"settings-filled": "\f1bc",
"settings": "\f1bd",
"share-filled": "\f1be",
"share-screen-outlined": "\f1bf",
"share-screen-stop": "\f1c0",
"share-screen": "\f1c1",
"show-message": "\f1c2",
"sidebar": "\f1c3",
"skip-next": "\f1c4",
"skip-previous": "\f1c5",
"smallscreen": "\f1c6",
"smile": "\f1c7",
"sort": "\f1c8",
"speaker-muted-story": "\f1c9",
"speaker-outline": "\f1ca",
"speaker-story": "\f1cb",
"speaker": "\f1cc",
"spoiler-disable": "\f1cd",
"spoiler": "\f1ce",
"sport": "\f1cf",
"star": "\f1d0",
"stars-lock": "\f1d1",
"stats": "\f1d2",
"stealth-future": "\f1d3",
"stealth-past": "\f1d4",
"stickers": "\f1d5",
"stop-raising-hand": "\f1d6",
"stop": "\f1d7",
"story-caption": "\f1d8",
"story-expired": "\f1d9",
"story-priority": "\f1da",
"story-reply": "\f1db",
"strikethrough": "\f1dc",
"tag-add": "\f1dd",
"tag-crossed": "\f1de",
"tag-filter": "\f1df",
"tag-name": "\f1e0",
"tag": "\f1e1",
"timer": "\f1e2",
"toncoin": "\f1e3",
"trade": "\f1e4",
"transcribe": "\f1e5",
"truck": "\f1e6",
"unarchive": "\f1e7",
"underlined": "\f1e8",
"unique-profile": "\f1e9",
"unlock-badge": "\f1ea",
"unlock": "\f1eb",
"unmute": "\f1ec",
"unpin": "\f1ed",
"unread": "\f1ee",
"up": "\f1ef",
"user-filled": "\f1f0",
"user-online": "\f1f1",
"user": "\f1f2",
"video-outlined": "\f1f3",
"video-stop": "\f1f4",
"video": "\f1f5",
"view-once": "\f1f6",
"voice-chat": "\f1f7",
"volume-1": "\f1f8",
"volume-2": "\f1f9",
"volume-3": "\f1fa",
"web": "\f1fb",
"webapp": "\f1fc",
"word-wrap": "\f1fd",
"zoom-in": "\f1fe",
"zoom-out": "\f1ff",
);
.icon-active-sessions::before {
@ -567,6 +568,9 @@ $icons-map: (
.icon-fragment::before {
content: map.get($icons-map, "fragment");
}
.icon-frozen-time::before {
content: map.get($icons-map, "frozen-time");
}
.icon-fullscreen::before {
content: map.get($icons-map, "fullscreen");
}

Binary file not shown.

Binary file not shown.

View File

@ -91,6 +91,7 @@ export type FontIconName =
| 'forums'
| 'forward'
| 'fragment'
| 'frozen-time'
| 'fullscreen'
| 'gifs'
| 'gift'

View File

@ -1458,9 +1458,24 @@ export interface LangPair {
'StoryTooltipReactionSent': undefined;
'StarsNeededTextSendPaidMessages': undefined;
'PaidMessageTransactionTotal': undefined;
'TitleFrozenAccount': undefined;
'SubtitleFrozenAccount': undefined;
'ComposerTitleFrozenAccount': undefined;
'ComposerSubtitleFrozenAccount': undefined;
'DescriptionRestrictedMedia': undefined;
'DescriptionScheduledPaidMediaNotAllowed': undefined;
'DescriptionScheduledPaidMessagesNotAllowed': undefined;
'FrozenAccountModalTitle': undefined;
'FrozenAccountViolationTitle': undefined;
'FrozenAccountViolationSubtitle': undefined;
'FrozenAccountReadOnlyTitle': undefined;
'FrozenAccountReadOnlySubtitle': undefined;
'FrozenAccountAppealTitle': undefined;
'ButtonAppeal': undefined;
'ButtonUnderstood': undefined;
'ActionPaidMessageGroupPriceFree': undefined;
'NotificationTitleNotSupportedInFrozenAccount': undefined;
'NotificationMessageNotSupportedInFrozenAccount': undefined;
}
export interface LangPairWithVariables<V extends unknown = LangVariable> {
@ -2346,6 +2361,21 @@ export interface LangPairWithVariables<V extends unknown = LangVariable> {
'PaidMessageTransactionDescription': {
'percent': V;
};
'FrozenAccountAppealSubtitle': {
'botLink': V;
'date': V;
};
'ActionPaidMessageGroupPrice': {
'stars': V;
};
'ApiMessageActionPaidMessagesRefundedOutgoing': {
'stars': V;
'user': V;
};
'ApiMessageActionPaidMessagesRefundedIncoming': {
'user': V;
'stars': V;
};
}
export interface LangPairPlural {

View File

@ -147,6 +147,14 @@ export function tryParseDeepLink(link: string): DeepLink | undefined {
}
}
export function getUsernameFromDeepLink(url: string) {
const deepLink = tryParseDeepLink(url);
if (deepLink?.type === 'publicUsernameOrBotLink') {
return deepLink.username;
}
return undefined;
}
function parseDeepLink(url: string) {
const correctUrl = ensureProtocol(url);
if (!correctUrl) {