Privacy: Support chat no forwards (#6775)

This commit is contained in:
Alexander Zinchuk 2026-03-31 11:29:04 +02:00
parent cbce577dab
commit 9cd17b4921
54 changed files with 1765 additions and 987 deletions

View File

@ -110,6 +110,7 @@ export interface GramJsAppConfig extends LimitsConfig {
stars_suggested_post_age_min?: number; stars_suggested_post_age_min?: number;
stars_suggested_post_future_max?: number; stars_suggested_post_future_max?: number;
stars_suggested_post_future_min?: number; stars_suggested_post_future_min?: number;
no_forwards_request_expire_period?: number;
ton_suggested_post_commission_permille?: number; ton_suggested_post_commission_permille?: number;
ton_suggested_post_amount_max?: number; ton_suggested_post_amount_max?: number;
ton_suggested_post_amount_min?: number; ton_suggested_post_amount_min?: number;
@ -250,6 +251,7 @@ export function buildAppConfig(json: GramJs.TypeJSONValue, hash: number): ApiApp
starsSuggestedPostAgeMin: appConfig.stars_suggested_post_age_min, starsSuggestedPostAgeMin: appConfig.stars_suggested_post_age_min,
starsSuggestedPostFutureMax: appConfig.stars_suggested_post_future_max, starsSuggestedPostFutureMax: appConfig.stars_suggested_post_future_max,
starsSuggestedPostFutureMin: appConfig.stars_suggested_post_future_min, starsSuggestedPostFutureMin: appConfig.stars_suggested_post_future_min,
noForwardsRequestExpirePeriod: appConfig.no_forwards_request_expire_period,
tonSuggestedPostCommissionPermille: appConfig.ton_suggested_post_commission_permille, tonSuggestedPostCommissionPermille: appConfig.ton_suggested_post_commission_permille,
tonSuggestedPostAmountMax: appConfig.ton_suggested_post_amount_max, tonSuggestedPostAmountMax: appConfig.ton_suggested_post_amount_max,
tonSuggestedPostAmountMin: appConfig.ton_suggested_post_amount_min, tonSuggestedPostAmountMin: appConfig.ton_suggested_post_amount_min,

View File

@ -573,6 +573,25 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
newCreatorId: buildApiPeerId(newCreatorId, 'user'), newCreatorId: buildApiPeerId(newCreatorId, 'user'),
}; };
} }
if (action instanceof GramJs.MessageActionNoForwardsToggle) {
const { prevValue, newValue } = action;
return {
mediaType: 'action',
type: 'noForwardsToggle',
prevValue: Boolean(prevValue),
newValue: Boolean(newValue),
};
}
if (action instanceof GramJs.MessageActionNoForwardsRequest) {
const { expired, prevValue, newValue } = action;
return {
mediaType: 'action',
type: 'noForwardsRequest',
isExpired: expired,
prevValue: Boolean(prevValue),
newValue: Boolean(newValue),
};
}
return UNSUPPORTED_ACTION; return UNSUPPORTED_ACTION;
} }

View File

@ -37,6 +37,7 @@ export function buildApiUserFullInfo(mtpUserFull: GramJs.users.UserFull): ApiUse
birthday, personalChannelId, personalChannelMessage, sponsoredEnabled, stargiftsCount, botVerification, birthday, personalChannelId, personalChannelMessage, sponsoredEnabled, stargiftsCount, botVerification,
botCanManageEmojiStatus, settings, sendPaidMessagesStars, displayGiftsButton, disallowedGifts, botCanManageEmojiStatus, settings, sendPaidMessagesStars, displayGiftsButton, disallowedGifts,
starsRating, starsMyPendingRating, starsMyPendingRatingDate, mainTab, note, starsRating, starsMyPendingRating, starsMyPendingRatingDate, mainTab, note,
noforwardsMyEnabled, noforwardsPeerEnabled,
}, },
users, users,
} = mtpUserFull; } = mtpUserFull;
@ -77,6 +78,8 @@ export function buildApiUserFullInfo(mtpUserFull: GramJs.users.UserFull): ApiUse
settings: buildApiPeerSettings(settings), settings: buildApiPeerSettings(settings),
mainTab: mainTab && buildApiProfileTab(mainTab), mainTab: mainTab && buildApiProfileTab(mainTab),
note: note && buildApiFormattedText(note), note: note && buildApiFormattedText(note),
noForwardsMyEnabled: noforwardsMyEnabled,
noForwardsPeerEnabled: noforwardsPeerEnabled,
}; };
} }

View File

@ -380,3 +380,21 @@ export function updateContactNote(user: ApiUser, note: ApiFormattedText) {
shouldReturnTrue: true, shouldReturnTrue: true,
}); });
} }
export function toggleNoForwards({
user,
isEnabled,
requestMsgId,
}: {
user: ApiUser;
isEnabled: boolean;
requestMsgId?: number;
}) {
return invokeRequest(new GramJs.messages.ToggleNoForwards({
peer: buildInputPeer(user.id, user.accessHash),
enabled: isEnabled,
requestMsgId,
}), {
shouldReturnTrue: true,
});
}

View File

@ -357,6 +357,19 @@ export interface ApiMessageActionChangeCreator extends ActionMediaType {
newCreatorId: string; newCreatorId: string;
} }
export interface ApiMessageActionNoForwardsToggle extends ActionMediaType {
type: 'noForwardsToggle';
prevValue: boolean;
newValue: boolean;
}
export interface ApiMessageActionNoForwardsRequest extends ActionMediaType {
type: 'noForwardsRequest';
isExpired?: boolean;
prevValue: boolean;
newValue: boolean;
}
export interface ApiMessageActionUnsupported extends ActionMediaType { export interface ApiMessageActionUnsupported extends ActionMediaType {
type: 'unsupported'; type: 'unsupported';
} }
@ -378,4 +391,4 @@ export type ApiMessageAction = ApiMessageActionUnsupported | ApiMessageActionCha
| ApiMessageActionSuggestedPostSuccess | ApiMessageActionSuggestedPostRefund | ApiMessageActionTodoCompletions | ApiMessageActionSuggestedPostSuccess | ApiMessageActionSuggestedPostRefund | ApiMessageActionTodoCompletions
| ApiMessageActionTodoAppendTasks | ApiMessageActionStarGiftPurchaseOffer | ApiMessageActionTodoAppendTasks | ApiMessageActionStarGiftPurchaseOffer
| ApiMessageActionStarGiftPurchaseOfferDeclined | ApiMessageActionNewCreatorPending | ApiMessageActionStarGiftPurchaseOfferDeclined | ApiMessageActionNewCreatorPending
| ApiMessageActionChangeCreator; | ApiMessageActionChangeCreator | ApiMessageActionNoForwardsToggle | ApiMessageActionNoForwardsRequest;

View File

@ -968,6 +968,12 @@ export interface KeyboardButtonGiftOffer extends ApiKeyboardButtonBase {
buttonType: 'accept' | 'reject'; buttonType: 'accept' | 'reject';
} }
export interface KeyboardButtonNoForwardsRequest extends ApiKeyboardButtonBase {
type: 'noForwardsRequest';
text: string;
buttonType: 'accept' | 'reject';
}
export type ApiKeyboardButton = ( export type ApiKeyboardButton = (
ApiKeyboardButtonSimple ApiKeyboardButtonSimple
| ApiKeyboardButtonReceipt | ApiKeyboardButtonReceipt
@ -983,6 +989,7 @@ export type ApiKeyboardButton = (
| KeyboardButtonSuggestedMessage | KeyboardButtonSuggestedMessage
| KeyboardButtonOpenThread | KeyboardButtonOpenThread
| KeyboardButtonGiftOffer | KeyboardButtonGiftOffer
| KeyboardButtonNoForwardsRequest
); );
export type ApiKeyboardButtons = ApiKeyboardButton[][]; export type ApiKeyboardButtons = ApiKeyboardButton[][];

View File

@ -274,6 +274,7 @@ export interface ApiAppConfig {
starsSuggestedPostAgeMin: number; starsSuggestedPostAgeMin: number;
starsSuggestedPostFutureMax: number; starsSuggestedPostFutureMax: number;
starsSuggestedPostFutureMin: number; starsSuggestedPostFutureMin: number;
noForwardsRequestExpirePeriod: number;
tonSuggestedPostCommissionPermille: number; tonSuggestedPostCommissionPermille: number;
tonSuggestedPostAmountMax: number; tonSuggestedPostAmountMax: number;
tonSuggestedPostAmountMin: number; tonSuggestedPostAmountMin: number;

View File

@ -85,6 +85,8 @@ export interface ApiUserFullInfo {
settings?: ApiPeerSettings; settings?: ApiPeerSettings;
mainTab?: ApiProfileTab; mainTab?: ApiProfileTab;
note?: ApiFormattedText; note?: ApiFormattedText;
noForwardsMyEnabled?: boolean;
noForwardsPeerEnabled?: boolean;
} }
export type ApiUserType = 'userTypeBot' | 'userTypeRegular' | 'userTypeDeleted' | 'userTypeUnknown'; export type ApiUserType = 'userTypeBot' | 'userTypeRegular' | 'userTypeDeleted' | 'userTypeUnknown';

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M14.208 22.56c-.291 0-.585-.06-.867-.185a2.1 2.1 0 0 1-1.268-1.945v-2.494c-.763.057-2.06.256-4.032.864-2.019.622-3.629 2.002-4.625 3.05a1.89 1.89 0 0 1-2.18.404 1.89 1.89 0 0 1-1.07-1.947c.36-2.833 1.353-6.764 4.045-9.409 2.795-2.748 6.058-3.36 7.862-3.468V4.838c0-.855.487-1.599 1.268-1.944a2.1 2.1 0 0 1 2.29.374l8.694 7.931a1.95 1.95 0 0 1 0 2.871L15.632 22c-.404.369-.908.56-1.424.56m-1.65-6.944q.162 0 .3.007a1.58 1.58 0 0 1 1.516 1.582v2.83l8.111-7.4-8.111-7.4v2.89a1.58 1.58 0 0 1-1.578 1.585l-.045-.001c-1.002 0-4.272.218-6.928 2.83-1.931 1.897-2.799 4.776-3.187 6.861 1.174-1.062 2.78-2.198 4.727-2.798 2.583-.796 4.22-.986 5.196-.986m.198 2.304h.003zM20.365 29.29c-.316 0-.633-.121-.874-.363l-3.289-3.289a1.15 1.15 0 0 1 1.627-1.627l2.57 2.57 9.524-8.689a1.151 1.151 0 0 1 1.55 1.7l-10.276 9.375a1.23 1.23 0 0 1-.832.323" /></svg>

After

Width:  |  Height:  |  Size: 909 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M6.921 7.047A2.05 2.05 0 0 0 4.868 9.03l-.001 10.644c0 5.96 4.85 10.81 10.81 10.81h.925a9.7 9.7 0 0 0 8.846-5.747l3.92-8.814a1.893 1.893 0 0 0-.883-2.475 1.819 1.819 0 0 0-2.323.616l-.043.062-2.841 5.66c-.05.098-.113.138-.21.138a.32.32 0 0 1-.202-.071c-.045-.042-.068-.095-.068-.185V5.43a2 2 0 0 0-.524-1.459 2.18 2.18 0 0 0-1.526-.694 2.053 2.053 0 0 0-2.051 1.95c-.004.262-.002 8.435-.002 9.68v.157c0 .135-.116.25-.251.25a.25.25 0 0 1-.25-.251V3.539c-.036-1.115-.907-1.986-1.983-2.024h-.003c-1.181 0-2.082.87-2.12 1.981l-.002 11.567c0 .136-.115.25-.252.25a.254.254 0 0 1-.25-.25V5.381c-.038-1.117-.909-1.987-1.984-2.024h-.002c-1.184 0-2.083.872-2.12 1.984l-.001 10.643c0 .136-.115.25-.251.25a.248.248 0 0 1-.25-.25V9.074c-.038-1.117-.91-1.989-1.985-2.025z" /></svg>

After

Width:  |  Height:  |  Size: 837 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="m28.893 27.266-2.813-2.812c1.971-2.344 3.107-5.313 3.107-8.454C29.187 8.729 23.27 2.814 16 2.814c-3.141 0-6.111 1.135-8.454 3.105L4.734 3.108a1.15 1.15 0 0 0-1.627 1.626l2.801 2.8-.012.013 1.637 1.638.012-.013 15.283 15.283-.013.012 1.637 1.636.013-.012 2.8 2.801a1.15 1.15 0 0 0 1.628 0 1.15 1.15 0 0 0 0-1.626M16 5.114c6.003 0 10.886 4.884 10.886 10.886 0 2.524-.884 4.917-2.433 6.827l-3.512-3.512.86-.86a1.15 1.15 0 1 0-1.626-1.626l-.86.86-2.165-2.165v-6.28a1.15 1.15 0 0 0-2.3 0v3.98L9.173 7.545A10.82 10.82 0 0 1 16 5.114M20.117 26.076c-2.311.948-4.927 1.09-7.497.287-3.48-1.087-6.097-3.803-7.081-7.312-.692-2.465-.521-4.956.385-7.168.182-.443.105-.95-.234-1.289-.572-.572-1.56-.4-1.87.346a13.27 13.27 0 0 0-.816 7.33C3.922 23.717 8.31 28.093 13.758 29c2.524.42 5.036.123 7.301-.82.747-.31.918-1.298.347-1.87-.339-.339-.845-.416-1.289-.234" class="cls-1"/><path d="M14.85 19.465v.337l-3.026-2.974a1.151 1.151 0 0 0-1.627 1.627l4.75 4.749c.28.281.653.437 1.05.438H16c.396 0 .769-.155 1.05-.435l.626-.627-.001-.001.14-.15z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="m24.367 21.046 3.402-3.104c.403-.368.634-.891.634-1.436 0-.546-.231-1.07-.634-1.438l-8.693-7.93a2.1 2.1 0 0 0-2.29-.373 2.1 2.1 0 0 0-1.267 1.944v2.593c-.42.025-.935.096-1.485.196L7.949 5.876a1.15 1.15 0 0 0-1.56 1.69l20.084 18.557a1.147 1.147 0 0 0 1.625-.064 1.15 1.15 0 0 0-.065-1.625zm-7.013-7.926c.3-.299.466-.698.466-1.123V9.106l8.112 7.399-3.261 2.974-6.39-5.905c.427-.034.785-.167 1.073-.454M19.287 22.566l-1.467 1.34v-2.83c0-.848-.666-1.544-1.518-1.584-.93-.033-2.633.099-5.494.982-1.946.6-3.552 1.735-4.727 2.798.388-2.084 1.255-4.963 3.186-6.863.436-.427.91-.813 1.41-1.146a1.151 1.151 0 0 0-1.278-1.914c-.619.413-1.205.891-1.744 1.42-2.692 2.647-3.683 6.577-4.042 9.408-.103.812.326 1.595 1.069 1.947s1.617.19 2.18-.402c.995-1.048 2.606-2.429 4.624-3.051 1.972-.607 3.27-.807 4.033-.864v2.495a2.1 2.1 0 0 0 1.267 1.944c.282.124.576.186.867.186a2.1 2.1 0 0 0 1.424-.56l1.76-1.606a1.151 1.151 0 0 0-1.55-1.7" /></svg>

After

Width:  |  Height:  |  Size: 998 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M7.38 26.33H5.06c-1.66 0-3.01-1.35-3.01-3.01V21c0-1.66 1.35-3.01 3.01-3.01h2.32c1.66 0 3.01 1.35 3.01 3.01v2.32c0 1.66-1.35 3.01-3.01 3.01M5.06 20c-.55 0-1 .45-1 1v2.33c0 .55.45 1 1 1h2.33c.55 0 1-.45 1-1V21c0-.55-.45-1-1-1zM17.16 26.33h-2.32c-1.66 0-3.01-1.35-3.01-3.01V21c0-1.66 1.35-3.01 3.01-3.01h2.32c1.66 0 3.01 1.35 3.01 3.01v2.32c0 1.66-1.35 3.01-3.01 3.01M14.83 20c-.55 0-1 .45-1 1v2.33c0 .55.45 1 1 1h2.33c.55 0 1-.45 1-1V21c0-.55-.45-1-1-1zM26.94 26.33h-2.32c-1.66 0-3.01-1.35-3.01-3.01V21c0-1.66 1.35-3.01 3.01-3.01h2.32c1.66 0 3.01 1.35 3.01 3.01v2.32c0 1.66-1.35 3.01-3.01 3.01M24.61 20c-.55 0-1 .45-1 1v2.33c0 .55.45 1 1 1h2.33c.55 0 1-.45 1-1V21c0-.55-.45-1-1-1zM25.78 15.26c-.55 0-1-.45-1-1V8.34c0-.32-.41-.67-1.01-.67H8.23c-.59 0-1.01.35-1.01.67v5.92c0 .55-.45 1-1 1s-1-.45-1-1V8.34c0-1.47 1.35-2.67 3.01-2.67h15.54c1.66 0 3.01 1.2 3.01 2.67v5.92c0 .55-.45 1-1 1" class="cls-1"/><path d="M6.22 15.99c-.27 0-.52-.11-.71-.29l-3.14-3.15a.996.996 0 1 1 1.41-1.41l2.44 2.44 2.44-2.44a.996.996 0 1 1 1.41 1.41L6.93 15.7a1 1 0 0 1-.71.29"/></svg> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M7.38 26.33H5.06c-1.66 0-3.01-1.35-3.01-3.01V21c0-1.66 1.35-3.01 3.01-3.01h2.32c1.66 0 3.01 1.35 3.01 3.01v2.32c0 1.66-1.35 3.01-3.01 3.01M5.06 20c-.55 0-1 .45-1 1v2.33c0 .55.45 1 1 1h2.33c.55 0 1-.45 1-1V21c0-.55-.45-1-1-1zM17.16 26.33h-2.32c-1.66 0-3.01-1.35-3.01-3.01V21c0-1.66 1.35-3.01 3.01-3.01h2.32c1.66 0 3.01 1.35 3.01 3.01v2.32c0 1.66-1.35 3.01-3.01 3.01M14.83 20c-.55 0-1 .45-1 1v2.33c0 .55.45 1 1 1h2.33c.55 0 1-.45 1-1V21c0-.55-.45-1-1-1zM26.94 26.33h-2.32c-1.66 0-3.01-1.35-3.01-3.01V21c0-1.66 1.35-3.01 3.01-3.01h2.32c1.66 0 3.01 1.35 3.01 3.01v2.32c0 1.66-1.35 3.01-3.01 3.01M24.61 20c-.55 0-1 .45-1 1v2.33c0 .55.45 1 1 1h2.33c.55 0 1-.45 1-1V21c0-.55-.45-1-1-1zM25.78 15.26c-.55 0-1-.45-1-1V8.34c0-.32-.41-.67-1.01-.67H8.23c-.59 0-1.01.35-1.01.67v5.92c0 .55-.45 1-1 1s-1-.45-1-1V8.34c0-1.47 1.35-2.67 3.01-2.67h15.54c1.66 0 3.01 1.2 3.01 2.67v5.92c0 .55-.45 1-1 1" /><path d="M6.22 15.99c-.27 0-.52-.11-.71-.29l-3.14-3.15a.996.996 0 1 1 1.41-1.41l2.44 2.44 2.44-2.44a.996.996 0 1 1 1.41 1.41L6.93 15.7a1 1 0 0 1-.71.29"/></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M16 1.333C7.9 1.333 1.333 7.9 1.333 16S7.9 30.667 16 30.667 30.667 24.1 30.667 16 24.1 1.333 16 1.333m7.687 11.282-8.858 8.857h-.001a1.476 1.476 0 0 1-2.088 0l-3.69-3.69a1.477 1.477 0 0 1 2.088-2.089l2.648 2.648 7.813-7.814a1.477 1.477 0 0 1 2.088 2.088" /></svg>

After

Width:  |  Height:  |  Size: 333 B

View File

@ -72,6 +72,8 @@
"PremiumPreviewUploadsDescription" = "4 GB per each document, unlimited storage for your chats and media overall."; "PremiumPreviewUploadsDescription" = "4 GB per each document, unlimited storage for your chats and media overall.";
"PremiumPreviewAdvancedChatManagementDescription" = "Tools to set the default folder, auto-archive and hide new chats from non-contacts."; "PremiumPreviewAdvancedChatManagementDescription" = "Tools to set the default folder, auto-archive and hide new chats from non-contacts.";
"PremiumPreviewAnimatedProfilesDescription" = "Video avatars animated in chat lists and chats to allow for additional self-expression."; "PremiumPreviewAnimatedProfilesDescription" = "Video avatars animated in chat lists and chats to allow for additional self-expression.";
"PremiumPreviewNoForwards" = "Disable Sharing";
"PremiumPreviewNoForwardsDescription" = "Prevent forwarding, saving and copying content in private chats.";
"PremiumLimitAccountsTitle" = "Limit Reached"; "PremiumLimitAccountsTitle" = "Limit Reached";
"PremiumLimitAccountsNoPremium" = "You have reached your current limit of connected accounts. You can free one more place by subscribing to Telegram Premium."; "PremiumLimitAccountsNoPremium" = "You have reached your current limit of connected accounts. You can free one more place by subscribing to Telegram Premium.";
"PremiumLimitAccounts" = "You have reached your current limit of connected accounts. You can free one more place by subscribing to Telegram Premium with one of these connected accounts."; "PremiumLimitAccounts" = "You have reached your current limit of connected accounts. You can free one more place by subscribing to Telegram Premium with one of these connected accounts.";
@ -1931,6 +1933,26 @@
"ActionPaymentUsedRecurring" = "You were charged {amount} via recurring payment"; "ActionPaymentUsedRecurring" = "You were charged {amount} via recurring payment";
"ActionScreenshotTaken" = "{from} took a screenshot!"; "ActionScreenshotTaken" = "{from} took a screenshot!";
"ActionScreenshotTakenYou" = "You took a screenshot!"; "ActionScreenshotTakenYou" = "You took a screenshot!";
"ActionSharingDisabled" = "{from} disabled sharing in this chat";
"ActionSharingDisabledYou" = "You disabled sharing in this chat";
"ActionSharingEnabled" = "{from} enabled sharing in this chat";
"ActionSharingEnabledYou" = "You enabled sharing in this chat";
"ActionSharingStillDisabled" = "Sharing in this chat is still disabled";
"ContextMenuNoForwardsPeer" = "{name} disabled copying and forwarding in this chat.";
"ContextMenuNoForwardsYou" = "You disabled copying and forwarding in this chat.";
"DisableSharing" = "Disable Sharing";
"EnableSharing" = "Enable Sharing";
"NotificationSharingEnabled" = "Sharing enabled for this chat.";
"NotificationSharingDisabled" = "Sharing disabled for this chat.";
"NoForwardingTitle" = "No Forwarding";
"NoForwardingDescription" = "Disable message forwarding to other chats.";
"NoSavingTitle" = "No Saving";
"NoSavingDescription" = "Disable copying text and saving photos and videos.";
"NoForwardsRequestTitle" = "{user} would like to enable sharing in this chat, which includes:";
"NoForwardsRequestYouTitle" = "You suggested to enable sharing in this chat, which includes:";
"NoForwardsRequestForwarding" = "forwarding messages";
"NoForwardsRequestSaving" = "saving photos and videos";
"NoForwardsRequestCopying" = "copying messages";
"ActionBotAllowedFromDomain" = "You allowed this bot to message you when you logged in on {domain}."; "ActionBotAllowedFromDomain" = "You allowed this bot to message you when you logged in on {domain}.";
"ActionBotAllowedFromApp" = "You allowed this bot to message you when you opened {app}."; "ActionBotAllowedFromApp" = "You allowed this bot to message you when you opened {app}.";
"ActionBotAppPlaceholder" = "App"; "ActionBotAppPlaceholder" = "App";
@ -2750,3 +2772,5 @@
"RankEditTextOwn" = "Share your role, title or how you're known in this group. Your tag is visible to all members."; "RankEditTextOwn" = "Share your role, title or how you're known in this group. Your tag is visible to all members.";
"RankEditText" = "Add a short tag next to {user}'s name."; "RankEditText" = "Add a short tag next to {user}'s name.";
"MenuAddCaption" = "Add Caption"; "MenuAddCaption" = "Add Caption";
"NoForwardsRequestReject" = "Reject";
"NoForwardsRequestAccept" = "Accept";

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><defs><style>.cls-1{fill:#fff}</style></defs><path d="M15.821 15.367c5.48 0 9.924-1.222 9.924-2.73 0-.94-1.731-1.77-4.364-2.26-.033-.535-.1-1.045-.118-1.39a8.7 8.7 0 0 0-.929-3.508c-.239-.471-.535-.935-.976-1.227s-1.055-.372-1.492-.075c-.59.401-.639 1.283-1.155 1.776-.238.228-.542.34-.862.428-.32-.088-.623-.2-.861-.428-.516-.493-.565-1.375-1.155-1.776-.437-.297-1.052-.216-1.493.075s-.736.756-.976 1.227a8.7 8.7 0 0 0-.929 3.508c-.017.342-.083.849-.117 1.38-2.664.49-4.42 1.323-4.42 2.27 0 1.508 4.443 2.73 9.923 2.73m12.225 2.487c-.956-1.02-1.874-2.075-2.81-3.114-.596.336-1.327.611-2.14.834l.003.13-.061 3.537c-3.775 1.212-6.969 4.125-8.522 7.773-.092.215-.179.438-.181.671s.098.482.302.594c.355.195.773-.091 1.082-.353a21.5 21.5 0 0 1 8.971-4.498c.224-.052.642-.19.306-.716-.198-.31-.692-.91-.878-1.228-.168-.288-.36-.404-.102-.614.332-.27.731-.482 1.034-.622.976-.455 1.717-.844 2.68-1.328.95-.477.717-.638.316-1.066" class="cls-1"/><path d="M17.533 18.326a2.7 2.7 0 0 0 2.346.451 2.5 2.5 0 0 0 1.15-.693c.426-.455.65-1.077.671-1.7.005-.153.01-.32.012-.489-1.742.335-3.645.486-5.242.517.118.754.496 1.478 1.063 1.914m-7.171-.785c.25.435.62.802 1.067 1.031 1.373.704 3.378.012 3.565-1.65a6 6 0 0 1 .096-.511c-1.552-.034-3.385-.182-5.073-.5a2.6 2.6 0 0 0-.005.547c.036.38.159.751.35 1.083m-.097 3.188c-1.116-1.52-1.866-3.346-1.96-5.224-.765-.224-1.448-.499-2.005-.83l-.296.328-2.035 2.25c-.202.223-.418.499-.352.792.054.238.275.393.48.525l3.577 2.31-1.116 1.411c2.568 1.51 4.556 2.599 7.268 3.741q.329-.684.725-1.33c-1.745-1.094-3.061-2.307-4.286-3.973" class="cls-1"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

View File

@ -105,6 +105,7 @@ export { default as OneTimeMediaModal } from '../components/modals/oneTimeMedia/
export { default as WebAppsCloseConfirmationModal } from '../components/main/WebAppsCloseConfirmationModal'; export { default as WebAppsCloseConfirmationModal } from '../components/main/WebAppsCloseConfirmationModal';
export { default as FrozenAccountModal } from '../components/modals/frozenAccount/FrozenAccountModal'; export { default as FrozenAccountModal } from '../components/modals/frozenAccount/FrozenAccountModal';
export { default as ProfileRatingModal } from '../components/modals/profileRating/ProfileRatingModal'; export { default as ProfileRatingModal } from '../components/modals/profileRating/ProfileRatingModal';
export { default as DisableSharingAboutModal } from '../components/modals/disableSharing/DisableSharingAboutModal';
export { default as EditRankModal } from '../components/modals/rank/EditRankModal'; export { default as EditRankModal } from '../components/modals/rank/EditRankModal';
export { default as RankModal } from '../components/modals/rank/RankModal'; export { default as RankModal } from '../components/modals/rank/RankModal';
export { default as QuickPreviewModal } from '../components/modals/quickPreview/QuickPreviewModal'; export { default as QuickPreviewModal } from '../components/modals/quickPreview/QuickPreviewModal';

View File

@ -35,6 +35,7 @@ import Experimental from '../../../assets/tgs/settings/Experimental.tgs';
import FoldersAll from '../../../assets/tgs/settings/FoldersAll.tgs'; import FoldersAll from '../../../assets/tgs/settings/FoldersAll.tgs';
import FoldersNew from '../../../assets/tgs/settings/FoldersNew.tgs'; import FoldersNew from '../../../assets/tgs/settings/FoldersNew.tgs';
import FoldersShare from '../../../assets/tgs/settings/FoldersShare.tgs'; import FoldersShare from '../../../assets/tgs/settings/FoldersShare.tgs';
import HandStop from '../../../assets/tgs/settings/HandStop.tgs';
import Lock from '../../../assets/tgs/settings/Lock.tgs'; import Lock from '../../../assets/tgs/settings/Lock.tgs';
import Passkeys from '../../../assets/tgs/settings/Passkeys.tgs'; import Passkeys from '../../../assets/tgs/settings/Passkeys.tgs';
import StarReaction from '../../../assets/tgs/stars/StarReaction.tgs'; import StarReaction from '../../../assets/tgs/stars/StarReaction.tgs';
@ -42,9 +43,11 @@ import StarReactionEffect from '../../../assets/tgs/stars/StarReactionEffect.tgs
import Unlock from '../../../assets/tgs/Unlock.tgs'; import Unlock from '../../../assets/tgs/Unlock.tgs';
import DuckNothingFoundPreview from '../../../assets/tgs-previews/DuckNothingFound.svg'; import DuckNothingFoundPreview from '../../../assets/tgs-previews/DuckNothingFound.svg';
import SearchPreview from '../../../assets/tgs-previews/Search.svg'; import SearchPreview from '../../../assets/tgs-previews/Search.svg';
import HandStopPreview from '../../../assets/tgs-previews/settings/HandStopPreview.png';
import PasskeysPreview from '../../../assets/tgs-previews/settings/Passkeys.svg'; import PasskeysPreview from '../../../assets/tgs-previews/settings/Passkeys.svg';
export const LOCAL_TGS_PREVIEW_URLS = { export const LOCAL_TGS_PREVIEW_URLS = {
HandStop: HandStopPreview,
BrokenGift: BrokenGiftPreview, BrokenGift: BrokenGiftPreview,
DuckNothingFound: DuckNothingFoundPreview, DuckNothingFound: DuckNothingFoundPreview,
Search: SearchPreview, Search: SearchPreview,
@ -93,4 +96,5 @@ export const LOCAL_TGS_URLS = {
DuckNothingFound, DuckNothingFound,
Passkeys, Passkeys,
DuckCake, DuckCake,
HandStop,
}; };

View File

@ -53,7 +53,7 @@
margin-bottom: 7.5rem; margin-bottom: 7.5rem;
} }
.limits, .stories { .limits, .stories, .noForward {
background: var(--color-background); background: var(--color-background);
} }
@ -68,6 +68,13 @@
height: calc(var(--vh) * 55 + 100px); height: calc(var(--vh) * 55 + 100px);
} }
.noForwardFooter {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
}
.header { .header {
position: absolute; position: absolute;
@ -112,7 +119,7 @@
} }
.description { .description {
padding: 0 5%; padding: 0 max(5%, 2rem);
font-size: 1rem; font-size: 1rem;
font-weight: var(--font-weight-normal); font-weight: var(--font-weight-normal);

View File

@ -30,6 +30,7 @@ import usePreviousDeprecated from '../../../hooks/usePreviousDeprecated';
import SliderDots from '../../common/SliderDots'; import SliderDots from '../../common/SliderDots';
import Button from '../../ui/Button'; import Button from '../../ui/Button';
import PremiumLimitPreview from './common/PremiumLimitPreview'; import PremiumLimitPreview from './common/PremiumLimitPreview';
import PremiumFeaturePreviewNoForwards from './previews/PremiumFeaturePreviewNoForwards';
import PremiumFeaturePreviewStickers from './previews/PremiumFeaturePreviewStickers'; import PremiumFeaturePreviewStickers from './previews/PremiumFeaturePreviewStickers';
import PremiumFeaturePreviewStories from './previews/PremiumFeaturePreviewStories'; import PremiumFeaturePreviewStories from './previews/PremiumFeaturePreviewStories';
import PremiumFeaturePreviewVideo from './previews/PremiumFeaturePreviewVideo'; import PremiumFeaturePreviewVideo from './previews/PremiumFeaturePreviewVideo';
@ -56,6 +57,7 @@ export const PREMIUM_FEATURE_TITLES: Record<ApiPremiumSection, string> = {
message_privacy: 'PremiumPreviewMessagePrivacy', message_privacy: 'PremiumPreviewMessagePrivacy',
effects: 'Premium.MessageEffects', effects: 'Premium.MessageEffects',
todo: 'PremiumPreviewTodo', todo: 'PremiumPreviewTodo',
pm_noforwards: 'PremiumPreviewNoForwards',
}; };
export const PREMIUM_FEATURE_DESCRIPTIONS: Record<ApiPremiumSection, string> = { export const PREMIUM_FEATURE_DESCRIPTIONS: Record<ApiPremiumSection, string> = {
@ -78,6 +80,7 @@ export const PREMIUM_FEATURE_DESCRIPTIONS: Record<ApiPremiumSection, string> = {
message_privacy: 'PremiumPreviewMessagePrivacyDescription', message_privacy: 'PremiumPreviewMessagePrivacyDescription',
effects: 'Premium.MessageEffectsInfo', effects: 'Premium.MessageEffectsInfo',
todo: 'PremiumPreviewTodoDescription', todo: 'PremiumPreviewTodoDescription',
pm_noforwards: 'PremiumPreviewNoForwardsDescription',
}; };
const LIMITS_TITLES: Record<ApiLimitTypeForPromo, string> = { const LIMITS_TITLES: Record<ApiLimitTypeForPromo, string> = {
@ -221,7 +224,8 @@ const PremiumFeatureModal: FC<OwnProps> = ({
}); });
const currentSection = filteredSections[currentSlideIndex]; const currentSection = filteredSections[currentSlideIndex];
const hasHeaderBackdrop = currentSection !== 'double_limits' && currentSection !== 'stories'; const hasHeaderBackdrop = currentSection !== 'double_limits' &&
currentSection !== 'stories' && currentSection !== 'pm_noforwards';
return ( return (
<div className={styles.root}> <div className={styles.root}>
@ -289,16 +293,31 @@ const PremiumFeatureModal: FC<OwnProps> = ({
); );
} }
if (section === 'pm_noforwards') {
return (
<div className={buildClassName(styles.slide, styles.noForward)}>
<PremiumFeaturePreviewNoForwards />
<div className={styles.noForwardFooter}>
<h1 className={styles.title}>
{lang(PREMIUM_FEATURE_TITLES.pm_noforwards as keyof LangPair)}
</h1>
<div className={styles.description}>
{lang(PREMIUM_FEATURE_DESCRIPTIONS.pm_noforwards as keyof LangPair)}
</div>
</div>
</div>
);
}
const i = promo.videoSections.indexOf(section); const i = promo.videoSections.indexOf(section);
if (i === -1) return undefined;
const shouldUseNewLang = section === 'todo'; const shouldUseNewLang = section === 'todo';
return ( return (
<div className={styles.slide}> <div className={styles.slide}>
<div className={styles.frame}> <div className={styles.frame}>
<PremiumFeaturePreviewVideo <PremiumFeaturePreviewVideo
isActive={currentSlideIndex === index} isActive={currentSlideIndex === index}
videoId={promo.videos[i].id} videoId={i !== -1 ? promo.videos[i].id : undefined}
videoThumbnail={promo.videos[i].thumbnail} videoThumbnail={i !== -1 ? promo.videos[i].thumbnail : undefined}
isDown={PREMIUM_BOTTOM_VIDEOS.includes(section)} isDown={PREMIUM_BOTTOM_VIDEOS.includes(section)}
index={index} index={index}
isReverseAnimation={index === reverseAnimationSlideIndex} isReverseAnimation={index === reverseAnimationSlideIndex}
@ -336,7 +355,7 @@ const PremiumFeatureModal: FC<OwnProps> = ({
)} )}
> >
<SliderDots <SliderDots
length={PREMIUM_FEATURE_SECTIONS.length} length={filteredSections.length}
active={currentSlideIndex} active={currentSlideIndex}
onSelectSlide={handleSelectSlide} onSelectSlide={handleSelectSlide}
/> />

View File

@ -57,6 +57,7 @@ import PremiumFile from '../../../assets/premium/PremiumFile.svg';
import PremiumLastSeen from '../../../assets/premium/PremiumLastSeen.svg'; import PremiumLastSeen from '../../../assets/premium/PremiumLastSeen.svg';
import PremiumLimits from '../../../assets/premium/PremiumLimits.svg'; import PremiumLimits from '../../../assets/premium/PremiumLimits.svg';
import PremiumMessagePrivacy from '../../../assets/premium/PremiumMessagePrivacy.svg'; import PremiumMessagePrivacy from '../../../assets/premium/PremiumMessagePrivacy.svg';
import PremiumNoforwards from '../../../assets/premium/PremiumNoForwardsPrivacy.svg';
import PremiumReactions from '../../../assets/premium/PremiumReactions.svg'; import PremiumReactions from '../../../assets/premium/PremiumReactions.svg';
import PremiumSpeed from '../../../assets/premium/PremiumSpeed.svg'; import PremiumSpeed from '../../../assets/premium/PremiumSpeed.svg';
import PremiumStatus from '../../../assets/premium/PremiumStatus.svg'; import PremiumStatus from '../../../assets/premium/PremiumStatus.svg';
@ -89,6 +90,7 @@ const PREMIUM_FEATURE_COLOR_ICONS: Record<ApiPremiumSection, string> = {
message_privacy: PremiumMessagePrivacy, message_privacy: PremiumMessagePrivacy,
effects: PremiumEffects, effects: PremiumEffects,
todo: PremiumBadge, todo: PremiumBadge,
pm_noforwards: PremiumNoforwards,
}; };
export type OwnProps = { export type OwnProps = {
@ -442,7 +444,7 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
</div> </div>
<div className={buildClassName(styles.list, isPremium && styles.noButton)}> <div className={buildClassName(styles.list, isPremium && styles.noButton)}>
{filteredSections.map((section, index) => { {filteredSections.map((section, index) => {
const shouldUseNewLang = section === 'todo'; const shouldUseNewLang = section === 'todo' || section === 'pm_noforwards';
return ( return (
<PremiumFeatureItem <PremiumFeatureItem
key={section} key={section}

View File

@ -0,0 +1,45 @@
.root {
display: flex;
flex-direction: column;
gap: 4rem;
padding-block: 3.5rem;
}
.header {
display: flex;
flex-direction: column;
gap: 0.75rem;
align-items: center;
}
.listItems {
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-inline: 1rem;
}
.listItem {
:global(.ListItem-button) {
align-items: flex-start;
padding-inline: 1.5rem;
}
:global(.ListItem-main-icon) {
margin-top: 0.875rem;
margin-inline-end: 0.875rem;
font-size: 1.5rem;
color: var(--accent-color);
}
:global(.multiline-item .subtitle) {
margin-top: 0.125rem;
font-size: 1rem;
line-height: 1.375rem;
}
}
.listItemTitle {
font-size: 1rem;
font-weight: var(--font-weight-medium);
}

View File

@ -0,0 +1,54 @@
import { memo, useMemo } from '../../../../lib/teact/teact';
import type { TableAboutData } from '../../../modals/common/TableAboutModal';
import buildClassName from '../../../../util/buildClassName';
import { LOCAL_TGS_PREVIEW_URLS, LOCAL_TGS_URLS } from '../../../common/helpers/animatedAssets';
import useLang from '../../../../hooks/useLang';
import AnimatedIconWithPreview from '../../../common/AnimatedIconWithPreview';
import ListItem from '../../../ui/ListItem';
import styles from './PremiumFeaturePreviewNoForwards.module.scss';
const ICON_SIZE = 100;
const PremiumFeaturePreviewNoForwards = () => {
const lang = useLang();
const listItemData = useMemo(() => {
return [
['no-share', lang('NoForwardingTitle'), lang('NoForwardingDescription')],
['no-download', lang('NoSavingTitle'), lang('NoSavingDescription')],
] satisfies TableAboutData;
}, [lang]);
return (
<div className={styles.root}>
<div className={styles.header}>
<AnimatedIconWithPreview
size={ICON_SIZE}
tgsUrl={LOCAL_TGS_URLS.HandStop}
previewUrl={LOCAL_TGS_PREVIEW_URLS.HandStop}
noLoop
/>
</div>
<div className={styles.listItems}>
{listItemData.map(([icon, title, subtitle]) => (
<ListItem
isStatic
multiline
icon={icon}
className={styles.listItem}
>
<span className={buildClassName('title', styles.listItemTitle)}>{title}</span>
<span className="subtitle">{subtitle}</span>
</ListItem>
))}
</div>
</div>
);
};
export default memo(PremiumFeaturePreviewNoForwards);

View File

@ -133,6 +133,8 @@ type StateProps = {
savedDialog?: ApiChat; savedDialog?: ApiChat;
disallowedGifts?: ApiDisallowedGifts; disallowedGifts?: ApiDisallowedGifts;
isAccountFrozen?: boolean; isAccountFrozen?: boolean;
noForwardsMyEnabled?: boolean;
noForwardsPeerEnabled?: boolean;
}; };
const CLOSE_MENU_ANIMATION_DURATION = 200; const CLOSE_MENU_ANIMATION_DURATION = 200;
@ -184,6 +186,8 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
canShowBoostModal, canShowBoostModal,
disallowedGifts, disallowedGifts,
isAccountFrozen, isAccountFrozen,
noForwardsMyEnabled,
noForwardsPeerEnabled,
channelMonoforumId, channelMonoforumId,
onJoinRequestsClick, onJoinRequestsClick,
onSubscribeChannel, onSubscribeChannel,
@ -220,6 +224,8 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
openBoostModal, openBoostModal,
reportMessages, reportMessages,
showNotification, showNotification,
toggleNoForwards,
openDisableSharingAboutModal,
} = getActions(); } = getActions();
const oldLang = useOldLang(); const oldLang = useOldLang();
@ -495,6 +501,21 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
closeMenu(); closeMenu();
}); });
const handleToggleNoForwards = useLastCallback(() => {
closeMenu();
if (isAccountFrozen) {
openFrozenAccountModal();
return;
}
if (noForwardsMyEnabled || noForwardsPeerEnabled) {
toggleNoForwards({ userId: chatId, isEnabled: false });
return;
}
openDisableSharingAboutModal({ userId: chatId });
});
const handleSendChannelMessage = useLastCallback(() => { const handleSendChannelMessage = useLastCallback(() => {
openChat({ id: channelMonoforumId }); openChat({ id: channelMonoforumId });
closeMenu(); closeMenu();
@ -801,6 +822,14 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
{isBlocked ? oldLang('BotRestart') : oldLang('Bot.Stop')} {isBlocked ? oldLang('BotRestart') : oldLang('Bot.Stop')}
</MenuItem> </MenuItem>
)} )}
{isPrivate && !isChatWithSelf && !isBot && (
<MenuItem
icon={noForwardsMyEnabled || noForwardsPeerEnabled ? 'allow-share' : 'no-share'}
onClick={handleToggleNoForwards}
>
{noForwardsMyEnabled || noForwardsPeerEnabled ? lang('EnableSharing') : lang('DisableSharing')}
</MenuItem>
)}
{isPrivate && !isChatWithSelf && !isBot && ( {isPrivate && !isChatWithSelf && !isBot && (
<MenuItem <MenuItem
icon={isBlocked ? 'user' : 'hand-stop'} icon={isBlocked ? 'user' : 'hand-stop'}
@ -906,6 +935,8 @@ export default memo(withGlobal<OwnProps>(
savedDialog, savedDialog,
disallowedGifts: userFullInfo?.disallowedGifts, disallowedGifts: userFullInfo?.disallowedGifts,
isAccountFrozen, isAccountFrozen,
noForwardsMyEnabled: userFullInfo?.noForwardsMyEnabled,
noForwardsPeerEnabled: userFullInfo?.noForwardsPeerEnabled,
}; };
}, },
)(HeaderMenuContainer)); )(HeaderMenuContainer));

View File

@ -16,6 +16,7 @@ import {
type ApiMessage, type ApiMessage,
type ApiPeer, type ApiPeer,
type KeyboardButtonGiftOffer, type KeyboardButtonGiftOffer,
type KeyboardButtonNoForwardsRequest,
MAIN_THREAD_ID, MAIN_THREAD_ID,
} from '../../../api/types'; } from '../../../api/types';
import { MediaViewerOrigin } from '../../../types'; import { MediaViewerOrigin } from '../../../types';
@ -60,6 +61,7 @@ import ActionMessageText from './ActionMessageText';
import ChannelPhoto from './actions/ChannelPhoto'; import ChannelPhoto from './actions/ChannelPhoto';
import Gift from './actions/Gift'; import Gift from './actions/Gift';
import GiveawayPrize from './actions/GiveawayPrize'; import GiveawayPrize from './actions/GiveawayPrize';
import NoForwardsRequest from './actions/NoForwardsRequest';
import StarGift from './actions/StarGift'; import StarGift from './actions/StarGift';
import StarGiftPurchaseOffer from './actions/StarGiftPurchaseOffer'; import StarGiftPurchaseOffer from './actions/StarGiftPurchaseOffer';
import StarGiftUnique from './actions/StarGiftUnique'; import StarGiftUnique from './actions/StarGiftUnique';
@ -103,6 +105,7 @@ type StateProps = {
isResizingContainer?: boolean; isResizingContainer?: boolean;
scrollTargetPosition?: ScrollTargetPosition; scrollTargetPosition?: ScrollTargetPosition;
isAccountFrozen?: boolean; isAccountFrozen?: boolean;
noForwardsRequestExpirePeriod: number;
}; };
const SINGLE_LINE_ACTIONS = new Set<ApiMessageAction['type']>([ const SINGLE_LINE_ACTIONS = new Set<ApiMessageAction['type']>([
@ -114,7 +117,7 @@ const SINGLE_LINE_ACTIONS = new Set<ApiMessageAction['type']>([
'unsupported', 'unsupported',
]); ]);
const HIDDEN_TEXT_ACTIONS = new Set<ApiMessageAction['type']>(['giftCode', 'prizeStars', const HIDDEN_TEXT_ACTIONS = new Set<ApiMessageAction['type']>(['giftCode', 'prizeStars',
'suggestProfilePhoto', 'suggestedPostApproval', 'starGiftPurchaseOffer']); 'suggestProfilePhoto', 'suggestedPostApproval', 'starGiftPurchaseOffer', 'noForwardsRequest']);
const ActionMessage = ({ const ActionMessage = ({
message, message,
@ -138,6 +141,7 @@ const ActionMessage = ({
isResizingContainer, isResizingContainer,
scrollTargetPosition, scrollTargetPosition,
isAccountFrozen, isAccountFrozen,
noForwardsRequestExpirePeriod,
observeIntersectionForBottom, observeIntersectionForBottom,
observeIntersectionForLoading, observeIntersectionForLoading,
observeIntersectionForPlaying, observeIntersectionForPlaying,
@ -159,6 +163,7 @@ const ActionMessage = ({
openGiftOfferAcceptModal, openGiftOfferAcceptModal,
declineStarGiftOffer, declineStarGiftOffer,
showNotification, showNotification,
toggleNoForwards,
} = getActions(); } = getActions();
const ref = useRef<HTMLDivElement>(); const ref = useRef<HTMLDivElement>();
@ -182,7 +187,12 @@ const ActionMessage = ({
const shouldRenderGiftOfferButtons = action.type === 'starGiftPurchaseOffer' const shouldRenderGiftOfferButtons = action.type === 'starGiftPurchaseOffer'
&& !message.isOutgoing && !action.isAccepted && !action.isDeclined && !hasGiftOfferExpired; && !message.isOutgoing && !action.isAccepted && !action.isDeclined && !hasGiftOfferExpired;
const shouldRenderInlineButtons = shouldRenderGiftOfferButtons; const hasNoForwardsRequestExpired = action.type === 'noForwardsRequest'
&& (message.date + noForwardsRequestExpirePeriod) <= getServerTime();
const shouldRenderNoForwardsButtons = action.type === 'noForwardsRequest'
&& !message.isOutgoing && !action.isExpired && !hasNoForwardsRequestExpired;
const shouldRenderInlineButtons = shouldRenderGiftOfferButtons || shouldRenderNoForwardsButtons;
const shouldSkipRender = isInsideTopic && action.type === 'topicCreate'; const shouldSkipRender = isInsideTopic && action.type === 'topicCreate';
@ -204,6 +214,21 @@ const ActionMessage = ({
], ],
], [lang]); ], [lang]);
const noForwardsInlineButtons: KeyboardButtonNoForwardsRequest[][] = useMemo(() => [
[
{
type: 'noForwardsRequest',
buttonType: 'reject',
text: lang('NoForwardsRequestReject'),
},
{
type: 'noForwardsRequest',
buttonType: 'accept',
text: lang('NoForwardsRequestAccept'),
},
],
], [lang]);
const [isRejectOfferDialogOpen, openRejectOfferDialog, closeRejectOfferDialog] = useFlag(false); const [isRejectOfferDialogOpen, openRejectOfferDialog, closeRejectOfferDialog] = useFlag(false);
const handleInlineButtonClick = useLastCallback((button: ApiKeyboardButton) => { const handleInlineButtonClick = useLastCallback((button: ApiKeyboardButton) => {
@ -220,6 +245,15 @@ const ActionMessage = ({
} else if (button.buttonType === 'reject') { } else if (button.buttonType === 'reject') {
openRejectOfferDialog(); openRejectOfferDialog();
} }
} else if (button.type === 'noForwardsRequest') {
if (action.type === 'noForwardsRequest') {
const isAccept = button.buttonType === 'accept';
toggleNoForwards({
userId: chatId,
isEnabled: isAccept ? action.newValue : action.prevValue,
requestMsgId: id,
});
}
} }
}); });
@ -522,6 +556,13 @@ const ActionMessage = ({
/> />
); );
case 'noForwardsRequest':
return (
<NoForwardsRequest
message={message}
/>
);
case 'suggestedPostApproval': case 'suggestedPostApproval':
if (action.isBalanceTooLow) { if (action.isBalanceTooLow) {
return ( return (
@ -614,13 +655,20 @@ const ActionMessage = ({
{(fullContent || shouldRenderInlineButtons) && ( {(fullContent || shouldRenderInlineButtons) && (
<div className={styles.contentWrapper}> <div className={styles.contentWrapper}>
{fullContent} {fullContent}
{shouldRenderInlineButtons && ( {shouldRenderGiftOfferButtons && (
<InlineButtons <InlineButtons
className={styles.inlineButtons} className={styles.inlineButtons}
inlineButtons={giftOfferInlineButtons} inlineButtons={giftOfferInlineButtons}
onClick={handleInlineButtonClick} onClick={handleInlineButtonClick}
/> />
)} )}
{shouldRenderNoForwardsButtons && (
<InlineButtons
className={styles.inlineButtons}
inlineButtons={noForwardsInlineButtons}
onClick={handleInlineButtonClick}
/>
)}
</div> </div>
)} )}
{contextMenuAnchor && ( {contextMenuAnchor && (
@ -703,6 +751,7 @@ export default memo(withGlobal<OwnProps>(
isResizingContainer, isResizingContainer,
scrollTargetPosition, scrollTargetPosition,
isAccountFrozen, isAccountFrozen,
noForwardsRequestExpirePeriod: global.appConfig.noForwardsRequestExpirePeriod,
}; };
}, },
)(ActionMessage)); )(ActionMessage));

View File

@ -1059,6 +1059,25 @@ const ActionMessageText = ({
case 'phoneCall': // Rendered as a regular message, but considered an action for the summary case 'phoneCall': // Rendered as a regular message, but considered an action for the summary
return lang(getCallMessageKey(action, isOutgoing)); return lang(getCallMessageKey(action, isOutgoing));
case 'noForwardsToggle': {
const { prevValue, newValue } = action;
if (newValue && newValue === prevValue) {
return lang('ActionSharingStillDisabled');
}
return translateWithYou(
lang,
newValue ? 'ActionSharingDisabled' : 'ActionSharingEnabled',
isOutgoing,
{ from: senderLink },
);
}
case 'noForwardsRequest': {
return isOutgoing
? lang('NoForwardsRequestYouTitle')
: lang('NoForwardsRequestTitle', { user: senderLink }, { withNodes: true });
}
case 'newCreatorPending': { case 'newCreatorPending': {
const { newCreatorId } = action; const { newCreatorId } = action;
const newCreator = selectPeer(global, newCreatorId); const newCreator = selectPeer(global, newCreatorId);

View File

@ -68,6 +68,7 @@ import {
selectStickerSet, selectStickerSet,
selectTopic, selectTopic,
selectUser, selectUser,
selectUserFullInfo,
selectUserStatus, selectUserStatus,
selectWebPageFromMessage, selectWebPageFromMessage,
} from '../../../global/selectors'; } from '../../../global/selectors';
@ -162,6 +163,8 @@ type StateProps = {
userFullName?: string; userFullName?: string;
canGift?: boolean; canGift?: boolean;
savedDialogId?: string; savedDialogId?: string;
noForwardsMyEnabled?: boolean;
noForwardsPeerEnabled?: boolean;
}; };
const selection = window.getSelection(); const selection = window.getSelection();
@ -232,6 +235,8 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
canGift, canGift,
className, className,
savedDialogId, savedDialogId,
noForwardsMyEnabled,
noForwardsPeerEnabled,
onClose, onClose,
onCloseAnimationEnd, onCloseAnimationEnd,
}) => { }) => {
@ -277,6 +282,11 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
const oldLang = useOldLang(); const oldLang = useOldLang();
const lang = useLang(); const lang = useLang();
const noForwardsNotice = noForwardsPeerEnabled
? lang('ContextMenuNoForwardsPeer', { name: userFullName })
: (noForwardsMyEnabled ? lang('ContextMenuNoForwardsYou') : undefined);
const { ref: containerRef } = useShowTransition({ const { ref: containerRef } = useShowTransition({
isOpen, isOpen,
onCloseAnimationEnd, onCloseAnimationEnd,
@ -774,6 +784,7 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
onSelectLanguage={handleSelectLanguage} onSelectLanguage={handleSelectLanguage}
userFullName={userFullName} userFullName={userFullName}
canGift={canGift} canGift={canGift}
noForwardsNotice={noForwardsNotice}
/> />
<PinMessageModal <PinMessageModal
isOpen={isPinModalOpen} isOpen={isPinModalOpen}
@ -805,6 +816,7 @@ export default memo(withGlobal<OwnProps>(
const chatFullInfo = !isPrivate ? selectChatFullInfo(global, message.chatId) : undefined; const chatFullInfo = !isPrivate ? selectChatFullInfo(global, message.chatId) : undefined;
const user = selectUser(global, message.chatId); const user = selectUser(global, message.chatId);
const userFullName = user && getUserFullName(user); const userFullName = user && getUserFullName(user);
const userFullInfo = isPrivate ? selectUserFullInfo(global, message.chatId) : undefined;
const { const {
seenByExpiresAt, seenByMaxChatMembers, maxUniqueReactions, readDateExpiresAt, seenByExpiresAt, seenByMaxChatMembers, maxUniqueReactions, readDateExpiresAt,
@ -970,6 +982,8 @@ export default memo(withGlobal<OwnProps>(
canGift, canGift,
savedDialogId, savedDialogId,
webPage, webPage,
noForwardsMyEnabled: userFullInfo?.noForwardsMyEnabled,
noForwardsPeerEnabled: userFullInfo?.noForwardsPeerEnabled,
}; };
}, },
)(ContextMenuContainer)); )(ContextMenuContainer));

View File

@ -75,4 +75,8 @@
top: 0; top: 0;
transform: translateY(calc(-100% - 0.5rem)); transform: translateY(calc(-100% - 0.5rem));
} }
.no-forwards-notice {
min-width: 12rem;
}
} }

View File

@ -136,6 +136,7 @@ type OwnProps = {
onReactionPickerOpen?: (position: IAnchorPosition) => void; onReactionPickerOpen?: (position: IAnchorPosition) => void;
userFullName?: string; userFullName?: string;
canGift?: boolean; canGift?: boolean;
noForwardsNotice?: string;
}; };
const SCROLLBAR_WIDTH = 10; const SCROLLBAR_WIDTH = 10;
@ -230,6 +231,7 @@ const MessageContextMenu: FC<OwnProps> = ({
onSelectLanguage, onSelectLanguage,
userFullName, userFullName,
canGift, canGift,
noForwardsNotice,
}) => { }) => {
const { const {
showNotification, openStickerSet, openCustomEmojiSets, loadStickers, openGiftModal, showNotification, openStickerSet, openCustomEmojiSets, loadStickers, openGiftModal,
@ -515,7 +517,7 @@ const MessageContextMenu: FC<OwnProps> = ({
</MenuItem> </MenuItem>
</> </>
)} )}
{(canLoadReadDate || shouldRenderShowWhen || isEdited) && ( {(canLoadReadDate || shouldRenderShowWhen || isEdited || noForwardsNotice) && (
<MenuSeparator size={hasCustomEmoji ? 'thin' : 'thick'} /> <MenuSeparator size={hasCustomEmoji ? 'thin' : 'thick'} />
)} )}
{(canLoadReadDate || shouldRenderShowWhen) && ( {(canLoadReadDate || shouldRenderShowWhen) && (
@ -531,6 +533,11 @@ const MessageContextMenu: FC<OwnProps> = ({
message={message} message={message}
/> />
)} )}
{noForwardsNotice && (
<MenuItem disabled withWrap className="no-forwards-notice">
{noForwardsNotice}
</MenuItem>
)}
</div> </div>
</Menu> </Menu>
); );

View File

@ -0,0 +1,27 @@
.root {
padding: 0.75rem 1rem;
text-align: center;
}
.title {
line-height: 1.125rem;
}
.list {
display: flex;
flex-direction: column;
gap: 0.5rem;
align-items: flex-start;
width: 100%;
}
.item {
display: flex;
gap: 0.375rem;
align-items: center;
}
.checkIcon {
font-size: 1rem;
}

View File

@ -0,0 +1,72 @@
import { memo } from '../../../../lib/teact/teact';
import { withGlobal } from '../../../../global';
import type { ApiMessage, ApiPeer } from '../../../../api/types';
import { getPeerTitle } from '../../../../global/helpers/peers';
import { selectSender } from '../../../../global/selectors';
import buildClassName from '../../../../util/buildClassName';
import { renderPeerLink } from '../helpers/messageActions';
import useLang from '../../../../hooks/useLang';
import Icon from '../../../common/icons/Icon';
import actionStyles from '../ActionMessage.module.scss';
import styles from './NoForwardsRequest.module.scss';
type OwnProps = {
message: ApiMessage;
};
type StateProps = {
sender?: ApiPeer;
};
const SHARING_FEATURES = [
'NoForwardsRequestForwarding',
'NoForwardsRequestSaving',
'NoForwardsRequestCopying',
] as const;
const NoForwardsRequest = ({
message,
sender,
}: OwnProps & StateProps) => {
const lang = useLang();
const { isOutgoing } = message;
const peerTitle = sender && getPeerTitle(lang, sender);
const peerLink = renderPeerLink(sender?.id, peerTitle || lang('ActionFallbackUser'));
const title = isOutgoing
? lang('NoForwardsRequestYouTitle')
: lang('NoForwardsRequestTitle', { user: peerLink }, { withNodes: true, withMarkdown: false });
return (
<div className={buildClassName(actionStyles.contentBox, styles.root)}>
<div className={styles.title}>
{title}
</div>
<div className={styles.list}>
{SHARING_FEATURES.map((featureKey) => (
<div key={featureKey} className={styles.item}>
<Icon name="check" className={styles.checkIcon} />
<span>{lang(featureKey)}</span>
</div>
))}
</div>
</div>
);
};
export default memo(withGlobal<OwnProps>(
(global, { message }): Complete<StateProps> => {
const sender = selectSender(global, message);
return {
sender,
};
},
)(NoForwardsRequest));

View File

@ -18,6 +18,7 @@ import ChatlistModal from './chatlist/ChatlistModal.async';
import CocoonModal from './cocoon/CocoonModal.async'; import CocoonModal from './cocoon/CocoonModal.async';
import CollectibleInfoModal from './collectible/CollectibleInfoModal.async'; import CollectibleInfoModal from './collectible/CollectibleInfoModal.async';
import DeleteAccountModal from './deleteAccount/DeleteAccountModal.async'; import DeleteAccountModal from './deleteAccount/DeleteAccountModal.async';
import DisableSharingAboutModal from './disableSharing/DisableSharingAboutModal.async';
import EmojiStatusAccessModal from './emojiStatusAccess/EmojiStatusAccessModal.async'; import EmojiStatusAccessModal from './emojiStatusAccess/EmojiStatusAccessModal.async';
import FrozenAccountModal from './frozenAccount/FrozenAccountModal.async'; import FrozenAccountModal from './frozenAccount/FrozenAccountModal.async';
import AboutStarGiftModal from './gift/AboutStarGiftModal.async'; import AboutStarGiftModal from './gift/AboutStarGiftModal.async';
@ -134,6 +135,7 @@ type ModalKey = keyof Pick<TabState,
'giftDescriptionRemoveModal' | 'giftDescriptionRemoveModal' |
'giftOfferAcceptModal' | 'giftOfferAcceptModal' |
'chatRefundModal' | 'chatRefundModal' |
'disableSharingAboutModal' |
'priceConfirmModal' | 'priceConfirmModal' |
'isFrozenAccountModalOpen' | 'isFrozenAccountModalOpen' |
'deleteAccountModal' | 'deleteAccountModal' |
@ -219,6 +221,7 @@ const MODALS: ModalRegistry = {
giftDescriptionRemoveModal: GiftDescriptionRemoveModal, giftDescriptionRemoveModal: GiftDescriptionRemoveModal,
giftOfferAcceptModal: GiftOfferAcceptModal, giftOfferAcceptModal: GiftOfferAcceptModal,
chatRefundModal: ChatRefundModal, chatRefundModal: ChatRefundModal,
disableSharingAboutModal: DisableSharingAboutModal,
priceConfirmModal: PriceConfirmModal, priceConfirmModal: PriceConfirmModal,
isFrozenAccountModalOpen: FrozenAccountModal, isFrozenAccountModalOpen: FrozenAccountModal,
deleteAccountModal: DeleteAccountModal, deleteAccountModal: DeleteAccountModal,

View File

@ -0,0 +1,14 @@
import type { OwnProps } from './DisableSharingAboutModal';
import { Bundles } from '../../../util/moduleLoader';
import useModuleLoader from '../../../hooks/useModuleLoader';
const DisableSharingAboutModalAsync = (props: OwnProps) => {
const { modal } = props;
const DisableSharingAboutModal = useModuleLoader(Bundles.Extra, 'DisableSharingAboutModal', !modal);
return DisableSharingAboutModal ? <DisableSharingAboutModal {...props} /> : undefined;
};
export default DisableSharingAboutModalAsync;

View File

@ -0,0 +1,23 @@
.header {
display: flex;
flex-direction: column;
gap: 0.75rem;
align-items: center;
}
.title {
margin: 0;
margin-bottom: 0.5rem;
font-size: 1.25rem;
font-weight: var(--font-weight-semibold);
}
.footer {
display: flex;
align-self: stretch;
margin-top: 1rem;
}
.unlockIcon {
font-size: 1.25rem;
}

View File

@ -0,0 +1,127 @@
import { memo, useMemo } from '../../../lib/teact/teact';
import { getActions, withGlobal } from '../../../global';
import type { TabState } from '../../../global/types';
import { selectIsCurrentUserPremium } from '../../../global/selectors';
import { LOCAL_TGS_PREVIEW_URLS, LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets';
import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback';
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
import Button from '../../ui/Button';
import TableAboutModal, { type TableAboutData } from '../common/TableAboutModal';
import styles from './DisableSharingAboutModal.module.scss';
const ICON_SIZE = 100;
export type OwnProps = {
modal: TabState['disableSharingAboutModal'];
};
type StateProps = {
isCurrentUserPremium?: boolean;
};
const DisableSharingAboutModal = ({
modal,
isCurrentUserPremium,
}: OwnProps & StateProps) => {
const {
closeDisableSharingAboutModal,
toggleNoForwards,
openPremiumModal,
} = getActions();
const lang = useLang();
const isOpen = Boolean(modal);
const renderingModal = useCurrentOrPrev(modal);
const userId = renderingModal?.userId;
const handleClose = useLastCallback(() => {
closeDisableSharingAboutModal();
});
const handleDisableSharing = useLastCallback(() => {
if (userId) {
toggleNoForwards({ userId, isEnabled: true });
}
closeDisableSharingAboutModal();
});
const handleOpenPremium = useLastCallback(() => {
closeDisableSharingAboutModal();
openPremiumModal({ initialSection: 'pm_noforwards' });
});
const header = useMemo(() => {
return (
<div className={styles.header}>
<AnimatedIconWithPreview
size={ICON_SIZE}
tgsUrl={LOCAL_TGS_URLS.HandStop}
previewUrl={LOCAL_TGS_PREVIEW_URLS.HandStop}
noLoop
/>
<h3 className={styles.title}>{lang('DisableSharing')}</h3>
</div>
);
}, [lang]);
const listItemData = useMemo(() => {
return [
['no-share', lang('NoForwardingTitle'), lang('NoForwardingDescription')],
['no-download', lang('NoSavingTitle'), lang('NoSavingDescription')],
] satisfies TableAboutData;
}, [lang]);
const footer = useMemo(() => {
if (isCurrentUserPremium) {
return (
<div className={styles.footer}>
<Button
onClick={handleDisableSharing}
noForcedUpperCase
>
{lang('DisableSharing')}
</Button>
</div>
);
}
return (
<div className={styles.footer}>
<Button
onClick={handleOpenPremium}
iconName="unlock-badge"
iconClassName={styles.unlockIcon}
noForcedUpperCase
>
{lang('UnlockButtonTitle')}
</Button>
</div>
);
}, [isCurrentUserPremium, lang, handleDisableSharing, handleOpenPremium]);
return (
<TableAboutModal
isOpen={isOpen}
header={header}
listItemData={listItemData}
footer={footer}
onClose={handleClose}
/>
);
};
export default memo(withGlobal<OwnProps>(
(global): Complete<StateProps> => {
return {
isCurrentUserPremium: selectIsCurrentUserPremium(global),
};
},
)(DisableSharingAboutModal));

View File

@ -68,6 +68,13 @@
&.disabled { &.disabled {
cursor: var(--custom-cursor, default) !important; cursor: var(--custom-cursor, default) !important;
opacity: 0.5 !important; opacity: 0.5 !important;
&:hover,
&:focus,
&:active {
transform: none !important;
background-color: transparent !important;
}
} }
&.destructive { &.destructive {

View File

@ -127,7 +127,7 @@ const MenuItem = (props: MenuItemProps) => {
return ( return (
<div <div
role="menuitem" role="menuitem"
tabIndex={0} tabIndex={disabled ? -1 : 0}
className={fullClassName} className={fullClassName}
onClick={handleClick} onClick={handleClick}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}

View File

@ -417,6 +417,7 @@ export const PREMIUM_FEATURE_SECTIONS = [
'message_privacy', 'message_privacy',
'effects', 'effects',
'todo', 'todo',
'pm_noforwards',
] as const; ] as const;
export const PREMIUM_BOTTOM_VIDEOS: ApiPremiumSection[] = [ export const PREMIUM_BOTTOM_VIDEOS: ApiPremiumSection[] = [

View File

@ -629,3 +629,14 @@ addActionHandler('markBotVerificationInfoShown', (global, actions, payload): Act
setGlobal(global); setGlobal(global);
}); });
addActionHandler('toggleNoForwards', async (global, actions, payload): Promise<void> => {
const { userId, isEnabled, requestMsgId } = payload;
const user = selectUser(global, userId);
if (!user) {
return;
}
await callApi('toggleNoForwards', { user, isEnabled, requestMsgId });
});

View File

@ -73,6 +73,7 @@ import {
updateThreadInfoMessagesCount, updateThreadInfoMessagesCount,
updateThreadReadState, updateThreadReadState,
} from '../../reducers/threads'; } from '../../reducers/threads';
import { updateUserFullInfo } from '../../reducers/users';
import { import {
selectCanAnimateSnapEffect, selectCanAnimateSnapEffect,
selectChat, selectChat,
@ -179,6 +180,19 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
} else { } else {
global = updateChatLastMessage(global, chatId, newMessage); global = updateChatLastMessage(global, chatId, newMessage);
} }
if (!isLocal && message.isOutgoing && message.content?.action?.type === 'noForwardsRequest') {
const currentMessageList = selectCurrentMessageList(global, tabId);
if (currentMessageList?.chatId === chatId && currentMessageList.type === 'thread') {
actions.focusMessage({
chatId,
threadId: MAIN_THREAD_ID,
messageId: message.id,
noHighlight: true,
tabId,
});
}
}
}); });
if (poll) { if (poll) {
@ -201,6 +215,37 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
global = replaceThreadLocalStateParam(global, chatId, threadId, 'typingDraftIdByRandomId', undefined); global = replaceThreadLocalStateParam(global, chatId, threadId, 'typingDraftIdByRandomId', undefined);
} }
if (!isLocal && message.content?.action?.type === 'noForwardsToggle') {
const { newValue } = message.content.action;
if (message.isOutgoing) {
global = updateUserFullInfo(global, chatId, {
noForwardsMyEnabled: newValue,
});
const tabId = getCurrentTabId();
if (selectCurrentMessageList(global, tabId)?.chatId === chatId) {
actions.showNotification({
icon: newValue ? 'hand-stop-filled' : 'select-filled',
message: { key: newValue ? 'NotificationSharingDisabled' : 'NotificationSharingEnabled' },
tabId,
});
}
} else {
const originalMessage = replyInfo?.replyToMsgId ?
selectChatMessage(global, chatId, replyInfo.replyToMsgId) : undefined;
// When peer accepted user request to enable sharing
if (originalMessage?.isOutgoing && !newValue) {
global = updateUserFullInfo(global, chatId, {
noForwardsMyEnabled: false,
});
} else {
global = updateUserFullInfo(global, chatId, {
noForwardsPeerEnabled: newValue,
});
}
}
}
setGlobal(global); setGlobal(global);
// Reload dialogs if chat is not present in the list // Reload dialogs if chat is not present in the list

View File

@ -65,6 +65,16 @@ addActionHandler('closeSuggestedStatusModal', (global, actions, payload): Action
addTabStateResetterAction('closeChatRefundModal', 'chatRefundModal'); addTabStateResetterAction('closeChatRefundModal', 'chatRefundModal');
addActionHandler('openDisableSharingAboutModal', (global, actions, payload): ActionReturnType => {
const { userId, tabId = getCurrentTabId() } = payload;
return updateTabState(global, {
disableSharingAboutModal: { userId },
}, tabId);
});
addTabStateResetterAction('closeDisableSharingAboutModal', 'disableSharingAboutModal');
addActionHandler('openProfileRatingModal', (global, actions, payload): ActionReturnType => { addActionHandler('openProfileRatingModal', (global, actions, payload): ActionReturnType => {
const { userId, level, tabId = getCurrentTabId() } = payload; const { userId, level, tabId = getCurrentTabId() } = payload;

View File

@ -97,7 +97,7 @@ import {
} from './threads'; } from './threads';
import { selectTopic, selectTopicFromMessage } from './topics'; import { selectTopic, selectTopicFromMessage } from './topics';
import { import {
selectBot, selectUser, selectUserStatus, selectBot, selectIsUserChatProtected, selectUser, selectUserStatus,
} from './users'; } from './users';
export function selectCurrentMessageList<T extends GlobalState>( export function selectCurrentMessageList<T extends GlobalState>(
@ -1141,11 +1141,19 @@ export function selectIsMessageProtected<T extends GlobalState>(global: T, messa
} }
export function selectIsChatProtected<T extends GlobalState>(global: T, chatId: string) { export function selectIsChatProtected<T extends GlobalState>(global: T, chatId: string) {
return selectChat(global, chatId)?.isProtected || false; const chat = selectChat(global, chatId);
if (!chat) return false;
if (chat.isProtected || (isUserId(chatId) && selectIsUserChatProtected(global, chatId))) {
return true;
}
return false;
} }
export function selectHasProtectedMessage<T extends GlobalState>(global: T, chatId: string, messageIds?: number[]) { export function selectHasProtectedMessage<T extends GlobalState>(global: T, chatId: string, messageIds?: number[]) {
if (selectChat(global, chatId)?.isProtected) { if (selectIsChatProtected(global, chatId)) {
return true; return true;
} }
@ -1159,7 +1167,7 @@ export function selectHasProtectedMessage<T extends GlobalState>(global: T, chat
} }
export function selectCanForwardMessages<T extends GlobalState>(global: T, chatId: string, messageIds?: number[]) { export function selectCanForwardMessages<T extends GlobalState>(global: T, chatId: string, messageIds?: number[]) {
if (selectChat(global, chatId)?.isProtected) { if (selectIsChatProtected(global, chatId)) {
return false; return false;
} }

View File

@ -29,6 +29,13 @@ export function selectIsUserBlocked<T extends GlobalState>(global: T, userId: st
return selectUserFullInfo(global, userId)?.isBlocked; return selectUserFullInfo(global, userId)?.isBlocked;
} }
export function selectIsUserChatProtected<T extends GlobalState>(global: T, userId: string) {
const fullInfo = selectUserFullInfo(global, userId);
if (!fullInfo) return undefined;
return Boolean(fullInfo.noForwardsMyEnabled || fullInfo.noForwardsPeerEnabled);
}
export function selectIsCurrentUserPremium<T extends GlobalState>(global: T) { export function selectIsCurrentUserPremium<T extends GlobalState>(global: T) {
if (!global.currentUserId) return false; if (!global.currentUserId) return false;

View File

@ -315,6 +315,11 @@ export interface ActionPayloads {
markBotVerificationInfoShown: { markBotVerificationInfoShown: {
peerId: string; peerId: string;
}; };
toggleNoForwards: {
userId: string;
isEnabled: boolean;
requestMsgId?: number;
};
// Message search // Message search
openMiddleSearch: { openMiddleSearch: {
@ -1938,6 +1943,10 @@ export interface ActionPayloads {
userId: string; userId: string;
} & WithTabId; } & WithTabId;
closeChatRefundModal: WithTabId | undefined; closeChatRefundModal: WithTabId | undefined;
openDisableSharingAboutModal: {
userId: string;
} & WithTabId;
closeDisableSharingAboutModal: WithTabId | undefined;
openProfileRatingModal: { openProfileRatingModal: {
userId: string; userId: string;
level: number; level: number;

View File

@ -719,6 +719,10 @@ export type TabState = {
starsToRefund: number; starsToRefund: number;
}; };
disableSharingAboutModal?: {
userId: string;
};
limitReachedModal?: { limitReachedModal?: {
limit: ApiLimitTypeWithModal; limit: ApiLimitTypeWithModal;
}; };

View File

@ -110,6 +110,7 @@ export const DEFAULT_APP_CONFIG: ApiAppConfig = {
'animated_userpics', 'animated_userpics',
'premium_stickers', 'premium_stickers',
'effects', 'effects',
'pm_noforwards',
], ],
isPremiumPurchaseBlocked: false, isPremiumPurchaseBlocked: false,
maxUniqueReactions: 11, maxUniqueReactions: 11,
@ -129,6 +130,7 @@ export const DEFAULT_APP_CONFIG: ApiAppConfig = {
starsSuggestedPostFutureMin: 300, starsSuggestedPostFutureMin: 300,
starsSuggestedPostFutureMax: 2678400, starsSuggestedPostFutureMax: 2678400,
starsSuggestedPostCommissionPermille: 850, starsSuggestedPostCommissionPermille: 850,
noForwardsRequestExpirePeriod: 86400,
tonSuggestedPostCommissionPermille: 850, tonSuggestedPostCommissionPermille: 850,
todoItemLengthMax: 64, todoItemLengthMax: 64,
todoItemsMax: 30, todoItemsMax: 30,

File diff suppressed because it is too large Load Diff

View File

@ -16,327 +16,332 @@
} }
$icons-map: ( $icons-map: (
"active-sessions": "\f101", "zoom-out": "\f101",
"add-caption": "\f102", "zoom-in": "\f102",
"add-filled": "\f103", "word-wrap": "\f103",
"add-one-badge": "\f104", "webapp": "\f104",
"add-user-filled": "\f105", "web": "\f105",
"add-user": "\f106", "warning": "\f106",
"add": "\f107", "volume-3": "\f107",
"admin": "\f108", "volume-2": "\f108",
"allow-speak": "\f109", "volume-1": "\f109",
"animals": "\f10a", "voice-chat": "\f10a",
"animations": "\f10b", "view-once": "\f10b",
"archive-filled": "\f10c", "video": "\f10c",
"archive-from-main": "\f10d", "video-stop": "\f10d",
"archive-to-main": "\f10e", "video-outlined": "\f10e",
"archive": "\f10f", "user": "\f10f",
"arrow-down-circle": "\f110", "user-tag": "\f110",
"arrow-down": "\f111", "user-stars": "\f111",
"arrow-left": "\f112", "user-online": "\f112",
"arrow-right": "\f113", "user-filled": "\f113",
"ask-support": "\f114", "up": "\f114",
"attach": "\f115", "unread": "\f115",
"auction-drop": "\f116", "unpin": "\f116",
"auction-filled": "\f117", "unmute": "\f117",
"auction-next-round": "\f118", "unlock": "\f118",
"auction": "\f119", "unlock-badge": "\f119",
"author-hidden": "\f11a", "unlist": "\f11a",
"avatar-archived-chats": "\f11b", "unlist-outline": "\f11b",
"avatar-deleted-account": "\f11c", "unique-profile": "\f11c",
"avatar-saved-messages": "\f11d", "undo": "\f11d",
"bold": "\f11e", "understood": "\f11e",
"boost-craft-chance": "\f11f", "underlined": "\f11f",
"boost-outline": "\f120", "unarchive": "\f120",
"boost": "\f121", "truck": "\f121",
"boostcircle": "\f122", "transcribe": "\f122",
"boosts": "\f123", "trade": "\f123",
"bot-command": "\f124", "topic-new": "\f124",
"bot-commands-filled": "\f125", "tools": "\f125",
"bots": "\f126", "toncoin": "\f126",
"brush": "\f127", "timer": "\f127",
"bug": "\f128", "tag": "\f128",
"calendar-filter": "\f129", "tag-name": "\f129",
"calendar": "\f12a", "tag-filter": "\f12a",
"camera-add": "\f12b", "tag-crossed": "\f12b",
"camera": "\f12c", "tag-add": "\f12c",
"car": "\f12d", "strikethrough": "\f12d",
"card": "\f12e", "story-reply": "\f12e",
"cash-circle": "\f12f", "story-priority": "\f12f",
"channel-filled": "\f130", "story-expired": "\f130",
"channel": "\f131", "story-caption": "\f131",
"channelviews": "\f132", "stop": "\f132",
"chat-badge": "\f133", "stop-raising-hand": "\f133",
"chats-badge": "\f134", "stickers": "\f134",
"check": "\f135", "stealth-past": "\f135",
"clock-edit": "\f136", "stealth-future": "\f136",
"clock": "\f137", "stats": "\f137",
"close-circle": "\f138", "stars-refund": "\f138",
"close-topic": "\f139", "stars-lock": "\f139",
"close": "\f13a", "star": "\f13a",
"closed-gift": "\f13b", "sport": "\f13b",
"cloud-download": "\f13c", "spoiler": "\f13c",
"collapse-modal": "\f13d", "spoiler-disable": "\f13d",
"collapse": "\f13e", "speaker": "\f13e",
"colorize": "\f13f", "speaker-story": "\f13f",
"combine-craft": "\f140", "speaker-outline": "\f140",
"comments-sticker": "\f141", "speaker-muted-story": "\f141",
"comments": "\f142", "sort": "\f142",
"copy-media": "\f143", "sort-by-price": "\f143",
"copy": "\f144", "sort-by-number": "\f144",
"craft": "\f145", "sort-by-date": "\f145",
"crop": "\f146", "smile": "\f146",
"crown-take-off-outline": "\f147", "smallscreen": "\f147",
"crown-take-off": "\f148", "skip-previous": "\f148",
"crown-wear-outline": "\f149", "skip-next": "\f149",
"crown-wear": "\f14a", "sidebar": "\f14a",
"darkmode": "\f14b", "show-message": "\f14b",
"data": "\f14c", "share-screen": "\f14c",
"delete-filled": "\f14d", "share-screen-stop": "\f14d",
"delete-left": "\f14e", "share-screen-outlined": "\f14e",
"delete-user": "\f14f", "share-filled": "\f14f",
"delete": "\f150", "settings": "\f150",
"diamond": "\f151", "settings-filled": "\f151",
"document": "\f152", "send": "\f152",
"double-badge": "\f153", "send-outline": "\f153",
"down": "\f154", "sell": "\f154",
"download": "\f155", "sell-outline": "\f155",
"dropdown-arrows": "\f156", "select": "\f156",
"eats": "\f157", "select-filled": "\f157",
"edit": "\f158", "search": "\f158",
"email": "\f159", "sd-photo": "\f159",
"enter": "\f15a", "scheduled": "\f15a",
"expand-modal": "\f15b", "schedule": "\f15b",
"expand": "\f15c", "saved-messages": "\f15c",
"eye-crossed-outline": "\f15d", "save-story": "\f15d",
"eye-crossed": "\f15e", "rotate": "\f15e",
"eye-outline": "\f15f", "revote": "\f15f",
"eye": "\f160", "revenue-split": "\f160",
"favorite-filled": "\f161", "reply": "\f161",
"favorite": "\f162", "reply-filled": "\f162",
"file-badge": "\f163", "replies": "\f163",
"flag": "\f164", "replace": "\f164",
"flip": "\f165", "reorder-tabs": "\f165",
"folder-badge": "\f166", "reopen-topic": "\f166",
"folder-tabs-bot": "\f167", "remove": "\f167",
"folder-tabs-channel": "\f168", "remove-quote": "\f168",
"folder-tabs-chat": "\f169", "reload": "\f169",
"folder-tabs-chats": "\f16a", "refund": "\f16a",
"folder-tabs-folder": "\f16b", "redo": "\f16b",
"folder-tabs-group": "\f16c", "recent": "\f16c",
"folder-tabs-star": "\f16d", "readchats": "\f16d",
"folder-tabs-user": "\f16e", "radial-badge": "\f16e",
"folder": "\f16f", "quote": "\f16f",
"fontsize": "\f170", "quote-text": "\f170",
"forums": "\f171", "proof-of-ownership": "\f171",
"forward": "\f172", "privacy-policy": "\f172",
"fragment": "\f173", "previous": "\f173",
"frozen-time": "\f174", "poll": "\f174",
"fullscreen": "\f175", "play": "\f175",
"gifs": "\f176", "play-story": "\f176",
"gift-transfer-inline": "\f177", "pip": "\f177",
"gift": "\f178", "pinned-message": "\f178",
"group-filled": "\f179", "pinned-chat": "\f179",
"group": "\f17a", "pin": "\f17a",
"grouped-disable": "\f17b", "pin-list": "\f17b",
"grouped": "\f17c", "pin-badge": "\f17c",
"hand-stop": "\f17d", "photo": "\f17d",
"hashtag": "\f17e", "phone": "\f17e",
"hd-photo": "\f17f", "phone-discard": "\f17f",
"heart-outline": "\f180", "phone-discard-outline": "\f180",
"heart": "\f181", "permissions": "\f181",
"help": "\f182", "pause": "\f182",
"info-filled": "\f183", "password-off": "\f183",
"info": "\f184", "open-in-new-tab": "\f184",
"install": "\f185", "one-filled": "\f185",
"italic": "\f186", "note": "\f186",
"key": "\f187", "non-contacts": "\f187",
"keyboard": "\f188", "noise-suppression": "\f188",
"lamp": "\f189", "nochannel": "\f189",
"language": "\f18a", "no-share": "\f18a",
"large-pause": "\f18b", "no-download": "\f18b",
"large-play": "\f18c", "next": "\f18c",
"link-badge": "\f18d", "next-link": "\f18d",
"link-broken": "\f18e", "new-chat-filled": "\f18e",
"link": "\f18f", "my-notes": "\f18f",
"location": "\f190", "muted": "\f190",
"lock-badge": "\f191", "mute": "\f191",
"lock": "\f192", "move-caption-up": "\f192",
"logout": "\f193", "move-caption-down": "\f193",
"loop": "\f194", "more": "\f194",
"mention": "\f195", "more-circle": "\f195",
"menu": "\f196", "monospace": "\f196",
"message-failed": "\f197", "microphone": "\f197",
"message-pending": "\f198", "microphone-alt": "\f198",
"message-read": "\f199", "message": "\f199",
"message-succeeded": "\f19a", "message-succeeded": "\f19a",
"message": "\f19b", "message-read": "\f19b",
"microphone-alt": "\f19c", "message-pending": "\f19c",
"microphone": "\f19d", "message-failed": "\f19d",
"monospace": "\f19e", "menu": "\f19e",
"more-circle": "\f19f", "mention": "\f19f",
"more": "\f1a0", "loop": "\f1a0",
"move-caption-down": "\f1a1", "logout": "\f1a1",
"move-caption-up": "\f1a2", "lock": "\f1a2",
"mute": "\f1a3", "lock-badge": "\f1a3",
"muted": "\f1a4", "location": "\f1a4",
"my-notes": "\f1a5", "link": "\f1a5",
"new-chat-filled": "\f1a6", "link-broken": "\f1a6",
"next-link": "\f1a7", "link-badge": "\f1a7",
"next": "\f1a8", "large-play": "\f1a8",
"nochannel": "\f1a9", "large-pause": "\f1a9",
"noise-suppression": "\f1aa", "language": "\f1aa",
"non-contacts": "\f1ab", "lamp": "\f1ab",
"note": "\f1ac", "keyboard": "\f1ac",
"one-filled": "\f1ad", "key": "\f1ad",
"open-in-new-tab": "\f1ae", "italic": "\f1ae",
"password-off": "\f1af", "install": "\f1af",
"pause": "\f1b0", "info": "\f1b0",
"permissions": "\f1b1", "info-filled": "\f1b1",
"phone-discard-outline": "\f1b2", "help": "\f1b2",
"phone-discard": "\f1b3", "heart": "\f1b3",
"phone": "\f1b4", "heart-outline": "\f1b4",
"photo": "\f1b5", "hd-photo": "\f1b5",
"pin-badge": "\f1b6", "hashtag": "\f1b6",
"pin-list": "\f1b7", "hand-stop": "\f1b7",
"pin": "\f1b8", "hand-stop-filled": "\f1b8",
"pinned-chat": "\f1b9", "grouped": "\f1b9",
"pinned-message": "\f1ba", "grouped-disable": "\f1ba",
"pip": "\f1bb", "group": "\f1bb",
"play-story": "\f1bc", "group-filled": "\f1bc",
"play": "\f1bd", "gift": "\f1bd",
"poll": "\f1be", "gift-transfer-inline": "\f1be",
"previous": "\f1bf", "gifs": "\f1bf",
"privacy-policy": "\f1c0", "fullscreen": "\f1c0",
"proof-of-ownership": "\f1c1", "frozen-time": "\f1c1",
"quote-text": "\f1c2", "fragment": "\f1c2",
"quote": "\f1c3", "forward": "\f1c3",
"radial-badge": "\f1c4", "forums": "\f1c4",
"rating-icons-level1": "\f1c5", "fontsize": "\f1c5",
"rating-icons-level10": "\f1c6", "folder": "\f1c6",
"rating-icons-level2": "\f1c7", "folder-badge": "\f1c7",
"rating-icons-level20": "\f1c8", "flip": "\f1c8",
"rating-icons-level3": "\f1c9", "flag": "\f1c9",
"rating-icons-level30": "\f1ca", "file-badge": "\f1ca",
"rating-icons-level4": "\f1cb", "favorite": "\f1cb",
"rating-icons-level40": "\f1cc", "favorite-filled": "\f1cc",
"rating-icons-level5": "\f1cd", "eye": "\f1cd",
"rating-icons-level50": "\f1ce", "eye-outline": "\f1ce",
"rating-icons-level6": "\f1cf", "eye-crossed": "\f1cf",
"rating-icons-level60": "\f1d0", "eye-crossed-outline": "\f1d0",
"rating-icons-level7": "\f1d1", "expand": "\f1d1",
"rating-icons-level70": "\f1d2", "expand-modal": "\f1d2",
"rating-icons-level8": "\f1d3", "enter": "\f1d3",
"rating-icons-level80": "\f1d4", "email": "\f1d4",
"rating-icons-level9": "\f1d5", "edit": "\f1d5",
"rating-icons-level90": "\f1d6", "eats": "\f1d6",
"rating-icons-negative": "\f1d7", "dropdown-arrows": "\f1d7",
"readchats": "\f1d8", "download": "\f1d8",
"recent": "\f1d9", "down": "\f1d9",
"redo": "\f1da", "double-badge": "\f1da",
"refund": "\f1db", "document": "\f1db",
"reload": "\f1dc", "diamond": "\f1dc",
"remove-quote": "\f1dd", "delete": "\f1dd",
"remove": "\f1de", "delete-user": "\f1de",
"reopen-topic": "\f1df", "delete-left": "\f1df",
"reorder-tabs": "\f1e0", "delete-filled": "\f1e0",
"replace": "\f1e1", "data": "\f1e1",
"replies": "\f1e2", "darkmode": "\f1e2",
"reply-filled": "\f1e3", "crown-wear": "\f1e3",
"reply": "\f1e4", "crown-wear-outline": "\f1e4",
"revenue-split": "\f1e5", "crown-take-off": "\f1e5",
"revote": "\f1e6", "crown-take-off-outline": "\f1e6",
"rotate": "\f1e7", "crop": "\f1e7",
"save-story": "\f1e8", "craft": "\f1e8",
"saved-messages": "\f1e9", "copy": "\f1e9",
"schedule": "\f1ea", "copy-media": "\f1ea",
"scheduled": "\f1eb", "comments": "\f1eb",
"sd-photo": "\f1ec", "comments-sticker": "\f1ec",
"search": "\f1ed", "combine-craft": "\f1ed",
"select": "\f1ee", "colorize": "\f1ee",
"sell-outline": "\f1ef", "collapse": "\f1ef",
"sell": "\f1f0", "collapse-modal": "\f1f0",
"send-outline": "\f1f1", "cloud-download": "\f1f1",
"send": "\f1f2", "closed-gift": "\f1f2",
"settings-filled": "\f1f3", "close": "\f1f3",
"settings": "\f1f4", "close-topic": "\f1f4",
"share-filled": "\f1f5", "close-circle": "\f1f5",
"share-screen-outlined": "\f1f6", "clock": "\f1f6",
"share-screen-stop": "\f1f7", "clock-edit": "\f1f7",
"share-screen": "\f1f8", "check": "\f1f8",
"show-message": "\f1f9", "chats-badge": "\f1f9",
"sidebar": "\f1fa", "chat-badge": "\f1fa",
"skip-next": "\f1fb", "channelviews": "\f1fb",
"skip-previous": "\f1fc", "channel": "\f1fc",
"smallscreen": "\f1fd", "channel-filled": "\f1fd",
"smile": "\f1fe", "cash-circle": "\f1fe",
"sort-by-date": "\f1ff", "card": "\f1ff",
"sort-by-number": "\f200", "car": "\f200",
"sort-by-price": "\f201", "camera": "\f201",
"sort": "\f202", "camera-add": "\f202",
"speaker-muted-story": "\f203", "calendar": "\f203",
"speaker-outline": "\f204", "calendar-filter": "\f204",
"speaker-story": "\f205", "bug": "\f205",
"speaker": "\f206", "brush": "\f206",
"spoiler-disable": "\f207", "bots": "\f207",
"spoiler": "\f208", "bot-commands-filled": "\f208",
"sport": "\f209", "bot-command": "\f209",
"star": "\f20a", "boosts": "\f20a",
"stars-lock": "\f20b", "boostcircle": "\f20b",
"stars-refund": "\f20c", "boost": "\f20c",
"stats": "\f20d", "boost-outline": "\f20d",
"stealth-future": "\f20e", "boost-craft-chance": "\f20e",
"stealth-past": "\f20f", "bold": "\f20f",
"stickers": "\f210", "avatar-saved-messages": "\f210",
"stop-raising-hand": "\f211", "avatar-deleted-account": "\f211",
"stop": "\f212", "avatar-archived-chats": "\f212",
"story-caption": "\f213", "author-hidden": "\f213",
"story-expired": "\f214", "auction": "\f214",
"story-priority": "\f215", "auction-next-round": "\f215",
"story-reply": "\f216", "auction-filled": "\f216",
"strikethrough": "\f217", "auction-drop": "\f217",
"tag-add": "\f218", "attach": "\f218",
"tag-crossed": "\f219", "ask-support": "\f219",
"tag-filter": "\f21a", "arrow-right": "\f21a",
"tag-name": "\f21b", "arrow-left": "\f21b",
"tag": "\f21c", "arrow-down": "\f21c",
"timer": "\f21d", "arrow-down-circle": "\f21d",
"toncoin": "\f21e", "archive": "\f21e",
"tools": "\f21f", "archive-to-main": "\f21f",
"topic-new": "\f220", "archive-from-main": "\f220",
"trade": "\f221", "archive-filled": "\f221",
"transcribe": "\f222", "animations": "\f222",
"truck": "\f223", "animals": "\f223",
"unarchive": "\f224", "allow-speak": "\f224",
"underlined": "\f225", "allow-share": "\f225",
"understood": "\f226", "admin": "\f226",
"undo": "\f227", "add": "\f227",
"unique-profile": "\f228", "add-user": "\f228",
"unlist-outline": "\f229", "add-user-filled": "\f229",
"unlist": "\f22a", "add-one-badge": "\f22a",
"unlock-badge": "\f22b", "add-filled": "\f22b",
"unlock": "\f22c", "add-caption": "\f22c",
"unmute": "\f22d", "active-sessions": "\f22d",
"unpin": "\f22e", "rating-icons-negative": "\f22e",
"unread": "\f22f", "rating-icons-level90": "\f22f",
"up": "\f230", "rating-icons-level9": "\f230",
"user-filled": "\f231", "rating-icons-level80": "\f231",
"user-online": "\f232", "rating-icons-level8": "\f232",
"user-stars": "\f233", "rating-icons-level70": "\f233",
"user-tag": "\f234", "rating-icons-level7": "\f234",
"user": "\f235", "rating-icons-level60": "\f235",
"video-outlined": "\f236", "rating-icons-level6": "\f236",
"video-stop": "\f237", "rating-icons-level50": "\f237",
"video": "\f238", "rating-icons-level5": "\f238",
"view-once": "\f239", "rating-icons-level40": "\f239",
"voice-chat": "\f23a", "rating-icons-level4": "\f23a",
"volume-1": "\f23b", "rating-icons-level30": "\f23b",
"volume-2": "\f23c", "rating-icons-level3": "\f23c",
"volume-3": "\f23d", "rating-icons-level20": "\f23d",
"warning": "\f23e", "rating-icons-level2": "\f23e",
"web": "\f23f", "rating-icons-level10": "\f23f",
"webapp": "\f240", "rating-icons-level1": "\f240",
"word-wrap": "\f241", "folder-tabs-user": "\f241",
"zoom-in": "\f242", "folder-tabs-star": "\f242",
"zoom-out": "\f243", "folder-tabs-group": "\f243",
"folder-tabs-folder": "\f244",
"folder-tabs-chats": "\f245",
"folder-tabs-chat": "\f246",
"folder-tabs-channel": "\f247",
"folder-tabs-bot": "\f248",
); );

Binary file not shown.

Binary file not shown.

View File

@ -1,324 +1,329 @@
export type FontIconName = export type FontIconName =
| 'active-sessions' | 'zoom-out'
| 'add-caption'
| 'add-filled'
| 'add-one-badge'
| 'add-user-filled'
| 'add-user'
| 'add'
| 'admin'
| 'allow-speak'
| 'animals'
| 'animations'
| 'archive-filled'
| 'archive-from-main'
| 'archive-to-main'
| 'archive'
| 'arrow-down-circle'
| 'arrow-down'
| 'arrow-left'
| 'arrow-right'
| 'ask-support'
| 'attach'
| 'auction-drop'
| 'auction-filled'
| 'auction-next-round'
| 'auction'
| 'author-hidden'
| 'avatar-archived-chats'
| 'avatar-deleted-account'
| 'avatar-saved-messages'
| 'bold'
| 'boost-craft-chance'
| 'boost-outline'
| 'boost'
| 'boostcircle'
| 'boosts'
| 'bot-command'
| 'bot-commands-filled'
| 'bots'
| 'brush'
| 'bug'
| 'calendar-filter'
| 'calendar'
| 'camera-add'
| 'camera'
| 'car'
| 'card'
| 'cash-circle'
| 'channel-filled'
| 'channel'
| 'channelviews'
| 'chat-badge'
| 'chats-badge'
| 'check'
| 'clock-edit'
| 'clock'
| 'close-circle'
| 'close-topic'
| 'close'
| 'closed-gift'
| 'cloud-download'
| 'collapse-modal'
| 'collapse'
| 'colorize'
| 'combine-craft'
| 'comments-sticker'
| 'comments'
| 'copy-media'
| 'copy'
| 'craft'
| 'crop'
| 'crown-take-off-outline'
| 'crown-take-off'
| 'crown-wear-outline'
| 'crown-wear'
| 'darkmode'
| 'data'
| 'delete-filled'
| 'delete-left'
| 'delete-user'
| 'delete'
| 'diamond'
| 'document'
| 'double-badge'
| 'down'
| 'download'
| 'dropdown-arrows'
| 'eats'
| 'edit'
| 'email'
| 'enter'
| 'expand-modal'
| 'expand'
| 'eye-crossed-outline'
| 'eye-crossed'
| 'eye-outline'
| 'eye'
| 'favorite-filled'
| 'favorite'
| 'file-badge'
| 'flag'
| 'flip'
| 'folder-badge'
| 'folder-tabs-bot'
| 'folder-tabs-channel'
| 'folder-tabs-chat'
| 'folder-tabs-chats'
| 'folder-tabs-folder'
| 'folder-tabs-group'
| 'folder-tabs-star'
| 'folder-tabs-user'
| 'folder'
| 'fontsize'
| 'forums'
| 'forward'
| 'fragment'
| 'frozen-time'
| 'fullscreen'
| 'gifs'
| 'gift-transfer-inline'
| 'gift'
| 'group-filled'
| 'group'
| 'grouped-disable'
| 'grouped'
| 'hand-stop'
| 'hashtag'
| 'hd-photo'
| 'heart-outline'
| 'heart'
| 'help'
| 'info-filled'
| 'info'
| 'install'
| 'italic'
| 'key'
| 'keyboard'
| 'lamp'
| 'language'
| 'large-pause'
| 'large-play'
| 'link-badge'
| 'link-broken'
| 'link'
| 'location'
| 'lock-badge'
| 'lock'
| 'logout'
| 'loop'
| 'mention'
| 'menu'
| 'message-failed'
| 'message-pending'
| 'message-read'
| 'message-succeeded'
| 'message'
| 'microphone-alt'
| 'microphone'
| 'monospace'
| 'more-circle'
| 'more'
| 'move-caption-down'
| 'move-caption-up'
| 'mute'
| 'muted'
| 'my-notes'
| 'new-chat-filled'
| 'next-link'
| 'next'
| 'nochannel'
| 'noise-suppression'
| 'non-contacts'
| 'note'
| 'one-filled'
| 'open-in-new-tab'
| 'password-off'
| 'pause'
| 'permissions'
| 'phone-discard-outline'
| 'phone-discard'
| 'phone'
| 'photo'
| 'pin-badge'
| 'pin-list'
| 'pin'
| 'pinned-chat'
| 'pinned-message'
| 'pip'
| 'play-story'
| 'play'
| 'poll'
| 'previous'
| 'privacy-policy'
| 'proof-of-ownership'
| 'quote-text'
| 'quote'
| 'radial-badge'
| 'rating-icons-level1'
| 'rating-icons-level10'
| 'rating-icons-level2'
| 'rating-icons-level20'
| 'rating-icons-level3'
| 'rating-icons-level30'
| 'rating-icons-level4'
| 'rating-icons-level40'
| 'rating-icons-level5'
| 'rating-icons-level50'
| 'rating-icons-level6'
| 'rating-icons-level60'
| 'rating-icons-level7'
| 'rating-icons-level70'
| 'rating-icons-level8'
| 'rating-icons-level80'
| 'rating-icons-level9'
| 'rating-icons-level90'
| 'rating-icons-negative'
| 'readchats'
| 'recent'
| 'redo'
| 'refund'
| 'reload'
| 'remove-quote'
| 'remove'
| 'reopen-topic'
| 'reorder-tabs'
| 'replace'
| 'replies'
| 'reply-filled'
| 'reply'
| 'revenue-split'
| 'revote'
| 'rotate'
| 'save-story'
| 'saved-messages'
| 'schedule'
| 'scheduled'
| 'sd-photo'
| 'search'
| 'select'
| 'sell-outline'
| 'sell'
| 'send-outline'
| 'send'
| 'settings-filled'
| 'settings'
| 'share-filled'
| 'share-screen-outlined'
| 'share-screen-stop'
| 'share-screen'
| 'show-message'
| 'sidebar'
| 'skip-next'
| 'skip-previous'
| 'smallscreen'
| 'smile'
| 'sort-by-date'
| 'sort-by-number'
| 'sort-by-price'
| 'sort'
| 'speaker-muted-story'
| 'speaker-outline'
| 'speaker-story'
| 'speaker'
| 'spoiler-disable'
| 'spoiler'
| 'sport'
| 'star'
| 'stars-lock'
| 'stars-refund'
| 'stats'
| 'stealth-future'
| 'stealth-past'
| 'stickers'
| 'stop-raising-hand'
| 'stop'
| 'story-caption'
| 'story-expired'
| 'story-priority'
| 'story-reply'
| 'strikethrough'
| 'tag-add'
| 'tag-crossed'
| 'tag-filter'
| 'tag-name'
| 'tag'
| 'timer'
| 'toncoin'
| 'tools'
| 'topic-new'
| 'trade'
| 'transcribe'
| 'truck'
| 'unarchive'
| 'underlined'
| 'understood'
| 'undo'
| 'unique-profile'
| 'unlist-outline'
| 'unlist'
| 'unlock-badge'
| 'unlock'
| 'unmute'
| 'unpin'
| 'unread'
| 'up'
| 'user-filled'
| 'user-online'
| 'user-stars'
| 'user-tag'
| 'user'
| 'video-outlined'
| 'video-stop'
| 'video'
| 'view-once'
| 'voice-chat'
| 'volume-1'
| 'volume-2'
| 'volume-3'
| 'warning'
| 'web'
| 'webapp'
| 'word-wrap'
| 'zoom-in' | 'zoom-in'
| 'zoom-out'; | 'word-wrap'
| 'webapp'
| 'web'
| 'warning'
| 'volume-3'
| 'volume-2'
| 'volume-1'
| 'voice-chat'
| 'view-once'
| 'video'
| 'video-stop'
| 'video-outlined'
| 'user'
| 'user-tag'
| 'user-stars'
| 'user-online'
| 'user-filled'
| 'up'
| 'unread'
| 'unpin'
| 'unmute'
| 'unlock'
| 'unlock-badge'
| 'unlist'
| 'unlist-outline'
| 'unique-profile'
| 'undo'
| 'understood'
| 'underlined'
| 'unarchive'
| 'truck'
| 'transcribe'
| 'trade'
| 'topic-new'
| 'tools'
| 'toncoin'
| 'timer'
| 'tag'
| 'tag-name'
| 'tag-filter'
| 'tag-crossed'
| 'tag-add'
| 'strikethrough'
| 'story-reply'
| 'story-priority'
| 'story-expired'
| 'story-caption'
| 'stop'
| 'stop-raising-hand'
| 'stickers'
| 'stealth-past'
| 'stealth-future'
| 'stats'
| 'stars-refund'
| 'stars-lock'
| 'star'
| 'sport'
| 'spoiler'
| 'spoiler-disable'
| 'speaker'
| 'speaker-story'
| 'speaker-outline'
| 'speaker-muted-story'
| 'sort'
| 'sort-by-price'
| 'sort-by-number'
| 'sort-by-date'
| 'smile'
| 'smallscreen'
| 'skip-previous'
| 'skip-next'
| 'sidebar'
| 'show-message'
| 'share-screen'
| 'share-screen-stop'
| 'share-screen-outlined'
| 'share-filled'
| 'settings'
| 'settings-filled'
| 'send'
| 'send-outline'
| 'sell'
| 'sell-outline'
| 'select'
| 'select-filled'
| 'search'
| 'sd-photo'
| 'scheduled'
| 'schedule'
| 'saved-messages'
| 'save-story'
| 'rotate'
| 'revote'
| 'revenue-split'
| 'reply'
| 'reply-filled'
| 'replies'
| 'replace'
| 'reorder-tabs'
| 'reopen-topic'
| 'remove'
| 'remove-quote'
| 'reload'
| 'refund'
| 'redo'
| 'recent'
| 'readchats'
| 'radial-badge'
| 'quote'
| 'quote-text'
| 'proof-of-ownership'
| 'privacy-policy'
| 'previous'
| 'poll'
| 'play'
| 'play-story'
| 'pip'
| 'pinned-message'
| 'pinned-chat'
| 'pin'
| 'pin-list'
| 'pin-badge'
| 'photo'
| 'phone'
| 'phone-discard'
| 'phone-discard-outline'
| 'permissions'
| 'pause'
| 'password-off'
| 'open-in-new-tab'
| 'one-filled'
| 'note'
| 'non-contacts'
| 'noise-suppression'
| 'nochannel'
| 'no-share'
| 'no-download'
| 'next'
| 'next-link'
| 'new-chat-filled'
| 'my-notes'
| 'muted'
| 'mute'
| 'move-caption-up'
| 'move-caption-down'
| 'more'
| 'more-circle'
| 'monospace'
| 'microphone'
| 'microphone-alt'
| 'message'
| 'message-succeeded'
| 'message-read'
| 'message-pending'
| 'message-failed'
| 'menu'
| 'mention'
| 'loop'
| 'logout'
| 'lock'
| 'lock-badge'
| 'location'
| 'link'
| 'link-broken'
| 'link-badge'
| 'large-play'
| 'large-pause'
| 'language'
| 'lamp'
| 'keyboard'
| 'key'
| 'italic'
| 'install'
| 'info'
| 'info-filled'
| 'help'
| 'heart'
| 'heart-outline'
| 'hd-photo'
| 'hashtag'
| 'hand-stop'
| 'hand-stop-filled'
| 'grouped'
| 'grouped-disable'
| 'group'
| 'group-filled'
| 'gift'
| 'gift-transfer-inline'
| 'gifs'
| 'fullscreen'
| 'frozen-time'
| 'fragment'
| 'forward'
| 'forums'
| 'fontsize'
| 'folder'
| 'folder-badge'
| 'flip'
| 'flag'
| 'file-badge'
| 'favorite'
| 'favorite-filled'
| 'eye'
| 'eye-outline'
| 'eye-crossed'
| 'eye-crossed-outline'
| 'expand'
| 'expand-modal'
| 'enter'
| 'email'
| 'edit'
| 'eats'
| 'dropdown-arrows'
| 'download'
| 'down'
| 'double-badge'
| 'document'
| 'diamond'
| 'delete'
| 'delete-user'
| 'delete-left'
| 'delete-filled'
| 'data'
| 'darkmode'
| 'crown-wear'
| 'crown-wear-outline'
| 'crown-take-off'
| 'crown-take-off-outline'
| 'crop'
| 'craft'
| 'copy'
| 'copy-media'
| 'comments'
| 'comments-sticker'
| 'combine-craft'
| 'colorize'
| 'collapse'
| 'collapse-modal'
| 'cloud-download'
| 'closed-gift'
| 'close'
| 'close-topic'
| 'close-circle'
| 'clock'
| 'clock-edit'
| 'check'
| 'chats-badge'
| 'chat-badge'
| 'channelviews'
| 'channel'
| 'channel-filled'
| 'cash-circle'
| 'card'
| 'car'
| 'camera'
| 'camera-add'
| 'calendar'
| 'calendar-filter'
| 'bug'
| 'brush'
| 'bots'
| 'bot-commands-filled'
| 'bot-command'
| 'boosts'
| 'boostcircle'
| 'boost'
| 'boost-outline'
| 'boost-craft-chance'
| 'bold'
| 'avatar-saved-messages'
| 'avatar-deleted-account'
| 'avatar-archived-chats'
| 'author-hidden'
| 'auction'
| 'auction-next-round'
| 'auction-filled'
| 'auction-drop'
| 'attach'
| 'ask-support'
| 'arrow-right'
| 'arrow-left'
| 'arrow-down'
| 'arrow-down-circle'
| 'archive'
| 'archive-to-main'
| 'archive-from-main'
| 'archive-filled'
| 'animations'
| 'animals'
| 'allow-speak'
| 'allow-share'
| 'admin'
| 'add'
| 'add-user'
| 'add-user-filled'
| 'add-one-badge'
| 'add-filled'
| 'add-caption'
| 'active-sessions'
| 'rating-icons-negative'
| 'rating-icons-level90'
| 'rating-icons-level9'
| 'rating-icons-level80'
| 'rating-icons-level8'
| 'rating-icons-level70'
| 'rating-icons-level7'
| 'rating-icons-level60'
| 'rating-icons-level6'
| 'rating-icons-level50'
| 'rating-icons-level5'
| 'rating-icons-level40'
| 'rating-icons-level4'
| 'rating-icons-level30'
| 'rating-icons-level3'
| 'rating-icons-level20'
| 'rating-icons-level2'
| 'rating-icons-level10'
| 'rating-icons-level1'
| 'folder-tabs-user'
| 'folder-tabs-star'
| 'folder-tabs-group'
| 'folder-tabs-folder'
| 'folder-tabs-chats'
| 'folder-tabs-chat'
| 'folder-tabs-channel'
| 'folder-tabs-bot';

View File

@ -54,6 +54,8 @@ export interface LangPair {
'PremiumPreviewUploadsDescription': undefined; 'PremiumPreviewUploadsDescription': undefined;
'PremiumPreviewAdvancedChatManagementDescription': undefined; 'PremiumPreviewAdvancedChatManagementDescription': undefined;
'PremiumPreviewAnimatedProfilesDescription': undefined; 'PremiumPreviewAnimatedProfilesDescription': undefined;
'PremiumPreviewNoForwards': undefined;
'PremiumPreviewNoForwardsDescription': undefined;
'PremiumLimitAccountsTitle': undefined; 'PremiumLimitAccountsTitle': undefined;
'PremiumLimitAccountsNoPremium': undefined; 'PremiumLimitAccountsNoPremium': undefined;
'PremiumLimitAccounts': undefined; 'PremiumLimitAccounts': undefined;
@ -1537,6 +1539,22 @@ export interface LangPair {
'ActionChangedPhotoChannel': undefined; 'ActionChangedPhotoChannel': undefined;
'ActionCreatedChannel': undefined; 'ActionCreatedChannel': undefined;
'ActionScreenshotTakenYou': undefined; 'ActionScreenshotTakenYou': undefined;
'ActionSharingDisabledYou': undefined;
'ActionSharingEnabledYou': undefined;
'ActionSharingStillDisabled': undefined;
'ContextMenuNoForwardsYou': undefined;
'DisableSharing': undefined;
'EnableSharing': undefined;
'NotificationSharingEnabled': undefined;
'NotificationSharingDisabled': undefined;
'NoForwardingTitle': undefined;
'NoForwardingDescription': undefined;
'NoSavingTitle': undefined;
'NoSavingDescription': undefined;
'NoForwardsRequestYouTitle': undefined;
'NoForwardsRequestForwarding': undefined;
'NoForwardsRequestSaving': undefined;
'NoForwardsRequestCopying': undefined;
'ActionBotAppPlaceholder': undefined; 'ActionBotAppPlaceholder': undefined;
'ActionGiftTextUnknown': undefined; 'ActionGiftTextUnknown': undefined;
'ActionGiftTextUnknownYou': undefined; 'ActionGiftTextUnknownYou': undefined;
@ -2018,6 +2036,8 @@ export interface LangPair {
'RankEditSave': undefined; 'RankEditSave': undefined;
'RankEditTextOwn': undefined; 'RankEditTextOwn': undefined;
'MenuAddCaption': undefined; 'MenuAddCaption': undefined;
'NoForwardsRequestReject': undefined;
'NoForwardsRequestAccept': undefined;
} }
export interface LangPairWithVariables<V = LangVariable> { export interface LangPairWithVariables<V = LangVariable> {
@ -2750,6 +2770,18 @@ export interface LangPairWithVariables<V = LangVariable> {
'ActionScreenshotTaken': { 'ActionScreenshotTaken': {
'from': V; 'from': V;
}; };
'ActionSharingDisabled': {
'from': V;
};
'ActionSharingEnabled': {
'from': V;
};
'ContextMenuNoForwardsPeer': {
'name': V;
};
'NoForwardsRequestTitle': {
'user': V;
};
'ActionBotAllowedFromDomain': { 'ActionBotAllowedFromDomain': {
'domain': V; 'domain': V;
}; };