Sponsored Message: Implement reporting (#4487)

This commit is contained in:
Alexander Zinchuk 2024-05-03 14:38:20 +02:00
parent 19e90c8fbb
commit 37ad59d937
46 changed files with 1067 additions and 524 deletions

View File

@ -74,6 +74,7 @@ export interface GramJsAppConfig extends LimitsConfig {
// Boosts
group_transcribe_level_min?: number;
new_noncontact_peers_require_premium_without_ownpremium?: boolean;
channel_restrict_sponsored_level_min?: number;
// Upload premium notifications
upload_premium_speedup_notify_period?: number;
upload_premium_speedup_download?: number;
@ -156,5 +157,6 @@ export function buildAppConfig(json: GramJs.TypeJSONValue, hash: number): ApiApp
bandwidthPremiumNotifyPeriod: appConfig.upload_premium_speedup_notify_period,
bandwidthPremiumUploadSpeedup: appConfig.upload_premium_speedup_upload,
bandwidthPremiumDownloadSpeedup: appConfig.upload_premium_speedup_download,
channelRestrictAdsLevelMin: appConfig.channel_restrict_sponsored_level_min,
};
}

View File

@ -17,11 +17,13 @@ import type {
ApiMissingInvitedUser,
ApiRestrictionReason,
ApiSendAsPeerId,
ApiSponsoredMessageReportResult,
ApiTopic,
} from '../../types';
import { omitUndefined, pick, pickTruthy } from '../../../util/iteratees';
import { getServerTime, getServerTimeOffset } from '../../../util/serverTime';
import { serializeBytes } from '../helpers';
import { buildApiUsernames } from './common';
import { omitVirtualClassFields } from './helpers';
import {
@ -645,3 +647,31 @@ export function buildApiMissingInvitedUser(
isRequiringPremiumToInvite: user.premiumWouldAllowInvite,
};
}
export function buildApiSponsoredMessageReportResult(
result: GramJs.channels.TypeSponsoredMessageReportResult,
): ApiSponsoredMessageReportResult {
if (result instanceof GramJs.channels.SponsoredMessageReportResultReported) {
return {
type: 'reported',
};
}
if (result instanceof GramJs.channels.SponsoredMessageReportResultAdsHidden) {
return {
type: 'hidden',
};
}
const title = result.title;
const options = result.options.map((option) => ({
text: option.text,
option: serializeBytes(option.option),
}));
return {
type: 'selectOption',
title,
options,
};
}

View File

@ -20,7 +20,6 @@ import type {
ApiReplyInfo,
ApiReplyKeyboard,
ApiSponsoredMessage,
ApiSponsoredWebPage,
ApiSticker,
ApiStory,
ApiStorySkipped,
@ -51,13 +50,12 @@ import {
resolveMessageApiChatId,
serializeBytes,
} from '../helpers';
import { buildApiBotApp } from './bots';
import { buildApiCallDiscardReason } from './calls';
import {
buildApiPhoto,
} from './common';
import { buildMessageContent, buildMessageMediaContent, buildMessageTextContent } from './messageContent';
import { buildApiPeerId, getApiChatIdFromMtpPeer, isPeerUser } from './peers';
import { buildApiPeerColor, buildApiPeerId, getApiChatIdFromMtpPeer } from './peers';
import { buildMessageReactions } from './reactions';
const LOCAL_MESSAGES_LIMIT = 1e6; // 1M
@ -79,33 +77,28 @@ export function setMessageBuilderCurrentUserId(_currentUserId: string) {
export function buildApiSponsoredMessage(mtpMessage: GramJs.SponsoredMessage): ApiSponsoredMessage | undefined {
const {
fromId, message, entities, startParam, channelPost, chatInvite, chatInviteHash, randomId, recommended, sponsorInfo,
additionalInfo, showPeerPhoto, webpage, buttonText, app,
message, entities, randomId, recommended, sponsorInfo, additionalInfo, buttonText, canReport, title, url, color,
} = mtpMessage;
const chatId = fromId ? getApiChatIdFromMtpPeer(fromId) : undefined;
const chatInviteTitle = chatInvite
? (chatInvite instanceof GramJs.ChatInvite
? chatInvite.title
: !(chatInvite.chat instanceof GramJs.ChatEmpty) ? chatInvite.chat.title : undefined)
: undefined;
let photo: ApiPhoto | undefined;
if (mtpMessage.photo instanceof GramJs.Photo) {
addPhotoToLocalDb(mtpMessage.photo);
photo = buildApiPhoto(mtpMessage.photo);
}
return {
randomId: serializeBytes(randomId),
isBot: fromId ? isPeerUser(fromId) : false,
text: buildMessageTextContent(message, entities),
expiresAt: Math.round(Date.now() / 1000) + SPONSORED_MESSAGE_CACHE_MS,
isRecommended: Boolean(recommended),
...(webpage && { webPage: buildSponsoredWebPage(webpage) }),
...(showPeerPhoto && { isAvatarShown: true }),
...(chatId && { chatId }),
...(chatInviteHash && { chatInviteHash }),
...(chatInvite && { chatInviteTitle }),
...(startParam && { startParam }),
...(channelPost && { channelPostId: channelPost }),
...(sponsorInfo && { sponsorInfo }),
...(additionalInfo && { additionalInfo }),
...(buttonText && { buttonText }),
...(app && { botApp: buildApiBotApp(app) }),
isRecommended: recommended,
sponsorInfo,
additionalInfo,
buttonText,
canReport,
title,
url,
peerColor: color && buildApiPeerColor(color),
photo,
};
}
@ -1094,19 +1087,3 @@ export function buildApiQuickReply(reply: GramJs.TypeQuickReply): ApiQuickReply
topMessageId: topMessage,
};
}
function buildSponsoredWebPage(webPage: GramJs.TypeSponsoredWebPage): ApiSponsoredWebPage {
let photo: ApiPhoto | undefined;
if (webPage.photo instanceof GramJs.Photo) {
addPhotoToLocalDb(webPage.photo);
photo = buildApiPhoto(webPage.photo);
}
return {
...pick(webPage, [
'url',
'siteName',
]),
photo,
};
}

View File

@ -22,7 +22,7 @@ export function buildApiUserFullInfo(mtpUserFull: GramJs.users.UserFull): ApiUse
profilePhoto, voiceMessagesForbidden, premiumGifts,
fallbackPhoto, personalPhoto, translationsDisabled, storiesPinnedAvailable,
contactRequirePremium, businessWorkHours, businessLocation, businessIntro,
birthday, personalChannelId, personalChannelMessage,
birthday, personalChannelId, personalChannelMessage, sponsoredEnabled,
},
users,
} = mtpUserFull;
@ -49,6 +49,7 @@ export function buildApiUserFullInfo(mtpUserFull: GramJs.users.UserFull): ApiUse
businessIntro: businessIntro && buildApiBusinessIntro(businessIntro),
personalChannelId: personalChannelId && buildApiPeerId(personalChannelId, 'channel'),
personalChannelMessageId: personalChannelMessage,
areAdsEnabled: sponsoredEnabled,
};
}

View File

@ -94,3 +94,15 @@ export async function resolveBusinessChatLink({ slug } : { slug: string }) {
chatLink,
};
}
export function toggleSponsoredMessages({
enabled,
}: {
enabled: boolean;
}) {
return invokeRequest(new GramJs.account.ToggleSponsoredMessages({
enabled,
}), {
shouldReturnTrue: true,
});
}

View File

@ -44,6 +44,7 @@ import {
buildApiChatReactions,
buildApiChatSettings,
buildApiMissingInvitedUser,
buildApiSponsoredMessageReportResult,
buildApiTopic,
buildChatMembers,
getPeerKey,
@ -70,6 +71,7 @@ import {
addEntitiesToLocalDb,
addMessageToLocalDb,
addPhotoToLocalDb,
deserializeBytes,
isChatFolder,
} from '../helpers';
import localDb from '../localDb';
@ -2017,3 +2019,39 @@ export async function fetchChannelRecommendations({ chat }: { chat: ApiChat }) {
result instanceof GramJs.messages.ChatsSlice ? result.count : undefined,
};
}
export async function reportSponsoredMessage({
chat,
randomId,
option,
}: {
chat: ApiChat;
randomId: string;
option: string;
}) {
const { id, accessHash } = chat;
const channel = buildInputEntity(id, accessHash);
try {
const result = await invokeRequest(new GramJs.channels.ReportSponsoredMessage({
channel: channel as GramJs.InputChannel,
randomId: deserializeBytes(randomId),
option: deserializeBytes(option),
}), {
shouldThrow: true,
});
if (!result) {
return undefined;
}
return buildApiSponsoredMessageReportResult(result);
} catch (err) {
if (err instanceof Error && err.message === 'PREMIUM_ACCOUNT_REQUIRED') {
return {
type: 'premiumRequired' as const,
};
}
return undefined;
}
}

View File

@ -22,7 +22,7 @@ export {
editTopic, toggleForum, fetchTopicById, createTopic, toggleParticipantsHidden, checkChatlistInvite,
joinChatlistInvite, createChalistInvite, editChatlistInvite, deleteChatlistInvite, fetchChatlistInvites,
fetchLeaveChatlistSuggestions, leaveChatlist, togglePeerTranslations, setViewForumAsMessages,
fetchChannelRecommendations, fetchSavedChats, toggleSavedDialogPinned,
fetchChannelRecommendations, fetchSavedChats, toggleSavedDialogPinned, reportSponsoredMessage,
} from './chats';
export {

View File

@ -1,7 +1,7 @@
import type { ThreadId } from '../../types';
import type { ApiWebDocument } from './bots';
import type { ApiGroupCall, PhoneCallAction } from './calls';
import type { ApiChat } from './chats';
import type { ApiChat, ApiPeerColor } from './chats';
import type { ApiInputStorePaymentPurpose, ApiPremiumGiftCodeOption } from './payments';
import type { ApiMessageStoryData, ApiWebPageStoryData } from './stories';
@ -382,12 +382,6 @@ export interface ApiWebPage {
story?: ApiWebPageStoryData;
}
export interface ApiSponsoredWebPage {
url: string;
siteName: string;
photo?: ApiPhoto;
}
export type ApiReplyInfo = ApiMessageReplyInfo | ApiStoryReplyInfo;
export interface ApiMessageReplyInfo {
@ -685,22 +679,18 @@ export type ApiThreadInfo = ApiCommentsInfo | ApiMessageThreadInfo;
export type ApiMessageOutgoingStatus = 'read' | 'succeeded' | 'pending' | 'failed';
export type ApiSponsoredMessage = {
chatId?: string;
randomId: string;
isRecommended?: boolean;
isAvatarShown?: boolean;
isBot?: boolean;
channelPostId?: number;
startParam?: string;
chatInviteHash?: string;
chatInviteTitle?: string;
isRecommended?: true;
text: ApiFormattedText;
webPage?: ApiSponsoredWebPage;
expiresAt: number;
sponsorInfo?: string;
additionalInfo?: string;
buttonText?: string;
botApp?: ApiBotApp;
canReport?: true;
title: string;
url: string;
photo?: ApiPhoto;
peerColor?: ApiPeerColor;
};
// KeyboardButtons
@ -841,6 +831,17 @@ export type ApiQuickReply = {
topMessageId: number;
};
export type ApiSponsoredMessageReportResult = {
type: 'reported' | 'hidden' | 'premiumRequired';
} | {
type: 'selectOption';
title: string;
options: {
text: string;
option: string;
}[];
};
export const MAIN_THREAD_ID = -1;
// `Symbol` can not be transferred from worker

View File

@ -208,6 +208,7 @@ export interface ApiAppConfig {
bandwidthPremiumNotifyPeriod?: number;
bandwidthPremiumUploadSpeedup?: number;
bandwidthPremiumDownloadSpeedup?: number;
channelRestrictAdsLevelMin?: number;
}
export interface ApiConfig {

View File

@ -53,6 +53,7 @@ export interface ApiUserFullInfo {
noVoiceMessages?: boolean;
premiumGifts?: ApiPremiumGiftOption[];
isTranslationDisabled?: true;
areAdsEnabled?: boolean;
hasPinnedStories?: boolean;
isContactRequirePremium?: boolean;
birthday?: ApiBirthday;

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><g clip-path="url(#clip0_427_83808)" transform="matrix(1.66667 0 0 1.66667 -5.667 -4)"><path fill="#000" d="M9.414 7h2.24l4.14-3.253C17.5 2.407 20 3.622 20 5.792v10.916c0 .272-.04.528-.111.767l1.818 1.818a1 1 0 0 1-1.414 1.414l-16-16a1 1 0 0 1 1.414-1.414zm2 2L18 15.586V5.792a.6.6 0 0 0-.97-.472l-4.383 3.443a1.107 1.107 0 0 1-.684.237zM4 11.25c0-1.573.855-2.947 2.125-3.682l1.516 1.515A2.251 2.251 0 0 0 8.25 13.5h3.713c.033 0 .066.002.099.004l5.77 5.77a2.554 2.554 0 0 1-2.038-.522L13 16.558V17a3 3 0 0 1-6 0v-1.687a4.252 4.252 0 0 1-3-4.063zm7 4.25H9V17a1 1 0 0 0 2 0Z"/></g></svg>

After

Width:  |  Height:  |  Size: 660 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><path fill="#000" d="M20.313 5.268a1.67 1.67 0 0 1 2.06-1.158 12.455 12.455 0 0 1 2.72 1.13 1.675 1.675 0 0 1 .64 2.278 1.668 1.668 0 0 1-2.273.64 9.117 9.117 0 0 0-1.992-.826 1.674 1.674 0 0 1-1.155-2.064Zm7.223 4.059a1.668 1.668 0 0 1 2.274.64c.482.86.86 1.776 1.127 2.726a1.67 1.67 0 1 1-3.215.905 9.109 9.109 0 0 0-.825-1.993 1.675 1.675 0 0 1 .639-2.278zm2.246 7.987a1.674 1.674 0 0 1 1.155 2.064 12.482 12.482 0 0 1-1.127 2.726 1.668 1.668 0 0 1-2.273.641 1.675 1.675 0 0 1-.64-2.278 9.192 9.192 0 0 0 .825-1.996 1.67 1.67 0 0 1 2.06-1.157zm-4.05 7.238a1.675 1.675 0 0 1-.639 2.278c-.859.483-1.773.863-2.72 1.13a1.674 1.674 0 0 1-.903-3.222 9.065 9.065 0 0 0 1.988-.826 1.668 1.668 0 0 1 2.274.64z" style="stroke-width:1.67169"/><path fill="#000" d="M14.531 1.05C6.936 1.806 1 8.247 1 16.036c0 7.79 5.936 14.23 13.531 14.985 1.73.171 3.172-1.292 3.172-2.936V3.986c0-1.643-1.441-3.107-3.172-2.935zm-.172 3.475v23.02c-5.649-.826-10.015-5.6-10.015-11.51S8.71 5.351 14.359 4.525z" style="stroke-linecap:round;-inkscape-stroke:none"/><path fill="#000" d="M15.361 3.318a1.672 1.672 0 0 0-1.181.49L1.822 16.194a1.672 1.672 0 0 0 .002 2.364 1.672 1.672 0 0 0 2.365-.002L16.547 6.172a1.672 1.672 0 0 0-.004-2.365 1.672 1.672 0 0 0-1.182-.489zm1.849 8.186a1.672 1.672 0 0 0-2.362.004L4.1 22.278a1.672 1.672 0 0 0 .004 2.363 1.672 1.672 0 0 0 2.363-.002l10.748-10.77a1.672 1.672 0 0 0-.004-2.365zm-1.18 7.879a1.672 1.672 0 0 0-1.182.492L8.475 26.26a1.672 1.672 0 0 0 .004 2.365 1.672 1.672 0 0 0 2.363-.002l6.373-6.387a1.672 1.672 0 0 0-.004-2.363 1.672 1.672 0 0 0-1.182-.49z" style="stroke-linecap:round;-inkscape-stroke:none"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -27,6 +27,7 @@ export { default as GiftCodeModal } from '../components/modals/giftcode/GiftCode
export { default as ChatlistModal } from '../components/modals/chatlist/ChatlistModal';
export { default as AboutAdsModal } from '../components/common/AboutAdsModal';
export { default as ReportAdModal } from '../components/modals/reportAd/ReportAdModal';
export { default as CalendarModal } from '../components/common/CalendarModal';
export { default as DeleteMessageModal } from '../components/common/DeleteMessageModal';
export { default as PinMessageModal } from '../components/common/PinMessageModal';

View File

@ -0,0 +1,35 @@
.title, .description {
text-align: center !important;
text-wrap: pretty;
padding-inline: 1.5rem;
}
.secondary {
color: var(--color-text-secondary);
}
.separator {
margin-block: 1rem;
width: 100%;
}
.topIcon {
--premium-gradient: linear-gradient(88.39deg, #6C93FF -2.56%, #976FFF 51.27%, #DF69D1 107.39%);
display: grid;
place-items: center;
flex-shrink: 0;
border-radius: 50%;
background: var(--premium-gradient);
font-size: 4rem;
color: white;
width: 6rem;
height: 6rem;
margin-bottom: 1rem;
}
.content {
display: flex;
flex-direction: column;
align-items: center;
}

View File

@ -1,45 +1,120 @@
import type { FC } from '../../lib/teact/teact';
import React, { memo } from '../../lib/teact/teact';
import React, { memo, useMemo } from '../../lib/teact/teact';
import buildClassName from '../../util/buildClassName';
import renderText from './helpers/renderText';
import useDerivedState from '../../hooks/useDerivedState';
import useLang from '../../hooks/useLang';
import useSelectorSignal from '../../hooks/useSelectorSignal';
import Button from '../ui/Button';
import ListItem from '../ui/ListItem';
import Modal from '../ui/Modal';
import Separator from '../ui/Separator';
import Icon from './Icon';
import SafeLink from './SafeLink';
import styles from './AboutAdsModal.module.scss';
export type OwnProps = {
isOpen: boolean;
onClose: () => void;
isRevenueSharing?: boolean;
onClose: NoneToVoidFunction;
};
const AboutAdsModal: FC<OwnProps> = ({
isOpen,
isRevenueSharing,
onClose,
}) => {
const lang = useLang();
const minLevelSignal = useSelectorSignal((global) => global.appConfig?.channelRestrictAdsLevelMin);
const minLevelToRestrictAds = useDerivedState(minLevelSignal);
const regularAdContent = useMemo(() => {
return (
<>
<h3>{lang('SponsoredMessageInfoScreen.Title')}</h3>
<p>{renderText(lang('SponsoredMessageInfoDescription1'), ['br'])}</p>
<p>{renderText(lang('SponsoredMessageInfoDescription2'), ['br'])}</p>
<p>{renderText(lang('SponsoredMessageInfoDescription3'), ['br'])}</p>
<p>
<SafeLink
url={lang('SponsoredMessageAlertLearnMoreUrl')}
text={lang('SponsoredMessageAlertLearnMoreUrl')}
/>
</p>
<p>{renderText(lang('SponsoredMessageInfoDescription4'), ['br'])}</p>
</>
);
}, [lang]);
const revenueSharingAdContent = useMemo(() => {
return (
<>
<div className={styles.topIcon}><Icon name="channel" /></div>
<h3 className={styles.title}>{lang('AboutRevenueSharingAds')}</h3>
<p className={buildClassName(styles.description, styles.secondary)}>
{lang('RevenueSharingAdsAlertSubtitle')}
</p>
<ListItem
isStatic
multiline
icon="lock"
>
<span className="title">{lang('RevenueSharingAdsInfo1Title')}</span>
<span className="subtitle">
{renderText(lang('RevenueSharingAdsInfo1Subtitle'), ['simple_markdown'])}
</span>
</ListItem>
<ListItem
isStatic
multiline
icon="revenue-split"
>
<span className="title">{lang('RevenueSharingAdsInfo2Title')}</span>
<span className="subtitle">
{renderText(lang('RevenueSharingAdsInfo2Subtitle'), ['simple_markdown'])}
</span>
</ListItem>
<ListItem
isStatic
multiline
icon="nochannel"
>
<span className="title">{lang('RevenueSharingAdsInfo3Title')}</span>
<span className="subtitle">
{renderText(lang('RevenueSharingAdsInfo3Subtitle', minLevelToRestrictAds), ['simple_markdown'])}
</span>
</ListItem>
<Separator className={styles.separator} />
<h3 className={styles.title}>{renderText(lang('RevenueSharingAdsInfo4Title'), ['simple_markdown'])}</h3>
<p className={styles.description}>
{renderText(lang('RevenueSharingAdsInfo4Subtitle2', ''), ['simple_markdown'])}
<SafeLink
url={lang('PromoteUrl')}
text={lang('LearnMoreArrow')}
/>
</p>
</>
);
}, [lang, minLevelToRestrictAds]);
return (
<Modal
isOpen={isOpen}
contentClassName={styles.content}
onClose={onClose}
hasCloseButton
title={lang('SponsoredMessageInfo')}
>
<p>{renderText(lang('SponsoredMessageInfoDescription1'), ['br'])}</p>
<p>{renderText(lang('SponsoredMessageInfoDescription2'), ['br'])}</p>
<p>{renderText(lang('SponsoredMessageInfoDescription3'), ['br'])}</p>
<p>
<SafeLink
url={lang('SponsoredMessageAlertLearnMoreUrl')}
text={lang('SponsoredMessageAlertLearnMoreUrl')}
/>
</p>
<p>{renderText(lang('SponsoredMessageInfoDescription4'), ['br'])}</p>
<div className="dialog-buttons mt-2">
<Button className="confirm-dialog-button" isText onClick={onClose}>{lang('Close')}</Button>
</div>
{isRevenueSharing ? revenueSharingAdContent : regularAdContent}
<Button
size="smaller"
onClick={onClose}
>
{lang('RevenueSharingAdsUnderstood')}
</Button>
</Modal>
);
};

View File

@ -0,0 +1,3 @@
.root {
position: relative;
}

View File

@ -1,30 +1,44 @@
import React, { memo } from '../../lib/teact/teact';
import type { ApiPeer } from '../../api/types';
import type { ApiPeer, ApiPeerColor } from '../../api/types';
import buildClassName from '../../util/buildClassName';
import { getPeerColorClass } from './helpers/peerColor';
import { getApiPeerColorClass, getPeerColorClass } from './helpers/peerColor';
import EmojiIconBackground from './embedded/EmojiIconBackground';
type OwnProps = {
import styles from './PeerColorWrapper.module.scss';
interface OwnProps extends React.HTMLAttributes<HTMLDivElement> {
peer?: ApiPeer;
peerColor?: ApiPeerColor;
noUserColors?: boolean;
shoudReset?: boolean;
className?: string;
emojiIconClassName?: string;
children: React.ReactNode;
};
}
function PeerColorWrapper({
peer, noUserColors, shoudReset, className, emojiIconClassName, children,
peer, peerColor, noUserColors, shoudReset, className, emojiIconClassName, children, ...otherProps
}: OwnProps) {
const color = peerColor || peer?.color;
return (
<div className={buildClassName(getPeerColorClass(peer, noUserColors, shoudReset), className)}>
{peer?.color?.backgroundEmojiId && (
<div
className={buildClassName(
styles.root,
peer && getPeerColorClass(peer, noUserColors, shoudReset),
peerColor && getApiPeerColorClass(peerColor),
className,
)}
// eslint-disable-next-line react/jsx-props-no-spreading
{...otherProps}
>
{color?.backgroundEmojiId && (
<EmojiIconBackground
className={emojiIconClassName}
emojiDocumentId={peer.color.backgroundEmojiId}
emojiDocumentId={color.backgroundEmojiId}
/>
)}
{children}

View File

@ -1,4 +1,4 @@
import type { ApiPeer } from '../../../api/types';
import type { ApiPeer, ApiPeerColor } from '../../../api/types';
import { getPeerColorCount, getPeerColorKey } from '../../../global/helpers';
@ -9,3 +9,7 @@ export function getPeerColorClass(peer?: ApiPeer, noUserColors?: boolean, should
}
return noUserColors ? `peer-color-count-${getPeerColorCount(peer)}` : `peer-color-${getPeerColorKey(peer)}`;
}
export function getApiPeerColorClass(color: ApiPeerColor) {
return `peer-color-${color.color}`;
}

View File

@ -114,7 +114,7 @@ const BusinessHours = ({
useEffect(() => {
if (!isExpanded) return;
const slide = document.querySelector<HTMLElement>(`.${ACTIVE_SLIDE_CLASS_NAME} .${styles.timetable}`);
const slide = document.querySelector<HTMLElement>(`.${ACTIVE_SLIDE_CLASS_NAME} > .${styles.timetable}`);
if (!slide) return;
const height = slide.offsetHeight;
@ -124,7 +124,7 @@ const BusinessHours = ({
}, [isExpanded]);
const handleAnimationStart = useLastCallback(() => {
const slide = document.querySelector<HTMLElement>(`.${TO_SLIDE_CLASS_NAME} .${styles.timetable}`)!;
const slide = document.querySelector<HTMLElement>(`.${TO_SLIDE_CLASS_NAME} > .${styles.timetable}`)!;
requestMeasure(() => {
const height = slide.offsetHeight;

View File

@ -52,6 +52,7 @@ import {
selectScrollOffset,
selectTabState,
selectThreadInfo,
selectUserFullInfo,
} from '../../global/selectors';
import animateScroll, { isAnimatingScroll, restartCurrentScrollAnimation } from '../../util/animateScroll';
import buildClassName from '../../util/buildClassName';
@ -101,7 +102,6 @@ type OwnProps = {
};
type StateProps = {
isCurrentUserPremium?: boolean;
isChatLoaded?: boolean;
isChannelChat?: boolean;
isGroupChat?: boolean;
@ -127,6 +127,7 @@ type StateProps = {
isEmptyThread?: boolean;
isForum?: boolean;
currentUserId: string;
areAdsEnabled?: boolean;
};
const MESSAGE_REACTIONS_POLLING_INTERVAL = 20 * 1000;
@ -150,7 +151,6 @@ const MessageList: FC<OwnProps & StateProps> = ({
hasTools,
onScrollDownToggle,
onNotchToggle,
isCurrentUserPremium,
isChatLoaded,
isForum,
isChannelChat,
@ -184,6 +184,7 @@ const MessageList: FC<OwnProps & StateProps> = ({
getForceNextPinnedInHeader,
onPinnedIntersectionChange,
isContactRequirePremium,
areAdsEnabled,
}) => {
const {
loadViewportMessages, setScrollOffset, loadSponsoredMessages, loadMessageReactions, copyMessagesByIds,
@ -230,10 +231,10 @@ const MessageList: FC<OwnProps & StateProps> = ({
}, [firstUnreadId]);
useEffect(() => {
if (!isCurrentUserPremium && isChannelChat && isSynced && isReady) {
if (areAdsEnabled && isChannelChat && isSynced && isReady) {
loadSponsoredMessages({ chatId });
}
}, [isCurrentUserPremium, chatId, isSynced, isReady, isChannelChat]);
}, [chatId, isSynced, isReady, isChannelChat, areAdsEnabled]);
// Updated only once when messages are loaded (as we want the unread divider to keep its position)
useSyncEffect(() => {
@ -633,7 +634,7 @@ const MessageList: FC<OwnProps & StateProps> = ({
/>
) : hasMessages ? (
<MessageListContent
isCurrentUserPremium={isCurrentUserPremium}
areAdsEnabled={areAdsEnabled}
chatId={chatId}
isComments={isComments}
isChannelChat={isChannelChat}
@ -705,8 +706,11 @@ export default memo(withGlobal<OwnProps>(
const chatFullInfo = !isUserId(chatId) ? selectChatFullInfo(global, chatId) : undefined;
const isEmptyThread = !selectThreadInfo(global, chatId, threadId)?.messagesCount;
const isCurrentUserPremium = selectIsCurrentUserPremium(global);
const areAdsEnabled = !isCurrentUserPremium || selectUserFullInfo(global, currentUserId)?.areAdsEnabled;
return {
isCurrentUserPremium: selectIsCurrentUserPremium(global),
areAdsEnabled,
isChatLoaded: true,
isRestricted,
restrictionReason,

View File

@ -32,7 +32,7 @@ import SponsoredMessage from './message/SponsoredMessage';
import MessageListBotInfo from './MessageListBotInfo';
interface OwnProps {
isCurrentUserPremium?: boolean;
areAdsEnabled?: boolean;
chatId: string;
threadId: ThreadId;
messageIds: number[];
@ -64,7 +64,7 @@ interface OwnProps {
const UNREAD_DIVIDER_CLASS = 'unread-divider';
const MessageListContent: FC<OwnProps> = ({
isCurrentUserPremium,
areAdsEnabled,
chatId,
threadId,
messageIds,
@ -290,7 +290,7 @@ const MessageListContent: FC<OwnProps> = ({
{withHistoryTriggers && <div ref={backwardsTriggerRef} key="backwards-trigger" className="backwards-trigger" />}
{shouldRenderBotInfo && <MessageListBotInfo isInMessageList key={`bot_info_${chatId}`} chatId={chatId} />}
{dateGroups.flat()}
{!isCurrentUserPremium && isViewportNewest && (
{areAdsEnabled && isViewportNewest && (
<SponsoredMessage key={chatId} chatId={chatId} containerRef={containerRef} />
)}
{withHistoryTriggers && (

View File

@ -674,47 +674,43 @@
}
}
.message-action-button {
.message-action-buttons {
position: absolute;
bottom: 0;
color: white;
background-color: var(--pattern-color);
border-radius: 1rem;
display: flex;
flex-direction: column;
opacity: 0;
transition: opacity 150ms, backdrop-filter 150ms, filter 150ms;
transition: opacity 150ms;
@media (pointer: coarse) {
opacity: 1 !important;
}
}
&:hover,
&:active,
&:focus {
background-color: var(--pattern-color) !important;
backdrop-filter: brightness(115%);
@supports not (backdrop-filter: brightness(115%)) {
filter: brightness(115%);
}
}
.message-action-button {
color: white;
}
&:hover,
&[data-is-document-group-hover] {
.message-action-button {
.message-action-buttons {
opacity: 1;
}
}
.message-action-button-shown {
.message-action-buttons-shown {
opacity: 1;
}
&.own .message-action-button {
&.own .message-action-buttons {
left: -3rem;
}
&:not(.own) .message-action-button {
&:not(.own) .message-action-buttons {
right: -3rem;
}

View File

@ -1389,7 +1389,7 @@ const Message: FC<OwnProps & StateProps> = ({
/>
{!isInDocumentGroup && (
<div className="message-select-control">
{isSelected && <i className="icon icon-select" />}
{isSelected && <Icon name="select" />}
</div>
)}
{isLastInDocumentGroup && (
@ -1398,7 +1398,7 @@ const Message: FC<OwnProps & StateProps> = ({
onClick={handleDocumentGroupSelectAll}
>
{isGroupSelected && (
<i className="icon icon-select" />
<Icon name="select" />
)}
</div>
)}
@ -1419,33 +1419,38 @@ const Message: FC<OwnProps & StateProps> = ({
)}
{renderContent()}
{!isInDocumentGroupNotLast && metaPosition === 'standalone' && !isStoryMention && renderReactionsAndMeta()}
{canShowActionButton && canForward ? (
<Button
className={buildClassName(
'message-action-button', isLoadingComments && 'message-action-button-shown',
)}
color="translucent-white"
round
size="tiny"
ariaLabel={lang('lng_context_forward_msg')}
onClick={isLastInDocumentGroup ? handleGroupForward : handleForward}
{canShowActionButton && (
<div className={buildClassName(
'message-action-buttons',
isLoadingComments && 'message-action-buttons-shown',
)}
>
<i className="icon icon-share-filled" />
</Button>
) : canShowActionButton && canFocus ? (
<Button
className={buildClassName(
'message-action-button', isLoadingComments && 'message-action-button-shown',
{canForward && (
<Button
className="message-action-button"
color="translucent-white"
round
size="tiny"
ariaLabel={lang('lng_context_forward_msg')}
onClick={isLastInDocumentGroup ? handleGroupForward : handleForward}
>
<Icon name="share-filled" />
</Button>
)}
color="translucent-white"
round
size="tiny"
ariaLabel="Focus message"
onClick={isPinnedList ? handleFocus : handleFocusForwarded}
>
<i className="icon icon-arrow-right" />
</Button>
) : undefined}
{canFocus && (
<Button
className="message-action-button"
color="translucent-white"
round
size="tiny"
ariaLabel="Focus message"
onClick={isPinnedList ? handleFocus : handleFocusForwarded}
>
<Icon name="arrow-right" />
</Button>
)}
</div>
)}
{withCommentButton && (
<CommentButton
threadInfo={repliesThreadInfo}

View File

@ -118,6 +118,7 @@ type OwnProps = {
onAboutAds?: NoneToVoidFunction;
onSponsoredHide?: NoneToVoidFunction;
onSponsorInfo?: NoneToVoidFunction;
onSponsoredReport?: NoneToVoidFunction;
onTranslate?: NoneToVoidFunction;
onShowOriginal?: NoneToVoidFunction;
onSelectLanguage?: NoneToVoidFunction;
@ -207,6 +208,7 @@ const MessageContextMenu: FC<OwnProps> = ({
onAboutAds,
onSponsoredHide,
onSponsorInfo,
onSponsoredReport,
onReactionPickerOpen,
onTranslate,
onShowOriginal,
@ -443,9 +445,21 @@ const MessageContextMenu: FC<OwnProps> = ({
{isSponsoredMessage && message.sponsorInfo && (
<MenuItem icon="channel" onClick={onSponsorInfo}>{lang('SponsoredMessageSponsor')}</MenuItem>
)}
{isSponsoredMessage && <MenuItem icon="help" onClick={onAboutAds}>{lang('SponsoredMessageInfo')}</MenuItem>}
{isSponsoredMessage && (
<MenuItem icon="info" onClick={onAboutAds}>
{lang(message.canReport ? 'AboutRevenueSharingAds' : 'SponsoredMessageInfo')}
</MenuItem>
)}
{isSponsoredMessage && message.canReport && (
<MenuItem icon="hand-stop" onClick={onSponsoredReport}>
{lang('ReportAd')}
</MenuItem>
)}
{isSponsoredMessage && onSponsoredHide && (
<MenuItem icon="stop" onClick={onSponsoredHide}>{lang('HideAd')}</MenuItem>
<>
<MenuSeparator />
<MenuItem icon="close-circle" onClick={onSponsoredHide}>{lang('HideAd')}</MenuItem>
</>
)}
{(canShowSeenBy || canShowReactionsCount) && !isSponsoredMessage && (
<>

View File

@ -4,6 +4,7 @@
margin-top: -0.5rem;
margin-bottom: 0.5rem;
z-index: calc(var(--z-sticky-date) + 1);
&::before {
display: none;
@ -36,26 +37,9 @@
color: var(--color-text);
}
&.with-avatar {
padding-left: 2.5rem !important;
& > .Avatar {
display: flex !important;
}
@media (max-width: 600px) {
padding-left: 2.875rem !important;
.message-content {
max-width: min(29rem, calc(100vw - 7.0625rem)) !important;
}
}
}
.message-action-button {
.message-action-buttons {
bottom: auto !important;
top: 0.5rem;
top: 0;
}
.message-content {
@ -67,7 +51,7 @@
}
.channel-avatar {
--radius: 0.125rem;
--radius: 0.25rem;
float: right;
margin: 0 0 0.5rem 0.5rem;
@ -86,7 +70,6 @@
font-size: calc(var(--message-text-size, 1rem) - 0.125rem);
background-color: var(--accent-background-color);
border-radius: 0.375rem;
position: relative;
overflow: hidden;
&::before {
@ -131,4 +114,19 @@
.svg-appendix {
transform: translate(0.01875rem, -0.00625rem);
}
.ad-about {
font-size: 0.6875rem;
margin-inline-start: 0.25rem;
border-radius: 1rem;
padding-inline: 0.375rem;
transition: 150ms filter ease-in;
background: var(--accent-background-active-color);
cursor: var(--custom-cursor, pointer);
filter: opacity(0.8);
&:hover {
filter: opacity(1);
}
}
}

View File

@ -1,23 +1,16 @@
import type { MouseEvent as ReactMouseEvent, RefObject } from 'react';
import type { RefObject } from 'react';
import type { FC } from '../../../lib/teact/teact';
import React, { memo, useEffect, useRef } from '../../../lib/teact/teact';
import { getActions, withGlobal } from '../../../global';
import type {
ApiChat, ApiSponsoredMessage, ApiUser,
} from '../../../api/types';
import type { ApiSponsoredMessage } from '../../../api/types';
import { getChatTitle, getUserFullName } from '../../../global/helpers';
import { selectChat, selectSponsoredMessage, selectUser } from '../../../global/selectors';
import { selectSponsoredMessage } from '../../../global/selectors';
import buildClassName from '../../../util/buildClassName';
import { extractCurrentThemeParams } from '../../../util/themeStyle';
import { IS_ANDROID, IS_TOUCH_ENV } from '../../../util/windowEnvironment';
import { getPeerColorClass } from '../../common/helpers/peerColor';
import renderText from '../../common/helpers/renderText';
import { IS_ANDROID } from '../../../util/windowEnvironment';
import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities';
import { preventMessageInputBlur } from '../helpers/preventMessageInputBlur';
import useAppLayout from '../../../hooks/useAppLayout';
import useContextMenuHandlers from '../../../hooks/useContextMenuHandlers';
import useFlag from '../../../hooks/useFlag';
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
@ -26,6 +19,8 @@ import useLastCallback from '../../../hooks/useLastCallback';
import AboutAdsModal from '../../common/AboutAdsModal.async';
import Avatar from '../../common/Avatar';
import Icon from '../../common/Icon';
import PeerColorWrapper from '../../common/PeerColorWrapper';
import Button from '../../ui/Button';
import MessageAppendix from './MessageAppendix';
import SponsoredMessageContextMenuContainer from './SponsoredMessageContextMenuContainer.async';
@ -39,31 +34,21 @@ type OwnProps = {
type StateProps = {
message?: ApiSponsoredMessage;
peer?: ApiChat;
bot?: ApiUser;
channel?: ApiChat;
};
const INTERSECTION_DEBOUNCE_MS = 200;
const SponsoredMessage: FC<OwnProps & StateProps> = ({
chatId,
peer,
message,
containerRef,
bot,
channel,
}) => {
const {
viewSponsoredMessage,
openChat,
openChatByInvite,
requestAppWebView,
startBot,
focusMessage,
openUrl,
openPremiumModal,
hideSponsoredMessages,
clickSponsoredMessage,
reportSponsoredMessage,
} = getActions();
const lang = useLang();
@ -83,11 +68,8 @@ const SponsoredMessage: FC<OwnProps & StateProps> = ({
isContextMenuOpen, contextMenuPosition,
handleBeforeContextMenu, handleContextMenu,
handleContextMenuClose, handleContextMenuHide,
} = useContextMenuHandlers(ref, IS_TOUCH_ENV, true, IS_ANDROID);
} = useContextMenuHandlers(ref, undefined, true, IS_ANDROID);
const [isAboutAdsModalOpen, openAboutAdsModal, closeAboutAdsModal] = useFlag(false);
const { isMobile } = useAppLayout();
const withAvatar = Boolean(message?.isAvatarShown && peer);
const isBotApp = Boolean(message?.botApp);
useEffect(() => {
return shouldObserve ? observeIntersection(contentRef.current!, (target) => {
@ -102,146 +84,30 @@ const SponsoredMessage: FC<OwnProps & StateProps> = ({
handleBeforeContextMenu(e);
};
const handleAvatarClick = useLastCallback(() => {
if (!peer) {
return;
}
openChat({ id: peer.id });
const handleReportSponsoredMessage = useLastCallback(() => {
reportSponsoredMessage({ chatId, randomId: message!.randomId });
});
const handleLinkClick = useLastCallback((e: ReactMouseEvent<HTMLButtonElement, MouseEvent>) => {
e.preventDefault();
clickSponsoredMessage({ chatId });
openUrl({ url: message!.webPage!.url, shouldSkipModal: true });
return false;
});
const handleCloseSponsoredMessage = useLastCallback(() => {
openPremiumModal();
const handleHideSponsoredMessage = useLastCallback(() => {
hideSponsoredMessages();
});
const handleClick = useLastCallback(() => {
if (!message) return;
clickSponsoredMessage({ chatId });
if (isBotApp) {
const { shortName } = message.botApp!;
const theme = extractCurrentThemeParams();
requestAppWebView({
botId: message.chatId!,
appName: shortName,
startApp: message.startParam,
theme,
});
} else if (message.chatInviteHash) {
openChatByInvite({ hash: message.chatInviteHash });
} else if (message.channelPostId) {
focusMessage({ chatId: message.chatId!, messageId: message.channelPostId });
} else {
openChat({ id: message.chatId });
if (message.startParam) {
startBot({
botId: message.chatId!,
param: message.startParam,
});
}
}
openUrl({ url: message!.url, shouldSkipModal: true });
});
if (!message) {
return undefined;
}
function renderAvatar() {
return (
<Avatar
size={isMobile ? 'small-mobile' : 'small'}
peer={peer}
onClick={peer ? handleAvatarClick : undefined}
/>
);
}
function renderPhoto() {
if (message?.botApp) {
if (!message.botApp.photo) return undefined;
return (
<Avatar
size="large"
peer={bot}
photo={message.botApp.photo}
className={buildClassName('channel-avatar', lang.isRtl && 'is-rtl')}
/>
);
}
if (channel) {
return (
<Avatar
size="large"
peer={channel}
className={buildClassName('channel-avatar', lang.isRtl && 'is-rtl')}
/>
);
}
return undefined;
}
function renderContent() {
if (message?.webPage) {
return (
<>
<div className="text-content with-meta" dir="auto" ref={contentRef}>
<div className="message-title message-peer" dir="ltr">
{renderText(message.webPage.siteName)}
</div>
<span className="text-content-inner" dir="auto">
{renderTextWithEntities({
text: message!.text.text,
entities: message!.text.entities,
})}
</span>
</div>
<Button
className="SponsoredMessage__button"
size="tiny"
color="translucent"
isRectangular
onClick={handleLinkClick}
>
<i className="icon icon-arrow-right" aria-hidden />
{lang('OpenLink')}
</Button>
</>
);
}
const buttonText = message?.buttonText ?? (
isBotApp
? lang('BotWebAppInstantViewOpen')
: (message!.isBot
? lang('Conversation.ViewBot')
: lang(message!.channelPostId ? 'Conversation.ViewPost' : 'Conversation.ViewChannel')
));
const title = isBotApp
? message!.botApp!.title
: (bot
? renderText(getUserFullName(bot) || '')
: (channel ? renderText(message!.chatInviteTitle || getChatTitle(lang, channel) || '') : '')
);
if (!message) return undefined;
return (
<>
<div className="message-title message-peer" dir="auto">{title}</div>
<div className="message-title message-peer" dir="auto">{message.title}</div>
<div className="text-content with-meta" dir="auto" ref={contentRef}>
<span className="text-content-inner" dir="auto">
{renderTextWithEntities({
@ -258,48 +124,64 @@ const SponsoredMessage: FC<OwnProps & StateProps> = ({
isRectangular
onClick={handleClick}
>
{buttonText}
{message.buttonText}
</Button>
</>
);
}
const contentClassName = buildClassName(
'message-content has-shadow has-solid-background has-appendix',
getPeerColorClass(bot || peer || channel),
);
return (
<div
ref={ref}
key="sponsored-message"
className={buildClassName('SponsoredMessage Message open', withAvatar && 'with-avatar')}
className="SponsoredMessage Message open"
>
{withAvatar && renderAvatar()}
<div
className={contentClassName}
className="message-content has-shadow has-solid-background has-appendix"
dir="auto"
onMouseDown={handleMouseDown}
onContextMenu={handleContextMenu}
>
<div className="content-inner" dir="auto">
{renderPhoto()}
<PeerColorWrapper peerColor={message.peerColor} className="content-inner" dir="auto">
{message.photo && (
<Avatar
size="large"
photo={message.photo}
className={buildClassName('channel-avatar', lang.isRtl && 'is-rtl')}
/>
)}
<span className="message-title message-type">
{message!.isRecommended ? lang('Message.RecommendedLabel') : lang('SponsoredMessage')}
<span onClick={openAboutAdsModal} className="ad-about">{lang('SponsoredMessageAdWhatIsThis')}</span>
</span>
{renderContent()}
</div>
</PeerColorWrapper>
<MessageAppendix />
<Button
className="message-action-button"
color="translucent-white"
round
size="tiny"
ariaLabel={lang('Close')}
onClick={handleCloseSponsoredMessage}
>
<i className="icon icon-close" aria-hidden />
</Button>
<div className="message-action-buttons">
<Button
className="message-action-button"
color="translucent-white"
round
size="tiny"
ariaLabel={lang('Close')}
onClick={handleHideSponsoredMessage}
>
<Icon name="close" />
</Button>
{message.canReport && (
<Button
className="message-action-button"
color="translucent-white"
round
size="tiny"
ariaLabel={lang('More')}
onClick={handleContextMenu}
onContextMenu={handleContextMenu}
>
<Icon name="more" />
</Button>
)}
</div>
</div>
{contextMenuPosition && (
<SponsoredMessageContextMenuContainer
@ -307,12 +189,14 @@ const SponsoredMessage: FC<OwnProps & StateProps> = ({
anchor={contextMenuPosition}
message={message!}
onAboutAds={openAboutAdsModal}
onReportAd={handleReportSponsoredMessage}
onClose={handleContextMenuClose}
onCloseAnimationEnd={handleContextMenuHide}
/>
)}
<AboutAdsModal
isOpen={isAboutAdsModalOpen}
isRevenueSharing={message.canReport}
onClose={closeAboutAdsModal}
/>
</div>
@ -322,14 +206,9 @@ const SponsoredMessage: FC<OwnProps & StateProps> = ({
export default memo(withGlobal<OwnProps>(
(global, { chatId }): StateProps => {
const message = selectSponsoredMessage(global, chatId);
const peer = message?.chatId ? selectChat(global, message?.chatId) : undefined;
const { chatId: fromChatId, isBot } = message || {};
return {
message,
peer,
bot: fromChatId && isBot ? selectUser(global, fromChatId) : undefined,
channel: !isBot && fromChatId ? selectChat(global, fromChatId) : undefined,
};
},
)(SponsoredMessage));

View File

@ -1,11 +1,10 @@
import type { FC } from '../../../lib/teact/teact';
import React, { memo } from '../../../lib/teact/teact';
import { getActions, withGlobal } from '../../../global';
import { getActions } from '../../../global';
import type { ApiSponsoredMessage } from '../../../api/types';
import type { IAnchorPosition } from '../../../types';
import { selectIsCurrentUserPremium, selectIsPremiumPurchaseBlocked } from '../../../global/selectors';
import buildClassName from '../../../util/buildClassName';
import useFlag from '../../../hooks/useFlag';
@ -18,22 +17,19 @@ export type OwnProps = {
isOpen: boolean;
message: ApiSponsoredMessage;
anchor: IAnchorPosition;
onAboutAds: () => void;
onClose: () => void;
onCloseAnimationEnd: () => void;
onAboutAds: NoneToVoidFunction;
onReportAd: NoneToVoidFunction;
onClose: NoneToVoidFunction;
onCloseAnimationEnd: NoneToVoidFunction;
};
type StateProps = {
canBuyPremium?: boolean;
};
const SponsoredMessageContextMenuContainer: FC<OwnProps & StateProps> = ({
const SponsoredMessageContextMenuContainer: FC<OwnProps> = ({
message,
anchor,
onAboutAds,
onReportAd,
onClose,
onCloseAnimationEnd,
canBuyPremium,
}) => {
const { openPremiumModal, showDialog } = getActions();
@ -60,6 +56,11 @@ const SponsoredMessageContextMenuContainer: FC<OwnProps & StateProps> = ({
});
});
const handleReportSponsoredMessage = useLastCallback(() => {
closeMenu();
onReportAd();
});
if (!anchor) {
return undefined;
}
@ -73,17 +74,12 @@ const SponsoredMessageContextMenuContainer: FC<OwnProps & StateProps> = ({
onClose={closeMenu}
onCloseAnimationEnd={closeMenu}
onAboutAds={handleAboutAdsOpen}
onSponsoredHide={canBuyPremium ? handleSponsoredHide : undefined}
onSponsoredHide={handleSponsoredHide}
onSponsorInfo={handleSponsorInfo}
onSponsoredReport={handleReportSponsoredMessage}
/>
</div>
);
};
export default memo(withGlobal<OwnProps>(
(global): StateProps => {
return {
canBuyPremium: !selectIsCurrentUserPremium(global) && !selectIsPremiumPurchaseBlocked(global),
};
},
)(SponsoredMessageContextMenuContainer));
export default memo(SponsoredMessageContextMenuContainer);

View File

@ -12,6 +12,7 @@ import ChatlistModal from './chatlist/ChatlistModal.async';
import GiftCodeModal from './giftcode/GiftCodeModal.async';
import InviteViaLinkModal from './inviteViaLink/InviteViaLinkModal.async';
import OneTimeMediaModal from './oneTimeMedia/OneTimeMediaModal.async';
import ReportAdModal from './reportAd/ReportAdModal.async';
import UrlAuthModal from './urlAuth/UrlAuthModal.async';
import WebAppModal from './webApp/WebAppModal.async';
@ -24,6 +25,7 @@ type ModalKey = keyof Pick<TabState,
'oneTimeMediaModal' |
'inviteViaLinkModal' |
'requestedAttachBotInstall' |
'reportAdModal' |
'webApp'
>;
@ -47,6 +49,7 @@ const MODALS: ModalRegistry = {
oneTimeMediaModal: OneTimeMediaModal,
inviteViaLinkModal: InviteViaLinkModal,
requestedAttachBotInstall: AttachBotInstallModal,
reportAdModal: ReportAdModal,
webApp: WebAppModal,
};
const MODAL_KEYS = Object.keys(MODALS) as ModalKey[];

View File

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

View File

@ -0,0 +1,54 @@
.slide { // Do not remove .slide, identifier used in JS
overflow-x: hidden;
}
.modalTitle {
display: flex;
flex-direction: column;
}
.optionText {
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.option {
margin-bottom: 0.25rem !important;
}
.optionButton {
padding-block: 0 !important;
}
.sectionTitle {
color: var(--color-primary);
padding-inline: 1rem;
}
.description {
margin-top: 0.75rem;
margin-bottom: 0;
color: var(--color-text-secondary);
padding-inline: 1rem;
}
.title {
margin-bottom: 0;
}
.titleMultiline > .title {
font-size: 1rem;
}
.subtitle {
font-size: 0.875rem;
color: var(--color-text-secondary);
}
.transition {
/* stylelint-disable-next-line plugin/no-low-performance-animation-properties */
transition: height 0.25s ease-in-out;
overflow: hidden;
}

View File

@ -0,0 +1,157 @@
import React, {
memo, useEffect, useMemo, useRef,
} from '../../../lib/teact/teact';
import { getActions } from '../../../global';
import type { TabState } from '../../../global/types';
import { requestMeasure, requestMutation } from '../../../lib/fasterdom/fasterdom';
import buildClassName from '../../../util/buildClassName';
import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback';
import Icon from '../../common/Icon';
import SafeLink from '../../common/SafeLink';
import Button from '../../ui/Button';
import ListItem from '../../ui/ListItem';
import Modal from '../../ui/Modal';
import Transition, { ACTIVE_SLIDE_CLASS_NAME, TO_SLIDE_CLASS_NAME } from '../../ui/Transition';
import styles from './ReportAdModal.module.scss';
const ADDED_PADDING = 40;
export type OwnProps = {
modal: TabState['reportAdModal'];
};
const ReportAdModal = ({
modal,
}: OwnProps) => {
const {
reportSponsoredMessage, closeReportAdModal, openPreviousReportAdModal,
} = getActions();
const lang = useLang();
const isOpen = Boolean(modal);
// eslint-disable-next-line no-null/no-null
const transitionRef = useRef<HTMLDivElement>(null);
const handleOptionClick = useLastCallback((e, option: string) => {
const { chatId, randomId } = modal!;
reportSponsoredMessage({ chatId, randomId, option });
});
const [renderingSection, renderingDepth] = useMemo(() => {
if (!modal) return [undefined, 0];
const sectionDepth = modal.sections.length - 1;
return [modal.sections[sectionDepth], sectionDepth];
}, [modal]);
const handleBackClick = useLastCallback(() => {
if (!renderingDepth) {
closeReportAdModal();
return;
}
openPreviousReportAdModal();
});
const bottomText = useMemo(() => {
if (!modal) return undefined;
const template = lang('lng_report_sponsored_reported_learn');
const parts = template.split('{link}');
return [
parts[0],
<SafeLink text={lang('lng_report_sponsored_reported_link')} url={lang('ReportAd.Help_URL')} />,
parts[1],
];
}, [lang, modal]);
const header = useMemo(() => {
if (!modal) {
return undefined;
}
const hasSubtitle = Boolean(renderingSection?.subtitle);
return (
<div className="modal-header-condensed">
<Button
round
color="translucent"
size="smaller"
ariaLabel={lang(renderingDepth ? 'Back' : 'Close')}
onClick={handleBackClick}
>
<Icon name={renderingDepth ? 'arrow-left' : 'close'} />
</Button>
<div className={buildClassName('modal-title', styles.modalTitle, hasSubtitle && styles.titleMultiline)}>
<h3 className={styles.title}>{lang('ReportAd')}</h3>
{hasSubtitle && (
<span className={styles.subtitle}>{renderingSection.subtitle}</span>
)}
</div>
</div>
);
}, [lang, modal, renderingDepth, renderingSection?.subtitle]);
useEffect(() => {
if (!modal) return;
const slide = document.querySelector<HTMLElement>(`.${ACTIVE_SLIDE_CLASS_NAME} > .${styles.slide}`);
if (!slide) return;
const height = slide.scrollHeight;
requestMutation(() => {
transitionRef.current!.style.height = `${height + ADDED_PADDING}px`;
});
}, [modal]);
const handleAnimationStart = useLastCallback(() => {
const slide = document.querySelector<HTMLElement>(`.${TO_SLIDE_CLASS_NAME} > .${styles.slide}`)!;
requestMeasure(() => {
const height = slide.scrollHeight;
requestMutation(() => {
transitionRef.current!.style.height = `${height + ADDED_PADDING}px`;
});
});
});
return (
<Modal
isOpen={isOpen}
hasCloseButton
header={header}
onClose={closeReportAdModal}
>
<Transition
name="slide"
className={styles.transition}
ref={transitionRef}
activeKey={renderingDepth}
onStart={handleAnimationStart}
>
<div className={styles.slide}>
<h3 className={styles.sectionTitle}>{renderingSection?.title}</h3>
{renderingSection?.options.map((option) => (
<ListItem
narrow
secondaryIcon="next"
className={styles.option}
buttonClassName={styles.optionButton}
clickArg={option.option}
onClick={handleOptionClick}
>
<div className={styles.optionText}>{option.text}</div>
</ListItem>
))}
</div>
<p className={styles.description}>{bottomText}</p>
</Transition>
</Modal>
);
};
export default memo(ReportAdModal);

View File

@ -7,7 +7,7 @@ import useLang from '../../hooks/useLang';
import styles from './Separator.module.scss';
type OwnProps = {
children: React.ReactNode;
children?: React.ReactNode;
className?: string;
};

View File

@ -49,7 +49,7 @@ export const MEDIA_PROGRESSIVE_CACHE_DISABLED = false;
export const MEDIA_PROGRESSIVE_CACHE_NAME = 'tt-media-progressive';
export const MEDIA_CACHE_MAX_BYTES = 512 * 1024; // 512 KB
export const CUSTOM_BG_CACHE_NAME = 'tt-custom-bg';
export const LANG_CACHE_NAME = 'tt-lang-packs-v34';
export const LANG_CACHE_NAME = 'tt-lang-packs-v35';
export const ASSET_CACHE_NAME = 'tt-assets';
export const AUTODOWNLOAD_FILESIZE_MB_LIMITS = [1, 5, 10, 50, 100, 500];
export const DATA_BROADCAST_CHANNEL_NAME = 'tt-global';

View File

@ -67,6 +67,7 @@ import {
addChatMessagesById,
addChats,
addUsers,
deleteSponsoredMessage,
removeOutlyingList,
removeRequestedMessageTranslation,
replaceSettings,
@ -90,6 +91,7 @@ import {
updateThreadUnreadFromForwardedMessage,
updateTopic,
updateUploadByMessageKey,
updateUserFullInfo,
updateUsers,
} from '../../reducers';
import { updateTabState } from '../../reducers/tabs';
@ -1548,6 +1550,79 @@ addActionHandler('clickSponsoredMessage', (global, actions, payload): ActionRetu
void callApi('clickSponsoredMessage', { chat, random: message.randomId });
});
addActionHandler('reportSponsoredMessage', async (global, actions, payload): Promise<void> => {
const {
chatId, randomId, option = '', tabId = getCurrentTabId(),
} = payload;
const chat = selectChat(global, chatId);
if (!chat) {
return;
}
const result = await callApi('reportSponsoredMessage', { chat, randomId, option });
if (!result) return;
if (result.type === 'premiumRequired') {
actions.openPremiumModal({ initialSection: 'no_ads', tabId });
actions.closeReportAdModal({ tabId });
return;
}
if (result.type === 'reported' || result.type === 'hidden') {
actions.showNotification({
message: translate(result.type === 'reported' ? 'AdReported' : 'AdHidden'),
tabId,
});
actions.closeReportAdModal({ tabId });
global = getGlobal();
global = deleteSponsoredMessage(global, chatId);
setGlobal(global);
return;
}
if (result.type === 'selectOption') {
global = getGlobal();
const oldSections = selectTabState(global, tabId).reportAdModal?.sections;
const selectedOption = oldSections?.[oldSections.length - 1]?.options.find((o) => o.option === option);
const newSection = {
title: result.title,
options: result.options,
subtitle: selectedOption?.text,
};
global = updateTabState(global, {
reportAdModal: {
chatId,
randomId,
sections: oldSections ? [...oldSections, newSection] : [newSection],
},
}, tabId);
setGlobal(global);
}
});
addActionHandler('hideSponsoredMessages', async (global, actions, payload): Promise<void> => {
const { tabId = getCurrentTabId() } = payload || {};
const isCurrentUserPremium = selectIsCurrentUserPremium(global);
if (!isCurrentUserPremium) {
actions.openPremiumModal({ initialSection: 'no_ads', tabId });
return;
}
const result = await callApi('toggleSponsoredMessages', { enabled: false });
if (!result) return;
global = getGlobal();
global = updateUserFullInfo(global, global.currentUserId!, {
areAdsEnabled: false,
});
setGlobal(global);
actions.showNotification({
message: translate('AdHidden'),
tabId,
});
});
addActionHandler('fetchUnreadMentions', async (global, actions, payload): Promise<void> => {
const { chatId, offsetId } = payload;
await fetchUnreadMentions(global, chatId, offsetId);

View File

@ -863,6 +863,33 @@ addActionHandler('closeOneTimeMediaModal', (global, actions, payload): ActionRet
setGlobal(global);
});
addActionHandler('closeReportAdModal', (global, actions, payload): ActionReturnType => {
const { tabId = getCurrentTabId() } = payload || {};
return updateTabState(global, {
reportAdModal: undefined,
}, tabId);
});
addActionHandler('openPreviousReportAdModal', (global, actions, payload): ActionReturnType => {
const { tabId = getCurrentTabId() } = payload || {};
const reportAdModal = selectTabState(global, tabId).reportAdModal;
if (!reportAdModal) {
return undefined;
}
if (reportAdModal.sections.length === 1) {
actions.closeReportAdModal({ tabId });
return undefined;
}
return updateTabState(global, {
reportAdModal: {
...reportAdModal,
sections: reportAdModal.sections.slice(0, -1),
},
}, tabId);
});
function copyTextForMessages(global: GlobalState, chatId: string, messageIds: number[]) {
const { type: messageListType, threadId } = selectCurrentMessageList(global) || {};
const lang = langProvider.translate;

View File

@ -671,6 +671,23 @@ export function updateSponsoredMessage<T extends GlobalState>(
};
}
export function deleteSponsoredMessage<T extends GlobalState>(
global: T, chatId: string,
): T {
const byChatId = global.messages.sponsoredByChatId;
if (!byChatId[chatId]) {
return global;
}
return {
...global,
messages: {
...global.messages,
sponsoredByChatId: omit(byChatId, [chatId]),
},
};
}
export function updateFocusDirection<T extends GlobalState>(
global: T, direction?: FocusDirection,
...[tabId = getCurrentTabId()]: TabArgs<T>

View File

@ -533,6 +533,19 @@ export type TabState = {
openedStickerSetShortName?: string;
openedCustomEmojiSetIds?: string[];
reportAdModal?: {
chatId: string;
randomId: string;
sections: {
title: string;
subtitle?: string;
options: {
text: string;
option: string;
}[];
}[];
};
activeDownloads: {
byChatId: {
[chatId: string]: {
@ -1465,6 +1478,14 @@ export interface ActionPayloads {
clickSponsoredMessage: {
chatId: string;
};
reportSponsoredMessage: {
chatId: string;
randomId: string;
option?: string;
} & WithTabId;
openPreviousReportAdModal: WithTabId | undefined;
closeReportAdModal: WithTabId | undefined;
hideSponsoredMessages: WithTabId | undefined;
loadSendAs: {
chatId: string;
};

View File

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

File diff suppressed because one or more lines are too long

View File

@ -80,8 +80,8 @@ chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5
chatForbidden#6592a1a7 id:long title:string = Chat;
channel#aadfc8f flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# stories_hidden:flags2.1?true stories_hidden_min:flags2.2?true stories_unavailable:flags2.3?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector<Username> stories_max_id:flags2.4?int color:flags2.7?PeerColor profile_color:flags2.8?PeerColor emoji_status:flags2.9?EmojiStatus level:flags2.10?int = Chat;
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#c9d31138 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions = ChatFull;
channelFull#44c054a7 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet = ChatFull;
chatFull#2633421b flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions reactions_limit:flags.20?int = ChatFull;
channelFull#bbab348d flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions reactions_limit:flags2.13?int stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet = ChatFull;
chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant;
chatParticipantAdmin#a0933f5b user_id:long inviter_id:long date:int = ChatParticipant;
@ -188,7 +188,7 @@ inputReportReasonGeoIrrelevant#dbd4feed = ReportReason;
inputReportReasonFake#f5ddd6e7 = ReportReason;
inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
inputReportReasonPersonalDetails#9ec7863d = ReportReason;
userFull#cc997720 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:# 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 premium_gifts:flags.19?Vector<PremiumGiftOption> 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 = UserFull;
userFull#cc997720 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 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 premium_gifts:flags.19?Vector<PremiumGiftOption> 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 = 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;
@ -359,6 +359,7 @@ updateBotBusinessConnect#8ae5c97a connection:BotBusinessConnection qts:int = Upd
updateBotNewBusinessMessage#9ddb347c flags:# connection_id:string message:Message reply_to_message:flags.0?Message qts:int = Update;
updateBotEditBusinessMessage#7df587c flags:# connection_id:string message:Message reply_to_message:flags.0?Message qts:int = Update;
updateBotDeleteBusinessMessage#a02a982e connection_id:string peer:Peer messages:Vector<int> qts:int = Update;
updateNewStoryReaction#1824e40b story_id:int peer:Peer reaction:Reaction = 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;
@ -927,7 +928,7 @@ inputWallPaperSlug#72091c80 slug:string = InputWallPaper;
inputWallPaperNoFile#967a462e id:long = InputWallPaper;
account.wallPapersNotModified#1c199183 = account.WallPapers;
account.wallPapers#cdc3858c hash:long wallpapers:Vector<WallPaper> = account.WallPapers;
codeSettings#ad253d78 flags:# allow_flashcall:flags.0?true current_number:flags.1?true allow_app_hash:flags.4?true allow_missed_call:flags.5?true allow_firebase:flags.7?true logout_tokens:flags.6?Vector<bytes> token:flags.8?string app_sandbox:flags.8?Bool = CodeSettings;
codeSettings#ad253d78 flags:# allow_flashcall:flags.0?true current_number:flags.1?true allow_app_hash:flags.4?true allow_missed_call:flags.5?true allow_firebase:flags.7?true unknown_number:flags.9?true logout_tokens:flags.6?Vector<bytes> token:flags.8?string app_sandbox:flags.8?Bool = CodeSettings;
wallPaperSettings#372efcd0 flags:# blur:flags.1?true motion:flags.2?true background_color:flags.0?int second_background_color:flags.4?int third_background_color:flags.5?int fourth_background_color:flags.6?int intensity:flags.3?int rotation:flags.4?int emoticon:flags.7?string = WallPaperSettings;
autoDownloadSettings#baa57628 flags:# disabled:flags.0?true video_preload_large:flags.1?true audio_preload_next:flags.2?true phonecalls_less_data:flags.3?true stories_preload:flags.4?true photo_size_max:int video_size_max:long file_size_max:long video_upload_maxbitrate:int small_queue_active_operations_max:int large_queue_active_operations_max:int = AutoDownloadSettings;
account.autoDownloadSettings#63cacf26 low:AutoDownloadSettings medium:AutoDownloadSettings high:AutoDownloadSettings = account.AutoDownloadSettings;
@ -967,6 +968,7 @@ inputThemeSettings#8fde504f flags:# message_colors_animated:flags.2?true base_th
themeSettings#fa58b6d4 flags:# message_colors_animated:flags.2?true base_theme:BaseTheme accent_color:int outbox_accent_color:flags.3?int message_colors:flags.0?Vector<int> wallpaper:flags.1?WallPaper = ThemeSettings;
webPageAttributeTheme#54b56617 flags:# documents:flags.0?Vector<Document> settings:flags.1?ThemeSettings = WebPageAttribute;
webPageAttributeStory#2e94c3e7 flags:# peer:Peer id:int story:flags.0?StoryItem = WebPageAttribute;
webPageAttributeStickerSet#50cc03d3 flags:# emojis:flags.0?true text_color:flags.1?true stickers:Vector<Document> = WebPageAttribute;
messages.votesList#4899484e flags:# count:int votes:Vector<MessagePeerVote> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = messages.VotesList;
bankCardOpenUrl#f568028a url:string name:string = BankCardOpenUrl;
payments.bankCardData#3e24e573 title:string open_urls:Vector<BankCardOpenUrl> = payments.BankCardData;
@ -1041,7 +1043,7 @@ botCommandScopePeerUser#a1321f3 peer:InputPeer user_id:InputUser = BotCommandSco
account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordResult;
account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
account.resetPasswordOk#e926d63e = account.ResetPasswordResult;
sponsoredMessage#ed5383f7 flags:# recommended:flags.5?true show_peer_photo:flags.6?true can_report:flags.12?true random_id:bytes from_id:flags.3?Peer chat_invite:flags.4?ChatInvite chat_invite_hash:flags.4?string channel_post:flags.2?int start_param:flags.0?string webpage:flags.9?SponsoredWebPage app:flags.10?BotApp message:string entities:flags.1?Vector<MessageEntity> button_text:flags.11?string sponsor_info:flags.7?string additional_info:flags.8?string = SponsoredMessage;
sponsoredMessage#bdedf566 flags:# recommended:flags.5?true can_report:flags.12?true random_id:bytes url:string title:string message:string entities:flags.1?Vector<MessageEntity> photo:flags.6?Photo color:flags.13?PeerColor button_text:string sponsor_info:flags.7?string additional_info:flags.8?string = SponsoredMessage;
messages.sponsoredMessages#c9ee1d87 flags:# posts_between:flags.0?int messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
messages.sponsoredMessagesEmpty#1839490f = messages.SponsoredMessages;
searchResultsCalendarPeriod#c9b0539f date:int min_msg_id:int max_msg_id:int count:int = SearchResultsCalendarPeriod;
@ -1165,14 +1167,13 @@ bots.botInfo#e8a775b0 name:string about:string description:string = bots.BotInfo
messagePeerVote#b6cc2d5c peer:Peer option:bytes date:int = MessagePeerVote;
messagePeerVoteInputOption#74cda504 peer:Peer date:int = MessagePeerVote;
messagePeerVoteMultiple#4628f6e6 peer:Peer options:Vector<bytes> date:int = MessagePeerVote;
sponsoredWebPage#3db8ec63 flags:# url:string site_name:string photo:flags.0?Photo = SponsoredWebPage;
storyViews#8d595cd6 flags:# has_viewers:flags.1?true views_count:int forwards_count:flags.2?int reactions:flags.3?Vector<ReactionCount> reactions_count:flags.4?int recent_viewers:flags.0?Vector<long> = StoryViews;
storyItemDeleted#51e6ee4f id:int = StoryItem;
storyItemSkipped#ffadc913 flags:# close_friends:flags.8?true id:int date:int expire_date:int = StoryItem;
storyItem#79b26a24 flags:# pinned:flags.5?true public:flags.7?true close_friends:flags.8?true min:flags.9?true noforwards:flags.10?true edited:flags.11?true contacts:flags.12?true selected_contacts:flags.13?true out:flags.16?true id:int date:int from_id:flags.18?Peer fwd_from:flags.17?StoryFwdHeader expire_date:int caption:flags.0?string entities:flags.1?Vector<MessageEntity> media:MessageMedia media_areas:flags.14?Vector<MediaArea> privacy:flags.2?Vector<PrivacyRule> views:flags.3?StoryViews sent_reaction:flags.15?Reaction = StoryItem;
stories.allStoriesNotModified#1158fe3e flags:# state:string stealth_mode:StoriesStealthMode = stories.AllStories;
stories.allStories#6efc5e81 flags:# has_more:flags.0?true count:int state:string peer_stories:Vector<PeerStories> chats:Vector<Chat> users:Vector<User> stealth_mode:StoriesStealthMode = stories.AllStories;
stories.stories#5dd8c3c8 count:int stories:Vector<StoryItem> chats:Vector<Chat> users:Vector<User> = stories.Stories;
stories.stories#63c3dd0a flags:# count:int stories:Vector<StoryItem> pinned_to_top:flags.0?Vector<int> chats:Vector<Chat> users:Vector<User> = stories.Stories;
storyView#b0bdeac5 flags:# blocked:flags.0?true blocked_my_stories_from:flags.1?true user_id:long date:int reaction:flags.2?Reaction = StoryView;
storyViewPublicForward#9083670b flags:# blocked:flags.0?true blocked_my_stories_from:flags.1?true message:Message = StoryView;
storyViewPublicRepost#bd74cf49 flags:# blocked:flags.0?true blocked_my_stories_from:flags.1?true peer_id:Peer story:StoryItem = StoryView;
@ -1284,6 +1285,9 @@ broadcastRevenueTransactionProceeds#557e2cc4 amount:long from_date:int to_date:i
broadcastRevenueTransactionWithdrawal#5a590978 flags:# pending:flags.0?true failed:flags.2?true amount:long date:int provider:string transaction_date:flags.1?int transaction_url:flags.1?string = BroadcastRevenueTransaction;
broadcastRevenueTransactionRefund#42d30d2e amount:long date:int provider:string = BroadcastRevenueTransaction;
stats.broadcastRevenueTransactions#87158466 count:int transactions:Vector<BroadcastRevenueTransaction> = stats.BroadcastRevenueTransactions;
reactionNotificationsFromContacts#bac3a61a = ReactionNotificationsFrom;
reactionNotificationsFromAll#4b9e22a0 = ReactionNotificationsFrom;
reactionsNotifySettings#56e34970 flags:# messages_notify_from:flags.0?ReactionNotificationsFrom stories_notify_from:flags.1?ReactionNotificationsFrom sound:NotificationSound show_previews:Bool = ReactionsNotifySettings;
---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;
@ -1345,6 +1349,7 @@ account.getRecentEmojiStatuses#f578105 hash:long = account.EmojiStatuses;
account.reorderUsernames#ef500eab order:Vector<string> = Bool;
account.toggleUsername#58d6b376 username:string active:Bool = Bool;
account.resolveBusinessChatLink#5492e5ee slug:string = account.ResolvedBusinessChatLinks;
account.toggleSponsoredMessages#b9d9a38d enabled:Bool = Bool;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
contacts.getContacts#5dd69e12 hash:long = contacts.Contacts;
@ -1395,7 +1400,7 @@ messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool;
messages.startBot#e6df7378 bot:InputUser peer:InputPeer random_id:long start_param:string = Updates;
messages.getMessagesViews#5784d3e1 peer:InputPeer id:Vector<int> increment:Bool = messages.MessageViews;
messages.migrateChat#a2875319 chat_id:long = Updates;
messages.searchGlobal#4bc6589a flags:# folder_id:flags.0?int q:string filter:MessagesFilter min_date:int max_date:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
messages.searchGlobal#4bc6589a flags:# broadcasts_only:flags.1?true folder_id:flags.0?int q:string filter:MessagesFilter min_date:int max_date:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
messages.getDocumentByHash#b1f2061f sha256:bytes size:long mime_type:string = Document;
messages.getSavedGifs#5cf09635 hash:long = messages.SavedGifs;
messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
@ -1457,7 +1462,7 @@ messages.saveDefaultSendAs#ccfddf96 peer:InputPeer send_as:InputPeer = Bool;
messages.sendReaction#d30d78d4 flags:# big:flags.1?true add_to_recent:flags.2?true peer:InputPeer msg_id:int reaction:flags.0?Vector<Reaction> = Updates;
messages.getMessagesReactions#8bba90e6 peer:InputPeer id:Vector<int> = Updates;
messages.getMessageReactionsList#461b3f48 flags:# peer:InputPeer id:int reaction:flags.0?Reaction offset:flags.1?string limit:int = messages.MessageReactionsList;
messages.setChatAvailableReactions#feb16771 peer:InputPeer available_reactions:ChatReactions = Updates;
messages.setChatAvailableReactions#5a150bd4 flags:# peer:InputPeer available_reactions:ChatReactions reactions_limit:flags.0?int = Updates;
messages.getAvailableReactions#18dea0ac hash:int = messages.AvailableReactions;
messages.setDefaultReaction#4f47a016 reaction:Reaction = Bool;
messages.translateText#63183030 flags:# peer:flags.0?InputPeer id:flags.0?Vector<int> text:flags.1?Vector<TextWithEntities> to_lang:string = messages.TranslatedText;
@ -1557,7 +1562,8 @@ channels.deleteTopicHistory#34435f2d channel:InputChannel top_msg_id:int = messa
channels.toggleParticipantsHidden#6a6e7854 channel:InputChannel enabled:Bool = Updates;
channels.clickSponsoredMessage#18afbc93 channel:InputChannel random_id:bytes = Bool;
channels.toggleViewForumAsMessages#9738bb15 channel:InputChannel enabled:Bool = Updates;
channels.getChannelRecommendations#83b70d97 channel:InputChannel = messages.Chats;
channels.getChannelRecommendations#25a71742 flags:# channel:flags.0?InputChannel = messages.Chats;
channels.reportSponsoredMessage#af8ff6b9 channel:InputChannel random_id:bytes option:bytes = channels.SponsoredMessageReportResult;
bots.setBotInfo#10cf3123 flags:# bot:flags.2?InputUser lang_code:string name:flags.3?string about:flags.0?string description:flags.1?string = Bool;
bots.canSendMessage#1359f4e6 bot:InputUser = Bool;
bots.allowSendMessage#f132e3ef bot:InputUser = Updates;

View File

@ -59,6 +59,7 @@
"account.reorderUsernames",
"account.toggleUsername",
"account.resolveBusinessChatLink",
"account.toggleSponsoredMessages",
"users.getUsers",
"users.getFullUser",
"contacts.getContacts",
@ -221,6 +222,7 @@
"channels.viewSponsoredMessage",
"channels.getSponsoredMessages",
"channels.getChannelRecommendations",
"channels.reportSponsoredMessage",
"bots.canSendMessage",
"bots.allowSendMessage",
"bots.invokeWebViewCustomMethod",

View File

@ -100,8 +100,8 @@ chatForbidden#6592a1a7 id:long title:string = Chat;
channel#aadfc8f flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# stories_hidden:flags2.1?true stories_hidden_min:flags2.2?true stories_unavailable:flags2.3?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector<Username> stories_max_id:flags2.4?int color:flags2.7?PeerColor profile_color:flags2.8?PeerColor emoji_status:flags2.9?EmojiStatus level:flags2.10?int = Chat;
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#c9d31138 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions = ChatFull;
channelFull#44c054a7 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet = ChatFull;
chatFull#2633421b flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions reactions_limit:flags.20?int = ChatFull;
channelFull#bbab348d flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions reactions_limit:flags2.13?int stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet = ChatFull;
chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant;
@ -228,7 +228,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason;
inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
inputReportReasonPersonalDetails#9ec7863d = ReportReason;
userFull#cc997720 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:# 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 premium_gifts:flags.19?Vector<PremiumGiftOption> 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 = UserFull;
userFull#cc997720 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 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 premium_gifts:flags.19?Vector<PremiumGiftOption> 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 = UserFull;
contact#145ade0b user_id:long mutual:Bool = Contact;
@ -412,6 +412,7 @@ updateBotBusinessConnect#8ae5c97a connection:BotBusinessConnection qts:int = Upd
updateBotNewBusinessMessage#9ddb347c flags:# connection_id:string message:Message reply_to_message:flags.0?Message qts:int = Update;
updateBotEditBusinessMessage#7df587c flags:# connection_id:string message:Message reply_to_message:flags.0?Message qts:int = Update;
updateBotDeleteBusinessMessage#a02a982e connection_id:string peer:Peer messages:Vector<int> qts:int = Update;
updateNewStoryReaction#1824e40b story_id:int peer:Peer reaction:Reaction = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -1171,7 +1172,7 @@ inputWallPaperNoFile#967a462e id:long = InputWallPaper;
account.wallPapersNotModified#1c199183 = account.WallPapers;
account.wallPapers#cdc3858c hash:long wallpapers:Vector<WallPaper> = account.WallPapers;
codeSettings#ad253d78 flags:# allow_flashcall:flags.0?true current_number:flags.1?true allow_app_hash:flags.4?true allow_missed_call:flags.5?true allow_firebase:flags.7?true logout_tokens:flags.6?Vector<bytes> token:flags.8?string app_sandbox:flags.8?Bool = CodeSettings;
codeSettings#ad253d78 flags:# allow_flashcall:flags.0?true current_number:flags.1?true allow_app_hash:flags.4?true allow_missed_call:flags.5?true allow_firebase:flags.7?true unknown_number:flags.9?true logout_tokens:flags.6?Vector<bytes> token:flags.8?string app_sandbox:flags.8?Bool = CodeSettings;
wallPaperSettings#372efcd0 flags:# blur:flags.1?true motion:flags.2?true background_color:flags.0?int second_background_color:flags.4?int third_background_color:flags.5?int fourth_background_color:flags.6?int intensity:flags.3?int rotation:flags.4?int emoticon:flags.7?string = WallPaperSettings;
@ -1236,6 +1237,7 @@ themeSettings#fa58b6d4 flags:# message_colors_animated:flags.2?true base_theme:B
webPageAttributeTheme#54b56617 flags:# documents:flags.0?Vector<Document> settings:flags.1?ThemeSettings = WebPageAttribute;
webPageAttributeStory#2e94c3e7 flags:# peer:Peer id:int story:flags.0?StoryItem = WebPageAttribute;
webPageAttributeStickerSet#50cc03d3 flags:# emojis:flags.0?true text_color:flags.1?true stickers:Vector<Document> = WebPageAttribute;
messages.votesList#4899484e flags:# count:int votes:Vector<MessagePeerVote> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = messages.VotesList;
@ -1361,7 +1363,7 @@ account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordR
account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
account.resetPasswordOk#e926d63e = account.ResetPasswordResult;
sponsoredMessage#ed5383f7 flags:# recommended:flags.5?true show_peer_photo:flags.6?true can_report:flags.12?true random_id:bytes from_id:flags.3?Peer chat_invite:flags.4?ChatInvite chat_invite_hash:flags.4?string channel_post:flags.2?int start_param:flags.0?string webpage:flags.9?SponsoredWebPage app:flags.10?BotApp message:string entities:flags.1?Vector<MessageEntity> button_text:flags.11?string sponsor_info:flags.7?string additional_info:flags.8?string = SponsoredMessage;
sponsoredMessage#bdedf566 flags:# recommended:flags.5?true can_report:flags.12?true random_id:bytes url:string title:string message:string entities:flags.1?Vector<MessageEntity> photo:flags.6?Photo color:flags.13?PeerColor button_text:string sponsor_info:flags.7?string additional_info:flags.8?string = SponsoredMessage;
messages.sponsoredMessages#c9ee1d87 flags:# posts_between:flags.0?int messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
messages.sponsoredMessagesEmpty#1839490f = messages.SponsoredMessages;
@ -1565,8 +1567,6 @@ messagePeerVote#b6cc2d5c peer:Peer option:bytes date:int = MessagePeerVote;
messagePeerVoteInputOption#74cda504 peer:Peer date:int = MessagePeerVote;
messagePeerVoteMultiple#4628f6e6 peer:Peer options:Vector<bytes> date:int = MessagePeerVote;
sponsoredWebPage#3db8ec63 flags:# url:string site_name:string photo:flags.0?Photo = SponsoredWebPage;
storyViews#8d595cd6 flags:# has_viewers:flags.1?true views_count:int forwards_count:flags.2?int reactions:flags.3?Vector<ReactionCount> reactions_count:flags.4?int recent_viewers:flags.0?Vector<long> = StoryViews;
storyItemDeleted#51e6ee4f id:int = StoryItem;
@ -1576,7 +1576,7 @@ storyItem#79b26a24 flags:# pinned:flags.5?true public:flags.7?true close_friends
stories.allStoriesNotModified#1158fe3e flags:# state:string stealth_mode:StoriesStealthMode = stories.AllStories;
stories.allStories#6efc5e81 flags:# has_more:flags.0?true count:int state:string peer_stories:Vector<PeerStories> chats:Vector<Chat> users:Vector<User> stealth_mode:StoriesStealthMode = stories.AllStories;
stories.stories#5dd8c3c8 count:int stories:Vector<StoryItem> chats:Vector<Chat> users:Vector<User> = stories.Stories;
stories.stories#63c3dd0a flags:# count:int stories:Vector<StoryItem> pinned_to_top:flags.0?Vector<int> chats:Vector<Chat> users:Vector<User> = stories.Stories;
storyView#b0bdeac5 flags:# blocked:flags.0?true blocked_my_stories_from:flags.1?true user_id:long date:int reaction:flags.2?Reaction = StoryView;
storyViewPublicForward#9083670b flags:# blocked:flags.0?true blocked_my_stories_from:flags.1?true message:Message = StoryView;
@ -1770,6 +1770,11 @@ broadcastRevenueTransactionRefund#42d30d2e amount:long date:int provider:string
stats.broadcastRevenueTransactions#87158466 count:int transactions:Vector<BroadcastRevenueTransaction> = stats.BroadcastRevenueTransactions;
reactionNotificationsFromContacts#bac3a61a = ReactionNotificationsFrom;
reactionNotificationsFromAll#4b9e22a0 = ReactionNotificationsFrom;
reactionsNotifySettings#56e34970 flags:# messages_notify_from:flags.0?ReactionNotificationsFrom stories_notify_from:flags.1?ReactionNotificationsFrom sound:NotificationSound show_previews:Bool = ReactionsNotifySettings;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1913,6 +1918,9 @@ account.deleteBusinessChatLink#60073674 slug:string = Bool;
account.getBusinessChatLinks#6f70dde1 = account.BusinessChatLinks;
account.resolveBusinessChatLink#5492e5ee slug:string = account.ResolvedBusinessChatLinks;
account.updatePersonalChannel#d94305e0 channel:InputChannel = Bool;
account.toggleSponsoredMessages#b9d9a38d enabled:Bool = Bool;
account.getReactionsNotifySettings#6dd654c = ReactionsNotifySettings;
account.setReactionsNotifySettings#316ce548 settings:ReactionsNotifySettings = ReactionsNotifySettings;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
@ -1993,7 +2001,7 @@ messages.startBot#e6df7378 bot:InputUser peer:InputPeer random_id:long start_par
messages.getMessagesViews#5784d3e1 peer:InputPeer id:Vector<int> increment:Bool = messages.MessageViews;
messages.editChatAdmin#a85bd1c2 chat_id:long user_id:InputUser is_admin:Bool = Bool;
messages.migrateChat#a2875319 chat_id:long = Updates;
messages.searchGlobal#4bc6589a flags:# folder_id:flags.0?int q:string filter:MessagesFilter min_date:int max_date:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
messages.searchGlobal#4bc6589a flags:# broadcasts_only:flags.1?true folder_id:flags.0?int q:string filter:MessagesFilter min_date:int max_date:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
messages.reorderStickerSets#78337739 flags:# masks:flags.0?true emojis:flags.1?true order:Vector<long> = Bool;
messages.getDocumentByHash#b1f2061f sha256:bytes size:long mime_type:string = Document;
messages.getSavedGifs#5cf09635 hash:long = messages.SavedGifs;
@ -2097,7 +2105,7 @@ messages.saveDefaultSendAs#ccfddf96 peer:InputPeer send_as:InputPeer = Bool;
messages.sendReaction#d30d78d4 flags:# big:flags.1?true add_to_recent:flags.2?true peer:InputPeer msg_id:int reaction:flags.0?Vector<Reaction> = Updates;
messages.getMessagesReactions#8bba90e6 peer:InputPeer id:Vector<int> = Updates;
messages.getMessageReactionsList#461b3f48 flags:# peer:InputPeer id:int reaction:flags.0?Reaction offset:flags.1?string limit:int = messages.MessageReactionsList;
messages.setChatAvailableReactions#feb16771 peer:InputPeer available_reactions:ChatReactions = Updates;
messages.setChatAvailableReactions#5a150bd4 flags:# peer:InputPeer available_reactions:ChatReactions reactions_limit:flags.0?int = Updates;
messages.getAvailableReactions#18dea0ac hash:int = messages.AvailableReactions;
messages.setDefaultReaction#4f47a016 reaction:Reaction = Bool;
messages.translateText#63183030 flags:# peer:flags.0?InputPeer id:flags.0?Vector<int> text:flags.1?Vector<TextWithEntities> to_lang:string = messages.TranslatedText;
@ -2258,7 +2266,7 @@ channels.toggleParticipantsHidden#6a6e7854 channel:InputChannel enabled:Bool = U
channels.clickSponsoredMessage#18afbc93 channel:InputChannel random_id:bytes = Bool;
channels.updateColor#d8aa3671 flags:# for_profile:flags.1?true channel:InputChannel color:flags.2?int background_emoji_id:flags.0?long = Updates;
channels.toggleViewForumAsMessages#9738bb15 channel:InputChannel enabled:Bool = Updates;
channels.getChannelRecommendations#83b70d97 channel:InputChannel = messages.Chats;
channels.getChannelRecommendations#25a71742 flags:# channel:flags.0?InputChannel = messages.Chats;
channels.updateEmojiStatus#f0d3e6a8 channel:InputChannel emoji_status:EmojiStatus = Updates;
channels.setBoostsToUnblockRestrictions#ad399cee channel:InputChannel boosts:int = Updates;
channels.setEmojiStickers#3cd930b7 channel:InputChannel stickerset:InputStickerSet = Bool;
@ -2398,6 +2406,7 @@ stories.getPeerMaxIDs#535983c3 id:Vector<InputPeer> = Vector<int>;
stories.getChatsToSend#a56a8b60 = messages.Chats;
stories.togglePeerStoriesHidden#bd0415c4 peer:InputPeer hidden:Bool = Bool;
stories.getStoryReactionsList#b9b2881f flags:# forwards_first:flags.2?true peer:InputPeer id:int reaction:flags.0?Reaction offset:flags.1?string limit:int = stories.StoryReactionsList;
stories.togglePinnedToTop#b297e9b peer:InputPeer id:Vector<int> = Bool;
premium.getBoostsList#60f67660 flags:# gifts:flags.0?true peer:InputPeer offset:string limit:int = premium.BoostsList;
premium.getMyBoosts#be77b4a = premium.MyBoosts;

View File

@ -158,110 +158,112 @@ $icons-map: (
"my-notes": "\f17f",
"new-chat-filled": "\f180",
"next": "\f181",
"noise-suppression": "\f182",
"non-contacts": "\f183",
"one-filled": "\f184",
"open-in-new-tab": "\f185",
"password-off": "\f186",
"pause": "\f187",
"permissions": "\f188",
"phone-discard-outline": "\f189",
"phone-discard": "\f18a",
"phone": "\f18b",
"photo": "\f18c",
"pin-badge": "\f18d",
"pin-list": "\f18e",
"pin": "\f18f",
"pinned-chat": "\f190",
"pinned-message": "\f191",
"pip": "\f192",
"play-story": "\f193",
"play": "\f194",
"poll": "\f195",
"premium": "\f196",
"previous": "\f197",
"privacy-policy": "\f198",
"quote-text": "\f199",
"quote": "\f19a",
"readchats": "\f19b",
"recent": "\f19c",
"reload": "\f19d",
"remove": "\f19e",
"reopen-topic": "\f19f",
"replace": "\f1a0",
"replies": "\f1a1",
"reply-filled": "\f1a2",
"reply": "\f1a3",
"revote": "\f1a4",
"save-story": "\f1a5",
"saved-messages": "\f1a6",
"schedule": "\f1a7",
"search": "\f1a8",
"select": "\f1a9",
"send-outline": "\f1aa",
"send": "\f1ab",
"settings-filled": "\f1ac",
"settings": "\f1ad",
"share-filled": "\f1ae",
"share-screen-outlined": "\f1af",
"share-screen-stop": "\f1b0",
"share-screen": "\f1b1",
"sidebar": "\f1b2",
"skip-next": "\f1b3",
"skip-previous": "\f1b4",
"smallscreen": "\f1b5",
"smile": "\f1b6",
"sort": "\f1b7",
"speaker-muted-story": "\f1b8",
"speaker-outline": "\f1b9",
"speaker-story": "\f1ba",
"speaker": "\f1bb",
"spoiler-disable": "\f1bc",
"spoiler": "\f1bd",
"sport": "\f1be",
"stats": "\f1bf",
"stealth-future": "\f1c0",
"stealth-past": "\f1c1",
"stickers": "\f1c2",
"stop-raising-hand": "\f1c3",
"stop": "\f1c4",
"story-caption": "\f1c5",
"story-expired": "\f1c6",
"story-priority": "\f1c7",
"story-reply": "\f1c8",
"strikethrough": "\f1c9",
"tag-add": "\f1ca",
"tag-crossed": "\f1cb",
"tag-filter": "\f1cc",
"tag-name": "\f1cd",
"tag": "\f1ce",
"timer": "\f1cf",
"transcribe": "\f1d0",
"truck": "\f1d1",
"unarchive": "\f1d2",
"underlined": "\f1d3",
"unlock-badge": "\f1d4",
"unlock": "\f1d5",
"unmute": "\f1d6",
"unpin": "\f1d7",
"unread": "\f1d8",
"up": "\f1d9",
"user-filled": "\f1da",
"user-online": "\f1db",
"user": "\f1dc",
"video-outlined": "\f1dd",
"video-stop": "\f1de",
"video": "\f1df",
"view-once": "\f1e0",
"voice-chat": "\f1e1",
"volume-1": "\f1e2",
"volume-2": "\f1e3",
"volume-3": "\f1e4",
"web": "\f1e5",
"webapp": "\f1e6",
"word-wrap": "\f1e7",
"zoom-in": "\f1e8",
"zoom-out": "\f1e9",
"nochannel": "\f182",
"noise-suppression": "\f183",
"non-contacts": "\f184",
"one-filled": "\f185",
"open-in-new-tab": "\f186",
"password-off": "\f187",
"pause": "\f188",
"permissions": "\f189",
"phone-discard-outline": "\f18a",
"phone-discard": "\f18b",
"phone": "\f18c",
"photo": "\f18d",
"pin-badge": "\f18e",
"pin-list": "\f18f",
"pin": "\f190",
"pinned-chat": "\f191",
"pinned-message": "\f192",
"pip": "\f193",
"play-story": "\f194",
"play": "\f195",
"poll": "\f196",
"premium": "\f197",
"previous": "\f198",
"privacy-policy": "\f199",
"quote-text": "\f19a",
"quote": "\f19b",
"readchats": "\f19c",
"recent": "\f19d",
"reload": "\f19e",
"remove": "\f19f",
"reopen-topic": "\f1a0",
"replace": "\f1a1",
"replies": "\f1a2",
"reply-filled": "\f1a3",
"reply": "\f1a4",
"revenue-split": "\f1a5",
"revote": "\f1a6",
"save-story": "\f1a7",
"saved-messages": "\f1a8",
"schedule": "\f1a9",
"search": "\f1aa",
"select": "\f1ab",
"send-outline": "\f1ac",
"send": "\f1ad",
"settings-filled": "\f1ae",
"settings": "\f1af",
"share-filled": "\f1b0",
"share-screen-outlined": "\f1b1",
"share-screen-stop": "\f1b2",
"share-screen": "\f1b3",
"sidebar": "\f1b4",
"skip-next": "\f1b5",
"skip-previous": "\f1b6",
"smallscreen": "\f1b7",
"smile": "\f1b8",
"sort": "\f1b9",
"speaker-muted-story": "\f1ba",
"speaker-outline": "\f1bb",
"speaker-story": "\f1bc",
"speaker": "\f1bd",
"spoiler-disable": "\f1be",
"spoiler": "\f1bf",
"sport": "\f1c0",
"stats": "\f1c1",
"stealth-future": "\f1c2",
"stealth-past": "\f1c3",
"stickers": "\f1c4",
"stop-raising-hand": "\f1c5",
"stop": "\f1c6",
"story-caption": "\f1c7",
"story-expired": "\f1c8",
"story-priority": "\f1c9",
"story-reply": "\f1ca",
"strikethrough": "\f1cb",
"tag-add": "\f1cc",
"tag-crossed": "\f1cd",
"tag-filter": "\f1ce",
"tag-name": "\f1cf",
"tag": "\f1d0",
"timer": "\f1d1",
"transcribe": "\f1d2",
"truck": "\f1d3",
"unarchive": "\f1d4",
"underlined": "\f1d5",
"unlock-badge": "\f1d6",
"unlock": "\f1d7",
"unmute": "\f1d8",
"unpin": "\f1d9",
"unread": "\f1da",
"up": "\f1db",
"user-filled": "\f1dc",
"user-online": "\f1dd",
"user": "\f1de",
"video-outlined": "\f1df",
"video-stop": "\f1e0",
"video": "\f1e1",
"view-once": "\f1e2",
"voice-chat": "\f1e3",
"volume-1": "\f1e4",
"volume-2": "\f1e5",
"volume-3": "\f1e6",
"web": "\f1e7",
"webapp": "\f1e8",
"word-wrap": "\f1e9",
"zoom-in": "\f1ea",
"zoom-out": "\f1eb",
);
.icon-active-sessions::before {
@ -651,6 +653,9 @@ $icons-map: (
.icon-next::before {
content: map.get($icons-map, "next");
}
.icon-nochannel::before {
content: map.get($icons-map, "nochannel");
}
.icon-noise-suppression::before {
content: map.get($icons-map, "noise-suppression");
}
@ -753,6 +758,9 @@ $icons-map: (
.icon-reply::before {
content: map.get($icons-map, "reply");
}
.icon-revenue-split::before {
content: map.get($icons-map, "revenue-split");
}
.icon-revote::before {
content: map.get($icons-map, "revote");
}

Binary file not shown.

Binary file not shown.

View File

@ -128,6 +128,7 @@ export type FontIconName =
| 'my-notes'
| 'new-chat-filled'
| 'next'
| 'nochannel'
| 'noise-suppression'
| 'non-contacts'
| 'one-filled'
@ -162,6 +163,7 @@ export type FontIconName =
| 'replies'
| 'reply-filled'
| 'reply'
| 'revenue-split'
| 'revote'
| 'save-story'
| 'saved-messages'